Răsfoiți Sursa

产品部分合并

xucaiqin 1 lună în urmă
părinte
comite
d31333092a
16 a modificat fișierele cu 897 adăugiri și 37 ștergeri
  1. 4 0
      sckw-modules-api/sckw-product-api/src/main/java/com/sckw/product/api/dubbo/GoodsInfoService.java
  2. 10 3
      sckw-modules/sckw-product/pom.xml
  3. 32 3
      sckw-modules/sckw-product/src/main/java/com/sckw/product/controller/KwpGoodsController.java
  4. 19 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/dao/KwpGoodsViewLogMapper.java
  5. 23 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/dubbo/GoodsInfoServiceImpl.java
  6. 98 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/KwpGoodsViewLog.java
  7. 8 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/req/BuildingMaterialsMarketListParam.java
  8. 6 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/BuildingMaterialsMarketList.java
  9. 2 2
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsDetail.java
  10. 201 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsDetailVo.java
  11. 62 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsEntInfo.java
  12. 56 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsViewLogVo.java
  13. 272 22
      sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpGoodsService.java
  14. 61 0
      sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpGoodsViewLogService.java
  15. 3 7
      sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpSearchLogService.java
  16. 40 0
      sckw-modules/sckw-product/src/main/resources/mapper/KwpGoodsViewLogMapper.xml

+ 4 - 0
sckw-modules-api/sckw-product-api/src/main/java/com/sckw/product/api/dubbo/GoodsInfoService.java

@@ -1,6 +1,7 @@
 package com.sckw.product.api.dubbo;
 
 import com.sckw.core.web.response.HttpResult;
+import com.sckw.product.api.model.AddressInfoDetail;
 import com.sckw.product.api.model.GoodsDetail;
 import com.sckw.product.api.model.KwpGoods;
 
@@ -73,7 +74,10 @@ public interface GoodsInfoService {
     KwpGoods getGoodsByGoodsName(String goodsName);
 
     List<Long> getGoodsByGoodsNameAndTaxRate(String goodsName, String goodsTaxRate);
+    List<Long> getGoodsByPara(String goodsName,List<String> goodsType, String goodsSpec);
 
     List<KwpGoods> getGoodsByNameTypeDesc(String goodsName, String goodsType, String goodsSpec);
     List<KwpGoods> findGoodsByGoodsName(String goodsName);
+
+    AddressInfoDetail getGoodsAddress(Long goodsId);
 }

+ 10 - 3
sckw-modules/sckw-product/pom.xml

@@ -77,7 +77,6 @@
             <version>${basic.version}</version>
 <!--            <version>1.0.0</version>-->
         </dependency>
-
         <dependency>
             <groupId>com.sckw</groupId>
             <artifactId>sckw-fleet-api</artifactId>
@@ -110,7 +109,15 @@
             <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
             <version>4.5.0</version>
         </dependency>
