xuhy
2025-11-13 b85a58e00895b366277554ab28cc2d9c0492d872
Merge remote-tracking branch 'origin/master'
4个文件已添加
5个文件已修改
374 ■■■■■ 已修改文件
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/CourseController.java 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxPayController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TCourse.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TCourseOrder.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/CourseOrderListQuery.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/GetCourseListQuery.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/task/base/AbstractJob.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/task/base/TimeJobType.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/OrderJob.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/CourseController.java
New file
@@ -0,0 +1,186 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.ImmutableMap;
import com.ruoyi.common.core.domain.BasePage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.OrderNos;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.model.TAppUser;
import com.ruoyi.system.model.TCourse;
import com.ruoyi.system.model.TCourseOrder;
import com.ruoyi.system.model.TEmploymentInfo;
import com.ruoyi.system.query.CourseOrderListQuery;
import com.ruoyi.system.query.GetCourseListQuery;
import com.ruoyi.system.service.TAppUserService;
import com.ruoyi.system.service.TCourseOrderService;
import com.ruoyi.system.service.TCourseService;
import com.ruoyi.system.service.TEmploymentInfoService;
import com.ruoyi.system.task.base.QuartzManager;
import com.ruoyi.system.task.base.TimeJobType;
import com.ruoyi.system.task.jobs.OrderJob;
import com.ruoyi.system.wxPay.model.WeixinPayProperties;
import com.ruoyi.system.wxPay.model.WxPaymentRefundModel;
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.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
@Slf4j
@RestController
@RequestMapping("/course")
@Api(tags = "课程模块")
public class CourseController {
    private final TCourseService courseService;
    private final TCourseOrderService courseOrderService;
    private final TokenService tokenService;
    private final WxV3Pay wxV3Pay;
    private final WeixinPayProperties weixinPayProperties;
    private final TAppUserService appUserService;
    public CourseController(TCourseService courseService, TCourseOrderService courseOrderService, TokenService tokenService, WxV3Pay wxV3Pay, WeixinPayProperties weixinPayProperties, TAppUserService appUserService) {
        this.courseService = courseService;
        this.courseOrderService = courseOrderService;
        this.tokenService = tokenService;
        this.wxV3Pay = wxV3Pay;
        this.weixinPayProperties = weixinPayProperties;
        this.appUserService = appUserService;
    }
    @PostMapping("/getCourseList")
    @ApiOperation(value = "课程模块--列表",response =  TCourse.class)
    public R<Page<TCourse>> getCourseList(@RequestBody GetCourseListQuery query) {
        Page<TCourse> page = new Page<>(query.getPageNum(), query.getPageSize());
        Page<TCourse> coursePage = courseService.page(page, Wrappers.<TCourse>lambdaQuery().eq(TCourse::getCourseType,query.getCourseType()).like(Objects.nonNull(query.getCourseTitle()), TCourse::getCourseTitle, query.getCourseTitle()).orderByDesc(TCourse::getCreateTime));
        return R.ok(coursePage);
    }
    @PostMapping("/getCourseDetail/{id}")
    @ApiOperation(value = "课程模块--详情",response =  TCourse.class)
    public R<TCourse> getCourseDetail(@PathVariable String id) {
        TCourse course = courseService.getById(id);
        long count = courseOrderService.count(Wrappers.lambdaQuery(TCourseOrder.class).eq(TCourseOrder::getCourseId, id).in(TCourseOrder::getPayStatus, Arrays.asList(1, 2, 5)));
        course.setSignUpNum((int) count);
        return R.ok(course);
    }
    @PostMapping("/getCourseOrderList")
    @ApiOperation(value = "课程模块--订单列表",response =  TCourseOrder.class)
    public R<Page<TCourseOrder>> getCourseOrderList(@RequestBody CourseOrderListQuery query) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        Page<TCourseOrder> page = new Page<>(query.getPageNum(), query.getPageSize());
        Page<TCourseOrder> courseOrderPage = courseOrderService.page(page, Wrappers.<TCourseOrder>lambdaQuery().eq(TCourseOrder::getAppUserId, userId).eq(Objects.nonNull(query.getPayStatus()),TCourseOrder::getPayStatus, query.getPayStatus()).orderByDesc(TCourseOrder::getCreateTime));
        return R.ok(courseOrderPage);
    }
    @PostMapping("/saveOrder")
    @ApiOperation(value = "课程模块--报名")
    public R<Map<String, Object>> saveOrder(@RequestBody TCourseOrder courseOrder) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        TAppUser appUser = appUserService.getById(userId);
        TCourse course = courseService.getById(courseOrder.getCourseId());
        long count = courseOrderService.count(Wrappers.lambdaQuery(TCourseOrder.class).eq(TCourseOrder::getCourseId, course.getId()).in(TCourseOrder::getPayStatus, Arrays.asList(1, 2, 5)));
        if(count>=course.getPeopleLimit()){
            return R.fail("报名人数已满");
        }
        courseOrder.setAppUserId(userId);
        courseOrder.setPayMoney(course.getCourseCost());
        courseOrder.setPayStatus(1);
        courseOrder.setOrderNumber(OrderNos.getDid());
        boolean save = courseOrderService.save(courseOrder);
        if (save) {
            Map<String, Object> result = wxV3Pay.jsApi(courseOrder.getOrderNumber(), course.getCourseCost().multiply(BigDecimal.valueOf(100)).longValue(), appUser.getOpenId(),"购买课程","");
            return R.ok(result);
        }
        Map<String, ? extends Object> maps =
                new ImmutableMap.Builder<String, String>().
                        put("id", courseOrder.getId())
                        .build();
        QuartzManager.addJob(
                OrderJob.class,
                (OrderJob.name+courseOrder.getId()).toUpperCase(),
                TimeJobType.CANCEL_ORDER,
                DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(15)),
                maps
        );
        return R.fail("报名失败");
    }
    @PostMapping("/payOrder")
    @ApiOperation(value = "课程模块--支付订单")
    public R<Map<String, Object>> payOrder(@RequestBody TCourseOrder courseOrder) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        TAppUser appUser = appUserService.getById(userId);
        TCourseOrder courseOrder1 = courseOrderService.getById(courseOrder.getId());
        if(!userId.equals(courseOrder1.getAppUserId())){
            return R.fail("无权限操作");
        }
        courseOrder1.setOrderNumber(OrderNos.getDid());
        courseOrderService.updateById(courseOrder1);
        Map<String, Object> result = wxV3Pay.jsApi(courseOrder1.getOrderNumber(), courseOrder1.getPayMoney().multiply(BigDecimal.valueOf(100)).longValue(), appUser.getOpenId(),"购买课程","");
        return R.ok(result);
    }
    @PostMapping("/refundOrder")
    @ApiOperation(value = "课程模块--退款")
    public R<?> refundOrder(@RequestBody TCourseOrder courseOrder) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        TCourseOrder courseOrder1 = courseOrderService.getById(courseOrder.getId());
        courseOrder1.setRefundReason(courseOrder.getRefundReason());
        courseOrder1.setRefundReasonPictures(courseOrder.getRefundReasonPictures());
        courseOrder1.setPayStatus(6);
        courseOrder1.setRequestRefundTime(LocalDateTime.now());
        courseOrder1.setRefundMoney(courseOrder1.getPayMoney());
        if(!userId.equals(courseOrder1.getAppUserId())){
            return R.fail("无权限操作");
        }
        if(courseOrder1.getPayStatus()!=2){
            return R.fail("状态不允许退款");
        }
        LocalDateTime payTime = courseOrder1.getPayTime();
        if(payTime==null){
            return R.fail("状态不允许退款");
        }
        if(payTime.plusDays(2).isBefore(LocalDateTime.now())){
            courseOrder1.setBankCardNumber(courseOrder.getBankCardNumber());
            courseOrder1.setCardholderName(courseOrder.getCardholderName());
            courseOrder1.setBankName(courseOrder.getBankName());
            courseOrder1.setRefundStatus(1);
            courseOrderService.updateById(courseOrder1);
        }else {
            WxPaymentRefundModel 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.setNotify_url("");
            refundModel.setFunds_account("AVAILABLE");
            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);
        }
        return R.ok();
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxPayController.java
@@ -1,24 +1,35 @@
package com.ruoyi.web.controller.api;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.CodeGenerateUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.model.TCourseOrder;
import com.ruoyi.system.service.TCourseOrderService;
import com.ruoyi.system.wxPay.enums.RefundEnum;
import com.ruoyi.system.wxPay.enums.TradeStateEnum;
import com.ruoyi.system.wxPay.model.WeixinPayProperties;
import com.ruoyi.system.wxPay.utils.WxTimeUtils;
import com.ruoyi.system.wxPay.utils.WxV3Pay;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
/**
 * 微信相关接口
@@ -36,35 +47,10 @@
    @Autowired
    private WeixinPayProperties weixinPayProperties;
    @Autowired
    private TCourseOrderService courseOrderService;
    /**
     * 按实际修改
     */
    @ApiOperation("订单支付")
    @PostMapping("orderPay")
    public R<Map<String, Object>> orderPay(@RequestParam(value = "inspectionId") String inspectionId) {
//        TSysOtherConfig sysOtherConfig = sysOtherConfigService.getById(1);
//        if (Objects.isNull(sysOtherConfig.getAiPrice())) {
//            return R.fail("系统未配置AI支付价格");
//        }
//
//        String userId = tokenService.getLoginUserApplet().getUserId();
//        TSysAppUser sysAppUser = sysAppUserService.getById(userId);
//        // 价格
//        Long totalPrice = sysOtherConfig.getAiPrice().multiply(new BigDecimal(100)).longValue();
//        // 生成订单号
//        String orderNo = CodeGenerateUtils.generateOrderSn();
//        // 查询用户信息 用户openid
//        String openId = sysAppUser.getOpenId();
//        // 存储支付记录
//        sysPayRecordService.saveData(orderNo, userId, sysOtherConfig.getAiPrice(), 1);
//        // 调用支付方法
//        Map<String, Object> result = wxV3Pay.jsApi(orderNo, totalPrice, openId,"AI检测报告支付","");
//        log.info("支付参数:{}", result);
        return R.ok();
    }
    /**
     * 支付回调
@@ -94,20 +80,16 @@
            String trade_state = jsonInfo.getString("trade_state");
            String success_time = jsonInfo.getString("success_time");
            // 查询订单
//            TSysPayRecord sysPayRecord = sysPayRecordService.getOne(Wrappers.lambdaQuery(TSysPayRecord.class)
//                    .eq(TSysPayRecord::getOrderNo, out_trade_no).last("LIMIT 1"));
            TCourseOrder order = courseOrderService.getOne(Wrappers.lambdaQuery(TCourseOrder.class)
                    .eq(TCourseOrder::getOrderNumber, out_trade_no).last("LIMIT 1"));
            if (trade_state.equals(TradeStateEnum.SUCCESS.name())) {
                log.info("回调成功");
                // 订单号查询订单
//                sysPayRecord.setPayState(2);
//                sysPayRecord.setPayTime(WxTimeUtils.dateToLocalDateTime(WxTimeUtils.toRfc3339Date(success_time)));
//                sysPayRecord.setTransactionId(transaction_id);
//                sysPayRecordService.updateById(sysPayRecord);
//
//                // 处理检测报告为可见
//                TSysInspection sysInspection = sysInspectionService.getById(sysPayRecord.getInspectionId());
//                sysInspection.setIsPay(1);
//                sysInspectionService.updateById(sysInspection);
                order.setPayStatus(2);
                order.setPayTime(WxTimeUtils.dateToLocalDateTime(WxTimeUtils.toRfc3339Date(success_time)));
                order.setTransactionId(transaction_id);
                courseOrderService.updateById(order);
                PrintWriter out = response.getWriter();
                out.print("SUCCESS");
@@ -119,4 +101,54 @@
            wxV3Pay.ack(false, e.getMessage());
        }
    }
    /**
     * 退款回调
     */
