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

20 KiB
Raw Blame History

Cursor Rules - API 测试和登录方法

适用范围


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 加密登录(推荐)

// 完整的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 获取(简化版)

// 快速获取Token的简化版本
async function getToken(username = 'admin', password = 'admin123') {
    const { getToken } = require('./test/quick-token-getter');
    return await getToken(username, password);
}

🔧 快速测试工具

使用 test 目录下的测试脚本

# 在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 - 快速获取版,代码简洁易懂

依赖安装

{
    "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 格式:

    {
      'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
      'encrypt-key': '[RSA加密的AES密钥]',
      'referer': 'http://localhost/login?redirect=%252Findex'
    }
    
  2. 正确的加密流程:

    • 生成 32 位随机 AES 密钥
    • 使用 AES-ECB 模式加密登录数据
    • 使用 RSA-PKCS1 加密 AES 密钥
    • 设置正确的请求头格式

3. 🔍 API 测试最佳实践

测试环境配置

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 测试方法

// 通用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 调用

// 带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;
}

分页接口测试

// 分页接口测试模板
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 接口测试套件

// 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 测试记录模板

测试结果记录格式

## 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 调用
-   ✅ 用户权限验证通过
-   ✅ 加密解密机制正常

详细测试报告模板

# 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 测试流程

// 完整的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();

性能测试脚本

// 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 开发和测试。