Compare commits

...

3 Commits

Author SHA1 Message Date
irving
d961e62cc2 合并 fix-lable 分支:优化订单通知消息标签
Some checks failed
Build and Push Backend / docker (push) Failing after 5s
2025-11-03 22:54:13 -05:00
irving
da2902c61c 重构:优化订单通知消息标签,支持动态显示订单类型
- 新增 OrderMessageLabelResolver 用于解析订单场景标签
- 修改微信公众号下单通知,根据下单类型(随机单/指定单/打赏/礼物)显示对应标签
- 更新 WxCustomMpService 接口,传递 placeType 和 rewardType 参数
- 完善相关单元测试和 Mock 配置
2025-11-03 22:51:48 -05:00
hucs-dev
f39fc4f040 feat: 🎁店员等级新增排序字段 2025-11-04 10:49:05 +08:00
15 changed files with 324 additions and 19 deletions

View File

@@ -69,4 +69,6 @@ public class PlayClerkLevelInfoEntity extends BaseEntity<PlayClerkLevelInfoEntit
private Integer styleType; private Integer styleType;
private String styleImageUrl; private String styleImageUrl;
private Long orderNumber;
} }

View File

@@ -68,4 +68,6 @@ public class PlayClerkLevelEditVo {
@ApiModelProperty(value = "样式图片URL", example = "https://example.com/style.jpg", notes = "等级样式图片URL") @ApiModelProperty(value = "样式图片URL", example = "https://example.com/style.jpg", notes = "等级样式图片URL")
private String styleImageUrl; private String styleImageUrl;
private Long orderNumber;
} }

View File

@@ -43,6 +43,7 @@ public class PlayClerkLevelInfoServiceImpl extends ServiceImpl<PlayClerkLevelInf
entity.setFirstRegularRatio(45); entity.setFirstRegularRatio(45);
entity.setNotFirstRegularRatio(50); entity.setNotFirstRegularRatio(50);
entity.setLevel(1); entity.setLevel(1);
entity.setOrderNumber(1L);
entity.setStyleType(entity.getLevel()); entity.setStyleType(entity.getLevel());
entity.setTenantId(sysTenantEntity.getTenantId()); entity.setTenantId(sysTenantEntity.getTenantId());
this.baseMapper.insert(entity); this.baseMapper.insert(entity);
@@ -64,6 +65,7 @@ public class PlayClerkLevelInfoServiceImpl extends ServiceImpl<PlayClerkLevelInf
entity.setFirstRegularRatio(45); entity.setFirstRegularRatio(45);
entity.setNotFirstRegularRatio(50); entity.setNotFirstRegularRatio(50);
entity.setLevel(1); entity.setLevel(1);
entity.setOrderNumber(1L);
entity.setStyleType(1); entity.setStyleType(1);
this.baseMapper.insert(entity); this.baseMapper.insert(entity);
return entity; return entity;

View File

@@ -344,10 +344,9 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl<PlayClerkUserInfoM
lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getOnboardingState, vo.getOnboardingState()); lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getOnboardingState, vo.getOnboardingState());
} }
// 排序:置顶状态优先,在线用户其次,最后按创建时间倒 // 排序:先按等级字段排序(越小的越在前面),再按在线状态排
lambdaQueryWrapper.orderByDesc(PlayClerkUserInfoEntity::getPinToTopState) lambdaQueryWrapper.orderByAsc(PlayClerkLevelInfoEntity::getOrderNumber)
.orderByDesc(PlayClerkUserInfoEntity::getOnlineState) .orderByDesc(PlayClerkUserInfoEntity::getOnlineState);
.orderByDesc(PlayClerkUserInfoEntity::getCreatedTime);
return this.baseMapper.selectJoinPage(page, PlayClerkUserInfoResultVo.class, lambdaQueryWrapper); return this.baseMapper.selectJoinPage(page, PlayClerkUserInfoResultVo.class, lambdaQueryWrapper);
} }

View File

