07-标记通知已读接口.md 16 KB

商户通知管理接口文档

模块概述

商户通知管理模块提供通知统计、通知列表查询、通知已读标记(单个/批量)等功能,支持系统通知、订单提醒等多种通知类型。


接口列表

  1. 获取系统通知和订单提醒统计
  2. 获取通知列表
  3. 标记通知为已读
  4. 批量标记通知为已读(一键已读)新增

接口一:获取系统通知和订单提醒统计

接口信息

  • 接口名称:获取系统通知和订单提醒统计
  • 接口路径GET /notice/getNoticeStatistics
  • 接口描述:查询指定商户的系统通知和订单提醒统计信息,包括最新一条通知内容和未读数量

请求参数

参数名 类型 必填 说明
receiverId String 接收人ID(商户ID,格式如:store_18241052019)

请求示例

GET /notice/getNoticeStatistics?receiverId=store_18241052019

响应参数

{
    "code": 200,
    "success": true,
    "data": {
        "systemNotice": {
            "id": "12345",
            "title": "店铺审核通知",
            "context": "您的店铺已通过审核",
            "noticeType": 1,
            "isRead": 0,
            "createdTime": "2025-11-12 14:30:00",
            "systemNum": 5
        },
        "orderNotice": {
            "id": "12346",
            "title": "新订单提醒",
            "context": "您有一笔新订单",
            "noticeType": 2,
            "isRead": 0,
            "createdTime": "2025-11-12 15:20:00",
            "orderNum": 3
        }
    },
    "msg": "查询成功"
}

接口二:获取通知列表

接口信息

  • 接口名称:获取通知列表
  • 接口路径GET /notice/getNoticeList
  • 接口描述:分页查询指定商户的通知列表,支持按通知类型筛选

请求参数

参数名 类型 必填 默认值 说明
pageNum int - 页码(从1开始)
pageSize int - 每页条数
receiverId String - 接收人ID(商户ID)
noticeType int 0 通知类型(0-其他,1-系统通知,2-订单提醒)

请求示例

GET /notice/getNoticeList?receiverId=store_18241052019&pageNum=1&pageSize=10&noticeType=0

响应参数

{
    "code": 200,
    "success": true,
    "data": {
        "records": [
            {
                "id": "12347",
                "title": "新评论通知",
                "context": "用户对您的店铺进行了评论",
                "noticeType": 0,
                "isRead": 0,
                "userName": "张三",
                "userImage": "https://example.com/avatar.jpg",
                "createdTime": "2025-11-12 16:00:00"
            }
        ],
        "total": 50,
        "size": 10,
        "current": 1,
        "pages": 5
    }
}

接口三:标记通知为已读

接口信息

  • 接口名称:标记通知为已读
  • 接口路径POST /notice/markNoticeAsRead
  • 接口描述:将指定ID的通知标记为已读状态

请求参数

参数名 类型 必填 说明
id Integer 通知ID

请求示例

POST /notice/markNoticeAsRead?id=2589

响应参数

{
    "code": 200,
    "success": true,
    "data": null,
    "msg": "标记已读成功"
}

接口四:批量标记通知为已读(一键已读)

接口信息

  • 接口名称:批量标记通知为已读(一键已读)
  • 接口路径POST /notice/markAllNoticesAsRead
  • 接口描述:批量将指定商户的未读通知标记为已读,支持按通知类型筛选

请求参数

参数名 类型 必填 说明
receiverId String 接收人ID(商户ID)
noticeType Integer 通知类型(0-其他,1-系统通知,2-订单提醒)。不传则标记所有类型的未读通知

请求示例

示例1:标记所有未读通知

POST /notice/markAllNoticesAsRead?receiverId=store_18241052019

示例2:只标记系统通知

POST /notice/markAllNoticesAsRead?receiverId=store_18241052019&noticeType=1

示例3:只标记订单提醒

POST /notice/markAllNoticesAsRead?receiverId=store_18241052019&noticeType=2

响应参数

成功响应(有未读通知)

{
    "code": 200,
    "success": true,
    "data": 15,
    "msg": "批量标记已读成功,共标记 15 条通知"
}

成功响应(无未读通知)

{
    "code": 200,
    "success": true,
    "data": 0,
    "msg": "没有需要标记的通知"
}

失败响应

{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "批量标记失败:数据库异常"
}

响应字段说明

字段名 类型 说明
code Integer 响应状态码,200表示成功
success Boolean 是否成功
data Integer 标记的通知数量
msg String 响应消息

业务逻辑说明

通知类型

noticeType 说明
0 系统通知和订单提醒之外的类型(如评论、关注、举报等)
1 系统通知(店铺审核、违规处理等)
2 订单提醒(新订单、订单核销等)

批量标记逻辑

