remix图标本地化
This commit is contained in:
@@ -0,0 +1,789 @@
|
||||
## 🗄️ PostgreSQL数据对接规范
|
||||
|
||||
### 概述
|
||||
|
||||
本系统使用PostgreSQL作为主数据库,通过PostgREST提供RESTful API接口。所有数据操作统一通过封装的`postgrest-client.ts`模块进行,确保数据访问的一致性和可维护性。
|
||||
|
||||
### 技术架构
|
||||
|
||||
```
|
||||
前端组件 (Remix Routes)
|
||||
↓
|
||||
API层 (app/api/[module]/[api].ts)
|
||||
↓
|
||||
PostgREST客户端 (postgrest-client.ts)
|
||||
↓
|
||||
PostgreSQL数据库
|
||||
```
|
||||
|
||||
### 基础封装方法
|
||||
|
||||
#### 1. 导入PostgREST客户端
|
||||
|
||||
```typescript
|
||||
import {
|
||||
postgrestGet,
|
||||
postgrestPost,
|
||||
postgrestPut,
|
||||
postgrestDelete,
|
||||
type PostgrestParams
|
||||
} from "../postgrest-client";
|
||||
```
|
||||
|
||||
#### 2. 查询操作 (GET)
|
||||
|
||||
```typescript
|
||||
// 基础查询
|
||||
const params: PostgrestParams = {
|
||||
select: '*',
|
||||
filter: {
|
||||
'id': `eq.${id}`,
|
||||
'status': `eq.active`
|
||||
},
|
||||
order: 'created_at.desc',
|
||||
limit: 20,
|
||||
offset: 0
|
||||
};
|
||||
|
||||
const response = await postgrestGet('table_name', params);
|
||||
|
||||
// 复杂查询示例
|
||||
const complexParams: PostgrestParams = {
|
||||
select: 'id,name,status,created_at,user_id,users(name)', // 关联查询
|
||||
filter: {
|
||||
'created_at': `gte.2023-01-01`,
|
||||
'status': `in.(active,pending)`
|
||||
},
|
||||
or: 'name.ilike.*keyword*,description.ilike.*keyword*', // OR条件
|
||||
order: 'created_at.desc,name.asc',
|
||||
limit: 50
|
||||
};
|
||||
|
||||
const response = await postgrestGet('documents', complexParams);
|
||||
```
|
||||
|
||||
#### 3. 创建操作 (POST)
|
||||
|
||||
```typescript
|
||||
// 单条记录创建
|
||||
const newRecord = {
|
||||
name: '文档名称',
|
||||
document_number: 'DOC001',
|
||||
type_id: 1,
|
||||
user_id: 123,
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
const response = await postgrestPost('documents', newRecord);
|
||||
|
||||
// 批量创建
|
||||
const batchRecords = [
|
||||
{ name: '文档1', type_id: 1 },
|
||||
{ name: '文档2', type_id: 2 }
|
||||
];
|
||||
|
||||
const response = await postgrestPost('documents', batchRecords);
|
||||
```
|
||||
|
||||
#### 4. 更新操作 (PUT/PATCH)
|
||||
|
||||
```typescript
|
||||
// 根据ID更新
|
||||
const updateData = {
|
||||
status: 'completed',
|
||||
audit_status: 1,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
const response = await postgrestPut(
|
||||
'documents',
|
||||
updateData,
|
||||
{ id: documentId }
|
||||
);
|
||||
|
||||
// 根据条件批量更新
|
||||
const response = await postgrestPut(
|
||||
'evaluation_results',
|
||||
{ status: 'reviewed' },
|
||||
{ document_id: docId, status: 'pending' }
|
||||
);
|
||||
```
|
||||
|
||||
#### 5. 删除操作 (DELETE)
|
||||
|
||||
```typescript
|
||||
// 根据ID删除
|
||||
const response = await postgrestDelete('documents', {
|
||||
filter: { 'id': `eq.${id}` }
|
||||
});
|
||||
|
||||
// 条件删除
|
||||
const response = await postgrestDelete('temp_files', {
|
||||
filter: { 'created_at': `lt.2023-01-01` }
|
||||
});
|
||||
```
|
||||
|
||||
### API层实现规范
|
||||
|
||||
#### 1. 文件结构
|
||||
```
|
||||
app/api/
|
||||
├── [module]/ # 功能模块
|
||||
│ ├── [feature].ts # 具体功能API
|
||||
│ └── types.ts # 类型定义 (可选)
|
||||
├── postgrest-client.ts # PostgREST客户端
|
||||
├── axios-client.ts # HTTP客户端
|
||||
└── error-handler.ts # 错误处理
|
||||
```
|
||||
|
||||
#### 2. API文件模板
|
||||
|
||||
```typescript
|
||||
// app/api/evaluation_points/reviews.ts
|
||||
import { postgrestGet, type PostgrestParams, postgrestPut, postgrestPost } from "../postgrest-client";
|
||||
import { getDocument } from "~/api/files/documents";
|
||||
import { formatDate } from "~/utils";
|
||||
|
||||
/**
|
||||
* 数据提取工具函数
|
||||
* 统一处理不同格式的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;
|
||||
}
|
||||
|
||||
// 类型定义
|
||||
interface EvaluationResult {
|
||||
id: string | number;
|
||||
document_id: string | number;
|
||||
evaluation_point_id: string | number;
|
||||
evaluated_results?: {
|
||||
result?: boolean;
|
||||
message?: string;
|
||||
data?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface EvaluationPoint {
|
||||
id: string | number;
|
||||
evaluation_point_groups_id: string | number;
|
||||
suggestion_message_type?: string;
|
||||
suggestion_message?: string;
|
||||
score?: number;
|
||||
updated_at?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// 前端使用的结果类型
|
||||
interface ReviewPointResult {
|
||||
id: string | number;
|
||||
title: string;
|
||||
groupName: string;
|
||||
status: string;
|
||||
content: string;
|
||||
suggestion: string;
|
||||
result?: boolean;
|
||||
score: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评查点数据
|
||||
* @param fileId 文件ID
|
||||
* @returns 评查点结果列表和统计数据
|
||||
*/
|
||||
export async function getReviewPoints(fileId: string) {
|
||||
try {
|
||||
// 步骤1: 获取文档基础数据
|
||||
const documentData = await getDocument(fileId);
|
||||
if (documentData.error) {
|
||||
return { error: documentData.error, status: documentData.status || 500 };
|
||||
}
|
||||
|
||||
// 步骤2: 查询评查结果
|
||||
const evaluationResultsParams: PostgrestParams = {
|
||||
select: '*',
|
||||
filter: { 'document_id': `eq.${fileId}` }
|
||||
};
|
||||
const evaluationResultsResponse = await postgrestGet('evaluation_results', evaluationResultsParams);
|
||||
|
||||
if (evaluationResultsResponse.error) {
|
||||
return { error: evaluationResultsResponse.error, status: evaluationResultsResponse.status };
|
||||
}
|
||||
|
||||
const evaluationResultsData = extractApiData<EvaluationResult[]>(evaluationResultsResponse.data) || [];
|
||||
|
||||
if (evaluationResultsData.length <= 0) {
|
||||
return {
|
||||
data: [],
|
||||
stats: { total: 0, success: 0, warning: 0, error: 0, score: 0 },
|
||||
error: '获取评查结果数据失败'
|
||||
};
|
||||
}
|
||||
|
||||
// 步骤3: 获取评查点详情
|
||||
const evaluationPointIds = evaluationResultsData.map(item => item.evaluation_point_id).filter(Boolean);
|
||||
|
||||
const evaluationPointsParams: PostgrestParams = {
|
||||
select: '*',
|
||||
filter: { 'id': `in.(${evaluationPointIds.join(',')})` }
|
||||
};
|
||||
const evaluationPointsResponse = await postgrestGet('evaluation_points', evaluationPointsParams);
|
||||
|
||||
if (evaluationPointsResponse.error) {
|
||||
return { error: evaluationPointsResponse.error, status: evaluationPointsResponse.status };
|
||||
}
|
||||
|
||||
const evaluationPointsData = extractApiData<EvaluationPoint[]>(evaluationPointsResponse.data) || [];
|
||||
|
||||
// 步骤4: 数据处理和转换
|
||||
const resultData: ReviewPointResult[] = evaluationResultsData.map(result => {
|
||||
const point = evaluationPointsData.find(p => p.id === result.evaluation_point_id);
|
||||
|
||||
return {
|
||||
id: result.id,
|
||||
title: result.evaluated_results?.message || '',
|
||||
groupName: point?.group_name || '',
|
||||
status: point?.suggestion_message_type || '',
|
||||
content: result.evaluated_results?.data || '',
|
||||
suggestion: point?.suggestion_message || '',
|
||||
result: result.evaluated_results?.result,
|
||||
score: point?.score || 0
|
||||
};
|
||||
});
|
||||
|
||||
// 步骤5: 统计数据计算
|
||||
const stats = {
|
||||
total: resultData.length,
|
||||
success: resultData.filter(item => item.result === true).length,
|
||||
warning: resultData.filter(item => item.status === 'warning').length,
|
||||
error: resultData.filter(item => item.status === 'error').length,
|
||||
score: resultData.reduce((sum, item) => sum + item.score, 0)
|
||||
};
|
||||
|
||||
return {
|
||||
data: resultData,
|
||||
stats,
|
||||
document: documentData.data
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取评查数据失败:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : '获取评查数据失败',
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评查结果
|
||||
* @param resultId 评查结果ID
|
||||
* @param editAuditStatusId 审核状态ID
|
||||
* @param result 评查结果
|
||||
* @param message 评查意见
|
||||
*/
|
||||
export async function updateReviewResult(
|
||||
resultId: string,
|
||||
editAuditStatusId: string | number,
|
||||
result: string,
|
||||
message: string
|
||||
): Promise<{
|
||||
data?: unknown;
|
||||
error?: string;
|
||||
status?: number;
|
||||
}> {
|
||||
try {
|
||||
if (!resultId) {
|
||||
return { error: '评查结果ID不能为空', status: 400 };
|
||||
}
|
||||
|
||||
// 获取当前数据
|
||||
const currentResultResponse = await postgrestGet('evaluation_results', {
|
||||
select: '*',
|
||||
filter: { id: `eq.${resultId}` }
|
||||
});
|
||||
|
||||
if (currentResultResponse.error) {
|
||||
return { error: currentResultResponse.error, status: currentResultResponse.status };
|
||||
}
|
||||
|
||||
const currentResultData = extractApiData<EvaluationResult[]>(currentResultResponse.data);
|
||||
|
||||
if (!currentResultData || currentResultData.length === 0) {
|
||||
return { error: '未找到评查结果数据', status: 404 };
|
||||
}
|
||||
|
||||
const currentResult = currentResultData[0];
|
||||
const currentEvaluatedResults = currentResult.evaluated_results || {};
|
||||
|
||||
// 构建更新数据
|
||||
const isReview = result === 'review';
|
||||
const updatedEvaluatedResults = {
|
||||
...currentEvaluatedResults,
|
||||
...(isReview ? { message } : { result: result === 'true', message }),
|
||||
};
|
||||
|
||||
// 更新评查结果
|
||||
const resultResponse = await postgrestPut(
|
||||
'evaluation_results',
|
||||
{ evaluated_results: updatedEvaluatedResults },
|
||||
{ id: resultId }
|
||||
);
|
||||
|
||||
if (resultResponse.error) {
|
||||
return { error: resultResponse.error, status: resultResponse.status };
|
||||
}
|
||||
|
||||
// 处理审核状态
|
||||
const editAuditStatusValue = isReview ? 0 : 1;
|
||||
|
||||
if (editAuditStatusId && editAuditStatusId !== '') {
|
||||
// 更新现有记录
|
||||
const auditStatusResponse = await postgrestPut(
|
||||
'audit_status',
|
||||
{ edit_audit_status: editAuditStatusValue },
|
||||
{ id: editAuditStatusId }
|
||||
);
|
||||
|
||||
if (auditStatusResponse.error) {
|
||||
return { error: auditStatusResponse.error, status: auditStatusResponse.status };
|
||||
}
|
||||
} else {
|
||||
// 创建新记录
|
||||
const newAuditStatus = {
|
||||
document_id: currentResult.document_id,
|
||||
evaluation_point_id: currentResult.evaluation_point_id,
|
||||
evaluation_result_id: resultId,
|
||||
edit_audit_status: editAuditStatusValue
|
||||
};
|
||||
|
||||
const postResponse = await postgrestPost('audit_status', newAuditStatus);
|
||||
|
||||
if (postResponse.error) {
|
||||
return { error: postResponse.error, status: postResponse.status };
|
||||
}
|
||||
}
|
||||
|
||||
return { data: extractApiData<unknown>(resultResponse.data) };
|
||||
} catch (error) {
|
||||
console.error('更新评查结果失败:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : '更新评查结果失败',
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 错误处理规范
|
||||
|
||||
```typescript
|
||||
// 统一的错误处理格式
|
||||
interface ApiResponse<T> {
|
||||
data?: T;
|
||||
error?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
// 在API函数中的错误处理
|
||||
export async function apiFunction(): Promise<ApiResponse<DataType>> {
|
||||
try {
|
||||
const response = await postgrestGet('table_name', params);
|
||||
|
||||
if (response.error) {
|
||||
return {
|
||||
error: response.error,
|
||||
status: response.status
|
||||
};
|
||||
}
|
||||
|
||||
const data = extractApiData<DataType>(response.data);
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
error: '数据格式错误',
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
|
||||
return { data };
|
||||
} catch (error) {
|
||||
console.error('API调用失败:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : '未知错误',
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 前端路由集成规范
|
||||
|
||||
#### 1. Loader函数中调用API
|
||||
|
||||
```typescript
|
||||
// app/routes/reviews.tsx
|
||||
import { getReviewPoints, updateReviewResult, confirmReviewResults } from "~/api/evaluation_points/reviews";
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const id = url.searchParams.get('id') || undefined;
|
||||
const previousRoute = url.searchParams.get('previousRoute') || '';
|
||||
|
||||
if (!id) {
|
||||
return Response.json({ result: false, message: '文件ID不能为空' });
|
||||
}
|
||||
|
||||
// 获取评查点数据
|
||||
const reviewData = await getReviewPoints(id);
|
||||
|
||||
if ('error' in reviewData && reviewData.error) {
|
||||
console.error("获取评查点数据错误:", reviewData.error);
|
||||
return Response.json({ result: false, message: reviewData.error });
|
||||
}
|
||||
|
||||
// 确保数据格式正确
|
||||
if ('document' in reviewData && 'data' in reviewData && 'reviewInfo' in reviewData && 'stats' in reviewData) {
|
||||
return Response.json({
|
||||
previousRoute: previousRoute,
|
||||
document: reviewData.document,
|
||||
reviewPoints: reviewData.data,
|
||||
reviewInfo: reviewData.reviewInfo,
|
||||
statistics: reviewData.stats
|
||||
});
|
||||
} else {
|
||||
console.error("返回的评查数据格式不正确", JSON.stringify(reviewData, null, 2));
|
||||
return Response.json({ result: false, message: '返回的评查数据格式不正确' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取评查数据失败:', error);
|
||||
return Response.json({ result: false, message: '获取评查数据失败' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 组件中处理API响应
|
||||
|
||||
```typescript
|
||||
// 在React组件中使用
|
||||
export default function ReviewDetails() {
|
||||
const loaderData = useLoaderData<typeof loader>();
|
||||
const { document, reviewPoints, statistics, reviewInfo } = loaderData;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// 处理loader错误
|
||||
useEffect(() => {
|
||||
if (Object.keys(loaderData).find(key => key === 'result') && !loaderData.result) {
|
||||
messageService.show({
|
||||
title: '错误',
|
||||
message: loaderData.message,
|
||||
type: 'error',
|
||||
confirmText: '确定',
|
||||
onConfirm: () => {
|
||||
navigate(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [loaderData, navigate]);
|
||||
|
||||
// 处理状态更新
|
||||
const handleReviewPointStatusChange = async (
|
||||
reviewPointResultId: string,
|
||||
editAuditStatusId: string | number,
|
||||
newStatus: string,
|
||||
message: string
|
||||
) => {
|
||||
try {
|
||||
const response = await updateReviewResult(
|
||||
reviewPointResultId,
|
||||
editAuditStatusId,
|
||||
newStatus,
|
||||
message
|
||||
);
|
||||
|
||||
if (response.error) {
|
||||
console.error('更新评查结果失败:', response.error);
|
||||
toastService.error(`更新评查结果失败: ${response.error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新本地状态
|
||||
setReviewData(prevData => {
|
||||
// 更新逻辑...
|
||||
});
|
||||
|
||||
toastService.success('评查点状态已更新');
|
||||
} catch (error) {
|
||||
console.error('更新评查结果出错:', error);
|
||||
toastService.error('更新评查结果失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 数据类型定义规范
|
||||
|
||||
#### 1. 数据库实体类型
|
||||
|
||||
```typescript
|
||||
// 数据库表对应的接口
|
||||
interface Document {
|
||||
id: number;
|
||||
user_id: number | null;
|
||||
type_id: number;
|
||||
name: string;
|
||||
document_number: string;
|
||||
path: string;
|
||||
storage_type: string;
|
||||
file_size: number;
|
||||
upload_time: string;
|
||||
is_test_document: boolean;
|
||||
evaluation_level: string;
|
||||
status: 'pass' | 'warning' | 'waiting' | 'processing' | 'fail';
|
||||
file_status: 'Waiting' | 'Cutting' | 'Extractioning' | 'Evaluationing' | 'Processed';
|
||||
audit_status: number; // -1: 不通过, 0: 待审核, 1: 通过, 2: 警告, 3: 审核中
|
||||
ocr_result?: Record<string, unknown>;
|
||||
extracted_results?: unknown;
|
||||
summary?: unknown;
|
||||
remark?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 前端UI类型
|
||||
|
||||
```typescript
|
||||
// 前端组件使用的接口
|
||||
interface ReviewFileUI {
|
||||
id: string;
|
||||
status: string;
|
||||
path: string;
|
||||
fileName: string;
|
||||
fileCode: string;
|
||||
fileType: string;
|
||||
fileTypeId: number;
|
||||
fileSize: number;
|
||||
uploadTime: string;
|
||||
reviewStatus: string;
|
||||
reviewStatusCode: number;
|
||||
issueCount: number;
|
||||
score?: number;
|
||||
auditStatus: number | null;
|
||||
issues: Array<{
|
||||
severity: 'info' | 'warning' | 'error' | 'critical';
|
||||
message: string;
|
||||
}>;
|
||||
createdBy: string;
|
||||
passCount: number;
|
||||
warningCount: number;
|
||||
failCount: number;
|
||||
manualCount: number;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. API参数类型
|
||||
|
||||
```typescript
|
||||
// 搜索参数类型
|
||||
interface DocumentSearchParams {
|
||||
keyword?: string;
|
||||
status?: string;
|
||||
fileType?: string;
|
||||
dateRange?: [string, string];
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
sortBy?: string;
|
||||
sortOrder?: 'asc' | 'desc';
|
||||
}
|
||||
```
|
||||
|
||||
### PostgREST客户端功能
|
||||
|
||||
#### 1. 查询参数转换
|
||||
|
||||
```typescript
|
||||
// 转换通用参数为PostgREST格式
|
||||
export function transformParams(params: PostgrestParams): QueryParams {
|
||||
const result: QueryParams = {};
|
||||
|
||||
// 处理select参数
|
||||
if (params.select) {
|
||||
result.select = params.select;
|
||||
}
|
||||
|
||||
// 处理过滤条件
|
||||
if (params.filter) {
|
||||
Object.entries(params.filter).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
result[key] = value as string | number | boolean;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理排序
|
||||
if (params.order) {
|
||||
result.order = params.order;
|
||||
}
|
||||
|
||||
// 处理分页
|
||||
if (params.limit !== undefined) {
|
||||
result.limit = params.limit;
|
||||
}
|
||||
|
||||
if (params.offset !== undefined) {
|
||||
result.offset = params.offset;
|
||||
}
|
||||
|
||||
// 处理OR条件
|
||||
if (params.or) {
|
||||
if (typeof params.or === 'string') {
|
||||
result.or = params.or;
|
||||
} else if (Array.isArray(params.or)) {
|
||||
const orConditions = params.or.map(condition => {
|
||||
const [field, operator] = Object.entries(condition)[0];
|
||||
return `${field}.${operator}`;
|
||||
});
|
||||
result.or = `(${orConditions.join(',')})`;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 开发环境日志
|
||||
|
||||
```typescript
|
||||
function logPostgrestQuery(endpoint: string, params?: QueryParams, method: string = 'GET'): void {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const baseUrl = 'http://nas.7bm.co:3000';
|
||||
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
|
||||
|
||||
console.log('\n📦 PostgREST 查询日志 ======================start=============');
|
||||
console.log(`📦 HTTP 方法: ${method}`);
|
||||
console.log(`📦 API 端点: ${decodeUrlForDisplay(`${baseUrl}/${normalizedEndpoint}`)}`);
|
||||
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
console.log('📦 查询参数:');
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
console.log(` - ${key}: ${JSON.stringify(value)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('PostgREST 查询日志=============================end============\n');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 数据预处理
|
||||
|
||||
```typescript
|
||||
function preprocessData(data: Record<string, unknown>): Record<string, unknown> {
|
||||
const processed: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
// 处理null值
|
||||
if (value === null) {
|
||||
processed[key] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 处理布尔值字符串
|
||||
if (typeof value === 'string' && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
|
||||
processed[key] = value.toLowerCase() === 'true';
|
||||
}
|
||||
// 处理ID字段
|
||||
else if ((key === 'id' || key.endsWith('_id') || key === 'pid') && value !== undefined) {
|
||||
try {
|
||||
const numValue = Number(value);
|
||||
if (!isNaN(numValue)) {
|
||||
processed[key] = numValue;
|
||||
} else {
|
||||
processed[key] = value;
|
||||
}
|
||||
} catch {
|
||||
processed[key] = value;
|
||||
}
|
||||
}
|
||||
// 其他值保持不变
|
||||
else {
|
||||
processed[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
```
|
||||
|
||||
### 开发最佳实践
|
||||
|
||||
#### 1. API命名规范
|
||||
- 获取数据: `get[EntityName]s()` 或 `get[EntityName]()`
|
||||
- 创建数据: `create[EntityName]()`
|
||||
- 更新数据: `update[EntityName]()`
|
||||
- 删除数据: `delete[EntityName]()`
|
||||
|
||||
#### 2. 错误处理策略
|
||||
- 所有API函数必须返回统一的响应格式
|
||||
- 在API层处理数据库错误,前端只处理业务逻辑错误
|
||||
- 使用TypeScript严格类型检查避免运行时错误
|
||||
|
||||
#### 3. 性能优化
|
||||
- 使用`select`参数只获取需要的字段
|
||||
- 合理使用`limit`和`offset`进行分页
|
||||
- 避免N+1查询,使用关联查询获取相关数据
|
||||
|
||||
#### 4. 调试技巧
|
||||
- 开发环境自动打印查询日志
|
||||
- 使用浏览器网络面板检查实际请求
|
||||
- 通过PostgREST文档验证查询语法
|
||||
|
||||
### 数据对接检查清单
|
||||
|
||||
#### 新功能开发前
|
||||
- [ ] 确认数据库表结构和关系
|
||||
- [ ] 定义TypeScript接口类型
|
||||
- [ ] 规划API函数命名和参数
|
||||
- [ ] 设计错误处理策略
|
||||
|
||||
#### API实现阶段
|
||||
- [ ] 导入必要的postgrest方法
|
||||
- [ ] 实现extractApiData数据提取函数
|
||||
- [ ] 定义所有相关的类型接口
|
||||
- [ ] 编写查询参数构建逻辑
|
||||
- [ ] 实现数据转换和处理逻辑
|
||||
- [ ] 添加完整的错误处理
|
||||
|
||||
#### 前端集成阶段
|
||||
- [ ] 在loader函数中调用API
|
||||
- [ ] 处理loading和error状态
|
||||
- [ ] 实现UI状态更新逻辑
|
||||
- [ ] 添加用户反馈(toast/message)
|
||||
- [ ] 测试各种边界情况
|
||||
|
||||
#### 上线前检查
|
||||
- [ ] API函数测试通过
|
||||
- [ ] 错误处理验证完成
|
||||
- [ ] 性能测试满足要求
|
||||
- [ ] 日志输出正常
|
||||
- [ ] 代码注释完整
|
||||
|
||||
---
|
||||
|
||||
*此数据对接规范基于实际项目经验总结,将根据项目发展持续更新和完善。*
|
||||
Reference in New Issue
Block a user