@@ -76,7 +76,14 @@ public class PlayOrderInfoController {
public R sendNotice(String orderId) { public R sendNotice(String orderId) {
PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(orderId); PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(orderId);
List<PlayClerkUserInfoEntity> clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).isNotNull(PlayClerkUserInfoEntity::getOpenid).eq(PlayClerkUserInfoEntity::getClerkState, "1").eq(PlayClerkUserInfoEntity::getSex, orderInfo.getSex())); List<PlayClerkUserInfoEntity> clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).isNotNull(PlayClerkUserInfoEntity::getOpenid).eq(PlayClerkUserInfoEntity::getClerkState, "1").eq(PlayClerkUserInfoEntity::getSex, orderInfo.getSex()));
wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderInfo.getOrderNo(), orderInfo.getOrderMoney().toString(), orderInfo.getCommodityName(), orderId); wxCustomMpService.sendCreateOrderMessageBatch(
clerkList,
orderInfo.getOrderNo(),
orderInfo.getOrderMoney().toString(),
orderInfo.getCommodityName(),
orderId,
orderInfo.getPlaceType(),
orderInfo.getRewardType());
return R.ok(); return R.ok();
} }
@@ -126,8 +133,15 @@ public class PlayOrderInfoController {
PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(vo.getOrderId()); PlayOrderInfoEntity orderInfo = orderInfoService.selectOrderInfoById(vo.getOrderId());
PlayCommodityInfoVo commodityInfo = playCommodityInfoService.queryCommodityInfo(orderInfo.getCommodityId(), PlayCommodityInfoVo commodityInfo = playCommodityInfoService.queryCommodityInfo(orderInfo.getCommodityId(),
clerkUserInfo.getLevelId()); clerkUserInfo.getLevelId());
wxCustomMpService.sendCreateOrderMessage(clerkUserInfo.getTenantId(), clerkUserInfo.getOpenid(), wxCustomMpService.sendCreateOrderMessage(
orderInfo.getOrderNo(), orderInfo.getOrderMoney().toString(), commodityInfo.getCommodityName(), vo.getOrderId()); clerkUserInfo.getTenantId(),
clerkUserInfo.getOpenid(),
orderInfo.getOrderNo(),
orderInfo.getOrderMoney().toString(),
commodityInfo.getCommodityName(),
vo.getOrderId(),
orderInfo.getPlaceType(),
orderInfo.getRewardType());
return R.ok("操作成功"); return R.ok("操作成功");
} }

View File

@@ -96,7 +96,14 @@ public class OrderJob {
redisTemplate.opsForValue().set("order_notice_" + orderInfo.getId(), "1", 30, java.util.concurrent.TimeUnit.MINUTES); redisTemplate.opsForValue().set("order_notice_" + orderInfo.getId(), "1", 30, java.util.concurrent.TimeUnit.MINUTES);
List<PlayClerkUserInfoEntity> clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).isNotNull(PlayClerkUserInfoEntity::getOpenid).eq(PlayClerkUserInfoEntity::getClerkState, "1") List<PlayClerkUserInfoEntity> clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class).isNotNull(PlayClerkUserInfoEntity::getOpenid).eq(PlayClerkUserInfoEntity::getClerkState, "1")
.eq(PlayClerkUserInfoEntity::getSex, orderInfo.getSex()).eq(PlayClerkUserInfoEntity::getTenantId, orderInfo.getTenantId())); .eq(PlayClerkUserInfoEntity::getSex, orderInfo.getSex()).eq(PlayClerkUserInfoEntity::getTenantId, orderInfo.getTenantId()));
wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderInfo.getOrderNo(), orderInfo.getOrderMoney().toString(), orderInfo.getCommodityName(), orderInfo.getId()); wxCustomMpService.sendCreateOrderMessageBatch(
clerkList,
orderInfo.getOrderNo(),
orderInfo.getOrderMoney().toString(),
orderInfo.getCommodityName(),
orderInfo.getId(),
orderInfo.getPlaceType(),
orderInfo.getRewardType());
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);

View File

@@ -0,0 +1,42 @@
package com.starry.admin.modules.order.module.constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
/**
* Resolves human-readable labels for order scenarios used in notifications.
*/
@Slf4j
public final class OrderMessageLabelResolver {
private OrderMessageLabelResolver() {
}
public static String resolve(String placeTypeCode, String rewardTypeCode) {
OrderConstant.PlaceType placeTypeEnum = OrderConstant.PlaceType.RANDOM;
if (StringUtils.isNotBlank(placeTypeCode)) {
try {
placeTypeEnum = OrderConstant.PlaceType.fromCode(placeTypeCode);
} catch (IllegalArgumentException ex) {
log.warn("未知的下单类型placeTypeCode={},按随机单处理。", placeTypeCode, ex);
}
}
switch (placeTypeEnum) {
case SPECIFIED:
return "指定单";
case REWARD:
OrderConstant.RewardType rewardTypeEnum = OrderConstant.RewardType.BALANCE;
if (StringUtils.isNotBlank(rewardTypeCode)) {
try {
rewardTypeEnum = OrderConstant.RewardType.fromCode(rewardTypeCode);
} catch (IllegalArgumentException ex) {
log.warn("未知的打赏类型rewardTypeCode={},按打赏处理。", rewardTypeCode, ex);
}
}
return rewardTypeEnum == OrderConstant.RewardType.GIFT ? "礼物" : "打赏";
case RANDOM:
default:
return "随机单";
}
}
}

