From e01ca79bdc371ac643c09d42633c6eda3fe0c938 Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 4 Jun 2026 14:34:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Project/fonrey/ADR.md | 47 +++ Project/fonrey/DATA_MODEL/STATE_MACHINE.md | 322 ++++++++++++++++++ Project/fonrey/README.md | 3 + Project/fonrey/TECH_STACK/本地开发手册.md | 260 ++++++++++++++ Project/fonrey/UI_DESIGN/ROUTES.md | 227 ++++++++++++ .../fonrey/UI_DESIGN/房源管理/房源列表_UI.md | 4 +- Project/fonrey/Vibe Coding项目管理技巧.md | 175 ++++++++++ 7 files changed, 1036 insertions(+), 2 deletions(-) create mode 100644 Project/fonrey/DATA_MODEL/STATE_MACHINE.md create mode 100644 Project/fonrey/TECH_STACK/本地开发手册.md create mode 100644 Project/fonrey/UI_DESIGN/ROUTES.md create mode 100644 Project/fonrey/Vibe Coding项目管理技巧.md diff --git a/Project/fonrey/ADR.md b/Project/fonrey/ADR.md index 584d22c0..bea79918 100644 --- a/Project/fonrey/ADR.md +++ b/Project/fonrey/ADR.md @@ -13,6 +13,8 @@ | 2026-04-30 | Atlas | 初始化 ADR 动态决策记录;补录当日关键技术与需求决策 | | 2026-05-02 | Sisyphus | 新增 ADR-20260502-001:合并系统管理与客户端发布两份 PRD 为统一的『平台管理后台 PRD』,原文件删除 | | 2026-05-02 | Sisyphus | 新增 ADR-20260502-003:定义『PRD 与 Tech 文档职责边界』规则(PRD 管 what/why、Tech 管 how);首次落地于登录管理 PRD v3.0 | +| 2026-06-04 | Sisyphus | 新增 ADR-20260604-001:列表分页全局采用 Keyset(cursor),允许 page-based 作过渡兼容;同步修订 `UI_DESIGN/ROUTES.md` 与 `UI_DESIGN/房源管理/房源列表_UI.md` | +| 2026-06-04 | Sisyphus | 新增 ADR-20260604-002:ROUTES.md 客源条目"重复客源(成交)"与"重复客源(公客)"合并为单条 `/clients/duplicates/?tab=transacted\|public`,客源条目数稳定为 19 | ## 一、记录规范(必须遵守) @@ -247,6 +249,43 @@ --- +## 2026-06-04 + +### ADR-20260604-001 +- **类型**:TECH +- **模块**:列表分页(全局) +- **状态**:accepted +- **背景**:`AGENTS.md` §4.5 与 `UI_DESIGN/ROUTES.md` §1 全局约定明文要求"所有列表查询必须使用 Keyset 分页(参数禁止包含 `offset`,统一使用 `cursor`)"。但 `UI_DESIGN/房源管理/房源列表_UI.md` 现存示例(行 97 query params 列表、行 741 HTMX `hx-vals='{"page": N}'`)仍是 page-based 风格,与全局规范冲突。MVP 阶段一次性切到 Keyset 会拖慢前端模板与示例代码改造节奏,且页码 UI(`[1][2][3]...[N]`)已被多份截图与组件清单采用。 +- **决策**: + 1. **目标口径**:列表分页长期统一为 Keyset(cursor);新增列表页与新增分页 API 不得使用 page/offset。 + 2. **过渡兼容**:MVP Phase 1 期间,已有 page-based 示例的模块文档与代码可保留 page UI 表现(页码导航 + 跳页),但**数据获取层必须支持 cursor 参数**;后端 API 接受 `cursor` 为主参数,`page` 仅作过渡兼容输入且不得用于 1000 条以上数据集。 + 3. **文档承接**:`UI_DESIGN/ROUTES.md` §1 通用约定新增"过渡兼容"说明;`UI_DESIGN/房源管理/房源列表_UI.md` 示例代码改为 `cursor`,分页 UI 表现保留。 + 4. **退出条件**:MVP GA 后 1 个版本内,所有 page-based 示例必须清理;届时新增 ADR 关闭本过渡条款。 +- **影响范围**: + - `UI_DESIGN/ROUTES.md` §1 + - `UI_DESIGN/房源管理/房源列表_UI.md` §2.1.1、§2.1.3 分页栏 + - 后续所有列表页 HTMX 模板与 API 视图 +- **关联文档**: + - `AGENTS.md` §4.5 + - `UI_DESIGN/ROUTES.md` + - `UI_DESIGN/房源管理/房源列表_UI.md` +- **备注**:B-04(Keyset 分页规范缺位)由本 ADR 部分关闭;剩余的列表索引矩阵补全留待后续 Major 修订。 + +### ADR-20260604-002 +- **类型**:REQ +- **模块**:客源管理(路由汇总) +- **状态**:accepted +- **背景**:`UI_DESIGN/ROUTES.md` §5 客源条目数与历史 handoff 口径"客源 19 项"不一致——实际列出 20 行,多出的 1 行来自将"重复客源(成交)"与"重复客源(公客)"拆分为两条独立路由。两者复用同一列表页结构、同一权限范围、同一组件清单,仅业务子集不同,符合 ROUTES.md 全局约定中"Tab 维度统一用 `?tab=` 表达"的模式。 +- **决策**:将两条重复客源路由合并为单条 `/clients/duplicates/`,子集通过 `?tab=transacted|public` 区分;客源条目总数稳定为 19。 +- **影响范围**: + - `UI_DESIGN/ROUTES.md` §5(合并 2 行为 1 行) +- **关联文档**: + - `UI_DESIGN/ROUTES.md` + - `UI_DESIGN/客源管理/客源列表_UI.md` +- **备注**:本 ADR 不变更任何业务规则,仅统一 URL 表达形式与条目计数。 + +--- + ## 三、按模块分类记录(视图索引) ## 3.1 测试治理(全局) @@ -274,6 +313,12 @@ - `ADR-20260502-001`:合并系统管理 PRD 与客户端发布 PRD 为统一的『平台管理后台 PRD』(REQ) - `ADR-20260502-002`:合并系统管理技术文档与客户端发布技术方案为统一的『平台管理后台技术方案』(TECH) +## 3.7 列表分页(全局) +- `ADR-20260604-001`:Keyset(cursor) 为目标口径;MVP 期允许 page-based 作过渡兼容(TECH) + +## 3.8 客源管理(路由) +- `ADR-20260604-002`:重复客源合并为单条 `/clients/duplicates/?tab=transacted|public`,客源条目数=19(REQ) + --- ## 四、历史记录(Append-Only Log) @@ -295,6 +340,8 @@ | ADR-20260502-001 | 2026-05-02 | 平台管理后台 | REQ | accepted | 合并系统管理 PRD + 客户端发布 PRD 为『平台管理后台 PRD』,原文件删除 | `PRD/平台管理后台/平台管理后台PRD.md` | | ADR-20260502-002 | 2026-05-02 | 平台管理后台 | TECH | accepted | 合并系统管理技术文档 + 客户端发布技术方案为『平台管理后台技术方案』(覆盖技术选型/页面路由表/API 设计三维度),原文件删除 | `TECH_STACK/平台管理后台技术方案.md` | | ADR-20260502-003 | 2026-05-02 | 文档治理 | REQ | accepted | PRD 管 what/why、Tech 管 how;PRD 必须移出 API 路径/Redis Key/字段类型/框架 API 等实现细节,由 Tech 与 DATA_MODEL 承接 | `PRD/登录管理/用户登录管理模块PRD.md` v3.0 | +| ADR-20260604-001 | 2026-06-04 | 列表分页(全局) | TECH | accepted | Keyset(cursor) 为目标口径;MVP 期允许 page-based 作过渡兼容 | `UI_DESIGN/ROUTES.md`、`UI_DESIGN/房源管理/房源列表_UI.md` | +| ADR-20260604-002 | 2026-06-04 | 客源管理(路由) | REQ | accepted | 重复客源合并为单条 `/clients/duplicates/?tab=transacted\|public`,客源条目数=19 | `UI_DESIGN/ROUTES.md` | --- diff --git a/Project/fonrey/DATA_MODEL/STATE_MACHINE.md b/Project/fonrey/DATA_MODEL/STATE_MACHINE.md new file mode 100644 index 00000000..6e13b5bb --- /dev/null +++ b/Project/fonrey/DATA_MODEL/STATE_MACHINE.md @@ -0,0 +1,322 @@ +> For AI assistants: Read this entire file before writing any code. All decisions here are final. + +# Fonrey — 统一状态机规范 (STATE_MACHINE) + +**定位**:本文档是 Fonrey 项目所有实体生命周期与状态流转的唯一权威定义。 +**变更历史**: + +| 日期 | 变更人 | 变更内容 | +|---|---|---| +| 2026-06-04 | Sisyphus | 初始版本:整合各模块状态机逻辑,对齐 ENUMS v2.2 | + +--- + +## 1. 通用约定 + +- **状态值命名**:统一采用 `lower_snake_case`,必须与 `DATA_MODEL/ENUMS.md` 完全一致。 +- **触发动作**:状态迁移必须由显式的动作(Action)或事件(Event)触发。 +- **操作日志**:所有关键状态迁移(除系统内部中间态外)必须记录对应的 `status_log` 或 `audit_log` 表。 +- **不可逆规则**:标注为“终态”的状态禁止迁回初始态或中间态。 +- **权限要求**:状态迁移接口必须校验操作者的 DataScope 与 Function Permission。 + +--- + +## 2. 租户生命周期 (Public.Tenant) + +**实体**: `public.tenants` | **字段**: `status` +**参考**: `DATA_MODEL_PUBLIC.md` §2.1, §4.1 + +```mermaid +stateDiagram-v2 + [*] --> creating + creating --> active: 初始化完成 + creating --> failed: 初始化失败 + active --> suspended: 逾期/违规/申请 + suspended --> active: 恢复条件满足 + active --> pending_delete: 申请注销 + suspended --> pending_delete: 申请注销 + pending_delete --> deleted: 30天后/人工确认 + deleted --> [*] +``` + +### 状态枚举值 +(domain: `public.tenant.status`) +- `creating`: 创建中 +- `active`: 正常 +- `suspended`: 已挂起 +- `pending_delete`: 待删除 +- `deleted`: 已删除 +- `failed`: 创建/初始化失败 + +### 状态迁移表 + +| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 | +|---|---|---|---|---|---| +| `creating` | `active` | 租户 Schema 初始化成功 | System | 写 `tenant_status_logs` | 否 | +| `creating` | `failed` | 初始化脚本执行失败 | System | 记录错误日志 | 否 | +| `active` | `suspended` | 欠费/违规/License过期 | System/SuperAdmin | 设置 `suspended_until`, 写日志 | 是 | +| `suspended` | `active` | 缴费成功/解封/到期恢复 | System/SuperAdmin | 清除挂起原因 | 是 | +| `active`/`suspended` | `pending_delete` | 提交注销申请 | TenantAdmin | 设置 `deleted_at` | 是 | +| `pending_delete` | `deleted` | 确认注销/静默期结束 | SuperAdmin/System | 物理删除或深度标记 | 否 | + +**来源章节**: `DATA_MODEL_PUBLIC.md` §4.1 + +--- + +## 3. 客源状态机 (Client) + +**实体**: `client.clients` | **字段**: `status` +**参考**: `DATA_MODEL_CLIENT.md` §3.1, §4 | `ENUMS.md` §3.6 + +```mermaid +stateDiagram-v2 + [*] --> buying + [*] --> renting + [*] --> buy_or_rent + + state "活跃态" as Active { + buying + renting + buy_or_rent + } + + Active --> suspended: 暂时无需求 + suspended --> Active: 重新激活 + + Active --> public: 手动转公/超时自动 + suspended --> public: 手动转公 + + Active --> bought: 转成交(我购) + Active --> rented_done: 转成交(我租) + + Active --> invalid: 转无效 + suspended --> invalid: 转无效 + + invalid --> Active: 经理审批恢复 + + bought --> [*] + rented_done --> [*] + public --> [*] +``` + +### 状态枚举值 +(domain: `client.status`) +- `buying`: 求购 +- `renting`: 求租 +- `buy_or_rent`: 租购 +- `suspended`: 暂缓 +- `bought`: 已购 (我购) +- `rented_done`: 已租 (我租) +- `public`: 公客 +- `invalid`: 无效 + +### 状态迁移表 + +| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 | +|---|---|---|---|---|---| +| `buying`/`renting`/`buy_or_rent` | `suspended` | 修改状态为暂缓 | 经纪人 | 写 `client_status_logs` | 是 | +| `suspended` | `buying`/`renting`/`buy_or_rent` | 重新激活需求 | 经纪人 | 更新 `last_active_at` | 是 | +| `Active`/`suspended` | `public` | 手动转公/超时自动转公 | 经纪人/System | 修改 `client_type='public'` | 否 | +| `Active` | `bought`/`rented_done` | 录入成交合同 | 经纪人 | 修改 `client_type='transacted'` | 否 | +| `Active`/`suspended` | `invalid` | 标记无效 | 经纪人 | 记录 `invalid_reason` | 需审批 | + +### 禁迁规则 +- `transacted` (bought/rented_done) 状态为绝对终态,禁止迁往任何其他状态。 +- `public` 状态客源禁止直接修改为 `private` 状态下的需求状态,必须走领用流程。 + +**来源章节**: `DATA_MODEL_CLIENT.md` §4, `ENUMS.md` §3.6 + +--- + +## 4. 房源状态机 (Property) + +**实体**: `property.properties` | **字段**: `status` +**参考**: `DATA_MODEL_PROPERTY.md` §3, §4.1 + +```mermaid +stateDiagram-v2 + [*] --> unlisted + unlisted --> for_sale: 挂牌出售 + unlisted --> for_rent: 挂牌出租 + + state "在盘态" as Listed { + for_sale + for_rent + for_sale_rent + } + + Listed --> suspended: 业主暂缓 + suspended --> Listed: 重新上架 + + Listed --> sold: 本司成交 + Listed --> sold_elsewhere: 他售 + Listed --> rented_elsewhere: 他租 + + Listed --> unlisted: 下架 + + sold --> [*] + sold_elsewhere --> [*] +``` + +### 状态枚举值 +(domain: `property.status`) +- `for_sale`: 出售 +- `for_rent`: 出租 +- `for_sale_rent`: 租售 +- `suspended`: 暂缓 +- `sold_elsewhere`: 他售 +- `rented_elsewhere`: 他租 +- `sold`: 成交 +- `unlisted`: 未挂牌 + +### 状态迁移表 + +| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 | +|---|---|---|---|---|---| +| `unlisted` | `for_sale`/`for_rent` | 房源挂牌 | 经纪人 | 新增 `listing_histories` | 是 | +| `Listed` | `suspended` | 业主通知暂缓 | 经纪人 | 写跟进日志 | 是 | +| `Listed` | `sold` | 本司录入成交 | 经纪人 | 自动结束挂牌历史 | 否 | +| `Listed` | `sold_elsewhere`/`rented_elsewhere` | 核实他司已成交 | 经纪人 | | 否 | +| `Listed` | `unlisted` | 强制下架 | 管理员 | | 是 | + +**来源章节**: `DATA_MODEL_PROPERTY.md` §3 + +--- + +## 5. 客户/房源等级 (Grade) + +**实体**: `client.clients` / `property.properties` | **字段**: `grade` +**参考**: `DATA_MODEL_CLIENT.md` §3.1 | `DATA_MODEL_PROPERTY.md` §4.1 + +### 状态枚举值 +(domain: `client.grade` / `property.grade`) +- `A`: A (急迫) +- `B`: B (较强) +- `C`: C (一般) +- `D`: D (较弱) +- `E`: E (暂不关注) - 仅客源 + +### 状态迁移表 + +| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 | +| ---- | ------------------- | ------- | ---- | ----------------------------- | ---- | +| 任意状态 | `A`/`B`/`C`/`D`/`E` | 手动调整等级 | 经纪人 | 写 `status_log` / `follow_log` | 是 | + +**来源章节**: `DATA_MODEL_CLIENT.md` §4, `DATA_MODEL_PROPERTY.md` §3 + +--- + +## 6. 账号状态机 (Account) + +**实体**: `login.user_accounts` | **字段**: `status` +**参考**: `DATA_MODEL_LOGIN.md` §5.1 + +```mermaid +stateDiagram-v2 + [*] --> active: 创建(初始密码) + active --> locked: 密码错误≥5次 + locked --> active: 30min到期/手动解锁 + active --> disabled: 离职/手动停用 + disabled --> active: 复职/手动恢复 +``` + +### 状态枚举值 +(domain: `login.user_account.status`) +- `active`: 启用 +- `disabled`: 停用 +- `locked`: 锁定 + +### 状态迁移表 + +| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 | +|---|---|---|---|---|---| +| `active` | `locked` | 密码连续错误5次 | System | 设置 `locked_until` | 是 | +| `locked` | `active` | 时间到期/管理员解锁 | System/Admin | | 是 | +| `active` | `disabled` | 员工离职/手动停用 | Admin/System | 踢出所有 Session | 是 | + +**来源章节**: `DATA_MODEL_LOGIN.md` §5.1 + +--- + +## 7. 升级事件状态机 (Upgrade Event) + +**实体**: `public.upgrade_events` | **字段**: `status` +**参考**: `DATA_MODEL_PUBLIC.md` §4.2 + +```mermaid +stateDiagram-v2 + [*] --> draft + draft --> pre_check: 提交 + pre_check --> pre_backup: 健康检查通过 + pre_backup --> batch_running: 备份完成 + batch_running --> batch_done: 批次租户成功 + batch_done --> batch_running: 下一批次 + batch_done --> succeeded: 全部完成 + + batch_running --> halted: 失败/门控拦截 + halted --> batch_running: 人工继续 + halted --> rollback_running: 人工回滚 + rollback_running --> rolled_back: 回滚完成 + + batch_running --> failed: 严重故障 +``` + +### 状态枚举值 +(domain: `public.upgrade_event.status`) +- `draft`: 草稿 +- `pre_check`: 预检查 +- `pre_backup`: 预备份 +- `batch_running`: 批次执行中 +- `batch_done`: 批次完成 +- `halted`: 已暂停 +- `succeeded`: 已成功 +- `failed`: 失败 +- `rollback_running`: 回滚中 +- `rolled_back`: 已回滚 + +**来源章节**: `DATA_MODEL_PUBLIC.md` §4.2 + +--- + +## 8. 其他辅助状态 + +### 8.1 备份/导出任务 (Backup/Export) +**字段**: `status` | **参考**: `ENUMS.md` §2.3 +- `pending` -> `in_progress` -> `success` / `failed` + +### 8.2 客户端发布 (Client Release) +**字段**: `status` | **参考**: `ENUMS.md` §2.4 +- `draft` -> `published` -> `archived` +- **约束**: 同平台架构下仅允许一个 `published`。 + +### 8.3 员工状态 (Staff) +**字段**: `status` | **参考**: `DATA_MODEL_ORG.md` §4 +- `probation` -> `active` -> `resigned` +- `active` <-> `frozen` + +### 8.4 审批流 (Approval) +**实体**: `number_holder_approvals` | **字段**: `status` +- `pending` -> `approved` / `rejected` + +--- + +## X. 状态机一致性校验清单 + +1. **枚举同步**: 状态字段值是否在 `ENUMS.md` 中定义且拼写一致? +2. **日志完备**: 每次 `status` 变更是否伴随对应的 `status_log` 插入动作? +3. **并发控制**: 状态变更是否包含 `version` 乐观锁检查? +4. **终态拦截**: 业务逻辑是否显式禁止从终态(如 `sold`, `deleted`, `rolled_back`)向外迁移? +5. **副作用触发**: 状态变更相关的异步任务(如转公客通知、缓存清理)是否已在 Service 层注册? + +--- + +## 待澄清问题 + +1. **Client Invalid 恢复路径**: `DATA_MODEL_CLIENT.md` §4 提到“需经理审批后可恢复”,但未定义恢复后的目标状态(是恢复到 `suspended` 还是之前的活跃态?)。 + - *涉及文件*: `DATA_MODEL_CLIENT.md` +2. **Property Unlisted 属性**: 房源在 `unlisted` 状态下,其 `attribute` (公/私盘) 是否有意义?若从 `private` 挂牌变为 `unlisted`,再次挂牌时是否应默认保留 `private` 属性? + - *涉及文件*: `DATA_MODEL_PROPERTY.md` +3. **Tenant Deleted 物理性**: `DATA_MODEL_PUBLIC.md` §4.1 提到“硬删除直接到 deleted”,但在 §2.1 又说“硬删除直接物理删除行”。需要明确 `deleted` 状态是代表软删除的终点还是物理删除前的标记。 + - *涉及文件*: `DATA_MODEL_PUBLIC.md` +4. **Staff Resigned 恢复**: 离职员工复职(`rejoin`)后,其 `status` 是回到 `probation` 还是 `active`? + - *涉及文件*: `DATA_MODEL_ORG.md` diff --git a/Project/fonrey/README.md b/Project/fonrey/README.md index 17f3a75a..181f69fb 100644 --- a/Project/fonrey/README.md +++ b/Project/fonrey/README.md @@ -36,6 +36,9 @@ | 技术总纲 | [`TECH_STACK/TECH_STACK.md`](./TECH_STACK/TECH_STACK.md) | 跨模块技术决策、模块索引、测试策略 | | 数据模型总览 | [`DATA_MODEL/DATA_MODEL.md`](./DATA_MODEL/DATA_MODEL.md) | 全局数据架构与子文档索引 | | API 全局契约 | [`TECH_STACK/API_CONTRACT.md`](./TECH_STACK/API_CONTRACT.md) | 请求/响应、错误码、分页、上传下载规范 | +| 本地开发与启动 | [`TECH_STACK/本地开发手册.md`](./TECH_STACK/本地开发手册.md) | 前置依赖、环境变量、`django-tenants` 初始化、种子数据、本地启动与验证 | +| 路由与组件映射 | [`UI_DESIGN/ROUTES.md`](./UI_DESIGN/ROUTES.md) | 全局唯一权威:模块 → 页面 → URL → 复用组件 → 四态 | +| 状态机权威 | [`DATA_MODEL/STATE_MACHINE.md`](./DATA_MODEL/STATE_MACHINE.md) | 全局唯一权威:所有实体生命周期与状态迁移规则 | | 测试规范 | [`TECH_STACK/测试规范.md`](./TECH_STACK/测试规范.md) | 测试流程、报告字段、CI 要求 | | 测试编号规则 | [`TEST_CASES/TEST_CASE_ID_SPEC.md`](./TEST_CASES/TEST_CASE_ID_SPEC.md) | `TC-FON-XXXXXX` 规则 | | 测试注册表 | [`TEST_CASES/TEST_CASE_REGISTRY.md`](./TEST_CASES/TEST_CASE_REGISTRY.md) | 测试编号水位与批次登记 | diff --git a/Project/fonrey/TECH_STACK/本地开发手册.md b/Project/fonrey/TECH_STACK/本地开发手册.md new file mode 100644 index 00000000..6232c6a5 --- /dev/null +++ b/Project/fonrey/TECH_STACK/本地开发手册.md @@ -0,0 +1,260 @@ +> For AI assistants: Read this entire file before writing any code. All decisions here are final. + +# 本地开发手册 (Local Development Manual) + +本手册为 Fonrey 项目本地环境搭建与开发验证的权威指南。旨在帮助新入场开发人员或 AI agent 快速启动项目并确保开发环境与生产环境的技术决策一致。 + +## §0 文档定位 + 变更历史表 + +| 日期 | 变更人 | 变更内容 | 版本 | +|---|---|---|---| +| 2026-06-04 | Sisyphus-Junior | 初始化本地开发手册,整合 ADR 与技术总纲决策 | v1.0 | + +--- + +## §1 前置依赖 + +运行 Fonrey 前,请确保本地环境已安装以下基础组件: + +- **操作系统**: 推荐 Linux (Ubuntu 22.04+) 或 macOS。Windows 用户建议使用 WSL2。 +- **Python**: 3.12+ +- **Node.js**: 20.x+ (用于 Tailwind CSS 编译) +- **Docker & Docker Compose**: 必须,用于运行数据库、缓存与消息队列。 +- **PostgreSQL**: 16.x (通过 Docker 运行) +- **Redis**: 7.x (通过 Docker 运行) +- **浏览器**: 最新版 Google Chrome 或 Chromium (用于 Playwright E2E 测试) + +--- + +## §2 仓库初始化 + +按照以下步骤克隆仓库并安装依赖: + +```bash +# 克隆仓库 +git clone fonrey +cd fonrey + +# 创建虚拟环境 +python -m venv venv +source venv/bin/activate # Linux/macOS +# .\venv\Scripts\activate # Windows + +# 安装 Python 依赖 +pip install --upgrade pip +pip install -r requirements.txt +pip install -r requirements/test.txt # 仅开发环境需要 + +# 安装前端依赖 +npm install +``` + +--- + +## §3 环境变量 (`.env`) + +在项目根目录创建 `.env` 文件。以下为必需项清单及示例值: + +```bash +# Django 基础配置 +DEBUG=True +SECRET_KEY=django-insecure-development-key-change-me +ALLOWED_HOSTS=localhost,127.0.0.1,.fonrey.test + +# 数据库配置 (需配合 Docker Compose) +DATABASE_URL=postgres://fonrey_user:fonrey_password@localhost:5432/fonrey_db +# 如果使用 PgBouncer +# DATABASE_URL=postgres://fonrey_user:fonrey_password@localhost:6432/fonrey_db + +# Redis 配置 +REDIS_URL=redis://localhost:6379/0 + +# Celery 配置 +CELERY_BROKER_URL=redis://localhost:6379/1 +CELERY_RESULT_BACKEND=redis://localhost:6379/1 + +# Cloudflare R2 / S3 存储 (本地开发推荐使用 MinIO) +AWS_ACCESS_KEY_ID=minio_admin +AWS_SECRET_ACCESS_KEY=minio_password +AWS_STORAGE_BUCKET_NAME=fonrey-media +AWS_S3_ENDPOINT_URL=http://localhost:9000 +AWS_S3_REGION_NAME=auto + +# Sentry (本地开发可选) +SENTRY_DSN= + +# 加密密钥 (AES-256-GCM,必须为 32 字节 Base64) +FIELD_ENCRYPTION_KEY=your-32-byte-base64-encoded-key +``` + +--- + +## §4 数据库与缓存启动 + +推荐使用 Docker Compose 快速拉起基础服务。项目根目录应包含 `docker-compose.yml`: + +```bash +# 启动 PostgreSQL, Redis, MinIO +docker-compose up -d + +# 验证服务状态 +docker-compose ps +``` + +**PgBouncer 提示**: 生产环境强制使用 PgBouncer 进行连接池管理。本地开发环境下,如果需要模拟高并发连接,可开启 docker-compose 中的 pgbouncer 服务并将 `DATABASE_URL` 指向 6432 端口。 + +--- + +## §5 django-tenants 初始化 + +由于项目采用 `django-tenants` 多租户架构,必须先初始化公共 Schema (public schema): + +```bash +# 1. 运行公共 Schema 的迁移 +python manage.py migrate_schemas --shared + +# 2. 创建第一个租户 (手动交互或使用脚本) +# 注意:在本地开发时,建议将租户域名映射到 .fonrey.test +``` + +--- + +## §6 测试租户与种子数据 + +目前项目尚未完成自动化 seed 命令。建议通过以下方式手动创建测试环境: + +### 6.1 创建初始租户 (建议命令) +*预期放置位置: `apps/tenant/management/commands/setup_dev_tenant.py`* + +```bash +# 尚未实现,需后续补写。目前请进入 python shell 手动创建: +python manage.py shell +>>> from apps.tenant.models import Tenant, Domain +>>> tenant = Tenant.objects.create(schema_name='test_tenant', name='测试经纪公司') +>>> Domain.objects.create(domain='test.fonrey.test', tenant=tenant, is_primary=True) +``` + +### 6.2 导入枚举种子数据 +```bash +# 导入 ENUMS.md 定义的公共枚举值 +python manage.py loaddata shared/fixtures/enum_labels.json +``` + +--- + +## §7 后端启动 + +本地开发环境直接使用 Django 开发服务器即可。生产环境必须使用 Gunicorn + Uvicorn。 + +```bash +# 本地开发模式 +python manage.py runserver 8000 +``` + +--- + +## §8 Celery 与 Celery Beat 启动 + +涉及异步任务(如图片处理、导出、配房)时,必须启动 Worker 和 Beat: + +```bash +# 启动 Worker +celery -A core worker -l info + +# 启动 Beat (定时任务) +celery -A core beat -l info +``` + +--- + +## §9 前端资源 + +Fonrey 依赖 Tailwind CSS 进行样式构建。 + +```bash +# 开发模式:监听文件变化并实时编译 +npm run dev + +# 生产构建 +npm run build +``` + +--- + +## §10 静态资源与对象存储本地替代方案 + +本地开发建议使用 MinIO 替代 Cloudflare R2。 + +1. 修改 `.env` 中的 `AWS_S3_ENDPOINT_URL` 指向 MinIO 地址。 +2. 确保已手动或通过脚本创建了 `media` 和 `releases` 桶。 +3. 访问 `http://localhost:9001` 进入 MinIO 控制台。 + +--- + +## §11 测试运行手册 + +所有测试基于 `pytest` 运行。 + +### 11.1 运行单元与集成测试 +```bash +# 运行全量测试 +pytest + +# 按目录跑集成测试 +pytest tests/integration/property/ + +# 查看覆盖率 +pytest --cov=apps --cov=core +``` + +### 11.2 运行 E2E 测试 (Playwright) +```bash +# 安装浏览器驱动 (仅需一次) +playwright install chromium + +# 运行 E2E +pytest tests/e2e/ +``` + +--- + +## §12 dev / staging / production 配置差异表 + +| 配置项 | Local Dev | Staging | Production | +|---|---|---|---| +| DEBUG | True | False | False | +| 多租户驱动 | django-tenants | django-tenants | django-tenants | +| 数据库 | Postgres 16 | Postgres 16 (RDS/Self-host) | Postgres 16 + PgBouncer | +| 存储服务 | MinIO (本地) | Cloudflare R2 | Cloudflare R2 | +| 任务队列 | Redis | Redis | Redis (Cluster/Sentinel) | +| 缓存 | Redis | Redis | Redis | +| 静态资源 | 本地处理 | Cloudflare CDN | Cloudflare CDN | +| 错误日志 | 控制台 | Sentry | Sentry + Grafana | +| 服务运行器 | runserver | Gunicorn + Uvicorn | Gunicorn + Uvicorn + Nginx | + +--- + +## §13 常见故障排查 + +- **schema 切换失败**: 检查 `django-tenants` 中间件是否已启用,且 `ALLOWED_HOSTS` 包含对应的子域名。 +- **R2 401**: 验证 `.env` 中的 AWS 凭证是否正确,桶是否存在。 +- **Celery 任务不执行**: 检查 Redis 是否连通,Worker 是否已启动,且任务是否使用了 `@tenant_task` 装饰器。 +- **HTMX 局刷 404**: 确认视图是否处理了 `HX-Request` 头,且 URL 路由配置正确。 + +--- + +## §14 关联文档索引 + +- [`AGENTS.md`](../AGENTS.md), 技术栈总览与禁止项 +- [`ADR.md`](../ADR.md), 关键技术决策追溯 +- [`TECH_STACK/测试规范.md`](./测试规范.md), 详细测试要求 +- [`DATA_MODEL/DATA_MODEL.md`](../DATA_MODEL/DATA_MODEL.md), 数据架构定义 + +--- + +## §15 待澄清/待补项 + +1. **种子数据脚本**: 尚未实现 `setup_dev_tenant` 命令,目前需手动在 Shell 创建租户。 +2. **MinIO 预设**: 需要一个初始化脚本自动创建所需的 S3 桶(media, releases)。 +3. **Playwright 浏览器环境**: 尚未在 CI 配置中固定特定 Chromium 版本,本地开发建议使用最新版。 +4. **系统配置 DDL**: `AGENTS.md` 提到系统配置 PRD 暂为空,对应的 DDL 迁移可能不完整。 diff --git a/Project/fonrey/UI_DESIGN/ROUTES.md b/Project/fonrey/UI_DESIGN/ROUTES.md new file mode 100644 index 00000000..f2c5e60e --- /dev/null +++ b/Project/fonrey/UI_DESIGN/ROUTES.md @@ -0,0 +1,227 @@ +# Fonrey 路由与组件映射表 (ROUTES.md) + +> **文档定位**:Fonrey MVP 全局路由与 UI 组件映射的权威来源。 +> **适用对象**:Web 开发、前端工程、后端视图开发。 +> **更新原则**:新增页面必须在此登记。URL 命名严格遵循 RESTful + Django 习惯。 +> **对齐基线**:本表与 `UI_DESIGN/*/*.md` 各模块设计文档对齐。模块设计文档的页面清单是权威,本文为汇总入口。 + +--- + +## 1. 通用约定 + +- **URL 规范**:资源复数形式 + 末尾斜杠(例如:`/properties/`)。 +- **多租户**:业务端 URL 不携带租户 ID(Schema 由 `django-tenants` 中间件按 subdomain 切换);平台管理后台为 `public` schema 下独立子域,前缀 `/admin/`。 +- **分页规范**:所有列表页强制使用 **Keyset 分页**。API 主参数统一为 `cursor`,禁止使用 `offset`。**过渡兼容**(MVP Phase 1 期,详见 `ADR-20260604-001`):已有 page-based 示例的模块可保留页码 UI 表现(页码导航 + 跳页),但后端 API 必须以 `cursor` 为主参数;`page` 仅作过渡输入且不得用于 1000 条以上数据集。MVP GA 后 1 个版本内须清理所有 page-based 示例。 +- **Tab 维度**:列表页的 Tab 切换统一以 query param `?tab=` 表达;切换走 HTMX 局刷,由后端根据 `tab` 返回对应数据。 +- **HTMX Partial 路径**:业务页面的局部刷新走 `//_partials//`;与某条记录强绑定的子区块走 `////partial`。 +- **JSON-only API 端点**:对外暴露给前端 AJAX/HTMX 的纯数据端点统一前缀 `/api/`。 +- **组件引用**:本表"复用组件"列必须严格引用 [`UI_SYSTEM/组件清单.md`](../UI_SYSTEM/组件清单.md) 中的标准名称,统一双语形式 `English(中文)`。新增组件必须先入组件清单再在此引用,禁止造词。 +- **页面权威来源**:每个模块的路由细节、Tab/筛选、交互态以同模块下 `UI_DESIGN/<模块>/*_UI.md` 为准;本表仅做全局汇总。 + +--- + +## 2. 全局基础页面 + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **仪表盘 (Dashboard)** | `/` | 统计页 | `Tab Navigation(标签页导航)`, `Collapsible Card Grid(折叠展开面板)` | **空**: 暂无统计数据(图表显示 0) / **加载**: Skeleton 占位 / **错误**: 全局 500 / **权限**: 重定向至登录 | +| **404 错误页** | `/404/` | 错误页 | 无 | **空**: 不适用 / **加载**: 不适用 / **错误**: 展示 404 图文 / **权限**: 公开 | +| **500 错误页** | `/500/` | 错误页 | 无 | **空**: 不适用 / **加载**: 不适用 / **错误**: 展示 500 图文 / **权限**: 公开 | +| **403 无权限页** | `/403/` | 错误页 | 无 | **空**: 不适用 / **加载**: 不适用 / **错误**: 展示 403 图文 / **权限**: 已登录但无权 | + +--- + +## 3. 登录管理模块 (Account) + +> 权威来源:[`UI_DESIGN/登录管理/登录_UI.md`](./登录管理/登录_UI.md) + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **Tenant 识别页** | `/account/tenant/verify/` | 基础页 | 无 | **空**: 不适用 / **加载**: 提交按钮 Loading / **错误**: 租户未识别/网络错误内联提示 / **权限**: 公开 | +| **登录页(密码 Tab)** | `/account/login/` | 基础页 | `Tab Navigation(标签页导航)` | **空**: 不适用 / **加载**: 提交按钮 Loading / **错误**: 凭证错误 / 锁定 / 禁用 / 会话过期 字段级提示 / **权限**: 公开 | +| **登录页(验证码 Tab)** | `/account/login/?tab=otp` | 基础页 | `Tab Navigation(标签页导航)` | **空**: 不适用 / **加载**: OTP 发送 / 倒计时态 / **错误**: OTP 错误 / 过期 内联提示 / **权限**: 公开 | +| **设置新密码页** | `/account/password/reset/` | 基础页 | 无 | **空**: 不适用 / **加载**: 强度校验态 + 提交 Loading / **错误**: 两次不一致/校验失败 内联提示 / **权限**: 仅通过 OTP 校验的合法 Token | +| **退出登录** | `/account/logout/` | 处理页 | 无 | **空**: 不适用 / **加载**: 渐进式遮罩 / **错误**: Toast 提示 / **权限**: 仅登录用户 | +| **(Partial) 租户识别** | `POST /api/auth/tenant/verify/` | API | — | **错误**: JSON 错误码 + 字段级提示 / **权限**: 公开 | +| **(Partial) 密码登录** | `POST /api/auth/login/` | API | — | **错误**: JSON 错误码(凭证/锁定/禁用) / **权限**: 公开 | +| **(Partial) OTP 发送** | `POST /api/auth/login/phone/` | API | — | **错误**: 频率限制/号码非法 / **权限**: 公开 | +| **(Partial) 找回密码-请求** | `POST /api/auth/recover/password/request/` | API | — | **错误**: 账号不存在/限流 / **权限**: 公开 | +| **(Partial) 找回密码-校验** | `POST /api/auth/recover/password/verify/` | API | — | **错误**: OTP 错误/过期 / **权限**: 公开 | +| **(Partial) 找回密码-重置** | `POST /api/auth/recover/password/reset/` | API | — | **错误**: Token 过期/校验失败 / **权限**: 持有有效重置 Token | + +--- + +## 4. 房源管理模块 (Property) + +> 权威来源:[`UI_DESIGN/房源管理/房源列表_UI.md`](./房源管理/房源列表_UI.md) / [`房源详情_UI.md`](./房源管理/房源详情_UI.md) / [`新增房源_UI.md`](./房源管理/新增房源_UI.md) + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **房源列表(全部)** | `/properties/` | 列表页 | `Data Table(数据表格)`, `Column Visibility Panel(自定义列显示)`, `Pagination(分页组件)`, `Toolbar(操作工具栏)`, `Export Button(导出按钮)`, `Smart Sort(智能排序)`, `Tab Navigation(标签页导航)` | **空**: 暂无房源(引导图 + 新增 CTA) / **加载**: 表格 Skeleton / **错误**: 列表刷新失败 Toast / **权限**: 403 遮罩 | +| **房源列表(出售 Tab)** | `/properties/?tab=for_sale` | 列表页 | 同上 | 同上 | +| **房源列表(出租 Tab)** | `/properties/?tab=for_rent` | 列表页 | 同上 | 同上 | +| **房源列表(未挂牌 Tab)** | `/properties/?tab=unlisted` | 列表页 | 同上 | 同上 | +| **房源列表(成交 Tab)** | `/properties/?tab=sold` | 列表页 | 同上 | 同上 | +| **重复房源** | `/properties/duplicates/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无重复 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅管理员/店长 | +| **嫌疑号码** | `/properties/suspect-numbers/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无嫌疑号 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅管理员/店长 | +| **新增房源** | `/properties/create/` | 编辑页(独立) | `Tab Navigation(标签页导航)`, `Multi-select Tag Input(多选标签选择器)`, `Tree Select / Cascading Dropdown(树形下拉选择器)` | **空**: 字段默认值 / **加载**: 表单骨架屏 / **错误**: 422 字段级 / 500 Toast / **权限**: 仅有权经纪人 | +| **房源详情** | `/properties//` | 详情页 | `Tab Navigation(标签页导航)`, `Accordion Progress Panel(可折叠进度检查面板)`, `Collapsible Card Grid(折叠展开面板)`, `Image Lightbox View(全屏图片灯箱预览器)` | **空**: 房源不存在(404) / **加载**: 模块化局部 Skeleton / **错误**: 详情加载失败 Toast / **权限**: 403 页面 | +| **房源全量编辑** | `/properties//edit/` | 编辑页(独立) | `Tab Navigation(标签页导航)`, `Inline Edit Mode(页面级读写切换)`, `Multi-select Tag Input(多选标签选择器)` | **空**: 不适用 / **加载**: 表单骨架屏 / **错误**: 提交失败(422/500) / **权限**: 仅维护人/管理员 | +| **挂牌历史** | `/properties//history/` | 子详情页 | `Collapsible Card Grid(折叠展开面板)` | **空**: 暂无挂牌记录 / **加载**: 列表 Skeleton / **错误**: Toast / **权限**: 403 遮罩 | +| **(Partial) 写跟进** | `/properties//_partials/followup/` | Drawer | `Drawer / Slide-over Panel(右侧抽屉面板)`, `Tab Navigation(标签页导航)` | **加载**: Drawer 内 Loading / **错误**: 提交失败 Toast / **权限**: 仅参与经纪人 | +| **(Partial) 相册管理** | `/properties//_partials/gallery/` | Modal | `Photo Gallery Manager(相册管理器)`, `Sortable Table with Drag Handle(带拖拽手柄的可排序表格)` | **空**: 暂无照片 / **加载**: 缩略图 Skeleton / **错误**: 上传失败 Toast / **权限**: 仅维护人/管理员 | + +--- + +## 5. 客源管理模块 (Client) + +> 权威来源:[`UI_DESIGN/客源管理/客源列表_UI.md`](./客源管理/客源列表_UI.md) / [`客源详情_UI.md`](./客源管理/客源详情_UI.md) / [`新增客源_UI.md`](./客源管理/新增客源_UI.md) / [`编辑客源_UI.md`](./客源管理/编辑客源_UI.md) + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **私客列表(全部)** | `/clients/private/` | 列表页 | `Data Table(数据表格)`, `Column Visibility Panel(自定义列显示)`, `Pagination(分页组件)`, `Toolbar(操作工具栏)`, `Tab Navigation(标签页导航)` | **空**: 暂无客源(引导录入) / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 403 遮罩 | +| **私客列表(求购 Tab)** | `/clients/private/?tab=buying` | 列表页 | 同上 | 同上 | +| **私客列表(求租 Tab)** | `/clients/private/?tab=renting` | 列表页 | 同上 | 同上 | +| **私客列表(暂缓 Tab,P2)** | `/clients/private/?tab=suspended` | 列表页 | 同上 | 同上 | +| **公客列表(P2)** | `/clients/public/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无公客 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅店长/管理员 | +| **成交客列表(P2)** | `/clients/transacted/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无成交客 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅管理员 | +| **重复客源** | `/clients/duplicates/?tab=transacted\|public` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)`, `Tab Navigation(标签页导航)` | **空**: 暂无重复 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅管理员(成交/公客子集见 `ADR-20260604-002`) | +| **客源回收站** | `/clients/deleted/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无回收 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅管理员 | +| **录入客源** | `/clients/create/` | 编辑页(独立) | `Tree Select / Cascading Dropdown(树形下拉选择器)`, `Multi-select Tag Input(多选标签选择器)` | **空**: 字段默认值 / **加载**: 表单 Loading / **错误**: 字段级 422 / 500 Toast / **权限**: 仅有权经纪人 | +| **客源详情** | `/clients//` | 详情页 | `Tab Navigation(标签页导航)`, `Collapsible Card Grid(折叠展开面板)` | **空**: 客源不存在 / **加载**: 局部 Skeleton / **错误**: Toast / **权限**: 403 页面 | +| **编辑客源** | `/clients//edit/` | 编辑页(独立) | `Tab Navigation(标签页导航)`, `Inline Edit Mode(页面级读写切换)`, `Tree Select / Cascading Dropdown(树形下拉选择器)` | **空**: 不适用 / **加载**: 表单 Loading / **错误**: 字段级 422 / **权限**: 仅归属人/首录人/管理员(其他重定向至详情 + Toast) | +| **(Partial) 电话重复检测** | `POST /clients/check-duplicate-phone/` | API | — | **错误**: 返回橙色警告 Partial / **权限**: 仅有权经纪人 | +| **(Partial) 跟进日志筛选/分页** | `/clients//follow-logs/partial/` | Partial | `Tab Navigation(标签页导航)` | **空**: 暂无跟进 / **错误**: Toast / **权限**: 仅参与经纪人 | +| **(Partial) 带看记录筛选** | `/clients//viewings/partial/` | Partial | — | **空**: 暂无带看 / **错误**: Toast / **权限**: 仅参与经纪人 | +| **(Partial) 客源解读刷新** | `/clients//insights/partial/` | Partial | — | **空**: 暂无洞察 / **错误**: Toast / **权限**: 仅参与经纪人 | +| **(Partial) 配房筛选/分页** | `/clients//matches/partial/` | Partial | `Pagination(分页组件)` | **空**: 暂无匹配 / **错误**: Toast / **权限**: 仅参与经纪人 | +| **(Partial) 查看号码** | `/clients//contacts//reveal-phone/` | API | — | **错误**: 频次超限/无权 Toast / **权限**: 仅归属人/上级 | +| **(Partial) 标记号码无效** | `PATCH /api/clients//contacts//mark-invalid/` | API | — | **错误**: 422 / 500 Toast / **权限**: 仅归属人/管理员 | +| **(Partial) 编辑基础信息弹窗** | `PATCH /api/clients//basic-info/` | Modal | `Modal Dialog(模态对话框)` | **错误**: 字段级 422 / **权限**: 仅归属人/管理员 | + +--- + +## 6. 楼盘/区域管理模块 (Complex / Region) + +> 权威来源:[`UI_DESIGN/楼盘管理/楼盘列表_UI.md`](./楼盘管理/楼盘列表_UI.md) / [`楼盘详情_UI.md`](./楼盘管理/楼盘详情_UI.md) / [`区域管理_UI.md`](./楼盘管理/区域管理_UI.md) + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **楼盘列表** | `/complexes/` | 列表页 | `Data Table(数据表格)`, `Column Visibility Panel(自定义列显示)`, `Pagination(分页组件)`, `Toolbar(操作工具栏)` | **空**: 暂无楼盘 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 全员可见/不可改 | +| **楼盘详情** | `/complexes//` | 详情页 | `Tab Navigation(标签页导航)`, `Collapsible Card Grid(折叠展开面板)` | **空**: 楼盘不存在 / **加载**: 局部 Skeleton / **错误**: Toast / **权限**: 全员可见 | +| **区域/商圈管理** | `/regions/` | 设置页 | `Tree Select / Cascading Dropdown(树形下拉选择器)`, `Dynamic Form Table(动态可增删行表格)` | **空**: 暂无配置 / **加载**: 树形 Skeleton / **错误**: 保存失败 Toast / **权限**: 仅系统管理员 | + +--- + +## 7. 组织人事模块 (Org / Staff) + +> 权威来源:[`UI_DESIGN/组织人事管理/组织人事_UI.md`](./组织人事管理/组织人事_UI.md) + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **组织架构** | `/org/` | 树形页 | `Tree Select / Cascading Dropdown(树形下拉选择器)`, `Drawer / Slide-over Panel(右侧抽屉面板)` | **空**: 暂无门店 / **加载**: 树形 Skeleton / **错误**: Toast / **权限**: 仅管理员 | +| **人员列表** | `/staff/` | 列表页 | `Data Table(数据表格)`, `Column Visibility Panel(自定义列显示)`, `Pagination(分页组件)`, `Toolbar(操作工具栏)` | **空**: 暂无员工 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅管理员/店长 | +| **人员详情** | `/staff//` | 详情页 | `Inline Edit Mode(页面级读写切换)`, `Collapsible Card Grid(折叠展开面板)` | **空**: 人员不存在 / **加载**: 局部 Skeleton / **错误**: Toast / **权限**: 仅本人/上级/管理员 | + +--- + +## 8. 权限管理模块 (Permission) + +> 权威来源:[`UI_DESIGN/权限管理/权限管理_UI.md`](./权限管理/权限管理_UI.md) + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **角色权限配置** | `/permissions/roles/` | 设置页 | `Tab Navigation(标签页导航)`, `Dynamic Form Table(动态可增删行表格)`, `Drawer / Slide-over Panel(右侧抽屉面板)` | **空**: 暂无角色 / **加载**: 列表 Loading / **错误**: 配置失败 Toast / **权限**: 仅系统管理员 | +| **人员权限分配** | `/permissions/assignments/` | 列表页 | `Data Table(数据表格)`, `Column Visibility Panel(自定义列显示)`, `Tree Select / Cascading Dropdown(树形下拉选择器)` | **空**: 暂无分配 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅系统管理员 | + +--- + +## 9. 系统配置模块 (Setting) + +> 权威来源:[`UI_DESIGN/系统配置/系统配置_UI.md`](./系统配置/系统配置_UI.md) / [`首页设置_UI.md`](./系统配置/首页设置_UI.md) + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **全局参数配置** | `/settings/params/` | 设置页 | `Inline Edit Mode(页面级读写切换)`, `Dynamic Form Table(动态可增删行表格)`, `Multi-Table Independent Pagination(同页多表格独立分页)` | **空**: 暂无参数 / **加载**: 局部 Skeleton / **错误**: 保存失败 Toast / **权限**: 仅系统管理员 | +| **首页设置** | `/settings/homepage/` | 设置页 | `Inline Edit Mode(页面级读写切换)`, `Collapsible Card Grid(折叠展开面板)` | **空**: 默认模板 / **加载**: 局部 Skeleton / **错误**: 保存失败 Toast / **权限**: 仅系统管理员 | +| **关注楼盘** | `/settings/watch-complexes/` | 设置页 | `Tree Select / Cascading Dropdown(树形下拉选择器)`, `Multi-select Tag Input(多选标签选择器)` | **空**: 暂未关注 / **加载**: Tree Skeleton / **错误**: 保存失败 Toast / **权限**: 仅本人/管理员 | +| **(Partial) 字段自定义** | `/settings/_partials/fields/` | Drawer | `Drawer / Slide-over Panel(右侧抽屉面板)`, `Sortable Table with Drag Handle(带拖拽手柄的可排序表格)` | **加载**: Drawer Loading / **错误**: 422 Toast / **权限**: 仅系统管理员 | + +--- + +## 10. 平台管理后台 (Platform Admin) + +> 仅由平台超级管理员在 Public Schema 下访问,独立子域,统一前缀 `/admin/`。 +> 权威来源:[`UI_DESIGN/平台管理后台/平台管理后台_UI.md`](./平台管理后台/平台管理后台_UI.md) §3.1。 + +| 页面名称 | URL 路径 | 页面类型 | 复用组件 | 空/加载/错误/权限状态描述 | +| :--- | :--- | :--- | :--- | :--- | +| **平台管理员登录** | `/admin/login/` | 基础页 | 无 | **空**: 不适用 / **加载**: 提交按钮 Loading / **错误**: 凭证错误内联提示 / **权限**: 公开 | +| **平台仪表盘** | `/admin/` | 统计页 | `Collapsible Card Grid(折叠展开面板)` | **空**: 暂无数据 / **加载**: Skeleton / **错误**: Toast / **权限**: 仅平台管理员 | +| **租户列表** | `/admin/tenants/` | 列表页 | `Data Table(数据表格)`, `Column Visibility Panel(自定义列显示)`, `Pagination(分页组件)`, `Toolbar(操作工具栏)` | **空**: 暂无租户 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营 | +| **新建租户** | `/admin/tenants/new/` | 编辑页(独立) | `Tab Navigation(标签页导航)`, `Multi-select Tag Input(多选标签选择器)` | **空**: 字段默认值 / **加载**: 表单 Loading / **错误**: 422 字段级 / **权限**: 仅平台超管 | +| **租户详情 - 基本信息** | `/admin/tenants//` | 详情页 | `Tab Navigation(标签页导航)`, `Collapsible Card Grid(折叠展开面板)`, `Inline Edit Mode(页面级读写切换)` | **空**: 租户不存在 / **加载**: 局部 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营 | +| **租户详情 - 用户管理** | `/admin/tenants//users/` | 子列表 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无用户 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营 | +| **租户详情 - 套餐信息** | `/admin/tenants//plan/` | 子设置页 | `Inline Edit Mode(页面级读写切换)`, `Modal Dialog(模态对话框)` | **空**: 默认套餐 / **加载**: 局部 Skeleton / **错误**: 422 字段级 / **权限**: 仅平台超管 | +| **租户详情 - 监控** | `/admin/tenants//monitoring/` | 子统计页 | `Collapsible Card Grid(折叠展开面板)` | **空**: 暂无指标 / **加载**: 图表 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营 | +| **租户详情 - 备份记录** | `/admin/tenants//backups/` | 子列表 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无备份 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营 | +| **租户详情 - 操作历史** | `/admin/tenants//history/` | 子列表 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无记录 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营/审计 | +| **系统版本管理** | `/admin/system/versions/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)`, `Drawer / Slide-over Panel(右侧抽屉面板)` | **空**: 暂无版本 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅平台超管 | +| **系统备份管理** | `/admin/system/backups/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)` | **空**: 暂无备份 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营 | +| **监控与告警** | `/admin/monitoring/` | 统计页 | `Collapsible Card Grid(折叠展开面板)`, `Tab Navigation(标签页导航)` | **空**: 暂无告警 / **加载**: 图表 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营 | +| **客户端版本管理** | `/admin/client-releases/` | 列表页 | `Data Table(数据表格)`, `Pagination(分页组件)`, `Drawer / Slide-over Panel(右侧抽屉面板)` | **空**: 暂无发布 / **加载**: 列表 Skeleton / **错误**: 发布失败 Toast / **权限**: 仅平台超管 | +| **审计日志** | `/admin/audit-logs/` | 列表页 | `Data Table(数据表格)`, `Column Visibility Panel(自定义列显示)`, `Pagination(分页组件)` | **空**: 暂无日志 / **加载**: 表格 Skeleton / **错误**: Toast / **权限**: 仅平台超管/运营/审计 | +| **管理员设置** | `/admin/settings/admins/` | 列表+设置 | `Data Table(数据表格)`, `Drawer / Slide-over Panel(右侧抽屉面板)`, `Inline Edit Mode(页面级读写切换)` | **空**: 暂无管理员 / **加载**: 表格 Skeleton / **错误**: 422 字段级 / **权限**: 仅平台超管 | + +--- + +## X. 非 MVP 占位 (v2 规划) + +- **移动端适配页**:`/m/` 起始的所有路由。 +- **合同管理**:`/contracts/`。 +- **财务结算**:`/finance/`。 +- **营销/三网发布**:`/marketing/`。 +- **新房模块**:`/new-houses/`。 + +--- + +## 附录 A: 页面状态定义 (Page States) + +- **空 (Empty)**: 使用 `Empty State` 设计,包含图标、说明文案及主要行动点 (CTA)。 +- **加载 (Loading)**: 优先使用 `Skeleton` 骨架屏(针对表格、详情块);按钮操作使用 `Spinner` 及禁用态。 +- **错误 (Error)**: + - 页面级:404/500 全屏错误页。 + - 异步/局部:通过 `HX-Trigger` 弹出 `fonrey:toast` 提示。 + - 表单:422 返回包含错误信息的 Partial,红色文字定位到字段。 +- **权限 (Permission)**: + - 无权访问页面:重定向至 403 页面。 + - 无权操作按钮:不渲染该按钮或置灰。 + +--- + +## 附录 B: 页面类型定义 (Page Types) + +- **列表页 (List)**: 主体为数据表格,含筛选、分页。 +- **详情页 (Detail)**: 多维展示单一资源,多为 Tab 布局。 +- **编辑页(独立) (Edit Page)**: 独立 URL 的全量表单页(非 Drawer/Modal)。 +- **设置页 (Setting)**: 多为左侧导航 + 右侧表单/列表,含读写切换。 +- **基础页 (Base)**: 独立布局,如登录、错误页。 +- **统计页 (Stats)**: 以指标卡 + 图表为主。 +- **子详情页 / 子列表 / 子设置页**: 嵌于某条记录上下文下的二级页面。 +- **Partial**: 供 HTMX 调用的局部 HTML 片段。 +- **API**: 仅返回 JSON / 局部 HTML 的端点,无独立页面。 + +--- + +## 附录 C: 与 UI_DESIGN 模块文档的对齐索引 + +| 模块 | 权威 UI 设计文档 | +| :--- | :--- | +| 登录 | [`UI_DESIGN/登录管理/登录_UI.md`](./登录管理/登录_UI.md) | +| 房源 | [`房源列表_UI.md`](./房源管理/房源列表_UI.md) / [`房源详情_UI.md`](./房源管理/房源详情_UI.md) / [`新增房源_UI.md`](./房源管理/新增房源_UI.md) | +| 客源 | [`客源列表_UI.md`](./客源管理/客源列表_UI.md) / [`客源详情_UI.md`](./客源管理/客源详情_UI.md) / [`新增客源_UI.md`](./客源管理/新增客源_UI.md) / [`编辑客源_UI.md`](./客源管理/编辑客源_UI.md) | +| 楼盘/区域 | [`楼盘列表_UI.md`](./楼盘管理/楼盘列表_UI.md) / [`楼盘详情_UI.md`](./楼盘管理/楼盘详情_UI.md) / [`区域管理_UI.md`](./楼盘管理/区域管理_UI.md) | +| 组织人事 | [`组织人事_UI.md`](./组织人事管理/组织人事_UI.md) | +| 权限 | [`权限管理_UI.md`](./权限管理/权限管理_UI.md) | +| 系统配置 | [`系统配置_UI.md`](./系统配置/系统配置_UI.md) / [`首页设置_UI.md`](./系统配置/首页设置_UI.md) | +| 平台管理后台 | [`平台管理后台_UI.md`](./平台管理后台/平台管理后台_UI.md) | diff --git a/Project/fonrey/UI_DESIGN/房源管理/房源列表_UI.md b/Project/fonrey/UI_DESIGN/房源管理/房源列表_UI.md index 4018a9bd..b755cdd9 100644 --- a/Project/fonrey/UI_DESIGN/房源管理/房源列表_UI.md +++ b/Project/fonrey/UI_DESIGN/房源管理/房源列表_UI.md @@ -94,7 +94,7 @@ #### 2.1.1 页面概述 -- **URL**:`/properties/`(query params:`tab`, `status`, `grade`, `page`, `q`, `sort`, `order` 等) +- **URL**:`/properties/`(query params:`tab`, `status`, `grade`, `cursor`, `q`, `sort`, `order` 等;分页采用 Keyset,详见 `ADR-20260604-001`) - **访问入口**:顶部全局导航栏「房源」菜单 → 默认进入全部房源列表 - **页面职责**:展示经纪人名下(或门店/全司)的房源列表,支持多维度搜索筛选、批量操作、状态快览 - **竞品参考截图**: @@ -738,7 +738,7 @@ | 左侧 | 总条数「共 N 条」 | | 中间 | 页码导航:`← 上一页 [1] [2] [3] [4] [5] … [N] 下一页 →` | | 右侧 | 每页条数「20条/页 ▾」(选项:20/50/100)+ 跳页「跳至」+ 输入框 + 「页」 | -| HTMX | `hx-get="/properties/" hx-vals='{"page": N}' hx-target="#property-list-container" hx-swap="innerHTML" hx-include="closest form"` | +| HTMX | `hx-get="/properties/" hx-vals='{"cursor": LAST_ID}' hx-target="#property-list-container" hx-swap="innerHTML" hx-include="closest form"`(Keyset 分页,`LAST_ID` 为当前页最后一条 `id`;详见 `ADR-20260604-001`。MVP 过渡期允许保留页码 UI 表现,但 `cursor` 是后端主参数) | | 当前页 | `bg-primary-600 text-white rounded-md w-8 h-8 font-medium` | | 非当前页 | `text-neutral-600 hover:bg-neutral-100 rounded-md w-8 h-8` | diff --git a/Project/fonrey/Vibe Coding项目管理技巧.md b/Project/fonrey/Vibe Coding项目管理技巧.md new file mode 100644 index 00000000..40c87b83 --- /dev/null +++ b/Project/fonrey/Vibe Coding项目管理技巧.md @@ -0,0 +1,175 @@ +这是一个非常实际的问题。用 OpenCode(或类似 AI 编码工具)做长周期项目,核心挑战是**每次会话 AI 都是"失忆"的**,你需要一套机制让它快速恢复上下文。 + +以下是一套完整的项目管理方案: + +--- + +## 核心思路:用文档作为 AI 的"外部记忆" + +AI 没有跨会话记忆,但它每次都能读文件。所以你的目标是:**把项目状态写进文件,而不是靠 AI 记住。** + +--- + +## 一、项目文档结构 + +在项目根目录维护以下几个关键文件: + +``` +project-root/ +├── CLAUDE.md # AI 行为规则 + 项目概览(OpenCode 每次自动读取) +├── TASK.md # 任务总表,P0/P1/P2 优先级 +├── PROGRESS.md # 每日进度日志(最重要) +├── ARCHITECTURE.md # 技术架构 +└── .opencode/ + └── context.md # 会话级补充上下文(可选) +``` + +--- + +## 二、最关键的文件:`PROGRESS.md` + +这是你每天结束时必须更新的文件,格式建议如下: + +```markdown +# 项目进度日志 + +## 2025-01-15(今日) + +### ✅ 已完成 +- [x] 实现了 PropertyListing 的 CRUD API +- [x] 修复了权限校验的 bug(见 commit abc123) + +### 🔄 进行中 +- [ ] 房源搜索功能(已完成 70%) + - 已做:Elasticsearch 索引配置 + - 未做:前端搜索组件、过滤器逻辑 + - 卡点:全文搜索中文分词需要引入 IK 插件 + +### ⏭️ 明日计划 +- 完成搜索前端组件 +- 开始客源管理模块的数据模型 + +### 🐛 已知问题 / 技术债 +- PropertyImage 上传后缩略图生成有时失败,原因未查 + +### 📁 今日改动的关键文件 +- `apps/listings/views.py` +- `apps/listings/serializers.py` +- `templates/listings/search.html` +``` + +--- + +## 三、`CLAUDE.md` — AI 的启动说明书 + +OpenCode 会自动读取项目根目录的 `CLAUDE.md`。把它写成"给 AI 的操作手册": + +```markdown +# Fonrey 项目 AI 开发规范 + +## 项目简介 +B2B SaaS 房产中介管理系统,Django + HTMX + Alpine.js + PostgreSQL + +## 每次会话开始前,你必须做的事 +1. 阅读 `PROGRESS.md` 了解当前进度 +2. 阅读 `TASK.md` 了解当前 Sprint 目标 +3. 确认今天要完成哪个具体任务后再写代码 + +## 技术约束 +- 前端:HTMX + Alpine.js + Tailwind,禁止引入 React/Vue +- 文件上传:统一用 FilePond +- 权限:使用自定义 RBAC,不使用 django-guardian + +## 代码规范 +- 所有新文件必须有注释说明用途 +- 每完成一个功能,更新 PROGRESS.md + +## 禁止事项 +- 不要修改 DATA_MODEL.md 中已定稳的数据表结构 +- 不要在没有确认的情况下重构已有模块 +``` + +--- + +## 四、每日工作流程 + +### 早上开始(2 分钟) + +给 AI 发这条固定的"启动 prompt": + +``` +请阅读 PROGRESS.md 和 TASK.md,告诉我: +1. 昨天完成了什么 +2. 今天应该继续做什么 +3. 有没有未解决的卡点 + +然后我们开始今天的工作。 +``` + +AI 读完文件后会给你一个摘要,你确认后直接开工。 + +### 晚上结束(5 分钟) + +让 AI 帮你更新进度日志: + +``` +今天的工作完成了,请帮我更新 PROGRESS.md: +- 完成了:[你说] +- 未完成的:[你说] +- 明天继续:[你说] +``` + +--- + +## 五、处理"卡点"和"上下文切换" + +### 当你在一个功能里遇到复杂问题时 + +创建一个临时文件 `SCRATCH.md`: + +```markdown +# 当前卡点:中文全文搜索 + +## 问题描述 +PostgreSQL tsvector 对中文分词支持差 + +## 已尝试的方案 +1. 方案A:用 zhparser - 失败,原因 xxx +2. 方案B:用 pg_jieba - 待验证 + +## 当前倾向 +方案B,明天继续 +``` + +下次会话告诉 AI:"先读 SCRATCH.md",秒回上下文。 + +### 当你需要切换模块时 + +在 PROGRESS.md 里明确标记"暂停点",记录文件名和函数名,这比描述更精确。 + +--- + +## 六、可选增强:Git commit 作为进度锚点 + +养成每完成一个小功能就 commit 的习惯,commit message 写清楚: + +``` +feat(listings): 完成房源搜索 API,支持关键词+区域过滤 + +TODO: 前端搜索组件待完成 +``` + +这样 AI 可以通过 `git log --oneline -20` 快速看到最近的工作轨迹,作为 PROGRESS.md 的补充。 + +--- + +## 总结 + +|文件|作用|更新频率| +|---|---|---| +|`CLAUDE.md`|AI 行为规范,一次写好|很少改| +|`TASK.md`|任务总表|每周调整| +|`PROGRESS.md`|每日进度日志|**每天必更新**| +|`SCRATCH.md`|当前卡点临时笔记|按需| + +**最核心的一句话:每天花 5 分钟更新 `PROGRESS.md`,比什么方法都有效。** AI 能读文件,但它读不了你的脑子——把脑子里的状态写进文件,问题就解决了。 \ No newline at end of file