master:推广营销完善;

pull/1121/head
liujiang 2025-05-14 15:00:21 +08:00
parent 2b1da50167
commit 09e66a856a
11 changed files with 62 additions and 68 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.3redis
*
* @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);
});
}

View File

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

View File

@ -30,7 +30,7 @@ public interface IAdvertRoundService {
* @param createDTO
* @return Integer
*/
Integer create(AdRoundStoreCreateDTO createDTO);
Integer create(AdRoundStoreCreateDTO createDTO) throws ParseException;
/**
* 广

View File

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

View File

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

View File

@ -60,7 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="startTime != null "> and ar.start_time &gt;= #{startTime}</if>
<if test="endTime != null "> and ar.end_time &lt;= #{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>