docs(spec): add SysApp application integration management design
- 为 rui/rui-frontend#4 工单编写设计规范 - 7 个文件变更:service 新建、index 改、router 改、locales 改、2 个 vue 新建 - 4 Tab 表单布局(基础信息/凭证信息/接口配置/高级) - 敏感字段脱敏(appSecret/appKey/aesKey) - certificates 字段 JSON 占位(等 rui/rui-framework#4 文件上传接口) 对应工单 rui/rui-frontend#4
This commit is contained in:
@@ -0,0 +1,469 @@
|
||||
# SysApp(第三方应用集成)管理界面设计规范
|
||||
|
||||
**工单**: rui/rui-frontend#4 — [UI-REQ] 后台增加 SysApp(第三方应用集成)管理界面
|
||||
**日期**: 2026-06-07
|
||||
**关联 Issue**: rui/rui-framework#4(文件上传接口待提供,前端先用 JSON 占位)
|
||||
**优先级**: P1
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标(Goal)
|
||||
|
||||
在 admin-ui 后台实现 **SysApp(第三方应用集成)管理模块**,为运营/管理员提供对第三方平台应用(微信、支付宝、Stripe 等)凭证信息的统一管理能力。模块包含列表展示、新增、编辑、删除、启停 5 个标准操作,遵循现有 `oauth2-client` 等模块的 CRUD 模式(`BaseService` + `RuiTable` + `FormDialog`),不引入新依赖。
|
||||
|
||||
## 2. 非目标(Non-Goals)
|
||||
|
||||
明确不在本期范围内的事项:
|
||||
|
||||
- **不修改后端**:本 Spec 仅涉及 admin-ui 前端;不修改 rui-framework / rui-cashier 等后端仓库代码或 API。
|
||||
- **不实现 certificates 文件上传 UI**:因 rui-framework 尚未提供文件上传接口(已提 Issue #4),certificates 字段本期用 JSON textarea 占位实现,标注「待后端文件上传接口就绪后升级」。
|
||||
- **不实现 isEncrypted 字段 UI**:后端为该字段预留,前端暂不展示。
|
||||
- **不修改 cashier-mobile / cashier-customer**:本 Spec 仅涉及 admin-ui。
|
||||
- **不引入 monaco-editor / @guolao/vue-monaco-editor 等新依赖**:证书编辑用原生 textarea + JSON.parse 校验。
|
||||
- **不修改 rui-framework 菜单 JSON 文件**:工单提到的 `data/menus/system.json` 属于 rui-framework 仓库菜单管理数据,不在本仓库范围内。
|
||||
- **不做多租户隔离增强**:tenantId 字段由后端按上下文自动填充,前端不主动设置。
|
||||
|
||||
## 3. 背景与上下文(Context)
|
||||
|
||||
### 3.1 后端接口现状
|
||||
|
||||
- **Controller**: `SysAppController`,继承 `BaseController`,自动具备 5 个标准操作:
|
||||
- `GET /system/admin/app/page` — 分页查询
|
||||
- `GET /system/admin/app/list` — 列表查询
|
||||
- `GET /system/admin/app/{id}` — 详情
|
||||
- `POST /system/admin/app` — 新增
|
||||
- `PUT /system/admin/app` — 修改
|
||||
- `DELETE /system/admin/app/{id}` — 删除
|
||||
- `DELETE /system/admin/app/batch` — 批量删除
|
||||
- `PUT /system/admin/app/status` — 启停
|
||||
- `GET /system/admin/app/export` — 导出
|
||||
- `POST /system/admin/app/import` — 导入
|
||||
- **鉴权**: `@AutoPermission("sys:app")`
|
||||
- **Swagger**: `http://localhost:9302/swagger-ui.html`
|
||||
- **后端 commit**: 27fa187(表)+ 29a9389(Service/Controller)+ 13b20ab(Result 规范)
|
||||
|
||||
### 3.2 前端代码基础
|
||||
|
||||
- **BaseService** (`admin-ui/src/service/BaseService.ts`) 已提供完整 CRUD 抽象,子类只需传 baseUrl:
|
||||
```ts
|
||||
class SysAppService extends BaseService {
|
||||
constructor() { super('/system/admin/app') }
|
||||
}
|
||||
export const sysAppService = new SysAppService()
|
||||
```
|
||||
- **RuiTable** 组件支持查询区、工具栏、列配置、slot、分页、导出、列设置、刷新、批量操作等开箱即用能力。
|
||||
- **OAuth2Client 页面**(`views/system/oauth2-client/Index.vue` + `OAuth2ClientFormDialog.vue`)是最相似的现有实现,本期直接照搬其模式。
|
||||
|
||||
### 3.3 路由与菜单
|
||||
|
||||
- admin-ui 路由采用 **前端硬编码 + i18n 键** 方式(`admin-ui/src/router/modules/system.ts`)。
|
||||
- 工单中提到的 `data/menus/system.json` 是 rui-framework 后端「菜单管理」功能加载的菜单数据,不影响 admin-ui 路由配置。
|
||||
|
||||
## 4. 关键设计决策(用户已确认)
|
||||
|
||||
| # | 决策项 | 选定方案 |
|
||||
|---|--------|---------|
|
||||
| 1 | 菜单归属 | 作为「系统管理」的子菜单,路由 `/system/app` |
|
||||
| 2 | 表单布局 | el-tabs 分 4 Tab(基础信息 / 凭证信息 / 接口配置 / 高级) |
|
||||
| 3 | certificates 字段 | 前端 JSON textarea 占位(待 rui/rui-framework#4 文件上传接口就绪后升级) |
|
||||
| 4 | 敏感字段(appSecret/appKey/aesKey) | 列表展示 6 个星号 `******`,编辑时留空表示不修改 |
|
||||
|
||||
## 5. 字段定义(共 21 个,UI 涉及 19 个)
|
||||
|
||||
| 字段 | 类型 | 必填 | UI 控件 | Tab | 说明 |
|
||||
|------|------|------|---------|-----|------|
|
||||
| id | Long | — | (仅后端) | — | 主键 |
|
||||
| tenantId | Long | — | (仅后端) | — | 租户ID 0=系统级(自动填充) |
|
||||
| ownerType | String | 是 | el-select | 1 | PLATFORM / TENANT |
|
||||
| platform | String | 是 | el-select | 1 | wechat / alipay / stripe |
|
||||
| name | String | 是 | el-input | 1 | 管理用名称 |
|
||||
| appId | String | 否 | el-input | 2 | 应用ID(UNIQUE) |
|
||||
| appSecret | String | 否 | el-input (password) | 2 | **敏感**:列表脱敏,编辑留空不修改 |
|
||||
| appKey | String | 否 | el-input (password) | 2 | **敏感** |
|
||||
| certificates | String | 否 | JSON textarea | 2 | 多证书 JSON 数组(**占位**) |
|
||||
| aesKey | String | 否 | el-input (password) | 2 | **敏感** |
|
||||
| redirectUri | String | 否 | el-input | 3 | OAuth2 回调地址 |
|
||||
| merchantId | String | 否 | el-input | 3 | 商户号 |
|
||||
| signType | String | 否 | el-select | 3 | RSA2 / MD5 / HMAC |
|
||||
| notifyUrl | String | 否 | el-input | 3 | 支付回调 |
|
||||
| apiBase | String | 否 | el-input | 3 | API 根地址 |
|
||||
| isSandbox | 0/1 | 否 | el-switch | 4 | 是否沙箱环境 |
|
||||
| extra | String | 否 | JSON textarea | 4 | JSON 扩展 |
|
||||
| isEncrypted | 0/1 | — | **不展示** | — | 预留加密字段(暂不实现) |
|
||||
| status | 0/1 | 否 | el-switch | 4 | 启用/禁用,默认 1 |
|
||||
| description | String | 否 | el-input (textarea) | 1 | 备注 |
|
||||
| sortNo | Int | 否 | el-input-number | 1 | 排序号 |
|
||||
|
||||
### 5.1 枚举映射(UI 展示用)
|
||||
|
||||
```ts
|
||||
const platformMap: Record<string, { label: string; type: 'primary' | 'success' | 'warning' }> = {
|
||||
wechat: { label: '微信', type: 'success' },
|
||||
alipay: { label: '支付宝', type: 'primary' },
|
||||
stripe: { label: 'Stripe', type: 'warning' },
|
||||
}
|
||||
|
||||
const ownerTypeMap: Record<string, { label: string; type: 'primary' | 'success' }> = {
|
||||
PLATFORM: { label: '平台级', type: 'primary' },
|
||||
TENANT: { label: '租户级', type: 'success' },
|
||||
}
|
||||
|
||||
const signTypeOptions = [
|
||||
{ label: 'RSA2', value: 'RSA2' },
|
||||
{ label: 'MD5', value: 'MD5' },
|
||||
{ label: 'HMAC', value: 'HMAC' },
|
||||
]
|
||||
```
|
||||
|
||||
## 6. 涉及文件清单(Files To Change)
|
||||
|
||||
| # | 文件 | 操作 | 用途 |
|
||||
|---|------|------|------|
|
||||
| 1 | `admin-ui/src/service/system/sysAppService.ts` | 新建 | 业务 Service,继承 `BaseService('/system/admin/app')` |
|
||||
| 2 | `admin-ui/src/service/system/index.ts` | 改动 | 追加 `export { sysAppService } from './sysAppService'` |
|
||||
| 3 | `admin-ui/src/router/modules/system.ts` | 改动 | 新增 `system/app` 路由,meta.i18n 键 `menu.systemApp` |
|
||||
| 4 | `admin-ui/src/locales/zh-CN.ts` | 改动 | `menu.systemApp: '应用集成'` |
|
||||
| 5 | `admin-ui/src/locales/en-US.ts` | 改动 | `menu.systemApp: 'App Integration'` |
|
||||
| 6 | `admin-ui/src/views/system/app/Index.vue` | 新建 | 列表页(RuiTable + 操作工具栏) |
|
||||
| 7 | `admin-ui/src/views/system/app/SysAppFormDialog.vue` | 新建 | 新增/编辑弹窗(4 Tab) |
|
||||
|
||||
**变更统计**:新建 3 个文件,修改 4 个文件。**总计 7 个文件**。
|
||||
|
||||
## 7. 列表页设计(`views/system/app/Index.vue`)
|
||||
|
||||
### 7.1 结构
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="text-xl font-bold mb-4">{{ $t('menu.systemApp') }}</h2>
|
||||
<RuiTable
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:load-data="loadData"
|
||||
:show-selection="true"
|
||||
:exportable="true"
|
||||
export-filename="SysApp应用集成列表"
|
||||
>
|
||||
<!-- 查询区、工具栏、列 slot、操作 slot -->
|
||||
</RuiTable>
|
||||
<SysAppFormDialog v-model:visible="dialogVisible" :row="currentRow" @success="handleFormSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 7.2 查询区(4 个条件)
|
||||
|
||||
| 控件 | 字段 | 控件类型 | 选项 |
|
||||
|------|------|---------|------|
|
||||
| 应用名称 | `name` | `el-input` (clearable) | — |
|
||||
| 平台 | `platform` | `el-select` (clearable) | wechat / alipay / stripe |
|
||||
| 所有者类型 | `ownerType` | `el-select` (clearable) | PLATFORM / TENANT |
|
||||
| 状态 | `status` | `el-select` (clearable) | 启用(1) / 禁用(0) |
|
||||
|
||||
### 7.3 列配置(columns)
|
||||
|
||||
| prop | label | 宽度/最小宽度 | slot | 备注 |
|
||||
|------|-------|--------------|------|------|
|
||||
| name | 应用名称 | minWidth 150 | — | — |
|
||||
| platform | 平台 | width 100 | 是 | 彩色 Tag |
|
||||
| ownerType | 所有者 | width 100 | 是 | PLATFORM 蓝、TENANT 绿 |
|
||||
| appId | 应用ID | minWidth 120 | — | 列表不脱敏(UNIQUE 标识) |
|
||||
| status | 状态 | width 90 | 是 | Switch |
|
||||
| createdAt | 创建时间 | minWidth 180 | — | dataType='dateTime',sortable='custom' |
|
||||
|
||||
> **脱敏说明**:appSecret / appKey / aesKey 三个敏感字段**不进入列表列**,仅在编辑弹窗中处理。列表中没有任何明文密钥展示位。
|
||||
|
||||
### 7.4 工具栏
|
||||
|
||||
- **左侧**:新增应用按钮(`type="primary"`)
|
||||
- **右侧**:批量删除(基于 `show-selection`) + 导出 + 刷新 + 列设置(RuiTable 内置)
|
||||
|
||||
### 7.5 行操作
|
||||
|
||||
- 编辑
|
||||
- 删除(ElMessageBox 二次确认,删除成功后 `tableRef.refresh()`)
|
||||
|
||||
### 7.6 启停切换
|
||||
|
||||
```ts
|
||||
async function handleStatusChange(row: any, status: number) {
|
||||
if (!row?.id) return
|
||||
try {
|
||||
await sysAppService.changeStatus(row.id, status)
|
||||
ElMessage.success(status === 1 ? '启用成功' : '禁用成功')
|
||||
} catch {
|
||||
row.status = status === 1 ? 0 : 1 // 失败回滚
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 表单弹窗设计(`views/system/app/SysAppFormDialog.vue`)
|
||||
|
||||
### 8.1 弹窗基本属性
|
||||
|
||||
- 宽度:`760px`
|
||||
- title:编辑时「编辑应用」/ 新增时「新增应用」
|
||||
- 关闭点击遮罩:`close-on-click-modal="false"`
|
||||
- props:`visible: boolean`、`row: any`
|
||||
- emits:`update:visible`、`success`
|
||||
|
||||
### 8.2 数据加载流程
|
||||
|
||||
```
|
||||
watch(visible, val => {
|
||||
if (val) {
|
||||
if (props.row) {
|
||||
// 编辑:拉取详情(确保拿到完整字段,包括敏感字段的明文用于编辑回显)
|
||||
sysAppService.getById(props.row.id).then(data => { form.value = { ...data } })
|
||||
} else {
|
||||
// 新增:重置为默认值
|
||||
form.value = { ...defaultForm }
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 8.3 默认值(新增时)
|
||||
|
||||
```ts
|
||||
const defaultForm = {
|
||||
id: undefined,
|
||||
ownerType: 'PLATFORM',
|
||||
platform: 'wechat',
|
||||
name: '',
|
||||
appId: '',
|
||||
appSecret: '',
|
||||
appKey: '',
|
||||
certificates: '',
|
||||
aesKey: '',
|
||||
redirectUri: '',
|
||||
merchantId: '',
|
||||
signType: 'RSA2',
|
||||
notifyUrl: '',
|
||||
apiBase: '',
|
||||
isSandbox: 0,
|
||||
extra: '',
|
||||
status: 1,
|
||||
description: '',
|
||||
sortNo: 0,
|
||||
}
|
||||
```
|
||||
|
||||
### 8.4 4 Tab 分布
|
||||
|
||||
#### Tab 1:基础信息
|
||||
|
||||
```
|
||||
- name* el-input (必填)
|
||||
- ownerType* el-select (必填, PLATFORM / TENANT)
|
||||
- platform* el-select (必填, wechat / alipay / stripe)
|
||||
- description el-input (textarea, :rows="2")
|
||||
- sortNo el-input-number
|
||||
```
|
||||
|
||||
#### Tab 2:凭证信息
|
||||
|
||||
```
|
||||
- appId el-input
|
||||
- appSecret el-input (type="password" show-password)
|
||||
- appKey el-input (type="password" show-password)
|
||||
- aesKey el-input (type="password" show-password)
|
||||
- certificates el-input (type="textarea" :rows="4")
|
||||
placeholder='[{"name":"cert1","content":"<PEM 内容>"}]'
|
||||
下方 helper text:「多证书 JSON 数组。格式:[{name, content}]。待后端文件上传接口就绪后升级为文件上传。」
|
||||
```
|
||||
|
||||
> **敏感字段编辑规则**:appSecret / appKey / aesKey 三个字段在编辑时**留空 = 不修改原值**。这一规则完全照搬 `OAuth2ClientFormDialog` 的现有做法,保证行为一致。
|
||||
|
||||
#### Tab 3:接口配置
|
||||
|
||||
```
|
||||
- redirectUri el-input
|
||||
- notifyUrl el-input
|
||||
- apiBase el-input
|
||||
- merchantId el-input
|
||||
- signType el-select (RSA2 / MD5 / HMAC)
|
||||
```
|
||||
|
||||
#### Tab 4:高级
|
||||
|
||||
```
|
||||
- isSandbox el-switch (0/1)
|
||||
- extra el-input (type="textarea" :rows="4")
|
||||
下方 helper text:「JSON 扩展字段,提交前需通过 JSON 格式校验」
|
||||
- status el-switch (0/1,默认 1)
|
||||
```
|
||||
|
||||
### 8.5 校验
|
||||
|
||||
- **必填字段**:`name`、`ownerType`、`platform`
|
||||
- **JSON 字段**:certificates 和 extra 字段在提交前调用 `validateJSON()` 校验,非空时尝试 `JSON.parse`,失败则 `ElMessage.error('xxx 字段 JSON 格式错误')` 并阻止提交。
|
||||
|
||||
```ts
|
||||
function validateJSON(value: string, fieldName: string): boolean {
|
||||
if (!value || !value.trim()) return true // 空值允许
|
||||
try {
|
||||
JSON.parse(value)
|
||||
return true
|
||||
} catch {
|
||||
ElMessage.error(`${fieldName} JSON 格式错误`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.6 提交逻辑
|
||||
|
||||
```ts
|
||||
async function handleSubmit() {
|
||||
await formRef.value.validate()
|
||||
if (!validateJSON(form.value.certificates, 'certificates')) return
|
||||
if (!validateJSON(form.value.extra, 'extra')) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const isEdit = !!form.value.id
|
||||
const success = isEdit
|
||||
? await sysAppService.update(form.value as any)
|
||||
: await sysAppService.add(form.value)
|
||||
if (success !== false) {
|
||||
ElMessage.success(isEdit ? '修改成功' : '新增成功')
|
||||
emit('success')
|
||||
dialogVisible.value = false
|
||||
}
|
||||
} catch {
|
||||
// 错误已由请求拦截器统一提示
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 9. Service 层设计(`service/system/sysAppService.ts`)
|
||||
|
||||
完整实现(参考 `oauth2ClientService.ts` 的极简模式):
|
||||
|
||||
```ts
|
||||
import { BaseService } from '../BaseService'
|
||||
|
||||
/**
|
||||
* SysApp(第三方应用集成)服务
|
||||
*
|
||||
* <p>负责与后端 /system/admin/app 接口的通信,继承 BaseService 获得标准 CRUD 能力。</p>
|
||||
*
|
||||
* <p><b>后续升级路径</b>:后端文件上传接口(rui/rui-framework#4)就绪后,可在此文件追加
|
||||
* `uploadCertificate(file: File)` 方法,并将表单中 certificates 字段从 JSON textarea
|
||||
* 升级为文件上传组件。</p>
|
||||
*/
|
||||
class SysAppService extends BaseService {
|
||||
constructor() {
|
||||
super('/system/admin/app')
|
||||
}
|
||||
}
|
||||
|
||||
/** SysApp 服务单例 */
|
||||
export const sysAppService = new SysAppService()
|
||||
```
|
||||
|
||||
并在 `service/system/index.ts` 末尾追加:
|
||||
```ts
|
||||
export { sysAppService } from './sysAppService'
|
||||
```
|
||||
|
||||
## 10. 路由与国际化
|
||||
|
||||
### 10.1 路由注册
|
||||
|
||||
在 `admin-ui/src/router/modules/system.ts` 的 `M` 常量中追加:
|
||||
```ts
|
||||
systemApp: 'menu.systemApp',
|
||||
```
|
||||
|
||||
在 `systemRoutes` 数组中追加:
|
||||
```ts
|
||||
{ path: 'system/app', name: 'SystemApp', component: () => import('@/views/system/app/Index.vue'), meta: { i18n: M.systemApp } },
|
||||
```
|
||||
|
||||
> 路由位置:放在 `systemOAuth2Client` 之后,保持「集成类」菜单分组相邻。
|
||||
|
||||
### 10.2 国际化
|
||||
|
||||
`admin-ui/src/locales/zh-CN.ts`(在 `systemOAuth2Client` 后追加):
|
||||
```ts
|
||||
systemApp: '应用集成',
|
||||
```
|
||||
|
||||
`admin-ui/src/locales/en-US.ts`(在 `system` block 内对应位置追加):
|
||||
```ts
|
||||
systemApp: 'App Integration',
|
||||
```
|
||||
|
||||
## 11. 错误处理
|
||||
|
||||
- **统一拦截**:所有 HTTP 错误由 `utils/request` 拦截器统一提示(已存在),前端代码不再额外 try-catch 提示。
|
||||
- **业务校验**:表单必填、JSON 格式校验在组件内完成,失败用 `ElMessage` 提示。
|
||||
- **状态回滚**:启停切换失败时,将 `row.status` 回滚到原值。
|
||||
- **删除确认**:删除前 `ElMessageBox.confirm` 二次确认。
|
||||
|
||||
## 12. 测试策略
|
||||
|
||||
### 12.1 静态检查
|
||||
|
||||
```bash
|
||||
pnpm --filter admin-ui type-check # 0 errors
|
||||
pnpm --filter admin-ui lint # 0 errors
|
||||
```
|
||||
|
||||
### 12.2 运行时验证(手动)
|
||||
|
||||
1. **启动 dev server**:`pnpm dev:admin`
|
||||
2. **登录** 后访问 `/system/app`(需菜单权限 `sys:app:query`,从后端菜单加载)
|
||||
3. **列表加载**:默认加载列表数据,列展示正确
|
||||
4. **查询**:按 name / platform / ownerType / status 过滤,验证结果正确
|
||||
5. **新增**:点「新增应用」→ 填写各 Tab 必填项 → 提交 → 列表出现新行
|
||||
6. **编辑**:点行内编辑 → 弹窗加载详情(4 Tab 回显)→ 修改 → 提交 → 列表更新
|
||||
7. **敏感字段验证**:编辑时不输入 appSecret → 提交后重新打开,明文应保持不变(**由后端「留空不修改」规则保证**)
|
||||
8. **JSON 字段验证**:certificates / extra 输入非 JSON 文本 → 提交时被拦截并提示
|
||||
9. **启停**:点击状态 Switch → 接口调用 → 列表状态切换;模拟失败时 row.status 回滚
|
||||
10. **删除**:点击删除 → 二次确认 → 提交 → 行从列表消失
|
||||
11. **批量删除**:选中多行 → 批量删除 → 全部消失
|
||||
12. **导出**:点击导出 → 下载 CSV 文件
|
||||
13. **脱敏验证**:在 devtools Network 面板检查 `/page` 返回的 records,**不应**包含 appSecret / appKey / aesKey 明文(这三个字段不进列表列)
|
||||
|
||||
### 12.3 验证清单(提交前必过)
|
||||
|
||||
- [ ] `pnpm type-check` 通过
|
||||
- [ ] `pnpm lint` 通过
|
||||
- [ ] 列表 → 新增 → 编辑 → 启停 → 删除 → 批量删除 完整跑通
|
||||
- [ ] 列表中不展示任何明文密钥
|
||||
- [ ] 敏感字段编辑留空后原值保持
|
||||
- [ ] certificates / extra 非法 JSON 提交被拦截
|
||||
- [ ] 菜单「应用集成」在侧边栏正确显示,路由跳转正常
|
||||
|
||||
## 13. 风险与缓解(Risks And Mitigations)
|
||||
|
||||
| # | 风险 | 缓解措施 |
|
||||
|---|------|---------|
|
||||
| 1 | **后端脱敏返回**:若后端在列表接口已经对 appSecret/appKey/aesKey 做脱敏(如 `******`),则编辑回显时弹窗中拿不到原值 | 列表用 `getById(id)` 拉详情(详情接口通常返回明文),并设计为编辑时强制用户重新输入敏感字段。本期采用「详情接口返回明文,编辑留空不修改」模式;如未来后端详情也脱敏,需在 UI 加「修改敏感字段」开关。 |
|
||||
| 2 | **certificates JSON 格式错误**:用户提交非合法 JSON 导致后端解析失败 | 提交前 `validateJSON()` 拦截,UI 给出明确错误提示;helper text 提示正确格式。 |
|
||||
| 3 | **文件上传接口未就绪**:certificates 暂用 JSON 占位,用户体验差 | 已在 rui/rui-framework#4 提 Issue;后端接口就绪后再升级为文件上传组件(升级路径已记录在 `sysAppService.ts` 注释中)。 |
|
||||
| 4 | **路由重复**:system.ts 中路由顺序错乱导致菜单不显示 | 路由注册在 systemOAuth2Client 之后;meta.i18n 键值与 locales 文件保持一致。 |
|
||||
| 5 | **列表数据过大导致性能问题** | 分页由 RuiTable 默认处理(page=1, size=10),无额外风险。 |
|
||||
| 6 | **前端调用了不存在的接口**:万一后端实际未提供 `import` 接口 | BaseService 自带 `importable` 开关默认 false,不暴露导入按钮。如后端未提供 import 接口则本 UI 不调用即可。 |
|
||||
|
||||
## 14. 决策摘要(Decision Summary)
|
||||
|
||||
- **架构**:照搬 `oauth2-client` 模式(`BaseService` 13 行 + `RuiTable` 列表 + `FormDialog` 弹窗),不引入新依赖、不发明新模式。
|
||||
- **菜单归属**:系统管理 → 子菜单「应用集成」,路由 `/system/app`。
|
||||
- **表单布局**:el-tabs 4 Tab,760px 弹窗。
|
||||
- **敏感字段**:appSecret / appKey / aesKey 列表脱敏 6 星号,编辑留空不修改。
|
||||
- **certificates 字段**:JSON textarea 占位,UI 注释提醒「待后端文件上传接口就绪后升级」。
|
||||
- **isEncrypted 字段**:UI 暂不实现。
|
||||
- **测试**:type-check + lint + 手动跑通完整 CRUD。
|
||||
- **依赖后端**:仅依赖 rui-framework 既有 `/system/admin/app` 接口;`/system/admin/file/upload`(rui/rui-framework#4)就绪后再升级 certificates 体验。
|
||||
|
||||
---
|
||||
|
||||
**设计评审状态**: 待评审
|
||||
**下一步**: 用户评审通过后,编写实施计划(Plan)
|
||||
@@ -0,0 +1,24 @@
|
||||
# Specs 索引
|
||||
|
||||
本目录存放已通过评审的设计规范(Spec)。每份 Spec 对应一个工单/需求,配套 Plan 在 `../plans/` 目录。
|
||||
|
||||
## 列表
|
||||
|
||||
| 日期 | 标题 | 工单 | 状态 |
|
||||
|------|------|------|------|
|
||||
| 2026-06-06 | [API 协作工作流设计](2026-06-06-api-collaboration-design.md) | 内部规范 | 已评审 |
|
||||
| 2026-06-07 | [用户管理接口适配设计](2026-06-07-user-management-api-adaptation-design.md) | rui/rui-frontend#2 | 已评审 |
|
||||
| 2026-06-07 | [SysApp 应用集成管理设计](2026-06-07-sysapp-management-design.md) | rui/rui-frontend#4 | 待评审 |
|
||||
|
||||
## 命名规范
|
||||
|
||||
- 文件名:`YYYY-MM-DD-<topic>-design.md`
|
||||
- 目录:`docs/superpowers/specs/`
|
||||
- 配套 Plan:`docs/superpowers/plans/YYYY-MM-DD-<topic>-plan.md`
|
||||
|
||||
## 状态
|
||||
|
||||
- **待评审**:Spec 刚写完,等待用户/团队评审
|
||||
- **已评审**:用户已批准,可进入 Plan 阶段
|
||||
- **已实施**:对应 Plan 已执行完成
|
||||
- **已归档**:功能上线,文档归档
|
||||
Reference in New Issue
Block a user