--- description: Code style and naming conventions --- # 代码风格和约定规则 ## 📝 通用代码风格 ### 文件命名 - **Go文件**: `camelCase.go` (例: `uploadPhotoHandler.go`) - **TypeScript**: `kebab-case.tsx` 或 `PascalCase.tsx` (组件) - **API文件**: `kebab-case.api` (例: `photo.api`) - **配置文件**: `kebab-case.yaml/.json` ### 注释规范 ```go // ✅ Go - 函数注释 // UploadPhoto 上传照片到服务器 // 支持JPEG、PNG、GIF、WebP格式,最大10MB func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest) (*types.UploadPhotoResponse, error) { // 实现逻辑 } ``` ```typescript // ✅ TypeScript - 接口注释 /** * 照片数据接口 * @interface Photo */ interface Photo { /** 照片唯一标识符 */ id: string /** 照片标题 */ title: string /** 文件名 */ filename: string } ``` ## 🎯 命名约定 ### 变量命名 ```go // ✅ Go - 驼峰命名 var photoID string var userList []User var maxFileSize int64 = 10 * 1024 * 1024 // 10MB // ❌ 避免 var photo_id string var PhotoId string ``` ```typescript // ✅ TypeScript - 驼峰命名 const photoList: Photo[] = [] const isLoading = false const handlePhotoUpload = () => {} // ✅ 常量 - 大写下划线 const MAX_FILE_SIZE = 10 * 1024 * 1024 const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ``` ### 函数命名 - **动词开头**: `getPhotoList`, `uploadPhoto`, `deleteCategory` - **布尔值**: `isVisible`, `hasPermission`, `canEdit` - **事件处理**: `handleClick`, `onPhotoSelect`, `onUploadSuccess` ## 🛡️ 错误处理 ### Go 错误处理 ```go // ✅ 标准错误处理 func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest) (*types.UploadPhotoResponse, error) { if !file.IsImageFile(req.File) { return nil, errorx.NewDefaultError("不支持的文件类型") } photoID, err := l.savePhoto(req) if err != nil { logx.Errorf("保存照片失败: %v", err) return nil, errorx.NewDefaultError("照片保存失败") } return &types.UploadPhotoResponse{ Id: photoID, // ... }, nil } ``` ### TypeScript 错误处理 ```typescript // ✅ 异步操作错误处理 try { const response = await api.post('/photos', formData) return response.data } catch (error) { if (axios.isAxiosError(error)) { const message = error.response?.data?.msg || '上传失败' throw new Error(message) } throw error } ``` ## 📦 导入组织 ### Go 导入顺序 ```go import ( // 标准库 "context" "fmt" "net/http" // 第三方库 "github.com/zeromicro/go-zero/rest/httpx" "github.com/zeromicro/go-zero/core/logx" // 本地包 "photography/internal/logic/photo" "photography/internal/svc" "photography/internal/types" ) ``` ### TypeScript 导入顺序 ```typescript // React 相关 import React, { useState, useEffect } from 'react' import { useRouter } from 'next/router' // 第三方库 import axios from 'axios' import { useQuery } from '@tanstack/react-query' // UI 组件 import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' // 本地模块 import { api } from '@/lib/api' import { Photo } from '@/types/api' import { useAuthStore } from '@/stores/authStore' ``` ## 🎨 CSS/样式约定 ### Tailwind CSS 类名顺序 ```tsx // ✅ 推荐顺序:布局 → 尺寸 → 间距 → 颜色 → 其他
{photo.title}
``` ### 响应式设计 ```tsx // ✅ 移动优先响应式
{photos.map(photo => ( ))}
``` ## 🔧 类型定义 ### TypeScript 接口规范 ```typescript // ✅ 明确的接口定义 interface PhotoCardProps { photo: Photo onEdit?: (id: string) => void onDelete?: (id: string) => void className?: string } // ✅ API 响应类型 interface ApiResponse { code: number msg: string data: T } type PhotoListResponse = ApiResponse<{ photos: Photo[] total: number page: number limit: number }> ``` ### Go 结构体规范 ```go // ✅ 结构体标签完整 type Photo struct { ID string `json:"id" db:"id"` Title string `json:"title" db:"title"` Description string `json:"description" db:"description"` Filename string `json:"filename" db:"filename"` Thumbnail string `json:"thumbnail" db:"thumbnail"` CategoryID string `json:"category_id" db:"category_id"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } ``` ## 📊 性能约定 ### 避免性能陷阱 ```typescript // ✅ 使用 useMemo 避免重复计算 const expensiveValue = useMemo(() => { return photos.filter(photo => photo.category_id === selectedCategory) }, [photos, selectedCategory]) // ✅ 使用 useCallback 避免重复渲染 const handlePhotoSelect = useCallback((id: string) => { setSelectedPhoto(photos.find(p => p.id === id)) }, [photos]) ``` ## 🔒 安全约定 ### 输入验证 ```go // ✅ 后端输入验证 if req.Title == "" { return nil, errorx.NewDefaultError("照片标题不能为空") } if len(req.Title) > 100 { return nil, errorx.NewDefaultError("照片标题不能超过100个字符") } ``` ```typescript // ✅ 前端输入验证 const validatePhoto = (data: PhotoFormData): string[] => { const errors: string[] = [] if (!data.title.trim()) { errors.push('标题不能为空') } if (data.title.length > 100) { errors.push('标题不能超过100个字符') } return errors } ``` 遵循这些约定可以保持代码的一致性和可维护性。