Design and implementation of one-time sign-in

Sign in:

Prerequisite instructions:

    When I first started doing sign-ins, I referred to the ideas and cases of many experts on the Internet, but found that they were incompatible with the sign-ins in my project and the requirements put forward by the boss, so in the process of communicating and guiding with the boss, I made A sign-in system that meets product requirements.

Difficulty 1 : Use one field to store the check-in status of the entire month, and need to calculate the number of consecutive check-in days?

Solution: Use an array to store the check-in status for a month. 0 means not signed in, 1 means signed in. After getting the array, loop forward according to the situation of the day to find if it is 1. If the number of consecutive check-in days is 1, add 1 and end the loop when it encounters 0. .

Pit 1: When the user signs in on the first day of the month, or when he clicks on the sign-in page for the first time in the month, the sign-in list is obtained. If the sign-in list is empty, an error is reported?

Cause and solution: The sign-in array is generated after the user signs in every month. When the user signs in for the first time every month, the sign-in array is not generated, so an error is reported. The solution is to add a judgment and return an empty array to the front end on the first day of each month.

 

1. Need analysis

1. The check-in reminder is turned on by default. When it is turned on, the system will push the check-in system message at 11:50 every day.

2. Sign in on a daily basis, if the sign in is successful, the number of consecutive days you have signed in +1

3. If there is an interruption in the middle, the consecutive sign-ins will be cleared, and the number of consecutive days will be counted again from the next sign-in.

4. Count the number of consecutive check-in days based on the natural month, and the statistics will be restarted on the 1st of the next month.

5. If you did not sign in today, the copy will be displayed: If you sign in successfully, you can get XX Fun Coins (call a third party: send a kafka message to the calculation center of Qu Coin);

6. You have signed in today, and the text is displayed: Sign in tomorrow to get XX Fun Coins. Change the sign in now to signed in button to be grayed out

2. Design points

1. Considering the number of users in the early stage, currently only dimensionality reduction is carried out in the web layer to protect the program. No caching is performed, and users directly access the database.

2. Currently, according to product requirements, only two interfaces for sign-in and sign-in list are provided.

3. The function of re-signing has been implemented. The interface currently exists and is available, but it is not needed by the product. When needed in the future, the interface can be used directly by releasing it.

3. Main flow chart

 

4. Call sequence example diagram

 

5. Main interface definition

1. Submit check-in

 @ApiOperation(value = "Submit check-in", notes = "Submit check-in", 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\":\"Number of consecutive check-in days\"\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 {

Important logic:

1. Every month's check-in is a new array, and the length of the array is consistent with the number of days in the month.

2. Continuous check-in, by judging the number of previous check-in days in the array, confirm the number of days the current user has continuously checked in.

}

1.1 Main logic of sign-in

    

/**
      * 
      * @Title: userSignByUserID  
      * @Description: TODO (user signs in by 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();                //Get user parameters             Map<String, Object> data = req.getData();              //Get userID             String userID = (String) req.getData().  get("userID");             //Get the current month and day of the system






            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);
            //Get the year and month of the system to calculate the number of days in each month and use it to set the length of the array
            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); 
            //Set the number of check-in days to 1
            int[] results = new int[maxDays];
            results[days-1] = 1; 
            //The array needs to be converted to int for storage
            String jsonString = JSONObject.toJSONString(results); 
            //Encapsulate the return object
            Map<String , Object> map = new HashMap<>();
            try {                 if((days-1) == 0) {//0 represents the first number of the array, indicating the first day of each month                     //Generate primary key ID                     Long id = SequenceUtil.create().sequenceNextVal("fl_user_sign");                     data.put("ID", id);                     //Encapsulate the user entity class                     UserSign userSign = new UserSign();                     userSign.setID(id);                     userSign.setUserID(userID);








                    userSign.setMonth(month);
                    userSign.setResults(jsonString);
                    //Sign-in
                    logger.info("User Center", "Sign-in provider: Submit sign-in", false, "First sign-in at the beginning of the month:"+ JsonUtils.objectToJson( userSign));
                    userSignsService.saveUserSign(userSign);
                    //Define the number of consecutive sign-in days
                    int conSigns = 1;
                    map.put("conSigns", conSigns);
                }else {                 //To determine how many consecutive sign-ins there are, you need to get the current month's sign-in list to determine whether you signed in the previous day. If you signed in yesterday, add 1 to the total number of sign-in days, otherwise it is set to 1                   logger.debug("User Center", "Sign-in provider: Parameters for judging continuous sign-ins", false,userID+"User ID and month"+month);                 UserSign us = userSignsService.queryUserSignList(userID,month); 



                //If us is empty, it means that No. 1 has not signed in. Here you need to regenerate a piece of data and insert it into the database 
                if(us == null) {                     //Generate primary key ID                         Long id = SequenceUtil.create().sequenceNextVal("fl_user_sign" );                         data.put("ID", id);                         //Encapsulate the user entity class                         UserSign userSign = new UserSign();                         userSign.setID(id);                         userSign.setUserID(userID);                         userSign.setMonth(month);                         userSign. setResults(jsonString);                         //Sign-in                         logger.info("User Center", "Sign-in provider: Submit sign-in", false, JsonUtils.objectToJson(userSign));











                        userSignsService.saveUserSign(userSign);
                        //Define the number of days of continuous sign-in
                        int conSigns = 1;
                        map.put("conSigns", conSigns);
                    }else {                   String results2 = us.getResults();                   Long ID = us.getID() ;                 //Convert string type to JSONArray                 JSONArray parseArray = JSONObject.parseArray(results2);                 //Judge the obtained sign-in array and calculate the number of consecutive sign-in days                 int conSigns = 1;                 for(int i=(days- 2);i>=0;i--) {                     Byte signs = parseArray.getByte(i);                      //If there is no sign-in the day before, return to the sign-in day









                    if(signs == 0 && i == (days-2)) {  
                        map.put("conSigns", conSigns);
                        break;
                    }
                    if(signs == 1) {                         //Judge the number of consecutive days here                            conSigns += 1;                          map.put("conSigns", conSigns);                         }                     //If the failure to sign in is not the same as yesterday, exit the loop                     if(signs == 0 && i != (days-2)) {                         break;                     }                 }                 //Will Today's data is inserted into the array, and the update operation is performed on the original array                 parseArray.set(days-1, 1);











                String jsonString2 = parseArray.toJSONString();
                //Sign in
                logger.info("User Center", "Sign in provider: Sign in parameters", false, jsonString2);
                userSignsService.updateUserSign(jsonString2,ID);
                    } 
                }  
                res.setData (map);
                //Send kafka message and add Qucoin experience value
                sendCoinKafkaMessage(userID);
                logger.info("User Center", "Sign-in provider: Send kafka message to Qucoin during sign-in and end of operation", false, userID) ;
                res.setSuccess(true);
            } catch (Exception e) {                 res.setSuccess(false);                 logger.error("User Center","Sign-in provider: Sign-in business failed",e);


                res.setErrorMsg("Sign-in failed");
            }
            return res;
        
    }

   

 

 

 

2.  Check-in list

 @ApiOperation(value = "check-in list", notes = "check-in list", httpMethod = "POST")

    @ApiImplicitParams({

            @ApiImplicitParam(name = "jsonPara", value = "{\"userID\":\"UserID\",\"month\":\"2018-01, time range, accuracy to month, return monthly check-in list \"}", 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签到测试正常:

Guess you like

Origin blog.csdn.net/Tank_666/article/details/82634217