feat(core):优化中文字体支持和证书生成功能

- 在BszxBmServiceImpl和BszxPayServiceImpl中增强字体检测逻辑,确保正确显示中文
- 调整macOS字体优先级,优先使用PingFang SC等系统字体- 添加多层字体回退机制,包括预定义字体、逻辑字体和错误提示
- 更新Dockerfile,安装文泉驿微米黑字体以支持中文显示- 添加中文乱码修复指南文档,提供三种修复方案和故障排查方法
- 创建fix-chinese-font.sh脚本,用于在运行中的容器内安装中文字体
- 删除不再使用的SQL脚本和证书检查脚本- 改进日志输出,提供更详细的字体加载信息和错误提示
This commit is contained in:
2025-10-30 14:46:53 +08:00
parent 3bb1e8f6ce
commit 6036869645
14 changed files with 415 additions and 1109 deletions

View File

@@ -10,8 +10,18 @@ RUN mkdir -p /app/logs
# 创建上传文件目录
RUN mkdir -p /app/uploads
# 安装wget用于健康检查并添加应用用户安全考虑
RUN apk add --no-cache wget && \
# 安装必要工具和中文字体支持
# fontconfig: 字体配置库
# ttf-dejavu: 包含DejaVu字体支持中文显示
# wqy-zenhei: 文泉驿正黑字体,开源中文字体
RUN apk add --no-cache wget fontconfig ttf-dejavu && \
# 下载并安装文泉驿微米黑字体(更好的中文支持)
wget -O /tmp/wqy-microhei.ttc https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc && \
mkdir -p /usr/share/fonts/truetype/wqy && \
mv /tmp/wqy-microhei.ttc /usr/share/fonts/truetype/wqy/ && \
# 刷新字体缓存
fc-cache -fv && \
# 创建应用用户(安全考虑)
addgroup -g 1000 appgroup && \
adduser -D -u 1000 -G appgroup appuser

View File

