ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
@@ -5,6 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; /** * 启动程序 @@ -12,6 +13,7 @@ * @author ruoyi */ @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) @EnableScheduling public class RuoYiApplication { public static void main(String[] args) ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
@@ -21,6 +21,7 @@ import com.ruoyi.system.domain.vo.UserAccountUpdateVo; import com.ruoyi.system.service.GroupWelcomeConfigService; import com.ruoyi.system.service.IpBlacklistService; import com.ruoyi.system.service.PaymentRecordService; import com.ruoyi.system.service.UserAccountService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -74,6 +75,8 @@ @Autowired InsuranceFeatureService insuranceFeatureService; @Value("${file.upload-dir}") private String uploadDir; @@ -401,6 +404,7 @@ long count = userPolicyService.count(new LambdaQueryWrapper<>(UserPolicy.class) .eq(UserPolicy::getUserId, userAccount.getId()) .eq(UserPolicy::getProductId,f.getId()) .eq(UserPolicy::getPayStatus,2) .ne(UserPolicy::getApprovalStatus,2) ); if(count > 0){ ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/UserPolicyServiceImpl.java
@@ -9,14 +9,14 @@ import com.ruoyi.common.utils.DateUtils; import com.ruoyi.im.comm.Result; import com.ruoyi.im.service.InsuranceProductService; import com.ruoyi.im.service.UserKycService; import com.ruoyi.im.service.UserPolicyService; import com.ruoyi.im.util.PayService; import com.ruoyi.im.util.ValidatorUtil; import com.ruoyi.system.domain.InsuranceProduct; import com.ruoyi.system.domain.UserAccount; import com.ruoyi.system.domain.UserPolicy; import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.dto.UserPolicyDto; import com.ruoyi.system.mapper.UserPolicyMapper; import com.ruoyi.system.service.PaymentRecordService; import com.ruoyi.system.service.UserAccountService; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; @@ -42,6 +42,10 @@ UserAccountService userAccountService; @Autowired private PayService payService; @Autowired private PaymentRecordService paymentRecordService; @Autowired private UserKycService userKycService; @Override @@ -75,7 +79,7 @@ ); if(count > 0){ return Result.error("你已申购此保险,请勿重复申购"); return Result.error("请勿重复提交,如未支付请3-5分钟重新提交申请"); } InsuranceProduct insuranceProduct = insuranceProductService.getById(dto.getProductId()); if(ObjectUtil.isEmpty(insuranceProduct)){ @@ -112,10 +116,10 @@ userPolicy.setTerm(insuranceProduct.getTerm()); userPolicy.setName(dto.getName()); userPolicy.setGender(dto.getGender()); userPolicy.setPayStatus(0); userPolicy.setPayStatus(1); userPolicy.setOrderNo(orderNo); userPolicy.setBirthDate(LocalDate.parse(dto.getBirthDate())); userPolicy.setNumberDays(insuranceProduct.getNumberDays()); userPolicy.setOccupation(dto.getOccupation()); userPolicy.setIdCard(dto.getIdCard()); userPolicy.setPhone(dto.getPhone()); @@ -127,21 +131,22 @@ userPolicy.setIsLifelong(insuranceProduct.getTerm() == 0 ? 0 : 1); save(userPolicy); extracted(userAccount, userPolicy.getId(), orderNo,PaymentRecord.PaymentStatus.PENDING.getCode(),userPolicy.getProductId(),userPolicy.getProductName()); return Result.success(payUrl); case 401: extracted(userAccount, null, orderNo,PaymentRecord.PaymentStatus.FAILED.getCode(),null,"获取支付通道失败"); return Result.error("未授权访问支付系统"); case 403: extracted(userAccount, null, orderNo,PaymentRecord.PaymentStatus.FAILED.getCode(),null,"获取支付通道失败"); return Result.error("禁止访问支付系统"); case 404: extracted(userAccount, null, orderNo,PaymentRecord.PaymentStatus.FAILED.getCode(),null,"获取支付通道失败"); return Result.error("支付接口不存在"); case 0014: // 注意:0014可能是字符串,需要根据实际情况处理 extracted(userAccount, null, orderNo,PaymentRecord.PaymentStatus.FAILED.getCode(),null,"获取支付通道失败"); return Result.error("当前支付不可用,请更换其他支付方式!"); default: extracted(userAccount, null, orderNo,PaymentRecord.PaymentStatus.FAILED.getCode(),null,"获取支付通道失败"); // 其他错误码 String message = (String) parse.get("message"); if (message != null && !message.isEmpty()) { @@ -170,7 +175,7 @@ userPolicy.setPayStatus(2); userPolicy.setOrderNo(orderNo); userPolicy.setBirthDate(LocalDate.parse(dto.getBirthDate())); userPolicy.setNumberDays(insuranceProduct.getNumberDays()); userPolicy.setOccupation(dto.getOccupation()); userPolicy.setIdCard(dto.getIdCard()); userPolicy.setPhone(dto.getPhone()); @@ -182,10 +187,42 @@ userPolicy.setIsLifelong(insuranceProduct.getTerm() == 0 ? 0 : 1); save(userPolicy); UserKyc userKyc = userKycService.getOne(new LambdaQueryWrapper<UserKyc>() .eq(UserKyc::getAccount, userAccount.getAccount()) ); PaymentRecord paymentRecord = new PaymentRecord(); paymentRecord.setUserId(userAccount.getId()); paymentRecord.setPaymentStatus(PaymentRecord.PaymentStatus.PAID.getCode()); paymentRecord.setProductId(userPolicy.getProductId()); paymentRecord.setOrderId(userPolicy.getId()); paymentRecord.setPayOrdeNo(orderNo); paymentRecord.setAccount(userAccount.getAccount()); paymentRecord.setName(userKyc.getName()); paymentRecord.setInvitationCode(userAccount.getInvitationCode()); paymentRecord.setProductName(insuranceProduct.getProductName()); paymentRecordService.save(paymentRecord); return Result.success(); } } private void extracted(UserAccount userAccount, Integer userPolicyId, String orderNo,Integer payCode,Integer productId,String productName) { UserKyc userKyc = userKycService.getOne(new LambdaQueryWrapper<UserKyc>() .eq(UserKyc::getAccount, userAccount.getAccount()) ); PaymentRecord paymentRecord = new PaymentRecord(); paymentRecord.setUserId(userAccount.getId()); paymentRecord.setAccount(userAccount.getAccount()); paymentRecord.setName(userKyc.getName()); paymentRecord.setInvitationCode(userAccount.getInvitationCode()); paymentRecord.setProductName(productName); paymentRecord.setPaymentStatus(payCode); paymentRecord.setProductId(productId); paymentRecord.setOrderId(userPolicyId); paymentRecord.setPayOrdeNo(orderNo); paymentRecordService.save(paymentRecord); } private final Random random = new Random(); /** * 生成随机订单号 (格式: 时间戳 + 6位随机数) ruoyi-admin/src/main/java/com/ruoyi/im/task/MedicalInsuranceTask.java
@@ -3,19 +3,25 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.im.service.MedicalInsuranceAccountService; import com.ruoyi.im.service.UserPolicyService; import com.ruoyi.im.service.impl.UserPolicyServiceImpl; import com.ruoyi.system.domain.MedicalInsuranceAccount; import com.ruoyi.system.domain.PaymentRecord; import com.ruoyi.system.domain.UserPolicy; import com.ruoyi.system.service.PaymentRecordService; import io.swagger.models.auth.In; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** * @program: ruoyiim @@ -23,6 +29,7 @@ * @create: 2025-09-21 14:21 **/ @Component @Slf4j public class MedicalInsuranceTask { @Autowired @@ -34,6 +41,10 @@ @Autowired MedicalInsuranceAccountService medicalInsuranceAccountService; @Autowired PaymentRecordService paymentRecordService; /** * 定时筛选用户保单状态 @@ -78,4 +89,45 @@ public boolean isExpired(LocalDate endDate) { return LocalDate.now().isAfter(endDate); } /** * 定时清除未支付保单 */ @Scheduled(cron = "0 */1 * * * ?") public void executeWithFixedDelay() { try { log.info("定时清除未支付保单定时任务开始执行,时间:{}", System.currentTimeMillis()); doBusiness(); log.info("定时清除未支付保单定时任务执行完成"); } catch (Exception e) { log.error("定时清除未支付保单定时任务执行异常", e); } } private void doBusiness() { // 计算5分钟前的时间 Date fiveMinutesAgo = new Date(System.currentTimeMillis() - 5 * 60 * 1000); log.info("查询条件:创建时间早于 {} 的未支付订单", fiveMinutesAgo); // 查询所有创建时间超过5分钟且状态为待支付的订单 List<UserPolicy> list = userPolicyService.list(new LambdaQueryWrapper<UserPolicy>() .eq(UserPolicy::getPayStatus, 1) // payStatus = 1 (待支付) .lt(UserPolicy::getCreatedAt, fiveMinutesAgo) // 创建时间早于5分钟前 .orderByAsc(UserPolicy::getCreatedAt) ); // 提取orderId列表 List<Integer> ids = list.stream() .map(UserPolicy::getId) // 提取orderId字段 .collect(Collectors.toList()); List<PaymentRecord> records = paymentRecordService.list(new LambdaQueryWrapper<PaymentRecord>() .in(PaymentRecord::getOrderId, ids) ); userPolicyService.removeByIds(list); userPolicyService.removeByIds(records); } } ruoyi-admin/src/main/java/com/ruoyi/web/controller/product/UserKycController.java
@@ -170,7 +170,9 @@ } if (StringUtils.isNotEmpty(userAccount.getNickname())) { userKyc.setNickName(userAccount.getNickname()); userKyc.setPhone(userAccount.getAccount()); } return Result.success(userKyc); } } ruoyi-admin/src/main/java/com/ruoyi/web/controller/product/UserPolicyController.java
@@ -20,8 +20,10 @@ import com.ruoyi.system.domain.dto.PayCallbackDTO; import com.ruoyi.system.domain.dto.UserPolicyDto; import com.ruoyi.im.service.UserPolicyService; 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; @@ -62,8 +64,9 @@ @Autowired InsuranceProductService insuranceProductService; @Autowired private PayService payService; PaymentRecordService paymentRecordService; @Value("${pay.key}") private String key; @@ -158,6 +161,30 @@ /** * 保单列表 */ @GetMapping("/PaymentRecordList") public TableDataInfo PaymentRecordList(@RequestParam(value = "payOrdeNo",required = false) String payOrdeNo, @RequestParam(value = "paymentStatus",required = false) Integer paymentStatus) { startPage(); LambdaQueryWrapper<PaymentRecord> wrapper = new LambdaQueryWrapper<>(); // 产品名称模糊查询 if (StringUtils.isNotEmpty(payOrdeNo)) { wrapper.eq(PaymentRecord::getPayOrdeNo, payOrdeNo); } if (paymentStatus != null) { wrapper.eq(PaymentRecord::getPaymentStatus, paymentStatus); } // 按创建时间倒序排列 wrapper.orderByDesc(PaymentRecord::getCreateTime); List<PaymentRecord> list = paymentRecordService.list(wrapper); return getDataTable(list); } /** * 保单审批 */ @GetMapping("/examine") @@ -192,12 +219,15 @@ } //计算到期时间 LocalDate expirationTime = calculateInsuranceEndDate(LocalDate.now(), userPolicy.getNumberDays()); 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); userPolicy.setInsuranceBenefitExpiryDate(insuranceBenefitExpiryDate); userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.ACTIVE); userPolicy.setUpdatedAt(new Date()); userPolicyService.updateById(userPolicy); @@ -217,6 +247,7 @@ 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()); @@ -427,7 +458,13 @@ userPolicy.setUpdatedAt(new Date()); boolean updateResult = userPolicyService.updateById(userPolicy); PaymentRecord paymentRecord = paymentRecordService.getOne(new LambdaQueryWrapper<PaymentRecord>() .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; @@ -443,7 +480,13 @@ userPolicy.setUpdatedAt(new Date()); boolean updateResult = userPolicyService.updateById(userPolicy); PaymentRecord paymentRecord = paymentRecordService.getOne(new LambdaQueryWrapper<PaymentRecord>() .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; ruoyi-system/src/main/java/com/ruoyi/system/domain/PaymentRecord.java
New file @@ -0,0 +1,130 @@ package com.ruoyi.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.time.LocalDateTime; /** * 支付记录实体类 */ @Data public class PaymentRecord { /** * 主键ID */ @TableId(type = IdType.AUTO) private Integer id; /** * 用户ID */ private Integer userId; // 邀请码 private String invitationCode; // 姓名 private String name; // 账号(唯一) private String account; // 产品名称 private String productName; /** * 产品ID */ private Integer productId; /** * 订单ID */ private Integer orderId; /** * 支付状态 */ private Integer paymentStatus; /** * 支付订单号 */ private String payOrdeNo; /** * 新增时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; /** * 修改时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; /** * 支付状态枚举 */ public enum PaymentStatus { FAILED(0, "拉取失败"), PENDING(1, "待支付"), PAID(2, "已支付"), EXPIRED(3, "超时/过期"); private final Integer code; private final String description; PaymentStatus(Integer code, String description) { this.code = code; this.description = description; } public Integer getCode() { return code; } public String getDescription() { return description; } public static PaymentStatus getByCode(Integer code) { for (PaymentStatus status : values()) { if (status.getCode().equals(code)) { return status; } } return null; } public static String getDescriptionByCode(Integer code) { PaymentStatus status = getByCode(code); return status != null ? status.getDescription() : "未知状态"; } } /** * 获取支付状态描述 */ public String getPaymentStatusDesc() { return PaymentStatus.getDescriptionByCode(this.paymentStatus); } /** * 判断是否已支付 */ public boolean isPaid() { return PaymentStatus.PAID.getCode().equals(this.paymentStatus); } /** * 判断是否待支付 */ public boolean isPending() { return PaymentStatus.PENDING.getCode().equals(this.paymentStatus); } } ruoyi-system/src/main/java/com/ruoyi/system/domain/UserKyc.java
@@ -54,4 +54,8 @@ //昵称 @TableField(exist = false) private String nickName; //手机 @TableField(exist = false) private String phone; } ruoyi-system/src/main/java/com/ruoyi/system/domain/UserPolicy.java
@@ -74,6 +74,8 @@ // 保险结束日期 private LocalDate endDate; // 领取失效日期 private LocalDate insuranceBenefitExpiryDate; // 是否终身保险 0:是 1:否 private Integer isLifelong ; ruoyi-system/src/main/java/com/ruoyi/system/mapper/PaymentRecordMapper.java
New file @@ -0,0 +1,10 @@ package com.ruoyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.system.domain.InsuranceProduct; import com.ruoyi.system.domain.PaymentRecord; import org.apache.ibatis.annotations.Mapper; @Mapper public interface PaymentRecordMapper extends BaseMapper<PaymentRecord> { } ruoyi-system/src/main/java/com/ruoyi/system/service/PaymentRecordService.java
New file @@ -0,0 +1,8 @@ package com.ruoyi.system.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.system.domain.PaymentRecord; import com.ruoyi.system.domain.UserAccount; public interface PaymentRecordService extends IService<PaymentRecord> { } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentRecordServiceImpl.java
New file @@ -0,0 +1,14 @@ package com.ruoyi.system.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.system.domain.IpBlacklist; import com.ruoyi.system.domain.PaymentRecord; import com.ruoyi.system.mapper.IpBlacklistMapper; import com.ruoyi.system.mapper.PaymentRecordMapper; import com.ruoyi.system.service.IpBlacklistService; import com.ruoyi.system.service.PaymentRecordService; import org.springframework.stereotype.Service; @Service public class PaymentRecordServiceImpl extends ServiceImpl<PaymentRecordMapper, PaymentRecord> implements PaymentRecordService { }