16 KiB
16 KiB
客服备用金台账实施方案(2026-10-18)
状态:已实施 P0(迁移 100/101,前端/后端已上线到代码库;未部署生产)。 最后校准:2026-10-18 代码基线:
main @ 7542d547背景:两位主管向客服发放备用金,客服用于充值、采购、赔偿等;目前全部走人工,无法监控每笔资金用途和每个客服余额。 结论:这不是报销系统,而是客服备用金(周转金)台账。P0 已完成“账户 + 流水 + 支出 + 对账”,不做复杂审批和总账。
〇、实际落地结果
| 项 | 实际实现 |
|---|---|
| 数据库 | 100_fund_accounts.sql:fund_accounts / fund_entries / manual_expenses / fund_reconciliations |
| 显示名 | 101_admin_display_name.sql:admin_users.display_name,备用金 owner_name 由触发器同步 |
| 后端 | admin-fund-service.ts、routes/admin/fund.ts;金额全部用分,写操作使用事务与行锁 |
| 前端 | AdminFundPage.tsx、FundManagementView.tsx、FundSupportView.tsx;管理端与客服端分流 |
| 权限 | fund.view / fund.issue / fund.expense / fund.reconcile / fund.adjust / fund.export |
| 审计 | 所有写接口接入 createJsonHandler({ audit }) |
| 测试 | 后端 738 通过 / 3 跳过;前端 143 通过;两端 typecheck、lint、build 通过 |
与本文原方案的差异:
- 实际表名
manual_expenses是客服备用金支出明细,不是后来财务方案里的公司人工支出表manual_expense_records。 - P0 未实现异常队列、批量发放/对账、导出、低余额提醒、大额支出主管确认;这些仍在 P1。
- 客服账户姓名使用
owner_name快照 +admin_users.display_name触发器同步;历史审计 payload 不重写。 - 支出凭证目前只校验数组非空,未校验对象存储 key 是否真实存在。
后续衔接:
- 财务侧公司人工支出登记应新建
manual_expense_records,通过fund_expense_id关联本表的manual_expenses.id,不要复用或改名。 - 备用金支出可作为“钱从客服备用金出”的来源证据;公司直付、垫付报销仍走公司人工支出登记。
一、目标与非目标
1.1 目标
系统必须能回答四个问题:
- 公司现在有多少钱在主管和客服手里?
- 每个客服当前余额是多少?
- 每一笔钱花到哪、为什么花、有没有凭证?
- 系统余额和客服实际余额对不对得上?
1.2 非目标(P0 不做)
- 不做垫付/直付的复杂资金性质区分;
- 不做多级审批流;
- 不做成本中心、订单毛利、复式总账;
- 不做平台账户独立钱包(当前口径:充值后立即用于订单/消耗,直接记支出);
- 不做多币种、发票、税务。
二、业务模型
flowchart LR
C["公司资金"] -->|拨付| S["主管资金池<br/>2 位主管"]
S -->|发放| A["客服备用金账户<br/>每人一个余额"]
A -->|支出| P["平台 / 供应商 / 客户"]
A -->|缴回| S
P -->|付款截图| E["支出记录"]
2.1 五类流水,缺一不可
| 流水类型 | 钱的方向 | 对客服余额 | 是否算支出 | 关键字段 |
|---|---|---|---|---|
| 期初 | 上线初始化 | 增加 | 否 | 金额、主管确认 |
| 发放 | 主管 → 客服 | 增加 | 否 | 金额、主管、客服、时间、凭证 |
| 支出 | 客服 → 平台/供应商/客户 | 减少 | 是 | 金额、用途、收款方、平台账号、订单号(可选)、付款截图 |
| 缴回 | 客服 → 主管 | 减少 | 否 | 金额、主管、客服、时间、凭证 |
| 调整 | 主管/财务 → 客服 | 增加或减少 | 视原因 | 差异金额、原因、审批人、凭证 |
只有“支出”没有“发放/缴回/期初”,余额永远算不出来。
2.2 余额公式
客服余额 = 期初 + 累计发放 + 累计调整 - 累计支出 - 累计缴回
主管池余额 = 期初 + 公司拨付 + 客服缴回 - 发放给客服 - 调整
余额只能由流水产生,不允许直接手工修改。
三、账户设计
3.1 主管资金池
- 两位主管各一个账户;
- 公司给主管拨付、主管发给客服、客服缴回,都记入主管池;
- 每个客服指定一个责任主管;
- 发放可以两位主管都操作,但每笔必须记录发放人;
- 对账由责任主管确认,避免“两个人都以为对方对过了”。
3.2 客服备用金账户
- 每个客服一个账户;
- 客服只能看到自己的余额和流水;
- 主管能看到自己名下客服的余额和流水;
- 财务/管理员能看到全部;
- 余额不足时不能继续支出,先由主管补拨。
3.3 平台账户
当前口径为 A:充值后立即用于订单/消耗,因此不单独建平台钱包。支出记录中保留“平台名称 + 平台账号”,用于追溯钱充到了哪里。
如果以后出现“充值后平台还有余额、以后继续用”的情况,再增加平台账户层,不混入客服余额。
四、数据模型
P0 四张表即可。
4.1 fund_accounts 资金账户
| 字段 | 说明 |
|---|---|
| id | 主键 |
| account_no | 账户编号,唯一 |
| account_type | supervisor / agent |
| owner_admin_user_id | 主管或客服的后台用户 ID |
| owner_name | 姓名快照 |
| responsible_supervisor_id | 客服的责任主管;主管账户为空 |
| balance_fen | 当前余额,分 |
| status | active / frozen / closed |
| created_at / updated_at | 时间 |
约束:
- 同一用户同一类型只能有一个账户;
- 余额不得为负;
- 冻结账户不能发放、不能支出,但允许缴回和调整。
4.2 fund_entries 资金流水
| 字段 | 说明 |
|---|---|
| id | 主键 |
| entry_no | 流水号,唯一 |
| account_id | 所属账户 |
| entry_type | opening / funding / issue / expense / return / adjustment |
| amount_fen | 有符号金额,正数流入、负数流出 |
| balance_after_fen | 记账后余额快照 |
| transfer_no | 转账批次号;发放/缴回的两条流水共用 |
| counterparty_account_id | 对方账户;支出时为空 |
| expense_id | 支出明细 ID;仅 expense 使用 |
| reconciliation_id | 对账调整关联 |
| occurred_at | 资金实际发生时间 |
| created_by_admin_user_id | 操作人 |
| evidence_json | 凭证文件 key、备注 |
| note | 说明 |
| created_at | 创建时间 |
约束:
amount_fen <> 0;- 余额只能通过流水变化;
- 发放、缴回必须同时写两条流水:主管负数、客服正数,或反过来;
- 支出必须同时写一条客服负数流水和一条
manual_expenses; - 已记账流水不可修改、不可删除;更正走调整或反向流水。
4.3 manual_expenses 支出明细
| 字段 | 说明 |
|---|---|
| id | 主键 |
| expense_no | 支出编号,唯一 |
| account_id | 客服备用金账户 |
| amount_fen | 支出金额,分 |
| category | platform_recharge / purchase / compensation / freight / other |
| payee_type | platform / supplier / customer / other |
| payee_name | 收款方名称 |
| payee_account | 收款账号 |
| platform_name | 充值平台名称 |
| platform_account | 平台账号 |
| order_id | 关联订单,可空 |
| order_item_id | 关联订单项,可空 |
| paid_at | 付款时间 |
| evidence_json | 付款截图、凭证 |
| note | 用途/原因 |
| fund_entry_id | 对应资金流水,唯一 |
| created_by_admin_user_id | 登记人 |
| created_at / updated_at | 时间 |
约束:
- 金额 > 0;
- 用途、收款方、付款时间、凭证必填;
- 订单号可选;赔偿类建议关联订单;
- 同一客服 + 同金额 + 同收款方 24 小时内重复,提示疑似重复;
- 付款时间不能晚于当前时间;超过 24 小时补记标记“迟登记”。
4.4 fund_reconciliations 对账
| 字段 | 说明 |
|---|---|
| id | 主键 |
| account_id | 客服或主管账户 |
| period_start / period_end | 对账区间 |
| system_balance_fen | 系统余额 |
| actual_balance_fen | 实际余额 |
| difference_fen | 差异 |
| status | pending / confirmed / discrepancy |
| reason | 差异原因 |
| evidence_json | 盘点/截图 |
| confirmed_by_admin_user_id | 确认人 |
| confirmed_at | 确认时间 |
| created_at | 创建时间 |
规则:
- 差异为 0 才能直接确认;
- 差异不为 0 必须填写原因,并走调整流水;
- 调整必须由主管/财务/管理员操作,不能由客服自行改余额。
五、业务流程
5.1 上线初始化
- 建立两位主管账户;
- 建立每个客服账户,指定责任主管;
- 主管确认每个客服的期初余额;
- 期初余额写
opening流水,备注“上线期初余额”; - 期初确认后,所有余额变化必须走流水。
5.2 公司 → 主管
管理员记录“拨付”,主管池增加。若公司层暂不管理,至少给主管池录期初余额。
5.3 主管 → 客服
主管选择客服、填写金额、付款方式、时间、凭证,提交后:
- 主管池减少;
- 客服余额增加;
- 生成两条
issue流水,共用一个transfer_no。
5.4 客服支出
客服填写金额、用途、收款方/平台、平台账号、订单号(可选)、付款时间、截图、备注,提交后:
- 客服余额减少;
- 生成一条
expense流水; - 生成一条
manual_expenses明细。
5.5 客服缴回
客服或主管发起缴回,主管确认后:
- 客服余额减少;
- 主管池增加;
- 生成两条
return流水。
5.6 对账
- 系统显示当前余额;
- 主管/客服盘点实际现金、微信、支付宝等余额;
- 输入实际余额,系统计算差异;
- 差异为 0 直接确认;不为 0 填写原因并走调整;
- 调整后重新对平。
建议频率:每笔流水当天记,每天生成余额快照,每周主管与客服对一次账。
六、权限设计
新增权限点:
fund.view:查看账户和流水;fund.issue:发放/缴回;fund.expense:登记支出;fund.reconcile:发起/确认对账;fund.adjust:调整余额;fund.export:导出。
角色建议:
| 角色 | 权限 |
|---|---|
| 管理员 | 全部 |
| 财务 | 查看全部、对账、调整、导出 |
主管(operator 或新增 fund_manager) |
查看名下客服、发放、缴回、对账 |
| 客服(support) | 登记自己的支出、查看自己的余额和流水 |
服务层还要做归属校验:
- 客服只能操作自己的账户;
- 主管只能操作自己名下客服;
- 发放人不能给自己发放;
- 调整必须由主管/财务/管理员操作;
- 所有写操作写审计日志。
七、页面设计
7.1 管理端“备用金”页
建议五个页签:
- 总览:公司总备用金、主管池合计、客服余额合计、本月支出、待对账人数、异常笔数。
- 客服余额:客服、责任主管、当前余额、本月发放、本月支出、本月缴回、最后交易、最后对账、状态。
- 主管资金池:主管、池余额、本月拨入、本月发放、本月收回、名下客服数、差异。
- 支出明细:时间、客服、金额、用途、收款方/平台、订单号、凭证、状态;支持筛选。
- 对账与异常:负余额、无凭证、长期未动、疑似重复、对账差异、超限额。
7.2 客服端“我的备用金”
- 当前余额;
- 发放/支出/缴回流水;
- “登记支出”按钮;
- “申请补拨”按钮;
- “申请缴回”按钮;
- 对账状态。
7.3 订单详情(可选)
订单详情增加“本单相关支出”小卡片,展示已关联的支出合计和明细。订单号不是必填,所以这个卡片只对已关联订单的支出生效。
八、接口设计
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /admin/fund/overview |
总览 |
| GET | /admin/fund/accounts |
账户列表 |
| GET | /admin/fund/accounts/:id |
账户详情 + 流水 |
| POST | /admin/fund/accounts/:id/funding |
公司 → 主管拨付 |
| POST | /admin/fund/accounts/:id/issue |
主管 → 客服发放 |
| POST | /admin/fund/accounts/:id/return |
客服 → 主管缴回 |
| POST | /admin/fund/accounts/:id/expenses |
登记支出 |
| GET | /admin/fund/expenses |
支出明细 |
| GET | /admin/fund/expenses/:id |
支出详情 |
| POST | /admin/fund/reconciliations |
发起对账 |
| POST | /admin/fund/reconciliations/:id/confirm |
确认对账 |
| POST | /admin/fund/reconciliations/:id/adjust |
差异调整 |
实现要求:
- 所有金额用“分”存储,前端按元展示;
- 发放、缴回、支出必须在数据库事务中完成;
- 账户行使用
SELECT ... FOR UPDATE,避免并发导致余额错乱; - 所有写接口使用现有
createJsonHandler({ audit })写审计日志; - 所有写接口做权限点和归属校验。
九、校验与异常
| 规则 | 处理 |
|---|---|
| 金额 <= 0 | 拒绝 |
| 余额不足 | 拒绝,提示先补拨 |
| 账户冻结 | 禁止发放/支出,允许缴回/调整 |
| 支出无凭证 | 拒绝 |
| 发放/缴回无凭证 | 允许备注+交接说明,但标记待补 |
| 收款方为空 | 拒绝 |
| 付款时间晚于当前 | 拒绝 |
| 付款时间早于当天 | 允许,标记迟登记 |
| 同客服+同金额+同收款方 24h 内重复 | 提示,需备注原因 |
| 对账差异未填原因 | 拒绝确认 |
| 调整无审批人 | 拒绝 |
异常队列:
- 负余额;
- 无凭证;
- 超过 7 天未对账;
- 余额长期不动;
- 单笔/单日支出异常;
- 同一凭证被多笔复用;
- 对账差异。
十、分期落地
P0(先能记、能看、能对)
- 四张表 + 索引;
- 主管池、客服账户;
- 期初、发放、支出、缴回、调整五类流水;
- 支出明细 + 凭证上传;
- 总览、客服余额、主管池、支出明细、对账页面;
- 权限点 + 审计;
- 上线期初余额;
- 每周对账。
P1(更好用)
- 异常队列和提醒;
- 余额上限、低余额提醒;
- 批量发放/批量对账;
- 导出;
- 大额支出主管确认;
- 移动端快速登记。
P2(算得更清)
- 订单关联支出汇总;
- 支出分类与成本中心打通;
- 单均人工成本、店铺/客服维度报表。
P3(财务总账)
- 复式记账;
- 与订单、钱包、提现、人工支出统一对账;
- 期间关账。
十一、上线步骤
- 业务确认:两位主管、客服名单、责任主管、支出分类、期初余额截止时间。
- 开发:迁移、后端台账服务、接口、前端页面、权限。
- 初始化:建立账户,录入期初余额,主管逐人确认。
- 试点:选 2 位主管 + 2~3 位客服,跑 1 周。
- 校准:根据试点调整字段、分类、对账频率。
- 全量:全部客服上线,关闭线下 Excel/微信群记账。
- 运营:每周对账,每月复盘余额和支出异常。
十二、测试与验收
12.1 必测
- 发放:主管池减少、客服余额增加,两条流水金额相反;
- 支出:客服余额减少,支出明细与流水一一对应;
- 缴回:客服余额减少、主管池增加;
- 余额不足:拒绝支出;
- 并发发放/支出:余额不超扣、不出现负数;
- 权限:客服不能看别人,主管不能看非名下客服,客服不能调整;
- 对账差异:必须填原因并生成调整流水;
- 审计:所有写操作有日志。
12.2 验收标准
- 每个客服的当前余额可查;
- 每笔支出可追溯到人、金额、用途、收款方、时间、凭证;
- 主管池余额与发放/缴回流水对得上;
- 系统余额与客服实际余额的差异有记录、有原因、有审批;
- 不存在无流水导致的余额变化;
- 不存在负余额(除非明确开启允许负余额)。
十三、待确认
- 两位主管的钱是公司资金,还是主管个人先垫?P0 先按公司资金池记,个人垫资在备注中说明。
- 客服账号是否都是现有
admin_users的support角色?如果是,直接绑定owner_admin_user_id。 - 客服余额是“一个总余额”,还是需要分微信/支付宝/现金多个钱包?P0 建议一个总余额 + 流水记付款方式。
- 期初余额的截止时间和确认人。
- 支出分类是否就用:平台充值、采购/拿货、客户赔偿、运费、其他。
- 对账频率:每周一次是否足够,是否需要每日快照。
- 大额支出是否需要主管事前确认;阈值多少。