diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/client/ClientOrderInfoController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/client/ClientOrderInfoController.java index 15e943d..a37d1b2 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/client/ClientOrderInfoController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/client/ClientOrderInfoController.java @@ -73,18 +73,18 @@ public class ClientOrderInfoController extends BaseController } } - /** - * 确认出货 - */ - @Log(title = "确认出货", businessType = BusinessType.UPDATE) - @PostMapping("/confirmShipment") - public AjaxResult confirmShipment(@RequestParam("orderId") String orderId) { - try { - return orderInfoService.confirmShipment(orderId); - } catch (Exception e) { - return AjaxResult.error("确认出货失败: " + e.getMessage()); - } - } + // /** + // * 确认出货 + // */ + // @Log(title = "确认出货", businessType = BusinessType.UPDATE) + // @PostMapping("/confirmShipment") + // public AjaxResult confirmShipment(@RequestParam("orderId") String orderId) { + // try { + // return orderInfoService.confirmShipment(orderId); + // } catch (Exception e) { + // return AjaxResult.error("确认出货失败: " + e.getMessage()); + // } + // } /** * 申请退款 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java index c92759b..36159ab 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -1,18 +1,24 @@ package com.ruoyi.common.utils; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; + import org.springframework.util.AntPathMatcher; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.text.StrFormatter; /** * 字符串工具类 - * + * * @author ruoyi */ public class StringUtils extends org.apache.commons.lang3.StringUtils @@ -28,7 +34,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 获取参数不为空值 - * + * * @param value defaultValue 要判断的value * @return value 返回值 */ @@ -39,7 +45,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Collection是否为空, 包含List,Set,Queue - * + * * @param coll 要判断的Collection * @return true:为空 false:非空 */ @@ -50,7 +56,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Collection是否非空,包含List,Set,Queue - * + * * @param coll 要判断的Collection * @return true:非空 false:空 */ @@ -61,7 +67,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象数组是否为空 - * + * * @param objects 要判断的对象数组 ** @return true:为空 false:非空 */ @@ -72,7 +78,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象数组是否非空 - * + * * @param objects 要判断的对象数组 * @return true:非空 false:空 */ @@ -83,7 +89,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Map是否为空 - * + * * @param map 要判断的Map * @return true:为空 false:非空 */ @@ -94,7 +100,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Map是否为空 - * + * * @param map 要判断的Map * @return true:非空 false:空 */ @@ -105,7 +111,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个字符串是否为空串 - * + * * @param str String * @return true:为空 false:非空 */ @@ -116,7 +122,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个字符串是否为非空串 - * + * * @param str String * @return true:非空串 false:空串 */ @@ -127,7 +133,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象是否为空 - * + * * @param object Object * @return true:为空 false:非空 */ @@ -138,7 +144,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象是否非空 - * + * * @param object Object * @return true:非空 false:空 */ @@ -149,7 +155,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象是否是数组类型(Java基本型别的数组) - * + * * @param object 对象 * @return true:是数组 false:不是数组 */ @@ -211,7 +217,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 截取字符串 - * + * * @param str 字符串 * @param start 开始 * @return 结果 @@ -242,7 +248,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 截取字符串 - * + * * @param str 字符串 * @param start 开始 * @param end 结束 @@ -288,7 +294,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串 - * + * * @param str 要截取的字符串 * @param open 起始字符串 * @param close 结束字符串 @@ -314,7 +320,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 判断是否为空,并且不是空白字符 - * + * * @param str 要判断的value * @return 结果 */ @@ -344,7 +350,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
- * + * * @param template 文本模板,被替换的部分用 {} 表示 * @param params 参数值 * @return 格式化后的文本 @@ -360,7 +366,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 是否为http(s)://开头 - * + * * @param link 链接 * @return 结果 */ @@ -371,7 +377,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 字符串转set - * + * * @param str 字符串 * @param sep 分隔符 * @return set集合 @@ -383,7 +389,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 字符串转list - * + * * @param str 字符串 * @param sep 分隔符 * @return list集合 @@ -395,7 +401,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 字符串转list - * + * * @param str 字符串 * @param sep 分隔符 * @param filterBlank 过滤纯空白 @@ -532,7 +538,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 是否包含字符串 - * + * * @param str 验证字符串 * @param strs 字符串组 * @return 包含返回true @@ -554,7 +560,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld - * + * * @param name 转换前的下划线大写方式命名的字符串 * @return 转换后的驼峰式命名的字符串 */ @@ -628,7 +634,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 - * + * * @param str 指定字符串 * @param strs 需要检查的字符串数组 * @return 是否匹配 @@ -650,11 +656,11 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils } /** - * 判断url是否与规则配置: - * ? 表示单个字符; - * * 表示一层路径内的任意字符串,不可跨层级; + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; * ** 表示任意层路径; - * + * * @param pattern 匹配规则 * @param url 需要匹配的url * @return @@ -673,7 +679,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 - * + * * @param num 数字对象 * @param size 字符串指定长度 * @return 返回数字的字符串格式,该字符串为指定长度。 @@ -685,7 +691,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 - * + * * @param s 原始字符串 * @param size 字符串指定长度 * @param c 用于补齐的字符 @@ -719,4 +725,42 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils } return sb.toString(); } -} \ No newline at end of file + + public static String convertToBeijingTime(String gmtTime) { + try { + // 移除时区标识 + String timeStr = gmtTime.replace(" Etc/GMT", ""); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + // 解析为本地时间 + LocalDateTime localDateTime = LocalDateTime.parse(timeStr, formatter); + + // GMT时间加8小时就是北京时间 + LocalDateTime beijingTime = localDateTime.plusHours(8); + + return beijingTime.format(formatter); + + } catch (Exception e) { + throw new RuntimeException("时间转换失败: " + e.getMessage(), e); + } + } + + /** + * 将GMT时间字符串转换为时间戳(毫秒) + */ + public static long convertToTimestampMillis(String gmtTime) { + try { + String timeStr = gmtTime.replace(" Etc/GMT", ""); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + LocalDateTime localDateTime = LocalDateTime.parse(timeStr, formatter); + ZonedDateTime gmtDateTime = localDateTime.atZone(ZoneId.of("GMT")); + + return gmtDateTime.toInstant().toEpochMilli(); + + } catch (Exception e) { + throw new RuntimeException("时间戳转换失败: " + e.getMessage(), e); + } + } + +} diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 077672c..f855995 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -30,6 +30,12 @@ provided + + com.github.wechatpay-apiv3 + wechatpay-java + 0.2.17 + + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/util/AppleyPay.java b/ruoyi-system/src/main/java/com/ruoyi/system/util/AppleyPay.java index c3620ec..7b8d73c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/util/AppleyPay.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/util/AppleyPay.java @@ -82,8 +82,8 @@ public class AppleyPay { * @param receipt 苹果传递前端给的值 * @param chooseEnv 是否时测试环境 */ - public ShopUser setIapCertificate(Long userId, String receipt, OrderInfo orderInfo,boolean chooseEnv) { - log.info("IOS端发送的购买凭证。数据有 userId = {},receipt = {},chooseEnv = {}",userId,receipt,chooseEnv); + public ShopUser setIapCertificate(Long userId, String receipt, OrderInfo orderInfo, boolean chooseEnv) { + log.info("IOS端发送的购买凭证。数据有 userId = {},receipt = {},chooseEnv = {}", userId, receipt, chooseEnv); if (userId == null || StringUtils.isEmpty(receipt)) { log.error("用户ID 或者 receipt为空"); return null; @@ -92,7 +92,7 @@ public class AppleyPay { url = chooseEnv == true ? certificateUrl : certificateUrlTest; final String certificateCode = receipt; if (StringUtils.isNotEmpty(certificateCode)) { - return sendHttpsCoon(url, certificateCode, userId,orderInfo); + return sendHttpsCoon(url, certificateCode, userId, orderInfo); } else { log.error("用户ID 或者 receipt为空"); return null; @@ -101,15 +101,15 @@ public class AppleyPay { /** * 发送请求 向苹果发起验证支付请求是否有效:本方法有认证方法进行调用 - * + *