@@ -0,0 +1,222 @@
# 捐款证书中文乱码修复指南
## 问题描述
生成的捐款证书图片中,中文字符显示为乱码或方块,影响证书的正常使用。
问题示例https://file-s209.shoplnk.cn/poster/10547/pay/2536.jpg?v=0409
## 根本原因
1. **系统缺少中文字体**Docker容器Alpine Linux默认不包含中文字体
2. **字体回退机制不完善**:当找不到中文字体时,回退到的默认字体不支持中文
3. **字体检测不够严格**:原代码只检测单个汉字,可能导致误判
## 修复方案
### 方案一重新构建Docker镜像推荐
#### 1. 使用更新后的Dockerfile
项目的 `Dockerfile` 已经更新,新增了中文字体支持:
```dockerfile
# 安装必要工具和中文字体支持
RUN apk add --no-cache wget fontconfig ttf-dejavu && \
# 下载并安装文泉驿微米黑字体(更好的中文支持)
wget -O /tmp/wqy-microhei.ttc https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc && \
mkdir -p /usr/share/fonts/truetype/wqy && \
mv /tmp/wqy-microhei.ttc /usr/share/fonts/truetype/wqy/ && \
# 刷新字体缓存
fc-cache -fv
```
#### 2. 重新构建镜像
```bash
# 打包项目
mvn clean package -DskipTests
# 构建Docker镜像
docker build -t websoft-api:latest .
# 停止旧容器
docker stop websoft-api-container
# 删除旧容器
docker rm websoft-api-container
# 启动新容器
docker run -d \
--name websoft-api-container \
-p 9200:9200 \
-v /path/to/uploads:/app/uploads \
-v /path/to/logs:/app/logs \
-e SPRING_PROFILES_ACTIVE=prod \
websoft-api:latest
```
### 方案二:在运行中的容器内安装字体
如果不方便重新构建镜像,可以在运行中的容器内安装字体:
```bash
# 进入容器
docker exec -it -u root websoft-api-container sh
# 安装字体工具
apk add --no-cache fontconfig ttf-dejavu
# 下载中文字体
wget -O /tmp/wqy-microhei.ttc https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc
# 创建字体目录
mkdir -p /usr/share/fonts/truetype/wqy
# 移动字体文件
mv /tmp/wqy-microhei.ttc /usr/share/fonts/truetype/wqy/
# 刷新字体缓存
fc-cache -fv
# 验证字体安装
fc-list :lang=zh
# 退出容器
exit
# 重启应用(不重启容器)
docker exec websoft-api-container pkill -f "java.*app.jar"
```
### 方案三:使用其他基础镜像
如果Alpine Linux的字体支持仍有问题可以考虑切换到Debian基础镜像
```dockerfile
FROM eclipse-temurin:17-jdk
# 安装中文字体
RUN apt-get update && \
apt-get install -y --no-install-recommends \
fonts-wqy-microhei \
fonts-wqy-zenhei \
fontconfig && \
fc-cache -fv && \
rm -rf /var/lib/apt/lists/*
```
## 代码改进说明
### 1. 增强的字体检测逻辑
```java
// 检查多个中文字符,确保字体真正支持中文
if (font.canDisplay('中') && font.canDisplay('文')) {
return font;
}
```
### 2. macOS字体优先级调整
```java
String[] chineseFonts = {
"PingFang SC", // 苹方 (macOS) - 优先使用
"STHeiti", // 华文黑体 (macOS)
"Hiragino Sans GB", // 冬青黑体 (macOS)
"Microsoft YaHei", // 微软雅黑 (Windows)
// ...
};
```
### 3. 多层字体回退机制
```java
// 第一层:预定义字体列表
// 第二层Java逻辑字体SansSerif, Serif等
// 第三层:明确的错误提示
```
### 4. 详细的日志输出
```java
System.out.println("✓ 成功使用字体: " + fontName + " (字号: " + fontSize + ")");
System.err.println("✗ 严重警告:系统中没有找到任何支持中文的字体!");
```
## 验证修复
### 1. 查看容器日志
```bash
docker logs -f websoft-api-container
```
成功加载字体时会看到:
```
✓ 成功使用字体: PingFang SC (字号: 26)
✓ 成功使用字体: PingFang SC (字号: 22)
```
### 2. 测试证书生成
调用API生成新的证书检查中文是否正常显示。
### 3. 验证字体列表
在容器内执行:
```bash
fc-list :lang=zh
```
应该能看到已安装的中文字体列表。
## macOS开发环境说明
如果在macOS上开发系统已经包含以下中文字体
- PingFang SC苹方推荐
- STHeiti华文黑体
- Hiragino Sans GB冬青黑体
代码已优化为优先使用这些字体。
## 故障排查
### 问题1Docker镜像构建失败
**原因**:网络问题导致字体下载失败
**解决**
```bash
# 使用国内镜像或手动下载字体
wget https://ghproxy.com/https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc
```
### 问题2容器重启后字体丢失
**原因**:使用方案二安装的字体未持久化
**解决**:使用方案一重新构建镜像
### 问题3日志显示找不到字体
**原因**:字体安装未生效或路径错误
**解决**
```bash
# 检查字体文件是否存在
docker exec websoft-api-container ls -la /usr/share/fonts/truetype/wqy/
# 重建字体缓存
docker exec -u root websoft-api-container fc-cache -fv
```
### 问题4仍然显示乱码
**原因**:可能是图片已缓存
**解决**
1. 清除浏览器缓存
2. 删除旧的证书图片文件
3. 重新生成证书URL参数v会自动更新
## 性能优化建议
1. **字体缓存**首次加载后字体会被JVM缓存后续生成速度更快
2. **字体大小**文泉驿微米黑字体文件约10MB不会显著增加镜像大小
3. **镜像层优化**字体安装合并到一个RUN命令中减少镜像层数
## 相关文件
- `/src/main/java/com/gxwebsoft/bszx/service/impl/BszxPayServiceImpl.java` - 捐款证书生成逻辑
- `/src/main/java/com/gxwebsoft/bszx/service/impl/BszxBmServiceImpl.java` - 报名证书生成逻辑
- `/Dockerfile` - Docker镜像构建配置
## 技术支持
如果问题仍未解决,请提供以下信息:
1. Docker镜像构建日志
2. 容器启动日志(包含字体加载信息)
3. 生成的证书图片链接
4. 系统环境信息(`docker version`, `docker info`

125
scripts/fix-chinese-font.sh Normal file
View File

@@ -0,0 +1,125 @@
#!/bin/bash
###############################################################################
# 捐款证书中文乱码修复脚本
# 用途在运行中的Docker容器内安装中文字体
# 适用于:无法重新构建镜像的紧急情况
###############################################################################
set -e # 遇到错误立即退出
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 容器名称(可根据实际情况修改)
CONTAINER_NAME="websoft-api-container"
echo -e "${GREEN}================================${NC}"
echo -e "${GREEN}中文字体修复脚本${NC}"
echo -e "${GREEN}================================${NC}"
echo ""
# 检查容器是否存在
echo -e "${YELLOW}步骤 1/6: 检查容器状态...${NC}"
if ! docker ps | grep -q "$CONTAINER_NAME"; then
echo -e "${RED}错误:容器 $CONTAINER_NAME 未运行!${NC}"
echo "当前运行的容器:"
docker ps --format "table {{.Names}}\t{{.Status}}"
echo ""
read -p "请输入正确的容器名称: " CONTAINER_NAME
if [ -z "$CONTAINER_NAME" ]; then
echo -e "${RED}容器名称不能为空,退出。${NC}"
exit 1
fi
fi
echo -e "${GREEN}✓ 容器正在运行${NC}"
echo ""
# 检查是否已安装字体
echo -e "${YELLOW}步骤 2/6: 检查是否已安装中文字体...${NC}"
if docker exec "$CONTAINER_NAME" fc-list :lang=zh 2>/dev/null | grep -q "WenQuanYi"; then
echo -e "${GREEN}✓ 中文字体已安装${NC}"
docker exec "$CONTAINER_NAME" fc-list :lang=zh
echo ""
read -p "是否重新安装?(y/N): " REINSTALL
if [[ ! "$REINSTALL" =~ ^[Yy]$ ]]; then
echo "跳过安装,退出。"
exit 0
fi
else
echo -e "${YELLOW}未检测到中文字体,开始安装...${NC}"
fi
echo ""
# 安装字体工具
echo -e "${YELLOW}步骤 3/6: 安装字体工具...${NC}"
docker exec -u root "$CONTAINER_NAME" sh -c "apk add --no-cache fontconfig ttf-dejavu wget" || {
echo -e "${RED}✗ 字体工具安装失败${NC}"
exit 1
}
echo -e "${GREEN}✓ 字体工具安装成功${NC}"
echo ""
# 下载中文字体
echo -e "${YELLOW}步骤 4/6: 下载文泉驿微米黑字体...${NC}"
echo "正在从GitHub下载约10MB请稍候..."
docker exec -u root "$CONTAINER_NAME" sh -c "
wget -O /tmp/wqy-microhei.ttc https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc 2>&1 | grep -E 'Connecting|Length|saved' || true
" || {
echo -e "${YELLOW}GitHub下载失败尝试使用代理...${NC}"
docker exec -u root "$CONTAINER_NAME" sh -c "
wget -O /tmp/wqy-microhei.ttc https://ghproxy.com/https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc
" || {
echo -e "${RED}✗ 字体下载失败${NC}"
echo "请检查网络连接或手动下载字体文件。"
exit 1
}
}
echo -e "${GREEN}✓ 字体下载成功${NC}"
echo ""
# 安装字体
echo -e "${YELLOW}步骤 5/6: 安装字体文件...${NC}"
docker exec -u root "$CONTAINER_NAME" sh -c "
mkdir -p /usr/share/fonts/truetype/wqy && \
mv /tmp/wqy-microhei.ttc /usr/share/fonts/truetype/wqy/ && \
fc-cache -fv
" || {
echo -e "${RED}✗ 字体安装失败${NC}"
exit 1
}
echo -e "${GREEN}✓ 字体安装成功${NC}"
echo ""
# 验证安装
echo -e "${YELLOW}步骤 6/6: 验证字体安装...${NC}"
FONT_COUNT=$(docker exec "$CONTAINER_NAME" fc-list :lang=zh | wc -l)
if [ "$FONT_COUNT" -gt 0 ]; then
echo -e "${GREEN}✓ 中文字体验证成功!${NC}"
echo "已安装的中文字体:"
docker exec "$CONTAINER_NAME" fc-list :lang=zh
else
echo -e "${RED}✗ 字体验证失败${NC}"
exit 1
fi
echo ""
# 完成提示
echo -e "${GREEN}================================${NC}"
echo -e "${GREEN}修复完成!${NC}"
echo -e "${GREEN}================================${NC}"
echo ""
echo "后续步骤:"
echo "1. 不需要重启容器,字体已生效"
echo "2. 重新生成捐款证书即可看到效果"
echo "3. 如果仍有问题,请查看容器日志:"
echo " docker logs -f $CONTAINER_NAME"
echo ""
echo -e "${YELLOW}注意:${NC}"
echo "- 此修复方法在容器重启后会失效"
echo "- 建议后续使用更新后的Dockerfile重新构建镜像"
echo "- 详细文档请参考docs/chinese-font-fix-guide.md"
echo ""

