From 16182134942cd99a94d56355b62b8f9e9261e7a0 Mon Sep 17 00:00:00 2001 From: huppygo Date: Sat, 11 Oct 2025 01:44:54 +0000 Subject: [PATCH 01/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9iot=E4=B8=ADxxljob?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E5=90=8D=E7=A7=B0=E9=87=8D=E5=A4=8D=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E6=9C=8D=E5=8A=A1=E5=90=AF=E5=8A=A8=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: huppygo --- .../cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java index f632aa0ed..a3c4510b2 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java +++ b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java @@ -36,7 +36,7 @@ public class IotOtaUpgradeJob { @Resource private IotDeviceService deviceService; - @XxlJob("deviceOfflineCheckJob") + @XxlJob("deviceUpgradeJob") @TenantJob // 多租户 public String execute(String param) throws Exception { // 1. 查询待推送的 OTA 升级记录 From 534b7e1f7374e4cf868860d0c6243a9ad4cab7bc Mon Sep 17 00:00:00 2001 From: wuKong Date: Mon, 10 Nov 2025 11:36:47 +0800 Subject: [PATCH 02/14] =?UTF-8?q?feat(pay,mall-trade):=20=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=95=86=E5=93=81=E8=AE=A2=E5=8D=95=E8=AF=A6?= =?UTF-8?q?=E6=83=85path=E9=85=8D=E7=BD=AE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 PayOrderApi 中新增 getMerchantOrderIdByPayOrderNo 方法 - 在 PayOrderApiImpl 中实现该方法,调用 service 层逻辑 - 在 PayOrderMapper 中增加 selectByNo 查询方法 - 在 PayOrderService 中定义 getMerchantOrderIdByNo 接口 - 在 PayOrderServiceImpl 中实现 getMerchantOrderIdByNo 业务逻辑 - 支持通过支付订单编号查询对应的商户订单编号feat(trade): 新增通过支付订单编号查询交易订单详情功能 - 在 AppTradeOrderController 中新增 getOrderDetailByOutTradeNo 接口 - 支持微信小程序订单中心跳转到订单详情页面 - 在 TradeOrderQueryService 中定义 getOrderByOutTradeNo 方法 - 在 TradeOrderQueryServiceImpl 中实现该方法 - 通过 PayOrderApi 调用获取商户订单编号 - 根据商户订单编号查询对应的交易订单信息 --- .../app/order/AppTradeOrderController.java | 36 ++++++++++++++++++- .../service/order/TradeOrderQueryService.java | 10 +++++- .../order/TradeOrderQueryServiceImpl.java | 18 ++++++++++ .../module/pay/api/order/PayOrderApi.java | 4 +++ .../module/pay/api/order/PayOrderApiImpl.java | 4 +++ .../pay/dal/mysql/order/PayOrderMapper.java | 4 +++ .../pay/service/order/PayOrderService.java | 8 ++++- .../service/order/PayOrderServiceImpl.java | 11 ++++++ 8 files changed, 92 insertions(+), 3 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index b2b887050..6f72e6507 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -105,7 +105,7 @@ public class AppTradeOrderController { && TradeOrderStatusEnum.isUnpaid(order.getStatus()) && !order.getPayStatus()) { tradeOrderUpdateService.syncOrderPayStatusQuietly(order.getId(), order.getPayOrderId()); // 重新查询,因为同步后,可能会有变化 - order = tradeOrderQueryService.getOrder(id); + order = tradeOrderQueryService.getOrder(order.getId()); } // 2.1 查询订单项 @@ -117,6 +117,40 @@ public class AppTradeOrderController { return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express)); } + @GetMapping("/get-detailByOutTradeNo") + @Operation(summary = "获得交易订单") + @Parameters({ + @Parameter(name = "id", description = "PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容"), + @Parameter(name = "sync", description = "是否同步支付状态", example = "true") + }) + public CommonResult getOrderDetailByOutTradeNo(@RequestParam("id") String id, + @RequestParam(value = "sync", required = false) Boolean sync) { + // PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容,如「index/orderDetail?id=${商品订单号}&channel=1」。PATH最多输入1条。 + // https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html + // 小程序商品订单详情path配置为:pages/order/detail?id=${商品订单号}&comein_type=wechat + // 通过 rpc payOrderNo -> tradeOrderId + // 1.1 查询订单 + TradeOrderDO order = tradeOrderQueryService.getOrderByOutTradeNo(getLoginUserId(), id); + if (order == null) { + return success(null); + } + // 1.2 sync 仅在等待支付 + if (Boolean.TRUE.equals(sync) + && TradeOrderStatusEnum.isUnpaid(order.getStatus()) && !order.getPayStatus()) { + tradeOrderUpdateService.syncOrderPayStatusQuietly(order.getId(), order.getPayOrderId()); + // 重新查询,因为同步后,可能会有变化 + order = tradeOrderQueryService.getOrder(order.getId()); + } + + // 2.1 查询订单项 + List orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId()); + // 2.2 查询物流公司 + DeliveryExpressDO express = order.getLogisticsId() != null && order.getLogisticsId() > 0 ? + deliveryExpressService.getDeliveryExpress(order.getLogisticsId()) : null; + // 2.3 最终组合 + return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express)); + } + @GetMapping("/get-express-track-list") @Operation(summary = "获得交易订单的物流轨迹") @Parameter(name = "id", description = "交易订单编号") diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java index d2509643d..975f79394 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java @@ -39,7 +39,15 @@ public interface TradeOrderQueryService { * @return 交易订单 */ TradeOrderDO getOrder(Long userId, Long id); - + /** + * 获得指定用户,指定的交易订单 + * PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容,如「index/orderDetail?id=${商品订单号}&channel=1」。PATH最多输入1条。 + * https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html + * @param userId 用户编号 + * @param outTradeNo 支付订单no + * @return 交易订单 + */ + TradeOrderDO getOrderByOutTradeNo(Long userId, String outTradeNo); /** * 获得指定用户,指定活动,指定状态的交易订单 * diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java index ad530f842..1470f17e8 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java @@ -1,13 +1,16 @@ package cn.iocoder.yudao.module.trade.service.order; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.member.api.user.MemberUserApi; import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; @@ -57,6 +60,8 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @Resource private MemberUserApi memberUserApi; + @Resource + private PayOrderApi payOrderApi; // =================== Order =================== @Override @@ -73,6 +78,19 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { } return order; } + @Override + public TradeOrderDO getOrderByOutTradeNo(Long userId, String outTradeNo) { + // PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容,如「index/orderDetail?id=${商品订单号}&channel=1」。PATH最多输入1条。 + // https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html + // 小程序商品订单详情path配置为:pages/order/detail?id=${商品订单号}&comein_type=wechat + // 通过 rpc payOrderNo -> tradeOrderId + String id = outTradeNo; + String merchantOrderId = payOrderApi.getMerchantOrderIdByPayOrderNo(outTradeNo).getCheckedData(); + if(CharSequenceUtil.isNotEmpty(merchantOrderId)){ + id = merchantOrderId; + } + return getSelf().getOrder(userId, Convert.toLong(id)); + } @Override public TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status) { diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java index c0d818d3b..07ac82489 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java @@ -39,4 +39,8 @@ public interface PayOrderApi { CommonResult updatePayOrderPrice(@RequestParam("id") Long id, @RequestParam("payPrice") Integer payPrice); + @PostMapping(PREFIX + "/getMerchantOrderIdByNo") + @Operation(summary = "根据支付订单编号获取商户订单编号") + @Parameter(name = "no", description = "支付单编号", example = "Pxxxx", required = true) + CommonResult getMerchantOrderIdByPayOrderNo(@RequestParam("no") String no); } diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java index 1ca3414db..d83971519 100644 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java @@ -37,4 +37,8 @@ public class PayOrderApiImpl implements PayOrderApi { return success(true); } + @Override + public CommonResult getMerchantOrderIdByPayOrderNo(String no) { + return success(payOrderService.getMerchantOrderIdByNo(no)); + } } diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java index 95510d5f7..e83d0387c 100755 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java @@ -48,6 +48,10 @@ public interface PayOrderMapper extends BaseMapperX { PayOrderDO::getMerchantOrderId, merchantOrderId); } + default PayOrderDO selectByNo(String no) { + return selectOne(PayOrderDO::getNo, no); + } + default int updateByIdAndStatus(Long id, Integer status, PayOrderDO update) { return update(update, new LambdaQueryWrapper() .eq(PayOrderDO::getId, id).eq(PayOrderDO::getStatus, status)); diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java index c4496f829..7853b4417 100755 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java @@ -40,6 +40,13 @@ public interface PayOrderService { */ PayOrderDO getOrder(Long appId, String merchantOrderId); + /** + * 获得商户订单编号 + * + * @param no 编号 + * @return merchantOrderId + */ + String getMerchantOrderIdByNo(String no); /** * 获得支付订单列表 * @@ -155,5 +162,4 @@ public interface PayOrderService { * @return 过期的订单数量 */ int expireOrder(); - } diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java index b7f18abd6..6597e048a 100755 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java @@ -84,6 +84,17 @@ public class PayOrderServiceImpl implements PayOrderService { return orderMapper.selectByAppIdAndMerchantOrderId(appId, merchantOrderId); } + @Override + public String getMerchantOrderIdByNo(String no) { + if(no.contains(payProperties.getOrderNoPrefix())){ + PayOrderDO order = orderMapper.selectByNo(no); + if(ObjectUtil.isNotNull(order)){ + return order.getMerchantOrderId(); + } + } + return null; + } + @Override public List getOrderList(Collection ids) { if (CollUtil.isEmpty(ids)) { From 2d7bad4e98a832e14a7ebdceaa740a2a474d6e83 Mon Sep 17 00:00:00 2001 From: wuKong Date: Mon, 10 Nov 2025 16:25:39 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=F0=9F=8E=A8=20feat(trade):=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E8=AE=A2=E5=8D=95=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D=E6=9F=A5=E8=AF=A2=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 getOrderDetail 接口参数 id 类型为 String - 根据 sync 参数决定调用不同的订单查询方法 - 删除冗余的 getOrderDetailByOutTradeNo 接口 - 优化订单支付状态同步逻辑 - 保持原有订单详情返回结构不变 --- .../app/order/AppTradeOrderController.java | 45 ++++--------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index 6f72e6507..7f2136fbe 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.trade.controller.app.order; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; @@ -93,10 +95,15 @@ public class AppTradeOrderController { @Parameter(name = "id", description = "交易订单编号"), @Parameter(name = "sync", description = "是否同步支付状态", example = "true") }) - public CommonResult getOrderDetail(@RequestParam("id") Long id, + public CommonResult getOrderDetail(@RequestParam("id") String id, @RequestParam(value = "sync", required = false) Boolean sync) { // 1.1 查询订单 - TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id); + TradeOrderDO order; + if (ObjectUtil.isNotNull(sync)) { + order = tradeOrderQueryService.getOrderByOutTradeNo(getLoginUserId(),id); + } else { + order = tradeOrderQueryService.getOrder(getLoginUserId(), Convert.toLong(id)); + } if (order == null) { return success(null); } @@ -117,40 +124,6 @@ public class AppTradeOrderController { return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express)); } - @GetMapping("/get-detailByOutTradeNo") - @Operation(summary = "获得交易订单") - @Parameters({ - @Parameter(name = "id", description = "PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容"), - @Parameter(name = "sync", description = "是否同步支付状态", example = "true") - }) - public CommonResult getOrderDetailByOutTradeNo(@RequestParam("id") String id, - @RequestParam(value = "sync", required = false) Boolean sync) { - // PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容,如「index/orderDetail?id=${商品订单号}&channel=1」。PATH最多输入1条。 - // https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html - // 小程序商品订单详情path配置为:pages/order/detail?id=${商品订单号}&comein_type=wechat - // 通过 rpc payOrderNo -> tradeOrderId - // 1.1 查询订单 - TradeOrderDO order = tradeOrderQueryService.getOrderByOutTradeNo(getLoginUserId(), id); - if (order == null) { - return success(null); - } - // 1.2 sync 仅在等待支付 - if (Boolean.TRUE.equals(sync) - && TradeOrderStatusEnum.isUnpaid(order.getStatus()) && !order.getPayStatus()) { - tradeOrderUpdateService.syncOrderPayStatusQuietly(order.getId(), order.getPayOrderId()); - // 重新查询,因为同步后,可能会有变化 - order = tradeOrderQueryService.getOrder(order.getId()); - } - - // 2.1 查询订单项 - List orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId()); - // 2.2 查询物流公司 - DeliveryExpressDO express = order.getLogisticsId() != null && order.getLogisticsId() > 0 ? - deliveryExpressService.getDeliveryExpress(order.getLogisticsId()) : null; - // 2.3 最终组合 - return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express)); - } - @GetMapping("/get-express-track-list") @Operation(summary = "获得交易订单的物流轨迹") @Parameter(name = "id", description = "交易订单编号") From d952cdec592dbd465021e1c25c7b2a873ddfb4c5 Mon Sep 17 00:00:00 2001 From: wuKong Date: Mon, 10 Nov 2025 16:34:33 +0800 Subject: [PATCH 04/14] =?UTF-8?q?docs(order):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade/controller/app/order/AppTradeOrderController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index 7f2136fbe..2705399a5 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -92,7 +92,7 @@ public class AppTradeOrderController { @GetMapping("/get-detail") @Operation(summary = "获得交易订单") @Parameters({ - @Parameter(name = "id", description = "交易订单编号"), + @Parameter(name = "id", description = "交易订单编号 或 微信小程序商品订单详情PATH配置的「${商品订单号} 」out_trade_no"), @Parameter(name = "sync", description = "是否同步支付状态", example = "true") }) public CommonResult getOrderDetail(@RequestParam("id") String id, From 45603c7132d09d820966198fa6848b68dd6e39b6 Mon Sep 17 00:00:00 2001 From: wuKong Date: Mon, 10 Nov 2025 17:30:56 +0800 Subject: [PATCH 05/14] =?UTF-8?q?fix(trade):=E4=BC=98=E5=8C=96=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将同步支付状态参数改为字符串类型以避免空指针异常 - 添加参数非空判断逻辑 - 保留原有 Long 类型订单 ID 查询方式 - 增强接口对不同输入参数的兼容性处理 - 使用 Hutool 工具类进行参数转换与校验 - 维持原接口功能不变的基础上提升健壮性 --- .../controller/app/order/AppTradeOrderController.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index 2705399a5..568e0c141 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.trade.controller.app.order; import cn.hutool.core.convert.Convert; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -96,13 +98,17 @@ public class AppTradeOrderController { @Parameter(name = "sync", description = "是否同步支付状态", example = "true") }) public CommonResult getOrderDetail(@RequestParam("id") String id, - @RequestParam(value = "sync", required = false) Boolean sync) { + @RequestParam(value = "sync", required = false) String syncParam) { // 1.1 查询订单 TradeOrderDO order; + Boolean sync = null; + if (CharSequenceUtil.isNotEmpty(syncParam)) { + sync = BooleanUtil.toBoolean(syncParam); + } if (ObjectUtil.isNotNull(sync)) { order = tradeOrderQueryService.getOrderByOutTradeNo(getLoginUserId(),id); } else { - order = tradeOrderQueryService.getOrder(getLoginUserId(), Convert.toLong(id)); + order = tradeOrderQueryService.getOrder(getLoginUserId(),Convert.toLong(id)); } if (order == null) { return success(null); From f63b83730077396a5f49f8fbfa382d1f544817de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8A=8B=E9=81=93=E6=BA=90=E7=A0=81?= Date: Fri, 21 Nov 2025 13:21:34 +0000 Subject: [PATCH 06/14] =?UTF-8?q?=E5=9B=9E=E9=80=80=20'Pull=20Request=20!2?= =?UTF-8?q?16=20:=20feat(pay,mall-trade):=20=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E5=95=86=E5=93=81=E8=AE=A2=E5=8D=95=E8=AF=A6=E6=83=85path?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=94=AF=E6=8C=81'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/order/AppTradeOrderController.java | 23 ++++--------------- .../service/order/TradeOrderQueryService.java | 10 +------- .../order/TradeOrderQueryServiceImpl.java | 18 --------------- .../module/pay/api/order/PayOrderApi.java | 4 ---- .../module/pay/api/order/PayOrderApiImpl.java | 4 ---- .../pay/dal/mysql/order/PayOrderMapper.java | 4 ---- .../pay/service/order/PayOrderService.java | 8 +------ .../service/order/PayOrderServiceImpl.java | 11 --------- 8 files changed, 7 insertions(+), 75 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index 568e0c141..b2b887050 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -1,9 +1,5 @@ package cn.iocoder.yudao.module.trade.controller.app.order; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.text.CharSequenceUtil; -import cn.hutool.core.util.BooleanUtil; -import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; @@ -94,22 +90,13 @@ public class AppTradeOrderController { @GetMapping("/get-detail") @Operation(summary = "获得交易订单") @Parameters({ - @Parameter(name = "id", description = "交易订单编号 或 微信小程序商品订单详情PATH配置的「${商品订单号} 」out_trade_no"), + @Parameter(name = "id", description = "交易订单编号"), @Parameter(name = "sync", description = "是否同步支付状态", example = "true") }) - public CommonResult getOrderDetail(@RequestParam("id") String id, - @RequestParam(value = "sync", required = false) String syncParam) { + public CommonResult getOrderDetail(@RequestParam("id") Long id, + @RequestParam(value = "sync", required = false) Boolean sync) { // 1.1 查询订单 - TradeOrderDO order; - Boolean sync = null; - if (CharSequenceUtil.isNotEmpty(syncParam)) { - sync = BooleanUtil.toBoolean(syncParam); - } - if (ObjectUtil.isNotNull(sync)) { - order = tradeOrderQueryService.getOrderByOutTradeNo(getLoginUserId(),id); - } else { - order = tradeOrderQueryService.getOrder(getLoginUserId(),Convert.toLong(id)); - } + TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id); if (order == null) { return success(null); } @@ -118,7 +105,7 @@ public class AppTradeOrderController { && TradeOrderStatusEnum.isUnpaid(order.getStatus()) && !order.getPayStatus()) { tradeOrderUpdateService.syncOrderPayStatusQuietly(order.getId(), order.getPayOrderId()); // 重新查询,因为同步后,可能会有变化 - order = tradeOrderQueryService.getOrder(order.getId()); + order = tradeOrderQueryService.getOrder(id); } // 2.1 查询订单项 diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java index 975f79394..d2509643d 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java @@ -39,15 +39,7 @@ public interface TradeOrderQueryService { * @return 交易订单 */ TradeOrderDO getOrder(Long userId, Long id); - /** - * 获得指定用户,指定的交易订单 - * PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容,如「index/orderDetail?id=${商品订单号}&channel=1」。PATH最多输入1条。 - * https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html - * @param userId 用户编号 - * @param outTradeNo 支付订单no - * @return 交易订单 - */ - TradeOrderDO getOrderByOutTradeNo(Long userId, String outTradeNo); + /** * 获得指定用户,指定活动,指定状态的交易订单 * diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java index 1470f17e8..ad530f842 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java @@ -1,16 +1,13 @@ package cn.iocoder.yudao.module.trade.service.order; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.member.api.user.MemberUserApi; import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; -import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; @@ -60,8 +57,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @Resource private MemberUserApi memberUserApi; - @Resource - private PayOrderApi payOrderApi; // =================== Order =================== @Override @@ -78,19 +73,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { } return order; } - @Override - public TradeOrderDO getOrderByOutTradeNo(Long userId, String outTradeNo) { - // PATH需包含「${商品订单号} 」,微信将把你在支付预下单接口填入的 out_trade_no 替换此内容,如「index/orderDetail?id=${商品订单号}&channel=1」。PATH最多输入1条。 - // https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html - // 小程序商品订单详情path配置为:pages/order/detail?id=${商品订单号}&comein_type=wechat - // 通过 rpc payOrderNo -> tradeOrderId - String id = outTradeNo; - String merchantOrderId = payOrderApi.getMerchantOrderIdByPayOrderNo(outTradeNo).getCheckedData(); - if(CharSequenceUtil.isNotEmpty(merchantOrderId)){ - id = merchantOrderId; - } - return getSelf().getOrder(userId, Convert.toLong(id)); - } @Override public TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status) { diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java index 07ac82489..c0d818d3b 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java @@ -39,8 +39,4 @@ public interface PayOrderApi { CommonResult updatePayOrderPrice(@RequestParam("id") Long id, @RequestParam("payPrice") Integer payPrice); - @PostMapping(PREFIX + "/getMerchantOrderIdByNo") - @Operation(summary = "根据支付订单编号获取商户订单编号") - @Parameter(name = "no", description = "支付单编号", example = "Pxxxx", required = true) - CommonResult getMerchantOrderIdByPayOrderNo(@RequestParam("no") String no); } diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java index d83971519..1ca3414db 100644 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java @@ -37,8 +37,4 @@ public class PayOrderApiImpl implements PayOrderApi { return success(true); } - @Override - public CommonResult getMerchantOrderIdByPayOrderNo(String no) { - return success(payOrderService.getMerchantOrderIdByNo(no)); - } } diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java index e83d0387c..95510d5f7 100755 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java @@ -48,10 +48,6 @@ public interface PayOrderMapper extends BaseMapperX { PayOrderDO::getMerchantOrderId, merchantOrderId); } - default PayOrderDO selectByNo(String no) { - return selectOne(PayOrderDO::getNo, no); - } - default int updateByIdAndStatus(Long id, Integer status, PayOrderDO update) { return update(update, new LambdaQueryWrapper() .eq(PayOrderDO::getId, id).eq(PayOrderDO::getStatus, status)); diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java index 7853b4417..c4496f829 100755 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java @@ -40,13 +40,6 @@ public interface PayOrderService { */ PayOrderDO getOrder(Long appId, String merchantOrderId); - /** - * 获得商户订单编号 - * - * @param no 编号 - * @return merchantOrderId - */ - String getMerchantOrderIdByNo(String no); /** * 获得支付订单列表 * @@ -162,4 +155,5 @@ public interface PayOrderService { * @return 过期的订单数量 */ int expireOrder(); + } diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java index 6597e048a..b7f18abd6 100755 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java @@ -84,17 +84,6 @@ public class PayOrderServiceImpl implements PayOrderService { return orderMapper.selectByAppIdAndMerchantOrderId(appId, merchantOrderId); } - @Override - public String getMerchantOrderIdByNo(String no) { - if(no.contains(payProperties.getOrderNoPrefix())){ - PayOrderDO order = orderMapper.selectByNo(no); - if(ObjectUtil.isNotNull(order)){ - return order.getMerchantOrderId(); - } - } - return null; - } - @Override public List getOrderList(Collection ids) { if (CollUtil.isEmpty(ids)) { From a0b7777783e981622bc5c8cb8cbb2eba3cea8ba6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Nov 2025 16:12:56 +0800 Subject: [PATCH 07/14] =?UTF-8?q?fix=EF=BC=9A=E3=80=90bpm=E3=80=91LoadBala?= =?UTF-8?q?nced=20=E7=9A=84=20RestTemplate=20=E5=9C=BA=E6=99=AF=E4=B8=8B?= =?UTF-8?q?=EF=BC=8C=E5=BD=B1=E5=93=8D=E6=99=AE=E9=80=9A=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=E7=9A=84=E8=AE=BF=E9=97=AE=EF=BC=8C=E8=A7=A3=E5=86=B3=20https:?= =?UTF-8?q?//gitee.com/zhijiantianya/yudao-cloud/issues/ICY7H3=20=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/config/YudaoWebAutoConfiguration.java | 14 +++++++++++++- .../bpm/api/event/CrmContractStatusListener.java | 8 +++++++- .../bpm/api/event/CrmReceivableStatusListener.java | 8 +++++++- .../flowable/core/util/BpmHttpRequestUtils.java | 8 +++++++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java index e27095a46..2b368f598 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java @@ -21,6 +21,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import org.springframework.util.AntPathMatcher; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @@ -146,9 +147,20 @@ public class YudaoWebAutoConfiguration { */ @Bean @ConditionalOnMissingBean - @LoadBalanced + @Primary public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder.build(); } + /** + * 创建 RestTemplate 实例(支持负载均衡) + * + * @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder} + */ + @Bean + @LoadBalanced + public RestTemplate loadBalancedRestTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmContractStatusListener.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmContractStatusListener.java index 12eb9cf17..00dc09500 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmContractStatusListener.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmContractStatusListener.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.bpm.api.event; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.client.RestTemplate; /** * 合同审批的结果的监听器实现类 @@ -11,6 +13,9 @@ import org.springframework.web.bind.annotation.RequestBody; */ public class CrmContractStatusListener extends BpmProcessInstanceStatusEventListener { + @Resource + private RestTemplate loadBalancedRestTemplate; + @Override public String getProcessDefinitionKey() { return "crm-contract-audit"; @@ -19,7 +24,8 @@ public class CrmContractStatusListener extends BpmProcessInstanceStatusEventList @Override public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) { BpmHttpRequestUtils.executeBpmHttpRequest(event, - "http://crm-server/rpc-api/crm/contract/update-audit-status"); + "http://crm-server/rpc-api/crm/contract/update-audit-status", + loadBalancedRestTemplate); } } diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmReceivableStatusListener.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmReceivableStatusListener.java index a780a5be7..336c94cee 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmReceivableStatusListener.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmReceivableStatusListener.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.bpm.api.event; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.client.RestTemplate; /** * 回款审批的结果的监听器实现类 @@ -11,6 +13,9 @@ import org.springframework.web.bind.annotation.RequestBody; */ public class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventListener { + @Resource + private RestTemplate loadBalancedRestTemplate; + @Override public String getProcessDefinitionKey() { return "crm-receivable-audit"; @@ -19,7 +24,8 @@ public class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventLi @Override public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) { BpmHttpRequestUtils.executeBpmHttpRequest(event, - "http://crm-server/rpc-api/crm/receivable/update-audit-status"); + "http://crm-server/rpc-api/crm/receivable/update-audit-status", + loadBalancedRestTemplate); } } diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java index 358ee9f4d..6de7f9dfa 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -80,6 +80,13 @@ public class BpmHttpRequestUtils { public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event, String url) { + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); + executeBpmHttpRequest(event, url, restTemplate); + } + + public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event, + String url, + RestTemplate restTemplate) { // 1.1 设置请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -102,7 +109,6 @@ public class BpmHttpRequestUtils { // } // 2. 发起请求 - RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); sendHttpRequest(url, headers, event, restTemplate); } From 69e595d62ef23f75147c34573eeb751eaf0d9be7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Nov 2025 09:07:03 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT?= =?UTF-8?q?=20=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimestampLocalDateTimeSerializer.java | 18 +++++++++- .../rule/DataPermissionRuleFactoryImpl.java | 34 +++++++++++++++---- .../framework/excel/core/util/ExcelUtils.java | 11 +++--- .../mybatis/core/mapper/BaseMapperX.java | 23 +++++++++++++ .../filter/ApiEncryptResponseWrapper.java | 13 +++---- .../web/config/YudaoWebAutoConfiguration.java | 3 ++ .../AiKnowledgeSegmentServiceImpl.java | 3 ++ .../core/enums/BpmnVariableConstants.java | 4 +-- .../BpmProcessInstanceCopyServiceImpl.java | 2 +- .../task/BpmProcessInstanceServiceImpl.java | 10 ++---- .../bpm/service/task/BpmTaskServiceImpl.java | 26 ++++++-------- .../vo/plan/CrmReceivablePlanRespVO.java | 1 - .../file/core/client/AbstractFileClient.java | 11 +++++- .../resources/codegen/vue3/views/index.vue.vm | 2 ++ .../vue3_vben5_ele/schema/views/data.ts.vm | 3 ++ .../dal/mysql/alert/IotAlertConfigMapper.java | 2 +- .../rule/IotSceneRuleMessageHandler.java | 3 -- .../service/coupon/CouponServiceImpl.java | 5 +-- .../DeliveryExpressTemplateConvert.java | 4 ++- .../convert/order/TradeOrderConvert.java | 4 ++- .../brokerage/BrokerageRecordDO.java | 2 +- .../brokerage/BrokerageRecordMapper.java | 2 +- .../aftersale/AfterSaleServiceImpl.java | 17 +++++++--- .../BrokerageWithdrawServiceImpl.java | 10 +++--- .../impl/weixin/AbstractWxPayClient.java | 2 +- .../service/social/SocialUserServiceImpl.java | 1 + .../service/user/AdminUserServiceImpl.java | 4 ++- 27 files changed, 156 insertions(+), 64 deletions(-) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java index ef767a558..12354256e 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java @@ -1,12 +1,16 @@ package cn.iocoder.yudao.framework.common.util.json.databind; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; +import org.apache.commons.lang3.reflect.FieldUtils; import java.io.IOException; +import java.lang.reflect.Field; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; /** * 基于时间戳的 LocalDateTime 序列化器 @@ -19,7 +23,19 @@ public class TimestampLocalDateTimeSerializer extends JsonSerializer clazz = gen.getOutputContext().getCurrentValue().getClass(); + Field field = FieldUtils.getField(clazz, fieldName, true); + // 情况一:有 JsonFormat 自定义注解,则使用它。https://github.com/YunaiV/ruoyi-vue-pro/pull/1019 + JsonFormat[] jsonFormats = field.getAnnotationsByType(JsonFormat.class); + if (jsonFormats.length > 0) { + String pattern = jsonFormats[0].pattern(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + gen.writeString(formatter.format(value)); + return; + } + + // 情况二:默认将 LocalDateTime 对象,转换为 Long 时间戳 gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java index eaa6e6aed..8119c7d52 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder; +import com.fhs.trans.service.impl.SimpleTransService; import lombok.RequiredArgsConstructor; import java.util.Collections; @@ -31,32 +32,53 @@ public class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory @Override // mappedStatementId 参数,暂时没有用。以后,可以基于 mappedStatementId + DataPermission 进行缓存 public List getDataPermissionRule(String mappedStatementId) { - // 1. 无数据权限 + // 1.1 无数据权限 if (CollUtil.isEmpty(rules)) { return Collections.emptyList(); } - // 2. 未配置,则默认开启 + // 1.2 未配置,则默认开启 DataPermission dataPermission = DataPermissionContextHolder.get(); if (dataPermission == null) { return rules; } - // 3. 已配置,但禁用 + // 1.3 已配置,但禁用 if (!dataPermission.enable()) { return Collections.emptyList(); } + // 1.4 特殊:数据翻译时,强制忽略数据权限 https://github.com/YunaiV/ruoyi-vue-pro/issues/1007 + if (isTranslateCall()) { + return Collections.emptyList(); + } - // 4. 已配置,只选择部分规则 + // 2.1 情况一:已配置,只选择部分规则 if (ArrayUtil.isNotEmpty(dataPermission.includeRules())) { return rules.stream().filter(rule -> ArrayUtil.contains(dataPermission.includeRules(), rule.getClass())) .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询 } - // 5. 已配置,只排除部分规则 + // 2.2 已配置,只排除部分规则 if (ArrayUtil.isNotEmpty(dataPermission.excludeRules())) { return rules.stream().filter(rule -> !ArrayUtil.contains(dataPermission.excludeRules(), rule.getClass())) .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询 } - // 6. 已配置,全部规则 + // 2.3 已配置,全部规则 return rules; } + /** + * 判断是否为数据翻译 {@link com.fhs.core.trans.anno.Trans} 的调用 + * + * 目前暂时只有这个办法,已经和 easy-trans 做过沟通 + * + * @return 是否 + */ + private boolean isTranslateCall() { + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + for (StackTraceElement e : stack) { + if (SimpleTransService.class.getName().equals(e.getClassName())) { + return true; + } + } + return false; + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java index bbcd23efe..f4f17ec1d 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.excel.core.util; import cn.idev.excel.FastExcelFactory; import cn.idev.excel.converters.longconverter.LongStringConverter; -import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; @@ -10,6 +9,7 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.io.InputStream; import java.util.List; /** @@ -45,9 +45,12 @@ public class ExcelUtils { } public static List read(MultipartFile file, Class head) throws IOException { - return FastExcelFactory.read(file.getInputStream(), head, null) - .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 - .doReadAllSync(); + // 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景 + try (InputStream inputStream = file.getInputStream()) { + return FastExcelFactory.read(inputStream, head, null) + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 + .doReadAllSync(); + } } } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java index 52ca947cc..9858a1aed 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java @@ -68,6 +68,29 @@ public interface BaseMapperX extends MPJBaseMapper { return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); } + /** + * 执行分页查询并返回结果。 + * + * @param pageParam 分页参数,包含页码、每页条数和排序字段信息。如果 pageSize 为 {@link PageParam#PAGE_SIZE_NONE},则不分页,直接查询所有数据。 + * @param clazz 结果集的类类型 + * @param lambdaWrapper MyBatis Plus Join 查询条件包装器 + * @param 结果集的泛型类型 + * @return 返回分页查询的结果,包括总记录数和当前页的数据列表 + */ + default PageResult selectJoinPage(SortablePageParam pageParam, Class clazz, MPJLambdaWrapper lambdaWrapper) { + // 特殊:不分页,直接查询全部 + if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) { + List list = selectJoinList(clazz, lambdaWrapper); + return new PageResult<>(list, (long) list.size()); + } + + // MyBatis Plus Join 查询 + IPage mpPage = MyBatisUtils.buildPage(pageParam, pageParam.getSortingFields()); + mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper); + // 转换返回 + return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); + } + default PageResult selectJoinPage(PageParam pageParam, Class resultTypeClass, MPJBaseJoin joinQueryWrapper) { IPage mpPage = MyBatisUtils.buildPage(pageParam); selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper); diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiEncryptResponseWrapper.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiEncryptResponseWrapper.java index fed38917b..a7b38f7db 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiEncryptResponseWrapper.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/encrypt/core/filter/ApiEncryptResponseWrapper.java @@ -42,15 +42,16 @@ public class ApiEncryptResponseWrapper extends HttpServletResponseWrapper { this.flushBuffer(); byte[] body = byteArrayOutputStream.toByteArray(); - // 2. 加密 body - String encryptedBody = symmetricEncryptor != null ? symmetricEncryptor.encryptBase64(body) - : asymmetricEncryptor.encryptBase64(body, KeyType.PublicKey); - response.getWriter().write(encryptedBody); - - // 3. 添加加密 header 标识 + // 2. 添加加密 header 标识 this.addHeader(properties.getHeader(), "true"); // 特殊:特殊:https://juejin.cn/post/6867327674675625992 this.addHeader("Access-Control-Expose-Headers", properties.getHeader()); + + // 3.1 加密 body + String encryptedBody = symmetricEncryptor != null ? symmetricEncryptor.encryptBase64(body) + : asymmetricEncryptor.encryptBase64(body, KeyType.PublicKey); + // 3.2 输出加密后的 body:(设置 header 要放在 response 的 write 之前) + response.getWriter().write(encryptedBody); } @Override diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java index 2b368f598..552259f2e 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; +import org.springframework.core.annotation.Order; import org.springframework.util.AntPathMatcher; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @@ -82,6 +83,7 @@ public class YudaoWebAutoConfiguration { } @Bean + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogCommonApi apiErrorLogApi) { return new GlobalExceptionHandler(applicationName, apiErrorLogApi); } @@ -104,6 +106,7 @@ public class YudaoWebAutoConfiguration { * 创建 CorsFilter Bean,解决跨域问题 */ @Bean + @Order(value = WebFilterOrderEnum.CORS_FILTER) // 特殊:修复因执行顺序影响到跨域配置不生效问题 public FilterRegistrationBean corsFilterBean() { // 创建 CorsConfiguration 对象 CorsConfiguration config = new CorsConfiguration(); diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index dd0f91315..43c7e9cef 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -227,6 +227,9 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService // 2. 检索 List documents = searchDocument(knowledge, reqBO); + if (CollUtil.isEmpty(documents)) { + return ListUtil.empty(); + } // 3.1 段落召回 List segments = segmentMapper diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index b3ce94614..c1d956836 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -54,11 +54,11 @@ public class BpmnVariableConstants { public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; /** - * 流程实例的变量前缀 - 用于退回操作,记录需要预测的节点:格式 NEED_SIMULATE_TASK_{节点定义 id} + * 流程实例的变量 - 用于退回操作,记录需要预测的节点 ids, 变量值类型为 Set * * 目的是:退回操作,预测节点会不准,在流程变量中记录需要预测的节点,来辅助预测 */ - public static final String PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX = "NEED_SIMULATE_TASK_"; + public static final String PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS = "NEED_SIMULATE_TASK_IDS"; /** * 流程实例的变量 - 是否跳过表达式 diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java index ed89cfe9d..584479d08 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -54,7 +54,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy } // 执行抄送 createProcessInstanceCopy(userIds, reason, - task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getId(), task.getName()); + task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getName(), task.getId()); } @Override diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 4938cf17a..c97277949 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.*; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; @@ -72,7 +72,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -227,11 +226,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3.2 获取由于退回操作,需要预测的节点。从流程变量中获取,回退操作会设置这些变量 Set needSimulateTaskDefKeysByReturn = new HashSet<>(); if (StrUtil.isNotEmpty(reqVO.getProcessInstanceId())) { - Map variables = runtimeService.getVariables(reqVO.getProcessInstanceId()); - Map simulateTaskVariables = MapUtil.filter(variables, - item -> item.getKey().startsWith(PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX)); - simulateTaskVariables.forEach((key, value) -> - needSimulateTaskDefKeysByReturn.add(StrUtil.removePrefix(key, PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX))); + Object needSimulateTaskIds = runtimeService.getVariable(reqVO.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS); + needSimulateTaskDefKeysByReturn.addAll(Convert.toSet(String.class, needSimulateTaskIds)); } // 移除运行中的节点,运行中的节点无需预测 if (CollUtil.isNotEmpty(runActivityNodes)) { diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index a658d6d4f..b00ad85e7 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -69,7 +69,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; -//import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; /** @@ -604,11 +603,13 @@ public class BpmTaskServiceImpl implements BpmTaskService { bpmnModel, reqVO.getNextAssignees(), instance); runtimeService.setVariables(task.getProcessInstanceId(), variables); - // 5. 移除辅助预测的流程变量,这些变量在回退操作中设置 - // todo @jason:可以直接 + 拼接哈 - String simulateVariableName = StrUtil.concat(false, - BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX, task.getTaskDefinitionKey()); - runtimeService.removeVariable(task.getProcessInstanceId(), simulateVariableName); + // 5. 如果当前节点 Id 存在于需要预测的流程节点中,从中移除。 流程变量在回退操作中设置 + Object needSimulateTaskIds = runtimeService.getVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS); + Set needSimulateTaskIdsByReturn = Convert.toSet(String.class, needSimulateTaskIds); + if (needSimulateTaskIdsByReturn.contains(task.getTaskDefinitionKey())) { + needSimulateTaskIdsByReturn.remove(task.getTaskDefinitionKey()); + runtimeService.setVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskIdsByReturn); + } // 6. 调用 BPM complete 去完成任务 taskService.complete(task.getId(), variables, true); @@ -937,11 +938,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { }); // 3. 构建需要预测的任务流程变量 - // TODO @jason:【驳回预测相关】是不是搞成一个变量,里面是 set 更简洁一点呀? Set needSimulateTaskDefinitionKeys = getNeedSimulateTaskDefinitionKeys(bpmnModel, currentTask, targetElement); - Map needSimulateVariables = convertMap(needSimulateTaskDefinitionKeys, - key -> StrUtil.concat(false, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX, key), item -> Boolean.TRUE); - // 4. 执行驳回 // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因: @@ -949,12 +946,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { runtimeService.createChangeActivityStateBuilder() .processInstanceId(currentTask.getProcessInstanceId()) .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) - // 设置需要预测的任务流程变量,用于辅助预测 - .processVariables(needSimulateVariables) - // 设置流程变量(local)节点退回标记, 用于退回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略,导致自动通过 + // 设置需要预测的任务 ids 的流程变量,用于辅助预测 + .processVariable(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskDefinitionKeys) + // 设置流程变量(local)节点退回标记, 用于退回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略,导致自动通过 .localVariable(reqVO.getTargetTaskDefinitionKey(), - String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), - Boolean.TRUE) + String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE) .changeState(); } diff --git a/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java b/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java index 2208887db..5feb99c22 100644 --- a/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java +++ b/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java @@ -59,7 +59,6 @@ public class CrmReceivablePlanRespVO { @ExcelProperty("回款编号") private Long receivableId; @Schema(description = "回款信息") - @ExcelProperty("回款信息") private CrmReceivableRespVO receivable; @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java b/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java index 3c7883b83..7ddade3ea 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java +++ b/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java @@ -19,10 +19,18 @@ public abstract class AbstractFileClient implem * 文件配置 */ protected Config config; + /** + * 原始的文件配置 + * + * 原因:{@link #config} 可能被子类所修改,无法用于判断配置是否变更 + * @link 相关案例 + */ + private Config originalConfig; public AbstractFileClient(Long id, Config config) { this.id = id; this.config = config; + this.originalConfig = config; } /** @@ -40,11 +48,12 @@ public abstract class AbstractFileClient implem public final void refresh(Config config) { // 判断是否更新 - if (config.equals(this.config)) { + if (config.equals(this.originalConfig)) { return; } log.info("[refresh][配置({})发生变化,重新初始化]", config); this.config = config; + this.originalConfig = config; // 初始化 this.init(); } diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm index fb7485d6d..857972a85 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm @@ -353,7 +353,9 @@ const handleDelete = async (id: number) => { // 发起删除 await ${simpleClassName}Api.delete${simpleClassName}(id) message.success(t('common.delSuccess')) +#if ( $table.templateType == 11 ) currentRow.value = {} +#end // 刷新列表 await getList() } catch {} diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/data.ts.vm index da9582448..c0f5b8e97 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/data.ts.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/views/data.ts.vm @@ -114,6 +114,7 @@ export function useFormSchema(): VbenFormSchema[] { showTime: true, format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'x', + class: '!w-full', }, #elseif($column.htmlType == "textarea")## 文本域 component: 'Textarea', @@ -317,6 +318,7 @@ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { showTime: true, format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'x', + class: '!w-full', }, #elseif($column.htmlType == "textarea")## 文本域 component: 'Textarea', @@ -552,6 +554,7 @@ export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${api showTime: true, format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'x', + class: '!w-full', }, #elseif($column.htmlType == "textarea")## 文本域 component: 'Textarea', diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java index c5d7154ff..52b0d360d 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java +++ b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java @@ -33,7 +33,7 @@ public interface IotAlertConfigMapper extends BaseMapperX { default List selectListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) { return selectList(new LambdaQueryWrapperX() .eq(IotAlertConfigDO::getStatus, status) - .apply(MyBatisUtils.findInSet("scene_rule_id", sceneRuleId))); + .apply(MyBatisUtils.findInSet("scene_rule_ids", sceneRuleId))); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageHandler.java b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageHandler.java index c39cefe4a..19e1f18ba 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageHandler.java @@ -42,9 +42,6 @@ public class IotSceneRuleMessageHandler implements IotMessageSubscriber couponTemplate.getTotalCount()) { // 已领取数量 >= 总发放数量 throw exception(COUPON_TEMPLATE_NOT_ENOUGH); } // 校验"固定日期"的有效期类型是否过期 diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java index b917d874b..3c02e8917 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java @@ -85,7 +85,9 @@ public interface DeliveryExpressTemplateConvert { .setChargeMode(template.getChargeMode()) .setCharge(convertTemplateCharge(findFirst(templateIdChargeMap.get(template.getId()), charge -> charge.getAreaIds().contains(areaId)))) .setFree(convertTemplateFree(findFirst(templateIdFreeMap.get(template.getId()), free -> free.getAreaIds().contains(areaId)))); - result.put(template.getId(), bo); + if (bo.getCharge() != null || bo.getFree() != null) { + result.put(template.getId(), bo); + } }); return result; } diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java index beacddab8..c90d9ed81 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java @@ -268,7 +268,9 @@ public interface TradeOrderConvert { .setTitle(StrUtil.format("{}成功购买{}", user.getNickname(), item.getSpuName())) .setFirstFixedPrice(0).setSecondFixedPrice(0); if (BooleanUtil.isTrue(spu.getSubCommissionType())) { - bo.setFirstFixedPrice(sku.getFirstBrokeragePrice()).setSecondFixedPrice(sku.getSecondBrokeragePrice()); + // 特殊:单独设置的佣金需要乘以购买数量。关联 https://gitee.com/yudaocode/yudao-mall-uniapp/issues/ICY7SJ + bo.setFirstFixedPrice(sku.getFirstBrokeragePrice() * item.getCount()) + .setSecondFixedPrice(sku.getSecondBrokeragePrice() * item.getCount()); } return bo; } diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/brokerage/BrokerageRecordDO.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/brokerage/BrokerageRecordDO.java index c819d723b..7dec2b850 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/brokerage/BrokerageRecordDO.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/brokerage/BrokerageRecordDO.java @@ -29,7 +29,7 @@ public class BrokerageRecordDO extends BaseDO { * 编号 */ @TableId - private Integer id; + private Long id; /** * 用户编号 *

diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java index a17c3bfca..8c64354f8 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java @@ -44,7 +44,7 @@ public interface BrokerageRecordMapper extends BaseMapperX { .lt(BrokerageRecordDO::getUnfreezeTime, unfreezeTime)); } - default int updateByIdAndStatus(Integer id, Integer status, BrokerageRecordDO updateObj) { + default int updateByIdAndStatus(Long id, Integer status, BrokerageRecordDO updateObj) { return update(updateObj, new LambdaQueryWrapper() .eq(BrokerageRecordDO::getId, id) .eq(BrokerageRecordDO::getStatus, status)); diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java index 70bdbb564..e7fb1c721 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.trade.service.aftersale; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; @@ -352,12 +353,20 @@ public class AfterSaleServiceImpl implements AfterSaleService { throw exception(AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND); } - // 发起退款单。注意,需要在事务提交后,再进行发起,避免重复发起 - createPayRefund(userIp, afterSale); + Integer newStatus; + if (ObjUtil.equals(afterSale.getRefundPrice(), 0)) { + // 特殊:退款为 0 的订单,直接标记为完成(积分商城)。关联案例:https://t.zsxq.com/AQEvL + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.WAIT_REFUND.getStatus(), new AfterSaleDO() + .setStatus(AfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now())); + newStatus = AfterSaleStatusEnum.COMPLETE.getStatus(); + } else { + // 发起退款单。注意,需要在事务提交后,再进行发起,避免重复发起 + createPayRefund(userIp, afterSale); + newStatus = afterSale.getStatus(); // 特殊:这里状态不变,而是最终 updateAfterSaleRefunded 处理!!! + } // 记录售后日志 - AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), - afterSale.getStatus()); // 特殊:这里状态不变,而是最终 updateAfterSaleRefunded 处理!!! + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus); } private void createPayRefund(String userIp, AfterSaleDO afterSale) { diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java index 6b49e25f5..8dab82e81 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java @@ -147,9 +147,10 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { userAccount = wallet.getId().toString(); } // 1.2 构建请求 + Integer transferPrice = withdraw.getPrice() - withdraw.getFeePrice(); // 计算实际转账金额(提现金额 - 手续费) PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO() .setAppKey(tradeOrderProperties.getPayAppKey()).setChannelCode(channelCode) - .setMerchantTransferId(withdraw.getId().toString()).setSubject("佣金提现").setPrice(withdraw.getPrice()) + .setMerchantTransferId(withdraw.getId().toString()).setSubject("佣金提现").setPrice(transferPrice) .setUserAccount(userAccount).setUserName(userName).setUserIp(getClientIP()) .setUserId(withdraw.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue()) // 用户信息 .setChannelExtras(channelExtras); @@ -280,9 +281,10 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { throw exception(BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_STATUS_NOT_SUCCESS_OR_CLOSED); } // 2.2 校验转账金额一致 - if (ObjectUtil.notEqual(payTransfer.getPrice(), withdraw.getPrice())) { - log.error("[validateBrokerageTransferStatusCanUpdate][withdraw({}) payTransfer({}) 转账金额不匹配,请进行处理!withdraw 数据是:{},payTransfer 数据是:{}]", - withdraw.getId(), payTransferId, JsonUtils.toJsonString(withdraw), JsonUtils.toJsonString(payTransfer)); + Integer expectedTransferPrice = withdraw.getPrice() - withdraw.getFeePrice(); // 转账金额 = 提现金额 - 手续费 + if (ObjectUtil.notEqual(payTransfer.getPrice(), expectedTransferPrice)) { + log.error("[validateBrokerageTransferStatusCanUpdate][withdraw({}) payTransfer({}) 转账金额不匹配,请进行处理!withdraw 数据是:{},payTransfer 数据是:{},期望转账金额:{}]", + withdraw.getId(), payTransferId, JsonUtils.toJsonString(withdraw), JsonUtils.toJsonString(payTransfer), expectedTransferPrice); throw exception(BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH); } // 2.3 校验转账订单匹配 diff --git a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java index 4ee904bbb..3b54862a7 100644 --- a/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java +++ b/yudao-module-pay/yudao-module-pay-server/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java @@ -70,7 +70,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient userIds = reqVO.getRoleId() != null ? permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId())) : null; - + if (userIds != null && userIds.isEmpty()) { + return PageResult.empty(); + } // 分页查询 return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds); } From a90192898684551700a370ac8f056541481f616f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Nov 2025 17:36:36 +0800 Subject: [PATCH 09/14] =?UTF-8?q?chore=EF=BC=9Aflowable=20from=207.0.1=20t?= =?UTF-8?q?o=207.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 2 +- .../flowable/core/util/BpmnModelUtils.java | 18 +++++++++--------- .../task/listener/BpmCallActivityListener.java | 4 ++-- .../task/listener/BpmUserTaskListener.java | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 943003808..b4e169898 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -52,7 +52,7 @@ 1.1.11 5.2.0 - 7.0.1 + 7.2.0 1.4.0 1.21.2 diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 349f88048..36ee811b4 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -18,11 +18,11 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConsta import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.converter.BpmnXMLConverter; -import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.delegate.Expression; import org.flowable.common.engine.impl.util.io.BytesStreamSource; -import org.flowable.engine.impl.el.FixedValue; import java.util.*; @@ -406,7 +406,7 @@ public class BpmnModelUtils { flowableListener.getFieldExtensions().add(fieldExtension); } - public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(FixedValue fixedValue) { + public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(Expression fixedValue) { String expressionText = fixedValue.getExpressionText(); Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); @@ -673,7 +673,7 @@ public class BpmnModelUtils { // 这条线路存在目标节点,直接返回 true FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); if (target.getId().equals(sourceFlowElement.getId())) { - return true; + return true; } // 如果目标节点为并行网关,跳过这个循环 (TODO 疑问:这个判断作用是防止回退到并行网关分支上的节点吗?) if (sourceFlowElement instanceof ParallelGateway) { @@ -798,9 +798,9 @@ public class BpmnModelUtils { // 情况:StartEvent/EndEvent/UserTask/ServiceTask if (currentElement instanceof StartEvent - || currentElement instanceof EndEvent - || currentElement instanceof UserTask - || currentElement instanceof ServiceTask) { + || currentElement instanceof EndEvent + || currentElement instanceof UserTask + || currentElement instanceof ServiceTask) { // 添加节点 FlowNode flowNode = (FlowNode) currentElement; resultElements.add(flowNode); @@ -982,8 +982,8 @@ public class BpmnModelUtils { */ private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java index da148a856..967c2e343 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -14,9 +14,9 @@ import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.Expression; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.ExecutionListener; -import org.flowable.engine.impl.el.FixedValue; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; @@ -34,7 +34,7 @@ public class BpmCallActivityListener implements ExecutionListener { public static final String DELEGATE_EXPRESSION = "${bpmCallActivityListener}"; @Setter - private FixedValue listenerConfig; + private Expression listenerConfig; @Resource private BpmProcessDefinitionService processDefinitionService; diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java index 0364eddda..c1f6d9c55 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.Expression; import org.flowable.engine.delegate.TaskListener; -import org.flowable.engine.impl.el.FixedValue; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.service.delegate.DelegateTask; import org.springframework.context.annotation.Scope; @@ -33,7 +33,7 @@ public class BpmUserTaskListener implements TaskListener { private BpmProcessInstanceService processInstanceService; @Setter - private FixedValue listenerConfig; + private Expression listenerConfig; @Override public void notify(DelegateTask delegateTask) { From e98716e52631f8af2ed3a65f32179c52f5288eb7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 24 Nov 2025 10:54:10 +0800 Subject: [PATCH 10/14] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT?= =?UTF-8?q?=20=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 5 +++++ .../bpm/service/task/BpmTaskServiceImpl.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index c97277949..c55d8df0c 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -12,6 +12,7 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; @@ -752,6 +753,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService @@ -762,6 +764,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { return FlowableUtils.executeAuthenticatedUserId(userId, () -> { // 获得流程定义 @@ -878,6 +881,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); @@ -907,6 +911,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index b00ad85e7..6545d83d6 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -376,6 +376,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { } // 2.2 过滤:只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null)); + + // 2.3 过滤:只能退回到已经处理过的节点(排除审批未经过的节点)。相关 issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/982 + List finishedTasks = getFinishedTaskListByProcessInstanceIdWithoutCancel(task.getProcessInstanceId()); + Set finishedTaskDefinitionKeys = convertSet(finishedTasks, HistoricTaskInstance::getTaskDefinitionKey); + previousUserList.removeIf(userTask -> !finishedTaskDefinitionKeys.contains(userTask.getId())); return previousUserList; } @@ -544,6 +549,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) { // 1.1 校验任务存在 Task task = validateTask(userId, reqVO.getId()); @@ -788,6 +794,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) { // 1.1 校验任务存在 Task task = validateTask(userId, reqVO.getId()); @@ -855,6 +862,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) { // 1.1 当前任务 task Task task = validateTask(userId, reqVO.getId()); @@ -983,6 +991,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO) { String taskId = reqVO.getId(); // 1.1 校验任务 @@ -1012,6 +1021,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override + @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void transferTask(Long userId, BpmTaskTransferReqVO reqVO) { String taskId = reqVO.getId(); // 1.1 校验任务 @@ -1042,6 +1053,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void moveTaskToEnd(String processInstanceId, String reason) { List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null); if (CollUtil.isEmpty(taskList)) { @@ -1080,6 +1092,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) { // 1. 获取和校验任务 TaskEntityImpl taskEntity = validateTaskCanCreateSign(userId, reqVO); @@ -1196,6 +1209,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA @SuppressWarnings("DataFlowIssue") public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) { // 1.1 校验 task 可以被减签 @@ -1235,6 +1249,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public void withdrawTask(Long userId, String taskId) { // 1.1 查询本人已办任务 HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery() From f881cb74c57f2583bb0c73add9e57c0fbdc2610b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 24 Nov 2025 11:10:53 +0800 Subject: [PATCH 11/14] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT?= =?UTF-8?q?=20=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=88AI?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/postgresql/ruoyi-vue-pro.sql | 96 +++++++++---------- .../module/ai/enums/model/AiPlatformEnum.java | 1 + .../yudao-module-ai-server/pom.xml | 6 +- .../ai/config/AiAutoConfiguration.java | 34 +++++++ .../ai/config/YudaoAiProperties.java | 14 +++ .../ai/core/model/AiModelFactoryImpl.java | 29 ++++-- .../ai/core/model/grok/GrokChatModel.java | 44 +++++++++ .../config/SecurityConfiguration.java | 11 ++- .../iocoder/yudao/module/ai/util/AiUtils.java | 5 +- .../src/main/resources/application.yaml | 2 + .../src/main/resources/application.yaml | 2 + 11 files changed, 180 insertions(+), 64 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java diff --git a/sql/postgresql/ruoyi-vue-pro.sql b/sql/postgresql/ruoyi-vue-pro.sql index f85884776..859c6c383 100644 --- a/sql/postgresql/ruoyi-vue-pro.sql +++ b/sql/postgresql/ruoyi-vue-pro.sql @@ -33,7 +33,7 @@ INSERT INTO dual VALUES (1); DROP TABLE IF EXISTS infra_api_access_log; CREATE TABLE infra_api_access_log ( - id int8 NOT NULL, + id int8 NOT NULL DEFAULT NEXTVAL('infra_api_access_log_seq'), trace_id varchar(64) NOT NULL DEFAULT '', user_id int8 NOT NULL DEFAULT 0, user_type int2 NOT NULL DEFAULT 0, @@ -102,7 +102,7 @@ CREATE SEQUENCE infra_api_access_log_seq DROP TABLE IF EXISTS infra_api_error_log; CREATE TABLE infra_api_error_log ( - id int8 NOT NULL, + id int8 NOT NULL DEFAULT NEXTVAL('infra_api_error_log_seq'), trace_id varchar(64) NOT NULL, user_id int8 NOT NULL DEFAULT 0, user_type int2 NOT NULL DEFAULT 0, @@ -175,7 +175,7 @@ CREATE SEQUENCE infra_api_error_log_seq DROP TABLE IF EXISTS infra_codegen_column; CREATE TABLE infra_codegen_column ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_codegen_column_seq'), table_id int8 NOT NULL, column_name varchar(200) NOT NULL, data_type varchar(100) NOT NULL, @@ -238,7 +238,7 @@ CREATE SEQUENCE infra_codegen_column_seq DROP TABLE IF EXISTS infra_codegen_table; CREATE TABLE infra_codegen_table ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_codegen_table_seq'), data_source_config_id int8 NOT NULL, scene int2 NOT NULL DEFAULT 1, table_name varchar(200) NOT NULL DEFAULT '', @@ -303,7 +303,7 @@ CREATE SEQUENCE infra_codegen_table_seq DROP TABLE IF EXISTS infra_config; CREATE TABLE infra_config ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_config_seq'), category varchar(50) NOT NULL, type int2 NOT NULL, name varchar(100) NOT NULL DEFAULT '', @@ -362,7 +362,7 @@ CREATE SEQUENCE infra_config_seq DROP TABLE IF EXISTS infra_data_source_config; CREATE TABLE infra_data_source_config ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_data_source_config_seq'), name varchar(100) NOT NULL DEFAULT '', url varchar(1024) NOT NULL, username varchar(255) NOT NULL, @@ -399,7 +399,7 @@ CREATE SEQUENCE infra_data_source_config_seq DROP TABLE IF EXISTS infra_file; CREATE TABLE infra_file ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_file_seq'), config_id int8 NULL DEFAULT NULL, name varchar(256) NULL DEFAULT NULL, path varchar(512) NOT NULL, @@ -440,7 +440,7 @@ CREATE SEQUENCE infra_file_seq DROP TABLE IF EXISTS infra_file_config; CREATE TABLE infra_file_config ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_file_config_seq'), name varchar(63) NOT NULL, storage int2 NOT NULL, remark varchar(255) NULL DEFAULT NULL, @@ -496,7 +496,7 @@ CREATE SEQUENCE infra_file_config_seq DROP TABLE IF EXISTS infra_file_content; CREATE TABLE infra_file_content ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_file_content_seq'), config_id int8 NOT NULL, path varchar(512) NOT NULL, content bytea NOT NULL, @@ -531,7 +531,7 @@ CREATE SEQUENCE infra_file_content_seq DROP TABLE IF EXISTS infra_job; CREATE TABLE infra_job ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_job_seq'), name varchar(32) NOT NULL, status int2 NOT NULL, handler_name varchar(64) NOT NULL, @@ -597,7 +597,7 @@ CREATE SEQUENCE infra_job_seq DROP TABLE IF EXISTS infra_job_log; CREATE TABLE infra_job_log ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('infra_job_log_seq'), job_id int8 NOT NULL, handler_name varchar(64) NOT NULL, handler_param varchar(255) NULL DEFAULT NULL, @@ -644,7 +644,7 @@ CREATE SEQUENCE infra_job_log_seq DROP TABLE IF EXISTS system_dept; CREATE TABLE system_dept ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_dept_seq'), name varchar(30) NOT NULL DEFAULT '', parent_id int8 NOT NULL DEFAULT 0, sort int4 NOT NULL DEFAULT 0, @@ -711,7 +711,7 @@ CREATE SEQUENCE system_dept_seq DROP TABLE IF EXISTS system_dict_data; CREATE TABLE system_dict_data ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_dict_data_seq'), sort int4 NOT NULL DEFAULT 0, label varchar(100) NOT NULL DEFAULT '', value varchar(100) NOT NULL DEFAULT '', @@ -1367,7 +1367,7 @@ CREATE SEQUENCE system_dict_data_seq DROP TABLE IF EXISTS system_dict_type; CREATE TABLE system_dict_type ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_dict_type_seq'), name varchar(100) NOT NULL DEFAULT '', type varchar(100) NOT NULL DEFAULT '', status int2 NOT NULL DEFAULT 0, @@ -1521,7 +1521,7 @@ CREATE SEQUENCE system_dict_type_seq DROP TABLE IF EXISTS system_login_log; CREATE TABLE system_login_log ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_login_log_seq'), log_type int8 NOT NULL, trace_id varchar(64) NOT NULL DEFAULT '', user_id int8 NOT NULL DEFAULT 0, @@ -1568,7 +1568,7 @@ CREATE SEQUENCE system_login_log_seq DROP TABLE IF EXISTS system_mail_account; CREATE TABLE system_mail_account ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_mail_account_seq'), mail varchar(255) NOT NULL, username varchar(255) NOT NULL, password varchar(255) NOT NULL, @@ -1623,7 +1623,7 @@ CREATE SEQUENCE system_mail_account_seq DROP TABLE IF EXISTS system_mail_log; CREATE TABLE system_mail_log ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_mail_log_seq'), user_id int8 NULL DEFAULT NULL, user_type int2 NULL DEFAULT NULL, to_mail varchar(255) NOT NULL, @@ -1682,7 +1682,7 @@ CREATE SEQUENCE system_mail_log_seq DROP TABLE IF EXISTS system_mail_template; CREATE TABLE system_mail_template ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_mail_template_seq'), name varchar(63) NOT NULL, code varchar(63) NOT NULL, account_id int8 NOT NULL, @@ -1740,7 +1740,7 @@ CREATE SEQUENCE system_mail_template_seq DROP TABLE IF EXISTS system_menu; CREATE TABLE system_menu ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_menu_seq'), name varchar(50) NOT NULL, permission varchar(100) NOT NULL DEFAULT '', type int2 NOT NULL, @@ -2714,7 +2714,7 @@ CREATE SEQUENCE system_menu_seq DROP TABLE IF EXISTS system_notice; CREATE TABLE system_notice ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_notice_seq'), title varchar(50) NOT NULL, content text NOT NULL, type int2 NOT NULL, @@ -2764,7 +2764,7 @@ CREATE SEQUENCE system_notice_seq DROP TABLE IF EXISTS system_notify_message; CREATE TABLE system_notify_message ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_notify_message_seq'), user_id int8 NOT NULL, user_type int2 NOT NULL, template_id int8 NOT NULL, @@ -2832,7 +2832,7 @@ CREATE SEQUENCE system_notify_message_seq DROP TABLE IF EXISTS system_notify_template; CREATE TABLE system_notify_template ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_notify_template_seq'), name varchar(63) NOT NULL, code varchar(64) NOT NULL, nickname varchar(255) NOT NULL, @@ -2877,7 +2877,7 @@ CREATE SEQUENCE system_notify_template_seq DROP TABLE IF EXISTS system_oauth2_access_token; CREATE TABLE system_oauth2_access_token ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_oauth2_access_token_seq'), user_id int8 NOT NULL, user_type int2 NOT NULL, user_info varchar(512) NOT NULL, @@ -2927,7 +2927,7 @@ CREATE SEQUENCE system_oauth2_access_token_seq DROP TABLE IF EXISTS system_oauth2_approve; CREATE TABLE system_oauth2_approve ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_oauth2_approve_seq'), user_id int8 NOT NULL, user_type int2 NOT NULL, client_id varchar(255) NOT NULL, @@ -2970,7 +2970,7 @@ CREATE SEQUENCE system_oauth2_approve_seq DROP TABLE IF EXISTS system_oauth2_client; CREATE TABLE system_oauth2_client ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_oauth2_client_seq'), client_id varchar(255) NOT NULL, secret varchar(255) NOT NULL, name varchar(255) NOT NULL, @@ -3041,7 +3041,7 @@ CREATE SEQUENCE system_oauth2_client_seq DROP TABLE IF EXISTS system_oauth2_code; CREATE TABLE system_oauth2_code ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_oauth2_code_seq'), user_id int8 NOT NULL, user_type int2 NOT NULL, code varchar(32) NOT NULL, @@ -3088,7 +3088,7 @@ CREATE SEQUENCE system_oauth2_code_seq DROP TABLE IF EXISTS system_oauth2_refresh_token; CREATE TABLE system_oauth2_refresh_token ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_oauth2_refresh_token_seq'), user_id int8 NOT NULL, refresh_token varchar(32) NOT NULL, user_type int2 NOT NULL, @@ -3131,7 +3131,7 @@ CREATE SEQUENCE system_oauth2_refresh_token_seq DROP TABLE IF EXISTS system_operate_log; CREATE TABLE system_operate_log ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_operate_log_seq'), trace_id varchar(64) NOT NULL DEFAULT '', user_id int8 NOT NULL, user_type int2 NOT NULL DEFAULT 0, @@ -3188,7 +3188,7 @@ CREATE SEQUENCE system_operate_log_seq DROP TABLE IF EXISTS system_post; CREATE TABLE system_post ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_post_seq'), code varchar(64) NOT NULL, name varchar(50) NOT NULL, sort int4 NOT NULL, @@ -3241,7 +3241,7 @@ CREATE SEQUENCE system_post_seq DROP TABLE IF EXISTS system_role; CREATE TABLE system_role ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_role_seq'), name varchar(30) NOT NULL, code varchar(100) NOT NULL, sort int4 NOT NULL, @@ -3304,7 +3304,7 @@ CREATE SEQUENCE system_role_seq DROP TABLE IF EXISTS system_role_menu; CREATE TABLE system_role_menu ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_role_menu_seq'), role_id int8 NOT NULL, menu_id int8 NOT NULL, creator varchar(64) NULL DEFAULT '', @@ -4210,7 +4210,7 @@ CREATE SEQUENCE system_role_menu_seq DROP TABLE IF EXISTS system_sms_channel; CREATE TABLE system_sms_channel ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_sms_channel_seq'), signature varchar(12) NOT NULL, code varchar(63) NOT NULL, status int2 NOT NULL, @@ -4264,7 +4264,7 @@ CREATE SEQUENCE system_sms_channel_seq DROP TABLE IF EXISTS system_sms_code; CREATE TABLE system_sms_code ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_sms_code_seq'), mobile varchar(11) NOT NULL, code varchar(6) NOT NULL, create_ip varchar(15) NOT NULL, @@ -4313,7 +4313,7 @@ CREATE SEQUENCE system_sms_code_seq DROP TABLE IF EXISTS system_sms_log; CREATE TABLE system_sms_log ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_sms_log_seq'), channel_id int8 NOT NULL, channel_code varchar(63) NOT NULL, template_id int8 NOT NULL, @@ -4384,7 +4384,7 @@ CREATE SEQUENCE system_sms_log_seq DROP TABLE IF EXISTS system_sms_template; CREATE TABLE system_sms_template ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_sms_template_seq'), type int2 NOT NULL, status int2 NOT NULL, code varchar(63) NOT NULL, @@ -4456,7 +4456,7 @@ CREATE SEQUENCE system_sms_template_seq DROP TABLE IF EXISTS system_social_client; CREATE TABLE system_social_client ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_social_client_seq'), name varchar(255) NOT NULL, social_type int2 NOT NULL, user_type int2 NOT NULL, @@ -4514,7 +4514,7 @@ CREATE SEQUENCE system_social_client_seq DROP TABLE IF EXISTS system_social_user; CREATE TABLE system_social_user ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_social_user_seq'), type int2 NOT NULL, openid varchar(32) NOT NULL, token varchar(256) NULL DEFAULT NULL, @@ -4563,7 +4563,7 @@ CREATE SEQUENCE system_social_user_seq DROP TABLE IF EXISTS system_social_user_bind; CREATE TABLE system_social_user_bind ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_social_user_bind_seq'), user_id int8 NOT NULL, user_type int2 NOT NULL, social_type int2 NOT NULL, @@ -4602,7 +4602,7 @@ CREATE SEQUENCE system_social_user_bind_seq DROP TABLE IF EXISTS system_tenant; CREATE TABLE system_tenant ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_tenant_seq'), name varchar(30) NOT NULL, contact_user_id int8 NULL DEFAULT NULL, contact_name varchar(30) NOT NULL, @@ -4660,7 +4660,7 @@ CREATE SEQUENCE system_tenant_seq DROP TABLE IF EXISTS system_tenant_package; CREATE TABLE system_tenant_package ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_tenant_package_seq'), name varchar(30) NOT NULL, status int2 NOT NULL DEFAULT 0, remark varchar(256) NULL DEFAULT '', @@ -4707,7 +4707,7 @@ CREATE SEQUENCE system_tenant_package_seq DROP TABLE IF EXISTS system_user_post; CREATE TABLE system_user_post ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_user_post_seq'), user_id int8 NOT NULL DEFAULT 0, post_id int8 NOT NULL DEFAULT 0, creator varchar(64) NULL DEFAULT '', @@ -4759,7 +4759,7 @@ CREATE SEQUENCE system_user_post_seq DROP TABLE IF EXISTS system_user_role; CREATE TABLE system_user_role ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_user_role_seq'), user_id int8 NOT NULL, role_id int8 NOT NULL, creator varchar(64) NULL DEFAULT '', @@ -4819,7 +4819,7 @@ CREATE SEQUENCE system_user_role_seq DROP TABLE IF EXISTS system_users; CREATE TABLE system_users ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('system_users_seq'), username varchar(30) NOT NULL, password varchar(100) NOT NULL DEFAULT '', nickname varchar(30) NOT NULL, @@ -4902,7 +4902,7 @@ CREATE SEQUENCE system_users_seq DROP TABLE IF EXISTS yudao_demo01_contact; CREATE TABLE yudao_demo01_contact ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('yudao_demo01_contact_seq'), name varchar(100) NOT NULL DEFAULT '', sex int2 NOT NULL, birthday timestamp NOT NULL, @@ -4952,7 +4952,7 @@ CREATE SEQUENCE yudao_demo01_contact_seq DROP TABLE IF EXISTS yudao_demo02_category; CREATE TABLE yudao_demo02_category ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('yudao_demo02_category_seq'), name varchar(100) NOT NULL DEFAULT '', parent_id int8 NOT NULL, creator varchar(64) NULL DEFAULT '', @@ -5001,7 +5001,7 @@ CREATE SEQUENCE yudao_demo02_category_seq DROP TABLE IF EXISTS yudao_demo03_course; CREATE TABLE yudao_demo03_course ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('yudao_demo03_course_seq'), student_id int8 NOT NULL, name varchar(100) NOT NULL DEFAULT '', score int2 NOT NULL, @@ -5063,7 +5063,7 @@ CREATE SEQUENCE yudao_demo03_course_seq DROP TABLE IF EXISTS yudao_demo03_grade; CREATE TABLE yudao_demo03_grade ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('yudao_demo03_grade_seq'), student_id int8 NOT NULL, name varchar(100) NOT NULL DEFAULT '', teacher varchar(255) NOT NULL, @@ -5111,7 +5111,7 @@ CREATE SEQUENCE yudao_demo03_grade_seq DROP TABLE IF EXISTS yudao_demo03_student; CREATE TABLE yudao_demo03_student ( - id int8 NOT NULL, + id int8 NOT NULL default nextval('yudao_demo03_student_seq'), name varchar(100) NOT NULL DEFAULT '', sex int2 NOT NULL, birthday timestamp NOT NULL, diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java index 47a4d2d71..612a91338 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java @@ -40,6 +40,7 @@ public enum AiPlatformEnum implements ArrayValuable { STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI MIDJOURNEY("Midjourney", "Midjourney"), // Midjourney SUNO("Suno", "Suno"), // Suno AI + GROK("Grok","Grok"), // Grok ; diff --git a/yudao-module-ai/yudao-module-ai-server/pom.xml b/yudao-module-ai/yudao-module-ai-server/pom.xml index a2ce08ba1..d40ba9247 100644 --- a/yudao-module-ai/yudao-module-ai-server/pom.xml +++ b/yudao-module-ai/yudao-module-ai-server/pom.xml @@ -19,9 +19,9 @@ 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno - 1.0.1 - 1.0.0.3 - 1.0.2 + 1.1.0 + 1.0.0.4 + 1.2.6 diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java index 26fbe0ad4..9009cbc8c 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl; import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel; +import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; @@ -16,7 +17,9 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatMod import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient; import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient; import cn.iocoder.yudao.module.ai.tool.method.PersonService; +import io.micrometer.observation.ObservationRegistry; import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.ai.deepseek.DeepSeekChatOptions; import org.springframework.ai.deepseek.api.DeepSeekApi; @@ -34,12 +37,14 @@ import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClie import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties; import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties; import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; +import java.util.Optional; /** * 芋道 AI 自动配置 @@ -60,6 +65,13 @@ public class AiAutoConfiguration { return new AiModelFactoryImpl(); } + @Bean + @ConditionalOnMissingBean + public ObservationRegistry observationRegistry() { + // 特殊:兜底有 ObservationRegistry Bean,避免相关的 ChatModel 创建报错。相关 issue:https://t.zsxq.com/CuPu4 + return ObservationRegistry.NOOP; + } + // ========== 各种 AI Client 创建 ========== @Bean @@ -252,6 +264,28 @@ public class AiAutoConfiguration { return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl()); } + public ChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(GrokChatModel.MODEL_DEFAULT); + } + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(Optional.ofNullable(properties.getBaseUrl()) + .orElse(GrokChatModel.BASE_URL)) + .completionsPath(GrokChatModel.COMPLETE_PATH) + .apiKey(properties.getApiKey()) + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model(properties.getModel()) + .temperature(properties.getTemperature()) + .maxTokens(properties.getMaxTokens()) + .topP(properties.getTopP()) + .build()) + .toolCallingManager(getToolCallingManager()) + .build(); + return new DouBaoChatModel(openAiChatModel); + } + // ========== RAG 相关 ========== @Bean diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java index 67d3bb5f3..986c24c18 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java @@ -160,6 +160,20 @@ public class YudaoAiProperties { } + @Data + public static class Grok { + + private String enable; + private String apiKey; + private String baseUrl; + + private String model; + private Double temperature; + private Integer maxTokens; + private Double topP; + + } + @Data public static class WebSearch { diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java index 75798ebd2..f8067dea2 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java @@ -87,7 +87,7 @@ import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiImageAutoConfig import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaEmbeddingModel; import org.springframework.ai.ollama.api.OllamaApi; -import org.springframework.ai.ollama.api.OllamaOptions; +import org.springframework.ai.ollama.api.OllamaEmbeddingOptions; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.OpenAiEmbeddingOptions; @@ -178,6 +178,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return buildGeminiChatModel(apiKey); case OLLAMA: return buildOllamaChatModel(url); + case GROK: + return buildGrokChatModel(apiKey,url); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } @@ -436,10 +438,12 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link ZhiPuAiChatAutoConfiguration} 的 zhiPuAiChatModel 方法 */ private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { - ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) - : new ZhiPuAiApi(url, apiKey); + ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey); + if (StrUtil.isNotEmpty(url)) { + zhiPuAiApiBuilder.baseUrl(url); + } ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); - return new ZhiPuAiChatModel(zhiPuAiApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE, + return new ZhiPuAiChatModel(zhiPuAiApiBuilder.build(), options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE, getObservationRegistry().getIfAvailable()); } @@ -586,6 +590,13 @@ public class AiModelFactoryImpl implements AiModelFactory { return new StabilityAiImageModel(stabilityAiApi); } + private ChatModel buildGrokChatModel(String apiKey,String url) { + YudaoAiProperties.Grok properties = new YudaoAiProperties.Grok() + .setBaseUrl(url) + .setApiKey(apiKey); + return new AiAutoConfiguration().buildGrokChatClient(properties); + } + // ========== 各种创建 EmbeddingModel 的方法 ========== /** @@ -601,10 +612,12 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link ZhiPuAiEmbeddingAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 */ private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { - ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) - : new ZhiPuAiApi(url, apiKey); + ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey); + if (StrUtil.isNotEmpty(url)) { + zhiPuAiApiBuilder.baseUrl(url); + } ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build(); - return new ZhiPuAiEmbeddingModel(zhiPuAiApi, MetadataMode.EMBED, zhiPuAiEmbeddingOptions); + return new ZhiPuAiEmbeddingModel(zhiPuAiApiBuilder.build(), MetadataMode.EMBED, zhiPuAiEmbeddingOptions); } /** @@ -632,7 +645,7 @@ public class AiModelFactoryImpl implements AiModelFactory { private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); - OllamaOptions ollamaOptions = OllamaOptions.builder().model(model).build(); + OllamaEmbeddingOptions ollamaOptions = OllamaEmbeddingOptions.builder().model(model).build(); return OllamaEmbeddingModel.builder() .ollamaApi(ollamaApi) .defaultOptions(ollamaOptions) diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java new file mode 100644 index 000000000..06eed2504 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.ai.framework.ai.core.model.grok; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import reactor.core.publisher.Flux; + +/** + * Grok {@link ChatModel} 实现类 + * + * + */ +@Slf4j +@RequiredArgsConstructor +public class GrokChatModel implements ChatModel { + + public static final String BASE_URL = "https://api.x.ai"; + public static final String COMPLETE_PATH = "/v1/chat/completions"; + public static final String MODEL_DEFAULT = "grok-4-fast-reasoning"; + + /** + * 兼容 OpenAI 接口,进行复用 + */ + private final ChatModel openAiChatModel; + + @Override + public ChatResponse call(Prompt prompt) { + return openAiChatModel.call(prompt); + } + + @Override + public Flux stream(Prompt prompt) { + return openAiChatModel.stream(prompt); + } + + @Override + public ChatOptions getDefaultOptions() { + return openAiChatModel.getDefaultOptions(); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java index 0e7a8ad45..aedd493c3 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java @@ -3,7 +3,8 @@ package cn.iocoder.yudao.module.ai.framework.security.config; import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; import cn.iocoder.yudao.module.infra.enums.ApiConstants; import jakarta.annotation.Resource; -import org.springframework.ai.mcp.server.autoconfigure.McpServerProperties; +import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerSseProperties; +import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -18,7 +19,9 @@ import java.util.Optional; public class SecurityConfiguration { @Resource - private Optional serverProperties; + private Optional mcpServerSseProperties; + @Resource + private Optional mcpServerStreamableHttpProperties; @Bean("aiAuthorizeRequestsCustomizer") public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { @@ -42,10 +45,12 @@ public class SecurityConfiguration { registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); // MCP Server - serverProperties.ifPresent(properties -> { + mcpServerSseProperties.ifPresent(properties -> { registry.requestMatchers(properties.getSseEndpoint()).permitAll(); registry.requestMatchers(properties.getSseMessageEndpoint()).permitAll(); }); + mcpServerStreamableHttpProperties.ifPresent(properties -> + registry.requestMatchers(properties.getMcpEndpoint()).permitAll()); } }; diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java index d209c62d4..dab4d5aa4 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java @@ -16,7 +16,7 @@ import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.deepseek.DeepSeekAssistantMessage; import org.springframework.ai.deepseek.DeepSeekChatOptions; import org.springframework.ai.minimax.MiniMaxChatOptions; -import org.springframework.ai.ollama.api.OllamaOptions; +import org.springframework.ai.ollama.api.OllamaChatOptions; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; @@ -68,6 +68,7 @@ public class AiUtils { case OPENAI: case GEMINI: // 复用 OpenAI 客户端 case BAI_CHUAN: // 复用 OpenAI 客户端 + case GROK: // 复用 OpenAI 客户端 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); case AZURE_OPENAI: @@ -77,7 +78,7 @@ public class AiUtils { return AnthropicChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); case OLLAMA: - return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) + return OllamaChatOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) .toolCallbacks(toolCallbacks).toolContext(toolContext).build(); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml index 98c2bf95f..236eb8e6d 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml +++ b/yudao-module-ai/yudao-module-ai-server/src/main/resources/application.yaml @@ -168,6 +168,8 @@ spring: filesystem: url: http://127.0.0.1:8089 sse-endpoint: /sse + annotation-scanner: + enabled: false # TODO @芋艿:有 bug https://github.com/spring-projects/spring-ai/issues/4917 需要官方修复 yudao: ai: diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index d69205ac6..58a66ea6b 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -235,6 +235,8 @@ spring: filesystem: url: http://127.0.0.1:8089 sse-endpoint: /sse + annotation-scanner: + enabled: false # TODO @芋艿:有 bug https://github.com/spring-projects/spring-ai/issues/4917 需要官方修复 yudao: ai: From d0d563ae6135777723093d1be64b8fff305b1949 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 24 Nov 2025 11:11:40 +0800 Subject: [PATCH 12/14] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT?= =?UTF-8?q?=20=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=88AI?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/service/chat/AiChatMessageServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 0f44eacbf..f29a5c3f9 100644 --- a/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -49,7 +49,7 @@ import org.springframework.ai.chat.model.StreamingChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.mcp.SyncMcpToolCallbackProvider; -import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties; +import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.resolution.ToolCallbackResolver; import org.springframework.beans.factory.annotation.Autowired; From 187148ee8bceff9d25165112aeb10a06b1974110 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 24 Nov 2025 11:18:53 +0800 Subject: [PATCH 13/14] =?UTF-8?q?fix:=20=E3=80=90framework=E3=80=91Timesta?= =?UTF-8?q?mpLocalDateTimeSerializer=20=E4=B8=AD=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20ReflectUtil=20=E6=9B=BF=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/json/databind/TimestampLocalDateTimeSerializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java index 12354256e..bed47e93b 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.framework.common.util.json.databind; +import cn.hutool.core.util.ReflectUtil; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import org.apache.commons.lang3.reflect.FieldUtils; import java.io.IOException; import java.lang.reflect.Field; @@ -25,7 +25,7 @@ public class TimestampLocalDateTimeSerializer extends JsonSerializer clazz = gen.getOutputContext().getCurrentValue().getClass(); - Field field = FieldUtils.getField(clazz, fieldName, true); + Field field = ReflectUtil.getField(clazz, fieldName); // 情况一:有 JsonFormat 自定义注解,则使用它。https://github.com/YunaiV/ruoyi-vue-pro/pull/1019 JsonFormat[] jsonFormats = field.getAnnotationsByType(JsonFormat.class); if (jsonFormats.length > 0) { From 3c1748aff13402e069ce24d73da17d4e05d7d9ea Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 24 Nov 2025 11:24:52 +0800 Subject: [PATCH 14/14] =?UTF-8?q?!208=20=E4=BD=BF=E7=94=A8=E5=A4=96?= =?UTF-8?q?=E9=83=A8=E4=B8=AD=E9=97=B4=E4=BB=B6=E6=97=B6=EF=BC=8C=E5=A6=82?= =?UTF-8?q?=EF=BC=9A=E9=87=91=E8=9D=B6=E3=80=81=E4=B8=9C=E6=96=B9=E9=80=9A?= =?UTF-8?q?=20=E5=9C=A8=E4=B8=8D=E9=87=8D=E5=90=AF=E6=95=B4=E4=B8=AA?= =?UTF-8?q?=E4=B8=AD=E9=97=B4=E4=BB=B6=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B?= =?UTF-8?q?=EF=BC=8C=E4=BA=8C=E6=AC=A1=E9=83=A8=E7=BD=B2=E6=88=96=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E6=9C=8D=E5=8A=A1=E5=90=8C=E6=97=B6=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E5=9C=A8=E4=B8=80=E4=B8=AA=E8=99=9A=E6=8B=9F=E6=9C=BA=E4=B8=8B?= =?UTF-8?q?=EF=BC=88JVM=EF=BC=89=20IdTypeEnvir=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/IdTypeEnvironmentPostProcessor.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java index 3a67b905f..0ea6bf252 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/IdTypeEnvironmentPostProcessor.java @@ -9,7 +9,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -56,11 +59,19 @@ public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor } public IdType getIdType(ConfigurableEnvironment environment) { - return environment.getProperty(ID_TYPE_KEY, IdType.class); + String value = environment.getProperty(ID_TYPE_KEY); + try { + return StrUtil.isNotBlank(value) ? IdType.valueOf(value) : IdType.NONE; + } catch (IllegalArgumentException ex) { + log.error("[getIdType][无法解析 id-type 配置值({})]", value, ex); + return IdType.NONE; + } } public void setIdType(ConfigurableEnvironment environment, IdType idType) { - environment.getSystemProperties().put(ID_TYPE_KEY, idType); + Map map = new HashMap<>(); + map.put(ID_TYPE_KEY, idType); + environment.getPropertySources().addFirst(new MapPropertySource("mybatisPlusIdType", map)); log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType); }