liujie
2025-12-29 7e2eb8af7459da7a03a23606651dfbfb5a5f2cee
文档调整
9个文件已修改
310 ■■■■■ 已修改文件
ruoyi-admin/src/main/resources/application.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/InsuranceController.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxPayController.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/application-prod.yml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TInsuranceOrder.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInsuranceVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wxPay/model/V3.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wxPay/utils/WxRefundUtil.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wxPay/utils/WxV3Pay.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml
@@ -1,4 +1,4 @@
# 项目相关配置
spring:
  profiles:
    active: prod
    active: test
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/InsuranceController.java
@@ -1,22 +1,36 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.BaseModel;
import com.ruoyi.common.core.domain.BasePage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.OrderNos;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.dto.BuyInsuranceDto;
import com.ruoyi.system.model.TBanner;
import com.ruoyi.system.model.TCourse;
import com.ruoyi.system.model.TInsuranceOrder;
import com.ruoyi.system.model.*;
import com.ruoyi.system.query.GetCourseListQuery;
import com.ruoyi.system.service.TAppUserService;
import com.ruoyi.system.service.TBannerService;
import com.ruoyi.system.service.TInsuranceOrderService;
import com.ruoyi.system.service.TInsuranceService;
import com.ruoyi.system.vo.UserInsuranceVo;
import com.ruoyi.system.wxPay.utils.WxRefundUtil;
import com.ruoyi.system.wxPay.utils.WxV3Pay;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
@Slf4j
@@ -27,10 +41,16 @@
    private final TInsuranceOrderService tInsuranceOrderService;
    private final TokenService tokenService;
    private final TInsuranceService tInsuranceService;
    private final WxV3Pay wxV3Pay;
    private final TAppUserService  tAppUserService;
    public InsuranceController(TInsuranceOrderService tInsuranceOrderService, TokenService tokenService) {
    public InsuranceController(TInsuranceOrderService tInsuranceOrderService, TokenService tokenService, TInsuranceService tInsuranceService, WxV3Pay wxV3Pay, TAppUserService tAppUserService) {
        this.tInsuranceOrderService = tInsuranceOrderService;
        this.tokenService = tokenService;
        this.tInsuranceService = tInsuranceService;
        this.wxV3Pay = wxV3Pay;
        this.tAppUserService = tAppUserService;
    }
@@ -55,12 +75,16 @@
    @PostMapping("/buyInsurance")
    @ApiOperation(value = "保险模块--购买保险")
    public R<?> buyInsurance(@RequestBody BuyInsuranceDto dto) {
    public R<?> buyInsurance(@RequestBody BuyInsuranceDto dto) throws ParseException {
        String userId = tokenService.getLoginUserApplet().getUserId();
        TAppUser appUser = tAppUserService.getById(userId);
        // 查询保险订单
        TInsuranceOrder one = tInsuranceOrderService.getOne(Wrappers.<TInsuranceOrder>lambdaQuery().eq(TInsuranceOrder::getAppUserId, userId).eq(TInsuranceOrder::getStatus, 2).orderByDesc(BaseModel::getCreateTime).last("LIMIT 1"));
        if(one!=null){
            if(one.getEndTime().after(new Date())){
            return R.fail("您已购买保险");
            }
        }
        TInsuranceOrder insuranceOrder = new TInsuranceOrder();
        insuranceOrder.setAppUserId(userId);
@@ -69,15 +93,86 @@
        insuranceOrder.setIdCard(dto.getIdCard());
        insuranceOrder.setImg(dto.getImg());
        insuranceOrder.setPayType(dto.getType());
        TInsurance insurance = tInsuranceService.getOne(new LambdaQueryWrapper<TInsurance>().eq(TInsurance::getStatus, 1));
        if(insurance==null){
            return R.fail("保险已关闭");
        }
        insuranceOrder.setInsuranceId(insurance.getId());
        insuranceOrder.setPayMoney(BigDecimal.valueOf(insurance.getInsuranceMoney()).setScale(2, RoundingMode.HALF_UP));
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = simpleDateFormat.parse(insurance.getValidityTime());
        insuranceOrder.setEndTime(date);
        String payNo = OrderNos.getDid();
        insuranceOrder.setPayNo(payNo);
        boolean save = tInsuranceOrderService.save(insuranceOrder);
        if(save){
            return R.ok("购买成功");
        }
        return R.fail("购买失败");
        Map<String, Object> result = wxV3Pay.jsApiInsurance(payNo, insuranceOrder.getPayMoney().multiply(BigDecimal.valueOf(100)).longValue(), appUser.getOpenId(),"购买保险","");
        return R.ok(result);
    }
    @ApiOperation(value = "保险模块--缴费记录")
    @PostMapping("/getInsuranceOrderList")
    public R<?> getInsuranceOrderList(@RequestBody BasePage query) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        Page<TInsuranceOrder> page = tInsuranceOrderService.page(new Page<>(query.getPageNum(), query.getPageSize()), Wrappers.<TInsuranceOrder>lambdaQuery().eq(TInsuranceOrder::getAppUserId, userId).ne(TInsuranceOrder::getStatus,0).orderByDesc(BaseModel::getCreateTime));
        return R.ok(page);
    }
    @PostMapping("/refundOrder")
    @ApiOperation(value = "保险模块--退款")
    public R<?> refundOrder(@RequestBody TInsuranceOrder order) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        TInsuranceOrder order1 = tInsuranceOrderService.getById(order.getId());
        order1.setRefundReason(order.getRefundReason());
        order1.setRefundReasonPictures(order.getRefundReasonPictures());
        order1.setRequestRefundTime(new Date());
        order1.setRefundMoney(order1.getPayMoney().doubleValue());
        if(!userId.equals(order1.getAppUserId())){
            return R.fail("无权限操作");
        }
        if(order1.getStatus()!=2){
            return R.fail("状态不允许退款");
        }
        order1.setStatus(4);
        LocalDateTime payTime = order1.getPayTime();
        if(payTime==null){
            return R.fail("状态不允许退款");
        }
        if(payTime.plusDays(2).isBefore(LocalDateTime.now())){
            order1.setBankCardNumber(order.getBankCardNumber());
            order1.setCardholderName(order.getCardholderName());
            order1.setRefundStatus(1);
            tInsuranceOrderService.updateById(order1);
        }else {
            String outRefundNo = OrderNos.getDid();
            order1.setRefundNumber(outRefundNo);
            tInsuranceOrderService.updateById(order1);
//            tInsuranceOrderService refundModel = new WxPaymentRefundModel();
////            refundModel.setSub_mchid(weixinPayProperties.getMchId());
////            refundModel.setSp_appid(weixinPayProperties.getAppId());
////            refundModel.setTransaction_id(courseOrder1.getTransactionId());
//            refundModel.setOut_trade_no(courseOrder1.getOrderNumber());
//            refundModel.setOut_refund_no(OrderNos.getDid());
//            refundModel.setReason(courseOrder1.getRefundReason());
//            refundModel.setGoods_detail(new ArrayList<>());
//            refundModel.setAmount(new WxPaymentRefundModel.RefundAmount(courseOrder1.getPayMoney().multiply(BigDecimal.valueOf(100)).intValue(), "CNY", courseOrder1.getPayMoney().multiply(BigDecimal.valueOf(100)).intValue()));
//            Map<String, Object> refund = wxV3Pay.refund(refundModel);
//            System.out.println(refund);
            String result = wxRefundUtil.refund(order1.getPayMoney().multiply(BigDecimal.valueOf(100)).intValue(), order1.getPayNo(),outRefundNo);
            if(result!=null){
                return R.fail(result);
            }
        }
        return R.ok();
    }
    @Autowired
    private WxRefundUtil wxRefundUtil;
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxPayController.java
@@ -7,7 +7,10 @@
import com.ruoyi.common.utils.CodeGenerateUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.model.TCourseOrder;
import com.ruoyi.system.model.TInsurance;
import com.ruoyi.system.model.TInsuranceOrder;
import com.ruoyi.system.service.TCourseOrderService;
import com.ruoyi.system.service.TInsuranceOrderService;
import com.ruoyi.system.wxPay.enums.RefundEnum;
import com.ruoyi.system.wxPay.enums.TradeStateEnum;
import com.ruoyi.system.wxPay.model.WeixinPayProperties;
@@ -28,6 +31,8 @@
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
@@ -49,6 +54,9 @@
    @Autowired
    private TCourseOrderService courseOrderService;
    @Autowired
    private TInsuranceOrderService tInsuranceOrderService;
