diff --git a/doc/ShopEntity_Mode运行模式处理逻辑文档.md b/doc/ShopEntity_Mode运行模式处理逻辑文档.md new file mode 100644 index 0000000..f96583f --- /dev/null +++ b/doc/ShopEntity_Mode运行模式处理逻辑文档.md @@ -0,0 +1,247 @@ +# 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 smartCabinetQueryWrapper = new QueryWrapper<>(); + smartCabinetQueryWrapper.eq("shop_id", command.getShopId()) + .eq("deleted", false); + List cabinetEntities = smartCabinetService.list(smartCabinetQueryWrapper); + + if (cabinetEntities != null && !cabinetEntities.isEmpty()) { + QueryWrapper cabinetCellQueryWrapper = new QueryWrapper<>(); + cabinetCellQueryWrapper.in("cabinet_id", cabinetEntities.stream() + .map(SmartCabinetEntity::getCabinetId) + .collect(Collectors.toList())) + .isNotNull("goods_id") + .eq("deleted", false); + List 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 getShopListByCorpid(String corpid, Long mode) { + QueryWrapper 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 \ No newline at end of file