用户使用流程图 — 内容与媒体
端口说明: 📱 小程序 | 🖥 管理后台 | 📋 规划师端 | ⚙️ 系统自动
分层说明: 🏠 页面 → 🔗 API → ⚙️ Service → 📦 数据实体
flowchart TD
%% ============ 颜色定义 ============
classDef page fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
classDef api fill:#fff3e0,stroke:#f57c00,stroke-width:1px
classDef service fill:#e8f5e9,stroke:#388e3c,stroke-width:1px
classDef data fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1px
classDef external fill:#fce4ec,stroke:#d32f2f,stroke-width:1px,stroke-dasharray:3 2
classDef actor fill:#e1f5fe,stroke:#0288d1,stroke-width:2px,stroke-dasharray:5 3
%% ============ 角色 ============
ROLE_USER(("👤 用户")):::actor
ROLE_ADMIN(("🖥 管理员")):::actor
%% ================================================================
%% 阶段一:文章浏览
%% ================================================================
subgraph 阶段一[阶段一:文章浏览]
direction TB
P1["📱 小程序:文章列表页"]:::page
P1 -->|"查看分类"| A1["🔗 POST /api/articles/categories"]:::api
A1 -->|"无参数"| S1["⚙️ ArticleCategoryService.getActiveCategories()"]:::service
S1 -->|"启用分类列表(按sortOrder排序)"| D1["📦 article_categories 表"]:::data
P1 -->|"按分类/维度/关键词过滤"| A2["🔗 POST /api/articles/list"]:::api
A2 -->|"categoryId, keyword, dimensionCode, page, size"| S2["⚙️ ArticleService.getPublicList()"]:::service
S2 -->|"已发布文章(含可见性过滤)"| D2["📦 articles 表(查询)"]:::data
P1 -->|"查看精选"| A3["🔗 POST /api/articles/featured"]:::api
A3 -->|"size, dimensionCode"| S3["⚙️ ArticleService.getFeatured()"]:::service
S3 -->|"isFeatured=1的已发布文章"| D2
P1 -->|"点击查看详情"| P1a["📱 小程序:文章详情页"]:::page
P1a -->|"文章ID"| A4["🔗 POST /api/articles/detail"]:::api
A4 -->|"id"| S4["⚙️ ArticleService.getDetail()"]:::service
S4 -->|"文章对象(含可见性校验)"| D2
end
%% ================================================================
%% 阶段二:阅读记录与积分
%% ================================================================
subgraph 阶段二[阶段二:阅读记录与积分]
P1a -->|"记录阅读(进入页面时)"| A5["🔗 POST /api/articles/record-read"]:::api
A5 -->|"id(文章ID), durationSeconds, childId"| S5["⚙️ ArticleService.recordRead()"]:::service
S5 -->|"viewCount+1, 写入阅读记录, 检查时长阈值, 创建Task(自动完成), 发放积分, PointsLog, 发放五维能量"| D3["📦 article_reading_records + tasks + points_logs + energy_records"]:::data
P1a -->|"上报阅读时长(定时上报)"| A6["🔗 POST /api/articles/report-reading-time"]:::api
A6 -->|"articleId, durationSeconds, childId"| S6["⚙️ ArticleService.reportReadingTime()"]:::service
S6 -->|"当日记录累加/新建"| D4["📦 article_reading_records(累加durationSeconds)"]:::data
P1 -->|"查看阅读统计"| P1b["📱 小程序:阅读统计页"]:::page
P1b -->|"孩子ID"| A7["🔗 POST /api/articles/reading-stats"]:::api
A7 -->|"childId"| S7["⚙️ ArticleService.getReadingStats()"]:::service
S7 -->|"月阅读数, 总时长, 进度百分比"| D4
end
%% ================================================================
%% 阶段三:每日贴士与AI互动
%% ================================================================
subgraph 阶段三[阶段三:每日贴士与AI互动]
P1 -->|"查看今日贴士"| P1c["📱 小程序:每日贴士页"]:::page
P1c -->|"无参数"| A8["🔗 POST /api/articles/daily-tip"]:::api
A8 -->|"无"| S8["⚙️ ArticleService.getDailyTip()"]:::service
S8 -->|"根据日期从心理类文章中轮选一篇"| D2
P1a -->|"获取AI问题"| A9["🔗 POST /api/articles/ai-questions"]:::api
A9 -->|"articleId, childId"| S9["⚙️ ArticleService.getAiQuestions()"]:::service
S9 -->|"调用Dify AI生成互动题目"| D5["📦 Dify AI(外部)"]:::data
P1a -->|"提交AI答案"| A10["🔗 POST /api/articles/submit-answers"]:::api
A10 -->|"recordId, answers(JSON), childId"| S10["⚙️ ArticleService.submitAnswers()"]:::service
S10 -->|"AI评分和反馈"| D5
end
%% ================================================================
%% 阶段四:用户发布内容
%% ================================================================
subgraph 阶段四[阶段四:用户发布内容]
P1 -->|"发布文章"| P1d["📱 小程序:发布文章页"]:::page
P1d -->|"标题, 正文, 封面图, 可见范围, 关联维度"| A11["🔗 POST /api/articles/publish"]:::api
A11 -->|"title, content, coverImage, visibility, relatedDimensions"| S11["⚙️ ArticleService.publishByUser()"]:::service
S11 -->|"公开→draft/待审核, 私密→published(家庭可见)"| D2
P1 -->|"我的帖子"| P1e["📱 小程序:我的帖子页"]:::page
P1e -->|"分页参数"| A12["🔗 POST /api/articles/my-posts"]:::api
A12 -->|"page, size"| S12["⚙️ ArticleService.getMyPosts()"]:::service
S12 -->|"用户发布的文章列表"| D2
end
%% ================================================================
%% 阶段五:分享
%% ================================================================
subgraph 阶段五[阶段五:内容分享]
P1a -->|"分享给好友"| P1f["📱 小程序:分享弹窗"]:::page
P1f -->|"目标类型, 目标ID, 分享渠道"| A13["🔗 POST /api/share/create"]:::api
A13 -->|"targetType, targetId, shareChannel, title"| S13["⚙️ ShareService.createShareEvent()"]:::service
S13 -->|"ShareEvent记录(含分享URL和有效期)"| D6["📦 share_events 表"]:::data
P1f -->|"好友点击分享链接"| A14["🔗 POST /api/share/click"]:::api
A14 -->|"shareEventId"| S14["⚙️ ShareService.recordClick()"]:::service
S14 -->|"clickCount+1"| D6
P1f -->|"好友阅读分享内容"| A15["🔗 POST /api/share/read"]:::api
A15 -->|"shareEventId, duration, readerIp"| S15["⚙️ ShareService.recordRead() + rewardReadPoints()"]:::service
S15 -->|"readCount+1, 达标则发放阅读者积分"| D6
P1 -->|"我的分享"| A16["🔗 POST /api/share/my"]:::api
A16 -->|"无参数"| S16["⚙️ ShareService.getUserShareEvents()"]:::service
S16 -->|"用户分享记录列表"| D6
P1 -->|"分享统计"| A17["🔗 POST /api/share/stats"]:::api
A17 -->|"无参数"| S17["⚙️ ShareService.getShareStats()"]:::service
S17 -->|"分享统计信息"| D6
P1 -->|"家庭足迹"| A18["🔗 POST /api/share/family-footprint"]:::api
A18 -->|"familyId"| S18["⚙️ ShareService.getFamilyFootprint()"]:::service
S18 -->|"家庭成员分享汇总"| D6
end
%% ================================================================
%% 阶段六:媒体管理
%% ================================================================
subgraph 阶段六[阶段六:媒体管理]
P2["📱 小程序:任务执行页"]:::page
P2 -->|"上传媒体文件"| A19["🔗 POST /api/media/upload"]:::api
A19 -->|"file(多部件), taskId, creatorId, description"| S19["⚙️ MediaRecordService.uploadMedia()"]:::service
S19 -->|"MediaRecord记录(含文件大小/类型校验)"| D7["📦 media_records 表"]:::data
P2 -->|"保存文字记录"| A20["🔗 POST /api/media/upload-text"]:::api
A20 -->|"taskId, creatorId, content"| S20["⚙️ MediaRecordService.saveTextRecord()"]:::service
S20 -->|"MediaRecord(类型=text)"| D7
P2 -->|"查看任务媒体"| A21["🔗 POST /api/media/task/{taskId}"]:::api
A21 -->|"路径参数: taskId"| S21["⚙️ MediaRecordService.getMediaByTaskId()"]:::service
S21 -->|"任务的媒体文件列表"| D7
P2 -->|"删除媒体"| A22["🔗 POST /api/media/{mediaId}"]:::api
A22 -->|"路径参数: mediaId, userId"| S22["⚙️ MediaRecordService.deleteMedia()"]:::service
S22 -->|"删除记录"| D7
P2 -->|"批量关联媒体"| A23["🔗 POST /api/media/attach"]:::api
A23 -->|"taskId, mediaIds列表"| S23["⚙️ MediaRecordService.attachMediaToTask()"]:::service
S23 -->|"更新media关联的taskId"| D7
end
%% ================================================================
%% 阶段七:管理后台文章管理
%% ================================================================
subgraph 阶段七[阶段七:管理后台文章管理]
P3["🖥 管理后台:文章管理页"]:::page
P3 -->|"查看列表(过滤)"| A24["🔗 POST /api/admin/articles/list"]:::api
A24 -->|"status, categoryId, keyword, contentType, difficultyLevel, dimensionCode, tagId, auditStatus, page, size"| S24["⚙️ ArticleService.getAdminList()"]:::service
S24 -->|"文章列表(含草稿)"| D2
P3 -->|"创建文章"| P3a["🖥 管理后台:创建文章表单"]:::page
P3a -->|"标题, 正文, 分类, 摘要, 封面, 标签, 作者, 阅读时间, 内容类型, 难度等级, 关联维度, 可见性, 文章类型, 状态"| A25["🔗 POST /api/admin/articles/create"]:::api
A25 -->|"全部文章字段"| S25["⚙️ ArticleService.create()"]:::service
S25 -->|"计算wordCount, AI自动打标签"| D2
P3 -->|"编辑文章"| P3b["🖥 管理后台:编辑文章表单"]:::page
P3b -->|"文章ID+修改字段"| A26["🔗 POST /api/admin/articles/update"]:::api
A26 -->|"id, 修改字段"| S26["⚙️ ArticleService.update()"]:::service
S26 -->|"更新字段, 内容变更则AI重新打标签"| D2
P3 -->|"查看详情"| A27["🔗 POST /api/admin/articles/detail"]:::api
A27 -->|"id"| S27["⚙️ ArticleService.getById()"]:::service
S27 -->|"Article对象"| D2
P3 -->|"删除"| A28["🔗 POST /api/admin/articles/delete"]:::api
A28 -->|"id"| S28["⚙️ ArticleService.delete()"]:::service
S28 -->|"删除记录"| D2
P3 -->|"发布/下架"| A29["🔗 POST /api/admin/articles/publish"]:::api
A29 -->|"id, status"| S29["⚙️ ArticleService.toggleStatus()"]:::service
S29 -->|"切换published/draft等状态"| D2
P3 -->|"设置精选"| A30["🔗 POST /api/admin/articles/toggle-featured"]:::api
A30 -->|"id, isFeatured"| S30["⚙️ ArticleService.toggleFeatured()"]:::service
S30 -->|"更新isFeatured字段"| D2
P3 -->|"提交审核"| A31["🔗 POST /api/admin/articles/submit-review"]:::api
A31 -->|"id"| S31["⚙️ ArticleService.submitForReview()"]:::service
S31 -->|"状态=pending, auditStatus=pending"| D2
P3 -->|"审核通过/驳回"| A32["🔗 POST /api/admin/articles/audit"]:::api
A32 -->|"id, auditStatus(approved/rejected), auditReason"| S32["⚙️ ArticleService.auditArticle()"]:::service
S32 -->|"approved→published / rejected→rejected(记录驳回原因)"| D2
P3 -->|"撤回"| A33["🔗 POST /api/admin/articles/withdraw"]:::api
A33 -->|"id"| S33["⚙️ ArticleService.withdraw()"]:::service
S33 -->|"状态=withdrawn"| D2
P3 -->|"驳回后转草稿"| A34["🔗 POST /api/admin/articles/re-draft"]:::api
A34 -->|"id"| S34["⚙️ ArticleService.reDraft()"]:::service
S34 -->|"状态=draft, 清空审核信息"| D2
P3 -->|"上传封面图"| A35["🔗 POST /api/admin/articles/upload/image"]:::api
A35 -->|"file(多部件)"| S35["⚙️ FileStorageService.store()"]:::service
S35 -->|"文件访问URL"| D8["📦 文件存储系统"]:::data
end
%% ================================================================
%% 阶段八:文章分类管理
%% ================================================================
subgraph 阶段八[阶段八:文章分类管理]
P3 -->|"查看分类列表"| A36["🔗 POST /api/admin/articles/categories/list"]:::api
A36 -->|"无参数"| S36["⚙️ ArticleCategoryService.listAll()"]:::service
S36 -->|"全部分类(含禁用)"| D1
P3 -->|"创建分类"| A37["🔗 POST /api/admin/articles/categories/create"]:::api
A37 -->|"name, icon, color, sortOrder"| S37["⚙️ ArticleCategoryService.create()"]:::service
S37 -->|"新分类记录"| D1
P3 -->|"更新分类"| A38["🔗 POST /api/admin/articles/categories/update"]:::api
A38 -->|"ArticleCategory对象(含id)"| S38["⚙️ ArticleCategoryService.update()"]:::service
S38 -->|"更新分类字段"| D1
P3 -->|"删除分类"| A39["🔗 POST /api/admin/articles/categories/delete"]:::api
A39 -->|"id"| S39["⚙️ ArticleCategoryService.delete()"]:::service
S39 -->|"删除分类记录"| D1
end
%% ================================================================
%% 角色关联
%% ================================================================
ROLE_USER -.- P1
ROLE_USER -.- P2
ROLE_ADMIN -.- P3
端点明细
文章浏览
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/articles/list |
文章列表 |
📱小程序 |
{categoryId, keyword, dimensionCode, page, size} |
Page<Article> |
POST /api/articles/categories |
文章分类 |
📱小程序 |
— |
[ArticleCategory] |
POST /api/articles/detail |
文章详情 |
📱小程序 |
{id} |
Article(含可见性校验) |
POST /api/articles/featured |
精选文章 |
📱小程序 |
{size, dimensionCode} |
[Article] |
POST /api/articles/record-read |
记录阅读(进页面) |
📱小程序 |
{id, durationSeconds, childId} |
{taskId, pointsEarned, alreadyCompleted, belowThreshold, minSeconds} |
POST /api/articles/report-reading-time |
上报阅读时长 |
📱小程序 |
{articleId, durationSeconds, childId} |
null |
POST /api/articles/reading-stats |
阅读统计 |
📱小程序 |
{childId} |
{totalArticles, totalDurationSeconds, targetDurationSeconds, progress} |
每日贴士与AI互动
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/articles/daily-tip |
每日贴士 |
📱小程序 |
— |
{id, title, summary, ...} |
POST /api/articles/ai-questions |
AI问题列表 |
📱小程序 |
{articleId, childId} |
{recordId, questions} |
POST /api/articles/submit-answers |
提交AI答案 |
📱小程序 |
{recordId, answers(JSON), childId} |
{score, feedback} |
用户发布
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/articles/publish |
发布文章(用户端) |
📱小程序 |
{title, content, coverImage, visibility, relatedDimensions} |
{id, status, auditStatus} |
POST /api/articles/my-posts |
我的帖子列表 |
📱小程序 |
{page, size} |
Page<Article> |
分享
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/share/create |
创建分享记录 |
📱小程序 |
{targetType, targetId, shareChannel, title} |
ShareEvent |
POST /api/share/click |
记录分享点击 |
📱小程序 |
{shareEventId} |
null |
POST /api/share/read |
记录分享阅读 |
📱小程序 |
{shareEventId, duration} |
{rewarded} |
POST /api/share/my |
我的分享记录 |
📱小程序 |
— |
[ShareEvent] |
POST /api/share/stats |
分享统计 |
📱小程序 |
— |
Map<String, Object> |
POST /api/share/family-footprint |
家庭足迹 |
📱小程序 |
{familyId} |
Map<String, Object> |
媒体管理
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/media/upload |
上传媒体文件 |
📱小程序 |
file(多部件), taskId, creatorId, description |
MediaRecord |
POST /api/media/upload-text |
保存文字记录 |
📱小程序 |
{taskId, creatorId, content} |
MediaRecord |
POST /api/media/task/{taskId} |
获取任务媒体 |
📱小程序 |
路径参数: taskId |
[MediaRecord] |
POST /api/media/{mediaId} |
删除媒体 |
📱小程序 |
路径参数: mediaId |
true |
POST /api/media/attach |
批量关联媒体到任务 |
📱小程序 |
{taskId, mediaIds} |
true |
管理后台文章
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/admin/articles/list |
文章列表(含草稿) |
🖥管理后台 |
{status, categoryId, keyword, contentType, difficultyLevel, dimensionCode, tagId, auditStatus, page, size} |
Page<Article> |
POST /api/admin/articles/create |
创建文章 |
🖥管理后台 |
文章全部字段 |
{id} |
POST /api/admin/articles/update |
更新文章 |
🖥管理后台 |
{id, title, content, categoryId, ...} |
"更新成功" |
POST /api/admin/articles/detail |
文章详情 |
🖥管理后台 |
{id} |
Article |
POST /api/admin/articles/delete |
删除文章 |
🖥管理后台 |
{id} |
"删除成功" |
POST /api/admin/articles/publish |
发布/下架文章 |
🖥管理后台 |
{id, status} |
"操作成功" |
POST /api/admin/articles/toggle-featured |
设置精选 |
🖥管理后台 |
{id, isFeatured} |
"操作成功" |
POST /api/admin/articles/submit-review |
提交审核 |
🖥管理后台 |
{id} |
"已提交审核" |
POST /api/admin/articles/audit |
审核文章 |
🖥管理后台 |
{id, auditStatus, auditReason} |
"审核完成" |
POST /api/admin/articles/withdraw |
撤回已发布文章 |
🖥管理后台 |
{id} |
"已撤回" |
POST /api/admin/articles/re-draft |
驳回后转草稿 |
🖥管理后台 |
{id} |
"已保存到草稿箱" |
POST /api/admin/articles/upload/image |
上传文章图片 |
🖥管理后台 |
file(多部件) |
图片URL |
管理后台分类
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/admin/articles/categories/list |
分类列表 |
🖥管理后台 |
— |
[ArticleCategory] |
POST /api/admin/articles/categories/create |
创建分类 |
🖥管理后台 |
ArticleCategory对象 |
"创建成功" |
POST /api/admin/articles/categories/update |
更新分类 |
🖥管理后台 |
ArticleCategory对象(含id) |
"更新成功" |
POST /api/admin/articles/categories/delete |
删除分类 |
🖥管理后台 |
{id} |
"删除成功" |
完整性分析
| # |
维度 |
评估 |
说明 |
| 1 |
流程完整性 |
✅ 完整 |
覆盖文章浏览→阅读记录→积分发放、每日贴士→AI互动、用户发布、分享全链路、媒体管理、管理后台文章CRUD+审核+分类管理共8个阶段 |
| 2 |
异常路径 |
⚠️ 部分覆盖 |
阅读时长不足阈值不发放积分(返回belowThreshold标记)已覆盖;重复阅读去重(同文章+同孩子)在Service层校验;文章可见性权限在ArticlePermissionService处理 |
| 3 |
端点覆盖 |
✅ 完整 |
39个端点全部映射到流程图中,与代码实际暴露的 /api/articles/*、/api/share/*、/api/media/*、/api/admin/articles/* 一致 |
| 4 |
角色覆盖 |
✅ 完整 |
普通用户(家长/孩子)浏览阅读发布、管理员后台管理、系统自动(积分发放/去重)均已覆盖 |
| 5 |
数据实体 |
✅ 完整 |
articles表、article_categories表、article_reading_records表、share_events表、media_records表、article_tags表、tasks表、points_logs表、energy_records表均在图中映射 |
| 6 |
一致性 |
✅ 与代码一致 |
所有端点路径与实际Controller(ArticleController、ArticleCategoryController、ShareController、MediaController、AdminArticleController)中的 @PostMapping 匹配 |
数据实体关系
erDiagram
Article ||--o{ ArticleReadingRecord : "阅读记录"
Article ||--o{ ShareEvent : "分享事件"
Article }o--|| ArticleCategory : "所属分类"
Article ||--o{ ArticleTag : "文章标签"
Task ||--o{ MediaRecord : "任务媒体"
ShareEvent ||--o{ ShareClick : "点击事件"
ShareEvent ||--o{ ShareRead : "阅读事件"
Article {
Long id PK
Long categoryId FK
string title
string summary
string coverImage
string content
string tags
string author
string articleType "normal/premium/original"
string contentType "article/knowledge/course/tip"
int difficultyLevel "1-5"
int wordCount
string relatedDimensions
string dimensionWeights "JSON五维权重"
string visibility "public/private/restricted"
string status "draft/pending/rejected/published/withdrawn"
string auditStatus "approved/pending/rejected"
string auditReason
int isFeatured
int viewCount
Long createdBy
string authorType "admin/parent/child"
}
ArticleCategory {
Long id PK
string name
string icon
string color
int sortOrder
int status "1=启用"
}
ArticleReadingRecord {
Long id PK
Long articleId FK
Long userId FK
Long childId FK
int durationSeconds
Long taskId "关联的积分任务"
date readAt
}
ShareEvent {
Long id PK
Long userId FK
string targetType "article/product"
Long targetId
string shareChannel "wechat/timeline"
string shareTitle
int clickCount
int readCount
string status
string shareUrl
date expiresAt
}
MediaRecord {
Long id PK
Long taskId FK
Long creatorId FK
string fileType "image/video/audio/text"
string fileUrl
string description
long fileSize
}