master:档口购买推广位功能v1.0

pull/1121/head
liujiang 2025-05-07 16:14:30 +08:00
parent 59ab724173
commit b33cf81155
58 changed files with 886 additions and 277 deletions

View File

@ -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 {
}
}

View File

@ -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

View File

@ -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));
}
}

View File

@ -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());
}
/**
*
*/

View File

@ -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;
}

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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列表")

View File

@ -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());
}};
}

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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:00redis广
* 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();
}
/**
*
*

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -68,4 +68,12 @@ public interface ISysProductCategoryService {
*/
List<AppHomeProdCateListResDTO> appCate();
/**
* 1
*
* @param parCateId ID
* @return
*/
List<ProdCateDTO> getSubList(Long parCateId);
}

View File

@ -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);
}
/**
*

View File

@ -25,6 +25,14 @@ public class Advert extends XktBaseEntity {
*/
@TableId
private Long id;
/**
* 广 10
*/
private String basicSymbol;
/**
* 广
*/
private Integer storeBuyLimit;
/**
* 广 APP
*/

View File

@ -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... advertplayNum
*/
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;
/**
*
*/

View File

@ -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... advertplayNum
*/
private String position;
/**
* 广ID
*/

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
package com.ruoyi.quartz.dto;
package com.ruoyi.xkt.dto.dailySale;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.ruoyi.quartz.dto;
package com.ruoyi.xkt.dto.dailySale;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.ruoyi.quartz.dto;
package com.ruoyi.xkt.dto.dailySale;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.ruoyi.quartz.dto;
package com.ruoyi.xkt.dto.dailySale;
import lombok.Data;

View File

@ -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")

View File

@ -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;
}

View File

@ -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列表")

View File

@ -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);
}

View File

@ -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> {
}

View File

@ -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> {
}

View File

@ -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;
/**
*

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
/**
*

View File

@ -90,4 +90,10 @@ public interface StoreProductMapper extends BaseMapper<StoreProduct> {
*/
List<ProductESDTO> selectESDTOList(@Param("idList") List<Long> idList);
/**
*
* @return
*/
List<String> getStyleList();
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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();
}

View File

@ -97,4 +97,10 @@ public interface IStoreProductService {
* @return List<StoreProdFuzzyResPicDTO>
*/
List<StoreProdFuzzyResPicDTO> fuzzyQueryResPicList(Long storeId, String prodArtNum);
/**
*
* @return
*/
List<String> getStyleList();
}

View File

@ -67,4 +67,11 @@ public interface IStoreService {
*/
StoreApproveResDTO getApproveInfo(Long storeId);
/**
* APP
*
* @param storeId ID
* @return StoreAppResDTO
*/
StoreAppResDTO getAppInfo(Long storeId);
}

View File

@ -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:0011: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 新增档口广告购买时,需要加锁,一定要锁住
}

View File

@ -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;
// 推广营销位锁 keysymbol + 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_idround_idpay_price[eg: A B C D E]
* 2. 广advert_idround_idpositionpay_price
* 广
* CREATE INDEX idx_advert_round_pay_pos ON advert_round (advert_id, round_id, pay_price, position)
* !
* a. b.
* <p>
* 10:0011: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 新增档口广告购买时,需要加锁,一定要锁住
}

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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();
}
/**
*
*

View File

@ -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()));
}
/**
*
*

View File

@ -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>

View File

@ -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>