feat(dev): 添加开发环境支付配置和优惠券字段修复功能

- 新增开发环境控制器和环境感知支付服务
- 添加数据库字段缺失修复指南
- 改进优惠券适用商品查询逻辑
-优化支付配置获取方式
This commit is contained in:
2025-08-15 02:47:02 +08:00
parent b2e0aa9f28
commit fa83ef5967
14 changed files with 1535 additions and 13 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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("=== 支付配置检查 ===");