Commit b1afc495 by zhangxingmin

push

parent 24017a14
package com.yd.csf.api.async;
import com.yd.csf.api.service.XxlJobService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 预计发佣-异步类
*/
@Slf4j
@Component
public class ApiExpectedFortuneAsyncService {
@Autowired
private XxlJobService xxlJobService;
@Value("${xxl.job.executor.calmAppName}")
private String calmAppName;
/**
* 创建XXL-Job定时任务-保单冷静期-异步方法
* @param taskBizId
* @param scheduleTime
*/
// @Async("jobTaskExecutor")
public void addCalmScheduleJob(String taskBizId, Date scheduleTime) {
String taskName = "冷静期定时任务";
String jobId = xxlJobService.addScheduleJob(taskName,taskBizId,calmAppName,scheduleTime);
log.info("创建XXL-Job定时任务成功-->jobId:{}",jobId);
}
}
package com.yd.csf.api.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 保单冷静期定时任务表 前端控制器
* </p>
*
* @author zxm
* @since 2026-03-06
*/
@RestController
@RequestMapping("/calmTask")
public class CalmTaskController {
}
package com.yd.csf.api.response;
import com.yd.csf.api.dto.AlgorithmResDto;
import com.yd.csf.api.dto.ExecuteBillingDto;
import lombok.Data;
import java.util.List;
@Data
public class ApiExecuteBillingResponse {
private ExecuteBillingDto dto;
private List<AlgorithmResDto> algorithmResDtoList;
}
package com.yd.csf.api.response;
import com.yd.csf.api.dto.AlgorithmResDto;
import com.yd.csf.api.dto.ExecuteBillingDto;
import lombok.Data;
import java.util.List;
@Data
public class ApiExecuteRewardResponse {
private ExecuteBillingDto dto;
private List<AlgorithmResDto> algorithmResDtoList;
}
......@@ -4,7 +4,10 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yd.common.result.Result;
import com.yd.csf.api.dto.ApiExpectedFortunePageResponseVO;
import com.yd.csf.api.dto.GenerateAgentDetailFycDto;
import com.yd.csf.api.dto.PayableReportResponse;
import com.yd.csf.api.response.ApiExecuteBillingResponse;
import com.yd.csf.api.response.ApiExecuteRewardResponse;
import com.yd.csf.feign.request.expectedfortune.ApiExpectedFortunePageRequest;
import com.yd.csf.feign.request.expectedfortune.ApiGenerateExpectedFortuneRequest;
import com.yd.csf.feign.request.expectedfortune.ExpectedFortuneAddRequest;
......@@ -16,6 +19,7 @@ import com.yd.csf.service.model.CommissionRuleBinding;
import com.yd.csf.service.model.ExpectedFortune;
import com.yd.csf.service.vo.ExpectedFortuneStatisticsVO;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
......@@ -62,6 +66,17 @@ public interface ApiExpectedFortuneService {
Boolean update(ExpectedFortuneUpdateRequest request);
@Async("commonAsyncExecutor")
@Transactional(rollbackFor = Exception.class)
void buildCalmTask(List<ApiExecuteBillingResponse> billingResponses,
List<ApiExecuteRewardResponse> rewardResponses,
List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
String policyNo);
@Async("commonAsyncExecutor")
@Transactional(rollbackFor = Exception.class)
Result generateAgentDetailFyc(GenerateAgentDetailFycDto dto);
Result<ApiExpectedFortunePageResponseVO> list(ApiExpectedFortunePageRequest request);
Boolean add(List<ExpectedFortuneAddRequest> request);
......
package com.yd.csf.api.service;
import java.util.Date;
public interface XxlJobService {
String addScheduleJob(String taskName, String taskBizId, String appName, Date scheduleTime);
}
......@@ -17,7 +17,11 @@ import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.common.utils.RedisUtil;
import com.yd.csf.api.async.ApiExpectedFortuneAsyncService;
import com.yd.csf.api.dto.*;
import com.yd.csf.api.response.ApiExecuteBillingResponse;
import com.yd.csf.api.response.ApiExecuteRewardResponse;
import com.yd.csf.api.service.ApiAgentDetailFycService;
import com.yd.csf.api.service.ApiBasicLawCalculateService;
import com.yd.csf.api.service.ApiExpectedFortuneLogService;
import com.yd.csf.api.service.ApiExpectedFortuneService;
......@@ -56,6 +60,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
......@@ -91,6 +96,12 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
private ApiSysDictFeignClient apiSysDictFeignClient;
@Resource
private PolicyFollowService policyFollowService;
@Autowired
private ApiAgentDetailFycService fycService;
@Autowired
private ApiExpectedFortuneAsyncService apiExpectedFortuneAsyncService;
@Autowired
private ICalmTaskService calmTaskService;
/**
* 生成预计发佣
......@@ -352,6 +363,8 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
// 使用编程式事务,确保方法内的事务一致性
return transactionTemplate.execute(status -> {
try {
List<ApiExecuteBillingResponse> billingResponses = new ArrayList<>();
List<ApiExecuteRewardResponse> rewardResponses = new ArrayList<>();
for (QueryPolicyAndBrokerDto brokerDto : queryPolicyAndBrokerDtoList) {
Integer paymentTerm = brokerDto.getPaymentTerm();
if (Objects.isNull(paymentTerm)) {
......@@ -361,20 +374,22 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
List<CommissionRuleBinding> commissionRuleBindingList = brokerRelUserIdList(brokerDto);
log.info("同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表->commissionRuleBindingList:{}",JSON.toJSONString(commissionRuleBindingList));
for (int i = 1; i <= paymentTerm; i++) {
executeBilling(ExecuteBillingDto.builder()
Result<List<ApiExecuteBillingResponse>> result = executeBilling(ExecuteBillingDto.builder()
.name(brokerDto.getBrokerName())
.policyAndBrokerDto(brokerDto)
.issueNumber(i)
.build());
billingResponses.addAll(result.getData());
if (!CollectionUtils.isEmpty(commissionRuleBindingList)) {
for (CommissionRuleBinding binding : commissionRuleBindingList) {
executeReward(ExecuteBillingDto.builder()
Result<List<ApiExecuteRewardResponse>> result1 = executeReward(ExecuteBillingDto.builder()
.clientUserBizId(binding.getTargetId())
.name(binding.getTargetName())
.policyAndBrokerDto(brokerDto)
.issueNumber(i)
.build());
rewardResponses.addAll(result1.getData());
}
}
}
......@@ -392,6 +407,10 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
.policyNo(policyNo)
.status(0)
.build());
//异步调用-计算当前转介人的获取所有基本法项目的佣金合值,存入保单冷静期定时任务表,构建保单冷静期定时任务
getSelf().buildCalmTask(billingResponses,rewardResponses,queryPolicyAndBrokerDtoList,policyNo);
return Result.success();
} catch (Exception e) {
//抛出异常,销毁redis缓存
......@@ -410,6 +429,92 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
}
/**
*
* 异步调用-计算当前转介人的获取所有基本法项目的佣金合值,存入保单冷静期定时任务表,构建保单冷静期定时任务
* @param billingResponses
* @param rewardResponses
* @param queryPolicyAndBrokerDtoList
* @param policyNo
*/
@Override
@Async("commonAsyncExecutor")
@Transactional(rollbackFor = Exception.class)
public void buildCalmTask(List<ApiExecuteBillingResponse> billingResponses,
List<ApiExecuteRewardResponse> rewardResponses,
List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
String policyNo) {
for (QueryPolicyAndBrokerDto brokerDto : queryPolicyAndBrokerDtoList) {
// 计算当前转介人的总佣金
BigDecimal totalFyc = BigDecimal.ZERO;
// 累加销售佣金(billingResponses 都是当前转介人自己的)
for (ApiExecuteBillingResponse response : billingResponses) {
if (response.getAlgorithmResDtoList() != null) {
for (AlgorithmResDto algo : response.getAlgorithmResDtoList()) {
if (algo.getCalculatedValue() != null) {
totalFyc = totalFyc.add(algo.getCalculatedValue());
}
}
}
}
// 累加奖励佣金(仅当获得积分用户是当前转介人时)
for (ApiExecuteRewardResponse response : rewardResponses) {
// 奖励的获得者是 response.getDto().getClientUserBizId()
if (response.getDto() != null
&& brokerDto.getBrokerBizId().equals(response.getDto().getClientUserBizId())
&& response.getAlgorithmResDtoList() != null) {
for (AlgorithmResDto algo : response.getAlgorithmResDtoList()) {
if (algo.getCalculatedValue() != null) {
totalFyc = totalFyc.add(algo.getCalculatedValue());
}
}
}
}
// 查询保单信息(获取 policyBizId 和 coolingOffDays)
Policy policy = policyService.lambdaQuery()
.eq(Policy::getPolicyNo, policyNo)
.one();
if (policy == null) {
log.error("保单不存在,policyNo: {}", policyNo);
return;
}
String policyBizId = policy.getPolicyBizId();
Integer coolingOffDays = policy.getCoolingOffDays();
// 构造 CalmTask 对象
CalmTask calmTask = new CalmTask();
calmTask.setCalmTaskBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_CALM_TASK.getCode()));
calmTask.setPolicyBizId(policyBizId);
calmTask.setPolicyNo(policyNo);
calmTask.setBrokerBizId(brokerDto.getBrokerBizId());
calmTask.setBrokerName(brokerDto.getBrokerName());
calmTask.setTotalFyc(totalFyc);
// coolingOffEndDate 从 brokerDto 获取,类型 LocalDate -> LocalDateTime(当天开始时间)
if (brokerDto.getCoolingOffEndDate() != null) {
calmTask.setCoolingOffEndDate(brokerDto.getCoolingOffEndDate().atStartOfDay());
}
calmTask.setCoolingOffDays(coolingOffDays);
// 执行时间 = 冷静期结束日期的当天开始时间(与冷却期结束时间一致或稍后,这里保持一致)
calmTask.setExecutionTime(brokerDto.getCoolingOffEndDate() != null ? brokerDto.getCoolingOffEndDate().atStartOfDay() : null);
calmTask.setExecutionStatus("0"); // 0-未执行
calmTask.setIsDeleted(0);
calmTask.setCreateTime(LocalDateTime.now());
// 保存 CalmTask
calmTaskService.save(calmTask);
// 异步创建 XXL-Job 定时任务
if (calmTask.getExecutionTime() != null) {
// 将 LocalDateTime 转换为 Date
Date scheduleTime = Date.from(calmTask.getExecutionTime().atZone(ZoneId.systemDefault()).toInstant());
apiExpectedFortuneAsyncService.addCalmScheduleJob(calmTask.getCalmTaskBizId(), scheduleTime);
}
}
}
/**
* 查询和当前转介人相关关系(推荐,一级管理,二级管理,辅导)的客户端用户ID列表数据
* @param brokerDto
* @return
......@@ -461,7 +566,8 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
* @return
*/
@Transactional(rollbackFor = Exception.class)
public Result executeReward(ExecuteBillingDto dto) {
public Result<List<ApiExecuteRewardResponse>> executeReward(ExecuteBillingDto dto) {
List<ApiExecuteRewardResponse> responses = new ArrayList<>();
QueryPolicyAndBrokerDto brokerDto = dto.getPolicyAndBrokerDto();
//构造销售佣金基本法项目的顺序下标值执行
List<Integer> executionOrderList = new ArrayList<>();
......@@ -484,6 +590,19 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
.isNegateExecutionOrderList(true)
.build());
log.info("执行获得积分用户的非销售佣金基本法项目->出参:{}",JSON.toJSONString(result));
ApiExecuteRewardResponse response = new ApiExecuteRewardResponse();
response.setAlgorithmResDtoList(result.getData());
response.setDto(dto);
responses.add(response);
//生成获得积分的用户的积分明细表记录
getSelf().generateAgentDetailFyc(GenerateAgentDetailFycDto.builder()
.agentId(dto.getClientUserBizId())
.provider(brokerDto.getBrokerBizId())
.policyNo(brokerDto.getPolicyNo())
.algorithmResDtoList(result.getData())
// .sourceType()
.build());
//生成保单预计发佣表记录 (非销售佣金基本法)
generateExpectedFortune(GenerateExpectedFortuneDto.builder()
......@@ -530,7 +649,8 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
* @return
*/
@Transactional(rollbackFor = Exception.class)
public Result executeBilling(ExecuteBillingDto dto) {
public Result<List<ApiExecuteBillingResponse>> executeBilling(ExecuteBillingDto dto) {
List<ApiExecuteBillingResponse> responses = new ArrayList<>();
log.info("执行 - 销售佣金(销售佣金基本法)——>入参:{}", JSON.toJSONString(dto));
QueryPolicyAndBrokerDto brokerDto = dto.getPolicyAndBrokerDto();
//构造销售佣金基本法项目的顺序下标值执行
......@@ -552,9 +672,22 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
.executionOrderList(executionOrderList)
.isNegateExecutionOrderList(false)
.build());
log.info("执行 - 销售佣金(销售佣金基本法)——>brokerDto:{}", JSON.toJSONString(brokerDto));
log.info("执行 - 销售佣金(销售佣金基本法)——>policyBrokerAlgorithm——>result:{}", JSON.toJSONString(result));
ApiExecuteBillingResponse response = new ApiExecuteBillingResponse();
response.setAlgorithmResDtoList(result.getData());
response.setDto(dto);
responses.add(response);
//生成业务员(转介人)积分明细表记录(销售积分)
getSelf().generateAgentDetailFyc(GenerateAgentDetailFycDto.builder()
.agentId(brokerDto.getBrokerBizId())
.policyNo(brokerDto.getPolicyNo())
.algorithmResDtoList(result.getData())
// .sourceType()
.build());
log.info("生成业务员(转介人)积分明细表记录=====结束");
//生成保单预计发佣表记录 (销售佣金基本法)
generateExpectedFortune(GenerateExpectedFortuneDto.builder()
//转介人绑定的基本法列表对应计算值
......@@ -590,6 +723,20 @@ public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService
}
/**
* 生成业务员(转介人)积分明细表记录
* @return
*/
@Override
@Async("commonAsyncExecutor")
@Transactional(rollbackFor = Exception.class)
public Result generateAgentDetailFyc(GenerateAgentDetailFycDto dto){
log.info("生成业务员(转介人)积分明细表记录=====开始");
//基本法计算 - 保存积分明细表
fycService.saveAgentDetailFyc(dto);
return Result.success();
}
/**
* 生成保单预计发佣表记录
*
* @return
......
package com.yd.csf.api.service.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yd.csf.api.service.XxlJobService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.Calendar;
import java.util.Date;
@Service
@Slf4j
public class XxlJobServiceImpl implements XxlJobService {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
// @Value("${xxl.job.executor.appname}")
// private String appName;
@Value("${xxl.job.admin.username:admin}")
private String adminUsername;
@Value("${xxl.job.admin.password:123456}")
private String adminPassword;
@Autowired
private RestTemplate restTemplate;
// 添加认证Cookie存储
private String authCookie;
// 添加API路径常量
private static final String API_LOGIN = "/login";
private static final String API_JOB_GROUP_LIST = "/jobgroup/pageList";
private static final String API_JOB_ADD = "/jobinfo/add";
private static final String API_JOB_START = "/jobinfo/start";
/**
* 登录XXL-Job获取认证Cookie
* @return
*/
private boolean loginXxlJob() {
try {
String loginUrl = adminAddresses + "/login";
MultiValueMap<String, String> loginParams = new LinkedMultiValueMap<>();
loginParams.add("userName", adminUsername);
loginParams.add("password", adminPassword);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> loginEntity = new HttpEntity<>(loginParams, headers);
ResponseEntity<String> loginResponse = restTemplate.postForEntity(loginUrl, loginEntity, String.class);
if (loginResponse.getStatusCode().is2xxSuccessful()) {
// 从响应头中获取Cookie
HttpHeaders responseHeaders = loginResponse.getHeaders();
if (responseHeaders.containsKey("Set-Cookie")) {
authCookie = responseHeaders.getFirst("Set-Cookie");
log.info("XXL-Job登录成功");
return true;
}
}
log.error("XXL-Job登录失败: {}", loginResponse.getStatusCode());
return false;
} catch (Exception e) {
log.error("XXL-Job登录异常", e);
return false;
}
}
/**
* 创建XXL-Job定时任务
* @param taskName 任务名称
* @param taskBizId 任务ID(业务唯一ID)
* @param appName 执行器名称
* @param scheduleTime 定时时间
* @return
*/
@Override
public String addScheduleJob(String taskName, String taskBizId, String appName, Date scheduleTime) {
try {
// 登录认证
if (authCookie == null && !loginXxlJob()) {
throw new RuntimeException("XXL-Job认证失败");
}
// 先获取执行器ID
Integer jobGroupId = getExecutorGroupId(appName);
if (jobGroupId == null) {
throw new RuntimeException("获取执行器ID失败,执行器可能未注册: " + appName);
}
String cronExpression = convertDateToCron(scheduleTime);
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("jobGroup", jobGroupId.toString()); // 使用动态获取的执行器ID
params.add("jobDesc", taskName + "-" + taskBizId);
params.add("author", "system");
params.add("scheduleType", "CRON");
params.add("scheduleConf", cronExpression);
params.add("glueType", "BEAN");
params.add("executorHandler", "emailSendJobHandler");
params.add("executorParam", taskBizId);
params.add("executorRouteStrategy", "FIRST");
params.add("misfireStrategy", "DO_NOTHING");
params.add("executorBlockStrategy", "SERIAL_EXECUTION");
String url = adminAddresses + "/jobinfo/add";
HttpHeaders headers = new HttpHeaders();
headers.add("Cookie", authCookie);
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
String jobId = extractJobId(response.getBody());
log.info("创建XXL-Job任务成功, taskId: {}, jobId: {}", taskBizId, jobId);
// 创建成功后自动启动任务
if (startJob(jobId)) {
log.info("自动启动XXL-Job任务成功, jobId: {}", jobId);
} else {
log.warn("自动启动XXL-Job任务失败, jobId: {}", jobId);
}
return jobId;
} else {
throw new RuntimeException("XXL-Job API调用失败: " + response.getStatusCode());
}
} catch (Exception e) {
log.error("创建XXL-Job任务失败", e);
throw new RuntimeException("创建定时任务失败: " + e.getMessage());
}
}
// 添加版本检测方法
private void checkApiPaths() {
log.info("正在检测XXL-Job Admin API路径...");
// 可以添加API路径检测逻辑
}
/**
* 根据执行器AppName获取执行器ID
* @param appName
* @return
*/
private Integer getExecutorGroupId(String appName) {
try {
// 尝试不同的API路径
String[] possiblePaths = {
"/jobgroup/pageList", // XXL-Job 2.3.0+
"/jobgroup/list", // 旧版本
"/jobgroup/listByApp" // 其他可能路径
};
for (String path : possiblePaths) {
Integer groupId = tryGetExecutorGroupId(appName, path);
if (groupId != null) {
log.info("成功获取执行器ID: {},使用的API路径: {}", groupId, path);
return groupId;
}
}
log.error("所有API路径尝试均失败,无法获取执行器ID");
return null;
} catch (Exception e) {
log.error("获取执行器ID异常", e);
return null;
}
}
/**
* 获取执行器ID
* @param appName
* @param apiPath
* @return
*/
private Integer tryGetExecutorGroupId(String appName, String apiPath) {
try {
String url = adminAddresses + apiPath;
log.info("尝试API路径: {}", url);
HttpHeaders headers = new HttpHeaders();
headers.add("Cookie", authCookie);
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// 对于分页接口可能需要添加参数
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
if (apiPath.equals("/jobgroup/pageList")) {
params.add("start", "0");
params.add("length", "100");
params.add("appname", appName);
}
HttpEntity<?> entity = params.isEmpty() ?
new HttpEntity<>(headers) :
new HttpEntity<>(params, headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.POST, entity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(response.getBody());
log.info("API响应: {}", response.getBody());
// 解析响应,根据不同的API路径处理
if (jsonNode.has("data")) {
JsonNode dataNode = jsonNode.get("data");
if (dataNode.isArray()) {
// 数组格式的响应
for (JsonNode item : dataNode) {
if (item.has("appname") && appName.equals(item.get("appname").asText())) {
return item.get("id").asInt();
}
}
} else if (dataNode.has("data")) {
// 分页格式的响应
JsonNode dataArray = dataNode.get("data");
if (dataArray.isArray()) {
for (JsonNode item : dataArray) {
if (item.has("appname") && appName.equals(item.get("appname").asText())) {
return item.get("id").asInt();
}
}
}
}
}
}
return null;
} catch (Exception e) {
log.warn("API路径 {} 尝试失败: {}", apiPath, e.getMessage());
return null;
}
}
/**
* 启动XXL-Job任务
* @param jobId
* @return
*/
private boolean startJob(String jobId) {
try {
String startUrl = adminAddresses + "/jobinfo/start";
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("id", jobId);
HttpHeaders headers = new HttpHeaders();
headers.add("Cookie", authCookie);
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers);
ResponseEntity<String> response = restTemplate.exchange(startUrl, HttpMethod.POST, entity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
log.info("启动任务成功, jobId: {}", jobId);
return true;
} else {
log.error("启动任务失败, jobId: {}, 状态码: {}, 响应: {}", jobId, response.getStatusCode(), response.getBody());
return false;
}
} catch (Exception e) {
log.error("启动任务异常, jobId: {}", jobId, e);
return false;
}
}
/**
* date转成Cron表达式
* @param date
* @return
*/
private String convertDateToCron(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int second = calendar.get(Calendar.SECOND);
int minute = calendar.get(Calendar.MINUTE);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int month = calendar.get(Calendar.MONTH) + 1;
int year = calendar.get(Calendar.YEAR);
return String.format("%d %d %d %d %d ? %d", second, minute, hour, day, month, year);
}
/**
* 从XXL-Job响应中提取真实的jobId
* 响应格式: {"code":200,"msg":null,"content":"6"}
*/
private String extractJobId(String responseBody) {
try {
log.info("XXL-Job响应: {}", responseBody);
if (responseBody == null || responseBody.trim().isEmpty()) {
log.warn("响应体为空");
return String.valueOf(System.currentTimeMillis());
}
// 使用JSON解析器正确解析响应
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
if (jsonNode.has("code") && jsonNode.get("code").asInt() == 200) {
if (jsonNode.has("content")) {
String jobId = jsonNode.get("content").asText();
log.info("提取到真实jobId: {}", jobId);
return jobId;
}
}
log.warn("无法从响应中解析jobId,响应: {}", responseBody);
return String.valueOf(System.currentTimeMillis());
} catch (Exception e) {
log.error("解析jobId异常", e);
return String.valueOf(System.currentTimeMillis());
}
}
}
\ No newline at end of file
......@@ -47,5 +47,11 @@
<artifactId>yd-base-feign</artifactId>
<version>${project.version}</version>
</dependency>
<!-- XXL-Job 核心依赖 -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
</dependency>
</dependencies>
</project>
package com.yd.csf.service.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@Configuration
@EnableAsync
public class AsyncConfig {
......@@ -54,4 +57,21 @@ public class AsyncConfig {
executor.initialize();
return executor;
}
@Bean("jobTaskExecutor")
public TaskExecutor jobTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("JobAsync-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
log.info("异步线程池初始化完成,线程名前缀: JobAsync-");
return executor;
}
}
\ No newline at end of file
package com.yd.csf.service.dao;
import com.yd.csf.service.model.CalmTask;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 保单冷静期定时任务表 Mapper 接口
* </p>
*
* @author zxm
* @since 2026-03-06
*/
public interface CalmTaskMapper extends BaseMapper<CalmTask> {
}
package com.yd.csf.service.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 保单冷静期定时任务表
* </p>
*
* @author zxm
* @since 2026-03-06
*/
@Getter
@Setter
@TableName("calm_task")
public class CalmTask implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 保单冷静期定时任务表唯一业务ID
*/
@TableField("calm_task_biz_id")
private String calmTaskBizId;
/**
* 新单跟进唯一业务ID
*/
@TableField("policy_biz_id")
private String policyBizId;
/**
* 保单号
*/
@TableField("policy_no")
private String policyNo;
/**
* 转介人业务id
*/
@TableField("broker_biz_id")
private String brokerBizId;
/**
* 转介人名称
*/
@TableField("broker_name")
private String brokerName;
/**
* 当前保单转介人累计FYC(当前保单转介人基本法计算积分合值)
*/
@TableField("total_fyc")
private BigDecimal totalFyc;
/**
* 冷静期结束日期
*/
@TableField("cooling_off_end_date")
private LocalDateTime coolingOffEndDate;
/**
* 冷静期天数
*/
@TableField("cooling_off_days")
private Integer coolingOffDays;
/**
* 执行时间(定时任务执行时间)
*/
@TableField("execution_time")
private LocalDateTime executionTime;
/**
* 执行状态(未执行,已执行)
*/
@TableField("execution_status")
private String executionStatus;
/**
* 通用备注
*/
@TableField("remark")
private String remark;
/**
* 删除标识: 0-正常, 1-删除
*/
@TableField("is_deleted")
private Integer isDeleted;
/**
* 创建人ID
*/
@TableField("creator_id")
private String creatorId;
/**
* 更新人ID
*/
@TableField("updater_id")
private String updaterId;
/**
* 创建时间
*/
@TableField("create_time")
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField("update_time")
private LocalDateTime updateTime;
}
package com.yd.csf.service.service;
import com.yd.csf.service.model.CalmTask;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 保单冷静期定时任务表 服务类
* </p>
*
* @author zxm
* @since 2026-03-06
*/
public interface ICalmTaskService extends IService<CalmTask> {
}
package com.yd.csf.service.service.impl;
import com.yd.csf.service.model.CalmTask;
import com.yd.csf.service.dao.CalmTaskMapper;
import com.yd.csf.service.service.ICalmTaskService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 保单冷静期定时任务表 服务实现类
* </p>
*
* @author zxm
* @since 2026-03-06
*/
@Service
public class CalmTaskServiceImpl extends ServiceImpl<CalmTaskMapper, CalmTask> implements ICalmTaskService {
}
......@@ -21,7 +21,7 @@ public class MyBatisPlusCodeGenerator {
})
.strategyConfig(builder -> {
builder.addInclude(
"premium_reconciliation","premium_remittance","premium_remittance_file"
"calm_task"
)
.entityBuilder()
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yd.csf.service.dao.CalmTaskMapper">
</mapper>
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