View File

@@ -184,32 +184,51 @@ public class BszxBmServiceImpl extends ServiceImpl<BszxBmMapper, BszxBm> impleme
try {
// 尝试使用系统中文字体
String[] chineseFonts = {
"Alibaba PuHuiTi 2.0",
"PingFang SC", // 苹方 (macOS) - 优先使用
"STHeiti", // 华文黑体 (macOS)
"Hiragino Sans GB", // 冬青黑体 (macOS)
"Microsoft YaHei", // 微软雅黑 (Windows)
"SimHei", // 黑体 (Windows)
"SimSun", // 宋体 (Windows)
"PingFang SC", // 苹方 (macOS)
"Hiragino Sans GB", // 冬青黑体 (macOS)
"WenQuanYi Micro Hei", // 文泉驿微米黑 (Linux)
"Noto Sans CJK SC", // 思源黑体 (Linux)
"Arial Unicode MS", // 支持Unicode的Arial
"DejaVu Sans" // 备用字体
};
for (String fontName : chineseFonts) {
Font font = new Font(fontName, Font.PLAIN, fontSize);
// 检查字体是否能正确显示中文
if (font.canDisplay('中')) {
System.out.println("使用字体: " + fontName);
if (font.canDisplay('中') && font.canDisplay('文')) {
System.out.println("✓ 成功使用字体: " + fontName + " (字号: " + fontSize + ")");
return font;
} else {
System.out.println("✗ 字体不支持中文: " + fontName);
}
}
// 如果没有找到合适的字体,尝试加载系统默认中文字体
System.out.println("⚠ 警告:未找到预定义的中文字体,尝试使用系统默认字体");
// 尝试使用逻辑字体名称这些在Java中通常会映射到系统字体
String[] logicalFonts = {"SansSerif", "Serif", "Monospaced", "Dialog", "DialogInput"};
for (String logicalFont : logicalFonts) {
Font font = new Font(logicalFont, Font.PLAIN, fontSize);
if (font.canDisplay('中') && font.canDisplay('文')) {
System.out.println("✓ 使用逻辑字体: " + logicalFont);
return font;
}
}
// 如果没有找到合适的字体,使用默认字体
System.out.println("警告:未找到支持中文的字体,使用默认字体");
return new Font(Font.SANS_SERIF, Font.PLAIN, fontSize);
// 最后的备选方案
System.err.println("❌ 严重警告:系统中没有找到任何支持中文的字体!请安装中文字体包。");
return new Font("SansSerif", Font.PLAIN, fontSize);
} catch (Exception e) {
System.err.println("创建中文字体失败: " + e.getMessage());
return new Font(Font.SANS_SERIF, Font.PLAIN, fontSize);
System.err.println("创建中文字体失败: " + e.getMessage());
e.printStackTrace();
return new Font("SansSerif", Font.PLAIN, fontSize);
}
}

