diff --git a/hiver-admin/pom.xml b/hiver-admin/pom.xml index d4991a66..2169fd11 100644 --- a/hiver-admin/pom.xml +++ b/hiver-admin/pom.xml @@ -16,37 +16,30 @@ cc.hiver hiver-base - 1.0-SNAPSHOT cc.hiver hiver-file - 1.0-SNAPSHOT cc.hiver hiver-quartz - 1.0-SNAPSHOT cc.hiver hiver-social - 1.0-SNAPSHOT cc.hiver hiver-app - 1.0-SNAPSHOT cc.hiver hiver-open - 1.0-SNAPSHOT cc.hiver hiver-mall - 1.0-SNAPSHOT @@ -83,7 +76,6 @@ org.springframework.boot spring-boot-maven-plugin - 2.3.7.RELEASE diff --git a/hiver-admin/src/main/resources/application.yml b/hiver-admin/src/main/resources/application.yml index dfcde390..28c298a9 100644 --- a/hiver-admin/src/main/resources/application.yml +++ b/hiver-admin/src/main/resources/application.yml @@ -34,6 +34,8 @@ spring: driver-class-name: com.mysql.jdbc.Driver # Druid StatViewServlet配置 druid: + test-on-borrow: true + validation-query: SELECT 1 stat-view-servlet: # 默认true 内置监控页面首页/druid/index.html enabled: true @@ -97,11 +99,11 @@ spring: listener: simple: # 手动确认消息 - acknowledge-mode: manual + acknowledge-mode: AUTO # 开启重试 retry: enabled: true - max-attempts: 3 + max-attempts: 1 template: retry: enabled: true diff --git a/hiver-core/pom.xml b/hiver-core/pom.xml index 9bea1c3b..5c44166f 100644 --- a/hiver-core/pom.xml +++ b/hiver-core/pom.xml @@ -158,7 +158,6 @@ com.alibaba fastjson - 1.2.62 com.aliyun @@ -173,8 +172,6 @@ com.github.binarywang weixin-java-mp - 4.6.0 - compile \ No newline at end of file diff --git a/hiver-modules/hiver-app/pom.xml b/hiver-modules/hiver-app/pom.xml index a1eacf29..d7a354e5 100644 --- a/hiver-modules/hiver-app/pom.xml +++ b/hiver-modules/hiver-app/pom.xml @@ -16,7 +16,6 @@ cn.dev33 sa-token-spring-boot-starter - 1.26.0 @@ -27,12 +26,10 @@ ch.ethz.ganymed ganymed-ssh2 - build210 com.jcraft jsch - 0.1.55 \ No newline at end of file diff --git a/hiver-modules/hiver-base/pom.xml b/hiver-modules/hiver-base/pom.xml index e53e2d71..d3af6ef2 100644 --- a/hiver-modules/hiver-base/pom.xml +++ b/hiver-modules/hiver-base/pom.xml @@ -13,20 +13,16 @@ cc.hiver hiver-mall - 1.0-SNAPSHOT - compile org.apache.httpcomponents httpclient - 4.5.13 com.github.binarywang weixin-java-mp - 4.6.0 diff --git a/hiver-modules/hiver-mall/pom.xml b/hiver-modules/hiver-mall/pom.xml index 09424a86..a452f94e 100644 --- a/hiver-modules/hiver-mall/pom.xml +++ b/hiver-modules/hiver-mall/pom.xml @@ -15,7 +15,6 @@ cc.hiver hiver-app - 1.0-SNAPSHOT com.alibaba @@ -31,8 +30,6 @@ cc.hiver hiver-file - 1.0-SNAPSHOT - compile org.springframework.boot @@ -41,7 +38,6 @@ com.alibaba fastjson - 1.2.83 diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallCouponController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallCouponController.java index 3ab39cd5..e6e3426f 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallCouponController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallCouponController.java @@ -4,18 +4,24 @@ import cc.hiver.core.common.utils.ResultUtil; import cc.hiver.core.common.vo.Result; import cc.hiver.mall.entity.MallCoupon; import cc.hiver.mall.entity.MallUserCoupon; +import cc.hiver.mall.mq.CouponMqConfig; import cc.hiver.mall.pojo.query.MallCouponQuery; import cc.hiver.mall.service.mybatis.MallCouponService; import cc.hiver.mall.service.mybatis.MallUserCouponService; +import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; import java.util.List; +@Slf4j @RestController @RequestMapping("/hiver/mall/coupon") @Api(tags = "优惠券接口") @@ -27,6 +33,17 @@ public class MallCouponController { @Autowired private MallUserCouponService mallUserCouponService; + @Autowired + private RabbitTemplate rabbitTemplate; + + @Data + public static class SendCouponReq { + private String userPhones; + private Integer giveNum; + private Integer type; + private String couponId; + } + @PostMapping("/add") @ApiOperation(value = "添加/发行优惠券") public Result addCoupon(@RequestBody MallCoupon coupon) { @@ -51,8 +68,20 @@ public class MallCouponController { @PostMapping("/send") @ApiOperation(value = "平台发放优惠券") public Result send(@RequestParam String userPhones,@RequestParam Integer giveNum, @RequestParam Integer type,@RequestParam String couponId) { - mallUserCouponService.send(userPhones, type,couponId, giveNum); - return ResultUtil.success("发放成功"); + try { + SendCouponReq req = new SendCouponReq(); + req.setUserPhones(userPhones); + req.setGiveNum(giveNum); + req.setType(type); + req.setCouponId(couponId); + + String message = JSON.toJSONString(req); + rabbitTemplate.convertAndSend(CouponMqConfig.COUPON_EXCHANGE, CouponMqConfig.COUPON_SEND_ROUTING_KEY, message); + return ResultUtil.success("发放任务已投递,后台排队处理中"); + } catch (Exception e) { + log.error("推送优惠券发放MQ消息失败: {}", e.getMessage(), e); + return ResultUtil.error("发放任务投递失败: " + e.getMessage()); + } } @GetMapping("/available") diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java index db2b5262..e71408ae 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java @@ -68,6 +68,7 @@ public class MallDeliveryOrderController { result.put("size", page.getSize()); result.put("current", page.getCurrent()); result.put("pages", page.getPages()); + result.put("zhipaiCount", page.getTotal()); //外卖、快递、跑腿待接数 result.put("orderCount", mallDeliveryOrderService.countOrdersByType(q.getRegionId())); return new ResultUtil().setData(result); @@ -273,7 +274,7 @@ public class MallDeliveryOrderController { public Result addDeliveryFee(@RequestParam String orderId,@RequestParam String deliveryId, @RequestParam BigDecimal additionalFee,@RequestParam BigDecimal olditionalFee) { try { - mallDeliveryOrderService.addDeliveryFee(orderId,deliveryId, additionalFee,olditionalFee); + //mallDeliveryOrderService.addDeliveryFee(orderId,deliveryId, additionalFee,olditionalFee); return ResultUtil.success("配送费增加成功"); } catch (Exception e) { log.error("增加配送费失败: {}", e.getMessage(), e); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java index 6ea6e6b9..66a66d23 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java @@ -250,8 +250,8 @@ public class ShopController { continue; } } - - if(s.getStatus() != 1){ + //已禁用 + if(s.getStatus() < 1){ continue; } if (CharSequenceUtil.isNotBlank(shop.getShopType())) { @@ -306,11 +306,19 @@ public class ShopController { compareResult = shoprank1.compareTo(shoprank2); } if ("desc".equalsIgnoreCase(order)) { - return -compareResult; + compareResult = -compareResult; } return compareResult; }); } + + // 稳定排序:因为 1 是正常营业,2 是暂停营业,所以使用升序排序 (1 -> 2) + // 这样既保留了前面销量/评分的排序,又能确保暂停营业的(2)排在最后 + allShops.sort((s1, s2) -> { + Integer status1 = s1.getStatus() == null ? 0 : s1.getStatus(); + Integer status2 = s2.getStatus() == null ? 0 : s2.getStatus(); + return status1.compareTo(status2); + }); int pageNumber = pageVo.getPageNumber() > 0 ? pageVo.getPageNumber() : 1; int pageSize = pageVo.getPageSize() > 0 ? pageVo.getPageSize() : 10; @@ -361,7 +369,6 @@ public class ShopController { return new ResultUtil>().setData(pageResult); } } - final Page page = shopService.findByCondition(shop, PageUtil.initPage(pageVo)); final List shopIdList = new ArrayList<>(); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java index 8f0128e5..6e5f244c 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java @@ -27,6 +27,7 @@ import javax.annotation.PostConstruct; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.util.Base64; @@ -94,8 +95,15 @@ public class WechatPayController { @PostMapping("/unified-order") public ResponseEntity> jsapiPay(@RequestBody Map request) throws Exception { - String outTradeNo = "ORDER_" + request.get("outTradeNo"); + String description = request.get("description"); + String outTradeNo = request.get("outTradeNo"); + if(description.equals("增加配送佣金")){ + outTradeNo = "ORDERDE_" + outTradeNo; + }else{ + outTradeNo = "ORDER_" + outTradeNo; + } + String openid = request.get("openid"); long amount = Long.parseLong(request.get("amount")); // 单位:分 @@ -115,14 +123,64 @@ public class WechatPayController { } } - @PostMapping("/paySuccess") - public ResponseEntity> paySuccess(@RequestBody Map request) throws Exception { - //订单支付成功逻辑 - mallOrderService.paySuccess(request.get("outTradeNo")); - Map payParams = new HashMap<>(); - payParams.put("code","200"); - payParams.put("message","支付成功订单更新"); - return ResponseEntity.ok(payParams); + /** + * 微信支付异步回调通知接口 (API v3) + * 微信服务器会在支付成功后 POST 请求该接口 + */ + @PostMapping("/notify") + public ResponseEntity> payNotify(@RequestBody Map request) { + Map result = new HashMap<>(); + try { + String eventType = (String) request.get("event_type"); + log.info("接收到微信支付回调,事件类型:{}", eventType); + + if ("TRANSACTION.SUCCESS".equals(eventType)) { + Map resource = (Map) request.get("resource"); + String ciphertext = (String) resource.get("ciphertext"); + String nonce = (String) resource.get("nonce"); + String associatedData = (String) resource.get("associated_data"); + + // 使用 APIv3 密钥解密 + String decryptedData = KeyUtils.decryptToString(associatedData, nonce, ciphertext, config.getApiV3Key()); + log.info("微信支付回调解密成功"); + + Map dataMap = objectMapper.readValue(decryptedData, Map.class); + log.info(decryptedData); + String tradeState = (String) dataMap.get("trade_state"); + + if ("SUCCESS".equals(tradeState)) { + String outTradeNo = (String) dataMap.get("out_trade_no"); + Map amountMap = (Map) dataMap.get("amount"); + if (amountMap == null) { + throw new RuntimeException("支付回调金额信息缺失"); + } + // 获取分单位的金额 + Integer totalFeeCents = (Integer) amountMap.get("total"); + // 转换为元 (如果需要) 或者直接用分存数据库 + BigDecimal totalAmount = new BigDecimal(totalFeeCents).divide(new BigDecimal(100)); + String orderId = outTradeNo; + // 发起支付时统一下单加了 "ORDER_" 前缀,所以这里截取回真实的 orderId + /*if (outTradeNo != null && outTradeNo.startsWith("ORDER_")) { + orderId = outTradeNo.substring(6); + }*/ + // 更新数据库订单状态 + mallOrderService.paySuccess(orderId,totalAmount); + log.info("订单支付成功状态更新完毕,orderId: {}", orderId); + } + } + + // 微信支付 V3 明确要求成功时返回 code: SUCCESS + result.put("code", "SUCCESS"); + result.put("message", "成功"); + return ResponseEntity.ok(result); + + } catch (Exception e) { + log.error("处理微信支付回调异常", e); + result.put("code", "FAIL"); + result.put("message", "业务处理异常: " + e.getMessage()); + // 如果返回非 2xx 状态码,微信会按照退避策略重新通知 + return ResponseEntity.status(500).body(result); + } } public Map buildPaySign(String prepayId) throws Exception { diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerController.java index e550af94..478d0f49 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerController.java @@ -202,7 +202,7 @@ public class WorkerController { */ @RequestMapping(value = "/onLine", method = RequestMethod.POST) @ApiOperation(value = "配送员上线") - public Result onLine(String id) { + public Result onLine(@RequestParam("id") String id) { try { workerService.onLine(id); return ResultUtil.success("上线成功"); @@ -222,7 +222,7 @@ public class WorkerController { */ @RequestMapping(value = "/offLine", method = RequestMethod.POST) @ApiOperation(value = "配送员下线") - public Result offLine(String id) { + public Result offLine(@RequestParam("id")String id) { try { workerService.offLine(id); return ResultUtil.success("下线成功"); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java index dca91dfb..a7d58e52 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java @@ -89,8 +89,8 @@ public class Shop extends HiverBaseEntity { @ApiModelProperty(value = "截止日") private String endTime; - @ApiModelProperty(value = "状态") - private int status; + @ApiModelProperty(value = "状态 0禁用 1启用 2暂停营业") + private Integer status; @ApiModelProperty(value = "备注") private String remark; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/CouponMqConfig.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/CouponMqConfig.java new file mode 100644 index 00000000..84e1ffea --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/CouponMqConfig.java @@ -0,0 +1,34 @@ +package cc.hiver.mall.mq; + +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.DirectExchange; +import org.springframework.amqp.core.Queue; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 优惠券MQ配置 + */ +@Configuration +public class CouponMqConfig { + + public static final String COUPON_EXCHANGE = "mall.coupon.exchange"; + public static final String COUPON_SEND_QUEUE = "mall.coupon.send.queue"; + public static final String COUPON_SEND_ROUTING_KEY = "mall.coupon.send.routing.key"; + + @Bean + public DirectExchange couponExchange() { + return new DirectExchange(COUPON_EXCHANGE, true, false); + } + + @Bean + public Queue couponSendQueue() { + return new Queue(COUPON_SEND_QUEUE, true); + } + + @Bean + public Binding bindingCouponSendQueue() { + return BindingBuilder.bind(couponSendQueue()).to(couponExchange()).with(COUPON_SEND_ROUTING_KEY); + } +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/CouponSendConsumer.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/CouponSendConsumer.java new file mode 100644 index 00000000..5b10b0fd --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/CouponSendConsumer.java @@ -0,0 +1,39 @@ +package cc.hiver.mall.mq; + +import cc.hiver.mall.controller.MallCouponController.SendCouponReq; +import cc.hiver.mall.service.mybatis.MallUserCouponService; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 优惠券发放MQ消费者 + */ +@Slf4j +@Component +public class CouponSendConsumer { + + @Autowired + private MallUserCouponService mallUserCouponService; + + @RabbitListener(queues = CouponMqConfig.COUPON_SEND_QUEUE) + public void handleCouponSend(String message) { + log.info("【MQ优惠券消费者】接收到优惠券发放消息: {}", message); + try { + SendCouponReq req = JSON.parseObject(message, SendCouponReq.class); + if (req == null || req.getUserPhones() == null || req.getCouponId() == null) { + log.warn("【MQ优惠券消费者】消息参数异常, message={}", message); + return; + } + + mallUserCouponService.send(req.getUserPhones(), req.getType(), req.getCouponId(), req.getGiveNum()); + log.info("【MQ优惠券消费者】优惠券发放处理成功, 手机号: {}", req.getUserPhones()); + + } catch (Exception e) { + log.error("【MQ优惠券消费者】处理优惠券发放消息异常: {}", e.getMessage(), e); + throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(e); + } + } +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java index b89de096..0c9e6b51 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java @@ -115,6 +115,7 @@ public class OrderAsyncConsumer { log.info("【异步队列处理】微信模版推送成功,推送总人数:{}", targetOpenIds.size()); } catch (Exception e) { log.error("【异步队列处理】微信推送异常(不影响主链路): {}", e.getMessage(), e); + throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(e); } } @@ -152,6 +153,7 @@ public class OrderAsyncConsumer { mallOrderService.updateSaleCacheIncrementalViaMq(shopId, regionId, items, shopSaleCount); } catch (Exception e) { log.error("【异步队列处理】缓存更新失败: {}", e.getMessage(), e); + throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(e); } } } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java index 25a91c0f..621f8ec6 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java @@ -67,6 +67,7 @@ public class OrderDelayConsumer { } } catch (Exception e) { log.error("【订单延时处理】处理超时消息异常: {}", e.getMessage(), e); + throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(e); } } @@ -123,7 +124,7 @@ public class OrderDelayConsumer { Shop shop = shopMapper.selectById(order.getShopId()); if (shop != null && StringUtils.isNotBlank(shop.getClientId())) { // 发送给商家端的 APP Push - jPushService.sendPushNotification(shop.getClientId(), "您的订单出餐已超时,请尽快处理!", orderId); + jPushService.sendPushNotification(shop.getClientId(), order.getNumberCode() + "订单出餐已超时,请尽快处理!", orderId); } } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/SettlementConfirmConsumer.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/SettlementConfirmConsumer.java index 4a1dba57..34979ad9 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/SettlementConfirmConsumer.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/SettlementConfirmConsumer.java @@ -56,8 +56,7 @@ public class SettlementConfirmConsumer { } catch (Exception e) { log.error("【MQ结算消费者】处理结算消息异常: {}", e.getMessage(), e); - // 此处由于是简单的配置,如果出现异常会根据 application.yml 中的重试机制进行重试(max-attempts: 3) - throw new RuntimeException(e); // 抛出异常以触发重试或放入死信队列 + throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(e); // 拒绝且不重新入队 } } } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java index 196249a5..5a8b981f 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java @@ -67,10 +67,9 @@ public interface MallDeliveryOrderService extends IService { /** * 用户增加配送费(在现有 deliveryFee 基础上追加) - * @param deliveryId 配送单ID * @param additionalFee 追加的配送费金额 */ - void addDeliveryFee(String orderId,String deliveryId, BigDecimal additionalFee,BigDecimal olditionalFee); + void addDeliveryFee(MallOrder order,MallDeliveryOrder delivery, BigDecimal additionalFee,BigDecimal olditionalFee); /** * 重新指定配送员(仅待接单状态可操作) diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java index 39abf30f..3bef9f0b 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import org.apache.ibatis.annotations.Param; +import java.math.BigDecimal; import java.util.Map; /** @@ -38,7 +39,7 @@ public interface MallOrderService extends IService { /** * 支付成功处理逻辑 */ - void paySuccess(String orderId); + void paySuccess(String orderId, BigDecimal totalFee); /** * 商家拒单(触发自动退款) diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java index 20421b20..26f75774 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java @@ -45,7 +45,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; @@ -105,6 +107,8 @@ public class ShopServiceImpl implements ShopService { @Override public Page findByCondition(Shop shop, Pageable pageable) { + Sort newSort = Sort.by(Sort.Direction.ASC, "status").and(pageable.getSort()); + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), newSort); return shopDao.findAll(new Specification() { @Nullable @Override diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopTakeawayServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopTakeawayServiceImpl.java index 1d037dd3..c1cf7e8d 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopTakeawayServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopTakeawayServiceImpl.java @@ -16,10 +16,11 @@ limitations under the License. package cc.hiver.mall.serviceimpl; import cc.hiver.mall.dao.mapper.ShopTakeawayMapper; +import cc.hiver.mall.entity.Shop; import cc.hiver.mall.entity.ShopTakeaway; import cc.hiver.mall.pojo.query.ShopTakeawayQuery; -import cc.hiver.mall.service.ShopTakeawayService; import cc.hiver.mall.service.ShopService; +import cc.hiver.mall.service.ShopTakeawayService; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; @@ -74,6 +75,17 @@ public class ShopTakeawayServiceImpl extends ServiceImpl uwOrder = new LambdaUpdateWrapper<>(); - uwOrder.eq(MallOrder::getId, orderId) - .set(MallOrder::getDeliveryFee, newOrderFee); + uwOrder.eq(MallOrder::getId, order.getId()) + .set(MallOrder::getDeliveryFee, newOrderFee).set(MallOrder::getTotalAmount, order.getTotalAmount().add(additionalFee)); mallOrderService.update(uwOrder); LambdaUpdateWrapper uw = new LambdaUpdateWrapper<>(); - uw.eq(MallDeliveryOrder::getId, deliveryId) + uw.eq(MallDeliveryOrder::getId, delivery.getId()) .set(MallDeliveryOrder::getDeliveryFee, newFee); this.update(uw); - log.info("配送单 {} 配送费增加 {},更新后为 {}", deliveryId, additionalFee, newFee); + log.info("配送单 {} 配送费增加 {},更新后为 {}", delivery.getId(), additionalFee, newFee); } /** diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java index 0b1508a4..7cf1c463 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java @@ -13,6 +13,7 @@ import cc.hiver.mall.pojo.query.MallOrderPageQuery; import cc.hiver.mall.pojo.vo.MallOrderVO; import cc.hiver.mall.service.CommentService; import cc.hiver.mall.service.ShopService; +import cc.hiver.mall.service.mybatis.MallDeliveryOrderService; import cc.hiver.mall.service.mybatis.MallOrderGroupService; import cc.hiver.mall.service.mybatis.MallOrderService; import cc.hiver.mall.utils.MerchantOrderSeqUtil; @@ -83,6 +84,9 @@ public class MallOrderServiceImpl extends ServiceImpl 跳过待商家接单,直接进入待配送或待消费 - if (DELIVERY_TYPE_EXPRESS == order.getDeliveryType()) { - updateOrderStatus(orderId, STATUS_WAIT_DELIVERY); - // 激活配送单,状态改为待接单(0) - Date createTime = new Timestamp(System.currentTimeMillis()); - // 1. 获取当前时间 (LocalDateTime) - LocalDateTime now = LocalDateTime.now(); - // 2. 加40分钟 - LocalDateTime futureTime = now.plusMinutes(40); - // 3. 如果需要转回 java.util.Date (为了兼容旧代码) - Date mustFinishTime = Date.from(futureTime.atZone(ZoneId.systemDefault()).toInstant()); - LambdaUpdateWrapper duw = new LambdaUpdateWrapper<>(); - duw.eq(MallDeliveryOrder::getOrderId, orderId) - .set(MallDeliveryOrder::getStatus, 0).set(MallDeliveryOrder::getCreateTime, createTime).set(MallDeliveryOrder::getMustFinishTime, mustFinishTime); - mallDeliveryOrderMapper.update(null, duw); - - // 触发异步推送和延迟监听流程 - triggerDeliveryAsyncEvents(orderId); - } else { - String latestSeq = merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2); - LambdaUpdateWrapper uw = new LambdaUpdateWrapper<>(); - uw.eq(MallOrder::getId, orderId).set(MallOrder::getStatus, STATUS_WAIT_PICKUP).set(MallOrder::getNumberCode, latestSeq); - this.update(uw); + public void paySuccess(String orderId,BigDecimal totalFee) { + if (orderId != null && orderId.startsWith("ORDER_")) { + orderId = orderId.substring(6); + MallOrder order = this.getById(orderId); + if (order == null) throw new RuntimeException("订单不存在"); + if (order.getStatus() != STATUS_WAIT_PAY) { + return; // 已经处理过或状态不对 } - } else if (order.getOrderType() == ORDER_TYPE_GROUP || order.getOrderType() == ORDER_TYPE_FACETOFACE) { - // 拼团订单支付成功 -> 改为待成团 - updateOrderStatus(orderId, STATUS_WAIT_GROUP); - - // 查找属于该订单的拼团并检查成团条件 - LambdaQueryWrapper gq = new LambdaQueryWrapper<>(); - gq.eq(MallOrderGroup::getHeadOrderId, orderId); - MallOrderGroup group = mallOrderGroupMapper.selectOne(gq); - if (group == null) { - // 如果不是团长,按参团人查找 - LambdaQueryWrapper gq2 = new LambdaQueryWrapper<>(); - gq2.eq(MallOrderGroup::getShopId, order.getShopId()) - .like(MallOrderGroup::getGroupOrderIds, order.getId()) - .last("LIMIT 1"); - group = mallOrderGroupMapper.selectOne(gq2); - //更新参团人数+1 - int newMembers = group.getCurrentMembers() + 1; - LambdaUpdateWrapper guw = new LambdaUpdateWrapper<>(); - guw.eq(MallOrderGroup::getId, group.getId()) - .set(MallOrderGroup::getCurrentMembers, newMembers); - mallOrderGroupMapper.update(group,guw); - }else{ - // 团长支付 且不是面对面团 状态改为待成团 - int newMembers = 1; - if(group.getIsFace() != 1){ - LambdaUpdateWrapper guw = new LambdaUpdateWrapper<>(); - guw.eq(MallOrderGroup::getId, group.getId()) - .set(MallOrderGroup::getStatus, GROUP_STATUS_FORMING).set(MallOrderGroup::getCurrentMembers, newMembers); - mallOrderGroupMapper.update(group,guw); - }else{ + + if (order.getOrderType() == ORDER_TYPE_NORMAL) { + // 直购支付成功 -> 跳过待商家接单,直接进入待配送或待消费 + if (DELIVERY_TYPE_EXPRESS == order.getDeliveryType()) { + updateOrderStatus(orderId, STATUS_WAIT_DELIVERY); + // 激活配送单,状态改为待接单(0) + Date createTime = new Timestamp(System.currentTimeMillis()); + // 1. 获取当前时间 (LocalDateTime) + LocalDateTime now = LocalDateTime.now(); + // 2. 加40分钟 + LocalDateTime futureTime = now.plusMinutes(40); + // 3. 如果需要转回 java.util.Date (为了兼容旧代码) + Date mustFinishTime = Date.from(futureTime.atZone(ZoneId.systemDefault()).toInstant()); + LambdaUpdateWrapper duw = new LambdaUpdateWrapper<>(); + duw.eq(MallDeliveryOrder::getOrderId, orderId) + .set(MallDeliveryOrder::getStatus, 0).set(MallDeliveryOrder::getCreateTime, createTime).set(MallDeliveryOrder::getMustFinishTime, mustFinishTime); + mallDeliveryOrderMapper.update(null, duw); + + // 触发异步推送和延迟监听流程 + triggerDeliveryAsyncEvents(orderId); + } else { + String latestSeq = merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2); + LambdaUpdateWrapper uw = new LambdaUpdateWrapper<>(); + uw.eq(MallOrder::getId, orderId).set(MallOrder::getStatus, STATUS_WAIT_PICKUP).set(MallOrder::getNumberCode, latestSeq); + this.update(uw); + } + } else if (order.getOrderType() == ORDER_TYPE_GROUP || order.getOrderType() == ORDER_TYPE_FACETOFACE) { + // 拼团订单支付成功 -> 改为待成团 + updateOrderStatus(orderId, STATUS_WAIT_GROUP); + + // 查找属于该订单的拼团并检查成团条件 + LambdaQueryWrapper gq = new LambdaQueryWrapper<>(); + gq.eq(MallOrderGroup::getHeadOrderId, orderId); + MallOrderGroup group = mallOrderGroupMapper.selectOne(gq); + if (group == null) { + // 如果不是团长,按参团人查找 + LambdaQueryWrapper gq2 = new LambdaQueryWrapper<>(); + gq2.eq(MallOrderGroup::getShopId, order.getShopId()) + .like(MallOrderGroup::getGroupOrderIds, order.getId()) + .last("LIMIT 1"); + group = mallOrderGroupMapper.selectOne(gq2); + //更新参团人数+1 + int newMembers = group.getCurrentMembers() + 1; LambdaUpdateWrapper guw = new LambdaUpdateWrapper<>(); guw.eq(MallOrderGroup::getId, group.getId()) .set(MallOrderGroup::getCurrentMembers, newMembers); mallOrderGroupMapper.update(group,guw); + }else{ + // 团长支付 且不是面对面团 状态改为待成团 + int newMembers = 1; + if(group.getIsFace() != 1){ + LambdaUpdateWrapper guw = new LambdaUpdateWrapper<>(); + guw.eq(MallOrderGroup::getId, group.getId()) + .set(MallOrderGroup::getStatus, GROUP_STATUS_FORMING).set(MallOrderGroup::getCurrentMembers, newMembers); + mallOrderGroupMapper.update(group,guw); + }else{ + LambdaUpdateWrapper guw = new LambdaUpdateWrapper<>(); + guw.eq(MallOrderGroup::getId, group.getId()) + .set(MallOrderGroup::getCurrentMembers, newMembers); + mallOrderGroupMapper.update(group,guw); + } + } + if (group != null) { + mallOrderGroupService.checkAndActivateGroup(group.getId()); } } - if (group != null) { - mallOrderGroupService.checkAndActivateGroup(group.getId()); + }else if(orderId != null && orderId.startsWith("ORDERDE_")){ + orderId = orderId.substring(8); + MallOrder order = this.getById(orderId); + if(order.getOrderType() == ORDER_TYPE_FACETOFACE){ + MallDeliveryOrder delivery = mallDeliveryOrderMapper.selectByGroupId(orderId); + mallDeliveryOrderService.addDeliveryFee(order,delivery, totalFee,order.getDeliveryFee()); + }else{ + MallDeliveryOrder delivery = mallDeliveryOrderMapper.selectById(orderId); + mallDeliveryOrderService.addDeliveryFee(order,delivery, totalFee,order.getDeliveryFee()); } } } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/KeyUtils.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/KeyUtils.java index eaf17107..da6b15e6 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/KeyUtils.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/KeyUtils.java @@ -22,4 +22,20 @@ public class KeyUtils { byte[] signed = sign.sign(); return Base64.getEncoder().encodeToString(signed); } + + public static String decryptToString(String associatedData, String nonce, String ciphertext, String apiV3Key) throws Exception { + try { + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding"); + javax.crypto.spec.SecretKeySpec key = new javax.crypto.spec.SecretKeySpec(apiV3Key.getBytes("utf-8"), "AES"); + javax.crypto.spec.GCMParameterSpec spec = new javax.crypto.spec.GCMParameterSpec(128, nonce.getBytes("utf-8")); + cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key, spec); + if (associatedData != null && !associatedData.isEmpty()) { + cipher.updateAAD(associatedData.getBytes("utf-8")); + } + byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext)); + return new String(bytes, "utf-8"); + } catch (Exception e) { + throw new Exception("AES/GCM/NoPadding 解密失败", e); + } + } } diff --git a/hiver-modules/hiver-social/pom.xml b/hiver-modules/hiver-social/pom.xml index c05eed74..7cc8e0e5 100644 --- a/hiver-modules/hiver-social/pom.xml +++ b/hiver-modules/hiver-social/pom.xml @@ -11,14 +11,10 @@ com.github.binarywang weixin-java-mp - 4.6.0 - compile cc.hiver hiver-mall - 1.0-SNAPSHOT - compile 4.0.0 diff --git a/pom.xml b/pom.xml index bd08f5f7..f98e74cf 100644 --- a/pom.xml +++ b/pom.xml @@ -50,8 +50,13 @@ 1.0.6 2.0 1.67 - 4.3.0 + 4.6.0 1.33 + 1.2.83 + 1.26.0 + 4.5.13 + build210 + 0.1.55 1.2.2 4.1.4 @@ -247,17 +252,98 @@ bcpkix-jdk15on ${bouncycastle.version} - + com.github.binarywang wx-java-mp-spring-boot-starter ${wx.java.mp.version} + + com.github.binarywang + weixin-java-mp + ${wx.java.mp.version} + com.github.binarywang wx-java-miniapp-spring-boot-starter ${wx.java.mp.version} + + com.github.binarywang + weixin-java-miniapp + ${wx.java.mp.version} + + + + com.alibaba + fastjson + ${fastjson.version} + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + + cn.dev33 + sa-token-spring-boot-starter + ${sa-token.version} + + + + ch.ethz.ganymed + ganymed-ssh2 + ${ganymed-ssh2.version} + + + com.jcraft + jsch + ${jsch.version} + + + + cc.hiver + hiver-core + ${project.version} + + + cc.hiver + hiver-mall + ${project.version} + + + cc.hiver + hiver-base + ${project.version} + + + cc.hiver + hiver-social + ${project.version} + + + cc.hiver + hiver-file + ${project.version} + + + cc.hiver + hiver-app + ${project.version} + + + cc.hiver + hiver-quartz + ${project.version} + + + cc.hiver + hiver-open + ${project.version} + + de.schlichtherle.truelicense @@ -311,6 +397,7 @@ org.springframework.boot spring-boot-maven-plugin + ${spring.boot.version}