# 批量标记通知已读接口(一键已读)- DTO版本 ## 更新说明 本文档为 `markAllNoticesAsRead` 接口的最新版本,该接口已从使用Query参数改为使用DTO接收JSON请求体。 --- ## 接口信息 ### 批量标记通知为已读(一键已读) #### 接口详情 - **接口名称**: 批量标记通知为已读(一键已读) - **接口路径**: `POST /notice/markAllNoticesAsRead` - **请求方式**: POST - **Content-Type**: application/json - **接口描述**: 批量将指定用户的未读通知标记为已读,支持按通知类型筛选 - **登录验证**: ❌ 不需要(公开接口) --- ## 请求参数 ### 请求体(JSON格式) 使用 `MarkAllNoticesReadDTO` 作为请求体: | 参数名 | 类型 | 必填 | 说明 | 示例值 | |--------|------|------|------|--------| | receiverId | String | 是 | 接收人ID(商户ID) | "store_18241052019" | | noticeType | Integer | 否 | 通知类型 | 1 | **noticeType 说明**: - `0`: 系统通知和订单提醒之外的类型 - `1`: 系统通知 - `2`: 订单提醒 - 不传或传 `null`: 标记所有类型的通知 --- ## DTO定义 ```java @Data @ApiModel(description = "批量标记通知已读请求参数") public class MarkAllNoticesReadDTO implements Serializable { @ApiModelProperty(value = "接收人ID(商户ID)", required = true, example = "store_18241052019") private String receiverId; @ApiModelProperty(value = "通知类型(0-系统通知和订单提醒之外的类型,1-系统通知,2-订单提醒)。不传则标记所有类型", required = false, example = "1") private Integer noticeType; } ``` --- ## 请求示例 ### 示例1: 标记所有类型的通知为已读 ```http POST /notice/markAllNoticesAsRead Content-Type: application/json { "receiverId": "store_18241052019" } ``` ```bash curl -X POST "http://localhost:8080/notice/markAllNoticesAsRead" \ -H "Content-Type: application/json" \ -d '{ "receiverId": "store_18241052019" }' ``` --- ### 示例2: 只标记系统通知为已读 ```http POST /notice/markAllNoticesAsRead Content-Type: application/json { "receiverId": "store_18241052019", "noticeType": 1 } ``` ```bash curl -X POST "http://localhost:8080/notice/markAllNoticesAsRead" \ -H "Content-Type: application/json" \ -d '{ "receiverId": "store_18241052019", "noticeType": 1 }' ``` --- ### 示例3: 只标记订单提醒为已读 ```http POST /notice/markAllNoticesAsRead Content-Type: application/json { "receiverId": "store_18241052019", "noticeType": 2 } ``` ```bash curl -X POST "http://localhost:8080/notice/markAllNoticesAsRead" \ -H "Content-Type: application/json" \ -d '{ "receiverId": "store_18241052019", "noticeType": 2 }' ``` --- ## 响应参数 ### 成功响应 ```json { "code": 200, "success": true, "data": 15, "msg": "批量标记已读成功,共标记 15 条通知" } ``` ### 响应字段说明 | 字段名 | 类型 | 说明 | |--------|------|------| | code | Integer | 状态码,200表示成功 | | success | Boolean | 是否成功 | | data | Integer | 被标记的通知数量 | | msg | String | 提示信息 | --- ## 响应示例 ### 场景1: 成功标记多条通知 **请求**: ```json { "receiverId": "store_18241052019", "noticeType": 1 } ``` **响应**: ```json { "code": 200, "success": true, "data": 15, "msg": "批量标记已读成功,共标记 15 条通知" } ``` --- ### 场景2: 没有需要标记的通知 **请求**: ```json { "receiverId": "store_18241052019", "noticeType": 1 } ``` **响应**: ```json { "code": 200, "success": true, "data": 0, "msg": "没有需要标记的通知" } ``` **说明**: 当前用户该类型的通知已全部已读,或不存在该类型的通知。 --- ### 场景3: 参数错误 **请求**: ```json { "noticeType": 1 } ``` **响应**: ```json { "code": 500, "success": false, "data": null, "msg": "receiverId不能为空" } ``` --- ### 场景4: 系统异常 **响应**: ```json { "code": 500, "success": false, "data": null, "msg": "标记失败:{异常信息}" } ``` --- ## 业务逻辑说明 ### 处理流程 ``` ┌─────────────────────────────────────────────┐ │ 1. 接收DTO参数 │ │ - receiverId (必填) │ │ - noticeType (可选) │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 2. 参数验证 │ │ IF receiverId == null OR receiverId.isEmpty()│ │ RETURN 错误:receiverId不能为空 │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 3. 构建查询条件 │ │ wrapper.eq("receiver_id", receiverId) │ │ wrapper.eq("is_read", 0) // 未读 │ │ IF noticeType != null │ │ wrapper.eq("notice_type", noticeType) │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 4. 查询符合条件的未读通知 │ │ List notices = selectList(wrapper)│ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 5. 批量更新为已读状态 │ │ updateWrapper.set("is_read", 1) │ │ updateWrapper.eq("receiver_id", receiverId) │ │ updateWrapper.eq("is_read", 0) │ │ IF noticeType != null │ │ updateWrapper.eq("notice_type", noticeType)│ │ int count = update(null, updateWrapper) │ └─────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 6. 返回结果 │ │ IF count > 0 │ │ RETURN "批量标记已读成功,共标记 {count} 条"│ │ ELSE │ │ RETURN "没有需要标记的通知" │ └─────────────────────────────────────────────┘ ``` ### 更新SQL ```sql UPDATE life_notice SET is_read = 1 WHERE receiver_id = ? AND is_read = 0 AND notice_type = ? -- 如果指定了noticeType ``` --- ## 前端集成示例 ### Vue.js 示例 ```javascript export default { methods: { // 一键已读(所有类型) async markAllAsRead() { try { const response = await this.$axios.post('/notice/markAllNoticesAsRead', { receiverId: this.currentUserId }); if (response.data.success) { this.$message.success(response.data.msg); // 刷新通知列表 this.fetchNoticeList(); } else { this.$message.error(response.data.msg); } } catch (error) { console.error('标记失败:', error); this.$message.error('操作失败,请稍后重试'); } }, // 标记指定类型的通知为已读 async markTypeAsRead(noticeType) { try { const response = await this.$axios.post('/notice/markAllNoticesAsRead', { receiverId: this.currentUserId, noticeType: noticeType }); if (response.data.success) { const count = response.data.data; this.$message.success(`成功标记 ${count} 条通知为已读`); this.fetchNoticeList(); } } catch (error) { console.error('标记失败:', error); } } } } ``` --- ### React 示例 ```javascript import { useState } from 'react'; import axios from 'axios'; import { message } from 'antd'; function NoticeComponent() { const [userId, setUserId] = useState('store_18241052019'); // 一键已读(所有类型) const markAllAsRead = async () => { try { const response = await axios.post('/notice/markAllNoticesAsRead', { receiverId: userId }); if (response.data.success) { message.success(response.data.msg); // 刷新列表 fetchNoticeList(); } } catch (error) { message.error('操作失败'); } }; // 标记指定类型 const markTypeAsRead = async (noticeType) => { try { const response = await axios.post('/notice/markAllNoticesAsRead', { receiverId: userId, noticeType: noticeType }); if (response.data.success) { const count = response.data.data; message.success(`成功标记 ${count} 条通知为已读`); } } catch (error) { message.error('操作失败'); } }; return (
); } ``` --- ### jQuery/原生JS示例 ```javascript // 使用jQuery function markAllNoticesAsRead(receiverId, noticeType) { $.ajax({ url: '/notice/markAllNoticesAsRead', type: 'POST', contentType: 'application/json', data: JSON.stringify({ receiverId: receiverId, noticeType: noticeType }), success: function(response) { if (response.success) { alert(response.msg); // 刷新通知列表 loadNoticeList(); } else { alert('操作失败:' + response.msg); } }, error: function(xhr, status, error) { alert('网络错误,请稍后重试'); } }); } // 使用原生Fetch API async function markAllNoticesAsRead(receiverId, noticeType = null) { try { const response = await fetch('/notice/markAllNoticesAsRead', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ receiverId: receiverId, noticeType: noticeType }) }); const data = await response.json(); if (data.success) { console.log('标记成功:', data.msg); return data.data; // 返回标记的数量 } else { console.error('标记失败:', data.msg); return 0; } } catch (error) { console.error('请求失败:', error); return 0; } } // 使用示例 markAllNoticesAsRead('store_18241052019'); // 标记所有类型 markAllNoticesAsRead('store_18241052019', 1); // 只标记系统通知 ``` --- ## 与旧版本对比 ### 旧版本(Query参数) ```java @PostMapping("/markAllNoticesAsRead") public R markAllNoticesAsRead( @RequestParam("receiverId") String receiverId, @RequestParam(value = "noticeType", required = false) Integer noticeType ) ``` **请求方式**: ``` POST /notice/markAllNoticesAsRead?receiverId=store_18241052019¬iceType=1 ``` --- ### 新版本(JSON Body + DTO) ```java @PostMapping("/markAllNoticesAsRead") public R markAllNoticesAsRead(@RequestBody MarkAllNoticesReadDTO dto) ``` **请求方式**: ```http POST /notice/markAllNoticesAsRead Content-Type: application/json { "receiverId": "store_18241052019", "noticeType": 1 } ``` --- ### 差异对比 | 项目 | 旧版本 | 新版本 | 说明 | |------|--------|--------|------| | 参数传递方式 | Query参数 | JSON Body | 更符合RESTful规范 | | 参数定义 | 直接在方法参数中 | 使用DTO | 更易维护和扩展 | | Content-Type | application/x-www-form-urlencoded | application/json | 标准JSON格式 | | Swagger文档 | @ApiImplicitParams | @ApiModel/@ApiModelProperty | 自动生成完整文档 | | 参数验证 | 手动验证 | 可使用@Valid + 验证注解 | 更规范 | | 可扩展性 | 低 | 高 | 新增参数只需修改DTO | --- ## 参数验证增强(可选) 如果需要更严格的参数验证,可以在DTO中添加验证注解: ```java import javax.validation.constraints.NotBlank; import javax.validation.constraints.Min; import javax.validation.constraints.Max; @Data @ApiModel(description = "批量标记通知已读请求参数") public class MarkAllNoticesReadDTO implements Serializable { @NotBlank(message = "接收人ID不能为空") @ApiModelProperty(value = "接收人ID(商户ID)", required = true) private String receiverId; @Min(value = 0, message = "通知类型必须大于等于0") @Max(value = 2, message = "通知类型必须小于等于2") @ApiModelProperty(value = "通知类型", required = false) private Integer noticeType; } ``` 然后在Controller中启用验证: ```java @PostMapping("/markAllNoticesAsRead") public R markAllNoticesAsRead(@RequestBody @Valid MarkAllNoticesReadDTO dto) { // ... } ``` --- ## 常见问题 ### Q1: 为什么要改用DTO? **答案**: 1. **更符合RESTful规范**: POST请求应使用JSON Body传递数据 2. **易于扩展**: 新增参数只需修改DTO,不影响方法签名 3. **类型安全**: 强类型DTO,编译时检查 4. **易于验证**: 可以使用Bean Validation注解 5. **文档更清晰**: Swagger自动生成完整的请求体文档 --- ### Q2: 旧版本的调用方式还能用吗? **答案**: 不能。接口已改为使用JSON Body,必须按新的方式调用。 --- ### Q3: 如何迁移现有代码? **答案**: **旧代码**: ```javascript $.post('/notice/markAllNoticesAsRead?receiverId=' + userId + '¬iceType=1'); ``` **新代码**: ```javascript $.ajax({ url: '/notice/markAllNoticesAsRead', type: 'POST', contentType: 'application/json', data: JSON.stringify({ receiverId: userId, noticeType: 1 }) }); ``` --- ### Q4: noticeType 不传和传null有区别吗? **答案**: 没有区别。不传或传null都表示标记所有类型的通知。 --- ### Q5: 返回的data是什么? **答案**: data是被标记为已读的通知数量(Integer类型)。如果为0表示没有需要标记的通知。 --- ## 测试用例 ### 测试场景1: 标记所有类型的通知 **请求**: ```json { "receiverId": "store_18241052019" } ``` **预期结果**: - 返回标记的数量 > 0 - 所有未读通知被标记为已读 --- ### 测试场景2: 只标记系统通知 **请求**: ```json { "receiverId": "store_18241052019", "noticeType": 1 } ``` **预期结果**: - 只有系统通知(noticeType=1)被标记 - 其他类型的通知保持不变 --- ### 测试场景3: receiverId为空 **请求**: ```json { "noticeType": 1 } ``` **预期结果**: - 返回错误:"receiverId不能为空" --- ### 测试场景4: 无未读通知 **前置条件**: - 用户的所有通知都已读 **请求**: ```json { "receiverId": "store_18241052019" } ``` **预期结果**: ```json { "code": 200, "data": 0, "msg": "没有需要标记的通知" } ``` --- ## 性能优化 ### 1. 批量更新 使用MyBatis-Plus的批量更新功能,一次SQL更新所有符合条件的记录: ```sql UPDATE life_notice SET is_read = 1 WHERE receiver_id = ? AND is_read = 0 AND notice_type = ? ``` ### 2. 索引优化 确保以下字段有索引: ```sql -- 复合索引 ALTER TABLE life_notice ADD INDEX idx_receiver_read_type (receiver_id, is_read, notice_type); ``` ### 3. 返回更新数量 使用 `update()` 方法的返回值直接获取更新的记录数,避免额外查询。 --- ## 注意事项 ### 1. Content-Type必须正确 ⚠️ 必须设置 `Content-Type: application/json`,否则服务器无法解析JSON请求体。 ### 2. JSON格式 ⚠️ 请求体必须是有效的JSON格式,字段名需要与DTO中的字段名完全匹配。 ### 3. noticeType可选 ⚠️ `noticeType` 是可选参数,不传或传null表示标记所有类型的通知。 ### 4. 返回值类型 ⚠️ `data` 字段是Integer类型,表示被标记的数量,不是布尔值。 --- ## 更新日志 ### 2025-11-17 **接口重构**: - ✅ 从Query参数改为JSON Body + DTO - ✅ 创建 `MarkAllNoticesReadDTO` 类 - ✅ 修改Controller方法签名 - ✅ 移除 @ApiImplicitParams 注解 - ✅ 使用 @RequestBody 接收参数 **优势**: - ✅ 更符合RESTful规范 - ✅ 更易扩展和维护 - ✅ Swagger文档更清晰 - ✅ 支持参数验证注解 **涉及文件**: - `MarkAllNoticesReadDTO.java` - 新增 - `NoticeController.java` - 修改 **代码质量**: - ✅ Linter检查:无错误 - ✅ 代码规范:符合Java规范 - ✅ 注释完整:完整的注释和文档 **开发人员**: ssk --- **文档版本**: v2.0 **最后更新**: 2025-11-17 **维护人员**: ssk