设置秒杀活动为自动开启

This commit is contained in:
lifenlong 2021-05-24 18:18:24 +08:00
parent 5c32fd9d7c
commit 16cc989cdf
29 changed files with 355 additions and 480 deletions

View File

@ -17,29 +17,29 @@ import java.util.List;
/**
* 买家端,限时抢购接口
* 买家端,秒杀活动接口
*
* @author paulG
* @date 2020/11/17 2:30 下午
*/
@Api(tags = "买家端,限时抢购接口")
@Api(tags = "买家端,秒杀活动接口")
@RestController
@RequestMapping("/buyer/promotion/seckill")
public class SeckillBuyerController {
/**
* 限时抢购
* 秒杀活动
*/
@Autowired
private SeckillApplyService seckillApplyService;
@ApiOperation(value = "获取当天限时抢购信息")
@ApiOperation(value = "获取当天秒杀活动信息")
@GetMapping
public ResultMessage<List<SeckillTimelineVO>> getSeckillTime() {
return ResultUtil.data(seckillApplyService.getSeckillTimeline());
}
@ApiOperation(value = "获取某个时刻的限时抢购商品信息")
@ApiOperation(value = "获取某个时刻的秒杀活动商品信息")
@GetMapping("/{timeline}")
public ResultMessage<List<SeckillGoodsVO>> getSeckillGoods(@PathVariable Integer timeline) {
return ResultUtil.data(seckillApplyService.getSeckillGoods(timeline));

View File

@ -3,15 +3,21 @@ package cn.lili.timetask.handler.impl.promotion;
import cn.lili.modules.order.cart.entity.vo.FullDiscountVO;
import cn.lili.modules.promotion.entity.dos.MemberCoupon;
import cn.lili.modules.promotion.entity.dos.PromotionGoods;
import cn.lili.modules.promotion.entity.dos.Seckill;
import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum;
import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum;
import cn.lili.modules.promotion.entity.vos.CouponVO;
import cn.lili.modules.promotion.entity.vos.PintuanVO;
import cn.lili.modules.promotion.service.*;
import cn.lili.modules.search.service.EsGoodsIndexService;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.SeckillSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import cn.lili.timetask.handler.EveryDayExecute;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
@ -53,6 +59,12 @@ public class PromotionEverydayExecute implements EveryDayExecute {
//促销商品
@Autowired
private PromotionGoodsService promotionGoodsService;
//设置
@Autowired
private SettingService settingService;
@Autowired
private SeckillService seckillService;
/**
@ -68,7 +80,35 @@ public class PromotionEverydayExecute implements EveryDayExecute {
query.addCriteria(Criteria.where("endTime").lt(new Date()));
List<String> promotionIds = new ArrayList<>();
//关闭满减活动
endFullDiscount(promotionIds,query);
//关闭拼团活动
endPintuan(promotionIds,query);
//关闭优惠券
endCoupon(promotionIds,query);
//每日新增秒杀活动
addSeckill();
promotionGoodsService.update(this.getUpdatePromotionGoodsWrapper(promotionIds));
}
/**
* 添加秒杀活动
* 从系统设置中获取秒杀活动的配置
* 添加30天后的秒杀活动
*/
private void addSeckill(){
Setting setting = settingService.get(SettingEnum.SECKILL_SETTING.name());
SeckillSetting seckillSetting=new Gson().fromJson(setting.getSettingValue(), SeckillSetting.class);
Seckill seckill=new Seckill(seckillSetting.getHours(),seckillSetting.getSeckillRule());
seckillService.saveSeckill(seckill);
}
/**
* 修改满额活动下的商品
* @param promotionIds 促销活动ID
* @param query 查询Wrapper
*/
private void endFullDiscount(List<String> promotionIds,Query query){
//关闭满减活动
List<FullDiscountVO> fullDiscountVOS = mongoTemplate.find(query, FullDiscountVO.class);
if (!fullDiscountVOS.isEmpty()) {
@ -87,7 +127,14 @@ public class PromotionEverydayExecute implements EveryDayExecute {
fullDiscountService.update(this.getUpdatePromotionWrapper(ids));
promotionIds.addAll(ids);
}
//关闭拼团活动
}
/**
* 修改拼团活动下的商品
* @param promotionIds 促销活动ID
* @param query 查询Wrapper
*/
private void endPintuan(List<String> promotionIds,Query query){
List<PintuanVO> pintuanVOS = mongoTemplate.find(query, PintuanVO.class);
if (!pintuanVOS.isEmpty()) {
//准备修改活动的id
@ -106,8 +153,14 @@ public class PromotionEverydayExecute implements EveryDayExecute {
pintuanService.update(this.getUpdatePromotionWrapper(ids));
promotionIds.addAll(ids);
}
}
//关闭优惠券活动
/**
* 修改优惠券下的商品
* @param promotionIds 促销活动ID
* @param query 查询Wrapper
*/
private void endCoupon(List<String> promotionIds,Query query){
List<CouponVO> couponVOS = mongoTemplate.find(query, CouponVO.class);
if (!couponVOS.isEmpty()) {
List<String> ids = new ArrayList<>();
@ -123,19 +176,19 @@ public class PromotionEverydayExecute implements EveryDayExecute {
ids.add(vo.getId());
}
couponService.update(this.getUpdatePromotionWrapper(ids));
LambdaUpdateWrapper<MemberCoupon> memberCouponLambdaUpdateWrapper = new LambdaUpdateWrapper<MemberCoupon>().in(MemberCoupon::getCouponId, ids).set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.EXPIRE.name());
LambdaUpdateWrapper<MemberCoupon> memberCouponLambdaUpdateWrapper = new LambdaUpdateWrapper<MemberCoupon>()
.in(MemberCoupon::getCouponId, ids)
.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name())
.set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.EXPIRE.name());
memberCouponService.update(memberCouponLambdaUpdateWrapper);
promotionIds.addAll(ids);
}
promotionGoodsService.update(this.getUpdatePromotionGoodsWrapper(promotionIds));
}
/**
* 获取促销修改查询条件 修改活动状态
* @param ids
* @return
* @param ids 促销活动ID
* @return 促销活动商品查询Wrapper
*/
private UpdateWrapper getUpdatePromotionWrapper(List<String> ids) {
UpdateWrapper updateWrapper = new UpdateWrapper<>();
@ -145,8 +198,8 @@ public class PromotionEverydayExecute implements EveryDayExecute {
}
/**
* 获取商品的促销修改查询条件 修改商品状态
* @param ids
* @return
* @param ids 促销活动ID
* @return 促销活动商品修改Wrapper
*/
private UpdateWrapper getUpdatePromotionGoodsWrapper(List<String> ids) {
UpdateWrapper updateWrapper = new UpdateWrapper<>();

View File

@ -219,7 +219,7 @@ public enum CachePrefix {
STORE_ID_FULL_DISCOUNT,
/**
* 限时抢购活动缓存key前缀
* 秒杀活动活动缓存key前缀
*/
STORE_ID_SECKILL,

View File

@ -15,7 +15,7 @@ public enum OrderOutTypeEnum {
* 出库类型枚举
*/
GOODS("商品"),
SECKILL_GOODS("限时抢购商品");
SECKILL_GOODS("秒杀活动商品");
private final String description;

View File

@ -1,11 +1,16 @@
package cn.lili.modules.promotion.entity.dos;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.lili.modules.promotion.entity.dto.BasePromotion;
import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Table;
@ -13,7 +18,7 @@ import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* 限时抢购实体类
* 秒杀活动实体类
*
* @author Chopper
* @date 2020-03-19 10:44 上午
@ -22,7 +27,8 @@ import java.util.Date;
@Entity
@Table(name = "li_seckill")
@TableName("li_seckill")
@ApiModel(value = "限时抢购活动")
@ApiModel(value = "秒杀活动活动")
@NoArgsConstructor
public class Seckill extends BasePromotion {
private static final long serialVersionUID = -9116425737163730836L;
@ -44,4 +50,30 @@ public class Seckill extends BasePromotion {
*/
@ApiModelProperty(value = "商家id集合以逗号分隔")
private String storeIds;
@ApiModelProperty(value = "商品数量")
private Integer goodsNum;
@ApiModelProperty(value = "店铺数量")
private Integer storeNum;
public Seckill(String hours,String seckillRule){
//默认创建30天后的秒杀活动
DateTime dateTime= DateUtil.beginOfDay(DateUtil.offset(new DateTime(), DateField.DAY_OF_YEAR, 30));
this.applyEndTime=dateTime;
this.hours=hours;
this.seckillRule=seckillRule;
this.goodsNum=0;
this.storeNum=0;
//BasePromotion
setStoreName("platform");
setStoreId("platform");
setPromotionName(DateUtil.formatDate(dateTime)+" 秒杀活动");
setStartTime(dateTime);
setEndTime(DateUtil.endOfDay(dateTime));
setPromotionStatus(PromotionStatusEnum.NEW.name());
}
}

View File

@ -13,7 +13,7 @@ import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 限时抢购申请实体类
* 秒杀活动申请实体类
*
* @author Chopper
* @date 2020-03-19 10:44 上午
@ -22,7 +22,7 @@ import javax.validation.constraints.NotNull;
@Entity
@Table(name = "li_seckill_apply")
@TableName("li_seckill_apply")
@ApiModel(value = "限时抢购申请")
@ApiModel(value = "秒杀活动申请")
public class SeckillApply extends BaseEntity {
private static final long serialVersionUID = 5440164641970820989L;

View File

@ -1,7 +1,7 @@
package cn.lili.modules.promotion.entity.enums;
/**
* 限时抢购状态枚举
* 秒杀活动状态枚举
*
* @author paulG
* @date 2020/8/26
@ -9,7 +9,7 @@ package cn.lili.modules.promotion.entity.enums;
public enum SeckillApplyStatusEnum {
/**
* 当前店铺对当前限时抢购的申请状态
* 当前店铺对当前秒杀活动的申请状态
*/
APPLIED("已经申请过"), NOT_APPLY("未报名"), EXPIRE("过期的");

View File

@ -5,7 +5,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 限时抢购申请视图对象
* 秒杀活动申请视图对象
*
* @author paulG
* @date 2020/8/21

View File

@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 限时抢购商品视图对象
* 秒杀活动商品视图对象
*
* @author paulG
* @date 2020/8/26

View File

@ -17,7 +17,7 @@ import java.util.Date;
import java.util.regex.Pattern;
/**
* 限时抢购查询通用类
* 秒杀活动查询通用类
*
* @author paulG
* @date 2020/8/21
@ -27,7 +27,7 @@ public class SeckillSearchParams implements Serializable {
private static final long serialVersionUID = -4052716630253333681L;
@ApiModelProperty(value = "限时抢购活动编号")
@ApiModelProperty(value = "秒杀活动活动编号")
private String seckillId;
@ApiModelProperty(value = "活动名称")

View File

@ -7,7 +7,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 限时抢购时刻视图对象
* 秒杀活动时刻视图对象
*
* @author paulG
* @date 2020/8/27
@ -26,7 +26,7 @@ public class SeckillTimelineVO implements Serializable {
@ApiModelProperty(value = "距离本组活动开始的时间秒为单位。如果活动的开始时间是10点服务器时间为8点距离开始还有多少时间")
private Long distanceStartTime;
@ApiModelProperty(value = "本组活动内的限时抢购商品列表")
@ApiModelProperty(value = "本组活动内的秒杀活动商品列表")
private List<SeckillGoodsVO> seckillGoodsList;
}

View File

@ -1,19 +1,14 @@
package cn.lili.modules.promotion.entity.vos;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.DateUtil;
import cn.lili.modules.promotion.entity.dos.Seckill;
import cn.lili.modules.promotion.entity.dos.SeckillApply;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* 限时抢购视图对象
* 秒杀活动视图对象
*
* @author paulG
* @date 2020/8/20
@ -30,60 +25,8 @@ public class SeckillVO extends Seckill {
private String seckillApplyStatus;
/**
* 当前限时抢购下所有的秒杀申请信息
* 当前秒杀活动下所有的秒杀申请信息
*/
private List<SeckillApply> seckillApplyList;
/**
* 检查当前时间
*/
public void checkTime() {
String[] timeRange = this.getHours().split(",");
int[] hoursSored = Arrays.stream(timeRange).mapToInt(Integer::parseInt).toArray();
Arrays.sort(hoursSored);
int lastTime = 0;
for (int s : hoursSored) {
if (lastTime == s) {
throw new ServiceException("抢购区间的值不能重复");
} else {
lastTime = s;
}
if (s < 0 || s > 23) {
throw new ServiceException("抢购区间必须在0点到23点的整点时刻");
}
}
// 活动开始时间
long startTime = this.getStartTime().getTime() / 1000;
// 报名截止时间
long applyEndTime = this.getApplyEndTime().getTime() / 1000;
int timeHour = hoursSored[0];
int endTimeHour = hoursSored[hoursSored.length - 1];
//获取活动开始当天0点的时间
String startDate = DateUtil.toString(startTime, DateUtil.STANDARD_DATE_FORMAT) + " " + (timeHour > 10 ? timeHour : "0" + timeHour) + ":00:00";
long startDayTime = cn.hutool.core.date.DateUtil.parse(startDate, DateUtil.STANDARD_FORMAT).getTime() / 1000;
// 结束时间
String endDate = DateUtil.toString(startTime, DateUtil.STANDARD_DATE_FORMAT) + " " + (endTimeHour > 10 ? endTimeHour : "0" + endTimeHour) + ":59:00";
long endDayTime = cn.hutool.core.date.DateUtil.parse(endDate, DateUtil.STANDARD_FORMAT).getTime() / 1000;
//活动时间小于当天开始时间
if (startDayTime < DateUtil.startOfTodDay()) {
throw new ServiceException("活动时间不能小于当前时间");
}
//报名截止时间小于当前时间
if (applyEndTime < DateUtil.getDateline()) {
throw new ServiceException("报名截止时间不能小于当前时间");
}
//报名截止时间大于活动开始当天的起始时间
if (applyEndTime >= startDayTime) {
throw new ServiceException("报名截止时间不能大于等于活动开始时间");
}
this.setStartTime(new Date(startDayTime * 1000));
this.setEndTime(new Date(endDayTime * 1000));
}
}

View File

@ -4,7 +4,7 @@ import cn.lili.modules.promotion.entity.dos.SeckillApply;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* 限时抢购申请数据处理层
* 秒杀活动申请数据处理层
*
* @author Chopper
* @date 2020/8/21

View File

@ -4,7 +4,7 @@ import cn.lili.modules.promotion.entity.dos.Seckill;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* 限时抢购数据处理层
* 秒杀活动数据处理层
*
* @author Chopper
* @date 2020/8/21

View File

@ -10,7 +10,6 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;
/**
* 秒杀申请业务层
@ -22,82 +21,46 @@ public interface SeckillApplyService extends IService<SeckillApply> {
/**
* 获取当天限时抢购信息列表时刻及对应时刻下的商品
* 获取当天秒杀活动信息列表时刻及对应时刻下的商品
*
* @return 限时抢购信息列表
* @return 秒杀活动信息列表
*/
List<SeckillTimelineVO> getSeckillTimeline();
/**
* 获取当天某个时刻的限时抢购商品列表
* 获取当天某个时刻的秒杀活动商品列表
*
* @param timeline 指定时刻
* @return 限时抢购商品列表
* @return 秒杀活动商品列表
*/
List<SeckillGoodsVO> getSeckillGoods(Integer timeline);
/**
* 审核一批申请
*
* @param ids 限时抢购申请编号
* @param seckillId 限时抢购编号
* @param applyStatus 审批状态
* @param failReason 驳回原因
*/
void auditBatchApply(String[] ids, String seckillId, String applyStatus, String failReason);
/**
* 分页查询限时请购申请列表
*
* @param queryParam 限时抢购申请查询参数
* @param pageVo 分页参数
* @return 限时请购申请列表
*/
IPage<SeckillApply> getSeckillApplyFromMysql(SeckillSearchParams queryParam, PageVO pageVo);
/**
* 从mongo中分页查询限时请购申请列表
*
* @param queryParam 限时抢购申请查询参数
* @param queryParam 秒杀活动申请查询参数
* @param pageVo 分页参数
* @return 限时请购申请列表
*/
IPage<SeckillApply> getSeckillApplyFromMongo(SeckillSearchParams queryParam, PageVO pageVo);
/**
* 添加限时抢购申请
* 添加秒杀活动申请
* 检测是否商品是否同时参加多个活动
* 将秒杀商品信息存入秒杀活动中更新mogo信息
* 保存秒杀活动商品促销商品信息
*
* @param seckillId 限时抢购编号
* @param seckillId 秒杀活动编号
* @param storeId 商家id
* @param seckillApplyList 限时抢购申请列表
* @param seckillApplyList 秒杀活动申请列表
*/
void addSeckillApply(String seckillId, String storeId, List<SeckillApplyVO> seckillApplyList);
/**
* 批量删除限时抢购申请
* 批量删除秒杀活动申请
*
* @param seckillId 限时抢购活动id
* @param ids 限时抢购申请id集合
* @param seckillId 秒杀活动活动id
* @param ids 秒杀活动申请id集合
*/
void removeSeckillApplyByIds(String seckillId, List<String> ids);
/**
* 更新限时抢购库存数量
*
* @param id 限时抢购申请限时抢购商品id
* @param num 数量
* @return 是否成功
*/
boolean updateSeckillStock(String id, Integer num);
/**
* 更新限时抢购库存数量
*
* @param map key 限时抢购申请限时抢购商品id value 为数量
* @return 是否成功
*/
boolean updateSeckillStock(Map<String, Integer> map);
}

View File

@ -17,78 +17,79 @@ public interface SeckillService extends IService<Seckill> {
/**
* 从mysql中根据条件获取限时抢购分页列表
* 从mysql中根据条件获取秒杀活动分页列表
*
* @param queryParam 查询参数
* @param pageVo 分页参数
* @return 限时抢购分页列表
* @return 秒杀活动分页列表
*/
IPage<Seckill> getSeckillByPageFromMysql(SeckillSearchParams queryParam, PageVO pageVo);
/**
* 从mongo中根据条件获取限时抢购分页列表
* 从mongo中根据条件获取秒杀活动分页列表
*
* @param queryParam 查询参数
* @param pageVo 分页参数
* @return 限时抢购分页列表
* @return 秒杀活动分页列表
*/
IPage<SeckillVO> getSeckillByPageFromMongo(SeckillSearchParams queryParam, PageVO pageVo);
/**
* 从mongo中获取限时抢购信息
* 从mongo中获取秒杀活动信息
*
* @param id 限时抢购id
* @return 限时抢购信息
* @param id 秒杀活动id
* @return 秒杀活动信息
*/
SeckillVO getSeckillByIdFromMongo(String id);
/**
* 保存限时抢购
* 保存秒杀活动
*
* @param seckill 限时抢购信息
* @param seckill 秒杀活动
* @return 是否保存成功
*/
boolean saveSeckill(SeckillVO seckill);
boolean saveSeckill(Seckill seckill);
/**
* 商家报名限时抢购活动
* 商家报名秒杀活动活动
*
* @param storeId 商家编号
* @param seckillId 限时抢购编号
* @param seckillId 秒杀活动编号
*/
void storeApply(String storeId, String seckillId);
/**
* 修改限时抢购
* 修改秒杀活动
*
* @param seckillVO 限时抢购信息
* @param seckillVO 秒杀活动信息
* @return 是否修改成功
*/
boolean modifySeckill(SeckillVO seckillVO);
/**
* 删除限时抢购
* 删除秒杀活动
*
* @param id 限时抢购编号
* @param id 秒杀活动编号
*/
void deleteSeckill(String id);
/**
* 开启一个限时抢购
* 开启一个秒杀活动
*
* @param id 限时抢购编号
* @param id 秒杀活动编号
*/
void openSeckill(String id);
/**
* 关闭一个限时抢购
* 关闭一个秒杀活动
*
* @param id 限时抢购编号
* @param id 秒杀活动编号
*/
void closeSeckill(String id);
/**
* 获取当前可参与的活动数量
*
* @return 可参与活动数量
*/
Integer getApplyNum();

View File

@ -505,11 +505,11 @@ public class PintuanServiceImpl extends ServiceImpl<PintuanMapper, Pintuan> impl
}
// 查询是否在同一时间段参与了拼团活动
Integer count = promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.SECKILL.name(), promotionGood.getSkuId(), pintuan.getStartTime(), pintuan.getEndTime());
// 查询是否在同一时间段参与了限时抢购活动
// 查询是否在同一时间段参与了秒杀活动活动
count += promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.PINTUAN.name(), promotionGood.getSkuId(), pintuan.getStartTime(), pintuan.getEndTime());
if (count > 0) {
log.error("商品[" + promotionGood.getGoodsName() + "]已经在重叠的时间段参加了限时抢购或拼团活动,不能参加拼团活动");
throw new ServiceException("商品[" + promotionGood.getGoodsName() + "]已经在重叠的时间段参加了限时抢购或拼团活动,不能参加拼团活动");
log.error("商品[" + promotionGood.getGoodsName() + "]已经在重叠的时间段参加了秒杀活动或拼团活动,不能参加拼团活动");
throw new ServiceException("商品[" + promotionGood.getGoodsName() + "]已经在重叠的时间段参加了秒杀活动或拼团活动,不能参加拼团活动");
}
}
LambdaQueryWrapper<PromotionGoods> queryWrapper = new LambdaQueryWrapper<>();

View File

@ -64,7 +64,7 @@ public class PromotionGoodsServiceImpl extends ServiceImpl<PromotionGoodsMapper,
//Redis
@Autowired
private StringRedisTemplate stringRedisTemplate;
//限时抢购申请
//秒杀活动申请
@Autowired
private SeckillApplyService seckillApplyService;
//规格商品
@ -306,7 +306,7 @@ public class PromotionGoodsServiceImpl extends ServiceImpl<PromotionGoodsMapper,
queryWrapper.eq(SeckillApply::getSeckillId, promotionId).eq(SeckillApply::getSkuId, skuId);
SeckillApply seckillApply = seckillApplyService.getOne(queryWrapper);
if (seckillApply == null) {
throw new ServiceException("当前限时抢购商品不存在!");
throw new ServiceException("当前秒杀活动商品不存在!");
}
LambdaUpdateWrapper<SeckillApply> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(SeckillApply::getSeckillId, promotionId).eq(SeckillApply::getSkuId, skuId);

View File

@ -33,7 +33,7 @@ public class PromotionPriceServiceImpl implements PromotionPriceService {
//ES商品
@Autowired
private EsGoodsSearchService goodsSearchService;
//限时抢购申请
//秒杀活动申请
@Autowired
private SeckillApplyService seckillApplyService;
//促销商品
@ -180,7 +180,7 @@ public class PromotionPriceServiceImpl implements PromotionPriceService {
}
/**
* 促销计算(限时抢购拼团)
* 促销计算(秒杀活动拼团)
*
* @param promotionMap 当前商品所有参加的促销活动
* @param goodsSkuPromotionPrice 商品SKU促销计算信息
@ -189,11 +189,11 @@ public class PromotionPriceServiceImpl implements PromotionPriceService {
*/
private void calculationPromotionMap(Map<String, Object> promotionMap, GoodsSkuPromotionPriceDTO goodsSkuPromotionPrice, EsGoodsIndex esGoodsIndex, String pintuanId) {
if (promotionMap != null && !promotionMap.isEmpty()) {
// 检查当前商品是否存在限时抢购活动
// 检查当前商品是否存在秒杀活动活动
Optional<String> existSeckill = promotionMap.keySet().parallelStream().filter(i -> i.contains(PromotionTypeEnum.SECKILL.name())).findFirst();
if (existSeckill.isPresent()) {
Seckill seckill = (Seckill) promotionMap.get(existSeckill.get());
// 计算限时抢购促销
// 计算秒杀活动促销
this.calculationSeckill(seckill, goodsSkuPromotionPrice);
seckill.setPromotionName(PromotionTypeEnum.SECKILL.name());
goodsSkuPromotionPrice.getJoinPromotion().add(seckill);
@ -338,9 +338,9 @@ public class PromotionPriceServiceImpl implements PromotionPriceService {
}
/**
* 计算限时抢购
* 计算秒杀活动
*
* @param seckill 限时抢购信息
* @param seckill 秒杀活动信息
* @param promotionPrice 商品SKU的计算促销信息
*/
private void calculationSeckill(Seckill seckill, GoodsSkuPromotionPriceDTO promotionPrice) {
@ -358,7 +358,7 @@ public class PromotionPriceServiceImpl implements PromotionPriceService {
promotionPrice.setDiscountPrice(discountPrice);
promotionPrice.setFinalePrice(seckillApply.getPrice());
} else {
log.error("购买数量超出限时抢购剩余数量");
log.error("购买数量超出秒杀活动剩余数量");
}
}
}

View File

@ -121,7 +121,7 @@ public class PromotionServiceImpl implements PromotionService {
queryWrapper.eq("promotion_status", PromotionStatusEnum.START.name());
queryWrapper.gt("start_time", new Date());
queryWrapper.lt("end_time", new Date());
// 获取当前进行的限时抢购活动
// 获取当前进行的秒杀活动活动
List<Seckill> seckillList = seckillService.list(queryWrapper);
if (seckillList != null && !seckillList.isEmpty()) {
for (Seckill seckill : seckillList) {
@ -375,7 +375,7 @@ public class PromotionServiceImpl implements PromotionService {
parseEndTime = cn.hutool.core.date.DateUtil.parse((format + " " + nextHour + ":59:59"), DateUtil.STANDARD_FORMAT);
}
seckill1.setStartTime(parseStartTime);
// 当时商品的限时抢购活动结束时间为下个时间段的开始
// 当时商品的秒杀活动活动结束时间为下个时间段的开始
seckill1.setEndTime(parseEndTime);
this.goodsIndexService.updateEsGoodsIndex(seckillApply.getSkuId(), seckill1, promotionTypeEnum.name() + "-" + seckillApply.getTimeLine(), seckillApply.getPrice());
}

View File

@ -3,12 +3,10 @@ package cn.lili.modules.promotion.serviceimpl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.cache.Cache;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.DateUtil;
import cn.lili.common.utils.PageUtil;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.PageVO;
import cn.lili.modules.goods.entity.dos.GoodsSku;
import cn.lili.modules.goods.service.GoodsSkuService;
@ -27,12 +25,9 @@ import cn.lili.modules.promotion.tools.PromotionCacheKeys;
import cn.lili.modules.promotion.tools.PromotionTools;
import cn.lili.modules.search.service.EsGoodsIndexService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
@ -75,15 +70,15 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
@Override
public List<SeckillTimelineVO> getSeckillTimeline() {
List<SeckillTimelineVO> timelineVoS = new ArrayList<>();
// 限时抢购缓存key
String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.toString(DateUtil.startOfTodDay(), DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT));
// 秒杀活动缓存key
String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.format(DateUtil.beginOfDay(new DateTime()), "yyyyMMdd"));
Map<Object, Object> cacheSeckill = cache.getHash(seckillCacheKey);
if (cacheSeckill == null || cacheSeckill.isEmpty()) {
// 如缓存中不存在则单独获取
try {
timelineVoS = getSeckillTimelineToCache(seckillCacheKey);
} catch (Exception e) {
log.error("获取限时抢购信息发生错误!", e);
log.error("获取秒杀活动信息发生错误!", e);
}
} else {
// 如缓存中存在则取缓存中转为展示的信息
@ -98,8 +93,8 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
@Override
public List<SeckillGoodsVO> getSeckillGoods(Integer timeline) {
List<SeckillGoodsVO> seckillGoodsVoS = new ArrayList<>();
// 限时抢购缓存key
String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.toString(DateUtil.startOfTodDay(), DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT));
// 秒杀活动缓存key
String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.format(DateUtil.beginOfDay(new DateTime()), "yyyyMMdd"));
Map<Object, Object> cacheSeckill = cache.getHash(seckillCacheKey);
if (cacheSeckill == null || cacheSeckill.isEmpty()) {
// 如缓存中不存在则单独获取
@ -116,57 +111,6 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
return seckillGoodsVoS;
}
@Override
public void auditBatchApply(String[] ids, String seckillId, String applyStatus, String failReason) {
if (ids == null || ids.length <= 0) {
throw new ServiceException("请提供要审核的商品");
}
if (StringUtils.isEmpty(applyStatus) || PromotionApplyStatusEnum.valueOf(applyStatus).name().isEmpty()) {
throw new ServiceException("提供的审批状态值不正确");
}
if (PromotionApplyStatusEnum.REFUSE.name().equals(applyStatus)) {
boolean isEmpty = StringUtils.isEmpty(failReason);
if (isEmpty) {
throw new ServiceException("在驳回状态下驳回原因必填");
}
}
SeckillVO seckillVO = this.mongoTemplate.findById(seckillId, SeckillVO.class);
if (seckillVO == null) {
log.error("编号为【" + seckillId + "】限时抢购请不存在");
throw new ServiceException();
}
List<SeckillApply> seckillApplyList = seckillVO.getSeckillApplyList();
for (String id : ids) {
Optional<SeckillApply> seckillApplyOptional = seckillApplyList.stream().filter(i -> i.getId().equals(id)).findFirst();
SeckillApply seckillApply;
if (seckillApplyOptional.isPresent()) {
seckillApply = seckillApplyOptional.get();
} else {
log.error("编号为【" + id + "】限时抢购的申请不存在");
throw new ServiceException();
}
seckillApply.setPromotionApplyStatus(PromotionApplyStatusEnum.valueOf(applyStatus).name());
seckillApply.setFailReason(failReason);
this.updateById(seckillApply);
if (PromotionApplyStatusEnum.PASS.name().equals(applyStatus)) {
//检查缓存中是否存在相同商品参与的限时抢购活动
checkCache(seckillVO.getStartTime().getTime());
}
}
seckillVO.setSeckillApplyList(seckillApplyList);
mongoTemplate.save(seckillVO);
}
@Override
public IPage<SeckillApply> getSeckillApplyFromMysql(SeckillSearchParams queryParam, PageVO pageVo) {
QueryWrapper<SeckillApply> queryWrapper = queryParam.wrapper();
return page(PageUtil.initPage(pageVo), queryWrapper);
}
@Override
public IPage<SeckillApply> getSeckillApplyFromMongo(SeckillSearchParams queryParam, PageVO pageVo) {
IPage<SeckillApply> seckillApplyIPage = new Page<>();
@ -201,133 +145,67 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
public void addSeckillApply(String seckillId, String storeId, List<SeckillApplyVO> seckillApplyList) {
SeckillVO seckill = mongoTemplate.findById(seckillId, SeckillVO.class);
if (seckill == null) {
throw new ServiceException("当前参与的限时抢购不存在!");
throw new ServiceException("当前参与的秒杀活动不存在!");
}
seckill.checkTime();
// 检查限时抢购申请是否合法
checkSeckillApplyList(seckill.getApplyEndTime().getTime(), seckill.getHours(), seckillApplyList, storeId);
String storeIds = seckill.getStoreIds() != null ? seckill.getStoreIds() : "";
boolean containsStore = false;
List<String> storeIdList = Arrays.asList(storeIds.split(","));
// 检查是否为已参加活动的店铺
if (CharSequenceUtil.isNotEmpty(seckillId) && !storeIdList.contains(storeId)) {
if (!CharSequenceUtil.isEmpty(storeIds)) {
String[] storeIdSplit = storeIds.split(",");
for (String s : storeIdSplit) {
if (s.equals(seckillId)) {
containsStore = true;
break;
}
}
storeIds = seckill.getStoreIds() + storeId + ",";
} else {
storeIds = storeId + ",";
}
seckill.setStoreIds(storeIds);
}
List<SeckillApply> originList = seckill.getSeckillApplyList();
// 检查秒杀活动申请是否合法
checkSeckillApplyList(seckill.getHours(), seckillApplyList, storeId);
//获取已参与活动的秒杀活动活动申请列表
List<SeckillApply> originList = seckill.getSeckillApplyList() != null ? seckill.getSeckillApplyList() : new ArrayList<>();
List<PromotionGoods> promotionGoodsList = new ArrayList<>();
if (originList == null) {
originList = new ArrayList<>();
}
for (SeckillApplyVO seckillApply : seckillApplyList) {
//获取参与活动的商品信息
GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(seckillApply.getSkuId());
//活动库存不能大于商品库存
if (goodsSku.getQuantity() < seckillApply.getQuantity()) {
throw new ServiceException(seckillApply.getGoodsName() + ",此商品库存不足");
}
/*
*************两种情况******************
团购时间段 |________________|
秒杀时间段 |_____| |_______|
************第三种情况******************
团购时间段 |______|
秒杀时间段 |________________|
************第四种情况******************
团购时间段 |________________|
秒杀时间段 |______|
这个商品的开始时间计算要用他参与的时间段来计算结束时间是当天晚上235959
*/
String startTimeStr = DateUtil.toString(seckill.getStartTime(), DateUtil.STANDARD_DATE_FORMAT) + " " + (seckillApply.getTimeLine() > 10 ? ("0" + seckillApply.getTimeLine()) : seckillApply.getTimeLine()) + ":00:00";
String endTimeStr = DateUtil.toString(seckill.getStartTime(), "yyyy-MM-dd") + " 23:59:59";
// 查询是否在同一时间段参与了拼团活动
Integer count = promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.PINTUAN.name(), goodsSku.getId(), DateUtil.toDate(startTimeStr, DateUtil.STANDARD_FORMAT), DateUtil.toDate(endTimeStr, DateUtil.STANDARD_FORMAT));
// 查询是否在同一时间段参与了限时抢购活动
count += promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.SECKILL.name(), goodsSku.getId(), DateUtil.toDate(startTimeStr, DateUtil.STANDARD_FORMAT), DateUtil.toDate(endTimeStr, DateUtil.STANDARD_FORMAT));
if (count > 0) {
throw new ServiceException("商品[" + goodsSku.getGoodsName() + "]已经在重叠的时间段参加了团购或限时抢购活动,不能参加限时抢购活动");
}
//获取秒杀活动时间段
DateTime startTime = DateUtil.offsetHour(seckill.getStartTime(), seckillApply.getTimeLine());
//检测是否可以发布促销商品
checkSeckillGoodsSKU(seckill, goodsSku, startTime);
//设置秒杀申请默认内容
seckillApply.setOriginalPrice(goodsSku.getPrice());
seckillApply.setPromotionApplyStatus(PromotionApplyStatusEnum.APPLY.name());
seckillApply.setPromotionApplyStatus(PromotionApplyStatusEnum.PASS.name());
seckillApply.setSalesNum(0);
Optional<SeckillApply> first = originList.stream().filter(i -> i.getSkuId().equals(seckillApply.getSkuId())).findFirst();
if (first.isPresent() && (first.get().getPromotionApplyStatus().equals(PromotionApplyStatusEnum.REFUSE.name()) || first.get().getPromotionApplyStatus().equals(PromotionApplyStatusEnum.APPLY.name()))) {
originList = originList.stream().filter(i -> !i.getSkuId().equals(seckillApply.getSkuId())).collect(Collectors.toList());
} else if (first.isPresent() && first.get().getPromotionApplyStatus().equals(PromotionApplyStatusEnum.PASS.name())) {
continue;
}
originList.add(seckillApply);
PromotionGoods promotionGoods = new PromotionGoods(goodsSku);
promotionGoods.setPrice(seckillApply.getPrice());
promotionGoods.setQuantity(seckillApply.getQuantity());
// 设置单独每个促销商品的结束时间
int nextHour = 23;
String[] split = seckill.getHours().split(",");
int[] hoursSored = Arrays.stream(split).mapToInt(Integer::parseInt).toArray();
Arrays.sort(hoursSored);
for (int i : hoursSored) {
if (seckillApply.getTimeLine() < i) {
nextHour = i;
break;
}
}
String format = cn.hutool.core.date.DateUtil.format(seckill.getStartTime(), DateUtil.STANDARD_DATE_FORMAT);
DateTime parseStartTime = cn.hutool.core.date.DateUtil.parse((format + " " + seckillApply.getTimeLine()), "yyyy-MM-dd HH");
DateTime parseEndTime = cn.hutool.core.date.DateUtil.parse((format + " " + nextHour), "yyyy-MM-dd HH");
// 如果是当天最后的时间段则设置到当天结束时间的59分59秒
if (nextHour == seckillApply.getTimeLine()) {
parseEndTime = cn.hutool.core.date.DateUtil.parse((format + " " + nextHour + ":59:59"), DateUtil.STANDARD_FORMAT);
}
promotionGoods.setStartTime(parseStartTime);
promotionGoods.setEndTime(parseEndTime);
//获取促销商品
PromotionGoods promotionGoods = this.setSeckillGoods(goodsSku, seckillApply, seckill);
promotionGoodsList.add(promotionGoods);
}
//保存秒杀活动申请
this.saveOrUpdateBatch(originList);
//设置秒杀活动下的申请列表
seckill.setSeckillApplyList(originList);
//mongo保存秒杀活动信息
this.mongoTemplate.save(seckill);
//保存促销活动商品信息
if (!promotionGoodsList.isEmpty()) {
LambdaQueryWrapper<PromotionGoods> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(PromotionGoods::getSkuId, promotionGoodsList.stream().map(PromotionGoods::getSkuId).collect(Collectors.toList())).eq(PromotionGoods::getStoreId, storeId);
queryWrapper.in(PromotionGoods::getSkuId, promotionGoodsList.stream().map(PromotionGoods::getSkuId).collect(Collectors.toList()))
.eq(PromotionGoods::getStoreId, storeId);
promotionGoodsService.remove(queryWrapper);
//初始化促销商品
PromotionTools.promotionGoodsInit(promotionGoodsList, seckill, PromotionTypeEnum.SECKILL);
promotionGoodsService.saveBatch(promotionGoodsList);
}
if (Boolean.FALSE.equals(containsStore)) {
seckillService.storeApply(storeId, seckill.getId());
}
}
/**
* 批量删除限时抢购申请
* 批量删除秒杀活动申请
*
* @param seckillId 限时抢购活动id
* @param ids 限时抢购申请id集合
* @param seckillId 秒杀活动活动id
* @param ids 秒杀活动申请id集合
*/
@Override
public void removeSeckillApplyByIds(String seckillId, List<String> ids) {
SeckillVO seckillVO = this.mongoTemplate.findById(seckillId, SeckillVO.class);
if (seckillVO == null) {
throw new ServiceException("当前限时抢购活动不存在!");
throw new ServiceException("当前秒杀活动活动不存在!");
}
if (seckillVO.getPromotionStatus().equals(PromotionStatusEnum.START.name())) {
throw new ServiceException("当前限时抢购活动已经开始,无法修改!");
throw new ServiceException("当前秒杀活动活动已经开始,无法修改!");
}
seckillVO.getSeckillApplyList().removeIf(seckillApply -> ids.contains(seckillApply.getId()));
this.mongoTemplate.save(seckillVO);
@ -335,78 +213,26 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
}
/**
* 更新限时抢购库存数量
* 检查秒杀活动申请列表参数信息
*
* @param id 限时抢购申请限时抢购商品id
* @param num 数量
* @return 是否成功
*/
@Override
public boolean updateSeckillStock(String id, Integer num) {
List<String> promotionIds = this.esGoodsIndexService.getPromotionIdByPromotionType(id, PromotionTypeEnum.SECKILL);
for (String promotionId : promotionIds) {
SeckillVO seckillVO = this.mongoTemplate.findById(promotionId, SeckillVO.class);
if (seckillVO == null) {
log.error("限时抢购活动id为" + promotionId + "的限时抢购活动不存在!");
break;
}
if (seckillVO.getSeckillApplyList() != null && !seckillVO.getSeckillApplyList().isEmpty()) {
Optional<SeckillApply> seckillApplyOptional = seckillVO.getSeckillApplyList().stream().filter(i -> i.getSkuId().equals(id)).findFirst();
if (seckillApplyOptional.isPresent()) {
SeckillApply seckillApply = seckillApplyOptional.get();
// 设置售卖数量
Integer countNum = seckillApply.getSalesNum() + num;
seckillApply.setSalesNum(countNum);
LambdaUpdateWrapper<SeckillApply> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(SeckillApply::getId, seckillApply.getId());
updateWrapper.set(SeckillApply::getQuantity, seckillApply.getQuantity() - num);
updateWrapper.set(SeckillApply::getSalesNum, countNum);
this.update(updateWrapper);
this.mongoTemplate.save(seckillVO);
}
}
}
return true;
}
/**
* 更新限时抢购库存数量
*
* @param map key 限时抢购申请限时抢购商品id value 为数量
* @return 是否成功
*/
@Override
public boolean updateSeckillStock(Map<String, Integer> map) {
boolean result = false;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
result = this.updateSeckillStock(entry.getKey(), entry.getValue());
}
return result;
}
/**
* 检查限时抢购申请列表参数信息
*
* @param applyEndTime 申请结束时间
* @param hours 限时抢购时间段
* @param seckillApplyList 限时抢购申请列表
* @param hours 秒杀活动时间段
* @param seckillApplyList 秒杀活动申请列表
* @param storeId 当前申请商家编号
*/
private void checkSeckillApplyList(Long applyEndTime, String hours, List<SeckillApplyVO> seckillApplyList, String storeId) {
private void checkSeckillApplyList(String hours, List<SeckillApplyVO> seckillApplyList, String storeId) {
List<String> existSku = new ArrayList<>();
for (SeckillApplyVO seckillApply : seckillApplyList) {
seckillApply.setStoreId(storeId);
if (seckillApply.getPrice() > seckillApply.getOriginalPrice()) {
throw new ServiceException("活动价格不能大于商品原价");
}
// 检查限时抢购申请的时刻是否存在在限时抢购的时间段内
// 检查秒杀活动申请的时刻是否存在在秒杀活动的时间段内
String[] rangeHours = hours.split(",");
boolean containsSame = Arrays.stream(rangeHours).anyMatch(i -> i.equals(seckillApply.getTimeLine().toString()));
if (!containsSame) {
throw new ServiceException("时刻参数异常");
}
//检查商品是否参加多个时间段的活动
if (existSku.contains(seckillApply.getSkuId())) {
throw new ServiceException(seckillApply.getGoodsName() + "该商品不能同时参加多个时间段的活动");
} else {
@ -419,13 +245,13 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
/**
* 组装促销商品信息
*
* @param seckillApply 限时抢购申请信息
* @param seckillStartTime 当前限时抢购申请的开始时间
* @param seckillApply 秒杀活动申请信息
* @param seckillStartTime 当前秒杀活动申请的开始时间
* @return 促销商品信息
*/
private PromotionGoods setPromotionGoods(SeckillApply seckillApply, Date seckillStartTime) {
PromotionGoods promotionGoods = new PromotionGoods();
promotionGoods.setTitle("限时抢购");
promotionGoods.setTitle("秒杀活动");
promotionGoods.setSkuId(seckillApply.getSkuId());
promotionGoods.setPromotionType(PromotionTypeEnum.SECKILL.name());
promotionGoods.setPromotionId(seckillApply.getSeckillId());
@ -435,9 +261,9 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
promotionGoods.setPromotionStatus(PromotionStatusEnum.NEW.name());
//商品活动的开始时间为当前商品的参加时间段
int timeLine = seckillApply.getTimeLine();
String date = DateUtil.toString(seckillStartTime, DateUtil.STANDARD_DATE_FORMAT);
long startTime = DateUtil.getDateline(date + " " + timeLine + ":00:00", DateUtil.STANDARD_FORMAT);
long endTime = DateUtil.getDateline(date + " 23:59:59", DateUtil.STANDARD_FORMAT);
String date = cn.lili.common.utils.DateUtil.toString(seckillStartTime, cn.lili.common.utils.DateUtil.STANDARD_DATE_FORMAT);
long startTime = cn.lili.common.utils.DateUtil.getDateline(date + " " + timeLine + ":00:00", cn.lili.common.utils.DateUtil.STANDARD_FORMAT);
long endTime = cn.lili.common.utils.DateUtil.getDateline(date + " 23:59:59", cn.lili.common.utils.DateUtil.STANDARD_FORMAT);
promotionGoods.setStartTime(new Date(startTime));
promotionGoods.setEndTime(new Date(endTime));
@ -445,30 +271,30 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
}
/**
* 检查缓存中是否存在相同商品参与的限时抢购活动
* 检查缓存中是否存在相同商品参与的秒杀活动活动
*
* @param startTime 限时抢购开始时间
* @param startTime 秒杀活动开始时间
*/
private void checkCache(Long startTime) {
String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.toString(startTime, DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT));
String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(cn.lili.common.utils.DateUtil.toString(startTime, cn.lili.common.utils.DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT));
Map<Object, Object> hash = cache.getHash(seckillCacheKey);
//如果缓存中存在当前审核商品参与的限时抢购活动商品信息清除
//如果缓存中存在当前审核商品参与的秒杀活动活动商品信息清除
if (hash != null && !hash.isEmpty()) {
cache.remove(seckillCacheKey);
}
}
/**
* 从缓存中获取限时抢购信息
* 从缓存中获取秒杀活动信息
*
* @param seckillCacheKey 限时抢购缓存键
* @return 限时抢购信息
* @param seckillCacheKey 秒杀活动缓存键
* @return 秒杀活动信息
*/
private List<SeckillTimelineVO> getSeckillTimelineToCache(String seckillCacheKey) {
List<SeckillTimelineVO> timelineList = new ArrayList<>();
LambdaQueryWrapper<Seckill> queryWrapper = new LambdaQueryWrapper<>();
// 查询当天时间段内的且状态不为结束或关闭的限时抢购活动
queryWrapper.gt(Seckill::getStartTime, new Date(DateUtil.startOfTodDay() * 1000)).lt(Seckill::getEndTime, DateUtil.endOfDate())
// 查询当天时间段内的且状态不为结束或关闭的秒杀活动活动
queryWrapper.gt(Seckill::getStartTime, new Date(cn.lili.common.utils.DateUtil.startOfTodDay() * 1000)).lt(Seckill::getEndTime, cn.lili.common.utils.DateUtil.endOfDate())
.and(i -> i.eq(Seckill::getPromotionStatus, PromotionStatusEnum.NEW.name())
.or(j -> j.eq(Seckill::getPromotionStatus, PromotionStatusEnum.START.name())));
List<Seckill> seckillList = seckillService.list(queryWrapper);
@ -483,14 +309,14 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
for (int i = 0; i < hoursSored.length; i++) {
SeckillTimelineVO tempTimeline = new SeckillTimelineVO();
if (hoursSored[i] >= hour || ((i + 1) < hoursSored.length && hoursSored[i + 1] > hour)) {
SimpleDateFormat format = new SimpleDateFormat(DateUtil.STANDARD_DATE_FORMAT);
SimpleDateFormat format = new SimpleDateFormat(cn.lili.common.utils.DateUtil.STANDARD_DATE_FORMAT);
String date = format.format(new Date());
//当前时间的秒数
long currentTime = DateUtil.getDateline();
//限时抢购的时刻
long timeLine = DateUtil.getDateline(date + " " + hoursSored[i], "yyyy-MM-dd HH");
long currentTime = cn.lili.common.utils.DateUtil.getDateline();
//秒杀活动的时刻
long timeLine = cn.lili.common.utils.DateUtil.getDateline(date + " " + hoursSored[i], "yyyy-MM-dd HH");
if ((i + 1) < hoursSored.length && hour > hoursSored[i] && hour <= hoursSored[i + 1]) {
timeLine = DateUtil.getDateline(date + " " + hoursSored[i + 1], "yyyy-MM-dd HH");
timeLine = cn.lili.common.utils.DateUtil.getDateline(date + " " + hoursSored[i + 1], "yyyy-MM-dd HH");
}
Long distanceTime = timeLine - currentTime < 0 ? 0 : timeLine - currentTime;
tempTimeline.setDistanceStartTime(distanceTime);
@ -507,16 +333,16 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
}
/**
* 组装当时间限时抢购的商品数据
* 组装当时间秒杀活动的商品数据
* w
*
* @param startTimeline 限时抢购开始时刻
* @return 当时间限时抢购的商品数据
* @param startTimeline 秒杀活动开始时刻
* @return 当时间秒杀活动的商品数据
*/
private List<SeckillGoodsVO> wrapperSeckillGoods(Integer startTimeline) {
List<SeckillGoodsVO> seckillGoodsVoS = new ArrayList<>();
LambdaQueryWrapper<Seckill> seckillLambdaQueryWrapper = new LambdaQueryWrapper<>();
seckillLambdaQueryWrapper.gt(Seckill::getStartTime, new Date(DateUtil.startOfTodDay() * 1000)).lt(Seckill::getEndTime, DateUtil.endOfDate())
seckillLambdaQueryWrapper.gt(Seckill::getStartTime, new Date(cn.lili.common.utils.DateUtil.startOfTodDay() * 1000)).lt(Seckill::getEndTime, cn.lili.common.utils.DateUtil.endOfDate())
.and(i -> i.eq(Seckill::getPromotionStatus, PromotionStatusEnum.NEW.name())
.or(j -> j.eq(Seckill::getPromotionStatus, PromotionStatusEnum.START.name())));
List<Seckill> seckillList = this.seckillService.list(seckillLambdaQueryWrapper);
@ -539,4 +365,53 @@ public class SeckillApplyServiceImpl extends ServiceImpl<SeckillApplyMapper, Sec
}
return seckillGoodsVoS;
}
/**
* 检测秒杀申请的商品
*
* @param seckill 秒杀活动
* @param goodsSku 商品SKU
* @param startTime 秒杀时段开启时间
*/
private void checkSeckillGoodsSKU(SeckillVO seckill, GoodsSku goodsSku, DateTime startTime) {
// 查询是否在同一时间段参与了拼团活动
if (promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.PINTUAN.name(), goodsSku.getId(), startTime, seckill.getEndTime()) > 0) {
throw new ServiceException("商品[" + goodsSku.getGoodsName() + "]已经在重叠的时间段参加了拼团活动,不能参加秒杀活动");
}
// 查询是否在同一时间段参与了秒杀活动活动
if (promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.SECKILL.name(), goodsSku.getId(), startTime, seckill.getEndTime()) > 0) {
throw new ServiceException("商品[" + goodsSku.getGoodsName() + "]已经在重叠的时间段参加了秒杀活动,不能参加秒杀活动活动");
}
}
/**
* 获取秒杀活动促销商品
*
* @param goodsSku 商品SKU
* @param seckillApply 秒杀活动申请
* @param seckill 秒杀活动
* @return 秒杀活动促销商品
*/
private PromotionGoods setSeckillGoods(GoodsSku goodsSku, SeckillApply seckillApply, Seckill seckill) {
//设置促销商品默认内容
PromotionGoods promotionGoods = new PromotionGoods(goodsSku);
promotionGoods.setPrice(seckillApply.getPrice());
promotionGoods.setQuantity(seckillApply.getQuantity());
// 设置单独每个促销商品的结束时间
int nextHour = 23;
String[] split = seckill.getHours().split(",");
int[] hoursSored = Arrays.stream(split).mapToInt(Integer::parseInt).toArray();
Arrays.sort(hoursSored);
for (int i : hoursSored) {
if (seckillApply.getTimeLine() < i) {
nextHour = i;
break;
}
}
DateTime startTime = DateUtil.offsetHour(seckill.getStartTime(), seckillApply.getTimeLine());
DateTime parseEndTime = DateUtil.offsetSecond(DateUtil.offsetHour(seckill.getStartTime(), nextHour), 1);
promotionGoods.setStartTime(startTime);
promotionGoods.setEndTime(parseEndTime);
return promotionGoods;
}
}

View File

@ -7,6 +7,7 @@ import cn.lili.common.exception.ServiceException;
import cn.lili.common.trigger.interfaces.TimeTrigger;
import cn.lili.common.trigger.model.TimeExecuteConstant;
import cn.lili.common.trigger.model.TimeTriggerMsg;
import cn.lili.common.utils.BeanUtil;
import cn.lili.common.utils.DateUtil;
import cn.lili.common.utils.PageUtil;
import cn.lili.common.utils.StringUtils;
@ -43,7 +44,7 @@ import java.util.List;
import java.util.stream.Collectors;
/**
* 限时抢购业务层实现
* 秒杀活动业务层实现
*
* @author Chopper
* @date 2020/8/21
@ -77,13 +78,6 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
return page(PageUtil.initPage(pageVo), queryWrapper);
}
/**
* 从mongo中根据条件获取限时抢购分页列表
*
* @param queryParam 查询参数
* @param pageVo 分页参数
* @return 限时抢购分页列表
*/
@Override
public IPage<SeckillVO> getSeckillByPageFromMongo(SeckillSearchParams queryParam, PageVO pageVo) {
IPage<SeckillVO> seckill = new Page<>(pageVo.getPageNumber(), pageVo.getPageSize());
@ -101,27 +95,24 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
return seckill;
}
/**
* 从mongo中获取限时抢购信息
*
* @param id 限时抢购id
* @return 限时抢购信息
*/
@Override
public SeckillVO getSeckillByIdFromMongo(String id) {
return this.checkSeckillExist(id);
}
@Override
public boolean saveSeckill(SeckillVO seckill) {
// 检查限时抢购参数
checkSeckillParam(seckill, seckill.getStoreId());
seckill.setPromotionStatus(PromotionStatusEnum.NEW.name());
public boolean saveSeckill(Seckill seckill) {
SeckillVO seckillVO=new SeckillVO();
BeanUtil.copyProperties(seckill,seckillVO);
// 检查秒杀活动参数
checkSeckillParam(seckillVO, seckill.getStoreId());
// 保存到MYSQL中
boolean result = this.save(seckill);
// 保存到MONGO中
this.mongoTemplate.save(seckill);
this.addSeckillStartTask(seckill);
//添加秒杀延时任务
this.addSeckillStartTask(seckillVO);
return result;
}
@ -135,17 +126,18 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
storeIds = storeId + ",";
}
seckill.setStoreIds(storeIds);
this.updateById(seckill);
}
@Override
public boolean modifySeckill(SeckillVO seckillVO) {
// 检查该限时抢购是否存在
// 检查该秒杀活动是否存在
SeckillVO seckill = checkSeckillExist(seckillVO.getId());
if (PromotionStatusEnum.START.name().equals(seckillVO.getPromotionStatus())) {
throw new ServiceException("活动已经开始,不能进行编辑删除操作");
}
// 检查限时抢购参数
// 检查秒杀活动参数
this.checkSeckillParam(seckillVO, seckillVO.getStoreId());
// 更新到MYSQL中
@ -170,7 +162,7 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
public void deleteSeckill(String id) {
Seckill seckill = checkSeckillExist(id);
if (PromotionStatusEnum.CLOSE.name().equals(seckill.getPromotionStatus()) || PromotionStatusEnum.END.name().equals(seckill.getPromotionStatus())) {
// 更新限时抢购状态为关闭标示删除标志
// 更新秒杀活动状态为关闭标示删除标志
LambdaUpdateWrapper<Seckill> updateWrapper = new LambdaUpdateWrapper<Seckill>().eq(Seckill::getId, id).set(Seckill::getDeleteFlag, true).set(Seckill::getPromotionStatus, PromotionStatusEnum.CLOSE.name());
this.update(updateWrapper);
LambdaUpdateWrapper<SeckillApply> seckillApplyLambdaUpdateWrapper = new LambdaUpdateWrapper<SeckillApply>().eq(SeckillApply::getSeckillId, id).set(SeckillApply::getDeleteFlag, true);
@ -183,14 +175,14 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.SECKILL.name() + seckill.getId())),
rocketmqCustomProperties.getPromotionTopic());
} else {
throw new ServiceException("限时抢购活动的状态不能删除");
throw new ServiceException("秒杀活动活动的状态不能删除");
}
}
/**
* 开启一个限时抢购
* 开启一个秒杀活动
*
* @param id 限时抢购编号
* @param id 秒杀活动编号
*/
@Override
public void openSeckill(String id) {
@ -227,7 +219,7 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
rocketmqCustomProperties.getPromotionTopic());
}
} else {
throw new ServiceException("限时抢购活动的状态不能关闭");
throw new ServiceException("秒杀活动活动的状态不能关闭");
}
}
@ -240,6 +232,10 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
return this.count(queryWrapper);
}
/**
* 添加秒杀活动延时任务
* @param seckill 秒杀活动
*/
private void addSeckillStartTask(SeckillVO seckill) {
PromotionMessage promotionMessage = new PromotionMessage(seckill.getId(), PromotionTypeEnum.SECKILL.name(), PromotionStatusEnum.START.name(), seckill.getStartTime(), seckill.getEndTime());
TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR,
@ -252,27 +248,26 @@ public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> impl
}
/**
* 检查该限时抢购是否存在
* 检查该秒杀活动是否存在
*
* @param id 限时抢购编号
* @return 限时抢购信息
* @param id 秒杀活动编号
* @return 秒杀活动信息
*/
private SeckillVO checkSeckillExist(String id) {
SeckillVO seckill = this.mongoTemplate.findById(id, SeckillVO.class);
if (seckill == null) {
throw new ServiceException("当前限时抢购活动不存在");
throw new ServiceException("当前秒杀活动活动不存在");
}
return seckill;
}
/**
* 检查限时抢购参数
* 检查秒杀活动参数
*
* @param seckill 限时抢购信息
* @param seckill 秒杀活动信息
* @param storeId 卖家编号
*/
private void checkSeckillParam(SeckillVO seckill, String storeId) {
seckill.checkTime();
// 同一时间段内相同的活动
QueryWrapper<Seckill> queryWrapper = PromotionTools.checkActiveTime(seckill.getStartTime(), seckill.getEndTime(), PromotionTypeEnum.SECKILL, storeId, seckill.getId());
int sameNum = this.count(queryWrapper);

View File

@ -28,7 +28,7 @@ public class PromotionCacheKeys {
}
/**
* 读取满优惠redis key
* 读取秒杀活动redis key
* @param timeStr 时间字符串格式为 yyyyMMdd
* @return 满优惠redis key
*/

View File

@ -0,0 +1,25 @@
package cn.lili.modules.system.entity.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 秒杀活动设置
*
* @author Bulbasaur
* @date: 2021/5/24 3:27 下午
*/
@Data
public class SeckillSetting implements Serializable {
@ApiModelProperty(value = "开启几点场 例如6,8,12")
@NotNull(message = "活动时间段不能为空")
private String hours;
@ApiModelProperty(value = "秒杀规则")
@NotNull(message = "秒杀规则不能为空")
private String seckillRule;
}

View File

@ -29,6 +29,8 @@ public enum SettingEnum {
POINT_SETTING,
//经验值设置
EXPERIENCE_SETTING,
//经验值设置
SECKILL_SETTING,
//微信 联合登陆设置
WECHAT_CONNECT,

View File

@ -27,8 +27,8 @@ import org.springframework.web.bind.annotation.*;
* @date: 2021/5/21 7:11 下午
*/
@RestController
@Api(tags = "管理端,优惠券接口")
@RequestMapping("/manager/promotion/coupon")
@Api(tags = "管理端,优惠券活动接口")
@RequestMapping("/manager/promotion/couponActivity")
public class CouponActivityManagerController {
@Autowired

View File

@ -19,13 +19,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 管理端,限时抢购接口
* 管理端,秒杀活动接口
*
* @author paulG
* @date 2020/8/20
**/
@RestController
@Api(tags = "管理端,限时抢购接口")
@Api(tags = "管理端,秒杀活动接口")
@RequestMapping("/manager/promotion/seckill")
public class SeckillManagerController {
@Autowired
@ -34,7 +34,7 @@ public class SeckillManagerController {
private SeckillApplyService seckillApplyService;
@PostMapping
@ApiOperation(value = "添加限时抢购")
@ApiOperation(value = "添加秒杀活动")
public ResultMessage<Seckill> addSeckill(SeckillVO seckillVO) {
AuthUser currentUser = UserContext.getCurrentUser();
seckillVO.setStoreId(currentUser.getId());
@ -45,7 +45,7 @@ public class SeckillManagerController {
}
@PutMapping
@ApiOperation(value = "修改限时抢购")
@ApiOperation(value = "修改秒杀活动")
public ResultMessage<Seckill> updateSeckill(SeckillVO seckillVO) {
AuthUser currentUser = UserContext.getCurrentUser();
seckillVO.setStoreId(currentUser.getId());
@ -62,7 +62,7 @@ public class SeckillManagerController {
}
@GetMapping
@ApiOperation(value = "分页查询限时抢购列表")
@ApiOperation(value = "分页查询秒杀活动列表")
public ResultMessage<IPage<SeckillVO>> getAll(SeckillSearchParams param, PageVO pageVo) {
pageVo.setNotConvert(true);
IPage<SeckillVO> page = seckillService.getSeckillByPageFromMongo(param, pageVo);
@ -70,38 +70,31 @@ public class SeckillManagerController {
}
@DeleteMapping("/{id}")
@ApiOperation(value = "删除一个限时抢购")
@ApiOperation(value = "删除一个秒杀活动")
public ResultMessage<Object> deleteSeckill(@PathVariable String id) {
seckillService.deleteSeckill(id);
return ResultUtil.success();
}
@PutMapping("/close/{id}")
@ApiOperation(value = "关闭一个限时抢购")
@ApiOperation(value = "关闭一个秒杀活动")
public ResultMessage<Object> closeSeckill(@PathVariable String id) {
seckillService.closeSeckill(id);
return ResultUtil.success();
}
@PutMapping("/open/{id}")
@ApiOperation(value = "一个限时抢购")
@ApiOperation(value = "一个秒杀活动")
public ResultMessage<Object> openSeckill(@PathVariable String id) {
seckillService.openSeckill(id);
return ResultUtil.success();
}
@GetMapping("/apply")
@ApiOperation(value = "获取限时抢购申请列表")
@ApiOperation(value = "获取秒杀活动申请列表")
public ResultMessage<IPage<SeckillApply>> getSeckillApply(SeckillSearchParams param, PageVO pageVo) {
IPage<SeckillApply> seckillApply = seckillApplyService.getSeckillApplyFromMongo(param, pageVo);
return ResultUtil.data(seckillApply);
}
@PutMapping("/apply/audit/{ids}")
@ApiOperation(value = "审核多个限时抢购申请")
public ResultMessage<Object> auditSeckillApply(@PathVariable String[] ids, String seckillId, String applyStatus, String failReason) {
seckillApplyService.auditBatchApply(ids, seckillId, applyStatus, failReason);
return ResultUtil.success();
}
}

View File

@ -83,11 +83,4 @@ class SeckillTest {
Assertions.assertTrue(true);
}
@Test
void audit() {
seckillApplyService.auditBatchApply(new String[]{"1327169604003061760"}, "123456", PromotionApplyStatusEnum.PASS.name(), "");
Assertions.assertTrue(true);
}
}

View File

@ -22,13 +22,13 @@ import java.util.Arrays;
import java.util.List;
/**
* 店铺端,限时抢购接口
* 店铺端,秒杀活动接口
*
* @author paulG
* @date 2020/8/26
**/
@RestController
@Api(tags = "店铺端,限时抢购接口")
@Api(tags = "店铺端,秒杀活动接口")
@RequestMapping("/store/promotion/seckill")
public class SeckillStoreController {
@Autowired
@ -37,14 +37,14 @@ public class SeckillStoreController {
private SeckillApplyService seckillApplyService;
@GetMapping
@ApiOperation(value = "获取限时抢购列表")
@ApiOperation(value = "获取秒杀活动列表")
public ResultMessage<IPage<SeckillVO>> getSeckillPage(SeckillSearchParams queryParam, PageVO pageVo) {
IPage<SeckillVO> seckillPage = seckillService.getSeckillByPageFromMongo(queryParam, pageVo);
return ResultUtil.data(seckillPage);
}
@GetMapping("/apply")
@ApiOperation(value = "获取限时抢购申请列表")
@ApiOperation(value = "获取秒杀活动申请列表")
public ResultMessage<IPage<SeckillApply>> getSeckillApplyPage(SeckillSearchParams queryParam, PageVO pageVo) {
String storeId = UserContext.getCurrentUser().getStoreId();
queryParam.setStoreId(storeId);
@ -53,19 +53,19 @@ public class SeckillStoreController {
}
@GetMapping("/{seckillId}")
@ApiOperation(value = "获取限时抢购")
@ApiOperation(value = "获取秒杀活动")
public ResultMessage<Seckill> getSeckill(@PathVariable String seckillId) {
return ResultUtil.data(seckillService.getSeckillByIdFromMongo(seckillId));
}
@GetMapping("/apply/{seckillApplyId}")
@ApiOperation(value = "获取限时抢购申请")
@ApiOperation(value = "获取秒杀活动申请")
public ResultMessage<SeckillApply> getSeckillApply(@PathVariable String seckillApplyId) {
return ResultUtil.data(seckillApplyService.getById(seckillApplyId));
}
@PostMapping(path = "/apply/{seckillId}", consumes = "application/json", produces = "application/json")
@ApiOperation(value = "添加限时抢购申请")
@ApiOperation(value = "添加秒杀活动申请")
public ResultMessage<String> addSeckillApply(@PathVariable String seckillId, @RequestBody List<SeckillApplyVO> applyVos) {
String storeId = UserContext.getCurrentUser().getStoreId();
seckillApplyService.addSeckillApply(seckillId, storeId, applyVos);
@ -73,7 +73,7 @@ public class SeckillStoreController {
}
@DeleteMapping("/apply/{seckillId}/{ids}")
@ApiOperation(value = "删除限时抢购申请")
@ApiOperation(value = "删除秒杀活动申请")
public ResultMessage<String> deleteSeckillApply(@PathVariable("seckillId") String seckillId, @PathVariable("ids") String ids) {
String[] idsSplit = ids.split(",");
seckillApplyService.removeSeckillApplyByIds(seckillId, Arrays.asList(idsSplit));