master:推广营销功能调整;

pull/1121/head
liujiang 2025-05-05 11:40:16 +08:00
parent 3b610b5ec2
commit 3fb105b8b8
44 changed files with 1081 additions and 76 deletions

View File

@ -6,10 +6,7 @@ import com.ruoyi.common.core.controller.XktBaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.Page;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.web.controller.xkt.vo.advert.AdvertCreateVO;
import com.ruoyi.web.controller.xkt.vo.advert.AdvertPageVO;
import com.ruoyi.web.controller.xkt.vo.advert.AdvertResVO;
import com.ruoyi.web.controller.xkt.vo.advert.AdvertUpdateVO;
import com.ruoyi.web.controller.xkt.vo.advert.*;
import com.ruoyi.xkt.dto.advert.AdvertCreateDTO;
import com.ruoyi.xkt.dto.advert.AdvertPageDTO;
import com.ruoyi.xkt.dto.advert.AdvertResDTO;
@ -21,6 +18,8 @@ import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 广Controller
*
@ -30,7 +29,7 @@ import org.springframework.web.bind.annotation.*;
@Api(tags = "推广营销")
@RestController
@RequiredArgsConstructor
@RequestMapping("/rest/v1/adverts")
@RequestMapping("/rest/v1/ads")
public class AdvertController extends XktBaseController {
final IAdvertService advertService;
@ -84,5 +83,14 @@ public class AdvertController extends XktBaseController {
return R.ok(advertService.offline(advertId));
}
/**
* 广
*/
@ApiOperation(value = "档口营销推广初始化数据", httpMethod = "GET", response = R.class)
@GetMapping(value = "/platform-list")
public R<List<AdvertPlatformResVO>> getPlatformList() {
return R.ok(BeanUtil.copyToList(advertService.getPlatformList(), AdvertPlatformResVO.class));
}
}

View File

@ -0,0 +1,72 @@
package com.ruoyi.web.controller.xkt;
import cn.hutool.core.bean.BeanUtil;
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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 广Controller
*
* @author ruoyi
* @date 2025-03-26
*/
@Api(tags = "推广营销轮次投放")
@RestController
@RequiredArgsConstructor
@RequestMapping("/rest/v1/ad-round-plays")
public class AdvertRoundPlayController extends XktBaseController {
final IAdvertRoundPlayService adPlayService;
/**
* 广
*/
@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)));
}
/**
* 广
*/
@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));
}
// TODO 获取最受欢迎推广位8个固定不动了
// TODO 获取最受欢迎推广位8个固定不动了
}

View File

