初始化工程
This commit is contained in:
326
src/services/httpService.ts
Normal file
326
src/services/httpService.ts
Normal file
@@ -0,0 +1,326 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import tokenManager from '../utils/tokenManager'
|
||||
import envConfig, { isDevelopment, getEnvInfo } from '../config/env'
|
||||
|
||||
// 请求方法类型
|
||||
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
||||
|
||||
// 请求配置接口
|
||||
export interface RequestConfig {
|
||||
url: string
|
||||
method?: HttpMethod
|
||||
data?: any
|
||||
params?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
needAuth?: boolean // 是否需要token认证
|
||||
showLoading?: boolean // 是否显示加载提示
|
||||
loadingText?: string // 加载提示文本
|
||||
}
|
||||
|
||||
// 响应数据接口
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number
|
||||
data: T
|
||||
message: string
|
||||
success: boolean
|
||||
}
|
||||
|
||||
// HTTP状态码常量
|
||||
export const HTTP_STATUS = {
|
||||
SUCCESS: 200,
|
||||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
SERVER_ERROR: 500
|
||||
}
|
||||
|
||||
class HttpService {
|
||||
private baseURL: string
|
||||
private timeout: number
|
||||
private enableLog: boolean
|
||||
|
||||
constructor() {
|
||||
// 使用环境配置
|
||||
this.baseURL = `${envConfig.apiBaseURL}/api/${envConfig.apiVersion}`
|
||||
this.timeout = envConfig.timeout
|
||||
this.enableLog = envConfig.enableLog
|
||||
|
||||
// 在开发环境下输出配置信息
|
||||
if (isDevelopment()) {
|
||||
console.log('🌍 HTTP服务初始化:', {
|
||||
baseURL: this.baseURL,
|
||||
timeout: this.timeout,
|
||||
envInfo: getEnvInfo()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 构建完整URL
|
||||
private buildUrl(url: string, params?: Record<string, any>): string {
|
||||
const fullUrl = url.startsWith('http') ? url : `${this.baseURL}${url}`
|
||||
|
||||
if (params) {
|
||||
const searchParams = new URLSearchParams()
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
searchParams.append(key, String(value))
|
||||
}
|
||||
})
|
||||
const queryString = searchParams.toString()
|
||||
return queryString ? `${fullUrl}?${queryString}` : fullUrl
|
||||
}
|
||||
|
||||
return fullUrl
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
private buildHeaders(config: RequestConfig): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Environment': envConfig.name, // 添加环境标识
|
||||
...config.headers
|
||||
}
|
||||
|
||||
// 如果需要认证,添加token
|
||||
if (config.needAuth !== false) {
|
||||
const authHeader = tokenManager.getAuthHeader()
|
||||
Object.assign(headers, authHeader)
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// 日志输出
|
||||
private log(level: 'info' | 'warn' | 'error', message: string, data?: any) {
|
||||
if (!this.enableLog) return
|
||||
|
||||
const logMethod = console[level] || console.log
|
||||
const timestamp = new Date().toLocaleTimeString()
|
||||
|
||||
if (data) {
|
||||
logMethod(`[${timestamp}] HTTP ${level.toUpperCase()}: ${message}`, data)
|
||||
} else {
|
||||
logMethod(`[${timestamp}] HTTP ${level.toUpperCase()}: ${message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理响应
|
||||
private handleResponse<T>(response: any): Promise<ApiResponse<T>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { statusCode, data } = response
|
||||
|
||||
this.log('info', `响应状态码: ${statusCode}`, { data })
|
||||
|
||||
// HTTP状态码检查
|
||||
if (statusCode !== HTTP_STATUS.SUCCESS) {
|
||||
this.handleHttpError(statusCode)
|
||||
reject(new Error(`HTTP Error: ${statusCode}`))
|
||||
return
|
||||
}
|
||||
|
||||
// 业务状态码检查
|
||||
if (data && typeof data === 'object') {
|
||||
if (data.success === false || (data.code && data.code !== 0 && data.code !== 200)) {
|
||||
this.handleBusinessError(data)
|
||||
reject(new Error(data.message || '请求失败'))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data)
|
||||
})
|
||||
}
|
||||
|
||||
// 处理HTTP错误
|
||||
private handleHttpError(statusCode: number): void {
|
||||
let message = '网络请求失败'
|
||||
|
||||
switch (statusCode) {
|
||||
case HTTP_STATUS.UNAUTHORIZED:
|
||||
message = '登录已过期,请重新登录'
|
||||
tokenManager.clearTokens()
|
||||
// 可以在这里跳转到登录页面
|
||||
break
|
||||
case HTTP_STATUS.FORBIDDEN:
|
||||
message = '没有权限访问该资源'
|
||||
break
|
||||
case HTTP_STATUS.NOT_FOUND:
|
||||
message = '请求的资源不存在'
|
||||
break
|
||||
case HTTP_STATUS.SERVER_ERROR:
|
||||
message = '服务器内部错误'
|
||||
break
|
||||
default:
|
||||
message = `请求失败 (${statusCode})`
|
||||
}
|
||||
|
||||
this.log('error', `HTTP错误 ${statusCode}: ${message}`)
|
||||
|
||||
Taro.showToast({
|
||||
title: message,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
// 处理业务错误
|
||||
private handleBusinessError(data: any): void {
|
||||
const message = data.message || '操作失败'
|
||||
|
||||
this.log('error', `业务错误: ${message}`, data)
|
||||
|
||||
Taro.showToast({
|
||||
title: message,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
|
||||
// 统一请求方法
|
||||
async request<T = any>(config: RequestConfig): Promise<ApiResponse<T>> {
|
||||
const {
|
||||
url,
|
||||
method = 'GET',
|
||||
data,
|
||||
params,
|
||||
showLoading = false,
|
||||
loadingText = '请求中...'
|
||||
} = config
|
||||
|
||||
const fullUrl = this.buildUrl(url, method === 'GET' ? params : undefined)
|
||||
|
||||
this.log('info', `发起请求: ${method} ${fullUrl}`, {
|
||||
data: method !== 'GET' ? data : undefined,
|
||||
params: method === 'GET' ? params : undefined
|
||||
})
|
||||
// 检查token(如果需要认证)
|
||||
if (config.needAuth === true && !tokenManager.hasValidToken()) {
|
||||
Taro.showToast({
|
||||
title: '请先登录',
|
||||
icon: 'none'
|
||||
})
|
||||
throw new Error('Token无效或已过期')
|
||||
}
|
||||
|
||||
// 显示加载提示
|
||||
if (showLoading) {
|
||||
Taro.showLoading({
|
||||
title: loadingText,
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
const requestConfig = {
|
||||
url: fullUrl,
|
||||
method: method,
|
||||
data: method !== 'GET' ? data : undefined,
|
||||
header: this.buildHeaders(config),
|
||||
timeout: this.timeout
|
||||
}
|
||||
|
||||
const response = await Taro.request(requestConfig)
|
||||
return this.handleResponse<T>(response)
|
||||
} catch (error) {
|
||||
this.log('error', '请求失败', error)
|
||||
|
||||
// 在模拟模式下返回模拟数据
|
||||
if (envConfig.enableMock && isDevelopment()) {
|
||||
this.log('info', '使用模拟数据')
|
||||
return this.getMockResponse<T>(url, method)
|
||||
}
|
||||
|
||||
Taro.showToast({
|
||||
title: '网络连接失败',
|
||||
icon: 'none'
|
||||
})
|
||||
|
||||
throw error
|
||||
} finally {
|
||||
// 隐藏加载提示
|
||||
if (showLoading) {
|
||||
Taro.hideLoading()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取模拟数据
|
||||
private getMockResponse<T>(url: string, method: string): ApiResponse<T> {
|
||||
this.log('info', `返回模拟数据: ${method} ${url}`)
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '模拟请求成功',
|
||||
data: {
|
||||
mockData: true,
|
||||
url,
|
||||
method,
|
||||
timestamp: new Date().toISOString()
|
||||
} as T
|
||||
}
|
||||
}
|
||||
|
||||
// GET请求
|
||||
get<T = any>(url: string, params?: Record<string, any>, config?: Partial<RequestConfig>): Promise<ApiResponse<T>> {
|
||||
return this.request<T>({
|
||||
url,
|
||||
method: 'GET',
|
||||
params,
|
||||
...config
|
||||
})
|
||||
}
|
||||
|
||||
// POST请求
|
||||
post<T = any>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<ApiResponse<T>> {
|
||||
return this.request<T>({
|
||||
url,
|
||||
method: 'POST',
|
||||
data,
|
||||
...config
|
||||
})
|
||||
}
|
||||
|
||||
// PUT请求
|
||||
put<T = any>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<ApiResponse<T>> {
|
||||
return this.request<T>({
|
||||
url,
|
||||
method: 'PUT',
|
||||
data,
|
||||
...config
|
||||
})
|
||||
}
|
||||
|
||||
// DELETE请求
|
||||
delete<T = any>(url: string, params?: Record<string, any>, config?: Partial<RequestConfig>): Promise<ApiResponse<T>> {
|
||||
return this.request<T>({
|
||||
url,
|
||||
method: 'DELETE',
|
||||
params,
|
||||
...config
|
||||
})
|
||||
}
|
||||
|
||||
// PATCH请求
|
||||
patch<T = any>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<ApiResponse<T>> {
|
||||
return this.request<T>({
|
||||
url,
|
||||
method: 'PATCH',
|
||||
data,
|
||||
...config
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前环境信息
|
||||
getEnvInfo() {
|
||||
return {
|
||||
baseURL: this.baseURL,
|
||||
timeout: this.timeout,
|
||||
enableLog: this.enableLog,
|
||||
...getEnvInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出HTTP服务实例
|
||||
export default new HttpService()
|
||||
Reference in New Issue
Block a user