feat(dev): 添加开发环境支付配置和优惠券字段修复功能
- 新增开发环境控制器和环境感知支付服务 - 添加数据库字段缺失修复指南 - 改进优惠券适用商品查询逻辑 -优化支付配置获取方式
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
package com.gxwebsoft.common.core.controller;
|
||||
|
||||
import com.gxwebsoft.common.core.service.EnvironmentAwarePaymentService;
|
||||
import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.common.system.service.PaymentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 开发环境管理控制器
|
||||
* 仅在开发环境启用,用于管理开发调试配置
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@Tag(name = "开发环境管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/dev")
|
||||
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
|
||||
public class DevEnvironmentController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private EnvironmentAwarePaymentService environmentAwarePaymentService;
|
||||
|
||||
@Autowired
|
||||
private PaymentCacheService paymentCacheService;
|
||||
|
||||
@Autowired
|
||||
private PaymentService paymentService;
|
||||
|
||||
@Value("${spring.profiles.active:dev}")
|
||||
private String activeProfile;
|
||||
|
||||
@Operation(summary = "获取当前环境信息")
|
||||
@GetMapping("/environment/info")
|
||||
public ApiResult<Map<String, Object>> getEnvironmentInfo() {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("activeProfile", activeProfile);
|
||||
info.put("isDevelopment", environmentAwarePaymentService.isDevelopmentEnvironment());
|
||||
info.put("isProduction", environmentAwarePaymentService.isProductionEnvironment());
|
||||
info.put("currentEnvironment", environmentAwarePaymentService.getCurrentEnvironment());
|
||||
|
||||
return success("获取成功", info);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取环境感知的支付配置")
|
||||
@GetMapping("/payment/config/{payType}")
|
||||
public ApiResult<Map<String, Object>> getPaymentConfig(@PathVariable Integer payType) {
|
||||
try {
|
||||
Integer tenantId = getTenantId();
|
||||
|
||||
// 获取原始配置
|
||||
Payment originalConfig = paymentCacheService.getPaymentConfig(payType, tenantId);
|
||||
|
||||
// 获取环境感知配置
|
||||
Payment envConfig = environmentAwarePaymentService.getEnvironmentAwarePaymentConfig(payType, tenantId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("tenantId", tenantId);
|
||||
result.put("payType", payType);
|
||||
result.put("environment", activeProfile);
|
||||
result.put("originalConfig", originalConfig);
|
||||
result.put("environmentAwareConfig", envConfig);
|
||||
|
||||
if (originalConfig != null && envConfig != null) {
|
||||
result.put("notifyUrlChanged", !originalConfig.getNotifyUrl().equals(envConfig.getNotifyUrl()));
|
||||
result.put("originalNotifyUrl", originalConfig.getNotifyUrl());
|
||||
result.put("environmentNotifyUrl", envConfig.getNotifyUrl());
|
||||
}
|
||||
|
||||
return success("获取成功", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取支付配置失败", e);
|
||||
return fail("获取失败: " + e.getMessage(),null);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "切换开发环境回调地址")
|
||||
@PostMapping("/payment/switch-notify-url")
|
||||
public ApiResult<?> switchNotifyUrl(@RequestBody Map<String, String> request) {
|
||||
try {
|
||||
String newNotifyUrl = request.get("notifyUrl");
|
||||
Integer payType = Integer.valueOf(request.getOrDefault("payType", "0"));
|
||||
|
||||
if (newNotifyUrl == null || newNotifyUrl.trim().isEmpty()) {
|
||||
return fail("回调地址不能为空");
|
||||
}
|
||||
|
||||
Integer tenantId = getTenantId();
|
||||
|
||||
// 获取当前配置
|
||||
Payment payment = paymentCacheService.getPaymentConfig(payType, tenantId);
|
||||
if (payment == null) {
|
||||
return fail("未找到支付配置");
|
||||
}
|
||||
|
||||
// 更新回调地址
|
||||
payment.setNotifyUrl(newNotifyUrl);
|
||||
|
||||
// 更新数据库
|
||||
boolean updated = paymentService.updateById(payment);
|
||||
|
||||
if (updated) {
|
||||
// 清除缓存,强制重新加载
|
||||
paymentCacheService.removePaymentConfig(payType.toString(), tenantId);
|
||||
|
||||
log.info("开发环境回调地址已更新: {} -> {}", payment.getNotifyUrl(), newNotifyUrl);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("oldNotifyUrl", payment.getNotifyUrl());
|
||||
result.put("newNotifyUrl", newNotifyUrl);
|
||||
result.put("payType", payType);
|
||||
result.put("tenantId", tenantId);
|
||||
|
||||
return success("回调地址更新成功", result);
|
||||
} else {
|
||||
return fail("更新失败");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("切换回调地址失败", e);
|
||||
return fail("切换失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "重置为生产环境回调地址")
|
||||
@PostMapping("/payment/reset-to-prod")
|
||||
public ApiResult<?> resetToProdNotifyUrl(@RequestParam(defaultValue = "0") Integer payType) {
|
||||
try {
|
||||
Integer tenantId = getTenantId();
|
||||
|
||||
// 获取当前配置
|
||||
Payment payment = paymentCacheService.getPaymentConfig(payType, tenantId);
|
||||
if (payment == null) {
|
||||
return fail("未找到支付配置");
|
||||
}
|
||||
|
||||
// 设置为生产环境回调地址
|
||||
String prodNotifyUrl = "https://cms-api.websoft.top/api/shop/shop-order/notify";
|
||||
String oldNotifyUrl = payment.getNotifyUrl();
|
||||
|
||||
payment.setNotifyUrl(prodNotifyUrl);
|
||||
|
||||
// 更新数据库
|
||||
boolean updated = paymentService.updateById(payment);
|
||||
|
||||
if (updated) {
|
||||
// 清除缓存
|
||||
paymentCacheService.removePaymentConfig(payType.toString(), tenantId);
|
||||
|
||||
log.info("回调地址已重置为生产环境: {} -> {}", oldNotifyUrl, prodNotifyUrl);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("oldNotifyUrl", oldNotifyUrl);
|
||||
result.put("newNotifyUrl", prodNotifyUrl);
|
||||
result.put("payType", payType);
|
||||
result.put("tenantId", tenantId);
|
||||
|
||||
return success("已重置为生产环境回调地址", result);
|
||||
} else {
|
||||
return fail("重置失败");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("重置回调地址失败", e);
|
||||
return fail("重置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "清除支付配置缓存")
|
||||
@PostMapping("/payment/clear-cache")
|
||||
public ApiResult<?> clearPaymentCache(@RequestParam(defaultValue = "0") Integer payType) {
|
||||
try {
|
||||
Integer tenantId = getTenantId();
|
||||
|
||||
paymentCacheService.removePaymentConfig(payType.toString(), tenantId);
|
||||
|
||||
log.info("支付配置缓存已清除: payType={}, tenantId={}", payType, tenantId);
|
||||
|
||||
return success("缓存清除成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("清除缓存失败", e);
|
||||
return fail("清除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "获取开发环境使用指南")
|
||||
@GetMapping("/guide")
|
||||
public ApiResult<Map<String, Object>> getDevGuide() {
|
||||
Map<String, Object> guide = new HashMap<>();
|
||||
|
||||
guide.put("title", "开发环境支付调试指南");
|
||||
guide.put("environment", activeProfile);
|
||||
|
||||
Map<String, String> steps = new HashMap<>();
|
||||
steps.put("step1", "使用 /api/dev/payment/switch-notify-url 切换到本地回调地址");
|
||||
steps.put("step2", "进行支付功能调试和测试");
|
||||
steps.put("step3", "调试完成后使用 /api/dev/payment/reset-to-prod 恢复生产环境配置");
|
||||
steps.put("step4", "或者直接在后台管理界面修改回调地址");
|
||||
|
||||
guide.put("steps", steps);
|
||||
|
||||
Map<String, String> apis = new HashMap<>();
|
||||
apis.put("环境信息", "GET /api/dev/environment/info");
|
||||
apis.put("查看配置", "GET /api/dev/payment/config/{payType}");
|
||||
apis.put("切换回调", "POST /api/dev/payment/switch-notify-url");
|
||||
apis.put("重置生产", "POST /api/dev/payment/reset-to-prod");
|
||||
apis.put("清除缓存", "POST /api/dev/payment/clear-cache");
|
||||
|
||||
guide.put("apis", apis);
|
||||
|
||||
Map<String, String> tips = new HashMap<>();
|
||||
tips.put("tip1", "此控制器仅在开发环境启用");
|
||||
tips.put("tip2", "生产环境不会加载这些接口");
|
||||
tips.put("tip3", "建议使用环境感知服务自动切换");
|
||||
tips.put("tip4", "记得在调试完成后恢复生产配置");
|
||||
|
||||
guide.put("tips", tips);
|
||||
|
||||
return success("获取成功", guide);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.gxwebsoft.common.core.service;
|
||||
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 环境感知的支付配置服务
|
||||
* 根据不同环境自动切换支付回调地址
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class EnvironmentAwarePaymentService {
|
||||
|
||||
@Autowired
|
||||
private PaymentCacheService paymentCacheService;
|
||||
|
||||
@Value("${spring.profiles.active:dev}")
|
||||
private String activeProfile;
|
||||
|
||||
@Value("${config.server-url:}")
|
||||
private String serverUrl;
|
||||
|
||||
// 开发环境回调地址配置
|
||||
@Value("${payment.dev.notify-url:http://frps-10550.s209.websoft.top/api/shop/shop-order/notify}")
|
||||
private String devNotifyUrl;
|
||||
|
||||
// 生产环境回调地址配置
|
||||
@Value("${payment.prod.notify-url:https://cms-api.websoft.top/api/shop/shop-order/notify}")
|
||||
private String prodNotifyUrl;
|
||||
|
||||
/**
|
||||
* 获取环境感知的支付配置
|
||||
* 根据当前环境自动调整回调地址
|
||||
*
|
||||
* @param payType 支付类型
|
||||
* @param tenantId 租户ID
|
||||
* @return 支付配置
|
||||
*/
|
||||
public Payment getEnvironmentAwarePaymentConfig(Integer payType, Integer tenantId) {
|
||||
// 获取原始支付配置
|
||||
Payment payment = paymentCacheService.getPaymentConfig(payType, tenantId);
|
||||
|
||||
if (payment == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 根据环境调整回调地址
|
||||
Payment envPayment = clonePayment(payment);
|
||||
String notifyUrl = getEnvironmentNotifyUrl();
|
||||
|
||||
log.info("环境感知支付配置 - 环境: {}, 原始回调: {}, 调整后回调: {}",
|
||||
activeProfile, payment.getNotifyUrl(), notifyUrl);
|
||||
|
||||
envPayment.setNotifyUrl(notifyUrl);
|
||||
|
||||
return envPayment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前环境获取回调地址
|
||||
*/
|
||||
private String getEnvironmentNotifyUrl() {
|
||||
if ("dev".equals(activeProfile) || "test".equals(activeProfile)) {
|
||||
// 开发/测试环境使用本地回调地址
|
||||
return devNotifyUrl;
|
||||
} else if ("prod".equals(activeProfile)) {
|
||||
// 生产环境使用生产回调地址
|
||||
return prodNotifyUrl;
|
||||
} else {
|
||||
// 默认使用配置的服务器地址
|
||||
return serverUrl + "/shop/shop-order/notify";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆支付配置对象
|
||||
*/
|
||||
private Payment clonePayment(Payment original) {
|
||||
Payment cloned = new Payment();
|
||||
cloned.setId(original.getId());
|
||||
cloned.setName(original.getName());
|
||||
cloned.setType(original.getType());
|
||||
cloned.setCode(original.getCode());
|
||||
cloned.setImage(original.getImage());
|
||||
cloned.setWechatType(original.getWechatType());
|
||||
cloned.setAppId(original.getAppId());
|
||||
cloned.setMchId(original.getMchId());
|
||||
cloned.setApiKey(original.getApiKey());
|
||||
cloned.setApiclientCert(original.getApiclientCert());
|
||||
cloned.setApiclientKey(original.getApiclientKey());
|
||||
cloned.setPubKey(original.getPubKey());
|
||||
cloned.setPubKeyId(original.getPubKeyId());
|
||||
cloned.setMerchantSerialNumber(original.getMerchantSerialNumber());
|
||||
cloned.setNotifyUrl(original.getNotifyUrl()); // 这个会被后续覆盖
|
||||
cloned.setComments(original.getComments());
|
||||
cloned.setSortNumber(original.getSortNumber());
|
||||
cloned.setStatus(original.getStatus());
|
||||
cloned.setDeleted(original.getDeleted());
|
||||
cloned.setTenantId(original.getTenantId());
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信支付配置(环境感知)
|
||||
*/
|
||||
public Payment getWechatPayConfig(Integer tenantId) {
|
||||
return getEnvironmentAwarePaymentConfig(0, tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付宝配置(环境感知)
|
||||
*/
|
||||
public Payment getAlipayConfig(Integer tenantId) {
|
||||
return getEnvironmentAwarePaymentConfig(1, tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前环境
|
||||
*/
|
||||
public String getCurrentEnvironment() {
|
||||
return activeProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为开发环境
|
||||
*/
|
||||
public boolean isDevelopmentEnvironment() {
|
||||
return "dev".equals(activeProfile) || "test".equals(activeProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为生产环境
|
||||
*/
|
||||
public boolean isProductionEnvironment() {
|
||||
return "prod".equals(activeProfile);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,12 @@
|
||||
<if test="param.couponId != null">
|
||||
AND a.coupon_id = #{param.couponId}
|
||||
</if>
|
||||
<if test="param.goodsId != null">
|
||||
AND a.goods_id = #{param.goodsId}
|
||||
</if>
|
||||
<if test="param.categoryId != null">
|
||||
AND a.category_id = #{param.categoryId}
|
||||
</if>
|
||||
<if test="param.type != null">
|
||||
AND a.type = #{param.type}
|
||||
</if>
|
||||
|
||||
@@ -28,8 +28,16 @@ public class ShopCouponApplyItemParam extends BaseParam {
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Integer couponId;
|
||||
|
||||
@Schema(description = "商品ID")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Boolean type;
|
||||
private Integer goodsId;
|
||||
|
||||
@Schema(description = "分类ID")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Integer categoryId;
|
||||
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "0服务1需求2闲置")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
|
||||
@@ -283,19 +283,33 @@ public class CouponStatusServiceImpl implements CouponStatusService {
|
||||
|
||||
if (userCoupon.getApplyRange() == ShopUserCoupon.APPLY_GOODS) {
|
||||
// 指定商品适用
|
||||
List<ShopCouponApplyItem> applyItems = shopCouponApplyItemService.list(
|
||||
new LambdaQueryWrapper<ShopCouponApplyItem>()
|
||||
.eq(ShopCouponApplyItem::getCouponId, userCoupon.getCouponId())
|
||||
.eq(ShopCouponApplyItem::getType, 1) // 类型1表示商品
|
||||
.isNotNull(ShopCouponApplyItem::getGoodsId)
|
||||
);
|
||||
try {
|
||||
List<ShopCouponApplyItem> applyItems = shopCouponApplyItemService.list(
|
||||
new LambdaQueryWrapper<ShopCouponApplyItem>()
|
||||
.eq(ShopCouponApplyItem::getCouponId, userCoupon.getCouponId())
|
||||
.eq(ShopCouponApplyItem::getType, 1) // 类型1表示商品
|
||||
);
|
||||
|
||||
List<Integer> applicableGoodsIds = applyItems.stream()
|
||||
.map(ShopCouponApplyItem::getGoodsId)
|
||||
.filter(goodsId -> goodsId != null)
|
||||
.collect(Collectors.toList());
|
||||
// 如果数据库中还没有 goods_id 字段,暂时使用 pk 字段作为商品ID
|
||||
List<Integer> applicableGoodsIds = applyItems.stream()
|
||||
.map(item -> {
|
||||
if (item.getGoodsId() != null) {
|
||||
return item.getGoodsId();
|
||||
} else if (item.getPk() != null) {
|
||||
// 临时方案:使用 pk 字段作为商品ID
|
||||
return item.getPk();
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(goodsId -> goodsId != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return goodsIds.stream().anyMatch(applicableGoodsIds::contains);
|
||||
return goodsIds.stream().anyMatch(applicableGoodsIds::contains);
|
||||
} catch (Exception e) {
|
||||
log.warn("查询优惠券适用商品失败,可能是数据库字段不存在: {}", e.getMessage());
|
||||
// 如果查询失败,默认返回true(允许使用)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (userCoupon.getApplyRange() == ShopUserCoupon.APPLY_CATEGORY) {
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
import com.gxwebsoft.common.core.config.CertificateProperties;
|
||||
import com.gxwebsoft.common.core.utils.*;
|
||||
import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
import com.gxwebsoft.common.core.service.EnvironmentAwarePaymentService;
|
||||
import com.gxwebsoft.common.core.config.SpringContextUtil;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
@@ -316,6 +318,7 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
||||
/**
|
||||
* 读取微信支付配置
|
||||
* 生产环境优先从缓存读取 Payment:1* 格式的商户信息
|
||||
* 开发环境自动使用本地回调地址
|
||||
*
|
||||
* @param order
|
||||
* @return
|
||||
@@ -324,7 +327,18 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
||||
// 先清除可能的错误缓存
|
||||
// paymentCacheService.removePaymentConfig(order.getPayType().toString(), order.getTenantId());
|
||||
|
||||
Payment payment = paymentCacheService.getPaymentConfig(order.getPayType(), order.getTenantId());
|
||||
// 使用环境感知的支付配置服务
|
||||
Payment payment;
|
||||
try {
|
||||
// 尝试使用环境感知服务
|
||||
EnvironmentAwarePaymentService envPaymentService =
|
||||
SpringContextUtil.getBean(EnvironmentAwarePaymentService.class);
|
||||
payment = envPaymentService.getEnvironmentAwarePaymentConfig(order.getPayType(), order.getTenantId());
|
||||
} catch (Exception e) {
|
||||
// 如果环境感知服务不可用,回退到原有方式
|
||||
log.warn("环境感知支付服务不可用,使用原有配置方式: {}", e.getMessage());
|
||||
payment = paymentCacheService.getPaymentConfig(order.getPayType(), order.getTenantId());
|
||||
}
|
||||
|
||||
// 添加详细的支付配置检查
|
||||
System.out.println("=== 支付配置检查 ===");
|
||||
|
||||
Reference in New Issue
Block a user