Browse Source

feat(order-module): 移植订单管理模块为独立包

创建订单管理模块包 @d8d/allin-order-module,包含:
1. 实体转换:EmploymentOrder、OrderPerson、OrderPersonAsset
2. 枚举集成:使用@d8d/allin-enums包中的OrderStatus和WorkStatus枚举
3. 文件模块集成:OrderPersonAsset实体添加fileId字段引用File实体
4. 循环依赖处理:使用personId代替DisabledPerson直接引用
5. 服务层转换:OrderService继承GenericCrudService
6. 路由层转换:Hono路由实现,包含自定义路由和CRUD路由
7. 集成测试:31个测试用例覆盖订单全生命周期

当前状态:基本完成,集成测试调试中
问题:Schema导出不一致导致创建订单API返回500错误

🤖 Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 5 days ago
parent
commit
fdb876017d

+ 84 - 0
allin-packages/order-module/package.json

@@ -0,0 +1,84 @@
+{
+  "name": "@d8d/allin-order-module",
+  "version": "1.0.0",
+  "description": "订单管理模块 - 提供订单、订单人员、订单人员资产的完整CRUD功能,集成文件模块和枚举包",
+  "type": "module",
+  "main": "src/index.ts",
+  "types": "src/index.ts",
+  "exports": {
+    ".": {
+      "types": "./src/index.ts",
+      "import": "./src/index.ts",
+      "require": "./src/index.ts"
+    },
+    "./services": {
+      "types": "./src/services/index.ts",
+      "import": "./src/services/index.ts",
+      "require": "./src/services/index.ts"
+    },
+    "./schemas": {
+      "types": "./src/schemas/index.ts",
+      "import": "./src/schemas/index.ts",
+      "require": "./src/schemas/index.ts"
+    },
+    "./routes": {
+      "types": "./src/routes/index.ts",
+      "import": "./src/routes/index.ts",
+      "require": "./src/routes/index.ts"
+    },
+    "./entities": {
+      "types": "./src/entities/index.ts",
+      "import": "./src/entities/index.ts",
+      "require": "./src/entities/index.ts"
+    }
+  },
+  "files": [
+    "src"
+  ],
+  "scripts": {
+    "build": "tsc",
+    "dev": "tsc --watch",
+    "test": "vitest run",
+    "test:watch": "vitest",
+    "test:integration": "vitest run tests/integration",
+    "test:coverage": "vitest run --coverage",
+    "lint": "eslint src --ext .ts,.tsx",
+    "typecheck": "tsc --noEmit"
+  },
+  "dependencies": {
+    "@d8d/shared-types": "workspace:*",
+    "@d8d/shared-utils": "workspace:*",
+    "@d8d/shared-crud": "workspace:*",
+    "@d8d/allin-enums": "workspace:*",
+    "@d8d/file-module": "workspace:*",
+    "@d8d/auth-module": "workspace:*",
+    "@d8d/user-module": "workspace:*",
+    "@hono/zod-openapi": "^1.0.2",
+    "typeorm": "^0.3.20",
+    "zod": "^4.1.12"
+  },
+  "devDependencies": {
+    "@types/node": "^22.10.2",
+    "typescript": "^5.8.3",
+    "vitest": "^3.2.4",
+    "@d8d/shared-test-util": "workspace:*",
+    "@typescript-eslint/eslint-plugin": "^8.18.1",
+    "@typescript-eslint/parser": "^8.18.1",
+    "eslint": "^9.17.0"
+  },
+  "peerDependencies": {
+    "hono": "^4.8.5"
+  },
+  "keywords": [
+    "order",
+    "management",
+    "employment",
+    "crud",
+    "api",
+    "file-module",
+    "enums",
+    "allin-module"
+  ],
+  "author": "D8D Team",
+  "license": "MIT"
+}

+ 113 - 0
allin-packages/order-module/src/entities/employment-order.entity.ts

@@ -0,0 +1,113 @@
+import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
+import { OrderPerson } from './order-person.entity';
+import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
+
+@Entity('employment_order', { comment: '用工订单表' })
+export class EmploymentOrder {
+  @PrimaryGeneratedColumn({
+    name: 'order_id',
+    type: 'int',
+    unsigned: true,
+    comment: '订单ID'
+  })
+  id!: number;
+
+  @Column({
+    name: 'order_name',
+    type: 'varchar',
+    length: 50,
+    nullable: true,
+    comment: '订单名称'
+  })
+  orderName?: string;
+
+  @Column({
+    name: 'platform_id',
+    type: 'int',
+    nullable: false,
+    comment: '用人平台ID'
+  })
+  platformId!: number;
+
+  @Column({
+    name: 'company_id',
+    type: 'int',
+    nullable: false,
+    comment: '用人单位ID'
+  })
+  companyId!: number;
+
+  @Column({
+    name: 'channel_id',
+    type: 'int',
+    nullable: true,
+    comment: '渠道ID'
+  })
+  channelId?: number;
+
+  @Column({
+    name: 'expected_start_date',
+    type: 'date',
+    nullable: true,
+    comment: '预计开始日期'
+  })
+  expectedStartDate?: Date;
+
+  @Column({
+    name: 'actual_start_date',
+    type: 'date',
+    nullable: true,
+    comment: '实际开始日期'
+  })
+  actualStartDate?: Date;
+
+  @Column({
+    name: 'actual_end_date',
+    type: 'date',
+    nullable: true,
+    comment: '实际结束日期'
+  })
+  actualEndDate?: Date;
+
+  @Column({
+    name: 'order_status',
+    type: 'enum',
+    enum: OrderStatus,
+    default: OrderStatus.DRAFT,
+    comment: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消'
+  })
+  orderStatus!: OrderStatus;
+
+  @Column({
+    name: 'work_status',
+    type: 'enum',
+    enum: WorkStatus,
+    default: WorkStatus.NOT_WORKING,
+    comment: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职'
+  })
+  workStatus!: WorkStatus;
+
+  @CreateDateColumn({
+    name: 'create_time',
+    type: 'timestamp',
+    nullable: false,
+    comment: '创建时间'
+  })
+  createTime!: Date;
+
+  @UpdateDateColumn({
+    name: 'update_time',
+    type: 'timestamp',
+    nullable: false,
+    comment: '更新时间'
+  })
+  updateTime!: Date;
+
+  // 关系定义
+  @OneToMany(() => OrderPerson, (orderPerson) => orderPerson.order)
+  orderPersons!: OrderPerson[];
+
+  constructor(partial?: Partial<EmploymentOrder>) {
+    Object.assign(this, partial);
+  }
+}

+ 3 - 0
allin-packages/order-module/src/entities/index.ts

@@ -0,0 +1,3 @@
+export { EmploymentOrder } from './employment-order.entity';
+export { OrderPerson } from './order-person.entity';
+export { OrderPersonAsset, AssetType, AssetFileType } from './order-person-asset.entity';

+ 109 - 0
allin-packages/order-module/src/entities/order-person-asset.entity.ts

@@ -0,0 +1,109 @@
+import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
+import { EmploymentOrder } from './employment-order.entity';
+import { File } from '@d8d/file-module';
+
+// 资产类型枚举 - 模块内部枚举
+export enum AssetType {
+  TAX = 'tax',
+  SALARY = 'salary',
+  JOB_RESULT = 'job_result',
+  CONTRACT_SIGN = 'contract_sign',
+  DISABILITY_CERT = 'disability_cert',
+  OTHER = 'other',
+}
+
+// 资产文件类型枚举 - 模块内部枚举
+export enum AssetFileType {
+  IMAGE = 'image',
+  VIDEO = 'video',
+}
+
+@Entity('order_person_asset', { comment: '订单人员资产表' })
+export class OrderPersonAsset {
+  @PrimaryGeneratedColumn({
+    name: 'op_id',
+    type: 'int',
+    unsigned: true,
+    comment: '关联ID'
+  })
+  id!: number;
+
+  @Column({
+    name: 'order_id',
+    type: 'int',
+    nullable: false,
+    comment: '订单ID'
+  })
+  orderId!: number;
+
+  @Column({
+    name: 'person_id',
+    type: 'int',
+    nullable: false,
+    comment: '残疾人ID'
+  })
+  personId!: number;
+
+  @Column({
+    name: 'asset_type',
+    type: 'varchar',
+    length: 50,
+    nullable: false,
+    comment: '资产类型:tax-税务, salary-薪资, job_result-工作成果, contract_sign-合同签署, disability_cert-残疾证明, other-其他'
+  })
+  assetType!: AssetType;
+
+  @Column({
+    name: 'asset_file_type',
+    type: 'varchar',
+    length: 50,
+    nullable: false,
+    comment: '资产文件类型:image-图片, video-视频'
+  })
+  assetFileType!: AssetFileType;
+
+  @Column({
+    name: 'file_id',
+    type: 'int',
+    nullable: false,
+    comment: '文件ID,引用files表'
+  })
+  fileId!: number;
+
+  @Column({
+    name: 'related_time',
+    type: 'timestamp',
+    nullable: false,
+    default: () => 'CURRENT_TIMESTAMP',
+    comment: '关联时间'
+  })
+  relatedTime!: Date;
+
+  @UpdateDateColumn({
+    name: 'update_time',
+    type: 'timestamp',
+    nullable: false,
+    comment: '更新时间'
+  })
+  updateTime!: Date;
+
+  // 关系定义 - 与订单的关联
+  @ManyToOne(() => EmploymentOrder, { onDelete: 'CASCADE' })
+  @JoinColumn({ name: 'order_id' })
+  order!: EmploymentOrder;
+
+  // 注意:移除对DisabledPerson的直接引用,改为使用personId字段
+  // 原代码:
+  // @ManyToOne(() => DisabledPerson, { onDelete: 'CASCADE' })
+  // @JoinColumn({ name: 'person_id' })
+  // person: DisabledPerson;
+
+  // 关系定义 - 与文件的关联
+  @ManyToOne(() => File, { onDelete: 'CASCADE' })
+  @JoinColumn({ name: 'file_id' })
+  file!: File;
+
+  constructor(partial?: Partial<OrderPersonAsset>) {
+    Object.assign(this, partial);
+  }
+}

