# 004 动态主数据技术决策

对应 Issue：B-41（`docs/issues/004-p1-dynamic-master-data.md`）。

## 账户状态机与生效区间

- `bank_accounts.status`：`pending → active → disabled`，审核退回为
  `returned`；退回后同公司重新提交复用同一行并回到 `pending`，不产生
  第二条记录。停用这个动作只对 `active` 开放，退回只对 `pending` 开放，
  越界操作返回 409。
- 生效区间 `effective_from` / `effective_to`（含当天）：公司提交时填写
  期望启用日，管理员审核通过时确认；停用时写入 `effective_to`（默认当天）。
- 两个判定函数分开：
  - `is_usable`（上传选择、当期操作）：仅 `active` 且在生效区间内。
  - `is_identifiable`（历史流水所有权识别）：`active` 或 `disabled` 且在
    生效区间内——停用只终止未来参与，不改写历史归属。

## 账号规范化与唯一性

- 账号入库前统一规范化：去空格/短横线、全角数字转半角，只存 6–32 位
  数字的规范形式；唯一性由数据库 `UNIQUE(account_number)` 保证，并发
  提交必然只有一个成功（有并发测试）。
- 公司端任何响应只含脱敏形式 `****尾四位`；完整账号只在总账管理端
  （授权审计视图）返回。审计日志 detail 也只写脱敏账号。

## 别名匹配优先级

- `account_aliases` 支持户名（`name`）与账号（`account`）两类别名，
  各自带生效区间和 `priority` 列。
- 匹配优先级确定：精确账号（0）< 账号别名（1000+priority）<
  户名别名（2000+priority）；同层内按 `priority` 再按账户 id 排序。
  别名只在自身生效区间且账户 `is_identifiable` 时参与匹配。

## 审计轨迹

- 所有主数据变更写入 `master_data_changes`：实体、动作、前值 JSON、
  后值 JSON、原因、操作人、时间。公司创建、账户提交/退回重提/
  审核/停用、别名创建全部覆盖，管理员可按实体查询。

## 前端边界

- 公司下拉（往来查询、审核筛选、流水筛选、提醒、期初）和公司表格全部
  由 `/api/admin/companies` 渲染，新增公司无需改代码即可被检索。
- 公司端账户目录、上传账户选择、手工记录资金来源只来自
  `/api/company/accounts`，且只有 `usable` 账户进入上传选择。
- 手工记录仍是浏览器 localStorage 演示数据（属 Issue 007/012 范围），
  账户登记已完全切换到服务端。
