test: 删除测试代码和脚本

- 移除多个测试类和测试用例
- 删除证书路径测试相关代码
- 移除性能测试和功能测试代码- 删除数据库脚本和SQL文件
This commit is contained in:
2025-09-06 13:05:46 +08:00
parent 6989125aeb
commit c8e8bd32eb
33 changed files with 0 additions and 4034 deletions

View File

@@ -1,70 +0,0 @@
#!/bin/bash
# 检查证书文件路径的脚本
echo "=== 检查证书文件路径 ==="
# 基础路径
CERT_ROOT="/www/wwwroot/file.ws"
FILE_BASE="$CERT_ROOT/file"
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/file/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;

View File

@@ -1,56 +0,0 @@
package com.gxwebsoft.bszx;
import com.gxwebsoft.bszx.service.BszxPayService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import javax.annotation.Resource;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.*;
/**
* 百色中学订单总金额统计测试
*
* @author 科技小王子
* @since 2025-07-31
*/
@SpringBootTest
@ActiveProfiles("test")
public class BszxOrderTotalTest {
@Resource
private BszxPayService bszxPayService;
@Test
void testBszxOrderTotal() {
// 测试百色中学订单总金额统计
BigDecimal total = bszxPayService.total();
// 验证返回值不为null
assertNotNull(total, "百色中学订单总金额不应该为null");
// 验证返回值大于等于0
assertTrue(total.compareTo(BigDecimal.ZERO) >= 0, "百色中学订单总金额应该大于等于0");
System.out.println("百色中学订单总金额统计结果:" + total);
}
@Test
void testBszxOrderTotalPerformance() {
// 测试性能
long startTime = System.currentTimeMillis();
BigDecimal total = bszxPayService.total();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("百色中学订单总金额统计耗时:" + duration + "ms");
System.out.println("统计结果:" + total);
// 验证查询时间在合理范围内小于5秒
assertTrue(duration < 5000, "查询时间应该在5秒以内");
}
}

View File

@@ -1,136 +0,0 @@
package com.gxwebsoft.house;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.house.entity.HouseInfo;
import com.gxwebsoft.house.service.HouseInfoService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import javax.annotation.Resource;
import java.math.BigDecimal;
/**
* 房产海报生成测试
*/
@SpringBootTest
@ActiveProfiles("dev")
public class HousePosterTest {
@Resource
private HouseInfoService houseInfoService;
@Test
public void testGeneratePoster() throws Exception {
// 创建测试房源信息
HouseInfo houseInfo = new HouseInfo();
houseInfo.setHouseId(1);
houseInfo.setHouseTitle("精装修三室两厅,拎包入住");
houseInfo.setHouseType("3室2厅1卫");
houseInfo.setExtent("120㎡");
houseInfo.setFloor("15/30层");
houseInfo.setRent(new BigDecimal("3500"));
houseInfo.setMonthlyRent(new BigDecimal("3500"));
houseInfo.setAddress("深圳市南山区科技园南区");
houseInfo.setPhone("13800138000");
houseInfo.setHouseLabel("近地铁,精装修,家电齐全");
houseInfo.setTenantId(1);
// 创建测试图片文件数据(使用您提供的格式)
JSONArray filesArray = new JSONArray();
JSONObject file1 = new JSONObject();
file1.put("size", 53148);
file1.put("type", "image");
file1.put("url", "https://oss.wsdns.cn/20250507/3a2f69042a6e41f2882030d7059a4247.jpg?x-oss-process=image/resize,w_1680/quality,Q_90");
file1.put("status", "success");
file1.put("message", "");
filesArray.add(file1);
JSONObject file2 = new JSONObject();
file2.put("size", 35301);
file2.put("type", "image");
file2.put("url", "https://oss.wsdns.cn/20250507/b375176af0b1403da6c4ff66ea4bc503.jpg?x-oss-process=image/resize,w_1680/quality,Q_90");
file2.put("status", "success");
file2.put("message", "");
filesArray.add(file2);
houseInfo.setFiles(filesArray.toJSONString());
// 生成海报
String posterUrl = houseInfoService.generatePoster(houseInfo);
System.out.println("生成的海报URL: " + posterUrl);
// 验证结果
assert posterUrl != null : "海报生成失败返回null";
assert posterUrl.contains("/poster/") : "海报URL格式不正确";
assert posterUrl.contains(".jpg") : "海报文件格式不正确";
System.out.println("房产海报生成测试通过!");
System.out.println("海报包含小程序码扫码可查看房源详情页sub_pages/house/detail/" + houseInfo.getHouseId());
}
@Test
public void testGeneratePosterWithMinimalData() throws Exception {
// 测试最小数据集
HouseInfo houseInfo = new HouseInfo();
houseInfo.setHouseId(2);
houseInfo.setHouseTitle("简单房源");
houseInfo.setTenantId(1);
// 只有一张图片
JSONArray filesArray = new JSONArray();
JSONObject file = new JSONObject();
file.put("size", 53148);
file.put("type", "image");
file.put("url", "https://oss.wsdns.cn/20250507/3a2f69042a6e41f2882030d7059a4247.jpg?x-oss-process=image/resize,w_1680/quality,Q_90");
file.put("status", "success");
file.put("message", "");
filesArray.add(file);
houseInfo.setFiles(filesArray.toJSONString());
// 生成海报
String posterUrl = houseInfoService.generatePoster(houseInfo);
System.out.println("最小数据海报URL: " + posterUrl);
// 验证结果
assert posterUrl != null : "最小数据海报生成失败";
System.out.println("最小数据房产海报生成测试通过!");
}
@Test
public void testGeneratePosterWithEmptyFiles() throws Exception {
// 测试空文件数据
HouseInfo houseInfo = new HouseInfo();
houseInfo.setHouseId(3);
houseInfo.setHouseTitle("无图片房源");
houseInfo.setTenantId(1);
houseInfo.setFiles("[]"); // 空数组
// 生成海报
String posterUrl = houseInfoService.generatePoster(houseInfo);
System.out.println("空文件海报URL: " + posterUrl);
// 验证结果 - 应该返回null
assert posterUrl == null : "空文件应该返回null";
System.out.println("空文件房产海报测试通过!");
}
@Test
public void testGeneratePosterWithNullHouseInfo() throws Exception {
// 测试null房源信息
String posterUrl = houseInfoService.generatePoster(null);
// 验证结果 - 应该返回null
assert posterUrl == null : "null房源信息应该返回null";
System.out.println("null房源信息测试通过");
}
}

View File

@@ -1,38 +0,0 @@
package com.gxwebsoft.house.util;
/**
* SortSceneUtil手动测试类
* 用于验证URL解码功能
*
* @author 科技小王子
* @since 2025-08-04
*/
public class SortSceneUtilManualTest {
public static void main(String[] args) {
// 测试URL编码的参数
String urlEncoded = "%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)";
System.out.println("原始URL编码参数: " + urlEncoded);
String result = SortSceneUtil.normalizeSortScene(urlEncoded);
System.out.println("标准化后的参数: " + result);
System.out.println("是否为价格升序: " + SortSceneUtil.isPriceAsc(urlEncoded));
// 测试其他格式
String[] testCases = {
"价格(低-高)",
"价格(高-低)",
"%E4%BB%B7%E6%A0%BC(%E9%AB%98-%E4%BD%8E)",
"最新发布",
"综合排序",
"面积(小-大)",
"面积(大-小)"
};
System.out.println("\n=== 测试各种排序场景 ===");
for (String testCase : testCases) {
String normalized = SortSceneUtil.normalizeSortScene(testCase);
System.out.println("输入: " + testCase + " -> 输出: " + normalized);
}
}
}

View File

@@ -1,63 +0,0 @@
package com.gxwebsoft.house.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* SortSceneUtil测试类
*
* @author 科技小王子
* @since 2025-08-04
*/
public class SortSceneUtilTest {
@Test
public void testNormalizeSortScene() {
// 测试URL编码的参数
String urlEncoded = "%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)";
String result = SortSceneUtil.normalizeSortScene(urlEncoded);
assertEquals("价格(低-高)", result);
// 测试已解码的参数
String decoded = "价格(低-高)";
result = SortSceneUtil.normalizeSortScene(decoded);
assertEquals("价格(低-高)", result);
// 测试空值
result = SortSceneUtil.normalizeSortScene(null);
assertNull(result);
result = SortSceneUtil.normalizeSortScene("");
assertNull(result);
result = SortSceneUtil.normalizeSortScene(" ");
assertNull(result);
}
@Test
public void testIsPriceAsc() {
assertTrue(SortSceneUtil.isPriceAsc("价格(低-高)"));
assertTrue(SortSceneUtil.isPriceAsc("%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)"));
assertFalse(SortSceneUtil.isPriceAsc("价格(高-低)"));
assertFalse(SortSceneUtil.isPriceAsc("最新发布"));
}
@Test
public void testIsPriceDesc() {
assertTrue(SortSceneUtil.isPriceDesc("价格(高-低)"));
assertFalse(SortSceneUtil.isPriceDesc("价格(低-高)"));
assertFalse(SortSceneUtil.isPriceDesc("最新发布"));
}
@Test
public void testIsLatest() {
assertTrue(SortSceneUtil.isLatest("最新发布"));
assertFalse(SortSceneUtil.isLatest("价格(低-高)"));
}
@Test
public void testIsComprehensive() {
assertTrue(SortSceneUtil.isComprehensive("综合排序"));
assertFalse(SortSceneUtil.isComprehensive("价格(低-高)"));
}
}

View File

