From e3e066ee8218d11ed8f6909ab0cf1f7af2c64fd5 Mon Sep 17 00:00:00 2001 From: wangfukang <15630117759@163.com> Date: Thu, 30 Apr 2026 15:18:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=8B=BC=E5=9B=A2=E6=95=B0?= =?UTF-8?q?=E6=8D=AE1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 7 +- .../mybatis/MallDeliveryOrderServiceImpl.java | 5 +- .../mybatis/MallOrderServiceImpl.java | 13 +- .../mybatis/MallRefundRecordServiceImpl.java | 10 +- .../mall/utils/ShopIndexCountCacheUtil.java | 148 ++++++++++++++++++ .../hiver/mall/utils/ShopStatsCountDTO.java | 22 +++ .../main/resources/mapper/MallOrderMapper.xml | 10 +- 7 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/ShopIndexCountCacheUtil.java create mode 100644 hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/ShopStatsCountDTO.java diff --git a/hiver-admin/src/main/resources/application.yml b/hiver-admin/src/main/resources/application.yml index 9b432fcc..7d87d5de 100644 --- a/hiver-admin/src/main/resources/application.yml +++ b/hiver-admin/src/main/resources/application.yml @@ -83,7 +83,7 @@ spring: max-active: 20 # 最大连接数,建议从默认的 8 调大到 20 或更高 max-wait: 2000ms # 获取连接的最大等待时间,避免无限期阻塞 max-idle: 10 # 最大空闲连接数 - min-idle: 1 # 最小空闲连接数 + min-idle: 4 # 最小空闲连接数 # Elasticsearch data: elasticsearch: @@ -143,6 +143,11 @@ spring: fail-on-empty-beans: false boot: admin: + monitor: + # 将默认超时时间从 10000ms 调整为 30000ms (30秒) + default-timeout: 30000 + # 检查状态的间隔,默认是 10000ms + status-interval: 15000 # 修改上下文路径 context-path: /hiver/admin client: diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java index 169c157b..4b3f8263 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java @@ -102,6 +102,9 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl oUw = new LambdaUpdateWrapper<>(); oUw.eq(MallOrder::getId, orderId) diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java index 09b9152c..f378de74 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java @@ -93,6 +93,9 @@ public class MallOrderServiceImpl extends ServiceImpl + * 底层使用 Redis Hash 结构,保证按 shopId + countType 维度的 O(1) 读写效率。 + *
+ *   Key   = SHOP_INDEX_COUNT:{shopId}
+ *   Field = countType
+ *   Value = Map 的 JSON 序列化
+ * 
+ *

