feat: 完善项目开发规范和测试文档体系

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

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

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

更新时间:2025-07-01
This commit is contained in:
zlwu committed 2025-07-01 17:53:05 +08:00
1 parent 7a5b9d1bea
commit 249b1dfeb4
44 files changed
+14027 -2852

No files matched your search

+682
View File
@@ -0,0 +1,682 @@
# 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 开发和测试。