@@ -1,136 +0,0 @@
package com.gxwebsoft.payment.enums;
import com.gxwebsoft.payment.utils.PaymentTypeCompatibilityUtil;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.*;
/**
* 支付方式枚举测试类
* 验证支付方式优化后的功能正确性
*
* @author 科技小王子
* @since 2025-08-30
*/
@DisplayName("支付方式枚举测试")
class PaymentTypeTest {
@Test
@DisplayName("测试核心支付方式")
void testCorePaymentTypes() {
// 测试8种核心支付方式
assertEquals(PaymentType.BALANCE, PaymentType.getByCode(0));
assertEquals(PaymentType.WECHAT, PaymentType.getByCode(1));
assertEquals(PaymentType.ALIPAY, PaymentType.getByCode(2));
assertEquals(PaymentType.UNION_PAY, PaymentType.getByCode(3));
assertEquals(PaymentType.CASH, PaymentType.getByCode(4));
assertEquals(PaymentType.POS, PaymentType.getByCode(5));
assertEquals(PaymentType.FREE, PaymentType.getByCode(6));
assertEquals(PaymentType.POINTS, PaymentType.getByCode(7));
// 验证核心支付方式标识
assertTrue(PaymentType.BALANCE.isCorePaymentType());
assertTrue(PaymentType.WECHAT.isCorePaymentType());
assertTrue(PaymentType.ALIPAY.isCorePaymentType());
assertTrue(PaymentType.UNION_PAY.isCorePaymentType());
assertTrue(PaymentType.CASH.isCorePaymentType());
assertTrue(PaymentType.POS.isCorePaymentType());
assertTrue(PaymentType.FREE.isCorePaymentType());
assertTrue(PaymentType.POINTS.isCorePaymentType());
}
@Test
@DisplayName("测试废弃支付方式")
void testDeprecatedPaymentTypes() {
// 测试废弃支付方式标识
assertTrue(PaymentType.WECHAT_NATIVE.isDeprecated());
assertTrue(PaymentType.MEMBER_CARD_OLD.isDeprecated());
assertTrue(PaymentType.VIP_MONTHLY.isDeprecated());
// 验证废弃支付方式仍然可以通过代码获取(向后兼容)
assertEquals(PaymentType.WECHAT_NATIVE, PaymentType.getByCode(102));
assertEquals(PaymentType.FREE_OLD, PaymentType.getByCode(12));
assertEquals(PaymentType.POINTS_OLD, PaymentType.getByCode(15));
}
@Test
@DisplayName("测试支付方式分类")
void testPaymentTypeCategories() {
// 测试微信支付类型
assertTrue(PaymentType.WECHAT.isWechatPay());
assertTrue(PaymentType.WECHAT_NATIVE.isWechatPay());
// 测试第三方支付
assertTrue(PaymentType.WECHAT.isThirdPartyPay());
assertTrue(PaymentType.ALIPAY.isThirdPartyPay());
assertTrue(PaymentType.UNION_PAY.isThirdPartyPay());
// 测试在线支付
assertTrue(PaymentType.WECHAT.isOnlinePay());
assertTrue(PaymentType.ALIPAY.isOnlinePay());
assertFalse(PaymentType.CASH.isOnlinePay());
assertFalse(PaymentType.POS.isOnlinePay());
}
@Test
@DisplayName("测试兼容性工具类")
void testCompatibilityUtil() {
// 测试废弃支付方式转换
assertEquals(Integer.valueOf(0), PaymentTypeCompatibilityUtil.convertToCore(2)); // 会员卡 -> 余额
assertEquals(Integer.valueOf(1), PaymentTypeCompatibilityUtil.convertToCore(102)); // 微信Native -> 微信
assertEquals(Integer.valueOf(2), PaymentTypeCompatibilityUtil.convertToCore(3)); // 支付宝编号调整
assertEquals(Integer.valueOf(6), PaymentTypeCompatibilityUtil.convertToCore(12)); // 免费编号调整
assertEquals(Integer.valueOf(7), PaymentTypeCompatibilityUtil.convertToCore(15)); // 积分编号调整
assertEquals(Integer.valueOf(3), PaymentTypeCompatibilityUtil.convertToCore(19)); // 银联编号调整
// 测试核心支付方式不变
assertEquals(Integer.valueOf(0), PaymentTypeCompatibilityUtil.convertToCore(0)); // 余额支付
assertEquals(Integer.valueOf(1), PaymentTypeCompatibilityUtil.convertToCore(1)); // 微信支付
assertEquals(Integer.valueOf(4), PaymentTypeCompatibilityUtil.convertToCore(4)); // 现金支付
assertEquals(Integer.valueOf(5), PaymentTypeCompatibilityUtil.convertToCore(5)); // POS机支付
// 测试废弃检查
assertTrue(PaymentTypeCompatibilityUtil.isDeprecated(102)); // 微信Native
assertTrue(PaymentTypeCompatibilityUtil.isDeprecated(12)); // 免费(旧)
assertTrue(PaymentTypeCompatibilityUtil.isDeprecated(15)); // 积分(旧)
assertFalse(PaymentTypeCompatibilityUtil.isDeprecated(0)); // 余额支付
assertFalse(PaymentTypeCompatibilityUtil.isDeprecated(1)); // 微信支付
// 测试核心支付方式检查
assertTrue(PaymentTypeCompatibilityUtil.isCorePaymentType(0)); // 余额支付
assertTrue(PaymentTypeCompatibilityUtil.isCorePaymentType(1)); // 微信支付
assertTrue(PaymentTypeCompatibilityUtil.isCorePaymentType(2)); // 支付宝支付
assertTrue(PaymentTypeCompatibilityUtil.isCorePaymentType(7)); // 积分支付
assertFalse(PaymentTypeCompatibilityUtil.isCorePaymentType(102)); // 微信Native
assertFalse(PaymentTypeCompatibilityUtil.isCorePaymentType(12)); // 免费(旧)
}
@Test
@DisplayName("测试迁移消息")
void testMigrationMessages() {
// 测试废弃支付方式的迁移消息
String message = PaymentTypeCompatibilityUtil.getMigrationMessage(102);
assertNotNull(message);
assertTrue(message.contains("微信Native"));
assertTrue(message.contains("微信支付"));
// 测试核心支付方式无迁移消息
assertNull(PaymentTypeCompatibilityUtil.getMigrationMessage(0));
assertNull(PaymentTypeCompatibilityUtil.getMigrationMessage(1));
}
@Test
@DisplayName("测试迁移报告生成")
void testMigrationReport() {
String report = PaymentTypeCompatibilityUtil.generateMigrationReport();
assertNotNull(report);
assertTrue(report.contains("核心支付方式"));
assertTrue(report.contains("废弃支付方式映射"));
assertTrue(report.contains("余额支付"));
assertTrue(report.contains("微信支付"));
System.out.println("=== 支付方式迁移报告 ===");
System.out.println(report);
}
}

View File

@@ -1,79 +0,0 @@
package com.gxwebsoft.shop;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* 证书路径构建测试
*
* @author 科技小王子
* @since 2025-08-09
*/
public class CertificatePathTest {
@Test
public void testDevCertificatePath() {
// 模拟开发环境配置
String uploadPath = "/Users/gxwebsoft/JAVA/mp-java/src/main/resources/";
Integer tenantId = 10324;
String privateKeyFile = "apiclient_key.pem";
// 构建证书路径
String tenantCertPath = uploadPath + "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + privateKeyFile;
// 验证路径构建结果
String expectedTenantPath = "/Users/gxwebsoft/JAVA/mp-java/src/main/resources/dev/wechat/10324";
String expectedPrivateKeyPath = "/Users/gxwebsoft/JAVA/mp-java/src/main/resources/dev/wechat/10324/apiclient_key.pem";
assertEquals(expectedTenantPath, tenantCertPath);
assertEquals(expectedPrivateKeyPath, privateKeyPath);
System.out.println("开发环境证书路径测试通过:");
System.out.println("租户证书目录: " + tenantCertPath);
System.out.println("私钥文件路径: " + privateKeyPath);
}
@Test
public void testProdCertificatePath() {
// 模拟生产环境配置
String uploadPath = "/www/wwwroot/file.ws";
Integer tenantId = 10324;
String privateKeyFile = "apiclient_key.pem";
// 构建证书路径生产环境不使用upload-path而是从数据库读取
// 这里只是为了对比展示
String tenantCertPath = uploadPath + "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + privateKeyFile;
// 验证路径构建结果
String expectedTenantPath = "/www/wwwroot/file.ws/dev/wechat/10324";
String expectedPrivateKeyPath = "/www/wwwroot/file.ws/dev/wechat/10324/apiclient_key.pem";
assertEquals(expectedTenantPath, tenantCertPath);
assertEquals(expectedPrivateKeyPath, privateKeyPath);
System.out.println("生产环境证书路径测试通过:");
System.out.println("租户证书目录: " + tenantCertPath);
System.out.println("私钥文件路径: " + privateKeyPath);
}
@Test
public void testMultipleTenants() {
String uploadPath = "/Users/gxwebsoft/JAVA/mp-java/src/main/resources/";
String privateKeyFile = "apiclient_key.pem";
// 测试多个租户的路径构建
Integer[] tenantIds = {10324, 10325, 10326};
for (Integer tenantId : tenantIds) {
String tenantCertPath = uploadPath + "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + privateKeyFile;
assertTrue(tenantCertPath.contains(tenantId.toString()));
assertTrue(privateKeyPath.endsWith(privateKeyFile));
System.out.println("租户 " + tenantId + " 证书路径: " + privateKeyPath);
}
}
}

View File

@@ -1,174 +0,0 @@
package com.gxwebsoft.shop;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.dto.OrderCreateRequest;
import com.gxwebsoft.shop.entity.ShopGoods;
import com.gxwebsoft.shop.entity.ShopGoodsSku;
import com.gxwebsoft.shop.service.OrderBusinessService;
import com.gxwebsoft.shop.service.ShopGoodsService;
import com.gxwebsoft.shop.service.ShopGoodsSkuService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* 多规格订单测试类
*
* @author 科技小王子
* @since 2025-07-30
*/
@ExtendWith(MockitoExtension.class)
public class MultiSpecOrderTest {
@Mock
private ShopGoodsService shopGoodsService;
@Mock
private ShopGoodsSkuService shopGoodsSkuService;
@InjectMocks
private OrderBusinessService orderBusinessService;
private User testUser;
private ShopGoods testGoods;
private ShopGoodsSku testSku;
@BeforeEach
void setUp() {
// 创建测试用户
testUser = new User();
testUser.setUserId(1);
testUser.setTenantId(1);
testUser.setOpenid("test_openid");
testUser.setPhone("13800138000");
// 创建测试商品
testGoods = new ShopGoods();
testGoods.setGoodsId(1);
testGoods.setName("测试商品");
testGoods.setPrice(new BigDecimal("100.00"));
testGoods.setStock(50);
testGoods.setStatus(0); // 正常状态
testGoods.setImage("test.jpg");
// 创建测试SKU
testSku = new ShopGoodsSku();
testSku.setId(1);
testSku.setGoodsId(1);
testSku.setPrice(new BigDecimal("120.00"));
testSku.setStock(20);
testSku.setStatus(0); // 正常状态
testSku.setSku("颜色:红色|尺寸:L");
testSku.setImage("sku_test.jpg");
}
@Test
void testCreateOrderWithSingleSpec() {
// 测试单规格商品下单
when(shopGoodsService.getById(1)).thenReturn(testGoods);
OrderCreateRequest request = createOrderRequest(false);
// 这里需要mock其他依赖服务实际测试中需要完整的Spring上下文
// 此测试主要验证多规格逻辑的正确性
assertNotNull(request);
assertEquals(1, request.getGoodsItems().size());
assertNull(request.getGoodsItems().get(0).getSkuId());
}
@Test
void testCreateOrderWithMultiSpec() {
// 测试多规格商品下单
when(shopGoodsService.getById(1)).thenReturn(testGoods);
when(shopGoodsSkuService.getById(1)).thenReturn(testSku);
OrderCreateRequest request = createOrderRequest(true);
assertNotNull(request);
assertEquals(1, request.getGoodsItems().size());
assertEquals(Integer.valueOf(1), request.getGoodsItems().get(0).getSkuId());
assertEquals("颜色:红色|尺寸:L", request.getGoodsItems().get(0).getSpecInfo());
}
@Test
void testSkuValidation() {
// 测试SKU验证逻辑
when(shopGoodsService.getById(1)).thenReturn(testGoods);
// 测试SKU不存在的情况
when(shopGoodsSkuService.getById(999)).thenReturn(null);
OrderCreateRequest request = createOrderRequest(true);
request.getGoodsItems().get(0).setSkuId(999); // 不存在的SKU ID
// 在实际测试中这里应该抛出BusinessException
// assertThrows(BusinessException.class, () -> orderBusinessService.createOrder(request, testUser));
}
@Test
void testStockValidation() {
// 测试库存验证
testSku.setStock(1); // 设置库存为1
when(shopGoodsService.getById(1)).thenReturn(testGoods);
when(shopGoodsSkuService.getById(1)).thenReturn(testSku);
OrderCreateRequest request = createOrderRequest(true);
request.getGoodsItems().get(0).setQuantity(5); // 购买数量超过库存
// 在实际测试中这里应该抛出BusinessException
// assertThrows(BusinessException.class, () -> orderBusinessService.createOrder(request, testUser));
}
@Test
void testPriceCalculation() {
// 测试价格计算
when(shopGoodsService.getById(1)).thenReturn(testGoods);
when(shopGoodsSkuService.getById(1)).thenReturn(testSku);
// 多规格商品应该使用SKU价格120.00而不是商品价格100.00
OrderCreateRequest request = createOrderRequest(true);
request.getGoodsItems().get(0).setQuantity(2);
// 期望总价格 = SKU价格(120.00) * 数量(2) = 240.00
BigDecimal expectedTotal = new BigDecimal("240.00");
request.setTotalPrice(expectedTotal);
assertEquals(expectedTotal, request.getTotalPrice());
}
/**
* 创建订单请求对象
*/
private OrderCreateRequest createOrderRequest(boolean withSku) {
OrderCreateRequest request = new OrderCreateRequest();
request.setType(0);
request.setTotalPrice(new BigDecimal("100.00"));
request.setPayPrice(new BigDecimal("100.00"));
request.setTotalNum(1);
request.setTenantId(1);
OrderCreateRequest.OrderGoodsItem item = new OrderCreateRequest.OrderGoodsItem();
item.setGoodsId(1);
item.setQuantity(1);
if (withSku) {
item.setSkuId(1);
item.setSpecInfo("颜色:红色|尺寸:L");
}
request.setGoodsItems(Arrays.asList(item));
return request;
}
}

View File

