diff --git a/klp-ui/src/api/wms/productSalesScript.js b/klp-ui/src/api/wms/productSalesScript.js index 750e7f90..28c676d2 100644 --- a/klp-ui/src/api/wms/productSalesScript.js +++ b/klp-ui/src/api/wms/productSalesScript.js @@ -51,3 +51,28 @@ export function delProductSalesScript(scriptId) { method: 'delete' }) } + +// // 记录话术访问频率 +// export function recordVisit(productId) { +// return request({ +// url: '/klp/productSalesScript/recordVisit/' + productId, +// method: 'post' +// }) +// } + +// // 获取热门产品排行 +// export function getHotProducts(limit = 10) { +// return request({ +// url: '/klp/productSalesScript/hotProducts', +// method: 'get', +// params: { limit } +// }) +// } + +// 获取产品咨询热度排行 +export function getProductRanking() { + return request({ + url: '/klp/productSalesScript/dashboard/ranking', + method: 'get' + }) +} diff --git a/klp-ui/src/views/wms/hotProduct/index.vue b/klp-ui/src/views/wms/hotProduct/index.vue new file mode 100644 index 00000000..13771f86 --- /dev/null +++ b/klp-ui/src/views/wms/hotProduct/index.vue @@ -0,0 +1,619 @@ + + + + + \ No newline at end of file diff --git a/klp-wms/src/main/java/com/klp/controller/WmsProductSalesScriptController.java b/klp-wms/src/main/java/com/klp/controller/WmsProductSalesScriptController.java index 42cb30dd..e9b4a0c7 100644 --- a/klp-wms/src/main/java/com/klp/controller/WmsProductSalesScriptController.java +++ b/klp-wms/src/main/java/com/klp/controller/WmsProductSalesScriptController.java @@ -6,7 +6,7 @@ import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; -import cn.dev33.satoken.annotation.SaCheckPermission; + import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import com.klp.common.annotation.RepeatSubmit; @@ -22,6 +22,9 @@ import com.klp.domain.vo.WmsProductSalesScriptVo; import com.klp.domain.bo.WmsProductSalesScriptBo; import com.klp.service.IWmsProductSalesScriptService; import com.klp.common.core.page.TableDataInfo; +import com.klp.common.utils.redis.RedisUtils; +import java.time.Duration; +import com.klp.domain.vo.ProductRankingVo; /** * 产品销售话术 @@ -63,7 +66,14 @@ public class WmsProductSalesScriptController extends BaseController { @GetMapping("/{scriptId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long scriptId) { - return R.ok(iWmsProductSalesScriptService.queryById(scriptId)); + WmsProductSalesScriptVo vo = iWmsProductSalesScriptService.queryById(scriptId); + + // 记录产品访问次数到Redis + if (vo != null && vo.getProductId() != null) { + iWmsProductSalesScriptService.recordProductViewCount(vo.getProductId()); + } + + return R.ok(vo); } /** @@ -97,4 +107,13 @@ public class WmsProductSalesScriptController extends BaseController { @PathVariable Long[] scriptIds) { return toAjax(iWmsProductSalesScriptService.deleteWithValidByIds(Arrays.asList(scriptIds), true)); } + + /** + * 获取产品咨询热度排行 + */ + @GetMapping("/dashboard/ranking") + public R> getProductRanking() { + List ranking = iWmsProductSalesScriptService.getProductRanking(); + return R.ok(ranking); + } } diff --git a/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java b/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java index 4f016506..2ec2d9cb 100644 --- a/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java +++ b/klp-wms/src/main/java/com/klp/controller/WmsStockIoController.java @@ -155,9 +155,6 @@ public class WmsStockIoController extends BaseController { * 扫码枪专用:根据明细ID直接入库,无需审核整单 */ @PostMapping("/scanInStock") - /** - * 扫码枪专用:根据传入明细参数直接入库,无需审核整单 - */ public R scanInStock(@RequestBody WmsStockIoDetailBo bo) { try { boolean result = iWmsStockIoService.scanInStockByBo(bo); diff --git a/klp-wms/src/main/java/com/klp/domain/vo/DashboardOverviewVO.java b/klp-wms/src/main/java/com/klp/domain/vo/DashboardOverviewVO.java index 10cc8ba5..d7b4405a 100644 --- a/klp-wms/src/main/java/com/klp/domain/vo/DashboardOverviewVO.java +++ b/klp-wms/src/main/java/com/klp/domain/vo/DashboardOverviewVO.java @@ -3,11 +3,37 @@ package com.klp.domain.vo; import lombok.Data; import java.util.List; +/** + * 数据看板概览视图对象 + * + * @author klp + * @date 2025-01-27 + */ @Data public class DashboardOverviewVO { + + /** + * 订单汇总 + */ private OrderSummaryVO orderSummary; + + /** + * 销售经理饼图 + */ private List salesManagerPie; + + /** + * 产品销量排行 + */ private List productRank; + + /** + * 订单物料分析 + */ private List orderMaterial; + + /** + * 客户分布 + */ private List customerRegion; } diff --git a/klp-wms/src/main/java/com/klp/domain/vo/ProductRankingVo.java b/klp-wms/src/main/java/com/klp/domain/vo/ProductRankingVo.java new file mode 100644 index 00000000..02c45a54 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/vo/ProductRankingVo.java @@ -0,0 +1,38 @@ +package com.klp.domain.vo; + +import lombok.Data; + +/** + * 产品咨询热度排行视图对象 + * + * @author klp + * @date 2025-07-24 + */ +@Data +public class ProductRankingVo { + + /** + * 产品ID + */ + private Long productId; + + /** + * 产品编号 + */ + private String productCode; + + /** + * 产品名称 + */ + private String productName; + + /** + * 访问次数 + */ + private Long viewCount; + + /** + * 排名 + */ + private Integer ranking; +} \ No newline at end of file diff --git a/klp-wms/src/main/java/com/klp/service/IWmsProductSalesScriptService.java b/klp-wms/src/main/java/com/klp/service/IWmsProductSalesScriptService.java index b49f56c2..4d47bf66 100644 --- a/klp-wms/src/main/java/com/klp/service/IWmsProductSalesScriptService.java +++ b/klp-wms/src/main/java/com/klp/service/IWmsProductSalesScriptService.java @@ -1,10 +1,10 @@ package com.klp.service; -import com.klp.domain.WmsProductSalesScript; import com.klp.domain.vo.WmsProductSalesScriptVo; import com.klp.domain.bo.WmsProductSalesScriptBo; import com.klp.common.core.page.TableDataInfo; import com.klp.common.core.domain.PageQuery; +import com.klp.domain.vo.ProductRankingVo; import java.util.Collection; import java.util.List; @@ -47,4 +47,13 @@ public interface IWmsProductSalesScriptService { */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + /** + * 记录产品访问次数 + */ + void recordProductViewCount(Long productId); + + /** + * 获取产品咨询热度排行 + */ + List getProductRanking(); } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsProductSalesScriptServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsProductSalesScriptServiceImpl.java index b93fa3c6..84db4bff 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsProductSalesScriptServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsProductSalesScriptServiceImpl.java @@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.klp.common.utils.StringUtils; +import com.klp.domain.WmsProduct; +import com.klp.mapper.WmsProductMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import com.klp.domain.bo.WmsProductSalesScriptBo; @@ -14,10 +16,16 @@ import com.klp.domain.vo.WmsProductSalesScriptVo; import com.klp.domain.WmsProductSalesScript; import com.klp.mapper.WmsProductSalesScriptMapper; import com.klp.service.IWmsProductSalesScriptService; +import com.klp.domain.vo.ProductRankingVo; +import com.klp.common.utils.redis.RedisUtils; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Collection; +import org.redisson.api.RScoredSortedSet; + +import javax.annotation.Resource; /** * 产品销售话术Service业务层处理 @@ -31,6 +39,9 @@ public class WmsProductSalesScriptServiceImpl implements IWmsProductSalesScriptS private final WmsProductSalesScriptMapper baseMapper; + @Resource + private WmsProductMapper wmsProductMapper; + /** * 查询产品销售话术 */ @@ -117,4 +128,59 @@ public class WmsProductSalesScriptServiceImpl implements IWmsProductSalesScriptS } return baseMapper.deleteBatchIds(ids) > 0; } + + @Override + public void recordProductViewCount(Long productId) { + String countKey = "product:view:count:" + productId; + String rankingKey = "product:view:ranking"; + + // 计数器+1(使用原子操作) + RedisUtils.incrAtomicValue(countKey); + + // 设置计数器过期时间为1天 + RedisUtils.expire(countKey, 24 * 60 * 60); + + // 更新排行榜(使用ZSet操作) + RScoredSortedSet zSet = RedisUtils.getClient().getScoredSortedSet(rankingKey); + zSet.addScore(productId.toString(), 1); + + // 设置排行榜过期时间为1天 + RedisUtils.expire(rankingKey, 24 * 60 * 60); + } + + @Override + public List getProductRanking() { + String rankingKey = "product:view:ranking"; + + // 获取排行榜前10名(按访问次数降序) + RScoredSortedSet zSet = RedisUtils.getClient().getScoredSortedSet(rankingKey); + + List rankingList = new ArrayList<>(); + int rank = 1; + + // 获取前10名的产品ID和分数 + Collection topProducts = zSet.valueRangeReversed(0, 9); + + for (String productIdStr : topProducts) { + Long productId = Long.valueOf(productIdStr); + Double score = zSet.getScore(productIdStr); + Long viewCount = score != null ? score.longValue() : 0L; + + ProductRankingVo vo = new ProductRankingVo(); + vo.setProductId(productId); + vo.setViewCount(viewCount); + vo.setRanking(rank++); + + //补充产品基础信息(这里需要注入ProductMapper) + WmsProduct product = wmsProductMapper.selectById(productId); + if (product != null) { + vo.setProductCode(product.getProductCode()); + vo.setProductName(product.getProductName()); + } + + rankingList.add(vo); + } + + return rankingList; + } }