feat(ECR-006): 落地运营后台 Phase A(admin API + admin-h5)
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s

新增独立鉴权的 /api/v1/admin 与 Vue 控制台;会员授予与审计同事务,并补集成/单测。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-06 18:35:53 +08:00
co-authored by Cursor
parent 4a583c9480
commit 879bf70cb7
59 changed files with 2462 additions and 20 deletions
+95
View File
@@ -0,0 +1,95 @@
/** Thin admin API client → /api/v1/admin (proxied to Go). */
export type ApiEnvelope<T> = { code: number; message: string; data?: T }
const TOKEN_KEY = 'yuxingu_admin_token'
export function getToken(): string | null {
return localStorage.getItem(TOKEN_KEY)
}
export function setToken(token: string | null) {
if (token) localStorage.setItem(TOKEN_KEY, token)
else localStorage.removeItem(TOKEN_KEY)
}
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
const headers: Record<string, string> = { Accept: 'application/json' }
if (body !== undefined) headers['Content-Type'] = 'application/json'
const token = getToken()
if (token) headers.Authorization = `Bearer ${token}`
const res = await fetch(`/api/v1/admin${path}`, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
})
const env = (await res.json()) as ApiEnvelope<T>
if (!res.ok || env.code !== 0) {
throw new Error(env.message || `HTTP ${res.status}`)
}
return env.data as T
}
export const adminApi = {
login: (username: string, password: string) =>
request<{ token: string; admin: { id: string; username: string } }>('POST', '/auth/login', {
username,
password,
}),
logout: () => request<{ ok: boolean }>('POST', '/auth/logout'),
me: () => request<{ id: string; username: string }>('GET', '/me'),
users: (q = '') =>
request<{ items: Array<{ id: string; status: string; created_at: string }> }>(
'GET',
`/users?q=${encodeURIComponent(q)}`,
),
user: (id: string) => request<UserDetail>('GET', `/users/${id}`),
grant: (id: string, plan: string) =>
request<{ ok: boolean }>('POST', `/users/${id}/membership/grant`, { plan }),
orders: () =>
request<{
items: Array<{
id: string
user_id: string
kind: string
plan?: string
amount_cents: number
status: string
created_at: string
}>
}>('GET', '/orders'),
audit: () =>
request<{
items: Array<{
id: string
admin_id: string
action: string
target_type: string
target_id: string
meta: unknown
created_at: string
}>
}>('GET', '/audit-logs'),
}
export type UserDetail = {
id: string
status: string
created_at: string
profiles: Array<{ id: string; relation: string; display_name: string }>
membership: {
active: boolean
plan?: string
status: string
expires_at?: string
ask_quota_left?: number
}
recent_orders: Array<{
id: string
kind: string
plan?: string
status: string
amount_cents: number
created_at: string
}>
}