From a6dd4f4611996bbadd122ea2459b233113c34838 Mon Sep 17 00:00:00 2001 From: gong <1157756119@qq.com> Date: Wed, 24 Dec 2025 15:23:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=95=86=E5=93=81=E5=85=B3?= =?UTF-8?q?=E8=81=94=E6=8E=A8=E8=8D=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/user/UProductController.java | 9 +++++ .../java/com/czg/service/ProductTest.java | 36 +++++++++++++++++++ .../java/com/czg/product/dto/ProductDTO.java | 21 +++++++++++ .../czg/product/dto/RelatedProductDTO.java | 31 ++++++++++++++++ .../java/com/czg/product/entity/Product.java | 4 +++ .../czg/product/service/ProductService.java | 1 + .../czg/product/service/UProductService.java | 3 ++ .../main/java/com/czg/utils/AliOcrUtil.java | 26 ++++---------- .../service/product/mapper/ProductMapper.java | 2 +- .../service/impl/ProductServiceImpl.java | 21 ++++++++--- .../service/impl/UProductServiceImpl.java | 29 ++++++++++++++- .../main/resources/mapper/ProductMapper.xml | 6 ++++ 12 files changed, 164 insertions(+), 25 deletions(-) create mode 100644 cash-api/product-server/src/test/java/com/czg/service/ProductTest.java create mode 100644 cash-common/cash-common-service/src/main/java/com/czg/product/dto/RelatedProductDTO.java diff --git a/cash-api/product-server/src/main/java/com/czg/controller/user/UProductController.java b/cash-api/product-server/src/main/java/com/czg/controller/user/UProductController.java index 7ab55611c..6c9f95681 100644 --- a/cash-api/product-server/src/main/java/com/czg/controller/user/UProductController.java +++ b/cash-api/product-server/src/main/java/com/czg/controller/user/UProductController.java @@ -72,6 +72,15 @@ public class UProductController { return CzgResult.success(list); } + /** + * 获取相关推荐商品 + */ + @GetMapping("/related/{id}") + public CzgResult> getRelatedProduct(@PathVariable("id") Long id) { + AssertUtil.isNull(id, "商品id不能为空"); + return CzgResult.success(uProductService.queryProductRelatedList(id)); + } + /** * 小程序点餐-商品详情 * diff --git a/cash-api/product-server/src/test/java/com/czg/service/ProductTest.java b/cash-api/product-server/src/test/java/com/czg/service/ProductTest.java new file mode 100644 index 000000000..56f48ced8 --- /dev/null +++ b/cash-api/product-server/src/test/java/com/czg/service/ProductTest.java @@ -0,0 +1,36 @@ +package com.czg.service; + +import com.alibaba.fastjson2.JSONObject; +import com.czg.product.dto.ProductDTO; +import com.czg.product.service.ProductService; +import com.czg.product.service.UProductService; +import com.czg.product.vo.ShopGroupProductVo; +import com.czg.product.vo.ShopProductVo; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +/** + * @author yjjie + * @date 2025/12/24 13:36 + */ +@SpringBootTest +public class ProductTest { + + @Resource + private ProductService productService; + + @Resource + private UProductService uProductService; + + @Test + public void testGetById() { +// ProductDTO product = productService.getProductById(169L); +// System.out.println(JSONObject.toJSONString( product)); + + List productVos = uProductService.queryProductRelatedList(169L); + System.out.println(JSONObject.toJSONString(productVos)); + } +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProductDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProductDTO.java index bfe0df414..9e638e7a4 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProductDTO.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProductDTO.java @@ -1,12 +1,15 @@ package com.czg.product.dto; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.annotation.JSONField; import com.czg.product.vo.ProductGroupVo; import com.czg.validator.group.DefaultGroup; import com.czg.validator.group.InsertGroup; import com.czg.validator.group.UpdateGroup; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.validation.constraints.*; import lombok.Data; @@ -235,6 +238,12 @@ public class ProductDTO implements Serializable { * 是否可售时间 1-是 0-否 */ private Integer isSaleTime; + /** + * 相关推荐商品 + */ + private List relatedRecommendJson; + @JsonIgnore + private String relatedRecommend; public Object getImages() { return JSON.parseArray(Convert.toStr(images, "[]")); @@ -250,4 +259,16 @@ public class ProductDTO implements Serializable { public Object getGroupSnap() { return JSON.parseArray(Convert.toStr(groupSnap, "[]")); } + + public String getRelatedRecommendStr() { + if (CollUtil.isNotEmpty(relatedRecommendJson)) { + JSONArray array = new JSONArray(); + for (RelatedProductDTO relatedProductDTO : relatedRecommendJson) { + array.add(relatedProductDTO.getId()); + } + return array.toJSONString(); + } + + return "[]"; + } } \ No newline at end of file diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/dto/RelatedProductDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/RelatedProductDTO.java new file mode 100644 index 000000000..b0571551d --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/RelatedProductDTO.java @@ -0,0 +1,31 @@ +package com.czg.product.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 相关推荐 商品 DTO + * + * @author yjjie + * @date 2025/12/24 13:34 + */ +@Data +public class RelatedProductDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 商品 ID + */ + private Long id; + /** + * 商品名称 + */ + private String name; + /** + * 商品图片 + */ + private String coverImg; +} diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/entity/Product.java b/cash-common/cash-common-service/src/main/java/com/czg/product/entity/Product.java index 91fdd421e..0b96a189e 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/product/entity/Product.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/entity/Product.java @@ -142,6 +142,10 @@ public class Product implements Serializable { * 退款是否退回库存 */ private Integer isRefundStock; + /** + * 相关推荐 + */ + private String relatedRecommend; /** * 创建时间 */ 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 54ceaeace..f24801f20 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 @@ -1,6 +1,7 @@ package com.czg.product.service; import com.czg.product.dto.ProductDTO; +import com.czg.product.dto.RelatedProductDTO; import com.czg.product.entity.Product; import com.czg.product.entity.ProductStockFlow; import com.czg.product.param.*; diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/service/UProductService.java b/cash-common/cash-common-service/src/main/java/com/czg/product/service/UProductService.java index 9562eb758..f2687f834 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/product/service/UProductService.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/service/UProductService.java @@ -1,5 +1,6 @@ package com.czg.product.service; +import com.czg.product.dto.RelatedProductDTO; import com.czg.product.entity.Product; import com.czg.product.param.ShopProductSkuParam; import com.czg.product.vo.ShopGroupProductVo; @@ -95,4 +96,6 @@ public interface UProductService extends IService { * @return 是否可售时间 1-是,0-否 */ Integer calcIsSaleTime(String days, LocalTime startTime, LocalTime endTime); + + List queryProductRelatedList(Long productId); } diff --git a/cash-common/cash-common-tools/src/main/java/com/czg/utils/AliOcrUtil.java b/cash-common/cash-common-tools/src/main/java/com/czg/utils/AliOcrUtil.java index 1318afdbe..9c0ad4583 100644 --- a/cash-common/cash-common-tools/src/main/java/com/czg/utils/AliOcrUtil.java +++ b/cash-common/cash-common-tools/src/main/java/com/czg/utils/AliOcrUtil.java @@ -16,9 +16,6 @@ import com.czg.exception.CzgException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import java.io.DataOutputStream; -import java.net.HttpURLConnection; -import java.net.URL; import java.util.List; import java.util.Map; @@ -35,8 +32,8 @@ public class AliOcrUtil { /** * description : *

使用凭据初始化账号Client

- * @return Client * + * @return Client * @throws Exception */ public static com.aliyun.bailian20231229.Client createClient() { @@ -56,7 +53,6 @@ public class AliOcrUtil { } - public static ApplyFileUploadLeaseResponseBody.ApplyFileUploadLeaseResponseBodyData applyFileUpload(byte[] bytes, String fileName) { String md5 = DigestUtil.md5Hex(bytes); @@ -95,7 +91,7 @@ public class AliOcrUtil { .setCategoryId("default") .setCategoryType("SESSION_FILE"); com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions(); - java.util.Map headers = new java.util.HashMap<>(); + java.util.Map headers = new java.util.HashMap<>(); try { // 复制代码运行请自行打印 API 的返回值 AddFileResponse addFileResponse = client.addFileWithOptions("llm-9zg04s7wlbvi32tq", addFileRequest, headers, runtime); @@ -110,7 +106,7 @@ public class AliOcrUtil { } } - public static boolean getFileStatus(String fileId) { + public static boolean getFileStatus(String fileId) { com.aliyun.bailian20231229.Client client = createClient(); com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions(); java.util.Map headers = new java.util.HashMap<>(); @@ -158,18 +154,17 @@ public class AliOcrUtil { } - - public static String appCall(byte[] bytes, String fileName) { - String id = null; + public static String appCall(byte[] bytes, String fileName) { + String id; try { id = getSessionId(bytes, fileName); } catch (Exception e) { throw new RuntimeException(e); } - ApplicationParam param = ApplicationParam.builder() + ApplicationParam param = ApplicationParam.builder() .apiKey("sk-2343af4413834ad1ab43b036e3a903de") .appId("cd612ac509a4499f8ac68a656532d4ae") - .prompt("你是一名票据OCR结构化专家,请从我提供的票据图片中智能提取信息并只输出JSON,不得添加解释、不补充不存在内容、不得返回空字符串、字段缺失填null、字段不可省略、数字一律用字符串,使用以下固定JSON结构:{\"documentType\":\"\",\"orderNumber\":\"\",\"date\":\"\",\"customerName\":\"\",\"operator\":\"\",\"items\":[{\"conName\":\"\",\"spec\":\"\",\"unitName\":\"\",\"inOutNumber\":\"\",\"purchasePrice\":\"\",\"subTotal\":\"\"}],\"totalAmount\":\"\",\"remark\":\"\"}。字段映射规则:documentType对应单据类型/销售单/采购单/出货单;orderNumber对应单号/编号/No;date对应日期/开单日期;customerName对应客户名称/收货单位/供应商;operator对应业务员/经办人/制单人/操作员;items.conName对应品名/名称;items.spec对应规格/型号;items.unitName对应单位;items.inOutNumber对应数量;items.purchasePrice对应单价;items.subTotal对应金额/小计;totalAmount对应总金额/合计金额;remark对应备注。严禁生成图片中不存在的字段内容,看不清或未出现的字段必须为null,不允许推测或补全,不得生成多余字段;items只能根据识别到的行生成,不得虚构。必须能识别旋转、倾斜、模糊、撕裂、光照差异、列顺序混乱、无表格线等情况并尽量恢复信息。最终输出必须是纯JSON,不得包含任何非JSON字符。") + .prompt("你是一名票据OCR结构化专家,请从我提供的票据图片中智能提取信息并只输出JSON,不得添加解释、不补充不存在内容、不得返回空字符串、字段缺失填null、字段不可省略、数字一律用字符串,使用以下固定JSON结构:{\"documentType\":\"\",\"orderNumber\":\"\",\"date\":\"\",\"customerName\":\"\",\"operator\":\"\",\"items\":[{\"conName\":\"\",\"spec\":\"\",\"unitName\":\"\",\"inOutNumber\":\"\",\"purchasePrice\":\"\",\"subTotal\":\"\"}],\"totalAmount\":\"\",\"remark\":\"\"}。字段映射规则:documentType对应单据类型/销售单/采购单/出货单;orderNumber对应单号/编号/No;date对应日期/开单日期;customerName对应客户名称/收货单位/供应商;operator对应业务员/经办人/制单人/操作员;items.conName对应品名/名称;items.spec对应规格/型号;items.unitName对应单位;items.inOutNumber对应数量;items.purchasePrice对应单价;items.subTotal对应金额/小计;totalAmount对应总金额/合计金额;remark对应备注。严禁生成图片中不存在的字段内容,看不清或未出现的字段必须为null,不允许推测或补全,不得生成多余字段;items只能根据识别到的行生成,不得虚构。必须能识别旋转、倾斜、模糊、撕裂、光照差异、列顺序混乱、无表格线等情况并尽量恢复信息。最终输出必须是纯JSON,不得包含任何非JSON字符。") .ragOptions(RagOptions.builder() .sessionFileIds(List.of(id)) .build()) @@ -187,11 +182,4 @@ public class AliOcrUtil { return result.getOutput().getText(); } - public static void main(String[] args) { - - } - - static void main() throws Exception { - } - } diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/mapper/ProductMapper.java b/cash-service/product-service/src/main/java/com/czg/service/product/mapper/ProductMapper.java index edd9e74f0..a17c672ce 100644 --- a/cash-service/product-service/src/main/java/com/czg/service/product/mapper/ProductMapper.java +++ b/cash-service/product-service/src/main/java/com/czg/service/product/mapper/ProductMapper.java @@ -32,7 +32,7 @@ public interface ProductMapper extends BaseMapper { List selectHotsProductList(@Param("shopId") Long shopId); - List selectGroupProductList(@Param("shopId") Long shopId); + List selectGroupProductList(@Param("shopId") Long shopId, @Param("idList") List idList); ShopProductInfoVo selectOneProductInfo(@Param("id") Long id, @Param("shopId") Long shopId); 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 980847ce7..751400229 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 @@ -6,14 +6,12 @@ import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONWriter; import com.czg.constant.CacheConstant; import com.czg.constants.SystemConstants; import com.czg.exception.CzgException; -import com.czg.product.dto.ProdConsRelationDTO; -import com.czg.product.dto.ProdSkuDTO; -import com.czg.product.dto.ProductDTO; -import com.czg.product.dto.ShopProdCategoryDTO; +import com.czg.product.dto.*; import com.czg.product.entity.*; import com.czg.product.enums.*; import com.czg.product.param.*; @@ -310,6 +308,7 @@ public class ProductServiceImpl extends ServiceImpl impl dto.setSkuList(skuList); List consList = prodConsRelationMapper.selectListByProdId(dto.getId()); dto.setConsList(consList); + dto.setRelatedRecommendJson(getRelateProductList(dto.getRelatedRecommend())); return dto; } @@ -336,6 +335,7 @@ public class ProductServiceImpl extends ServiceImpl impl } entity.setIsDel(SystemConstants.OneZero.ZERO); entity.setShopId(shopId); + entity.setRelatedRecommend(dto.getRelatedRecommendStr()); super.save(entity); dto.setId(entity.getId()); // 清除商品分类列表缓存 @@ -404,6 +404,7 @@ public class ProductServiceImpl extends ServiceImpl impl if (!ProductTypeEnum.SKU.value().equals(entity.getType())) { UpdateChain.of(Product.class).set(Product::getSpecId, null).eq(Product::getId, dto.getId()).update(); } + entity.setRelatedRecommend(dto.getRelatedRecommendStr()); super.updateById(entity); // 清除商品分类列表缓存 clearProductCache(old.getCategoryId()); @@ -788,4 +789,16 @@ public class ProductServiceImpl extends ServiceImpl impl String key = StrUtil.format(CacheConstant.SHOP_PRODUCT_STOCK, shopId, productId); redisService.set(key, stockNumber); } + + private List getRelateProductList(String relatedProduct) { + if (StrUtil.isNotBlank(relatedProduct) && !"[]".equals(relatedProduct)) { + List idList = JSONArray.parseArray(relatedProduct, Long.class); + + QueryWrapper wrapper = QueryWrapper.create().select(Product::getId, Product::getName, Product::getCoverImg).eq(Product::getIsDel, SystemConstants.OneZero.ZERO) + .in(Product::getId, idList); + return super.listAs(wrapper, RelatedProductDTO.class); + } + + return new ArrayList<>(); + } } diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/UProductServiceImpl.java b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/UProductServiceImpl.java index f90c08edc..571210bf6 100644 --- a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/UProductServiceImpl.java +++ b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/UProductServiceImpl.java @@ -6,6 +6,7 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSONArray; import com.czg.constant.CacheConstant; import com.czg.constants.SystemConstants; import com.czg.exception.CzgException; @@ -27,6 +28,7 @@ import com.czg.service.product.mapper.ProdGroupMapper; import com.czg.service.product.mapper.ProdGroupRelationMapper; import com.czg.service.product.mapper.ProdSkuMapper; import com.czg.service.product.mapper.ProductMapper; +import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.spring.service.impl.ServiceImpl; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -78,7 +80,7 @@ public class UProductServiceImpl extends ServiceImpl imp query().select(ProdGroup::getId, ProdGroup::getName, ProdGroup::getSortMode, ProdGroup::getUseTime, ProdGroup::getSaleStartTime, ProdGroup::getSaleEndTime) .eq(ProdGroup::getShopId, shopId).eq(ProdGroup::getStatus,SystemConstants.OneZero.ONE) .orderBy(ProdGroup::getSort, true), ShopGroupProductVo.class); - List productAllList = productMapper.selectGroupProductList(shopId); + List productAllList = productMapper.selectGroupProductList(shopId, null); productAllList.forEach(item -> { List skuList = prodSkuMapper.selectListByQueryAs(query() .eq(ProdSku::getProductId, item.getId()) @@ -233,6 +235,31 @@ public class UProductServiceImpl extends ServiceImpl imp return SystemConstants.OneZero.ZERO; } + @Override + public List queryProductRelatedList(Long productId) { + Product product = getOne(QueryWrapper.create().eq(Product::getId, productId) + .eq(Product::getShopId, StpKit.USER.getShopId()) + .eq(Product::getIsDel, SystemConstants.OneZero.ZERO)); + + if (product == null) { + throw new CzgException("商品信息不存在"); + } + + if (StrUtil.isBlank(product.getRelatedRecommend()) || "[]".equals(product.getRelatedRecommend())) { + return new ArrayList<>(); + } + + List productAllList = productMapper.selectGroupProductList(product.getShopId(), JSONArray.parseArray(product.getRelatedRecommend(), Long.class)); + productAllList.forEach(item -> { + List skuList = prodSkuMapper.selectListByQueryAs(query() + .eq(ProdSku::getProductId, item.getId()) + .eq(ProdSku::getIsGrounding, SystemConstants.OneZero.ONE) + .eq(ProdSku::getIsDel, SystemConstants.OneZero.ZERO), ProdSkuDTO.class); + item.setSkuList(skuList); + }); + return productAllList; + } + /** * 计算是否在可售时间内 * diff --git a/cash-service/product-service/src/main/resources/mapper/ProductMapper.xml b/cash-service/product-service/src/main/resources/mapper/ProductMapper.xml index 67dceac71..da58d37db 100644 --- a/cash-service/product-service/src/main/resources/mapper/ProductMapper.xml +++ b/cash-service/product-service/src/main/resources/mapper/ProductMapper.xml @@ -147,6 +147,12 @@ and t2.sale_price is not null and t1.shop_id = #{shopId} + + and t1.id in + + #{item} + + order by t1.sort desc,t1.id desc