@ -41,7 +41,7 @@ public class AdvertCreateVO {
private Integer playInterval;
@NotNull(message = "播放轮次不能为空!")
@ApiModelProperty(value = "播放轮次")
private Integer playTimes;
private Integer playRound;
@NotNull(message = "播放数量不能为空!")
@ApiModelProperty(value = "播放数量")
private Integer playNum;

View File

@ -0,0 +1,53 @@
package com.ruoyi.web.controller.xkt.vo.advert;
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("推广营销返回分组数据")
@Data
@Accessors(chain = true)
public class AdvertPlatformResVO {
@ApiModelProperty(value = "平台ID")
private Integer platformId;
@ApiModelProperty(value = "平台名称")
private String platformName;
@ApiModelProperty(value = "tabList")
private List<APTabVO> tabList;
@Data
@ApiModel(value = "平台下tab")
@Accessors(chain = true)
public static class APTabVO {
@ApiModelProperty(value = "tabId")
private Integer tabId;
@ApiModelProperty(value = "tabName")
private String tabName;
@ApiModelProperty(value = "类型列表")
List<APTypeVO> typeList;
}
@Data
@ApiModel(value = "tab下type")
@Accessors(chain = true)
public static class APTypeVO {
@ApiModelProperty("广告ID")
private Long advertId;
@ApiModelProperty(value = "typeId")
private Integer typeId;
@ApiModelProperty(value = "typeName")
private String typeName;
@ApiModelProperty(value = "示例url")
private String demoUrl;
}
}

View File

@ -16,7 +16,7 @@ import java.util.Date;
* @version v1.0
* @date 2025/3/27 15:12
*/
@ApiModel("创建推广营销")
@ApiModel("推广营销返回数据")
@Data
@Accessors(chain = true)
public class AdvertResVO {
@ -34,7 +34,7 @@ public class AdvertResVO {
@ApiModelProperty(value = "播放间隔")
private Integer playInterval;
@ApiModelProperty(value = "播放轮次")
private Integer playTimes;
private Integer playRound;
@ApiModelProperty(value = "播放数量")
private Integer playNum;
@ApiModelProperty(value = "推广状态")

View File

@ -44,7 +44,7 @@ public class AdvertUpdateVO {
private Integer playInterval;
@NotNull(message = "播放轮次不能为空!")
@ApiModelProperty(value = "播放轮次")
private Integer playTimes;
private Integer playRound;
@NotNull(message = "播放数量不能为空!")
@ApiModelProperty(value = "播放数量")
private Integer playNum;

View File

@ -0,0 +1,38 @@
package com.ruoyi.web.controller.xkt.vo.advertRoundPlay;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
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)
public class AdPlayStoreCreateVO {
@NotNull(message = "广告ID不能为空!")
@ApiModelProperty(value = "广告ID")
private Long advertId;
@NotNull(message = "广告轮次ID不能为空!")
@ApiModelProperty(value = "广告轮次ID")
private Long advertRoundId;
@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;
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.web.controller.xkt.vo.advertRoundPlay;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author liujiang
* @version v1.0
* @date 2025/3/27 15:12
*/
@ApiModel("当前类型档口营销推广数据")
@Data
@Accessors(chain = true)
public class AdPlayStoreResVO {
}

View File

@ -219,4 +219,5 @@ public class Constants
*/
public static final Long TOPMOST_PRODUCT_CATEGORY_ID = 1L;
}

View File

@ -18,8 +18,8 @@ import java.io.IOException;
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/rest/v1/daily-sale")
public class DailySaleController extends BaseController {
@RequestMapping("/rest/v1/daily-task")
public class DailyTaskController extends BaseController {
final XktTask task;
@ -65,4 +65,10 @@ public class DailySaleController extends BaseController {
return R.ok();
}
@PostMapping("/advert-round")
public R dailyRound(SysJob sysJob) {
task.dailyAdvertRound();
return R.ok();
}
}

View File

@ -29,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -60,6 +61,8 @@ public class XktTask {
final StoreProductCategoryAttributeMapper cateAttrMapper;
final StoreProductStatisticsMapper prodStatMapper;
final EsClientWrapper esClientWrapper;
final AdvertMapper advertMapper;
final AdvertRoundMapper advertRoundMapper;
/**
* 1
@ -290,6 +293,93 @@ public class XktTask {
this.storeProdMapper.updateById(storeProdList);
}
/**
* 广
*/
@Transactional
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)) {
return;
}
// 正在投放 或 待投放列表
List<AdvertRound> advertRoundList = this.advertRoundMapper.selectList(new LambdaQueryWrapper<AdvertRound>()
.eq(AdvertRound::getDelFlag, Constants.UNDELETED)
.in(AdvertRound::getLaunchStatus,
Arrays.asList(AdLaunchStatus.UN_LAUNCH.getValue(), AdLaunchStatus.LAUNCHING.getValue())));
// 投放轮次按照advertId进行分组
Map<Long, List<AdvertRound>> advertRoundMap = advertRoundList.stream().collect(Collectors.groupingBy(AdvertRound::getAdvertId));
// 待更新或待新增的推广轮次列表
List<AdvertRound> updateList = new ArrayList<>();
advertList.forEach(advert -> {
List<AdvertRound> roundList = advertRoundMap.get(advert.getId());
// 如果没有 投放中 或 待投放的推广轮次,则 一次性创建所有的轮次
if (CollectionUtils.isEmpty(roundList)) {
// 播放的轮次
for (int i = 0; i < advert.getPlayRound(); i++) {
// 如果i = 0 则表明从未创建过推广位,直接新建所有
final Integer launchStatus = i == 0 ? AdLaunchStatus.LAUNCHING.getValue() : AdLaunchStatus.UN_LAUNCH.getValue();
final LocalDate now = i == 0 ? LocalDate.now() : LocalDate.now().plusDays((long) advert.getPlayInterval() * i);
// 间隔时间
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))));
}
}
} else {
// 判断当天是否为播放轮次最小结束时间的下一天 最小结束时间为yyyy-MM-dd格式
final Date compareDate = java.sql.Date.valueOf(LocalDate.now().minusDays(1));
final Date minEndTime = roundList.stream().min(Comparator.comparing(AdvertRound::getEndTime)).get().getEndTime();
if (Objects.equals(minEndTime, compareDate)) {
// 将播放轮次为1的推广轮置为已过期
roundList.stream().filter(x -> Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue())).forEach(x -> x.setLaunchStatus(AdLaunchStatus.EXPIRED.getValue()));
// 将播放轮次 大于 1 的推广轮 依次减1
roundList.stream().filter(x -> x.getRoundId() > AdRoundType.PLAY_ROUND.getValue()).forEach(x -> x.setRoundId(x.getRoundId() - 1));
// 将播放轮次为1 且 投放状态为:待投放的 置为投放中
roundList.stream().filter(x -> Objects.equals(x.getRoundId(), AdRoundType.PLAY_ROUND.getValue())
&& Objects.equals(x.getLaunchStatus(), AdLaunchStatus.UN_LAUNCH.getValue())).forEach(x -> x.setLaunchStatus(AdLaunchStatus.LAUNCHING.getValue()));
updateList.addAll(roundList);
// 如果播放轮次有更新,则需重新判断
int diff = advert.getPlayRound() - roundList.stream().mapToInt(AdvertRound::getRoundId).max().getAsInt();
// 当前最大轮次
int maxRoundId = roundList.stream().mapToInt(AdvertRound::getRoundId).max().getAsInt();
// diff < 0 代表轮次有减少,则不新增播放轮, diff == 0 则代表播放轮次不增不减,不做调整
if (diff > 0) {
// 最大轮次的结束时间
final LocalDate maxEndTime = roundList.stream().max(Comparator.comparing(AdvertRound::getEndTime))
.map(round -> round.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
.orElseThrow(() -> new ServiceException("获取推广轮次最大结束时间失败", HttpStatus.ERROR));
LocalDate maxEndTimeNextDay = maxEndTime.plusDays(1);
// 根据轮次差来判断当前需要补多少播放轮
for (int j = 0; j < diff; j++) {
// 推广轮次 + 1
maxRoundId += 1;
final LocalDate startDate = j == 0 ? maxEndTimeNextDay : maxEndTimeNextDay.plusDays((long) advert.getPlayInterval() * j);
// 间隔时间
final LocalDate endDate = startDate.plusDays(advert.getPlayInterval() - 1);
// 每一轮的播放数量
for (int i = 0; i < advert.getPlayNum(); i++) {
// 生成最新的下一轮推广位
updateList.add(new AdvertRound().setAdvertId(advert.getId()).setRoundId(maxRoundId).setLaunchStatus(AdLaunchStatus.UN_LAUNCH.getValue())
.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))));
}
}
}
}
}
});
if (CollectionUtils.isNotEmpty(updateList)) {
this.advertRoundMapper.insertOrUpdate(updateList);
}
}
/**
*

View File

@ -56,7 +56,7 @@ public class Advert extends XktBaseEntity {
/**
*
*/
private Integer playTimes;
private Integer playRound;
/**
*
*/
@ -73,6 +73,10 @@ public class Advert extends XktBaseEntity {
* 广
*/
private String picSize;
/**
*
*/
private Integer prodMaxNum;
/**
* 广
*/

View File

@ -0,0 +1,106 @@
package com.ruoyi.xkt.domain;
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
*
* @author liujiang
* @date 2025-05-03
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class AdvertRound extends XktBaseEntity {
private static final long serialVersionUID = 1L;
/**
* 广ID
*/
@TableId
private Long id;
/**
* 广ID
*/
private Long advertId;
/**
* ID
*/
private Integer roundId;
/**
*
*/
private Integer launchStatus;
/**
*
*/
private Date startTime;
/**
*
*/
private Date endTime;
/**
* 广 A B C D E... advertplayNum
*/
private String position;
/**
* 广ID
*/
private Long storeId;
/**
* 广
*/
private BigDecimal payPrice;
/**
*
*/
private Integer biddingStatus;
/**
*
*/
private Integer picAuditStatus;
/**
* 0 1
*/
private Integer picSet;
/**
* 广ID sysFile.id
*/
private Long picId;
/**
* 广IDID eg: 1 1,2,3
*/
private String prodIdStr;
/**
*
*/
private Integer picDesignType;
/**
*
*/
private Integer sysIntercept;
}

View File

@ -0,0 +1,77 @@
package com.ruoyi.xkt.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.ruoyi.common.core.domain.XktBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* 广 advert_round_play
*
* @author liujiang
* @date 2025-05-03
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AdvertRoundPlay extends XktBaseEntity {
private static final long serialVersionUID = 1L;
/**
* 广ID
*/
@TableId
private Long id;
/**
* 广ID
*/
private Long advertId;
/**
* 广ID
*/
private Long advertRoundId;
/**
* 广ID
*/
private Long storeId;
/**
* 广
*/
private BigDecimal payPrice;
/**
*
*/
private Integer biddingStatus;
/**
*
*/
private Integer picAuditStatus;
/**
* 0 1
*/
private Integer picSet;
/**
* 广ID sysFile.id
*/
private Long picId;
/**
* 广IDID eg: 1 1,2,3
*/
private String prodIdStr;
/**
*
*/
private Integer picDesignType;
/**
*
*/
private Integer sysIntercept;
}

View File

@ -34,7 +34,7 @@ public class AdvertCreateDTO {
@ApiModelProperty(value = "播放间隔")
private Integer playInterval;
@ApiModelProperty(value = "播放轮次")
private Integer playTimes;
private Integer playRound;
@ApiModelProperty(value = "播放数量")
private Integer playNum;
@ApiModelProperty(value = "推广范例图片")

View File

@ -0,0 +1,22 @@
package com.ruoyi.xkt.dto.advert;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author liujiang
* @version v1.0
* @date 2025/3/27 15:12
*/
@ApiModel("推广营销平台与tab数据")
@Data
@Accessors(chain = true)
public class AdvertPlatTabDTO {
private Integer platformId;
private Integer tabId;
private Integer typeId;
private Long advertId;
}

View File

@ -0,0 +1,53 @@
package com.ruoyi.xkt.dto.advert;
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("推广营销返回分组数据")
@Data
@Accessors(chain = true)
public class AdvertPlatformResDTO {
@ApiModelProperty(value = "平台ID")
private Integer platformId;
@ApiModelProperty(value = "平台名称")
private String platformName;
@ApiModelProperty(value = "tabList")
private List<APTabDTO> tabList;
@Data
@ApiModel(value = "平台下tab")
@Accessors(chain = true)
public static class APTabDTO {
@ApiModelProperty(value = "tabId")
private Integer tabId;
@ApiModelProperty(value = "tabName")
private String tabName;
@ApiModelProperty(value = "类型列表")
List<APTypeDTO> typeList;
}
@Data
@ApiModel(value = "tab下type")
@Accessors(chain = true)
public static class APTypeDTO {
@ApiModelProperty("广告ID")
private Long advertId;
@ApiModelProperty(value = "typeId")
private Integer typeId;
@ApiModelProperty(value = "typeName")
private String typeName;
@ApiModelProperty(value = "示例url")
private String demoUrl;
}
}

View File

@ -32,7 +32,7 @@ public class AdvertResDTO {
@ApiModelProperty(value = "播放间隔")
private Integer playInterval;
@ApiModelProperty(value = "播放轮次")
private Integer playTimes;
private Integer playRound;
@ApiModelProperty(value = "播放数量")
private Integer playNum;
@ApiModelProperty(value = "推广状态")

View File

@ -36,7 +36,7 @@ public class AdvertUpdateDTO {
@ApiModelProperty(value = "播放间隔")
private Integer playInterval;
@ApiModelProperty(value = "播放轮次")
private Integer playTimes;
private Integer playRound;
@ApiModelProperty(value = "播放数量")
private Integer playNum;
@ApiModelProperty(value = "推广范例图片")

View File

@ -0,0 +1,33 @@
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

@ -0,0 +1,18 @@
package com.ruoyi.xkt.dto.advertRoundPlay;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author liujiang
* @version v1.0
* @date 2025/3/27 15:12
*/
@ApiModel("当前类型档口营销推广数据")
@Data
@Accessors(chain = true)
public class AdPlayStoreResDTO {
}

View File

@ -0,0 +1,35 @@
package com.ruoyi.xkt.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 广
* @author liujiang
* @date 2025-04-02 23:42
*/
@Getter
@AllArgsConstructor
public enum AdBiddingStatus {
// 已出价
BIDDING(1, "已出价"),
// 竞价失败
BIDDING_FAIL(2, "竞价失败"),
// 竞价成功
BIDDING_SUCCESS(3, "竞价成功"),
;
private final Integer value;
private final String label;
public static AdBiddingStatus of(Integer value) {
for (AdBiddingStatus e : AdBiddingStatus.values()) {
if (e.getValue().equals(value)) {
return e;
}
}
return null;
}
}

View File

@ -0,0 +1,37 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 广
* @author liujiang
* @date 2025-04-02 23:42
*/
@Getter
@AllArgsConstructor
public enum AdDesignType {
// 自主设计
STORE_DESIGN(1, "自主设计"),
// 平台设计
SYS_DESIGN(2, "平台设计");
;
private final Integer value;
private final String label;
public static AdDesignType of(Integer value) {
for (AdDesignType e : AdDesignType.values()) {
if (e.getValue().equals(value)) {
return e;
}
}
throw new ServiceException("推广营销图片设计类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -29,6 +31,6 @@ public enum AdDiscountType {
return e;
}
}
return null;
throw new ServiceException("推广营销折扣类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -32,6 +34,6 @@ public enum AdDisplayType {
return e;
}
}
return null;
throw new ServiceException("推广营销展示类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -0,0 +1,40 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 广
* @author liujiang
* @date 2025-04-02 23:42
*/
@Getter
@AllArgsConstructor
public enum AdLaunchStatus {
// 投放中
LAUNCHING(1, "投放中"),
// 待投放
UN_LAUNCH(2, "待投放"),
// 已过期
EXPIRED(3, "已过期"),
// 已退订
CANCEL(4, "已退订"),
;
private final Integer value;
private final String label;
public static AdLaunchStatus of(Integer value) {
for (AdLaunchStatus e : AdLaunchStatus.values()) {
if (e.getValue().equals(value)) {
return e;
}
}
throw new ServiceException("营销推广投放状态不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,10 +1,12 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 广
* 广线
*
* @author liujiang
* @date 2025-04-02 23:42
@ -30,6 +32,6 @@ public enum AdOnlineStatus {
return e;
}
}
return null;
throw new ServiceException("营销推广上线状态不存在!", HttpStatus.ERROR);
}
}

View File

@ -0,0 +1,39 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 广
* @author liujiang
* @date 2025-04-02 23:42
*/
@Getter
@AllArgsConstructor
public enum AdPicAuditStatus {
// 待审核
UN_AUDIT(1, "待审核"),
// 审核通过
AUDIT_PASS(2, "审核通过"),
// 审核驳回
AUDIT_REJECTED(3, "审核驳回"),
;
private final Integer value;
private final String label;
public static AdPicAuditStatus of(Integer value) {
for (AdPicAuditStatus e : AdPicAuditStatus.values()) {
if (e.getValue().equals(value)) {
return e;
}
}
throw new ServiceException("营销推广图片审核类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -26,6 +28,6 @@ public enum AdPlatformType {
return e;
}
}
return null;
throw new ServiceException("推广营销平台类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -0,0 +1,40 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 广
* @author liujiang
* @date 2025-04-02 23:42
*/
@Getter
@AllArgsConstructor
public enum AdRoundType {
// 播放轮
PLAY_ROUND(1, "播放轮"),
// 第二轮
SECOND_ROUND(2, "第二轮"),
// 第三轮
THIRD_ROUND(3, "第三轮"),
// 第四轮
FOURTH_ROUND(4, "第四轮"),
;
private final Integer value;
private final String label;
public static AdRoundType of(Integer value) {
for (AdRoundType e : AdRoundType.values()) {
if (e.getValue().equals(value)) {
return e;
}
}
throw new ServiceException("营销推广播放轮次不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -38,6 +40,6 @@ public enum AdTab {
return e;
}
}
return null;
throw new ServiceException("推广营销TAB不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,80 +15,80 @@ import lombok.Getter;
public enum AdType {
// 顶部横向大图
PC_HOME_TOP_LEFT_BANNER(1, "顶部横向大图"),
PC_HOME_TOP_LEFT_BANNER(1, "顶部横向大图", "/url"),
// 顶部纵向小图
PC_HOME_TOP_RIGHT_BANNER(2, "顶部纵向小图"),
PC_HOME_TOP_RIGHT_BANNER(2, "顶部纵向小图", "/url"),
// 人气榜左侧大图
PC_HOME_POP_LEFT_BANNER(3, "人气榜左大图"),
PC_HOME_POP_LEFT_BANNER(3, "人气榜左大图", "/url"),
// 人气榜中上侧
PC_HOME_POP_MID_TOP(4, "人气榜中上侧"),
PC_HOME_POP_MID_TOP(4, "人气榜中上侧", "/url"),
// 人气榜中下侧
PC_HOME_POP_MID_BOTTOM(5, "人气榜中下侧"),
PC_HOME_POP_MID_BOTTOM(5, "人气榜中下侧", "/url"),
// 人气榜右上侧
PC_HOME_POP_RIGHT_TOP(6, "人气榜右上侧"),
PC_HOME_POP_RIGHT_TOP(6, "人气榜右上侧", "/url"),
// 人气榜右下侧
PC_HOME_POP_RIGHT_BOTTOM(7, "人气榜右下侧"),
PC_HOME_POP_RIGHT_BOTTOM(7, "人气榜右下侧", "/url"),
// 首页档口横幅
PC_HOME_SINGLE_BANNER(8, "首页档口横幅"),
PC_HOME_SINGLE_BANNER(8, "首页档口横幅", "/url"),
// 首页商品列表
PC_HOME_PRODUCT_LIST(9, "首页商品列表"),
PC_HOME_PRODUCT_LIST(9, "首页商品列表", "/url"),
// 首页两侧固定挂耳
PC_HOME_FIXED_EAR(10, "首页两侧固定挂耳"),
PC_HOME_FIXED_EAR(10, "首页两侧固定挂耳", "/url"),
// 首页搜索框下名称
PC_HOME_SEARCH_DOWN_NAME(11, "首页搜索框下名称"),
PC_HOME_SEARCH_DOWN_NAME(11, "首页搜索框下名称", "/url"),
// 首页搜索框商品
PC_HOME_SEARCH_PRODUCT(12, "首页搜索框商品"),
PC_HOME_SEARCH_PRODUCT(12, "首页搜索框商品", "/url"),
// 首页搜索框档口
PC_HOME_SEARCH_STORE(13, "首页搜索框档口"),
PC_HOME_SEARCH_STORE(13, "首页搜索框档口", "/url"),
// 首页以图搜款商品
PC_HOME_PIC_SEARCH_PRODUCT(14, "首页以图搜款商品"),
PC_HOME_PIC_SEARCH_PRODUCT(14, "首页以图搜款商品", "/url"),
// 新品馆顶部横向大图
PC_NEW_PROD_TOP_LEFT_BANNER(30, "新品馆顶部横向大图"),
PC_NEW_PROD_TOP_LEFT_BANNER(30, "新品馆顶部横向大图", "/url"),
// 新品馆顶部纵向大图
PC_NEW_PROD_TOP_RIGHT(31, "新品馆顶部纵向大图"),
PC_NEW_PROD_TOP_RIGHT(31, "新品馆顶部纵向大图", "/url"),
// 新品馆品牌榜
PC_NEW_PROD_BRAND_BANNER(32, "新品馆品牌榜"),
PC_NEW_PROD_BRAND_BANNER(32, "新品馆品牌榜", "/url"),
// 新品馆热卖榜左大图
PC_NEW_PROD_HOT_LEFT_BANNER(33, "新品馆热卖榜左大图"),
PC_NEW_PROD_HOT_LEFT_BANNER(33, "新品馆热卖榜左大图", "/url"),
// 新品馆热卖榜右推广商品
PC_NEW_PROD_HOT_RIGHT_PRODUCT(34, "新品馆热卖榜右推广商品"),
PC_NEW_PROD_HOT_RIGHT_PRODUCT(34, "新品馆热卖榜右推广商品", "/url"),
// 新品馆横幅
PC_NEW_PROD_SINGLE_BANNER(35, "新品馆横幅"),
PC_NEW_PROD_SINGLE_BANNER(35, "新品馆横幅", "/url"),
// 新品馆商品列表
PC_NEW_PROD_PRODUCT_LIST(36, "新品馆商品列表"),
PC_NEW_PROD_PRODUCT_LIST(36, "新品馆商品列表", "/url"),
// PC搜索结果
PC_SEARCH_RESULT(40, "PC搜索结果"),
PC_SEARCH_RESULT(40, "PC搜索结果", "/url"),
// PC用户中心
PC_USER_CENTER(41, "PC用户中心"),
PC_USER_CENTER(41, "PC用户中心", "/url"),
// PC下载页
PC_DOWNLOAD(42, "PC下载页"),
PC_DOWNLOAD(42, "PC下载页", "/url"),
// APP首页顶部轮播图
APP_HOME_TOP_BANNER(50, "APP首页顶部轮播图"),
APP_HOME_TOP_BANNER(50, "APP首页顶部轮播图", "/url"),
// APP首页推荐商品区
APP_HOME_RECOMMEND_PRODUCT(51, "APP首页推荐商品区"),
APP_HOME_RECOMMEND_PRODUCT(51, "APP首页推荐商品区", "/url"),
// APP首页热卖推荐
APP_HOME_HOT_RECOMMEND(52, "APP首页热卖推荐"),
APP_HOME_HOT_RECOMMEND(52, "APP首页热卖推荐", "/url"),
// APP首页人气榜
APP_HOME_POP_RECOMMEND(53, "APP首页人气榜"),
APP_HOME_POP_RECOMMEND(53, "APP首页人气榜", "/url"),
// APP首页新品榜
APP_HOME_NEW_PROD_RECOMMEND(54, "APP首页新品榜"),
APP_HOME_NEW_PROD_RECOMMEND(54, "APP首页新品榜", "/url"),
// APP搜索结果
APP_SEARCH_RESULT(55, "APP搜索结果"),
APP_SEARCH_RESULT(55, "APP搜索结果", "/url"),
// APP分类页轮播图
APP_CATEGORY_TOP_BANNER(70, "APP分类页轮播图"),
APP_CATEGORY_TOP_BANNER(70, "APP分类页轮播图", "/url"),
// APP个人中心猜你喜欢
APP_USER_CENTER_GUESS_YOU_LIKE(71, "APP个人中心猜你喜欢"),
APP_USER_CENTER_GUESS_YOU_LIKE(71, "APP个人中心猜你喜欢", "/url"),
// 以图搜款结果推广商品
PIC_SEARCH_RESULT_PRODUCT(80, "以图搜款结果推广商品"),
PIC_SEARCH_RESULT_PRODUCT(80, "以图搜款结果推广商品", "/url"),
@ -94,6 +96,7 @@ public enum AdType {
private final Integer value;
private final String label;
private final String demoUrl;
public static AdType of(Integer value) {
for (AdType e : AdType.values()) {
@ -101,6 +104,6 @@ public enum AdType {
return e;
}
}
return null;
throw new ServiceException("营销推广推广类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -24,6 +26,6 @@ public enum StorageType {
return e;
}
}
return null;
throw new ServiceException("档口入库类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -31,6 +33,6 @@ public enum StoreStatus {
return e;
}
}
return null;
throw new ServiceException("档口状态类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.xkt.enums;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -44,6 +46,6 @@ public enum StoreTagType {
return e;
}
}
return null;
throw new ServiceException("档口标签类型不存在!", HttpStatus.ERROR);
}
}

View File

@ -2,8 +2,7 @@ package com.ruoyi.xkt.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.xkt.domain.Advert;
import com.ruoyi.xkt.dto.advert.AdvertPageDTO;
import com.ruoyi.xkt.dto.advert.AdvertResDTO;
import com.ruoyi.xkt.dto.advert.AdvertPlatTabDTO;
import org.springframework.stereotype.Repository;
import java.util.List;
@ -17,11 +16,6 @@ import java.util.List;
@Repository
public interface AdvertMapper extends BaseMapper<Advert> {
/**
* 广
* @param pageDTO
* @return
*/
// List<AdvertResDTO> selectAdvertPage(AdvertPageDTO pageDTO);
List<AdvertPlatTabDTO> selectPlatTabList();
}

View File

@ -0,0 +1,16 @@
package com.ruoyi.xkt.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.xkt.domain.AdvertRound;
import org.springframework.stereotype.Repository;
/**
* 广Mapper
*
* @author ruoyi
* @date 2025-03-26
*/
@Repository
public interface AdvertRoundMapper extends BaseMapper<AdvertRound> {
}

View File

@ -0,0 +1,16 @@
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

@ -5,7 +5,7 @@ import com.ruoyi.xkt.domain.AdvertStoreFile;
import org.springframework.stereotype.Repository;
/**
* 广Mapper
* 广广 Mapper
*
* @author ruoyi
* @date 2025-03-26

View File

@ -0,0 +1,31 @@
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

@ -1,10 +1,9 @@
package com.ruoyi.xkt.service;
import com.ruoyi.common.core.page.Page;
import com.ruoyi.xkt.dto.advert.AdvertCreateDTO;
import com.ruoyi.xkt.dto.advert.AdvertPageDTO;
import com.ruoyi.xkt.dto.advert.AdvertResDTO;
import com.ruoyi.xkt.dto.advert.AdvertUpdateDTO;
import com.ruoyi.xkt.dto.advert.*;
import java.util.List;
/**
* 广Service
@ -54,4 +53,11 @@ public interface IAdvertService {
*/
Integer offline(Long advertId);
/**
*
*
* @return List<AdvertGroupResDTO>
*/
List<AdvertPlatformResDTO> getPlatformList();
}

View File

@ -0,0 +1,83 @@
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

@ -12,12 +12,11 @@ import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.xkt.domain.Advert;
import com.ruoyi.xkt.domain.SysFile;
import com.ruoyi.xkt.dto.advert.AdvertCreateDTO;
import com.ruoyi.xkt.dto.advert.AdvertPageDTO;
import com.ruoyi.xkt.dto.advert.AdvertResDTO;
import com.ruoyi.xkt.dto.advert.AdvertUpdateDTO;
import com.ruoyi.xkt.dto.storeCustomer.StoreCusPageResDTO;
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.xkt.mapper.AdvertMapper;
import com.ruoyi.xkt.mapper.SysFileMapper;
import com.ruoyi.xkt.service.IAdvertService;
@ -27,9 +26,11 @@ import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 广Service
@ -173,6 +174,39 @@ public class AdvertServiceImpl implements IAdvertService {
return this.advertMapper.updateById(advert);
}
/**
*
*
* @return List<AdvertGroupResDTO>
*/
@Override
@Transactional(readOnly = true)
public List<AdvertPlatformResDTO> getPlatformList() {
List<AdvertPlatTabDTO> list = this.advertMapper.selectPlatTabList();
if (CollectionUtils.isEmpty(list)) {
return null;
}
// 所有的推广平台
List<AdvertPlatformResDTO> platformList = new ArrayList<>();
list.stream().collect(Collectors
.groupingBy(AdvertPlatTabDTO::getPlatformId, Collectors
.groupingBy(AdvertPlatTabDTO::getTabId)))
.forEach((platformId, tabMap) -> {
// 平台下所有的tab
List<AdvertPlatformResDTO.APTabDTO> tabList = new ArrayList<>();
tabMap.forEach((tabId, typeList) -> tabList.add(new AdvertPlatformResDTO.APTabDTO()
.setTabId(tabId).setTabName(AdTab.of(tabId).getLabel())
// tab下所有的推广类型
.setTypeList(typeList.stream().map(type -> new AdvertPlatformResDTO.APTypeDTO()
.setAdvertId(type.getAdvertId()).setTypeId(type.getTypeId()).setTypeName(AdType.of(type.getTypeId()).getLabel())
.setDemoUrl(AdType.of(type.getTypeId()).getDemoUrl())).collect(Collectors.toList()))));
platformList.add(new AdvertPlatformResDTO().setPlatformId(platformId)
.setPlatformName(AdPlatformType.of(platformId).getLabel())
.setTabList(tabList));
});
return platformList;
}
/**
*
*/

View File

@ -4,5 +4,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.xkt.mapper.AdvertMapper">
<select id="selectPlatTabList" resultType="com.ruoyi.xkt.dto.advert.AdvertPlatTabDTO">
SELECT
id AS advertId,
platform_id,
tab_id,
type_id
FROM
advert a
WHERE
del_flag = 0
AND online_status = 1
GROUP BY
platform_id,
tab_id,
type_id
</select>
</mapper>