# 餐厅管理系统 - 前端API接口文档

## 📋 接口概述

本系统使用JWT Token进行身份认证。除了登录注册接口外，所有API都需要在请求头中携带有效的JWT Token。

**服务器地址**: `http://localhost:8080`

## 🔐 认证机制

### JWT Token使用方式
- **获取Token**: 通过登录接口获取
- **使用Token**: 在请求头中添加 `Authorization: Bearer {token}`
- **Token有效期**: 24小时
- **无需Token**: 仅注册和登录接口

## 📱 认证相关接口

### 1. 用户注册

**接口地址**: `POST /api/auth/register`  
**是否需要Token**: ❌ 否

**请求参数**:
```json
{
    "accountName": "用户名（3-20字符，字母数字下划线）",
    "accountPassword": "密码（8-20字符，至少包含字母和数字）",
    "confirmPassword": "确认密码（需与密码相同）",
    "accountPhone": "手机号（11位数字，可选）",
    "accountType": "账户类型（admin/manager/customer）",
    "accountGender": "性别（male/female，可选）"
}
```

**请求示例**:
```javascript
const registerData = {
    accountName: "zhangsan",
    accountPassword: "password123",
    confirmPassword: "password123",
    accountPhone: "13812345678",
    accountType: "customer",
    accountGender: "male"
};

const response = await fetch('http://localhost:8080/api/auth/register', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(registerData)
});

const result = await response.json();
```

**响应示例**:
```json
{
    "code": 200,
    "msg": "成功",
    "data": {
        "token": null,
        "accountInfo": {
            "accountId": 1,
            "accountName": "zhangsan",
            "accountType": "customer",
            "accountPhone": "13812345678",
            "accountGender": "male",
            "accountImg": null
        }
    }
}
```

### 2. 用户登录

**接口地址**: `POST /api/auth/login`  
**是否需要Token**: ❌ 否

**请求参数**:
```json
{
    "accountName": "用户名",
    "accountPassword": "密码"
}
```

**请求示例**:
```javascript
const loginData = {
    accountName: "zhangsan",
    accountPassword: "password123"
};

const response = await fetch('http://localhost:8080/api/auth/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(loginData)
});

const result = await response.json();

// 保存Token到本地存储
if (result.code === 200) {
    const jwtToken = result.data.token;
    localStorage.setItem('token', jwtToken);
    localStorage.setItem('userInfo', JSON.stringify(result.data.accountInfo));
}
```

**响应示例**:
```json
{
    "code": 200,
    "msg": "成功",
    "data": {
        "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZ3NhbiIsImlhdCI6MTcwNDczNzgxLCJleHAiOjE3NTA1NjAxODF9...",
        "accountInfo": {
            "accountId": 1,
            "accountName": "zhangsan",
            "accountType": "customer",
            "accountPhone": "13812345678",
            "accountGender": "male",
            "accountImg": null
        }
    }
}
```

### 3. 修改密码

**接口地址**: `PUT /api/auth/change-password`  
**是否需要Token**: ✅ 是

**请求参数**:
```json
{
    "oldPassword": "当前密码",
    "newPassword": "新密码（8-20字符，至少包含字母和数字）",
    "confirmPassword": "确认新密码"
}
```

**请求示例**:
```javascript
// 获取存储的Token
const storedToken = localStorage.getItem('token');

const passwordData = {
    oldPassword: "password123",
    newPassword: "newpassword456",
    confirmPassword: "newpassword456"
};

// 构建请求头，确保完整Token传递
const headers = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${storedToken}`
};

const response = await fetch('http://localhost:8080/api/auth/change-password', {
    method: 'PUT',
    headers: headers,
    body: JSON.stringify(passwordData)
});

const result = await response.json();
```

**响应示例**:
```json
{
    "code": 200,
    "msg": "成功",
    "data": "密码修改成功"
}
```

### 4. 获取当前用户信息

**接口地址**: `GET /api/auth/current-user`  
**是否需要Token**: ✅ 是

**请求示例**:
```javascript
// 获取存储的Token
const authToken = localStorage.getItem('token');

// 构建请求头
const requestHeaders = {
    'Authorization': `Bearer ${authToken}`
};

const response = await fetch('http://localhost:8080/api/auth/current-user', {
    method: 'GET',
    headers: requestHeaders
});

const result = await response.json();
```

**响应示例**:
```json
{
    "code": 200,
    "msg": "成功",
    "data": "当前登录用户: zhangsan"
}
```

## 🔧 前端Token管理

### Token存储和使用

```javascript
class AuthManager {
    // 保存Token - 确保完整保存
    static saveToken(tokenString, userInfo) {
        if (tokenString && tokenString.trim()) {
            localStorage.setItem('token', tokenString);
            localStorage.setItem('userInfo', JSON.stringify(userInfo));
        }
    }

