2021SC@SDUSC
优惠券功能(二)
本篇文章着重分析实现优惠券功能的JPress项目的代码。
文章目录
一、CouponCodeServiceProvider
提供优惠券状态码管理相关接口的实现。
1.1 CouponCodeService
CouponCode findById(Object id);
根据主键查找 Model。
List<CouponCode> findAll();
查询所有的优惠券码的数据。
boolean deleteById(Object id);
根据主键删除Model
boolean delete(CouponCode model);
删除优惠券码Model
Object save(CouponCode model);
新增优惠券码Model
Object saveOrUpdate(CouponCode model);
新增或者更新 Model 数据(主键值为 null 就新增,不为 null 则更新)
boolean update(CouponCode model);
更新此 Model 的数据,务必要保证此 Model 的主键不能为 null
Page<CouponCode> paginate(int page, int pageSize);
通过页号和页大小进行分页
Page<CouponCode> paginateByCouponId(int page, int pageSize, Long couponId);
通过优惠码id进行分页
CouponCode findByCode(String code);
输入优惠码查询
Ret valid(CouponCode couponCode, BigDecimal orderTotalAmount, long usedUserId);
返回优惠码是否有效
long queryCountByCouponId(long couponId);
计算所有优惠券
List<CouponCode> findAvailableByUserId(long userid);
查询某位用户有效的优惠码
1.2 CouponCodeServiceProvider函数用途
本类只用jboot.db.model.Column实现了各种对数据的的操作和读取。
public Page<CouponCode> paginateByCouponId(int page, int pageSize, Long couponId)
通过优惠码id进行分页
CouponCode findByCode(String code);
输入优惠码查询
public Ret valid(CouponCode couponCode, BigDecimal orderTotalAmount, long usedUserId)
优惠券使用时查询优惠券的可实用性。
- 是本类的最重要的方法之一。
- 适用于判断"优惠码只能会员使用"、”订单价格超过一定额度可以使用“、”优惠券配置错误“等各种功能。
- 返回值为Ret对象,在前文已经详细介绍过,具体作用为在位返回值,返回一定正确、报错信息。
long queryCountByCouponId(long couponId);
计算所有优惠券
private boolean isCouponCodeUnExpire(Coupon coupon, CouponCode couponCode)
检查优惠券的时效有效性
- 是本类的最重要的方法之一。
- 判断优惠券是绝对时间有效、相对时间有效等情况。
- 具体通过获取数据库设置优惠券时的时间以及当前系统时间进行计算,返回布尔值。
public List<CouponCode> findAvailableByUserId(long userid, BigDecimal orderTotalAmount)
查询某位用户有效的优惠码,支付时可选择;获取用户可用的券码
public List<CouponCode> findAvailableByUserId(long userid)
获取有效的优惠券码列表,个人中心中使用
1.3 Jboot注解@Bean
JBoot 提供了可以通过注解 @Bean
给 UserServiceImpl.class
添加在类上,这样 Jboot 在启动的时候,会自动扫描到 UserServiceImpl.class
,并通过 JbootAopFactory 把 UserService 和 UserServiceImpl 添加上关联关系。
代码如下:
Controller:
@RequestMapping("/helloworld")
public class MyController extends Controller{
@Inject
private UserService userService;
public void index(){
renderJson(userService.findAll());
}
UserService:
public interface UserService{
List<User> findAll();
}
UserServiceImpl:
@Bean
public class UserServiceImpl implements UserService{
public List<User> findAll(){
//do sth
}
}
当一个接口有多个实现类,或者当在系统中存在多个实例对象,比如有两份 Cache 对象,一份可能是 Redis Server1,一份可能是 Redis Server2,或者有两份数据源 DataSource 等,在这种情况下,我们注入的时候就需要确定注入那个实例。
这时候,我们就需要用到 @Bean(name= "myName")
去给不同的子类去添加注释。
例如:
@RequestMapping("/helloworld")
public class MyController extends Controller{
@Inject
@Bean(name="userServieImpl1") //注入名称为 userServieImpl1 的实例
private UserService userService1;
@Inject
@Bean(name="userServieImpl2") //注入名称为 userServieImpl2 的实例
private UserService userService2;
public void index(){
renderJson(userService.findAll());
}
}
UserService:
public interface UserService{
List<User> findAll();
}
UserServiceImpl1:
@Bean(name="userServiceImpl1")
public class UserServiceImpl1 implements UserService{
public List<User> findAll(){
//do sth
}
}
UserServiceImpl2:
@Bean(name="userServiceImpl2")
public class UserServiceImpl2 implements UserService{
public List<User> findAll(){
//do sth
}
}
1.4 Jboot注解@BeanExclude
当我们使用 @Bean
给某个类添加上注解之后,这个类会做好其实现的所有接口,但是,很多时候我们往往不需要这样做,比如
@Bean
public class UserServiceImpl implements UserService,
OtherInterface1,OtherInterface2...{
public List<User> findAll(){
//do sth
}
}
在某些场景下,我们可能只希望 UserServiceImpl 和 UserService 做好映射关系,此时,@BeanExclude
就派上用场了。
如下代码排除了 UserServiceImpl 和 OtherInterface1,OtherInterface2 的映射关系。
@Bean
@BeanExclude({OtherInterface1.cass,OtherInterface2.class})
public class UserServiceImpl implements UserService,
OtherInterface1,OtherInterface2...{
public List<User> findAll(){
//do sth
}
}
二、CouponServiceProvider
2.1 CouponService
Coupon findById(Object id);
根据主键查找Model
List<Coupon> findAll();
查询所有优惠券数据
boolean batchDeleteByIds(Object... ids);
批处理删除id为输入数据的优惠券
Object saveOrUpdate(Coupon model);
新增或者更新 Model 数据(主键值为 null 就新增,不为 null 则更新)
boolean update(Coupon model);
更新此 Model 的数据,务必要保证此 Model 的主键不能为 null
void doSyncTakeCount(long couponId);
计算分发数量
void doSyncUsedCount(long couponId);
计算使用过的数量
2.2 CouponServiceProvider函数用途
@Override
public void doSyncTakeCount(long couponId) {
long count = couponCodeService.queryCountByCouponId(couponId);
Coupon coupon = findById(couponId);
coupon.setTakeCount(count);
update(coupon);
}
- 使用优惠券码的service对象,通过优惠券id查询所有分发出的优惠券数量。
- 使用
JbootServiceBase
的findById方法根据优惠券id得到优惠券对象。 - 设置优惠券分发数量。
- 使用
JbootServiceBase
更新数据库对应的优惠券对象。
@Override
public void doSyncUsedCount(long couponId) {
long count = usedRecordService.queryCountByCouponId(couponId);
Coupon coupon = findById(couponId);
coupon.setUsedCount(count);
update(coupon);
}
- 使用优惠券码的service对象,通过优惠券id查询所有使用过的的优惠券数量。
- 使用
JbootServiceBase
的findById方法根据优惠券id得到优惠券对象。 - 设置优惠券使用数量。
- 使用
JbootServiceBase
更新数据库对应的优惠券对象。
三、CouponUsedRecordServiceProvider
2.1 CouponUsedRecordService
CouponUsedRecord findById(Object id);
通过id查询被使用过的优惠券的记录
List<CouponUsedRecord> findAll();
查询所有优惠券使用记录
Object saveOrUpdate(CouponUsedRecord model);
新增或者更新 Model 数据(主键值为 null 就新增,不为 null 则更新)
Page<CouponUsedRecord> paginate(int page, int pageSize, Columns columns);
对优惠卷记录进行分页
2.2 CouponUsedRecordServiceProvider函数用途
@Override
public Page<CouponUsedRecord> paginate(int page, int pageSize, Columns columns) {
return doJoin(paginateByColumns(page, pageSize, columns, "id desc"));
}
private Page<CouponUsedRecord> doJoin(Page<CouponUsedRecord> page) {
couponCodeService.join(page, "code_id");
orderService.join(page, "used_order_id");
return page;
}
使用JbootServiceJoiner
联表查询
@Override
public long queryCountByCouponId(long couponId) {
return findCountByColumns(Columns.create("coupon_id", couponId));
}
使用jboot.db.model.Columns
对couponId进行计算数量并返回
四、补充:面向切面编程
在对注解的分析中,其实我们很多次碰到了面向切面编程这个概念。
- 面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。
- 这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
五、总结
本篇文章详细介绍和分析了关于优惠券的函数的运用,结合上一篇文章的Model的分析与这章介绍的面向切面编程的方法,对JFinal、Jboot、JPress有更深层次的了解。在Jboot中有更多的面向切面编程的注解,链接如下: