# 盘古用户平台 - 应用管理模块技术方案

---

| 文档信息 | 内容 |
|---------|------|
| **文档版本** | V1.0 |
| **项目名称** | 盘古用户平台（Pangu User Platform） |
| **模块名称** | 应用管理模块 |
| **编写团队 | pangu |
| **创建日期** | 2026-01-31 |
| **审核状态** | 待评审 |

---

## 修订记录

| 版本 | 日期 | 修订人 | 修订内容 |
|------|------|--------|----------|
| V1.0 | 2026-01-31 | pangu | 初稿 |

---

## 目录

1. [模块概述](#1-模块概述)
2. [需求分析](#2-需求分析)
3. [系统设计](#3-系统设计)
4. [前端技术方案](#4-前端技术方案)
5. [后端技术方案](#5-后端技术方案)
6. [数据库设计](#6-数据库设计)
7. [接口设计](#7-接口设计)
8. [开发计划](#8-开发计划)
9. [测试方案](#9-测试方案)
10. [风险评估](#10-风险评估)

---

## 1. 模块概述

### 1.1 模块定位

应用管理模块是盘古用户平台的核心模块之一，负责管理接入平台的第三方应用，控制API接口访问权限。通过该模块，超级管理员可以创建应用、分配接口权限、管理应用密钥，实现对外开放API的安全访问控制。

### 1.2 核心价值

| 价值点 | 描述 |
|--------|------|
| **统一接入管理** | 对所有第三方应用进行统一注册和管理 |
| **细粒度权限控制** | 精确控制每个应用可访问的API接口 |
| **安全认证机制** | 通过AppId+AppSecret签名机制保障接口安全 |
| **密钥生命周期管理** | 支持密钥重置，确保安全风险可控 |

### 1.3 模块边界

```
┌─────────────────────────────────────────────────────────────┐
│                      应用管理模块                            │
├─────────────────────────────────────────────────────────────┤
│  ┌───────────────┐  ┌───────────────┐  ┌───────────────┐   │
│  │   应用CRUD    │  │   接口授权    │  │   密钥管理    │   │
│  │  - 新增应用   │  │  - 接口勾选   │  │  - 自动生成   │   │
│  │  - 编辑应用   │  │  - 权限存储   │  │  - 重置密钥   │   │
│  │  - 删除应用   │  │  - 权限校验   │  │  - 安全提示   │   │
│  │  - 查询列表   │  │               │  │               │   │
│  └───────────────┘  └───────────────┘  └───────────────┘   │
│                              │                              │
│                              ▼                              │
│                    ┌───────────────┐                        │
│                    │  开放API网关  │                        │
│                    │  - 签名验证   │                        │
│                    │  - 权限校验   │                        │
│                    │  - 请求转发   │                        │
│                    └───────────────┘                        │
└─────────────────────────────────────────────────────────────┘
```

---

## 2. 需求分析

### 2.1 功能需求

#### 2.1.1 功能清单

| 功能编号 | 功能名称 | 功能描述 | 优先级 | 状态 |
|---------|---------|---------|:------:|:----:|
| APP-001 | 应用列表查询 | 按应用名称、编码、状态筛选查询 | P0 | 待开发 |
| APP-002 | 新增应用 | 创建新应用，自动生成应用编码和密钥 | P0 | 待开发 |
| APP-003 | 编辑应用 | 修改应用信息和接口授权 | P0 | 待开发 |
| APP-004 | 删除应用 | 删除应用及其授权信息 | P1 | 待开发 |
| APP-005 | 重置密钥 | 重新生成应用密钥 | P0 | 待开发 |
| APP-006 | 接口授权 | 配置应用可访问的API接口 | P0 | 待开发 |
| APP-007 | 禁用/启用应用 | 控制应用访问权限 | P0 | 待开发 |

#### 2.1.2 业务规则

| 规则编号 | 规则描述 | 验证方式 |
|---------|---------|---------|
| APP-R01 | 应用编码由系统自动生成，格式：YY + 6位数字序号 | 后端生成 |
| APP-R02 | 应用密钥由系统自动生成，32位随机字符串 | 后端生成 |
| APP-R03 | 重置密钥后，旧密钥立即失效 | 后端处理 |
| APP-R04 | 重置密钥后需弹窗显示新密钥并提供复制功能 | 前端展示 |
| APP-R05 | 删除应用需popconfirm二次确认 | 前端交互 |
| APP-R06 | 禁用应用后，该应用无法调用任何API接口 | 后端拦截 |
| APP-R07 | 接口授权采用勾选方式，可多选 | 前端交互 |
| APP-R08 | 应用名称不能重复 | 后端校验 |

### 2.2 非功能需求

| 需求类型 | 需求描述 | 指标 |
|---------|---------|------|
| **性能** | 应用列表查询响应时间 | ≤ 500ms |
| **性能** | 接口授权校验响应时间 | ≤ 50ms |
| **安全** | 密钥加密存储 | AES加密 |
| **安全** | 接口签名验证 | MD5签名 |
| **安全** | 防重放攻击 | 时间戳校验（5分钟内有效） |
| **可用性** | 权限变更实时生效 | 无需重启服务 |

### 2.3 用户角色

| 角色 | 权限范围 |
|------|---------|
| **超级管理员** | 可查看所有应用，可新增/编辑/删除应用，可进行接口授权，可重置密钥 |
| **分公司用户** | 无权限 |
| **学校用户** | 无权限 |

---

## 3. 系统设计

### 3.1 整体架构

```
┌─────────────────────────────────────────────────────────────────────┐
│                           前端层                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                 应用管理页面 (Vue 3 + Element Plus)           │   │
│  │  - 列表页 (index.vue)                                         │   │
│  │  - 新增/编辑弹窗 (AppDialog.vue)                              │   │
│  │  - 密钥展示弹窗 (SecretDialog.vue)                            │   │
│  └─────────────────────────────────────────────────────────────┘   │
└────────────────────────────────┬────────────────────────────────────┘
                                 │ HTTP/HTTPS
                                 ▼
┌─────────────────────────────────────────────────────────────────────┐
│                          后端服务层                                  │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │              应用管理服务 (Spring Boot)                       │   │
│  │  - ApplicationController                                       │   │
│  │  - ApplicationService                                          │   │
│  │  - ApplicationMapper                                           │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │              开放API网关                                       │   │
│  │  - ApiAuthInterceptor (签名验证)                              │   │
│  │  - ApiPermissionFilter (权限校验)                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
└────────────────────────────────┬────────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────────┐
│                          数据层                                      │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐              │
│  │ pg_application│  │ pg_app_api   │  │ pg_api_dict  │              │
│  │   应用表      │  │ 应用接口授权  │  │  接口字典    │              │
│  └──────────────┘  └──────────────┘  └──────────────┘              │
│                                                                      │
│  ┌──────────────┐                                                   │
│  │    Redis     │ ← 缓存应用授权信息，提升验证性能                   │
│  └──────────────┘                                                   │
└─────────────────────────────────────────────────────────────────────┘
```

### 3.2 数据流设计

#### 3.2.1 应用创建流程

```
┌───────────┐      ┌───────────┐      ┌───────────┐      ┌───────────┐
│   前端     │      │ Controller │      │  Service  │      │   MySQL   │
└─────┬─────┘      └─────┬─────┘      └─────┬─────┘      └─────┬─────┘
      │                  │                  │                  │
      │  POST /api/application             │                  │
      │──────────────────►                 │                  │
      │                  │                  │                  │
      │                  │  insertApplication                 │
      │                  │──────────────────►                 │
      │                  │                  │                  │
      │                  │                  │ 1.生成应用编码   │
      │                  │                  │ 2.生成密钥       │
      │                  │                  │ 3.加密存储       │
      │                  │                  │──────────────────►
      │                  │                  │                  │
      │                  │                  │ 4.保存接口授权   │
      │                  │                  │──────────────────►
      │                  │                  │                  │
      │  返回新密钥（仅新增时）             │                  │
      │◄──────────────────                 │                  │
      │                  │                  │                  │
```

#### 3.2.2 开放API调用流程

```
┌───────────┐      ┌───────────┐      ┌───────────┐      ┌───────────┐
│ 第三方应用 │      │ API网关    │      │   Redis   │      │   业务    │
└─────┬─────┘      └─────┬─────┘      └─────┬─────┘      └─────┬─────┘
      │                  │                  │                  │
      │  GET /open/xxx   │                  │                  │
      │  Header: X-App-Id, X-Timestamp, X-Sign                │
      │──────────────────►                 │                  │
      │                  │                  │                  │
      │                  │  1.验证时间戳   │                  │
      │                  │  2.查询应用信息 │                  │
      │                  │──────────────────►                 │
      │                  │◄──────────────────                 │
      │                  │                  │                  │
      │                  │  3.验证签名     │                  │
      │                  │  4.检查应用状态 │                  │
      │                  │  5.检查接口权限 │                  │
      │                  │                  │                  │
      │                  │                  │  转发请求       │
      │                  │─────────────────────────────────────►
      │                  │                  │                  │
      │  返回业务数据   │                  │                  │
      │◄──────────────────────────────────────────────────────│
      │                  │                  │                  │
```

---

## 4. 前端技术方案

### 4.1 技术栈

| 技术 | 版本 | 用途 |
|------|------|------|
| Vue | 3.x | 前端框架 |
| Element Plus | 2.x | UI组件库 |
| Axios | 1.x | HTTP客户端 |
| Pinia | 2.x | 状态管理 |

### 4.2 目录结构

```
pangu-ui/src/
├── api/
│   └── application.js          # 应用管理API封装
├── views/
│   └── application/
│       ├── index.vue           # 应用列表页
│       └── components/
│           ├── AppDialog.vue   # 新增/编辑弹窗
│           └── SecretDialog.vue # 密钥展示弹窗
└── mock/
    └── application.js          # Mock数据（开发阶段）
```

### 4.3 页面设计

#### 4.3.1 应用列表页 (index.vue)

**页面布局**

```
┌─────────────────────────────────────────────────────────────────┐
│ 搜索区域                                                         │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ ┌────┐ ┌────┐  │
│ │ 应用名称     │ │ 应用编码     │ │ 状态 ▼   │ │搜索│ │重置│  │
│ └──────────────┘ └──────────────┘ └──────────┘ └────┘ └────┘  │
├─────────────────────────────────────────────────────────────────┤
│ [+ 新增]                                                         │
├─────────────────────────────────────────────────────────────────┤
│ 表格区域                                                         │
│ ┌──────┬──────────┬────────────────────┬────────┬──────┬─────┐│
│ │应用名│ 应用编码  │ 授权接口            │ 状态   │ 创建 │操作 ││
│ ├──────┼──────────┼────────────────────┼────────┼──────┼─────┤│
│ │智慧  │ YY000001 │ [学校] [年级] [+3] │ ● 正常 │01-01 │编辑 ││
│ │校园  │          │                    │        │admin │重置 ││
│ │      │          │                    │        │      │删除 ││
│ └──────┴──────────┴────────────────────┴────────┴──────┴─────┘│
├─────────────────────────────────────────────────────────────────┤
│                                     共 20 条 [<] 1 2 3 ... [>]  │
└─────────────────────────────────────────────────────────────────┘
```

**核心代码结构**

```vue
<template>
  <div class="app-container">
    <!-- 搜索区域 -->
    <el-card shadow="never" class="search-wrapper">
      <el-form :model="queryParams" :inline="true">
        <el-form-item label="应用名称">
          <el-input v-model="queryParams.appName" placeholder="请输入" clearable />
        </el-form-item>
        <el-form-item label="应用编码">
          <el-input v-model="queryParams.appCode" placeholder="请输入" clearable />
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="queryParams.status" placeholder="全部" clearable>
            <el-option label="正常" value="0" />
            <el-option label="停用" value="1" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery">搜索</el-button>
          <el-button @click="resetQuery">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>

    <!-- 表格区域 -->
    <el-card shadow="never">
      <el-button type="primary" @click="handleAdd">新增</el-button>
      <el-table :data="tableData" border stripe>
        <el-table-column prop="appName" label="应用名称" />
        <el-table-column prop="appCode" label="应用编码" />
        <el-table-column prop="apis" label="授权接口">
          <template #default="{ row }">
            <el-tag v-for="api in row.apis?.slice(0, 3)" :key="api" size="small">
              {{ api }}
            </el-tag>
            <el-tag v-if="row.apis?.length > 3" type="info">+{{ row.apis.length - 3 }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="status" label="状态">
          <template #default="{ row }">
            <el-tag :type="row.status === '0' ? 'success' : 'danger'">
              {{ row.status === '0' ? '正常' : '停用' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间" />
        <el-table-column label="操作" fixed="right">
          <template #default="{ row }">
            <el-button link @click="handleEdit(row)">编辑</el-button>
            <el-button link @click="handleResetSecret(row)">重置密钥</el-button>
            <el-popconfirm title="确定删除?" @confirm="handleDelete(row)">
              <template #reference>
                <el-button link type="danger">删除</el-button>
              </template>
            </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination v-model:current-page="queryParams.pageNum" :total="total" />
    </el-card>

    <!-- 弹窗组件 -->
    <AppDialog ref="appDialogRef" @success="handleQuery" />
    <SecretDialog ref="secretDialogRef" />
  </div>
</template>
```

#### 4.3.2 新增/编辑弹窗 (AppDialog.vue)

**表单布局**

```
┌─────────────────────────────────────────────────────────────────┐
│ 新增应用 / 编辑应用                                        [X] │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   应用名称*   [________________________]                        │
│                                                                  │
│   应用编码    [保存后自动生成___________]  （只读）             │
│                                                                  │
│   应用描述    [________________________]                        │
│               [________________________]                        │
│               [________________________]                        │
│                                                                  │
│   联系人      [________________________]                        │
│                                                                  │
│   联系电话    [________________________]                        │
│                                                                  │
│   状态        [●] 正常  [○] 停用                                │
│                                                                  │
│   接口授权    [✓] 查询学生信息                                  │
│               [✓] 查询学校信息                                  │
│               [✓] 查询年级信息                                  │
│               [ ] 查询班级信息                                  │
│               [ ] 查询会员信息                                  │
│               [ ] 查询区域树                                    │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│                                        [取消]  [确定]           │
└─────────────────────────────────────────────────────────────────┘
```

**表单字段说明**

| 字段 | 类型 | 必填 | 校验规则 | 说明 |
|------|------|:----:|---------|------|
| appName | 文本 | ✓ | 最大100字符 | 应用名称 |
| appCode | 文本 | - | 只读 | 系统自动生成 |
| description | 文本域 | - | 最大500字符 | 应用描述 |
| contactPerson | 文本 | - | 最大50字符 | 联系人 |
| contactPhone | 文本 | - | 手机号格式 | 联系电话 |
| status | 开关 | ✓ | - | 0正常/1停用 |
| apiCodes | 复选框组 | - | - | 授权的接口编码列表 |

#### 4.3.3 密钥展示弹窗 (SecretDialog.vue)

**弹窗布局**

```
┌─────────────────────────────────────────────────────────────────┐
│ 应用密钥                                                   [X] │
├─────────────────────────────────────────────────────────────────┤
│  ⚠️ 请妥善保管密钥，密钥重置后旧密钥将立即失效                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   应用名称    AI智慧平台                                        │
│                                                                  │
│   应用编码    YY000001                                          │
│                                                                  │
│   应用密钥    [a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6] [复制]       │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                      [关闭]     │
└─────────────────────────────────────────────────────────────────┘
```

### 4.4 API接口封装

```javascript
/**
 * 应用管理API接口
 * @author pangu
 */
import request from '@/utils/request'

// 获取应用列表
export function listApplication(params) {
  return request.get('/api/application/list', { params })
}

// 获取应用详情
export function getApplication(appId) {
  return request.get(`/api/application/${appId}`)
}

// 新增应用
export function addApplication(data) {
  return request.post('/api/application', data)
}

// 修改应用
export function updateApplication(data) {
  return request.put('/api/application', data)
}

// 删除应用
export function deleteApplication(appId) {
  return request.delete(`/api/application/${appId}`)
}

// 重置密钥
export function resetAppSecret(appId) {
  return request.put(`/api/application/resetSecret/${appId}`)
}

// 获取API接口列表（用于授权选择）
export function getApiList() {
  return request.get('/api/application/apiList')
}
```

### 4.5 前端开发规范

| 规范项 | 说明 |
|-------|------|
| 组件命名 | 大驼峰，如 `AppDialog.vue` |
| 方法命名 | 小驼峰，如 `handleQuery`、`handleEdit` |
| 事件处理 | 以 `handle` 开头，如 `handleSubmit` |
| API请求 | 统一使用 `@/utils/request` 封装 |
| 表单验证 | 使用 Element Plus 的 Form 组件校验 |
| 错误处理 | 统一使用 `ElMessage` 提示 |

---

## 5. 后端技术方案

### 5.1 技术栈

| 技术 | 版本 | 用途 |
|------|------|------|
| Spring Boot | 3.3.x | 应用框架 |
| Spring Security | 6.x | 安全框架 |
| MyBatis Plus | 3.5.x | ORM框架 |
| Redis | 7.x | 缓存 |
| Hutool | 5.x | 工具库 |
| JDK | 17+ | 运行环境 |

### 5.2 模块结构

```
pangu-admin/
├── controller/
│   └── ApplicationController.java    # 应用管理控制器
├── service/
│   ├── IApplicationService.java      # 应用服务接口
│   └── impl/
│       └── ApplicationServiceImpl.java
├── mapper/
│   ├── ApplicationMapper.java        # 应用Mapper
│   └── AppApiMapper.java             # 应用接口授权Mapper
├── domain/
│   ├── entity/
│   │   ├── Application.java          # 应用实体
│   │   ├── AppApi.java               # 应用接口授权实体
│   │   └── ApiDict.java              # 接口字典实体
│   ├── vo/
│   │   └── ApplicationVO.java        # 应用视图对象
│   └── dto/
│       └── ApplicationDTO.java       # 应用传输对象
└── util/
    └── SecretGenerator.java          # 密钥生成工具

pangu-open/
├── controller/
│   └── OpenApiController.java        # 开放API控制器
├── interceptor/
│   └── ApiAuthInterceptor.java       # API认证拦截器
└── service/
    └── ApiAuthService.java           # API认证服务
```

### 5.3 核心类设计

#### 5.3.1 实体类 (Application.java)

```java
/**
 * 应用实体
 * @author pangu
 */
@Data
@TableName("pg_application")
public class Application extends BaseEntity {
    
    /** 应用ID */
    @TableId(type = IdType.AUTO)
    private Long appId;
    
    /** 应用编码 */
    private String appCode;
    
    /** 应用名称 */
    private String appName;
    
    /** 应用密钥（加密存储） */
    private String appSecret;
    
    /** 应用描述 */
    private String appDesc;
    
    /** 联系人 */
    private String contactPerson;
    
    /** 联系电话 */
    private String contactPhone;
    
    /** 状态（0正常 1停用） */
    private String status;
    
    /** 删除标志 */
    @TableLogic
    private String delFlag;
}
```

#### 5.3.2 服务接口 (IApplicationService.java)

```java
/**
 * 应用管理服务接口
 * @author pangu
 */
public interface IApplicationService {
    
    /**
     * 查询应用列表
     */
    List<ApplicationVO> selectApplicationList(ApplicationDTO dto);
    
    /**
     * 查询应用详情
     */
    ApplicationVO selectApplicationById(Long appId);
    
    /**
     * 新增应用
     * @return 返回应用编码和密钥
     */
    Map<String, String> insertApplication(ApplicationDTO dto);
    
    /**
     * 修改应用
     */
    int updateApplication(ApplicationDTO dto);
    
    /**
     * 删除应用
     */
    int deleteApplicationById(Long appId);
    
    /**
     * 重置密钥
     * @return 返回新密钥
     */
    String resetAppSecret(Long appId);
    
    /**
     * 根据应用编码查询应用信息（用于API认证）
     */
    Application selectByAppCode(String appCode);
    
    /**
     * 检查应用是否有接口权限
     */
    boolean checkApiPermission(String appCode, String apiPath);
}
```

#### 5.3.3 控制器 (ApplicationController.java)

```java
/**
 * 应用管理控制器
 * @author pangu
 */
@RestController
@RequestMapping("/api/application")
@PreAuthorize("hasRole('admin')")  // 仅超级管理员可访问
public class ApplicationController extends BaseController {
    
    @Autowired
    private IApplicationService applicationService;
    
    /**
     * 查询应用列表
     */
    @GetMapping("/list")
    public TableDataInfo list(ApplicationDTO dto) {
        startPage();
        List<ApplicationVO> list = applicationService.selectApplicationList(dto);
        return getDataTable(list);
    }
    
    /**
     * 获取应用详情
     */
    @GetMapping("/{appId}")
    public AjaxResult getInfo(@PathVariable Long appId) {
        return AjaxResult.success(applicationService.selectApplicationById(appId));
    }
    
    /**
     * 新增应用
     */
    @PostMapping
    @Log(title = "应用管理", businessType = BusinessType.INSERT)
    public AjaxResult add(@Validated @RequestBody ApplicationDTO dto) {
        Map<String, String> result = applicationService.insertApplication(dto);
        return AjaxResult.success(result);
    }
    
    /**
     * 修改应用
     */
    @PutMapping
    @Log(title = "应用管理", businessType = BusinessType.UPDATE)
    public AjaxResult edit(@Validated @RequestBody ApplicationDTO dto) {
        return toAjax(applicationService.updateApplication(dto));
    }
    
    /**
     * 删除应用
     */
    @DeleteMapping("/{appId}")
    @Log(title = "应用管理", businessType = BusinessType.DELETE)
    public AjaxResult remove(@PathVariable Long appId) {
        return toAjax(applicationService.deleteApplicationById(appId));
    }
    
    /**
     * 重置密钥
     */
    @PutMapping("/resetSecret/{appId}")
    @Log(title = "应用管理", businessType = BusinessType.UPDATE)
    public AjaxResult resetSecret(@PathVariable Long appId) {
        String newSecret = applicationService.resetAppSecret(appId);
        return AjaxResult.success("重置成功", Map.of("appSecret", newSecret));
    }
    
    /**
     * 获取API接口列表
     */
    @GetMapping("/apiList")
    public AjaxResult apiList() {
        return AjaxResult.success(applicationService.selectApiDictList());
    }
}
```

### 5.4 核心算法

#### 5.4.1 应用编码生成

```java
/**
 * 生成应用编码
 * 格式：YY + 6位序号（从000001开始）
 */
public String generateAppCode() {
    // 获取当前最大序号
    String maxCode = applicationMapper.selectMaxAppCode();
    int nextSeq = 1;
    if (StringUtils.isNotBlank(maxCode)) {
        nextSeq = Integer.parseInt(maxCode.substring(2)) + 1;
    }
    return String.format("YY%06d", nextSeq);
}
```

#### 5.4.2 密钥生成

```java
/**
 * 生成32位随机密钥
 */
public String generateAppSecret() {
    return RandomUtil.randomString(32);
}
```

#### 5.4.3 签名验证

```java
/**
 * 验证API请求签名
 */
public boolean verifySign(HttpServletRequest request) {
    String appId = request.getHeader("X-App-Id");
    String timestamp = request.getHeader("X-Timestamp");
    String sign = request.getHeader("X-Sign");
    
    // 1. 验证时间戳（5分钟内有效）
    long reqTime = Long.parseLong(timestamp);
    if (Math.abs(System.currentTimeMillis() - reqTime) > 5 * 60 * 1000) {
        throw new ApiException("请求已过期");
    }
    
    // 2. 获取应用密钥
    Application app = applicationService.selectByAppCode(appId);
    if (app == null || "1".equals(app.getStatus())) {
        throw new ApiException("应用不存在或已禁用");
    }
    
    // 3. 构建签名字符串
    String signStr = buildSignString(request, app.getAppSecret());
    
    // 4. 验证签名
    String expectedSign = DigestUtils.md5Hex(signStr).toUpperCase();
    return expectedSign.equals(sign);
}

/**
 * 构建签名字符串
 * 将请求参数按ASCII排序后拼接，末尾追加appSecret
 */
private String buildSignString(HttpServletRequest request, String appSecret) {
    Map<String, String> params = new TreeMap<>();
    request.getParameterMap().forEach((key, values) -> {
        if (values.length > 0) {
            params.put(key, values[0]);
        }
    });
    
    StringBuilder sb = new StringBuilder();
    params.forEach((key, value) -> {
        if (sb.length() > 0) sb.append("&");
        sb.append(key).append("=").append(value);
    });
    sb.append("&appSecret=").append(appSecret);
    
    return sb.toString();
}
```

### 5.5 缓存设计

| 缓存KEY | 过期时间 | 说明 |
|---------|---------|------|
| `app:info:{appCode}` | 30分钟 | 应用基本信息 |
| `app:apis:{appCode}` | 30分钟 | 应用授权的接口列表 |

```java
/**
 * 清除应用缓存
 * 在修改、删除、重置密钥时调用
 */
public void clearAppCache(String appCode) {
    redisCache.deleteObject("app:info:" + appCode);
    redisCache.deleteObject("app:apis:" + appCode);
}
```

---

## 6. 数据库设计

### 6.1 表结构

#### 6.1.1 应用表 (pg_application)

```sql
CREATE TABLE `pg_application` (
  `app_id` bigint NOT NULL AUTO_INCREMENT COMMENT '应用ID',
  `app_code` varchar(32) NOT NULL COMMENT '应用编码',
  `app_name` varchar(100) NOT NULL COMMENT '应用名称',
  `app_secret` varchar(64) NOT NULL COMMENT '应用密钥',
  `app_desc` varchar(500) DEFAULT NULL COMMENT '应用描述',
  `contact_person` varchar(50) DEFAULT NULL COMMENT '联系人',
  `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
  `status` char(1) DEFAULT '0' COMMENT '状态（0正常 1停用）',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志（0存在 1删除）',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`app_id`),
  UNIQUE KEY `uk_app_code` (`app_code`)
) ENGINE=InnoDB COMMENT='应用表';
```

#### 6.1.2 应用接口授权表 (pg_app_api)

```sql
CREATE TABLE `pg_app_api` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `app_id` bigint NOT NULL COMMENT '应用ID',
  `api_code` varchar(100) NOT NULL COMMENT '接口编码',
  `api_name` varchar(100) DEFAULT NULL COMMENT '接口名称',
  `api_path` varchar(200) NOT NULL COMMENT '接口路径',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_app_api` (`app_id`, `api_code`),
  KEY `idx_app_id` (`app_id`)
) ENGINE=InnoDB COMMENT='应用接口授权表';
```

#### 6.1.3 API接口字典表 (pg_api_dict)

```sql
CREATE TABLE `pg_api_dict` (
  `api_id` bigint NOT NULL AUTO_INCREMENT COMMENT '接口ID',
  `api_code` varchar(100) NOT NULL COMMENT '接口编码',
  `api_name` varchar(100) NOT NULL COMMENT '接口名称',
  `api_path` varchar(200) NOT NULL COMMENT '接口路径',
  `api_method` varchar(10) DEFAULT 'GET' COMMENT '请求方法',
  `api_desc` varchar(500) DEFAULT NULL COMMENT '接口描述',
  `order_num` int DEFAULT 0 COMMENT '显示顺序',
  `status` char(1) DEFAULT '0' COMMENT '状态（0正常 1停用）',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`api_id`),
  UNIQUE KEY `uk_api_code` (`api_code`)
) ENGINE=InnoDB COMMENT='API接口字典表';
```

### 6.2 初始化数据

```sql
-- API接口字典数据
INSERT INTO pg_api_dict (api_id, api_code, api_name, api_path, api_method, api_desc, order_num) VALUES
(1, 'STUDENT_LIST', '查询学生信息', '/open/student/list', 'GET', '获取学生列表', 1),
(2, 'SCHOOL_LIST', '查询学校信息', '/open/school/list', 'GET', '获取学校列表', 2),
(3, 'GRADE_LIST', '查询年级信息', '/open/grade/list', 'GET', '获取年级列表', 3),
(4, 'CLASS_LIST', '查询班级信息', '/open/class/list', 'GET', '获取班级列表', 4),
(5, 'MEMBER_LIST', '查询会员信息', '/open/member/list', 'GET', '获取会员列表', 5),
(6, 'REGION_TREE', '查询区域树', '/open/region/tree', 'GET', '获取区域树形结构', 6);

-- 示例应用数据
INSERT INTO pg_application (app_id, app_code, app_name, app_secret, app_desc, status, create_time) VALUES
(1, 'YY000001', 'AI智慧平台', 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6', 'AI智慧教育平台接入', '0', NOW());

-- 示例应用接口授权
INSERT INTO pg_app_api (id, app_id, api_code, api_name, api_path, create_time) VALUES
(1, 1, 'SCHOOL_LIST', '查询学校信息', '/open/school/list', NOW()),
(2, 1, 'GRADE_LIST', '查询年级信息', '/open/grade/list', NOW()),
(3, 1, 'CLASS_LIST', '查询班级信息', '/open/class/list', NOW());
```

### 6.3 索引设计

| 表名 | 索引名 | 索引字段 | 说明 |
|------|--------|---------|------|
| pg_application | uk_app_code | app_code | 应用编码唯一 |
| pg_app_api | uk_app_api | app_id, api_code | 应用接口唯一 |
| pg_app_api | idx_app_id | app_id | 按应用查询 |
| pg_api_dict | uk_api_code | api_code | 接口编码唯一 |

---

## 7. 接口设计

### 7.1 管理端接口

#### 7.1.1 查询应用列表

| 项目 | 内容 |
|------|------|
| **接口地址** | `GET /api/application/list` |
| **请求参数** | appName, appCode, status, pageNum, pageSize |
| **响应格式** | TableDataInfo（分页格式） |

#### 7.1.2 获取应用详情

| 项目 | 内容 |
|------|------|
| **接口地址** | `GET /api/application/{appId}` |
| **请求参数** | appId（路径参数） |
| **响应格式** | AjaxResult |

#### 7.1.3 新增应用

| 项目 | 内容 |
|------|------|
| **接口地址** | `POST /api/application` |
| **请求体** | ApplicationDTO |
| **响应格式** | AjaxResult（包含appCode和appSecret） |

**请求示例**

```json
{
    "appName": "AI智慧平台",
    "appDesc": "AI教育智慧平台",
    "contactPerson": "张经理",
    "contactPhone": "13812345678",
    "status": "0",
    "apiCodes": ["STUDENT_LIST", "SCHOOL_LIST", "GRADE_LIST"]
}
```

**响应示例**

```json
{
    "code": 200,
    "msg": "新增成功",
    "data": {
        "appCode": "YY000002",
        "appSecret": "x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6"
    }
}
```

#### 7.1.4 修改应用

| 项目 | 内容 |
|------|------|
| **接口地址** | `PUT /api/application` |
| **请求体** | ApplicationDTO（包含appId） |
| **响应格式** | AjaxResult |

#### 7.1.5 删除应用

| 项目 | 内容 |
|------|------|
| **接口地址** | `DELETE /api/application/{appId}` |
| **请求参数** | appId（路径参数） |
| **响应格式** | AjaxResult |

#### 7.1.6 重置密钥

| 项目 | 内容 |
|------|------|
| **接口地址** | `PUT /api/application/resetSecret/{appId}` |
| **请求参数** | appId（路径参数） |
| **响应格式** | AjaxResult（包含新的appSecret） |

**响应示例**

```json
{
    "code": 200,
    "msg": "重置成功",
    "data": {
        "appSecret": "n1e2w3s4e5c6r7e8t9k0e1y2v3a4l5u6"
    }
}
```

#### 7.1.7 获取API接口列表

| 项目 | 内容 |
|------|------|
| **接口地址** | `GET /api/application/apiList` |
| **请求参数** | 无 |
| **响应格式** | AjaxResult |

**响应示例**

```json
{
    "code": 200,
    "msg": "查询成功",
    "data": [
        { "apiCode": "STUDENT_LIST", "apiName": "查询学生信息", "apiPath": "/open/student/list" },
        { "apiCode": "SCHOOL_LIST", "apiName": "查询学校信息", "apiPath": "/open/school/list" },
        { "apiCode": "GRADE_LIST", "apiName": "查询年级信息", "apiPath": "/open/grade/list" },
        { "apiCode": "CLASS_LIST", "apiName": "查询班级信息", "apiPath": "/open/class/list" },
        { "apiCode": "MEMBER_LIST", "apiName": "查询会员信息", "apiPath": "/open/member/list" },
        { "apiCode": "REGION_TREE", "apiName": "查询区域树", "apiPath": "/open/region/tree" }
    ]
}
```

### 7.2 开放API接口

#### 7.2.1 签名规则

**请求头**

| Header | 必填 | 说明 |
|--------|:----:|------|
| X-App-Id | 是 | 应用编码 |
| X-Timestamp | 是 | 时间戳（毫秒） |
| X-Sign | 是 | 签名 |

**签名算法**

```
1. 将所有请求参数按参数名ASCII升序排序
2. 拼接成 key1=value1&key2=value2 格式
3. 末尾追加 &appSecret=xxx
4. 对整个字符串进行MD5加密（32位大写）
```

**Java签名示例**

```java
public String generateSign(Map<String, String> params, String appSecret) {
    // 1. 参数排序
    Map<String, String> sortedParams = new TreeMap<>(params);
    
    // 2. 拼接字符串
    StringBuilder sb = new StringBuilder();
    sortedParams.forEach((key, value) -> {
        if (sb.length() > 0) sb.append("&");
        sb.append(key).append("=").append(value);
    });
    sb.append("&appSecret=").append(appSecret);
    
    // 3. MD5加密
    return DigestUtils.md5Hex(sb.toString()).toUpperCase();
}
```

---

## 8. 开发计划

### 8.1 阶段划分

| 阶段 | 内容 | 交付物 |
|------|------|--------|
| **阶段一：设计评审** | 技术方案评审、接口设计评审 | 评审通过的技术方案 |
| **阶段二：后端开发** | 数据库表创建、后端接口开发 | 可调试的后端API |
| **阶段三：前端开发** | 前端页面开发、接口联调 | 可运行的前端页面 |
| **阶段四：联调测试** | 前后端联调、功能测试 | 测试报告 |
| **阶段五：验收上线** | 用户验收、部署上线 | 上线报告 |

### 8.2 开发任务清单

#### 8.2.1 后端任务

| 序号 | 任务 | 优先级 | 依赖 |
|:----:|------|:------:|------|
| 1 | 创建数据库表 | P0 | - |
| 2 | 实体类和Mapper开发 | P0 | 1 |
| 3 | Service层开发 | P0 | 2 |
| 4 | Controller层开发 | P0 | 3 |
| 5 | 应用编码生成器 | P0 | 3 |
| 6 | 密钥生成与加密 | P0 | 3 |
| 7 | API签名验证拦截器 | P0 | 3 |
| 8 | 缓存处理 | P1 | 7 |
| 9 | 单元测试 | P1 | 4 |

#### 8.2.2 前端任务

| 序号 | 任务 | 优先级 | 依赖 |
|:----:|------|:------:|------|
| 1 | API接口封装 | P0 | - |
| 2 | 列表页面开发 | P0 | 1 |
| 3 | 新增/编辑弹窗开发 | P0 | 1 |
| 4 | 密钥展示弹窗开发 | P0 | 1 |
| 5 | Mock数据开发 | P1 | 1 |
| 6 | 接口联调 | P0 | 后端4 |
| 7 | 样式优化 | P2 | 6 |

### 8.3 里程碑

| 里程碑 | 完成标准 |
|--------|---------|
| M1 设计完成 | 技术方案通过评审 |
| M2 后端完成 | 后端接口开发完成，Postman可调试 |
| M3 前端完成 | 前端页面开发完成，Mock数据可运行 |
| M4 联调完成 | 前后端联调通过，功能可正常使用 |
| M5 验收完成 | 用户验收通过，可部署上线 |

---

## 9. 测试方案

### 9.1 测试范围

| 测试类型 | 测试内容 |
|---------|---------|
| **功能测试** | 应用CRUD、接口授权、密钥重置 |
| **接口测试** | API签名验证、权限校验 |
| **性能测试** | 列表查询性能、签名验证性能 |
| **安全测试** | 密钥加密、防重放攻击 |

### 9.2 测试用例

#### 9.2.1 功能测试用例

| 用例编号 | 用例名称 | 前置条件 | 测试步骤 | 预期结果 |
|---------|---------|---------|---------|---------|
| TC-APP-001 | 新增应用 | 已登录超管账号 | 1.点击新增 2.填写表单 3.选择接口 4.提交 | 应用创建成功，显示密钥 |
| TC-APP-002 | 应用编码自动生成 | 已有应用YY000001 | 新增应用 | 新应用编码为YY000002 |
| TC-APP-003 | 重置密钥 | 已有应用 | 点击重置密钥 | 弹窗显示新密钥，可复制 |
| TC-APP-004 | 删除应用 | 已有应用 | 点击删除，确认 | 应用删除成功 |
| TC-APP-005 | 禁用应用 | 已有应用 | 编辑状态为停用 | 应用无法调用API |
| TC-APP-006 | 接口授权 | 已有应用 | 勾选部分接口 | 仅授权接口可访问 |

#### 9.2.2 接口测试用例

| 用例编号 | 用例名称 | 测试步骤 | 预期结果 |
|---------|---------|---------|---------|
| TC-API-001 | 签名正确 | 使用正确密钥生成签名 | 请求成功 |
| TC-API-002 | 签名错误 | 使用错误密钥生成签名 | 返回"签名验证失败" |
| TC-API-003 | 时间戳过期 | 使用10分钟前的时间戳 | 返回"请求已过期" |
| TC-API-004 | 应用禁用 | 使用禁用应用的密钥 | 返回"应用已禁用" |
| TC-API-005 | 无接口权限 | 访问未授权接口 | 返回"无权访问该接口" |

### 9.3 验收标准

| 验收项 | 验收标准 |
|-------|---------|
| 功能完整性 | 所有P0功能用例通过 |
| 接口安全性 | 签名验证、权限校验正常 |
| 性能指标 | 列表查询 ≤500ms，签名验证 ≤50ms |
| 代码质量 | 无严重BUG，代码规范检查通过 |

---

## 10. 风险评估

### 10.1 风险清单

| 风险编号 | 风险描述 | 影响程度 | 发生概率 | 应对措施 |
|---------|---------|:--------:|:--------:|---------|
| R01 | 密钥泄露 | 高 | 低 | 加密存储，传输加密，操作审计 |
| R02 | 签名算法被破解 | 高 | 低 | 预留算法升级能力 |
| R03 | API被恶意调用 | 中 | 中 | 频率限制，IP白名单（预留） |
| R04 | 权限变更不实时 | 低 | 中 | 使用缓存时设置合理过期时间 |

### 10.2 应对方案

#### R01 密钥泄露

- **预防**：密钥加密存储，只在新增和重置时显示一次
- **发现**：监控异常调用行为
- **处置**：支持立即重置密钥使旧密钥失效

#### R03 API被恶意调用

- **当前**：签名验证 + 时间戳防重放
- **预留**：接口频率限制（可后续添加）
- **预留**：IP白名单机制（可后续添加）

---

## 附录

### 附录A：墨刀原型对照

| 原型页面 | 对应功能 | 实现状态 |
|---------|---------|:--------:|
| 应用管理-列表 | 应用列表查询 | 待开发 |
| 应用管理-编辑 | 新增/编辑应用、接口授权 | 待开发 |

### 附录B：相关文档

| 文档名称 | 路径 |
|---------|------|
| 需求规格说明书 | docs/01-需求文档/需求规格说明书_v1.0.md |
| 系统设计文档 | docs/02-系统设计/系统设计文档_v1.0.md |
| 数据库设计文档 | docs/03-数据库设计/数据库设计文档_v1.0.md |
| 接口设计文档 | docs/04-接口文档/接口设计文档_v1.0.md |

---

## 审核签字

| 角色 | 姓名 | 日期 | 签字 |
|-----|------|------|------|
| 技术负责人 | | | |
| 前端负责人 | | | |
| 后端负责人 | | | |
| 测试负责人 | | | |

---

*文档结束*
