diff --git a/backend-java/src/main/java/com/competition/modules/biz/contest/controller/ContestRegistrationController.java b/backend-java/src/main/java/com/competition/modules/biz/contest/controller/ContestRegistrationController.java index 11990d6..85a4b1d 100644 --- a/backend-java/src/main/java/com/competition/modules/biz/contest/controller/ContestRegistrationController.java +++ b/backend-java/src/main/java/com/competition/modules/biz/contest/controller/ContestRegistrationController.java @@ -37,7 +37,9 @@ public class ContestRegistrationController { @RequirePermission("contest:read") @Operation(summary = "获取报名统计") public Result> getStats(@RequestParam(required = false) Long contestId) { - return Result.success(registrationService.getStats(contestId, SecurityUtil.getCurrentTenantId())); + Long tenantId = SecurityUtil.getCurrentTenantId(); + boolean isSuperAdmin = SecurityUtil.isSuperAdmin(); + return Result.success(registrationService.getStats(contestId, tenantId, isSuperAdmin)); } @GetMapping diff --git a/backend-java/src/main/java/com/competition/modules/biz/contest/service/IContestRegistrationService.java b/backend-java/src/main/java/com/competition/modules/biz/contest/service/IContestRegistrationService.java index f8b8a3e..a0f4064 100644 --- a/backend-java/src/main/java/com/competition/modules/biz/contest/service/IContestRegistrationService.java +++ b/backend-java/src/main/java/com/competition/modules/biz/contest/service/IContestRegistrationService.java @@ -15,7 +15,7 @@ public interface IContestRegistrationService extends IService> findAll(QueryRegistrationDto dto, Long tenantId, boolean isSuperTenant); - Map getStats(Long contestId, Long tenantId); + Map getStats(Long contestId, Long tenantId, boolean isSuperAdmin); Map findDetail(Long id, Long tenantId); diff --git a/backend-java/src/main/java/com/competition/modules/biz/contest/service/impl/ContestRegistrationServiceImpl.java b/backend-java/src/main/java/com/competition/modules/biz/contest/service/impl/ContestRegistrationServiceImpl.java index 72f0545..c0b5dfb 100644 --- a/backend-java/src/main/java/com/competition/modules/biz/contest/service/impl/ContestRegistrationServiceImpl.java +++ b/backend-java/src/main/java/com/competition/modules/biz/contest/service/impl/ContestRegistrationServiceImpl.java @@ -123,20 +123,26 @@ public class ContestRegistrationServiceImpl extends ServiceImpl getStats(Long contestId, Long tenantId) { - log.info("获取报名统计,赛事ID:{}", contestId); + public Map getStats(Long contestId, Long tenantId, boolean isSuperAdmin) { + log.info("获取报名统计,赛事ID:{},租户ID:{},超管:{}", contestId, tenantId, isSuperAdmin); + // 非超管需要按租户过滤,与列表查询保持一致 LambdaQueryWrapper baseWrapper = new LambdaQueryWrapper<>(); if (contestId != null) { baseWrapper.eq(BizContestRegistration::getContestId, contestId); } - + if (!isSuperAdmin && tenantId != null) { + baseWrapper.eq(BizContestRegistration::getTenantId, tenantId); + } long total = count(baseWrapper); LambdaQueryWrapper pendingWrapper = new LambdaQueryWrapper<>(); if (contestId != null) { pendingWrapper.eq(BizContestRegistration::getContestId, contestId); } + if (!isSuperAdmin && tenantId != null) { + pendingWrapper.eq(BizContestRegistration::getTenantId, tenantId); + } pendingWrapper.eq(BizContestRegistration::getRegistrationState, "pending"); long pending = count(pendingWrapper); @@ -144,6 +150,9 @@ public class ContestRegistrationServiceImpl extends ServiceImpl() - .eq(BizContestWork::getRegistrationId, reg.getId())); + .eq(BizContestWork::getRegistrationId, reg.getId()) + .eq(BizContestWork::getIsLatest, true)); result.put("hasSubmittedWork", workCount > 0); result.put("workCount", workCount); @@ -318,6 +320,30 @@ public class PublicActivityService { throw new BusinessException(400, "未报名或报名未通过"); } + // 查询活动提交规则 + BizContest contest = contestMapper.selectById(contestId); + String submitRule = contest != null ? contest.getSubmitRule() : "once"; + + // 查询已有作品 + BizContestWork existingWork = contestWorkMapper.selectOne( + new LambdaQueryWrapper() + .eq(BizContestWork::getContestId, contestId) + .eq(BizContestWork::getRegistrationId, reg.getId()) + .eq(BizContestWork::getIsLatest, true) + .orderByDesc(BizContestWork::getVersion) + .last("LIMIT 1")); + + if (existingWork != null) { + if ("once".equals(submitRule)) { + throw new BusinessException(400, "该活动仅允许提交一次作品"); + } + // resubmit 模式:将旧作品标记为非最新 + existingWork.setIsLatest(false); + contestWorkMapper.updateById(existingWork); + } + + int nextVersion = (existingWork != null) ? existingWork.getVersion() + 1 : 1; + BizContestWork work = new BizContestWork(); work.setContestId(contestId); work.setRegistrationId(reg.getId()); @@ -328,7 +354,7 @@ public class PublicActivityService { work.setSubmitterUserId(userId); work.setStatus("submitted"); work.setSubmitTime(LocalDateTime.now()); - work.setVersion(1); + work.setVersion(nextVersion); work.setIsLatest(true); if (dto.get("userWorkId") != null) { work.setUserWorkId(Long.valueOf(dto.get("userWorkId").toString())); diff --git a/frontend/src/api/public.ts b/frontend/src/api/public.ts index d3e1f0b..5c1dcfb 100644 --- a/frontend/src/api/public.ts +++ b/frontend/src/api/public.ts @@ -214,6 +214,7 @@ export interface PublicActivity { registerEndTime: string submitStartTime: string submitEndTime: string + submitRule: string reviewStartTime: string reviewEndTime: string organizers: any diff --git a/frontend/src/views/contests/Index.vue b/frontend/src/views/contests/Index.vue index d69f390..665996d 100644 --- a/frontend/src/views/contests/Index.vue +++ b/frontend/src/views/contests/Index.vue @@ -155,7 +155,6 @@ diff --git a/frontend/src/views/contests/components/AddJudgeDrawer.vue b/frontend/src/views/contests/components/AddJudgeDrawer.vue index c1cfe34..a6a114c 100644 --- a/frontend/src/views/contests/components/AddJudgeDrawer.vue +++ b/frontend/src/views/contests/components/AddJudgeDrawer.vue @@ -348,7 +348,6 @@ const handleSubmit = async () => { ) } - message.success("添加评委成功") emit("success") } catch (error: any) { message.error(error?.response?.data?.message || "添加评委失败") diff --git a/frontend/src/views/public/ActivityDetail.vue b/frontend/src/views/public/ActivityDetail.vue index 18ba866..f472799 100644 --- a/frontend/src/views/public/ActivityDetail.vue +++ b/frontend/src/views/public/ActivityDetail.vue @@ -60,12 +60,18 @@ 登录后查看作品 + + 立即报名 + - 需先报名才能提交作品 + 报名已截止 提交作品 + + 重新提交 + 作品已提交 @@ -246,14 +252,27 @@ const formatDate = (d: string) => dayjs(d).format('YYYY-MM-DD') const isLoggedIn = computed(() => !!localStorage.getItem('public_token')) +// 报名是否仍在开放中 +const isRegisterOpen = computed(() => { + if (!activity.value) return false + const now = dayjs() + return !now.isBefore(activity.value.registerStartTime) && now.isBefore(activity.value.registerEndTime) +}) + +// 是否允许重新提交(submitRule === 'resubmit' 且已提交过作品) +const canResubmit = computed(() => { + return hasSubmittedWork.value && activity.value?.submitRule === 'resubmit' +}) + // 活动当前阶段 const currentStage = computed(() => { if (!activity.value) return 'pending' const now = dayjs() const a = activity.value if (now.isBefore(a.registerStartTime)) return 'pending' + // 提交阶段优先:如果到了提交时间且在提交截止前,优先显示提交(报名与提交可能重叠) + if (a.submitStartTime && !now.isBefore(a.submitStartTime) && a.submitEndTime && now.isBefore(a.submitEndTime)) return 'submit' if (now.isBefore(a.registerEndTime)) return 'register' - if (a.submitStartTime && now.isBefore(a.submitEndTime)) return 'submit' if (a.reviewStartTime && now.isBefore(a.reviewEndTime)) return 'review' if (a.status === 'finished' || a.resultState === 'published') return 'finished' return 'review'