11 KiB
企业银行账户与付款制单设计
1. 边界与交付顺序
本任务在 OA 内建立付款准备能力,不连接银行 API、不提交付款、不代替网银复核。交付按同一任务内的三个层次顺序完成:
- 银行机构与公司付款账户主数据。
- 员工收款信息默认脱敏、规范化银行标识和付款批次冻结快照。
- 已复核批次的 BIFF8 制单导出、使用状态与审计。
三层共享同一迁移和权限设计,拆成独立任务会使中间版本产生无法制单或无法建批次的短暂合同,因此本轮保留为一个复杂 Trellis task,按提交批次分层验证。
2. 数据模型
2.1 BankInstitution
新增银行机构主数据:
idUUIDcode唯一、不可变的规范化代码;首版至少存在CCBname展示名称enabled、archived_atcreated_at/by、updated_at/by
银行机构由 payment.account.manage 管理。员工收款资料与公司付款账户都引用同一主数据,不把银行名称字符串当身份键。已被引用的代码不能修改或物理删除;停用只阻止新绑定。
2.2 PaymentAccount
新增独立付款账户表并关联 LegalEntity:
id、legal_entity_id、bank_institution_idaccount_nameaccount_number_encryptedenabled、archived_atcreated_at/by、updated_at/by
PostgreSQL 使用部分唯一索引保证同一法人主体最多一条 enabled=true AND archived_at IS NULL 的账户;服务端先做自然中文校验,数据库索引负责并发兜底。
已启用账户不原地覆盖账号。变更操作在同一事务内归档旧记录并创建新记录,保留历史身份;停用只改变旧记录状态。完整账号沿用现有字段加密服务,普通 DTO只计算脱敏尾号。
2.3 Employee 与 PaymentBatch
Employee增加可空bank_institution_id。存量数据不通过自由文本自动猜测;由 HR/admin 在 UI 中确认。PaymentBatch增加可空legal_entity_id、payment_account_id、内部付款账户快照、制单次数、最近制单时间和操作者。- 报销付款项的
snapshot_json.payee增加内部密文和银行机构代码;既有姓名、银行显示名和脱敏尾号继续保留。
付款账户快照仅存内部使用字段:账户记录 ID、法人主体 ID、账户名称、银行机构代码/名称、原始密文和脱敏尾号。API 映射层只返回名称、银行名称和脱敏尾号。
迁移全部新增为可空,禁止回填历史完整账号。历史批次因此保持可查看、可人工付款,但制单明确失败关闭。
3. 权限与威胁模型
新增权限:
| 权限 | 角色 | 用途 |
|---|---|---|
payment.account.manage |
finance_mgr、admin/* |
银行机构与公司付款账户新增、替换、停用 |
payment.account.read |
finance、finance_mgr、admin/* |
公司账户脱敏列表/详情 |
payment.account.read_full |
finance、finance_mgr、admin/* |
填理由查看公司完整账号 |
payment.order.export |
明确具有 finance 或 finance_mgr 业务角色者 |
已复核批次制单;单有 admin/* 不作为财务业务角色代办 |
employee.bank.manage 不授予财务或 system_admin。员工完整收款账号查看继续使用独立读取权限,但现有 GET 默认改为脱敏;完整值通过带原因的 POST 动作获取并先写审计。
公司付款账户维护防的是误配置、错误来源账户和缺少及时知情,不新增双人审批。真实受益人欺诈边界由“HR/admin 维护员工收款资料”和“finance/finance_mgr 制单”分离控制。
4. 主数据 API 与页面
4.1 公司付款账户
新增专用路由而不复用 Feature Flag:
- 脱敏列出银行机构与付款账户。
- 创建首个账户、替换当前账户、停用当前账户。
- POST 完整账号查看动作,body 必须有
reason。
管理入口放在 PC 管理 -> 基础资料/财务配置 -> 企业银行账户。默认卡片只显示法人主体、账户名称、银行名称、脱敏尾号和状态;完整账号查看、替换、停用均使用明确动作与后果确认。移动端不提供配置入口。
账户新增、替换或停用在事务内写脱敏审计,并为所有 active admin 与 CEO 创建 OA 通知;通知不带完整账号、密文或本地路径,也不要求 CEO 具备账户主数据页面权限。
4.2 员工收款信息
人员档案增加收款信息区:
- HR/admin 可选银行机构并录入账号。
- finance/finance_mgr 只能看银行名称和脱敏尾号。
- 员工本人在个人资料看到自己的银行名称和脱敏尾号。
- 授权角色查看完整账号时填写原因;服务端先审计后返回,前端不会把完整值缓存到长期状态或普通查询缓存。
5. 批次创建与冻结快照
对 entityType=reimbursement 且银行渠道的新批次:
- 在同一事务锁定所选报销。
- 解析每张报销所有人的
legal_entity_id,要求非空且全批次一致。 - 查询该法人主体唯一启用的建行付款账户。
- 要求每位收款人有
CCB银行机构与可用账号密文。 - 直接复制员工现有密文,不解密后重新加密;只为脱敏尾号做一次受控解密。
- 冻结付款账户和每一项收款信息后创建批次。
缺法人主体、混合法人主体、缺付款账户、非建行、缺账号或解密失败时,创建失败并一次性返回自然中文处理建议。其他渠道及非报销实体沿用原流程,但不具备银行制单能力。
API 映射函数必须显式挑选安全字段。测试扫描所有普通批次/API/AP 响应,断言不存在 v1: 密文、完整账号或原始 snapshot_json。
6. 制单服务与接口
6.1 资格校验
POST 制单接口接收 { reason },仅允许明确 finance/finance_mgr 业务角色、payment.order.export 权限和 reviewed 状态。它必须拒绝:
- 历史/不完整快照
- 非报销或非银行渠道
- 公司或任一员工不是
CCB - 缺完整密文、账号解密失败
- 缺金额、业务编号或付款账户
- 其他四种批次状态
校验聚合所有业务阻塞项后统一返回;权限失败仍立即拒绝,不泄露批次内容。
6.2 确定性行与字段
项目按 entity_code ASC, id ASC 显式排序。唯一 ID 仅作同业务编号异常重复时的兜底,不进入银行文件。
- 序号:从 1 开始的字符串。
- 付款方账号/名称:付款账户快照。
- 行别代码:固定字符串
01。 - 收款方账号/名称、开户行:收款快照。
- 联行号:空字符串。
- 客户流水号:单一
buildCustomerReference(batchNo, rowIndex)函数生成,写入前校验长度、字符集和批内唯一。 - 金额:初版数值单元格 + 两位小数格式。
- 用途:
报销款。 - 备注:报销单编号。
- 短信标志:字符串
0;手机号与附加信息留空。
禁止部分成功:任意一项失败时不生成文件、不更新制单状态、不写成功导出审计。
6.3 BIFF8 与下载
- 使用 SheetJS 官方精确
0.20.3tarball并锁定 package-lock 完整性。 - 运行时代码只调用生成 API,不解析上传文件或用户提供的工作簿。
- 显式构建七个字符串字段的
{ t: "s", v: value }单元格;金额使用数值类型和两位小数格式。 - 工作簿按顺序创建
Sheet1、Sheet2、Sheet3,Sheet1 固定 15 列表头。 - 内存生成
bookType: "biff8"buffer,先完成审计和制单计数事务,再设置application/vnd.ms-excel、安全附件名、nosniff、private, no-store等响应头并发送。 - 不写对象存储或服务器持久目录。网络中断后的已记录导出按保守口径视为已生成,用户可填写新原因重导。
7. 页面动作与进度
付款批次 DTO 返回脱敏付款账户摘要、paymentOrderExportCount、最近导出时间/人和 canExportPaymentOrder/阻塞原因。
- 新快照 + reviewed + 未导出:顶部主动作
下载银行制单;回单区仍可直接上传,人工路径不被阻断。 - 已导出且无回单:主动作
上传银行回单,重新下载制单放入更多。 - 重导确认固定展示:
重新下载不会作废之前的文件。如果上一份已在网银导入,请勿重复提交。 - 已有回单:主动作
登记付款完成。 - 旧批次或资料不完整:不显示可执行制单动作,展示具体原因;原人工付款动作保持。
- 详情显示
未生成制单或已生成 N 次;处理进度按是否导出显示已批准的两种文案。
整体详情页结构统一仍由后续独立任务负责,本任务只在当前操作组和处理进度数据中接入制单状态,不顺带重构页面骨架。
8. 审计、通知与隐私
审计动作至少覆盖:付款账户创建/替换/停用、公司完整账号查看、员工完整账号查看、银行制单首次导出/重导和失败阻断摘要。审计 extra 只存业务 ID、法人主体、批次号、笔数、合计金额、原因、导出次数和结果代码;不存完整账号、密文、工作簿 buffer 或原始快照。
普通日志只记录固定错误码、批次 ID/业务号和数量。依赖或解密错误返回自然中文业务错误,禁止裸 Error 使页面白屏,也禁止把底层加密异常文本返回客户端。
9. 验证与依赖门禁
- Node 单元/路由测试覆盖权限矩阵、单一启用账户并发约束、通知、审计、批次快照、历史拒绝、CCB-only、确定性排序、重导和隐私负向断言。
- 使用合成数据生成
.xls,由 CI 明确安装的xlrd==2.0.2独立读回三个工作表、15 列、七个字符串字段、金额、中文和重复导出一致性。 - 用合成非建行项证明整批失败且没有文件、制单计数或成功审计。
- Excel/WPS 人工打开验证中文、长字符串和金额;企业网银只有存在明确“仅校验”步骤时才使用虚构建行数据做最终确认。
- 安装依赖后运行 npm audit/许可证与 lockfile 审查;如官方精确 tarball不可重复获取或引入不可接受告警,暂停而非换用 registry 旧版。
10. 迁移、上线与回滚
- 迁移只新增表、可空关系、可空快照和制单统计字段,不修改或回填存量账号/批次。
- 部署后先由 admin 建立 CCB 主数据与公司付款账户,再由 HR/admin 为员工确认 CCB 机构;这两类生产写入必须另行批准,完整账号不经过聊天或仓库。
- 配置完成前暂停创建新的银行渠道付款批次,避免产生无法制单的新历史快照;已有杨俊批次继续人工付款。
- 公网
/ready、角色/隐私只读检查和一条合成/受控流程通过后才通知财务使用制单。 - 代码回滚保留新增表和可空字段,不删除已产生的审计、通知或制单统计;旧应用忽略新字段并继续人工付款。