//    @PostMapping("refund/notify")
//    @ApiOperation("退款回调")
//    public void refundNotify(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"))
//        }
//    }
    @PostMapping("refund/notify")
    public void refundNotify(HttpServletRequest request) throws IOException {
        try {
            Map<String, Object> params = wxV3Pay.verifyNotify(request, new TypeReference<Map<String, Object>>() {
            });
            // 商户订单号
            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())) {
                TCourseOrder order = courseOrderService.getOne(Wrappers.lambdaQuery(TCourseOrder.class)
                        .eq(TCourseOrder::getRefundNumber, out_refund_no).last("LIMIT 1"));
                order.setPayStatus(3);
                order.setRefundStatus(2);
                order.setRefundTransactionId(transaction_id);
                courseOrderService.updateById(order);
                wxV3Pay.ack();
            }
        }catch (Exception e){
            wxV3Pay.ack(false, "不是成功的退款状态");
        }
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TCourse.java
@@ -66,4 +66,8 @@
    @TableField("status")
    private Integer status;
    @ApiModelProperty(value = "已经报名人数")
    @TableField(exist = false)
    private Integer signUpNum;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TCourseOrder.java
@@ -72,7 +72,7 @@
    @TableField("id_card")
    private String idCard;
    @ApiModelProperty(value = "支付状态 1=待支付 2=已支付 3=已退款")
    @ApiModelProperty(value = "支付状态 1=待支付 2=已支付 3=已退款  4=已取消  5=已完成")
    @TableField("pay_status")
    private Integer payStatus;