View File

@@ -355,7 +355,9 @@ public class WxCustomController {
orderNo, orderNo,
netAmount.toString(), netAmount.toString(),
commodityInfo.getCommodityName(), commodityInfo.getCommodityName(),
order.getId()); order.getId(),
order.getPlaceType(),
order.getRewardType());
return R.ok("成功"); return R.ok("成功");
} }
@@ -430,7 +432,14 @@ public class WxCustomController {
.eq(PlayClerkUserInfoEntity::getListingState, ListingStatus.LISTED.getCode()) .eq(PlayClerkUserInfoEntity::getListingState, ListingStatus.LISTED.getCode())
.eq(PlayClerkUserInfoEntity::getOnlineState, "1") .eq(PlayClerkUserInfoEntity::getOnlineState, "1")
.eq(PlayClerkUserInfoEntity::getSex, vo.getSex())); .eq(PlayClerkUserInfoEntity::getSex, vo.getSex()));
wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderNo, netAmount.toString(), commodityInfo.getCommodityName(),order.getId()); wxCustomMpService.sendCreateOrderMessageBatch(
clerkList,
orderNo,
netAmount.toString(),
commodityInfo.getCommodityName(),
order.getId(),
order.getPlaceType(),
order.getRewardType());
// 记录订单,指定指定未接单后,进行退款处理 // 记录订单,指定指定未接单后,进行退款处理
overdueOrderHandlerTask.enqueue(orderId + "_" + SecurityUtils.getTenantId()); overdueOrderHandlerTask.enqueue(orderId + "_" + SecurityUtils.getTenantId());
// 下单成功后,先根据用户条件进行随机分配 // 下单成功后,先根据用户条件进行随机分配

View File

