# 开放接口实现说明 ## 📋 实现概述 已按照方案一(独立 OpenApi Service 层)完成了开放接口的完整实现,包括后端业务逻辑和前端管理界面。 --- ## ✅ 已完成的工作 ### 1️⃣ 后端实现 #### 核心文件清单 | 文件路径 | 说明 | 状态 | |---------|------|------| | `openapi/utils/DataMaskUtil.java` | 数据脱敏工具类 | ✅ 新建 | | `openapi/domain/vo/OpenStudentVo.java` | 开放接口学生VO | ✅ 已优化 | | `openapi/service/IOpenApiStudentService.java` | 开放接口服务接口 | ✅ 已存在 | | `openapi/service/impl/OpenApiStudentServiceImpl.java` | 开放接口服务实现 | ✅ 已完善 | | `openapi/controller/OpenApiStudentController.java` | 开放接口控制器 | ✅ 已更新 | #### 实现细节 **1. DataMaskUtil 工具类** ```java 位置:org.dromara.pangu.openapi.utils.DataMaskUtil 提供的脱敏方法: - maskName() // 姓名脱敏:张三 -> 张* - maskPhone() // 手机号脱敏:13812345678 -> 138****5678 - maskIdCard() // 身份证脱敏:110101199001011234 -> 110101********1234 - maskEmail() // 邮箱脱敏:example@qq.com -> e****e@qq.com - maskAddress() // 地址脱敏:保留省市区 ``` **2. OpenApiStudentServiceImpl 服务实现** 核心逻辑: ``` 1. 调用原有业务服务(IPgStudentService)获取数据 2. 在 Service 层进行数据转换(StudentVo -> OpenStudentVo) 3. 使用 DataMaskUtil 对敏感字段进行脱敏 4. 敏感字段不返回(身份证号、住址、会员信息等) 5. 返回开放接口专用 VO ``` **3. OpenApiStudentController 控制器** 接口定义: ``` GET /open/api/student/list 查询参数: - studentName: 学生姓名(模糊查询) - schoolId: 学校ID - gradeId: 年级ID - classId: 班级ID - pageNum: 页码 - pageSize: 每页条数 返回数据: { "code": 200, "msg": "操作成功", "rows": [ { "studentId": 1, "studentCode": "S2024001", "studentName": "张*", // 已脱敏 "gender": "1", "schoolId": 1, "schoolName": "第一小学", "gradeId": 1, "gradeName": "三年级", "classId": 1, "className": "1班" } ], "total": 100 } ``` #### 技术亮点 1. **分层清晰** - Controller 只负责接收请求 - Service 处理业务逻辑(脱敏、转换) - 复用原有 Service,避免重复代码 2. **数据安全** - 敏感字段脱敏(姓名使用 maskName) - 高敏感字段不返回(身份证、住址等) - 使用专用 VO,与内部数据结构解耦 3. **易于扩展** - 新增开放接口只需: - 创建专用 VO - 创建 Service 接口和实现 - 创建 Controller - DataMaskUtil 可复用 --- ### 2️⃣ 前端实现 #### 核心文件清单 | 文件路径 | 说明 | 状态 | |---------|------|------| | `views/application/index.vue` | 应用管理主页面 | ✅ 已优化 | | `views/application/components/AppDialog.vue` | 新增/编辑弹窗 | ✅ 已优化 | | `views/application/components/SecretDialog.vue` | 密钥展示弹窗 | ✅ 已存在 | | `api/pangu/application.js` | 应用管理API | ✅ 已存在 | #### 功能实现 **1. 应用列表页面** 功能点: - ✅ 搜索:应用名称、应用编码、状态 - ✅ 列表展示:应用信息、授权接口、状态 - ✅ 新增应用 - ✅ 编辑应用 - ✅ 查看密钥 - ✅ 重置密钥(需二次确认) - ✅ 删除应用(需二次确认) - ✅ 分页功能 **2. 新增/编辑弹窗** 表单字段: - 应用名称(必填) - 应用编码(自动生成) - 应用描述 - 联系人 - 联系电话(手机号格式校验) - 状态(正常/停用) - 接口授权(多选框,展示接口名称、路径、描述) 特性: - ✅ 表单校验 - ✅ 接口授权选项动态加载 - ✅ 新增成功后自动显示密钥 - ✅ 编辑时回显数据 **3. 密钥展示弹窗** 功能: - 展示应用名称、编码、密钥 - 一键复制密钥 - 安全提示(密钥重置后旧密钥失效) --- ## 🔍 使用示例 ### 第三方应用调用示例 **1. Java 调用** ```java // 1. 准备请求参数 Map params = new TreeMap<>(); params.put("pageNum", "1"); params.put("pageSize", "10"); params.put("studentName", "张"); // 2. 计算签名 String timestamp = String.valueOf(System.currentTimeMillis()); StringBuilder signStr = new StringBuilder(); params.forEach((k, v) -> signStr.append(k).append("=").append(v).append("&")); signStr.append("appSecret=").append(APP_SECRET); String sign = DigestUtils.md5Hex(signStr.toString()).toUpperCase(); // 3. 发送请求 HttpRequest request = HttpRequest.get(BASE_URL + "/open/api/student/list") .header("X-App-Id", APP_CODE) .header("X-Timestamp", timestamp) .header("X-Sign", sign) .form(params); String response = request.execute().body(); ``` **2. Python 调用** ```python import hashlib import time import requests # 1. 准备参数 params = { "pageNum": "1", "pageSize": "10", "studentName": "张" } # 2. 计算签名 timestamp = str(int(time.time() * 1000)) sorted_params = sorted(params.items()) sign_str = "&".join([f"{k}={v}" for k, v in sorted_params]) sign_str += f"&appSecret={APP_SECRET}" sign = hashlib.md5(sign_str.encode()).hexdigest().upper() # 3. 发送请求 headers = { "X-App-Id": APP_CODE, "X-Timestamp": timestamp, "X-Sign": sign } response = requests.get( f"{BASE_URL}/open/api/student/list", params=params, headers=headers ) print(response.json()) ``` --- ## 🧪 测试建议 ### 后端测试 1. **单元测试 DataMaskUtil** ```java @Test public void testMaskName() { assertEquals("张*", DataMaskUtil.maskName("张三")); assertEquals("李**", DataMaskUtil.maskName("李四五")); } ``` 2. **集成测试 OpenApiStudentService** - 测试分页查询 - 测试数据脱敏是否生效 - 测试敏感字段是否被过滤 3. **接口测试 OpenApiStudentController** - 使用 Postman 或 curl 测试 - 验证鉴权是否生效 - 验证返回数据格式 ### 前端测试 1. **功能测试** - 应用列表加载 - 新增应用(含接口授权) - 编辑应用 - 查看密钥 - 重置密钥 - 删除应用 2. **表单校验** - 必填项校验 - 手机号格式校验 --- ## 📝 后续扩展指南 ### 新增开放接口(以教师接口为例) **步骤一:创建专用 VO** ```java // openapi/domain/vo/OpenTeacherVo.java @Data @Schema(description = "开放接口教师信息") public class OpenTeacherVo implements Serializable { private Long teacherId; private String teacherName; // 已脱敏 private String teacherCode; private String schoolName; // 敏感字段不暴露 } ``` **步骤二:创建 Service 接口** ```java // openapi/service/IOpenApiTeacherService.java public interface IOpenApiTeacherService { TableDataInfo selectPageList( String teacherName, Long schoolId, PageQuery pageQuery ); } ``` **步骤三:创建 Service 实现** ```java // openapi/service/impl/OpenApiTeacherServiceImpl.java @Service @RequiredArgsConstructor public class OpenApiTeacherServiceImpl implements IOpenApiTeacherService { private final IPgTeacherService teacherService; // 复用原有服务 @Override public TableDataInfo selectPageList(...) { // 1. 调用原有服务 // 2. 转换并脱敏 // 3. 返回 } private OpenTeacherVo convertToOpenApiVo(TeacherVo source) { // 使用 DataMaskUtil 进行脱敏 } } ``` **步骤四:创建 Controller** ```java // openapi/controller/OpenApiTeacherController.java @RestController @RequestMapping("/open/api/teacher") public class OpenApiTeacherController { private final IOpenApiTeacherService openApiTeacherService; @GetMapping("/list") public TableDataInfo list(...) { return openApiTeacherService.selectPageList(...); } } ``` **步骤五:添加接口字典** ```sql INSERT INTO pg_api_dict (api_id, api_code, api_name, api_path, api_method, api_desc, status, order_num) VALUES (1700000000000000002, 'OPEN_TEACHER_LIST', '教师列表查询', '/open/api/teacher/list', 'GET', '分页查询教师信息', '0', 20); ``` **步骤六:在应用管理中授权** 前端应用管理页面 -> 编辑应用 -> 勾选"教师列表查询"接口 --- ## 🎯 核心优势总结 1. **安全可控** - 五重验证机制(参数、时间戳、应用状态、签名、接口权限) - 敏感数据脱敏 - 高敏感字段不返回 2. **易于扩展** - Service 层可复用 - DataMaskUtil 可复用 - 新增接口遵循统一规范 3. **便于维护** - 分层清晰 - 代码解耦 - 文档完善 4. **用户友好** - 前端界面直观 - 接口授权可视化 - 密钥管理便捷 --- ## 📞 技术支持 如有问题,请参考: 1. 需求与技术设计方案文档 2. 代码注释 3. 本实现说明文档 --- *实现完成时间:2026-02-04* *实现人员:pangu*