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
531 lines
11 KiB
Markdown
Executable File
531 lines
11 KiB
Markdown
Executable File
# 编码规范
|
||
|
||
本文档定义新 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-components:PascalCase(与组件一致)
|
||
- Tailwind 工具类遵循官方惯例
|
||
|
||
```css
|
||
/* ✅ 正确 */
|
||
.asset-selector { ... }
|
||
.asset-item { ... }
|
||
.asset-item--active { ... }
|
||
```
|
||
|
||
---
|
||
|
||
**最后更新**: 2026-07-23
|
||
**版本**: v1.1
|