Files
happy-life-star/mini-program/src/stores/app.js
T

502 lines
13 KiB
JavaScript

import { reactive, computed, readonly } from 'vue'
import * as authService from '../services/auth.js'
import * as userProfileService from '../services/userProfile.js'
import * as lifeEventService from '../services/lifeEvent.js'
import * as epicScriptService from '../services/epicScript.js'
import * as lifePathService from '../services/lifePath.js'
const state = reactive({
isLoggedIn: false,
isLoading: false,
currentStep: 1,
userInfo: null,
userProfile: null,
events: [],
scripts: [],
inspirationRecommendations: [],
paths: [],
currentPath: null,
registrationData: {
nickname: '',
gender: '',
mbti: '',
zodiac: '',
profession: '',
hobbies: [],
childhood: { date: '', text: '' },
joy: { date: '', text: '' },
low: { date: '', text: '' },
future: { vision: '', ideal: '' },
city: '',
industry: '',
company: '',
personalityTags: [],
personalityTagLibrary: [],
hobbyLibrary: [],
birthday: ''
}
})
const SESSION_STATUS = {
AUTHENTICATED: 'authenticated',
UNAUTHENTICATED: 'unauthenticated',
ERROR: 'error'
}
const logAuth = (label, payload = {}) => {
console.log(`[AUTH] ${label}`, payload)
}
const hasProfile = computed(() => {
return state.userProfile && state.userProfile.nickname
})
const resetAuthState = () => {
state.isLoggedIn = false
state.userInfo = null
state.userProfile = null
}
const applyProfileResponse = (profileData) => {
const profile = userProfileService.transformToFrontendFormat(profileData)
state.userProfile = profile
if (profile) {
Object.assign(state.registrationData, profile)
}
return profile
}
const clearStoredTokens = () => {
uni.removeStorageSync('access_token')
uni.removeStorageSync('refresh_token')
}
const login = async (phone, smsCode) => {
state.isLoading = true
try {
const res = await authService.login({ phone, smsCode })
state.isLoggedIn = true
await fetchUserProfile()
return { success: true, hasProfile: hasProfile.value }
} catch (error) {
return { success: false, error: error.message }
} finally {
state.isLoading = false
}
}
const loginWithWechat = async (payload = {}) => {
state.isLoading = true
try {
await authService.wechatLogin(payload)
state.isLoggedIn = true
await fetchUserProfile()
return { success: true, hasProfile: hasProfile.value }
} catch (error) {
return { success: false, error: error.message }
} finally {
state.isLoading = false
}
}
const logout = async () => {
await authService.logout()
resetAuthState()
}
const fetchUserProfile = async (options = {}) => {
const { throwOnError = false } = options
try {
const res = await userProfileService.getCurrentProfile()
if (res.data) {
const profile = applyProfileResponse(res.data)
logAuth('profile:success', { hasProfile: true })
return profile
} else {
state.userProfile = null
logAuth('profile:empty', { hasProfile: false })
}
return null
} catch (error) {
state.userProfile = null
logAuth('profile:fail', {
message: error.message,
statusCode: error.statusCode,
code: error.code,
isNetworkError: Boolean(error.isNetworkError)
})
if (throwOnError) {
throw error
}
return null
}
}
const saveUserProfile = async () => {
state.isLoading = true
try {
const dataToSave = { ...state.registrationData }
let response
if (state.userProfile?.id) {
response = await userProfileService.updateProfile({
id: state.userProfile.id,
...dataToSave
})
} else {
response = await userProfileService.createProfile(dataToSave)
}
if (response?.data) {
applyProfileResponse(response.data)
}
await fetchUserProfile()
return { success: true }
} catch (error) {
return { success: false, error: error.message }
} finally {
state.isLoading = false
}
}
const updateRegistration = (data) => {
Object.assign(state.registrationData, data)
}
const setCurrentStep = (step) => {
state.currentStep = step
}
const fetchEvents = async () => {
try {
const res = await lifeEventService.getEventList()
state.events = lifeEventService.transformListToFrontend(res.data || [])
return state.events
} catch (error) {
return []
}
}
const createEvent = async (eventData) => {
try {
await lifeEventService.createEvent(eventData)
await fetchEvents()
return { success: true }
} catch (error) {
return { success: false, error: error.message }
}
}
const updateEvent = async (eventData) => {
try {
await lifeEventService.updateEvent(eventData)
await fetchEvents()
return { success: true }
} catch (error) {
return { success: false, error: error.message }
}
}
const deleteEvent = async (id) => {
try {
await lifeEventService.deleteEvent(id)
await fetchEvents()
return { success: true }
} catch (error) {
return { success: false, error: error.message }
}
}
const assistEventWriting = async (eventData, callbacks = {}) => {
try {
const res = await lifeEventService.assistEventWriting(eventData, callbacks)
return { success: true, data: res.data }
} catch (error) {
return { success: false, error: error.message }
}
}
const chatAboutEvent = async (eventData) => {
try {
const res = await lifeEventService.chatAboutEvent(eventData)
return { success: true, data: res.data }
} catch (error) {
return { success: false, error: error.message }
}
}
const shareEvent = async (eventData) => {
try {
const res = await lifeEventService.shareEvent(eventData)
return { success: true, data: res.data }
} catch (error) {
return { success: false, error: error.message }
}
}
const favoriteEvent = async ({ id, favorite }) => {
try {
const res = await lifeEventService.favoriteEvent({ id, favorite })
return { success: true, data: res.data }
} catch (error) {
return { success: false, error: error.message }
}
}
const getEventById = (id) => {
return state.events.find(event => String(event.id) === String(id)) || null
}
const fetchScripts = async () => {
try {
const res = await epicScriptService.getScriptList()
state.scripts = epicScriptService.transformListToFrontend(res.data || [])
return state.scripts
} catch (error) {
return []
}
}
const createScript = async (scriptData) => {
try {
const res = await epicScriptService.createScript(scriptData)
const script = epicScriptService.transformToFrontendFormat(res.data)
await fetchScripts()
return { success: true, data: script || res.data }
} catch (error) {
return { success: false, error: error.message }
}
}
const updateScript = async (scriptData) => {
try {
const res = await epicScriptService.updateScript(scriptData)
const script = epicScriptService.transformToFrontendFormat(res.data)
await fetchScripts()
return { success: true, data: script || res.data }
} catch (error) {
return { success: false, error: error.message }
}
}
const deleteScript = async (id) => {
try {
await epicScriptService.deleteScript(id)
await fetchScripts()
return { success: true }
} catch (error) {
return { success: false, error: error.message }
}
}
const fetchInspirationRecommendations = async () => {
try {
const res = await epicScriptService.getInspirationRecommendations()
state.inspirationRecommendations = res.data || []
return state.inspirationRecommendations
} catch (error) {
state.inspirationRecommendations = []
return []
}
}
const fetchRandomInspirations = async (size = 3) => {
try {
const res = await epicScriptService.getRandomInspirations(size)
return res.data || []
} catch (error) {
return []
}
}
const generateScriptFromInspiration = async ({ prompt, style, length, useSocialInsights = true }) => {
try {
const profile = state.userProfile || state.registrationData
const res = await epicScriptService.generateFromInspiration({
prompt,
style,
length,
characterInfo: epicScriptService.buildCharacterInfo(profile),
lifeEventsSummary: epicScriptService.buildLifeEventsSummary(state.events, profile),
useSocialInsights,
source: 'mini-program'
})
await fetchScripts()
return { success: true, data: res.data }
} catch (error) {
return { success: false, error: error.message }
}
}
const getScriptById = (id) => {
return state.scripts.find(script => String(script.id) === String(id)) || null
}
const selectScript = async (id) => {
try {
await epicScriptService.selectScript(id)
await fetchScripts()
return { success: true }
} catch (error) {
return { success: false, error: error.message }
}
}
const fetchPaths = async () => {
try {
const res = await lifePathService.getPathList()
state.paths = lifePathService.transformListToFrontend(res.data || [])
return state.paths
} catch (error) {
return []
}
}
const setCurrentPath = (path) => {
state.currentPath = path
}
const restoreSession = async () => {
const token = uni.getStorageSync('access_token')
const refreshToken = uni.getStorageSync('refresh_token')
logAuth('restore:start', {
hasAccessToken: Boolean(token),
hasRefreshToken: Boolean(refreshToken)
})
if (!token) {
clearStoredTokens()
resetAuthState()
logAuth('restore:unauthenticated', { reason: 'missing_access_token' })
return { status: SESSION_STATUS.UNAUTHENTICATED, reason: 'missing_access_token' }
}
try {
const isValid = await authService.validateTokenStrict()
if (!isValid) {
throw new Error('Token validation returned false')
}
logAuth('validate:success')
state.isLoggedIn = true
await fetchUserProfile({ throwOnError: true })
return {
status: SESSION_STATUS.AUTHENTICATED,
hasProfile: hasProfile.value,
reason: 'access_token_valid'
}
} catch (validateError) {
logAuth('validate:fail', {
message: validateError.message,
statusCode: validateError.statusCode,
code: validateError.code,
isNetworkError: Boolean(validateError.isNetworkError)
})
if (validateError.isNetworkError) {
resetAuthState()
return {
status: SESSION_STATUS.ERROR,
reason: 'validate_network_error',
error: validateError
}
}
}
if (!refreshToken) {
clearStoredTokens()
resetAuthState()
logAuth('refresh:skip', { reason: 'missing_refresh_token' })
return { status: SESSION_STATUS.UNAUTHENTICATED, reason: 'missing_refresh_token' }
}
try {
logAuth('refresh:start')
await authService.refreshToken()
logAuth('refresh:success')
state.isLoggedIn = true
await fetchUserProfile({ throwOnError: true })
return {
status: SESSION_STATUS.AUTHENTICATED,
hasProfile: hasProfile.value,
reason: 'refresh_token_valid'
}
} catch (refreshError) {
logAuth('refresh:fail', {
message: refreshError.message,
statusCode: refreshError.statusCode,
code: refreshError.code,
isNetworkError: Boolean(refreshError.isNetworkError)
})
if (refreshError.isNetworkError) {
resetAuthState()
return {
status: SESSION_STATUS.ERROR,
reason: 'refresh_network_error',
error: refreshError
}
}
clearStoredTokens()
resetAuthState()
return {
status: SESSION_STATUS.UNAUTHENTICATED,
reason: 'refresh_failed',
error: refreshError
}
}
}
const initialize = async () => {
return restoreSession()
}
export const useAppStore = () => {
return readonly({
get isLoggedIn() { return state.isLoggedIn },
get isLoading() { return state.isLoading },
get currentStep() { return state.currentStep },
get userInfo() { return state.userInfo },
get userProfile() { return state.userProfile },
get events() { return state.events },
get scripts() { return state.scripts },
get inspirationRecommendations() { return state.inspirationRecommendations },
get paths() { return state.paths },
get currentPath() { return state.currentPath },
get registrationData() { return state.registrationData },
get hasProfile() { return hasProfile.value },
login,
loginWithWechat,
logout,
fetchUserProfile,
saveUserProfile,
updateRegistration,
setCurrentStep,
fetchEvents,
createEvent,
updateEvent,
deleteEvent,
assistEventWriting,
chatAboutEvent,
shareEvent,
favoriteEvent,
getEventById,
fetchScripts,
createScript,
updateScript,
deleteScript,
fetchInspirationRecommendations,
fetchRandomInspirations,
generateScriptFromInspiration,
getScriptById,
selectScript,
fetchPaths,
setCurrentPath,
initialize,
restoreSession,
SESSION_STATUS
})
}
export default useAppStore