create-art-app-pino 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +213 -0
  2. package/dist/index.cjs +418 -0
  3. package/package.json +44 -0
  4. package/template/auth-art/_env.development +2 -0
  5. package/template/auth-art/src/api/auth.ts +37 -0
  6. package/template/auth-art/src/router/guards/beforeEach.ts +64 -0
  7. package/template/auth-art/src/router/index.ts.ejs +52 -0
  8. package/template/auth-art/src/store/modules/user.ts +64 -0
  9. package/template/auth-art/src/utils/http/index.ts +60 -0
  10. package/template/auth-art/src/views/auth/login/index.vue +172 -0
  11. package/template/auth-zhihuishu/_env.development +5 -0
  12. package/template/auth-zhihuishu/_env.local.example +5 -0
  13. package/template/auth-zhihuishu/src/constants/auth.ts +10 -0
  14. package/template/auth-zhihuishu/src/router/guards/beforeEach.ts +47 -0
  15. package/template/auth-zhihuishu/src/router/index.ts.ejs +46 -0
  16. package/template/auth-zhihuishu/src/services/authService.ts +55 -0
  17. package/template/auth-zhihuishu/src/store/modules/auth.ts +227 -0
  18. package/template/auth-zhihuishu/src/types/auth.ts +43 -0
  19. package/template/auth-zhihuishu/src/utils/auth.ts +161 -0
  20. package/template/auth-zhihuishu/src/utils/http/index.ts +61 -0
  21. package/template/base/AGENTS.md.ejs +65 -0
  22. package/template/base/_gitignore.ejs +6 -0
  23. package/template/base/_husky/commit-msg +1 -0
  24. package/template/base/_husky/pre-commit.ejs +3 -0
  25. package/template/base/_husky/pre-push +1 -0
  26. package/template/base/_vscode/extensions.json.ejs +6 -0
  27. package/template/base/_vscode/settings.json.ejs +26 -0
  28. package/template/base/commitlint.config.mjs +6 -0
  29. package/template/base/eslint.config.mjs +11 -0
  30. package/template/base/index.html +13 -0
  31. package/template/base/lint-staged.config.mjs.ejs +4 -0
  32. package/template/base/package.json.ejs +58 -0
  33. package/template/base/src/App.vue +3 -0
  34. package/template/base/src/assets/styles/index.scss +10 -0
  35. package/template/base/src/env.d.ts +7 -0
  36. package/template/base/src/main.ts +19 -0
  37. package/template/base/src/router/index.ts.ejs +42 -0
  38. package/template/base/src/store/index.ts +7 -0
  39. package/template/base/src/store/modules/setting.ts +17 -0
  40. package/template/base/src/utils/http/index.ts +34 -0
  41. package/template/base/src/views/dashboard/index.vue +42 -0
  42. package/template/base/src/views/exception/403.vue +25 -0
  43. package/template/base/src/views/exception/404.vue +25 -0
  44. package/template/base/src/views/index/index.vue +122 -0
  45. package/template/base/tsconfig.app.json +12 -0
  46. package/template/base/tsconfig.json +19 -0
  47. package/template/base/tsconfig.node.json +7 -0
  48. package/template/base/vite.config.ts +34 -0
  49. package/template/feature-echarts/package.json +5 -0
  50. package/template/feature-echarts/src/plugins/echarts.ts +22 -0
  51. package/template/feature-markdown/package.json +5 -0
  52. package/template/feature-markdown/src/components/MdPreviewWrapper.vue +26 -0
  53. package/template/feature-sse/package.json +5 -0
  54. package/template/feature-sse/src/utils/sse.ts +100 -0
  55. package/template/scaffold-doc-governance/CHANGELOG.md +19 -0
  56. package/template/scaffold-doc-governance/_github/PULL_REQUEST_TEMPLATE.md +28 -0
  57. package/template/scaffold-doc-governance/docs/README.md +35 -0
  58. package/template/scaffold-doc-governance/docs/adr/0001-doc-governance.md +195 -0
  59. package/template/scaffold-doc-governance/docs/architecture/.gitkeep +0 -0
  60. package/template/scaffold-doc-governance/docs/archive/.gitkeep +0 -0
  61. package/template/scaffold-doc-governance/docs/changes/requirements/.gitkeep +0 -0
  62. package/template/scaffold-doc-governance/docs/getting-started/quick-start.md.ejs +62 -0
  63. package/template/scaffold-doc-governance/docs/guides/.gitkeep +0 -0
  64. package/template/scaffold-doc-governance/docs/maintenance/incidents/.gitkeep +0 -0
  65. package/template/scaffold-doc-governance/docs/maintenance/troubleshooting.md +25 -0
  66. package/template/scaffold-doc-governance/docs/reference/.gitkeep +0 -0
  67. package/template/scaffold-doc-governance/docs/reference/cli.md +46 -0
  68. package/template/scaffold-doc-governance/docs/templates/adr.md +21 -0
  69. package/template/scaffold-doc-governance/docs/templates/incident.md +25 -0
  70. package/template/scaffold-doc-governance/docs/templates/requirement.md +29 -0
  71. package/template/scaffold-doc-governance/package.json +5 -0
  72. package/template/scaffold-doc-governance/scripts/docs/lint-frontmatter.mjs +85 -0
  73. package/template/scaffold-doc-governance/scripts/docs/lint-links.sh +13 -0
  74. package/template/scaffold-doc-governance/scripts/docs/lint-naming.mjs +89 -0
  75. package/template/scaffold-doc-governance/scripts/docs/lint-no-private-path.sh +19 -0
