新增分佣
This commit is contained in:
@@ -73,7 +73,8 @@ public class SecurityConfig {
|
||||
"/api/cms/form-record",
|
||||
"/api/shop/merchant-account/getMerchantAccountByPhone",
|
||||
"/api/hjm/hjm-car/**",
|
||||
"/api/chat/**"
|
||||
"/api/chat/**",
|
||||
"/api/shop/shop-order/test"
|
||||
)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.http.HttpRequest;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.system.entity.DictData;
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.common.system.entity.UserRole;
|
||||
@@ -121,6 +122,24 @@ public class RequestUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public User getByUserIdWithoutLogin(Integer userId) {
|
||||
String path = "/system/user/getByUserId/" + userId;
|
||||
try {
|
||||
// 链式构建请求
|
||||
String result = HttpRequest.get(getServerUrl().concat(path))
|
||||
.header("Tenantid", TENANT_ID)
|
||||
.timeout(20000)//超时,毫秒
|
||||
.execute().body();
|
||||
JSONObject jsonObject = JSONObject.parseObject(result);
|
||||
System.out.println("jsonObject = " + jsonObject);
|
||||
final String data = jsonObject.getString("data");
|
||||
return JSONObject.parseObject(data, User.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
public boolean saveUserByPhone(ShopMerchantAccount merchantAccount) {
|
||||
String path = "/system/user/";
|
||||
@@ -173,7 +192,6 @@ public class RequestUtil {
|
||||
try {
|
||||
// 链式构建请求
|
||||
final String result = HttpRequest.get(getServerUrl().concat("/system/user-referee/getReferee/" + userId))
|
||||
.header("Authorization", ACCESS_TOKEN)
|
||||
.header("Tenantid", TENANT_ID)
|
||||
.timeout(20000)
|
||||
.execute().body();
|
||||
@@ -202,6 +220,21 @@ public class RequestUtil {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
public void updateWithoutLogin(User user) {
|
||||
String path = "/system/user/updateWithoutLogin";
|
||||
try {
|
||||
// 链式构建请求
|
||||
final String body = HttpRequest.put(getServerUrl().concat(path))
|
||||
.header("Tenantid", TENANT_ID)
|
||||
.body(JSONUtil.toJSONString(user))
|
||||
.timeout(20000)
|
||||
.execute().body();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getMpOrderQrCode(String orderNo) {
|
||||
String path = "/wx-login/getOrderQRCode/";
|
||||
try {
|
||||
@@ -271,6 +304,21 @@ public class RequestUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResult<?> pageDictData(Integer dictId) {
|
||||
String path = "/system/dict-data/page";
|
||||
try {
|
||||
// 链式构建请求
|
||||
final String body = HttpRequest.get(getServerUrl().concat(path).concat("?dictId=" + dictId))
|
||||
.header("tenantId", TENANT_ID)
|
||||
.timeout(20000)
|
||||
.execute().body();
|
||||
return JSONUtil.parseObject(body, ApiResult.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 余额支付通知
|
||||
public void pushBalancePayNotify(Transaction transaction, Payment payment) {
|
||||
System.out.println("payment = " + payment);
|
||||
|
||||
@@ -3,16 +3,12 @@ package com.gxwebsoft.common.core.utils;
|
||||
import com.wechat.pay.java.core.Config;
|
||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 微信支付证书自动配置工具类
|
||||
* 使用RSAAutoCertificateConfig实现证书自动管理
|
||||
*
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2024-07-26
|
||||
*/
|
||||
@@ -20,47 +16,38 @@ import javax.annotation.Resource;
|
||||
@Component
|
||||
public class WechatCertAutoConfig {
|
||||
|
||||
@Value("${spring.profiles.active:prod}")
|
||||
private String activeProfile;
|
||||
|
||||
@Resource
|
||||
private ConfigProperties configProperties;
|
||||
|
||||
@Resource
|
||||
private ConfigProperties configProperties;
|
||||
|
||||
/**
|
||||
* 创建微信支付自动证书配置
|
||||
*
|
||||
*
|
||||
* @param merchantId 商户号
|
||||
* @param privateKeyPath 私钥文件路径
|
||||
* @param merchantSerialNumber 商户证书序列号
|
||||
* @param apiV3Key APIv3密钥
|
||||
* @return 微信支付配置对象
|
||||
*/
|
||||
public Config createAutoConfig(String merchantId, String privateKeyPath,
|
||||
public Config createAutoConfig(String merchantId, String privateKeyPath,
|
||||
String merchantSerialNumber, String apiV3Key) {
|
||||
try {
|
||||
log.info("创建微信支付自动证书配置...");
|
||||
log.info("商户号: {}", merchantId);
|
||||
log.info("私钥路径: {}", privateKeyPath);
|
||||
log.info("证书序列号: {}", merchantSerialNumber);
|
||||
|
||||
|
||||
Config config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(merchantId)
|
||||
.privateKeyFromPath(privateKeyPath)
|
||||
.merchantSerialNumber(merchantSerialNumber)
|
||||
.apiV3Key(apiV3Key)
|
||||
.build();
|
||||
|
||||
|
||||
log.info("✅ 微信支付自动证书配置创建成功");
|
||||
log.info("🔄 系统将自动管理平台证书的下载和更新");
|
||||
|
||||
|
||||
return config;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 创建微信支付自动证书配置失败: {}", e.getMessage(), e);
|
||||
|
||||
|
||||
// 提供详细的错误诊断信息
|
||||
log.error("🔍 错误诊断:");
|
||||
log.error("1. 请检查商户平台是否已开启API安全功能");
|
||||
@@ -68,48 +55,28 @@ public class WechatCertAutoConfig {
|
||||
log.error("3. 请验证APIv3密钥和证书序列号是否正确");
|
||||
log.error("4. 请检查网络连接是否正常");
|
||||
log.error("5. 请确认私钥文件路径是否正确: {}", privateKeyPath);
|
||||
|
||||
|
||||
throw new RuntimeException("微信支付自动证书配置失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用默认开发环境配置创建自动证书配置
|
||||
* 根据当前环境自动选择证书路径
|
||||
* 开发环境拼接规则:配置文件upload-path + dev/wechat/ + 租户ID
|
||||
*
|
||||
*
|
||||
* @return 微信支付配置对象
|
||||
*/
|
||||
public Config createDefaultDevConfig() {
|
||||
String merchantId = "1723321338";
|
||||
String privateKeyPath;
|
||||
String privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem";
|
||||
String merchantSerialNumber = "2B933F7C35014A1C363642623E4A62364B34C4EB";
|
||||
String apiV3Key = "0kF5OlPr482EZwtn9zGufUcqa7ovgxRL";
|
||||
|
||||
// 根据环境选择证书路径
|
||||
if ("dev".equals(activeProfile)) {
|
||||
// 开发环境:使用配置文件upload-path拼接证书路径
|
||||
String uploadPath = configProperties.getUploadPath(); // 配置文件路径
|
||||
String tenantId = "10550"; // 租户ID
|
||||
String certPath = uploadPath + "dev/wechat/" + tenantId + "/";
|
||||
privateKeyPath = certPath + "apiclient_key.pem";
|
||||
|
||||
log.info("开发环境:使用配置文件upload-path拼接证书路径");
|
||||
log.info("配置文件upload-path: {}", uploadPath);
|
||||
log.info("证书基础路径: {}", certPath);
|
||||
log.info("私钥文件路径: {}", privateKeyPath);
|
||||
} else {
|
||||
// 生产环境:使用相对路径,由系统动态解析
|
||||
privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem";
|
||||
log.info("生产环境:使用相对证书路径 - {}", privateKeyPath);
|
||||
}
|
||||
|
||||
|
||||
return createAutoConfig(merchantId, privateKeyPath, merchantSerialNumber, apiV3Key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试证书配置是否正常
|
||||
*
|
||||
*
|
||||
* @param config 微信支付配置
|
||||
* @return 是否配置成功
|
||||
*/
|
||||
@@ -117,56 +84,56 @@ public class WechatCertAutoConfig {
|
||||
try {
|
||||
// 这里可以添加一些基本的配置验证逻辑
|
||||
log.info("🧪 测试微信支付证书配置...");
|
||||
|
||||
|
||||
if (config == null) {
|
||||
log.error("配置对象为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
log.info("✅ 证书配置测试通过");
|
||||
return true;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 证书配置测试失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取配置使用说明
|
||||
*
|
||||
*
|
||||
* @return 使用说明
|
||||
*/
|
||||
public String getUsageInstructions() {
|
||||
return """
|
||||
🚀 微信支付自动证书配置使用说明
|
||||
================================
|
||||
|
||||
|
||||
✅ 优势:
|
||||
1. 自动下载微信支付平台证书
|
||||
2. 证书过期时自动更新
|
||||
3. 无需手动管理 wechatpay_cert.pem 文件
|
||||
4. 符合微信支付官方最佳实践
|
||||
|
||||
|
||||
📝 使用方法:
|
||||
|
||||
|
||||
// 方法1: 使用默认开发环境配置
|
||||
Config config = wechatCertAutoConfig.createDefaultDevConfig();
|
||||
|
||||
|
||||
// 方法2: 自定义配置
|
||||
Config config = wechatCertAutoConfig.createAutoConfig(
|
||||
"商户号",
|
||||
"私钥路径",
|
||||
"证书序列号",
|
||||
"商户号",
|
||||
"私钥路径",
|
||||
"证书序列号",
|
||||
"APIv3密钥"
|
||||
);
|
||||
|
||||
|
||||
🔧 前置条件:
|
||||
1. 微信商户平台已开启API安全功能
|
||||
2. 已申请使用微信支付公钥
|
||||
3. 私钥文件存在且路径正确
|
||||
4. 网络连接正常
|
||||
|
||||
|
||||
📚 更多信息:
|
||||
https://pay.weixin.qq.com/doc/v3/merchant/4012153196
|
||||
""";
|
||||
|
||||
@@ -119,7 +119,7 @@ public class User implements UserDetails {
|
||||
private String payMoney;
|
||||
|
||||
@Schema(description = "实际消费的金额(不含退款)")
|
||||
private String expendMoney;
|
||||
private BigDecimal expendMoney;
|
||||
|
||||
@Schema(description = "会员等级ID")
|
||||
private Integer gradeId;
|
||||
|
||||
@@ -6,14 +6,15 @@ import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
import com.gxwebsoft.common.core.config.CertificateProperties;
|
||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||
import com.gxwebsoft.common.core.utils.CertificateLoader;
|
||||
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
|
||||
import com.gxwebsoft.common.core.utils.WechatPayConfigValidator;
|
||||
import com.gxwebsoft.common.core.utils.*;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.system.entity.DictData;
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.shop.entity.ShopGoods;
|
||||
import com.gxwebsoft.shop.service.ShopGoodsService;
|
||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||
import com.gxwebsoft.shop.service.OrderBusinessService;
|
||||
@@ -39,7 +40,9 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -53,329 +56,354 @@ 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 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 RedisUtil redisUtil;
|
||||
@Resource
|
||||
private ConfigProperties conf;
|
||||
@Resource
|
||||
private CertificateProperties certConfig;
|
||||
@Resource
|
||||
private CertificateLoader certificateLoader;
|
||||
@Resource
|
||||
private WechatCertAutoConfig wechatCertAutoConfig;
|
||||
@Resource
|
||||
private WechatPayConfigValidator wechatPayConfigValidator;
|
||||
@Resource
|
||||
private ShopGoodsService shopGoodsService;
|
||||
@Value("${spring.profiles.active}")
|
||||
String active;
|
||||
@Resource
|
||||
private RequestUtil requestUtil;
|
||||
|
||||
@Operation(summary = "分页查询订单")
|
||||
@GetMapping("/page")
|
||||
public ApiResult<PageResult<ShopOrder>> page(ShopOrderParam param) {
|
||||
// 使用关联查询
|
||||
return success(shopOrderService.pageRel(param));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询全部订单")
|
||||
@GetMapping()
|
||||
public ApiResult<List<ShopOrder>> list(ShopOrderParam param) {
|
||||
// 使用关联查询
|
||||
return success(shopOrderService.listRel(param));
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('shop:shopOrder:list')")
|
||||
@Operation(summary = "根据id查询订单")
|
||||
@GetMapping("/{id}")
|
||||
public ApiResult<ShopOrder> 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("/test")
|
||||
public ApiResult<?> test() {
|
||||
requestUtil.setTenantId("10550");
|
||||
User user = requestUtil.getByUserIdWithoutLogin(33035);
|
||||
return success(user);
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> 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 (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<ShopOrder> list) {
|
||||
if (shopOrderService.saveBatch(list)) {
|
||||
return success("添加成功");
|
||||
}
|
||||
return fail("添加失败");
|
||||
}
|
||||
|
||||
@Operation(summary = "批量修改订单")
|
||||
@PutMapping("/batch")
|
||||
public ApiResult<?> removeBatch(@RequestBody BatchParam<ShopOrder> batchParam) {
|
||||
if (batchParam.update(shopOrderService, "order_id")) {
|
||||
return success("修改成功");
|
||||
}
|
||||
return fail("修改失败");
|
||||
}
|
||||
|
||||
@Operation(summary = "批量删除订单")
|
||||
@DeleteMapping("/batch")
|
||||
public ApiResult<?> removeBatch(@RequestBody List<Integer> 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<BigDecimal> total() {
|
||||
return success(shopOrderService.total());
|
||||
}
|
||||
|
||||
@Schema(description = "异步通知")
|
||||
@PostMapping("/notify/{tenantId}")
|
||||
public String wxNotify(@RequestHeader Map<String, String> 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("/page")
|
||||
public ApiResult<PageResult<ShopOrder>> page(ShopOrderParam param) {
|
||||
// 使用关联查询
|
||||
return success(shopOrderService.pageRel(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());
|
||||
@Operation(summary = "查询全部订单")
|
||||
@GetMapping()
|
||||
public ApiResult<List<ShopOrder>> list(ShopOrderParam param) {
|
||||
// 使用关联查询
|
||||
return success(shopOrderService.listRel(param));
|
||||
}
|
||||
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();
|
||||
@PreAuthorize("hasAuthority('shop:shopOrder:list')")
|
||||
@Operation(summary = "根据id查询订单")
|
||||
@GetMapping("/{id}")
|
||||
public ApiResult<ShopOrder> get(@PathVariable("id") Integer id) {
|
||||
// 使用关联查询
|
||||
return success(shopOrderService.getByIdRel(id));
|
||||
}
|
||||
|
||||
// 创建通知配置 - 使用与下单方法相同的证书配置逻辑
|
||||
NotificationConfig config;
|
||||
try {
|
||||
if (active.equals("dev")) {
|
||||
// 开发环境 - 使用配置文件的upload-path构建证书路径
|
||||
String uploadPath = conf.getUploadPath();
|
||||
String tenantCertPath = uploadPath + "dev/wechat/" + tenantId;
|
||||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||||
@Operation(summary = "添加订单")
|
||||
@PostMapping()
|
||||
public ApiResult<?> save(@Valid @RequestBody OrderCreateRequest request) {
|
||||
User loginUser = getLoginUser();
|
||||
if (loginUser == null) {
|
||||
return fail("用户未登录");
|
||||
}
|
||||
if (request.getTotalPrice() == null || request.getTotalPrice().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
if (request.getGoodsItems() != null && !request.getGoodsItems().isEmpty()) {
|
||||
BigDecimal totalPrice = BigDecimal.ZERO;
|
||||
int totalNum = 0;
|
||||
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
|
||||
ShopGoods goods = shopGoodsService.getById(item.getGoodsId().toString());
|
||||
if (goods != null) {
|
||||
BigDecimal price = goods.getPrice().multiply(new BigDecimal(item.getQuantity().toString()));
|
||||
totalPrice = totalPrice.add(price);
|
||||
totalNum += Integer.parseInt(item.getQuantity().toString());
|
||||
}
|
||||
}
|
||||
request.setTotalPrice(totalPrice);
|
||||
request.setPayPrice(totalPrice);
|
||||
request.setTotalNum(totalNum);
|
||||
request.setFormId(Integer.parseInt(request.getGoodsItems().get(0).getQuantity().toString()));
|
||||
}
|
||||
}
|
||||
if (request.getTenantId() == null) {
|
||||
request.setTenantId(getTenantId());
|
||||
}
|
||||
try {
|
||||
Map<String, String> wxOrderInfo = orderBusinessService.createOrder(request, loginUser);
|
||||
return success("下单成功", wxOrderInfo);
|
||||
} catch (Exception e) {
|
||||
logger.error("创建订单失败", e);
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("开发环境异步通知证书路径: {}", privateKeyPath);
|
||||
logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath);
|
||||
@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("添加失败");
|
||||
}
|
||||
|
||||
// 检查证书文件是否存在
|
||||
if (!certificateLoader.certificateExists(privateKeyPath)) {
|
||||
logger.error("证书文件不存在: {}", privateKeyPath);
|
||||
throw new RuntimeException("证书文件不存在: " + privateKeyPath);
|
||||
@PreAuthorize("hasAuthority('shop:shopOrder:update')")
|
||||
@Operation(summary = "修改订单")
|
||||
@PutMapping()
|
||||
public ApiResult<?> update(@RequestBody ShopOrder shopOrder) {
|
||||
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<ShopOrder> list) {
|
||||
if (shopOrderService.saveBatch(list)) {
|
||||
return success("添加成功");
|
||||
}
|
||||
return fail("添加失败");
|
||||
}
|
||||
|
||||
@Operation(summary = "批量修改订单")
|
||||
@PutMapping("/batch")
|
||||
public ApiResult<?> removeBatch(@RequestBody BatchParam<ShopOrder> batchParam) {
|
||||
if (batchParam.update(shopOrderService, "order_id")) {
|
||||
return success("修改成功");
|
||||
}
|
||||
return fail("修改失败");
|
||||
}
|
||||
|
||||
@Operation(summary = "批量删除订单")
|
||||
@DeleteMapping("/batch")
|
||||
public ApiResult<?> removeBatch(@RequestBody List<Integer> 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("修复失败");
|
||||
}
|
||||
|
||||
@Schema(description = "异步通知")
|
||||
@PostMapping("/notify/{tenantId}")
|
||||
public String wxNotify(@RequestHeader Map<String, String> 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);
|
||||
}
|
||||
|
||||
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
|
||||
logger.info("开始处理微信支付异步通知 - 租户ID: {}", tenantId);
|
||||
logger.info("支付配置信息 - 商户号: {}, 应用ID: {}", payment.getMchId(), payment.getAppId());
|
||||
|
||||
// 使用验证器获取有效的 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();
|
||||
final String certBasePath = certRootPath + "/file";
|
||||
|
||||
String privateKeyRelativePath = payment.getApiclientKey();
|
||||
String privateKeyFullPath = privateKeyRelativePath.startsWith("/")
|
||||
? certBasePath + privateKeyRelativePath
|
||||
: certBasePath + "/" + privateKeyRelativePath;
|
||||
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";
|
||||
// 验证微信支付配置
|
||||
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());
|
||||
}
|
||||
// 2. 未支付则处理更新订单状态
|
||||
if (order.getPayStatus().equals(false)) {
|
||||
// 5. TODO 处理订单状态
|
||||
order.setPayTime(DateUtil.date());
|
||||
order.setExpirationTime(order.getCreateTime());
|
||||
order.setPayStatus(true);
|
||||
order.setTransactionId(transactionId);
|
||||
order.setPayPrice(new BigDecimal(NumberUtil.decimalFormat("0.00", total * 0.01)));
|
||||
order.setExpirationTime(DateUtil.offset(DateUtil.date(), DateField.YEAR, 10));
|
||||
System.out.println("实际付款金额 = " + order.getPayPrice());
|
||||
shopOrderService.updateByOutTradeNo(order);
|
||||
return "SUCCESS";
|
||||
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();
|
||||
final String certBasePath = certRootPath + "/file";
|
||||
|
||||
String privateKeyRelativePath = payment.getApiclientKey();
|
||||
String privateKeyFullPath = privateKeyRelativePath.startsWith("/")
|
||||
? certBasePath + privateKeyRelativePath
|
||||
: certBasePath + "/" + privateKeyRelativePath;
|
||||
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);
|
||||
}
|
||||
}
|
||||
} 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";
|
||||
// 初始化 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(DateUtil.date());
|
||||
order.setExpirationTime(order.getCreateTime());
|
||||
order.setPayStatus(true);
|
||||
order.setTransactionId(transactionId);
|
||||
order.setPayPrice(new BigDecimal(NumberUtil.decimalFormat("0.00", total * 0.01)));
|
||||
order.setExpirationTime(DateUtil.offset(DateUtil.date(), DateField.YEAR, 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";
|
||||
}
|
||||
|
||||
logger.warn("⚠️ 异步通知处理完成但未找到匹配的支付成功状态");
|
||||
return "fail";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class ShopDealerOrder implements Serializable {
|
||||
private Integer isSettled;
|
||||
|
||||
@Schema(description = "结算时间")
|
||||
private Integer settleTime;
|
||||
private Long settleTime;
|
||||
|
||||
@Schema(description = "商城ID")
|
||||
private Integer tenantId;
|
||||
|
||||
@@ -69,6 +69,9 @@ public class ShopGoods implements Serializable {
|
||||
@Schema(description = "经销商价格")
|
||||
private BigDecimal dealerPrice;
|
||||
|
||||
@Schema(description = "佣金")
|
||||
private BigDecimal commission;
|
||||
|
||||
@Schema(description = "库存计算方式(10下单减库存 20付款减库存)")
|
||||
private Integer deductStockType;
|
||||
|
||||
|
||||
@@ -39,4 +39,5 @@ public interface ShopOrderGoodsService extends IService<ShopOrderGoods> {
|
||||
*/
|
||||
ShopOrderGoods getByIdRel(Integer id);
|
||||
|
||||
List<ShopOrderGoods> getListByOrderId(Integer orderId);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.shop.mapper.ShopOrderGoodsMapper;
|
||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||
@@ -44,4 +45,12 @@ public class ShopOrderGoodsServiceImpl extends ServiceImpl<ShopOrderGoodsMapper,
|
||||
return param.getOne(baseMapper.selectListRel(param));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShopOrderGoods> getListByOrderId(Integer orderId) {
|
||||
return list(
|
||||
new LambdaQueryWrapper<ShopOrderGoods>()
|
||||
.eq(ShopOrderGoods::getOrderId, orderId)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,30 +4,25 @@
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
import com.gxwebsoft.common.core.config.CertificateProperties;
|
||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||
import com.gxwebsoft.common.core.utils.CertificateLoader;
|
||||
import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
import com.gxwebsoft.common.core.utils.WechatPayDiagnostic;
|
||||
import com.gxwebsoft.common.core.utils.WechatPayCertificateDiagnostic;
|
||||
import com.gxwebsoft.common.core.utils.*;
|
||||
import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.shop.entity.*;
|
||||
import com.gxwebsoft.shop.service.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.gxwebsoft.common.system.service.PaymentService;
|
||||
import com.gxwebsoft.common.system.service.SettingService;
|
||||
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
||||
import com.gxwebsoft.shop.mapper.ShopOrderMapper;
|
||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||
import com.gxwebsoft.shop.param.ShopOrderParam;
|
||||
import com.gxwebsoft.common.core.web.PageParam;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
import com.wechat.pay.java.core.Config;
|
||||
import com.wechat.pay.java.core.RSAConfig;
|
||||
import com.wechat.pay.java.core.RSAPublicKeyConfig;
|
||||
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
|
||||
import com.wechat.pay.java.core.exception.ServiceException;
|
||||
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
||||
import com.wechat.pay.java.service.payments.jsapi.model.*;
|
||||
@@ -38,10 +33,8 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.sql.Date;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -62,6 +55,8 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
@Resource
|
||||
private ShopOrderGoodsService shopOrderGoodsService;
|
||||
@Resource
|
||||
private ShopGoodsService shopGoodsService;
|
||||
@Resource
|
||||
private PaymentService paymentService;
|
||||
@Resource
|
||||
private SettingService settingService;
|
||||
@@ -77,6 +72,12 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
private WechatPayDiagnostic wechatPayDiagnostic;
|
||||
@Resource
|
||||
private WechatPayCertificateDiagnostic certificateDiagnostic;
|
||||
@Resource
|
||||
private RequestUtil requestUtil;
|
||||
@Resource
|
||||
private ShopDealerOrderService shopDealerOrderService;
|
||||
@Resource
|
||||
private ShopDealerCapitalService shopDealerCapitalService;
|
||||
|
||||
@Override
|
||||
public PageResult<ShopOrder> pageRel(ShopOrderParam param) {
|
||||
@@ -243,6 +244,61 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
@Override
|
||||
public void updateByOutTradeNo(ShopOrder order) {
|
||||
baseMapper.updateByOutTradeNo(order);
|
||||
if (order.getTenantId().equals(10550)) {
|
||||
requestUtil.setTenantId(order.getTenantId().toString());
|
||||
ApiResult<?> partnerConditionReq = requestUtil.pageDictData(1460);
|
||||
if (partnerConditionReq.getCode().equals(0) && partnerConditionReq.getData() != null) {
|
||||
LinkedHashMap<String, Object> dictDataMap = (LinkedHashMap<String, Object>) partnerConditionReq.getData();
|
||||
List<LinkedHashMap> dictDataList = (List<LinkedHashMap>) dictDataMap.get("list");
|
||||
String dictDataCode = (String) dictDataList.get(0).get("dictDataCode");
|
||||
BigDecimal partnerCondition = new BigDecimal(dictDataCode);
|
||||
|
||||
User user = requestUtil.getByUserIdWithoutLogin(order.getUserId());
|
||||
if (user != null) {
|
||||
user.setExpendMoney(user.getExpendMoney().add(order.getPayPrice()));
|
||||
if (user.getExpendMoney().compareTo(partnerCondition) >= 0) {
|
||||
user.setGradeId(3);
|
||||
}
|
||||
requestUtil.updateWithoutLogin(user);
|
||||
|
||||
// 上级
|
||||
User parent = requestUtil.getParent(order.getUserId());
|
||||
if (parent != null) {
|
||||
|
||||
List<ShopOrderGoods> shopOrderGoodsList = shopOrderGoodsService.getListByOrderId(order.getOrderId());
|
||||
List<Integer> goodsIds = shopOrderGoodsList.stream().map(ShopOrderGoods::getGoodsId).toList();
|
||||
List<ShopGoods> shopGoodsList = shopGoodsService.listByIds(goodsIds);
|
||||
BigDecimal commission = BigDecimal.ZERO;
|
||||
for (ShopOrderGoods shopOrderGoods : shopOrderGoodsList) {
|
||||
ShopGoods shopGoods = shopGoodsList.stream().filter(sG -> sG.getGoodsId().equals(shopOrderGoods.getGoodsId())).findFirst().orElse(null);
|
||||
if (shopGoods != null) {
|
||||
commission = commission.add(shopGoods.getCommission().multiply(BigDecimal.valueOf(shopOrderGoods.getTotalNum())));
|
||||
}
|
||||
}
|
||||
parent.setBalance(parent.getBalance().add(commission));
|
||||
requestUtil.updateWithoutLogin(user);
|
||||
|
||||
// 分销订单
|
||||
ShopDealerOrder shopDealerOrder = new ShopDealerOrder();
|
||||
shopDealerOrder.setUserId(parent.getUserId());
|
||||
shopDealerOrder.setOrderId(order.getOrderId());
|
||||
shopDealerOrder.setOrderPrice(order.getTotalPrice());
|
||||
shopDealerOrder.setFirstUserId(order.getUserId());
|
||||
shopDealerOrder.setFirstMoney(commission);
|
||||
shopDealerOrder.setIsSettled(1);
|
||||
shopDealerOrder.setSettleTime(DateUtil.currentSeconds());
|
||||
shopDealerOrderService.save(shopDealerOrder);
|
||||
|
||||
// 分销资明细
|
||||
ShopDealerCapital shopDealerCapital = new ShopDealerCapital();
|
||||
shopDealerCapital.setUserId(parent.getUserId());
|
||||
shopDealerCapital.setOrderId(order.getOrderId());
|
||||
shopDealerCapital.setFlowType(10);
|
||||
shopDealerCapitalService.save(shopDealerCapital);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,9 +360,8 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
|
||||
// 开发环境配置 - 使用自动证书配置
|
||||
if (active.equals("dev")) {
|
||||
// 开发环境 - 使用配置文件的upload-path构建证书路径
|
||||
String uploadPath = config.getUploadPath();
|
||||
String tenantCertPath = uploadPath + "dev/wechat/" + order.getTenantId();
|
||||
// 构建包含租户号的证书路径: dev/wechat/{tenantId}/
|
||||
String tenantCertPath = "dev/wechat/" + order.getTenantId();
|
||||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||||
|
||||
System.out.println("开发环境证书路径 - 租户ID: " + order.getTenantId());
|
||||
@@ -396,9 +451,8 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
payment.getPubKeyId() != null && !payment.getPubKeyId().isEmpty()) {
|
||||
|
||||
try {
|
||||
// 开发环境使用配置文件的upload-path构建公钥路径
|
||||
String uploadPath = config.getUploadPath();
|
||||
String tenantCertPath = uploadPath + "dev/wechat/" + order.getTenantId();
|
||||
// 开发环境固定使用 wechatpay_public_key.pem
|
||||
String tenantCertPath = "dev/wechat/" + order.getTenantId();
|
||||
String pubKeyPath = tenantCertPath + "/wechatpay_public_key.pem";
|
||||
|
||||
System.out.println("开发环境公钥文件路径: " + pubKeyPath);
|
||||
@@ -472,8 +526,7 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
System.err.println("⚠️ 开发环境回退到基础RSA配置...");
|
||||
try {
|
||||
// 方案1:尝试使用RSA证书配置(需要商户证书文件)
|
||||
String uploadPath = config.getUploadPath();
|
||||
String tenantCertPath = uploadPath + "dev/wechat/" + order.getTenantId();
|
||||
String tenantCertPath = "dev/wechat/" + order.getTenantId();
|
||||
String apiclientCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
|
||||
|
||||
if (certificateLoader.certificateExists(apiclientCertPath)) {
|
||||
@@ -651,23 +704,23 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal total() {
|
||||
try {
|
||||
// 使用数据库聚合查询统计订单总金额,性能更高
|
||||
BigDecimal total = baseMapper.selectTotalAmount();
|
||||
@Override
|
||||
public BigDecimal total() {
|
||||
try {
|
||||
// 使用数据库聚合查询统计订单总金额,性能更高
|
||||
BigDecimal total = baseMapper.selectTotalAmount();
|
||||
|
||||
if (total == null) {
|
||||
total = BigDecimal.ZERO;
|
||||
}
|
||||
if (total == null) {
|
||||
total = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
log.info("统计订单总金额完成,总金额:{}", total);
|
||||
return total;
|
||||
log.info("统计订单总金额完成,总金额:{}", total);
|
||||
return total;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("统计订单总金额失败", e);
|
||||
return BigDecimal.ZERO;
|
||||
} catch (Exception e) {
|
||||
log.error("统计订单总金额失败", e);
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://1Panel-mysql-Bqdt:3306/website?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://1Panel-mysql-Bqdt:3306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
|
||||
username: modules
|
||||
password: 8YdLnk7KsPAyDXGA
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
@@ -45,7 +45,7 @@ mqtt:
|
||||
config:
|
||||
# 生产环境接口
|
||||
server-url: https://server.websoft.top/api
|
||||
upload-path: /www/wwwroot/file.ws
|
||||
upload-path: /www/wwwroot/file.ws/
|
||||
|
||||
# 阿里云OSS云存储
|
||||
endpoint: https://oss-cn-shenzhen.aliyuncs.com
|
||||
|
||||
@@ -4,7 +4,7 @@ server:
|
||||
# 多环境配置
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
active: prod
|
||||
|
||||
application:
|
||||
name: server
|
||||
|
||||
Reference in New Issue
Block a user