# 电子名片系统技术文档 ## 1. 文档目标 本文档用于定义本项目的技术架构、技术栈、中间件、数据隔离策略、部署方案与工程结构,目标是在满足 `JDK 21 + Spring Boot + Vue 3 + TypeScript` 约束下,实现一套轻量、稳定、易扩展的多租户电子名片系统。 ## 2. 技术设计原则 - 正确性优先:先保证多租户隔离、权限控制、数据一致性和关键链路稳定。 - 轻量优先:优先采用单体模块化架构,避免过早拆分微服务。 - 稳定优先:使用成熟、社区活跃、可持续维护的主流组件。 - 扩展优先:模块边界清晰,为后续审核流、线索、预约、营销等功能预留能力。 ## 3. 总体架构 系统由 3 个前后端子系统组成: 1. 后端服务:统一提供租户、账号、名片、文件、统计等业务接口。 2. 后台管理端:供超级管理员、租户管理员、普通用户登录管理数据。 3. 微信小程序:面向公众展示事务所与个人电子名片。 说明: - 后台系统为多租户统一平台。 - 微信小程序按“1 个租户 = 1 个小程序 AppID”设计,每个事务所拥有独立小程序。 - 多个租户的小程序可复用同一套后端服务与同一套代码模板,通过配置完成租户绑定。 ```mermaid flowchart LR A["超级管理员 / 租户管理员 / 普通用户"] --> B["后台管理端 Vue3 + TS"] C["微信访客"] --> D["微信小程序"] B --> E["Spring Boot API"] D --> E E --> F["MySQL 8"] E --> G["Redis"] E --> H["MinIO"] E --> I["日志/监控"] ``` ## 4. 推荐工程结构 建议在当前仓库基础上扩展为如下结构: ```text easycard/ ├─ docs/ # 产品/技术/接口/部署文档 ├─ backend/ # Spring Boot 后端 │ ├─ easycard-boot/ # 启动模块 │ ├─ easycard-common/ # 通用基础能力 │ ├─ easycard-module-system/ # 系统管理、权限、日志 │ ├─ easycard-module-tenant/ # 多租户管理 │ ├─ easycard-module-org/ # 组织/事务所信息 │ ├─ easycard-module-user/ # 用户与账号 │ ├─ easycard-module-card/ # 名片管理 │ ├─ easycard-module-file/ # 文件上传与素材管理 │ └─ easycard-module-stat/ # 浏览与分享统计 ├─ admin-web/ # Vue3 后台管理端 └─ frontend_miniprogram/ # 微信小程序 ``` 说明: - 当前仓库已存在 `frontend_miniprogram/`,后续应保持为独立子项目。 - 后端采用“单体应用 + 模块化分层”方案,控制复杂度,同时便于后续拆分。 ## 5. 技术栈选型 ## 5.1 后端技术栈 | 分类 | 选型 | 说明 | | --- | --- | --- | | JDK | JDK 21 | 满足长期支持版本要求 | | 框架 | Spring Boot 3.3.x | 稳定、生态成熟 | | Web | Spring MVC | 提供 REST API | | 参数校验 | Spring Validation | 请求参数校验 | | ORM | MyBatis-Plus | 开发效率高,适合后台管理型系统 | | 数据库 | MySQL 8.0 | 成熟稳定,部署简单 | | 缓存 | Redis 7 | 缓存、会话、验证码、热点数据 | | 鉴权 | Spring Security 6 + JWT | 标准化、安全性强、扩展性好 | | 对象存储 | MinIO | 轻量、私有化友好,可替换云 OSS | | 数据迁移 | Flyway | 管理数据库版本与初始化脚本 | | API 文档 | springdoc-openapi + Knife4j | 自动生成接口文档,方便联调 | | 日志 | Logback | 默认稳定方案 | | 对象映射 | MapStruct | 降低 DTO/VO 转换样板代码 | | 构建 | Maven | Java 生态成熟、稳定 | | 测试 | JUnit 5 + Spring Boot Test + Testcontainers | 保证接口与数据库集成质量 | ### 5.1.1 为什么不优先使用微服务 当前场景的核心复杂度在于: - 多租户隔离 - 角色权限控制 - 小程序与后台双端联动 - 名片内容与文件管理 这些问题本质上更适合“模块化单体”优先解决。若过早拆分为微服务,会明显增加: - 部署复杂度 - 联调成本 - 配置和监控成本 - 分布式事务与调用链复杂度 因此第一阶段推荐单体架构,待租户数量、访问量和团队规模增长后,再按模块拆分。 ## 5.2 后台管理端技术栈 | 分类 | 选型 | 说明 | | --- | --- | --- | | 框架 | Vue 3 | 主流稳定 | | 语言 | TypeScript | 类型安全 | | 构建工具 | Vite | 启动与构建速度快 | | 路由 | Vue Router | 标准路由方案 | | 状态管理 | Pinia | 轻量清晰 | | UI 组件库 | Element Plus | 适合中后台、生态成熟 | | 请求库 | Axios | 简洁稳定 | | 样式 | SCSS + CSS Variables | 可维护、便于主题化 | | 表格/表单增强 | Element Plus 原生能力优先 | 减少额外依赖 | | 图表 | ECharts | 用于统计分析页 | ## 5.3 微信小程序技术栈 | 分类 | 选型 | 说明 | | --- | --- | --- | | 框架 | 原生微信小程序 | 当前仓库已采用 | | 语言 | TypeScript | 已有基础 | | 样式 | Less | 当前仓库已采用 | | 数据请求 | 封装 `wx.request` | 简洁稳定,避免引入额外运行时 | | 本地缓存 | `wx.setStorageSync` / `wx.getStorageSync` | 用于轻量本地状态,如浏览历史 | 说明: - 当前小程序已有静态页面和 Mock 数据,后续改造重点是抽离 API 层、租户配置、环境变量管理和错误处理。 - 本项目按“一个小程序对应一个租户”设计,小程序内不再依赖页面参数动态切换租户。 - 小程序租户识别以 `AppID` 为主键,后台需维护 `AppID -> tenant_id` 的映射配置。 ## 6. 核心架构设计 ## 6.1 多租户模型 第一阶段推荐使用: - 共享数据库 - 共享数据表结构 - 业务表统一增加 `tenant_id` 该方案优点: - 成本低 - 开发快 - 部署简单 - 适合中小规模 SaaS 需要重点保证: - 所有租户业务表都必须带 `tenant_id` - 查询默认按 `tenant_id` 过滤 - 非平台角色不可跨租户访问 - 日志中要记录操作人、租户、操作对象 补充说明: - 多租户主要体现在后台管理端和后端数据层。 - 小程序虽然是“一租户一 AppID”,但后端仍是统一多租户服务,公开接口和管理接口都需要具备租户隔离能力。 ### 6.1.1 表分类建议 平台级表: - `sys_tenant` - `sys_platform_user` - `sys_platform_role` - `sys_menu` - `sys_dict` - `tenant_miniapp_config` 租户级表: - `tenant_user` - `tenant_role` - `tenant_user_role` - `org_department` - `org_firm_profile` - `card_profile` - `file_asset` - `card_view_stat` - `operation_log` ### 6.1.2 未来扩展策略 当单租户数据量或安全要求显著上升时,可平滑扩展为: - 大租户独立数据库 - 普通租户共享数据库 为此后端需抽象租户上下文与数据访问入口,不把 `tenant_id` 获取逻辑写死在业务代码中。 ### 6.1.3 小程序与租户绑定模型 本项目推荐采用: - 后台:多租户统一平台 - 小程序:一个租户对应一个微信小程序 AppID - 后端:统一一套服务,按小程序配置映射到具体租户 该模式的优点: - 每个事务所有独立品牌入口,体验更清晰 - 不需要在小程序页面参数中显式传递 `tenantCode` - 可直接以小程序 `AppID` 作为租户识别主键 - 每个租户可独立发布、独立审核、独立配置微信能力 - 后端仍可复用统一业务代码和统一数据库结构 需要接受的代价: - 每增加一个租户,就需要新增一个小程序 AppID - 小程序发布、审核、备案、类目维护成本会随租户数量线性增加 - 构建与配置管理需要支持按租户生成不同发行包 ## 6.2 权限模型 推荐采用 RBAC 模型: - 用户 - 角色 - 菜单 - 按钮权限点 - 数据范围 角色建议: - `PLATFORM_SUPER_ADMIN` - `TENANT_ADMIN` - `TENANT_USER` 权限控制分 3 层: 1. 接口层:校验是否登录、是否具备角色/权限。 2. 服务层:校验数据归属与业务状态。 3. 数据层:统一追加租户过滤条件。 ## 6.3 登录认证设计 ### 6.3.1 后台管理端 采用 `JWT Access Token + Redis` 组合: - 登录成功后签发短期 Access Token - Redis 记录登录会话、验证码和黑名单 - 退出登录时清理 Redis 中的会话信息 这样做的原因: - 便于水平扩容 - 比传统服务端 Session 更适合前后端分离 - Redis 介入后可控制踢下线、单点登录、风控扩展 ### 6.3.2 微信小程序 小程序端建议区分两类访问: - 公开访问:无需登录,可浏览公开名片 - 成员访问:后续如需在小程序维护本人名片,可通过微信登录绑定用户 第一阶段的最简实现: - 小程序仅做公开展示 - 每个小程序在构建时写入固定 `AppID` 配置 - 小程序启动后直接读取当前租户配置并调用公开接口 若后续要支持小程序内登录: - 前端调用 `wx.login()` - 后端调用微信 `code2Session` - 将 `openid/unionId` 与系统用户绑定 ### 6.3.3 小程序如何确定对应租户 由于本项目采用“一个小程序对应一个租户”的模式,因此租户识别以微信小程序 `AppID` 为主,不再依赖页面参数中的 `tenantCode`。 推荐落地方式如下: #### 1. 租户配置表维护小程序信息 建议新增配置表或配置字段: - `tenant_id` - `tenant_code` - `miniapp_app_id` - `miniapp_app_secret` - `miniapp_name` - `miniapp_original_id` - `request_domain` - `publish_status` 其中: - `miniapp_app_id` 是当前方案中的租户识别主键 - `miniapp_app_secret` 仅后端保存,必须加密存储 - `request_domain` 用于约束该小程序实际访问的接口域名,可作为辅助校验项 后台管理端需要新增“小程序配置”功能,至少支持: - 为租户录入和修改 `AppID` - 配置 `AppSecret` - 校验 `AppID` 全平台唯一 - 启停用小程序配置 - 查询当前租户是否已完成小程序配置 #### 2. 小程序构建时注入固定租户配置 每个租户的小程序发行包包含固定配置,例如: ```ts export const tenantRuntimeConfig = { appId: 'wx1234567890xxxxxx', apiBaseUrl: 'https://api.example.com', } ``` 说明: - `appId` 是前端运行时的固定配置 - 同一个租户的小程序所有页面默认只访问本租户数据 - 不允许在小程序内切换到其他租户 #### 3. 后端接口通过租户上下文处理公开请求 公开接口建议改为以下风格: - `/api/open/profile` - `/api/open/cards` - `/api/open/card/{cardId}` - `/api/open/card/{cardId}/view` - `/api/open/card/{cardId}/share` 即: - 小程序端不需要在路径中传 `tenantCode` - 后端通过 `AppID` 识别当前租户 #### 4. 请求上下文的识别策略 推荐策略如下: 1. 小程序请求时显式传递 `X-Miniapp-Appid` 2. Spring Boot 在过滤器中解析 `X-Miniapp-Appid` 3. 后端查询 `tenant_miniapp_config` 获取对应 `tenant_id` 4. 将 `tenant_id` 写入 `TenantContext` 5. 后续查询统一按 `tenant_id` 过滤 请求示例: ```http GET /api/v1/open/profile X-Miniapp-Appid: wx1234567890xxxxxx ``` 对应的服务端逻辑: ```text X-Miniapp-Appid -> tenant_miniapp_config -> tenant_id -> TenantContext ``` 说明: - 虽然 `X-Miniapp-Appid` 由前端传入,但其值来自构建时固定配置,不是页面动态参数 - `request_domain` 可作为辅助白名单校验,避免错误小程序配置访问非目标域名 - 对登录态接口和管理接口,仍必须叠加 token 与权限校验,不能只依赖 `AppID` 这种方式的优点是: - 实现简单,适合当前单租户小程序模式 - 后端租户识别统一 - 与微信登录场景天然兼容,便于后续通过 `AppID + AppSecret` 调用 `code2Session` #### 5. 管理端与小程序的数据边界 需要明确: - 小程序公开接口只返回“已发布、允许公开”的租户信息和名片信息 - 即使攻击者伪造请求,也只能尝试访问公开数据,不应接触后台管理数据 - 后台管理接口仍然必须基于登录态、角色和 `tenant_id` 做严格隔离 #### 6. 构建与发布策略 一个租户一个小程序,建议采用“同一套源码,多套租户配置”的方式发布: - 公共源码只维护一套 - 每个租户维护一份环境配置 - 打包时选择目标租户配置生成对应小程序包 建议配置文件结构: ```text frontend_miniprogram/ ├─ config/ │ ├─ tenants/ │ │ ├─ dev.default.ts │ │ ├─ prod.nj_xx_law.ts │ │ └─ prod.sample.ts ``` 这样可以保证: - 代码复用 - 配置隔离 - 发版过程可控 #### 7. 结论 在“一个小程序对应一个租户”的前提下,小程序识别租户的核心是: - 小程序 `AppID` - 后台维护的 `AppID -> tenant_id` 映射关系 - 小程序请求头中的 `X-Miniapp-Appid` 后端则通过统一的租户上下文机制,把请求路由到正确的租户数据范围。 ## 6.4 文件存储设计 使用 MinIO 统一管理: - 租户 Logo - 用户头像 - 名片二维码 - 封面图 建议做法: - 按业务分桶或目录管理 - 保存原始文件名、MIME、大小、哈希值 - 生成缩略图或压缩图 - 通过业务表引用文件 ID,而非直接在业务表中散落 URL ## 6.5 统计设计 统计范围: - 名片浏览次数 - 名片分享次数 - 热门名片排行 - 按日趋势统计 第一阶段建议方案: - 实时写入行为日志 - 定时汇总到统计表 这样可以兼顾: - 原始数据可追溯 - 查询统计页更高效 ## 7. 分层设计 后端建议遵循以下分层: - Controller:参数接收、鉴权注解、响应封装 - Service:业务编排、事务控制、权限校验 - Domain/Manager:核心领域规则 - Mapper:数据库访问 - DTO/VO:输入输出模型 统一规范: - 禁止 Controller 直接编写复杂业务逻辑 - 禁止 Service 直接暴露数据库实体给前端 - 输入输出对象分离,避免前端字段误改 ## 8. API 设计建议 ## 8.1 接口风格 - 统一 RESTful 风格 - 统一响应结构:`code`、`message`、`data` - 列表接口统一支持分页 - 查询接口统一支持关键字、状态、时间区间筛选 ## 8.2 接口分组建议 平台端: - `/api/platform/auth/*` - `/api/platform/tenants/*` - `/api/platform/users/*` - `/api/platform/config/*` 租户后台: - `/api/tenant/auth/*` - `/api/tenant/firm/*` - `/api/tenant/org/*` - `/api/tenant/users/*` - `/api/tenant/cards/*` - `/api/tenant/files/*` - `/api/tenant/stats/*` 小程序公开端: - `/api/open/profile` - `/api/open/cards` - `/api/open/card/{cardId}` - `/api/open/card/{cardId}/view` - `/api/open/card/{cardId}/share` ## 8.3 版本管理 建议从一开始就保留版本前缀: - `/api/v1/...` 这样便于未来做不兼容升级。 ## 9. 数据库设计建议 ## 9.1 核心表 建议第一阶段至少包含以下核心表: - `sys_tenant`:租户信息 - `tenant_miniapp_config`:租户与小程序配置映射 - `sys_user`:登录用户 - `sys_role`:角色 - `sys_user_role`:用户角色关系 - `org_department`:部门/分所 - `org_firm_profile`:事务所主页信息 - `card_profile`:个人名片主表 - `card_specialty`:名片专业领域 - `file_asset`:文件素材 - `card_view_log`:名片浏览日志 - `card_share_log`:名片分享日志 - `card_stat_daily`:名片按日统计表 - `sys_login_log`:登录日志 - `sys_operation_log`:操作日志 ## 9.2 通用字段规范 每个业务表建议统一包含: - `id` - `tenant_id` - `created_by` - `created_time` - `updated_by` - `updated_time` - `deleted` 说明: - 平台级表可不包含 `tenant_id` - 删除建议优先逻辑删除,关键日志表使用物理追加 ### 9.3 小程序配置表建议 建议新增 `tenant_miniapp_config` 表,用于描述租户与微信小程序的绑定关系。 建议字段: - `id` - `tenant_id` - `miniapp_app_id` - `miniapp_app_secret` - `miniapp_name` - `miniapp_original_id` - `request_domain` - `version_tag` - `publish_status` - `created_time` - `updated_time` 用途说明: - 后台超级管理员或平台实施人员配置租户对应的小程序信息 - 后端在微信登录、发布管理、AppID 识别时使用该表 - 后续若增加多个环境,可在该表中扩展 `env` 字段区分测试和生产配置 约束建议: - `miniapp_app_id` 全局唯一 - `tenant_id` 与 `miniapp_app_id` 建立唯一映射 - `miniapp_app_secret` 必须加密存储,不向前端返回 ## 10. 部署方案 ## 10.1 部署目标 目标是“轻量、稳定、可私有化部署”,推荐采用 Docker Compose 作为首期部署方式。 ## 10.2 推荐中间件 | 组件 | 版本建议 | 用途 | | --- | --- | --- | | Nginx | 1.26+ | 反向代理、静态资源分发 | | JDK | 21 | 运行 Spring Boot | | MySQL | 8.0 | 主数据库 | | Redis | 7 | 缓存与会话 | | MinIO | 最新稳定版 | 文件存储 | ## 10.2.1 服务器配置建议 本项目第一阶段目标是“轻量、稳定、可扩展”,因此不建议一开始就采购过重的集群资源。推荐按环境和业务规模分层配置。 ### 1. 开发环境 适用场景: - 本地开发 - 单人或小团队联调 建议配置: - CPU:4 核 - 内存:8 GB - 系统盘:100 GB SSD 部署建议: - MySQL、Redis、MinIO 可通过 Docker Desktop 或本机服务运行 - 后端、后台前端、小程序开发工具本地启动 ### 2. 测试环境 适用场景: - 功能测试 - 接口联调 - UAT 验收 建议配置: - CPU:4 核 - 内存:8 GB 到 16 GB - 系统盘:100 GB SSD - 数据盘:100 GB SSD 部署建议: - 单台 Linux 服务器部署 `Nginx + Spring Boot + MySQL + Redis + MinIO` - 使用 Docker Compose 管理服务,便于迁移和重建环境 ### 3. 生产环境当前推荐 适用场景: - 仅服务 1 家事务所 - 约 20 名后台用户 - 小程序访问量较低到中等 - 以名片展示、资料维护、图片上传为主 建议配置: - CPU:4 核 - 内存:8 GB - 系统盘:50 GB SSD - 数据盘:100 GB SSD - 带宽:3 Mbps 到 5 Mbps 部署方式: - 单机部署 - 使用 Docker Compose 部署 `Nginx + Spring Boot + MySQL + Redis + MinIO` 说明: - 这是当前项目规模下更匹配的正式生产配置 - 对于 1 家事务所、20 名用户,该配置已能覆盖日常访问和后台维护需求 - 若图片、二维码、封面素材不多,`50 GB 系统盘 + 100 GB 数据盘` 基本足够 - MinIO、MySQL 数据目录建议挂载到数据盘,避免挤占系统盘 ### 4. 容量规划建议 磁盘容量建议重点考虑以下部分: - MySQL 数据量 - MinIO 文件素材 - 日志文件 - 数据库备份 经验建议: - 当前项目规模下,初期生产环境总可用磁盘 150 GB 左右即可起步 - 若头像、二维码、宣传海报较多,可将数据盘扩展到 200 GB - 数据库与对象存储应避免共用过小系统盘 ### 5. 本项目首期推荐结论 按照当前明确范围: - 1 家事务所 - 约 20 名用户 - 单租户使用 推荐采用以下生产配置: - 1 台 4 核 8 GB Linux 云服务器 - 1 块 50 GB 系统盘 - 1 块 100 GB 数据盘 - 3 Mbps 到 5 Mbps 带宽 - Docker Compose 部署 `Nginx + Spring Boot + MySQL + Redis + MinIO` 如果希望多留一些冗余,可升级为: - 1 台 4 核 16 GB 或 8 核 8 GB Linux 云服务器 - 数据盘提升到 200 GB 当前规模下,不需要一开始就做多机、主从或集群部署。 ## 10.3 部署拓扑 ```mermaid flowchart TB U["浏览器 / 微信小程序"] --> N["Nginx"] N --> A["admin-web 静态站点"] N --> B["backend API"] B --> C["MySQL"] B --> D["Redis"] B --> E["MinIO"] ``` ## 10.4 环境划分 建议至少划分 3 套环境: - `dev`:本地开发环境 - `test`:测试联调环境 - `prod`:生产环境 每套环境均需独立配置: - 数据库连接 - Redis 地址 - 对象存储地址 - JWT 密钥 - 微信小程序配置 补充说明: - 由于采用“一租户一小程序”,生产环境需要为每个租户维护独立的小程序生产配置 - 测试环境可使用测试小程序 AppID 或体验版配置 ## 10.5 配置管理 建议采用: - Spring Profiles 区分环境 - 前端使用 `.env.development`、`.env.test`、`.env.production` - 敏感配置通过环境变量注入,不写死在仓库 ## 10.6 发布方式 推荐发布步骤: 1. 后端 Maven 打包为可执行 Jar。 2. 后台管理端 Vite 构建后部署到 Nginx 静态目录。 3. 选择目标租户的小程序配置,构建对应发行包。 4. 小程序通过微信开发者工具上传发布到该租户对应的小程序账号。 5. 数据库通过 Flyway 自动迁移到目标版本。 小程序发布补充要求: - 每个租户需独立维护小程序 AppID、类目、服务器域名和上传主体信息 - 发布前需校验当前构建包是否使用了正确的租户配置 - 建议在后台配置中记录当前租户对应的小程序版本号和发布时间 - 后端在生产环境中以 `AppID` 作为公开接口租户识别的主键 ### 10.6.1 Flyway 迁移约定 后端建议使用以下目录承载迁移脚本: - [db/migration/mysql](/Users/slience/postgraduate/easycard/backend/easycard-boot/src/main/resources/db/migration/mysql) 首批迁移建议: - `V1__create_core_schema.sql`:创建核心表结构 - `V2__seed_platform_base_data.sql`:初始化平台角色、菜单和字典 约束: - 不回改已上线版本脚本 - 所有字段变更、新表、新索引均追加新版本 - 租户级默认数据在业务代码的“创建租户”流程中初始化,不直接写入平台基线迁移 ## 11. 可观测性与运维 第一阶段建议最少落地以下能力: - 应用日志滚动归档 - 登录日志与操作日志 - API 错误日志 - 文件上传失败日志 - 慢 SQL 排查 若进入正式商用阶段,建议增加: - Prometheus + Grafana - Spring Boot Actuator - 异常告警通知 ## 12. 安全设计 必须满足以下安全要求: - 密码使用强哈希算法存储 - JWT 密钥与数据库密码不得入库明文 - 所有后台接口进行登录与权限校验 - 文件上传限制扩展名、大小与 MIME - 公开接口做基础限流 - 防止越权读取其他租户数据 - 敏感操作写入审计日志 ## 13. 开发实施建议 建议按以下顺序推进开发: 1. 先搭建后端基础框架、权限框架、租户框架、数据库脚手架。 2. 再完成后台管理端登录、租户管理、事务所管理、用户管理、名片管理。 3. 最后将微信小程序从 Mock 数据切换为真实接口,并补齐统计与异常处理。 ## 14. 第一阶段交付清单 第一阶段建议交付内容如下: - 后端基础框架与数据库脚本 - 超级管理员租户管理 - 租户管理员事务所管理、用户管理、名片管理 - 普通用户个人名片维护 - 小程序主页、列表、详情、历史动态化 - 文件上传 - 浏览统计 - 接口文档 - Docker Compose 部署文件 ## 15. 技术结论 结合当前需求与仓库现状,推荐采用: - 后端:`JDK 21 + Spring Boot 3.3 + Spring Security + MyBatis-Plus + MySQL + Redis + MinIO + Flyway` - 后台:`Vue 3 + TypeScript + Vite + Pinia + Element Plus` - 小程序:`原生微信小程序 + TypeScript + Less + 一租户一 AppID 的租户配置方案` - 部署:`Nginx + Docker Compose` 该方案在复杂度、交付速度、稳定性、私有化友好程度和后续扩展能力之间较为均衡,适合作为本项目第一阶段正式落地方案。后台统一多租户,小程序按租户独立发布,二者职责边界清晰,便于实施和运营。