|
|
@@ -0,0 +1,743 @@
|
|
|
+# 商户通知管理接口文档
|
|
|
+
|
|
|
+## 模块概述
|
|
|
+
|
|
|
+商户通知管理模块提供通知统计、通知列表查询、通知已读标记等功能,支持系统通知、订单提醒等多种通知类型。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 接口列表
|
|
|
+
|
|
|
+1. [获取系统通知和订单提醒统计](#接口一获取系统通知和订单提醒统计)
|
|
|
+2. [获取通知列表](#接口二获取通知列表)
|
|
|
+3. [标记通知为已读](#接口三标记通知为已读)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 接口一:获取系统通知和订单提醒统计
|
|
|
+
|
|
|
+### 接口信息
|
|
|
+
|
|
|
+- **接口名称**:获取系统通知和订单提醒统计
|
|
|
+- **接口路径**:`GET /notice/getNoticeStatistics`
|
|
|
+- **接口描述**:查询指定商户的系统通知和订单提醒统计信息,包括最新一条通知内容和未读数量
|
|
|
+
|
|
|
+### 请求参数
|
|
|
+
|
|
|
+#### Query 参数
|
|
|
+
|
|
|
+| 参数名 | 类型 | 必填 | 说明 |
|
|
|
+|--------|------|------|------|
|
|
|
+| receiverId | String | 是 | 接收人ID(商户ID,格式如:store_18241052019) |
|
|
|
+
|
|
|
+#### 请求示例
|
|
|
+
|
|
|
+```http
|
|
|
+GET /notice/getNoticeStatistics?receiverId=store_18241052019
|
|
|
+```
|
|
|
+
|
|
|
+### 响应参数
|
|
|
+
|
|
|
+#### 有通知数据时
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "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": "查询成功"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 无通知数据时
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "success": true,
|
|
|
+ "data": {
|
|
|
+ "systemNotice": "null",
|
|
|
+ "orderNotice": "null"
|
|
|
+ },
|
|
|
+ "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-订单提醒) |
|
|
|
+
|
|
|
+#### 请求示例
|
|
|
+
|
|
|
+```http
|
|
|
+GET /notice/getNoticeList?receiverId=store_18241052019&pageNum=1&pageSize=10¬iceType=0
|
|
|
+```
|
|
|
+
|
|
|
+### 响应参数
|
|
|
+
|
|
|
+#### 响应数据结构
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "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 | 总页数 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 接口三:标记通知为已读
|
|
|
+
|
|
|
+### 接口信息
|
|
|
+
|
|
|
+- **接口名称**:标记通知为已读
|
|
|
+- **接口路径**:`POST /notice/markNoticeAsRead`
|
|
|
+- **接口描述**:将指定ID的通知标记为已读状态
|
|
|
+
|
|
|
+### 请求参数
|
|
|
+
|
|
|
+#### Query 参数
|
|
|
+
|
|
|
+| 参数名 | 类型 | 必填 | 说明 |
|
|
|
+|--------|------|------|------|
|
|
|
+| id | Integer | 是 | 通知ID |
|
|
|
+
|
|
|
+#### 请求示例
|
|
|
+
|
|
|
+```http
|
|
|
+POST /notice/markNoticeAsRead?id=2589
|
|
|
+```
|
|
|
+
|
|
|
+### 响应参数
|
|
|
+
|
|
|
+#### 成功响应
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "success": true,
|
|
|
+ "data": null,
|
|
|
+ "msg": "标记已读成功"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 失败响应
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 500,
|
|
|
+ "success": false,
|
|
|
+ "data": null,
|
|
|
+ "msg": "标记已读失败"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 响应字段说明
|
|
|
+
|
|
|
+| 字段名 | 类型 | 说明 |
|
|
|
+|--------|------|------|
|
|
|
+| code | Integer | 响应状态码,200表示成功 |
|
|
|
+| success | Boolean | 是否成功 |
|
|
|
+| data | Any | 数据(此接口为null) |
|
|
|
+| msg | String | 响应消息 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 业务逻辑说明
|
|
|
+
|
|
|
+### 通知类型
|
|
|
+
|
|
|
+| noticeType | 说明 |
|
|
|
+|------------|------|
|
|
|
+| 0 | 系统通知和订单提醒之外的类型(如评论、关注、举报等) |
|
|
|
+| 1 | 系统通知(店铺审核、违规处理等) |
|
|
|
+| 2 | 订单提醒(新订单、订单核销等) |
|
|
|
+
|
|
|
+### 已读状态
|
|
|
+
|
|
|
+| isRead | 说明 |
|
|
|
+|--------|------|
|
|
|
+| 0 | 未读 |
|
|
|
+| 1 | 已读 |
|
|
|
+
|
|
|
+### 平台类型判断逻辑
|
|
|
+
|
|
|
+**平台类型 `platformType`** 用于标识通知来源平台:
|
|
|
+- `"1"`:默认平台(本平台)
|
|
|
+- `"2"`:其他平台
|
|
|
+
|
|
|
+**判断规则**:
|
|
|
+1. `businessId` 为空 → 默认平台
|
|
|
+2. 标题为 "店铺审核通知" → 默认平台
|
|
|
+3. 举报内容分类 `reportContextType` 为 1、2、3(商户、用户、动态) → 默认平台
|
|
|
+
|
|
|
+### 用户信息关联
|
|
|
+
|
|
|
+通知中的 `senderId` 格式:
|
|
|
+- **系统通知**:`"system"`
|
|
|
+- **商户发送**:`"store_手机号"`(如:`store_18241052019`)
|
|
|
+- **普通用户发送**:`"user_手机号"`(如:`user_13800138000`)
|
|
|
+
|
|
|
+系统会自动查询并关联发送人的 `userName` 和 `userImage`。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 技术实现
|
|
|
+
|
|
|
+### Controller 层
|
|
|
+
|
|
|
+**文件路径**:`alien-store-platform/src/main/java/shop/alien/storeplatform/controller/NoticeController.java`
|
|
|
+
|
|
|
+```java
|
|
|
+@Slf4j
|
|
|
+@Api(tags = {"web端商户通知管理"})
|
|
|
+@ApiSort(6)
|
|
|
+@CrossOrigin
|
|
|
+@RestController
|
|
|
+@RequestMapping("/notice")
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class NoticeController {
|
|
|
+
|
|
|
+ private final NoticeService noticeService;
|
|
|
+
|
|
|
+ @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) {
|
|
|
+ // 查询通知列表
|
|
|
+ }
|
|
|
+
|
|
|
+ @ApiOperation("标记通知为已读")
|
|
|
+ @PostMapping("/markNoticeAsRead")
|
|
|
+ public R<Boolean> markNoticeAsRead(@RequestParam("id") Integer id) {
|
|
|
+ // 标记通知为已读
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Service 层
|
|
|
+
|
|
|
+**文件路径**:`alien-store-platform/src/main/java/shop/alien/storeplatform/service/NoticeService.java`
|
|
|
+
|
|
|
+```java
|
|
|
+public interface NoticeService {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取系统通知和订单提醒统计
|
|
|
+ */
|
|
|
+ JSONObject getNoticeStatistics(String receiverId);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取通知列表
|
|
|
+ */
|
|
|
+ IPage<LifeNoticeVo> getNoticeList(int pageNum, int pageSize,
|
|
|
+ String receiverId, int noticeType);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 标记通知为已读
|
|
|
+ */
|
|
|
+ int markNoticeAsRead(Integer id);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Service 实现层
|
|
|
+
|
|
|
+**文件路径**:`alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/NoticeServiceImpl.java`
|
|
|
+
|
|
|
+#### 核心实现
|
|
|
+
|
|
|
+**1. getNoticeStatistics - 通知统计**
|
|
|
+
|
|
|
+```java
|
|
|
+@Override
|
|
|
+public JSONObject getNoticeStatistics(String receiverId) {
|
|
|
+ // 1. 查询系统通知和订单提醒(noticeType: 1, 2)
|
|
|
+ LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(LifeNotice::getReceiverId, receiverId);
|
|
|
+ queryWrapper.in(LifeNotice::getNoticeType, 1, 2);
|
|
|
+ List<LifeNotice> lifeNoticeList = lifeNoticeMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ // 2. 获取最新一条系统通知和订单提醒
|
|
|
+ LifeNotice systemNotice = lifeNoticeList.stream()
|
|
|
+ .filter(item -> 1 == item.getNoticeType())
|
|
|
+ .max(Comparator.comparing(LifeNotice::getCreatedTime))
|
|
|
+ .orElse(null);
|
|
|
+
|
|
|
+ // 3. 统计未读数量
|
|
|
+ long systemUnreadCount = lifeNoticeList.stream()
|
|
|
+ .filter(item -> 1 == item.getNoticeType() && item.getIsRead() == 0)
|
|
|
+ .count();
|
|
|
+
|
|
|
+ // 4. 构建返回结果
|
|
|
+ // ...
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**2. getNoticeList - 通知列表**
|
|
|
+
|
|
|
+```java
|
|
|
+@Override
|
|
|
+public IPage<LifeNoticeVo> getNoticeList(int pageNum, int pageSize,
|
|
|
+ String receiverId, int noticeType) {
|
|
|
+ // 1. 查询通知列表
|
|
|
+ List<LifeNotice> lifeNoticeList = lifeNoticeMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ // 2. 解析 senderId,分组为 store 和 user
|
|
|
+ Map<String, List<String>> senderIdMap = /* 分组逻辑 */;
|
|
|
+
|
|
|
+ // 3. 查询违规举报信息(用于平台类型判断)
|
|
|
+ List<LifeUserViolation> lifeUserViolationList = /* 查询逻辑 */;
|
|
|
+
|
|
|
+ // 4. 查询用户信息(商户和普通用户)
|
|
|
+ List<LifeMessageVo> userList = lifeMessageMapper
|
|
|
+ .getLifeUserAndStoreUserByPhone(storePhones, userPhones);
|
|
|
+
|
|
|
+ // 5. 组装 LifeNoticeVo,关联用户信息
|
|
|
+ // 6. 设置平台类型标识
|
|
|
+ // 7. 手动分页
|
|
|
+ // 8. 构建分页结果
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**3. markNoticeAsRead - 标记已读**
|
|
|
+
|
|
|
+```java
|
|
|
+@Override
|
|
|
+public int markNoticeAsRead(Integer id) {
|
|
|
+ // 使用 LambdaUpdateWrapper 更新 isRead 字段
|
|
|
+ LambdaUpdateWrapper<LifeNotice> wrapper = new LambdaUpdateWrapper<>();
|
|
|
+ wrapper.eq(LifeNotice::getId, id);
|
|
|
+ wrapper.set(LifeNotice::getIsRead, 1);
|
|
|
+
|
|
|
+ return lifeNoticeMapper.update(null, wrapper);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 依赖注入
|
|
|
+
|
|
|
+### Service 实现类
|
|
|
+
|
|
|
+```java
|
|
|
+private final LifeNoticeMapper lifeNoticeMapper; // 通知Mapper
|
|
|
+private final LifeMessageMapper lifeMessageMapper; // 消息Mapper(用于查询用户信息)
|
|
|
+private final LifeUserViolationMapper lifeUserViolationMapper; // 违规举报Mapper
|
|
|
+```
|
|
|
+
|
|
|
+### 导入依赖
|
|
|
+
|
|
|
+```java
|
|
|
+import cn.hutool.core.collection.CollectionUtil;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
+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 |
|
|
|
+
|
|
|
+### 接口三:标记已读
|
|
|
+
|
|
|
+| 项目 | 原接口 | 新接口 |
|
|
|
+|------|--------|--------|
|
|
|
+| 服务 | alien-store(app端) | alien-store-platform(web端) |
|
|
|
+| 路径 | /alienStore/notice/readNoticeById | /notice/markNoticeAsRead |
|
|
|
+| 方法名 | readNoticeById | markNoticeAsRead |
|
|
|
+| 请求方式 | GET | POST |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 数据表说明
|
|
|
+
|
|
|
+### 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、id 为空或无效
|
|
|
+2. **数据库异常**:查询或更新失败
|
|
|
+3. **分页参数错误**:pageNum < 1 或 pageSize < 1
|
|
|
+4. **通知不存在**:标记已读时通知ID不存在
|
|
|
+
|
|
|
+### 错误响应示例
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 500,
|
|
|
+ "success": false,
|
|
|
+ "data": null,
|
|
|
+ "msg": "查询失败:数据库连接异常"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 使用场景
|
|
|
+
|
|
|
+### 场景一:通知中心首页
|
|
|
+
|
|
|
+1. 调用 **getNoticeStatistics** 获取未读数量,显示通知角标
|
|
|
+2. 调用 **getNoticeList** 获取最新通知列表
|
|
|
+
|
|
|
+### 场景二:通知分类浏览
|
|
|
+
|
|
|
+根据不同 `noticeType` 查询不同类型的通知:
|
|
|
+- `noticeType=0`:其他通知(评论、关注等)
|
|
|
+- `noticeType=1`:系统通知
|
|
|
+- `noticeType=2`:订单提醒
|
|
|
+
|
|
|
+### 场景三:通知已读管理
|
|
|
+
|
|
|
+1. 用户点击某条通知
|
|
|
+2. 调用 **markNoticeAsRead** 标记为已读
|
|
|
+3. 重新调用 **getNoticeStatistics** 更新未读数量
|
|
|
+
|
|
|
+### 场景四:实时更新
|
|
|
+
|
|
|
+通过 WebSocket 或定时轮询:
|
|
|
+1. 定期调用 **getNoticeStatistics** 更新未读数量
|
|
|
+2. 有新通知时刷新 **getNoticeList**
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 测试建议
|
|
|
+
|
|
|
+### 接口一(通知统计)测试场景
|
|
|
+
|
|
|
+1. ✅ 有系统通知和订单提醒
|
|
|
+2. ✅ 只有系统通知
|
|
|
+3. ✅ 只有订单提醒
|
|
|
+4. ✅ 没有任何通知
|
|
|
+5. ✅ 未读数量统计准确性
|
|
|
+6. ✅ 最新通知时间排序正确性
|
|
|
+
|
|
|
+### 接口二(通知列表)测试场景
|
|
|
+
|
|
|
+1. ✅ 正常分页查询
|
|
|
+2. ✅ 空数据场景
|
|
|
+3. ✅ 不同 noticeType 查询
|
|
|
+4. ✅ 用户信息关联正确性
|
|
|
+5. ✅ 平台类型标识正确性
|
|
|
+6. ✅ 分页边界测试(第1页、最后1页、超出页数)
|
|
|
+7. ✅ 大数据量性能测试
|
|
|
+
|
|
|
+### 接口三(标记已读)测试场景
|
|
|
+
|
|
|
+1. ✅ 标记未读通知为已读
|
|
|
+2. ✅ 重复标记(应该仍然返回成功)
|
|
|
+3. ✅ 不存在的ID(返回失败)
|
|
|
+4. ✅ 已删除的通知(MyBatis-Plus 自动过滤)
|
|
|
+5. ✅ 并发标记测试
|
|
|
+
|
|
|
+### 测试SQL
|
|
|
+
|
|
|
+```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;
|
|
|
+
|
|
|
+-- 标记通知为已读
|
|
|
+UPDATE life_notice
|
|
|
+SET is_read = 1
|
|
|
+WHERE id = 2589
|
|
|
+ AND delete_flag = 0;
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 注意事项
|
|
|
+
|
|
|
+1. **receiverId 格式**:通常为 `store_` + 手机号(如:`store_18241052019`)
|
|
|
+2. **逻辑删除**:MyBatis-Plus 自动处理 `delete_flag` 字段
|
|
|
+3. **手动分页**:由于需要关联多表和复杂逻辑,采用手动分页而非数据库分页
|
|
|
+4. **性能优化**:
|
|
|
+ - 一次性查询所有通知,避免N+1问题
|
|
|
+ - 批量查询用户信息和违规举报信息
|
|
|
+ - 使用 Stream API 进行数据转换和过滤
|
|
|
+5. **空值处理**:通知统计接口返回字符串 `"null"` 而非 null 对象
|
|
|
+6. **日志记录**:详细记录查询参数、结果数量、操作结果,便于问题追踪
|
|
|
+7. **幂等性**:标记已读接口支持重复调用,不会产生副作用
|
|
|
+8. **安全性**:需要验证 receiverId 和 id 的合法性,防止越权访问
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 接口调用流程示例
|
|
|
+
|
|
|
+### 完整业务流程
|
|
|
+
|
|
|
+```
|
|
|
+用户进入通知中心
|
|
|
+ ↓
|
|
|
+① 调用 getNoticeStatistics
|
|
|
+ ↓
|
|
|
+显示:系统通知(5)、订单提醒(3)
|
|
|
+ ↓
|
|
|
+② 调用 getNoticeList(noticeType=1)
|
|
|
+ ↓
|
|
|
+显示系统通知列表(分页)
|
|
|
+ ↓
|
|
|
+用户点击某条通知
|
|
|
+ ↓
|
|
|
+③ 调用 markNoticeAsRead(id=2589)
|
|
|
+ ↓
|
|
|
+标记成功后,再次调用 ① 更新未读数量
|
|
|
+ ↓
|
|
|
+显示:系统通知(4)、订单提醒(3)
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 更新日志
|
|
|
+
|
|
|
+### 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 实现层:实现复杂的通知列表查询逻辑
|
|
|
+- ✅ 依赖注入:添加 `LifeMessageMapper` 和 `LifeUserViolationMapper`
|
|
|
+- ✅ 用户信息关联:实现商户和普通用户信息查询
|
|
|
+- ✅ 平台类型判断:实现复杂的平台类型标识逻辑
|
|
|
+- ✅ 手动分页:实现内存分页功能
|
|
|
+- ✅ 业务逻辑:完全复用原接口逻辑
|
|
|
+
|
|
|
+**接口三(标记已读)**:
|
|
|
+- ✅ 完成接口迁移:从 alien-store 迁移到 alien-store-platform
|
|
|
+- ✅ Controller 层:添加 `markNoticeAsRead` 接口
|
|
|
+- ✅ Service 层:添加 `markNoticeAsRead` 方法定义
|
|
|
+- ✅ Service 实现层:使用 LambdaUpdateWrapper 实现更新逻辑
|
|
|
+- ✅ 请求方式:改为 POST,更符合 REST 规范
|
|
|
+- ✅ 命名优化:`readNoticeById` → `markNoticeAsRead`(更语义化)
|
|
|
+- ✅ 业务逻辑:完全复用原接口逻辑
|
|
|
+- ✅ Linter 检查:无错误
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 开发者信息
|
|
|
+
|
|
|
+- **迁移时间**:2025-11-12
|
|
|
+- **原服务**:alien-store(app端商户)
|
|
|
+- **目标服务**:alien-store-platform(web端商户)
|
|
|
+- **技术栈**:Spring Boot + MyBatis-Plus + FastJSON + Hutool + Java 8
|
|
|
+- **开发人员**:ssk
|
|
|
+- **文档版本**:v1.0
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 附录:常见问题
|
|
|
+
|
|
|
+### Q1:为什么通知列表使用手动分页?
|
|
|
+
|
|
|
+**A**:因为需要关联多个表(用户信息、违规举报信息)并进行复杂的数据处理(平台类型判断),如果使用数据库分页,SQL会非常复杂且难以维护。手动分页虽然会查询全部数据,但在通知数量不是特别大的情况下(通常每个商户的通知数量有限),性能是可以接受的。
|
|
|
+
|
|
|
+### Q2:为什么通知统计返回字符串 "null" 而不是 null?
|
|
|
+
|
|
|
+**A**:这是为了保持与原接口的兼容性。原接口就是这样设计的,可能是为了避免前端处理 null 值的麻烦。
|
|
|
+
|
|
|
+### Q3:如何批量标记多个通知为已读?
|
|
|
+
|
|
|
+**A**:当前接口只支持单个通知标记。如需批量标记,可以扩展接口,接收 ID 数组参数,循环调用更新逻辑。
|
|
|
+
|
|
|
+### Q4:通知的删除和标记已读有什么区别?
|
|
|
+
|
|
|
+**A**:
|
|
|
+- **标记已读**:`is_read = 1`,通知仍然存在,只是标记为已读状态
|
|
|
+- **删除通知**:`delete_flag = 1`,逻辑删除,通知不再显示在列表中
|
|
|
+
|
|
|
+### Q5:如何保证通知的实时性?
|
|
|
+
|
|
|
+**A**:建议结合以下方案:
|
|
|
+1. 后端:使用 WebSocket 推送新通知
|
|
|
+2. 前端:定时轮询通知统计接口(如每30秒)
|
|
|
+3. 缓存:使用 Redis 缓存未读数量,减少数据库压力
|