* 支持的订阅产品ID: * - com.mingyue.product.month (月付) * - com.mingyue.product.quarterly (季付) * - com.mingyue.product.semiAnnual (半年付) * - com.mingyue.product.annual (年付) * - * @param url 支付的环境校验地址 - * @param code 接口传递的 receipt + * @param url 支付的环境校验地址 + * @param code 接口传递的 receipt * @param userId 用户ID * @return 结果 */ @@ -177,14 +177,14 @@ public class AppleyPay { // 处理 latest_receipt_info(可能是数组或单个对象) InAppTransaction latestTransaction = null; if (latestReceiptInfoObj instanceof JSONArray) { - // 如果是数组,取最后一个(最新的交易) + // 如果是数组,取第一个(最新的交易) JSONArray receiptArray = (JSONArray) latestReceiptInfoObj; if (receiptArray.isEmpty()) { log.error("latest_receipt_info 数组为空"); return null; } // 获取最新的交易记录(数组最后一个元素) - Object lastReceipt = receiptArray.get(receiptArray.size() - 1); + Object lastReceipt = receiptArray.get(0); latestTransaction = JSONUtil.toBean(lastReceipt.toString(), InAppTransaction.class); } else { // 如果是单个对象 @@ -200,11 +200,12 @@ public class AppleyPay { String transactionId = latestTransaction.getTransactionId(); String productId = latestTransaction.getProductId(); // 获取过期时间(北京时间) - Long expiresDateMsInBeijing = latestTransaction.getExpiresDateMsInBeijing(); + String expiresData = latestTransaction.getExpiresDate(); + //long expiresDateMsInBeijing = StringUtils.convertToTimestampMillis(expiresData); // 查询交易信息是否已经存在 OrderInfo selectOrderInfo = orderInfoMapper.selectByTradeNo(transactionId); - if (selectOrderInfo != null){ + if (selectOrderInfo != null) { log.warn("交易ID {} 已存在,可能重复处理。用户ID: {}", transactionId, userId); // 交易信息已存在 return null; @@ -230,15 +231,14 @@ public class AppleyPay { } } - // 验证订阅是否过期(对于订阅类型,使用北京时间) - if (expiresDateMsInBeijing != null) { - long currentTimestamp = System.currentTimeMillis(); - if (expiresDateMsInBeijing < currentTimestamp) { - log.warn("订阅已过期。交易ID: {}, 过期时间(北京时间): {}, 当前时间: {}", - transactionId, new Date(expiresDateMsInBeijing), new Date(currentTimestamp)); - return null; - } - } + // 验证订阅是否过期(对于订阅类型,使用北京时间)TODO:// 待修改 + + long currentTimestamp = System.currentTimeMillis(); + // if (expiresDateMsInBeijing < currentTimestamp) { + // log.warn("订阅已过期。交易ID: {}, 过期时间(北京时间): {}, 当前时间: {}", + // transactionId, new Date(expiresDateMsInBeijing), new Date(currentTimestamp)); + // return null; + // } // 根据产品ID处理不同的订阅类型 String packageType = null; @@ -251,7 +251,7 @@ public class AppleyPay { // 创建或更新订单记录 OrderInfo orderInfo = null; boolean isNewOrder = false; // 标记是否为新订单 - + // 如果 orderParam 为 null 或 orderId 为空,需要创建新订单 if (orderParam == null || orderParam.getOrderId() == null) { // 创建新订单 @@ -286,7 +286,7 @@ public class AppleyPay { orderInfo.setPayTime(DateUtils.getNowDate()); orderInfo.setStartTime(startTime); orderInfo.setTradeNo(transactionId); - orderInfo.setCallbackContent(transactionId); // 保存交易ID + orderInfo.setCallbackContent(latestTransaction.getOriginalTransactionId()); // 保存交易ID orderInfo.setCallTime(DateUtils.getNowDate()); orderInfo.setUpdateTime(DateUtils.getNowDate()); @@ -301,7 +301,7 @@ public class AppleyPay { Date endTime = calculateEndTime(startTime, packageType); orderInfo.setEndTime(endTime); orderInfo.setPackageType(packageType); - } else if ("com.mingyue.product.quarterly".equals(productId)) { + } else if ("com.mingyue.item.quarterly".equals(productId)) { // 季付 packageType = "2"; orderName = "VIP会员包季度"; @@ -312,7 +312,7 @@ public class AppleyPay { orderInfo.setOrderName(orderName); orderInfo.setEndTime(endTime); orderInfo.setPackageType(packageType); - } else if ("com.mingyue.product.semiAnnual".equals(productId)) { + } else if ("com.mingyue.item.semiAnnual".equals(productId)) { // 半年付 packageType = "3"; orderName = "VIP会员包半年"; @@ -323,7 +323,7 @@ public class AppleyPay { orderInfo.setOrderName(orderName); orderInfo.setEndTime(endTime); orderInfo.setPackageType(packageType); - } else if ("com.mingyue.product.annual".equals(productId)) { + } else if ("com.mingyue.item.annual".equals(productId)) { // 年付 packageType = "4"; orderName = "VIP会员包年"; @@ -338,14 +338,14 @@ public class AppleyPay { log.error("用户ID:{}, 未知的产品ID: {}", userId, productId); return null; } - + // 根据是否为新订单选择插入或更新 if (isNewOrder) { orderInfoService.insertOrderInfo(orderInfo); } else { orderInfoService.updateOrderInfo(orderInfo); } - + // 更新用户VIP时间 return shopUserService.updateShopUserVipTime(userId, packageType); } catch (Exception e) { @@ -357,7 +357,7 @@ public class AppleyPay { /** * 根据套餐类型计算结束时间 * - * @param startTime 开始时间 + * @param startTime 开始时间 * @param packageType 套餐类型:1-月付, 3-季付, 6-半年付, 12-年付 * @return 结束时间 */ @@ -423,7 +423,6 @@ public class AppleyPay { } } - /** * 注意:下面代码跟苹果支付业务无关。 * 这里的code 是前端请求苹果,苹果给前端的一个密钥(如果我们通过base64解密后,可获得signature、purchase-info、environment、pod、signing-status)这个密钥用于告诉Java服务器 想苹果服务器校验订单是否成功的参数 @@ -474,8 +473,12 @@ public class AppleyPay { // 添加padding switch (base64.length() % 4) { - case 2: base64 += "=="; break; - case 3: base64 += "="; break; + case 2: + base64 += "=="; + break; + case 3: + base64 += "="; + break; } byte[] decodedBytes = Base64.decode(base64); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/util/InAppTransaction.java b/ruoyi-system/src/main/java/com/ruoyi/system/util/InAppTransaction.java index e3f2e92..026e023 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/util/InAppTransaction.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/util/InAppTransaction.java @@ -1,5 +1,6 @@ package com.ruoyi.system.util; +import com.ruoyi.common.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -129,6 +130,11 @@ public class InAppTransaction { } } + public static void main(String[] args) { + String expiresDateMs = "2025-11-06 13:54:04 Etc/GMT"; + System.out.println(StringUtils.convertToTimestampMillis(expiresDateMs)); + } + /** * 获取购买日期时间戳(北京时间,毫秒) * 将太平洋时间转换为北京时间 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/util/WeChatAppServiceExtensionExample.java b/ruoyi-system/src/main/java/com/ruoyi/system/util/WeChatAppServiceExtensionExample.java new file mode 100644 index 0000000..bc3b12b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/util/WeChatAppServiceExtensionExample.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.util; + +import com.wechat.pay.java.core.Config; +import com.wechat.pay.java.core.RSAAutoCertificateConfig; +import com.wechat.pay.java.core.exception.HttpException; +import com.wechat.pay.java.core.exception.MalformedMessageException; +import com.wechat.pay.java.core.exception.ServiceException; +import com.wechat.pay.java.service.payments.app.AppServiceExtension; +import com.wechat.pay.java.service.payments.app.model.CloseOrderRequest; +import com.wechat.pay.java.service.payments.app.model.PrepayRequest; +import com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse; +import com.wechat.pay.java.service.payments.app.model.QueryOrderByIdRequest; +import com.wechat.pay.java.service.payments.app.model.QueryOrderByOutTradeNoRequest; +import com.wechat.pay.java.service.payments.model.Transaction; + +public class WeChatAppServiceExtensionExample { + /** + * 商户号 + */ + public static String merchantId = "190000****"; + + /** + * 商户API私钥路径 + */ + public static String privateKeyPath = "/Users/yourname/your/path/apiclient_key.pem"; + + /** + * 商户证书序列号 + */ + public static String merchantSerialNumber = "5157F09EFDC096DE15EBE81A47057A72********"; + + /** + * 商户APIV3密钥 + */ + public static String apiV3Key = "..."; + + public static AppServiceExtension service; + + public static void main(String[] args) { + // 初始化商户配置 + Config config = + new RSAAutoCertificateConfig.Builder() + .merchantId(merchantId) + // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 + .privateKeyFromPath(privateKeyPath) + .merchantSerialNumber(merchantSerialNumber) + .apiV3Key(apiV3Key) + .build(); + // 初始化服务 + service = new AppServiceExtension.Builder().config(config).build(); + try { + // ... 调用接口 + PrepayWithRequestPaymentResponse response = prepayWithRequestPayment(); + System.out.println(response); + } catch (HttpException e) { // 发送HTTP请求失败 + // 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义 + } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500 + // 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义 + } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败 + // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义 + } + } + + /** + * 关闭订单 + */ + public static void closeOrder() { + + CloseOrderRequest request = new CloseOrderRequest(); + // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义 + // 调用接口 + service.closeOrder(request); + } + + /** + * APP支付下单,并返回APP调起支付数据 + */ + public static PrepayWithRequestPaymentResponse prepayWithRequestPayment() { + PrepayRequest request = new PrepayRequest(); + // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义 + // 调用接口 + return service.prepayWithRequestPayment(request); + } + + /** + * 微信支付订单号查询订单 + */ + public static Transaction queryOrderById() { + + QueryOrderByIdRequest request = new QueryOrderByIdRequest(); + // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义 + // 调用接口 + return service.queryOrderById(request); + } + + /** + * 商户订单号查询订单 + */ + public static Transaction queryOrderByOutTradeNo() { + + QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest(); + // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义 + // 调用接口 + return service.queryOrderByOutTradeNo(request); + } +} diff --git a/ruoyi-system/src/main/resources/mapper/system/ShopUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/ShopUserMapper.xml index 3731d9b..2c0c4b8 100644 --- a/ruoyi-system/src/main/resources/mapper/system/ShopUserMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/ShopUserMapper.xml @@ -69,6 +69,8 @@ + +