阅读本文大概需要 6 分钟。

来自:网络

前言

最近在做社交业务,用户进入APP后有签到功能,签到成功后获取相应的奖励:项目状况:前期尝试业务阶段;

特点:

  • 快速实现(不需要做太重,满足初期推广运营即可)
  • 快速投入市场去运营

用户签到:

  • 用户在每次启动时查询签到记录(规则:连续7日签到从0开始,签到过程中有断签从0开始)
  • 如果今日未签到则提示用户可以进行签到
  • 用户签到获取相应的奖励

提到签到,脑海中首先浮现特点:

  • 需要记录每位用户每天的签到情况
  • 查询时根据规则进行签到记录情况

需求&流程设计&技术实现方案

1.需求原型图

2.查询签到记录

3.进行签到

4.技术实现方案

  • SpringBoot
  • MySQL

数据库表结构

1.签到记录最新表

   CREATE TABLE `zh_sign_in` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`bu_no` varchar(32) DEFAULT NULL COMMENT '业务编码',`customer_id` varchar(32) DEFAULT NULL COMMENT '签到用户编码',`sign_in_date` datetime DEFAULT NULL COMMENT '签到日期(单位精确到日)',`reward_money` int(11) DEFAULT NULL COMMENT '本次签到奖励金币个数',`continuite_day` int(2) DEFAULT '1' COMMENT '连续签到天数(A:7天内如果有断签从0开始 B:7天签满从0开始)',`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',`param1` int(2) DEFAULT NULL COMMENT '预留字段1',`param2` int(4) DEFAULT NULL COMMENT '预留字段2',`param3` int(11) DEFAULT NULL COMMENT '预留字段3',`param4` varchar(20) DEFAULT NULL COMMENT '预留字段4',`param5` varchar(32) DEFAULT NULL COMMENT '预留字段5',`param6` varchar(64) DEFAULT NULL COMMENT '预留字段6',PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `uk_zh_sign_in_buno` (`bu_no`),UNIQUE KEY `uk_zh_sign_in_cid_signindate` (`customer_id`,`sign_in_date`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户签到表';

2.签到记录历史表

   CREATE TABLE `zh_sign_in_hist` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`bu_no` varchar(32) DEFAULT NULL COMMENT '业务编码',`customer_id` varchar(32) DEFAULT NULL COMMENT '签到用户编码',`sign_in_date` datetime NULL DEFAULT NULL COMMENT '签到日期(单位精确到日)',`reward_money` int(11) DEFAULT NULL COMMENT '本次签到奖励金币个数',`continuite_day` int(2) DEFAULT '1' COMMENT '连续签到天数(A:7天内如果有断签从0开始 B:7天签满从0开始)',`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',`param1` int(2) DEFAULT NULL COMMENT '预留字段1',`param2` int(4) DEFAULT NULL COMMENT '预留字段2',`param3` int(11) DEFAULT NULL COMMENT '预留字段3',`param4` varchar(20) DEFAULT NULL COMMENT '预留字段4',`param5` varchar(32) DEFAULT NULL COMMENT '预留字段5',`param6` varchar(64) DEFAULT NULL COMMENT '预留字段6',PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `uk_zh_sign_in_hist_cid_signindate` (`customer_id`,`sign_in_date`) USING BTREE,KEY `key_zh_sign_in_hist_buno` (`bu_no`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户签到历史表';

代码实现

1.完整代码(GitHub,欢迎大家Star,Fork,Watch)

https://github.com/dangnianchuntian/springboot

2.主要代码展示

         Controller/* * Copyright (c) 2020. zhanghan_java@163.com All Rights Reserved.* 项目名称:Spring Boot实战:签到奖励实现方案* 类名称:SignInController.java* 创建人:张晗* 联系方式:zhanghan_java@163.com* 开源地址: https://github.com/dangnianchuntian/springboot* 博客地址: https://zhanghan.blog.csdn.net*/package com.zhanghan.zhsignin.controller;import com.zhanghan.zhsignin.controller.request.PostSignInRequest;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import com.zhanghan.zhsignin.controller.request.ListSignInDetailRequest;import com.zhanghan.zhsignin.service.SignInService;@RestControllerpublic class SignInController {@Autowiredprivate SignInService signInService;/*** 查询签到记录*/@RequestMapping(value = "/list/sign/in/detail", method = RequestMethod.POST)public Object listSignInDetail(@RequestBody @Validated ListSignInDetailRequest listSignInDetailRequest) {return signInService.listSignInDetail(listSignInDetailRequest);}/*** 用户进行签到*/@RequestMapping(value = "/post/sign/in", method = RequestMethod.POST)public Object postSignIn(@RequestBody @Validated PostSignInRequest postSignInRequest) {return signInService.postSignIn(postSignInRequest);}}

