This commit is contained in:
yml
2026-04-08 16:30:42 +08:00
commit 313c036845
131 changed files with 22393 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
const ADMIN_TOKEN_KEY = 'order-site-admin-token'
const ADMIN_EXPIRES_AT_KEY = 'order-site-admin-token-expires-at'
const ADMIN_USER_ID_KEY = 'order-site-admin-user-id'
const ADMIN_USERNAME_KEY = 'order-site-admin-username'
const ADMIN_ROLE_KEY = 'order-site-admin-role'
export function getAdminToken() {
return localStorage.getItem(ADMIN_TOKEN_KEY) || ''
}
export function getAdminTokenExpiresAt() {
return localStorage.getItem(ADMIN_EXPIRES_AT_KEY) || ''
}
export function getAdminUserId() {
const value = Number(localStorage.getItem(ADMIN_USER_ID_KEY) || 0)
return Number.isFinite(value) && value > 0 ? value : 0
}
export function getAdminUsername() {
return localStorage.getItem(ADMIN_USERNAME_KEY) || ''
}
export function getAdminRole() {
const role = localStorage.getItem(ADMIN_ROLE_KEY)
return role === 'admin' ? 'admin' : 'operator'
}
export function hasAdminRole(role: 'admin' | 'operator') {
if (role === 'operator') {
return Boolean(getAdminToken())
}
return getAdminRole() === 'admin'
}
export function setAdminSession(
token: string,
expiresAt: string,
user?: { userId: number; username: string; role: 'admin' | 'operator' },
) {
localStorage.setItem(ADMIN_TOKEN_KEY, token)
localStorage.setItem(ADMIN_EXPIRES_AT_KEY, expiresAt)
if (user?.userId) {
localStorage.setItem(ADMIN_USER_ID_KEY, String(user.userId))
}
if (user?.username) {
localStorage.setItem(ADMIN_USERNAME_KEY, user.username)
}
if (user?.role) {
localStorage.setItem(ADMIN_ROLE_KEY, user.role)
}
}
export function clearAdminSession() {
localStorage.removeItem(ADMIN_TOKEN_KEY)
localStorage.removeItem(ADMIN_EXPIRES_AT_KEY)
localStorage.removeItem(ADMIN_USER_ID_KEY)
localStorage.removeItem(ADMIN_USERNAME_KEY)
localStorage.removeItem(ADMIN_ROLE_KEY)
}
export function hasAdminSession() {
return Boolean(getAdminToken())
}
+45
View File
@@ -0,0 +1,45 @@
export function formatStatusWithRaw(status: string, labelMap: Record<string, string> = {}) {
const normalized = String(status || '').trim()
if (!normalized) {
return '-'
}
const key = normalized.toLowerCase()
const label = labelMap[key]
if (!label || label === normalized) {
return normalized
}
return `${label} (${normalized})`
}
export function formatAuditAction(action: string) {
const labelMap: Record<string, string> = {
admin_user_created: '创建后台用户',
admin_user_role_updated: '修改用户角色',
admin_user_status_updated: '修改用户状态',
admin_user_password_reset: '重置用户密码',
task_release_cdk: '释放任务 CDK',
task_regenerate_claim_link: '重发领取链接',
task_closed: '关闭任务',
task_mark_manual_review: '转人工处理',
inventory_cdk_released: '释放库存 CDK',
inventory_cdk_invalidated: '作废库存 CDK',
webhook_replayed: '重放 Webhook',
}
return formatStatusWithRaw(action, labelMap)
}
export function formatAuditTargetType(targetType: string) {
const labelMap: Record<string, string> = {
admin_user: '后台用户',
task: '交付任务',
cdk: 'CDK 库存',
webhook_event: 'Webhook 事件',
}
return formatStatusWithRaw(targetType, labelMap)
}
+73
View File
@@ -0,0 +1,73 @@
export const adminPayStatusOptions = [
{ label: '全部', value: '' },
{ label: '未支付', value: 'unpaid' },
{ label: '已支付', value: 'paid' },
{ label: '失败', value: 'failed' },
{ label: '已退款', value: 'refunded' },
]
export const adminTaskStatusOptions = [
{ label: '全部', value: '' },
{ label: '待支付', value: 'pending_payment' },
{ label: '已支付', value: 'paid' },
{ label: '待库存', value: 'waiting_inventory' },
{ label: '已预占', value: 'cdk_reserved' },
{ label: '已生成链接', value: 'link_generated' },
{ label: '已创建会话', value: 'claimed' },
{ label: '已确认角色', value: 'role_confirmed' },
{ label: '兑换中', value: 'redeeming' },
{ label: '已兑换', value: 'redeemed' },
{ label: '待重试', value: 'retry_pending' },
{ label: '人工处理', value: 'manual_review' },
{ label: '已过期', value: 'expired' },
{ label: '已关闭', value: 'closed' },
]
export const adminCdkStatusOptions = [
{ label: '全部', value: '' },
{ label: '可用', value: 'available' },
{ label: '已预占', value: 'reserved' },
{ label: '已发放', value: 'delivered' },
{ label: '已作废', value: 'invalid' },
]
export const adminWebhookProcessedOptions = [
{ label: '全部', value: '' },
{ label: '处理成功', value: '1' },
{ label: '处理失败', value: '0' },
]
export const adminUserRoleOptions = [
{ label: '全部', value: '' },
{ label: '管理员', value: 'admin' },
{ label: '普通运营', value: 'operator' },
]
export const adminUserStatusOptions = [
{ label: '全部', value: '' },
{ label: '生效中', value: 'active' },
{ label: '已停用', value: 'disabled' },
]
export const adminAuditActionOptions = [
{ label: '全部', value: '' },
{ label: '创建后台用户', value: 'admin_user_created' },
{ label: '修改用户角色', value: 'admin_user_role_updated' },
{ label: '修改用户状态', value: 'admin_user_status_updated' },
{ label: '重置用户密码', value: 'admin_user_password_reset' },
{ label: '释放任务 CDK', value: 'task_release_cdk' },
{ label: '重发领取链接', value: 'task_regenerate_claim_link' },
{ label: '关闭任务', value: 'task_closed' },
{ label: '转人工处理', value: 'task_mark_manual_review' },
{ label: '释放库存 CDK', value: 'inventory_cdk_released' },
{ label: '作废库存 CDK', value: 'inventory_cdk_invalidated' },
{ label: '重放 Webhook', value: 'webhook_replayed' },
]
export const adminAuditTargetTypeOptions = [
{ label: '全部', value: '' },
{ label: '后台用户', value: 'admin_user' },
{ label: '交付任务', value: 'task' },
{ label: 'CDK 库存', value: 'cdk' },
{ label: 'Webhook 事件', value: 'webhook_event' },
]
+1
View File
@@ -0,0 +1 @@
export { formatDateTime as formatAdminDateTime } from '@/utils/date-time'
+22
View File
@@ -0,0 +1,22 @@
import { formatStatusWithRaw } from '@/utils/admin-display'
export function formatAdminWebhookEventType(eventType: string) {
const normalized = String(eventType || '').trim()
if (!normalized) {
return '-'
}
const labelMap: Record<string, string> = {
payment_success: '支付成功',
trade_create: '订单创建',
trade_closed: '订单关闭',
buyer_confirm_goods: '确认收货',
auto_delivery_done: '自动发货完成',
auto_delivery_confirmed: '自动发货确认完成',
refund_apply: '买家申请退款',
trade_event: '交易事件',
}
return formatStatusWithRaw(normalized, labelMap)
}
+51
View File
@@ -0,0 +1,51 @@
const ISO_DATE_TIME_PATTERN =
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/
export function isIsoDateTimeString(value: unknown): value is string {
return typeof value === 'string' && ISO_DATE_TIME_PATTERN.test(value.trim())
}
export function formatDateTime(value: string | null | undefined, fallback = '-') {
const normalized = String(value || '').trim()
if (!normalized) {
return fallback
}
const date = new Date(normalized)
if (Number.isNaN(date.getTime())) {
return normalized.replace('T', ' ').replace('Z', '')
}
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
export function normalizeDateTimeValue(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map((item) => normalizeDateTimeValue(item))
}
if (value && typeof value === 'object') {
return Object.fromEntries(
Object.entries(value).map(([key, currentValue]) => [key, normalizeDateTimeValue(currentValue)]),
)
}
if (isIsoDateTimeString(value)) {
return formatDateTime(value, '')
}
return value
}
export function stringifyDisplayJson(value: unknown, space = 2) {
return JSON.stringify(normalizeDateTimeValue(value), null, space)
}