View File

@@ -238,32 +238,51 @@ public class BszxPayServiceImpl extends ServiceImpl<BszxPayMapper, BszxPay> impl
try {
// 尝试使用系统中文字体
String[] chineseFonts = {
"Alibaba PuHuiTi 2.0",
"PingFang SC", // 苹方 (macOS) - 优先使用
"STHeiti", // 华文黑体 (macOS)
"Hiragino Sans GB", // 冬青黑体 (macOS)
"Microsoft YaHei", // 微软雅黑 (Windows)
"SimHei", // 黑体 (Windows)
"SimSun", // 宋体 (Windows)
"PingFang SC", // 苹方 (macOS)
"Hiragino Sans GB", // 冬青黑体 (macOS)
"WenQuanYi Micro Hei", // 文泉驿微米黑 (Linux)
"Noto Sans CJK SC", // 思源黑体 (Linux)
"Arial Unicode MS", // 支持Unicode的Arial
"DejaVu Sans" // 备用字体
};
for (String fontName : chineseFonts) {
Font font = new Font(fontName, Font.PLAIN, fontSize);
// 检查字体是否能正确显示中文
if (font.canDisplay('中')) {
System.out.println("使用字体: " + fontName);
if (font.canDisplay('中') && font.canDisplay('文')) {
System.out.println("✓ 成功使用字体: " + fontName + " (字号: " + fontSize + ")");
return font;
} else {
System.out.println("✗ 字体不支持中文: " + fontName);
}
}
// 如果没有找到合适的字体,尝试加载系统默认中文字体
System.out.println("⚠ 警告:未找到预定义的中文字体,尝试使用系统默认字体");
// 尝试使用逻辑字体名称这些在Java中通常会映射到系统字体
String[] logicalFonts = {"SansSerif", "Serif", "Monospaced", "Dialog", "DialogInput"};
for (String logicalFont : logicalFonts) {
Font font = new Font(logicalFont, Font.PLAIN, fontSize);
if (font.canDisplay('中') && font.canDisplay('文')) {
System.out.println("✓ 使用逻辑字体: " + logicalFont);
return font;
}
}
// 如果没有找到合适的字体,使用默认字体
System.out.println("警告:未找到支持中文的字体,使用默认字体");
return new Font(Font.SANS_SERIF, Font.PLAIN, fontSize);
// 最后的备选方案
System.err.println("❌ 严重警告:系统中没有找到任何支持中文的字体!请安装中文字体包。");
return new Font("SansSerif", Font.PLAIN, fontSize);
} catch (Exception e) {
System.err.println("创建中文字体失败: " + e.getMessage());
return new Font(Font.SANS_SERIF, Font.PLAIN, fontSize);
System.err.println("创建中文字体失败: " + e.getMessage());
e.printStackTrace();
return new Font("SansSerif", Font.PLAIN, fontSize);
}
}
}

View File

@@ -1,70 +0,0 @@
#!/bin/bash
# 检查证书文件路径的脚本
echo "=== 检查证书文件路径 ==="
# 基础路径
CERT_ROOT="/www/wwwroot/file.ws"
FILE_BASE="$CERT_ROOT"
echo "证书根路径: $CERT_ROOT"
echo "文件基础路径: $FILE_BASE"
# 检查根目录
if [ -d "$CERT_ROOT" ]; then
echo "✓ 证书根目录存在: $CERT_ROOT"
else
echo "✗ 证书根目录不存在: $CERT_ROOT"
fi
# 检查file目录
if [ -d "$FILE_BASE" ]; then
echo "✓ 文件基础目录存在: $FILE_BASE"
else
echo "✗ 文件基础目录不存在: $FILE_BASE"
fi
# 列出file目录下的内容
echo ""
echo "=== file目录内容 ==="
if [ -d "$FILE_BASE" ]; then
ls -la "$FILE_BASE"
else
echo "file目录不存在"
fi
# 查找所有.pem文件
echo ""
echo "=== 查找所有.pem文件 ==="
find "$CERT_ROOT" -name "*.pem" -type f 2>/dev/null | head -20
# 查找所有.p12文件
echo ""
echo "=== 查找所有.p12文件 ==="
find "$CERT_ROOT" -name "*.p12" -type f 2>/dev/null | head -20
# 检查特定文件
SPECIFIC_FILE="/www/wwwroot/file.ws/20250727/c27fe16e08314431a56c3489818af64f.pem"
echo ""
echo "=== 检查特定文件 ==="
echo "文件路径: $SPECIFIC_FILE"
if [ -f "$SPECIFIC_FILE" ]; then
echo "✓ 文件存在"
echo "文件大小: $(stat -c%s "$SPECIFIC_FILE") bytes"
echo "文件权限: $(stat -c%A "$SPECIFIC_FILE")"
echo "文件所有者: $(stat -c%U:%G "$SPECIFIC_FILE")"
# 检查文件内容
echo ""
echo "文件前3行:"
head -3 "$SPECIFIC_FILE"
echo "..."
echo "文件后3行:"
tail -3 "$SPECIFIC_FILE"
else
echo "✗ 文件不存在"
fi
echo ""
echo "=== 检查完成 ==="

