From 7793a2fe4c020234972bb5f762502d514196bc54 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Wed, 26 Aug 2026 09:09:26 -0600 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=97=A7=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E4=B8=8B=E7=BA=BF=E5=90=8E=E7=9A=84=E6=AE=8B=E7=95=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 旧 Django 后端 2026-08-26 下线、回滚路径作废,代码里还留着几处以它为前提的 死代码和过期注释。 - PUBLIC_WS_URL / PUBLIC_OJ_URL 全部删除。BaseWebSocket 的 `${PUBLIC_WS_URL}/${path}/` fallback 是 Channels 时代的写法,而三个子类 早就各自传 url,等于永不执行;config.vue 里 PUBLIC_OJ_URL 只是个会被 getWebsiteConfig() 立刻覆盖、且指着 :8000 的假初值。WebSocketConfig.path 随之去掉,url 改成必填。 - vite.config:删掉 /api2、/ws2 的迁移期注释,换成还成立的约束(代理这三段 要和 docker/Caddyfile 同步);newBackend 改名 backend。 - 六处 “回滚时旧后端还要读” 换成真实理由:snake_case 保留的结论不变,但因为 判题机按这套键名写、存量 JSONB 就是这形状。 - CLAUDE.md:数据库一节开头「不写迁移 + 先想清楚回滚」与下一节的 drizzle migration 自相矛盾,改掉;判题状态码不再要求同步到 OnlineJudge,理由换成 历史 submission.result 和沙箱用的就是这套编码。 Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 16 ++++++------ apps/web/.env.production | 2 -- apps/web/.env.staging | 2 -- apps/web/.env.test | 2 -- apps/web/src/admin/contest/helper.vue | 2 +- apps/web/src/admin/setting/config.vue | 2 +- apps/web/src/env.d.ts | 2 -- apps/web/src/shared/composables/websocket.ts | 26 ++++++-------------- apps/web/src/utils/types.ts | 9 ++++--- apps/web/vite.config.ts | 16 ++++++------ packages/contract/src/problem.ts | 5 ++-- 11 files changed, 33 insertions(+), 51 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5631622..5e7fe80 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -95,11 +95,11 @@ dev 直接起不来。 加完路由跑 `bun run --filter '@oj2/api' check:routes`。 -### 判题状态码要三处同步 +### 判题状态码不能改 -`apps/api/src/judge/status.ts`、`apps/web/src/utils/constants.ts`、 -以及上一代的 `../OnlineJudge/submission/models.py`(回滚时要对得上)。 -题目表情 reaction 的语义 key 同理。 +`apps/api/src/judge/status.ts` 和 `apps/web/src/utils/constants.ts` 必须一致。 +这些整数是**落库的值**:12 万条历史提交的 `submission.result` 就是它们,判题沙箱回的也是 +这套编码,所以只能新增、不能改已有的含义。题目表情 reaction 的语义 key 同理。 ### 比赛只有 ACM 模式 @@ -112,9 +112,11 @@ dev 直接起不来。 ## 数据库 -Drizzle schema 是从生产库 `drizzle-kit pull` 出来的,**不写迁移**。 -新旧后端跑在同一套表结构上(阶段 5 演练逐列比对过,零差异),这是回滚能成立的前提 —— -所以改 schema 前先想清楚回滚怎么办。 +Drizzle schema 最初是 `drizzle-kit pull` 从生产库拉出来的,所以它长得像 Django 建的表 +(表名、bigint/int4 混用、外键全是 NO ACTION),`schema.ts` 顶部记了哪些地方是手工修的。 + +**schema 现在归 OJ2 独占。** 旧后端已下线,「改 schema 要考虑回滚」这条约束不再存在, +结构变更走下面的 migration 正常演进即可。 ### 改 schema 走 drizzle migration diff --git a/apps/web/.env.production b/apps/web/.env.production index c19eedf..a6346e9 100644 --- a/apps/web/.env.production +++ b/apps/web/.env.production @@ -1,8 +1,6 @@ PUBLIC_ENV=xuyue.cc PUBLIC_MAXKB_URL=https://maxkb.xuyue.cc/chat/api/embed?protocol=https&host=maxkb.xuyue.cc&token=dd37457027c40b39 -PUBLIC_OJ_URL=https://oj.xuyue.cc PUBLIC_CODE_URL=https://code.xuyue.cc PUBLIC_JUDGE0_URL=https://judge0api.xuyue.cc PUBLIC_SIGNALING_URL=wss://signaling.xuyue.cc -PUBLIC_WS_URL=wss://oj.xuyue.cc/ws PUBLIC_ICONIFY_URL=https://icon.xuyue.cc \ No newline at end of file diff --git a/apps/web/.env.staging b/apps/web/.env.staging index 7916890..3e73c7b 100644 --- a/apps/web/.env.staging +++ b/apps/web/.env.staging @@ -1,8 +1,6 @@ PUBLIC_ENV=school PUBLIC_MAXKB_URL=http://10.13.114.114:92/chat/api/embed?protocol=http&host=10.13.114.114:92&token=dd37457027c40b39 -PUBLIC_OJ_URL=http://10.13.114.114:81 PUBLIC_CODE_URL=http://10.13.114.114:82 PUBLIC_JUDGE0_URL=http://10.13.114.114:8082 PUBLIC_ICONIFY_URL=http://10.13.114.114:8098 PUBLIC_SIGNALING_URL=ws://10.13.114.114:8085 -PUBLIC_WS_URL=ws://10.13.114.114:81/ws \ No newline at end of file diff --git a/apps/web/.env.test b/apps/web/.env.test index cf7dc87..823e478 100644 --- a/apps/web/.env.test +++ b/apps/web/.env.test @@ -1,8 +1,6 @@ PUBLIC_ENV=test PUBLIC_MAXKB_URL=http://10.13.114.114:92/chat/api/embed?protocol=http&host=10.13.114.114:92&token=dd37457027c40b39 -PUBLIC_OJ_URL=http://10.13.114.114:93 PUBLIC_CODE_URL=http://10.13.114.114:82 PUBLIC_JUDGE0_URL=http://10.13.114.114:8082 PUBLIC_ICONIFY_URL=http://10.13.114.114:8098 PUBLIC_SIGNALING_URL=ws://10.13.114.114:8085 -PUBLIC_WS_URL=ws://10.13.114.114:93/ws \ No newline at end of file diff --git a/apps/web/src/admin/contest/helper.vue b/apps/web/src/admin/contest/helper.vue index 7f19951..b6a71a8 100644 --- a/apps/web/src/admin/contest/helper.vue +++ b/apps/web/src/admin/contest/helper.vue @@ -13,7 +13,7 @@ interface Props { /** * ACM 助手行。`acInfo` 的**内容**是 acm_contest_rank.submission_info 的 JSONB 原文, - * 键名保持 snake_case —— 回滚时旧后端还要读。 + * 键名保持 snake_case —— 判题写进去的就是这套键名,见 utils/types.ts 的 SubmissionInfo。 */ type HelperItem = Omit & { acInfo: SubmissionInfo diff --git a/apps/web/src/admin/setting/config.vue b/apps/web/src/admin/setting/config.vue index 35ecdcd..1c2187f 100644 --- a/apps/web/src/admin/setting/config.vue +++ b/apps/web/src/admin/setting/config.vue @@ -117,7 +117,7 @@ const abnormalServers = computed(() => ) const websiteConfig = reactive({ - websiteBaseUrl: import.meta.env.PUBLIC_OJ_URL, + websiteBaseUrl: "", websiteName: "判题狗", websiteNameShortcut: "判题狗", websiteFooter: "所有权归属于徐越,感谢青岛大学开源 OJ 系统,感谢开源社区", diff --git a/apps/web/src/env.d.ts b/apps/web/src/env.d.ts index 671f9d4..5e6ff03 100644 --- a/apps/web/src/env.d.ts +++ b/apps/web/src/env.d.ts @@ -3,12 +3,10 @@ interface ImportMetaEnv { readonly PUBLIC_ENV: string readonly PUBLIC_MAXKB_URL: string - readonly PUBLIC_OJ_URL: string readonly PUBLIC_CODE_URL: string readonly PUBLIC_JUDGE0_URL: string readonly PUBLIC_ICONIFY_URL: string readonly PUBLIC_SIGNALING_URL: string - readonly PUBLIC_WS_URL: string } interface ImportMeta { diff --git a/apps/web/src/shared/composables/websocket.ts b/apps/web/src/shared/composables/websocket.ts index 8f9d002..7948fa0 100644 --- a/apps/web/src/shared/composables/websocket.ts +++ b/apps/web/src/shared/composables/websocket.ts @@ -18,10 +18,8 @@ export interface WebSocketMessage { * WebSocket 配置 */ export interface WebSocketConfig { - /** WebSocket 路径(如 '/ws/submission/') */ - path: string - /** 完整 URL;提供后覆盖 PUBLIC_WS_URL + path */ - url?: string + /** 完整 URL。后端只认 /ws/submissions 和 /ws/config 两条,按当前页面的协议与 host 拼 */ + url: string /** 最大重连次数,默认 5 */ maxReconnectAttempts?: number /** 重连延迟(毫秒),默认 1000 */ @@ -61,7 +59,7 @@ export class BaseWebSocket { public status: Ref = ref("disconnected") constructor(config: WebSocketConfig) { - this.url = config.url ?? `${import.meta.env.PUBLIC_WS_URL}/${config.path}/` + this.url = config.url this.maxReconnectAttempts = config.maxReconnectAttempts ?? 5 this.reconnectDelay = config.reconnectDelay ?? 1000 @@ -306,10 +304,7 @@ class SubmissionWebSocket extends BaseWebSocket { constructor() { const protocol = window.location.protocol === "https:" ? "wss:" : "ws:" - super({ - path: "submission", - url: `${protocol}//${window.location.host}/ws/submissions`, - }) + super({ url: `${protocol}//${window.location.host}/ws/submissions` }) } /** @@ -387,7 +382,8 @@ export function useSubmissionWebSocket( * * class NotificationWebSocket extends BaseWebSocket { * constructor() { - * super({ path: 'notification' }) + * const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' + * super({ url: `${protocol}//${window.location.host}/ws/notifications` }) * } * } * @@ -448,10 +444,7 @@ export interface FlowchartEvaluationUpdate extends WebSocketMessage { class FlowchartWebSocket extends BaseWebSocket { constructor() { const protocol = window.location.protocol === "https:" ? "wss:" : "ws:" - super({ - path: "flowchart", - url: `${protocol}//${window.location.host}/ws/submissions`, - }) + super({ url: `${protocol}//${window.location.host}/ws/submissions` }) } /** @@ -518,10 +511,7 @@ export interface ConfigUpdate extends WebSocketMessage { class ConfigWebSocket extends BaseWebSocket { constructor() { const protocol = window.location.protocol === "https:" ? "wss:" : "ws:" - super({ - path: "config", - url: `${protocol}//${window.location.host}/ws/config`, - }) + super({ url: `${protocol}//${window.location.host}/ws/config` }) } /** diff --git a/apps/web/src/utils/types.ts b/apps/web/src/utils/types.ts index 2975c97..d67b9f1 100644 --- a/apps/web/src/utils/types.ts +++ b/apps/web/src/utils/types.ts @@ -19,7 +19,8 @@ import type { /** * 个人主页数据。`acmProblemsStatus` 的**内容**保持 snake_case —— - * 它是 user_profile.acm_problems_status 的 JSONB 原文,回滚时旧后端还要读。 + * 它是 user_profile.acm_problems_status 的 JSONB 原文,库里存量数据就是这套键名, + * 改名等于要把全部历史行迁一遍。 */ export type Profile = Omit & { user: SessionUser @@ -281,7 +282,7 @@ interface Info { /** * 判题产出的统计。**键名保持 snake_case** —— 这是 submission.statistic_info - * JSONB 的原文,判题机写进去、回滚时旧后端还要读,不能跟着响应字段一起改名。 + * JSONB 的原文,判题机按这套键名写进去,不能跟着响应字段一起改名。 */ export interface StatisticInfo { score?: number @@ -368,7 +369,7 @@ export type BlankContest = Omit< /** * acm_contest_rank.submission_info 的 JSONB 内容。**键名保持 snake_case** —— - * 判题写进去、回滚时旧后端还要读,不能跟着响应字段一起改名。 + * 判题按这套键名写进去,历史比赛的榜单行也是这个形状,不能跟着响应字段一起改名。 */ export interface SubmissionInfo { is_ac: boolean @@ -380,7 +381,7 @@ export interface SubmissionInfo { /** * 榜单行。`submissionInfo` 的**内容**仍是 snake_case —— 它是 acm_contest_rank - * 表的 JSONB 原文,回滚时旧后端还要读,见 SubmissionInfo。 + * 表的 JSONB 原文,见 SubmissionInfo。 */ export type ContestRank = Omit< import("@oj2/contract").ContestRankItem, diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index ed4b671..5ea553f 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -92,9 +92,8 @@ function injectMaxkb(maxkbUrl: string | undefined): Plugin { export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), "PUBLIC_") - // 开发时一律指向本机新后端。PUBLIC_OJ_URL / PUBLIC_WS_URL 是迁移期指向 - // 旧 Django 用的,端点搬完后不再参与代理。 - const newBackend = { + // 开发时一律指向本机后端(apps/api,3000)。 + const backend = { target: "http://localhost:3000", changeOrigin: true, } @@ -162,12 +161,11 @@ export default defineConfig(({ mode }) => { server: { port: 5173, proxy: { - // 迁移期间 /api2、/ws2 是「新后端」的临时前缀,/api、/ws 还指着 Django。 - // 端点已全部搬完,临时前缀去掉、统一指向新后端 —— 生产的 Caddy 也只认 - // /api、/ws、/public 这三段(docker/Caddyfile)。 - "/api": newBackend, - "/public": newBackend, - "/ws": { ...newBackend, ws: true }, + // 只有这三段过后端,和生产的 Caddy 认的是同一套(docker/Caddyfile), + // 别在这里加代理规则而不同步改 Caddyfile。 + "/api": backend, + "/public": backend, + "/ws": { ...backend, ws: true }, }, }, } diff --git a/packages/contract/src/problem.ts b/packages/contract/src/problem.ts index f095df8..2fa7f7d 100644 --- a/packages/contract/src/problem.ts +++ b/packages/contract/src/problem.ts @@ -11,9 +11,8 @@ export const problemDifficultySchema = z.enum(["Low", "Mid", "High"]) /** * SQL 题配置与展示数据。两者都是 `problem.sql_config` / `problem.sql_display` - * 的 **JSONB 原文**,所以键名保持 snake_case —— 回滚时旧后端要读同一份, - * 且旧后端 `judge/sql_runner.py:build_display` 产出的就是这个形状 - * (生产库 9 道 SQL 题逐条比对过,键集完全一致)。 + * 的 **JSONB 原文**,所以键名保持 snake_case —— 生产库 9 道 SQL 题存的就是这个形状 + * (移植自旧后端 `judge/sql_runner.py:build_display`,逐条比对过,键集完全一致)。 * * 写成精确 schema 而不是 `z.record(z.unknown())`:前端原来得自己手抄一份 * SQLDisplay 接口才能渲染表格,抄错了没人拦得住。