From d895e1da63a248d77257d3bd311a5853775188a1 Mon Sep 17 00:00:00 2001 From: xiong Date: Thu, 30 Jul 2026 13:30:24 +0800 Subject: [PATCH] docs: rewrite documentation from current implementation --- README.md | 172 +++----- docs/README.md | 28 ++ docs/adr/0001-authoritative-dotnet-backend.md | 32 -- ...entication-authorization-hardening-plan.md | 98 ----- .../authentication-authorization-security.md | 146 ------ .../endpoint-authorization-manifest.md | 9 - docs/architecture/overview.md | 124 ++++++ .../saas-product-and-api-roadmap.md | 230 ---------- docs/architecture/security-and-tenancy.md | 124 ++++++ docs/assets/tiku-backend-architecture.svg | 2 +- docs/migration-roadmap.md | 111 ----- docs/migration/contracts/README.md | 20 - .../contracts/operation-inventory.csv | 415 ------------------ docs/migration/phase-1-repository-baseline.md | 35 -- .../phase-2-engineering-foundation.md | 27 -- ...nant-isolation-and-shared-question-bank.md | 52 --- .../phase-4-external-provider-decoupling.md | 42 -- .../phase-5-backoffice-worker-operations.md | 47 -- ...dent-experience-and-content-consumption.md | 54 --- docs/migration/phase-8-ai-foundation.md | 61 --- ...phase-9-saas-marketplace-and-onboarding.md | 50 --- docs/operations.md | 160 +++++++ docs/quickstart.md | 126 +++--- 23 files changed, 553 insertions(+), 1612 deletions(-) create mode 100644 docs/README.md delete mode 100644 docs/adr/0001-authoritative-dotnet-backend.md delete mode 100644 docs/architecture/authentication-authorization-hardening-plan.md delete mode 100644 docs/architecture/authentication-authorization-security.md delete mode 100644 docs/architecture/endpoint-authorization-manifest.md create mode 100644 docs/architecture/overview.md delete mode 100644 docs/architecture/saas-product-and-api-roadmap.md create mode 100644 docs/architecture/security-and-tenancy.md delete mode 100644 docs/migration-roadmap.md delete mode 100644 docs/migration/contracts/README.md delete mode 100644 docs/migration/contracts/operation-inventory.csv delete mode 100644 docs/migration/phase-1-repository-baseline.md delete mode 100644 docs/migration/phase-2-engineering-foundation.md delete mode 100644 docs/migration/phase-3-tenant-isolation-and-shared-question-bank.md delete mode 100644 docs/migration/phase-4-external-provider-decoupling.md delete mode 100644 docs/migration/phase-5-backoffice-worker-operations.md delete mode 100644 docs/migration/phase-7-student-experience-and-content-consumption.md delete mode 100644 docs/migration/phase-8-ai-foundation.md delete mode 100644 docs/migration/phase-9-saas-marketplace-and-onboarding.md create mode 100644 docs/operations.md diff --git a/README.md b/README.md index 0f57511..b3057fa 100644 --- a/README.md +++ b/README.md @@ -1,155 +1,89 @@ # TIKU Backend -题库 SaaS 的正式后端。项目已收敛到 ASP.NET Core + EF Core + PostgreSQL,本仓库是后续开发的唯一目标后端。架构决策见 [ADR 0001](docs/adr/0001-authoritative-dotnet-backend.md)。 +TIKU Backend 是题库 SaaS 的 ASP.NET Core 后端,使用 EF Core 管理 PostgreSQL 数据,提供平台端、租户端和学生端 API,并由独立 Worker 处理后台任务。 ![TIKU Backend 技术架构图](docs/assets/tiku-backend-architecture.svg) -## 架构边界 +## 当前技术栈 -- 旧 NestJS / Supabase 仓库只作为业务行为和接口清单参考,不作为运行时依赖。 -- 不兼容旧 Supabase 数据库、RLS、Storage bucket、Refresh Token 或旧题单 JSON。 -- PostgreSQL 按标准 PostgreSQL 使用,不绑定 Supabase 托管能力。 -- 普通 schema 由 EF Core entity、Fluent Configuration 和 Migration 管理;`Tiku.DbMigrator` 是迁移入口。 -- 多租户隔离不使用 PostgreSQL RLS;由 Host 租户解析、EF Query Filter、写入拦截器、PostgreSQL 约束和真实 PostgreSQL 测试共同保证。 -- 身份、短信、对象存储、支付、通知和 AI 都通过 Application 层接口表达业务意图;第三方 SDK、密钥读取和 provider 细节只允许出现在 Infrastructure。 -- 租户自定义域名由可信 Host 解析,不接受 query/header 伪造切换租户。 -- 公共题库由唯一平台主体拥有;订阅有效租户可访问公共题,租户私题只属于本租户。 +- .NET 10 / ASP.NET Core Controller API +- Entity Framework Core 10 + Npgsql 10 + PostgreSQL +- ASP.NET Core Identity + RSA JWT + 数据库存储的 Session +- Scalar + OpenAPI(仅 Development 暴露) +- Redis(分布式安全频控和生产输出缓存) +- MassTransit 8 + RabbitMQ 4 + EF Core Outbox +- Serilog + OpenTelemetry +- xUnit 单元测试和真实 PostgreSQL 集成测试 -## 技术栈与目录 - -- ASP.NET Core Controller API -- Entity Framework Core + Npgsql -- PostgreSQL -- Serilog -- ZLinq -- AlibabaCloud OSS / SMS SDK -- Senparc.Weixin.* -- AlipaySDKNet.Standard -- Microsoft Semantic Kernel(仅 Infrastructure AI 边界) -- xUnit +## 解决方案结构 ```text -Tiku.Api # HTTP API、认证授权、OpenAPI/Scalar、静态原型入口 -Tiku.Application # 应用服务、用例编排、接口抽象 -Tiku.Domain # 领域实体、枚举、基础类型 -Tiku.Infrastructure # EF Core、PostgreSQL、外部服务实现 -Tiku.DbMigrator # 数据库迁移启动项目 -Tiku.Worker # 后台任务入口 -Tiku.UnitTests # 单元测试 -Tiku.IntegrationTests # API / EF / PostgreSQL 集成测试 -docs # ADR、架构说明和迁移路线 +Tiku.Api HTTP API、中间件、认证授权、OpenAPI/Scalar、静态管理端 +Tiku.Application 用例契约、应用服务接口和安全上下文 +Tiku.Domain 领域实体、枚举和值对象 +Tiku.Infrastructure EF Core、PostgreSQL、认证、消息和外部服务实现 +Tiku.Contracts API 与 Worker 之间的版本化消息契约 +Tiku.DbMigrator 数据库迁移、内置目录 seed 和平台管理员引导 +Tiku.Worker 域名、订阅、用量和后台任务处理 +Tiku.UnitTests 单元测试 +Tiku.IntegrationTests API、授权、EF 模型、迁移和真实 PostgreSQL 测试 ``` -## 平台端静态原型 +依赖方向固定为:`Domain <- Application <- Infrastructure`。`Api`、`Worker` 和 `DbMigrator` 是组合根;第三方 SDK、数据库访问和密钥处理只放在 Infrastructure。 -平台端 demo 已作为静态文件挂到 API 项目: +## 快速启动 -```text -GET /platform-admin/ -``` - -它是功能原型壳,不是正式视觉规范。当前默认连接同源真实 API,并提供平台账号密码登录和首次改密流程;token 只保存在当前浏览器标签的 `sessionStorage`。真实模式不会回退显示 Mock 数据,目前开放概览、租户、员工和审计这组已经落地后端契约的页面,账务、公共题库等页面随对应 API 实现逐步开放。 - -本地首次启动先创建 `tiku` 数据库并执行迁移: +需要 .NET 10 SDK 和 PostgreSQL。Development 默认连接本机 `tiku` 数据库,也可以通过 `DATABASE_URL` 覆盖。 ```bash createdb -h 127.0.0.1 -U "$(whoami)" tiku +dotnet restore TIKU-BACKEND.slnx +dotnet build TIKU-BACKEND.slnx --no-restore ASPNETCORE_ENVIRONMENT=Development dotnet run --project Tiku.DbMigrator +dotnet run --project Tiku.Api ``` -Development 首次迁移会通过 EF Core 官方推荐的 `UseSeeding` / `UseAsyncSeeding` 初始化平台超级管理员;Migration Lock 保证并发安全,后续重复执行迁移会幂等跳过,不会重置密码或重复创建: +Development 首次迁移会创建平台管理员 `admin@tiku.local`,随机临时密码只在 DbMigrator 首次运行的终端输出。完整步骤见[本地开发与运行](docs/quickstart.md)。 -```text -登录地址:http://localhost:5090/platform-admin/ -初始账号:admin@tiku.local -初始密码:由 Tiku.DbMigrator 安全随机生成,仅在首次初始化的终端输出一次 -``` +默认开发入口: -首次登录必须立即修改初始密码;正式密码至少 8 位,并同时包含字母和数字。如果丢失首次输出的临时密码,应删除尚无业务数据的本地开发库后重新初始化,不要把密码补写到源码、`appsettings*.json` 或 README。Production 不会自动创建默认管理员,必须使用下文的显式安全引导命令。 +- 平台管理端: +- Scalar: +- OpenAPI: +- Liveness: +- Readiness: -开发环境只隐藏 EF Core 成功 SQL 日志,ORM 警告与错误仍会输出。 +## 运行时边界 -当前不把后端改成 MVC/Razor,也不为这个静态 demo 单独维护 Node 服务。后续平台端产品化时,建议迁为独立 React/Vite/Next 工程,.NET 继续提供 API。 +- API 不自动执行数据库迁移;部署和本地初始化都使用 `Tiku.DbMigrator`。 +- Development 可不配置 Redis 和 RabbitMQ;Production 缺少任一依赖时 API 与 Worker 会拒绝启动。 +- 租户由可信 Host 解析;平台 Host 上只有允许的路径可通过 `x-tenant-code` 或 `tenantCode` 指定租户。 +- 租户数据由 EF Query Filter、写入拦截器、租户限定外键/唯一索引和 PostgreSQL guard 共同隔离。 +- 普通请求默认要求认证;匿名接口必须显式声明 `[AllowAnonymous]`。 +- API 只在 Development 映射 OpenAPI 和 Scalar,不应把文档端点作为生产依赖。 -## 数据库与 ORM 分工 - -EF Core code-first migration 负责: - -- 表、列、索引、普通外键; -- 普通 unique/check constraint; -- 模型快照和迁移历史; -- 通过 `Tiku.DbMigrator` 显式执行迁移。 - -PostgreSQL guard 负责 EF 无法表达的跨表租户不变量: - -- `TenantQuestionReference` 只能引用平台公共题或当前租户私题; -- `TaxonomyNode` 父节点只能属于平台主体或当前租户; -- 需要读取 `tenants.mode` 或比较 owner 关系的条件约束。 -- 已发布套餐版本及其模块、额度清单不可修改;订阅套餐类型与订单快照必须一致。 -- 套餐控制 Feature,角色控制 Permission,菜单仅按有效权限生成导航;内容后台按题库、词汇、手册、视频、分数线和站点内容分模块授权。 -- 员工、学生、私有题、存储、导入、导出和短信额度在业务写入时原子消费,Worker 定期按真实数据校准当前量。 - -维护规则: - -1. 租户 SQL 放在 `PostgreSqlTenantConstraintSql.cs`,SaaS 商品 SQL 放在 `PostgreSqlSaasCatalogConstraintSql.cs`。 -2. Migration 只调用集中 helper,不复制 trigger SQL。 -3. 重建 `InitialSchema` 后,`Up()` 末尾必须调用 `EnsureTenantIsolationGuards()` 和 `EnsureSaasCatalogGuards()`;`Down()` 先调用对应 Drop helper。 -4. 新增 guard 前先判断能否用 EF FK / unique / check 表达;表达不了才加 PostgreSQL guard。 -5. 每个 guard 必须有 migration script 断言和真实 PostgreSQL 越权测试。 - -## 开发约束 - -- 新增租户实体必须实现租户 marker,并通过模型测试确认 Query Filter、租户唯一索引和组合外键。 -- 普通 Controller / Service 不接受可写 `tenantId`、任意 owner tenant GUID、任意 bucket 或 provider 密钥。 -- 题目写接口使用 `QuestionLocator`;答题接口使用 `sessionQuestionId`;不得恢复裸 `QuestionId` 练习写入。 -- 自定义域名请求不得通过 `tenantCode`、`host` query 或客户端转发头切换租户。 -- 业务层不得直接引用第三方 SDK namespace、拼 OSS bucket、读取微信/支付/短信/AI 密钥。 -- 不新增 Supabase provider、Supabase URL 拼接、Supabase Storage 或 Supabase Auth 兼容层。 -- AI 面向租户教师后台和题目反馈审核;租户 API Key 存 `TenantSecret`,SK 类型不得进入 Domain/Application/API。 - -## 文档入口 - -- [本地开发快速开始](docs/quickstart.md) -- [当前认证、授权与 Host 安全策略](docs/architecture/authentication-authorization-security.md) -- [认证与授权待补强清单](docs/architecture/authentication-authorization-hardening-plan.md) -- [迁移路线与剩余范围](docs/migration-roadmap.md) -- [API 契约基线说明](docs/migration/contracts/README.md) -- [AI 底座设计](docs/migration/phase-8-ai-foundation.md) -- [第九阶段:SaaS 模块商城与租户交付闭环](docs/migration/phase-9-saas-marketplace-and-onboarding.md) - -## 常用命令 +## 常用验证 ```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 has-pending-model-changes \ + --project Tiku.Infrastructure \ + --startup-project Tiku.DbMigrator \ + --no-build git diff --check ``` -生成迁移 SQL: +PostgreSQL 特有的迁移、事务、约束和跨租户不变量必须由 `Tiku.IntegrationTests` 在真实 PostgreSQL 上验证,不能只依赖 EF InMemory。 -```bash -dotnet ef migrations script \ - --project Tiku.Infrastructure \ - --startup-project Tiku.DbMigrator -``` +## 文档 -检查模型是否有未生成 migration 的变更: +当前文档统一从[文档总览](docs/README.md)进入: -```bash -dotnet ef migrations has-pending-model-changes \ - --project Tiku.Infrastructure \ - --startup-project Tiku.DbMigrator -``` +- [系统架构与业务边界](docs/architecture/overview.md) +- [认证、授权与租户隔离](docs/architecture/security-and-tenancy.md) +- [配置与后台任务](docs/operations.md) +- [本地开发与运行](docs/quickstart.md) -Production 首次部署可在迁移完成后显式创建平台超级管理员: - -```bash -export TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL='admin@example.com' -export TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD='replace-with-a-strong-temporary-password' -export TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME='Platform Administrator' -dotnet run --project Tiku.DbMigrator -- --bootstrap-platform-admin -``` - -该命令只允许在不存在任何平台角色用户绑定时执行。不要把临时密码写入仓库配置或命令行参数。 +接口、DTO、请求参数和响应模型以运行时 OpenAPI/Scalar 为准;文档不再维护手写接口清单或迁移过程记录。 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..b4b7ec1 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,28 @@ +# TIKU Backend 文档 + +这里仅记录当前代码已经实现的架构、运行方式和维护约束。接口细节以 Development 环境的 OpenAPI/Scalar 为准,数据库结构以 EF Core Migration 和模型快照为准。 + +## 阅读入口 + +| 文档 | 内容 | 适合谁 | +| --- | --- | --- | +| [本地开发与运行](quickstart.md) | PostgreSQL 初始化、启动 API/Worker、验证命令、常见问题 | 新开发者 | +| [系统架构与业务边界](architecture/overview.md) | 项目依赖、运行时组件、当前业务模块、请求与消息链路 | 开发与评审人员 | +| [认证、授权与租户隔离](architecture/security-and-tenancy.md) | 登录、Session、JWT、Cookie/CSRF、Realm、RBAC、Capability、DataScope、租户隔离 | API 与安全开发者 | +| [配置与后台任务](operations.md) | 环境配置、Production 启动门禁、Redis/RabbitMQ、Worker、健康检查 | 开发与运维人员 | + +## 权威来源 + +- API 契约:`Tiku.Api/Controllers`、请求/响应 DTO 和运行时 OpenAPI。 +- 数据模型:`Tiku.Domain`、`Tiku.Infrastructure/Persistence/Configurations` 和 EF Core Migration。 +- 认证授权:`Tiku.Api/Configuration`、`Tiku.Api/Middleware`、`Tiku.Application/Security`、`Tiku.Infrastructure/Security`。 +- 后台任务:`Tiku.Worker`、`Tiku.Application/Jobs`、`Tiku.Infrastructure/Jobs` 和 `Tiku.Infrastructure/Messaging`。 +- 外部服务:Application 接口与 Infrastructure 实现;运行时租户配置存储在 `TenantExternalProvider` 和 `TenantSecret`。 + +## 维护规则 + +1. 文档只描述当前可从代码或自动化测试确认的行为。 +2. 新增租户实体时,同时验证 Query Filter、租户唯一索引、组合外键和写入拦截器。 +3. 数据库结构变更必须生成 EF Core Migration,并检查 migration script 和 pending model changes。 +4. 不在文档中保存连接密码、JWT 私钥、证书密码、Provider 密钥或平台管理员临时密码。 +5. 不再维护阶段路线图、旧后端接口对比、评审快照或开发过程记录。 diff --git a/docs/adr/0001-authoritative-dotnet-backend.md b/docs/adr/0001-authoritative-dotnet-backend.md deleted file mode 100644 index d81fdac..0000000 --- a/docs/adr/0001-authoritative-dotnet-backend.md +++ /dev/null @@ -1,32 +0,0 @@ -# ADR 0001:以 .NET + PostgreSQL 作为唯一目标后端 - -- 状态:已接受 -- 日期:2026-07-27 - -## 背景 - -旧 NestJS 后端与 Supabase 数据库、认证、存储和规则耦合较深。新的 ASP.NET Core 后端已经具备 EF Core 模型、迁移链和业务 API。前后端尚未正式开发,仍可直接收敛技术路线。 - -## 决策 - -1. 本仓库是题库 SaaS 唯一继续演进的后端。 -2. 旧 NestJS 仓库冻结为业务行为、接口契约和数据迁移参考。 -3. EF Core Migration 是普通数据库结构变更的权威历史;生产通过 `Tiku.DbMigrator` 显式执行迁移,API 不自动同步结构。 -4. 应用只依赖标准 PostgreSQL 和 Npgsql,不以 Supabase 作为运行时依赖或托管目标。 -5. 身份、对象存储、短信、支付、通知和 AI 通过 Application 接口隔离供应商,并通过 `TenantExternalProvider` + `TenantSecret` 配置。 -6. Yudao 只参考 RBAC、租户套餐、审计和后台产品设计,不作为运行时依赖。 -7. 新功能按业务闭环迁移和验收,不以 Controller 或 endpoint 数量作为完成标准。 - -## 边界 - -- 认证和授权由 ASP.NET Core、JWT、数据库 Session 和应用权限策略负责。 -- 多租户隔离由请求租户上下文、EF 查询/写入防护、数据库约束和真实 PostgreSQL 测试共同负责。 -- 普通表、列、索引、外键和普通约束由 EF Core Migration 管理。 -- EF 不能表达的跨表租户不变量使用集中 PostgreSQL guard,由 migration 调用并接受测试。 -- 旧 OpenAPI 用于发现能力缺口,不要求新接口逐字兼容。 - -## 结果 - -- 不建设长期双后端或双写链路。 -- 不保留 Supabase 作为认证、存储或数据库运行时目标。 -- 更换 PostgreSQL 托管商或外部服务供应商不要求重写业务代码。 diff --git a/docs/architecture/authentication-authorization-hardening-plan.md b/docs/architecture/authentication-authorization-hardening-plan.md deleted file mode 100644 index 163623f..0000000 --- a/docs/architecture/authentication-authorization-hardening-plan.md +++ /dev/null @@ -1,98 +0,0 @@ -# 认证与授权待补强清单 - -> 2026-07-29 实施状态:可信代理启动校验、外部登录成员生命周期、Redis 跨实例频控与故障关闭、固定目录数据库 Capability、事务化 System Scope 审计、MassTransit EF Bus/Consumer Outbox 与即时 BackgroundJob Consumer、浏览器 Cookie/CSRF 主链路及 endpoint manifest 已落地。本地 RabbitMQ 4.3.4 已验证停机期间事务提交、outbox 积压及重启补发;生产网关 ACL 和生产 Broker 演练仍属于部署验收项。 - -当前生效规则见 [认证、授权与 Host 安全策略](authentication-authorization-security.md)。本文只记录尚需补强的安全事项,不重复描述已实现体系。 - -## P0:可信代理与 Host fail-closed - -- Production 必须配置正式 `PlatformHosts` 和 `TrustedProxyAddresses`。 -- Production 不允许只保留 `localhost` / `127.0.0.1` 作为平台 Host。 -- 未受信来源伪造 `X-Forwarded-Host` 不能改变 realm 或 tenant context。 -- 受信代理只接受一跳转发,网关必须覆盖客户端伪造的 Forwarded Headers。 -- API 公网入口必须只能由受信网关访问。 - -实现说明:应用已强制 `ForwardLimit=1`,Production 缺少正式 Host、显式 `AllowedHosts` 或可信代理地址时启动失败;公网 ACL 和网关覆盖转发头由部署层落实。 - -验收: - -- Host A + Tenant B token 返回 403。 -- 未知 Host 的非豁免路径返回 404。 -- Production 缺少可信代理或正式平台 Host 时启动失败。 - -## P0:Disabled / Invited 成员生命周期 - -- 外部身份登录不得静默恢复 Disabled membership。 -- Invited membership 不得被微信登录静默激活。 -- 首次外部登录是否允许创建学生成员,必须由租户自注册策略控制。 -- 成员恢复只能由管理员显式操作并写审计。 - -实现说明:`TenantAuthPolicy.AllowExternalStudentSelfRegistration` 控制首次外部登录;Disabled/Invited 不会被登录激活,管理员成员变更会同步撤销 Session、写审计并发布生命周期事件。 - -验收: - -- Disabled 成员旧 access/refresh 立即失效。 -- Disabled 成员不能通过微信 Web 或小程序登录恢复。 -- 关闭自注册时,首次外部登录被拒绝。 - -## P1:SaaS Capability 授权 - -RBAC 只回答“用户是否有操作权限”;Capability 负责“租户是否购买、启用并可使用该能力”。 - -默认组合: - -```text -Tenant Active - + Subscription 有效 - + Module / Feature 可用 - + Operation Permission - + DataScope / Resource Scope -``` - -实现说明:`SaasFeature`、显式 `PermissionModule.RequiredFeatureCode`、不可变套餐版本、`TenantFeatureOverride` 与 `IFeatureAccessService` 已进入数据库授权链路。未知 Feature、未购买模块和失效订阅均 fail-closed;角色绑定保留历史权限,但鉴权和菜单只使用当前有效权限。 - -验收: - -- 有 permission 但套餐不含模块,返回 403。 -- 套餐包含模块但没有 permission,返回 403。 -- PastDue / Cancelled / Expired 不能创建新的受限资源。 -- 修改套餐后,旧 access token 不需要等待过期即可失去能力。 - -## P1:接口最小权限与 DataScope 审计 - -- 建立 endpoint authorization manifest:method、route、realm、module、permission、DataScope、audit action。 -- 后台写接口不得只使用 `[Authorize]`。 -- tenant/platform 权限不得串用。 -- `[AllowAnonymous]` 只能出现在白名单路由。 -- All-only 资源必须显式声明。 -- 新增 Controller action 未进入 manifest 时测试失败。 - -实现说明:`AuthorizationManifestTests` 对全部 Controller HTTP Action 的 method、route、匿名标记和 policy 生成稳定摘要;MVC convention 同时为全部非匿名 Controller endpoint 生成 realm、module、permission、operation、All-only 与 audit action 运行时元数据,变更会触发测试失败并要求安全评审。 - -验收: - -- 列表、详情、创建、更新、删除、批量、导出和 Worker job 使用一致 DataScope。 -- 租户 A 管理员不能读取或操作租户 B 数据。 - -## P1:System Scope 审计 - -- `ITenantExecutionScope` 创建 System Scope 时必须记录 caller、reason、target tenant 和 request/job id。 -- 平台操作、Worker、迁移验证和受审计公共题库服务才允许使用 System Scope。 -- 跨租户写操作必须落 `AuditLog`。 - -实现说明:`SystemScopeRequest` 强制 caller、reason、target tenant 和 correlation ID;没有租户目标时必须显式声明 `IsGlobal`,且 Worker/公共题库不能创建全局 scope。旧参数签名已移除。成功路径的 entered 审计、跨租户业务写入和 completed 审计处于同一 PostgreSQL 事务,异常路径回滚业务并持久化 entered/failed 审计。 - -验收: - -- 未声明 reason 的 System Scope 创建失败。 -- Worker scope 不串租户。 -- 高风险平台操作都有审计记录。 - -## P2:客户端与协议规范 - -- 浏览器 token 存储策略在正式前固定:纯 Bearer、本域 BFF 或 cookie 方案只能选一种主链路。 -- Access token 继续短期有效,不把角色和权限写入 JWT。 -- 登录审计和错误响应避免泄露手机号、openId、邮箱完整值。 -- 出现第三方生态登录、开放 API 或多客户端授权需求时,再评估 OpenIddict / OIDC,不继续扩展私有协议。 - -实现说明:浏览器使用 `/api/browser-auth` + Secure/HttpOnly Cookie + Origin/CSRF 校验;原 `/api/auth` Bearer 契约继续供小程序、原生和服务调用。 diff --git a/docs/architecture/authentication-authorization-security.md b/docs/architecture/authentication-authorization-security.md deleted file mode 100644 index f84d70e..0000000 --- a/docs/architecture/authentication-authorization-security.md +++ /dev/null @@ -1,146 +0,0 @@ -# 认证、授权与 Host 安全策略 - -本文是当前生效安全规范。新增接口或修改登录流程时,以本文档和自动化测试为准;前端菜单、JWT 字符串和历史角色约定不能代替 API 授权。 - -## 授权域 - -- `tenant`:租户业务域,必须绑定 Active 租户和 Active `TenantMembership`。 -- `platform`:平台运营域,只能从配置的 Platform Host 进入,不绑定租户。 - -核心规则: - -- Host、JWT scope、tenant claim、数据库 Session 和请求租户上下文必须一致。 -- JWT 只证明已认证会话,不承载可直接授权的角色或权限。 -- 后台权限每次从数据库角色绑定解析;菜单只控制 UI 展示。 -- 租户后台能力同时要求 Active tenant、有效订阅、模块权益和 operation permission;Capability 仍以 PostgreSQL 为准。 -- 数据权限必须进入 SQL;无法可靠映射 owner、region 或 class 的资源采用 All-only fail-closed。 -- 用户、成员、租户、后台角色、权限、SecurityStamp 或 Session 任一失效,旧 token 不能继续取得能力。 -- Controller 默认要求认证;公开接口必须显式 `[AllowAnonymous]`。 - -```text -Client - -> Trusted proxy - -> TenantResolutionMiddleware - -> JWT + AuthSession validation - -> Authorization handler + current access context - -> EF tenant filter + DataScope SQL + PostgreSQL constraints -``` - -## 账号与 Session - -- 账号由 ASP.NET Core Identity 管理。 -- 密码最少 8 位且必须同时包含字母和数字;PBKDF2 迭代次数 210,000。 -- 连续 5 次密码失败后锁定 15 分钟。 -- 普通租户用户使用手机号和密码或手机号短信验证码登录;平台管理员当前使用账号和密码登录。 -- 微信等外部身份只保存 provider subject、openid、unionid,不保存 `session_key` 或原始 secret。 -- Data Protection key 持久化到 PostgreSQL;非 Development 环境必须提供带私钥的 PKCS#12 证书保护 key ring。 - -Access token: - -- RSA SHA-256 签名,Header 必须包含 `kid`。 -- 固定 15 分钟。 -- 包含 `sub`、`sid`、`jti`、`iat`、`iss`、`aud`、`exp`、`scope`。 -- tenant token 必须包含 `tid`;platform token 禁止包含 `tid`。 -- 不包含 role 或 permission claim。 - -Refresh token: - -```text -v2.{t|p}.{tenantId|-}.{sessionId}.{64-byte-random-secret} -``` - -- 数据库只保存完整 refresh token 的 SHA-256 hash。 -- 刷新在事务内轮换 Session。 -- 并发刷新只允许一个成功。 -- 已轮换 token 被复用时视为重放,撤销整个 token family 并写审计。 -- logout 撤销当前 refresh token family;logout-all 更新 SecurityStamp 并撤销用户全部 Session。 - -浏览器入口使用 `/api/browser-auth/*`:access/refresh token 仅写入 Secure、HttpOnly Cookie,响应体不返回 token;不安全方法必须通过同源 Origin 与双提交 CSRF 校验。`/api/auth/*` Bearer 契约继续供小程序、原生客户端和服务调用。 - -## Host 与 tenant 解析 - -Host 是认证上下文,不是普通参数。`TenantResolutionMiddleware` 在 Authentication 前执行。 - -| 请求入口 | 租户上下文 | 允许 realm | 默认结果 | -| --- | --- | --- | --- | -| Platform Host | 无租户 | platform;白名单入口可用 tenantCode 引导 tenant 登录 | 继续 | -| Active 租户 Host | Host 绑定租户 | tenant | 继续 | -| 租户 Host + 其他 tenantCode/header | 冲突 | 无 | 403 | -| Platform Host + platform realm + tenantCode | 非法混合 | 无 | 400 | -| 未知 Host | 无 | 无 | 非豁免路径 404 | -| Pending/禁用域名 | 无 | 无 | 404 | - -规则: - -- 自定义域名不接受 `tenantCode`、`host` query 或客户端转发头覆盖。 -- tenant JWT 的 `tid` 必须与 Host 解析租户一致。 -- platform JWT 不能访问租户 Host。 -- 平台 Host 上的租户登录引导才允许受控使用 `tenantCode`。 -- 只接受可信代理写入的 Forwarded Headers;直连客户端伪造无效。 - -## RBAC、菜单与 DataScope - -租户后台与平台后台角色分离: - -- 租户角色、权限、菜单、用户角色绑定都带租户上下文。 -- 平台角色不带租户键,不能自动读取租户业务数据。 -- 菜单只决定 UI bootstrap 展示,不作为 API 授权依据。 -- 后台 API 必须声明明确 permission;高风险写操作必须记录审计。 -- UI bootstrap 只返回“有效 permission 推导菜单”与有效 Capability 的交集;租户不能绑定当前无权使用的模块权限。 -- Trial/Active 且在有效期内可写;PastDue/Cancelled/Expired 仅允许已有权益模块的历史读取。 - -DataScope: - -- `All`:当前租户内该模块全部资源。 -- `Restricted`:按 region/class/owner 等资源关系过滤。 -- `Self`:只允许当前用户关联资源。 -- 无法可靠表达资源关系的模块只允许 `All`,不能退化为仅按 tenant 查询。 - -## 短信验证码 - -- 验证码生成、哈希、频控、过期和校验由自有业务服务负责。 -- `ISmsProvider` 只负责发送。 -- 发送失败必须记录失败状态,不能留下可验证验证码。 -- 登录、绑定、找回密码等场景使用独立 purpose 和频控键。 -- Redis Lua 同时执行跨实例 IP、账号、租户、手机号和 purpose 窗口计数;key 只使用 GUID 或不可逆哈希。 -- Redis 不可用时密码尝试、短信发送和短信校验失败关闭;普通授权请求仍直接查询 PostgreSQL。 - -## 可靠安全事件 - -- `Tiku.Contracts` 只包含版本化 DTO,不引用 EF、HTTP 或 Provider SDK。 -- API 使用 MassTransit EF Bus Outbox,Worker consumer 使用 EF inbox/outbox;业务变更、审计和消息由同一 DbContext 提交。 -- RabbitMQ 消息只负责非权威失效版本、菜单刷新和下游通知;成员、租户、Session 或套餐失效不等待 consumer。 -- 即时 `BackgroundJob` 由 `BackgroundJobRequestedV1` Consumer 执行;延时任务和失败后的定时重试继续由数据库调度器处理,同一即时任务不会同时进入两种消费路径。业务 handler 必须使用受审计 System Scope 提供的 scoped `DbContext`。 -- System Scope 只能通过完整 `SystemScopeRequest` 创建;成功路径将 entered 审计、跨租户业务写入和 completed 审计放入同一 PostgreSQL 事务。 - -## 审计与错误 - -必须落审计: - -- 登录、刷新重放、logout-all、强制改密; -- 角色、权限、成员状态、租户状态、Provider 配置、支付运营动作; -- System Scope 和跨租户平台操作。 - -错误响应: - -- 401:未认证或 token/session 无效。 -- 403:已认证但 realm、tenant、permission、DataScope 或套餐能力不满足。 -- 404:未知 Host、不可见资源或需要隐藏存在性的资源。 -- 响应不得泄露完整手机号、openId、邮箱、密钥、支付账号或内部 provider payload。 - -## 生产配置清单 - -- 正式 `PlatformHosts`。 -- 可信代理地址和网络 ACL。 -- 非通配 `AllowedHosts`。 -- JWT issuer、audience、当前 `KeyId`、RSA 私钥和旧公钥集合。 -- Data Protection 证书。 -- CORS 明确 Origin。 -- Redis 7.2+ 连接串;Production 缺失时拒绝启动。 -- RabbitMQ 4.x Host、virtual host 与凭据;Production 缺失时拒绝启动。 -- 默认镜像不依赖 `x-delayed-message` 插件;Consumer 使用有限即时重试,延时业务重试落回 PostgreSQL `RunAfter`。 -- 公网只暴露覆盖 Forwarded Headers 的可信网关,API ACL 只允许该网关访问。 -- Secret encryption key。 -- 短信、对象存储、支付、通知和 AI provider 只通过租户 Provider 配置读取密钥。 - -待补强事项见 [认证与授权待补强清单](authentication-authorization-hardening-plan.md)。 diff --git a/docs/architecture/endpoint-authorization-manifest.md b/docs/architecture/endpoint-authorization-manifest.md deleted file mode 100644 index eb2ffd1..0000000 --- a/docs/architecture/endpoint-authorization-manifest.md +++ /dev/null @@ -1,9 +0,0 @@ -# Endpoint authorization manifest - -Controller 授权面由 `AuthorizationManifestTests` 按 HTTP method、route、controller/action、匿名标记和 policy 生成稳定摘要。 -`EndpointAuthorizationMetadataConvention` 为全部非匿名 Controller endpoint 生成 realm、module、permission、CapabilityOperation、All-only DataScope 与 audit action 元数据,测试从运行时 `EndpointDataSource` 验证覆盖。新增、删除或修改 Action 时摘要测试必须失败,评审者确认元数据后才能更新 count/hash。 - -该清单是防止接口绕过评审的变更门禁;实际授权事实仍来自 PostgreSQL permission、Capability 和 DataScope,不能用摘要替代运行时校验。 - -- Action 数量:332 -- SHA-256:`a80fe477ba3021625e17c9fc639e5109bab678178f8024a51c3c732bf5a46d3f` diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md new file mode 100644 index 0000000..73c7ae9 --- /dev/null +++ b/docs/architecture/overview.md @@ -0,0 +1,124 @@ +# 系统架构与业务边界 + +本文描述当前仓库的实际代码结构和运行时职责。接口路径、DTO 和响应模型以运行时 OpenAPI 为准。 + +## 分层与依赖 + +```text + +------------------+ + | Tiku.Contracts | + +--------^---------+ + | ++-----------+ +---------------+---------------+ +| Tiku.Api | | Tiku.Worker / Tiku.DbMigrator | ++-----+-----+ +---------------+---------------+ + | | + +-------------+-------------+ + v + +---------------------+ + | Tiku.Infrastructure | + +----------+----------+ + v + +---------------------+ + | Tiku.Application | + +----------+----------+ + v + +---------------------+ + | Tiku.Domain | + +---------------------+ +``` + +- `Tiku.Domain` 保存领域实体、枚举和基础类型。除 Identity stores 抽象外,不依赖持久化或 Provider SDK。 +- `Tiku.Application` 定义用例契约、Provider 接口、安全上下文和业务目录,依赖 Domain。 +- `Tiku.Infrastructure` 实现 EF Core、PostgreSQL、Identity、外部 Provider、消息和后台任务,依赖 Application、Domain 与 Contracts。 +- `Tiku.Contracts` 保存 API 与 Worker 使用的版本化消息 DTO,不引用 HTTP、EF Core 或 Provider SDK。 +- `Tiku.Api`、`Tiku.Worker` 和 `Tiku.DbMigrator` 是独立运行入口。 + +## 运行时组件 + +### API + +`Tiku.Api/Program.cs` 只负责组合服务、构建应用和启用请求管线。管线的关键顺序是: + +```text +Forwarded Headers + -> HTTPS / 压缩 / 静态文件 + -> Routing / CORS + -> Host 租户解析 + -> 浏览器 CSRF + -> JWT 认证 / 认证专用限流 / 全局限流 + -> 当前用户上下文 / 授权 + -> SaaS Feature 校验 + -> Output Cache + -> Controllers +``` + +OpenAPI 和 Scalar 只在 Development 映射。平台管理静态文件由 `Tiku.Api/wwwroot` 同源托管,默认入口是 `/platform-admin/`。 + +### DbMigrator + +`Tiku.DbMigrator` 是唯一迁移入口,执行顺序为: + +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` 时创建管理员。 + +API 和 Worker 都不自动迁移数据库。 + +### Worker + +`Tiku.Worker` 当前注册四个独立 Hosted Service: + +| Worker | 周期 | 当前职责 | +| --- | --- | --- | +| `TenantDomainWorker` | `TenantDomains:PollSeconds`,限制为 10~3600 秒 | 校验自定义域名 CNAME/TXT,调用网关 TLS 接口并失效租户缓存 | +| `SaasSubscriptionWorker` | 60 秒 | 处理到期、宽限期等 SaaS 订阅生命周期 | +| `FeatureUsageWorker` | `FeatureUsageReconciliation:IntervalMinutes`,限制为 1~1440 分钟 | 按真实业务数据校准租户 Feature 用量 | +| `BackgroundJobsWorker` | 2 秒,4 个分区 | 租约处理 PostgreSQL 中的延时/待执行后台任务;未配置 RabbitMQ 时也处理即时任务 | + +后台任务当前支持 `content_import`、`content_export`、`statistics_aggregation`、`commerce_reconciliation` 和 `tenant_domain_recheck`。`asset_security_scan` 会明确失败,直到配置实际扫描 Provider;不能把它描述为已接通扫描服务。 + +配置 RabbitMQ 后,即时安全事件和后台任务请求使用 MassTransit;API 使用 EF Bus Outbox,Worker Consumer 使用 EF inbox/outbox。延时任务仍由 PostgreSQL `RunAfter` 和租约 Worker 处理。 + +## 数据与持久化 + +- 数据库使用标准 PostgreSQL,普通 schema 由 EF Core entity、Fluent Configuration 和 Migration 管理。 +- 当前模型启用 `citext`、`ltree` 和 `pg_trgm` 扩展,并统一映射为 `snake_case`。 +- Data Protection key ring 由 API 持久化到 PostgreSQL;非 Development 必须使用 X509 证书保护。 +- MassTransit inbox/outbox 表与业务表处于同一 `TikuDbContext`。 +- PostgreSQL 不启用 RLS;租户隔离由应用和数据库多层共同保证,详见[认证、授权与租户隔离](security-and-tenancy.md)。 + +## 当前业务模块 + +### 平台端 + +- 租户、Owner、域名、状态、员工、角色和审计告警。 +- 平台公共题库、分类节点、题目、导入和资源上传。 +- SaaS Feature、额度定义、套餐版本、报价、订单、支付、退款、订阅、发票和催缴。 +- 平台级 CRM、短信渠道/模板和支付应用配置。 + +### 租户端 + +- 员工、角色、权限、菜单、DataScope 和租户设置。 +- 私有题库、公共题库引用、内容目录、词汇、手册、视频、分数线、站点内容、导入导出和资源。 +- 学生、班级、CRM 跟进、监管规则、报表和审计。 +- 学生商城、订单、支付、退款、优惠券、积分、推广和分佣。 +- 租户 SaaS 目录、账务、订阅、用量、发票和 onboarding 状态。 +- 身份、短信、对象存储、支付、通知和 AI 的租户 Provider 配置边界。 + +### 学生端 + +- Host 对应的运行时品牌、导航、Feature 和登录方式 bootstrap。 +- 账号登录、个人资料、通知、签到和积分。 +- 题目目录、练习会话、作答、收藏、错题、视频播放和进度。 +- 学生商品、订单、支付、优惠券、权益和推广关系。 + +是否存在某个具体操作,应以 Controller 和 OpenAPI 为准,不能仅凭本节的模块名称推断。 + +## 外部服务边界 + +Application 通过接口表达身份、短信、对象存储、支付、通知、域名和 AI 能力;Infrastructure 当前包含自托管身份、阿里云短信/OSS、微信、支付宝、站内通知、DNS JSON 查询和 HTTP 网关实现。 + +租户级 Provider 元数据和密钥分别存入 `TenantExternalProvider` 与 `TenantSecret`。密钥由 32 字节 master key 加密,API 不应把明文、`SecretRef` 或 Provider 内部 payload 返回给客户端。 diff --git a/docs/architecture/saas-product-and-api-roadmap.md b/docs/architecture/saas-product-and-api-roadmap.md deleted file mode 100644 index 472074a..0000000 --- a/docs/architecture/saas-product-and-api-roadmap.md +++ /dev/null @@ -1,230 +0,0 @@ -# SaaS 题库产品边界与后续接口路线 - -本文档定义平台端、租户端、学生端的目标边界,以及下一阶段接口开发顺序。当前 ASP.NET Core 后端是实现基线;旧 NestJS 和 `tiki-web` 只用于核对业务行为,yudao 只用于参考套餐、商城、支付和后台运营的模块划分。 - -## 当前判断 - -现有后端已经具备继续开发的基础:Host 租户解析、强租户隔离、共享与私有题库、RBAC、Provider 解耦、租户前端运行时配置、学生练习闭环、交易基础和 Worker 基座均已落地。 - -第九阶段已经完成 SaaS 产品与交付闭环。当前主要缺口是: - -- 教师发布作业、考试、批阅和查看班级结果的教学闭环。 -- Provider 自助配置、公共题库运营和高流量查询读模型仍需完善。 - -## 三端边界 - -### 平台端 - -平台端是 SaaS 控制面,负责: - -- 租户、租户 Owner、状态、域名和生命周期。 -- SaaS 业务模块、套餐、附加包、价格和额度。 -- 租户订阅、SaaS 订单、支付、退款、账单、发票、催缴和用量。 -- 平台员工、平台角色、平台权限、审计和告警。 -- 公共题库、公共分类、题目版本、发布和反馈质量运营。 -- 平台自身的收款 Provider,不使用租户配置的学生商城支付账号。 - -### 租户端 - -租户端是机构控制面,负责: - -- 员工、自定义角色、权限、班级、学生和数据范围。 -- 私有题库、公共题库消费、分类扩展、组卷、导入和导出。 -- 作业、考试、每日一练、批阅和教学报告。 -- 品牌、主题、导航、首页模块和自定义域名。 -- 身份、SMS、对象存储、学生商城支付、通知和 AI Provider。 -- 学生商品、会员、优惠券、激活码、积分、CRM、推广和分佣。 -- 本租户 SaaS 订阅、账单、用量、续费和升级。 - -### 学生端 - -学生端是租户域名下的数据面,负责: - -- 根据 Host 获取租户品牌、功能、导航和允许的登录方式。 -- 登录、绑定、个人资料和通知。 -- 题库、练习、考试、作业、错题、收藏和学习报告。 -- 词汇、知识手册、视频、分数线等可选内容模块。 -- 租户自己的学生商城、订单、支付、优惠券、积分和权益。 - -## 套餐能力与权限分层 - -不能用一套“模块”同时表达套餐、权限和菜单。目标模型固定为: - -| 概念 | 用途 | -| --- | --- | -| `SaaSFeature` | 平台可销售的业务能力 | -| `SaasOfferingVersionFeature` | 不可变套餐版本包含哪些业务能力 | -| `SaasOfferingVersionLimit` | 套餐版本的员工、学生、题目、存储、导出和 AI 额度 | -| `PermissionModule` | 后台权限页面的业务分组 | -| `BackendPermission` | `view/create/update/import/export/approve/retry` 等操作权限 | -| `BackendMenu` | 根据有效权限生成的前端导航,不作为鉴权依据 | - -建议的可售卖能力包括: - -- `question_bank.private` -- `learning.practice` -- `learning.assignment` -- `learning.exam` -- `content.vocabulary` -- `content.handbook` -- `content.video` -- `content.scoreline` -- `marketing.site_content` -- `student.management` -- `commerce.student_store` -- `crm.followup` -- `growth.referral_commission` -- `ai.teacher_assistant` - -身份安全、角色管理、账务中心和续费入口属于核心能力。即使套餐过期,也不能阻止租户查看账单、配置管理员或完成续费。 - -每次受保护的业务请求必须同时满足: - -```text -租户有效 -+ 订阅状态允许当前读写操作 -+ 套餐或附加包包含业务能力 -+ 未超过对应额度 -+ 当前角色具有操作权限 -+ DataScope 允许访问目标数据 -``` - -## 双交易域 - -平台 SaaS 商城和租户学生商城必须是两个独立边界。 - -### PlatformBilling - -平台向租户收费,包含: - -- SaaS 套餐、附加包和报价。 -- SaaS 订单、支付、退款、订阅、账单和发票。 -- 平台收款 Provider 和平台支付回调。 -- 租户用量、超额计费、额度预警和催缴。 - -### TenantCommerce - -租户向学生收费,包含: - -- SVIP、课程资料和其他学生商品。 -- 学生订单、支付、退款、优惠券、激活码和权益。 -- 当前租户配置的支付 Provider 和回调。 - -两类订单、支付账号、回调地址、审计和对账不得共用业务表或服务。 - -## 已完成的 SaaS 商城与交付接口 - -### 平台 SaaS 商城 - -- 平台模块、额度定义、基础套餐、附加包和不可变版本统一在 `/api/platform-admin/saas/**`。 -- 租户目录、报价、下单、支付、订阅变更、续费、取消、用量和发票统一在 `/api/tenant-billing/**`。 -- 人工、微信和支付宝平台收款使用平台主体 Provider,订单和回调与学生商城分离。 -- `/api/tenant-onboarding/status` 汇总 Owner、订阅、域名、登录方式、Provider 和前端发布状态。 - -租户自助账务接口建议统一在 `/api/tenant-billing/**`: - -```text -GET /api/tenant-billing/catalog -POST /api/tenant-billing/quotes -POST /api/tenant-billing/orders -POST /api/tenant-billing/payments -GET /api/tenant-billing/orders/{orderNo} -GET /api/tenant-billing/subscription -POST /api/tenant-billing/subscription/change -POST /api/tenant-billing/subscription/renew -POST /api/tenant-billing/subscription/cancel -GET /api/tenant-billing/usage -GET /api/tenant-billing/invoices -``` - -### Provider 自助管理 - -统一使用 `TenantExternalProvider + TenantSecret`,补齐: - -```text -GET /api/tenant-admin/providers -PUT /api/tenant-admin/providers -POST /api/tenant-admin/providers/test -POST /api/tenant-admin/providers/activate -POST /api/tenant-admin/providers/disable -PUT /api/tenant-admin/providers/secrets -POST /api/tenant-admin/providers/secrets/rotate -``` - -运行时 bootstrap 需要增加脱敏的登录方式配置,不能返回 SecretRef、密钥或第三方内部配置。 - -### 教师教学闭环 - -- 作业、考试、每日一练的创建和发布。 -- 发布目标:班级、学生组、指定学生。 -- 开始时间、截止时间、限时、补交和自动交卷规则。 -- 学生答题草稿、断点续答和最终提交。 -- 客观题自动批改,主观题教师批阅、复核和评语。 -- 完成率、成绩分布、薄弱知识点和学生明细。 -- 试卷、成绩、每日一练和战报导出。 - -现有 `PracticeBlueprint` 和 `PracticeSession` 可以作为题目装配及作答底座,但不能代替教师发布对象和班级任务状态。 - -### 平台公共题库运营 - -- 公共题库和公共分类主干管理。 -- 题目草稿、审核、发布、撤回和版本对比。 -- 重复题检测、反馈汇总和人工复核。 -- 使用量、错误率、反馈率和版本采用情况。 -- 已发布旧版本禁止物理删除。 - -### 查询性能和读模型 - -- 普通列表采用游标分页和稳定排序,禁止默认返回大集合。 -- 题目、院校和知识点搜索优先使用 PostgreSQL trigram/全文索引。 -- 首页、排行榜和运营看板使用聚合表或异步投影。 -- 公共目录和 runtime bootstrap 使用 Redis 缓存并主动失效。 -- 导入、导出、统计、资源扫描和对账进入 Worker。 -- 使用 OpenTelemetry 观测慢查询、接口耗时、缓存命中和 Worker 延迟。 - -## 实施顺序 - -### 9A~9C:已完成 - -- `SaasFeature`、`PermissionModule`、`BackendPermission`、`BackendMenu` 已分层。 -- `SaasOfferingVersion` 发布后由 Application 和 PostgreSQL guard 双重禁止修改。 -- `IFeatureAccessService` 统一处理租户、订阅、Feature、覆盖、额度与权限过滤。 -- PlatformBilling 与 TenantCommerce 使用独立订单、支付、回调和 Provider 配置。 -- 平台创建租户及 Owner 后,租户可完成购买、开通和 onboarding。 - -### 9D:教师教学与考试 - -- 作业、考试、班级发布、批阅和教学报告。 -- 智能组卷、每日一练、PDF 命题和战报持久化。 -- 导出任务通过 Worker 和对象存储交付。 - -### 9E:学生端与性能治理 - -- runtime 登录选项、手机号绑定和找回密码。 -- 作业/考试中心、断点续答和报告。 -- 根据产品决定是否迁移备考时间线和择校功能。 -- 完成分页、索引、缓存、聚合投影和性能基线测试。 - -### 9F:AI 独立阶段 - -- 面向租户教师的基础对话和后续 Function Call。 -- AI 题目反馈审核,只输出建议和人工复核标记。 -- Semantic Kernel 仅存在于 Infrastructure。 -- 租户 API Key 保存到 `TenantSecret`。 -- AI 调用量、成本和额度进入 SaaS 计量体系。 - -## 验收原则 - -- 套餐未包含的功能不能分配权限、不能显示菜单、不能调用 API、不能由 Worker 绕过执行。 -- 平台角色、租户角色和学生身份不能跨 realm 使用。 -- 租户 A 不能读取或修改租户 B 的配置、学生、题库、订单和 Provider。 -- 平台 SaaS 支付与租户学生支付使用不同配置、订单域和回调链路。 -- 套餐过期后业务写入受限,但账务、续费、安全和历史数据仍可访问。 -- 高风险操作、支付状态变化、订阅变化和 Provider 变化都有审计记录。 -- 关键查询在接近生产的数据量下验证执行计划、分页稳定性和响应时间。 - -## 参考边界 - -- 旧 NestJS:核对已有接口语义、状态机和异常行为,不要求保留旧 URL。 -- `tiki-web`:参考已实际使用的刷题、词汇、手册、商城、营销、教研和运营功能,不复制 PocketBase 查询方式。 -- yudao:参考租户套餐、商城订单、支付、退款、权限和审计的模块拆分,不照搬菜单 ID 套餐模型或 Java 运行时。 diff --git a/docs/architecture/security-and-tenancy.md b/docs/architecture/security-and-tenancy.md new file mode 100644 index 0000000..96887b4 --- /dev/null +++ b/docs/architecture/security-and-tenancy.md @@ -0,0 +1,124 @@ +# 认证、授权与租户隔离 + +本文说明当前请求实际经过的安全边界。新增接口、实体或后台任务时,必须保持这些边界闭合。 + +## 认证入口 + +API 支持两组认证接口: + +- `/api/auth/**` 返回 access token 与 refresh token,适合 Bearer 客户端。 +- `/api/browser-auth/**` 把 token 写入 HttpOnly Cookie,适合同源浏览器客户端。 + +当前登录方式: + +- 平台账号:账号/密码。 +- 租户账号:手机号/密码、手机号/短信验证码。 +- 租户可配置微信网页授权和微信小程序授权。 + +主要流程包括短信发送、密码登录、短信登录、微信登录、refresh、logout、logout-all 和首次登录强制改密。具体请求与响应字段以 Scalar 为准。 + +密码至少 8 位,并必须同时包含字母和数字。连续 5 次失败触发 15 分钟 Identity lockout。短信验证码由本服务生成和哈希,发送 Provider 只负责投递;验证码校验最多允许 5 次尝试。 + +## JWT 与 Session + +- access token 使用 RSA SHA-256 签名,默认有效期 15 分钟。 +- refresh token 默认有效期 30 天,服务端只保存哈希。 +- JWT 必须包含用户、Session、`jti`、签发时间和 `realm`;租户 realm 还必须包含租户 ID。 +- 每次 JWT 认证都会核对数据库 Session、用户/成员状态、安全版本和租户上下文,不把 JWT 声明当作永久授权事实。 +- refresh token 轮换并检测重放;logout 撤销当前 Session,logout-all 撤销用户全部 Session。 +- 平台 token 只能在平台 Host 使用;租户 token 必须与 Host 或允许路径上的 tenant code 解析结果一致。上下文冲突返回 401,不允许静默切换租户。 + +Production 必须显式配置 JWT `KeyId`、私钥和验证公钥集合,不能使用 Development 临时密钥。 + +## 浏览器 Cookie 与 CSRF + +Browser Auth 使用 access、refresh 和 CSRF Cookie: + +- access/refresh Cookie 为 HttpOnly。 +- Bearer handler 只会在同源浏览器请求中回退读取 access Cookie;显式 `Authorization` header 优先。 +- 使用 Cookie 的非安全方法必须通过 `BrowserCsrfMiddleware` 的 Origin/Referer 与 CSRF token 校验。 +- 跨源浏览器使用必须同时正确配置 `Cors` 和 `BrowserAuth:AllowedOrigins`;允许凭据时不能使用通配 Origin。 + +非浏览器客户端应使用 Bearer token,不应复制浏览器 Cookie 流程。 + +## Realm、Permission、Feature 与 DataScope + +每个受保护操作可能同时经过四层判断: + +```text +Realm(platform / tenant) + + BackendPermission(操作权限) + + SaaSFeature(套餐能力) + + DataScope(资源范围) +``` + +- Realm 防止平台身份、租户员工和学生身份跨授权域复用。 +- `BackendPermission` 控制 `view/manage/read/write/operate` 等操作。 +- `SaaSFeature` 是固定代码目录,当前包括后台基础、私有题库、练习、作业、考试、词汇、手册、视频、分数线、站点内容、学生管理、学生商城、CRM、推广分佣和教师 AI。 +- 菜单由有效 Permission 与 Feature 共同推导,只用于 UI bootstrap,不是 API 授权依据。 +- DataScope 支持 `All`、`Restricted` 和 `Self`。无法提供可靠资源 predicate 时返回空查询,不能退化为“当前租户全部数据”。 +- 套餐状态、Feature override 和额度使用量来自 PostgreSQL;Redis 只用于失效通知和缓存,不能成为授权真相。 + +Controller 默认受 Fallback Policy 保护,匿名接口必须显式标记 `[AllowAnonymous]`。`EndpointAuthorizationMetadataConvention` 为非匿名 Controller endpoint 补充 realm、module、permission、Feature 操作、All-only DataScope 和审计元数据,集成测试从运行时 `EndpointDataSource` 验证覆盖。 + +## Host 与租户上下文 + +`TenantResolutionMiddleware` 在认证前解析租户: + +1. 对平台 Host,不默认建立租户上下文。 +2. 对非平台 Host,按启用的租户域名查找租户;未匹配且不属于豁免路径时返回 404。 +3. 只有 `TenantCodePathPrefixes` 明确允许的路径,才能在平台 Host 使用 `x-tenant-code` 或 `tenantCode` 解析租户。 +4. JWT tenant ID 与已解析租户必须一致,否则认证失败。 + +Development 默认平台 Host 是 `localhost` 和 `127.0.0.1`。Production 启动校验要求: + +- 至少一个非 loopback 的正式平台 Host; +- 非通配 `AllowedHosts`; +- 至少一个合法的 `TrustedProxyAddresses`; +- 仅信任一跳且来源位于可信代理列表的 `X-Forwarded-For/Host/Proto`。 + +客户端不得通过任意 header、query 或转发头绕过以上路径和可信代理限制。 + +## 数据库租户隔离 + +当前 PostgreSQL 连接角色不依赖 RLS。租户隔离由以下机制共同完成: + +### 查询 + +`TikuDbContext` 自动为所有包含 `TenantId` 的实体应用 Query Filter。普通请求只有在租户上下文已解析且 ID 匹配时可见;System Scope 才能绕过。 + +模型启动校验会拒绝: + +- 含 `TenantId` 但未实现 `ITenantOwned` 的实体; +- 缺少租户 Query Filter 的实体; +- 未包含 `TenantId` 且未显式声明全局唯一的 unique index; +- 租户实体之间未使用租户限定 principal key 的外键。 + +### 写入 + +`TenantIsolationSaveChangesInterceptor` 检查新增、修改和删除实体的租户所有权,防止普通请求写入其他租户或伪造 `TenantId`。Controller 与 Service 不应接受可任意填写的租户 ID、owner tenant ID、bucket 或 Secret 引用。 + +### 数据库约束 + +能用 FK、unique 和 check 表达的规则优先使用 EF 配置。当前集中 PostgreSQL guard 额外保证: + +- `TenantQuestionReference` 只能指向平台公共题或当前租户私题,且 source 必须匹配所有者类型。 +- `TaxonomyNode` 的父节点只能属于平台主体或当前租户。 +- 已发布 SaaS 套餐版本及其 Feature/额度清单不可修改,并校验订阅与订单快照的一致性。 + +这些 guard 由 Migration helper 统一安装和移除,不允许在多份 Migration 中复制 SQL。 + +## System Scope 与可靠事件 + +跨租户 Worker、迁移、seed 和平台级后台操作必须通过 `ITenantContextInitializer.InitializeSystem` 或受审计的 `ITenantExecutionScope` 进入 System Scope,并提供明确原因。业务代码不得直接关闭 Query Filter。 + +配置 RabbitMQ 时: + +- API 使用 EF Bus Outbox,把业务写入、审计和消息放在同一数据库事务边界。 +- Worker Consumer 使用 EF inbox/outbox 和有限即时重试。 +- Session、成员、租户和套餐状态始终从 PostgreSQL 重新校验,不等待消息消费后才失效。 +- 延时/定时重试使用 PostgreSQL `RunAfter`,不依赖 RabbitMQ delayed-message 插件。 + +## 安全配置门禁 + +Production 还会在启动时验证 Redis、RabbitMQ、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 797c191..353f02a 100644 --- a/docs/assets/tiku-backend-architecture.svg +++ b/docs/assets/tiku-backend-architecture.svg @@ -46,7 +46,7 @@ P 平台管理端 - /platform-admin 静态原型 + /platform-admin 静态管理端 平台账号 · SaaS 运营 diff --git a/docs/migration-roadmap.md b/docs/migration-roadmap.md deleted file mode 100644 index 4507993..0000000 --- a/docs/migration-roadmap.md +++ /dev/null @@ -1,111 +0,0 @@ -# 迁移路线与剩余范围 - -本文档是旧 PocketBase / Supabase / NestJS 后端迁移到 ASP.NET Core + PostgreSQL 后端的状态入口。 - -## 固定原则 - -- 新前端按新 REST API 对接;旧 URL 默认不兼容。 -- 旧 NestJS 只作为行为清单和验收参考。 -- 不保留 Supabase 运行时依赖、Auth/Storage provider 或 RLS 模型。 -- 数据一致性优先落 PostgreSQL FK / unique / check / index;跨表租户不变量用集中 PostgreSQL guard。 -- 外部身份、短信、对象存储、支付、通知和 AI 都通过 Application 接口与 `TenantExternalProvider` 配置解耦。 -- 新功能按业务闭环验收,不按 endpoint 数量验收。 - -## 已完成主线 - -- 仓库转正:本仓库是唯一目标后端,见 [ADR 0001](adr/0001-authoritative-dotnet-backend.md)。 -- 工程底座:ASP.NET Core、EF Core、Npgsql、PostgreSQL、Serilog、RateLimiter、Options 校验。 -- 数据库:greenfield `InitialSchema`,真实 PostgreSQL 迁移和集成测试。 -- 租户隔离:Host 解析、`ITenantContext`、EF Query Filter、SaveChanges 拦截器、组合外键、PostgreSQL guard。 -- 共享题库:平台公共题库、租户私题、`TenantQuestionReference`、版本锁定练习。 -- 前端运行时:自定义域名、DNS/TLS 生命周期、`GET /api/runtime/bootstrap`。 -- 外部服务解耦:`TenantExternalProvider` + `TenantSecret`,身份、短信、OSS、支付、通知 provider 边界。 -- 后台底座:平台/租户 RBAC、菜单、审计、平台后台、租户后台、交易运营、Worker 任务模型。 -- 学生体验:视频搜索/播放/进度、题目解析视频、签到、积分流水、内容导入异步化。 -- AI 基础:已引入 SK 包到 Infrastructure,已固定 provider-neutral 对话存储方向;业务功能仍待实现。 -- SaaS 商城:可售卖 Feature、不可变套餐版本、独立 PlatformBilling、租户自助账务、业务额度、订阅周期 Worker 和 onboarding 已落地。 - -阶段归档: - -- [第一阶段:仓库转正基线](migration/phase-1-repository-baseline.md) -- [第二阶段:.NET 工程底座](migration/phase-2-engineering-foundation.md) -- [第三阶段:强租户隔离、共享题库与租户前端运行时](migration/phase-3-tenant-isolation-and-shared-question-bank.md) -- [第四阶段:外部服务解耦](migration/phase-4-external-provider-decoupling.md) -- [第五阶段:后台能力与 Worker 基座](migration/phase-5-backoffice-worker-operations.md) -- [第七阶段:学生端体验与内容消费闭环](migration/phase-7-student-experience-and-content-consumption.md) -- [第八阶段:AI 底座与教师端对话](migration/phase-8-ai-foundation.md) -- [第九阶段:SaaS 模块商城与租户交付闭环](migration/phase-9-saas-marketplace-and-onboarding.md) -- [API 契约基线](migration/contracts/README.md) - -后续产品化开发统一按 [SaaS 题库产品边界与后续接口路线](architecture/saas-product-and-api-roadmap.md) 执行。该文档定义三端边界、平台 SaaS 商城与租户学生商城的双交易域、套餐能力与 RBAC 分层,以及阶段 9A~9F。 - -## 剩余范围 - -### 1. AI 教师端对话与反馈审核 - -- 租户教师后台基础对话。 -- function calling 只允许调用受审计的后端业务函数。 -- AI 审核题目反馈只生成建议、风险等级和人工复核标记,不直接改业务状态。 -- 租户 AI Provider 配置、API Key 托管、调用审计和成本记录。 -- 后续再做推荐报告、导出、RAG 和多模型路由。 - -### 2. 内容导出与导入增强 - -- 内容导出任务创建、查询和下载。 -- 题库、题目、学生数据导出到对象存储。 -- 导入 preview/result/issue 更细化。 -- 大文件导入进度、失败行回放和重试。 - -### 3. Worker 实处理器补强 - -- `content_export` 完整导出。 -- `asset_security_scan` 接真实扫描 provider。 -- `statistics_aggregation` 增量聚合。 -- `commerce_reconciliation` 接真实 provider bill downloader。 -- `tenant_domain_recheck` 周期调度和告警联动。 - -### 4. 教师教学闭环 - -- 作业、考试、班级发布、批阅和教学报告。 -- 学生断点续答、自动交卷、主观题复核和成绩分析。 -- 教学导出通过 Worker 和对象存储交付。 - -### 5. 后台运营细化 - -- 租户 secrets 通用后台管理。 -- 租户监督规则、跟进报表和洞察报表。 -- 更细粒度 RBAC 权限点。 -- 操作审计覆盖率补齐。 -- 发票、催缴、佣金联动调账。 - -### 6. 旧路径兼容评估 - -只有前端明确依赖且重写成本高时,才增加薄兼容 Controller。兼容层不得恢复: - -- Supabase 相关路径; -- 旧 grant/adoption 公共题库授权模型; -- 旧 `QuestionIds` JSON; -- 旧 Provider 专用配置表。 - -已由新接口替代的旧行为: - -- `/api/profile/activity-tasks` -> `/api/points/tasks` -- `/api/profile/exchange-items`、`/api/profile/exchange-items/redeem` -> `/api/points/exchange-items`、`/api/points/exchange-orders` -- 公共题库 adopt/sync/grant -> 平台公共题库所有权 + `TenantQuestionReference` - -## 每批验收 - -```bash -dotnet restore TIKU-BACKEND.slnx -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-build -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-build -dotnet ef migrations script \ - --project Tiku.Infrastructure \ - --startup-project Tiku.DbMigrator -dotnet ef migrations has-pending-model-changes \ - --project Tiku.Infrastructure \ - --startup-project Tiku.DbMigrator -git diff --check -git status --short --branch -``` diff --git a/docs/migration/contracts/README.md b/docs/migration/contracts/README.md deleted file mode 100644 index edab195..0000000 --- a/docs/migration/contracts/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# API 契约基线 - -`operation-inventory.csv` 是旧 NestJS 与当前 .NET 运行时 OpenAPI 的机械比较结果。 - -状态: - -- `exact_match`:HTTP 方法和路径完全一致,仍需核对 DTO、响应、权限和业务错误。 -- `legacy_only`:只存在于旧 NestJS,后续决定迁移、替代或删除。 -- `target_only`:只存在于 .NET,通常是新 REST 设计、诊断接口或路径调整。 - -重新生成: - -```bash -python3 scripts/compare_openapi.py \ - --legacy /path/to/nest-openapi.json \ - --target /path/to/dotnet-openapi.json \ - --output docs/migration/contracts/operation-inventory.csv -``` - -原始 OpenAPI 文件较大且变化频繁,不提交仓库;只提交归一化后的 CSV 基线。 diff --git a/docs/migration/contracts/operation-inventory.csv b/docs/migration/contracts/operation-inventory.csv deleted file mode 100644 index cf4ed62..0000000 --- a/docs/migration/contracts/operation-inventory.csv +++ /dev/null @@ -1,415 +0,0 @@ -method,path,status,legacy_operation_id,legacy_summary,target_operation_id,target_summary -GET,/api/ai/school-recommendations,legacy_only,AiController_list,查询院校推荐报告,,AI 延后到 Semantic Kernel + 租户自带 API Key 独立阶段 -GET,/api/ai/school-recommendations/detail,legacy_only,AiController_detail,获取院校推荐报告详情,,AI 延后到 Semantic Kernel + 租户自带 API Key 独立阶段 -GET,/api/ai/school-recommendations/export,legacy_only,AiController_exportReport,导出院校推荐报告,,AI 延后到 Semantic Kernel + 租户自带 API Key 独立阶段 -POST,/api/ai/school-recommendations/generate,legacy_only,AiController_generate,生成院校推荐报告,,AI 延后到 Semantic Kernel + 租户自带 API Key 独立阶段 -GET,/api/assets/{assetId}/download,target_only,,,,获取资源下载地址 -GET,/api/assets/{assetId}/preview,target_only,,,,获取资源预览地址 -POST,/api/auth/login/password,target_only,,,,手机号密码登录 -POST,/api/auth/login/sms,target_only,,,,短信验证码登录 -POST,/api/auth/logout,exact_match,AuthController_logout,退出登录,,退出登录 -GET,/api/auth/me,legacy_only,AuthController_me,获取当前登录用户,, -POST,/api/auth/oauth/qq,legacy_only,AuthController_qq,QQ OAuth 登录,, -POST,/api/auth/oauth/wechat,exact_match,AuthController_wechat,微信网页 OAuth 登录,,微信网页 OAuth 登录 -POST,/api/auth/oauth/wechat-miniapp,exact_match,AuthController_miniapp,微信小程序登录,,微信小程序登录 -POST,/api/auth/phone/bind,legacy_only,AuthController_bindPhone,绑定手机号,, -POST,/api/auth/refresh,target_only,,,,刷新登录会话 -POST,/api/auth/sms/send,legacy_only,AuthController_sendSms,发送短信验证码,, -POST,/api/auth/sms/verify,legacy_only,AuthController_verifySms,校验短信验证码并登录,, -GET,/api/catalog/announcements,exact_match,CatalogController_announcements,查询公告,,查询公告 -GET,/api/catalog/app-assets,target_only,,,,查询应用资源 -GET,/api/catalog/assets,legacy_only,CatalogController_assets,查询内容资源,, -GET,/api/catalog/assets/download,legacy_only,CatalogController_download,获取资源下载地址,, -GET,/api/catalog/assets/preview,legacy_only,CatalogController_preview,获取资源预览地址,, -GET,/api/catalog/banners,exact_match,CatalogController_banners,查询首页横幅,,查询首页横幅 -GET,/api/catalog/categories,exact_match,CatalogController_categories,查询题目分类,,查询题目分类 -GET,/api/catalog/content-assets,target_only,,,,查询内容资源 -GET,/api/catalog/content-entries,exact_match,CatalogController_contentEntries,查询内容入口,,查询内容入口 -GET,/api/catalog/content-nodes,exact_match,CatalogController_contentNodes,查询内容导航节点,,查询内容导航节点 -GET,/api/catalog/exam-dates,exact_match,CatalogController_examDates,查询考试日期,,查询考试日期 -GET,/api/catalog/faqs,exact_match,CatalogController_faqs,查询常见问题,,查询常见问题 -GET,/api/catalog/handbook-chapters,exact_match,CatalogController_handbookChapters,查询知识手册章节,,查询知识手册章节 -GET,/api/catalog/handbook-entries,exact_match,CatalogController_handbookEntries,查询知识手册条目,,查询知识手册条目 -GET,/api/catalog/handbook-subjects,exact_match,CatalogController_handbookSubjects,查询知识手册科目,,查询知识手册科目 -GET,/api/catalog/images,target_only,,,,查询图片资源 -GET,/api/catalog/majors,exact_match,CatalogController_majors,查询专业目录,,查询专业目录 -GET,/api/catalog/module-nodes,exact_match,CatalogController_moduleNodes,查询模块导航节点,,查询模块导航节点 -GET,/api/catalog/practice-blueprints,exact_match,CatalogController_blueprints,查询练习蓝图,,查询练习蓝图 -GET,/api/catalog/products,exact_match,CatalogController_products,查询可购买产品,,查询可购买产品 -GET,/api/catalog/question-banks,target_only,,,,查询题库列表 -GET,/api/catalog/question-categories,target_only,,,,查询题目分类 -GET,/api/catalog/question-collections,exact_match,CatalogController_collections,查询可用题集,,查询可用题集 -GET,/api/catalog/question-collections/questions,exact_match,CatalogController_collectionQuestions,查询题集内题目,,查询题集内题目 -GET,/api/catalog/question-videos,target_only,,,,查询题目关联视频 -GET,/api/catalog/questions,exact_match,CatalogController_questions,查询已发布题目,,查询已发布题目 -GET,/api/catalog/questions/{questionId},target_only,,,,查询题目详情 -GET,/api/catalog/questions/{questionId}/versions,target_only,,,,查询题目版本 -GET,/api/catalog/region-modules,exact_match,CatalogController_regionModules,查询地区功能模块,,查询地区功能模块 -GET,/api/catalog/regions,exact_match,CatalogController_regions,查询可用地区,,查询可用地区 -GET,/api/catalog/schools,exact_match,CatalogController_schools,查询院校目录,,查询院校目录 -GET,/api/catalog/subjects,exact_match,CatalogController_subjects,查询科目目录,,查询科目目录 -GET,/api/catalog/svip-plans,exact_match,CatalogController_svipPlans,查询 SVIP 套餐,,查询 SVIP 套餐 -GET,/api/catalog/timelines,legacy_only,CatalogController_timelines,查询考试时间线,, -GET,/api/catalog/video-explanations,target_only,,,,查询视频讲解 -GET,/api/catalog/vocabulary-units,exact_match,CatalogController_vocabularyUnits,查询词汇单元,,查询词汇单元 -GET,/api/catalog/vocabulary-words,exact_match,CatalogController_vocabularyWords,查询词汇单词,,查询词汇单词 -POST,/api/commerce/activation-codes/check,legacy_only,CommerceOrdersController_checkCode,检查激活码是否可兑换,, -POST,/api/commerce/activation-codes/redeem,legacy_only,CommerceOrdersController_redeemCode,兑换激活码并发放权益,, -GET,/api/commerce/adjustment-vouchers,legacy_only,CommerceAdjustmentsController_list,查询调账凭证,, -POST,/api/commerce/adjustment-vouchers,legacy_only,CommerceAdjustmentsController_create,创建调账凭证,, -GET,/api/commerce/adjustment-vouchers/events,legacy_only,CommerceAdjustmentsController_events,查询调账凭证事件,, -GET,/api/commerce/adjustment-vouchers/report,legacy_only,CommerceAdjustmentsController_report,查询调账统计报告,, -POST,/api/commerce/adjustment-vouchers/status,legacy_only,CommerceAdjustmentsController_status,审核或关闭调账凭证,, -GET,/api/commerce/coupons,target_only,,,,查询当前用户优惠券 -POST,/api/commerce/coupons/check,target_only,,,,校验优惠券并预览订单金额 -POST,/api/commerce/coupons/claim,exact_match,CommerceOrdersController_claimCoupon,领取优惠券,,领取优惠券 -GET,/api/commerce/entitlements,legacy_only,CommerceOrdersController_entitlements,查询当前用户有效权益,, -GET,/api/commerce/entitlements/check,legacy_only,CommerceOrdersController_entitlementCheck,检查当前用户是否拥有指定权益,, -GET,/api/commerce/entitlements/current,target_only,,,,查询当前用户权益 -GET,/api/commerce/operations/anomalies,legacy_only,CommerceAdjustmentsController_operations,查询支付运营异常总览,, -GET,/api/commerce/orders,exact_match,CommerceOrdersController_list,查询当前用户订单,,查询当前用户订单 -POST,/api/commerce/orders,exact_match,CommerceOrdersController_create,创建商城订单,,创建学生端订单 -GET,/api/commerce/orders/detail,legacy_only,CommerceOrdersController_detail,查询订单详情,, -GET,/api/commerce/orders/status,legacy_only,CommerceOrdersController_status,查询订单及最新支付状态,, -GET,/api/commerce/orders/{orderNo},target_only,,,,查询当前用户订单详情 -POST,/api/commerce/payments,target_only,,,,创建订单支付 -POST,/api/commerce/payments/create,legacy_only,CommercePaymentsController_createPayment,创建第三方支付参数,, -POST,/api/commerce/payments/manual-confirm,legacy_only,CommercePaymentsController_manualConfirm,人工确认线下支付,, -POST,/api/commerce/payments/notify/alipay,exact_match,CommercePaymentsController_alipayPayment,接收支付宝支付结果通知,,支付宝支付回调 -POST,/api/commerce/payments/notify/wechat-pay,target_only,,,,微信支付回调 -POST,/api/commerce/payments/notify/wechat_pay,legacy_only,CommercePaymentsController_wechatPayment,接收微信支付成功通知,, -GET,/api/commerce/reconciliation/anomalies,legacy_only,CommerceReconciliationController_anomalies,查询对账异常汇总,, -GET,/api/commerce/reconciliation/batches,legacy_only,CommerceReconciliationController_batches,查询对账批次,, -POST,/api/commerce/reconciliation/import,legacy_only,CommerceReconciliationController_import,导入支付渠道账单并创建对账批次,, -GET,/api/commerce/reconciliation/issues,legacy_only,CommerceReconciliationController_issues,查询对账处理工单,, -POST,/api/commerce/reconciliation/issues/create,legacy_only,CommerceReconciliationController_createIssue,从对账异常创建处理工单,, -GET,/api/commerce/reconciliation/issues/events,legacy_only,CommerceReconciliationController_issueEvents,查询对账工单事件,, -POST,/api/commerce/reconciliation/issues/status,legacy_only,CommerceReconciliationController_updateIssue,更新对账工单状态,, -GET,/api/commerce/reconciliation/items,legacy_only,CommerceReconciliationController_items,查询对账明细,, -POST,/api/commerce/reconciliation/preview,legacy_only,CommerceReconciliationController_preview,预览支付渠道账单对账,, -GET,/api/commerce/reconciliation/provider-bills/jobs,legacy_only,CommerceReconciliationController_billJobs,查询渠道账单下载任务,, -POST,/api/commerce/reconciliation/provider-bills/request,legacy_only,CommerceReconciliationController_requestBill,创建渠道官方账单下载任务,, -GET,/api/commerce/refunds,legacy_only,CommercePaymentsController_refunds,查询租户退款申请,, -POST,/api/commerce/refunds,legacy_only,CommercePaymentsController_createRefund,创建退款申请,, -POST,/api/commerce/refunds/notify/alipay,legacy_only,CommercePaymentsController_alipayRefund,接收支付宝退款结果通知,, -POST,/api/commerce/refunds/notify/wechat_pay,legacy_only,CommercePaymentsController_wechatRefund,接收微信退款结果通知,, -POST,/api/commerce/refunds/status,legacy_only,CommercePaymentsController_updateRefund,审核或处理退款,, -PUT,/api/commission/member-rate,exact_match,ReferralCommissionController_memberRate,设置成员专属佣金率,, -GET,/api/commission/orders,exact_match,ReferralCommissionController_orders,查询佣金来源明细,, -GET,/api/commission/settings,exact_match,ReferralCommissionController_settings,查询租户佣金配置,, -PUT,/api/commission/settings,exact_match,ReferralCommissionController_updateSettings,保存租户佣金配置,, -GET,/api/commission/settlements,exact_match,ReferralCommissionController_settlements,查询佣金结算单,, -GET,/api/commission/settlements/export,exact_match,ReferralCommissionController_export,导出佣金结算明细,, -POST,/api/commission/settlements/generate,exact_match,ReferralCommissionController_generate,生成佣金结算单,, -GET,/api/commission/settlements/proofs,exact_match,ReferralCommissionController_proofs,查询佣金结算凭证,, -POST,/api/commission/settlements/proofs,exact_match,ReferralCommissionController_createProof,提交佣金打款或票据凭证,, -POST,/api/commission/settlements/proofs/status,exact_match,ReferralCommissionController_updateProof,审核佣金结算凭证,, -POST,/api/commission/settlements/status,exact_match,ReferralCommissionController_updateStatus,更新佣金结算状态,, -GET,/api/commission/summary,exact_match,ReferralCommissionController_summary,查询佣金汇总,, -GET,/api/crm/config,exact_match,ReferralCrmController_config,查询 CRM 推送配置,,查询 CRM 推送配置 -PUT,/api/crm/config,exact_match,ReferralCrmController_upsertConfig,保存 CRM 推送与线索分配配置,,保存 CRM 推送配置 -GET,/api/crm/dead-letters,exact_match,ReferralCrmController_deadLetters,查询 CRM 死信任务与汇总,,查询 CRM 死信任务与汇总 -GET,/api/crm/queue,exact_match,ReferralCrmController_queue,查询 CRM webhook 队列,,查询 CRM webhook 队列 -POST,/api/crm/queue/action,exact_match,ReferralCrmController_action,重试或忽略 CRM 死信任务,,重试或忽略 CRM 死信任务 -GET,/api/crm/queue/logs,exact_match,ReferralCrmController_logs,查询 CRM 队列执行日志,,查询 CRM 队列执行日志 -GET,/api/health,target_only,,,,健康检查 -POST,/api/learning/answers,exact_match,LearningController_answer,提交题目答案,,提交题目答案 -GET,/api/learning/favorites/questions,exact_match,LearningController_favoriteQuestions,查询收藏题目,,查询收藏题目 -POST,/api/learning/favorites/questions,exact_match,LearningController_toggleFavoriteQuestion,收藏或取消收藏题目,,收藏或取消收藏题目 -GET,/api/learning/leaderboard,exact_match,LearningController_leaderboard,查询学习排行榜,,查询学习排行榜 -GET,/api/learning/practice-reports,exact_match,LearningController_reports,查询练习报告列表,,查询练习报告列表 -POST,/api/learning/practice-sessions,exact_match,LearningController_createSession,创建练习会话,,创建练习会话 -GET,/api/learning/practice-sessions/detail,exact_match,LearningController_sessionDetail,获取练习会话详情,,获取练习会话详情 -GET,/api/learning/practice-sessions/history,exact_match,LearningController_history,查询练习历史,,查询练习历史 -GET,/api/learning/practice-sessions/report,exact_match,LearningController_sessionReport,获取练习会话报告,,获取练习会话报告 -POST,/api/learning/practice-sessions/submit,exact_match,LearningController_submitSession,提交练习会话,,提交练习会话并生成报告 -GET,/api/learning/stats,exact_match,LearningController_stats,查询学习统计,,查询学习统计 -GET,/api/learning/trend,exact_match,LearningController_trend,查询学习趋势,,查询学习趋势 -GET,/api/learning/vocabulary/favorites,exact_match,LearningController_favoriteWords,查询收藏单词,,查询收藏单词 -POST,/api/learning/vocabulary/favorites,exact_match,LearningController_toggleFavoriteWord,收藏或取消收藏单词,,收藏或取消收藏单词 -GET,/api/learning/vocabulary/progress,exact_match,LearningController_wordProgress,查询单词学习进度,,查询单词学习进度 -POST,/api/learning/vocabulary/progress,exact_match,LearningController_updateWordProgress,更新单词学习进度,,更新单词学习进度 -POST,/api/learning/vocabulary/review,exact_match,LearningController_reviewWord,提交单词复习结果,,提交单词复习结果 -GET,/api/learning/vocabulary/review-plan,exact_match,LearningController_wordPlan,生成单词复习计划,,生成单词复习计划 -GET,/api/learning/vocabulary/stats,exact_match,LearningController_wordStats,查询单词学习统计,,查询单词学习统计 -GET,/api/learning/wrong-questions,exact_match,LearningController_wrongQuestions,查询错题列表,,查询错题列表 -POST,/api/learning/wrong-questions/resolve,exact_match,LearningController_resolveWrong,将错题标记为已解决,,将错题标记为已解决 -GET,/api/learning/wrong-questions/review-plan,exact_match,LearningController_wrongPlan,生成错题复习计划,,生成错题复习计划 -GET,/api/me,target_only,,,, -GET,/api/platform-admin/audit-alert-rules,legacy_only,PlatformAdminAuditController_rules,查询平台审计告警规则,, -GET,/api/platform-admin/audit-alerts,legacy_only,PlatformAdminAuditController_alerts,查询平台审计告警,, -POST,/api/platform-admin/audit-alerts/status,legacy_only,PlatformAdminAuditController_updateAlert,更新平台审计告警状态,, -GET,/api/platform-admin/audit-logs,legacy_only,PlatformAdminAuditController_logs,查询平台审计日志,, -GET,/api/platform-admin/audit-logs/export,legacy_only,PlatformAdminAuditController_exportLogs,导出平台审计日志,, -GET,/api/platform-admin/audit-notification-channels,legacy_only,PlatformAdminAuditController_channels,查询审计告警通知渠道,, -PUT,/api/platform-admin/audit-notification-channels,legacy_only,PlatformAdminAuditController_upsertChannel,创建或更新审计告警通知渠道,, -GET,/api/platform-admin/audit-notification-events,legacy_only,PlatformAdminAuditController_events,查询审计告警通知事件,, -GET,/api/platform-admin/dunning-notification-channels,legacy_only,PlatformAdminDunningChannelsController_channels,查询平台催缴通知渠道,, -PUT,/api/platform-admin/dunning-notification-channels,legacy_only,PlatformAdminDunningChannelsController_upsertChannel,创建或更新平台催缴通知渠道,, -GET,/api/platform-admin/dunning-notification-events,legacy_only,PlatformAdminDunningEventsController_events,查询平台催缴通知事件,, -GET,/api/platform-admin/invoices,legacy_only,PlatformAdminBillingController_invoices,查询平台租户账单,, -POST,/api/platform-admin/invoices,legacy_only,PlatformAdminBillingController_createInvoice,手工创建租户账单,, -POST,/api/platform-admin/invoices/from-subscription,legacy_only,PlatformAdminBillingController_fromSubscription,为单个订阅生成账单,, -POST,/api/platform-admin/invoices/from-subscriptions-batch,legacy_only,PlatformAdminBillingController_fromSubscriptionsBatch,批量生成订阅账单,, -POST,/api/platform-admin/invoices/from-usage-overage,legacy_only,PlatformAdminBillingController_fromUsage,批量生成用量超额账单,, -POST,/api/platform-admin/invoices/payments/manual-confirm,legacy_only,PlatformAdminBillingController_confirmPayment,人工确认平台服务费收款,, -POST,/api/platform-admin/invoices/process-overdue,legacy_only,PlatformAdminBillingController_processOverdue,处理逾期账单并创建催缴记录,, -GET,/api/platform-admin/invoices/reminders,legacy_only,PlatformAdminBillingController_reminders,查询账单催缴记录,, -GET,/api/platform-admin/invoices/subscription-candidates,legacy_only,PlatformAdminBillingController_subscriptionCandidates,预览订阅账单候选,, -GET,/api/platform-admin/invoices/usage-overage-candidates,legacy_only,PlatformAdminBillingController_usageCandidates,预览用量超额账单候选,, -GET,/api/platform-admin/overview,legacy_only,PlatformAdminOverviewController_overview,查询平台经营概览,, -GET,/api/platform-admin/permissions,legacy_only,PlatformAdminOverviewController_permissions,查询当前平台管理员权限,, -GET,/api/platform-admin/plans,legacy_only,PlatformAdminOverviewController_plans,查询平台 SaaS 套餐,, -GET,/api/platform-admin/question-bank-grants,legacy_only,PlatformAdminQuestionBanksController_grants,查询公共题库授权规则,, -PUT,/api/platform-admin/question-bank-grants,legacy_only,PlatformAdminQuestionBanksController_upsertGrant,创建或更新公共题库授权,, -GET,/api/platform-admin/question-bank-sync-status,legacy_only,PlatformAdminQuestionBanksController_syncStatus,查询公共题库采用与同步状态,, -GET,/api/platform-admin/question-banks,legacy_only,PlatformAdminQuestionBanksController_banks,查询平台公共题库,, -GET,/api/platform-admin/staff,legacy_only,PlatformAdminOverviewController_staff,查询平台员工列表,, -PUT,/api/platform-admin/staff,legacy_only,PlatformAdminOverviewController_upsertStaff,创建或更新平台员工,, -PATCH,/api/platform-admin/staff/status,legacy_only,PlatformAdminOverviewController_updateStaffStatus,启用或禁用平台员工,, -POST,/api/platform-admin/subscriptions,legacy_only,PlatformAdminBillingController_createSubscription,创建租户订阅,, -GET,/api/platform-admin/tenants,legacy_only,PlatformAdminTenantsController_list,查询平台租户列表,, -POST,/api/platform-admin/tenants,legacy_only,PlatformAdminTenantsController_create,创建平台租户,, -PUT,/api/platform-admin/tenants/billing-profile,legacy_only,PlatformAdminTenantsController_billingProfile,保存租户账务与开票资料,, -GET,/api/platform-admin/tenants/detail,legacy_only,PlatformAdminTenantsController_detail,查询平台租户详情,, -PATCH,/api/platform-admin/tenants/status,legacy_only,PlatformAdminTenantsController_status,更新租户业务与账务状态,, -GET,/api/platform-admin/usage,legacy_only,PlatformAdminBillingController_usage,查询租户平台用量记录,, -POST,/api/platform-admin/usage,legacy_only,PlatformAdminBillingController_recordUsage,记录租户平台用量,, -GET,/api/points/exchange-items,target_only,,,,查询积分兑换项 -GET,/api/points/exchange-orders,target_only,,,,查询当前用户积分兑换订单 -POST,/api/points/exchange-orders,target_only,,,,创建积分兑换订单 -GET,/api/points/summary,target_only,,,,查询当前用户积分摘要 -GET,/api/points/tasks,target_only,,,,查询当前可领取积分任务 -POST,/api/points/tasks/claim,target_only,,,,领取积分任务奖励 -GET,/api/profile/activity-tasks,legacy_only,ProfileController_tasks,查询积分活动任务,,由 /api/points/tasks 替代 -POST,/api/profile/activity-tasks/claim,legacy_only,ProfileController_claimTask,领取活动任务奖励,,由 /api/points/tasks/claim 替代 -GET,/api/profile/badges,exact_match,ProfileController_badges,查询徽章列表,,查询徽章列表 -POST,/api/profile/check-in,exact_match,ProfileController_checkIn,每日签到,,每日签到 -GET,/api/profile/exam-countdowns,exact_match,ProfileController_countdowns,查询考试倒计时,,查询考试倒计时 -GET,/api/profile/exchange-items,legacy_only,ProfileController_exchangeItems,查询积分兑换商品,,由 /api/points/exchange-items 替代 -POST,/api/profile/exchange-items/redeem,legacy_only,ProfileController_redeem,兑换积分商品,,由 /api/points/exchange-orders 替代 -GET,/api/profile/feedbacks,exact_match,ProfileController_feedbacks,查询反馈记录,,查询反馈记录 -POST,/api/profile/feedbacks,exact_match,ProfileController_submitFeedback,提交意见反馈,,提交意见反馈 -GET,/api/profile/me,exact_match,ProfileController_me,获取当前学生资料,,获取当前学生资料 -PATCH,/api/profile/me,exact_match,ProfileController_updateMe,更新当前学生资料,,更新当前学生资料 -GET,/api/profile/notifications,exact_match,ProfileController_notificationList,查询用户通知,,查询用户通知 -POST,/api/profile/notifications/status,exact_match,ProfileController_notificationStatus,更新通知状态,,更新通知状态 -GET,/api/profile/score-events,exact_match,ProfileController_scoreEvents,查询积分流水,,查询当前用户积分流水 -GET,/api/questions/videos,exact_match,QuestionVideoController_list,查询单道题目的解析视频,,查询单道题目的解析视频 -POST,/api/questions/videos/batch,exact_match,QuestionVideoController_batch,批量查询题目解析视频,,批量查询题目解析视频 -POST,/api/referral/bind,exact_match,ReferralPublicController_bind,绑定当前用户的推荐归属,,绑定当前用户推荐归属 -GET,/api/referral/conversion-report,exact_match,ReferralManagementController_conversion,查询推荐转化与佣金报告,,查询推荐转化报告 -POST,/api/referral/invite-code,exact_match,ReferralPublicController_invite,生成或查询当前成员邀请码,,生成或查询当前成员邀请码 -POST,/api/referral/manual-bind,exact_match,ReferralManagementController_manualBind,人工调整学生推荐归属,,人工调整学生推荐归属 -POST,/api/referral/qrcode,exact_match,ReferralPublicController_qrcode,生成或查询推广二维码,,生成或查询推广二维码 -POST,/api/referral/resolve,exact_match,ReferralPublicController_resolve,解析推荐邀请码,,解析推荐邀请码 -GET,/api/referral/sales-clients,exact_match,ReferralManagementController_clients,查询推荐人名下客户,,查询推荐人名下客户 -GET,/api/referral/sales-stats,exact_match,ReferralManagementController_salesStats,查询销售推荐统计排行,,查询销售推荐统计排行 -GET,/api/referral/stats,exact_match,ReferralManagementController_stats,查询推荐人个人统计,,查询推荐人个人统计 -GET,/api/referral/team,exact_match,ReferralManagementController_team,查询推荐团队成员,,查询推荐团队成员 -PUT,/api/referral/team,exact_match,ReferralManagementController_upsertTeam,新增或更新推荐团队关系,,新增或更新推荐团队关系 -POST,/api/referral/track-event,exact_match,ReferralPublicController_track,记录推荐行为并按规则创建线索,,记录推荐行为 -GET,/api/scoreline/fields,exact_match,ScorelineController_fields,查询分数线字段配置,,查询分数线字段配置 -GET,/api/scoreline/majors,legacy_only,ScorelineController_majors,查询分数线专业,, -GET,/api/scoreline/records,exact_match,ScorelineController_records,分页查询分数线记录,,分页查询分数线记录 -GET,/api/scoreline/schools,legacy_only,ScorelineController_schools,查询分数线院校,, -GET,/api/scoreline/trend,exact_match,ScorelineController_trend,查询历年分数线趋势,,查询历年分数线趋势 -GET,/api/scoreline/years,exact_match,ScorelineController_years,查询分数线可用年份,,查询分数线可用年份 -GET,/api/tenant-admin/activation-codes,legacy_only,TenantCodesController_codes,查询激活码,, -PUT,/api/tenant-admin/activation-codes,legacy_only,TenantCodesController_upsertCode,新增或更新激活码,, -POST,/api/tenant-admin/activation-codes/generate,legacy_only,TenantCodesController_generate,批量生成激活码,, -GET,/api/tenant-admin/announcements,legacy_only,TenantMarketingOperationsController_announcements,查询租户公告,, -PUT,/api/tenant-admin/announcements,legacy_only,TenantMarketingOperationsController_upsertAnnouncement,新增或更新租户公告,, -GET,/api/tenant-admin/audit-logs,exact_match,TenantGovernanceController_audit,查询租户审计日志,,查询租户审计日志 -GET,/api/tenant-admin/auth-providers,exact_match,TenantIntegrationConfigController_authProviders,查询租户登录 Provider 公开配置,,查询租户登录 Provider 公开配置 -PUT,/api/tenant-admin/auth-providers,exact_match,TenantIntegrationConfigController_upsertAuthProvider,新增或更新租户登录 Provider,,新增或更新租户登录 Provider -GET,/api/tenant-admin/badge-grants,exact_match,TenantBadgeManagementController_grants,查询勋章发放记录,,查询勋章发放记录 -POST,/api/tenant-admin/badge-grants,exact_match,TenantBadgeManagementController_grant,向租户成员发放勋章,,向租户成员发放勋章 -GET,/api/tenant-admin/badges,exact_match,TenantBadgeManagementController_badges,查询租户勋章,,查询租户勋章 -PUT,/api/tenant-admin/badges,exact_match,TenantBadgeManagementController_upsertBadge,新增或更新租户勋章,,新增或更新租户勋章 -GET,/api/tenant-admin/banners,legacy_only,TenantMarketingOperationsController_banners,查询租户 Banner,, -PUT,/api/tenant-admin/banners,legacy_only,TenantMarketingOperationsController_upsertBanner,新增或更新租户 Banner,, -PUT,/api/tenant-admin/branding,exact_match,TenantAppearanceController_branding,更新租户品牌信息,,更新租户品牌信息 -GET,/api/tenant-admin/classes,exact_match,TenantAdminClassesController_list,查询可管理的班级,,查询租户班级 -PUT,/api/tenant-admin/classes,exact_match,TenantAdminClassesController_upsert,新增或更新班级,,新增或更新租户班级 -POST,/api/tenant-admin/classes/disable,exact_match,TenantAdminClassesController_disable,停用班级,,停用租户班级 -GET,/api/tenant-admin/classes/members,exact_match,TenantAdminClassesController_members,查询班级成员,,查询班级成员 -PUT,/api/tenant-admin/classes/members,exact_match,TenantAdminClassesController_upsertMember,新增或更新班级成员,,新增或更新班级成员 -POST,/api/tenant-admin/classes/members/bulk-assign,legacy_only,TenantAdminClassesController_bulkAssign,批量分配班级成员,, -POST,/api/tenant-admin/classes/members/remove,exact_match,TenantAdminClassesController_removeMember,移除班级成员,,移除班级成员 -GET,/api/tenant-admin/code-batches,legacy_only,TenantCodesController_batches,查询兑换码批次,, -PUT,/api/tenant-admin/code-batches,legacy_only,TenantCodesController_upsertBatch,新增或更新兑换码批次,, -GET,/api/tenant-admin/coupons,legacy_only,TenantCodesController_coupons,查询优惠券,, -PUT,/api/tenant-admin/coupons,legacy_only,TenantCodesController_upsertCoupon,新增或更新优惠券,, -GET,/api/tenant-admin/coupons/redemptions,legacy_only,TenantCodesController_redemptions,查询优惠券核销明细,, -GET,/api/tenant-admin/coupons/report,legacy_only,TenantCodesController_report,查询优惠券核销报表,, -GET,/api/tenant-admin/dashboard,legacy_only,TenantAdminInsightsController_dashboard,查询租户运营管理看板,, -GET,/api/tenant-admin/domains,exact_match,TenantIntegrationConfigController_domains,查询租户域名,,查询租户域名 -POST,/api/tenant-admin/domains,exact_match,TenantIntegrationConfigController_createDomain,添加租户域名,,添加租户域名 -GET,/api/tenant-admin/exam-dates,legacy_only,TenantMarketingOperationsController_examDates,查询租户考试日期,, -PUT,/api/tenant-admin/exam-dates,legacy_only,TenantMarketingOperationsController_upsertExamDate,新增或更新考试日期,, -GET,/api/tenant-admin/faqs,legacy_only,TenantMarketingOperationsController_faqs,查询租户常见问题,, -PUT,/api/tenant-admin/faqs,legacy_only,TenantMarketingOperationsController_upsertFaq,新增或更新租户常见问题,, -GET,/api/tenant-admin/feedbacks,exact_match,TenantMarketingOperationsController_feedbacks,查询用户反馈,,查询用户反馈 -GET,/api/tenant-admin/feedbacks/events,legacy_only,TenantMarketingOperationsController_feedbackEvents,查询单条反馈处理事件,, -GET,/api/tenant-admin/feedbacks/report,legacy_only,TenantMarketingOperationsController_feedbackReport,查询反馈处理统计报表,, -POST,/api/tenant-admin/feedbacks/status,exact_match,TenantMarketingOperationsController_updateFeedback,更新反馈处理状态并可发放奖励,,处理用户反馈 -GET,/api/tenant-admin/members,exact_match,TenantGovernanceController_members,查询租户成员,,查询租户成员 -PUT,/api/tenant-admin/members,exact_match,TenantGovernanceController_upsert,新增或更新租户成员,,新增或更新租户成员 -POST,/api/tenant-admin/members/disable,exact_match,TenantGovernanceController_disable,停用租户成员并撤销会话,,停用租户成员并撤销会话 -GET,/api/tenant-admin/notifications,target_only,,,,查询用户站内通知 -PUT,/api/tenant-admin/notifications,target_only,,,,新增或更新用户站内通知 -GET,/api/tenant-admin/overview,legacy_only,TenantAdminInsightsController_overview,查询租户基础信息与公开配置,, -GET,/api/tenant-admin/payment-accounts,legacy_only,TenantIntegrationConfigController_payments,查询租户支付账号公开配置,, -PUT,/api/tenant-admin/payment-accounts,legacy_only,TenantIntegrationConfigController_upsertPayment,新增或更新租户支付账号,, -GET,/api/tenant-admin/permissions,exact_match,TenantAdminRolesController_permissions,查询当前管理员权限矩阵,,查询租户后台权限矩阵 -GET,/api/tenant-admin/point-activity-claims,legacy_only,TenantPointsController_claims,查询积分任务领取记录,, -GET,/api/tenant-admin/point-activity-tasks,legacy_only,TenantPointsController_tasks,查询积分活动任务,, -PUT,/api/tenant-admin/point-activity-tasks,legacy_only,TenantPointsController_upsertTask,新增或更新积分活动任务,, -GET,/api/tenant-admin/point-exchange-items,legacy_only,TenantPointsController_items,查询积分兑换项,, -PUT,/api/tenant-admin/point-exchange-items,legacy_only,TenantPointsController_upsertItem,新增或更新积分兑换项,, -GET,/api/tenant-admin/point-exchange-orders,legacy_only,TenantPointsController_orders,查询积分兑换订单,, -GET,/api/tenant-admin/points-risk-report,legacy_only,TenantPointsController_risk,查询积分风险报表,, -GET,/api/tenant-admin/role-templates,exact_match,TenantAdminRolesController_templates,查询租户角色模板,,查询租户角色模板 -PUT,/api/tenant-admin/role-templates,exact_match,TenantAdminRolesController_upsert,新增或更新租户角色模板,,新增或更新租户角色模板 -POST,/api/tenant-admin/role-templates/disable,exact_match,TenantAdminRolesController_disable,停用租户角色模板,,停用租户角色模板 -GET,/api/tenant-admin/secrets,legacy_only,TenantSecretVaultController_list,查询租户密钥掩码状态,, -PUT,/api/tenant-admin/secrets,legacy_only,TenantSecretVaultController_upsert,写入或轮换租户密钥,, -PUT,/api/tenant-admin/settings,exact_match,TenantAppearanceController_settings,更新租户公开设置与功能开关,,更新租户公开设置与功能开关 -GET,/api/tenant-admin/student-followups,target_only,,,,查询学生跟进 -PUT,/api/tenant-admin/student-followups,target_only,,,,新增或更新学生跟进 -GET,/api/tenant-admin/student-notes,target_only,,,,查询学生备注 -PUT,/api/tenant-admin/student-notes,target_only,,,,新增或更新学生备注 -GET,/api/tenant-admin/students,exact_match,TenantAdminStudentsController_list,游标分页查询租户学生,,查询租户学生 -PUT,/api/tenant-admin/students,exact_match,TenantAdminStudentsController_upsert,新增或更新租户学生档案,,新增或更新租户学生档案 -POST,/api/tenant-admin/students/bulk-upsert,legacy_only,TenantAdminStudentsController_bulkUpsert,批量新增或更新租户学生,, -POST,/api/tenant-admin/students/crm-push,legacy_only,TenantAdminStudentsController_crmPush,批量创建学生跟进并推送 CRM,, -GET,/api/tenant-admin/students/followups,legacy_only,TenantAdminEngagementController_followups,查询学生跟进任务,, -PUT,/api/tenant-admin/students/followups,legacy_only,TenantAdminEngagementController_upsertFollowup,新增或更新学生跟进任务,, -GET,/api/tenant-admin/students/followups/report,legacy_only,TenantAdminEngagementController_report,查询学生跟进统计报表,, -GET,/api/tenant-admin/students/notes,legacy_only,TenantAdminEngagementController_notes,查询学生备注,, -PUT,/api/tenant-admin/students/notes,legacy_only,TenantAdminEngagementController_upsertNote,新增或更新学生备注,, -POST,/api/tenant-admin/students/status,exact_match,TenantAdminStudentsController_status,禁用、邀请或恢复租户学生,,更新租户学生状态 -POST,/api/tenant-admin/students/supervision/generate,legacy_only,TenantAdminSupervisionController_generate,批量生成学习督导跟进任务,, -GET,/api/tenant-admin/students/supervision/preview,legacy_only,TenantAdminSupervisionController_preview,预览学习风险学生与督导原因,, -GET,/api/tenant-admin/students/supervision/rules,legacy_only,TenantAdminSupervisionController_rules,查询学习督导规则,, -PUT,/api/tenant-admin/students/supervision/rules,legacy_only,TenantAdminSupervisionController_upsertRule,新增或更新学习督导规则,, -GET,/api/tenant-admin/teachers,legacy_only,TenantAdminInsightsController_teachers,查询租户教师,, -GET,/api/tenant-admin/theme,exact_match,TenantAppearanceController_theme,查询租户当前主题与草稿,,查询租户当前主题与草稿 -GET,/api/tenant-admin/theme-templates,exact_match,TenantAppearanceController_templates,查询可用的租户主题模板,,查询可用租户主题模板 -POST,/api/tenant-admin/theme/preview,exact_match,TenantAppearanceController_preview,生成并保存租户主题草稿,,生成租户主题草稿 -POST,/api/tenant-admin/theme/publish,exact_match,TenantAppearanceController_publish,发布租户主题,,发布租户主题 -GET,/api/tenant-admin/user-notifications,legacy_only,TenantAdminInsightsController_notifications,查询租户用户通知,, -GET,/api/tenant-commerce/activation-codes,target_only,,,,查询兑换码 -POST,/api/tenant-commerce/activation-codes/redeem,target_only,,,,后台核销兑换码 -POST,/api/tenant-commerce/code-batches,target_only,,,,创建兑换码批次 -GET,/api/tenant-commerce/coupons,target_only,,,,查询租户优惠券 -PUT,/api/tenant-commerce/coupons,target_only,,,,新增或更新租户优惠券 -GET,/api/tenant-commerce/coupons/redemptions,target_only,,,,查询优惠券领取和核销记录 -GET,/api/tenant-commerce/coupons/report,target_only,,,,查询优惠券基础报表 -GET,/api/tenant-commerce/orders,target_only,,,,查询租户订单 -GET,/api/tenant-commerce/payment-accounts,target_only,,,,查询租户支付账号 -PUT,/api/tenant-commerce/payment-accounts,target_only,,,,新增或更新租户支付账号 -GET,/api/tenant-commerce/payments,target_only,,,,查询租户支付记录 -GET,/api/tenant-commerce/point-activity-claims,target_only,,,,查询积分任务领取记录 -GET,/api/tenant-commerce/point-activity-tasks,target_only,,,,查询积分活动任务 -PUT,/api/tenant-commerce/point-activity-tasks,target_only,,,,新增或更新积分活动任务 -GET,/api/tenant-commerce/point-exchange-items,target_only,,,,查询积分兑换项 -PUT,/api/tenant-commerce/point-exchange-items,target_only,,,,新增或更新积分兑换项 -GET,/api/tenant-commerce/point-exchange-orders,target_only,,,,查询积分兑换订单 -POST,/api/tenant-commerce/point-exchange-orders/status,target_only,,,,更新积分兑换订单状态 -PUT,/api/tenant-commerce/secrets,target_only,,,,写入或轮换租户密钥 -GET,/api/tenant-content/assets,exact_match,TenantContentAssetsController_list,查询租户内容资产,,查询租户内容资产 -PUT,/api/tenant-content/assets,exact_match,TenantContentAssetsController_upsert,新增或更新内容资产,,新增或更新内容资产 -GET,/api/tenant-content/assets/access-events,exact_match,TenantContentAssetsController_accessEvents,查询资产访问审计事件,,查询资产访问审计事件 -POST,/api/tenant-content/assets/confirm-upload,legacy_only,TenantContentAssetsController_confirmUpload,确认并验证内容资产上传,, -GET,/api/tenant-content/assets/security-scan-events,exact_match,TenantContentAssetsController_scanEvents,查询资产安全扫描事件,,查询资产安全扫描事件 -POST,/api/tenant-content/assets/sign-download,exact_match,TenantContentAssetsController_signDownload,签发管理侧资产下载地址,,签发管理侧资产下载地址 -POST,/api/tenant-content/assets/sign-preview,exact_match,TenantContentAssetsController_signPreview,签发管理侧资产预览地址,,签发管理侧资产预览地址 -POST,/api/tenant-content/assets/sign-upload,legacy_only,TenantContentAssetsController_signUpload,签发内容资产上传凭证,, -POST,/api/tenant-content/assets/uploads/confirm,target_only,,,,确认资产上传完成 -POST,/api/tenant-content/assets/uploads/sign,target_only,,,,创建资产上传签名 -GET,/api/tenant-content/content-entries,legacy_only,ContentNavigationController_entries,查询租户内容入口,, -PUT,/api/tenant-content/content-entries,legacy_only,ContentNavigationController_upsertEntry,新增或更新内容入口,, -GET,/api/tenant-content/content-nodes,legacy_only,ContentNavigationController_nodes,查询内容导航节点,, -PUT,/api/tenant-content/content-nodes,legacy_only,ContentNavigationController_upsertNode,新增或更新内容导航节点,, -GET,/api/tenant-content/entries,target_only,,,,查询租户内容入口 -POST,/api/tenant-content/entries,target_only,,,,创建或更新内容入口 -GET,/api/tenant-content/exports/jobs,legacy_only,TenantContentExportsController_jobs,查询题库导出任务,, -POST,/api/tenant-content/exports/questions,legacy_only,TenantContentExportsController_create,创建题库导出或立即生成 JSON,, -GET,/api/tenant-content/handbook-chapters,exact_match,HandbookManagementController_chapters,查询管理侧知识手册章节,,查询管理侧知识手册章节 -PUT,/api/tenant-content/handbook-chapters,exact_match,HandbookManagementController_upsertChapter,新增或更新知识手册章节,,新增或更新知识手册章节 -GET,/api/tenant-content/handbook-entries,exact_match,HandbookManagementController_entries,查询管理侧知识手册条目,,查询管理侧知识手册条目 -PUT,/api/tenant-content/handbook-entries,exact_match,HandbookManagementController_upsertEntry,新增或更新知识手册条目,,新增或更新知识手册条目 -GET,/api/tenant-content/handbook-subjects,exact_match,HandbookManagementController_subjects,查询管理侧知识手册科目,,查询管理侧知识手册科目 -PUT,/api/tenant-content/handbook-subjects,exact_match,HandbookManagementController_upsertSubject,新增或更新知识手册科目,,新增或更新知识手册科目 -GET,/api/tenant-content/import-jobs,target_only,,,,查询内容导入任务 -GET,/api/tenant-content/import-jobs/{jobId},target_only,,,,查询内容导入任务详情 -GET,/api/tenant-content/imports,legacy_only,TenantContentImportsController_jobs,查询内容导入任务,, -GET,/api/tenant-content/imports/detail,exact_match,TenantContentImportsController_detail,查询内容导入任务详情,,查询内容导入任务详情 -GET,/api/tenant-content/imports/field-mapping,exact_match,TenantContentImportsController_fieldMapping,查询导入字段映射说明,,查询导入字段映射 -POST,/api/tenant-content/imports/handbook,legacy_only,TenantContentImportsController_importHandbook,执行或排队知识手册导入,,由 /api/tenant-content/imports/{importType} 替代 -GET,/api/tenant-content/imports/issues,exact_match,TenantContentImportsController_issues,查询内容导入问题明细,,查询内容导入问题明细 -GET,/api/tenant-content/imports/post-check,exact_match,TenantContentImportsController_postCheckStatus,查询内容导入后检查状态,,查询内容导入后检查状态 -POST,/api/tenant-content/imports/post-check,exact_match,TenantContentImportsController_runPostCheck,执行内容导入后完整性检查,,执行内容导入后完整性检查 -POST,/api/tenant-content/imports/preview/handbook,legacy_only,TenantContentImportsController_previewHandbook,预览知识手册导入数据,,由 /api/tenant-content/imports/preview/{importType} 替代 -POST,/api/tenant-content/imports/preview/questions,legacy_only,TenantContentImportsController_previewQuestions,预览题目导入数据,,由 /api/tenant-content/imports/preview/{importType} 替代 -POST,/api/tenant-content/imports/preview/scoreline,legacy_only,TenantContentImportsController_previewScoreline,预览分数线导入数据,,由 /api/tenant-content/imports/preview/{importType} 替代 -POST,/api/tenant-content/imports/preview/videos,legacy_only,TenantContentImportsController_previewVideos,预览视频解析导入数据,,由 /api/tenant-content/imports/preview/{importType} 替代 -POST,/api/tenant-content/imports/preview/vocabulary,legacy_only,TenantContentImportsController_previewVocabulary,预览词汇导入数据,,由 /api/tenant-content/imports/preview/{importType} 替代 -POST,/api/tenant-content/imports/preview/{importType},target_only,,,,预览内容导入数据 -POST,/api/tenant-content/imports/questions,legacy_only,TenantContentImportsController_importQuestions,执行或排队题目导入,,由 /api/tenant-content/imports/{importType} 替代 -POST,/api/tenant-content/imports/scoreline,legacy_only,TenantContentImportsController_importScoreline,执行或排队分数线导入,,由 /api/tenant-content/imports/{importType} 替代 -GET,/api/tenant-content/imports/templates,exact_match,TenantContentImportsController_template,获取内容导入模板,,获取内容导入模板 -POST,/api/tenant-content/imports/videos,legacy_only,TenantContentImportsController_importVideos,执行或排队视频解析导入,,由 /api/tenant-content/imports/{importType} 替代 -POST,/api/tenant-content/imports/vocabulary,legacy_only,TenantContentImportsController_importVocabulary,执行或排队词汇导入,,由 /api/tenant-content/imports/{importType} 替代 -POST,/api/tenant-content/imports/{importType},target_only,,,,执行同步或异步内容导入 -GET,/api/tenant-content/media-analytics/asset-events,legacy_only,TenantContentAssetsController_assetAnalytics,查询媒体资产访问明细,, -GET,/api/tenant-content/media-analytics/summary,legacy_only,TenantContentAssetsController_summary,查询媒体访问分析汇总,, -GET,/api/tenant-content/media-analytics/video-events,legacy_only,TenantContentAssetsController_videoAnalytics,查询媒体视频播放明细,, -GET,/api/tenant-content/nodes,target_only,,,,查询租户内容节点 -POST,/api/tenant-content/nodes,target_only,,,,创建或更新内容节点 -GET,/api/tenant-content/notifications,legacy_only,TenantContentPublicBanksController_notifications,查询租户内容通知,, -POST,/api/tenant-content/notifications/status,legacy_only,TenantContentPublicBanksController_updateNotifications,批量更新租户内容通知状态,, -GET,/api/tenant-content/operations/{kind},target_only,,,,查询运营内容 -PUT,/api/tenant-content/operations/{kind},target_only,,,,新增或更新运营内容 -GET,/api/tenant-content/practice-blueprints,exact_match,ContentNavigationController_blueprints,查询练习蓝图,,查询练习蓝图 -POST,/api/tenant-content/practice-blueprints,target_only,,,,创建或更新练习蓝图 -PUT,/api/tenant-content/practice-blueprints,legacy_only,ContentNavigationController_upsertBlueprint,新增或更新练习蓝图,, -GET,/api/tenant-content/public-question-banks,legacy_only,TenantContentPublicBanksController_list,查询可采用的公共题库,, -POST,/api/tenant-content/public-question-banks/adopt,legacy_only,TenantContentPublicBanksController_adopt,采用公共题库并复制内容,, -GET,/api/tenant-content/public-question-banks/conflicts,legacy_only,TenantContentPublicBanksController_conflicts,查询公共题库同步冲突,, -POST,/api/tenant-content/public-question-banks/conflicts/resolve,legacy_only,TenantContentPublicBanksController_resolve,解决单条公共题库同步冲突,, -POST,/api/tenant-content/public-question-banks/conflicts/resolve-batch,legacy_only,TenantContentPublicBanksController_resolveBatch,批量解决公共题库同步冲突,, -POST,/api/tenant-content/public-question-banks/sync,legacy_only,TenantContentPublicBanksController_sync,同步已采用的公共题库,, -GET,/api/tenant-content/question-collections,exact_match,ContentNavigationController_collections,查询题目集合,,查询租户题集 -POST,/api/tenant-content/question-collections,target_only,,,,创建或更新题集 -PUT,/api/tenant-content/question-collections,legacy_only,ContentNavigationController_upsertCollection,新增或更新题目集合,, -PUT,/api/tenant-content/question-collections/items,legacy_only,ContentNavigationController_replaceItems,整体替换题集中的题目,, -POST,/api/tenant-content/question-collections/items/replace,target_only,,,,替换题集题目 -POST,/api/tenant-content/question-videos,exact_match,VideoManagementController_bind,绑定题目与解析视频,,绑定题目与解析视频 -PATCH,/api/tenant-content/questions,exact_match,QuestionManagementController_update,更新题目并可选择创建新版本,,更新题目并可选择创建新版本 -POST,/api/tenant-content/questions,exact_match,QuestionManagementController_create,创建题目及首个版本,,创建题目及首个版本 -GET,/api/tenant-content/scoreline/fields,exact_match,ScorelineManagementController_fields,查询管理侧分数线字段,,查询管理侧分数线字段 -PUT,/api/tenant-content/scoreline/fields,exact_match,ScorelineManagementController_upsertField,新增或更新动态分数线字段,,新增或更新动态分数线字段 -GET,/api/tenant-content/scoreline/majors,exact_match,ScorelineManagementController_majors,查询管理侧分数线专业,,查询管理侧分数线专业 -PUT,/api/tenant-content/scoreline/majors,exact_match,ScorelineManagementController_upsertMajor,新增或更新分数线专业,,新增或更新分数线专业 -GET,/api/tenant-content/scoreline/records,exact_match,ScorelineManagementController_records,查询管理侧分数线记录,,查询管理侧分数线记录 -PUT,/api/tenant-content/scoreline/records,exact_match,ScorelineManagementController_upsertRecord,新增或更新分数线记录,,新增或更新分数线记录 -GET,/api/tenant-content/scoreline/schools,exact_match,ScorelineManagementController_schools,查询管理侧分数线院校,,查询管理侧分数线院校 -PUT,/api/tenant-content/scoreline/schools,exact_match,ScorelineManagementController_upsertSchool,新增或更新分数线院校,,新增或更新分数线院校 -GET,/api/tenant-content/scoreline/trend,target_only,,,,查询分数线趋势摘要 -GET,/api/tenant-content/scoreline/years,target_only,,,,查询分数线年份 -GET,/api/tenant-content/videos,exact_match,VideoManagementController_list,查询租户视频解析,,查询租户视频解析 -PUT,/api/tenant-content/videos,exact_match,VideoManagementController_upsert,新增或更新视频解析,,新增或更新视频解析 -GET,/api/tenant-content/vocabulary-units,exact_match,VocabularyManagementController_units,查询管理侧词汇单元,,查询管理侧词汇单元 -PUT,/api/tenant-content/vocabulary-units,exact_match,VocabularyManagementController_upsertUnit,新增或更新词汇单元,,新增或更新词汇单元 -GET,/api/tenant-content/vocabulary-words,exact_match,VocabularyManagementController_words,查询管理侧词汇,,查询管理侧词汇 -PUT,/api/tenant-content/vocabulary-words,exact_match,VocabularyManagementController_upsertWord,新增或更新词汇,,新增或更新词汇 -GET,/api/tenant/current-public,target_only,,,,获取公开租户配置 -GET,/api/tenant/resolve,exact_match,TenantController_resolve,解析当前租户,,解析当前租户 -GET,/api/tenants/current,target_only,,,, -POST,/api/videos/play,exact_match,VideoController_play,申请视频播放地址,,申请视频播放地址 -POST,/api/videos/progress,exact_match,VideoController_progress,上报视频播放进度,,上报视频播放进度 -GET,/api/videos/search,exact_match,VideoController_search,搜索通用解析视频,,搜索通用解析视频 -GET,/health,legacy_only,HealthController_check,检查 API 与数据库健康状态,, diff --git a/docs/migration/phase-1-repository-baseline.md b/docs/migration/phase-1-repository-baseline.md deleted file mode 100644 index 793529e..0000000 --- a/docs/migration/phase-1-repository-baseline.md +++ /dev/null @@ -1,35 +0,0 @@ -# 第一阶段:仓库转正基线 - -状态:已完成。 - -## 目标 - -- 确认 ASP.NET Core + EF Core + PostgreSQL 仓库为唯一目标后端。 -- 建立旧 NestJS OpenAPI 与当前 .NET OpenAPI 的机械比较基线。 -- 接入自建 Git 上游。 - -## 结果 - -- 旧 NestJS 仅作为行为、接口和迁移参考。 -- 新功能在 .NET 仓库开发。 -- 旧 URL 不要求逐字兼容。 -- API 差距记录在 `docs/migration/contracts/operation-inventory.csv`。 - -基线快照: - -| 项 | 数量 | -| --- | ---: | -| 旧 NestJS OpenAPI 操作 | 342 | -| 当前 .NET OpenAPI 操作 | 237 | -| 仅旧 NestJS 存在 | 177 | -| 仅当前 .NET 存在 | 72 | - -## 验收 - -```bash -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-restore -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-restore -dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -git diff --check -``` diff --git a/docs/migration/phase-2-engineering-foundation.md b/docs/migration/phase-2-engineering-foundation.md deleted file mode 100644 index 1d334e2..0000000 --- a/docs/migration/phase-2-engineering-foundation.md +++ /dev/null @@ -1,27 +0,0 @@ -# 第二阶段:.NET 工程底座 - -状态:已完成。 - -## 目标 - -- 建立 ASP.NET Core / EF Core / PostgreSQL 工程底座。 -- 固定数据库迁移边界:API 不自动改库,迁移由 `Tiku.DbMigrator` 执行。 -- 使用真实 PostgreSQL 验证 schema、事务、JSONB、约束和扩展。 - -## 结果 - -- Development 未配置连接串时默认连接本机 `tiku` 数据库并使用当前系统用户。 -- EF Core 使用 Npgsql 与 PostgreSQL 扩展。 -- Secret payload 进入加密字段;不提交本地连接串和密钥。 -- 真实 PostgreSQL 集成测试成为数据库能力验收入口。 - -## 验收 - -```bash -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-restore -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-restore -dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -dotnet run --project Tiku.DbMigrator -git diff --check -``` diff --git a/docs/migration/phase-3-tenant-isolation-and-shared-question-bank.md b/docs/migration/phase-3-tenant-isolation-and-shared-question-bank.md deleted file mode 100644 index e48a487..0000000 --- a/docs/migration/phase-3-tenant-isolation-and-shared-question-bank.md +++ /dev/null @@ -1,52 +0,0 @@ -# 第三阶段:租户隔离、共享题库与前端运行时 - -状态:已完成主线设计和实现。 - -## 目标 - -- 普通业务代码默认只能读取和写入当前租户数据。 -- 公共题库由平台主体拥有,有效租户可访问。 -- 租户私题只属于本租户。 -- 公共题和私题可混合组卷、答题、收藏、错题和统计。 -- 租户自定义域名安全解析到统一前端运行时配置。 - -## 结果 - -- `ITenantContext` 只读化,请求租户由中间件解析。 -- EF Core Query Filter 自动按租户过滤。 -- SaveChanges 拦截器自动写入当前租户并拒绝跨租户写入。 -- DbContext 使用 scoped `AddDbContext`,避免池化串租户状态。 -- `TenantResolutionMiddleware` 在认证前按可信 Host 解析租户。 -- JWT tenant claim 与 Host 解析不一致时返回 403。 -- 未知、Pending、禁用域名在业务前返回 404。 -- 唯一 `PlatformOwned` 租户拥有公共题库、公共题、公共题版本和公共分类主干。 -- `TenantQuestionReference` 作为公共题/私题消费引用。 -- `PracticeSessionQuestion` 锁定题目版本,历史答题按原版本回放。 -- `TenantFrontendConfig` 支持 Draft / Preview / Publish。 -- `GET /api/runtime/bootstrap` 仅根据当前 Host 返回公开配置。 - -## 数据库边界 - -EF Core 负责实体、索引、外键和普通约束。以下跨表租户不变量由集中 PostgreSQL guard 管理: - -- `TenantQuestionReference` 只能引用平台公共题或当前租户私题。 -- `TaxonomyNode` 父节点只能属于平台主体或当前租户。 - -维护规则见根目录 README 的“数据库与 ORM 分工”。 - -## 验收 - -- A 租户不能读取、修改、删除或 Attach B 租户数据。 -- A 可访问公共题和 A 私题,不能构造 B 私题引用。 -- 公共题 V1 创建会话后发布 V2,旧会话仍使用 V1,新会话使用 V2。 -- Host A 携带 Tenant B JWT 返回 403。 -- Pending/未知域名和 Suspended 租户无法获取 runtime bootstrap。 -- 新增租户实体漏写 filter、组合外键或租户索引时模型测试失败。 - -```bash -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-build -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-build -dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -git diff --check -``` diff --git a/docs/migration/phase-4-external-provider-decoupling.md b/docs/migration/phase-4-external-provider-decoupling.md deleted file mode 100644 index fe96316..0000000 --- a/docs/migration/phase-4-external-provider-decoupling.md +++ /dev/null @@ -1,42 +0,0 @@ -# 第四阶段:外部服务解耦 - -状态:已完成。 - -## 目标 - -- 完全移除 Supabase Auth / Storage 兼容层。 -- 业务层只依赖身份、短信、对象存储、支付和通知抽象。 -- 第三方 SDK、账号、bucket、密钥和 claim 结构只出现在 Infrastructure provider 边界。 - -## 结果 - -- Provider 配置统一为 `TenantExternalProvider` + `TenantSecret`。 -- `Capability` 覆盖 Identity、ObjectStorage、Sms、Payment、Notification。 -- 同一租户内 `Capability + Provider` 唯一。 -- `ConfigPublic` 只保存公开配置;敏感字段必须进入 `TenantSecret`。 -- 删除旧 `TenantAuthProvider`、`TenantPaymentAccount` 和 Supabase storage provider 路径。 -- 阿里云 OSS、阿里云短信、微信、支付宝 SDK 只允许在 Infrastructure 使用。 - -## 接口边界 - -- `IIdentityProvider`:封装 password、sms、wechat_web、wechat_miniapp 身份解析,不签发 JWT。 -- `ISmsProvider`:只负责发送,验证码生成、哈希、频控和校验归业务服务。 -- `IObjectStorageService`:bucket/provider 从租户配置解析,业务输入不得任意覆盖。 -- `IPaymentProvider`:支付账户和密钥从统一 Provider 配置加载。 -- `INotificationProvider`:默认站内通知持久化,后续外发通道按 provider 扩展。 - -## 验收 - -- 租户 A/B Provider 配置和密钥互不读取。 -- `ConfigPublic` 拒绝 `secret`、`token`、`key`、`privateKey` 等敏感字段。 -- 资产上传不能伪造 bucket/provider。 -- 业务层不引用第三方 SDK namespace。 -- 生产代码不回流 Supabase provider 或旧专用配置表。 - -```bash -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-build -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-build -dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -git diff --check -``` diff --git a/docs/migration/phase-5-backoffice-worker-operations.md b/docs/migration/phase-5-backoffice-worker-operations.md deleted file mode 100644 index 8e2fd5b..0000000 --- a/docs/migration/phase-5-backoffice-worker-operations.md +++ /dev/null @@ -1,47 +0,0 @@ -# 第五阶段:后台能力与 Worker 基座 - -状态:已完成第一轮底座实现。 - -## 目标 - -- 参考 yudao 后台能力,重建权限、菜单、审计、交易运营和 Worker 基座。 -- 微信生态统一使用 `Senparc.Weixin.*`。 -- 业务层继续只依赖 Application 接口,不直接引用第三方 SDK。 - -## 结果 - -- 微信支付切换到 `Senparc.Weixin.TenPayV3`。 -- 生产代码禁止 `SKIT.FlurlHttpClient.Wechat.*`。 -- 新增平台/租户后台权限、菜单、角色和用户角色绑定模型。 -- 菜单只控制 UI 展示,不作为 API 鉴权依据。 -- 角色、权限、菜单和用户角色绑定写操作落 `AuditLog`。 -- 租户交易运营补齐退款、对账批次、对账 issue 和事件记录。 -- 新增 `BackgroundJob` 统一任务模型。 -- Worker 基于 `Microsoft.Extensions.Hosting` + `BackgroundService`。 -- 任务处理器骨架覆盖 `content_export`、`content_import`、`asset_security_scan`、`statistics_aggregation`、`commerce_reconciliation`、`tenant_domain_recheck`。 -- PostgreSQL tenant guard 统一放入集中 SQL helper,由 migration 调用。 - -## 固定 SDK - -- OSS:`AlibabaCloud.OSS.V2` -- 阿里云短信:`AlibabaCloud.SDK.Dysmsapi20170525` -- 支付宝:`AlipaySDKNet.Standard` -- 微信公众号:`Senparc.Weixin.MP` -- 微信小程序:`Senparc.Weixin.WxOpen` -- 微信支付 V3:`Senparc.Weixin.TenPayV3` - -## 验收 - -- platform token / tenant token 后台权限不能串用。 -- 高风险写操作都有审计。 -- 租户 A 不能查询或处理租户 B 交易数据。 -- Worker 必须通过 `ITenantExecutionScope` 初始化租户或 System Scope。 -- 架构扫描禁止 Supabase、SKIT 微信支付、业务层第三方 SDK、`IgnoreQueryFilters`、`FromSql`、`ExecuteSql`、直接 `NpgsqlCommand`。 - -```bash -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-build -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-build -dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -git diff --check -``` diff --git a/docs/migration/phase-7-student-experience-and-content-consumption.md b/docs/migration/phase-7-student-experience-and-content-consumption.md deleted file mode 100644 index fbc7608..0000000 --- a/docs/migration/phase-7-student-experience-and-content-consumption.md +++ /dev/null @@ -1,54 +0,0 @@ -# 第七阶段:学生端体验与内容消费闭环 - -状态:已完成。 - -## 目标 - -- 补齐学生端视频消费、Profile 签到、积分流水和内容导入异步化。 -- 不实现 AI 业务功能;AI 进入第八阶段。 - -## 结果 - -学生端视频接口: - -- `GET /api/videos/search` -- `POST /api/videos/play` -- `POST /api/videos/progress` -- `GET /api/questions/videos` -- `POST /api/questions/videos/batch` - -Profile 与积分接口: - -- `POST /api/profile/check-in` -- `GET /api/profile/score-events` - -内容导入入口: - -- `POST /api/tenant-content/imports/preview/{importType}` -- `POST /api/tenant-content/imports/{importType}` -- `GET /api/tenant-content/imports/detail` - -## 边界 - -- 播放接口不暴露 OSS bucket、真实 object key 或 provider 细节。 -- 公共题和租户私题关联视频都必须按当前租户可见性校验。 -- 播放进度按租户、用户、视频、题目维度幂等更新。 -- 签到复用积分任务和积分流水;同一用户、同一租户、同一天只能成功一次。 -- `questions`、`vocabulary`、`handbook`、`scoreline`、`videos` 导入语义统一映射为 `importType`。 -- 大批量导入创建 `content_import` 后台任务,Worker 使用 `ITenantExecutionScope` 执行。 -- 本阶段未实现 `/api/ai/**`。SK 包和 AI provider 边界在第八阶段引入。 - -## 旧接口替代 - -- `/api/profile/activity-tasks` -> `/api/points/tasks` -- `/api/profile/exchange-items` -> `/api/points/exchange-items` -- `/api/profile/exchange-items/redeem` -> `/api/points/exchange-orders` - -## 验收 - -- 租户 A 不能播放租户 B 视频。 -- 公共题和租户私题解析视频都按权限返回。 -- 播放进度重复上报不产生重复记录。 -- 每日签到同一天只能成功一次。 -- 签到、积分任务和兑换产生可查询积分流水。 -- 异步导入创建 `content_import` job,Worker 成功写入结果。 diff --git a/docs/migration/phase-8-ai-foundation.md b/docs/migration/phase-8-ai-foundation.md deleted file mode 100644 index f9f4aa4..0000000 --- a/docs/migration/phase-8-ai-foundation.md +++ /dev/null @@ -1,61 +0,0 @@ -# 第八阶段:AI 底座与教师端对话 - -状态:基础包和边界已引入,业务接口待实现。 - -## 使用场景 - -### 租户教师 AI 对话 - -- 教师在租户后台发起对话。 -- AI 根据当前租户 Provider 配置调用模型。 -- 对话历史按租户和教师隔离保存。 -- 后续预留 function calling,但只能调用受审计的后端业务函数。 -- function 有写操作时必须复用 RBAC、DataScope、Tenant Scope 和 AuditLog。 - -### AI 审核题目反馈 - -- 输入:题目反馈、题目摘要、反馈类型、提交用户上下文摘要。 -- 输出:审核建议、风险等级、归类标签、是否建议人工复核。 -- 不做 function calling。 -- 不直接修改题目、反馈状态或用户数据。 - -## 存储模型 - -不要把 Semantic Kernel 的 `ChatHistory`、`ChatMessageContent`、`KernelContent`、tool call object graph 或 provider 原始 response 作为 EF Core 持久化模型。 - -数据库保存 provider-neutral 消息: - -- `AiConversation`:租户、教师、标题、场景、Provider、模型、状态、metadata。 -- `AiConversationMessage`:租户、会话、序号、role、文本、tool call id/name/arguments/result summary、token、metadata。 -- `AiToolCallLog`:租户、会话、消息、函数名、输入摘要、结果、耗时、错误码。 -- `AiFeedbackReview`:租户、题目反馈、建议、风险、标签、人工复核标记、metadata。 - -运行时由 Infrastructure adapter 把规范化消息转换为 SK / OpenAI-compatible 请求。DeepSeek 等 provider 只接收目标接口允许的 `role`、`content`、`tool_calls`、`tool_call_id` 字段;SK metadata 不得原样回放给模型 API。 - -## Provider 与密钥边界 - -- AI Provider 使用 `TenantExternalProvider(capability=ai)`。 -- 租户 API Key 存 `TenantSecret`,通过 `SecretRef` 关联。 -- `ConfigPublic` 只允许 provider、model、endpoint、deployment、temperature、max token 等公开配置。 -- `ConfigPublic` 禁止 `secret`、`token`、`apiKey`、`key`、`privateKey` 等敏感字段。 -- `Microsoft.SemanticKernel` 只引用在 `Tiku.Infrastructure`。 -- `Tiku.Api`、`Tiku.Application`、`Tiku.Domain` 不直接引用 Semantic Kernel namespace。 - -## 第一批接口 - -- `POST /api/tenant-admin/ai/conversations` -- `GET /api/tenant-admin/ai/conversations` -- `GET /api/tenant-admin/ai/conversations/{conversationId}` -- `POST /api/tenant-admin/ai/conversations/{conversationId}/messages` -- `POST /api/tenant-admin/ai/question-feedback/review` - -第一批先接 fake/local AI provider 跑通对话、日志和隔离,再接真实模型。 - -## 暂不做 - -- 学生端 AI。 -- 复杂 RAG。 -- 自动改题或自动发布题目。 -- 自动处理反馈状态。 -- 让客户端指定任意 function call。 -- 明文 API Key 配置。 diff --git a/docs/migration/phase-9-saas-marketplace-and-onboarding.md b/docs/migration/phase-9-saas-marketplace-and-onboarding.md deleted file mode 100644 index c2fc56f..0000000 --- a/docs/migration/phase-9-saas-marketplace-and-onboarding.md +++ /dev/null @@ -1,50 +0,0 @@ -# 第九阶段:SaaS 模块商城与租户交付闭环 - -## 目标 - -- 套餐决定租户购买的业务 Feature。 -- 角色决定员工可执行的 Permission,菜单只用于导航展示。 -- 平台 SaaS 收费与租户学生商城完全分离。 -- 平台创建租户和 Owner 后,租户可自助购买、开通并完成学生端初始化。 - -## 已完成 - -- `SaasFeature`、`PermissionModule`、`SaasOffering` 与不可变 `SaasOfferingVersion`。 -- 基础套餐、附加包、模块清单、额度定义、租户覆盖和原子用量记录。 -- `IFeatureAccessService` 统一读写状态、Feature、权限过滤和额度判断。 -- 题库、词汇、手册、视频、分数线和站点内容使用独立 `PermissionModule` 与后台权限点,单独购买、授权和生成菜单。 -- `/api/platform-admin/saas/**` 商品、订单、支付、订阅、发票、催缴和人工收款管理。 -- `/api/tenant-billing/**` 目录、幂等报价、幂等下单、支付、续费、变更、取消、用量和发票。 -- `/api/platform-billing/callbacks/{provider}` 独立平台收款回调。 -- 平台创建租户与 Owner 的事务化开户,以及 `/api/tenant-onboarding/status`。 -- runtime/UI bootstrap 返回有效 Feature、登录方式、权限、菜单、订阅和额度摘要。 -- 员工、学生、私有题、存储、导入、导出和短信额度已接入真实写路径;当前量由 Worker 定期按业务事实校准。 -- Worker 自动应用周期末降级、取消、Trial 到期、PastDue 和 Expired 状态转换,多 Worker 通过乐观并发保证幂等。 -- 旧 `ProductModule`、可变套餐、旧订阅和重复平台发票模型已删除。 - -## 数据库边界 - -- 当前仓库只有一个 greenfield `InitialSchema`。 -- 发布套餐版本、Feature 清单和额度清单不可修改。 -- 订阅基础项必须引用基础套餐;订阅项类型必须匹配 Offering 类型。 -- 订阅项可绑定产生它的 `PlatformBillingOrderItem` 价格快照。 -- 平台支付事件按租户、Provider 和 Provider Event ID 幂等。 -- 每个订阅最多一个 Active 基础套餐项和一个 Scheduled 基础套餐项,周期末原子切换。 -- 报价、订单和支付均保存租户内唯一幂等键,客户端不能提交最终价格。 - -## 验收 - -```bash -dotnet build TIKU-BACKEND.slnx --no-restore -dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-build -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-build -dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -dotnet ef migrations has-pending-model-changes --project Tiku.Infrastructure --startup-project Tiku.DbMigrator -git diff --check -``` - -真实 PostgreSQL 测试覆盖套餐发布不可变、报价与订单幂等、人工/微信/支付宝收款、签名与金额拒绝、重复回调、跨租户订单隔离、未购模块权限拒绝、额度写路径与事实校准、订阅周期转换和 onboarding ready。 - -## 后续 - -第十阶段实现教师作业、考试、批阅和教学报告。教师 AI 对话与题目反馈审核继续独立排期。 diff --git a/docs/operations.md b/docs/operations.md new file mode 100644 index 0000000..10e3c68 --- /dev/null +++ b/docs/operations.md @@ -0,0 +1,160 @@ +# 配置与后台任务 + +本文列出 API、DbMigrator 和 Worker 当前实际读取的配置。敏感值应通过环境变量、Secret Manager 或部署平台密钥注入,不能提交到仓库。 + +## 进程与依赖 + +| 进程 | PostgreSQL | Redis | RabbitMQ | 说明 | +| --- | --- | --- | --- | --- | +| `Tiku.Api` | 必需 | Development 可选;Production 必需 | Development 可选;Production 必需 | 提供 HTTP API、静态管理端、认证和 Outbox 发布 | +| `Tiku.Worker` | 必需 | Development 可选;Production 必需 | Development 可选;Production 必需 | 消费消息并轮询后台任务 | +| `Tiku.DbMigrator` | 必需 | 不需要 | 不需要 | 执行 Migration、内置目录 seed 和管理员引导 | + +Development 未配置 Redis 时,安全服务使用进程内/数据库防线;未配置 RabbitMQ 时,Worker 从 PostgreSQL 处理即时和延时任务。Production 不允许这两个降级模式。 + +## 数据库 + +解析顺序: + +1. `ConnectionStrings:Database` +2. `DATABASE_URL` +3. 仅 Development:`Host=localhost;Database=tiku;Username=<当前系统用户>` + +DbMigrator 命令: + +```bash +dotnet run --project Tiku.DbMigrator + +dotnet ef migrations script \ + --project Tiku.Infrastructure \ + --startup-project Tiku.DbMigrator + +dotnet ef migrations has-pending-model-changes \ + --project Tiku.Infrastructure \ + --startup-project Tiku.DbMigrator +``` + +Production 首次创建平台管理员必须显式执行: + +```bash +export TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL='admin@example.com' +export TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD='use-a-strong-temporary-password' +export TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME='Platform Administrator' +dotnet run --project Tiku.DbMigrator -- --bootstrap-platform-admin +``` + +该命令只允许在不存在平台角色用户绑定时执行。管理员首次登录后必须改密。 + +## Redis + +连接串读取 `ConnectionStrings:Redis` 或 `REDIS_URL`。当前用途: + +- 密码、短信发送和短信校验的跨实例安全窗口计数; +- 安全状态和租户 Feature 缓存失效; +- Production 的 ASP.NET Core Output Cache。 + +Redis key 使用环境前缀;配置解析会强制 `AbortOnConnectFail=false`。Redis 不是用户、Session、权限、套餐或用量的权威数据源。 + +## RabbitMQ 与 Outbox + +配置节: + +```json +{ + "RabbitMq": { + "Host": "rabbitmq://localhost", + "VirtualHost": "/", + "Username": "guest", + "Password": "guest", + "OutboxBacklogAlertCount": 1000, + "OutboxOldestMessageAlertSeconds": 300 + } +} +``` + +本地可用环境变量形式覆盖,例如 `RabbitMq__Host`。Production 必须同时提供有效 Host、Username 和 Password。 + +当前消息配置: + +- kebab-case endpoint 名称; +- PostgreSQL EF Bus Outbox,1 秒查询间隔; +- Consumer 端 EF inbox/outbox; +- Consumer 单并发、prefetch 1; +- 1、5、15 秒有限即时重试; +- 不使用 RabbitMQ delayed-message 插件,延时任务保留在 PostgreSQL。 + +API 只发布消息,不注册 Consumer;Worker 注册 `SecurityStateChangedConsumer` 和 `BackgroundJobRequestedConsumer`。 + +## Worker 配置 + +```json +{ + "TenantDomains": { + "Enabled": true, + "PollSeconds": 60, + "BatchSize": 50, + "DnsJsonEndpoint": "https://cloudflare-dns.com/dns-query", + "VerificationRecordPrefix": "_tiku-verification", + "AllowedCnameTargets": [], + "GatewayBaseUrl": null, + "GatewayApiKey": null + }, + "SaasSubscriptions": { + "Enabled": true, + "BatchSize": 100, + "PastDueGraceDays": 7 + }, + "FeatureUsageReconciliation": { + "Enabled": true, + "BatchSize": 100, + "IntervalMinutes": 60 + } +} +``` + +域名只有在 `AllowedCnameTargets`、DNS JSON endpoint、Gateway URL 和 API key 配置完成后,才可能从 Pending/Failed 进入 Active。仅 DNS 验证成功不代表 TLS 已就绪。 + +后台任务状态和 `RunAfter` 存在 PostgreSQL。Worker 使用租约并发处理;同一即时任务在启用 RabbitMQ 后不会同时进入消息 Consumer 和数据库即时轮询路径。 + +## 安全与网络配置 + +Production 启动至少需要核对: + +| 配置 | 作用 | +| --- | --- | +| `Security:Jwt` | issuer、audience、当前 key ID、RSA 私钥和验证公钥 | +| `Security:DataProtection` | application name、X509 证书路径和密码 | +| `Security:TenantSecrets` | key ID 和 Base64 编码的 32 字节 master key | +| `Authentication:Sms` | 至少 32 字符的验证码 pepper 和频控阈值 | +| `Tenancy:Resolution` | 正式平台 Host、可信代理、tenant code 允许路径 | +| `AllowedHosts` | 非通配 Host allowlist | +| `Cors` | 明确的 Origin、Header、Method 和凭据策略 | +| `BrowserAuth:AllowedOrigins` | 允许使用 Browser Auth 的 HTTP(S) Origin | +| `RateLimiting` | 全局和认证端点限流 | + +可用环境变量覆盖包括: + +- `TIKU_DATA_PROTECTION_APPLICATION_NAME` +- `TIKU_DATA_PROTECTION_CERTIFICATE_PATH` +- `TIKU_DATA_PROTECTION_CERTIFICATE_PASSWORD` +- `TIKU_TENANT_SECRET_KEY_ID` +- `TIKU_TENANT_SECRET_MASTER_KEY` +- `TIKU_SMS_CODE_PEPPER` + +不要在命令输出、文档、Git diff 或错误报告中粘贴这些值。 + +## 对象存储与外部 Provider + +对象存储读取 `Storage` / `Storage:AliyunOss`,也支持 `STORAGE_*` 与 `ALIYUN_OSS_*` 环境变量。当前默认实现是阿里云 OSS,并强制租户 key 前缀、上传大小和 MIME allowlist。 + +身份、短信、支付、通知和 AI 的租户配置由业务后台写入 `TenantExternalProvider`;敏感值写入加密的 `TenantSecret`。全局默认配置不能绕过租户 Provider 状态和 Secret 边界。 + +## 健康检查与观测 + +- `GET /api/health`:轻量 liveness,只说明 API 进程可响应。 +- `GET /api/health/ready`:检查 PostgreSQL、已配置 Redis、已配置 RabbitMQ,并报告 Outbox pending、最老消息年龄和告警阈值;依赖未就绪时返回 503。 +- API 每 30 秒采样一次 Outbox backlog,并暴露 `tiku.outbox.pending` 与 `tiku.outbox.oldest_age` meter。 +- 设置 `OpenTelemetry:OtlpEndpoint` 后导出 ASP.NET Core、HTTP client 和数据库观测数据。 +- Serilog 输出结构化请求日志;数据库性能拦截器记录慢查询指标。 + +Readiness 为绿色不等于认证授权、跨租户隔离或 Broker 恢复演练已通过,发布仍需执行对应集成测试。 diff --git a/docs/quickstart.md b/docs/quickstart.md index 83b4977..34e2e60 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,40 +1,19 @@ -# 本地开发快速开始 +# 本地开发与运行 -## 可选分布式依赖 - -本地单实例开发可以不配置 Redis/RabbitMQ,认证频控仍保留 PostgreSQL/进程内防线;Production 两者均为启动必填项。 - -```bash -export ConnectionStrings__Redis='localhost:6379,abortConnect=false' -export RabbitMq__Host='rabbitmq://localhost' -export RabbitMq__Username='guest' -export RabbitMq__Password='guest' -``` - -RabbitMQ 使用 MassTransit 8.5.10 和 PostgreSQL EF Bus/Consumer Outbox。`GET /api/health` 是 liveness,`GET /api/health/ready` 检查 PostgreSQL、已配置的 Redis、RabbitMQ bus health,并返回 outbox pending、最老消息时长和阈值告警;服务健康不等于认证授权验收完成。 -官方 RabbitMQ 4.x 镜像无需安装 delayed-message 插件;不要配置 `UseDelayedRedelivery`,延时后台任务由 PostgreSQL `RunAfter` 调度。 - -本地 Broker 重启/outbox 恢复演练(仅对明确指定的测试容器执行 stop/start): - -```bash -TIKU_TEST_RABBITMQ=rabbitmq://localhost \ -TIKU_TEST_RABBITMQ_RESTART=1 \ -TIKU_TEST_RABBITMQ_CONTAINER=tiku-rabbitmq \ -dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj \ - --filter 'FullyQualifiedName~Bus_outbox_drains_after_real_broker_restart' -``` - -这份文档用于从全新开发环境启动 TIKU Backend、初始化 PostgreSQL,并完成平台管理员的首次登录。 +本页用于从全新开发环境启动当前 TIKU Backend。数据库迁移由 DbMigrator 执行,API 不会自动创建或更新 schema。 ## 1. 准备环境 -需要安装: +必需: - .NET 10 SDK; -- PostgreSQL(当前本地开发已验证 PostgreSQL 18); +- PostgreSQL; - `psql`、`createdb` 等 PostgreSQL 命令行工具。 -确认工具可用: +可选: + +- Redis 7; +- RabbitMQ 4。 ```bash dotnet --version @@ -42,7 +21,7 @@ pg_isready -h 127.0.0.1 -p 5432 psql --version ``` -## 2. 获取并还原项目 +## 2. 还原并构建 ```bash git clone TIKU-BACKEND @@ -51,78 +30,95 @@ dotnet restore TIKU-BACKEND.slnx dotnet build TIKU-BACKEND.slnx --no-restore ``` -## 3. 创建本地数据库 +## 3. 创建 PostgreSQL 数据库 -如果本机 PostgreSQL 允许当前系统用户无密码登录,可以直接执行: +当前系统用户能本地登录 PostgreSQL 时: ```bash createdb -h 127.0.0.1 -U "$(whoami)" tiku ``` -Development 环境未显式配置连接串时,API 和 DbMigrator 默认使用: +Development 未显式配置连接串时,API、DbMigrator 和设计时 EF 工具默认使用: ```text Host=localhost;Database=tiku;Username=<当前系统用户> ``` -如果数据库用户名、端口或认证方式不同,通过环境变量传入连接串: +其他用户、端口或认证方式使用环境变量: ```bash export DATABASE_URL='Host=127.0.0.1;Port=5432;Database=tiku;Username=<数据库用户>;Password=<本地密码>' ``` -不要把包含密码的连接串写进 README、`appsettings*.json` 或提交到 Git。团队成员应各自使用环境变量、.NET Secret Manager 或受控密钥存储。 +不要把含密码的连接串写入 `appsettings*.json`、README 或 Git。 -## 4. 执行迁移并初始化管理员 +## 4. 执行迁移和 seed ```bash ASPNETCORE_ENVIRONMENT=Development dotnet run --project Tiku.DbMigrator ``` -DbMigrator 会执行全部 EF Core Migration,并在全新 Development 数据库中自动创建平台超级管理员: +DbMigrator 会: + +1. 执行所有 EF Core Migration; +2. seed 内置 Feature、Permission、菜单和额度目录; +3. 在全新 Development 数据库创建平台超级管理员。 ```text 账号:admin@tiku.local -密码:首次初始化时安全随机生成,只在当前终端输出一次 +密码:首次创建时随机生成,只在当前终端输出一次 ``` -请立即保存终端显示的临时密码。重复执行 DbMigrator 是幂等的,不会重复创建管理员、重置密码或再次显示密码。 +重复运行是幂等的,不会重置密码或再次显示临时密码。首次登录必须改密;不要为了找回密码删除已有业务数据的数据库。 -管理员首次登录后必须修改临时密码。正式密码至少 8 位,并同时包含字母和数字;平台管理员当前使用账号和密码登录,不要求绑定认证器。 +Migration 需要 `citext`、`ltree` 和 `pg_trgm` 扩展。执行迁移的 PostgreSQL 用户必须有创建扩展的权限,或由管理员预先安装。 -普通租户用户以手机号作为账号,可以使用手机号和密码登录,也可以使用手机号和短信验证码登录。 - -如果数据库已经包含平台管理员,自动初始化会跳过。不要为了重新获取密码删除包含业务数据的数据库。 - -## 5. 启动 API 和平台后台 +## 5. 启动 API ```bash dotnet run --project Tiku.Api ``` -默认开发地址: +默认 Development 入口: -- 平台后台: -- Scalar API 文档: +- 平台管理端: +- Scalar: - OpenAPI JSON: -- 健康检查: +- Liveness: +- Readiness: -平台后台默认连接同源真实 API,不会回退到 Mock 数据。当前开放的是已有后端契约的概览、租户、员工、审计和告警等页面;尚未接入真实接口的模块暂不开放。 +OpenAPI 和 Scalar 仅在 Development 映射。接口路径、输入字段、响应模型和授权要求以这里生成的文档为准。 -## 6. 可选:启动 Worker +## 6. 可选:启动 Redis 和 RabbitMQ -需要调试后台任务时,另开终端并使用相同数据库连接: +本地单实例开发可以不配置这两个依赖。需要验证分布式安全频控、Output Cache、消息和 Outbox 时,先启动本地服务,再设置: + +```bash +export ConnectionStrings__Redis='localhost:6379,abortConnect=false' +export RabbitMq__Host='rabbitmq://localhost' +export RabbitMq__VirtualHost='/' +export RabbitMq__Username='guest' +export RabbitMq__Password='guest' +``` + +RabbitMQ 使用 4.x,当前代码不依赖 delayed-message 插件。延时任务由 PostgreSQL `RunAfter` 调度。 + +## 7. 可选:启动 Worker + +需要处理域名、订阅、用量或后台任务时,在另一个终端使用相同配置启动: ```bash dotnet run --project Tiku.Worker ``` -普通 API 开发不要求同时启动 Worker。 +Worker 会立即开始轮询。域名 DNS/TLS 流程只有在 `TenantDomains` 的 CNAME target 和 Gateway 配置完整后才能激活自定义域名。 -## 7. 开发前验证 +## 8. 开发验证 ```bash curl --fail http://localhost:5090/api/health +curl --fail http://localhost:5090/api/health/ready + dotnet test TIKU-BACKEND.slnx --no-build dotnet format TIKU-BACKEND.slnx --verify-no-changes --no-restore dotnet ef migrations has-pending-model-changes \ @@ -132,29 +128,33 @@ dotnet ef migrations has-pending-model-changes \ git diff --check ``` -PostgreSQL 特有的 Migration、约束、事务和租户隔离行为必须使用真实 PostgreSQL 验证,不能只依赖 EF InMemory 测试。 +`Tiku.IntegrationTests` 会创建临时 PostgreSQL 数据库,验证 API、授权、迁移和租户隔离。测试账户和测试数据库只用于自动化验证。 ## 常见问题 ### 连接 PostgreSQL 失败 -先检查服务和实际登录信息: - ```bash pg_isready -h 127.0.0.1 -p 5432 psql -h 127.0.0.1 -U <数据库用户> -d postgres -c 'select current_user;' ``` -然后确认当前终端中的 `DATABASE_URL` 指向正确的主机、端口、数据库和用户。 +确认当前终端的 `DATABASE_URL` 指向真实存在的数据库,并且 API、DbMigrator 和 Worker 使用同一连接配置。 -### 首次迁移无法创建扩展 +### 无法创建 PostgreSQL 扩展 -Migration 会创建 `citext` 和 `ltree` 扩展。初始化数据库的 PostgreSQL 用户必须有安装这些扩展所需的权限;请让本地数据库管理员预先安装扩展或授予对应权限。 +请让数据库管理员安装 `citext`、`ltree`、`pg_trgm`,或授予迁移用户创建这些扩展所需的权限。 ### 没看到管理员临时密码 -临时密码只在全新 Development 数据库首次创建管理员时显示。如果管理员绑定已经存在,迁移会安全跳过。请使用已有管理员账号的密码恢复流程,不要在源码或文档中添加固定密码。 +临时密码只在全新 Development 数据库第一次创建管理员时显示。已有管理员时 DbMigrator 会跳过;应使用正常密码恢复流程。 -### API 启动后出现 HTTPS 重定向警告 +### Readiness 返回 503 -本地仅使用 HTTP profile 时可能看到无法确定 HTTPS 端口的警告,不影响 `http://localhost:5090` 的开发访问。需要验证 HTTPS 时使用项目的 `https` launch profile。 +检查响应中的 `database`、`redis.ready` 和 `rabbitMq.ready`。只配置了 Redis/RabbitMQ 连接串但服务未启动时,readiness 会按已配置依赖检查并返回 503。 + +### API 出现 HTTPS 重定向警告 + +仅使用 HTTP launch profile 时可能无法确定 HTTPS 端口,不影响 `http://localhost:5090` 的本地访问。需要验证 HTTPS 时使用项目的 `https` profile。 + +更多配置见[配置与后台任务](operations.md),安全边界见[认证、授权与租户隔离](architecture/security-and-tenancy.md)。