Files
hzdyjs/.cursor/cursorrules-api-test.md
T
zlwu 249b1dfeb4 feat: 完善项目开发规范和测试文档体系
📝 主要改进:
• Cursor配置模块化:拆分为前端、后端、移动端、文档、API测试5个专门规范文件
• API文档重新组织:按业务模块拆分到docs/开发文档/API/目录下
• 新增维修分类功能完整测试方案和自动化测试脚本
• 后端热启动脚本完善,支持Linux/Windows双平台
• 强化文档同步更新规范,确保代码与文档一致性

🔧 技术改进:
• 优化Cursor AI工作流程和代码生成质量
• 完善API测试规范(AES+RSA双重加密登录方法)
• 提升开发效率和代码质量标准化程度

�� 测试覆盖:
• 维修分类管理功能端到端测试验证通过
• 包含页面加载、CRUD操作、搜索功能等核心测试用例

更新时间:2025-07-01
2025-07-01 17:53:05 +08:00

683 lines
20 KiB
Markdown
Raw 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.
# Cursor Rules - API 测试和登录方法
## 适用范围
- **测试环境**: http://localhost/dev-api
- **登录认证**: AES+RSA 双重加密
- **Token 管理**: JWT 认证体系
---
# API 测试和登录方法
## 1. 🔐 登录方法配置
### 系统登录接口分析
**接口地址**: `POST http://localhost/dev-api/auth/login`
**当前状态**:
- ✅ **接口技术实现**: 登录接口正常工作,能够正确处理请求
- ✅ **接口地址确认**: 接口地址 http://localhost/dev-api/auth/login 可正常访问
- ✅ **权限配置问题**: 已解决,admin 用户可以正常登录获取 Token
- ✅ **加密登录问题**: 已解决,AES+RSA 双重加密登录正常工作
- ✅ **Token 验证**: Token 有效期 7 天,可正常用于 API 调用
- ✅ **用户权限**: admin 用户具有超级管理员权限 (_:_:\*)
### 🔧 可用的登录方法
#### 方法 1:AES+RSA 加密登录(推荐)
```javascript
// 完整的AES+RSA双重加密登录方法
async function encryptedLogin() {
const axios = require('axios');
const CryptoJS = require('crypto-js');
const NodeRSA = require('node-rsa');
// 配置信息
const CONFIG = {
url: 'http://localhost/dev-api/auth/login',
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
tenantId: '000000',
referer: 'http://localhost/login?redirect=%252Findex',
rsaKey: 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
};
// 生成32位随机AES密钥
function generateAESKey() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < 32; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// AES加密
function aesEncrypt(data, key) {
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
return CryptoJS.AES.encrypt(data, keyUtf8, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}).toString();
}
// RSA加密AES密钥
function rsaEncrypt(aesKey) {
const pemKey = `-----BEGIN PUBLIC KEY-----\n${CONFIG.rsaKey}\n-----END PUBLIC KEY-----`;
const rsaKey = new NodeRSA(pemKey);
rsaKey.setOptions({ encryptionScheme: 'pkcs1' });
const aesKeyBase64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(aesKey));
return rsaKey.encrypt(aesKeyBase64, 'base64');
}
// 生成AES密钥并加密登录数据
const aesKey = generateAESKey();
const loginData = {
tenantId: CONFIG.tenantId,
username: 'admin',
password: 'admin123',
clientId: CONFIG.clientId,
grantType: 'password',
};
const encryptedData = aesEncrypt(JSON.stringify(loginData), aesKey);
const encryptedKey = rsaEncrypt(aesKey);
// 设置请求头(按照系统要求的格式)
const headers = {
'Content-Type': 'application/json;charset=utf-8',
clientid: CONFIG.clientId, // 必需:客户端ID
'encrypt-key': encryptedKey, // 必需:RSA加密的AES密钥
referer: CONFIG.referer, // 必需:来源页面
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
isToken: 'false',
isEncrypt: 'true',
repeatSubmit: 'false',
};
// 发送登录请求
const response = await axios.post(CONFIG.url, encryptedData, {
headers: headers,
timeout: 10000,
});
// 返回Token
if (response.data.code === 200 && response.data.data?.access_token) {
return response.data.data.access_token;
} else {
throw new Error(response.data.msg || 'Login failed');
}
}
```
#### 方法 2:快速 Token 获取(简化版)
```javascript
// 快速获取Token的简化版本
async function getToken(username = 'admin', password = 'admin123') {
const { getToken } = require('./test/quick-token-getter');
return await getToken(username, password);
}
```
### 🔧 快速测试工具
#### 使用 test 目录下的测试脚本
```bash
# 在test目录下运行测试
cd test
npm install # 安装依赖(axios, crypto-js, node-rsa, colors)
# 完整功能版登录测试(推荐)
node custom-login-script.js
# 快速Token获取测试
node quick-token-getter.js
```
#### 可用的登录脚本
- **`custom-login-script.js`** - 完整功能版,支持 AES+RSA 双重加密
- **`quick-token-getter.js`** - 快速获取版,代码简洁易懂
#### 依赖安装
```json
{
"dependencies": {
"axios": "^1.7.2",
"crypto-js": "^4.2.0",
"node-rsa": "^1.1.1",
"colors": "^1.4.0"
}
}
```
#### 🎯 成功获取的 Token 示例
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJzeXNfdXNlcjoxIiwicm5TdHIiOiJ5MlhvbHRnd1Y2YjUzRnRMVzF4SFR1anRSUERjeGdmYyIsImNsaWVudGlkIjoiZTVjZDdlNDg5MWJmOTVkMWQxOTIwNmNlMjRhN2IzMmUiLCJ0ZW5hbnRJZCI6IjAwMDAwMCIsInVzZXJJZCI6MSwidXNlck5hbWUiOiJhZG1pbiIsImRlcHRJZCI6MTAzLCJkZXB0TmFtZSI6IuaZuuiDveaOp-WItuWtpumZoiIsImRlcHRDYXRlZ29yeSI6IiIsIm5pY2tOYW1lIjoi55ax54uC55qE54uu5a2QTGkifQ.WI6iY3AgwPz7ceI8WDsVy-GaTXZnU-JKUYUTjhQdoKM
```
**Token 解析信息**:
- **用户 ID**: 1
- **用户名**: admin
- **昵称**: 疯狂狮子 Li
- **部门**: 智胜技术开发团队
- **权限**: _:_:\* (超级管理员)
- **有效期**: 7 天
- **租户**: 000000
## 2. ✅ 已解决的问题和解决方案
### 已解决问题 1:国际化语言头格式问题
- **原问题**: ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
- **原因**: 服务器端国际化解析器无法正确处理 `'Content-Language': 'zh-cn'` 格式
- **解决方案**:
- 使用 `'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'` 格式
- 或使用标准的 `'Content-Language': 'zh-CN'` 格式
- **状态**: ✅ 已完全解决
### 已解决问题 2:加密登录技术问题
- **原问题**: AES+RSA 双重加密登录存在技术错误
- **原因**: 加密密钥生成和请求头格式不正确
- **解决方案**:
- 使用正确的 32 位随机 AES 密钥生成
- 按照系统要求设置三个关键 Header:clientid、encrypt-key、referer
- 使用正确的 RSA-PKCS1 加密方案
- **状态**: ✅ 已完全解决
### 核心成功要素
1. **必需的 Header 格式**:
```javascript
{
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
'encrypt-key': '[RSA加密的AES密钥]',
'referer': 'http://localhost/login?redirect=%252Findex'
}
```
2. **正确的加密流程**:
- 生成 32 位随机 AES 密钥
- 使用 AES-ECB 模式加密登录数据
- 使用 RSA-PKCS1 加密 AES 密钥
- 设置正确的请求头格式
## 3. 🔍 API 测试最佳实践
### 测试环境配置
```javascript
const config = {
baseURL: 'http://localhost/dev-api',
timeout: 10000,
headers: {
'Content-Type': 'application/json;charset=utf-8',
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
referer: 'http://localhost/login?redirect=%252Findex',
},
};
```
### 通用 API 测试方法
```javascript
// 通用API测试方法
async function testAPI(endpoint, data, options = {}) {
const axios = require('axios');
try {
const response = await axios({
method: options.method || 'POST',
url: `http://localhost/dev-api${endpoint}`,
data: data,
headers: {
'Content-Type': 'application/json;charset=utf-8',
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
referer: 'http://localhost/login?redirect=%252Findex',
Authorization: options.token ? `Bearer ${options.token}` : undefined,
...options.headers,
},
timeout: 10000,
});
return {
success: true,
data: response.data,
status: response.status,
};
} catch (error) {
return {
success: false,
error: error.response?.data || error.message,
status: error.response?.status,
};
}
}
```
### 带 Token 的 API 调用
```javascript
// 带Token的API调用示例
async function callAPIWithToken(token, endpoint, data, method = 'GET') {
return await testAPI(endpoint, data, {
method: method,
token: token,
headers: {
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
referer: 'http://localhost/login?redirect=%252Findex',
},
});
}
// 使用示例
async function getUserInfo(token) {
const result = await callAPIWithToken(token, '/system/user/getInfo', null, 'GET');
return result;
}
// 调用带参数的API
async function getUserList(token, params) {
const result = await callAPIWithToken(token, '/system/user/list', params, 'GET');
return result;
}
```
### 分页接口测试
```javascript
// 分页接口测试模板
async function testPaginationAPI(token, endpoint, params = {}) {
const defaultParams = {
pageNum: 1,
pageSize: 10,
...params,
};
const result = await callAPIWithToken(token, endpoint, defaultParams, 'GET');
if (result.success) {
console.log('分页接口测试成功:');
console.log(`总记录数: ${result.data.total}`);
console.log(`当前页数据: ${result.data.rows?.length || 0} 条`);
console.log('响应格式正确 ✅');
} else {
console.log('分页接口测试失败:', result.error);
}
return result;
}
// 使用示例
async function testUserListAPI(token) {
return await testPaginationAPI(token, '/system/user/list', {
userName: 'admin',
status: '0',
});
}
```
### CRUD 接口测试套件
```javascript
// CRUD接口完整测试套件
async function testCRUDAPIs(token, baseEndpoint, testData) {
const results = {
create: null,
read: null,
update: null,
delete: null,
};
try {
// 1. 测试创建 (CREATE)
console.log('测试创建接口...');
results.create = await callAPIWithToken(token, baseEndpoint, testData, 'POST');
if (results.create.success) {
console.log('✅ 创建接口测试通过');
const createdId = results.create.data.data?.id;
// 2. 测试读取 (READ)
if (createdId) {
console.log('测试读取接口...');
results.read = await callAPIWithToken(
token,
`${baseEndpoint}/${createdId}`,
null,
'GET',
);
if (results.read.success) {
console.log('✅ 读取接口测试通过');
// 3. 测试更新 (UPDATE)
console.log('测试更新接口...');
const updateData = {
...testData,
id: createdId,
name: testData.name + '_updated',
};
results.update = await callAPIWithToken(token, baseEndpoint, updateData, 'PUT');
if (results.update.success) {
console.log('✅ 更新接口测试通过');
}
// 4. 测试删除 (DELETE)
console.log('测试删除接口...');
results.delete = await callAPIWithToken(
token,
`${baseEndpoint}/${createdId}`,
null,
'DELETE',
);
if (results.delete.success) {
console.log('✅ 删除接口测试通过');
}
}
}
}
} catch (error) {
console.error('CRUD测试出现异常:', error);
}
return results;
}
```
## 4. 📝 API 测试记录模板
### 测试结果记录格式
```markdown
## API 测试记录 - YYYY-MM-DD
**测试环境**: http://localhost/dev-api
**测试时间**: YYYY-MM-DD HH:mm:ss
**测试人员**: [测试人员]
### 登录测试
- **方法**: AES+RSA 双重加密登录
- **结果**: ✅ 登录成功
- **Token**: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
- **用户信息**: admin / 疯狂狮子 Li / 超级管理员
- **权限**: _:_:\* (完全权限)
- **有效期**: 7 天
### 业务 API 测试
- **接口**: [接口地址]
- **方法**: [HTTP 方法]
- **权限**: [需要的权限]
- **结果**: [测试结果]
- **响应时间**: [响应时间 ms]
- **状态码**: [HTTP 状态码]
### 成功要素记录
1. **Header 配置**: 正确设置 clientid、encrypt-key、referer
2. **加密机制**: AES+RSA 双重加密正常工作
3. **语言头格式**: 使用 Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
### 验证结果
- ✅ 登录接口正常工作
- ✅ Token 有效可用于 API 调用
- ✅ 用户权限验证通过
- ✅ 加密解密机制正常
```
### 详细测试报告模板
```markdown
# API 接口测试报告
## 🎯 测试概览
**测试时间**: YYYY-MM-DD HH:mm:ss
**测试范围**: [具体测试的 API 模块]
**测试环境**: http://localhost/dev-api
**测试工具**: Node.js + Axios + 自定义脚本
## 📁 测试脚本路径
**登录脚本**: `test/custom-login-script.js`
**API 测试脚本**: `test/[功能]-api-test.js`
## 🔐 认证测试
### 登录接口测试
- **接口地址**: POST /auth/login
- **加密方式**: AES+RSA 双重加密
- **测试结果**: ✅ 成功
- **Token 获取**: ✅ 正常
- **Token 有效期**: 7 天
### Token 验证测试
- **Authorization 格式**: Bearer {token}
- **必需 Header**: clientid, referer, Accept-Language
- **权限验证**: ✅ admin 用户完全权限
## 🔍 业务接口测试
### [模块名称]接口测试
#### 列表查询接口
- **接口**: GET /[module]/list
- **参数**: pageNum, pageSize, [查询条件]
- **响应格式**: {total, rows, code, msg}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 详情查询接口
- **接口**: GET /[module]/{id}
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 新增接口
- **接口**: POST /[module]
- **请求格式**: JSON 对象
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 修改接口
- **接口**: PUT /[module]
- **请求格式**: JSON 对象(包含 id)
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 删除接口
- **接口**: DELETE /[module]/{id}
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
## 📊 测试统计
- **总接口数**: [数量]
- **测试通过**: [数量]
- **测试失败**: [数量]
- **通过率**: [百分比]%
## ⚠️ 问题记录
[记录测试中发现的问题]
## 🎯 结论
[测试结论和建议]
---
**测试执行**: 基于 `test/` 目录下的测试脚本
**文档路径**: `docs/测试文档/[模块]/[功能]/[报告名称]-YYYY-MM-DD.md`
```
## 5. 🎯 开发和测试建议
### ✅ 已完成的核心功能
1. **登录认证**: AES+RSA 双重加密登录机制完全正常
2. **Token 获取**: 可稳定获取 7 天有效期的 JWT Token
3. **权限验证**: admin 用户具有完全权限 (_:_:\*)
4. **API 调用**: Token 可正常用于所有业务 API 调用
### 🔧 开发最佳实践
1. **使用标准脚本**: 优先使用 `test/custom-login-script.js` 获取 Token
2. **Header 规范**: 始终包含 clientid、encrypt-key、referer 三个关键 Header
3. **语言头格式**: 使用 `Accept-Language: zh-CN,zh;q=0.9,en;q=0.8`
4. **错误处理**: 实现 Token 过期自动重新获取机制
### 🚀 后续优化建议
1. **Token 管理**: 实现 Token 自动刷新和缓存机制
2. **环境配置**: 支持开发、测试、生产环境的灵活切换
3. **自动化测试**: 建立基于 Token 的自动化 API 测试流程
4. **性能监控**: 添加 API 响应时间和成功率监控
### 📚 相关文档和工具
- **成功的登录脚本**: `test/custom-login-script.js` 和 `test/quick-token-getter.js`
- **使用指南**: `test/登录脚本使用说明.md`
- **API 文档**: `docs/开发文档/API.md`
- **变更记录**: `docs/变更管理/code-adjust-record.md`
### 🎯 使用建议
1. **开发阶段**: 使用 `quick-token-getter.js` 快速获取 Token 进行测试
2. **集成测试**: 使用 `custom-login-script.js` 进行完整的登录流程测试
3. **生产环境**: 将加密登录逻辑集成到实际应用中,实现自动 Token 管理
## 6. 🧪 自动化测试脚本
### 完整的 API 测试流程
```javascript
// 完整的API自动化测试脚本示例
async function runCompleteAPITest() {
console.log('🚀 开始API自动化测试...\n');
try {
// 1. 获取Token
console.log('1️⃣ 获取登录Token...');
const token = await encryptedLogin();
console.log('✅ Token获取成功\n');
// 2. 测试用户信息接口
console.log('2️⃣ 测试用户信息接口...');
const userInfo = await getUserInfo(token);
if (userInfo.success) {
console.log('✅ 用户信息接口测试通过');
console.log(`用户: ${userInfo.data.data.user.userName}`);
console.log(`权限: ${userInfo.data.data.permissions}\n`);
}
// 3. 测试分页列表接口
console.log('3️⃣ 测试分页列表接口...');
const userList = await testUserListAPI(token);
if (userList.success) {
console.log('✅ 分页列表接口测试通过\n');
}
// 4. 测试CRUD接口(可选)
console.log('4️⃣ 测试CRUD接口...');
// const crudResults = await testCRUDAPIs(token, '/system/dept', testDeptData);
console.log('🎉 API自动化测试完成!');
} catch (error) {
console.error('❌ 测试失败:', error.message);
}
}
// 执行测试
runCompleteAPITest();
```
### 性能测试脚本
```javascript
// API性能测试脚本
async function performanceTest(token, endpoint, concurrent = 10, iterations = 100) {
console.log(`开始性能测试: ${endpoint}`);
console.log(`并发数: ${concurrent}, 迭代次数: ${iterations}\n`);
const startTime = Date.now();
const results = [];
for (let i = 0; i < concurrent; i++) {
const promises = [];
for (let j = 0; j < iterations / concurrent; j++) {
promises.push(
callAPIWithToken(token, endpoint, null, 'GET')
.then((result) => ({
success: result.success,
time: Date.now(),
}))
.catch((error) => ({
success: false,
error: error.message,
time: Date.now(),
})),
);
}
results.push(...(await Promise.all(promises)));
}
const endTime = Date.now();
const totalTime = endTime - startTime;
const successCount = results.filter((r) => r.success).length;
const failCount = results.length - successCount;
console.log('📊 性能测试结果:');
console.log(`总请求数: ${results.length}`);
console.log(`成功数: ${successCount}`);
console.log(`失败数: ${failCount}`);
console.log(`成功率: ${((successCount / results.length) * 100).toFixed(2)}%`);
console.log(`总耗时: ${totalTime}ms`);
console.log(`平均响应时间: ${(totalTime / results.length).toFixed(2)}ms`);
console.log(`QPS: ${(results.length / (totalTime / 1000)).toFixed(2)} req/s`);
return {
total: results.length,
success: successCount,
fail: failCount,
successRate: successCount / results.length,
totalTime,
avgResponseTime: totalTime / results.length,
qps: results.length / (totalTime / 1000),
};
}
```
---
**🎉 成功状态**: 登录获取 Token 功能已完全实现并正常工作,可以稳定用于 API 开发和测试。