接口四(markAllNoticesAsRead) 的核心逻辑:

  1. 条件组合

    • 必须条件:receiverId = ? AND isRead = 0
    • 可选条件:noticeType = ?(如果传入)
  2. 更新操作

    • 将符合条件的所有通知的 isRead 字段更新为 1
  3. 返回结果

    • 返回实际更新的行数
  4. SQL 等效

    -- 标记所有未读通知
    UPDATE life_notice 
    SET is_read = 1 
    WHERE receiver_id = 'store_18241052019' 
     AND is_read = 0 
     AND delete_flag = 0;
       
    -- 只标记系统通知
    UPDATE life_notice 
    SET is_read = 1 
    WHERE receiver_id = 'store_18241052019' 
     AND is_read = 0 
     AND notice_type = 1 
     AND delete_flag = 0;
    

技术实现

Controller 层

@ApiOperation("批量标记通知为已读(一键已读)")
@PostMapping("/markAllNoticesAsRead")
public R<Integer> markAllNoticesAsRead(
        @RequestParam("receiverId") String receiverId,
        @RequestParam(value = "noticeType", required = false) Integer noticeType) {
    try {
        int result = noticeService.markAllNoticesAsRead(receiverId, noticeType);
        if (result > 0) {
            return R.data(result, "批量标记已读成功,共标记 " + result + " 条通知");
        }
        return R.data(0, "没有需要标记的通知");
    } catch (Exception e) {
        log.error("批量标记失败: {}", e.getMessage(), e);
        return R.fail(e.getMessage());
    }
}

Service 层

/**
 * 批量标记通知为已读(一键已读)
 *
 * @param receiverId 接收人ID(商户ID)
 * @param noticeType 通知类型(可选,不传则标记所有类型)
 * @return 影响行数
 */
int markAllNoticesAsRead(String receiverId, Integer noticeType);

Service 实现层

@Override
public int markAllNoticesAsRead(String receiverId, Integer noticeType) {
    log.info("批量标记通知为已读: receiverId={}, noticeType={}", 
             receiverId, noticeType);
    
    // 使用 LambdaUpdateWrapper 批量更新 isRead 字段
    LambdaUpdateWrapper<LifeNotice> wrapper = new LambdaUpdateWrapper<>();
    
    // 1. 接收人条件(必须)
    wrapper.eq(LifeNotice::getReceiverId, receiverId);
    
    // 2. 只更新未读的通知
    wrapper.eq(LifeNotice::getIsRead, 0);
    
    // 3. 通知类型条件(可选)
    if (noticeType != null) {
        wrapper.eq(LifeNotice::getNoticeType, noticeType);
    }
    
    // 4. 设置为已读
    wrapper.set(LifeNotice::getIsRead, 1);
    
    int result = lifeNoticeMapper.update(null, wrapper);
    
    log.info("批量标记完成: 影响行数={}", result);
    return result;
}

使用场景

场景一:通知中心一键清除

用户进入通知中心,点击"一键已读"按钮:

// 前端调用示例
async function markAllAsRead() {
    const response = await axios.post('/notice/markAllNoticesAsRead', null, {
        params: {
            receiverId: 'store_18241052019'
        }
    });
    
    if (response.data.success) {
        console.log(`成功标记 ${response.data.data} 条通知为已读`);
        // 刷新通知列表和统计
        refreshNoticeList();
        refreshNoticeStatistics();
    }
}

场景二:分类一键已读

用户在"系统通知"标签页点击"全部已读":

// 只标记系统通知为已读
async function markSystemNoticesAsRead() {
    const response = await axios.post('/notice/markAllNoticesAsRead', null, {
        params: {
            receiverId: 'store_18241052019',
            noticeType: 1  // 系统通知
        }
    });
    
    if (response.data.success) {
        console.log(`成功标记 ${response.data.data} 条系统通知为已读`);
    }
}

场景三:定时自动标记

后台定时任务,自动标记超过7天的未读通知(需扩展接口)


接口调用流程

完整业务流程

用户进入通知中心
    ↓
① 调用 getNoticeStatistics
    ↓
显示:系统通知(5)、订单提醒(3)
    ↓
② 调用 getNoticeList(noticeType=1)
    ↓
显示系统通知列表
    ↓
用户点击"全部已读"按钮
    ↓
③ 调用 markAllNoticesAsRead(receiverId, noticeType=1)
    ↓
返回:成功标记 5 条通知
    ↓
④ 重新调用 getNoticeStatistics
    ↓
显示:系统通知(0)、订单提醒(3)

对比:单个 vs 批量标记

特性 单个标记 批量标记
接口 markNoticeAsRead markAllNoticesAsRead
参数 id(通知ID) receiverId + noticeType(可选)
更新范围 单条通知 多条未读通知
返回值 影响行数(0或1) 影响行数(实际标记数量)
使用场景 点击单条通知 一键清除未读
性能 单次更新 批量更新(更高效)

性能对比

假设有 100 条未读通知:

方式一:循环调用单个标记

// ❌ 效率低:需要 100 次网络请求
for (let id of noticeIds) {
    await markNoticeAsRead(id);  // 100次请求
}

方式二:调用批量标记

// ✅ 效率高:只需 1 次网络请求
await markAllNoticesAsRead(receiverId, noticeType);  // 1次请求

错误处理

异常情况

  1. 参数错误:receiverId 为空
  2. 数据库异常:批量更新失败
  3. 无权限:尝试标记其他商户的通知

错误响应示例

{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "批量标记失败:receiverId 不能为空"
}

