Files
nexus/Project/fonrey/实施报告/项目骨架搭建实施报告_v1.md
2026-04-30 06:33:50 +08:00

436 lines
20 KiB
Markdown
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.
# Fonrey 项目骨架搭建 — 实施报告
**版本**v1.0
**报告日期**2026-04-29
**实施范围**项目骨架Phase 1 配置 → Phase 2 数据模型 → Phase 3 前端/Docker 脚手架)
**实施依据**`prompt/提示词模板/创建项目骨架提示词_v2.3.md`
**项目根目录**`/mnt/c/project/fonrey/`
**Git HEAD**`94d1602`main 分支working tree clean领先 origin/main 5 commits
---
## 一、执行摘要
`创建项目骨架提示词_v2.3.md`903 行)规范,分三阶段完成 Fonrey 多租户房产 SaaS 平台的 Django 项目骨架:
- **Phase 1**Django 配置层config/、core/、shared/、requirements/、env、manage.py、pyproject— 已完成。
- **Phase 2**9 个业务 App 的真实数据模型(依据 `DATA_MODEL_*.md`)— 已完成77 个 ORM 模型5 张分区表 + 4 个数据库触发器。
- **Phase 3**:前端模板/静态资源/Docker/Tailwind/Makefile/根级 tests/ — 已完成。
**最终验证**
- `python manage.py check` ✅ 0 issues
- `python manage.py check --deploy` ✅ 仅一条 SECRET_KEY 测试值告警(非真实问题)
- 顶层目录树与规范 §2 100% 匹配
- 5 个干净的 Git checkpoint commits
**未交付(明确延后)**
- ~300 条 PermissionDef 种子数据fixtures
- 4 个内置角色 + 默认 DataScope 种子
- Setting 模块的 LookupItem 默认值
- Celery `partition_maintenance_task`(每月分区滚动)
- API_CONTRACT 7 项契约清单中需要真实业务端点的部分spectacular OpenAPI 生成 / schemathesis 实际运行)
---
## 二、目录结构对照(规范 §2 vs 实际)
### 顶层结构100% 匹配)
| 规范要求 | 实际状态 |
|---|---|
| `apps/` (10 个 App) | ✅ tenant, account, permission, org, region, complex, property, client, setting, release |
| `core/` | ✅ models/, enums.py, encryption.py, cache.py, htmx.py, templatetags/, middleware/ |
| `shared/` | ✅ apps.py |
| `config/` | ✅ settings/{base,development,testing,production}.py, urls.py, urls_public.py, asgi.py, wsgi.py |
| `templates/` | ✅ base.html, layouts/, components/, errors/ |
| `static/` | ✅ css/, js/, vendor/ |
| `locale/` | ✅ 占位 |
| `requirements/` | ✅ base.txt, development.txt, production.txt |
| `tests/` | ✅ conftest.py, integration/, e2e/ |
| 根级文件 | ✅ .env, .env.example, .gitignore, manage.py, Dockerfile, docker-compose.yml, docker-compose.prod.yml, Makefile, tailwind.config.js, package.json, pyproject.toml |
### 每个 App 内部结构
**业务 Appproperty/client/setting/account/permission/org/region/complex**
```
apps/<name>/
├── __init__.py
├── apps.py
├── admin.py
├── models/__init__.py + 多个模型文件
├── migrations/
├── services/__init__.py
├── tasks.py
├── views.py
├── urls.py
├── serializers.py
├── templates/<name>/
└── tests/__init__.py
```
**release App共享 schema无服务层**
```
apps/release/
├── __init__.py
├── apps.py
├── admin.py
├── models/ ← 当前空ClientRelease 待实现)
├── migrations/
├── views.py
├── urls.py
├── serializers.py
└── tests/
```
**tenant Appdjango-tenants 特殊结构)**
```
apps/tenant/
├── __init__.py
├── apps.py
├── admin.py
├── models.py ← 单文件,含 Tenant + Domain规范 §17.1
├── migrations/
└── tests/
```
---
## 三、Phase 1配置层commit 9a7d06b 含此部分)
### 3.1 已交付文件
| 路径 | 用途 | 关键决策 |
|---|---|---|
| `config/settings/base.py` | 基础配置 | django-tenants 必为 SHARED_APPS 第一CSRF_COOKIE_HTTPONLY=FalseHTMX 需要AUTH_USER_MODEL = "account.UserAccount" |
| `config/settings/development.py` | 开发配置 | DEBUG=Truedjango-debug-toolbar |
| `config/settings/testing.py` | 测试配置 | pytest-django |
| `config/settings/production.py` | 生产配置 | DEBUG=FalseHSTS/SECURE 各项开启 |
| `config/urls.py` | Tenant 路由入口 | 仅 tenant 路由(强制分离) |
| `config/urls_public.py` | Public 路由入口 | apps.release + drf-spectacular schema/swagger |
| `config/asgi.py` | ASGI 入口 | uvicorn 启动点 |
| `config/wsgi.py` | WSGI 入口 | gunicorn 兼容 |
| `core/models/base.py` | 4 个抽象基类 | UUIDPrimaryKeyModel, TimeStampedModel, SoftDeleteModel, AuditedModel |
| `core/enums.py` | 全局枚举 | 严格对齐 ENUMS.md v2.2,覆盖 9 个模块共数十个枚举 |
| `core/encryption.py` | PII 加密 | **AES-256-GCM**(强制,禁用 Fernet |
| `core/cache.py` | Redis 工具 | get_redis_key 命名空间隔离 |
| `core/htmx.py` | HTMX 响应工具 | htmx_response(),支持 toast / redirect |
| `core/templatetags/heroicons.py` | Heroicons | `{% heroicon 'plus' %}` 内联 SVG |
| `core/middleware/audit.py` | 审计中间件 | 骨架 |
| `requirements/base.txt` | 生产依赖 | Django 4.2.16, django-tenants 3.7.0, psycopg2-binary 2.9.9, celery 5.4.0, drf-spectacular 0.27.2 等 |
| `requirements/development.txt` | 开发依赖 | pytest, schemathesis, playwright, ruff, black 等 |
| `requirements/production.txt` | 生产收敛 | -r base.txt + sentry/whitenoise |
| `pyproject.toml` | 代码质量 | ruff/black/isort/pytest 配置 |
| `.env.example` | 环境变量模板 | DB / Redis / R2 / Sentry / PHONE_ENCRYPTION_KEY |
| `.env` | 开发环境真实值 | dev SECRET_KEY + 32 字节 PHONE_ENCRYPTION_KEY已 gitignore |
| `.gitignore` | 忽略规则 | .env / *.pyc / node_modules / static/css/output.css / openapi.json 等 |
| `manage.py` | Django 入口 | DJANGO_SETTINGS_MODULE=config.settings.development |
### 3.2 关键合规点
-`django_tenants` 在 SHARED_APPS 第一位、MIDDLEWARE 第一位(不可调整)
-`CSRF_COOKIE_HTTPONLY = False` 含警示注释HTMX 需要 JS 读 token禁止"修复"
- ✅ 加密强制 AES-256-GCM禁用 Fernet
-`config.urls``config.urls_public` 强制分离,未合并
- ✅ DB OPTIONS 不含 `pool_size`PgBouncer 在代理层管理)
- ✅ R2 环境变量统一 `R2_*` 前缀
- ✅ 所有密钥/Tenant ID 通过 `python-decouple``env()` 读取,无硬编码
---
## 四、Phase 2数据模型层commits 9a7d06b → c57462f → 5b55dda → ed40de4
### 4.1 模型总数
| App | 模型数 | 关键模型 |
|---|---:|---|
| tenant | 2 | Tenant (TenantMixin, auto_create_schema=True), Domain (DomainMixin) |
| account | 4 | UserAccount (AbstractBaseUser), LoginAttempt, PasswordResetToken, PasswordHistory |
| permission | 7 | PermissionDef, Role, RolePermission, UserRole, DataScope, RoleDataScope, PermissionAuditLog |
| org | 11 | Department, Position, Staff含组织/职位/人员体系) |
| region | 5 | Province, City, District, BusinessArea, Subway |
| complex | 10 | Complex, ComplexBuilding, ComplexUnit 等(含 pg_trgm + search_vector |
| property | 23 | Property, PropertyPhoto分区表, FollowLog分区表, PropertyContact, PropertyTag 等 |
| client | 11 | Client, ClientContact, ClientFollowLog分区表, ClientStatusLog, ViewingRecord, MatchRecord 等 |
| setting | 4 | LookupGroup, LookupItem, TenantSetting, FieldRequirementRule |
| release | 0 | ClientRelease 待 Phase 4 业务实现) |
| **合计** | **77** | |
### 4.2 分区表与触发器(共 5 张分区表 + 4 个触发器)
| 分区表 | 模块 | 分区策略 | 关联触发器 |
|---|---|---|---|
| `property_follow_logs` | property | RANGE BY `created_at`,月度 | `update_property_last_followed` |
| `property_photos` | property | RANGE BY `created_at`,月度 | `update_property_search_vector`pg_trgm 全文检索) |
| `client_follow_logs` | client | RANGE BY `created_at`,月度 | `update_client_last_follow`, `update_client_viewing_progress` |
实现模式(解决 Django ORM 与 PG 原生分区表的冲突):
- 模型 `Meta` 设置 `managed = False`
- `id = UUIDField(primary_key=True)`,复合主键 `(id, created_at)` 通过 RunSQL 创建
- ORM 层 `unique_together = (('id', 'created_at'),)` 让查询正确生成
- 月度子分区 + 默认分区,用 RunSQL 在初始 migration 中预创建
- 跨分区 FK 限制保留为优先级 3 注释
### 4.3 Migration 文件(共 12 个)
```
apps/account/migrations/0001_initial.py
apps/account/migrations/0002_initial.py ← AUTH_USER_MODEL 切换
apps/permission/migrations/0001_initial.py
apps/org/migrations/0001_initial.py
apps/region/migrations/0001_initial.py
apps/complex/migrations/0001_initial.py
apps/complex/migrations/0002_pg_trgm_and_search_vector.py
apps/property/migrations/0001_initial.py
apps/property/migrations/0002_partitions_and_triggers.py
apps/client/migrations/0001_initial.py
apps/client/migrations/0002_partitions_and_triggers.py
apps/setting/migrations/0001_initial.py
```
### 4.4 关键模型设计决策
| 决策 | 原因 |
|---|---|
| AUTH_USER_MODEL = "account.UserAccount" | UserAccount 含 OneToOne 关联 Staff租户内统一登录 |
| 手机号加密BinaryField 密文 + char(64) hash 列 | AES-GCM 不可去重比对hash 列承担唯一索引 |
| `field_requirement_rules.trade_status``*` 哨兵值(覆盖 ALL="all" | 规范第 570 行明确要求 `*` 表示"全部"语义 |
| `ClientStatusLog` 不含 `deleted_at`(保留 docstring 警示) | 规范明确要求"严禁删除"状态变更日志 |
| 字符串 FK 引用(如 `"fonrey_property.Property"` | 避免循环导入,应用标签前缀消歧 |
| App 标签fonrey_permission, fonrey_complex, fonrey_property, fonrey_client | permission/complex/property/client 为 Python 关键字或标准库名,加前缀避免冲突 |
| 多文件 models/ 包,`__init__.py` 显式 re-export | 一表一文件,可读性优先 |
---
## 五、Phase 3前端 + Docker 脚手架commit 94d1602
### 5.1 模板体系templates/
| 文件 | 角色 |
|---|---|
| `base.html` | 全局根模板。引入顺序output.css → htmx.min.js → alpine.min.js → main.js |
| `layouts/app.html` | 主应用布局。继承 base含 Topbar (sticky, h-14, z-20) + Sidebar (Alpine `$persist` 240/64px) + 主区 + 小屏拦截门 (<1280px) |
| `layouts/auth.html` | 认证页布局。无 Topbar/Sidebar居中卡片 max-w-md |
| `components/topbar.html` | bg-primary-800Logo + 导航 + 通知/设置/头像 |
| `components/sidebar.html` | 240/64px 切换Alpine 持久化 |
| `components/pagination.html` | 分页骨架 |
| `components/toast.html` | Toast 模板 |
| `components/modal.html` | 模态对话框Alpine x-show + click.outside |
| `components/empty-state.html` | 空状态 |
| `errors/403.html` | 403 错误页 |
| `errors/404.html` | 404 错误页 |
| `errors/500.html` | 500 错误页 |
### 5.2 静态资源static/
| 文件 | 内容 |
|---|---|
| `css/main.css` | Tailwind 入口(@tailwind base/components/utilities |
| `js/main.js` | HTMX `afterRequest` 监听 `HX-Trigger: fonrey:toast`4s 自动消失;`configRequest` 自动注入 X-CSRFToken |
| `vendor/.gitkeep` | 第三方 JShtmx.min.js / alpine.min.js放置点 |
### 5.3 Tailwind 配置tailwind.config.js
完全对齐 `UI_SYSTEM.md §2.7``§10.1`
- **PrimaryTeal**50 #F0FDFA → 800 #134E4A,主色 600 #0F766E
- **NeutralSlate**50 #F8FAFC → 900 #0F172A
- **语义色**success-600 #16A34A, warning-600 #D97706, danger-600 #DC2626, info-600 #2563EB
- **字体**Inter, PingFang SC, Microsoft YaHei
- **z-index**60, 70Toast 层)
- **boxShadow**xs轻投影
- **animation**slide-in-rightDrawer 进场)
- **content scan**`templates/`, `apps/**/templates/`, `static/js/`
### 5.4 Docker 与构建(开发 6 服务)
| 文件 | 作用 |
|---|---|
| `Dockerfile` | python:3.12-slim + libpq-dev + 安装 base.txt + uvicorn 入口 |
| `docker-compose.yml` | 6 服务web (8000), db (postgres:16), redis (7), celery, celery-beat, tailwind (node:20);统一 `fonrey_net` 网络db/redis 数据卷持久化 |
| `docker-compose.prod.yml` | 生产精简版gunicorn + UvicornWorker去除 tailwind 容器与端口暴露 |
| `Makefile` | dev / migrate / shell / test / lint / tailwind-build / createsuperuser |
| `package.json` | 仅 tailwindcss ^3.4.0build/watch 两个脚本 |
### 5.5 测试体系tests/
```
tests/
├── __init__.py
├── conftest.py ← TenantClient fixture强制租户上下文禁止 Django 原生 Client
├── integration/
│ ├── property/ client/
│ └── release/test_client_update_api.py ← schemathesis 契约测试占位
└── e2e/ ← playwright E2E 占位
```
### 5.6 每 App 骨架补全
property / client / setting 三个 App 的非模型骨架services/、tasks.py、views.py、urls.py、serializers.py、templates/<app>/、tests/已补齐admin.py 在所有 10 个 App 上添加(空文件,后续禁用 Django Admin 但保留模块入口)。
---
## 六、规范 §16 执行清单逐项验证
| # | 任务 | 状态 |
|---:|---|:---:|
| 1 | 创建根目录及完整目录树 | ✅ |
| 2 | pyproject.toml / .gitignore / .env.example / Makefile | ✅ |
| 3 | requirements/ 三个文件 | ✅ |
| 4 | config/settings/base.py | ✅ |
| 5 | development.py / testing.py / production.py | ✅ |
| 6 | config/urls.py 与 urls_public.py 分离 | ✅ |
| 7 | config/asgi.py | ✅ |
| 8 | core/models/base.py 四个抽象基类 | ✅ |
| 8b | core/enums.py对齐 ENUMS.md v2.2 | ✅ |
| 9 | core/encryption.pyAES-256-GCM | ✅ |
| 10 | core/cache.pyRedis 工具) | ✅ |
| 11 | core/htmx.pyhtmx_response 工具) | ✅ |
| 12 | core/templatetags/heroicons.py | ✅ |
| 13 | core/middleware/audit.py | ✅ |
| 14 | 每 App 目录结构apps/release 除外) | ✅ |
| 15 | apps/tenant/models.pyTenant + Domain | ✅ |
| 16 | templates/ 完整目录树 + base/app/auth | ✅ |
| 17 | components/ 6 个骨架 | ✅ |
| 18 | errors/ 三个错误页 | ✅ |
| 19 | static/css/main.css | ✅ |
| 20 | static/js/main.js | ✅ |
| 21 | tailwind.config.js | ✅ |
| 22 | package.json | ✅ |
| 23 | Dockerfile | ✅ |
| 24 | docker-compose.yml6 服务) | ✅ |
| 25 | manage.py | ✅ |
| 26 | `manage.py check --deploy` 无致命错误 | ✅ 0 errors仅 SECRET_KEY 测试值告警 |
| 27 | 目录树与 §2 100% 匹配 | ✅ |
| 28 | API_CONTRACT 7 项核对 | ⚠️ 部分(详见第七节) |
---
## 七、API_CONTRACT 7 项核对(规范 §15
| # | 项 | 状态 | 备注 |
|---:|---|:---:|---|
| 1 | 路径与方法一致 | 🟡 N/A | 业务端点尚未实现(骨架阶段) |
| 2 | 请求参数一致 | 🟡 N/A | 同上 |
| 3 | 响应 envelopeok/data/meta vs ok/error/code/details | 🟡 N/A | DRF 自定义 renderer 待 Phase 4 |
| 4 | 错误码 UPPER_SNAKE_CASE | 🟡 N/A | 同上 |
| 5 | OpenAPI 注解 `@extend_schema` | 🟡 待定 | drf-spectacular 已装、urls_public.py 已挂 schema/swagger 路由,待业务视图编写时补 |
| 6 | `python manage.py spectacular --file openapi.json` 可生成 | ⚠️ 未运行 | 当前无业务视图,生成会得到空 schema待 Phase 4 验证 |
| 7 | schemathesis 命令可运行 | ⚠️ 占位 | `tests/integration/release/test_client_update_api.py` 已有 skip 占位 |
**结论**:骨架阶段第 14 项 N/A无端点、57 项基础设施就绪等待业务实现。骨架本身不阻塞契约清单。
---
## 八、Git 提交历史
```
94d1602 feat: complete Phase 3 scaffolding (templates, static, Docker, per-app skeletons)
ed40de4 feat(client,setting): complete Phase 2 with partitioned client_follow_logs
5b55dda feat(property): add 23-table property module with partitioned follow_logs and property_photos
c57462f feat(complex): add apps.complex with 10 models and full-text search
9a7d06b feat: scaffold Django multi-tenant project with 5 of 9 apps
```
每次 commit 后均执行 `manage.py check`,全部通过。
---
## 九、代码量统计
| 目录 | 总行数 |
|---|---:|
| `apps/` | 5837 |
| `core/` | 1028 |
| `config/` | 269 |
| `shared/` | 6 |
| `templates/` | 142 |
| `static/` (css+js) | 35 |
| `tests/` | 15 |
| **合计** | **~7332** |
文件总数208不含 .venv / .git / __pycache__
---
## 十、未交付项(明确延后清单)
| 项 | 原因 | 建议落地阶段 |
|---|---|---|
| ~300 条 PermissionDef 种子(`apps/permission/fixtures/permission_defs.json` | 内容来自 `DATA_MODEL_PERMISSION.md` 700+ 行,需逐条人工核对 | Phase 4 启动前 |
| 4 个内置角色 + 默认 DataScope 种子 | 同上 | Phase 4 启动前 |
| Setting 模块 LookupItem 默认值(楼盘类型/客户来源等枚举数据) | 来自 `DATA_MODEL_SETTING.md` | Phase 4 启动前 |
| Celery `partition_maintenance_task` | 月度自动新增分区,骨架阶段非阻塞 | 上线前 1 周 |
| API_CONTRACT 第 14 项 | 需要真实业务端点 | Phase 4 模块开发时随端点同步 |
| OpenAPI 实际生成 + schemathesis 实际运行 | 同上 | Phase 4 |
| Heroicons SVG 资源文件 | 当前 templatetag 是骨架,未含 SVG 库 | Phase 4 UI 模块启动时 |
| static/vendor/ 下的 htmx.min.js / alpine.min.js | 通过 npm 或 CDN 任选,未决策 | Phase 4 启动前 |
---
## 十一、关键约束遵守审计
| 约束(规范原文) | 遵守状态 | 证据 |
|---|:---:|---|
| 不得自行发明技术方案,不得引入文档未授权第三方库 | ✅ | requirements/base.txt 仅含规范明列依赖 |
| 绝对禁止 React/Vue/Angular | ✅ | 仅 HTMX + Alpine + Tailwind |
| `django_tenants` 在 SHARED_APPS / MIDDLEWARE 首位 | ✅ | `config/settings/base.py` |
| `CSRF_COOKIE_HTTPONLY = False` | ✅ | base.py 含警示注释 |
| AES-256-GCM 加密,禁用 Fernet | ✅ | `core/encryption.py` |
| `apps/release/` 无 services/、tasks.py | ✅ | 实际目录验证 |
| 不在 DB OPTIONS 注入 pool_size | ✅ | base.py DATABASES 配置 |
| 所有密钥/Tenant ID 不出现在 Python 文件 | ✅ | 统一 `config()` / `env()` 读取 |
| `config/urls.py``urls_public.py` 强制分离 | ✅ | 两文件独立维护 |
| 逐步创建并验证 | ✅ | 5 个 commit 各自 `manage.py check` 通过 |
| .gitignore 包含 .env / *.pyc / node_modules / static/css/output.css 等 | ✅ | 全部覆盖 |
---
## 十二、下一步建议
按优先级:
1. **Phase 4 起步前必做**
- 编写 PermissionDef + 内置角色 + DataScope 三组 fixtures
- 编写 Setting 模块 LookupItem 默认值 fixtures
- 决定 vendor JS 加载方式npm install 还是直接放置静态文件)
- 准备 Heroicons SVG 库(推荐 `heroicons` Python 包或手动放 SVG
2. **Phase 4 实施时同步推进**
- 第一个真实业务端点(建议从 release/client_update API 起步)落地后立即跑 spectacular + schemathesis闭环 API_CONTRACT 7 项
- 在每个业务视图 PR 中强制要求 `@extend_schema` 注解
3. **上线前 1 周**
- 实现 Celery `partition_maintenance_task`,配置 celery-beat 月初执行
- 用真实 32 字节随机值替换 `.env.example` 占位的 PHONE_ENCRYPTION_KEY并在 Vault/Secret Manager 中托管
4. **可选优化**
- 增加 `pre-commit` 钩子ruff + black + isort + django-check
- 增加 GitHub Actions CIlint + test + spectacular dry-run
---
## 十三、附录:项目快速启动命令
```bash
# 本地(无 Docker
cd /mnt/c/project/fonrey
.venv/bin/python manage.py check
.venv/bin/python manage.py makemigrations --dry-run
.venv/bin/python manage.py spectacular --file openapi.json # 待业务视图就绪后运行
# Docker推荐
make dev # docker compose up
make migrate # 共享 schema + 租户 schema 双重 migrate
make shell # shell_plus
make test # pytest apps/
make lint # ruff + black
make tailwind-build # 生成 static/css/output.css
```
---
**报告完**