Test #1

Merged
admin merged 256 commits from test into main 2026-09-28 16:01:16 +08:00
3 changed files with 132 additions and 7 deletions
Showing only changes of commit 08a80e7b44 - Show all commits
+1 -1
View File
@@ -47,7 +47,7 @@
| 工作包 | 执行任务 | 写入范围 | 交付目标 | 依赖与集成边界 |
|---|---|---|---|---|
| `WALLET-BE-FINANCE` | 【开发总控】全流程开发 | `baiyue-firstleg/finance/`、Finance OpenAPI、模块 SQL 与统一增量 SQL、必要 Finance/File 内部客户端 | 新增客户资金账户、充值申请和资金流水主账;提供客户账户/流水/充值申请接口及平台账户列表/详情、待审充值、审核、手工充值/扣款接口;实现客户隔离、金额校验、权限、幂等、乐观并发与测试 | Finance 拥有余额和流水;付款截图经 Finance 调用 File 内部 `FINANCE_VOUCHER` 保存;不接支付回调、不接订单消费 |
| `WALLET-BE-FINANCE` | 【开发总控】全流程开发 | `baiyue-firstleg/finance/`、Finance OpenAPI、模块 SQL 与统一增量 SQL、必要 Finance/File 内部客户端 | 新增客户资金账户、充值申请、授信额度、资金冻结和流水主账;提供客户账户/流水/充值申请接口及平台账户列表/详情、待审充值、审核、手工充值/扣款/授信接口,并提供业务模块使用的冻结、结算和释放内部接口;实现客户隔离、金额校验、权限、服务令牌、幂等、乐观并发与测试 | Finance 拥有余额、授信、冻结和流水;扣款先使用实际资金再使用授信,充值先归还已用授信;付款截图经 Finance 调用 File 内部 `FINANCE_VOUCHER` 保存;不接第三方支付回调,具体一件代发业务调用后续接线 |
| `WALLET-FE-CLIENT` | 【开发全流程前端】Client | `apps/client-web/src/modules/finance/` 及该模块路由、导航和测试 | 新增客户资金账户页,显示实际/冻结/可用余额和流水;按原型完成金额选择、微信/支付宝静态二维码、付款截图上传、提交待审核及状态反馈 | 客户范围只取登录态;只调用 `/api/client/finance/v1/**`,无二维码配置时明确不可提交,不模拟支付成功 |
| `WALLET-FE-ADMIN` | 【开发全流程前端】Admin | `apps/admin-web/src/modules/finance/` 及对应测试 | 将客户账户页收敛为三余额账户;详情中待审核客户充值置顶并提供审核,保留流水;列表/详情提供手工充值和手工扣款,操作列固定首列 | 只消费 Finance 正式 API;审核、充值、扣款分别受写权限控制;不在前端计算或兜底余额 |
| `WALLET-BE-IAM` | 【开发全流程后端】IAM权限 | `baiyue-firstleg/iam/` 及必要权限初始化 SQL、OpenAPI 和测试 | 新增并初始化客户账户查看/充值申请和平台充值审核/账户调账权限,保持列表只读与写操作分离 | 企业负责人默认获得客户账户查看和充值申请权限;平台财务角色按现有初始化规则获得审核与调账权限,不修改 Finance 业务 |
+2 -2
View File
@@ -2,9 +2,9 @@
## 当前确认:一件代发客户资金账户(2026-09-05)
当前对话已将资金账户作为一件代发的前置节点重新纳入范围,并覆盖本文后续历史章节中“钱包不做”的旧结论。首批固定为 CNY,满足 `实际余额 = 冻结金额 + 可用余额`;冻结金额本批只持久化和展示,暂不接消费。
当前对话已将资金账户作为一件代发的前置节点重新纳入范围,并覆盖本文后续历史章节中“钱包不做”的旧结论。首批固定为 CNY。账户分别保存实际余额、冻结金额、授信额度、已用授信和冻结授信;`总可用余额 = 实际余额 + 授信额度 - 已用授信 - 冻结金额`,`可用授信 = 授信额度 - 已用授信 - 冻结授信`。扣款及业务冻结先使用未冻结的实际资金,再使用授信;充值先归还已用授信,剩余金额增加实际余额。
客户选择固定或自定义金额,查看后台配置的微信/支付宝静态收款码,付款后上传 JPG/PNG 截图形成待审核申请。平台在客户账户明细顶部审核或驳回,也可手工充值、扣款;每次变动写不可变账户流水。审核、账户更新、流水和幂等结果在 Finance 单事务内完成,文件内容仍归 File 服务。接口契约见 [客户资金账户 OpenAPI](openapi/finance-wallet-v1.yaml)。
客户选择固定或自定义金额,查看后台配置的微信/支付宝静态收款码,付款后上传 JPG/PNG 截图形成待审核申请。平台在客户账户明细顶部审核或驳回,也可手工充值、扣款和设置客户授信额度;每次变动写不可变账户流水。新账户授信额度读取 `baiyue.finance.wallet.default-credit-limit`,默认 `0.00`,平台设置的客户额度随后独立持久化。Finance 还提供仅服务令牌可访问、按业务类型和业务单号幂等的冻结、结算和释放接口,供一件代发等模块接入。审核、账户更新、流水、冻结记录和幂等结果在 Finance 单事务内完成,文件内容仍归 File 服务。接口契约见 [客户资金账户 OpenAPI](openapi/finance-wallet-v1.yaml)。
> 盘点日期:2026-08-10
> 当前状态:F0/F1 已有 Finance 服务、数据库、权限、Admin/Client 页面和基础接口;订单与客户 11 个 P0 operation 的目标契约已冻结,本地实现验收按独立 G1-G4 证据矩阵均为 `PASS`;真实 MySQL、测试环境 HTTP 冒烟和领导页面验收仍需单独执行
+129 -4
View File
@@ -1,14 +1,22 @@
openapi: 3.0.3
info:
title: 客户资金账户与充值 API
version: 1.0.0
description: 静态收款码、付款截图申请、平台审核及手工充值扣款。当前币种固定 CNY,消费暂不接入。
title: 客户资金账户、授信与充值 API
version: 1.1.0
description: |
CNY 资金账户支持静态收款码、付款截图申请、平台审核、手工充扣、授信额度及业务冻结/结算/释放。
总可用余额 = 实际余额 + 授信额度 - 已用授信 - 冻结金额;业务扣款先使用未冻结的实际资金,再使用授信。
充值先归还已用授信,剩余部分增加实际余额。新账户授信额度由
`baiyue.finance.wallet.default-credit-limit` 初始化,默认 `0.00`,平台可按客户调整。
paths:
/api/client/finance/v1/account:
get:
summary: 查询当前客户资金账户
responses:
'200': { description: 成功 }
'200':
description: 成功
content:
application/json:
schema: { $ref: '#/components/schemas/WalletAccountEnvelope' }
/api/client/finance/v1/account/ledger:
get:
summary: 查询当前客户账户流水
@@ -91,6 +99,71 @@ paths:
schema: { $ref: '#/components/schemas/AccountAdjustmentRequest' }
responses:
'200': { description: 成功 }
/api/platform/finance/v1/accounts/{customerId}/credit-limit:
put:
summary: 设置客户授信额度
parameters:
- $ref: '#/components/parameters/CustomerId'
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/CreditLimitRequest' }
responses:
'200': { description: 成功并生成授信调整流水 }
/internal/finance/v1/wallet/accounts/{customerId}:
get:
summary: 业务模块查询客户资金账户
parameters:
- $ref: '#/components/parameters/InternalServiceToken'
- $ref: '#/components/parameters/CustomerId'
responses:
'200': { description: 成功 }
/internal/finance/v1/wallet/accounts/{customerId}/holds:
post:
summary: 按业务单冻结资金,先冻结实际资金再冻结授信
parameters:
- $ref: '#/components/parameters/InternalServiceToken'
- $ref: '#/components/parameters/IdempotencyKey'
- $ref: '#/components/parameters/CustomerId'
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/WalletFreezeRequest' }
responses:
'200': { description: 成功;同一客户、业务类型和业务单号只允许一条冻结记录 }
/internal/finance/v1/wallet/accounts/{customerId}/holds/{businessType}/{businessNo}/settle:
post:
summary: 结算已冻结资金
parameters:
- $ref: '#/components/parameters/InternalServiceToken'
- $ref: '#/components/parameters/IdempotencyKey'
- $ref: '#/components/parameters/CustomerId'
- $ref: '#/components/parameters/BusinessType'
- $ref: '#/components/parameters/BusinessNo'
requestBody:
content:
application/json:
schema: { $ref: '#/components/schemas/WalletHoldActionRequest' }
responses:
'200': { description: 成功;实际冻结转为实际扣减,授信冻结转为已用授信 }
/internal/finance/v1/wallet/accounts/{customerId}/holds/{businessType}/{businessNo}/release:
post:
summary: 释放已冻结资金
parameters:
- $ref: '#/components/parameters/InternalServiceToken'
- $ref: '#/components/parameters/IdempotencyKey'
- $ref: '#/components/parameters/CustomerId'
- $ref: '#/components/parameters/BusinessType'
- $ref: '#/components/parameters/BusinessNo'
requestBody:
content:
application/json:
schema: { $ref: '#/components/schemas/WalletHoldActionRequest' }
responses:
'200': { description: 成功;实际资金和授信恢复为可用 }
components:
parameters:
IdempotencyKey:
@@ -98,6 +171,11 @@ components:
in: header
required: true
schema: { type: string, maxLength: 128 }
InternalServiceToken:
name: X-Internal-Service-Token
in: header
required: true
schema: { type: string, writeOnly: true }
CustomerId:
name: customerId
in: path
@@ -108,7 +186,35 @@ components:
in: path
required: true
schema: { type: string, pattern: '^[1-9][0-9]*$' }
BusinessType:
name: businessType
in: path
required: true
schema: { type: string, pattern: '^[A-Z][A-Z0-9_]{1,31}$' }
BusinessNo:
name: businessNo
in: path
required: true
schema: { type: string, minLength: 1, maxLength: 128 }
schemas:
WalletAccountEnvelope:
type: object
properties:
data: { $ref: '#/components/schemas/WalletAccount' }
WalletAccount:
type: object
required: [currencyCode, actualBalance, frozenBalance, availableBalance, creditLimit, creditUsed, creditFrozen, availableCredit, version]
properties:
currencyCode: { type: string, enum: [CNY] }
actualBalance: { type: number, format: decimal, minimum: 0 }
frozenBalance: { type: number, format: decimal, minimum: 0, description: 实际资金冻结与授信冻结合计 }
availableBalance: { type: number, format: decimal, minimum: 0, description: 实际可用资金与可用授信合计 }
creditLimit: { type: number, format: decimal, minimum: 0 }
creditUsed: { type: number, format: decimal, minimum: 0 }
creditFrozen: { type: number, format: decimal, minimum: 0 }
availableCredit: { type: number, format: decimal, minimum: 0 }
version: { type: integer, minimum: 0 }
updatedAt: { type: string, format: date-time, nullable: true }
RechargeReviewRequest:
type: object
required: [decision, expectedVersion]
@@ -127,3 +233,22 @@ components:
referenceNo: { type: string, maxLength: 128 }
remark: { type: string, maxLength: 500 }
expectedAccountVersion: { type: integer, minimum: 0 }
CreditLimitRequest:
type: object
required: [creditLimit, expectedAccountVersion]
properties:
creditLimit: { type: number, format: decimal, minimum: 0 }
remark: { type: string, maxLength: 500 }
expectedAccountVersion: { type: integer, minimum: 0 }
WalletFreezeRequest:
type: object
required: [amount, businessType, businessNo]
properties:
amount: { type: number, format: decimal, minimum: 0.01 }
businessType: { type: string, minLength: 1, maxLength: 64 }
businessNo: { type: string, minLength: 1, maxLength: 128 }
remark: { type: string, maxLength: 500 }
WalletHoldActionRequest:
type: object
properties:
remark: { type: string, maxLength: 500 }