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

View File

@@ -187,3 +187,19 @@ coupon:
auto-update: true
# 批量处理大小
batch-size: 1000
# 支付配置
payment:
# 开发环境配置
dev:
# 开发环境回调地址(本地调试用)
notify-url: "http://frps-10550.s209.websoft.top/api/shop/shop-order/notify"
# 开发环境是否启用环境感知
environment-aware: true
# 生产环境配置
prod:
# 生产环境回调地址
notify-url: "https://cms-api.websoft.top/api/shop/shop-order/notify"
# 生产环境是否启用环境感知
environment-aware: false

View File

@@ -0,0 +1,206 @@
-- 创建开发专用租户和支付配置
-- 用于隔离开发环境和生产环境的支付回调地址
-- ========================================
-- 1. 创建开发专用租户(如果不存在)
-- ========================================
-- 检查是否已存在开发租户
SELECT 'Checking for dev tenant...' as status;
-- 插入开发租户租户ID使用 9999 避免与生产冲突)
INSERT IGNORE INTO sys_tenant (
tenant_id,
tenant_name,
tenant_code,
contact_person,
contact_phone,
contact_email,
status,
deleted,
create_time,
update_time,
comments
) VALUES (
9999,
'开发测试租户',
'DEV_TENANT',
'开发者',
'13800000000',
'dev@websoft.top',
1,
0,
NOW(),
NOW(),
'专用于开发环境测试,不影响生产环境'
);
-- ========================================
-- 2. 创建开发环境专用支付配置
-- ========================================
-- 微信支付开发配置
INSERT IGNORE INTO sys_payment (
name,
type,
code,
image,
wechat_type,
app_id,
mch_id,
api_key,
apiclient_cert,
apiclient_key,
pub_key,
pub_key_id,
merchant_serial_number,
notify_url,
comments,
sort_number,
status,
deleted,
tenant_id,
create_time,
update_time
) VALUES (
'微信支付-开发环境',
0, -- 微信支付
'wechat_dev',
'/static/images/wechat_pay.png',
1, -- 普通商户
'wx1234567890abcdef', -- 开发环境AppID
'1234567890', -- 开发环境商户号
'your_dev_api_key_32_characters_long',
'dev/wechat/apiclient_cert.pem',
'dev/wechat/apiclient_key.pem',
'dev/wechat/wechatpay_cert.pem',
'your_pub_key_id',
'your_merchant_serial_number',
'http://frps-10550.s209.websoft.top/api/shop/shop-order/notify', -- 开发环境回调地址
'开发环境专用配置,使用本地回调地址',
1,
1, -- 启用
0, -- 未删除
9999, -- 开发租户ID
NOW(),
NOW()
);
-- 支付宝开发配置
INSERT IGNORE INTO sys_payment (
name,
type,
code,
app_id,
mch_id,
api_key,
notify_url,
comments,
sort_number,
status,
deleted,
tenant_id,
create_time,
update_time
) VALUES (
'支付宝-开发环境',
1, -- 支付宝
'alipay_dev',
'your_dev_alipay_app_id',
'your_dev_alipay_mch_id',
'your_dev_alipay_private_key',
'http://frps-10550.s209.websoft.top/api/shop/shop-order/notify', -- 开发环境回调地址
'开发环境专用支付宝配置',
2,
1, -- 启用
0, -- 未删除
9999, -- 开发租户ID
NOW(),
NOW()
);
-- ========================================
-- 3. 创建开发环境用户(可选)
-- ========================================
-- 创建开发专用用户
INSERT IGNORE INTO sys_user (
user_id,
username,
password,
nickname,
avatar,
sex,
phone,
email,
email_verified,
real_name,
id_card,
birthday,
department_id,
status,
deleted,
tenant_id,
create_time,
update_time,
comments
) VALUES (
99999,
'dev_user',
'$2a$10$yKTnKzKqKqKqKqKqKqKqKOKqKqKqKqKqKqKqKqKqKqKqKqKqKqKqK', -- 密码: dev123456
'开发测试用户',
'/static/images/default_avatar.png',
1,
'13800000001',
'dev_user@websoft.top',
1,
'开发者',
'000000000000000000',
'1990-01-01',
1,
0, -- 正常状态
0, -- 未删除
9999, -- 开发租户ID
NOW(),
NOW(),
'开发环境专用测试用户'
);
-- ========================================
-- 4. 验证创建结果
-- ========================================
-- 检查租户创建结果
SELECT
'Tenant Check' as check_type,
tenant_id,
tenant_name,
tenant_code,
status
FROM sys_tenant
WHERE tenant_id = 9999;
-- 检查支付配置创建结果
SELECT
'Payment Config Check' as check_type,
id,
name,
type,
notify_url,
tenant_id,
status
FROM sys_payment
WHERE tenant_id = 9999;
-- 检查用户创建结果
SELECT
'User Check' as check_type,
user_id,
username,
nickname,
tenant_id,
status
FROM sys_user
WHERE tenant_id = 9999;
SELECT '开发环境配置创建完成!' as result;

