import { postgrestGet, type PostgrestParams } from "../postgrest-client"; /** * 数据提取工具函数 * 统一处理不同格式的API响应 */ function extractApiData(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('contract_categories', params); if (response.error) { return { error: response.error, status: response.status || 500 }; } const categories = extractApiData(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('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(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 = {}; 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('contract_categories', { select: 'id', filter: { 'name': `ilike.*${cleanKeyword}*` }, token }); if (categoryResponse.data) { const categories = extractApiData(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('contract_categories', { select: 'id', filter: { 'name': `eq.${category}` }, token }); if (categoryResponse.data) { const categories = extractApiData(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('contract_templates', params); if (response.error) { return { error: response.error, status: response.status || 500 }; } const templates = extractApiData(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('contract_templates', params); if (response.error) { return { error: response.error, status: response.status || 500 }; } const templates = extractApiData(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('contract_templates', params); if (response.error) { return { error: response.error, status: response.status || 500 }; } const templates = extractApiData(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 = {} ) { return await getContractTemplates({ keyword: query, ...filters }); }