@@ -1,170 +0,0 @@
package com.gxwebsoft.shop;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.dto.OrderCreateRequest;
import com.gxwebsoft.shop.entity.ShopGoods;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
import com.gxwebsoft.shop.service.OrderBusinessService;
import com.gxwebsoft.shop.service.ShopGoodsService;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* 订单业务服务测试类
*
* @author 科技小王子
* @since 2025-01-26
*/
@ExtendWith(MockitoExtension.class)
public class OrderBusinessServiceTest {
@Mock
private ShopOrderService shopOrderService;
@Mock
private ShopOrderGoodsService shopOrderGoodsService;
@Mock
private ShopGoodsService shopGoodsService;
@InjectMocks
private OrderBusinessService orderBusinessService;
private User testUser;
private OrderCreateRequest testRequest;
private ShopGoods testGoods;
@BeforeEach
void setUp() {
// 准备测试用户
testUser = new User();
testUser.setUserId(1);
testUser.setOpenid("test_openid");
testUser.setPhone("13800138000");
// 准备测试商品
testGoods = new ShopGoods();
testGoods.setGoodsId(10018);
testGoods.setName("科技小王子大米年卡套餐2.5kg");
testGoods.setPrice(new BigDecimal("99.00"));
testGoods.setImage("test_image.jpg");
// 准备测试订单请求
testRequest = new OrderCreateRequest();
testRequest.setType(0);
testRequest.setTotalPrice(new BigDecimal("99.00"));
testRequest.setPayPrice(new BigDecimal("99.00"));
testRequest.setTotalNum(1);
testRequest.setPayType(1);
testRequest.setTenantId(1);
testRequest.setAddressId(10832);
testRequest.setComments("科技小王子大米年卡套餐2.5kg");
testRequest.setDeliveryType(0);
// 准备商品项列表
OrderCreateRequest.OrderGoodsItem goodsItem = new OrderCreateRequest.OrderGoodsItem();
goodsItem.setGoodsId(10018);
goodsItem.setQuantity(1);
goodsItem.setPayType(1);
testRequest.setGoodsItems(Arrays.asList(goodsItem));
}
@Test
void testCreateOrderWithGoods() {
// Mock 商品查询
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
// Mock 订单保存
when(shopOrderService.save(any(ShopOrder.class))).thenAnswer(invocation -> {
ShopOrder order = invocation.getArgument(0);
order.setOrderId(1); // 模拟数据库生成的ID
return true;
});
// Mock 订单商品批量保存
when(shopOrderGoodsService.saveBatch(anyList())).thenReturn(true);
// Mock 微信支付订单创建
HashMap<String, String> wxOrderInfo = new HashMap<>();
wxOrderInfo.put("prepay_id", "test_prepay_id");
when(shopOrderService.createWxOrder(any(ShopOrder.class))).thenReturn(wxOrderInfo);
// 执行测试
Map<String, String> result = orderBusinessService.createOrder(testRequest, testUser);
// 验证结果
assert result != null;
assert result.containsKey("prepay_id");
// 验证方法调用
verify(shopGoodsService, times(1)).getById(10018);
verify(shopOrderService, times(1)).save(any(ShopOrder.class));
verify(shopOrderGoodsService, times(1)).saveBatch(anyList());
verify(shopOrderService, times(1)).createWxOrder(any(ShopOrder.class));
}
@Test
void testCreateOrderWithMultipleGoods() {
// 准备多个商品项
OrderCreateRequest.OrderGoodsItem goodsItem1 = new OrderCreateRequest.OrderGoodsItem();
goodsItem1.setGoodsId(10018);
goodsItem1.setQuantity(1);
goodsItem1.setPayType(1);
OrderCreateRequest.OrderGoodsItem goodsItem2 = new OrderCreateRequest.OrderGoodsItem();
goodsItem2.setGoodsId(10019);
goodsItem2.setQuantity(2);
goodsItem2.setPayType(1);
testRequest.setGoodsItems(Arrays.asList(goodsItem1, goodsItem2));
testRequest.setTotalPrice(new BigDecimal("297.00")); // 99 + 99*2
// Mock 商品查询
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
ShopGoods testGoods2 = new ShopGoods();
testGoods2.setGoodsId(10019);
testGoods2.setName("测试商品2");
testGoods2.setPrice(new BigDecimal("99.00"));
testGoods2.setImage("test_image2.jpg");
when(shopGoodsService.getById(10019)).thenReturn(testGoods2);
// Mock 其他服务
when(shopOrderService.save(any(ShopOrder.class))).thenAnswer(invocation -> {
ShopOrder order = invocation.getArgument(0);
order.setOrderId(1);
return true;
});
when(shopOrderGoodsService.saveBatch(anyList())).thenReturn(true);
when(shopOrderService.createWxOrder(any(ShopOrder.class))).thenReturn(new HashMap<>());
// 执行测试
orderBusinessService.createOrder(testRequest, testUser);
// 验证商品查询次数
verify(shopGoodsService, times(1)).getById(10018);
verify(shopGoodsService, times(1)).getById(10019);
// 验证保存的商品项数量
verify(shopOrderGoodsService, times(1)).saveBatch(argThat(list ->
((List<ShopOrderGoods>) list).size() == 2
));
}
}

View File

@@ -1,56 +0,0 @@
package com.gxwebsoft.shop;
import com.gxwebsoft.shop.service.ShopOrderService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import javax.annotation.Resource;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.*;
/**
* 订单总金额统计测试
*
* @author 科技小王子
* @since 2025-07-30
*/
@SpringBootTest
@ActiveProfiles("test")
public class OrderTotalTest {
@Resource
private ShopOrderService shopOrderService;
@Test
void testOrderTotal() {
// 测试订单总金额统计
BigDecimal total = shopOrderService.total();
// 验证返回值不为null
assertNotNull(total, "订单总金额不应该为null");
// 验证返回值大于等于0
assertTrue(total.compareTo(BigDecimal.ZERO) >= 0, "订单总金额应该大于等于0");
System.out.println("订单总金额统计结果:" + total);
}
@Test
void testOrderTotalPerformance() {
// 测试性能
long startTime = System.currentTimeMillis();
BigDecimal total = shopOrderService.total();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("订单总金额统计耗时:" + duration + "ms");
System.out.println("统计结果:" + total);
// 验证查询时间在合理范围内小于5秒
assertTrue(duration < 5000, "查询时间应该在5秒以内");
}
}

View File

@@ -1,315 +0,0 @@
package com.gxwebsoft.shop;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.config.OrderConfigProperties;
import com.gxwebsoft.shop.dto.OrderCreateRequest;
import com.gxwebsoft.shop.entity.ShopGoods;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.service.OrderBusinessService;
import com.gxwebsoft.shop.service.ShopGoodsService;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* 订单验证测试类
* 测试商品信息后台验证逻辑
*/
@ExtendWith(MockitoExtension.class)
class OrderValidationTest {
@Mock
private ShopOrderService shopOrderService;
@Mock
private ShopOrderGoodsService shopOrderGoodsService;
@Mock
private ShopGoodsService shopGoodsService;
@Mock
private OrderConfigProperties orderConfig;
@InjectMocks
private OrderBusinessService orderBusinessService;
private User testUser;
private OrderCreateRequest testRequest;
private ShopGoods testGoods;
@BeforeEach
void setUp() {
// 准备测试用户
testUser = new User();
testUser.setUserId(1);
testUser.setNickname("测试用户");
testUser.setPhone("13800138000");
// 准备测试商品
testGoods = new ShopGoods();
testGoods.setGoodsId(10018);
testGoods.setName("测试商品");
testGoods.setPrice(new BigDecimal("99.00"));
testGoods.setStatus(0); // 上架状态
testGoods.setStock(100); // 库存100
testGoods.setCanBuyNumber(10); // 最大购买数量10
testGoods.setCode("TEST001");
// 准备测试订单请求
testRequest = new OrderCreateRequest();
testRequest.setType(0);
testRequest.setTitle("测试订单");
testRequest.setTotalPrice(new BigDecimal("99.00"));
testRequest.setTenantId(1);
// 准备商品项
OrderCreateRequest.OrderGoodsItem goodsItem = new OrderCreateRequest.OrderGoodsItem();
goodsItem.setGoodsId(10018);
goodsItem.setQuantity(1);
testRequest.setGoodsItems(Arrays.asList(goodsItem));
}
@Test
void testValidateOrderRequest_Success() {
// Mock 商品查询
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
when(orderConfig.getTenantRule(1)).thenReturn(null);
// 执行验证 - 应该成功
assertDoesNotThrow(() -> {
// 使用反射调用私有方法进行测试
java.lang.reflect.Method method = OrderBusinessService.class
.getDeclaredMethod("validateOrderRequest", OrderCreateRequest.class, User.class);
method.setAccessible(true);
method.invoke(orderBusinessService, testRequest, testUser);
});
// 验证总金额被正确设置
assertEquals(new BigDecimal("99.00"), testRequest.getTotalPrice());
}
@Test
void testValidateOrderRequest_GoodsNotFound() {
// Mock 商品不存在
when(shopGoodsService.getById(10018)).thenReturn(null);
// 执行验证 - 应该抛出异常
Exception exception = assertThrows(Exception.class, () -> {
java.lang.reflect.Method method = OrderBusinessService.class
.getDeclaredMethod("validateOrderRequest", OrderCreateRequest.class, User.class);
method.setAccessible(true);
method.invoke(orderBusinessService, testRequest, testUser);
});
// 检查是否是 InvocationTargetException 包装的 BusinessException
assertTrue(exception instanceof java.lang.reflect.InvocationTargetException);
Throwable cause = exception.getCause();
assertTrue(cause instanceof BusinessException);
assertTrue(cause.getMessage().contains("商品不存在"));
}
@Test
void testValidateOrderRequest_GoodsOffShelf() {
// 设置商品为下架状态
testGoods.setStatus(1);
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
// 执行验证 - 应该抛出异常
Exception exception = assertThrows(Exception.class, () -> {
java.lang.reflect.Method method = OrderBusinessService.class
.getDeclaredMethod("validateOrderRequest", OrderCreateRequest.class, User.class);
method.setAccessible(true);
method.invoke(orderBusinessService, testRequest, testUser);
});
// 检查是否是 InvocationTargetException 包装的 BusinessException
assertTrue(exception instanceof java.lang.reflect.InvocationTargetException);
Throwable cause = exception.getCause();
assertTrue(cause instanceof BusinessException);
assertTrue(cause.getMessage().contains("商品已下架"));
}
@Test
void testValidateOrderRequest_InsufficientStock() {
// 设置库存不足
testGoods.setStock(0);
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
// 执行验证 - 应该抛出异常
Exception exception = assertThrows(Exception.class, () -> {
java.lang.reflect.Method method = OrderBusinessService.class
.getDeclaredMethod("validateOrderRequest", OrderCreateRequest.class, User.class);
method.setAccessible(true);
method.invoke(orderBusinessService, testRequest, testUser);
});
// 检查是否是 InvocationTargetException 包装的 BusinessException
assertTrue(exception instanceof java.lang.reflect.InvocationTargetException);
Throwable cause = exception.getCause();
assertTrue(cause instanceof BusinessException);
assertTrue(cause.getMessage().contains("商品库存不足"));
}
@Test
void testValidateOrderRequest_ExceedBuyLimit() {
// 设置购买数量超过限制
testRequest.getGoodsItems().get(0).setQuantity(15); // 超过最大购买数量10
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
// 执行验证 - 应该抛出异常
Exception exception = assertThrows(Exception.class, () -> {
java.lang.reflect.Method method = OrderBusinessService.class
.getDeclaredMethod("validateOrderRequest", OrderCreateRequest.class, User.class);
method.setAccessible(true);
method.invoke(orderBusinessService, testRequest, testUser);
});
// 检查是否是 InvocationTargetException 包装的 BusinessException
assertTrue(exception instanceof java.lang.reflect.InvocationTargetException);
Throwable cause = exception.getCause();
assertTrue(cause instanceof BusinessException);
assertTrue(cause.getMessage().contains("购买数量超过限制"));
}
@Test
void testValidateOrderRequest_PriceCalculation() {
// 设置多个商品项
OrderCreateRequest.OrderGoodsItem goodsItem1 = new OrderCreateRequest.OrderGoodsItem();
goodsItem1.setGoodsId(10018);
goodsItem1.setQuantity(2);
OrderCreateRequest.OrderGoodsItem goodsItem2 = new OrderCreateRequest.OrderGoodsItem();
goodsItem2.setGoodsId(10019);
goodsItem2.setQuantity(1);
testRequest.setGoodsItems(Arrays.asList(goodsItem1, goodsItem2));
testRequest.setTotalPrice(new BigDecimal("297.00")); // 99*2 + 99*1
// 准备第二个商品
ShopGoods testGoods2 = new ShopGoods();
testGoods2.setGoodsId(10019);
testGoods2.setName("测试商品2");
testGoods2.setPrice(new BigDecimal("99.00"));
testGoods2.setStatus(0);
testGoods2.setStock(100);
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
when(shopGoodsService.getById(10019)).thenReturn(testGoods2);
when(orderConfig.getTenantRule(1)).thenReturn(null);
// 执行验证 - 应该成功
assertDoesNotThrow(() -> {
java.lang.reflect.Method method = OrderBusinessService.class
.getDeclaredMethod("validateOrderRequest", OrderCreateRequest.class, User.class);
method.setAccessible(true);
method.invoke(orderBusinessService, testRequest, testUser);
});
// 验证总金额计算正确
assertEquals(new BigDecimal("297.00"), testRequest.getTotalPrice());
}
@Test
void testValidateOrderRequest_PriceDiscrepancy() {
// 设置前端传入的金额与后台计算不一致
testRequest.setTotalPrice(new BigDecimal("88.00")); // 错误的金额
when(shopGoodsService.getById(10018)).thenReturn(testGoods);
// 执行验证 - 应该抛出异常
Exception exception = assertThrows(Exception.class, () -> {
java.lang.reflect.Method method = OrderBusinessService.class
.getDeclaredMethod("validateOrderRequest", OrderCreateRequest.class, User.class);
method.setAccessible(true);
method.invoke(orderBusinessService, testRequest, testUser);
});
// 检查是否是 InvocationTargetException 包装的 BusinessException
assertTrue(exception instanceof java.lang.reflect.InvocationTargetException);
Throwable cause = exception.getCause();
assertTrue(cause instanceof BusinessException);
assertTrue(cause.getMessage().contains("订单金额计算错误"));
}
@Test
void testBuildShopOrder_RequiredFields() throws Exception {
// Mock 配置
OrderConfigProperties.DefaultConfig defaultConfig = new OrderConfigProperties.DefaultConfig();
defaultConfig.setDefaultComments("默认备注");
when(orderConfig.getDefaultConfig()).thenReturn(defaultConfig);
// 使用反射调用 buildShopOrder 方法
java.lang.reflect.Method buildMethod = OrderBusinessService.class
.getDeclaredMethod("buildShopOrder", OrderCreateRequest.class, User.class);
buildMethod.setAccessible(true);
ShopOrder result = (ShopOrder) buildMethod.invoke(orderBusinessService, testRequest, testUser);
// 验证必需字段都已设置
assertNotNull(result.getPayPrice(), "payPrice 不能为空");
assertNotNull(result.getPrice(), "price 不能为空");
assertNotNull(result.getReducePrice(), "reducePrice 不能为空");
assertNotNull(result.getMoney(), "money 不能为空");
assertNotNull(result.getPayStatus(), "payStatus 不能为空");
assertNotNull(result.getOrderStatus(), "orderStatus 不能为空");
assertNotNull(result.getDeliveryStatus(), "deliveryStatus 不能为空");
assertNotNull(result.getPayType(), "payType 不能为空");
// 验证默认值
assertEquals(testRequest.getTotalPrice(), result.getPayPrice());
assertEquals(testRequest.getTotalPrice(), result.getPrice());
assertEquals(BigDecimal.ZERO, result.getReducePrice());
assertEquals(testRequest.getTotalPrice(), result.getMoney());
assertEquals(false, result.getPayStatus());
assertEquals(Integer.valueOf(0), result.getOrderStatus());
assertEquals(Integer.valueOf(10), result.getDeliveryStatus());
assertEquals(Integer.valueOf(1), result.getPayType());
// 验证关键字段 - 租户ID影响微信支付证书路径
assertNotNull(result.getTenantId(), "tenantId 不能为空");
assertEquals(testRequest.getTenantId(), result.getTenantId(), "tenantId 必须正确设置");
}
@Test
void testBuildShopOrder_TenantIdValidation() throws Exception {
// 创建一个新的请求对象租户ID为空
OrderCreateRequest requestWithoutTenant = new OrderCreateRequest();
requestWithoutTenant.setType(0);
requestWithoutTenant.setTitle("测试订单");
requestWithoutTenant.setTotalPrice(new BigDecimal("99.00"));
requestWithoutTenant.setTenantId(null); // 设置为空
// 准备商品项
OrderCreateRequest.OrderGoodsItem goodsItem = new OrderCreateRequest.OrderGoodsItem();
goodsItem.setGoodsId(10018);
goodsItem.setQuantity(1);
requestWithoutTenant.setGoodsItems(Arrays.asList(goodsItem));
// 使用反射调用 buildShopOrder 方法
java.lang.reflect.Method buildMethod = OrderBusinessService.class
.getDeclaredMethod("buildShopOrder", OrderCreateRequest.class, User.class);
buildMethod.setAccessible(true);
// 执行验证 - 应该抛出异常
Exception exception = assertThrows(Exception.class, () -> {
buildMethod.invoke(orderBusinessService, requestWithoutTenant, testUser);
});
// 检查是否是 InvocationTargetException 包装的 BusinessException
assertTrue(exception instanceof java.lang.reflect.InvocationTargetException);
Throwable cause = exception.getCause();
assertTrue(cause instanceof BusinessException);
assertTrue(cause.getMessage().contains("租户ID不能为空"));
}
}

