Files
OJ2/apps/web/CLAUDE.md
yuetsh 5da661d7f0
Some checks failed
Deploy / deploy (push) Has been cancelled
refactor(契约): 补上 AI / 站内信 / 自学留痕等接口的闸门,并订正前端 CLAUDE.md 的过期内容
闸门再补 13 个端点:AI 系列(detail / solved / duration / heatmap /
login-summary / pinned)、站内信列表、教程列表与学习进度、题目逐年 AC、
流程图列表。学生端读接口至此基本覆盖(写操作与纯前端伪状态不接)。

文档订正(原文写着"后端是 ../OnlineJudge 的 Django 5"、"utils/http.ts"、
"utils/permissions.ts",这些都早已不存在):
- 项目概述改成 OJ2 前端,并点明要兼容机房老 Chrome;
- HTTP 客户端改成实际存在的 utils/api.ts;
- 新增「Contract guard」一节,把**失败策略**(记日志 + 放行原始数据、不抛错)
  和排查入口(window.__OJ2_CONTRACT_DRIFT__)写清楚,并提醒同一个 schema
  后端也在 parse、收紧前要用生产数据核验;
- Related Repository 指向同仓的 ../api 与 packages/contract。

验证:vue-tsc 与 vite build 通过;13 个新端点对真实接口全部通过
(11 通过 / 0 失败 / 0 跳过)。
2026-09-10 04:25:24 -06:00

6.3 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

OJ2 的前端OJ2/apps/web),代码从上一代 ojnext/ 原样搬来、只替换了 API 层 ojnext../OnlineJudge 都已下线且完全冻结,一行都不改。Vue 3 + TypeScript ViteRolldown 内核、Naive UI、Pinia、Vue Router。

要兼容机房的老 Chrome< 94vite.config.ts 的 legacy 配置与 mermaid-legacy 等 fallback 依赖不能动,理由写在该文件的注释里。

Commands

npm start              # Start dev server on port 5173
npm run build          # Production build
npm run build:staging  # Staging build
npm run build:test     # Test build
npm fmt                # Format with Prettier

No test suite is configured. Linting is via Prettier only.

Architecture

Directory Structure

src/
├── shared/        # Cross-cutting concerns: layout, stores, composables, API
├── oj/            # User-facing features (problems, submissions, contests, etc.)
├── admin/         # Admin panel features
├── utils/         # Constants, types, HTTP client, helpers
├── routes.ts      # Route definitions (two top-level: ojs, admins)
├── main.ts        # App entry point
└── App.vue        # Root component with Naive UI theme setup

Module Pattern

Each feature module (under oj/ or admin/) typically has:

  • views/ — page-level Vue components
  • components/ — feature-specific components
  • api.ts — API calls specific to the feature

Shared logic lives in shared/:

  • store/ — Pinia stores: user (auth/roles), config (site-wide settings), authModal (login/signup form state), screenMode (problem split-screen layout), loginSummary (AI activity summary), collab (help-request queue + collab room)
  • composables/pagination (URL-synced), websocket (reconnect + heartbeat), collabDoc (Yjs binding for the collab channel), configUpdate (WS-pushed config sync), useMermaid (lazy Mermaid render), breakpoints, maxkb
  • layout/default.vue and admin.vue layout wrappers
  • api.ts — shared API calls (auth, profile, tags, captcha)

Auto-Imports

Configured via unplugin-auto-import and unplugin-vue-components. You do not need to manually import:

  • Vue APIs (ref, computed, watch, etc.)
  • Vue Router (useRouter, useRoute)
  • Pinia (defineStore, storeToRefs)
  • VueUse composables
  • Naive UI composables (useDialog, useMessage, useNotification, useLoadingBar)
  • Naive UI components (all N* components)
  • Naive UI types (DataTableColumn, FormRules, FormItemRule, SelectOption, UploadCustomRequestOptions, UploadFileInfo, MenuOption, DropdownOption)

Generated type declaration files: src/auto-imports.d.ts, src/components.d.ts.

Path Aliases

utils  →  ./src/utils
oj     →  ./src/oj
admin  →  ./src/admin
shared →  ./src/shared

HTTP Client

utils/api.ts — Axios instance with interceptors (baseURL: "/api", withCredentials). It unwraps both the axios envelope and the backend's { data } envelope, so callers get the payload directly. All API calls proxy through the dev server (see vite.config.ts).

Contract guard (utils/contract.ts)

@oj2/contract 的 zod schema 是前后端唯一的形状来源utils/types.ts 只做 z.infer 派生与少量前端专有的收窄(都写了理由)。读接口应当走守卫:

const endpoint = `problems/${encodeURIComponent(id)}`
return contract("GET /problems/:id", problemDetailSchema, await api.get<unknown>(endpoint))

失败策略是「记日志 + 放行原始数据」,不抛错。 形状对不上时:控制台打一条带 端点和字段路径的记录、去重后记进 window.__OJ2_CONTRACT_DRIFT__、然后返回原始 数据让页面继续渲染。面向学生的生产站点,少一个字段的代价远小于白屏。

排查线上分歧就是打开控制台敲 window.__OJ2_CONTRACT_DRIFT__;某条路径长期为空之后, 那条路径可以升级成硬失败(直接 schema.parse),在那之前不要改。

改动 schema 时要记住同一个 schema 后端也在 parse(如 submissionDetailSchema.parse 在路由里),所以收紧一个字段前先用生产数据核一遍, 否则一条不符合的历史记录会让整个列表 500。

Key Utilities

  • utils/constants.ts — Judge status codes, language IDs, difficulty levels, contest types
  • utils/types.ts — 契约类型的派生与前端专有收窄(不是手写的一份平行类型)
  • utils/contract.ts — 运行时契约闸门,见上
  • utils/judge.ts — Judge-related utilities
  • utils/renders.ts — Table column render helpers for Naive UI DataTable

Environment Variables

Variables prefixed with PUBLIC_ are injected at build time. Env files: .env, .env.staging, .env.test.

Variable Purpose
PUBLIC_OJ_URL Backend REST API base URL
PUBLIC_WS_URL WebSocket server URL
PUBLIC_ENV Environment name (dev/staging/production)
PUBLIC_CODE_URL Code execution service
PUBLIC_JUDGE0_URL Judge0 API
PUBLIC_MAXKB_URL Knowledge base service
PUBLIC_ICONIFY_URL Iconify icon CDN

Routing

Routes are defined in src/routes.ts with two root routes: ojs (user-facing) and admins (admin panel). Route meta fields used:

  • requiresAuth — redirect to login if not authenticated
  • requiresSuperAdmin — super admin only
  • requiresProblemPermission — problem management access

Real-time Features

  • WebSocket via composable in shared/composables/ for submission status updates
  • Yjs over the /ws/collab channel for classroom help requests and collaborative code editing (students raise a hand, teachers join their editor). The server is a dumb relay — it authenticates, assigns rooms, and forwards frames without parsing them. See docs/specs/2026-08-28-collab-help-request-design.md.

后端就在同一个仓库的 ../apiBun + Hono + Drizzle编译成单二进制 契约在 ../../packages/contract不要再去看 OnlineJudge/ —— 那是已下线的 Django 后端,只作参照、完全冻结。详见 ../CLAUDE.md