From f8b251573040d2762507909a02f274a6cf4d751c Mon Sep 17 00:00:00 2001
From: heheng <heheng@123456>
Date: 星期四, 23 一月 2025 14:40:27 +0800
Subject: [PATCH] 特种作业非煤缴费版本优化

---
 exam-system/src/main/java/com/gkhy/exam/pay/service/impl/CoalPayServiceImpl.java           |   61 +-
 exam-system/src/main/java/com/gkhy/exam/pay/utils/PayUtils.java                            |  120 +++--
 ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisLock.java                           |   98 ++++
 exam-system/src/main/java/com/gkhy/exam/pay/controller/NonCoalPayController.java           |   14 
 exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalPayOrder.java                   |   20 
 ruoyi-common/src/main/java/com/ruoyi/common/config/RedissonConfig.java                     |   29 +
 exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalCateRep.java                    |   68 +++
 exam-system/src/main/resources/mapper/pay/NonCoalPayMapper.xml                             |   69 +++
 ruoyi-admin/src/main/resources/application-pro.yml                                         |  237 +++++-----
 exam-system/src/main/java/com/gkhy/exam/pay/service/NonCoalPayStudentService.java          |    2 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java                          |   10 
 exam-system/pom.xml                                                                        |    6 
 exam-system/src/test/java/TextPay.java                                                     |   77 +--
 exam-system/src/main/java/com/gkhy/exam/pay/service/impl/NonCoalPayStudentServiceImpl.java |  148 ++++++
 exam-system/src/main/java/com/gkhy/exam/pay/mapper/NonCoalPayMapper.java                   |    3 
 exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalStuRep.java                     |   46 ++
 ruoyi-admin/src/main/resources/application-dev.yml                                         |  237 +++++-----
 17 files changed, 880 insertions(+), 365 deletions(-)

diff --git a/exam-system/pom.xml b/exam-system/pom.xml
index 6a85092..505b850 100644
--- a/exam-system/pom.xml
+++ b/exam-system/pom.xml
@@ -31,6 +31,12 @@
             <artifactId>qiniu-java-sdk</artifactId>
             <version>[7.2.0, 7.2.99]</version>
         </dependency>
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>3.18.0</version>
+            <scope>compile</scope>
+        </dependency>
 
     </dependencies>
 
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/controller/NonCoalPayController.java b/exam-system/src/main/java/com/gkhy/exam/pay/controller/NonCoalPayController.java
index 86139f4..8d11c9a 100644
--- a/exam-system/src/main/java/com/gkhy/exam/pay/controller/NonCoalPayController.java
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/controller/NonCoalPayController.java
@@ -147,7 +147,7 @@
      */
     @PostMapping("/editNonCoalStu")
     @ApiOperation(value = "修改非煤缴费学员")
-    public AjaxResult editNonCoalStu(@RequestBody NonCoalPayStudent nonCoalPayStudent) {
+    public AjaxResult editNonCoalStu(@Validated @RequestBody NonCoalPayStudent nonCoalPayStudent) {
         return toAjax(nonCoalPayStudentService.updateNonCoalPayStudent(nonCoalPayStudent));
     }
 
@@ -194,4 +194,16 @@
     }
 
 
+    @PostMapping("/sendOrder")
+    @ApiOperation(value = "生成财政订单")
+    @Anonymous
+    @RepeatSubmit
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", dataTypeClass = Long.class, value = "个人缴费学员数据id,团队缴费数据id", required = true),
+            @ApiImplicitParam(name = "payType", dataTypeClass = String.class, value = "1个人2是团队", required = true),
+    })
+    public AjaxResult sendOrder(@RequestParam("id") Long id, @RequestParam("payType") String payType) {
+        return success(nonCoalPayStudentService.sendOrder(id, payType));
+    }
+
 }
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalCateRep.java b/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalCateRep.java
new file mode 100644
index 0000000..c5810ce
--- /dev/null
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalCateRep.java
@@ -0,0 +1,68 @@
+package com.gkhy.exam.pay.dto.rep;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class NonCoalCateRep {
+    /**
+     * $column.columnComment
+     */
+    private Long id;
+
+    /**
+     * $column.columnComment
+     */
+    @ApiModelProperty("非煤管理id")
+    private Long nonCoalPayId;
+
+    /**
+     * 类别id
+     */
+
+    @ApiModelProperty("类别id")
+    private Long categoryId;
+
+    @ApiModelProperty("类别名称")
+    private String subjectName;
+
+    /**
+     * 类别1理论2实操
+     */
+    @ApiModelProperty("类别类型id")
+    private Long categoryType;
+
+    @ApiModelProperty("缴费金额")
+    private BigDecimal categoryAmount;
+
+
+    @ApiModelProperty("业务编码")
+    private String businessCode;
+
+    /**
+     * 单位编码
+     */
+    @ApiModelProperty("单位编码")
+    private String companyCode;
+
+    /**
+     * 开票人
+     */
+    @ApiModelProperty("开票人")
+    private String drawer;
+
+    /**
+     * 复核人
+     */
+    @ApiModelProperty("复核人")
+    private String reviewer;
+
+    /**
+     * 开票单位社会信用代码
+     */
+    @ApiModelProperty("开票单位社会信用代码")
+    private String invoicingCompanyCode;
+
+}
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalPayOrder.java b/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalPayOrder.java
new file mode 100644
index 0000000..17b35dd
--- /dev/null
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalPayOrder.java
@@ -0,0 +1,20 @@
+package com.gkhy.exam.pay.dto.rep;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class NonCoalPayOrder {
+
+    private Long id;
+    private String batchName;
+    private BigDecimal amount;
+    private Long payPersonType;
+    private String payCompanyName;
+    private String payCompanyCard;
+
+    private List<NonCoalStuRep> nonCoalStuList;
+    private List<NonCoalCateRep> nonCoalPayCategoryList;
+}
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalStuRep.java b/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalStuRep.java
new file mode 100644
index 0000000..b42ba0d
--- /dev/null
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/dto/rep/NonCoalStuRep.java
@@ -0,0 +1,46 @@
+package com.gkhy.exam.pay.dto.rep;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class NonCoalStuRep {
+
+    private Long id;
+
+    /**
+     * 姓名
+     */
+    @ApiModelProperty("姓名")
+    private String studentName;
+
+    /**
+     * 身份证号
+     */
+    @ApiModelProperty("身份证号")
+    private String idCard;
+
+    /**
+     * 电话
+     */
+
+    @ApiModelProperty("电话")
+    private String phone;
+
+    /**
+     * 0男 1女 2未知
+     */
+
+    @ApiModelProperty("性别 0男1女2未知")
+    private Long sex;
+
+    /**
+     * 是否缴款0否1是
+     */
+    @ApiModelProperty("是否缴款0否1是")
+    private Long payStatus;
+
+
+    @ApiModelProperty("财政订单编号")
+    private String orderId;
+}
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/mapper/NonCoalPayMapper.java b/exam-system/src/main/java/com/gkhy/exam/pay/mapper/NonCoalPayMapper.java
index 8da20c7..fab9763 100644
--- a/exam-system/src/main/java/com/gkhy/exam/pay/mapper/NonCoalPayMapper.java
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/mapper/NonCoalPayMapper.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.gkhy.exam.pay.dto.rep.NonCoalPayDetailH5RepDto;
 import com.gkhy.exam.pay.dto.rep.NonCoalPayDetailRepDto;
+import com.gkhy.exam.pay.dto.rep.NonCoalPayOrder;
 import com.gkhy.exam.pay.dto.rep.NonCoalPayPageRepDto;
 import com.gkhy.exam.pay.entity.NonCoalPay;
 import org.apache.ibatis.annotations.Param;
@@ -59,6 +60,8 @@
      */
     public int insertNonCoalPay(NonCoalPay nonCoalPay);
 
