diff --git a/ops-platform/components.d.ts b/ops-platform/components.d.ts
index 1daf8d2..d6e5a5e 100644
--- a/ops-platform/components.d.ts
+++ b/ops-platform/components.d.ts
@@ -16,6 +16,7 @@ declare module 'vue' {
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
+ ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
diff --git a/ops-platform/src/api/ops.ts b/ops-platform/src/api/ops.ts
index d08a59c..e9eb093 100644
--- a/ops-platform/src/api/ops.ts
+++ b/ops-platform/src/api/ops.ts
@@ -131,6 +131,64 @@ export interface SensitiveWordPage {
totalPages: number
}
+export interface PushDeviceInfo {
+ id: string
+ vendor: string
+ tokenPreview: string
+ platform?: string
+ deviceId: string
+ brand?: string
+ model?: string
+ osVersion?: string
+ appVersion?: string
+ receivePush: boolean
+ lastLoginAt: string
+ updatedAt: string
+}
+
+export interface PushDiagnostics {
+ tokenType: 'PUSH' | 'IM' | 'UNKNOWN'
+ appId?: string
+ userId?: string
+ online: boolean
+ lastSeenAt: number
+ canSendOfflineMessage: boolean
+ deliverableDevice?: PushDeviceInfo | null
+ deliverableDevices: PushDeviceInfo[]
+ devices: PushDeviceInfo[]
+}
+
+export interface PushDeviceLog {
+ id: string
+ appId: string
+ userId: string
+ vendor: string
+ tokenPreview: string
+ platform?: string
+ deviceId: string
+ brand?: string
+ model?: string
+ osVersion?: string
+ appVersion?: string
+ receivePush: boolean
+ eventType: 'REGISTER' | 'UNREGISTER' | 'RECEIVE_PUSH_UPDATE'
+ createdAt: string
+}
+
+export interface PushDeviceLogPage {
+ content: PushDeviceLog[]
+ total: number
+ totalPages: number
+}
+
+export interface PushTestResult {
+ appId: string
+ userId: string
+ sent: boolean
+ targetCount: number
+ targets: PushDeviceInfo[]
+}
+
export const opsApi = {
listTenants: (keyword = '', page = 0, size = 20) =>
client.get<{ data: TenantPage }>('/ops/tenants', { params: { keyword, page, size } }),
@@ -189,4 +247,13 @@ export const opsApi = {
deleteSensitiveWord: (id: string) =>
client.delete(`/ops/risk/sensitive-words/${id}`),
+
+ searchPushByToken: (token: string, appId = '') =>
+ client.get<{ data: PushDiagnostics }>('/ops/push/search', { params: { token, appId } }),
+
+ listPushDeviceLogs: (appId: string, userId: string, page = 0, size = 20) =>
+ client.get<{ data: PushDeviceLogPage }>('/ops/push/device-logs', { params: { appId, userId, page, size } }),
+
+ sendPushTestOffline: (payload: { appId: string; userId: string; title: string; body: string; payload?: string }) =>
+ client.post<{ data: PushTestResult }>('/ops/push/test-offline', payload),
}
diff --git a/ops-platform/src/router/index.ts b/ops-platform/src/router/index.ts
index 719b83e..422c1e7 100644
--- a/ops-platform/src/router/index.ts
+++ b/ops-platform/src/router/index.ts
@@ -16,6 +16,7 @@ const router = createRouter({
{ path: 'service-requests', component: () => import('@/views/services/ServiceRequestsView.vue') },
{ path: 'apps', component: () => import('@/views/apps/AppListView.vue') },
{ path: 'apps/:id', component: () => import('@/views/apps/AppDetailView.vue') },
+ { path: 'push', component: () => import('@/views/push/PushDiagnosticsView.vue') },
{ path: 'operation-logs', component: () => import('@/views/logs/OperationLogView.vue') },
{ path: 'risk-control', component: () => import('@/views/risk/RiskControlView.vue') },
],
diff --git a/ops-platform/src/views/layout/MainLayout.vue b/ops-platform/src/views/layout/MainLayout.vue
index bdc691e..9dba907 100644
--- a/ops-platform/src/views/layout/MainLayout.vue
+++ b/ops-platform/src/views/layout/MainLayout.vue
@@ -66,7 +66,7 @@
+
+
diff --git a/tenant-platform/src/api/app.ts b/tenant-platform/src/api/app.ts
index 3642ea4..d781fec 100644
--- a/tenant-platform/src/api/app.ts
+++ b/tenant-platform/src/api/app.ts
@@ -116,7 +116,7 @@ export const appApi = {
appId: string,
platform: string,
serviceType: string,
- config: Partial & Partial & Partial,
+ config: Partial & Partial & Partial | Record,
) =>
client.put<{ data: FeatureService }>(`/apps/${appId}/services/config`, config, {
params: { platform, serviceType },
diff --git a/tenant-platform/src/views/push/PushConfigView.vue b/tenant-platform/src/views/push/PushConfigView.vue
index 2945a73..5da6411 100644
--- a/tenant-platform/src/views/push/PushConfigView.vue
+++ b/tenant-platform/src/views/push/PushConfigView.vue
@@ -50,8 +50,7 @@
@@ -78,7 +77,7 @@ type VendorKey = keyof PushServiceConfig
type FieldDef = {
key: string
label: string
- type?: 'password' | 'textarea' | 'switch'
+ type?: 'textarea' | 'switch'
placeholder?: string
rows?: number
}
@@ -117,7 +116,7 @@ const vendorDefs: VendorDef[] = [
hint: '填写 AppId / AppSecret,供服务端推送使用。',
fields: [
{ key: 'appId', label: 'AppId' },
- { key: 'appSecret', label: 'AppSecret', type: 'password' },
+ { key: 'appSecret', label: 'AppSecret' },
],
},
{
@@ -127,7 +126,7 @@ const vendorDefs: VendorDef[] = [
fields: [
{ key: 'appId', label: 'AppId' },
{ key: 'appKey', label: 'AppKey' },
- { key: 'appSecret', label: 'AppSecret', type: 'password' },
+ { key: 'appSecret', label: 'AppSecret' },
],
},
{
@@ -137,7 +136,7 @@ const vendorDefs: VendorDef[] = [
fields: [
{ key: 'appId', label: 'AppId' },
{ key: 'appKey', label: 'AppKey' },
- { key: 'masterSecret', label: 'MasterSecret', type: 'password' },
+ { key: 'masterSecret', label: 'MasterSecret' },
],
},
{
@@ -147,7 +146,7 @@ const vendorDefs: VendorDef[] = [
fields: [
{ key: 'appId', label: 'AppId' },
{ key: 'appKey', label: 'AppKey' },
- { key: 'appSecret', label: 'AppSecret', type: 'password' },
+ { key: 'appSecret', label: 'AppSecret' },
],
},
{
@@ -157,7 +156,7 @@ const vendorDefs: VendorDef[] = [
fields: [
{ key: 'appId', label: 'AppId' },
{ key: 'clientId', label: 'ClientId' },
- { key: 'clientSecret', label: 'ClientSecret', type: 'password' },
+ { key: 'clientSecret', label: 'ClientSecret' },
],
},
{
@@ -212,7 +211,7 @@ async function saveConfig() {
if (!app.value) return
saving.value = true
try {
- await appApi.updateServiceConfig(app.value.id, servicePlatform.value, 'PUSH', pushConfig)
+ await appApi.updateServiceConfig(app.value.id, servicePlatform.value, 'PUSH', toPushConfigRequest())
ElMessage.success('推送配置已保存')
await loadData()
} catch {
@@ -222,6 +221,31 @@ async function saveConfig() {
}
}
+function toPushConfigRequest(): Record {
+ return {
+ huaweiAppId: pushConfig.huawei.appId ?? '',
+ huaweiAppSecret: pushConfig.huawei.appSecret ?? '',
+ xiaomiAppId: pushConfig.xiaomi.appId ?? '',
+ xiaomiAppKey: pushConfig.xiaomi.appKey ?? '',
+ xiaomiAppSecret: pushConfig.xiaomi.appSecret ?? '',
+ oppoAppId: pushConfig.oppo.appId ?? '',
+ oppoAppKey: pushConfig.oppo.appKey ?? '',
+ oppoMasterSecret: pushConfig.oppo.masterSecret ?? '',
+ vivoAppId: pushConfig.vivo.appId ?? '',
+ vivoAppKey: pushConfig.vivo.appKey ?? '',
+ vivoAppSecret: pushConfig.vivo.appSecret ?? '',
+ honorAppId: pushConfig.honor.appId ?? '',
+ honorClientId: pushConfig.honor.clientId ?? '',
+ honorClientSecret: pushConfig.honor.clientSecret ?? '',
+ apnsTeamId: pushConfig.apns.teamId ?? '',
+ apnsKeyId: pushConfig.apns.keyId ?? '',
+ apnsBundleId: pushConfig.apns.bundleId ?? '',
+ apnsKeyPath: pushConfig.apns.keyPath ?? '',
+ apnsSandbox: pushConfig.apns.sandbox ?? false,
+ fcmServiceAccountJson: pushConfig.fcm.serviceAccountJson ?? '',
+ }
+}
+
async function onTogglePushService(enable: boolean) {
if (enable) {
ElMessage.info('请通过服务开通流程启用推送服务')