diff --git a/.trellis/spec/backend/configuration-and-runtime.md b/.trellis/spec/backend/configuration-and-runtime.md new file mode 100644 index 0000000..fb8b8c3 --- /dev/null +++ b/.trellis/spec/backend/configuration-and-runtime.md @@ -0,0 +1,24 @@ +# 配置与运行时 + +## 配置入口 + +进程配置集中在 `zhixing-server/src/zhixing_server/bootstrap/config.py:Settings`: + +- 继承 `pydantic_settings.BaseSettings`,字段声明默认值和可接受类型。 +- 通过 `SettingsConfigDict(env_file=".env", env_prefix="ZHIXING_", extra="ignore")` 读取环境变量;新增变量应遵循 `ZHIXING_` 命名。 +- `app_env` 使用 `Literal["development", "test", "production"]`,不要在业务代码里重复字符串校验。 +- `get_settings()` 使用 `@lru_cache`,在一个进程内复用同一配置对象。 + +端点通过 FastAPI 依赖注入拿配置,例如 `interfaces/http/system.py:system_status` 的 `Annotated[Settings, Depends(get_settings)]`。不要在路由或领域代码中直接读取 `os.environ`、`.env` 或 Docker 变量。 + +## 应用生命周期与部署 + +- `bootstrap/app.py:create_app` 创建 `FastAPI(title=settings.app_name)`,然后挂载 `operational_router` 和 `api_v1_router`。 +- `main.py` 只导出 ASGI `app`,容器入口可以安全导入它。 +- 健康检查固定为 `/healthz`;业务 API 固定挂在 `/api/v1` 下。 +- 开发环境由 Vite 将 `/api` 和 `/healthz` 代理到 `VITE_DEV_API_TARGET`(默认 `http://localhost:8000`);生产环境由 `zhixing-web/nginx/default.conf.template` 以 `API_UPSTREAM` 代理。浏览器代码不要拼接后端绝对地址。 +- Docker Compose 的环境变量和端口默认值见根目录 `.env.example`、`docker-compose.dev.yml` 和 `docker-compose.prod.yml`。 + +## 目前没有的能力 + +当前仓库没有数据库连接、迁移、任务队列或日志配置实现。不要只因为 `Settings.log_level` 字段存在就引入一套日志框架;如果新增持久化或结构化日志,应先形成可验证的实现和对应规格。 diff --git a/.trellis/spec/backend/database-guidelines.md b/.trellis/spec/backend/database-guidelines.md deleted file mode 100644 index b61aa78..0000000 --- a/.trellis/spec/backend/database-guidelines.md +++ /dev/null @@ -1,51 +0,0 @@ -# Database Guidelines - -> Database patterns and conventions for this project. - ---- - -## Overview - - - -(To be filled by the team) - ---- - -## Query Patterns - - - -(To be filled by the team) - ---- - -## Migrations - - - -(To be filled by the team) - ---- - -## Naming Conventions - - - -(To be filled by the team) - ---- - -## Common Mistakes - - - -(To be filled by the team) diff --git a/.trellis/spec/backend/directory-structure.md b/.trellis/spec/backend/directory-structure.md index 9bb253d..5993968 100644 --- a/.trellis/spec/backend/directory-structure.md +++ b/.trellis/spec/backend/directory-structure.md @@ -1,54 +1,50 @@ -# Directory Structure +# 后端目录与模块边界 -> How backend code is organized in this project. +## 当前布局 ---- - -## Overview - - - -(To be filled by the team) - ---- - -## Directory Layout - -``` - -src/ -├── ... -└── ... +```text +zhixing-server/ +├── src/zhixing_server/ +│ ├── bootstrap/ # 应用工厂和进程配置 +│ │ ├── app.py +│ │ └── config.py +│ ├── interfaces/http/ # 跨上下文 HTTP 路由目录 +│ │ ├── router.py # /api/v1 路由目录 +│ │ └── system.py # /healthz、/api/v1/system/status +│ ├── modules/ # 业务 bounded context(目前为空) +│ │ └── README.md +│ ├── shared/ # 没有特定业务所有权的小型基础能力 +│ ├── main.py # ASGI 入口,只导出 app +│ └── __init__.py +└── tests/ # 按行为/入口组织的 pytest 测试 ``` ---- +`zhixing-server/src/zhixing_server/bootstrap/app.py:create_app` 负责组合 FastAPI 应用,`main.py` 只执行 `app = create_app()`。不要把应用组合、环境读取或业务逻辑塞进 `main.py`。 -## Module Organization +## 新增业务上下文 - +只有在领域语言和所有权边界明确后,才在 `modules/` 下创建上下文。推荐形状来自 `zhixing-server/src/zhixing_server/modules/README.md`: -(To be filled by the team) +```text +modules// +├── domain/ # 实体、值对象、领域服务、端口 +├── application/ # 用例和编排 +├── infrastructure/ # 存储及外部系统适配器 +└── presentation/ # HTTP 或消息驱动的交付适配器 +``` ---- +domain 层不能导入 FastAPI、持久化客户端或 infrastructure 适配器。上下文内的 presentation 负责把输入转换成用例需要的类型;顶层 `interfaces/http` 只承载跨上下文的运维入口和路由目录。 -## Naming Conventions +## 命名与导入 - +- Python 包、模块和函数使用 `snake_case`;测试文件使用 `test_*.py`,例如 `tests/test_system_http.py`。 +- 对外公开的类、响应模型和函数应有类型标注与文档字符串,参照 `bootstrap/config.py` 和 `interfaces/http/system.py`。 +- `shared/` 只能放跨上下文且无业务归属的原语;不要把某个上下文的领域规则放进去。 +- 避免新建全局 `services/`、`repositories/` 或 `utils/` 目录来绕过上下文边界。 -(To be filled by the team) +## 参考实现 ---- - -## Examples - - - -(To be filled by the team) +- 应用组合:`zhixing-server/src/zhixing_server/bootstrap/app.py` +- 路由目录:`zhixing-server/src/zhixing_server/interfaces/http/router.py` +- 运维/系统端点:`zhixing-server/src/zhixing_server/interfaces/http/system.py` +- 当前 HTTP 契约测试:`zhixing-server/tests/test_system_http.py` diff --git a/.trellis/spec/backend/error-handling.md b/.trellis/spec/backend/error-handling.md index bcd5533..9bd2a6c 100644 --- a/.trellis/spec/backend/error-handling.md +++ b/.trellis/spec/backend/error-handling.md @@ -1,51 +1,24 @@ -# Error Handling +# 错误处理 -> How errors are handled in this project. +## 当前行为 ---- +服务目前没有自定义异常层、全局异常处理器或统一错误 envelope。FastAPI/Pydantic 负责请求验证和标准 HTTP 错误响应;现有成功端点只返回声明过的响应模型。新增错误契约前,应先确认前端是否需要稳定的错误字段,并用 HTTP 契约测试锁定它。 -## Overview +## 分层规则 - +## 前端边界 -(To be filled by the team) +`zhixing-web/src/shared/api/request-json.ts:requestJson` 对非 2xx 响应抛出 `ApiError`,并保留 HTTP 状态码;React Query 通过 `isError` 将其传给 feature 页面。页面应显式渲染加载、错误和成功状态,参照 `features/system/pages/system-status-page.tsx`,不要把错误静默成“无数据”。 ---- +当前 `requestJson` 只对响应体做泛型断言,不做运行时 schema 校验。因此来自外部或不稳定来源的数据在进入页面前必须增加明确的运行时校验,不能把 TypeScript 类型当成网络验证。 -## Error Types +## 测试要求 - - -(To be filled by the team) - ---- - -## Error Handling Patterns - - - -(To be filled by the team) - ---- - -## API Error Responses - - - -(To be filled by the team) - ---- - -## Common Mistakes - - - -(To be filled by the team) +- 对新增 HTTP 错误状态,优先在 `zhixing-server/tests/` 验证状态码和 JSON 契约。 +- 对前端错误展示,在页面测试中 mock query hook 的 `isError`/`error` 状态并断言用户可见文本。 +- 不要只测试异常类被抛出而不测试边界响应或用户行为。 diff --git a/.trellis/spec/backend/http-api-contracts.md b/.trellis/spec/backend/http-api-contracts.md new file mode 100644 index 0000000..156219f --- /dev/null +++ b/.trellis/spec/backend/http-api-contracts.md @@ -0,0 +1,30 @@ +# HTTP API 契约 + +## 路由组合 + +`zhixing-server/src/zhixing_server/interfaces/http/router.py` 是 `/api/v1` 的唯一目录入口: + +```python +api_v1_router = APIRouter(prefix="/api/v1") +api_v1_router.include_router(system_router, prefix="/system", tags=["system"]) +``` + +跨上下文的运维端点放在 `operational_router`;业务端点应由对应上下文的 presentation 适配器提供,再由路由目录挂载。不要在 `bootstrap/app.py` 里堆积路径字符串。 + +## 响应模型 + +每个稳定的 JSON 响应都应有 Pydantic `BaseModel`,并在装饰器中声明 `response_model`。现有 `HealthResponse` 和 `SystemStatusResponse` 位于 `interfaces/http/system.py`: + +- `/healthz` 返回严格的 `{"status": "ok"}`,设置 `include_in_schema=False` 以供容器探针使用。 +- `/api/v1/system/status` 返回 `status`、`service`、`environment`;只暴露安全的运行时元数据,不返回环境变量原文或秘密。 +- 字段名是前后端契约的一部分。变更时同步更新 `zhixing-server/tests/test_system_http.py`、`zhixing-web/src/features/system/api/system.types.ts` 和页面测试。 + +## 输入、依赖和错误 + +- 通过 FastAPI 参数、`Annotated` 依赖和 Pydantic 模型完成边界转换;不要把原始请求对象传入 domain。 +- 简单、无阻塞的当前端点使用同步 `def`,保持与 `system.py` 一致;只有实际需要异步 I/O 时才使用 `async def`。 +- 当前服务没有自定义错误 envelope。验证错误和显式 HTTP 错误沿用 FastAPI 默认契约,除非先增加全局约定并补充测试。 + +## 验证 + +后端契约用 `TestClient(create_app())` 测试,参照 `tests/test_system_http.py`。前端使用同源 `/api/v1/...` 路径,经 `shared/api/request-json.ts` 调用;不要在 feature 页面内直接调用 `fetch`。 diff --git a/.trellis/spec/backend/index.md b/.trellis/spec/backend/index.md index 1c0b4c4..2a32c4a 100644 --- a/.trellis/spec/backend/index.md +++ b/.trellis/spec/backend/index.md @@ -1,38 +1,33 @@ -# Backend Development Guidelines +# 后端开发规格 -> Best practices for backend development in this project. +后端是 Python 3.12 + FastAPI 的单体服务。当前代码只包含启动组合、环境配置、运维端点和 `system` HTTP 切片;业务 bounded context 尚未创建。新增后端代码应先确认它属于启动边界、共享基础设施、HTTP 入口还是某个明确的 bounded context。 ---- +## 规格导航 -## Overview +| 规格 | 用途 | +| --- | --- | +| [目录与模块边界](./directory-structure.md) | 包结构、bounded context 和导入边界 | +| [配置与运行时](./configuration-and-runtime.md) | `Settings`、应用工厂和部署环境 | +| [HTTP 契约](./http-api-contracts.md) | 路由组合、响应模型和同源 API 路径 | +| [错误处理](./error-handling.md) | 当前 FastAPI 错误行为及跨层错误传递 | +| [质量与测试](./quality-guidelines.md) | Ruff、Pyright、pytest 及禁止模式 | -This directory contains guidelines for backend development. Fill in each file with your project's specific conventions. +## 开发前检查 ---- +- 先阅读 `docs/adr/0001-bounded-context-first-modular-monolith.md`,确认新业务是否有清晰的语言和所有权边界。 +- 先阅读目标上下文的 `modules//README.md`(如已存在),再决定 domain、application、infrastructure、presentation 的位置。 +- 变更 HTTP 字段时同时检查 `zhixing-server/tests/`、前端 feature API 类型以及 `docs/adr/0002-use-a-same-origin-browser-api.md`。 +- 不要为了“未来可能需要”创建空的数据库、服务或日志层;当前仓库没有这些实现。 -## Guidelines Index +## 质量检查 -| Guide | Description | Status | -|-------|-------------|--------| -| [Directory Structure](./directory-structure.md) | Module organization and file layout | To fill | -| [Database Guidelines](./database-guidelines.md) | ORM patterns, queries, migrations | To fill | -| [Error Handling](./error-handling.md) | Error types, handling strategies | To fill | -| [Quality Guidelines](./quality-guidelines.md) | Code standards, forbidden patterns | To fill | -| [Logging Guidelines](./logging-guidelines.md) | Structured logging, log levels | To fill | +在 `zhixing-server/` 下运行: ---- +```bash +uv run ruff format --check . +uv run ruff check . +uv run pyright +uv run pytest +``` -## How to Fill These Guidelines - -For each guideline file: - -1. Document your project's **actual conventions** (not ideals) -2. Include **code examples** from your codebase -3. List **forbidden patterns** and why -4. Add **common mistakes** your team has made - -The goal is to help AI assistants and new team members understand how YOUR project works. - ---- - -**Language**: All documentation should be written in **English**. +根目录 `./dev.sh check` 和 `./dev.sh test` 会执行完整前后端检查。 diff --git a/.trellis/spec/backend/logging-guidelines.md b/.trellis/spec/backend/logging-guidelines.md deleted file mode 100644 index bb930df..0000000 --- a/.trellis/spec/backend/logging-guidelines.md +++ /dev/null @@ -1,51 +0,0 @@ -# Logging Guidelines - -> How logging is done in this project. - ---- - -## Overview - - - -(To be filled by the team) - ---- - -## Log Levels - - - -(To be filled by the team) - ---- - -## Structured Logging - - - -(To be filled by the team) - ---- - -## What to Log - - - -(To be filled by the team) - ---- - -## What NOT to Log - - - -(To be filled by the team) diff --git a/.trellis/spec/backend/quality-guidelines.md b/.trellis/spec/backend/quality-guidelines.md index c1e1065..957b426 100644 --- a/.trellis/spec/backend/quality-guidelines.md +++ b/.trellis/spec/backend/quality-guidelines.md @@ -1,51 +1,38 @@ -# Quality Guidelines +# 后端质量与测试 -> Code quality standards for backend development. +## 工具链 ---- +`zhixing-server/pyproject.toml` 固定 Python 3.12,使用 `uv` 管理环境和锁文件。质量门禁由以下配置定义: -## Overview +- Ruff 格式化:100 列、双引号。 +- Ruff lint:`B`, `E`, `F`, `I`, `SIM`, `UP`。 +- Pyright:`strict`,检查 `src` 和 `tests`。 +- Pytest:严格配置和严格 markers,测试目录为 `tests/`。 - +```bash +uv run ruff format --check . +uv run ruff check . +uv run pyright +uv run pytest +``` -(To be filled by the team) +## 代码规则 ---- +- 函数、方法和公开类使用完整类型标注;公共入口写清楚参数、返回值和边界原因,参照 `bootstrap/config.py`、`bootstrap/app.py`。 +- HTTP 响应显式使用 Pydantic 模型和 `response_model`,不要返回随意拼接的 dict。 +- 配置通过 `Settings` 注入,不直接读取进程环境;应用组合集中在 `create_app`。 +- 领域代码不得依赖 FastAPI、持久化客户端或 infrastructure,符合 `modules/README.md` 的边界。 +- 变更端点时同时更新行为测试和与之对应的前端契约。 -## Forbidden Patterns +## 测试形状 - +`tests/test_system_http.py` 使用 `TestClient(create_app())` 从真实应用组合出发,断言状态码和完整 JSON。新增路由优先采用相同的黑盒 HTTP 测试;只有纯领域逻辑才单独测试函数或对象。 -(To be filled by the team) +## 禁止模式 ---- - -## Required Patterns - - - -(To be filled by the team) - ---- - -## Testing Requirements - - - -(To be filled by the team) - ---- - -## Code Review Checklist - - - -(To be filled by the team) +- 不要提交未使用的 import、宽泛 `except Exception`、无类型的公共参数或绕过 Pyright 的 `Any`。 +- 不要把秘密、完整环境变量、请求凭据写进响应或日志;系统状态端点只返回安全元数据。 +- 不要引入没有真实使用场景的空数据库/日志抽象,或在尚未理解 bounded context 前创建业务模块。 +- 不要为了让测试通过复制 `create_app` 的路由逻辑;测试应调用实际应用工厂。 diff --git a/.trellis/spec/frontend/component-guidelines.md b/.trellis/spec/frontend/component-guidelines.md index 6836c3f..3e36299 100644 --- a/.trellis/spec/frontend/component-guidelines.md +++ b/.trellis/spec/frontend/component-guidelines.md @@ -1,59 +1,49 @@ -# Component Guidelines +# 组件与样式 -> How components are built in this project. +## 组件形状 ---- +React 模块使用具名函数组件,不使用无意义的 default export。`features/system/pages/system-status-page.tsx` 展示了页面组件如何组合 query、store 和 shared UI;`shared/ui/card.tsx` 展示了通过原生 HTML 属性扩展 primitive 的方式。 -## Overview +```tsx +export function Button({ + className, + size, + type = "button", + variant, + ...props +}: ButtonHTMLAttributes & + VariantProps) { + return ( +