feat(看板娘): 记得你常错在哪,题目页上会主动开口,互动都记下来
Deploy / deploy (push) Successful in 51s

- 按学生的行为挑话:最近 20 次提交里某类错特别多(运行出错细到 NameError、TypeError、
  ValueError、EOFError),再错这一类时点名说;今天做对第 3 道起偶尔数一数;
  第一次戳她有见面的话,戳得多了说「又是你」
- 题目页上主动开口:同一题错到第 5 次、连着 3 次没跑例子就交还错了、又犯老毛病、
  15 分钟没交没跑。比赛里不说,至少隔 3 分钟,同一件事一题一次,10 秒后收起
- 互动记进新表 mascot_event(0043):点开、点按钮、主动开口、摸头、点烦、被拖走;
  只记看板娘这边的事,每人每分钟最多 30 条
- GET /mascot/profile 给最近的错类、今天做对几道、点过几次;门槛按本机生产快照定
  (266 人里运行出错 ≥3 的 87 人、答案错误 ≥5 的 82 人)
- 编辑过程(粘贴)数据学生侧不用,免得冤枉人

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-03 16:22:58 +08:00
1 parent ef3c53c671
commit 81ef4dca6b
12 files changed
+4630 -29

No files matched your search

+1
View File
@@ -12,6 +12,7 @@ export * from './flowchart'
export * from './flowchart-doc'
export * from './judge-status'
export * from './language'
export * from './mascot'
export * from './problem'
export * from './problemset'
export * from './ranking'
+49
View File
@@ -0,0 +1,49 @@
import { z } from 'zod'
/**
* 看板娘的互动记录(POST /mascot/events)。前台每次互动发一条,落进 `mascot_event`:
*
* - `open`:点她冒出气泡(situation = 她当时判断的情况,line = 说的那句)
* - `action`:点了气泡里的按钮(action = 按钮上的字)
* - `speak`:她主动开口(situation / line 同 open)
* - `pat`:连点触发了摸头彩蛋
* - `overclick`:连点到她嫌烦
* - `move`:被拖到了别处(line 记落点贴的边,比如 `right bottom`)
*
* 只记看板娘这边的事,不记代码、不记按键。
*/
export const MASCOT_EVENT_KINDS = ['open', 'action', 'speak', 'pat', 'overclick', 'move'] as const
export const mascotEventKindSchema = z.enum(MASCOT_EVENT_KINDS)
export type MascotEventKind = z.infer<typeof mascotEventKindSchema>
export const mascotEventSchema = z.object({
kind: mascotEventKindSchema,
situation: z.string().max(64).optional(),
line: z.string().max(100).optional(),
action: z.string().max(32).optional(),
/** 当时在哪一页(路由路径,不带查询串) */
path: z.string().max(200),
})
export type MascotEventInput = z.infer<typeof mascotEventSchema>
/**
* 看板娘按学生的行为挑话用的(GET /mascot/profile)。只给学生自己的,进页面时拉一次。
*
* `recent` 是最近 20 次题库提交(不含比赛)里各类错误的次数;`runtimeErrorType` 是其中
* 运行出错最多的那一种(NameError、TypeError …,C/C++ 没有这一项),没有就是 null。
*/
export const mascotProfileSchema = z.object({
/** 一共点开过她几次(open 事件数),按账号算 */
pokes: z.number().int(),
/** 今天(东八区)做对了几道不同的题 */
todaySolved: z.number().int(),
recent: z.object({
total: z.number().int(),
wrongAnswer: z.number().int(),
compileError: z.number().int(),
runtimeError: z.number().int(),
timeout: z.number().int(),
runtimeErrorType: z.string().nullable(),
}),
})
export type MascotProfile = z.infer<typeof mascotProfileSchema>