master:推广营销完善;
parent
2b1da50167
commit
09e66a856a
|
|
@ -16,6 +16,7 @@ import org.springframework.validation.annotation.Validated;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +39,7 @@ public class AdvertRoundController extends XktBaseController {
|
|||
@ApiOperation(value = "档口购买推广营销", httpMethod = "POST", response = R.class)
|
||||
@Log(title = "档口购买推广营销", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public R<Integer> create(@Validated @RequestBody AdRoundStoreCreateVO createVO) {
|
||||
public R<Integer> create(@Validated @RequestBody AdRoundStoreCreateVO createVO) throws ParseException {
|
||||
return R.ok(advertRoundService.create(BeanUtil.toBean(createVO, AdRoundStoreCreateDTO.class)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
package com.ruoyi.web.controller.xkt.vo.advertRound;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author liujiang
|
||||
|
|
@ -28,11 +26,9 @@ public class AdRoundPopularResVO {
|
|||
@ApiModelProperty(value = "推广展示类型")
|
||||
private Integer showType;
|
||||
@ApiModelProperty(value = "投放开始时间")
|
||||
@JsonFormat(pattern = "MM月dd日", timezone = "GMT+8")
|
||||
private Date startTime;
|
||||
private String startTime;
|
||||
@ApiModelProperty(value = "投放结束时间")
|
||||
@JsonFormat(pattern = "MM月dd日", timezone = "GMT+8")
|
||||
private Date endTime;
|
||||
private String endTime;
|
||||
@ApiModelProperty(value = "起拍价格")
|
||||
private BigDecimal startPrice;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,4 +46,15 @@ public class CacheConstants
|
|||
* 用户STS
|
||||
*/
|
||||
public static final String USER_STS_KEY = "user_sts:";
|
||||
|
||||
/**
|
||||
* 档口
|
||||
*/
|
||||
public static final String STORE_KEY = "store:";
|
||||
|
||||
/**
|
||||
* 推广购买截止时间
|
||||
*/
|
||||
public static final String ADVERT_DEADLINE_KEY = "advert_deadline:";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,14 +224,9 @@ public class Constants
|
|||
*/
|
||||
public static final Long TOPMOST_PRODUCT_CATEGORY_ID = 1L;
|
||||
|
||||
/**
|
||||
* redis store前缀
|
||||
*/
|
||||
public static final String STORE_REDIS_PREFIX = "store_";
|
||||
|
||||
/**
|
||||
* 最受欢迎的8个推广位
|
||||
*/
|
||||
public static final String ADVERT_POPULAR = "popular";
|
||||
public static final String ADVERT_POPULAR = "ADVERT_POPULAR";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
|||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static String MM_DD = "MM月dd日";
|
||||
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static String YYYYMMDD = "yyyyMMdd";
|
||||
|
|
@ -75,6 +77,11 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
|||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static final String timeMMDD(final Date date)
|
||||
{
|
||||
return parseDateToStr(MM_DD, date);
|
||||
}
|
||||
|
||||
public static final String parseDateToStr(final String format, final Date date)
|
||||
{
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
|||
import co.elastic.clients.elasticsearch.core.BulkResponse;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.domain.entity.SysProductCategory;
|
||||
|
|
@ -298,7 +299,7 @@ public class XktTask {
|
|||
}
|
||||
|
||||
/**
|
||||
* 每晚定时任务更新推广的播放轮次,这个要次日凌晨更新
|
||||
* 定时任务更新推广的播放轮次,这个要次日凌晨更新 凌晨12:01:00分更新
|
||||
*/
|
||||
@Transactional
|
||||
public void dailyAdvertRound() throws ParseException {
|
||||
|
|
@ -438,7 +439,7 @@ public class XktTask {
|
|||
* 比如:5.1 - 5.3
|
||||
* a. 现在是4.30 则截止时间是 4.30 22:00
|
||||
* b. 现在是5.2,则截止时间是 5.2 22:00:00 。
|
||||
* c. 现在是5.3,则第一轮还有请求,肯定是人为的不用管。请求第三轮 或者 第四轮 不报错。只处理第二轮请求
|
||||
* c. 现在是5.3,则第一轮还有请求,肯定是人为的不用管。每一轮都要放到redis中
|
||||
*
|
||||
* @throws ParseException
|
||||
*/
|
||||
|
|
@ -457,7 +458,7 @@ public class XktTask {
|
|||
return;
|
||||
}
|
||||
storeList.forEach(store -> {
|
||||
redisCache.setCacheObject(Constants.STORE_REDIS_PREFIX + store.getId(), store.getId(), 1, TimeUnit.DAYS);
|
||||
redisCache.setCacheObject(CacheConstants.STORE_KEY + store.getId(), store.getId(), 1, TimeUnit.DAYS);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.xkt.dto.advertRound;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
|
@ -27,9 +28,9 @@ public class AdRoundPopularResDTO {
|
|||
@ApiModelProperty(value = "推广展示类型")
|
||||
private Integer showType;
|
||||
@ApiModelProperty(value = "投放开始时间")
|
||||
private Date startTime;
|
||||
private String startTime;
|
||||
@ApiModelProperty(value = "投放结束时间")
|
||||
private Date endTime;
|
||||
private String endTime;
|
||||
@ApiModelProperty(value = "起拍价格")
|
||||
private BigDecimal startPrice;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public interface IAdvertRoundService {
|
|||
* @param createDTO 购买入参
|
||||
* @return Integer
|
||||
*/
|
||||
Integer create(AdRoundStoreCreateDTO createDTO);
|
||||
Integer create(AdRoundStoreCreateDTO createDTO) throws ParseException;
|
||||
|
||||
/**
|
||||
* 初始化每天购买广告的截止时间
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.page.Page;
|
||||
|
|
@ -40,6 +41,7 @@ import java.util.function.BinaryOperator;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.ruoyi.common.constant.CacheConstants.ADVERT_DEADLINE_KEY;
|
||||
import static com.ruoyi.common.constant.Constants.ADVERT_POPULAR;
|
||||
|
||||
/**
|
||||
|
|
@ -329,12 +331,12 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Integer create(AdRoundStoreCreateDTO createDTO) {
|
||||
public Integer create(AdRoundStoreCreateDTO createDTO) throws ParseException {
|
||||
// 截止时间都是当天 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);
|
||||
}
|
||||
if (ObjectUtils.isEmpty(redisCache.getCacheObject(Constants.STORE_REDIS_PREFIX + createDTO.getStoreId()))) {
|
||||
if (ObjectUtils.isEmpty(redisCache.getCacheObject(CacheConstants.STORE_KEY + createDTO.getStoreId()))) {
|
||||
throw new ServiceException("档口不存在!", HttpStatus.ERROR);
|
||||
}
|
||||
// 如果是位置枚举的推广位,则需要传position
|
||||
|
|
@ -468,7 +470,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
// 判断当前时间距离开播是否小于72h,若是:则不可取消
|
||||
Date threeDaysAfter = DateUtils.toDate(LocalDateTime.now().plusDays(3));
|
||||
if (threeDaysAfter.after(advertRound.getStartTime())) {
|
||||
throw new ServiceException("距推广开播小于72小时,不可退订!");
|
||||
throw new ServiceException("距推广开播小于72小时,不可退订噢!");
|
||||
}
|
||||
// 将费用退回到档口余额中
|
||||
assetService.refundAdvertFee(advertRound.getStoreId(), advertRound.getPayPrice());
|
||||
|
|
@ -503,8 +505,8 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
.groupingBy(AdvertRound::getAdvertId, Collectors.minBy(Comparator.comparing(AdvertRound::getRoundId))));
|
||||
// 将minRoundIdMap中的值转换为List<AdRoundPopularResDTO>
|
||||
List<AdRoundPopularResDTO> list = minRoundIdMap.values().stream().map(Optional::get).map(x -> new AdRoundPopularResDTO().setAdvertId(x.getAdvertId())
|
||||
.setTypeId(x.getTypeId()).setTypeName(AdType.of(x.getTypeId()).getLabel()).setShowType(x.getShowType()).setStartTime(x.getStartTime())
|
||||
.setEndTime(x.getEndTime()).setStartPrice(x.getStartPrice()))
|
||||
.setTypeId(x.getTypeId()).setTypeName(AdType.of(x.getTypeId()).getLabel()).setShowType(x.getShowType())
|
||||
.setStartTime(DateUtils.timeMMDD(x.getStartTime())).setEndTime(DateUtils.timeMMDD(x.getEndTime())).setStartPrice(x.getStartPrice()))
|
||||
.collect(Collectors.toList());
|
||||
// 存到redis中
|
||||
redisCache.setCacheObject(ADVERT_POPULAR, list, 1, TimeUnit.DAYS);
|
||||
|
|
@ -581,37 +583,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取已抢购推广位列表
|
||||
*
|
||||
* @param storeId 档口ID
|
||||
* @param advertRoundList 播放轮次列表
|
||||
* @param recordList 未购买的播放轮次列表
|
||||
* @return List<AdRoundStoreResDTO.ADRSRoundRecordDTO>
|
||||
*/
|
||||
private List<AdRoundStoreResDTO.ADRSRoundRecordDTO> getStoreBoughtList(Long storeId, List<AdvertRound> advertRoundList, List<AdvertRoundRecord> recordList) {
|
||||
// 每一轮最高的出价map
|
||||
Map<Integer, BigDecimal> maxBidMap = advertRoundList.stream().filter(x -> ObjectUtils.isNotEmpty(x.getPayPrice())).collect(Collectors
|
||||
.groupingBy(AdvertRound::getRoundId, Collectors
|
||||
.mapping(AdvertRound::getPayPrice, Collectors.reducing(BigDecimal.ZERO, BigDecimal::max))));
|
||||
// 先处理 已抢购广告位列表,此处不用管 播放的轮次 统一展示前4轮的结果
|
||||
List<AdRoundStoreResDTO.ADRSRoundRecordDTO> boughtRoundList = advertRoundList.stream().filter(x -> Objects.equals(x.getStoreId(), storeId))
|
||||
.map(x -> BeanUtil.toBean(x, AdRoundStoreResDTO.ADRSRoundRecordDTO.class).setTypeName(Objects.requireNonNull(AdType.of(x.getTypeId())).getLabel())
|
||||
.setBiddingStatusName(Objects.requireNonNull(AdBiddingStatus.of(x.getBiddingStatus())).getLabel()))
|
||||
.collect(Collectors.toList());
|
||||
// 查询其它轮次是否有购买记录
|
||||
if (CollectionUtils.isEmpty(recordList)) {
|
||||
return boughtRoundList;
|
||||
}
|
||||
boughtRoundList.addAll(recordList.stream().sorted(Comparator.comparing(AdvertRoundRecord::getBiddingStatus))
|
||||
.map(x -> BeanUtil.toBean(x, AdRoundStoreResDTO.ADRSRoundRecordDTO.class).setTypeName(Objects.requireNonNull(AdType.of(x.getTypeId())).getLabel())
|
||||
.setBiddingStatusName(Objects.requireNonNull(AdBiddingStatus.of(x.getBiddingStatus())).getLabel() + ",最新出价:" + maxBidMap.get(x.getRoundId())))
|
||||
.collect(Collectors.toList()));
|
||||
return boughtRoundList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过定时任务往redis中放当前推广位 当前播放轮 或 即将播放轮 的截止时间;
|
||||
* 通过定时任务往redis中放当前推广位 当前播放轮 或 即将播放轮 的截止时间;每晚12:05:00执行
|
||||
* 比如:5.1 - 5.3
|
||||
* a. 现在是4.30 则截止时间是 4.30 22:00
|
||||
* b. 现在是5.2,则截止时间是 5.2 22:00:00 。
|
||||
|
|
@ -645,7 +617,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
AdvertRound firstRound = roundList.stream().filter(x -> Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue()))
|
||||
.max(Comparator.comparing(AdvertRound::getEndTime))
|
||||
.orElseThrow(() -> new ServiceException("获取推广结束时间失败,请联系客服!", HttpStatus.ERROR));
|
||||
redisCache.setCacheObject(firstRound.getSymbol(), filterTime, 1, TimeUnit.DAYS);
|
||||
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + firstRound.getSymbol(), filterTime, 1, TimeUnit.DAYS);
|
||||
// 第二轮之后的轮次过期时间都为开始时间前一天
|
||||
Map<String, Date> roundSymbolMap = roundList.stream().filter(x -> !Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue()))
|
||||
.collect(Collectors.toMap(AdvertRound::getSymbol, AdvertRound::getStartTime, (s1, s2) -> s2));
|
||||
|
|
@ -653,7 +625,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
roundSymbolMap.forEach((symbol, startTime) -> {
|
||||
// 推广开始时间的前一天的 22:00:00
|
||||
LocalDateTime futureTimeFilter = startTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().minusDays(1).withHour(22).withMinute(0).withSecond(0);
|
||||
redisCache.setCacheObject(symbol, futureTimeFilter, 1, TimeUnit.DAYS);
|
||||
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + symbol, futureTimeFilter, 1, TimeUnit.DAYS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -666,7 +638,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
roundSymbolMap.forEach((symbol, startTime) -> {
|
||||
// 推广开始时间的前一天的 22:00:00
|
||||
LocalDateTime futureTimeFilter = startTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().minusDays(1).withHour(22).withMinute(0).withSecond(0);
|
||||
redisCache.setCacheObject(symbol, futureTimeFilter, 1, TimeUnit.DAYS);
|
||||
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + symbol, futureTimeFilter, 1, TimeUnit.DAYS);
|
||||
});
|
||||
}
|
||||
List<AdvertRound> otherRoundList = roundList.stream().filter(x -> !Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue())
|
||||
|
|
@ -676,7 +648,7 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
otherRoundSymbolMap.forEach((symbol, startTime) -> {
|
||||
// 推广开始时间的前一天的 22:00:00
|
||||
LocalDateTime futureTimeFilter = startTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().minusDays(1).withHour(22).withMinute(0).withSecond(0);
|
||||
redisCache.setCacheObject(symbol, futureTimeFilter, 1, TimeUnit.DAYS);
|
||||
redisCache.setCacheObject(ADVERT_DEADLINE_KEY + symbol, futureTimeFilter, 1, TimeUnit.DAYS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -685,11 +657,11 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
if (now.equals(firstRoundEndTime)) {
|
||||
// 将位置枚举的每一个symbol都存放到redis中
|
||||
roundList.stream().filter(x -> !Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue()))
|
||||
.forEach(x -> redisCache.setCacheObject(x.getSymbol(), filterTime, 1, TimeUnit.DAYS));
|
||||
.forEach(x -> redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(), filterTime, 1, TimeUnit.DAYS));
|
||||
} else {
|
||||
// 将位置枚举的每一个symbol都存放到redis中
|
||||
roundList.stream().filter(x -> Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue()))
|
||||
.forEach(x -> redisCache.setCacheObject(x.getSymbol(), filterTime, 1, TimeUnit.DAYS));
|
||||
.forEach(x -> redisCache.setCacheObject(ADVERT_DEADLINE_KEY + x.getSymbol(), filterTime, 1, TimeUnit.DAYS));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -697,14 +669,23 @@ public class AdvertRoundServiceImpl implements IAdvertRoundService {
|
|||
|
||||
|
||||
/**
|
||||
* 获取当前推广轮次的过期时间
|
||||
* 获取当前推广轮次的过期时间 每一个轮次都有过期时间,
|
||||
*
|
||||
* @param symbol 符号
|
||||
* @return
|
||||
*/
|
||||
private String getDeadline(String symbol) {
|
||||
final String deadline = redisCache.getCacheObject(symbol);
|
||||
return StringUtils.isNotBlank(deadline) ? deadline : "";
|
||||
private String getDeadline(String symbol) throws ParseException {
|
||||
String deadline = redisCache.getCacheObject(ADVERT_DEADLINE_KEY + symbol);
|
||||
if (StringUtils.isNotBlank(deadline)) {
|
||||
return deadline;
|
||||
}
|
||||
// 重新存到redis中
|
||||
this.saveAdvertDeadlineToRedis();
|
||||
deadline = redisCache.getCacheObject(ADVERT_DEADLINE_KEY + symbol);
|
||||
if (StringUtils.isEmpty(deadline)) {
|
||||
throw new ServiceException("获取推广轮次过期时间失败!请联系客服", HttpStatus.ERROR);
|
||||
}
|
||||
return deadline;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.page.Page;
|
||||
|
|
@ -75,7 +76,7 @@ public class StoreServiceImpl implements IStoreService {
|
|||
// 创建档口账户
|
||||
assetService.createInternalAccountIfNotExists(store.getId());
|
||||
// 放到redis中
|
||||
redisCache.setCacheObject(Constants.STORE_REDIS_PREFIX + store.getId(), store.getId());
|
||||
redisCache.setCacheObject(CacheConstants.STORE_KEY + store.getId(), store.getId());
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="startTime != null "> and ar.start_time >= #{startTime}</if>
|
||||
<if test="endTime != null "> and ar.end_time <= #{endTime}</if>
|
||||
ORDER BY
|
||||
ar.start_time DESC
|
||||
ar.start_time
|
||||
</select>
|
||||
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="roundId != null "> and ar.round_id = #{roundId}</if>
|
||||
<if test="sysIntercept != null "> and ar.sys_intercept = #{sysIntercept}</if>
|
||||
ORDER BY
|
||||
ar.start_time DESC
|
||||
ar.start_time
|
||||
</select>
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue