diff --git a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java index 7972875..9864eaa 100644 --- a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java +++ b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java @@ -35,7 +35,7 @@ public class SecurityConfig { return http.authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**") .permitAll() - .antMatchers(HttpMethod.GET, "/api/file/**","/**", "/api/captcha", "/") + .antMatchers(HttpMethod.GET, "/api/file/**", "/**", "/api/captcha", "/") .permitAll() .antMatchers( "/api/login", @@ -78,8 +78,9 @@ public class SecurityConfig { "/api/chat/**", "/api/shop/getShopInfo", "/api/shop/shop-order/test", - "/api/qr-code/**" - ) + "/api/qr-code/**", + "/api/shop/order-delivery/notify" + ) .permitAll() .anyRequest() .authenticated() diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java index 6556204..74718f5 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java @@ -14,10 +14,11 @@ import com.gxwebsoft.common.core.utils.WechatCertAutoConfig; import com.gxwebsoft.common.core.utils.WechatPayConfigValidator; import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.system.entity.Payment; -import com.gxwebsoft.shop.service.ShopOrderGoodsService; -import com.gxwebsoft.shop.service.ShopOrderService; -import com.gxwebsoft.shop.service.OrderBusinessService; -import com.gxwebsoft.shop.service.OrderCancelService; +import com.gxwebsoft.shop.entity.ShopExpress; +import com.gxwebsoft.shop.entity.ShopOrderDelivery; +import com.gxwebsoft.shop.entity.ShopUserAddress; +import com.gxwebsoft.shop.service.*; +import com.gxwebsoft.shop.service.impl.KuaiDi100Impl; import com.gxwebsoft.shop.task.OrderAutoCancelTask; import com.gxwebsoft.shop.entity.ShopOrder; import com.gxwebsoft.shop.param.ShopOrderParam; @@ -27,6 +28,7 @@ import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.system.entity.User; +import com.kuaidi100.sdk.request.BOrderReq; import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RequestParam; @@ -60,503 +62,525 @@ import java.util.Map; @RestController @RequestMapping("/api/shop/shop-order") public class ShopOrderController extends BaseController { - private static final Logger logger = LoggerFactory.getLogger(ShopOrderController.class); - @Resource - private ShopOrderService shopOrderService; - @Resource - private ShopOrderGoodsService shopOrderGoodsService; - @Resource - private OrderBusinessService orderBusinessService; - @Resource - private OrderCancelService orderCancelService; - @Resource - private OrderAutoCancelTask orderAutoCancelTask; - @Resource - private RedisUtil redisUtil; - @Resource - private ConfigProperties conf; - @Resource - private CertificateProperties certConfig; - @Resource - private CertificateLoader certificateLoader; - @Resource - private WechatCertAutoConfig wechatCertAutoConfig; - @Resource - private WechatPayConfigValidator wechatPayConfigValidator; - @Value("${spring.profiles.active}") - String active; + private static final Logger logger = LoggerFactory.getLogger(ShopOrderController.class); + @Resource + private ShopOrderService shopOrderService; + @Resource + private ShopOrderGoodsService shopOrderGoodsService; + @Resource + private OrderBusinessService orderBusinessService; + @Resource + private OrderCancelService orderCancelService; + @Resource + private OrderAutoCancelTask orderAutoCancelTask; + @Resource + private RedisUtil redisUtil; + @Resource + private ConfigProperties conf; + @Resource + private CertificateProperties certConfig; + @Resource + private CertificateLoader certificateLoader; + @Resource + private WechatCertAutoConfig wechatCertAutoConfig; + @Resource + private WechatPayConfigValidator wechatPayConfigValidator; + @Value("${spring.profiles.active}") + String active; + @Resource + private KuaiDi100Impl kuaiDi100; + @Resource + private ShopExpressService expressService; + @Resource + private ShopUserAddressService shopUserAddressService; + @Resource + private ShopOrderDeliveryService shopOrderDeliveryService; - @Operation(summary = "分页查询订单") - @GetMapping("/page") - public ApiResult> page(ShopOrderParam param) { - // 使用关联查询 - return success(shopOrderService.pageRel(param)); - } - - @Operation(summary = "查询全部订单") - @GetMapping() - public ApiResult> list(ShopOrderParam param) { - // 使用关联查询 - return success(shopOrderService.listRel(param)); - } - - @PreAuthorize("hasAuthority('shop:shopOrder:list')") - @Operation(summary = "根据id查询订单") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(shopOrderService.getByIdRel(id)); - } - - @Operation(summary = "添加订单") - @PostMapping() - public ApiResult save(@RequestBody OrderCreateRequest request) { - User loginUser = getLoginUser(); - if (loginUser == null) { - return fail("用户未登录"); + @Operation(summary = "分页查询订单") + @GetMapping("/page") + public ApiResult> page(ShopOrderParam param) { + // 使用关联查询 + return success(shopOrderService.pageRel(param)); } - try { - Map wxOrderInfo = orderBusinessService.createOrder(request, loginUser); - return success("下单成功", wxOrderInfo); - } catch (Exception e) { - logger.error("创建订单失败 - 用户ID:{},请求:{}", loginUser.getUserId(), request, e); - return fail(e.getMessage()); - } - } - - @Operation(summary = "添加订单(兼容旧版本)") - @PostMapping("/legacy") - public ApiResult saveLegacy(@RequestBody ShopOrder shopOrder) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - shopOrder.setUserId(loginUser.getUserId()); - shopOrder.setOpenid(loginUser.getOpenid()); - shopOrder.setPayUserId(loginUser.getUserId()); - if (shopOrder.getOrderNo() == null) { - shopOrder.setOrderNo(Long.toString(IdUtil.getSnowflakeNextId())); - } - if (shopOrder.getComments() == null) { - shopOrder.setComments("暂无"); - } - // 微信支付(商品金额不能为0) - if (shopOrder.getTotalPrice().compareTo(BigDecimal.ZERO) == 0) { - return fail("商品金额不能为0"); - } - // 百色中学项目捐赠金额不能低于20元 - if (shopOrder.getTenantId().equals(10324) && shopOrder.getTotalPrice().compareTo(new BigDecimal("10")) < 0) { - return fail("捐款金额最低不能少于10元,感谢您的爱心捐赠^_^"); - } - // 测试支付 - if (loginUser.getPhone().equals("13737128880")) { - shopOrder.setPrice(new BigDecimal("0.01")); - shopOrder.setTotalPrice(new BigDecimal("0.01")); - } - if (shopOrderService.save(shopOrder)) { - return success("下单成功", shopOrderService.createWxOrder(shopOrder)); - } - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('shop:shopOrder:update')") - @Operation(summary = "修改订单") - @PutMapping() - public ApiResult update(@RequestBody ShopOrder shopOrder) { - // 申请退款 - if(shopOrder.getOrderStatus().equals(4)){ - shopOrder.setRefundApplyTime(LocalDateTime.now()); - } - if (shopOrderService.updateById(shopOrder)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @Operation(summary = "删除订单") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (shopOrderService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @Operation(summary = "批量添加订单") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (shopOrderService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @Operation(summary = "批量修改订单") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(shopOrderService, "order_id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @Operation(summary = "批量删除订单") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (shopOrderService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @Operation(summary = "修复订单") - @PutMapping("/repair") - public ApiResult repair(@RequestBody ShopOrder shopOrder) { - final ShopOrder order = shopOrderService.getByOutTradeNo(shopOrder.getOrderNo()); - if(order != null){ - shopOrderService.queryOrderByOutTradeNo(order); - return success("修复成功"); - } - return fail("修复失败"); - } - - @Operation(summary = "统计订单总金额") - @GetMapping("/total") - public ApiResult total() { - return success(shopOrderService.total()); - } - - @Operation(summary = "取消订单") - @PutMapping("/cancel/{id}") - public ApiResult cancelOrder(@PathVariable("id") Integer id) { - try { - User loginUser = getLoginUser(); - if (loginUser == null) { - return fail("用户未登录"); - } - - ShopOrder order = shopOrderService.getById(id); - if (order == null) { - return fail("订单不存在"); - } - - // 检查订单是否属于当前用户(非管理员用户) - if (!loginUser.getUserId().equals(order.getUserId()) && - !hasOrderCancelAuthority()) { - return fail("无权限取消此订单"); - } - - // 检查订单状态 - if (order.getPayStatus() != null && order.getPayStatus()) { - return fail("订单已支付,无法取消"); - } - - if (order.getOrderStatus() != null && order.getOrderStatus() != 0) { - return fail("订单状态不允许取消"); - } - - boolean success = orderCancelService.cancelOrder(order); - if (success) { - return success("订单取消成功"); - } else { - return fail("订单取消失败"); - } - - } catch (Exception e) { - logger.error("取消订单失败,订单ID:{}", id, e); - return fail("取消订单失败:" + e.getMessage()); - } - } - - @PreAuthorize("hasAuthority('shop:shopOrder:manage')") - @Operation(summary = "手动触发订单自动取消任务(管理员)") - @PostMapping("/auto-cancel/trigger") - public ApiResult triggerAutoCancelTask() { - try { - orderAutoCancelTask.manualCancelExpiredOrders(); - return success("自动取消任务已触发"); - } catch (Exception e) { - logger.error("触发自动取消任务失败", e); - return fail("触发失败:" + e.getMessage()); - } - } - - @PreAuthorize("hasAuthority('shop:shopOrder:manage')") - @Operation(summary = "获取自动取消任务状态(管理员)") - @GetMapping("/auto-cancel/status") - public ApiResult getAutoCancelTaskStatus() { - try { - String status = orderAutoCancelTask.getTaskStatus(); - return success(status); - } catch (Exception e) { - logger.error("获取自动取消任务状态失败", e); - return fail("获取状态失败:" + e.getMessage()); - } - } - - @Schema(description = "异步通知11") - @PostMapping("/notify/{tenantId}") - public String wxNotify(@RequestHeader Map header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) { - logger.info("异步通知*************** = " + tenantId); - - // 获取支付配置信息用于解密 - String key = "Payment:1:".concat(tenantId.toString()); - Payment payment = redisUtil.get(key, Payment.class); - - // 检查支付配置 - if (ObjectUtil.isEmpty(payment)) { - throw new RuntimeException("未找到租户支付配置信息,租户ID: " + tenantId); + @Operation(summary = "查询全部订单") + @GetMapping() + public ApiResult> list(ShopOrderParam param) { + // 使用关联查询 + return success(shopOrderService.listRel(param)); } - logger.info("开始处理微信支付异步通知 - 租户ID: {}", tenantId); - logger.info("支付配置信息 - 商户号: {}, 应用ID: {}", payment.getMchId(), payment.getAppId()); - - // 验证微信支付配置 - WechatPayConfigValidator.ValidationResult validation = wechatPayConfigValidator.validateWechatPayConfig(payment, tenantId); - if (!validation.isValid()) { - logger.error("❌ 微信支付配置验证失败: {}", validation.getErrors()); - logger.info("📋 配置诊断报告:\n{}", wechatPayConfigValidator.generateDiagnosticReport(payment, tenantId)); - throw new RuntimeException("微信支付配置验证失败: " + validation.getErrors()); + @PreAuthorize("hasAuthority('shop:shopOrder:list')") + @Operation(summary = "根据id查询订单") + @GetMapping("/{id}") + public ApiResult get(@PathVariable("id") Integer id) { + // 使用关联查询 + return success(shopOrderService.getByIdRel(id)); } - logger.info("✅ 微信支付配置验证通过"); - RequestParam requestParam = new RequestParam.Builder() - .serialNumber(header.get("wechatpay-serial")) - .nonce(header.get("wechatpay-nonce")) - .signature(header.get("wechatpay-signature")) - .timestamp(header.get("wechatpay-timestamp")) - .body(body) - .build(); - - // 创建通知配置 - 使用与下单方法相同的证书配置逻辑 - NotificationConfig config; - try { - if (active.equals("dev")) { - // 开发环境 - 构建包含租户号的私钥路径 - String tenantCertPath = "dev/wechat/" + tenantId; - String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile(); - - logger.info("开发环境异步通知证书路径: {}", privateKeyPath); - logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath); - - // 检查证书文件是否存在 - if (!certificateLoader.certificateExists(privateKeyPath)) { - logger.error("证书文件不存在: {}", privateKeyPath); - throw new RuntimeException("证书文件不存在: " + privateKeyPath); + @Operation(summary = "添加订单") + @PostMapping() + public ApiResult save(@RequestBody OrderCreateRequest request) { + User loginUser = getLoginUser(); + if (loginUser == null) { + return fail("用户未登录"); } - String privateKey = certificateLoader.loadCertificatePath(privateKeyPath); - - // 使用验证器获取有效的 APIv3 密钥 - String apiV3Key = wechatPayConfigValidator.getValidApiV3Key(payment); - - logger.info("私钥文件加载成功: {}", privateKey); - logger.info("使用APIv3密钥来源: {}", payment.getApiKey() != null && !payment.getApiKey().trim().isEmpty() ? "数据库配置" : "配置文件默认"); - logger.info("APIv3密钥长度: {}", apiV3Key != null ? apiV3Key.length() : 0); - logger.info("商户证书序列号: {}", payment.getMerchantSerialNumber()); - - // 使用自动证书配置 - config = new RSAAutoCertificateConfig.Builder() - .merchantId(payment.getMchId()) - .privateKeyFromPath(privateKey) - .merchantSerialNumber(payment.getMerchantSerialNumber()) - .apiV3Key(apiV3Key) - .build(); - - logger.info("✅ 开发环境使用自动证书配置创建通知解析器成功"); - } else { - // 生产环境 - 使用自动证书配置 - final String certRootPath = certConfig.getCertRootPath(); - logger.info("生产环境证书根路径: {}", certRootPath); - - String privateKeyRelativePath = payment.getApiclientKey(); - logger.info("数据库中的私钥相对路径: {}", privateKeyRelativePath); - - // 生产环境已经没有/file目录,所有路径都直接拼接到根路径 - String privateKeyFullPath; - // 处理数据库中可能存在的历史路径格式 - String cleanPath = privateKeyRelativePath; - if (privateKeyRelativePath.startsWith("/file/")) { - // 去掉历史的 /file/ 前缀 - cleanPath = privateKeyRelativePath.substring(6); - } else if (privateKeyRelativePath.startsWith("file/")) { - // 去掉历史的 file/ 前缀 - cleanPath = privateKeyRelativePath.substring(5); + try { + Map wxOrderInfo = orderBusinessService.createOrder(request, loginUser); + return success("下单成功", wxOrderInfo); + } catch (Exception e) { + logger.error("创建订单失败 - 用户ID:{},请求:{}", loginUser.getUserId(), request, e); + return fail(e.getMessage()); } - // 确保路径以 / 开头 - if (!cleanPath.startsWith("/")) { - cleanPath = "/" + cleanPath; + } + + @Operation(summary = "添加订单(兼容旧版本)") + @PostMapping("/legacy") + public ApiResult saveLegacy(@RequestBody ShopOrder shopOrder) { + // 记录当前登录用户id + User loginUser = getLoginUser(); + if (loginUser != null) { + shopOrder.setUserId(loginUser.getUserId()); + shopOrder.setOpenid(loginUser.getOpenid()); + shopOrder.setPayUserId(loginUser.getUserId()); + if (shopOrder.getOrderNo() == null) { + shopOrder.setOrderNo(Long.toString(IdUtil.getSnowflakeNextId())); + } + if (shopOrder.getComments() == null) { + shopOrder.setComments("暂无"); + } + // 微信支付(商品金额不能为0) + if (shopOrder.getTotalPrice().compareTo(BigDecimal.ZERO) == 0) { + return fail("商品金额不能为0"); + } + // 百色中学项目捐赠金额不能低于20元 + if (shopOrder.getTenantId().equals(10324) && shopOrder.getTotalPrice().compareTo(new BigDecimal("10")) < 0) { + return fail("捐款金额最低不能少于10元,感谢您的爱心捐赠^_^"); + } + // 测试支付 + if (loginUser.getPhone().equals("13737128880")) { + shopOrder.setPrice(new BigDecimal("0.01")); + shopOrder.setTotalPrice(new BigDecimal("0.01")); + } + if (shopOrderService.save(shopOrder)) { + return success("下单成功", shopOrderService.createWxOrder(shopOrder)); + } } - privateKeyFullPath = certRootPath + cleanPath; - - logger.info("生产环境私钥完整路径: {}", privateKeyFullPath); - String privateKey = certificateLoader.loadCertificatePath(privateKeyFullPath); - String apiV3Key = payment.getApiKey(); - - // 使用自动证书配置 - config = new RSAAutoCertificateConfig.Builder() - .merchantId(payment.getMchId()) - .privateKeyFromPath(privateKey) - .merchantSerialNumber(payment.getMerchantSerialNumber()) - .apiV3Key(apiV3Key) - .build(); - - logger.info("✅ 生产环境使用自动证书配置创建通知解析器成功"); - } - } catch (Exception e) { - logger.error("❌ 创建通知配置失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e); - logger.error("🔍 错误详情: {}", e.getMessage()); - logger.error("💡 请检查:"); - logger.error("1. 证书文件是否存在且路径正确"); - logger.error("2. APIv3密钥是否配置正确"); - logger.error("3. 商户证书序列号是否正确"); - logger.error("4. 网络连接是否正常"); - throw new RuntimeException("微信支付通知配置失败: " + e.getMessage(), e); + return fail("添加失败"); } - // 初始化 NotificationParser - NotificationParser parser = new NotificationParser(config); - logger.info("✅ 通知解析器创建成功,准备解析异步通知"); - - // 以支付通知回调为例,验签、解密并转换成 Transaction - try { - logger.info("开始解析微信支付异步通知..."); - Transaction transaction = parser.parse(requestParam, Transaction.class); - logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}", - transaction.getTradeStateDesc(), transaction.getOutTradeNo()); - - if (StrUtil.equals("支付成功", transaction.getTradeStateDesc())) { - final String outTradeNo = transaction.getOutTradeNo(); - final String transactionId = transaction.getTransactionId(); - final Integer total = transaction.getAmount().getTotal(); - final String tradeStateDesc = transaction.getTradeStateDesc(); - final Transaction.TradeStateEnum tradeState = transaction.getTradeState(); - final Transaction.TradeTypeEnum tradeType = transaction.getTradeType(); - System.out.println("transaction = " + transaction); - System.out.println("tradeStateDesc = " + tradeStateDesc); - System.out.println("tradeType = " + tradeType); - System.out.println("tradeState = " + tradeState); - System.out.println("outTradeNo = " + outTradeNo); - System.out.println("amount = " + total); - // 1. 查询要处理的订单 - ShopOrder order = shopOrderService.getByOutTradeNo(outTradeNo); - logger.info("查询要处理的订单order = " + order); - // 2. 已支付则跳过 - if (order.getPayStatus().equals(true)) { - return "SUCCESS"; + @PreAuthorize("hasAuthority('shop:shopOrder:update')") + @Operation(summary = "修改订单") + @PutMapping() + public ApiResult update(@RequestBody ShopOrder shopOrder) throws Exception { + ShopOrder shopOrderNow = shopOrderService.getById(shopOrder.getOrderId()); + // 申请退款 + if (shopOrder.getOrderStatus().equals(4)) { + shopOrder.setRefundApplyTime(LocalDateTime.now()); } - // 2. 未支付则处理更新订单状态 - if (order.getPayStatus().equals(false)) { - // 5. TODO 处理订单状态 - order.setPayTime(LocalDateTime.now()); - order.setExpirationTime(order.getCreateTime()); - order.setPayStatus(true); - order.setTransactionId(transactionId); - order.setPayPrice(new BigDecimal(NumberUtil.decimalFormat("0.00", total * 0.01))); - order.setExpirationTime(LocalDateTime.now().plusYears(10)); - System.out.println("实际付款金额 = " + order.getPayPrice()); - // 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量) - shopOrderService.updateByOutTradeNo(order); - return "SUCCESS"; + if (shopOrderNow.getDeliveryStatus().equals(10) && shopOrder.getDeliveryStatus().equals(20)) { + ShopOrderDelivery shopOrderDelivery = new ShopOrderDelivery(); + shopOrderDelivery.setOrderId(shopOrder.getOrderId()); + shopOrderDelivery.setDeliveryMethod(30); + shopOrderDelivery.setExpressId(shopOrder.getExpressId()); + shopOrderDelivery.setSendName(shopOrder.getSendName()); + shopOrderDelivery.setSendPhone(shopOrder.getSendPhone()); + shopOrderDelivery.setSendAddress(shopOrder.getSendAddress()); + shopOrderDeliveryService.save(shopOrderDelivery); + + shopOrderDeliveryService.setExpress(getLoginUser(), shopOrderDelivery, shopOrder); + } - } - } catch (Exception e) { - logger.error("❌ 处理微信支付异步通知失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e); - logger.error("🔍 异常详情: {}", e.getMessage()); - logger.error("💡 可能的原因:"); - logger.error("1. 证书配置错误或证书文件损坏"); - logger.error("2. 微信支付平台证书已过期"); - logger.error("3. 签名验证失败"); - logger.error("4. 请求参数格式错误"); - - // 返回失败,微信会重试 - return "fail"; + if (shopOrderService.updateById(shopOrder)) { + return success("修改成功"); + } + return fail("修改失败"); } - logger.warn("⚠️ 异步通知处理完成但未找到匹配的支付成功状态"); - return "fail"; - } - - @Operation(summary = "更新订单支付状态", description = "用户支付成功后主动同步订单状态") - @PutMapping("/payment-status") - public ApiResult updateOrderPaymentStatus(@RequestBody UpdatePaymentStatusRequest request) { - logger.info("收到更新订单支付状态请求: orderNo={}, paymentStatus={}, transactionId={}", - request.getOrderNo(), request.getPaymentStatus(), request.getTransactionId()); - - final User loginUser = getLoginUser(); - if (loginUser == null) { - return fail("请先登录"); + @Operation(summary = "删除订单") + @DeleteMapping("/{id}") + public ApiResult remove(@PathVariable("id") Integer id) { + if (shopOrderService.removeById(id)) { + return success("删除成功"); + } + return fail("删除失败"); } - try { - // 参数验证 - if (StrUtil.isBlank(request.getOrderNo())) { - return fail("订单号不能为空"); - } - - // 查询订单 - ShopOrder order = shopOrderService.getByOrderNo(request.getOrderNo(), loginUser.getTenantId()); - if (order == null) { - return fail("订单不存在"); - } - - // 权限验证:只能更新自己的订单 - if (!order.getUserId().equals(loginUser.getUserId())) { - return fail("无权限操作此订单"); - } - - // 如果订单已经是支付成功状态,直接返回成功 - if (order.getPayStatus()) { - logger.info("订单已经是支付成功状态,无需更新: orderNo={}", request.getOrderNo()); - return success("订单状态已是最新"); - } - - // 调用支付状态同步服务 - boolean updated = shopOrderService.syncPaymentStatus( - request.getOrderNo(), - request.getPaymentStatus(), - request.getTransactionId(), - request.getPayTime(), - loginUser.getTenantId() - ); - - if (updated) { - logger.info("订单支付状态更新成功: orderNo={}, paymentStatus={}", - request.getOrderNo(), request.getPaymentStatus()); - return success("订单状态更新成功"); - } else { - logger.warn("订单支付状态更新失败: orderNo={}", request.getOrderNo()); - return fail("订单状态更新失败"); - } - - } catch (Exception e) { - logger.error("更新订单支付状态异常: orderNo={}, error={}", request.getOrderNo(), e.getMessage(), e); - return fail("更新订单状态失败: " + e.getMessage()); + @Operation(summary = "批量添加订单") + @PostMapping("/batch") + public ApiResult saveBatch(@RequestBody List list) { + if (shopOrderService.saveBatch(list)) { + return success("添加成功"); + } + return fail("添加失败"); } - } - /** - * 检查是否有订单取消权限 - */ - private boolean hasOrderCancelAuthority() { - try { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null) { - return false; - } - - // 检查是否有管理员权限 - return authentication.getAuthorities().stream() - .anyMatch(authority -> - authority.getAuthority().equals("shop:shopOrder:cancel") || - authority.getAuthority().equals("shop:shopOrder:update") || - authority.getAuthority().equals("ROLE_ADMIN") || - authority.getAuthority().equals("shop:shopOrder:manage")); - } catch (Exception e) { - logger.warn("检查订单取消权限时发生异常", e); - return false; + @Operation(summary = "批量修改订单") + @PutMapping("/batch") + public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + if (batchParam.update(shopOrderService, "order_id")) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @Operation(summary = "批量删除订单") + @DeleteMapping("/batch") + public ApiResult removeBatch(@RequestBody List ids) { + if (shopOrderService.removeByIds(ids)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @Operation(summary = "修复订单") + @PutMapping("/repair") + public ApiResult repair(@RequestBody ShopOrder shopOrder) { + final ShopOrder order = shopOrderService.getByOutTradeNo(shopOrder.getOrderNo()); + if (order != null) { + shopOrderService.queryOrderByOutTradeNo(order); + return success("修复成功"); + } + return fail("修复失败"); + } + + @Operation(summary = "统计订单总金额") + @GetMapping("/total") + public ApiResult total() { + return success(shopOrderService.total()); + } + + @Operation(summary = "取消订单") + @PutMapping("/cancel/{id}") + public ApiResult cancelOrder(@PathVariable("id") Integer id) { + try { + User loginUser = getLoginUser(); + if (loginUser == null) { + return fail("用户未登录"); + } + + ShopOrder order = shopOrderService.getById(id); + if (order == null) { + return fail("订单不存在"); + } + + // 检查订单是否属于当前用户(非管理员用户) + if (!loginUser.getUserId().equals(order.getUserId()) && + !hasOrderCancelAuthority()) { + return fail("无权限取消此订单"); + } + + // 检查订单状态 + if (order.getPayStatus() != null && order.getPayStatus()) { + return fail("订单已支付,无法取消"); + } + + if (order.getOrderStatus() != null && order.getOrderStatus() != 0) { + return fail("订单状态不允许取消"); + } + + boolean success = orderCancelService.cancelOrder(order); + if (success) { + return success("订单取消成功"); + } else { + return fail("订单取消失败"); + } + + } catch (Exception e) { + logger.error("取消订单失败,订单ID:{}", id, e); + return fail("取消订单失败:" + e.getMessage()); + } + } + + @PreAuthorize("hasAuthority('shop:shopOrder:manage')") + @Operation(summary = "手动触发订单自动取消任务(管理员)") + @PostMapping("/auto-cancel/trigger") + public ApiResult triggerAutoCancelTask() { + try { + orderAutoCancelTask.manualCancelExpiredOrders(); + return success("自动取消任务已触发"); + } catch (Exception e) { + logger.error("触发自动取消任务失败", e); + return fail("触发失败:" + e.getMessage()); + } + } + + @PreAuthorize("hasAuthority('shop:shopOrder:manage')") + @Operation(summary = "获取自动取消任务状态(管理员)") + @GetMapping("/auto-cancel/status") + public ApiResult getAutoCancelTaskStatus() { + try { + String status = orderAutoCancelTask.getTaskStatus(); + return success(status); + } catch (Exception e) { + logger.error("获取自动取消任务状态失败", e); + return fail("获取状态失败:" + e.getMessage()); + } + } + + @Schema(description = "异步通知11") + @PostMapping("/notify/{tenantId}") + public String wxNotify(@RequestHeader Map header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) { + logger.info("异步通知*************** = " + tenantId); + + // 获取支付配置信息用于解密 + String key = "Payment:1:".concat(tenantId.toString()); + Payment payment = redisUtil.get(key, Payment.class); + + // 检查支付配置 + if (ObjectUtil.isEmpty(payment)) { + throw new RuntimeException("未找到租户支付配置信息,租户ID: " + tenantId); + } + + logger.info("开始处理微信支付异步通知 - 租户ID: {}", tenantId); + logger.info("支付配置信息 - 商户号: {}, 应用ID: {}", payment.getMchId(), payment.getAppId()); + + // 验证微信支付配置 + WechatPayConfigValidator.ValidationResult validation = wechatPayConfigValidator.validateWechatPayConfig(payment, tenantId); + if (!validation.isValid()) { + logger.error("❌ 微信支付配置验证失败: {}", validation.getErrors()); + logger.info("📋 配置诊断报告:\n{}", wechatPayConfigValidator.generateDiagnosticReport(payment, tenantId)); + throw new RuntimeException("微信支付配置验证失败: " + validation.getErrors()); + } + logger.info("✅ 微信支付配置验证通过"); + + RequestParam requestParam = new RequestParam.Builder() + .serialNumber(header.get("wechatpay-serial")) + .nonce(header.get("wechatpay-nonce")) + .signature(header.get("wechatpay-signature")) + .timestamp(header.get("wechatpay-timestamp")) + .body(body) + .build(); + + // 创建通知配置 - 使用与下单方法相同的证书配置逻辑 + NotificationConfig config; + try { + if (active.equals("dev")) { + // 开发环境 - 构建包含租户号的私钥路径 + String tenantCertPath = "dev/wechat/" + tenantId; + String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile(); + + logger.info("开发环境异步通知证书路径: {}", privateKeyPath); + logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath); + + // 检查证书文件是否存在 + if (!certificateLoader.certificateExists(privateKeyPath)) { + logger.error("证书文件不存在: {}", privateKeyPath); + throw new RuntimeException("证书文件不存在: " + privateKeyPath); + } + + String privateKey = certificateLoader.loadCertificatePath(privateKeyPath); + + // 使用验证器获取有效的 APIv3 密钥 + String apiV3Key = wechatPayConfigValidator.getValidApiV3Key(payment); + + logger.info("私钥文件加载成功: {}", privateKey); + logger.info("使用APIv3密钥来源: {}", payment.getApiKey() != null && !payment.getApiKey().trim().isEmpty() ? "数据库配置" : "配置文件默认"); + logger.info("APIv3密钥长度: {}", apiV3Key != null ? apiV3Key.length() : 0); + logger.info("商户证书序列号: {}", payment.getMerchantSerialNumber()); + + // 使用自动证书配置 + config = new RSAAutoCertificateConfig.Builder() + .merchantId(payment.getMchId()) + .privateKeyFromPath(privateKey) + .merchantSerialNumber(payment.getMerchantSerialNumber()) + .apiV3Key(apiV3Key) + .build(); + + logger.info("✅ 开发环境使用自动证书配置创建通知解析器成功"); + } else { + // 生产环境 - 使用自动证书配置 + final String certRootPath = certConfig.getCertRootPath(); + logger.info("生产环境证书根路径: {}", certRootPath); + + String privateKeyRelativePath = payment.getApiclientKey(); + logger.info("数据库中的私钥相对路径: {}", privateKeyRelativePath); + + // 生产环境已经没有/file目录,所有路径都直接拼接到根路径 + String privateKeyFullPath; + // 处理数据库中可能存在的历史路径格式 + String cleanPath = privateKeyRelativePath; + if (privateKeyRelativePath.startsWith("/file/")) { + // 去掉历史的 /file/ 前缀 + cleanPath = privateKeyRelativePath.substring(6); + } else if (privateKeyRelativePath.startsWith("file/")) { + // 去掉历史的 file/ 前缀 + cleanPath = privateKeyRelativePath.substring(5); + } + // 确保路径以 / 开头 + if (!cleanPath.startsWith("/")) { + cleanPath = "/" + cleanPath; + } + privateKeyFullPath = certRootPath + cleanPath; + + logger.info("生产环境私钥完整路径: {}", privateKeyFullPath); + String privateKey = certificateLoader.loadCertificatePath(privateKeyFullPath); + String apiV3Key = payment.getApiKey(); + + // 使用自动证书配置 + config = new RSAAutoCertificateConfig.Builder() + .merchantId(payment.getMchId()) + .privateKeyFromPath(privateKey) + .merchantSerialNumber(payment.getMerchantSerialNumber()) + .apiV3Key(apiV3Key) + .build(); + + logger.info("✅ 生产环境使用自动证书配置创建通知解析器成功"); + } + } catch (Exception e) { + logger.error("❌ 创建通知配置失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e); + logger.error("🔍 错误详情: {}", e.getMessage()); + logger.error("💡 请检查:"); + logger.error("1. 证书文件是否存在且路径正确"); + logger.error("2. APIv3密钥是否配置正确"); + logger.error("3. 商户证书序列号是否正确"); + logger.error("4. 网络连接是否正常"); + throw new RuntimeException("微信支付通知配置失败: " + e.getMessage(), e); + } + + // 初始化 NotificationParser + NotificationParser parser = new NotificationParser(config); + logger.info("✅ 通知解析器创建成功,准备解析异步通知"); + + // 以支付通知回调为例,验签、解密并转换成 Transaction + try { + logger.info("开始解析微信支付异步通知..."); + Transaction transaction = parser.parse(requestParam, Transaction.class); + logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}", + transaction.getTradeStateDesc(), transaction.getOutTradeNo()); + + if (StrUtil.equals("支付成功", transaction.getTradeStateDesc())) { + final String outTradeNo = transaction.getOutTradeNo(); + final String transactionId = transaction.getTransactionId(); + final Integer total = transaction.getAmount().getTotal(); + final String tradeStateDesc = transaction.getTradeStateDesc(); + final Transaction.TradeStateEnum tradeState = transaction.getTradeState(); + final Transaction.TradeTypeEnum tradeType = transaction.getTradeType(); + System.out.println("transaction = " + transaction); + System.out.println("tradeStateDesc = " + tradeStateDesc); + System.out.println("tradeType = " + tradeType); + System.out.println("tradeState = " + tradeState); + System.out.println("outTradeNo = " + outTradeNo); + System.out.println("amount = " + total); + // 1. 查询要处理的订单 + ShopOrder order = shopOrderService.getByOutTradeNo(outTradeNo); + logger.info("查询要处理的订单order = " + order); + // 2. 已支付则跳过 + if (order.getPayStatus().equals(true)) { + return "SUCCESS"; + } + // 2. 未支付则处理更新订单状态 + if (order.getPayStatus().equals(false)) { + // 5. TODO 处理订单状态 + order.setPayTime(LocalDateTime.now()); + order.setExpirationTime(order.getCreateTime()); + order.setPayStatus(true); + order.setTransactionId(transactionId); + order.setPayPrice(new BigDecimal(NumberUtil.decimalFormat("0.00", total * 0.01))); + order.setExpirationTime(LocalDateTime.now().plusYears(10)); + System.out.println("实际付款金额 = " + order.getPayPrice()); + // 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量) + shopOrderService.updateByOutTradeNo(order); + return "SUCCESS"; + } + } + } catch (Exception e) { + logger.error("❌ 处理微信支付异步通知失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e); + logger.error("🔍 异常详情: {}", e.getMessage()); + logger.error("💡 可能的原因:"); + logger.error("1. 证书配置错误或证书文件损坏"); + logger.error("2. 微信支付平台证书已过期"); + logger.error("3. 签名验证失败"); + logger.error("4. 请求参数格式错误"); + + // 返回失败,微信会重试 + return "fail"; + } + + logger.warn("⚠️ 异步通知处理完成但未找到匹配的支付成功状态"); + return "fail"; + } + + @Operation(summary = "更新订单支付状态", description = "用户支付成功后主动同步订单状态") + @PutMapping("/payment-status") + public ApiResult updateOrderPaymentStatus(@RequestBody UpdatePaymentStatusRequest request) { + logger.info("收到更新订单支付状态请求: orderNo={}, paymentStatus={}, transactionId={}", + request.getOrderNo(), request.getPaymentStatus(), request.getTransactionId()); + + final User loginUser = getLoginUser(); + if (loginUser == null) { + return fail("请先登录"); + } + + try { + // 参数验证 + if (StrUtil.isBlank(request.getOrderNo())) { + return fail("订单号不能为空"); + } + + // 查询订单 + ShopOrder order = shopOrderService.getByOrderNo(request.getOrderNo(), loginUser.getTenantId()); + if (order == null) { + return fail("订单不存在"); + } + + // 权限验证:只能更新自己的订单 + if (!order.getUserId().equals(loginUser.getUserId())) { + return fail("无权限操作此订单"); + } + + // 如果订单已经是支付成功状态,直接返回成功 + if (order.getPayStatus()) { + logger.info("订单已经是支付成功状态,无需更新: orderNo={}", request.getOrderNo()); + return success("订单状态已是最新"); + } + + // 调用支付状态同步服务 + boolean updated = shopOrderService.syncPaymentStatus( + request.getOrderNo(), + request.getPaymentStatus(), + request.getTransactionId(), + request.getPayTime(), + loginUser.getTenantId() + ); + + if (updated) { + logger.info("订单支付状态更新成功: orderNo={}, paymentStatus={}", + request.getOrderNo(), request.getPaymentStatus()); + return success("订单状态更新成功"); + } else { + logger.warn("订单支付状态更新失败: orderNo={}", request.getOrderNo()); + return fail("订单状态更新失败"); + } + + } catch (Exception e) { + logger.error("更新订单支付状态异常: orderNo={}, error={}", request.getOrderNo(), e.getMessage(), e); + return fail("更新订单状态失败: " + e.getMessage()); + } + } + + /** + * 检查是否有订单取消权限 + */ + private boolean hasOrderCancelAuthority() { + try { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return false; + } + + // 检查是否有管理员权限 + return authentication.getAuthorities().stream() + .anyMatch(authority -> + authority.getAuthority().equals("shop:shopOrder:cancel") || + authority.getAuthority().equals("shop:shopOrder:update") || + authority.getAuthority().equals("ROLE_ADMIN") || + authority.getAuthority().equals("shop:shopOrder:manage")); + } catch (Exception e) { + logger.warn("检查订单取消权限时发生异常", e); + return false; + } } - } } diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderDeliveryController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderDeliveryController.java index 7aa8e78..7da73e8 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderDeliveryController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderDeliveryController.java @@ -1,8 +1,18 @@ package com.gxwebsoft.shop.controller; +import cn.binarywang.wx.miniapp.api.WxMaOrderShippingService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaOrderShippingServiceImpl; +import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*; +import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import com.gxwebsoft.common.core.utils.JSONUtil; import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.shop.entity.*; +import com.gxwebsoft.shop.service.ShopExpressService; import com.gxwebsoft.shop.service.ShopOrderDeliveryService; -import com.gxwebsoft.shop.entity.ShopOrderDelivery; import com.gxwebsoft.shop.param.ShopOrderDeliveryParam; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.PageResult; @@ -10,13 +20,20 @@ import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.common.core.annotation.OperationLog; import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.shop.service.impl.KuaiDi100Impl; +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.BOrderReq; +import com.kuaidi100.sdk.response.SubscribeResp; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 发货单控制器 @@ -31,6 +48,19 @@ public class ShopOrderDeliveryController extends BaseController { @Resource private ShopOrderDeliveryService shopOrderDeliveryService; + + @Operation(summary = "发货回调") + @PostMapping("/notify") + @GetMapping("/notify") + public SubscribeResp notify(@RequestBody Map data) { + System.out.println("快递100回调:" + data); + SubscribeResp subscribeResp = new SubscribeResp(); + subscribeResp.setResult(Boolean.TRUE); + subscribeResp.setReturnCode("200"); + subscribeResp.setMessage("成功"); + return subscribeResp; + } + @Operation(summary = "分页查询发货单") @GetMapping("/page") public ApiResult> page(ShopOrderDeliveryParam param) { diff --git a/src/main/java/com/gxwebsoft/shop/entity/KuaiDi100Resp.java b/src/main/java/com/gxwebsoft/shop/entity/KuaiDi100Resp.java new file mode 100644 index 0000000..c1348ad --- /dev/null +++ b/src/main/java/com/gxwebsoft/shop/entity/KuaiDi100Resp.java @@ -0,0 +1,11 @@ +package com.gxwebsoft.shop.entity; + +import lombok.Data; + +@Data +public class KuaiDi100Resp { + private String message; + private Integer returnCode; + private Boolean result; + private Object data; +} diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java b/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java index 0e7ed6b..d9a34ba 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java @@ -308,4 +308,23 @@ public class ShopOrder implements Serializable { @Schema(description = "报名信息") @TableField(exist = false) private BszxBm bm; + + @Schema(description = "快递id") + @TableField(exist = false) + private Integer expressId; + + @Schema(description = "发货人") + @TableField(exist = false) + private String sendName; + + @Schema(description = "发货人联系方式") + @TableField(exist = false) + private String sendPhone; + + @Schema(description = "发货地址") + @TableField(exist = false) + private String sendAddress; + + @TableField(exist = false) + private ShopOrderDelivery shopOrderDelivery; } diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopOrderDelivery.java b/src/main/java/com/gxwebsoft/shop/entity/ShopOrderDelivery.java index 6794a9d..7edf12b 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopOrderDelivery.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopOrderDelivery.java @@ -1,6 +1,7 @@ package com.gxwebsoft.shop.entity; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; import com.fasterxml.jackson.annotation.JsonFormat; @@ -39,6 +40,15 @@ public class ShopOrderDelivery implements Serializable { @Schema(description = "物流公司ID") private Integer expressId; + @Schema(description = "发货人") + private String sendName; + + @Schema(description = "发货人联系方式") + private String sendPhone; + + @Schema(description = "发货地址") + private String sendAddress; + @Schema(description = "物流单号") private String expressNo; @@ -63,4 +73,6 @@ public class ShopOrderDelivery implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; + @TableField(exist = false) + private String expressName; } diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java b/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java index 4b19486..195affc 100644 --- a/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java +++ b/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java @@ -3,11 +3,15 @@ package com.gxwebsoft.shop.entity; import java.math.BigDecimal; import com.baomidou.mybatisplus.annotation.IdType; import java.time.LocalDate; + +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalTime; import java.time.LocalDateTime; import com.fasterxml.jackson.annotation.JsonFormat; import java.io.Serializable; +import java.util.List; + import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -112,4 +116,6 @@ public class ShopOrderGoods implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; + @TableField(exist = false) + private List goodsList; } diff --git a/src/main/java/com/gxwebsoft/shop/service/KuaiDi100.java b/src/main/java/com/gxwebsoft/shop/service/KuaiDi100.java new file mode 100644 index 0000000..6905f53 --- /dev/null +++ b/src/main/java/com/gxwebsoft/shop/service/KuaiDi100.java @@ -0,0 +1,16 @@ +package com.gxwebsoft.shop.service; + +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.BOrderReq; + +public interface KuaiDi100 { + /** + * 商家寄件 + * @param bOrderReq + * @return + * @throws Exception + */ + HttpResult border(BOrderReq bOrderReq) throws Exception; + + String pollList(String com, String num, String phone) throws Exception; +} diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopOrderDeliveryService.java b/src/main/java/com/gxwebsoft/shop/service/ShopOrderDeliveryService.java index 3fffbcb..1253f5b 100644 --- a/src/main/java/com/gxwebsoft/shop/service/ShopOrderDeliveryService.java +++ b/src/main/java/com/gxwebsoft/shop/service/ShopOrderDeliveryService.java @@ -2,10 +2,13 @@ package com.gxwebsoft.shop.service; import com.baomidou.mybatisplus.extension.service.IService; import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.shop.entity.ShopOrder; import com.gxwebsoft.shop.entity.ShopOrderDelivery; import com.gxwebsoft.shop.param.ShopOrderDeliveryParam; import java.util.List; +import java.util.Map; /** * 发货单Service @@ -39,4 +42,7 @@ public interface ShopOrderDeliveryService extends IService { */ ShopOrderDelivery getByIdRel(Integer deliveryId); + ShopOrderDelivery getByOrderId(Integer orderId); + + Map setExpress(User user, ShopOrderDelivery orderDelivery, ShopOrder order) throws Exception; } diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/KuaiDi100Impl.java b/src/main/java/com/gxwebsoft/shop/service/impl/KuaiDi100Impl.java new file mode 100644 index 0000000..62bfe6b --- /dev/null +++ b/src/main/java/com/gxwebsoft/shop/service/impl/KuaiDi100Impl.java @@ -0,0 +1,57 @@ +package com.gxwebsoft.shop.service.impl; + +import com.google.gson.Gson; +import com.gxwebsoft.shop.service.KuaiDi100; +import com.kuaidi100.sdk.api.QueryTrack; +import com.kuaidi100.sdk.contant.ApiInfoConstant; +import com.kuaidi100.sdk.core.BaseClient; +import com.kuaidi100.sdk.core.IBaseClient; +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.*; +import com.kuaidi100.sdk.utils.SignUtils; +import org.springframework.stereotype.Service; + +@Service +public class KuaiDi100Impl implements KuaiDi100 { + private String key = "bekcrvqy1510"; + private String secret = "8c9dd34b6ff34cad9dd6a239bd0c4d26"; + private String customer = "2E5DE2DB6B39822D793FE45EDDFB00B1"; + + @Override + public HttpResult border(BOrderReq bOrderReq) throws Exception { + PrintReq printReq = new PrintReq(); + + String t = String.valueOf(System.currentTimeMillis()); + String param = new Gson().toJson(bOrderReq); + + printReq.setKey(key); + printReq.setSign(SignUtils.printSign(param, t, key, secret)); + printReq.setT(t); + printReq.setParam(param); + printReq.setMethod(ApiInfoConstant.B_ORDER_OFFICIAL_ORDER_METHOD); + System.out.println(printReq); +// IBaseClient bOrder = new BOrderOfficial(); + IBaseClient bOrder = new BaseClient() { + @Override + public String getApiUrl(BaseRequest baseRequest) { + return "https://api.kuaidi100.com/apiMock/border"; + } + }; + return bOrder.execute(printReq); + } + + @Override + public String pollList(String com, String num, String phone) throws Exception { + QueryTrackReq queryTrackReq = new QueryTrackReq(); + QueryTrackParam queryTrackParam = new QueryTrackParam(); + queryTrackParam.setCom(com); + queryTrackParam.setNum(num); + queryTrackParam.setPhone(phone); + String param = new Gson().toJson(queryTrackParam); + queryTrackReq.setSign(SignUtils.querySign(param, key, customer)); + queryTrackReq.setCustomer(customer); + queryTrackReq.setParam(param); + IBaseClient baseClient = new QueryTrack(); + return baseClient.execute(queryTrackReq).getBody(); + } +} diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderDeliveryServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderDeliveryServiceImpl.java index 1a94ab3..41cd996 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderDeliveryServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderDeliveryServiceImpl.java @@ -1,15 +1,24 @@ package com.gxwebsoft.shop.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.common.core.utils.JSONUtil; +import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.shop.entity.*; import com.gxwebsoft.shop.mapper.ShopOrderDeliveryMapper; -import com.gxwebsoft.shop.service.ShopOrderDeliveryService; -import com.gxwebsoft.shop.entity.ShopOrderDelivery; +import com.gxwebsoft.shop.service.*; import com.gxwebsoft.shop.param.ShopOrderDeliveryParam; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.BOrderReq; import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 发货单Service实现 @@ -19,6 +28,18 @@ import java.util.List; */ @Service public class ShopOrderDeliveryServiceImpl extends ServiceImpl implements ShopOrderDeliveryService { + @Resource + private ShopExpressService expressService; + @Resource + private KuaiDi100Impl kuaiDi100; + @Resource + private ShopOrderService shopOrderService; + @Resource + private ShopOrderGoodsService shopOrderGoodsService; + @Resource + private ShopGoodsService shopGoodsService; + @Resource + private ShopUserAddressService shopUserAddressService; @Override public PageResult pageRel(ShopOrderDeliveryParam param) { @@ -44,4 +65,100 @@ public class ShopOrderDeliveryServiceImpl extends ServiceImpl() + .eq(ShopOrderDelivery::getOrderId, orderId) + .last("limit 1") + ); + } + + @Override + public Map setExpress(User user, ShopOrderDelivery orderDelivery, ShopOrder order) throws Exception { + ShopExpress express = expressService.getByIdRel(orderDelivery.getExpressId()); + ShopUserAddress userAddress = shopUserAddressService.getByIdRel(order.getAddressId()); + + BOrderReq bOrderReq = new BOrderReq(); + bOrderReq.setKuaidicom(express.getKuaidi100Code()); + bOrderReq.setSendManName(orderDelivery.getSendName()); + bOrderReq.setSendManMobile(orderDelivery.getSendPhone()); + bOrderReq.setSendManPrintAddr(order.getSendAddress()); + bOrderReq.setRecManName(userAddress.getName()); + bOrderReq.setRecManMobile(userAddress.getPhone()); + bOrderReq.setRecManPrintAddr(order.getAddress()); + bOrderReq.setCallBackUrl("https://cms-api.websoft.top/api/shop/order-delivery/notify"); + HttpResult res = kuaiDi100.border(bOrderReq); + if (res.getStatus() != 200) return new HashMap<>() {{ + put("res", false); + put("msg", "快递100接口异常"); + }}; + KuaiDi100Resp kuaiDi100Resp = JSONUtil.parseObject(res.getBody(), KuaiDi100Resp.class); + if (kuaiDi100Resp == null) return new HashMap<>() {{ + put("res", false); + put("msg", "快递100接口异常"); + }}; + + if (!kuaiDi100Resp.getResult()) + return new HashMap<>() {{ + put("res", false); + put("msg", kuaiDi100Resp.getMessage()); + }}; + Map bOrderData = (Map) kuaiDi100Resp.getData(); + orderDelivery.setExpressNo((String) bOrderData.get("kuaidinum")); + if (updateById(orderDelivery)) { + order.setDeliveryStatus(20); + order.setDeliveryTime(LocalDateTime.now()); + shopOrderService.updateById(order); + + if (order.getPayType().equals(1)) { + List orderGoodsList = shopOrderGoodsService.getListByOrderId(order.getOrderId()); + // 上传小程序发货信息 +// WxMaOrderShippingInfoUploadRequest uploadRequest = new WxMaOrderShippingInfoUploadRequest(); +// uploadRequest.setLogisticsType(1); +// uploadRequest.setDeliveryMode(1); +// +// OrderKeyBean orderKeyBean = new OrderKeyBean(); +// orderKeyBean.setOrderNumberType(2); +// orderKeyBean.setTransactionId(order.getTransactionId()); +// uploadRequest.setOrderKey(orderKeyBean); +// +// List shippingList = new ArrayList<>(); +// ShippingListBean shippingListBean = new ShippingListBean(); +// shippingListBean.setTrackingNo((String) bOrderData.get("kuaidinum")); +// shippingListBean.setExpressCompany(express.getWxCode()); +// ContactBean contactBean = new ContactBean(); +// contactBean.setReceiverContact(user.getMobile()); +// shippingListBean.setContact(contactBean); +// +// ShopGoods shopGoods = shopGoodsService.getById(orderGoodsList.get(0).getGoodsId()); +// +// String itemDesc = shopGoods.getName(); +// if (orderGoodsList.size() > 1) itemDesc += "等" + orderGoodsList.size() + "件商品"; +// shippingListBean.setItemDesc(itemDesc); +// shippingList.add(shippingListBean); +// uploadRequest.setShippingList(shippingList); +// +// uploadRequest.setUploadTime(new DateTime().toString(DatePattern.UTC_WITH_ZONE_OFFSET_PATTERN)); +// +// PayerBean payerBean = new PayerBean(); +// +// payerBean.setOpenid(user.getOpenid()); +// uploadRequest.setPayer(payerBean); +// +// WxMaService wxMaService = weChatController.wxMaService(); +// WxMaOrderShippingService wxMaOrderShippingService = new WxMaOrderShippingServiceImpl(wxMaService); +// WxMaOrderShippingInfoBaseResponse response = wxMaOrderShippingService.upload(uploadRequest); +// System.out.println("response" + response); + } + return new HashMap<>() {{ + put("res", true); + put("msg", "操作成功"); + }}; + } + return new HashMap<>() {{ + put("res", false); + put("msg", "未知错误"); + }}; + } } diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java index 01a1411..51db11c 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java @@ -79,6 +79,10 @@ public class ShopOrderServiceImpl extends ServiceImpl 10 && d.getDeliveryType().equals(0)) { + ShopOrderDelivery shopOrderDelivery = shopOrderDeliveryService.getByOrderId(d.getOrderId()); + if (shopOrderDelivery != null) { + ShopExpress shopExpress = shopExpressService.getById(shopOrderDelivery.getExpressId()); + if (shopExpress != null) { + shopOrderDelivery.setExpressName(shopExpress.getExpressName()); + } + } + d.setShopOrderDelivery(shopOrderDelivery); + } }); } return new PageResult<>(list, page.getTotal()); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index b2b6a2e..3fd6176 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -7,7 +7,15 @@ server: # 数据源配置 spring: datasource: - url: jdbc:mysql://8.134.169.209:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai +# url: jdbc:mysql://47.119.165.234:3308/website?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 +# username: website +# password: Th2ywSKxWFHeYZS8 + +# url: jdbc:mysql://182.90.229.54:3306/db_10556?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 +# username: db_10556 +# password: 3FErjCpAFmbAR7Xw + + url: jdbc:mysql://8.134.169.209:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: modules password: P7KsAyDXG8YdLnkA driver-class-name: com.mysql.cj.jdbc.Driver @@ -25,8 +33,6 @@ logging: level: com.gxwebsoft: DEBUG com.baomidou.mybatisplus: DEBUG - com.gxwebsoft.shop.mapper: DEBUG - org.apache.ibatis: DEBUG socketio: host: localhost #IP地址 @@ -48,12 +54,7 @@ mqtt: config: # 开发环境接口 server-url: https://server.websoft.top/api - upload-path: /Users/gxwebsoft/JAVA/mp-java/src/main/resources # window(D:\Temp) - - # JWT配置 - jwt: - secret: websoft-jwt-secret-key-2025-dev-environment - expire: 86400 # token过期时间(秒) 24小时 + upload-path: /Users/gxwebsoft/Documents/uploads/ # window(D:\Temp) # 开发环境证书配置 certificate: