## Context 参见 `proposal.md` 的 Why。当前运营弹窗配置读取链路由 Query 直接读取配置模型并映射为统一响应 DTO;配置表只保存 `creator`、`updater` 账号 ID,账号名称位于 `tb_account.username`。列表与详情共用响应 DTO,但当前映射函数没有账号名称输入。 项目要求读取使用 Query/GORM,关联通过 ID 显式查询,不新增 GORM 关联或数据库外键。账号采用软删除,历史配置需要能解析已删除账号。 ## Goals / Non-Goals **Goals:** - 列表和详情使用同一账号名称语义与响应字段。 - 列表名称补充保持常数次查询,不产生 N+1。 - 名称查询错误沿既有稳定错误链返回。 **Non-Goals:** - 不保存创建人或更新人的名称快照。 - 不修改配置写入、版本、启停、排序或权限语义。 - 不修改账号删除策略或数据库 Schema。 ## Decisions ### 1. 在响应 DTO 增加 `creator_name` 与 `updater_name` 保留 `creator`、`updater`,新增同语义名称字段。这样现有调用方保持兼容,后台可直接展示名称。未采用替换 ID 或嵌套账号对象,因为都会扩大契约变更并增加前端迁移成本。 ### 2. Query 批量读取当前账号名称 列表先读取当页配置,再收集非零且去重后的创建人、更新人 ID,通过一次 `Unscoped` GORM 查询读取 `id, username`,生成名称映射后投影响应。详情复用同一批量加载函数,以单元素 ID 集合获得相同语义。 未采用 SQL JOIN:当前 Query 已先执行总数与分页读取,独立批量查询更容易保持 Count、排序和配置模型扫描不变,也符合项目显式关联约束。未采用逐条账号查询,避免 N+1。 ### 3. 已删除账号可见,缺失账号返回空名称 账号查询使用 `Unscoped`,因此软删除账号仍能提供历史用户名。映射中没有对应记录时保留字符串零值,表示账号事实已不存在。该行为不制造占位文案,避免把展示策略固化到 API。 ### 4. 查询失败不得静默降级 账号名称查询失败包装为稳定数据库错误并终止列表或详情响应。静默返回空名称会把数据库故障与“账号记录不存在”混为同一可观察结果,违反契约。 ## Risks / Trade-offs - [账号用户名后续可修改,历史配置展示的是当前用户名而非操作时快照] → 本次明确只补充当前账号名称;若业务要求历史身份不可变,应另建名称快照变更。 - [列表增加一次账号查询] → 对当页 ID 去重并只选择 `id, username`,查询数量固定为一次。 - [历史脏数据引用不存在账号] → 保留账号 ID,名称返回空字符串,使调用方仍能识别原始关联值。 ## Migration Plan 1. 更新响应 DTO 与 Query 投影。 2. 重新生成 OpenAPI 文档并构建 API、Worker。 3. 在维护者指定的隔离环境请求列表和详情,核对正常账号、软删除账号及不存在账号三种结果。 4. 回滚只需恢复应用代码与生成文档;无数据库迁移或数据回滚。