Răsfoiți Sursa

docs: 文章设计文档增加浏览权限控制(public/login/private)

User 3 luni în urmă
părinte
comite
9d6447a24e

+ 52 - 4
docs/superpowers/specs/2026-06-08-article-publishing-system-design.md

@@ -75,6 +75,8 @@ Web管理端 (cfc-web)                   后端 (cfc-backend)
 | author | VARCHAR(100) | 作者名 |
 | read_time | INT DEFAULT 0 | 预计阅读分钟数 |
 | related_dimensions | VARCHAR(100) | 关联五维,JSON数组格式 `["mind","wisdom"]` |
+| **visibility** | VARCHAR(20) DEFAULT 'public' | 浏览权限:`public` / `login` / `private` |
+| **visible_to** | TEXT | 私密指定人群,JSON数组格式(见下方说明) |
 | status | VARCHAR(20) DEFAULT 'draft' | draft / published / archived |
 | is_featured | TINYINT DEFAULT 0 | 1=精选(在发现页展示)/ 0=普通 |
 | published_at | DATETIME | 发布时间 |
@@ -83,6 +85,27 @@ Web管理端 (cfc-web)                   后端 (cfc-backend)
 | created_at | DATETIME | |
 | updated_at | DATETIME | |
 
+**`visible_to` 格式说明:**
+
+`visibility=private` 时,`visible_to` 为 JSON 数组,每个元素是一个条件组(同一组内为 AND 关系,数组内不同组为 OR 关系):
+
+```json
+[
+  { "type": "family", "familyId": 42 },
+  { "type": "vendor_type", "vendorType": "planner" },
+  { "type": "vendor_type", "vendorType": "activity_provider" },
+  { "type": "user", "userId": 10086 }
+]
+```
+
+支持的 `type`:
+
+| type | 说明 | 参数 |
+|:-----|:------|:------|
+| `family` | 指定家庭 | `familyId` |
+| `vendor_type` | 指定供应商类型 | `vendorType`:planner / activity_provider / product_supplier |
+| `user` | 指定单个用户 | `userId` |
+
 ### 3.3 article_reading_records(已有表,仅增加关联)
 
 现有 `article_reading_records` 表结构不变,在写入时增加 `content` 字段填入文章ID,用于后续统计每个文章的阅读时长。
@@ -95,11 +118,26 @@ Web管理端 (cfc-web)                   后端 (cfc-backend)
 
 | 端点 | 方法 | 说明 | 参数 |
 |------|------|------|------|
-| `/api/articles/list` | POST | 文章分页列表 | `{ categoryId, keyword, page, size }` — 返回published |
-| `/api/articles/detail` | POST | 文章详情(含内容) | `{ id }` — 返回文章全部字段 |
-| `/api/articles/featured` | POST | 精选文章列表 | `{ size }` — 发现页用 |
+| `/api/articles/list` | POST | 文章分页列表 | `{ categoryId, keyword, page, size }` — 返回当前用户可见的published文章 |
+| `/api/articles/detail` | POST | 文章详情(含内容) | `{ id }` — 无权限时返回403 |
+| `/api/articles/featured` | POST | 精选文章列表 | `{ size }` — 只返回当前用户可见的精选 |
 | `/api/articles/categories` | POST | 分类列表 | 无 — 返回启用的分类 |
 
+**权限过滤逻辑(后端统一处理):**
+
+```
+当前请求用户:
+- 未登录 → 只能看到 visibility='public' 的文章
+- 已登录(无家庭/非供应商) → 看到 visibility='public' + visibility='login' 的文章
+- 已登录且有家庭 → 看到 public + login + private中visible_to含其familyId的
+- 已登录且是某类供应商 → 看到 public + login + private中visible_to含其vendorType的
+```
+
+`/api/articles/list` 和 `/api/articles/featured` 自动返回当前用户可见范围的文章。
+`/api/articles/detail` 检测权限,无权时返回 `{ code: 403, message: "无权访问该文章" }`。
+
+> **注意:** 未登录用户请求列表或详情时,`@RequestAttribute("userId")` 会为 null,需要在 Controller 中特殊处理(允许 userId=null 时只查 public)。
+
 **响应结构示例:**
 
 ```json
@@ -225,6 +263,13 @@ POST /api/articles/list
 - 作者(input)
 - 预计阅读时间(number input)
 - 关联五维(checkbox组:智/富/行/心/身)
+- 浏览权限(radio 三选一)
+  - 公开(public)— 未登录可见
+  - 登录可见(login)
+  - 私密(private)— 选此项时展开指定人群配置
+    - 家庭搜索+选择(按家庭ID/名称搜索)
+    - 供应商类型勾选(规划师/活动方/供应商)
+    - 单个用户搜索+选择
 - 状态(draft/published 切换)
 - 精选开关
 - 保存/发布按钮
@@ -252,6 +297,7 @@ POST /api/articles/list
 | ArticleCategoryController.java | controller/content/ |
 | AdminArticleController.java | controller/admin/ |
 | ArticleDTO.java | dto/(可选,非必须) |
+| ArticlePermissionService.java | service/(权限过滤逻辑抽离到独立Service) |
 
 ### 前端新增/修改
 
@@ -277,7 +323,9 @@ POST /api/articles/list
 | **ContentSection** 区块系统 | 无依赖,独立运行 |
 | **ArticleReadingRecord** 阅读记录 | 详情页调用记录接口写入数据;阅读数据可被 EnergyService 引用用于智维度计算 |
 | **五维能量系统** | articles 表的 `related_dimensions` 字段为能量系统预留关联入口 |
-| **用户角色** | 仅 admin 可管理文章;小程序端所有用户(含未登录)可阅读 |
+| **用户角色** | 仅 admin 可管理文章;小程序端按`visibility` + `visible_to` 控制不同用户的可见范围(public / login / private) |
+| **家庭系统** | 私密文章可按 `familyId` 精确指定可见家庭 |
+| **供应商系统** | 私密文章可按 `vendor_type` 指定某类供应商可见 |
 
 ---