+ 80 - 0
allin-packages/order-module/src/entities/order-person.entity.ts

@@ -0,0 +1,80 @@
+import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn } from 'typeorm';
+import { EmploymentOrder } from './employment-order.entity';
+import { WorkStatus } from '@d8d/allin-enums';
+
+@Entity('order_person', { comment: '订单人员关联表' })
+export class OrderPerson {
+  @PrimaryGeneratedColumn({
+    name: 'op_id',
+    type: 'int',
+    unsigned: true,
+    comment: '关联ID'
+  })
+  id!: number;
+
+  @Column({
+    name: 'order_id',
+    type: 'int',
+    nullable: false,
+    comment: '订单ID'
+  })
+  orderId!: number;
+
+  @Column({
+    name: 'person_id',
+    type: 'int',
+    nullable: false,
+    comment: '残疾人ID'
+  })
+  personId!: number;
+
+  @Column({
+    name: 'join_date',
+    type: 'date',
+    nullable: false,
+    comment: '入职日期'
+  })
+  joinDate!: Date;
+
+  @Column({
+    name: 'leave_date',
+    type: 'date',
+    nullable: true,
+    comment: '离职日期'
+  })
+  leaveDate?: Date;
+
+  @Column({
+    name: 'work_status',
+    type: 'enum',
+    enum: WorkStatus,
+    default: WorkStatus.NOT_WORKING,
+    comment: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职'
+  })
+  workStatus!: WorkStatus;
+
+  @Column({
+    name: 'salary_detail',
+    type: 'decimal',
+    precision: 10,
+    scale: 2,
+    nullable: false,
+    comment: '个人薪资'
+  })
+  salaryDetail!: number;
+
+  // 关系定义 - 与订单的关联
+  @ManyToOne(() => EmploymentOrder, (order) => order.orderPersons, { onDelete: 'CASCADE' })
+  @JoinColumn({ name: 'order_id' })
+  order!: EmploymentOrder;
+
+  // 注意:移除对DisabledPerson的直接引用,改为使用personId字段
+  // 原代码:
+  // @ManyToOne(() => DisabledPerson, { onDelete: 'CASCADE' })
+  // @JoinColumn({ name: 'person_id' })
+  // person: DisabledPerson;
+
+  constructor(partial?: Partial<OrderPerson>) {
+    Object.assign(this, partial);
+  }
+}

+ 7 - 0
allin-packages/order-module/src/index.ts

@@ -0,0 +1,7 @@
+export * from './entities/employment-order.entity';
+export * from './entities/order-person.entity';
+export * from './entities/order-person-asset.entity';
+export * from './services/order.service';
+export * from './schemas/order.schema';
+export * from './routes/order.routes';
+export { orderRoutes } from './routes/order.routes';

+ 19 - 0
allin-packages/order-module/src/routes/order-crud.routes.ts

@@ -0,0 +1,19 @@
+import { createCrudRoutes } from '@d8d/shared-crud';
+import { authMiddleware } from '@d8d/auth-module';
+import { EmploymentOrder } from '../entities/employment-order.entity';
+import { EmploymentOrderSchema, CreateOrderSchema, UpdateOrderSchema } from '../schemas/order.schema';
+
+export const orderCrudRoutes = createCrudRoutes({
+  entity: EmploymentOrder,
+  createSchema: CreateOrderSchema,
+  updateSchema: UpdateOrderSchema,
+  getSchema: EmploymentOrderSchema,
+  listSchema: EmploymentOrderSchema,
+  searchFields: ['orderName', 'platformId', 'companyId', 'channelId'],
+  middleware: [authMiddleware],
+  readOnly: true, // 设置为只读,因为创建、更新、删除操作通过自定义路由处理
+  userTracking: {
+    createdByField: 'createdBy',
+    updatedByField: 'updatedBy'
+  }
+});

+ 795 - 0
allin-packages/order-module/src/routes/order-custom.routes.ts

