From b1fd515fb3208708aa03f94f17a9f8e8c0a8999d Mon Sep 17 00:00:00 2001 From: irving Date: Sat, 1 Nov 2025 15:07:59 -0400 Subject: [PATCH] =?UTF-8?q?feat(order):=20=E6=96=B0=E5=A2=9E=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=AB=AF=E5=AE=8C=E6=88=90=E8=AE=A2=E5=8D=95=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=EF=BC=88=E5=BA=97=E5=91=98=E7=AB=AF=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OrderConstant 新增角色 GROUP_LEADER、触发源 WX_CLERK_MGMT;补充映射 - IPlayOrderInfoService 新增 completeOrderByManagement 方法 - PlayOrderInfoServiceImpl:校验权限(仅运营/组长,组长仅限本组);ACCEPTED 自动切换为 IN_PROGRESS 后完成;抽取 completeOrderInternal;完善 GROUP_LEADER 的 Actor/Source 映射 - WxClerkController 新增 POST /wx/clerk/order/complete 接口,支持备注参数 - 新增请求体 PlayOrderCompleteVo - 新增单测 PlayOrderInfoServiceImplTest 覆盖核心流程与边界 --- .../order/module/constant/OrderConstant.java | 8 +- .../order/module/vo/PlayOrderCompleteVo.java | 20 +++ .../order/service/IPlayOrderInfoService.java | 10 ++ .../impl/OrderLifecycleServiceImpl.java | 2 + .../impl/PlayOrderInfoServiceImpl.java | 140 ++++++++++++++--- .../weichat/controller/WxClerkController.java | 33 ++++ .../impl/PlayOrderInfoServiceImplTest.java | 144 ++++++++++++++++++ 7 files changed, 331 insertions(+), 26 deletions(-) create mode 100644 play-admin/src/main/java/com/starry/admin/modules/order/module/vo/PlayOrderCompleteVo.java create mode 100644 play-admin/src/test/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImplTest.java diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderConstant.java b/play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderConstant.java index 580af5c..e2b42f6 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderConstant.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/module/constant/OrderConstant.java @@ -214,7 +214,8 @@ public class OrderConstant { public enum OperatorType { CUSTOMER("0", "顾客"), CLERK("1", "店员"), - ADMIN("2", "管理员"); + ADMIN("2", "管理员"), + GROUP_LEADER("3", "组长"); private final String code; private final String description; @@ -238,6 +239,7 @@ public class OrderConstant { public enum OrderActor { CUSTOMER, CLERK, + GROUP_LEADER, ADMIN, SYSTEM; } @@ -461,6 +463,10 @@ public class OrderConstant { * 微信店员端操作触发 */ WX_CLERK("wx_clerk"), + /** + * 微信店员端管理能力触发(组长/运营) + */ + WX_CLERK_MGMT("wx_clerk_mgmt"), /** * 管理后台控制台界面发起 */ diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/module/vo/PlayOrderCompleteVo.java b/play-admin/src/main/java/com/starry/admin/modules/order/module/vo/PlayOrderCompleteVo.java new file mode 100644 index 0000000..4206d27 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/order/module/vo/PlayOrderCompleteVo.java @@ -0,0 +1,20 @@ +package com.starry.admin.modules.order.module.vo; + +import java.io.Serializable; +import javax.validation.constraints.NotBlank; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 店员端完成订单请求体。 + */ +@Data +@Accessors(chain = true) +public class PlayOrderCompleteVo implements Serializable { + + @NotBlank(message = "订单ID不能为空") + private String orderId; + + /** 可选备注信息,记录在生命周期日志中。 */ + private String remark; +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/service/IPlayOrderInfoService.java b/play-admin/src/main/java/com/starry/admin/modules/order/service/IPlayOrderInfoService.java index e9488bc..503d25b 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/service/IPlayOrderInfoService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/service/IPlayOrderInfoService.java @@ -219,6 +219,16 @@ public interface IPlayOrderInfoService extends IService { **/ void updateStateTo23(String operatorByType, String operatorBy, String orderState, String orderId); + /** + * 管理端完成订单(运营/组长) + * + * @param operatorByType 操作人类型 + * @param operatorBy 操作人标识 + * @param orderId 订单ID + * @param remark 操作备注 + */ + void completeOrderByManagement(String operatorByType, String operatorBy, String orderId, String remark); + /** * 修改订单状态为取消订单 管理员、店员、顾客均可操作 * diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java index 841897e..b97a72c 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java @@ -792,6 +792,8 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService { return OrderActor.CUSTOMER; case CLERK: return OrderActor.CLERK; + case GROUP_LEADER: + return OrderActor.GROUP_LEADER; case ADMIN: return OrderActor.ADMIN; default: diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java index e13d3e7..c52a6fa 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/PlayOrderInfoServiceImpl.java @@ -39,6 +39,7 @@ import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService; import com.starry.admin.modules.order.service.IPlayOrderInfoService; import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService; import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator; +import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity; import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService; import com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo; import com.starry.admin.modules.shop.service.IPlayCouponDetailsService; @@ -726,33 +727,25 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl captor = ArgumentCaptor.forClass(OrderCompletionContext.class); + verify(orderLifecycleService).completeOrder(eq(order.getId()), captor.capture()); + OrderCompletionContext context = captor.getValue(); + assertEquals(OrderActor.ADMIN, context.getOperatorActor()); + assertEquals("sys-admin", context.getOperatorId()); + assertEquals(OrderTriggerSource.WX_CLERK_MGMT, context.getTriggerSource()); + assertEquals(remark, context.getComment()); + } + + @Test + void completeOrderByManagement_deniesLeaderOutsideGroup() { + PlayOrderInfoEntity order = new PlayOrderInfoEntity(); + order.setId("order-2"); + order.setOrderStatus(OrderStatus.IN_PROGRESS.getCode()); + order.setGroupId("group-A"); + when(baseMapper.selectById("order-2")).thenReturn(order); + + PlayClerkUserInfoEntity leader = new PlayClerkUserInfoEntity(); + leader.setId("leader-clerk"); + leader.setSysUserId("leader-sys"); + when(playClerkUserInfoService.getById("leader-clerk")).thenReturn(leader); + + PlayPersonnelGroupInfoEntity leaderGroup = new PlayPersonnelGroupInfoEntity(); + leaderGroup.setId("group-B"); + when(playClerkGroupInfoService.selectByUserId("leader-sys")).thenReturn(leaderGroup); + + assertThrows( + CustomException.class, + () -> service.completeOrderByManagement( + OperatorType.GROUP_LEADER.getCode(), "leader-clerk", order.getId(), null)); + verify(orderLifecycleService, never()).completeOrder(eq(order.getId()), any()); + } + + @Test + void completeOrderByManagement_allowsLeaderInGroup() { + PlayOrderInfoEntity order = new PlayOrderInfoEntity(); + order.setId("order-3"); + order.setOrderStatus(OrderStatus.IN_PROGRESS.getCode()); + order.setGroupId("group-C"); + order.setAcceptBy("clerk-accepted"); + when(baseMapper.selectById("order-3")).thenReturn(order); + + PlayClerkUserInfoEntity leader = new PlayClerkUserInfoEntity(); + leader.setId("leader-clerk"); + leader.setSysUserId("leader-sys"); + when(playClerkUserInfoService.getById("leader-clerk")).thenReturn(leader); + + PlayPersonnelGroupInfoEntity leaderGroup = new PlayPersonnelGroupInfoEntity(); + leaderGroup.setId("group-C"); + when(playClerkGroupInfoService.selectByUserId("leader-sys")).thenReturn(leaderGroup); + + service.completeOrderByManagement( + OperatorType.GROUP_LEADER.getCode(), "leader-clerk", order.getId(), null); + + ArgumentCaptor captor = ArgumentCaptor.forClass(OrderCompletionContext.class); + verify(orderLifecycleService).completeOrder(eq(order.getId()), captor.capture()); + OrderCompletionContext context = captor.getValue(); + assertEquals(OrderActor.GROUP_LEADER, context.getOperatorActor()); + assertEquals("leader-clerk", context.getOperatorId()); + assertEquals(OrderTriggerSource.WX_CLERK_MGMT, context.getTriggerSource()); + assertNull(context.getComment()); + } + + @Test + void completeOrderByManagement_promotesAcceptedToInProgress() { + PlayOrderInfoEntity accepted = new PlayOrderInfoEntity(); + accepted.setId("order-4"); + accepted.setOrderStatus(OrderStatus.ACCEPTED.getCode()); + PlayOrderInfoEntity inProgress = new PlayOrderInfoEntity(); + inProgress.setId("order-4"); + inProgress.setOrderStatus(OrderStatus.IN_PROGRESS.getCode()); + when(baseMapper.selectById("order-4")).thenReturn(accepted, inProgress); + + service.completeOrderByManagement(OperatorType.ADMIN.getCode(), "sys-admin", accepted.getId(), null); + + ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(PlayOrderInfoEntity.class); + verify(baseMapper).updateById(updateCaptor.capture()); + PlayOrderInfoEntity updateEntity = updateCaptor.getValue(); + assertEquals(OrderStatus.IN_PROGRESS.getCode(), updateEntity.getOrderStatus()); + + ArgumentCaptor completionCaptor = ArgumentCaptor.forClass(OrderCompletionContext.class); + verify(orderLifecycleService).completeOrder(eq(accepted.getId()), completionCaptor.capture()); + assertEquals(OrderTriggerSource.WX_CLERK_MGMT, completionCaptor.getValue().getTriggerSource()); + } +}