Files
olons_admin/docs/admin_reference_prd/第12章状态机与数据模型补强设计.md
T

17 KiB
Raw Blame History

第 12 章状态机与数据模型补强设计

文档版本:V1.0
编制日期:2026-10-08
依据:后台管理系统需求文档第 12 章
关联设计:后台管理系统概要设计
适用项目:olons_admin

1. 文档目标

本文档用于补齐需求文档第 12 章中尚未闭环的状态机、资金冲正、内容归档与历史版本回滚能力,作为数据库迁移、后端接口、前端操作、权限配置和测试验收的实施依据。

本次设计遵循以下原则:

  1. 复用现有 HotGo 分层、字典、审批、审计、权限和多租户能力。
  2. 不修改已经投入使用的状态编号,只追加新状态或转换规则。
  3. 客户端只提交业务意图,最终状态由服务端状态机决定。
  4. 已生效资金不得修改或删除历史台账,只能生成反向流水冲正。
  5. 历史内容和配置不得原地覆盖,回滚必须生成新的草稿版本。
  6. 所有高风险执行都必须重新检查业务状态、审批状态和当前数据版本。

2. 实施范围

2.1 本期范围

  • 会员状态合法转换和注销不可逆。
  • 充值入账后的审批冲正。
  • 任务佣金结算后的审批冲正。
  • 文本内容归档和历史版本生成回滚草稿。
  • 状态转换、重复执行、并发执行和资金一致性测试。
  • 新增操作对应的菜单权限、业务审计和幂等保护。

2.2 不在本期范围

  • 管理员 MFA 的真实认证流程。
  • 支付渠道退款接口和外部清算实现。
  • 新建独立的状态机管理页面。
  • 修改 HotGo 原有管理员、角色、字典和个人设置模块。
  • 直接修改 GoFrame CLI 生成的 entity、do、dao/internal 文件。

MFA 数据字段继续保留,但真实验证码、再认证凭证和会话升级机制后续单独设计开发。

3. 现状评估

领域 已有能力 本次缺口
会员 正常、禁用、注销;风险状态;双人审批;状态日志 未限制全部非法转换,注销后仍可能被旧审批恢复
钱包 可用余额、冻结余额、乐观锁、不可变台账、幂等键、reversal_of 充值和任务佣金尚未提供完整冲正入口
充值 待支付、待审核、成功、驳回、过期、取消;审核入账 成功后无冲正状态、审批和执行流程
任务审核 待审核、通过、驳回、已结算、已冲正;佣金台账字段 已冲正仅有字典和字段,缺少业务实现
内容 草稿、待审、已发布、已下线、已归档;内容版本 缺少归档和历史版本生成回滚草稿接口
配置 草稿、审批、发布、版本、回滚草稿 已满足本章要求,本期不重做
审计 Trace ID、前后摘要、哈希链、审批单号 新增流程需要补充结构化差异和冲正关联信息

4. 状态机设计

4.1 通用规则

每个状态变更必须依次执行:

  1. 查询目标记录并加行锁。
  2. 校验记录存在且属于当前租户。
  3. 校验当前状态与申请时的 fromStatus 一致。
  4. 校验 fromStatus → toStatus 在允许转换表中。
  5. 校验审批已通过、尚未执行,且申请人与执行人符合职责分离要求。
  6. 在同一事务内完成业务状态、台账、审批执行时间和审计记录更新。
  7. 事务提交后再触发缓存清理、通知或 Outbox 事件。

状态转换判断统一放在后端,不接受前端直接指定任意结果状态。

4.2 会员状态

现有会员状态编号保持不变:

常量 值 含义
AppMemberStatusNormal 1 正常
AppMemberStatusDisabled 2 禁用
AppMemberStatusCancelled 3 已注销

允许转换:

当前状态 目标状态 是否允许 说明
正常 禁用 是 必须审批
正常 注销 是 必须审批,执行后不可恢复
禁用 正常 是 必须审批
禁用 注销 是 必须审批
注销 任意状态 否 注销不可逆
任意状态 相同状态 否 防止无意义重复申请

需求中的“受限”继续由现有 risk_status 表达,不新增或重排会员主状态:

  • AppMemberRiskNormal:正常。
  • AppMemberRiskWatch:观察或限制部分能力。
  • AppMemberRiskBlocked:拦截高风险能力。

