From b4c0f89291fffac65862d7d4b37b67fa8d731277 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Sat, 19 Sep 2026 07:07:57 -0600 Subject: [PATCH] =?UTF-8?q?fix(=E4=B8=AA=E4=BA=BA=E4=B8=BB=E9=A1=B5):=20?= =?UTF-8?q?=E6=80=BB=E5=85=B1=E5=AD=A6=E4=B9=A0=E5=A4=A9=E6=95=B0=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E6=9C=89=E6=8F=90=E4=BA=A4=E7=9A=84=E5=A4=A9=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原来是首末提交之间跨了多久,现在按东八区日期对提交去重计数,比赛提交也算。 首末提交时间仍只看比赛外的提交,口径不变。 Co-Authored-By: Claude Opus 5 --- apps/api/src/routes/account.ts | 26 +++++++++++++++----------- apps/web/src/oj/user/index.vue | 2 +- packages/contract/src/account.ts | 2 ++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/api/src/routes/account.ts b/apps/api/src/routes/account.ts index 74cc556..6423708 100644 --- a/apps/api/src/routes/account.ts +++ b/apps/api/src/routes/account.ts @@ -46,7 +46,7 @@ import { failure, success } from "../http" import { JudgeStatus } from "../judge/status" import { getBooleanOption } from "../services/options" import { getUserProfileById } from "../services/profile" -import { weekStart } from "../time" +import { localTime, weekStart } from "../time" import { isTeacherOrAbove, objectValue, @@ -196,25 +196,29 @@ accountRoutes.post("/me/avatar", requireAuth, async (c) => { accountRoutes.get("/users/:id/metrics", async (c) => { const userId = queryInteger(c.req.param("id"), 0, { min: 1 }) + // 学习天数连比赛提交一起算;首末提交时间照旧只看比赛外的提交 + const outsideContest = sql`filter (where ${schema.submission.contestId} is null)` const [row] = await db .select({ - total: count(), - first: min(schema.submission.createTime), - latest: sql`max(${schema.submission.createTime})`, + first: sql< + string | null + >`min(${schema.submission.createTime}) ${outsideContest}`, + latest: sql< + string | null + >`max(${schema.submission.createTime}) ${outsideContest}`, + activeDays: countDistinct( + sql`date(${localTime(schema.submission.createTime)})`, + ), }) .from(schema.submission) - .where( - and( - eq(schema.submission.userId, userId), - isNull(schema.submission.contestId), - ), - ) - if (!row?.total || !row.first || !row.latest) + .where(eq(schema.submission.userId, userId)) + if (!row?.first || !row.latest) return failure(c, 404, "no-submissions", "暂无提交") return success(c, { now: new Date().toISOString(), first: row.first, latest: row.latest, + activeDays: row.activeDays, } satisfies Metrics) }) diff --git a/apps/web/src/oj/user/index.vue b/apps/web/src/oj/user/index.vue index 37c0186..746f656 100644 --- a/apps/web/src/oj/user/index.vue +++ b/apps/web/src/oj/user/index.vue @@ -81,7 +81,7 @@ async function init() { firstSubmissionAt.value = parseTime(metricsRes.first) latestSubmissionAt.value = parseTime(metricsRes.latest) toLatestAt.value = durationToDays(metricsRes.latest, metricsRes.now) - learnDuration.value = durationToDays(metricsRes.first, metricsRes.latest) + learnDuration.value = `${metricsRes.activeDays} 天` } } finally { toggle(false) diff --git a/packages/contract/src/account.ts b/packages/contract/src/account.ts index 04ef278..8abd4d0 100644 --- a/packages/contract/src/account.ts +++ b/packages/contract/src/account.ts @@ -19,6 +19,8 @@ export const metricsSchema = z.object({ now: z.string(), latest: z.string(), first: z.string(), + /** 有提交的日历天数(东八区),不是首末提交之间跨了多少天 */ + activeDays: z.number().int(), }) export const rankProfileSchema = z.object({