service

   /** Copyright (c) 2020. zhanghan_java@163.com All Rights Reserved.* 项目名称:Spring Boot实战:签到奖励实现方案* 类名称:SignInServiceImpl.java* 创建人:张晗* 联系方式:zhanghan_java@163.com* 开源地址: https://github.com/dangnianchuntian/springboot* 博客地址: https://zhanghan.blog.csdn.net*/package com.zhanghan.zhsignin.service.impl;import cn.hutool.core.util.IdUtil;import com.zhanghan.zhsignin.config.SignInRewardMoneyListConfig;import com.zhanghan.zhsignin.constant.SignInConstant;import com.zhanghan.zhsignin.controller.request.ListSignInDetailRequest;import com.zhanghan.zhsignin.controller.request.PostSignInRequest;import com.zhanghan.zhsignin.controller.response.ListSignInDetailResponse;import com.zhanghan.zhsignin.mybatis.entity.XZhSignInEntity;import com.zhanghan.zhsignin.mybatis.entity.XZhSignInHistEntity;import com.zhanghan.zhsignin.mybatis.mapper.XZhSignInHistMapper;import com.zhanghan.zhsignin.mybatis.mapper.XZhSignInMapper;import com.zhanghan.zhsignin.service.SignInService;import com.zhanghan.zhsignin.util.DateUtils;import com.zhanghan.zhsignin.util.wrapper.WrapMapper;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import java.util.Date;import java.util.List;import java.util.stream.Collectors;import static com.zhanghan.zhsignin.constant.SignInConstant.*;@Servicepublic class SignInServiceImpl implements SignInService {@Autowiredprivate XZhSignInMapper xZhSignInMapper;@Autowiredprivate XZhSignInHistMapper xZhSignInHistMapper;//校验连续天数是否为7@Value("#{T(java.lang.Integer).parseInt('${zh.sign.in.continuite.day.threshold:7}')}")public Integer continuiteDayThreshold;//签到奖励金币集合配置@Autowiredpublic SignInRewardMoneyListConfig signInRewardMoneyListConfig;/*** 查询用户签到记录*/@Overridepublic Object listSignInDetail(ListSignInDetailRequest listSignInDetailRequest) {//若配置文件中未配置签到奖励则不展示签到记录List<Integer> signInRewardMoneyListConfigList = signInRewardMoneyListConfig.getList();if (CollectionUtils.isEmpty(signInRewardMoneyListConfigList)) {return WrapMapper.ok(new ListSignInDetailResponse(false));}String customerId = listSignInDetailRequest.getCustomerId();XZhSignInEntity xZhSignInEntity = xZhSignInMapper.findByCustomerId(customerId);List<ListSignInDetailResponse.SignInDetail> signInDetailList = signInRewardMoneyListConfigList.stream().map(aa -> new ListSignInDetailResponse.SignInDetail(0, aa)).collect(Collectors.toList());//该用户之前未签到过if (null == xZhSignInEntity) {return WrapMapper.ok(new ListSignInDetailResponse(TODAY_NOT_SIGN_IN, SignInConstant.CONTINUITE_DAY_ZERO, signInDetailList));}long signInDateTime = xZhSignInEntity.getSignInDate().getTime();//最近一次签到是否为昨日之前if (signInDateTime < DateUtils.getYesterdayDateTime()) {return WrapMapper.ok(new ListSignInDetailResponse(TODAY_NOT_SIGN_IN, SignInConstant.CONTINUITE_DAY_ZERO, signInDetailList));}//最近一次签到是否为昨日Integer todaySignStatus = TODAY_YES_SIGN_IN;Integer continuiteDay = xZhSignInEntity.getContinuiteDay();if (signInDateTime < DateUtils.getTodayDateTime()) {//最近一次签到是昨日且之前已连续签到7日if (continuiteDay >= continuiteDayThreshold) {return WrapMapper.ok(new ListSignInDetailResponse(TODAY_NOT_SIGN_IN, SignInConstant.CONTINUITE_DAY_ZERO, signInDetailList));}//最近一次签到是昨日且之前连续未超7日todaySignStatus = TODAY_NOT_SIGN_IN;}//查询用户签到历史记录List<XZhSignInHistEntity> xZhSignInHistEntitieList = xZhSignInHistMapper.listByCustomerIdAndLimit(customerId, continuiteDay);for (XZhSignInHistEntity xZhSignInHistEntity : xZhSignInHistEntitieList) {ListSignInDetailResponse.SignInDetail signInDetail = new ListSignInDetailResponse.SignInDetail(TODAY_YES_SIGN_IN, xZhSignInHistEntity.getRewardMoney());signInDetailList.remove(xZhSignInHistEntity.getContinuiteDay() - 1);signInDetailList.add(xZhSignInHistEntity.getContinuiteDay() - 1, signInDetail);}return WrapMapper.ok(new ListSignInDetailResponse(todaySignStatus, continuiteDay, signInDetailList));}/*** 进行签到*/@Overridepublic Object postSignIn(PostSignInRequest postSignInRequest) {//若配置文件中未配置签到奖励则不展示签到记录List<Integer> signInRewardMoneyListConfigList = signInRewardMoneyListConfig.getList();if (CollectionUtils.isEmpty(signInRewardMoneyListConfigList)) {return WrapMapper.ok();}//获取session用户对象String customerId = postSignInRequest.getCustomerId();//根据customerId查询用户签到记录XZhSignInEntity xZhSignInEntityByCustomerId = xZhSignInMapper.findByCustomerId(customerId);//签到记录是否为空if (null == xZhSignInEntityByCustomerId) {XZhSignInEntity xZhSignInEntity = new XZhSignInEntity();xZhSignInEntity.setBuNo(IdUtil.simpleUUID());xZhSignInEntity.setCustomerId(customerId);xZhSignInEntity.setContinuiteDay(CONTINUITE_DAY_ONE);xZhSignInEntity.setRewardMoney(signInRewardMoneyListConfigList.get(0));xZhSignInEntity.setSignInDate(DateUtils.getTodayDate());insertSigninAndHist(xZhSignInEntity);return WrapMapper.ok();}long signInDateTime = xZhSignInEntityByCustomerId.getSignInDate().getTime();if (signInDateTime == DateUtils.getTodayDateTime()) {return WrapMapper.error("今天已经签到");}//获取连续签到天数Integer continuiteDay = continuiteDay(xZhSignInEntityByCustomerId.getContinuiteDay(), signInDateTime);xZhSignInEntityByCustomerId.setSignInDate(DateUtils.getTodayDate());xZhSignInEntityByCustomerId.setContinuiteDay(continuiteDay);xZhSignInEntityByCustomerId.setRewardMoney(signInRewardMoneyListConfigList.get(continuiteDay - 1));xZhSignInEntityByCustomerId.setUpdateTime(new Date());xZhSignInEntityByCustomerId.setBuNo(IdUtil.simpleUUID());updateSignInAndInsertHist(xZhSignInEntityByCustomerId);return WrapMapper.ok();}private Integer continuiteDay(Integer continuiteDay, Long signInDateTime) {if (signInDateTime < DateUtils.getYesterdayDateTime()) {return CONTINUITE_DAY_ONE;}if (continuiteDay >= continuiteDayThreshold) {return CONTINUITE_DAY_ONE;}return continuiteDay + 1;}private void insertSigninAndHist(XZhSignInEntity xZhSignInEntity) {xZhSignInMapper.insertSelective(xZhSignInEntity);XZhSignInHistEntity xZhSignInHistEntity = new XZhSignInHistEntity();BeanUtils.copyProperties(xZhSignInEntity, xZhSignInHistEntity);xZhSignInHistEntity.setId(null);xZhSignInHistMapper.insertSelective(xZhSignInHistEntity);}private void updateSignInAndInsertHist(XZhSignInEntity xZhSignInEntity) {xZhSignInMapper.updateByPrimaryKeySelective(xZhSignInEntity);XZhSignInHistEntity xZhSignInHistEntity = new XZhSignInHistEntity();BeanUtils.copyProperties(xZhSignInEntity, xZhSignInHistEntity);xZhSignInHistEntity.setId(null);xZhSignInHistMapper.insertSelective(xZhSignInHistEntity);}} 

测试

  • 模拟用户进行签到

    • 进行请求
  • 查看数据库结果
  • 模拟用户查询签到记录

    • 进行请求

总结

  • 亮点:实现业务连续签到,断签以及奖励的业务
  • 注意点:基于数据库查询做的,在进行签到接口需要用redis锁防止并发操作
  • 后续会持续分享更多业务中的亮点

java签到_实战:如果让你用SpringBoot实现签到奖励的功能,你会怎么做?相关推荐

  1. python京东自动签到_利用python Selenium实现自动登陆京东签到领金币功能

    如何自动登陆京东? 我们先来看一下京东的登陆页面,如下图所示: [插入图片,登陆页面] 登陆框就是右面这一个框框了,但是目前我们遇到一个困呐,默认的登陆方式是扫码登陆,如果我们想要以用户民个.密码的形 ...

  2. 重学 Java 设计模式:实战外观模式「基于SpringBoot开发门面模式中间件,统一控制接口白名单场景」

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!

  3. java 多线程缓存_[Java教程]【JAVA并发编程实战】12、使用condition实现多线程下的有界缓存先进先出队列...

    [Java教程][JAVA并发编程实战]12.使用condition实现多线程下的有界缓存先进先出队列 0 2016-11-29 17:00:10 package cn.study.concurren ...

  4. Java并发编程实战_不愧是领军人物!这种等级的“Java并发编程宝典”谁能撰写?...

    前言 大家都知道并发编程技术就是在同一个处理器上同时的去处理多个任务,充分的利用到处理器的每个核心,最大化的发挥处理器的峰值性能,这样就可以避免我们因为性能而产生的一些问题. 大厂的核心负载肯定是非常 ...

  5. Java爬虫_资源网站爬取实战

    对 http://bestcbooks.com/  这个网站的书籍进行爬取 (爬取资源分享在结尾) 下面是通过一个URL获得其对应网页源码的方法 传入一个 url  返回其源码 (获得源码后,对源码进 ...

  6. java设计按月每天签到_活动攻略|新同学新签到,欢乐福利全都要!

    12月03日[每日一题答案]- 枫叶书签 答对问题即有机会获得金币.道具.积分,还有宠物好奇星噢~ 12月来了,和12月一起来的 是我们的新同学--花轮! 谁不喜欢浪漫体贴的小少爷呢! 喵星星感觉 自 ...

  7. java设计按月每天签到_签到功能java实现

    不嫌代码写的烂 可以参考下 https://github.com/ren2881971/WeChat 着急实现功能 没重构 ###### /** * 微信授权 * * */ public class ...

  8. 尚学堂java培训_送给 Java 自学者或者初学者的最全知识清单,2020 年 Java 就该这么学...

    最近逛知乎,发现有很多想自学 Java 或者 Java 初学者提问,不知道如何学习 Java?我接触 Java 快 8 年的时间了,一直从事 Java 开发工作,自己一直升级打怪,对于如何更好的学习 ...

  9. Redis总结_实战篇

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 实战篇Redis 开篇导读 1.短信登录 1.1.导入黑马点评项目 1.1.1 .导入SQL 1.1.2.有关当前模型 1. ...

最新文章

  1. Socketserver 笔记
  2. EF6 在原有数据库中使用 CodeFirst 总复习(四、新建实体对象)
  3. python有趣的小项目-Python几个有趣和特别的小故事
  4. jQuery获取隐藏域和radio单项框的值
  5. 假设mysql数据表t1有字段_使用ROMA Connect集成数据
  6. JS 入门经典 第三章 判断、循环和函数
  7. 缓冲区溢出还是问题吗?C++/CLI安全编码
  8. 国外公司技术博客盘点
  9. 【原创】Erlang 之 entop 使用问题
  10. 为 NokiaQt SDK增加新的Sym“.NET研究”bian SDK开发平台
  11. Mac实时远程抓Ubuntu的网络包
  12. dell服务器运维,【产品鉴赏】戴尔DELL R710服务器
  13. Word 无法创建工作文件 请检查临时环境变量 解决方法
  14. HIVE 多个相同属性字段元素合并到一列中
  15. JSP的 页面访问执行流程
  16. [全程动图]解决Offline Explorer崩溃闪退的问题和一些小技巧(如何下载js、100线程下载)
  17. 我的世界java怎么自制皮肤_我的世界手机版皮肤怎么做 自己做皮肤教程
  18. 短信网关通道对接及分流策略说明
  19. Daily Scrum Meeting 11.03
  20. 汇编指令: JO、JNO、JB、JNB、JE、JNE、JBE、JA、JS、JNS、JP、JNP、JL、JNL、JNG、JG、JCXZ、JECXZ、JMP、JMPE

热门文章

  1. 利用ionic3进行上一行和左一行不动,中间移动的功能
  2. Spring加载context的几种方法
  3. Python正则替换字符串函数re.sub用法示例(1)
  4. WCF全双工以及用户名密码验证
  5. 微软风格的CSS横向菜单
  6. JavaScript DOM操作 提高篇
  7. python11-28笔记(1.6-1.7)
  8. 怎么建立微信生态用户增长模型?
  9. 【设计模式】单例模式 Singleton Pattern
  10. [cocos2dx笔记010]用于UI的事件管理器