Commit f47a08f6 by zhangxingmin

Merge remote-tracking branch 'origin/test' into test

parents 5bd40c00 a556208c
......@@ -32,6 +32,7 @@ import com.yd.csf.service.service.*;
import com.yd.csf.service.vo.PolicyFollowDetailVO;
import com.yd.csf.service.vo.PolicyFollowRecordVO;
import com.yd.csf.service.vo.PolicyFollowVO;
import com.yd.csf.service.vo.PolicyNumberResponseVO;
import com.yd.feign.config.FeignTokenInterceptor;
import com.yd.insurance.base.feign.client.insurancereconciliationcompany.ApiInsuranceReconciliationCompanyFeignClient;
import com.yd.insurance.base.feign.request.insurancereconciliationcompany.ApiInsuranceReconciliationCompanyPageRequest;
......@@ -55,7 +56,6 @@ import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
......@@ -528,11 +528,19 @@ public class ApiPolicyFollowController {
*/
@PostMapping("/change_status")
@Operation(summary = "修改跟进状态")
@Transactional(rollbackFor = Exception.class)
public Result<Boolean> changePolicyFollowStatus(@RequestBody ChangePolicyFollowStatusRequest changePolicyFollowStatusRequest,
HttpServletRequest request) {
if (changePolicyFollowStatusRequest == null || StringUtils.isBlank(changePolicyFollowStatusRequest.getPolicyBizId())) {
return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "policyBizId不能为空");
}
if (ObjectUtils.isEmpty(changePolicyFollowStatusRequest.getEffectiveDate())) {
return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "生效日期不能为空");
}
if (ObjectUtils.isEmpty(changePolicyFollowStatusRequest.getUnderwritingDate())) {
return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "核保日期不能为空");
}
String policyBizId = changePolicyFollowStatusRequest.getPolicyBizId();
PolicyFollow policyFollow = policyFollowService.getByPolicyBizId(policyBizId);
if (policyFollow == null) {
......@@ -565,29 +573,9 @@ public class ApiPolicyFollowController {
if (PolicyFollowStatusEnum.EFFECTIVE.equals(currentStatusEnum)) {
// 获取Token
String token = request.getHeader("Authorization");
log.info("修改跟进状态获取token:{}",token);
// 异步:根据保单生成预计出账记录
CompletableFuture.runAsync(() -> {
try {
// 设置ThreadLocal Token
FeignTokenInterceptor.setThreadLocalToken(token);
// 调用 ApiExpectedFortuneService 的 generateWithLogAndRedis(包含日志和Redis处理)
ApiGenerateExpectedFortuneRequest generateExpectedFortuneRequest = new ApiGenerateExpectedFortuneRequest();
generateExpectedFortuneRequest.setPolicyNo(policyFollow.getPolicyNo());
log.info("生成预计发佣generateWithLogAndRedis——>PolicyNo:{}",policyFollow.getPolicyNo());
Result<ApiGenerateExpectedFortuneResponse> result = apiExpectedFortuneService.generateWithLogAndRedis(generateExpectedFortuneRequest);
if (result == null || result.getCode() != 200) {
log.error("新单跟进-异步生成预计出账记录失败:保单号={}, 响应结果={}",
policyFollow.getPolicyNo(), result);
} else {
log.info("新单跟进-异步生成预计出账记录成功:保单号={}", policyFollow.getPolicyNo());
}
} catch (Exception e) {
log.error("新单跟进-异步生成预计出账记录失败:保单号={}, 错误信息={}",
policyFollow.getPolicyNo(), e.getMessage(), e);
}
});
log.info("修改跟进状态获取token:{}", token);
// 同步:根据保单生成预计出账记录
generateExpectedFortuneSync(token, policyFollow.getPolicyNo());
}
return Result.success(true);
}
......@@ -728,8 +716,27 @@ public class ApiPolicyFollowController {
*/
@PostMapping("/policyNos")
@Operation(summary = "查询保单号列表")
public Result<List<String>> queryPolicyNumbers(@RequestBody PolicyNosQueryRequest policyNosQueryRequest) {
List<String> policyNumbers = policyFollowService.queryPolicyNumbers(policyNosQueryRequest.getPolicyNo());
return Result.success(policyNumbers);
public Result<Page<PolicyNumberResponseVO>> queryPolicyNumbers(@RequestBody PolicyNosQueryRequest policyNosQueryRequest) {
return Result.success(policyFollowService.queryPolicyNumbers(policyNosQueryRequest));
}
/**
* 同步生成预计出账记录
*
* @param token 认证Token
* @param policyNo 保单号
*/
private void generateExpectedFortuneSync(String token, String policyNo) {
// 设置ThreadLocal Token
FeignTokenInterceptor.setThreadLocalToken(token);
// 调用 ApiExpectedFortuneService 的 generateSyncWithLogAndRedis(同步版本,包含日志和Redis处理)
ApiGenerateExpectedFortuneRequest generateExpectedFortuneRequest = new ApiGenerateExpectedFortuneRequest();
generateExpectedFortuneRequest.setPolicyNo(policyNo);
log.info("生成预计发佣generateSyncWithLogAndRedis——>PolicyNo:{}", policyNo);
Result<ApiGenerateExpectedFortuneResponse> result = apiExpectedFortuneService.generateSyncWithLogAndRedis(generateExpectedFortuneRequest);
if (result != null && result.getCode() == 200) {
log.info("新单跟进-同步生成预计出账记录成功:保单号={}", policyNo);
}
}
}
\ No newline at end of file
......@@ -32,11 +32,24 @@ public interface ApiExpectedFortuneService {
*/
Result<ApiGenerateExpectedFortuneResponse> generateWithLogAndRedis(ApiGenerateExpectedFortuneRequest request);
Result<ApiGenerateExpectedFortuneResponse> generateSyncWithLogAndRedis(ApiGenerateExpectedFortuneRequest request);
@Async("asyncQueryExecutor")
Result execute(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
List<CommissionRuleBinding> commissionRuleBindingList,
String policyNo);
/**
* 同步执行预计发佣生成
* @param queryPolicyAndBrokerDtoList 保单和转介人列表
* @param commissionRuleBindingList 基本法绑定列表
* @param policyNo 保单号
* @return 执行结果
*/
Result executeSync(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
List<CommissionRuleBinding> commissionRuleBindingList,
String policyNo);
Result<IPage<ApiExpectedFortunePageResponse>> page(ApiExpectedFortunePageRequest request);
QueryWrapper<ExpectedFortune> getQueryWrapper(ApiExpectedFortunePageRequest request);
......
......@@ -142,6 +142,51 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
}
/**
* 同步生成预计发佣
*
* @param request
* @return
*/
public Result<ApiGenerateExpectedFortuneResponse> generateSync(ApiGenerateExpectedFortuneRequest request) {
//查询当前保单号是否正在执行预计发佣的缓存,有值就说明正在执行,无值说明执行完毕或者没有执行。
String value = redisUtil.getCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
if (StringUtil.isNotBlank(value)) {
//有值正在执行,提示
return Result.fail("生成预计发佣正在执行中,无需再次执行");
} else {
//无值设置值,表示正在执行
redisUtil.setCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo(), request.getPolicyNo());
}
List<ExpectedFortune> list = iExpectedFortuneService.queryList(request.getPolicyNo());
if (!CollectionUtils.isEmpty(list)) {
throw new BusinessException("当前保单已经生成过预计发佣数据,无需再次生成");
}
//根据保单号查询保单和转介人列表信息
List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList = policyService.queryPolicyBrokerList(request.getPolicyNo());
if (CollectionUtils.isEmpty(queryPolicyAndBrokerDtoList)) {
throw new BusinessException("保单和转介人信息不存在");
}
//查询所有绑定基本法的业务员(客户端用户表用户), 计算业务员绑定的所有基本法对应的计算值, 新增积分明细和发佣记录
List<CommissionRuleBinding> commissionRuleBindingList = new ArrayList<>();
//根据租户ID和项目ID查询客户端用户ID列表
Result<List<String>> result = apiClientUserFeignClient.clientUserBizIdList(TenantEnum.YD.getTenantBizId(), ProjectEnum.CSF_MINI_PROGRAM.getProjectBizId());
if (!CollectionUtils.isEmpty(result.getData())) {
commissionRuleBindingList = iCommissionRuleBindingService.queryList(CommissionRuleBindingDto.builder()
.targetIdList(result.getData())
.build());
}
if (CollectionUtils.isEmpty(commissionRuleBindingList)) {
throw new BusinessException("绑定基本法数据不存在");
}
//遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表(同步执行)
executeSync(queryPolicyAndBrokerDtoList, commissionRuleBindingList, request.getPolicyNo());
return Result.success(null, "生成预计发佣成功");
}
/**
* 生成预计发佣(包含日志记录和Redis处理)
* 用于内部服务调用,会自动保存日志和异常时销毁Redis缓存
*
......@@ -161,14 +206,63 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
return result;
} catch (Exception e) {
// 抛出异常,销毁redis缓存
redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
try {
redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
} catch (Exception redisEx) {
log.error("删除Redis缓存失败: {}", redisEx.getMessage(), redisEx);
}
// 保存预计发佣日志记录(失败)
try {
apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
.errorMsg(e.getMessage())
.policyNo(request.getPolicyNo())
.status(1)
.build());
} catch (Exception logEx) {
log.error("保存预计发佣日志失败: {}", logEx.getMessage(), logEx);
}
// 重新抛出原始异常,确保上层事务能够回滚
throw e;
}
}
/**
* 同步生成预计发佣(包含日志记录和Redis处理)
* 用于需要同步等待完成的场景
*
* @param request
* @return
*/
@Override
public Result<ApiGenerateExpectedFortuneResponse> generateSyncWithLogAndRedis(ApiGenerateExpectedFortuneRequest request) {
try {
Result<ApiGenerateExpectedFortuneResponse> result = generateSync(request);
// 保存预计发佣日志记录(成功)
apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
.errorMsg(e.getMessage())
.errorMsg(result.getMsg())
.policyNo(request.getPolicyNo())
.status(1)
.status(2)
.build());
return Result.fail(e.getMessage());
return result;
} catch (Exception e) {
// 抛出异常,销毁redis缓存
try {
redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
} catch (Exception redisEx) {
log.error("删除Redis缓存失败: {}", redisEx.getMessage(), redisEx);
}
// 保存预计发佣日志记录(失败)
try {
apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
.errorMsg(e.getMessage())
.policyNo(request.getPolicyNo())
.status(1)
.build());
} catch (Exception logEx) {
log.error("保存预计发佣日志失败: {}", logEx.getMessage(), logEx);
}
// 重新抛出原始异常,确保上层事务能够回滚
throw e;
}
}
......@@ -241,6 +335,74 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
});
}
/**
* 同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表
*
* @param queryPolicyAndBrokerDtoList
* @param commissionRuleBindingList
* @param policyNo
* @return
*/
public Result executeSync(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
List<CommissionRuleBinding> commissionRuleBindingList,
String policyNo) {
// 使用编程式事务,确保方法内的事务一致性
return transactionTemplate.execute(status -> {
try {
for (QueryPolicyAndBrokerDto brokerDto : queryPolicyAndBrokerDtoList) {
Integer paymentTerm = brokerDto.getPaymentTerm();
if (Objects.isNull(paymentTerm)) {
throw new BusinessException("保单的供款年期不存在");
}
for (int i = 1; i <= paymentTerm; i++) {
executeBilling(ExecuteBillingDto.builder()
.name(brokerDto.getBrokerName())
.policyAndBrokerDto(brokerDto)
.issueNumber(i)
.build());
for (CommissionRuleBinding binding : commissionRuleBindingList) {
executeReward(ExecuteBillingDto.builder()
.clientUserBizId(binding.getTargetId())
.name(binding.getTargetName())
.policyAndBrokerDto(brokerDto)
.issueNumber(i)
.build());
}
}
}
//批量设置应付款编号
updatePayableNoBatch(policyNo);
//执行成功完毕,也要销毁redis缓存
redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);
//保存预计发佣日志记录
//成功日志
apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
.errorMsg("")
.policyNo(policyNo)
.status(0)
.build());
return Result.success();
} catch (Exception e) {
//抛出异常,销毁redis缓存
redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);
status.setRollbackOnly(); // 标记回滚
log.error("同步执行预计发佣失败,已回滚所有操作", e);
//保存预计发佣日志记录
apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
.errorMsg(e.getMessage())
.policyNo(policyNo)
.status(1)
.build());
throw new BusinessException("同步处理失败: " + e.getMessage());
}
});
}
private void updatePayableNoBatch(String policyNo) {
// 查询最新一条有 payableNo 记录
ExpectedFortune latest = iExpectedFortuneService.getOne(
......
......@@ -3,6 +3,7 @@ package com.yd.csf.service.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
......@@ -21,6 +22,18 @@ public class ChangePolicyFollowStatusRequest {
private String status;
/**
* 生效日期
*/
@Schema(description = "生效日期")
private Date effectiveDate;
/**
* 核保日期
*/
@Schema(description = "核保日期")
private Date underwritingDate;
/**
* 跟进信息
*/
@Schema(description = "跟进信息")
......
package com.yd.csf.service.dto;
import com.yd.common.dto.PageDto;
import lombok.Data;
@Data
public class PolicyNosQueryRequest {
public class PolicyNosQueryRequest extends PageDto {
private String policyNo;
}
......@@ -4,9 +4,10 @@ import org.apache.commons.lang3.ObjectUtils;
public enum PolicyStatusEnum {
INFORCE("生效", "INFORCE"),
INFORCE("生效", "INFORCE"),
LAPSED("保单失效", "LAPSED"),
MATURED("期满终止", "MATURED"),
MATURED("退保", "MATURED"),
COOLING_PERIOD_MATURED("冷静期退保", "COOLING_PERIOD_MATURED"),
;
//字典项标签(名称)
private String itemLabel;
......
......@@ -8,6 +8,7 @@ import com.yd.csf.service.enums.PolicyFollowStatusEnum;
import com.yd.csf.service.model.PolicyFollow;
import com.yd.csf.service.vo.PolicyFollowDetailVO;
import com.yd.csf.service.vo.PolicyFollowVO;
import com.yd.csf.service.vo.PolicyNumberResponseVO;
import java.util.List;
import java.util.Map;
......@@ -78,8 +79,8 @@ public interface PolicyFollowService extends IService<PolicyFollow> {
/**
* 查询保单号列表
*
* @param policyNo 保单号
* @param policyNosQueryRequest 保单号查询请求
* @return 保单号列表
*/
List<String> queryPolicyNumbers(String policyNo);
Page<PolicyNumberResponseVO> queryPolicyNumbers(PolicyNosQueryRequest policyNosQueryRequest);
}
......@@ -20,14 +20,15 @@ import com.yd.csf.service.common.ErrorCode;
import com.yd.csf.service.dto.*;
import com.yd.csf.service.enums.FnaStatusEnum;
import com.yd.csf.service.enums.PolicyFollowStatusEnum;
import com.yd.csf.service.enums.PolicyStatusEnum;
import com.yd.csf.service.model.*;
import com.yd.csf.service.service.*;
import com.yd.csf.service.dao.PolicyFollowMapper;
import com.yd.csf.service.utils.AsyncQueryUtil;
import com.yd.csf.service.utils.GSONUtil;
import com.yd.csf.service.vo.PolicyAdditionalVO;
import com.yd.csf.service.vo.PolicyFollowDetailVO;
import com.yd.csf.service.vo.PolicyFollowVO;
import com.yd.csf.service.vo.PolicyNumberResponseVO;
import com.yd.insurance.base.feign.client.insurancereconciliationcompany.ApiInsuranceReconciliationCompanyFeignClient;
import com.yd.insurance.base.feign.request.insurancereconciliationcompany.ApiInsuranceReconciliationCompanyPageRequest;
import lombok.extern.slf4j.Slf4j;
......@@ -470,15 +471,20 @@ public class PolicyFollowServiceImpl extends ServiceImpl<PolicyFollowMapper, Pol
AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
String loginUserId = currentLoginUser.getId().toString();
policyFollow.setStatus(changePolicyFollowStatusRequest.getStatus());
policyFollow.setNextStatusList(getNextStatus(policyFollowStatusEnum));
policyFollow.setUpdaterId(loginUserId);
policyFollowService.updateById(policyFollow);
policyFollowService.lambdaUpdate()
.set(PolicyFollow::getEffectiveDate, changePolicyFollowStatusRequest.getEffectiveDate())
.set(PolicyFollow::getUnderwritingDate, changePolicyFollowStatusRequest.getUnderwritingDate())
.set(PolicyFollow::getStatus, changePolicyFollowStatusRequest.getStatus())
.set(PolicyFollow::getNextStatusList, getNextStatus(policyFollowStatusEnum))
.set(PolicyFollow::getUpdaterId, loginUserId)
.eq(PolicyFollow::getId, policyFollow.getId())
.update();
// 如果是生效状态,同步保单、预计发佣、预计来佣
if (PolicyFollowStatusEnum.EFFECTIVE.equals(policyFollowStatusEnum)) {
Policy policy = new Policy();
BeanUtils.copyProperties(policyFollow, policy, "id");
policy.setStatus(PolicyStatusEnum.INFORCE.getItemValue());
// 手动映射不同名的字段
policy.setPaymentPremium(policyFollow.getInitialPremium());
policy.setCurrency(policyFollow.getPolicyCurrency());
......@@ -814,15 +820,48 @@ public class PolicyFollowServiceImpl extends ServiceImpl<PolicyFollowMapper, Pol
}
@Override
public List<String> queryPolicyNumbers(String policyNo) {
public Page<PolicyNumberResponseVO> queryPolicyNumbers(PolicyNosQueryRequest policyNosQueryRequest) {
String policyNo = policyNosQueryRequest.getPolicyNo();
// 创建分页对象
Page<PolicyFollow> page = new Page<>(policyNosQueryRequest.getPageNo(), policyNosQueryRequest.getPageSize());
// 构建查询条件
QueryWrapper<PolicyFollow> queryWrapper = new QueryWrapper<>();
queryWrapper.select("policy_no");
queryWrapper.select("policy_no", "insurance_company", "insurance_company_biz_id", "insured", "insured_biz_id");
queryWrapper.like(ObjectUtils.isNotEmpty(policyNo), "policy_no", policyNo);
queryWrapper.isNotNull("policy_no");
return policyFollowService.list(queryWrapper)
.stream()
.map(PolicyFollow::getPolicyNo)
// 执行分页查询
Page<PolicyFollow> policyFollowPage = policyFollowService.page(page, queryWrapper);
// 转换结果
Page<PolicyNumberResponseVO> resultPage = new Page<>(policyFollowPage.getCurrent(), policyFollowPage.getSize(), policyFollowPage.getTotal());
List<PolicyNumberResponseVO> voList = policyFollowPage.getRecords().stream()
.map(this::convertToPolicyNumberResponseVO)
.collect(Collectors.toList());
resultPage.setRecords(voList);
return resultPage;
}
/**
* 转换为保单号查询响应视图
*
* @param policyFollow 新单跟进实体
* @return 保单号查询响应视图
*/
private PolicyNumberResponseVO convertToPolicyNumberResponseVO(PolicyFollow policyFollow) {
if (policyFollow == null) {
return null;
}
PolicyNumberResponseVO vo = new PolicyNumberResponseVO();
vo.setPolicyNo(policyFollow.getPolicyNo());
vo.setInsuranceCompany(policyFollow.getInsuranceCompany());
vo.setInsuranceCompanyBizId(policyFollow.getInsuranceCompanyBizId());
vo.setInsured(policyFollow.getInsured());
vo.setInsuredBizId(policyFollow.getInsuredBizId());
return vo;
}
/**
......
......@@ -198,8 +198,6 @@ public class PolicyServiceImpl extends ServiceImpl<PolicyMapper, Policy>
return CollUtil.newArrayList();
}
try {
log.info("Feign返回数据: {}", JSONUtil.parseObj(result).toString());
// 直接使用Map来处理数据,避免复杂的类型转换
Object data = result.getData();
List<PolicyProductInfo> policyProductInfoList = new ArrayList<>();
......
......@@ -103,6 +103,12 @@ public class PolicyFollowVO implements Serializable {
private String productName;
/**
* 缴费年期
*/
@Schema(description = "缴费年期")
private Integer issueNumber;
/**
* 保险公司
*/
@Schema(description = "保险公司")
......
package com.yd.csf.service.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 保单号查询响应视图
*/
@Data
@Schema(description = "保单号查询响应")
public class PolicyNumberResponseVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 保单号
*/
@Schema(description = "保单号")
private String policyNo;
/**
* 保险公司
*/
@Schema(description = "保险公司")
private String insuranceCompany;
/**
* 保险公司业务id
*/
@Schema(description = "保险公司业务id")
private String insuranceCompanyBizId;
/**
* 受保人
*/
@Schema(description = "受保人")
private String insured;
/**
* 受保人业务id
*/
@Schema(description = "受保人业务id")
private String insuredBizId;
}
......@@ -68,8 +68,8 @@
else 0
end as paidRatio,
case when ifnull(sum(ef.hkd_amount), 0) > 0
then round(ifnull(sum(ef.unpaid_amount), 0) / ifnull(sum(ef.hkd_amount), 0) * 100, 2)
else 0
then round(100 - (ifnull(sum(ef.paid_amount), 0) / ifnull(sum(ef.hkd_amount), 0) * 100), 2)
else 100
end as unpaidRatio,
MAX(first_broker.broker_name) as broker,
DATE_ADD(DATE_ADD(MAX(p.effective_date), INTERVAL 2 MONTH), INTERVAL (ef.fortune_period - 1) YEAR) as payoutDate
......@@ -120,8 +120,8 @@
else 0
end as paidRatio,
case when ifnull(ef.hkd_amount, 0) > 0
then round(ifnull(ef.unpaid_amount, 0) / ifnull(ef.hkd_amount, 0) * 100, 2)
else 0
then round(100 - (ifnull(ef.paid_amount, 0) / ifnull(ef.hkd_amount, 0) * 100), 2)
else 100
end as unpaidRatio,
ef.broker as broker,
ef.payout_date as payoutDate
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment