# AI 文章润色与拼写检查功能 - 需求规格说明书 | 文档版本 | 修订日期 | 修订人 | 修订内容 | | :------- | :--------- | :----- | :---------------------------------------- | | V1.0 | 2026-05-23 | — | 初始版本,定义 AI 文章润色功能完整需求 | --- ## 1. 项目概述 ### 1.1 功能背景 在博客写作过程中,创作者常面临语法不通顺、用词不当、标点不规范等问题。手动校对耗时且容易遗漏。为提升写作效率与文章质量,在 Jog 博客系统的文章编辑器中新增 **AI 润色** 功能,一键优化文本表达,并提供原文与润色结果的对比视图,允许用户灵活采纳。 ### 1.2 功能目标 - 降低创作者校对与润色的时间成本 - 通过 AI 自动优化语法、表达和标点,提升文章专业性 - 提供原文与润色结果的对比视图,让用户掌控修改粒度 - 支持段落级润色,避免一次性替换全文带来的风险 - 复用现有 DashScope 大模型接入能力,降低开发成本 ### 1.3 适用范围 | 角色 | 涉及范围 | | :------------- | :---------------------------------------------------- | | 产品经理 | 验证功能形态与对比交互流程 | | 前端开发工程师 | 实现"AI 润色"按钮、对比视图、采纳/撤销交互 | | 后端开发工程师 | 新增润色 API,复用 DashScope 调用链路 | | 测试工程师 | 编写自动化测试与回归用例 | | 运维工程师 | 无需额外配置(复用已有 `dashscope.*` 配置项) | --- ## 2. 名词解释 | 术语 | 说明 | | :----------------- | :------------------------------------------------------------------------------------- | | 润色 | 对文本进行语法修正、表达优化、标点规范化,保持原意不变 | | 对比视图 | 左侧展示原文、右侧展示润色结果的并排布局,方便用户逐段比对 | | 采纳 | 用户确认使用润色后的文本替换原文 | | 部分采纳 | 用户仅替换选定段落,而非全文替换 | | DashScope | 阿里云百炼对外暴露的 API 服务名,本系统对接其 OpenAI 兼容模式 | | Qwen-Turbo | 通义千问 Turbo 系列模型,速度快、成本低,适合文本润色等任务 | --- ## 3. 功能需求清单 ### 3.1 入口与按钮交互(前端) #### 3.1.1 功能描述 在文章编辑器页面(`/app/editor` 与 `/app/editor/:id`)的富文本工具栏右侧新增 **"AI 润色"** 按钮。用户选中需要润色的文本段落后,点击按钮调用后端润色接口,系统展示原文与润色结果的对比视图供用户决策。 #### 3.1.2 业务规则 | 编号 | 规则项 | 业务规则说明 | | :-------------------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------- | | FR-AI-POLISH-FE-01 | 按钮位置 | 按钮显示在富文本编辑器工具栏右侧,文案为"AI 润色",可配图标(如钢笔图标),使用 Element Plus `type="warning"` 区分现有按钮 | | FR-AI-POLISH-FE-02 | 触发前置条件 | 用户需在编辑器中选中文本(纯文本长度 ≥ 20 字符);未选中或选中过短时提示"请先选中至少 20 个字符的文本" | | FR-AI-POLISH-FE-03 | Loading 状态 | 按钮在请求期间显示 `loading` 态并禁用,防止重复点击 | | FR-AI-POLISH-FE-04 | 对比视图 | 润色完成后,在编辑器下方弹出对比面板:左侧为"原文"(带浅红背景)、右侧为"润色后"(带浅绿背景),逐段展示差异 | | FR-AI-POLISH-FE-05 | 段落级操作 | 每段对比行提供"采纳"按钮,用户可逐段替换;面板顶部提供"全部采纳"按钮,一键替换所有段落 | | FR-AI-POLISH-FE-06 | 采纳行为 | 点击"采纳",将该段润色文本替换回编辑器对应位置;替换后该段高亮闪烁一次以提示用户 | | FR-AI-POLISH-FE-07 | 关闭对比视图 | 提供"关闭"按钮关闭对比面板;关闭前若有未采纳段落,弹出确认"有未采纳的润色结果,确定关闭?" | | FR-AI-POLISH-FE-08 | 失败提示 | 接口返回失败时,提示后端返回的 `message`,按钮恢复可点击状态 | | FR-AI-POLISH-FE-09 | 超时提示 | 请求超过 30 秒未返回,提示"AI 服务响应超时,请稍后再试" | | FR-AI-POLISH-FE-10 | 多次润色 | 用户可对同一段落多次润色;对比视图始终展示当前原文与最新润色结果的对比 | #### 3.1.3 验收标准 | 编号 | 验证场景 | 预期结果 | | :-------------------- | :---------------------------------------------------------- | :-------------------------------------------------------------------------------- | | AC-AI-POLISH-FE-01 | 未选中文本时点击"AI 润色" | 提示"请先选中至少 20 个字符的文本",不发起后端请求 | | AC-AI-POLISH-FE-02 | 选中 ≥ 20 字符文本,点击按钮 | 按钮进入 loading 态,请求成功后显示对比视图 | | AC-AI-POLISH-FE-03 | 对比视图中点击单段"采纳" | 该段原文被润色文本替换,编辑器光标跳转到替换位置 | | AC-AI-POLISH-FE-04 | 对比视图中点击"全部采纳" | 所有段落被替换,面板自动关闭,提示"已全部采纳" | | AC-AI-POLISH-FE-05 | 有未采纳段落时点击关闭 | 弹出确认对话框;取消则保持面板;确认则关闭面板 | | AC-AI-POLISH-FE-06 | 后端返回 5001 错误 | 提示"AI 服务暂时不可用,请稍后再试",按钮恢复可用 | | AC-AI-POLISH-FE-07 | 在编辑文章(带 `:id`)页面同样可使用润色 | 行为与新建文章一致 | --- ### 3.2 AI 润色接口(后端) #### 3.2.1 功能描述 后端新增 **`POST /api/app/article/polish`** 接口,接收用户选中的文本段落,调用阿里云百炼大模型进行润色后返回润色结果。 #### 3.2.2 业务规则 | 编号 | 规则项 | 业务规则说明 | | :-------------------- | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | | FR-AI-POLISH-BE-01 | 接口路径与方法 | `POST /api/app/article/polish`,需登录用户调用(继承 `/api/app/**` 的 `USER`/`ADMIN` 权限校验) | | FR-AI-POLISH-BE-02 | 请求参数 | `text`(必填,最长 10000,为去除 HTML 标签后的纯文本选中内容) | | FR-AI-POLISH-BE-03 | 入参校验 | `text` 为空或去除 HTML 后字符数 < 20,返回 `400 BAD_REQUEST` 与提示"文本过短,无法润色" | | FR-AI-POLISH-BE-04 | 内容截断 | 若 `text` 字符数 > 6000,仅取前 6000 字符送入大模型,避免超过模型输入限制 | | FR-AI-POLISH-BE-05 | 模型与平台 | 复用现有 DashScope OpenAI 兼容模式调用,默认使用 `qwen-turbo` | | FR-AI-POLISH-BE-06 | 提示词(Prompt) | System:你是一名专业的文字编辑,请润色用户提供的中文文本。修正语法错误、优化表达方式、规范标点符号,保持原意不变,使文本更流畅专业。直接输出润色后的文本,不要添加任何解释或标记。
User:请润色下面这段中文文本:{用户文本} | | FR-AI-POLISH-BE-07 | 段落保持 | 润色后的文本应保持与原文相同的段落结构;若模型合并了段落,后端按原文段落数拆分后返回 | | FR-AI-POLISH-BE-08 | 鉴权方式 | 复用 `dashscope.api-key`,通过 `Authorization: Bearer {key}` 传递 | | FR-AI-POLISH-BE-09 | 超时与重试 | 单次调用超时 30 秒,不做自动重试;超时返回 `5002` 错误码 | | FR-AI-POLISH-BE-10 | 限流 | 复用现有 Redis 限流机制,同一用户每分钟最多调用 5 次(与摘要、标签推荐共享配额);超出返回 `429 RATE_LIMITED` | | FR-AI-POLISH-BE-11 | 异常处理 | DashScope 返回非 2xx 或 JSON 结构异常时,统一抛出 `BusinessException`,错误码 `5001 AI 服务暂时不可用` | | FR-AI-POLISH-BE-12 | 日志记录 | 记录请求耗时、文本长度、是否成功;不记录请求与响应的完整文本,避免日志膨胀 | #### 3.2.3 响应数据结构 ```json { "code": 200, "message": "success", "data": { "original": "这是需要润色的原文文本。", "polished": "这是经过润色的原文文本。", "diffSegments": [ { "index": 0, "original": "这是需要润色的原文文本。", "polished": "这是经过润色的原文文本。" } ], "model": "qwen-turbo", "promptTokens": 512, "completionTokens": 256, "costMillis": 1420 }, "timestamp": 1716268800000 } ``` | 字段 | 类型 | 说明 | | :----------------- | :------------ | :------------------------------------------------ | | original | String | 用户提交的原始文本 | | polished | String | 润色后的完整文本 | | diffSegments | Array | 逐段对比数据,前端据此渲染对比视图 | | diffSegments[].index | int | 段落序号(从 0 开始) | | diffSegments[].original | String | 该段原文 | | diffSegments[].polished | String | 该段润色后文本 | | model | String | 实际使用的模型名称 | | promptTokens | Integer | 输入消耗的 token 数(可为 null) | | completionTokens | Integer | 输出消耗的 token 数(可为 null) | | costMillis | Long | 接口耗时(毫秒) | #### 3.2.4 验收标准 | 编号 | 验证场景 | 预期结果 | | :-------------------- | :---------------------------------------------------------- | :-------------------------------------------------------------------------------- | | AC-AI-POLISH-BE-01 | 未登录调用接口 | 返回 `401 UNAUTHORIZED` | | AC-AI-POLISH-BE-02 | 登录用户传入合法文本 | 返回 200,`data.polished` 为润色后文本,`diffSegments` 数组非空 | | AC-AI-POLISH-BE-03 | 文本 < 20 字符 | 返回 `400 BAD_REQUEST`,提示"文本过短,无法润色" | | AC-AI-POLISH-BE-04 | `dashscope.api-key` 未配置 | 返回 `5005`,提示"AI 服务未配置 API Key" | | AC-AI-POLISH-BE-05 | DashScope 返回 401(API Key 无效) | 返回 `5001`,记录 ERROR 日志 | | AC-AI-POLISH-BE-06 | 单用户 1 分钟内连续调用 6 次 | 第 6 次返回 `429 RATE_LIMITED` | --- ### 3.3 对比视图交互设计 #### 3.3.1 组件结构 ``` ┌─────────────────────────────────────────────────────────┐ │ 工具栏 [B][I][H2][H3][列表][引用][分割线] [✨ AI 润色] │ ├─────────────────────────────────────────────────────────┤ │ │ │ [富文本编辑器内容区] │ │ │ ├─────────────────────────────────────────────────────────┤ │ AI 润色结果对比 [全部采纳] [✕] │ │ ┌─────────────────────┬─────────────────────────────┐ │ │ │ 原文 │ 润色后 │ │ │ │ 这是一段需要润色的 │ 这是一段经过润色的文本。 │ │ │ │ 文本。 │ [采纳] │ │ │ ├─────────────────────┼─────────────────────────────┤ │ │ │ 另一段原文... │ 另一段润色后... [采纳] │ │ │ └─────────────────────┴─────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` #### 3.3.2 交互规则 | 编号 | 规则项 | 说明 | | :-------------------- | :--------------- | :---------------------------------------------------------------------- | | FR-AI-POLISH-UI-01 | 差异高亮 | 原文与润色后之间的差异字符以不同颜色高亮(红色删除线 = 原文被删,绿色下划线 = 新增) | | FR-AI-POLISH-UI-02 | 段落边框 | 已采纳的段落行变为灰色并禁用"采纳"按钮,显示"已采纳"标记 | | FR-AI-POLISH-UI-03 | 全部采纳 | 点击"全部采纳"后,面板显示"全部已采纳"状态 2 秒后自动关闭 | | FR-AI-POLISH-UI-04 | 快捷键 | `Escape` 键关闭对比面板(触发表 FR-AI-POLISH-FE-07 的确认逻辑) | | FR-AI-POLISH-UI-05 | 空状态 | 若润色后文本与原文完全一致(无优化空间),展示提示"未发现需要润色的内容" | --- ## 4. 交互流程 ``` 1. 用户在编辑器中选中一段文本 2. 用户点击工具栏"AI 润色"按钮 3. 系统校验选中文本长度(≥ 20 字符) 4. 按钮进入 loading 态,调用后端润色 API 5. API 返回后,编辑器下方弹出对比视图 6. 用户查看逐段差异: a. 点击某段"采纳" → 该段原文被润色文本替换 b. 点击"全部采纳" → 全文替换,面板关闭 c. 点击"关闭" → 若有未采纳段落,确认后关闭 7. 完成后,用户可继续编辑或保存文章 ``` --- ## 5. 接口与代码对齐 ### 5.1 后端涉及代码 | 模块 | 文件 | 变更说明 | | :-------------- | :---------------------------------------------------------------- | :-------------------------------------------- | | jog-module-blog | `dto/PolishTextDTO.java` | 新增请求 DTO(`text` 字段 + `@NotBlank`) | | jog-module-blog | `dto/PolishResultVO.java` | 新增响应 VO(含 `diffSegments` 对比数组) | | jog-module-blog | `dto/DiffSegmentVO.java` | 新增段对比 VO(`index`、`original`、`polished`)| | jog-module-blog | `service/TextPolishService.java` + `service/impl/TextPolishServiceImpl.java` | 新增润色服务 | | jog-module-blog | `controller/ArticleController.java` | 新增 `POST /api/app/article/polish` | ### 5.2 前端涉及代码 | 模块 | 文件 | 变更说明 | | :----------- | :------------------------------------ | :---------------------------------------------------- | | jog-frontend | `src/api/article.js` | 新增 `polishArticleText(data)` | | jog-frontend | `src/views/app/ArticleEditor.vue` | 工具栏新增"AI 润色"按钮、新增对比面板组件 | | jog-frontend | `src/components/PolishDiffPanel.vue` | 新增对比面板组件(原文 vs 润色后,段落级采纳) | --- ## 6. 非功能性需求 | 编号 | 类型 | 需求说明 | | :------ | :-------- | :------------------------------------------------------------------------- | | NFR-01 | 性能 | 95 分位响应时间 ≤ 10 秒(取决于 DashScope 服务,由超时 + 限流保护) | | NFR-02 | 可用性 | DashScope 不可用时,用户仍可手动编辑文本,业务不受阻塞 | | NFR-03 | 安全性 | API Key 不入库、不入日志、不入仓库;前端不感知 Key 任何信息 | | NFR-04 | 可扩展性 | 后端通过 `TextPolishService` 接口抽象,未来可扩展拼写检查等独立功能 | | NFR-05 | 可观测性 | 关键节点输出 INFO/ERROR 日志,包含耗时、文本长度 | --- ## 7. 风险与缓解 | 风险 | 影响 | 缓解措施 | | :---------------------------- | :---------------------------------- | :------------------------------------------------------------------ | | 模型输出偏离原意 | 润色结果不可用 | 提示词强调"保持原意不变";对比视图让用户逐段审核 | | DashScope 服务限流或宕机 | 润色不可用 | 设置超时与降级,前端允许用户手动编辑 | | 模型返回格式异常 | 对比视图渲染失败 | 后端对返回结果做段落对齐校验,失败时降级为全文对比 | | 大量用户同时调用 | 超出 API 配额 | Redis 限流每分钟 5 次/用户 | --- **编写人:** AI 助手 **评审人:** 待评审 **生效日期:** 2026-05-23