feat(store): authSlice 扩展 userInfo 并持久化 token

This commit is contained in:
ozh 2026-09-15 11:57:54 +08:00
parent 884a7b8343
commit c65d179333
2 changed files with 71 additions and 5 deletions

View File

@ -1,30 +1,39 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { storage } from '@/utils/storage'
// import type 避免运行时循环依赖:store → api/auth → http/client → store
import type { WxUserInfo } from '@/api/auth'
interface State {
token: string | null
userId: string | null
userInfo: WxUserInfo | null // 登录返回的完整用户信息(spec D6)
}
const STORAGE_KEY = 'auth_token'
const initialState: State = {
// initialState 用惰性函数:保证 reducer(undefined) 时才读取 storage,
// 使测试里 beforeEach 清空 mock 后再初始化能拿到正确的干净状态
const initialState = (): State => ({
token: storage.get<string>(STORAGE_KEY),
userId: null
}
userId: null,
userInfo: null
})
const slice = createSlice({
name: 'auth',
initialState,
reducers: {
setAuth(state, action: PayloadAction<{ token: string; userId: string }>) {
// token 落盘持久化;userInfo 只入内存,冷启动由后续接口拉取,避免缓存过期数据(spec D6)
setAuth(state, action: PayloadAction<{ token: string; userInfo: WxUserInfo }>) {
state.token = action.payload.token
state.userId = action.payload.userId
state.userId = action.payload.userInfo.userId
state.userInfo = action.payload.userInfo
storage.set(STORAGE_KEY, action.payload.token)
},
clearAuth(state) {
state.token = null
state.userId = null
state.userInfo = null
storage.remove(STORAGE_KEY)
}
}

View File

@ -0,0 +1,57 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
// 内存 Map 模拟 Taro storage(同 tests/unit/family/family.spec.ts 模式)
const mem = new Map<string, unknown>()
vi.mock('@/utils/storage', () => ({
storage: {
get: <T>(key: string): T | null => (mem.has(key) ? (mem.get(key) as T) : null),
set: <T>(key: string, value: T): void => { mem.set(key, value) },
remove: (key: string): void => { mem.delete(key) }
}
}))
import reducer, { setAuth, clearAuth } from '@/store/authSlice'
import type { WxUserInfo } from '@/api/auth'
const userInfo: WxUserInfo = {
userId: 'u-100',
userType: 1,
avatar: 'https://example.com/a.png',
nickName: '张大爷',
mobile: '13800000000',
gender: 1,
region: '深圳市南山区',
loginFrom: 'miniapp'
}
beforeEach(() => {
mem.clear()
})
describe('authSlice', () => {
it('setAuth 写入 token/userId/userInfo,token 落盘', () => {
let state = reducer(undefined, { type: 'init' })
state = reducer(state, setAuth({ token: 'tk-1', userInfo }))
expect(state.token).toBe('tk-1')
expect(state.userId).toBe('u-100')
expect(state.userInfo).toEqual(userInfo)
expect(mem.get('auth_token')).toBe('tk-1')
})
it('setAuth 后 clearAuth 全部清空并移除持久化 token', () => {
let state = reducer(undefined, { type: 'init' })
state = reducer(state, setAuth({ token: 'tk-1', userInfo }))
state = reducer(state, clearAuth())
expect(state.token).toBeNull()
expect(state.userId).toBeNull()
expect(state.userInfo).toBeNull()
expect(mem.has('auth_token')).toBe(false)
})
it('初始态从 storage 恢复 token', () => {
mem.set('auth_token', 'tk-saved')
const state = reducer(undefined, { type: 'init' })
expect(state.token).toBe('tk-saved')
expect(state.userInfo).toBeNull()
})
})