@@ -17,6 +17,7 @@ import com.starry.admin.modules.clerk.module.enums.OnboardingStatus;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService; import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService; import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
import com.starry.admin.modules.order.module.constant.OrderMessageLabelResolver;
import com.starry.admin.modules.order.module.entity.PlayOrderComplaintInfoEntity; import com.starry.admin.modules.order.module.entity.PlayOrderComplaintInfoEntity;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity; import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelAdminInfoEntity; import com.starry.admin.modules.personnel.module.entity.PlayPersonnelAdminInfoEntity;
@@ -149,7 +150,8 @@ public class WxCustomMpService {
} }
} }
public void sendCreateOrderMessageBatch(List<PlayClerkUserInfoEntity> clerkList, String orderNo, String string, String commodityName, String orderId) { public void sendCreateOrderMessageBatch(List<PlayClerkUserInfoEntity> clerkList, String orderNo, String string,
String commodityName, String orderId, String placeType, String rewardType) {
if (CollectionUtils.isEmpty(clerkList)) { if (CollectionUtils.isEmpty(clerkList)) {
return; return;
} }
@@ -159,7 +161,7 @@ public class WxCustomMpService {
.filter(ca -> OnboardingStatus.isActive(ca.getOnboardingState())) .filter(ca -> OnboardingStatus.isActive(ca.getOnboardingState()))
.filter(ca -> ListingStatus.isListed(ca.getListingState())) .filter(ca -> ListingStatus.isListed(ca.getListingState()))
.forEach(ca -> sendCreateOrderMessage(ca.getTenantId(), ca.getOpenid(), orderNo, string, commodityName, .forEach(ca -> sendCreateOrderMessage(ca.getTenantId(), ca.getOpenid(), orderNo, string, commodityName,
orderId))); orderId, placeType, rewardType)));
} }
/** /**
@@ -173,7 +175,7 @@ public class WxCustomMpService {
* @param orderId * @param orderId
*/ */
public void sendCreateOrderMessage(String tenantId, String openId, String orderNo, String money, public void sendCreateOrderMessage(String tenantId, String openId, String orderNo, String money,
String commodityName, String orderId) { String commodityName, String orderId, String placeType, String rewardType) {
SysTenantEntity tenant = tenantService.selectSysTenantByTenantId(tenantId); SysTenantEntity tenant = tenantService.selectSysTenantByTenantId(tenantId);
WxMpTemplateMessage templateMessage = getWxMpTemplateMessage(tenant.getXindingdanshoulitongzhiTemplateId(), WxMpTemplateMessage templateMessage = getWxMpTemplateMessage(tenant.getXindingdanshoulitongzhiTemplateId(),
@@ -181,7 +183,7 @@ public class WxCustomMpService {
List<WxMpTemplateData> data = new ArrayList<>(); List<WxMpTemplateData> data = new ArrayList<>();
data.add(new WxMpTemplateData("time6", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"))); data.add(new WxMpTemplateData("time6", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")));
data.add(new WxMpTemplateData("character_string9", orderNo)); data.add(new WxMpTemplateData("character_string9", orderNo));
data.add(new WxMpTemplateData("short_thing5", "陪聊下单")); data.add(new WxMpTemplateData("short_thing5", OrderMessageLabelResolver.resolve(placeType, rewardType)));
data.add(new WxMpTemplateData("thing11", commodityName)); data.add(new WxMpTemplateData("thing11", commodityName));
data.add(new WxMpTemplateData("amount8", money)); data.add(new WxMpTemplateData("amount8", money));
templateMessage.setData(data); templateMessage.setData(data);
@@ -198,7 +200,6 @@ public class WxCustomMpService {
} }
} }
/** /**
* 店员接单后,通过微信公众号发送消息 * 店员接单后,通过微信公众号发送消息
* *

View File

@@ -3,6 +3,7 @@ package com.starry.admin.api;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
@@ -11,6 +12,7 @@ import org.springframework.test.web.servlet.MockMvc;
@AutoConfigureMockMvc @AutoConfigureMockMvc
@ActiveProfiles("apitest") @ActiveProfiles("apitest")
@TestPropertySource(properties = "spring.task.scheduling.enabled=false") @TestPropertySource(properties = "spring.task.scheduling.enabled=false")
@Import(MockWxMpServiceConfig.class)
public abstract class AbstractApiTest { public abstract class AbstractApiTest {
protected static final String TENANT_HEADER = "X-Tenant"; protected static final String TENANT_HEADER = "X-Tenant";

View File

@@ -0,0 +1,31 @@
package com.starry.admin.api;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpTemplateMsgService;
import org.mockito.Mockito;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
/**
* Provides stubbed WeChat MP services in the apitest profile so integration tests never hit external APIs.
*/
@TestConfiguration
@Profile("apitest")
public class MockWxMpServiceConfig {
@Bean
@Primary
public WxMpService wxMpService() {
WxMpService service = mock(WxMpService.class, Mockito.RETURNS_DEEP_STUBS);
WxMpTemplateMsgService templateMsgService = mock(WxMpTemplateMsgService.class);
when(service.getTemplateMsgService()).thenReturn(templateMsgService);
when(service.switchoverTo(Mockito.anyString())).thenReturn(service);
when(service.switchover(Mockito.anyString())).thenReturn(true);
return service;
}
}

View File

@@ -397,6 +397,14 @@ class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport {
Assertions.assertThat(order.getFinalAmount()).isEqualByComparingTo(expectedNet); Assertions.assertThat(order.getFinalAmount()).isEqualByComparingTo(expectedNet);
Assertions.assertThat(order.getDiscountAmount()).isEqualByComparingTo(discount); Assertions.assertThat(order.getDiscountAmount()).isEqualByComparingTo(discount);
verify(wxCustomMpService).sendCreateOrderMessageBatch(
anyList(),
eq(order.getOrderNo()),
eq(expectedNet.toString()),
eq(order.getCommodityName()),
eq(order.getId()),
eq(order.getPlaceType()),
eq(order.getRewardType()));
String orderId = order.getId(); String orderId = order.getId();
PlayCouponDetailsEntity detailAfterOrderPlaced = couponDetailsService.getById(couponDetailId); PlayCouponDetailsEntity detailAfterOrderPlaced = couponDetailsService.getById(couponDetailId);
@@ -667,7 +675,9 @@ class WxCustomRandomOrderApiTest extends WxCustomOrderApiTestSupport {
eq(order.getOrderNo()), eq(order.getOrderNo()),
eq(order.getFinalAmount().toString()), eq(order.getFinalAmount().toString()),
eq(order.getCommodityName()), eq(order.getCommodityName()),
eq(order.getId())); eq(order.getId()),
eq(order.getPlaceType()),
eq(order.getRewardType()));
verify(overdueOrderHandlerTask).enqueue(order.getId() + "_" + ApiTestDataSeeder.DEFAULT_TENANT_ID); verify(overdueOrderHandlerTask).enqueue(order.getId() + "_" + ApiTestDataSeeder.DEFAULT_TENANT_ID);
reset(wxCustomMpService); reset(wxCustomMpService);

View File

@@ -244,7 +244,9 @@ class WxCustomSpecifiedOrderApiTest extends WxCustomOrderApiTestSupport {
anyString(), anyString(),
eq(expectedNet.toString()), eq(expectedNet.toString()),
eq(order.getCommodityName()), eq(order.getCommodityName()),
eq(order.getId())); eq(order.getId()),
eq(order.getPlaceType()),
eq(order.getRewardType()));
int ratio = order.getEstimatedRevenueRatio(); int ratio = order.getEstimatedRevenueRatio();
BigDecimal baseRevenue = grossAmount BigDecimal baseRevenue = grossAmount
@@ -411,7 +413,9 @@ class WxCustomSpecifiedOrderApiTest extends WxCustomOrderApiTestSupport {
anyString(), anyString(),
eq(order.getFinalAmount().toString()), eq(order.getFinalAmount().toString()),
eq(order.getCommodityName()), eq(order.getCommodityName()),
eq(order.getId())); eq(order.getId()),
eq(order.getPlaceType()),
eq(order.getRewardType()));
ensureTenantContext(); ensureTenantContext();
long afterCount = playOrderInfoService.lambdaQuery() long afterCount = playOrderInfoService.lambdaQuery()

