Реализация функции входа не так сложна, как вы думаете (2)

Java
Реализация функции входа не так сложна, как вы думаете (2)

1 календарный цикл регистрации

Период заезда:Обычно используемый цикл регистрации составляет одну неделю или один месяц. Наше приложение принимает план на один месяц. Интерфейс календаря регистрации на рынке аналогичен. Далее я поделюсь с вами планом реализации ежемесячного календаря регистрации. и сопутствующая регистрация План реализации миссии.

2 Эффект отображения и анализ интерфейса

2.1 Рендеринг

2.2 Анализ спроса

Анализируя рисунок, этот интерфейс можно условно разделить на четыре части.

  • Всего очков часть главы
  • Самая важная часть отображения календаря регистрации
  • Раздел конфигурации непрерывной регистрации копирования
  • Войти в раздел отображения задач

Путем анализа я разделил этот интерфейс на три интерфейса

  • /signInПротокол GET используется для запроса общего количества баллов и части календаря регистрации в заголовке.
  • /signIn/configurationПротокол GET Запросите конфигурацию непрерывного копирования входа. Если вам не нужен фон, вы можете настроить количество и количество копий непрерывного входа, чтобы получить баллы. Этот интерфейс можно не использовать, а внешний интерфейс сложно - закодировано.
  • /signIn/taskПротокол GET используется для запроса задач регистрации и состояния завершения каждой задачи.

3 Запросите общее количество баллов, войдите в интерфейс календаря.

 public ResponseResult selectSignIn(Integer userId, Integer year, Integer month) {
        boolean signFlag = Boolean.FALSE;
        String signKey = String.format(RedisKeyConstant.USER_SIGN_IN, year, userId);
        LocalDate date = LocalDate.of(year, month, 1);
        //这个方法前面的文章有介绍过.是查询出一个偏移值区间的位图集合
        List<Long> list = cacheClient.getBit(signKey, month * 100 + 1, date.lengthOfMonth());
        //查询reids中当前用户补签的hash列表 (hash列表的key为补签的日期,value存在就说明这个日期补签了)
        String retroactiveKey = String.format(RedisKeyConstant.USER_RETROACTIVE_SIGN_IN, date.getMonthValue(), userId);
        Set<String> keys = cacheClient.hkeys(retroactiveKey);
        TreeMap<Integer, Integer> signMap = new TreeMap<>();
        if (list != null && list.size() > 0) {
            // 由低位到高位,为0表示未签,为1表示已签
            long v = list.get(0) == null ? 0 : list.get(0);
            //循环次数为当月的天数
            for (int i = date.lengthOfMonth(); i > 0; i--) {
                LocalDate d = date.withDayOfMonth(i);
                int type = 0;
                if (v >> 1 << 1 != v) {
                    //状态为正常签到
                    type = 1;
                    //这里和当前日期对比,方便前端特殊标记今天是否签到
                    if (d.compareTo(LocalDate.now()) == 0) {
                        signFlag = Boolean.TRUE;
                    }
                }
                if (keys.contains(d.getDayOfMonth() + "")) {
                    //状态为补签
                    type = 2;
                }
                //返回给前端当月的所有日期,以及签,补签或者未签的状态
                signMap.put(Integer.parseInt(d.format(DateTimeFormatter.ofPattern("dd"))), type);
                v >>= 1;
            }
        }
        ResponseResult responseResult = ResponseResult.newSingleData();
        Map<String, Object> result = new HashMap<>(2);
        //前文有介绍过这个表存储了用户的总积分
        UserIntegral userIntegral = userIntegralService.getOne(new LambdaQueryWrapper<UserIntegral>().eq(UserIntegral::getUserId, userId));
        //用户总积分
        result.put("total", userIntegral.getIntegral());
        //用户今日是否签到
        result.put("todaySignFlag", signFlag ? 1 : 0);
        //后端返回日期是为了防止手机端直接修改系统时间导致的问题
        result.put("today", LocalDate.now().getDayOfMonth());
        //当月的签到情况
        result.put("signCalendar", signMap);
        //返回给前端这个月的第一天是星期几,方便前端渲染日历图的时候定位
        result.put("firstDayOfWeek", date.getDayOfWeek().getValue());
        //服务器的当前月份(同上,防止手机端直接修改系统时间)
        result.put("monthValue", date.getMonthValue());
        //用户当月补签的次数
        result.put("retroactiveCount", keys.size());
        //日历部分会有上月的结尾几天的数据,所以这里需要返回给前端上个月共有多少天
        result.put("lengthOfLastMonth", date.minusMonths(1).lengthOfMonth());
        responseResult.setData(result);
        return responseResult;
    }

Поскольку запрос растрового изображения Redis используется как единое целое, данные регистрации каждого пользователя изолированы по ключу, а временная сложность составляетO(1), Данные могут быть возвращены в течение 100 миллисекунд.

