5个文件已修改
164 ■■■■■ 已修改文件
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/DatasourceModel.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/config/DatasourceModel.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
@@ -2,6 +2,7 @@
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.exception.user.StudyLoginException;
import com.ruoyi.common.core.utils.JwtUtils;
@@ -209,7 +210,8 @@
            // 单设备登录校验:账号已在其他设备登录时,将先登录的设备强制下线
            // 注意:此处不能放在 try 中,否则会被 catch(Exception) 吞掉异常
            if (user != null && !Boolean.TRUE.equals(user.getIsCanLogin())) {
                throw new StudyLoginException("当前登录账号在其他设备登录!", 505);
                // 家长端被挤下线统一返回 401,便于前端统一跳转登录页
                throw new StudyLoginException("当前登录账号在其他设备登录!", HttpStatus.UNAUTHORIZED);
            }
        }
        return user;
ruoyi-service/ruoyi-goods/src/main/java/com/ruoyi/goods/config/DatasourceModel.java
@@ -23,9 +23,9 @@
    private Integer initialSize =2;
//    private String url = "jdbc:mysql://127.0.0.1:3306/dolphin_goods?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai";
//    private String url = "jdbc:mysql://192.168.110.111:3306/dolphin_goods?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai";
//    private String username = "root";
//    private String password = "root";
//    private String password = "123456";
//    private String driverClassName = "com.mysql.cj.jdbc.Driver";
//    private Integer maxActive =20;
//    private Long maxWait = 60000L;
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/config/DatasourceModel.java
@@ -21,7 +21,7 @@
    private Long maxWait = 60000L;
    private Integer minIdle =5;
    private Integer initialSize =2;
