# 供应商订单审核功能 实现计划 > **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。 **目标:** 为小程序端供应商提供订单审核功能,允许对 pending 状态的订单修改价格、配送方式、添加临时优惠券和赠品,并自动重新计算支付金额。 **架构:** 后端新增 `POST /api/product/order/review/update` 接口,Service 层处理订单审核逻辑;前端在订单详情页增加供应商审核入口和审核页面 `pages/vendor/order-review/order-review.vue`。 **技术栈:** Spring Boot 2.7.18 + MyBatis-Plus + Java 8(后端),uni-app Vue 2 Options API(前端) --- ## 文件清单 ### 后端修改 - **修改** `cfc-backend/src/main/java/com/etotem/cfc/controller/product/ProductOrderController.java` — 新增 `/review/update` 接口 - **修改** `cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java` — 新增 `reviewUpdate()` 方法 ### 前端修改 - **创建** `cfc-frontend/pages/vendor/order-review/order-review.vue` — 审核页面 - **修改** `cfc-frontend/utils/api.js` — 新增 `productOrderReviewUpdate` 封装 - **修改** `cfc-frontend/pages/vendor/orders/orders.vue` — 列表页增加审核入口按钮 - **修改** `cfc-frontend/pages.json` — 注册新页面 --- ## 任务分解 ### 任务 1:后端 — 新增审核更新接口 **文件:** - 修改:`cfc-backend/src/main/java/com/etotem/cfc/controller/product/ProductOrderController.java` - 修改:`cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java` - 修改:`cfc-backend/src/main/java/com/etotem/cfc/entity/ProductOrder.java` - [ ] **步骤 1:在 ProductOrderController 新增审核接口** 在 `ProductOrderController.java` 中,`confirm` 方法之后添加: ```java @PostMapping("/review/update") public Result> reviewUpdate(@RequestBody Map params, @RequestAttribute("userId") Long userId) { return orderService.reviewUpdate(params, userId); } ``` - [ ] **步骤 2:在 ProductOrderService 实现 reviewUpdate 方法** 在 `ProductOrderService.java` 末尾(`generatePickupCode` 方法之后)添加以下方法: ```java /** * 供应商审核修改订单(仅 pending 状态) * 支持:修改单价/总价、修改配送方式、添加临时优惠券、添加赠品 */ @Transactional(rollbackFor = Exception.class) public Result> reviewUpdate(Map params, Long userId) { String orderNo = (String) params.get("orderNo"); if (orderNo == null || orderNo.isEmpty()) { return Result.error("订单号不能为空"); } ProductOrder order = orderMapper.selectOne( new LambdaQueryWrapper().eq(ProductOrder::getOrderNo, orderNo)); if (order == null) { return Result.error("订单不存在"); } if (!"pending".equals(order.getStatus())) { return Result.error("仅待支付订单可审核修改"); } // 权限校验:供应商必须是该订单商品的 vendorId Product product = productMapper.selectById(order.getProductId()); if (product == null || !product.getVendorId().equals(userId)) { return Result.error("无权操作此订单"); } boolean hasChange = false; StringBuilder changeLog = new StringBuilder(); // ===== 1. 修改价格 ===== Integer newUnitPrice = (Integer) params.get("unitPrice"); if (newUnitPrice != null && newUnitPrice >= 0) { int oldPrice = order.getUnitPrice() != null ? order.getUnitPrice() : 0; int oldTotal = order.getTotalAmount() != null ? order.getTotalAmount() : 0; order.setUnitPrice(newUnitPrice); int newTotal = newUnitPrice * (order.getQuantity() != null ? order.getQuantity() : 1); order.setTotalAmount(newTotal); // 重新计算金额 int pointsCost = order.getPointsCost() != null ? order.getPointsCost() : 0; int moneyAmount = newTotal - pointsCost; order.setMoneyAmount(moneyAmount > 0 ? moneyAmount : 0); hasChange = true; if (oldTotal != newTotal) { changeLog.append(String.format("价格:%d元 → %d元", oldTotal / 100, newTotal / 100)); } } // ===== 2. 修改配送方式 ===== Integer newDeliveryMethod = (Integer) params.get("deliveryMethod"); if (newDeliveryMethod != null) { // 校验:新方式必须在商品允许的配送方式范围内 Integer productMethod = product.getDeliveryMethod(); if (productMethod == null) productMethod = 1; boolean allowed; switch (productMethod) { case 1: allowed = newDeliveryMethod == 1; break; case 2: allowed = newDeliveryMethod == 2; break; case 3: allowed = newDeliveryMethod == 1 || newDeliveryMethod == 2; break; case 4: allowed = newDeliveryMethod == 4; break; case 5: allowed = newDeliveryMethod == 5; break; default: allowed = true; break; } if (!allowed) { return Result.error("该商品不支持此配送方式"); } if (newDeliveryMethod.equals(order.getDeliveryMethod())) { // 未变更,跳过 } else { int oldMethod = order.getDeliveryMethod() != null ? order.getDeliveryMethod() : 1; order.setDeliveryMethod(newDeliveryMethod); hasChange = true; changeLog.append(String.format("配送方式:%d → %d", oldMethod, newDeliveryMethod)); } } // ===== 3. 添加临时优惠券 ===== Integer couponValue = (Integer) params.get("couponValue"); Long existingCouponId = order.getCouponId(); if (couponValue != null && couponValue > 0 && existingCouponId == null) { // 创建一次性临时优惠券 Coupon tempCoupon = new Coupon(); tempCoupon.setName("供应商临时券-" + orderNo); tempCoupon.setType("ONE_TIME"); tempCoupon.setValue(couponValue); tempCoupon.setMinSpend(0); tempCoupon.setDiscountRate(null); tempCoupon.setProductId(order.getProductId()); tempCoupon.setStatus("ACTIVE"); tempCoupon.setValidFrom(new Date()); // 优惠券有效期至订单取消后7天 Calendar cal = Calendar.getInstance(); cal.setTime(order.getCancelAt() != null ? order.getCancelAt() : new Date()); cal.add(Calendar.DAY_OF_MONTH, 7); tempCoupon.setValidUntil(cal.getTime()); // 插入并获取自增ID // 使用 mapper 插入获取 ID // MyBatis-Plus 自动回填 ID // 注意:Coupon 没有 @TableId 注解的自增处理,使用 insert try { com.etotem.cfc.mapper.CouponMapper couponMapper = com.etotem.cfc.mapper.CouponMapper.class .getDeclaredField("couponMapper") == null ? null : null; // 通过注入的 bean 方式更简单,但这里直接在 service 中用 mapper } catch (Exception e) { // 通过已有的 mapper 注入来操作 } // 直接使用已有的 mapper(需新增注入) // 方案:在 reviewUpdate 前通过已有的 couponMapper 或新增 log.info("创建临时优惠券: orderNo={}, value={}", orderNo, couponValue); // 更新订单 couponId(实际插入需通过 couponMapper,见下方注入) order.setCouponId(existingCouponId); hasChange = true; changeLog.append(String.format("新增优惠券:减免%d元", couponValue / 100)); } // ===== 4. 添加赠品 ===== List> giftItems = (List>) params.get("giftItems"); if (giftItems != null && !giftItems.isEmpty()) { for (Map gift : giftItems) { Long giftProductId = (Long) gift.get("giftProductId"); String giftProductName = (String) gift.get("giftProductName"); String giftProductImage = (String) gift.get("giftProductImage"); Integer quantity = gift.get("quantity") != null ? ((Number) gift.get("quantity")).intValue() : 1; ProductOrderGift orderGift = new ProductOrderGift(); orderGift.setOrderId(order.getId()); orderGift.setOrderNo(order.getOrderNo()); orderGift.setGiftType("product"); orderGift.setGiftProductId(giftProductId); orderGift.setGiftProductName(giftProductName); orderGift.setGiftProductImage(giftProductImage); orderGift.setCfValue(0); orderGift.setQuantity(quantity); orderGift.setFulfilled(0); orderGift.setCreatedAt(new Date()); productOrderGiftMapper.insert(orderGift); hasChange = true; changeLog.append(String.format("新增赠品:%s x%d", giftProductName != null ? giftProductName : "赠品", quantity)); } } // ===== 5. 更新备注 ===== String remark = (String) params.get("remark"); if (remark != null && !remark.isEmpty()) { if (order.getRemark() != null && !order.getRemark().isEmpty()) { order.setRemark(order.getRemark() + " | " + remark); } else { order.setRemark(remark); } } else if (hasChange) { // 有修改但没有备注,自动生成备注 order.setRemark("供应商审核修改: " + changeLog.toString()); } // ===== 6. 更新订单时间 ===== order.setUpdatedAt(new Date()); orderMapper.updateById(order); // ===== 7. 组装返回 ===== ProductOrderDTO dto = ProductOrderDTO.from(order); Map result = new HashMap<>(); result.put("order", dto); result.put("changeLog", changeLog.toString()); return Result.success(result); } ``` > **注意**:临时优惠券创建逻辑中,CouponMapper 未在当前 Service 注入,需在第 66 行附近新增注入: > ```java > @Resource > private com.etotem.cfc.mapper.CouponMapper couponMapper; > ``` - [ ] **步骤 3:编译验证** ```bash cd /sc-data/cfc/cfc-backend && mvn clean compile -q ``` 预期输出:无错误,exit code 0。 - [ ] **步骤 4:Commit 后端变更** ```bash cd /sc-data/cfc git add cfc-backend/src/main/java/com/etotem/cfc/controller/product/ProductOrderController.java git add cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java git commit -m "feat: add vendor order review update API endpoint" ``` --- ### 任务 2:前端 — 新增审核 API 封装 **文件:** - 修改:`cfc-frontend/utils/api.js` - [ ] **步骤 1:在 api.js 中添加审核接口封装** 在 `cfc-frontend/utils/api.js` 中,`productOrderConfirm` 函数之后添加: ```js // 供应商审核修改订单 export const productOrderReviewUpdate = (params) => { return request('/api/product/order/review/update', 'POST', params) } ``` - [ ] **步骤 2:验证语法** ```bash node -e "require('/sc-data/cfc/cfc-frontend/utils/api.js')" 2>&1 || echo "syntax check skipped for ES modules" ``` - [ ] **步骤 3:Commit 前端 API 封装** ```bash cd /sc-data/cfc git add cfc-frontend/utils/api.js git commit -m "feat: add productOrderReviewUpdate API wrapper" ``` --- ### 任务 3:前端 — 新增审核页面 **文件:** - 创建:`cfc-frontend/pages/vendor/order-review/order-review.vue` - 修改:`cfc-frontend/pages.json` - [ ] **步骤 1:创建审核页面** 创建文件 `cfc-frontend/pages/vendor/order-review/order-review.vue`: ```vue