+    List<NonCoalPayOrder> selectNonCoalPayOrderByParam(@Param("dataId") Long dataId, @Param("payType") Long payType);
+
     /**
      * 修改【请填写功能名称】
      *
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/service/NonCoalPayStudentService.java b/exam-system/src/main/java/com/gkhy/exam/pay/service/NonCoalPayStudentService.java
index 05ff00c..5c2ee94 100644
--- a/exam-system/src/main/java/com/gkhy/exam/pay/service/NonCoalPayStudentService.java
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/service/NonCoalPayStudentService.java
@@ -31,6 +31,8 @@
      */
     public List<NonCoalPayStudent> selectNonCoalPayStudentList(NonCoalPayStudent nonCoalPayStudent);
 
+    public String sendOrder(Long dataId, String payType);
+
     /**
      * 新增【请填写功能名称】
      *
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/CoalPayServiceImpl.java b/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/CoalPayServiceImpl.java
index 4043ca1..4271919 100644
--- a/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/CoalPayServiceImpl.java
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/CoalPayServiceImpl.java
@@ -23,7 +23,6 @@
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.mapper.SysDeptMapper;
-import org.aspectj.lang.reflect.UnlockSignature;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.springframework.beans.BeanUtils;
@@ -37,7 +36,6 @@
 import java.util.Date;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
 import java.util.stream.Collectors;
 
 @Service
@@ -58,11 +56,13 @@
     @Autowired
     private RedissonClient redissonClient;
 
-
+    @Autowired
+    private PayUtils payUtils;
 
 
     /**
      * 缴费管理列表
+     *
      * @param coalPay
      * @return
      */
