fix(ui,test): 说明巡检与重登的联动关系;修掉一条按分钟偶发失败的测试

看图时发现两个问题:

一、页面上那段说明是 P1a 时代的文案,已经过时且误导:「重登仍由人工在
「CK 检测与重登」页面操作」——P1b 上线后自动重登已存在。两个作业其实是联动的
上下游:巡检把账号标成 invalid,「CK 自动重登」的候选条件正是这个字段,所以
巡检一发现失效、重登作业下一周期就会接手。刻意分成两个作业是为了止损粒度
(巡检只读零风险;重登会真实登录,出问题可单独关掉)。据此改写说明文案,并在
两张卡片上标出「下游:CK 自动重登」/「上游:巡检标记为失效且有 CK 的账号」。

二、jobs 接口新增 pending_relogin:当前有多少条正等着自动重登。页面靠它把
上下游关系显性化 —— 没有这个数字,两个开关看起来像互不相干。计数走重登作业
自己的候选查询,保证「页面显示的待办」和「作业实际会捞的」是同一口径。

顺带修掉 tests/test_douyu_email_code_pick.py 里一条既有缺陷:它用
max(0, now.minute - 30) 构造「30 分钟前的邮件」,分钟数小于 30 时会退化成当前
整点(21:00:12 → "今天 21:00"),只比 now 早十几秒,于是断言随「当前几点」偶发
失败 —— 每小时前 30 分钟必红(已确认在我本次所有改动之前的提交上同样失败,
不是本次引入)。改成真实的时间减法,跨天时用「昨天」。

全量 298 通过。提交时另一路 SQLite→MySQL 的测试改造仍在同一工作区进行,
本次只暂存本功能相关文件。
This commit is contained in:
yml2213
2026-10-02 21:01:17 +08:00
parent 8fc8e623b9
commit ed6e186655
5 changed files with 68 additions and 5 deletions
+2
View File
@@ -883,6 +883,8 @@ export interface AutomationSchedulerStatus {
export interface AutomationJobsResponse {
scheduler: AutomationSchedulerStatus;
/** 巡检已标记失效、正等自动重登处理的条数(体现两个作业的上下游关系)。 */
pending_relogin: number;
jobs: AutomationJobItem[];
}
+16 -2
View File
@@ -45,6 +45,7 @@ export default function AutomationPage() {
const [runs, setRuns] = useState<AutomationRunItem[]>([]);
const [manualGroups, setManualGroups] = useState<AutomationManualGroup[]>([]);
const [manualTotal, setManualTotal] = useState(0);
const [pendingRelogin, setPendingRelogin] = useState(0);
const [loading, setLoading] = useState(false);
const [savingKey, setSavingKey] = useState<string | null>(null);
const [runningKey, setRunningKey] = useState<string | null>(null);
@@ -57,6 +58,7 @@ export default function AutomationPage() {
try {
const data = await automationApi.jobs();
setJobs(data.jobs);
setPendingRelogin(data.pending_relogin ?? 0);
setSchedulerEnabled(data.scheduler.enabled);
setSchedulerRunning(data.scheduler.running);
const history = await automationApi.runs({ limit: 50 });
@@ -226,8 +228,8 @@ export default function AutomationPage() {
type="info"
showIcon
style={{ marginBottom: 16 }}
message="巡检只检测、不改 CK"
description="每 5 分钟跑一轮,每轮按「每轮条数」上限挑最久没检测的账号;「单账号重检间隔」指同一个账号最多多久重新检测一次。本作业只标记失效账号,不会替换任何 Cookie、也不会改账号状态,重登仍由人工在「CK 检测与重登」页面操作。"
message="巡检只检测、不改 CK;发现失效后由「CK 自动重登」接手"
description="两个作业是联动的上下游:巡检把失效账号标记为 invalid,「CK 自动重登」的候选条件正是这个标记,所以巡检一发现失效,重登作业在下一个周期就会把它救回来(需打开重登开关)。刻意分成两个作业是为了止损粒度 —— 巡检只读、零风险;重登会真实登录(消耗邮箱验证码、可能遇到极验),出问题可单独关掉重登而不影响检测。巡检本身不会替换任何 Cookie、也不会改账号状态。"
/>
{jobs.map((job) => (
@@ -235,6 +237,18 @@ export default function AutomationPage() {
key={job.key}
type="inner"
title={job.title}
extra={
job.key === "ck_relogin" ? (
<Typography.Text type="secondary">
上游:巡检标记为失效且有 CK 的账号,当前待重登{" "}
<Typography.Text strong>{pendingRelogin}</Typography.Text> 个
</Typography.Text>
) : job.key === "ck_health" ? (
<Typography.Text type="secondary">
下游:CK 自动重登(发现失效后自动接手)
</Typography.Text>
) : null
}
style={{ marginBottom: 16 }}
>
<Descriptions size="small" column={2} style={{ marginBottom: 16 }}>