master:档口购买推广位功能v1.0
parent
59ab724173
commit
b33cf81155
|
|
@ -87,6 +87,17 @@ public class SysProductCategoryController extends XktBaseController {
|
|||
return R.ok(BeanUtil.copyToList(prodCateService.selectList(BeanUtil.toBean(listVO, ProdCateListDTO.class)), ProdCateListResVO.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据1级分类获取二级分类列表
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:category:list')")
|
||||
@ApiOperation(value = "根据1级分类获取二级分类列表", httpMethod = "GET", response = R.class)
|
||||
@GetMapping("/sub/{parCateId}")
|
||||
public R<List<ProdCateVO>> getSubList(@PathVariable Long parCateId) {
|
||||
return R.ok(BeanUtil.copyToList(prodCateService.getSubList(parCateId), ProdCateVO.class));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* APP首页获取商品分类
|
||||
|
|
@ -109,4 +120,6 @@ public class SysProductCategoryController extends XktBaseController {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,18 @@ import com.ruoyi.common.annotation.Log;
|
|||
import com.ruoyi.common.core.controller.XktBaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.web.controller.xkt.vo.advert.AdvertCreateVO;
|
||||
import com.ruoyi.web.controller.xkt.vo.advertRoundPlay.AdPlayStoreCreateVO;
|
||||
import com.ruoyi.web.controller.xkt.vo.advertRoundPlay.AdPlayStoreResVO;
|
||||
import com.ruoyi.xkt.dto.advert.AdvertCreateDTO;
|
||||
import com.ruoyi.xkt.dto.advertRoundPlay.AdPlayStoreCreateDTO;
|
||||
import com.ruoyi.xkt.service.IAdvertRoundPlayService;
|
||||
import com.ruoyi.web.controller.xkt.vo.advertRound.AdRoundStoreCreateVO;
|
||||
import com.ruoyi.web.controller.xkt.vo.advertRound.AdRoundStoreResVO;
|
||||
import com.ruoyi.xkt.dto.advertRound.AdRoundStoreCreateDTO;
|
||||
import com.ruoyi.xkt.service.IAdvertRoundService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* 推广营销轮次投放Controller
|
||||
*
|
||||
|
|
@ -26,11 +26,10 @@ import org.springframework.web.bind.annotation.*;
|
|||
@Api(tags = "推广营销轮次投放")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/rest/v1/ad-round-plays")
|
||||
public class AdvertRoundPlayController extends XktBaseController {
|
||||
|
||||
final IAdvertRoundPlayService adPlayService;
|
||||
@RequestMapping("/rest/v1/ad-rounds")
|
||||
public class AdvertRoundController extends XktBaseController {
|
||||
|
||||
final IAdvertRoundService advertRoundService;
|
||||
|
||||
/**
|
||||
* 档口购买推广营销
|
||||
|
|
@ -38,23 +37,32 @@ public class AdvertRoundPlayController extends XktBaseController {
|
|||
@ApiOperation(value = "档口购买推广营销", httpMethod = "POST", response = R.class)
|
||||
@Log(title = "档口购买推广营销", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public R<Integer> create(@Validated @RequestBody AdPlayStoreCreateVO createVO) {
|
||||
return R.ok(adPlayService.create(BeanUtil.toBean(createVO, AdPlayStoreCreateDTO.class)));
|
||||
public R<Integer> create(@Validated @RequestBody AdRoundStoreCreateVO createVO) {
|
||||
return R.ok(advertRoundService.create(BeanUtil.toBean(createVO, AdRoundStoreCreateDTO.class)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据类型查询当前档口的推广营销数据
|
||||
*/
|
||||
@ApiOperation(value = "根据类型查询当前档口的推广营销数据", httpMethod = "GET", response = R.class)
|
||||
@GetMapping(value = "/{typeId}/{storeId}")
|
||||
public R<AdPlayStoreResVO> getStoreAdInfo(@PathVariable("typeId") Integer typeId,
|
||||
@PathVariable("storeId") Long storeId) {
|
||||
return R.ok(BeanUtil.toBean(adPlayService.getStoreAdInfo(storeId, typeId), AdPlayStoreResVO.class));
|
||||
public R<AdRoundStoreResVO> getStoreAdInfo(@PathVariable("typeId") Integer typeId,
|
||||
@PathVariable("storeId") Long storeId) {
|
||||
return R.ok(BeanUtil.toBean(advertRoundService.getStoreAdInfo(storeId, typeId), AdRoundStoreResVO.class));
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/test")
|
||||
public void test() throws ParseException {
|
||||
advertRoundService.saveAdvertDeadlineToRedis();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO 退订
|
||||
|
||||
// TODO 每晚定时任务 调整 推广营销的offerStatus
|
||||
|
||||
|
||||
|
||||
|
|
@ -103,5 +103,14 @@ public class StoreController extends XktBaseController {
|
|||
return R.ok(BeanUtil.toBean(storeService.getApproveInfo(storeId), StoreApproveResVO.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* APP获取档口基本信息
|
||||
*/
|
||||
@ApiOperation(value = "档口审核是获取档口基本信息", httpMethod = "GET", response = R.class)
|
||||
@GetMapping(value = "/app/{storeId}")
|
||||
public R<StoreAppResVO> getAppInfo(@PathVariable("storeId") Long storeId) {
|
||||
return R.ok(BeanUtil.toBean(storeService.getAppInfo(storeId), StoreAppResVO.class));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,15 @@ public class StoreProductController extends XktBaseController {
|
|||
|
||||
final IStoreProductService storeProdService;
|
||||
|
||||
/**
|
||||
* 查询档口商品所有的风格
|
||||
*/
|
||||
@ApiOperation(value = "查询档口商品所有的风格", httpMethod = "GET", response = R.class)
|
||||
@GetMapping(value = "/styles")
|
||||
public R<List<String>> getStyleList() {
|
||||
return R.ok(storeProdService.getStyleList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询档口商品
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package com.ruoyi.web.controller.xkt.vo.advertRound;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author liujiang
|
||||
* @version v1.0
|
||||
* @date 2025/3/27 15:12
|
||||
*/
|
||||
@ApiModel("档口购买推广营销位")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class AdRoundStoreCreateVO {
|
||||
|
||||
@NotNull(message = "广告ID不能为空!")
|
||||
@ApiModelProperty(value = "广告ID")
|
||||
private Long advertId;
|
||||
@NotNull(message = "广告轮次ID不能为空!")
|
||||
@ApiModelProperty(value = "广告轮次ID")
|
||||
private Long roundId;
|
||||
@NotNull(message = "typeId不能为空!")
|
||||
@ApiModelProperty(value = "typeId")
|
||||
private Integer typeId;
|
||||
@ApiModelProperty(value = "[不一定传]广告位置 A B C D E")
|
||||
private String position;
|
||||
@NotNull(message = "推广档口ID不能为空!")
|
||||
@ApiModelProperty(value = "推广档口ID")
|
||||
private Long storeId;
|
||||
@NotNull(message = "推广档口出价不能为空!")
|
||||
@ApiModelProperty(value = "推广档口出价")
|
||||
private BigDecimal payPrice;
|
||||
@ApiModelProperty(value = "图片设计(1 自主设计、2 平台设计)")
|
||||
private Integer picDesignType;
|
||||
@ApiModelProperty(value = "推广商品ID列表")
|
||||
private String prodIdStr;
|
||||
@NotBlank(message = "对象锁符号不能为空!")
|
||||
@ApiModelProperty(value = "对象锁符号")
|
||||
private String symbol;
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.web.controller.xkt.vo.advertRoundPlay;
|
||||
package com.ruoyi.web.controller.xkt.vo.advertRound;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import lombok.Data;
|
||||
|
|
@ -12,7 +12,7 @@ import lombok.experimental.Accessors;
|
|||
@ApiModel("当前类型档口营销推广数据")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdPlayStoreResVO {
|
||||
public class AdRoundStoreResVO {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.ruoyi.web.controller.xkt.vo.store;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liujiang
|
||||
* @version v1.0
|
||||
* @date 2025/3/27 15:12
|
||||
*/
|
||||
@ApiModel("档口App基本信息")
|
||||
@Data
|
||||
public class StoreAppResVO {
|
||||
|
||||
@ApiModelProperty(value = "档口ID")
|
||||
private Long storeId;
|
||||
@ApiModelProperty(value = "档口名称")
|
||||
private String storeName;
|
||||
@ApiModelProperty(value = "是否已关注")
|
||||
private Boolean attention;
|
||||
@ApiModelProperty(value = "标签列表")
|
||||
private List<String> tagList;
|
||||
@ApiModelProperty(value = "档口地址")
|
||||
private String storeAddress;
|
||||
@ApiModelProperty(value = "联系电话")
|
||||
private String contactPhone;
|
||||
@ApiModelProperty(value = "备选联系电话")
|
||||
private String contactBackPhone;
|
||||
|
||||
}
|
||||
|
|
@ -20,6 +20,8 @@ public class IndexSearchVO extends BasePageVO {
|
|||
|
||||
@ApiModelProperty(value = "搜索内容")
|
||||
private String search;
|
||||
@ApiModelProperty(value = "商品状态列表")
|
||||
private List<String> prodStatusList;
|
||||
@ApiModelProperty(value = "一级类目ID列表")
|
||||
private List<String> parCateIdList;
|
||||
@ApiModelProperty(value = "二级类目ID列表")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package com.ruoyi.common.constant;
|
||||
|
||||
import java.util.Locale;
|
||||
import com.ruoyi.common.enums.AdType;
|
||||
import io.jsonwebtoken.Claims;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 通用常量信息
|
||||
*
|
||||
|
|
@ -219,5 +223,23 @@ public class Constants
|
|||
*/
|
||||
public static final Long TOPMOST_PRODUCT_CATEGORY_ID = 1L;
|
||||
|
||||
/**
|
||||
* 枚举的广告位类型
|
||||
*/
|
||||
public final static List<Integer> posEnumTypeList = new ArrayList<Integer>() {{
|
||||
// 人气榜中上侧
|
||||
add(AdType.PC_HOME_POP_MID_TOP.getValue());
|
||||
// 人气榜中下侧
|
||||
add(AdType.PC_HOME_POP_MID_BOTTOM.getValue());
|
||||
// 人气榜右上侧
|
||||
add(AdType.PC_HOME_POP_RIGHT_TOP.getValue());
|
||||
// 人气榜右下侧
|
||||
add(AdType.PC_HOME_POP_RIGHT_BOTTOM.getValue());
|
||||
// 新品馆热卖榜右推广商品
|
||||
add(AdType.PC_NEW_PROD_HOT_RIGHT_PRODUCT.getValue());
|
||||
// PC下载页
|
||||
add(AdType.PC_DOWNLOAD.getValue());
|
||||
}};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.xkt.enums;
|
||||
package com.ruoyi.common.enums;
|
||||
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
|
|
@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* 调度任务信息操作处理
|
||||
|
|
@ -71,4 +72,12 @@ public class DailyTaskController extends BaseController {
|
|||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/advert-round-filter")
|
||||
public R dailyRoundFilterTime(SysJob sysJob) throws ParseException {
|
||||
task.saveAdvertDeadlineToRedis();
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,20 @@ 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.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.framework.es.EsClientWrapper;
|
||||
import com.ruoyi.quartz.domain.*;
|
||||
import com.ruoyi.quartz.dto.DailySaleCusDTO;
|
||||
import com.ruoyi.quartz.dto.DailySaleDTO;
|
||||
import com.ruoyi.quartz.dto.DailySaleProdDTO;
|
||||
import com.ruoyi.quartz.dto.WeekCateSaleDTO;
|
||||
import com.ruoyi.quartz.mapper.*;
|
||||
import com.ruoyi.system.mapper.SysProductCategoryMapper;
|
||||
import com.ruoyi.xkt.domain.*;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleCusDTO;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleDTO;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleProdDTO;
|
||||
import com.ruoyi.xkt.dto.dailySale.WeekCateSaleDTO;
|
||||
import com.ruoyi.xkt.dto.dailyStoreTag.DailyStoreTagDTO;
|
||||
import com.ruoyi.xkt.enums.*;
|
||||
import com.ruoyi.xkt.mapper.*;
|
||||
import com.ruoyi.xkt.service.IAdvertRoundService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
|
@ -27,10 +28,13 @@ import org.springframework.stereotype.Component;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -63,6 +67,8 @@ public class XktTask {
|
|||
final EsClientWrapper esClientWrapper;
|
||||
final AdvertMapper advertMapper;
|
||||
final AdvertRoundMapper advertRoundMapper;
|
||||
final RedisCache redisCache;
|
||||
final IAdvertRoundService advertRoundService;
|
||||
|
||||
/**
|
||||
* 每晚1点同步档口销售数据
|
||||
|
|
@ -297,7 +303,7 @@ public class XktTask {
|
|||
* 每晚定时任务更新推广的播放轮次,这个要次日凌晨更新
|
||||
*/
|
||||
@Transactional
|
||||
public void dailyAdvertRound(){
|
||||
public void dailyAdvertRound() {
|
||||
List<Advert> advertList = this.advertMapper.selectList(new LambdaQueryWrapper<Advert>()
|
||||
.eq(Advert::getDelFlag, Constants.UNDELETED).eq(Advert::getOnlineStatus, AdOnlineStatus.ONLINE.getValue()));
|
||||
if (CollectionUtils.isEmpty(advertList)) {
|
||||
|
|
@ -325,10 +331,15 @@ public class XktTask {
|
|||
final LocalDate endDate = now.plusDays(advert.getPlayInterval() - 1);
|
||||
// 按照播放数量依次生成下一轮播放的推广位
|
||||
for (int j = 0; j < advert.getPlayNum(); j++) {
|
||||
updateList.add(new AdvertRound().setAdvertId(advert.getId()).setRoundId(i + 1).setLaunchStatus(launchStatus)
|
||||
.setStartTime(java.sql.Date.valueOf(now)).setEndTime(java.sql.Date.valueOf(endDate))
|
||||
// 依次按照26个字母顺序 如果i == 0 则A i == 1 则B i==2则C
|
||||
.setPosition(String.valueOf((char) ('A' + j))));
|
||||
// 依次按照26个字母顺序 如果i == 0 则A i == 1 则B i==2则C
|
||||
final String position = String.valueOf((char) ('A' + j));
|
||||
// 当前播放轮次id
|
||||
final int roundId = i + 1;
|
||||
updateList.add(new AdvertRound().setAdvertId(advert.getId()).setTypeId(advert.getTypeId()).setRoundId(roundId).setLaunchStatus(launchStatus)
|
||||
.setStartTime(java.sql.Date.valueOf(now)).setEndTime(java.sql.Date.valueOf(endDate)).setPosition(position)
|
||||
.setSymbol(Constants.posEnumTypeList.contains(advert.getTypeId())
|
||||
// 如果是位置枚举的推广位,则需要精确到某一个position的推广位,反之,若是时间范围,则直接精确到播放轮次即可
|
||||
? advert.getBasicSymbol() + roundId + position : advert.getBasicSymbol() + roundId));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -364,13 +375,20 @@ public class XktTask {
|
|||
final LocalDate endDate = startDate.plusDays(advert.getPlayInterval() - 1);
|
||||
// 每一轮的播放数量
|
||||
for (int i = 0; i < advert.getPlayNum(); i++) {
|
||||
// 依次按照26个字母顺序 如果i == 0 则A i == 1 则B i==2则C
|
||||
final String position = String.valueOf((char) ('A' + j));
|
||||
// 生成最新的下一轮推广位
|
||||
updateList.add(new AdvertRound().setAdvertId(advert.getId()).setRoundId(maxRoundId).setLaunchStatus(AdLaunchStatus.UN_LAUNCH.getValue())
|
||||
updateList.add(new AdvertRound().setAdvertId(advert.getId()).setTypeId(advert.getTypeId()).setRoundId(maxRoundId)
|
||||
.setLaunchStatus(AdLaunchStatus.UN_LAUNCH.getValue()).setPosition(position)
|
||||
// java.sql.Date 直接转化成yyyy-MM-dd格式
|
||||
.setStartTime(java.sql.Date.valueOf(startDate)).setEndTime(java.sql.Date.valueOf(endDate))
|
||||
// 依次按照26个字母顺序 如果i == 0 则A i == 1 则B i==2则C
|
||||
.setPosition(String.valueOf((char) ('A' + i))));
|
||||
.setSymbol(Constants.posEnumTypeList.contains(advert.getTypeId())
|
||||
// 如果是位置枚举的推广位,则需要精确到某一个position的推广位,反之,若是时间范围,则直接精确到播放轮次即可
|
||||
? advert.getBasicSymbol() + maxRoundId + position : advert.getBasicSymbol() + maxRoundId));
|
||||
}
|
||||
}
|
||||
// 需要更新推广位轮次最新的资源锁
|
||||
this.advertRoundService.initAdvertLockMap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -381,6 +399,22 @@ public class XktTask {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过定时任务(每天晚上9:00)往redis中放当前推广位 当前播放轮 或 即将播放轮 的截止时间;
|
||||
* 比如:5.1 - 5.3
|
||||
* a. 现在是4.30 则截止时间是 4.30 22:00
|
||||
* b. 现在是5.2,则截止时间是 5.2 22:00:00 。
|
||||
* c. 现在是5.3,则第一轮还有请求,肯定是人为的不用管。请求第三轮 或者 第四轮 不报错。只处理第二轮请求
|
||||
*
|
||||
* @throws ParseException
|
||||
*/
|
||||
public void saveAdvertDeadlineToRedis() throws ParseException {
|
||||
// 直接调service方法,若当时redis出了问题,也方便第一时间 通过业务流程弥补 两边都有一个补偿机制
|
||||
advertRoundService.saveAdvertDeadlineToRedis();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 给商品打风格标签
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.quartz.mapper.DailySaleCustomerMapper">
|
||||
<mapper namespace="com.ruoyi.xkt.mapper.DailySaleCustomerMapper">
|
||||
|
||||
<select id="selectDailySale" resultType="com.ruoyi.quartz.dto.DailySaleCusDTO">
|
||||
<select id="selectDailySale" resultType="com.ruoyi.xkt.dto.dailySale.DailySaleCusDTO">
|
||||
SELECT
|
||||
ss.store_id,
|
||||
ss.store_cus_id,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.quartz.mapper.DailySaleMapper">
|
||||
<mapper namespace="com.ruoyi.xkt.mapper.DailySaleMapper">
|
||||
|
||||
<select id="selectDailySale" resultType="com.ruoyi.quartz.dto.DailySaleDTO">
|
||||
<select id="selectDailySale" resultType="com.ruoyi.xkt.dto.dailySale.DailySaleDTO">
|
||||
SELECT
|
||||
ss.store_id,
|
||||
COALESCE(SUM(CASE WHEN ssd.sale_type = 1 THEN ssd.amount ELSE 0 END), 0) AS saleAmount,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.quartz.mapper.DailySaleProductMapper">
|
||||
<mapper namespace="com.ruoyi.xkt.mapper.DailySaleProductMapper">
|
||||
|
||||
<select id="selectDailySale" resultType="com.ruoyi.quartz.dto.DailySaleProdDTO">
|
||||
<select id="selectDailySale" resultType="com.ruoyi.xkt.dto.dailySale.DailySaleProdDTO">
|
||||
SELECT
|
||||
sp.store_id,
|
||||
sp.id AS store_prod_id,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.quartz.mapper.WeekCateSaleMapper">
|
||||
<mapper namespace="com.ruoyi.xkt.mapper.WeekCateSaleMapper">
|
||||
|
||||
<select id="selectWeekCateSale" resultType="com.ruoyi.quartz.dto.WeekCateSaleDTO">
|
||||
<select id="selectWeekCateSale" resultType="com.ruoyi.xkt.dto.dailySale.WeekCateSaleDTO">
|
||||
SELECT
|
||||
sp.prod_cate_id,
|
||||
COUNT( prod_cate_id ) AS count
|
||||
|
|
|
|||
|
|
@ -68,4 +68,12 @@ public interface ISysProductCategoryService {
|
|||
*/
|
||||
List<AppHomeProdCateListResDTO> appCate();
|
||||
|
||||
/**
|
||||
* 根据1级分类获取二级分类列表
|
||||
*
|
||||
* @param parCateId 一级分类ID
|
||||
* @return
|
||||
*/
|
||||
List<ProdCateDTO> getSubList(Long parCateId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,6 +161,20 @@ public class SysProductCategoryServiceImpl implements ISysProductCategoryService
|
|||
.map(x -> BeanUtil.toBean(x, AppHomeProdCateListResDTO.class)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据1级分类获取二级分类列表
|
||||
*
|
||||
* @param parCateId 一级分类ID
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<ProdCateDTO> getSubList(Long parCateId) {
|
||||
List<SysProductCategory> subCateList = this.prodCateMapper.selectList(new LambdaQueryWrapper<SysProductCategory>()
|
||||
.eq(SysProductCategory::getParentId, parCateId).eq(SysProductCategory::getDelFlag, Constants.UNDELETED));
|
||||
return BeanUtil.copyToList(subCateList, ProdCateDTO.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 组装商品分类树
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ public class Advert extends XktBaseEntity {
|
|||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 每一个广告位基础锁符号 10位大小写、字母组成的字符串
|
||||
*/
|
||||
private String basicSymbol;
|
||||
/**
|
||||
* 每个档口可以购买当前广告位数量限制
|
||||
*/
|
||||
private Integer storeBuyLimit;
|
||||
/**
|
||||
* 推广平台 电脑端 、APP
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -31,10 +31,18 @@ public class AdvertRound extends XktBaseEntity {
|
|||
* 广告ID
|
||||
*/
|
||||
private Long advertId;
|
||||
/**
|
||||
* 推广类型
|
||||
*/
|
||||
private Integer typeId;
|
||||
/**
|
||||
* 轮次ID
|
||||
*/
|
||||
private Integer roundId;
|
||||
/**
|
||||
* 资源锁标识 位置枚举的推广位精确到A B C D E 等,时间范围的推广位精确到具体类型
|
||||
*/
|
||||
private String symbol;
|
||||
/**
|
||||
* 投放状态
|
||||
*/
|
||||
|
|
@ -51,21 +59,6 @@ public class AdvertRound extends XktBaseEntity {
|
|||
* 广告位置 A B C D E... 对应advert中的playNum
|
||||
*/
|
||||
private String position;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 推广档口ID
|
||||
*/
|
||||
|
|
@ -75,9 +68,13 @@ public class AdvertRound extends XktBaseEntity {
|
|||
*/
|
||||
private BigDecimal payPrice;
|
||||
/**
|
||||
* 竞价状态
|
||||
* 只要有人出价:这个就被置为 已出价 晚上定时任务改为 竞价成功
|
||||
*/
|
||||
private Integer biddingStatus;
|
||||
/**
|
||||
* 竞价状态 竞价成功 竞价失败的进入到 advert_round_record表中
|
||||
*/
|
||||
private Integer biddingTempStatus;
|
||||
/**
|
||||
* 图片审核状态
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,39 +4,61 @@ import com.baomidou.mybatisplus.annotation.TableId;
|
|||
import com.ruoyi.common.core.domain.XktBaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 广告营销每一轮播放 advert_round_play
|
||||
* 广告营销每一轮竞价历史数据 advert_round_record
|
||||
*
|
||||
* @author liujiang
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AdvertRoundPlay extends XktBaseEntity {
|
||||
@Accessors(chain = true)
|
||||
public class AdvertRoundRecord extends XktBaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 广告轮次播放ID
|
||||
* 广告营销每一轮历史竞价记录ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 推广营销轮次位置ID
|
||||
*/
|
||||
private Long advertRoundId;
|
||||
/**
|
||||
* 广告ID
|
||||
*/
|
||||
private Long advertId;
|
||||
/**
|
||||
* 广告轮次ID
|
||||
* 轮次ID
|
||||
*/
|
||||
private Long advertRoundId;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private Integer roundId;
|
||||
/**
|
||||
* 资源锁标识 位置枚举的推广位精确到A B C D E 等,时间范围的推广位精确到具体类型
|
||||
*/
|
||||
private String symbol;
|
||||
/**
|
||||
* 投放状态
|
||||
*/
|
||||
private Integer launchStatus;
|
||||
/**
|
||||
* 投放开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
/**
|
||||
* 投放结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
/**
|
||||
* 广告位置 A B C D E... 对应advert中的playNum
|
||||
*/
|
||||
private String position;
|
||||
/**
|
||||
* 推广档口ID
|
||||
*/
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.domain;
|
||||
package com.ruoyi.xkt.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.domain;
|
||||
package com.ruoyi.xkt.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.domain;
|
||||
package com.ruoyi.xkt.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.domain;
|
||||
package com.ruoyi.xkt.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.domain;
|
||||
package com.ruoyi.xkt.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
package com.ruoyi.web.controller.xkt.vo.advertRoundPlay;
|
||||
package com.ruoyi.xkt.dto.advertRound;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
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;
|
||||
|
||||
|
|
@ -16,23 +17,25 @@ import java.math.BigDecimal;
|
|||
@ApiModel("档口购买推广营销位")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdPlayStoreCreateVO {
|
||||
public class AdRoundStoreCreateDTO {
|
||||
|
||||
@NotNull(message = "广告ID不能为空!")
|
||||
@ApiModelProperty(value = "广告ID")
|
||||
private Long advertId;
|
||||
@NotNull(message = "广告轮次ID不能为空!")
|
||||
@ApiModelProperty(value = "广告轮次ID")
|
||||
private Long advertRoundId;
|
||||
@NotNull(message = "推广档口ID不能为空!")
|
||||
private Long roundId;
|
||||
@ApiModelProperty(value = "typeId")
|
||||
private Integer typeId;
|
||||
@ApiModelProperty(value = "广告位置 A B C D E")
|
||||
private String position;
|
||||
@ApiModelProperty(value = "推广档口ID")
|
||||
private Long storeId;
|
||||
@NotNull(message = "推广档口出价不能为空!")
|
||||
@ApiModelProperty(value = "推广档口出价")
|
||||
private BigDecimal payPrice;
|
||||
@ApiModelProperty(value = "图片设计(1 自主设计、2 平台设计)")
|
||||
private Integer picDesignType;
|
||||
@ApiModelProperty(value = "推广商品ID列表")
|
||||
private String prodIdStr;
|
||||
@ApiModelProperty(value = "对象锁符号")
|
||||
private String symbol;
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.xkt.dto.advertRoundPlay;
|
||||
package com.ruoyi.xkt.dto.advertRound;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import lombok.Data;
|
||||
|
|
@ -12,7 +12,7 @@ import lombok.experimental.Accessors;
|
|||
@ApiModel("当前类型档口营销推广数据")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdPlayStoreResDTO {
|
||||
public class AdRoundStoreResDTO {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package com.ruoyi.xkt.dto.advertRoundPlay;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @author liujiang
|
||||
* @version v1.0
|
||||
* @date 2025/3/27 15:12
|
||||
*/
|
||||
@ApiModel("档口购买推广营销位")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdPlayStoreCreateDTO {
|
||||
|
||||
@ApiModelProperty(value = "广告ID")
|
||||
private Long advertId;
|
||||
@ApiModelProperty(value = "广告轮次ID")
|
||||
private Long advertRoundId;
|
||||
@ApiModelProperty(value = "推广档口ID")
|
||||
private Long storeId;
|
||||
@ApiModelProperty(value = "推广档口出价")
|
||||
private BigDecimal payPrice;
|
||||
@ApiModelProperty(value = "图片设计(1 自主设计、2 平台设计)")
|
||||
private Integer picDesignType;
|
||||
@ApiModelProperty(value = "推广商品ID列表")
|
||||
private String prodIdStr;
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.dto;
|
||||
package com.ruoyi.xkt.dto.dailySale;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.dto;
|
||||
package com.ruoyi.xkt.dto.dailySale;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.dto;
|
||||
package com.ruoyi.xkt.dto.dailySale;
|
||||
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.quartz.dto;
|
||||
package com.ruoyi.xkt.dto.dailySale;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
|
@ -17,6 +17,8 @@ import java.util.List;
|
|||
@Accessors(chain = true)
|
||||
public class ESProductDTO {
|
||||
|
||||
@ApiModelProperty(value = "档口商品ID")
|
||||
private String storeProdId;
|
||||
@ApiModelProperty(value = "货号")
|
||||
private String prodArtNum;
|
||||
@ApiModelProperty(value = "档口商品分类ID")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package com.ruoyi.xkt.dto.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("档口App基本信息")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class StoreAppResDTO {
|
||||
|
||||
@ApiModelProperty(value = "档口ID")
|
||||
private Long storeId;
|
||||
@ApiModelProperty(value = "档口名称")
|
||||
private String storeName;
|
||||
@ApiModelProperty(value = "是否已关注")
|
||||
private Boolean attention;
|
||||
@ApiModelProperty(value = "标签列表")
|
||||
private List<String> tagList;
|
||||
@ApiModelProperty(value = "档口地址")
|
||||
private String storeAddress;
|
||||
@ApiModelProperty(value = "联系电话")
|
||||
private String contactPhone;
|
||||
@ApiModelProperty(value = "备选联系电话")
|
||||
private String contactBackPhone;
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,8 @@ public class IndexSearchDTO extends BasePageDTO {
|
|||
|
||||
@ApiModelProperty(value = "搜索内容")
|
||||
private String search;
|
||||
@ApiModelProperty(value = "商品状态列表")
|
||||
private List<String> prodStatusList;
|
||||
@ApiModelProperty(value = "一级类目ID列表")
|
||||
private List<String> parCateIdList;
|
||||
@ApiModelProperty(value = "二级类目ID列表")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.ruoyi.xkt.mapper;
|
|||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.xkt.domain.AdvertRound;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
|
|
@ -13,4 +14,15 @@ import org.springframework.stereotype.Repository;
|
|||
@Repository
|
||||
public interface AdvertRoundMapper extends BaseMapper<AdvertRound> {
|
||||
|
||||
/**
|
||||
* 判断当前档口是否超买广告位
|
||||
*
|
||||
* @param advertId 推广位ID
|
||||
* @param roundId 播放轮次ID
|
||||
* @param storeId 档口ID
|
||||
* @param position 位置
|
||||
* @return true 已超 false 未超
|
||||
*/
|
||||
boolean isStallOverBuy(@Param("advertId") Long advertId, @Param("roundId") Long roundId, @Param("storeId") Long storeId, @Param("position") String position);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.xkt.domain.AdvertRoundPlay;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 推广营销轮次Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Repository
|
||||
public interface AdvertRoundPlayMapper extends BaseMapper<AdvertRoundPlay> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.xkt.domain.AdvertRound;
|
||||
import com.ruoyi.xkt.domain.AdvertRoundRecord;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 推广营销轮次历史记录Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Repository
|
||||
public interface AdvertRoundRecordMapper extends BaseMapper<AdvertRoundRecord> {
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.ruoyi.quartz.mapper;
|
||||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.quartz.domain.DailyProdTag;
|
||||
import com.ruoyi.xkt.domain.DailyProdTag;
|
||||
|
||||
/**
|
||||
* 调度任务信息 数据层
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.ruoyi.quartz.mapper;
|
||||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.quartz.domain.DailySaleCustomer;
|
||||
import com.ruoyi.quartz.dto.DailySaleCusDTO;
|
||||
import com.ruoyi.xkt.domain.DailySaleCustomer;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleCusDTO;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.ruoyi.quartz.mapper;
|
||||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.quartz.domain.DailySale;
|
||||
import com.ruoyi.quartz.dto.DailySaleDTO;
|
||||
import com.ruoyi.xkt.domain.DailySale;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleDTO;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
package com.ruoyi.quartz.mapper;
|
||||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.quartz.domain.DailySaleCustomer;
|
||||
import com.ruoyi.quartz.domain.DailySaleProduct;
|
||||
import com.ruoyi.quartz.dto.DailySaleDTO;
|
||||
import com.ruoyi.quartz.dto.DailySaleProdDTO;
|
||||
import com.ruoyi.xkt.domain.DailySaleProduct;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleProdDTO;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.ruoyi.quartz.mapper;
|
||||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.quartz.domain.DailyStoreTag;
|
||||
import com.ruoyi.xkt.domain.DailyStoreTag;
|
||||
|
||||
/**
|
||||
* 调度任务信息 数据层
|
||||
|
|
@ -90,4 +90,10 @@ public interface StoreProductMapper extends BaseMapper<StoreProduct> {
|
|||
*/
|
||||
List<ProductESDTO> selectESDTOList(@Param("idList") List<Long> idList);
|
||||
|
||||
/**
|
||||
* 获取风格列表
|
||||
* @return
|
||||
*/
|
||||
List<String> getStyleList();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.ruoyi.quartz.mapper;
|
||||
package com.ruoyi.xkt.mapper;
|
||||
|
||||
import com.ruoyi.quartz.dto.WeekCateSaleDTO;
|
||||
import com.ruoyi.xkt.dto.dailySale.WeekCateSaleDTO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.ruoyi.xkt.service;
|
||||
|
||||
import com.ruoyi.xkt.dto.advertRoundPlay.AdPlayStoreCreateDTO;
|
||||
import com.ruoyi.xkt.dto.advertRoundPlay.AdPlayStoreResDTO;
|
||||
|
||||
/**
|
||||
* 推广营销Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
public interface IAdvertRoundPlayService {
|
||||
|
||||
/**
|
||||
* 获取当前类型下档口的推广营销数据
|
||||
*
|
||||
* @param storeId 档口ID
|
||||
* @param typeId 推广类型ID
|
||||
* @return AdRoundPlayStoreResDTO
|
||||
*/
|
||||
AdPlayStoreResDTO getStoreAdInfo(Long storeId, Integer typeId);
|
||||
|
||||
/**
|
||||
* 档口购买推广营销
|
||||
*
|
||||
* @param createDTO 购买入参
|
||||
* @return Integer
|
||||
*/
|
||||
Integer create(AdPlayStoreCreateDTO createDTO);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.ruoyi.xkt.service;
|
||||
|
||||
import com.ruoyi.xkt.dto.advertRound.AdRoundStoreCreateDTO;
|
||||
import com.ruoyi.xkt.dto.advertRound.AdRoundStoreResDTO;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* 推广营销Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
public interface IAdvertRoundService {
|
||||
|
||||
/**
|
||||
* 获取当前类型下档口的推广营销数据
|
||||
*
|
||||
* @param storeId 档口ID
|
||||
* @param typeId 推广类型ID
|
||||
* @return AdRoundPlayStoreResDTO
|
||||
*/
|
||||
AdRoundStoreResDTO getStoreAdInfo(Long storeId, Integer typeId);
|
||||
|
||||
/**
|
||||
* 档口购买推广营销
|
||||
*
|
||||
* @param createDTO 购买入参
|
||||
* @return Integer
|
||||
*/
|
||||
Integer create(AdRoundStoreCreateDTO createDTO);
|
||||
|
||||
/**
|
||||
* 初始化每天购买广告的截止时间
|
||||
*
|
||||
* @throws ParseException
|
||||
*/
|
||||
void saveAdvertDeadlineToRedis() throws ParseException;
|
||||
|
||||
/**
|
||||
* 初始化广告竞争的资源锁
|
||||
*/
|
||||
void initAdvertLockMap();
|
||||
|
||||
void test();
|
||||
|
||||
}
|
||||
|
|
@ -97,4 +97,10 @@ public interface IStoreProductService {
|
|||
* @return List<StoreProdFuzzyResPicDTO>
|
||||
*/
|
||||
List<StoreProdFuzzyResPicDTO> fuzzyQueryResPicList(Long storeId, String prodArtNum);
|
||||
|
||||
/**
|
||||
* 获取商品所有的风格
|
||||
* @return
|
||||
*/
|
||||
List<String> getStyleList();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,4 +67,11 @@ public interface IStoreService {
|
|||
*/
|
||||
StoreApproveResDTO getApproveInfo(Long storeId);
|
||||
|
||||
/**
|
||||
* 获取APP档口基本信息
|
||||
*
|
||||
* @param storeId 档口ID
|
||||
* @return StoreAppResDTO
|
||||
*/
|
||||
StoreAppResDTO getAppInfo(Long storeId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
package com.ruoyi.xkt.service.impl;
|
||||
|
||||
import com.ruoyi.xkt.dto.advertRoundPlay.AdPlayStoreCreateDTO;
|
||||
import com.ruoyi.xkt.dto.advertRoundPlay.AdPlayStoreResDTO;
|
||||
import com.ruoyi.xkt.mapper.AdvertRoundPlayMapper;
|
||||
import com.ruoyi.xkt.service.IAdvertRoundPlayService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 推广营销轮次播放Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdvertRoundPlayServiceImpl implements IAdvertRoundPlayService {
|
||||
|
||||
final AdvertRoundPlayMapper adRoundPlayMapper;
|
||||
|
||||
/**
|
||||
* 获取当前类型下档口的推广营销数据
|
||||
*
|
||||
* @param storeId 档口ID
|
||||
* @param typeId 推广类型ID
|
||||
* @return AdRoundPlayStoreResDTO
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public AdPlayStoreResDTO getStoreAdInfo(Long storeId, Integer typeId) {
|
||||
// 先获取所有 投放中 待投放的营销推广
|
||||
// 再判断当前当前与每一轮推广营销中的关系,已出价、竞价失败、竞价成功等
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 档口购买推广营销
|
||||
* 思路:每次筛选出某个类型,价格最低的推广位,然后只操作这行数据。
|
||||
* 若:该行数据已经有其它档口先竞价了。先进行比价。如果出价低于最低价格,则抛出异常:“已经有档口出价更高了噢,请重新出价!”
|
||||
* 若:新出价比原数据价格高,则:a. 给原数据档口创建转移支付单 b.新档口占据改行数据位置。
|
||||
*
|
||||
* 等到晚上10:00,所有档口购买完成。再通过定时任务(11:30),给每个类型,按照价格从高到低,进行排序。
|
||||
*
|
||||
*
|
||||
* LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
|
||||
* wrapper.eq(Product::getType, specificType) // 筛选出特定类型的记录
|
||||
* .orderByAsc(Product::getPrice) // 升序排序,NULL 会在最前面
|
||||
* .last("LIMIT 1"); // 取第一条(即 price 最低或为 null)
|
||||
*
|
||||
* @param createDTO 购买入参
|
||||
* @return Integer
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
|
||||
// TODO 要加锁,必须要锁住,有可能多个档口同时购买同一个推广营销位,导致购买失败,锁必须要做好
|
||||
// TODO 要加锁,必须要锁住,有可能多个档口同时购买同一个推广营销位,导致购买失败,锁必须要做好
|
||||
// TODO 要加锁,必须要锁住,有可能多个档口同时购买同一个推广营销位,导致购买失败,锁必须要做好
|
||||
|
||||
|
||||
public Integer create(AdPlayStoreCreateDTO createDTO) {
|
||||
|
||||
// 判断当前档口出价是否低于最低价格,若是,则提示:“已经有档口出价更高了噢,请重新出价!”
|
||||
|
||||
|
||||
// 要判断当前是多个档口购买一个推广位,没有排序。还是多个档口购买同一个推广位,需要排序。
|
||||
|
||||
|
||||
// 这里还有支付对接的情况!! 如果遇到支付延迟怎么办?
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// TODO 新增档口广告购买时,需要加锁,一定要锁住
|
||||
// TODO 新增档口广告购买时,需要加锁,一定要锁住
|
||||
// TODO 新增档口广告购买时,需要加锁,一定要锁住
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
package com.ruoyi.xkt.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
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.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.xkt.domain.AdvertRound;
|
||||
import com.ruoyi.xkt.domain.AdvertRoundRecord;
|
||||
import com.ruoyi.xkt.dto.advertRound.AdRoundStoreCreateDTO;
|
||||
import com.ruoyi.xkt.dto.advertRound.AdRoundStoreResDTO;
|
||||
import com.ruoyi.xkt.enums.AdBiddingStatus;
|
||||
import com.ruoyi.xkt.enums.AdLaunchStatus;
|
||||
import com.ruoyi.xkt.enums.AdRoundType;
|
||||
import com.ruoyi.xkt.mapper.AdvertRoundMapper;
|
||||
import com.ruoyi.xkt.mapper.AdvertRoundRecordMapper;
|
||||
import com.ruoyi.xkt.service.IAdvertRoundService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 推广营销轮次播放Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
||||
|
||||
final AdvertRoundMapper advertRoundMapper;
|
||||
final AdvertRoundRecordMapper advertRoundRecordMapper;
|
||||
final RedisCache redisCache;
|
||||
|
||||
// 推广营销位锁 key:symbol + roundId 或者 symbol + roundId + position 。value都是new Object()
|
||||
public static Map<String, Object> advertLockMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 项目启动后执行一次,做初始化操作
|
||||
*/
|
||||
@PostConstruct
|
||||
public void initAdvertLockMap() {
|
||||
// 清空advertLockMap,便于多次幂等操作
|
||||
advertLockMap.clear();
|
||||
// 初始化 advertRound的 symbol 锁资源对象
|
||||
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
|
||||
.eq(AdvertRound::getDelFlag, Constants.UNDELETED)
|
||||
.in(AdvertRound::getLaunchStatus, Arrays.asList(AdLaunchStatus.LAUNCHING.getValue(), AdLaunchStatus.UN_LAUNCH.getValue())));
|
||||
if (CollectionUtils.isEmpty(advertRoundList)) {
|
||||
return;
|
||||
}
|
||||
// 依次初始化所资源对象 如果是时间范围的推广位,则锁的是 那一个播放轮;如果是位置枚举则 锁的是某一个具体资源位
|
||||
advertRoundList.stream().collect(Collectors.groupingBy(AdvertRound::getTypeId))
|
||||
.forEach((typeId, roundList) -> roundList
|
||||
// 初始化 锁资源对象
|
||||
.forEach(round -> advertLockMap.putIfAbsent(round.getSymbol(), new Object())));
|
||||
|
||||
System.err.println(advertLockMap);
|
||||
|
||||
}
|
||||
|
||||
public void test() {
|
||||
System.err.println(advertLockMap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前类型下档口的推广营销数据
|
||||
*
|
||||
* @param storeId 档口ID
|
||||
* @param typeId 推广类型ID
|
||||
* @return AdRoundPlayStoreResDTO
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public AdRoundStoreResDTO getStoreAdInfo(Long storeId, Integer typeId) {
|
||||
// 先获取所有 投放中 待投放的营销推广
|
||||
// 再判断当前当前与每一轮推广营销中的关系,已出价、竞价失败、竞价成功等
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 档口购买推广营销
|
||||
* 主要是两个场景:1. 某个广告位(advert_id)某个轮次(round_id)按照出价(pay_price)决定能否购买。[eg: A B C D E]
|
||||
* 2. 某个广告位(advert_id)某个轮次(round_id)某个具体位置(position)按照出价(pay_price)决定能否购买。
|
||||
* 思路:每次筛选出某个类型,价格最低的推广位,然后只操作这行数据
|
||||
* (创建索引:CREATE INDEX idx_advert_round_pay_pos ON advert_round (advert_id, round_id, pay_price, position))。
|
||||
* 若:该行数据已经有其它档口先竞价了。先进行比价。如果出价低于最低价格,则抛出异常:“已经有档口出价更高了噢,请重新出价!”
|
||||
* 若:新出价比原数据价格高,则:a. 给原数据档口创建转移支付单 b.新档口占据该行位置,更新数据。
|
||||
* <p>
|
||||
* 等到晚上10:00,所有档口购买完成。再通过定时任务(11:30),给每个类型,按照价格从高到低,进行排序。并将首页各个位置的广告数据推送到ES中。为空的广告位如何填充??
|
||||
* <p>
|
||||
*
|
||||
* @param createDTO 购买入参
|
||||
* @return Integer
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Integer create(AdRoundStoreCreateDTO createDTO) {
|
||||
|
||||
// 截止时间都是当天 22:00:00,并且只会处理马上播放的这一轮。比如 5.1-5.3,当前为4.30,处理这一轮;当前为5.2,处理这一轮;当前为5.3(最后一天),处理下一轮。
|
||||
if (DateUtils.getTime().compareTo(this.getDeadline(createDTO.getSymbol())) > 0) {
|
||||
throw new ServiceException("竞价结束,已经有档口出价更高了噢!", HttpStatus.ERROR);
|
||||
}
|
||||
|
||||
// 校验position位置是否必传
|
||||
this.isPositionRequired(createDTO);
|
||||
|
||||
// 判断当前推广位的这一轮每个档口可以买几个,不可超买
|
||||
boolean isOverBuy = this.advertRoundMapper.isStallOverBuy(createDTO.getAdvertId(), createDTO.getRoundId(), createDTO.getStoreId(), createDTO.getPosition());
|
||||
if (isOverBuy) {
|
||||
throw new ServiceException("已购买过该推广位,不可超买哦!", HttpStatus.ERROR);
|
||||
}
|
||||
|
||||
|
||||
// 当前营销推广位的锁
|
||||
Object lockObj = Optional.ofNullable(advertLockMap.get(createDTO.getSymbol())).orElseThrow(() -> new ServiceException("symbol不存在!", HttpStatus.ERROR));
|
||||
// 锁当前推广营销位
|
||||
synchronized (lockObj) {
|
||||
LambdaQueryWrapper<AdvertRound> queryWrapper = new LambdaQueryWrapper<AdvertRound>()
|
||||
.eq(AdvertRound::getAdvertId, createDTO.getAdvertId()).eq(AdvertRound::getRoundId, createDTO.getRoundId())
|
||||
.eq(AdvertRound::getDelFlag, Constants.UNDELETED).orderByAsc(AdvertRound::getPayPrice).last("LIMIT 1");
|
||||
AdvertRound minPriceAdvert = Optional.ofNullable(this.advertRoundMapper.selectOne(queryWrapper)).orElseThrow(() -> new ServiceException("获取推广营销位置失败,请联系客服!", HttpStatus.ERROR));
|
||||
// 判断当前档口出价是否低于该推广位最低价格,若是,则提示:"已经有档口出价更高了噢,请重新出价!".
|
||||
if (createDTO.getPayPrice().compareTo(ObjectUtils.defaultIfNull(minPriceAdvert.getPayPrice(), BigDecimal.ZERO)) < 0) {
|
||||
throw new ServiceException("已经有档口出价更高了噢,请重新出价!", HttpStatus.ERROR);
|
||||
}
|
||||
|
||||
// storeId不为空,表明之前有其他的档口竞价
|
||||
if (ObjectUtils.isNotEmpty(minPriceAdvert.getStoreId())) {
|
||||
|
||||
// TODO 将推广费退至原档口余额
|
||||
// TODO 将推广费退至原档口余额
|
||||
|
||||
// 记录竞价失败的档口推广营销
|
||||
this.record(minPriceAdvert);
|
||||
}
|
||||
|
||||
// 更新出价最低的广告位
|
||||
minPriceAdvert.setStoreId(createDTO.getStoreId()).setPayPrice(createDTO.getPayPrice())
|
||||
.setBiddingStatus(AdBiddingStatus.BIDDING.getValue())
|
||||
.setBiddingTempStatus(AdBiddingStatus.BIDDING_SUCCESS.getValue())
|
||||
.setPicDesignType(createDTO.getPicDesignType()).setProdIdStr(createDTO.getProdIdStr());
|
||||
this.advertRoundMapper.updateById(minPriceAdvert);
|
||||
|
||||
// TODO 新竞价成功的档口扣减余额
|
||||
// TODO 新竞价成功的档口扣减余额
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过定时任务往redis中放当前推广位 当前播放轮 或 即将播放轮 的截止时间;
|
||||
* 比如:5.1 - 5.3
|
||||
* a. 现在是4.30 则截止时间是 4.30 22:00
|
||||
* b. 现在是5.2,则截止时间是 5.2 22:00:00 。
|
||||
* c. 现在是5.3,则第一轮还有请求,肯定是人为的不用管。请求第三轮 或者 第四轮 不报错。只处理第二轮请求
|
||||
*
|
||||
* @throws ParseException
|
||||
*/
|
||||
public void saveAdvertDeadlineToRedis() throws ParseException {
|
||||
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
|
||||
.eq(AdvertRound::getDelFlag, Constants.UNDELETED)
|
||||
.in(AdvertRound::getLaunchStatus, Arrays.asList(AdLaunchStatus.LAUNCHING.getValue(), AdLaunchStatus.UN_LAUNCH.getValue()))
|
||||
.in(AdvertRound::getRoundId, Arrays.asList(AdRoundType.PLAY_ROUND.getValue(), AdRoundType.SECOND_ROUND.getValue())));
|
||||
if (CollectionUtils.isEmpty(advertRoundList)) {
|
||||
return;
|
||||
}
|
||||
// 服务器当前时间 yyyy-MM-dd
|
||||
final Date now = DateUtils.parseDate(DateUtils.getDate(), DateUtils.YYYY_MM_DD);
|
||||
// 当天截止的时间 yyyy-MM-dd 22:00:00
|
||||
final String filterTime = DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)
|
||||
.format(LocalDateTime.now().withHour(22).withMinute(0).withSecond(0));
|
||||
advertRoundList.stream().collect(Collectors.groupingBy(AdvertRound::getAdvertId))
|
||||
.forEach((advertId, roundList) -> {
|
||||
// 判断当前时间所处的阶段 小于第一轮播放时间(有可能新广告还未开播)、处于第一轮中间、处于第一轮最后一天
|
||||
final Date firstRoundEndTime = roundList.stream().filter(x -> x.getRoundId().equals(AdRoundType.PLAY_ROUND.getValue()))
|
||||
.max(Comparator.comparing(AdvertRound::getEndTime))
|
||||
.orElseThrow(() -> new ServiceException("获取推广结束时间失败,请联系客服!", HttpStatus.ERROR)).getEndTime();
|
||||
// 默认第一轮的symbol
|
||||
String symbol = roundList.stream().filter(x -> x.getRoundId().equals(AdRoundType.PLAY_ROUND.getValue()))
|
||||
.map(AdvertRound::getSymbol).findAny().orElseThrow(() -> new ServiceException("获取推广第一轮symbol失败,请联系客服!", HttpStatus.ERROR));
|
||||
// 第一轮最后一天,则直接处理第二轮的symbol
|
||||
if (now.equals(firstRoundEndTime)) {
|
||||
String secondRoundSymbol = roundList.stream().filter(x -> x.getRoundId().equals(AdRoundType.SECOND_ROUND.getValue()))
|
||||
.map(AdvertRound::getSymbol).findAny().orElse(null);
|
||||
// 有可能第二轮不存在,因为该推广已下线,则定时任务没有生成后续轮次
|
||||
if (StringUtils.isEmpty(secondRoundSymbol)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 作用于某一轮推广的symbol(包含了播放轮次的)
|
||||
/*String symbol = now.before(firstRoundEndTime)
|
||||
? roundList.stream().filter(x -> x.getRoundId().equals(AdRoundType.PLAY_ROUND.getValue()))
|
||||
.map(AdvertRound::getSymbol).findAny().orElseThrow(() -> new ServiceException("获取推广第一轮symbol失败,请联系客服!", HttpStatus.ERROR))
|
||||
: roundList.stream().filter(x -> x.getRoundId().equals(AdRoundType.SECOND_ROUND.getValue()))
|
||||
.map(AdvertRound::getSymbol).findAny().orElseThrow(() -> new ServiceException("获取推广第二轮symbol失败,请联系客服!", HttpStatus.ERROR));*/
|
||||
// 存放到redis中 有效期90分钟
|
||||
redisCache.setCacheObject(symbol, filterTime, 90, TimeUnit.MINUTES);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前类型position是否必传
|
||||
* @param createDTO 购买广告位入参
|
||||
*/
|
||||
private void isPositionRequired(AdRoundStoreCreateDTO createDTO) {
|
||||
// 如果是位置枚举的推广位,则需要传position
|
||||
if (Constants.posEnumTypeList.contains(createDTO.getTypeId()) && StringUtils.isEmpty(createDTO.getPosition())) {
|
||||
throw new ServiceException("当前推广类型position:必传!", HttpStatus.ERROR);
|
||||
}
|
||||
if (!Constants.posEnumTypeList.contains(createDTO.getTypeId()) && StringUtils.isNotBlank(createDTO.getPosition())) {
|
||||
throw new ServiceException("当前推广类型position:不传!", HttpStatus.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前推广轮次的过期时间
|
||||
* @param symbol 符号
|
||||
* @return
|
||||
*/
|
||||
private String getDeadline(String symbol) {
|
||||
final String deadline = redisCache.getCacheObject(symbol);
|
||||
return StringUtils.isNotBlank(deadline) ? deadline : "";
|
||||
}
|
||||
|
||||
// TODO 在每晚11:30起定时任务,重新梳理,每个广告位的大小顺序
|
||||
// TODO 在每晚11:30起定时任务,重新梳理,每个广告位的大小顺序
|
||||
|
||||
|
||||
/**
|
||||
* 记录竞价失败档口推广营销
|
||||
* @param failAdvert 竞价失败的推广营销
|
||||
*/
|
||||
private void record(AdvertRound failAdvert) {
|
||||
// 新增推广营销历史记录 将旧档口推广营销 置为竞价失败
|
||||
AdvertRoundRecord record = BeanUtil.toBean(failAdvert, AdvertRoundRecord.class);
|
||||
record.setId(null);
|
||||
record.setAdvertRoundId(failAdvert.getId());
|
||||
// 置为竞价失败
|
||||
record.setBiddingStatus(AdBiddingStatus.BIDDING_FAIL.getValue());
|
||||
this.advertRoundRecordMapper.insert(record);
|
||||
}
|
||||
|
||||
|
||||
// TODO 新增档口广告购买时,需要加锁,一定要锁住
|
||||
// TODO 新增档口广告购买时,需要加锁,一定要锁住
|
||||
// TODO 新增档口广告购买时,需要加锁,一定要锁住
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.xkt.service.impl;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
|
|
@ -16,7 +17,7 @@ import com.ruoyi.xkt.dto.advert.*;
|
|||
import com.ruoyi.xkt.enums.AdOnlineStatus;
|
||||
import com.ruoyi.xkt.enums.AdPlatformType;
|
||||
import com.ruoyi.xkt.enums.AdTab;
|
||||
import com.ruoyi.xkt.enums.AdType;
|
||||
import com.ruoyi.common.enums.AdType;
|
||||
import com.ruoyi.xkt.mapper.AdvertMapper;
|
||||
import com.ruoyi.xkt.mapper.SysFileMapper;
|
||||
import com.ruoyi.xkt.service.IAdvertService;
|
||||
|
|
@ -64,6 +65,7 @@ public class AdvertServiceImpl implements IAdvertService {
|
|||
SysFile file = BeanUtil.toBean(createDTO.getExample(), SysFile.class);
|
||||
this.fileMapper.insert(file);
|
||||
Advert advert = BeanUtil.toBean(createDTO, Advert.class);
|
||||
advert.setBasicSymbol(random10Str());
|
||||
advert.setOnlineStatus(AdOnlineStatus.ONLINE.getValue());
|
||||
advert.setExamplePicId(file.getId());
|
||||
return this.advertMapper.insert(advert);
|
||||
|
|
@ -222,4 +224,24 @@ public class AdvertServiceImpl implements IAdvertService {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 随机生成10位,包含大小写字母、数字的字符串
|
||||
* @return
|
||||
*/
|
||||
public static String random10Str() {
|
||||
final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
+ "abcdefghijklmnopqrstuvwxyz"
|
||||
+ "0123456789";
|
||||
final int STRING_LENGTH = 10;
|
||||
SecureRandom random = new SecureRandom();
|
||||
StringBuilder sb = new StringBuilder(STRING_LENGTH);
|
||||
for (int i = 0; i < STRING_LENGTH; i++) {
|
||||
int index = random.nextInt(CHARACTERS.length());
|
||||
sb.append(CHARACTERS.charAt(index));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,17 +8,22 @@ 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.common.utils.DateUtils;
|
||||
import com.ruoyi.framework.es.EsClientWrapper;
|
||||
import com.ruoyi.xkt.dto.es.ESProductDTO;
|
||||
import com.ruoyi.xkt.dto.website.IndexSearchDTO;
|
||||
import com.ruoyi.xkt.service.IIndexSearchService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -58,7 +63,7 @@ public class IndexSearchServiceImpl implements IIndexSearchService {
|
|||
// 构建 bool 查询
|
||||
BoolQuery.Builder boolQuery = new BoolQuery.Builder();
|
||||
// 添加 price 范围查询
|
||||
if (searchDTO.getMinPrice() != null && searchDTO.getMaxPrice() != null) {
|
||||
if (ObjectUtils.isNotEmpty(searchDTO.getMinPrice()) && ObjectUtils.isNotEmpty(searchDTO.getMaxPrice())) {
|
||||
RangeQuery.Builder builder = new RangeQuery.Builder();
|
||||
builder.number(NumberRangeQuery.of(n -> n.field("prodPrice").gte(Double.valueOf(searchDTO.getMinPrice()))
|
||||
.lte(Double.valueOf(searchDTO.getMaxPrice()))));
|
||||
|
|
@ -68,12 +73,21 @@ public class IndexSearchServiceImpl implements IIndexSearchService {
|
|||
if (StringUtils.isNotBlank(searchDTO.getSearch())) {
|
||||
MultiMatchQuery multiMatchQuery = MultiMatchQuery.of(m -> m
|
||||
.query(searchDTO.getSearch())
|
||||
.fields("prodTitle", "prodArtNum", "storeName")
|
||||
.fields("prodTitle", "prodArtNum", "storeName", "prodCateName", "parCateName")
|
||||
);
|
||||
boolQuery.must(multiMatchQuery._toQuery());
|
||||
}
|
||||
// 添加prodStatus 过滤条件
|
||||
if (CollectionUtils.isNotEmpty(searchDTO.getProdStatusList())) {
|
||||
TermsQueryField termsQueryField = new TermsQueryField.Builder()
|
||||
.value(searchDTO.getProdStatusList().stream()
|
||||
.map(IndexSearchServiceImpl::newFieldValue)
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
boolQuery.filter(f -> f.terms(t -> t.field("prodStatus").terms(termsQueryField)));
|
||||
}
|
||||
// 添加 prodCateId 过滤条件
|
||||
if (searchDTO.getProdCateIdList() != null && !searchDTO.getProdCateIdList().isEmpty()) {
|
||||
if (CollectionUtils.isNotEmpty(searchDTO.getProdCateIdList())) {
|
||||
TermsQueryField termsQueryField = new TermsQueryField.Builder()
|
||||
.value(searchDTO.getProdCateIdList().stream()
|
||||
.map(IndexSearchServiceImpl::newFieldValue)
|
||||
|
|
@ -82,7 +96,7 @@ public class IndexSearchServiceImpl implements IIndexSearchService {
|
|||
boolQuery.filter(f -> f.terms(t -> t.field("prodCateId").terms(termsQueryField)));
|
||||
}
|
||||
// 添加 parCateId 过滤条件
|
||||
if (searchDTO.getParCateIdList() != null && !searchDTO.getParCateIdList().isEmpty()) {
|
||||
if (CollectionUtils.isNotEmpty(searchDTO.getParCateIdList())) {
|
||||
TermsQueryField termsQueryField = new TermsQueryField.Builder()
|
||||
.value(searchDTO.getParCateIdList().stream()
|
||||
.map(IndexSearchServiceImpl::newFieldValue)
|
||||
|
|
@ -91,7 +105,7 @@ public class IndexSearchServiceImpl implements IIndexSearchService {
|
|||
boolQuery.filter(f -> f.terms(t -> t.field("parCateId").terms(termsQueryField)));
|
||||
}
|
||||
// 添加 style 过滤条件
|
||||
if (searchDTO.getStyleList() != null && !searchDTO.getStyleList().isEmpty()) {
|
||||
if (CollectionUtils.isNotEmpty(searchDTO.getStyleList())) {
|
||||
TermsQueryField termsQueryField = new TermsQueryField.Builder()
|
||||
.value(searchDTO.getStyleList().stream()
|
||||
.map(IndexSearchServiceImpl::newFieldValue)
|
||||
|
|
@ -100,7 +114,7 @@ public class IndexSearchServiceImpl implements IIndexSearchService {
|
|||
boolQuery.filter(f -> f.terms(t -> t.field("style.keyword").terms(termsQueryField)));
|
||||
}
|
||||
// 添加 season 过滤条件
|
||||
if (searchDTO.getSeasonList() != null && !searchDTO.getSeasonList().isEmpty()) {
|
||||
if (CollectionUtils.isNotEmpty(searchDTO.getSeasonList())) {
|
||||
TermsQueryField termsQueryField = new TermsQueryField.Builder()
|
||||
.value(searchDTO.getSeasonList().stream()
|
||||
.map(IndexSearchServiceImpl::newFieldValue)
|
||||
|
|
@ -108,6 +122,20 @@ public class IndexSearchServiceImpl implements IIndexSearchService {
|
|||
.build();
|
||||
boolQuery.filter(f -> f.terms(t -> t.field("season.keyword").terms(termsQueryField)));
|
||||
}
|
||||
|
||||
// 如果是按照时间过滤,则表明是“新品”,则限制 时间范围 20天前到现在
|
||||
if (Objects.equals(searchDTO.getSort(), "createTime")) {
|
||||
// 当前时间
|
||||
final String nowStr = DateUtils.getTime();
|
||||
// 当前时间往前推20天,获取当天的0点0分0秒
|
||||
LocalDateTime ago = LocalDateTime.now().minusDays(20).withHour(0).withMinute(0).withSecond(0);
|
||||
// ago 转化为 yyyy-MM-dd HH:mm:ss
|
||||
String agoStr = ago.format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS));
|
||||
RangeQuery.Builder builder = new RangeQuery.Builder();
|
||||
builder.date(DateRangeQuery.of(d -> d.field("createTime").gte(agoStr).lte(nowStr)));
|
||||
boolQuery.filter(builder.build()._toQuery());
|
||||
}
|
||||
|
||||
// 构建最终的查询
|
||||
Query query = new Query.Builder().bool(boolQuery.build()).build();
|
||||
// 执行搜索
|
||||
|
|
@ -115,8 +143,12 @@ public class IndexSearchServiceImpl implements IIndexSearchService {
|
|||
.query(query).from(searchDTO.getPageNum() - 1).size(searchDTO.getPageSize())
|
||||
.sort(sort -> sort.field(f -> f.field(searchDTO.getSort()).order(SortOrder.Desc))),
|
||||
ESProductDTO.class);
|
||||
|
||||
System.err.println(resList);
|
||||
|
||||
return CollectionUtils.isEmpty(resList.hits().hits()) ? Page.empty(searchDTO.getPageSize(), searchDTO.getPageNum())
|
||||
: Page.convert(new PageInfo<>(resList.hits().hits().stream().map(Hit::source).collect(Collectors.toList())));
|
||||
: Page.convert(new PageInfo<>(resList.hits().hits().stream().map(x -> x.source().setStoreProdId(x.id())).collect(Collectors.toList())));
|
||||
// : Page.convert(new PageInfo<>(resList.hits().hits().stream().map(Hit::source).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
private static FieldValue newFieldValue(String value) {
|
||||
|
|
|
|||
|
|
@ -457,6 +457,17 @@ public class StoreProductServiceImpl implements IStoreProductService {
|
|||
return this.storeProdMapper.fuzzyQueryResPicList(storeId, prodArtNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品所有的风格
|
||||
*
|
||||
* @return List<String>
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<String> getStyleList() {
|
||||
return this.storeProdMapper.getStyleList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理档口商品属性
|
||||
*
|
||||
|
|
|
|||
|
|
@ -6,18 +6,22 @@ import com.github.pagehelper.PageHelper;
|
|||
import com.github.pagehelper.PageInfo;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.page.Page;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.system.mapper.SysUserMapper;
|
||||
import com.ruoyi.xkt.domain.DailyStoreTag;
|
||||
import com.ruoyi.xkt.domain.Store;
|
||||
import com.ruoyi.xkt.domain.UserSubscriptions;
|
||||
import com.ruoyi.xkt.dto.store.*;
|
||||
import com.ruoyi.xkt.dto.storeCertificate.StoreCertResDTO;
|
||||
import com.ruoyi.xkt.enums.StoreStatus;
|
||||
import com.ruoyi.xkt.mapper.DailyStoreTagMapper;
|
||||
import com.ruoyi.xkt.mapper.StoreMapper;
|
||||
import com.ruoyi.xkt.mapper.UserSubscriptionsMapper;
|
||||
import com.ruoyi.xkt.service.IStoreCertificateService;
|
||||
import com.ruoyi.xkt.service.IStoreService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
|
@ -27,6 +31,7 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 档口Service业务层处理
|
||||
|
|
@ -41,6 +46,8 @@ public class StoreServiceImpl implements IStoreService {
|
|||
final StoreMapper storeMapper;
|
||||
final IStoreCertificateService storeCertService;
|
||||
final SysUserMapper userMapper;
|
||||
final DailyStoreTagMapper storeTagMapper;
|
||||
final UserSubscriptionsMapper userSubMapper;
|
||||
|
||||
/**
|
||||
* 注册时新增档口数据
|
||||
|
|
@ -158,6 +165,31 @@ public class StoreServiceImpl implements IStoreService {
|
|||
}};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取APP档口基本信息
|
||||
*
|
||||
* @param storeId 档口ID
|
||||
* @return StoreAppResDTO
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public StoreAppResDTO getAppInfo(Long storeId) {
|
||||
Store store = Optional.ofNullable(this.storeMapper.selectOne(new LambdaQueryWrapper<Store>()
|
||||
.eq(Store::getId, storeId).eq(Store::getDelFlag, Constants.UNDELETED)))
|
||||
.orElseThrow(() -> new ServiceException("档口不存在!", HttpStatus.ERROR));
|
||||
// 获取档口的标签
|
||||
List<DailyStoreTag> storeTagList = this.storeTagMapper.selectList(new LambdaQueryWrapper<DailyStoreTag>()
|
||||
.eq(DailyStoreTag::getStoreId, storeId).eq(DailyStoreTag::getDelFlag, Constants.UNDELETED)
|
||||
.orderByAsc(DailyStoreTag::getType));
|
||||
// 判断当前用户是否已关注档口
|
||||
UserSubscriptions userSub = this.userSubMapper.selectOne(new LambdaQueryWrapper<UserSubscriptions>()
|
||||
.eq(UserSubscriptions::getUserId, SecurityUtils.getUserId()).eq(UserSubscriptions::getStoreId, storeId)
|
||||
.eq(UserSubscriptions::getDelFlag, Constants.UNDELETED));
|
||||
return BeanUtil.toBean(store, StoreAppResDTO.class)
|
||||
.setAttention(ObjectUtils.isNotEmpty(userSub) ? Boolean.TRUE : Boolean.FALSE)
|
||||
.setTagList(storeTagList.stream().map(DailyStoreTag::getTag).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改档口基本信息
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
<?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.ruoyi.xkt.mapper.AdvertRoundMapper">
|
||||
|
||||
<select id="isStallOverBuy" >
|
||||
SELECT
|
||||
CASE
|
||||
WHEN
|
||||
COUNT( id ) >= ( SELECT IFNULL( store_buy_limit, 0 ) FROM advert WHERE id = 1 ) THEN
|
||||
TRUE ELSE FALSE
|
||||
END AS isOverBuy
|
||||
FROM
|
||||
advert_round
|
||||
WHERE
|
||||
round_id = #{roundId}
|
||||
AND advert_id = #{advertId}
|
||||
AND store_id = #{storeId}
|
||||
AND del_flag = 0
|
||||
<if test="position != null and position != ''"> and position = #{position}</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -209,4 +209,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</select>
|
||||
|
||||
|
||||
<select id="getStyleList" >
|
||||
SELECT DISTINCT
|
||||
dict_value
|
||||
FROM
|
||||
store_product_category_attribute
|
||||
WHERE
|
||||
dict_type = 'style'
|
||||
AND del_flag = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue