我在最近做了一个微服务项目,说是微服务项目,但其实核心技术还是SpringBoot那一套核心技术,对于我们初学者把前面那些基础学好是非常的有必要的。我们先来看看这个小模块,因为我负责个人中心的一些小功能的实现,就把其中一个比较有代表性的签到功能拿出来分享给大家。刚开始我做这个功能的时候也是一脸懵逼、无从下手,在博客上面逛了好久也没有想到比较好的思路,本人新手,其中如果有什么不对的地方还麻烦路过的大神请多指教。

先来看这个数据库表的设计

我们数据库表几经更改,最后都是按照最新的阿里开发规约黄山版设计。

首先是用户签到表,这个存储的用户信息id,并且通过这个外键来查询具体记录,我们这前端只传给一个userInfoId,从这个我们可以得到用户的持续登录天数,后期也比较好的去判断用户每天是否签到,有助于业务的处理。

再就是用户签到积分里程明细表,用户签到之后积分会有相应的增加,我们就通过这张表记录详细的用户签到的积分里程信息。因为前端传来的也是userInfoId,并且积分在下单过程中是能够消费的,可以用来抵扣一部分的现金,涉及订单的东西就会比较麻烦,因为得确定订单状态,之后还有可能涉及失败回滚,积分退回这一操作。还有一个功能就是绑定消费券,用一定的积分换取消费券也是一个需要实现的,所以就在这个表中加了消费券的相关外键。再就是需要确定积分的来源与去向,通过一个type就能较好地实现,从而避免既创建积分消费表又创建积分增加表的繁琐,在具体业务查询也是比较方便,可以自己直接判断或者使用一个枚举类都是不错的选择。当然这个积分里程数是这里的核心,是必不可少的。第三是这个过期积分的处理,我们因为之前经验不足,而直接使用xxl-job这个分布式定时任务中间件,我在之前的博客也有整理一些xxl-job的相关知识,大家想深入了解一下可以看下这篇文章

http://t.csdn.cn/wN1e6

当然其中有一些东西也是我们忽略的,最后师哥说如果直接使用这个那定时任务设置的更新时间就有很大的问题,如果设计的是1s更新一次那就对系统是很大的性能消耗,设计1min那时效性又会比较差;过期积分放在Rocket延时队列或者Redis延迟队列中,比如优惠券过期时间,就是到了那个时间之后,才把消息发给你,这样就能保证数据的实时性。这个表也是后来做业务新增需求之后新增的,这样也方便业务的处理。 

接口测试

接口测试用的国产软件ApiPost,挺适合团队协作开发的,大部分都是中文,可以较好替代Postman这个工具,推荐大家有时间可以去GitHub上下载使用一段时间试试。

当然还有很多功能是后续优化的时候还需要再添加到里面去的。

相关实体类

@Data
@TableName("user_mileage_req")
public class UserMileageReq implements Serializable {@TableId@JsonIgnoreprivate Long id;/*** 用户账号编号(消费者);关联user主表id字段,n vs 1*/@JsonIgnoreprivate Long userInfoId;/*** 优惠券编号(积分兑换优惠券);关联discount_coupon主表id字段,1 vs 1*/@JsonIgnoreprivate Long discountCouponId;/*** 订单账号编号(积分订单抵现);关联order主表id字段,1 vs 1*/@JsonIgnoreprivate Long orderInfoId;/*** 积分变化类型*/private Integer type;/*** 积分数量*/private Integer mileageCount;public UserMileageReq(Long id, Long userInfoId, Long discountCouponId, Long orderInfoId, Integer type, Integer mileageCount, Date createTime, Date updateTime, Integer deleted) {this.id = id;this.userInfoId = userInfoId;this.discountCouponId = discountCouponId;this.orderInfoId = orderInfoId;this.type = type;this.mileageCount = mileageCount;this.createTime = createTime;this.updateTime = updateTime;this.deleted = deleted;}/*** 创建时间*/@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")private Date createTime;/*** 更新时间*/@JsonIgnoreprivate Date updateTime;/*** 逻辑删除;单选:0-未删除,1-删除,默认值:0*/@JsonIgnoreprivate Integer deleted;public UserMileageReq(Long id, Long userInfoId, Long orderInfoId, Integer type, Integer mileageCount) {this.id = id;this.userInfoId = userInfoId;this.orderInfoId = orderInfoId;this.type = type;this.mileageCount = mileageCount;}public UserMileageReq(Long userInfoId, Integer type, Integer mileageCount) {this.userInfoId = userInfoId;this.type = type;this.mileageCount = mileageCount;}
}
@Data
@TableName("user_sign_in")
public class SignIn implements Serializable {@TableId@JsonIgnoreprivate Long id;/*** 用户ID*/@JsonIgnore@NotNullprivate Long userInfoId;/*** 连续签到天数*/private int duration;/*** 创建时间*/@JsonIgnoreprivate Date createTime;/*** 更新时间*/@JsonIgnoreprivate Date updateTime;/*** 是否删除*/@JsonIgnoreprivate Integer deleted;/*** 里程总数*/@TableField(exist = false)private Integer mileageCount;public SignIn(Long id, Long userInfoId, int duration, Date createTime, Date updateTime, Integer deleted) {this.id = id;this.userInfoId = userInfoId;this.duration = duration;this.createTime = createTime;this.updateTime = updateTime;this.deleted = deleted;}public SignIn(int duration, Integer mileageCount, Integer signed) {this.duration = duration;this.mileageCount = mileageCount;this.signed = signed;}/*** 会员等级*/@TableField(exist = false)private Integer memberLevel;/*** 是否签到*/@TableField(exist = false)private Integer signed;}
@Data
@TableName("user_mileage_expired")
public class UserMileageExpired implements Serializable {@TableIdprivate Long id ;/** 用户id */@NotNullprivate Long userInfoId ;/** 积分数量 */private Integer mileageCount ;/** 过期时间 */private Date expireTime ;/** 创建时间 */private Date createTime ;/** 更新时间 */private Date updateTime ;/** 是否删除;0-否,1-是 */private Integer deleted ;public UserMileageExpired(Long id, Long userInfoId, Integer mileageCount, Date expireTime) {this.id = id;this.userInfoId = userInfoId;this.mileageCount = mileageCount;this.expireTime = expireTime;}
}

接口和具体实现类

public interface ISignInService extends IService<SignIn> {/*** 获取用户签到信息** @param userInfoId* @return*/SignIn getMessages(Long userInfoId);/*** * 用户签到操作** @param userInfoId* @param mileage* @return*/void signIn(Long userInfoId, Integer mileage);
}public interface IUserMileageReqService extends IService<UserMileageReq> {/*** 用户积分流水** @param userInfoId* @return*/List<UserMileageReq> mileageDetail(Long userInfoId);/*** 用户积分月度流水** @param userInfoId* @return*/List mileageMonthDetail(Long userInfoId);/*** 获得积分* @param userInfoId* @param orderInfoId* @param mileage*/void mileageAdd(Long userInfoId,Long orderInfoId,Integer mileage);/*** 用户积分使用* @param userInfoId* @param mileage*/void mileageUse(Long userInfoId,Integer mileage);
}public interface IUserMileageExpiredService extends IService<UserMileageExpired> {/*** 过期里程批量更新*/void updateMileageList();
}

用户签到

