You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digital-rmb-backend/docs/superpowers/specs/2026-08-03-issuance-request...

127 lines
7.8 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 数字货币发行请求模块设计
## 目标
实现“①发送数字货币发行请求”教学页面的后端。学生携带有效的本系统 JWT 后,可以在商业银行端查询库存、创建并逐步处理发行申请,并在中央银行端查看接收结果。接口不区分教师与学生角色。
## 范围
本期仅覆盖页面的第 1 步:商业银行生成并发送发行请求,以及中央银行显示已接收的请求。两个端是独立的接口入口和查询视图,但属于同一教学系统并共享发行申请数据。第 2 至第 7 步(验签、业务核查、准备金扣减、数字货币生成、确权)不在本期实现范围内。
## 限界上下文与分层
扩展既有 `issuance` 限界上下文,不新增顶层模块。`IssuanceRequest` 是聚合根;库存查询是该上下文的只读查询模型。
```text
issuance/
domain/
model/ IssuanceRequest、IssuanceApplicationId、DenominationItem、IssuanceRequestStatus
repository/ IssuanceRequestRepository
service/ IssuanceMessageComposer、IssuanceSignatureService
application/
command/ CreateIssuanceRequestCommand、UpdateDenominationsCommand
query/ IssuanceInventoryQueryService、IssuanceRequestQueryService
service/ IssuanceRequestApplicationService
infrastructure/
persistence/ MyBatis-Plus Entity、Mapper、Repository 实现
crypto/ Bouncy Castle 的 SM3/SM2 实验实现
interfaces/
rest/ CommercialBankIssuanceController、CentralBankIssuanceController
dto/ 请求与响应 DTO
```
依赖方向保持 `interfaces → application → domain``infrastructure` 只实现领域端口。Controller 不直接调用 Mapper。
## 聚合与状态机
`IssuanceRequest` 保存机构代码、机构标识、发行金额、面额明细、币种、时间戳、待签名原文、摘要、签名、签名密钥标识、请求报文和当前状态。
商业银行申请状态只能按下列顺序变化:
```text
DRAFT → MESSAGE_PREPARED → DIGESTED → SIGNED → PACKAGED → SENT
```
中央银行接收状态独立保存:
```text
NOT_RECEIVED → RECEIVED
```
- 只有 `DRAFT` 可修改总金额、面额明细和币种。
- 面额数量必须为正整数,`Σ(面额 × 数量)` 必须等于发行总金额。
- `prepare-message` 只允许从 `DRAFT` 执行,并冻结时间戳和待签名原文。
- `digest`、`sign`、`package` 只允许在前一状态执行。
- `send``PACKAGED` 进入 `SENT` 后,在同一事务中将中央银行接收状态设为 `RECEIVED` 并记录接收时间,模拟中央银行已接收;重复发送返回同一请求,不生成第二笔记录。
- 已到达目标状态的重复按钮请求返回当前数据,不重新计算摘要或签名。
待签名原文采用固定字段顺序:
```text
ISSUE|{bankCode}|{organizationId}|{totalAmount}|{denominations}|{currency}|{timestamp}
```
`denominations` 按面额从大到小序列化为 `面额:数量`,例如 `100:400,50:100,20:200,10:50,5:80,1:100`
## 密码学教学实现
服务端使用 Bouncy Castle 提供的 SM3 和 SM2 算法:摘要为 SM3 十六进制文本,签名算法为 `SM3withSM2`。私钥不通过接口输入或返回。
本期通过 `SigningKeyProvider` 端口提供 `sm2-key-02` 实验密钥;基础设施实现仅在服务端生成并缓存密钥对。响应只暴露 `signingKeyRef`、摘要和签名。后续第 2 步验签可替换为配置化或密钥库实现,不改变聚合和接口契约。
## 数据模型
基础初始化脚本 `src/main/resources/schema.sql` 增加以下幂等表和演示数据:
| 表 | 关键字段 | 用途 |
| --- | --- | --- |
| `issuance_bank_inventory` | `bank_code`、`current_balance`、`warning_threshold` | 查询库存、预警阈值和建议补充金额。预置 `BKCHCNBJ00001`。 |
| `issuance_request` | `id`、`request_no`、`bank_code`、`organization_id`、`total_amount`、`currency`、`request_timestamp`、`message_text`、`digest`、`signature`、`signing_key_ref`、`payload_json`、`status`、`central_receive_status`、`central_received_at`、审计字段 | 发行申请聚合持久化。 |
| `issuance_request_denomination` | `request_id`、`denomination`、`quantity` | 发行请求的面额明细,`request_id + denomination` 唯一。 |
建议补充金额始终计算为 `max(warning_threshold - current_balance, 0)`,不单独持久化。
## REST 接口
所有接口要求有效 JWT不添加角色限定。商业银行端与中央银行端使用不同的 URL 前缀和 Controller中央银行端不提供创建、修改、签名或发送操作。
| 方法与路径 | 作用 |
| --- | --- |
| `GET /api/v1/commercial-banks/issuance/inventory?bankCode=BKCHCNBJ00001` | 商业银行端返回库存余额、预警阈值、建议补充金额。 |
| `POST /api/v1/commercial-banks/issuance/requests` | 商业银行端创建 `DRAFT` 发行申请。 |
| `PUT /api/v1/commercial-banks/issuance/requests/{id}` | 商业银行端仅在 `DRAFT` 更新金额、面额明细和币种。 |
| `GET /api/v1/commercial-banks/issuance/requests/{id}` | 商业银行端返回完整申请与处理产物。 |
| `POST /api/v1/commercial-banks/issuance/requests/{id}/prepare-message` | 商业银行端冻结时间戳并生成待签名原文。 |
| `POST /api/v1/commercial-banks/issuance/requests/{id}/digest` | 商业银行端对待签名原文生成 SM3 摘要。 |
| `POST /api/v1/commercial-banks/issuance/requests/{id}/sign` | 商业银行端使用 `sm2-key-02` 生成 SM2 签名。 |
| `POST /api/v1/commercial-banks/issuance/requests/{id}/package` | 商业银行端生成请求 JSON 报文。 |
| `POST /api/v1/commercial-banks/issuance/requests/{id}/send` | 商业银行端模拟发送;申请变为 `SENT`,中央银行接收状态变为 `RECEIVED`。 |
| `GET /api/v1/central-banks/issuance/requests/{id}` | 中央银行端返回接收状态、接收时间和 JSON 报文。 |
不存在的申请返回 `RESOURCE_NOT_FOUND`;状态不合法或金额校验不通过返回 `VALIDATION_ERROR`;未认证请求沿用既有 Spring Security 的 `UNAUTHORIZED` 响应。
## Swagger 文档
Swagger 页面使用中文,所有新增 Java 源文件和 OpenAPI 元数据以 UTF-8 保存,页面不允许出现乱码。发行域接口按以下中文标签分组:
- `数字货币发行模块 - 商业银行端`:库存查询、创建申请、更新申请、生成原文、摘要、签名、封装和发送。
- `数字货币发行模块 - 中央银行端`:查询发行请求接收状态、接收时间和请求报文。
每个接口通过中文 `summary``description` 明确说明所属模块、所属端、调用条件、状态变化及返回内容;请求字段和响应字段使用中文 `@Schema` 描述。OpenAPI 根标题固定为“数字人民币教学仿真后端”,接口分组和接口说明不得使用英文替代中文。
## 测试标准
- 领域单元测试覆盖金额守恒、非法状态流转和重复发送幂等性。
- 应用/持久化测试覆盖库存建议金额、请求与面额明细的保存和读取。
- `MockMvc` 测试覆盖学生有效 JWT 下的所有首期接口,以及未认证被拒绝。
- 密码学测试校验 SM3 输出长度和 SM2 签名可由同一实验公钥验证。
- 全量 Maven 测试在 JDK 8 下通过;以 `dev` profile 启动后Swagger 页面可访问。
- 访问 `/v3/api-docs` 和 Swagger UI确认“数字货币发行模块 - 商业银行端”“数字货币发行模块 - 中央银行端”标签及中文说明可正常显示,不含乱码字符。
## 明确约束
- 使用 JDK 8、Spring Boot 2.7.18、MyBatis-Plus 3.5.17、Spring Security 5.7。
- 不恢复 Flyway所有基础表变化同步更新幂等 `schema.sql` 并在 118 测试库验证。
- 不在日志、接口响应或数据库中保存私钥明文。
- 不实现真实央行网络调用、准备金扣减或数字货币生成。