View File

@@ -1,194 +0,0 @@
-- 优惠券状态管理优化SQL脚本
-- 作者: WebSoft
-- 日期: 2025-01-15
-- 说明: 优化优惠券查询性能,添加必要的索引
-- ========================================
-- 1. 添加索引优化查询性能
-- ========================================
-- 用户优惠券表索引优化
CREATE INDEX IF NOT EXISTS idx_user_coupon_status ON shop_user_coupon(user_id, status, expire_time);
CREATE INDEX IF NOT EXISTS idx_user_coupon_expire ON shop_user_coupon(expire_time) WHERE status = 0;
CREATE INDEX IF NOT EXISTS idx_user_coupon_order ON shop_user_coupon(order_id) WHERE status = 1;
-- 优惠券模板表索引优化
CREATE INDEX IF NOT EXISTS idx_coupon_status_expire ON shop_coupon(status, expire_type, end_time);
-- ========================================
-- 2. 统一状态字段(如果需要数据迁移)
-- ========================================
-- 检查现有数据的状态一致性
SELECT
'状态一致性检查' as check_item,
COUNT(*) as total_count,
SUM(CASE WHEN status = 0 AND is_use = 0 AND is_expire = 0 THEN 1 ELSE 0 END) as available_count,
SUM(CASE WHEN status = 1 AND is_use = 1 THEN 1 ELSE 0 END) as used_count,
SUM(CASE WHEN status = 2 OR is_expire = 1 THEN 1 ELSE 0 END) as expired_count,
SUM(CASE WHEN
(status = 0 AND (is_use = 1 OR is_expire = 1)) OR
(status = 1 AND is_use = 0) OR
(status = 2 AND is_expire = 0)
THEN 1 ELSE 0 END) as inconsistent_count
FROM shop_user_coupon;
-- 修复状态不一致的数据
UPDATE shop_user_coupon
SET status = 1, is_use = 1
WHERE status = 0 AND is_use = 1 AND is_expire = 0;
UPDATE shop_user_coupon
SET status = 2, is_expire = 1
WHERE status = 0 AND is_expire = 1;
UPDATE shop_user_coupon
SET status = 2, is_expire = 1
WHERE status IN (0, 1) AND end_time < NOW();
-- ========================================
-- 3. 添加触发器自动更新过期状态(可选)
-- ========================================
DELIMITER $$
-- 创建触发器:在查询时自动检查过期状态
CREATE TRIGGER IF NOT EXISTS tr_check_coupon_expire
BEFORE UPDATE ON shop_user_coupon
FOR EACH ROW
BEGIN
-- 如果是未使用状态且已过期,自动更新为过期状态
IF NEW.status = 0 AND NEW.end_time < NOW() THEN
SET NEW.status = 2;
SET NEW.is_expire = 1;
END IF;
END$$
DELIMITER ;
-- ========================================
-- 4. 创建视图简化查询
-- ========================================
-- 创建用户可用优惠券视图
CREATE OR REPLACE VIEW v_user_available_coupons AS
SELECT
uc.*,
c.name as coupon_name,
c.description as coupon_description,
c.apply_range,
c.apply_range_config,
CASE
WHEN uc.end_time < NOW() THEN '已过期'
WHEN uc.status = 1 THEN '已使用'
WHEN uc.status = 0 THEN '可使用'
ELSE '未知状态'
END as status_desc
FROM shop_user_coupon uc
LEFT JOIN shop_coupon c ON uc.coupon_id = c.id
WHERE uc.deleted = 0;
-- 创建优惠券统计视图
CREATE OR REPLACE VIEW v_coupon_statistics AS
SELECT
user_id,
COUNT(*) as total_count,
SUM(CASE WHEN status = 0 AND end_time >= NOW() THEN 1 ELSE 0 END) as available_count,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as used_count,
SUM(CASE WHEN status = 2 OR end_time < NOW() THEN 1 ELSE 0 END) as expired_count
FROM shop_user_coupon
WHERE deleted = 0
GROUP BY user_id;
-- ========================================
-- 5. 存储过程:批量处理过期优惠券
-- ========================================
DELIMITER $$
CREATE PROCEDURE IF NOT EXISTS sp_update_expired_coupons()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE update_count INT DEFAULT 0;
-- 声明异常处理
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
-- 批量更新过期优惠券
UPDATE shop_user_coupon
SET status = 2, is_expire = 1
WHERE status = 0
AND end_time < NOW()
AND deleted = 0;
-- 获取更新数量
SET update_count = ROW_COUNT();
COMMIT;
-- 返回更新数量
SELECT update_count as updated_count;
END$$
DELIMITER ;
-- ========================================
-- 6. 性能监控查询
-- ========================================
-- 查看优惠券状态分布
SELECT
status,
CASE
WHEN status = 0 THEN '未使用'
WHEN status = 1 THEN '已使用'
WHEN status = 2 THEN '已过期'
ELSE '未知'
END as status_name,
COUNT(*) as count,
ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM shop_user_coupon WHERE deleted = 0), 2) as percentage
FROM shop_user_coupon
WHERE deleted = 0
GROUP BY status
ORDER BY status;
-- 查看即将过期的优惠券7天内
SELECT
COUNT(*) as expiring_soon_count,
MIN(end_time) as earliest_expire_time,
MAX(end_time) as latest_expire_time
FROM shop_user_coupon
WHERE status = 0
AND end_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 7 DAY)
AND deleted = 0;
-- 查看优惠券使用率统计
SELECT
DATE(create_time) as date,
COUNT(*) as issued_count,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as used_count,
ROUND(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as usage_rate
FROM shop_user_coupon
WHERE deleted = 0
AND create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(create_time)
ORDER BY date DESC;
-- ========================================
-- 7. 清理和维护
-- ========================================
-- 清理过期很久的优惠券记录(可选,谨慎使用)
-- DELETE FROM shop_user_coupon
-- WHERE status = 2
-- AND end_time < DATE_SUB(NOW(), INTERVAL 1 YEAR)
-- AND deleted = 0;
COMMIT;

View File

@@ -1,78 +0,0 @@
-- 优惠券功能相关数据库表
-- 1. 更新优惠券模板表
ALTER TABLE `shop_coupon`
ADD COLUMN `description` varchar(500) COMMENT '优惠券描述' AFTER `name`,
ADD COLUMN `total_count` int(11) DEFAULT -1 COMMENT '发放总数量(-1表示无限制)',
ADD COLUMN `issued_count` int(11) DEFAULT 0 COMMENT '已发放数量',
ADD COLUMN `limit_per_user` int(11) DEFAULT -1 COMMENT '每人限领数量(-1表示无限制)',
ADD COLUMN `enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用(0禁用 1启用)',
MODIFY COLUMN `apply_range` int(11) DEFAULT 10 COMMENT '适用范围(10全部商品 20指定商品 30指定分类)',
MODIFY COLUMN `status` int(11) DEFAULT 0 COMMENT '状态, 0正常, 1禁用',
MODIFY COLUMN `user_id` int(11) COMMENT '创建用户ID';
-- 2. 创建用户优惠券表
CREATE TABLE `shop_user_coupon` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`coupon_id` int(11) NOT NULL COMMENT '优惠券模板ID',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`name` varchar(100) NOT NULL COMMENT '优惠券名称',
`description` varchar(500) DEFAULT NULL COMMENT '优惠券描述',
`type` int(11) NOT NULL COMMENT '优惠券类型(10满减券 20折扣券 30免费劵)',
`reduce_price` decimal(10,2) DEFAULT NULL COMMENT '满减券-减免金额',
`discount` int(11) DEFAULT NULL COMMENT '折扣券-折扣率(0-100)',
`min_price` decimal(10,2) DEFAULT NULL COMMENT '最低消费金额',
`apply_range` int(11) DEFAULT 10 COMMENT '适用范围(10全部商品 20指定商品 30指定分类)',
`apply_range_config` text COMMENT '适用范围配置(json格式)',
`start_time` datetime DEFAULT NULL COMMENT '有效期开始时间',
`end_time` datetime DEFAULT NULL COMMENT '有效期结束时间',
`status` int(11) DEFAULT 0 COMMENT '使用状态(0未使用 1已使用 2已过期)',
`use_time` datetime DEFAULT NULL COMMENT '使用时间',
`order_id` bigint(20) DEFAULT NULL COMMENT '使用订单ID',
`order_no` varchar(50) DEFAULT NULL COMMENT '使用订单号',
`obtain_type` int(11) DEFAULT 10 COMMENT '获取方式(10主动领取 20系统发放 30活动赠送)',
`obtain_source` varchar(200) DEFAULT NULL COMMENT '获取来源描述',
`deleted` tinyint(1) DEFAULT 0 COMMENT '是否删除, 0否, 1是',
`tenant_id` int(11) DEFAULT NULL COMMENT '租户id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_coupon_id` (`coupon_id`),
KEY `idx_status` (`status`),
KEY `idx_end_time` (`end_time`),
KEY `idx_order_id` (`order_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户优惠券';
-- 3. 创建优惠券使用记录表(可选,用于详细统计)
CREATE TABLE `shop_coupon_usage_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`user_coupon_id` bigint(20) NOT NULL COMMENT '用户优惠券ID',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`order_id` bigint(20) NOT NULL COMMENT '订单ID',
`order_no` varchar(50) NOT NULL COMMENT '订单号',
`order_amount` decimal(10,2) NOT NULL COMMENT '订单金额',
`discount_amount` decimal(10,2) NOT NULL COMMENT '优惠金额',
`final_amount` decimal(10,2) NOT NULL COMMENT '最终金额',
`use_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '使用时间',
`tenant_id` int(11) DEFAULT NULL COMMENT '租户id',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_use_time` (`use_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券使用记录';
-- 4. 插入示例优惠券模板数据
INSERT INTO `shop_coupon` (`name`, `description`, `type`, `reduce_price`, `discount`, `min_price`, `total_count`, `issued_count`, `limit_per_user`, `expire_type`, `expire_day`, `apply_range`, `apply_range_config`, `enabled`, `sort_number`, `status`, `user_id`, `tenant_id`) VALUES
('新用户专享券', '新用户注册即可领取满100减20', 10, 20.00, NULL, 100.00, 1000, 0, 1, 10, 30, 10, NULL, 1, 1, 0, 1, 1),
('满减优惠券', '全场通用满200减50', 10, 50.00, NULL, 200.00, 500, 0, 2, 20, NULL, 10, NULL, 1, 2, 0, 1, 1),
('折扣优惠券', '全场9折优惠券', 20, NULL, 10, 50.00, 300, 0, 1, 10, 15, 10, NULL, 1, 3, 0, 1, 1),
('生日专享券', '生日当天专享满50减30', 10, 30.00, NULL, 50.00, -1, 0, 1, 10, 7, 10, NULL, 1, 4, 0, 1, 1),
('消费返券', '消费满500返100优惠券', 10, 100.00, NULL, 300.00, -1, 0, -1, 10, 60, 10, NULL, 1, 5, 0, 1, 1);
-- 5. 创建索引优化查询性能
CREATE INDEX `idx_shop_coupon_enabled_status` ON `shop_coupon` (`enabled`, `status`);
CREATE INDEX `idx_shop_coupon_expire_type` ON `shop_coupon` (`expire_type`, `start_time`, `end_time`);
CREATE INDEX `idx_shop_user_coupon_user_status` ON `shop_user_coupon` (`user_id`, `status`);
CREATE INDEX `idx_shop_user_coupon_expire` ON `shop_user_coupon` (`status`, `end_time`);

View File

@@ -1,206 +0,0 @@
-- 创建开发专用租户和支付配置
-- 用于隔离开发环境和生产环境的支付回调地址
-- ========================================
-- 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

@@ -1,107 +0,0 @@
-- 修复BigDecimal字段的null值问题
-- 将null值替换为0.00避免JSON序列化异常
-- ========================================
-- 1. 修复用户优惠券表的null值
-- ========================================
-- 检查当前null值情况
SELECT
'shop_user_coupon null值检查' as table_name,
COUNT(*) as total_records,
COUNT(CASE WHEN reduce_price IS NULL THEN 1 END) as null_reduce_price,
COUNT(CASE WHEN min_price IS NULL THEN 1 END) as null_min_price
FROM shop_user_coupon;
-- 修复reduce_price字段的null值
UPDATE shop_user_coupon
SET reduce_price = 0.00
WHERE reduce_price IS NULL;
-- 修复min_price字段的null值
UPDATE shop_user_coupon
SET min_price = 0.00
WHERE min_price IS NULL;
-- ========================================
-- 2. 修复优惠券模板表的null值
-- ========================================
-- 检查当前null值情况
SELECT
'shop_coupon null值检查' as table_name,
COUNT(*) as total_records,
COUNT(CASE WHEN reduce_price IS NULL THEN 1 END) as null_reduce_price,
COUNT(CASE WHEN min_price IS NULL THEN 1 END) as null_min_price
FROM shop_coupon;
-- 修复reduce_price字段的null值
UPDATE shop_coupon
SET reduce_price = 0.00
WHERE reduce_price IS NULL;
-- 修复min_price字段的null值
UPDATE shop_coupon
SET min_price = 0.00
WHERE min_price IS NULL;
-- ========================================
-- 3. 设置字段默认值(可选)
-- ========================================
-- 为用户优惠券表字段设置默认值
ALTER TABLE shop_user_coupon
MODIFY COLUMN reduce_price DECIMAL(10,2) DEFAULT 0.00 COMMENT '满减券-减免金额';
ALTER TABLE shop_user_coupon
MODIFY COLUMN min_price DECIMAL(10,2) DEFAULT 0.00 COMMENT '最低消费金额';
-- 为优惠券模板表字段设置默认值
ALTER TABLE shop_coupon
MODIFY COLUMN reduce_price DECIMAL(10,2) DEFAULT 0.00 COMMENT '满减券-减免金额';
ALTER TABLE shop_coupon
MODIFY COLUMN min_price DECIMAL(10,2) DEFAULT 0.00 COMMENT '最低消费金额';
-- ========================================
-- 4. 验证修复结果
-- ========================================
-- 检查修复后的情况
SELECT
'shop_user_coupon 修复后检查' as table_name,
COUNT(*) as total_records,
COUNT(CASE WHEN reduce_price IS NULL THEN 1 END) as null_reduce_price,
COUNT(CASE WHEN min_price IS NULL THEN 1 END) as null_min_price,
MIN(reduce_price) as min_reduce_price,
MIN(min_price) as min_min_price
FROM shop_user_coupon;
SELECT
'shop_coupon 修复后检查' as table_name,
COUNT(*) as total_records,
COUNT(CASE WHEN reduce_price IS NULL THEN 1 END) as null_reduce_price,
COUNT(CASE WHEN min_price IS NULL THEN 1 END) as null_min_price,
MIN(reduce_price) as min_reduce_price,
MIN(min_price) as min_min_price
FROM shop_coupon;
-- ========================================
-- 5. 检查其他可能的BigDecimal字段
-- ========================================
-- 检查订单相关表的BigDecimal字段
SELECT
'shop_order BigDecimal字段检查' as check_type,
COUNT(*) as total_records,
COUNT(CASE WHEN order_price IS NULL THEN 1 END) as null_order_price,
COUNT(CASE WHEN pay_price IS NULL THEN 1 END) as null_pay_price
FROM shop_order;
-- 如果发现null值可以执行以下修复
/*
UPDATE shop_order SET order_price = 0.00 WHERE order_price IS NULL;
UPDATE shop_order SET pay_price = 0.00 WHERE pay_price IS NULL;
*/
SELECT 'BigDecimal null值修复完成' as result;

View File

@@ -1,101 +0,0 @@
-- 修复优惠券适用商品表字段缺失问题
-- 作者: 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

@@ -1,133 +0,0 @@
-- 修复支付状态错误将错误的102值修正为正确的1已付款
--
-- 问题原因ShopOrderMapper.xml中的updateByOutTradeNo方法错误地将payType的值赋给了payStatus字段
-- 修复内容:
-- 1. 将所有payStatus=102的记录修改为payStatus=1已付款
-- 2. 验证修复结果
-- ========================================
-- 1. 检查当前问题数据
-- ========================================
-- 查看所有payStatus=102的订单
SELECT
'=== 问题数据检查 ===' as section,
order_id,
order_no,
pay_type,
pay_status,
pay_time,
transaction_id,
tenant_id,
create_time
FROM shop_order
WHERE pay_status = 102
ORDER BY create_time DESC;
-- 统计问题数据数量
SELECT
'=== 问题统计 ===' as section,
COUNT(*) as total_error_records,
COUNT(DISTINCT tenant_id) as affected_tenants,
MIN(create_time) as earliest_error,
MAX(create_time) as latest_error
FROM shop_order
WHERE pay_status = 102;
-- 按租户统计问题数据
SELECT
'=== 按租户统计 ===' as section,
tenant_id,
COUNT(*) as error_count,
MIN(create_time) as first_error,
MAX(create_time) as last_error
FROM shop_order
WHERE pay_status = 102
GROUP BY tenant_id
ORDER BY error_count DESC;
-- ========================================
-- 2. 备份问题数据(可选)
-- ========================================
-- 创建备份表(如果需要)
-- CREATE TABLE shop_order_pay_status_backup_20250830 AS
-- SELECT * FROM shop_order WHERE pay_status = 102;
-- ========================================
-- 3. 修复数据
-- ========================================
-- 修复将payStatus=102的记录改为payStatus=1已付款
-- 注意:只修复那些有支付时间和交易号的订单(确实已支付的订单)
UPDATE shop_order
SET pay_status = 1
WHERE pay_status = 102
AND pay_time IS NOT NULL
AND transaction_id IS NOT NULL
AND transaction_id != '';
-- 对于没有支付时间或交易号的订单,设置为未付款状态
UPDATE shop_order
SET pay_status = 0
WHERE pay_status = 102
AND (pay_time IS NULL OR transaction_id IS NULL OR transaction_id = '');
-- ========================================
-- 4. 验证修复结果
-- ========================================
-- 检查是否还有payStatus=102的记录
SELECT
'=== 修复结果检查 ===' as section,
COUNT(*) as remaining_error_records
FROM shop_order
WHERE pay_status = 102;
-- 验证修复后的数据分布
SELECT
'=== 支付状态分布 ===' as section,
pay_status,
COUNT(*) as record_count,
CASE
WHEN pay_status = 0 THEN '未付款'
WHEN pay_status = 1 THEN '已付款'
ELSE CONCAT('异常状态: ', pay_status)
END as status_desc
FROM shop_order
GROUP BY pay_status
ORDER BY pay_status;
-- 检查最近修复的订单
SELECT
'=== 最近修复的订单 ===' as section,
order_id,
order_no,
pay_type,
pay_status,
pay_time,
transaction_id,
tenant_id
FROM shop_order
WHERE pay_status = 1
AND pay_time IS NOT NULL
AND transaction_id IS NOT NULL
AND update_time >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
ORDER BY update_time DESC
LIMIT 10;
-- ========================================
-- 5. 生成修复报告
-- ========================================
SELECT
'=== 修复完成报告 ===' as section,
CONCAT(
'问题原因ShopOrderMapper.xml中updateByOutTradeNo方法错误地将payType值赋给payStatus字段\n',
'修复方案:\n',
'1. 修复了XML映射文件中的错误pay_status = #{param.payType} -> pay_status = #{param.payStatus}\n',
'2. 修复了数据库中已存在的错误数据\n',
'3. 建议:检查其他可能的类似错误\n'
) as fix_summary;
SELECT '✅ 支付状态修复完成!请验证支付回调功能是否正常工作。' as result;

View File

@@ -1,183 +0,0 @@
-- 生产环境安全的支付配置脚本
-- 此脚本可以安全地在生产数据库执行
-- 不会创建测试数据,只添加必要的配置支持
-- ========================================
-- 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

@@ -1,17 +0,0 @@
-- 简单修复优惠券适用商品表字段缺失问题
-- 执行前请备份数据库
-- 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;