feat(projects): 工作台接口切换为真实数据

This commit is contained in:
2026-06-12 19:49:17 +08:00
parent 0652a24c5e
commit 6896a86130
9 changed files with 1062 additions and 839 deletions

View File

@@ -1,3 +1,4 @@
import dayjs from 'dayjs';
import { normalizeNullableStringId, normalizeStringId } from './shared';
type ProjectStatusCode = Api.Project.ProjectStatusCode;
@@ -76,6 +77,60 @@ export type MyOwnedProjectResponse = Omit<Api.Project.MyOwnedProjectItem, 'id' |
members?: MyOwnedProjectMemberResponse[] | null;
};
export type MyTaskResponse = Omit<
Api.Project.MyTaskItem,
| 'id'
| 'projectId'
| 'executionId'
| 'priority'
| 'plannedEndDate'
| 'progressRate'
| 'createTime'
| 'parentTaskId'
| 'availableActions'
> & {
id: StringIdResponse;
projectId: StringIdResponse;
executionId?: StringIdResponse | null;
priority?: string | number | null;
plannedEndDate?: ProjectLocalDateValue;
progressRate?: number | string | null;
createTime?: string | number | null;
parentTaskId?: StringIdResponse | null;
availableActions?: LifecycleActionResponse<Api.Project.ProjectTaskActionCode>[] | null;
};
export type TeamLoadDistributionItemResponse = Omit<Api.Project.TeamLoadDistributionItem, 'projectId'> & {
projectId?: StringIdResponse | null;
};
export type TeamLoadMemberResponse = Omit<Api.Project.TeamLoadMember, 'userId' | 'items'> & {
userId: StringIdResponse;
items?: TeamLoadDistributionItemResponse[] | null;
};
export type TeamLoadResponse = {
members?: TeamLoadMemberResponse[] | null;
};
export type WorklogDistributionItemResponse = Omit<Api.Project.WorklogDistributionItem, 'projectId'> & {
projectId?: StringIdResponse | null;
};
export type MyWorklogWeekResponse = Omit<Api.Project.MyWorklogWeekResult, 'dailyHours' | 'distribution'> & {
dailyHours?: number[] | null;
distribution?: WorklogDistributionItemResponse[] | null;
};
export type TeamWorklogWeekMemberResponse = Omit<Api.Project.TeamWorklogWeekMember, 'userId' | 'items'> & {
userId: StringIdResponse;
items?: WorklogDistributionItemResponse[] | null;
};
export type TeamWorklogWeekResponse = Omit<Api.Project.TeamWorklogWeekResult, 'members'> & {
members?: TeamWorklogWeekMemberResponse[] | null;
};
export type ExecutionAssigneeResponse = Omit<Api.Project.ExecutionAssignee, 'id' | 'executionId' | 'userId'> & {
id: StringIdResponse;
executionId: StringIdResponse;
@@ -263,6 +318,28 @@ export function normalizeProjectLocalDate(value: ProjectLocalDateValue | undefin
return String(value);
}
/**
* 后端 LocalDateTime 统一序列化为毫秒时间戳(也可能是数字字符串/格式化字符串),
* 归一为 'YYYY-MM-DD HH:mm:ss' 供展示与 dayjs 解析。
*/
export function normalizeProjectDateTime(value: string | number | null | undefined): string {
if (value === null || value === undefined || value === '') {
return '';
}
let parsed: dayjs.Dayjs;
if (typeof value === 'number') {
parsed = dayjs(value);
} else if (/^\d+$/.test(value)) {
// 字符串形态的毫秒时间戳dayjs 无法直接解析,先转数值(时间值非 ID安全整数范围内
parsed = dayjs(Number(value));
} else {
parsed = dayjs(value);
}
return parsed.isValid() ? parsed.format('YYYY-MM-DD HH:mm:ss') : '';
}
export function normalizeLifecycleActions<ActionCode extends string>(
actions: LifecycleActionResponse<ActionCode>[] | null | undefined
): Api.Project.LifecycleAction<ActionCode>[] {
@@ -296,6 +373,15 @@ function normalizePriority(value: string | number | null | undefined): string {
return String(value);
}
function normalizeProgressRate(value: number | string | null | undefined) {
if (value === null || value === undefined || value === '') {
return null;
}
const numeric = typeof value === 'number' ? value : Number(value ?? 0);
return Number.isFinite(numeric) ? numeric : null;
}
export function normalizeProjectExecution(response: ProjectExecutionResponse): Api.Project.ProjectExecution {
return {
...response,
@@ -366,6 +452,75 @@ export function normalizeMyOwnedProject(response: MyOwnedProjectResponse): Api.P
};
}
export function normalizeMyTask(response: MyTaskResponse): Api.Project.MyTaskItem {
return {
...response,
id: normalizeStringId(response.id),
projectId: normalizeStringId(response.projectId),
executionId: normalizeNullableStringId(response.executionId),
executionName: response.executionName ?? null,
statusName: response.statusName ?? null,
priority: normalizePriority(response.priority),
plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
progressRate: normalizeProgressRate(response.progressRate) ?? 0,
createTime: normalizeProjectDateTime(response.createTime),
parentTaskId: normalizeNullableStringId(response.parentTaskId),
terminal: Boolean(response.terminal),
allowEdit: Boolean(response.allowEdit),
availableActions: normalizeLifecycleActions(response.availableActions)
};
}
function normalizeWorklogDistributionItem(
response: WorklogDistributionItemResponse | TeamLoadDistributionItemResponse
): { projectId: string | null; projectName: string | null; kind: 'project' | 'personal' | 'other' } {
return {
projectId: normalizeNullableStringId(response.projectId),
projectName: response.projectName ?? null,
kind: response.kind
};
}
export function normalizeTeamLoad(response: TeamLoadResponse): Api.Project.TeamLoadResult {
return {
members: (response.members ?? []).map(member => ({
userId: normalizeStringId(member.userId),
userNickname: member.userNickname ?? '',
items: (member.items ?? []).map(item => ({
...normalizeWorklogDistributionItem(item),
count: typeof item.count === 'number' ? item.count : 0
})),
dueSoonCount: typeof member.dueSoonCount === 'number' ? member.dueSoonCount : 0,
overdueCount: typeof member.overdueCount === 'number' ? member.overdueCount : 0
}))
};
}
export function normalizeMyWorklogWeek(response: MyWorklogWeekResponse): Api.Project.MyWorklogWeekResult {
return {
weekStart: response.weekStart ?? '',
dailyHours: response.dailyHours ?? [0, 0, 0, 0, 0],
distribution: (response.distribution ?? []).map(item => ({
...normalizeWorklogDistributionItem(item),
hours: typeof item.hours === 'number' ? item.hours : 0
}))
};
}
export function normalizeTeamWorklogWeek(response: TeamWorklogWeekResponse): Api.Project.TeamWorklogWeekResult {
return {
weekStart: response.weekStart ?? '',
members: (response.members ?? []).map(member => ({
userId: normalizeStringId(member.userId),
userNickname: member.userNickname ?? '',
items: (member.items ?? []).map(item => ({
...normalizeWorklogDistributionItem(item),
hours: typeof item.hours === 'number' ? item.hours : 0
}))
}))
};
}
export function normalizeExecutionAssignee(response: ExecutionAssigneeResponse): Api.Project.ExecutionAssignee {
return {
...response,

View File

@@ -13,6 +13,8 @@ import {
type MyExecutionResponse,
type MyOwnedProjectResponse,
type MyParticipatedProjectResponse,
type MyTaskResponse,
type MyWorklogWeekResponse,
type ProjectExecutionResponse,
type ProjectLocalDateValue,
type ProjectMemberResponse,
@@ -20,19 +22,25 @@ import {
type TaskAssigneeFromApiResponse,
type TaskAssigneeLogResponse,
type TaskWorklogResponse,
type TeamLoadResponse,
type TeamWorklogWeekResponse,
getProjectLifecycleActions,
normalizeExecutionAssignee,
normalizeExecutionAssigneeLog,
normalizeMyExecution,
normalizeMyOwnedProject,
normalizeMyParticipatedProject,
normalizeMyTask,
normalizeMyWorklogWeek,
normalizeProjectExecution,
normalizeProjectLocalDate,
normalizeProjectMember,
normalizeProjectTask,
normalizeTaskAssignee,
normalizeTaskAssigneeLog,
normalizeTaskWorklog
normalizeTaskWorklog,
normalizeTeamLoad,
normalizeTeamWorklogWeek
} from './project-shared';
const PROJECT_PREFIX = `${WEB_SERVICE_PREFIX}/project/project`;
@@ -440,6 +448,57 @@ export async function fetchGetMyOwnedProjectPage(params?: Api.Project.MyProjectS
}));
}
/** 获取工作台「我的任务」(跨项目聚合,负责人/在岗协办人口径,只返回非终态;隐式取当前登录用户) */
export async function fetchGetMyTaskPage(params?: Api.Project.MyTaskSearchParams) {
type MyTaskPageResponse = Api.Project.PageResult<MyTaskResponse>;
const result = await request<MyTaskPageResponse>({
...safeJsonRequestConfig,
url: `${PROJECT_PREFIX}/me/tasks/page`,
method: 'get',
params
});
return mapServiceResult(result as ServiceRequestResult<MyTaskPageResponse>, data => ({
...data,
list: data.list.map(normalizeMyTask)
}));
}
/** 获取工作台「团队负载」(团队 = 当前用户 + 管理链路直接下级members[0] 恒为当前用户) */
export async function fetchGetMyTeamLoad() {
const result = await request<TeamLoadResponse>({
...safeJsonRequestConfig,
url: `${PROJECT_PREFIX}/me/team-load`,
method: 'get'
});
return mapServiceResult(result as ServiceRequestResult<TeamLoadResponse>, normalizeTeamLoad);
}
/** 获取工作台「我的工时周聚合」weekStart 传任意日期,后端归一到所在周周一;逐日工时为均摊推算值) */
export async function fetchGetMyWorklogWeek(params: Api.Project.WorklogWeekParams) {
const result = await request<MyWorklogWeekResponse>({
...safeJsonRequestConfig,
url: `${PROJECT_PREFIX}/me/worklog-week`,
method: 'get',
params
});
return mapServiceResult(result as ServiceRequestResult<MyWorklogWeekResponse>, normalizeMyWorklogWeek);
}
/** 获取工作台「团队工时周聚合」(成员集合与团队负载同口径;周标准工时后端不返回,前端落常量) */
export async function fetchGetTeamWorklogWeek(params: Api.Project.WorklogWeekParams) {
const result = await request<TeamWorklogWeekResponse>({
...safeJsonRequestConfig,
url: `${PROJECT_PREFIX}/me/team-worklog-week`,
method: 'get',
params
});
return mapServiceResult(result as ServiceRequestResult<TeamWorklogWeekResponse>, normalizeTeamWorklogWeek);
}
/** 获取项目执行状态看板 */
export function fetchGetProjectExecutionStatusBoard(
projectId: string,