pangu-user-platform/docs/应用管理技术方案.md

418 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 应用管理技术方案
## 一、功能概述
应用管理模块用于管理接入盘古用户认证中心的第三方应用系统,实现应用注册、密钥管理、接口授权等功能。
## 二、数据库设计
### 2.1 ER 关系图
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ pg_application │ │ pg_app_api │ │ pg_api_dict │
│ (第三方应用表) │ 1───n │ (应用API授权表) │ n───1 │ (API接口字典表) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 2.2 表结构设计
#### 2.2.1 第三方应用表 (pg_application)
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| app_id | bigint | 是 | 应用ID主键雪花算法 |
| app_code | varchar(32) | 是 | 应用编码唯一格式YY000001 |
| app_name | varchar(100) | 是 | 应用名称 |
| app_secret | varchar(100) | 是 | 应用密钥32位UUID |
| contact_person | varchar(50) | 否 | 联系人 |
| contact_phone | varchar(20) | 否 | 联系电话 |
| status | char(1) | 是 | 状态0正常 1停用 |
| tenant_id | varchar(20) | 是 | 租户编号 |
| create_dept | bigint | 否 | 创建部门 |
| create_by | bigint | 否 | 创建者 |
| create_time | datetime | 否 | 创建时间 |
| update_by | bigint | 否 | 更新者 |
| update_time | datetime | 否 | 更新时间 |
| del_flag | char(1) | 是 | 删除标志0存在 1删除 |
| remark | varchar(500) | 否 | 备注 |
**索引**
- PRIMARY KEY (`app_id`)
- UNIQUE KEY `uk_app_code` (`app_code`)
#### 2.2.2 API接口字典表 (pg_api_dict)
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| api_id | bigint | 是 | API ID主键 |
| api_code | varchar(50) | 是 | API编码唯一 |
| api_name | varchar(100) | 是 | API名称 |
| api_path | varchar(200) | 是 | API路径 |
| api_method | varchar(10) | 是 | 请求方法GET/POST/PUT/DELETE |
| api_desc | varchar(500) | 否 | API描述 |
| status | char(1) | 是 | 状态0正常 1停用 |
| order_num | int | 是 | 排序号 |
**索引**
- PRIMARY KEY (`api_id`)
- UNIQUE KEY `uk_api_code` (`api_code`)
#### 2.2.3 应用API授权表 (pg_app_api)
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | bigint | 是 | 主键 |
| app_id | bigint | 是 | 应用ID |
| api_id | bigint | 是 | API ID |
| create_time | datetime | 否 | 创建时间 |
**索引**
- PRIMARY KEY (`id`)
- UNIQUE KEY `uk_app_api` (`app_id`, `api_id`)
### 2.3 SQL 脚本
```sql
-- 第三方应用表
DROP TABLE IF EXISTS `pg_application`;
CREATE TABLE `pg_application` (
`app_id` bigint NOT NULL COMMENT '应用ID',
`app_code` varchar(32) NOT NULL COMMENT '应用编码',
`app_name` varchar(100) NOT NULL COMMENT '应用名称',
`app_secret` varchar(100) NOT NULL COMMENT '应用密钥',
`contact_person` varchar(50) DEFAULT NULL COMMENT '联系人',
`contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
`status` char(1) DEFAULT '0' COMMENT '状态0正常 1停用',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
`create_by` bigint DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint DEFAULT NULL 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='第三方应用表';
-- API接口字典表
DROP TABLE IF EXISTS `pg_api_dict`;
CREATE TABLE `pg_api_dict` (
`api_id` bigint NOT NULL COMMENT 'API ID',
`api_code` varchar(50) NOT NULL COMMENT 'API编码',
`api_name` varchar(100) NOT NULL COMMENT 'API名称',
`api_path` varchar(200) NOT NULL COMMENT 'API路径',
`api_method` varchar(10) DEFAULT 'GET' COMMENT '请求方法',
`api_desc` varchar(500) DEFAULT NULL COMMENT 'API描述',
`status` char(1) DEFAULT '0' COMMENT '状态0正常 1停用',
`order_num` int DEFAULT 0 COMMENT '排序',
PRIMARY KEY (`api_id`),
UNIQUE KEY `uk_api_code` (`api_code`)
) ENGINE=InnoDB COMMENT='API接口字典表';
-- 应用API授权表
DROP TABLE IF EXISTS `pg_app_api`;
CREATE TABLE `pg_app_api` (
`id` bigint NOT NULL COMMENT '主键',
`app_id` bigint NOT NULL COMMENT '应用ID',
`api_id` bigint NOT NULL COMMENT 'API ID',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_app_api` (`app_id`, `api_id`)
) ENGINE=InnoDB COMMENT='应用API授权表';
-- 菜单数据
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time)
VALUES (2300, '应用管理', 0, 4, 'application', 'business/application/index', '', 1, 0, 'C', '0', '0', 'business:application:list', 'component', 103, 1, NOW());
INSERT INTO sys_menu VALUES (2301, '应用查询', 2300, 1, '', '', '', 1, 0, 'F', '0', '0', 'business:application:query', '#', 103, 1, NOW(), NULL, NULL, '');
INSERT INTO sys_menu VALUES (2302, '应用新增', 2300, 2, '', '', '', 1, 0, 'F', '0', '0', 'business:application:add', '#', 103, 1, NOW(), NULL, NULL, '');
INSERT INTO sys_menu VALUES (2303, '应用修改', 2300, 3, '', '', '', 1, 0, 'F', '0', '0', 'business:application:edit', '#', 103, 1, NOW(), NULL, NULL, '');
INSERT INTO sys_menu VALUES (2304, '应用删除', 2300, 4, '', '', '', 1, 0, 'F', '0', '0', 'business:application:remove', '#', 103, 1, NOW(), NULL, NULL, '');
INSERT INTO sys_menu VALUES (2305, '重置密钥', 2300, 5, '', '', '', 1, 0, 'F', '0', '0', 'business:application:resetSecret', '#', 103, 1, NOW(), NULL, NULL, '');
INSERT INTO sys_menu VALUES (2306, '接口授权', 2300, 6, '', '', '', 1, 0, 'F', '0', '0', 'business:application:api', '#', 103, 1, NOW(), NULL, NULL, '');
-- API字典初始数据
INSERT INTO pg_api_dict (api_id, api_code, api_name, api_path, api_method, api_desc, status, order_num)
VALUES (1700000000000000001, 'OPEN_STUDENT_LIST', '学生列表', '/open/api/student/list', 'GET', '开放API-学生列表分页查询', '0', 10);
```
## 三、后端代码结构
### 3.1 目录结构
```
pangu-modules/pangu-business/src/main/java/org/dromara/pangu/application/
├── controller/
│ └── PgApplicationController.java # 应用管理控制器
├── domain/
│ ├── PgApplication.java # 第三方应用实体
│ ├── PgApiDict.java # API字典实体
│ └── PgAppApi.java # 应用API授权实体
├── mapper/
│ ├── PgApplicationMapper.java # 应用Mapper
│ ├── PgApiDictMapper.java # API字典Mapper
│ └── PgAppApiMapper.java # 应用API授权Mapper
└── service/
├── IPgApplicationService.java # 应用服务接口
├── IPgApiDictService.java # API字典服务接口
└── impl/
├── PgApplicationServiceImpl.java # 应用服务实现
└── PgApiDictServiceImpl.java # API字典服务实现
```
### 3.2 API 接口列表
| 接口 | 方法 | 路径 | 权限标识 | 说明 |
|------|------|------|----------|------|
| 应用列表 | GET | /business/application/list | business:application:list | 分页查询应用列表 |
| 应用详情 | GET | /business/application/{appId} | business:application:query | 获取应用详情 |
| 新增应用 | POST | /business/application | business:application:add | 新增第三方应用 |
| 修改应用 | PUT | /business/application | business:application:edit | 修改应用信息 |
| 删除应用 | DELETE | /business/application/{appIds} | business:application:remove | 删除应用 |
| 重置密钥 | PUT | /business/application/resetSecret/{appId} | business:application:edit | 重置应用密钥 |
| 接口列表 | GET | /business/application/apiList | business:application:list | 获取可授权的API列表 |
### 3.3 核心代码
#### 3.3.1 控制器 (PgApplicationController.java)
```java
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/business/application")
public class PgApplicationController extends BaseController {
private final IPgApplicationService applicationService;
private final IPgApiDictService apiDictService;
@SaCheckPermission("business:application:list")
@GetMapping("/list")
public TableDataInfo<PgApplication> list(PgApplication app, PageQuery pageQuery) {
return applicationService.selectPageList(app, pageQuery);
}
@SaCheckPermission("business:application:edit")
@Log(title = "应用管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetSecret/{appId}")
public R<PgApplication> resetSecret(@PathVariable Long appId) {
PgApplication app = applicationService.selectById(appId);
if (app == null) {
return R.fail("应用不存在");
}
String newSecret = IdUtil.fastSimpleUUID();
app.setAppSecret(newSecret);
applicationService.update(app);
return R.ok(app);
}
}
```
#### 3.3.2 服务实现 (PgApplicationServiceImpl.java)
```java
@RequiredArgsConstructor
@Service
public class PgApplicationServiceImpl implements IPgApplicationService {
private final PgApplicationMapper baseMapper;
private final PgAppApiMapper appApiMapper;
private final PgApiDictMapper apiDictMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public int insert(PgApplication app) {
// 自动生成应用编码格式YY000001
String appCode = generateAppCode();
app.setAppCode(appCode);
// 自动生成32位密钥
app.setAppSecret(IdUtil.fastSimpleUUID());
int rows = baseMapper.insert(app);
// 保存接口授权
if (rows > 0 && app.getApiCodes() != null && !app.getApiCodes().isEmpty()) {
saveAppApis(app.getAppId(), app.getApiCodes());
}
return rows;
}
private String generateAppCode() {
// 查询当前最大编码,生成下一个序号
// 格式YY + 6位数字如 YY000001
}
@Override
public boolean checkApiPermission(String appCode, String apiPath) {
// 校验应用是否有访问该接口的权限
List<String> paths = appApiMapper.selectApiPathsByAppCode(appCode);
return paths != null && paths.contains(apiPath);
}
}
```
#### 3.3.3 实体类 (PgApplication.java)
```java
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("pg_application")
public class PgApplication extends BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long appId;
private String appCode;
private String appName;
private String appSecret;
private String contactPerson;
private String contactPhone;
private String status;
private String tenantId;
@TableLogic
private String delFlag;
private String remark;
/** 接口授权API编码列表不落库 */
@TableField(exist = false)
private List<String> apiCodes;
}
```
## 四、前端代码结构
### 4.1 目录结构
```
frontend/src/
├── api/pangu/
│ └── application.js # API接口定义
└── views/application/
├── index.vue # 应用列表页面
└── components/
├── AppDialog.vue # 新增/编辑弹窗
└── SecretDialog.vue # 密钥展示弹窗
```
### 4.2 API 接口 (application.js)
```javascript
import request from '@/utils/request'
// 获取应用列表
export function getApplicationList(params) {
return request({ url: '/business/application/list', method: 'get', params })
}
// 新增应用
export function addApplication(data) {
return request({ url: '/business/application', method: 'post', data })
}
// 修改应用
export function updateApplication(data) {
return request({ url: '/business/application', method: 'put', data })
}
// 删除应用
export function deleteApplication(appId) {
return request({ url: `/business/application/${appId}`, method: 'delete' })
}
// 重置应用密钥
export function resetAppSecret(appId) {
return request({ url: `/business/application/resetSecret/${appId}`, method: 'put' })
}
// 获取接口授权选项列表
export function getApiAuthOptions() {
return request({ url: '/business/application/apiList', method: 'get' })
}
```
### 4.3 页面组件
#### 列表页面 (index.vue)
- 搜索条件:应用名称、应用编码、状态
- 列表字段:应用名称、应用编码、授权接口、状态、创建时间、创建人
- 操作按钮:新增、编辑、重置密钥、删除
#### 新增/编辑弹窗 (AppDialog.vue)
- 表单字段:应用名称、应用描述、联系人、联系电话、状态、接口授权
- 接口授权:多选框组,从 API 字典加载
## 五、业务流程
### 5.1 新增应用流程
```
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 填写表单 │───>│ 提交保存 │───>│ 生成编码 │───>│ 生成密钥 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │
v v
┌──────────────────────────┐
│ 保存应用 + 接口授权 │
└──────────────────────────┘
```
### 5.2 第三方调用接口流程
```
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 携带凭证 │───>│ 拦截校验 │───>│ 权限检查 │───>│ 执行业务 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │
│ v v
│ 校验AppCode 校验该应用
│ 和AppSecret 是否有权限
│ 访问该接口
v
请求头携带:
X-App-Code
X-App-Secret
X-Timestamp
X-Sign
```
## 六、文件清单
### 6.1 后端文件
| 文件路径 | 说明 |
|----------|------|
| `pangu-business/src/.../application/controller/PgApplicationController.java` | 应用管理控制器 |
| `pangu-business/src/.../application/domain/PgApplication.java` | 第三方应用实体 |
| `pangu-business/src/.../application/domain/PgApiDict.java` | API字典实体 |
| `pangu-business/src/.../application/domain/PgAppApi.java` | 应用API授权实体 |
| `pangu-business/src/.../application/mapper/PgApplicationMapper.java` | 应用Mapper |
| `pangu-business/src/.../application/mapper/PgApiDictMapper.java` | API字典Mapper |
| `pangu-business/src/.../application/mapper/PgAppApiMapper.java` | 应用API授权Mapper |
| `pangu-business/src/.../application/service/IPgApplicationService.java` | 应用服务接口 |
| `pangu-business/src/.../application/service/IPgApiDictService.java` | API字典服务接口 |
| `pangu-business/src/.../application/service/impl/PgApplicationServiceImpl.java` | 应用服务实现 |
| `pangu-business/src/.../application/service/impl/PgApiDictServiceImpl.java` | API字典服务实现 |
### 6.2 前端文件
| 文件路径 | 说明 |
|----------|------|
| `frontend/src/api/pangu/application.js` | API接口定义 |
| `frontend/src/views/application/index.vue` | 应用列表页面 |
| `frontend/src/views/application/components/AppDialog.vue` | 新增/编辑弹窗 |
| `frontend/src/views/application/components/SecretDialog.vue` | 密钥展示弹窗 |
### 6.3 SQL 文件
| 文件路径 | 说明 |
|----------|------|
| `pangu-business/sql/pangu_tables.sql` | 表结构包含pg_application、pg_api_dict、pg_app_api |
| `pangu-business/sql/pangu_menu.sql` | 菜单数据 |
| `pangu-business/sql/open_api_dict_data.sql` | API字典初始数据 |
---
*文档版本v1.0*
*更新日期2026-02-04*
*作者pangu*