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:
2026-05-10 11:38:35 +08:00
parent 507d1ebdab
commit 60c63850ee
36 changed files with 4545 additions and 3043 deletions
+186 -9
View File
@@ -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
})
}