This commit is contained in:
jsowell
2026-05-14 10:51:39 +08:00
parent 5e35a8c82d
commit 02c7a069cc
4 changed files with 282 additions and 11 deletions

View File

@@ -21,6 +21,7 @@ import com.jsowell.common.constant.Constants;
import com.jsowell.common.constant.RabbitConstants; import com.jsowell.common.constant.RabbitConstants;
import com.jsowell.common.core.domain.ykc.TransactionRecordsData; import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
import com.jsowell.common.core.redis.RedisCache; import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.enums.MemberWalletEnum;
import com.jsowell.common.enums.adapay.AdapayStatusEnum; import com.jsowell.common.enums.adapay.AdapayStatusEnum;
import com.jsowell.common.enums.adapay.MerchantDelayModeEnum; import com.jsowell.common.enums.adapay.MerchantDelayModeEnum;
import com.jsowell.common.enums.ykc.*; import com.jsowell.common.enums.ykc.*;
@@ -65,6 +66,10 @@ import java.util.stream.Collectors;
@Service @Service
public class TempService { public class TempService {
private static final String JSOWELL_MEMBER_MERCHANT_ID = "1";
private static final String NANTONG_CHENMING_WALLET_MERCHANT_ID = "575";
private Logger logger = LoggerFactory.getLogger(this.getClass()); private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired @Autowired
@@ -1557,22 +1562,105 @@ public class TempService {
/** /**
* 从文件中导入会员余额 * 从文件中导入会员余额
*/ */
public void batchImportMemberBalance(List<ImportMemberBalanceDTO> list) { public BatchImportMemberBalanceResultDTO batchImportMemberBalance(List<ImportMemberBalanceDTO> list) {
BatchImportMemberBalanceResultDTO result = new BatchImportMemberBalanceResultDTO();
if (CollectionUtils.isEmpty(list)) { if (CollectionUtils.isEmpty(list)) {
return; return result;
}
result.setTotalCount(list.size());
List<ImportMemberBalanceItemResultDTO> itemResults = list.parallelStream()
.map(this::importMemberBalanceSafely)
.collect(Collectors.toList());
long successCount = itemResults.stream().filter(ImportMemberBalanceItemResultDTO::isSuccess).count();
List<ImportMemberBalanceItemResultDTO> failedList = itemResults.stream()
.filter(item -> !item.isSuccess())
.collect(Collectors.toList());
result.setSuccessCount((int) successCount);
result.setFailCount(failedList.size());
result.setFailedList(failedList);
logger.info("批量导入会员余额完成, totalCount:{}, successCount:{}, failCount:{}",
result.getTotalCount(), result.getSuccessCount(), result.getFailCount());
return result;
}
private ImportMemberBalanceItemResultDTO importMemberBalanceSafely(ImportMemberBalanceDTO memberBalanceDTO) {
String phone = memberBalanceDTO != null ? memberBalanceDTO.getPhone() : null;
try {
importMemberBalance(memberBalanceDTO);
return ImportMemberBalanceItemResultDTO.success(phone);
} catch (Exception e) {
logger.error("导入会员余额失败, phone:{}, param:{}", phone, JSON.toJSONString(memberBalanceDTO), e);
return ImportMemberBalanceItemResultDTO.fail(phone, e.getMessage());
} }
list.parallelStream().forEach(this::importMemberBalance);
} }
private void importMemberBalance(ImportMemberBalanceDTO memberBalanceDTO) { private void importMemberBalance(ImportMemberBalanceDTO memberBalanceDTO) {
// 1. 根据手机号查询万车充会员信息 if (memberBalanceDTO == null) {
MemberBasicInfo memberBasicInfo = memberBasicInfoService.selectInfoByMobileNumber(memberBalanceDTO.getPhone(), "1"); throw new BusinessException("", "导入会员余额失败,参数不能为空");
// 2. 如果没有万车充会员信息,则自动注册万车充会员,并创建“南通晨鸣中锦置业有限责任公司”运营商钱包 }
if (memberBasicInfo == null) { String phone = memberBalanceDTO.getPhone();
BigDecimal balance = memberBalanceDTO.getBalance();
if (StringUtils.isBlank(phone)) {
throw new BusinessException("", "导入会员余额失败,手机号不能为空");
}
if (balance == null || balance.compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("", "导入会员余额失败余额必须大于0");
} }
// 3. 根据balance添加余额 // 1. 根据手机号查询万车充会员信息;不存在则静默注册一个属于万车充体系的会员。
} MemberBasicInfo memberBasicInfo = findOrCreateJsowellMember(phone);
}
// 2. 给“南通晨鸣中锦置业有限责任公司”运营商钱包增加本金余额。
increaseMemberWalletBalance(memberBasicInfo.getMemberId(), balance);
logger.info("导入会员余额成功, phone:{}, memberId:{}, merchantId:{}, balance:{}",
phone, memberBasicInfo.getMemberId(), NANTONG_CHENMING_WALLET_MERCHANT_ID, balance);
}
/**
* 根据手机号查询万车充会员;若不存在,则自动注册后重新查询。
*/
private MemberBasicInfo findOrCreateJsowellMember(String phone) {
MemberBasicInfo memberBasicInfo = memberBasicInfoService.selectInfoByMobileNumber(phone, JSOWELL_MEMBER_MERCHANT_ID);
if (memberBasicInfo != null) {
return memberBasicInfo;
}
logger.info("导入会员余额时未查询到万车充会员,开始自动注册, phone:{}, merchantId:{}",
phone, JSOWELL_MEMBER_MERCHANT_ID);
MemberRegisterAndLoginDTO loginDTO = MemberRegisterAndLoginDTO.builder()
.mobileNumber(phone)
.firstLevelMerchantId(JSOWELL_MEMBER_MERCHANT_ID)
.build();
memberService.memberRegisterAndLoginV2(loginDTO);
memberBasicInfo = memberBasicInfoService.selectInfoByMobileNumber(phone, JSOWELL_MEMBER_MERCHANT_ID);
if (memberBasicInfo == null) {
logger.error("导入会员余额时自动注册后仍未查询到会员, phone:{}, merchantId:{}",
phone, JSOWELL_MEMBER_MERCHANT_ID);
throw new BusinessException(ReturnCodeEnum.CODE_MEMBER_REGISTER_AND_LOGIN_ERROR);
}
return memberBasicInfo;
}
/**
* 给指定会员在目标运营商钱包中增加本金余额。
*
* <p>如果该运营商钱包不存在,现有余额逻辑会自动创建钱包并记录流水。</p>
*/
private void increaseMemberWalletBalance(String memberId, BigDecimal balance) {
UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
.memberId(memberId)
.type(MemberWalletEnum.TYPE_IN.getValue())
.subType(MemberWalletEnum.SUBTYPE_TOP_UP.getValue())
.updatePrincipalBalance(balance)
.targetMerchantId(NANTONG_CHENMING_WALLET_MERCHANT_ID)
.build();
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
}
}

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);
}
}
}

View File

@@ -0,0 +1,14 @@
package com.jsowell.pile.dto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class BatchImportMemberBalanceResultDTO {
private int totalCount;
private int successCount;
private int failCount;
private List<ImportMemberBalanceItemResultDTO> failedList = new ArrayList<>();
}

View File

@@ -0,0 +1,25 @@
package com.jsowell.pile.dto;
import lombok.Data;
@Data
public class ImportMemberBalanceItemResultDTO {
private boolean success;
private String phone;
private String errorMessage;
public static ImportMemberBalanceItemResultDTO success(String phone) {
ImportMemberBalanceItemResultDTO result = new ImportMemberBalanceItemResultDTO();
result.setSuccess(true);
result.setPhone(phone);
return result;
}
public static ImportMemberBalanceItemResultDTO fail(String phone, String errorMessage) {
ImportMemberBalanceItemResultDTO result = new ImportMemberBalanceItemResultDTO();
result.setSuccess(false);
result.setPhone(phone);
result.setErrorMessage(errorMessage);
return result;
}
}