|
|
@@ -0,0 +1,612 @@
|
|
|
+# API 文档
|
|
|
+
|
|
|
+## 目录
|
|
|
+
|
|
|
+- [基础配置](#基础配置)
|
|
|
+- [请求封装](#请求封装)
|
|
|
+- [用户相关接口](#用户相关接口)
|
|
|
+- [职位相关接口](#职位相关接口)
|
|
|
+- [面试相关接口](#面试相关接口)
|
|
|
+- [文件上传接口](#文件上传接口)
|
|
|
+- [错误处理](#错误处理)
|
|
|
+
|
|
|
+## 基础配置
|
|
|
+
|
|
|
+### API 基础地址
|
|
|
+
|
|
|
+- **生产环境**: `https://backend.qicai321.com`
|
|
|
+- **测试环境**: `http://192.168.66.187:8083`
|
|
|
+
|
|
|
+### 请求配置
|
|
|
+
|
|
|
+- **请求超时**: 60000ms (60秒)
|
|
|
+- **认证方式**: Bearer Token + CSRF Token
|
|
|
+- **数据格式**: JSON
|
|
|
+
|
|
|
+### 请求头
|
|
|
+
|
|
|
+所有请求会自动添加以下请求头:
|
|
|
+
|
|
|
+```javascript
|
|
|
+{
|
|
|
+ 'Authorization': 'Bearer {token}',
|
|
|
+ 'X-CSRF-Token': '{csrfToken}',
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 请求封装
|
|
|
+
|
|
|
+### HTTP 方法
|
|
|
+
|
|
|
+项目使用封装的 `http` 对象提供以下方法:
|
|
|
+
|
|
|
+```javascript
|
|
|
+import http from '@/utils/request.js';
|
|
|
+
|
|
|
+// GET 请求
|
|
|
+http.get(url, data, options)
|
|
|
+
|
|
|
+// POST 请求
|
|
|
+http.post(url, data, options)
|
|
|
+
|
|
|
+// PUT 请求
|
|
|
+http.put(url, data, options)
|
|
|
+
|
|
|
+// DELETE 请求
|
|
|
+http.delete(url, data, options)
|
|
|
+
|
|
|
+// 文件上传
|
|
|
+http.upload(url, filePath, name, formData, options)
|
|
|
+```
|
|
|
+
|
|
|
+### 请求拦截器
|
|
|
+
|
|
|
+自动添加的功能:
|
|
|
+
|
|
|
+1. **Token 认证**: 自动从存储中获取 token 并添加到请求头
|
|
|
+2. **CSRF 保护**: 自动添加 CSRF token
|
|
|
+3. **URL 处理**: 自动拼接基础 URL
|
|
|
+4. **错误处理**: 统一的错误处理和提示
|
|
|
+
|
|
|
+### 响应拦截器
|
|
|
+
|
|
|
+自动处理的功能:
|
|
|
+
|
|
|
+1. **状态码检查**: HTTP 状态码检查
|
|
|
+2. **业务状态**: 检查业务返回码 (code/status)
|
|
|
+3. **数据提取**: 自动提取 response.data.data
|
|
|
+4. **错误映射**: 统一的错误码映射
|
|
|
+
|
|
|
+## 用户相关接口
|
|
|
+
|
|
|
+### 微信登录
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { wxLogin } from '@/api/user.js';
|
|
|
+
|
|
|
+// 请求参数
|
|
|
+const params = {
|
|
|
+ code: '微信登录code',
|
|
|
+ userInfo: {
|
|
|
+ nickname: '用户昵称',
|
|
|
+ avatarUrl: '头像URL',
|
|
|
+ gender: 0, // 性别
|
|
|
+ province: '省份',
|
|
|
+ city: '城市',
|
|
|
+ country: '国家',
|
|
|
+ tenant_id: 1
|
|
|
+ },
|
|
|
+ signature: '签名',
|
|
|
+ rawData: '原始数据',
|
|
|
+ encryptedData: '加密数据',
|
|
|
+ iv: '初始向量',
|
|
|
+ _csrf: 'CSRF token'
|
|
|
+};
|
|
|
+
|
|
|
+// 调用
|
|
|
+const result = await wxLogin(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /wechat/wechatLogin`
|
|
|
+
|
|
|
+**返回数据**:
|
|
|
+
|
|
|
+```javascript
|
|
|
+{
|
|
|
+ status: 2000,
|
|
|
+ code: 0,
|
|
|
+ data: {
|
|
|
+ token: 'jwt_token',
|
|
|
+ userInfo: {
|
|
|
+ openid: 'openid',
|
|
|
+ nickname: '昵称',
|
|
|
+ avatarUrl: '头像',
|
|
|
+ tenant_id: 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 获取用户信息
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { getUserInfo } from '@/api/user.js';
|
|
|
+
|
|
|
+// 获取当前用户信息
|
|
|
+const userInfo = await getUserInfo();
|
|
|
+
|
|
|
+// 获取指定用户信息
|
|
|
+const otherUserInfo = await getUserInfo(userId, openid);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `GET /wechat/getUserDetail` 或 `GET /api/wechat/get_user_info`
|
|
|
+
|
|
|
+### 获取用户手机号
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { getUserPhoneNumber } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ code: '微信授权code',
|
|
|
+ encryptedData: '加密数据',
|
|
|
+ iv: '初始向量'
|
|
|
+};
|
|
|
+
|
|
|
+const phoneInfo = await getUserPhoneNumber(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /wechat/getUserPhoneNumber`
|
|
|
+
|
|
|
+### 更新用户信息
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { updateUserInfo } from '@/api/user.js';
|
|
|
+
|
|
|
+const userData = {
|
|
|
+ nickname: '新昵称',
|
|
|
+ phone: '手机号',
|
|
|
+ // ...其他用户信息
|
|
|
+};
|
|
|
+
|
|
|
+await updateUserInfo(userData);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `PUT /api/user/update`
|
|
|
+
|
|
|
+### 上传头像
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { uploadAvatar } from '@/api/user.js';
|
|
|
+
|
|
|
+const filePath = '/path/to/image.jpg';
|
|
|
+const result = await uploadAvatar(filePath);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /api/upload/avatar`
|
|
|
+
|
|
|
+### 退出登录
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { logout } from '@/api/user.js';
|
|
|
+
|
|
|
+await logout();
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /wechat/wechatLogout`
|
|
|
+
|
|
|
+### 填写用户信息
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { fillUserInfo } from '@/api/user.js';
|
|
|
+
|
|
|
+const userInfo = {
|
|
|
+ name: '姓名',
|
|
|
+ phone: '手机号',
|
|
|
+ email: '邮箱',
|
|
|
+ // ...其他信息
|
|
|
+};
|
|
|
+
|
|
|
+await fillUserInfo(userInfo);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /api/wechat/save_user_info`
|
|
|
+
|
|
|
+## 职位相关接口
|
|
|
+
|
|
|
+### 获取职位列表
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { getJobList } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ page: 1,
|
|
|
+ limit: 50,
|
|
|
+ searchTerms: '关键词',
|
|
|
+ status: '状态',
|
|
|
+ tenant_id: 1
|
|
|
+};
|
|
|
+
|
|
|
+const jobList = await getJobList(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `GET /api/system/job/list`
|
|
|
+
|
|
|
+**请求参数**:
|
|
|
+
|
|
|
+| 参数 | 类型 | 必填 | 说明 |
|
|
|
+|------|------|------|------|
|
|
|
+| page | Number | 否 | 页码,默认1 |
|
|
|
+| limit | Number | 否 | 每页数量,默认50 |
|
|
|
+| searchTerms | String | 否 | 搜索关键词 |
|
|
|
+| status | String | 否 | 职位状态 |
|
|
|
+| tenant_id | Number | 否 | 租户ID |
|
|
|
+
|
|
|
+### 申请职位
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { applyJob } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ job_id: 123,
|
|
|
+ user_id: 456,
|
|
|
+ resume_id: 789
|
|
|
+};
|
|
|
+
|
|
|
+await applyJob(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /api/job/apply`
|
|
|
+
|
|
|
+### 获取职位申请详情
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { getApplicationDetail } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ id: 123,
|
|
|
+ application_id: 456
|
|
|
+};
|
|
|
+
|
|
|
+const detail = await getApplicationDetail(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `GET /api/job/application_detail`
|
|
|
+
|
|
|
+**请求参数**:
|
|
|
+
|
|
|
+| 参数 | 类型 | 必填 | 说明 |
|
|
|
+|------|------|------|------|
|
|
|
+| id | Number | 是 | 用户ID |
|
|
|
+| tenant_id | Number | 是 | 租户ID |
|
|
|
+| application_id | Number | 是 | 申请ID |
|
|
|
+
|
|
|
+## 面试相关接口
|
|
|
+
|
|
|
+### 获取面试列表
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { getInterviewList } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ user_id: 123,
|
|
|
+ job_id: 456
|
|
|
+};
|
|
|
+
|
|
|
+const interviewList = await getInterviewList(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `GET /job/questions`
|
|
|
+
|
|
|
+### 获取面试详情
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { getInterviewDetail } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ interview_id: 123
|
|
|
+};
|
|
|
+
|
|
|
+const detail = await getInterviewDetail(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `GET /interview_question/detail`
|
|
|
+
|
|
|
+### 提交面试答案
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { submitAnswer } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ question_id: 123,
|
|
|
+ answer: '答案内容',
|
|
|
+ video_url: '视频URL',
|
|
|
+ audio_url: '音频URL'
|
|
|
+};
|
|
|
+
|
|
|
+await submitAnswer(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /api/job/submit_answer`
|
|
|
+
|
|
|
+### 获取问题数据
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { getQuestions } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ job_id: 123,
|
|
|
+ tenant_id: 1
|
|
|
+};
|
|
|
+
|
|
|
+const questions = await getQuestions(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `GET /api/wechat/choice_questions/`
|
|
|
+
|
|
|
+**请求参数**:
|
|
|
+
|
|
|
+| 参数 | 类型 | 必填 | 说明 |
|
|
|
+|------|------|------|------|
|
|
|
+| job_id | Number | 是 | 职位ID |
|
|
|
+| tenant_id | Number | 是 | 租户ID |
|
|
|
+
|
|
|
+### 语音面试互动
|
|
|
+
|
|
|
+```javascript
|
|
|
+// 直接调用 uni.request
|
|
|
+const res = await uni.request({
|
|
|
+ url: `${apiBaseUrl}/api/voice_interview_interaction/`,
|
|
|
+ method: 'POST',
|
|
|
+ data: {
|
|
|
+ tenant_id: 1,
|
|
|
+ question_id: 123,
|
|
|
+ position_config_id: 456,
|
|
|
+ application_id: 789
|
|
|
+ }
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /api/voice_interview_interaction/`
|
|
|
+
|
|
|
+## 文件上传接口
|
|
|
+
|
|
|
+### 上传照片
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { uploadPhoto } from '@/api/user.js';
|
|
|
+
|
|
|
+const params = {
|
|
|
+ file: '文件路径',
|
|
|
+ type: 'photo',
|
|
|
+ user_id: 123
|
|
|
+};
|
|
|
+
|
|
|
+const result = await uploadPhoto(params);
|
|
|
+```
|
|
|
+
|
|
|
+**接口地址**: `POST /api/upload/`
|
|
|
+
|
|
|
+**上传文件示例**:
|
|
|
+
|
|
|
+```javascript
|
|
|
+import http from '@/utils/request.js';
|
|
|
+
|
|
|
+const result = await http.upload(
|
|
|
+ '/api/upload/',
|
|
|
+ filePath,
|
|
|
+ 'file', // 文件字段名
|
|
|
+ {
|
|
|
+ type: 'photo',
|
|
|
+ tenant_id: 1
|
|
|
+ }
|
|
|
+);
|
|
|
+```
|
|
|
+
|
|
|
+## 错误处理
|
|
|
+
|
|
|
+### 错误码说明
|
|
|
+
|
|
|
+| HTTP 状态码 | 业务码 | 说明 | 处理方式 |
|
|
|
+|------------|--------|------|---------|
|
|
|
+| 200 | 2000 | 请求成功 | 正常处理 |
|
|
|
+| 400 | - | 请求参数错误 | 检查请求参数 |
|
|
|
+| 401 | - | 未登录或token过期 | 跳转登录页面 |
|
|
|
+| 403 | - | 无权限 | 提示用户 |
|
|
|
+| 500 | - | 服务器错误 | 提示用户稍后重试 |
|
|
|
+| 400 | 999 | 业务错误(如职位已截止) | 显示错误信息 |
|
|
|
+
|
|
|
+### 错误处理示例
|
|
|
+
|
|
|
+```javascript
|
|
|
+import http from '@/utils/request.js';
|
|
|
+
|
|
|
+try {
|
|
|
+ const result = await http.post('/api/example', data);
|
|
|
+ // 处理成功结果
|
|
|
+} catch (error) {
|
|
|
+ // 错误已在拦截器中处理并提示用户
|
|
|
+ // 这里可以做额外的处理
|
|
|
+ if (error.status === 401) {
|
|
|
+ // 跳转到登录页面
|
|
|
+ uni.reLaunch({ url: '/pages/login/login' });
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 特殊错误码 999
|
|
|
+
|
|
|
+当返回 `status: 999` 或 `code: 999` 时,表示业务逻辑错误(如职位申请已截止),不会自动提示,需要手动处理:
|
|
|
+
|
|
|
+```javascript
|
|
|
+try {
|
|
|
+ await applyJob(params);
|
|
|
+} catch (error) {
|
|
|
+ if (error.status === 999) {
|
|
|
+ uni.showToast({
|
|
|
+ title: error.message || '申请已截止',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 使用示例
|
|
|
+
|
|
|
+### 方式一:使用 API 模块
|
|
|
+
|
|
|
+```javascript
|
|
|
+// 在页面中导入
|
|
|
+import { wxLogin, getUserInfo } from '@/api/user.js';
|
|
|
+
|
|
|
+// 使用
|
|
|
+export default {
|
|
|
+ methods: {
|
|
|
+ async handleLogin() {
|
|
|
+ try {
|
|
|
+ const result = await wxLogin(params);
|
|
|
+ // 处理登录结果
|
|
|
+ } catch (error) {
|
|
|
+ // 错误处理
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 方式二:使用 API 服务
|
|
|
+
|
|
|
+```javascript
|
|
|
+import apiService from '@/services/ApiService.js';
|
|
|
+
|
|
|
+// 获取用户API
|
|
|
+const userApi = apiService.get('user');
|
|
|
+const result = await userApi.getUserInfo();
|
|
|
+
|
|
|
+// 或使用简写
|
|
|
+const userApi = apiService.user;
|
|
|
+const result = await userApi.getUserInfo();
|
|
|
+```
|
|
|
+
|
|
|
+### 方式三:使用 Composition API
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { useUserApi } from '@/composables/useUserApi.js';
|
|
|
+
|
|
|
+export default {
|
|
|
+ setup() {
|
|
|
+ const { login, loading, error } = useUserApi();
|
|
|
+
|
|
|
+ const handleLogin = async () => {
|
|
|
+ try {
|
|
|
+ const result = await login(code, userInfo);
|
|
|
+ return result;
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Login failed:', error.value);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return {
|
|
|
+ handleLogin,
|
|
|
+ loading,
|
|
|
+ error
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 方式四:直接使用 uni.request
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { apiBaseUrl } from '@/common/config.js';
|
|
|
+
|
|
|
+const res = await uni.request({
|
|
|
+ url: `${apiBaseUrl}/api/wechat/user/get_full_info`,
|
|
|
+ method: 'GET',
|
|
|
+ data: {
|
|
|
+ tenant_id: 1,
|
|
|
+ openid: 'openid'
|
|
|
+ }
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+## 注意事项
|
|
|
+
|
|
|
+### Token 管理
|
|
|
+
|
|
|
+- Token 存储在本地存储中: `uni.getStorageSync('token')`
|
|
|
+- 建议在 app.onLaunch 时检查 token 有效性
|
|
|
+- Token 过期后需要重新登录
|
|
|
+
|
|
|
+### CSRF Token
|
|
|
+
|
|
|
+- CSRF Token 存储在: `uni.getStorageSync('csrfToken')`
|
|
|
+- 自动添加到请求头: `X-CSRF-Token`
|
|
|
+- 建议在首次加载时获取 CSRF token
|
|
|
+
|
|
|
+### 请求超时
|
|
|
+
|
|
|
+- 默认超时时间: 60秒
|
|
|
+- 长请求可能需要调整超时时间
|
|
|
+- 使用 `options.timeout` 参数自定义超时时间
|
|
|
+
|
|
|
+### 并发请求
|
|
|
+
|
|
|
+- 多个请求并发时可使用 `Promise.all()`
|
|
|
+- 注意避免重复请求(使用防抖/节流)
|
|
|
+- 某些接口有防重入保护(如面试互动接口)
|
|
|
+
|
|
|
+### 测试环境切换
|
|
|
+
|
|
|
+修改 `common/config.js` 中的 `apiBaseUrl`:
|
|
|
+
|
|
|
+```javascript
|
|
|
+// 生产环境
|
|
|
+export const apiBaseUrl = 'https://backend.qicai321.com';
|
|
|
+
|
|
|
+// 测试环境
|
|
|
+export const apiBaseUrl = 'http://192.168.66.187:8083';
|
|
|
+```
|
|
|
+
|
|
|
+## API 列表汇总
|
|
|
+
|
|
|
+### 用户相关
|
|
|
+
|
|
|
+- 微信登录: `POST /wechat/wechatLogin`
|
|
|
+- 获取用户信息: `GET /wechat/getUserDetail`
|
|
|
+- 获取手机号: `POST /wechat/getUserPhoneNumber`
|
|
|
+- 更新用户信息: `PUT /api/user/update`
|
|
|
+- 上传头像: `POST /api/upload/avatar`
|
|
|
+- 退出登录: `POST /wechat/wechatLogout`
|
|
|
+- 填写用户信息: `POST /api/wechat/save_user_info`
|
|
|
+
|
|
|
+### 职位相关
|
|
|
+
|
|
|
+- 获取职位列表: `GET /api/system/job/list`
|
|
|
+- 申请职位: `POST /api/job/apply`
|
|
|
+- 获取申请详情: `GET /api/job/application_detail`
|
|
|
+
|
|
|
+### 面试相关
|
|
|
+
|
|
|
+- 获取面试列表: `GET /job/questions`
|
|
|
+- 获取面试详情: `GET /interview_question/detail`
|
|
|
+- 提交答案: `POST /api/job/submit_answer`
|
|
|
+- 获取问题: `GET /api/wechat/choice_questions/`
|
|
|
+- 面试互动: `POST /api/voice_interview_interaction/`
|
|
|
+
|
|
|
+### 文件相关
|
|
|
+
|
|
|
+- 上传文件: `POST /api/upload/`
|
|
|
+
|
|
|
+### 公共相关
|
|
|
+
|
|
|
+- 用户协议: `GET /api/public/agreements/terms_of_service/`
|
|
|
+
|
|
|
+## 更新日志
|
|
|
+
|
|
|
+- **v1.0.0** (2024): 初始版本
|
|
|
+ - 基础API封装
|
|
|
+ - 用户、职位、面试相关接口
|
|
|
+ - 统一的错误处理
|
|
|
+ - CSRF 防护机制
|
|
|
+
|