签到得积分功能实现

1. 需求分析

动态生成日历

签到可以获得积分,礼券,小样,正品等

连续签到,需要显示给用户,已经连续签到多少天

若中断签到,将重置签到,从第一天开始计算

签到对于时间的精确度要求比较高,当天签到的,那么记录的签到时间是当天,如果还有后续的一系列操作,比如计算积分,那后续操作的时间必须和签到的时间一致

对于凌晨,跨天之类的场景,特别需要注意


2.表结构设计

用户每日签到表user_day_sign

签到奖励表sign_reward

以及积分表user_point和积分变动流水账单表user_point_stream,是以前就有的,只需要对相应的积分增加和流水账单数据的增加

3. 后台代码实现

Controller

/**
 * 签到相关
 */
@Slf4j
@RestController
@RequestMapping("/api")
public class UserDaySignController extends BaseController {

	@Resource
	private UserDaySignService userDaySignService;

	/**
	 * 签到日历
	 */
	@GetMapping(value = "/user/daySignInfo")
	@LogonRequired
	public AppResult<UserDaySignInfoRes> daySignInfo() {

		UserDaySignInfoRes userDaySignInfoRes = userDaySignService.daySignInfo(getUid());

		return AppResultUtils.normal(userDaySignInfoRes);
	}

	/**
	 * 日签到
	 */
	@GetMapping(value = "/user/daySignIn")
	@LogonRequired
	public AppResult<Boolean> daySignIn(@Validated UserSignInReq userSignInReq) {
		return AppResultUtils.normal(userDaySignService.daySignIn(getUid(),userSignInReq));
	}

service 


public interface UserDaySignService extends IService<UserDaySign> {

    /**
     * 签到日历
     * @param uid 用户id
     * @return
     */
    UserDaySignInfoRes daySignInfo(Integer uid);

    /**
     * 日签到
     * @param uid 用户id
     * @param userSignInReq 用户签到信息请求
     * @return
     */
    Boolean daySignIn(Integer uid, UserSignInReq userSignInReq);
}

具体实现impl:

@Service
public class UserDaySignImpl extends ServiceImpl<UserDaySignMapper, UserDaySign> implements UserDaySignService {

    @Resource
    private SignRewardMapper signRewardMapper;

    @Resource
    private UserPointMapper userPointMapper;

    @Resource
    private UserPointStreamMapper userPointStreamMapper;


    @Override
    public UserDaySignInfoRes daySignInfo(Integer uid) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("uid",uid);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String today = sdf.format(new Date());
        queryWrapper.orderByDesc("sign_day");
        queryWrapper.last("limit 1");
        UserDaySign userDaySign = baseMapper.selectOne(queryWrapper);
        if (userDaySign == null) {//day_sign无数据
            userDaySign = new UserDaySign();
            userDaySign.setSignDay(today);
            userDaySign.setConsecutiveDays(0);
            userDaySign.setUid(uid);
            userDaySign.setSignStatus(0);
            baseMapper.insert(userDaySign);
        } else {
            if (!userDaySign.getSignDay().equals(today)) {//有数据但不是今天的,沿用前一天的连续签到时间和开始签到日期
                UserDaySign userDaySign1 = new UserDaySign();
                if (userDaySign.getConsecutiveDays() > 0) {
                    userDaySign1.setConsecutiveDays(userDaySign.getConsecutiveDays());
                    userDaySign1.setFirstSignDay(userDaySign.getFirstSignDay());
                } else {
                    userDaySign.setConsecutiveDays(0);
                }
                userDaySign1.setSignDay(today);
                userDaySign1.setUid(uid);
                userDaySign1.setSignStatus(0);
                baseMapper.insert(userDaySign1);
            }
        }
        UserPoint userPoint = userPointMapper.selectOne(new QueryWrapper<UserPoint>().eq("uid",uid));
        queryWrapper = new QueryWrapper();
        queryWrapper.orderByAsc("consecutive_days");
        List<SignReward> signRewards = signRewardMapper.selectList(queryWrapper);
        UserDaySignInfoRes userDaySignInfoRes = new UserDaySignInfoRes();
        if (userPoint != null) {
            userDaySignInfoRes.setConsecutiveDays(userDaySign.getConsecutiveDays());
            userDaySignInfoRes.setRewardTotal(userPoint.getTotalBalance());
            List<UserDaySignRes> calendar = new ArrayList<>();
            for (SignReward signReward : signRewards) {
                UserDaySignRes userDaySignRes = new UserDaySignRes();
                userDaySignRes.setConsecutiveDays(signReward.getConsecutiveDays());
                userDaySignRes.setRewardAmount(signReward.getRewardAmount());
                if (userDaySign.getConsecutiveDays() > 0 && userDaySign.getConsecutiveDays() >= signReward.getConsecutiveDays()) {
                    userDaySignRes.setIsConsecutive(true);
                } else {
                    userDaySignRes.setIsConsecutive(false);
                }
                calendar.add(userDaySignRes);
            }
            userDaySignInfoRes.setCalendar(calendar);
        }
        return userDaySignInfoRes;
    }