@@ -72,7 +72,7 @@
         List<CoalPay> coalPays = coalPayMapper.selectCoalPayList(coalPay);
         for (CoalPay pay : coalPays) {
             CoalPayRepDto coalPayRepDto = new CoalPayRepDto();
-            BeanUtils.copyProperties(pay,coalPayRepDto);
+            BeanUtils.copyProperties(pay, coalPayRepDto);
             //部门数据
             SysDept sysDept = sysDeptMapper.selectDeptById(pay.getDeptId());
             coalPayRepDto.setDeptName(sysDept.getDeptName());
@@ -98,7 +98,7 @@
 
         //基本数据
         CoalPay coalPay = coalPayMapper.selectById(id);
-        BeanUtils.copyProperties(coalPay,coalPayRepDto);
+        BeanUtils.copyProperties(coalPay, coalPayRepDto);
         //考点名称
         SysDept sysDept = sysDeptMapper.selectDeptById(coalPay.getDeptId());
         coalPayRepDto.setDeptName(sysDept.getDeptName());
@@ -120,12 +120,12 @@
     @Override
     public int insertCoalPay(CoalPayDto coalPayDto) {
         CoalPay coalPay = new CoalPay();
-        BeanUtils.copyProperties(coalPayDto,coalPay);
+        BeanUtils.copyProperties(coalPayDto, coalPay);
         coalPay.setCreateBy(SecurityUtils.getUsername());
         coalPay.setCreateTime(new Date());
         int insert = coalPayMapper.insertBath(coalPay);
-        if (CollectionUtils.isEmpty(coalPayDto.getCoalPayCategoryies())){
-            throw new BusinessException(this.getClass(),ResultConstants.BUSINESS_ERROR,"工种类别不能为空");
+        if (CollectionUtils.isEmpty(coalPayDto.getCoalPayCategoryies())) {
+            throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "工种类别不能为空");
         }
         List<CoalPayCategory> coalPayCategories = coalPayDto.getCoalPayCategoryies();
         for (CoalPayCategory coalPayCategory : coalPayCategories) {
@@ -137,18 +137,19 @@
 
     /**
      * 修改缴费信息
+     *
      * @param coalPayDto
      * @return
      */
     @Override
     public int updateCoalPay(CoalPayDto coalPayDto) {
         CoalPay coalPay = new CoalPay();
-        BeanUtils.copyProperties(coalPayDto,coalPay);
+        BeanUtils.copyProperties(coalPayDto, coalPay);
         coalPay.setUpdateBy(SecurityUtils.getUsername());
         coalPay.setUpdateTime(new Date());
         int i = coalPayMapper.updateCoalPayById(coalPay);
         int update = coalPayCategoryMapper.deleteByCoalPayId(coalPayDto.getId());
-        if (update>0){
+        if (update > 0) {
             List<CoalPayCategory> coalPayCategories = coalPayDto.getCoalPayCategoryies();
             for (CoalPayCategory coalPayCategory : coalPayCategories) {
                 coalPayCategory.setCoalPayId(coalPay.getId());
@@ -161,13 +162,13 @@
     @Override
     public AjaxResult deleteCoalPayByIds(Long[] ids) {
         for (Long id : ids) {
-            List<CoalPayStudent> coalPayStudents = coalPayStudentService.selectByCoalPayIdAndPayStatus(id,1);
-            if (!CollectionUtils.isEmpty(coalPayStudents)){
-                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR,"已有学员完成缴费,请勿删除");
+            List<CoalPayStudent> coalPayStudents = coalPayStudentService.selectByCoalPayIdAndPayStatus(id, 1);
+            if (!CollectionUtils.isEmpty(coalPayStudents)) {
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "已有学员完成缴费,请勿删除");
             }
         }
         int i = coalPayMapper.updateByIds(ids);
-        if (i>0){
+        if (i > 0) {
             return AjaxResult.success();
         }
         return AjaxResult.error();
@@ -175,7 +176,7 @@
 
     //个人查询缴费
     @Override
-    public List<CoalPayStudentRep> selectCoalPay(String idcard,String phone) {
+    public List<CoalPayStudentRep> selectCoalPay(String idcard, String phone) {
         CoalPayStudentReq coalPayStudentReq = new CoalPayStudentReq();
         coalPayStudentReq.setIdCard(idcard);
         coalPayStudentReq.setPhone(phone);
@@ -194,7 +195,7 @@
             //查询对应批次以及批次包含工种类别
             CoalPay coalPay = coalPayMapper.selectById(payStudent.getCoalPayId());
             CoalPayRepDto coalPayRepDto = new CoalPayRepDto();
-            BeanUtils.copyProperties(coalPay,coalPayRepDto);
+            BeanUtils.copyProperties(coalPay, coalPayRepDto);
             //考点名称
             SysDept sysDept = sysDeptMapper.selectDeptById(coalPay.getDeptId());
             coalPayRepDto.setDeptName(sysDept.getDeptName());
@@ -210,8 +211,8 @@
     @Override
     public int updateCoalPayType(CoalPayTypeReq coalPayTypeReq) {
         CoalPay byId = coalPayMapper.selectById(coalPayTypeReq.getCoalPayId());
-        if (coalPayTypeReq.getPayPersonType() != null && byId.getPayPersonType() == 2){
-            throw new BusinessException(this.getClass(),ResultConstants.BUSINESS_ERROR,"已为团体缴费,不可更改");
+        if (coalPayTypeReq.getPayPersonType() != null && byId.getPayPersonType() == 2) {
+            throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "已为团体缴费,不可更改");
         }
         return coalPayMapper.updateByPayId(coalPayTypeReq);
     }
@@ -224,7 +225,7 @@
         CoalTicket coalTicket = coalCategoryMapper.selectCoalTicket();
 
         PayReqData payReqData = new PayReqData();
-        payReqData.setOrderNo(RandomUtil.generateOrderNumber());
+        payReqData.setOrderNo(RandomUtil.generateOrderNumber(coalPayId, "CO"));
         payReqData.setMoney(coalPay.getAmount());
         payReqData.setAmount(1);
         payReqData.setPayerName(coalPayStudent.getName());
@@ -248,34 +249,34 @@
 
         CoalPayStudent payStudent = new CoalPayStudent();
 
-        RLock lock = redissonClient.getLock("SWSPKMAS_PAY_PERSON");
+        RLock lock = redissonClient.getLock("SWSPKMAS_PAY_PERSON_" + coalPayId);
         try {
             lock.lock(10, TimeUnit.SECONDS);
-            if (coalPayStudent.getGovPayStatus()!=0){
-                throw new BusinessException(this.getClass(),ResultConstants.BUSINESS_ERROR,"请勿重复发起支付");
+            if (coalPayStudent.getGovPayStatus() != 0) {
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "请勿重复发起支付");
             }
-            if (StringUtils.isEmpty(coalPayStudent.getPayCode())){
+            if (StringUtils.isEmpty(coalPayStudent.getPayCode())) {
 
             }
 
 
-            ResultVo resultVo = PayUtils.sendApiPost(payReqData);
-            if (resultVo.getRespcode().equals("BUS0000")){
+            ResultVo resultVo = payUtils.sendApiPost(payReqData);
+            if (resultVo.getRespcode().equals("BUS0000")) {
                 payStudent.setId(studentId);
                 payStudent.setOrderId(resultVo.getRespdata().getOrderNo());
                 payStudent.setGovPayStatus(1);
                 payStudent.setPayCode(resultVo.getRespdata().getBillNo());
                 coalPayStudentService.updateByCoalPayStudent(payStudent);
                 return resultVo.getRespdata().getBillNo();
-            }else {
-                throw new BusinessException(this.getClass(),ResultConstants.BUSINESS_ERROR,"发起支付失败,请稍后重试");
+            } else {
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "发起支付失败,请稍后重试");
             }
-        }catch (BusinessException e){
-            throw new BusinessException(this.getClass(),ResultConstants.BUSINESS_ERROR,"请稍后重试");
+        } catch (BusinessException e) {
+            throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "请稍后重试");
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {
-            if(lock.isLocked()){
+            if (lock.isLocked()) {
                 lock.unlock();
             }
         }
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/NonCoalPayStudentServiceImpl.java b/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/NonCoalPayStudentServiceImpl.java
index 3b78a96..21dda14 100644
--- a/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/NonCoalPayStudentServiceImpl.java
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/service/impl/NonCoalPayStudentServiceImpl.java
@@ -4,23 +4,34 @@
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.gkhy.exam.institutionalaccess.enums.StudentSex;
+import com.gkhy.exam.pay.dto.rep.NonCoalCateRep;
+import com.gkhy.exam.pay.dto.rep.NonCoalPayOrder;
+import com.gkhy.exam.pay.dto.rep.NonCoalStuRep;
 import com.gkhy.exam.pay.dto.req.NonCoalPayStuImport;
 import com.gkhy.exam.pay.dto.req.NonCoalPayStudentReqDto;
 import com.gkhy.exam.pay.entity.NonCoalPay;
 import com.gkhy.exam.pay.entity.NonCoalPayStudent;
+import com.gkhy.exam.pay.entity.PayReqData;
 import com.gkhy.exam.pay.mapper.NonCoalPayMapper;
 import com.gkhy.exam.pay.mapper.NonCoalPayStudentMapper;
 import com.gkhy.exam.pay.service.NonCoalPayStudentService;
+import com.gkhy.exam.pay.utils.PayUtils;
+import com.gkhy.exam.pay.utils.ResultVo;
+import com.ruoyi.common.constant.ResultConstants;
+import com.ruoyi.common.exception.BusinessException;
 import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.RandomUtil;
+import com.ruoyi.common.utils.RedisLock;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 
@@ -31,11 +42,138 @@
  * @date 2025-01-16
  */
 @Service
+@Slf4j
 public class NonCoalPayStudentServiceImpl extends ServiceImpl<NonCoalPayStudentMapper, NonCoalPayStudent> implements NonCoalPayStudentService {
     @Resource
     private NonCoalPayStudentMapper nonCoalPayStudentMapper;
     @Resource
     private NonCoalPayMapper nonCoalPayMapper;
+    @Resource
+    private PayUtils payUtils;
+    @Autowired
+    private RedisLock redisLock;
+
+    private static final String NON_COAL_PAY_STUDENT_LOCK = "NON_COAL_PAY_STUDENT_LOCK_";
+
+
+    @Override
+    public String sendOrder(Long dataId, String payType) {
+
+        String lockKey = NON_COAL_PAY_STUDENT_LOCK + dataId + "_PAY_TYPE_" + payType;
+
+
+        Boolean b = redisLock.tryLock(lockKey, 10, 20, TimeUnit.SECONDS);
+        if (b) {
+            String orderNo = RandomUtil.generateOrderNumber(dataId, "NC");
+            List<NonCoalPayOrder> nonCoalPayOrders = nonCoalPayMapper.selectNonCoalPayOrderByParam(dataId, Long.valueOf(payType));
+            if (StringUtils.isEmpty(nonCoalPayOrders)) {
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "未找到相关缴费信息");
+            }
+            NonCoalPayOrder nonCoalPayOrder = nonCoalPayOrders.get(0);
+
+            List<NonCoalStuRep> nonCoalStuList = nonCoalPayOrder.getNonCoalStuList();
+
+            if (StringUtils.isEmpty(nonCoalStuList)) {
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "未找到需要缴费的学员");
+            }
+            NonCoalStuRep nonCoalStuRep = nonCoalStuList.get(0);
+            if (nonCoalStuRep.getPayStatus() == 1) {
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "已缴费请勿重复缴费");
+            }
+            List<NonCoalCateRep> nonCoalPayCategoryList = nonCoalPayOrder.getNonCoalPayCategoryList();
+
+            if (StringUtils.isEmpty(nonCoalPayCategoryList)) {
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "缴费信息缺失");
+            }
+            // 业务处理
+            //查询主信息   查询是否是团队
+            PayReqData payReqData = dealData(nonCoalPayOrder, orderNo, payType);
+
+            try {
+                ResultVo resultVo = payUtils.sendApiPost(payReqData);
+                log.info("调用接口返回结果:" + resultVo);
+                if (resultVo.getRespcode().equals("BUS0000")) {
+                    String orderId = resultVo.getRespdata().getOrderId();
+                    String billNo = resultVo.getRespdata().getBillNo();
+                    if (StringUtils.isNotEmpty(orderId)) {
+                        // 更新订单
+                        if ("1".equals(payType)) {
+                            baseMapper.update(null, Wrappers.<NonCoalPayStudent>lambdaUpdate()
+                                    .set(NonCoalPayStudent::getOrderId, orderId)
+                                    .set(NonCoalPayStudent::getOrderNo, orderNo)
+                                    .set(NonCoalPayStudent::getPayCode, billNo)
+                                    .eq(NonCoalPayStudent::getId, dataId).eq(NonCoalPayStudent::getDelFlag, 0)
+                                    .eq(NonCoalPayStudent::getPayType, payType).eq(NonCoalPayStudent::getPayStatus, 0));
+                        } else {
+                            //团体缴费
+                            baseMapper.update(null, Wrappers.<NonCoalPayStudent>lambdaUpdate()
+                                    .set(NonCoalPayStudent::getOrderId, orderId)
+                                    .set(NonCoalPayStudent::getOrderNo, orderNo)
+                                    .set(NonCoalPayStudent::getPayCode, billNo)
+                                    .eq(NonCoalPayStudent::getNonCoalPayId, dataId).eq(NonCoalPayStudent::getDelFlag, 0)
+                                    .eq(NonCoalPayStudent::getPayType, payType).eq(NonCoalPayStudent::getPayStatus, 0));
+                        }
+                    }
+                    return orderId;
+                } else {
+                    log.error("生成订单失败:" + resultVo.getRespmsg() + ",请稍后重试,错误编码:" + resultVo.getRespcode() + "参数如下:" + payReqData);
+                    throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, "发起支付失败,请稍后重试");
+                }
+
+            } catch (Exception e) {
+                log.error("发起支付调用接口失败:" + e);
+                throw new BusinessException(this.getClass(), ResultConstants.BUSINESS_ERROR, e.getMessage());
+            } finally {
+                redisLock.unlock(lockKey);
+            }
+        }
+
+
+        return null;
+    }
+
+    private PayReqData dealData(NonCoalPayOrder nonCoalPayOrder, String orderNo, String payType) {
+        List<NonCoalStuRep> nonCoalStuList = nonCoalPayOrder.getNonCoalStuList();
+        List<NonCoalCateRep> nonCoalPayCategoryList = nonCoalPayOrder.getNonCoalPayCategoryList();
+        PayReqData payReqData = new PayReqData();
+        payReqData.setOrderNo(orderNo);
+        payReqData.setPayerType(Integer.valueOf(payType));
+        payReqData.setMoney(nonCoalPayOrder.getAmount().multiply(BigDecimal.valueOf(nonCoalStuList.size())));
+        payReqData.setAmount(nonCoalStuList.size());
+
+        payReqData.setDesc(nonCoalPayOrder.getBatchName());
+
+        NonCoalCateRep nonCoalCateRep1 = nonCoalPayCategoryList.get(0);
+        payReqData.setHandlingPerson(nonCoalCateRep1.getDrawer());
+        payReqData.setChecker(nonCoalCateRep1.getReviewer());
+        payReqData.setEnterCode(nonCoalCateRep1.getCompanyCode());
+        payReqData.setInvoiceSocialCode(nonCoalCateRep1.getInvoicingCompanyCode());
+
+        List<PayReqData.Feedata> feedatas1 = new ArrayList<>();
+        for (NonCoalCateRep nonCoalCateRep : nonCoalPayCategoryList) {
+            PayReqData.Feedata feedatas = new PayReqData.Feedata();
+            feedatas.setAmount(nonCoalStuList.size());
+            feedatas.setBusCode(nonCoalCateRep.getBusinessCode());
+            feedatas.setPrice(nonCoalCateRep.getCategoryAmount());
+            feedatas1.add(feedatas);
+        }
+        payReqData.setFeeDatas(feedatas1);
+        if ("1".equals(payType)) {
+            //个人缴费
+            NonCoalStuRep nonCoalStuRep = nonCoalStuList.get(0);
+            payReqData.setPayerName(nonCoalStuRep.getStudentName());
+            payReqData.setCertNo(nonCoalStuRep.getIdCard());
+        } else {
+            //团体缴费
+            payReqData.setPayerName(nonCoalPayOrder.getPayCompanyName());
+            payReqData.setCertNo(nonCoalPayOrder.getPayCompanyCard());
+            //todo 待确认
+            payReqData.setInvoiceSocialCode(nonCoalPayOrder.getPayCompanyCard());
+            payReqData.setEnterCode(nonCoalPayOrder.getPayCompanyCard());
+        }
+        return payReqData;
+
+    }
 
     /**
      * 查询【请填写功能名称】
@@ -212,7 +350,7 @@
                     stu1.setName(stu.getName());
                     stu1.setIdCard(stu.getIdCard());
                     stu1.setPhone(stu.getPhone());
-               
+
 //                    stu1.setOrderNo(RandomUtil.generateOrderNumber());
                     stu1.setSex(StudentSex.BOY.getStatus().equals(stu.getSex().trim()) ? 0L : 1L);
                     nonCoalPayStudentMapper.insertNonCoalPayStudent(stu1);
diff --git a/exam-system/src/main/java/com/gkhy/exam/pay/utils/PayUtils.java b/exam-system/src/main/java/com/gkhy/exam/pay/utils/PayUtils.java
index 135fade..0c23d46 100644
--- a/exam-system/src/main/java/com/gkhy/exam/pay/utils/PayUtils.java
+++ b/exam-system/src/main/java/com/gkhy/exam/pay/utils/PayUtils.java
@@ -2,8 +2,12 @@
 
 import com.alibaba.fastjson2.JSONObject;
 import com.gkhy.exam.pay.entity.PayReqData;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.http.*;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.StatusLine;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.HttpResponseException;
@@ -25,8 +29,9 @@
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
 
-import javax.annotation.Resource;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
@@ -40,32 +45,45 @@
 /**
  * 缴费相关接口
  */
+@Component
+@Slf4j
 public class PayUtils {
 
-    private final static String appid="ED76A5F1703540BE977D34780B371FEB";
+//    private final static String appid = "ED76A5F1703540BE977D34780B371FEB";
 
+    @Value("${finance.orderUrl}")
+    private String orderUrl;
 
-    public static ResultVo sendApiPost(PayReqData payReqData) throws IOException {
+    @Value("${finance.payNotifyUrl}")
+    private String payNotifyUrl;
+
+    @Value("${finance.payQueryUrl}")
+    private String payQueryUrl;
+
+    @Value("${finance.apiId}")
+    private String appId;
+
+    public ResultVo sendApiPost(PayReqData payReqData) throws IOException {
 
         //正式
-        String proUrl="http://finpt.xjcz.gov.cn/fs-service/fs-pay/invoice.do";
+        String proUrl = "http://finpt.xjcz.gov.cn/fs-service/fs-pay/invoice.do";
         //测试
-        String testUrl= "http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/invoice.do";
+        String testUrl = "http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/invoice.do";
 
         Map<String, String> param = new HashMap<>();
 
-        HttpPost httpPost = new HttpPost(testUrl);
+        HttpPost httpPost = new HttpPost(orderUrl);
 
         //请求参数转为json格式base64编码
         String reqData = Base64.getEncoder().encodeToString(JSONObject.toJSONString(payReqData).getBytes());
-        String mac = appid+"||"+reqData;
+        String mac = appId + "||" + reqData;
         mac = DigestUtils.md5Hex(mac.getBytes());
-        param.put("appid",appid);
-        param.put("reqdata",reqData);
-        param.put("mac",mac);
+        param.put("appid", appId);
+        param.put("reqdata", reqData);
+        param.put("mac", mac);
 
-        httpPost.setEntity(assemblyFormEntity(param,"utf-8"));
-        HttpClient httpClient = getHttpClient(testUrl);
+        httpPost.setEntity(assemblyFormEntity(param, "utf-8"));
+        HttpClient httpClient = getHttpClient(orderUrl);
         HttpResultVo execute = httpClient.execute(httpPost, getResponseHandler());
         String stringContent = execute.getStringContent();
         ResultVo resultVo = JSONObject.parseObject(stringContent, ResultVo.class);
@@ -117,13 +135,15 @@
                 }
 
                 @Override
-                public void checkClientTrusted(X509Certificate[] xcs, String str) {}
+                public void checkClientTrusted(X509Certificate[] xcs, String str) {
+                }
 
                 @Override
-                public void checkServerTrusted(X509Certificate[] xcs, String str) {}
+                public void checkServerTrusted(X509Certificate[] xcs, String str) {
+                }
             };
             SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
-            ctx.init(null, new TrustManager[] {trustManager}, null);
+            ctx.init(null, new TrustManager[]{trustManager}, null);
             SSLConnectionSocketFactory socketFactory =
                     new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
             // 创建Registry
@@ -210,81 +230,79 @@
 
 
     //缴费结果通知
-    public static Map<String,String> receive(JSONObject jsonObject) throws IOException {
+    public Map<String, String> receive(JSONObject jsonObject) throws IOException {
 
         Map<String, String> params = new HashMap<>();
         JSONObject reqdata = new JSONObject();
 
-        Map<String,String> result = (Map<String, String>) jsonObject.get("reqdata");
+        Map<String, String> result = (Map<String, String>) jsonObject.get("reqdata");
         String orderNo = result.get("orderNo");
 
         //确认是否成功
-        String  notarize= affirmPost(orderNo);
-        reqdata.put("orderNo",orderNo);
-        reqdata.put("status",notarize);
+        String notarize = affirmPost(orderNo);
+        reqdata.put("orderNo", orderNo);
+        reqdata.put("status", notarize);
 
         String req = Base64.getEncoder().encodeToString(reqdata.toJSONString().getBytes());
-        String mac = appid+"||"+req;
+        String mac = appId + "||" + req;
         mac = DigestUtils.md5Hex(mac.getBytes());
-        params.put("appid",appid);
-        params.put("reqdata",req);
-        params.put("mac",mac);
+        params.put("appid", appId);
+        params.put("reqdata", req);
+        params.put("mac", mac);
 
         return params;
     }
 
     //缴费结果确认查询
-    public static String affirmPost(String orderNo) throws IOException {
-        String porUrl="http://finpt.xjcz.gov.cn/fs-service/fs-pay/notifyConfirm.do";
-        String testUrl="http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/notifyConfirm.do";
+    public String affirmPost(String orderNo) throws IOException {
 
         HashMap<String, String> param = new HashMap<>();
 
         JSONObject jsonObject = new JSONObject();
-        jsonObject.put("orderNo",orderNo);
+        jsonObject.put("orderNo", orderNo);
         String reqdata = Base64.getEncoder().encodeToString(jsonObject.toJSONString().getBytes());
-        String mac = appid+"||"+reqdata;
-        mac=DigestUtils.md5Hex(mac.getBytes());
-        param.put("appid",appid);
-        param.put("reqdata",reqdata);
-        param.put("mac",mac);
+        String mac = appId + "||" + reqdata;
+        mac = DigestUtils.md5Hex(mac.getBytes());
+        param.put("appid", appId);
+        param.put("reqdata", reqdata);
+        param.put("mac", mac);
 
-        HttpPost httppost = new HttpPost(testUrl);
-        httppost.setEntity(assemblyFormEntity(param,"utf-8"));
-        HttpClient httpClient = getHttpClient(testUrl);
+        HttpPost httppost = new HttpPost(payNotifyUrl);
+        httppost.setEntity(assemblyFormEntity(param, "utf-8"));
+        HttpClient httpClient = getHttpClient(payNotifyUrl);
         HttpResultVo execute = httpClient.execute(httppost, getResponseHandler());
         String stringContent = execute.getStringContent();
         ResultVo resultVo = JSONObject.parseObject(stringContent, ResultVo.class);
-        System.out.printf("请求结果为:"+resultVo);
-        if (resultVo.getRespcode().equals("BUS0000")){
+        log.info("请求结果为:" + resultVo);
+        if (resultVo.getRespcode().equals("BUS0000")) {
             return "success";
         }
         return "fail";
     }
 
     //缴费结果查询
-    public static JSONObject query(String orderNo) throws IOException {
-        String proUrl="http://finpt.xjcz.gov.cn/fs-service/fs-pay/query.do";
-        String testUrl="http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/query.do";
+    public JSONObject query(String orderNo) throws IOException {
+        String proUrl = "http://finpt.xjcz.gov.cn/fs-service/fs-pay/query.do";
+        String testUrl = "http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/query.do";
 
         HashMap<String, String> param = new HashMap<>();
 
         JSONObject jsonObject = new JSONObject();
-        jsonObject.put("orderNo",orderNo);
+        jsonObject.put("orderNo", orderNo);
         String reqdata = Base64.getEncoder().encodeToString(jsonObject.toJSONString().getBytes());
-        String mac = appid+"||"+reqdata;
+        String mac = appId + "||" + reqdata;
         mac = DigestUtils.md5Hex(mac.getBytes());
-        param.put("appid",appid);
-        param.put("reqdata",reqdata);
-        param.put("mac",mac);
+        param.put("appid", appId);
+        param.put("reqdata", reqdata);
+        param.put("mac", mac);
 
-        HttpPost httppost = new HttpPost(testUrl);
-        httppost.setEntity(assemblyFormEntity(param,"utf-8"));
-        HttpClient httpClient = getHttpClient(testUrl);
+        HttpPost httppost = new HttpPost(payQueryUrl);
+        httppost.setEntity(assemblyFormEntity(param, "utf-8"));
+        HttpClient httpClient = getHttpClient(payQueryUrl);
         HttpResultVo execute = httpClient.execute(httppost, getResponseHandler());
         String stringContent = execute.getStringContent();
         JSONObject result = JSONObject.parseObject(stringContent);
-        System.out.printf("请求结果json为:"+result);
+        log.info("请求结果json为:" + result);
         return result;
     }
 }
diff --git a/exam-system/src/main/resources/mapper/pay/NonCoalPayMapper.xml b/exam-system/src/main/resources/mapper/pay/NonCoalPayMapper.xml
index b273dae..352ec81 100644
--- a/exam-system/src/main/resources/mapper/pay/NonCoalPayMapper.xml
+++ b/exam-system/src/main/resources/mapper/pay/NonCoalPayMapper.xml
@@ -202,6 +202,75 @@
 
     </select>
 
+    <resultMap type="com.gkhy.exam.pay.dto.rep.NonCoalPayOrder" id="getNonCoalPayResultOrder">
+        <result property="id" column="id"/>
+        <result property="batchName" column="batch_name"/>
+        <result property="amount" column="amount"/>
+        <result property="payPersonType" column="pay_person_type"/>
+        <result property="payCompanyName" column="pay_company_name"/>
+        <result property="payCompanyCard" column="pay_company_card"/>
+        <collection property="nonCoalPayCategoryList" javaType="java.util.List"
+                    resultMap="nonCoalPayCategoryListOrder"/>
+        <collection property="nonCoalStuList" javaType="java.util.List"
+                    resultMap="nonCoalPayStuListOrder"/>
+    </resultMap>
+    <resultMap id="nonCoalPayCategoryListOrder" type="com.gkhy.exam.pay.dto.rep.NonCoalCateRep">
+        <result property="id" column="non_category_id"/>
+        <result property="businessCode" column="business_code"/>
+        <result property="companyCode" column="company_code"/>
+        <result property="invoicingCompanyCode" column="invoicing_company_code"/>
+        <result property="drawer" column="drawer"/>
+        <result property="reviewer" column="reviewer"/>
+        <result property="categoryId" column="category_id"/>
+        <result property="subjectName" column="subject_name"/>
+        <result property="categoryType" column="category_type"/>
+        <result property="categoryAmount" column="category_amount"/>
+    </resultMap>
+
+    <resultMap id="nonCoalPayStuListOrder" type="com.gkhy.exam.pay.dto.rep.NonCoalStuRep">
+        <result property="id" column="non_coal_student_id"/>
+        <result property="phone" column="phone"/>
+        <result property="idCard" column="id_card"/>
+        <result property="studentName" column="student_name"/>
+        <result property="payStatus" column="pay_status"/>
+        <result property="orderId" column="order_id"/>
+    </resultMap>
+
+
+    <select id="selectNonCoalPayOrderByParam" resultMap="getNonCoalPayResultOrder">
+        select a.id,
+        a.batch_name,
+        a.pay_type,
+        a.amount,
+        a.pay_person_type,
+        a.pay_company_name,
+        a.pay_company_card,
+        b.id as non_category_id,
+        b.category_id,
+        c.subject_name,
+        c.category_type,
+        c.business_code,
+        c.company_code,
+        c.drawer,
+        c.reviewer,
+        c.invoicing_company_code,
+        b.category_amount,
+        t.id as non_coal_student_id,
+        t.phone,
+        t.id_card,
+        t.name as student_name,
+        t.pay_status,
+        t.order_id
+        from non_coal_pay a
+        inner join non_coal_pay_student t on t.non_coal_pay_id = a.id and t.del_flag = 0 and t.pay_status = 0
+        inner join non_coal_pay_category b on a.id = b.non_coal_pay_id and b.del_flag = 0
+        left join non_coal_category c on b.category_id = c.id and c.del_flag = 0
+        where a.del_flag = 0
+        <if test="payType != null and payType == 2 ">and a.id = #{dataId} and t.pay_type = #{payType}</if>
+        <if test="payType != null and payType == 1 ">and t.id = #{dataId} and t.pay_type = #{payType}</if>
+
+    </select>
+
 
     <insert id="insertNonCoalPay" parameterType="NonCoalPay" useGeneratedKeys="true" keyProperty="id">
         insert into non_coal_pay
diff --git a/exam-system/src/test/java/TextPay.java b/exam-system/src/test/java/TextPay.java
index f3b89da..3d46294 100644
--- a/exam-system/src/test/java/TextPay.java
+++ b/exam-system/src/test/java/TextPay.java
@@ -1,21 +1,16 @@
-import com.alibaba.fastjson2.JSONObject;
 import com.gkhy.exam.pay.entity.PayReqData;
 import com.gkhy.exam.pay.utils.PayUtils;
 import com.gkhy.exam.pay.utils.ResultVo;
-import com.google.gson.JsonObject;
 import com.ruoyi.common.utils.RandomUtil;
-import com.ruoyi.common.utils.uuid.UUID;
 import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.junit4.SpringRunner;
 
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 @SpringBootTest(classes = TextPay.class)
 @ActiveProfiles("dev")
@@ -23,53 +18,49 @@
 public class TextPay {
 
 
-   @Test
-   public void paytext() throws IOException {
+    @Test
+    public void paytext() throws IOException {
 
 
-       PayReqData payReqData = new PayReqData();
-       PayReqData.Feedata feedatas = new PayReqData.Feedata();
+        PayReqData payReqData = new PayReqData();
+        PayReqData.Feedata feedatas = new PayReqData.Feedata();
 
-       payReqData.setDesc("安全技术考试考务费_煤矿安全作业-001504");
-       payReqData.setOrderNo(RandomUtil.generateOrderNumber());
-       payReqData.setMoney(BigDecimal.valueOf(103));
-       payReqData.setAmount(1);
-       payReqData.setPayerName("测试");
-       payReqData.setCertNo("411381199801093991");
-       payReqData.setPayerType(1);
-       payReqData.setInvoiceSocialCode("11650000MB1957293J");
-       payReqData.setHandlingPerson("张三");
-       payReqData.setChecker("李四");
-       payReqData.setEnterCode("680534083");
+        payReqData.setDesc("安全技术考试考务费_煤矿安全作业-001504");
+        payReqData.setOrderNo(RandomUtil.generateOrderNumber(1L, "CO"));
+        payReqData.setMoney(BigDecimal.valueOf(103));
+        payReqData.setAmount(1);
+        payReqData.setPayerName("测试");
+        payReqData.setCertNo("411381199801093991");
+        payReqData.setPayerType(1);
+        payReqData.setInvoiceSocialCode("11650000MB1957293J");
+        payReqData.setHandlingPerson("张三");
+        payReqData.setChecker("李四");
+        payReqData.setEnterCode("680534083");
 
-       List<PayReqData.Feedata> feedatas1 = new ArrayList<>();
-       feedatas.setAmount(1);
-       feedatas.setBusCode("DZ001504");
-       feedatas.setPrice(BigDecimal.valueOf(103));
-       feedatas1.add(feedatas);
-       payReqData.setFeeDatas(feedatas1);
+        List<PayReqData.Feedata> feedatas1 = new ArrayList<>();
+        feedatas.setAmount(1);
+        feedatas.setBusCode("DZ001504");
+        feedatas.setPrice(BigDecimal.valueOf(103));
+        feedatas1.add(feedatas);
+        payReqData.setFeeDatas(feedatas1);
 
 
-       PayUtils payUtils = new PayUtils();
-       ResultVo resultVo = payUtils.sendApiPost(payReqData);
-       System.out.printf("请求结果:"+resultVo);
+        PayUtils payUtils = new PayUtils();
+        ResultVo resultVo = payUtils.sendApiPost(payReqData);
+        System.out.printf("请求结果:" + resultVo);
 
-   }
+    }
 
 
-
-   @Test
-    public void text001(){
-       try {
-//           String s = PayUtils.affirmPost("10000001");
-           JSONObject query = PayUtils.query("1000001");
-       } catch (IOException e) {
-           throw new RuntimeException(e);
-       }
-   }
-
-
-
+    @Test
+    public void text001() {
+//       try {
+////           String s = PayUtils.affirmPost("10000001");
+//           //JSONObject query = PayUtils.query("1000001");
+//       } catch (IOException e) {
+//           throw new RuntimeException(e);
+//       }
+    }
 
 
 }
diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml
index 3098c60..a687cfe 100644
--- a/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/ruoyi-admin/src/main/resources/application-dev.yml
@@ -1,132 +1,139 @@
 # 开发环境配置
 server:
-    # 服务器的HTTP端口,默认为8080
-    port: 8085
-    servlet:
-        # 应用的访问路径
-        context-path: /api
-    tomcat:
-        # tomcat的URI编码
-        uri-encoding: UTF-8
-        # 连接数满后的排队数,默认为100
-        accept-count: 1000
-        threads:
-            # tomcat最大线程数,默认为200
-            max: 800
-            # Tomcat启动初始化的线程数,默认值10
-            min-spare: 100
+  # 服务器的HTTP端口,默认为8080
+  port: 8085
+  servlet:
+    # 应用的访问路径
+    context-path: /api
+  tomcat:
+    # tomcat的URI编码
+    uri-encoding: UTF-8
+    # 连接数满后的排队数,默认为100
+    accept-count: 1000
+    threads:
+      # tomcat最大线程数,默认为200
+      max: 800
+      # Tomcat启动初始化的线程数,默认值10
+      min-spare: 100
 
 # 数据源配置
 spring:
-    datasource:
-        type: com.alibaba.druid.pool.DruidDataSource
-        driverClassName: com.mysql.cj.jdbc.Driver
-        druid:
-            # 主库数据源
-            master:
-                url: jdbc:mysql://192.168.2.16:7006/swspkmas?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
-                username: root
-                password: 2farwL3yPXfbH2AP
-            # 从库数据源
-            slave:
-                # 从数据源开关/默认关闭
-                enabled: false
-                url: 
-                username: 
-                password: 
-            # 初始连接数
-            initialSize: 5
-            # 最小连接池数量
-            minIdle: 10
-            # 最大连接池数量
-            maxActive: 20
-            # 配置获取连接等待超时的时间
-            maxWait: 60000
-            # 配置连接超时时间
-            connectTimeout: 30000
-            # 配置网络超时时间
-            socketTimeout: 60000
-            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
-            timeBetweenEvictionRunsMillis: 60000
-            # 配置一个连接在池中最小生存的时间,单位是毫秒
-            minEvictableIdleTimeMillis: 300000
-            # 配置一个连接在池中最大生存的时间,单位是毫秒
-            maxEvictableIdleTimeMillis: 900000
-            # 配置检测连接是否有效
-            validationQuery: SELECT 1 FROM DUAL
-            testWhileIdle: true
-            testOnBorrow: false
-            testOnReturn: false
-            webStatFilter: 
-                enabled: true
-            statViewServlet:
-                enabled: true
-                # 设置白名单,不填则允许所有访问
-                allow:
-                url-pattern: /druid/*
-                # 控制台管理用户名和密码
-                login-username: ruoyi
-                login-password: 123456
-            filter:
-                stat:
-                    enabled: true
-                    # 慢SQL记录
-                    log-slow-sql: true
-                    slow-sql-millis: 1000
-                    merge-sql: true
-                wall:
-                    config:
-                        multi-statement-allow: true
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: com.mysql.cj.jdbc.Driver
+    druid:
+      # 主库数据源
+      master:
+        url: jdbc:mysql://192.168.2.16:7006/swspkmas?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
+        username: root
+        password: 2farwL3yPXfbH2AP
+      # 从库数据源
+      slave:
+        # 从数据源开关/默认关闭
+        enabled: false
+        url:
+        username:
+        password:
+      # 初始连接数
+      initialSize: 5
+      # 最小连接池数量
+      minIdle: 10
+      # 最大连接池数量
+      maxActive: 20
+      # 配置获取连接等待超时的时间
+      maxWait: 60000
+      # 配置连接超时时间
+      connectTimeout: 30000
+      # 配置网络超时时间
+      socketTimeout: 60000
+      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+      timeBetweenEvictionRunsMillis: 60000
+      # 配置一个连接在池中最小生存的时间,单位是毫秒
+      minEvictableIdleTimeMillis: 300000
+      # 配置一个连接在池中最大生存的时间,单位是毫秒
+      maxEvictableIdleTimeMillis: 900000
+      # 配置检测连接是否有效
+      validationQuery: SELECT 1 FROM DUAL
+      testWhileIdle: true
+      testOnBorrow: false
+      testOnReturn: false
+      webStatFilter:
+        enabled: true
+      statViewServlet:
+        enabled: true
+        # 设置白名单,不填则允许所有访问
+        allow:
+        url-pattern: /druid/*
+        # 控制台管理用户名和密码
+        login-username: ruoyi
+        login-password: 123456
+      filter:
+        stat:
+          enabled: true
+          # 慢SQL记录
+          log-slow-sql: true
+          slow-sql-millis: 1000
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
 
-    # redis 配置
-    redis:
-        # 地址
-        host: 192.168.2.16
-        # 端口,默认为6379
-        port: 6379
-        # 数据库索引
-        database: 5
-        # 密码
-#        password: wioowr23923sd3*&
-        # 连接超时时间
-        timeout: 10s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
+  # redis 配置
+  redis:
+    # 地址
+    host: 192.168.2.16
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 5
+    # 密码
+    #    password: wioowr23923sd3*&
+    # 连接超时时间
+    timeout: 10s
+    lettuce:
+      pool:
+        # 连接池中的最小空闲连接
+        min-idle: 0
+        # 连接池中的最大空闲连接
+        max-idle: 8
+        # 连接池的最大数据库连接数
+        max-active: 8
+        # #连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-wait: -1ms
 tripartite:
-    restSever: https://inspurtestcx.saws.org.cn/sjjh
-    appKey: hj92qe
-    appPwd: dxep6j
+  restSever: https://inspurtestcx.saws.org.cn/sjjh
+  appKey: hj92qe
+  appPwd: dxep6j
 
 #windous测试
 file:
-    path:
-        #基础路径
-        dcPath: upload
-        urlRootPath: /uploadfile/
-        module:
-            #用户模块
-            accountPath: /account/
+  path:
+    #基础路径
+    dcPath: upload
+    urlRootPath: /uploadfile/
+    module:
+      #用户模块
+      accountPath: /account/
 
 #线程池配置
 threadPool:
-    corePoolSize: 20
-    maxPoolSize: 20
-    queueCapacity: 10000
-    scheduling:
-        #控制线程是否执行 true:执行;false:不执行
-        enabled: true
+  corePoolSize: 20
+  maxPoolSize: 20
+  queueCapacity: 10000
+  scheduling:
+    #控制线程是否执行 true:执行;false:不执行
+    enabled: true
 
 
 # 七牛云相关信息
 safecheckqiniuymes:
-    accesskey: 5YprpjY0BJiyjII2VqlHed7UhBEvvkPZicbwd8Kl
-    secretkey: m3gGQNQ9cLmVBBZwPXZ5-Wzr0duzyAPPmJUx4_ay
-    templateid: 1844221365930962944
\ No newline at end of file
+  accesskey: 5YprpjY0BJiyjII2VqlHed7UhBEvvkPZicbwd8Kl
+  secretkey: m3gGQNQ9cLmVBBZwPXZ5-Wzr0duzyAPPmJUx4_ay
+  templateid: 1844221365930962944
+
+#财政接口地址及api
+finance:
+  apiId: ED76A5F1703540BE977D34780B371FEB
+  orderUrl: http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/invoice.do
+  payNotifyUrl: http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/notifyConfirm.do
+  payQueryUrl: http://finpt.xjcz.gov.cn/fs-service/fs-pay/query.do
diff --git a/ruoyi-admin/src/main/resources/application-pro.yml b/ruoyi-admin/src/main/resources/application-pro.yml
index cb188b5..20fc39e 100644
--- a/ruoyi-admin/src/main/resources/application-pro.yml
+++ b/ruoyi-admin/src/main/resources/application-pro.yml
@@ -1,131 +1,138 @@
 # 开发环境配置
 server:
-    # 服务器的HTTP端口,默认为8080
-    port: 8085
-    servlet:
-        # 应用的访问路径
-        context-path: /api
-    tomcat:
-        # tomcat的URI编码
-        uri-encoding: UTF-8
-        # 连接数满后的排队数,默认为100
-        accept-count: 1000
-        threads:
-            # tomcat最大线程数,默认为200
-            max: 800
-            # Tomcat启动初始化的线程数,默认值10
-            min-spare: 100
+  # 服务器的HTTP端口,默认为8080
+  port: 8085
+  servlet:
+    # 应用的访问路径
+    context-path: /api
+  tomcat:
+    # tomcat的URI编码
+    uri-encoding: UTF-8
+    # 连接数满后的排队数,默认为100
+    accept-count: 1000
+    threads:
+      # tomcat最大线程数,默认为200
+      max: 800
+      # Tomcat启动初始化的线程数,默认值10
+      min-spare: 100
 
 # 数据源配置
 spring:
-    datasource:
-        type: com.alibaba.druid.pool.DruidDataSource
-        driverClassName: com.mysql.cj.jdbc.Driver
-        druid:
-            # 主库数据源
-            master:
-                url: jdbc:mysql://127.0.0.1:7006/swspkmas?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
-                username: root
-                password: 2farwL3yPXfbH2AP
-            # 从库数据源
-            slave:
-                # 从数据源开关/默认关闭
-                enabled: false
-                url: 
-                username: 
-                password: 
-            # 初始连接数
-            initialSize: 5
-            # 最小连接池数量
-            minIdle: 10
-            # 最大连接池数量
-            maxActive: 20
-            # 配置获取连接等待超时的时间
-            maxWait: 60000
-            # 配置连接超时时间
-            connectTimeout: 30000
-            # 配置网络超时时间
-            socketTimeout: 60000
-            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
-            timeBetweenEvictionRunsMillis: 60000
-            # 配置一个连接在池中最小生存的时间,单位是毫秒
-            minEvictableIdleTimeMillis: 300000
-            # 配置一个连接在池中最大生存的时间,单位是毫秒
-            maxEvictableIdleTimeMillis: 900000
-            # 配置检测连接是否有效
-            validationQuery: SELECT 1 FROM DUAL
-            testWhileIdle: true
-            testOnBorrow: false
-            testOnReturn: false
-            webStatFilter: 
-                enabled: true
-            statViewServlet:
-                enabled: true
-                # 设置白名单,不填则允许所有访问
-                allow:
-                url-pattern: /druid/*
-                # 控制台管理用户名和密码
-                login-username: ruoyi
-                login-password: 123456
-            filter:
-                stat:
-                    enabled: true
-                    # 慢SQL记录
-                    log-slow-sql: true
-                    slow-sql-millis: 1000
-                    merge-sql: true
-                wall:
-                    config:
-                        multi-statement-allow: true
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: com.mysql.cj.jdbc.Driver
+    druid:
+      # 主库数据源
+      master:
+        url: jdbc:mysql://127.0.0.1:7006/swspkmas?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
+        username: root
+        password: 2farwL3yPXfbH2AP
+      # 从库数据源
+      slave:
+        # 从数据源开关/默认关闭
+        enabled: false
+        url:
+        username:
+        password:
+      # 初始连接数
+      initialSize: 5
+      # 最小连接池数量
+      minIdle: 10
+      # 最大连接池数量
+      maxActive: 20
+      # 配置获取连接等待超时的时间
+      maxWait: 60000
+      # 配置连接超时时间
+      connectTimeout: 30000
+      # 配置网络超时时间
+      socketTimeout: 60000
+      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+      timeBetweenEvictionRunsMillis: 60000
+      # 配置一个连接在池中最小生存的时间,单位是毫秒
+      minEvictableIdleTimeMillis: 300000
+      # 配置一个连接在池中最大生存的时间,单位是毫秒
+      maxEvictableIdleTimeMillis: 900000
+      # 配置检测连接是否有效
+      validationQuery: SELECT 1 FROM DUAL
+      testWhileIdle: true
+      testOnBorrow: false
+      testOnReturn: false
+      webStatFilter:
+        enabled: true
+      statViewServlet:
+        enabled: true
+        # 设置白名单,不填则允许所有访问
+        allow:
+        url-pattern: /druid/*
+        # 控制台管理用户名和密码
+        login-username: ruoyi
+        login-password: 123456
+      filter:
+        stat:
+          enabled: true
+          # 慢SQL记录
+          log-slow-sql: true
+          slow-sql-millis: 1000
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
 
-    # redis 配置
-    redis:
-        # 地址
-        host: 127.0.0.1
-        # 端口,默认为6379
-        port: 6379
-        # 数据库索引
-        database: 5
-        # 密码
-        password: akj78avauba789a
-        # 连接超时时间
-        timeout: 10s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
+  # redis 配置
+  redis:
+    # 地址
+    host: 127.0.0.1
+    # 端口,默认为6379
+    port: 6379
+    # 数据库索引
+    database: 5
+    # 密码
+    password: akj78avauba789a
+    # 连接超时时间
+    timeout: 10s
+    lettuce:
+      pool:
+        # 连接池中的最小空闲连接
+        min-idle: 0
+        # 连接池中的最大空闲连接
+        max-idle: 8
+        # 连接池的最大数据库连接数
+        max-active: 8
+        # #连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-wait: -1ms
 tripartite:
-    restSever: https://inspurtestcx.saws.org.cn/sjjh
-    appKey: hj92qe
-    appPwd: dxep6j
+  restSever: https://inspurtestcx.saws.org.cn/sjjh
+  appKey: hj92qe
+  appPwd: dxep6j
 
 #windous测试
 file:
-    path:
-        #基础路径
-        dcPath: upload
-        urlRootPath: /uploadfile/
-        module:
-            #用户模块
-            accountPath: /account/
+  path:
+    #基础路径
+    dcPath: upload
+    urlRootPath: /uploadfile/
+    module:
+      #用户模块
+      accountPath: /account/
 
 #线程池配置
 threadPool:
-    corePoolSize: 20
-    maxPoolSize: 20
-    queueCapacity: 10000
-    scheduling:
-        #控制线程是否执行 true:执行;false:不执行
-        enabled: true
+  corePoolSize: 20
+  maxPoolSize: 20
+  queueCapacity: 10000
+  scheduling:
+    #控制线程是否执行 true:执行;false:不执行
+    enabled: true
 
 # 七牛云相关信息
 safecheckqiniuymes:
-    accesskey: 5YprpjY0BJiyjII2VqlHed7UhBEvvkPZicbwd8Kl
-    secretkey: m3gGQNQ9cLmVBBZwPXZ5-Wzr0duzyAPPmJUx4_ay
-    templateid: 1844221365930962944
\ No newline at end of file
+  accesskey: 5YprpjY0BJiyjII2VqlHed7UhBEvvkPZicbwd8Kl
+  secretkey: m3gGQNQ9cLmVBBZwPXZ5-Wzr0duzyAPPmJUx4_ay
+  templateid: 1844221365930962944
+
+
+finance:
+  apiId: ED76A5F1703540BE977D34780B371FEB
+  orderUrl: http://finpt.xjcz.gov.cn/fs-service/fs-pay/invoice.do
+  payNotifyUrl: http://finpt.xjcz.gov.cn/fs-service/fs-pay/notifyConfirm.do
+  payQueryUrl: http://finpt.xjcz.gov.cn/fs-service-test/fs-pay/query.do
\ No newline at end of file
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RedissonConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RedissonConfig.java
new file mode 100644
index 0000000..a2ba522
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RedissonConfig.java
@@ -0,0 +1,29 @@
+package com.ruoyi.common.config;
+
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RedissonConfig {
+    @Value("${spring.redis.host}")
+    private String host;
+
+    @Value("${spring.redis.port}")
+    private String port;
+
+//    @Value("${spring.redis.password}")
+//    private String password;
+
+    @Bean(destroyMethod = "shutdown")
+    @ConditionalOnMissingBean(RedissonClient.class)
+    public RedissonClient redissonClient() {
+        Config config = new Config();
+        config.useSingleServer().setAddress("redis://" + host + ":" + port); // 更多.set
+        return Redisson.create(config);
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java
index 89ed10b..80a68c2 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java
@@ -35,20 +35,20 @@
     public static void main(String[] args) {
         String s = generateRandomString(26);
         System.out.println(s);
-        System.out.println(generateOrderNumber());
+        System.out.println(generateOrderNumber(10L, "NC"));
 
     }
 
+    private static final Random RANDOM = new Random();
     private static final AtomicInteger SEQUENCE = new AtomicInteger(0);
     private static final int MAX_SEQUENCE = 99999;// 5位数的序列号
     private static final String ORDER_PER = "SN";
 
-    public static synchronized String generateOrderNumber() {
+    public static String generateOrderNumber(Long dataId, String orderType) {
         String datePart = new SimpleDateFormat("yyyyMMddhhmm").format(new Date());
-        int sequencePart = SEQUENCE.getAndIncrement() % MAX_SEQUENCE;
-        String sequenceStr = String.format("%05d", sequencePart); // 保证序列号是5位数,不足的前面补0
+        int randomPart = RANDOM.nextInt(90000) + 10000; // 生成5位随机数
 
-        return ORDER_PER + datePart + sequenceStr;
+        return ORDER_PER + datePart + String.format("%05d", randomPart) + dataId;
     }
 
 }
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisLock.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisLock.java
new file mode 100644
index 0000000..e7d24b8
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RedisLock.java
@@ -0,0 +1,98 @@
+package com.ruoyi.common.utils;
+
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class RedisLock {
+    @Autowired
+    private RedissonClient redissonClient;
+
+    /**
+     * 获取锁
+     *
+     * @param lockKey 锁实例key
+     * @return 锁信息
+     */
+    public RLock getRLock(String lockKey) {
+        return redissonClient.getLock(lockKey);
+    }
+
+    /**
+     * 加锁
+     *
+     * @param lockKey 锁实例key
+     * @return 锁信息
+     */
+    public RLock lock(String lockKey) {
+        RLock lock = getRLock(lockKey);
+        lock.lock();
+        return lock;
+    }
+
+    /**
+     * 加锁
+     *
+     * @param lockKey   锁实例key
+     * @param leaseTime 上锁后自动释放锁时间
+     * @return true=成功;false=失败
+     */
+    public Boolean tryLock(String lockKey, long leaseTime) {
+        return tryLock(lockKey, 0, leaseTime, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 加锁
+     *
+     * @param lockKey   锁实例key
+     * @param leaseTime 上锁后自动释放锁时间
+     * @param unit      时间颗粒度
+     * @return true=加锁成功;false=加锁失败
+     */
+    public Boolean tryLock(String lockKey, long leaseTime, TimeUnit unit) {
+        return tryLock(lockKey, 0, leaseTime, unit);
+    }
+
+    /**
+     * 加锁
+     *
+     * @param lockKey   锁实例key
+     * @param waitTime  最多等待时间
+     * @param leaseTime 上锁后自动释放锁时间
+     * @param unit      时间颗粒度
+     * @return true=加锁成功;false=加锁失败
+     */
+    public Boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
+        RLock rLock = getRLock(lockKey);
+        boolean tryLock = false;
+        try {
+            tryLock = rLock.tryLock(waitTime, leaseTime, unit);
+        } catch (InterruptedException e) {
+            return false;
+        }
+        return tryLock;
+    }
+
+    /**
+     * 释放锁
+     *
+     * @param lockKey 锁实例key
+     */
+    public void unlock(String lockKey) {
+        RLock lock = getRLock(lockKey);
+        lock.unlock();
+    }
+
+    /**
+     * 释放锁
+     *
+     * @param lock 锁信息
+     */
+    public void unlock(RLock lock) {
+        lock.unlock();
+    }
+}

--
Gitblit v1.9.2