## 背景(用户反馈) 「精英宝典和电竞手册已开通的话,应该增加时间展示,可以看到开通时间和过期时间」 - 精英宝典:开通时间来自事件;过期时间 `elite_expire_time` 本来就有 - 电竞手册:开通时间来自事件;**过期时间原来没有落库** —— 只在 `_refresh_esports_handbook` 的返回值里带着,界面上只能看到「已开通」 ## 改动 - 新增 `accounts.esports_expire_time`(迁移 0049,与 `elite_expire_time` 对称), 刷新电竞手册时写回 - 详情页:**已开通才显示**「开通 x / 到期 x」两行小字;顺手删掉底部那段 重复的「开通时间」说明(已挪到每项旁边) - 新增 `formatUnixSeconds()`:斗鱼的到期时间是**秒级时间戳**, 原 `formatTime()` 只收字符串;新函数对 `0`/空/负数一律显示 `-` ## 明确回答:历史开通记录**查不到** 实测官方兑换记录里**只有商品兑换、没有开通记录**(账号 61:精英 2 条都是套装)。 我们自己的 `elite_opened` / `esports_opened` 事件也只有上线后的。 所以历史上的开通时间拿不回来 —— 按用户说的「没有就算了」, 并把这点写进界面说明,别再让人去找。 ## 测的时候抓出一个真 bug(又是 1970) 电竞到期时间的 `"0"`(斗鱼用它表示「没有到期时间」)被原样存成 `0` → 界面会显示 **1970/1/1**。已修(`0`/空 → `None`)并加回归用例 —— 这个坑在积分到期上踩过一次,这次差点重演。 ## 迁移 0048 改名 `20261004_0048_overview_summary.py` → `20261004_0048_high_value_owned.py`: 上一轮把内容从「汇总」收敛成单一用途的 `high_value_owned`,文件名没跟着改。 revision id 不变、迁移状态不受影响;同时清了 `__pycache__` (本仓库被过期 `.pyc` 咬过,症状是「改了没生效」)。 ## 验证(本地) `TZ=Asia/Shanghai` **608 passed / 6 skipped**;ruff 全绿;前端 build 通过、lint 0 errors。
50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
import dayjs from 'dayjs';
|
|
import utc from 'dayjs/plugin/utc';
|
|
import timezone from 'dayjs/plugin/timezone';
|
|
|
|
dayjs.extend(utc);
|
|
dayjs.extend(timezone);
|
|
|
|
/**
|
|
* 将后端返回的 ISO 时间字符串格式化为本地可读格式。
|
|
* 兼容带时区(如 2026-06-22T15:51:26+00:00 / 2026-06-22T15:51:26Z)与
|
|
* 不带时区后缀的 UTC 时间字符串(如 2026-06-22T15:51:26 / 2026-06-22 15:51:26),
|
|
* 自动转为本地时区并格式化为 "YYYY-MM-DD HH:mm:ss"。
|
|
*/
|
|
function normalizeUtcDateStr(val: string): string {
|
|
let raw = val.trim();
|
|
if (/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(raw)) {
|
|
raw = raw.replace(' ', 'T') + 'Z';
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
export function formatTime(val: string | null | undefined): string {
|
|
if (!val) return '-';
|
|
const d = dayjs(normalizeUtcDateStr(val));
|
|
return d.isValid() ? d.format('YYYY-MM-DD HH:mm:ss') : val;
|
|
}
|
|
|
|
/**
|
|
* 简短格式,仅显示月-日 时:分
|
|
*/
|
|
export function formatTimeShort(val: string | null | undefined): string {
|
|
if (!val) return '-';
|
|
const d = dayjs(normalizeUtcDateStr(val));
|
|
return d.isValid() ? d.format('MM-DD HH:mm') : val;
|
|
}
|
|
|
|
/**
|
|
* 斗鱼给的**秒级时间戳** → 可读文本。
|
|
*
|
|
* ⚠️ `0` / `null` / 负数 一律显示 `-`:斗鱼用 0 表示「没有这个时间」,
|
|
* 而 `new Date(0)` 是 **1970/1/1** —— 直接格式化会让运营以为
|
|
* "到期时间是 1970 年"(这个 bug 真出现过)。
|
|
*/
|
|
export function formatUnixSeconds(val: number | string | null | undefined): string {
|
|
if (val === null || val === undefined || val === '') return '-';
|
|
const seconds = typeof val === 'number' ? val : Number(val);
|
|
if (!Number.isFinite(seconds) || seconds <= 0) return '-';
|
|
return formatTime(new Date(seconds * 1000).toISOString());
|
|
}
|