Merge branch 'dev' into feature-BI

This commit is contained in:
Lemon
2026-05-25 09:28:54 +08:00
27 changed files with 1376 additions and 115 deletions

View File

@@ -51,17 +51,12 @@ import com.jsowell.common.util.id.SnowflakeIdWorker;
import com.jsowell.common.util.id.UUID;
import com.jsowell.common.util.ip.AddressUtils;
import com.jsowell.framework.async.JsowellThreadFactory;
import com.jsowell.netty.handler.yunkuaichong.HeartbeatRequestHandler;
import com.jsowell.netty.handler.yunkuaichong.TransactionRecordsRequestHandler;
import com.jsowell.netty.service.camera.impl.CameraBusinessServiceImpl;
import com.jsowell.netty.service.yunkuaichong.YKCBusinessService;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.domain.ykcCommond.IssueQRCodeCommand;
import com.jsowell.pile.domain.ykcCommond.ProofreadTimeCommand;
import com.jsowell.pile.dto.*;
import com.jsowell.pile.dto.amap.GetStationInfoDTO;
import com.jsowell.pile.dto.lutongyunting.BindCouponDTO;
import com.jsowell.pile.mapper.MemberBasicInfoMapper;
import com.jsowell.pile.mapper.PileBillingTemplateMapper;
import com.jsowell.pile.service.*;
import com.jsowell.pile.service.batteryreport.BatteryChargeReportService;
@@ -75,7 +70,6 @@ import com.jsowell.pile.vo.base.MemberWalletVO;
import com.jsowell.pile.vo.base.PileInfoVO;
import com.jsowell.pile.vo.uniapp.customer.*;
import com.jsowell.pile.vo.web.*;
import com.jsowell.service.MemberService;
import com.jsowell.service.OrderService;
import com.jsowell.service.PileService;
import com.jsowell.service.TempService;
@@ -146,51 +140,27 @@ public class SpringBootTestController {
@Autowired
private AdapayMemberAccountService adapayMemberAccountService;
@Autowired
private PileMsgRecordService pileMsgRecordService;
@Autowired
private StationSplitConfigService stationSplitConfigService;
@Autowired
private PileStationInfoService pileStationInfoService;
@Autowired
private YKCPushCommandService ykcPushBusinessService;
@Autowired
private HeartbeatRequestHandler heartbeatRequestHandler;
@Autowired
private YKCBusinessService ykcBusinessService;
@Autowired
private PileBillingTemplateMapper pileBillingTemplateMapper;
@Autowired
private PileRemoteService pileRemoteService;
@Autowired
private MemberService memberService;
@Autowired
private OrderService orderService;
@Autowired
private PileBillingTemplateService pileBillingTemplateService;
@Autowired
private MemberBasicInfoMapper memberBasicInfoMapper;
@Autowired
private SimCardService simCardService;
@Autowired
private PileBasicInfoService pileBasicInfoService;
@Autowired
private WechatPayService wechatPayService;
@Autowired
private OrderBasicInfoService orderBasicInfoService;
@@ -231,9 +201,6 @@ public class SpringBootTestController {
@Autowired
private PileConnectorInfoService pileConnectorInfoService;
@Autowired
private TransactionRecordsRequestHandler transactionRecordsRequestHandler;
@Autowired
private MemberBasicInfoService memberBasicInfoService;
@@ -243,9 +210,6 @@ public class SpringBootTestController {
@Autowired
private OrderPileOccupyService orderPileOccupyService;
@Autowired
private CameraBusinessServiceImpl cameraBusinessServiceImpl;
@Autowired
private LTYTService ltytService;
@@ -267,9 +231,6 @@ public class SpringBootTestController {
@Autowired
private MemberWalletInfoService memberWalletInfoService;
@Autowired
private MemberGroupService memberGroupService;
@Autowired
private HuaWeiService huaWeiService;
@@ -313,6 +274,28 @@ public class SpringBootTestController {
private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10, JsowellThreadFactory.forName("test-thread-factory"));
@Test
public void importTest() {
List<ImportMemberBalanceDTO> list = Lists.newArrayList();
list.add(new ImportMemberBalanceDTO("18512341235", new BigDecimal("100")));
tempService.batchImportMemberBalance(list);
}
@Test
public void testRefundAmount() {
ApplyRefundDTO dto = new ApplyRefundDTO();
dto.setMemberId("42833346");
dto.setRefundAmount(new BigDecimal("13.12"));
dto.setWechatAppId(Constants.XIXIAO_APP_ID);
dto.setRefundType("2");
dto.setWalletCode("8986380447445731");
String mode = pileMerchantInfoService.getDelayModeByWechatAppId(dto.getWechatAppId());
// 获取处理逻辑
AbstractProgramLogic orderLogic = ProgramLogicFactory.getProgramLogic(mode);
orderLogic.refundBalance(dto);
}
@Test
public void deleteThirdPartyStationRelationTest() {
ThirdPartyStationRelationDTO dto = new ThirdPartyStationRelationDTO();

View File

@@ -0,0 +1,275 @@
package com.jsowell.service;
import com.alipay.api.AlipayClient;
import com.jsowell.alipay.factory.AlipayClientFactory;
import com.jsowell.common.constant.CacheConstants;
import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.enums.adapay.AdapayPayChannelEnum;
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.JWTUtils;
import com.jsowell.pile.domain.MemberBasicInfo;
import com.jsowell.pile.dto.MemberRegisterAndLoginDTO;
import com.jsowell.pile.service.MemberBasicInfoService;
import com.jsowell.pile.transaction.dto.MemberTransactionDTO;
import com.jsowell.pile.transaction.service.TransactionService;
import com.jsowell.pile.util.MerchantUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.dao.DuplicateKeyException;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
class MemberServiceRegisterAndLoginTest {
private static final LoginInvoker OLD_LOGIN = MemberService::memberRegisterAndLogin;
private static final LoginInvoker V2_LOGIN = MemberService::memberRegisterAndLoginV2;
@BeforeAll
static void initJwtConfig() {
setStaticField(JWTUtils.class, "secret", "dGVzdC1zZWNyZXQ=");
setStaticField(JWTUtils.class, "serviceExpireTime", 60);
}
@Test
void memberRegisterAndLoginV2_shouldCreateMemberAndWallet_whenXiXiaoMerchantFirstLogin() {
RedisCache redisCache = mock(RedisCache.class);
TransactionService transactionService = mock(TransactionService.class);
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
when(redisCache.lock(anyString(), anyString(), eq(60))).thenReturn(true);
when(memberBasicInfoService.selectInfoByMobileNumber("13800138000", MerchantUtils.XIXIAO_MERCHANT_ID)).thenReturn(null);
when(memberBasicInfoService.queryMemberInfoByMemberId(anyString())).thenReturn(null);
when(memberBasicInfoService.generateWalletCode()).thenReturn("WALLET-001");
MemberService service = newMemberService(redisCache, transactionService, memberBasicInfoService);
MemberRegisterAndLoginDTO dto = MemberRegisterAndLoginDTO.builder()
.mobileNumber("13800138000")
.firstLevelMerchantId(MerchantUtils.XIXIAO_MERCHANT_ID)
.openId("wx-open-id")
.requestSource(AdapayPayChannelEnum.WX_LITE.getValue())
.build();
String memberToken = service.memberRegisterAndLoginV2(dto);
assertNotNull(memberToken);
ArgumentCaptor<MemberTransactionDTO> captor = ArgumentCaptor.forClass(MemberTransactionDTO.class);
verify(transactionService).createMember(captor.capture());
MemberTransactionDTO memberTransactionDTO = captor.getValue();
assertNotNull(memberTransactionDTO.getMemberBasicInfo());
assertEquals("13800138000", memberTransactionDTO.getMemberBasicInfo().getMobileNumber());
assertEquals("wx-open-id", memberTransactionDTO.getMemberBasicInfo().getOpenId());
assertNotNull(memberTransactionDTO.getMemberWalletInfo());
assertEquals(MerchantUtils.XIXIAO_MERCHANT_ID, memberTransactionDTO.getMemberWalletInfo().getMerchantId());
assertEquals("WALLET-001", memberTransactionDTO.getMemberWalletInfo().getWalletCode());
verify(memberBasicInfoService, never()).updateMemberBasicInfo(any(MemberBasicInfo.class));
}
@Test
void memberRegisterAndLoginV2_shouldUpdateOpenId_whenExistingWechatMemberOpenIdChanged() {
RedisCache redisCache = mock(RedisCache.class);
TransactionService transactionService = mock(TransactionService.class);
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
when(redisCache.lock(anyString(), anyString(), eq(60))).thenReturn(true);
MemberBasicInfo memberBasicInfo = MemberBasicInfo.builder()
.memberId("M001")
.nickName("会员M001")
.mobileNumber("13800138001")
.merchantId(99L)
.openId("old-open-id")
.build();
when(memberBasicInfoService.selectInfoByMobileNumber("13800138001", "99")).thenReturn(memberBasicInfo);
MemberService service = newMemberService(redisCache, transactionService, memberBasicInfoService);
MemberRegisterAndLoginDTO dto = MemberRegisterAndLoginDTO.builder()
.mobileNumber("13800138001")
.firstLevelMerchantId("99")
.openId("new-open-id")
.requestSource(AdapayPayChannelEnum.WX_LITE.getValue())
.build();
String memberToken = service.memberRegisterAndLoginV2(dto);
assertNotNull(memberToken);
assertEquals("new-open-id", memberBasicInfo.getOpenId());
verify(memberBasicInfoService).updateMemberBasicInfo(memberBasicInfo);
verify(transactionService, never()).createMember(any(MemberTransactionDTO.class));
}
@Test
void memberRegisterAndLoginV2_shouldThrow_whenRegisterLockNotAcquired() {
RedisCache redisCache = mock(RedisCache.class);
TransactionService transactionService = mock(TransactionService.class);
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
when(redisCache.lock(anyString(), anyString(), eq(60))).thenReturn(false);
MemberService service = newMemberService(redisCache, transactionService, memberBasicInfoService);
MemberRegisterAndLoginDTO dto = MemberRegisterAndLoginDTO.builder()
.mobileNumber("13800138002")
.firstLevelMerchantId("88")
.openId("wx-open-id")
.requestSource(AdapayPayChannelEnum.WX_LITE.getValue())
.build();
BusinessException exception = assertThrows(BusinessException.class, () -> service.memberRegisterAndLoginV2(dto));
assertEquals(ReturnCodeEnum.CODE_MEMBER_REGISTER_AND_LOGIN_PROCESSING.getValue(), exception.getCode());
verify(memberBasicInfoService, never()).selectInfoByMobileNumber(anyString(), anyString());
verify(redisCache, never()).unLock(CacheConstants.USER_APP_REGISTER + "13800138002:88");
verify(transactionService, never()).createMember(any(MemberTransactionDTO.class));
}
@Test
void memberRegisterAndLoginV2_shouldMatchOriginal_whenExistingAlipayMemberBuyerIdChanged() {
exerciseExistingAlipayBuyerIdUpdate(OLD_LOGIN);
exerciseExistingAlipayBuyerIdUpdate(V2_LOGIN);
}
@Test
void memberRegisterAndLoginV2_shouldMatchOriginal_whenDuplicateKeyHappensDuringRegister() {
exerciseDuplicateKeyFallback(OLD_LOGIN);
exerciseDuplicateKeyFallback(V2_LOGIN);
}
@Test
void memberRegisterAndLoginV2_shouldMatchOriginal_whenLockNotAcquired() {
exerciseLockFailure(OLD_LOGIN);
exerciseLockFailure(V2_LOGIN);
}
private static void exerciseExistingAlipayBuyerIdUpdate(LoginInvoker invoker) {
RedisCache redisCache = mock(RedisCache.class);
TransactionService transactionService = mock(TransactionService.class);
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
when(redisCache.lock(anyString(), anyString(), eq(60))).thenReturn(true);
MemberBasicInfo memberBasicInfo = MemberBasicInfo.builder()
.memberId("A001")
.nickName("会员A001")
.mobileNumber("13900139000")
.merchantId(66L)
.buyerId("old-buyer-id")
.build();
when(memberBasicInfoService.selectInfoByMobileNumber("13900139000", "66")).thenReturn(memberBasicInfo);
MemberService service = newMemberService(redisCache, transactionService, memberBasicInfoService);
MemberRegisterAndLoginDTO dto = MemberRegisterAndLoginDTO.builder()
.mobileNumber("13900139000")
.firstLevelMerchantId("66")
.buyerId("new-buyer-id")
.requestSource(AdapayPayChannelEnum.ALIPAY_LITE.getValue())
.build();
String memberToken = invoker.invoke(service, dto);
assertNotNull(memberToken);
assertEquals("new-buyer-id", memberBasicInfo.getBuyerId());
verify(memberBasicInfoService).updateMemberBasicInfo(memberBasicInfo);
verify(transactionService, never()).createMember(any(MemberTransactionDTO.class));
}
private static void exerciseDuplicateKeyFallback(LoginInvoker invoker) {
RedisCache redisCache = mock(RedisCache.class);
TransactionService transactionService = mock(TransactionService.class);
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
when(redisCache.lock(anyString(), anyString(), eq(60))).thenReturn(true);
MemberBasicInfo existingMember = MemberBasicInfo.builder()
.memberId("D001")
.nickName("会员D001")
.mobileNumber("13700137000")
.merchantId(77L)
.openId("wx-dup-open-id")
.build();
when(memberBasicInfoService.selectInfoByMobileNumber("13700137000", "77")).thenReturn(null, existingMember);
when(memberBasicInfoService.queryMemberInfoByMemberId(anyString())).thenReturn(null);
doThrow(new DuplicateKeyException("duplicate key")).when(transactionService).createMember(any(MemberTransactionDTO.class));
MemberService service = newMemberService(redisCache, transactionService, memberBasicInfoService);
MemberRegisterAndLoginDTO dto = MemberRegisterAndLoginDTO.builder()
.mobileNumber("13700137000")
.firstLevelMerchantId("77")
.openId("wx-dup-open-id")
.requestSource(AdapayPayChannelEnum.WX_LITE.getValue())
.build();
String memberToken = invoker.invoke(service, dto);
assertNotNull(memberToken);
verify(transactionService).createMember(any(MemberTransactionDTO.class));
verify(memberBasicInfoService, times(2)).selectInfoByMobileNumber("13700137000", "77");
verify(memberBasicInfoService, never()).updateMemberBasicInfo(any(MemberBasicInfo.class));
}
private static void exerciseLockFailure(LoginInvoker invoker) {
RedisCache redisCache = mock(RedisCache.class);
TransactionService transactionService = mock(TransactionService.class);
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
when(redisCache.lock(anyString(), anyString(), eq(60))).thenReturn(false);
MemberService service = newMemberService(redisCache, transactionService, memberBasicInfoService);
MemberRegisterAndLoginDTO dto = MemberRegisterAndLoginDTO.builder()
.mobileNumber("13600136000")
.firstLevelMerchantId("55")
.openId("wx-lock-open-id")
.requestSource(AdapayPayChannelEnum.WX_LITE.getValue())
.build();
BusinessException exception = assertThrows(BusinessException.class, () -> invoker.invoke(service, dto));
assertEquals(ReturnCodeEnum.CODE_MEMBER_REGISTER_AND_LOGIN_PROCESSING.getValue(), exception.getCode());
verify(memberBasicInfoService, never()).selectInfoByMobileNumber(anyString(), anyString());
verify(transactionService, never()).createMember(any(MemberTransactionDTO.class));
}
private static MemberService newMemberService(RedisCache redisCache,
TransactionService transactionService,
MemberBasicInfoService memberBasicInfoService) {
AlipayClientFactory alipayClientFactory = mock(AlipayClientFactory.class);
when(alipayClientFactory.getAlipayClient()).thenReturn(mock(AlipayClient.class));
MemberService service = new MemberService(alipayClientFactory);
setField(service, "redisCache", redisCache);
setField(service, "transactionService", transactionService);
setField(service, "memberBasicInfoService", memberBasicInfoService);
return service;
}
private static void setField(Object target, String fieldName, Object value) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void setStaticField(Class<?> targetClass, String fieldName, Object value) {
try {
Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(null, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@FunctionalInterface
private interface LoginInvoker {
String invoke(MemberService service, MemberRegisterAndLoginDTO dto);
}
}

View File

@@ -0,0 +1,144 @@
package com.jsowell.service;
import com.jsowell.pile.dto.BatchImportMemberBalanceResultDTO;
import com.jsowell.common.enums.MemberWalletEnum;
import com.jsowell.pile.domain.MemberBasicInfo;
import com.jsowell.pile.dto.ImportMemberBalanceDTO;
import com.jsowell.pile.dto.ImportMemberBalanceItemResultDTO;
import com.jsowell.pile.dto.MemberRegisterAndLoginDTO;
import com.jsowell.pile.service.MemberBasicInfoService;
import com.jsowell.pile.vo.web.UpdateMemberBalanceDTO;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
class TempServiceImportMemberBalanceTest {
@Test
void importMemberBalance_shouldIncreaseWalletBalance_whenMemberAlreadyExists() throws Exception {
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
MemberService memberService = mock(MemberService.class);
MemberBasicInfo memberBasicInfo = MemberBasicInfo.builder()
.memberId("M1001")
.mobileNumber("13800000001")
.merchantId(1L)
.build();
when(memberBasicInfoService.selectInfoByMobileNumber("13800000001", "1")).thenReturn(memberBasicInfo);
TempService tempService = newTempService(memberBasicInfoService, memberService);
ImportMemberBalanceDTO dto = new ImportMemberBalanceDTO();
dto.setPhone("13800000001");
dto.setBalance(new BigDecimal("88.50"));
invokeImportMemberBalance(tempService, dto);
verify(memberService, never()).memberRegisterAndLoginV2(any(MemberRegisterAndLoginDTO.class));
ArgumentCaptor<UpdateMemberBalanceDTO> captor = ArgumentCaptor.forClass(UpdateMemberBalanceDTO.class);
verify(memberBasicInfoService).updateMemberBalance(captor.capture());
UpdateMemberBalanceDTO updateDto = captor.getValue();
assertEquals("M1001", updateDto.getMemberId());
assertEquals(MemberWalletEnum.TYPE_IN.getValue(), updateDto.getType());
assertEquals(MemberWalletEnum.SUBTYPE_TOP_UP.getValue(), updateDto.getSubType());
assertEquals(new BigDecimal("88.50"), updateDto.getUpdatePrincipalBalance());
assertEquals("575", updateDto.getTargetMerchantId());
}
@Test
void importMemberBalance_shouldRegisterMemberBeforeIncreaseBalance_whenMemberDoesNotExist() throws Exception {
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
MemberService memberService = mock(MemberService.class);
MemberBasicInfo memberBasicInfo = MemberBasicInfo.builder()
.memberId("M2002")
.mobileNumber("13800000002")
.merchantId(1L)
.build();
when(memberBasicInfoService.selectInfoByMobileNumber("13800000002", "1")).thenReturn(null, memberBasicInfo);
TempService tempService = newTempService(memberBasicInfoService, memberService);
ImportMemberBalanceDTO dto = new ImportMemberBalanceDTO();
dto.setPhone("13800000002");
dto.setBalance(new BigDecimal("100"));
invokeImportMemberBalance(tempService, dto);
ArgumentCaptor<MemberRegisterAndLoginDTO> loginCaptor = ArgumentCaptor.forClass(MemberRegisterAndLoginDTO.class);
verify(memberService).memberRegisterAndLoginV2(loginCaptor.capture());
MemberRegisterAndLoginDTO loginDTO = loginCaptor.getValue();
assertEquals("13800000002", loginDTO.getMobileNumber());
assertEquals("1", loginDTO.getFirstLevelMerchantId());
ArgumentCaptor<UpdateMemberBalanceDTO> balanceCaptor = ArgumentCaptor.forClass(UpdateMemberBalanceDTO.class);
verify(memberBasicInfoService).updateMemberBalance(balanceCaptor.capture());
assertEquals("M2002", balanceCaptor.getValue().getMemberId());
assertEquals("575", balanceCaptor.getValue().getTargetMerchantId());
assertEquals(new BigDecimal("100"), balanceCaptor.getValue().getUpdatePrincipalBalance());
}
@Test
void batchImportMemberBalance_shouldReturnSummary_whenMixedRecords() {
MemberBasicInfoService memberBasicInfoService = mock(MemberBasicInfoService.class);
MemberService memberService = mock(MemberService.class);
MemberBasicInfo memberBasicInfo = MemberBasicInfo.builder()
.memberId("M3003")
.mobileNumber("13800000003")
.merchantId(1L)
.build();
when(memberBasicInfoService.selectInfoByMobileNumber("13800000003", "1")).thenReturn(memberBasicInfo);
TempService tempService = newTempService(memberBasicInfoService, memberService);
ImportMemberBalanceDTO successDto = new ImportMemberBalanceDTO();
successDto.setPhone("13800000003");
successDto.setBalance(new BigDecimal("50"));
ImportMemberBalanceDTO failDto = new ImportMemberBalanceDTO();
failDto.setPhone("");
failDto.setBalance(new BigDecimal("20"));
BatchImportMemberBalanceResultDTO result = tempService.batchImportMemberBalance(Arrays.asList(successDto, failDto));
assertNotNull(result);
assertEquals(2, result.getTotalCount());
assertEquals(1, result.getSuccessCount());
assertEquals(1, result.getFailCount());
assertEquals(1, result.getFailedList().size());
ImportMemberBalanceItemResultDTO failedItem = result.getFailedList().get(0);
assertEquals("", failedItem.getPhone());
}
private static TempService newTempService(MemberBasicInfoService memberBasicInfoService, MemberService memberService) {
TempService tempService = new TempService();
setField(tempService, "memberBasicInfoService", memberBasicInfoService);
setField(tempService, "memberService", memberService);
return tempService;
}
private static void invokeImportMemberBalance(TempService tempService, ImportMemberBalanceDTO dto) throws Exception {
Method method = TempService.class.getDeclaredMethod("importMemberBalance", ImportMemberBalanceDTO.class);
method.setAccessible(true);
method.invoke(tempService, dto);
}
private static void setField(Object target, String fieldName, Object value) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}