@@ -0,0 +1,795 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { AppDataSource, ErrorSchema, parseWithAwait } from '@d8d/shared-utils';
+import { authMiddleware } from '@d8d/auth-module';
+import { AuthContext } from '@d8d/shared-types';
+import { OrderService } from '../services/order.service';
+import {
+  EmploymentOrderSchema,
+  CreateOrderSchema,
+  UpdateOrderSchema,
+  QueryOrderSchema,
+  BatchAddPersonsSchema,
+  CreateOrderPersonAssetSchema,
+  QueryOrderPersonAssetSchema
+} from '../schemas/order.schema';
+import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
+
+// 创建订单路由
+const createOrderRoute = createRoute({
+  method: 'post',
+  path: '/create',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: CreateOrderSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '订单创建成功',
+      content: {
+        'application/json': { schema: EmploymentOrderSchema }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '创建订单失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 更新订单路由
+const updateOrderRoute = createRoute({
+  method: 'put',
+  path: '/update/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      id: z.coerce.number().int().positive().openapi({
+        param: { name: 'id', in: 'path' },
+        example: 1,
+        description: '订单ID'
+      })
+    }),
+    body: {
+      content: {
+        'application/json': { schema: UpdateOrderSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '订单更新成功',
+      content: {
+        'application/json': { schema: EmploymentOrderSchema }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '订单不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '更新订单失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 删除订单路由
+const deleteOrderRoute = createRoute({
+  method: 'delete',
+  path: '/delete/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      id: z.coerce.number().int().positive().openapi({
+        param: { name: 'id', in: 'path' },
+        example: 1,
+        description: '订单ID'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '订单删除成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            success: z.boolean().openapi({ description: '是否成功' })
+          })
+        }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '订单不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '删除订单失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 分页查询订单路由
+const getAllOrdersRoute = createRoute({
+  method: 'get',
+  path: '/list',
+  middleware: [authMiddleware],
+  request: {
+    query: QueryOrderSchema
+  },
+  responses: {
+    200: {
+      description: '获取订单列表成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            data: z.array(EmploymentOrderSchema).openapi({ description: '订单列表' }),
+            total: z.number().int().openapi({ description: '总记录数' })
+          })
+        }
+      }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '获取订单列表失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 获取单个订单详情路由
+const getOrderByIdRoute = createRoute({
+  method: 'get',
+  path: '/detail/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      id: z.coerce.number().int().positive().openapi({
+        param: { name: 'id', in: 'path' },
+        example: 1,
+        description: '订单ID'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '获取订单详情成功',
+      content: {
+        'application/json': { schema: EmploymentOrderSchema.nullable() }
+      }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '订单不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '获取订单详情失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 激活订单路由
+const activateOrderRoute = createRoute({
+  method: 'post',
+  path: '/activate/{orderId}',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      orderId: z.coerce.number().int().positive().openapi({
+        param: { name: 'orderId', in: 'path' },
+        example: 1,
+        description: '订单ID'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '订单激活成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            success: z.boolean().openapi({ description: '是否成功' })
+          })
+        }
+      }
+    },
+    400: {
+      description: '参数错误或订单状态不符合要求',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '订单不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '激活订单失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 关闭订单路由
+const closeOrderRoute = createRoute({
+  method: 'post',
+  path: '/close/{orderId}',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      orderId: z.coerce.number().int().positive().openapi({
+        param: { name: 'orderId', in: 'path' },
+        example: 1,
+        description: '订单ID'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '订单关闭成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            success: z.boolean().openapi({ description: '是否成功' })
+          })
+        }
+      }
+    },
+    400: {
+      description: '参数错误或订单状态不符合要求',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '订单不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '关闭订单失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 批量添加人员到订单路由
+const batchAddPersonsRoute = createRoute({
+  method: 'post',
+  path: '/{orderId}/persons/batch',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      orderId: z.coerce.number().int().positive().openapi({
+        param: { name: 'orderId', in: 'path' },
+        example: 1,
+        description: '订单ID'
+      })
+    }),
+    body: {
+      content: {
+        'application/json': { schema: BatchAddPersonsSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '批量添加人员成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            success: z.boolean().openapi({ description: '是否成功' }),
+            message: z.string().openapi({ description: '操作结果消息' }),
+            addedCount: z.number().int().openapi({ description: '实际添加的人员数量' })
+          })
+        }
+      }
+    },
+    400: {
+      description: '参数错误或订单状态不符合要求',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '订单不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '批量添加人员失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 创建订单人员资产路由
+const createOrderPersonAssetRoute = createRoute({
+  method: 'post',
+  path: '/assets/create',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: CreateOrderPersonAssetSchema }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '订单人员资产创建成功',
+      content: {
+        'application/json': { schema: z.object({
+          id: z.number().int().openapi({ description: '资产ID' }),
+          orderId: z.number().int().openapi({ description: '订单ID' }),
+          personId: z.number().int().openapi({ description: '人员ID' }),
+          assetType: z.string().openapi({ description: '资产类型' }),
+          assetFileType: z.string().openapi({ description: '资产文件类型' }),
+          fileId: z.number().int().openapi({ description: '文件ID' }),
+          relatedTime: z.string().datetime().openapi({ description: '关联时间' }),
+          createTime: z.string().datetime().openapi({ description: '创建时间' }),
+          updateTime: z.string().datetime().openapi({ description: '更新时间' })
+        }) }
+      }
+    },
+    400: {
+      description: '参数错误或文件不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '订单或人员不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '创建订单人员资产失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 查询订单人员资产路由
+const queryOrderPersonAssetRoute = createRoute({
+  method: 'get',
+  path: '/assets/query',
+  middleware: [authMiddleware],
+  request: {
+    query: QueryOrderPersonAssetSchema
+  },
+  responses: {
+    200: {
+      description: '查询订单人员资产成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            data: z.array(z.object({
+              id: z.number().int().openapi({ description: '资产ID' }),
+              orderId: z.number().int().openapi({ description: '订单ID' }),
+              personId: z.number().int().openapi({ description: '人员ID' }),
+              assetType: z.string().openapi({ description: '资产类型' }),
+              assetFileType: z.string().openapi({ description: '资产文件类型' }),
+              fileId: z.number().int().openapi({ description: '文件ID' }),
+              relatedTime: z.string().datetime().openapi({ description: '关联时间' }),
+              createTime: z.string().datetime().openapi({ description: '创建时间' }),
+              updateTime: z.string().datetime().openapi({ description: '更新时间' })
+            })).openapi({ description: '资产列表' }),
+            total: z.number().int().openapi({ description: '总记录数' })
+          })
+        }
+      }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '查询订单人员资产失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 删除订单人员资产路由
+const deleteOrderPersonAssetRoute = createRoute({
+  method: 'delete',
+  path: '/assets/delete/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      id: z.coerce.number().int().positive().openapi({
+        param: { name: 'id', in: 'path' },
+        example: 1,
+        description: '资产ID'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '删除订单人员资产成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            success: z.boolean().openapi({ description: '是否成功' }),
+            message: z.string().openapi({ description: '操作结果消息' })
+          })
+        }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    401: {
+      description: '认证失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '资产不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '删除订单人员资产失败',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>()
+  // 创建订单
+  .openapi(createOrderRoute, async (c) => {
+    try {
+      const data = c.req.valid('json');
+      const user = c.get('user');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.create(data, user?.id);
+
+      return c.json(await parseWithAwait(EmploymentOrderSchema, result), 200);
+    } catch (error) {
+      if (error instanceof z.ZodError) {
+        return c.json({
+          code: 400,
+          message: '参数错误',
+          errors: error.issues
+        }, 400);
+      }
+
+      // 处理枚举验证错误
+      if (error instanceof Error && (
+        error.message.includes('订单状态无效') ||
+        error.message.includes('工作状态无效')
+      )) {
+        return c.json({
+          code: 400,
+          message: error.message
+        }, 400);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '创建订单失败'
+      }, 500);
+    }
+  })
+  // 更新订单
+  .openapi(updateOrderRoute, async (c) => {
+    try {
+      const { id } = c.req.valid('param');
+      const data = c.req.valid('json');
+      const user = c.get('user');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.update(id, data, user?.id);
+
+      if (!result) {
+        return c.json({ code: 404, message: '订单不存在' }, 404);
+      }
+
+      const validatedResult = await parseWithAwait(EmploymentOrderSchema, result);
+      return c.json(validatedResult, 200);
+    } catch (error) {
+      if (error instanceof z.ZodError) {
+        return c.json({
+          code: 400,
+          message: '参数错误',
+          errors: error.issues
+        }, 400);
+      }
+
+      // 处理枚举验证错误
+      if (error instanceof Error && (
+        error.message.includes('订单状态无效') ||
+        error.message.includes('工作状态无效')
+      )) {
+        return c.json({
+          code: 400,
+          message: error.message
+        }, 400);
+      }
+
+      // 处理订单不存在错误
+      if (error instanceof Error && error.message.includes('订单不存在')) {
+        return c.json({
+          code: 404,
+          message: '订单不存在'
+        }, 404);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '更新订单失败'
+      }, 500);
+    }
+  })
+  // 删除订单
+  .openapi(deleteOrderRoute, async (c) => {
+    try {
+      const { id } = c.req.valid('param');
+      const user = c.get('user');
+      const orderService = new OrderService(AppDataSource);
+
+      const success = await orderService.delete(id, user?.id);
+
+      if (!success) {
+        return c.json({ code: 404, message: '订单不存在' }, 404);
+      }
+
+      return c.json({ success }, 200);
+    } catch (error) {
+      if (error instanceof z.ZodError) {
+        return c.json({
+          code: 400,
+          message: '参数错误',
+          errors: error.issues
+        }, 400);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '删除订单失败'
+      }, 500);
+    }
+  })
+  // 获取所有订单
+  .openapi(getAllOrdersRoute, async (c) => {
+    try {
+      const query = c.req.valid('query');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.findAll(query);
+
+      return c.json(result, 200);
+    } catch (error) {
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '获取订单列表失败'
+      }, 500);
+    }
+  })
+  // 获取单个订单详情
+  .openapi(getOrderByIdRoute, async (c) => {
+    try {
+      const { id } = c.req.valid('param');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.findOne(id);
+
+      if (!result) {
+        return c.json({ code: 404, message: '订单不存在' }, 404);
+      }
+
+      const validatedResult = await parseWithAwait(EmploymentOrderSchema, result);
+      return c.json(validatedResult, 200);
+    } catch (error) {
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '获取订单详情失败'
+      }, 500);
+    }
+  })
+  // 激活订单
+  .openapi(activateOrderRoute, async (c) => {
+    try {
+      const { orderId } = c.req.valid('param');
+      const orderService = new OrderService(AppDataSource);
+
+      const success = await orderService.activateOrder(orderId);
+
+      return c.json({ success }, 200);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes('订单ID')) {
+        return c.json({
+          code: 404,
+          message: error.message
+        }, 404);
+      }
+
+      if (error instanceof Error && error.message.includes('只有草稿状态的订单才能激活')) {
+        return c.json({
+          code: 400,
+          message: error.message
+        }, 400);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '激活订单失败'
+      }, 500);
+    }
+  })
+  // 关闭订单
+  .openapi(closeOrderRoute, async (c) => {
+    try {
+      const { orderId } = c.req.valid('param');
+      const orderService = new OrderService(AppDataSource);
+
+      const success = await orderService.closeOrder(orderId);
+
+      return c.json({ success }, 200);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes('订单ID')) {
+        return c.json({
+          code: 404,
+          message: error.message
+        }, 404);
+      }
+
+      if (error instanceof Error && error.message.includes('只有已确认或进行中的订单才能关闭')) {
+        return c.json({
+          code: 400,
+          message: error.message
+        }, 400);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '关闭订单失败'
+      }, 500);
+    }
+  })
+  // 批量添加人员到订单
+  .openapi(batchAddPersonsRoute, async (c) => {
+    try {
+      const { orderId } = c.req.valid('param');
+      const { persons } = c.req.valid('json');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.batchAddPersons(orderId, persons);
+
+      return c.json(result, 200);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes('订单ID')) {
+        return c.json({
+          code: 404,
+          message: error.message
+        }, 404);
+      }
+
+      if (error instanceof Error && error.message.includes('已结束或已取消')) {
+        return c.json({
+          code: 400,
+          message: error.message
+        }, 400);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '批量添加人员失败'
+      }, 500);
+    }
+  })
+  // 创建订单人员资产
+  .openapi(createOrderPersonAssetRoute, async (c) => {
+    try {
+      const data = c.req.valid('json');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.createOrderPersonAsset(data);
+
+      return c.json(result, 200);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes('文件不存在')) {
+        return c.json({
+          code: 400,
+          message: error.message
+        }, 400);
+      }
+
+      if (error instanceof Error && error.message.includes('人员ID')) {
+        return c.json({
+          code: 404,
+          message: error.message
+        }, 404);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '创建订单人员资产失败'
+      }, 500);
+    }
+  })
+  // 查询订单人员资产
+  .openapi(queryOrderPersonAssetRoute, async (c) => {
+    try {
+      const query = c.req.valid('query');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.queryOrderPersonAsset(query);
+
+      return c.json(result, 200);
+    } catch (error) {
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '查询订单人员资产失败'
+      }, 500);
+    }
+  })
+  // 删除订单人员资产
+  .openapi(deleteOrderPersonAssetRoute, async (c) => {
+    try {
+      const { id } = c.req.valid('param');
+      const orderService = new OrderService(AppDataSource);
+
+      const result = await orderService.deleteOrderPersonAsset(id);
+
+      return c.json(result, 200);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes('资产ID')) {
+        return c.json({
+          code: 404,
+          message: error.message
+        }, 404);
+      }
+
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '删除订单人员资产失败'
+      }, 500);
+    }
+  });
+
+export default app;

+ 15 - 0
allin-packages/order-module/src/routes/order.routes.ts

@@ -0,0 +1,15 @@
+import { OpenAPIHono } from '@hono/zod-openapi';
+import { AuthContext } from '@d8d/shared-types';
+import orderCustomRoutes from './order-custom.routes';
+import { orderCrudRoutes } from './order-crud.routes';
+
+/**
+ * 订单管理模块路由
+ * 聚合自定义路由和CRUD路由
+ */
+const orderRoutes = new OpenAPIHono<AuthContext>()
+  .route('/', orderCustomRoutes)
+  .route('/', orderCrudRoutes);
+
+export { orderRoutes };
+export default orderRoutes;

+ 1 - 0
allin-packages/order-module/src/schemas/index.ts

@@ -0,0 +1 @@
+export * from './order.schema';

+ 312 - 0
allin-packages/order-module/src/schemas/order.schema.ts

