Deploy / deploy (push) Canceled after 0s
只改界面文字和注释:网址 /problemset、接口、表名、字段名都不动,老链接照常能打开。 设计稿页名「题单重设计」和已执行的迁移文件保留原样。 线上 3 个成就(题单收藏家 / 入门 / 通关)已同步改名。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
import { z } from 'zod'
|
|
|
|
export const achievementRaritySchema = z.enum(['bronze', 'silver', 'gold', 'platinum'])
|
|
|
|
export const achievementSchema = z.object({
|
|
id: z.number().int(),
|
|
name: z.string(),
|
|
description: z.string(),
|
|
icon: z.string(),
|
|
rarity: achievementRaritySchema,
|
|
hidden: z.boolean(),
|
|
metric: z.string().nullable(),
|
|
operator: z.enum(['gte', 'lte']).nullable(),
|
|
threshold: z.number().int().nullable(),
|
|
unlocked: z.boolean(),
|
|
unlockTime: z.string().nullable(),
|
|
backfilled: z.boolean(),
|
|
/** 连续几周那一串给的是「现在连着几周」,不是历史最长 —— 要拿下一档得从现在数起 */
|
|
progress: z.number().nullable(),
|
|
/** 全站几人有 */
|
|
holders: z.number().int(),
|
|
/** 本班(被看的人的班)几人有;没有班级是 null */
|
|
classHolders: z.number().int().nullable(),
|
|
})
|
|
|
|
/** 「本班」那张卡:只在看自己、而且有班级时给 */
|
|
export const achievementClassBoardSchema = z.object({
|
|
className: z.string(),
|
|
/** 本班最近拿到的(补发的不算),新的在前 */
|
|
recent: z.array(
|
|
z.object({
|
|
username: z.string(),
|
|
achievementId: z.number().int(),
|
|
unlockTime: z.string(),
|
|
}),
|
|
),
|
|
/** 本班成就最多的前几名;一样多的名次相同,先拿到的排前面 */
|
|
most: z.array(
|
|
z.object({
|
|
username: z.string(),
|
|
count: z.number().int(),
|
|
rank: z.number().int(),
|
|
}),
|
|
),
|
|
/** 自己不在 most 里时的名次;不计入排名的人是 null */
|
|
me: z.object({ rank: z.number().int(), count: z.number().int() }).nullable(),
|
|
})
|
|
|
|
export const achievementListSchema = z.object({
|
|
username: z.string(),
|
|
achievements: z.array(achievementSchema),
|
|
classBoard: achievementClassBoardSchema.nullable(),
|
|
})
|
|
|
|
export const pendingAchievementSchema = z.object({
|
|
id: z.number().int(),
|
|
name: z.string(),
|
|
description: z.string(),
|
|
icon: z.string(),
|
|
rarity: achievementRaritySchema,
|
|
})
|
|
|
|
/**
|
|
* WebSocket 推来的解锁通知。比 `/achievements/pending` 多一个 `kind` ——
|
|
* 专题奖章和成就来自两张表、id 会重叠,前端靠它区分(见 events.ts 的 publishAchievementNotification)。
|
|
*/
|
|
export const achievementNotificationSchema = pendingAchievementSchema.extend({
|
|
kind: z.enum(['achievement', 'badge']),
|
|
})
|
|
|
|
export const markAchievementsReadSchema = z.object({
|
|
ids: z.array(z.number().int()),
|
|
})
|
|
|
|
export type Achievement = z.infer<typeof achievementSchema>
|
|
export type AchievementList = z.infer<typeof achievementListSchema>
|
|
export type PendingAchievement = z.infer<typeof pendingAchievementSchema>
|
|
export type AchievementClassBoard = z.infer<typeof achievementClassBoardSchema>
|
|
export type AchievementRarity = z.infer<typeof achievementRaritySchema>
|
|
export type AchievementNotification = z.infer<typeof achievementNotificationSchema>
|
|
|
|
export type MarkAchievementsRead = z.infer<typeof markAchievementsReadSchema>
|