@@ -0,0 +1,37 @@
1
+ import request from '@/utils/http'
2
+
3
+ export interface LoginParams {
4
+ userName: string
5
+ password: string
6
+ }
7
+
8
+ export interface LoginResponse {
9
+ token: string
10
+ refreshToken?: string
11
+ }
12
+
13
+ export interface UserInfo {
14
+ userId: number | string
15
+ userName: string
16
+ avatar?: string
17
+ roles?: string[]
18
+ }
19
+
20
+ /**
21
+ * 登录
22
+ */
23
+ export function fetchLogin(params: LoginParams) {
24
+ return request.post<LoginResponse>({
25
+ url: '/api/auth/login',
26
+ params,
27
+ })
28
+ }
29
+
30
+ /**
31
+ * 获取用户信息
32
+ */
33
+ export function fetchGetUserInfo() {
34
+ return request.get<UserInfo>({
35
+ url: '/api/user/info',
36
+ })
37
+ }
@@ -0,0 +1,64 @@
1
+ import type { Router } from 'vue-router'
2
+ import NProgress from 'nprogress'
3
+ import { useUserStore } from '@/store/modules/user'
4
+ import { fetchGetUserInfo } from '@/api/auth'
5
+
6
+ // 是否已获取过用户信息
7
+ let hasLoadedUserInfo = false
8
+
9
+ /**
10
+ * 重置路由守卫状态(登出时调用)
11
+ */
12
+ export function resetRouterState(): void {
13
+ hasLoadedUserInfo = false
14
+ }
15
+
16
+ /**
17
+ * 设置路由前置守卫
18
+ */
19
+ export function setupBeforeEachGuard(router: Router): void {
20
+ router.beforeEach(async (to, _from, next) => {
21
+ NProgress.start()
22
+
23
+ const userStore = useUserStore()
24
+
25
+ // 访问登录页直接放行
26
+ if (to.path === '/login') {
27
+ if (userStore.isLogin) {
28
+ next('/')
29
+ } else {
30
+ next()
31
+ }
32
+ return
33
+ }
34
+
35
+ // 未登录跳转到登录页
36
+ if (!userStore.isLogin) {
37
+ next({
38
+ path: '/login',
39
+ query: { redirect: to.fullPath },
40
+ })
41
+ return
42
+ }
43
+
44
+ // 首次进入已登录页面时获取用户信息
45
+ if (!hasLoadedUserInfo) {
46
+ try {
47
+ const userInfo = await fetchGetUserInfo()
48
+ userStore.setUserInfo(userInfo as any)
49
+ hasLoadedUserInfo = true
50
+ } catch (error) {
51
+ console.error('[Router] 获取用户信息失败:', error)
52
+ userStore.logOut()
53
+ next({ path: '/login' })
54
+ return
55
+ }
56
+ }
57
+
58
+ next()
59
+ })
60
+
61
+ router.afterEach(() => {
62
+ NProgress.done()
63
+ })
64
+ }
@@ -0,0 +1,52 @@
1
+ import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
2
+ import type { RouteRecordRaw } from 'vue-router'
3
+ import { setupBeforeEachGuard } from './guards/beforeEach'
4
+
5
+ const routes: RouteRecordRaw[] = [
6
+ {
7
+ path: '/login',
8
+ name: 'Login',
9
+ component: () => import('@/views/auth/login/index.vue'),
10
+ meta: { title: '登录' },
11
+ },
12
+ {
13
+ path: '/',
14
+ name: 'Layout',
15
+ component: () => import('@/views/index/index.vue'),
16
+ redirect: '/dashboard',
17
+ children: [
18
+ {
19
+ path: 'dashboard',
20
+ name: 'Dashboard',
21
+ component: () => import('@/views/dashboard/index.vue'),
22
+ meta: { title: '仪表盘', icon: 'ep:home-filled' },
23
+ },
24
+ ],
25
+ },
26
+ {
27
+ path: '/403',
28
+ name: 'Forbidden',
29
+ component: () => import('@/views/exception/403.vue'),
30
+ meta: { title: '403' },
31
+ },
32
+ {
33
+ path: '/404',
34
+ name: 'NotFound',
35
+ component: () => import('@/views/exception/404.vue'),
36
+ meta: { title: '404' },
37
+ },
38
+ {
39
+ path: '/:pathMatch(.*)*',
40
+ redirect: '/404',
41
+ },
42
+ ]
43
+
44
+ const router = createRouter({
45
+ history: <%- options.routerMode === 'hash' ? 'createWebHashHistory()' : "createWebHistory(import.meta.env.BASE_URL)" %>,
46
+ routes,
47
+ })
48
+
49
+ // 设置路由守卫
50
+ setupBeforeEachGuard(router)
51
+
52
+ export default router
@@ -0,0 +1,64 @@
1
+ import { defineStore } from 'pinia'
2
+ import { ref, computed } from 'vue'
3
+ import { useRouter } from 'vue-router'
4
+
5
+ export const useUserStore = defineStore(
6
+ 'userStore',
7
+ () => {
8
+ const router = useRouter()
9
+
10
+ const isLogin = ref(false)
11
+ const accessToken = ref('')
12
+ const refreshToken = ref('')
13
+ const info = ref<Record<string, any>>({})
14
+
15
+ const getUserInfo = computed(() => info.value)
16
+
17
+ function setUserInfo(newInfo: Record<string, any>) {
18
+ info.value = newInfo
19
+ }
20
+
21
+ function setLoginStatus(status: boolean) {
22
+ isLogin.value = status
23
+ }
24
+
25
+ function setToken(newAccessToken: string, newRefreshToken?: string) {
26
+ accessToken.value = newAccessToken
27
+ if (newRefreshToken) {
28
+ refreshToken.value = newRefreshToken
29
+ }
30
+ }
31
+
32
+ function logOut() {
33
+ info.value = {}
34
+ isLogin.value = false
35
+ accessToken.value = ''
36
+ refreshToken.value = ''
37
+
38
+ const currentRoute = router.currentRoute.value
39
+ const redirect = currentRoute.path !== '/login' ? currentRoute.fullPath : undefined
40
+ router.push({
41
+ path: '/login',
42
+ query: redirect ? { redirect } : undefined,
43
+ })
44
+ }
45
+
46
+ return {
47
+ isLogin,
48
+ accessToken,
49
+ refreshToken,
50
+ info,
51
+ getUserInfo,
52
+ setUserInfo,
53
+ setLoginStatus,
54
+ setToken,
55
+ logOut,
56
+ }
57
+ },
58
+ {
59
+ persist: {
60
+ key: 'user',
61
+ storage: localStorage,
62
+ },
63
+ },
64
+ )
@@ -0,0 +1,60 @@
1
+ import type { AxiosRequestConfig } from 'axios'
2
+ import axios from 'axios'
3
+ import { ElMessage } from 'element-plus'
4
+ import { useUserStore } from '@/store/modules/user'
5
+
6
+ const http = axios.create({
7
+ baseURL: import.meta.env.VITE_API_BASE_URL || '',
8
+ timeout: 30000,
9
+ })
10
+
11
+ // 请求拦截器:注入 token
12
+ http.interceptors.request.use(
13
+ (config) => {
14
+ const userStore = useUserStore()
15
+ if (userStore.accessToken) {
16
+ config.headers.Authorization = `Bearer ${userStore.accessToken}`
17
+ }
18
+ return config
19
+ },
20
+ error => Promise.reject(error),
21
+ )
22
+
23
+ // 响应拦截器:处理 401 和业务错误
24
+ http.interceptors.response.use(
25
+ (response) => {
26
+ return response.data
27
+ },
28
+ (error) => {
29
+ if (error.response?.status === 401) {
30
+ const userStore = useUserStore()
31
+ userStore.logOut()
32
+ return Promise.reject(error)
33
+ }
34
+ const message = error.response?.data?.message || error.message || '请求失败'
35
+ ElMessage.error(message)
36
+ return Promise.reject(error)
37
+ },
38
+ )
39
+
40
+ interface RequestConfig extends AxiosRequestConfig {
41
+ url: string
42
+ params?: any
43
+ }
44
+
45
+ const request = {
46
+ get<T>(config: RequestConfig): Promise<T> {
47
+ return http.get(config.url, { params: config.params, ...config }) as unknown as Promise<T>
48
+ },
49
+ post<T>(config: RequestConfig): Promise<T> {
50
+ return http.post(config.url, config.params, config) as unknown as Promise<T>
51
+ },
52
+ put<T>(config: RequestConfig): Promise<T> {
53
+ return http.put(config.url, config.params, config) as unknown as Promise<T>
54
+ },
55
+ delete<T>(config: RequestConfig): Promise<T> {
56
+ return http.delete(config.url, { params: config.params, ...config }) as unknown as Promise<T>
57
+ },
58
+ }
59
+
60
+ export default request
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <div class="login-container">
3
+ <div class="login-card">
4
+ <div class="login-header">
5
+ <h2 class="login-title">欢迎登录</h2>
6
+ <p class="login-subtitle">请输入您的账号和密码</p>
7
+ </div>
8
+
9
+ <el-form
10
+ ref="formRef"
11
+ :model="formData"
12
+ :rules="rules"
13
+ class="login-form"
14
+ @keyup.enter="handleSubmit"
15
+ >
16
+ <el-form-item prop="username">
17
+ <el-input
18
+ v-model.trim="formData.username"
19
+ placeholder="请输入用户名"
20
+ size="large"
21
+ :prefix-icon="User"
22
+ />
23
+ </el-form-item>
24
+
25
+ <el-form-item prop="password">
26
+ <el-input
27
+ v-model.trim="formData.password"
28
+ placeholder="请输入密码"
29
+ type="password"
30
+ size="large"
31
+ show-password
32
+ :prefix-icon="Lock"
33
+ />
34
+ </el-form-item>
35
+
36
+ <el-form-item>
37
+ <div class="login-options">
38
+ <el-checkbox v-model="formData.rememberPassword">记住密码</el-checkbox>
39
+ </div>
40
+ </el-form-item>
41
+
42
+ <el-form-item>
43
+ <el-button
44
+ type="primary"
45
+ size="large"
46
+ class="login-button"
47
+ :loading="loading"
48
+ @click="handleSubmit"
49
+ >
50
+ 登录
51
+ </el-button>
52
+ </el-form-item>
53
+ </el-form>
54
+ </div>
55
+ </div>
56
+ </template>
57
+
58
+ <script setup lang="ts">
59
+ import { ref, reactive } from 'vue'
60
+ import { useRouter, useRoute } from 'vue-router'
61
+ import { User, Lock } from '@element-plus/icons-vue'
62
+ import { ElNotification, type FormInstance, type FormRules } from 'element-plus'
63
+ import { useUserStore } from '@/store/modules/user'
64
+ import { fetchLogin } from '@/api/auth'
65
+
66
+ defineOptions({ name: 'Login' })
67
+
68
+ const router = useRouter()
69
+ const route = useRoute()
70
+ const userStore = useUserStore()
71
+ const formRef = ref<FormInstance>()
72
+ const loading = ref(false)
73
+
74
+ const formData = reactive({
75
+ username: '',
76
+ password: '',
77
+ rememberPassword: true,
78
+ })
79
+
80
+ const rules: FormRules = {
81
+ username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
82
+ password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
83
+ }
84
+
85
+ async function handleSubmit() {
86
+ if (!formRef.value) return
87
+
88
+ const valid = await formRef.value.validate().catch(() => false)
89
+ if (!valid) return
90
+
91
+ loading.value = true
92
+
93
+ try {
94
+ const { token, refreshToken } = await fetchLogin({
95
+ userName: formData.username,
96
+ password: formData.password,
97
+ })
98
+
99
+ if (!token) {
100
+ throw new Error('登录失败:未获取到 token')
101
+ }
102
+
103
+ userStore.setToken(token, refreshToken)
104
+ userStore.setLoginStatus(true)
105
+
106
+ ElNotification({
107
+ title: '登录成功',
108
+ type: 'success',
109
+ duration: 2500,
110
+ message: '欢迎回来!',
111
+ })
112
+
113
+ const redirect = route.query.redirect as string
114
+ router.push(redirect || '/')
115
+ } catch (error: any) {
116
+ ElNotification({
117
+ title: '登录失败',
118
+ type: 'error',
119
+ duration: 3000,
120
+ message: error.message || '请检查用户名和密码',
121
+ })
122
+ } finally {
123
+ loading.value = false
124
+ }
125
+ }
126
+ </script>
127
+
128
+ <style scoped>
129
+ .login-container {
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ min-height: 100vh;
134
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
135
+ }
136
+
137
+ .login-card {
138
+ width: 400px;
139
+ padding: 40px;
140
+ background: #fff;
141
+ border-radius: 12px;
142
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
143
+ }
144
+
145
+ .login-header {
146
+ text-align: center;
147
+ margin-bottom: 30px;
148
+ }
149
+
150
+ .login-title {
151
+ font-size: 24px;
152
+ font-weight: 600;
153
+ color: #1a1a1a;
154
+ margin-bottom: 8px;
155
+ }
156
+
157
+ .login-subtitle {
158
+ font-size: 14px;
159
+ color: #999;
160
+ }
161
+
162
+ .login-options {
163
+ display: flex;
164
+ justify-content: space-between;
165
+ align-items: center;
166
+ width: 100%;
167
+ }
168
+
169
+ .login-button {
170
+ width: 100%;
171
+ }
172
+ </style>
@@ -0,0 +1,5 @@
1
+ # 认证模式:dev-token(开发)/ cas(生产,需部署在 *.zhihuishu.com 下)
2
+ VITE_AUTH_MODE=dev-token
3
+
4
+ # API 基础地址
5
+ VITE_API_BASE_URL=http://localhost:3000
@@ -0,0 +1,5 @@
1
+ # 本地开发 token,可填写纯 token 或 Bearer xxx
2
+ VITE_DEV_TOKEN=
3
+
4
+ # 本地开发用户 UID(对应后端 user_uid)
5
+ VITE_DEV_USER_UID=
@@ -0,0 +1,10 @@
1
+ export const casCookieName = 'CASLOGC'
2
+ export const casTicketCookieName = 'CASTGC'
3
+ export const zhihuishuCASLoginUrl = 'https://passport.zhihuishu.com/login?source=20&service='
4
+
5
+ export const authStorageKeys = {
6
+ token: 'auth_token',
7
+ userInfo: 'user_info',
8
+ expiresAt: 'auth_token_expires_at',
9
+ mode: 'auth_mode',
10
+ } as const
@@ -0,0 +1,47 @@
1
+ import type { Router } from 'vue-router'
2
+ import NProgress from 'nprogress'
3
+ import { useAuthStore } from '@/store/modules/auth'
4
+ import { isDevTokenAuthMode, isZhihuishuDomain } from '@/utils/auth'
5
+
6
+ /**
7
+ * 设置路由前置守卫(智慧树 CAS + dev-token 双模式)
8
+ */
9
+ export function setupBeforeEachGuard(router: Router): void {
10
+ router.beforeEach(async (to) => {
11
+ NProgress.start()
12
+
13
+ const authStore = useAuthStore()
14
+
15
+ // 错误页直接放行
16
+ if (to.name === 'Forbidden' || to.name === 'NotFound') {
17
+ return true
18
+ }
19
+
20
+ // dev-token 模式
21
+ if (isDevTokenAuthMode()) {
22
+ const success = await authStore.autoLogin()
23
+ if (!success) {
24
+ return { path: '/403', replace: true }
25
+ }
26
+ return true
27
+ }
28
+
29
+ // CAS 模式:需要在智慧树域名下运行
30
+ if (!isZhihuishuDomain()) {
31
+ authStore.logout(to.fullPath)
32
+ return false
33
+ }
34
+
35
+ const success = await authStore.autoLogin()
36
+ if (!success) {
37
+ authStore.logout(to.fullPath)
38
+ return false
39
+ }
40
+
41
+ return true
42
+ })
43
+
44
+ router.afterEach(() => {
45
+ NProgress.done()
46
+ })
47
+ }
@@ -0,0 +1,46 @@
1
+ import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
2
+ import type { RouteRecordRaw } from 'vue-router'
3
+ import { setupBeforeEachGuard } from './guards/beforeEach'
4
+
5
+ const routes: RouteRecordRaw[] = [
6
+ {
7
+ path: '/',
8
+ name: 'Layout',
9
+ component: () => import('@/views/index/index.vue'),
10
+ redirect: '/dashboard',
11
+ children: [
12
+ {
13
+ path: 'dashboard',
14
+ name: 'Dashboard',
15
+ component: () => import('@/views/dashboard/index.vue'),
16
+ meta: { title: '仪表盘', icon: 'ep:home-filled' },
17
+ },
18
+ ],
19
+ },
20
+ {
21
+ path: '/403',
22
+ name: 'Forbidden',
23
+ component: () => import('@/views/exception/403.vue'),
24
+ meta: { title: '403' },
25
+ },
26
+ {
27
+ path: '/404',
28
+ name: 'NotFound',
29
+ component: () => import('@/views/exception/404.vue'),
30
+ meta: { title: '404' },
31
+ },
32
+ {
33
+ path: '/:pathMatch(.*)*',
34
+ redirect: '/404',
35
+ },
36
+ ]
37
+
38
+ const router = createRouter({
39
+ history: <%- options.routerMode === 'hash' ? 'createWebHashHistory()' : "createWebHistory(import.meta.env.BASE_URL)" %>,
40
+ routes,
41
+ })
42
+
43
+ // 设置路由守卫(CAS + dev-token 双模式)
44
+ setupBeforeEachGuard(router)
45
+
46
+ export default router
@@ -0,0 +1,55 @@
1
+ import type {
2
+ AuthLoginData,
3
+ AuthLoginParams,
4
+ AuthLoginResponseData,
5
+ AuthLoginResponsePayload,
6
+ } from '@/types/auth'
7
+ import axios from 'axios'
8
+
9
+ const authService = axios.create({
10
+ baseURL: import.meta.env.VITE_API_BASE_URL || '',
11
+ timeout: 30000,
12
+ })
13
+
14
+ export class AuthServiceError extends Error {
15
+ code?: number
16
+
17
+ constructor(message: string, code?: number) {
18
+ super(message)
19
+ this.name = 'AuthServiceError'
20
+ this.code = code
21
+ }
22
+ }
23
+
24
+ /**
25
+ * 从登录响应中提取 token 与用户信息
26
+ */
27
+ function resolveLoginData(payload: AuthLoginResponsePayload, fallbackUserUid: string): AuthLoginData {
28
+ const responseData: AuthLoginResponseData = payload.data ?? payload
29
+
30
+ if (!responseData.token) {
31
+ throw new AuthServiceError(payload.message || '登录响应缺少 token', payload.code)
32
+ }
33
+
34
+ return {
35
+ token: responseData.token,
36
+ userUid: responseData.user_uid === undefined ? fallbackUserUid : String(responseData.user_uid),
37
+ name: responseData.name?.trim() || fallbackUserUid,
38
+ expiresIn: responseData.expires_in,
39
+ isSuperAdmin: responseData.is_super_admin ?? false,
40
+ }
41
+ }
42
+
43
+ /**
44
+ * 使用 CASLOGC 中的用户 ID 换取业务 token
45
+ */
46
+ export async function loginWithCasUser(params: AuthLoginParams): Promise<AuthLoginData> {
47
+ const response = await authService.post<AuthLoginResponsePayload>('/auth/login', params)
48
+ const payload = response.data
49
+
50
+ if (typeof payload.code === 'number' && payload.code !== 200) {
51
+ throw new AuthServiceError(payload.message || '登录失败', payload.code)
52
+ }
53
+
54
+ return resolveLoginData(payload, params.user_uid)
55
+ }