hookehuyr

feat(登录): 添加用户信息认证工具函数并重构登录逻辑

refactor: 将登录页面的用户信息处理逻辑提取到独立工具函数
test: 添加用户信息认证工具函数的单元测试
chore: 添加vitest测试框架及相关依赖
###
# @Date: 2025-03-20 23:40:15
# @LastEditors: hookehuyr hookehuyr@gmail.com
# @LastEditTime: 2025-12-02 18:34:41
# @LastEditTime: 2025-12-09 21:05:15
# @FilePath: /mlaj/.env.development
# @Description: 文件描述
###
......
......@@ -7,6 +7,7 @@
"dev": ". ~/.nvm/nvm.sh && nvm use 18.19.1 && vite",
"build": ". ~/.nvm/nvm.sh && nvm use 18.19.1 && vite build",
"preview": "vite preview",
"test": "vitest run",
"tar": "tar -czvpf dist.tar.gz mlaj",
"build_tar": "npm run build && npm run tar",
"scp-dev": "scp dist.tar.gz ipadbiz-inner:/opt/space-dev/f",
......@@ -62,6 +63,7 @@
"tailwindcss": "^3.4.1",
"unplugin-auto-import": "^19.1.1",
"unplugin-vue-components": "^28.4.1",
"vite": "^6.2.0"
"vite": "^6.2.0",
"vitest": "^3.2.0"
}
}
......
This diff is collapsed. Click to expand it.
import { describe, expect, it, vi } from 'vitest'
import { applyUserInfoAuth } from '../auth_user_info'
const createStorage = () => {
const store = {}
return {
store,
setItem: vi.fn((key, value) => {
store[key] = value
}),
getItem: vi.fn((key) => store[key] ?? null),
removeItem: vi.fn((key) => {
delete store[key]
}),
clear: vi.fn(() => {
Object.keys(store).forEach((key) => delete store[key])
})
}
}
describe('applyUserInfoAuth', () => {
it('从 response.data.user_info 写入鉴权与缓存', () => {
const storage = createStorage()
const set_auth_headers = vi.fn()
const response = {
data: {
user_info: {
user_id: '100',
HTTP_USER_TOKEN: 'token-xxx',
user_name: '张三'
}
}
}
const ok = applyUserInfoAuth(response, { storage, set_auth_headers })
expect(ok).toBe(true)
expect(set_auth_headers).toHaveBeenCalledTimes(1)
expect(set_auth_headers).toHaveBeenCalledWith('100', 'token-xxx')
expect(storage.setItem).toHaveBeenCalledTimes(1)
expect(storage.setItem).toHaveBeenCalledWith('user_info', JSON.stringify(response.data.user_info))
})
it('缺少 user_id 或 HTTP_USER_TOKEN 时不做任何写入', () => {
const storage = createStorage()
const set_auth_headers = vi.fn()
const ok = applyUserInfoAuth({ user_id: '100' }, { storage, set_auth_headers })
expect(ok).toBe(false)
expect(set_auth_headers).not.toHaveBeenCalled()
expect(storage.setItem).not.toHaveBeenCalled()
})
it('支持直接传入 user_info 对象', () => {
const storage = createStorage()
const set_auth_headers = vi.fn()
const user_info = { user_id: 1, HTTP_USER_TOKEN: 't' }
const ok = applyUserInfoAuth(user_info, { storage, set_auth_headers })
expect(ok).toBe(true)
expect(set_auth_headers).toHaveBeenCalledWith(1, 't')
expect(storage.setItem).toHaveBeenCalledWith('user_info', JSON.stringify(user_info))
})
})
export const applyUserInfoAuth = (input, options = {}) => {
const user_info = input && input.data && input.data.user_info ? input.data.user_info : input
if (!user_info || typeof user_info !== 'object') {
return false
}
const { user_id, HTTP_USER_TOKEN } = user_info
if (!user_id || !HTTP_USER_TOKEN) {
return false
}
const set_auth_headers = options.set_auth_headers
if (typeof set_auth_headers === 'function') {
set_auth_headers(user_id, HTTP_USER_TOKEN)
}
const storage = Object.prototype.hasOwnProperty.call(options, 'storage') ? options.storage : globalThis.localStorage
if (storage && typeof storage.setItem === 'function') {
storage.setItem('user_info', JSON.stringify(user_info || {}))
}
return true
}
......@@ -192,6 +192,7 @@ import { smsAPI } from "@/api/common";
import { showToast } from "vant";
import UserAgreement from "@/components/ui/UserAgreement.vue";
import { setAuthHeaders } from "@/utils/axios";
import { applyUserInfoAuth } from "@/utils/auth_user_info";
import weixinLogo from '@/assets/images/weixin_logo_lg.jpeg'
import { startWxAuth } from '@/router/guards'
import { wxInfo } from '@/utils/tools'
......@@ -310,14 +311,7 @@ const handleSubmit = async () => {
error.value = response.msg || "登录失败,请检查您的输入项";
return;
} else {
// 获取data里面的 user_id, HTTP_USER_TOKEN, 并设置到后面所有的请求头里面,headers.User-Id, headers.User-Token
const { user_id, HTTP_USER_TOKEN } = response?.data?.user_info || {};
if (user_id && HTTP_USER_TOKEN) {
// 设置认证请求头
setAuthHeaders(user_id, HTTP_USER_TOKEN);
// 缓存user_info
localStorage.setItem('user_info', JSON.stringify(response?.data?.user_info || {}));
}
applyUserInfoAuth(response, { set_auth_headers: setAuthHeaders, storage: localStorage })
}
const { code, data } = await getUserInfoAPI();
......
......@@ -90,6 +90,7 @@ import { showToast } from 'vant'
import { useTitle } from '@vueuse/core'
import { setAuthHeaders } from "@/utils/axios";
import VideoBackground from '@/components/ui/VideoBackground.vue'
import { applyUserInfoAuth } from '@/utils/auth_user_info'
// 导入接口
import { smsAPI } from '@/api/common'
......@@ -172,14 +173,7 @@ const handleLogin = async () => {
try {
const res = await loginAPI({ mobile: phone.value, sms_code: code.value, entry: entry.value, referrer_user_id: referrer_user_id.value })
if (res.code) {
// 获取data里面的 user_id, HTTP_USER_TOKEN, 并设置到后面所有的请求头里面,headers.User-Id, headers.User-Token
const { user_id, HTTP_USER_TOKEN } = res?.data?.user_info || {};
if (user_id && HTTP_USER_TOKEN) {
// 设置认证请求头
setAuthHeaders(user_id, HTTP_USER_TOKEN);
// 缓存user_info
localStorage.setItem('user_info', JSON.stringify(res?.data?.user_info || {}));
}
applyUserInfoAuth(res, { set_auth_headers: setAuthHeaders, storage: localStorage })
const userInfo = await userInfoAPI()
// 登录之后需要判断是否有完善个人信息
if (userInfo.code) {
......