diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/StoreProductController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/StoreProductController.java index 1e9066952..1aadff21f 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/StoreProductController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/StoreProductController.java @@ -22,6 +22,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.List; /** @@ -84,33 +85,33 @@ public class StoreProductController extends XktBaseController { /** * 新增档口商品 */ - @PreAuthorize("@ss.hasPermi('system:product:add')") +// @PreAuthorize("@ss.hasPermi('system:product:add')") @Log(title = "档口商品", businessType = BusinessType.INSERT) @ApiOperation(value = "新增档口商品", httpMethod = "POST", response = R.class) @PostMapping - public R add(@Validated @RequestBody StoreProdVO storeProdVO) { + public R add(@Validated @RequestBody StoreProdVO storeProdVO) throws IOException { return R.ok(storeProdService.insertStoreProduct(BeanUtil.toBean(storeProdVO, StoreProdDTO.class))); } /** * 修改档口商品 */ - @PreAuthorize("@ss.hasPermi('system:product:edit')") +// @PreAuthorize("@ss.hasPermi('system:product:edit')") @ApiOperation(value = "修改档口商品", httpMethod = "PUT", response = R.class) @Log(title = "档口商品", businessType = BusinessType.UPDATE) @PutMapping("/{storeProdId}") - public R edit(@PathVariable Long storeProdId, @Validated @RequestBody StoreProdVO storeProdVO) { + public R edit(@PathVariable Long storeProdId, @Validated @RequestBody StoreProdVO storeProdVO) throws IOException { return R.ok(storeProdService.updateStoreProduct(storeProdId, BeanUtil.toBean(storeProdVO, StoreProdDTO.class))); } /** * 修改档口商品状态 */ - @PreAuthorize("@ss.hasPermi('system:product:edit')") +// @PreAuthorize("@ss.hasPermi('system:product:edit')") @Log(title = "修改档口商品状态", businessType = BusinessType.UPDATE) @ApiOperation(value = "修改档口商品状态", httpMethod = "PUT", response = R.class) @PutMapping("/prod-status") - public R editProdStatus(@Validated @RequestBody StoreProdStatusVO prodStatusVO) { + public R editProdStatus(@Validated @RequestBody StoreProdStatusVO prodStatusVO) throws IOException { storeProdService.updateStoreProductStatus(BeanUtil.toBean(prodStatusVO, StoreProdStatusDTO.class)); return R.ok(); } @@ -118,7 +119,7 @@ public class StoreProductController extends XktBaseController { /** * 获取档口图片空间 */ - @PreAuthorize("@ss.hasPermi('system:product:query')") +// @PreAuthorize("@ss.hasPermi('system:product:query')") @ApiOperation(value = "获取档口图片空间", httpMethod = "GET", response = R.class) @GetMapping(value = "/pic-space/{storeId}") public R getStoreProductPicSpace(@PathVariable("storeId") Long storeId) { @@ -129,7 +130,7 @@ public class StoreProductController extends XktBaseController { /** * 导出档口商品列表 */ - @PreAuthorize("@ss.hasPermi('system:product:export')") +// @PreAuthorize("@ss.hasPermi('system:product:export')") @Log(title = "档口商品", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, StoreProduct storeProduct) { diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdStatusVO.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdStatusVO.java index af35b0eae..1183f8abb 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdStatusVO.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdStatusVO.java @@ -20,7 +20,7 @@ public class StoreProdStatusVO { @NotNull(message = "档口商品ID不能为空!") @ApiModelProperty(value = "档口商品ID", required = true) private List storeProdIdList; - @NotBlank(message = "档口商品状态不能为空!") + @NotNull(message = "档口商品状态不能为空!") @ApiModelProperty(value = "档口商品状态", required = true) private Integer prodStatus; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdVO.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdVO.java index 686c040fc..c66e33e57 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdVO.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/storeProd/StoreProdVO.java @@ -30,9 +30,15 @@ public class StoreProdVO { @ApiModelProperty(value = "档口ID", required = true) @NotNull(message = "档口ID不能为空!") private Long storeId; + @ApiModelProperty(value = "档口名称") + @NotBlank(message = "档口名称不能为空!") + private String storeName; @ApiModelProperty(value = "商品分类ID", required = true) @NotNull(message = "商品分类ID不能为空!") private Long prodCateId; + @ApiModelProperty(value = "商品分类名称") + @NotBlank(message = "商品分类名称不能为空!") + private String prodCateName; @ApiModelProperty(value = "工厂货号") @Size(min = 0, max = 15, message = "工厂货号不能超过60个字!") private String factoryArtNum; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index e53fb216a..5b0df3987 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -211,6 +211,8 @@ public class Constants public static final String EXPRESS_REGION_LIST_CACHE_KEY = "EXPRESS_REGION_LIST"; public static final String EXPRESS_REGION_TREE_CACHE_KEY = "EXPRESS_REGION_TREE"; + // ES 索引 product_info + public static final String ES_IDX_PRODUCT_INFO = "product_info"; diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/DailySaleController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/DailySaleController.java index 356fc7355..28e887cdb 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/DailySaleController.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/DailySaleController.java @@ -9,6 +9,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.io.IOException; + /** * 调度任务信息操作处理 * @@ -52,7 +54,7 @@ public class DailySaleController extends BaseController { } @PostMapping("/prod-tag") - public R dailyProdTag(SysJob sysJob) { + public R dailyProdTag(SysJob sysJob) throws IOException { task.dailyProdTag(); return R.ok(); } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/XktTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/XktTask.java index 08fb18acc..083ab3085 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/XktTask.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/XktTask.java @@ -1,11 +1,14 @@ package com.ruoyi.quartz.task; import cn.hutool.core.bean.BeanUtil; +import co.elastic.clients.elasticsearch.core.BulkResponse; +import co.elastic.clients.elasticsearch.core.bulk.BulkOperation; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.entity.SysProductCategory; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.framework.es.EsClientWrapper; import com.ruoyi.quartz.domain.*; import com.ruoyi.quartz.dto.DailySaleCusDTO; import com.ruoyi.quartz.dto.DailySaleDTO; @@ -19,11 +22,11 @@ import com.ruoyi.xkt.enums.*; import com.ruoyi.xkt.mapper.*; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.time.LocalDate; import java.time.ZoneId; import java.util.*; @@ -56,6 +59,7 @@ public class XktTask { final DailyProdTagMapper dailyProdTagMapper; final StoreProductCategoryAttributeMapper cateAttrMapper; final StoreProductStatisticsMapper prodStatMapper; + final EsClientWrapper esClientWrapper; /** * 每晚1点同步档口销售数据 @@ -211,7 +215,7 @@ public class XktTask { * 给商品打标 */ @Transactional - public void dailyProdTag() { + public void dailyProdTag() throws IOException { // 先删除所有的商品标签,保证数据唯一性 List existList = this.dailyProdTagMapper.selectList(new LambdaQueryWrapper() .eq(DailyProdTag::getDelFlag, Constants.UNDELETED)); @@ -241,12 +245,15 @@ public class XktTask { return; } this.dailyProdTagMapper.insert(tagList); + // 更新商品的标签到ES + this.updateESTags(tagList); } /** * 每日更新档口商品的各项权重数据 */ + @Transactional public void dailyProdWeight() { List storeProdList = this.storeProdMapper.selectList(new LambdaQueryWrapper() .eq(StoreProduct::getDelFlag, Constants.UNDELETED)); @@ -309,10 +316,10 @@ public class XktTask { /** * 给商品打标 七日上新 * - * @param yesterday 昨天 - * @param fourDaysAgo 4天前 - * @param oneWeekAgo 一周前 - * @param tagList 标签列表 + * @param yesterday 昨天 + * @param fourDaysAgo 4天前 + * @param oneWeekAgo 一周前 + * @param tagList 标签列表 */ private void tagSevenDayNew(Date yesterday, Date fourDaysAgo, Date oneWeekAgo, List tagList) { List storeProdList = this.storeProdMapper.selectList(new LambdaQueryWrapper() @@ -545,6 +552,31 @@ public class XktTask { } } + /** + * 更新商品的标签到ES + * + * @param tagList 标签集合 + * @throws IOException + */ + private void updateESTags(List tagList) throws IOException { + // 构建一个批量数据集合 + List list = new ArrayList<>(); + tagList.stream().collect(Collectors.groupingBy(DailyProdTag::getStoreProdId)) + .forEach((storeProdId, tags) -> { + // 构建部分文档更新请求 + list.add(new BulkOperation.Builder().update(u -> u + .action(a -> a.doc(new HashMap() {{ + put("tags", tags.stream().map(DailyProdTag::getTag).collect(Collectors.toList())); + }})) + .id(String.valueOf(storeProdId)) + .index(Constants.ES_IDX_PRODUCT_INFO)) + .build()); + }); + // 调用bulk方法执行批量更新操作 + BulkResponse bulkResponse = esClientWrapper.getEsClient().bulk(e -> e.index(Constants.ES_IDX_PRODUCT_INFO).operations(list)); + System.out.println("bulkResponse.items() = " + bulkResponse.items()); + } + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysProductCategoryMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysProductCategoryMapper.java index b58766ad4..a18ef3d7d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysProductCategoryMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysProductCategoryMapper.java @@ -2,6 +2,7 @@ package com.ruoyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.common.core.domain.entity.SysProductCategory; +import com.ruoyi.system.domain.dto.productCategory.ProdCateDTO; /** * 商品分类 数据层 @@ -10,4 +11,12 @@ import com.ruoyi.common.core.domain.entity.SysProductCategory; */ public interface SysProductCategoryMapper extends BaseMapper { + /** + * 获取当前分类的父类 + * + * @param prodCateId 当前分类 + * @return ProdCateDTO + */ + ProdCateDTO getParentCate(Long prodCateId); + } diff --git a/ruoyi-system/src/main/resources/mapper/system/SysProductCategoryMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysProductCategoryMapper.xml new file mode 100644 index 000000000..4f2bf74ad --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysProductCategoryMapper.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/xkt/src/main/java/com/ruoyi/xkt/dto/es/ESProductDTO.java b/xkt/src/main/java/com/ruoyi/xkt/dto/es/ESProductDTO.java index ce2d0bad3..48d3bebaa 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/dto/es/ESProductDTO.java +++ b/xkt/src/main/java/com/ruoyi/xkt/dto/es/ESProductDTO.java @@ -3,6 +3,7 @@ package com.ruoyi.xkt.dto.es; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.experimental.Accessors; import java.util.List; @@ -13,6 +14,7 @@ import java.util.List; */ @ApiModel("ES返回商品数据") @Data +@Accessors(chain = true) public class ESProductDTO { @ApiModelProperty(value = "货号") @@ -40,7 +42,7 @@ public class ESProductDTO { @ApiModelProperty(value = "适合季节") private String season; @ApiModelProperty(value = "商品状态") - private String status; + private String prodStatus; @ApiModelProperty(value = "档口ID") private String storeId; @ApiModelProperty(value = "档口名称") diff --git a/xkt/src/main/java/com/ruoyi/xkt/dto/storeProduct/ProductESDTO.java b/xkt/src/main/java/com/ruoyi/xkt/dto/storeProduct/ProductESDTO.java new file mode 100644 index 000000000..ae1866354 --- /dev/null +++ b/xkt/src/main/java/com/ruoyi/xkt/dto/storeProduct/ProductESDTO.java @@ -0,0 +1,59 @@ +package com.ruoyi.xkt.dto.storeProduct; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * @author liujiang + * @version v1.0 + * @date 2025/3/27 15:12 + */ +@ApiModel("ES返回商品数据") +@Data +@Accessors(chain = true) +public class ProductESDTO { + + @ApiModelProperty(value = "档口商品ID") + private Long id; + @ApiModelProperty(value = "货号") + private String prodArtNum; + @ApiModelProperty(value = "档口商品分类ID") + private String prodCateId; + @ApiModelProperty(value = "档口商品分类名称") + private String prodCateName; + @ApiModelProperty(value = "销量权重") + private String saleWeight; + @ApiModelProperty(value = "推荐权重") + private String recommendWeight; + @ApiModelProperty(value = "人气权重") + private String popularityWeight; + @ApiModelProperty(value = "创建时间") + private String createTime; + @ApiModelProperty(value = "主图") + private String mainPic; + @ApiModelProperty(value = "上级分类名称") + private String parCateName; + @ApiModelProperty(value = "上级分类ID") + private String parCateId; + @ApiModelProperty(value = "单价") + private String prodPrice; + @ApiModelProperty(value = "适合季节") + private String season; + @ApiModelProperty(value = "商品状态") + private String prodStatus; + @ApiModelProperty(value = "档口ID") + private String storeId; + @ApiModelProperty(value = "档口名称") + private String storeName; + @ApiModelProperty(value = "风格") + private String style; + @ApiModelProperty(value = "标签") + private List tags; + @ApiModelProperty(value = "标题") + private String prodTitle; + +} diff --git a/xkt/src/main/java/com/ruoyi/xkt/dto/storeProduct/StoreProdDTO.java b/xkt/src/main/java/com/ruoyi/xkt/dto/storeProduct/StoreProdDTO.java index a2fd25e17..6c126fb10 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/dto/storeProduct/StoreProdDTO.java +++ b/xkt/src/main/java/com/ruoyi/xkt/dto/storeProduct/StoreProdDTO.java @@ -15,6 +15,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.Date; @@ -34,8 +35,12 @@ public class StoreProdDTO { private String prodName; @ApiModelProperty(value = "档口ID") private Long storeId; + @ApiModelProperty(value = "档口名称") + private String storeName; @ApiModelProperty(value = "商品分类ID") private Long prodCateId; + @ApiModelProperty(value = "商品分类名称") + private String prodCateName; @ApiModelProperty(value = "工厂货号") private String factoryArtNum; @ApiModelProperty(value = "商品货号") diff --git a/xkt/src/main/java/com/ruoyi/xkt/mapper/StoreProductMapper.java b/xkt/src/main/java/com/ruoyi/xkt/mapper/StoreProductMapper.java index 05f2d9c65..fbf8cb0fd 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/mapper/StoreProductMapper.java +++ b/xkt/src/main/java/com/ruoyi/xkt/mapper/StoreProductMapper.java @@ -3,6 +3,7 @@ package com.ruoyi.xkt.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.xkt.domain.StoreProduct; import com.ruoyi.xkt.dto.storeHomepage.StoreHomeResDTO; +import com.ruoyi.xkt.dto.storeProduct.ProductESDTO; import com.ruoyi.xkt.dto.storeProduct.StoreProdFuzzyResPicDTO; import com.ruoyi.xkt.dto.storeProduct.StoreProdStatusCountDTO; import org.apache.ibatis.annotations.Param; @@ -81,4 +82,12 @@ public interface StoreProductMapper extends BaseMapper { * @return StoreProdCountDTO */ StoreProdStatusCountDTO selectStatusCount(Long storeId); + + /** + * 档口商品ID列表 + * @param idList id列表 + * @return List + */ + List selectESDTOList(@Param("idList") List idList); + } diff --git a/xkt/src/main/java/com/ruoyi/xkt/service/IStoreProductService.java b/xkt/src/main/java/com/ruoyi/xkt/service/IStoreProductService.java index f510219ce..6da97933b 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/service/IStoreProductService.java +++ b/xkt/src/main/java/com/ruoyi/xkt/service/IStoreProductService.java @@ -4,6 +4,7 @@ import com.ruoyi.common.core.page.Page; import com.ruoyi.xkt.domain.StoreProduct; import com.ruoyi.xkt.dto.storeProduct.*; +import java.io.IOException; import java.util.List; /** @@ -46,7 +47,7 @@ public interface IStoreProductService { * @param storeProdDTO 档口商品 * @return 结果 */ - int insertStoreProduct(StoreProdDTO storeProdDTO); + int insertStoreProduct(StoreProdDTO storeProdDTO) throws IOException; /** * 修改档口商品 @@ -54,14 +55,14 @@ public interface IStoreProductService { * @param storeProdDTO 档口商品 * @return 结果 */ - public int updateStoreProduct(Long storeProdId, StoreProdDTO storeProdDTO); + public int updateStoreProduct(Long storeProdId, StoreProdDTO storeProdDTO) throws IOException; /** * 更新档口商品状态 * * @param prodStatusDTO 更新状态入参 */ - public void updateStoreProductStatus(StoreProdStatusDTO prodStatusDTO); + public void updateStoreProductStatus(StoreProdStatusDTO prodStatusDTO) throws IOException; /** * 批量删除档口商品 diff --git a/xkt/src/main/java/com/ruoyi/xkt/service/impl/IndexSearchServiceImpl.java b/xkt/src/main/java/com/ruoyi/xkt/service/impl/IndexSearchServiceImpl.java index aa2299801..654284eec 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/service/impl/IndexSearchServiceImpl.java +++ b/xkt/src/main/java/com/ruoyi/xkt/service/impl/IndexSearchServiceImpl.java @@ -6,6 +6,7 @@ import co.elastic.clients.elasticsearch._types.query_dsl.*; import co.elastic.clients.elasticsearch.core.SearchResponse; import co.elastic.clients.elasticsearch.core.search.Hit; import com.github.pagehelper.PageInfo; +import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.page.Page; import com.ruoyi.framework.es.EsClientWrapper; import com.ruoyi.xkt.dto.es.ESProductDTO; @@ -42,7 +43,6 @@ public class IndexSearchServiceImpl implements IIndexSearchService { @Transactional(readOnly = true) public Page search(IndexSearchDTO searchDTO) throws IOException { - String indexName = "product_info"; /*// 查询索引 GetIndexResponse res = esClientWrapper.getEsClient().indices().get(request -> request.index(indexName)); System.err.println(res); @@ -55,70 +55,8 @@ public class IndexSearchServiceImpl implements IIndexSearchService { System.err.println(response);*/ // 分页查询 - // 如果有搜索词,则要分词 - if (StringUtils.isNotBlank(searchDTO.getSearch())) { - } - - - // 多个query 根据入参是否为空 分别赋值,有multi_match , terms , range - - - /*SearchResponse list = esClientWrapper.getEsClient().search(s -> s - .index(indexName) - .query(q -> q - .bool(b -> b - .must(m -> m - .multiMatch(mm -> mm - .query(searchDTO.getSearch()) - .fields("prodTitle", "prodArtNum", "storeName")) - ) - .filter(f -> f - .terms(t -> t - .field("prodCateId") - .terms(new TermsQueryField.Builder() - .value(searchDTO.getProdCateIdList().stream() - .map(IndexSearchServiceImpl::newFieldValue) - .collect(Collectors.toList())) - .build() - )) - ) - .filter(f -> f - .terms(t -> t - .field("parCateId") - .terms(new TermsQueryField.Builder() - .value(searchDTO.getParCateIdList().stream() - .map(IndexSearchServiceImpl::newFieldValue) - .collect(Collectors.toList())) - .build()))) - .filter(f -> f - .terms(t -> t - .field("style.keyword") - .terms(new TermsQueryField.Builder() - .value(searchDTO.getStyleList().stream() - .map(IndexSearchServiceImpl::newFieldValue) - .collect(Collectors.toList())) - .build()))) - .filter(f -> f - .terms(t -> t - .field("season.keyword") - .terms(new TermsQueryField.Builder() - .value(searchDTO.getSeasonList().stream() - .map(IndexSearchServiceImpl::newFieldValue) - .collect(Collectors.toList())) - .build()))) - ) - ) - .from(0) - .size(10) - .sort(sort -> sort.field(f -> f.field("recommendWeight").order(SortOrder.Desc))) - , - ESProductInfo.class - );*/ - - // 构建 bool 查询 BoolQuery.Builder boolQuery = new BoolQuery.Builder(); - // 添加 price 范围查询 if (searchDTO.getMinPrice() != null && searchDTO.getMaxPrice() != null) { RangeQuery.Builder builder = new RangeQuery.Builder(); @@ -173,7 +111,7 @@ public class IndexSearchServiceImpl implements IIndexSearchService { // 构建最终的查询 Query query = new Query.Builder().bool(boolQuery.build()).build(); // 执行搜索 - SearchResponse resList = esClientWrapper.getEsClient().search(s -> s.index(indexName) + SearchResponse resList = esClientWrapper.getEsClient().search(s -> s.index(Constants.ES_IDX_PRODUCT_INFO) .query(query).from(searchDTO.getPageNum() - 1).size(searchDTO.getPageSize()) .sort(sort -> sort.field(f -> f.field(searchDTO.getSort()).order(SortOrder.Desc))), ESProductDTO.class); diff --git a/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreProductServiceImpl.java b/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreProductServiceImpl.java index f22f8f28a..7ee9b72be 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreProductServiceImpl.java +++ b/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreProductServiceImpl.java @@ -1,6 +1,10 @@ package com.ruoyi.xkt.service.impl; import cn.hutool.core.bean.BeanUtil; +import co.elastic.clients.elasticsearch.core.BulkResponse; +import co.elastic.clients.elasticsearch.core.CreateResponse; +import co.elastic.clients.elasticsearch.core.UpdateResponse; +import co.elastic.clients.elasticsearch.core.bulk.BulkOperation; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; @@ -9,7 +13,11 @@ import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.page.Page; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.framework.es.EsClientWrapper; +import com.ruoyi.system.domain.dto.productCategory.ProdCateDTO; +import com.ruoyi.system.mapper.SysProductCategoryMapper; import com.ruoyi.xkt.domain.*; +import com.ruoyi.xkt.dto.es.ESProductDTO; import com.ruoyi.xkt.dto.storeColor.StoreColorDTO; import com.ruoyi.xkt.dto.storeProdCateAttr.StoreProdCateAttrDTO; import com.ruoyi.xkt.dto.storeProdColor.StoreProdColorDTO; @@ -22,6 +30,7 @@ import com.ruoyi.xkt.dto.storeProduct.*; import com.ruoyi.xkt.dto.storeProductFile.StoreProdFileDTO; import com.ruoyi.xkt.dto.storeProductFile.StoreProdFileResDTO; import com.ruoyi.xkt.dto.storeProductFile.StoreProdMainPicDTO; +import com.ruoyi.xkt.enums.EProductStatus; import com.ruoyi.xkt.enums.FileType; import com.ruoyi.xkt.enums.ProductSizeStatus; import com.ruoyi.xkt.mapper.*; @@ -33,6 +42,8 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; +import java.math.BigDecimal; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -59,6 +70,9 @@ public class StoreProductServiceImpl implements IStoreProductService { final StoreProductColorSizeMapper storeProdColorSizeMapper; final StoreProductProcessMapper storeProdProcMapper; final StoreMapper storeMapper; + final EsClientWrapper esClientWrapper; + final SysProductCategoryMapper prodCateMapper; + /** * 查询档口商品 @@ -157,9 +171,10 @@ public class StoreProductServiceImpl implements IStoreProductService { @Override @Transactional - public int insertStoreProduct(StoreProdDTO storeProdDTO) { + public int insertStoreProduct(StoreProdDTO storeProdDTO) throws IOException { // 组装StoreProduct数据 - StoreProduct storeProd = BeanUtil.toBean(storeProdDTO, StoreProduct.class).setVoucherDate(DateUtils.getNowDate()); + StoreProduct storeProd = BeanUtil.toBean(storeProdDTO, StoreProduct.class).setVoucherDate(DateUtils.getNowDate()) + .setRecommendWeight(0L).setSaleWeight(0L).setPopularityWeight(0L); int count = this.storeProdMapper.insert(storeProd); // 处理编辑档口商品颜色 this.handleStoreProdColorList(storeProdDTO.getColorList(), storeProd.getId(), storeProd.getStoreId(), Boolean.TRUE); @@ -167,6 +182,8 @@ public class StoreProductServiceImpl implements IStoreProductService { this.handleStoreProdColorSizeList(storeProdDTO.getSizeList(), storeProd.getId(), Boolean.TRUE); // 处理StoreProduct其它属性 this.handleStoreProdProperties(storeProd, storeProdDTO); + // 向ES索引: product_info 创建文档 + this.createESDoc(storeProd, storeProdDTO); return count; } @@ -179,7 +196,7 @@ public class StoreProductServiceImpl implements IStoreProductService { */ @Override @Transactional - public int updateStoreProduct(final Long storeProdId, StoreProdDTO storeProdDTO) { + public int updateStoreProduct(final Long storeProdId, StoreProdDTO storeProdDTO) throws IOException { StoreProduct storeProd = Optional.ofNullable(this.storeProdMapper.selectOne(new LambdaQueryWrapper() .eq(StoreProduct::getId, storeProdId).eq(StoreProduct::getDelFlag, Constants.UNDELETED))) .orElseThrow(() -> new ServiceException("档口商品不存在!", HttpStatus.ERROR)); @@ -204,9 +221,12 @@ public class StoreProductServiceImpl implements IStoreProductService { this.handleStoreProdColorList(storeProdDTO.getColorList(), storeProdId, storeProd.getStoreId(), Boolean.FALSE); // 处理编辑档口商品颜色尺码 this.handleStoreProdColorSizeList(storeProdDTO.getSizeList(), storeProdId, Boolean.FALSE); + // 更新索引: product_info 的文档 + this.updateESDoc(storeProd, storeProdDTO); return count; } + /** * 处理店铺商品颜色尺码列表 * @@ -325,19 +345,36 @@ public class StoreProductServiceImpl implements IStoreProductService { */ @Override @Transactional - public void updateStoreProductStatus(StoreProdStatusDTO prodStatusDTO) { + public void updateStoreProductStatus(StoreProdStatusDTO prodStatusDTO) throws IOException { // 根据商品ID列表查询数据库中的商品信息 List storeProdList = this.storeProdMapper.selectByIds(prodStatusDTO.getStoreProdIdList()); // 检查查询结果是否为空,如果为空,则抛出异常 if (CollectionUtils.isEmpty(storeProdList)) { throw new ServiceException("档口商品不存在!", HttpStatus.ERROR); } + List reSaleList = new ArrayList<>(); // 遍历查询到的商品列表,设置新的商品状态 - storeProdList.forEach(x -> x.setProdStatus(prodStatusDTO.getProdStatus())); + storeProdList.forEach(x -> { + // 已下架上的商品重新上架 + if (Objects.equals(x.getProdStatus(), EProductStatus.OFF_SALE.getValue()) + && Objects.equals(prodStatusDTO.getProdStatus(), EProductStatus.ON_SALE.getValue())) { + reSaleList.add(x); + } + x.setProdStatus(prodStatusDTO.getProdStatus()); + }); // 保存更新后的商品列表到数据库 this.storeProdMapper.updateById(storeProdList); + // 筛选商品状态为已下架,则删除ES文档 + if (Objects.equals(prodStatusDTO.getProdStatus(), EProductStatus.OFF_SALE.getValue())) { + this.deleteESDoc(prodStatusDTO.getStoreProdIdList()); + } + // 已下架的商品重新上架 + if (CollectionUtils.isNotEmpty(reSaleList)) { + this.reSaleCreateESDoc(reSaleList); + } } + /** * 批量删除档口商品 * @@ -478,4 +515,105 @@ public class StoreProductServiceImpl implements IStoreProductService { } } + /** + * 向ES索引新增文档 + * + * @param storeProd 档口产品 + * @param storeProdDTO 档口产品新增入参 + * @throws IOException + */ + private void createESDoc(StoreProduct storeProd, StoreProdDTO storeProdDTO) throws IOException { + ESProductDTO esProductDTO = this.getESDTO(storeProd, storeProdDTO); + // 向索引中添加数据 + CreateResponse createResponse = esClientWrapper.getEsClient().create(e -> e.index(Constants.ES_IDX_PRODUCT_INFO) + .id(storeProd.getId().toString()).document(esProductDTO)); + System.out.println("createResponse.result() = " + createResponse.result()); + } + + /** + * 向ES索引更新文档 + * + * @param storeProd 档口商品 + * @param storeProdDTO 档口商品更新入参 + * @throws IOException + */ + private void updateESDoc(StoreProduct storeProd, StoreProdDTO storeProdDTO) throws IOException { + ESProductDTO esProductDTO = this.getESDTO(storeProd, storeProdDTO); + UpdateResponse updateResponse = esClientWrapper.getEsClient().update(u -> u + .index(Constants.ES_IDX_PRODUCT_INFO).doc(esProductDTO).id(storeProd.getId().toString()), ESProductDTO.class); + System.out.println("deleteResponse.result() = " + updateResponse.result()); + } + + /** + * 组装ES 入参 DTO + * + * @param storeProd 档口商品 + * @param storeProdDTO 档口商品更新入参 + * @return + */ + private ESProductDTO getESDTO(StoreProduct storeProd, StoreProdDTO storeProdDTO) { + // 获取第一张主图 + String firstMainPic = storeProdDTO.getFileList().stream().filter(x -> Objects.equals(x.getFileType(), FileType.MAIN_PIC.getValue())) + .min(Comparator.comparing(StoreProdFileDTO::getOrderNum)).map(StoreProdFileDTO::getFileUrl) + .orElseThrow(() -> new ServiceException("商品主图不存在!", HttpStatus.ERROR)); + // 获取上一级分类的分类ID 及 分类名称 + ProdCateDTO parCate = this.prodCateMapper.getParentCate(storeProdDTO.getProdCateId()); + // 获取当前商品的最低价格 + BigDecimal minPrice = storeProdDTO.getPriceList().stream().min(Comparator.comparing(StoreProdColorPriceDTO::getPrice)) + .map(StoreProdColorPriceDTO::getPrice).orElseThrow(() -> new ServiceException("商品价格不存在!", HttpStatus.ERROR)); + // 获取使用季节 + String season = storeProdDTO.getCateAttrList().stream().filter(x -> Objects.equals(x.getDictType(), "suitable_season")) + .map(StoreProdCateAttrDTO::getDictValue).findAny().orElse(""); + // 获取风格 + String style = storeProdDTO.getCateAttrList().stream().filter(x -> Objects.equals(x.getDictType(), "style")) + .map(StoreProdCateAttrDTO::getDictValue).findAny().orElse(""); + return BeanUtil.toBean(storeProd, ESProductDTO.class) + .setProdCateName(storeProdDTO.getProdCateName()).setSaleWeight("0").setRecommendWeight("0").setPopularityWeight("0") + .setCreateTime(DateUtils.getTime()).setStoreName(storeProdDTO.getStoreName()).setMainPic(firstMainPic) + .setParCateId(parCate.getProdCateId().toString()).setParCateName(parCate.getName()).setProdPrice(minPrice.toString()) + .setSeason(season).setStyle(style).setTags(Collections.singletonList(style)); + } + + /** + * 删除ES索引文档 + * + * @param storeProdIdList 文档ID列表 + * @throws IOException + */ + private void deleteESDoc(List storeProdIdList) throws IOException { + List list = storeProdIdList.stream().map(x -> new BulkOperation.Builder().delete( + d -> d.id(String.valueOf(x)).index(Constants.ES_IDX_PRODUCT_INFO)).build()).collect(Collectors.toList()); + // 调用bulk方法执行批量插入操作 + BulkResponse bulkResponse = esClientWrapper.getEsClient().bulk(e -> e.index(Constants.ES_IDX_PRODUCT_INFO).operations(list)); + System.out.println("bulkResponse.items() = " + bulkResponse.items()); + } + + /** + * 重新创建ES索引下的文档 + * + * @param reSaleList 重新上架商品列表 + * @throws IOException + */ + private void reSaleCreateESDoc(List reSaleList) throws IOException { + if (CollectionUtils.isEmpty(reSaleList)) { + return; + } + List esDTOList = this.storeProdMapper.selectESDTOList(reSaleList.stream() + .map(StoreProduct::getId).distinct().collect(Collectors.toList())); + Map esDTOMap = esDTOList.stream().collect(Collectors.toMap(ProductESDTO::getId, Function.identity())); + // 构建一个批量数据集合 + List list = reSaleList.stream().map(x -> { + final String createTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, x.getCreateTime()); + final ProductESDTO esDTO = Optional.ofNullable(esDTOMap.get(x.getId())).orElseThrow(() -> new ServiceException("档口商品不存在!", HttpStatus.ERROR)); + ESProductDTO esProductDTO = BeanUtil.toBean(x, ESProductDTO.class).setProdCateName(esDTO.getProdCateName()).setMainPic(esDTO.getMainPic()) + .setParCateId(esDTO.getParCateId()).setParCateName(esDTO.getParCateName()).setProdPrice(esDTO.getProdPrice()).setStoreName(esDTO.getStoreName()) + .setSeason(esDTO.getSeason()).setStyle(esDTO.getStyle()).setCreateTime(createTime); + return new BulkOperation.Builder().create(d -> d.document(esProductDTO).id(String.valueOf(x.getId())) + .index(Constants.ES_IDX_PRODUCT_INFO)).build(); + }).collect(Collectors.toList()); + // 调用bulk方法执行批量插入操作 + BulkResponse bulkResponse = esClientWrapper.getEsClient().bulk(e -> e.index(Constants.ES_IDX_PRODUCT_INFO).operations(list)); + System.out.println("bulkResponse.items() = " + bulkResponse.items()); + } + } diff --git a/xkt/src/main/resources/mapper/StoreProductMapper.xml b/xkt/src/main/resources/mapper/StoreProductMapper.xml index 762c962bb..3933dab45 100644 --- a/xkt/src/main/resources/mapper/StoreProductMapper.xml +++ b/xkt/src/main/resources/mapper/StoreProductMapper.xml @@ -176,5 +176,37 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" del_flag = 0 AND store_id = #{storeId} + + \ No newline at end of file