import { defineStore } from 'pinia' import { authApi, profileApi } from '@/api/auth' import { courseApi } from '@/api/courses' import { activityApi } from '@/api/activities' import type { ApiUser } from '@/types/api' import type { UserProfile } from '@/types' import { clearToken, setToken } from '@/utils/request' import { DEFAULT_AVATAR_SRC } from '@/utils/svg' import { CAN_SEE_RADAR_STORAGE_KEY } from '@/stores/tabbar' const STORAGE_KEYS = { profile: 'slakeProfile', } function syncRadarTabPermission(user: ApiUser | null) { uni.setStorageSync(CAN_SEE_RADAR_STORAGE_KEY, Boolean(user?.can_see_radar)) } function readJson(key: string, fallback: T): T { try { const raw = uni.getStorageSync(key) if (!raw) return fallback return typeof raw === 'string' ? JSON.parse(raw) : raw } catch { return fallback } } export const useUserStore = defineStore('user', { state: () => ({ user: null as ApiUser | null, profile: readJson(STORAGE_KEYS.profile, {}), signedCourseIds: [] as number[], signedActivityIds: [] as number[], authReady: false, _authInitPromise: null as Promise | null, }), getters: { isLoggedIn: (state) => Boolean(state.user), isPartner: (state) => Boolean(state.user?.is_partner), displayName: (state) => { if (!state.user) return '未登录' return state.user.name || state.user.nickname || state.profile.name || '微信用户' }, avatarSrc: (state) => { if (!state.user) return DEFAULT_AVATAR_SRC return state.user.avatar_url || state.profile.avatar || DEFAULT_AVATAR_SRC }, canSeeRadar: (state) => Boolean(state.user?.can_see_radar), canSeePapers: (state) => Boolean(state.user?.can_see_papers), isStaff: (state) => Boolean(state.user?.is_admin || state.user?.is_grid_member), }, actions: { async loginWithWechat(code: string, profile?: { nickname?: string; avatar_url?: string }) { const data = await authApi.wechatLogin({ code, ...profile }) this.applyAuth(data.token, data.user) }, async loginWithDev(profile?: { nickname?: string; avatar_url?: string }) { const data = await authApi.devLogin(profile) this.applyAuth(data.token, data.user) }, applyAuth(token: string, user: ApiUser) { setToken(token) this.user = user this.profile = { ...this.profile, name: user.name || user.nickname || this.profile.name, avatar: user.avatar_url || this.profile.avatar, company: user.company || this.profile.company, mobile: user.mobile || this.profile.mobile, title: user.job_title || this.profile.title, research_direction: user.research_direction || this.profile.research_direction, research_direction_ids: user.research_direction_ids || this.profile.research_direction_ids, custom_research_directions: [], } uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile)) syncRadarTabPermission(user) }, async fetchMe() { const user = await authApi.me() this.user = user this.profile = { ...this.profile, name: user.name || user.nickname || this.profile.name, avatar: user.avatar_url || this.profile.avatar, company: user.company || this.profile.company, mobile: user.mobile || this.profile.mobile, title: user.job_title || this.profile.title, research_direction: user.research_direction || this.profile.research_direction, research_direction_ids: user.research_direction_ids || this.profile.research_direction_ids, custom_research_directions: [], } uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile)) syncRadarTabPermission(user) await this.syncSignups() }, async syncSignups() { if (!this.isLoggedIn) return const [courses, activities] = await Promise.all([ courseApi.mySignups(), activityApi.mySignups(), ]) this.signedCourseIds = courses.items.map((item) => item.id) this.signedActivityIds = activities.items.map((item) => item.id) }, async logout() { try { await authApi.logout() } catch { // ignore } this.user = null this.signedCourseIds = [] this.signedActivityIds = [] syncRadarTabPermission(null) clearToken() }, async saveProfile(profile: UserProfile) { let user = this.user if (this.isLoggedIn) { user = await profileApi.update({ name: profile.name, mobile: profile.mobile, company: profile.company, avatar_url: profile.avatar, nickname: profile.name, job_title: profile.title, research_direction_ids: profile.research_direction_ids, custom_research_directions: profile.custom_research_directions, }) this.user = user } this.profile = { ...this.profile, ...profile, research_direction: user?.research_direction || this.profile.research_direction, research_direction_ids: user?.research_direction_ids || profile.research_direction_ids, custom_research_directions: [], } uni.setStorageSync(STORAGE_KEYS.profile, JSON.stringify(this.profile)) }, markCourseSigned(id: number) { if (!this.signedCourseIds.includes(id)) { this.signedCourseIds.push(id) } }, markActivitySigned(id: number) { if (!this.signedActivityIds.includes(id)) { this.signedActivityIds.push(id) } }, hasSignedCourse(id: number) { return this.signedCourseIds.includes(id) }, hasSignedActivity(id: number) { return this.signedActivityIds.includes(id) }, async ensureAuth() { const token = uni.getStorageSync('slake_miniapp_token') if (!token) { this.user = null this.authReady = true return } if (this.user) { this.authReady = true return } if (!this._authInitPromise) { this._authInitPromise = this.fetchMe() .then(() => { this.authReady = true }) .catch(() => { clearToken() this.user = null this.profile = {} uni.removeStorageSync(STORAGE_KEYS.profile) this.authReady = true }) .finally(() => { this._authInitPromise = null }) } await this._authInitPromise }, async bootstrap() { await this.ensureAuth() }, }, })