java 用户实现签到以及补签功能

import com.seerbigdata.business.achievement.domain.param.SignInSupplementParam;
import com.seerbigdata.business.achievement.domain.vo.UserSignInInfoVo;
import com.seerbigdata.business.achievement.server.IUserSignInService;
import com.seerbigdata.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping(value = "/user/sginIn", produces = MediaType.APPLICATION_JSON_VALUE)
@Api(value = "用户签到管理", tags = "用户签到管理")
@RequiredArgsConstructor
@Slf4j
public class UserSginInController {

    @Autowired
    private IUserSignInService signInService;

    @PostMapping("/sign-in")
    @ApiOperation(value = "用户签到", notes = "用户签到")
    public R<Boolean> signIn() {
        boolean result = signInService.signIn();
        return R.success(result);
    }

    /**
     * 判断今天是否签到
     */
    @GetMapping("/is-sign-in")
    @ApiOperation(value = "判断今天是否签到", notes = "判断今天是否签到")
    public R<Boolean> isSignIn() {
        boolean result = signInService.isSignInToday(null);
        return R.success(result);
    }

    @GetMapping("/getUserSignInInfo/consecutiveDays")
    @ApiOperation(value = "获取用户连续签到信息-天数", notes = "获取用户连续签到信息-天数")
    public R<Integer> getUserSignInInfoToday() {
        Integer result = signInService.getUserSignInInfoToday(null);
        return R.success(result);
    }

    @GetMapping("/getUserSignInInfo/month")
    @ApiOperation(value = "获取用户签到信息-当月-前后15天", notes = "获取用户签到信息-当月-前后15天")
    public R<List<UserSignInInfoVo>> getUserSignInInfo() {
        List<UserSignInInfoVo> result = signInService.getUserSignInInfoMonth(null);
        return R.success(result);
    }

    @GetMapping("/getUserSignInInfo/halfAYear")
    @ApiOperation(value = "获取用户签到信息-半年", notes = "获取用户签到信息-半年")
    public R<List<UserSignInInfoVo>> getUserSignInInfoHalfAYear() {
        List<UserSignInInfoVo> result = signInService.getUserSignInInfoHalfAYear(null);
        return R.success(result);
    }

    /**
     * 补签到功能
     */
    @PostMapping("/supplement")
    @ApiOperation(value = "补签到功能-自动补最近一天", notes = "补签到功能-自动补最近一天")
    public R<Boolean> supplementSignIn() {
        boolean result = signInService.supplementSignIn(null, null);
        return R.success(result);
    }

    /**
     * 补签到功能
     */
    @PostMapping("/supplement/byTime")
    @ApiOperation(value = "补签到功能-固定日期", notes = "补签到功能-固定日期")
    public R<Boolean> supplementSignIn(@RequestBody SignInSupplementParam param) {
        boolean result = signInService.supplementSignIn(null, param.getSupplementDate());
        return R.success(result);
    }
}
import com.baomidou.mybatisplus.extension.service.IService;
import com.seerbigdata.business.achievement.domain.entity.UserAchievement;
import com.seerbigdata.business.achievement.domain.entity.UserSignIn;
import com.seerbigdata.business.achievement.domain.vo.UserSignInInfoVo;

import java.time.LocalDate;
import java.util.List;

/**
 * @author jax
 */
public interface IUserSignInService extends IService<UserSignIn> {
    boolean signIn();

    boolean isSignInToday(String userId);

    List<UserSignInInfoVo> getUserSignInInfoMonth(String userId);

    List<UserSignInInfoVo> getUserSignInInfoHalfAYear(String userId);

    boolean supplementSignIn(String userId, LocalDate supplementDate);

    Integer getUserSignInInfoToday(String userId);
}

数据落入缓存(Redis)的操作自己补充

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seerbigdata.business.achievement.domain.entity.UserSignIn;
import com.seerbigdata.business.achievement.domain.vo.UserSignInInfoVo;
import com.seerbigdata.business.achievement.mapper.UserSignInMapper;
import com.seerbigdata.business.achievement.server.IUserSignInService;
import com.seerbigdata.common.exception.GlobalException;
import com.seerbigdata.common.utils.SecurityUtils;
import com.seerbigdata.common.utils.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jax
 */
