API_NOTICE_MANAGEMENT.md 18 KB

商户通知管理接口文档

模块概述

商户通知管理模块提供通知统计、通知列表查询等功能,支持系统通知、订单提醒等多种通知类型。


接口列表

  1. 获取系统通知和订单提醒统计
  2. 获取通知列表

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

接口信息

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

请求参数

Query 参数

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

请求示例

GET /notice/getNoticeStatistics?receiverId=store_18241052019

响应参数

有通知数据时

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

响应字段说明

字段名 类型 说明
systemNotice Object/String 系统通知对象或字符串"null"
systemNotice.systemNum Long 系统通知未读数量
orderNotice Object/String 订单提醒对象或字符串"null"
orderNotice.orderNum Long 订单提醒未读数量

接口二:获取通知列表

接口信息

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

请求参数

Query 参数

参数名 类型 必填 默认值 说明
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",
                "senderId": "user_13800138000",
                "receiverId": "store_18241052019",
                "businessId": 301,
                "title": "新评论通知",
                "context": "用户对您的店铺进行了评论",
                "noticeType": 0,
                "isRead": 0,
                "userName": "张三",
                "userImage": "https://example.com/avatar.jpg",
                "platformType": "1",
                "createdTime": "2025-11-12 16:00:00"
            }
        ],
        "total": 50,
        "size": 10,
        "current": 1,
        "pages": 5
    },
    "msg": null
}

响应字段说明

字段名 类型 说明
records Array 当前页通知列表
records[].id String 通知ID
records[].senderId String 发送人ID
records[].receiverId String 接收人ID
records[].businessId Integer 业务ID
records[].title String 通知标题
records[].context String 通知内容
records[].noticeType Integer 通知类型
records[].isRead Integer 是否已读(0-未读,1-已读)
records[].userName String 发送用户名称
records[].userImage String 发送用户头像
records[].platformType String 平台类型(1-默认平台,2-其他)
records[].createdTime String 创建时间
total Long 总记录数
size Integer 每页条数
current Integer 当前页码
pages Integer 总页数

业务逻辑说明

通知类型

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

平台类型判断逻辑

平台类型 platformType 用于标识通知来源平台:

  • "1":默认平台(本平台)
  • "2":其他平台

判断规则

  1. businessId 为空 → 默认平台
  2. 标题为 "店铺审核通知" → 默认平台
  3. 举报内容分类 reportContextType 为 1、2、3(商户、用户、动态) → 默认平台

用户信息关联