View File

@@ -1,75 +0,0 @@
package com.gxwebsoft.shop;
import com.gxwebsoft.common.core.utils.WechatPayUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.*;
/**
* 微信支付商品描述字段测试
* 验证字节长度限制处理
*/
@SpringBootTest
public class WechatPayDescriptionTest {
@Test
public void testTruncateToByteLimit() {
// 测试正常情况 - 字符串长度在限制内
String shortText = "正常商品描述";
String result1 = WechatPayUtils.truncateToByteLimit(shortText, 127);
assertEquals(shortText, result1);
assertTrue(result1.getBytes(StandardCharsets.UTF_8).length <= 127);
// 测试超长中文字符串
String longText = "【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋";
String result2 = WechatPayUtils.truncateToByteLimit(longText, 127);
assertNotNull(result2);
assertTrue(result2.getBytes(StandardCharsets.UTF_8).length <= 127);
System.out.println("原始文本字节数: " + longText.getBytes(StandardCharsets.UTF_8).length);
System.out.println("截断后文本: " + result2);
System.out.println("截断后字节数: " + result2.getBytes(StandardCharsets.UTF_8).length);
// 测试null和空字符串
assertNull(WechatPayUtils.truncateToByteLimit(null, 127));
assertEquals("", WechatPayUtils.truncateToByteLimit("", 127));
// 测试英文字符串
String englishText = "This is a very long English description that might exceed the byte limit for WeChat Pay description field";
String result3 = WechatPayUtils.truncateToByteLimit(englishText, 127);
assertNotNull(result3);
assertTrue(result3.getBytes(StandardCharsets.UTF_8).length <= 127);
// 测试混合字符串
String mixedText = "Product Name 产品名称 with special characters @#$%^&*()";
String result4 = WechatPayUtils.truncateToByteLimit(mixedText, 50);
assertNotNull(result4);
assertTrue(result4.getBytes(StandardCharsets.UTF_8).length <= 50);
}
@Test
public void testSpecificErrorCase() {
// 测试错误信息中的具体案例
String errorText = "【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋";
// 验证原始文本确实超过127字节
int originalBytes = errorText.getBytes(StandardCharsets.UTF_8).length;
System.out.println("原始文本: " + errorText);
System.out.println("原始字节数: " + originalBytes);
assertTrue(originalBytes > 127, "原始文本应该超过127字节");
// 测试截断
String truncated = WechatPayUtils.processDescription(errorText);
int truncatedBytes = truncated.getBytes(StandardCharsets.UTF_8).length;
System.out.println("截断后文本: " + truncated);
System.out.println("截断后字节数: " + truncatedBytes);
// 验证截断后的文本符合要求
assertNotNull(truncated);
assertTrue(truncatedBytes <= 127, "截断后字节数应该不超过127");
assertFalse(truncated.contains("\uFFFD"), "截断后的文本不应包含无效字符");
}
}

View File

@@ -1,113 +0,0 @@
package com.gxwebsoft.shop.service;
import com.gxwebsoft.shop.entity.ShopUserCoupon;
import com.gxwebsoft.shop.service.CouponStatusService.CouponStatusResult;
import com.gxwebsoft.shop.service.CouponStatusService.CouponValidationResult;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* 优惠券状态管理服务测试
*
* @author WebSoft
* @since 2025-01-15
*/
@SpringBootTest
@ActiveProfiles("dev")
public class CouponStatusServiceTest {
@Autowired
private CouponStatusService couponStatusService;
@Test
public void testCouponStatusConstants() {
// 测试状态常量
assertEquals(0, ShopUserCoupon.STATUS_UNUSED);
assertEquals(1, ShopUserCoupon.STATUS_USED);
assertEquals(2, ShopUserCoupon.STATUS_EXPIRED);
// 测试类型常量
assertEquals(10, ShopUserCoupon.TYPE_REDUCE);
assertEquals(20, ShopUserCoupon.TYPE_DISCOUNT);
assertEquals(30, ShopUserCoupon.TYPE_FREE);
// 测试适用范围常量
assertEquals(10, ShopUserCoupon.APPLY_ALL);
assertEquals(20, ShopUserCoupon.APPLY_GOODS);
assertEquals(30, ShopUserCoupon.APPLY_CATEGORY);
}
@Test
public void testCouponStatusMethods() {
// 创建测试优惠券
ShopUserCoupon coupon = new ShopUserCoupon();
coupon.setStatus(ShopUserCoupon.STATUS_UNUSED);
coupon.setEndTime(LocalDateTime.now().plusDays(7));
// 测试可用状态
assertTrue(coupon.isAvailable());
assertFalse(coupon.isUsed());
assertFalse(coupon.isExpired());
assertEquals("可使用", coupon.getStatusDesc());
// 测试已使用状态
coupon.markAsUsed(123, "ORDER123");
assertTrue(coupon.isUsed());
assertFalse(coupon.isAvailable());
assertEquals("已使用", coupon.getStatusDesc());
assertEquals(Integer.valueOf(123), coupon.getOrderId());
assertEquals("ORDER123", coupon.getOrderNo());
// 测试过期状态
coupon.setStatus(ShopUserCoupon.STATUS_UNUSED);
coupon.setEndTime(LocalDateTime.now().minusDays(1));
assertTrue(coupon.isExpired());
assertFalse(coupon.isAvailable());
assertEquals("已过期", coupon.getStatusDesc());
}
@Test
public void testValidateCouponForOrder() {
// 这个测试需要数据库中有实际的优惠券数据
// 这里只是演示测试结构
List<Integer> goodsIds = Arrays.asList(1, 2, 3);
BigDecimal totalAmount = new BigDecimal("150.00");
// 注意这个测试需要实际的优惠券ID在真实环境中需要先创建测试数据
// CouponValidationResult result = couponStatusService.validateCouponForOrder(1L, totalAmount, goodsIds);
// assertNotNull(result);
System.out.println("优惠券验证测试需要实际的数据库数据");
}
@Test
public void testGetUserCouponsGroupByStatus() {
// 这个测试需要数据库中有实际的用户和优惠券数据
// 这里只是演示测试结构
// 注意这个测试需要实际的用户ID在真实环境中需要先创建测试数据
// CouponStatusResult result = couponStatusService.getUserCouponsGroupByStatus(1);
// assertNotNull(result);
// assertTrue(result.getTotalCount() >= 0);
System.out.println("用户优惠券分组测试需要实际的数据库数据");
}
@Test
public void testUpdateExpiredCoupons() {
// 测试批量更新过期优惠券
int updatedCount = couponStatusService.updateExpiredCoupons();
assertTrue(updatedCount >= 0);
System.out.println("更新了 " + updatedCount + " 张过期优惠券");
}
}

View File