@@ -0,0 +1,312 @@
+import { z } from '@hono/zod-openapi';
+import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
+import { AssetType, AssetFileType } from '../entities';
+
+// 用工订单实体Schema
+export const EmploymentOrderSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '订单ID',
+    example: 1
+  }),
+  orderName: z.string().max(50).nullable().optional().openapi({
+    description: '订单名称',
+    example: '2024年Q1用工订单'
+  }),
+  platformId: z.number().int().positive().openapi({
+    description: '用人平台ID',
+    example: 1
+  }),
+  companyId: z.number().int().positive().openapi({
+    description: '用人单位ID',
+    example: 1
+  }),
+  channelId: z.number().int().positive().nullable().optional().openapi({
+    description: '渠道ID',
+    example: 1
+  }),
+  expectedStartDate: z.coerce.date().nullable().optional().openapi({
+    description: '预计开始日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  actualStartDate: z.coerce.date().nullable().optional().openapi({
+    description: '实际开始日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  actualEndDate: z.coerce.date().nullable().optional().openapi({
+    description: '实际结束日期',
+    example: '2024-12-31T00:00:00Z'
+  }),
+  orderStatus: z.nativeEnum(OrderStatus).default(OrderStatus.DRAFT).openapi({
+    description: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消',
+    example: OrderStatus.DRAFT
+  }),
+  workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).openapi({
+    description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
+    example: WorkStatus.NOT_WORKING
+  }),
+  createTime: z.coerce.date().openapi({
+    description: '创建时间',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  updateTime: z.coerce.date().openapi({
+    description: '更新时间',
+    example: '2024-01-01T00:00:00Z'
+  })
+});
+
+// 创建用工订单DTO
+export const CreateEmploymentOrderSchema = z.object({
+  orderName: z.string().max(50).optional().openapi({
+    description: '订单名称',
+    example: '2024年Q1用工订单'
+  }),
+  platformId: z.coerce.number().int().positive().openapi({
+    description: '用人平台ID',
+    example: 1
+  }),
+  companyId: z.coerce.number().int().positive().openapi({
+    description: '用人单位ID',
+    example: 1
+  }),
+  channelId: z.coerce.number().int().positive().optional().openapi({
+    description: '渠道ID',
+    example: 1
+  }),
+  expectedStartDate: z.coerce.date().optional().openapi({
+    description: '预计开始日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  actualStartDate: z.coerce.date().optional().openapi({
+    description: '实际开始日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  actualEndDate: z.coerce.date().optional().openapi({
+    description: '实际结束日期',
+    example: '2024-12-31T00:00:00Z'
+  }),
+  orderStatus: z.nativeEnum(OrderStatus).default(OrderStatus.DRAFT).optional().openapi({
+    description: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消',
+    example: OrderStatus.DRAFT
+  }),
+  workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).optional().openapi({
+    description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
+    example: WorkStatus.NOT_WORKING
+  })
+});
+
+// 更新用工订单DTO
+export const UpdateEmploymentOrderSchema = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    description: '订单ID',
+    example: 1
+  }),
+  orderName: z.string().max(50).optional().openapi({
+    description: '订单名称',
+    example: '2024年Q1用工订单'
+  }),
+  platformId: z.coerce.number().int().positive().optional().openapi({
+    description: '用人平台ID',
+    example: 1
+  }),
+  companyId: z.coerce.number().int().positive().optional().openapi({
+    description: '用人单位ID',
+    example: 1
+  }),
+  channelId: z.coerce.number().int().positive().optional().openapi({
+    description: '渠道ID',
+    example: 1
+  }),
+  expectedStartDate: z.coerce.date().optional().openapi({
+    description: '预计开始日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  actualStartDate: z.coerce.date().optional().openapi({
+    description: '实际开始日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  actualEndDate: z.coerce.date().optional().openapi({
+    description: '实际结束日期',
+    example: '2024-12-31T00:00:00Z'
+  }),
+  orderStatus: z.nativeEnum(OrderStatus).optional().openapi({
+    description: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消',
+    example: OrderStatus.DRAFT
+  }),
+  workStatus: z.nativeEnum(WorkStatus).optional().openapi({
+    description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
+    example: WorkStatus.NOT_WORKING
+  })
+});
+
+// 订单人员关联实体Schema
+export const OrderPersonSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '关联ID',
+    example: 1
+  }),
+  orderId: z.number().int().positive().openapi({
+    description: '订单ID',
+    example: 1
+  }),
+  personId: z.number().int().positive().openapi({
+    description: '残疾人ID',
+    example: 1
+  }),
+  joinDate: z.coerce.date().openapi({
+    description: '入职日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  leaveDate: z.coerce.date().nullable().optional().openapi({
+    description: '离职日期',
+    example: '2024-12-31T00:00:00Z'
+  }),
+  workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).openapi({
+    description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
+    example: WorkStatus.NOT_WORKING
+  }),
+  salaryDetail: z.coerce.number().positive().openapi({
+    description: '个人薪资',
+    example: 5000.00
+  })
+});
+
+// 创建订单人员关联DTO
+export const CreateOrderPersonSchema = z.object({
+  orderId: z.coerce.number().int().positive().openapi({
+    description: '订单ID',
+    example: 1
+  }),
+  personId: z.coerce.number().int().positive().openapi({
+    description: '残疾人ID',
+    example: 1
+  }),
+  joinDate: z.coerce.date().openapi({
+    description: '入职日期',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  leaveDate: z.coerce.date().optional().openapi({
+    description: '离职日期',
+    example: '2024-12-31T00:00:00Z'
+  }),
+  workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).optional().openapi({
+    description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
+    example: WorkStatus.NOT_WORKING
+  }),
+  salaryDetail: z.coerce.number().positive().openapi({
+    description: '个人薪资',
+    example: 5000.00
+  })
+});
+
+// 订单人员资产实体Schema
+export const OrderPersonAssetSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '关联ID',
+    example: 1
+  }),
+  orderId: z.number().int().positive().openapi({
+    description: '订单ID',
+    example: 1
+  }),
+  personId: z.number().int().positive().openapi({
+    description: '残疾人ID',
+    example: 1
+  }),
+  assetType: z.nativeEnum(AssetType).openapi({
+    description: '资产类型:tax-税务, salary-薪资, job_result-工作成果, contract_sign-合同签署, disability_cert-残疾证明, other-其他',
+    example: AssetType.SALARY
+  }),
+  assetFileType: z.nativeEnum(AssetFileType).openapi({
+    description: '资产文件类型:image-图片, video-视频',
+    example: AssetFileType.IMAGE
+  }),
+  fileId: z.number().int().positive().openapi({
+    description: '文件ID,引用files表',
+    example: 1
+  }),
+  relatedTime: z.coerce.date().openapi({
+    description: '关联时间',
+    example: '2024-01-01T00:00:00Z'
+  }),
+  updateTime: z.coerce.date().openapi({
+    description: '更新时间',
+    example: '2024-01-01T00:00:00Z'
+  })
+});
+
+// 创建订单人员资产DTO
+export const CreateOrderPersonAssetSchema = z.object({
+  orderId: z.coerce.number().int().positive().openapi({
+    description: '订单ID',
+    example: 1
+  }),
+  personId: z.coerce.number().int().positive().openapi({
+    description: '残疾人ID',
+    example: 1
+  }),
+  assetType: z.nativeEnum(AssetType).openapi({
+    description: '资产类型:tax-税务, salary-薪资, job_result-工作成果, contract_sign-合同签署, disability_cert-残疾证明, other-其他',
+    example: AssetType.SALARY
+  }),
+  assetFileType: z.nativeEnum(AssetFileType).openapi({
+    description: '资产文件类型:image-图片, video-视频',
+    example: AssetFileType.IMAGE
+  }),
+  fileId: z.coerce.number().int().positive().openapi({
+    description: '文件ID,引用files表',
+    example: 1
+  }),
+  relatedTime: z.coerce.date().optional().openapi({
+    description: '关联时间',
+    example: '2024-01-01T00:00:00Z'
+  })
+});
+
+// 分页查询参数Schema
+export const PaginationQuerySchema = z.object({
+  skip: z.coerce.number().int().min(0).default(0).optional().openapi({
+    description: '跳过记录数',
+    example: 0
+  }),
+  take: z.coerce.number().int().min(1).max(100).default(10).optional().openapi({
+    description: '获取记录数',
+    example: 10
+  })
+});
+
+// 删除订单DTO
+export const DeleteOrderSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '订单ID',
+    example: 1
+  })
+});
+
+// 删除订单人员DTO
+export const DeleteOrderPersonSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '关联ID',
+    example: 1
+  })
+});
+
+// 删除订单人员资产DTO
+export const DeleteOrderPersonAssetSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '关联ID',
+    example: 1
+  })
+});
+
+// 类型定义
+export type EmploymentOrder = z.infer<typeof EmploymentOrderSchema>;
+export type CreateEmploymentOrderDto = z.infer<typeof CreateEmploymentOrderSchema>;
+export type UpdateEmploymentOrderDto = z.infer<typeof UpdateEmploymentOrderSchema>;
+export type OrderPerson = z.infer<typeof OrderPersonSchema>;
+export type CreateOrderPersonDto = z.infer<typeof CreateOrderPersonSchema>;
+export type OrderPersonAsset = z.infer<typeof OrderPersonAssetSchema>;
+export type CreateOrderPersonAssetDto = z.infer<typeof CreateOrderPersonAssetSchema>;
+export type PaginationQuery = z.infer<typeof PaginationQuerySchema>;
+export type DeleteOrderDto = z.infer<typeof DeleteOrderSchema>;
+export type DeleteOrderPersonDto = z.infer<typeof DeleteOrderPersonSchema>;
+export type DeleteOrderPersonAssetDto = z.infer<typeof DeleteOrderPersonAssetSchema>;

+ 472 - 0
allin-packages/order-module/src/services/order.service.ts

