Files
xiaoxia-saas/docs/CODING-STANDARD.md
T
xiaoxia 3862996045
CI/CD Pipeline / Check if frontend-only change (push) Waiting to run
CI/CD Pipeline / Validate - Code Quality (push) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (push) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (push) Waiting to run
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Waiting to run
CI/CD Pipeline / PR Build Web Image (push) Waiting to run
CI/CD Pipeline / PR Build Worker Image (push) Waiting to run
CI/CD Pipeline / Build Staging API Image (push) Waiting to run
CI/CD Pipeline / Build Staging Web Image (push) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Waiting to run
CI/CD Pipeline / Build Production Web Image (push) Waiting to run
CI/CD Pipeline / Build Production Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
refactor(#783): 统一API文件命名风格为kebab-case (#788)
2026-07-23 22:34:56 +08:00

531 lines
11 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 编码规范
本文档定义新 SaaS 项目的 Python 编码规范。
---
## 1. 基础规范
遵循 [PEP 8](https://peps.python.org/pep-0008/),以下是重点和补充。
### 1.1 命名规范
**模块/包**:小写 + 下划线
```python
# ✅ 正确
from packages.domain import entities
from packages.adapters.in_memory import project_repository
# ❌ 错误
from packages.Domain import Entities
from packages.adapters.InMemory import ProjectRepository
```
**类**PascalCase
```python
# ✅ 正确
class Project:
pass
class InMemoryProjectRepository:
pass
# ❌ 错误
class project:
pass
class in_memory_project_repository:
pass
```
**函数/变量**:小写 + 下划线
```python
# ✅ 正确
def create_project(workspace_id: str, name: str) -> Project:
pass
user_count = 10
# ❌ 错误
def CreateProject(workspace_id: str, name: str) -> Project:
pass
UserCount = 10
```
**常量**:大写 + 下划线
```python
# ✅ 正确
MAX_PROJECT_NAME_LENGTH = 100
DEFAULT_PAGE_SIZE = 20
# ❌ 错误
maxProjectNameLength = 100
default_page_size = 20
```
**私有属性/方法**:前缀 `_`
```python
class Project:
def __init__(self):
self._internal_state = {}
def _validate(self):
pass
```
---
## 2. Type Hints
**强制使用** type hints,提升代码可读性和 IDE 支持。
```python
# ✅ 正确
def create_project(workspace_id: str, name: str, description: str = "") -> Project:
pass
def list_projects(workspace_id: str) -> list[Project]:
pass
def get_project(project_id: str) -> Project | None:
pass
# ❌ 错误
def create_project(workspace_id, name, description=""):
pass
```
**复杂类型**
```python
from typing import Protocol, Any
# Dict/List
def update_metadata(metadata: dict[str, Any]) -> None:
pass
# Optional (Python 3.10+ 用 | None)
def get_user(user_id: str) -> User | None:
pass
# Protocol
class Repository(Protocol):
def get(self, id: str) -> Entity | None:
pass
```
---
## 3. Dataclass
**优先使用** `dataclass` 定义实体和值对象。
```python
from dataclasses import dataclass, field
from datetime import datetime, timezone
# ✅ 正确
@dataclass(slots=True)
class Project:
id: str
workspace_id: str
name: str
description: str = ""
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
# ❌ 错误(不用 dataclass
class Project:
def __init__(self, id: str, workspace_id: str, name: str, description: str = ""):
self.id = id
self.workspace_id = workspace_id
self.name = name
self.description = description
```
**为什么用 `slots=True`**
- 节省内存
- 防止意外添加属性
- 提升性能
---
## 4. 注释与文档
### 4.1 模块/类/函数注释
**使用中文注释**
```python
def create_project(workspace_id: str, name: str, description: str = "") -> Project:
"""
创建项目。
Args:
workspace_id: 工作空间 ID
name: 项目名称(不能为空)
description: 项目描述(可选)
Returns:
创建的项目实体
Raises:
ValueError: 项目名称为空时
"""
pass
```
### 4.2 复杂逻辑注释
```python
# 正确做法:复杂逻辑加注释
def calculate_priority(job: IngestJob) -> int:
# 优先级规则:
# 1. FAILED 状态最高(需要重试)
# 2. PENDING 状态次高(等待处理)
# 3. PROCESSING 状态最低(正在处理)
if job.status == IngestJobStatus.FAILED:
return 100
elif job.status == IngestJobStatus.PENDING:
return 50
else:
return 10
```
---
## 5. 异常处理
### 5.1 使用具体异常
```python
# ✅ 正确
def get_project(project_id: str) -> Project:
if not project_id:
raise ValueError("项目 ID 不能为空")
project = repository.get(project_id)
if project is None:
raise KeyError(f"项目 {project_id} 不存在")
return project
# ❌ 错误
def get_project(project_id: str) -> Project:
if not project_id:
raise Exception("错误") # 太宽泛
```
### 5.2 自定义异常
```python
class ProjectNotFoundError(Exception):
"""项目不存在异常。"""
pass
class ProjectNameTooLongError(ValueError):
"""项目名称过长异常。"""
pass
```
---
## 6. Clean Architecture 约束
### 6.1 依赖方向
```
Apps (api/worker/web)
Application (use cases)
Ports (interfaces) ← Adapters (implementations)
Domain (entities/rules)
```
**Domain 层**
- ❌ 不能依赖任何外层
- ❌ 不能依赖 SQLAlchemy、FastAPI、Celery
- ✅ 只能依赖 Python 标准库
```python
# ✅ 正确(Domain 层)
from dataclasses import dataclass
from datetime import datetime
from uuid import uuid4
@dataclass(slots=True)
class Project:
id: str
name: str
# ❌ 错误(Domain 层)
from sqlalchemy import Column, String # ❌ 不能依赖 SQLAlchemy
from fastapi import HTTPException # ❌ 不能依赖 FastAPI
@dataclass(slots=True)
class Project:
id: str
name: str
```
**Application 层**
- ✅ 可以依赖 Domain + Ports
- ❌ 不能依赖 Adapters
**Adapters 层**
- ✅ 可以依赖 Domain + Ports
- ✅ 可以使用外部库(SQLAlchemy、Redis 等)
---
## 7. 测试
### 7.1 测试文件命名
```
tests/
├── integration/
│ ├── test_projects.py
│ ├── test_ingest_pipeline.py
│ └── test_classification_pipeline.py
└── unit/
├── test_project_entity.py
└── test_asset_validation.py
```
### 7.2 测试函数命名
```python
# ✅ 正确
def test_create_project_with_valid_name():
pass
def test_create_project_with_empty_name_should_fail():
pass
def test_list_projects_by_workspace():
pass
# ❌ 错误
def test1():
pass
def test_project():
pass
```
### 7.3 测试结构(AAA 模式)
```python
def test_create_project():
# Arrange(准备)
workspace_id = "ws-1"
name = "测试项目"
repository = InMemoryProjectRepository()
use_case = CreateProjectUseCase(repository)
# Act(执行)
project = use_case.execute(
CreateProjectCommand(workspace_id=workspace_id, name=name)
)
# Assert(断言)
assert project.name == name
assert project.workspace_id == workspace_id
```
---
## 8. 代码格式化
### 8.1 行长度
- 最大 120 字符
- 优先 88 字符(Black 默认)
### 8.2 导入顺序
```python
# 1. 标准库
import os
from datetime import datetime
from typing import Protocol
# 2. 第三方库
from fastapi import FastAPI
from sqlalchemy import Column
# 3. 本地模块
from packages.domain import Project
from packages.application import CreateProjectUseCase
```
### 8.3 空行
```python
# 类之间:2 行
class User:
pass
class Workspace:
pass
# 函数之间:1 行
def create_user():
pass
def list_users():
pass
```
---
## 9. 安全规范
### 9.1 禁止硬编码敏感信息
```python
# ❌ 错误
DATABASE_URL = "postgresql://admin:password123@localhost/db"
API_KEY = "sk-1234567890abcdef"
# ✅ 正确
import os
DATABASE_URL = os.getenv("DATABASE_URL")
API_KEY = os.getenv("API_KEY")
```
### 9.2 输入验证
```python
# ✅ 正确
def create_project(name: str) -> Project:
clean_name = name.strip()
if not clean_name:
raise ValueError("项目名称不能为空")
if len(clean_name) > 100:
raise ValueError("项目名称不能超过 100 字符")
return Project(id=uuid4().hex, name=clean_name)
```
---
## 10. 性能规范
### 10.1 避免 N+1 查询
```python
# ❌ 错误
projects = repository.list_by_workspace("ws-1")
for project in projects:
assets = asset_repository.list_by_project(project.id) # N+1
# ✅ 正确
projects = repository.list_by_workspace("ws-1")
project_ids = [p.id for p in projects]
assets = asset_repository.list_by_projects(project_ids) # 一次查询
```
### 10.2 使用生成器
```python
# ✅ 正确(大数据集)
def list_all_assets() -> Generator[Asset, None, None]:
for asset in repository.stream():
yield asset
# ❌ 错误(加载全部到内存)
def list_all_assets() -> list[Asset]:
return repository.list_all() # 可能 OOM
```
---
## 8. 前端规范(React + TypeScript
### 8.1 文件命名规范
| 类型 | 风格 | 示例 | 说明 |
|------|------|------|------|
| **目录名** | kebab-case | `editing-planner/`, `asset-selector/` | 全小写,多单词用短横线连接 |
| **组件文件** | PascalCase | `AssetSelector.tsx`, `MediaPanel.tsx` | 与组件导出名一致 |
| **页面组件** | PascalCase | `EditingPlanner.tsx`, `MyTemplates.tsx` | 放在 kebab-case 目录中 |
| **API 文件** | kebab-case | `template-editor.ts`, `voice-clone.ts` | 与 RESTful 资源路径风格一致 |
| **Hooks** | camelCase (use前缀) | `useAuth.ts`, `useCloneProgress.ts` | React 官方惯例 |
| **Store** | kebab-case | `auth-store.ts`, `ui-store.ts` | |
| **工具函数/helpers** | kebab-case | `format-duration.ts`, `date-utils.ts` | |
| **类型定义** | kebab-case | `types.ts`, `subtitle-types.ts` | 目录内类型定义可用 `types.ts` |
| **常量** | UPPER_SNAKE_CASE | `MAX_UPLOAD_SIZE`, `API_BASE_URL` | |
| **测试文件** | 与被测文件同名 + `.test` | `auth.test.ts`, `AssetSelector.test.tsx` | 放在 `test/` 目录下,保持相同相对路径 |
### 8.2 组件命名
- 组件名使用 **PascalCase**,与文件名一致
- 默认导出组件名与文件名相同
- 高阶组件/包装器用 `with` 前缀:`withAuth(Component)`
- 渲染属性组件用 `Render` 后缀:`UserRender`
```tsx
// ✅ 正确
// 文件: AssetSelector.tsx
const AssetSelector: React.FC<AssetSelectorProps> = ({ assets }) => { ... };
export default AssetSelector;
// ❌ 错误
// 文件: asset-selector.tsx
const assetSelector = () => { ... };
```
### 8.3 变量与函数命名
- **变量/函数**camelCase
- **布尔变量**:用 `is/has/should/can` 前缀
- **事件处理函数**:用 `handle` 前缀 + 事件名
- **事件 handler prop**:用 `on` 前缀
```tsx
// ✅ 正确
const isLoading = true;
const hasError = false;
const handleSubmit = () => { ... };
<Button onClick={onClick} />
```
### 8.4 导入路径
- 使用 `@/` 别名引用 `src/` 下的文件
- 同一目录内用相对路径 `./`
- 导入顺序:React → 第三方库 → @/内部模块 → 相对路径 → 样式
```tsx
import React, { useState } from 'react';
import { Button, Modal } from 'antd';
import { useAuth } from '@/hooks/useAuth';
import { Asset } from '@/api/asset-selector';
import { MediaPanel } from './MediaPanel';
import './AssetSelector.css';
```
### 8.5 CSS/样式命名
- CSS Modules / CSS 类名:kebab-case
- styled-componentsPascalCase(与组件一致)
- Tailwind 工具类遵循官方惯例
```css
/* ✅ 正确 */
.asset-selector { ... }
.asset-item { ... }
.asset-item--active { ... }
```
---
**最后更新**: 2026-07-23
**版本**: v1.1