记一次签到的设计和实现 - Go语言中文社区

记一次签到的设计和实现


签到:

前提说明:

    在刚开始做签到的时候,参考了网上诸多大神的想法和案例,但都发现与自己项目中的签到,以及老大所提出的要求格格不入,所以就在和老大沟通和指导的过程中,做出符合产品要求的签到系统。

难点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签到测试正常:

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Tank_666/article/details/82634217
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-09-04 14:42:13
  • 阅读 ( 1052 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

推荐文章

猜你喜欢