发起状态审批时保存 fromStatus;执行时必须重新比对。如果会员状态已变化,则旧审批失效,不得覆盖最新状态。

4.3 充值状态

保留现有 1~6 状态,追加:

常量 值 含义
RechargeReversed 7 已冲正

核心转换:

当前状态 目标状态 触发方式
用户未支付 手动审核 支付凭证或支付结果进入审核
用户未支付 已过期/已取消 超时任务或用户取消
手动审核 已入账 审批通过并写入入账台账
手动审核 手动驳回 审批驳回
已入账 已冲正 冲正审批通过并生成反向台账

已入账 → 已冲正 是成功充值唯一允许的后续资金状态转换。已冲正记录不得再次冲正。

4.4 任务审核状态

保留现有状态编号:

常量 值 含义
TaskSubmissionPending 1 待审核
TaskSubmissionApproved 2 已通过
TaskSubmissionRejected 3 已驳回
TaskSubmissionSettled 4 已结算
TaskSubmissionReversed 5 已冲正

当前佣金为同步结算时,可在同一事务中完成“待审核 → 已通过 → 已结算”,数据库最终状态为“已结算”;若以后改为异步结算,“已通过”可作为中间状态。

允许转换:

当前状态 目标状态 说明
待审核 已驳回 保存驳回原因和是否允许重提
待审核 已结算 审核通过并成功写入佣金台账
已结算 已冲正 冲正审批通过并写入反向台账
已驳回 其他状态 禁止;重新提交需生成新提交记录
已冲正 其他状态 禁止

4.5 内容状态

使用现有 BizPublishStatusOptions:

草稿 → 待审 → 已发布 → 已下线 → 已归档

规则:

  • 只有草稿可以提交审核。
  • 只有审批通过的版本可以发布。
  • 只有已发布内容可以下线。
  • 只有已下线内容可以归档。
  • 已归档内容不可直接编辑、发布或恢复。
  • 历史版本回滚不修改历史版本,而是复制为一个版本号更大的新草稿。

站点配置继续使用现有“历史版本 → 新回滚草稿 → 审批 → 发布”流程,不增加直接覆盖线上配置的接口。

5. 数据库变更

新增迁移文件:

server/storage/data/migrations/20261008_009_state_machine_hardening.sql

5.1 hg_recharge_order

字段 类型 默认值 说明
reversal_approval_id bigint unsigned 0 冲正审批单 ID
reversal_ledger_id bigint unsigned 0 冲正台账 ID
reversed_at datetime(3) NULL 冲正执行时间

建议增加索引:

KEY `idx_recharge_reversal_approval` (`tenant_id`,`reversal_approval_id`)

5.2 hg_task_submission

保留已有 reversal_approval_id,新增:

字段 类型 默认值 说明
reversal_ledger_id bigint unsigned 0 佣金冲正台账 ID
reversed_at datetime(3) NULL 冲正执行时间

建议增加索引:

KEY `idx_task_submission_reversal` (`tenant_id`,`reversal_approval_id`)

5.3 hg_content_version

为了保留回滚来源,新增:

字段 类型 默认值 说明
base_version_id bigint unsigned 0 新版本来源的历史版本 ID

正常新增版本可为 0;通过回滚生成的新草稿必须保存来源版本 ID。

5.4 生成代码

迁移执行后,使用 GoFrame CLI 重新生成下列表对应的代码:

  • hg_recharge_order
  • hg_task_submission
  • hg_content_version

生成范围包括 entity、do、dao/internal 和 dao,禁止手工修改生成文件。

6. 后端接口设计

6.1 会员状态

复用现有接口:

  • POST /appMember/statusRequest
  • POST /appMember/statusExecute

补强内容:

  • StatusRequest 校验允许转换并写入申请时状态。
  • StatusExecute 锁定会员记录后再次校验允许转换。
  • 已注销会员禁止发起和执行任何恢复状态请求。
  • 业务审计必须记录 before.memberStatus 和 after.memberStatus。

6.2 充值冲正

新增接口:

接口 Method 权限 说明
/rechargeOrder/reversalRequest POST /rechargeOrder/reversalRequest 发起充值冲正审批
/rechargeOrder/reversalExecute POST /rechargeOrder/reversalExecute 执行已通过的充值冲正

