fix(sid): harden admin assignment flow

This commit is contained in:
2026-09-08 13:01:41 +08:00
parent f243b7180b
commit c2dec1e711
2 changed files with 56 additions and 18 deletions
+12 -12
View File
@@ -50,7 +50,7 @@ export interface paths {
content: { content: {
"application/json": { "application/json": {
/** /**
* @example archived * @example all
* @enum {string|null} * @enum {string|null}
*/ */
state?: "active" | "archived" | "trashed" | "all" | null; state?: "active" | "archived" | "trashed" | "all" | null;
@@ -539,7 +539,7 @@ export interface paths {
*/ */
language_code?: string; language_code?: string;
/** /**
* @example draft * @example published
* @enum {string} * @enum {string}
*/ */
status?: "draft" | "awaiting_publish" | "published"; status?: "draft" | "awaiting_publish" | "published";
@@ -4685,7 +4685,7 @@ export interface paths {
content: { content: {
"application/json": { "application/json": {
/** /**
* @example viewed * @example taken_by_other
* @enum {string|null} * @enum {string|null}
*/ */
status?: "sent" | "delivered" | "viewed" | "accepted" | "rejected" | "ignored" | "expired" | "taken_by_other" | null; status?: "sent" | "delivered" | "viewed" | "accepted" | "rejected" | "ignored" | "expired" | "taken_by_other" | null;
@@ -5163,7 +5163,7 @@ export interface paths {
content: { content: {
"application/json": { "application/json": {
/** /**
* @example inactive * @example busy
* @enum {string|null} * @enum {string|null}
*/ */
status?: "offline" | "online" | "inactive" | "busy" | null; status?: "offline" | "online" | "inactive" | "busy" | null;
@@ -5448,12 +5448,12 @@ export interface paths {
content: { content: {
"application/json": { "application/json": {
/** /**
* @example private * @example friends
* @enum {string|null} * @enum {string|null}
*/ */
visibility?: "public" | "friends" | "private" | null; visibility?: "public" | "friends" | "private" | null;
/** /**
* @example blocked * @example visible
* @enum {string|null} * @enum {string|null}
*/ */
status?: "blocked" | "visible" | "all" | null; status?: "blocked" | "visible" | "all" | null;
@@ -5779,7 +5779,7 @@ export interface paths {
content: { content: {
"application/json": { "application/json": {
/** /**
* @example all * @example pending
* @enum {string|null} * @enum {string|null}
*/ */
status?: "pending" | "blocked" | "all" | null; status?: "pending" | "blocked" | "all" | null;
@@ -9744,7 +9744,7 @@ export interface paths {
content: { content: {
"application/json": { "application/json": {
/** /**
* @example all * @example blocked
* @enum {string|null} * @enum {string|null}
*/ */
status?: "pending" | "blocked" | "all" | null; status?: "pending" | "blocked" | "all" | null;
@@ -10107,11 +10107,11 @@ export interface paths {
content: { content: {
"application/json": { "application/json": {
/** /**
* @example web * @example android
* @enum {string|null} * @enum {string|null}
*/ */
platform?: "ios" | "android" | "web" | "desktop" | null; platform?: "ios" | "android" | "web" | "desktop" | null;
/** @example false */ /** @example true */
is_online?: boolean | null; is_online?: boolean | null;
/** /**
* @description Must be at least 1. * @description Must be at least 1.
@@ -11170,7 +11170,7 @@ export interface operations {
content: { content: {
"application/json": { "application/json": {
/** /**
* @description SID 值,支持带/不带横杠。 * @description SID 值,格式为 3 位非歧义字母加 6 位数字,支持带/不带横杠。
* @example ABC-888888 * @example ABC-888888
*/ */
sid: string; sid: string;
@@ -11214,7 +11214,7 @@ export interface operations {
*/ */
user_uid: string; user_uid: string;
/** /**
* @description SID 值,支持带/不带横杠。 * @description SID 值,格式为 3 位非歧义字母加 6 位数字,支持带/不带横杠。
* @example ABC-168888 * @example ABC-168888
*/ */
sid: string; sid: string;
+44 -6
View File
@@ -70,6 +70,18 @@ function RulesTab() {
} }
} }
if (rules.isPending) return <p className="text-sm text-muted-foreground">正在加载保留规则…</p>
if (rules.isError) {
return (
<div className="text-sm text-destructive">
<p>保留规则加载失败。</p>
<Button className="mt-2" variant="outline" size="sm" onClick={() => void rules.refetch()}>
重新加载
</Button>
</div>
)
}
return ( return (
<div className="grid gap-3 md:grid-cols-2"> <div className="grid gap-3 md:grid-cols-2">
{(rules.data?.data ?? []).map((rule) => ( {(rules.data?.data ?? []).map((rule) => (
@@ -131,6 +143,18 @@ function LuckyTab() {
} }
} }
if (numbers.isPending) return <p className="text-sm text-muted-foreground">正在加载幸运号码…</p>
if (numbers.isError) {
return (
<div className="text-sm text-destructive">
<p>幸运号码加载失败。</p>
<Button className="mt-2" variant="outline" size="sm" onClick={() => void numbers.refetch()}>
重新加载
</Button>
</div>
)
}
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="flex flex-wrap items-end gap-3"> <div className="flex flex-wrap items-end gap-3">
@@ -140,8 +164,10 @@ function LuckyTab() {
id="lucky-number" id="lucky-number"
className="w-32 font-mono" className="w-32 font-mono"
maxLength={3} maxLength={3}
inputMode="numeric"
pattern="[0-9]{3}"
value={draft.number} value={draft.number}
onChange={(e) => setDraft((d) => ({ ...d, number: e.target.value }))} onChange={(e) => setDraft((d) => ({ ...d, number: e.target.value.replace(/\D/g, '') }))}
placeholder="888" placeholder="888"
/> />
</div> </div>
@@ -198,6 +224,8 @@ function AssignTab() {
const [checkResult, setCheckResult] = useState<SidCheckResult | null>(null) const [checkResult, setCheckResult] = useState<SidCheckResult | null>(null)
const [userUid, setUserUid] = useState('') const [userUid, setUserUid] = useState('')
const [busy, setBusy] = useState(false) const [busy, setBusy] = useState(false)
const validSid = /^[A-HJ-NPR-Z]{3}-?\d{6}$/i.test(sid)
const validUid = /^[0-9A-HJKMNP-TV-Z]{26}$/i.test(userUid)
const check = async () => { const check = async () => {
setBusy(true) setBusy(true)
@@ -238,11 +266,14 @@ function AssignTab() {
id="sid-input" id="sid-input"
className="font-mono" className="font-mono"
value={sid} value={sid}
onChange={(e) => setSid(e.target.value)} onChange={(e) => {
placeholder="668888" setSid(e.target.value.toUpperCase())
setCheckResult(null)
}}
placeholder="ABC-668888"
/> />
</div> </div>
<Button variant="outline" onClick={check} disabled={!sid || busy}> <Button variant="outline" onClick={check} disabled={!validSid || busy}>
查询 查询
</Button> </Button>
</div> </div>
@@ -270,14 +301,21 @@ function AssignTab() {
<Input <Input
id="assign-uid" id="assign-uid"
className="font-mono" className="font-mono"
maxLength={26}
value={userUid} value={userUid}
onChange={(e) => setUserUid(e.target.value)} onChange={(e) => setUserUid(e.target.value.toUpperCase())}
placeholder="01JXXXXXXXXXXXXXXXXXXXXXXX" placeholder="01JXXXXXXXXXXXXXXXXXXXXXXX"
/> />
</div> </div>
<Button <Button
onClick={assign} onClick={assign}
disabled={!sid || !userUid || busy || (checkResult?.reserved === true && !canOverride)} disabled={
!validSid ||
!validUid ||
!checkResult ||
busy ||
(checkResult.reserved && !canOverride)
}
> >
分配 SID 分配 SID
</Button> </Button>