feat(push): implement system notification and push framework

This commit is contained in:
ryan
2026-06-14 16:52:06 +08:00
parent aee457093d
commit cb018b3b60
60 changed files with 8756 additions and 160 deletions
+7
View File
@@ -26,6 +26,7 @@ import {UserService} from './user';
import {ConfigService} from './config';
import {UploadService} from './upload';
import {DbManageService} from './db-manage';
import {PushService} from './push';
/**
* 服务对象
@@ -47,6 +48,8 @@ const services = {
upload: UploadService,
/** 数据库管理服务 */
dbManage: DbManageService,
/** 通知推送服务 */
push: PushService,
} as const;
export default services;
@@ -131,3 +134,7 @@ export type { UploadImageResponse } from './upload';
// 数据库管理服务
export { DbManageService } from './db-manage';
export type { DBOverview, TableDataResponse, ExecuteSQLResponse } from './db-manage';
// 通知推送服务
export { PushService } from './push';
export type { PushEvent, PushHistory, PushChannelConfig, ListPushHistoriesRequest, ListPushHistoriesResponse, UpdatePushEventRequest, TestPushRequest } from './push';
+5
View File
@@ -0,0 +1,5 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
export * from './types';
export * from './push.service';
+123
View File
@@ -0,0 +1,123 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
import {BaseService} from '@/lib/services';
import type {
ChannelDefinition,
CreateChannelRequest,
CreatePushEventRequest,
EventMetadata,
ListPushHistoriesRequest,
ListPushHistoriesResponse,
PushChannel,
PushEvent,
TestChannelRequest,
TestPushRequest,
UpdateChannelRequest,
UpdatePushEventRequest
} from './types';
/**
* 通知推送服务类
*/
export class PushService extends BaseService {
protected static readonly basePath = '/api/v1/admin/push';
/**
* 获取通知事件列表
*/
static async listEvents(): Promise<PushEvent[]> {
return this.get<PushEvent[]>('/events');
}
/**
* 获取系统内置通知事件元数据列表
*/
static async listBuiltInEvents(): Promise<EventMetadata[]> {
return this.get<EventMetadata[]>('/events/builtin');
}
/**
* 创建通知事件配置
*/
static async createEvent(data: CreatePushEventRequest): Promise<PushEvent> {
return this.post<PushEvent>('/events', data as unknown as Record<string, unknown>);
}
/**
* 更新指定通知事件配置
*/
static async updateEvent(id: number, data: UpdatePushEventRequest): Promise<void> {
return this.put<void>(`/events/${id}`, data as unknown as Record<string, unknown>);
}
/**
* 删除指定通知事件配置
*/
static async deleteEvent(id: number): Promise<void> {
return this.delete<void>(`/events/${id}`);
}
/**
* 快捷切换事件启用状态
*/
static async toggleEvent(id: number): Promise<boolean> {
return this.post<boolean>(`/events/${id}/toggle`);
}
/**
* 分页查询通知推送历史
*/
static async listHistories(params: ListPushHistoriesRequest): Promise<ListPushHistoriesResponse> {
return this.get<ListPushHistoriesResponse>('/histories', params as unknown as Record<string, unknown>);
}
/**
* 发送测试推送进行联通性校验
*/
static async testPush(data: TestPushRequest): Promise<void> {
return this.post<void>('/test', data as unknown as Record<string, unknown>);
}
/**
* 获取所有消息通道
*/
static async listChannels(): Promise<PushChannel[]> {
return this.get<PushChannel[]>('/channels');
}
/**
* 创建新的消息通道
*/
static async createChannel(data: CreateChannelRequest): Promise<PushChannel> {
return this.post<PushChannel>('/channels', data as unknown as Record<string, unknown>);
}
/**
* 更新指定消息通道
*/
static async updateChannel(id: number, data: UpdateChannelRequest): Promise<PushChannel> {
return this.put<PushChannel>(`/channels/${id}`, data as unknown as Record<string, unknown>);
}
/**
* 删除指定消息通道
*/
static async deleteChannel(id: number): Promise<void> {
return this.delete<void>(`/channels/${id}`);
}
/**
* 测试消息通道连通性
*/
static async testChannel(data: TestChannelRequest): Promise<void> {
return this.post<void>('/channels/test', data as unknown as Record<string, unknown>);
}
/**
* 获取各消息通道的动态表单定义
*/
static async listChannelDefinitions(): Promise<ChannelDefinition[]> {
return this.get<ChannelDefinition[]>('/channels/definitions');
}
}
+136
View File
@@ -0,0 +1,136 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
export interface PushEvent {
id: number;
event_key: string;
name: string;
channels: string[];
targets: string[];
template: string;
enabled: boolean;
created_at: string;
updated_at: string;
}
export interface PushHistory {
id: number;
event_key: string;
channel: string;
target: string;
title: string;
content: string;
level: 'INFO' | 'IMPORTANT' | 'CRITICAL';
status: 'success' | 'failed';
error_msg?: string;
created_at: string;
}
export interface PushChannelConfig {
channel: string;
url?: string;
secret?: string;
key?: string;
ext?: Record<string, unknown>;
}
export interface ListPushHistoriesRequest {
page?: number;
page_size?: number;
event_key?: string;
status?: string;
}
export interface ListPushHistoriesResponse {
total: number;
results: PushHistory[];
}
export interface UpdatePushEventRequest {
channels: string[];
targets: string[];
template: string;
enabled: boolean;
}
export interface TestPushRequest {
config: PushChannelConfig;
target?: string;
}
export interface EventMetadata {
key: string;
name: string;
default_template: {
title: string;
content: string;
level: string;
ext?: Record<string, unknown>;
};
description: string;
}
export interface CreatePushEventRequest {
event_key: string;
channels: string[];
targets?: string[];
template?: string;
enabled: boolean;
}
export interface PushChannel {
id: number;
name: string;
description?: string;
type: string;
token?: string;
url: string;
other: string;
enabled: boolean;
created_at?: string;
updated_at?: string;
}
export interface CreateChannelRequest {
name: string;
description?: string;
type: string;
token?: string;
url: string;
other: string;
enabled?: boolean;
}
export interface UpdateChannelRequest {
description?: string;
type: string;
token?: string;
url: string;
other: string;
enabled?: boolean;
}
export interface TestChannelRequest {
name?: string;
type?: string;
url?: string;
other?: string;
target?: string;
}
export interface ChannelFieldDef {
key: string;
label: string;
type: 'text' | 'password' | 'textarea';
required: boolean;
placeholder?: string;
description?: string;
}
export interface ChannelDefinition {
type: string;
name: string;
description: string;
fields: ChannelFieldDef[];
}