@Service
public class UserSignInServiceImpl extends ServiceImpl<UserSignInMapper, UserSignIn> implements IUserSignInService {
    @Override
    public boolean signIn() {
        String userId = SecurityUtils.getUserId();
        boolean signInToday = this.isSignInToday(userId);
        if (signInToday) {
            throw new GlobalException("今天已经签到过了");
        }
        // 获取今天的日期Date格式
        LocalDate today = LocalDate.now();
        // 检查昨日是否签到
        UserSignIn yesterdaySignInRecord = this.lambdaQuery().eq(UserSignIn::getUserId, userId)
                .eq(UserSignIn::getSignInDate, today.minusDays(1)).one();

        int consecutiveDays = 1;
        if (Objects.nonNull(yesterdaySignInRecord)) {
            // 获取昨日的连续签到天数
            int yesterdayConsecutiveDays = yesterdaySignInRecord.getConsecutiveDays();
            consecutiveDays = yesterdayConsecutiveDays + 1;
        }
        UserSignIn userSignIn = new UserSignIn();
        userSignIn.setUserId(userId);
        userSignIn.setSignInDate(java.sql.Date.valueOf(today));
        userSignIn.setConsecutiveDays(consecutiveDays);
        // 保存签到记录
        return this.save(userSignIn);
    }

    @Override
    public boolean isSignInToday(String userId) {
        if (StringUtils.isEmpty(userId)) {
           userId = SecurityUtils.getUserId();
        }
        if (StringUtils.isEmpty(userId)) {
            throw new GlobalException("用户未登录");
        }
        // 获取今天的日期Date格式
        LocalDate today = LocalDate.now();
        // 查询今天是否已经签到
        return this.lambdaQuery()
        .eq(UserSignIn::getUserId, userId)
        .eq(UserSignIn::getSignInDate, today)
        .exists();
    }

    /**
     * 获取用户最近一个月的签到情况
     */
    @Override
    public List<UserSignInInfoVo> getUserSignInInfoMonth(String userId) {

        // 获取当前日期和前后15天的日期范围
        LocalDate today = LocalDate.now();
        LocalDate startDate = today.minusDays(15);
        LocalDate endDate = today.plusDays(15);
        return this.getUserSignInInfo(userId, startDate, endDate);
    }

    /**
     * 获取用户近期半年签到情况
     */
    @Override
    public List<UserSignInInfoVo> getUserSignInInfoHalfAYear(String userId) {

        // 获取用户近期半年签到情况
        LocalDate today = LocalDate.now();
        LocalDate startDate = today.minusMonths(6);
        return this.getUserSignInInfo(userId, startDate, today);
    }

    public List<UserSignInInfoVo> getUserSignInInfo(String userId, LocalDate startDate, LocalDate endDate) {
        // 如果用户ID为空,从安全上下文中获取用户ID
        if (StringUtils.isEmpty(userId)) {
            userId = SecurityUtils.getUserId();
        }
        if (StringUtils.isEmpty(userId)) {
            throw new GlobalException("用户未登录");
        }

        // 查询用户在指定日期范围内的签到记录
        List<UserSignIn> signInRecords = this.lambdaQuery()
                .eq(UserSignIn::getUserId, userId)
                .between(UserSignIn::getSignInDate, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate))
                .orderByAsc(UserSignIn::getSignInDate)
                .list();

        // 将签到记录映射到日期对应的 Map<LocalDate, UserSignIn>
        Map<LocalDate, UserSignIn> signInRecordMap = signInRecords.stream()
                .collect(Collectors.toMap(
                        record -> convertToLocalDate(record.getSignInDate()), // Conversion method
                        record -> record
                ));

        // 创建包含指定日期范围的日期集合
        List<LocalDate> dateRange = createDateRange(startDate, endDate);

