341 lines
9.3 KiB
TypeScript
341 lines
9.3 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';
|
|
}
|
|
|
|
export interface SearchResult {
|
|
templates: ContractTemplate[];
|
|
total: number;
|
|
page: number;
|
|
pageSize: number;
|
|
totalPages: number;
|
|
}
|
|
|
|
/**
|
|
* 获取所有合同分类
|
|
*/
|
|
export async function getContractCategories() {
|
|
try {
|
|
const params: PostgrestParams = {
|
|
select: '*',
|
|
order: 'sort_order.asc,name.asc'
|
|
};
|
|
|
|
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
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取所有合同分类及其模板数量(使用聚合查询)
|
|
*/
|
|
export async function getContractCategoriesWithCount() {
|
|
try {
|
|
// 获取所有分类
|
|
const categoriesResponse = await postgrestGet<ContractCategory[]>('contract_categories', {
|
|
select: '*',
|
|
order: 'sort_order.asc,name.asc'
|
|
});
|
|
|
|
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}` }
|
|
});
|
|
|
|
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'
|
|
} = searchParams;
|
|
|
|
// 构建查询参数
|
|
const params: PostgrestParams = {
|
|
select: 'id,template_code,title,category_id,description,file_format,is_featured,created_at,updated_at,contract_categories(id,name,icon)',
|
|
limit: pageSize,
|
|
offset: (page - 1) * pageSize,
|
|
order: `${sortBy}.${sortOrder}`
|
|
};
|
|
|
|
// 构建过滤条件
|
|
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();
|
|
// 使用PostgREST的or条件进行模糊搜索,正确的格式需要括号
|
|
params.or = `(title.ilike.*${cleanKeyword}*,description.ilike.*${cleanKeyword}*,template_code.ilike.*${cleanKeyword}*)`;
|
|
}
|
|
|
|
// 如果有分类名称,需要先获取分类ID
|
|
if (category && !category_id) {
|
|
const categoryResponse = await postgrestGet<ContractCategory[]>('contract_categories', {
|
|
select: 'id',
|
|
filter: { 'name': `eq.${category}` }
|
|
});
|
|
|
|
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
|
|
};
|
|
|
|
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获取单个合同模板
|
|
*/
|
|
export async function getContractTemplate(id: string | number) {
|
|
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,contract_categories(id,name,icon,description)',
|
|
filter: { 'id': `eq.${id}` }
|
|
};
|
|
|
|
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
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取推荐模板
|
|
*/
|
|
export async function getFeaturedTemplates(limit: number = 6) {
|
|
try {
|
|
const params: PostgrestParams = {
|
|
select: 'id,template_code,title,category_id,description,file_format,is_featured,created_at,updated_at,contract_categories(id,name,icon)',
|
|
filter: { 'is_featured': 'eq.true' },
|
|
order: 'updated_at.desc',
|
|
limit
|
|
};
|
|
|
|
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
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 搜索合同模板(智能搜索)
|
|
*/
|
|
export async function searchContractTemplates(
|
|
query: string,
|
|
filters: Omit<TemplateSearchParams, 'keyword'> = {}
|
|
) {
|
|
return await getContractTemplates({
|
|
keyword: query,
|
|
...filters
|
|
});
|
|
}
|