-
+        <dependency>
+            <groupId>com.sckw</groupId>
+            <artifactId>sckw-order-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sckw</groupId>
+            <artifactId>sckw-contract-api</artifactId>
+            <version>${basic.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
@@ -129,4 +136,4 @@
         </plugins>
     </build>
 
-</project>
+</project>

+ 32 - 3
sckw-modules/sckw-product/src/main/java/com/sckw/product/controller/KwpGoodsController.java

@@ -22,6 +22,7 @@ import lombok.RequiredArgsConstructor;
 import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+
 import java.util.List;
 
 /**
@@ -45,7 +46,7 @@ public class KwpGoodsController {
      * @Param param:
      * @return: com.sckw.core.web.response.HttpResult
      */
-    @RepeatSubmit(interval = 1000,message ="前方拥堵,请稍后尝试")
+    @RepeatSubmit(interval = 1000, message = "前方拥堵,请稍后尝试")
     @PostMapping(value = "/addDraft", produces = MediaType.APPLICATION_JSON_VALUE)
     public HttpResult addDraft(@RequestBody @Validated AddGoodsDraftParam param) {
         kwpGoodsService.addDraft(param);
@@ -59,7 +60,7 @@ public class KwpGoodsController {
      * @Param addGoodsParam:
      * @return: com.sckw.core.web.response.HttpResult
      */
-    @RepeatSubmit(interval = 1000,message ="前方拥堵,请稍后尝试")
+    @RepeatSubmit(interval = 1000, message = "前方拥堵,请稍后尝试")
     @Operation(summary = "添加商品上架", description = "添加商品上架")
     @PostMapping(value = "/addShelves", produces = MediaType.APPLICATION_JSON_VALUE)
     public HttpResult addShelves(@RequestBody @Validated AddGoodsParam addGoodsParam) {
@@ -80,6 +81,12 @@ public class KwpGoodsController {
         return HttpResult.ok(kwpGoodsService.getDetail(id));
     }
 
+    @Operation(summary = "商品详情", description = "商品详情信息")
+    @GetMapping("/detail2")
+    public HttpResult detail2(@RequestParam Long id, String keywords, Integer source, HttpServletRequest request) {
+        return HttpResult.ok(kwpGoodsService.getDetail2(id, keywords, source, request));
+    }
+
     /**
      * @desc: 修改
      * @author: yzc
@@ -89,7 +96,7 @@ public class KwpGoodsController {
      */
     @Operation(summary = "修改商品", description = "根据id修改商品")
     @PostMapping(value = "/update", produces = MediaType.APPLICATION_JSON_VALUE)
-    @RepeatSubmit(interval = 1000,message ="前方拥堵,请稍后尝试")
+    @RepeatSubmit(interval = 1000, message = "前方拥堵,请稍后尝试")
     public HttpResult update(@RequestBody @Validated UpdateGoodsParam updateGoodsParam) {
         kwpGoodsService.update(updateGoodsParam);
         return HttpResult.ok("修改商品成功");
@@ -179,6 +186,28 @@ public class KwpGoodsController {
         return HttpResult.ok("批量删除成功");
     }
 
+    /**
+     * 平台热搜词前十
+     *
+     * @return
+     */
+    @GetMapping("/hot")
+    @Operation(summary = "平台热搜词前十", description = "平台热搜词前十")
+    public HttpResult hot() {
+        return HttpResult.ok(kwpGoodsService.hotKeys());
+    }
+
+    /**
+     * 热销商品列表(商品推荐)
+     *
+     * @return
+     */
+    @GetMapping(value = "/hotGoods", produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "热销商品列表", description = "热销商品列表")
+    public HttpResult hotGoods() {
+        return HttpResult.ok(kwpGoodsService.hootGoods());
+    }
+
     /**
      * @desc: 建材市场分页查询
      * @author: yzc

+ 19 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/dao/KwpGoodsViewLogMapper.java

@@ -0,0 +1,19 @@
+package com.sckw.product.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.sckw.product.model.KwpGoodsViewLog;
+import com.sckw.product.model.vo.res.GoodsViewLogVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @author xucaiqin
+ * @date 2025-11-14 09:19:22
+ */
+@Mapper
+public interface KwpGoodsViewLogMapper extends BaseMapper<KwpGoodsViewLog> {
+    List<GoodsViewLogVo> searchViewCount(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end);
+}

+ 23 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/dubbo/GoodsInfoServiceImpl.java

@@ -1,12 +1,17 @@
 package com.sckw.product.dubbo;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.sckw.core.utils.BeanUtils;
 import com.sckw.core.utils.CollectionUtils;
 import com.sckw.core.web.response.HttpResult;
 import com.sckw.product.api.dubbo.GoodsInfoService;
+import com.sckw.product.api.model.AddressInfoDetail;
 import com.sckw.product.api.model.GoodsDetail;
 import com.sckw.product.api.model.KwpGoods;
+import com.sckw.product.dao.KwpGoodsAddressMapper;
+import com.sckw.product.model.KwpGoodsAddress;
 import com.sckw.product.repository.KwpGoodsRepository;
 import com.sckw.product.service.KwpGoodsService;
 import com.sckw.redis.constant.RedisConstant;
@@ -37,6 +42,8 @@ public class GoodsInfoServiceImpl implements GoodsInfoService {
     @Autowired
     private KwpGoodsService kwpGoodsService;
     private final KwpGoodsRepository kwpGoodsRepository;
+    @Autowired
+    private KwpGoodsAddressMapper kwpGoodsAddressMapper;
 
     @Override
     public GoodsDetail getDetailById(Long id) {
@@ -107,6 +114,12 @@ public class GoodsInfoServiceImpl implements GoodsInfoService {
         return CollUtil.emptyIfNull(ids);
     }
 
+    @Override
+    public List<Long> getGoodsByPara(String goodsName, List<String> goodsType, String goodsSpec) {
+        List<Long> ids = kwpGoodsService.selectByPara(goodsName, goodsType, goodsSpec);
+        return CollUtil.emptyIfNull(ids);
+    }
+
     @Override
     public List<KwpGoods> getGoodsByNameTypeDesc(String goodsName, String goodsType, String goodsSpec) {
         List<com.sckw.product.model.KwpGoods> kwpGoods = kwpGoodsRepository.queryByGoodsNameAndTypeAndDesc(goodsName, goodsType, goodsSpec);
@@ -130,6 +143,16 @@ public class GoodsInfoServiceImpl implements GoodsInfoService {
                 .collect(Collectors.toList());
     }
 
+    @Override
+    public AddressInfoDetail getGoodsAddress(Long goodsId) {
+        KwpGoodsAddress kwpGoodsAddress = kwpGoodsAddressMapper.selectOne(new LambdaQueryWrapper<KwpGoodsAddress>()
+                .eq(KwpGoodsAddress::getGoodsId, goodsId).eq(KwpGoodsAddress::getDelFlag, 0).last("limit 1"));
+        if (Objects.nonNull(kwpGoodsAddress)) {
+            return BeanUtil.toBean(kwpGoodsAddress, AddressInfoDetail.class);
+        }
+        return null;
+    }
+
     @NotNull
     private static KwpGoods getKwpGoods(com.sckw.product.model.KwpGoods g) {
         KwpGoods goods = new KwpGoods();

+ 98 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/KwpGoodsViewLog.java

@@ -0,0 +1,98 @@
+package com.sckw.product.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+import java.time.LocalDateTime;
+
+/**
+ * 搜索查看日志表
+* @date 2025-11-14 09:27:54
+* @author xucaiqin
+*/
+@Getter
+@Setter
+@ToString
+@Accessors(chain = true)
+@TableName("kwp_goods_view_log")
+public class KwpGoodsViewLog {
+    private Long id;
+
+    /**
+     * 企业id
+     */
+    private Long entId;
+
+    /**
+     * 用户
+     */
+    private Long userId;
+
+    /**
+     * 商品id
+     */
+    private Long goodsId;
+
+    /**
+     * 商品名称
+     */
+    private String goodsName;
+
+    /**
+     * 商品类别
+     */
+    private String goodsType;
+
+    /**
+     * 来源 1-商品推荐 2-搜索
+     */
+    private Integer source;
+
+    /**
+     * 当前搜索词
+     */
+    private String searchWords;
+
+    /**
+     * 设备信息
+     */
+    private String userAgent;
+
+    /**
+     * IP地址
+     */
+    private String ipAddress;
+
+    /**
+     * 搜索时间
+     */
+    private LocalDateTime searchTime;
+
+    /**
+     * 创建人
+     */
+    private Long createBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新人
+     */
+    private Long updateBy;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 删除标识(0正常/1删除)
+     */
+    private Integer delFlag;
+}

+ 8 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/req/BuildingMaterialsMarketListParam.java

@@ -39,5 +39,13 @@ public class BuildingMaterialsMarketListParam extends PageRequest {
      * 区域等级
      */
     private Integer areaLevel;
+    /**
+     * 签约状态 1-已签约 2-未签约
+     */
+    private Integer sign;
+    /**
+     * 合同id
+     */
+    private Long contractId;
 
 }

+ 6 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/BuildingMaterialsMarketList.java

@@ -1,6 +1,7 @@
 package com.sckw.product.model.vo.res;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
@@ -85,4 +86,9 @@ public class BuildingMaterialsMarketList {
      */
     private String supplyEnt;
 
+    @Schema(description = "签约价")
+    private BigDecimal signPrice;
+
+    @Schema(description = "是否签约")
+    private boolean signFlag;
 }

+ 2 - 2
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsDetail.java

@@ -194,14 +194,14 @@ public class GoodsDetail {
      * 上架时间
      */
     @Schema(description = "上架时间")
-    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date addedTime;
 
     /**
      * 下架时间
      */
     @Schema(description = "下架时间")
-    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date shelfTime;
 
     /**

+ 201 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsDetailVo.java

@@ -0,0 +1,201 @@
+package com.sckw.product.model.vo.res;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @desc: 商品详情响应
+ * @author: yzc
+ * @date: 2023-07-05 11:49
+ */
+@Getter
+@Setter
+@ToString
+@Accessors(chain = true)
+@Schema(description = "商品详情")
+public class GoodsDetailVo {
+
+    /**
+     * 商品id
+     */
+    @Schema(description = "商品id")
+    private Long id;
+
+    /**
+     * 企业id
+     */
+    @Schema(description = "企业id")
+    private Long entId;
+
+    /**
+     * 商品编号
+     */
+    @Schema(description = "商品编号")
+    private String code;
+
+    /**
+     * 商品名称
+     */
+    @Schema(description = "商品名称")
+    private String name;
+
+    /**
+     * 商品类型
+     */
+    @Schema(description = "商品类型")
+    private String goodsType;
+
+    /**
+     * 商品类型集合
+     */
+    @Schema(description = "商品类型集合")
+    private List<String> goodsTypes;
+
+    /**
+     * 商品类型lab
+     */
+    @Schema(description = "商品类型lab")
+    private String goodsTypeLabel;
+
+    /**
+     * 商品类型labs
+     */
+    @Schema(description = "商品类型labs")
+    private String goodsTypeLabels;
+
+    /**
+     * 库存数量
+     */
+    @Schema(description = "库存数量")
+    private BigDecimal amount;
+
+    /**
+     * 价格单位
+     */
+    @Schema(description = "价格单位")
+    private String priceUnit;
+
+    /**
+     * 富文本内容
+     */
+    @Schema(description = "富文本内容")
+    private String content;
+
+    /**
+     * 参考价格
+     */
+    @Schema(description = "参考价格")
+    private BigDecimal price;
+
+    /**
+     * 单位(吨、方、件、箱、其他)
+     */
+    @Schema(description = "单位(吨、方、件、箱、其他)")
+    private String unit;
+
+    /**
+     * 单位lab
+     */
+    @Schema(description = "单位lab")
+    private String unitLabel;
+
+    /**
+     * 尺寸大小
+     */
+    @Schema(description = "尺寸大小")
+    private String spec;
+
+    /**
+     * 预付款最低限额
+     */
+    @Schema(description = "预付款最低限额")
+    private BigDecimal advancePrice;
+
+    /**
+     * 专属客户经理id
+     */
+    @Schema(description = "专属客户经理id")
+    private Long manager;
+
+    /**
+     * 专属客户经理姓名
+     */
+    @Schema(description = "专属客户经理姓名")
+    private String managerName;
+
+    /**
+     * 成交量
+     */
+    @Schema(description = "成交量")
+    private BigDecimal performedAmount;
+
+    /**
+     * 缩略图
+     */
+    @Schema(description = "缩略图")
+    private String thumb;
+
+    /**
+     * 上架时间
+     */
+    @Schema(description = "上架时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date addedTime;
+
+    /**
+     * 下架时间
+     */
+    @Schema(description = "下架时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date shelfTime;
+
+    /**
+     * 备注
+     */
+    @Schema(description = "备注")
+    private String remark;
+
+    /**
+     * 状态:0草稿/1上架/2下架
+     */
+    @Schema(description = "状态:0草稿/1上架/2下架")
+    private Integer status;
+
+    /**
+     * 状态label
+     */
+    @Schema(description = "状态label")
+    private String statusLabel;
+
+    @Schema(description = "供应商信息")
+    private GoodsEntInfo goodsEntInfo;
+
+    /**
+     * 商品详情图片
+     */
+    @Schema(description = "商品详情图片")
+    private List<GoodsImagesDetail> images;
+
+    /**
+     * 地址信息
+     */
+    @Schema(description = "商品发货地址信息")
+    private AddressInfoDetail addressInfo;
+
+    /**
+     * 参数目录
+     */
+    @Schema(description = "参数目录")
+    private List<GoodsAttributesDetail> attributes;
+
+
+
+}

+ 62 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsEntInfo.java

@@ -0,0 +1,62 @@
+package com.sckw.product.model.vo.res;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * desc 商品供应商信息
+ * author zk
+ * date 2023/12/4 0004
+ */
+@Data
+@Schema(description = "商品供应商信息")
+public class GoodsEntInfo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 42704905372870133L;
+
+    /**
+     * 企业名称
+     */
+    @Schema(description = "企业名称")
+    private String ent;
+
+    /**
+     * 企业头像
+     */
+    @Schema(description = "企业头像")
+    private String entHead;
+    /**
+     * 企业主营范围
+     */
+    @Schema(description = "企业主营范围")
+    private String entBusiness;
+
+    /**
+     * 企业地址
+     */
+    @Schema(description = "企业地址")
+    private String entAddress;
+
+    /**
+     * 联系人
+     */
+    @Schema(description = "联系人")
+    private String contacts;
+
+    /**
+     * 联系电话
+     */
+    @Schema(description = "联系电话")
+    private String phone;
+
+
+    /**
+     * 详细地址
+     */
+    @Schema(description = "详细地址")
+    private String detailAddress;
+}

+ 56 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/model/vo/res/GoodsViewLogVo.java

@@ -0,0 +1,56 @@
+package com.sckw.product.model.vo.res;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 搜索查看日志表
+ *
+ * @author xucaiqin
+ * @date 2025-11-14 09:27:54
+ */
+@Getter
+@Setter
+public class GoodsViewLogVo {
+    private Long id;
+
+    /**
+     * 企业id
+     */
+    private Long entId;
+
+    /**
+     * 用户
+     */
+    private Long userId;
+
+    /**
+     * 商品id
+     */
+    private Long goodsId;
+
+    /**
+     * 商品名称
+     */
+    private String goodsName;
+
+    /**
+     * 商品类别
+     */
+    private String goodsType;
+
+    /**
+     * 来源 1-商品推荐 2-搜索
+     */
+    private Integer source;
+
+    /**
+     * 当前搜索词
+     */
+    private String searchWords;
+    /**
+     * 浏览量
+     */
+    private Integer count;
+
+}

+ 272 - 22
sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpGoodsService.java

@@ -1,12 +1,16 @@
 package com.sckw.product.service;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.collect.Lists;
+import com.sckw.contract.api.RemoteContractService;
+import com.sckw.contract.api.model.vo.TradeContractGoodsDto;
 import com.sckw.core.common.enums.enums.DictTypeEnum;
 import com.sckw.core.exception.BusinessException;
 import com.sckw.core.exception.CustomPromptException;
@@ -17,21 +21,21 @@ import com.sckw.core.model.page.PageResult;
 import com.sckw.core.model.vo.TableBottom;
 import com.sckw.core.model.vo.TableStatisticRes;
 import com.sckw.core.model.vo.TableTop;
-import com.sckw.core.utils.BeanUtils;
-import com.sckw.core.utils.CollectionUtils;
-import com.sckw.core.utils.FileUtils;
-import com.sckw.core.utils.StringUtils;
+import com.sckw.core.utils.*;
 import com.sckw.core.web.constant.HttpStatus;
 import com.sckw.core.web.context.LoginUserHolder;
 import com.sckw.excel.utils.DateUtil;
 import com.sckw.fleet.api.RemoteFleetService;
 import com.sckw.fleet.api.model.vo.TmsTruckAxleNumVO;
+import com.sckw.order.api.dubbo.TradeOrderInfoService;
+import com.sckw.order.api.model.OrderSaleVo;
 import com.sckw.payment.api.dubbo.PayCenterDubboService;
 import com.sckw.payment.api.model.constant.ChannelEnum;
 import com.sckw.payment.api.model.dto.WalletDto;
 import com.sckw.payment.api.model.dto.common.R;
 import com.sckw.product.api.model.GoodsNumStsParam;
 import com.sckw.product.dao.KwpGoodsMapper;
+import com.sckw.product.dao.KwpGoodsViewLogMapper;
 import com.sckw.product.enums.GoodsStatusEnum;
 import com.sckw.product.model.*;
 import com.sckw.product.model.vo.req.*;
@@ -45,7 +49,6 @@ import com.sckw.stream.model.UserInfo;
 import com.sckw.system.api.RemoteSystemService;
 import com.sckw.system.api.RemoteUserService;
 import com.sckw.system.api.model.dto.res.EntCacheResDto;
-import com.sckw.system.api.model.dto.res.KwsUserResDto;
 import com.sckw.system.api.model.dto.res.SysDictResDto;
 import com.sckw.system.api.model.dto.res.UserCacheResDto;
 import jakarta.servlet.http.HttpServletRequest;
@@ -54,12 +57,15 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.stream.function.StreamBridge;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.time.LocalDateTime;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -84,8 +90,15 @@ public class KwpGoodsService {
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private RemoteFleetService remoteFleetService;
 
+    @DubboReference(version = "1.0.0", group = "design", check = false)
+    private TradeOrderInfoService tradeOrderInfoService;
+    @DubboReference(version = "1.0.0", group = "design", check = false)
+    private RemoteContractService remoteContractService;
+
 
     private final KwpGoodsMapper kwpGoodsMapper;
+    private final KwpGoodsViewLogMapper kwpGoodsViewLogMapper;
+    private final KwpGoodsViewLogService kwpGoodsViewLogService;
     private final KwpGoodsAddressService kwpGoodsAddressService;
     private final KwpGoodsAttributeService kwpGoodsAttributeService;
     private final KwpGoodsImageService kwpGoodsImageService;
@@ -93,7 +106,8 @@ public class KwpGoodsService {
     private final KwpGoodsUnitService kwpGoodsUnitService;
     private final StreamBridge streamBridge;
     private final KwpGoodsRepository kwpGoodsRepository;
-
+    private final RedisTemplate<String, Object> redisTemplate;
+    private final KwpSearchLogService kwpSearchLogService;
     @Value("${goods.url.list.pc}")
     private String pcGoodsListUrl;
     @Value("${goods.url.list.app}")
@@ -212,6 +226,88 @@ public class KwpGoodsService {
         }
     }
 
+    public GoodsDetailVo getDetail2(Long id, String keywords, Integer source, HttpServletRequest request) {
+        KwpGoods goods = kwpGoodsMapper.selectById(id);
+        if (Objects.isNull(goods)) {
+            throw new BusinessException("当前商品不存在!");
+        }
+        GoodsDetailVo detail = BeanUtils.copyProperties(goods, GoodsDetailVo.class);
+        GoodsEntInfo goodsEntInfo = new GoodsEntInfo();
+
+        kwpGoodsViewLogService.saveLog(id, detail.getName(), detail.getGoodsType(), keywords, source, request);
+
+        Map<Long, EntCacheResDto> entMap = remoteSystemService.queryEntCacheMapByIds(Arrays.asList(detail.getEntId(), goods.getSupplyEntId()));
+//        EntCacheResDto ent = entMap.get(detail.getEntId());
+//        if (Objects.nonNull(ent)) {
+//            goodsEntInfo.setEnt(ent.getFirmName());
+//            goodsEntInfo.setEntHead(ent.getHead());
+//            goodsEntInfo.setEntBusiness(ent.getBusiness());
+//            goodsEntInfo.setEntAddress(ent.getCityName());
+//        }
+        EntCacheResDto supplyEnt = entMap.get(goods.getSupplyEntId());
+        if (Objects.nonNull(supplyEnt)) {
+            goodsEntInfo.setEnt(supplyEnt.getFirmName());
+            goodsEntInfo.setEntHead(supplyEnt.getHead());
+            goodsEntInfo.setEntBusiness(supplyEnt.getBusiness());
+            goodsEntInfo.setEntAddress(supplyEnt.getCityName());
+            goodsEntInfo.setContacts(supplyEnt.getContacts());
+            goodsEntInfo.setPhone(supplyEnt.getPhone());
+        }
+        UserCacheResDto managerInfo = remoteSystemService.queryUserCacheById(detail.getManager());
+        if (Objects.nonNull(managerInfo)) {
+            detail.setManagerName(managerInfo.getName());
+        }
+        Map<String, Map<String, String>> dict = remoteSystemService.queryDictByType(List.of(DictTypeEnum.PRODUCT_NAME_TYPE.getType(),
+                DictTypeEnum.UNIT_TYPE.getType(),
+                DictTypeEnum.TAX_RATE.getType(),
+                DictTypeEnum.GOODS_STATUS.getType(),
+                DictTypeEnum.ADDRESS_TYPE.getType()));
+        Map<String, String> productNameMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        Map<String, String> unitMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        Map<String, String> goodsStatusMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        Map<String, String> addressMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        if (CollectionUtils.isNotEmpty(dict)) {
+            productNameMap = dict.get(DictTypeEnum.PRODUCT_NAME_TYPE.getType());
+            unitMap = dict.get(DictTypeEnum.UNIT_TYPE.getType());
+            goodsStatusMap = dict.get(DictTypeEnum.GOODS_STATUS.getType());
+            addressMap = dict.get(DictTypeEnum.ADDRESS_TYPE.getType());
+        }
+        List<SysDictResDto> types = remoteSystemService.queryDictFrontAll(DictTypeEnum.PRODUCT_NAME_TYPE.getType(), detail.getGoodsType());
+        if (CollectionUtils.isNotEmpty(types)) {
+            detail.setGoodsTypes(types.stream().map(SysDictResDto::getValue).toList())
+                    .setGoodsTypeLabels(String.join(Global.RIGHT_SLASH, types.stream().map(SysDictResDto::getLabel).toList()));
+        }
+        detail.setGoodsTypeLabel(CollectionUtils.isNotEmpty(productNameMap) ? productNameMap.get(detail.getGoodsType()) : null)
+                .setUnitLabel(CollectionUtils.isNotEmpty(unitMap) ? unitMap.get(detail.getUnit()) : null)
+                .setStatusLabel(CollectionUtils.isNotEmpty(goodsStatusMap) ? goodsStatusMap.get(String.valueOf(detail.getStatus())) : null);
+        //商品图片信息
+        if (Objects.nonNull(goods.getThumb())) {
+            detail.setThumb(goods.getThumb());
+        }
+        List<KwpGoodsImage> goodsImages = kwpGoodsImageService.getByGoodsId(id);
+        List<GoodsImagesDetail> images = BeanUtils.copyToList(goodsImages, GoodsImagesDetail.class);
+        //商品价格
+        List<KwpGoodsPriceRange> priceRanges = kwpGoodsPriceRangeService.getByGoodsId(id);
+        BigDecimal price = Optional.ofNullable(priceRanges)
+                .filter(list -> !list.isEmpty())
+                .map(list -> list.get(0))
+                .map(KwpGoodsPriceRange::getPrice)
+                .orElse(null);
+
+        //商品属性信息
+        List<KwpGoodsAttribute> attributesList = kwpGoodsAttributeService.getByGoodsId(id);
+        List<GoodsAttributesDetail> attributes = BeanUtils.copyToList(attributesList, GoodsAttributesDetail.class);
+        //商品地址信息
+        KwpGoodsAddress goodsAddress = kwpGoodsAddressService.getByGoodsId(id);
+        AddressInfoDetail addressInfo = BeanUtils.copyProperties(goodsAddress, AddressInfoDetail.class);
+        if (Objects.nonNull(addressInfo)) {
+            addressInfo.setTypeName(CollectionUtils.isNotEmpty(addressMap) ? addressMap.get(addressInfo.getType()) : null);
+        }
+        detail.setImages(images).setPrice(price).setGoodsEntInfo(goodsEntInfo).setAttributes(attributes).setAddressInfo(addressInfo);
+
+        return detail;
+    }
+
     /**
      * @desc: 获取商品详情
      * @author: yzc
@@ -720,7 +816,7 @@ public class KwpGoodsService {
                     .map(priceRangeList -> priceRangeList.get(0))
                     .map(KwpGoodsPriceRange::getPrice)
                     .orElse(null);
-                goodsList.setPrice(price);
+            goodsList.setPrice(price);
 
             result.add(goodsList);
         });
@@ -858,6 +954,11 @@ public class KwpGoodsService {
     public PageResult buildingMaterialsMarketList(BuildingMaterialsMarketListParam params, HttpServletRequest request) {
         Page<KwpGoods> page = new Page<>(params.getPage(), params.getPageSize());
         LambdaQueryWrapper<KwpGoods> wrapper = new LambdaQueryWrapper<>();
+        if (StrUtil.isNotBlank(params.getKeywords())) {
+            kwpSearchLogService.saveLog(params.getKeywords(), 1, request);
+            redisTemplate.opsForZSet().incrementScore(RedisConstant.HOT_KEY, params.getKeywords(), 1);
+            redisTemplate.expire(RedisConstant.HOT_KEY, DateTimeUtil.getTime(), TimeUnit.SECONDS);
+        }
         wrapper.eq(KwpGoods::getStatus, GoodsStatusEnum.PUT_ON_SHELVES.getCode()).eq(KwpGoods::getDelFlag, Global.NO);
         if (StringUtils.isNotBlank(params.getGoodsType()) && StringUtils.isNotBlank(params.getGoodsTypeValue())) {
             List<SysDictResDto> goodsTypeList = remoteSystemService.queryDictBottom(params.getGoodsType(), params.getGoodsTypeValue());
@@ -878,22 +979,19 @@ public class KwpGoodsService {
         if (StringUtils.isNotBlank(params.getKeywords())) {
             List<EntCacheResDto> entList = remoteSystemService.queryEntCacheByName(params.getKeywords());
             List<Long> entIds = entList.stream().map(EntCacheResDto::getId).toList();
-            if (CollectionUtils.isNotEmpty(entIds)) {
-                wrapper.and(e -> e.in(KwpGoods::getSupplyEntId, entIds).or().like(KwpGoods::getName, params.getKeywords()));
+            wrapper.and(kwpGoodsLambdaQueryWrapper -> kwpGoodsLambdaQueryWrapper.like(KwpGoods::getName, params.getKeywords())
+                    .or().like(KwpGoods::getSpec, params.getKeywords())
+                    .or(CollUtil.isNotEmpty(entIds)).in(CollUtil.isNotEmpty(entIds), KwpGoods::getSupplyEntId, entIds)
+            );
+        }
+        if (Objects.nonNull(params.getSign())) {//查询签约或未签约商品
+            List<Long> longs = tradeOrderInfoService.querySignGoods(LoginUserHolder.getEntId());
+            if (Objects.equals(params.getSign(), 1)) {
+                wrapper.in(CollUtil.isNotEmpty(longs), KwpGoods::getId, longs);
             } else {
-                wrapper.like(KwpGoods::getName, params.getKeywords());
+                wrapper.notIn(CollUtil.isNotEmpty(longs), KwpGoods::getId, longs);
             }
         }
-
-        //专场逻辑
-        String accessSpecial = request.getHeader("Access-Special");
-        //是否主平台
-        boolean mainPlatform = remoteSystemService.queryMainPlatform(accessSpecial);
-        if (!mainPlatform) {
-            List<Long> entIds = remoteSystemService.queryEntIdsByCode(accessSpecial, String.valueOf(EntTypeEnum.SUPPLIER.getCode()));
-            wrapper.in(KwpGoods::getEntId, entIds);
-        }
-
         wrapper.last("order by RAND()");
         Page<KwpGoods> kwpGoodsPage = kwpGoodsMapper.selectPage(page, wrapper);
         List<KwpGoods> list = kwpGoodsPage.getRecords();
@@ -928,6 +1026,7 @@ public class KwpGoodsService {
             productNameMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
             unitMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
         }
+        Long entId = LoginUserHolder.getEntId();
         list.forEach(e -> {
             BuildingMaterialsMarketList materials = BeanUtils.copyProperties(e, BuildingMaterialsMarketList.class);
             KwpGoodsAddress address = addressMap.get(e.getId());
@@ -939,6 +1038,14 @@ public class KwpGoodsService {
                     .setPrice(CollectionUtils.isEmpty(priceRanges) ? null : priceRanges.get(0).getPrice())
                     .setThumb(FileUtils.splice(e.getThumb()))
                     .setSupplyEnt(entMap.get(e.getSupplyEntId()));
+            materials.setSignFlag(false);
+            if (Objects.nonNull(entId)) {
+                TradeContractGoodsDto tradeContractResDto = remoteContractService.queryTradeContractNew(entId, e.getId(),LocalDateTime.now());
+                if (Objects.nonNull(tradeContractResDto)) {
+                    materials.setSignPrice(tradeContractResDto.getPrice());
+                    materials.setSignFlag(true);
+                }
+            }
             result.add(materials);
         });
         return PageResult.build(params.getPage(), params.getPageSize(), kwpGoodsPage.getTotal(), result);
@@ -1291,12 +1398,12 @@ public class KwpGoodsService {
         return list.stream().map(KwpGoods::getId).toList();
     }
 
-    public GoodsInfoResp getGoods( GoodsInfoReq req) {
+    public GoodsInfoResp getGoods(GoodsInfoReq req) {
         log.info("查询商品信息,请求参数 :{}", JSON.toJSONString(req));
         //查询上商品信息
         Long entId =StringUtils.isNotBlank(req.getEntId()) ? Long.valueOf(req.getEntId()) : null;
         List<KwpGoods> goods = kwpGoodsRepository.queryByEntIdAndGoodsName(entId, req.getGoodsName());
-        if (org.apache.commons.collections4.CollectionUtils.isEmpty( goods)){
+        if (org.apache.commons.collections4.CollectionUtils.isEmpty(goods)) {
             return new GoodsInfoResp();
         }
         //组装商品参数
@@ -1316,4 +1423,147 @@ public class KwpGoodsService {
         goodsInfo.setPriceUnit(x.getPriceUnit());
         return goodsInfo;
     }
+
+    public Set<Object> hotKeys() {
+        return redisTemplate.opsForZSet().reverseRange(RedisConstant.HOT_KEY, 0, 9);
+    }
+
+    private List<BuildingMaterialsMarketList> tran(List<KwpGoods> list) {
+        List<BuildingMaterialsMarketList> result = new ArrayList<>(list.size());
+
+        List<Long> goodsIds = new ArrayList<>(list.size());
+        List<Long> supplyEntIds = new ArrayList<>(list.size());
+        list.forEach(e -> {
+            goodsIds.add(e.getId());
+            if (Objects.nonNull(e.getSupplyEntId())) {
+                supplyEntIds.add(e.getSupplyEntId());
+            }
+        });
+
+        //地址信息
+        Map<Long, KwpGoodsAddress> addressMap = kwpGoodsAddressService.getByGoodsIds(goodsIds).stream()
+                .collect(Collectors.toMap(KwpGoodsAddress::getGoodsId, e -> e, (k1, k2) -> k1));
+        //价格梯度信息
+        Map<Long, List<KwpGoodsPriceRange>> priceRangeMap = kwpGoodsPriceRangeService.getByGoodsIds(goodsIds).stream()
+                .collect(Collectors.groupingBy(KwpGoodsPriceRange::getGoodsId));
+        //供应企业信息
+        List<EntCacheResDto> entList = remoteSystemService.queryEntCacheByIds(supplyEntIds);
+        Map<Long, String> entMap = entList.stream().collect(Collectors.toMap(EntCacheResDto::getId, EntCacheResDto::getFirmName, (k1, k2) -> k1));
+        Map<String, Map<String, String>> dict = remoteSystemService.queryDictByType(List.of(DictTypeEnum.PRODUCT_NAME_TYPE.getType(),
+                DictTypeEnum.UNIT_TYPE.getType()));
+        Map<String, String> productNameMap, unitMap;
+        if (CollectionUtils.isNotEmpty(dict)) {
+            productNameMap = dict.get(DictTypeEnum.PRODUCT_NAME_TYPE.getType());
+            unitMap = dict.get(DictTypeEnum.UNIT_TYPE.getType());
+        } else {
+            productNameMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+            unitMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        }
+        Long entId = LoginUserHolder.getEntId();
+
+        list.forEach(e -> {
+            BuildingMaterialsMarketList materials = BeanUtils.copyProperties(e, BuildingMaterialsMarketList.class);
+            KwpGoodsAddress address = addressMap.get(e.getId());
+            List<KwpGoodsPriceRange> priceRanges = priceRangeMap.get(e.getId());
+            materials.setGoodsTypeLabel(CollectionUtils.isNotEmpty(productNameMap) ? productNameMap.get(e.getGoodsType()) : null)
+                    .setUnitLabel(CollectionUtils.isNotEmpty(unitMap) ? unitMap.get(e.getUnit()) : null)
+                    .setAddressName(Objects.isNull(address) ? null : address.getCityName())
+                    .setDetailAddress(Objects.isNull(address) ? null : address.getDetailAddress())
+                    .setPrice(CollectionUtils.isEmpty(priceRanges) ? null : priceRanges.get(0).getPrice())
+                    .setThumb(FileUtils.splice(e.getThumb()))
+                    .setSupplyEnt(entMap.get(e.getSupplyEntId()))
+            ;
+            materials.setSignFlag(false);
+            if (Objects.nonNull(entId)) {
+                TradeContractGoodsDto tradeContractResDto = remoteContractService.queryTradeContractNew(entId, e.getId(),LocalDateTime.now());
+                if (Objects.nonNull(tradeContractResDto)) {
+                    materials.setSignPrice(tradeContractResDto.getPrice());
+                    materials.setSignFlag(true);
+                }
+            }
+            result.add(materials);
+        });
+        return result;
+    }
+
+    public List<BuildingMaterialsMarketList> hootGoods() {
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime start = now.minusDays(30);
+        Map<Long, BigDecimal> res = new HashMap<>();
+        Map<Long, OrderSaleVo> sale = new HashMap<>();
+        Map<Long, GoodsViewLogVo> view = new HashMap<>();
+        //30天销售量 60%
+        List<OrderSaleVo> orderSaleVos = tradeOrderInfoService.queryRecentSale(start, now);
+        //30天浏览量 40%
+        List<GoodsViewLogVo> goodsViewLogVos = kwpGoodsViewLogMapper.searchViewCount(start, now);
+        if (CollUtil.isNotEmpty(goodsViewLogVos)) {
+            view = goodsViewLogVos.stream().collect(Collectors.toMap(GoodsViewLogVo::getGoodsId, e -> e, (a, b) -> a));
+        }
+        if (CollUtil.isNotEmpty(orderSaleVos)) {
+            sale = orderSaleVos.stream().collect(Collectors.toMap(OrderSaleVo::getGoodsId, e -> e, (a, b) -> a));
+        }
+
+        for (Map.Entry<Long, OrderSaleVo> one : sale.entrySet()) {
+            BigDecimal mul = NumberUtil.mul(one.getValue().getAmount(), new BigDecimal("0.6"));
+            GoodsViewLogVo goodsViewLogVo = view.get(one.getKey());
+            BigDecimal mul2 = new BigDecimal("0");
+            if (Objects.nonNull(goodsViewLogVo)) {
+                mul2 = NumberUtil.mul(goodsViewLogVo.getCount(), new BigDecimal("0.4"));
+            }
+            res.put(one.getKey(), NumberUtil.add(mul, mul2));
+        }
+        for (Map.Entry<Long, GoodsViewLogVo> two : view.entrySet()) {
+            BigDecimal bigDecimal = res.get(two.getKey());
+            //校验是否已经计算比重值
+            if (Objects.isNull(bigDecimal)) {
+                BigDecimal mul = NumberUtil.mul(two.getValue().getCount(), new BigDecimal("0.4"));
+                OrderSaleVo orderSaleVo = sale.get(two.getKey());
+                BigDecimal mul2 = new BigDecimal("0");
+                if (Objects.nonNull(orderSaleVo)) {
+                    mul2 = NumberUtil.mul(orderSaleVo.getAmount(), new BigDecimal("0.6"));
+                }
+                res.put(two.getKey(), NumberUtil.add(mul, mul2));
+            }
+        }
+        //所有的商品id
+        Set<Long> longs = res.keySet();
+        if (CollUtil.isEmpty(longs)) { //查询商品信息,按创建时间倒叙取数据
+            List<KwpGoods> kwpGoods = kwpGoodsMapper.selectList(new LambdaQueryWrapper<KwpGoods>()
+                    .eq(KwpGoods::getDelFlag, 0)
+                    .orderByDesc(KwpGoods::getCreateTime).last("limit 10")
+            );
+            if (CollectionUtils.isEmpty(kwpGoods)) {
+                return Collections.emptyList();
+            }
+            return tran(kwpGoods);
+        }
+        if (CollUtil.size(longs) < 10) {//不足10个,查询商品信息,补足10个
+            List<KwpGoods> add = kwpGoodsMapper.selectBatchIds(longs);
+
+            List<KwpGoods> kwpGoods = kwpGoodsMapper.selectList(new LambdaQueryWrapper<KwpGoods>()
+                    .eq(KwpGoods::getDelFlag, 0).orderByDesc(KwpGoods::getCreateTime).last("limit " + (10 - CollUtil.size(longs)))
+            );
+            add.addAll(kwpGoods);
+            return tran(add);
+        }
+        List<KwpGoods> add = kwpGoodsMapper.selectBatchIds(CollUtil.sub(longs, 0, 10));
+
+        return tran(add);
+    }
+
+    public List<Long> selectByPara(String goodsName, List<String> goodsType, String goodsSpec) {
+        LambdaQueryWrapper<KwpGoods> wrapper = new LambdaQueryWrapper<>();
+        wrapper.and(w -> w.like(StringUtils.isNotBlank(goodsName), KwpGoods::getName, goodsName)
+                .or()
+                .in(CollUtil.isNotEmpty(goodsType), KwpGoods::getGoodsType, goodsType)
+                .or()
+                .like(StringUtils.isNotBlank(goodsSpec), KwpGoods::getSpec, goodsSpec));
+        List<KwpGoods> list = kwpGoodsMapper.selectList(wrapper);
+        if (CollUtil.isEmpty(list)) {
+            return Collections.emptyList();
+        }
+        return list.stream().map(KwpGoods::getId).toList();
+    }
+
+
 }

+ 61 - 0
sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpGoodsViewLogService.java

@@ -0,0 +1,61 @@
+package com.sckw.product.service;
+
+import cn.hutool.extra.servlet.JakartaServletUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import com.sckw.core.utils.IdWorker;
+import com.sckw.core.web.context.LoginUserHolder;
+import com.sckw.product.dao.KwpGoodsViewLogMapper;
+import com.sckw.product.model.KwpGoodsViewLog;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author xucaiqin
+ * @date 2025-11-14 09:19:22
+ */
+@Service
+@RequiredArgsConstructor
+public class KwpGoodsViewLogService {
+
+    private final KwpGoodsViewLogMapper kwpGoodsViewLogMapper;
+
+    private String parseUserAgent(HttpServletRequest request) {
+        String userAgentStr = request.getHeader("User-Agent");
+
+        UserAgent userAgent = UserAgentUtil.parse(userAgentStr);
+
+        // 3. 提取关键信息
+        String browser = userAgent.getBrowser().getName(); // 浏览器名称(如 Chrome、Edge)
+        String browserVersion = userAgent.getVersion(); // 浏览器版本
+        String os = userAgent.getOs().getName(); // 操作系统(如 Windows 10、macOS、iOS)
+        String device = userAgent.getPlatform().getName(); // 设备类型(如 PC、iPhone、Android)
+
+        // 输出示例:Chrome 120.0.0.0 / Windows 10 / PC
+        return String.format("%s %s / %s / %s", browser, browserVersion, os, device);
+    }
+
+    public void saveLog(Long id, String name, String goodsType,String keywords, Integer source, HttpServletRequest request) {
+        KwpGoodsViewLog kwpGoodsViewLog = new KwpGoodsViewLog();
+        kwpGoodsViewLog.setId(new IdWorker(1).nextId());
+        kwpGoodsViewLog.setEntId(LoginUserHolder.getEntId());
+        kwpGoodsViewLog.setUserId(LoginUserHolder.getUserId());
+        kwpGoodsViewLog.setSearchWords(keywords);
+        kwpGoodsViewLog.setGoodsId(id);
+        kwpGoodsViewLog.setGoodsName(name);
+        kwpGoodsViewLog.setGoodsType(goodsType);
+        kwpGoodsViewLog.setSource(source);
+        kwpGoodsViewLog.setUserAgent(parseUserAgent(request));
+        kwpGoodsViewLog.setIpAddress(JakartaServletUtil.getClientIP(request));
+        kwpGoodsViewLog.setSearchTime(LocalDateTime.now());
+        kwpGoodsViewLog.setCreateBy(LoginUserHolder.getUserId());
+        kwpGoodsViewLog.setCreateTime(LocalDateTime.now());
+        kwpGoodsViewLog.setDelFlag(0);
+        kwpGoodsViewLogMapper.insert(kwpGoodsViewLog);
+    }
+
+
+}

+ 3 - 7
sckw-modules/sckw-product/src/main/java/com/sckw/product/service/KwpSearchLogService.java

@@ -1,21 +1,17 @@
 package com.sckw.product.service;
 
-import java.time.LocalDateTime;
-
 import cn.hutool.extra.servlet.JakartaServletUtil;
-import cn.hutool.extra.servlet.ServletUtil;
 import cn.hutool.http.useragent.UserAgent;
 import cn.hutool.http.useragent.UserAgentUtil;
 import com.sckw.core.utils.IdWorker;
 import com.sckw.core.web.context.LoginUserHolder;
+import com.sckw.product.dao.KwpSearchLogMapper;
+import com.sckw.product.model.KwpSearchLog;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 
-import javax.annotation.Resource;
-
-import com.sckw.product.model.KwpSearchLog;
-import com.sckw.product.dao.KwpSearchLogMapper;
+import java.time.LocalDateTime;
 
 /**
  * @author xucaiqin

+ 40 - 0
sckw-modules/sckw-product/src/main/resources/mapper/KwpGoodsViewLogMapper.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.sckw.product.dao.KwpGoodsViewLogMapper">
+  <resultMap id="BaseResultMap" type="com.sckw.product.model.KwpGoodsViewLog">
+    <!--@mbg.generated-->
+    <id column="id" jdbcType="BIGINT" property="id" />
+    <result column="ent_id" jdbcType="BIGINT" property="entId" />
+    <result column="user_id" jdbcType="BIGINT" property="userId" />
+    <result column="search_words" jdbcType="VARCHAR" property="searchWords" />
+    <result column="source" jdbcType="INTEGER" property="source" />
+    <result column="user_agent" jdbcType="VARCHAR" property="userAgent" />
+    <result column="ip_address" jdbcType="VARCHAR" property="ipAddress" />
+    <result column="search_time" jdbcType="TIMESTAMP" property="searchTime" />
+    <result column="create_by" jdbcType="BIGINT" property="createBy" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_by" jdbcType="BIGINT" property="updateBy" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+    <result column="del_flag" jdbcType="INTEGER" property="delFlag" />
+  </resultMap>
+
+  <select id="searchViewCount" resultType="com.sckw.product.model.vo.res.GoodsViewLogVo">
+      select kgvl.goods_id,
+             kgvl.goods_name,
+             kgvl.goods_type,
+             count(kgvl.goods_id) as count
+      from kwp_goods_view_log kgvl
+      <where>
+          kgvl.del_flag = 0
+
+          <if test="start != null and end != null">
+              and kgvl.search_time between #{start} and #{end}
+          </if>
+          group by kgvl.goods_id,
+                   kgvl.goods_name,
+                   kgvl.goods_type
+          order by count desc
+          limit 20
+      </where>
+  </select>
+</mapper>