文件
hl-api-changelog/changelogs-v2/2026-09/07_7232_费用报销-新增接口-管理后台.md
T
2026-09-09 23:21:35 +08:00

18 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 7232 费用报销(申请→审批→出纳付款)+ 出纳队列接通费用线 admin yst 新增接口 deployed verified pending hl-admin v2.1 2026-09-07 后端已合 dev-v3(hl-finance expense 域 + cashier 域扩 EXPENSE 分支)并部署测试服,E2E 16 PASS/0 FAIL。出纳 queue/pay/payments 入参枚举扩容 EXPENSE,省略时行为不变。 2026-09-07 dev-v3

费用报销(申请→审批→出纳付款)+ 出纳队列接通费用线

财务域新增费用报销子域:员工申请报销 → 财务审批(本期手工)→ 出纳付款 → 回写付讫。同时出纳域待付款队列 / 付款登记 / 已付款台账三个既有接口的入参枚举扩容,接通费用报销线(payType/bizType 新增 EXPENSE 值)。

  • expense 新域路径前缀:/admin/finance/expenses(8 个新端点)
  • cashier 既有域路径前缀:/admin/finance/cashier(3 个端点入参枚举扩容,无字段增删)

二、变更接口清单

# 接口 方法 路径 变更类型
1 报销分页 GET /admin/finance/expenses/page 新增
2 申请报销 POST /admin/finance/expenses 新增
3 报销详情 GET /admin/finance/expenses/{id} 新增
4 编辑草稿 PUT /admin/finance/expenses/{id} 新增
5 删除草稿 DELETE /admin/finance/expenses/{id} 新增
6 提交报销 PUT /admin/finance/expenses/{id}/submit 新增
7 批准报销 PUT /admin/finance/expenses/{id}/approve 新增
8 驳回报销 PUT /admin/finance/expenses/{id}/reject 新增
9 出纳待付款队列 GET /admin/finance/cashier/queue 修改(入参 payType 扩容 EXPENSE)
10 出纳付款登记 POST /admin/finance/cashier/pay 修改(入参 bizType 扩容 EXPENSE)
11 出纳已付款台账 GET /admin/finance/cashier/payments/page 修改(入参 bizType 扩容 EXPENSE)

三、接口详情

1. 报销分页 GET /admin/finance/expenses/page

使用场景

管理后台「费用报销」列表页,按状态 / 部门 / 公司主体 / 关键字筛选分页查询。

认证 / 幂等性 / 限流

  • 认证:管理后台管理员 Token(Authorization: Bearer <admin-token>),未登录返 401。
  • 幂等性:查询接口,天然幂等。
  • 限流:走网关默认限流,无特殊配置。

入参(Query)

字段 位置 类型 必填 约束 说明
status Query String 否 见状态枚举 按报销状态筛选,省略查全部
departmentId Query Number 否 正整数 按部门 ID 筛选
company Query String 否 — 按公司主体名称筛选
keyword Query String 否 — 关键字模糊搜索(单号 / 事由)
page Query Number 是 ≥1 页码
pageSize Query Number 是 1-100 每页条数

出参

字段 类型 说明
data.records[].id String 报销单 ID(雪花 ID,Long 转字符串防 JS 精度丢失)
data.records[].expenseNo String 报销单号(FY- 前缀)
data.records[].expenseCategory String 费用分类末级名称
data.records[].departmentId String 部门 ID
data.records[].departmentName String 部门名称
data.records[].company String 公司主体名称
data.records[].amount Number 报销金额
data.records[].offsetLoanAmt Number 冲销借款金额(本期恒 0,见业务边界)
data.records[].payableAmt Number 应付金额(本期 = amount)
data.records[].reason String 报销事由
data.records[].rejectReason String 驳回原因(仅 REJECTED 状态有值,其余为 null)
data.records[].fundAccountId String 付讫资金账户 ID(仅 PAID 有值)
data.records[].status String 状态码,见状态枚举
data.records[].paidAt String 付讫时间(仅 PAID 有值)
data.records[].approvalInstanceId String 审批实例 ID(本期恒空,企微审批未接)
data.records[].createTime String 创建时间
data.total / page / pageSize Number 分页元信息

请求示例(典型)

GET /admin/finance/expenses/page?page=1&pageSize=20&status=APPROVED&departmentId=10086
Authorization: Bearer <admin-token>

