- 题目页:这节课同班做对的人多就说「全班已经 N 个人做对了」、都卡着就安慰、第一个做对的夸一句; 通过率不到三成的题错了说「本来就难」、高于七成的说「再看看题」;AI 提示开了没用就提一句; 做对了还有挑战就问敢不敢;流程图分低、拿 S 各有话 - 常点「太难了」的学生错了换软话;这周交得多、例子跑得少的念一句;卡住却从不举手的劝举手; 这周要提示多的劝先自己想 - 首页(这节课没要做的时)每次戳随机说一件:知识点差几道升级、周榜差几道超过前一名、 今天明天的比赛、三天内的新公告;两周以上没来说「好久不见」,只说一回 - 排名页第一名、差前一名几道;成就页差一点的;自学没全对的练一练;专题差最后一道拿奖章 - 运行例子、自己输入、举手也记进 mascot_event(trial、help),GET /mascot/profile 多给「太难了」次数和这一周交、跑例子、举手、要提示的次数 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
81ef4dca6b
commit
aa7e0a6eb8
12 files changed
+296
-25
No files matched your search
@@ -1,11 +1,11 @@
|
||||
import { mascotEventSchema, type MascotProfile } from '@oj2/contract'
|
||||
import { and, count, countDistinct, desc, eq, gte, isNull, sql } from 'drizzle-orm'
|
||||
import { and, count, countDistinct, desc, eq, gte, inArray, isNull, sql } from 'drizzle-orm'
|
||||
import { Hono } from 'hono'
|
||||
|
||||
import { requireAuth, type AppEnv } from '../auth/middleware'
|
||||
import { db, schema } from '../db'
|
||||
import { parseBody, success } from '../http'
|
||||
import { dayStart } from '../time'
|
||||
import { dayStart, weekStart } from '../time'
|
||||
|
||||
export const mascotRoutes = new Hono<AppEnv>()
|
||||
|
||||
@@ -49,7 +49,9 @@ mascotRoutes.get('/mascot/profile', requireAuth, async (c) => {
|
||||
const userId = c.get('user')!.id
|
||||
const s = schema.submission
|
||||
|
||||
const [pokes, today, recent] = await Promise.all([
|
||||
const week = weekStart()
|
||||
const e = schema.mascotEvent
|
||||
const [pokes, today, recent, tooHard, weekSubmits, weekEvents, weekHints] = await Promise.all([
|
||||
db
|
||||
.select({ value: count() })
|
||||
.from(schema.mascotEvent)
|
||||
@@ -64,7 +66,26 @@ mascotRoutes.get('/mascot/profile', requireAuth, async (c) => {
|
||||
.where(and(eq(s.userId, userId), isNull(s.contestId)))
|
||||
.orderBy(desc(s.createTime))
|
||||
.limit(RECENT_SUBMISSIONS),
|
||||
db
|
||||
.select({ value: count() })
|
||||
.from(schema.reaction)
|
||||
.where(and(eq(schema.reaction.userId, userId), eq(schema.reaction.type, 'too_hard'))),
|
||||
db
|
||||
.select({ value: count() })
|
||||
.from(s)
|
||||
.where(and(eq(s.userId, userId), isNull(s.contestId), gte(s.createTime, week))),
|
||||
db
|
||||
.select({ kind: e.kind, value: count() })
|
||||
.from(e)
|
||||
.where(and(eq(e.userId, userId), inArray(e.kind, ['trial', 'help']), gte(e.createdAt, week)))
|
||||
.groupBy(e.kind),
|
||||
db
|
||||
.select({ value: count() })
|
||||
.from(schema.aiHint)
|
||||
.innerJoin(s, eq(s.id, schema.aiHint.submissionId))
|
||||
.where(and(eq(s.userId, userId), gte(schema.aiHint.createTime, week))),
|
||||
])
|
||||
const eventCount = (kind: string) => weekEvents.find((row) => row.kind === kind)?.value ?? 0
|
||||
|
||||
const types = new Map<string, number>()
|
||||
for (const row of recent) if (row.result === 4 && row.errorType) types.set(row.errorType, (types.get(row.errorType) ?? 0) + 1)
|
||||
@@ -74,6 +95,13 @@ mascotRoutes.get('/mascot/profile', requireAuth, async (c) => {
|
||||
return success(c, {
|
||||
pokes: pokes[0]?.value ?? 0,
|
||||
todaySolved: today[0]?.value ?? 0,
|
||||
tooHard: tooHard[0]?.value ?? 0,
|
||||
week: {
|
||||
submits: weekSubmits[0]?.value ?? 0,
|
||||
trials: eventCount('trial'),
|
||||
helps: eventCount('help'),
|
||||
aiHints: weekHints[0]?.value ?? 0,
|
||||
},
|
||||
recent: {
|
||||
total: recent.length,
|
||||
wrongAnswer: tally(-1),
|
||||
|
||||
Reference in new issue
Block a user