Ready for Development
As a 开发者, I want 将order模块从allin_system-master移植为独立包@d8d/allin-order-module,完成实体重构、文件模块集成和循环依赖处理, so that 我们可以将Allin系统的订单管理功能集成到当前项目中,遵循现有的模块化架构和编码标准,并实现与@d8d/file-module的文件集成和与@d8d/allin-disability-module的依赖解耦。
allin-packages/order-module目录结构@d8d/allin-enums包中的OrderStatus和WorkStatus枚举OrderPersonAsset实体,添加fileId字段引用File实体fileId参数@d8d/file-module和@d8d/allin-enums的依赖allin-packages/order-module/allin-packages/salary-module/package.json
allin-packages/salary-module/package.json@d8d/allin-order-module,添加对@d8d/file-module和@d8d/allin-enums的依赖@d8d/file-module, @d8d/allin-enums, @d8d/core-module, @d8d/shared-crud, @d8d/shared-utilspnpm-workspace.yaml中添加allin-packages/*配置allin-packages/salary-module/tsconfig.json
allin-packages/salary-module/tsconfig.jsonallin-packages/salary-module/vitest.config.ts
allin-packages/salary-module/vitest.config.tspnpm-workspace.yaml中添加allin-packages/order-module
/mnt/code/188-179-template-6/pnpm-workspace.yaml'allin-packages/order-module'源文件路径: allin_system-master/server/src/order/employment_order.entity.ts
目标文件路径: allin-packages/order-module/src/entities/employment-order.entity.ts
参考对照文件:
allin-packages/salary-module/src/entities/salary-level.entity.ts(实体结构)allin-packages/enums/src/enums/order-status.enum.ts(枚举引用)allin-packages/enums/src/enums/work-status.enum.ts(枚举引用)修改要点:
orderId改为id以遵循GenericCrudService约定orderStatus字段类型从string改为OrderStatus枚举workStatus字段,使用WorkStatus枚举order_name → orderName, platform_id → platformId等@Entity('employment_order')源文件路径: allin_system-master/server/src/order/order_person.entity.ts
目标文件路径: allin-packages/order-module/src/entities/order-person.entity.ts
参考对照文件:
allin-packages/disability-module/src/entities/disabled-person.entity.ts(实体结构)DisabledPerson实体,需要解耦处理修改要点:
opId改为idpersonId字段类型保持为number(不直接引用DisabledPerson实体)@Entity('order_person')DisabledPerson的直接引用,改为使用personId: number源文件路径: allin_system-master/server/src/order/order_person_asset.entity.ts
目标文件路径: allin-packages/order-module/src/entities/order-person-asset.entity.ts
参考对照文件:
allin-packages/disability-module/src/entities/disabled-photo.entity.ts(文件实体集成)packages/file-module/src/entities/file.entity.ts(文件实体引用)修改要点:
opId改为idassetUrl: string字段,添加fileId: number字段@ManyToOne(() => File)DisabledPerson的直接引用,改为使用personId: numberAssetType和AssetFileType枚举移到实体文件中(模块内部枚举)@Entity('order_person_asset')目标文件路径:
allin-packages/order-module/src/dto/create-order.dto.tsallin-packages/order-module/src/dto/update-order.dto.tsallin-packages/order-module/src/dto/create-order-person.dto.tsallin-packages/order-module/src/dto/create-order-person-asset.dto.tsallin-packages/order-module/src/schemas/order.schema.ts参考对照文件:
allin-packages/salary-module/src/dto/create-salary.dto.ts(DTO结构)allin-packages/salary-module/src/dto/update-salary.dto.tsallin-packages/salary-module/src/schemas/salary.schema.ts(Zod schema)allin-packages/disability-module/src/schemas/disabled-person.schema.ts(文件ID验证)修改要点:
fileId验证OrderStatus和WorkStatus枚举源文件路径: allin_system-master/server/src/order/order.service.ts
目标文件路径: allin-packages/order-module/src/services/order.service.ts
参考对照文件:
allin-packages/salary-module/src/services/salary.service.ts(服务层结构)allin-packages/disability-module/src/services/disabled-person.service.ts(文件验证逻辑)修改要点:
GenericCrudService<EmploymentOrder>DisabledPersonRepository的直接依赖fileId的有效性源文件路径: allin_system-master/server/src/order/order.controller.ts
目标文件路径: allin-packages/order-module/src/routes/order.routes.ts
参考对照文件:
allin-packages/salary-module/src/routes/salary.routes.ts(Hono路由结构)allin-packages/disability-module/src/routes/disabled-person.routes.ts(文件ID参数处理)修改要点:
fileId参数处理目标文件路径:
allin-packages/order-module/src/index.tsallin-packages/order-module/src/order.module.ts参考对照文件:
allin-packages/salary-module/src/index.ts(导出结构)allin-packages/salary-module/src/salary.module.ts(模块定义)目标文件路径:
allin-packages/order-module/tests/unit/order.controller.test.tsallin-packages/order-module/tests/unit/order.service.test.tsallin-packages/order-module/tests/integration/order.integration.test.ts参考对照文件:
allin-packages/salary-module/tests/unit/salary.controller.test.ts(单元测试结构)allin-packages/salary-module/tests/integration/salary.integration.test.ts(集成测试结构)allin-packages/disability-module/tests/integration/disability.integration.test.ts(文件集成测试)测试重点:
OrderStatus和WorkStatus枚举的正确使用问题: 订单模块与disability-module存在相互引用 源文件分析:
DisabledPerson实体EmploymentOrder和OrderPerson实体解决方案:
PersonReference接口,避免直接引用DisabledPerson实体personId: number代替直接的实体引用allin-packages/salary-module与@d8d/geo-areas的集成方式实施步骤:
PersonReference类型personId: number代替person: DisabledPersonpersonId: number代替person: DisabledPersonpnpm testpnpm test:integrationpnpm typecheckpnpm buildid作为主键字段名,而不是orderId、opId等特定名称@d8d/file-module的关联关系,参考disabled-photo.entity.tspnpm-workspace.yaml中添加新包路径'allin-packages/order-module'tests/integration/setup.tspnpm typecheck检查类型错误,特别注意枚举导入和循环依赖@d8d/allin-enums包导入OrderStatus和WorkStatus枚举id(而不是channelId)以遵循GenericCrudService约定status字段实现软删除,而不是硬删除.route('/', customRoutes).route('/', crudRoutes)聚合自定义路由和CRUD路由@d8d/allin-platform-module的依赖company_name和platform_id组合)@ManyToOne(() => Platform, { eager: true })company_id降序排列{ success: true },失败返回{ success: false, message: "..." }id以遵循GenericCrudService约定@ManyToOne(() => Platform, { eager: true })status字段实现软删除,而不是硬删除.route('/', customRoutes).route('/', crudRoutes)聚合路由OrderStatus和WorkStatus枚举集成:
import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
@Column({
name: 'order_status',
type: 'enum',
enum: OrderStatus,
default: OrderStatus.DRAFT,
comment: '订单状态'
})
orderStatus!: OrderStatus;
@Column({
name: 'work_status',
type: 'enum',
enum: WorkStatus,
default: WorkStatus.NOT_WORKING,
comment: '工作状态'
})
workStatus!: WorkStatus;
OrderPersonAsset实体文件集成:
import { File } from '@d8d/file-module/entities';
@Column({
name: 'file_id',
type: 'int',
nullable: false,
comment: '文件ID,引用files表'
})
fileId!: number;
@ManyToOne(() => File)
@JoinColumn({ name: 'file_id' })
file!: File;
循环依赖处理方案:
// 原代码(有循环依赖):
// @ManyToOne(() => DisabledPerson)
// person!: DisabledPerson;
// 新代码(解耦):
@Column({
name: 'person_id',
type: 'int',
nullable: false,
comment: '人员ID'
})
personId!: number;
// 可选:定义引用接口
export interface PersonReference {
id: number;
name: string;
idCard: string;
}
AssetType和AssetFileType枚举:
order-person-asset.entity.ts中定义即可TAX = 'tax', SALARY = 'salary'等@d8d/file-module// 处理PostgreSQL类型兼容
@Column({
name: 'order_status',
type: 'enum',
enum: OrderStatus,
default: OrderStatus.DRAFT
})
orderStatus!: OrderStatus;
// 处理decimal字段
@Column({
name: 'total_amount',
type: 'decimal',
precision: 10,
scale: 2
})
totalAmount!: number; // Schema中使用z.coerce.number()
// 使用z.coerce.number()处理数据库返回的字符串
const CreateOrderSchema = z.object({
totalAmount: z.coerce.number().min(0),
// 确保字段名与实体完全一致
createTime: z.string().datetime().optional(),
updateTime: z.string().datetime().optional(),
});
// 确保测试数据包含所有必填字段
const testOrderData = {
orderName: '测试订单',
platformId: 1,
orderStatus: OrderStatus.DRAFT,
workStatus: WorkStatus.NOT_WORKING,
totalAmount: 1000.00,
// 不要遗漏任何必填字段
};
// 合理设计路由结构,避免CRUD路由与自定义路由冲突
const orderRoutes = new Hono()
.basePath('/orders')
.route('/', customRoutes) // 自定义路由
.route('/', crudRoutes); // CRUD路由(设置为readOnly: true)
// DELETE成功返回200,GET聚合端点正确处理404
app.delete('/:id', async (c) => {
const result = await service.delete(id);
return c.json({ success: true }, 200); // 不是404
});
app.get('/aggregated/:id', async (c) => {
const data = await service.findAggregated(id);
if (!data) {
return c.json({ error: 'Not found' }, 404); // 正确处理404
}
return c.json(data);
});
// 在关键路由中添加console.debug便于问题排查
app.post('/create', async (c) => {
const body = await c.req.json();
console.debug('创建订单请求:', body);
// ...处理逻辑
});
// 正确处理布尔返回值,保持与原始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);
}
});
// 考虑使用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;
}
// 公司名称在同一平台下唯一性检查
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);
}
// 保持与数据库值一致的小写字符串枚举
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),
});
// 合理聚合自定义路由和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;
开发时间: 2025-12-03 开发者: James (全栈开发工程师) 开发状态: 类型检查已通过,准备运行完整测试
✅ 创建包结构和配置文件 (AC: 1)
allin-packages/order-module/@d8d/file-module和@d8d/allin-enums的依赖'allin-packages/order-module'✅ 迁移实体文件 (AC: 2, 3, 4)
personId: number代替DisabledPerson引用fileId字段和File实体关联✅ 创建DTO和Schema文件 (AC: 8)
✅ 迁移控制器和服务 (AC: 6, 7)
✅ 创建模块主文件 (AC: 1)
✅ 创建测试文件 (AC: 10)
✅ 处理循环依赖 (AC: 5)
personId: number代替DisabledPerson的直接引用✅ 集成测试失败:创建订单API返回500错误,错误信息:"Cannot read properties of undefined (reading 'orderStatus')"
CreateOrderSchema但Schema文件中导出的是CreateEmploymentOrderSchemaexport const CreateOrderSchema = CreateEmploymentOrderSchema;✅ 订单人员资产创建测试失败:返回400错误
AssetType.DISABILITY_CERT枚举值✅ 类型检查失败:运行pnpm typecheck发现大量类型错误
@d8d/allin-enums包中的OrderStatus和WorkStatus枚举故事创建时间:2025-12-02 创建者:Bob (Scrum Master) 更新说明:重新创建故事,吸取之前移植故事的经验教训,在任务中标注迁移文件的路径和参考对照文件的路径 经验教训更新:2025-12-02,基于故事007.004和007.007的经验教训补充 开发记录更新:2025-12-03,记录开发进度和当前问题