必威体育Betway必威体育官网
当前位置:首页 > IT技术

记一次签到的设计和实现

时间:2019-08-06 12:42:11来源:IT技术作者:seo实验室小编阅读:50次「手机版」
 

签到

签到:

前提说明:

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

难点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单片机的电子温度计系统的设计与实现

设计一个基于51单片机的电子温度计系统,其采用STC12C5A60S2芯片作为控制中心,DS18B20温度传感器为测温元件,LCD为显示器件。硬件设计

那些你想太多的“过度设计”

过度设计,一般是说过度满足用户需求的设计,用户想要A,你给了他ABCDE,结果BCDE全部用不上,既让用户选择困难,又浪费了团队开发时间。即使

设计师的年终总结,该如何下手?

2018年即将进入尾声,年终总结也要开始写起来了。作为一个设计师,你的年终总结思路可以是怎样的?一起来看看吧~又到了一年一度总结的

用户激活:围绕用户体验的Onboarding设计大法

onboarding是新用户引导流程,是刺激用户激活的增长手段之一;文章是在研究了上百个优秀网站的新用户引导后,提炼出来的方法论,很具有参

看不惯又干不掉的引导界面,该怎么设计?

这个世界上有 10 大经典谎言,其中之一就是在注册或安装时的那句「我已阅读并同意本条款的使用。」谎言从何而来?又去何处?今天分享的

分享到:

栏目导航

推荐阅读

热门阅读