@@ -90,6 +98,55 @@
                order.setPayTime(WxTimeUtils.dateToLocalDateTime(WxTimeUtils.toRfc3339Date(success_time)));
                order.setTransactionId(transaction_id);
                courseOrderService.updateById(order);
                PrintWriter out = response.getWriter();
                out.print("SUCCESS");
                out.flush();
                out.close();
            }
        } catch (Exception e) {
            log.error("支付回调异常:{}", e, e);
            wxV3Pay.ack(false, e.getMessage());
        }
    }
    /**
     * 支付回调
     */
    @PostMapping("pay/notifyInsurance")
    @ApiOperation("保险订单回调")
    public void notifyInsurance(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            System.err.println("微信回调");
            BufferedReader reader = request.getReader();
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
            System.err.println("全部请求体" + requestBody);
            JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
            JSONObject resource = jsonObject.getJSONObject("resource");
            AesUtil aesUtil = new AesUtil(weixinPayProperties.getV3().getApiKey().getBytes(StandardCharsets.UTF_8));
            String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
                    resource.getString("ciphertext"));
            System.err.println("微信解密的字符串信息" + decryptedData);
            JSONObject jsonInfo = JSONObject.parse(decryptedData);
            String out_trade_no = jsonInfo.getString("out_trade_no");
            String transaction_id = jsonInfo.getString("transaction_id");
            String trade_state = jsonInfo.getString("trade_state");
            String success_time = jsonInfo.getString("success_time");
            // 查询订单
            TInsuranceOrder order = tInsuranceOrderService.getOne(Wrappers.lambdaQuery(TInsuranceOrder.class)
                    .eq(TInsuranceOrder::getPayNo, out_trade_no).last("LIMIT 1"));
            if (trade_state.equals(TradeStateEnum.SUCCESS.name())) {
                log.info("回调成功");
                // 订单号查询订单
                order.setStatus(2);
                order.setPayTime(LocalDateTime.now());
                order.setTransactionId(transaction_id);
                tInsuranceOrderService.updateById(order);
                PrintWriter out = response.getWriter();
                out.print("SUCCESS");
@@ -168,4 +225,50 @@
            wxV3Pay.ack(false, "不是成功的退款状态");
        }
    }
    @PostMapping("refund/notifyInsurance")
    public void notifyInsurance(HttpServletRequest request) throws IOException {
        try {
            System.err.println("微信退款回调");
            BufferedReader reader = request.getReader();
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
            System.err.println("全部请求体" + requestBody);
            JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
            JSONObject resource = jsonObject.getJSONObject("resource");
            AesUtil aesUtil = new AesUtil(weixinPayProperties.getV3().getApiKey().getBytes(StandardCharsets.UTF_8));
            String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
                    resource.getString("ciphertext"));
            System.err.println("微信解密的字符串信息" + decryptedData);
            JSONObject params = JSONObject.parseObject(decryptedData);
            // 商户订单号
            String out_trade_no = params.get("out_trade_no").toString();
            // 商户退款单号
            String out_refund_no = params.get("out_refund_no").toString();
            // 退款状态
            String tradeState = params.get("refund_status").toString();
            // 微信支付订单号
            String transaction_id = params.get("transaction_id").toString();
            if (tradeState.equals(RefundEnum.SUCCESS.name())) {
                TInsuranceOrder order = tInsuranceOrderService.getOne(Wrappers.lambdaQuery(TInsuranceOrder.class)
                        .eq(TInsuranceOrder::getRefundNumber, out_refund_no).last("LIMIT 1"));
                order.setStatus(1);
                order.setRefundStatus(2);
                order.setRefundTransactionId(transaction_id);
                tInsuranceOrderService.updateById(order);
                wxV3Pay.ack();
            }
        }catch (Exception e){
            wxV3Pay.ack(false, "不是成功的退款状态");
        }
    }
}
ruoyi-applet/src/main/resources/application-prod.yml
@@ -226,5 +226,9 @@
      mchSerialNo: 5ED4C99FE5657DA2579BE5E7AE4255C9468627D9
      # 支付成功回调地址
      notifyPayUrl: https://rhljsq.pzhsoar.com/applet/wx/pay/notify
      # 保险支付成功回调地址
      insuranceNotifyPayUrl: https://rhljsq.pzhsoar.com/applet/wx/pay/notifyInsurance
      # 支付退款回调地址
      notifyRefundUrl: https://rhljsq.pzhsoar.com/applet/wx/refund/notify
      # 保险支付退款回调地址
      notifyInsuranceRefundUrl: https://rhljsq.pzhsoar.com/applet/wx/refund/notifyInsurance
