master:档口推荐列表功能;

pull/1121/head
liujiang 2025-05-30 14:43:25 +08:00
parent e7f333e21d
commit ec869177b3
24 changed files with 458 additions and 37 deletions

View File

@ -16,7 +16,8 @@ import com.ruoyi.web.controller.xkt.vo.advertRound.pc.newProd.*;
import com.ruoyi.web.controller.xkt.vo.advertRound.pc.store.PCStoreMidBannerVO;
import com.ruoyi.web.controller.xkt.vo.advertRound.pc.store.PCStoreTopBannerVO;
import com.ruoyi.web.controller.xkt.vo.advertRound.picSearch.PicSearchAdvertVO;
import com.ruoyi.web.controller.xkt.website.IndexSearchVO;
import com.ruoyi.web.controller.xkt.vo.website.IndexSearchVO;
import com.ruoyi.web.controller.xkt.vo.website.StoreSearchVO;
import com.ruoyi.xkt.dto.advertRound.app.index.APPIndexHotSaleDTO;
import com.ruoyi.xkt.dto.advertRound.app.index.APPIndexNewProdDTO;
import com.ruoyi.xkt.dto.advertRound.app.index.APPIndexPopularSaleDTO;
@ -24,7 +25,9 @@ import com.ruoyi.xkt.dto.advertRound.app.index.APPSearchDTO;
import com.ruoyi.xkt.dto.advertRound.pc.PCSearchDTO;
import com.ruoyi.xkt.dto.advertRound.pc.index.PCIndexRecommendDTO;
import com.ruoyi.xkt.dto.advertRound.pc.newProd.PCNewRecommendDTO;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreRecommendDTO;
import com.ruoyi.xkt.dto.website.IndexSearchDTO;
import com.ruoyi.xkt.dto.website.StoreSearchDTO;
import com.ruoyi.xkt.service.IWebsiteAPPService;
import com.ruoyi.xkt.service.IWebsitePCService;
import io.swagger.annotations.Api;
@ -48,6 +51,16 @@ import java.util.List;
@RequestMapping("/rest/v1/website")
public class WebsiteController extends XktBaseController {
// TODO 查询先按照storeWeight倒排之后再按照各种 权重 进行排序
// TODO 查询先按照storeWeight倒排之后再按照各种 权重 进行排序
// TODO 查询先按照storeWeight倒排之后再按照各种 权重 进行排序
// TODO 查询先按照storeWeight倒排之后再按照各种 权重 进行排序
// TODO 查询先按照storeWeight倒排之后再按照各种 权重 进行排序
final IWebsitePCService websitePCService;
final IWebsiteAPPService websiteAPPService;
@ApiOperation(value = "PC 首页 为你推荐", httpMethod = "POST", response = R.class)
@ -164,6 +177,12 @@ public class WebsiteController extends XktBaseController {
return R.ok(BeanUtil.copyToList(websitePCService.getPcStoreMidBannerList(), PCStoreMidBannerVO.class));
}
@ApiOperation(value = "PC 档口馆 档口列表", httpMethod = "POST", response = R.class)
@PostMapping("/pc/store/recommend")
public R<Page<PCStoreRecommendDTO>> pcStoreRecommendPage(@Validated @RequestBody StoreSearchVO searchVO) {
return R.ok(websitePCService.pcStoreRecommendPage(BeanUtil.toBean(searchVO, StoreSearchDTO.class)));
}
@ApiOperation(value = "以图搜款推广", httpMethod = "GET", response = R.class)
@GetMapping("/pic-search")
public R<List<PicSearchAdvertVO>> getPicSearchList() {

View File

@ -17,9 +17,9 @@ public class BasePageVO {
@NotNull(message = "pageNum不能为空")
@ApiModelProperty(value = "pageNum", required = true)
private int pageNum;
private int pageNum = 1;
@NotNull(message = "pageSize不能为空")
@ApiModelProperty(value = "pageSize", required = true)
private int pageSize;
private int pageSize = 20;
}

View File

@ -31,7 +31,7 @@ public class StoreProdViewVO {
@ApiModelProperty(value = "售价")
private BigDecimal price;
@ApiModelProperty(name = "商品标签列表")
private List<String> prodTagList;
private List<String> tags;
@ApiModelProperty(value = "档口ID")
private Long storeId;
@ApiModelProperty(value = "档口名称")

View File

@ -1,4 +1,4 @@
package com.ruoyi.web.controller.xkt.website;
package com.ruoyi.web.controller.xkt.vo.website;
import co.elastic.clients.elasticsearch._types.SortOrder;
import com.ruoyi.web.controller.xkt.vo.BasePageVO;

View File

@ -0,0 +1,22 @@
package com.ruoyi.web.controller.xkt.vo.website;
import com.ruoyi.web.controller.xkt.vo.BasePageVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author liujiang
* @version v1.0
* @date 2025/3/27 15:12
*/
@EqualsAndHashCode(callSuper = true)
@ApiModel("档口排行榜搜索")
@Data
public class StoreSearchVO extends BasePageVO {
@ApiModelProperty(value = "档口名称")
private String storeName;
}

View File

@ -207,6 +207,14 @@ public class CacheConstants {
/**
*
*/
public static final String TOP_IMG_SEARCH_PRODUCT = "top_img_search_product";
public static final String IMG_SEARCH_PRODUCT_HOT = "img_search_product_hot";
/**
*
*/
public static final String PC_STORE_RECOMMEND_LIST = "pc_store_recommend_list";
/**
* 广
*/
public static final String PC_STORE_RECOMMEND_ADVERT = "pc_store_recommend_advert";
}

View File

@ -251,12 +251,16 @@ public class Constants
/**
* APP 广 广 广广 205广 3 7 11 15 19
*/
public static final Set<Integer> insertPositions = new HashSet<>(Arrays.asList(2, 6, 10, 14, 18));
public static final Set<Integer> APP_INSERT_POSITIONS = new HashSet<>(Arrays.asList(2, 6, 10, 14, 18));
/**
* 广 5 9 13 17 20
*/
public static final Set<Integer> pic_res_insert_positions = new HashSet<>(Arrays.asList(4, 8, 12, 16, 19));
public static final Set<Integer> PIC_SEARCH_INSERT_POSITIONS = new HashSet<>(Arrays.asList(4, 8, 12, 16, 19));
/**
* 广 2 9 18 27 36
*/
public static final Set<Integer> STORE_RECOMMEND_INSERT_POSITIONS = new HashSet<>(Arrays.asList(2, 9, 18, 27, 36));
}

View File

@ -7,6 +7,7 @@ import lombok.Getter;
/**
* 广
*
* @author liujiang
* @date 2025-04-02 23:42
*/
@ -69,6 +70,8 @@ public enum AdType {
PC_STORE_TOP_BANNER(201, "档口馆顶部轮播图", "/url"),
// 档口馆 横幅
PC_STORE_MID_BANNER(202, "档口馆横幅", "/url"),
// 档口馆 推荐档口
PC_STORE_RECOMMEND(203, "档口馆推荐档口", "/url"),
// 首页以图搜款框商品、以图搜款结果商品、点击以图搜款界面
@ -105,7 +108,6 @@ public enum AdType {
APP_USER_CENTER_GUESS_YOU_LIKE(602, "APP我的猜你喜欢", "/url"),
;
private final Integer value;

View File

@ -66,6 +66,12 @@ public class DailyTaskController extends BaseController {
return R.ok();
}
@PostMapping("/store-weight")
public R dailyStoreWeight(SysJob sysJob) {
task.dailyStoreWeight();
return R.ok();
}
@PostMapping("/advert-round")
public R dailyRound(SysJob sysJob) throws ParseException {
task.dailyAdvertRound();
@ -79,7 +85,7 @@ public class DailyTaskController extends BaseController {
}
@PostMapping("/store-redis")
public R saveStoreToRedis(SysJob sysJob) throws ParseException {
public R saveStoreToRedis(SysJob sysJob) {
task.saveStoreToRedis();
return R.ok();
}

View File

@ -19,6 +19,8 @@ import com.ruoyi.framework.es.EsClientWrapper;
import com.ruoyi.framework.notice.fs.FsNotice;
import com.ruoyi.xkt.domain.*;
import com.ruoyi.xkt.dto.account.WithdrawPrepareResult;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreRecommendDTO;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreRecommendTempDTO;
import com.ruoyi.xkt.dto.dailySale.DailySaleCusDTO;
import com.ruoyi.xkt.dto.dailySale.DailySaleDTO;
import com.ruoyi.xkt.dto.dailySale.DailySaleProdDTO;
@ -26,6 +28,7 @@ import com.ruoyi.xkt.dto.dailySale.WeekCateSaleDTO;
import com.ruoyi.xkt.dto.dailyStoreTag.DailyStoreTagDTO;
import com.ruoyi.xkt.dto.order.StoreOrderCancelDTO;
import com.ruoyi.xkt.dto.order.StoreOrderRefund;
import com.ruoyi.xkt.dto.storeProductFile.StoreProdFileLatestFourProdDTO;
import com.ruoyi.xkt.enums.*;
import com.ruoyi.xkt.manager.PaymentManager;
import com.ruoyi.xkt.mapper.*;
@ -89,6 +92,7 @@ public class XktTask {
final IPictureSearchService pictureSearchService;
final PictureSearchMapper pictureSearchMapper;
final StoreProductStatisticsMapper storeProdStatMapper;
final StoreProductFileMapper storeProdFileMapper;
/**
*
@ -315,7 +319,7 @@ public class XktTask {
/**
*
* 1:00
*/
@Transactional
public void dailyProdWeight() {
@ -354,6 +358,55 @@ public class XktTask {
this.storeProdMapper.updateById(storeProdList);
}
/**
* 1:10
*/
@Transactional
public void dailyStoreWeight() {
List<StoreProduct> storeProdList = this.storeProdMapper.selectList(new LambdaQueryWrapper<StoreProduct>().eq(StoreProduct::getDelFlag, Constants.UNDELETED));
if (CollectionUtils.isEmpty(storeProdList)) {
return;
}
// 按照storeId 的维度,对档口权重 按照 recommendWeight 进行汇总,按照降序排列
Map<Long, Long> storeWeightMap = storeProdList.stream().collect(Collectors.groupingBy(StoreProduct::getStoreId,
Collectors.summingLong(x -> ObjectUtils.defaultIfNull(x.getRecommendWeight(), 0L))));
// 筛选每个档口最新的4个商品及主图
List<Store> storeList = this.storeMapper.selectList(new LambdaQueryWrapper<Store>().eq(Store::getDelFlag, Constants.UNDELETED));
Map<Long, Store> storeMap = storeList.stream().collect(Collectors.toMap(Store::getId, Function.identity()));
List<DailyStoreTag> storeTagList = this.dailyStoreTagMapper.selectList(new LambdaQueryWrapper<DailyStoreTag>()
.eq(DailyStoreTag::getDelFlag, Constants.UNDELETED));
// 档口标签map
Map<Long, List<String>> storeTagMap = storeTagList.stream().collect(Collectors
.groupingBy(DailyStoreTag::getStoreId, Collectors.collectingAndThen(Collectors.toList(), list -> list.stream()
.sorted(Comparator.comparing(DailyStoreTag::getType)).map(DailyStoreTag::getTag).collect(Collectors.toList()))));
List<StoreProdFileLatestFourProdDTO> latest4ProdList = this.storeProdFileMapper.selectLatestFourProdList();
Map<Long, List<StoreProdFileLatestFourProdDTO>> latestProdMap = latest4ProdList.stream().collect(Collectors
.groupingBy(StoreProdFileLatestFourProdDTO::getStoreId));
List<PCStoreRecommendTempDTO> storeRecommendList = new ArrayList<>();
storeWeightMap.forEach((storeId, recommendWeight) -> {
final Store store = storeMap.get(storeId);
storeRecommendList.add(new PCStoreRecommendTempDTO().setStoreId(storeId).setTags(storeTagMap.get(storeId))
.setAdvert(Boolean.FALSE).setRecommendWeight(recommendWeight)
.setStoreWeight(ObjectUtils.isNotEmpty(store) ? store.getStoreWeight() : null)
.setStoreName(ObjectUtils.isNotEmpty(store) ? store.getStoreName() : "")
.setStoreAddress(ObjectUtils.isNotEmpty(store) ? store.getStoreAddress() : "")
.setContactPhone(ObjectUtils.isNotEmpty(store) ? store.getContactPhone() : "")
.setQqAccount(ObjectUtils.isNotEmpty(store) ? store.getQqAccount() : "")
.setWechatAccount(ObjectUtils.isNotEmpty(store) ? store.getWechatAccount() : "")
.setProdList(BeanUtil.copyToList(latestProdMap.get(storeId), PCStoreRecommendTempDTO.PCSRNewProdDTO.class)));
});
if (CollectionUtils.isEmpty(storeRecommendList)) {
return;
}
// 先按照 档口权重 倒序排,再按照 推荐权重 倒序排
storeRecommendList.sort(Comparator.comparing(PCStoreRecommendTempDTO::getStoreWeight, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(PCStoreRecommendTempDTO::getRecommendWeight, Comparator.nullsLast(Comparator.reverseOrder())));
// 返回给前端的数据 不包含 storeWeight 和 storeRecommnedWeight
List<PCStoreRecommendDTO> recommendList = BeanUtil.copyToList(storeRecommendList, PCStoreRecommendDTO.class);
// 放到redis中
redisCache.setCacheObject(CacheConstants.PC_STORE_RECOMMEND_LIST, recommendList);
}
/**
* 广 12:01:00
*/

View File

@ -152,7 +152,7 @@ public class Store extends XktBaseEntity {
/**
* 100-100
*/
private Integer weight;
private Long storeWeight;
@Override

View File

@ -34,7 +34,6 @@ public class PCIndexMidStyleDTO {
private List<PCIMSStyleDTO> styleList;
@Data
@ApiModel(value = "风格榜列表")
public static class PCIMSStyleDTO {
@ApiModelProperty(value = "2商品")
private Integer displayType;

View File

@ -0,0 +1,50 @@
package com.ruoyi.xkt.dto.advertRound.pc.store;
import com.fasterxml.jackson.annotation.JsonIgnore;
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("PC 档口馆 档口列表")
@Data
@Accessors(chain = true)
public class PCStoreRecommendDTO {
@ApiModelProperty(value = "档口ID")
private Long storeId;
@ApiModelProperty(value = "档口名称")
private String storeName;
@ApiModelProperty(value = "会员等级")
private Integer memberLevel;
@ApiModelProperty(value = "档口标签")
private List<String> tags;
@ApiModelProperty(value = "是否广告")
private Boolean advert;
@ApiModelProperty(value = "联系电话")
private String contactPhone;
@ApiModelProperty(value = "微信账号")
private String wechatAccount;
@ApiModelProperty(value = "QQ账号")
private String qqAccount;
@ApiModelProperty(value = "档口地址")
private String storeAddress;
@ApiModelProperty(value = "最新上新列表")
List<PCSRNewProdDTO> prodList;
@Data
public static class PCSRNewProdDTO {
@ApiModelProperty(value = "档口商品ID")
private Long storeProdId;
@ApiModelProperty(value = "商品第一张主图路径")
private String mainPicUrl;
}
}

View File

@ -0,0 +1,53 @@
package com.ruoyi.xkt.dto.advertRound.pc.store;
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("PC 档口馆 档口列表")
@Data
@Accessors(chain = true)
public class PCStoreRecommendTempDTO {
@ApiModelProperty(value = "档口ID")
private Long storeId;
@ApiModelProperty(value = "档口名称")
private String storeName;
@ApiModelProperty(value = "会员等级")
private Integer memberLevel;
@ApiModelProperty(value = "档口标签")
private List<String> tags;
@ApiModelProperty(value = "是否广告")
private Boolean advert;
@ApiModelProperty(value = "联系电话")
private String contactPhone;
@ApiModelProperty(value = "微信账号")
private String wechatAccount;
@ApiModelProperty(value = "QQ账号")
private String qqAccount;
@ApiModelProperty(value = "档口地址")
private String storeAddress;
@ApiModelProperty(value = "推荐数值")
private Long recommendWeight;
@ApiModelProperty(value = "档口权重")
private Long storeWeight;
@ApiModelProperty(value = "最新上新列表")
List<PCSRNewProdDTO> prodList;
@Data
public static class PCSRNewProdDTO {
@ApiModelProperty(value = "档口商品ID")
private Long storeProdId;
@ApiModelProperty(value = "商品第一张主图路径")
private String mainPicUrl;
}
}

View File

@ -36,7 +36,7 @@ public class StoreProdViewDTO {
@ApiModelProperty(value = "标签字符串")
private String tagStr;
@ApiModelProperty(name = "商品标签列表")
private List<String> prodTagList;
private List<String> tags;
@ApiModelProperty(value = "档口ID")
private Long storeId;
@ApiModelProperty(value = "档口名称")

View File

@ -0,0 +1,25 @@
package com.ruoyi.xkt.dto.storeProductFile;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.RequiredArgsConstructor;
/**
* @author liujiang
* @version v1.0
* @date 2025/3/27 15:12
*/
@ApiModel("筛选档口最新的4个商品")
@Data
@RequiredArgsConstructor
public class StoreProdFileLatestFourProdDTO {
@ApiModelProperty(value = "档口ID")
private Long storeId;
@ApiModelProperty(value = "档口商品ID")
private Long storeProdId;
@ApiModelProperty(value = "主图url")
private String mainPicUrl;
}

View File

@ -0,0 +1,23 @@
package com.ruoyi.xkt.dto.website;
import co.elastic.clients.elasticsearch._types.SortOrder;
import com.ruoyi.xkt.dto.BasePageDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author liujiang
* @version v1.0
* @date 2025/3/27 15:12
*/
@EqualsAndHashCode(callSuper = true)
@ApiModel("档口排行榜搜索")
@Data
public class StoreSearchDTO extends BasePageDTO {
@ApiModelProperty(value = "档口名称")
private String storeName;
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.xkt.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.xkt.domain.StoreProductFile;
import com.ruoyi.xkt.dto.storeProductFile.StoreProdFileLatestFourProdDTO;
import com.ruoyi.xkt.dto.storeProductFile.StoreProdFilePicSpaceResDTO;
import com.ruoyi.xkt.dto.storeProductFile.StoreProdFileResDTO;
import com.ruoyi.xkt.dto.storeProductFile.StoreProdMainPicDTO;
@ -71,4 +72,10 @@ public interface StoreProductFileMapper extends BaseMapper<StoreProductFile> {
*/
List<StoreProdFileResDTO> selectMainPic(@Param("storeProdIdList") List<String> storeProdIdList);
/**
* 4
* @return List<StoreProdFileLatestFourProdDTO>
*/
List<StoreProdFileLatestFourProdDTO> selectLatestFourProdList();
}

View File

@ -26,10 +26,10 @@ public interface IPictureSearchService {
*
* @return List<TopProductMatchDTO>
*/
List<TopProductMatchDTO> listImgSearchTopProduct();
List<StoreProdViewDTO> listImgSearchTopProduct();
/**
*
*/
void cacheImgSearchTopProduct();
List<StoreProdViewDTO> cacheImgSearchTopProduct();
}

View File

@ -7,10 +7,12 @@ import com.ruoyi.xkt.dto.advertRound.pc.PCUserCenterDTO;
import com.ruoyi.xkt.dto.advertRound.pc.index.*;
import com.ruoyi.xkt.dto.advertRound.pc.newProd.*;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreMidBannerDTO;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreRecommendDTO;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreTopBannerDTO;
import com.ruoyi.xkt.dto.advertRound.picSearch.PicSearchAdvertDTO;
import com.ruoyi.xkt.dto.es.ESProductDTO;
import com.ruoyi.xkt.dto.website.IndexSearchDTO;
import com.ruoyi.xkt.dto.website.StoreSearchDTO;
import java.io.IOException;
import java.util.List;
@ -187,4 +189,12 @@ public interface IWebsitePCService {
*/
Page<PCSearchDTO> psSearchPage(IndexSearchDTO searchDTO) throws IOException;
/**
* PC
*
* @param searchDTO
* @return Page<PCStoreRecommendDTO>
*/
Page<PCStoreRecommendDTO> pcStoreRecommendPage(StoreSearchDTO searchDTO);
}

View File

@ -91,11 +91,11 @@ public class PictureSearchServiceImpl implements IPictureSearchService {
.map(ProductMatchDTO::getStoreProductId).distinct().collect(Collectors.toList()),
java.sql.Date.valueOf(LocalDate.now()), java.sql.Date.valueOf(LocalDate.now().minusMonths(2)));
// 设置商品标签
storeProdViewAttrList.stream().filter(x -> StringUtils.isNotBlank(x.getTagStr())).forEach(x -> x.setProdTagList(StrUtil.split(x.getTagStr(), ",")));
storeProdViewAttrList.stream().filter(x -> StringUtils.isNotBlank(x.getTagStr())).forEach(x -> x.setTags(StrUtil.split(x.getTagStr(), ",")));
// 以图搜款广告
List<PicSearchAdvertDTO> picSearchAdverts = websitePCService.getPicSearchList();
// 将广告插入到预定位置
return insertAdvertsIntoList(storeProdViewAttrList, BeanUtil.copyToList(picSearchAdverts, StoreProdViewDTO.class), Constants.pic_res_insert_positions);
return insertAdvertsIntoList(storeProdViewAttrList, BeanUtil.copyToList(picSearchAdverts, StoreProdViewDTO.class), Constants.PIC_SEARCH_INSERT_POSITIONS);
}
/**
@ -104,24 +104,23 @@ public class PictureSearchServiceImpl implements IPictureSearchService {
*/
@Override
@Transactional(readOnly = true)
public List<TopProductMatchDTO> listImgSearchTopProduct() {
List<TopProductMatchDTO> topProductMatchList = redisCache.getCacheObject(CacheConstants.TOP_IMG_SEARCH_PRODUCT);
if (CollectionUtils.isNotEmpty(topProductMatchList)) {
return topProductMatchList;
public List<StoreProdViewDTO> listImgSearchTopProduct() {
List<StoreProdViewDTO> picSearchHotList = redisCache.getCacheObject(CacheConstants.IMG_SEARCH_PRODUCT_HOT);
if (CollectionUtils.isNotEmpty(picSearchHotList)) {
return picSearchHotList;
}
// 重新缓存数据到redis
this.cacheImgSearchTopProduct();
return redisCache.getCacheObject(CacheConstants.TOP_IMG_SEARCH_PRODUCT);
return this.cacheImgSearchTopProduct();
}
@Override
public void cacheImgSearchTopProduct() {
public List<StoreProdViewDTO> cacheImgSearchTopProduct() {
//热搜规则暂定1个月内搜图次数排序最多返回前100查询同款商品时最多检索1000条数据
List<ProductImgSearchCountDTO> prodImgSearchCounts = storeProdStatisticsMapper
.listProdImgSearchCount(java.sql.Date.valueOf(LocalDate.now()), java.sql.Date.valueOf(LocalDate.now().minusMonths(1)));
.listProdImgSearchCount(java.sql.Date.valueOf(LocalDate.now().minusMonths(1)), java.sql.Date.valueOf(LocalDate.now()));
if (CollUtil.isEmpty(prodImgSearchCounts)) {
return;
return new ArrayList<>();
}
Map<Long, Integer> searchCountMap = prodImgSearchCounts.stream().collect(Collectors
.toMap(ProductImgSearchCountDTO::getStoreProductId, ProductImgSearchCountDTO::getImgSearchCount));
@ -136,7 +135,8 @@ public class PictureSearchServiceImpl implements IPictureSearchService {
.sorted(Comparator.comparing(StoreProdViewDTO::getImgSearchCount).reversed())
.limit(100)
.collect(Collectors.toList());
redisCache.setCacheObject(CacheConstants.TOP_IMG_SEARCH_PRODUCT, storeProdViewAttrList);
redisCache.setCacheObject(CacheConstants.IMG_SEARCH_PRODUCT_HOT, storeProdViewAttrList);
return storeProdViewAttrList;
}
/**

View File

@ -89,7 +89,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
List<APPIndexHotSaleDTO> redisList = this.redisCache.getCacheObject(CacheConstants.APP_INDEX_HOT_SALE_ADVERT);
if (CollectionUtils.isNotEmpty(redisList)) {
// 添加广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.APP_INSERT_POSITIONS));
} else {
// 从数据库查首页精选热卖推广(精准搜索是否存在推广,不存在从已过期的数据中拉数据来凑数)
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
@ -116,7 +116,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
// 放到redis中 有效期1天
this.redisCache.setCacheObject(CacheConstants.APP_INDEX_HOT_SALE_ADVERT, hotSaleList, 1, TimeUnit.DAYS);
// 添加了广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, hotSaleList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, hotSaleList, Constants.APP_INSERT_POSITIONS));
}
}
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), realDataList);
@ -143,7 +143,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
List<APPIndexPopularSaleDTO> redisList = this.redisCache.getCacheObject(CacheConstants.APP_INDEX_POPULAR_SALE_ADVERT);
if (CollectionUtils.isNotEmpty(redisList)) {
// 添加广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.APP_INSERT_POSITIONS));
} else {
// 从数据库查首页 人气爆品 推广(精准搜索是否存在推广,不存在从已过期的数据中拉数据来凑数)
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
@ -170,7 +170,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
// 放到redis中 有效期1天
this.redisCache.setCacheObject( CacheConstants.APP_INDEX_POPULAR_SALE_ADVERT, popularSaleList, 1, TimeUnit.DAYS);
// 添加了广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, popularSaleList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, popularSaleList, Constants.APP_INSERT_POSITIONS));
}
}
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), realDataList);
@ -197,7 +197,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
List<APPIndexNewProdDTO> redisList = this.redisCache.getCacheObject(CacheConstants.APP_INDEX_NEW_PROD);
if (CollectionUtils.isNotEmpty(redisList)) {
// 添加广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.APP_INSERT_POSITIONS));
} else {
// 从数据库查首页 新品榜 推广(精准搜索是否存在推广,不存在从已过期的数据中拉数据来凑数)
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
@ -224,7 +224,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
// 放到redis中 有效期1天
this.redisCache.setCacheObject(CacheConstants.APP_INDEX_NEW_PROD, newProdList, 1, TimeUnit.DAYS);
// 添加了广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, newProdList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, newProdList, Constants.APP_INSERT_POSITIONS));
}
}
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), realDataList);
@ -251,7 +251,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
List<APPSearchDTO> redisList = this.redisCache.getCacheObject(CacheConstants.APP_SEARCH);
if (CollectionUtils.isNotEmpty(redisList)) {
// 添加广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, redisList, Constants.APP_INSERT_POSITIONS));
} else {
// 从数据库查首页 新品榜 推广(精准搜索是否存在推广,不存在从已过期的数据中拉数据来凑数)
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
@ -278,7 +278,7 @@ public class WebsiteAPPServiceImpl implements IWebsiteAPPService {
// 放到redis中 有效期1天
this.redisCache.setCacheObject(CacheConstants.APP_SEARCH, newProdList, 1, TimeUnit.DAYS);
// 添加了广告的数据
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, newProdList, Constants.insertPositions));
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), insertAdvertsIntoList(realDataList, newProdList, Constants.APP_INSERT_POSITIONS));
}
}
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), realDataList);

View File

@ -3,7 +3,6 @@ package com.ruoyi.xkt.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -24,6 +23,7 @@ import com.ruoyi.xkt.dto.advertRound.pc.PCUserCenterDTO;
import com.ruoyi.xkt.dto.advertRound.pc.index.*;
import com.ruoyi.xkt.dto.advertRound.pc.newProd.*;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreMidBannerDTO;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreRecommendDTO;
import com.ruoyi.xkt.dto.advertRound.pc.store.PCStoreTopBannerDTO;
import com.ruoyi.xkt.dto.advertRound.picSearch.PicSearchAdvertDTO;
import com.ruoyi.xkt.dto.dailySale.CateSaleRankDTO;
@ -34,6 +34,7 @@ import com.ruoyi.xkt.dto.storeProduct.StoreProdPriceAndMainPicDTO;
import com.ruoyi.xkt.dto.storeProduct.StoreProdViewDTO;
import com.ruoyi.xkt.dto.storeProductFile.StoreProdFileResDTO;
import com.ruoyi.xkt.dto.website.IndexSearchDTO;
import com.ruoyi.xkt.dto.website.StoreSearchDTO;
import com.ruoyi.xkt.enums.AdBiddingStatus;
import com.ruoyi.xkt.enums.AdDisplayType;
import com.ruoyi.xkt.enums.AdLaunchStatus;
@ -247,6 +248,86 @@ public class WebsitePCServiceImpl implements IWebsitePCService {
return new Page<>(page.getPageNum(), page.getPageSize(), page.getPages(), page.getTotal(), realDataList);
}
/**
* PC
*
* @param searchDTO
* @return Page<PCStoreRecommendDTO>
*/
@Override
@Transactional(readOnly = true)
public Page<PCStoreRecommendDTO> pcStoreRecommendPage(StoreSearchDTO searchDTO) {
// 从redis中获取档口推荐列表
List<PCStoreRecommendDTO> redisList = this.redisCache.getCacheObject(CacheConstants.PC_STORE_RECOMMEND_LIST);
if (CollectionUtils.isEmpty(redisList)) {
return Page.empty(searchDTO.getPageSize(), searchDTO.getPageNum());
}
// 正确的分页数据
List<PCStoreRecommendDTO> realDataList = redisList.stream()
.filter(x -> StringUtils.isEmpty(x.getStoreName()) || x.getStoreName().contains(searchDTO.getStoreName()))
.skip((long) (searchDTO.getPageNum() - 1) * searchDTO.getPageSize())
.limit(searchDTO.getPageSize()).collect(Collectors.toList());
final long pages = (long) Math.ceil((double) redisList.size() / searchDTO.getPageSize());
// APP 只有第一页 有数据 其它页暂时没有广告
if (searchDTO.getPageNum() > 1) {
return new Page<>(searchDTO.getPageNum(), searchDTO.getPageSize(), pages, redisList.size(), realDataList);
}
// 从redis中获取数据
List<PCStoreRecommendDTO> redisAdvertList = this.redisCache.getCacheObject(CacheConstants.PC_STORE_RECOMMEND_ADVERT);
if (CollectionUtils.isNotEmpty(redisAdvertList)) {
// 添加广告的数据
return new Page<>(searchDTO.getPageNum(), searchDTO.getPageSize(), pages, redisList.size(),
insertAdvertsIntoList(realDataList, redisAdvertList, Constants.STORE_RECOMMEND_INSERT_POSITIONS));
} else {
// 从数据库查档口推荐列表 是否存在推广
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
.isNotNull(AdvertRound::getStoreId).eq(AdvertRound::getDelFlag, Constants.UNDELETED)
.eq(AdvertRound::getTypeId, AdType.PC_STORE_RECOMMEND.getValue())
.eq(AdvertRound::getLaunchStatus, AdLaunchStatus.LAUNCHING.getValue())
.eq(AdvertRound::getBiddingStatus, AdBiddingStatus.BIDDING_SUCCESS.getValue()));
if (CollectionUtils.isNotEmpty(advertRoundList)) {
// 档口列表
List<Store> storeList = this.storeMapper.selectList(new LambdaQueryWrapper<Store>()
.in(Store::getId, advertRoundList.stream().map(AdvertRound::getStoreId).collect(Collectors.toList())));
Map<Long, Store> storeMap = storeList.stream().collect(Collectors.toMap(Store::getId, Function.identity()));
// 档口所有的标签
List<DailyStoreTag> storeTagList = this.dailyStoreTagMapper.selectList(new LambdaQueryWrapper<DailyStoreTag>()
.in(DailyStoreTag::getStoreId, advertRoundList.stream().map(AdvertRound::getStoreId).collect(Collectors.toList())));
Map<Long, List<String>> storeTagMap = CollectionUtils.isEmpty(storeTagList) ? new ConcurrentHashMap<>()
: storeTagList.stream().collect(Collectors.groupingBy(DailyStoreTag::getStoreId, Collectors.mapping(DailyStoreTag::getTag, Collectors.toList())));
// 商品的主图map
List<StoreProdPriceAndMainPicDTO> mainPicList = this.storeProdMapper.selectPriceAndMainPicList(advertRoundList.stream().filter(x -> StringUtils.isNotBlank(x.getProdIdStr()))
.map(x -> x.getProdIdStr().split(",")).flatMap(Arrays::stream).map(Long::valueOf).distinct().collect(Collectors.toList()));
Map<Long, StoreProdPriceAndMainPicDTO> mainPicMap = mainPicList.stream().collect(Collectors.toMap(StoreProdPriceAndMainPicDTO::getStoreProdId, x -> x));
List<PCStoreRecommendDTO> storeRecommendList = new ArrayList<>();
advertRoundList.stream().filter(x -> StringUtils.isNotBlank(x.getProdIdStr())).forEach(x -> {
Store store = storeMap.get(x.getStoreId());
PCStoreRecommendDTO storeRecommend = new PCStoreRecommendDTO().setAdvert(Boolean.TRUE).setStoreId(x.getStoreId())
.setStoreName(ObjectUtils.isNotEmpty(store) ? store.getStoreName() : "").setAdvert(Boolean.TRUE)
.setTags(storeTagMap.getOrDefault(x.getStoreId(), new ArrayList<>()))
.setContactPhone(ObjectUtils.isNotEmpty(store) ? store.getContactPhone() : "")
.setWechatAccount(ObjectUtils.isNotEmpty(store) ? store.getWechatAccount() : "")
.setQqAccount(ObjectUtils.isNotEmpty(store) ? store.getQqAccount() : "")
.setStoreAddress(ObjectUtils.isNotEmpty(store) ? store.getStoreAddress() : "");
// 这里是一个档口上传多个档口商品所以需要对prodIdStr的逗号进行分割
List<Long> prodIdList = StrUtil.split(x.getProdIdStr(), ",").stream().map(Long::parseLong).collect(Collectors.toList());
storeRecommend.setProdList(prodIdList.stream().map(storeProdId -> {
StoreProdPriceAndMainPicDTO mainPicDTO = mainPicMap.get(storeProdId);
return new PCStoreRecommendDTO.PCSRNewProdDTO().setStoreProdId(storeProdId)
.setMainPicUrl(ObjectUtils.isNotEmpty(mainPicDTO) ? mainPicDTO.getMainPicUrl() : "");
}).collect(Collectors.toList()));
storeRecommendList.add(storeRecommend);
});
// 放到redis中过期时间为1天
this.redisCache.setCacheObject(CacheConstants.PC_STORE_RECOMMEND_ADVERT, storeRecommendList, 1, TimeUnit.DAYS);
// 添加广告的数据
return new Page<>(searchDTO.getPageNum(), searchDTO.getPageSize(), pages, redisList.size(),
insertAdvertsIntoList(realDataList, storeRecommendList, Constants.STORE_RECOMMEND_INSERT_POSITIONS));
}
}
return new Page<>(searchDTO.getPageNum(), searchDTO.getPageSize(), pages, redisList.size(), realDataList);
}
/**
* PC
*
@ -1580,4 +1661,34 @@ public class WebsitePCServiceImpl implements IWebsitePCService {
final List<ESProductDTO> esProdList = resList.hits().hits().stream().map(x -> x.source().setStoreProdId(x.id())).collect(Collectors.toList());
return new Page<>(searchDTO.getPageNum(), searchDTO.getPageSize(), total / searchDTO.getPageSize() + 1, total, esProdList);
}
/**
* 广
*
* @param dataList
* @param adverts 广
* @param positions 广
* @param <T>
* @return
*/
public static <T> List<T> insertAdvertsIntoList(List<T> dataList, List<T> adverts, Set<Integer> positions) {
List<T> mergedList = new ArrayList<>(dataList); // 先拷贝原始数据
int advertIndex = 0;
// 遍历所有广告插入位置
for (Integer position : positions) {
if (advertIndex >= adverts.size()) {
// 广告已经插完,结束循环
break;
}
if (position >= 0 && position < mergedList.size()) {
// 插入位置合法,插入广告
mergedList.add(position, adverts.get(advertIndex++));
} else {
// 插入位置非法(大于等于当前列表长度),追加到末尾
mergedList.add(adverts.get(advertIndex++));
}
}
return mergedList;
}
}

View File

@ -104,5 +104,34 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</select>
<select id="selectLatestFourProdList" resultType="com.ruoyi.xkt.dto.storeProductFile.StoreProdFileLatestFourProdDTO">
WITH RankedProducts AS (
SELECT
spf.store_prod_id,
spf.store_id,
sf.file_url AS mainPicUrl,
ROW_NUMBER() OVER ( PARTITION BY spf.store_id ORDER BY spf.create_time DESC ) AS rank_num
FROM
store_product_file spf
LEFT JOIN store_product sp ON spf.store_prod_id = sp.id
LEFT JOIN sys_file sf ON spf.file_id = sf.id
LEFT JOIN store s ON spf.store_id = s.id
WHERE
spf.del_flag = 0
AND spf.file_type = 1
AND spf.order_num = 1
) SELECT
store_prod_id,
store_id,
mainPicUrl
FROM
RankedProducts
WHERE
rank_num &lt;= 4
ORDER BY
store_id,
rank_num
</select>
</mapper>