shop-back-end/doc/ShopEntity_Mode运行模式处理逻辑文档.md

247 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ShopEntity Mode 运行模式处理逻辑文档
## 1. 概述
ShopEntity 的 `mode` 字段定义了商店的运行模式,影响订单创建、支付、审批、库存管理等多个业务环节。本文档详细说明 `mode` 字段的定义、各模式的处理逻辑以及在项目中的具体实现。
## 2. Mode 字段定义
### 2.1 数据库定义
- **字段名**: `mode`
- **类型**: `INTEGER`
- **位置**: `agileboot-domain/src/main/java/com/agileboot/domain/shop/shop/db/ShopEntity.java:46-48`
### 2.2 模式枚举
```java
// 运行模式定义
0: 支付模式 (Payment Mode)
1: 审批模式 (Approval Mode)
2: 借还模式 (Borrow Return Mode)
3: 会员模式 (Membership Mode)
4: 耗材模式 (Consumable Mode)
```
### 2.3 相关DTO/Command定义
- **ShopDTO**: 包含 `mode` 字段用于API响应 (`agileboot-domain/src/main/java/com/agileboot/domain/shop/shop/dto/ShopDTO.java:35-36`)
- **SubmitOrderCommand**: 包含 `mode` 字段用于订单提交 (`agileboot-domain/src/main/java/com/agileboot/domain/shop/order/command/SubmitOrderCommand.java:34-35`)
- **SearchShopQuery**: 包含 `mode` 字段用于商店查询 (`agileboot-domain/src/main/java/com/agileboot/domain/shop/shop/query/SearchShopQuery.java:20-21`)
## 3. 核心处理逻辑
### 3.1 ShopApplicationService - 商店模式变更校验
**文件位置**: `agileboot-domain/src/main/java/com/agileboot/domain/shop/shop/ShopApplicationService.java:72-92`
**关键逻辑**: 当商店模式从其他模式变更为 **会员模式 (3)** 时,系统会执行严格的校验:
```java
if (command.getMode() != null && !command.getMode().equals(model.getMode())) {
if (command.getMode().equals(3)) {
// 检查该商店下是否存在已绑定商品的柜子
QueryWrapper<SmartCabinetEntity> smartCabinetQueryWrapper = new QueryWrapper<>();
smartCabinetQueryWrapper.eq("shop_id", command.getShopId())
.eq("deleted", false);
List<SmartCabinetEntity> cabinetEntities = smartCabinetService.list(smartCabinetQueryWrapper);
if (cabinetEntities != null && !cabinetEntities.isEmpty()) {
QueryWrapper<CabinetCellEntity> cabinetCellQueryWrapper = new QueryWrapper<>();
cabinetCellQueryWrapper.in("cabinet_id", cabinetEntities.stream()
.map(SmartCabinetEntity::getCabinetId)
.collect(Collectors.toList()))
.isNotNull("goods_id")
.eq("deleted", false);
List<CabinetCellEntity> cells = cabinetCellService.list(cabinetCellQueryWrapper);
if (cells != null && !cells.isEmpty()) {
throw new RuntimeException("该商店下存在已绑定商品的柜子,请先解绑商品后再修改模式");
}
}
}
}
```
**业务含义**: 会员模式下,柜子格口用于会员租赁,不允许绑定具体商品。变更前需确保所有柜子格口未绑定商品。
### 3.2 OrderApplicationService - 订单创建与模式处理
**文件位置**: `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/OrderApplicationService.java`
#### 3.2.1 订单创建时的模式设置
- **行 222**: `orderModel.setMode(command.getMode());` - 将命令中的模式设置到订单模型
- **行 408-419**: `processOrderGoods` 方法中的模式相关逻辑:
```java
if (orderModel.getMode() != null && orderModel.getMode().equals(3)) {
// 会员模式:使用柜子单元价格,商品名称为"柜子名称+格口+格口号"
goodsModel.setPrice(cabinetCellEntity.getCellPrice());
goodsModel.setTotalAmount(cabinetCellEntity.getCellPrice());
goodsModel.setGoodsName(cabinetEntity.getCabinetName() + "格口" + cabinetCellEntity.getCellNo());
} else {
// 其他模式:计算商品金额并验证库存
goodsModel.calculateTotal();
goodsModel.validateQuantity();
if (cabinetCellEntity == null || cabinetCellEntity.getStock() < goodsModel.getQuantity()) {
throw new ApiException(ErrorCode.FAILED, "柜子库存不足");
}
}
```
**业务差异**:
- **会员模式**: 商品价格 = 柜子格口价格,商品名称 = 柜子名称 + 格口号
- **其他模式**: 商品价格 = 商品单价 × 数量,需验证库存充足性
### 3.3 OrderModel - 支付成功处理
**文件位置**: `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/model/OrderModel.java:103-115`
**关键逻辑**: `handlePaymentSuccess` 方法中的模式分支处理:
```java
if (this.getMode() != null && this.getMode().equals(3)) {
for (ShopOrderGoodsEntity orderGoods : orderGoodsList) {
CabinetCellEntity cell = cabinetCellService.getById(orderGoods.getCellId());
cell.setIsRented(1); // 设置为已租用状态
cell.updateById();
}
} else {
orderGoodsList.forEach(orderGoods -> {
// 扣减商品库存
deductGoodsStock(orderGoods.getGoodsId(), orderGoods.getQuantity(), orderGoods.getCellId());
});
}
```
**业务差异**:
- **会员模式**: 更新柜子格口的 `isRented` 状态为 1已租用
- **其他模式**: 扣减商品库存和柜子格口库存
### 3.4 ReturnApprovalApplicationService - 退货审批处理
**文件位置**: `agileboot-domain/src/main/java/com/agileboot/domain/shop/approval/ReturnApprovalApplicationService.java`
#### 3.4.1 审批通过后的库存处理
- **行 349-364**: 根据订单商品模式进行不同的库存/状态恢复:
```java
if (orderGoodsModel.getMode() != null && orderGoodsModel.getMode().equals(3)) {
// 会员模式:更新柜子格口租赁状态为未租用
CabinetCellEntity cell = cabinetCellService.getById(orderGoodsModel.getCellId());
cell.setIsRented(0);
cell.updateById();
} else {
// 其他模式:恢复商品库存和格口库存
GoodsModel goodsModel = goodsModelFactory.loadById(orderGoodsModel.getGoodsId());
goodsModel.setStock(goodsModel.getStock() + orderGoodsModel.getQuantity());
goodsModel.updateById();
CabinetCellModel cabinetCellModel = cabinetCellModelFactory.loadById(orderGoodsModel.getCellId());
cabinetCellModel.setStock(cabinetCellModel.getStock() + orderGoodsModel.getQuantity());
cabinetCellModel.updateById();
}
```
#### 3.4.2 自动审批逻辑
- **行 830-850**: 提交审批申请时的自动审批判断:
```java
if (orderGoods.getMode() != null && orderGoods.getMode().equals(3)) {
// 会员模式:自动审批
UpdateReturnApprovalCommand approveCommand = new UpdateReturnApprovalCommand();
// ... 设置审批参数
approveApproval(approveCommand);
} else {
// 其他模式:根据商品 autoApproval 字段决定是否自动审批
GoodsModel goodsModel = goodsModelFactory.loadById(orderGoods.getGoodsId());
if (goodsModel.getAutoApproval().equals(1)) {
// 自动审批
// ... 设置审批参数
approveApproval(approveCommand);
}
}
```
#### 3.4.3 通知消息差异化
- **行 864-868**: 发送审批通知时的描述差异:
```java
if (orderGoods.getMode() != null && orderGoods.getMode().equals(3)) {
article.setDescription("退还格口:" + orderGoods.getGoodsName());
} else {
article.setDescription("退还商品:" + orderGoods.getGoodsName());
}
```
### 3.5 ShopService - 模式查询逻辑
**文件位置**: `agileboot-domain/src/main/java/com/agileboot/domain/shop/shop/db/ShopServiceImpl.java:52-59`
**关键方法**: `getShopListByCorpid` - 获取指定企业下非特定模式的商店列表
```java
public List<ShopEntity> getShopListByCorpid(String corpid, Long mode) {
QueryWrapper<ShopEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("corpid", corpid)
.ne("mode", mode) // 排除指定模式
.eq("deleted", 0);
return this.list(queryWrapper);
}
```
**业务用途**: 获取企业微信下除了指定模式外的其他商店,用于模式切换时的校验或展示。
## 4. 各模式业务特性总结
| 模式 | 代码 | 核心特性 | 库存管理 | 价格计算 | 审批逻辑 |
|------|------|----------|----------|----------|----------|
| **支付模式** | 0 | 即时支付,商品交易 | 扣减商品库存 | 商品单价 × 数量 | 不涉及审批 |
| **审批模式** | 1 | 需上级审批通过 | 审批后扣减库存 | 商品单价 × 数量 | 人工审批流程 |
| **借还模式** | 2 | 借用归还机制 | 借用时扣减,归还时恢复 | 可能涉及租赁费用 | 借用审批 |
| **会员模式** | 3 | 柜子格口租赁 | 更新格口租赁状态 | 格口租赁价格 | 自动审批 |
| **耗材模式** | 4 | 消耗品领用 | 扣减库存 | 商品单价 × 数量 | 根据商品 autoApproval 配置 |
## 5. 关键业务规则
### 5.1 模式变更限制
- 变更为 **会员模式 (3)** 前,必须确保商店下所有柜子格口未绑定商品
- 模式变更校验仅针对变更为会员模式的情况
### 5.2 库存管理差异
- **会员模式**: 管理格口租赁状态 (`isRented`),不涉及商品库存扣减
- **其他模式**: 管理商品库存和格口库存,需确保库存充足
### 5.3 审批自动化
- **会员模式**: 退货申请自动审批
- **其他模式**: 根据商品的 `autoApproval` 配置决定是否自动审批
### 5.4 价格计算
- **会员模式**: 价格 = 柜子格口价格 (`cellPrice`)
- **其他模式**: 价格 = 商品单价 × 数量
## 6. 代码调用关系
```
1. 前端提交订单
2. SubmitOrderCommand (包含 mode)
3. OrderApplicationService.createOrder()
4. OrderModel.setMode() / processOrderGoods()
5. 支付成功 → OrderModel.handlePaymentSuccess()
6. 根据 mode 执行不同业务逻辑:
- mode=3: 更新格口租赁状态
- 其他: 扣减库存
```
## 7. 注意事项
1. **模式一致性**: 订单中的 `mode` 必须与创建时商店的 `mode` 保持一致
2. **数据完整性**: 会员模式下,商品名称和价格来自柜子格口信息,需确保格口数据完整
3. **状态管理**: 会员模式的格口租赁状态 (`isRented`) 需与订单状态同步更新
4. **审批流程**: 不同模式对应不同的审批策略,需根据业务场景正确配置
---
**文档版本**: 1.0
**最后更新**: 2025-12-15
**相关文件**: ShopEntity.java, ShopApplicationService.java, OrderApplicationService.java, ReturnApprovalApplicationService.java