历史上一共 6 条站内信(最后一条 2025-06),发信接口两代前端都没接过,后台页面只有一句 「未完待续」,OJ2 顶栏也没有入口。 0033 是 DROP TABLE,部署要显式放行:OJ2_ALLOW_DESTRUCTIVE=1 docker/deploy.sh, 或 CI 手工触发时勾 allow_destructive。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
734f4aed05
commit
4240a451e4
13 files changed
+3838
-308
No files matched your search
@@ -1,2 +0,0 @@
|
||||
<template>未完待续</template>
|
||||
<script lang="ts" setup></script>
|
||||
@@ -57,7 +57,6 @@ import type {
|
||||
Announcement,
|
||||
AnnouncementListItem,
|
||||
Profile,
|
||||
Message,
|
||||
SubmissionListItem,
|
||||
Exercise,
|
||||
ProblemDetail,
|
||||
@@ -380,13 +379,6 @@ export function getAnnouncement(id: number) {
|
||||
return api.get<Announcement>(`announcements/${id}`)
|
||||
}
|
||||
|
||||
export function getMessageList(offset = 0, limit = 10) {
|
||||
// language 的收窄同 getSubmissions,见那里的说明
|
||||
return api.get<{ results: Message[]; total: number }>("messages", {
|
||||
params: { limit, offset },
|
||||
})
|
||||
}
|
||||
|
||||
export function getReaction(problemID: number) {
|
||||
return api.get<ReactionState>(`problems/${problemID}/reaction`)
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
<template>
|
||||
<div class="oj-page message-page">
|
||||
<h2>消息</h2>
|
||||
<n-list v-if="messages.length">
|
||||
<n-list-item :style="{ overflow: 'auto' }" v-for="(item, index) in messages" :key="index">
|
||||
<n-flex size="large" vertical>
|
||||
<n-flex align="center">
|
||||
<div>发送时间</div>
|
||||
<div>{{ parseTime(item.createTime, "YYYY年M月D日 HH:mm:ss") }}</div>
|
||||
<div>发送者</div>
|
||||
<div>{{ item.sender.username }}</div>
|
||||
</n-flex>
|
||||
<n-flex align="center">
|
||||
<div>题目序号</div>
|
||||
<n-button
|
||||
text
|
||||
type="info"
|
||||
@click="router.push('/problem/' + item.submission.problemDisplayId)"
|
||||
>
|
||||
{{ item.submission.problemDisplayId }}
|
||||
</n-button>
|
||||
<n-text :type="JUDGE_STATUS[item.submission.result]['type']">
|
||||
{{ JUDGE_STATUS[item.submission.result]["name"] }}
|
||||
</n-text>
|
||||
<Copy :value="item.submission.code" />
|
||||
</n-flex>
|
||||
<n-code
|
||||
:language="LANGUAGE_FORMAT_VALUE[item.submission.language]"
|
||||
:code="item.submission.code"
|
||||
show-line-numbers
|
||||
/>
|
||||
<div v-html="item.message"></div>
|
||||
</n-flex>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
<n-empty v-else description="没有消息" style="margin: 48px 0"></n-empty>
|
||||
<Pagination v-model:limit="query.limit" v-model:page="query.page" :total="total" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getMessageList } from "oj/api"
|
||||
import { JUDGE_STATUS, LANGUAGE_FORMAT_VALUE } from "utils/constants"
|
||||
import Copy from "shared/components/Copy.vue"
|
||||
import Pagination from "shared/components/Pagination.vue"
|
||||
import { parseTime } from "utils/functions"
|
||||
import type { Message } from "utils/types"
|
||||
|
||||
const router = useRouter()
|
||||
const messages = ref<Message[]>([])
|
||||
const total = ref(0)
|
||||
|
||||
const query = reactive({
|
||||
limit: 10,
|
||||
page: 1,
|
||||
})
|
||||
|
||||
async function listMessages() {
|
||||
const offset = (query.page - 1) * query.limit
|
||||
const res = await getMessageList(offset, query.limit)
|
||||
total.value = res.total
|
||||
messages.value = res.results
|
||||
}
|
||||
|
||||
onMounted(listMessages)
|
||||
watch(query, listMessages, { deep: true })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.message-page h2 {
|
||||
margin: 0 0 8px;
|
||||
font-size: var(--oj-fs-title);
|
||||
}
|
||||
|
||||
.message-page :deep(.n-list-item) {
|
||||
padding: 20px 0;
|
||||
font-size: var(--oj-fs-sec);
|
||||
}
|
||||
</style>
|
||||
@@ -111,11 +111,6 @@ export const ojs: RouteRecordRaw = {
|
||||
component: () => import("oj/achievement/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "message",
|
||||
component: () => import("oj/user/message.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "learn",
|
||||
name: "learn home",
|
||||
@@ -268,12 +263,6 @@ export const admins: RouteRecordRaw = {
|
||||
props: true,
|
||||
meta: { requiresSuperAdmin: true },
|
||||
},
|
||||
{
|
||||
path: "message/list",
|
||||
name: "admin message list",
|
||||
component: () => import("admin/communication/messages.vue"),
|
||||
meta: { requiresSuperAdmin: true },
|
||||
},
|
||||
{
|
||||
path: "tutorial/list",
|
||||
name: "admin tutorial list",
|
||||
|
||||
@@ -7,8 +7,6 @@ import type {
|
||||
RankProfile,
|
||||
SessionUser,
|
||||
UserProfile,
|
||||
EmbeddedSubmission as ContractEmbeddedSubmission,
|
||||
Message as ContractMessage,
|
||||
ProblemDifficulty,
|
||||
JudgeStatus,
|
||||
AstRules,
|
||||
@@ -245,12 +243,6 @@ export type Submission = Omit<SubmissionDetail, "result"> & {
|
||||
result: SUBMISSION_RESULT
|
||||
}
|
||||
|
||||
/**
|
||||
* 站内信里嵌的提交:problem 是展示题号而非数字 id,且不含 info / ip / contestId。
|
||||
* 契约里已经窄化好了(`embeddedSubmissionSchema`),这里不再重复 Omit + 增补。
|
||||
*/
|
||||
export type EmbeddedSubmission = ContractEmbeddedSubmission
|
||||
|
||||
export type SubmissionListItem = ContractSubmissionListItem
|
||||
|
||||
export interface SubmissionListPayload {
|
||||
@@ -358,14 +350,6 @@ export type {
|
||||
/** 后台编辑表单:请求体 + id(新建时填 0,提交前由 api 层剥掉) */
|
||||
export type AnnouncementEdit = CreateAnnouncementRequest & { id: number }
|
||||
|
||||
/**
|
||||
* 站内信。**直接取契约** —— 原来这里把 `submission` 换成前端窄化过的
|
||||
* `EmbeddedSubmission`,但那个窄化已经搬进 `embeddedSubmissionSchema`
|
||||
* (statisticInfo / language 在契约里不再是 unknown),两者双向可赋值、
|
||||
* 完全等价,这层 Omit 已经没有内容。
|
||||
*/
|
||||
export type Message = ContractMessage
|
||||
|
||||
/**
|
||||
* 题目表情。三个类型都直接取自契约 —— 语义 key 必须与后端 reaction/models.py
|
||||
* 的 ReactionType 一致(见根 CLAUDE.md),手抄一份迟早对不上。
|
||||
|
||||
Reference in new issue
Block a user