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表需要更新签到连续天数,以及如果是第一次签到的话还要更新签到起始天数。