商户通知管理模块提供通知统计、通知列表查询、通知已读标记等功能,支持系统通知、订单提醒等多种通知类型。
GET /notice/getNoticeStatistics| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| 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": "查询成功"
}
{
"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| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| 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¬iceType=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 | 总页数 |
POST /notice/markNoticeAsRead| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Integer | 是 | 通知ID |
POST /notice/markNoticeAsRead?id=2589
{
"code": 200,
"success": true,
"data": null,
"msg": "标记已读成功"
}
{
"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":其他平台判断规则:
businessId 为空 → 默认平台reportContextType 为 1、2、3(商户、用户、动态) → 默认平台通知中的 senderId 格式:
"system""store_手机号"(如:store_18241052019)"user_手机号"(如:user_13800138000)系统会自动查询并关联发送人的 userName 和 userImage。
文件路径:alien-store-platform/src/main/java/shop/alien/storeplatform/controller/NoticeController.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) {
// 标记通知为已读
}
}
文件路径:alien-store-platform/src/main/java/shop/alien/storeplatform/service/NoticeService.java
public interface NoticeService {
/**
* 获取系统通知和订单提醒统计
*/
JSONObject getNoticeStatistics(String receiverId);
/**
* 获取通知列表
*/
IPage<LifeNoticeVo> getNoticeList(int pageNum, int pageSize,
String receiverId, int noticeType);
/**
* 标记通知为已读
*/
int markNoticeAsRead(Integer id);
}
文件路径:alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/NoticeServiceImpl.java
1. getNoticeStatistics - 通知统计
@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 - 通知列表
@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 - 标记已读
@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);
}
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.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 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| 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 | 创建时间 |
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | Integer | 主键ID |
| report_context_type | String | 举报内容分类(0-商户,1-用户,2-动态,3-评论,4-二手商品,5-二手用户) |
| violation_type | String | 违规类型 |
| processing_status | String | 处理状态(0-未处理,1-违规,2-未违规) |
{
"code": 500,
"success": false,
"data": null,
"msg": "查询失败:数据库连接异常"
}
根据不同 noticeType 查询不同类型的通知:
noticeType=0:其他通知(评论、关注等)noticeType=1:系统通知noticeType=2:订单提醒通过 WebSocket 或定时轮询:
-- 查询商户的通知统计
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;
store_ + 手机号(如:store_18241052019)delete_flag 字段"null" 而非 null 对象用户进入通知中心
↓
① 调用 getNoticeStatistics
↓
显示:系统通知(5)、订单提醒(3)
↓
② 调用 getNoticeList(noticeType=1)
↓
显示系统通知列表(分页)
↓
用户点击某条通知
↓
③ 调用 markNoticeAsRead(id=2589)
↓
标记成功后,再次调用 ① 更新未读数量
↓
显示:系统通知(4)、订单提醒(3)
接口一(通知统计):
NoticeController,添加 getNoticeStatistics 接口NoticeService,添加方法定义NoticeServiceImpl,实现统计逻辑接口二(通知列表):
getNoticeList 接口getNoticeList 方法定义LifeMessageMapper 和 LifeUserViolationMapper接口三(标记已读):
markNoticeAsRead 接口markNoticeAsRead 方法定义readNoticeById → markNoticeAsRead(更语义化)A:因为需要关联多个表(用户信息、违规举报信息)并进行复杂的数据处理(平台类型判断),如果使用数据库分页,SQL会非常复杂且难以维护。手动分页虽然会查询全部数据,但在通知数量不是特别大的情况下(通常每个商户的通知数量有限),性能是可以接受的。
A:这是为了保持与原接口的兼容性。原接口就是这样设计的,可能是为了避免前端处理 null 值的麻烦。
A:当前接口只支持单个通知标记。如需批量标记,可以扩展接口,接收 ID 数组参数,循环调用更新逻辑。
A:
is_read = 1,通知仍然存在,只是标记为已读状态delete_flag = 1,逻辑删除,通知不再显示在列表中A:建议结合以下方案: