|
@@ -218,33 +218,80 @@ Ready for Development
|
|
|
11. **枚举使用**:通过`@d8d/allin-enums`包导入OrderStatus和WorkStatus枚举
|
|
11. **枚举使用**:通过`@d8d/allin-enums`包导入OrderStatus和WorkStatus枚举
|
|
|
12. **区域包集成经验**:参考salary-module与geo-areas的集成方式处理外部依赖
|
|
12. **区域包集成经验**:参考salary-module与geo-areas的集成方式处理外部依赖
|
|
|
|
|
|
|
|
-### 关键经验教训总结(基于007.004和007.007):
|
|
|
|
|
|
|
+#### 从故事007.001(渠道管理模块)吸取的经验教训:
|
|
|
|
|
+1. **实体主键命名**:主键属性名应直接定义为`id`(而不是`channelId`)以遵循GenericCrudService约定
|
|
|
|
|
+2. **布尔返回值处理**:需要正确处理布尔返回值转换,保持与原始API兼容
|
|
|
|
|
+3. **软删除考虑**:考虑使用`status`字段实现软删除,而不是硬删除
|
|
|
|
|
+4. **路由聚合模式**:使用`.route('/', customRoutes).route('/', crudRoutes)`聚合自定义路由和CRUD路由
|
|
|
|
|
+5. **默认值设置**:创建时设置合理的默认值(contact_person, contact_phone等)
|
|
|
|
|
+
|
|
|
|
|
+#### 从故事007.002(公司管理模块)吸取的经验教训:
|
|
|
|
|
+1. **模块间依赖处理**:正确配置对`@d8d/allin-platform-module`的依赖
|
|
|
|
|
+2. **复合唯一性检查**:公司名称在同一平台下唯一性检查(`company_name`和`platform_id`组合)
|
|
|
|
|
+3. **关联关系配置**:正确配置与Platform实体的关联关系`@ManyToOne(() => Platform, { eager: true })`
|
|
|
|
|
+4. **跨模块业务逻辑**:处理跨模块的业务逻辑,如平台ID验证
|
|
|
|
|
+5. **排序配置**:默认按`company_id`降序排列
|
|
|
|
|
+
|
|
|
|
|
+#### 从故事007.003(枚举常量包)吸取的经验教训:
|
|
|
|
|
+1. **枚举值一致性**:保持与原始数据库值一致(小写字符串,下划线分隔)
|
|
|
|
|
+2. **数字枚举处理**:残疾等级使用数字枚举(1, 2, 3, 4)
|
|
|
|
|
+3. **注释完整性**:为每个枚举值添加中文注释说明和业务含义
|
|
|
|
|
+4. **包配置优化**:枚举常量包直接引用源码,避免不必要的构建步骤
|
|
|
|
|
+5. **类型安全**:提供类型安全的枚举常量,提高代码可读性和维护性
|
|
|
|
|
+
|
|
|
|
|
+#### 从故事007.006(平台管理模块)吸取的经验教训:
|
|
|
|
|
+1. **基础依赖包设计**:作为基础包,不需要依赖其他allin模块
|
|
|
|
|
+2. **唯一性索引配置**:平台名称唯一性索引配置
|
|
|
|
|
+3. **返回值格式**:成功返回`{ success: true }`,失败返回`{ success: false, message: "..." }`
|
|
|
|
|
+4. **错误信息明确**:提供明确的错误信息,如"平台不存在或名称重复"
|
|
|
|
|
+5. **路由模式参考**:参考channel-module的路由模式实现
|
|
|
|
|
+
|
|
|
|
|
+### 关键经验教训总结(基于史诗007所有已完成故事):
|
|
|
|
|
|
|
|
#### 数据库和类型相关:
|
|
#### 数据库和类型相关:
|
|
|
1. **PostgreSQL类型兼容**:注意tinyint → smallint,datetime → timestamp的类型转换
|
|
1. **PostgreSQL类型兼容**:注意tinyint → smallint,datetime → timestamp的类型转换
|
|
|
2. **Decimal字段处理**:数据库返回的decimal字段可能是字符串格式,需要使用z.coerce.number()处理
|
|
2. **Decimal字段处理**:数据库返回的decimal字段可能是字符串格式,需要使用z.coerce.number()处理
|
|
|
3. **Schema一致性**:确保实体字段名与Schema定义完全匹配,特别注意时间字段命名
|
|
3. **Schema一致性**:确保实体字段名与Schema定义完全匹配,特别注意时间字段命名
|
|
|
|
|
+4. **枚举值一致性**:保持与原始数据库值一致(小写字符串,下划线分隔)
|
|
|
|
|
+5. **数字枚举处理**:残疾等级等使用数字枚举(1, 2, 3, 4)
|
|
|
|
|
+
|
|
|
|
|
+#### 实体设计相关:
|
|
|
|
|
+6. **实体主键命名**:主键属性名应直接定义为`id`以遵循GenericCrudService约定
|
|
|
|
|
+7. **唯一性约束**:配置正确的唯一性索引(单字段或复合字段)
|
|
|
|
|
+8. **关联关系配置**:正确配置实体间关联关系,如`@ManyToOne(() => Platform, { eager: true })`
|
|
|
|
|
+9. **软删除考虑**:考虑使用`status`字段实现软删除,而不是硬删除
|
|
|
|
|
+10. **默认值设置**:创建时设置合理的默认值
|
|
|
|
|
|
|
|
#### 测试相关:
|
|
#### 测试相关:
|
|
|
-4. **测试数据完整性**:确保测试数据包含所有必填字段(如subBankName、operatorId等)
|
|
|
|
|
-5. **唯一性约束**:测试数据避免创建违反唯一性约束的记录
|
|
|
|
|
-6. **测试期望值**:根据实际数据调整测试断言期望值
|
|
|
|
|
-7. **调试信息**:在路由中添加console.debug便于问题排查
|
|
|
|
|
|
|
+11. **测试数据完整性**:确保测试数据包含所有必填字段
|
|
|
|
|
+12. **唯一性约束**:测试数据避免创建违反唯一性约束的记录
|
|
|
|
|
+13. **测试期望值**:根据实际数据调整测试断言期望值
|
|
|
|
|
+14. **调试信息**:在路由中添加console.debug便于问题排查
|
|
|
|
|
|
|
|
#### 路由和API相关:
|
|
#### 路由和API相关:
|
|
|
-8. **路由冲突**:CRUD路由可能与自定义路由冲突,需要合理设计路由结构
|
|
|
|
|
-9. **错误响应**:确保DELETE返回200(成功)而不是404,GET聚合端点正确处理404场景
|
|
|
|
|
-10. **API兼容性**:保持与原始NestJS API相同的请求/响应格式
|
|
|
|
|
|
|
+15. **路由冲突**:CRUD路由可能与自定义路由冲突,需要合理设计路由结构
|
|
|
|
|
+16. **路由聚合模式**:使用`.route('/', customRoutes).route('/', crudRoutes)`聚合路由
|
|
|
|
|
+17. **错误响应**:确保DELETE返回200(成功)而不是404,GET聚合端点正确处理404场景
|
|
|
|
|
+18. **API兼容性**:保持与原始NestJS API相同的请求/响应格式
|
|
|
|
|
+19. **布尔返回值处理**:需要正确处理布尔返回值转换
|
|
|
|
|
+20. **错误信息明确**:提供明确的错误信息,如"平台不存在或名称重复"
|
|
|
|
|
|
|
|
#### 模块集成相关:
|
|
#### 模块集成相关:
|
|
|
-11. **文件模块集成**:正确使用fileId字段而不是URL字段,建立与File实体的关联
|
|
|
|
|
-12. **循环依赖处理**:使用ID引用代替直接实体引用,避免模块间循环依赖
|
|
|
|
|
-13. **枚举集成**:通过@d8d/allin-enums包导入共享枚举
|
|
|
|
|
|
|
+21. **文件模块集成**:正确使用fileId字段而不是URL字段,建立与File实体的关联
|
|
|
|
|
+22. **循环依赖处理**:使用ID引用代替直接实体引用,避免模块间循环依赖
|
|
|
|
|
+23. **枚举集成**:通过@d8d/allin-enums包导入共享枚举
|
|
|
|
|
+24. **模块间依赖处理**:正确配置模块间依赖关系
|
|
|
|
|
+25. **跨模块业务逻辑**:处理跨模块的业务逻辑,如平台ID验证
|
|
|
|
|
+26. **基础依赖包设计**:基础包不需要依赖其他allin模块
|
|
|
|
|
+27. **区域包集成**:参考salary-module与geo-areas的集成方式
|
|
|
|
|
|
|
|
#### 开发流程相关:
|
|
#### 开发流程相关:
|
|
|
-14. **workspace配置**:及时在pnpm-workspace.yaml中添加新包路径
|
|
|
|
|
-15. **类型检查**:使用pnpm typecheck检查类型错误,特别注意枚举导入
|
|
|
|
|
-16. **测试依赖**:集成测试需要正确配置测试数据库和实体
|
|
|
|
|
|
|
+28. **workspace配置**:及时在pnpm-workspace.yaml中添加新包路径
|
|
|
|
|
+29. **类型检查**:使用pnpm typecheck检查类型错误,特别注意枚举导入
|
|
|
|
|
+30. **测试依赖**:集成测试需要正确配置测试数据库和实体
|
|
|
|
|
+31. **包配置优化**:枚举常量包直接引用源码,避免不必要的构建步骤
|
|
|
|
|
+32. **排序配置**:默认按主键降序排列
|
|
|
|
|
+33. **注释完整性**:为枚举值添加中文注释说明和业务含义
|
|
|
|
|
|
|
|
### 技术细节:
|
|
### 技术细节:
|
|
|
1. **OrderStatus和WorkStatus枚举集成**:
|
|
1. **OrderStatus和WorkStatus枚举集成**:
|
|
@@ -447,6 +494,102 @@ app.post('/create', async (c) => {
|
|
|
});
|
|
});
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+### 7. 布尔返回值处理(基于007.001和007.006)
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 正确处理布尔返回值,保持与原始API兼容
|
|
|
|
|
+app.post('/create', async (c) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const result = await service.create(data);
|
|
|
|
|
+ return c.json({ success: true }, 200);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ success: false,
|
|
|
|
|
+ message: error.message || '创建失败'
|
|
|
|
|
+ }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 8. 软删除实现(基于007.001和007.002)
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 考虑使用status字段实现软删除
|
|
|
|
|
+@Column({
|
|
|
|
|
+ name: 'status',
|
|
|
|
|
+ type: 'tinyint',
|
|
|
|
|
+ default: 1,
|
|
|
|
|
+ comment: '状态:0-删除,1-正常'
|
|
|
|
|
+})
|
|
|
|
|
+status!: number;
|
|
|
|
|
+
|
|
|
|
|
+// 在服务层实现软删除
|
|
|
|
|
+async delete(id: number): Promise<boolean> {
|
|
|
|
|
+ const entity = await this.repository.findOne({ where: { id } });
|
|
|
|
|
+ if (!entity) return false;
|
|
|
|
|
+
|
|
|
|
|
+ entity.status = 0; // 软删除
|
|
|
|
|
+ entity.updateTime = new Date();
|
|
|
|
|
+ await this.repository.save(entity);
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 9. 复合唯一性检查(基于007.002)
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 公司名称在同一平台下唯一性检查
|
|
|
|
|
+async create(data: CreateCompanyDto): Promise<Company> {
|
|
|
|
|
+ // 检查(companyName, platformId)组合是否已存在
|
|
|
|
|
+ const existing = await this.repository.findOne({
|
|
|
|
|
+ where: {
|
|
|
|
|
+ companyName: data.companyName,
|
|
|
|
|
+ platformId: data.platformId,
|
|
|
|
|
+ status: 1 // 只检查正常状态的记录
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (existing) {
|
|
|
|
|
+ throw new Error('同一平台下公司名称已存在');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return super.create(data);
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 10. 枚举使用规范(基于007.003)
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 保持与数据库值一致的小写字符串枚举
|
|
|
|
|
+import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
|
|
|
|
|
+
|
|
|
|
|
+// 在实体中使用
|
|
|
|
|
+@Column({
|
|
|
|
|
+ name: 'order_status',
|
|
|
|
|
+ type: 'enum',
|
|
|
|
|
+ enum: OrderStatus,
|
|
|
|
|
+ default: OrderStatus.DRAFT,
|
|
|
|
|
+ comment: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消'
|
|
|
|
|
+})
|
|
|
|
|
+orderStatus!: OrderStatus;
|
|
|
|
|
+
|
|
|
|
|
+// 在Schema中验证
|
|
|
|
|
+const CreateOrderSchema = z.object({
|
|
|
|
|
+ orderStatus: z.nativeEnum(OrderStatus).default(OrderStatus.DRAFT),
|
|
|
|
|
+ workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING),
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 11. 路由聚合模式(基于007.001和007.006)
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 合理聚合自定义路由和CRUD路由
|
|
|
|
|
+import customRoutes from './order-custom.routes';
|
|
|
|
|
+import crudRoutes from './order-crud.routes';
|
|
|
|
|
+
|
|
|
|
|
+const orderRoutes = new Hono()
|
|
|
|
|
+ .basePath('/orders')
|
|
|
|
|
+ .route('/', customRoutes) // 自定义业务逻辑路由
|
|
|
|
|
+ .route('/', crudRoutes); // 标准CRUD路由(readOnly: true)
|
|
|
|
|
+
|
|
|
|
|
+export default orderRoutes;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
## Dev Agent Record
|
|
## Dev Agent Record
|
|
|
*将在开发完成后填写*
|
|
*将在开发完成后填写*
|
|
|
|
|
|