Compare commits

...

2 Commits

Author SHA1 Message Date
huiqiao d54b1fedd7 Merge remote-tracking branch 'origin/master' 1 month ago
huiqiao cc3d6707d5 feat(mall): 添加店铺缓存机制和商品变动时的缓存刷新 1 month ago
  1. 97
      README2.md
  2. 34
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ProductController.java
  3. 85
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java
  4. 31
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/ShopCacheDTO.java
  5. 16
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/ShopService.java
  6. 51
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java
  7. 18
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopTakeawayServiceImpl.java

97
README2.md

@ -0,0 +1,97 @@
# Reddoor Micro-ERP System
## 项目简介 (Project Overview)
本项目是一个基于微服务架构思想的单体多模块后端企业级应用程序(Micro-ERP),涵盖了**完善的供应链(商城)、物流管理及企业级业务处理**。该系统提供了从商品管理、采购、销售、库存到财务结算的完整闭环,同时集成了复杂的物流运输路由计算、场站管理以支撑线下业务。
该项目适合作为新零售、电商ERP、同城/干线物流等混合业务场景的后端基石。
---
## 技术栈 (Technology Stack)
### 核心框架
* **后端应用框架**: Spring Boot 2.5.14
* **安全认证**: JWT (jjwt) + 自定义/Shiro方案
* **持久层框架**: MyBatis-Plus 3.5.1 (含代码生成器)
* **API文档**: Knife4j / Swagger 2
### 数据与缓存
* **关系型数据库**: MySQL 5.7+ (连接池: Druid)
* **缓存与分布式锁**: Redis (Redisson 3.17.2)
### 云服务与第三方集成
* **对象存储(OSS)**: 支持 MinIO, 阿里云 OSS, 腾讯云 COS, 七牛云
* **消息通知**: 腾讯云短信服务 (Tencent Cloud SMS)
* **支付集成**: 支付宝 (Alipay SDK), Payment Spring Boot Starter
* **微信生态**: WxJava (支持小程序与微信公众号)
### 工具与中间件
* **工具箱**: Hutool, Guava (Gson), MapStruct, EasyPoi (Excel导入导出)
* **流程引擎**: Smart Flow (流程管理框架)
* **授权与安全**: TrueLicense (License许可验证), Bcprov (加密算法)
* **网络通信**: Smart Socket (AIO)
### 测试环境
* **单元/集成测试**: TestNG
* **UI/自动化测试**: Selenium
* **测试报告**: ExtentReports TestNG Adapter
---
## 项目结构 (Project Structure)
该工程采用 Maven 多模块 (Multi-Module) 结构,明确化了业务边界:
```text
school (Root)
├── hiver-admin # 后端管理系统的入口模块 (启动应用)
├── hiver-core # 核心基础层:包含基础配置、物流实体与基础Dao (logistics, shopprint)
├── hiver-modules # 业务模块层结构封装:
│ ├── hiver-app # 移动端/APP API (C端与普通工人端)
│ ├── hiver-base # 基础服务 (系统字典、通用业务或基础RBAC)
│ ├── hiver-file # 文件上传与对象存储统一管控
│ ├── hiver-mall # 供应链商城与ERP业务 (核心业务模块)
│ ├── hiver-open # 开放平台/第三方对接API
│ ├── hiver-quartz # 分布式定时任务调度模块
│ └── hiver-social # 社交与圈子功能等模块
└── pom.xml # 全局依赖管理
```
---
## 核心业务模块分析 (Business Models Analysis)
### 1. 供应链与商城管理 (hiver-mall)
* **商品流**: 包括商品(Goods)、产品(Product)、分类(Category)、属性(Attribute)和品牌(Brand)的管理。
* **交易流**: 提供针对客户(Customer)、供应商(Supplier)的管理,涵盖采购单(Purchase)、销售单(Sale)、订单管理(Order)、购物车(Cart)、退货退款(ReturnSale/ReturnCommission)。
* **库存与财务**: 支持仓储管理(Stock/GoodsStock)、资金记录(DealingsRecord)、充值(Recharge)。
* **店铺与终端**: 提供门店/网点(Shop, ShopArea, ShopTakeaway)与终端员工/工人的管理(Worker, WorkerAuth, UserClockIn)。
### 2. 物流与运输业务 (hiver-core logistics*)
深入集成了物流执行侧的实体:
* **路由与节点**: 路由(logisticsroute)、公司路由(logisticscompanyroute)、站点管理(logisticsstation)、中转站(logisticstransferstation)。
* **业务操作**: 物流订单(logisticsorder)、装车日志(logisticsentruckinglog)、落车费规则计算(logisticslandingfeerules)。
### 3. 多端支持与扩展 (hiver-app / hiver-open)
* 提供给移动端(APP、小程序)的标准RESTful接口,支持微信生态。
* 提供标准的Open API以便与外部ERP/WMS进行数据对接。
---
## 快速开始 (Getting Started)
### 1. 环境准备
* JDK 1.8
* Maven 3.6+
* MySQL 5.7+
* Redis 6.x+ (安装并正常运行)
### 2. 初始化项目
1. **克隆代码**: 将本工程导入到 IDEA 中。
2. **执行构建**: 在根目录下执行 `mvn clean install -Dmaven.test.skip=true` 解决所有依赖。
3. **导入数据库**: 寻找相关的 `.sql` 脚本导入到 MySQL 数据库中(如有)。自行配置好 `application-dev.yml` 中的 Redis 和 MySQL 信息。
4. **启动服务**: 找到 `hiver-admin` 模块中的 `HiverApplication.java` 启动类,直接启动即可运行后台 API 服务。
### 3. 注意事项
* 如果提示 `lombok` 缺失,请在 IDE 中安装 Lombok 插件并开启 `Enable Annotation Processing`
* 支付配置与OSS/短信配置:若是进行本地开发调试,请在配置或者数据库字典中替换成对应的测试 KEY 和 Secret。