        // 创建 UserSignInInfoVo 对象列表,标记每一天是否签到
        return dateRange.stream()
                .map(date -> {
                    UserSignInInfoVo infoVo = new UserSignInInfoVo();
                    infoVo.setDate(date.toString());
                    infoVo.setSignIn(signInRecordMap.containsKey(date));
                    return infoVo;
                })
                .collect(Collectors.toList());
    }

    private List<LocalDate> createDateRange(LocalDate startDate, LocalDate endDate) {
        List<LocalDate> dateRange = new ArrayList<>();
        LocalDate currentDate = startDate;
        while (!currentDate.isAfter(endDate)) {
            dateRange.add(currentDate);
            currentDate = currentDate.plusDays(1);
        }
        return dateRange;
    }

    /**
     * 将 java.util.Date 转换为 LocalDate
     */
    private LocalDate convertToLocalDate(Date date) {
        return Instant.ofEpochMilli(date.getTime())
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
    }


    /**
     * 补卡功能 - 用户补签并更新后续签到记录
     */
    @Override
    public boolean supplementSignIn(String userId, LocalDate supplementDate) {
        if (StringUtils.isEmpty(userId)) {
            userId = SecurityUtils.getUserId();
        }
        if (StringUtils.isEmpty(userId)) {
            throw new GlobalException("用户未登录");
        }
        // 如果supplementDate为空获取最近没有签到的一天
        if (supplementDate == null) {
            UserSignIn lastSignInRecord = this.lambdaQuery()
                    .eq(UserSignIn::getUserId, userId)
                    .apply("DATE(sign_in_date) < CURDATE()")
                    .orderByAsc(UserSignIn::getSignInDate)
                    .last("LIMIT 1")
                    .one(); // Use .one() to fetch a single record

            if (lastSignInRecord != null) {
                // Convert java.sql.Date to LocalDate
                Date signInDate = lastSignInRecord.getSignInDate();
                LocalDate lastSignInDate = signInDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                supplementDate = lastSignInDate.minusDays(1);
            } else {
                // 没有签到记录补昨天的
                // Handle the case where there are no prior sign-in records
                supplementDate = LocalDate.now().minusDays(1); // Default to current date or any other logic you need
            }
        }

        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 校验补卡日期不能超过今天且在一个月之内
        if (supplementDate.isAfter(today) || supplementDate.isBefore(today.minusMonths(1))) {
            throw new GlobalException("补卡日期无效,只能在最近一个月内补卡");
        }

        // 查询该日期是否已经签到
        boolean hasSignedInOnDate = this.lambdaQuery()
                .eq(UserSignIn::getUserId, userId)
                .apply("DATE(sign_in_date) = {0}", supplementDate)
                .exists();

        if (hasSignedInOnDate) {
            throw new GlobalException("该日期已经签到过,无法补卡");
        }

        // 插入补卡记录
        UserSignIn supplementSignIn = new UserSignIn();
        supplementSignIn.setUserId(userId);
        supplementSignIn.setSignInDate(java.sql.Date.valueOf(supplementDate));
        // 查询上一天的连续签到日
        UserSignIn previousSignIn = this.lambdaQuery()
                .eq(UserSignIn::getUserId, userId)
                .apply("DATE(sign_in_date) = {0}", supplementDate.minusDays(1))
                .one();

        if (Objects.isNull(previousSignIn)) {
            supplementSignIn.setConsecutiveDays(1); // 初始设置为1天
        } else {
            supplementSignIn.setConsecutiveDays(previousSignIn.getConsecutiveDays() + 1);
        }
        this.save(supplementSignIn);

        // 更新从补卡日期到今天的签到记录的连续天数
        List<UserSignIn> signInRecords = this.lambdaQuery()
                .eq(UserSignIn::getUserId, userId)
                .ge(UserSignIn::getSignInDate, java.sql.Date.valueOf(supplementDate))
                .orderByAsc(UserSignIn::getSignInDate)
                .list();

        LocalDate lastSignInDate = null;
        int consecutiveDays = 0;
        if (!Objects.isNull(previousSignIn)) {
            consecutiveDays = previousSignIn.getConsecutiveDays();
        }
        for (UserSignIn signInRecord : signInRecords) {
            LocalDate signInDate = convertToLocalDate(signInRecord.getSignInDate());
            // 判断是否是连续的签到日期
            if (lastSignInDate == null || signInDate.isEqual(lastSignInDate.plusDays(1))) {
                consecutiveDays++;
            } else {
                consecutiveDays = 1; // 重置连续天数
            }

            signInRecord.setConsecutiveDays(consecutiveDays);
            lastSignInDate = signInDate;
        }

        // 批量更新签到记录
        this.updateBatchById(signInRecords);

        return true;
    }

    /**
     * 获取用户连续签到天数
     * @param userId
     * @return
     */
    @Override
    public Integer getUserSignInInfoToday(String userId) {
        if (StringUtils.isEmpty(userId)) {
            userId = SecurityUtils.getUserId();
        }
        if (StringUtils.isEmpty(userId)) {
            throw new GlobalException("用户未登录");
        }
        if (isSignInToday(userId)) {
            return this.lambdaQuery()
                    .eq(UserSignIn::getUserId, userId)
                    .eq(UserSignIn::getSignInDate, java.sql.Date.valueOf(LocalDate.now()))
                    .one()
                    .getConsecutiveDays();
        } else {
            // 查询昨天的签到数量
            UserSignIn yesterdaySignIn = this.lambdaQuery()
                    .eq(UserSignIn::getUserId, userId)
                    .eq(UserSignIn::getSignInDate, java.sql.Date.valueOf(LocalDate.now().minusDays(1)))
                    .one();
            if (Objects.isNull(yesterdaySignIn)) {
                return 0;
            } else {
                return yesterdaySignIn.getConsecutiveDays();
            }
        }

    }
}
import com.baomidou.mybatisplus.annotation.TableName;
import com.seerbigdata.common.core.domain.BaseEntity;
import lombok.*;

import java.util.Date;

/**
 * 用户签到表
 *
 * @author : jax
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Builder
@TableName("busi_user_sign_in")
public class UserSignIn extends BaseEntity {

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = -1164281569112323239L;

    /**
     * id
     */
    private String id;

    /**
     * 用户ID
     */
    private String userId;

    /**
     * 签到日期
     */
    private Date signInDate;

    /**
     * 连续签到天数
     */
    private Integer consecutiveDays;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值