申请参数至少包括:

  • 充值单 ID。
  • 标准原因编码。
  • 补充说明。
  • 幂等键。
  • 证据附件或证据摘要,按现有 JSON 附件格式保存。

申请前置条件:

  • 充值单状态为“已入账”。
  • 存在原入账台账。
  • 尚未发起或执行冲正。
  • 原审批和入账结果有效。

执行必须在同一事务中:

  1. 锁定审批单、充值单、钱包账户和原台账。
  2. 校验审批已通过且未执行。
  3. 校验充值单仍为“已入账”。
  4. 校验原台账属于该充值单和该钱包。
  5. 生成金额相同、方向相反的新台账,并设置 reversal_of=原台账ID。
  6. 更新钱包余额和乐观锁版本,余额不得为负数。
  7. 更新充值单为“已冲正”,保存冲正审批、台账和时间。
  8. 标记审批已执行并写入业务审计。

6.3 任务佣金冲正

新增接口:

接口 Method 权限 说明
/taskSubmission/reversalRequest POST /taskSubmission/reversalRequest 发起佣金冲正审批
/taskSubmission/reversalExecute POST /taskSubmission/reversalExecute 执行已通过的佣金冲正

申请前置条件:

  • 提交记录状态为“已结算”。
  • ledger_id 有效。
  • 尚未发起或执行冲正。
  • 必须填写标准原因和补充说明。

执行逻辑与充值冲正一致,但业务状态更新为 TaskSubmissionReversed,冲正流水指向佣金原流水。

6.4 内容归档与回滚

新增接口:

接口 Method 权限 说明
/content/archive POST /content/archive 将已下线内容归档
/content/rollback POST /content/rollback 根据历史版本生成新草稿

归档规则:

  • 当前内容必须为“已下线”。
  • 归档后不可编辑或直接重新发布。
  • 保存操作人、原因和审计记录。

回滚规则:

  • 指定内容 ID 和历史版本 ID。
  • 历史版本必须属于该内容。
  • 新版本号为当前最大版本号加 1。
  • 复制历史版本正文、摘要、封面和附件信息。
  • 新版本状态为“草稿”,base_version_id 指向来源版本。
  • 不修改当前线上版本和历史版本。

7. 后端代码影响范围

按 HotGo 分层补充以下内容:

层级 主要文件
常量与字典 server/internal/consts/trade.go、product_task.go、portal.go
API 契约 server/api/admin/rechargeorder/、tasksubmission/、portal/
Controller server/internal/controller/admin/sys/ 对应薄控制器
Service 在 server/internal/service/sys.go 的现有接口中追加方法
Input server/internal/model/input/sysin/ 对应模块文件
Logic app_member.go、recharge_order.go、task_submission.go、portal.go
路由 复用现有模块控制器注册,无需新增一级菜单
迁移 20261008_009_state_machine_hardening.sql

状态值合法性通过后端字典校验;字段名使用 dao.Xxx.Columns(),禁止在 Logic 中硬编码数据库列名。

8. 前端设计

不增加一级菜单,只在现有页面增加操作入口。

8.1 充值管理

  • 状态为“已入账”且拥有申请权限时显示“申请冲正”。
  • 已存在冲正审批时显示审批状态,不允许重复申请。
  • 审批通过且拥有执行权限时显示“执行冲正”。
  • 已冲正记录显示原台账和冲正台账关联信息。

8.2 会员任务审核

  • 状态为“已结算”时显示“申请冲正”。
  • 申请弹窗包含原因字典、补充说明和证据。
  • 审批通过后由有执行权限的管理员执行。
  • 状态为“已冲正”后不再显示审核或冲正按钮。

8.3 文本管理

  • 已下线内容显示“归档”。
  • 历史版本列表显示“生成回滚草稿”。
  • 回滚前明确提示“不会覆盖当前线上版本”。
  • 已归档内容的编辑和发布按钮禁用。

前端继续使用 Naive UI、BasicTable、BasicForm、BasicModal 和 http.request,状态标签通过后端字典渲染。

9. 权限设计

新增操作权限:

/rechargeOrder/reversalRequest
/rechargeOrder/reversalExecute
/taskSubmission/reversalRequest
/taskSubmission/reversalExecute
/content/archive
/content/rollback

权限规则:

  • 申请权限和执行权限分离。
  • 申请人不得审批或执行自己的高风险申请。
  • 执行接口必须独立校验权限,不能只依赖前端按钮隐藏。
  • 新权限通过 hg_admin_menu 的按钮类型记录配置,并授权给管理角色和业务角色。

