仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在:
【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客
全篇会结合业务介绍重点设计逻辑,其中重点包括接口类、业务类,具体的结合源代码分析,读起来也不复杂~
谨慎:源代码中有一些注释是错误的,有的注释意思完全相反,有的注释对不上号,我在阅读过程中就顺手更新了,并且在我不会的地方添加了新的注释,所以在读源代码过程中一定要谨慎啊!
目录
A1.优惠券
B1.M端(属于显式操作)
B2.S端(属于显式操作)
B3.B端(属于显式操作)
B4.consumer端(属于业务操作)
A2.券活动
B1.M端(属于显式操作)
A3.满减
B1.S端(属于显式操作)
B2.M端(属于显式操作)
A4.秒杀
B1.M端(属于显式操作)
B2.S端(属于显式操作)
B3.B端(属于显式操作)
B4.consumer端(属于业务操作)
A5.A6.拼团和砍价(待记录)
A7.积分商品
B1.积分分类
C1.M端(属于显式操作)
C2.B端(属于显式操作)
B2.积分商品
C1.M端(属于显式操作)
C1.B端(属于显式操作)
开始前先说一下,促销的各类管理上不是特别的难理解,只要理解各个表结构和页面需要的接口就行,重点在于促销活动的业务service类底层都是实现自一个接口,因为所有的促销都是有相似的通用逻辑(例如,增加促销、修改促销、初始化等等)。
A1.优惠券
B1.M端(属于显式操作)
这儿的接口没啥好说的
- 获取优惠券列表、通过id获取优惠券详情、添加优惠券、修改优惠券、修改优惠券状态、批量删除
- 查询全部分类列表(见商品分类,M端)
- 分页获取商品sku列表(见商品,M端)
B2.S端(属于显式操作)
这里的和M端的逻辑类似,只是有些属性需要修改
- 获取优惠券列表、通过id获取优惠券详情、添加优惠券、修改优惠券、修改优惠券状态、批量删除
- 获取店铺经营的分类(见商品分类,S端)
- 分页获取商品Sku列表(见商品,S端)
B3.B端(属于显式操作)
这里的就是B端前台显示的,和用户已领取的;
但是 获取可领取优惠券列表 接口有点不方便,只返回了平台所有优惠券,并没有标注当前登录账号是否有该优惠券~
- 获取可领取优惠券列表、获取当前会员的优惠券列表、会员领取优惠券
B4.consumer端(属于业务操作)
用户领取的优惠券有截止日期的,所以需要添加定时任务来操作会员优惠券的状态变为过期。
详见:cn.lili.timetask.handler.impl.coupon.CouponExecute
但是不明白,这里为什么用EveryDayExecute,按理说应该用每秒检测的,在线上看这也是按秒的,可能这里写错了~
/**
* 优惠券状态监测
*
* @author Bulbasaur
* @since 2021/5/24 10:08 上午
*/
@Component
public class CouponExecute implements EveryDayExecute {
/**
* 过期常量,过期后或者使用后一定时间内,删除无效的优惠券,物理删除
*/
static final int EXPIRATION_DAY = 3;
@Autowired
private MemberCouponService memberCouponService;
/**
* 检测优惠券的使用时间,超期未使用则失效
* 此方法用于领取*天后失效优惠券使用
*/
@Override
public void execute() {
//业务 1
//将过期优惠券从领取状态变更为过期状态
LambdaUpdateWrapper<MemberCoupon> updateWrapper = new LambdaUpdateWrapper<MemberCoupon>()
.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name())
.le(MemberCoupon::getEndTime, new Date())
.set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.EXPIRE.name());
this.memberCouponService.update(updateWrapper);
//业务 2
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) - EXPIRATION_DAY);
Date removeTime = calendar.getTime();
//删除过期/已使用的优惠券(过期一定时间后物理删除)
LambdaUpdateWrapper<MemberCoupon> deleteWrapper = new LambdaUpdateWrapper<MemberCoupon>()
//如果结束时间小于 当前时间增加指定删除日期,则删除
.le(MemberCoupon::getEndTime, removeTime);
this.memberCouponService.remove(deleteWrapper);
}
}
A2.券活动
根据优惠券添加券活动,关联的会员直接存到主表里面的scope里面,关联的券存到子表li_coupon_activity_item 里面。
这个业务就是给指定用户发放优惠券的,他的活动开始时间和结束时间其实没有用,在创建活动的时候就直接给指定用户发放了,并且即便活动未开始、活动已结束也可以使用,所以真正的业务效果是啥呢?TvT
B1.M端(属于显式操作)
- 获取优惠券活动分页、通过id获取优惠券活动、添加优惠券活动、关闭优惠券活动
- 获取优惠券列表(见优惠券,M端)
- 会员分页列表(见会员,S端)
A3.满减
满减是由店铺发起的,运营M端可以关闭,其中满减里面回赠送包邮、优惠券、赠品~
包邮会在下单时满足条件的时候去掉邮费,赠送优惠券会在下单付款后赠送会员指定券(该券是店铺创建的活动券),赠品会在下单后生成赠品类型的子订单,价格为零。
同一个商品sku只能存在一个满减活动,所以不能存在多个全品类的满减活动~
满减活动失效是没有定时任务的,对于S端M端来说只是每次获取时进行判断,然后记录状态,表中并不存在状态这个字段,如果想要关闭满减活动,会直接将开始-截止日期清空的,这样会根据时间判断为已关闭。在B端只会在获取sku的详情时会获取该sku绑定的、有效的促销满减活动。
(私以为这样的设计不太合理,这样关闭是不会记录下来这个被关闭的满减活动的开始日期的。所以可以添加一个状态字段来记录,也方便。)
之后的促销活动:秒杀,也是这个逻辑的!
B1.S端(属于显式操作)
- 根据条件分页查询满优惠活动、通过id获取、新增满优惠活动、修改满优惠活动、删除满优惠活动、修改满额活动状态
- 获取店铺经营的分类(见商品分类,S端)
- 分页获取商品Sku列表(见商品,S端)
B2.M端(属于显式操作)
- 获取满优惠列表、获取满优惠详情、修改满额活动状态
A4.秒杀
每一天的秒杀是由系统自动添加的,然后想参加的店铺可以往某个秒杀的某个时间里面添加商品sku,运营端可以删除商品sku或者关闭秒杀。
系统一开始就需要先往系统里面添加后几天秒杀,然后之后的每一天都添加秒杀。例如:12-01日系统一开始添加七天的秒杀到 12-08,第二天 12-02 会执行定时任务,继续添加秒杀直到 12-09,由于 12-03 到 12-08 已经添加了定时任务,就会跳过,只添加 12-09 的秒杀。(这只是简单逻辑哈,实际上是判断后7天哪天没有开启的秒杀就添加秒杀,因为已生成的秒杀可以关闭~)
买方B端可以查看秒杀列表,进行秒杀商品购买,秒杀商品即使不再秒杀列表里面也会限时秒杀信息的。秒杀列表只是一个快捷入口。
B1.M端(属于显式操作)
- 初始化秒杀活动、分页查询秒杀活动列表、通过id获取、修改秒杀活动、删除一个秒杀活动、操作秒杀活动状态
- 获取秒杀活动申请列表、删除秒杀活动申请
- 获取店铺经营的分类(见商品分类,S端)
- 分页获取商品Sku列表(见商品,S端)
B2.S端(属于显式操作)
- 获取秒杀活动列表、通过id获取秒杀活动信息、获取秒杀活动申请列表、通过id获取秒杀活动申请、添加秒杀活动申请、删除秒杀活动商品
- 获取店铺经营的分类(见商品分类,S端)
- 分页获取商品Sku列表(见商品,S端)
B3.B端(属于显式操作)
- 获取当天秒杀活动信息、获取某个时刻的秒杀活动商品信息
B4.consumer端(属于业务操作)
每日定时任务,创建秒杀活动
详见:cn.lili.timetask.handler.impl.promotion.PromotionEverydayExecute
/**
* 促销活动每日定时器
*
* @author Chopper
* @since 2021/3/18 3:23 下午
*/
@Slf4j
@Component
public class PromotionEverydayExecute implements EveryDayExecute {
/**
* ES商品索引
*/
@Autowired
private EsGoodsIndexService esGoodsIndexService;
/**
* 系统设置
*/
@Autowired
private SettingService settingService;
/**
* 秒杀活动
*/
@Autowired
private SeckillService seckillService;
/**
* 将已过期的促销活动置为结束,添加秒杀活动
*/
@Override
public void execute() {
try {
//清除所有商品索引的无效促销活动
this.esGoodsIndexService.cleanInvalidPromotion();
} catch (Exception e) {
log.error("清楚商品索引中无效促销异常", e);
}
try {
//定时创建活动
addSeckill();
} catch (Exception e) {
log.error("秒杀活动添加异常", e);
}
}
/**
* 添加秒杀活动
* 从系统设置中获取秒杀活动的配置
* 添加明天后的秒杀活动
*/
private void addSeckill() {
Setting setting = settingService.get(SettingEnum.SECKILL_SETTING.name());
SeckillSetting seckillSetting = new Gson().fromJson(setting.getSettingValue(), SeckillSetting.class);
log.info("生成秒杀活动设置:{}", seckillSetting);
for (int i = 1; i <= SeckillService.PRE_CREATION; i++) {
Seckill seckill = new Seckill(i, seckillSetting.getHours(), seckillSetting.getSeckillRule());
//如果已经存在促销,则不再次保存
if (seckillService.list(PromotionTools.checkActiveTime(seckill.getStartTime(), seckill.getEndTime(), PromotionTypeEnum.SECKILL, null, seckill.getId())).isEmpty()) {
boolean result = seckillService.savePromotions(seckill);
log.info("生成秒杀活动参数:{},结果:{}", seckill, result);
}
}
}
}
A5.A6.拼团和砍价(待记录)
拼团和砍价也是只有买方小程序/APP端才有的业务,买方PC端是没有的。
拼团和砍价的详细业务来不及了解,后面记录。
A7.积分商品
积分商品也是只有买方小程序/APP端才有的业务,买方PC端是没有的。
积分商品的每个活动都包含一个商品sku,并且只由运营M端可以管理积分,店铺S端是不会管理的。添加为积分商品的商品sku下单后的金额为 0 ,也就是这单收益为 0 ,但是在店对账中会通过设置的积分结算金额再补给店铺,所以最终可以说是运营端出费的~~~(补充一下平台优惠券也是会在结算时补给店铺,具体补多少看优惠券里面店铺占比的~)
积分分类是为了买方B端进行的分类,和商品没关联的。
B1.积分分类
C1.M端(属于显式操作)
-
获取积分商品分类分页、通过id获取积分商品分类、添加积分商品分类、修改积分商品分类、删除积分商品分类
C2.B端(属于显式操作)
这个接口直接放到了买方端的积分商品里面,见积分商品,买方B端。
B2.积分商品
C1.M端(属于显式操作)
- 分页获取积分商品、通过id获取积分商品详情、添加积分商品、修改积分商品、删除积分商品、修改积分商品状态
C1.B端(属于显式操作)
-
分页获取积分商品、获取积分活动商品
-
获取积分商品分类分页