//    private String url = "jdbc:mysql://127.0.0.1:3306/dolphin_study?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai";
//    private String url = "jdbc:mysql://192.168.110.111:3306/dolphin_study?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai";
//    private String username = "root";
//    private String password = "123456";
//    private String driverClassName = "com.mysql.cj.jdbc.Driver";
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TStudyController.java
@@ -764,7 +764,8 @@
        // 听音选图
        List<TStudyListen> list = studyListenService.list(new QueryWrapper<TStudyListen>()
                .eq("week", week)
                .eq("day", day).orderByAsc("studySort"));
                // studySort 会有重复值,追加 id 升序作为二级排序,保证与提交数组顺序一致
                .eq("day", day).orderByAsc("studySort").orderByAsc("id"));
        List<TSubject> list9 = subjectService.list();
        for (TStudyListen tStudyListen : list) {
@@ -793,7 +794,7 @@
        // 看音选图
        List<TStudyLook> list1 = studyLookService.list(new QueryWrapper<TStudyLook>()
                .eq("week", week)
                .eq("day", day).orderByAsc("studySort")
                .eq("day", day).orderByAsc("studySort").orderByAsc("id")
        );
        for (TStudyLook tStudyLook : list1) {
            int index = 0;
@@ -828,7 +829,7 @@
        // 归纳排除
        List<TStudyInduction> list2 = studyInductionService.list(new QueryWrapper<TStudyInduction>()
                .eq("week", week)
                .eq("day", day).orderByAsc("studySort")
                .eq("day", day).orderByAsc("studySort").orderByAsc("id")
        );
        for (TStudyInduction tStudyInduction : list2) {
            StringBuilder names = new StringBuilder();
@@ -856,7 +857,7 @@
        // 有问有答
        List<TStudyAnswer> list3 = studyAnswerService.list(new QueryWrapper<TStudyAnswer>()
                .eq("week", week)
                .eq("day", day).orderByAsc("studySort"));
                .eq("day", day).orderByAsc("studySort").orderByAsc("id"));
        for (TStudyAnswer tStudyAnswer : list3) {
            StringBuilder names = new StringBuilder();
            StudyAnswerVO studyAnswerVO = new StudyAnswerVO();
@@ -909,7 +910,7 @@
        // 音图相配
        List<TStudyPair> list4 = studyPairService.list(new QueryWrapper<TStudyPair>()
                .eq("week", week)
                .eq("day", day).orderByAsc("studySort")
                .eq("day", day).orderByAsc("studySort").orderByAsc("id")
        );
        for (TStudyPair tStudyPair : list4) {
            StringBuilder names = new StringBuilder();
ruoyi-service/ruoyi-study/src/main/java/com/ruoyi/study/controller/TUserController.java
@@ -223,8 +223,27 @@
                                tVipOrder.getMoney().toString(),
                                "/base/user/aliPayBuyVip");
            case 3:
                // 苹果内购:回传流水号后立即开通会员;苹果服务器通知(/testApple)作为兜底,两条链路共用 completeVipOrder
                if (tVipOrder == null) {
                    return AjaxResult.error("订单不存在");
                }
                // 已经支付或已退款,避免重复回传覆盖状态
                if (tVipOrder.getPayState() != null && tVipOrder.getPayState() != 1) {
                    System.err.println("苹果支付订单无需重复处理 orderId=" + tVipOrder.getId()
                            + ", payState=" + tVipOrder.getPayState());
                    return AjaxResult.success();
                }
                if (!StringUtils.hasText(dto.getTransactionIdentifier())) {
                    return AjaxResult.error("苹果流水号不能为空");
                }
                System.err.println("苹果支付回传流水号 orderId=" + tVipOrder.getId()
                        + ", transactionIdentifier=" + dto.getTransactionIdentifier());
                tVipOrder.setTransactionId(dto.getTransactionIdentifier());
                // 先落库流水号,保证后续苹果通知(/testApple)能按 transactionId 匹配到本订单
                vipOrderService.updateById(tVipOrder);
                // 不依赖苹果通知的到达顺序:回传流水号后立即开通会员
                // 苹果通知(/testApple)作为兜底,重复调用时由 completeVipOrder 的幂等保护避免重复开通
                completeVipOrder(tVipOrder);
        }
        return AjaxResult.success();
    }
@@ -376,10 +395,22 @@
            System.err.println("data"+data);
            // 苹果流水号
            String string = transactionInfo.getString("originalTransactionId");
            // 订单匹配:优先按本次交易的 transactionId 匹配(客户端支付成功后已回传 /pay 落库),
            // 匹配不到再退回 originalTransactionId(同一订阅的自动续订没有新订单,需挂到首单上)
            TVipOrder one = vipOrderService.getOne(new QueryWrapper<TVipOrder>()
                    .eq("transactionId", string)
                    .eq("payType", 3));
            System.err.println("回调通知类型"+notificationType);
                    .eq("payType", 3)
                    .eq("transactionId", transactionId)
                    .last("limit 1"));
            if (one == null) {
                one = vipOrderService.getOne(new QueryWrapper<TVipOrder>()
                        .eq("payType", 3)
                        .eq("transactionId", string)
                        .last("limit 1"));
            }
            System.err.println("回调通知类型"+notificationType
                    + ", transactionId=" + transactionId
                    + ", originalTransactionId=" + string
                    + ", 匹配订单=" + (one == null ? "无" : one.getId()));
            if ("REFUND".equals(notificationType)){
                if (one!=null && one.getPayState() == 2){
                    one.setPayState(3);
@@ -413,57 +444,16 @@
                        byId1.setVipPayTime(tVipOrder.getPayTime());
                        userService.updateById(byId1);
                    }
                    // 退款成功,更新订单状态
                    vipOrderService.updateById(one);
                }
                vipOrderService.updateById(one);
            }else{
                if (one!=null){
                    one.setPayState(2);
                    one.setPayTime(new Date());
                    TUser byId1 = userService.getById(one.getUserId());
                    if (byId1.getVipPayTime() == null) {
                        // 是否是首次充值会员
                        byId1.setVipPayTime(new Date());
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(new Date());
                        calendar.add(Calendar.MONTH, one.getCount());
                        Date dateAfterOneMonth = calendar.getTime();
                        dateAfterOneMonth.setHours(23);
                        dateAfterOneMonth.setMinutes(59);
                        dateAfterOneMonth.setSeconds(59);
                        byId1.setVipEndTime(dateAfterOneMonth);
                        one.setTime(dateAfterOneMonth);
                        userService.updateById(byId1);
                    } else {
                        // 不是首次 判断vipEndTime 是否到期 如果没有 加指定月份时间 如果到期了 将会员到期时间从当前增加指定月份
                        if (byId1.getVipEndTime().getTime() < new Date().getTime()) {
                            Calendar calendar = Calendar.getInstance();
                            calendar.setTime(new Date());
                            calendar.add(Calendar.MONTH, one.getCount());
                            Date dateAfterOneMonth = calendar.getTime();
                            dateAfterOneMonth.setHours(23);
                            dateAfterOneMonth.setMinutes(59);
                            dateAfterOneMonth.setSeconds(59);
                            one.setTime(dateAfterOneMonth);
                            byId1.setVipEndTime(dateAfterOneMonth);
                            userService.updateById(byId1);
                        } else {
                            Calendar calendar = Calendar.getInstance();
                            calendar.setTime(byId1.getVipEndTime());
                            calendar.add(Calendar.MONTH, one.getCount());
                            Date dateAfterOneMonth = calendar.getTime();
                            dateAfterOneMonth.setHours(23);
                            dateAfterOneMonth.setMinutes(59);
                            dateAfterOneMonth.setSeconds(59);
                            one.setTime(dateAfterOneMonth);
                            byId1.setVipEndTime(dateAfterOneMonth);
                            userService.updateById(byId1);
                        }
                    }
                    // 与客户端回传流水号(/pay)共用同一段开通逻辑,保证两条链路行为一致
                    completeVipOrder(one);
                }
            }
            vipOrderService.updateById(one);
            System.err.println("苹果流水号"+string);
            PrintWriter out = response.getWriter();
            out.write("success");
