1
zj
2025-10-11 2c7b7986f347961adec433e378a1f82434c7150e
1
8 files modified
4 files added
340 ■■■■■ changed files
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java 2 ●●●●● patch | view | raw | blame | history
ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java 4 ●●●● patch | view | raw | blame | history
ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/UserPolicyServiceImpl.java 61 ●●●● patch | view | raw | blame | history
ruoyi-admin/src/main/java/com/ruoyi/im/task/MedicalInsuranceTask.java 52 ●●●●● patch | view | raw | blame | history
ruoyi-admin/src/main/java/com/ruoyi/web/controller/product/UserKycController.java 2 ●●●●● patch | view | raw | blame | history
ruoyi-admin/src/main/java/com/ruoyi/web/controller/product/UserPolicyController.java 51 ●●●●● patch | view | raw | blame | history
ruoyi-system/src/main/java/com/ruoyi/system/domain/PaymentRecord.java 130 ●●●●● patch | view | raw | blame | history
ruoyi-system/src/main/java/com/ruoyi/system/domain/UserKyc.java 4 ●●●● patch | view | raw | blame | history
ruoyi-system/src/main/java/com/ruoyi/system/domain/UserPolicy.java 2 ●●●●● patch | view | raw | blame | history
ruoyi-system/src/main/java/com/ruoyi/system/mapper/PaymentRecordMapper.java 10 ●●●●● patch | view | raw | blame | history
ruoyi-system/src/main/java/com/ruoyi/system/service/PaymentRecordService.java 8 ●●●●● patch | view | raw | blame | history
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentRecordServiceImpl.java 14 ●●●●● patch | view | raw | blame | history
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 {
}