从主工作区未提交改动合并回填 2026-09-09 从未提交的完整发布记录(origin/main 此前只有骨架,缺这部分细节): - 新候选发布准备草案:固定 b87cc55、只读预检清单、回滚目标确认(以当前 生产运行版本为准,不默认仍是历史 f223bfb)。 - 两笔提交推送回执:81102c2(迁移预检修复,归 09-06-release-gate-integrity) 与 b87cc55(本任务,17 文件)分别提交推送。 - 生产只读预检:迁移对账 PASS 无 pending,39/3/1 最低配置齐全,磁盘可用 8.32 GiB/使用率 85.9%。 - 获准 root 通道备份验真:备份 PASS_INTEGRITY_ONLY;生成 845 个可清理镜像 候选清单(7 天以上、无 tag/digest、无容器引用、排除生产回滚镜像)。 - 镜像清理执行:845 项逐一核验后删除 811 项、34 项 Docker 拒绝跳过,磁盘 从 8.32G 净增至 46.43 GiB(实测净变化,不是镜像虚拟大小相加)。 - 部署执行与复核:14:07 CST deploy-existing.sh 返回 0,运行 build 切换为 b87cc55aca0003130cf8a5bbe38d0dd15528389c,/ready 200,无 seed 阶段、 迁移无 pending,权限解析器逐项核对与候选一致。 无产品代码改动,这批全是已实际发生的生产操作(备份验真、镜像清理、发布) 留下的审计记录补齐。 Co-Authored-By: Claude Sonnet 5 <[email protected]>
9.6 KiB
已批准实施方案:初始化与日常升级分离
已核事实
- 当前本地基线 b918530。deploy/trialrun/deploy.sh 的 run-init 无条件执行配置预检、migrate deploy、seed:config、seed:trialrun。
- deploy/trialrun/docker-compose.yml 的 init 默认命令也执行 migrate 与两类 seed;只改 deploy.sh 不能覆盖默认 init 使用路径。
- seed-config 包括工作流取消激活后 upsert、假期条目删除重建等写入;seed-trialrun 对既有开关强制更新。不能用“本次值与默认相同”替代发布模式隔离。
- 原生命周期隔离 task 已约定 existing 不操作 db 生命周期、init 和应用采用依赖隔离;本轮保留这些合同。
拟采用路径
使用既有 existing/initialize 模式,不新增第三个部署模式。拟在 server/src/scripts 增加一个小型 init 命令,由 server/package.json 暴露统一入口,供 deploy.sh 和 Compose init 默认 command 共用;不复制两套分支。当前 Dockerfile 已 COPY server,无需为入口修改构建拓扑。模式必须显式传入,空值或非法值在迁移/seed 前失败;不默认 initialize。deploy.sh 用容器环境参数显式传递已验证的模式,Compose 直接调用则要求调用者提供模式。保持 --no-deps;受支持的直接调用文档同样明确依赖隔离,不改变 Compose 服务拓扑。
顺序:配置预检 → migrate deploy → initialize 才执行 seed:config 和 seed:trialrun → 两种模式均运行只读基础配置检查 → 成功才进入应用构建/切换。迁移和 seed 子命令非零即停,不重试、不自动改模式。未明确模式的旧直接 init 调用将失败,这是刻意的安全兼容变化,需在交审及操作手册写明。
只读配置检查是本项目报销主链路的最低配置门禁,不声称这些都是服务启动必需项。使用短只读事务和错误即停,只输出检查代码/数量及维护入口,不输出记录内容。拟阻断条件限定为以下三类完全缺失:至少一个 enabled expenseType、至少一个启用的 approvalRule、至少一个 entity_type=reimbursement 且 is_active 的 workflowDefinition(与当前提交入口选择条件一致)。不要求固定名称、固定 v1、固定角色、固定金额阈值或内容等于 seed 默认值。
这只是“非空”最低门禁,不证明金额区间无空洞、审批人都可解析或全部单据可提交。空库冒充 existing 因缺三类基础配置失败,不自动初始化。管理员故意关闭全部报销基础配置的环境也会停止发布,而不是被强制恢复;这是本方案新增的明确兼容约束,须用户批准。
确需新增配置的数据升级另设可审查的版本化变更;本轮不借默认 seed 自动补齐。发现缺项则报告维护路径,由负责人另批初始化或数据修复,不能在 existing 中静默写入。
风险及回滚
停止 seed 不等于停止 migrate,也不保证旧镜像能撤销数据变化。应用回滚、迁移兼容、数据库恢复分开说明。初始化 seed 仍有写入风险,非法模式和 init 失败必须停止切换。最终实施须在独立工作树进行,避免与尚未提交的 Trellis 修复混合。
已核源码与配置裁决
| 对象 | 证据 | 方案裁决 |
|---|---|---|
| 部署入口 | deploy-existing.sh 显式设置 existing;deploy.sh 本地及远端都校验模式,run-init 覆盖 Compose 默认 command | 两条 init 路径统一入口,模式传入容器;保留 V1/V2 Compose 支持 |
| 报销审批规则 | reimbursements.js 的 loadApprovalRuleForAmount 无匹配时 409 | 仅查启用规则非空;不宣称覆盖全部金额 |
| 报销工作流 | reimbursements.js 提交读取 active reimbursement definition;缺失时报业务错误 | 查 active 定义存在,不改审批链,不强制 v1 |
| 费用类型 | reimbursements.js 的 resolveEnabledExpenseTypesByIds 只接收 enabled 类型 | 查至少一个 enabled,不重建默认类型,不验证每种差旅费用 |
| 工作区 | workspaceSettings.js 的 mapWorkspaceSetting(null) 返回 buildDefaultWorkspaceSetting | 非发布硬阻断,不创建记录 |
| 编号 | documentSequence.js 的 resolveDocumentSequenceConfig 及 issueDocumentNumber 有解析兜底和业务时 upsert | 本检查不调用发号函数,不要求初始化序列存在,不更改业务发号写入 |
| 开关 | featureFlags/index.js 结合环境门禁与 DB 记录评估 | 不强制 force_on 或默认 rollout,不把管理员关闭视为缺配置 |
| 日历、预算、部门、用户/主体/银行卡及其他模块 | 属于各自业务就绪与配置范围 | 不扩为全站发布门禁,不读账号/银行资料,不在发布时自动维护 |
维护提示沿用费用类型、审批规则、流程定义的既有管理入口;若初始化数据根本未建,由运维另审 initialize 或版本化数据修复,不建议在 existing 重跑整套 seed。
实施文件与验证
预计涉及 deploy/trialrun/deploy.sh、docker-compose.yml、server/package.json、新的小型 init/只读检查脚本及定向测试,原 deployment spec/手册和本 task。复用 server/tests/unit/scripts/deployment-db-lifecycle.test.js、deployment-safety-contract.test.js;新测试覆盖真实统一入口执行顺序,不只匹配脚本文本。
矩阵:existing/initialize × 成功/非法或缺模式/配置预检失败/迁移失败/seed失败/只读缺项或查询失败。existing 中 seed 调用次数恒为零且成功路径必须真正到达应用步骤;initialize 的两个 seed 必须按序执行,失败不得到达应用步骤。直接 Compose 默认 command 使用同一入口,已有依赖隔离的变异测试继续生效。
合成库测试与生产完全隔离:验证 initialize 空库建立配置;人为修改已有开关、工作流、日历及其他 seed 管理表后运行 existing 两次,比较配置字段、版本/时间戳不变,迁移记录单列。只用测试身份/临时数据库,不能以无 Docker 或初始化失败冒充通过。环境前置不足先报告,不借用 ARM 正式归档或生产库。
审批边界
新候选发布准备草案(提交后,未授权部署)
- 候选固定为 b87cc55aca0003130cf8a5bbe38d0dd15528389c,已推远端。执行来源为干净的 seed-preflight-integration 副本,不使用仍在旧基线且有其他治理差异的主工作树。此前候选和例外不自动转移。
- 本地已有证据:已审两包、顺序补丁集成与 110/110 定向回归。尚无完整镜像构建或完整候选门禁通过证据;不将定向回归冒充普通发布资格,不自动启动暂停的 R1、阶段 B 或修复 58 条红项。
- 下一步申请本候选的一次生产只读预检:运行版本/镜像身份、候选迁移集合与数据库迁移状态、三类 enabled/active 最低配置计数、磁盘余量、最近备份时效及已有回滚制品元数据。只输出脱敏摘要;不读取个人访问历史、单据明细或银行信息,不执行 migrate/seed,不写配置,不切容器。现有审批记录不替代本轮明确边界。
- 预检必须执行已修的状态解析逻辑;如远端仍为旧脚本,应先设计获准的调用方式,不以“只读”名义自行上传或安装文件。读取受限则标未验证,不绕权限。
- 回滚目标以本轮确认的生产运行版本为准,不默认仍是历史 f223bfb。旧镜像必须保持独立可用;备份完整性与恢复能力分别记录。已知 DB 最小恢复成功不代表附件或应用恢复成功,也不证明新备份已恢复演练;不沿用历史“从未成功”的过期结论。
- 若最低配置缺失,报告管理员,不自动 seed/启用配置。existing 仍会执行迁移,配置检查失败可能发生在迁移落库之后,镜像回滚不能撤销数据变化;任何数据恢复还涉及恢复点之后业务数据的处置,另行审批。
- 汇总本轮证据后再提交发布决定:普通路径需要完整候选验收;若仍未具备,明确列出缺口与回滚验收,用户对本 SHA 单独批准例外和部署。未获批准不执行真实发布,不拿旧例外填空。
- 部署获批后按既有 existing 路径执行,检查日志无 seed 阶段、版本/健康一致;AP 验收覆盖仅 ceo/manager 拒绝、finance/finance_mgr 可读、兼财务多角色可读、审批/复核入口保留。真人制单/付款流程由授权人员验收,下载不等于付款;不代操作真实银行业务。
- 失败停止并报告,不自动扩大写入或恢复范围;成功也不删除独立回滚制品。磁盘治理、恢复附件/应用验证、统一待办和错误豁免期限沿原任务处理,不混进本次发布写入。
用户已同意本 task 规划方向;以上统一入口、直接调用显式模式和三类非空硬门禁为本轮收敛的待审实施方案。获批前保持 planning;不把此前“好的”解释成批准尚未呈现的兼容变化。实施只在独立工作树,不叠加未提交的治理修复。提交、推送、生产部署另批。
后续授权与实施:用户对收敛方案明确回复“是”,已进入独立工作树实施。共享入口为 server/src/scripts/deploy-init.js。只读检查通过 Prisma repeatable-read 事务显式 SET TRANSACTION READ ONLY;费用类型没有专用管理页面,改为提示管理员核对既有 /api/expense-types,缺失数据另批修复,不新增 UI。
环境契约同步限定 init 可注入且必须声明 DEPLOY_DATA_MODE,并要求 Compose 缺省展开为空,拒绝默认 initialize;server/worker 仍不允许该 override。此为已批准的模式传递配套,不扩大业务环境变量或放宽校验器。