@@ -1,145 +0,0 @@
package com.gxwebsoft.shop.service;
import com.gxwebsoft.shop.entity.ShopGoods;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import javax.annotation.Resource;
/**
* 商品销量累加功能测试
*
* @author WebSoft
* @since 2025-08-23
*/
@Slf4j
@SpringBootTest
@ActiveProfiles("dev")
public class ShopGoodsSalesTest {
@Resource
private ShopGoodsService shopGoodsService;
/**
* 测试累加商品销量功能
*/
@Test
public void testAddSaleCount() {
// 测试商品ID请根据实际数据库中的商品ID进行调整
Integer testGoodsId = 1;
Integer addCount = 5;
log.info("=== 开始测试商品销量累加功能 ===");
// 1. 查询商品当前销量
ShopGoods goodsBefore = shopGoodsService.getById(testGoodsId);
if (goodsBefore == null) {
log.error("测试失败:商品不存在 - 商品ID: {}", testGoodsId);
return;
}
Integer salesBefore = goodsBefore.getSales() != null ? goodsBefore.getSales() : 0;
log.info("累加前商品销量 - 商品ID: {}, 商品名称: {}, 当前销量: {}",
testGoodsId, goodsBefore.getName(), salesBefore);
// 2. 执行销量累加
boolean result = shopGoodsService.addSaleCount(testGoodsId, addCount);
log.info("销量累加操作结果: {}", result ? "成功" : "失败");
// 3. 查询累加后的销量
ShopGoods goodsAfter = shopGoodsService.getById(testGoodsId);
Integer salesAfter = goodsAfter.getSales() != null ? goodsAfter.getSales() : 0;
log.info("累加后商品销量 - 商品ID: {}, 商品名称: {}, 累加后销量: {}",
testGoodsId, goodsAfter.getName(), salesAfter);
// 4. 验证结果
Integer expectedSales = salesBefore + addCount;
if (salesAfter.equals(expectedSales)) {
log.info("✅ 测试成功!销量正确累加 - 预期: {}, 实际: {}", expectedSales, salesAfter);
} else {
log.error("❌ 测试失败!销量累加不正确 - 预期: {}, 实际: {}", expectedSales, salesAfter);
}
log.info("=== 商品销量累加功能测试完成 ===");
}
/**
* 测试参数验证
*/
@Test
public void testAddSaleCountValidation() {
log.info("=== 开始测试参数验证 ===");
// 测试null商品ID
boolean result1 = shopGoodsService.addSaleCount(null, 5);
log.info("null商品ID测试结果: {}", result1 ? "失败(应该返回false)" : "成功");
// 测试null销量
boolean result2 = shopGoodsService.addSaleCount(1, null);
log.info("null销量测试结果: {}", result2 ? "失败(应该返回false)" : "成功");
// 测试负数销量
boolean result3 = shopGoodsService.addSaleCount(1, -1);
log.info("负数销量测试结果: {}", result3 ? "失败(应该返回false)" : "成功");
// 测试零销量
boolean result4 = shopGoodsService.addSaleCount(1, 0);
log.info("零销量测试结果: {}", result4 ? "失败(应该返回false)" : "成功");
// 测试不存在的商品ID
boolean result5 = shopGoodsService.addSaleCount(999999, 5);
log.info("不存在商品ID测试结果: {}", result5 ? "失败(应该返回false)" : "成功");
log.info("=== 参数验证测试完成 ===");
}
/**
* 测试大批量累加
*/
@Test
public void testBatchAddSaleCount() {
Integer testGoodsId = 1;
log.info("=== 开始测试批量累加 ===");
// 查询初始销量
ShopGoods goodsBefore = shopGoodsService.getById(testGoodsId);
if (goodsBefore == null) {
log.error("测试失败:商品不存在 - 商品ID: {}", testGoodsId);
return;
}
Integer salesBefore = goodsBefore.getSales() != null ? goodsBefore.getSales() : 0;
log.info("批量累加前销量: {}", salesBefore);
// 模拟多次购买
int totalAdded = 0;
for (int i = 1; i <= 10; i++) {
boolean result = shopGoodsService.addSaleCount(testGoodsId, i);
if (result) {
totalAdded += i;
log.info("第{}次累加成功,累加数量: {}", i, i);
} else {
log.error("第{}次累加失败", i);
}
}
// 验证最终结果
ShopGoods goodsAfter = shopGoodsService.getById(testGoodsId);
Integer salesAfter = goodsAfter.getSales() != null ? goodsAfter.getSales() : 0;
Integer expectedSales = salesBefore + totalAdded;
log.info("批量累加结果 - 累加前: {}, 总累加量: {}, 累加后: {}, 预期: {}",
salesBefore, totalAdded, salesAfter, expectedSales);
if (salesAfter.equals(expectedSales)) {
log.info("✅ 批量累加测试成功!");
} else {
log.error("❌ 批量累加测试失败!");
}
log.info("=== 批量累加测试完成 ===");
}
}

View File

@@ -1,136 +0,0 @@
package com.gxwebsoft.shop.service;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import javax.annotation.Resource;
import java.util.List;
/**
* 订单商品忽略租户隔离功能测试
*
* @author WebSoft
* @since 2025-08-23
*/
@Slf4j
@SpringBootTest
@ActiveProfiles("dev")
public class ShopOrderGoodsIgnoreTenantTest {
@Resource
private ShopOrderGoodsService shopOrderGoodsService;
/**
* 测试忽略租户隔离查询订单商品
*/
@Test
public void testGetListByOrderIdIgnoreTenant() {
// 测试订单ID请根据实际数据库中的订单ID进行调整
Integer testOrderId = 1;
log.info("=== 开始测试忽略租户隔离查询订单商品功能 ===");
// 1. 使用普通方法查询订单商品(受租户隔离影响)
List<ShopOrderGoods> orderGoodsNormal = shopOrderGoodsService.getListByOrderId(testOrderId);
log.info("普通查询结果 - 订单ID: {}, 商品数量: {}", testOrderId,
orderGoodsNormal != null ? orderGoodsNormal.size() : 0);
// 2. 使用忽略租户隔离方法查询订单商品
List<ShopOrderGoods> orderGoodsIgnoreTenant = shopOrderGoodsService.getListByOrderIdIgnoreTenant(testOrderId);
log.info("忽略租户隔离查询结果 - 订单ID: {}, 商品数量: {}", testOrderId,
orderGoodsIgnoreTenant != null ? orderGoodsIgnoreTenant.size() : 0);
// 3. 验证结果
if (orderGoodsIgnoreTenant != null && !orderGoodsIgnoreTenant.isEmpty()) {
log.info("✅ 忽略租户隔离查询成功!");
for (ShopOrderGoods orderGoods : orderGoodsIgnoreTenant) {
log.info("订单商品详情 - ID: {}, 商品ID: {}, 商品名称: {}, 数量: {}, 租户ID: {}",
orderGoods.getId(),
orderGoods.getGoodsId(),
orderGoods.getGoodsName(),
orderGoods.getTotalNum(),
orderGoods.getTenantId());
}
} else {
log.warn("⚠️ 忽略租户隔离查询结果为空,可能订单不存在或没有商品");
}
log.info("=== 忽略租户隔离查询订单商品功能测试完成 ===");
}
/**
* 测试参数验证
*/
@Test
public void testGetListByOrderIdIgnoreTenantValidation() {
log.info("=== 开始测试参数验证 ===");
// 测试null订单ID
List<ShopOrderGoods> result1 = shopOrderGoodsService.getListByOrderIdIgnoreTenant(null);
log.info("null订单ID测试结果: {}", result1.isEmpty() ? "成功(返回空列表)" : "失败");
// 测试不存在的订单ID
List<ShopOrderGoods> result2 = shopOrderGoodsService.getListByOrderIdIgnoreTenant(999999);
log.info("不存在订单ID测试结果: {}", result2.isEmpty() ? "成功(返回空列表)" : "失败");
log.info("=== 参数验证测试完成 ===");
}
/**
* 测试跨租户查询
*/
@Test
public void testCrossTenantQuery() {
log.info("=== 开始测试跨租户查询 ===");
// 查询不同租户的订单商品(请根据实际数据调整)
Integer[] testOrderIds = {1, 2, 3, 4, 5};
for (Integer orderId : testOrderIds) {
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(orderId);
if (orderGoodsList != null && !orderGoodsList.isEmpty()) {
log.info("订单ID: {}, 商品数量: {}", orderId, orderGoodsList.size());
for (ShopOrderGoods orderGoods : orderGoodsList) {
log.info(" - 商品: {} (ID: {}), 数量: {}, 租户: {}",
orderGoods.getGoodsName(),
orderGoods.getGoodsId(),
orderGoods.getTotalNum(),
orderGoods.getTenantId());
}
} else {
log.info("订单ID: {} - 无商品或不存在", orderId);
}
}
log.info("=== 跨租户查询测试完成 ===");
}
/**
* 测试批量查询性能
*/
@Test
public void testBatchQuery() {
log.info("=== 开始测试批量查询性能 ===");
Integer[] testOrderIds = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
long startTime = System.currentTimeMillis();
int totalGoods = 0;
for (Integer orderId : testOrderIds) {
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(orderId);
totalGoods += orderGoodsList.size();
}
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
log.info("批量查询结果 - 查询订单数: {}, 总商品数: {}, 耗时: {}ms",
testOrderIds.length, totalGoods, duration);
log.info("=== 批量查询性能测试完成 ===");
}
}

View File

@@ -1,141 +0,0 @@
package com.gxwebsoft.shop.service;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.service.UserService;
import com.gxwebsoft.shop.entity.ShopOrder;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import javax.annotation.Resource;
import java.math.BigDecimal;
/**
* 订单更新业务测试
*
* @author WebSoft
* @since 2025-08-23
*/
@Slf4j
@SpringBootTest
@ActiveProfiles("dev")
public class ShopOrderUpdate10550ServiceTest {
@Resource
private ShopOrderUpdate10550Service shopOrderUpdate10550Service;
@Resource
private UserService userService;
/**
* 测试用户等级升级功能
*/
@Test
public void testUserGradeUpgrade() {
log.info("=== 开始测试用户等级升级功能 ===");
// 创建测试订单
ShopOrder testOrder = createTestOrder();
// 查询用户升级前的信息
User userBefore = userService.getByIdIgnoreTenant(testOrder.getUserId());
if (userBefore != null) {
log.info("升级前用户信息 - ID: {}, 等级: {}, 消费金额: {}",
userBefore.getUserId(), userBefore.getGradeId(), userBefore.getExpendMoney());
}
// 执行订单更新业务
shopOrderUpdate10550Service.update(testOrder);
// 查询用户升级后的信息
User userAfter = userService.getByIdIgnoreTenant(testOrder.getUserId());
if (userAfter != null) {
log.info("升级后用户信息 - ID: {}, 等级: {}, 消费金额: {}",
userAfter.getUserId(), userAfter.getGradeId(), userAfter.getExpendMoney());
}
log.info("=== 用户等级升级功能测试完成 ===");
}
/**
* 测试合伙人条件配置获取
*/
@Test
public void testPartnerConditionConfig() {
log.info("=== 开始测试合伙人条件配置获取 ===");
// 创建测试订单
ShopOrder testOrder = createTestOrder();
// 执行订单更新业务(会在日志中显示合伙人条件)
shopOrderUpdate10550Service.update(testOrder);
log.info("=== 合伙人条件配置获取测试完成 ===");
}
/**
* 测试异常处理
*/
@Test
public void testExceptionHandling() {
log.info("=== 开始测试异常处理 ===");
// 测试null订单
try {
shopOrderUpdate10550Service.update(null);
log.info("null订单处理正常应该有异常日志");
} catch (Exception e) {
log.info("null订单处理捕获异常 - {}", e.getMessage());
}
// 测试无效用户ID的订单
ShopOrder invalidOrder = new ShopOrder();
invalidOrder.setOrderId(999999);
invalidOrder.setUserId(999999);
invalidOrder.setTenantId(10550);
invalidOrder.setPayPrice(new BigDecimal("100.00"));
try {
shopOrderUpdate10550Service.update(invalidOrder);
log.info("无效用户订单处理:正常(应该有警告日志)");
} catch (Exception e) {
log.info("无效用户订单处理:捕获异常 - {}", e.getMessage());
}
log.info("=== 异常处理测试完成 ===");
}
/**
* 测试批量订单处理
*/
@Test
public void testBatchOrderProcessing() {
log.info("=== 开始测试批量订单处理 ===");
// 模拟多个订单
for (int i = 1; i <= 5; i++) {
ShopOrder order = createTestOrder();
order.setOrderId(1000 + i);
order.setPayPrice(new BigDecimal("50.00").multiply(BigDecimal.valueOf(i)));
log.info("处理第{}个订单 - 订单ID: {}, 金额: {}", i, order.getOrderId(), order.getPayPrice());
shopOrderUpdate10550Service.update(order);
}
log.info("=== 批量订单处理测试完成 ===");
}
/**
* 创建测试订单
*/
private ShopOrder createTestOrder() {
ShopOrder order = new ShopOrder();
order.setOrderId(1001);
order.setUserId(1); // 请根据实际数据库中的用户ID调整
order.setTenantId(10550);
order.setPayPrice(new BigDecimal("500.00")); // 测试金额
order.setTotalPrice(new BigDecimal("500.00"));
return order;
}
}

