|
|
@@ -8,10 +8,14 @@ import com.train.entity.TrainEnrollment;
|
|
|
import com.train.entity.TrainInvite;
|
|
|
import com.train.entity.TrainOrder;
|
|
|
import com.train.entity.TrainSalon;
|
|
|
+import com.train.entity.TrainSalonCheckin;
|
|
|
+import com.train.entity.TrainSalonFeedback;
|
|
|
import com.train.entity.TrainUser;
|
|
|
import com.train.mapper.TrainEnrollmentMapper;
|
|
|
import com.train.mapper.TrainInviteMapper;
|
|
|
import com.train.mapper.TrainOrderMapper;
|
|
|
+import com.train.mapper.TrainSalonCheckinMapper;
|
|
|
+import com.train.mapper.TrainSalonFeedbackMapper;
|
|
|
import com.train.mapper.TrainSalonMapper;
|
|
|
import com.train.mapper.TrainUserMapper;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
@@ -47,10 +51,17 @@ public class SalonController {
|
|
|
private CfcActivityMapper cfcActivityMapper;
|
|
|
@Resource
|
|
|
private TrainOrderMapper trainOrderMapper;
|
|
|
+ @Resource
|
|
|
+ private TrainSalonCheckinMapper trainSalonCheckinMapper;
|
|
|
+ @Resource
|
|
|
+ private TrainSalonFeedbackMapper trainSalonFeedbackMapper;
|
|
|
|
|
|
/** 占用名额的报名状态 */
|
|
|
private static final List<String> OCCUPY_STATUS = Arrays.asList("pending", "paid", "confirmed");
|
|
|
|
|
|
+ /** 可签到/反馈的报名状态(已支付或已确认) */
|
|
|
+ private static final List<String> ACTIVE_STATUS = Arrays.asList("paid", "confirmed");
|
|
|
+
|
|
|
/**
|
|
|
* 可报名沙龙期次列表(含剩余名额/价格)
|
|
|
*/
|
|
|
@@ -217,8 +228,128 @@ public class SalonController {
|
|
|
.orderByDesc(TrainOrder::getId)
|
|
|
.last("LIMIT 1"));
|
|
|
row.put("orderNo", order == null ? null : order.getOrderNo());
|
|
|
+ // 沙龙状态(前端据此决定是否显示签到入口)
|
|
|
+ row.put("salonStatus", s == null ? null : s.getStatus());
|
|
|
+ // 已签到标记
|
|
|
+ Long checkedIn = trainSalonCheckinMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<TrainSalonCheckin>()
|
|
|
+ .eq(TrainSalonCheckin::getUid, userId)
|
|
|
+ .eq(TrainSalonCheckin::getSalonId, e.getSalonId()));
|
|
|
+ row.put("checkedIn", checkedIn != null && checkedIn > 0);
|
|
|
+ // 已提交反馈标记
|
|
|
+ TrainSalonFeedback fb = e.getSalonId() == null ? null : trainSalonFeedbackMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<TrainSalonFeedback>()
|
|
|
+ .eq(TrainSalonFeedback::getUid, userId)
|
|
|
+ .eq(TrainSalonFeedback::getSalonId, e.getSalonId())
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ row.put("feedbackSubmitted", fb != null);
|
|
|
result.add(row);
|
|
|
}
|
|
|
return Result.success(result);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 沙龙现场签到(需已报名且已支付/已确认;幂等,重复签到返回 true)
|
|
|
+ */
|
|
|
+ @Operation(summary = "沙龙签到")
|
|
|
+ @PostMapping("/checkin")
|
|
|
+ public Result<Boolean> checkin(@RequestBody Map<String, Object> body,
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
+ if (body.get("salonId") == null) {
|
|
|
+ return Result.error("请选择沙龙期次");
|
|
|
+ }
|
|
|
+ Long salonId = Long.valueOf(body.get("salonId").toString());
|
|
|
+ TrainSalon salon = trainSalonMapper.selectById(salonId);
|
|
|
+ if (salon == null) {
|
|
|
+ return Result.error("沙龙不存在");
|
|
|
+ }
|
|
|
+ if (!"active".equals(salon.getStatus())) {
|
|
|
+ return Result.error("该沙龙未在进行中");
|
|
|
+ }
|
|
|
+ // 报名且已支付/已确认才可签到
|
|
|
+ Long enrolled = trainEnrollmentMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<TrainEnrollment>()
|
|
|
+ .eq(TrainEnrollment::getUid, userId)
|
|
|
+ .eq(TrainEnrollment::getSalonId, salonId)
|
|
|
+ .in(TrainEnrollment::getStatus, ACTIVE_STATUS));
|
|
|
+ if (enrolled == null || enrolled == 0) {
|
|
|
+ return Result.error("请先报名并完成支付");
|
|
|
+ }
|
|
|
+ // 幂等:已签到直接返回成功
|
|
|
+ Long existed = trainSalonCheckinMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<TrainSalonCheckin>()
|
|
|
+ .eq(TrainSalonCheckin::getUid, userId)
|
|
|
+ .eq(TrainSalonCheckin::getSalonId, salonId));
|
|
|
+ if (existed != null && existed > 0) {
|
|
|
+ return Result.success(true);
|
|
|
+ }
|
|
|
+ TrainSalonCheckin checkin = new TrainSalonCheckin();
|
|
|
+ checkin.setUid(userId);
|
|
|
+ checkin.setSalonId(salonId);
|
|
|
+ try {
|
|
|
+ trainSalonCheckinMapper.insert(checkin);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ // 唯一键冲突(并发重复签到)按已签到处理
|
|
|
+ }
|
|
|
+ return Result.success(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提交沙龙反馈(需已报名且已支付/已确认;评分 1-5;幂等,重复提交返回 true)
|
|
|
+ */
|
|
|
+ @Operation(summary = "提交沙龙反馈")
|
|
|
+ @PostMapping("/feedback")
|
|
|
+ public Result<Boolean> feedback(@RequestBody Map<String, Object> body,
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
+ if (body.get("salonId") == null) {
|
|
|
+ return Result.error("请选择沙龙期次");
|
|
|
+ }
|
|
|
+ Long salonId = Long.valueOf(body.get("salonId").toString());
|
|
|
+ TrainSalon salon = trainSalonMapper.selectById(salonId);
|
|
|
+ if (salon == null) {
|
|
|
+ return Result.error("沙龙不存在");
|
|
|
+ }
|
|
|
+ Long enrolled = trainEnrollmentMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<TrainEnrollment>()
|
|
|
+ .eq(TrainEnrollment::getUid, userId)
|
|
|
+ .eq(TrainEnrollment::getSalonId, salonId)
|
|
|
+ .in(TrainEnrollment::getStatus, ACTIVE_STATUS));
|
|
|
+ if (enrolled == null || enrolled == 0) {
|
|
|
+ return Result.error("请先报名参加该沙龙");
|
|
|
+ }
|
|
|
+ int rating = 5;
|
|
|
+ if (body.get("rating") != null) {
|
|
|
+ try {
|
|
|
+ rating = Integer.parseInt(body.get("rating").toString());
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ rating = 5;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (rating < 1 || rating > 5) {
|
|
|
+ return Result.error("评分需在 1-5 之间");
|
|
|
+ }
|
|
|
+ String comment = body.get("comment") == null ? "" : body.get("comment").toString().trim();
|
|
|
+ if (comment.length() > 500) {
|
|
|
+ comment = comment.substring(0, 500);
|
|
|
+ }
|
|
|
+ // 幂等:已提交直接返回成功
|
|
|
+ Long existed = trainSalonFeedbackMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<TrainSalonFeedback>()
|
|
|
+ .eq(TrainSalonFeedback::getUid, userId)
|
|
|
+ .eq(TrainSalonFeedback::getSalonId, salonId));
|
|
|
+ if (existed != null && existed > 0) {
|
|
|
+ return Result.success(true);
|
|
|
+ }
|
|
|
+ TrainSalonFeedback feedback = new TrainSalonFeedback();
|
|
|
+ feedback.setUid(userId);
|
|
|
+ feedback.setSalonId(salonId);
|
|
|
+ feedback.setRating(rating);
|
|
|
+ feedback.setComment(comment);
|
|
|
+ try {
|
|
|
+ trainSalonFeedbackMapper.insert(feedback);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ // 唯一键冲突(并发重复提交)按已提交处理
|
|
|
+ }
|
|
|
+ return Result.success(true);
|
|
|
+ }
|
|
|
}
|