import { WEB_SERVICE_PREFIX } from '@/constants/service'; import { request } from '../request'; import { type ServiceRequestResult, mapServiceResult, normalizeNullableStringId, safeJsonRequestConfig } from './shared'; import { type ProjectResponse, normalizeProject } from './project'; const PROJECT_PREFIX = `${WEB_SERVICE_PREFIX}/project/project`; /** * group-page 原始响应。 * 组级 managerUserId、productId:后端对小数值 Long(如 1001)仍按数字返回,需 String() 归一; * projects 字段与 page 接口项目行完全一致,复用 ProjectResponse / normalizeProject。 */ type ProjectGroupResponse = Omit & { productId?: string | number | null; managerUserId?: string | number | null; projects: ProjectResponse[]; }; type ProjectGroupPageResponse = Omit & { list: ProjectGroupResponse[]; }; /** 归一化分组:组级 ID String 化,组内项目复用 normalizeProject(id/managerUserId/productId/日期统一口径) */ function normalizeProjectGroup(group: ProjectGroupResponse): Api.Project.ProjectGroup { return { ...group, productId: normalizeNullableStringId(group.productId), managerUserId: normalizeNullableStringId(group.managerUserId), projects: Array.isArray(group.projects) ? group.projects.map(normalizeProject) : [] }; } /** * 项目列表「按产品分组」分页。 * * 后端契约见《项目列表产品分组-前端API-2026-06-10》: * - pageNo/pageSize 为产品组维度分页;statusCode 不传 = 「全部」口径(后端从状态机推导, * 当前等价 pending/active/paused/completed,不含 cancelled/archived)。 * - 组内 projects 仅返前 topN 条(默认 5),projectTotal 为该口径组内全量计数; * 剩余项目由页面按 productId / orphanOnly + statusCodes 走 page 接口展开拉取。 * - typeCounts / hasBaseline 现状恒按「全部」口径统计,不随 statusCode 变化;其中 typeCounts 已提需求 * 改为与 projectTotal 同口径(见《2026-06-11-项目分组接口typeCounts口径-后端接口需求》),后端落地后更新本注释; * hasBaseline = 存在非已取消的主线项目(已归档/完成也算占坑),前端直接消费、不自行推导。 */ export async function fetchGetProjectGroupPage(params?: Api.Project.ProjectGroupSearchParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/group-page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: Array.isArray(data.list) ? data.list.map(normalizeProjectGroup) : [] })); }