@Service
public class SignInServiceImpl extends ServiceImpl<SignInDAO, SignIn> implements ISignInService {@Autowiredprivate IUserMileageReqService IUserMileageReqService;@Autowiredprivate UserInfoServiceImpl userInfoService;@Autowiredprivate UserMileageExpiredServiceImpl expiredService;@Autowiredprivate MileageUtil mileageUtil;/*** 用户签到信息获取** @param userInfoId* @return*/@Overridepublic SignIn getMessages(Long userInfoId) {SignIn signIn = getSingnIn(userInfoId);if (Objects.isNull(signIn)) {return new SignIn(0, getMileage(userInfoId), 0);}if (mileageUtil.isNowDay(signIn.getUpdateTime())) {signIn.setSigned(0);}signIn.setSigned(1);signIn.setMileageCount(getMileage(userInfoId));return signIn;}/*** 用户签到** @param userInfoId* @param mileage* @return*/@Override@Transactional(rollbackFor = Exception.class)public void signIn(Long userInfoId, Integer mileage) {SignIn signIn = getSingnIn(userInfoId);if (Objects.isNull(signIn)) {this.save(new SignIn(null, userInfoId, 1, DateUtil.date(), DateUtil.date(), 0));signIn(userInfoId, mileage, 1);return;}if (!mileageUtil.isNowDay(signIn.getUpdateTime())) {if (mileageUtil.isYesterday(signIn.getUpdateTime())) {signIn(userInfoId, mileage, signIn.getDuration() + 1);return;}signIn(userInfoId, mileage, 1);return;}Asserts.fail("签到失败");}/*** 保存签到信息** @param userInfoId* @param mileage* @param duration* @return*/private void signIn(Long userInfoId, Integer mileage, Integer duration) {LambdaUpdateWrapper<SignIn> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(SignIn::getUserInfoId, userInfoId).set(SignIn::getCreateTime, DateUtil.date()).set(SignIn::getDuration, duration);this.update(updateWrapper);expiredService.save(new UserMileageExpired(null, userInfoId, mileage, DateUtil.offset(DateUtil.date(), DateField.YEAR, 1)));IUserMileageReqService.save(new UserMileageReq(null, userInfoId, 0L, 0L, 1, mileage, DateUtil.date(), DateUtil.date(), 0));updateMileage(userInfoId, getMileage(userInfoId) + mileage);}/*** 更新用户积分** @param userInfoId* @param mileage* @return*/private void updateMileage(Long userInfoId, Integer mileage) {LambdaUpdateWrapper<UserInfo> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(UserInfo::getId, userInfoId).set(UserInfo::getMileageCount, mileage);userInfoService.update(updateWrapper);}/*** 查询用户里程数量** @param userInfoId* @return*/private Integer getMileage(Long userInfoId) {return expiredService.getBaseMapper().getMileage(userInfoId);}/*** 根据UserInfoId查询用户签到信息** @param userInfoId* @return SignIn*/private SignIn getSingnIn(Long userInfoId) {LambdaQueryWrapper<SignIn> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(SignIn::getUserInfoId, userInfoId);return this.getOne(queryWrapper);}}
@Service
public class UserMileageReqServiceImpl extends ServiceImpl<UserMileageReqDAO, UserMileageReq> implements IUserMileageReqService {@Autowiredprivate UserMileageExpiredServiceImpl expiredService;@Autowiredprivate MileageUtil mileageUtil;@Autowiredprivate UserMileageReqServiceImpl userMileageReqService;/*** 用户月度积分流水** @param userInfoId* @return*/@Override@Transactional(readOnly = true)public List<UserMileageReq> mileageDetail(Long userInfoId) {LambdaQueryWrapper<UserMileageReq> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.ge(UserMileageReq::getCreateTime, DateUtil.beginOfMonth(DateUtil.date())).le(UserMileageReq::getCreateTime, DateUtil.endOfMonth(DateUtil.date())).eq(UserMileageReq::getUserInfoId, userInfoId);return this.list(queryWrapper);}/*** 用户年度积分流水** @param userInfoId* @return*/@Override@Transactional(rollbackFor = Exception.class)public List mileageMonthDetail(Long userInfoId) {List<Date> months = mileageUtil.months();DateTime dateTime = DateUtil.offset(DateUtil.beginOfMonth(DateUtil.date()), DateField.YEAR, -1);LambdaQueryWrapper<UserMileageReq> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(UserMileageReq::getUserInfoId, userInfoId).ge(UserMileageReq::getCreateTime, dateTime).le(UserMileageReq::getCreateTime, DateUtil.beginOfMonth(DateUtil.date()));List<UserMileageReq> userMileageReqs = this.list(queryWrapper);List<MonthDetailVO> list = new ArrayList<>();for (Date month : months) {MonthDetailVO monthDetail = new MonthDetailVO(month, 0, 0, 0);//创建List集合存储每月信息for (UserMileageReq userMileageReq : userMileageReqs) {if (DateTime.of(month).month() == DateTime.of(userMileageReq.getCreateTime()).month()) {if (userMileageReq.getType() < 3) {monthDetail.setInputMileage(monthDetail.getInputMileage() + userMileageReq.getMileageCount());}if (userMileageReq.getType() == 3) {monthDetail.setExpiredMileage(monthDetail.getExpiredMileage() + userMileageReq.getMileageCount());}if (userMileageReq.getType() == 4) {monthDetail.setOutputMileage(monthDetail.getOutputMileage() + userMileageReq.getMileageCount());}}}list.add(monthDetail);}return list;}/*** 获得积分** @param userInfoId* @param orderInfoId* @param mileage*/@Override@Transactional(rollbackFor = Exception.class)public void mileageAdd(Long userInfoId, Long orderInfoId, Integer mileage) {expiredService.save(new UserMileageExpired(null, userInfoId, mileage, DateUtil.offset(DateUtil.date(), DateField.YEAR, 1)));userMileageReqService.save(new UserMileageReq(null, userInfoId, orderInfoId, 2, mileage));}/*** 用户积分使用** @param userInfoId* @param mileage*/@Override@Transactional(rollbackFor = Exception.class)public void mileageUse(Long userInfoId, Integer mileage) {LambdaQueryWrapper<UserMileageExpired> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(UserMileageExpired::getUserInfoId, userInfoId).ge(UserMileageExpired::getDeleted, 0);List<UserMileageExpired> list = expiredService.list(queryWrapper);//集合按日期排序list.sort(Comparator.comparing(UserMileageExpired::getExpireTime));List<UserMileageExpired> expiredList = new ArrayList<>();Integer mileages = 0;for (UserMileageExpired userMileage : list) {mileages += userMileage.getMileageCount();userMileage.setDeleted(1);expiredList.add(userMileage);if (mileages == mileage) {expiredService.updateBatchById(expiredList);this.save(new UserMileageReq(userInfoId,4,mileage));return;}if (mileages > mileage) {expiredService.save(new UserMileageExpired(null, userInfoId, mileages - mileage, userMileage.getExpireTime()));expiredService.updateBatchById(expiredList);this.save(new UserMileageReq(userInfoId,4,mileage));return;}}Asserts.fail("积分余额不足");}}
@Service
public class UserMileageExpiredServiceImpl extends ServiceImpl<UserMileageExpiredDAO, UserMileageExpired> implements IUserMileageExpiredService {/*** 过期里程批量更新*/@Autowiredprivate UserMileageReqServiceImpl userMileageReqService;@Override@Transactional(rollbackFor = Exception.class)public void updateMileageList() {List<UserMileageExpired> allList = this.list();List<UserMileageExpired> expiredList = new ArrayList<>();List<UserMileageReq> reqList = new ArrayList<>();for (UserMileageExpired userMileageExpired : allList) {if (userMileageExpired.getExpireTime().before(DateUtil.date())) {userMileageExpired.setDeleted(1);UserMileageReq userMileageReq = new UserMileageReq(userMileageExpired.getUserInfoId(),5,userMileageExpired.getMileageCount());reqList.add(userMileageReq);expiredList.add(userMileageExpired);}}if (ObjectUtil.isNotNull(expiredList)) {this.updateBatchById(expiredList);userMileageReqService.saveBatch(reqList);return;}Asserts.fail("积分更新失败");}
}

Controller层

@RestController
@RequestMapping("/user/sign-in")
public class SignInController {@Autowiredprivate SignInServiceImpl signInService;@RequestMapping("/messages")public CommonResult getMessages(@RequestParam("userInfoId") @NotNull Long userInfoId) {SignIn signIn = signInService.getMessages(userInfoId);return CommonResult.success(signIn);}@RequestMapping("save")public CommonResult signIn(@RequestParam("userInfoId") @NotNull Long userInfoId,@RequestParam("mileage") Integer mileage) {signInService.signIn(userInfoId, mileage);return CommonResult.success("签到成功");}
}
@RestController
@RequestMapping("/user/intergral")
public class MileageController {@Autowiredprivate UserMileageReqServiceImpl userMileageReqService;@Autowiredprivate UserMileageExpiredServiceImpl userMileageExpiredService;@RequestMapping("/detail")public CommonResult mileageDetail(@RequestParam("userInfoId") @NotNull Long userInfoId) {List<UserMileageReq> userMileageReqs = userMileageReqService.mileageDetail(userInfoId);return CommonResult.success(userMileageReqs);}@RequestMapping("/monthly-summary")public CommonResult mileageMonthDetail(@RequestParam("userInfoId") @NotNull Long userInfoId) {List userMileageReqs = userMileageReqService.mileageMonthDetail(userInfoId);return CommonResult.success(userMileageReqs);}@RequestMapping("/mileage-order")public CommonResult mileageAdd(@RequestParam("orderInfoId") @NotNull Long orderInfoId,@RequestParam("userInfoId") @NotNull Long userInfoId,@RequestParam("mileage") Integer mileage) {userMileageReqService.mileageAdd(userInfoId,orderInfoId,mileage);return CommonResult.success("积分获取成功");}@RequestMapping("/mileage-use")public CommonResult mileageUse(@RequestParam("userInfoId") @NotNull Long userInfoId,@RequestParam("mileage") Integer mileage) {userMileageReqService.mileageUse(userInfoId,mileage);return CommonResult.success("积分使用成功");}}

其中用的主要是Springboot+MybatisPlus核心技术,然后引用hutool工具包来简化代码的开发。大家觉得好用可以点赞收藏一下,整理总结发布不易。

Java中Springboot实战之签到功能详解(超全面)相关推荐

