签到
签到:
前提说明:
在刚开始做签到的时候,参考了网上诸多大神的想法和案例,但都发现与自己项目中的签到,以及老大所提出的要求格格不入,所以就在和老大沟通和指导的过程中,做出符合产品要求的签到系统。
难点1:用一个字段来存储整个月的签到情况,并且需要计算连续签到的天数?
解决办法:用数组来存储一个月的签到情况,0表示未签到,1表示签到,拿到数组后根据当天的情况向前循环查找是1的话连续签到的天数加1遇到0的时候结束循环。
坑1:用户在月初第一天签到的时候,或者当月第一次点击到签到页面的时候获取签到列表,签到列表为空报错?
原因及解决办法:签到的数组是每个月用户签到后才会生成的数组,每个月第一次签到的时候由于签到数组没有生成,所以才会报错。解决办法就是增加判断,在每个月初第一天的时候返回给前端一个空数组。
1. 需求分析
1.签到提醒默认打开,打开状态下每天11点50系统推送打卡签到系统消息
2.按日签到,签到成功,已连续签到天数+1
3.中间中断的话连续签到清空,从下次签到再次统计连续天数
4.按自然月统计连续签到天数,下个月1日重新进行统计
5.今日未签到,显示文案:签到成功可获得XX趣币(调用第三方:向趣币的计算中心发送kafka消息);
6.今日已签到,显示文案:明日签到可获得XX趣币。立即签到改为已签到按钮置灰可
2. 设计要点
1.考虑到前期用户数量,目前只在web层进行降维,对程序进行保护,没有进行缓存处理,用户直接访问数据库。
2.目前根据产品需求,只提供签到的签到列表两个接口。
3.补签的功能已经实现,目前接口存在、可用,只是产品不需要,在未来需要的时候直接将接口放开就可以使用。
3. 主要流程图
4. 调用序列示例图
5. 主要接口定义
1.提交签到
@ApiOperation(value = "提交签到", notes = "提交签到", httpMethod = "POST")
@ApiImplicitparams({
@ApiImplicitParam(name = "jsonpara", value = "{\"userID\":\"用户ID\"}", required = true) })
@Apiresponses({ @ApiResponse(code = 200, message = "\t{\n" + "\t\"status\": 200,\n"
+ "\t \"info\": {\"conSigns\":\"连续签到的天数\"\n" + "\t },\n"
+ "\t \"message\": \"操作成功\",\n" + "\t \"bzcode\": \"\"\n" + "\t}\n") })
@requestMAPPing(value = "/signin/submit", method = RequestMethod.POST)
public ReturnData submitDailySignIn(@Requestbody Map<String, Object> jsonPara) throws Exception {
重要的逻辑:
1.每个月签到都是一个新的数组,数组的长度与当月的天数保持一致。
2.连续签到,通过判断数组往前签到天数的情况,确认当前用户连续签到的天数。
}
1.1签到的主要逻辑
/** * * @title: userSignByUserID * @Description: TODO(用户通过userID进行签到) * @param: @param req * @param: @return * @return: Response<Map<String,Object>> * @throws */ @Override public Response<Map<String, Object>> userSignByUserID(Request<Map<String, Object>> req) { Response<Map<String, Object>> res = Response.create(); //获取用户参数 Map<String, Object> data = req.getData(); //获取用户userID String userID = (String) req.getData().get("userID"); //获取系统当前的月和天 calendar calendar1 = Calendar.getinstance(); String pattern = "yyyy-MM"; simpledateformat stf = new SimpleDateFormat(pattern); String month = stf.format(calendar1.getTime()); int days = calendar1.get(Calendar.DAY_OF_MONTH); //获取系统的年份和月份来计算每月有多少天,用来设置数组的长度 int year = calendar1.get(Calendar.YEAR); int months = calendar1.get(Calendar.MONTH); calendar1.set(Calendar.YEAR, year); calendar1.set(Calendar.MONTH, months); int maxDays = calendar1.getActualMaximum(Calendar.DAY_OF_MONTH); //设置签到的天数为1 int[] results = new int[maxDays]; results[days-1] = 1; //需要在此将数组转为int进行存储 String jsonString = jsonobject.toJSONString(results); //封装返回对象 Map<String, Object> map = new HashMap<>(); try { if((days-1) == 0) {//0表示数组的第一个数,表示每月的第一天 //生成主键ID Long id = sequenceUtil.create().sequencenextval("fl_user_sign"); data.put("ID", id); //封装用户实体类 UserSign userSign = new UserSign(); userSign.setID(id); userSign.setUserID(userID); userSign.setMonth(month); userSign.setResults(jsonString); //签到 logger.info("用户中心", "签到的provider:提交签到", false,"月初第一次签到:"+ JsonUtils.objectToJson(userSign)); userSignsService.saveUserSign(userSign); //定义连续签到的天数 int conSigns = 1; map.put("conSigns", conSigns); }else { //判断连续签到几天,需要取到当月签到的列表,判断前一天是否签到,如果昨天签到就在签到总天数加1,否则就置为1 logger.debug("用户中心", "签到的provider:判断连续签到入参", false, userID+"用户ID和月份"+month); UserSign us = userSignsService.queryUserSignList(userID,month); //如果us为空,说明1号没有签到,这里需要重新生成一条数据插入到数据库中 if(us == null) { //生成主键ID Long id = SequenceUtil.create().sequenceNextVal("fl_user_sign"); data.put("ID", id); //封装用户实体类 UserSign userSign = new UserSign(); userSign.setID(id); userSign.setUserID(userID); userSign.setMonth(month); userSign.setResults(jsonString); //签到 logger.info("用户中心", "签到的provider:提交签到", false, JsonUtils.objectToJson(userSign)); userSignsService.saveUserSign(userSign); //定义连续签到的天数 int conSigns = 1; map.put("conSigns", conSigns); }else { String results2 = us.getResults(); Long ID = us.getID(); //将string类型和jsonarray进行互相转换 JSONArray parseArray = JSONObject.parseArray(results2); //对于拿到的签到数组进行判断,计算连续签到的天数 int conSigns = 1; for(int i=(days-2);i>=0;i--) { Byte signs = parseArray.getByte(i); //如果是前一天没有签到就返回签到一天 if(signs == 0 && i == (days-2)) { map.put("conSigns", conSigns); break; } if(signs == 1) { //在此判断连续的天数 conSigns += 1; map.put("conSigns", conSigns); } //如果未签到不是昨天的情况,退出循环 if(signs == 0 && i != (days-2)) { break; } } //将今天的数据插入到数组中,在原来的数组里进行update操作 parseArray.set(days-1, 1); String jsonString2 = parseArray.toJSONString(); //签到 logger.info("用户中心", "签到的provider:签到入参", false, jsonString2); userSignsService.updateUserSign(jsonString2,ID); } } res.setData(map); //发送kafka消息,添加趣币经验值 sendCoinKafkaMessage(userID); logger.info("用户中心", "签到的provider:签到中发送kafka消息给趣币运行结束", false,userID); res.setSuccess(true); } catch (Exception e) { res.setSuccess(false); logger.ERROR("用户中心", "签到的provider:签到的业务失败",e); res.setErrorMsg("签到失败"); } return res;
} |
2. 签到列表
@ApiOperation(value = "签到列表", notes = "签到列表", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(name = "jsonPara", value = "{\"userID\":\"用户ID\",\"month\":\"2018-01,时间范围,精确度到月,返回月度签到列表\"}", required = true) })
@ApiResponses({ @ApiResponse(code = 200, message = "\t{\n" + "\t\"status\": 200,\n"
+ "\t \"info\": {\n" + "\t "
+ "\t \"signArray\":\"[0,1]\"\n" + "\t },\n"
+ "\t \"message\": \"操作成功\",\n" + "\t \"bzcode\": \"\"\n" + "\t}\n") })
@RequestMapping(value = "/signin/list", method = RequestMethod.POST)
public ReturnData DailySignInList(@RequestBody Map<String, Object> jsonPara) throws Exception {
1.通过用户ID和当前的月份,获取用户当月的签到情况。
}
2.1获取签到列表的主要逻辑:
/** * * @Title: queryUserSignList * @Description: TODO(获取用户签到列表) * @param: @param req * @param: @return * @return: Response<Map<String,Object>> * @throws */ @Override public Response<Map<String, Object>> queryUserSignList(Request<Map<String, Object>> req) { Response<Map<String, Object>> res = Response.create(); //获取用户参数 Map<String, Object> data = req.getData(); // 获取签到列表,将获取到的月份和天数进行字符串的拼接 String userID = (String) data.get("userID"); String month = (String) data.get("month");
//date用户返回月份和签到的情况 Map<String,Object> date = new HashMap<>(); try { logger.info("用户中心", "签到的provider:获取用户签到列表的入参", false, userID+"用户ID和月份"+month); UserSign us = userSignsService.queryUserSignList(userID,month); if(us == null) { logger.error("用户中心", "签到的provider:获取用户签到列表","获取用户签到列表失败(用户可能为新用户没有签到记录)"); date.put("signArray",new JSONArray()); }else { logger.debug("用户中心", "签到的provider:获取用户签到列表", false, JsonUtils.objectToJson(us)); String results = us.getResults(); //将string类型和JSONArray进行互相转换 JSONArray parseArray = JSONObject.parseArray(results); //目前直接将一个jsonArray返回 /* date.put("ID", us.getID());*/ date.put("signArray", parseArray); } //获取当前用户的趣币总数 Response<Map<String, Object>> response = engineUserServiceRpc.myselfSummary(req); if (response.isSuccess()){ Object coin = response.getData().get("coin"); logger.info("UserCenter","queryUserSignList",false,"获取用户的趣币总数为:"+coin); date.put("coin", coin); }else{ logger.error("UserCenter","queryUserSignList","用户获取趣币总数失败:错误信息为:"+response.getErrorMsg()+"错误码为:"+response.getErrorCode()); date.put("coin", ""); } res.setData(date); res.setSuccess(true); } catch (Exception e) { logger.error("用户中心", "签到的provider:获取用户签到列表","获取用户签到列表失败:"+e); res.setSuccess(false); res.setErrorMsg("获取用户签到列表失败"); }
return res; } |
3.补签:
/**
*
* @Title: DailySignInRetroactive @Description: TODO(补签) @param: @param { "userID":"用户ID",
*"day":"具体需要补签的那天是几号" } @param: @return { "status":"200", "info":{
*
*} "message":"操作成功" } @param: @throws Exception @return: ReturnData @throws
*/
/*@ApiOperation(value = "补签", notes = "补签", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(name = "jsonPara", value = "{\"userID\":\"用户ID\",\"day\":\"具体需要补签的那天是几号(1)\"}", required = true) })
@ApiResponses({ @ApiResponse(code = 200, message = "\t{\n" + "\t\"status\": 200,\n"
+ "\t \"info\": {\n" + "\t },\n"
+ "\t \"message\": \"操作成功\",\n" + "\t \"bzcode\": \"\"\n" + "\t}\n") })
@RequestMapping(value = "/signin/resign", method = RequestMethod.POST)
public ReturnData DailySignInRetroactive(@RequestBody Map<String, Object> jsonPara) throws Exception {
//通过用户ID和具体补签的日期进行补签
主要逻辑:根据用户ID获取当月的签到列表,然后将补签的具体那一天进行修改。
}
/** * * <p>Title: retroactiveUserSignIn</p> * <p>Description: 用户补签</p> * @param req * @return * @see com.foriseland.fsoa.social.consumer.api.IUserSignService#retroactiveUserSignIn(com.foriseland.fjf.rpc.storage.Request) */ @Override public Response<Map<String, Object>> retroactiveUserSignIn(Request<Map<String, Object>> req) { Response<Map<String, Object>> res = Response.create(); //获取用户参数 Map<String, Object> data = req.getData(); //补签时获取用户信息 String userID = (String) data.get("userID"); String day = (String) data.get("day"); //获取系统时间的年月份 Calendar calendar1 = Calendar.getInstance(); String pattern = "yyyy-MM"; SimpleDateFormat stf = new SimpleDateFormat(pattern); String month = stf.format(calendar1.getTime()); //date用户返回补签的情况 Map<String,Object> date = new HashMap<>(); try { logger.info("用户中心", "签到的provider:补签中获取签到列表", false, userID+"用户ID和月份"+month); UserSign us = userSignsService.queryUserSignList(userID,month); if(us == null) { res.setErrorMsg("获取用户签到列表失败" ); return res; } logger.debug("用户中心", "签到的provider:补签列表的出参", false, JsonUtils.objectToJson(us)); String results = us.getResults(); //将string类型和JSONArray进行互相转换 JSONArray parseArray = JSONObject.parseArray(results); //将今天的数据插入到数组中,在原来的数组里进行update操作 parseArray.set((integer.valueOf(day)-1), 1); String jsonString2 = parseArray.toJSONString(); //补签到 logger.info("用户中心", "签到的provider:补签入参", false, userID+"用户ID和月份"+month); userSignsService.updateUserSign(jsonString2,us.getID()); //目前直接将一个jsonArray返回 res.setData(date); } catch (Exception e) { logger.error("用户中心", "签到的provider:补签入参", "补签失败"); res.setSuccess(false); res.setErrorMsg("补签失败"); } return res; } |
6. 数据库定义
1.主要作用:保存用户签到的基本情况。
字段 |
是否索引 |
描述 |
ID |
否 |
签到的ID |
USER_ID |
否 |
用户的ID |
MONTH |
否 |
月份(形式:2018-02) |
RESULTS |
否 |
数组内存储0,1 |
IS_DEL |
否 |
是否删除 |
7. 缓存设计
无
8. 测试
使用jmeter进行测试,QA签到测试正常:
相关阅读
设计一个基于51单片机的电子温度计系统,其采用STC12C5A60S2芯片作为控制中心,DS18B20温度传感器为测温元件,LCD为显示器件。硬件设计
过度设计,一般是说过度满足用户需求的设计,用户想要A,你给了他ABCDE,结果BCDE全部用不上,既让用户选择困难,又浪费了团队开发时间。即使
2018年即将进入尾声,年终总结也要开始写起来了。作为一个设计师,你的年终总结思路可以是怎样的?一起来看看吧~又到了一年一度总结的
onboarding是新用户引导流程,是刺激用户激活的增长手段之一;文章是在研究了上百个优秀网站的新用户引导后,提炼出来的方法论,很具有参
这个世界上有 10 大经典谎言,其中之一就是在注册或安装时的那句「我已阅读并同意本条款的使用。」谎言从何而来?又去何处?今天分享的