import { WEB_SERVICE_PREFIX } from '@/constants/service'; import { request } from '../request'; import { type ServiceRequestResult, mapServiceResult, normalizeNullableStringId, normalizeStringId, safeJsonRequestConfig } from './shared'; import { type ExecutionAssigneeLogResponse, type ExecutionAssigneeResponse, type MyExecutionResponse, type MyOwnedProjectResponse, type MyParticipatedProjectResponse, type ProjectExecutionResponse, type ProjectLocalDateValue, type ProjectMemberResponse, type ProjectTaskResponse, type TaskAssigneeFromApiResponse, type TaskAssigneeLogResponse, type TaskWorklogResponse, getProjectLifecycleActions, normalizeExecutionAssignee, normalizeExecutionAssigneeLog, normalizeMyExecution, normalizeMyOwnedProject, normalizeMyParticipatedProject, normalizeProjectExecution, normalizeProjectLocalDate, normalizeProjectMember, normalizeProjectTask, normalizeTaskAssignee, normalizeTaskAssigneeLog, normalizeTaskWorklog } from './project-shared'; const PROJECT_PREFIX = `${WEB_SERVICE_PREFIX}/project/project`; type ProjectResponse = Omit< Api.Project.Project, 'id' | 'managerUserId' | 'productId' | 'plannedStartDate' | 'plannedEndDate' | 'actualStartDate' | 'actualEndDate' > & { id: string | number; managerUserId?: string | number | null; productId?: string | number | null; plannedStartDate?: ProjectLocalDateValue; plannedEndDate?: ProjectLocalDateValue; actualStartDate?: ProjectLocalDateValue; actualEndDate?: ProjectLocalDateValue; }; type ProjectPageResponse = Api.Project.PageResult; type ProjectExecutionPageResponse = Api.Project.PageResult; type ProjectTaskPageResponse = Api.Project.PageResult; type StatusBoardResponse = Api.Project.StatusBoard; type ProjectTaskBoardPageResponse = { items: Array<{ statusCode: string; statusName: string; sort: number; terminal?: boolean; list: ProjectTaskResponse[]; total: number; }>; }; type ProjectContextResponse = Omit & { currentProject: Omit & { id: string | number }; navs: Array & { id: string | number }>; }; function getExecutionPrefix(projectId: string) { return `${PROJECT_PREFIX}/${projectId}/executions`; } function getTaskPrefix(projectId: string, executionId: string) { return `${getExecutionPrefix(projectId)}/${executionId}/tasks`; } /** 归一化项目数据 */ function normalizeProject(project: ProjectResponse): Api.Project.Project { return { ...project, id: normalizeStringId(project.id), managerUserId: normalizeNullableStringId(project.managerUserId) ?? '', productId: normalizeNullableStringId(project.productId), plannedStartDate: normalizeProjectLocalDate(project.plannedStartDate), plannedEndDate: normalizeProjectLocalDate(project.plannedEndDate), actualStartDate: normalizeProjectLocalDate(project.actualStartDate), actualEndDate: normalizeProjectLocalDate(project.actualEndDate) }; } /** 将项目详情组装为设置页数据 */ function createProjectSettings(project: Api.Project.Project): Api.Project.ProjectSettings { return { baseInfo: { id: project.id, projectCode: project.projectCode, projectName: project.projectName, directionCode: project.directionCode, projectType: project.projectType, productId: project.productId, productName: project.productName ?? null, managerUserId: project.managerUserId, managerUserNickname: project.managerUserNickname ?? null, statusCode: project.statusCode, plannedStartDate: project.plannedStartDate, plannedEndDate: project.plannedEndDate, actualStartDate: project.actualStartDate, actualEndDate: project.actualEndDate, projectDesc: project.projectDesc, lastStatusReason: project.lastStatusReason }, lifecycle: { statusCode: project.statusCode, lastStatusReason: project.lastStatusReason, availableActions: getProjectLifecycleActions(project.statusCode) } }; } /** 获取项目分页 */ export async function fetchGetProjectPage(params?: Api.Project.ProjectSearchParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeProject) })); } /** 获取项目入口页概览统计 */ export function fetchGetProjectOverviewSummary() { return request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/overview-summary`, method: 'get' }); } /** 获取项目详情 */ export async function fetchGetProject(id: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/get`, method: 'get', params: { id } }); return mapServiceResult(result as ServiceRequestResult, normalizeProject); } /** 根据产品ID获取产品下的所有项目 */ export async function fetchGetProjectListByProductId(productId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/list-by-product`, method: 'get', params: { productId } }); return mapServiceResult(result as ServiceRequestResult, data => data.map(normalizeProject)); } /** 创建项目 */ export async function fetchCreateProject(data: Api.Project.SaveProjectParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/create`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 创建项目(含初始团队,原子接口) */ export async function fetchCreateProjectWithTeam(data: Api.Project.CreateProjectWithTeamParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/create-with-team`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 更新项目 */ export function fetchUpdateProject(data: Api.Project.UpdateProjectParams) { return request({ url: `${PROJECT_PREFIX}/update`, method: 'put', data }); } /** 变更项目状态 */ export function fetchChangeProjectStatus(data: Api.Project.ChangeProjectStatusParams) { return request({ url: `${PROJECT_PREFIX}/change-status`, method: 'post', data }); } /** 删除项目 */ export function fetchDeleteProject(data: Api.Project.DeleteProjectParams) { return request({ url: `${PROJECT_PREFIX}/delete`, method: 'post', data }); } /** 获取项目上下文 */ export async function fetchGetProjectContext(id: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/${id}/context`, method: 'get' }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, currentProject: { ...data.currentProject, id: normalizeStringId(data.currentProject.id) }, navs: data.navs.map(nav => ({ ...nav, id: normalizeStringId(nav.id) })) })); } /** 获取项目成员列表 */ export async function fetchGetProjectMembers(id: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/${id}/members`, method: 'get' }); return mapServiceResult(result as ServiceRequestResult, data => data.map(normalizeProjectMember) ); } /** 创建项目成员 */ export async function fetchCreateProjectMember(id: string, data: Api.Project.CreateProjectMemberParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/${id}/members`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 更新项目成员 */ export function fetchUpdateProjectMember(id: string, memberId: string, data: Api.Project.UpdateProjectMemberParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/${id}/members/${memberId}`, method: 'put', data }); } /** 移出项目成员 */ export function fetchInactiveProjectMember( id: string, memberId: string, data: Api.Project.InactiveProjectMemberParams ) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/${id}/members/${memberId}/inactive`, method: 'post', data }); } export async function fetchBatchCreateProjectMembers(id: string, data: Api.Project.BatchCreateProjectMembersParams) { const result = await request>({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/${id}/members/batch`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult>, list => Array.isArray(list) ? list.map(normalizeStringId) : [] ); } export function fetchBatchInactiveProjectMembers(id: string, data: Api.Project.BatchInactiveProjectMembersParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/${id}/members/batch/inactive`, method: 'post', data }); } /** 获取项目设置 */ export async function fetchGetProjectSettings(id: string) { const result = await fetchGetProject(id); if (result.error || !result.data) { return result as ServiceRequestResult; } return { ...result, data: createProjectSettings(result.data) }; } /** 更新项目设置基础信息 */ export async function fetchUpdateProjectSettingBaseInfo( id: string, data: Api.Project.UpdateProjectSettingBaseInfoParams ) { const detailResult = await fetchGetProject(id); if (detailResult.error || !detailResult.data) { return detailResult as ServiceRequestResult; } return fetchUpdateProject({ id, projectCode: detailResult.data.projectCode, projectName: data.projectName, directionCode: data.directionCode, projectType: data.projectType, productId: detailResult.data.productId, managerUserId: detailResult.data.managerUserId, plannedStartDate: data.plannedStartDate, plannedEndDate: data.plannedEndDate, actualStartDate: detailResult.data.actualStartDate, actualEndDate: detailResult.data.actualEndDate, projectDesc: data.projectDesc }); } /** 获取项目执行分页 */ export async function fetchGetProjectExecutionPage( projectId: string, params?: Api.Project.ProjectExecutionSearchParams ) { const result = await request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeProjectExecution) })); } /** 获取工作台「我负责的执行」(跨项目聚合,owner 隐式取当前登录用户) */ export async function fetchGetMyExecutionPage(params?: Api.Project.MyExecutionSearchParams) { type MyExecutionPageResponse = Api.Project.PageResult; const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/me/executions/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeMyExecution) })); } /** 获取工作台「我参与的项目」(成员视角,附我的角色与任务量;隐式取当前登录用户) */ export async function fetchGetMyParticipatedProjectPage(params?: Api.Project.MyProjectSearchParams) { type MyParticipatedProjectPageResponse = Api.Project.PageResult; const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/me/participated/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeMyParticipatedProject) })); } /** 获取工作台「我负责的项目」(项目负责人视角,附聚合统计与成员负载;隐式取当前登录用户) */ export async function fetchGetMyOwnedProjectPage(params?: Api.Project.MyProjectSearchParams) { type MyOwnedProjectPageResponse = Api.Project.PageResult; const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_PREFIX}/me/owned/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeMyOwnedProject) })); } /** 获取项目执行状态看板 */ export function fetchGetProjectExecutionStatusBoard( projectId: string, params?: Api.Project.ProjectExecutionStatusBoardParams ) { return request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/status-board`, method: 'get', params }); } /** 获取项目执行详情 */ export async function fetchGetProjectExecution(projectId: string, executionId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}`, method: 'get' }); return mapServiceResult(result as ServiceRequestResult, normalizeProjectExecution); } /** 创建项目执行 */ export async function fetchCreateProjectExecution(projectId: string, data: Api.Project.CreateProjectExecutionParams) { const result = await request({ ...safeJsonRequestConfig, url: getExecutionPrefix(projectId), method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 更新项目执行 */ export function fetchUpdateProjectExecution( projectId: string, executionId: string, data: Api.Project.UpdateProjectExecutionParams ) { return request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}`, method: 'put', data }); } /** 变更项目执行负责人 */ export function fetchChangeProjectExecutionOwner( projectId: string, executionId: string, data: Api.Project.ChangeExecutionOwnerParams ) { return request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}/change-owner`, method: 'post', data }); } /** 删除项目执行 */ export function fetchDeleteProjectExecution( projectId: string, executionId: string, data: Api.Project.DeleteProjectExecutionParams ) { return request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}`, method: 'delete', data }); } /** 执行删除预检(spec §2.1:返回是否含下挂数据,用于前端弹层分流) */ export function fetchPrecheckDeleteProjectExecution(projectId: string, executionId: string) { return request({ url: `${getExecutionPrefix(projectId)}/${executionId}/delete-precheck`, method: 'get' }); } /** 变更项目执行状态 */ export function fetchChangeProjectExecutionStatus( projectId: string, executionId: string, data: Api.Project.ChangeExecutionStatusParams ) { return request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}/change-status`, method: 'post', data }); } /** 获取项目执行协办人 */ export async function fetchGetProjectExecutionAssignees(projectId: string, executionId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}/assignees`, method: 'get' }); return mapServiceResult(result as ServiceRequestResult, data => data.map(normalizeExecutionAssignee) ); } /** 创建项目执行协办人 */ export async function fetchCreateProjectExecutionAssignee( projectId: string, executionId: string, data: Api.Project.CreateExecutionAssigneeParams ) { const result = await request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}/assignees`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 移除项目执行协办人 */ export function fetchInactiveProjectExecutionAssignee( projectId: string, executionId: string, payload: { assigneeId: string; data: Api.Project.InactiveExecutionAssigneeParams } ) { return request({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}/assignees/${payload.assigneeId}/inactive`, method: 'post', data: payload.data }); } /** 获取项目执行协办人变更历史分页 */ export async function fetchGetProjectExecutionAssigneeLogPage( projectId: string, executionId: string, params?: Api.Project.ExecutionAssigneeLogSearchParams ) { const result = await request>({ ...safeJsonRequestConfig, url: `${getExecutionPrefix(projectId)}/${executionId}/assignee-logs`, method: 'get', params }); return mapServiceResult( result as ServiceRequestResult>, data => ({ ...data, list: data.list.map(normalizeExecutionAssigneeLog) }) ); } /** 获取项目任务分页 */ export async function fetchGetProjectTaskPage( projectId: string, executionId: string, params?: Api.Project.ProjectTaskSearchParams ) { const result = await request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeProjectTask) })); } /** 获取项目任务状态看板 */ export function fetchGetProjectTaskStatusBoard( projectId: string, executionId: string, params?: Api.Project.ProjectTaskStatusBoardParams ) { return request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/status-board`, method: 'get', params }); } /** * 任务看板按状态分组的分页接口。 * * 看板模式专用:一次请求拿到所有列(或指定列)的首屏 + 总数,替代"5 列 5 次 page"的旧方式。 * 列内向下滚续页时再传 `statusCode=[X]&pageNo=N+1` 单列查询。 */ export async function fetchGetProjectTaskBoardPage( projectId: string, executionId: string, params?: Api.Project.ProjectTaskBoardPageParams ) { const result = await request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/board-page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ items: data.items.map(item => ({ ...item, list: item.list.map(normalizeProjectTask) })) })); } /** 获取项目任务详情 */ export async function fetchGetProjectTask(projectId: string, executionId: string, taskId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${taskId}`, method: 'get' }); return mapServiceResult(result as ServiceRequestResult, normalizeProjectTask); } /** 创建项目任务 */ export async function fetchCreateProjectTask( projectId: string, executionId: string, data: Api.Project.SaveProjectTaskParams ) { const result = await request({ ...safeJsonRequestConfig, url: getTaskPrefix(projectId, executionId), method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 更新项目任务 */ export function fetchUpdateProjectTask( projectId: string, executionId: string, payload: { taskId: string; data: Api.Project.SaveProjectTaskParams } ) { return request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${payload.taskId}`, method: 'put', data: payload.data }); } /** 删除项目任务 */ // eslint-disable-next-line max-params export function fetchDeleteProjectTask( projectId: string, executionId: string, taskId: string, data: Api.Project.DeleteProjectTaskParams ) { return request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${taskId}`, method: 'delete', data }); } /** 任务删除预检(spec §2.1) */ export function fetchPrecheckDeleteProjectTask(projectId: string, executionId: string, taskId: string) { return request({ url: `${getTaskPrefix(projectId, executionId)}/${taskId}/delete-precheck`, method: 'get' }); } /** 变更项目任务状态 */ export function fetchChangeProjectTaskStatus( projectId: string, executionId: string, payload: { taskId: string; data: Api.Project.ChangeTaskStatusParams } ) { return request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${payload.taskId}/change-status`, method: 'post', data: payload.data }); } // ============= 项目级跨执行任务(不带 executionId 路径段) ============= // 调试文档:所有接口挂在 /project/project/{projectId}/tasks/* 下;通过 involveUserId / ownerId / executionIds 等 // 入参组合表达"我的任务 / 项目全部 / 指定执行"等视角。原有执行级 {eid}/tasks/page 等保留不动。 function getProjectTasksPrefix(projectId: string) { return `${PROJECT_PREFIX}/${projectId}/tasks`; } /** 项目级跨执行任务分页 */ export async function fetchGetProjectTaskPageCross( projectId: string, params?: Api.Project.ProjectTaskCrossSearchParams ) { const result = await request({ ...safeJsonRequestConfig, url: `${getProjectTasksPrefix(projectId)}/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeProjectTask) })); } /** 项目级跨执行任务状态看板 */ export function fetchGetProjectTaskStatusBoardCross( projectId: string, params?: Api.Project.ProjectTaskCrossStatusBoardParams ) { return request({ ...safeJsonRequestConfig, url: `${getProjectTasksPrefix(projectId)}/status-board`, method: 'get', params }); } /** 项目级跨执行任务看板分页(每列共用同一组 pageNo / pageSize;列内固定 plannedEndDate ASC, id DESC) */ export async function fetchGetProjectTaskBoardPageCross( projectId: string, params?: Api.Project.ProjectTaskCrossBoardPageParams ) { const result = await request({ ...safeJsonRequestConfig, url: `${getProjectTasksPrefix(projectId)}/board-page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ items: data.items.map(item => ({ ...item, list: item.list.map(normalizeProjectTask) })) })); } /** * 项目级"今日小条"汇总(4 个数字 + 服务器日期边界)。 * * scope=all 必须有 project:task:query 权限,否则 403(PROJECT_OBJECT_PERMISSION_DENIED)。 * 前端切到"项目全部"视角前应已基于权限码隐藏入口;如真被 403,UI 应自动切回"我的"。 */ export function fetchGetProjectTaskSummary(projectId: string, params?: Api.Project.ProjectTaskSummaryParams) { return request({ ...safeJsonRequestConfig, url: `${getProjectTasksPrefix(projectId)}/summary`, method: 'get', params }); } type TaskWorklogPageResponse = Api.Project.PageResult; function getWorklogPrefix(projectId: string, executionId: string, taskId: string) { return `${getTaskPrefix(projectId, executionId)}/${taskId}/worklogs`; } /** 获取任务工时分页 */ // eslint-disable-next-line max-params export async function fetchGetProjectTaskWorklogPage( projectId: string, executionId: string, taskId: string, params?: Api.Project.TaskWorklogSearchParams ) { const result = await request({ ...safeJsonRequestConfig, url: getWorklogPrefix(projectId, executionId, taskId), method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeTaskWorklog) })); } /** 新增任务工时 */ // eslint-disable-next-line max-params export async function fetchCreateProjectTaskWorklog( projectId: string, executionId: string, taskId: string, data: Api.Project.SaveTaskWorklogParams ) { const result = await request({ ...safeJsonRequestConfig, url: getWorklogPrefix(projectId, executionId, taskId), method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 修改任务工时 */ // eslint-disable-next-line max-params export function fetchUpdateProjectTaskWorklog( projectId: string, executionId: string, taskId: string, payload: { worklogId: string; data: Api.Project.SaveTaskWorklogParams } ) { return request({ ...safeJsonRequestConfig, url: `${getWorklogPrefix(projectId, executionId, taskId)}/${payload.worklogId}`, method: 'put', data: payload.data }); } /** 删除任务工时 */ // eslint-disable-next-line max-params export function fetchDeleteProjectTaskWorklog( projectId: string, executionId: string, taskId: string, worklogId: string ) { return request({ ...safeJsonRequestConfig, url: `${getWorklogPrefix(projectId, executionId, taskId)}/${worklogId}`, method: 'delete' }); } /** 5.6 获取任务协办人列表(仅当前活跃) */ export async function fetchGetProjectTaskAssignees(projectId: string, executionId: string, taskId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${taskId}/assignees`, method: 'get' }); return mapServiceResult(result as ServiceRequestResult, data => data.map(normalizeTaskAssignee) ); } /** 5.7 加入任务协办人 */ // eslint-disable-next-line max-params export async function fetchCreateProjectTaskAssignee( projectId: string, executionId: string, taskId: string, data: Api.Project.CreateTaskAssigneeParams ) { const result = await request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${taskId}/assignees`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 5.8 退出任务协办人 */ // eslint-disable-next-line max-params export function fetchInactiveProjectTaskAssignee( projectId: string, executionId: string, taskId: string, assigneeId: string, data: Api.Project.InactiveTaskAssigneeParams ) { return request({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${taskId}/assignees/${assigneeId}/inactive`, method: 'post', data }); } /** 5.9 任务协办人变更历史分页 */ // eslint-disable-next-line max-params export async function fetchGetProjectTaskAssigneeLogPage( projectId: string, executionId: string, taskId: string, params?: Api.Project.TaskAssigneeLogSearchParams ) { const result = await request>({ ...safeJsonRequestConfig, url: `${getTaskPrefix(projectId, executionId)}/${taskId}/assignee-logs`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult>, data => ({ ...data, list: data.list.map(normalizeTaskAssigneeLog) })); } // ========== 项目需求 API ========== const PROJECT_REQUIREMENT_PREFIX = `${WEB_SERVICE_PREFIX}/project/project/requirement`; type ProjectRequirementResponse = Omit< Api.Project.ProjectRequirement, 'id' | 'projectId' | 'parentId' | 'moduleId' | 'proposerId' | 'currentHandlerUserId' | 'sourceBizId' | 'attachments' > & { id: string | number; projectId: string | number; parentId: string | number; moduleId: string | number; proposerId: string | number; currentHandlerUserId?: string | number | null; sourceBizId?: string | number | null; attachments?: AttachmentItemResponse[] | null; children?: ProjectRequirementResponse[]; }; type ProjectRequirementPageResponse = Api.Project.PageResult; type ProjectRequirementReviewResponse = Omit< Api.Project.ProjectRequirementReview, 'id' | 'requirementId' | 'operatorId' | 'attendees' | 'attachments' > & { id: string | number; requirementId: string | number; operatorId: string | number; attendees?: Array<{ userId: string | number; nickname: string; }>; attachments?: AttachmentItemResponse[] | null; }; type ProjectRequirementModuleResponse = Omit & { id: string | number; parentId: string | number; projectId: string | number; children?: ProjectRequirementModuleResponse[]; }; type AttachmentItemResponse = Omit & { fileId?: string | number; id?: string | number; }; function normalizeAttachments(list?: AttachmentItemResponse[] | null): Api.Project.AttachmentItem[] | null { if (!list) { return null; } return list.map(item => { const rawId = item.fileId ?? item.id; return { ...item, fileId: rawId === null || rawId === undefined ? '' : String(rawId) }; }); } function normalizeProjectRequirement(requirement: ProjectRequirementResponse): Api.Project.ProjectRequirement { return { ...requirement, id: normalizeStringId(requirement.id), projectId: normalizeStringId(requirement.projectId), parentId: normalizeStringId(requirement.parentId), moduleId: normalizeStringId(requirement.moduleId), proposerId: normalizeStringId(requirement.proposerId), currentHandlerUserId: normalizeNullableStringId(requirement.currentHandlerUserId), sourceBizId: normalizeNullableStringId(requirement.sourceBizId), attachments: normalizeAttachments(requirement.attachments), progressRate: typeof requirement.progressRate === 'number' ? requirement.progressRate : 0, children: requirement.children?.map(normalizeProjectRequirement) }; } function normalizeProjectRequirementReview( review: ProjectRequirementReviewResponse ): Api.Project.ProjectRequirementReview { return { ...review, id: normalizeStringId(review.id), requirementId: normalizeStringId(review.requirementId), operatorId: normalizeStringId(review.operatorId), attendees: review.attendees?.map(item => ({ ...item, userId: normalizeStringId(item.userId) })), attachments: normalizeAttachments(review.attachments) }; } function normalizeProjectRequirementModule( module: ProjectRequirementModuleResponse ): Api.Project.ProjectRequirementModule { return { ...module, id: normalizeStringId(module.id), parentId: normalizeStringId(module.parentId), projectId: normalizeStringId(module.projectId), children: module.children?.map(normalizeProjectRequirementModule) }; } /** 获取项目需求分页列表 */ export async function fetchGetProjectRequirementPage(params?: Api.Project.ProjectRequirementSearchParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/page`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeProjectRequirement) })); } /** 获取项目需求树形列表 */ export async function fetchGetProjectRequirementTree(params?: Api.Project.ProjectRequirementSearchParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/tree`, method: 'get', params }); return mapServiceResult(result as ServiceRequestResult, data => ({ ...data, list: data.list.map(normalizeProjectRequirement) })); } /** 获取项目需求详情 */ export async function fetchGetProjectRequirement(id: string, projectId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/get`, method: 'get', params: { id, projectId } }); return mapServiceResult(result as ServiceRequestResult, normalizeProjectRequirement); } /** 创建项目需求 */ export async function fetchCreateProjectRequirement(data: Api.Project.SaveProjectRequirementParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/create`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 更新项目需求 */ export function fetchUpdateProjectRequirement(data: Api.Project.UpdateProjectRequirementParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/update`, method: 'put', data }); } /** 变更项目需求状态 */ export function fetchChangeProjectRequirementStatus(data: Api.Project.ChangeProjectRequirementStatusParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/change-status`, method: 'post', data }); } /** 删除项目需求 */ export function fetchDeleteProjectRequirement(data: Api.Project.DeleteProjectRequirementParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/delete`, method: 'post', data }); } /** 拆分项目需求 */ export async function fetchSplitProjectRequirement(data: Api.Project.SplitProjectRequirementParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/split`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 关闭项目需求 */ export function fetchCloseProjectRequirement(data: Api.Project.CloseProjectRequirementParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/close`, method: 'post', data }); } /** 获取项目需求可执行状态动作列表 */ export async function fetchGetProjectRequirementAllowedTransitions(requirementId: string, projectId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/allowed-transitions`, method: 'get', params: { requirementId, projectId } }); return mapServiceResult( result as ServiceRequestResult, data => data ); } /** 批量获取项目需求可执行状态动作列表 */ export async function fetchGetProjectRequirementAllowedTransitionsBatch( data: Api.Project.ProjectRequirementBatchReqVO ) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/allowed-transitions/batch`, method: 'post', data }); return mapServiceResult( result as ServiceRequestResult, data1 => data1.map(item => ({ requirementId: normalizeStringId(item.requirementId), transitions: item.transitions })) ); } /** 提交项目需求评审 */ export async function fetchSubmitProjectRequirementReview(data: Api.Project.ProjectRequirementReviewSubmitParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/review/submit`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 获取项目需求评审记录 */ export async function fetchGetProjectRequirementReview(projectId: string, requirementId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/review/get`, method: 'get', params: { projectId, requirementId } }); return mapServiceResult( result as ServiceRequestResult, normalizeProjectRequirementReview ); } /** 获取项目需求状态字典 */ export async function fetchGetProjectRequirementStatusDict() { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/status/dict`, method: 'get' }); return mapServiceResult(result as ServiceRequestResult, data => data); } /** 获取项目需求模块树 */ export async function fetchGetProjectRequirementModuleTree(projectId: string) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/module/tree`, method: 'get', params: { projectId } }); return mapServiceResult(result as ServiceRequestResult, data => data.map(normalizeProjectRequirementModule) ); } /** 创建项目需求模块 */ export async function fetchCreateProjectRequirementModule(data: Api.Project.SaveProjectRequirementModuleParams) { const result = await request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/module/create`, method: 'post', data }); return mapServiceResult(result as ServiceRequestResult, normalizeStringId); } /** 更新项目需求模块 */ export function fetchUpdateProjectRequirementModule(data: Api.Project.SaveProjectRequirementModuleParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/module/update`, method: 'put', data }); } /** 删除项目需求模块 */ export function fetchDeleteProjectRequirementModule(data: Api.Project.DeleteProjectRequirementModuleParams) { return request({ ...safeJsonRequestConfig, url: `${PROJECT_REQUIREMENT_PREFIX}/module/delete`, method: 'post', data }); }