|
|
@@ -0,0 +1,365 @@
|
|
|
+package com.jzg.organization.service.impl;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.jzg.commons.core.base.BaseController;
|
|
|
+import com.jzg.commons.core.page.HttpResult;
|
|
|
+import com.jzg.commons.entity.po.SysUserSignIn;
|
|
|
+import com.jzg.commons.entity.po.SysUserSignInRule;
|
|
|
+import com.jzg.commons.entity.vo.LotteryActivityVO;
|
|
|
+import com.jzg.commons.entity.vo.LocalActivityTableVO;
|
|
|
+import com.jzg.commons.entity.vo.SignRuleVO;
|
|
|
+import com.jzg.organization.entity.dto.SignInDTO;
|
|
|
+import com.jzg.organization.entity.vo.SignInRecordVO;
|
|
|
+import com.jzg.organization.entity.vo.SignInStatisticsVO;
|
|
|
+import com.jzg.organization.entity.vo.SignInVO;
|
|
|
+import com.jzg.organization.mapper.SysUserSignInMapper;
|
|
|
+import com.jzg.organization.mapper.SysUserSignInRuleMapper;
|
|
|
+import com.jzg.organization.service.LotteryActivityService;
|
|
|
+import com.jzg.organization.service.SignInService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.temporal.TemporalAdjusters;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 签到服务实现类
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class SignInServiceImpl implements SignInService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SysUserSignInMapper signInMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SysUserSignInRuleMapper signInRuleMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private LotteryActivityService lotteryActivityService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private HttpServletRequest request;
|
|
|
+
|
|
|
+ @Value("${lottery.activity.scene:3}")
|
|
|
+ private Integer lotteryScene;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public HttpResult<SignInVO> signIn(SignInDTO dto) {
|
|
|
+ // 获取当前用户和租户信息
|
|
|
+ String tenantId = getTenantId(dto.getTenantId());
|
|
|
+ String userId = getUserId(dto.getUserId());
|
|
|
+
|
|
|
+ if (StrUtil.isBlank(tenantId) || StrUtil.isBlank(userId)) {
|
|
|
+ return HttpResult.error("用户未登录或登录信息异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
+
|
|
|
+ // 检查今日是否已签到
|
|
|
+ int count = signInMapper.countByDate(tenantId, userId, today);
|
|
|
+ if (count > 0) {
|
|
|
+ return HttpResult.error("今日已签到,请勿重复签到");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算连续签到天数
|
|
|
+ Integer continuousDays = calculateContinuousDays(tenantId, userId);
|
|
|
+
|
|
|
+ // 保存签到记录
|
|
|
+ SysUserSignIn signIn = new SysUserSignIn();
|
|
|
+ signIn.setTenantId(tenantId);
|
|
|
+ signIn.setUserId(userId);
|
|
|
+ signIn.setSignInDate(today);
|
|
|
+ signIn.setContinuousDays(continuousDays);
|
|
|
+ signIn.setSignInTime(LocalDateTime.now());
|
|
|
+ signInMapper.insert(signIn);
|
|
|
+
|
|
|
+ // 检查并触发签到规则
|
|
|
+ List<SignRuleVO> triggeredRules = checkAndTriggerSignRules(tenantId, userId, continuousDays);
|
|
|
+
|
|
|
+ // 统计本次获得的抽奖次数
|
|
|
+ int totalLotteryChance = triggeredRules.stream()
|
|
|
+ .filter(r -> StrUtil.isNotBlank(r.getSignLotteryNum()))
|
|
|
+ .mapToInt(r -> Integer.parseInt(r.getSignLotteryNum()))
|
|
|
+ .sum();
|
|
|
+
|
|
|
+ // 构建返回结果
|
|
|
+ SignInVO vo = new SignInVO();
|
|
|
+ vo.setSuccess(true);
|
|
|
+ vo.setSignInDate(today);
|
|
|
+ vo.setContinuousDays(continuousDays);
|
|
|
+ vo.setLotteryChance(totalLotteryChance);
|
|
|
+ vo.setTriggeredRules(triggeredRules);
|
|
|
+ vo.setSignInTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ String message = buildSuccessMessage(continuousDays, totalLotteryChance, triggeredRules);
|
|
|
+ vo.setMessage(message);
|
|
|
+
|
|
|
+ log.info("用户签到成功,tenantId={}, userId={}, continuousDays={}, lotteryChance={}",
|
|
|
+ tenantId, userId, continuousDays, totalLotteryChance);
|
|
|
+
|
|
|
+ return HttpResult.ok(message, vo);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查并触发可完成的签到规则
|
|
|
+ * 每个规则在活动期间只能完成一次
|
|
|
+ */
|
|
|
+ private List<SignRuleVO> checkAndTriggerSignRules(String tenantId, String userId, Integer continuousDays) {
|
|
|
+ List<SignRuleVO> triggeredRules = new ArrayList<>();
|
|
|
+
|
|
|
+ // 获取当前有效的活动
|
|
|
+ List<LotteryActivityVO> activities = lotteryActivityService.getActivitiesByScene(lotteryScene);
|
|
|
+
|
|
|
+ for (LotteryActivityVO activity : activities) {
|
|
|
+ // 检查活动是否有效
|
|
|
+ if (!lotteryActivityService.isActivityValid(activity)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (activity.getLocalActivityTables() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取用户已完成的规则
|
|
|
+ List<SysUserSignInRule> completedRules = signInRuleMapper.selectCompletedRules(
|
|
|
+ tenantId, userId, activity.getId());
|
|
|
+ Set<String> completedSignNums = completedRules.stream()
|
|
|
+ .map(SysUserSignInRule::getRuleSignNum)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ // 遍历所有本地活动表
|
|
|
+ for (LocalActivityTableVO table : activity.getLocalActivityTables()) {
|
|
|
+ // 只处理 type=7 的签到规则
|
|
|
+ if (!"7".equals(table.getType()) || table.getSignRules() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查每个签到规则
|
|
|
+ for (SignRuleVO rule : table.getSignRules()) {
|
|
|
+ if (StrUtil.isBlank(rule.getSignNum())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer requiredDays = Integer.parseInt(rule.getSignNum());
|
|
|
+
|
|
|
+ // 判断是否满足条件且未完成过
|
|
|
+ if (continuousDays >= requiredDays
|
|
|
+ && !completedSignNums.contains(rule.getSignNum())) {
|
|
|
+
|
|
|
+ // 记录规则完成
|
|
|
+ completeSignRule(tenantId, userId, activity.getId(),
|
|
|
+ table.getId(), rule);
|
|
|
+
|
|
|
+ triggeredRules.add(rule);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return triggeredRules;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录规则完成
|
|
|
+ */
|
|
|
+ private void completeSignRule(String tenantId, String userId, String activityId,
|
|
|
+ String tableId, SignRuleVO rule) {
|
|
|
+ // 先查询是否已存在记录(防重入)
|
|
|
+ SysUserSignInRule exist = signInRuleMapper.selectByRule(
|
|
|
+ tenantId, userId, activityId, rule.getSignNum());
|
|
|
+
|
|
|
+ if (exist != null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ SysUserSignInRule record = new SysUserSignInRule();
|
|
|
+ record.setTenantId(tenantId);
|
|
|
+ record.setUserId(userId);
|
|
|
+ record.setActivityId(activityId);
|
|
|
+ record.setLocalActivityTableId(tableId);
|
|
|
+ record.setRuleSignNum(rule.getSignNum());
|
|
|
+ record.setLotteryNum(StrUtil.isNotBlank(rule.getSignLotteryNum())
|
|
|
+ ? Integer.parseInt(rule.getSignLotteryNum()) : 0);
|
|
|
+ record.setStatus(1);
|
|
|
+ record.setReceiveTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ signInRuleMapper.insert(record);
|
|
|
+
|
|
|
+ log.info("签到规则完成,tenantId={}, userId={}, activityId={}, ruleSignNum={}, lotteryNum={}",
|
|
|
+ tenantId, userId, activityId, rule.getSignNum(), record.getLotteryNum());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算连续签到天数
|
|
|
+ */
|
|
|
+ private Integer calculateContinuousDays(String tenantId, String userId) {
|
|
|
+ SysUserSignIn latest = signInMapper.selectLatestByUser(tenantId, userId);
|
|
|
+
|
|
|
+ if (latest == null) {
|
|
|
+ return 1; // 首次签到
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDate yesterday = LocalDate.now().minusDays(1);
|
|
|
+
|
|
|
+ // 如果上次签到是昨天,则连续天数+1
|
|
|
+ if (latest.getSignInDate().isEqual(yesterday)) {
|
|
|
+ return latest.getContinuousDays() + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果上次签到是今天(重复签到,理论上不会走到这里)
|
|
|
+ if (latest.getSignInDate().isEqual(LocalDate.now())) {
|
|
|
+ return latest.getContinuousDays();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 断签了,从1开始
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建成功消息
|
|
|
+ */
|
|
|
+ private String buildSuccessMessage(Integer continuousDays, Integer lotteryChance, List<SignRuleVO> rules) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("签到成功!连续签到").append(continuousDays).append("天");
|
|
|
+
|
|
|
+ if (lotteryChance > 0 && CollUtil.isNotEmpty(rules)) {
|
|
|
+ sb.append(",恭喜完成");
|
|
|
+ for (int i = 0; i < rules.size(); i++) {
|
|
|
+ SignRuleVO rule = rules.get(i);
|
|
|
+ if (i > 0) {
|
|
|
+ sb.append("、");
|
|
|
+ }
|
|
|
+ sb.append(rule.getSignNum()).append("天签到任务");
|
|
|
+ }
|
|
|
+ sb.append(",获得").append(lotteryChance).append("次抽奖机会");
|
|
|
+ }
|
|
|
+
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public HttpResult<SignInStatisticsVO> getStatistics(String tenantId, String userId) {
|
|
|
+ String tId = getTenantId(tenantId);
|
|
|
+ String uId = getUserId(userId);
|
|
|
+
|
|
|
+ if (StrUtil.isBlank(tId) || StrUtil.isBlank(uId)) {
|
|
|
+ return HttpResult.error("用户未登录或登录信息异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
+ LocalDate monthStart = today.with(TemporalAdjusters.firstDayOfMonth());
|
|
|
+
|
|
|
+ // 查询本月签到记录
|
|
|
+ List<SysUserSignIn> monthRecords = signInMapper.selectListByUser(tId, uId, monthStart, today);
|
|
|
+
|
|
|
+ // 查询历史最高连续天数
|
|
|
+ Integer maxContinuousDays = signInMapper.selectMaxContinuousDays(tId, uId);
|
|
|
+ if (maxContinuousDays == null) {
|
|
|
+ maxContinuousDays = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询今日是否已签到
|
|
|
+ int todayCount = signInMapper.countByDate(tId, uId, today);
|
|
|
+
|
|
|
+ // 计算当前连续天数
|
|
|
+ Integer currentContinuous = 0;
|
|
|
+ if (CollUtil.isNotEmpty(monthRecords)) {
|
|
|
+ SysUserSignIn latest = monthRecords.get(0);
|
|
|
+ if (latest.getSignInDate().isEqual(today) || latest.getSignInDate().isEqual(today.minusDays(1))) {
|
|
|
+ currentContinuous = latest.getContinuousDays();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询累计签到天数
|
|
|
+ Integer totalDays = signInMapper.selectCount(
|
|
|
+ Wrappers.<SysUserSignIn>lambdaQuery()
|
|
|
+ .eq(SysUserSignIn::getTenantId, tId)
|
|
|
+ .eq(SysUserSignIn::getUserId, uId)
|
|
|
+ .eq(SysUserSignIn::getIsDelete, 0)
|
|
|
+ ).intValue();
|
|
|
+
|
|
|
+ SignInStatisticsVO vo = new SignInStatisticsVO();
|
|
|
+ vo.setMonthTotalDays(monthRecords.size());
|
|
|
+ vo.setCurrentContinuousDays(currentContinuous);
|
|
|
+ vo.setMaxContinuousDays(maxContinuousDays);
|
|
|
+ vo.setTotalDays(totalDays);
|
|
|
+ vo.setTodaySigned(todayCount > 0);
|
|
|
+ vo.setRecentSignDates(monthRecords.stream()
|
|
|
+ .map(SysUserSignIn::getSignInDate)
|
|
|
+ .collect(Collectors.toList()));
|
|
|
+
|
|
|
+ return HttpResult.ok(vo);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public HttpResult<List<SignInRecordVO>> getRecords(String tenantId, String userId, Integer days) {
|
|
|
+ String tId = getTenantId(tenantId);
|
|
|
+ String uId = getUserId(userId);
|
|
|
+
|
|
|
+ if (StrUtil.isBlank(tId) || StrUtil.isBlank(uId)) {
|
|
|
+ return HttpResult.error("用户未登录或登录信息异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (days == null || days <= 0) {
|
|
|
+ days = 30;
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDate endDate = LocalDate.now();
|
|
|
+ LocalDate startDate = endDate.minusDays(days - 1);
|
|
|
+
|
|
|
+ List<SysUserSignIn> records = signInMapper.selectListByUser(tId, uId, startDate, endDate);
|
|
|
+
|
|
|
+ if (CollUtil.isEmpty(records)) {
|
|
|
+ return HttpResult.ok(Collections.emptyList());
|
|
|
+ }
|
|
|
+
|
|
|
+ List<SignInRecordVO> voList = records.stream().map(r -> {
|
|
|
+ SignInRecordVO vo = new SignInRecordVO();
|
|
|
+ vo.setId(r.getId());
|
|
|
+ vo.setSignInDate(r.getSignInDate());
|
|
|
+ vo.setContinuousDays(r.getContinuousDays());
|
|
|
+ vo.setSignInTime(r.getSignInTime());
|
|
|
+ return vo;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+
|
|
|
+ return HttpResult.ok(voList);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取租户ID
|
|
|
+ */
|
|
|
+ private String getTenantId(String dtoTenantId) {
|
|
|
+ if (StrUtil.isNotBlank(dtoTenantId)) {
|
|
|
+ return dtoTenantId;
|
|
|
+ }
|
|
|
+ // 从请求头或上下文中获取
|
|
|
+ Object tenantObj = request.getAttribute("tenantId");
|
|
|
+ return tenantObj != null ? tenantObj.toString() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户ID
|
|
|
+ */
|
|
|
+ private String getUserId(String dtoUserId) {
|
|
|
+ if (StrUtil.isNotBlank(dtoUserId)) {
|
|
|
+ return dtoUserId;
|
|
|
+ }
|
|
|
+ // 从请求头或上下文中获取
|
|
|
+ Object userObj = request.getAttribute("userId");
|
|
|
+ return userObj != null ? userObj.toString() : null;
|
|
|
+ }
|
|
|
+}
|