feat(站内通知): 铃铛支持按类型大类筛选(系统异常/任务异常/配置异常/系统通知)
原来铃铛只能按关键字和日期筛,任务失败、密钥欠费、麦象异常全混在一起, 用户没法只看自己关心的那类。 后端 NotificationService 增加 scene→大类映射(未知 scene 归「系统通知」兜底), 列表接口新增 category 参数,落到 SQL 是 scene IN (...);「系统通知」作兜底类 还要纳入未登记的 scene,故表达为「= system 或 不在其它三类里」。列表项返回 category,前端不必各自维护一份 scene 映射。前后端两个通知接口同步加参数。 前端铃铛筛选区新增一行类型 chip(全部 + 四类),与关键字、日期一起参与 hasFilter 与重置;空分类不下发,避免后端查询落空。
This commit is contained in:
+6
-2
@@ -44,20 +44,24 @@ public class AdminNotificationController {
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "通知分页列表",
|
||||
description = "onlyUnread=true 时只返回未读;keyword 模糊匹配标题/内容;startDate/endDate 为年月日闭区间。")
|
||||
description = "onlyUnread=true 时只返回未读;keyword 模糊匹配标题/内容;"
|
||||
+ "category 按类型大类筛选(system_error/task_error/config_error/system_notice);"
|
||||
+ "startDate/endDate 为年月日闭区间。")
|
||||
public ApiResponse<NotificationPageVo> page(
|
||||
HttpServletRequest request,
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Long page,
|
||||
@Parameter(description = "每页数量") @RequestParam(defaultValue = "20") Long pageSize,
|
||||
@Parameter(description = "只看未读") @RequestParam(required = false, defaultValue = "false") Boolean onlyUnread,
|
||||
@Parameter(description = "关键字(标题/内容)") @RequestParam(required = false) String keyword,
|
||||
@Parameter(description = "类型大类:system_error/task_error/config_error/system_notice")
|
||||
@RequestParam(required = false) String category,
|
||||
@Parameter(description = "起始日期(yyyy-MM-dd,含)")
|
||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
@Parameter(description = "结束日期(yyyy-MM-dd,含)")
|
||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
||||
Long userId = currentAdminId(request);
|
||||
return ApiResponse.success(notificationService.page(userId, NotificationService.AUDIENCE_ADMIN,
|
||||
NotificationPageQuery.of(page, pageSize, onlyUnread, keyword, startDate, endDate)));
|
||||
NotificationPageQuery.of(page, pageSize, onlyUnread, keyword, category, startDate, endDate)));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/read")
|
||||
|
||||
+6
-2
@@ -44,20 +44,24 @@ public class NotificationController {
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "通知分页列表",
|
||||
description = "onlyUnread=true 时只返回未读;keyword 模糊匹配标题/内容;startDate/endDate 为年月日闭区间。")
|
||||
description = "onlyUnread=true 时只返回未读;keyword 模糊匹配标题/内容;"
|
||||
+ "category 按类型大类筛选(system_error/task_error/config_error/system_notice);"
|
||||
+ "startDate/endDate 为年月日闭区间。")
|
||||
public ApiResponse<NotificationPageVo> page(
|
||||
HttpServletRequest request,
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Long page,
|
||||
@Parameter(description = "每页数量") @RequestParam(defaultValue = "20") Long pageSize,
|
||||
@Parameter(description = "只看未读") @RequestParam(required = false, defaultValue = "false") Boolean onlyUnread,
|
||||
@Parameter(description = "关键字(标题/内容)") @RequestParam(required = false) String keyword,
|
||||
@Parameter(description = "类型大类:system_error/task_error/config_error/system_notice")
|
||||
@RequestParam(required = false) String category,
|
||||
@Parameter(description = "起始日期(yyyy-MM-dd,含)")
|
||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
@Parameter(description = "结束日期(yyyy-MM-dd,含)")
|
||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
||||
Long userId = currentUserId(request);
|
||||
return ApiResponse.success(notificationService.page(userId, NotificationService.AUDIENCE_USER,
|
||||
NotificationPageQuery.of(page, pageSize, onlyUnread, keyword, startDate, endDate)));
|
||||
NotificationPageQuery.of(page, pageSize, onlyUnread, keyword, category, startDate, endDate)));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/read")
|
||||
|
||||
+7
-3
@@ -5,8 +5,9 @@ import lombok.Data;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 通知列表查询条件:分页 + 未读过滤 + 关键字(标题/内容模糊)+ 创建日期区间(年月日闭区间)。
|
||||
* 铃铛面板的「按天搜索」与「内容搜索」都走这里,空值表示不限制。
|
||||
* 通知列表查询条件:分页 + 未读过滤 + 关键字(标题/内容模糊)+ 创建日期区间(年月日闭区间)
|
||||
* + 类型大类(system_error/task_error/config_error/system_notice)。
|
||||
* 铃铛面板的「按天搜索」「内容搜索」「类型筛选」都走这里,空值表示不限制。
|
||||
*/
|
||||
@Data
|
||||
public class NotificationPageQuery {
|
||||
@@ -15,16 +16,19 @@ public class NotificationPageQuery {
|
||||
private Long pageSize;
|
||||
private Boolean onlyUnread;
|
||||
private String keyword;
|
||||
private String category;
|
||||
private LocalDate startDate;
|
||||
private LocalDate endDate;
|
||||
|
||||
public static NotificationPageQuery of(Long page, Long pageSize, Boolean onlyUnread,
|
||||
String keyword, LocalDate startDate, LocalDate endDate) {
|
||||
String keyword, String category,
|
||||
LocalDate startDate, LocalDate endDate) {
|
||||
NotificationPageQuery query = new NotificationPageQuery();
|
||||
query.setPage(page);
|
||||
query.setPageSize(pageSize);
|
||||
query.setOnlyUnread(onlyUnread);
|
||||
query.setKeyword(keyword);
|
||||
query.setCategory(category);
|
||||
query.setStartDate(startDate);
|
||||
query.setEndDate(endDate);
|
||||
return query;
|
||||
|
||||
+2
@@ -11,6 +11,8 @@ public class NotificationItemVo {
|
||||
private Long id;
|
||||
/** 场景:secret_balance/secret_invalid/task_failed/service_down/system */
|
||||
private String scene;
|
||||
/** 类型大类(铃铛筛选用):system_error/task_error/config_error/system_notice */
|
||||
private String category;
|
||||
/** 级别:info/warning/error */
|
||||
private String level;
|
||||
private String title;
|
||||
|
||||
+75
-7
@@ -15,6 +15,7 @@ import org.springframework.stereotype.Service;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 站内通知存取:桌面端用户(audience=user)与后台管理员(audience=admin)共用一张表,
|
||||
@@ -40,6 +41,27 @@ public class NotificationService {
|
||||
public static final String SCENE_MAIXIANG_ANOMALY = "maixiang_anomaly";
|
||||
public static final String SCENE_SYSTEM = "system";
|
||||
|
||||
/**
|
||||
* 通知大类:scene 太细(技术语义),铃铛按用户看得懂的四类归并筛选。
|
||||
* null/空分类表示不筛选,见 {@link #scenesOfCategory}。
|
||||
*/
|
||||
public static final String CATEGORY_SYSTEM_ERROR = "system_error";
|
||||
public static final String CATEGORY_TASK_ERROR = "task_error";
|
||||
public static final String CATEGORY_CONFIG_ERROR = "config_error";
|
||||
public static final String CATEGORY_SYSTEM_NOTICE = "system_notice";
|
||||
|
||||
/**
|
||||
* scene → 大类。未知 scene(历史遗留或新增未登记)归入「系统通知」,
|
||||
* 保证任一通知只属于一类、筛选项互斥且完备。
|
||||
*/
|
||||
private static final Map<String, String> SCENE_TO_CATEGORY = Map.of(
|
||||
SCENE_SERVICE_DOWN, CATEGORY_SYSTEM_ERROR,
|
||||
SCENE_MAIXIANG_ANOMALY, CATEGORY_SYSTEM_ERROR,
|
||||
SCENE_TASK_FAILED, CATEGORY_TASK_ERROR,
|
||||
SCENE_SECRET_BALANCE, CATEGORY_CONFIG_ERROR,
|
||||
SCENE_SECRET_INVALID, CATEGORY_CONFIG_ERROR,
|
||||
SCENE_SYSTEM, CATEGORY_SYSTEM_NOTICE);
|
||||
|
||||
private static final long MAX_PAGE_SIZE = 100L;
|
||||
private static final int TITLE_MAX_LENGTH = 128;
|
||||
private static final int CONTENT_MAX_LENGTH = 512;
|
||||
@@ -143,12 +165,35 @@ public class NotificationService {
|
||||
vo.setPage(safePage);
|
||||
vo.setPageSize(safeSize);
|
||||
vo.setUnreadCount(unreadCount(userId, audience));
|
||||
log.info("[notification] 列表查询 userId={} audience={} page={} size={} onlyUnread={} keyword={} 起={} 止={} 命中={}",
|
||||
userId, audience, safePage, safeSize, onlyUnread, normalize(safe.getKeyword()),
|
||||
safe.getStartDate(), safe.getEndDate(), total);
|
||||
log.info("[notification] 列表查询 userId={} audience={} page={} size={} onlyUnread={} category={} keyword={} 起={} 止={} 命中={}",
|
||||
userId, audience, safePage, safeSize, onlyUnread, normalize(safe.getCategory()),
|
||||
normalize(safe.getKeyword()), safe.getStartDate(), safe.getEndDate(), total);
|
||||
return vo;
|
||||
}
|
||||
|
||||
/** scene → 用户可见大类;未知 scene(历史遗留/新增未登记)一律归「系统通知」。 */
|
||||
public static String categoryOf(String scene) {
|
||||
return SCENE_TO_CATEGORY.getOrDefault(normalize(scene), CATEGORY_SYSTEM_NOTICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 大类对应的 scene 集合,供查询侧做 scene IN (...) 过滤。
|
||||
* 空值或未知大类返回空列表,调用方据此跳过该筛选(不报错,避免前端传错值就查不到数据)。
|
||||
*/
|
||||
public static List<String> scenesOfCategory(String category) {
|
||||
String wanted = normalize(category);
|
||||
if (wanted.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<String> scenes = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : SCENE_TO_CATEGORY.entrySet()) {
|
||||
if (entry.getValue().equals(wanted)) {
|
||||
scenes.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
return scenes;
|
||||
}
|
||||
|
||||
/** 摘要:未读数 + 最新通知 id(前端轮询判断是否有新通知)。 */
|
||||
public NotificationSummaryVo summary(Long userId, String audience) {
|
||||
NotificationSummaryVo vo = new NotificationSummaryVo();
|
||||
@@ -242,9 +287,11 @@ public class NotificationService {
|
||||
|
||||
/**
|
||||
* 列表筛选:关键字模糊匹配标题/内容;日期按「年月日」闭区间
|
||||
* (起日 00:00 起含、止日次日 00:00 前不含,避免当天 23:59 漏行)。
|
||||
* (起日 00:00 起含、止日次日 00:00 前不含,避免当天 23:59 漏行);
|
||||
* 类型按大类映射成 scene 集合过滤。
|
||||
*/
|
||||
private void applyFilters(LambdaQueryWrapper<UserNotificationEntity> wrapper, NotificationPageQuery query) {
|
||||
applyCategoryFilter(wrapper, query.getCategory());
|
||||
String keyword = normalize(query.getKeyword());
|
||||
if (!keyword.isEmpty()) {
|
||||
wrapper.and(nested -> nested.like(UserNotificationEntity::getTitle, keyword)
|
||||
@@ -258,8 +305,28 @@ public class NotificationService {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean existsByDedupeKey(String dedupeKey) {
|
||||
return selectByDedupeKey(dedupeKey) != null;
|
||||
/**
|
||||
* 类型筛选:「系统通知」是兜底类,除 scene=system 外还要包含所有未登记 scene
|
||||
* (否则直接改库塞入的 scene 在筛选时会凭空消失),故表达为「= system 或 不在其它三类里」;
|
||||
* 未知分类一律不追加条件,避免前端传错值就查不到任何数据。
|
||||
*/
|
||||
private void applyCategoryFilter(LambdaQueryWrapper<UserNotificationEntity> wrapper, String category) {
|
||||
String wanted = normalize(category);
|
||||
List<String> scenes = wanted.isEmpty() ? List.of() : scenesOfCategory(wanted);
|
||||
if (scenes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (CATEGORY_SYSTEM_NOTICE.equals(wanted)) {
|
||||
List<String> classified = new ArrayList<>(SCENE_TO_CATEGORY.keySet());
|
||||
classified.remove(SCENE_SYSTEM);
|
||||
wrapper.and(nested -> nested.eq(UserNotificationEntity::getScene, SCENE_SYSTEM)
|
||||
.or().notIn(UserNotificationEntity::getScene, classified));
|
||||
return;
|
||||
}
|
||||
wrapper.in(UserNotificationEntity::getScene, scenes);
|
||||
}
|
||||
|
||||
private boolean existsByDedupeKey(String dedupeKey) { return selectByDedupeKey(dedupeKey) != null;
|
||||
}
|
||||
|
||||
private UserNotificationEntity selectByDedupeKey(String dedupeKey) {
|
||||
@@ -273,6 +340,7 @@ public class NotificationService {
|
||||
NotificationItemVo vo = new NotificationItemVo();
|
||||
vo.setId(row.getId());
|
||||
vo.setScene(row.getScene());
|
||||
vo.setCategory(categoryOf(row.getScene()));
|
||||
vo.setLevel(row.getLevel());
|
||||
vo.setTitle(row.getTitle());
|
||||
vo.setContent(row.getContent());
|
||||
@@ -282,7 +350,7 @@ public class NotificationService {
|
||||
return vo;
|
||||
}
|
||||
|
||||
private String normalize(String value) {
|
||||
private static String normalize(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -34,7 +34,8 @@ class AdminNotificationControllerTest {
|
||||
when(adminAuthSupport.requireAdmin(request)).thenReturn(admin(24L));
|
||||
|
||||
controller.summary(request);
|
||||
controller.page(request, 1L, 20L, false, "余额", LocalDate.of(2026, 9, 10), LocalDate.of(2026, 9, 13));
|
||||
controller.page(request, 1L, 20L, false, "余额", NotificationService.CATEGORY_CONFIG_ERROR,
|
||||
LocalDate.of(2026, 9, 10), LocalDate.of(2026, 9, 13));
|
||||
|
||||
verify(notificationService).summary(24L, NotificationService.AUDIENCE_ADMIN);
|
||||
ArgumentCaptor<NotificationPageQuery> queryCaptor = ArgumentCaptor.forClass(NotificationPageQuery.class);
|
||||
@@ -44,6 +45,7 @@ class AdminNotificationControllerTest {
|
||||
assertThat(query.getPageSize()).isEqualTo(20L);
|
||||
assertThat(query.getOnlyUnread()).isFalse();
|
||||
assertThat(query.getKeyword()).isEqualTo("余额");
|
||||
assertThat(query.getCategory()).isEqualTo(NotificationService.CATEGORY_CONFIG_ERROR);
|
||||
assertThat(query.getStartDate()).isEqualTo(LocalDate.of(2026, 9, 10));
|
||||
assertThat(query.getEndDate()).isEqualTo(LocalDate.of(2026, 9, 13));
|
||||
}
|
||||
|
||||
+3
-1
@@ -33,7 +33,8 @@ class NotificationControllerTest {
|
||||
when(adminAuthSupport.requireUser(request)).thenReturn(user(1095L));
|
||||
|
||||
controller.summary(request);
|
||||
controller.page(request, 2L, 20L, true, "欠费", LocalDate.of(2026, 9, 1), LocalDate.of(2026, 9, 13));
|
||||
controller.page(request, 2L, 20L, true, "欠费", NotificationService.CATEGORY_TASK_ERROR,
|
||||
LocalDate.of(2026, 9, 1), LocalDate.of(2026, 9, 13));
|
||||
|
||||
verify(notificationService).summary(1095L, NotificationService.AUDIENCE_USER);
|
||||
ArgumentCaptor<NotificationPageQuery> queryCaptor = ArgumentCaptor.forClass(NotificationPageQuery.class);
|
||||
@@ -43,6 +44,7 @@ class NotificationControllerTest {
|
||||
assertThat(query.getPageSize()).isEqualTo(20L);
|
||||
assertThat(query.getOnlyUnread()).isTrue();
|
||||
assertThat(query.getKeyword()).isEqualTo("欠费");
|
||||
assertThat(query.getCategory()).isEqualTo(NotificationService.CATEGORY_TASK_ERROR);
|
||||
assertThat(query.getStartDate()).isEqualTo(LocalDate.of(2026, 9, 1));
|
||||
assertThat(query.getEndDate()).isEqualTo(LocalDate.of(2026, 9, 13));
|
||||
}
|
||||
|
||||
+85
-4
@@ -133,7 +133,7 @@ class NotificationServiceTest {
|
||||
when(mapper.selectList(any())).thenReturn(List.of(first, second));
|
||||
|
||||
NotificationPageVo page = service.page(7L, NotificationService.AUDIENCE_USER,
|
||||
NotificationPageQuery.of(1L, 20L, false, null, null, null));
|
||||
NotificationPageQuery.of(1L, 20L, false, null, null, null, null));
|
||||
|
||||
assertThat(page.getItems()).hasSize(2);
|
||||
assertThat(page.getItems().get(0).getRead()).isTrue();
|
||||
@@ -149,7 +149,7 @@ class NotificationServiceTest {
|
||||
when(mapper.selectCount(any())).thenReturn(0L, 0L);
|
||||
|
||||
NotificationPageVo page = service.page(7L, NotificationService.AUDIENCE_ADMIN,
|
||||
NotificationPageQuery.of(0L, 500L, true, null, null, null));
|
||||
NotificationPageQuery.of(0L, 500L, true, null, null, null, null));
|
||||
|
||||
assertThat(page.getItems()).isEmpty();
|
||||
assertThat(page.getPage()).isEqualTo(1L);
|
||||
@@ -204,7 +204,7 @@ class NotificationServiceTest {
|
||||
when(mapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
service.page(24L, NotificationService.AUDIENCE_ADMIN,
|
||||
NotificationPageQuery.of(1L, 20L, true, null, null, null));
|
||||
NotificationPageQuery.of(1L, 20L, true, null, null, null, null));
|
||||
|
||||
ArgumentCaptor<LambdaQueryWrapper<UserNotificationEntity>> countCaptor =
|
||||
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
||||
@@ -224,7 +224,7 @@ class NotificationServiceTest {
|
||||
when(mapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
service.page(24L, NotificationService.AUDIENCE_ADMIN, NotificationPageQuery.of(
|
||||
1L, 20L, false, "余额不足", LocalDate.of(2026, 9, 10), LocalDate.of(2026, 9, 13)));
|
||||
1L, 20L, false, "余额不足", null, LocalDate.of(2026, 9, 10), LocalDate.of(2026, 9, 13)));
|
||||
|
||||
ArgumentCaptor<LambdaQueryWrapper<UserNotificationEntity>> listCaptor =
|
||||
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
||||
@@ -235,6 +235,87 @@ class NotificationServiceTest {
|
||||
.contains("%余额不足%", LocalDateTime.of(2026, 9, 10, 0, 0), LocalDateTime.of(2026, 9, 14, 0, 0));
|
||||
}
|
||||
|
||||
/** 类型大类映射:scene 归并成四类,未知 scene 兜底「系统通知」,保证筛选互斥且完备。 */
|
||||
@Test
|
||||
void categoryOfMapsScenesToUserVisibleGroups() {
|
||||
assertThat(NotificationService.categoryOf(NotificationService.SCENE_TASK_FAILED))
|
||||
.isEqualTo(NotificationService.CATEGORY_TASK_ERROR);
|
||||
assertThat(NotificationService.categoryOf(NotificationService.SCENE_SERVICE_DOWN))
|
||||
.isEqualTo(NotificationService.CATEGORY_SYSTEM_ERROR);
|
||||
assertThat(NotificationService.categoryOf(NotificationService.SCENE_MAIXIANG_ANOMALY))
|
||||
.isEqualTo(NotificationService.CATEGORY_SYSTEM_ERROR);
|
||||
assertThat(NotificationService.categoryOf(NotificationService.SCENE_SECRET_BALANCE))
|
||||
.isEqualTo(NotificationService.CATEGORY_CONFIG_ERROR);
|
||||
assertThat(NotificationService.categoryOf(NotificationService.SCENE_SECRET_INVALID))
|
||||
.isEqualTo(NotificationService.CATEGORY_CONFIG_ERROR);
|
||||
assertThat(NotificationService.categoryOf(NotificationService.SCENE_SYSTEM))
|
||||
.isEqualTo(NotificationService.CATEGORY_SYSTEM_NOTICE);
|
||||
// 历史遗留/新增未登记的 scene 不能被筛丢
|
||||
assertThat(NotificationService.categoryOf("legacy_scene"))
|
||||
.isEqualTo(NotificationService.CATEGORY_SYSTEM_NOTICE);
|
||||
assertThat(NotificationService.categoryOf(null))
|
||||
.isEqualTo(NotificationService.CATEGORY_SYSTEM_NOTICE);
|
||||
}
|
||||
|
||||
/** 类型筛选落到 SQL 上是 scene IN (...);空/未知分类不追加条件,避免前端传错值就查不到数据。 */
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void pageAppliesCategoryAsSceneInFilter() {
|
||||
when(mapper.selectCount(any())).thenReturn(1L, 0L);
|
||||
when(mapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
service.page(24L, NotificationService.AUDIENCE_USER, NotificationPageQuery.of(
|
||||
1L, 20L, false, null, NotificationService.CATEGORY_SYSTEM_ERROR, null, null));
|
||||
|
||||
ArgumentCaptor<LambdaQueryWrapper<UserNotificationEntity>> listCaptor =
|
||||
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
||||
verify(mapper).selectList(listCaptor.capture());
|
||||
LambdaQueryWrapper<UserNotificationEntity> wrapper = listCaptor.getValue();
|
||||
assertThat(wrapper.getSqlSegment()).contains("scene");
|
||||
assertThat(wrapper.getParamNameValuePairs().values())
|
||||
.contains(NotificationService.SCENE_SERVICE_DOWN, NotificationService.SCENE_MAIXIANG_ANOMALY);
|
||||
|
||||
assertThat(NotificationService.scenesOfCategory(null)).isEmpty();
|
||||
assertThat(NotificationService.scenesOfCategory("")).isEmpty();
|
||||
assertThat(NotificationService.scenesOfCategory("not_a_category")).isEmpty();
|
||||
}
|
||||
|
||||
/** 「系统通知」是兜底类:除 scene=system 外还要纳入未登记 scene,否则那些通知筛选时会消失。 */
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void pageTreatsSystemNoticeCategoryAsCatchAll() {
|
||||
when(mapper.selectCount(any())).thenReturn(1L, 0L);
|
||||
when(mapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
service.page(24L, NotificationService.AUDIENCE_USER, NotificationPageQuery.of(
|
||||
1L, 20L, false, null, NotificationService.CATEGORY_SYSTEM_NOTICE, null, null));
|
||||
|
||||
ArgumentCaptor<LambdaQueryWrapper<UserNotificationEntity>> listCaptor =
|
||||
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
||||
verify(mapper).selectList(listCaptor.capture());
|
||||
LambdaQueryWrapper<UserNotificationEntity> wrapper = listCaptor.getValue();
|
||||
assertThat(wrapper.getSqlSegment()).contains("scene");
|
||||
assertThat(wrapper.getParamNameValuePairs().values())
|
||||
.contains(NotificationService.SCENE_SYSTEM, NotificationService.SCENE_TASK_FAILED);
|
||||
}
|
||||
|
||||
/** 列表项必须带类型大类,前端据此展示与筛选,不必各自维护一份 scene 映射。 */
|
||||
@Test
|
||||
void pageItemsCarryCategory() {
|
||||
UserNotificationEntity row = new UserNotificationEntity();
|
||||
row.setId(9L);
|
||||
row.setScene(NotificationService.SCENE_SECRET_BALANCE);
|
||||
when(mapper.selectCount(any())).thenReturn(1L, 0L);
|
||||
when(mapper.selectList(any())).thenReturn(List.of(row));
|
||||
|
||||
NotificationPageVo page = service.page(7L, NotificationService.AUDIENCE_USER,
|
||||
NotificationPageQuery.of(1L, 20L, false, null, null, null, null));
|
||||
|
||||
assertThat(page.getItems()).hasSize(1);
|
||||
assertThat(page.getItems().get(0).getCategory())
|
||||
.isEqualTo(NotificationService.CATEGORY_CONFIG_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void summaryAndUnreadCountScopeByReceiverAndAudience() {
|
||||
|
||||
Reference in New Issue
Block a user