th-app-auth.ts 3.78 KB
/** 绑定门店(与 ThAppLoginOutputDto.locations 一致) */
export interface ThAppBoundLocationDto {
  id: string;
  locationCode: string;
  locationName: string;
  fullAddress: string;
  state: boolean;
}

export interface ThAppLoginInput {
  tenantId: string;
  email: string;
  password: string;
  uuid?: string;
  code?: string;
}

export interface ThAppLoginOutputDto {
  token: string;
  refreshToken: string;
  tenantId: string;
  tenantName: string;
  locations: ThAppBoundLocationDto[];
}

/** 防止重复提交导致前一个请求被 Abort、后一个已在 Network 成功但 UI 仍报错 */
let loginInFlight: Promise<ThAppLoginOutputDto> | null = null;

function getApiBase(): string {
  const raw =
    (import.meta.env.VITE_GLOB_API_URL as string | undefined) ??
    'http://saas-test.3ffoodsafety.com/api/app';
  return raw.replace(/^"|"$/g, '').replace(/\/$/, '');
}

function normalizeLocation(raw: Record<string, unknown>): ThAppBoundLocationDto {
  return {
    id: String(raw.id ?? raw.Id ?? ''),
    locationCode: String(raw.locationCode ?? raw.LocationCode ?? ''),
    locationName: String(raw.locationName ?? raw.LocationName ?? ''),
    fullAddress: String(raw.fullAddress ?? raw.FullAddress ?? ''),
    state: raw.state !== false && raw.State !== false,
  };
}

function normalizeLoginOutput(raw: unknown): ThAppLoginOutputDto {
  const o = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;
  const locs = o.locations ?? o.Locations;
  const arr = Array.isArray(locs) ? locs : [];
  return {
    token: String(o.token ?? o.Token ?? ''),
    refreshToken: String(o.refreshToken ?? o.RefreshToken ?? ''),
    tenantId: String(o.tenantId ?? o.TenantId ?? ''),
    tenantName: String(o.tenantName ?? o.TenantName ?? ''),
    locations: arr.map((x) =>
      normalizeLocation(x as Record<string, unknown>),
    ),
  };
}

function normalizeLocationList(raw: unknown): ThAppBoundLocationDto[] {
  const arr = Array.isArray(raw) ? raw : [];
  return arr.map((x) => normalizeLocation(x as Record<string, unknown>));
}

function loginNetworkError(): Error {
  return new Error(
    `登录网络异常:无法连接 ${getApiBase()},请检查网络、后端是否可用,以及是否已配置 CORS 允许本地前端域名`,
  );
}

/**
 * POST /api/app/th-app-auth/login(匿名)
 * 与租户下拉一致,走 requestClient 直连 VITE_GLOB_API_URL(不再使用 XHR + dev 代理)
 */
export async function thAppLogin(
  input: ThAppLoginInput,
): Promise<ThAppLoginOutputDto> {
  if (loginInFlight) {
    return loginInFlight;
  }

  loginInFlight = (async () => {
    const { requestClient } = await import('#/api/request');
    try {
      const raw = await requestClient.post<unknown>(
        'th-app-auth/login',
        {
          tenantId: input.tenantId,
          email: input.email.trim(),
          password: input.password,
          ...(input.uuid ? { uuid: input.uuid } : {}),
          ...(input.code != null && input.code !== ''
            ? { code: input.code }
            : {}),
        },
        {
          errorMessageMode: 'none',
          successMessageMode: 'none',
          headers: {
            __tenant: input.tenantId,
          },
        },
      );
      return normalizeLoginOutput(raw);
    } catch (error) {
      if (error instanceof Error && error.message.trim()) {
        throw error;
      }
      throw loginNetworkError();
    }
  })().finally(() => {
    loginInFlight = null;
  });

  return loginInFlight;
}

/** GET /api/app/th-app-auth/my-locations(Bearer + 租户上下文) */
export async function thAppMyLocations(): Promise<ThAppBoundLocationDto[]> {
  const { requestClient } = await import('#/api/request');
  const raw = await requestClient.get<unknown>('th-app-auth/my-locations', {
    timeout: 30_000,
  });
  return normalizeLocationList(raw);
}