View File

@@ -0,0 +1,101 @@
-- 修复优惠券适用商品表字段缺失问题
-- 作者: WebSoft
-- 日期: 2025-01-15
-- 说明: 添加缺失的 goods_id 和 category_id 字段
-- ========================================
-- 1. 检查表是否存在
-- ========================================
SELECT 'Checking table existence...' as status;
-- ========================================
-- 2. 添加缺失的字段
-- ========================================
-- 添加 goods_id 字段(如果不存在)
SET @sql = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'shop_coupon_apply_item'
AND COLUMN_NAME = 'goods_id') = 0,
'ALTER TABLE shop_coupon_apply_item ADD COLUMN goods_id INT(11) NULL COMMENT "商品ID" AFTER coupon_id;',
'SELECT "goods_id column already exists" as result;'
));
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 添加 category_id 字段(如果不存在)
SET @sql = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'shop_coupon_apply_item'
AND COLUMN_NAME = 'category_id') = 0,
'ALTER TABLE shop_coupon_apply_item ADD COLUMN category_id INT(11) NULL COMMENT "分类ID" AFTER goods_id;',
'SELECT "category_id column already exists" as result;'
));
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ========================================
-- 3. 更新现有数据(如果需要)
-- ========================================
-- 如果表中有数据但 goods_id 为空,可以根据业务逻辑设置默认值
-- 这里只是示例,实际需要根据业务需求调整
UPDATE shop_coupon_apply_item
SET goods_id = pk
WHERE goods_id IS NULL AND type = 1 AND pk IS NOT NULL;
-- ========================================
-- 4. 添加索引优化查询性能
-- ========================================
-- 添加 goods_id 索引
CREATE INDEX IF NOT EXISTS idx_coupon_apply_item_goods ON shop_coupon_apply_item(coupon_id, goods_id);
-- 添加 category_id 索引
CREATE INDEX IF NOT EXISTS idx_coupon_apply_item_category ON shop_coupon_apply_item(coupon_id, category_id);
-- 添加类型索引
CREATE INDEX IF NOT EXISTS idx_coupon_apply_item_type ON shop_coupon_apply_item(coupon_id, type);
-- ========================================
-- 5. 验证修复结果
-- ========================================
-- 检查表结构
SELECT
COLUMN_NAME,
DATA_TYPE,
IS_NULLABLE,
COLUMN_DEFAULT,
COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'shop_coupon_apply_item'
ORDER BY ORDINAL_POSITION;
-- 检查数据
SELECT
'Data check' as check_type,
COUNT(*) as total_records,
COUNT(goods_id) as records_with_goods_id,
COUNT(category_id) as records_with_category_id
FROM shop_coupon_apply_item;
-- ========================================
-- 6. 创建示例数据(可选)
-- ========================================
-- 如果表为空,插入一些示例数据用于测试
INSERT IGNORE INTO shop_coupon_apply_item
(coupon_id, goods_id, category_id, type, status, tenant_id, create_time, update_time)
VALUES
(1, 1, NULL, 1, 0, 1, NOW(), NOW()),
(1, 2, NULL, 1, 0, 1, NOW(), NOW()),
(2, NULL, 1, 2, 0, 1, NOW(), NOW()),
(2, NULL, 2, 2, 0, 1, NOW(), NOW());
SELECT 'Database fix completed successfully!' as result;

View File