    // 获取完整Token
    static getToken() {
        const tokenValue = localStorage.getItem('token');
        return tokenValue ? tokenValue.trim() : null;
    }

    // 获取用户信息
    static getUserInfo() {
        const userInfo = localStorage.getItem('userInfo');
        return userInfo ? JSON.parse(userInfo) : null;
    }

    // 清除Token（登出）
    static logout() {
        localStorage.removeItem('token');
        localStorage.removeItem('userInfo');
    }

    // 检查是否已登录
    static isLoggedIn() {
        const token = this.getToken();
        return token && token.length > 0;
    }

    // 创建认证头 - 确保完整Token传递
    static getAuthHeaders() {
        const fullToken = this.getToken();
        return fullToken ? { 'Authorization': `Bearer ${fullToken}` } : {};
    }
}
```

### 统一请求封装

```javascript
class ApiClient {
    static baseURL = 'http://localhost:8080';

    // 通用请求方法 - 使用变量方式确保Token完整传递
    static async request(url, options = {}) {
        // 获取完整Token
        const currentToken = AuthManager.getToken();
        
        // 构建基础headers
        const baseHeaders = {
            'Content-Type': 'application/json'
        };

        // 如果有Token，添加认证header
        const authHeaders = currentToken ? { 'Authorization': `Bearer ${currentToken}` } : {};
        
        // 合并headers
        const finalHeaders = {
            ...baseHeaders,
            ...authHeaders,
            ...(options.headers || {})
        };

        const config = {
            ...options,
            headers: finalHeaders
        };

        try {
            const response = await fetch(`${this.baseURL}${url}`, config);
            const result = await response.json();

            // 处理认证失败
            if (response.status === 403 || response.status === 401) {
                AuthManager.logout();
                // 跳转到登录页面
                window.location.href = '/login';
                return null;
            }

            return result;
        } catch (error) {
            console.error('API请求失败:', error);
            throw error;
        }
    }

    // GET请求
    static async get(url) {
        return this.request(url, { method: 'GET' });
    }