ruoyi-system/src/main/java/com/ruoyi/system/query/CourseOrderListQuery.java
New file
@@ -0,0 +1,13 @@
package com.ruoyi.system.query;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("课程订单Query")
public class CourseOrderListQuery extends BasePage {
    @ApiModelProperty("支付状态 1=待支付 2=已支付 3=已退款  4=已取消  5=已完成")
    private Integer payStatus;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/GetCourseListQuery.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.system.query;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("课程查询query")
public class GetCourseListQuery extends BasePage {
    @ApiModelProperty("课程类型 1=社区大学课程 2=暑期青少年工艺培训班")
    private Integer courseType;
    @ApiModelProperty("课程名称")
    private String courseTitle;
}
ruoyi-system/src/main/java/com/ruoyi/system/task/base/AbstractJob.java
@@ -5,10 +5,7 @@
import com.ruoyi.system.model.TDeliberationVote;
import com.ruoyi.system.model.TQuestionnaire;
import com.ruoyi.system.model.TQuestionnaireStem;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.TCommunityActivityService;
import com.ruoyi.system.service.TDeliberationVoteService;
import com.ruoyi.system.service.TQuestionnaireService;
import com.ruoyi.system.service.*;
import com.ruoyi.system.task.utils.SpringContextsUtil;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
@@ -28,11 +25,14 @@
    protected TCommunityActivityService communityActivityService;
    protected TQuestionnaireService questionnaireService;
    protected TCourseOrderService courseOrderService;
    public AbstractJob(){
        this.sysUserService = SpringContextsUtil.getBean(ISysUserService.class);
        this.redisCache = SpringContextsUtil.getBean(RedisCache.class);
        this.deliberationVoteService = SpringContextsUtil.getBean(TDeliberationVoteService.class);
        this.questionnaireService = SpringContextsUtil.getBean(TQuestionnaireService.class);
        this.courseOrderService = SpringContextsUtil.getBean(TCourseOrderService.class);
    }
     
ruoyi-system/src/main/java/com/ruoyi/system/task/base/TimeJobType.java
@@ -5,8 +5,7 @@
 * @date 2025年2月17日 下午7:22:28
 */
public enum TimeJobType {
    DELIBERATION_VOTE_PUSH("deliberation_vote_push","议事投票"),
    ACTIVITY_PUSH("activity_push","社区活动"),
    CANCEL_ORDER("cancel_order","取消订单"),
    QUESTIONNAIRE_PUSH("questionnaire_push","问卷调查");
    private String type;
    private String desc;
ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/OrderJob.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.system.task.jobs;
import com.ruoyi.system.model.TCourseOrder;
import com.ruoyi.system.task.base.AbstractJob;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
 * 订单定时任务
 * @author Administrator
 *
 */
public class OrderJob extends AbstractJob {
    public static final String name = "cancel_order";
    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        JobDataMap maps = context.getMergedJobDataMap();
        String id = maps.getString("id");
        try {
            TCourseOrder tCourseOrder = courseOrderService.getById(id);
            if(tCourseOrder.getPayStatus()==1){
                tCourseOrder.setPayStatus(4);
                courseOrderService.updateById(tCourseOrder);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}