@@ -0,0 +1,472 @@
+import { GenericCrudService } from '@d8d/shared-crud';
+import { DataSource, Repository, DataSourceOptions, In, Not } from 'typeorm';
+import { EmploymentOrder } from '../entities/employment-order.entity';
+import { OrderPerson } from '../entities/order-person.entity';
+import { OrderPersonAsset } from '../entities/order-person-asset.entity';
+import { FileService, File } from '@d8d/file-module';
+import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
+
+export class OrderService extends GenericCrudService<EmploymentOrder> {
+  private readonly orderPersonRepository: Repository<OrderPerson>;
+  private readonly orderPersonAssetRepository: Repository<OrderPersonAsset>;
+  private readonly fileRepository: Repository<File>;
+  private fileService: FileService;
+
+  constructor(dataSource: DataSource) {
+    super(dataSource, EmploymentOrder);
+    this.orderPersonRepository = dataSource.getRepository(OrderPerson);
+    this.orderPersonAssetRepository = dataSource.getRepository(OrderPersonAsset);
+    this.fileRepository = dataSource.getRepository(File);
+    this.fileService = new FileService(dataSource);
+  }
+
+  /**
+   * 创建订单 - 覆盖父类方法,添加验证和业务逻辑
+   */
+  async create(data: Partial<EmploymentOrder>, userId?: string | number): Promise<EmploymentOrder> {
+    // 验证枚举值
+    if (data.orderStatus && !Object.values(OrderStatus).includes(data.orderStatus)) {
+      throw new Error('订单状态无效');
+    }
+    if (data.workStatus && !Object.values(WorkStatus).includes(data.workStatus)) {
+      throw new Error('工作状态无效');
+    }
+
+    // 设置默认值
+    const orderData = {
+      ...data,
+      orderStatus: data.orderStatus || OrderStatus.DRAFT,
+      workStatus: data.workStatus || WorkStatus.NOT_WORKING,
+    };
+
+    return super.create(orderData, userId);
+  }
+
+  /**
+   * 更新订单 - 覆盖父类方法,添加验证
+   */
+  async update(id: number, data: Partial<EmploymentOrder>, userId?: string | number): Promise<EmploymentOrder | null> {
+    // 验证枚举值
+    if (data.orderStatus && !Object.values(OrderStatus).includes(data.orderStatus)) {
+      throw new Error('订单状态无效');
+    }
+    if (data.workStatus && !Object.values(WorkStatus).includes(data.workStatus)) {
+      throw new Error('工作状态无效');
+    }
+
+    return super.update(id, data, userId);
+  }
+
+  /**
+   * 分页查询订单 - 自定义方法,返回源服务的格式
+   */
+  async findAll(query: {
+    orderName?: string;
+    platformId?: number;
+    companyId?: number;
+    channelId?: number;
+    orderStatus?: OrderStatus;
+    page?: number;
+    limit?: number;
+  }): Promise<{ data: any[]; total: number }> {
+    const {
+      orderName,
+      platformId,
+      companyId,
+      channelId,
+      orderStatus,
+      page = 1,
+      limit = 10
+    } = query;
+
+    const queryBuilder = this.repository.createQueryBuilder('order');
+
+    // 构建查询条件
+    if (orderName) {
+      queryBuilder.andWhere('order.orderName LIKE :orderName', { orderName: `%${orderName}%` });
+    }
+    if (platformId) {
+      queryBuilder.andWhere('order.platformId = :platformId', { platformId });
+    }
+    if (companyId) {
+      queryBuilder.andWhere('order.companyId = :companyId', { companyId });
+    }
+    if (channelId) {
+      queryBuilder.andWhere('order.channelId = :channelId', { channelId });
+    }
+    if (orderStatus) {
+      queryBuilder.andWhere('order.orderStatus = :orderStatus', { orderStatus });
+    }
+
+    // 获取总数
+    const total = await queryBuilder.getCount();
+
+    // 获取数据
+    const data = await queryBuilder
+      .skip((page - 1) * limit)
+      .take(limit)
+      .orderBy('order.createTime', 'DESC')
+      .getMany();
+
+    // 获取每个订单的人员数量
+    const orderIds = data.map(order => order.id);
+    let personCounts: Array<{ orderId: number; count: number }> = [];
+
+    if (orderIds.length > 0) {
+      const personCountQuery = await this.orderPersonRepository
+        .createQueryBuilder('orderPerson')
+        .select('orderPerson.orderId', 'orderId')
+        .addSelect('COUNT(orderPerson.id)', 'count')
+        .where('orderPerson.orderId IN (:...orderIds)', { orderIds })
+        .groupBy('orderPerson.orderId')
+        .getRawMany();
+
+      personCounts = personCountQuery.map(item => ({
+        orderId: item.orderId,
+        count: parseInt(item.count)
+      }));
+    }
+
+    // 格式化返回数据
+    const formattedData = data.map(order => {
+      const personCount = personCounts.find(pc => pc.orderId === order.id)?.count || 0;
+      return {
+        orderId: order.id,
+        orderName: order.orderName,
+        platformId: order.platformId,
+        companyId: order.companyId,
+        channelId: order.channelId,
+        expectedStartDate: order.expectedStartDate,
+        actualStartDate: order.actualStartDate,
+        actualEndDate: order.actualEndDate,
+        orderStatus: order.orderStatus,
+        workStatus: order.workStatus,
+        createTime: order.createTime,
+        updateTime: order.updateTime,
+        personCount
+      };
+    });
+
+    return { data: formattedData, total };
+  }
+
+  /**
+   * 查询单个订单详情 - 自定义方法
+   */
+  async findOne(id: number): Promise<any | null> {
+    const order = await this.repository.findOne({
+      where: { id },
+      relations: ['orderPersons']
+    });
+
+    if (!order) {
+      return null;
+    }
+
+    // 获取订单人员详情(这里简化处理,实际可能需要关联更多信息)
+    const orderPersons = order.orderPersons || [];
+
+    return {
+      orderId: order.id,
+      orderName: order.orderName,
+      platformId: order.platformId,
+      companyId: order.companyId,
+      channelId: order.channelId,
+      expectedStartDate: order.expectedStartDate,
+      actualStartDate: order.actualStartDate,
+      actualEndDate: order.actualEndDate,
+      orderStatus: order.orderStatus,
+      workStatus: order.workStatus,
+      createTime: order.createTime,
+      updateTime: order.updateTime,
+      orderPersons: orderPersons.map(person => ({
+        opId: person.id,
+        orderId: person.orderId,
+        personId: person.personId,
+        joinDate: person.joinDate,
+        leaveDate: person.leaveDate,
+        workStatus: person.workStatus,
+        salaryDetail: person.salaryDetail
+      }))
+    };
+  }
+
+  /**
+   * 订单激活 - 自定义业务方法
+   */
+  async activateOrder(orderId: number): Promise<boolean> {
+    const queryRunner = this.dataSource.createQueryRunner();
+    await queryRunner.connect();
+    await queryRunner.startTransaction();
+
+    try {
+      // 查询订单
+      const order = await queryRunner.manager.findOne(EmploymentOrder, {
+        where: { id: orderId }
+      });
+
+      if (!order) {
+        throw new Error(`订单ID ${orderId} 不存在`);
+      }
+
+      if (order.orderStatus !== OrderStatus.DRAFT) {
+        throw new Error(`只有草稿状态的订单才能激活,当前订单状态为: ${order.orderStatus}`);
+      }
+
+      // 更新订单状态为已确认
+      order.orderStatus = OrderStatus.CONFIRMED;
+      order.actualStartDate = new Date();
+      await queryRunner.manager.save(order);
+
+      // 更新订单人员状态为待就业
+      await queryRunner.manager.update(OrderPerson,
+        { orderId: orderId },
+        { workStatus: WorkStatus.PRE_WORKING }
+      );
+
+      await queryRunner.commitTransaction();
+      return true;
+    } catch (error) {
+      await queryRunner.rollbackTransaction();
+      throw error;
+    } finally {
+      await queryRunner.release();
+    }
+  }
+
+  /**
+   * 订单关闭 - 自定义业务方法
+   */
+  async closeOrder(orderId: number): Promise<boolean> {
+    const queryRunner = this.dataSource.createQueryRunner();
+    await queryRunner.connect();
+    await queryRunner.startTransaction();
+
+    try {
+      // 查询订单
+      const order = await queryRunner.manager.findOne(EmploymentOrder, {
+        where: { id: orderId }
+      });
+
+      if (!order) {
+        throw new Error(`订单ID ${orderId} 不存在`);
+      }
+
+      if (order.orderStatus !== OrderStatus.CONFIRMED && order.orderStatus !== OrderStatus.IN_PROGRESS) {
+        throw new Error(`只有已确认或进行中的订单才能关闭,当前订单状态为: ${order.orderStatus}`);
+      }
+
+      // 更新订单状态为已完成
+      order.orderStatus = OrderStatus.COMPLETED;
+      order.actualEndDate = new Date();
+      await queryRunner.manager.save(order);
+
+      // 更新订单人员状态为已离职
+      await queryRunner.manager.update(OrderPerson,
+        { orderId: orderId },
+        {
+          workStatus: WorkStatus.RESIGNED,
+          leaveDate: new Date()
+        }
+      );
+
+      await queryRunner.commitTransaction();
+      return true;
+    } catch (error) {
+      await queryRunner.rollbackTransaction();
+      throw error;
+    } finally {
+      await queryRunner.release();
+    }
+  }
+
+  /**
+   * 批量添加人员到订单 - 自定义业务方法
+   */
+  async batchAddPersons(orderId: number, persons: Array<{
+    personId: number;
+    joinDate: Date;
+    salaryDetail: number;
+  }>): Promise<{ success: boolean; message: string; addedCount: number }> {
+    // 验证订单是否存在
+    const order = await this.repository.findOne({
+      where: { id: orderId }
+    });
+
+    if (!order) {
+      throw new Error(`订单ID ${orderId} 不存在`);
+    }
+
+    if (order.orderStatus === OrderStatus.COMPLETED || order.orderStatus === OrderStatus.CANCELLED) {
+      throw new Error(`订单ID ${orderId} 已结束或已取消,无法添加人员`);
+    }
+
+    // 检查哪些人员已经在订单中
+    const existingPersons = await this.orderPersonRepository.find({
+      where: { orderId }
+    });
+
+    const existingPersonIds = existingPersons.map(p => p.personId);
+
+    // 过滤掉已存在的人员
+    const newPersons = persons.filter(p => !existingPersonIds.includes(p.personId));
+
+    if (newPersons.length === 0) {
+      return { success: true, message: '所有人员都已在此订单中', addedCount: 0 };
+    }
+
+    // 使用事务添加新人员
+    const queryRunner = this.dataSource.createQueryRunner();
+    await queryRunner.connect();
+    await queryRunner.startTransaction();
+
+    try {
+      // 创建订单人员关联
+      const orderPersons = newPersons.map(person => {
+        return queryRunner.manager.create(OrderPerson, {
+          orderId: orderId,
+          personId: person.personId,
+          joinDate: person.joinDate,
+          workStatus: WorkStatus.NOT_WORKING,
+          salaryDetail: person.salaryDetail,
+        });
+      });
+
+      await queryRunner.manager.save(orderPersons);
+
+      await queryRunner.commitTransaction();
+
+      return {
+        success: true,
+        message: `成功添加 ${newPersons.length} 个人员到订单`,
+        addedCount: newPersons.length
+      };
+    } catch (error) {
+      await queryRunner.rollbackTransaction();
+      throw error;
+    } finally {
+      await queryRunner.release();
+    }
+  }
+
+  /**
+   * 验证文件ID是否存在
+   */
+  async validateFileId(fileId: number): Promise<boolean> {
+    try {
+      const file = await this.fileRepository.findOne({ where: { id: fileId } });
+      return !!file;
+    } catch (error) {
+      console.error('验证文件ID失败:', error);
+      return false;
+    }
+  }
+
+  /**
+   * 创建订单人员资产 - 自定义业务方法
+   */
+  async createOrderPersonAsset(data: {
+    orderId: number;
+    personId: number;
+    assetType: string;
+    assetFileType: string;
+    fileId: number;
+    relatedTime?: Date;
+  }): Promise<OrderPersonAsset> {
+    // 验证文件ID是否存在
+    const fileExists = await this.validateFileId(data.fileId);
+    if (!fileExists) {
+      throw new Error('文件不存在');
+    }
+
+    // 验证订单和人员关联是否存在
+    const orderPerson = await this.orderPersonRepository.findOne({
+      where: { orderId: data.orderId, personId: data.personId }
+    });
+
+    if (!orderPerson) {
+      throw new Error(`人员ID ${data.personId} 不在订单ID ${data.orderId} 中`);
+    }
+
+    // 创建资产记录
+    const asset = this.orderPersonAssetRepository.create({
+      orderId: data.orderId,
+      personId: data.personId,
+      assetType: data.assetType,
+      assetFileType: data.assetFileType,
+      fileId: data.fileId,
+      relatedTime: data.relatedTime || new Date()
+    });
+
+    return await this.orderPersonAssetRepository.save(asset);
+  }
+
+  /**
+   * 查询订单人员资产 - 自定义业务方法
+   */
+  async queryOrderPersonAsset(query: {
+    orderId?: number;
+    personId?: number;
+    assetType?: string;
+    assetFileType?: string;
+    page?: number;
+    limit?: number;
+  }): Promise<{ data: OrderPersonAsset[]; total: number }> {
+    const {
+      orderId,
+      personId,
+      assetType,
+      assetFileType,
+      page = 1,
+      limit = 10
+    } = query;
+
+    const queryBuilder = this.orderPersonAssetRepository.createQueryBuilder('asset');
+
+    // 构建查询条件
+    if (orderId) {
+      queryBuilder.andWhere('asset.orderId = :orderId', { orderId });
+    }
+    if (personId) {
+      queryBuilder.andWhere('asset.personId = :personId', { personId });
+    }
+    if (assetType) {
+      queryBuilder.andWhere('asset.assetType = :assetType', { assetType });
+    }
+    if (assetFileType) {
+      queryBuilder.andWhere('asset.assetFileType = :assetFileType', { assetFileType });
+    }
+
+    // 获取总数
+    const total = await queryBuilder.getCount();
+
+    // 获取数据
+    const data = await queryBuilder
+      .skip((page - 1) * limit)
+      .take(limit)
+      .orderBy('asset.updateTime', 'DESC')
+      .getMany();
+
+    return { data, total };
+  }
+
+  /**
+   * 删除订单人员资产 - 自定义业务方法
+   */
+  async deleteOrderPersonAsset(id: number): Promise<{ success: boolean; message: string }> {
+    const asset = await this.orderPersonAssetRepository.findOne({
+      where: { id }
+    });
+
+    if (!asset) {
+      throw new Error(`资产ID ${id} 不存在`);
+    }
+
+    await this.orderPersonAssetRepository.delete({ id });
+
+    return {
+      success: true,
+      message: `成功删除资产ID ${id}`
+    };
+  }
+}