@@ -472,6 +462,47 @@
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 苹果内购:将订单置为已支付,并开通/顺延会员
     * 客户端回传流水号(/pay)与苹果服务器通知(/testApple)共用此方法,保证两条链路行为一致
     *
     * @param one 待支付的会员订单(非空)
     */
    private void completeVipOrder(TVipOrder one) {
        // 幂等保护:已支付(2)或已退款(3)的订单直接返回,避免 /pay 与苹果通知两条链路重复顺延会员
        if (one.getPayState() != null && one.getPayState() != 1) {
            System.err.println("苹果订单已处理,跳过开通 orderId=" + one.getId()
                    + ", payState=" + one.getPayState());
            return;
        }
        one.setPayState(2);
        one.setPayTime(new Date());
        TUser user = userService.getById(one.getUserId());
        Calendar calendar = Calendar.getInstance();
        if (user.getVipPayTime() == null) {
            // 首次充值会员:记录首次充值时间,会员到期时间从当前时间起算
            user.setVipPayTime(new Date());
            calendar.setTime(new Date());
        } else if (user.getVipEndTime().getTime() < System.currentTimeMillis()) {
            // 非首次且会员已到期:从当前时间重新起算
            calendar.setTime(new Date());
        } else {
            // 非首次且会员未到期:在原到期时间基础上顺延
            calendar.setTime(user.getVipEndTime());
        }
        calendar.add(Calendar.MONTH, one.getCount());
        Date vipEndTime = calendar.getTime();
        vipEndTime.setHours(23);
        vipEndTime.setMinutes(59);
        vipEndTime.setSeconds(59);
        one.setTime(vipEndTime);
        user.setVipEndTime(vipEndTime);
        userService.updateById(user);
        // 支付成功,订单状态置为已支付
        vipOrderService.updateById(one);
    }
    /**
@@ -506,9 +537,18 @@
            System.err.println("data"+data);
            // 苹果流水号
            String string = transactionInfo.getString("originalTransactionId");
            String transactionId = transactionInfo.getString("transactionId");
            // 订单匹配:优先按本次交易的 transactionId,匹配不到再退回 originalTransactionId
            TVipOrder one = vipOrderService.getOne(new QueryWrapper<TVipOrder>()
                    .eq("transactionId", string)
                    .eq("payType", 3));
                    .eq("payType", 3)
                    .eq("transactionId", transactionId)
                    .last("limit 1"));
            if (one == null) {
                one = vipOrderService.getOne(new QueryWrapper<TVipOrder>()
                        .eq("payType", 3)
                        .eq("transactionId", string)
                        .last("limit 1"));
            }
            if (one!=null && one.getPayState() == 2){
                one.setPayState(3);
                one.setBackTime(new Date());
@@ -540,8 +580,9 @@
                    byId1.setVipPayTime(tVipOrder.getPayTime());
                    userService.updateById(byId1);
                }
                // 退款成功,更新订单状态
                vipOrderService.updateById(one);
            }
            vipOrderService.updateById(one);
            System.err.println("苹果流水号"+string);
            PrintWriter out = response.getWriter();
            out.write("success");