Browse Source
- 新增 BigDecimalDeserializer 自定义反序列化器,处理 null值和空字符串 - 添加 DatabaseFixController 控制器,用于检查和修复数据库中的 null值问题 - 修改 ShopUserCouponController 中的查询逻辑,确保 BigDecimal 字段不为 null - 更新 ShopCoupon 和 ShopUserCoupon 实体类,为 BigDecimal 字段添加 JsonSerialize 和 JsonInclude 注解 - 新增 SQL 脚本 fix_bigdecimal_null_values.sql,用于修复数据库中的 null 值问题- 修改 application.yml,配置 Jackson序列化和反序列化相关参数main
8 changed files with 419 additions and 13 deletions
@ -0,0 +1,41 @@ |
|||||
|
package com.gxwebsoft.common.core.config; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.JsonParser; |
||||
|
import com.fasterxml.jackson.databind.DeserializationContext; |
||||
|
import com.fasterxml.jackson.databind.JsonDeserializer; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.math.BigDecimal; |
||||
|
|
||||
|
/** |
||||
|
* BigDecimal 自定义反序列化器 |
||||
|
* 处理null值和空字符串,避免反序列化异常 |
||||
|
* |
||||
|
* @author WebSoft |
||||
|
* @since 2025-01-15 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public class BigDecimalDeserializer extends JsonDeserializer<BigDecimal> { |
||||
|
|
||||
|
@Override |
||||
|
public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { |
||||
|
String value = p.getValueAsString(); |
||||
|
|
||||
|
if (value == null || value.trim().isEmpty() || "null".equals(value)) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
return new BigDecimal(value); |
||||
|
} catch (NumberFormatException e) { |
||||
|
log.warn("无法解析BigDecimal值: {}, 返回null", value); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public BigDecimal getNullValue(DeserializationContext ctxt) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
@ -0,0 +1,204 @@ |
|||||
|
package com.gxwebsoft.common.core.controller; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import com.gxwebsoft.common.core.web.ApiResult; |
||||
|
import com.gxwebsoft.common.core.web.BaseController; |
||||
|
import com.gxwebsoft.shop.entity.ShopCoupon; |
||||
|
import com.gxwebsoft.shop.entity.ShopUserCoupon; |
||||
|
import com.gxwebsoft.shop.service.ShopCouponService; |
||||
|
import com.gxwebsoft.shop.service.ShopUserCouponService; |
||||
|
import io.swagger.v3.oas.annotations.Operation; |
||||
|
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* 数据库修复工具控制器 |
||||
|
* 仅在开发环境启用,用于修复数据库问题 |
||||
|
* |
||||
|
* @author WebSoft |
||||
|
* @since 2025-01-15 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Tag(name = "数据库修复工具") |
||||
|
@RestController |
||||
|
@RequestMapping("/api/database-fix") |
||||
|
// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
|
||||
|
public class DatabaseFixController extends BaseController { |
||||
|
|
||||
|
@Autowired |
||||
|
private ShopUserCouponService shopUserCouponService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ShopCouponService shopCouponService; |
||||
|
|
||||
|
@Operation(summary = "检查BigDecimal null值问题") |
||||
|
@GetMapping("/check-bigdecimal-nulls") |
||||
|
public ApiResult<Map<String, Object>> checkBigDecimalNulls() { |
||||
|
try { |
||||
|
Map<String, Object> result = new HashMap<>(); |
||||
|
|
||||
|
// 检查用户优惠券表
|
||||
|
List<ShopUserCoupon> userCoupons = shopUserCouponService.list(); |
||||
|
long userCouponNullReducePrice = userCoupons.stream() |
||||
|
.mapToLong(c -> c.getReducePrice() == null ? 1 : 0) |
||||
|
.sum(); |
||||
|
long userCouponNullMinPrice = userCoupons.stream() |
||||
|
.mapToLong(c -> c.getMinPrice() == null ? 1 : 0) |
||||
|
.sum(); |
||||
|
|
||||
|
// 检查优惠券模板表
|
||||
|
List<ShopCoupon> coupons = shopCouponService.list(); |
||||
|
long couponNullReducePrice = coupons.stream() |
||||
|
.mapToLong(c -> c.getReducePrice() == null ? 1 : 0) |
||||
|
.sum(); |
||||
|
long couponNullMinPrice = coupons.stream() |
||||
|
.mapToLong(c -> c.getMinPrice() == null ? 1 : 0) |
||||
|
.sum(); |
||||
|
|
||||
|
Map<String, Object> userCouponStats = new HashMap<>(); |
||||
|
userCouponStats.put("totalRecords", userCoupons.size()); |
||||
|
userCouponStats.put("nullReducePrice", userCouponNullReducePrice); |
||||
|
userCouponStats.put("nullMinPrice", userCouponNullMinPrice); |
||||
|
|
||||
|
Map<String, Object> couponStats = new HashMap<>(); |
||||
|
couponStats.put("totalRecords", coupons.size()); |
||||
|
couponStats.put("nullReducePrice", couponNullReducePrice); |
||||
|
couponStats.put("nullMinPrice", couponNullMinPrice); |
||||
|
|
||||
|
result.put("shopUserCoupon", userCouponStats); |
||||
|
result.put("shopCoupon", couponStats); |
||||
|
result.put("needsFix", userCouponNullReducePrice > 0 || userCouponNullMinPrice > 0 || |
||||
|
couponNullReducePrice > 0 || couponNullMinPrice > 0); |
||||
|
|
||||
|
return success("检查完成", result); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
log.error("检查BigDecimal null值失败", e); |
||||
|
return fail("检查失败: " + e.getMessage(),null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "修复BigDecimal null值问题") |
||||
|
@PostMapping("/fix-bigdecimal-nulls") |
||||
|
public ApiResult<Map<String, Object>> fixBigDecimalNulls() { |
||||
|
try { |
||||
|
Map<String, Object> result = new HashMap<>(); |
||||
|
int userCouponFixed = 0; |
||||
|
int couponFixed = 0; |
||||
|
|
||||
|
// 修复用户优惠券表
|
||||
|
List<ShopUserCoupon> userCoupons = shopUserCouponService.list(); |
||||
|
for (ShopUserCoupon userCoupon : userCoupons) { |
||||
|
boolean needUpdate = false; |
||||
|
|
||||
|
if (userCoupon.getReducePrice() == null) { |
||||
|
userCoupon.setReducePrice(BigDecimal.ZERO); |
||||
|
needUpdate = true; |
||||
|
} |
||||
|
|
||||
|
if (userCoupon.getMinPrice() == null) { |
||||
|
userCoupon.setMinPrice(BigDecimal.ZERO); |
||||
|
needUpdate = true; |
||||
|
} |
||||
|
|
||||
|
if (needUpdate) { |
||||
|
shopUserCouponService.updateById(userCoupon); |
||||
|
userCouponFixed++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 修复优惠券模板表
|
||||
|
List<ShopCoupon> coupons = shopCouponService.list(); |
||||
|
for (ShopCoupon coupon : coupons) { |
||||
|
boolean needUpdate = false; |
||||
|
|
||||
|
if (coupon.getReducePrice() == null) { |
||||
|
coupon.setReducePrice(BigDecimal.ZERO); |
||||
|
needUpdate = true; |
||||
|
} |
||||
|
|
||||
|
if (coupon.getMinPrice() == null) { |
||||
|
coupon.setMinPrice(BigDecimal.ZERO); |
||||
|
needUpdate = true; |
||||
|
} |
||||
|
|
||||
|
if (needUpdate) { |
||||
|
shopCouponService.updateById(coupon); |
||||
|
couponFixed++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result.put("userCouponFixed", userCouponFixed); |
||||
|
result.put("couponFixed", couponFixed); |
||||
|
result.put("totalFixed", userCouponFixed + couponFixed); |
||||
|
|
||||
|
log.info("BigDecimal null值修复完成: 用户优惠券{}条, 优惠券模板{}条", userCouponFixed, couponFixed); |
||||
|
|
||||
|
return success("修复完成", result); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
log.error("修复BigDecimal null值失败", e); |
||||
|
return fail("修复失败: " + e.getMessage(), null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "测试优惠券接口") |
||||
|
@GetMapping("/test-coupon-api") |
||||
|
public ApiResult<Map<String, Object>> testCouponApi() { |
||||
|
try { |
||||
|
Map<String, Object> result = new HashMap<>(); |
||||
|
|
||||
|
// 测试查询用户优惠券
|
||||
|
List<ShopUserCoupon> userCoupons = shopUserCouponService.list( |
||||
|
new QueryWrapper<ShopUserCoupon>().last("LIMIT 5") |
||||
|
); |
||||
|
|
||||
|
// 测试查询优惠券模板
|
||||
|
List<ShopCoupon> coupons = shopCouponService.list( |
||||
|
new QueryWrapper<ShopCoupon>().last("LIMIT 5") |
||||
|
); |
||||
|
|
||||
|
result.put("userCouponsCount", userCoupons.size()); |
||||
|
result.put("couponsCount", coupons.size()); |
||||
|
result.put("userCouponsSample", userCoupons); |
||||
|
result.put("couponsSample", coupons); |
||||
|
result.put("testStatus", "SUCCESS"); |
||||
|
|
||||
|
return success("测试成功", result); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
log.error("测试优惠券接口失败", e); |
||||
|
Map<String, Object> errorResult = new HashMap<>(); |
||||
|
errorResult.put("testStatus", "FAILED"); |
||||
|
errorResult.put("errorMessage", e.getMessage()); |
||||
|
errorResult.put("errorType", e.getClass().getSimpleName()); |
||||
|
|
||||
|
return fail("测试失败: " + e.getMessage(), errorResult); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "获取修复指南") |
||||
|
@GetMapping("/guide") |
||||
|
public ApiResult<Map<String, String>> getFixGuide() { |
||||
|
Map<String, String> guide = new HashMap<>(); |
||||
|
|
||||
|
guide.put("step1", "GET /api/database-fix/check-bigdecimal-nulls - 检查null值问题"); |
||||
|
guide.put("step2", "POST /api/database-fix/fix-bigdecimal-nulls - 修复null值问题"); |
||||
|
guide.put("step3", "GET /api/database-fix/test-coupon-api - 测试修复效果"); |
||||
|
guide.put("step4", "重启应用,验证优惠券功能正常"); |
||||
|
|
||||
|
guide.put("note1", "此工具仅在开发环境可用"); |
||||
|
guide.put("note2", "修复前建议备份数据库"); |
||||
|
guide.put("note3", "修复完成后可以删除此控制器"); |
||||
|
|
||||
|
return success("获取成功", guide); |
||||
|
} |
||||
|
} |
@ -0,0 +1,107 @@ |
|||||
|
-- 修复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; |
Loading…
Reference in new issue