    // POST请求
    static async post(url, data) {
        return this.request(url, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }

    // PUT请求
    static async put(url, data) {
        return this.request(url, {
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }

    // DELETE请求
    static async delete(url) {
        return this.request(url, { method: 'DELETE' });
    }
}
```

### 认证相关操作

```javascript
class AuthAPI {
    // 用户注册
    static async register(userData) {
        return ApiClient.post('/api/auth/register', userData);
    }

    // 用户登录 - 确保完整Token保存
    static async login(credentials) {
        const result = await ApiClient.post('/api/auth/login', credentials);
        
        if (result && result.code === 200) {
            // 获取完整Token并保存
            const completeToken = result.data.token;
            AuthManager.saveToken(completeToken, result.data.accountInfo);
        }
        
        return result;
    }

    // 修改密码 - 使用变量方式传递Token
    static async changePassword(passwordData) {
        // 获取完整Token
        const userToken = AuthManager.getToken();
        
        // 构建请求头
        const requestHeaders = {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${userToken}`
        };

        // 直接使用fetch确保Token完整传递
        const response = await fetch(`${ApiClient.baseURL}/api/auth/change-password`, {
            method: 'PUT',
            headers: requestHeaders,
            body: JSON.stringify(passwordData)
        });

        return await response.json();
    }

    // 获取当前用户 - 使用变量方式传递Token
    static async getCurrentUser() {
        const accessToken = AuthManager.getToken();
        const headers = { 'Authorization': `Bearer ${accessToken}` };
        
        const response = await fetch(`${ApiClient.baseURL}/api/auth/current-user`, {
            method: 'GET',
            headers: headers
        });

        return await response.json();
    }

    // 登出
    static logout() {
        AuthManager.logout();
        window.location.href = '/login';
    }
}
```

## 🎯 使用示例

### 登录页面

```javascript
// 登录表单提交
async function handleLogin(event) {
    event.preventDefault();
    
    const formData = new FormData(event.target);
    const loginCredentials = {
        accountName: formData.get('username'),
        accountPassword: formData.get('password')
    };

    try {
        const loginResult = await AuthAPI.login(loginCredentials);
        
        if (loginResult.code === 200) {
            alert('登录成功！');
            // 验证Token是否正确保存
            const savedToken = AuthManager.getToken();
            console.log('Token已保存:', savedToken ? '是' : '否');
            
            window.location.href = '/dashboard';
        } else {
            alert(`登录失败：${loginResult.msg}`);
        }
    } catch (error) {
        alert('登录请求失败，请检查网络连接');
    }
}
```

### 注册页面

```javascript
// 注册表单提交
async function handleRegister(event) {
    event.preventDefault();
    
    const formData = new FormData(event.target);
    const registrationData = {
        accountName: formData.get('username'),
        accountPassword: formData.get('password'),
        confirmPassword: formData.get('confirmPassword'),
        accountPhone: formData.get('phone'),
        accountType: formData.get('userType'),
        accountGender: formData.get('gender')
    };

    try {
        const registerResult = await AuthAPI.register(registrationData);
        
        if (registerResult.code === 200) {
            alert('注册成功！请登录');
            window.location.href = '/login';
        } else {
            alert(`注册失败：${registerResult.msg}`);
        }
    } catch (error) {
        alert('注册请求失败，请检查网络连接');
    }
}
```

### 修改密码页面

```javascript
// 修改密码表单提交
async function handleChangePassword(event) {
    event.preventDefault();
    
    const formData = new FormData(event.target);
    const changePasswordInfo = {
        oldPassword: formData.get('oldPassword'),
        newPassword: formData.get('newPassword'),
        confirmPassword: formData.get('confirmPassword')
    };

    try {
        // 使用专门的方法确保Token完整传递
        const changeResult = await AuthAPI.changePassword(changePasswordInfo);
        
        if (changeResult.code === 200) {
            alert('密码修改成功！');
        } else {
            alert(`密码修改失败：${changeResult.msg}`);
        }
    } catch (error) {
        alert('请求失败，请检查网络连接');
    }
}
```

### 业务API调用示例

```javascript
// 获取数据列表 - 使用变量方式确保Token传递
async function fetchDataWithAuth(apiEndpoint) {
    try {
        // 获取完整Token
        const authenticationToken = AuthManager.getToken();
        
        if (!authenticationToken) {
            alert('请先登录');
            window.location.href = '/login';
            return;
        }

        // 构建请求头
        const apiHeaders = {
            'Authorization': `Bearer ${authenticationToken}`
        };

        const response = await fetch(`http://localhost:8080${apiEndpoint}`, {
            method: 'GET',
            headers: apiHeaders
        });

        const result = await response.json();
        
        if (result && result.code === 200) {
            return result.data;
        } else {
            alert(`请求失败：${result.msg}`);
            return null;
        }
    } catch (error) {
        alert('请求失败，请检查网络连接');
        return null;
    }
}

// 发送数据 - 使用变量方式确保Token传递
async function postDataWithAuth(apiEndpoint, postData) {
    try {
        // 获取完整Token
        const userAuthToken = AuthManager.getToken();
        
        if (!userAuthToken) {
            alert('请先登录');
            window.location.href = '/login';
            return;
        }

        // 构建请求头
        const requestHeaders = {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${userAuthToken}`
        };

        const response = await fetch(`http://localhost:8080${apiEndpoint}`, {
            method: 'POST',
            headers: requestHeaders,
            body: JSON.stringify(postData)
        });

        const result = await response.json();
        
        if (result && result.code === 200) {
            return result.data;
        } else {
            alert(`操作失败：${result.msg}`);
            return null;
        }
    } catch (error) {
        alert('请求失败，请检查网络连接');
        return null;
    }
}
```

## 🚨 错误处理

### 常见错误码

| 错误码 | 说明 | 处理方式 |
|--------|------|----------|
| 200 | 成功 | 正常处理数据 |
| 401 | 未认证 | 跳转到登录页面 |
| 403 | 已禁止/Token无效 | 清除Token，跳转到登录页面 |
| 500 | 服务器错误 | 显示错误信息，建议重试 |

### Token验证和错误处理

```javascript
// 页面加载时验证Token有效性
async function validateTokenOnPageLoad() {
    const currentToken = AuthManager.getToken();
    
    if (!currentToken) {
        // 没有Token，跳转到登录页
        window.location.href = '/login';
        return;
    }

    try {
        // 测试Token是否有效
        const testHeaders = { 'Authorization': `Bearer ${currentToken}` };
        
        const testResponse = await fetch('http://localhost:8080/api/auth/test', {
            method: 'GET',
            headers: testHeaders
        });

        if (testResponse.status === 403 || testResponse.status === 401) {
            // Token无效，清除并跳转
            AuthManager.logout();
            window.location.href = '/login';
        }
    } catch (error) {
        console.error('Token验证失败:', error);
    }
}

// 页面加载时执行Token验证
window.addEventListener('DOMContentLoaded', validateTokenOnPageLoad);
```

## 📝 重要注意事项

1. **Token完整性**: 
   - 始终使用变量方式传递Token，避免字符串截断
   - 在存储和读取Token时检查完整性

2. **Token安全**: 
   - Token存储在localStorage中，注意防止XSS攻击
   - 生产环境建议考虑更安全的存储方式

3. **请求头构建**:
   - 使用变量方式构建Authorization头
   - 确保Token前缀"Bearer "正确添加

4. **错误处理**:
   - 统一处理401/403错误，自动跳转登录页
   - 为用户提供友好的错误提示

5. **调试建议**:
   - 在控制台检查Token是否完整
   - 验证请求头是否正确设置

这份文档强调了使用变量方式来确保Token完整传递，避免了直接字符串拼接可能导致的截断问题！