  1. java中List的用法和实例详解

    Java中List的用法和实例详解 List的用法 List包括List接口以及List接口的所有实现类.因为List接口实现了Collection接口,所以List接口拥有Collection接口提 ...

  2. java throw与throws_基于Java中throw和throws的区别(详解)

    系统自动抛出的异常 所有系统定义的编译和运行异常都可以由系统自动抛出,称为标准异常,并且 Java 强烈地要求应用程序进行完整的异常处理,给用户友好的提示,或者修正后使程序继续执行. 语句抛出的异常 ...

  3. java里的进制转换函数_基于Java中进制的转换函数详解

    十进制转成十六进制: Integer.toHexString(int i) 十进制转成八进制 Integer.toOctalString(int i) 十进制转成二进制 Integer.toBinar ...

  4. throws java_基于Java中throw和throws的区别(详解)

    系统自动抛出的异常 所有系统定义的编译和运行异常都可以由系统自动抛出,称为标准异常,并且 Java 强烈地要求应用程序进行完整的异常处理,给用户友好的提示,或者修正后使程序继续执行. 语句抛出的异常 ...

  5. java线程和内核线程的,Java中内核线程理论及实例详解

    1.概念 内核线程是直接由操作系统内核控制的,内核通过调度器来完成内核线程的调度并负责将其映射到处理器上执行.内核态下的线程执行速度理论上是最高的,但是用户不会直接操作内核线程,而是通过内核线程的接口 ...

