rbacRoleService.ts 5.24 KB
import { createApiClient } from "../lib/apiClient";
import { normalizeRoleFromApi, type PagedResultDto, type RoleDto, type RoleGetListInput } from "../types/role";

const api = createApiClient({
  getToken: () => {
    try {
      return localStorage.getItem("access_token") ?? localStorage.getItem("token") ?? null;
    } catch {
      return null;
    }
  },
});

const PATH = "/rbac-role";

export type RbacRoleUpsertInput = {
  roleName: string;
  roleCode: string;
  remark?: string | null;
  dataScope?: number | null;
  state: boolean;
  orderNum?: number | null;
  /** 访问权限编码(与表单一致) */
  accessPermissionCodes?: string[] | null;
  /** 与 `accessPermissionCodes` 同源,提交时合并为 `accessPermissions` */
  accessPermissions?: string[] | null;
};

function mergeUpsertAccessPermissions(input: RbacRoleUpsertInput): string[] {
  const merged = [...(input.accessPermissionCodes ?? []), ...(input.accessPermissions ?? [])];
  const seen = new Set<string>();
  const out: string[] = [];
  for (const x of merged) {
    if (typeof x !== "string") continue;
    const t = x.trim();
    if (!t || seen.has(t)) continue;
    seen.add(t);
    out.push(t);
  }
  return out;
}

/** 创建/更新请求体:`accessPermissions` 为 JSON 数组字符串 */
function buildRbacRoleRequestBody(input: RbacRoleUpsertInput): Record<string, unknown> {
  const codes = mergeUpsertAccessPermissions(input);
  return {
    roleName: input.roleName,
    roleCode: input.roleCode,
    remark: input.remark ?? null,
    dataScope: input.dataScope ?? 0,
    state: input.state,
    orderNum: input.orderNum ?? null,
    accessPermissions: codes.length > 0 ? JSON.stringify(codes) : null,
  };
}

/**
 * 角色分页列表(与创建/更新同一套 `/rbac-role` 数据源)
 */
export async function getRbacRoles(
  input: RoleGetListInput,
  signal?: AbortSignal,
): Promise<PagedResultDto<RoleDto>> {
  const raw = await api.requestJson<PagedResultDto<Record<string, unknown>>>({
    path: PATH,
    method: "GET",
    query: {
      SkipCount: input.skipCount,
      MaxResultCount: input.maxResultCount,
      RoleName: input.roleName,
      RoleCode: input.roleCode,
      State: input.state,
    },
    signal,
  });
  const itemsRaw = raw.items ?? (raw as { Items?: unknown[] }).Items ?? [];
  const items = (Array.isArray(itemsRaw) ? itemsRaw : []).map((x) =>
    normalizeRoleFromApi(x as Record<string, unknown>),
  );
  const total =
    typeof raw.totalCount === "number"
      ? raw.totalCount
      : typeof (raw as { TotalCount?: number }).TotalCount === "number"
        ? (raw as { TotalCount: number }).TotalCount
        : items.length;
  return { totalCount: total, items };
}

export async function createRbacRole(input: RbacRoleUpsertInput): Promise<RoleDto> {
  const raw = await api.requestJson<unknown>({
    path: PATH,
    method: "POST",
    body: buildRbacRoleRequestBody(input),
  });
  return normalizeRoleFromApi(raw);
}

export async function updateRbacRole(id: string, input: RbacRoleUpsertInput): Promise<RoleDto> {
  const raw = await api.requestJson<unknown>({
    path: `${PATH}/${encodeURIComponent(id)}`,
    method: "PUT",
    body: buildRbacRoleRequestBody(input),
  });
  return normalizeRoleFromApi(raw);
}

function extractMenuIdsFromRoleDetail(raw: unknown): string[] {
  if (!raw || typeof raw !== "object") return [];
  const r = raw as Record<string, unknown>;

  // 常见命名:menuIds / MenuIds
  const direct =
    Array.isArray(r.menuIds) ? r.menuIds :
    Array.isArray((r as any).MenuIds) ? (r as any).MenuIds :
    undefined;
  if (direct) return (direct as unknown[]).map(String);

  // 包一层:{ data: { menuIds } }
  const data = (r as any).data ?? (r as any).Data;
  if (data && typeof data === "object") {
    const dd = data as Record<string, unknown>;
    const fromData = Array.isArray(dd.menuIds) ? dd.menuIds : Array.isArray(dd.MenuIds) ? dd.MenuIds : undefined;
    if (fromData) return (fromData as unknown[]).map(String);
  }

  // 可能是 roleMenus: [{ menuId: '...' }] 之类
  const roleMenus = Array.isArray((r as any).roleMenus) ? (r as any).roleMenus : Array.isArray((r as any).RoleMenus) ? (r as any).RoleMenus : undefined;
  if (Array.isArray(roleMenus)) {
    const ids: string[] = [];
    for (const x of roleMenus) {
      if (!x || typeof x !== "object") continue;
      const rr = x as Record<string, unknown>;
      const mid = rr.menuId ?? rr.MenuId ?? rr.id ?? rr.Id;
      if (mid) ids.push(String(mid));
    }
    return ids;
  }

  return [];
}

/**
 * Swagger(常见约定):DELETE /api/app/rbac-role,Body 为 ID 数组
 */
export async function deleteRbacRoles(ids: string[]): Promise<void> {
  if (!ids.length) return;
  await api.requestJson<unknown>({
    path: PATH,
    method: "DELETE",
    body: ids,
  });
}

export async function deleteRbacRole(id: string): Promise<void> {
  await deleteRbacRoles([id]);
}

/**
 * Swagger: GET /api/app/rbac-role/{id}
 * 用于“查看当前角色菜单权限”,尽量从返回对象里提取 menuIds 字段。
 */
export async function getRbacRoleMenuIds(roleId: string, signal?: AbortSignal): Promise<string[]> {
  const raw = await api.requestJson<unknown>({
    path: `${PATH}/${encodeURIComponent(roleId)}`,
    method: "GET",
    signal,
  });
  return extractMenuIdsFromRoleDetail(raw);
}