Files
creator-hub/web/src/utils/helpers.ts
T
rogee f1a62e25de
douyin-release-gate / verify (push) Failing after 4m22s
feat(account): create browser environments before verified login
2026-10-06 05:55:53 +08:00

107 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 公共展示工具:字段语义逐一对照 web.archived/src/shared/ui/ui.jsx
export function dateTime(value?: string | number | null): string {
if (!value) return '—';
const date = new Date(value);
return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString();
}
// 优先展示后端具体 reason;409/503 冲突类错误即使无 hint 也直接透出后端
// 分类文案(如「该竞品正在同步中」),避免接口报错时界面无任何反馈。
// 409 时若后端附带 reason_code(如 start/resume 的就绪原因),追加展示帮助定位。
export function conflictMessage(error: any, conflictHint?: string): string {
const status = error?.status ?? error?.body?.status;
const message = error?.body?.reason || error?.body?.error || error?.message || '请求失败';
const reason = error?.body?.reason_code;
if (status === 409 && conflictHint) return reason ? `${message}:${conflictHint}(${reason})` : `${message}:${conflictHint}`;
if ((status === 409 || status === 503) && message === '请求失败') {
if (conflictHint) return reason ? `${conflictHint}(${reason})` : conflictHint;
return reason ? `请求失败(409,${reason}),请稍后重试` : `请求失败(${status}),请稍后重试`;
}
if (reason && status === 409) return `${message}(${reason})`;
return message;
}
export const platforms = [
{ value: 'douyin', label: '抖音' },
];
export function platformLabel(value?: string): string {
return platforms.find((item) => item.value === value)?.label || value || '—';
}
export const shareJobStatus: Record<string, { label: string; tone: 'success' | 'warning' | 'error' | 'default' }> = {
queued: { label: '待解析', tone: 'warning' },
processing: { label: '解析中', tone: 'warning' },
succeeded: { label: '已加入监控', tone: 'success' },
failed: { label: '解析失败', tone: 'error' },
};
// 对齐归档版 extractShareURL / platformForShareURL
const shareURLPattern = /https?:\/\/[^\s<>"'`]+/giu;
const trailingShareURLPunctuation = /[,。;!?、)》)】\]}>,.?!:;]+$/u;
export function platformForShareURL(value: string): string {
try {
const hostname = new URL(value).hostname.toLowerCase();
if (['www.douyin.com', 'v.douyin.com'].includes(hostname)) return 'douyin';
} catch {
return '';
}
return '';
}
export function extractShareURL(value?: string): string {
const candidates = String(value || '')
.match(shareURLPattern)
?.map((candidate) => candidate.replace(trailingShareURLPunctuation, ''))
.filter(Boolean);
return candidates?.find((candidate) => platformForShareURL(candidate)) || candidates?.[0] || '';
}
// 账号可恢复性判定(对齐归档版 accountReadiness):绑定、出口健康、账号暂停三层。
export const readinessReasonText: Record<string, string> = {
account_paused: '账号已暂停',
binding_missing: '未绑定运行环境',
network_exit_missing: '未绑定固定出口',
network_exit_unhealthy: '固定出口不健康',
runtime_stop_pending: '运行环境停止结果待确认',
runtime_missing: '运行环境未启动',
runtime_active: '仍有活动运行实例',
account_conflict: '账号状态已变化,请刷新后重试',
environment_unavailable: '环境状态未知',
};
export interface AccountReadiness {
label: string;
reason: string;
canResume: boolean;
ready: boolean;
}
export function accountReadiness(account: any, binding: any, bindingError: any = null): AccountReadiness {
if (bindingError) return { label: '环境状态未知', reason: 'environment_unavailable', canResume: false, ready: false };
if (!binding) return { label: '未绑定运行环境', reason: 'binding_missing', canResume: false, ready: false };
let blocked = '';
if (binding.runtime_cleanup_pending) {
blocked = 'runtime_stop_pending';
} else if (binding.network_exit_id && binding.network_exit_health !== 'healthy') {
blocked = 'network_exit_unhealthy';
}
if (blocked) return { label: readinessReasonText[blocked], reason: blocked, canResume: false, ready: false };
if (account.runtime_status === 'paused') {
const stopped = !binding.runtime_id;
return { label: stopped ? '资源就绪,可恢复' : '等待运行环境停止', reason: stopped ? '' : 'runtime_stop_pending', canResume: stopped, ready: false };
}
const schedule = binding.schedule_block_reason || '';
return {
label: schedule ? readinessReasonText[schedule] || `不可调度:${schedule}` : '可调度',
reason: schedule,
canResume: false,
ready: !schedule,
};
}
export function isCollectionAccount(account: any): boolean {
return ['normal', 'muted'].includes(account.business_status) && account.login_status === 'logged_in';
}