    @Override
    public Boolean daySignIn(Integer uid, UserSignInReq userSignInReq) {
        //用户签到,更新user_day_sign记录
        QueryWrapper queryWrapper = new QueryWrapper<UserDaySign>();
        queryWrapper.eq("uid",uid);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String today = sdf.format(new Date());
        queryWrapper.eq("sign_day",today);
        UserDaySign userDaySign = baseMapper.selectOne(queryWrapper);
        if (userDaySign != null) {
            userDaySign.setConsecutiveDays(userSignInReq.getConsecutiveDays());
            if (userSignInReq.getConsecutiveDays() == 0) {
                userDaySign.setFirstSignDay(today);
            }
            userDaySign.setRewardAmount(userSignInReq.getRewardAmount());
            userDaySign.setSignStatus(1);
            baseMapper.updateById(userDaySign);
        } else {
            return false;
        }
        UserPoint userPoint = userPointMapper.selectOne(new QueryWrapper<UserPoint>().eq("uid",uid));
        userPoint.setTotalBalance(userPoint.getTotalBalance().add(userSignInReq.getRewardAmount()));
        UserPointStream userPointStream = new UserPointStream();
        userPointStream.setAmount(userSignInReq.getRewardAmount());
        userPointStream.setPurpose(1);
        userPointStream.setType(2);
        userPointStream.setUid(uid);
        userPointStream.setRemainAmount(userPoint.getTotalBalance());
        userPointStream.setTotalAmount(userPoint.getTotalBalance());
        if (userPointMapper.updateById(userPoint) == 1 && userPointStreamMapper.insert(userPointStream) == 1) {
            return true;
        }
        return false;
    }
}

其中用到的数据返回有两个类:

UserDaySignRes 

/**
 * 签到日历
 */
@Data
public class UserDaySignRes {

	/**
	 * 天数
	 */
	private int consecutiveDays;

	/**
	 * 是否已签到
	 */
	private Boolean isConsecutive;

	/**
	 * 积分
	 */
	private BigDecimal rewardAmount;
}

UserDaySignInfoRes 

/**
 * 签到日历
 */
@Data
public class UserDaySignInfoRes {

	/**
	 * 连续天数
	 */
	private int consecutiveDays;


	/**
	 * 总积分
	 */
	private BigDecimal rewardTotal;

	/**
	 * 签到日历
	 */
	private List<UserDaySignRes> calendar;
}

用户签到请求:

UserSignInReq 

/**
 * 用户签到信息请求
 */
@Data
public class UserSignInReq {

	@NotNull(message = "连续签到天数不能为空")
	private int consecutiveDays;
	@NotNull(message = "签到奖励积分不能为空")
	private BigDecimal rewardAmount;
}

 一共有两个接口,一个是查询接口,查询总积分+签到日历+当前连续签到天数。  一个是签到接口:返回签到是否成功,签到目前是增加了明细变更,更新了总积分额度。还有一个user_day_sign表需要更新签到连续天数,以及如果是第一次签到的话还要更新签到起始天数。