4. Запросите задачу регистрации и статус выполнения задачи.

В этой части используется метод запросов Redis и MySQL. Мы выполнили фоновую настройку задачи. Она разделена на одну, которую можно выполнить только один раз.福利任务и можно сбрасывать каждый день每日任务.

4.1 Структура таблицы

При разработке этой таблицы задач всегда необходимо обращать внимание на тип и метод перехода, поскольку разные задачи имеют разное функциональное разделение.jump_typeвыделить их функциональные зоны.jump_sourceЭто может быть адрес H5 или маршрутный адрес мобильного телефона, его можно гибко контролировать, интерфейс вызывает интерфейс, который выполняет задачу, и передает соответствующую задачу.task_tagдля выполнения указанной задачи

CREATE TABLE `t_user_integral_task` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `task_type` tinyint(4) DEFAULT '1' COMMENT '任务类型 1.每日任务 2福利任务',
  `task_tag` varchar(100) DEFAULT NULL COMMENT '任务前端标识(大写字母组合)',
  `task_title` varchar(100) DEFAULT NULL COMMENT '任务标题',
  `icon` varchar(255) DEFAULT NULL COMMENT '小图标',
  `task_copy` varchar(100) DEFAULT NULL COMMENT '任务文案',
  `integral` int(16) DEFAULT '0' COMMENT '任务赠送积分数',
  `jump_type` tinyint(4) DEFAULT NULL COMMENT '跳转方式 1.跳转指定商品 2.跳转链接 3.跳转指定接口,4:跳转随机商品',
  `jump_source` text COMMENT '跳转或分享的地址',
  `sort` tinyint(2) DEFAULT '0' COMMENT '排序号',
  `delete_flag` tinyint(2) DEFAULT '0' COMMENT '删除/隐藏,0:未删除/未隐藏,1:已删除/已隐藏',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户任务表'

4.2 Запрос задачи

Поскольку существует всего около десяти ежедневных задач и задач благосостояния, запрос mysql выполняется очень быстро, тогда статус выполнения сохраняется в Redis, а временная сложность составляетO(1)

public ResponseResult selectSignInTask(Integer userId) {
        ResponseResult responseResult = ResponseResult.newSingleData();
        //先查出签到任务的mysql记录.
        List<UserIntegralTask> userIntegralTaskList = list(new LambdaQueryWrapper<UserIntegralTask>()
                .orderByDesc(UserIntegralTask::getTaskType).orderByAsc(UserIntegralTask::getSort));
        //创建一个map,key为任务的task_tag,value存在则是完成了该任务.
        //每日任务和福利任务分为两个reids hash存储.每日任务的key中包含当天日期,过期时间为一天.福利任务则是永久保存
        Map<String, String> completeFlagMap = new HashMap<>(userIntegralTaskList.size());
        Map<String, String> welfareMap = cacheClient.hgetAll(String.format(RedisKeyConstant.USER_SIGN_WELFARE_TASK, userId));
        if (CollUtil.isNotEmpty(welfareMap)) completeFlagMap.putAll(welfareMap);
        Map<String, String> dailyMap = cacheClient.hgetAll(String.format(RedisKeyConstant.USER_SIGN_DAILY_TASK, LocalDate.now().getDayOfMonth(), userId));
        //把两个hash合并
        if (CollUtil.isNotEmpty(dailyMap)) completeFlagMap.putAll(dailyMap);
        //循环库中的任务列表,并用hash的get方法查询是否完成,然后给到前端
        userIntegralTaskList.forEach(task -> {
            task.setCreateTime(null);
            task.setUpdateTime(null);
            task.setIntegral(null);
            String value = completeFlagMap.get(task.getTaskTag());
            if (null == value) {
                task.setCompleteFlag(0);
            } else {
                task.setCompleteFlag(1);
            }
        });
        responseResult.setData(userIntegralTaskList);
        return responseResult;
    }

4.3 Выполните задание

Метод для выполнения задачи. Установить как общедоступный метод. Передать соответствующийtask_tagИдентифицировать для выполнения указанной задачи.Остается только судить, является ли это ежедневной задачей или задачей благосостояния.Запишите их в разные хэши redis.

    //伪代码
    public ResponseResult saveSignInTask(Integer userId, String tag) {
       //查询出mysql中对应的tag任务,获取关键信息.(`integral`)
       ....
       //写入积分记录表.对应当前任务title的记录
       ...
       //在redis里写入当前用户的这个任务完成状态(这里要注意如果是每日任务要给hash 列表给一天的过期时间,防止脏数据长时间不被清理,占用redis的内存空间)
    }

На данный момент реализована функция входа на основе схемы растрового изображения Redis. Содержание реализации примерно такое: вход, регистрация, увеличение и уменьшение баллов, а также можно настроить функции задач. Если у вас есть другие потребности, пожалуйста, оставьте сообщение в области комментариев, чтобы обсудить вместе.