10 KiB
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 模式枚举
// 运行模式定义
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) 时,系统会执行严格的校验:
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方法中的模式相关逻辑:
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 方法中的模式分支处理:
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: 根据订单商品模式进行不同的库存/状态恢复:
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: 提交审批申请时的自动审批判断:
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: 发送审批通知时的描述差异:
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 - 获取指定企业下非特定模式的商店列表
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. 注意事项
- 模式一致性: 订单中的
mode必须与创建时商店的mode保持一致 - 数据完整性: 会员模式下,商品名称和价格来自柜子格口信息,需确保格口数据完整
- 状态管理: 会员模式的格口租赁状态 (
isRented) 需与订单状态同步更新 - 审批流程: 不同模式对应不同的审批策略,需根据业务场景正确配置
文档版本: 1.0 最后更新: 2025-12-15 相关文件: ShopEntity.java, ShopApplicationService.java, OrderApplicationService.java, ReturnApprovalApplicationService.java