fix: allow manager cancellation of random orders
This commit is contained in:
@@ -44,7 +44,9 @@ 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.module.entity.PlayPersonnelAdminInfoEntity;
|
||||
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||
import com.starry.admin.modules.personnel.service.IPlayPersonnelAdminInfoService;
|
||||
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||
import com.starry.admin.modules.weichat.entity.order.*;
|
||||
@@ -98,6 +100,9 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
@Resource
|
||||
private IPlayPersonnelGroupInfoService playClerkGroupInfoService;
|
||||
|
||||
@Resource
|
||||
private IPlayPersonnelAdminInfoService playPersonnelAdminInfoService;
|
||||
|
||||
@Resource
|
||||
private IPlayCouponDetailsService playCouponDetailsService;
|
||||
|
||||
@@ -1057,7 +1062,9 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
throw new CustomException("只能操作本人订单");
|
||||
}
|
||||
if ("1".equals(operatorByType) && !operatorBy.equals(orderInfo.getAcceptBy())) {
|
||||
throw new CustomException("只能操作本人订单");
|
||||
if (!isClerkManagementOperator(operatorBy)) {
|
||||
throw new CustomException("只能操作本人订单");
|
||||
}
|
||||
}
|
||||
// 取消订单(必须订单未接单或者为开始状态)
|
||||
if (!orderInfo.getOrderStatus().equals(OrderStatus.PENDING.getCode())
|
||||
@@ -1079,6 +1086,22 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
notificationSender.sendOrderCancelMessageAsync(orderInfo, refundReason);
|
||||
}
|
||||
|
||||
private boolean isClerkManagementOperator(String clerkId) {
|
||||
if (StringUtils.isBlank(clerkId)) {
|
||||
return false;
|
||||
}
|
||||
PlayClerkUserInfoEntity clerkInfo = playClerkUserInfoService.selectById(clerkId);
|
||||
if (clerkInfo == null || StringUtils.isBlank(clerkInfo.getSysUserId())) {
|
||||
return false;
|
||||
}
|
||||
PlayPersonnelAdminInfoEntity adminInfo = playPersonnelAdminInfoService.selectByUserId(clerkInfo.getSysUserId());
|
||||
if (adminInfo != null) {
|
||||
return true;
|
||||
}
|
||||
PlayPersonnelGroupInfoEntity groupInfo = playClerkGroupInfoService.selectByUserId(clerkInfo.getSysUserId());
|
||||
return groupInfo != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 已接单/服务中的订单强制取消,仅允许店员本人或管理员操作
|
||||
*/
|
||||
@@ -1087,7 +1110,9 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, P
|
||||
public void forceCancelOngoingOrder(String operatorByType, String operatorBy, String orderId, BigDecimal refundAmount,
|
||||
String refundReason, List<String> images) {
|
||||
if (!"2".equals(operatorByType)) {
|
||||
throw new CustomException("禁止操作");
|
||||
if (!("1".equals(operatorByType) && isClerkManagementOperator(operatorBy))) {
|
||||
throw new CustomException("禁止操作");
|
||||
}
|
||||
}
|
||||
PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
|
||||
if (!OrderStatus.ACCEPTED.getCode().equals(orderInfo.getOrderStatus())
|
||||
|
||||
@@ -15,8 +15,11 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
||||
import com.starry.admin.common.task.OverdueOrderHandlerTask;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelAdminInfoEntity;
|
||||
import com.starry.admin.modules.personnel.service.IPlayPersonnelAdminInfoService;
|
||||
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
||||
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
||||
import com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo;
|
||||
@@ -39,6 +42,19 @@ import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport {
|
||||
|
||||
private static final String DEFAULT_WECHAT_CODE = "apitest-customer-wx";
|
||||
private static final String CANCEL_REASON = "API random cancel by role";
|
||||
private static final int CODE_OK = 200;
|
||||
private static final int CODE_FAIL = 500;
|
||||
private static final int SINGLE_QUANTITY = 1;
|
||||
private static final String CLERK_STATE_ACTIVE = "1";
|
||||
private static final String CLERK_ONLINE_ENABLED = "1";
|
||||
private static final String CLERK_LISTING_ENABLED = "1";
|
||||
private static final String CLERK_DISPLAY_ENABLED = "1";
|
||||
private static final String CLERK_RANDOM_ORDER_ENABLED = "1";
|
||||
private static final String CLERK_FIXING_LEVEL = "1";
|
||||
private static final String CLERK_ONBOARDING_STATE = "1";
|
||||
|
||||
@MockBean
|
||||
private NotificationSender notificationSender;
|
||||
|
||||
@@ -51,6 +67,9 @@ class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport {
|
||||
@org.springframework.beans.factory.annotation.Autowired
|
||||
private com.starry.admin.modules.order.service.IPlayOrderInfoService orderInfoService;
|
||||
|
||||
@org.springframework.beans.factory.annotation.Autowired
|
||||
private IPlayPersonnelAdminInfoService playPersonnelAdminInfoService;
|
||||
|
||||
@Test
|
||||
void randomOrderFailsWhenBalanceInsufficient() throws Exception {
|
||||
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
@@ -243,6 +262,228 @@ class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void randomOrderCancellationPermissions_pendingOrder() throws Exception {
|
||||
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
String nonManagerClerkId = "clerk-apitest-nonmgr-" + IdUtils.getUuid().substring(0, 6);
|
||||
String adminClerkId = "clerk-apitest-admin-" + IdUtils.getUuid().substring(0, 6);
|
||||
String adminSysUserId = "apitest-admin-user-" + IdUtils.getUuid().substring(0, 6);
|
||||
String adminSysUserCode = "apitest-admin-code-" + IdUtils.getUuid().substring(0, 6);
|
||||
String customerToken = null;
|
||||
String leaderToken = null;
|
||||
String nonManagerToken = null;
|
||||
String adminToken = null;
|
||||
try {
|
||||
resetCustomerBalance();
|
||||
customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||
|
||||
leaderToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CLERK_ID);
|
||||
clerkUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CLERK_ID, leaderToken);
|
||||
nonManagerToken = createClerk(nonManagerClerkId, null, null);
|
||||
adminToken = createClerk(adminClerkId, adminSysUserId, null);
|
||||
createAdminInfo(adminSysUserId, adminSysUserCode);
|
||||
|
||||
String pendingOrderId = createRandomOrder("API random pending " + IdUtils.getUuid(), customerToken);
|
||||
assertCancelAsClerk(pendingOrderId, nonManagerToken, CODE_FAIL);
|
||||
|
||||
String leaderOrderId = createRandomOrder("API random pending leader " + IdUtils.getUuid(), customerToken);
|
||||
assertCancelAsClerk(leaderOrderId, leaderToken, CODE_OK);
|
||||
assertOrderStatus(leaderOrderId, OrderConstant.OrderStatus.CANCELLED.getCode());
|
||||
|
||||
String adminOrderId = createRandomOrder("API random pending admin " + IdUtils.getUuid(), customerToken);
|
||||
assertCancelAsClerk(adminOrderId, adminToken, CODE_OK);
|
||||
assertOrderStatus(adminOrderId, OrderConstant.OrderStatus.CANCELLED.getCode());
|
||||
} finally {
|
||||
if (nonManagerClerkId != null) {
|
||||
removeClerk(nonManagerClerkId);
|
||||
}
|
||||
if (adminClerkId != null) {
|
||||
removeClerk(adminClerkId);
|
||||
}
|
||||
removeAdminInfo(adminSysUserId);
|
||||
CustomSecurityContextHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void randomOrderCancellationPermissions_acceptedOrder() throws Exception {
|
||||
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
String nonManagerClerkId = "clerk-apitest-nonmgr-" + IdUtils.getUuid().substring(0, 6);
|
||||
String otherClerkId = "clerk-apitest-other-" + IdUtils.getUuid().substring(0, 6);
|
||||
String adminClerkId = "clerk-apitest-admin-" + IdUtils.getUuid().substring(0, 6);
|
||||
String adminSysUserId = "apitest-admin-user-" + IdUtils.getUuid().substring(0, 6);
|
||||
String adminSysUserCode = "apitest-admin-code-" + IdUtils.getUuid().substring(0, 6);
|
||||
String customerToken = null;
|
||||
String leaderToken = null;
|
||||
String nonManagerToken = null;
|
||||
String otherToken = null;
|
||||
String adminToken = null;
|
||||
try {
|
||||
resetCustomerBalance();
|
||||
customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
|
||||
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
|
||||
|
||||
leaderToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CLERK_ID);
|
||||
clerkUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CLERK_ID, leaderToken);
|
||||
nonManagerToken = createClerk(nonManagerClerkId, null, null);
|
||||
otherToken = createClerk(otherClerkId, null, null);
|
||||
adminToken = createClerk(adminClerkId, adminSysUserId, null);
|
||||
createAdminInfo(adminSysUserId, adminSysUserCode);
|
||||
|
||||
String acceptedOrderId = createRandomOrder("API random accepted " + IdUtils.getUuid(), customerToken);
|
||||
acceptOrder(acceptedOrderId, nonManagerToken);
|
||||
|
||||
assertCancelAsClerk(acceptedOrderId, otherToken, CODE_FAIL);
|
||||
assertCancelAsClerk(acceptedOrderId, nonManagerToken, CODE_OK);
|
||||
assertOrderStatus(acceptedOrderId, OrderConstant.OrderStatus.CANCELLED.getCode());
|
||||
|
||||
String leaderOrderId = createRandomOrder("API random accepted leader " + IdUtils.getUuid(), customerToken);
|
||||
acceptOrder(leaderOrderId, nonManagerToken);
|
||||
assertCancelAsClerk(leaderOrderId, leaderToken, CODE_OK);
|
||||
assertOrderStatus(leaderOrderId, OrderConstant.OrderStatus.CANCELLED.getCode());
|
||||
|
||||
String adminOrderId = createRandomOrder("API random accepted admin " + IdUtils.getUuid(), customerToken);
|
||||
acceptOrder(adminOrderId, nonManagerToken);
|
||||
assertCancelAsClerk(adminOrderId, adminToken, CODE_OK);
|
||||
assertOrderStatus(adminOrderId, OrderConstant.OrderStatus.CANCELLED.getCode());
|
||||
} finally {
|
||||
removeClerk(nonManagerClerkId);
|
||||
removeClerk(otherClerkId);
|
||||
removeClerk(adminClerkId);
|
||||
removeAdminInfo(adminSysUserId);
|
||||
CustomSecurityContextHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private String createRandomOrder(String remark, String customerToken) throws Exception {
|
||||
String payload = "{" +
|
||||
"\"sex\":\"" + OrderConstant.Gender.FEMALE.getCode() + "\"," +
|
||||
"\"levelId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID + "\"," +
|
||||
"\"commodityId\":\"" + ApiTestDataSeeder.DEFAULT_COMMODITY_ID + "\"," +
|
||||
"\"commodityQuantity\":" + SINGLE_QUANTITY + "," +
|
||||
"\"weiChatCode\":\"" + DEFAULT_WECHAT_CODE + "\"," +
|
||||
"\"excludeHistory\":\"" + OrderConstant.EXCLUDE_HISTORY_NO + "\"," +
|
||||
"\"couponIds\":[]," +
|
||||
"\"remark\":\"" + remark + "\"" +
|
||||
"}";
|
||||
|
||||
mockMvc.perform(post("/wx/custom/order/random")
|
||||
.header(USER_HEADER, DEFAULT_USER)
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(payload))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(CODE_OK));
|
||||
|
||||
ensureTenantContext();
|
||||
PlayOrderInfoEntity order = playOrderInfoService.lambdaQuery()
|
||||
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
|
||||
.eq(PlayOrderInfoEntity::getRemark, remark)
|
||||
.orderByDesc(PlayOrderInfoEntity::getCreatedTime)
|
||||
.last("limit 1")
|
||||
.one();
|
||||
Assertions.assertThat(order).isNotNull();
|
||||
return order.getId();
|
||||
}
|
||||
|
||||
private void acceptOrder(String orderId, String clerkTokenValue) throws Exception {
|
||||
mockMvc.perform(get("/wx/clerk/order/accept")
|
||||
.param("id", orderId)
|
||||
.header(USER_HEADER, DEFAULT_USER)
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkTokenValue))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(CODE_OK));
|
||||
}
|
||||
|
||||
private void assertCancelAsClerk(String orderId, String clerkTokenValue, int expectedCode) throws Exception {
|
||||
String payload = "{" +
|
||||
"\"orderId\":\"" + orderId + "\"," +
|
||||
"\"refundReason\":\"" + CANCEL_REASON + "\"," +
|
||||
"\"images\":[]" +
|
||||
"}";
|
||||
mockMvc.perform(post("/wx/clerk/order/cancellation")
|
||||
.header(USER_HEADER, DEFAULT_USER)
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkTokenValue)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(payload))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(expectedCode));
|
||||
}
|
||||
|
||||
private void assertOrderStatus(String orderId, String expectedStatus) {
|
||||
ensureTenantContext();
|
||||
PlayOrderInfoEntity order = playOrderInfoService.selectOrderInfoById(orderId);
|
||||
Assertions.assertThat(order).isNotNull();
|
||||
Assertions.assertThat(order.getOrderStatus()).isEqualTo(expectedStatus);
|
||||
}
|
||||
|
||||
private String createClerk(String clerkId, String sysUserId, String groupId) {
|
||||
ensureTenantContext();
|
||||
PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
|
||||
entity.setId(clerkId);
|
||||
entity.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
entity.setSysUserId(sysUserId);
|
||||
entity.setOpenid("openid-" + clerkId);
|
||||
entity.setNickname("测试店员-" + clerkId);
|
||||
entity.setGroupId(groupId);
|
||||
entity.setLevelId(ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
|
||||
entity.setFixingLevel(CLERK_FIXING_LEVEL);
|
||||
entity.setSex(OrderConstant.Gender.FEMALE.getCode());
|
||||
entity.setPhone("1390000" + clerkId.substring(0, 4));
|
||||
entity.setWeiChatCode("apitest-clerk-" + clerkId);
|
||||
entity.setAvatar("https://example.com/avatar.png");
|
||||
entity.setAccountBalance(BigDecimal.ZERO);
|
||||
entity.setOnboardingState(CLERK_ONBOARDING_STATE);
|
||||
entity.setListingState(CLERK_LISTING_ENABLED);
|
||||
entity.setDisplayState(CLERK_DISPLAY_ENABLED);
|
||||
entity.setOnlineState(CLERK_ONLINE_ENABLED);
|
||||
entity.setRandomOrderState(CLERK_RANDOM_ORDER_ENABLED);
|
||||
entity.setClerkState(CLERK_STATE_ACTIVE);
|
||||
entity.setEntryTime(LocalDateTime.now());
|
||||
clerkUserInfoService.save(entity);
|
||||
String token = wxTokenService.createWxUserToken(clerkId);
|
||||
clerkUserInfoService.updateTokenById(clerkId, token);
|
||||
return token;
|
||||
}
|
||||
|
||||
private void removeClerk(String clerkId) {
|
||||
if (clerkId == null) {
|
||||
return;
|
||||
}
|
||||
ensureTenantContext();
|
||||
clerkUserInfoService.removeById(clerkId);
|
||||
}
|
||||
|
||||
private void createAdminInfo(String sysUserId, String sysUserCode) {
|
||||
if (sysUserId == null) {
|
||||
return;
|
||||
}
|
||||
ensureTenantContext();
|
||||
PlayPersonnelAdminInfoEntity entity = new PlayPersonnelAdminInfoEntity();
|
||||
entity.setId("admin-info-" + sysUserId);
|
||||
entity.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
entity.setSysUserId(sysUserId);
|
||||
entity.setSysUserCode(sysUserCode);
|
||||
entity.setAdminName("API测试管理员");
|
||||
entity.setLeaderName("API测试负责人");
|
||||
entity.setAddTime(LocalDateTime.now());
|
||||
playPersonnelAdminInfoService.save(entity);
|
||||
}
|
||||
|
||||
private void removeAdminInfo(String sysUserId) {
|
||||
if (sysUserId == null) {
|
||||
return;
|
||||
}
|
||||
ensureTenantContext();
|
||||
playPersonnelAdminInfoService.lambdaUpdate()
|
||||
.eq(PlayPersonnelAdminInfoEntity::getSysUserId, sysUserId)
|
||||
.remove();
|
||||
}
|
||||
|
||||
@Test
|
||||
// 测试用例:订单已接单后由管理员强制取消,也应释放所使用的优惠券
|
||||
void randomOrderForceCancelReleasesCoupon() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user