Files
leaudit-platform-frontend/app/api/contract-template/templates.ts
T

388 lines
11 KiB
TypeScript

import { postgrestGet, type PostgrestParams } from "../postgrest-client";
/**
* 数据提取工具函数
* 统一处理不同格式的API响应
*/
function extractApiData<T>(responseData: unknown): T | null {
if (!responseData) {
return null;
}
// 格式1: { code: number, msg: string, data: T }
if (typeof responseData === 'object' && responseData !== null &&
'code' in responseData &&
'data' in responseData &&
(responseData as { data: unknown }).data) {
return (responseData as { data: T }).data;
}
// 格式2: 直接是数据对象
return responseData as T;
}
// 类型定义
export interface ContractCategory {
id: number;
name: string;
icon?: string;
description?: string;
sort_order: number;
created_at: string;
updated_at: string;
}
export interface ContractTemplate {
id: number;
template_code: string;
title: string;
category_id: number;
description?: string;
file_path?: string;
file_format: 'docx' | 'pdf' | 'txt';
is_featured: boolean;
created_at: string;
updated_at: string;
pdf_file_path?: string;
// 关联的分类信息
category?: ContractCategory;
}
export interface TemplateSearchParams {
keyword?: string;
category?: string;
category_id?: number;
file_format?: string;
is_featured?: boolean;
page?: number;
pageSize?: number;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
token?: string; // JWT token
}
export interface SearchResult {
templates: ContractTemplate[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}
/**
* 获取所有合同分类
* @param jwt JWT token (可选)
*/
export async function getContractCategories(jwt?: string) {
try {
const params: PostgrestParams = {
select: '*',
order: 'sort_order.asc,name.asc',
token: jwt
};
const response = await postgrestGet<ContractCategory[]>('contract_categories', params);
if (response.error) {
return { error: response.error, status: response.status || 500 };
}
const categories = extractApiData<ContractCategory[]>(response.data) || [];
return { data: categories };
} catch (error) {
console.error('获取合同分类失败:', error);
return {
error: error instanceof Error ? error.message : '获取合同分类失败',
status: 500
};
}
}
/**
* 获取所有合同分类及其模板数量(使用聚合查询)
* @param jwt JWT token (可选)
*/
export async function getContractCategoriesWithCount(jwt?: string) {
try {
// 获取所有分类
const categoriesResponse = await postgrestGet<ContractCategory[]>('contract_categories', {
select: '*',
order: 'sort_order.asc,name.asc',
token: jwt
});
if (categoriesResponse.error) {
return { error: categoriesResponse.error, status: categoriesResponse.status || 500 };
}
const categories = extractApiData<ContractCategory[]>(categoriesResponse.data) || [];
// 获取每个分类的模板数量
const categoriesWithCount = await Promise.all(
categories.map(async (category) => {
try {
// 简化方案:获取该分类下的所有模板ID,然后计算数量
const countResponse = await postgrestGet<{ id: number }[]>('contract_templates', {
select: 'id',
filter: { 'category_id': `eq.${category.id}` },
token: jwt
});
let templateCount = 0;
if (!countResponse.error && countResponse.data) {
const templates = extractApiData<{ id: number }[]>(countResponse.data) || [];
templateCount = templates.length;
}
return {
...category,
template_count: templateCount
};
} catch (error) {
console.error(`获取分类${category.name}的模板数量失败:`, error);
return {
...category,
template_count: 0
};
}
})
);
return { data: categoriesWithCount };
} catch (error) {
console.error('获取分类及模板数量失败:', error);
return {
error: error instanceof Error ? error.message : '获取分类及模板数量失败',
status: 500
};
}
}
// 扩展分类接口,包含模板数量
export interface ContractCategoryWithCount extends ContractCategory {
template_count: number;
}
/**
* 获取合同模板列表(支持筛选和分页)
*/
export async function getContractTemplates(searchParams: TemplateSearchParams = {}) {
try {
const {
keyword,
category,
category_id,
file_format,
is_featured,
page = 1,
pageSize = 6,
sortBy = 'updated_at',
sortOrder = 'desc',
token
} = searchParams;
// 构建查询参数
const params: PostgrestParams = {
select: 'id,template_code,title,category_id,description,file_path,file_format,is_featured,created_at,updated_at,pdf_file_path,category:contract_categories(id,name,icon,description)',
limit: pageSize,
offset: (page - 1) * pageSize,
order: `${sortBy}.${sortOrder}`,
token
};
// 构建过滤条件
const filters: Record<string, string> = {};
if (category_id) {
filters['category_id'] = `eq.${category_id}`;
}
if (file_format) {
filters['file_format'] = `eq.${file_format}`;
}
if (is_featured !== undefined) {
filters['is_featured'] = `eq.${is_featured}`;
}
// 处理关键词搜索
if (keyword && keyword.trim()) {
const cleanKeyword = keyword.trim();
// 先查询匹配的分类ID
let matchingCategoryIds: number[] = [];
try {
const categoryResponse = await postgrestGet<ContractCategory[]>('contract_categories', {
select: 'id',
filter: { 'name': `ilike.*${cleanKeyword}*` },
token
});
if (categoryResponse.data) {
const categories = extractApiData<ContractCategory[]>(categoryResponse.data) || [];
matchingCategoryIds = categories.map(cat => cat.id);
// console.log('匹配的分类ID:', matchingCategoryIds);
}
} catch (error) {
console.error('查询分类失败:', error);
}
// 构建搜索条件
const searchConditions = [
`title.ilike.*${cleanKeyword}*`,
`description.ilike.*${cleanKeyword}*`,
`template_code.ilike.*${cleanKeyword}*`,
// 如果有匹配的分类,添加分类ID条件
...(matchingCategoryIds.length > 0 ? [`category_id.in.(${matchingCategoryIds.join(',')})`] : [])
];
params.or = `(${searchConditions.join(',')})`;
// console.log('搜索关键词:', cleanKeyword);
// console.log('搜索条件:', params.or);
}
// 如果有分类名称,需要先获取分类ID
if (category && !category_id) {
const categoryResponse = await postgrestGet<ContractCategory[]>('contract_categories', {
select: 'id',
filter: { 'name': `eq.${category}` },
token
});
if (categoryResponse.data) {
const categories = extractApiData<ContractCategory[]>(categoryResponse.data) || [];
if (categories.length > 0) {
filters['category_id'] = `eq.${categories[0].id}`;
}
}
}
if (Object.keys(filters).length > 0) {
params.filter = filters;
}
// 执行查询
const response = await postgrestGet<ContractTemplate[]>('contract_templates', params);
if (response.error) {
return { error: response.error, status: response.status || 500 };
}
const templates = extractApiData<ContractTemplate[]>(response.data) || [];
// 获取总数(用于分页)
const countParams: PostgrestParams = {
select: 'id',
filter: params.filter,
or: params.or,
token
};
const countResponse = await postgrestGet<{ id: number }[]>('contract_templates', countParams);
let total = 0;
if (!countResponse.error && countResponse.data) {
const countData = extractApiData<{ id: number }[]>(countResponse.data) || [];
total = countData.length;
}
return {
data: {
templates,
total,
page,
pageSize,
totalPages: Math.ceil(total / pageSize)
} as SearchResult
};
} catch (error) {
console.error('获取合同模板失败:', error);
return {
error: error instanceof Error ? error.message : '获取合同模板失败',
status: 500
};
}
}
/**
* 根据ID获取单个合同模板
* @param id 模板ID
* @param jwt JWT token (可选)
*/
export async function getContractTemplate(id: string | number, jwt?: string) {
try {
const params: PostgrestParams = {
select: 'id,template_code,title,category_id,description,file_path,file_format,is_featured,created_at,updated_at,pdf_file_path,category:contract_categories(id,name,icon,description)',
filter: { 'id': `eq.${id}` },
token: jwt
};
const response = await postgrestGet<ContractTemplate[]>('contract_templates', params);
if (response.error) {
return { error: response.error, status: response.status || 500 };
}
const templates = extractApiData<ContractTemplate[]>(response.data) || [];
if (templates.length === 0) {
return { error: '模板不存在', status: 404 };
}
return { data: templates[0] };
} catch (error) {
console.error('获取合同模板详情失败:', error);
return {
error: error instanceof Error ? error.message : '获取合同模板详情失败',
status: 500
};
}
}
/**
* 获取推荐模板
* @param limit 数量限制
* @param jwt JWT token (可选)
*/
export async function getFeaturedTemplates(limit: number = 6, jwt?: string) {
try {
const params: PostgrestParams = {
select: 'id,template_code,title,category_id,description,file_path,file_format,is_featured,created_at,updated_at,pdf_file_path,category:contract_categories(id,name,icon,description)',
filter: { 'is_featured': 'eq.true' },
order: 'updated_at.desc',
limit,
token: jwt
};
const response = await postgrestGet<ContractTemplate[]>('contract_templates', params);
if (response.error) {
return { error: response.error, status: response.status || 500 };
}
const templates = extractApiData<ContractTemplate[]>(response.data) || [];
return { data: templates };
} catch (error) {
console.error('获取推荐模板失败:', error);
return {
error: error instanceof Error ? error.message : '获取推荐模板失败',
status: 500
};
}
}
/**
* 搜索合同模板(智能搜索)
* @param query 搜索关键词
* @param filters 过滤条件
*/
export async function searchContractTemplates(
query: string,
filters: Omit<TemplateSearchParams, 'keyword'> = {}
) {
return await getContractTemplates({
keyword: query,
...filters
});
}