View File

@@ -1,155 +0,0 @@
package com.gxwebsoft.test;
import com.gxwebsoft.common.core.config.ConfigProperties;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.Resource;
import java.io.File;
/**
* 证书路径拼接测试
* 验证开发环境的路径拼接规则配置文件upload-path + dev/wechat/ + 租户ID
*
* @author 科技小王子
* @since 2025-08-09
*/
@SpringBootTest
@ActiveProfiles("dev")
public class CertificatePathConcatenationTest {
@Value("${spring.profiles.active:prod}")
private String activeProfile;
@Resource
private ConfigProperties configProperties;
@Test
public void testCertificatePathConcatenation() {
System.out.println("=== 证书路径拼接测试 ===");
System.out.println("当前环境: " + activeProfile);
if ("dev".equals(activeProfile)) {
testDevEnvironmentPathConcatenation();
} else {
testProdEnvironmentPathConcatenation();
}
System.out.println("=== 证书路径拼接测试完成 ===");
}
private void testDevEnvironmentPathConcatenation() {
System.out.println("--- 开发环境路径拼接测试 ---");
// 获取配置文件中的upload-path
String uploadPath = configProperties.getUploadPath();
System.out.println("配置文件upload-path: " + uploadPath);
// 拼接规则配置文件upload-path + dev/wechat/ + 租户ID
String tenantId = "10550";
String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/";
String privateKeyPath = certBasePath + "apiclient_key.pem";
String certPath = certBasePath + "apiclient_cert.pem";
System.out.println("拼接规则: upload-path + dev/wechat/ + 租户ID");
System.out.println("租户ID: " + tenantId);
System.out.println("证书基础路径: " + certBasePath);
System.out.println("私钥文件路径: " + privateKeyPath);
System.out.println("证书文件路径: " + certPath);
// 验证路径是否正确
File privateKeyFile = new File(privateKeyPath);
File certFile = new File(certPath);
System.out.println("--- 文件存在性验证 ---");
System.out.println("私钥文件存在: " + privateKeyFile.exists());
System.out.println("证书文件存在: " + certFile.exists());
if (privateKeyFile.exists()) {
System.out.println("✅ 私钥文件路径拼接正确");
System.out.println(" 文件大小: " + privateKeyFile.length() + " bytes");
} else {
System.out.println("❌ 私钥文件路径拼接错误或文件不存在");
}
if (certFile.exists()) {
System.out.println("✅ 证书文件路径拼接正确");
System.out.println(" 文件大小: " + certFile.length() + " bytes");
} else {
System.out.println("❌ 证书文件路径拼接错误或文件不存在");
}
// 验证期望的路径
String expectedPath = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/";
System.out.println("--- 路径验证 ---");
System.out.println("期望的证书路径: " + expectedPath);
System.out.println("实际拼接路径: " + certBasePath);
System.out.println("路径匹配: " + expectedPath.equals(certBasePath));
if (expectedPath.equals(certBasePath)) {
System.out.println("✅ 路径拼接规则正确");
} else {
System.out.println("❌ 路径拼接规则需要调整");
System.out.println(" 请检查配置文件中的upload-path设置");
}
}
private void testProdEnvironmentPathConcatenation() {
System.out.println("--- 生产环境路径配置测试 ---");
System.out.println("生产环境使用数据库配置的证书路径");
System.out.println("路径格式: {uploadPath}/file/{relativePath}");
String uploadPath = configProperties.getUploadPath();
System.out.println("配置的upload-path: " + uploadPath);
// 模拟生产环境路径拼接
String relativePath = "wechat/10550/apiclient_key.pem";
String prodPath = uploadPath + "file/" + relativePath;
System.out.println("生产环境示例路径: " + prodPath);
System.out.println("✅ 生产环境路径配置逻辑正确");
}
@Test
public void testMultipleTenantPaths() {
System.out.println("=== 多租户路径拼接测试 ===");
if (!"dev".equals(activeProfile)) {
System.out.println("跳过:仅在开发环境测试多租户路径");
return;
}
String uploadPath = configProperties.getUploadPath();
String[] tenantIds = {"10324", "10398", "10547", "10549", "10550"};
System.out.println("配置文件upload-path: " + uploadPath);
System.out.println("测试多个租户的证书路径拼接:");
for (String tenantId : tenantIds) {
String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/";
String privateKeyPath = certBasePath + "apiclient_key.pem";
File privateKeyFile = new File(privateKeyPath);
System.out.println("租户 " + tenantId + ": " + (privateKeyFile.exists() ? "" : "") + " " + privateKeyPath);
}
System.out.println("=== 多租户路径拼接测试完成 ===");
}
@Test
public void testConfigurationProperties() {
System.out.println("=== 配置属性测试 ===");
System.out.println("当前环境: " + activeProfile);
System.out.println("ConfigProperties注入: " + (configProperties != null ? "" : ""));
if (configProperties != null) {
System.out.println("upload-path: " + configProperties.getUploadPath());
System.out.println("upload-location: " + configProperties.getUploadLocation());
System.out.println("server-url: " + configProperties.getServerUrl());
}
System.out.println("=== 配置属性测试完成 ===");
}
}

View File

@@ -1,99 +0,0 @@
package com.gxwebsoft.test;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* 证书路径修复验证测试
*
* @author 科技小王子
* @since 2025-08-09
*/
@SpringBootTest
@ActiveProfiles("dev")
public class CertificatePathFixTest {
private static final String CERT_BASE_PATH = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550";
@Test
public void testCertificatePathFix() {
System.out.println("=== 证书路径修复验证测试 ===");
// 验证证书目录存在
File certDir = new File(CERT_BASE_PATH);
assert certDir.exists() && certDir.isDirectory() : "证书目录不存在: " + CERT_BASE_PATH;
System.out.println("✅ 证书目录存在: " + CERT_BASE_PATH);
// 验证私钥文件
String privateKeyPath = CERT_BASE_PATH + "/apiclient_key.pem";
File privateKeyFile = new File(privateKeyPath);
assert privateKeyFile.exists() && privateKeyFile.isFile() : "私钥文件不存在: " + privateKeyPath;
System.out.println("✅ 私钥文件存在: " + privateKeyPath);
System.out.println(" 文件大小: " + privateKeyFile.length() + " bytes");
// 验证证书文件
String certPath = CERT_BASE_PATH + "/apiclient_cert.pem";
File certFile = new File(certPath);
assert certFile.exists() && certFile.isFile() : "证书文件不存在: " + certPath;
System.out.println("✅ 证书文件存在: " + certPath);
System.out.println(" 文件大小: " + certFile.length() + " bytes");
// 验证文件内容格式
try {
String privateKeyContent = Files.readString(Paths.get(privateKeyPath));
assert privateKeyContent.contains("-----BEGIN PRIVATE KEY-----") : "私钥文件格式错误";
System.out.println("✅ 私钥文件格式正确");
String certContent = Files.readString(Paths.get(certPath));
assert certContent.contains("-----BEGIN CERTIFICATE-----") : "证书文件格式错误";
System.out.println("✅ 证书文件格式正确");
} catch (Exception e) {
throw new RuntimeException("读取证书文件失败: " + e.getMessage(), e);
}
System.out.println("=== 证书路径修复验证完成 ===");
System.out.println("🎉 所有证书文件验证通过!");
System.out.println();
System.out.println("📋 修复内容总结:");
System.out.println("1. 修复了 SettingServiceImpl 中的硬编码证书路径");
System.out.println("2. 更新了 WechatCertAutoConfig 中的默认开发环境配置");
System.out.println("3. 证书路径已指向正确位置: " + CERT_BASE_PATH);
System.out.println();
System.out.println("🔧 下一步建议:");
System.out.println("1. 重启应用程序以使配置生效");
System.out.println("2. 测试微信支付功能是否正常工作");
System.out.println("3. 检查应用日志确认证书加载成功");
}
@Test
public void testCertificatePathStructure() {
System.out.println("=== 证书目录结构验证 ===");
File baseDir = new File("/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat");
if (baseDir.exists()) {
File[] tenantDirs = baseDir.listFiles(File::isDirectory);
if (tenantDirs != null) {
System.out.println("发现租户证书目录:");
for (File tenantDir : tenantDirs) {
System.out.println(" - 租户ID: " + tenantDir.getName());
File privateKey = new File(tenantDir, "apiclient_key.pem");
File cert = new File(tenantDir, "apiclient_cert.pem");
File p12 = new File(tenantDir, "apiclient_cert.p12");
System.out.println(" 私钥文件: " + (privateKey.exists() ? "" : ""));
System.out.println(" 证书文件: " + (cert.exists() ? "" : ""));
System.out.println(" P12文件: " + (p12.exists() ? "" : ""));
}
}
}
System.out.println("=== 目录结构验证完成 ===");
}
}

View File

@@ -1,107 +0,0 @@
package com.gxwebsoft.test;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.utils.CertificateLoader;
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
import com.wechat.pay.java.core.Config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
/**
* 证书测试类
*/
@SpringBootTest
@ActiveProfiles("dev")
public class CertificateTest {
@Autowired
private CertificateProperties certConfig;
@Autowired
private CertificateLoader certificateLoader;
@Autowired
private WechatCertAutoConfig wechatCertAutoConfig;
@Test
public void testCertificateLoading() {
try {
System.out.println("=== 证书加载测试 ===");
// 测试租户ID
String tenantId = "10550";
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
System.out.println("证书路径: " + privateKeyPath);
System.out.println("加载模式: " + certConfig.getLoadMode());
System.out.println("开发环境证书路径前缀: " + certConfig.getDevCertPath());
// 检查证书文件是否存在
boolean exists = certificateLoader.certificateExists(privateKeyPath);
System.out.println("证书文件是否存在: " + exists);
if (!exists) {
System.err.println("❌ 证书文件不存在: " + privateKeyPath);
System.out.println("💡 请确认证书文件已放置在正确位置:");
System.out.println(" src/main/resources/dev/wechat/10550/apiclient_key.pem");
return;
}
// 测试证书加载
String privateKeyFile = certificateLoader.loadCertificatePath(privateKeyPath);
System.out.println("✅ 私钥文件加载成功: " + privateKeyFile);
// 测试自动证书配置
System.out.println("=== 测试自动证书配置 ===");
Config config = wechatCertAutoConfig.createAutoConfig(
"1723321338", // 测试商户号
privateKeyFile,
"test-serial-number", // 测试序列号
"test-api-key" // 测试API密钥
);
System.out.println("✅ 自动证书配置创建成功");
} catch (Exception e) {
System.err.println("❌ 证书测试失败: " + e.getMessage());
e.printStackTrace();
}
}
@Test
public void testNotificationCertificateConfig() {
try {
System.out.println("=== 异步通知证书配置测试 ===");
// 模拟异步通知中的证书配置逻辑
String tenantId = "10550";
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
System.out.println("租户ID: " + tenantId);
System.out.println("证书目录: " + tenantCertPath);
System.out.println("私钥路径: " + privateKeyPath);
// 检查证书文件是否存在
if (!certificateLoader.certificateExists(privateKeyPath)) {
System.err.println("❌ 证书文件不存在: " + privateKeyPath);
throw new RuntimeException("证书文件不存在: " + privateKeyPath);
}
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
String apiV3Key = certConfig.getWechatPay().getDev().getApiV3Key();
System.out.println("✅ 私钥文件加载成功: " + privateKey);
System.out.println("APIv3密钥配置: " + (apiV3Key != null ? "已配置" : "未配置"));
System.out.println("✅ 异步通知证书配置测试通过");
} catch (Exception e) {
System.err.println("❌ 异步通知证书配置测试失败: " + e.getMessage());
e.printStackTrace();
}
}
}

