diff --git a/.codex/agent-rules/backend.md b/.codex/agent-rules/backend.md index 8d05ecb09..91d85f288 100644 --- a/.codex/agent-rules/backend.md +++ b/.codex/agent-rules/backend.md @@ -30,6 +30,7 @@ - 无事务链路需求的普通查询、纯函数和简单私有方法继续使用明确参数。 - service 内部需要根据事务上下文选择 Repository 时,优先使用 `BaseService.getRepo(ctx, EntityClass)`;不要在业务方法里反复写 `ctx.manager?.getRepository(Entity) || this.xxxRepository`。 - 拿到 repo 后 save/update/delete/find 都能做,不需要再包一层 `saveEntity` 之类的单一用途方法。 +- service 拼接用户与项目范围查询条件时,优先使用 `BaseService.buildUserProjectQuery(userId, projectId)`;不要直接写 `{ userId, projectId }`,否则 `projectId` 为空时可能把 `null`/`undefined` 带入 TypeORM 条件,导致查询或 update 不符合预期。 - `ctx` 类型统一从 `BaseService` 导出的 `ServiceContext` 复用,不要在每个 service 里重复定义。 - 需要“有事务则复用、无事务则开启”时,使用 `BaseService.transactionWithCtx(ctx, callback)`:`ctx.manager` 存在则直接执行 callback,否则自动 `this.transaction()`。不要在业务代码里手写 `if (ctx.manager) { ... } else { await this.transaction(...) }`。 - 新增方法注意不要与 `BaseService` 基类方法签名冲突,例如 `delete(id)` vs `BaseService.delete(ids, where?)`,ts-node 下会直接 TS2416 编译报错。冲突时改用具体名称如 `deleteById`。 diff --git a/AGENTS.md b/AGENTS.md index 4bc5260cf..90f466ecb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,6 +47,7 @@ Certd 是支持私有化部署的 SSL/TLS 证书自动化管理平台,提供 W - 不要把 `packages/ui/certd-server/data/`、`logs/`、生成的 metadata/dist 等运行时或构建产物纳入改动,除非任务明确要求。 - 做数据库结构变更时,添加或更新迁移脚本,不要依赖 TypeORM 自动同步。 - 做插件相关任务时,先读取对应 `.trae/skills//SKILL.md`,再进入具体实现。 +- 后端 service 拼接可选 `projectId` 查询条件时,不要直接写 `{ userId, projectId }`;应使用 `BaseService.buildUserProjectQuery(userId, projectId)`,只有 `projectId != null` 时才加入查询条件。 ## 工作方式 diff --git a/packages/libs/lib-server/src/basic/base-service.ts b/packages/libs/lib-server/src/basic/base-service.ts index c33df847d..2dc04000a 100644 --- a/packages/libs/lib-server/src/basic/base-service.ts +++ b/packages/libs/lib-server/src/basic/base-service.ts @@ -56,6 +56,16 @@ export abstract class BaseService { return dataSource.getRepository(entity); } + protected buildUserProjectQuery(userId: number, projectId?: number) { + const query: { userId: number; projectId?: number } = { + userId, + }; + if (projectId != null) { + query.projectId = projectId; + } + return query; + } + /** * 获得单个ID * @param id ID diff --git a/packages/libs/lib-server/tsconfig.build.json b/packages/libs/lib-server/tsconfig.build.json index bcca8fcc1..949315930 100644 --- a/packages/libs/lib-server/tsconfig.build.json +++ b/packages/libs/lib-server/tsconfig.build.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "sourceMap": false, - "inlineSourceMap": false + "inlineSourceMap": false, + "declarationMap": true } } diff --git a/packages/ui/certd-server/src/config/config.default.ts b/packages/ui/certd-server/src/config/config.default.ts index 192a42b3c..d3beff1d3 100644 --- a/packages/ui/certd-server/src/config/config.default.ts +++ b/packages/ui/certd-server/src/config/config.default.ts @@ -80,7 +80,7 @@ const development = { type: "better-sqlite3", database: "./data/db.sqlite", synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true - logging: false, + logging: true, highlightSql: false, // 配置实体模型 或者 entities: '/entity', diff --git a/packages/ui/certd-server/src/configuration.ts b/packages/ui/certd-server/src/configuration.ts index 8eaaef70c..f24c45feb 100644 --- a/packages/ui/certd-server/src/configuration.ts +++ b/packages/ui/certd-server/src/configuration.ts @@ -134,5 +134,6 @@ export class MainConfiguration { }); logger.info("当前环境:", this.app.getEnv()); // prod + } } diff --git a/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.test.ts b/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.test.ts index 2e68ca984..1705d9be2 100644 --- a/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.test.ts +++ b/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.test.ts @@ -3,6 +3,7 @@ import { CertApplyTemplateService } from "./cert-apply-template-service.js"; function createService(list: any[]) { const service = new CertApplyTemplateService(); + const updateWhereList: any[] = []; function matchesWhere(item: any, where: any) { for (const key of Object.keys(where)) { const expected = where[key]; @@ -23,6 +24,7 @@ function createService(list: any[]) { return list.find(item => matchesWhere(item, where)); }, async update(where: any, patch: any) { + updateWhereList.push({ ...where }); for (const item of list) { if (matchesWhere(item, where)) { Object.assign(item, patch); @@ -30,6 +32,7 @@ function createService(list: any[]) { } }, }; + (service as any).updateWhereList = updateWhereList; return service; } @@ -158,7 +161,7 @@ describe("CertApplyTemplateService", () => { }); }); - it("sets default for templates with null project id", async () => { + it("does not include project id condition when setting default without project id", async () => { const list = [ { id: 1, @@ -189,8 +192,12 @@ describe("CertApplyTemplateService", () => { await service.setDefault(2, 10, null); + assert.deepEqual((service as any).updateWhereList, [ + { userId: 10 }, + { userId: 10, id: 2 }, + ]); assert.equal(list[0].isDefault, false); assert.equal(list[1].isDefault, true); - assert.equal(list[2].isDefault, true); + assert.equal(list[2].isDefault, false); }); }); diff --git a/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.ts b/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.ts index 1de6f935a..49f9aa211 100644 --- a/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.ts +++ b/packages/ui/certd-server/src/modules/cert/service/cert-apply-template-service.ts @@ -47,16 +47,16 @@ export class CertApplyTemplateService extends BaseService