feat: 修复 Redis 超时问题、固定小程序端口、新增人生事件模块及优化多个页面
- 修复 Redis 超时:添加 commons-pool2 依赖,启用 Lettuce 连接池,超时提升至 15s - 固定 mini-program H5 端口为 5175,避免与 web 项目端口冲突 - 新增人生事件(life-event)模块:表单和详情页面 - 新增 EpicScript 灵感接口(Controller/Service/DTO) - 优化登录、引导、主页、记录、剧本详情等多个页面 - 优化服务管理脚本和 Nginx 配置 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ const state = reactive({
|
||||
userProfile: null,
|
||||
events: [],
|
||||
scripts: [],
|
||||
inspirationRecommendations: [],
|
||||
paths: [],
|
||||
currentPath: null,
|
||||
registrationData: {
|
||||
@@ -29,10 +30,31 @@ const state = reactive({
|
||||
}
|
||||
})
|
||||
|
||||
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 clearStoredTokens = () => {
|
||||
uni.removeStorageSync('access_token')
|
||||
uni.removeStorageSync('refresh_token')
|
||||
}
|
||||
|
||||
const login = async (phone, smsCode) => {
|
||||
state.isLoading = true
|
||||
try {
|
||||
@@ -49,20 +71,33 @@ const login = async (phone, smsCode) => {
|
||||
|
||||
const logout = async () => {
|
||||
await authService.logout()
|
||||
state.isLoggedIn = false
|
||||
state.userInfo = null
|
||||
state.userProfile = null
|
||||
resetAuthState()
|
||||
}
|
||||
|
||||
const fetchUserProfile = async () => {
|
||||
const fetchUserProfile = async (options = {}) => {
|
||||
const { throwOnError = false } = options
|
||||
try {
|
||||
const res = await userProfileService.getCurrentProfile()
|
||||
if (res.data) {
|
||||
state.userProfile = userProfileService.transformToFrontendFormat(res.data)
|
||||
Object.assign(state.registrationData, state.userProfile)
|
||||
logAuth('profile:success', { hasProfile: true })
|
||||
} else {
|
||||
state.userProfile = null
|
||||
logAuth('profile:empty', { hasProfile: false })
|
||||
}
|
||||
return res.data
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@@ -116,6 +151,10 @@ const createEvent = async (eventData) => {
|
||||
}
|
||||
}
|
||||
|
||||
const getEventById = (id) => {
|
||||
return state.events.find(event => String(event.id) === String(id)) || null
|
||||
}
|
||||
|
||||
const fetchScripts = async () => {
|
||||
try {
|
||||
const res = await epicScriptService.getScriptList()
|
||||
@@ -136,6 +175,48 @@ const createScript = async (scriptData) => {
|
||||
}
|
||||
}
|
||||
|
||||
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 }) => {
|
||||
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),
|
||||
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)
|
||||
@@ -160,12 +241,101 @@ const setCurrentPath = (path) => {
|
||||
state.currentPath = path
|
||||
}
|
||||
|
||||
const initialize = async () => {
|
||||
const restoreSession = async () => {
|
||||
const token = uni.getStorageSync('access_token')
|
||||
if (token) {
|
||||
state.isLoggedIn = true
|
||||
await fetchUserProfile()
|
||||
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 = () => {
|
||||
@@ -180,12 +350,19 @@ export const useAppStore = () => {
|
||||
setCurrentStep,
|
||||
fetchEvents,
|
||||
createEvent,
|
||||
getEventById,
|
||||
fetchScripts,
|
||||
createScript,
|
||||
fetchInspirationRecommendations,
|
||||
fetchRandomInspirations,
|
||||
generateScriptFromInspiration,
|
||||
getScriptById,
|
||||
selectScript,
|
||||
fetchPaths,
|
||||
setCurrentPath,
|
||||
initialize
|
||||
initialize,
|
||||
restoreSession,
|
||||
SESSION_STATUS
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user