package com.ruoyi.web.controller.product; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.im.comm.Result; import com.ruoyi.im.service.InsuranceProductService; import com.ruoyi.im.service.MedicalInsuranceAccountService; import com.ruoyi.im.service.impl.InsurancePositionServiceImpl; import com.ruoyi.im.util.PayService; import com.ruoyi.im.util.RedisDistributedLock; import com.ruoyi.im.util.UserPolicyUtils; import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.dto.PayCallbackDTO; import com.ruoyi.system.domain.dto.SubordinateInformationDto; import com.ruoyi.system.domain.dto.UserPolicyDto; import com.ruoyi.im.service.UserPolicyService; import com.ruoyi.system.domain.out.UserTeamAndPositionOut; import com.ruoyi.system.service.FundsLogService; import com.ruoyi.system.service.PaymentRecordService; import com.ruoyi.system.service.UserAccountService; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @RestController @RequestMapping("/userPolicy") @Slf4j public class UserPolicyController extends BaseController { @Autowired UserPolicyService userPolicyService; @Autowired MedicalInsuranceAccountService medicalInsuranceAccountService; @Autowired UserAccountService userAccountService; @Autowired InsurancePositionServiceImpl insurancePositionService; @Autowired private RedisDistributedLock redisDistributedLock; @Autowired InsuranceProductService insuranceProductService; @Autowired PaymentRecordService paymentRecordService; @Autowired FundsLogService fundsLogService; @Value("${pay.key}") private String key; /** * 保险购买申请 */ @PostMapping("/purchaseApplication") public Result purchaseApplication(UserPolicyDto dto) { synchronized (dto.getAccount()) { // 生成锁的key:基于用户ID和产品ID,防止同一用户同时购买同一产品 String lockKey = redisDistributedLock.generateLockKey(dto.getAccount(), dto.getProductId()); boolean lockAcquired = false; try { // 尝试获取分布式锁:等待10秒,锁过期30秒 lockAcquired = redisDistributedLock.tryLock(lockKey, 30L, 10L); if (!lockAcquired) { return Result.error("操作过于频繁,请稍后重试"); } // 执行购买逻辑 return userPolicyService.purchaseApplication(dto); } catch (Exception e) { e.printStackTrace(); return Result.error("购买失败"); } finally { // 释放锁 if (lockAcquired) { redisDistributedLock.releaseLock(lockKey); } } } } /** * 根据用户id查询保单 */ @GetMapping("/updateGender") public AjaxResult updateGender(@RequestParam(value = "id") Integer id,@RequestParam(value = "gender") UserPolicy.Gender gender) { try { UserPolicy userPolicy = userPolicyService.getById(id); if(ObjectUtil.isEmpty(userPolicy)){ AjaxResult.error("保单不存在!"); } userPolicy.setGender(gender); userPolicyService.updateById(userPolicy); return AjaxResult.success("修改成功"); }catch (Exception e){ e.printStackTrace(); return AjaxResult.error("修改失败!"); } } /** * 根据用户id查询保单 */ @GetMapping("/getPolicyById") public Result getPolicyById(@RequestParam(value = "account") String account) { try { UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper() .eq(UserAccount::getAccount,account)); List list = userPolicyService.list(new LambdaQueryWrapper() .eq(UserPolicy::getUserId, userAccount.getId()) .eq(UserPolicy::getPayStatus,2) .eq(UserPolicy::getApprovalStatus,1) ); return Result.success(list); }catch (Exception e){ e.printStackTrace(); return Result.error("查询失败"); } } /** * 保单列表 */ @GetMapping("/list") public TableDataInfo list(@RequestParam(value = "account",required = false) String account, @RequestParam(value = "state",defaultValue = "0") Integer state, @RequestParam(value = "productName",required = false) String productName) { startPage(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); // 产品名称模糊查询 if (org.apache.commons.lang3.StringUtils.isNotBlank(account)) { wrapper.like(UserPolicy::getAccount, account); } // 产品代码模糊查询 if (org.apache.commons.lang3.StringUtils.isNotBlank(productName)) { wrapper.like(UserPolicy::getProductName, productName); } // 审批状态 if (state != null && state == 3) { wrapper.ne(UserPolicy::getApprovalStatus, 0); }else{ wrapper.eq(UserPolicy::getApprovalStatus, 0); wrapper.eq(UserPolicy::getPayStatus,2); } // 按创建时间倒序排列 wrapper.orderByDesc(UserPolicy::getCreatedAt); List list = userPolicyService.list(wrapper); return getDataTable(list); } /** * 支付记录列表 */ @GetMapping("/PaymentRecordList") public TableDataInfo PaymentRecordList( @RequestParam(value = "account",required = false) String account, @RequestParam(value = "payOrdeNo",required = false) String payOrdeNo, @RequestParam(value = "paymentStatus",required = false) Integer paymentStatus) { startPage(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (StringUtils.isNotEmpty(account)) { wrapper.eq(PaymentRecord::getAccount, account); } // 产品名称模糊查询 if (StringUtils.isNotEmpty(payOrdeNo)) { wrapper.eq(PaymentRecord::getPayOrdeNo, payOrdeNo); } if (paymentStatus != null) { wrapper.eq(PaymentRecord::getPaymentStatus, paymentStatus); } // 按创建时间倒序排列 wrapper.orderByDesc(PaymentRecord::getCreateTime); List list = paymentRecordService.list(wrapper); return getDataTable(list); } /** * 保单审批 */ @GetMapping("/examine") @Transactional public AjaxResult examine(@RequestParam(value = "id") Integer id, @RequestParam(value = "approvalStatus") Integer approvalStatus, @RequestParam(value = "message",required = false) String message) { try { UserPolicy userPolicy = userPolicyService.getById(id); if(ObjectUtil.isEmpty(userPolicy)){ return AjaxResult.error("订单不存在"); } if(userPolicy.getApprovalStatus() != 0){ return AjaxResult.error("订单已审批!"); } if(approvalStatus == 2 && StringUtils.isEmpty(message)){ return AjaxResult.error("驳回理由不能为空!"); } if(userPolicy.getPayStatus() != 3 && userPolicy.getPayStatus() != 2){ return AjaxResult.error("保单未支付完成,不能进行审批!"); } UserAccount userAccount = userAccountService.getById(userPolicy.getUserId()); if(approvalStatus == 2){ userPolicy.setApprovalStatus(approvalStatus); userPolicy.setMessage(message); userPolicy.setUpdatedAt(new Date()); userPolicyService.updateById(userPolicy); userAccount.setBalance(userAccount.getBalance().add(userPolicy.getPremium())); userAccountService.updateById(userAccount); fundsLogService.addLog(userAccount.getId(), userAccount.getAccount(), userPolicy.getPremium(), OperationType.REFUND); return AjaxResult.success("审批成功"); } //计算到期时间 LocalDate expirationTime = calculateInsuranceEndDate(LocalDate.now(), userPolicy.getTerm()); //保险金领取到期时间 LocalDate insuranceBenefitExpiryDate = calculateInsuranceEndDateToDay(LocalDate.now(), userPolicy.getNumberDays()); userPolicy.setApprovalStatus(approvalStatus); userPolicy.setMessage(message); userPolicy.setStartDate(LocalDate.now()); userPolicy.setEndDate(expirationTime.minusDays(1)); userPolicy.setInsuranceBenefitExpiryDate(insuranceBenefitExpiryDate); userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.ACTIVE); userPolicy.setUpdatedAt(new Date()); userPolicyService.updateById(userPolicy); //每天可领 BigDecimal amountClaimed = userPolicy.getCoverageAmount() .divide(new BigDecimal(userPolicy.getNumberDays()), 4, RoundingMode.HALF_UP); MedicalInsuranceAccount medicalInsuranceAccount = new MedicalInsuranceAccount(); medicalInsuranceAccount.setUserId(userPolicy.getUserId()); medicalInsuranceAccount.setPolicyId(userPolicy.getId()); medicalInsuranceAccount.setProductId(userPolicy.getProductId()); medicalInsuranceAccount.setTotalQuota(userPolicy.getCoverageAmount()); medicalInsuranceAccount.setRemainingBalance(BigDecimal.ZERO); medicalInsuranceAccount.setAmountClaimed(amountClaimed); medicalInsuranceAccount.setAlreadyReceived(BigDecimal.ZERO); medicalInsuranceAccount.setAmountAlreadyUsed(BigDecimal.ZERO); medicalInsuranceAccount.setEffectiveDate(userPolicy.getStartDate()); medicalInsuranceAccount.setInsuranceBenefitExpiryDate(userPolicy.getInsuranceBenefitExpiryDate()); medicalInsuranceAccount.setExpiryDate(userPolicy.getEndDate()); medicalInsuranceAccount.setAccountStatus(MedicalInsuranceAccount.AccountStatus.ACTIVE); medicalInsuranceAccount.setCreatedAt(new Date()); medicalInsuranceAccount.setUpdatedAt(new Date()); medicalInsuranceAccountService.save(medicalInsuranceAccount); //判断上级用户职位达成 if(approvalStatus == 1){ if(StringUtils.isNotEmpty(userAccount.getInvitationAccount())){ //上级 UserAccount superiorUser = userAccountService.getOne(new LambdaQueryWrapper() .eq(UserAccount::getAccount, userAccount.getInvitationAccount()) ); //查询上级的所有下级 List userAccountList = userAccountService.list(new LambdaQueryWrapper() .eq(UserAccount::getInvitationAccount, superiorUser.getAccount()) ); if(userAccountList.size() == 0){ return AjaxResult.success("审批成功"); } List idList = userAccountList.stream() .map(UserAccount::getId) .collect(Collectors.toList()); //查询下级的保单 List userPolicyList = userPolicyService.list(new LambdaQueryWrapper<>(UserPolicy.class) .in(UserPolicy::getUserId, idList) ); // 手动将当前审批的保单加入到列表中(因为事务隔离可能查不到) userPolicyList.add(userPolicy); if(userPolicyList.size() == 0){ return AjaxResult.success("审批成功"); } //生效保单数量 long activePolicies = UserPolicyUtils.countActivePolicies(userPolicyList); //查询所有职位 List positions = insurancePositionService.list(); positions.forEach(f->{ if(activePolicies >= f.getNumberPeople()){ superiorUser.setPosition(f.getPosition()); superiorUser.setAgreedTime(LocalDate.now()); } }); userAccountService.updateById(superiorUser); } return AjaxResult.success("审批成功"); } return AjaxResult.success("审批成功"); }catch (Exception e){ e.printStackTrace(); // 手动设置回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return AjaxResult.error("审批失败!"); } } /** * 计算保险到期日按天 * @param startDate 保险开始日期 * @param termDays 保险天数 * @return 保险到期日期 */ public static LocalDate calculateInsuranceEndDateToDay(LocalDate startDate, int termDays) { return calculateDateAfterDays(startDate, termDays); } /** * 计算指定天数后的日期 * @param startDate 开始日期 * @param daysToAdd 要添加的天数 * @return 计算后的日期 */ public static LocalDate calculateDateAfterDays(LocalDate startDate, int daysToAdd) { if (startDate == null) { throw new IllegalArgumentException("开始日期不能为空"); } return startDate.plusDays(daysToAdd); } /** * 计算保险到期日 * @param startDate 保险开始日期 * @param termYears 保险年限 * @return 保险到期日期 */ public static LocalDate calculateInsuranceEndDate(LocalDate startDate, int termYears) { return calculateDateAfterYears(startDate, termYears); } /** * 根据基准日期和年限推算日期 */ public static LocalDate calculateDateAfterYears(LocalDate baseDate, int years) { validateBaseDate(baseDate); return baseDate.plusYears(years); } /** * 验证基准日期 */ private static void validateBaseDate(LocalDate baseDate) { if (baseDate == null) { throw new IllegalArgumentException("基准日期不能为null"); } } @PostMapping("/notify") public String payNotify(@RequestBody PayCallbackDTO callbackDTO) { try { log.info("收到支付回调通知: {}", JSON.toJSONString(callbackDTO)); // 1. 验证签名 if (!verifySign(callbackDTO)) { log.error("签名验证失败: {}", callbackDTO.getOrderId()); return "签名验证失败"; } PaymentRecord paymentRecord = paymentRecordService.getOne(new LambdaQueryWrapper() .eq(PaymentRecord::getPayOrdeNo, callbackDTO.getOrderId()) ); if (paymentRecord == null) { log.error("支付订单不存在: {}", paymentRecord.getPayOrdeNo()); return "支付订单不存在"; } // 2. 根据订单号查询保单 UserPolicy userPolicy = userPolicyService.getOne(new LambdaQueryWrapper() .eq(UserPolicy::getId, paymentRecord.getOrderId())); if (userPolicy == null) { log.error("订单不存在: {}", callbackDTO.getOrderId()); return "订单不存在"; } if(userPolicy.getPayStatus() == 2){ log.error("订单已支付完成: {}", callbackDTO.getOrderId()); return "订单已支付完成"; } // 3. 根据订单状态处理业务逻辑 boolean processResult = processPayResult(callbackDTO, userPolicy); return processResult ? "success" : "fail"; } catch (Exception e) { log.error("支付回调处理异常: {}", e.getMessage(), e); return "FAIL"; } } private boolean verifySign(PayCallbackDTO callbackDTO) { try { Map params = new HashMap<>(); params.put("channelCode", callbackDTO.getChannelCode()); params.put("orderId", callbackDTO.getOrderId()); params.put("orderMoney", callbackDTO.getOrderMoney()); params.put("orderStatus", callbackDTO.getOrderStatus()); params.put("userCode", callbackDTO.getUserCode()); String generatedSign = generateCallbackSign(params); return generatedSign.equalsIgnoreCase(callbackDTO.getSign()); } catch (Exception e) { log.error("签名验证异常: {}", e.getMessage(), e); return false; } } private String generateCallbackSign(Map params) { try { List keys = new ArrayList<>(params.keySet()); Collections.sort(keys); StringBuilder stringA = new StringBuilder(); for (String key : keys) { Object value = params.get(key); if (value != null && !"".equals(value.toString().trim()) && !"sign".equals(key)) { if (stringA.length() > 0) { stringA.append("&"); } stringA.append(key).append("=").append(value.toString()); } } String stringSignTemp = stringA.toString() + "&key=" + key; String md5Result = md5(stringSignTemp); return md5Result.toLowerCase(); } catch (Exception e) { throw new RuntimeException("生成回调签名失败: " + e.getMessage()); } } private boolean processPayResult(PayCallbackDTO callbackDTO, UserPolicy userPolicy) { try { switch (callbackDTO.getOrderStatus()) { case 2: // 已支付 return handlePaySuccess(callbackDTO, userPolicy); case 3: // 超时/过期 return handlePayTimeout(callbackDTO, userPolicy); default: log.info("订单状态未完成: {}, 状态: {}", callbackDTO.getOrderId(), callbackDTO.getOrderStatus()); return true; } } catch (Exception e) { log.error("处理支付结果异常: {}", e.getMessage(), e); return false; } } private boolean handlePaySuccess(PayCallbackDTO callbackDTO, UserPolicy userPolicy) { if (userPolicy.getPayStatus() == 2) { log.info("订单已支付,无需重复处理: {}", callbackDTO.getOrderId()); return true; } userPolicy.setPayStatus(2); // 2-支付成功 userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.ACTIVE); userPolicy.setUpdatedAt(new Date()); boolean updateResult = userPolicyService.updateById(userPolicy); PaymentRecord paymentRecord = paymentRecordService.getOne(new LambdaQueryWrapper() .eq(PaymentRecord::getPayOrdeNo, callbackDTO.getOrderId()) ); if(ObjectUtil.isNotEmpty(paymentRecord)){ paymentRecord.setPaymentStatus(PaymentRecord.PaymentStatus.PAID.getCode()); paymentRecordService.updateById(paymentRecord); } if (updateResult) { log.info("支付成功处理完成: {}", callbackDTO.getOrderId()); return true; } else { log.error("更新保单状态失败: {}", callbackDTO.getOrderId()); return false; } } private boolean handlePayTimeout(PayCallbackDTO callbackDTO, UserPolicy userPolicy) { userPolicy.setPayStatus(3); // 3-支付超时/过期 userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.EXPIRED); userPolicy.setUpdatedAt(new Date()); boolean updateResult = userPolicyService.updateById(userPolicy); PaymentRecord paymentRecord = paymentRecordService.getOne(new LambdaQueryWrapper() .eq(PaymentRecord::getPayOrdeNo, callbackDTO.getOrderId()) ); if(ObjectUtil.isNotEmpty(paymentRecord)){ paymentRecord.setPaymentStatus(PaymentRecord.PaymentStatus.EXPIRED.getCode()); paymentRecordService.updateById(paymentRecord); } if (updateResult) { log.info("支付超时处理完成: {}", callbackDTO.getOrderId()); return true; } else { log.error("更新保单超时状态失败: {}", callbackDTO.getOrderId()); return false; } } private String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder hexString = new StringBuilder(); for (byte b : messageDigest) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5加密失败", e); } } /** * 保单列表 */ @GetMapping("/deleteRecordById") public AjaxResult PaymentRecordList(@RequestParam(value = "id",required = false) Integer id) { // 查询所有创建时间超过5分钟且状态为待支付的订单 PaymentRecord byId = paymentRecordService.getById(id); if(ObjectUtil.isEmpty(byId)){ return AjaxResult.error("订单不存在!"); } if(byId.getPaymentStatus() == 2){ return AjaxResult.error("订单已支付,禁止删除"); }else{ paymentRecordService.removeById(byId); } UserPolicy userPolicy = userPolicyService.getById(byId.getOrderId()); if(ObjectUtil.isNotEmpty(userPolicy) && userPolicy.getPayStatus() == 2){ return AjaxResult.error("订单已支付,禁止删除"); }else{ userPolicyService.removeById(userPolicy); } return AjaxResult.success("删除成功"); } /** * 保单列表 */ @GetMapping("/getFundsLogList") public TableDataInfo getFundsLogList(@RequestParam(value = "account",required = false) String account,@RequestParam(value = "operationType",required = false) Integer operationType) { startPage(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if(StringUtils.isNotEmpty(account)){ wrapper.eq(FundsLog::getAccount,account); } if (ObjectUtil.isNotEmpty(operationType)){ wrapper.eq(FundsLog::getOperationType,operationType); } wrapper.orderByDesc(FundsLog::getCreateTime); List list = fundsLogService.list(wrapper); return getDataTable(list); } }