初始化工程

This commit is contained in:
筱野
2025-08-10 21:09:19 +08:00
commit 42c71f5d13
30 changed files with 12882 additions and 0 deletions

326
src/services/httpService.ts Normal file
View 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()