去年 4 月底,地陪服务平台脚手架期启动一周后,需要在两天内产出 27 个业务模块的设计文档。10 项细分服务、多角色分账模型(平台/商家/代理/分销/地陪)、完整后台,每个模块一个独立设计文档——如果逐个手写,进度撑不住。
这篇记录批量产出这类文档的方法论。核心思路是先定统一的文档模板与字段规范,再定跨模块共享的核心模型,最后按模板填充各模块。要点在于:共享模型必须先定死,因为改一次就要改所有相关文档。
先定文档模板骨架
27 个模块的设计文档虽然内容不同,但结构可以统一。我设定的骨架如下:
- 1. Goal:这个模块解决什么
- 2. Recommended Approach:为什么采用这个方案而不是其他(这很关键,很多团队会漏掉)
- 3. Scope:包含什么、不包含什么(防止后续蠕虫式功能膨胀)
- 4. Information Architecture:如果涉及后台,菜单信息架构怎么组织
- 5. Page Design:UI 分区、筛选项、表格列、统计卡片等
- 6. Data Model:数据库表结构、字段、枚举、索引
- 7. API Design:接口地址、参数、响应格式
- 8. Testing Strategy:前后端各自怎么测
这个骨架对所有 27 个模块都适用。没有骨架时,每个模块文档的结构都不一样,审查和理解成本翻倍;有骨架后,新增模块只需要按位置填空。
定死五个跨模块共享的核心模型
更关键的是定死核心数据模型,特别是账本和订单。一旦这些模型后改,27 个文档都要返工。
1. 订单模型
订单需要覆盖所有场景:用户下单、服务提供者接单、支付、取消、改期、超时计费、位置追踪、评价。这 10 项服务都公用一套订单模型,字段只增不减。
核心字段:orderId、userId、escortId(服务提供者)、serviceId、status(状态机)、amount、createdAt、startTime、endTime、cancelledAt 等。
最容易踩的坑:不要为了"简洁"就给每种服务拆一个订单表。一开始拆,后面统计、对账、消息推送都是灾难。
2. 账本模型(最复杂)
这是文章的重点。27 个模块里,账本类模块有 5 个:
- Platform Ledger(平台收支)
- Merchant Ledger(商家明细)
- Agency Ledger(代理商明细)
- Distributor Ledger(分销商明细)
- Escort Ledger(地陪/达人明细)
再加上 3 个交易流水类:
- Recharge Records(充值)
- Refund Records(退款)
- Withdrawal Accounts(提现账户)
这 8 个都遵循同一套流水结构,共通字段有:
{
ledgerRecordId, // 唯一标识
role, // 账本角色 (user/escort/merchant/distributor)
direction, // 方向 (income/expense)
amount, // 金额
remark, // 备注
orderNo, // 源单号(可追溯)
recordedAt, // 记账时间
createdAt,
updatedAt
}
角色特定字段会有差异(比如 Merchant Ledger 多了 paymentMethod、balanceAfter、sceneType 等),但这套基础字段集合对所有 8 个账本都适用。
最关键的决策:每个角色独立一张表,不要合并。 很多人会想"都是账本,能不能用一个大表加 role 字段区分?"不行。原因有三:
- 查询效率:同一个角色的流水集中在一张表,索引策略清晰;混在一张表里,where 条件变复杂
- 扩展性:未来某个角色的流水逻辑变复杂,新加字段只影响这张表
- 权限隔离:后台按角色展示不同的账本页面,数据表和权限边界对齐,防止权限越界的 Bug
3. 单笔订单如何拆成多条流水(多角色分账)
这是设计中最容易出错的地方。用户下单一笔,金额是 100 元。这 100 元要在 5 个账本里都体现:
- Platform Ledger:+100(收入)
- Merchant Ledger:-70(服务提供者从平台结账)
- Escort Ledger:+60(地陪佣金)
- Agency Ledger:+5(所属代理分成)
- Distributor Ledger:+5(所属分销分成)
一笔订单,拆成 5 条流水,分别计入 5 个账本。这不能在前端聚合,必须在订单产生时就由 order-service 通过 MQ 广播给各个 ledger 消费者异步处理。
4. 幂等键设计
MQ 消息可能被重复消费。幂等键设计决定了"重复消费时是否产生重复的流水记录"。
常见的幂等键结构:{orderId}-{ledgerRecordType}-{direction}-{roleId}
比如:"order-12345-commission-income-escort-789"
这个 key 存到 Redis,TTL 设为 24 小时。消费者拿到消息后,先检查这个 key 是否存在。如果存在,说明之前处理过,直接返回;如果不存在,才生成一条新的 ledger_record。
不这样做的后果:用户投诉"我下单了,怎么平台账户进账两次",查日志发现订单服务发了两条相同的 MQ 消息,或者消费者重启期间消息重新投递了。
5. 对账口径
"对账"是财务部门关心的。账本数据必须能对应到源单据。每条流水都要有 orderNo 字段(或其他源单号),这样财务人员可以随时拿着订单号查到对应的账本记录。
后台的"平台收支"页面,最关键的操作是能按订单号检索流水。如果没有这个,财务无法核账。
按模板批量填充
定好模板和核心模型后,填充就快了。以 Merchant Ledger 为例,从模板复制到文档,然后:
- Goal 部分:改成"查看商家账户余额流水"
- Recommended Approach:解释为什么独立一张表而不是从商家余额字段反推
- Scope:列出这个模块包括查询、删除,不包括批量删除、导出
- Information Architecture:菜单路由
/finance/merchant-ledger - Page Design:筛选项(商家、支付方式、时间)、表格列(商户名、手机号、金额、余额、时间等)
- Data Model:表名
merchant_ledger_records,字段集合 - API Design:
GET /admin/finance/merchant-ledger、DELETE /admin/finance/merchant-ledger/:id - Testing Strategy:参数转发、筛选正确、金额计算正确
从模板到完成,一个模块 20-30 分钟。27 个模块,按这个节奏,两天内完全可以产出。
避免的陷阱
陷阱 1:过度个性化。如果每个模块都要求"独特的设计",模板就没用了。要求统一:所有后台列表页遵循同一个 UI 结构,只改列和筛选项。
陷阱 2:字段命名不一致。A 账本叫 ledgerRecordId,B 账本叫 recordId,C 账本叫 id。查询时容易出错。从一开始就定好规范,全部遵循。
陷阱 3:索引规划滞后。数据模型写完了,才想起来没有索引。到了后期"查询慢"才加索引,可能已经影响了代码逻辑。索引要在设计文档里就列出来。
陷阱 4:忽略 Not Included。设计文档里不写"不包含什么",后续就有人问"为什么没有批量删除""为什么没有导出"。明确列出 Scope 的边界,能省很多周期。
总结
批量产出设计文档的要诀是复用而非重复编写。文档模板保证了结构统一,核心数据模型保证了逻辑一致,两者结合,27 个模块的设计工作量从"无法估算"变成"可预测"。
最后的提醒:这套方法对脚手架期这样"框架先行、业务后补"的场景特别有效。但如果业务需求本身就不清楚,文档多了也没用——反而会变成"写了一堆高保真但实际没用的文档"。务必确保需求先冻结,再启动文档产出。
■