'优惠券领取改良完成'

This commit is contained in:
Chopper711 2023-01-05 18:02:40 +08:00
parent 5218702005
commit 613f397c33
13 changed files with 390 additions and 62 deletions

View File

@ -1,51 +0,0 @@
/** 增加签到日期 **/
ALTER TABLE li_member_sign
ADD day int DEFAULT NULL COMMENT '签到日 ';
ALTER TABLE li_member_sign
DROP INDEX uk_member_day;
ALTER TABLE li_member_sign
add unique uk_member_day (member_id, day) COMMENT 'uk_member_day';
-- ----------------------------
-- Table structure for li_hot_words_history
-- ----------------------------
DROP TABLE IF EXISTS `li_hot_words_history`;
CREATE TABLE `li_hot_words_history`
(
`id` bigint NOT NULL COMMENT 'ID',
`create_time` datetime(6) DEFAULT NULL COMMENT '创建时间',
`keywords` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '热词',
`score` int DEFAULT NULL COMMENT '热词分数',
PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb3 COLLATE = utf8_bin COMMENT '热词历史表';
-- ----------------------------
-- Records of li_hot_words_history
-- ----------------------------
-- ----------------------------
-- Table structure for li_wholesale
-- ----------------------------
DROP TABLE IF EXISTS `li_wholesale`;
CREATE TABLE `li_wholesale`
(
`id` bigint NOT NULL,
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`create_time` datetime(6) DEFAULT NULL,
`delete_flag` bit(1) DEFAULT NULL,
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`update_time` datetime(6) DEFAULT NULL,
`price` decimal(10, 2) DEFAULT NULL COMMENT '价格',
`goods_id` bigint DEFAULT NULL COMMENT '商品id',
`sku_id` bigint DEFAULT NULL COMMENT '商品skuId',
`num` int DEFAULT NULL COMMENT '起购量',
PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_bin COMMENT '批发规则表';
ALTER TABLE li_wholesale
ADD template_id bigint DEFAULT NULL COMMENT '商品模版id';
/** 店铺--默认页面是否开启**/
ALTER TABLE li_store ADD page_show bit(1) DEFAULT NULL COMMENT '默认页面是否开启';

View File

@ -0,0 +1,9 @@
-- 会员优惠券标识
CREATE TABLE `li_member_coupon_sign` (
`id` bigint NOT NULL,
`coupon_activity_Id` bigint NULL DEFAULT NULL COMMENT '优惠券活动id',
`member_id` bigint NULL DEFAULT NULL COMMENT '会员id',
`invalid_time` datetime NULL DEFAULT NULL COMMENT '过期时间',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;

View File

@ -0,0 +1,45 @@
package cn.lili.timetask.handler.impl.promotion;
import cn.lili.common.enums.PromotionTypeEnum;
import cn.lili.modules.promotion.entity.dos.Seckill;
import cn.lili.modules.promotion.service.MemberCouponSignService;
import cn.lili.modules.promotion.service.SeckillService;
import cn.lili.modules.promotion.tools.PromotionTools;
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.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 促销活动每日定时器
*
* @author Chopper
* @since 2021/3/18 3:23 下午
*/
@Slf4j
@Component
public class MemberCouponSignEverydayExecute implements EveryDayExecute {
@Autowired
private MemberCouponSignService memberCouponSignService;
/**
* 将已过期的促销活动置为结束
*/
@Override
public void execute() {
try {
memberCouponSignService.clean();
} catch (Exception e) {
log.error("清除领取优惠券标记异常", e);
}
}
}

View File

@ -240,6 +240,10 @@ public enum CachePrefix {
* 积分商品缓存key前缀
*/
STORE_ID_EXCHANGE,
/**
* 会员领取标记
*/
MEMBER_COUPON_SIGN,
//================交易=================

View File

@ -185,7 +185,7 @@ public class RedisCache implements Cache {
public Long counter(Object key) {
HyperLogLogOperations<Object, Object> operations = redisTemplate.opsForHyperLogLog();
//add 方法对应 PFADD 命令
//add 方法对应 PFCOUNT 命令
return operations.size(key);
}

View File

@ -26,15 +26,16 @@ public class DateUtil {
* @return 今天开始时间
*/
public static Long getDayOfStart() {
return DateUtil.getDateline()/(60*24*60);
return DateUtil.getDateline() / (60 * 24 * 60);
}
/**
* 指定日的开始时间
*
* @return 指定日时间
*/
public static Long getDayOfStart(Date date) {
return date.getTime()/(60*24*60);
return date.getTime() / (60 * 24 * 60);
}
/**
@ -349,6 +350,22 @@ public class DateUtil {
return getDateline(mon, STANDARD_FORMAT);
}
/**
* 获取当前天的结束时间
*
* @return 当前天的开始时间
*/
public static Date getCurrentDayStartTime() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, cal.get(Calendar.SECOND) - 1);
return cal.getTime();
}
/**
* 获取当前天的结束时间
*
@ -366,6 +383,21 @@ public class DateUtil {
return cal.getTime();
}
/**
* 获取干净的时间
*
* @return 时间对象
*/
public static Calendar getCleanCalendar() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal;
}
/**
* 获取延时时间
*

View File

@ -2,6 +2,7 @@ package cn.lili.modules.promotion.entity.dos;
import cn.lili.modules.promotion.entity.enums.CouponActivitySendTypeEnum;
import cn.lili.modules.promotion.entity.enums.CouponActivityTypeEnum;
import cn.lili.modules.promotion.entity.enums.CouponFrequencyEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ -36,6 +37,12 @@ public class CouponActivity extends BasePromotions {
@NotNull(message = "请选择活动范围")
@ApiModelProperty(value = "活动范围", allowableValues = "ALL:全部会员,DESIGNATED指定会员")
private String activityScope;
/**
* @see CouponFrequencyEnum
*/
@NotNull(message = "领取周期")
@ApiModelProperty(value = "活动范围", allowableValues = " DAY:每天, WEEK:每周, MONTH:每月")
private String couponFrequencyEnum;
@ApiModelProperty(value = "活动范围详情,只有精准发券使用")
private String activityScopeInfo;

View File

@ -0,0 +1,50 @@
package cn.lili.modules.promotion.entity.dos;
import cn.lili.mybatis.BaseIdEntity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
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.EqualsAndHashCode;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* 会员优惠券实体类
*
* @author Chopper
* @since 2020-03-19 10:44 上午
*/
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("li_member_coupon_sign")
@ApiModel(value = "会员优惠券领取标记")
public class MemberCouponSign extends BaseIdEntity {
@ApiModelProperty(value = "优惠券活动ID")
private String couponActivityId;
@ApiModelProperty(value = "会员ID")
private String memberId;
@ApiModelProperty(value = "失效时间,到达失效时间后自动删除,用户可以再次领取")
private Date invalidTime;
@CreatedDate
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间", hidden = true)
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || yyyy/MM/dd HH:mm:ss|| yyyy/MM/dd ||epoch_millis")
private Date createTime;
}

View File

@ -0,0 +1,35 @@
package cn.lili.modules.promotion.entity.enums;
/**
* 优惠券活动发送类型枚举
*
* @author Bulbasaur
* @since 2021/5/20 5:47 下午
*/
public enum CouponFrequencyEnum {
/**
* 领取周期
*/
DAY("每天"), WEEK("每周"), MONTH("每月");
private final String description;
CouponFrequencyEnum(String str) {
this.description = str;
}
public String description() {
return description;
}
public static boolean exist(String name) {
try {
CouponFrequencyEnum.valueOf(name);
} catch (IllegalArgumentException e) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,17 @@
package cn.lili.modules.promotion.mapper;
import cn.lili.modules.promotion.entity.dos.MemberCouponSign;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* 会员优惠券领取标记
*
* @author liushuai(liushuai711 @ gmail.com)
* @version v4.0
* @Description:
* @since 2023/1/3 18:11
*/
public interface MemberCouponSignMapper extends BaseMapper<MemberCouponSign> {
}

View File

@ -0,0 +1,33 @@
package cn.lili.modules.promotion.service;
import cn.lili.modules.promotion.entity.dos.MemberCouponSign;
import cn.lili.modules.promotion.entity.vos.CouponActivityVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* 优惠券领取标记业务层
*
* @author liushuai(liushuai711 @ gmail.com)
* @version v4.0
* @Description:
* @since 2023/1/3 18:12
*/
public interface MemberCouponSignService extends IService<MemberCouponSign> {
/**
* 清除缓存
* 清除失效标记
*/
void clean();
/**
* 清除缓存
* 清除失效标记
*/
List<CouponActivityVO> receiveCoupon(List<CouponActivityVO> couponActivity);
}

View File

@ -8,7 +8,6 @@ import cn.lili.common.enums.PromotionTypeEnum;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.service.MemberService;
import cn.lili.modules.promotion.entity.dos.Coupon;
import cn.lili.modules.promotion.entity.dos.CouponActivity;
@ -20,10 +19,7 @@ import cn.lili.modules.promotion.entity.enums.*;
import cn.lili.modules.promotion.entity.vos.CouponActivityItemVO;
import cn.lili.modules.promotion.entity.vos.CouponActivityVO;
import cn.lili.modules.promotion.mapper.CouponActivityMapper;
import cn.lili.modules.promotion.service.CouponActivityItemService;
import cn.lili.modules.promotion.service.CouponActivityService;
import cn.lili.modules.promotion.service.CouponService;
import cn.lili.modules.promotion.service.MemberCouponService;
import cn.lili.modules.promotion.service.*;
import cn.lili.modules.promotion.tools.PromotionTools;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import groovy.util.logging.Slf4j;
@ -31,7 +27,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@ -50,6 +49,11 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
private MemberCouponService memberCouponService;
@Autowired
private CouponActivityItemService couponActivityItemService;
@Autowired
private MemberCouponSignService memberCouponSignService;
@Autowired
private MemberService memberService;
@ -90,6 +94,7 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
}
}
/**
* 初始化促销字段
*
@ -181,6 +186,14 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
//获取当前正在进行的优惠券活动
List<CouponActivityVO> couponActivities = currentCouponActivity(couponActivityTrigger.getCouponActivityTypeEnum().name());
/**
* 自动发送优惠券则需要补足日志
*/
if (couponActivityTrigger.getCouponActivityTypeEnum().equals(CouponActivityTypeEnum.AUTO_COUPON)) {
couponActivities = memberCouponSignService.receiveCoupon(couponActivities);
}
//优惠券发放列表
List<CouponActivityItemVO> couponActivityItemVOS = new ArrayList<>();
@ -191,6 +204,7 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
authUser.setId(couponActivityTrigger.getUserId());
authUser.setNickName(couponActivityTrigger.getNickName());
return this.sendCoupon(authUser, couponActivityItemVOS);
}
@ -307,16 +321,20 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
* @param couponActivityItems 优惠券列表
*/
private List<MemberCoupon> sendCoupon(AuthUser authUser, List<? extends CouponActivityItem> couponActivityItems) {
//最终优惠券列表
List<MemberCoupon> finalCoupons = new ArrayList<>();
//循环优惠券赠送列表
for (CouponActivityItem couponActivityItem : couponActivityItems) {
//获取优惠券
Coupon coupon = couponService.getById(couponActivityItem.getCouponId());
//判断优惠券是否存在
if (coupon != null) {
List<MemberCoupon> memberCouponList = new LinkedList<>();
//循环优惠券的领取数量
int activitySendNum = couponActivityItem.getNum();
List<MemberCoupon> memberCouponList = new ArrayList<>();
for (int i = 1; i <= activitySendNum; i++) {
MemberCoupon memberCoupon = new MemberCoupon(coupon);
memberCoupon.setMemberId(authUser.getId());
@ -325,6 +343,8 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
memberCoupon.setPlatformFlag(PromotionTools.PLATFORM_ID.equals(coupon.getStoreId()));
memberCouponList.add(memberCoupon);
}
finalCoupons.addAll(memberCouponList);
//批量添加优惠券
memberCouponService.saveBatch(memberCouponList);
//添加优惠券已领取数量
@ -333,7 +353,10 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
log.error("赠送优惠券失败,当前优惠券不存在:" + couponActivityItem.getCouponId());
}
}
return new ArrayList<>();
if (finalCoupons.isEmpty()) {
return new ArrayList<>();
}
return finalCoupons;
}

View File

@ -0,0 +1,124 @@
package cn.lili.modules.promotion.serviceimpl;
import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.DateUtil;
import cn.lili.modules.promotion.entity.dos.CouponActivity;
import cn.lili.modules.promotion.entity.dos.MemberCouponSign;
import cn.lili.modules.promotion.entity.enums.CouponFrequencyEnum;
import cn.lili.modules.promotion.entity.vos.CouponActivityVO;
import cn.lili.modules.promotion.mapper.MemberCouponSignMapper;
import cn.lili.modules.promotion.service.MemberCouponSignService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* 会员优惠券领取标记
*
* @author liushuai(liushuai711 @ gmail.com)
* @version v4.0
* @Description:
* @since 2023/1/3 18:13
*/
@Service
public class MemberCouponSignServiceImpl extends ServiceImpl<MemberCouponSignMapper, MemberCouponSign> implements MemberCouponSignService {
@Autowired
private Cache cache;
@Override
public void clean() {
LambdaQueryWrapper<MemberCouponSign> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.lt(MemberCouponSign::getInvalidTime, DateUtil.getCurrentDayStartTime());
this.baseMapper.delete(queryWrapper);
cache.remove(CachePrefix.MEMBER_COUPON_SIGN.getPrefix());
}
@Override
public List<CouponActivityVO> receiveCoupon(List<CouponActivityVO> couponActivity) {
List<MemberCouponSign> memberCouponSigns = new ArrayList<>();
//查询当前用户领取标记
LambdaQueryWrapper<MemberCouponSign> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(MemberCouponSign::getMemberId, UserContext.getCurrentUser().getId());
List<MemberCouponSign> oldMemberCouponSigns = this.list(queryWrapper);
//定义生效的活动
List<CouponActivityVO> assertCouponActivity = new ArrayList<>();
//生成标记对象
couponActivityFor:
for (CouponActivityVO activity : couponActivity) {
//如果旧的标记中包含记号则略过
for (MemberCouponSign oldMemberCouponSign : oldMemberCouponSigns) {
if (oldMemberCouponSign.getCouponActivityId().equals(activity.getId())) {
continue couponActivityFor;
}
}
assertCouponActivity.add(activity);
MemberCouponSign memberCouponSign = new MemberCouponSign();
memberCouponSign.setMemberId(UserContext.getCurrentUser().getId());
memberCouponSign.setCouponActivityId(activity.getId());
memberCouponSign.setInvalidTime(getInvalidTime(activity));
memberCouponSigns.add(memberCouponSign);
}
this.saveBatch(memberCouponSigns);
return assertCouponActivity;
}
/**
* 根据活动优惠券获取标记失效时间
*
* @param activity
* @return
*/
private Date getInvalidTime(CouponActivity activity) {
//领取周期符合预设
if (CouponFrequencyEnum.exist(activity.getCouponFrequencyEnum())) {
Calendar cal = DateUtil.getCleanCalendar();
switch (CouponFrequencyEnum.valueOf(activity.getCouponFrequencyEnum())) {
case DAY:
return DateUtil.getCurrentDayEndTime();
case WEEK:
//周一
cal.set(Calendar.DAY_OF_WEEK, 2);
//去往下周
cal.set(Calendar.WEEK_OF_YEAR, cal.get(Calendar.WEEK_OF_YEAR) + 1);
//减1毫秒上个星期最后一刻
cal.set(Calendar.MILLISECOND, -1);
return cal.getTime();
case MONTH:
//日期 1日
cal.set(Calendar.DAY_OF_MONTH, 1);
//下个月
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + 1);
//减少一毫秒去上个月最后一刻
cal.set(Calendar.MILLISECOND, -1);
return cal.getTime();
default:
throw new ServiceException();
}
} else {
throw new ServiceException();
}
}
}