View File

@@ -1,119 +0,0 @@
package com.gxwebsoft.test;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.Resource;
import java.io.File;
/**
* 基于环境的证书路径配置测试
*
* @author 科技小王子
* @since 2025-08-09
*/
@SpringBootTest
@ActiveProfiles("dev")
public class EnvironmentBasedCertificateTest {
@Value("${spring.profiles.active:prod}")
private String activeProfile;
@Test
public void testEnvironmentBasedCertificateConfig() {
System.out.println("=== 环境基础证书配置测试 ===");
System.out.println("当前激活的环境: " + activeProfile);
if ("dev".equals(activeProfile)) {
System.out.println("✅ 检测到开发环境");
testDevEnvironmentCertificates();
} else {
System.out.println("✅ 检测到生产环境");
testProdEnvironmentCertificates();
}
System.out.println("=== 环境基础证书配置测试完成 ===");
}
private void testDevEnvironmentCertificates() {
System.out.println("--- 开发环境证书路径测试 ---");
String devCertPath = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550";
String privateKeyPath = devCertPath + "/apiclient_key.pem";
String certPath = devCertPath + "/apiclient_cert.pem";
System.out.println("开发环境证书目录: " + devCertPath);
System.out.println("私钥文件路径: " + privateKeyPath);
System.out.println("证书文件路径: " + certPath);
// 验证文件存在
File privateKeyFile = new File(privateKeyPath);
File certFile = new File(certPath);
assert privateKeyFile.exists() : "开发环境私钥文件不存在: " + privateKeyPath;
assert certFile.exists() : "开发环境证书文件不存在: " + certPath;
System.out.println("✅ 开发环境证书文件验证通过");
System.out.println(" - 私钥文件大小: " + privateKeyFile.length() + " bytes");
System.out.println(" - 证书文件大小: " + certFile.length() + " bytes");
}
private void testProdEnvironmentCertificates() {
System.out.println("--- 生产环境证书路径测试 ---");
System.out.println("生产环境将使用数据库配置的证书路径");
System.out.println("证书路径格式: {uploadPath}/file/{relativePath}");
System.out.println("✅ 生产环境配置逻辑正确");
}
@Test
public void testCertificatePathLogic() {
System.out.println("=== 证书路径逻辑测试 ===");
// 模拟不同环境的路径构建逻辑
String uploadPath = "/www/wwwroot/file.ws/";
String relativePath = "wechat/10550/apiclient_key.pem";
if ("dev".equals(activeProfile)) {
// 开发环境:使用固定的本地路径
String devPath = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/apiclient_key.pem";
System.out.println("开发环境路径: " + devPath);
File devFile = new File(devPath);
System.out.println("开发环境文件存在: " + devFile.exists());
} else {
// 生产环境:使用数据库配置的路径
String prodPath = uploadPath + "file/" + relativePath;
System.out.println("生产环境路径: " + prodPath);
System.out.println("生产环境路径构建逻辑正确");
}
System.out.println("=== 证书路径逻辑测试完成 ===");
}
@Test
public void testEnvironmentSwitching() {
System.out.println("=== 环境切换测试 ===");
// 测试环境判断逻辑
System.out.println("当前环境: " + activeProfile);
if ("dev".equals(activeProfile)) {
System.out.println("✅ 开发环境配置:");
System.out.println(" - 使用本地固定证书路径");
System.out.println(" - 路径: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/");
System.out.println(" - 优点: 开发便利,无需配置数据库路径");
} else if ("prod".equals(activeProfile)) {
System.out.println("✅ 生产环境配置:");
System.out.println(" - 使用数据库存储的证书路径");
System.out.println(" - 路径格式: {uploadPath}/file/{relativePath}");
System.out.println(" - 优点: 灵活配置,支持多租户");
} else {
System.out.println("⚠️ 未知环境: " + activeProfile);
System.out.println(" - 默认使用生产环境配置");
}
System.out.println("=== 环境切换测试完成 ===");
}
}

View File

