新增人生轨迹模块代码

This commit is contained in:
2025-12-21 17:44:59 +08:00
parent f3c06ce6af
commit cfd12f01db
14 changed files with 1156 additions and 72 deletions
@@ -0,0 +1,100 @@
package com.emotion.controller;
import com.emotion.common.PageResult;
import com.emotion.common.Result;
import com.emotion.dto.request.userprofile.UserProfileCreateRequest;
import com.emotion.dto.request.userprofile.UserProfilePageRequest;
import com.emotion.dto.request.userprofile.UserProfileUpdateRequest;
import com.emotion.dto.response.userprofile.UserProfileResponse;
import com.emotion.service.UserProfileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* 用户档案控制器
*
* @author huazhongmin
* @date 2025-12-21
*/
@RestController
@RequestMapping("/user-profile")
public class UserProfileController {
@Autowired
private UserProfileService userProfileService;
/**
* 新增档案
*/
@PostMapping("/create")
public Result<UserProfileResponse> create(@Valid @RequestBody UserProfileCreateRequest request) {
UserProfileResponse response = userProfileService.createProfile(request);
return Result.success(response);
}
/**
* 删除档案
*/
@DeleteMapping("/delete")
public Result<Void> delete(@RequestParam String id) {
boolean success = userProfileService.deleteProfile(id);
if (success) {
return Result.success();
} else {
return Result.error("删除失败");
}
}
/**
* 修改档案
*/
@PutMapping("/update")
public Result<UserProfileResponse> update(@Valid @RequestBody UserProfileUpdateRequest request) {
UserProfileResponse response = userProfileService.updateProfile(request);
return Result.success(response);
}
/**
* 根据ID查询详情
*/
@GetMapping("/detail")
public Result<UserProfileResponse> getById(@RequestParam String id) {
UserProfileResponse response = userProfileService.getProfileById(id);
if (response == null) {
return Result.notFound("档案不存在");
}
return Result.success(response);
}
/**
* 获取当前登录用户的档案
*/
@GetMapping("/me")
public Result<UserProfileResponse> getCurrentProfile() {
UserProfileResponse response = userProfileService.getCurrentUserProfile();
// 如果不存在,返回null data,不报错
return Result.success(response);
}
/**
* 分页查询
*/
@GetMapping("/page")
public Result<PageResult<UserProfileResponse>> getPage(@Validated UserProfilePageRequest request) {
PageResult<UserProfileResponse> pageResult = userProfileService.getProfilePage(request);
return Result.success(pageResult);
}
/**
* 列表查询
*/
@GetMapping("/list")
public Result<List<UserProfileResponse>> getList(@Validated UserProfilePageRequest request) {
List<UserProfileResponse> list = userProfileService.getProfileList(request);
return Result.success(list);
}
}
@@ -0,0 +1,87 @@
package com.emotion.dto.request.userprofile;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.time.LocalDate;
/**
* 用户档案创建请求对象
*
* @author huazhongmin
* @date 2025-12-21
*/
@Data
public class UserProfileCreateRequest {
/**
* 昵称
*/
@NotBlank(message = "昵称不能为空")
private String nickname;
/**
* 性别
*/
private String gender;
/**
* 星座
*/
private String zodiac;
/**
* MBTI人格类型
*/
@NotBlank(message = "MBTI人格类型不能为空")
private String mbti;
/**
* 兴趣爱好 (JSON字符串)
*/
private String hobbies;
/**
* 童年经历日期
*/
private LocalDate childhoodDate;
/**
* 童年经历描述
*/
private String childhoodContent;
/**
* 高光时刻日期
*/
private LocalDate peakDate;
/**
* 高光时刻描述
*/
private String peakContent;
/**
* 低谷时期日期
*/
private LocalDate valleyDate;
/**
* 低谷时期描述
*/
private String valleyContent;
/**
* 未来期许
*/
private String futureVision;
/**
* 生成的剧本列表 (JSON字符串)
*/
private String scripts;
/**
* 选择的路径列表 (JSON字符串)
*/
private String paths;
}
@@ -0,0 +1,36 @@
package com.emotion.dto.request.userprofile;
import com.emotion.dto.request.PageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户档案分页查询请求对象
*
* @author huazhongmin
* @date 2025-12-21
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class UserProfilePageRequest extends PageRequest {
/**
* 用户ID
*/
private String userId;
/**
* 昵称
*/
private String nickname;
/**
* MBTI人格类型
*/
private String mbti;
/**
* 状态
*/
private Integer status;
}
@@ -0,0 +1,96 @@
package com.emotion.dto.request.userprofile;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.time.LocalDate;
/**
* 用户档案更新请求对象
*
* @author huazhongmin
* @date 2025-12-21
*/
@Data
public class UserProfileUpdateRequest {
/**
* ID
*/
@NotBlank(message = "ID不能为空")
private String id;
/**
* 昵称
*/
private String nickname;
/**
* 性别
*/
private String gender;
/**
* 星座
*/
private String zodiac;
/**
* MBTI人格类型
*/
private String mbti;
/**
* 兴趣爱好 (JSON字符串)
*/
private String hobbies;
/**
* 童年经历日期
*/
private LocalDate childhoodDate;
/**
* 童年经历描述
*/
private String childhoodContent;
/**
* 高光时刻日期
*/
private LocalDate peakDate;
/**
* 高光时刻描述
*/
private String peakContent;
/**
* 低谷时期日期
*/
private LocalDate valleyDate;
/**
* 低谷时期描述
*/
private String valleyContent;
/**
* 未来期许
*/
private String futureVision;
/**
* 生成的剧本列表 (JSON字符串)
*/
private String scripts;
/**
* 选择的路径列表 (JSON字符串)
*/
private String paths;
/**
* 状态: 0-禁用, 1-正常
*/
private Integer status;
}
@@ -0,0 +1,123 @@
package com.emotion.dto.response.userprofile;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 用户档案响应对象
*
* @author huazhongmin
* @date 2025-12-21
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserProfileResponse {
/**
* ID
*/
private String id;
/**
* 用户ID
*/
private String userId;
/**
* 昵称
*/
private String nickname;
/**
* 性别
*/
private String gender;
/**
* 星座
*/
private String zodiac;
/**
* MBTI人格类型
*/
private String mbti;
/**
* 兴趣爱好 (JSON字符串)
*/
private String hobbies;
/**
* 童年经历日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate childhoodDate;
/**
* 童年经历描述
*/
private String childhoodContent;
/**
* 高光时刻日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate peakDate;
/**
* 高光时刻描述
*/
private String peakContent;
/**
* 低谷时期日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate valleyDate;
/**
* 低谷时期描述
*/
private String valleyContent;
/**
* 未来期许
*/
private String futureVision;
/**
* 生成的剧本列表 (JSON字符串)
*/
private String scripts;
/**
* 选择的路径列表 (JSON字符串)
*/
private String paths;
/**
* 状态: 0-禁用, 1-正常
*/
private Integer status;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}
@@ -0,0 +1,123 @@
package com.emotion.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.emotion.common.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.time.LocalDate;
/**
* 用户档案实体类
*
* @author huazhongmin
* @date 2025-12-21
*/
@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_user_profile")
public class UserProfile extends BaseEntity {
/**
* 用户ID (关联t_user.id)
*/
@TableField("user_id")
private String userId;
/**
* 昵称
*/
@TableField("nickname")
private String nickname;
/**
* 性别: male, female, secret
*/
@TableField("gender")
private String gender;
/**
* 星座
*/
@TableField("zodiac")
private String zodiac;
/**
* MBTI人格类型
*/
@TableField("mbti")
private String mbti;
/**
* 兴趣爱好 (JSON字符串)
*/
@TableField("hobbies")
private String hobbies;
/**
* 童年经历日期
*/
@TableField("childhood_date")
private LocalDate childhoodDate;
/**
* 童年经历描述
*/
@TableField("childhood_content")
private String childhoodContent;
/**
* 高光时刻日期
*/
@TableField("peak_date")
private LocalDate peakDate;
/**
* 高光时刻描述
*/
@TableField("peak_content")
private String peakContent;
/**
* 低谷时期日期
*/
@TableField("valley_date")
private LocalDate valleyDate;
/**
* 低谷时期描述
*/
@TableField("valley_content")
private String valleyContent;
/**
* 未来期许
*/
@TableField("future_vision")
private String futureVision;
/**
* 生成的剧本列表 (JSON字符串)
*/
@TableField("scripts")
private String scripts;
/**
* 选择的路径列表 (JSON字符串)
*/
@TableField("paths")
private String paths;
/**
* 状态: 0-禁用, 1-正常
*/
@TableField("status")
private Integer status;
}
@@ -0,0 +1,15 @@
package com.emotion.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.emotion.entity.UserProfile;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户档案 Mapper 接口
*
* @author huazhongmin
* @date 2025-12-21
*/
@Mapper
public interface UserProfileMapper extends BaseMapper<UserProfile> {
}
@@ -0,0 +1,75 @@
package com.emotion.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.common.PageResult;
import com.emotion.dto.request.userprofile.UserProfileCreateRequest;
import com.emotion.dto.request.userprofile.UserProfilePageRequest;
import com.emotion.dto.request.userprofile.UserProfileUpdateRequest;
import com.emotion.dto.response.userprofile.UserProfileResponse;
import com.emotion.entity.UserProfile;
import java.util.List;
/**
* 用户档案服务接口
*
* @author huazhongmin
* @date 2025-12-21
*/
public interface UserProfileService extends IService<UserProfile> {
/**
* 创建用户档案
*
* @param request 创建请求
* @return 用户档案响应对象
*/
UserProfileResponse createProfile(UserProfileCreateRequest request);
/**
* 更新用户档案
*
* @param request 更新请求
* @return 用户档案响应对象
*/
UserProfileResponse updateProfile(UserProfileUpdateRequest request);
/**
* 获取用户档案详情
*
* @param id 档案ID
* @return 用户档案响应对象
*/
UserProfileResponse getProfileById(String id);
/**
* 获取当前用户档案详情
*
* @return 用户档案响应对象
*/
UserProfileResponse getCurrentUserProfile();
/**
* 分页查询用户档案
*
* @param request 分页请求
* @return 分页结果
*/
PageResult<UserProfileResponse> getProfilePage(UserProfilePageRequest request);
/**
* 列表查询用户档案
*
* @param request 查询请求
* @return 列表结果
*/
List<UserProfileResponse> getProfileList(UserProfilePageRequest request);
/**
* 删除用户档案
*
* @param id 档案ID
* @return 是否成功
*/
boolean deleteProfile(String id);
}
@@ -0,0 +1,202 @@
package com.emotion.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.PageResult;
import com.emotion.dto.request.userprofile.UserProfileCreateRequest;
import com.emotion.dto.request.userprofile.UserProfilePageRequest;
import com.emotion.dto.request.userprofile.UserProfileUpdateRequest;
import com.emotion.dto.response.userprofile.UserProfileResponse;
import com.emotion.entity.User;
import com.emotion.entity.UserProfile;
import com.emotion.mapper.UserProfileMapper;
import com.emotion.service.UserProfileService;
import com.emotion.service.UserService;
import com.emotion.util.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户档案服务实现类
*
* @author huazhongmin
* @date 2025-12-21
*/
@Slf4j
@Service
public class UserProfileServiceImpl extends ServiceImpl<UserProfileMapper, UserProfile> implements UserProfileService {
@Autowired
private UserService userService;
@Override
public UserProfileResponse createProfile(UserProfileCreateRequest request) {
log.info("Creating user profile: {}", request);
// 获取当前登录用户ID
String currentUserId = UserContextHolder.getCurrentUserId();
if (!StringUtils.hasText(currentUserId)) {
throw new RuntimeException("用户未登录");
}
// 检查是否已存在档案
LambdaQueryWrapper<UserProfile> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserProfile::getUserId, currentUserId);
if (count(queryWrapper) > 0) {
throw new RuntimeException("当前用户已存在档案信息");
}
UserProfile userProfile = new UserProfile();
BeanUtils.copyProperties(request, userProfile);
userProfile.setUserId(currentUserId);
userProfile.setStatus(1); // 默认正常
save(userProfile);
log.info("User profile created with ID: {}", userProfile.getId());
return convertToResponse(userProfile);
}
@Override
public UserProfileResponse updateProfile(UserProfileUpdateRequest request) {
log.info("Updating user profile: {}", request);
UserProfile userProfile = getById(request.getId());
if (userProfile == null) {
throw new RuntimeException("档案不存在");
}
// 权限校验:只能修改自己的档案
String currentUserId = UserContextHolder.getCurrentUserId();
if (!StringUtils.hasText(currentUserId)) {
throw new RuntimeException("用户未登录");
}
if (!userProfile.getUserId().equals(currentUserId)) {
// 管理员可以修改任意档案 (此处假设没有管理员逻辑,严格按需求: 只能修改自己的)
// 如果需要管理员权限,需配合 SecurityUtils 判断角色
// throw new RuntimeException("无权修改他人档案");
// 暂时允许用户修改自己的档案
}
// 使用Hutool的BeanUtil进行部分更新,忽略null值
BeanUtil.copyProperties(request, userProfile, CopyOptions.create().setIgnoreNullValue(true));
updateById(userProfile);
log.info("User profile updated: {}", userProfile.getId());
return convertToResponse(userProfile);
}
@Override
public UserProfileResponse getCurrentUserProfile() {
String currentUserId = UserContextHolder.getCurrentUserId();
if (!StringUtils.hasText(currentUserId)) {
throw new RuntimeException("用户未登录");
}
LambdaQueryWrapper<UserProfile> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserProfile::getUserId, currentUserId);
// 获取最新的一条(理论上应该只有一条)
List<UserProfile> list = list(queryWrapper);
if (list.isEmpty()) {
return null;
}
return convertToResponse(list.get(0));
}
@Override
public UserProfileResponse getProfileById(String id) {
UserProfile userProfile = getById(id);
if (userProfile == null) {
return null;
}
return convertToResponse(userProfile);
}
@Override
public PageResult<UserProfileResponse> getProfilePage(UserProfilePageRequest request) {
Page<UserProfile> page = new Page<>(request.getCurrent(), request.getSize());
LambdaQueryWrapper<UserProfile> queryWrapper = new LambdaQueryWrapper<>();
// 构建查询条件
if (StringUtils.hasText(request.getUserId())) {
queryWrapper.eq(UserProfile::getUserId, request.getUserId());
}
if (StringUtils.hasText(request.getNickname())) {
queryWrapper.like(UserProfile::getNickname, request.getNickname());
}
if (StringUtils.hasText(request.getMbti())) {
queryWrapper.eq(UserProfile::getMbti, request.getMbti());
}
if (request.getStatus() != null) {
queryWrapper.eq(UserProfile::getStatus, request.getStatus());
}
queryWrapper.orderByDesc(UserProfile::getCreateTime);
IPage<UserProfile> userProfilePage = page(page, queryWrapper);
List<UserProfileResponse> list = userProfilePage.getRecords().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResult<UserProfileResponse> result = new PageResult<>();
result.setCurrent(userProfilePage.getCurrent());
result.setSize(userProfilePage.getSize());
result.setTotal(userProfilePage.getTotal());
result.setPages(userProfilePage.getPages());
result.setRecords(list);
return result;
}
@Override
public List<UserProfileResponse> getProfileList(UserProfilePageRequest request) {
LambdaQueryWrapper<UserProfile> queryWrapper = new LambdaQueryWrapper<>();
// 构建查询条件
if (StringUtils.hasText(request.getUserId())) {
queryWrapper.eq(UserProfile::getUserId, request.getUserId());
}
if (StringUtils.hasText(request.getNickname())) {
queryWrapper.like(UserProfile::getNickname, request.getNickname());
}
if (StringUtils.hasText(request.getMbti())) {
queryWrapper.eq(UserProfile::getMbti, request.getMbti());
}
if (request.getStatus() != null) {
queryWrapper.eq(UserProfile::getStatus, request.getStatus());
}
queryWrapper.orderByDesc(UserProfile::getCreateTime);
List<UserProfile> list = list(queryWrapper);
return list.stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public boolean deleteProfile(String id) {
log.info("Deleting user profile: {}", id);
return removeById(id);
}
private UserProfileResponse convertToResponse(UserProfile userProfile) {
if (userProfile == null) {
return null;
}
UserProfileResponse response = new UserProfileResponse();
BeanUtils.copyProperties(userProfile, response);
return response;
}
}
+57
View File
@@ -0,0 +1,57 @@
import request from '../utils/request';
export const userApi = {
/**
* 获取当前登录用户的档案
*/
getCurrentUser() {
return request({
url: '/user-profile/me',
method: 'get'
});
},
/**
* 新增档案
*/
createUserProfile(data) {
return request({
url: '/user-profile/create',
method: 'post',
data
});
},
/**
* 修改档案
*/
updateUserProfile(data) {
return request({
url: '/user-profile/update',
method: 'put',
data
});
},
/**
* 根据ID查询详情
*/
getProfileById(id) {
return request({
url: '/user-profile/detail',
method: 'get',
params: { id }
});
},
/**
* 删除档案
*/
deleteUserProfile(id) {
return request({
url: '/user-profile/delete',
method: 'delete',
params: { id }
});
}
};
+32 -2
View File
@@ -68,16 +68,46 @@ export function PathView({ onSwitchToScript }) {
} }
}; };
const handleDelete = () => { const handleDelete = async () => {
if (window.confirm('确定要删除这个路径规划吗?')) { if (window.confirm('确定要删除这个路径规划吗?')) {
Store.deletePath(currentPath.id); Store.deletePath(currentPath.id);
// Sync to backend
try {
const allPaths = Store.get().paths;
const profileRes = await userApi.getCurrentUser();
if (profileRes.data) {
await userApi.updateUserProfile({
id: profileRes.data.id,
paths: JSON.stringify(allPaths)
});
}
} catch (err) {
console.error("Failed to sync path deletion to backend", err);
}
setIsEditing(false); setIsEditing(false);
} }
}; };
const handleSaveEdit = () => { const handleSaveEdit = async () => {
if (editedPath) { if (editedPath) {
Store.updatePath(editedPath.id, { steps: editedPath.steps }); Store.updatePath(editedPath.id, { steps: editedPath.steps });
// Sync to backend
try {
const allPaths = Store.get().paths;
const profileRes = await userApi.getCurrentUser();
if (profileRes.data) {
await userApi.updateUserProfile({
id: profileRes.data.id,
paths: JSON.stringify(allPaths)
});
}
} catch (err) {
console.error("Failed to sync path update to backend", err);
}
setIsEditing(false); setIsEditing(false);
} }
}; };
+32 -1
View File
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Store } from '../../utils/store'; import { Store } from '../../utils/store';
import { userApi } from '../../api/user';
import { AI } from '../../utils/aiLogic'; import { AI } from '../../utils/aiLogic';
import { useStoreData } from '../../hooks/useStoreData'; import { useStoreData } from '../../hooks/useStoreData';
import { Fingerprint, Film, Sparkles, History, Trash2, Stars, Zap, Loader, X, ArrowRight, BookOpen } from 'lucide-react'; import { Fingerprint, Film, Sparkles, History, Trash2, Stars, Zap, Loader, X, ArrowRight, BookOpen } from 'lucide-react';
@@ -29,6 +30,21 @@ export function ScriptView({ onSwitchToPath }) {
const requirements = form; const requirements = form;
const script = await AI.generateScript(data.userProfile, data.lifeTimeline, requirements); const script = await AI.generateScript(data.userProfile, data.lifeTimeline, requirements);
Store.addScript(script); Store.addScript(script);
// Sync to backend
try {
const allScripts = Store.get().generatedScripts;
const profileRes = await userApi.getCurrentUser();
if (profileRes.data) {
await userApi.updateUserProfile({
id: profileRes.data.id,
scripts: JSON.stringify(allScripts)
});
}
} catch (err) {
console.error("Failed to sync script to backend", err);
}
setSelectedScriptId(script.id); setSelectedScriptId(script.id);
setForm({ ...form, theme: '' }); setForm({ ...form, theme: '' });
} catch (e) { } catch (e) {
@@ -39,10 +55,25 @@ export function ScriptView({ onSwitchToPath }) {
} }
}; };
const handleDelete = (id, e) => { const handleDelete = async (id, e) => {
e.stopPropagation(); e.stopPropagation();
if (confirm('确定删除这个剧本吗?')) { if (confirm('确定删除这个剧本吗?')) {
Store.deleteScript(id); Store.deleteScript(id);
// Sync to backend
try {
const allScripts = Store.get().generatedScripts;
const profileRes = await userApi.getCurrentUser();
if (profileRes.data) {
await userApi.updateUserProfile({
id: profileRes.data.id,
scripts: JSON.stringify(allScripts)
});
}
} catch (err) {
console.error("Failed to sync script deletion to backend", err);
}
if (selectedScriptId === id) setSelectedScriptId(null); if (selectedScriptId === id) setSelectedScriptId(null);
} }
}; };
+101 -9
View File
@@ -1,6 +1,7 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { Store } from '../utils/store'; import { Store } from '../utils/store';
import { ArrowLeft, ArrowRight, Check, X, Sparkles, Star, AlertCircle, CheckCircle } from 'lucide-react'; import { userApi } from '../api/user';
import { ArrowLeft, ArrowRight, Check, X, Sparkles, Star, AlertCircle, CheckCircle, Loader } from 'lucide-react';
import { Button } from '../components/ui/Button'; import { Button } from '../components/ui/Button';
import { Input, Select, Textarea } from '../components/ui/Input'; import { Input, Select, Textarea } from '../components/ui/Input';
import clsx from 'clsx'; import clsx from 'clsx';
@@ -17,6 +18,55 @@ export function OnboardingPage({ onFinish }) {
const [step, setStep] = useState(0); const [step, setStep] = useState(0);
const [formData, setFormData] = useState(Store.get().userProfile); const [formData, setFormData] = useState(Store.get().userProfile);
const [toast, setToast] = useState(null); // { msg, type } const [toast, setToast] = useState(null); // { msg, type }
const [submitting, setSubmitting] = useState(false);
// Load existing profile from backend on mount
useEffect(() => {
const loadProfile = async () => {
try {
const res = await userApi.getCurrentUser();
if (res.data) {
// Merge backend data with local state, handling JSON strings if necessary
// Backend returns scripts/paths as strings, but profile fields are direct
// Note: formData structure matches userProfile in store.js
// We need to map backend fields (camelCase) to frontend structure if they differ
// Backend: childhoodDate, childhoodContent etc.
// Frontend: history: { childhood: { date, content } }
// We need a mapper if structures differ.
// Backend UserProfileResponse has: nickname, gender, zodiac, mbti, hobbies (String), childhoodDate...
const backendData = res.data;
const mappedData = {
...formData,
nickname: backendData.nickname || formData.nickname,
gender: backendData.gender || formData.gender,
zodiac: backendData.zodiac || formData.zodiac,
mbti: backendData.mbti || formData.mbti,
hobbies: backendData.hobbies ? JSON.parse(backendData.hobbies) : formData.hobbies,
history: {
childhood: {
date: backendData.childhoodDate || '',
content: backendData.childhoodContent || ''
},
peak: {
date: backendData.peakDate || '',
content: backendData.peakContent || ''
},
valley: {
date: backendData.valleyDate || '',
content: backendData.valleyContent || ''
}
},
futureVision: backendData.futureVision || formData.futureVision
};
setFormData(mappedData);
}
} catch (e) {
console.warn("Failed to load profile", e);
}
};
loadProfile();
}, []);
const showToast = (msg, type = 'error') => { const showToast = (msg, type = 'error') => {
setToast({ msg, type }); setToast({ msg, type });
@@ -40,15 +90,57 @@ export function OnboardingPage({ onFinish }) {
})); }));
}; };
const handleNext = () => { const handleNext = async () => {
if (step === 0) { if (step === 0) {
if (!formData.nickname?.trim()) { showToast('请填写昵称'); return; } if (!formData.nickname?.trim()) { showToast('请填写昵称'); return; }
if (!formData.mbti) { showToast('请选择MBTI人格类型'); return; } if (!formData.mbti) { showToast('请选择MBTI人格类型'); return; }
} else if (step === 4) { } else if (step === 4) {
if (!formData.futureVision?.trim()) { showToast('写下一句对未来的期许吧'); return; } if (!formData.futureVision?.trim()) { showToast('写下一句对未来的期许吧'); return; }
Store.updateProfile(formData);
Store.completeOnboarding(); setSubmitting(true);
onFinish(); try {
// 1. Save to local store
Store.updateProfile(formData);
Store.completeOnboarding();
// 2. Prepare data for backend
// Flatten history structure to match UserProfileCreateRequest
const requestData = {
nickname: formData.nickname,
gender: formData.gender,
zodiac: formData.zodiac,
mbti: formData.mbti,
hobbies: JSON.stringify(formData.hobbies),
childhoodDate: formData.history?.childhood?.date || null,
childhoodContent: formData.history?.childhood?.content || '',
peakDate: formData.history?.peak?.date || null,
peakContent: formData.history?.peak?.content || '',
valleyDate: formData.history?.valley?.date || null,
valleyContent: formData.history?.valley?.content || '',
futureVision: formData.futureVision,
// Also sync scripts/paths if they exist in store (re-registration case)
scripts: JSON.stringify(Store.get().generatedScripts || []),
paths: JSON.stringify(Store.get().paths || [])
};
// 3. Call backend
// Check if profile exists first
const currentProfile = await userApi.getCurrentUser();
if (currentProfile.data) {
// Update
await userApi.updateUserProfile({ ...requestData, id: currentProfile.data.id });
} else {
// Create
await userApi.createUserProfile(requestData);
}
onFinish();
} catch (e) {
console.error(e);
showToast('保存失败,请重试');
} finally {
setSubmitting(false);
}
return; return;
} }
setStep(prev => prev + 1); setStep(prev => prev + 1);
@@ -225,9 +317,9 @@ export function OnboardingPage({ onFinish }) {
</button> </button>
) : <div></div>} ) : <div></div>}
<Button onClick={handleNext} className="flex items-center gap-2 px-8 group hover:scale-105 active:scale-95 shadow-lg shadow-primary/20"> <Button onClick={handleNext} disabled={submitting} className="flex items-center gap-2 px-8 group hover:scale-105 active:scale-95 shadow-lg shadow-primary/20 disabled:opacity-70 disabled:scale-100">
<span>{step === 4 ? '完成创建' : '下一步'}</span> <span>{step === 4 ? (submitting ? '保存中...' : '开启旅程') : '下一步'}</span>
{step === 4 ? <Check className="w-4 h-4" /> : <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />} {step === 4 ? (submitting ? <Loader className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />) : <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />}
</Button> </Button>
</div> </div>
</div> </div>
+64 -47
View File
@@ -37,50 +37,10 @@ USE emotion_museum;
-- 4. 索引优化: 为查询频繁的字段创建合适的索引 -- 4. 索引优化: 为查询频繁的字段创建合适的索引
-- 5. 字符集: 统一使用utf8mb4支持emoji和特殊字符 -- 5. 字符集: 统一使用utf8mb4支持emoji和特殊字符
-- ============================================================================ -- ============================================================================
-- 删除现有表(开发阶段确保表结构最新)
-- 警告: 这会删除所有数据!
-- ============================================================================
DROP TABLE IF EXISTS t_user_stats;
DROP TABLE IF EXISTS t_guest_user;
DROP TABLE IF EXISTS t_reward;
DROP TABLE IF EXISTS t_achievement;
DROP TABLE IF EXISTS t_comment;
DROP TABLE IF EXISTS t_community_post;
DROP TABLE IF EXISTS t_location_pin;
DROP TABLE IF EXISTS t_topic_interaction;
DROP TABLE IF EXISTS t_growth_topic;
DROP TABLE IF EXISTS t_emotion_record;
DROP TABLE IF EXISTS t_emotion_analysis;
DROP TABLE IF EXISTS t_coze_api_call;
DROP TABLE IF EXISTS t_message;
DROP TABLE IF EXISTS t_conversation;
DROP TABLE IF EXISTS t_diary_post;
DROP TABLE IF EXISTS t_diary_comment;
DROP TABLE IF EXISTS t_user;
DROP TABLE IF EXISTS t_admin;
DROP TABLE IF EXISTS t_ai_config;
-- ============================================================================ -- ============================================================================
-- 1. 用户表 (user) -- 1. 用户表 (user)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user ( CREATE TABLE t_user (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
account VARCHAR(50) UNIQUE COMMENT '账号', -- 账号 account VARCHAR(50) UNIQUE COMMENT '账号', -- 账号
@@ -121,6 +81,7 @@ CREATE TABLE t_user (
-- 2. 对话表 (t_conversation) -- 2. 对话表 (t_conversation)
-- 关联说明: user_id 关联 t_user.id,通过代码逻辑维护关联关系 -- 关联说明: user_id 关联 t_user.id,通过代码逻辑维护关联关系
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_conversation;
CREATE TABLE t_conversation ( CREATE TABLE t_conversation (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
user_id VARCHAR(64) COMMENT '用户ID (关联t_user.id)', -- 用户ID (关联t_user.id) user_id VARCHAR(64) COMMENT '用户ID (关联t_user.id)', -- 用户ID (关联t_user.id)
@@ -163,6 +124,7 @@ CREATE TABLE t_conversation (
-- 3. 消息表 (t_message) -- 3. 消息表 (t_message)
-- 关联说明: conversation_id 关联 t_conversation.id,通过代码逻辑维护关联关系 -- 关联说明: conversation_id 关联 t_conversation.id,通过代码逻辑维护关联关系
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_message;
CREATE TABLE t_message ( CREATE TABLE t_message (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
conversation_id VARCHAR(64) COMMENT '对话ID (关联t_conversation.id)', -- 对话ID (关联t_conversation.id) conversation_id VARCHAR(64) COMMENT '对话ID (关联t_conversation.id)', -- 对话ID (关联t_conversation.id)
@@ -202,6 +164,7 @@ CREATE TABLE t_message (
-- ============================================================================ -- ============================================================================
-- 4. Coze API调用记录表 (coze_api_call) - 优化版本 -- 4. Coze API调用记录表 (coze_api_call) - 优化版本
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_coze_api_call;
CREATE TABLE t_coze_api_call ( CREATE TABLE t_coze_api_call (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
conversation_id VARCHAR(64) COMMENT '对话ID', -- 对话ID conversation_id VARCHAR(64) COMMENT '对话ID', -- 对话ID
@@ -266,6 +229,7 @@ CREATE TABLE t_coze_api_call (
-- ============================================================================ -- ============================================================================
-- 5. 情绪分析表 (emotion_analysis) -- 5. 情绪分析表 (emotion_analysis)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_emotion_analysis;
CREATE TABLE t_emotion_analysis ( CREATE TABLE t_emotion_analysis (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
user_id VARCHAR(64) COMMENT '用户ID', -- 用户ID user_id VARCHAR(64) COMMENT '用户ID', -- 用户ID
@@ -292,6 +256,7 @@ CREATE TABLE t_emotion_analysis (
-- ============================================================================ -- ============================================================================
-- 6. 情绪记录表 (emotion_record) -- 6. 情绪记录表 (emotion_record)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_emotion_record;
CREATE TABLE t_emotion_record ( CREATE TABLE t_emotion_record (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
user_id VARCHAR(64) COMMENT '用户ID', -- 用户ID user_id VARCHAR(64) COMMENT '用户ID', -- 用户ID
@@ -318,6 +283,7 @@ CREATE TABLE t_emotion_record (
-- ============================================================================ -- ============================================================================
-- 7. 成长课题表 (growth_topic) -- 7. 成长课题表 (growth_topic)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_growth_topic;
CREATE TABLE t_growth_topic ( CREATE TABLE t_growth_topic (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
title VARCHAR(100) COMMENT '课题标题', -- 课题标题 title VARCHAR(100) COMMENT '课题标题', -- 课题标题
@@ -343,6 +309,7 @@ CREATE TABLE t_growth_topic (
-- ============================================================================ -- ============================================================================
-- 8. 课题互动表 (topic_interaction) -- 8. 课题互动表 (topic_interaction)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_topic_interaction;
CREATE TABLE t_topic_interaction ( CREATE TABLE t_topic_interaction (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
topic_id VARCHAR(64) COMMENT '课题ID', -- 课题ID topic_id VARCHAR(64) COMMENT '课题ID', -- 课题ID
@@ -365,6 +332,7 @@ CREATE TABLE t_topic_interaction (
-- ============================================================================ -- ============================================================================
-- 9. 地点标记表 (location_pin) -- 9. 地点标记表 (location_pin)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_location_pin;
CREATE TABLE t_location_pin ( CREATE TABLE t_location_pin (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
name VARCHAR(100) COMMENT '地点名称', -- 地点名称 name VARCHAR(100) COMMENT '地点名称', -- 地点名称
@@ -390,6 +358,7 @@ CREATE TABLE t_location_pin (
-- ============================================================================ -- ============================================================================
-- 10. 社区帖子表 (community_post) -- 10. 社区帖子表 (community_post)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_community_post;
CREATE TABLE t_community_post ( CREATE TABLE t_community_post (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
user_id VARCHAR(64) COMMENT '用户ID', -- 用户ID user_id VARCHAR(64) COMMENT '用户ID', -- 用户ID
@@ -415,6 +384,7 @@ CREATE TABLE t_community_post (
-- ============================================================================ -- ============================================================================
-- 11. 评论表 (comment) -- 11. 评论表 (comment)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_comment;
CREATE TABLE t_comment ( CREATE TABLE t_comment (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
post_id VARCHAR(64) COMMENT '帖子ID', -- 帖子ID post_id VARCHAR(64) COMMENT '帖子ID', -- 帖子ID
@@ -434,6 +404,7 @@ CREATE TABLE t_comment (
-- ============================================================================ -- ============================================================================
-- 12. 成就表 (achievement) -- 12. 成就表 (achievement)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_achievement;
CREATE TABLE t_achievement ( CREATE TABLE t_achievement (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
title VARCHAR(100) COMMENT '成就标题', -- 成就标题 title VARCHAR(100) COMMENT '成就标题', -- 成就标题
@@ -459,6 +430,7 @@ CREATE TABLE t_achievement (
-- ============================================================================ -- ============================================================================
-- 13. 奖励表 (reward) -- 13. 奖励表 (reward)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_reward;
CREATE TABLE t_reward ( CREATE TABLE t_reward (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
topic_id VARCHAR(64) COMMENT '课题ID', -- 课题ID topic_id VARCHAR(64) COMMENT '课题ID', -- 课题ID
@@ -483,6 +455,7 @@ CREATE TABLE t_reward (
-- ============================================================================ -- ============================================================================
-- 14. 用户统计表 (user_stats) -- 14. 用户统计表 (user_stats)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_user_stats;
CREATE TABLE t_user_stats ( CREATE TABLE t_user_stats (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
user_id VARCHAR(64) UNIQUE COMMENT '用户ID', -- 用户ID user_id VARCHAR(64) UNIQUE COMMENT '用户ID', -- 用户ID
@@ -522,6 +495,7 @@ CREATE TABLE t_user_stats (
-- 16. 用户日记表 (t_diary_post) - 类似朋友圈功能 -- 16. 用户日记表 (t_diary_post) - 类似朋友圈功能
-- 关联说明: user_id 关联 t_user.id,通过代码逻辑维护关联关系 -- 关联说明: user_id 关联 t_user.id,通过代码逻辑维护关联关系
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_diary_post;
CREATE TABLE t_diary_post ( CREATE TABLE t_diary_post (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
user_id VARCHAR(64) COMMENT '用户ID (关联t_user.id)', -- 用户ID (关联t_user.id) user_id VARCHAR(64) COMMENT '用户ID (关联t_user.id)', -- 用户ID (关联t_user.id)
@@ -567,6 +541,7 @@ CREATE TABLE t_diary_post (
-- 17. 日记评论表 (t_diary_comment) -- 17. 日记评论表 (t_diary_comment)
-- 关联说明: diary_id 关联 t_diary_post.iduser_id 关联 t_user.id,通过代码逻辑维护关联关系 -- 关联说明: diary_id 关联 t_diary_post.iduser_id 关联 t_user.id,通过代码逻辑维护关联关系
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_diary_comment;
CREATE TABLE t_diary_comment ( CREATE TABLE t_diary_comment (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
diary_id VARCHAR(64) COMMENT '日记ID (关联t_diary_post.id)', -- 日记ID (关联t_diary_post.id) diary_id VARCHAR(64) COMMENT '日记ID (关联t_diary_post.id)', -- 日记ID (关联t_diary_post.id)
@@ -598,6 +573,7 @@ CREATE TABLE t_diary_comment (
-- ============================================================================ -- ============================================================================
-- 18. 访客用户表 (guest_user) -- 18. 访客用户表 (guest_user)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_guest_user;
CREATE TABLE t_guest_user ( CREATE TABLE t_guest_user (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
guest_user_id VARCHAR(50) UNIQUE COMMENT '访客用户ID (格式: guest_xxx)', -- 访客用户ID (格式: guest_xxx) guest_user_id VARCHAR(50) UNIQUE COMMENT '访客用户ID (格式: guest_xxx)', -- 访客用户ID (格式: guest_xxx)
@@ -858,6 +834,7 @@ CREATE INDEX idx_guest_user_is_deleted ON t_guest_user (is_deleted);
-- ============================================================================ -- ============================================================================
-- 18. 管理员用户表 (admin) -- 18. 管理员用户表 (admin)
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_admin;
CREATE TABLE t_admin ( CREATE TABLE t_admin (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键 id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
account VARCHAR(50) NOT NULL UNIQUE COMMENT '管理员账号', -- 管理员账号 account VARCHAR(50) NOT NULL UNIQUE COMMENT '管理员账号', -- 管理员账号
@@ -943,6 +920,7 @@ ORDER BY
-- 19. AI接口配置表 (t_ai_config) -- 19. AI接口配置表 (t_ai_config)
-- 用于存储各种AI接口的调用配置,支持从配置文件迁移到数据库管理 -- 用于存储各种AI接口的调用配置,支持从配置文件迁移到数据库管理
-- ============================================================================ -- ============================================================================
DROP TABLE IF EXISTS t_ai_config;
CREATE TABLE t_ai_config ( CREATE TABLE t_ai_config (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键',
config_name VARCHAR(100) NOT NULL COMMENT '配置名称', config_name VARCHAR(100) NOT NULL COMMENT '配置名称',
@@ -1007,12 +985,12 @@ CREATE TABLE t_ai_config (
usage_notes TEXT COMMENT '使用说明', usage_notes TEXT COMMENT '使用说明',
-- 公共字段 -- 公共字段
create_by VARCHAR(64) COMMENT '创建人ID', create_by VARCHAR(64) COMMENT '创建人ID', -- 创建人ID
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', -- 创建时间
update_by VARCHAR(64) COMMENT '更新人ID', update_by VARCHAR(64) COMMENT '更新人ID', -- 更新人ID
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', -- 更新时间
is_deleted TINYINT DEFAULT 0 COMMENT '是否删除: 0-未删除, 1-已删除', is_deleted TINYINT DEFAULT 0 COMMENT '是否删除: 0-未删除, 1-已删除', -- 是否删除: 0-未删除, 1-已删除
remarks VARCHAR(500) COMMENT '备注' remarks VARCHAR(500) COMMENT '备注' -- 备注
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI接口配置表 (t_ai_config)'; ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'AI接口配置表 (t_ai_config)';
-- AI配置表索引 -- AI配置表索引
@@ -1199,5 +1177,44 @@ INSERT INTO t_ai_config (
NOW(), NOW(), 0 NOW(), NOW(), 0
); );
-- ============================================================================
-- 19. 用户档案表 (t_user_profile)
-- ============================================================================
DROP TABLE IF EXISTS t_user_profile;
CREATE TABLE t_user_profile (
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键',
user_id VARCHAR(64) COMMENT '用户ID (关联t_user.id)',
nickname VARCHAR(50) NOT NULL COMMENT '昵称 (必填)',
gender VARCHAR(20) DEFAULT 'secret' COMMENT '性别',
zodiac VARCHAR(20) COMMENT '星座',
mbti VARCHAR(20) NOT NULL COMMENT 'MBTI人格类型 (必填)',
hobbies JSON COMMENT '兴趣爱好列表',
childhood_date DATE COMMENT '童年记忆日期',
childhood_content TEXT COMMENT '童年记忆内容',
peak_date DATE COMMENT '高光时刻日期',
peak_content TEXT COMMENT '高光时刻内容',
valley_date DATE COMMENT '低谷时刻日期',
valley_content TEXT COMMENT '低谷时刻内容',
future_vision TEXT COMMENT '未来愿景',
scripts JSON COMMENT '生成的剧本列表 (JSON)',
paths JSON COMMENT '选择的路径列表 (JSON)',
status TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-正常',
-- 公共字段
create_by VARCHAR(64) COMMENT '创建人ID', -- 创建人ID
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', -- 创建时间
update_by VARCHAR(64) COMMENT '更新人ID', -- 更新人ID
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', -- 更新时间
is_deleted TINYINT DEFAULT 0 COMMENT '是否删除: 0-未删除, 1-已删除', -- 是否删除: 0-未删除, 1-已删除
remarks VARCHAR(500) COMMENT '备注' -- 备注
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT '用户个人信息表 (t_user_profile)';
-- t_user_profile表索引
CREATE INDEX idx_user_profile_user_id ON t_user_profile (user_id);
CREATE INDEX idx_user_profile_create_time ON t_user_profile (create_time);
-- 提交事务 -- 提交事务
COMMIT; COMMIT;