Files
ccdi/docs/superpowers/specs/2026-03-18-lsfx-logid-primary-binding-design.md

8.0 KiB

LSFX Mock LogId 主体账号绑定设计

背景

当前 lsfx-mock-server 的上传文件接口已经会为文件记录生成 enterpriseNameListaccountNoList,但银行流水查询接口 getBSByLogId 仍然在 StatementService 内独立随机或写死 leNameaccountMaskNo。这导致同一个 logId 在不同接口里看到的“本方主体/本方账号”并不统一。

新增需求要求:

  • 每次上传流水文件时,随机生成银行流水的本方主体和本方账号。
  • 一个本方主体对应一个本方账号。
  • 一个 logId 对应一组固定的本方主体和本方账号。
  • 同样的逻辑也覆盖“拉取本行信息”链路产生的 logId

目标

  • 为每个新生成的 logId 绑定一组随机本方主体和本方账号。
  • 上传文件、上传状态、银行流水查询三类接口对同一 logId 返回一致的主体账号信息。
  • 保持现有接口出参结构兼容,不引入前端契约变更。

范围

In Scope

  • lsfx-mock-server/services/file_service.py
  • lsfx-mock-server/services/statement_service.py
  • 上传文件链路的 logId 绑定生成
  • 拉取本行信息链路的 logId 绑定生成
  • 对应测试和实施记录

Out of Scope

  • 前端页面或后端 Java 工程改造
  • 一个 logId 下支持多个本方主体/账号
  • logId 复用同一主体账号映射

设计原则

  • 一个 logId = 一个本方主体 + 一个本方账号
  • 同一个 logId 的所有流水记录都使用同一组 leName/accountMaskNo
  • 外部响应兼容当前数组格式,但内部语义收敛为单值绑定
  • 主体账号由 FileService 统一生成,StatementService 只消费,不再自行决定

现状问题

上传文件链路

FileService.upload_file() 在创建 FileRecord 时会随机生成:

  • account_no_list
  • enterprise_name_list

但它们只服务于上传接口和解析状态接口。

拉取本行信息链路

FileService.fetch_inner_flow() 目前仅返回随机 logId,不会创建 FileRecord,因此后续没有主体账号上下文可以复用。

流水查询链路

StatementService._generate_random_statement() 当前独立写死或随机:

  • accountMaskNo
  • leName

它不知道上传或拉取本行信息时已经生成了什么主体账号。

方案概览

将“本方主体/本方账号”的主数据收敛到 FileService 维护的 FileRecord 中,并让所有基于 logId 的接口围绕同一份绑定工作:

  1. FileService 在生成新 logId 时,创建并保存一组主体账号绑定。
  2. upload_file()fetch_inner_flow() 都走这套绑定生成逻辑。
  3. StatementService 根据 logId 读取绑定值,填入流水的 leName/accountMaskNo
  4. 对外仍然返回:
    • enterpriseNameList: [name]
    • accountNoList: [account]
  5. 删除记录时,若 logId 绑定被删除,后续兼容逻辑生成的新记录也必须保持接口间自洽。

数据模型设计

FileRecord 扩展

建议在现有 FileRecord 上明确单值语义字段:

  • primary_enterprise_name: str
  • primary_account_no: str

同时保留兼容字段:

  • enterprise_name_list
  • account_no_list

约束为:

  • enterprise_name_list == [primary_enterprise_name]
  • account_no_list == [primary_account_no]

这样既不破坏当前响应格式,也让内部逻辑不再把主体和账号当成可变长集合。

主体账号生成策略

新增统一生成方法,例如:

  • _generate_primary_account_binding()

职责:

  • 随机选取一个本方主体名称
  • 随机生成一个本方账号
  • 返回单一绑定对象

建议主体名称从固定 Mock 名称池中随机选择,例如:

  • 测试主体A
  • 测试主体B
  • 测试主体C
  • 兰溪测试主体一部
  • 兰溪测试主体二部

账号生成规则保持简单:

  • 生成合法长度的纯数字字符串
  • 作为单个 logId 的专属账号使用