34
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ProductController.java

@ -18,6 +18,8 @@ import cc.hiver.mall.productpicture.service.ProductPictureService;
import cc.hiver.mall.service.mybatis.ProductCategoryService; import cc.hiver.mall.service.mybatis.ProductCategoryService;
import cc.hiver.mall.service.mybatis.ProductGroupBuyPriceService; import cc.hiver.mall.service.mybatis.ProductGroupBuyPriceService;
import cc.hiver.mall.service.mybatis.ProductService; import cc.hiver.mall.service.mybatis.ProductService;
import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.entity.Shop;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@ -52,6 +54,28 @@ public class ProductController {
@Autowired @Autowired
private ProductGroupBuyPriceService productGroupBuyPriceService; private ProductGroupBuyPriceService productGroupBuyPriceService;
@Autowired
private ShopService shopService;
private void triggerCacheRefreshByProductId(String productId) {
Product product = productService.getById(productId);
if (product != null && StringUtils.isNotEmpty(product.getShopId())) {
Shop shop = shopService.get(product.getShopId());
if (shop != null) {
shopService.refreshShopCache(shop.getId(), shop.getRegionId());
}
}
}
private void triggerCacheRefreshByProductIds(String ids) {
if (StringUtils.isNotEmpty(ids)) {
String[] idArray = ids.split(",");
if (idArray.length > 0) {
triggerCacheRefreshByProductId(idArray[0]);
}
}
}
@RequestMapping(value = "/save", method = RequestMethod.POST) @RequestMapping(value = "/save", method = RequestMethod.POST)
@ApiOperation("新增货品") @ApiOperation("新增货品")
public Result save(@RequestBody ProductVo productVo) { public Result save(@RequestBody ProductVo productVo) {
@ -105,6 +129,7 @@ public class ProductController {
productGroupBuyPriceService.saveBatch(productGroupBuyPrices); productGroupBuyPriceService.saveBatch(productGroupBuyPrices);
} }
if (result) { if (result) {
triggerCacheRefreshByProductId(product.getId());
return ResultUtil.success("添加成功"); return ResultUtil.success("添加成功");
} else { } else {
return ResultUtil.error("添加失败"); return ResultUtil.error("添加失败");
@ -167,6 +192,7 @@ public class ProductController {
productGroupBuyPriceService.saveBatch(productGroupBuyPrices); productGroupBuyPriceService.saveBatch(productGroupBuyPrices);
} }
if (result) { if (result) {
triggerCacheRefreshByProductId(productVo.getId());
return ResultUtil.success("修改成功"); return ResultUtil.success("修改成功");
} else { } else {
return ResultUtil.error("修改失败"); return ResultUtil.error("修改失败");
@ -181,6 +207,7 @@ public class ProductController {
updateWrapper.eq("id", id); updateWrapper.eq("id", id);
final boolean result = productService.update(updateWrapper); final boolean result = productService.update(updateWrapper);
if (result) { if (result) {
triggerCacheRefreshByProductId(id);
return ResultUtil.success("上架成功"); return ResultUtil.success("上架成功");
} else { } else {
return ResultUtil.error("上架失败"); return ResultUtil.error("上架失败");
@ -203,6 +230,7 @@ public class ProductController {
} }
final boolean result = productService.batchUp(ids); final boolean result = productService.batchUp(ids);
if (result) { if (result) {
triggerCacheRefreshByProductIds(ids);
return ResultUtil.success("上架成功"); return ResultUtil.success("上架成功");
} else { } else {
return ResultUtil.error("上架失败"); return ResultUtil.error("上架失败");
@ -234,6 +262,7 @@ public class ProductController {
updateWrapper.eq("id", id); updateWrapper.eq("id", id);
final boolean result = productService.update(updateWrapper); final boolean result = productService.update(updateWrapper);
if (result) { if (result) {
triggerCacheRefreshByProductId(id);
return ResultUtil.success("下架成功"); return ResultUtil.success("下架成功");
} else { } else {
return ResultUtil.error("下架失败"); return ResultUtil.error("下架失败");
@ -248,6 +277,7 @@ public class ProductController {
updateWrapper.eq("id", id); updateWrapper.eq("id", id);
final boolean result = productService.update(updateWrapper); final boolean result = productService.update(updateWrapper);
if (result) { if (result) {
triggerCacheRefreshByProductId(id);
return ResultUtil.success("设置成功"); return ResultUtil.success("设置成功");
} else { } else {
return ResultUtil.error("设置失败"); return ResultUtil.error("设置失败");
@ -262,6 +292,7 @@ public class ProductController {
updateWrapper.eq("id", id); updateWrapper.eq("id", id);
final boolean result = productService.update(updateWrapper); final boolean result = productService.update(updateWrapper);
if (result) { if (result) {
triggerCacheRefreshByProductId(id);
return ResultUtil.success("设置成功"); return ResultUtil.success("设置成功");
} else { } else {
return ResultUtil.error("设置失败"); return ResultUtil.error("设置失败");
@ -284,6 +315,7 @@ public class ProductController {
} }
final boolean result = productService.batchDown(ids); final boolean result = productService.batchDown(ids);
if (result) { if (result) {
triggerCacheRefreshByProductIds(ids);
return ResultUtil.success("下架成功"); return ResultUtil.success("下架成功");
} else { } else {
return ResultUtil.error("下架失败"); return ResultUtil.error("下架失败");
@ -298,6 +330,7 @@ public class ProductController {
updateWrapper.eq("id", id); updateWrapper.eq("id", id);
final boolean result = productService.update(updateWrapper); final boolean result = productService.update(updateWrapper);
if (result) { if (result) {
triggerCacheRefreshByProductId(id);
return ResultUtil.success("删除成功"); return ResultUtil.success("删除成功");
} else { } else {
return ResultUtil.error("删除失败"); return ResultUtil.error("删除失败");
@ -319,6 +352,7 @@ public class ProductController {
} }
final boolean result = productService.batchDeleteProduct(ids); final boolean result = productService.batchDeleteProduct(ids);
if (result) { if (result) {
triggerCacheRefreshByProductIds(ids);
return ResultUtil.success("删除成功"); return ResultUtil.success("删除成功");
} else { } else {
return ResultUtil.error("删除失败"); return ResultUtil.error("删除失败");

85
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java

@ -28,7 +28,6 @@ import cc.hiver.core.entity.User;
import cc.hiver.core.service.UserService; import cc.hiver.core.service.UserService;
import cc.hiver.mall.entity.Shop; import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.entity.ShopArea; import cc.hiver.mall.entity.ShopArea;
import cc.hiver.mall.entity.ShopTakeaway;
import cc.hiver.mall.entity.ShopUser; import cc.hiver.mall.entity.ShopUser;
import cc.hiver.mall.invitelog.service.InviteLogService; import cc.hiver.mall.invitelog.service.InviteLogService;
import cc.hiver.mall.pojo.dto.ShopRevenue; import cc.hiver.mall.pojo.dto.ShopRevenue;
@ -37,7 +36,6 @@ import cc.hiver.mall.pojo.vo.ProductPageVO;
import cc.hiver.mall.pojo.vo.QueryShopRevenueVO; import cc.hiver.mall.pojo.vo.QueryShopRevenueVO;
import cc.hiver.mall.service.ShopAreaService; import cc.hiver.mall.service.ShopAreaService;
import cc.hiver.mall.service.ShopService; import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.service.ShopTakeawayService;
import cc.hiver.mall.service.ShopUserService; import cc.hiver.mall.service.ShopUserService;
import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@ -45,6 +43,10 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import cn.hutool.json.JSONUtil;
import cc.hiver.core.common.redis.RedisTemplateHelper;
import cc.hiver.mall.pojo.dto.ShopCacheDTO;
import org.springframework.data.domain.PageImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -83,7 +85,7 @@ public class ShopController {
private InviteLogService inviteLogService; private InviteLogService inviteLogService;
@Autowired @Autowired
private ShopTakeawayService shopTakeawayService; private RedisTemplateHelper redisTemplateHelper;
@RequestMapping(value = "/getAll", method = RequestMethod.GET) @RequestMapping(value = "/getAll", method = RequestMethod.GET)
@ApiOperation("获取全部数据") @ApiOperation("获取全部数据")
@ -183,6 +185,7 @@ public class ShopController {
if (shop.getYearFee() != null && shop.getYearFee().compareTo(BigDecimal.ZERO) > 0) { if (shop.getYearFee() != null && shop.getYearFee().compareTo(BigDecimal.ZERO) > 0) {
inviteLogService.updateInviteLogIsOpen(shop.getId()); inviteLogService.updateInviteLogIsOpen(shop.getId());
} }
shopService.refreshShopCache(shop.getId(), shop.getRegionId());
return ResultUtil.success("编辑成功"); return ResultUtil.success("编辑成功");
} }
@ -193,6 +196,7 @@ public class ShopController {
final Shop shop = shopService.findById(id); final Shop shop = shopService.findById(id);
shop.setShopName(shopName); shop.setShopName(shopName);
shopService.update(shop); shopService.update(shop);
shopService.refreshShopCache(shop.getId(), shop.getRegionId());
return ResultUtil.success("编辑成功"); return ResultUtil.success("编辑成功");
} }
@ -205,6 +209,9 @@ public class ShopController {
shopService.delete(id); shopService.delete(id);
// 删除店铺用户 // 删除店铺用户
shopUserService.deleteAllByShopId(id); shopUserService.deleteAllByShopId(id);
if (shop != null && shop.getRegionId() != null) {
shopService.removeShopCache(shop.getId(), shop.getRegionId());
}
} }
return ResultUtil.success("删除成功"); return ResultUtil.success("删除成功");
@ -214,14 +221,68 @@ public class ShopController {
@ApiOperation("多条件分页获取公司列表") @ApiOperation("多条件分页获取公司列表")
public Result<Page<Shop>> getByCondition(Shop shop, public Result<Page<Shop>> getByCondition(Shop shop,
PageVo pageVo) { PageVo pageVo) {
String regionId = shop.getRegionId();
if (CharSequenceUtil.isNotBlank(regionId)) {
List<Object> values = redisTemplateHelper.hValues("SHOP_CACHE:" + regionId);
if (values != null && !values.isEmpty()) {
List<Shop> allShops = new ArrayList<>();
for (Object v : values) {
ShopCacheDTO dto = JSONUtil.toBean(v.toString(), ShopCacheDTO.class);
Shop s = dto.getShop();
s.setProducts(dto.getProducts());
if (CharSequenceUtil.isNotBlank(shop.getShopArea())) {
if (!shop.getShopArea().equals(s.getShopArea())) {
continue;
}
}
allShops.add(s);
}
String sort = pageVo.getSort();
String order = pageVo.getOrder();
if (CharSequenceUtil.isNotBlank(sort)) {
allShops.sort((s1, s2) -> {
int compareResult = 0;
if ("saleCount".equals(sort)) {
Integer count1 = s1.getSaleCount();
Integer count2 = s2.getSaleCount();
Integer c1 = count1 == null ? 0 : count1;
Integer c2 = count2 == null ? 0 : count2;
compareResult = c1.compareTo(c2);
} else if ("shopScore".equals(sort)) {
BigDecimal score1 = s1.getShopScore() == null ? BigDecimal.ZERO : s1.getShopScore();
BigDecimal score2 = s2.getShopScore() == null ? BigDecimal.ZERO : s2.getShopScore();
compareResult = score1.compareTo(score2);
}
if ("desc".equalsIgnoreCase(order)) {
return -compareResult;
}
return compareResult;
});
}
int pageNumber = pageVo.getPageNumber() == null ? 1 : pageVo.getPageNumber();
int pageSize = pageVo.getPageSize() == null ? 10 : pageVo.getPageSize();
int fromIndex = (pageNumber - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, allShops.size());
List<Shop> pageList;
if (fromIndex >= allShops.size()) {
pageList = new ArrayList<>();
} else {
pageList = allShops.subList(fromIndex, toIndex);
}
Page<Shop> pageResult = new PageImpl<>(pageList, PageUtil.initPage(pageVo), allShops.size());
return new ResultUtil<Page<Shop>>().setData(pageResult);
}
}
final Page<Shop> page = shopService.findByCondition(shop, PageUtil.initPage(pageVo)); final Page<Shop> page = shopService.findByCondition(shop, PageUtil.initPage(pageVo));
final List<String> shopIdList = new ArrayList<>(); final List<String> shopIdList = new ArrayList<>();
page.getContent().forEach(e -> { page.getContent().forEach(e -> {
shopIdList.add(e.getId()); shopIdList.add(e.getId());
}); });
//查询业务信息
List<ShopTakeaway> shopTakeaways = shopTakeawayService.selectListByshopId(shopIdList);
//查询推荐商品 //查询推荐商品
final ProductPageQuery productPageQuery = new ProductPageQuery(); final ProductPageQuery productPageQuery = new ProductPageQuery();
productPageQuery.setShopIdList(shopIdList); productPageQuery.setShopIdList(shopIdList);
@ -230,18 +291,17 @@ public class ShopController {
page.getContent().forEach(e -> { page.getContent().forEach(e -> {
final List<ProductPageVO> products = new ArrayList<>(); final List<ProductPageVO> products = new ArrayList<>();
if(productList.getRecords().size() > 0){ if(productList.getRecords() != null && productList.getRecords().size() > 0){
productList.getRecords().forEach(productPageVO -> { productList.getRecords().forEach(productPageVO -> {
if (e.getId().equals(productPageVO.getShopId())) { if (e.getId().equals(productPageVO.getShopId())) {
products.add(productPageVO); products.add(productPageVO);
} }
}); });
} }
e.setProducts(products); e.setProducts(products);
for(ShopTakeaway shopTakeawa : shopTakeaways) { if (CharSequenceUtil.isNotBlank(regionId)) {
if (e.getId().equals(shopTakeawa.getShopId())) { shopService.refreshShopCache(e.getId(), regionId);
e.setShopTakeaway(shopTakeawa);
}
} }
}); });
return new ResultUtil<Page<Shop>>().setData(page); return new ResultUtil<Page<Shop>>().setData(page);
@ -253,6 +313,7 @@ public class ShopController {
final Shop shop = shopService.get(id); final Shop shop = shopService.get(id);
shop.setStatus(ShopConstant.SHOP_STATUS_NORMAL); shop.setStatus(ShopConstant.SHOP_STATUS_NORMAL);
shopService.update(shop); shopService.update(shop);
shopService.refreshShopCache(shop.getId(), shop.getRegionId());
return ResultUtil.success("操作成功"); return ResultUtil.success("操作成功");
} }
@ -262,7 +323,7 @@ public class ShopController {
final Shop shop = shopService.get(id); final Shop shop = shopService.get(id);
shop.setStatus(ShopConstant.SHOP_STATUS_LOCK); shop.setStatus(ShopConstant.SHOP_STATUS_LOCK);
shopService.update(shop); shopService.update(shop);
shopService.removeShopCache(shop.getId(), shop.getRegionId());
return ResultUtil.success("操作成功"); return ResultUtil.success("操作成功");
} }
@ -371,6 +432,7 @@ public class ShopController {
oldShop.setSubtitle(shop.getSubtitle()); oldShop.setSubtitle(shop.getSubtitle());
} }
shopService.update(oldShop); shopService.update(oldShop);
shopService.refreshShopCache(oldShop.getId(), oldShop.getRegionId());
return ResultUtil.success("更新成功"); return ResultUtil.success("更新成功");
} }
@ -415,6 +477,7 @@ public class ShopController {
final Shop shop = shopService.findById(shopId); final Shop shop = shopService.findById(shopId);
shop.setShopIcon(shopIcon); shop.setShopIcon(shopIcon);
shopService.update(shop); shopService.update(shop);
shopService.refreshShopCache(shop.getId(), shop.getRegionId());
return ResultUtil.data(shop); return ResultUtil.data(shop);
} }
} }

31
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/ShopCacheDTO.java

@ -0,0 +1,31 @@
package cc.hiver.mall.pojo.dto;
import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.entity.ShopTakeaway;
import cc.hiver.mall.pojo.vo.ProductPageVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 店铺Redis缓存传输对象
*
* @author cc
*/
@Data
@ApiModel(value = "店铺Redis缓存对象")
public class ShopCacheDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "店铺基础信息")
private Shop shop;
@ApiModelProperty(value = "店铺外卖配送信息")
private ShopTakeaway shopTakeaway;
@ApiModelProperty(value = "店铺推荐/可用商品列表")
private List<ProductPageVO> products;
}

16
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/ShopService.java

@ -73,4 +73,20 @@ public interface ShopService extends HiverBaseService<Shop, String> {
List<Shop> getShopSaleCounts(List<String> shopIdList); List<Shop> getShopSaleCounts(List<String> shopIdList);
IPage<ProductPageVO> getShareList(ProductPageQuery productPageQuery); IPage<ProductPageVO> getShareList(ProductPageQuery productPageQuery);
/**
* 刷新或写入店铺及其附属信息到Redis缓存
*
* @param shopId 店铺ID
* @param regionId 区域ID
*/
void refreshShopCache(String shopId, String regionId);
/**
* 从Redis缓存移除指定店铺
*
* @param shopId 店铺ID
* @param regionId 区域ID
*/
void removeShopCache(String shopId, String regionId);
} }

51
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java

@ -36,6 +36,11 @@ import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.service.mybatis.ProductGroupBuyPriceService; import cc.hiver.mall.service.mybatis.ProductGroupBuyPriceService;
import cc.hiver.mall.utils.DateUtil; import cc.hiver.mall.utils.DateUtil;
import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import cc.hiver.core.common.redis.RedisTemplateHelper;
import cc.hiver.mall.dao.mapper.ShopTakeawayMapper;
import cc.hiver.mall.entity.ShopTakeaway;
import cc.hiver.mall.pojo.dto.ShopCacheDTO;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -87,6 +92,12 @@ public class ShopServiceImpl implements ShopService {
@Autowired @Autowired
private ProductGroupBuyPriceService productGroupBuyPriceService; private ProductGroupBuyPriceService productGroupBuyPriceService;
@Autowired
private RedisTemplateHelper redisTemplateHelper;
@Autowired
private ShopTakeawayMapper shopTakeawayMapper;
@Override @Override
public HiverBaseDao<Shop, String> getRepository() { public HiverBaseDao<Shop, String> getRepository() {
return shopDao; return shopDao;
@ -226,4 +237,44 @@ public class ShopServiceImpl implements ShopService {
return page; return page;
} }
@Override
public void refreshShopCache(String shopId, String regionId) {
if (CharSequenceUtil.isBlank(regionId)) {
return;
}
Shop shop = shopDao.getById(shopId);
if (shop == null) {
removeShopCache(shopId, regionId);
return;
}
ShopCacheDTO cacheDTO = new ShopCacheDTO();
cacheDTO.setShop(shop);
ShopTakeaway takeaway = shopTakeawayMapper.selectByPrimaryKey(shopId);
cacheDTO.setShopTakeaway(takeaway);
ProductPageQuery query = new ProductPageQuery();
query.setPageNum(1);
query.setPageSize(200);
List<String> shopIdList = new ArrayList<>();
shopIdList.add(shopId);
query.setShopIdList(shopIdList);
query.setIsPush(1); // 推荐
IPage<ProductPageVO> productList = getShareList(query);
cacheDTO.setProducts(productList.getRecords());
String redisKey = "SHOP_CACHE:" + regionId;
redisTemplateHelper.hPut(redisKey, shopId, JSONUtil.toJsonStr(cacheDTO));
}
@Override
public void removeShopCache(String shopId, String regionId) {
if (CharSequenceUtil.isBlank(regionId)) {
return;
}
String redisKey = "SHOP_CACHE:" + regionId;
redisTemplateHelper.hDelete(redisKey, shopId);
}
} }

18
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopTakeawayServiceImpl.java

@ -19,10 +19,12 @@ import cc.hiver.mall.dao.mapper.ShopTakeawayMapper;
import cc.hiver.mall.entity.ShopTakeaway; import cc.hiver.mall.entity.ShopTakeaway;
import cc.hiver.mall.pojo.query.ShopTakeawayQuery; import cc.hiver.mall.pojo.query.ShopTakeawayQuery;
import cc.hiver.mall.service.ShopTakeawayService; import cc.hiver.mall.service.ShopTakeawayService;
import cc.hiver.mall.service.ShopService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -40,6 +42,11 @@ public class ShopTakeawayServiceImpl extends ServiceImpl<ShopTakeawayMapper, Sh
@Autowired @Autowired
private ShopTakeawayMapper shopTakeawayMapper; private ShopTakeawayMapper shopTakeawayMapper;
@Autowired
@Lazy
private ShopService shopService;
@Override @Override
public ShopTakeaway selectByPrimaryKey(String shopId) { public ShopTakeaway selectByPrimaryKey(String shopId) {
return shopTakeawayMapper.selectByPrimaryKey(shopId); return shopTakeawayMapper.selectByPrimaryKey(shopId);
@ -47,18 +54,29 @@ public class ShopTakeawayServiceImpl extends ServiceImpl<ShopTakeawayMapper, Sh
@Override @Override
public void deleteByPrimaryKey(String shopId) { public void deleteByPrimaryKey(String shopId) {
ShopTakeaway old = shopTakeawayMapper.selectByPrimaryKey(shopId);
shopTakeawayMapper.deleteByPrimaryKey(shopId); shopTakeawayMapper.deleteByPrimaryKey(shopId);
if (old != null && old.getRegionId() != null) {
shopService.refreshShopCache(shopId, old.getRegionId());
}
} }
@Override @Override
public ShopTakeaway insert(ShopTakeaway shopTakeaway) { public ShopTakeaway insert(ShopTakeaway shopTakeaway) {
shopTakeawayMapper.insert(shopTakeaway); shopTakeawayMapper.insert(shopTakeaway);
if (shopTakeaway.getRegionId() != null) {
shopService.refreshShopCache(shopTakeaway.getShopId(), shopTakeaway.getRegionId());
}
return shopTakeaway; return shopTakeaway;
} }
@Override @Override
public void updateByPrimaryKeySelective(ShopTakeaway shopTakeaway) { public void updateByPrimaryKeySelective(ShopTakeaway shopTakeaway) {
shopTakeawayMapper.updateByPrimaryKeySelective(shopTakeaway); shopTakeawayMapper.updateByPrimaryKeySelective(shopTakeaway);
ShopTakeaway old = shopTakeawayMapper.selectByPrimaryKey(shopTakeaway.getShopId());
if (old != null && old.getRegionId() != null) {
shopService.refreshShopCache(old.getShopId(), old.getRegionId());
}
} }
@Override @Override

Loading…
Cancel
Save