@@ -1,101 +0,0 @@
package com.gxwebsoft.test;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* 异步通知证书修复验证测试
*/
public class NotificationCertificateFixTest {
@Test
public void testCertificatePathFix() {
System.out.println("=== 异步通知证书路径修复验证 ===");
// 模拟异步通知中的证书路径构建逻辑
String tenantId = "10550";
String devCertPath = "dev";
String certDir = "wechat";
String privateKeyFile = "apiclient_key.pem";
// 修复后的路径构建逻辑
String tenantCertPath = devCertPath + "/" + certDir + "/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + privateKeyFile;
System.out.println("租户ID: " + tenantId);
System.out.println("证书目录: " + tenantCertPath);
System.out.println("私钥路径: " + privateKeyPath);
// 测试从classpath加载证书
try {
Resource resource = new ClassPathResource(privateKeyPath);
System.out.println("证书资源存在: " + resource.exists());
if (resource.exists()) {
// 模拟CertificateLoader.loadFromClasspath的逻辑
Path tempFile = Files.createTempFile("cert_", ".pem");
try (InputStream inputStream = resource.getInputStream()) {
Files.copy(inputStream, tempFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
}
String tempPath = tempFile.toAbsolutePath().toString();
System.out.println("✅ 证书加载成功: " + tempPath);
// 验证临时文件
File tempCertFile = new File(tempPath);
System.out.println("临时证书文件大小: " + tempCertFile.length() + " bytes");
// 清理临时文件
Files.deleteIfExists(tempFile);
} else {
System.err.println("❌ 证书文件不存在: " + privateKeyPath);
}
} catch (IOException e) {
System.err.println("❌ 证书加载失败: " + e.getMessage());
e.printStackTrace();
}
System.out.println("=== 测试完成 ===");
}
@Test
public void testAllTenantCertificates() {
System.out.println("=== 测试所有租户证书 ===");
String[] tenantIds = {"10398", "10550"};
String devCertPath = "dev";
String certDir = "wechat";
String privateKeyFile = "apiclient_key.pem";
for (String tenantId : tenantIds) {
System.out.println("\n--- 测试租户: " + tenantId + " ---");
String tenantCertPath = devCertPath + "/" + certDir + "/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + privateKeyFile;
System.out.println("证书路径: " + privateKeyPath);
try {
Resource resource = new ClassPathResource(privateKeyPath);
if (resource.exists()) {
System.out.println("✅ 租户 " + tenantId + " 证书存在");
} else {
System.out.println("❌ 租户 " + tenantId + " 证书不存在");
}
} catch (Exception e) {
System.err.println("❌ 租户 " + tenantId + " 证书检查失败: " + e.getMessage());
}
}
System.out.println("\n=== 所有租户证书测试完成 ===");
}
}

View File

@@ -1,89 +0,0 @@
package com.gxwebsoft.test;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.utils.CertificateLoader;
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
import com.gxwebsoft.common.core.service.PaymentCacheService;
import com.gxwebsoft.common.system.entity.Payment;
import com.wechat.pay.java.core.Config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
/**
* 微信支付配置测试
*/
@SpringBootTest
@ActiveProfiles("dev")
public class WechatPayConfigTest {
@Autowired
private CertificateProperties certConfig;
@Autowired
private CertificateLoader certificateLoader;
@Autowired
private WechatCertAutoConfig wechatCertAutoConfig;
@Autowired
private PaymentCacheService paymentCacheService;
@Test
public void testWechatPayConfig() {
try {
System.out.println("=== 微信支付配置测试 ===");
// 测试租户ID
String tenantId = "10550";
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
System.out.println("证书路径: " + privateKeyPath);
System.out.println("加载模式: " + certConfig.getLoadMode());
// 测试证书加载
String privateKeyFile = certificateLoader.loadCertificatePath(privateKeyPath);
System.out.println("私钥文件路径: " + privateKeyFile);
// 测试数据库支付配置
System.out.println("=== 测试数据库支付配置 ===");
try {
Payment payment = paymentCacheService.getPaymentConfig(0, 10550); // 微信支付租户ID 10550
System.out.println("数据库配置获取成功:");
System.out.println("商户号: " + payment.getMchId());
System.out.println("序列号: " + payment.getMerchantSerialNumber());
System.out.println("API密钥: " + (payment.getApiKey() != null ? "已配置(长度:" + payment.getApiKey().length() + ")" : "未配置"));
System.out.println("应用ID: " + payment.getAppId());
// 使用数据库配置进行测试
if (payment.getMchId() != null && payment.getMerchantSerialNumber() != null && payment.getApiKey() != null) {
Config dbConfig = wechatCertAutoConfig.createAutoConfig(
payment.getMchId(),
privateKeyFile,
payment.getMerchantSerialNumber(),
payment.getApiKey()
);
System.out.println("使用数据库配置创建成功: " + (dbConfig != null));
} else {
System.out.println("数据库配置不完整,无法创建微信支付配置");
}
} catch (Exception e) {
System.err.println("数据库配置获取失败: " + e.getMessage());
// 回退到配置文件参数
System.out.println("=== 回退到配置文件参数 ===");
String devApiKey = certConfig.getWechatPay().getDev().getApiV3Key();
System.out.println("API密钥: " + (devApiKey != null ? "已配置(长度:" + devApiKey.length() + ")" : "未配置"));
}
System.out.println("=== 测试完成 ===");
} catch (Exception e) {
System.err.println("微信支付配置测试失败: " + e.getMessage());
e.printStackTrace();
}
}
}

View File

@@ -1,134 +0,0 @@
package com.gxwebsoft.test;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.utils.CertificateLoader;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.system.entity.Payment;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
/**
* 微信支付配置验证测试
*/
@SpringBootTest
@ActiveProfiles("dev")
public class WechatPayConfigValidationTest {
@Autowired
private CertificateProperties certConfig;
@Autowired
private CertificateLoader certificateLoader;
@Autowired
private RedisUtil redisUtil;
@Test
public void testWechatPayConfigValidation() {
System.out.println("=== 微信支付配置验证 ===");
// 1. 检查配置文件中的 APIv3 密钥
String configApiV3Key = certConfig.getWechatPay().getDev().getApiV3Key();
System.out.println("配置文件 APIv3 密钥: " + configApiV3Key);
System.out.println("配置文件 APIv3 密钥长度: " + (configApiV3Key != null ? configApiV3Key.length() : 0));
// 2. 检查 Redis 中的支付配置
String tenantId = "10550";
String redisKey = "Payment:1:" + tenantId;
Payment payment = redisUtil.get(redisKey, Payment.class);
if (payment != null) {
System.out.println("\n=== Redis 支付配置 ===");
System.out.println("商户号: " + payment.getMchId());
System.out.println("应用ID: " + payment.getAppId());
System.out.println("数据库 APIv3 密钥: " + payment.getApiKey());
System.out.println("数据库 APIv3 密钥长度: " + (payment.getApiKey() != null ? payment.getApiKey().length() : 0));
System.out.println("商户证书序列号: " + payment.getMerchantSerialNumber());
// 3. 比较两个 APIv3 密钥
System.out.println("\n=== APIv3 密钥比较 ===");
boolean keysMatch = (configApiV3Key != null && configApiV3Key.equals(payment.getApiKey()));
System.out.println("配置文件与数据库密钥是否一致: " + keysMatch);
if (!keysMatch) {
System.out.println("⚠️ 警告: 配置文件与数据库中的 APIv3 密钥不一致!");
System.out.println("配置文件密钥: " + configApiV3Key);
System.out.println("数据库密钥: " + payment.getApiKey());
}
} else {
System.out.println("❌ 未找到 Redis 中的支付配置: " + redisKey);
// 尝试其他可能的键格式
String[] possibleKeys = {
"Payment:1:10550",
"Payment:0:10550",
"Payment:10",
"Payment:1" + "0" // Payment:10
};
System.out.println("\n=== 尝试其他 Redis 键格式 ===");
for (String key : possibleKeys) {
Payment p = redisUtil.get(key, Payment.class);
if (p != null) {
System.out.println("✅ 找到支付配置: " + key);
System.out.println(" 商户号: " + p.getMchId());
System.out.println(" APIv3密钥: " + p.getApiKey());
break;
}
}
}
// 4. 验证证书文件
System.out.println("\n=== 证书文件验证 ===");
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
boolean certExists = certificateLoader.certificateExists(privateKeyPath);
System.out.println("证书文件存在: " + certExists);
System.out.println("证书路径: " + privateKeyPath);
if (certExists) {
try {
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
System.out.println("✅ 证书加载成功: " + privateKey);
} catch (Exception e) {
System.out.println("❌ 证书加载失败: " + e.getMessage());
}
}
System.out.println("\n=== 验证完成 ===");
}
@Test
public void testApiV3KeyValidation() {
System.out.println("=== APIv3 密钥格式验证 ===");
String configKey = certConfig.getWechatPay().getDev().getApiV3Key();
if (configKey != null) {
System.out.println("APIv3 密钥: " + configKey);
System.out.println("密钥长度: " + configKey.length());
// APIv3 密钥应该是32位字符串
if (configKey.length() == 32) {
System.out.println("✅ APIv3 密钥长度正确 (32位)");
} else {
System.out.println("❌ APIv3 密钥长度错误应为32位实际为: " + configKey.length());
}
// 检查是否包含特殊字符
boolean hasSpecialChars = !configKey.matches("^[a-zA-Z0-9]+$");
if (hasSpecialChars) {
System.out.println("⚠️ APIv3 密钥包含特殊字符,可能导致解密失败");
} else {
System.out.println("✅ APIv3 密钥格式正确 (仅包含字母和数字)");
}
} else {
System.out.println("❌ APIv3 密钥未配置");
}
}
}

View File

@@ -1,158 +0,0 @@
package com.gxwebsoft.test;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.service.PaymentCacheService;
import com.gxwebsoft.common.core.utils.CertificateLoader;
import com.gxwebsoft.common.system.entity.Payment;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 微信支付路径处理测试
*
* @author 科技小王子
* @since 2025-07-29
*/
@SpringBootTest
public class WechatPayPathTest {
@Autowired
private PaymentCacheService paymentCacheService;
@Autowired
private CertificateLoader certificateLoader;
@Autowired
private CertificateProperties certConfig;
@Test
public void testPublicKeyPathHandling() {
try {
System.out.println("=== 微信支付公钥路径处理测试 ===");
// 测试租户ID
Integer tenantId = 10547;
// 获取支付配置
Payment payment = paymentCacheService.getWechatPayConfig(tenantId);
if (payment == null) {
System.err.println("❌ 支付配置不存在");
return;
}
System.out.println("数据库配置信息:");
System.out.println("租户ID: " + tenantId);
System.out.println("商户号: " + payment.getMchId());
System.out.println("公钥文件配置: " + payment.getPubKey());
System.out.println("公钥ID: " + payment.getPubKeyId());
// 测试路径处理逻辑
if (payment.getPubKey() != null && !payment.getPubKey().isEmpty()) {
System.out.println("\n=== 路径处理测试 ===");
String pubKeyPath;
if (payment.getPubKey().startsWith("dev/wechat/") || payment.getPubKey().startsWith("/")) {
// 如果数据库中存储的是完整路径,直接使用
pubKeyPath = payment.getPubKey();
System.out.println("✅ 检测到完整路径,直接使用: " + pubKeyPath);
} else {
// 如果是相对路径,需要拼接租户目录
String tenantCertPath = "dev/wechat/" + tenantId;
pubKeyPath = tenantCertPath + "/" + payment.getPubKey();
System.out.println("✅ 检测到相对路径,拼接后: " + pubKeyPath);
}
// 测试文件是否存在
System.out.println("\n=== 文件存在性检查 ===");
if (certificateLoader.certificateExists(pubKeyPath)) {
try {
String actualPath = certificateLoader.loadCertificatePath(pubKeyPath);
System.out.println("✅ 公钥文件存在: " + actualPath);
// 检查文件大小
java.io.File file = new java.io.File(actualPath);
if (file.exists()) {
System.out.println("文件大小: " + file.length() + " 字节");
System.out.println("文件可读: " + file.canRead());
}
} catch (Exception e) {
System.err.println("❌ 加载公钥文件失败: " + e.getMessage());
}
} else {
System.err.println("❌ 公钥文件不存在: " + pubKeyPath);
// 提供修复建议
System.out.println("\n=== 修复建议 ===");
if (payment.getPubKey().contains("/")) {
System.out.println("1. 检查数据库中的路径是否正确");
System.out.println("2. 确认文件是否已上传到指定位置");
System.out.println("3. 当前配置的路径: " + payment.getPubKey());
} else {
System.out.println("1. 将公钥文件放置到: src/main/resources/dev/wechat/" + tenantId + "/");
System.out.println("2. 或者更新数据库配置为完整路径");
}
}
// 测试其他证书文件
System.out.println("\n=== 其他证书文件检查 ===");
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
String merchantCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
System.out.println("私钥文件: " + privateKeyPath + " - " +
(certificateLoader.certificateExists(privateKeyPath) ? "✅ 存在" : "❌ 不存在"));
System.out.println("商户证书: " + merchantCertPath + " - " +
(certificateLoader.certificateExists(merchantCertPath) ? "✅ 存在" : "❌ 不存在"));
} else {
System.out.println("⚠️ 未配置公钥信息");
}
System.out.println("\n=== 测试完成 ===");
} catch (Exception e) {
System.err.println("❌ 测试失败: " + e.getMessage());
e.printStackTrace();
}
}
@Test
public void testCreateCorrectDirectoryStructure() {
System.out.println("=== 创建正确的目录结构建议 ===");
try {
Integer tenantId = 10547;
Payment payment = paymentCacheService.getWechatPayConfig(tenantId);
if (payment != null && payment.getPubKey() != null) {
System.out.println("当前数据库配置的公钥路径: " + payment.getPubKey());
if (payment.getPubKey().contains("/")) {
// 包含路径分隔符,说明是完整路径
System.out.println("\n建议的文件放置位置:");
System.out.println("src/main/resources/" + payment.getPubKey());
// 创建目录的命令
String dirPath = payment.getPubKey().substring(0, payment.getPubKey().lastIndexOf("/"));
System.out.println("\n创建目录的命令:");
System.out.println("mkdir -p src/main/resources/" + dirPath);
// 复制文件的命令
System.out.println("\n如果您有公钥文件可以这样复制:");
System.out.println("cp your_public_key.pem src/main/resources/" + payment.getPubKey());
} else {
// 只是文件名,需要放在租户目录下
System.out.println("\n建议的文件放置位置:");
System.out.println("src/main/resources/dev/wechat/" + tenantId + "/" + payment.getPubKey());
System.out.println("\n或者更新数据库配置为:");
System.out.println("UPDATE sys_payment SET pub_key = 'dev/wechat/" + tenantId + "/" + payment.getPubKey() + "' WHERE tenant_id = " + tenantId + " AND type = 0;");
}
}
} catch (Exception e) {
System.err.println("获取配置失败: " + e.getMessage());
}
}
}

View File

@@ -1,150 +0,0 @@
package com.gxwebsoft.test;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.service.PaymentCacheService;
import com.gxwebsoft.common.core.utils.CertificateLoader;
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
import com.gxwebsoft.common.system.entity.Payment;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 微信支付公钥配置测试
*
* @author 科技小王子
* @since 2025-07-29
*/
@SpringBootTest
public class WechatPayPublicKeyTest {
@Autowired
private PaymentCacheService paymentCacheService;
@Autowired
private CertificateLoader certificateLoader;
@Autowired
private CertificateProperties certConfig;
@Autowired
private WechatCertAutoConfig wechatCertAutoConfig;
@Test
public void testPublicKeyConfiguration() {
try {
System.out.println("=== 微信支付公钥配置测试 ===");
// 测试租户ID
Integer tenantId = 10547;
// 获取支付配置
Payment payment = paymentCacheService.getWechatPayConfig(tenantId);
System.out.println("支付配置获取成功:");
System.out.println("商户号: " + payment.getMchId());
System.out.println("应用ID: " + payment.getAppId());
System.out.println("序列号: " + payment.getMerchantSerialNumber());
System.out.println("API密钥: " + (payment.getApiKey() != null ? "已配置(长度:" + payment.getApiKey().length() + ")" : "未配置"));
System.out.println("公钥文件: " + payment.getPubKey());
System.out.println("公钥ID: " + payment.getPubKeyId());
// 测试证书文件加载
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
System.out.println("\n=== 证书文件检查 ===");
System.out.println("私钥路径: " + privateKeyPath);
if (certificateLoader.certificateExists(privateKeyPath)) {
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
System.out.println("✅ 私钥文件加载成功: " + privateKey);
// 检查是否配置了公钥
if (payment.getPubKey() != null && !payment.getPubKey().isEmpty() &&
payment.getPubKeyId() != null && !payment.getPubKeyId().isEmpty()) {
System.out.println("\n=== 公钥配置测试 ===");
String pubKeyPath = tenantCertPath + "/" + payment.getPubKey();
System.out.println("公钥路径: " + pubKeyPath);
if (certificateLoader.certificateExists(pubKeyPath)) {
String pubKeyFile = certificateLoader.loadCertificatePath(pubKeyPath);
System.out.println("✅ 公钥文件加载成功: " + pubKeyFile);
// 测试RSA公钥配置
try {
Config config = new RSAPublicKeyConfig.Builder()
.merchantId(payment.getMchId())
.privateKeyFromPath(privateKey)
.publicKeyFromPath(pubKeyFile)
.publicKeyId(payment.getPubKeyId())
.merchantSerialNumber(payment.getMerchantSerialNumber())
.apiV3Key(payment.getApiKey())
.build();
System.out.println("✅ RSA公钥配置创建成功");
System.out.println("配置类型: " + config.getClass().getSimpleName());
} catch (Exception e) {
System.err.println("❌ RSA公钥配置失败: " + e.getMessage());
e.printStackTrace();
}
} else {
System.err.println("❌ 公钥文件不存在: " + pubKeyPath);
System.out.println("💡 建议: 请将公钥文件放置到指定位置");
}
} else {
System.out.println("\n⚠ 未配置公钥信息");
System.out.println("💡 建议: 在数据库中配置 pubKey 和 pubKeyId 字段");
// 测试自动证书配置
System.out.println("\n=== 自动证书配置测试 ===");
try {
Config autoConfig = wechatCertAutoConfig.createAutoConfig(
payment.getMchId(),
privateKey,
payment.getMerchantSerialNumber(),
payment.getApiKey()
);
System.out.println("✅ 自动证书配置创建成功");
System.out.println("配置类型: " + autoConfig.getClass().getSimpleName());
} catch (Exception e) {
System.err.println("❌ 自动证书配置失败: " + e.getMessage());
System.err.println("错误类型: " + e.getClass().getName());
if (e.getMessage() != null && e.getMessage().contains("certificate")) {
System.err.println("🔍 这是证书相关错误,建议使用公钥模式");
}
}
}
} else {
System.err.println("❌ 私钥文件不存在: " + privateKeyPath);
}
System.out.println("\n=== 测试完成 ===");
} catch (Exception e) {
System.err.println("❌ 测试失败: " + e.getMessage());
e.printStackTrace();
}
}
@Test
public void testCreateSamplePublicKeyConfig() {
System.out.println("=== 创建示例公钥配置 ===");
System.out.println("如果您的后台使用公钥模式,请在数据库中配置以下字段:");
System.out.println("1. pubKey: 公钥文件名,例如 'wechatpay_public_key.pem'");
System.out.println("2. pubKeyId: 公钥ID例如 'PUB_KEY_ID_0112422897022025011300326200001208'");
System.out.println("3. 将公钥文件放置到: src/main/resources/dev/wechat/{tenantId}/");
System.out.println("\n示例SQL更新语句:");
System.out.println("UPDATE sys_payment SET ");
System.out.println(" pub_key = 'wechatpay_public_key.pem',");
System.out.println(" pub_key_id = 'PUB_KEY_ID_0112422897022025011300326200001208'");
System.out.println("WHERE tenant_id = 10547 AND type = 0;");
}
}