交易订单管理 — 技术方案
依据:交易订单管理功能需求.md(同目录)
关联:../供应商结算管理/供应商结算管理-草稿.md(结算单由本模块完成支付时生成)
1. 技术架构
| 层级 |
说明 |
| 整体 |
RuoYi v3.9.2(springboot2 分支)单体后端 + 若依 Vue2 前端 |
| 运行时 |
JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| 数据库 |
MySQL 5.7.39,InnoDB,utf8mb4 |
后端分层(com.ruoyi.web.modules.trading)
| 组件 |
职责 |
BizTradeOrderController |
订单 CRUD、完成支付、耳标候选 |
IBizTradeOrderService / BizTradeOrderServiceImpl |
事务:保存主从表、完成支付并生成结算单 |
TradeOrderValidation |
R1~R8 |
TradeOrderEarTagResolver |
下拉:listSupplierEarTagOptions(进栏子表);保存:(M−B)−C listEligible |
TradeOrderNoGenerator |
订单编号 YYYYMMDD+3 位序号 |
SettlementOnPayService |
完成支付时写 biz_supplier_settlement(费用快照) |
BizTradeOrderMapper / BizTradeOrderLineMapper |
订单持久化 |
业务摘要
| 场景 |
行为 |
| 新增/修改 |
仅 order_status=0;校验 R1~R8;生成/保留 order_no;主表 + 明细 batchInsert;修改先删明细再插 |
| 完成支付 |
@Transactional:状态→已完成、凭证、完成时间 → 一条结算单 → 按明细删 biz_market_entry_ear_tag(幂等:结算已存在则拒绝) |
| 删除 |
仅待支付;del_flag=2 |
| 列表/详情 |
del_flag=0;JOIN 供应商/承销商名;明细组装 lineList |
初始化脚本:sql/biz_trade_order.sql、sql/biz_supplier_settlement.sql
2. 源码位置(建议)
| 类型 |
路径 |
| Controller |
.../controller/BizTradeOrderController.java |
| Service |
.../service/IBizTradeOrderService.java、.../impl/BizTradeOrderServiceImpl.java |
| 结算写入 |
.../support/SettlementOnPayService.java |
| 校验/耳标/等级 |
.../support/TradeOrderValidation.java、TradeOrderEarTagResolver.java、TradeOrderGradeResolver.java、TradeOrderRules.java |
| Mapper |
.../mapper/BizTradeOrderMapper.java、BizTradeOrderLineMapper.java、BizGradeWeightConfigMapper.java |
| XML |
mapper/trading/BizTradeOrderMapper.xml、BizTradeOrderLineMapper.xml、BizGradeWeightConfigMapper.xml |
| 实体 |
.../domain/BizTradeOrder.java、BizTradeOrderLine.java、BizGradeWeightConfig.java |
供应商结算管理(独立迭代,本方案仅定义表与生成逻辑):BizSupplierSettlementController、/tradeMarket/supplierSettlement。
3. 数据库设计
3.1 主表:biz_trade_order(交易订单)
| 字段名 |
类型 |
非空 |
默认值 |
说明 |
id |
bigint(20) |
Y |
自增 |
主键 |
order_no |
varchar(11) |
Y |
— |
订单编号 YYYYMMDD+3 位序号,唯一 |
supplier_id |
bigint(20) |
Y |
— |
biz_supplier.id |
distributor_id |
bigint(20) |
Y |
— |
biz_distributor.id(承销商) |
total_heads |
int(11) |
Y |
— |
交易总头数 = 明细行数 |
total_amount |
decimal(12,2) |
Y |
— |
交易总金额(元) |
order_status |
char(1) |
Y |
'0' |
0 待支付 1 已完成 |
pay_voucher_url |
varchar(512) |
N |
NULL |
支付凭证 URL |
pay_voucher_path |
varchar(512) |
N |
NULL |
支付凭证路径 |
finish_time |
datetime |
N |
NULL |
订单完成时间(完成支付时写入) |
del_flag |
char(1) |
Y |
'0' |
0 存在 2 逻辑删除 |
create_by / create_time / update_by / update_time |
若依惯例 |
— |
— |
审计;订单创建时间用 create_time |
remark |
varchar(500) |
N |
NULL |
备注 |
索引:PRIMARY KEY (id);UNIQUE KEY uk_order_no (order_no);KEY idx_supplier (supplier_id);KEY idx_distributor (distributor_id);KEY idx_order_status (order_status);KEY idx_create_time (create_time);KEY idx_del_flag (del_flag)。
3.2 子表:biz_trade_order_line(采购明细)
| 字段名 |
类型 |
非空 |
默认值 |
说明 |
id |
bigint(20) |
Y |
自增 |
主键 |
order_id |
bigint(20) |
Y |
— |
主表 ID |
supplier_id |
bigint(20) |
Y |
— |
供应商 ID(与主表一致,冗余) |
ear_tag |
varchar(32) |
Y |
— |
耳标号 |
weight |
decimal(12,2) |
Y |
— |
重量 kg,>0 |
amount |
decimal(12,2) |
Y |
— |
金额 元,>0 |
grade_code |
char(1) |
N |
NULL |
品质等级编码;保存时由 TradeOrderGradeResolver 按 biz_grade_weight_config 匹配后写入 |
grade_name |
varchar(32) |
N |
NULL |
品质等级名称快照;与 grade_code 同时写入 |
索引:PRIMARY KEY (id);UNIQUE KEY uk_order_ear (order_id, ear_tag);KEY idx_order_id (order_id);KEY idx_supplier (supplier_id);KEY idx_ear_tag (ear_tag)。
维护:新增/修改主表成功后批量插入;supplier_id 由服务端从主表带出;修改时 DELETE ... WHERE order_id=? 后重插。
3.3 关联表:biz_supplier_settlement(供应商结算,本模块写入)
结算模块查询/完成结算在 /tradeMarket/supplierSettlement 实现;仅在订单完成支付时由 SettlementOnPayService 插入一条。
| 字段名 |
类型 |
非空 |
说明 |
id |
bigint(20) |
Y |
主键 |
settlement_no |
varchar(14) |
Y |
JSD+YYYYMMDD+3 位序号,唯一 |
order_id |
bigint(20) |
Y |
关联订单,唯一(一对一) |
order_no |
varchar(11) |
Y |
冗余,列表按关联订单号模糊 |
supplier_id |
bigint(20) |
Y |
冗余 |
total_heads |
int(11) |
Y |
来自订单 |
total_amount |
decimal(12,2) |
Y |
来自订单 |
fee_method |
tinyint(4) |
Y |
快照:生成时供应商收费方式 |
fee_standard |
decimal(12,2) |
N |
快照:收费标准(元/头) |
transaction_fee_rate |
decimal(5,2) |
N |
快照:交易费率 % |
management_fee_rate |
decimal(5,2) |
N |
快照:管理费率 % |
service_fee_amount |
decimal(12,2) |
Y |
服务费(元),生成时计算固化 |
payable_amount |
decimal(12,2) |
Y |
实际应付 = 总金额 − 服务费 |
settlement_status |
char(1) |
Y |
0 待结算 1 已完成 |
settle_voucher_url / settle_voucher_path |
varchar(512) |
N |
结算凭证(结算模块完成时写入) |
settle_finish_time |
datetime |
N |
结算完成时间 |
del_flag |
char(1) |
Y |
0/2 |
| 审计字段 |
— |
— |
若依惯例 |
索引:UNIQUE KEY uk_order_id (order_id);UNIQUE KEY uk_settlement_no (settlement_no);KEY idx_order_no (order_no);KEY idx_settlement_no (settlement_no);KEY idx_supplier;KEY idx_settlement_status;KEY idx_del_flag。
3.4 只读关联表
| 表 |
用途 |
biz_supplier |
供应商校验、收费快照来源 |
biz_distributor |
承销商校验 |
biz_market_entry + biz_market_entry_ear_tag |
耳标池 M |
biz_isolation_patrol |
耳标池 B(selectSickDeadEarTagRows) |
biz_grade_weight_config |
等级重量区间;保存订单明细时匹配 grade_code |
3.5 枚举
| 字段 |
值 |
含义 |
order_status |
0 / 1 |
待支付 / 已完成 |
settlement_status |
0 / 1 |
待结算 / 已完成 |
del_flag |
0 / 2 |
正常 / 已删 |
fee_method |
1 / 2 / 3 |
按户 / 按头 / 按额(同供应商表) |
3.6 DDL
见 sql/biz_trade_order.sql、sql/biz_supplier_settlement.sql、sql/biz_grade_weight_config.sql。
4. 字段命名(JSON 小驼峰)
| JSON |
库表 |
说明 |
id |
id |
主键 |
orderNo |
order_no |
订单编号(新增响应可返回) |
supplierId |
supplier_id |
|
supplierName |
— |
列表/详情 JOIN |
distributorId |
distributor_id |
承销商 |
distributorName |
— |
JOIN |
totalHeads |
total_heads |
|
totalAmount |
total_amount |
|
orderStatus |
order_status |
0/1 |
orderStatusName |
— |
展示:待支付/已完成 |
payVoucherUrl / payVoucherPath |
支付凭证 |
完成支付入参 |
finishTime |
finish_time |
订单完成时间 |
earTag |
ear_tag |
明细耳标 |
weight |
weight |
明细重量 kg |
amount |
amount |
明细金额 元 |
gradeCode |
grade_code |
明细等级编码;响应返回;请求体不传,保存时服务端按重量匹配 |
gradeName |
grade_name |
明细等级名称快照;响应返回;请求体不传,与 gradeCode 同时写入 |
lineList |
子表 |
[{ earTag, weight, amount }](supplierId、gradeCode、gradeName 服务端写入,请求体不传) |
orderNo(筛) |
order_no |
列表模糊 LIKE |
5. 核心业务实现
5.1 订单编号 TradeOrderNoGenerator
- 规则:
yyyyMMdd + 3 位序号(001~999)。
- 查询当日
del_flag=0 最大序号 +1;冲突重试。
- 超 999 抛业务异常。
5.2 耳标候选 TradeOrderEarTagResolver
与功能需求 §4.4 一致,下拉与保存分流:
| 方法 |
用途 |
SQL / 逻辑 |
listSupplierEarTagOptions(supplierId) |
GET /supplierEarTags |
BizMarketEntryEarTagMapper.selectEarTagsBySupplierIdOnly:biz_market_entry_ear_tag 按 supplier_id,distinct ear_tag |
listEligible(supplierId, excludeOrderId) |
保存 R3、completePay 前复检 |
(M−B)−C |
| 集合 |
SQL 要点(仅 listEligible) |
| M |
biz_market_entry del_flag=0 + supplier_id → selectDistinctEarTagsBySupplierId |
| B |
biz_isolation_patrol:del_flag=0、sick_dead_qty>0(selectSickDeadEarTagRows) |
| C |
biz_trade_order_line JOIN biz_trade_order:del_flag=0、order_status in (0,1),order_id <> excludeOrderId |
BizTradeOrderServiceImpl.releaseMarketEntryEarTags:支付成功后 deleteBySupplierIdAndEarTag(明细 supplier_id + ear_tag)。
5.2.1 等级匹配 TradeOrderGradeResolver
- 配置表:
biz_grade_weight_config(sql/biz_grade_weight_config.sql)。
- 保存/修改订单、
completePay 前复检时,在 TradeOrderValidation.prepareAndValidateForSave 内对每行明细:
- 读取全部配置(
sort_order 升序);
- 若
min_weight ≤ weight ≤ max_weight(闭区间,BigDecimal.compareTo),写入 grade_code;
- 无匹配或配置为空 → 业务异常(
MSG_GRADE_NOT_MATCHED / MSG_GRADE_CONFIG_EMPTY)。
- 请求体中的
gradeCode 忽略,以服务端匹配结果为准。
5.3 保存校验 TradeOrderValidation
| 规则 |
实现 |
| R1~R2 |
非空 + biz_supplier / biz_distributor 存在且 del_flag=0 |
| R3~R4 |
耳标池 + EarTagCodec 去重;weight/amount > 0 |
| R5~R6 |
totalHeads = lineList.size();totalAmount = sum(amount) 保留 2 位小数 |
| R7 |
order_status=1 拒绝 update |
| R8 |
order_no 唯一 |
5.4 完成支付 completePay
接口:PUT /tradeMarket/tradeOrder/completePay(见 §6.8)。
事务步骤:
- 锁订单行或校验
order_status=0、del_flag=0;
- 校验支付凭证后缀/路径(
jpg|jpeg|png,≤10MB 由上传接口保证);
- 再校验 R3~R6、供应商
fee_method 已配置;
UPDATE 订单:order_status=1,finish_time=now(),凭证字段;
- 若
biz_supplier_settlement 已存在 order_id → 抛错(幂等);
SettlementOnPayService.createFromOrder(order):生成 settlement_no,写快照与 service_fee_amount、payable_amount(HALF_UP 2 位小数)。
- 按订单明细
supplier_id + ear_tag 删除 biz_market_entry_ear_tag 对应行(释放进栏耳标池,与 /supplierEarTags 数据源一致)。
服务费计算(BigDecimal):
fee_method=1 → serviceFee = 0
fee_method=2 → serviceFee = totalHeads × feeStandard
fee_method=3 → serviceFee = totalAmount × (transactionFeeRate + managementFeeRate) / 100
payableAmount = totalAmount − serviceFee
5.5 删除
- 仅
order_status=0;del_flag=2;
- 已完成拒绝;可选:存在结算单时亦拒绝(已完成必存在)。
6. 接口设计
Base Path:/tradeMarket/tradeOrder
权限:tradeMarket:tradeOrder:list|query|add|edit|remove|pay
响应:AjaxResult / TableDataInfo
/supplierEarTags 须在 /{id} 之前注册。
| 说明 |
HTTP |
URI |
权限 |
| 分页列表 |
GET |
/list |
list |
| 详情 |
GET |
/{id} |
query |
| 新增 |
POST |
/ |
add |
| 修改 |
PUT |
/ |
edit |
| 删除 |
DELETE |
/{ids} |
remove |
| 耳标候选 |
GET |
/supplierEarTags |
list 或 query |
| 完成支付 |
PUT |
/completePay |
pay |
6.1 列表 GET /list
| 参数 |
类型 |
说明 |
pageNum / pageSize |
int |
默认 1 / 20 |
orderNo |
string |
模糊 |
supplierId |
long |
精确 |
distributorId |
long |
精确 |
- 条件:
del_flag=0;排序 create_time DESC, id DESC。
- 行字段:§4 所列 +
lineSummary(可选,首耳标+等 N 头)。
6.2 详情 GET /{id}
data:主表 + lineList + 双方名称 + 状态中文。
6.3 新增 POST /
| Body |
必填 |
说明 |
supplierId |
Y |
|
distributorId |
Y |
|
lineList |
Y |
≥1 行 |
totalHeads / totalAmount |
N |
服务端覆盖 |
remark |
N |
|
- 生成
orderNo;order_status=0;写主表+明细。
6.4 修改 PUT /
id 必填;仅 order_status=0;Body 同新增;orderNo 不可改。
6.5 删除 DELETE /{ids}
6.6 耳标候选 GET /supplierEarTags
data:string[];仅按 supplierId 查询 biz_market_entry_ear_tag(distinct ear_tag),不剔除巡查病死、不剔除他单占用。
- 保存时服务端仍执行 (M−B)−C(
TradeOrderValidation / listEligible)。
6.7 完成支付 PUT /completePay
| Body |
必填 |
说明 |
id |
Y |
订单主键 |
payVoucherUrl |
Y |
先 /common/upload |
payVoucherPath |
Y |
|
- 成功:
code=200;订单已完成;结算单已生成(响应可带 settlementId / settlementNo 便于跳转)。
- 失败:订单仍为待支付。
6.8 主数据联调(前端)
| 用途 |
接口 |
| 供应商搜索 |
GET /tradeMarket/supplier/list(supplierName 模糊) |
| 承销商搜索 |
GET /tradeMarket/distributor/list(distributorName 模糊) |
| 凭证上传 |
POST /common/upload |
7. 关联方案:供应商结算管理
结算模块完整设计见 ../供应商结算管理/供应商结算管理技术方案.md(v1.0)。
7.1 模块边界
| 能力 |
交易订单 |
供应商结算 |
| 表 |
biz_trade_order(+line) |
biz_supplier_settlement |
| 创建结算单 |
completePay 写入 |
— |
| 列表/完成结算/打印凭证 |
— |
独立 Controller |
| 删除订单 |
仅待支付 |
不删结算单 |
7.2 结算模块接口(建议,供联调)
Base Path:/tradeMarket/supplierSettlement
权限:tradeMarket:supplierSettlement:list|query|settle
| 说明 |
HTTP |
URI |
| 列表 |
GET |
/list |
| 详情 |
GET |
/{id} |
| 完成结算 |
PUT |
/completeSettle |
| 打印凭证 |
前端 |
详情数据 |
列表参数:settlementNo、orderNo 模糊;del_flag=0;排序 create_time DESC。
完成结算 Body:id、settleVoucherUrl、settleVoucherPath(规则同订单支付凭证)→ settlement_status=1,settle_finish_time=now()。
7.3 状态与一致性
订单 order_status: 0 → 1
结算 settlement_status: (无) → 0 → 1
uk_order_id 保证一笔订单一条结算单。
- 订单已完成后不回写订单状态。
7.4 打印(前端)
| 功能 |
数据来源 |
| 打印小票 |
GET /tradeOrder/{id},orderStatus=0 |
| 打印凭证(订单) |
同上,orderStatus=1,含 payVoucherUrl |
| 打印凭证(结算) |
GET /supplierSettlement/{id} |
8. 菜单与权限(示例)
| 类型 |
名称 |
权限标识 |
| 菜单 |
交易订单管理 |
tradeMarket:tradeOrder:list |
| 按钮 |
查询 |
tradeMarket:tradeOrder:query |
| 按钮 |
新增 |
tradeMarket:tradeOrder:add |
| 按钮 |
修改 |
tradeMarket:tradeOrder:edit |
| 按钮 |
删除 |
tradeMarket:tradeOrder:remove |
| 按钮 |
完成支付 |
tradeMarket:tradeOrder:pay |
| 菜单 |
供应商结算管理 |
tradeMarket:supplierSettlement:list |
组件路径:tradeMarket/tradeOrder/index、tradeMarket/supplierSettlement/index。
9. 交付清单
10. 修订记录
| 版本 |
说明 |
| 1.0 |
初稿:订单主从表、完成支付生成结算、耳标 M−B−C、接口与结算关联方案 |
| 1.1 |
biz_trade_order_line:weight_kg→weight,amount_yuan→amount |
| 1.2 |
biz_trade_order_line 增加 supplier_id |
| 1.3 |
完成支付后按 supplier_id+ear_tag 删除 biz_market_entry_ear_tag |
| 1.4 |
与实现对齐:/supplierEarTags 仅子表;§5.2 下拉/保存分流;功能需求 v1.2 |
| 1.5 |
§7 引用《供应商结算管理技术方案》v1.0 |
| 1.6 |
biz_trade_order_line.grade_code;TradeOrderGradeResolver + biz_grade_weight_config |
| 1.7 |
biz_trade_order_line.grade_name;保存时与 grade_code 一并写入 |