第 1/27 页 Java 课程设计项目实例《基于微服务的在线签到》子系统 一、前言 作为智能考勤管理系统、会议管理系统等多种系统组成部分的"在线签到"子系统从 业务逻辑本身来看,并没有什么复杂性。不外乎是前端用户通过手机 APP、PC 电脑应用程 序或者 Web 浏览器等形式的客户端访问服务器端相关程序中的签到方法,记录用户签到的 时间等信息。但问题是如何能够让一个系统同时能够支持多种不同形式的客户端访问? "在线签到"子系统选用目前比较流行热门的"微服务"( Microservice)作为系统 的技术实现方案,作者在本文中将重点介绍子系统所应用的核心技术——微服务、反射、 对象序列化、多线程以及基于 TIP/IP 的 Socket 通讯等,此外还通过具体的程序代码实现为 读者介绍微服务的底层实现原理、Socket 通讯编程实现。 当然,作为课程设计文档的规范格式中所必需的"系统需求"、"系统设计""项 目总结"等章节的内容在本文中,由于本文的篇幅关系,作者暂不涉及这些章节的内容— —本文的写作重点在"技术""实现"两方面。感兴趣的读者可以将本文嵌入到自己的 课程设计系统中或者引用本文相关内容, 以丰富完善自己的课程设计、 毕业设计等文档。 二、系统所应用的核心技术——微服务 1、单体架构的应用 (1)什么是单体架构(Monolithic Architecture)的应用 在传统的软件应用开发中,基本上都是将一个软件项目相关的功能程序代码(包含有 系统的 DO/DAO,Service,UI 等所有逻辑)、资源文件、配置文件数据库等方面的内容 打包为一个 JAR 程序包或者多个 JAR 程序包文件(当然也可能为 WAR、EAR 或其它归档 格式的文件),以这样的方式发布的应用程序,一般称为单体架构的应用。如下示例图所 示为单体架构的应用结构关系示图,单体架构的应用可以是 PC 端的应用程序,也可以是 基于浏览器的 Web 应用或者移动端的 App 应用等形式。 第 2/27 页 (2)基于单体架构的应用所体现出的主要优点 首先,便于共享管理。由于在单个归档文件中包含所有的功能程序及相关的资源文 件,便于在团队之间以及不同的部署阶段之间共享,也方便管理。 其次,易于测试。单体应用一旦成功部署,所有的系统功能服务都能够正常地提供, 能够简化对它的测试过程。因为高度集中,没有附加的外部依赖,对单体应用的每项测试 都可以在部署完成后正常地开展。 最后,易于部署。只需要将它的单个归档文件(JAR、WAR、EAR)复制到发布的某 个系统目录中。全部的功能程序都在本地主机中或者 Web 服务器中,没有分布式的管理 调用的消耗。 (3)基于单体架构的应用所带来的主要缺点 1)系统将会越来越臃肿 随之而来的问题将是部署效率低。因为当越来越多的程序功能模块都集中在同一项目 相关的单个归档文件(JAR、WAR、EAR)时,整个系统将会越来越庞大变得越来越臃 肿,维护困难——对单体应用的编译打包、部署、测试的整个过程会非常耗时;系统的扩 展性也不够高:而且很难满足高并发应用环境下的业务需求。 2)系统资源无法隔离 由于在整个单体系统中的各个功能模块都依赖于同样的资源文件、系统库、内存等资 源,一旦某个功能模块对相关的资源如果使用不当,整个应用系统都有可能会崩溃。扩展 能力受限:单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。 3)系统开发成本高 系统项目在早期开发时,由小团队的开发人员进行协作修改代码,打包部署,更新发 第 3/27 页 布等方面的事情是可控的。但是随着团队人员不断地增加,如果此时仍然还是按照早期的 方法去开发。而如果测试阶段只要在系统中有某个功能有问题,就得重新编译、打包部 署。此外,所有的开发人员又都得参与其中,效率低下,开发成本极高。 基于单体架构的应用方式比较适合小型的项目,但是对于大型的应用项目不能再应用 单体架构,否则将使得系统的稳定性、扩展性灵活性都很难满足要求。因此,需要分布 式的应用开发模式下的微服务架构。 2、微服务架构风格 (1)什么是微服务架构风格 微服务架构(Microservice Architecture)风格是一种将一个单一应用程序(单体程序) 开发为一组小型服务的方法,每个服务都运行在自己的进程中,服务间的通信完全可以采 用轻量级的通信机制。这些服务围绕业务能力构建并且服务可用不同的编程语言开发使 用不同的数据存储技术。如下示例图所示为微服务架构的应用结构关系示图。 比如在某系统中有 Customer 模块 Product 模块,但是 Customer 模块 Product 模块 两者之间并没有直接的关系,而只是交互一些数据。因此,就可以将这两个模块分开,
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值