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;
}