@@ -0,0 +1,183 @@
-- 生产环境安全的支付配置脚本
-- 此脚本可以安全地在生产数据库执行
-- 不会创建测试数据,只添加必要的配置支持
-- ========================================
-- 1. 检查当前环境(手动确认)
-- ========================================
SELECT
'请确认这是您要修改的数据库' as warning,
DATABASE() as current_database,
NOW() as execution_time;
-- 暂停执行,让用户确认
-- 如果确认无误,请删除下面这行注释继续执行
-- SELECT 'Please confirm this is the correct database before proceeding' as confirmation_required;
-- ========================================
-- 2. 添加支付配置表字段(如果不存在)
-- ========================================
-- 检查是否需要添加环境标识字段
SELECT
CASE
WHEN COUNT(*) = 0 THEN '需要添加environment字段'
ELSE '环境字段已存在'
END as environment_field_status
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'sys_payment'
AND COLUMN_NAME = 'environment';
-- 添加环境标识字段(如果不存在)
SET @sql = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'sys_payment'
AND COLUMN_NAME = 'environment') = 0,
'ALTER TABLE sys_payment ADD COLUMN environment VARCHAR(20) DEFAULT "prod" COMMENT "环境标识(dev/test/prod)" AFTER tenant_id;',
'SELECT "environment column already exists" as result;'
));
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ========================================
-- 3. 为现有支付配置添加环境标识
-- ========================================
-- 将现有配置标记为生产环境
UPDATE sys_payment
SET environment = 'prod'
WHERE environment IS NULL OR environment = '';
-- ========================================
-- 4. 创建开发环境配置的安全方式
-- ========================================
-- 方式1复制现有生产配置作为开发模板推荐
-- 注意这里使用您现有的租户ID不创建新租户
-- 获取当前生产环境的微信支付配置
SELECT
'当前微信支付配置' as config_type,
id,
name,
app_id,
mch_id,
notify_url,
tenant_id,
environment
FROM sys_payment
WHERE type = 0 AND status = 1 AND deleted = 0
ORDER BY id DESC LIMIT 1;
-- 获取当前生产环境的支付宝配置
SELECT
'当前支付宝配置' as config_type,
id,
name,
app_id,
mch_id,
notify_url,
tenant_id,
environment
FROM sys_payment
WHERE type = 1 AND status = 1 AND deleted = 0
ORDER BY id DESC LIMIT 1;
-- ========================================
-- 5. 手动创建开发配置的SQL模板
-- ========================================
-- 以下SQL需要您手动修改参数后执行
-- 请根据上面查询的结果,修改相应的参数
/*
-- 微信支付开发配置模板(请修改参数后执行)
INSERT INTO sys_payment (
name, type, code, image, wechat_type,
app_id, mch_id, api_key, apiclient_cert, apiclient_key,
pub_key, pub_key_id, merchant_serial_number, notify_url,
comments, sort_number, status, deleted, tenant_id, environment,
create_time, update_time
) VALUES (
'微信支付-开发环境',
0, -- 微信支付
'wechat_dev',
'/static/images/wechat_pay.png',
1, -- 普通商户
'YOUR_DEV_APP_ID', -- 请替换为您的开发环境AppID
'YOUR_DEV_MCH_ID', -- 请替换为您的开发环境商户号
'YOUR_DEV_API_KEY', -- 请替换为您的开发环境API密钥
'dev/wechat/apiclient_cert.pem',
'dev/wechat/apiclient_key.pem',
'dev/wechat/wechatpay_cert.pem',
'YOUR_DEV_PUB_KEY_ID',
'YOUR_DEV_MERCHANT_SERIAL',
'http://frps-10550.s209.websoft.top/api/shop/shop-order/notify', -- 开发环境回调
'开发环境专用配置',
1,
1, -- 启用
0, -- 未删除
YOUR_TENANT_ID, -- 请替换为您的租户ID
'dev', -- 开发环境标识
NOW(),
NOW()
);
*/
-- ========================================
-- 6. 更安全的方案:仅更新现有配置的回调地址
-- ========================================
-- 查看当前回调地址
SELECT
'当前回调地址检查' as check_type,
id,
name,
type,
notify_url,
tenant_id,
environment
FROM sys_payment
WHERE status = 1 AND deleted = 0;
-- 如果您只是想临时修改回调地址进行调试可以使用以下SQL
-- 注意:请先备份原始配置!
/*
-- 备份当前配置
CREATE TABLE IF NOT EXISTS sys_payment_backup AS
SELECT *, NOW() as backup_time FROM sys_payment WHERE status = 1;
-- 临时修改回调地址(请谨慎使用)
UPDATE sys_payment
SET notify_url = 'http://frps-10550.s209.websoft.top/api/shop/shop-order/notify'
WHERE type = 0 AND tenant_id = YOUR_TENANT_ID AND status = 1;
-- 恢复原始配置的SQL调试完成后执行
UPDATE sys_payment
SET notify_url = 'https://cms-api.websoft.top/api/shop/shop-order/notify'
WHERE type = 0 AND tenant_id = YOUR_TENANT_ID AND status = 1;
*/
-- ========================================
-- 7. 验证配置
-- ========================================
-- 检查所有支付配置
SELECT
'最终配置检查' as check_type,
id,
name,
type,
notify_url,
tenant_id,
environment,
status
FROM sys_payment
WHERE deleted = 0
ORDER BY tenant_id, type;
SELECT '生产环境安全配置完成!请根据注释中的模板手动创建开发配置。' as result;

View File

@@ -0,0 +1,17 @@
-- 简单修复优惠券适用商品表字段缺失问题
-- 执行前请备份数据库
-- 1. 添加缺失的字段
ALTER TABLE shop_coupon_apply_item
ADD COLUMN IF NOT EXISTS goods_id INT(11) NULL COMMENT '商品ID' AFTER coupon_id;
ALTER TABLE shop_coupon_apply_item
ADD COLUMN IF NOT EXISTS category_id INT(11) NULL COMMENT '分类ID' AFTER goods_id;
-- 2. 添加索引
CREATE INDEX IF NOT EXISTS idx_coupon_apply_item_goods ON shop_coupon_apply_item(coupon_id, goods_id);
CREATE INDEX IF NOT EXISTS idx_coupon_apply_item_category ON shop_coupon_apply_item(coupon_id, category_id);
CREATE INDEX IF NOT EXISTS idx_coupon_apply_item_type ON shop_coupon_apply_item(coupon_id, type);
-- 3. 检查表结构
DESCRIBE shop_coupon_apply_item;