本次不要求“同一主体跨所有 logId 始终映射同一账号”,只要求:

  • 对单个 logId 而言,主体与账号是一对一且稳定的

接口联动设计

1. 上传文件接口

upload_file() 在创建 FileRecord 时:

  • 生成 logId
  • 生成主体账号绑定
  • 回填:
    • primary_enterprise_name
    • primary_account_no
    • enterprise_name_list
    • account_no_list

上传响应里的:

  • accountsOfLog[*].accountName
  • accountsOfLog[*].accountNo
  • uploadLogList[*].enterpriseNameList
  • uploadLogList[*].accountNoList

都来自这组绑定。

2. 拉取本行信息接口

fetch_inner_flow() 不能再只返回裸 logId,需要同时:

  • 生成新 logId
  • 创建 FileRecord
  • 生成主体账号绑定并落入 self.file_records

这样同一个 logId 后续再查上传状态或银行流水时,能拿到一致的本方主体和账号。

3. 上传状态接口

check_parse_status()get_upload_status() 对已存在的 FileRecord,统一返回其中的绑定数据。

get_upload_status() 走兼容分支按 logId 现场生成记录,生成出的主体账号也必须写成单一绑定,并在该次响应内部保持自洽。

4. 银行流水查询接口

StatementService 不再自己决定:

  • leName
  • accountMaskNo

而是通过 logId 获取对应绑定:

  • leName = primary_enterprise_name
  • accountMaskNo = primary_account_no

同一 logId 下生成的所有流水记录都使用这一组值。

组件边界

FileService

负责:

  • 生成 logId
  • 生成并保存主体账号绑定
  • 对上传/拉取本行信息/上传状态返回统一绑定值

StatementService

负责:

  • 读取 logId 对应主体账号绑定
  • 将绑定值注入每条流水记录
  • 不再自行随机本方主体或本方账号

为避免重复造一份状态,StatementService 需要拿到查询绑定的方法或共享 file_records 访问能力,但不负责写绑定。

错误处理

  • logId 没有对应绑定记录,允许走兼容生成逻辑,但必须在当前响应范围内自洽。
  • 不允许同一次 getBSByLogId 返回中出现多个本方主体或多个本方账号。
  • 不允许上传响应和流水响应对同一 logId 返回不同主体账号。

测试设计

至少补 3 类测试:

1. 上传文件链路测试

验证上传接口返回中:

  • accountsOfLogaccountName/accountNo
  • uploadLogListenterpriseNameList/accountNoList

三者一致,且数组长度为 1

2. 拉取本行信息链路测试

验证 fetch_inner_flow() 返回 logId 后:

  • FileService 内已存在对应 FileRecord
  • 该记录带有主体账号绑定
  • 后续查询上传状态或银行流水时能看到相同绑定

3. 银行流水链路测试

验证同一 logId 下:

  • 第一页和第二页流水里的 leName/accountMaskNo 一致
  • 重复查询结果中的本方主体账号不漂移
  • 流水里的 leName/accountMaskNoFileRecord 中的绑定完全一致

成功标准

  • 上传文件和拉取本行信息产生的新 logId 都会绑定一组随机本方主体与账号。
  • 同一 logId 的上传响应、上传状态、银行流水查询三处返回一致的主体账号。
  • 同一 logId 的所有流水记录只出现一个 leName 和一个 accountMaskNo
  • 对外接口结构不变,前端无需因这次改动调整解析逻辑。

风险与约束

  • 现有 get_upload_status() 存在按 logId 即时生成确定性记录的兼容路径,这部分要特别注意与 file_records 中真实记录的优先级。
  • 若后续一个 logId 需要支持多个本方账号,本次设计需要重做,因为当前明确锁定为“一对一”。
  • StatementService 新增对 FileService 绑定数据的依赖后,需要避免双向循环依赖,优先通过轻量查询函数或共享只读访问注入。

实施输出

实施阶段应至少产出:

  • Mock 服务代码改动
  • 对应 pytest 测试
  • 一份后端实施计划
  • 一份前端实施计划
  • 一份实施记录文档