feat(站内通知): 铃铛支持按类型大类筛选(系统异常/任务异常/配置异常/系统通知)

原来铃铛只能按关键字和日期筛,任务失败、密钥欠费、麦象异常全混在一起,
用户没法只看自己关心的那类。

后端 NotificationService 增加 scene→大类映射(未知 scene 归「系统通知」兜底),
列表接口新增 category 参数,落到 SQL 是 scene IN (...);「系统通知」作兜底类
还要纳入未登记的 scene,故表达为「= system 或 不在其它三类里」。列表项返回
category,前端不必各自维护一份 scene 映射。前后端两个通知接口同步加参数。

前端铃铛筛选区新增一行类型 chip(全部 + 四类),与关键字、日期一起参与
hasFilter 与重置;空分类不下发,避免后端查询落空。
This commit is contained in:
2026-09-16 10:32:11 +08:00
parent f566573fce
commit aea0e16279
11 changed files with 318 additions and 29 deletions
+26 -4
View File
@@ -8,7 +8,10 @@ import {
readLastNotifiedId,
writeLastNotifiedId,
} from '../src/shared/utils/notification-bell.ts'
import { buildNotificationListQuery } from '../src/shared/api/types/modules/notification.ts'
import {
NOTIFICATION_CATEGORY_OPTIONS,
buildNotificationListQuery,
} from '../src/shared/api/types/modules/notification.ts'
function createStorage() {
const store = new Map<string, string>()
@@ -133,13 +136,14 @@ test('groupNotificationsByDay 时间缺失/非法归入未知时间组', () => {
)
})
test('buildNotificationListQuery:关键字去空白、空条件不下发、日期原样透传', () => {
test('buildNotificationListQuery:关键字去空白、空条件不下发、分类与日期原样透传', () => {
assert.deepEqual(buildNotificationListQuery(), { page: 1, pageSize: 20, onlyUnread: 'false' })
const query = buildNotificationListQuery({
page: 2,
pageSize: 10,
keyword: ' 余额不足 ',
category: 'config_error',
startDate: '2026-09-10',
endDate: '2026-09-13',
})
@@ -148,13 +152,31 @@ test('buildNotificationListQuery:关键字去空白、空条件不下发、日
pageSize: 10,
onlyUnread: 'false',
keyword: '余额不足',
category: 'config_error',
startDate: '2026-09-10',
endDate: '2026-09-13',
})
// 面板清空筛选后不能带空串日期(后端日期绑定会 400)
const cleared = buildNotificationListQuery({ keyword: ' ', startDate: '', endDate: '' })
// 面板清空筛选后不能带空串日期(后端日期绑定会 400),空分类同样不下发
const cleared = buildNotificationListQuery({
keyword: ' ',
category: ' ',
startDate: '',
endDate: '',
})
assert.equal('keyword' in cleared, false)
assert.equal('category' in cleared, false)
assert.equal('startDate' in cleared, false)
assert.equal('endDate' in cleared, false)
})
/** 类型筛选项必须与后端四个大类逐一对应,漏一个用户就永远筛不到那类通知。 */
test('类型筛选项覆盖后端四个大类且标签非空', () => {
assert.deepEqual(
NOTIFICATION_CATEGORY_OPTIONS.map((option) => option.value),
['system_error', 'task_error', 'config_error', 'system_notice'],
)
for (const option of NOTIFICATION_CATEGORY_OPTIONS) {
assert.ok(option.label.trim().length > 0, `分类 ${option.value} 缺中文标签`)
}
})