Ver Fonte

📝 docs(story): 创建故事018.002 - 完善订单编辑功能

基于史诗018(残疾人网站Bug修复)创建第二个用户故事,解决订单管理模块的编辑和显示问题。

主要问题:
- 订单详情页显示"公司29"/"平台3"/"渠道4"而非完整名称
- 订单编辑功能可能不完整

根因分析:
- EmploymentOrder Entity缺少与Company/Platform/Channel的关联定义
- OrderService.findOne()未加载关联数据
- 前端组件直接显示ID而非关联对象名称

技术方案:
1. 后端添加@ManyToOne关联关系
2. Service方法添加relations加载关联数据
3. 前端组件使用关联对象的名称字段
4. 处理关联对象可能为空的情况

验收标准(6项):
- 订单编辑功能正常工作
- 显示完整公司/平台/渠道名称
- 关联信息以可读名称显示
- 编辑后保存成功

🤖 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 há 2 semanas atrás
pai
commit
07c6b0a7e6
1 ficheiros alterados com 470 adições e 0 exclusões
  1. 470 0
      docs/stories/018.002.story.md

+ 470 - 0
docs/stories/018.002.story.md

@@ -0,0 +1,470 @@
+# 故事 018.002 - 完善订单编辑功能
+
+## Status
+Approved
+
+## Story
+**作为** 订单管理员
+**我希望** 能够完整编辑订单信息
+**以便** 修正订单中的错误或更新订单信息
+
+## Acceptance Criteria
+1. 订单编辑功能正常工作,能够修改订单信息
+2. 订单详情页显示完整的公司名称(而非"公司29")
+3. 订单详情页显示完整的平台名称(而非"平台3")
+4. 订单详情页显示完整的渠道名称(而非"渠道4")
+5. 订单关联信息(公司、平台、渠道)以可读的名称显示
+6. 订单编辑后保存成功,数据正确更新
+
+## Tasks / Subtasks
+
+### Task 1: 排查订单编辑和详情显示问题根因 (AC: 1, 2, 3, 4, 5)
+- [ ] 检查 `OrderManagement.tsx` 中订单详情获取逻辑
+- [ ] 验证后端API `order-custom.routes.ts` 中获取订单详情时的关联查询
+- [ ] 检查 `OrderService.findOne()` 方法是否正确加载关联数据(公司、平台、渠道)
+- [ ] 验证 `EmploymentOrder` Entity 是否定义了与 Company、Platform、Channel 的关联关系
+- [ ] 检查订单详情页 `OrderDetailModal` 组件显示逻辑
+- [ ] 验证前端是否正确处理后端返回的关联对象数据
+
+### Task 2: 修复后端订单详情API关联查询 (AC: 2, 3, 4, 5)
+- [ ] 在 `EmploymentOrder` Entity 中添加关联关系定义(如果缺失)
+- [ ] 修改 `OrderService.findOne()` 方法,添加关联加载:
+  - `relations: ['company', 'platform', 'channel']`
+- [ ] 确保返回的订单对象包含完整的公司、平台、渠道信息
+- [ ] 验证Schema定义是否允许返回关联对象
+
+### Task 3: 修复前端订单详情显示 (AC: 2, 3, 4, 5)
+- [ ] 修改 `OrderDetailModal.tsx` 组件,使用关联对象的名称字段
+- [ ] 确保公司名称显示为 `order.company.companyName` 而非 `order.companyId`
+- [ ] 确保平台名称显示为 `order.platform.platformName` 而非 `order.platformId`
+- [ ] 确保渠道名称显示为 `order.channel.channelName` 而非 `order.channelId`
+- [ ] 处理关联对象可能为空的情况(null check)
+
+### Task 4: 修复前端订单编辑功能 (AC: 1, 6)
+- [ ] 检查 `OrderForm.tsx` 组件的编辑模式实现
+- [ ] 验证表单初始化时是否正确填充订单数据
+- [ ] 确保编辑表单能够正常提交
+- [ ] 检查 `OrderManagement.tsx` 中编辑按钮的事件处理
+- [ ] 验证更新订单API调用是否正确
+
+### Task 5: 优化订单列表显示 (AC: 2, 3, 4)
+- [ ] 修改 `OrderManagement.tsx` 列表页面,显示完整的公司/平台/渠道名称
+- [ ] 在 `OrderService.findAll()` 方法中添加关联数据加载
+- [ ] 确保列表页面也显示可读的名称而非ID
+
+### Task 6: 单元测试 (AC: 所有)
+- [ ] 为订单详情关联查询添加单元测试
+- [ ] 测试后端 `OrderService.findOne()` 方法正确加载关联数据
+- [ ] 测试前端组件正确显示关联对象名称
+- [ ] 测试订单编辑功能完整流程
+
+### Task 7: 集成测试 (AC: 1, 6)
+- [ ] 编写端到端集成测试:创建订单并编辑
+- [ ] 编写端到端集成测试:查看订单详情页验证名称显示
+- [ ] 验证数据库中订单信息正确更新
+
+## Dev Notes
+
+### 数据模型信息
+
+**EmploymentOrder Entity**
+[Source: allin-packages/order-module/src/entities/employment-order.entity.ts]
+
+**字段定义**:
+- `id` (order_id): 主键,自增整数
+- `orderName` (order_name): 订单名称,varchar(50),可选
+- `platformId` (platform_id): 用人平台ID,int,非空
+- `companyId` (company_id): 用人单位ID,int,非空
+- `channelId` (channel_id): 渠道ID,int,可选
+- `expectedStartDate` (expected_start_date): 预计开始日期,date,可选
+- `actualStartDate` (actual_start_date): 实际开始日期,date,可选
+- `actualEndDate` (actual_end_date): 实际结束日期,date,可选
+- `orderStatus` (order_status): 订单状态,enum,默认 `OrderStatus.DRAFT`
+- `workStatus` (work_status): 工作状态,enum,默认 `WorkStatus.NOT_WORKING`
+- `createTime` (create_time): 创建时间,timestamp
+- `updateTime` (update_time): 更新时间,timestamp
+
+**关系定义**:
+- `@OneToMany(() => OrderPerson, (orderPerson) => orderPerson.order)`: 订单人员关系
+
+**缺失的关系定义**:
+- 当前Entity未定义与 Company、Platform、Channel 的关联关系
+- 需要添加 `@ManyToOne` 关系以便 TypeORM 加载关联数据
+
+### 关联Entity参考
+
+**Company Entity**
+[Source: allin-packages/company-module/src/entities/company.entity.ts]
+- 主键: `id` (company_id)
+- 名称字段: `companyName`
+
+**Platform Entity**
+[Source: allin-packages/platform-module/src/entities/platform.entity.ts]
+- 主键: `id` (platform_id)
+- 名称字段: `platformName`
+
+**Channel Entity**
+[Source: allin-packages/channel-module/src/entities/channel.entity.ts]
+- 主键: `id` (channel_id)
+- 名称字段: `channelName`
+
+### 后端Service层
+
+**OrderService.findOne()**
+[Source: allin-packages/order-module/src/services/order.service.ts#169-214]
+
+**当前实现**:
+```typescript
+async findOne(id: number): Promise<any | null> {
+  const order = await this.repository.findOne({
+    where: { id },
+    relations: ['orderPersons', 'orderPersons.person']
+  });
+
+  if (!order) {
+    return null;
+  }
+
+  // 返回格式化数据,但缺少公司、平台、渠道信息
+  return {
+    id: order.id,
+    orderName: order.orderName,
+    platformId: order.platformId,  // 只返回ID
+    companyId: order.companyId,    // 只返回ID
+    channelId: order.channelId,    // 只返回ID
+    // ... 其他字段
+  };
+}
+```
+
+**问题分析**:
+1. `relations` 中未包含 `company`, `platform`, `channel`
+2. 返回的数据只包含ID,不包含关联对象的完整信息
+3. 需要添加关联加载并在返回数据中包含关联对象
+
+### 后端路由层
+
+**获取订单详情路由**
+[Source: allin-packages/order-module/src/routes/order-custom.routes.ts#223-257]
+
+**路由定义**:
+```typescript
+const getOrderByIdRoute = createRoute({
+  method: 'get',
+  path: '/detail/{id}',
+  middleware: [authMiddleware],
+  operationId: 'getOrderById',
+  // ...
+});
+```
+
+**路由处理器**:
+[Source: allin-packages/order-module/src/routes/order-custom.routes.ts#995-1014]
+```typescript
+.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) {
+    // 错误处理
+  }
+});
+```
+
+### 前端组件结构
+
+**OrderManagement 组件**
+[Source: allin-packages/order-management-ui/src/components/OrderManagement.tsx]
+
+**关键功能**:
+- 订单列表展示
+- 编辑按钮触发编辑对话框
+- 查看详情按钮触发详情弹窗
+
+**状态管理**:
+```typescript
+const [selectedOrder, setSelectedOrder] = useState<OrderDetail | undefined>();
+const [isOrderFormOpen, setIsOrderFormOpen] = useState(false);
+const [isOrderDetailOpen, setIsOrderDetailOpen] = useState(false);
+```
+
+**获取订单详情**:
+```typescript
+const getOrderDetailMutation = useMutation({
+  mutationFn: async (id: number) => {
+    const response = await orderClientManager.get().detail[':id'].$get({
+      param: { id },
+    });
+    // ...
+  },
+  onSuccess: (data) => {
+    setSelectedOrder(data);
+    setIsOrderDetailOpen(true);
+  },
+});
+```
+
+**OrderDetailModal 组件**
+[Source: allin-packages/order-management-ui/src/components/OrderDetailModal.tsx]
+
+**显示问题**:
+- 当前可能直接显示 `platformId`, `companyId`, `channelId` 数字ID
+- 需要改为显示关联对象的名称字段
+
+**OrderForm 组件**
+[Source: allin-packages/order-management-ui/src/components/OrderForm.tsx]
+
+**编辑功能**:
+- 需要验证编辑模式是否正常工作
+- 表单初始化应该加载现有订单数据
+- 提交时调用更新API
+
+### API客户端
+
+**orderClientManager**
+[Source: allin-packages/order-management-ui/src/api/orderClient.ts]
+
+**获取详情方法**:
+```typescript
+const response = await orderClientManager.get().detail[':id'].$get({
+  param: { id },
+});
+```
+
+**更新订单方法**:
+```typescript
+const response = await orderClientManager.get().update[':id'].$put({
+  param: { id },
+  json: updateData,
+});
+```
+
+### Schema定义
+
+**EmploymentOrderSchema**
+[Source: allin-packages/order-module/src/schemas/order.schema.ts]
+
+需要验证Schema是否支持返回关联对象。可能需要:
+1. 添加关联对象的字段定义
+2. 或使用 `.nullable()` 和 `.optional()` 处理可能为空的关联
+
+### 修复建议
+
+#### 后端修复步骤
+
+1. **修改 EmploymentOrder Entity**:
+```typescript
+// allin-packages/order-module/src/entities/employment-order.entity.ts
+import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn } from 'typeorm';
+import { Company } from '@d8d/allin-company-module/entities';
+import { Platform } from '@d8d/allin-platform-module/entities';
+import { Channel } from '@d8d/allin-channel-module/entities';
+
+@Entity('employment_order', { comment: '用工订单表' })
+export class EmploymentOrder {
+  // ... 现有字段
+
+  @ManyToOne(() => Company)
+  @JoinColumn({ name: 'company_id' })
+  company!: Company;
+
+  @ManyToOne(() => Platform)
+  @JoinColumn({ name: 'platform_id' })
+  platform!: Platform;
+
+  @ManyToOne(() => Channel)
+  @JoinColumn({ name: 'channel_id' })
+  channel?: Channel;
+}
+```
+
+2. **修改 OrderService.findOne()**:
+```typescript
+async findOne(id: number): Promise<any | null> {
+  const order = await this.repository.findOne({
+    where: { id },
+    relations: [
+      'orderPersons',
+      'orderPersons.person',
+      'company',    // 添加关联
+      'platform',   // 添加关联
+      'channel'     // 添加关联
+    ]
+  });
+
+  if (!order) {
+    return null;
+  }
+
+  return {
+    id: order.id,
+    orderName: order.orderName,
+    platformId: order.platformId,
+    companyId: order.companyId,
+    channelId: order.channelId,
+    // 添加关联对象信息
+    company: order.company ? {
+      id: order.company.id,
+      companyName: order.company.companyName
+    } : null,
+    platform: order.platform ? {
+      id: order.platform.id,
+      platformName: order.platform.platformName
+    } : null,
+    channel: order.channel ? {
+      id: order.channel.id,
+      channelName: order.channel.channelName
+    } : null,
+    // ... 其他字段
+  };
+}
+```
+
+#### 前端修复步骤
+
+1. **修改 OrderDetailModal.tsx**:
+```typescript
+// 显示公司名称
+{order.company?.companyName || `公司${order.companyId}`}
+
+// 显示平台名称
+{order.platform?.platformName || `平台${order.platformId}`}
+
+// 显示渠道名称
+{order.channel?.channelName || `渠道${order.channelId}`}
+```
+
+2. **修改 OrderManagement.tsx 列表显示**:
+```typescript
+// 表格列显示名称而非ID
+<TableCell>
+  {order.company?.companyName || `公司${order.companyId}`}
+</TableCell>
+```
+
+### 编码标准
+[Source: docs/architecture/coding-standards.md]
+
+**TypeScript严格模式**: 所有代码必须启用严格类型检查
+**命名约定**:
+- 文件名: kebab-case (如: `order-detail-modal.tsx`)
+- 类名: PascalCase (如: `OrderDetailModal`)
+- 函数/变量: camelCase (如: `getOrderById`)
+- 接口: PascalCase,无I前缀 (如: `OrderDetail`)
+
+**UI包开发规范**:
+[Source: docs/architecture/ui-package-standards.md]
+- API路径映射验证: 确保API路径与实际后端路由一致
+- 类型推断最佳实践: 使用RPC推断类型
+- 测试选择器优化: 为关键交互元素添加 `data-testid` 属性
+
+### 项目结构
+[Source: docs/architecture/source-tree.md]
+
+**订单相关包**:
+```
+allin-packages/
+├── order-module/                    # 订单后端模块
+│   ├── src/
+│   │   ├── entities/                # 数据实体
+│   │   │   └── employment-order.entity.ts
+│   │   ├── services/                # 业务逻辑
+│   │   │   └── order.service.ts
+│   │   └── routes/                  # API路由
+│   │       └── order-custom.routes.ts
+│   └── tests/
+└── order-management-ui/             # 订单前端UI
+    ├── src/
+    │   ├── components/              # UI组件
+    │   │   ├── OrderManagement.tsx
+    │   │   ├── OrderForm.tsx
+    │   │   └── OrderDetailModal.tsx
+    │   └── api/                     # API客户端
+    │       └── orderClient.ts
+    └── tests/                       # 测试文件
+        └── integration/
+```
+
+**关联包**:
+- `allin-packages/company-module`: 企业管理模块
+- `allin-packages/platform-module`: 平台管理模块
+- `allin-packages/channel-module`: 渠道管理模块
+
+### 技术栈
+[Source: docs/architecture/tech-stack.md]
+
+- **运行时**: Node.js 20.18.3
+- **前端框架**: React 19.1.0
+- **数据库**: PostgreSQL 17 + TypeORM 0.3.25
+- **状态管理**: React Query 5.83.0
+- **表单管理**: react-hook-form + zod
+- **测试框架**: Vitest 3.2.4
+
+### 前序故事经验
+[Source: docs/stories/018.001.story.md - Dev Agent Record]
+
+**故事018.001经验**:
+- 排查问题时先检查代码逻辑,再检查实际数据
+- 使用 `console.debug` 查看实际API返回数据
+- 前端显示问题可能是后端未返回完整数据导致
+- Entity关联关系需要正确定义才能使用 TypeORM 的 relations 加载
+
+### 可能的问题点
+
+#### 后端问题
+1. **Entity缺少关联定义**: EmploymentOrder 未定义与 Company/Platform/Channel 的关系
+2. **Service未加载关联**: `findOne()` 方法的 relations 中未包含关联表
+3. **返回数据不完整**: 只返回ID,不包含关联对象信息
+4. **Schema不支持关联对象**: Schema可能需要更新以支持嵌套对象
+
+#### 前端问题
+1. **显示ID而非名称**: 组件直接显示ID字段
+2. **未处理null**: 关联对象可能为空,需要null check
+3. **编辑功能未实现**: OrderForm可能只支持创建,不支持编辑
+4. **类型不匹配**: TypeScript类型可能未包含关联对象字段
+
+### 修复检查清单
+
+在修复过程中,请按以下顺序检查:
+
+1. [ ] 后端Entity是否定义了Company/Platform/Channel关联关系
+2. [ ] OrderService.findOne()是否加载了关联数据
+3. [ ] OrderService.findAll()是否加载了关联数据(用于列表显示)
+4. [ ] 后端API返回数据是否包含完整的关联对象信息
+5. [ ] Schema是否支持返回嵌套的关联对象
+6. [ ] 前端OrderDetailModal是否使用关联对象的名称字段
+7. [ ] 前端OrderManagement列表是否显示名称而非ID
+8. [ ] 前端OrderForm编辑功能是否正常工作
+9. [ ] 是否处理了关联对象为空的情况
+10. [ ] 是否添加了必要的单元测试和集成测试
+
+## Change Log
+| Date | Version | Description | Author |
+|------|---------|-------------|--------|
+| 2025-12-31 | 1.0 | 创建故事文档 | Bob (Scrum Master) |
+
+## Dev Agent Record
+
+### Agent Model Used
+待开发时填写
+
+### Debug Log References
+待开发时填写
+
+### Completion Notes List
+待开发时填写
+
+### File List
+待开发时填写
+
+## QA Results
+待QA测试时填写