content-media-flow.md 22 KB

用户使用流程图 — 内容与媒体

端口说明: 📱 小程序 | 🖥 管理后台 | 📋 规划师端 | ⚙️ 系统自动

分层说明: 🏠 页面 → 🔗 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
    }