+ 989 - 0
allin-packages/order-module/tests/integration/order.integration.test.ts

@@ -0,0 +1,989 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { testClient } from 'hono/testing';
+import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
+import { JWTUtil } from '@d8d/shared-utils';
+import { UserEntity, Role } from '@d8d/user-module';
+import { File } from '@d8d/file-module';
+import orderRoutes from '../../src/routes/order.routes';
+import { EmploymentOrder } from '../../src/entities/employment-order.entity';
+import { OrderPerson } from '../../src/entities/order-person.entity';
+import { OrderPersonAsset } from '../../src/entities/order-person-asset.entity';
+import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
+
+// 设置集成测试钩子
+setupIntegrationDatabaseHooksWithEntities([UserEntity, File, Role, EmploymentOrder, OrderPerson, OrderPersonAsset])
+
+describe('订单管理API集成测试', () => {
+  let client: ReturnType<typeof testClient<typeof orderRoutes>>;
+  let testToken: string;
+  let testUser: UserEntity;
+  let testFile: File;
+
+  beforeEach(async () => {
+    // 创建测试客户端
+    client = testClient(orderRoutes);
+
+    // 获取数据源
+    const dataSource = await IntegrationTestDatabase.getDataSource();
+
+    // 创建测试用户
+    const userRepository = dataSource.getRepository(UserEntity);
+    testUser = userRepository.create({
+      username: `test_user_${Date.now()}`,
+      password: 'test_password',
+      nickname: '测试用户',
+      registrationSource: 'web'
+    });
+    await userRepository.save(testUser);
+
+    // 生成测试用户的token
+    testToken = JWTUtil.generateToken({
+      id: testUser.id,
+      username: testUser.username,
+      roles: [{name:'user'}]
+    });
+
+    // 创建测试文件
+    const fileRepository = dataSource.getRepository(File);
+    testFile = fileRepository.create({
+      name: 'test_file.pdf',
+      type: 'application/pdf',
+      size: 1024,
+      path: `test/${Date.now()}_test_file.pdf`,
+      uploadUserId: testUser.id,
+      uploadTime: new Date(),
+      createdAt: new Date(),
+      updatedAt: new Date()
+    });
+    await fileRepository.save(testFile);
+  });
+
+  describe('POST /order/create', () => {
+    it('应该成功创建订单', async () => {
+      const createData = {
+        orderName: '测试订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date().toISOString(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING
+      };
+
+      const response = await client.create.$post({
+        json: createData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      if (response.status !== 200) {
+        const error = await response.json();
+        console.debug('创建订单失败:', JSON.stringify(error, null, 2));
+      }
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.id).toBeDefined();
+        expect(data.orderName).toBe('测试订单');
+        expect(data.orderStatus).toBe(OrderStatus.DRAFT);
+        expect(data.workStatus).toBe(WorkStatus.NOT_WORKING);
+      }
+    });
+
+    it('应该验证订单状态枚举值', async () => {
+      const createData = {
+        orderName: '测试订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date().toISOString(),
+        orderStatus: 'INVALID_STATUS', // 无效的状态
+        workStatus: WorkStatus.NOT_WORKING
+      };
+
+      const response = await client.create.$post({
+        json: createData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该验证工作状态枚举值', async () => {
+      const createData = {
+        orderName: '测试订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date().toISOString(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: 'INVALID_STATUS' // 无效的状态
+      };
+
+      const response = await client.create.$post({
+        json: createData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+  });
+
+  describe('GET /order/list', () => {
+    let testOrder: EmploymentOrder;
+
+    beforeEach(async () => {
+      // 创建测试订单数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+
+      testOrder = orderRepository.create({
+        orderName: '测试订单1',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+
+      const testOrder2 = orderRepository.create({
+        orderName: '测试订单2',
+        platformId: 2,
+        companyId: 2,
+        channelId: 2,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.CONFIRMED,
+        workStatus: WorkStatus.PRE_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder2);
+    });
+
+    it('应该返回所有订单列表', async () => {
+      const response = await client.list.$get({
+        query: {}
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data).toHaveProperty('data');
+        expect(data).toHaveProperty('total');
+        expect(data.data).toHaveLength(2);
+        expect(data.total).toBe(2);
+      }
+    });
+
+    it('应该支持按订单名称搜索', async () => {
+      const response = await client.list.$get({
+        query: {
+          orderName: '测试订单1'
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(1);
+        expect(data.data[0].orderName).toBe('测试订单1');
+      }
+    });
+
+    it('应该支持按订单状态过滤', async () => {
+      const response = await client.list.$get({
+        query: {
+          orderStatus: OrderStatus.CONFIRMED
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(1);
+        expect(data.data[0].orderStatus).toBe(OrderStatus.CONFIRMED);
+      }
+    });
+
+    it('应该支持分页查询', async () => {
+      const response = await client.list.$get({
+        query: {
+          page: '1',
+          limit: '1'
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(1);
+        expect(data.total).toBe(2);
+      }
+    });
+  });
+
+  describe('GET /order/detail/:id', () => {
+    let testOrder: EmploymentOrder;
+
+    beforeEach(async () => {
+      // 创建测试订单数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+
+      testOrder = orderRepository.create({
+        orderName: '测试订单详情',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+    });
+
+    it('应该返回指定ID的订单详情', async () => {
+      const response = await client.detail[':id'].$get({
+        param: { id: testOrder.id.toString() }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data?.id).toBe(testOrder.id);
+        expect(data?.orderName).toBe('测试订单详情');
+        expect(data?.orderStatus).toBe(OrderStatus.DRAFT);
+      }
+    });
+
+    it('应该处理不存在的订单ID', async () => {
+      const response = await client.detail[':id'].$get({
+        param: { id: '999999' }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+    });
+  });
+
+  describe('POST /order/activate/:orderId', () => {
+    let testOrder: EmploymentOrder;
+
+    beforeEach(async () => {
+      // 创建测试订单数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+
+      testOrder = orderRepository.create({
+        orderName: '待激活订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+    });
+
+    it('应该成功激活草稿状态的订单', async () => {
+      const response = await client.activate[':orderId'].$post({
+        param: { orderId: testOrder.id.toString() }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.success).toBe(true);
+
+        // 验证订单状态已更新
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const orderRepository = dataSource.getRepository(EmploymentOrder);
+        const updatedOrder = await orderRepository.findOne({
+          where: { id: testOrder.id }
+        });
+        expect(updatedOrder?.orderStatus).toBe(OrderStatus.CONFIRMED);
+        expect(updatedOrder?.actualStartDate).toBeDefined();
+      }
+    });
+
+    it('应该处理非草稿状态的订单激活', async () => {
+      // 更新订单状态为已确认
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+      await orderRepository.update(testOrder.id, { orderStatus: OrderStatus.CONFIRMED });
+
+      const response = await client.activate[':orderId'].$post({
+        param: { orderId: testOrder.id.toString() }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该处理不存在的订单ID', async () => {
+      const response = await client.activate[':orderId'].$post({
+        param: { orderId: '999999' }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+    });
+  });
+
+  describe('POST /order/close/:orderId', () => {
+    let testOrder: EmploymentOrder;
+
+    beforeEach(async () => {
+      // 创建测试订单数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+
+      testOrder = orderRepository.create({
+        orderName: '待关闭订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        actualStartDate: new Date(),
+        orderStatus: OrderStatus.CONFIRMED,
+        workStatus: WorkStatus.PRE_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+    });
+
+    it('应该成功关闭已确认状态的订单', async () => {
+      const response = await client.close[':orderId'].$post({
+        param: { orderId: testOrder.id.toString() }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.success).toBe(true);
+
+        // 验证订单状态已更新
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const orderRepository = dataSource.getRepository(EmploymentOrder);
+        const updatedOrder = await orderRepository.findOne({
+          where: { id: testOrder.id }
+        });
+        expect(updatedOrder?.orderStatus).toBe(OrderStatus.COMPLETED);
+        expect(updatedOrder?.actualEndDate).toBeDefined();
+      }
+    });
+
+    it('应该处理非已确认或进行中状态的订单关闭', async () => {
+      // 更新订单状态为草稿
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+      await orderRepository.update(testOrder.id, { orderStatus: OrderStatus.DRAFT });
+
+      const response = await client.close[':orderId'].$post({
+        param: { orderId: testOrder.id.toString() }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该处理不存在的订单ID', async () => {
+      const response = await client.close[':orderId'].$post({
+        param: { orderId: '999999' }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+    });
+  });
+
+  describe('POST /order/:orderId/persons/batch', () => {
+    let testOrder: EmploymentOrder;
+
+    beforeEach(async () => {
+      // 创建测试订单数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+
+      testOrder = orderRepository.create({
+        orderName: '批量添加人员测试订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+    });
+
+    it('应该成功批量添加人员到订单', async () => {
+      const batchData = {
+        persons: [
+          {
+            personId: 1001,
+            joinDate: new Date().toISOString(),
+            salaryDetail: 5000.00
+          },
+          {
+            personId: 1002,
+            joinDate: new Date().toISOString(),
+            salaryDetail: 6000.00
+          }
+        ]
+      };
+
+      const response = await client[':orderId'].persons.batch.$post({
+        param: { orderId: testOrder.id.toString() },
+        json: batchData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.success).toBe(true);
+        expect(data.addedCount).toBe(2);
+
+        // 验证人员已添加到订单
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const orderPersonRepository = dataSource.getRepository(OrderPerson);
+        const orderPersons = await orderPersonRepository.find({
+          where: { orderId: testOrder.id }
+        });
+        expect(orderPersons).toHaveLength(2);
+      }
+    });
+
+    it('应该处理重复的人员添加', async () => {
+      // 先添加一个人员
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderPersonRepository = dataSource.getRepository(OrderPerson);
+      const existingPerson = orderPersonRepository.create({
+        orderId: testOrder.id,
+        personId: 1001,
+        joinDate: new Date(),
+        workStatus: WorkStatus.NOT_WORKING,
+        salaryDetail: 5000.00
+      });
+      await orderPersonRepository.save(existingPerson);
+
+      const batchData = {
+        persons: [
+          {
+            personId: 1001, // 已存在的人员
+            joinDate: new Date().toISOString(),
+            salaryDetail: 5000.00
+          },
+          {
+            personId: 1002, // 新人员
+            joinDate: new Date().toISOString(),
+            salaryDetail: 6000.00
+          }
+        ]
+      };
+
+      const response = await client[':orderId'].persons.batch.$post({
+        param: { orderId: testOrder.id.toString() },
+        json: batchData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.success).toBe(true);
+        expect(data.addedCount).toBe(1); // 只添加了一个新人员
+      }
+    });
+
+    it('应该处理已结束或已取消的订单', async () => {
+      // 更新订单状态为已完成
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+      await orderRepository.update(testOrder.id, { orderStatus: OrderStatus.COMPLETED });
+
+      const batchData = {
+        persons: [
+          {
+            personId: 1001,
+            joinDate: new Date().toISOString(),
+            salaryDetail: 5000.00
+          }
+        ]
+      };
+
+      const response = await client[':orderId'].persons.batch.$post({
+        param: { orderId: testOrder.id.toString() },
+        json: batchData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该处理不存在的订单ID', async () => {
+      const batchData = {
+        persons: [
+          {
+            personId: 1001,
+            joinDate: new Date().toISOString(),
+            salaryDetail: 5000.00
+          }
+        ]
+      };
+
+      const response = await client[':orderId'].persons.batch.$post({
+        param: { orderId: '999999' },
+        json: batchData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+    });
+  });
+
+  describe('POST /order/assets/create', () => {
+    let testOrder: EmploymentOrder;
+    let testOrderPerson: OrderPerson;
+
+    beforeEach(async () => {
+      // 创建测试订单数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+      const orderPersonRepository = dataSource.getRepository(OrderPerson);
+
+      testOrder = orderRepository.create({
+        orderName: '资产测试订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+
+      testOrderPerson = orderPersonRepository.create({
+        orderId: testOrder.id,
+        personId: 1001,
+        joinDate: new Date(),
+        workStatus: WorkStatus.NOT_WORKING,
+        salaryDetail: 5000.00
+      });
+      await orderPersonRepository.save(testOrderPerson);
+    });
+
+    it('应该成功创建订单人员资产', async () => {
+      const assetData = {
+        orderId: testOrder.id,
+        personId: testOrderPerson.personId,
+        assetType: '身份证',
+        assetFileType: 'image',
+        fileId: testFile.id,
+        relatedTime: new Date().toISOString()
+      };
+
+      const response = await client.assets.create.$post({
+        json: assetData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.id).toBeDefined();
+        expect(data.orderId).toBe(testOrder.id);
+        expect(data.personId).toBe(testOrderPerson.personId);
+        expect(data.assetType).toBe('身份证');
+        expect(data.fileId).toBe(testFile.id);
+      }
+    });
+
+    it('应该处理不存在的文件ID', async () => {
+      const assetData = {
+        orderId: testOrder.id,
+        personId: testOrderPerson.personId,
+        assetType: '身份证',
+        assetFileType: 'image',
+        fileId: 999999, // 不存在的文件ID
+        relatedTime: new Date().toISOString()
+      };
+
+      const response = await client.assets.create.$post({
+        json: assetData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该处理人员不在订单中的情况', async () => {
+      const assetData = {
+        orderId: testOrder.id,
+        personId: 999999, // 不在订单中的人员
+        assetType: '身份证',
+        assetFileType: 'image',
+        fileId: testFile.id,
+        relatedTime: new Date().toISOString()
+      };
+
+      const response = await client.assets.create.$post({
+        json: assetData
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+    });
+  });
+
+  describe('GET /order/assets/query', () => {
+    let testOrder: EmploymentOrder;
+    let testOrderPerson: OrderPerson;
+    let testAsset: OrderPersonAsset;
+
+    beforeEach(async () => {
+      // 创建测试数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+      const orderPersonRepository = dataSource.getRepository(OrderPerson);
+      const assetRepository = dataSource.getRepository(OrderPersonAsset);
+
+      testOrder = orderRepository.create({
+        orderName: '资产查询测试订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+
+      testOrderPerson = orderPersonRepository.create({
+        orderId: testOrder.id,
+        personId: 1001,
+        joinDate: new Date(),
+        workStatus: WorkStatus.NOT_WORKING,
+        salaryDetail: 5000.00
+      });
+      await orderPersonRepository.save(testOrderPerson);
+
+      testAsset = assetRepository.create({
+        orderId: testOrder.id,
+        personId: testOrderPerson.personId,
+        assetType: '身份证',
+        assetFileType: 'image',
+        fileId: testFile.id,
+        relatedTime: new Date()
+      });
+      await assetRepository.save(testAsset);
+
+      // 创建第二个资产
+      const testAsset2 = assetRepository.create({
+        orderId: testOrder.id,
+        personId: testOrderPerson.personId,
+        assetType: '学历证书',
+        assetFileType: 'pdf',
+        fileId: testFile.id,
+        relatedTime: new Date()
+      });
+      await assetRepository.save(testAsset2);
+    });
+
+    it('应该查询所有订单人员资产', async () => {
+      const response = await client.assets.query.$get({
+        query: {}
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data).toHaveProperty('data');
+        expect(data).toHaveProperty('total');
+        expect(data.data).toHaveLength(2);
+        expect(data.total).toBe(2);
+      }
+    });
+
+    it('应该支持按订单ID查询', async () => {
+      const response = await client.assets.query.$get({
+        query: {
+          orderId: testOrder.id.toString()
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(2);
+        expect(data.data[0].orderId).toBe(testOrder.id);
+      }
+    });
+
+    it('应该支持按人员ID查询', async () => {
+      const response = await client.assets.query.$get({
+        query: {
+          personId: testOrderPerson.personId.toString()
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(2);
+        expect(data.data[0].personId).toBe(testOrderPerson.personId);
+      }
+    });
+
+    it('应该支持按资产类型查询', async () => {
+      const response = await client.assets.query.$get({
+        query: {
+          assetType: '身份证'
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(1);
+        expect(data.data[0].assetType).toBe('身份证');
+      }
+    });
+
+    it('应该支持分页查询', async () => {
+      const response = await client.assets.query.$get({
+        query: {
+          page: '1',
+          limit: '1'
+        }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.data).toHaveLength(1);
+        expect(data.total).toBe(2);
+      }
+    });
+  });
+
+  describe('DELETE /order/assets/delete/:id', () => {
+    let testAsset: OrderPersonAsset;
+
+    beforeEach(async () => {
+      // 创建测试数据
+      const dataSource = await IntegrationTestDatabase.getDataSource();
+      const orderRepository = dataSource.getRepository(EmploymentOrder);
+      const orderPersonRepository = dataSource.getRepository(OrderPerson);
+      const assetRepository = dataSource.getRepository(OrderPersonAsset);
+
+      const testOrder = orderRepository.create({
+        orderName: '资产删除测试订单',
+        platformId: 1,
+        companyId: 1,
+        channelId: 1,
+        expectedStartDate: new Date(),
+        orderStatus: OrderStatus.DRAFT,
+        workStatus: WorkStatus.NOT_WORKING,
+        createdBy: testUser.id
+      });
+      await orderRepository.save(testOrder);
+
+      const testOrderPerson = orderPersonRepository.create({
+        orderId: testOrder.id,
+        personId: 1001,
+        joinDate: new Date(),
+        workStatus: WorkStatus.NOT_WORKING,
+        salaryDetail: 5000.00
+      });
+      await orderPersonRepository.save(testOrderPerson);
+
+      testAsset = assetRepository.create({
+        orderId: testOrder.id,
+        personId: testOrderPerson.personId,
+        assetType: '身份证',
+        assetFileType: 'image',
+        fileId: testFile.id,
+        relatedTime: new Date()
+      });
+      await assetRepository.save(testAsset);
+    });
+
+    it('应该成功删除订单人员资产', async () => {
+      const response = await client.assets.delete[':id'].$delete({
+        param: { id: testAsset.id.toString() }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(200);
+
+      if (response.status === 200) {
+        const data = await response.json();
+        expect(data.success).toBe(true);
+
+        // 验证资产已删除
+        const dataSource = await IntegrationTestDatabase.getDataSource();
+        const assetRepository = dataSource.getRepository(OrderPersonAsset);
+        const deletedAsset = await assetRepository.findOne({
+          where: { id: testAsset.id }
+        });
+        expect(deletedAsset).toBeNull();
+      }
+    });
+
+    it('应该处理不存在的资产ID', async () => {
+      const response = await client.assets.delete[':id'].$delete({
+        param: { id: '999999' }
+      }, {
+        headers: {
+          'Authorization': `Bearer ${testToken}`
+        }
+      });
+
+      expect(response.status).toBe(404);
+    });
+  });
+
+  describe('认证测试', () => {
+    it('应该要求认证', async () => {
+      const response = await client.list.$get({
+        query: {}
+        // 不提供Authorization header
+      });
+
+      expect(response.status).toBe(401);
+    });
+
+    it('应该验证无效token', async () => {
+      const response = await client.list.$get({
+        query: {}
+      }, {
+        headers: {
+          'Authorization': 'Bearer invalid_token'
+        }
+      });
+
+      expect(response.status).toBe(401);
+    });
+  });
+});

+ 16 - 0
allin-packages/order-module/tsconfig.json

@@ -0,0 +1,16 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "composite": true,
+    "rootDir": ".",
+    "outDir": "dist"
+  },
+  "include": [
+    "src/**/*",
+    "tests/**/*"
+  ],
+  "exclude": [
+    "node_modules",
+    "dist"
+  ]
+}

+ 21 - 0
allin-packages/order-module/vitest.config.ts

@@ -0,0 +1,21 @@
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+  test: {
+    globals: true,
+    environment: 'node',
+    include: ['tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+    coverage: {
+      provider: 'v8',
+      reporter: ['text', 'json', 'html'],
+      exclude: [
+        'tests/**',
+        '**/*.d.ts',
+        '**/*.config.*',
+        '**/dist/**'
+      ]
+    },
+    // 关闭并行测试以避免数据库连接冲突
+    fileParallelism: false
+  }
+});

+ 58 - 2
docs/stories/007.005.transplant-order-management-module.story.md

@@ -591,11 +591,67 @@ export default orderRoutes;
 ```
 
 ## Dev Agent Record
-*将在开发完成后填写*
+**开发时间**: 2025-12-03
+**开发者**: James (全栈开发工程师)
+**开发状态**: 基本完成,集成测试调试中
+
+### 已完成的工作:
+1. **✅ 创建包结构和配置文件** (AC: 1)
+   - 创建目录:`allin-packages/order-module/`
+   - 初始化package.json:添加对`@d8d/file-module`和`@d8d/allin-enums`的依赖
+   - 配置tsconfig.json、vitest.config.ts
+   - 在pnpm-workspace.yaml中添加`'allin-packages/order-module'`
+
+2. **✅ 迁移实体文件** (AC: 2, 3, 4)
+   - 迁移EmploymentOrder实体:集成OrderStatus和WorkStatus枚举,字段名改为驼峰命名
+   - 迁移OrderPerson实体:处理循环依赖,使用`personId: number`代替DisabledPerson引用
+   - 迁移OrderPersonAsset实体:集成文件模块,添加`fileId`字段和File实体关联
+
+3. **✅ 创建DTO和Schema文件** (AC: 8)
+   - 创建order.schema.ts:包含所有实体和DTO的Zod Schema
+   - 包含枚举验证、文件ID验证等
+
+4. **✅ 迁移控制器和服务** (AC: 6, 7)
+   - 迁移OrderService:继承GenericCrudService,添加文件验证、枚举验证等业务逻辑
+   - 创建Hono路由:order-custom.routes.ts(自定义业务路由)、order-crud.routes.ts(标准CRUD路由)、order.routes.ts(主路由聚合)
+
+5. **✅ 创建模块主文件** (AC: 1)
+   - 创建index.ts:导出所有公共API
+   - 创建order.module.ts:模块定义
+
+6. **✅ 创建测试文件** (AC: 10)
+   - 创建order.integration.test.ts:包含31个集成测试用例
+   - 覆盖订单全生命周期、枚举验证、文件关联测试等
+
+7. **✅ 处理循环依赖** (AC: 5)
+   - 在OrderPerson和OrderPersonAsset实体中使用`personId: number`代替DisabledPerson的直接引用
+   - 避免与disability-module的循环依赖
+
+### 当前问题:
+1. **集成测试失败**:创建订单API返回500错误,错误信息:`"Cannot read properties of undefined (reading 'orderStatus')"`
+   - 问题定位:Schema导出不一致,路由中导入`CreateOrderSchema`但Schema文件中导出的是`CreateEmploymentOrderSchema`
+   - 需要修复Schema导出或路由导入
+
+2. **订单人员资产创建测试失败**:返回400错误
+   - 可能原因:文件ID验证失败或测试数据问题
+
+### 技术实现细节:
+1. **枚举集成**:正确使用`@d8d/allin-enums`包中的OrderStatus和WorkStatus枚举
+2. **文件模块集成**:在OrderPersonAsset实体中建立与File实体的关联
+3. **循环依赖处理**:使用ID引用代替直接实体引用
+4. **PostgreSQL类型兼容**:正确处理enum字段类型
+5. **路由聚合**:使用Hono的路由聚合模式,聚合自定义路由和CRUD路由
+
+### 下一步:
+1. 修复Schema导出问题
+2. 调试集成测试
+3. 运行完整测试套件验证
+4. 提交代码
 
 ---
 
 *故事创建时间:2025-12-02*
 *创建者:Bob (Scrum Master)*
 *更新说明:重新创建故事,吸取之前移植故事的经验教训,在任务中标注迁移文件的路径和参考对照文件的路径*
-*经验教训更新:2025-12-02,基于故事007.004和007.007的经验教训补充*
+*经验教训更新:2025-12-02,基于故事007.004和007.007的经验教训补充*
+*开发记录更新:2025-12-03,记录开发进度和当前问题*