diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsController.java index 4074e63..a799dac 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsController.java @@ -129,35 +129,7 @@ public class ShopGoodsController extends BaseController { @Operation(summary = "统计信息") @GetMapping("/data") public ApiResult> data(ShopGoodsParam param) { - Map data = new HashMap<>(); - final LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - - if (param.getMerchantId() != null) { - wrapper.eq(ShopGoods::getMerchantId,param.getMerchantId()); - } - - long totalNum = shopGoodsService.count( - wrapper.eq(ShopGoods::getStatus,0).gt(ShopGoods::getStock,0) - ); - data.put("totalNum", Math.toIntExact(totalNum)); - wrapper.clear(); - - long totalNum2 = shopGoodsService.count( - wrapper.gt(ShopGoods::getStatus,0) - ); - data.put("totalNum2", Math.toIntExact(totalNum2)); - wrapper.clear(); - - long totalNum3 = shopGoodsService.count( - wrapper.eq(ShopGoods::getStock,0) - ); - data.put("totalNum3", Math.toIntExact(totalNum3)); - wrapper.clear(); - - // 下架已售罄的商品 - shopGoodsService.update(new LambdaUpdateWrapper().eq(ShopGoods::getStock,0).set(ShopGoods::getStatus,1)); - - return success(data); + return success(shopGoodsService.getCountSummary(param)); } } diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsMapper.java index 20ac974..65176bb 100644 --- a/src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsMapper.java +++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsMapper.java @@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; import java.util.List; +import java.util.Map; /** * 商品Mapper @@ -48,4 +49,23 @@ public interface ShopGoodsMapper extends BaseMapper { @Update("UPDATE shop_goods SET sales = IFNULL(sales, 0) + #{saleCount} WHERE goods_id = #{goodsId}") int addSaleCount(@Param("goodsId") Integer goodsId, @Param("saleCount") Integer saleCount); + /** + * 商品数量统计(出售中=status=0且stock>0 / 待上架=status>0 / 已售罄=stock=0) + * 一条 SQL 聚合,替代 3 次 count 查询 + * + * @param param 查询参数 + * @return 包含 totalNum/totalNum2/totalNum3 的统计 Map + */ + Map selectCountSummary(@Param("param") ShopGoodsParam param); + + /** + * 下架已售罄(库存为0)的商品,忽略租户隔离(全局下架) + * 由定时任务调用,从统计读接口剥离出的写操作 + * + * @return 影响的行数 + */ + @InterceptorIgnore(tenantLine = "true") + @Update("UPDATE shop_goods SET status = 1 WHERE stock = 0 AND status != 1") + int downSoldOutGoods(); + } diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml index a14c354..5c0ae7c 100644 --- a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml +++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml @@ -148,4 +148,18 @@ + + + diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopGoodsService.java b/src/main/java/com/gxwebsoft/shop/service/ShopGoodsService.java index 115cf42..c360ee3 100644 --- a/src/main/java/com/gxwebsoft/shop/service/ShopGoodsService.java +++ b/src/main/java/com/gxwebsoft/shop/service/ShopGoodsService.java @@ -6,6 +6,7 @@ import com.gxwebsoft.shop.entity.ShopGoods; import com.gxwebsoft.shop.param.ShopGoodsParam; import java.util.List; +import java.util.Map; /** * 商品Service @@ -49,4 +50,19 @@ public interface ShopGoodsService extends IService { */ boolean addSaleCount(Integer goodsId, Integer saleCount); + /** + * 商品数量统计(出售中/待上架/已售罄),一条 SQL 聚合替代 3 次 count + * + * @param param 查询参数 + * @return 包含 totalNum/totalNum2/totalNum3 的统计 Map + */ + Map getCountSummary(ShopGoodsParam param); + + /** + * 下架已售罄(库存为0)的商品,供定时任务调用 + * + * @return 影响的行数 + */ + int downSoldOutGoods(); + } diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopGoodsServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopGoodsServiceImpl.java index 4984d72..02aa2c3 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopGoodsServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopGoodsServiceImpl.java @@ -12,6 +12,8 @@ import org.springframework.stereotype.Service; import lombok.extern.slf4j.Slf4j; import java.util.List; +import java.util.HashMap; +import java.util.Map; /** * 商品Service实现 @@ -72,4 +74,26 @@ public class ShopGoodsServiceImpl extends ServiceImpl getCountSummary(ShopGoodsParam param) { + Map summary = baseMapper.selectCountSummary(param); + Map data = new HashMap<>(3); + data.put("totalNum", toInt(summary.get("totalNum"))); + data.put("totalNum2", toInt(summary.get("totalNum2"))); + data.put("totalNum3", toInt(summary.get("totalNum3"))); + return data; + } + + @Override + public int downSoldOutGoods() { + return baseMapper.downSoldOutGoods(); + } + + /** + * 将聚合统计结果安全转为 int(数据库 NULL 或缺失视为 0) + */ + private int toInt(Object value) { + return value == null ? 0 : ((Number) value).intValue(); + } + } diff --git a/src/main/java/com/gxwebsoft/shop/task/GoodsSoldOutTask.java b/src/main/java/com/gxwebsoft/shop/task/GoodsSoldOutTask.java new file mode 100644 index 0000000..2ce6967 --- /dev/null +++ b/src/main/java/com/gxwebsoft/shop/task/GoodsSoldOutTask.java @@ -0,0 +1,43 @@ +package com.gxwebsoft.shop.task; + +import com.gxwebsoft.shop.service.ShopGoodsService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * 已售罄商品自动下架定时任务 + * + *

从 {@code ShopGoodsController.data()} 统计读接口中剥离出的写操作: + * 原实现每次打开商品列表都会执行 {@code UPDATE ... SET status=1 WHERE stock=0}, + * 属于在查询接口里写库的反模式,且翻页/排序若合并进 page 会产生高频写。 + * 此处改为独立定时任务,避免读链路与写操作耦合。

+ * + *

默认每 5 分钟执行一次,可通过配置 {@code shop.goods.sold-out.cron} 调整。

+ * + * @author WebSoft + * @since 2026-08-05 + */ +@Slf4j +@Component +public class GoodsSoldOutTask { + + @Autowired + private ShopGoodsService shopGoodsService; + + /** + * 定时下架已售罄(库存为 0)的商品,忽略租户隔离(全局下架所有商户) + */ + @Scheduled(cron = "${shop.goods.sold-out.cron:0 */5 * * * ?}") + public void downSoldOutGoods() { + try { + int count = shopGoodsService.downSoldOutGoods(); + if (count > 0) { + log.info("定时下架已售罄商品完成,下架数量: {}", count); + } + } catch (Exception e) { + log.error("定时下架已售罄商品任务执行失败", e); + } + } +}