View File

@@ -67,7 +67,14 @@ class WxOrderInfoControllerApiTest extends WxCustomOrderApiTestSupport {
// Relax notifications to avoid strict verification noise // Relax notifications to avoid strict verification noise
doNothing().when(notificationSender).sendOrderMessageAsync(Mockito.any()); doNothing().when(notificationSender).sendOrderMessageAsync(Mockito.any());
doNothing().when(notificationSender).sendOrderFinishMessageAsync(Mockito.any()); doNothing().when(notificationSender).sendOrderFinishMessageAsync(Mockito.any());
doNothing().when(wxCustomMpService).sendCreateOrderMessageBatch(anyList(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); doNothing().when(wxCustomMpService).sendCreateOrderMessageBatch(
anyList(),
Mockito.anyString(),
Mockito.anyString(),
Mockito.anyString(),
Mockito.anyString(),
Mockito.anyString(),
Mockito.anyString());
doNothing().when(overdueOrderHandlerTask).enqueue(Mockito.anyString()); doNothing().when(overdueOrderHandlerTask).enqueue(Mockito.anyString());
} }

View File

@@ -0,0 +1,173 @@
package com.starry.admin.modules.weichat.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.module.enums.ListingStatus;
import com.starry.admin.modules.clerk.module.enums.OnboardingStatus;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.system.module.entity.SysTenantEntity;
import com.starry.admin.modules.system.service.impl.SysTenantServiceImpl;
import java.util.Arrays;
import java.util.List;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpTemplateMsgService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@ExtendWith(MockitoExtension.class)
class WxCustomMpServiceTest {
private static final String TENANT_ID = "tenant-1";
private static final String APP_ID = "wx-app";
@Mock
private WxMpService wxMpService;
@Mock
private WxMpTemplateMsgService templateMsgService;
@Mock
private SysTenantServiceImpl tenantService;
@Mock
private com.starry.admin.modules.custom.service.IPlayCustomUserInfoService customUserInfoService;
@Mock
private com.starry.admin.modules.personnel.service.IPlayPersonnelAdminInfoService playPersonnelAdminInfoService;
@Mock
private com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService clerkUserInfoService;
@Mock
private ThreadPoolTaskExecutor executor;
@InjectMocks
private WxCustomMpService wxCustomMpService;
private SysTenantEntity buildTenant() {
return new SysTenantEntity()
.setTenantId(TENANT_ID)
.setTenantKey("tenant-key")
.setAppId(APP_ID)
.setSecret("wx-secret")
.setXindingdanshoulitongzhiTemplateId("template-create-order");
}
@Test
void sendCreateOrderMessageUsesOrderLabelResolver() throws WxErrorException {
SysTenantEntity tenant = buildTenant();
when(tenantService.selectSysTenantByTenantId(TENANT_ID)).thenReturn(tenant);
when(wxMpService.switchoverTo(tenant.getAppId())).thenReturn(wxMpService);
when(wxMpService.getTemplateMsgService()).thenReturn(templateMsgService);
PlayClerkUserInfoEntity clerkDetails = new PlayClerkUserInfoEntity();
clerkDetails.setId("clerk-1");
clerkDetails.setNickname("Lily");
when(clerkUserInfoService.selectByOpenid("openid-clerk")).thenReturn(clerkDetails);
wxCustomMpService.sendCreateOrderMessage(
TENANT_ID,
"openid-clerk",
"ORDER-001",
"88.88",
"伴聊套餐",
"order-1",
OrderConstant.PlaceType.REWARD.getCode(),
OrderConstant.RewardType.GIFT.getCode());
ArgumentCaptor<WxMpTemplateMessage> captor = ArgumentCaptor.forClass(WxMpTemplateMessage.class);
verify(templateMsgService).sendTemplateMsg(captor.capture());
WxMpTemplateMessage message = captor.getValue();
assertThat(message.getTemplateId()).isEqualTo(tenant.getXindingdanshoulitongzhiTemplateId());
assertThat(message.getToUser()).isEqualTo("openid-clerk");
assertThat(message.getUrl())
.isEqualTo("https://" + tenant.getTenantKey() + ".julyharbor.com/clerk/#/orderDetail/order-1");
List<WxMpTemplateData> data = message.getData();
assertThat(data)
.filteredOn(templateData -> "short_thing5".equals(templateData.getName()))
.singleElement()
.extracting(WxMpTemplateData::getValue)
.isEqualTo("礼物");
assertThat(data)
.filteredOn(templateData -> "character_string9".equals(templateData.getName()))
.singleElement()
.extracting(WxMpTemplateData::getValue)
.isEqualTo("ORDER-001");
}
@Test
void sendCreateOrderMessageBatchFiltersInactiveClerksAndForwardsLabel() throws WxErrorException {
SysTenantEntity tenant = buildTenant();
when(tenantService.selectSysTenantByTenantId(TENANT_ID)).thenReturn(tenant);
when(wxMpService.switchoverTo(anyString())).thenReturn(wxMpService);
when(wxMpService.getTemplateMsgService()).thenReturn(templateMsgService);
doAnswer(invocation -> {
Runnable runnable = invocation.getArgument(0);
runnable.run();
return null;
}).when(executor).execute(any(Runnable.class));
PlayClerkUserInfoEntity eligible = new PlayClerkUserInfoEntity();
eligible.setTenantId(TENANT_ID);
eligible.setOpenid("openid-eligible");
eligible.setOnboardingState(OnboardingStatus.ACTIVE.getCode());
eligible.setListingState(ListingStatus.LISTED.getCode());
PlayClerkUserInfoEntity offboarded = new PlayClerkUserInfoEntity();
offboarded.setTenantId(TENANT_ID);
offboarded.setOpenid("openid-offboarded");
offboarded.setOnboardingState(OnboardingStatus.OFFBOARDED.getCode());
offboarded.setListingState(ListingStatus.LISTED.getCode());
PlayClerkUserInfoEntity delisted = new PlayClerkUserInfoEntity();
delisted.setTenantId(TENANT_ID);
delisted.setOpenid("openid-delisted");
delisted.setOnboardingState(OnboardingStatus.ACTIVE.getCode());
delisted.setListingState(ListingStatus.DELISTED.getCode());
PlayClerkUserInfoEntity clerkDetails = new PlayClerkUserInfoEntity();
clerkDetails.setId("clerk-eligible");
clerkDetails.setNickname("Kara");
when(clerkUserInfoService.selectByOpenid("openid-eligible")).thenReturn(clerkDetails);
wxCustomMpService.sendCreateOrderMessageBatch(
Arrays.asList(eligible, offboarded, delisted, null),
"ORDER-002",
"45.00",
"单人畅聊",
"order-2",
OrderConstant.PlaceType.SPECIFIED.getCode(),
OrderConstant.RewardType.NOT_APPLICABLE.getCode());
ArgumentCaptor<WxMpTemplateMessage> captor = ArgumentCaptor.forClass(WxMpTemplateMessage.class);
verify(templateMsgService, times(1)).sendTemplateMsg(captor.capture());
WxMpTemplateMessage message = captor.getValue();
assertThat(message.getToUser()).isEqualTo("openid-eligible");
assertThat(message.getData())
.filteredOn(templateData -> "short_thing5".equals(templateData.getName()))
.singleElement()
.extracting(WxMpTemplateData::getValue)
.isEqualTo("指定单");
}
}