响应示例(典型)

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "records": [
      {
        "id": "2094278854020399106",
        "expenseNo": "FY-20260907-0001",
        "expenseCategory": "市内交通费",
        "departmentId": "10086",
        "departmentName": "市场部",
        "company": "呼籁旅行社有限公司",
        "amount": 300.00,
        "offsetLoanAmt": 0,
        "payableAmt": 300.00,
        "reason": "客户拜访打车费",
        "rejectReason": null,
        "fundAccountId": null,
        "status": "APPROVED",
        "paidAt": null,
        "approvalInstanceId": null,
        "createTime": "2026-09-07 10:00:00"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}

空数据 / 边界响应

无匹配时 records=[]、total=0,HTTP 200,前端正常渲染空列表。

2. 申请报销 POST /admin/finance/expenses

VO: ExpenseCreateReqVO

使用场景

员工在管理后台提交一张费用报销单,落库为 PENDING 草稿(未进审批)。

认证 / 幂等性 / 限流

  • 认证:管理后台管理员 Token。
  • 幂等性:非幂等(每次创建生成新单号),重复提交产生多张单据,前端提交后应禁用按钮。
  • 限流:网关默认。

入参(Body)

字段 类型 必填 约束 说明
expenseCategory String 是 必须存在、为末级(level=2)、未停用 费用分类末级名称,按名称传不按 ID(取自 fin_expense_category 字典)
departmentId Number 是 必须存在 部门 ID(用户域组织树 wechat_department)
company String 是 必须为启用项 公司主体名称,按名称传(取自 travel_agency 启用项)
amount Number 是 >0 报销金额
reason String 是 非空 报销事由

出参

字段 类型 说明
data.id String 新建报销单 ID
data.expenseNo String 报销单号(FY- 前缀)
data.status String 创建后为 PENDING

请求示例(典型成功)

{
  "expenseCategory": "市内交通费",
  "departmentId": 10086,
  "company": "呼籁旅行社有限公司",
  "amount": 300.00,
  "reason": "客户拜访打车费"
}

请求示例(边界情况)

金额为最小粒度 / 事由最长:

{
  "expenseCategory": "办公用品费",
  "departmentId": 10086,
  "company": "呼籁旅行社有限公司",
  "amount": 0.01,
  "reason": "采购 A4 打印纸一箱"
}

请求示例(业务失败)

费用分类传了非末级 / 已停用名称 → 598703:

{
  "expenseCategory": "交通费",
  "departmentId": 10086,
  "company": "呼籁旅行社有限公司",
  "amount": 300.00,
  "reason": "打车"
}
{"code":598703,"message":"费用分类非法(不存在/非末级/已停用)","success":false,"data":null}

金额 ≤0 → 598704;部门或公司主体非法 → 598705。

3. 报销详情 GET /admin/finance/expenses/{id}

入参

字段 位置 类型 必填 说明
id Path String 是 报销单 ID(雪花字符串)

出参

字段与分页行一致(id/expenseNo/expenseCategory/departmentId/departmentName/company/amount/offsetLoanAmt/payableAmt/reason/rejectReason/fundAccountId/status/paidAt/approvalInstanceId/createTime),见 §1 出参表。

错误响应

{"code":598701,"message":"报销单不存在","success":false,"data":null}

4. 编辑草稿 PUT /admin/finance/expenses/{id}

VO: ExpenseUpdateReqVO(字段与 ExpenseCreateReqVO 完全一致,见 §2 入参表)

业务边界

  • 仅 PENDING 状态可编辑,其余状态调用返回 598702。
  • 编辑不改单号,状态保持 PENDING。

请求示例

{
  "expenseCategory": "市内交通费",
  "departmentId": 10086,
  "company": "呼籁旅行社有限公司",
  "amount": 350.00,
  "reason": "客户拜访打车费(含返程)"
}

错误响应

{"code":598702,"message":"报销单状态非法(仅PENDING可编辑删除、提交须PENDING、审批须SUBMITTED)","success":false,"data":null}

5. 删除草稿 DELETE /admin/finance/expenses/{id}

业务边界

  • 仅 PENDING 状态可删除,其余状态调用返回 598702。
  • 删除为软删除,单据不再出现在任何列表。

出参

字段 类型 说明
data Boolean true 表示删除成功

6. 提交报销 PUT /admin/finance/expenses/{id}/submit

业务边界

  • 仅 PENDING 状态可提交,提交后流转 PENDING → SUBMITTED,单据进入锁定态(不可编辑 / 删除)。
  • 无 Body 入参。

出参

字段 类型 说明
data.id String 报销单 ID
data.status String 提交后为 SUBMITTED

7. 批准报销 PUT /admin/finance/expenses/{id}/approve

业务边界

  • 仅 SUBMITTED 状态可批准,流转 SUBMITTED → APPROVED,进入出纳待付款队列。
  • 本期为手工审批(不接企微审批流),approvalInstanceId 恒空。
  • 无 Body 入参。

出参

字段 类型 说明
data.id String 报销单 ID
data.status String 批准后为 APPROVED

8. 驳回报销 PUT /admin/finance/expenses/{id}/reject

入参(Body)

字段 类型 必填 约束 说明
rejectReason String 是 非空 驳回原因(回显在详情 rejectReason 字段)

业务边界

  • 仅 SUBMITTED 状态可驳回,流转 SUBMITTED → REJECTED,不进出纳队列。

请求示例

{"rejectReason": "缺少打车发票附件,请补充后重新提交"}

出参

字段 类型 说明
data.id String 报销单 ID
data.status String 驳回后为 REJECTED

9. 出纳待付款队列 GET /admin/finance/cashier/queue(修改:payType 扩容)

变更点

入参 payType 枚举由仅 NONBIZ 扩容为 NONBIZ / EXPENSE。传 EXPENSE 时返回费用报销线(fin_expense 表 status=APPROVED 的单据);其余字段、结构、分页契约不变。

入参(Query,增量)

字段 位置 类型 必填 约束 说明
payType Query String 否 NONBIZ / EXPENSE 付款类型筛选;省略 / 空 = 既有行为

出参(队列行,费用线字段填充规则)

字段 类型 费用线(payType=EXPENSE)取值
id String 报销单 ID
bizNo String 报销单号(= expenseNo)
payType String EXPENSE
payTypeName String 费用
unitId / unitName String 槽位复用:部门 ID / 部门名称
category / categoryName String 费用分类(末级名称)
amount Number 报销金额
fee Number 手续费(费用线为 0)
actualAmount Number 应付金额(= payableAmt)
operatorName String 申请人
createTime String 创建时间
occurDate String 发生日期
status String 队列状态
remark String 报销事由(= reason)

请求示例

GET /admin/finance/cashier/queue?payType=EXPENSE
Authorization: Bearer <admin-token>

10. 出纳付款登记 POST /admin/finance/cashier/pay(修改:bizType 扩容)

变更点

入参 bizType 枚举新增 EXPENSE 值。bizType=EXPENSE 时 bizId 传报销单 ID,登记付款后回写 fin_expense 状态为 PAID、记录 fundAccountId / paidAt,同事务记资金流水。

入参(Body,关键字段)

字段 类型 必填 约束 说明
bizType String 是 新增 EXPENSE 业务类型;费用报销传 EXPENSE
bizId String 是 对应业务单据 ID bizType=EXPENSE 时传报销单 ID
fundAccountId String 是 必须存在 付款资金账户 ID
actualAmount Number 是 >0 出纳手录实际打款金额(默认带入 payableAmt,可改)
其余字段 — — — 与既有 NONBIZ 线一致,不变

业务边界

  • 出纳实际打款金额与 payableAmt 不一致时后端打 WARN 审计日志但不硬拦(允许出纳按实际打款登记)。
  • 并发防重:同事务记资金流水 + 条件更新回写,对同一报销单重复付款返回 598602。

错误响应

重复付款:

{"code":598602,"message":"该单据已付款,请勿重复操作","success":false,"data":null}

付款类型非法(598607 文案已扩为含 EXPENSE):

{"code":598607,"message":"付款类型非法,本期支持 NONBIZ/EXPENSE","success":false,"data":null}

11. 出纳已付款台账 GET /admin/finance/cashier/payments/page(修改:bizType 扩容)

变更点

入参新增 bizType 筛选,支持 EXPENSE;传空 / 省略 = 既有 NONBIZ 行为。其余字段、结构不变。

入参(Query,增量)

字段 位置 类型 必填 约束 说明
bizType Query String 否 NONBIZ / EXPENSE 业务类型筛选;空 = NONBIZ(既有行为)

请求示例

GET /admin/finance/cashier/payments/page?page=1&pageSize=20&bizType=EXPENSE
Authorization: Bearer <admin-token>

六.5、枚举 / 数据字典

status(报销单状态,代码枚举)

所属字段: ExpensePageReqVO.status / ExpenseRespVO.status | 类型: String

值 中文 说明
PENDING 待提交 草稿态,可编辑可删除
SUBMITTED 审批中 已提交,锁定(不可编辑 / 删除)
APPROVED 已批准 审批通过,待出纳付款
REJECTED 已驳回 审批驳回,rejectReason 有值
PAID 已付讫 出纳付款完成,终态只读

状态机:PENDING →(submit)→ SUBMITTED →(approve)→ APPROVED →(出纳付款)→ PAID;SUBMITTED →(reject)→ REJECTED。

payType / bizType(出纳域,枚举值扩容)

所属字段: cashier/queue 入参 payType、cashier/pay 入参 bizType、cashier/payments/page 入参 bizType | 类型: String

值 中文 说明
NONBIZ 非业务付款 既有值,行为不变
EXPENSE 费用(报销) 新增值,费用报销线

expenseCategory(费用分类,数据字典 fin_expense_category)

  • 按末级名称(level=2)传值,按名称不按 ID。
  • 仅正常(未停用)项可选;传非末级 / 不存在 / 已停用名称返回 598703。

company(公司主体,数据字典 travel_agency)

  • 按名称传值,仅启用项可选;非法返回 598705。

错误码(新段 5987xx + 出纳段扩充)

错误码 含义 触发场景
598701 报销单不存在 详情 / 编辑 / 删除 / 提交 / 审批传错 ID
598702 报销单状态非法 非 PENDING 编辑删除、非 PENDING 提交、非 SUBMITTED 审批
598703 费用分类非法 分类不存在 / 非末级 / 已停用
598704 金额无效 amount ≤ 0
598705 部门或公司主体非法 departmentId 不存在或 company 非启用项
598706 单号取号撞号重试耗尽 极端并发下取号失败(重试后仍撞号)
598607 付款类型非法(文案扩充) 出纳付款 / 台账传非 NONBIZ/EXPENSE 值

六.6、修改前后对比

项目 修改前 修改后
费用报销接口 无 新增 /admin/finance/expenses 下 8 端点(分页 / 申请 / 详情 / 编辑 / 删除 / 提交 / 批准 / 驳回)
出纳队列 payType 仅 NONBIZ 扩容 NONBIZ / EXPENSE
出纳付款 bizType 仅 NONBIZ 扩容 NONBIZ / EXPENSE
出纳台账筛选 无 bizType 入参 新增 bizType 入参(空 = NONBIZ)
598607 文案 仅提示 NONBIZ 扩为「付款类型非法,本期支持 NONBIZ/EXPENSE」

六.7、影响评估

  • 是否破坏向后兼容:否。expense 8 端点为纯新增;出纳 3 端点为入参枚举扩容,省略新值时行为与之前完全一致。
  • 前端是否必须同步上线:否。旧功能不受影响;费用报销页面与出纳队列费用线 Tab 可按节奏上线。
  • 前端 workaround 清理点:无(全新能力)。

七、不影响范围

  • 小程序端(C 端):费用报销为纯管理后台能力,/mp/** 零改动。
  • 出纳 NONBIZ 线:不传 payType/bizType=EXPENSE 时行为与之前完全一致。
  • 员工借款 / 冲销:本期未建借款域,offsetLoanAmt 恒 0、payableAmt = amount,留 TODO 后续接入。

八、测试环境已验证

192.168.100.236 测试服行为级 E2E 16 PASS / 0 FAIL:

  • 全链路走通:申请 → 提交 → 批准 → 出纳付款 → 回写 PAID。
  • 结存勾稽正确:付款前资金账户余额 114894 − 打款 300 = 114594。
  • 并发防重生效:重复付款返回 598602。
  • 四类校验生效:598702(状态非法)/ 598703(分类非法)/ 598704(金额无效)/ 598705(部门或公司非法)。

当前状态

  • 后端:已部署并已验证。
  • 前端:待处理(费用报销页面 + 出纳队列费用线 Tab)。

十、相关文档

关联 / 联系人

  • Issue: #7232
  • PR: #7247
  • 后端负责人: @yst
  • 当前状态: 后端已就绪(TEST 已验证),前端待接入。