fix:活动详情优化
This commit is contained in:
parent
4a70bc7d43
commit
b3954ffcf3
@ -17,6 +17,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "赛事公告")
|
||||
@ -27,6 +28,34 @@ public class ContestNoticeController {
|
||||
|
||||
private final IContestNoticeService noticeService;
|
||||
|
||||
/**
|
||||
* 解析日期时间字符串,兼容 ISO 格式(带毫秒和 Z 时区标记)
|
||||
*/
|
||||
private LocalDateTime parseDateTime(String dateTime) {
|
||||
if (!StringUtils.hasText(dateTime)) {
|
||||
return null;
|
||||
}
|
||||
// 尝试 ISO 格式(yyyy-MM-dd'T'HH:mm:ss.SSSZ 或 yyyy-MM-dd'T'HH:mm:ss'Z')
|
||||
try {
|
||||
// 处理带 Z 的 ISO 格式
|
||||
if (dateTime.endsWith("Z")) {
|
||||
return LocalDateTime.parse(dateTime.substring(0, dateTime.length() - 1));
|
||||
}
|
||||
// 处理带毫秒的格式
|
||||
if (dateTime.contains(".") && dateTime.indexOf(".") + 4 == dateTime.length()) {
|
||||
return LocalDateTime.parse(dateTime.substring(0, dateTime.indexOf(".")));
|
||||
}
|
||||
return LocalDateTime.parse(dateTime);
|
||||
} catch (Exception e) {
|
||||
// 尝试空格分隔格式
|
||||
try {
|
||||
return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@RequirePermission("notice:create")
|
||||
@Operation(summary = "创建公告")
|
||||
@ -38,7 +67,7 @@ public class ContestNoticeController {
|
||||
notice.setNoticeType(dto.getNoticeType());
|
||||
notice.setPriority(dto.getPriority());
|
||||
if (StringUtils.hasText(dto.getPublishTime())) {
|
||||
notice.setPublishTime(LocalDateTime.parse(dto.getPublishTime()));
|
||||
notice.setPublishTime(parseDateTime(dto.getPublishTime()));
|
||||
}
|
||||
// 设置当前租户 ID(租户隔离)
|
||||
notice.setTenantId(SecurityUtil.getCurrentTenantId());
|
||||
@ -102,7 +131,7 @@ public class ContestNoticeController {
|
||||
notice.setNoticeType(dto.getNoticeType());
|
||||
notice.setPriority(dto.getPriority());
|
||||
if (StringUtils.hasText(dto.getPublishTime())) {
|
||||
notice.setPublishTime(LocalDateTime.parse(dto.getPublishTime()));
|
||||
notice.setPublishTime(parseDateTime(dto.getPublishTime()));
|
||||
}
|
||||
noticeService.updateById(notice);
|
||||
return Result.success();
|
||||
|
||||
@ -18,6 +18,8 @@ import com.competition.modules.biz.contest.mapper.ContestMapper;
|
||||
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
||||
import com.competition.modules.biz.contest.service.IContestService;
|
||||
import com.competition.modules.sys.entity.SysTenant;
|
||||
import com.competition.modules.sys.mapper.SysTenantMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -37,6 +39,7 @@ public class ContestServiceImpl extends ServiceImpl<ContestMapper, BizContest> i
|
||||
private final ContestAttachmentMapper contestAttachmentMapper;
|
||||
private final ContestRegistrationMapper contestRegistrationMapper;
|
||||
private final ContestWorkMapper contestWorkMapper;
|
||||
private final SysTenantMapper sysTenantMapper;
|
||||
|
||||
// 支持两种日期格式:ISO 格式 (T 分隔) 和空格分隔格式
|
||||
private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
@ -204,6 +207,24 @@ public class ContestServiceImpl extends ServiceImpl<ContestMapper, BizContest> i
|
||||
List<BizContestAttachment> attachments = contestAttachmentMapper.selectList(attWrapper);
|
||||
result.put("attachments", attachments);
|
||||
|
||||
// 查询授权租户的详细信息
|
||||
List<Integer> tenantIds = contest.getContestTenants();
|
||||
if (tenantIds != null && !tenantIds.isEmpty()) {
|
||||
List<Map<String, Object>> tenantInfoList = new ArrayList<>();
|
||||
for (Integer tenantId : tenantIds) {
|
||||
SysTenant tenant = sysTenantMapper.selectById(tenantId.longValue());
|
||||
if (tenant != null) {
|
||||
Map<String, Object> tenantInfo = new LinkedHashMap<>();
|
||||
tenantInfo.put("id", tenant.getId());
|
||||
tenantInfo.put("name", tenant.getName());
|
||||
tenantInfo.put("code", tenant.getCode());
|
||||
tenantInfo.put("tenantType", tenant.getTenantType());
|
||||
tenantInfoList.add(tenantInfo);
|
||||
}
|
||||
}
|
||||
result.put("contestTenantInfos", tenantInfoList);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,14 @@ export interface Contest {
|
||||
endTime: string;
|
||||
address?: string;
|
||||
content?: string;
|
||||
visibility?: "public" | "designated" | "internal"; // 可见范围
|
||||
contestTenants?: number[];
|
||||
contestTenantInfos?: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
tenantType?: string;
|
||||
}>;
|
||||
coverUrl?: string;
|
||||
posterUrl?: string;
|
||||
contactName?: string;
|
||||
|
||||
@ -250,6 +250,60 @@
|
||||
|
||||
<!-- 右侧:组织信息 -->
|
||||
<div class="content-right">
|
||||
<div class="sidebar-card">
|
||||
<div class="sidebar-header">
|
||||
<EyeOutlined />
|
||||
<span>可见范围</span>
|
||||
</div>
|
||||
<div class="sidebar-body">
|
||||
<div class="sidebar-item">
|
||||
<div class="item-label">
|
||||
<GlobalOutlined />
|
||||
可见范围
|
||||
</div>
|
||||
<div class="item-value">
|
||||
{{ getVisibilityText(contest?.visibility) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="contest?.visibility === 'designated' && contest?.contestTenantInfos?.length"
|
||||
class="sidebar-item"
|
||||
>
|
||||
<div class="item-label">
|
||||
<TeamOutlined />
|
||||
开放机构
|
||||
</div>
|
||||
<div class="item-value">
|
||||
<div
|
||||
v-for="tenant in contest.contestTenantInfos"
|
||||
:key="tenant.id"
|
||||
class="org-name"
|
||||
>
|
||||
{{ tenant.name }}({{ tenant.code }})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-card">
|
||||
<div class="sidebar-header">
|
||||
<InfoCircleOutlined />
|
||||
<span>基本信息</span>
|
||||
</div>
|
||||
<div class="sidebar-body">
|
||||
<div class="sidebar-item">
|
||||
<div class="item-label">
|
||||
<ClockCircleOutlined />
|
||||
创建时间
|
||||
</div>
|
||||
<div class="item-value">
|
||||
{{ formatDateTime(contest?.createTime) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-card">
|
||||
<div class="sidebar-header">
|
||||
<TeamOutlined />
|
||||
@ -262,15 +316,18 @@
|
||||
主办单位
|
||||
</div>
|
||||
<div class="item-value">
|
||||
<template
|
||||
v-if="contest.organizers && contest.organizers.length"
|
||||
>
|
||||
<div
|
||||
v-for="org in contest.organizers"
|
||||
:key="org"
|
||||
class="org-name"
|
||||
>
|
||||
{{ org }}
|
||||
<template v-if="contest.organizers">
|
||||
<template v-if="Array.isArray(contest.organizers)">
|
||||
<div
|
||||
v-for="org in contest.organizers"
|
||||
:key="org"
|
||||
class="org-name"
|
||||
>
|
||||
{{ org }}
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="contest.organizers" class="org-name">
|
||||
{{ contest.organizers }}
|
||||
</div>
|
||||
</template>
|
||||
<span v-else class="empty-text">暂无</span>
|
||||
@ -283,17 +340,18 @@
|
||||
协办单位
|
||||
</div>
|
||||
<div class="item-value">
|
||||
<template
|
||||
v-if="
|
||||
contest.coOrganizers && contest.coOrganizers.length
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="org in contest.coOrganizers"
|
||||
:key="org"
|
||||
class="org-name"
|
||||
>
|
||||
{{ org }}
|
||||
<template v-if="contest.coOrganizers">
|
||||
<template v-if="Array.isArray(contest.coOrganizers)">
|
||||
<div
|
||||
v-for="org in contest.coOrganizers"
|
||||
:key="org"
|
||||
class="org-name"
|
||||
>
|
||||
{{ org }}
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="contest.coOrganizers" class="org-name">
|
||||
{{ contest.coOrganizers }}
|
||||
</div>
|
||||
</template>
|
||||
<span v-else class="empty-text">暂无</span>
|
||||
@ -306,15 +364,18 @@
|
||||
赞助单位
|
||||
</div>
|
||||
<div class="item-value">
|
||||
<template
|
||||
v-if="contest.sponsors && contest.sponsors.length"
|
||||
>
|
||||
<div
|
||||
v-for="sp in contest.sponsors"
|
||||
:key="sp"
|
||||
class="org-name"
|
||||
>
|
||||
{{ sp }}
|
||||
<template v-if="contest.sponsors">
|
||||
<template v-if="Array.isArray(contest.sponsors)">
|
||||
<div
|
||||
v-for="sp in contest.sponsors"
|
||||
:key="sp"
|
||||
class="org-name"
|
||||
>
|
||||
{{ sp }}
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="contest.sponsors" class="org-name">
|
||||
{{ contest.sponsors }}
|
||||
</div>
|
||||
</template>
|
||||
<span v-else class="empty-text">暂无</span>
|
||||
@ -382,6 +443,8 @@ import {
|
||||
GiftOutlined,
|
||||
PhoneOutlined,
|
||||
UserOutlined,
|
||||
GlobalOutlined,
|
||||
InfoCircleOutlined,
|
||||
} from "@ant-design/icons-vue"
|
||||
import dayjs from "dayjs"
|
||||
import { useAuthStore } from "@/stores/auth"
|
||||
@ -513,6 +576,19 @@ const getNoticeTypeText = (type?: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
const getVisibilityText = (visibility?: string) => {
|
||||
switch (visibility) {
|
||||
case "public":
|
||||
return "公开(所有公众用户可见)"
|
||||
case "designated":
|
||||
return "指定机构"
|
||||
case "internal":
|
||||
return "仅内部"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
const getRankClass = (rank?: number) => {
|
||||
if (rank === 1) return "rank-1"
|
||||
if (rank === 2) return "rank-2"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user