package cn.lili.modules.promotion.serviceimpl; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapBuilder; import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.json.JSONUtil; import cn.lili.common.enums.PromotionTypeEnum; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; import cn.lili.common.properties.RocketmqCustomProperties; import cn.lili.modules.promotion.entity.dos.Seckill; import cn.lili.modules.promotion.entity.dos.SeckillApply; import cn.lili.modules.promotion.entity.dto.search.SeckillSearchParams; import cn.lili.modules.promotion.entity.enums.PromotionsApplyStatusEnum; import cn.lili.modules.promotion.entity.vos.SeckillVO; import cn.lili.modules.promotion.mapper.SeckillMapper; import cn.lili.modules.promotion.service.SeckillApplyService; import cn.lili.modules.promotion.service.SeckillService; import cn.lili.modules.promotion.tools.PromotionTools; 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.rocketmq.RocketmqSendCallbackBuilder; import cn.lili.rocketmq.tags.GoodsTagsEnum; 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.conditions.update.UpdateWrapper; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; /** * 秒杀活动业务层实现 * * @author Chopper * @since 2020/8/21 */ @Service @Slf4j public class SeckillServiceImpl extends AbstractPromotionsServiceImpl implements SeckillService { /** * 设置 */ @Autowired private SettingService settingService; @Autowired private SeckillApplyService seckillApplyService; /** * rocketMq配置 */ @Autowired private RocketmqCustomProperties rocketmqCustomProperties; /** * rocketMq */ @Autowired private RocketMQTemplate rocketMQTemplate; @Override public SeckillVO getSeckillDetail(String id) { Seckill seckill = this.checkSeckillExist(id); SeckillVO seckillVO = new SeckillVO(); BeanUtils.copyProperties(seckill, seckillVO); SeckillSearchParams searchParams = new SeckillSearchParams(); searchParams.setSeckillId(id); seckillVO.setSeckillApplyList(this.seckillApplyService.getSeckillApplyList(searchParams)); return seckillVO; } @Override @Transactional(rollbackFor = Exception.class) public void init() { //清除演示数据 List seckillList = this.list(); for (Seckill seckill : seckillList) { seckill.setStartTime(null); seckill.setEndTime(null); this.updateEsGoodsIndex(seckill); } this.remove(new QueryWrapper<>()); Setting setting = settingService.get(SettingEnum.SECKILL_SETTING.name()); SeckillSetting seckillSetting = new Gson().fromJson(setting.getSettingValue(), SeckillSetting.class); for (int i = 1; i <= PRE_CREATION; i++) { Seckill seckill = new Seckill(i, seckillSetting.getHours(), seckillSetting.getSeckillRule()); this.savePromotions(seckill); } } @Override public long getApplyNum() { DateTime now = DateUtil.date(); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.ge(Seckill::getApplyEndTime, now); queryWrapper.le(Seckill::getStartTime, now); queryWrapper.ge(Seckill::getEndTime, now); return this.count(queryWrapper); } @Override public void updateSeckillGoodsNum(String seckillId) { Seckill seckill = this.getById(seckillId); if (seckill != null) { SeckillSearchParams searchParams = new SeckillSearchParams(); searchParams.setSeckillId(seckillId); LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(Seckill::getId, seckillId); updateWrapper.set(Seckill::getGoodsNum, this.seckillApplyService.getSeckillApplyCount(searchParams)); this.update(updateWrapper); } } /** * 通用促销更新 * 调用顺序: * 1. checkStatus 检查促销状态 * 2. checkPromotions 检查促销参数 * 3. saveOrUpdate 保存促销信息 * 4. updatePromotionGoods 更新促销商品信息 * 5. updateEsGoodsIndex 更新商品索引促销信息 * * @param promotions 促销信息 * @return 是否更新成功 */ @Override @Transactional(rollbackFor = Exception.class) public boolean updatePromotions(Seckill promotions) { this.checkStatus(promotions); this.checkPromotions(promotions); //如果申请结束时间在当前时间之前 if (promotions.getApplyEndTime().before(new Date()) || promotions.getApplyEndTime().after(promotions.getStartTime())) { throw new ServiceException(ResultCode.APPLY_END_TIME_ERROR); } boolean result = this.updateById(promotions); seckillApplyService.updateSeckillApplyTime(promotions); return result; } /** * 更新商品索引限时抢购信息 * * @param seckill 限时抢购信息 */ @Override @Transactional(rollbackFor = Exception.class) public void updateEsGoodsSeckill(Seckill seckill, List seckillApplies) { if (seckillApplies != null && !seckillApplies.isEmpty()) { // 更新促销范围 seckill.setScopeId(ArrayUtil.join(seckillApplies.stream().map(SeckillApply::getSkuId).toArray(), ",")); UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", seckill.getId()); updateWrapper.set("scope_id", seckill.getScopeId()); this.update(updateWrapper); //循环秒杀商品数据,将数据按照时间段进行存储 for (SeckillApply seckillApply : seckillApplies) { if (seckillApply.getPromotionApplyStatus().equals(PromotionsApplyStatusEnum.PASS.name())) { this.setSeckillApplyTime(seckill, seckillApply); } } if (!seckillApplies.isEmpty()) { this.updateEsGoodsIndex(seckill); } } } /** * 删除商品索引限时抢购信息 * * @param seckill 限时抢购信息 * @param skuIds 商品skuId列表 */ @Override public void deleteEsGoodsSeckill(Seckill seckill, List skuIds) { Map build = MapBuilder.create().put("promotionKey", this.getPromotionType() + "-" + seckill.getId()).put("scopeId", ArrayUtil.join(skuIds.toArray(), ",")).build(); //删除商品促销消息 String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.DELETE_GOODS_INDEX_PROMOTIONS.name(); //发送mq消息 rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(build), RocketmqSendCallbackBuilder.commonCallback()); } @Override public void setSeckillApplyTime(Seckill seckill, SeckillApply seckillApply) { //下一个时间,默认为当天结束时间 int nextHour = PromotionTools.nextHour(seckill.getHours().split(","), seckillApply.getTimeLine()); String format = DateUtil.format(seckill.getStartTime(), DatePattern.NORM_DATE_PATTERN); DateTime parseStartTime = DateUtil.parse((format + " " + seckillApply.getTimeLine()), "yyyy-MM-dd HH"); DateTime parseEndTime = DateUtil.parse((format + " " + nextHour), "yyyy-MM-dd HH"); //如果是当天最后的时间段则设置到当天结束时间的59分59秒 if (nextHour == seckillApply.getTimeLine()) { parseEndTime = DateUtil.parse((format + " " + nextHour + ":59:59"), DatePattern.NORM_DATETIME_PATTERN); } seckill.setStartTime(parseStartTime); //当时商品的秒杀活动活动结束时间为下个时间段的开始 seckill.setEndTime(parseEndTime); } /** * 检查该秒杀活动是否存在 * * @param id 秒杀活动编号 * @return 秒杀活动信息 */ private Seckill checkSeckillExist(String id) { Seckill seckill = this.getById(id); if (seckill == null) { throw new ServiceException(ResultCode.SECKILL_NOT_EXIST_ERROR); } return seckill; } /** * 初始化促销字段 * * @param promotions 促销实体 */ @Override public void initPromotion(Seckill promotions) { super.initPromotion(promotions); if (promotions.getStartTime() != null && promotions.getEndTime() == null) { promotions.setEndTime(DateUtil.endOfDay(promotions.getStartTime())); } if (promotions.getApplyEndTime() == null && promotions.getStartTime() != null) { promotions.setApplyEndTime(promotions.getStartTime()); } } /** * 检查促销状态 * * @param promotions 促销实体 */ @Override public void checkStatus(Seckill promotions) { super.checkStatus(promotions); if (promotions.getStartTime() != null && CharSequenceUtil.isNotEmpty(promotions.getHours())) { Integer[] split = Convert.toIntArray(promotions.getHours().split(",")); Arrays.sort(split); String startTimeStr = DateUtil.format(promotions.getStartTime(), DatePattern.NORM_DATE_PATTERN) + " " + split[0] + ":00"; promotions.setStartTime(DateUtil.parse(startTimeStr, DatePattern.NORM_DATETIME_MINUTE_PATTERN)); promotions.setEndTime(DateUtil.endOfDay(promotions.getStartTime())); } } /** * 当前促销类型 * * @return 当前促销类型 */ @Override public PromotionTypeEnum getPromotionType() { return PromotionTypeEnum.SECKILL; } }