积分与余额账务系统硬切重构
This commit is contained in:
@@ -12,6 +12,21 @@ import { apiMethods } from './modules/index.js'
|
||||
const API_ROOT = window.aksrtAdminData?.apiUrl || '/wp-json/aksrt/v1'
|
||||
const NONCE = window.aksrtAdminData?.nonce || ''
|
||||
|
||||
function createIdempotencyKey() {
|
||||
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
if (typeof crypto === 'undefined' || typeof crypto.getRandomValues !== 'function') {
|
||||
throw new Error('当前浏览器无法生成安全操作标识')
|
||||
}
|
||||
const bytes = new Uint8Array(16)
|
||||
crypto.getRandomValues(bytes)
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
||||
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')
|
||||
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
|
||||
}
|
||||
|
||||
function normalizeDomainKey(domain) {
|
||||
return String(domain || 'global').trim().replace(/-/g, '_')
|
||||
}
|
||||
@@ -85,6 +100,7 @@ function joinRestUrl(baseUrl, endpoint) {
|
||||
class ApiClient {
|
||||
constructor() {
|
||||
this.baseUrl = API_ROOT
|
||||
this.pendingIdempotencyKeys = new Map()
|
||||
}
|
||||
|
||||
normalizeDomainKey(domain) {
|
||||
@@ -138,11 +154,22 @@ class ApiClient {
|
||||
finalBody = JSON.stringify(body)
|
||||
}
|
||||
|
||||
const isWrite = method !== 'GET' && method !== 'HEAD'
|
||||
const suppliedKey = headers?.['Idempotency-Key'] || headers?.['idempotency-key'] || ''
|
||||
const idempotencySignature = isWrite ? `${method}:${url}:${finalBody ?? ''}` : ''
|
||||
const idempotencyKey = isWrite && !suppliedKey
|
||||
? (this.pendingIdempotencyKeys.get(idempotencySignature) || createIdempotencyKey())
|
||||
: suppliedKey
|
||||
if (isWrite && !suppliedKey) {
|
||||
this.pendingIdempotencyKeys.set(idempotencySignature, idempotencyKey)
|
||||
}
|
||||
|
||||
const config = {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': NONCE,
|
||||
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
|
||||
...headers,
|
||||
},
|
||||
...rest,
|
||||
@@ -174,6 +201,9 @@ class ApiClient {
|
||||
throw new Error(msg)
|
||||
}
|
||||
|
||||
if (idempotencySignature !== '') {
|
||||
this.pendingIdempotencyKeys.delete(idempotencySignature)
|
||||
}
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error(`[API Error] ${endpoint}:`, error)
|
||||
|
||||
@@ -28,17 +28,17 @@ export const usersApiMethods = {
|
||||
return this.request('/identity/settings/test', { method: 'POST', body: JSON.stringify(data) })
|
||||
},
|
||||
|
||||
getCreditSettings() { return this.request('/credits/settings') },
|
||||
saveCreditSettings(data) { return this.request('/credits/settings', { method: 'POST', body: JSON.stringify(data) }) },
|
||||
getCreditSettings() { return this.request('/assets/settings') },
|
||||
saveCreditSettings(data) { return this.request('/assets/settings', { method: 'POST', body: JSON.stringify(data) }) },
|
||||
listCreditUsers(params = {}) {
|
||||
const qs = new URLSearchParams(params).toString()
|
||||
return this.request('/credits/users' + (qs ? `?${qs}` : ''))
|
||||
return this.request('/assets/accounts' + (qs ? `?${qs}` : ''))
|
||||
},
|
||||
adjustUserCredits(id, delta, reason) {
|
||||
return this.request(`/credits/users/${id}/adjust`, { method: 'POST', body: JSON.stringify({ delta, reason }) })
|
||||
adjustUserCredits(id, asset, delta, remark) {
|
||||
return this.request(`/assets/accounts/${id}/adjust`, { method: 'POST', body: JSON.stringify({ asset, delta, remark }) })
|
||||
},
|
||||
getCreditLog(params = {}) {
|
||||
const qs = new URLSearchParams(params).toString()
|
||||
return this.request('/credits/log' + (qs ? `?${qs}` : ''))
|
||||
return this.request('/assets/transactions' + (qs ? `?${qs}` : ''))
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user