fix:多项前端修复与功能对齐
- 修复评委端进入评审contestId为NaN(record.id→record.contestId) - 修复评委评审详情403(活动名称改为路由传参,跳过需要contest:read权限的接口) - 已发布活动隐藏编辑按钮 - 添加评委成功提示去重(移除子组件重复message) - 用户端活动阶段判断修复(报名与提交重叠时优先显示提交阶段) - 用户端作品提交支持submitRule(once/resubmit)重新提交 - 后端公共API补充submitRule字段返回 - 报名统计接口增加租户隔离,修复统计与列表数据不一致 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3ef05de193
commit
1003776dd3
@ -37,7 +37,9 @@ public class ContestRegistrationController {
|
||||
@RequirePermission("contest:read")
|
||||
@Operation(summary = "获取报名统计")
|
||||
public Result<Map<String, Object>> 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
|
||||
|
||||
@ -15,7 +15,7 @@ public interface IContestRegistrationService extends IService<BizContestRegistra
|
||||
|
||||
PageResult<Map<String, Object>> findAll(QueryRegistrationDto dto, Long tenantId, boolean isSuperTenant);
|
||||
|
||||
Map<String, Object> getStats(Long contestId, Long tenantId);
|
||||
Map<String, Object> getStats(Long contestId, Long tenantId, boolean isSuperAdmin);
|
||||
|
||||
Map<String, Object> findDetail(Long id, Long tenantId);
|
||||
|
||||
|
||||
@ -123,20 +123,26 @@ public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrat
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStats(Long contestId, Long tenantId) {
|
||||
log.info("获取报名统计,赛事ID:{}", contestId);
|
||||
public Map<String, Object> getStats(Long contestId, Long tenantId, boolean isSuperAdmin) {
|
||||
log.info("获取报名统计,赛事ID:{},租户ID:{},超管:{}", contestId, tenantId, isSuperAdmin);
|
||||
|
||||
// 非超管需要按租户过滤,与列表查询保持一致
|
||||
LambdaQueryWrapper<BizContestRegistration> 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<BizContestRegistration> 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<ContestRegistrat
|
||||
if (contestId != null) {
|
||||
passedWrapper.eq(BizContestRegistration::getContestId, contestId);
|
||||
}
|
||||
if (!isSuperAdmin && tenantId != null) {
|
||||
passedWrapper.eq(BizContestRegistration::getTenantId, tenantId);
|
||||
}
|
||||
passedWrapper.eq(BizContestRegistration::getRegistrationState, "passed");
|
||||
long passed = count(passedWrapper);
|
||||
|
||||
@ -151,6 +160,9 @@ public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrat
|
||||
if (contestId != null) {
|
||||
rejectedWrapper.eq(BizContestRegistration::getContestId, contestId);
|
||||
}
|
||||
if (!isSuperAdmin && tenantId != null) {
|
||||
rejectedWrapper.eq(BizContestRegistration::getTenantId, tenantId);
|
||||
}
|
||||
rejectedWrapper.eq(BizContestRegistration::getRegistrationState, "rejected");
|
||||
long rejected = count(rejectedWrapper);
|
||||
|
||||
|
||||
@ -85,6 +85,7 @@ public class PublicActivityService {
|
||||
result.put("registerState", contest.getRegisterState());
|
||||
result.put("submitStartTime", contest.getSubmitStartTime());
|
||||
result.put("submitEndTime", contest.getSubmitEndTime());
|
||||
result.put("submitRule", contest.getSubmitRule());
|
||||
result.put("reviewStartTime", contest.getReviewStartTime());
|
||||
result.put("reviewEndTime", contest.getReviewEndTime());
|
||||
result.put("workType", contest.getWorkType());
|
||||
@ -116,10 +117,11 @@ public class PublicActivityService {
|
||||
result.put("registrationState", reg.getRegistrationState());
|
||||
result.put("registrationTime", reg.getRegistrationTime());
|
||||
|
||||
// 查询是否已提交作品
|
||||
// 查询是否已提交作品(只统计最新版本)
|
||||
Long workCount = contestWorkMapper.selectCount(
|
||||
new LambdaQueryWrapper<BizContestWork>()
|
||||
.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<BizContestWork>()
|
||||
.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()));
|
||||
|
||||
@ -214,6 +214,7 @@ export interface PublicActivity {
|
||||
registerEndTime: string
|
||||
submitStartTime: string
|
||||
submitEndTime: string
|
||||
submitRule: string
|
||||
reviewStartTime: string
|
||||
reviewEndTime: string
|
||||
organizers: any
|
||||
|
||||
@ -155,7 +155,6 @@
|
||||
<template v-else>
|
||||
<a-button type="link" size="small" @click="router.push(`/${tenantCode}/contests/${record.id}`)">查看</a-button>
|
||||
<a-button v-permission="'contest:update'" type="link" size="small" @click="handleAddJudge(record.id)">评委</a-button>
|
||||
<a-button v-permission="'contest:update'" type="link" size="small" @click="handleEdit(record.id)">编辑</a-button>
|
||||
<a-button v-permission="'contest:publish'" type="link" size="small" style="color: #f59e0b" @click="handlePublishClick(record)">取消发布</a-button>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@ -348,7 +348,6 @@ const handleSubmit = async () => {
|
||||
)
|
||||
}
|
||||
|
||||
message.success("添加评委成功")
|
||||
emit("success")
|
||||
} catch (error: any) {
|
||||
message.error(error?.response?.data?.message || "添加评委失败")
|
||||
|
||||
@ -60,12 +60,18 @@
|
||||
<a-button v-if="!isLoggedIn" type="primary" size="large" block class="action-btn" @click="goLogin">
|
||||
登录后查看作品
|
||||
</a-button>
|
||||
<a-button v-else-if="!hasRegistered && isRegisterOpen" type="primary" size="large" block class="action-btn" @click="showRegisterModal = true">
|
||||
立即报名
|
||||
</a-button>
|
||||
<a-button v-else-if="!hasRegistered" size="large" block disabled class="action-btn-disabled">
|
||||
需先报名才能提交作品
|
||||
报名已截止
|
||||
</a-button>
|
||||
<a-button v-else-if="!hasSubmittedWork" type="primary" size="large" block class="action-btn" @click="openSubmitWork">
|
||||
<upload-outlined /> 提交作品
|
||||
</a-button>
|
||||
<a-button v-else-if="canResubmit" type="primary" size="large" block class="action-btn" @click="openSubmitWork">
|
||||
<upload-outlined /> 重新提交
|
||||
</a-button>
|
||||
<a-button v-else size="large" block class="action-btn-done">
|
||||
<check-circle-outlined /> 作品已提交
|
||||
</a-button>
|
||||
@ -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'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user