From fe594c9ef59371827e231e09c712cb3ea32d47db Mon Sep 17 00:00:00 2001 From: xiong Date: Mon, 3 Aug 2026 10:27:35 +0800 Subject: [PATCH] Refactor documentation: - Remove outdated development plan from `development-plan.md`. - Update `operations.md` to include details on authorization cache configuration. - Revise `quickstart.md` for clarity on local development setup and database initialization. - Delete redundant `redis-authorization-cache.md`. - Add new `tenant-provisioning.md` to document the process of setting up a tenant from an empty database. --- AGENTS.md | 2 +- README.md | 5 +- docs/README.md | 28 +- docs/architecture/overview.md | 12 +- docs/architecture/security-and-tenancy.md | 14 +- docs/assets/tiku-backend-architecture.svg | 26 +- docs/development-plan.md | 335 ---------------------- docs/operations.md | 2 + docs/quickstart.md | 196 ++++--------- docs/redis-authorization-cache.md | 39 --- docs/tenant-provisioning.md | 210 ++++++++++++++ 11 files changed, 329 insertions(+), 540 deletions(-) delete mode 100644 docs/development-plan.md delete mode 100644 docs/redis-authorization-cache.md create mode 100644 docs/tenant-provisioning.md diff --git a/AGENTS.md b/AGENTS.md index 0e99e5f..097dc7c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Project Structure & Module Organization -`TIKU-BACKEND.slnx` is a .NET 10 modular monolith. `Tiku.Domain` holds entities; `Tiku.Application` defines use-case contracts; `Tiku.Infrastructure` contains EF Core, PostgreSQL, Redis, authentication, jobs, and external integrations. `Tiku.Api` and `Tiku.Worker` are independent runtimes; `Tiku.DbMigrator` owns migration and bootstrap work. Tests live in `Tiku.UnitTests` and `Tiku.IntegrationTests`. The React/TypeScript admin UI is in `Tiku.PlatformAdmin.Web`; documentation and deployment assets live under `docs/` and `deploy/`. +`TIKU-BACKEND.slnx` is a .NET 10 modular monolith. `Tiku.Domain` holds entities; `Tiku.Application` defines use-case contracts; `Tiku.Infrastructure` contains EF Core, PostgreSQL, Redis, jobs, and external integrations. `Tiku.Api` and `Tiku.Worker` are independent runtimes; `Tiku.DbMigrator` owns migration and bootstrap work. Tests live in `Tiku.UnitTests` and `Tiku.IntegrationTests`. The React/TypeScript UI is in `Tiku.PlatformAdmin.Web`; documentation and deployment assets live under `docs/` and `deploy/`. Keep dependencies flowing `Domain <- Application <- Infrastructure`. Database access, SDK integrations, and secret handling belong in Infrastructure, not controllers or domain types. diff --git a/README.md b/README.md index e1d0c47..add2f1b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ dotnet run --project Tiku.Api dotnet run --project Tiku.Worker ``` -Development 首次迁移会创建平台管理员 `admin@tiku.local`,随机临时密码只在 DbMigrator 首次运行的终端输出。完整步骤见[本地开发与运行](docs/quickstart.md)。 +默认 Development seed 会在尚无平台角色绑定时创建平台管理员 `admin@tiku.local` 和演示数据;随机临时密码只在首次创建时输出。日常步骤见[本地开发快速上手](docs/quickstart.md),不含演示数据的完整 SaaS 验收见[空数据库到租户建站验收](docs/tenant-provisioning.md)。 默认开发入口: @@ -86,6 +86,7 @@ PostgreSQL 特有的迁移、事务、约束和跨租户不变量必须由 `Tiku - [系统架构与业务边界](docs/architecture/overview.md) - [认证、授权与租户隔离](docs/architecture/security-and-tenancy.md) - [配置与后台任务](docs/operations.md) -- [本地开发与运行](docs/quickstart.md) +- [本地开发快速上手](docs/quickstart.md) +- [空数据库到租户建站验收](docs/tenant-provisioning.md) 接口、DTO、请求参数和响应模型以运行时 OpenAPI/Scalar 为准;文档不再维护手写接口清单或迁移过程记录。 diff --git a/docs/README.md b/docs/README.md index ba07da3..9180dbc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,14 +2,25 @@ 这里仅记录当前代码已经实现的架构、运行方式和维护约束。接口细节以 Development 环境的 OpenAPI/Scalar 为准,数据库结构以 EF Core Migration 和模型快照为准。 -## 阅读入口 +## 从这里开始 -| 文档 | 内容 | 适合谁 | -| --- | --- | --- | -| [本地开发与运行](quickstart.md) | PostgreSQL 初始化、启动 API、验证命令、常见问题 | 新开发者 | -| [系统架构与业务边界](architecture/overview.md) | 项目依赖、运行时组件、当前业务模块和后台任务链路 | 开发与评审人员 | -| [认证、授权与租户隔离](architecture/security-and-tenancy.md) | 登录、Session、JWT、Cookie/CSRF、Realm、RBAC、Capability、DataScope、租户隔离 | API 与安全开发者 | -| [配置与后台任务](operations.md) | 环境配置、Production 启动门禁、Redis、Worker、ClamAV 和健康检查 | 开发与运维人员 | +| 目标 | 文档 | +| --- | --- | +| 首次拉取代码,启动本地开发环境 | [本地开发快速上手](quickstart.md) | +| 从空库验收租户创建、Owner 激活和站点发布 | [空数据库到租户建站验收](tenant-provisioning.md) | +| 理解项目分层、运行时和业务边界 | [系统架构与业务边界](architecture/overview.md) | +| 修改认证、权限或租户数据 | [认证、授权与租户隔离](architecture/security-and-tenancy.md) | +| 部署 API/Worker、配置依赖或排查任务 | [配置与后台任务](operations.md) | + +## 文档边界 + +- `README.md`:项目定位、最短启动入口和仓库地图。 +- `docs/quickstart.md`:非破坏性的日常开发路径。 +- `docs/tenant-provisioning.md`:会创建或清理本地数据库的完整 SaaS 验收路径。 +- `docs/architecture/**`:当前代码的稳定设计与安全边界。 +- `docs/operations.md`:部署配置、后台处理、健康检查和恢复演练。 + +路线图、阶段计划、代码评审快照和历史迁移过程不放在当前实现文档中;需要保留时应进入 Issue、项目管理系统或明确标记的历史归档。 ## 权威来源 @@ -25,4 +36,5 @@ 2. 新增租户实体时,同时验证 Query Filter、租户唯一索引、组合外键和写入拦截器。 3. 数据库结构变更必须生成 EF Core Migration,并检查 migration script 和 pending model changes。 4. 不在文档中保存连接密码、JWT 私钥、证书密码、Provider 密钥或平台管理员临时密码。 -5. 不再维护阶段路线图、旧后端接口对比、评审快照或开发过程记录。 +5. 代码行为变化时更新对应主题,不新增内容重叠的临时说明文件。 +6. 不维护阶段路线图、旧后端接口对比、评审快照或开发过程记录。 diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index c1315cc..8a6f4ec 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -37,8 +37,8 @@ `Tiku.Api/Program.cs` 只负责组合服务、构建应用和启用请求管线。管线的关键顺序是: ```text -Forwarded Headers - -> HTTPS / 压缩 / 静态文件 +结构化请求日志 / 数据库请求指标 / 异常处理 + -> Forwarded Headers / HTTPS / 响应压缩 -> Routing / CORS -> Host 租户解析 -> 浏览器 CSRF @@ -49,7 +49,7 @@ Forwarded Headers -> Controllers ``` -OpenAPI 和 Scalar 只在 Development 映射。平台管理端位于独立的 `Tiku.PlatformAdmin.Web` React 工程,由 OpenAPI 生成接口契约并单独构建、部署;`Tiku.Api` 不再托管平台前端静态文件。 +OpenAPI 和 Scalar 只在 Development 映射。平台管理端位于独立的 `Tiku.PlatformAdmin.Web` React 工程,由 OpenAPI 生成接口契约;Development 通过 `Tiku.Api.csproj` 的 SPA Proxy 启动 Vite,Production 仍应独立构建和部署,API 不托管平台前端静态文件。 ### DbMigrator @@ -58,14 +58,14 @@ OpenAPI 和 Scalar 只在 Development 映射。平台管理端位于独立的 `T 1. 解析 `ConnectionStrings:Database` 或 `DATABASE_URL`。 2. 进入带审计原因的 System Scope。 3. 执行 `Database.MigrateAsync()`。 -4. seed 内置 SaaS Feature、PermissionModule、BackendPermission 和 BackendMenu 目录。 -5. Development 全新数据库自动 seed 平台管理员;非 Development 仅在显式传入 `--bootstrap-platform-admin` 时创建管理员。 +4. seed 内置 SaaS Feature、PermissionModule、BackendPermission、BackendMenu 和 `starter` 套餐目录。 +5. 默认 Development seed 在尚无平台角色绑定时创建平台管理员与演示数据;`--skip-development-seed` 可关闭该行为,`--bootstrap-platform-admin` 用于显式创建首个平台管理员。 API 不自动迁移数据库。 ### Worker 后台处理 -`Tiku.Worker` 独立注册六个 Hosted Service,API 进程不注册后台循环: +`Tiku.Worker` 独立注册六个 Hosted Service。API 不承载这些生产后台循环;仅 Development 会额外注册 `.localhost` 域名生命周期旁路,方便本地建站验收。 | Worker | 周期 | 当前职责 | | --- | --- | --- | diff --git a/docs/architecture/security-and-tenancy.md b/docs/architecture/security-and-tenancy.md index 864de44..3e4b3ff 100644 --- a/docs/architecture/security-and-tenancy.md +++ b/docs/architecture/security-and-tenancy.md @@ -83,7 +83,7 @@ Development 默认平台 Host 是 `localhost` 和 `127.0.0.1`。Production 启 新租户创建时必须提供主域名。域名统一转换为小写 ASCII/IDN Host,并使用随机 32 字节 Base64Url TXT 值验证所有权;只有 DNS 与 TLS 都成功后才进入 `Active`。平台领取 Owner 激活链接前还会校验租户、Active 主域名和试用/订阅状态。 -激活链接格式固定为 `https://{primaryHost}/activate/{activationId}#token={token}`。Token 位于 fragment,不进入 HTTP 请求、服务器访问日志或 Referer;PostgreSQL 只保存 SHA-256 哈希。Grant 绑定签发时的 `DomainId`,浏览器激活要求请求 Host、已解析 Tenant、Grant 和 Active 主域名完全一致。并发签发由事务 advisory lock 和部分唯一索引收敛为一个有效 Grant;幂等重放只返回 Grant ID 与过期时间,不能恢复明文。 +Production 默认激活链接为 `https://{primaryHost}/activate/{activationId}#token={token}`;Development 可通过专用 localhost 模板指向租户前端端口。Token 位于 fragment,不进入 HTTP 请求、服务器访问日志或 Referer;PostgreSQL 只保存 SHA-256 哈希。Grant 绑定签发时的 `DomainId`,浏览器激活要求请求 Host、已解析 Tenant、Grant 和 Active 主域名完全一致。并发签发由事务 advisory lock 和部分唯一索引收敛为一个有效 Grant;幂等重放只返回 Grant ID 与过期时间,不能恢复明文。 浏览器激活在受审计事务中消费 Grant、设置密码、清除强制改密状态并创建数据库 Session。成功响应仅写入 HttpOnly access/refresh Cookie 与可读 CSRF Cookie,不向 JavaScript 返回 Token Pair;密码策略失败会整体回滚,Grant 不会提前消费。 @@ -124,6 +124,18 @@ Development 默认平台 Host 是 `localhost` 和 `127.0.0.1`。Production 启 - 租户、套餐和 Feature 变更在数据库提交后直接失效当前 API 进程与 Redis 中的相关缓存。 - 后台任务在业务事务提交后持久化到 PostgreSQL,独立 Worker 使用租约执行;延时和重试由 `RunAfter` 控制。 +## Redis 授权缓存模式 + +`Security:AuthorizationCache:Mode` 支持三种当前实现模式: + +- `Disabled`:直接以 PostgreSQL 完成安全状态和权限读取。 +- `Shadow`:PostgreSQL 决策仍为准,同时读取、回填并比较 Redis 结果。 +- `Active`:优先读取 Redis 安全状态与权限快照;缓存缺失或 Redis 不可用时回退 PostgreSQL。 + +缓存键按环境、realm、租户、用户和 Session 隔离,不保存 JWT、Refresh Token、手机号或邮箱。权限快照键包含持久化授权版本;用户、成员、租户、Session 或角色权限变化会推进版本并触发缓存失效。提交后若 Redis 失效失败,事件保留在 PostgreSQL,由 `AuthorizationCacheInvalidationWorker` 重试;旧版本不会覆盖新版本。 + +Redis 不是授权事实源。Redis 与 PostgreSQL 同时无法完成安全校验时返回 503 `auth_security_unavailable`,不能使用本地旧快照放行。 + ## 安全配置门禁 Production 还会在启动时验证 Redis、Data Protection 证书、租户 Secret master key、短信 pepper、CORS 和外部服务配置。完整配置入口见[配置与后台任务](../operations.md)。 diff --git a/docs/assets/tiku-backend-architecture.svg b/docs/assets/tiku-backend-architecture.svg index 5beb9e1..a5b9313 100644 --- a/docs/assets/tiku-backend-architecture.svg +++ b/docs/assets/tiku-backend-architecture.svg @@ -1,6 +1,6 @@ TIKU Backend 当前技术架构图 - TIKU Backend 是 ASP.NET Core 模块化单体。API 承载 HTTP 与后台 Hosted Service,DbMigrator 负责迁移,运行时依赖 PostgreSQL、Redis 和外部服务。 + TIKU Backend 是 ASP.NET Core 模块化单体。API 承载 HTTP,Worker 独立处理后台任务,DbMigrator 负责迁移,运行时依赖 PostgreSQL、Redis 和外部服务。 @@ -46,7 +46,7 @@ P 平台管理端 - /platform-admin 静态管理端 + React / Vite 管理端 平台账号 · SaaS 运营 @@ -79,12 +79,12 @@ API Tiku.Api ASP.NET Core Controller API - OpenAPI / Scalar · 静态文件 + OpenAPI / Scalar(Development) 请求管线(按执行顺序) 1 日志 / 异常 / Forwarded Headers / HTTPS - 2 Static / Routing / CORS + 2 响应压缩 / Routing / CORS 3 可信 Host 租户解析 / Browser CSRF 4 JWT / 认证分区限流 / Rate Limiter 5 CurrentPrincipal / Authorization / Feature @@ -148,18 +148,18 @@ 实现 Application 接口并操作 Domain - 单体后台处理与迁移 + 后台处理与迁移 BG - API Hosted Services - 域名 DNS / TLS 生命周期 - SaaS 订阅生命周期 - Feature 用量校准 - 后台任务租约执行 - 与 HTTP 共用 API 进程 + Tiku.Worker + 独立后台运行时 + 域名 / 订阅 / 用量 + 任务 / 授权缓存失效 + 商业账务 / Worker 心跳 + PostgreSQL 锁与任务租约 @@ -202,7 +202,7 @@ PostgreSQL 任务租约 即时任务 / RunAfter SKIP LOCKED / 重试 - API Hosted Service 执行 + Tiku.Worker 独立执行 对象存储 @@ -232,5 +232,5 @@ - 项目依赖方向:Api → Application + Infrastructure | DbMigrator → Infrastructure | Infrastructure → Application + Domain | Application → Domain + 项目依赖方向:Api / Worker → Application + Infrastructure | DbMigrator → Infrastructure | Infrastructure → Application + Domain | Application → Domain diff --git a/docs/development-plan.md b/docs/development-plan.md deleted file mode 100644 index 3b3cd0e..0000000 --- a/docs/development-plan.md +++ /dev/null @@ -1,335 +0,0 @@ -# TIKU Backend 后续开发计划 - -## 1. 计划目标 - -后续开发围绕教育业务闭环、SaaS 产品化、可靠性、性能和可运营性推进。每个阶段必须形成可独立验收的业务结果,不以接口数量或数据表数量作为完成标准。 - -总体目标: - -- 建立服务端可信的练习、评分、报告和学习分析链路。 -- 完成内容生产、教学组织、学生学习和教师干预闭环。 -- 打通套餐、权益、商品、订单、支付和教育资源访问。 -- 提供平台端、租户端、教师端和学生端可实际使用的工作流。 -- 建立真实 PostgreSQL、Redis 和对象存储路径下的质量与性能门禁。 -- 保持模块化单体边界,在明确出现独立扩缩容需求前不拆分微服务。 - -## 2. 实施原则 - -- PostgreSQL 是业务状态、权限、权益、余额、订单和任务状态的权威来源。 -- Redis 只用于缓存、限流和可重建的加速数据,不作为授权或交易事实来源。 -- 客户端提交的用户、租户、角色、正确性、价格、权益和状态均不可信,必须由服务端解析或计算。 -- 所有可能被网络重试的写操作必须具备幂等语义。 -- 所有状态转换必须定义合法前置状态,并通过事务或比较并交换更新保证并发安全。 -- 所有租户数据访问必须同时满足租户隔离、操作权限、DataScope、套餐权益和资源可见性规则。 -- 导入、导出、聚合、提醒、扫描等长任务统一进入 `background_jobs`。 -- 前端接口以实时 OpenAPI 为唯一契约来源,不维护第二套手写接口模型。 -- 每个行为变更都要有回归测试;PostgreSQL 特有约束必须使用真实 PostgreSQL 验证。 - -## 3. 阶段总览 - -| 阶段 | 主题 | 建议周期 | 主要结果 | -| --- | --- | ---: | --- | -| P0 | 学习核心链路可信化 | 2 个迭代 | 服务端判分、幂等答题、并发安全交卷、不可变报告 | -| P1 | 内容生产与练习编排 | 2 个迭代 | 题目生命周期、题集、练习蓝图、导入发布闭环 | -| P2 | 教学组织与教师运营 | 2~3 个迭代 | 班级、任务、提醒、学情和学生干预 | -| P3 | 学生学习与个性化 | 2~3 个迭代 | 错题复习、间隔学习、自适应练习和成长激励 | -| P4 | SaaS 权益与商业闭环 | 2 个迭代 | 套餐权益、教育商品、支付、退款和激活码联动 | -| P5 | 产品界面与契约交付 | 持续并行 | 四类用户拥有完整可操作工作流 | -| P6 | 性能、扩展与可观测性 | 持续并行 | 业务压测、聚合读模型、多实例安全和 SLO 告警 | - -周期用于安排依赖关系,不作为压缩验收范围的依据。阶段可以并行准备,但不得越过前置发布门禁。 - -## 4. P0:学习核心链路可信化 - -### 4.1 题目与评分快照 - -- 为练习会话题目保存题目 ID、版本 ID、题型、题干、选项、标准答案、解析、分值和评分规则快照。 -- 会话创建后不得因题库后续编辑而改变本次练习的评分结果。 -- 学生会话详情只能返回安全投影,不返回标准答案、解析或选项正确标记。 -- 为单选、多选、判断、填空建立独立服务端评分器。 -- 主观题支持 `PendingReview`、`TeacherReviewed` 和 `SelfPractice` 等明确状态。 -- 自评结果只能用于练习反馈,不计入权威成绩、积分和排行榜。 - -### 4.2 答题幂等与并发控制 - -- 答题 DTO 增加 `IdempotencyKey`、`ExpectedSessionVersion` 和 `ClientSequence`。 -- 建立答题幂等记录,保存操作类型、请求哈希、响应快照和完成状态。 -- 相同幂等键与相同请求体必须重放原响应。 -- 相同幂等键与不同请求体必须返回明确冲突。 -- 练习会话增加并发版本和最后客户端序列。 -- 使用数据库唯一约束和比较并交换更新阻止重复答案、乱序写入和多设备并发覆盖。 -- 明确答案修改策略:允许覆盖时保留版本历史;不允许覆盖时返回稳定错误码。 - -### 4.3 交卷与报告状态机 - -- 定义 `Active → Scoring → Submitted`、`Active → Expired`、`Active → Cancelled` 状态转换。 -- 交卷请求增加幂等键,并保证同一会话只能生成一份权威报告。 -- 评分、错题更新、积分事件和报告生成在同一事务边界内提交,或通过同事务创建的后台任务可靠续办。 -- 报告发布后保持不可变;重新评分必须生成新版本并记录原因、操作者和审计事件。 -- 处理评分中断、任务重试和超时租约回收。 - -### 4.4 P0 测试与发布门禁 - -- 覆盖重复答题、幂等冲突、旧版本、乱序序列、双设备并发和重复交卷。 -- 覆盖跨租户、非本人会话、过期会话、已提交会话和未授权题目。 -- 覆盖学生响应不泄露答案及解析。 -- 覆盖各客观题型的服务端判分和边界输入。 -- 使用真实 PostgreSQL 验证唯一约束、事务竞争和失败回滚。 -- 完成迁移首次执行及幂等第二次执行验证。 - -完成标准:客户端无法伪造成绩;网络重试不会产生重复答案、重复报告、重复积分或重复错题计数。 - -## 5. P1:内容生产与练习编排 - -### 5.1 内容生命周期 - -- 统一题目、目录节点、题集和练习蓝图的 `Draft → Review → Published → Archived` 生命周期。 -- 增加乐观版本、状态前置校验、发布审计和归档恢复规则。 -- 支持题目版本对比、引用关系查看和安全回滚。 -- 发布前校验题干、选项、答案、解析、分类、资源和题型结构完整性。 -- 已被历史练习引用的版本不得物理删除。 - -### 5.2 题集与练习蓝图 - -- 支持人工题集、动态规则题集和固定快照题集。 -- 练习蓝图支持知识点、题型、难度、题量、分值、随机种子和去重规则。 -- 创建练习时记录蓝图版本、抽题结果和随机种子,确保结果可重放。 -- 增加题量不足、资源不可见、跨租户引用和已归档内容的失败关闭规则。 - -### 5.3 导入与批量运营 - -- 导入流程拆分为上传、解析、预检、确认、执行和结果下载。 -- 预检结果精确到行、字段和错误代码。 -- 导入支持幂等键、来源哈希和重复内容策略。 -- 批量发布、归档和分类必须提供影响范围预览。 -- 大批量任务进入后台任务系统,并支持进度、取消、重试和结果资产。 - -### 5.4 内容质量指标 - -- 记录题目使用次数、作答人数、正确率、平均耗时、跳过率和争议率。 -- 为低质量、异常高正确率、异常低正确率和长期未使用题目提供运营筛选。 -- 质量指标采用异步增量聚合,不在学生提交请求中执行大范围统计。 - -完成标准:运营人员可以从草稿创建到发布、组卷、导入和质量复盘完成完整工作流。 - -## 6. P2:教学组织与教师运营 - -### 6.1 班级与成员 - -- 完善班级、教师、助教、学生和分组模型。 -- 支持邀请、批量导入、转班、退班、冻结和历史成员查询。 -- 所有成员变更记录操作者、原因、时间和前后状态。 -- DataScope 支持按校区、部门、班级和本人范围过滤。 - -### 6.2 学习任务 - -- 支持练习、每日一练、作业和考试任务。 -- 支持目标班级、分组、指定学生、发布时间、截止时间、补交和重做策略。 -- 发布任务时固定内容或蓝图版本,避免后续编辑改变已发布任务。 -- 学生任务列表明确返回未开始、进行中、已提交、已逾期和已批改状态。 - -### 6.3 提醒与通知 - -- 支持任务发布、即将截止、逾期、批改完成和权益到期提醒。 -- 提醒任务使用幂等键和领取租约,防止多实例重复发送。 -- 通知记录渠道、模板版本、发送状态、失败原因和重试次数。 -- 支持租户级通知策略、免打扰时段和渠道开关。 - -### 6.4 学情与干预 - -- 建立学生、班级、知识点和任务维度的学习统计。 -- 支持未完成、连续退步、薄弱知识点和异常作答行为规则。 -- 风险命中后生成教师待办,可记录联系、备注、处理结果和下次跟进时间。 -- 教师只能查看 DataScope 允许的学生和班级。 - -完成标准:教师能够发布任务、查看进度、完成批改、识别风险并记录干预结果。 - -## 7. P3:学生学习与个性化 - -### 7.1 错题复习 - -- 错题记录包含知识点、错误次数、最近错误、最近复习、掌握状态和下一次复习时间。 -- 区分未掌握、学习中、待巩固和已掌握状态。 -- 复习结果更新间隔,不以单次答对立即永久解决。 -- 错题复习生成稳定会话,并记录生成规则版本。 - -### 7.2 词汇与间隔学习 - -- 定义明确的间隔重复算法、等级、下次复习时间和遗忘重置规则。 -- 复习计划按到期时间、掌握程度和每日上限生成。 -- 算法版本和关键输入写入学习事件,保证结果可解释。 - -### 7.3 自适应练习 - -- 建立用户知识点掌握度读模型。 -- 根据最近表现、题目难度、重复间隔和任务目标选择题目。 -- 推荐结果保存输入快照、算法版本、候选集合和最终选择。 -- 提供固定规则回退,推荐服务异常时仍可生成可用练习。 - -### 7.4 成长与激励 - -- 徽章、连续学习、积分和阶段目标使用可配置规则。 -- 奖励发放必须幂等,并记录触发事件和规则版本。 -- 排行榜支持租户、班级、时间范围和隐私开关。 -- 防止通过重复提交、回放请求或自评结果刷取奖励。 - -完成标准:学生可以获得稳定、可解释且不会重复奖励的个性化学习计划。 - -## 8. P4:SaaS 权益与商业闭环 - -### 8.1 统一权益判定 - -- 建立统一权益服务,组合操作权限、DataScope、套餐 Feature、额度、区域策略和灰度状态。 -- 统一返回拒绝原因和可观测诊断信息。 -- 前端 bootstrap 只展示最终可用能力,但后端仍独立执行完整判定。 -- 权益变更后可靠失效本地缓存和 Redis 缓存。 - -### 8.2 教育商品与权益发放 - -- 商品可绑定课程、题库、练习次数、有效期和会员等级。 -- 支付成功后幂等发放权益。 -- 退款、撤销、订单关闭和订阅到期执行明确的权益回收或冻结策略。 -- 保留每次权益变更的来源订单、规则、操作者和审计记录。 - -### 8.3 激活码 - -- 支持批次、渠道、数量、有效期、领取限制和适用租户。 -- 激活过程使用事务和唯一约束,防止重复核销。 -- 激活结果与权益服务联动,并支持撤销审计。 -- 提供批次使用率、渠道效果和异常核销报表。 - -### 8.4 租户自助运营 - -- onboarding 使用可恢复状态机覆盖租户、Owner、域名、品牌、套餐和支付配置。 -- 域名验证、证书和网关配置进入后台任务并保留诊断信息。 -- 主题、导航和模块配置支持草稿、预览、发布、版本和回滚。 -- 平台支持面向指定租户查看能力、用量、账务、任务和配置异常。 - -完成标准:租户能够完成开通、配置、购买、使用、续费和故障诊断,不依赖人工修改数据库。 - -## 9. P5:产品界面与契约交付 - -### 9.1 平台端 - -- 完成租户 onboarding、域名、套餐、账务、用量、题库和运营任务工作台。 -- 所有长任务显示进度、失败原因、重试和结果下载入口。 -- 提供租户能力诊断和配置版本回滚入口。 - -### 9.2 租户管理端与教师端 - -- 完成员工、角色、学生、班级、内容、任务、学情、商品和订单工作流。 -- 页面操作权限与后端权限清单保持一致。 -- 批量操作必须先展示影响范围和失败明细。 - -### 9.3 学生端 - -- 完成运行时 bootstrap、登录、任务、练习、错题、词汇、视频、权益和订单闭环。 -- 网络重试统一携带幂等键和客户端序列。 -- 明确处理会话过期、版本冲突、权益不足和任务已结束状态。 - -### 9.4 契约门禁 - -- 每次 Controller 变更后重新生成 OpenAPI 和前端类型。 -- CI 检查 OpenAPI operation、DTO 和前端生成产物是否同步。 -- 禁止前端手写后端枚举值、权限代码和请求模型。 - -完成标准:每类用户都可以在界面中完成对应业务闭环,且不存在仅有 API、没有可操作入口的已发布能力。 - -## 10. P6:性能、扩展与可观测性 - -### 10.1 查询与读模型 - -- 将学习统计的多次顺序查询改为条件聚合或专用汇总读模型。 -- 将趋势按日期、知识点和班级的聚合下推 PostgreSQL。 -- 为高频筛选建立与实际查询匹配的复合索引,并使用真实执行计划验证。 -- 控制分页上限,避免无界列表和大对象图加载。 -- 为核心端点建立单请求数据库命令数预算。 - -### 10.2 缓存策略 - -- 缓存目录、运行时配置、Feature 快照、公共内容和稳定聚合结果。 -- 缓存键必须包含租户、资源版本和影响结果的策略版本。 -- 写操作提交成功后再执行缓存失效;失效失败必须可重试。 -- 记录缓存命中率、回源率、失效延迟和热键。 - -### 10.3 后台处理与多实例 - -- 所有任务领取继续使用 PostgreSQL 租约和 `FOR UPDATE SKIP LOCKED`。 -- 生命周期扫描和周期性调度增加跨实例互斥或唯一调度记录。 -- 任务处理器必须幂等,并区分可重试和永久失败。 -- 记录任务排队时间、执行时间、重试次数、租约过期和死任务数量。 - -### 10.4 业务性能测试 - -- 保留公共缓存读和依赖就绪场景。 -- 增加带 JWT、权限、租户解析和 DataScope 的认证分页读。 -- 增加创建练习、并发答题、交卷、报告和排行榜场景。 -- 增加下单、支付回调、退款和权益发放场景。 -- 增加导入、导出和提醒后台任务积压恢复场景。 -- 结果必须报告目标速率、实际 QPS、状态分布、错误率、p95、p99、丢弃迭代和依赖指标。 -- 稳态容量测试至少持续 30 分钟,并使用接近生产的数据量和独立发压端。 - -### 10.5 可观测性与 SLO - -- 建立 API 延迟、错误率、数据库命令数、连接池等待和慢 SQL 指标。 -- 建立 Redis 延迟、命中率、连接失败和缓存失效指标。 -- 建立答题冲突、重复交卷、评分失败、支付回调失败和权益补偿指标。 -- 日志统一包含请求 ID、租户 ID、用户 ID、操作、任务 ID 和业务对象 ID,敏感值必须脱敏。 -- 为认证、练习、支付、后台任务和数据库连接池设置发布告警门槛。 - -完成标准:核心业务拥有可重复的容量基线,扩容不会导致重复任务、重复发放或状态竞争。 - -## 11. 每阶段统一交付物 - -每个阶段合并前必须同时交付: - -- Domain、Application、Infrastructure 和 API 边界清晰的实现。 -- 可审查的 EF Core Migration;特殊 PostgreSQL 约束使用手写 SQL。 -- DTO、OpenAPI operation、错误码和生成的前端类型。 -- 单元测试、API 集成测试和真实 PostgreSQL 集成测试。 -- 跨租户、越权、重复请求、并发竞争和失败回滚负向测试。 -- 必要的指标、结构化日志和运维配置。 -- 对应平台端、租户端、教师端或学生端工作流。 -- 发布、回滚、数据兼容和缓存失效说明。 - -## 12. 统一发布门禁 - -发布候选必须满足: - -```bash -dotnet restore TIKU-BACKEND.slnx -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test TIKU-BACKEND.slnx --no-build -dotnet format TIKU-BACKEND.slnx --verify-no-changes --no-restore -dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -git diff --check -``` - -涉及数据库或种子数据时还必须: - -- 在 Development 环境运行真实 `Tiku.DbMigrator`。 -- 再运行一次迁移器验证幂等性。 -- 验证迁移前后关键数据兼容性。 -- 验证真实 PostgreSQL 下的租户隔离、约束和并发行为。 - -涉及性能敏感路径时还必须: - -- 运行对应业务场景压测。 -- 对照最近一次有效基线检查吞吐、p95、p99、错误率和数据库命令数。 -- 性能退化未解释或超过阶段门槛时不得发布。 - -## 13. 建议执行顺序 - -严格按以下顺序启动开发: - -1. 服务端评分与题目快照。 -2. 答题幂等、会话版本和客户端序列。 -3. 并发安全交卷与不可变报告。 -4. 内容生命周期、题集和练习蓝图。 -5. 班级、任务、提醒、学情和教师干预。 -6. 错题间隔复习、词汇学习和自适应练习。 -7. 套餐权益、教育商品、支付和激活码联动。 -8. 完成各角色产品界面及 OpenAPI 契约门禁。 -9. 聚合读模型、业务压测、多实例安全和 SLO 告警。 - -P0 未通过发布门禁前,不应上线积分奖励、排行榜或基于正确率的推荐;这些能力依赖可信评分结果。 diff --git a/docs/operations.md b/docs/operations.md index a743cb8..cd9febb 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -55,6 +55,8 @@ dotnet run --project Tiku.DbMigrator -- --bootstrap-platform-admin Redis key 使用环境前缀;配置解析会强制 `AbortOnConnectFail=false`。Redis 不是用户、Session、权限、套餐或用量的权威数据源。 +授权缓存由 `Security:AuthorizationCache` 配置,默认 `Mode` 为 `Disabled`。切换为 `Shadow` 或 `Active` 前,应先确认 API 与 Worker 使用同一 Redis 和 PostgreSQL,并观察 `Tiku.Security.AuthorizationCache` 的 mismatch、fallback 与 Redis 延迟指标。回滚只需切回 `Disabled`,不应回退授权版本或失效事件相关数据库结构。详细故障语义见[认证、授权与租户隔离](architecture/security-and-tenancy.md#redis-授权缓存模式)。 + ## Worker 与后台处理配置 ```json diff --git a/docs/quickstart.md b/docs/quickstart.md index 2623598..9afabe9 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,47 +1,61 @@ -# 空数据库到租户建站 +# 本地开发快速上手 -本页记录 Development 环境从全新 PostgreSQL 数据库完成平台管理员、租户、Owner 激活、网站发布和学生端访问的真实流程。数据库迁移只由 `Tiku.DbMigrator` 执行,API 不会自动更新 Schema。 +本页用于日常开发:初始化依赖、迁移数据库、启动 API/Worker/平台前端并完成基础验证。若要从空数据库验收“创建租户 → Owner 激活 → 发布站点”的完整流程,请改看[空数据库到租户建站验收](tenant-provisioning.md)。 -## 1. 准备环境 +## 前置依赖 -需要 .NET 10、Node.js 24+、npm 11+、PostgreSQL、Redis,以及 `psql`、`createdb`、`dropdb`。 +- .NET 10 SDK +- Node.js 24+、npm 11+ +- PostgreSQL +- Redis(Development 可不配置;涉及分布式缓存、频控或完整验收时应启动) ```bash dotnet --version node --version npm --version pg_isready -h 127.0.0.1 -p 5432 -redis-cli -h 127.0.0.1 -p 6379 ping ``` -首次拉取代码后安装依赖: +首次拉取代码后恢复依赖: ```bash dotnet restore TIKU-BACKEND.slnx npm --prefix Tiku.PlatformAdmin.Web install -npm --prefix /path/to/tiku-saas-web install ``` -## 2. 创建空数据库 +## 配置数据库 -以下操作只针对本地数据库 `tiku`。如果它已经包含需要保留的数据,请先备份,不要执行清理命令。 +Development 未配置连接串时,默认使用当前系统用户连接本机 `tiku` 数据库: + +```text +Host=localhost;Database=tiku;Username=<当前系统用户> +``` + +首次使用可创建数据库: ```bash -dropdb --if-exists -h 127.0.0.1 -U <数据库用户> tiku createdb -h 127.0.0.1 -U <数据库用户> tiku ``` -Development 未配置连接串时默认使用当前系统用户连接本机 `tiku`。其他用户或端口应显式设置: +使用其他地址、端口或账号时,通过环境变量覆盖: ```bash export ConnectionStrings__Database='Host=127.0.0.1;Port=5432;Database=tiku;Username=<数据库用户>;Password=<本地密码>' ``` -不要把连接串、密码或 Token 写入 `appsettings*.json`、README 或 Git。 +不要把连接串、密码、Token 或私钥写入仓库。 -## 3. 初始化目录、starter 套餐和首个平台账号 +## 迁移与初始化 -先设置一次性 Bootstrap 参数,再执行生产式空库初始化: +API 不执行 Migration。数据库结构、内置权限/菜单目录和 `starter` 套餐统一由 `Tiku.DbMigrator` 初始化: + +```bash +ASPNETCORE_ENVIRONMENT=Development dotnet run --project Tiku.DbMigrator +``` + +默认 Development seed 会在尚无平台角色绑定时创建 `admin@tiku.local`、演示租户和演示运营数据;随机临时密码只在首次创建时输出,首次登录必须改密。 + +若需要不含演示数据的空库,使用显式 Bootstrap 流程,不要运行默认 Development seed: ```bash export ASPNETCORE_ENVIRONMENT=Development @@ -54,157 +68,69 @@ dotnet run --project Tiku.DbMigrator -- \ --bootstrap-platform-admin ``` -该命令按固定顺序执行: +完整空库验收步骤见[空数据库到租户建站验收](tenant-provisioning.md)。 -1. EF Core Migration; -2. Feature、Permission、菜单和额度目录; -3. 内置 `starter` 套餐及其已发布版本; -4. 可选的平台超级管理员 Bootstrap。 +## 启动运行时 -`starter` 是零元、CNY、已发布的建站基础套餐,包含 `core.backoffice` 和 `marketing.site_content`。首个平台账号使用临时密码,首次登录必须改密。 - -清除 Bootstrap 密码并再运行一次 Migrator,确认日常重复执行不会创建演示租户或重复目录: +启动 API: ```bash -unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD -unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL -unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME - -dotnet run --project Tiku.DbMigrator -- --skip-development-seed +ASPNETCORE_ENVIRONMENT=Development dotnet run --project Tiku.Api --launch-profile http ``` -Migration 需要 `citext`、`ltree` 和 `pg_trgm` 扩展。执行用户必须可以创建这些扩展,或由数据库管理员预先安装。 - -## 4. 启动真实服务 - -确保 Redis 已运行,然后从后端仓库启动 API: - -```bash -export ASPNETCORE_ENVIRONMENT=Development -export ConnectionStrings__Redis='localhost:6379,abortConnect=false' -dotnet run --project Tiku.Api --launch-profile http -``` - -API 在 Development 会同时拉起平台管理端 Vite 服务: +`Tiku.Api.csproj` 的 SPA Proxy 会在 Development 启动 `Tiku.PlatformAdmin.Web` 的 Vite 服务。默认入口: - 平台管理端: - API: - Scalar: - OpenAPI: +- Liveness: - Readiness: -不要使用 `http://localhost:5090/platform-admin/` 作为开发入口;该路径受 API 授权保护,未登录访问返回 401。 - -在 `tiku-saas-web` 仓库另开终端,使用真实 API 模式启动租户前端: +后台循环不在 API 内运行。需要处理域名、订阅、任务队列、授权缓存失效或商业账务时,另开终端启动 Worker: ```bash -VITE_DATA_MODE=api \ -VITE_DEV_API_TARGET='http://localhost:5090' \ -npm run dev -- --host 0.0.0.0 --port 5180 +ASPNETCORE_ENVIRONMENT=Development dotnet run --project Tiku.Worker ``` -浏览器始终请求同源 `/api`,Vite 只在服务端把它代理到 `VITE_DEV_API_TARGET`。代理保留原始 Host,因此 `school.localhost` 能由后端解析到正确租户。 - -## 5. 浏览器建站流程 - -### 5.1 平台首次登录 - -1. 打开 ; -2. 使用 Bootstrap 邮箱和临时密码登录; -3. 按页面要求设置新密码; -4. 进入“租户管理”。 - -### 5.2 创建租户和本地域名 - -点击“新建租户”,至少填写: - -- 租户短编码,例如 `school`; -- 租户名称; -- 主域名 `school.localhost`; -- Owner 姓名; -- Owner 邮箱或手机号。 - -不选择套餐时,后端自动使用 `starter` 和默认试用天数。Development 配置只对精确的 `localhost` 或 `*.localhost` 启用 DNS/TLS 旁路,通常数秒内显示“DNS 与 TLS 已激活”。其他域名仍走真实 DNS 和网关流程。 - -### 5.3 签发并消费 Owner 链接 - -1. 域名 Active 后点击“领取激活链接”; -2. 填写审计原因并确认; -3. 立即保存弹窗中的一次性链接; -4. 用完整链接打开 `http://school.localhost:5180/activate/...#token=...`; -5. Owner 设置密码后自动进入 `/manage/onboarding`。 - -Token 只在签发成功弹窗显示一次。租户前端读入 Fragment 后立即清除地址栏;后端消费后不能重放。平台管理员看不到 Owner 密码,也不需要审批 Owner 激活。 - -### 5.4 配置并发布 - -向导依次完成:品牌信息、模板、主题样式、页面模块、桌面/移动预览、发布上线。保存草稿不会影响学生端;发布成功后 Runtime 读取已发布配置。 - -打开或刷新 ,应看到新的品牌、导航、主题和首页模块。退出租户后台后,可在 使用 Owner 账号重新登录。 - -## 6. 预期状态 - -| 阶段 | 预期状态 | -| --- | --- | -| 刚创建域名 | `pending` | -| Development 后台任务处理完成 | 域名 `active` | -| Owner 尚未领取链接 | `ready_to_issue` | -| 链接签发 | `issued` | -| Owner 激活并登录 | 进入 `/manage/onboarding` | -| 草稿完成但未发布 | `ready_to_launch` | -| 发布完成 | 学生端显示已发布配置 | - -## 7. 常见问题 - -### `.localhost` 一直 Pending - -确认 API 使用 `ASPNETCORE_ENVIRONMENT=Development`,并加载: - -```json -{ - "TenantDomains": { - "PollSeconds": 2, - "EnableDevelopmentLocalhostBypass": true - } -} -``` - -API 日志应出现 `Processed ... pending Development tenant domains`。非 Development 环境启用该旁路会在启动时失败。 - -### 激活页只出现 OPTIONS,没有 POST - -不要把 API 地址配置为浏览器请求 Base URL。租户前端真实模式必须使用同源 `/api`,开发代理目标使用: +如需 Redis,API 与 Worker 应使用同一实例: ```bash -VITE_DEV_API_TARGET=http://localhost:5090 +export ConnectionStrings__Redis='localhost:6379,abortConnect=false' ``` -确保旧的 `VITE_API_BASE_URL` 未注入进程,并重启 Vite。 +## 验证修改 -### 激活失败后地址栏已没有 Token +```bash +dotnet build TIKU-BACKEND.slnx --no-restore +dotnet test TIKU-BACKEND.slnx --no-build +dotnet format TIKU-BACKEND.slnx --verify-no-changes --no-restore +npm --prefix Tiku.PlatformAdmin.Web run check +dotnet ef migrations has-pending-model-changes \ + --project Tiku.Infrastructure \ + --startup-project Tiku.DbMigrator \ + --no-build +git diff --check +``` -如果后端尚未消费 Token,可重新打开平台最初交付的完整链接。已经消费、过期或丢失明文时,必须由平台撤销并重新签发,不能从数据库或日志恢复。 +PostgreSQL 特有的 Migration、事务、约束和租户隔离行为必须由真实 PostgreSQL 集成测试验证,EF InMemory 不能替代。接口、DTO 和错误响应以当前运行时 OpenAPI/Scalar 为准。 -### 发布后旧标签仍显示筹备页 +## 常见问题 -草稿与已发布配置相互隔离。确认向导显示发布成功后,刷新学生端标签,让它重新请求 Runtime Bootstrap。 +### API 报数据库不可用 -### Cookie 没有建立 +确认 PostgreSQL 已启动、数据库存在,并核对 `ConnectionStrings__Database` 或 `DATABASE_URL`。非 Development 环境没有本地默认连接串。 -必须通过 `school.localhost:5180` 访问激活和后台,不能改用 `localhost:5180` 或把 `tenantId` 填进请求。浏览器 Session 使用 Secure/HttpOnly Cookie,写请求使用 CSRF 双提交 Token,前端不得降级保存 JWT。 +### 找不到平台管理员临时密码 + +默认 Development seed 和显式 Bootstrap 都只在创建账号时输出一次临时密码,后续运行不会重放。不要从日志或数据库恢复明文;应通过受控流程重置,或在确认无需保留本地数据后重建开发数据库。 ### Readiness 返回 503 -确认 PostgreSQL 和 Redis 均可访问。匿名 readiness 只返回总体状态;依赖详情需要具有 `platform:operations:view` 权限的平台账号。 +`/api/health/ready` 会检查 PostgreSQL 和已配置的 Redis。先验证数据库连接;配置 Redis 后还需确认 Redis 可访问。匿名响应不会暴露依赖详情。 -## 8. 清理 +### API 启动了但后台任务不执行 -先停止 API、平台 Vite 和租户 Vite,再只删除明确的本地数据库: +Production 和常规 Development 都需要独立运行 `Tiku.Worker`。Development 仅额外在 API 中注册本地域名生命周期旁路,不代表 API 承载全部 Worker 循环。 -```bash -dropdb -h 127.0.0.1 -U <数据库用户> tiku -``` - -该操作不可恢复,会删除本轮创建的平台账号、租户、Session、草稿和发布配置。 - -更多配置见[配置与后台任务](operations.md),安全边界见[认证、授权与租户隔离](architecture/security-and-tenancy.md)。 +下一步可阅读[系统架构与业务边界](architecture/overview.md)、[认证、授权与租户隔离](architecture/security-and-tenancy.md)和[配置与后台任务](operations.md)。 diff --git a/docs/redis-authorization-cache.md b/docs/redis-authorization-cache.md deleted file mode 100644 index c490b75..0000000 --- a/docs/redis-authorization-cache.md +++ /dev/null @@ -1,39 +0,0 @@ -# Redis 认证授权缓存 - -## 请求链路 - -受保护请求先在本地完成 JWT 验签,再通过一次 Redis MGET 校验 Session、用户、租户、成员资格和持久化授权版本。权限快照使用 60 秒进程内缓存和 5 分钟 Redis 缓存;本地快照键包含授权版本,因此撤权后的下一请求不会继续使用旧权限。PostgreSQL 始终是事实源,Redis 读取失败时绕过本地快照并回退数据库。 - -缓存不保存 JWT、Refresh Token、手机号或邮箱。Redis key 使用环境隔离前缀,并区分 platform/tenant realm、tenant、user 和 session。 - -## 配置与故障语义 - -```json -{ - "Security": { - "AuthorizationCache": { - "Mode": "Disabled", - "LocalSnapshotSeconds": 60, - "DistributedStateSeconds": 60, - "DistributedSnapshotSeconds": 300, - "JitterPercent": 20 - } - } -} -``` - -- `Disabled`:保持 PostgreSQL 权威读取,仅维护持久化授权版本。 -- `Shadow`:PostgreSQL 决策仍为准,同时读取、回填和比较 Redis 结果。 -- `Active`:Redis 为主要读取路径,缓存缺失或不可用时回退 PostgreSQL。 -- Redis 与 PostgreSQL 同时不可用时返回 `503`,错误码为 `auth_security_unavailable`。 - -生产环境的 API 和 Worker 都必须配置 `ConnectionStrings:Redis` 或 `REDIS_URL`。Worker 重试 `authorization_cache_invalidations` 中未完成的失效事件;Redis 版本写入是单调的,旧事件不会覆盖新版本。 - -## 发布与回滚 - -1. 先部署迁移,保持 `Disabled`。 -2. 切换 `Shadow`,观察 `Tiku.Security.AuthorizationCache` 指标中的 mismatch、fallback 和 Redis 延迟。 -3. 确认无非并发不一致后,对单实例启用 `Active`,再逐步扩容。 -4. 回滚时只把模式切回 `Disabled`,不回退数据库迁移。 - -RBAC 表和权限目录由 PostgreSQL 触发器在业务事务内推进授权版本并写入失效事件。任何新增的用户、成员、租户、Session 或角色权限写路径,也必须调用 `IAuthorizationStateInvalidator` 完成同步 Redis 失效。 diff --git a/docs/tenant-provisioning.md b/docs/tenant-provisioning.md new file mode 100644 index 0000000..3123f02 --- /dev/null +++ b/docs/tenant-provisioning.md @@ -0,0 +1,210 @@ +# 空数据库到租户建站验收 + +本页记录 Development 环境从全新 PostgreSQL 数据库完成平台管理员、租户、Owner 激活、网站发布和学生端访问的真实流程。数据库迁移只由 `Tiku.DbMigrator` 执行,API 不会自动更新 Schema。 + +## 1. 准备环境 + +需要 .NET 10、Node.js 24+、npm 11+、PostgreSQL、Redis,以及 `psql`、`createdb`、`dropdb`。 + +```bash +dotnet --version +node --version +npm --version +pg_isready -h 127.0.0.1 -p 5432 +redis-cli -h 127.0.0.1 -p 6379 ping +``` + +首次拉取代码后安装依赖: + +```bash +dotnet restore TIKU-BACKEND.slnx +npm --prefix Tiku.PlatformAdmin.Web install +npm --prefix /path/to/tiku-saas-web install +``` + +## 2. 创建空数据库 + +以下操作只针对本地数据库 `tiku`。如果它已经包含需要保留的数据,请先备份,不要执行清理命令。 + +```bash +dropdb --if-exists -h 127.0.0.1 -U <数据库用户> tiku +createdb -h 127.0.0.1 -U <数据库用户> tiku +``` + +Development 未配置连接串时默认使用当前系统用户连接本机 `tiku`。其他用户或端口应显式设置: + +```bash +export ConnectionStrings__Database='Host=127.0.0.1;Port=5432;Database=tiku;Username=<数据库用户>;Password=<本地密码>' +``` + +不要把连接串、密码或 Token 写入 `appsettings*.json`、README 或 Git。 + +## 3. 初始化目录、starter 套餐和首个平台账号 + +先设置一次性 Bootstrap 参数,再执行生产式空库初始化: + +```bash +export ASPNETCORE_ENVIRONMENT=Development +export TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL='<平台管理员邮箱>' +export TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD='<临时密码>' +export TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME='<显示名称>' + +dotnet run --project Tiku.DbMigrator -- \ + --skip-development-seed \ + --bootstrap-platform-admin +``` + +该命令按固定顺序执行: + +1. EF Core Migration; +2. Feature、Permission、菜单和额度目录; +3. 内置 `starter` 套餐及其已发布版本; +4. 可选的平台超级管理员 Bootstrap。 + +`starter` 是零元、CNY、已发布的建站基础套餐,包含 `core.backoffice` 和 `marketing.site_content`。首个平台账号使用临时密码,首次登录必须改密。 + +清除 Bootstrap 密码并再运行一次 Migrator,确认日常重复执行不会创建演示租户或重复目录: + +```bash +unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD +unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL +unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME + +dotnet run --project Tiku.DbMigrator -- --skip-development-seed +``` + +Migration 需要 `citext`、`ltree` 和 `pg_trgm` 扩展。执行用户必须可以创建这些扩展,或由数据库管理员预先安装。 + +## 4. 启动真实服务 + +确保 Redis 已运行,然后从后端仓库启动 API: + +```bash +export ASPNETCORE_ENVIRONMENT=Development +export ConnectionStrings__Redis='localhost:6379,abortConnect=false' +dotnet run --project Tiku.Api --launch-profile http +``` + +API 在 Development 会同时拉起平台管理端 Vite 服务: + +- 平台管理端: +- API: +- Scalar: +- OpenAPI: +- Readiness: + +不要使用 `http://localhost:5090/platform-admin/` 作为开发入口;该路径受 API 授权保护,未登录访问返回 401。 + +在 `tiku-saas-web` 仓库另开终端,使用真实 API 模式启动租户前端: + +```bash +VITE_DATA_MODE=api \ +VITE_DEV_API_TARGET='http://localhost:5090' \ +npm run dev -- --host 0.0.0.0 --port 5180 +``` + +浏览器始终请求同源 `/api`,Vite 只在服务端把它代理到 `VITE_DEV_API_TARGET`。代理保留原始 Host,因此 `school.localhost` 能由后端解析到正确租户。 + +## 5. 浏览器建站流程 + +### 5.1 平台首次登录 + +1. 打开 ; +2. 使用 Bootstrap 邮箱和临时密码登录; +3. 按页面要求设置新密码; +4. 进入“租户管理”。 + +### 5.2 创建租户和本地域名 + +点击“新建租户”,至少填写: + +- 租户短编码,例如 `school`; +- 租户名称; +- 主域名 `school.localhost`; +- Owner 姓名; +- Owner 邮箱或手机号。 + +不选择套餐时,后端自动使用 `starter` 和默认试用天数。Development 配置只对精确的 `localhost` 或 `*.localhost` 启用 DNS/TLS 旁路,通常数秒内显示“DNS 与 TLS 已激活”。其他域名仍走真实 DNS 和网关流程。 + +### 5.3 签发并消费 Owner 链接 + +1. 域名 Active 后点击“领取激活链接”; +2. 填写审计原因并确认; +3. 立即保存弹窗中的一次性链接; +4. 用完整链接打开 `http://school.localhost:5180/activate/...#token=...`; +5. Owner 设置密码后自动进入 `/manage/onboarding`。 + +Token 只在签发成功弹窗显示一次。租户前端读入 Fragment 后立即清除地址栏;后端消费后不能重放。平台管理员看不到 Owner 密码,也不需要审批 Owner 激活。 + +### 5.4 配置并发布 + +向导依次完成:品牌信息、模板、主题样式、页面模块、桌面/移动预览、发布上线。保存草稿不会影响学生端;发布成功后 Runtime 读取已发布配置。 + +打开或刷新 ,应看到新的品牌、导航、主题和首页模块。退出租户后台后,可在 使用 Owner 账号重新登录。 + +## 6. 预期状态 + +| 阶段 | 预期状态 | +| --- | --- | +| 刚创建域名 | `pending` | +| Development 后台任务处理完成 | 域名 `active` | +| Owner 尚未领取链接 | `ready_to_issue` | +| 链接签发 | `issued` | +| Owner 激活并登录 | 进入 `/manage/onboarding` | +| 草稿完成但未发布 | `ready_to_launch` | +| 发布完成 | 学生端显示已发布配置 | + +## 7. 常见问题 + +### `.localhost` 一直 Pending + +确认 API 使用 `ASPNETCORE_ENVIRONMENT=Development`,并加载: + +```json +{ + "TenantDomains": { + "PollSeconds": 2, + "EnableDevelopmentLocalhostBypass": true + } +} +``` + +API 日志应出现 `Processed ... pending Development tenant domains`。非 Development 环境启用该旁路会在启动时失败。 + +### 激活页只出现 OPTIONS,没有 POST + +不要把 API 地址配置为浏览器请求 Base URL。租户前端真实模式必须使用同源 `/api`,开发代理目标使用: + +```bash +VITE_DEV_API_TARGET=http://localhost:5090 +``` + +确保旧的 `VITE_API_BASE_URL` 未注入进程,并重启 Vite。 + +### 激活失败后地址栏已没有 Token + +如果后端尚未消费 Token,可重新打开平台最初交付的完整链接。已经消费、过期或丢失明文时,必须由平台撤销并重新签发,不能从数据库或日志恢复。 + +### 发布后旧标签仍显示筹备页 + +草稿与已发布配置相互隔离。确认向导显示发布成功后,刷新学生端标签,让它重新请求 Runtime Bootstrap。 + +### Cookie 没有建立 + +必须通过 `school.localhost:5180` 访问激活和后台,不能改用 `localhost:5180` 或把 `tenantId` 填进请求。浏览器 Session 使用 Secure/HttpOnly Cookie,写请求使用 CSRF 双提交 Token,前端不得降级保存 JWT。 + +### Readiness 返回 503 + +确认 PostgreSQL 和 Redis 均可访问。匿名 readiness 只返回总体状态;依赖详情需要具有 `platform:operations:view` 权限的平台账号。 + +## 8. 清理 + +先停止 API、平台 Vite 和租户 Vite,再只删除明确的本地数据库: + +```bash +dropdb -h 127.0.0.1 -U <数据库用户> tiku +``` + +该操作不可恢复,会删除本轮创建的平台账号、租户、Session、草稿和发布配置。 + +日常开发启动见[本地开发快速上手](quickstart.md),更多配置见[配置与后台任务](operations.md),安全边界见[认证、授权与租户隔离](architecture/security-and-tenancy.md)。