From d9d95da453006ada9a93e1052a8529f6fb251679 Mon Sep 17 00:00:00 2001 From: wangw <1594593906@qq.com> Date: Wed, 6 May 2026 11:10:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=95=86=E5=93=81=20=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/ProductController.java | 15 +++++-- .../java/com/czg/service/RedisService.java | 31 +++++++++++++ .../czg/product/param/ProductBatchParam.java | 44 +++++++++++++++++++ .../czg/product/service/ProductService.java | 5 ++- .../service/impl/ProductServiceImpl.java | 39 ++++++++++++++++ 5 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 cash-common/cash-common-service/src/main/java/com/czg/product/param/ProductBatchParam.java diff --git a/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java b/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java index d87cce137..fd4a895b2 100644 --- a/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java +++ b/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java @@ -63,7 +63,6 @@ public class ProductController { * 商品-分页 */ @GetMapping("page") - @OperationLog("商品-分页") //@SaAdminCheckPermission("product:page") public CzgResult> getProductPage(ProductDTO param) { Page data = productService.getProductPage(param); @@ -83,7 +82,6 @@ public class ProductController { * 商品-列表 */ @GetMapping("list") - @OperationLog("商品-列表") public CzgResult> getProductList(@RequestParam(required = false) Long categoryId) { return CzgResult.success(productService.getProductCacheList(StpKit.USER.getShopId(), categoryId)); } @@ -92,7 +90,6 @@ public class ProductController { * 商品-详情 */ @GetMapping("{id}") - @OperationLog("商品-详情") //@SaAdminCheckPermission("product:info") public CzgResult getProductById(@PathVariable("id") Long id) { AssertUtil.isNull(id, "{}不能为空", "id"); @@ -242,7 +239,6 @@ public class ProductController { * 商品-统计 */ @GetMapping("statistics") - @OperationLog("商品-统计") //@SaAdminCheckPermission("product:statistics") public CzgResult statistics(ProductInfoParam param) { Long shopId = StpKit.USER.getShopId(0L); @@ -251,6 +247,17 @@ public class ProductController { return CzgResult.success(data); } + /** + * 商品-批量操作 + */ + @GetMapping("batchOperate") + public CzgResult batchOperate(ProductBatchParam param) { + Long shopId = StpKit.USER.getShopId(); + param.setShopId(shopId); + productService.batchOperate(param); + return CzgResult.success(); + } + /** * 商品-报损 */ diff --git a/cash-common/cash-common-redis/src/main/java/com/czg/service/RedisService.java b/cash-common/cash-common-redis/src/main/java/com/czg/service/RedisService.java index c5d078a80..a7cf4acda 100644 --- a/cash-common/cash-common-redis/src/main/java/com/czg/service/RedisService.java +++ b/cash-common/cash-common-redis/src/main/java/com/czg/service/RedisService.java @@ -5,7 +5,10 @@ import com.alibaba.fastjson2.JSONWriter; import jakarta.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -52,6 +55,34 @@ public class RedisService { return redisTemplate.delete(keys); } + /** + * 删除某前缀Key + * @param prefix + */ + public void deleteKeysByPrefixSafe(String prefix) { + // 拼接匹配规则:前缀 + * → 比如传入 user → 匹配 user* + String pattern = prefix + "*"; + + // redisTemplate.execute:使用 Redis 底层连接执行原生命令(性能更高) + redisTemplate.execute((RedisCallback) connection -> { + // try-with-resources:自动关闭游标,避免资源泄漏 + try (Cursor cursor = connection.scan( + // SCAN 配置:匹配规则、每次扫描1000条 + ScanOptions.scanOptions().match(pattern).count(1000).build() + )) { + // 迭代遍历扫描到的所有 key + while (cursor.hasNext()) { + // 删除当前遍历到的 key + connection.del(cursor.next()); + } + } catch (Exception e) { + // 异常捕获打印,避免删除失败导致程序报错 + e.printStackTrace(); + } + return null; + }); + } + /** * 指定缓存失效时间 * diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/param/ProductBatchParam.java b/cash-common/cash-common-service/src/main/java/com/czg/product/param/ProductBatchParam.java new file mode 100644 index 000000000..89260e7c0 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/param/ProductBatchParam.java @@ -0,0 +1,44 @@ +package com.czg.product.param; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 商品批量操作 + * + * @author ww + */ +@Data +public class ProductBatchParam implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 商品id集合 + */ + @NotNull(message = "商品id集合不能为空") + private List ids; + /** + * 商品操作 + * category 修改分类 + * isSale 上下架 + * is_sold_stock 售罄 + * isAutoSoldStock 自动售罄 + */ + @NotBlank(message = "操作类型不能为空") + private String type; + /** + * 操作值 + */ + @NotBlank(message = "操作值不能为空") + private String value; + /** + * 店铺id + */ + private Long shopId; +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ProductService.java b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ProductService.java index 8aeb99ee5..1a9dd70c6 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ProductService.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ProductService.java @@ -110,7 +110,10 @@ public interface ProductService extends IService { * @param param 商品报损入参 */ void reportDamage(ProductReportDamageParam param); - + /** + * 批量操作商品 + */ + void batchOperate(ProductBatchParam param); /** * 商品统计 * diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ProductServiceImpl.java b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ProductServiceImpl.java index aad24b992..d65133ca4 100644 --- a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ProductServiceImpl.java +++ b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ProductServiceImpl.java @@ -401,6 +401,19 @@ public class ProductServiceImpl extends ServiceImpl impl redisService.del(CacheConstant.USER_CLIENT_HOTS_PRODUCT + "::" + shopId); } + /** + * 清除某个商品分类的缓存 + */ + private void clearProductAllCache(Long shopId) { + //分类缓存 + redisService.deleteKeysByPrefixSafe(ADMIN_CLIENT_PRODUCT_LIST + "::" + shopId); + //用户端分组缓存 + redisService.del(CacheConstant.USER_CLIENT_GROUPS_PRODUCT + "::" + shopId); + //用户端热销缓存 + redisService.del(CacheConstant.USER_CLIENT_HOTS_PRODUCT + "::" + shopId); + } + + /** * 计算是否在可售时间内 * @@ -778,6 +791,32 @@ public class ProductServiceImpl extends ServiceImpl impl consStockByProduct(shopId, InOutTypeEnum.OUT, InOutItemEnum.DAMAGE_OUT, productStockList, null, "【商品报损,自动报损相关耗材】"); } + @Override + public void batchOperate(ProductBatchParam param) { + Product product = new Product(); + //商品操作 + //category 修改分类 + //isSale 上下架 + //is_sold_stock 售罄 + //isAutoSoldStock 自动售罄 + switch (param.getType()) { + case "category": + product.setCategoryId(Long.valueOf(param.getValue())); + break; + case "isSale": + product.setIsSale(Integer.valueOf(param.getValue())); + break; + case "is_sold_stock": + product.setIsSoldStock(Integer.valueOf(param.getValue())); + break; + case "isAutoSoldStock": + product.setIsAutoSoldStock(Integer.valueOf(param.getValue())); + break; + } + update(product, query().eq(Product::getShopId, param.getShopId()).in(Product::getId, param.getIds())); + clearProductAllCache(param.getShopId()); + } + @Override public ProductStatisticsVo getProductStatistics(ProductInfoParam param) { ProductStatisticsVo data = productStockFlowMapper.getProductStatistics(param);