  6. python 数列筛选_对numpy中的数组条件筛选功能详解

    在程序设计中,时常会遇到数据的唯一化.相同.相异信息的提取等工作,在格式化的向量存储矩阵中南,numpy能够提供比较不错的快速处理功能. 1,唯一化的实现: In [63]: data = np.ar ...

  7. java中switchcase用法,java中的switch case语句使用详解

    java中的switch case语句 switch-case语句格式如下: ? swtich()变量类型只能是int.short.char.byte和enum类型(JDK 1.7 之后,类型也可以是 ...

  8. java中带符号十六进制转换成十进制详解

    java中带符号十六进制转换成十进制详解 代码如下 代码如下 必须拿ffff进行测试,否则测不出异同 public void test1(){String strHex="ffff" ...

  9. java里throws详细讲解,基于Java中throw和throws的区别(详解)

    系统自动抛出的异常 所有系统定义的编译和运行异常都可以由系统自动抛出,称为标准异常,并且 Java 强烈地要求应用程序进行完整的异常处理,给用户友好的提示,或者修正后使程序继续执行. 语句抛出的异常 ...

  10. java中drawimage方法_canvas.drawImage()方法详解

    首先看html5.js /** @param {Element} img_elem @param {Number} dx_or_sx @param {Number} dy_or_sy @param { ...

最新文章

  1. 腾讯云CentOS7.2+Django2.2+Python3.7+Nginx+Uwsgi 初学者部署教程
  2. java 命名代码检查-注解处理器
  3. Leetcode:Intersection of Two Linked Lists
  4. 恶意代码实战Lab13-01分析
  5. odoo xml样式_自定义Odoo的界面样式
  6. 用Java写数据到POST请求
  7. Django中使用缓存
  8. Java toString()方法
  9. sqlyog企业版 v8.32注册码
  10. 查看本地MYSQL数据库的IP地址
  11. SQL 查询字段包含特殊符号的数据
  12. Java位运算优化:位域、位图棋盘等
  13. 反序列化漏洞利用总结
  14. python 编写获取阿里巴巴行业分类数据代码
  15. 木马情报报告:内部抓捕botnet-Dridex
  16. idea中groupid、artifactId、Version的意思
  17. python中done()是什么意思_turtle.done()的作用是什么
  18. P4278 带插入区间K小值
  19. 距离感应音乐玩具计算机系统设计,可编程机器人玩具程小奔红外测距传感器测量距离案例...
  20. Win7 安装 VisualStudio2019

热门文章

  1. Android 做一个简单记事本app
  2. 毁人不倦 - 大公司小公司
  3. andorid自动化测试之Monkey(上)
  4. PHP中级程序员常见面试题
  5. 网络工程师基础--流水线吞吐率
  6. ddqn玩flappybird
  7. windows驱动开发8:虚拟摄像头方案
  8. 虚拟摄像头之二: 配置v4l2loopback虚拟摄像头为前置或后置摄像头
  9. Bat 脚本学习 (基础篇)
  10. 【STM32】HAL库——ADC