diff --git a/.cursor/rules/admin/react-vite.mdc b/.cursor/rules/admin/react-vite.mdc
new file mode 100644
index 0000000..6aada16
--- /dev/null
+++ b/.cursor/rules/admin/react-vite.mdc
@@ -0,0 +1,624 @@
+# 管理后台 React + Vite 开发规则
+
+## 🏗️ 项目架构
+
+### 目录结构
+```
+admin/
+├── src/
+│ ├── components/ # React 组件
+│ │ ├── ui/ # shadcn/ui 基础组件
+│ │ ├── DashboardLayout.tsx # 布局组件
+│ │ ├── ProtectedRoute.tsx # 路由守卫
+│ │ └── ErrorBoundary.tsx # 错误边界
+│ ├── pages/ # 页面组件
+│ │ ├── Dashboard.tsx # 仪表盘
+│ │ ├── Photos.tsx # 照片管理
+│ │ ├── Categories.tsx # 分类管理
+│ │ ├── Users.tsx # 用户管理
+│ │ └── LoginPage.tsx # 登录页
+│ ├── services/ # API 服务
+│ │ ├── api.ts # API 基础配置
+│ │ ├── authService.ts # 认证服务
+│ │ ├── photoService.ts # 照片服务
+│ │ └── categoryService.ts # 分类服务
+│ ├── stores/ # 状态管理
+│ │ └── authStore.ts # 认证状态
+│ ├── types/ # TypeScript 类型
+│ │ └── index.ts # 类型定义
+│ ├── utils/ # 工具函数
+│ ├── lib/ # 库配置
+│ │ └── utils.ts # shadcn utils
+│ ├── App.tsx # 应用根组件
+│ └── main.tsx # 应用入口
+├── public/ # 静态资源
+├── index.html # HTML 模板
+├── vite.config.ts # Vite 配置
+└── package.json # 依赖配置
+```
+
+## 🎯 开发规范
+
+### 应用入口配置
+```tsx
+// main.tsx
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { BrowserRouter } from 'react-router-dom'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
+import App from './App'
+import './index.css'
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 5 * 60 * 1000, // 5分钟
+ retry: 2,
+ },
+ },
+})
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+
+
+
+)
+```
+
+### 应用根组件
+```tsx
+// App.tsx
+import { Routes, Route, Navigate } from 'react-router-dom'
+import { Toaster } from '@/components/ui/toaster'
+import { ErrorBoundary } from '@/components/ErrorBoundary'
+import { ProtectedRoute } from '@/components/ProtectedRoute'
+import { DashboardLayout } from '@/components/DashboardLayout'
+import { LoginPage } from '@/pages/LoginPage'
+import { Dashboard } from '@/pages/Dashboard'
+import { Photos } from '@/pages/Photos'
+import { PhotoUpload } from '@/pages/PhotoUpload'
+import { Categories } from '@/pages/Categories'
+import { Users } from '@/pages/Users'
+import { Settings } from '@/pages/Settings'
+
+function App() {
+ return (
+
+
+ } />
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+ }
+ />
+
+
+
+ )
+}
+
+export default App
+```
+
+## 🧩 核心组件
+
+### 布局组件
+```tsx
+// components/DashboardLayout.tsx
+import { useState } from 'react'
+import { Link, useLocation } from 'react-router-dom'
+import {
+ LayoutDashboard,
+ Image,
+ FolderOpen,
+ Users,
+ Settings,
+ LogOut,
+ Menu
+} from 'lucide-react'
+import { Button } from '@/components/ui/button'
+import { useAuthStore } from '@/stores/authStore'
+
+const navigation = [
+ { name: '仪表盘', href: '/dashboard', icon: LayoutDashboard },
+ { name: '照片管理', href: '/photos', icon: Image },
+ { name: '分类管理', href: '/categories', icon: FolderOpen },
+ { name: '用户管理', href: '/users', icon: Users },
+ { name: '系统设置', href: '/settings', icon: Settings },
+]
+
+interface DashboardLayoutProps {
+ children: React.ReactNode
+}
+
+export function DashboardLayout({ children }: DashboardLayoutProps) {
+ const [sidebarOpen, setSidebarOpen] = useState(false)
+ const location = useLocation()
+ const { user, logout } = useAuthStore()
+
+ return (
+
+ {/* 侧边栏 */}
+
+
+
+ 摄影管理
+
+
+
+
+
+
+
+
+
+
+
+ {/* 主内容区 */}
+
+ {/* 顶部导航 */}
+
+
+ {/* 页面内容 */}
+
+ {children}
+
+
+
+ )
+}
+```
+
+### 路由守卫
+```tsx
+// components/ProtectedRoute.tsx
+import { Navigate } from 'react-router-dom'
+import { useAuthStore } from '@/stores/authStore'
+import { Loading } from '@/components/Loading'
+
+interface ProtectedRouteProps {
+ children: React.ReactNode
+}
+
+export function ProtectedRoute({ children }: ProtectedRouteProps) {
+ const { token, isLoading } = useAuthStore()
+
+ if (isLoading) {
+ return
+ }
+
+ if (!token) {
+ return
+ }
+
+ return <>{children}>
+}
+```
+
+## 🎛️ 状态管理 (Zustand)
+
+### 认证状态管理
+```typescript
+// stores/authStore.ts
+import { create } from 'zustand'
+import { persist } from 'zustand/middleware'
+import { authService } from '@/services/authService'
+
+interface User {
+ id: string
+ username: string
+ email: string
+ role: string
+}
+
+interface AuthState {
+ user: User | null
+ token: string | null
+ isLoading: boolean
+
+ // Actions
+ login: (credentials: LoginCredentials) => Promise
+ logout: () => void
+ checkAuth: () => Promise
+}
+
+export const useAuthStore = create()(
+ persist(
+ (set, get) => ({
+ user: null,
+ token: null,
+ isLoading: false,
+
+ login: async (credentials) => {
+ set({ isLoading: true })
+ try {
+ const response = await authService.login(credentials)
+ set({
+ user: response.user,
+ token: response.token,
+ isLoading: false,
+ })
+ } catch (error) {
+ set({ isLoading: false })
+ throw error
+ }
+ },
+
+ logout: () => {
+ set({ user: null, token: null })
+ // 清除持久化数据
+ localStorage.removeItem('auth-storage')
+ },
+
+ checkAuth: async () => {
+ const { token } = get()
+ if (!token) return
+
+ try {
+ const user = await authService.getCurrentUser()
+ set({ user })
+ } catch (error) {
+ // Token 无效,清除状态
+ get().logout()
+ }
+ },
+ }),
+ {
+ name: 'auth-storage',
+ partialize: (state) => ({
+ token: state.token,
+ user: state.user
+ }),
+ }
+ )
+)
+```
+
+## 🌐 API 服务层
+
+### API 基础配置
+```typescript
+// services/api.ts
+import axios, { AxiosError } from 'axios'
+import { useAuthStore } from '@/stores/authStore'
+
+export const api = axios.create({
+ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8888/api/v1',
+ timeout: 10000,
+})
+
+// 请求拦截器
+api.interceptors.request.use((config) => {
+ const token = useAuthStore.getState().token
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ }
+ return config
+})
+
+// 响应拦截器
+api.interceptors.response.use(
+ (response) => response,
+ (error: AxiosError) => {
+ if (error.response?.status === 401) {
+ useAuthStore.getState().logout()
+ window.location.href = '/login'
+ }
+ return Promise.reject(error)
+ }
+)
+
+// API 响应类型
+export interface ApiResponse {
+ code: number
+ msg: string
+ data: T
+}
+```
+
+### 照片服务
+```typescript
+// services/photoService.ts
+import { api, ApiResponse } from './api'
+
+export interface Photo {
+ id: string
+ title: string
+ description?: string
+ filename: string
+ thumbnail: string
+ category_id?: string
+ user_id: string
+ created_at: string
+ updated_at: string
+}
+
+export interface PhotoListResponse {
+ photos: Photo[]
+ total: number
+ page: number
+ limit: number
+}
+
+export const photoService = {
+ // 获取照片列表
+ getPhotos: async (params: {
+ page?: number
+ limit?: number
+ category_id?: string
+ keyword?: string
+ }): Promise => {
+ const { data } = await api.get>('/photos', {
+ params,
+ })
+ return data.data
+ },
+
+ // 上传照片
+ uploadPhoto: async (formData: FormData): Promise => {
+ const { data } = await api.post>('/photos', formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ })
+ return data.data
+ },
+
+ // 更新照片
+ updatePhoto: async (id: string, updates: Partial): Promise => {
+ await api.put(`/photos/${id}`, updates)
+ },
+
+ // 删除照片
+ deletePhoto: async (id: string): Promise => {
+ await api.delete(`/photos/${id}`)
+ },
+
+ // 获取照片详情
+ getPhoto: async (id: string): Promise => {
+ const { data } = await api.get>(`/photos/${id}`)
+ return data.data
+ },
+}
+```
+
+## 📱 页面组件
+
+### 照片管理页面
+```tsx
+// pages/Photos.tsx
+import { useState } from 'react'
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
+import { Plus, Edit, Trash2 } from 'lucide-react'
+import { Button } from '@/components/ui/button'
+import { Card, CardContent } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
+import { useToast } from '@/hooks/use-toast'
+import { photoService } from '@/services/photoService'
+
+export function Photos() {
+ const [page, setPage] = useState(1)
+ const { toast } = useToast()
+ const queryClient = useQueryClient()
+
+ // 获取照片列表
+ const { data, isLoading, error } = useQuery({
+ queryKey: ['photos', page],
+ queryFn: () => photoService.getPhotos({ page, limit: 12 }),
+ })
+
+ // 删除照片
+ const deleteMutation = useMutation({
+ mutationFn: photoService.deletePhoto,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['photos'] })
+ toast({
+ title: '成功',
+ description: '照片已删除',
+ })
+ },
+ onError: (error) => {
+ toast({
+ title: '错误',
+ description: '删除失败',
+ variant: 'destructive',
+ })
+ },
+ })
+
+ const handleDelete = (id: string) => {
+ if (confirm('确认删除这张照片吗?')) {
+ deleteMutation.mutate(id)
+ }
+ }
+
+ if (isLoading) return 加载中...
+ if (error) return 加载失败
+
+ return (
+
+
+
+
+ {data?.photos.map((photo) => (
+
+
+
+

+
+
+
+
+
+
+
+
+
{photo.title}
+ {photo.description && (
+
+ {photo.description}
+
+ )}
+
+
+ {photo.category_id ? '已分类' : '未分类'}
+
+
+ {new Date(photo.created_at).toLocaleDateString()}
+
+
+
+
+
+ ))}
+
+
+ {/* 分页 */}
+ {data && data.total > 12 && (
+
+
+
+
+ )}
+
+ )
+}
+```
+
+## 🔧 开发工具配置
+
+### Vite 配置
+```typescript
+// vite.config.ts
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import path from 'path'
+
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:8888',
+ changeOrigin: true,
+ },
+ },
+ },
+ build: {
+ outDir: 'dist',
+ sourcemap: true,
+ },
+})
+```
+
+### 环境变量
+```bash
+# .env.local
+VITE_API_URL=http://localhost:8888/api/v1
+VITE_APP_TITLE=摄影作品管理后台
+```
+
+参考 [TASK_PROGRESS.md](mdc:TASK_PROGRESS.md) 了解管理后台开发进度。
+description:
+globs:
+alwaysApply: false
+---
diff --git a/.cursor/rules/backend/api-development.mdc b/.cursor/rules/backend/api-development.mdc
new file mode 100644
index 0000000..fab3808
--- /dev/null
+++ b/.cursor/rules/backend/api-development.mdc
@@ -0,0 +1,750 @@
+---
+globs: backend/api/**/*.api
+alwaysApply: false
+---
+# 后端 API 开发规则
+
+## 🔄 API 开发工作流
+
+### 1. 需求分析
+- 明确API功能和参数
+- 确定请求/响应格式
+- 考虑错误处理场景
+
+### 2. API 定义 (.api文件)
+在 `backend/api/desc/` 目录下定义:
+
+```api
+// photo.api
+syntax = "v1"
+
+info(
+ title: "Photography Photo API"
+ desc: "照片管理相关API"
+ author: "iriver"
+ email: "iriver@example.com"
+ version: "v1"
+)
+
+import "common.api"
+
+type (
+ UploadPhotoRequest {
+ Title string `form:"title"`
+ Description string `form:"description,optional"`
+ CategoryId string `form:"category_id,optional"`
+ File string `form:"file"`
+ }
+
+ UploadPhotoResponse {
+ Id string `json:"id"`
+ Title string `json:"title"`
+ Filename string `json:"filename"`
+ Thumbnail string `json:"thumbnail"`
+ CreatedAt string `json:"created_at"`
+ }
+
+ UpdatePhotoRequest {
+ Id string `path:"id"`
+ Title string `json:"title,optional"`
+ Description string `json:"description,optional"`
+ CategoryId string `json:"category_id,optional"`
+ }
+)
+
+@server(
+ group: photo
+ prefix: /api/v1
+)
+service photography-api {
+ @doc "上传照片"
+ @handler uploadPhoto
+ post /photos (UploadPhotoRequest) returns (UploadPhotoResponse)
+
+ @doc "更新照片信息"
+ @handler updatePhoto
+ put /photos/:id (UpdatePhotoRequest) returns (BaseResponse)
+
+ @doc "删除照片"
+ @handler deletePhoto
+ delete /photos/:id (IdPathRequest) returns (BaseResponse)
+
+ @doc "获取照片详情"
+ @handler getPhoto
+ get /photos/:id (IdPathRequest) returns (PhotoResponse)
+
+ @doc "获取照片列表"
+ @handler getPhotoList
+ get /photos (PhotoListRequest) returns (PhotoListResponse)
+}
+```
+
+### 3. 代码生成
+```bash
+cd backend
+make api
+```
+
+### 4. Handler 实现模式
+```go
+func (h *UploadPhotoHandler) UploadPhoto(w http.ResponseWriter, r *http.Request) {
+ var req types.UploadPhotoRequest
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := photo.NewUploadPhotoLogic(r.Context(), h.svcCtx)
+ resp, err := l.UploadPhoto(&req, r)
+ response.Response(w, resp, err)
+}
+```
+
+### 5. Logic 实现模式
+```go
+func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest, r *http.Request) (resp *types.UploadPhotoResponse, err error) {
+ // 1. 参数验证
+ if err = l.validateRequest(req); err != nil {
+ return nil, err
+ }
+
+ // 2. 获取上传文件
+ fileHeader, err := l.getUploadFile(r)
+ if err != nil {
+ return nil, err
+ }
+
+ // 3. 文件处理
+ savedPath, thumbnail, err := l.processFile(fileHeader)
+ if err != nil {
+ return nil, err
+ }
+
+ // 4. 数据持久化
+ photo, err := l.savePhoto(req, savedPath, thumbnail)
+ if err != nil {
+ return nil, err
+ }
+
+ // 5. 构造响应
+ return l.buildResponse(photo), nil
+}
+```
+
+## 📋 API 接口规范
+
+### 请求规范
+```go
+// 路径参数
+type IdPathRequest {
+ Id string `path:"id"`
+}
+
+// 查询参数
+type PhotoListRequest {
+ Page int `form:"page,default=1"`
+ Limit int `form:"limit,default=10"`
+ CategoryId string `form:"category_id,optional"`
+ Keyword string `form:"keyword,optional"`
+}
+
+// JSON请求体
+type UpdatePhotoRequest {
+ Title string `json:"title,optional"`
+ Description string `json:"description,optional"`
+ CategoryId string `json:"category_id,optional"`
+}
+
+// 文件上传 (multipart/form-data)
+type UploadPhotoRequest {
+ Title string `form:"title"`
+ File string `form:"file"`
+}
+```
+
+### 响应规范
+```go
+// 基础响应
+type BaseResponse {
+ Code int `json:"code"`
+ Msg string `json:"msg"`
+ Data any `json:"data"`
+}
+
+// 单个资源响应
+type PhotoResponse {
+ BaseResponse
+ Data Photo `json:"data"`
+}
+
+// 列表响应
+type PhotoListResponse {
+ BaseResponse
+ Data PhotoListData `json:"data"`
+}
+
+type PhotoListData {
+ List []Photo `json:"list"`
+ Total int `json:"total"`
+ Page int `json:"page"`
+ Limit int `json:"limit"`
+}
+```
+
+## 🧪 API 测试策略
+
+### 1. 手动测试 (curl)
+```bash
+# 健康检查
+curl -X GET "http://localhost:8888/api/v1/health"
+
+# 用户登录
+curl -X POST "http://localhost:8888/api/v1/auth/login" \
+ -H "Content-Type: application/json" \
+ -d '{"username":"admin","password":"123456"}'
+
+# 上传照片
+curl -X POST "http://localhost:8888/api/v1/photos" \
+ -H "Authorization: Bearer $TOKEN" \
+ -F "title=测试照片" \
+ -F "description=这是一张测试照片" \
+ -F "file=@test.jpg"
+
+# 获取照片列表
+curl -X GET "http://localhost:8888/api/v1/photos?page=1&limit=10" \
+ -H "Authorization: Bearer $TOKEN"
+
+# 更新照片
+curl -X PUT "http://localhost:8888/api/v1/photos/123" \
+ -H "Authorization: Bearer $TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{"title":"更新后的标题"}'
+
+# 删除照片
+curl -X DELETE "http://localhost:8888/api/v1/photos/123" \
+ -H "Authorization: Bearer $TOKEN"
+```
+
+### 2. HTTP文件测试
+创建 `test_api.http` 文件:
+```http
+### 登录获取token
+POST http://localhost:8888/api/v1/auth/login
+Content-Type: application/json
+
+{
+ "username": "admin",
+ "password": "123456"
+}
+
+### 设置变量
+@token = {{login.response.body.data.token}}
+
+### 上传照片
+POST http://localhost:8888/api/v1/photos
+Authorization: Bearer {{token}}
+Content-Type: multipart/form-data; boundary=boundary
+
+--boundary
+Content-Disposition: form-data; name="title"
+
+测试照片标题
+--boundary
+Content-Disposition: form-data; name="file"; filename="test.jpg"
+Content-Type: image/jpeg
+
+< ./test.jpg
+--boundary--
+
+### 获取照片列表
+GET http://localhost:8888/api/v1/photos?page=1&limit=5
+Authorization: Bearer {{token}}
+```
+
+## 🛡️ 安全和验证
+
+### 认证验证
+```go
+// 获取当前用户ID
+func (l *UploadPhotoLogic) getCurrentUserID() (string, error) {
+ userID := l.ctx.Value("userID")
+ if userID == nil {
+ return "", errors.New("用户未认证")
+ }
+ return userID.(string), nil
+}
+```
+
+### 参数验证
+```go
+func (l *UploadPhotoLogic) validateRequest(req *types.UploadPhotoRequest) error {
+ if req.Title == "" {
+ return errorx.NewDefaultError("照片标题不能为空")
+ }
+
+ if len(req.Title) > 100 {
+ return errorx.NewDefaultError("照片标题不能超过100个字符")
+ }
+
+ return nil
+}
+```
+
+### 文件验证
+```go
+func (l *UploadPhotoLogic) validateFile(fileHeader *multipart.FileHeader) error {
+ // 文件大小验证
+ if fileHeader.Size > file.MaxFileSize {
+ return errorx.NewDefaultError("文件大小不能超过10MB")
+ }
+
+ // 文件类型验证
+ if !file.IsImageFile(fileHeader.Filename) {
+ return errorx.NewDefaultError("只支持图片文件")
+ }
+
+ return nil
+}
+```
+
+## 📊 错误处理
+
+### 标准错误响应
+```go
+// 参数错误
+return nil, errorx.NewCodeError(400, "参数错误")
+
+// 认证错误
+return nil, errorx.NewCodeError(401, "未认证")
+
+// 权限错误
+return nil, errorx.NewCodeError(403, "权限不足")
+
+// 资源不存在
+return nil, errorx.NewCodeError(404, "照片不存在")
+
+// 服务器错误
+return nil, errorx.NewCodeError(500, "服务器内部错误")
+```
+
+### 业务错误处理
+```go
+func (l *UploadPhotoLogic) handleBusinessError(err error) error {
+ switch {
+ case errors.Is(err, sql.ErrNoRows):
+ return errorx.NewCodeError(404, "资源不存在")
+ case strings.Contains(err.Error(), "duplicate"):
+ return errorx.NewCodeError(409, "资源已存在")
+ default:
+ logx.Errorf("业务处理失败: %v", err)
+ return errorx.NewCodeError(500, "处理失败")
+ }
+}
+```
+
+## 🔧 开发工具
+
+### Makefile 命令
+```makefile
+# 生成API代码
+api:
+ goctl api go -api api/desc/photography.api -dir ./
+
+# 启动服务
+run:
+ go run cmd/api/main.go
+
+# 构建
+build:
+ go build -o bin/photography-api cmd/api/main.go
+
+# 测试
+test:
+ go test ./...
+```
+
+### 调试技巧
+```go
+// 添加调试日志
+logx.Infof("处理上传照片请求: %+v", req)
+logx.Errorf("文件保存失败: %v", err)
+
+// 检查请求context
+userID := l.ctx.Value("userID")
+logx.Infof("当前用户: %v", userID)
+```
+
+当前API开发状态参考 [TASK_PROGRESS.md](mdc:TASK_PROGRESS.md)。
+# 后端 API 开发规则
+
+## 🔄 API 开发工作流
+
+### 1. 需求分析
+- 明确API功能和参数
+- 确定请求/响应格式
+- 考虑错误处理场景
+
+### 2. API 定义 (.api文件)
+在 `backend/api/desc/` 目录下定义:
+
+```api
+// photo.api
+syntax = "v1"
+
+info(
+ title: "Photography Photo API"
+ desc: "照片管理相关API"
+ author: "iriver"
+ email: "iriver@example.com"
+ version: "v1"
+)
+
+import "common.api"
+
+type (
+ UploadPhotoRequest {
+ Title string `form:"title"`
+ Description string `form:"description,optional"`
+ CategoryId string `form:"category_id,optional"`
+ File string `form:"file"`
+ }
+
+ UploadPhotoResponse {
+ Id string `json:"id"`
+ Title string `json:"title"`
+ Filename string `json:"filename"`
+ Thumbnail string `json:"thumbnail"`
+ CreatedAt string `json:"created_at"`
+ }
+
+ UpdatePhotoRequest {
+ Id string `path:"id"`
+ Title string `json:"title,optional"`
+ Description string `json:"description,optional"`
+ CategoryId string `json:"category_id,optional"`
+ }
+)
+
+@server(
+ group: photo
+ prefix: /api/v1
+)
+service photography-api {
+ @doc "上传照片"
+ @handler uploadPhoto
+ post /photos (UploadPhotoRequest) returns (UploadPhotoResponse)
+
+ @doc "更新照片信息"
+ @handler updatePhoto
+ put /photos/:id (UpdatePhotoRequest) returns (BaseResponse)
+
+ @doc "删除照片"
+ @handler deletePhoto
+ delete /photos/:id (IdPathRequest) returns (BaseResponse)
+
+ @doc "获取照片详情"
+ @handler getPhoto
+ get /photos/:id (IdPathRequest) returns (PhotoResponse)
+
+ @doc "获取照片列表"
+ @handler getPhotoList
+ get /photos (PhotoListRequest) returns (PhotoListResponse)
+}
+```
+
+### 3. 代码生成
+```bash
+cd backend
+make api
+```
+
+### 4. Handler 实现模式
+```go
+func (h *UploadPhotoHandler) UploadPhoto(w http.ResponseWriter, r *http.Request) {
+ var req types.UploadPhotoRequest
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := photo.NewUploadPhotoLogic(r.Context(), h.svcCtx)
+ resp, err := l.UploadPhoto(&req, r)
+ response.Response(w, resp, err)
+}
+```
+
+### 5. Logic 实现模式
+```go
+func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest, r *http.Request) (resp *types.UploadPhotoResponse, err error) {
+ // 1. 参数验证
+ if err = l.validateRequest(req); err != nil {
+ return nil, err
+ }
+
+ // 2. 获取上传文件
+ fileHeader, err := l.getUploadFile(r)
+ if err != nil {
+ return nil, err
+ }
+
+ // 3. 文件处理
+ savedPath, thumbnail, err := l.processFile(fileHeader)
+ if err != nil {
+ return nil, err
+ }
+
+ // 4. 数据持久化
+ photo, err := l.savePhoto(req, savedPath, thumbnail)
+ if err != nil {
+ return nil, err
+ }
+
+ // 5. 构造响应
+ return l.buildResponse(photo), nil
+}
+```
+
+## 📋 API 接口规范
+
+### 请求规范
+```go
+// 路径参数
+type IdPathRequest {
+ Id string `path:"id"`
+}
+
+// 查询参数
+type PhotoListRequest {
+ Page int `form:"page,default=1"`
+ Limit int `form:"limit,default=10"`
+ CategoryId string `form:"category_id,optional"`
+ Keyword string `form:"keyword,optional"`
+}
+
+// JSON请求体
+type UpdatePhotoRequest {
+ Title string `json:"title,optional"`
+ Description string `json:"description,optional"`
+ CategoryId string `json:"category_id,optional"`
+}
+
+// 文件上传 (multipart/form-data)
+type UploadPhotoRequest {
+ Title string `form:"title"`
+ File string `form:"file"`
+}
+```
+
+### 响应规范
+```go
+// 基础响应
+type BaseResponse {
+ Code int `json:"code"`
+ Msg string `json:"msg"`
+ Data any `json:"data"`
+}
+
+// 单个资源响应
+type PhotoResponse {
+ BaseResponse
+ Data Photo `json:"data"`
+}
+
+// 列表响应
+type PhotoListResponse {
+ BaseResponse
+ Data PhotoListData `json:"data"`
+}
+
+type PhotoListData {
+ List []Photo `json:"list"`
+ Total int `json:"total"`
+ Page int `json:"page"`
+ Limit int `json:"limit"`
+}
+```
+
+## 🧪 API 测试策略
+
+### 1. 手动测试 (curl)
+```bash
+# 健康检查
+curl -X GET "http://localhost:8888/api/v1/health"
+
+# 用户登录
+curl -X POST "http://localhost:8888/api/v1/auth/login" \
+ -H "Content-Type: application/json" \
+ -d '{"username":"admin","password":"123456"}'
+
+# 上传照片
+curl -X POST "http://localhost:8888/api/v1/photos" \
+ -H "Authorization: Bearer $TOKEN" \
+ -F "title=测试照片" \
+ -F "description=这是一张测试照片" \
+ -F "file=@test.jpg"
+
+# 获取照片列表
+curl -X GET "http://localhost:8888/api/v1/photos?page=1&limit=10" \
+ -H "Authorization: Bearer $TOKEN"
+
+# 更新照片
+curl -X PUT "http://localhost:8888/api/v1/photos/123" \
+ -H "Authorization: Bearer $TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{"title":"更新后的标题"}'
+
+# 删除照片
+curl -X DELETE "http://localhost:8888/api/v1/photos/123" \
+ -H "Authorization: Bearer $TOKEN"
+```
+
+### 2. HTTP文件测试
+创建 `test_api.http` 文件:
+```http
+### 登录获取token
+POST http://localhost:8888/api/v1/auth/login
+Content-Type: application/json
+
+{
+ "username": "admin",
+ "password": "123456"
+}
+
+### 设置变量
+@token = {{login.response.body.data.token}}
+
+### 上传照片
+POST http://localhost:8888/api/v1/photos
+Authorization: Bearer {{token}}
+Content-Type: multipart/form-data; boundary=boundary
+
+--boundary
+Content-Disposition: form-data; name="title"
+
+测试照片标题
+--boundary
+Content-Disposition: form-data; name="file"; filename="test.jpg"
+Content-Type: image/jpeg
+
+< ./test.jpg
+--boundary--
+
+### 获取照片列表
+GET http://localhost:8888/api/v1/photos?page=1&limit=5
+Authorization: Bearer {{token}}
+```
+
+## 🛡️ 安全和验证
+
+### 认证验证
+```go
+// 获取当前用户ID
+func (l *UploadPhotoLogic) getCurrentUserID() (string, error) {
+ userID := l.ctx.Value("userID")
+ if userID == nil {
+ return "", errors.New("用户未认证")
+ }
+ return userID.(string), nil
+}
+```
+
+### 参数验证
+```go
+func (l *UploadPhotoLogic) validateRequest(req *types.UploadPhotoRequest) error {
+ if req.Title == "" {
+ return errorx.NewDefaultError("照片标题不能为空")
+ }
+
+ if len(req.Title) > 100 {
+ return errorx.NewDefaultError("照片标题不能超过100个字符")
+ }
+
+ return nil
+}
+```
+
+### 文件验证
+```go
+func (l *UploadPhotoLogic) validateFile(fileHeader *multipart.FileHeader) error {
+ // 文件大小验证
+ if fileHeader.Size > file.MaxFileSize {
+ return errorx.NewDefaultError("文件大小不能超过10MB")
+ }
+
+ // 文件类型验证
+ if !file.IsImageFile(fileHeader.Filename) {
+ return errorx.NewDefaultError("只支持图片文件")
+ }
+
+ return nil
+}
+```
+
+## 📊 错误处理
+
+### 标准错误响应
+```go
+// 参数错误
+return nil, errorx.NewCodeError(400, "参数错误")
+
+// 认证错误
+return nil, errorx.NewCodeError(401, "未认证")
+
+// 权限错误
+return nil, errorx.NewCodeError(403, "权限不足")
+
+// 资源不存在
+return nil, errorx.NewCodeError(404, "照片不存在")
+
+// 服务器错误
+return nil, errorx.NewCodeError(500, "服务器内部错误")
+```
+
+### 业务错误处理
+```go
+func (l *UploadPhotoLogic) handleBusinessError(err error) error {
+ switch {
+ case errors.Is(err, sql.ErrNoRows):
+ return errorx.NewCodeError(404, "资源不存在")
+ case strings.Contains(err.Error(), "duplicate"):
+ return errorx.NewCodeError(409, "资源已存在")
+ default:
+ logx.Errorf("业务处理失败: %v", err)
+ return errorx.NewCodeError(500, "处理失败")
+ }
+}
+```
+
+## 🔧 开发工具
+
+### Makefile 命令
+```makefile
+# 生成API代码
+api:
+ goctl api go -api api/desc/photography.api -dir ./
+
+# 启动服务
+run:
+ go run cmd/api/main.go
+
+# 构建
+build:
+ go build -o bin/photography-api cmd/api/main.go
+
+# 测试
+test:
+ go test ./...
+```
+
+### 调试技巧
+```go
+// 添加调试日志
+logx.Infof("处理上传照片请求: %+v", req)
+logx.Errorf("文件保存失败: %v", err)
+
+// 检查请求context
+userID := l.ctx.Value("userID")
+logx.Infof("当前用户: %v", userID)
+```
+
+当前API开发状态参考 [TASK_PROGRESS.md](mdc:TASK_PROGRESS.md)。
diff --git a/.cursor/rules/backend/go-zero-framework.mdc b/.cursor/rules/backend/go-zero-framework.mdc
new file mode 100644
index 0000000..c6395fe
--- /dev/null
+++ b/.cursor/rules/backend/go-zero-framework.mdc
@@ -0,0 +1,492 @@
+---
+globs: backend/*,backend/**/*.go
+alwaysApply: false
+---
+# Go-Zero 框架开发规则
+
+## 🚀 架构规范
+
+### 核心概念
+- **Handler**: HTTP请求处理层,只做参数验证和调用Logic
+- **Logic**: 业务逻辑层,包含核心业务代码
+- **Model**: 数据访问层,处理数据库操作
+- **Types**: 请求/响应类型定义
+
+### 文件结构
+```
+backend/
+├── cmd/api/main.go # 服务启动入口
+├── etc/photography-api.yaml # 配置文件
+├── api/desc/ # API定义文件
+├── internal/
+│ ├── handler/ # HTTP处理器
+│ ├── logic/ # 业务逻辑
+│ ├── model/ # 数据模型
+│ ├── middleware/ # 中间件
+│ ├── svc/ # 服务上下文
+│ └── types/ # 类型定义
+└── pkg/ # 工具包
+```
+
+## 🎯 开发流程
+
+### 1. API定义
+在 `api/desc/` 目录下定义接口:
+```api
+service photography-api {
+ @handler uploadPhoto
+ post /api/v1/photos (UploadPhotoRequest) returns (UploadPhotoResponse)
+}
+
+type UploadPhotoRequest {
+ Title string `form:"title"`
+ Description string `form:"description,optional"`
+ File string `form:"file"`
+}
+
+type UploadPhotoResponse {
+ Id string `json:"id"`
+ Title string `json:"title"`
+ Filename string `json:"filename"`
+ Thumbnail string `json:"thumbnail"`
+}
+```
+
+### 2. 代码生成
+```bash
+cd backend
+make api # 生成handler和logic骨架
+```
+
+### 3. Handler实现
+```go
+func (h *UploadPhotoHandler) UploadPhoto(w http.ResponseWriter, r *http.Request) {
+ var req types.UploadPhotoRequest
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := logic.NewUploadPhotoLogic(r.Context(), h.svcCtx)
+ resp, err := l.UploadPhoto(&req)
+ response.Response(w, resp, err)
+}
+```
+
+### 4. Logic实现
+```go
+func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest) (*types.UploadPhotoResponse, error) {
+ // 1. 参数验证
+ if req.Title == "" {
+ return nil, errorx.NewDefaultError("照片标题不能为空")
+ }
+
+ // 2. 业务逻辑
+ photoID := uuid.New().String()
+
+ // 3. 数据持久化
+ photo := &model.Photo{
+ ID: photoID,
+ Title: req.Title,
+ // ...
+ }
+
+ err := l.svcCtx.PhotoModel.Insert(l.ctx, photo)
+ if err != nil {
+ return nil, err
+ }
+
+ return &types.UploadPhotoResponse{
+ Id: photoID,
+ Title: req.Title,
+ // ...
+ }, nil
+}
+```
+
+## 🔧 工具包使用
+
+### 文件处理
+使用 [pkg/utils/file/file.go](mdc:backend/pkg/utils/file/file.go):
+```go
+import "photography/pkg/utils/file"
+
+// 验证图片文件
+if !file.IsImageFile(filename) {
+ return errors.New("不支持的文件类型")
+}
+
+// 保存文件并生成缩略图
+savedPath, thumbnail, err := file.SaveImage(fileData, filename)
+```
+
+### JWT认证
+使用 [pkg/utils/jwt/jwt.go](mdc:backend/pkg/utils/jwt/jwt.go):
+```go
+import "photography/pkg/utils/jwt"
+
+// 生成token
+token, err := jwt.GenerateToken(userID, username)
+
+// 验证token
+userID, err := jwt.ParseToken(tokenString)
+```
+
+### 错误处理
+使用 [pkg/errorx/errorx.go](mdc:backend/pkg/errorx/errorx.go):
+```go
+import "photography/pkg/errorx"
+
+// 业务错误
+return nil, errorx.NewDefaultError("用户不存在")
+
+// 自定义错误码
+return nil, errorx.NewCodeError(40001, "参数错误")
+```
+
+## 🛡️ 中间件
+
+### JWT认证中间件
+在 `internal/middleware/auth.go` 中实现:
+```go
+func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ token := r.Header.Get("Authorization")
+ if token == "" {
+ httpx.Error(w, errors.New("未提供认证token"))
+ return
+ }
+
+ userID, err := jwt.ParseToken(strings.TrimPrefix(token, "Bearer "))
+ if err != nil {
+ httpx.Error(w, errors.New("token无效"))
+ return
+ }
+
+ // 将用户ID注入到context
+ ctx := context.WithValue(r.Context(), "userID", userID)
+ next(w, r.WithContext(ctx))
+ })
+}
+```
+
+## 📊 数据模型
+
+### 模型定义示例
+```go
+type Photo struct {
+ ID string `db:"id" json:"id"`
+ Title string `db:"title" json:"title"`
+ Description string `db:"description" json:"description"`
+ Filename string `db:"filename" json:"filename"`
+ Thumbnail string `db:"thumbnail" json:"thumbnail"`
+ CategoryID string `db:"category_id" json:"category_id"`
+ UserID string `db:"user_id" json:"user_id"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+}
+```
+
+### 数据库操作
+```go
+// 插入
+err := l.svcCtx.PhotoModel.Insert(l.ctx, photo)
+
+// 查询
+photo, err := l.svcCtx.PhotoModel.FindOne(l.ctx, photoID)
+
+// 更新
+err := l.svcCtx.PhotoModel.Update(l.ctx, photo)
+
+// 删除
+err := l.svcCtx.PhotoModel.Delete(l.ctx, photoID)
+```
+
+## 🔄 配置管理
+
+### 配置文件: `etc/photography-api.yaml`
+```yaml
+Name: photography-api
+Host: 0.0.0.0
+Port: 8888
+
+Auth:
+ AccessSecret: your-secret-key
+ AccessExpire: 86400
+
+DataSource: photography.db
+
+Log:
+ ServiceName: photography-api
+ Mode: file
+ Path: logs
+ Level: info
+```
+
+## 🧪 测试规范
+
+### 单元测试
+```go
+func TestUploadPhotoLogic(t *testing.T) {
+ // 准备测试数据
+ req := &types.UploadPhotoRequest{
+ Title: "测试照片",
+ File: "test.jpg",
+ }
+
+ // 执行测试
+ logic := NewUploadPhotoLogic(context.Background(), svcCtx)
+ resp, err := logic.UploadPhoto(req)
+
+ // 断言结果
+ assert.NoError(t, err)
+ assert.NotEmpty(t, resp.Id)
+ assert.Equal(t, "测试照片", resp.Title)
+}
+```
+
+参考 [TASK_PROGRESS.md](mdc:TASK_PROGRESS.md) 了解当前后端开发进度。
+# Go-Zero 框架开发规则
+
+## 🚀 架构规范
+
+### 核心概念
+- **Handler**: HTTP请求处理层,只做参数验证和调用Logic
+- **Logic**: 业务逻辑层,包含核心业务代码
+- **Model**: 数据访问层,处理数据库操作
+- **Types**: 请求/响应类型定义
+
+### 文件结构
+```
+backend/
+├── cmd/api/main.go # 服务启动入口
+├── etc/photography-api.yaml # 配置文件
+├── api/desc/ # API定义文件
+├── internal/
+│ ├── handler/ # HTTP处理器
+│ ├── logic/ # 业务逻辑
+│ ├── model/ # 数据模型
+│ ├── middleware/ # 中间件
+│ ├── svc/ # 服务上下文
+│ └── types/ # 类型定义
+└── pkg/ # 工具包
+```
+
+## 🎯 开发流程
+
+### 1. API定义
+在 `api/desc/` 目录下定义接口:
+```api
+service photography-api {
+ @handler uploadPhoto
+ post /api/v1/photos (UploadPhotoRequest) returns (UploadPhotoResponse)
+}
+
+type UploadPhotoRequest {
+ Title string `form:"title"`
+ Description string `form:"description,optional"`
+ File string `form:"file"`
+}
+
+type UploadPhotoResponse {
+ Id string `json:"id"`
+ Title string `json:"title"`
+ Filename string `json:"filename"`
+ Thumbnail string `json:"thumbnail"`
+}
+```
+
+### 2. 代码生成
+```bash
+cd backend
+make api # 生成handler和logic骨架
+```
+
+### 3. Handler实现
+```go
+func (h *UploadPhotoHandler) UploadPhoto(w http.ResponseWriter, r *http.Request) {
+ var req types.UploadPhotoRequest
+ if err := httpx.Parse(r, &req); err != nil {
+ httpx.ErrorCtx(r.Context(), w, err)
+ return
+ }
+
+ l := logic.NewUploadPhotoLogic(r.Context(), h.svcCtx)
+ resp, err := l.UploadPhoto(&req)
+ response.Response(w, resp, err)
+}
+```
+
+### 4. Logic实现
+```go
+func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest) (*types.UploadPhotoResponse, error) {
+ // 1. 参数验证
+ if req.Title == "" {
+ return nil, errorx.NewDefaultError("照片标题不能为空")
+ }
+
+ // 2. 业务逻辑
+ photoID := uuid.New().String()
+
+ // 3. 数据持久化
+ photo := &model.Photo{
+ ID: photoID,
+ Title: req.Title,
+ // ...
+ }
+
+ err := l.svcCtx.PhotoModel.Insert(l.ctx, photo)
+ if err != nil {
+ return nil, err
+ }
+
+ return &types.UploadPhotoResponse{
+ Id: photoID,
+ Title: req.Title,
+ // ...
+ }, nil
+}
+```
+
+## 🔧 工具包使用
+
+### 文件处理
+使用 [pkg/utils/file/file.go](mdc:backend/pkg/utils/file/file.go):
+```go
+import "photography/pkg/utils/file"
+
+// 验证图片文件
+if !file.IsImageFile(filename) {
+ return errors.New("不支持的文件类型")
+}
+
+// 保存文件并生成缩略图
+savedPath, thumbnail, err := file.SaveImage(fileData, filename)
+```
+
+### JWT认证
+使用 [pkg/utils/jwt/jwt.go](mdc:backend/pkg/utils/jwt/jwt.go):
+```go
+import "photography/pkg/utils/jwt"
+
+// 生成token
+token, err := jwt.GenerateToken(userID, username)
+
+// 验证token
+userID, err := jwt.ParseToken(tokenString)
+```
+
+### 错误处理
+使用 [pkg/errorx/errorx.go](mdc:backend/pkg/errorx/errorx.go):
+```go
+import "photography/pkg/errorx"
+
+// 业务错误
+return nil, errorx.NewDefaultError("用户不存在")
+
+// 自定义错误码
+return nil, errorx.NewCodeError(40001, "参数错误")
+```
+
+## 🛡️ 中间件
+
+### JWT认证中间件
+在 `internal/middleware/auth.go` 中实现:
+```go
+func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ token := r.Header.Get("Authorization")
+ if token == "" {
+ httpx.Error(w, errors.New("未提供认证token"))
+ return
+ }
+
+ userID, err := jwt.ParseToken(strings.TrimPrefix(token, "Bearer "))
+ if err != nil {
+ httpx.Error(w, errors.New("token无效"))
+ return
+ }
+
+ // 将用户ID注入到context
+ ctx := context.WithValue(r.Context(), "userID", userID)
+ next(w, r.WithContext(ctx))
+ })
+}
+```
+
+## 📊 数据模型
+
+### 模型定义示例
+```go
+type Photo struct {
+ ID string `db:"id" json:"id"`
+ Title string `db:"title" json:"title"`
+ Description string `db:"description" json:"description"`
+ Filename string `db:"filename" json:"filename"`
+ Thumbnail string `db:"thumbnail" json:"thumbnail"`
+ CategoryID string `db:"category_id" json:"category_id"`
+ UserID string `db:"user_id" json:"user_id"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+}
+```
+
+### 数据库操作
+```go
+// 插入
+err := l.svcCtx.PhotoModel.Insert(l.ctx, photo)
+
+// 查询
+photo, err := l.svcCtx.PhotoModel.FindOne(l.ctx, photoID)
+
+// 更新
+err := l.svcCtx.PhotoModel.Update(l.ctx, photo)
+
+// 删除
+err := l.svcCtx.PhotoModel.Delete(l.ctx, photoID)
+```
+
+## 🔄 配置管理
+
+### 配置文件: `etc/photography-api.yaml`
+```yaml
+Name: photography-api
+Host: 0.0.0.0
+Port: 8888
+
+Auth:
+ AccessSecret: your-secret-key
+ AccessExpire: 86400
+
+DataSource: photography.db
+
+Log:
+ ServiceName: photography-api
+ Mode: file
+ Path: logs
+ Level: info
+```
+
+## 🧪 测试规范
+
+### 单元测试
+```go
+func TestUploadPhotoLogic(t *testing.T) {
+ // 准备测试数据
+ req := &types.UploadPhotoRequest{
+ Title: "测试照片",
+ File: "test.jpg",
+ }
+
+ // 执行测试
+ logic := NewUploadPhotoLogic(context.Background(), svcCtx)
+ resp, err := logic.UploadPhoto(req)
+
+ // 断言结果
+ assert.NoError(t, err)
+ assert.NotEmpty(t, resp.Id)
+ assert.Equal(t, "测试照片", resp.Title)
+}
+```
+
+参考 [TASK_PROGRESS.md](mdc:TASK_PROGRESS.md) 了解当前后端开发进度。
diff --git a/.cursor/rules/common/code-style.mdc b/.cursor/rules/common/code-style.mdc
new file mode 100644
index 0000000..bf38f4f
--- /dev/null
+++ b/.cursor/rules/common/code-style.mdc
@@ -0,0 +1,262 @@
+---
+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
+// ✅ 推荐顺序:布局 → 尺寸 → 间距 → 颜色 → 其他
+
+

+
+```
+
+### 响应式设计
+```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
+}
+```
+
+遵循这些约定可以保持代码的一致性和可维护性。
diff --git a/.cursor/rules/common/development-workflow.mdc b/.cursor/rules/common/development-workflow.mdc
new file mode 100644
index 0000000..11172b2
--- /dev/null
+++ b/.cursor/rules/common/development-workflow.mdc
@@ -0,0 +1,151 @@
+---
+description: General development workflow and best practices
+---
+
+# 开发工作流规则
+
+## 🎯 当前优先级任务
+
+基于 [TASK_PROGRESS.md](mdc:TASK_PROGRESS.md),当前高优先级任务:
+
+### 🔥 立即处理
+1. **完善照片更新和删除业务逻辑** - 后端CRUD完整性
+2. **完善分类更新和删除业务逻辑** - 后端CRUD完整性
+3. **前端与后端API集成测试** - 端到端功能验证
+
+### 📋 本周目标
+- 实现完整的照片和分类CRUD操作
+- 前后端API集成调试
+- 用户认证流程实现
+
+## 🔄 开发流程规范
+
+### Git 工作流
+```bash
+# 功能开发
+git checkout -b feature/photo-update-api
+git add .
+git commit -m "feat: 实现照片更新API"
+git push origin feature/photo-update-api
+
+# 代码审查后合并
+git checkout main
+git merge feature/photo-update-api
+```
+
+### 提交信息规范
+```bash
+feat: 新功能
+fix: 修复bug
+docs: 文档更新
+style: 代码格式
+refactor: 重构
+test: 测试
+chore: 构建/工具
+```
+
+## 🧪 测试策略
+
+### 后端测试
+```bash
+# API 测试
+curl -X GET http://localhost:8888/api/v1/health
+curl -X POST http://localhost:8888/api/v1/auth/login \
+ -H "Content-Type: application/json" \
+ -d '{"username":"admin","password":"123456"}'
+
+# 功能测试
+make test
+```
+
+### 前端测试
+```bash
+# 启动开发服务器
+cd admin && bun run dev # 管理后台:5173
+cd frontend && pnpm dev # 用户界面:3000
+cd ui && pnpm dev # 组件库:6006
+```
+
+## 🚀 部署流程
+
+### 开发环境
+```bash
+# 后端
+cd backend
+make run # 端口:8888
+
+# 前端项目并行启动
+cd admin && bun run dev &
+cd frontend && pnpm dev &
+```
+
+### 生产环境准备
+1. 配置PostgreSQL数据库
+2. 更新CI/CD流程
+3. 配置反向代理
+4. 设置监控和日志
+
+## 📁 文件组织原则
+
+### 后端文件
+- 新Handler: `internal/handler/{module}/{action}Handler.go`
+- 新Logic: `internal/logic/{module}/{action}Logic.go`
+- 新API: `api/desc/{module}.api`
+
+### 前端文件
+- 新页面: `src/pages/{PageName}.tsx`
+- 新组件: `src/components/{ComponentName}.tsx`
+- 新服务: `src/services/{serviceName}.ts`
+
+## 🔧 开发工具配置
+
+### VS Code 推荐插件
+- Go语言:`golang.go`
+- React:`ES7+ React/Redux/React-Native snippets`
+- Tailwind:`Tailwind CSS IntelliSense`
+- API测试:`REST Client`
+
+### 本地环境变量
+```bash
+# backend/.env
+DATABASE_URL="sqlite:photography.db"
+JWT_SECRET="your-secret-key"
+UPLOAD_PATH="./uploads"
+
+# frontend/.env.local
+NEXT_PUBLIC_API_URL="http://localhost:8888/api/v1"
+```
+
+## 🛠️ 故障排除
+
+### 常见问题
+1. **端口冲突**: 检查8888(后端)、3000(前端)、5173(管理后台)
+2. **依赖问题**: 删除node_modules重新安装
+3. **Go模块**: 运行`go mod tidy`清理依赖
+4. **API调用失败**: 检查CORS设置和认证token
+
+### 调试命令
+```bash
+# 检查服务状态
+lsof -i :8888
+netstat -tlnp | grep 8888
+
+# 查看日志
+tail -f backend/logs/photography.log
+```
+
+## 📊 进度跟踪
+
+### 完成标准
+- ✅ 代码通过测试
+- ✅ API接口可正常调用
+- ✅ 前端界面功能正常
+- ✅ 更新TASK_PROGRESS.md状态
+
+### 每日更新
+在 [TASK_PROGRESS.md](mdc:TASK_PROGRESS.md) 记录:
+- 完成的任务
+- 遇到的问题
+- 明日计划
+
+保持项目进度透明化和可追踪性。
diff --git a/.cursor/rules/common/git-workflow.mdc b/.cursor/rules/common/git-workflow.mdc
new file mode 100644
index 0000000..eac874f
--- /dev/null
+++ b/.cursor/rules/common/git-workflow.mdc
@@ -0,0 +1,248 @@
+# Git 工作流规则
+
+## 🌿 分支管理
+
+### 分支命名规范
+```bash
+# 功能分支
+feature/photo-upload-api
+feature/admin-dashboard
+feature/responsive-design
+
+# 修复分支
+fix/photo-delete-bug
+fix/login-redirect-issue
+
+# 优化分支
+refactor/api-structure
+refactor/component-organization
+
+# 文档分支
+docs/api-documentation
+docs/deployment-guide
+```
+
+### 分支工作流
+```bash
+# 1. 从主分支创建功能分支
+git checkout main
+git pull origin main
+git checkout -b feature/photo-update-api
+
+# 2. 开发和提交
+git add .
+git commit -m "feat: 实现照片更新API"
+
+# 3. 推送分支
+git push origin feature/photo-update-api
+
+# 4. 创建Pull Request
+# 在GitHub/GitLab上创建PR
+
+# 5. 代码审查后合并
+git checkout main
+git pull origin main
+git branch -d feature/photo-update-api
+```
+
+## 📝 提交信息规范
+
+### 提交类型
+```bash
+feat: 新功能
+fix: 修复bug
+docs: 文档更新
+style: 代码格式化(不影响代码逻辑)
+refactor: 重构(既不是新增功能,也不是修复bug)
+perf: 性能优化
+test: 添加测试
+chore: 构建过程或辅助工具的变动
+ci: CI/CD配置文件和脚本的变动
+```
+
+### 提交信息格式
+```bash
+():
+
+
+
+