+ * + * @author system + */ +@Slf4j +@Component +public class ShopIndexCountCacheUtil { + + /** Redis Key 前缀 */ + private static final String KEY_PREFIX = "SHOP:STATS:COUNT:"; + + @Autowired + private RedisTemplateHelper redisTemplateHelper; + + // ================================================================ + // Key 构建 + // ================================================================ + private String buildKey(String shopId) { + return KEY_PREFIX + shopId; + } + + // ================================================================ + // 核心:更新操作 (增/改) + // ================================================================ + + /** + * 更新单个字段的数值(支持原子增减) + * 场景:订单状态变更时,只修改特定计数(如:待出餐 +1) + * + * @param shopId 店铺ID + * @param field 字段名(如 "pendingCooking") + * @param delta 变化量(正数表示增加,负数表示减少) + */ + public void incrementField(String shopId, String field, long delta) { + if (StringUtils.isBlank(shopId) || StringUtils.isBlank(field)) { + return; + } + try { + String key = buildKey(shopId); + // Redis hIncrBy 自动处理不存在的情况(默认为0) + redisTemplateHelper.hIncrBy(key, field, delta); + log.debug("更新计数缓存: shopId={}, field={}, delta={}", shopId, field, delta); + } catch (Exception e) { + log.error("更新计数缓存失败: shopId={}, field={}", shopId, field, e); + } + } + + /** + * 覆盖式更新(全量替换) + * 场景:后台管理手动修正数据,或初始化数据 + */ + public void put(String shopId, ShopStatsCountDTO statsDTO) { + if (StringUtils.isBlank(shopId) || statsDTO == null) { + return; + } + try { + String key = buildKey(shopId); + String json = JSONUtil.toJsonStr(statsDTO); + redisTemplateHelper.hPut(key, statsDTO.getShopId(), json); + log.info("全量覆盖计数缓存: shopId={}", shopId); + } catch (Exception e) { + log.error("全量覆盖计数缓存失败: shopId={}", shopId, e); + } + } + + // ================================================================ + // 查询操作 (查) + // ================================================================ + + /** + * 获取所有统计数据 + */ + public ShopStatsCountDTO getAll(String shopId) { + if (StringUtils.isBlank(shopId)) { + return null; + } + try { + String key = buildKey(shopId); + Map entries = redisTemplateHelper.hGetAll(key); + if (entries == null || entries.isEmpty()) { + return null; + } + // 将 Map 转回 DTO 对象 + String json = JSONUtil.toJsonStr(entries); + return JSONUtil.toBean(json, ShopStatsCountDTO.class); + } catch (Exception e) { + log.error("获取计数缓存失败: shopId={}", shopId, e); + return null; + } + } + + /** + * 获取单个字段的数值 + * 场景:首页只展示“待出餐”数量,无需查出整个对象 + */ + public Integer getCount(String shopId, String field) { + if (StringUtils.isBlank(shopId) || StringUtils.isBlank(field)) { + return 0; + } + try { + Object val = redisTemplateHelper.hGet(buildKey(shopId), field); + return val == null ? 0 : Integer.parseInt(val.toString()); + } catch (Exception e) { + log.error("获取单个计数失败: shopId={}, field={}", shopId, field, e); + return 0; + } + } + + // ================================================================ + // 删除操作 (删) + // ================================================================ + + /** + * 删除指定店铺的所有统计数据缓存 + */ + public void remove(String shopId) { + if (StringUtils.isBlank(shopId)) { + return; + } + try { + redisTemplateHelper.delete(buildKey(shopId)); + log.info("清除店铺计数缓存: shopId={}", shopId); + } catch (Exception e) { + log.error("清除店铺计数缓存失败: shopId={}", shopId, e); + } + } + +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/ShopStatsCountDTO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/ShopStatsCountDTO.java new file mode 100644 index 00000000..d4c4aa30 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/ShopStatsCountDTO.java @@ -0,0 +1,22 @@ +package cc.hiver.mall.utils; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ShopStatsCountDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private String shopId; // 冗余存储,方便排查 + private Integer pendingConsumption = 0; // 待消费 + private Integer pendingCooking = 0; // 待出餐 + private Integer pendingPickup = 0; // 待取货 + private Integer pendingDelivery = 0; // 待送达 + + public ShopStatsCountDTO() {} + + public ShopStatsCountDTO(String shopId) { + this.shopId = shopId; + } +} \ No newline at end of file diff --git a/hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml b/hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml index e43e99a8..6ba0664b 100644 --- a/hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml +++ b/hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml @@ -43,10 +43,14 @@ SELECT COUNT(*) AS count, CASE - WHEN (`status` = 3 AND delivery_type = 1) THEN 'daiqu' - WHEN (`status` = 4 AND delivery_type = 1) THEN 'daisong' + WHEN (`status` = 3 AND delivery_type = 1 AND shop_make_time IS NOT NULL) THEN 'daiqu' + WHEN (`status` = 3 AND delivery_type = 2 AND user_require_make = 1) + OR + (`status` = 3 AND delivery_type = 1 AND shop_make_time IS NULL) + THEN 'daichucan' WHEN (`status` = 3 AND delivery_type = 2 AND user_require_make IS NULL) THEN 'daixiaofei' - WHEN (`status` = 3 AND delivery_type = 2 AND user_require_make = 1) THEN 'daichucan' + WHEN (`status` = 4 AND delivery_type = 1) THEN 'daisong' + ELSE 'other' END AS counttype FROM mall_order