合同基本列表数据查询基本完善

This commit is contained in:
2025-05-30 17:40:19 +08:00
parent d0c479f9d4
commit d292dcfccf
5 changed files with 714 additions and 295 deletions
+341
View File
@@ -0,0 +1,341 @@
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
});
}