order.service.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. import { GenericCrudService } from '@d8d/shared-crud';
  2. import { DataSource, Repository, DataSourceOptions, In, Not } from 'typeorm';
  3. import { EmploymentOrder } from '../entities/employment-order.entity';
  4. import { OrderPerson } from '../entities/order-person.entity';
  5. import { OrderPersonAsset } from '../entities/order-person-asset.entity';
  6. import { AssetType, AssetFileType } from '../schemas/order.schema';
  7. import { FileService, File } from '@d8d/file-module';
  8. import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
  9. export class OrderService extends GenericCrudService<EmploymentOrder> {
  10. private readonly orderPersonRepository: Repository<OrderPerson>;
  11. private readonly orderPersonAssetRepository: Repository<OrderPersonAsset>;
  12. private readonly fileRepository: Repository<File>;
  13. private fileService: FileService;
  14. constructor(dataSource: DataSource) {
  15. super(dataSource, EmploymentOrder);
  16. this.orderPersonRepository = dataSource.getRepository(OrderPerson);
  17. this.orderPersonAssetRepository = dataSource.getRepository(OrderPersonAsset);
  18. this.fileRepository = dataSource.getRepository(File);
  19. this.fileService = new FileService(dataSource);
  20. }
  21. /**
  22. * 创建订单 - 覆盖父类方法,添加验证和业务逻辑
  23. */
  24. async create(data: Partial<EmploymentOrder>, userId?: string | number): Promise<EmploymentOrder> {
  25. // 验证枚举值
  26. if (data.orderStatus && !Object.values(OrderStatus).includes(data.orderStatus)) {
  27. throw new Error('订单状态无效');
  28. }
  29. if (data.workStatus && !Object.values(WorkStatus).includes(data.workStatus)) {
  30. throw new Error('工作状态无效');
  31. }
  32. // 设置默认值
  33. const orderData = {
  34. ...data,
  35. orderStatus: data.orderStatus || OrderStatus.DRAFT,
  36. workStatus: data.workStatus || WorkStatus.NOT_WORKING,
  37. };
  38. return super.create(orderData, userId);
  39. }
  40. /**
  41. * 更新订单 - 覆盖父类方法,添加验证
  42. */
  43. async update(id: number, data: Partial<EmploymentOrder>, userId?: string | number): Promise<EmploymentOrder | null> {
  44. // 验证枚举值
  45. if (data.orderStatus && !Object.values(OrderStatus).includes(data.orderStatus)) {
  46. throw new Error('订单状态无效');
  47. }
  48. if (data.workStatus && !Object.values(WorkStatus).includes(data.workStatus)) {
  49. throw new Error('工作状态无效');
  50. }
  51. return super.update(id, data, userId);
  52. }
  53. /**
  54. * 分页查询订单 - 自定义方法,返回源服务的格式
  55. */
  56. async findAll(query: {
  57. orderName?: string;
  58. platformId?: number;
  59. companyId?: number;
  60. channelId?: number;
  61. orderStatus?: OrderStatus;
  62. startDate?: string;
  63. endDate?: string;
  64. page?: number;
  65. limit?: number;
  66. }): Promise<{ data: any[]; total: number }> {
  67. const {
  68. orderName,
  69. platformId,
  70. companyId,
  71. channelId,
  72. orderStatus,
  73. startDate,
  74. endDate,
  75. page = 1,
  76. limit = 10
  77. } = query;
  78. const queryBuilder = this.repository.createQueryBuilder('order');
  79. // 构建查询条件
  80. if (orderName) {
  81. queryBuilder.andWhere('order.orderName LIKE :orderName', { orderName: `%${orderName}%` });
  82. }
  83. if (platformId) {
  84. queryBuilder.andWhere('order.platformId = :platformId', { platformId });
  85. }
  86. if (companyId) {
  87. queryBuilder.andWhere('order.companyId = :companyId', { companyId });
  88. }
  89. if (channelId) {
  90. queryBuilder.andWhere('order.channelId = :channelId', { channelId });
  91. }
  92. if (orderStatus) {
  93. queryBuilder.andWhere('order.orderStatus = :orderStatus', { orderStatus });
  94. }
  95. // 日期范围查询 - 基于createTime字段
  96. if (startDate) {
  97. queryBuilder.andWhere('order.createTime >= :startDate', { startDate: `${startDate}T00:00:00Z` });
  98. }
  99. if (endDate) {
  100. queryBuilder.andWhere('order.createTime <= :endDate', { endDate: `${endDate}T23:59:59Z` });
  101. }
  102. // 获取总数
  103. const total = await queryBuilder.getCount();
  104. // 获取数据
  105. const data = await queryBuilder
  106. .skip((page - 1) * limit)
  107. .take(limit)
  108. .orderBy('order.createTime', 'DESC')
  109. .getMany();
  110. // 获取每个订单的人员数量
  111. const orderIds = data.map(order => order.id);
  112. let personCounts: Array<{ orderId: number; count: number }> = [];
  113. if (orderIds.length > 0) {
  114. const personCountQuery = await this.orderPersonRepository
  115. .createQueryBuilder('orderPerson')
  116. .select('orderPerson.orderId', 'orderId')
  117. .addSelect('COUNT(orderPerson.id)', 'count')
  118. .where('orderPerson.orderId IN (:...orderIds)', { orderIds })
  119. .groupBy('orderPerson.orderId')
  120. .getRawMany();
  121. personCounts = personCountQuery.map(item => ({
  122. orderId: item.orderId,
  123. count: parseInt(item.count)
  124. }));
  125. }
  126. // 格式化返回数据
  127. const formattedData = data.map(order => {
  128. const personCount = personCounts.find(pc => pc.orderId === order.id)?.count || 0;
  129. return {
  130. id: order.id,
  131. orderName: order.orderName,
  132. platformId: order.platformId,
  133. companyId: order.companyId,
  134. channelId: order.channelId,
  135. expectedStartDate: order.expectedStartDate,
  136. actualStartDate: order.actualStartDate,
  137. actualEndDate: order.actualEndDate,
  138. orderStatus: order.orderStatus,
  139. workStatus: order.workStatus,
  140. createTime: order.createTime,
  141. updateTime: order.updateTime,
  142. personCount
  143. };
  144. });
  145. return { data: formattedData, total };
  146. }
  147. /**
  148. * 查询单个订单详情 - 自定义方法
  149. */
  150. async findOne(id: number): Promise<any | null> {
  151. const order = await this.repository.findOne({
  152. where: { id },
  153. relations: ['orderPersons', 'orderPersons.person']
  154. });
  155. if (!order) {
  156. return null;
  157. }
  158. // 获取订单人员详情(这里简化处理,实际可能需要关联更多信息)
  159. const orderPersons = order.orderPersons || [];
  160. return {
  161. id: order.id,
  162. orderName: order.orderName,
  163. platformId: order.platformId,
  164. companyId: order.companyId,
  165. channelId: order.channelId,
  166. expectedStartDate: order.expectedStartDate,
  167. actualStartDate: order.actualStartDate,
  168. actualEndDate: order.actualEndDate,
  169. orderStatus: order.orderStatus,
  170. workStatus: order.workStatus,
  171. createTime: order.createTime,
  172. updateTime: order.updateTime,
  173. orderPersons: orderPersons.map(person => ({
  174. id: person.id, // 注意:schema期望id字段,不是opId
  175. orderId: person.orderId,
  176. personId: person.personId,
  177. joinDate: person.joinDate,
  178. leaveDate: person.leaveDate,
  179. workStatus: person.workStatus,
  180. salaryDetail: person.salaryDetail,
  181. // 残疾人员的详细信息
  182. person: person.person ? {
  183. id: person.person.id,
  184. name: person.person.name,
  185. gender: person.person.gender,
  186. disabilityType: person.person.disabilityType,
  187. phone: person.person.phone,
  188. // 可以根据需要添加更多字段
  189. } : null
  190. }))
  191. };
  192. }
  193. /**
  194. * 订单激活 - 自定义业务方法
  195. */
  196. async activateOrder(orderId: number): Promise<boolean> {
  197. const queryRunner = this.dataSource.createQueryRunner();
  198. await queryRunner.connect();
  199. await queryRunner.startTransaction();
  200. try {
  201. // 查询订单
  202. const order = await queryRunner.manager.findOne(EmploymentOrder, {
  203. where: { id: orderId }
  204. });
  205. if (!order) {
  206. throw new Error(`订单ID ${orderId} 不存在`);
  207. }
  208. if (order.orderStatus !== OrderStatus.DRAFT) {
  209. throw new Error(`只有草稿状态的订单才能激活,当前订单状态为: ${order.orderStatus}`);
  210. }
  211. // 更新订单状态为已确认
  212. order.orderStatus = OrderStatus.CONFIRMED;
  213. order.actualStartDate = new Date();
  214. await queryRunner.manager.save(order);
  215. // 更新订单人员状态为待就业
  216. await queryRunner.manager.update(OrderPerson,
  217. { orderId: orderId },
  218. { workStatus: WorkStatus.PRE_WORKING }
  219. );
  220. await queryRunner.commitTransaction();
  221. return true;
  222. } catch (error) {
  223. await queryRunner.rollbackTransaction();
  224. throw error;
  225. } finally {
  226. await queryRunner.release();
  227. }
  228. }
  229. /**
  230. * 订单关闭 - 自定义业务方法
  231. */
  232. async closeOrder(orderId: number): Promise<boolean> {
  233. const queryRunner = this.dataSource.createQueryRunner();
  234. await queryRunner.connect();
  235. await queryRunner.startTransaction();
  236. try {
  237. // 查询订单
  238. const order = await queryRunner.manager.findOne(EmploymentOrder, {
  239. where: { id: orderId }
  240. });
  241. if (!order) {
  242. throw new Error(`订单ID ${orderId} 不存在`);
  243. }
  244. if (order.orderStatus !== OrderStatus.CONFIRMED && order.orderStatus !== OrderStatus.IN_PROGRESS) {
  245. throw new Error(`只有已确认或进行中的订单才能关闭,当前订单状态为: ${order.orderStatus}`);
  246. }
  247. // 更新订单状态为已完成
  248. order.orderStatus = OrderStatus.COMPLETED;
  249. order.actualEndDate = new Date();
  250. await queryRunner.manager.save(order);
  251. // 更新订单人员状态为已离职
  252. await queryRunner.manager.update(OrderPerson,
  253. { orderId: orderId },
  254. {
  255. workStatus: WorkStatus.RESIGNED,
  256. leaveDate: new Date()
  257. }
  258. );
  259. await queryRunner.commitTransaction();
  260. return true;
  261. } catch (error) {
  262. await queryRunner.rollbackTransaction();
  263. throw error;
  264. } finally {
  265. await queryRunner.release();
  266. }
  267. }
  268. /**
  269. * 批量添加人员到订单 - 自定义业务方法
  270. */
  271. async batchAddPersons(orderId: number, persons: Array<{
  272. personId: number;
  273. joinDate: Date;
  274. salaryDetail: number;
  275. }>): Promise<{ success: boolean; message: string; addedCount: number }> {
  276. // 验证订单是否存在
  277. const order = await this.repository.findOne({
  278. where: { id: orderId }
  279. });
  280. if (!order) {
  281. throw new Error(`订单ID ${orderId} 不存在`);
  282. }
  283. if (order.orderStatus === OrderStatus.COMPLETED || order.orderStatus === OrderStatus.CANCELLED) {
  284. throw new Error(`订单ID ${orderId} 已结束或已取消,无法添加人员`);
  285. }
  286. // 检查哪些人员已经在订单中
  287. const existingPersons = await this.orderPersonRepository.find({
  288. where: { orderId }
  289. });
  290. const existingPersonIds = existingPersons.map(p => p.personId);
  291. // 过滤掉已存在的人员
  292. const newPersons = persons.filter(p => !existingPersonIds.includes(p.personId));
  293. if (newPersons.length === 0) {
  294. return { success: true, message: '所有人员都已在此订单中', addedCount: 0 };
  295. }
  296. // 使用事务添加新人员
  297. const queryRunner = this.dataSource.createQueryRunner();
  298. await queryRunner.connect();
  299. await queryRunner.startTransaction();
  300. try {
  301. // 创建订单人员关联
  302. const orderPersons = newPersons.map(person => {
  303. return queryRunner.manager.create(OrderPerson, {
  304. orderId: orderId,
  305. personId: person.personId,
  306. joinDate: person.joinDate,
  307. workStatus: WorkStatus.NOT_WORKING,
  308. salaryDetail: person.salaryDetail,
  309. });
  310. });
  311. await queryRunner.manager.save(orderPersons);
  312. await queryRunner.commitTransaction();
  313. return {
  314. success: true,
  315. message: `成功添加 ${newPersons.length} 个人员到订单`,
  316. addedCount: newPersons.length
  317. };
  318. } catch (error) {
  319. await queryRunner.rollbackTransaction();
  320. throw error;
  321. } finally {
  322. await queryRunner.release();
  323. }
  324. }
  325. /**
  326. * 验证文件ID是否存在
  327. */
  328. async validateFileId(fileId: number): Promise<boolean> {
  329. try {
  330. const file = await this.fileRepository.findOne({ where: { id: fileId } });
  331. return !!file;
  332. } catch (error) {
  333. console.error('验证文件ID失败:', error);
  334. return false;
  335. }
  336. }
  337. /**
  338. * 创建订单人员资产 - 自定义业务方法
  339. */
  340. async createOrderPersonAsset(data: {
  341. orderId: number;
  342. personId: number;
  343. assetType: AssetType;
  344. assetFileType: AssetFileType;
  345. fileId: number;
  346. relatedTime?: Date;
  347. }): Promise<OrderPersonAsset> {
  348. // 验证文件ID是否存在
  349. const fileExists = await this.validateFileId(data.fileId);
  350. if (!fileExists) {
  351. throw new Error('文件不存在');
  352. }
  353. // 验证订单和人员关联是否存在
  354. const orderPerson = await this.orderPersonRepository.findOne({
  355. where: { orderId: data.orderId, personId: data.personId }
  356. });
  357. if (!orderPerson) {
  358. throw new Error(`人员ID ${data.personId} 不在订单ID ${data.orderId} 中`);
  359. }
  360. // 创建资产记录
  361. const asset = new OrderPersonAsset({
  362. orderId: data.orderId,
  363. personId: data.personId,
  364. assetType: data.assetType,
  365. assetFileType: data.assetFileType,
  366. fileId: data.fileId,
  367. relatedTime: data.relatedTime || new Date()
  368. });
  369. return await this.orderPersonAssetRepository.save(asset);
  370. }
  371. /**
  372. * 查询订单人员资产 - 自定义业务方法
  373. */
  374. async queryOrderPersonAsset(query: {
  375. orderId?: number;
  376. personId?: number;
  377. assetType?: string;
  378. assetFileType?: string;
  379. page?: number;
  380. limit?: number;
  381. }): Promise<{ data: OrderPersonAsset[]; total: number }> {
  382. const {
  383. orderId,
  384. personId,
  385. assetType,
  386. assetFileType,
  387. page = 1,
  388. limit = 10
  389. } = query;
  390. const queryBuilder = this.orderPersonAssetRepository.createQueryBuilder('asset');
  391. // 构建查询条件
  392. if (orderId) {
  393. queryBuilder.andWhere('asset.orderId = :orderId', { orderId });
  394. }
  395. if (personId) {
  396. queryBuilder.andWhere('asset.personId = :personId', { personId });
  397. }
  398. if (assetType) {
  399. queryBuilder.andWhere('asset.assetType = :assetType', { assetType });
  400. }
  401. if (assetFileType) {
  402. queryBuilder.andWhere('asset.assetFileType = :assetFileType', { assetFileType });
  403. }
  404. // 获取总数
  405. const total = await queryBuilder.getCount();
  406. // 获取数据,加载文件关系
  407. const data = await queryBuilder
  408. .leftJoinAndSelect('asset.file', 'file')
  409. .skip((page - 1) * limit)
  410. .take(limit)
  411. .orderBy('asset.updateTime', 'DESC')
  412. .getMany();
  413. return { data, total };
  414. }
  415. /**
  416. * 删除订单人员资产 - 自定义业务方法
  417. */
  418. async deleteOrderPersonAsset(id: number): Promise<{ success: boolean; message: string }> {
  419. const asset = await this.orderPersonAssetRepository.findOne({
  420. where: { id }
  421. });
  422. if (!asset) {
  423. throw new Error(`资产ID ${id} 不存在`);
  424. }
  425. await this.orderPersonAssetRepository.delete({ id });
  426. return {
  427. success: true,
  428. message: `成功删除资产ID ${id}`
  429. };
  430. }
  431. /**
  432. * 更新订单人员工作状态 - 自定义业务方法
  433. */
  434. async updatePersonWorkStatus(orderId: number, personId: number, workStatus: WorkStatus): Promise<{ success: boolean; message: string }> {
  435. // 验证订单是否存在
  436. const order = await this.repository.findOne({
  437. where: { id: orderId }
  438. });
  439. if (!order) {
  440. throw new Error(`订单ID ${orderId} 不存在`);
  441. }
  442. // 验证工作状态是否有效
  443. if (!Object.values(WorkStatus).includes(workStatus)) {
  444. throw new Error('工作状态无效');
  445. }
  446. // 查找订单人员关联
  447. const orderPerson = await this.orderPersonRepository.findOne({
  448. where: { orderId, personId }
  449. });
  450. if (!orderPerson) {
  451. throw new Error(`人员ID ${personId} 不在订单ID ${orderId} 中`);
  452. }
  453. // 更新工作状态
  454. orderPerson.workStatus = workStatus;
  455. // 根据工作状态设置相关日期
  456. if (workStatus === WorkStatus.WORKING && !orderPerson.actualStartDate) {
  457. orderPerson.actualStartDate = new Date();
  458. } else if (workStatus === WorkStatus.RESIGNED && !orderPerson.leaveDate) {
  459. orderPerson.leaveDate = new Date();
  460. }
  461. await this.orderPersonRepository.save(orderPerson);
  462. return {
  463. success: true,
  464. message: `成功更新人员 ${personId} 的工作状态为 ${workStatus}`
  465. };
  466. }
  467. }