fix: 应用 xhigh 代码评审的跨端修复
ci / server (push) Failing after 13s
ci / design-tokens (push) Failing after 11s

来自 xhigh code review 的正确性/健壮性修复,覆盖全部五端:
- server:鉴权 fail-closed、计量交叉校验与配额扣穿处理、WS 网关并发与关闭顺序、
  billing 行锁、redis Lua 过期与设备槽刷新、config 解析
- desktop:会话 epoch 防串话、WS 重连与 401 处理、api 客户端复用、统一 usePoll 轮询
- android:握手时序、请求头封装、账户状态派生、按需重组
- ios:finalize 宽限、串行采集、错误文案服务端优先、删除死代码 CommitController

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-13 11:50:08 +08:00
parent 50b49f3cbe
commit b5ab92a57e
42 changed files with 2125 additions and 469 deletions
+62 -36
View File
@@ -1,11 +1,13 @@
// 登录 / 购买窗口四步流:扫码登录 → 时长包 → 支付二维码 → 完成
// 规格见 doc/frontend-design.html 4.2;二维码内容来自后端,轮询自动推进。
import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import { createRoot } from 'react-dom/client';
import '@dudu/design/styles.css';
import { Button } from '@dudu/design/components/core/Button';
import { Badge } from '@dudu/design/components/core/Badge';
import { invoke } from '../shared/tauri';
import { setThemePref } from '../shared/theme';
import { usePoll } from '../shared/usePoll';
function Center({ children }) {
return (
@@ -32,47 +34,64 @@ function QrBox({ value }) {
function LoginApp() {
const [step, setStep] = useState('login'); // login | plans | pay | done
const [qr, setQr] = useState(null);
const [qrExpired, setQrExpired] = useState(false);
const [packs, setPacks] = useState([]);
const [sel, setSel] = useState('pack_500');
const [order, setOrder] = useState(null);
const [me, setMe] = useState(null);
const timer = useRef(null);
const [qrNonce, setQrNonce] = useState(0); // 递增以触发二维码重建(刷新)
// 扫码登录:创建二维码 + 轮询
// 启动应用主题:随系统/已存偏好(深色模式下不再停在浅色)。
useEffect(() => {
invoke('get_settings').then((s) => s && setThemePref(s.theme));
}, []);
// 扫码登录:创建二维码(进入 login 步或点击刷新时重建)。
useEffect(() => {
if (step !== 'login') return undefined;
let stop = false;
invoke('login_qr_create').then((r) => {
if (!r || stop) return;
setQr(r);
timer.current = setInterval(async () => {
const st = await invoke('login_qr_poll', { state: r.state });
if (st && st.status === 'confirmed') {
clearInterval(timer.current);
const m = await invoke('fetch_me');
setMe(m);
const ps = await invoke('fetch_packs');
setPacks(ps || []);
setStep('plans');
}
}, 1000);
});
return () => { stop = true; clearInterval(timer.current); };
}, [step]);
let live = true;
setQrExpired(false);
setQr(null);
invoke('login_qr_create').then((r) => { if (live && r) setQr(r); });
return () => { live = false; };
}, [step, qrNonce]);
// 支付轮询
useEffect(() => {
if (step !== 'pay' || !order) return undefined;
const t = setInterval(async () => {
const st = await invoke('order_status', { orderId: order.order_id });
if (st && st.status === 'paid') {
clearInterval(t);
setMe(await invoke('fetch_me'));
setStep('done');
}
}, 1000);
return () => clearInterval(t);
}, [step, order]);
// 扫码登录轮询:拿到 confirmed/expired 即停(不再永久 1Hz 打后端)。
usePoll(async (alive) => {
const st = await invoke('login_qr_poll', { state: qr.state });
if (!alive()) return 'stop';
if (st && st.status === 'confirmed') {
const m = await invoke('fetch_me');
const ps = await invoke('fetch_packs');
if (!alive()) return 'stop';
setMe(m);
setPacks(ps || []);
setStep('plans');
return 'stop';
}
// 后端 2 分钟 qrTTL 过期后返回 expired:停轮询并提示点击刷新。
if (st && st.status === 'expired') {
setQrExpired(true);
return 'stop';
}
return undefined;
}, 1000, [qr && qr.state], step === 'login' && !!qr && !qrExpired);
// 支付轮询:付款成功即停。
usePoll(async (alive) => {
const st = await invoke('order_status', { orderId: order.order_id });
if (!alive()) return 'stop';
if (st && st.status === 'paid') {
const m = await invoke('fetch_me');
if (!alive()) return 'stop';
setMe(m);
setStep('done');
return 'stop';
}
return undefined;
}, 1000, [order && order.order_id], step === 'pay' && !!order);
const refreshQr = () => setQrNonce((n) => n + 1);
const pack = packs.find((p) => p.id === sel);
@@ -80,8 +99,15 @@ function LoginApp() {
return (
<Center>
<div style={{ fontSize: 'var(--text-lg)', fontWeight: 'var(--weight-semibold)', color: 'var(--text-1)' }}>微信扫码登录</div>
<QrBox value={qr && qr.qr_url} />
<div style={{ fontSize: 'var(--text-sm)', color: 'var(--text-2)', textAlign: 'center' }}>打开微信扫一扫确认后自动登录</div>
<QrBox value={qrExpired ? '二维码已过期' : (qr && qr.qr_url)} />
{qrExpired ? (
<>
<div style={{ fontSize: 'var(--text-sm)', color: 'var(--text-2)', textAlign: 'center' }}>二维码已过期点击刷新重新扫码</div>
<Button variant="primary" size="sm" onClick={refreshQr}>刷新二维码</Button>
</>
) : (
<div style={{ fontSize: 'var(--text-sm)', color: 'var(--text-2)', textAlign: 'center' }}>打开微信扫一扫确认后自动登录</div>
)}
</Center>
);
}