测试建议

测试场景

  1. 标记所有类型noticeType 不传,验证所有未读通知被标记
  2. 标记指定类型:传入 noticeType=1,验证只标记系统通知
  3. 重复调用:多次调用,验证返回 0(已无未读通知)
  4. 空数据场景:商户无未读通知,验证返回 0
  5. 并发测试:多个请求同时标记,验证数据一致性
  6. 边界测试
    • receiverId 不存在
    • noticeType 为无效值(如 99)

测试SQL

-- 查询商户的未读通知数量(标记前)
SELECT 
    notice_type,
    COUNT(*) AS unread_count
FROM life_notice
WHERE receiver_id = 'store_18241052019'
  AND is_read = 0
  AND delete_flag = 0
GROUP BY notice_type;

-- 执行批量标记(模拟)
UPDATE life_notice 
SET is_read = 1 
WHERE receiver_id = 'store_18241052019' 
  AND is_read = 0 
  AND delete_flag = 0;

-- 验证标记结果(标记后)
SELECT 
    notice_type,
    COUNT(*) AS unread_count
FROM life_notice
WHERE receiver_id = 'store_18241052019'
  AND is_read = 0
  AND delete_flag = 0
GROUP BY notice_type;
-- 应该返回 0 条记录

注意事项

  1. 幂等性:支持重复调用,多次调用不会产生副作用
  2. 性能优化:使用批量更新,避免循环调用单个标记接口
  3. 逻辑删除:MyBatis-Plus 自动过滤 delete_flag=1 的记录
  4. 事务安全:更新操作在事务中执行,保证数据一致性
  5. 日志记录:记录操作参数和影响行数,便于问题追踪
  6. 返回信息:明确告知用户标记了多少条通知
  7. 可选参数noticeType 不传时标记所有类型,传入时只标记指定类型
  8. 安全性:需要验证 receiverId 的合法性,防止越权操作

接口汇总表

序号 接口名称 接口路径 请求方式 主要功能
1 获取通知统计 /notice/getNoticeStatistics GET 查询未读数量和最新通知
2 获取通知列表 /notice/getNoticeList GET 分页查询通知列表
3 标记单个已读 /notice/markNoticeAsRead POST 标记指定通知为已读
4 批量标记已读 /notice/markAllNoticesAsRead POST 一键标记多个通知为已读

更新日志

2025-11-12

新增接口

  • markAllNoticesAsRead(批量标记通知为已读)
    • Controller 层:添加 /markAllNoticesAsRead 接口
    • Service 层:添加 markAllNoticesAsRead 方法定义
    • Service 实现层:实现批量更新逻辑
    • 支持可选参数 noticeType,可按类型筛选
    • 返回实际标记的通知数量
    • 完整的日志记录和异常处理
    • Linter 检查:无错误

其他接口

  • ✅ getNoticeStatistics(通知统计)
  • ✅ getNoticeList(通知列表)
  • ✅ markNoticeAsRead(单个标记已读)

开发者信息

  • 迁移时间:2025-11-12
  • 原服务:alien-store(app端商户)
  • 目标服务:alien-store-platform(web端商户)
  • 技术栈:Spring Boot + MyBatis-Plus + FastJSON + Java 8
  • 开发人员:ssk
  • 文档版本:v2.0(新增批量标记接口)

常见问题

Q1:批量标记和单个标记有什么区别?

A

  • 单个标记:针对指定ID的通知,适用于用户点击单条通知时
  • 批量标记:针对指定商户的所有未读通知(可按类型筛选),适用于"一键已读"功能

Q2:批量标记是否支持按时间范围筛选?

A:当前版本不支持按时间范围筛选。如需此功能,可以扩展接口,添加 startTimeendTime 参数。

Q3:批量标记是否会标记已读的通知?

A:不会。批量标记只会更新 isRead = 0(未读)的通知,已读通知(isRead = 1)会被自动跳过。

Q4:如何实现"标记最近7天的未读通知"?

A:需要扩展接口,添加时间范围参数:

wrapper.ge(LifeNotice::getCreatedTime, LocalDate.now().minusDays(7));

Q5:批量标记的性能如何?

A:性能优异。使用 MyBatis-Plus 的批量更新,一次SQL执行完成所有更新操作,比循环调用单个标记接口效率高数百倍。

Q6:批量标记后如何通知前端更新?

A:建议的方案:

  1. 批量标记成功后,前端重新调用 getNoticeStatistics 更新角标
  2. 刷新当前通知列表页面
  3. 如果使用 WebSocket,可以推送更新事件

扩展建议

未来可扩展功能

  1. 按时间范围标记

    • 添加 startTimeendTime 参数
    • 实现"标记最近7天的通知"等功能
  2. 按ID列表标记

    • 添加 ids 数组参数
    • 支持勾选多个通知后批量标记
  3. 标记并删除

    • 一次操作完成标记已读+逻辑删除
    • 适用于"清空通知"功能
  4. 定时自动标记

    • 后台定时任务
    • 自动标记超过指定天数的未读通知
  5. 标记统计

    • 返回更详细的统计信息
    • 如:按类型分别显示标记数量