talent-employment.integration.test.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. import { describe, it, expect, beforeEach } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
  4. import { JWTUtil } from '@d8d/shared-utils';
  5. import { UserType } from '@d8d/shared-types';
  6. import { UserEntity, Role } from '@d8d/user-module';
  7. import { File } from '@d8d/file-module';
  8. import { DisabledPerson, DisabledBankCard, DisabledPhoto, DisabledRemark, DisabledVisit } from '@d8d/allin-disability-module/entities';
  9. import { Company } from '@d8d/allin-company-module';
  10. import { Platform } from '@d8d/allin-platform-module';
  11. import { Channel } from '@d8d/allin-channel-module';
  12. import { BankName } from '@d8d/bank-names-module';
  13. import talentEmploymentRoutes from '../../src/routes/talent-employment.routes';
  14. import { EmploymentOrder } from '../../src/entities/employment-order.entity';
  15. import { OrderPerson } from '../../src/entities/order-person.entity';
  16. import { OrderPersonAsset } from '../../src/entities/order-person-asset.entity';
  17. import { AssetType, AssetFileType } from '../../src/schemas/order.schema';
  18. import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
  19. // 设置集成测试钩子
  20. setupIntegrationDatabaseHooksWithEntities([
  21. UserEntity, Role, File, Platform, Company, Channel,
  22. DisabledPerson, DisabledBankCard, DisabledPhoto, DisabledRemark, DisabledVisit,
  23. BankName,
  24. EmploymentOrder, OrderPerson, OrderPersonAsset
  25. ]);
  26. describe('人才就业信息API集成测试 - 故事015.005 + 015.006', () => {
  27. let client: ReturnType<typeof testClient<typeof talentEmploymentRoutes>>;
  28. let testToken: string;
  29. let testTalentUser: UserEntity;
  30. let testDisabledPerson: DisabledPerson;
  31. let testCompany: Company;
  32. let testPlatform: Platform;
  33. let testFile: File;
  34. beforeEach(async () => {
  35. // 创建测试客户端
  36. client = testClient(talentEmploymentRoutes);
  37. // 获取数据源
  38. const dataSource = await IntegrationTestDatabase.getDataSource();
  39. // 创建测试银行
  40. const bankNameRepository = dataSource.getRepository(BankName);
  41. const testBankName = bankNameRepository.create({
  42. name: '测试银行',
  43. code: 'TEST001',
  44. remark: '测试银行',
  45. status: 1
  46. });
  47. await bankNameRepository.save(testBankName);
  48. // 创建测试残疾人 - 使用正确的字段名
  49. const personRepository = dataSource.getRepository(DisabledPerson);
  50. testDisabledPerson = personRepository.create({
  51. name: '李四',
  52. gender: '1',
  53. disabilityType: '肢体残疾',
  54. disabilityLevel: '三级',
  55. idCard: `110101${Date.now() % 100000000}`,
  56. disabilityId: `D${Date.now() % 100000000}`,
  57. idAddress: '北京市朝阳区',
  58. phone: '13900139000',
  59. province: '北京市',
  60. city: '北京市',
  61. district: '朝阳区',
  62. jobStatus: 0, // 0-未在职,1-已在职
  63. canDirectContact: 1,
  64. isInBlackList: 0,
  65. birthDate: new Date('1990-01-01'),
  66. idValidDate: new Date('2030-01-01'),
  67. disabilityValidDate: new Date('2025-12-31'),
  68. specificDisability: '左腿轻微残疾'
  69. });
  70. await personRepository.save(testDisabledPerson);
  71. // 创建测试人才用户(残疾人已经创建,可以用作personId)
  72. const userRepository = dataSource.getRepository(UserEntity);
  73. testTalentUser = userRepository.create({
  74. username: `talent_${Date.now()}`,
  75. password: 'test_password',
  76. nickname: '测试人才',
  77. userType: UserType.TALENT,
  78. personId: testDisabledPerson.id,
  79. registrationSource: 'mini',
  80. isDisabled: 0,
  81. isDeleted: 0
  82. });
  83. await userRepository.save(testTalentUser);
  84. // 生成测试用户的token
  85. testToken = JWTUtil.generateToken({
  86. id: testTalentUser.id,
  87. username: testTalentUser.username,
  88. roles: [{ name: 'talent' }]
  89. }, {
  90. personId: testDisabledPerson.id,
  91. userType: UserType.TALENT
  92. });
  93. // 创建测试平台
  94. const platformRepository = dataSource.getRepository(Platform);
  95. testPlatform = platformRepository.create({
  96. platformName: '测试平台',
  97. status: 1
  98. });
  99. await platformRepository.save(testPlatform);
  100. // 创建测试企业
  101. const companyRepository = dataSource.getRepository(Company);
  102. testCompany = companyRepository.create({
  103. platformId: testPlatform.id,
  104. companyName: '测试科技有限公司',
  105. contactPerson: '张三',
  106. contactPhone: '13800138000',
  107. status: 1
  108. });
  109. await companyRepository.save(testCompany);
  110. // 创建测试文件(用于视频资产)
  111. const fileRepository = dataSource.getRepository(File);
  112. testFile = fileRepository.create({
  113. name: '工资视频_2025-01.mp4',
  114. type: 'video/mp4',
  115. size: 1024000,
  116. path: `videos/${Date.now()}_salary_video.mp4`,
  117. uploadUserId: testTalentUser.id,
  118. uploadTime: new Date()
  119. });
  120. await fileRepository.save(testFile);
  121. });
  122. describe('GET /employment/status - 当前就业状态查询', () => {
  123. it('应该成功查询当前就业状态 - 在职状态', async () => {
  124. // 创建测试订单
  125. const dataSource = await IntegrationTestDatabase.getDataSource();
  126. const orderRepository = dataSource.getRepository(EmploymentOrder);
  127. const testOrder = orderRepository.create({
  128. platformId: testPlatform.id,
  129. companyId: testCompany.id,
  130. orderName: '包装工',
  131. expectedStartDate: new Date('2025-01-01'),
  132. actualStartDate: new Date('2025-01-15'),
  133. orderStatus: OrderStatus.IN_PROGRESS,
  134. workStatus: WorkStatus.WORKING
  135. });
  136. await orderRepository.save(testOrder);
  137. // 创建订单人员关联
  138. const orderPersonRepository = dataSource.getRepository(OrderPerson);
  139. const orderPerson = orderPersonRepository.create({
  140. orderId: testOrder.id,
  141. personId: testDisabledPerson.id,
  142. joinDate: new Date('2025-01-15'),
  143. actualStartDate: new Date('2025-01-15'),
  144. workStatus: WorkStatus.WORKING,
  145. salaryDetail: 3500.00
  146. });
  147. await orderPersonRepository.save(orderPerson);
  148. // 查询当前就业状态
  149. const response = await client.employment.status.$get(undefined, {
  150. headers: {
  151. 'Authorization': `Bearer ${testToken}`
  152. }
  153. });
  154. expect(response.status).toBe(200);
  155. if (response.status === 200) {
  156. const data = await response.json();
  157. expect(data.companyName).toBe('测试科技有限公司');
  158. expect(data.orderId).toBe(testOrder.id);
  159. expect(data.orderName).toBe('包装工');
  160. expect(data.workStatus).toBe('working');
  161. expect(data.salaryLevel).toBe(3500.00);
  162. }
  163. });
  164. it('应该返回200和null当用户无就业记录时 - 故事015.006', async () => {
  165. const response = await client.employment.status.$get(undefined, {
  166. headers: {
  167. 'Authorization': `Bearer ${testToken}`
  168. }
  169. });
  170. // 无就业记录时返回200状态码和null,不是404
  171. expect(response.status).toBe(200);
  172. if (response.status === 200) {
  173. const data = await response.json();
  174. expect(data).toBeNull();
  175. }
  176. });
  177. it('应该返回404当用户未关联残疾人信息时', async () => {
  178. // 创建一个没有personId的用户
  179. const dataSource = await IntegrationTestDatabase.getDataSource();
  180. const userRepository = dataSource.getRepository(UserEntity);
  181. const userWithoutPerson = userRepository.create({
  182. username: `no_person_${Date.now()}`,
  183. password: 'test_password',
  184. nickname: '无残疾人关联用户',
  185. userType: UserType.TALENT,
  186. registrationSource: 'mini',
  187. isDisabled: 0,
  188. isDeleted: 0
  189. });
  190. await userRepository.save(userWithoutPerson);
  191. const token = JWTUtil.generateToken({
  192. id: userWithoutPerson.id,
  193. username: userWithoutPerson.username,
  194. roles: [{ name: 'talent' }]
  195. }, {
  196. userType: UserType.TALENT
  197. });
  198. const response = await client.employment.status.$get(undefined, {
  199. headers: {
  200. 'Authorization': `Bearer ${token}`
  201. }
  202. });
  203. expect(response.status).toBe(404);
  204. if (response.status === 404) {
  205. const error = await response.json();
  206. expect(error.code).toBe(404);
  207. expect(error.message).toContain('用户不存在');
  208. }
  209. });
  210. });
  211. describe('GET /employment/salary-records - 薪资记录查询', () => {
  212. beforeEach(async () => {
  213. // 创建测试订单和人员关联
  214. const dataSource = await IntegrationTestDatabase.getDataSource();
  215. const orderRepository = dataSource.getRepository(EmploymentOrder);
  216. const testOrder = orderRepository.create({
  217. platformId: testPlatform.id,
  218. companyId: testCompany.id,
  219. orderName: '包装工',
  220. expectedStartDate: new Date('2025-01-01'),
  221. actualStartDate: new Date('2025-01-15'),
  222. orderStatus: OrderStatus.IN_PROGRESS,
  223. workStatus: WorkStatus.WORKING
  224. });
  225. await orderRepository.save(testOrder);
  226. const orderPersonRepository = dataSource.getRepository(OrderPerson);
  227. const orderPerson = orderPersonRepository.create({
  228. orderId: testOrder.id,
  229. personId: testDisabledPerson.id,
  230. joinDate: new Date('2025-01-15'),
  231. actualStartDate: new Date('2025-01-15'),
  232. workStatus: WorkStatus.WORKING,
  233. salaryDetail: 3500.00
  234. });
  235. await orderPersonRepository.save(orderPerson);
  236. });
  237. it('应该成功查询薪资记录', async () => {
  238. const response = await client.employment['salary-records'].$get({
  239. query: {
  240. skip: 0,
  241. take: 10
  242. }
  243. }, {
  244. headers: {
  245. 'Authorization': `Bearer ${testToken}`
  246. }
  247. });
  248. if (response.status !== 200) {
  249. const error = await response.json();
  250. console.debug('查询薪资记录失败:', JSON.stringify(error, null, 2));
  251. }
  252. expect(response.status).toBe(200);
  253. if (response.status === 200) {
  254. const data = await response.json();
  255. expect(data.data).toBeInstanceOf(Array);
  256. expect(data.total).toBeGreaterThanOrEqual(0);
  257. }
  258. });
  259. it('应该支持按月份过滤薪资记录', async () => {
  260. const response = await client.employment['salary-records'].$get({
  261. query: {
  262. month: '2025-01',
  263. skip: 0,
  264. take: 10
  265. }
  266. }, {
  267. headers: {
  268. 'Authorization': `Bearer ${testToken}`
  269. }
  270. });
  271. expect(response.status).toBe(200);
  272. if (response.status === 200) {
  273. const data = await response.json();
  274. expect(data.data).toBeInstanceOf(Array);
  275. }
  276. });
  277. });
  278. describe('GET /employment/history - 就业历史查询', () => {
  279. beforeEach(async () => {
  280. // 创建多个测试订单来模拟就业历史
  281. const dataSource = await IntegrationTestDatabase.getDataSource();
  282. const orderRepository = dataSource.getRepository(EmploymentOrder);
  283. const order1 = orderRepository.create({
  284. platformId: testPlatform.id,
  285. companyId: testCompany.id,
  286. orderName: '包装工',
  287. expectedStartDate: new Date('2024-06-01'),
  288. actualStartDate: new Date('2024-06-15'),
  289. actualEndDate: new Date('2024-12-31'),
  290. orderStatus: OrderStatus.COMPLETED,
  291. workStatus: WorkStatus.RESIGNED
  292. });
  293. await orderRepository.save(order1);
  294. const orderPersonRepository = dataSource.getRepository(OrderPerson);
  295. const orderPerson1 = orderPersonRepository.create({
  296. orderId: order1.id,
  297. personId: testDisabledPerson.id,
  298. joinDate: new Date('2024-06-15'),
  299. leaveDate: new Date('2024-12-31'),
  300. workStatus: WorkStatus.RESIGNED,
  301. salaryDetail: 3000.00
  302. });
  303. await orderPersonRepository.save(orderPerson1);
  304. const order2 = orderRepository.create({
  305. platformId: testPlatform.id,
  306. companyId: testCompany.id,
  307. orderName: '组装工',
  308. expectedStartDate: new Date('2025-01-01'),
  309. actualStartDate: new Date('2025-01-15'),
  310. orderStatus: OrderStatus.IN_PROGRESS,
  311. workStatus: WorkStatus.WORKING
  312. });
  313. await orderRepository.save(order2);
  314. const orderPerson2 = orderPersonRepository.create({
  315. orderId: order2.id,
  316. personId: testDisabledPerson.id,
  317. joinDate: new Date('2025-01-15'),
  318. workStatus: WorkStatus.WORKING,
  319. salaryDetail: 3500.00
  320. });
  321. await orderPersonRepository.save(orderPerson2);
  322. });
  323. it('应该成功查询就业历史,按时间倒序排列', async () => {
  324. const response = await client.employment['history'].$get({
  325. query: {
  326. skip: 0,
  327. take: 20
  328. }
  329. }, {
  330. headers: {
  331. 'Authorization': `Bearer ${testToken}`
  332. }
  333. });
  334. if (response.status !== 200) {
  335. const error = await response.json();
  336. console.debug('查询就业历史失败:', JSON.stringify(error, null, 2));
  337. }
  338. expect(response.status).toBe(200);
  339. if (response.status === 200) {
  340. const data = await response.json();
  341. expect(data.data).toBeInstanceOf(Array);
  342. expect(data.total).toBeGreaterThanOrEqual(0);
  343. // 验证按时间倒序排列(最新的在前)
  344. if (data.data.length > 1) {
  345. expect(new Date(data.data[0].joinDate).getTime())
  346. .toBeGreaterThanOrEqual(new Date(data.data[1].joinDate).getTime());
  347. }
  348. }
  349. });
  350. it('应该支持分页查询就业历史', async () => {
  351. const response = await client.employment['history'].$get({
  352. query: {
  353. skip: 0,
  354. take: 1
  355. }
  356. }, {
  357. headers: {
  358. 'Authorization': `Bearer ${testToken}`
  359. }
  360. });
  361. expect(response.status).toBe(200);
  362. if (response.status === 200) {
  363. const data = await response.json();
  364. expect(data.data).toBeInstanceOf(Array);
  365. expect(data.total).toBe(2);
  366. }
  367. });
  368. });
  369. describe('GET /employment/salary-videos - 薪资视频查询', () => {
  370. beforeEach(async () => {
  371. // 创建测试订单和人员关联
  372. const dataSource = await IntegrationTestDatabase.getDataSource();
  373. const orderRepository = dataSource.getRepository(EmploymentOrder);
  374. const testOrder = orderRepository.create({
  375. platformId: testPlatform.id,
  376. companyId: testCompany.id,
  377. orderName: '包装工',
  378. expectedStartDate: new Date('2025-01-01'),
  379. orderStatus: OrderStatus.IN_PROGRESS,
  380. workStatus: WorkStatus.WORKING
  381. });
  382. await orderRepository.save(testOrder);
  383. const orderPersonRepository = dataSource.getRepository(OrderPerson);
  384. const orderPerson = orderPersonRepository.create({
  385. orderId: testOrder.id,
  386. personId: testDisabledPerson.id,
  387. joinDate: new Date('2025-01-15'),
  388. workStatus: WorkStatus.WORKING,
  389. salaryDetail: 3500.00
  390. });
  391. await orderPersonRepository.save(orderPerson);
  392. // 创建薪资视频资产
  393. const assetRepository = dataSource.getRepository(OrderPersonAsset);
  394. const salaryVideo = assetRepository.create({
  395. orderId: testOrder.id,
  396. personId: testDisabledPerson.id,
  397. fileId: testFile.id,
  398. assetType: AssetType.SALARY_VIDEO,
  399. assetFileType: AssetFileType.VIDEO,
  400. status: 'verified',
  401. relatedTime: new Date('2025-01-15')
  402. });
  403. await assetRepository.save(salaryVideo);
  404. });
  405. it('应该成功查询薪资视频', async () => {
  406. const response = await client.employment['salary-videos'].$get({
  407. query: {
  408. skip: 0,
  409. take: 10
  410. }
  411. }, {
  412. headers: {
  413. 'Authorization': `Bearer ${testToken}`
  414. }
  415. });
  416. if (response.status !== 200) {
  417. const error = await response.json();
  418. console.debug('查询薪资视频失败:', JSON.stringify(error, null, 2));
  419. }
  420. expect(response.status).toBe(200);
  421. if (response.status === 200) {
  422. const data = await response.json();
  423. expect(data.data).toBeInstanceOf(Array);
  424. expect(data.total).toBeGreaterThanOrEqual(0);
  425. }
  426. });
  427. it('应该支持按类型过滤薪资视频', async () => {
  428. const response = await client.employment['salary-videos'].$get({
  429. query: {
  430. assetType: 'salary_video',
  431. skip: 0,
  432. take: 10
  433. }
  434. }, {
  435. headers: {
  436. 'Authorization': `Bearer ${testToken}`
  437. }
  438. });
  439. expect(response.status).toBe(200);
  440. if (response.status === 200) {
  441. const data = await response.json();
  442. expect(data.data).toBeInstanceOf(Array);
  443. }
  444. });
  445. it('应该支持按月份过滤薪资视频', async () => {
  446. const response = await client.employment['salary-videos'].$get({
  447. query: {
  448. month: '2025-01',
  449. skip: 0,
  450. take: 10
  451. }
  452. }, {
  453. headers: {
  454. 'Authorization': `Bearer ${testToken}`
  455. }
  456. });
  457. expect(response.status).toBe(200);
  458. if (response.status === 200) {
  459. const data = await response.json();
  460. expect(data.data).toBeInstanceOf(Array);
  461. }
  462. });
  463. });
  464. describe('认证和权限验证', () => {
  465. it('应该返回401当未提供token时', async () => {
  466. const response = await client.employment.status.$get(undefined, {
  467. headers: {}
  468. });
  469. expect(response.status).toBe(401);
  470. });
  471. it('应该返回401当token无效时', async () => {
  472. const response = await client.employment.status.$get(undefined, {
  473. headers: {
  474. 'Authorization': 'Bearer invalid_token'
  475. }
  476. });
  477. expect(response.status).toBe(401);
  478. });
  479. });
  480. });