u
This commit is contained in:
1 parent
6849579652
commit
d60be4a75b
24 files changed
+463
-179
No files matched your search
@@ -12,5 +12,6 @@ export default antfu({
|
||||
'no-console': 'off', // 关闭 console 报错
|
||||
'node/prefer-global/process': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'no-useless-catch': 'off',
|
||||
},
|
||||
})
|
||||
@@ -5,7 +5,7 @@ import { Stack, useRouter } from 'expo-router'
|
||||
import * as SplashScreen from 'expo-splash-screen'
|
||||
import { StatusBar } from 'expo-status-bar'
|
||||
import { RefreshCw } from 'lucide-react-native'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import EmptyState from '@/components/common/EmptyStatus'
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
|
||||
import { useUserStore } from '@/store/user'
|
||||
@@ -33,33 +33,23 @@ export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) {
|
||||
|
||||
export default function RootLayout() {
|
||||
const router = useRouter()
|
||||
|
||||
const token = useUserStore(state => state.token)
|
||||
const userStore = useUserStore()
|
||||
|
||||
const [fontsLoaded] = useFonts({
|
||||
SpaceMono: require('@/static/fonts/SpaceMono-Regular.ttf'),
|
||||
})
|
||||
|
||||
const [storeLoaded, setStoreLoaded] = useState(false)
|
||||
|
||||
// 等待 store 恢复数据
|
||||
useEffect(() => {
|
||||
if (token !== undefined) {
|
||||
setStoreLoaded(true)
|
||||
}
|
||||
}, [token])
|
||||
|
||||
// splash screen 隐藏 & 登录跳转
|
||||
useEffect(() => {
|
||||
if (fontsLoaded && storeLoaded) {
|
||||
if (fontsLoaded) {
|
||||
SplashScreen.hideAsync()
|
||||
|
||||
// 如果没有 token,则跳转登录页
|
||||
if (token === null) {
|
||||
if (!userStore.isLoggedIn()) {
|
||||
router.replace('/auth')
|
||||
}
|
||||
}
|
||||
}, [fontsLoaded, storeLoaded, token])
|
||||
}, [fontsLoaded])
|
||||
|
||||
return (
|
||||
<GluestackUIProvider>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { StepLayoutItem } from '@/components/custom/StepLayout'
|
||||
import { Slot } from 'expo-router'
|
||||
import { Slot, Stack, useRouter } from 'expo-router'
|
||||
import { Image, Text, View } from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import StepLayout from '@/components/custom/StepLayout'
|
||||
import CustomTabbar from '@/components/custom/Tabbar'
|
||||
|
||||
const steps: StepLayoutItem[] = [
|
||||
{
|
||||
@@ -22,5 +25,6 @@ export default function AuthLayout() {
|
||||
<StepLayout steps={steps}>
|
||||
<Slot />
|
||||
</StepLayout>
|
||||
|
||||
)
|
||||
}
|
||||
@@ -36,17 +36,20 @@ export default function AuthLogin() {
|
||||
setForm(prev => ({ ...prev, [field]: value }))
|
||||
}
|
||||
const defaultLogin = () => {
|
||||
alovaInstance.Post('/auth/login/default', {
|
||||
alovaInstance.Post('/auth/test', {
|
||||
title: 'foo',
|
||||
body: 'bar',
|
||||
userId: 1,
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('成功返回:', res)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('请求失败或未授权:', err)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<View className="flex flex-col items-center px-6 h-full">
|
||||
<Image
|
||||
source={require('@/static/images/logo.png')}
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function AuthRegister() {
|
||||
|
||||
return (
|
||||
<ScrollView ref={scrollRef} style={{ flex: 1 }}>
|
||||
<View className="flex flex-col items-center px-6">
|
||||
<View className="flex flex-col items-center px-6 bg-white ">
|
||||
<Image
|
||||
source={require('@/static/images/logo.png')}
|
||||
style={{ width: 120, height: 120 }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Ionicons } from '@expo/vector-icons'
|
||||
import { usePathname, useRouter } from 'expo-router'
|
||||
import { Image, Pressable, ScrollView, Text, View } from 'react-native'
|
||||
import Animated, { SlideInRight } from 'react-native-reanimated'
|
||||
import { Image, Pressable, Text, View } from 'react-native'
|
||||
import Animated, { SlideInLeft, SlideInRight } from 'react-native-reanimated'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
|
||||
export interface StepLayoutItem {
|
||||
@@ -31,11 +31,13 @@ export default function StepLayout({ steps, children, showHearder = true }: Step
|
||||
// 是否显示header
|
||||
const headerVisible = showHearder !== false && currentStep.showHearder !== false
|
||||
|
||||
const enteringAnimation = canGoBack ? SlideInRight.duration(300) : SlideInLeft.duration(300)
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<Animated.View
|
||||
key={pathname}
|
||||
entering={SlideInRight.duration(300)}
|
||||
entering={enteringAnimation}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
{headerVisible && (
|
||||
@@ -44,10 +46,7 @@ export default function StepLayout({ steps, children, showHearder = true }: Step
|
||||
onPress={canGoBack ? (currentStep.onBack ?? (() => router.back())) : undefined}
|
||||
style={{ opacity: canGoBack ? 1 : 0 }}
|
||||
>
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={22}
|
||||
/>
|
||||
<Ionicons name="arrow-back" size={22} />
|
||||
</Pressable>
|
||||
|
||||
<Text className="flex-1 text-center text-lg font-semibold">
|
||||
|
||||
Whitespace-only changes.
@@ -1,21 +1,110 @@
|
||||
import type { Method } from 'alova'
|
||||
import type { AxiosError } from 'axios'
|
||||
import { axiosRequestAdapter } from '@alova/adapter-axios'
|
||||
import { createAlova } from 'alova'
|
||||
import { createServerTokenAuthentication } from 'alova/client'
|
||||
import adapterFetch from 'alova/fetch'
|
||||
import { useToast } from '@/utils/toast'
|
||||
import { router } from 'expo-router'
|
||||
import { userStore } from '@/store/user'
|
||||
import { toast } from '@/utils/toast'
|
||||
|
||||
const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({
|
||||
// ...
|
||||
})
|
||||
enum HttpCode {
|
||||
Success = 200,
|
||||
Unauthorized = 401,
|
||||
RefreshToken = 402,
|
||||
Fail = 500,
|
||||
}
|
||||
|
||||
const WhiteList = [
|
||||
'/auth/login/**',
|
||||
'/auth/register/**',
|
||||
'/auth/refresh/token',
|
||||
]
|
||||
|
||||
function isWhiteListed(url: string) {
|
||||
return WhiteList.some((pattern) => {
|
||||
if (pattern.endsWith('/**')) {
|
||||
const base = pattern.slice(0, -3)
|
||||
return url.startsWith(base)
|
||||
}
|
||||
return url === pattern
|
||||
})
|
||||
}
|
||||
|
||||
const alovaInstance = createAlova({
|
||||
// baseURL: 'http://192.168.1.136:8085/app',
|
||||
baseURL: 'http://192.168.5.6:8085/app',
|
||||
baseURL: 'http://192.168.1.136:8085/app',
|
||||
requestAdapter: axiosRequestAdapter(),
|
||||
responded(response) {
|
||||
// response自动被推断为AxiosResponse类型
|
||||
|
||||
return response.data
|
||||
responded: {
|
||||
onSuccess: async (response, method: Method) => {
|
||||
const resData = response.data as ResponseData<any>
|
||||
|
||||
if (resData.code === HttpCode.Success) {
|
||||
return resData.data
|
||||
}
|
||||
|
||||
// Access Token 失效 → 重新登录
|
||||
if (resData.code === HttpCode.Unauthorized) {
|
||||
console.log('===到这一步了===')
|
||||
router.replace('/auth/login')
|
||||
return
|
||||
}
|
||||
|
||||
// Access Token 过期但 Refresh Token 有效 → 刷新 token 并重试
|
||||
if (resData.code === HttpCode.RefreshToken) {
|
||||
try {
|
||||
const refreshToken = userStore.getState().userInfo?.refreshToken
|
||||
if (!refreshToken) {
|
||||
router.replace('/auth/login')
|
||||
return
|
||||
}
|
||||
|
||||
// 调用刷新接口
|
||||
const res = await alovaInstance.Get<ResponseData<string>>('/auth/refresh/token', {
|
||||
headers: {
|
||||
refreshToken,
|
||||
},
|
||||
})
|
||||
const newToken = res.data
|
||||
|
||||
// 更新 store
|
||||
userStore.getState().setUserInfo({
|
||||
...userStore.getState().userInfo!,
|
||||
token: newToken,
|
||||
})
|
||||
|
||||
// 重试原请求
|
||||
method.config.headers.token = newToken
|
||||
await method.send()
|
||||
|
||||
return
|
||||
}
|
||||
catch (err) {
|
||||
console.log('===到这一步了===')
|
||||
router.replace('/auth/login')
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 其他错误
|
||||
return Promise.reject(resData.message || '请求失败')
|
||||
},
|
||||
|
||||
onError: (error: AxiosError, method) => {
|
||||
console.log(error.status)
|
||||
return Promise.reject(error)
|
||||
},
|
||||
},
|
||||
beforeRequest(method) {
|
||||
if (isWhiteListed(method.url))
|
||||
return
|
||||
|
||||
if (!userStore.getState().isLoggedIn()) {
|
||||
toast.show({
|
||||
title: '尚未登录',
|
||||
})
|
||||
router.replace('/auth/login')
|
||||
return
|
||||
}
|
||||
|
||||
method.config.headers.token = userStore.getState().userInfo?.token ?? ''
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -4,26 +4,22 @@ import { create } from 'zustand'
|
||||
import { createJSONStorage, persist } from 'zustand/middleware'
|
||||
|
||||
interface UserState {
|
||||
userInfo: { userId: string, username: string } | null
|
||||
token: string | null
|
||||
setUserInfo: (userInfo: UserState['userInfo']) => void
|
||||
setToken: (token: string) => void
|
||||
userInfo: UserInfo | null
|
||||
setUserInfo: (userInfo: UserInfo | null) => void
|
||||
logout: () => void
|
||||
isLoggedIn: () => boolean
|
||||
}
|
||||
|
||||
export const useUserStore = create<UserState>()(
|
||||
const useUserStore = create<UserState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
userInfo: null,
|
||||
token: null,
|
||||
setUserInfo: userInfo => set({ userInfo }),
|
||||
setToken: token => set({ token }),
|
||||
logout: () => {
|
||||
console.log('当前 token:', get().token)
|
||||
set({ userInfo: null, token: null })
|
||||
console.log('当前 token:', get().userInfo?.token)
|
||||
set({ userInfo: null })
|
||||
},
|
||||
isLoggedIn: () => !!get().token,
|
||||
isLoggedIn: () => !!get().userInfo?.token,
|
||||
}),
|
||||
{
|
||||
name: 'user-storage',
|
||||
@@ -31,3 +27,6 @@ export const useUserStore = create<UserState>()(
|
||||
},
|
||||
),
|
||||
)
|
||||
const userStore = useUserStore
|
||||
|
||||
export { userStore, useUserStore }
|
||||
@@ -0,0 +1,20 @@
|
||||
export {}
|
||||
|
||||
declare global {
|
||||
interface ResponseData<T> {
|
||||
trackId: string
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
interface UserInfo {
|
||||
account: string
|
||||
id: string
|
||||
seqNo: number
|
||||
username: string
|
||||
gender: number
|
||||
token?: string
|
||||
refreshToken?: string
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { IToastProps } from '@gluestack-ui/core/lib/esm/toast/creator/types'
|
||||
import React, { useRef } from 'react'
|
||||
import React, { createContext, useContext, useRef } from 'react'
|
||||
import { Toast, ToastDescription, ToastTitle, useToast as useGluestackToast } from '@/components/ui/toast'
|
||||
|
||||
type ToastVariant = 'solid' | 'outline'
|
||||
@@ -12,28 +12,74 @@ interface ShowToastOptions extends IToastProps {
|
||||
action?: ToastAction
|
||||
}
|
||||
|
||||
// 创建全局 toast 实例
|
||||
let globalToast: any = null
|
||||
|
||||
// 创建 Context 用于在组件内部使用
|
||||
const ToastContext = createContext<ReturnType<typeof useGluestackToast> | null>(null)
|
||||
|
||||
export function ToastProviderWrapper({ children }: { children: React.ReactNode }) {
|
||||
const toast = useGluestackToast()
|
||||
const toastRef = useRef(toast)
|
||||
toastRef.current = toast
|
||||
|
||||
return <>{children}</>
|
||||
// 设置全局实例
|
||||
React.useEffect(() => {
|
||||
globalToast = {
|
||||
show: (options: ShowToastOptions) => {
|
||||
const id = Math.random().toString()
|
||||
toast.show({
|
||||
id,
|
||||
placement: options.placement ?? 'top',
|
||||
duration: options.duration !== undefined ? options.duration : 3000,
|
||||
avoidKeyboard: options.avoidKeyboard ?? false,
|
||||
containerStyle: options.containerStyle,
|
||||
onCloseComplete: options.onCloseComplete,
|
||||
render: options.render
|
||||
? () => options.render!({ id })
|
||||
: ({ id }) => (
|
||||
<Toast
|
||||
nativeID={`toast-${id}`}
|
||||
variant={options.variant ?? 'solid'}
|
||||
action={options.action ?? 'muted'}
|
||||
>
|
||||
{options.title && <ToastTitle className="font-blod">{options.title}</ToastTitle>}
|
||||
{options.description && <ToastDescription>{options.description}</ToastDescription>}
|
||||
</Toast>
|
||||
),
|
||||
})
|
||||
return id
|
||||
},
|
||||
close: (id: string) => {
|
||||
toast.close(id)
|
||||
},
|
||||
closeAll: () => {
|
||||
toast.closeAll()
|
||||
},
|
||||
isActive: (id: string) => toast.isActive(id),
|
||||
}
|
||||
}, [toast])
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={toast}>
|
||||
{children}
|
||||
</ToastContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 Hook,用法:
|
||||
* const toast = useToast();
|
||||
* toast.show({ ... });
|
||||
* toast.close(id);
|
||||
* toast.closeAll();
|
||||
* 自定义 Hook,用于 React 组件内部
|
||||
*/
|
||||
export function useToast() {
|
||||
const toast = useGluestackToast()
|
||||
const toast = useContext(ToastContext)
|
||||
|
||||
if (!toast) {
|
||||
throw new Error('useToast must be used within a ToastProviderWrapper')
|
||||
}
|
||||
|
||||
return {
|
||||
show: (options: ShowToastOptions) => {
|
||||
const id = Math.random().toString()
|
||||
|
||||
toast.show({
|
||||
id,
|
||||
placement: options.placement ?? 'top',
|
||||
@@ -54,18 +100,40 @@ export function useToast() {
|
||||
</Toast>
|
||||
),
|
||||
})
|
||||
|
||||
return id
|
||||
},
|
||||
|
||||
close: (id: string) => {
|
||||
toast.close(id)
|
||||
},
|
||||
|
||||
closeAll: () => {
|
||||
toast.closeAll()
|
||||
},
|
||||
|
||||
isActive: (id: string) => toast.isActive(id),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局 toast 实例,可在组件外部使用
|
||||
*/
|
||||
export const toast = {
|
||||
show: (options: ShowToastOptions) => {
|
||||
if (!globalToast) {
|
||||
console.warn('Toast not initialized. Make sure ToastProviderWrapper is rendered.')
|
||||
return ''
|
||||
}
|
||||
return globalToast.show(options)
|
||||
},
|
||||
close: (id: string) => {
|
||||
if (globalToast) {
|
||||
globalToast.close(id)
|
||||
}
|
||||
},
|
||||
closeAll: () => {
|
||||
if (globalToast) {
|
||||
globalToast.closeAll()
|
||||
}
|
||||
},
|
||||
isActive: (id: string) => {
|
||||
return globalToast ? globalToast.isActive(id) : false
|
||||
},
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
"include": [
|
||||
"./**/*.ts",
|
||||
"./**/*.tsx",
|
||||
"./src/types/*.d.ts",
|
||||
"src/**/*.d.ts",
|
||||
"nativewind-env.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
|
||||
@@ -13,18 +13,48 @@ func InitAuthRouter(r *router.Router) {
|
||||
group := r.Group("auth")
|
||||
group.POST("/login/default", loginDefault)
|
||||
group.POST("/register/default", registerDefault)
|
||||
group.POST("/refresh/token", RefreshAccessToken)
|
||||
}
|
||||
|
||||
//func RefreshToken(c *router.Context) error {
|
||||
// _, err := c.GetBodyWithRequired("accessToken", "refreshToken")
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// //accessToken := bodyData.GetString("accessToken")
|
||||
// //refreshToken := bodyData.GetString("refreshToken")
|
||||
//}
|
||||
func RefreshAccessToken(c *router.Context) error {
|
||||
// 从 Header 获取 Refresh Token
|
||||
refreshToken := c.Header("refreshToken")
|
||||
if refreshToken == "" {
|
||||
return response.Fail(c).
|
||||
Code(response.HttpCode.Unauthorized).
|
||||
Message("未提供 Token").
|
||||
Send()
|
||||
}
|
||||
|
||||
// 验证 Refresh Token
|
||||
tokenResult := jwtx.VerifyToken(refreshToken)
|
||||
|
||||
if !tokenResult.IsValid || tokenResult.IsExpired {
|
||||
// Refresh Token 无效或过期,需要重新登录
|
||||
return response.Fail(c).
|
||||
Code(response.HttpCode.Unauthorized).
|
||||
Message("登录已过期,请重新登录").
|
||||
Send()
|
||||
}
|
||||
|
||||
userID := tokenResult.Claims.UserID
|
||||
|
||||
// 生成新的 Access Token
|
||||
newAccessToken, err := jwtx.CreateAccessToken(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 返回新的 Access Token
|
||||
respData := map[string]string{
|
||||
"accessToken": newAccessToken,
|
||||
}
|
||||
|
||||
return response.Success(c).
|
||||
Data(respData).
|
||||
Send()
|
||||
}
|
||||
|
||||
// 默认登录
|
||||
// 默认登录
|
||||
func loginDefault(c *router.Context) error {
|
||||
// 获取请求体并校验必填字段
|
||||
@@ -43,13 +73,12 @@ func loginDefault(c *router.Context) error {
|
||||
)
|
||||
user, err := db.FindOne("user_info", userQuery)
|
||||
if err != nil {
|
||||
return response.Success(c).Message("查询用户失败,请稍后重试").IsSuccess(false).Send()
|
||||
return response.Success(c).Message("查询用户失败,请稍后重试").Send()
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
// 账号或密码错误
|
||||
return response.Success(c).
|
||||
IsSuccess(false).
|
||||
return response.Fail(c).
|
||||
Message("账号或密码错误").
|
||||
Send()
|
||||
}
|
||||
@@ -99,7 +128,7 @@ func registerDefault(c *router.Context) error {
|
||||
}
|
||||
|
||||
if len(existingUsers) > 0 {
|
||||
return response.Error(c).IsSuccess(false).Message("该账号已被注册").Send()
|
||||
return response.Fail(c).Message("该账号已被注册").Send()
|
||||
}
|
||||
|
||||
// 构造新用户数据
|
||||
@@ -115,7 +144,7 @@ func registerDefault(c *router.Context) error {
|
||||
return err
|
||||
}
|
||||
if insertedRows != 1 {
|
||||
return response.Success(c).IsSuccess(false).Message("注册失败").Send()
|
||||
return response.Fail(c).Message("注册失败").Send()
|
||||
}
|
||||
|
||||
return response.Success(c).Message("注册成功").Send()
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
response "base-go-v2/internal/reponse"
|
||||
"base-go-v2/internal/router"
|
||||
"base-go-v2/internal/utils/jwtx"
|
||||
)
|
||||
|
||||
func AuthMiddleware() router.HandlerFunc {
|
||||
return func(c *router.Context) error {
|
||||
token := c.Header("token")
|
||||
if token == "" {
|
||||
c.Abort()
|
||||
return response.Fail(c).
|
||||
Code(response.HttpCode.Unauthorized).
|
||||
Message("未提供 Token").
|
||||
Send()
|
||||
}
|
||||
|
||||
tokenVerify := jwtx.VerifyToken(token)
|
||||
|
||||
// Access Token 有效
|
||||
if tokenVerify.IsValid {
|
||||
c.Set("user_id", tokenVerify.Claims.UserID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Access Token 失效 → 返回刷新提示
|
||||
if !tokenVerify.IsValid {
|
||||
c.Abort()
|
||||
return response.Fail(c).
|
||||
Code(response.HttpCode.RefreshToken).
|
||||
Message("Access Token 已过期,请刷新").
|
||||
Send()
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func ErrorMiddleware() router.HandlerFunc {
|
||||
// be 是 nil 指针类型
|
||||
if errors.As(err, &be) {
|
||||
// 业务错误,不记录日志
|
||||
return response.Error(c).Message(err.Error()).Send()
|
||||
return response.Fail(c).Message(err.Error()).Send()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,6 @@ func ErrorMiddleware() router.HandlerFunc {
|
||||
logx.String("error", err.Error()),
|
||||
logx.String("stack", stack),
|
||||
)
|
||||
return response.Error(c).Message("服务器内部错误").Send()
|
||||
return response.Fail(c).Message("服务器内部错误").Send()
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func RecoveryMiddleware() router.HandlerFunc {
|
||||
)
|
||||
|
||||
// 返回统一 JSON 响应
|
||||
_ = response.Error(c).Message("系统 Panic").Send()
|
||||
_ = response.Fail(c).Message("系统 Panic").Send()
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ func InitAppRouter() *router.Router {
|
||||
r.Use(middleware.CORSMiddleware())
|
||||
r.Use(middleware.LoggerMiddleware())
|
||||
r.Use(middleware.ErrorMiddleware())
|
||||
r.Use(middleware.AuthMiddleware()).ExcludePaths("/auth/**")
|
||||
|
||||
auth.InitAuthRouter(r)
|
||||
return r
|
||||
|
||||
@@ -8,128 +8,71 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Result 是最终响应结构体
|
||||
type Result struct {
|
||||
IsSuccess bool `json:"isSuccess"` // 是否成功
|
||||
Code string `json:"code"` // 状态码
|
||||
Message string `json:"message"` // 提示信息
|
||||
Data interface{} `json:"data,omitempty"` // 数据
|
||||
TrackId string `json:"trackId,omitempty"` // 请求追踪ID
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
TrackId string `json:"trackId,omitempty"`
|
||||
}
|
||||
|
||||
// Code 定义状态码
|
||||
type Code struct {
|
||||
Value string
|
||||
Message string
|
||||
var HttpCode = struct {
|
||||
Success int
|
||||
Unauthorized int
|
||||
Fail int
|
||||
RefreshToken int
|
||||
}{
|
||||
Success: 200,
|
||||
Unauthorized: 401,
|
||||
Fail: 500,
|
||||
RefreshToken: 402,
|
||||
}
|
||||
|
||||
// 常用状态码
|
||||
var (
|
||||
CodeSuccess = Code{"0000", "请求成功"}
|
||||
CodeFail = Code{"0001", "请求失败"}
|
||||
CodeNoLogin = Code{"1001", "未登录"}
|
||||
CodeLoginExpired = Code{"1002", "登录已过期"}
|
||||
CodeInvalidToken = Code{"1003", "Token 无效"}
|
||||
CodeInvalidAccount = Code{"1005", "账号或密码错误"}
|
||||
)
|
||||
|
||||
const defaultMessage = "未知错误"
|
||||
|
||||
// Builder 用于构建响应
|
||||
type Builder struct {
|
||||
c *router.Context
|
||||
result Result
|
||||
}
|
||||
|
||||
// ---------------- 构造函数 ----------------
|
||||
|
||||
// Success 创建成功响应
|
||||
func Success(c *router.Context) *Builder {
|
||||
return &Builder{
|
||||
c: c,
|
||||
result: Result{
|
||||
IsSuccess: true,
|
||||
Code: CodeSuccess.Value,
|
||||
Message: CodeSuccess.Message,
|
||||
Code: HttpCode.Success,
|
||||
Message: "请求成功",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Error 创建失败响应
|
||||
func Error(c *router.Context) *Builder {
|
||||
func Fail(c *router.Context) *Builder {
|
||||
return &Builder{
|
||||
c: c,
|
||||
result: Result{
|
||||
IsSuccess: false,
|
||||
Code: CodeFail.Value,
|
||||
Message: CodeFail.Message,
|
||||
Code: HttpCode.Fail,
|
||||
Message: "请求失败",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- 链式方法 ----------------
|
||||
|
||||
// 设置 IsSuccess 字段
|
||||
func (b *Builder) IsSuccess(IsSuccess bool) *Builder {
|
||||
b.result.IsSuccess = IsSuccess
|
||||
return b
|
||||
}
|
||||
|
||||
// 设置 Code,并根据 Code 自动设置 IsSuccess
|
||||
func (b *Builder) Code(code string) *Builder {
|
||||
func (b *Builder) Code(code int) *Builder {
|
||||
b.result.Code = code
|
||||
b.result.Message = GetMessage(code)
|
||||
b.result.IsSuccess = code == CodeSuccess.Value
|
||||
return b
|
||||
}
|
||||
|
||||
// 设置 Code 对象,并自动设置 IsSuccess
|
||||
func (b *Builder) CodeObj(code Code) *Builder {
|
||||
b.result.Code = code.Value
|
||||
b.result.Message = code.Message
|
||||
b.result.IsSuccess = code.Value == CodeSuccess.Value
|
||||
return b
|
||||
}
|
||||
|
||||
// 自定义提示信息
|
||||
func (b *Builder) Message(msg string) *Builder {
|
||||
b.result.Message = msg
|
||||
return b
|
||||
}
|
||||
|
||||
// 设置返回数据
|
||||
func (b *Builder) Data(data interface{}) *Builder {
|
||||
b.result.Data = data
|
||||
return b
|
||||
}
|
||||
|
||||
// 发送响应
|
||||
func (b *Builder) Send() error {
|
||||
// 默认 Data 为空
|
||||
if b.result.Data == nil {
|
||||
b.result.Data = ""
|
||||
}
|
||||
|
||||
// 从 routinex 获取 trackId
|
||||
trackId := routinex.Get(logx.TrackID)
|
||||
if trackId != nil {
|
||||
if trackId := routinex.Get(logx.TrackID); trackId != nil {
|
||||
b.result.TrackId = strutil.ToString(trackId)
|
||||
}
|
||||
|
||||
return b.c.JSON(http.StatusOK, b.result)
|
||||
}
|
||||
|
||||
// ---------------- 辅助函数 ----------------
|
||||
|
||||
// 根据 Code 获取默认提示信息
|
||||
func GetMessage(value string) string {
|
||||
for _, c := range []Code{
|
||||
CodeSuccess, CodeFail, CodeNoLogin,
|
||||
CodeLoginExpired, CodeInvalidToken, CodeInvalidAccount,
|
||||
} {
|
||||
if c.Value == value {
|
||||
return c.Message
|
||||
}
|
||||
}
|
||||
return defaultMessage
|
||||
}
|
||||
@@ -67,8 +67,24 @@ func (n *node) search(parts []string, height int, params map[string]string) *nod
|
||||
|
||||
// 中间件条目
|
||||
type middlewareEntry struct {
|
||||
handler HandlerFunc
|
||||
excludePaths map[string]struct{}
|
||||
handler HandlerFunc
|
||||
excludePaths map[string]struct{}
|
||||
excludePrefix []string
|
||||
}
|
||||
|
||||
// 判断路径是否被排除
|
||||
func (m *middlewareEntry) IsExcluded(path string) bool {
|
||||
if m.excludePaths != nil {
|
||||
if _, ok := m.excludePaths[path]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, prefix := range m.excludePrefix {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Router 主结构
|
||||
@@ -78,7 +94,7 @@ type Router struct {
|
||||
basePath string
|
||||
}
|
||||
|
||||
// NewRouter 创建
|
||||
// 创建 Router
|
||||
func NewRouter(basePath ...string) *Router {
|
||||
path := "/"
|
||||
if len(basePath) > 0 && basePath[0] != "" {
|
||||
@@ -90,11 +106,13 @@ func NewRouter(basePath ...string) *Router {
|
||||
}
|
||||
}
|
||||
|
||||
// MiddlewareHandle 用于链式排除路径
|
||||
type MiddlewareHandle struct {
|
||||
router *Router
|
||||
entryIdx int
|
||||
}
|
||||
|
||||
// Use 注册中间件
|
||||
func (r *Router) Use(handler HandlerFunc) *MiddlewareHandle {
|
||||
entry := middlewareEntry{handler: handler}
|
||||
r.middleware = append(r.middleware, entry)
|
||||
@@ -104,15 +122,27 @@ func (r *Router) Use(handler HandlerFunc) *MiddlewareHandle {
|
||||
}
|
||||
}
|
||||
|
||||
// ExcludePaths 支持 /** 前缀匹配
|
||||
func (mh *MiddlewareHandle) ExcludePaths(paths ...string) *Router {
|
||||
excludeMap := make(map[string]struct{}, len(paths))
|
||||
excludeMap := make(map[string]struct{}, 0)
|
||||
excludePrefix := make([]string, 0)
|
||||
|
||||
for _, p := range paths {
|
||||
excludeMap[p] = struct{}{}
|
||||
if strings.HasSuffix(p, "/**") {
|
||||
base := strings.TrimSuffix(p, "/**")
|
||||
excludePrefix = append(excludePrefix, base)
|
||||
} else {
|
||||
excludeMap[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
mh.router.middleware[mh.entryIdx].excludePaths = excludeMap
|
||||
mh.router.middleware[mh.entryIdx].excludePrefix = excludePrefix
|
||||
|
||||
return mh.router
|
||||
}
|
||||
|
||||
// Group 创建子路由组
|
||||
func (r *Router) Group(prefix string, m ...HandlerFunc) *Router {
|
||||
newMiddleware := make([]middlewareEntry, len(r.middleware))
|
||||
copy(newMiddleware, r.middleware)
|
||||
@@ -127,6 +157,7 @@ func (r *Router) Group(prefix string, m ...HandlerFunc) *Router {
|
||||
}
|
||||
}
|
||||
|
||||
// GET/POST
|
||||
func (r *Router) GET(path string, handler HandlerFunc) *Router {
|
||||
return r.handle("GET", path, handler)
|
||||
}
|
||||
@@ -145,7 +176,7 @@ func (r *Router) handle(method, path string, handler HandlerFunc) *Router {
|
||||
return r
|
||||
}
|
||||
|
||||
// Handler 入口函数(fasthttp)
|
||||
// Handler 入口(fasthttp)
|
||||
func (r *Router) Handler(ctx *fasthttp.RequestCtx) {
|
||||
method := string(ctx.Method())
|
||||
root := r.roots[method]
|
||||
@@ -166,10 +197,8 @@ func (r *Router) Handler(ctx *fasthttp.RequestCtx) {
|
||||
handlers := make([]HandlerFunc, 0)
|
||||
path := string(ctx.Path())
|
||||
for _, m := range r.middleware {
|
||||
if m.excludePaths != nil {
|
||||
if _, excluded := m.excludePaths[path]; excluded {
|
||||
continue
|
||||
}
|
||||
if m.IsExcluded(path) {
|
||||
continue
|
||||
}
|
||||
handlers = append(handlers, m.handler)
|
||||
}
|
||||
@@ -185,6 +214,7 @@ func (r *Router) Handler(ctx *fasthttp.RequestCtx) {
|
||||
_ = c.Next()
|
||||
}
|
||||
|
||||
// 辅助函数
|
||||
func parsePattern(pattern string) []string {
|
||||
vs := strings.Split(strings.Trim(pattern, "/"), "/")
|
||||
parts := make([]string, 0, len(vs))
|
||||
|
||||
@@ -15,9 +15,9 @@ type CustomClaims struct {
|
||||
|
||||
// TokenResult 用于返回验证结果
|
||||
type TokenResult struct {
|
||||
Claims *CustomClaims
|
||||
Valid bool
|
||||
Expired bool
|
||||
Claims *CustomClaims
|
||||
IsValid bool
|
||||
IsExpired bool
|
||||
}
|
||||
|
||||
// CreateAccessToken 创建 Access Token,只返回 token 和 error
|
||||
@@ -61,7 +61,7 @@ func createToken(userID int, expiry time.Duration) (string, error) {
|
||||
|
||||
// VerifyToken 验证 token 并返回结构化结果
|
||||
func VerifyToken(tokenString string) TokenResult {
|
||||
result := TokenResult{Valid: false}
|
||||
result := TokenResult{IsValid: false, IsExpired: false}
|
||||
|
||||
token, _ := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.App.JWT.Secret), nil
|
||||
@@ -73,9 +73,9 @@ func VerifyToken(tokenString string) TokenResult {
|
||||
}
|
||||
|
||||
result.Claims = claims
|
||||
result.Valid = true
|
||||
result.IsValid = true
|
||||
if claims.ExpiresAt != nil && time.Now().After(claims.ExpiresAt.Time) {
|
||||
result.Expired = true
|
||||
result.IsExpired = true
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -36,25 +36,25 @@ func NotEmpty(data map[string]interface{}, fields ...string) error {
|
||||
// collectErrors 递归收集字段错误
|
||||
func collectErrors(v interface{}, path string, errs map[string]string) {
|
||||
if v == nil {
|
||||
errs[path] = fmt.Sprintf("字段 %s 不能为空", path)
|
||||
errs[path] = "字段不能为空"
|
||||
return
|
||||
}
|
||||
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
if strings.TrimSpace(val) == "" {
|
||||
errs[path] = fmt.Sprintf("字段 %s 不能为空", path)
|
||||
errs[path] = "字段不能为空"
|
||||
}
|
||||
case []interface{}:
|
||||
if len(val) == 0 {
|
||||
errs[path] = fmt.Sprintf("字段 %s 不能为空", path)
|
||||
errs[path] = "字段不能为空"
|
||||
}
|
||||
for i, elem := range val {
|
||||
collectErrors(elem, fmt.Sprintf("%s[%d]", path, i), errs)
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if len(val) == 0 {
|
||||
errs[path] = fmt.Sprintf("字段 %s 不能为空", path)
|
||||
errs[path] = "字段不能为空"
|
||||
}
|
||||
for k, elem := range val {
|
||||
collectErrors(elem, fmt.Sprintf("%s.%s", path, k), errs)
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,69 @@
|
||||
{"level":"INFO","ts":"2025-11-06 13:11:55.116","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0.0005349,"trackId":"376961877109899264"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:14:17.628","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0.0005502,"trackId":"376962474844356608"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:15:48.248","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0.0009768,"trackId":"376962854932185088"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:19:48.605","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0.0005207,"trackId":"376963863062511616"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:21:27.160","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0.0005053,"trackId":"376964276432142336"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:22:45.690","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376964605815029760"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:24:24.423","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376965019931246592"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:24:44.251","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376965103095906304"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:26:19.417","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0.0006208,"trackId":"376965502251040768"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:31:17.803","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376966753772634112"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:31:49.685","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376966887495434240"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:31:51.226","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376966893958856704"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:31:52.059","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/login/default","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376966897448517632"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:38:42.256","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0008604,"trackId":"376968617939435520"}
|
||||
{"level":"INFO","ts":"2025-11-06 13:49:29.911","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0006377,"trackId":"376971334405586944"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:03:51.212","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0009761,"trackId":"376974946959622144"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:06:06.794","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376975515635941376"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:18:02.722","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376978518455615488"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:18:38.073","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0008201,"trackId":"376978666724261888"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:19:30.070","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0002887,"trackId":"376978884819681280"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:20:01.974","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0008341,"trackId":"376979018630561792"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:20:12.293","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979061915779072"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:22:02.141","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0009975,"trackId":"376979522647490560"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:22:19.654","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0009764,"trackId":"376979596102336512"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:22:58.685","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979759810215936"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:22:59.290","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979762351964160"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:22:59.524","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979763333431296"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:22:59.723","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979764168097792"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:22:59.923","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979765006958592"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:00.125","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0001014,"trackId":"376979765854208000"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:08.297","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979800130060288"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:09.077","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0010337,"trackId":"376979803397423104"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:12.741","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979818765352960"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:13.561","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979822208876544"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:14.337","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979825463656448"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:15.086","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979828605190144"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:15.826","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979831708975104"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:16.041","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979832610750464"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:16.207","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979833307004928"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:16.410","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979834158448640"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:16.624","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0009752,"trackId":"376979835051835392"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:16.824","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979835894890496"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:17.023","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979836729556992"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:17.222","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0004907,"trackId":"376979837564223488"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:17.443","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0002058,"trackId":"376979838491164672"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:17.622","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0009347,"trackId":"376979839241945088"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:17.841","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979840160497664"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:18.041","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0002851,"trackId":"376979840999358464"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:18.259","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979841913716736"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:18.457","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.000401,"trackId":"376979842739994624"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:18.673","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979843650158592"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:18.891","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979844564516864"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:19.110","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0006448,"trackId":"376979845478875136"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:19.309","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979846317735936"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:19.525","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979847223705600"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:19.740","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0007353,"trackId":"376979848121286656"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:19.939","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979848960147456"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:20.140","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.000381,"trackId":"376979849803202560"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:20.376","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0005992,"trackId":"376979850793058304"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:20.591","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376979851694833664"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:20.773","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0005447,"trackId":"376979852454002688"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:23:21.190","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0003462,"trackId":"376979854207221760"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:25:18.653","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376980346882752512"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:25:20.423","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376980354306670592"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:25:33.356","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.000976,"trackId":"376980408547409920"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:25:48.802","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376980473336823808"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:25:49.654","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0009762,"trackId":"376980476906176512"}
|
||||
{"level":"INFO","ts":"2025-11-06 14:26:01.687","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0,"trackId":"376980527380430848"}
|
||||
{"level":"INFO","ts":"2025-11-06 16:01:53.899","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/auth/test","method":"POST","ip":"192.168.1.136","elapsed":0.0009752,"trackId":"377004653902036992"}
|
||||
Whitespace-only changes.
Reference in new issue
Block a user