新增功能:团课签到前验证是否到店签到

This commit was merged in pull request #33.
This commit is contained in:
2026-06-18 10:48:23 +08:00
parent 0b2146f237
commit 8da58a8f51
2 changed files with 32 additions and 12 deletions
+4 -1
View File
@@ -648,7 +648,9 @@
| 4 | 当前时间早于开课前2小时 | `未到签到时间,请在开课前2小时内签到` | | 4 | 当前时间早于开课前2小时 | `未到签到时间,请在开课前2小时内签到` |
| 5 | 当前时间晚于团课结束时间 | `团课已结束,无法签到` | | 5 | 当前时间晚于团课结束时间 | `团课已结束,无法签到` |
| 6 | 课程当前人数已达上限 | `课程已满员,无法签到` | | 6 | 课程当前人数已达上限 | `课程已满员,无法签到` |
| 7 | 用户未预约此课程 | `您未预约此课程,无法签到` | | 7 | 用户今日无到店签到记录 | `请先完成到店签到` |
| 8 | 到店签到状态非SUCCESS | `到店签到未成功,请重新签到` |
| 9 | 用户未预约此课程 | `您未预约此课程,无法签到` |
| - | 请求体为空 | `请求体不能为空` | | - | 请求体为空 | `请求体不能为空` |
| - | 请求体缺少courseId | `courseId不能为空` | | - | 请求体缺少courseId | `courseId不能为空` |
@@ -1664,6 +1666,7 @@
- 团课状态不为"已结束"(含已过结束时间) - 团课状态不为"已结束"(含已过结束时间)
- 当前时间在开课前2小时内(签到时间窗口) - 当前时间在开课前2小时内(签到时间窗口)
- 课程当前人数未达到最大人数上限 - 课程当前人数未达到最大人数上限
- 用户今日已成功到店签到(查询 sign_in_record 表当日 SUCCESS 记录)
- 用户已预约该课程(有效预约) - 用户已预约该课程(有效预约)
4. **删除团课**:采用软删除机制,数据保留可恢复 4. **删除团课**:采用软删除机制,数据保留可恢复
@@ -28,6 +28,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@@ -49,6 +50,7 @@ public class GroupCourseService implements IGroupCourseService {
private final RedisUtil redisUtil; private final RedisUtil redisUtil;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final GroupCourseStateMachine stateMachine; private final GroupCourseStateMachine stateMachine;
private final DatabaseClient databaseClient;
private static final String CACHE_KEY_PREFIX = "group_course:page:"; private static final String CACHE_KEY_PREFIX = "group_course:page:";
private static final String CACHE_KEY_ID_PREFIX = "group_course:id:"; private static final String CACHE_KEY_ID_PREFIX = "group_course:id:";
@@ -65,7 +67,8 @@ public class GroupCourseService implements IGroupCourseService {
MemberCardRepository memberCardRepository, MemberCardRepository memberCardRepository,
RedisUtil redisUtil, RedisUtil redisUtil,
ObjectMapper objectMapper, ObjectMapper objectMapper,
GroupCourseStateMachine stateMachine){ GroupCourseStateMachine stateMachine,
DatabaseClient databaseClient){
this.groupCourseRepository = groupCourseRepository; this.groupCourseRepository = groupCourseRepository;
this.bookingRepository = bookingRepository; this.bookingRepository = bookingRepository;
this.groupCourseTypeRepository = groupCourseTypeRepository; this.groupCourseTypeRepository = groupCourseTypeRepository;
@@ -75,6 +78,7 @@ public class GroupCourseService implements IGroupCourseService {
this.redisUtil = redisUtil; this.redisUtil = redisUtil;
this.objectMapper = objectMapper; this.objectMapper = objectMapper;
this.stateMachine = stateMachine; this.stateMachine = stateMachine;
this.databaseClient = databaseClient;
} }
@Override @Override
@@ -506,16 +510,29 @@ public class GroupCourseService implements IGroupCourseService {
return Mono.error(new RuntimeException("课程已满员,无法签到")); return Mono.error(new RuntimeException("课程已满员,无法签到"));
} }
// 校验5:用户已预约此课程(有效预约,状态为0-已预约 // 校验5:用户今日是否已到店签到(直接查询sign_in_record表
return bookingRepository.findValidBooking(courseId, memberId) LocalDateTime todayStart = LocalDateTime.now().toLocalDate().atStartOfDay();
.switchIfEmpty(Mono.error(new RuntimeException("您未预约此课程,无法签到"))) LocalDateTime todayEnd = todayStart.plusDays(1);
.flatMap(booking -> { return databaseClient.sql("SELECT sign_in_status FROM sign_in_record WHERE member_id = :memberId AND sign_in_time >= :startTime AND sign_in_time < :endTime AND is_delete = false ORDER BY sign_in_time DESC LIMIT 1")
// 更新课程当前人数 .bind("memberId", memberId)
return groupCourseRepository.updateCurrentMembers(courseId, 1) .bind("startTime", todayStart)
.flatMap(updatedCourse -> { .bind("endTime", todayEnd)
// 更新预约状态为已出席 .map(row -> row.get("sign_in_status", String.class))
return bookingRepository.updateStatus(booking.getId(), "2") .one()
.thenReturn(updatedCourse); .switchIfEmpty(Mono.error(new RuntimeException("请先完成到店签到")))
.flatMap(status -> {
if (!"SUCCESS".equals(status)) {
return Mono.error(new RuntimeException("到店签到未成功,请重新签到"));
}
// 校验6:用户已预约此课程(有效预约,状态为0-已预约)
return bookingRepository.findValidBooking(courseId, memberId)
.switchIfEmpty(Mono.error(new RuntimeException("您未预约此课程,无法签到")))
.flatMap(booking -> {
return groupCourseRepository.updateCurrentMembers(courseId, 1)
.flatMap(updatedCourse -> {
return bookingRepository.updateStatus(booking.getId(), "2")
.thenReturn(updatedCourse);
});
}); });
}); });
}) })