| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- import { HttpClient, HttpErrorResponse } from '@angular/common/http';
- import { inject, Injectable } from '@angular/core';
- import { firstValueFrom, Subject } from 'rxjs';
- import { CLOUD_FUNCTION_BASE_URL } from './admin.constants';
- import { AdminSessionService } from './admin-session.service';
- import { CloudEnvelope, LegacyCloudEnvelope } from './admin.models';
- export class CloudFunctionError extends Error {
- constructor(message: string, readonly status = 0, readonly payload?: unknown) {
- super(message);
- }
- }
- @Injectable({ providedIn: 'root' })
- export class CloudFunctionsService {
- readonly dataChanges$ = new Subject<'local' | 'broadcast'>();
- private readonly http = inject(HttpClient);
- private readonly sessions = inject(AdminSessionService);
- private readonly dataChannel = this.createDataChannel();
- async admin<T>(operation: string, params: Record<string, unknown> = {}): Promise<T> {
- const token = this.sessions.session()?.sessionToken;
- if (!token) throw new CloudFunctionError('管理员会话已失效', 401);
- try {
- const envelope = await firstValueFrom(this.http.post<CloudEnvelope<T>>(
- `${CLOUD_FUNCTION_BASE_URL}/xiaoshu/admin/gateway`,
- { token, params: { operation, ...params } },
- ));
- if (!envelope.success) { await this.handleAuthenticationFailure(token, 0, envelope); throw new CloudFunctionError(envelope.message || envelope.error || '云函数执行失败'); }
- if (this.isMutation(operation)) this.announceDataChange();
- return envelope.data;
- } catch (error) {
- if (error instanceof CloudFunctionError) throw error;
- if (error instanceof HttpErrorResponse) {
- const payload = error.error as { error?: string; message?: string } | null;
- await this.handleAuthenticationFailure(token, error.status, payload);
- throw new CloudFunctionError(payload?.message || payload?.error || error.message, error.status, payload);
- }
- throw new CloudFunctionError(error instanceof Error ? error.message : '未知云函数错误');
- }
- }
- private async handleAuthenticationFailure(token: string, status: number, payload: unknown): Promise<void> {
- const body = payload as { code?: number; error?: string; message?: string; retmsg?: string } | null;
- const message = String(body?.message || body?.retmsg || body?.error || '');
- const invalid = status === 401 || Number(body?.code) === 209 ||
- /^(登录状态已失效|管理员会话已失效|账号已停用|账号已删除|invalid session token)[::。.!]?$/i.test(message.trim());
- if (/^(账号已停用|账号已删除)[::。.!]?$/.test(message.trim())) this.sessions.invalidateSession(token);
- else if (invalid) await this.sessions.verifySessionAfterFailure(token);
- }
- private isMutation(operation: string): boolean {
- return /\/(?:create|save|update|delete|recycle|restore|complete|adjust|post|commit|confirm|cancel|review|mark-paid|deactivate|run|retry|refresh|add-adjustment|exclude-line|import-commit)$/.test(operation);
- }
- private createDataChannel(): BroadcastChannel | null {
- if (typeof globalThis.BroadcastChannel !== 'function') return null;
- const channel = new globalThis.BroadcastChannel('xiaoshu-live-data-v1');
- channel.addEventListener('message', () => this.dataChanges$.next('broadcast'));
- return channel;
- }
- private announceDataChange(): void {
- this.dataChanges$.next('local');
- this.dataChannel?.postMessage({ changedAt: new Date().toISOString() });
- }
- async operations<T>(operation: string, params: Record<string, unknown> = {}): Promise<T> {
- const token = this.sessions.session()?.sessionToken;
- if (!token) throw new CloudFunctionError('管理员会话已失效', 401);
- try {
- const envelope = await firstValueFrom(this.http.post<CloudEnvelope<T>>(
- `${CLOUD_FUNCTION_BASE_URL}/xiaoshu/ops/gateway-v3`,
- { token, params: { operation, ...params } },
- ));
- if (!envelope.success) { await this.handleAuthenticationFailure(token, 0, envelope); throw new CloudFunctionError(envelope.message || envelope.error || '运营云函数执行失败'); }
- if (this.isMutation(operation)) this.announceDataChange();
- return envelope.data;
- } catch (error) {
- if (error instanceof CloudFunctionError) throw error;
- if (error instanceof HttpErrorResponse) {
- const payload = error.error as { error?: string; message?: string } | null;
- await this.handleAuthenticationFailure(token, error.status, payload);
- throw new CloudFunctionError(payload?.message || payload?.error || error.message, error.status, payload);
- }
- throw new CloudFunctionError(error instanceof Error ? error.message : '未知运营云函数错误');
- }
- }
- async app<T>(action: string, params: Record<string, unknown> = {}): Promise<T> {
- const token = this.sessions.session()?.sessionToken;
- if (!token) throw new CloudFunctionError('管理员会话已失效', 401);
- try {
- const envelope = await firstValueFrom(this.http.post<LegacyCloudEnvelope<T>>(
- `${CLOUD_FUNCTION_BASE_URL}/xiaoshu/app/gateway`,
- { token, params: { action, ...params } },
- ));
- if (envelope.retcode === -1) { await this.handleAuthenticationFailure(token, 0, envelope); throw new CloudFunctionError(envelope.retmsg || '云函数执行失败'); }
- return envelope.result;
- } catch (error) {
- if (error instanceof CloudFunctionError) throw error;
- if (error instanceof HttpErrorResponse) {
- const payload = error.error as { error?: string; message?: string; retmsg?: string } | null;
- await this.handleAuthenticationFailure(token, error.status, payload);
- throw new CloudFunctionError(payload?.retmsg || payload?.message || payload?.error || error.message, error.status, payload);
- }
- throw new CloudFunctionError(error instanceof Error ? error.message : '未知云函数错误');
- }
- }
- }
|