feat(M08-B): 建立保洁管理后台
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import type {
|
||||
CleaningSettlement,
|
||||
CleaningTask,
|
||||
PageResult,
|
||||
SettlementStatus,
|
||||
TaskStatus,
|
||||
TransferMode
|
||||
} from './types';
|
||||
|
||||
const API_BASE = (import.meta.env.VITE_ADMIN_API_BASE_URL || '/admin-api').replace(/\/$/, '');
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
public readonly code: string,
|
||||
message = code,
|
||||
public readonly traceId = ''
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ApiSession {
|
||||
token: string;
|
||||
}
|
||||
|
||||
async function request<T>(
|
||||
session: ApiSession,
|
||||
path: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
if (!session.token.trim()) {
|
||||
throw new ApiError('AUTH_TOKEN_REQUIRED', '需要先填入后台访问令牌');
|
||||
}
|
||||
const headers = new Headers(options.headers);
|
||||
headers.set('authorization', `Bearer ${session.token.trim()}`);
|
||||
if (options.body && !headers.has('content-type')) {
|
||||
headers.set('content-type', 'application/json');
|
||||
}
|
||||
const response = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok || payload.code !== 0) {
|
||||
throw new ApiError(
|
||||
String(payload.code || `HTTP_${response.status}`),
|
||||
String(payload.message || '请求失败'),
|
||||
String(payload.traceId || '')
|
||||
);
|
||||
}
|
||||
return payload.data as T;
|
||||
}
|
||||
|
||||
export function listCleaningTasks(
|
||||
session: ApiSession,
|
||||
input: { page: number; pageSize: number; status?: TaskStatus }
|
||||
) {
|
||||
const params = new URLSearchParams({
|
||||
page: String(input.page),
|
||||
pageSize: String(input.pageSize)
|
||||
});
|
||||
if (input.status) params.set('status', input.status);
|
||||
return request<PageResult<CleaningTask>>(session, `/cleaning/tasks?${params}`);
|
||||
}
|
||||
|
||||
export function assignCleaningTask(
|
||||
session: ApiSession,
|
||||
taskId: string,
|
||||
input: { cleanerUserId: string; note?: string }
|
||||
) {
|
||||
return request<CleaningTask>(session, `/cleaning/tasks/${encodeURIComponent(taskId)}/assign`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
});
|
||||
}
|
||||
|
||||
export function completeCleaningTask(session: ApiSession, taskId: string, note?: string) {
|
||||
return request<CleaningTask>(session, `/cleaning/tasks/${encodeURIComponent(taskId)}/complete`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ note })
|
||||
});
|
||||
}
|
||||
|
||||
export function rejectCleaningTask(session: ApiSession, taskId: string, reason: string) {
|
||||
return request<CleaningTask>(session, `/cleaning/tasks/${encodeURIComponent(taskId)}/reject`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ reason })
|
||||
});
|
||||
}
|
||||
|
||||
export function reclaimCleaningTimeouts(
|
||||
session: ApiSession,
|
||||
input: { olderThanMinutes: number; limit: number }
|
||||
) {
|
||||
return request<{ reclaimed: number; taskIds: string[] }>(session, '/cleaning/reclaim-timeouts', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
});
|
||||
}
|
||||
|
||||
export function listCleaningSettlements(
|
||||
session: ApiSession,
|
||||
input: { page: number; pageSize: number; status?: SettlementStatus }
|
||||
) {
|
||||
const params = new URLSearchParams({
|
||||
page: String(input.page),
|
||||
pageSize: String(input.pageSize)
|
||||
});
|
||||
if (input.status) params.set('status', input.status);
|
||||
return request<PageResult<CleaningSettlement>>(session, `/cleaning/settlements?${params}`);
|
||||
}
|
||||
|
||||
export function generateCleaningSettlement(
|
||||
session: ApiSession,
|
||||
input: { cleanerUserId: string; storeId?: string; note?: string }
|
||||
) {
|
||||
return request<CleaningSettlement>(session, '/cleaning/settlements', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmCleaningSettlement(session: ApiSession, settlementId: string, note?: string) {
|
||||
return request<CleaningSettlement>(
|
||||
session,
|
||||
`/cleaning/settlements/${encodeURIComponent(settlementId)}/confirm`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ note })
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function executeWechatTransfer(
|
||||
session: ApiSession,
|
||||
settlementId: string,
|
||||
input: { mode: TransferMode; note?: string }
|
||||
) {
|
||||
return request<{ settlement: CleaningSettlement; transferState: string; idempotent: boolean }>(
|
||||
session,
|
||||
`/cleaning/settlements/${encodeURIComponent(settlementId)}/wechat-transfer`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function syncWechatTransfer(session: ApiSession, settlementId: string, note?: string) {
|
||||
return request<{ settlement: CleaningSettlement; transferState: string; idempotent: boolean }>(
|
||||
session,
|
||||
`/cleaning/settlements/${encodeURIComponent(settlementId)}/wechat-transfer/sync`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ note })
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function recordPayoutFailure(
|
||||
session: ApiSession,
|
||||
settlementId: string,
|
||||
input: { error: string; payoutChannel?: string; payoutReference?: string; note?: string }
|
||||
) {
|
||||
return request<CleaningSettlement>(
|
||||
session,
|
||||
`/cleaning/settlements/${encodeURIComponent(settlementId)}/payout-failure`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user