Преглед изворни кода

📝 docs(architecture): add RPC client usage standards

- add client creation and configuration guidelines
- add type extraction specifications using InferRequestType and InferResponseType
- document API call standards for GET, POST, PUT and DELETE requests
- add error handling specifications including unified error handling and status code checking
- document query parameter standards for pagination and filtering
- add performance optimization guidelines for query caching and debounced search
yourname пре 4 месеци
родитељ
комит
22183814ad
1 измењених фајлова са 209 додато и 0 уклоњено
  1. 209 0
      docs/architecture/admin-dashboard-standards.md

+ 209 - 0
docs/architecture/admin-dashboard-standards.md

@@ -476,6 +476,215 @@ test('should create new entity', async ({ page }) => {
 });
 ```
 
+## RPC Client 使用规范
+
+### 客户端创建和配置
+
+#### 客户端导入
+```typescript
+// 从统一的API模块导入客户端
+import { userClient, roleClient, fileClient } from '@/client/api';
+import type { InferRequestType, InferResponseType } from 'hono/client';
+```
+
+#### 类型提取规范
+```typescript
+// 使用InferResponseType提取响应类型
+type UserResponse = InferResponseType<typeof userClient.$get, 200>['data'][0];
+type UserListResponse = InferResponseType<typeof userClient.$get, 200>;
+
+// 使用InferRequestType提取请求类型
+type CreateUserRequest = InferRequestType<typeof userClient.$post>['json'];
+type UpdateUserRequest = InferRequestType<typeof userClient[':id']['$put']>['json'];
+```
+
+### API 调用规范
+
+#### GET 请求
+```typescript
+// 基础查询
+const { data, isLoading, refetch } = useQuery({
+  queryKey: ['users', searchParams],
+  queryFn: async () => {
+    const res = await userClient.$get({
+      query: {
+        page: searchParams.page,
+        pageSize: searchParams.limit,
+        keyword: searchParams.keyword,
+        filters: hasActiveFilters ? JSON.stringify(filters) : undefined
+      }
+    });
+    if (res.status !== 200) throw new Error('获取数据失败');
+    return await res.json();
+  }
+});
+
+// 带路径参数的查询
+const { data } = useQuery({
+  queryKey: ['user', userId],
+  queryFn: async () => {
+    const res = await userClient[':id'].$get({
+      param: { id: userId }
+    });
+    if (res.status !== 200) throw new Error('获取用户详情失败');
+    return await res.json();
+  }
+});
+```
+
+#### POST 请求
+```typescript
+// 创建资源
+const handleCreate = async (data: CreateUserRequest) => {
+  try {
+    const res = await userClient.$post({
+      json: data
+    });
+    if (res.status !== 201) throw new Error('创建失败');
+    toast.success('创建成功');
+    refetch(); // 重新获取数据
+  } catch {
+    toast.error('创建失败,请重试');
+  }
+};
+```
+
+#### PUT 请求
+```typescript
+// 更新资源
+const handleUpdate = async (id: number, data: UpdateUserRequest) => {
+  try {
+    const res = await userClient[':id']['$put']({
+      param: { id },
+      json: data
+    });
+    if (res.status !== 200) throw new Error('更新失败');
+    toast.success('更新成功');
+    refetch();
+  } catch {
+    toast.error('更新失败,请重试');
+  }
+};
+```
+
+#### DELETE 请求
+```typescript
+// 删除资源
+const handleDelete = async (id: number) => {
+  try {
+    const res = await userClient[':id']['$delete']({
+      param: { id }
+    });
+    if (res.status !== 204) throw new Error('删除失败');
+    toast.success('删除成功');
+    refetch();
+  } catch {
+    toast.error('删除失败,请重试');
+  }
+};
+```
+
+### 错误处理规范
+
+#### 统一错误处理模式
+```typescript
+// 统一操作处理函数
+const handleOperation = async (operation: () => Promise<any>) => {
+  try {
+    await operation();
+    toast.success('操作成功');
+  } catch (error) {
+    console.error('操作失败:', error);
+    toast.error('操作失败,请重试');
+  }
+};
+
+// 使用示例
+const handleCreateUser = async (data: CreateUserRequest) => {
+  await handleOperation(async () => {
+    const res = await userClient.$post({ json: data });
+    if (res.status !== 201) throw new Error('创建失败');
+  });
+};
+```
+
+#### 状态码检查
+```typescript
+// 标准状态码检查模式
+const checkStatus = (res: Response, expectedStatus: number) => {
+  if (res.status !== expectedStatus) {
+    throw new Error(`操作失败,状态码: ${res.status}`);
+  }
+};
+
+// 使用示例
+const res = await userClient.$post({ json: data });
+checkStatus(res, 201); // 期望201 Created
+```
+
+### 查询参数规范
+
+#### 分页参数
+```typescript
+const searchParams = {
+  page: 1,        // 当前页码
+  pageSize: 10,   // 每页数量
+  keyword: ''     // 搜索关键词
+};
+```
+
+#### 筛选参数
+```typescript
+const filters = {
+  status: undefined as string | undefined,
+  category: [] as number[],
+  dateRange: undefined as { start?: string; end?: string } | undefined
+};
+
+// 将筛选条件转换为查询参数
+const filterParams = Object.keys(filters).reduce((acc, key) => {
+  const value = filters[key as keyof typeof filters];
+  if (value !== undefined && value !== null &&
+      (!Array.isArray(value) || value.length > 0)) {
+    acc[key] = value;
+  }
+  return acc;
+}, {} as Record<string, unknown>);
+```
+
+### 性能优化
+
+#### 查询缓存配置
+```typescript
+const { data } = useQuery({
+  queryKey: ['users', searchParams, filters],
+  queryFn: fetchUsers,
+  staleTime: 5 * 60 * 1000,  // 5分钟
+  cacheTime: 10 * 60 * 1000, // 10分钟
+  refetchOnWindowFocus: false // 避免窗口聚焦时重复请求
+});
+```
+
+#### 防抖搜索
+```typescript
+// 防抖函数
+const debounce = (func: Function, delay: number) => {
+  let timeoutId: NodeJS.Timeout;
+  return (...args: any[]) => {
+    clearTimeout(timeoutId);
+    timeoutId = setTimeout(() => func(...args), delay);
+  };
+};
+
+// 防抖搜索
+const debouncedSearch = useCallback(
+  debounce((keyword: string) => {
+    setSearchParams(prev => ({ ...prev, keyword, page: 1 }));
+  }, 300),
+  []
+);
+```
+
 ## 代码质量规范
 
 ### 代码风格