SpringBoot实战(十八):签到奖励实现方案(转载)

前言
最近在做社交业务,用户进入APP后有签到功能,签到成功后获取相应的奖励:

项目状况:前期尝试业务阶段;

特点:

快速实现(不需要做太重,满足初期推广运营即可)
快速投入市场去运营
用户签到:

用户在每次启动时查询签到记录(规则:连续7日签到从0开始,签到过程中有断签从0开始)
如果今日未签到则提示用户可以进行签到
用户签到获取相应的奖励
提到签到,脑海中首先浮现特点:

需要记录每位用户每天的签到情况
查询时根据规则进行签到记录情况
需求&流程设计&技术实现方案
需求原型图

查询签到记录

进行签到

技术实现方案

SpringBoot

MySQL

数据库表结构
签到记录最新表

CREATE TABLE zh_sign_in (
id bigint(20) NOT NULL AUTO_INCREMENT,
bu_no varchar(32) DEFAULT NULL COMMENT ‘业务编码’,
customer_id varchar(32) DEFAULT NULL COMMENT ‘签到用户编码’,
sign_in_date datetime DEFAULT NULL COMMENT ‘签到日期(单位精确到日)’,
reward_money int(11) DEFAULT NULL COMMENT ‘本次签到奖励金币个数’,
continuite_day int(2) DEFAULT ‘1’ COMMENT ‘连续签到天数(A:7天内如果有断签从0开始 B:7天签满从0开始)’,
create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT ‘创建时间’,
update_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT ‘更新时间’,
param1 int(2) DEFAULT NULL COMMENT ‘预留字段1’,
param2 int(4) DEFAULT NULL COMMENT ‘预留字段2’,
param3 int(11) DEFAULT NULL COMMENT ‘预留字段3’,
param4 varchar(20) DEFAULT NULL COMMENT ‘预留字段4’,
param5 varchar(32) DEFAULT NULL COMMENT ‘预留字段5’,
param6 varchar(64) DEFAULT NULL COMMENT ‘预留字段6’,
PRIMARY KEY (id) USING BTREE,
UNIQUE KEY uk_zh_sign_in_buno (bu_no),
UNIQUE KEY uk_zh_sign_in_cid_signindate (customer_id,sign_in_date) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘用户签到表’;

签到记录历史表

CREATE TABLE zh_sign_in_hist (
id bigint(20) NOT NULL AUTO_INCREMENT,
bu_no varchar(32) DEFAULT NULL COMMENT ‘业务编码’,
customer_id varchar(32) DEFAULT NULL COMMENT ‘签到用户编码’,
sign_in_date datetime NULL DEFAULT NULL COMMENT ‘签到日期(单位精确到日)’,
reward_money int(11) DEFAULT NULL COMMENT ‘本次签到奖励金币个数’,
continuite_day int(2) DEFAULT ‘1’ COMMENT ‘连续签到天数(A:7天内如果有断签从0开始 B:7天签满从0开始)’,
create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT ‘创建时间’,
update_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT ‘更新时间’,
param1 int(2) DEFAULT NULL COMMENT ‘预留字段1’,
param2 int(4) DEFAULT NULL COMMENT ‘预留字段2’,
param3 int(11) DEFAULT NULL COMMENT ‘预留字段3’,
param4 varchar(20) DEFAULT NULL COMMENT ‘预留字段4’,
param5 varchar(32) DEFAULT NULL COMMENT ‘预留字段5’,
param6 varchar(64) DEFAULT NULL COMMENT ‘预留字段6’,
PRIMARY KEY (id) USING BTREE,
UNIQUE KEY uk_zh_sign_in_hist_cid_signindate (customer_id,sign_in_date) USING BTREE,
KEY key_zh_sign_in_hist_buno (bu_no) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘用户签到历史表’;

代码实现
完整代码(GitHub,欢迎大家Star,Fork,Watch)

https://github.com/dangnianchuntian/springboot

主要代码展示

Controller
/*

  • Copyright © 2020. zhanghan_java@163.com All Rights Reserved.
  • 项目名称:Spring Boot实战:签到奖励实现方案
  • 类名称:SignInController.java
  • 创建人:张晗
  • 联系方式:zhanghan_java@163.com
  • 开源地址: https://github.com/dangnianchuntian/springboot
  • 博客地址: https://zhanghan.blog.csdn.net
    */

package com.zhanghan.zhsignin.controller;

import com.zhanghan.zhsignin.controller.request.PostSignInRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.zhanghan.zhsignin.controller.request.ListSignInDetailRequest;
import com.zhanghan.zhsignin.service.SignInService;

@RestController
public class SignInController {

@Autowired
private SignInService signInService;

/**
 * 查询签到记录
 */
@RequestMapping(value = "/list/sign/in/detail", method = RequestMethod.POST)
public Object listSignInDetail(@RequestBody @Validated ListSignInDetailRequest listSignInDetailRequest) {
    return signInService.listSignInDetail(listSignInDetailRequest);
}

/**
 * 用户进行签到
 */
@RequestMapping(value = "/post/sign/in", method = RequestMethod.POST)
public Object postSignIn(@RequestBody @Validated PostSignInRequest postSignInRequest) {
    return signInService.postSignIn(postSignInRequest);
}

}

service

/*

  • Copyright © 2020. zhanghan_java@163.com All Rights Reserved.
  • 项目名称:Spring Boot实战:签到奖励实现方案
  • 类名称:SignInServiceImpl.java
  • 创建人:张晗
  • 联系方式:zhanghan_java@163.com
  • 开源地址: https://github.com/dangnianchuntian/springboot
  • 博客地址: https://zhanghan.blog.csdn.net
    */

package com.zhanghan.zhsignin.service.impl;

import cn.hutool.core.util.IdUtil;
import com.zhanghan.zhsignin.config.SignInRewardMoneyListConfig;
import com.zhanghan.zhsignin.constant.SignInConstant;
import com.zhanghan.zhsignin.controller.request.ListSignInDetailRequest;
import com.zhanghan.zhsignin.controller.request.PostSignInRequest;
import com.zhanghan.zhsignin.controller.response.ListSignInDetailResponse;
import com.zhanghan.zhsignin.mybatis.entity.XZhSignInEntity;
import com.zhanghan.zhsignin.mybatis.entity.XZhSignInHistEntity;
import com.zhanghan.zhsignin.mybatis.mapper.XZhSignInHistMapper;
import com.zhanghan.zhsignin.mybatis.mapper.XZhSignInMapper;
import com.zhanghan.zhsignin.service.SignInService;
import com.zhanghan.zhsignin.util.DateUtils;
import com.zhanghan.zhsignin.util.wrapper.WrapMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.zhanghan.zhsignin.constant.SignInConstant.*;

@Service
public class SignInServiceImpl implements SignInService {

@Autowired
private XZhSignInMapper xZhSignInMapper;

@Autowired
private XZhSignInHistMapper xZhSignInHistMapper;

//校验连续天数是否为7
@Value("#{T(java.lang.Integer).parseInt('${zh.sign.in.continuite.day.threshold:7}')}")
public Integer continuiteDayThreshold;

//签到奖励金币集合配置
@Autowired
public SignInRewardMoneyListConfig signInRewardMoneyListConfig;


/**
 * 查询用户签到记录
 */
@Override
public Object listSignInDetail(ListSignInDetailRequest listSignInDetailRequest) {

    //若配置文件中未配置签到奖励则不展示签到记录
    List<Integer> signInRewardMoneyListConfigList = signInRewardMoneyListConfig.getList();
    if (CollectionUtils.isEmpty(signInRewardMoneyListConfigList)) {
        return WrapMapper.ok(new ListSignInDetailResponse(false));
    }

    String customerId = listSignInDetailRequest.getCustomerId();
    XZhSignInEntity xZhSignInEntity = xZhSignInMapper.findByCustomerId(customerId);

    List<ListSignInDetailResponse.SignInDetail> signInDetailList = signInRewardMoneyListConfigList.stream().map(aa -> new ListSignInDetailResponse.SignInDetail(0, aa)).collect(Collectors.toList());

    //该用户之前未签到过
    if (null == xZhSignInEntity) {
        return WrapMapper.ok(new ListSignInDetailResponse(TODAY_NOT_SIGN_IN, SignInConstant.CONTINUITE_DAY_ZERO, signInDetailList));
    }

    long signInDateTime = xZhSignInEntity.getSignInDate().getTime();

    //最近一次签到是否为昨日之前
    if (signInDateTime < DateUtils.getYesterdayDateTime()) {
        return WrapMapper.ok(new ListSignInDetailResponse(TODAY_NOT_SIGN_IN, SignInConstant.CONTINUITE_DAY_ZERO, signInDetailList));
    }

    //最近一次签到是否为昨日
    Integer todaySignStatus = TODAY_YES_SIGN_IN;
    Integer continuiteDay = xZhSignInEntity.getContinuiteDay();
    if (signInDateTime < DateUtils.getTodayDateTime()) {
        //最近一次签到是昨日且之前已连续签到7日
        if (continuiteDay >= continuiteDayThreshold) {
            return WrapMapper.ok(new ListSignInDetailResponse(TODAY_NOT_SIGN_IN, SignInConstant.CONTINUITE_DAY_ZERO, signInDetailList));
        }
        //最近一次签到是昨日且之前连续未超7日
        todaySignStatus = TODAY_NOT_SIGN_IN;
    }
    //查询用户签到历史记录
    List<XZhSignInHistEntity> xZhSignInHistEntitieList = xZhSignInHistMapper.listByCustomerIdAndLimit(customerId, continuiteDay);
    for (XZhSignInHistEntity xZhSignInHistEntity : xZhSignInHistEntitieList) {
        ListSignInDetailResponse.SignInDetail signInDetail = new ListSignInDetailResponse.SignInDetail(TODAY_YES_SIGN_IN, xZhSignInHistEntity.getRewardMoney());
        signInDetailList.remove(xZhSignInHistEntity.getContinuiteDay() - 1);
        signInDetailList.add(xZhSignInHistEntity.getContinuiteDay() - 1, signInDetail);
    }

    return WrapMapper.ok(new ListSignInDetailResponse(todaySignStatus, continuiteDay, signInDetailList));
}

/**
 * 进行签到
 */
@Override
public Object postSignIn(PostSignInRequest postSignInRequest) {

    //若配置文件中未配置签到奖励则不展示签到记录
    List<Integer> signInRewardMoneyListConfigList = signInRewardMoneyListConfig.getList();
    if (CollectionUtils.isEmpty(signInRewardMoneyListConfigList)) {
        return WrapMapper.ok();
    }

    //获取session用户对象
    String customerId = postSignInRequest.getCustomerId();
    //根据customerId查询用户签到记录
    XZhSignInEntity xZhSignInEntityByCustomerId = xZhSignInMapper.findByCustomerId(customerId);
    //签到记录是否为空
    if (null == xZhSignInEntityByCustomerId) {
        XZhSignInEntity xZhSignInEntity = new XZhSignInEntity();
        xZhSignInEntity.setBuNo(IdUtil.simpleUUID());
        xZhSignInEntity.setCustomerId(customerId);
        xZhSignInEntity.setContinuiteDay(CONTINUITE_DAY_ONE);
        xZhSignInEntity.setRewardMoney(signInRewardMoneyListConfigList.get(0));
        xZhSignInEntity.setSignInDate(DateUtils.getTodayDate());
        insertSigninAndHist(xZhSignInEntity);
        return WrapMapper.ok();
    }

    long signInDateTime = xZhSignInEntityByCustomerId.getSignInDate().getTime();
    if (signInDateTime == DateUtils.getTodayDateTime()) {
        return WrapMapper.error("今天已经签到");
    }

    //获取连续签到天数
    Integer continuiteDay = continuiteDay(xZhSignInEntityByCustomerId.getContinuiteDay(), signInDateTime);
    xZhSignInEntityByCustomerId.setSignInDate(DateUtils.getTodayDate());
    xZhSignInEntityByCustomerId.setContinuiteDay(continuiteDay);
    xZhSignInEntityByCustomerId.setRewardMoney(signInRewardMoneyListConfigList.get(continuiteDay - 1));
    xZhSignInEntityByCustomerId.setUpdateTime(new Date());
    xZhSignInEntityByCustomerId.setBuNo(IdUtil.simpleUUID());
    updateSignInAndInsertHist(xZhSignInEntityByCustomerId);

    return WrapMapper.ok();

}

private Integer continuiteDay(Integer continuiteDay, Long signInDateTime) {
    if (signInDateTime < DateUtils.getYesterdayDateTime()) {
        return CONTINUITE_DAY_ONE;
    }
    if (continuiteDay >= continuiteDayThreshold) {
        return CONTINUITE_DAY_ONE;
    }
    return continuiteDay + 1;
}

private void insertSigninAndHist(XZhSignInEntity xZhSignInEntity) {
    xZhSignInMapper.insertSelective(xZhSignInEntity);
    XZhSignInHistEntity xZhSignInHistEntity = new XZhSignInHistEntity();
    BeanUtils.copyProperties(xZhSignInEntity, xZhSignInHistEntity);
    xZhSignInHistEntity.setId(null);
    xZhSignInHistMapper.insertSelective(xZhSignInHistEntity);
}

private void updateSignInAndInsertHist(XZhSignInEntity xZhSignInEntity) {
    xZhSignInMapper.updateByPrimaryKeySelective(xZhSignInEntity);
    XZhSignInHistEntity xZhSignInHistEntity = new XZhSignInHistEntity();
    BeanUtils.copyProperties(xZhSignInEntity, xZhSignInHistEntity);
    xZhSignInHistEntity.setId(null);
    xZhSignInHistMapper.insertSelective(xZhSignInHistEntity);
}

}

测试
模拟用户进行签到

进行请求

查看数据库结果

模拟用户查询签到记录

进行请求

总结
亮点:实现业务连续签到,断签以及奖励的业务
注意点:基于数据库查询做的,在进行签到接口需要用redis锁防止并发操作
后续会持续分享更多业务中的亮点

原文链接:https://blog.csdn.net/zhanghan18333611647/article/details/108306434?utm_medium=distribute.pc_feed.none-task-blog-cf-3.nonecase&depth_1-utm_source=distribute.pc_feed.none-task-blog-cf-3.nonecase&request_id=5f4c61096fbae3485ef38a58

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值