ruoyi-system/src/main/java/com/ruoyi/system/model/TInsuranceOrder.java
@@ -15,6 +15,7 @@
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
/**
@@ -81,7 +82,7 @@
    @TableField("pay_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @ApiModelProperty(value = "支付时间")
    private Date payTime;
    private LocalDateTime payTime;
    @TableField("end_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @ApiModelProperty(value = "截至时间")
ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInsuranceVo.java
@@ -5,6 +5,7 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
@Data
@@ -16,7 +17,7 @@
    @ApiModelProperty("保险支付时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date payTime;
    private LocalDateTime payTime;
    @ApiModelProperty("保险到期时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
ruoyi-system/src/main/java/com/ruoyi/system/wxPay/model/V3.java
@@ -48,11 +48,24 @@
    private String notifyPayUrl;
    /**
     * 支付回调地址 保险
     *
     * @return
     */
    private String insuranceNotifyPayUrl;
    /**
     * 退款回调地址
     *
     * @return
     */
    private String notifyRefundUrl;
    /**
     * 退款回调地址
     *
     * @return
     */
    private String notifyInsuranceRefundUrl;
    /**
     * 退款回调地址
ruoyi-system/src/main/java/com/ruoyi/system/wxPay/utils/WxRefundUtil.java
@@ -80,6 +80,41 @@
      // TODO: 请求失败,根据状态码执行不同的逻辑
    }
  }
  public String refundInsurance(int money,String outTradeNo,String outRefundNo) {
    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
    WxRefundUtil client = new WxRefundUtil(
           this. wxV3Pay.getConfig().getMchId(),                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
            this. wxV3Pay.getConfig().getV3().getMchSerialNo(),         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
            this. wxV3Pay.getConfig().getV3().getPrivateKeyPath() ,     // 商户API证书私钥文件路径,本地文件路径
            this. wxV3Pay.getConfig().getV3().getPublicKeyId() ,      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
            this. wxV3Pay.getConfig().getV3().getPublicKeyPath()         // 微信支付公钥文件路径,本地文件路径
    );
    CreateRequest request = new CreateRequest();
    request.outTradeNo = outTradeNo;
    request.reason = "退款";
    request.fundsAccount = ReqFundsAccount.AVAILABLE;
    request.notifyUrl =this.wxV3Pay.getConfig().getV3().getNotifyRefundUrl();
    request.amount = new AmountReq();
    request.amount.refund = money;
    request.outRefundNo =outRefundNo;
    request.amount.total = money;
    request.amount.currency = "CNY";
    request.goodsDetail = new ArrayList<>();
    try {
      Refund response = client.run(request);
        // TODO: 请求成功,继续业务逻辑
        System.out.println(response);
        return null;
    } catch (WXPayUtility.ApiException e) {
      String body = e.getBody();
      JSONObject jsonObject = JSONObject.parseObject(body);
      String result = jsonObject.get("message").toString();
      return result;
      // TODO: 请求失败,根据状态码执行不同的逻辑
    }
  }
  public Refund run(CreateRequest request) {
    String uri = PATH;
ruoyi-system/src/main/java/com/ruoyi/system/wxPay/utils/WxV3Pay.java
@@ -138,6 +138,36 @@
            throw new RuntimeException("获取支付数据错误!");
        }
    }
    public Map<String, Object> jsApiInsurance(String tradeNo, Long amount, String openid, String description,String attach) {
        JsapiPrepay client = new JsapiPrepay(
                this.config.getMchId(),                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
                this.config.getV3().getMchSerialNo(),         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
                this.config.getV3().getPrivateKeyPath(),     // 商户API证书私钥文件路径,本地文件路径
                this.config.getV3().getPublicKeyId(),      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
                this.config.getV3().getPublicKeyPath()           // 微信支付公钥文件路径,本地文件路径
        );
        JsapiPrepay.DirectAPIv3JsapiPrepayRequest request = new JsapiPrepay.DirectAPIv3JsapiPrepayRequest();
        request.appid = this.config.getAppId();
        request.mchid = this.config.getMchId();
        request.description = description;
        request.outTradeNo = tradeNo;
//        request.timeExpire = "2018-06-08T10:34:56+08:00";
        request.attach = attach;
        request.notifyUrl = this.config.getV3().getInsuranceNotifyPayUrl();
        request.goodsTag = "WXG";
        request.supportFapiao = false;
        request.amount = new JsapiPrepay.CommonAmountInfo();
        request.amount.total = amount;
        request.amount.currency = "CNY";
        request.payer = new JsapiPrepay.JsapiReqPayerInfo();
        request.payer.openid = openid;
        try {
            JsapiPrepay.DirectAPIv3JsapiPrepayResponse response = client.run(request);
            return wxTuneUp(this.privateKeySigner, this.config.getAppId(), response.prepayId);
        } catch (WXPayUtility.ApiException e) {
            throw new RuntimeException("获取支付数据错误!");
        }
    }
//    /**
//     * jsApi下单
//     *