10. 台账、幂等与审计

10.1 台账要求

  • 原台账不可更新和删除。
  • 冲正台账金额必须等于原台账金额。
  • 冲正台账方向必须与原台账相反。
  • reversal_of 必须指向原台账 ID。
  • 同一原台账只能存在一条有效冲正台账。
  • 钱包变更后必须满足 after_amount = before_amount ± amount。
  • 可用余额和冻结余额不得小于 0。

10.2 幂等要求

  • 申请接口使用调用方传入的幂等键。
  • 执行接口以审批单 ID 和业务单 ID 共同保证幂等。
  • 重复执行已完成审批时返回清晰业务错误,不重复写钱包和台账。
  • 数据库唯一索引冲突必须转换为用户可理解的业务错误。

10.3 审计要求

业务审计至少记录:

  • 模块和动作。
  • 操作人、目标类型和目标 ID。
  • 原状态和目标状态。
  • 原台账 ID 和冲正台账 ID。
  • 冲正金额、原因和证据摘要。
  • 审批单号和 Trace ID。
  • 变更前后摘要与结构化差异。

11. 异常与并发处理

场景 处理要求
重复点击执行 只允许第一次成功,后续返回“审批已执行”
两个管理员同时执行 通过事务行锁保证只有一个事务成功
审批后业务状态变化 拒绝执行,要求重新发起审批
钱包余额不足以冲正 整体回滚,不产生台账和状态变化
原台账不存在或归属不符 拒绝执行并记录 Warning 日志
审批通过但事务失败 审批保持未执行,可排查后重试
审计写入失败 核心事务整体回滚
通知发送失败 不回滚核心事务,记录 Warning 并通过队列重试

12. 测试方案

12.1 状态机测试

每个状态机都要覆盖:

  • 所有允许转换成功。
  • 所有禁止转换失败。
  • 相同状态重复申请失败。
  • 注销会员恢复失败。
  • 申请后状态变化导致执行失败。
  • 已执行审批重复执行失败。

12.2 资金测试

  • 充值入账冲正后余额正确。
  • 任务佣金冲正后余额正确。
  • 原台账保持不变。
  • 冲正台账方向、金额和 reversal_of 正确。
  • 幂等键重复提交不产生重复台账。
  • 两个并发执行请求只生成一条冲正台账。
  • 余额不足时业务状态、钱包和台账均不变化。
  • 审计日志、审批单和台账可以相互追溯。

12.3 内容测试

  • 只有已下线内容可以归档。
  • 已归档内容不可编辑和发布。
  • 历史版本回滚生成新草稿。
  • 回滚不改变当前发布版本。
  • 新草稿的 base_version_id、版本号和内容正确。

12.4 回归测试

  • 原充值审核、提现审核和任务审核流程不受影响。
  • 原会员禁用和恢复流程仍可使用。
  • 原站点配置版本与回滚流程不受影响。
  • 字典加载、权限菜单和审计列表正常。

13. 验收标准

满足以下条件后视为第 12 章补强完成:

  1. 注销会员无法通过任何后台状态接口恢复。
  2. 充值成功后只能通过审批冲正,不允许直接修改余额或原台账。
  3. 已结算任务佣金可以通过审批冲正,且不可重复冲正。
  4. 每次冲正都能从业务单追溯到原台账、冲正台账、审批单和审计记录。
  5. 已下线内容可以归档,历史版本可以生成新草稿且不覆盖线上数据。
  6. 所有新增接口具有后端权限校验和明确的中文业务错误。
  7. 非法转换、重复执行、并发执行和余额不足测试全部通过。
  8. 后端编译、前端类型检查和现有模块回归测试通过。

14. 推荐实施顺序

  1. 新增数据库迁移并用 GoFrame CLI 重新生成模型和 DAO。
  2. 补充状态字典和统一转换判断。
  3. 修复会员状态申请与执行校验。
  4. 实现充值冲正申请与执行。
  5. 实现任务佣金冲正申请与执行。
  6. 实现内容归档和历史版本回滚草稿。
  7. 增加按钮权限和角色授权。
  8. 增加前端操作按钮、弹窗和状态展示。
  9. 完成状态机、资金并发和回归测试。