通知中的 senderId 格式:

  • 系统通知"system"
  • 商户发送"store_手机号"(如:store_18241052019
  • 普通用户发送"user_手机号"(如:user_13800138000

系统会自动查询并关联发送人的 userNameuserImage


技术实现

Controller 层

文件路径alien-store-platform/src/main/java/shop/alien/storeplatform/controller/NoticeController.java

@ApiOperation("获取系统通知和订单提醒统计")
@GetMapping("/getNoticeStatistics")
public R<JSONObject> getNoticeStatistics(@RequestParam("receiverId") String receiverId) {
    // ...
}

@ApiOperation("获取通知列表")
@GetMapping("/getNoticeList")
public R<IPage<LifeNoticeVo>> getNoticeList(@RequestParam("pageNum") int pageNum,
                                             @RequestParam("pageSize") int pageSize,
                                             @RequestParam("receiverId") String receiverId,
                                             @RequestParam(value = "noticeType", defaultValue = "0") int noticeType) {
    // ...
}

Service 层

文件路径alien-store-platform/src/main/java/shop/alien/storeplatform/service/NoticeService.java

JSONObject getNoticeStatistics(String receiverId);

IPage<LifeNoticeVo> getNoticeList(int pageNum, int pageSize, String receiverId, int noticeType);

Service 实现层

文件路径alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/NoticeServiceImpl.java

getNoticeList 核心逻辑

@Override
public IPage<LifeNoticeVo> getNoticeList(int pageNum, int pageSize, String receiverId, int noticeType) {
    // 1. 查询通知列表
    LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(LifeNotice::getReceiverId, receiverId);
    queryWrapper.eq(LifeNotice::getNoticeType, noticeType);
    queryWrapper.eq(LifeNotice::getDeleteFlag, 0);
    queryWrapper.orderByDesc(LifeNotice::getCreatedTime);
    List<LifeNotice> lifeNoticeList = lifeNoticeMapper.selectList(queryWrapper);

    // 2. 解析 senderId,分组为 store 和 user
    Map<String, List<String>> senderIdMap = lifeNoticeList.stream()
            .map(LifeNotice::getSenderId)
            .filter(item -> item != null && !"system".equals(item) && item.contains("_"))
            .collect(Collectors.groupingBy(
                    item -> item.split("_")[0],  // 按 store 或 user 分组
                    Collectors.mapping(
                            item -> item.split("_")[1],  // 提取手机号
                            Collectors.toList()
                    )));

    // 3. 查询违规举报信息(用于判断平台类型)
    List<Integer> businessIdList = lifeNoticeList.stream()
            .map(LifeNotice::getBusinessId)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    
    List<LifeUserViolation> lifeUserViolationList = 
            lifeUserViolationMapper.selectBatchIds(businessIdList);
    
    Map<Integer, String> lifeUserViolationMap = lifeUserViolationList.stream()
            .filter(item -> item.getReportContextType() != null)
            .collect(Collectors.toMap(
                    LifeUserViolation::getId,
                    LifeUserViolation::getReportContextType,
                    (existing, replacement) -> existing
            ));

    // 4. 查询用户信息(商户和普通用户)
    String storePhones = senderIdMap.containsKey("store") ? 
            "'" + String.join("','", senderIdMap.get("store")) + "'" : "''";
    String userPhones = senderIdMap.containsKey("user") ? 
            "'" + String.join("','", senderIdMap.get("user")) + "'" : "''";
    
    List<LifeMessageVo> userList = 
            lifeMessageMapper.getLifeUserAndStoreUserByPhone(storePhones, userPhones);

    // 5. 组装 LifeNoticeVo,关联用户信息
    List<LifeNoticeVo> noticeVoList = new ArrayList<>();
    lifeNoticeList.forEach(item -> {
        LifeNoticeVo noticeVo = new LifeNoticeVo();
        BeanUtils.copyProperties(item, noticeVo);
        
        // 设置用户信息(如果不是系统通知)
        if (!"system".equals(noticeVo.getSenderId())) {
            LifeMessageVo userinfo = userList.stream()
                    .filter(user -> user.getPhoneId().equals(item.getSenderId()))
                    .findFirst()
                    .orElse(null);
            if (userinfo != null) {
                noticeVo.setUserName(userinfo.getUserName());
                noticeVo.setUserImage(userinfo.getUserImage());
            }
        }
        
        noticeVoList.add(noticeVo);
    });

    // 6. 设置平台类型标识
    for (LifeNoticeVo lifeNoticeVo : noticeVoList) {
        // businessId 为空时,设置为默认平台
        if (lifeNoticeVo.getBusinessId() == null) {
            lifeNoticeVo.setPlatformType("1");
        }
        
        // 店铺审核通知设置为默认平台
        if ("店铺审核通知".equals(lifeNoticeVo.getTitle())) {
            lifeNoticeVo.setPlatformType("1");
        }
        
        // 根据举报类型判断平台类型
        if (lifeNoticeVo.getBusinessId() != null && 
            lifeUserViolationMap.containsKey(lifeNoticeVo.getBusinessId())) {
            String reportContextType = lifeUserViolationMap.get(lifeNoticeVo.getBusinessId());
            if ("1,2,3".contains(reportContextType)) {
                lifeNoticeVo.setPlatformType("1");
            }
        }
    }

    // 7. 手动分页
    List<LifeNoticeVo> pageList = noticeVoList.stream()
            .skip((long) (pageNum - 1) * pageSize)
            .limit(pageSize)
            .collect(Collectors.toList());

    // 8. 构建分页结果
    IPage<LifeNoticeVo> result = new Page<>();
    result.setRecords(pageList);
    result.setTotal(noticeVoList.size());
    result.setPages((int) Math.ceil(noticeVoList.size() / (double) pageSize));
    result.setSize(pageSize);
    result.setCurrent(pageNum);

    return result;
}

依赖注入

Service 实现类

private final LifeNoticeMapper lifeNoticeMapper;         // 通知Mapper
private final LifeMessageMapper lifeMessageMapper;       // 消息Mapper(用于查询用户信息)
private final LifeUserViolationMapper lifeUserViolationMapper;  // 违规举报Mapper

导入依赖

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import shop.alien.entity.store.LifeNotice;
import shop.alien.entity.store.LifeUserViolation;
import shop.alien.entity.store.vo.LifeMessageVo;
import shop.alien.entity.store.vo.LifeNoticeVo;
import shop.alien.mapper.LifeMessageMapper;
import shop.alien.mapper.LifeNoticeMapper;
import shop.alien.mapper.LifeUserViolationMapper;

import java.util.*;
import java.util.stream.Collectors;

原接口对比

接口一:通知统计

项目 原接口 新接口
服务 alien-store(app端) alien-store-platform(web端)
路径 /alienStore/notice/getSystemAndOrderNoticeSum /notice/getNoticeStatistics
方法名 getSystemAndOrderNoticeSum getNoticeStatistics

接口二:通知列表

项目 原接口 新接口
服务 alien-store(app端) alien-store-platform(web端)
路径 /alienStore/notice/getNoticeByPhoneId /notice/getNoticeList
方法名 getNoticeList getNoticeList
Controller LifeNoticeController NoticeController
Service LifeNoticeService NoticeService

数据表说明

life_notice(通知表)

字段名 类型 说明
id String 主键ID
sender_id String 发送人ID
receiver_id String 接收人ID
business_id Integer 业务ID
title String 通知标题
context String 通知内容
notice_type Integer 通知类型(0/1/2)
is_read Integer 是否已读(0-未读,1-已读)
delete_flag Integer 删除标记(0-未删除,1-已删除)
created_time Date 创建时间

life_user_violation(用户举报表)

字段名 类型 说明
id Integer 主键ID
report_context_type String 举报内容分类(0-商户,1-用户,2-动态,3-评论,4-二手商品,5-二手用户)
violation_type String 违规类型
processing_status String 处理状态(0-未处理,1-违规,2-未违规)

错误处理

异常情况

  1. 参数错误:receiverId、pageNum、pageSize 为空或无效
  2. 数据库异常:查询失败
  3. 分页参数错误:pageNum < 1 或 pageSize < 1

错误响应示例

{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "查询失败:数据库连接异常"
}

使用场景

1. 首页通知角标

调用 getNoticeStatistics 获取未读数量,在首页显示通知角标。

2. 通知中心列表

调用 getNoticeList 获取通知列表,展示不同类型的通知:

  • noticeType=0:其他通知(评论、关注等)
  • noticeType=1:系统通知
  • noticeType=2:订单提醒

3. 实时更新

通过 WebSocket 或定时轮询更新通知数量和列表。


测试建议

测试场景

接口一(通知统计)

  1. 有系统通知和订单提醒
  2. 只有系统通知
  3. 只有订单提醒
  4. 没有任何通知
  5. 未读数量统计准确性

接口二(通知列表)

  1. 正常分页查询
  2. 空数据场景
  3. 不同 noticeType 查询
  4. 用户信息关联正确性
  5. 平台类型标识正确性
  6. 分页边界测试(第1页、最后1页、超出页数)

测试SQL

-- 查询商户的通知统计
SELECT 
    notice_type,
    COUNT(*) AS total_count,
    SUM(CASE WHEN is_read = 0 THEN 1 ELSE 0 END) AS unread_count,
    MAX(created_time) AS latest_time
FROM life_notice
WHERE receiver_id = 'store_18241052019'
  AND notice_type IN (1, 2)
  AND delete_flag = 0
GROUP BY notice_type;

-- 查询通知列表
SELECT 
    id, sender_id, receiver_id, business_id, title, context,
    notice_type, is_read, created_time
FROM life_notice
WHERE receiver_id = 'store_18241052019'
  AND notice_type = 0
  AND delete_flag = 0
ORDER BY created_time DESC
LIMIT 10 OFFSET 0;

注意事项

  1. receiverId 格式:通常为 store_ + 手机号(如:store_18241052019
  2. 逻辑删除:MyBatis-Plus 自动处理 delete_flag 字段
  3. 手动分页:由于需要关联多表和复杂逻辑,采用手动分页而非数据库分页
  4. 性能优化
    • 一次性查询所有通知,避免N+1问题
    • 批量查询用户信息和违规举报信息
    • 使用 Stream API 进行数据转换和过滤
  5. 空值处理:通知统计接口返回字符串 "null" 而非 null 对象
  6. 日志记录:详细记录查询参数、结果数量,便于问题追踪

更新日志

2025-11-12

接口一(通知统计)

  • ✅ 完成接口迁移:从 alien-store 迁移到 alien-store-platform
  • ✅ Controller 层:创建 NoticeController,添加 getNoticeStatistics 接口
  • ✅ Service 层:创建 NoticeService,添加方法定义
  • ✅ Service 实现层:创建 NoticeServiceImpl,实现统计逻辑
  • ✅ 业务逻辑:完全复用原接口逻辑
  • ✅ 命名规范:符合web端命名规范

接口二(通知列表)

  • ✅ 完成接口迁移:从 alien-store 迁移到 alien-store-platform
  • ✅ Controller 层:添加 getNoticeList 接口
  • ✅ Service 层:添加 getNoticeList 方法定义
  • ✅ Service 实现层:实现复杂的通知列表查询逻辑
  • ✅ 依赖注入:添加 LifeMessageMapperLifeUserViolationMapper
  • ✅ 用户信息关联:实现商户和普通用户信息查询
  • ✅ 平台类型判断:实现复杂的平台类型标识逻辑
  • ✅ 手动分页:实现内存分页功能
  • ✅ Linter 检查:修复所有警告
  • ✅ 业务逻辑:完全复用原接口逻辑
  • ✅ 命名规范:符合web端命名规范

开发者信息

  • 迁移时间:2025-11-12
  • 原服务:alien-store(app端商户)
  • 目标服务:alien-store-platform(web端商户)
  • 技术栈:Spring Boot + MyBatis-Plus + FastJSON + Hutool + Java 8