2026-04-23 09:05:55 +08:00
|
|
|
|
import { WEB_SERVICE_PREFIX } from '@/constants/service';
|
|
|
|
|
|
import { request } from '../request';
|
|
|
|
|
|
import {
|
|
|
|
|
|
type ServiceRequestResult,
|
|
|
|
|
|
mapServiceResult,
|
|
|
|
|
|
normalizeNullableStringId,
|
|
|
|
|
|
normalizeStringId,
|
|
|
|
|
|
safeJsonRequestConfig
|
|
|
|
|
|
} from './shared';
|
|
|
|
|
|
import { normalizeProductMember, normalizeProductSettings } from './product-shared';
|
|
|
|
|
|
|
|
|
|
|
|
const PRODUCT_PREFIX = `${WEB_SERVICE_PREFIX}/project/product`;
|
|
|
|
|
|
|
|
|
|
|
|
type ProductResponse = Omit<Api.Product.Product, 'id' | 'managerUserId'> & {
|
|
|
|
|
|
id: string | number;
|
|
|
|
|
|
managerUserId?: string | number | null;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
type ProductPageResponse = Api.Product.PageResult<ProductResponse>;
|
|
|
|
|
|
|
2026-04-24 16:38:43 +08:00
|
|
|
|
type ProductActivityTimelineItemResponse = Omit<
|
|
|
|
|
|
Api.Product.ProductActivityTimelineItem,
|
|
|
|
|
|
'id' | 'operatorUserId' | 'targetUserId' | 'occurredAt'
|
|
|
|
|
|
> & {
|
|
|
|
|
|
id: string | number;
|
|
|
|
|
|
operatorUserId?: string | number | null;
|
|
|
|
|
|
targetUserId?: string | number | null;
|
|
|
|
|
|
occurredAt: number | string;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
type ProductActivityTimelinePageResponse = Omit<
|
|
|
|
|
|
Api.Product.PageResult<ProductActivityTimelineItemResponse>,
|
|
|
|
|
|
'total'
|
|
|
|
|
|
> & {
|
|
|
|
|
|
total: number | string;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-04-23 09:05:55 +08:00
|
|
|
|
function normalizeProduct(product: ProductResponse): Api.Product.Product {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...product,
|
|
|
|
|
|
id: normalizeStringId(product.id),
|
|
|
|
|
|
managerUserId: normalizeNullableStringId(product.managerUserId) ?? ''
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-24 16:38:43 +08:00
|
|
|
|
function normalizeOccurredAt(occurredAt: number | string) {
|
|
|
|
|
|
const value = Number(occurredAt);
|
|
|
|
|
|
|
|
|
|
|
|
return Number.isFinite(value) ? value : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function normalizePageTotal(total: number | string) {
|
|
|
|
|
|
const value = Number(total);
|
|
|
|
|
|
|
|
|
|
|
|
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function normalizeProductActivityTimelineItem(
|
|
|
|
|
|
item: ProductActivityTimelineItemResponse
|
|
|
|
|
|
): Api.Product.ProductActivityTimelineItem {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
id: normalizeStringId(item.id),
|
|
|
|
|
|
operatorUserId: normalizeNullableStringId(item.operatorUserId),
|
|
|
|
|
|
targetUserId: normalizeNullableStringId(item.targetUserId),
|
|
|
|
|
|
occurredAt: normalizeOccurredAt(item.occurredAt)
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function createProductActivityTimelinePageQuery(params: Api.Product.ProductActivityTimelinePageParams) {
|
|
|
|
|
|
const query = new URLSearchParams();
|
|
|
|
|
|
|
|
|
|
|
|
query.append('pageNo', String(params.pageNo));
|
|
|
|
|
|
query.append('pageSize', String(params.pageSize));
|
|
|
|
|
|
|
|
|
|
|
|
if (params.activityType) {
|
|
|
|
|
|
query.append('activityType', params.activityType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
params.actionTypes?.forEach(actionType => {
|
|
|
|
|
|
if (actionType) {
|
|
|
|
|
|
query.append('actionTypes', actionType);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (params.startTime && params.endTime) {
|
|
|
|
|
|
query.append('startTime', params.startTime);
|
|
|
|
|
|
query.append('endTime', params.endTime);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return query.toString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 21:13:21 +08:00
|
|
|
|
/** 获取产品分页 */
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export async function fetchGetProductPage(params?: Api.Product.ProductSearchParams) {
|
|
|
|
|
|
const result = await request<ProductPageResponse>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/page`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<ProductPageResponse>, data => ({
|
|
|
|
|
|
...data,
|
|
|
|
|
|
list: data.list.map(normalizeProduct)
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-09 11:30:34 +08:00
|
|
|
|
/** 获取产品入口页概览统计 */
|
|
|
|
|
|
export function fetchGetProductOverviewSummary() {
|
|
|
|
|
|
return request<Api.Product.ProductOverviewSummary>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/overview-summary`,
|
|
|
|
|
|
method: 'get'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 21:13:21 +08:00
|
|
|
|
/** 获取产品详情 */
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export async function fetchGetProduct(id: string) {
|
|
|
|
|
|
const result = await request<ProductResponse>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/get`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { id }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<ProductResponse>, normalizeProduct);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 21:13:21 +08:00
|
|
|
|
/** 新增产品 */
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export async function fetchCreateProduct(data: Api.Product.SaveProductParams) {
|
|
|
|
|
|
const result = await request<string | number>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/create`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-12 21:41:39 +08:00
|
|
|
|
/** 创建产品(含初始团队,原子接口) */
|
|
|
|
|
|
export async function fetchCreateProductWithTeam(data: Api.Product.CreateProductWithTeamParams) {
|
|
|
|
|
|
const result = await request<string | number>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/create-with-team`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 21:13:21 +08:00
|
|
|
|
/** 更新产品 */
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export function fetchUpdateProduct(data: Api.Product.UpdateProductParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/update`,
|
|
|
|
|
|
method: 'put',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 21:13:21 +08:00
|
|
|
|
/** 改变产品状态 */
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export function fetchChangeProductStatus(data: Api.Product.ChangeProductStatusParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/change-status`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 21:13:21 +08:00
|
|
|
|
/** 删除产品 */
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export function fetchDeleteProduct(data: Api.Product.DeleteProductParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/delete`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-06 17:50:29 +08:00
|
|
|
|
// ========== 产品需求 API ==========
|
|
|
|
|
|
const REQUIREMENT_PREFIX = `${WEB_SERVICE_PREFIX}/project/product/requirement`;
|
|
|
|
|
|
|
|
|
|
|
|
type RequirementResponse = Omit<
|
|
|
|
|
|
Api.Product.Requirement,
|
2026-05-13 23:09:35 +08:00
|
|
|
|
| 'id'
|
|
|
|
|
|
| 'parentId'
|
|
|
|
|
|
| 'moduleId'
|
|
|
|
|
|
| 'proposerId'
|
|
|
|
|
|
| 'currentHandlerUserId'
|
|
|
|
|
|
| 'implementProjectId'
|
|
|
|
|
|
| 'sourceBizId'
|
|
|
|
|
|
| 'attachments'
|
2026-05-06 17:50:29 +08:00
|
|
|
|
> & {
|
|
|
|
|
|
id: string | number;
|
|
|
|
|
|
parentId: string | number;
|
|
|
|
|
|
moduleId: string | number;
|
|
|
|
|
|
proposerId: string | number;
|
|
|
|
|
|
currentHandlerUserId?: string | number | null;
|
|
|
|
|
|
implementProjectId?: string | number | null;
|
2026-05-09 18:15:10 +08:00
|
|
|
|
implementProjectName?: string | null;
|
2026-05-06 17:50:29 +08:00
|
|
|
|
sourceBizId?: string | number | null;
|
2026-05-13 23:09:35 +08:00
|
|
|
|
attachments?: AttachmentItemResponse[] | null;
|
2026-05-06 17:50:29 +08:00
|
|
|
|
children?: RequirementResponse[];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
type RequirementPageResponse = Api.Product.PageResult<RequirementResponse>;
|
|
|
|
|
|
|
2026-05-13 23:09:35 +08:00
|
|
|
|
type AttachmentItemResponse = Omit<Api.Project.AttachmentItem, 'fileId'> & {
|
|
|
|
|
|
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)
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-06 17:50:29 +08:00
|
|
|
|
function normalizeRequirement(requirement: RequirementResponse): Api.Product.Requirement {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...requirement,
|
|
|
|
|
|
id: normalizeStringId(requirement.id),
|
|
|
|
|
|
parentId: normalizeStringId(requirement.parentId),
|
|
|
|
|
|
moduleId: normalizeStringId(requirement.moduleId),
|
|
|
|
|
|
proposerId: normalizeStringId(requirement.proposerId),
|
|
|
|
|
|
currentHandlerUserId: normalizeNullableStringId(requirement.currentHandlerUserId),
|
|
|
|
|
|
implementProjectId: normalizeNullableStringId(requirement.implementProjectId),
|
2026-05-09 18:15:10 +08:00
|
|
|
|
implementProjectName: requirement.implementProjectName ?? null,
|
2026-05-06 17:50:29 +08:00
|
|
|
|
sourceBizId: normalizeNullableStringId(requirement.sourceBizId),
|
2026-05-13 23:09:35 +08:00
|
|
|
|
attachments: normalizeAttachments(requirement.attachments),
|
2026-05-06 17:50:29 +08:00
|
|
|
|
children: requirement.children?.map(normalizeRequirement)
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取需求分页列表 */
|
|
|
|
|
|
export async function fetchGetRequirementPage(params?: Api.Product.RequirementSearchParams) {
|
|
|
|
|
|
const result = await request<RequirementPageResponse>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/page`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<RequirementPageResponse>, data => ({
|
|
|
|
|
|
...data,
|
|
|
|
|
|
list: data.list.map(normalizeRequirement)
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取需求树形列表(支持分页,pageSize只算父需求) */
|
|
|
|
|
|
export async function fetchGetRequirementTree(params?: Api.Product.RequirementSearchParams) {
|
|
|
|
|
|
const result = await request<Api.Product.PageResult<RequirementResponse>>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/tree`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<Api.Product.PageResult<RequirementResponse>>, data => ({
|
|
|
|
|
|
...data,
|
|
|
|
|
|
list: data.list.map(normalizeRequirement)
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取需求详情 */
|
|
|
|
|
|
export async function fetchGetRequirement(id: string, productId: string) {
|
|
|
|
|
|
const result = await request<RequirementResponse>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/get`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { id, productId }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<RequirementResponse>, normalizeRequirement);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 创建需求 */
|
|
|
|
|
|
export async function fetchCreateRequirement(data: Api.Product.SaveRequirementParams) {
|
|
|
|
|
|
const result = await request<string | number>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/create`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 更新需求 */
|
|
|
|
|
|
export function fetchUpdateRequirement(data: Api.Product.UpdateRequirementParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/update`,
|
|
|
|
|
|
method: 'put',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 变更需求状态 */
|
|
|
|
|
|
export function fetchChangeRequirementStatus(data: Api.Product.ChangeRequirementStatusParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/change-status`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 删除需求 */
|
|
|
|
|
|
export function fetchDeleteRequirement(data: Api.Product.DeleteRequirementParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/delete`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 拆分需求 */
|
|
|
|
|
|
export async function fetchSplitRequirement(data: Api.Product.SplitRequirementParams) {
|
|
|
|
|
|
const result = await request<string | number>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/split`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 关闭需求 */
|
|
|
|
|
|
export function fetchCloseRequirement(data: Api.Product.CloseRequirementParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/close`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取需求可执行的状态动作列表 */
|
|
|
|
|
|
export async function fetchGetRequirementAllowedTransitions(requirementId: string, productId: string) {
|
|
|
|
|
|
const result = await request<Api.Product.RequirementLifecycleAction[]>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/allowed-transitions`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { requirementId, productId }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<Api.Product.RequirementLifecycleAction[]>, data => data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取需求生命周期信息 */
|
|
|
|
|
|
export async function fetchGetRequirementLifecycle(requirementId: string, productId: string) {
|
|
|
|
|
|
const result = await request<Api.Product.RequirementLifecycleInfo>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/lifecycle`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { requirementId, productId }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<Api.Product.RequirementLifecycleInfo>, data => data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 17:09:53 +08:00
|
|
|
|
/** 获取需求所有状态字典 */
|
|
|
|
|
|
export async function fetchGetRequirementStatusDict() {
|
|
|
|
|
|
const result = await request<Api.Product.RequirementStatusDict[]>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/status/dict`,
|
|
|
|
|
|
method: 'get'
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<Api.Product.RequirementStatusDict[]>, data => data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取需求终止态状态字典 */
|
|
|
|
|
|
export async function fetchGetRequirementTerminalStatusDict() {
|
|
|
|
|
|
const result = await request<Api.Product.RequirementStatusDict[]>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/status/dict/terminal`,
|
|
|
|
|
|
method: 'get'
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<Api.Product.RequirementStatusDict[]>, data => data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 21:13:21 +08:00
|
|
|
|
/** 判断产品需求是否已分流生成项目需求 */
|
|
|
|
|
|
export async function fetchHasDispatchedProjectRequirement(requirementId: string, productId: string) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/has-dispatched`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { requirementId, productId }
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 根据当前产品需求id获取对应地,所流转到项目侧的项目需求id */
|
|
|
|
|
|
export async function fetchGetDispatchedProjectLink(productRequirementId: string) {
|
|
|
|
|
|
return request<{ projectRequirementId: string; projectId: string }>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/dispatched-project-link`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { productRequirementId }
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-06 17:50:29 +08:00
|
|
|
|
// ========== 模块管理 API ==========
|
|
|
|
|
|
type RequirementModuleResponse = Omit<Api.Product.RequirementModule, 'id' | 'parentId' | 'productId'> & {
|
|
|
|
|
|
id: string | number;
|
|
|
|
|
|
parentId: string | number;
|
|
|
|
|
|
productId: string | number;
|
|
|
|
|
|
children?: RequirementModuleResponse[];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function normalizeRequirementModule(module: RequirementModuleResponse): Api.Product.RequirementModule {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...module,
|
|
|
|
|
|
id: normalizeStringId(module.id),
|
|
|
|
|
|
parentId: normalizeStringId(module.parentId),
|
|
|
|
|
|
productId: normalizeStringId(module.productId),
|
|
|
|
|
|
children: module.children?.map(normalizeRequirementModule)
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 获取需求模块树 */
|
|
|
|
|
|
export async function fetchGetRequirementModuleTree(productId: string) {
|
|
|
|
|
|
const result = await request<RequirementModuleResponse[]>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/module/tree`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { productId }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<RequirementModuleResponse[]>, data =>
|
|
|
|
|
|
data.map(normalizeRequirementModule)
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 创建需求模块 */
|
|
|
|
|
|
export async function fetchCreateRequirementModule(data: Api.Product.SaveRequirementModuleParams) {
|
|
|
|
|
|
const result = await request<string | number>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/module/create`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 更新需求模块 */
|
|
|
|
|
|
export function fetchUpdateRequirementModule(data: Api.Product.SaveRequirementModuleParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/module/update`,
|
|
|
|
|
|
method: 'put',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 删除需求模块 */
|
|
|
|
|
|
export function fetchDeleteRequirementModule(data: Api.Product.DeleteRequirementModuleParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${REQUIREMENT_PREFIX}/module/delete`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export async function fetchGetProductSettings(id: string) {
|
|
|
|
|
|
const result = await request<Api.Product.ProductSettings>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/${id}/settings`,
|
|
|
|
|
|
method: 'get'
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<Api.Product.ProductSettings>, normalizeProductSettings);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function fetchUpdateProductSettingBaseInfo(id: string, data: Api.Product.UpdateProductSettingBaseInfoParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/${id}/settings/base-info`,
|
|
|
|
|
|
method: 'put',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function fetchGetProductMembers(id: string) {
|
|
|
|
|
|
const result = await request<Api.Product.ProductMember[]>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/${id}/members`,
|
|
|
|
|
|
method: 'get'
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<Api.Product.ProductMember[]>, data =>
|
|
|
|
|
|
data.map(normalizeProductMember)
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-24 16:38:43 +08:00
|
|
|
|
export async function fetchGetProductActivityTimelinePage(
|
|
|
|
|
|
id: string,
|
|
|
|
|
|
params: Api.Product.ProductActivityTimelinePageParams
|
|
|
|
|
|
) {
|
|
|
|
|
|
const query = createProductActivityTimelinePageQuery(params);
|
|
|
|
|
|
const url = query ? `${PRODUCT_PREFIX}/${id}/activities/page?${query}` : `${PRODUCT_PREFIX}/${id}/activities/page`;
|
|
|
|
|
|
const result = await request<ProductActivityTimelinePageResponse>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url,
|
|
|
|
|
|
method: 'get'
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<ProductActivityTimelinePageResponse>, data => ({
|
|
|
|
|
|
total: normalizePageTotal(data.total),
|
|
|
|
|
|
list: data.list.map(normalizeProductActivityTimelineItem)
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-23 09:05:55 +08:00
|
|
|
|
export async function fetchCreateProductMember(id: string, data: Api.Product.CreateProductMemberParams) {
|
|
|
|
|
|
const result = await request<string | number>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/${id}/members`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function fetchUpdateProductMember(id: string, memberId: string, data: Api.Product.UpdateProductMemberParams) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/${id}/members/${memberId}`,
|
|
|
|
|
|
method: 'put',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function fetchInactiveProductMember(
|
|
|
|
|
|
id: string,
|
|
|
|
|
|
memberId: string,
|
|
|
|
|
|
data: Api.Product.InactiveProductMemberParams
|
|
|
|
|
|
) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
...safeJsonRequestConfig,
|
|
|
|
|
|
url: `${PRODUCT_PREFIX}/${id}/members/${memberId}/inactive`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|