文档修改
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
- 架构师方法论:`Project/fonrey/prompt/engineering-backend-architect.md`
|
||||
- 现有技术栈草案:`Project/fonrey/TECH_STACK/TECH_STACK.md`
|
||||
- 现有数据模型草案:`Project/fonrey/DATA_MODEL/DATA_MODEL.md`
|
||||
- 现有UI总体设计方案:`Project/fonrey/UI_SYSTEM/UI_SYSTEM.md`
|
||||
|
||||
---
|
||||
|
||||
|
||||
603
Project/fonrey/prompt/提示词模板/创建项目骨架提示词_v1.md
Normal file
603
Project/fonrey/prompt/提示词模板/创建项目骨架提示词_v1.md
Normal file
@@ -0,0 +1,603 @@
|
||||
# Fonrey 项目骨架搭建 — 工程执行提示词
|
||||
## 你的角色与约束
|
||||
你是一名资深 Django 后端工程师。你的任务是**严格按照规范**搭建 Fonrey 项目骨架,不得自行发明技术方案,不得引入文档未授权的第三方库。每一步操作后必须验证结果。
|
||||
**项目工作目录**:`/mnt/c/Project/`(在此目录下创建 `fonrey/` 子目录)
|
||||
**执行方式**:逐步创建,每创建一个文件/目录后立即验证,遇到冲突停下来询问而不是自行决策。
|
||||
---
|
||||
## 一、技术栈约束(必读,不得违反)
|
||||
| 层级 | 技术 | 版本约束 |
|
||||
|------|------|----------|
|
||||
| Backend | Django | 4.2 LTS(ASGI 模式) |
|
||||
| Multi-tenant | django-tenants | latest stable |
|
||||
| Database | PostgreSQL | 16 |
|
||||
| Cache | Redis | latest stable |
|
||||
| Tasks | Celery + Celery Beat | latest stable |
|
||||
| Storage | django-storages + boto3 | Cloudflare R2(S3 兼容) |
|
||||
| Frontend | HTMX + Alpine.js + Tailwind CSS | HTMX 2.x, Alpine 3.x, Tailwind 3.x |
|
||||
| Icons | Heroicons v2 | inline SVG via templatetag |
|
||||
| Server | Gunicorn + Uvicorn workers | ASGI |
|
||||
| Container | Docker + Docker Compose | — |
|
||||
| Monitoring | Sentry SDK | — |
|
||||
**绝对禁止**:React / Vue / Angular;任何 JS 框架;`nodeIntegration: true`;硬编码密钥/ID;跨租户 SQL 查询。
|
||||
---
|
||||
## 二、目录结构(严格按此创建,不得增减顶层结构)
|
||||
```
|
||||
fonrey/
|
||||
├── apps/
|
||||
│ ├── tenant/ # django-tenants 配置(in SHARED_APPS)
|
||||
│ ├── account/ # 登录认证(in TENANT_APPS)
|
||||
│ ├── permission/ # 权限管理(in TENANT_APPS)
|
||||
│ ├── org/ # 组织人事(in TENANT_APPS)
|
||||
│ ├── region/ # 区域管理(in TENANT_APPS)
|
||||
│ ├── complex/ # 楼盘管理(in TENANT_APPS)
|
||||
│ ├── property/ # 房源核心(in TENANT_APPS)
|
||||
│ ├── client/ # 客源管理(in TENANT_APPS)
|
||||
│ ├── setting/ # 系统设置(in TENANT_APPS)
|
||||
│ └── release/ # 客户端发布管理(in SHARED_APPS)
|
||||
├── core/
|
||||
│ ├── __init__.py
|
||||
│ ├── models/
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── base.py # 抽象基类(见第四节规范)
|
||||
│ ├── encryption.py # PII 加密(AES-256-GCM)
|
||||
│ ├── cache.py # Redis 工具
|
||||
│ ├── templatetags/
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── heroicons.py # {% heroicon 'plus' %} templatetag
|
||||
│ └── middleware/
|
||||
│ ├── __init__.py
|
||||
│ └── audit.py # 审计日志中间件骨架
|
||||
├── shared/
|
||||
│ ├── __init__.py
|
||||
│ └── apps.py # 公共 Schema App 配置
|
||||
├── config/
|
||||
│ ├── __init__.py
|
||||
│ ├── settings/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── base.py # 基础配置
|
||||
│ │ ├── development.py
|
||||
│ │ └── production.py
|
||||
│ ├── urls.py
|
||||
│ ├── asgi.py # ASGI 入口
|
||||
│ └── wsgi.py
|
||||
├── templates/
|
||||
│ ├── base.html # 全局基础模板
|
||||
│ ├── layouts/
|
||||
│ │ ├── auth.html # 认证页独立布局
|
||||
│ │ └── app.html # 主应用布局(含 Topbar + Sidebar)
|
||||
│ ├── components/ # 可复用组件片段
|
||||
│ │ ├── topbar.html
|
||||
│ │ ├── sidebar.html
|
||||
│ │ ├── pagination.html
|
||||
│ │ ├── toast.html
|
||||
│ │ ├── modal.html
|
||||
│ │ └── empty-state.html
|
||||
│ └── errors/
|
||||
│ ├── 403.html
|
||||
│ ├── 404.html
|
||||
│ └── 500.html
|
||||
├── static/
|
||||
│ ├── css/
|
||||
│ │ └── main.css # Tailwind 入口(@tailwind directives)
|
||||
│ ├── js/
|
||||
│ │ └── main.js # Alpine.js 初始化 + 全局 HTMX 事件
|
||||
│ └── vendor/ # 第三方 JS/CSS(htmx.min.js, alpine.min.js 等)
|
||||
├── locale/ # 预留国际化(v2),当前仅中文
|
||||
├── .env.example
|
||||
├── .env # 不入 git
|
||||
├── .gitignore
|
||||
├── manage.py
|
||||
├── requirements/
|
||||
│ ├── base.txt
|
||||
│ ├── development.txt
|
||||
│ └── production.txt
|
||||
├── docker-compose.yml
|
||||
├── docker-compose.prod.yml
|
||||
├── Dockerfile
|
||||
├── Makefile # 常用命令快捷方式
|
||||
├── tailwind.config.js
|
||||
├── package.json # 仅用于 Tailwind 构建,不含业务 JS
|
||||
└── pyproject.toml # ruff + black + isort 配置
|
||||
```
|
||||
每个 `apps/<name>/` 内部结构如下(以 `property` 为典型,其他 App 骨架相同):
|
||||
```
|
||||
apps/property/
|
||||
├── __init__.py
|
||||
├── apps.py
|
||||
├── admin.py
|
||||
├── models/
|
||||
│ ├── __init__.py # 统一 re-export
|
||||
│ └── .gitkeep # 骨架阶段留空,后续一表一文件
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── .gitkeep
|
||||
├── tasks.py # Celery 任务骨架
|
||||
├── views.py # HTMX/JSON 视图骨架
|
||||
├── urls.py
|
||||
├── templates/
|
||||
│ └── property/ # App 级模板(可覆盖全局同名模板)
|
||||
└── tests/
|
||||
├── __init__.py
|
||||
└── .gitkeep
|
||||
```
|
||||
---
|
||||
## 三、Django 配置规范
|
||||
### 3.1 INSTALLED_APPS 分区
|
||||
```python
|
||||
# config/settings/base.py
|
||||
SHARED_APPS = [
|
||||
"django_tenants", # 必须第一位
|
||||
"apps.tenant", # Tenant / Domain 模型
|
||||
"apps.release", # ClientRelease 模型
|
||||
"shared", # 公共 Schema App
|
||||
# Django 内置
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
# 第三方(shared)
|
||||
"django_celery_beat",
|
||||
"django_celery_results",
|
||||
]
|
||||
TENANT_APPS = [
|
||||
"apps.account",
|
||||
"apps.permission",
|
||||
"apps.org",
|
||||
"apps.region",
|
||||
"apps.complex",
|
||||
"apps.property",
|
||||
"apps.client",
|
||||
"apps.setting",
|
||||
"core",
|
||||
]
|
||||
INSTALLED_APPS = list(SHARED_APPS) + list(TENANT_APPS)
|
||||
```
|
||||
### 3.2 核心配置项(base.py 必须包含以下所有项)
|
||||
```python
|
||||
# 多租户
|
||||
TENANT_MODEL = "tenant.Tenant"
|
||||
TENANT_DOMAIN_MODEL = "tenant.Domain"
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.UUIDField" # 全局 UUID PK
|
||||
# 数据库(从环境变量读取)
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django_tenants.postgresql_backend",
|
||||
"NAME": env("DB_NAME"),
|
||||
"USER": env("DB_USER"),
|
||||
"PASSWORD": env("DB_PASSWORD"),
|
||||
"HOST": env("DB_HOST", default="localhost"),
|
||||
"PORT": env("DB_PORT", default="5432"),
|
||||
"CONN_MAX_AGE": 60,
|
||||
"OPTIONS": {"pool_size": 10}, # PgBouncer 协同
|
||||
}
|
||||
}
|
||||
DATABASE_ROUTERS = ["django_tenants.routers.TenantSyncRouter"]
|
||||
# Redis(Cache + Session + Celery Broker)
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": env("REDIS_URL", default="redis://127.0.0.1:6379/0"),
|
||||
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
|
||||
"KEY_PREFIX": "fonrey",
|
||||
}
|
||||
}
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
||||
SESSION_CACHE_ALIAS = "default"
|
||||
# Celery
|
||||
CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="redis://127.0.0.1:6379/1")
|
||||
CELERY_RESULT_BACKEND = "django-db"
|
||||
CELERY_TASK_ALWAYS_EAGER = False
|
||||
CELERY_TASK_TIME_LIMIT = 300
|
||||
CELERY_TASK_SOFT_TIME_LIMIT = 270
|
||||
# 存储(Cloudflare R2)
|
||||
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
AWS_S3_ENDPOINT_URL = env("R2_ENDPOINT_URL")
|
||||
AWS_ACCESS_KEY_ID = env("R2_ACCESS_KEY_ID")
|
||||
AWS_SECRET_ACCESS_KEY = env("R2_SECRET_ACCESS_KEY")
|
||||
AWS_STORAGE_BUCKET_NAME = env("R2_BUCKET_NAME", default="media")
|
||||
AWS_S3_CUSTOM_DOMAIN = env("R2_CUSTOM_DOMAIN", default=None)
|
||||
AWS_DEFAULT_ACL = "private"
|
||||
# ASGI
|
||||
ASGI_APPLICATION = "config.asgi.application"
|
||||
# Sentry(production 环境激活)
|
||||
# 见 production.py
|
||||
# 安全
|
||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = "Lax"
|
||||
CSRF_COOKIE_HTTPONLY = False # HTMX 需要读取
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
# 模板
|
||||
TEMPLATES = [{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [BASE_DIR / "templates"],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
]
|
||||
},
|
||||
}]
|
||||
# HTMX
|
||||
HTMX_GLOBAL_CSRF = True # 全局 CSRF 注入
|
||||
# 日志(骨架,production 扩展)
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {"console": {"class": "logging.StreamHandler"}},
|
||||
"root": {"handlers": ["console"], "level": "INFO"},
|
||||
}
|
||||
```
|
||||
### 3.3 中间件顺序(严格按此,不得调整)
|
||||
```python
|
||||
MIDDLEWARE = [
|
||||
"django_tenants.middleware.main.TenantMainMiddleware", # 必须第一位
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"whitenoise.middleware.WhiteNoiseMiddleware", # 静态文件
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"core.middleware.audit.AuditMiddleware", # 自定义审计(骨架)
|
||||
]
|
||||
```
|
||||
---
|
||||
## 四、核心抽象基类(core/models/base.py)
|
||||
严格按以下规范实现,不得修改字段名、类型、顺序:
|
||||
```python
|
||||
import uuid
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
class UUIDPrimaryKeyModel(models.Model):
|
||||
"""所有业务模型的根基类:UUID v4 主键"""
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
class Meta:
|
||||
abstract = True
|
||||
class TimeStampedModel(UUIDPrimaryKeyModel):
|
||||
"""追加创建/更新时间(TIMESTAMPTZ)"""
|
||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ["-created_at"]
|
||||
class SoftDeleteModel(TimeStampedModel):
|
||||
"""软删除:deleted_at=NULL 表示未删除"""
|
||||
deleted_at = models.DateTimeField(null=True, blank=True, db_index=True)
|
||||
objects = ActiveManager() # 默认过滤已删除
|
||||
all_objects = models.Manager() # 包含已删除记录
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
self.deleted_at = timezone.now()
|
||||
self.save(update_fields=["deleted_at"])
|
||||
def hard_delete(self):
|
||||
super().delete()
|
||||
def restore(self):
|
||||
self.deleted_at = None
|
||||
self.save(update_fields=["deleted_at"])
|
||||
@property
|
||||
def is_deleted(self):
|
||||
return self.deleted_at is not None
|
||||
class Meta:
|
||||
abstract = True
|
||||
class AuditedModel(SoftDeleteModel):
|
||||
"""审计字段:操作人(FK to Staff,允许 NULL 表示系统操作)"""
|
||||
created_by = models.ForeignKey(
|
||||
"org.Staff",
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="%(app_label)s_%(class)s_created",
|
||||
db_index=True,
|
||||
)
|
||||
updated_by = models.ForeignKey(
|
||||
"org.Staff",
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="%(app_label)s_%(class)s_updated",
|
||||
)
|
||||
class Meta:
|
||||
abstract = True
|
||||
class ActiveManager(models.Manager):
|
||||
"""默认只返回未软删除的记录"""
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(deleted_at__isnull=True)
|
||||
```
|
||||
---
|
||||
## 五、PII 加密(core/encryption.py)
|
||||
骨架实现,接口固定(后续补充实现体),确保接口签名正确:
|
||||
```python
|
||||
from cryptography.fernet import Fernet
|
||||
import hashlib
|
||||
import base64
|
||||
from django.conf import settings
|
||||
class PhoneEncryption:
|
||||
"""
|
||||
手机号 AES-256-GCM 加密存储 + SHA-256 哈希索引
|
||||
存储字段:phone_encrypted(加密密文)+ phone_hash(哈希,用于精确查询)
|
||||
显示:脱敏格式 138****1234
|
||||
"""
|
||||
@staticmethod
|
||||
def encrypt(phone: str) -> str:
|
||||
"""加密手机号,返回 base64 密文"""
|
||||
... # TODO: 实现
|
||||
@staticmethod
|
||||
def decrypt(ciphertext: str) -> str:
|
||||
"""解密返回明文"""
|
||||
... # TODO: 实现
|
||||
@staticmethod
|
||||
def hash(phone: str) -> str:
|
||||
"""返回 SHA-256 哈希(用于 DB 索引查询)"""
|
||||
... # TODO: 实现
|
||||
@staticmethod
|
||||
def mask(phone: str) -> str:
|
||||
"""返回脱敏格式:138****1234"""
|
||||
if not phone or len(phone) < 7:
|
||||
return "***"
|
||||
return phone[:3] + "****" + phone[-4:]
|
||||
```
|
||||
---
|
||||
## 六、Heroicons Templatetag(core/templatetags/heroicons.py)
|
||||
```python
|
||||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
import os
|
||||
register = template.Library()
|
||||
ICONS_PATH = os.path.join(os.path.dirname(__file__), "..", "static", "icons")
|
||||
@register.simple_tag
|
||||
def heroicon(name: str, size: str = "24", style: str = "outline", css_class: str = "") -> str:
|
||||
"""
|
||||
用法: {% heroicon 'plus' %}
|
||||
{% heroicon 'trash' size='20' style='solid' css_class='text-danger-600' %}
|
||||
"""
|
||||
# 骨架:实际从 heroicons vendor 文件读取 SVG
|
||||
# size 可选: 12, 16, 20, 24
|
||||
# style 可选: outline, solid, mini
|
||||
css = f'class="w-{size//4 if isinstance(size, int) else size} h-{size//4 if isinstance(size, int) else size} {css_class}"'
|
||||
return mark_safe(f'<!-- heroicon:{style}/{name} -->') # TODO: 替换为实际 SVG
|
||||
```
|
||||
---
|
||||
## 七、模板体系
|
||||
### 7.1 base.html(全局根模板)
|
||||
包含以下 block 定义(骨架,后续填充):
|
||||
- `{% block title %}` — 页面标题
|
||||
- `{% block extra_head %}` — 额外 CSS/meta
|
||||
- `{% block body_class %}` — body class 注入
|
||||
- `{% block content %}` — 页面主内容
|
||||
- `{% block extra_js %}` — 页面级 JS
|
||||
引入资源顺序:
|
||||
1. Tailwind CSS(编译后的 `main.css`)
|
||||
2. Flatpickr CSS(条件加载)
|
||||
3. HTMX `htmx.min.js`
|
||||
4. Alpine.js `alpine.min.js`(defer,必须在 HTMX 之后)
|
||||
5. 全局 `main.js`(初始化 Toast 监听、HTMX 事件、CSP nonce 等)
|
||||
### 7.2 layouts/app.html(主应用布局)
|
||||
继承 `base.html`,包含:
|
||||
- Topbar(`bg-primary-800`,高 56px,sticky top-0 z-20)
|
||||
- 左:Logo 150px 区
|
||||
- 中:8 个主导航 Tab(主页/房源/客源/营销/交易/数据/人事/系统)+ 全局搜索
|
||||
- 右:通知铃 + 设置齿轮 + 头像菜单
|
||||
- Sidebar(固定,展开 240px / 收起 64px,Alpine `$persist` 记忆状态,z-20)
|
||||
- 主内容区(`ml-60` 或 `ml-16`,`px-6 py-4`)
|
||||
- Toast 容器(fixed bottom-right,z-70)
|
||||
- 小屏拦截门(`window.innerWidth < 1280` 时显示全屏提示)
|
||||
### 7.3 layouts/auth.html(认证页布局)
|
||||
独立布局,无 Sidebar/Topbar,居中卡片 `max-w-md`。
|
||||
### 7.4 HTMX Toast 约定
|
||||
后端响应头触发 Toast(所有需要通知用户的操作必须返回此头):
|
||||
```python
|
||||
# 工具函数骨架(core/htmx.py)
|
||||
from django.http import HttpResponse
|
||||
def htmx_response(content="", status=200, toast=None, redirect=None):
|
||||
"""
|
||||
toast: {"type": "success|error|warning|info", "message": "..."}
|
||||
"""
|
||||
response = HttpResponse(content, status=status)
|
||||
if toast:
|
||||
import json
|
||||
response["HX-Trigger"] = json.dumps({"fonrey:toast": toast})
|
||||
if redirect:
|
||||
response["HX-Redirect"] = redirect
|
||||
return response
|
||||
```
|
||||
---
|
||||
## 八、Docker Compose 规范
|
||||
### docker-compose.yml(开发环境)
|
||||
包含以下服务,网络统一使用 `fonrey_net`:
|
||||
| 服务 | 镜像 | 端口 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `web` | 本地 Dockerfile | 8000:8000 | Django ASGI(Uvicorn) |
|
||||
| `db` | postgres:16-alpine | 5432:5432 | PostgreSQL |
|
||||
| `redis` | redis:7-alpine | 6379:6379 | Cache + Broker |
|
||||
| `celery` | 同 web 镜像 | — | `celery -A config worker` |
|
||||
| `celery-beat` | 同 web 镜像 | — | `celery -A config beat` |
|
||||
| `tailwind` | node:20-alpine | — | `npm run watch`(开发热重载) |
|
||||
所有服务通过环境变量从 `.env` 文件读取配置(`env_file: .env`)。
|
||||
`db` 和 `redis` 必须配置 `volumes` 持久化数据。
|
||||
### Dockerfile
|
||||
```dockerfile
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
# 系统依赖(PostgreSQL 客户端、构建工具)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libpq-dev gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY requirements/base.txt requirements/base.txt
|
||||
RUN pip install --no-cache-dir -r requirements/base.txt
|
||||
COPY . .
|
||||
# 收集静态文件(production 阶段)
|
||||
# RUN python manage.py collectstatic --noinput
|
||||
EXPOSE 8000
|
||||
CMD ["uvicorn", "config.asgi:application", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
---
|
||||
## 九、requirements 规范
|
||||
### requirements/base.txt(精确版本锁定)
|
||||
```
|
||||
Django==4.2.16
|
||||
django-tenants==3.7.0
|
||||
psycopg2-binary==2.9.9
|
||||
django-redis==5.4.0
|
||||
celery==5.4.0
|
||||
django-celery-beat==2.7.0
|
||||
django-celery-results==2.5.1
|
||||
django-storages[s3]==1.14.4
|
||||
boto3==1.35.0
|
||||
cryptography==43.0.0
|
||||
whitenoise==6.8.2
|
||||
gunicorn==23.0.0
|
||||
uvicorn[standard]==0.32.0
|
||||
sentry-sdk[django]==2.18.0
|
||||
python-decouple==3.8 # .env 读取
|
||||
Pillow==11.0.0 # 图片处理
|
||||
```
|
||||
### requirements/development.txt
|
||||
```
|
||||
-r base.txt
|
||||
ruff==0.7.0
|
||||
black==24.10.0
|
||||
pytest-django==4.9.0
|
||||
factory-boy==3.3.1
|
||||
django-debug-toolbar==4.4.6
|
||||
```
|
||||
---
|
||||
## 十、Makefile 快捷命令
|
||||
```makefile
|
||||
.PHONY: dev migrate shell createsuperuser test lint
|
||||
dev:
|
||||
docker compose up
|
||||
migrate:
|
||||
docker compose exec web python manage.py migrate_schemas --shared
|
||||
docker compose exec web python manage.py migrate_schemas
|
||||
shell:
|
||||
docker compose exec web python manage.py shell_plus
|
||||
test:
|
||||
docker compose exec web pytest apps/ -v
|
||||
lint:
|
||||
ruff check . && black --check .
|
||||
tailwind-build:
|
||||
npm run build
|
||||
createsuperuser:
|
||||
docker compose exec web python manage.py create_tenant_superuser
|
||||
```
|
||||
---
|
||||
## 十一、.env.example 模板
|
||||
```bash
|
||||
# Django
|
||||
SECRET_KEY=your-secret-key-here
|
||||
DEBUG=True
|
||||
DJANGO_SETTINGS_MODULE=config.settings.development
|
||||
ALLOWED_HOSTS=localhost,127.0.0.1
|
||||
# Database
|
||||
DB_NAME=fonrey
|
||||
DB_USER=fonrey
|
||||
DB_PASSWORD=fonrey
|
||||
DB_HOST=db
|
||||
DB_PORT=5432
|
||||
# Redis
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
CELERY_BROKER_URL=redis://redis:6379/1
|
||||
# Cloudflare R2
|
||||
R2_ENDPOINT_URL=https://<account_id>.r2.cloudflarestorage.com
|
||||
R2_ACCESS_KEY_ID=
|
||||
R2_SECRET_ACCESS_KEY=
|
||||
R2_BUCKET_NAME=media
|
||||
R2_CUSTOM_DOMAIN=
|
||||
# Sentry(production 填写)
|
||||
SENTRY_DSN=
|
||||
# PII 加密密钥(AES-256,生产环境必须替换)
|
||||
PHONE_ENCRYPTION_KEY=
|
||||
```
|
||||
---
|
||||
## 十二、tailwind.config.js(完整规范)
|
||||
严格按照 UI_SYSTEM.md §2.7 和 §10.1 的规范实现,包含:
|
||||
1. **Primary 色(Teal)**:`primary-50` (#F0FDFA) 到 `primary-800` (#134E4A),`primary-600` (#0F766E) 为主色
|
||||
2. **Neutral 色(Slate)**:`neutral-50` (#F8FAFC) 到 `neutral-900` (#0F172A)
|
||||
3. **语义色**:`success-600` (#16A34A), `warning-600` (#D97706), `danger-600` (#DC2626), `info-600` (#2563EB)
|
||||
4. **字体栈**:Inter, PingFang SC, Microsoft YaHei, sans-serif
|
||||
5. **自定义 z-index**:z-60, z-70(Toast 层)
|
||||
6. **自定义 boxShadow**:xs
|
||||
7. **动画**:`slide-in-right`(Drawer 进场)
|
||||
8. **content 扫描路径**:`./templates/**/*.html`, `./apps/**/templates/**/*.html`, `./static/js/**/*.js`
|
||||
---
|
||||
## 十三、package.json(仅 Tailwind 构建)
|
||||
```json
|
||||
{
|
||||
"name": "fonrey-frontend",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tailwindcss -i ./static/css/main.css -o ./static/css/output.css --minify",
|
||||
"watch": "tailwindcss -i ./static/css/main.css -o ./static/css/output.css --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
## 十四、pyproject.toml(代码质量工具)
|
||||
```toml
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
select = ["E", "F", "I", "N", "W", "UP"]
|
||||
ignore = ["E501"]
|
||||
target-version = "py312"
|
||||
[tool.black]
|
||||
line-length = 100
|
||||
target-version = ["py312"]
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
line_length = 100
|
||||
[tool.pytest.ini_options]
|
||||
DJANGO_SETTINGS_MODULE = "config.settings.development"
|
||||
python_files = ["test_*.py", "*_test.py"]
|
||||
addopts = "--reuse-db"
|
||||
```
|
||||
---
|
||||
## 十五、执行顺序与验证清单
|
||||
按以下顺序执行,每步完成后打 ✅:
|
||||
```
|
||||
[ ] 1. 创建根目录 fonrey/ 及上述完整目录树(含所有 __init__.py)
|
||||
[ ] 2. 创建 pyproject.toml / .gitignore / .env.example / Makefile
|
||||
[ ] 3. 创建 requirements/ 三个文件
|
||||
[ ] 4. 创建 config/settings/base.py(完整配置)
|
||||
[ ] 5. 创建 config/settings/development.py 和 production.py
|
||||
[ ] 6. 创建 config/urls.py(骨架路由,含 django-tenants URL routing)
|
||||
[ ] 7. 创建 config/asgi.py(ASGI 入口)
|
||||
[ ] 8. 创建 core/models/base.py(四个抽象基类)
|
||||
[ ] 9. 创建 core/encryption.py(PhoneEncryption 骨架)
|
||||
[ ] 10. 创建 core/cache.py(Redis 工具骨架)
|
||||
[ ] 11. 创建 core/htmx.py(htmx_response 工具)
|
||||
[ ] 12. 创建 core/templatetags/heroicons.py
|
||||
[ ] 13. 创建 core/middleware/audit.py(骨架)
|
||||
[ ] 14. 为每个 App 创建目录结构(含 apps.py、models/__init__.py、services/__init__.py、tasks.py 骨架、views.py 骨架、urls.py 骨架)
|
||||
[ ] 15. 创建 apps/tenant/models.py(Tenant、Domain 模型,django-tenants 规范)
|
||||
[ ] 16. 创建 templates/ 完整目录树及 base.html、layouts/app.html、layouts/auth.html 骨架
|
||||
[ ] 17. 创建 components/ 模板骨架(topbar, sidebar, pagination, toast, modal, empty-state)
|
||||
[ ] 18. 创建 templates/errors/ 三个错误页骨架
|
||||
[ ] 19. 创建 static/css/main.css(Tailwind 入口)
|
||||
[ ] 20. 创建 static/js/main.js(Alpine 初始化 + HTMX 全局事件)
|
||||
[ ] 21. 创建 tailwind.config.js(完整色彩/字体规范)
|
||||
[ ] 22. 创建 package.json
|
||||
[ ] 23. 创建 Dockerfile
|
||||
[ ] 24. 创建 docker-compose.yml(5 个服务)
|
||||
[ ] 25. 创建 manage.py
|
||||
[ ] 26. 验证:python manage.py check --deploy 无致命错误
|
||||
[ ] 27. 验证:项目目录树与第二节规范 100% 匹配
|
||||
```
|
||||
---
|
||||
## 十六、关键注意事项
|
||||
1. **django-tenants `apps/tenant/models.py`** 必须定义 `Tenant`(继承 `TenantMixin`)和 `Domain`(继承 `DomainMixin`),且 `Tenant` 的 `auto_create_schema = True`。
|
||||
2. **`shared/` App** 的 `apps.py` 中 `name = "shared"`,用于公共 Schema 的跨租户共享数据(如 PermissionDef 等)。
|
||||
3. **所有 App 的 `apps.py`** 必须包含正确的 `name`(含包路径,如 `apps.property`)和 `verbose_name`(中文)。
|
||||
4. **`config/urls.py`** 使用 `django-tenants` 的 URL 路由模式,区分 public schema 路由和 tenant schema 路由。
|
||||
5. **`apps/release/`** 放在 `SHARED_APPS`(所有租户共享一张版本表),其余业务 App 放 `TENANT_APPS`。
|
||||
6. **`.gitignore`** 必须包含:`.env`、`*.pyc`、`__pycache__/`、`.DS_Store`、`node_modules/`、`static/css/output.css`、`media/`、`dist/`。
|
||||
7. **模板中所有异步 HTMX 请求**在骨架阶段只需占位,但必须包含正确的 `hx-` 属性结构,不可省略 `hx-target` 和 `hx-swap`。
|
||||
8. **Toast 系统**:前端监听 `htmx:afterRequest` 事件,检查响应头 `HX-Trigger` 中的 `fonrey:toast`,动态插入 Toast DOM,4 秒自动消失。
|
||||
9. **小屏拦截**:`layouts/app.html` 中内嵌 JS,`window.innerWidth < 1280` 时显示全屏遮罩,文案:"Fonrey 当前仅支持桌面端(≥1280px),请在电脑上访问"。
|
||||
10. **所有密码、密钥、Tenant ID** 禁止出现在任何 Python 文件中,统一从 `python-decouple` 的 `env()` 读取。
|
||||
@@ -260,13 +260,14 @@
|
||||
|
||||
## 额外要求
|
||||
|
||||
1. **PRD优先**:有截图的功能,以截图呈现的 UI 为补充说明,以PRD 文字为主要参考;截图和 PRD 有冲突时,以PRD为准,并在文档中注明差异。
|
||||
2. **组件引用**:每次使用特殊组件(如 Data Table、Tree Select、Drawer 等),必须在"使用的特殊组件"表格中引用组件规范设计.md 的对应章节编号,并说明如有差异的自定义部分。
|
||||
3. **HTMX 落地**:每个需要异步更新的交互(筛选、分页、弹窗提交)必须写出完整的 HTMX 属性,Engineer 可以直接复制使用。
|
||||
4. **Alpine.js 分工**:说明哪些状态由 Alpine.js 管理(弹窗开关、选中状态、表单联动),哪些交互走 HTMX(数据加载、表单提交)。
|
||||
5. **禁止设计移动端**:所有布局仅针对 ≥1280px 桌面端。
|
||||
6. **优先级标注**:P0 功能用 🔴,P1 用 🟡,P2 用 ⚫,确保 Engineer 知道实现顺序。
|
||||
7. **不要遗漏边界状态**:每个列表页必须包含空状态设计;每个表单必须包含校验失败状态;每个异步操作必须包含 Loading 状态。
|
||||
1. **类似页面UI设计有限**: 如有已完成的类似页面设计,请作为主要参考,设计页面布局,内容布局等。
|
||||
2. **PRD优先**:有截图的功能,以截图呈现的 UI 为补充说明,以PRD 文字为主要参考;截图和 PRD 有冲突时,以PRD为准,并在文档中注明差异。
|
||||
3. **组件引用**:每次使用特殊组件(如 Data Table、Tree Select、Drawer 等),必须在"使用的特殊组件"表格中引用组件规范设计.md 的对应章节编号,并说明如有差异的自定义部分。
|
||||
4. **HTMX 落地**:每个需要异步更新的交互(筛选、分页、弹窗提交)必须写出完整的 HTMX 属性,Engineer 可以直接复制使用。
|
||||
5. **Alpine.js 分工**:说明哪些状态由 Alpine.js 管理(弹窗开关、选中状态、表单联动),哪些交互走 HTMX(数据加载、表单提交)。
|
||||
6. **禁止设计移动端**:所有布局仅针对 ≥1280px 桌面端。
|
||||
7. **优先级标注**:P0 功能用 🔴,P1 用 🟡,P2 用 ⚫,确保 Engineer 知道实现顺序。
|
||||
8. **不要遗漏边界状态**:每个列表页必须包含空状态设计;每个表单必须包含校验失败状态;每个异步操作必须包含 Loading 状态。
|
||||
|
||||
---PROMPT END---
|
||||
|
||||
|
||||
108
Project/fonrey/prompt/提示词模板/模块实现TASK项目任务生成提示词_v1.md
Normal file
108
Project/fonrey/prompt/提示词模板/模块实现TASK项目任务生成提示词_v1.md
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
你是一个 B2B SaaS 项目的产品/技术项目经理。你的任务是根据下方的 PRD.md及PRD_MPV.md 内容,生成一份完整的 TASK.md 项目任务看板文件。
|
||||
|
||||
## 任务说明:
|
||||
根据{{模块名称}}的PRD文档,生成具体能落地的项目实现TASK列表
|
||||
|
||||
## 输入材料
|
||||
|
||||
- 模块PRD文档:
|
||||
- 模块DATA_MODEL文档:
|
||||
- TECH_STACK文档:
|
||||
- UI_SYSTEM文档:
|
||||
|
||||
## 输出要求
|
||||
|
||||
### 文件结构规则
|
||||
按以下 Phase 划分,严格对应 PRD 优先级:
|
||||
- **Phase 1 - MVP(P0,上线前必须完成)** → PRD 中标注 P0 的功能
|
||||
- **Phase 2 - 增强功能(P1,MVP 后第一迭代)** → PRD 中标注 P1 的功能
|
||||
- **Phase 3 - 路线图功能(P2,已规划未排期)** → PRD 中标注 P2 的功能
|
||||
- **Phase 4 - 明确不做(Out of Scope)** → PRD 第 3 节"非目标"中的功能,仅列出条目作为备忘,不写 task 详情
|
||||
|
||||
### 模块顺序(按 PRD 顺序)
|
||||
1. 用户登录
|
||||
2. 楼盘管理
|
||||
3. 房源管理
|
||||
4. 客源管理
|
||||
5. 组织人事
|
||||
6. 权限管理
|
||||
7. 系统配置
|
||||
8. 系统管理(运营后台)
|
||||
9. 客户端发布
|
||||
|
||||
**请在项目目录里寻找匹配的文档填入下面具体的任务列表**
|
||||
### 每条 Task 格式(严格遵守)
|
||||
```
|
||||
|
||||
#### [模块中文名]
|
||||
|
||||
- [ ] US-XXX [动词开头的功能描述,主语为"经纪人/店长/管理员/系统"]
|
||||
- 参考PRD文档:`Project/fonrey/PRD/[模块名]/[模块名]模块PRD.md` - [对应 Story 或功能点名称]
|
||||
- 参考DATA_MODEL文档:`Project/fonrey/DATA_MODEL/DATA_MODEL_[模块英文名].md`
|
||||
- 参考UI_Design文档:`Project/fonrey/UI_DESIGN/[模块名]/[功能名]_UI.md`
|
||||
- 参考UI静态原型页面:`Project/fonrey/UI_DESIGN/[模块名]/[功能名]_UI.html`
|
||||
- 验收标准:[从 PRD 功能描述提炼 2-4 条可测试的验收标准,用分号分隔]
|
||||
|
||||
```
|
||||
|
||||
### US 编号规则
|
||||
- US 编号全局唯一,按模块分段:
|
||||
- 用户登录:US-ACCOUNT-001 ~ US-ACCOUNT-100
|
||||
- 楼盘管理:US-COMPLEX-001 ~ US-COMPLEX-100
|
||||
- 房源管理:US-PROPERTY-001 ~ US-PROPERTY-100
|
||||
- 客源管理:US-CLIENT-001 ~ US-CLIENT-100
|
||||
- 组织人事:US-ORG-001 ~ US-ORG-100
|
||||
- 权限管理:US-PERMISSION-001 ~ US-PERMISSION-100
|
||||
- 系统配置:US-SETTING-001 ~ US-SETTING-100
|
||||
- 系统管理:US-SYSTEM-001 ~ US-SYSTEM-100
|
||||
- 客户端发布:US-RELEASE-001 ~ US-RELEASE-100
|
||||
|
||||
### 文件头部格式
|
||||
```
|
||||
|
||||
## Project Task Board
|
||||
|
||||
### 项目状态总览
|
||||
|
||||
- 产品名称:Fonrey 房睿
|
||||
- 当前阶段:MVP Phase 1
|
||||
- 技术栈:[待填写]
|
||||
- 最后更新:2026-04-24
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
|
||||
### 文件尾部格式
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 已完成
|
||||
|
||||
(暂无)
|
||||
|
||||
```
|
||||
|
||||
## 生成规则与约束
|
||||
|
||||
1. **PRD 里每一行功能条目都必须生成至少一条 Task**,不得遗漏
|
||||
2. **Phase 4(Out of Scope)** 只用注释格式列出,不写 US 编号和子项:
|
||||
```
|
||||
|
||||
<!-- OUT OF SCOPE: 移动端适配 - v2 规划 -->
|
||||
|
||||
```
|
||||
3. **验收标准**必须是可测试的具体行为,禁止写"功能正常"等模糊描述
|
||||
4. 文档路径中的模块英文名对照:
|
||||
- 房源管理 → PROPERTY
|
||||
- 客源管理 → CLIENT
|
||||
- 楼盘管理 → COMPLEX
|
||||
- 组织人事 → ORG
|
||||
- 权限管理 → PERMISSION
|
||||
- 用户登录 → ACCOUNT
|
||||
- 系统配置 → SETTING
|
||||
- 系统管理 → SYSTEM
|
||||
3. 一个 PRD 功能条目如果包含多个子操作(如"新增/编辑/查看"),可以拆成多条 Task,也可以合并为一条,根据复杂度判断
|
||||
4. 输出纯 Markdown 格式,不加任何解释说明,直接输出文件内容
|
||||
Reference in New Issue
Block a user