-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add request library and cache hook, automatically generate requ…
…ests through interface documentation
- Loading branch information
1 parent
a7ddf17
commit a66431e
Showing
19 changed files
with
1,926 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { ResultEnum } from '@/api/enum'; | ||
import { userLogin } from '@/utils/user'; | ||
import AsyncStorage from '@react-native-async-storage/async-storage'; | ||
import axios, { AxiosRequestConfig } from 'axios'; | ||
|
||
const baseURL = 'https://fzuhelper.west2.online/'; | ||
|
||
const request = axios.create({ | ||
baseURL, | ||
timeout: 5000, | ||
}); | ||
|
||
interface PendingTask { | ||
config: AxiosRequestConfig; | ||
resolve: Function; | ||
reject: Function; | ||
} | ||
|
||
let refreshing = false; | ||
let queue: PendingTask[] = []; | ||
// 白名单,防止重新获取时被拦截 | ||
const WhiteList = ['/api/v1/login/access-token', '/api/v1/internal/user/login']; | ||
|
||
request.interceptors.response.use( | ||
async response => { | ||
let { data, config } = response; | ||
|
||
if (refreshing && !WhiteList.includes(config.url || '')) { | ||
return new Promise((resolve, reject) => { | ||
queue.push({ | ||
config, | ||
resolve, | ||
reject, | ||
}); | ||
}); | ||
} | ||
|
||
// accessToken过期 | ||
if (data.code === ResultEnum.AuthAccessExpiredCode) { | ||
refreshing = true; | ||
|
||
// 尝试刷新token | ||
try { | ||
const res = await axios.get('/api/v1/login/refresh-token', { | ||
baseURL, | ||
timeout: 5000, | ||
headers: { | ||
Authorization: await AsyncStorage.getItem('refresh_token'), | ||
}, | ||
}); | ||
const { 'access-token': accessToken, 'refresh-token': refreshToken } = | ||
res.headers; | ||
console.log(accessToken, refreshToken); | ||
accessToken && | ||
(await AsyncStorage.setItem('access_token', accessToken)); | ||
refreshToken && | ||
(await AsyncStorage.setItem('refresh_token', refreshToken)); | ||
|
||
queue.forEach(({ config, resolve }) => { | ||
resolve(request(config)); | ||
}); | ||
refreshing = false; | ||
queue = []; | ||
return request(config); | ||
} catch (error: any) { | ||
if (error?.data?.code === ResultEnum.AuthRefreshExpiredCode) { | ||
//TODO 重新输入账号密码登录 | ||
} else { | ||
queue.forEach(({ config, reject }) => { | ||
reject(request(config)); | ||
}); | ||
refreshing = false; | ||
queue = []; | ||
} | ||
} | ||
} | ||
// 处理jwch cookie异常 | ||
if (data.code === ResultEnum.BizJwchCookieExceptionCode) { | ||
// TODO 尝试重新登录并获取cookies和id | ||
const id = await AsyncStorage.getItem('user_id'); | ||
const password = await AsyncStorage.getItem('user_password'); | ||
if (id && password) { | ||
refreshing = true; | ||
try { | ||
await userLogin({ | ||
id, | ||
password, | ||
}); | ||
queue.forEach(({ config, resolve }) => { | ||
resolve(request(config)); | ||
}); | ||
refreshing = false; | ||
|
||
queue = []; | ||
return request(config); | ||
} catch (error) { | ||
// TODO 判断是否为密码错误 | ||
queue.forEach(({ reject }) => { | ||
reject(); | ||
}); | ||
refreshing = false; | ||
queue = []; | ||
return Promise.reject(); | ||
} | ||
} | ||
} | ||
|
||
// 其他错误 | ||
if (data.code !== ResultEnum.SuccessCode) { | ||
//TODO 错误消息提示处理 | ||
return Promise.reject(response); | ||
} | ||
|
||
// 更新accessToken和refreshToken | ||
const { 'access-token': accessToken, 'refresh-token': refreshToken } = | ||
response.headers; | ||
console.log(response.headers); | ||
accessToken && (await AsyncStorage.setItem('access_token', accessToken)); | ||
refreshToken && (await AsyncStorage.setItem('refresh_token', refreshToken)); | ||
|
||
return response; | ||
}, | ||
error => { | ||
// 判断是否为超时error | ||
if (error.message.indexOf('timeout') !== -1) { | ||
//TODO 超时消息处理 | ||
} | ||
// 判断是否为网络error或者不存在页面 | ||
if (error.message.indexOf('Network Error') !== -1) { | ||
//TODO 网络错误消息处理 | ||
} | ||
|
||
return Promise.reject(error); | ||
}, | ||
); | ||
|
||
request.interceptors.request.use(async function (config) { | ||
const accessToken = await AsyncStorage.getItem('access_token'); | ||
const id = await AsyncStorage.getItem('id'); | ||
const cookies = await AsyncStorage.getItem('cookies'); | ||
if (accessToken) { | ||
config.headers.Authorization = accessToken; | ||
} | ||
if (id) { | ||
config.headers.Id = id; | ||
} | ||
if (cookies) { | ||
config.headers.Cookies = cookies; | ||
} | ||
return config; | ||
}); | ||
|
||
export default request; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Description: 枚举定义 https://github.com/west2-online/fzuhelper-server/blob/main/pkg/errno/code.go | ||
export enum ResultEnum { | ||
SuccessCode = '10000', | ||
ParamErrorCode = '20001', // 参数错误 | ||
ParamEmptyCode = '20002', // 参数为空 | ||
ParamMissingHeaderCode = '20003', // 缺少请求头数据(id or cookies) | ||
ParamInvalidCode = '20004', // 参数无效 | ||
ParamMissingCode = '20005', // 参数缺失 | ||
ParamTooLongCode = '20006', // 参数过长 | ||
ParamTooShortCode = '20007', // 参数过短 | ||
ParamTypeCode = '20008', // 参数类型错误 | ||
ParamFormatCode = '20009', // 参数格式错误 | ||
ParamRangeCode = '20010', // 参数范围错误 | ||
ParamValueCode = '20011', // 参数值错误 | ||
ParamFileNotExistCode = '20012', // 文件不存在 | ||
ParamFileReadErrorCode = '20013', // 文件读取错误 | ||
|
||
AuthErrorCode = '30001', // 鉴权错误 | ||
AuthInvalidCode = '30002', // 鉴权无效 | ||
AuthAccessExpiredCode = '30003', // 访问令牌过期 | ||
AuthRefreshExpiredCode = '30004', // 刷新令牌过期 | ||
AuthMissingCode = '30005', // 鉴权缺失 | ||
|
||
BizErrorCode = '40001', // 业务错误 | ||
BizLogicCode = '40002', // 业务逻辑错误 | ||
BizLimitCode = '40003', // 业务限制错误 | ||
BizNotExist = '40005', // 业务不存在错误 | ||
BizFileUploadErrorCode = '40006', // 文件上传错误(service 层) | ||
BizJwchCookieExceptionCode = '40007', // jwch cookie异常 | ||
|
||
InternalServiceErrorCode = '50001', // 未知服务错误 | ||
InternalDatabaseErrorCode = '50002', // 数据库错误 | ||
InternalRedisErrorCode = '50003', // Redis错误 | ||
InternalNetworkErrorCode = '50004', // 网络错误 | ||
InternalTimeoutErrorCode = '50005', // 超时错误 | ||
InternalIOErrorCode = '50006', // IO错误 | ||
InternalJSONErrorCode = '50007', // JSON错误 | ||
InternalXMLErrorCode = '50008', // XML错误 | ||
InternalURLEncodeErrorCode = '50009', // URL编码错误 | ||
InternalHTTPErrorCode = '50010', // HTTP错误 | ||
InternalHTTP2ErrorCode = '50011', // HTTP2错误 | ||
InternalGRPCErrorCode = '50012', // GRPC错误 | ||
InternalThriftErrorCode = '50013', // Thrift错误 | ||
InternalProtobufErrorCode = '50014', // Protobuf错误 | ||
InternalSQLErrorCode = '50015', // SQL错误 | ||
InternalNoSQLErrorCode = '50016', // NoSQL错误 | ||
InternalORMErrorCode = '50017', // ORM错误 | ||
InternalQueueErrorCode = '50018', // 队列错误 | ||
InternalETCDErrorCode = '50019', // ETCD错误 | ||
InternalTraceErrorCode = '50020', // Trace错误 | ||
|
||
// SuccessCodePaper paper在旧版Android中的SuccessCode是2000,用作兼容 | ||
SuccessCodePaper = '2000', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* eslint-disable */ | ||
// @ts-ignore | ||
import * as API from './types'; | ||
import request from '../axios'; | ||
|
||
/** 获取学分统计 注意这里可能涉及到辅修 GET /api/v1/jwch/academic/credit */ | ||
export async function getApiV1JwchAcademicCredit(options?: { | ||
[key: string]: unknown; | ||
}) { | ||
return request<{ | ||
code: string; | ||
message: string; | ||
data: { type: string; gain: string; total: string }[]; | ||
}>('/api/v1/jwch/academic/credit', { | ||
method: 'GET', | ||
...(options || {}), | ||
}); | ||
} | ||
|
||
/** 绩点排名 GET /api/v1/jwch/academic/gpa */ | ||
export async function getApiV1JwchAcademicGpa(options?: { | ||
[key: string]: unknown; | ||
}) { | ||
return request<{ | ||
code: string; | ||
message: string; | ||
data: { time: string; data: { type: string; value: string }[] }; | ||
}>('/api/v1/jwch/academic/gpa', { | ||
method: 'GET', | ||
...(options || {}), | ||
}); | ||
} | ||
|
||
/** 获取专业培养计划 GET /api/v1/jwch/academic/plan */ | ||
export async function getApiV1JwchAcademicPlan(options?: { | ||
[key: string]: unknown; | ||
}) { | ||
return request<{ code: string; message: string; data: string }>( | ||
'/api/v1/jwch/academic/plan', | ||
{ | ||
method: 'GET', | ||
...(options || {}), | ||
} | ||
); | ||
} | ||
|
||
/** 成绩详情 GET /api/v1/jwch/academic/scores */ | ||
export async function getApiV1JwchAcademicScores(options?: { | ||
[key: string]: unknown; | ||
}) { | ||
return request<{ | ||
code: string; | ||
message: string; | ||
data: { | ||
credit: string; | ||
gpa: string; | ||
name: string; | ||
score: string; | ||
teacher: string; | ||
term: string; | ||
year: string; | ||
}[]; | ||
}>('/api/v1/jwch/academic/scores', { | ||
method: 'GET', | ||
...(options || {}), | ||
}); | ||
} | ||
|
||
/** 统考成绩 CET、省计算机 GET /api/v1/jwch/academic/unified-exam */ | ||
export async function getApiV1JwchAcademicUnifiedExam(options?: { | ||
[key: string]: unknown; | ||
}) { | ||
return request<{ | ||
code: string; | ||
message: string; | ||
data: { name: string; score: string; term: string }[]; | ||
}>('/api/v1/jwch/academic/unified-exam', { | ||
method: 'GET', | ||
...(options || {}), | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* eslint-disable */ | ||
// @ts-ignore | ||
import * as API from './types'; | ||
import request from '../axios'; | ||
|
||
/** 测试拦截功能 测试/api路由下接口能否被正确拦截 GET /api/v1/jwch/ping */ | ||
export async function getApiV1JwchPing(options?: { [key: string]: unknown }) { | ||
return request<{ code: string; message: string }>('/api/v1/jwch/ping', { | ||
method: 'GET', | ||
...(options || {}), | ||
}); | ||
} | ||
|
||
/** 获取 token 通过在header中提供id和cookies来获取token GET /api/v1/login/access-token */ | ||
export async function getApiV1LoginAccessToken(options?: { | ||
[key: string]: unknown; | ||
}) { | ||
return request<{ code: string; message: string }>( | ||
'/api/v1/login/access-token', | ||
{ | ||
method: 'GET', | ||
...(options || {}), | ||
} | ||
); | ||
} | ||
|
||
/** 刷新 token 通过在 header 中提供 refreshtoken(长期) 来刷新 accesstoken(短期) GET /api/v1/login/refresh-token */ | ||
export async function getApiV1LoginRefreshToken(options?: { | ||
[key: string]: unknown; | ||
}) { | ||
return request<{ code: string; message: string }>( | ||
'/api/v1/login/refresh-token', | ||
{ | ||
method: 'GET', | ||
...(options || {}), | ||
} | ||
); | ||
} |
Oops, something went wrong.