feat: unify app identity on appKey in platforms

这个提交包含在:
XuqmGroup 2026-05-08 10:09:22 +08:00
父节点 6eeea6f268
当前提交 775e6c85e8
共有 10 个文件被更改,包括 31 次插入32 次删除

查看文件

@ -15,7 +15,7 @@ const router = createRouter({
{ path: 'statistics', component: () => import('@/views/statistics/StatisticsView.vue') },
{ 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: 'apps/:appKey', 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') },

查看文件

@ -61,7 +61,7 @@ const route = useRoute()
const detail = ref<AppDetail | null>(null)
async function loadDetail() {
const res = await opsApi.getApp(route.params.id as string)
const res = await opsApi.getApp(route.params.appKey as string)
detail.value = res.data.data
}

查看文件

@ -17,7 +17,7 @@
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="$router.push(`/apps/${row.id}`)">
<el-button link type="primary" @click="$router.push(`/apps/${row.appKey}`)">
详情
</el-button>
</template>

查看文件

@ -10,7 +10,7 @@
</div>
<el-table :data="requests" v-loading="loading">
<el-table-column prop="appKey" label="AppID" width="180" />
<el-table-column prop="appKey" label="AppKey" width="180" />
<el-table-column prop="platform" label="平台" width="100">
<template #default="{ row }">
<el-tag size="small">{{ row.platform }}</el-tag>

查看文件

@ -52,7 +52,7 @@
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button link type="primary" @click="$router.push(`/apps/${row.id}`)">详情</el-button>
<el-button link type="primary" @click="$router.push(`/apps/${row.appKey}`)">详情</el-button>
</template>
</el-table-column>
</el-table>

查看文件

@ -46,7 +46,7 @@ const router = createRouter({
component: () => import('@/views/logs/OperationLogView.vue'),
},
{
path: 'apps/:id',
path: 'apps/:appKey',
component: () => import('@/views/apps/AppDetailView.vue'),
},
{

查看文件

@ -36,11 +36,10 @@
</div>
<template v-if="imService">
<div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap">
<!-- IM 管理页按 appKey 作用域查询不能把租户 app.id 直接传进去 -->
<el-button size="small" @click="$router.push({ path: `/apps/${route.params.id}/im`, query: { appKey: app.appKey } })">
<el-button size="small" @click="$router.push({ path: `/apps/${app.appKey}/im`, query: { appKey: app.appKey } })">
即时通讯管理
</el-button>
<el-button size="small" type="primary" plain @click="$router.push(`/apps/${route.params.id}/im-config`)">
<el-button size="small" type="primary" plain @click="$router.push(`/apps/${app.appKey}/im-config`)">
服务配置
</el-button>
</div>
@ -80,7 +79,7 @@
</div>
<div class="service-actions">
<template v-if="isServiceEnabled('PUSH')">
<el-button size="small" type="primary" plain @click="$router.push(`/apps/${route.params.id}/push-config`)">
<el-button size="small" type="primary" plain @click="$router.push(`/apps/${app.appKey}/push-config`)">
推送配置
</el-button>
<el-button size="small" @click="$router.push(`/apps/${app.appKey}/push-management`)">
@ -118,7 +117,7 @@
</span>
</div>
<div class="service-actions">
<el-button v-if="isServiceEnabled('UPDATE')" size="small" type="primary" plain @click="$router.push(`/apps/${route.params.id}/update`)">
<el-button v-if="isServiceEnabled('UPDATE')" size="small" type="primary" plain @click="$router.push(`/apps/${app.appKey}/update`)">
版本管理
</el-button>
<el-button v-else size="small" type="primary" plain @click="openActivationRequest('UPDATE')">
@ -222,9 +221,9 @@ function getServiceRepresentative(svcType: string) {
}
async function loadData() {
const id = route.params.id as string
const appKey = route.params.appKey as string
const [appRes, svcRes] = await Promise.all([
appApi.get(id), appApi.getServices(id),
appApi.get(appKey), appApi.getServices(appKey),
])
app.value = appRes.data.data
services.value = svcRes.data.data
@ -239,7 +238,7 @@ async function onToggleService(svcType: string, enable: boolean) {
type: 'warning', confirmButtonText: '确认关闭', cancelButtonText: '取消',
})
const platform = getServiceRepresentative(svcType)?.platform ?? 'ANDROID'
await appApi.toggleService(route.params.id as string, platform, svcType, false)
await appApi.toggleService(app.value?.appKey ?? (route.params.appKey as string), platform, svcType, false)
ElMessage.success('已关闭')
loadData()
}
@ -256,7 +255,7 @@ async function submitActivationRequest() {
}
submittingActivation.value = true
try {
await client.post(`/apps/${route.params.id}/services/request-activation`, null, {
await client.post(`/apps/${app.value?.appKey ?? (route.params.appKey as string)}/services/request-activation`, null, {
params: {
platform: activationForm.value.platform,
serviceType: activationForm.value.serviceType,
@ -280,7 +279,7 @@ function openVerifyDialog(purpose: 'REVEAL_SECRET' | 'RESET_SECRET') {
async function sendVerifyCode() {
sendingCode.value = true
try {
await appApi.requestSecretVerify(route.params.id as string, verifyPurpose.value)
await appApi.requestSecretVerify(app.value?.appKey ?? (route.params.appKey as string), verifyPurpose.value)
codeSent.value = true
ElMessage.success('验证码已发送')
} catch {
@ -294,13 +293,13 @@ async function submitVerify() {
if (verifyCode.value.length !== 6) return ElMessage.warning('请输入6位验证码')
submittingVerify.value = true
try {
const id = route.params.id as string
const appKey = app.value?.appKey ?? (route.params.appKey as string)
if (verifyPurpose.value === 'REVEAL_SECRET') {
const res = await appApi.revealSecret(id, verifyCode.value)
const res = await appApi.revealSecret(appKey, verifyCode.value)
revealedSecret.value = res.data.data.appSecret
ElMessage.success('AppSecret 已显示,请妥善保管')
} else {
const res = await appApi.resetSecret(id, verifyCode.value)
const res = await appApi.resetSecret(appKey, verifyCode.value)
revealedSecret.value = res.data.data.appSecret
ElMessage.success('AppSecret 已重置,旧密钥立即失效')
}

查看文件

@ -17,8 +17,8 @@
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button link type="primary" @click="$router.push(`/apps/${row.id}`)">详情</el-button>
<el-button link type="danger" @click="handleDelete(row.id)">删除</el-button>
<el-button link type="primary" @click="$router.push(`/apps/${row.appKey}`)">详情</el-button>
<el-button link type="danger" @click="handleDelete(row.appKey)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -93,9 +93,9 @@ async function handleCreate() {
}
}
async function handleDelete(id: string) {
async function handleDelete(appKey: string) {
await ElMessageBox.confirm('确定删除此应用?删除后不可恢复。', '警告', { type: 'warning' })
await appApi.delete(id)
await appApi.delete(appKey)
ElMessage.success('已删除')
loadApps()
}

查看文件

@ -23,7 +23,7 @@
<el-switch :model-value="imEnabled" @change="(val: boolean) => onToggleImService(val)" />
</div>
<div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap">
<!-- 服务配置页使用的是租户应用主键 app.idIM 管理页才带 appKey -->
<!-- 服务配置页IM 管理页现在都按 appKey 作为应用唯一标识 -->
<el-button size="small" @click="$router.push({ path: `/apps/${route.params.appKey}/im`, query: { appKey: app.appKey } })">
即时通讯管理
</el-button>

查看文件

@ -59,7 +59,7 @@
<el-tab-pane label="版本管理" name="UPDATE">
<div class="toolbar responsive-toolbar">
<el-select
v-model="updateAppId"
v-model="updateAppKey"
placeholder="选择应用"
style="width: 320px"
filterable
@ -67,9 +67,9 @@
>
<el-option
v-for="app in apps"
:key="app.id"
:key="app.appKey"
:label="`${app.name} · ${app.packageName}`"
:value="app.id"
:value="app.appKey"
/>
</el-select>
<el-input-number v-model="updateLimit" :min="20" :max="200" :step="10" controls-position="right" />
@ -125,7 +125,7 @@ const updateLoading = ref(false)
const updateLogs = ref<UpdateOperationLog[]>([])
const updateLimit = ref(100)
const apps = ref<App[]>([])
const updateAppId = ref('')
const updateAppKey = ref('')
onMounted(async () => {
await Promise.all([
@ -148,8 +148,8 @@ async function loadApps() {
try {
const res = await appApi.list()
apps.value = res.data.data
if (!updateAppId.value && apps.value.length) {
updateAppId.value = apps.value[0].id
if (!updateAppKey.value && apps.value.length) {
updateAppKey.value = apps.value[0].appKey
}
} catch {
// ignore; empty state will be shown in the selector
@ -173,13 +173,13 @@ async function loadTenantLogs() {
}
async function loadUpdateLogs() {
if (!updateAppId.value) {
if (!updateAppKey.value) {
updateLogs.value = []
return
}
updateLoading.value = true
try {
const res = await updateAdminApi.listOperationLogs(updateAppId.value, updateLimit.value)
const res = await updateAdminApi.listOperationLogs(updateAppKey.value, updateLimit.value)
updateLogs.value = res.data.data ?? []
} finally {
updateLoading.value = false