18 KiB
小程序用户信息去写死 + 编辑资料保存生效 — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 去除小程序人生轨迹页和编辑资料页的所有硬编码用户信息,扩展后端 t_user_profile 表支持 city/industry/company/personalityTags/birthday 字段,确保编辑资料能完整保存和回显。
Architecture: 后端通过数据库 ALTER + Java 实体/DTO 扩展新增5个字段;前端同步扩展 Service 转换函数和 Store 数据结构,并移除两个页面中所有 || '硬编码默认值' 回退和示例事件数据。
Tech Stack: Java 17 / Spring Boot / MyBatis-Plus / MySQL;uni-app (Vue 3) / JavaScript
File Structure
| 文件 | 改动 | 职责 |
|---|---|---|
sql/emotion_museum_ddl.sql |
追加 | 数据库迁移:新增5列 |
server/.../entity/UserProfile.java |
修改 | ORM 实体新增字段 |
server/.../request/UserProfileCreateRequest.java |
修改 | 创建请求 DTO 新增字段 |
server/.../request/UserProfileUpdateRequest.java |
修改 | 更新请求 DTO 新增字段 |
server/.../response/UserProfileResponse.java |
修改 | 响应 DTO 新增字段 |
mini-program/src/services/userProfile.js |
修改 | 前后端数据格式双向转换 |
mini-program/src/stores/app.js |
修改 | 全局状态 registrationData 扩展 |
mini-program/src/pages/main/RecordView.vue |
修改 | 去除写死默认值和 sampleEvents |
mini-program/src/pages/onboarding/index.vue |
修改 | 去除硬编码默认值,空值友好展示 |
Task 1: 数据库迁移 — 新增5个字段
Files:
-
Modify:
sql/emotion_museum_ddl.sql -
Step 1: 追加 ALTER TABLE 语句
在 sql/emotion_museum_ddl.sql 文件末尾追加:
-- 用户档案扩展字段:城市、行业、公司、性格标签、生日
alter table emotion_museum.t_user_profile
add city varchar(100) null comment '所在城市',
add industry varchar(100) null comment '行业',
add company varchar(200) null comment '公司',
add personality_tags json null comment '性格标签列表',
add birthday date null comment '生日';
- Step 2: Commit
git add sql/emotion_museum_ddl.sql
git commit -m "db: add city, industry, company, personality_tags, birthday to t_user_profile"
Task 2: 后端实体扩展 — UserProfile.java
Files:
-
Modify:
server/src/main/java/com/emotion/entity/UserProfile.java -
Step 1: 在 idealLife 字段后新增5个字段
找到 idealLife 字段定义(约第115行),在其后、scripts 字段前插入:
/**
* 所在城市
*/
@TableField("city")
private String city;
/**
* 行业
*/
@TableField("industry")
private String industry;
/**
* 公司
*/
@TableField("company")
private String company;
/**
* 性格标签 (JSON字符串)
*/
@TableField("personality_tags")
private String personalityTags;
/**
* 生日
*/
@TableField("birthday")
private LocalDate birthday;
注意:LocalDate 的 import 已在文件顶部存在(第12行),无需新增 import。
- Step 2: Commit
git add server/src/main/java/com/emotion/entity/UserProfile.java
git commit -m "feat: extend UserProfile entity with city/industry/company/personalityTags/birthday"
Task 3: 后端创建请求 DTO 扩展 — UserProfileCreateRequest.java
Files:
-
Modify:
server/src/main/java/com/emotion/dto/request/userprofile/UserProfileCreateRequest.java -
Step 1: 在 idealLife 字段后新增5个字段
找到 idealLife 字段定义(约第86行),在其后、scripts 字段前插入:
/**
* 所在城市
*/
private String city;
/**
* 行业
*/
private String industry;
/**
* 公司
*/
private String company;
/**
* 性格标签 (JSON字符串)
*/
private String personalityTags;
/**
* 生日
*/
private LocalDate birthday;
- Step 2: Commit
git add server/src/main/java/com/emotion/dto/request/userprofile/UserProfileCreateRequest.java
git commit -m "feat: extend UserProfileCreateRequest with city/industry/company/personalityTags/birthday"
Task 4: 后端更新请求 DTO 扩展 — UserProfileUpdateRequest.java
Files:
-
Modify:
server/src/main/java/com/emotion/dto/request/userprofile/UserProfileUpdateRequest.java -
Step 1: 在 idealLife 字段后新增5个字段
找到 idealLife 字段定义(约第90行),在其后、scripts 字段前插入:
/**
* 所在城市
*/
private String city;
/**
* 行业
*/
private String industry;
/**
* 公司
*/
private String company;
/**
* 性格标签 (JSON字符串)
*/
private String personalityTags;
/**
* 生日
*/
private LocalDate birthday;
- Step 2: Commit
git add server/src/main/java/com/emotion/dto/request/userprofile/UserProfileUpdateRequest.java
git commit -m "feat: extend UserProfileUpdateRequest with city/industry/company/personalityTags/birthday"
Task 5: 后端响应 DTO 扩展 — UserProfileResponse.java
Files:
-
Modify:
server/src/main/java/com/emotion/dto/response/userprofile/UserProfileResponse.java -
Step 1: 在 idealLife 字段后新增5个字段
找到 idealLife 字段定义(约第105行),在其后、scripts 字段前插入:
/**
* 所在城市
*/
private String city;
/**
* 行业
*/
private String industry;
/**
* 公司
*/
private String company;
/**
* 性格标签 (JSON字符串)
*/
private String personalityTags;
/**
* 生日
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthday;
- Step 2: Commit
git add server/src/main/java/com/emotion/dto/response/userprofile/UserProfileResponse.java
git commit -m "feat: extend UserProfileResponse with city/industry/company/personalityTags/birthday"
Task 6: 前端 Service 扩展 — userProfile.js
Files:
-
Modify:
mini-program/src/services/userProfile.js -
Step 1: 扩展 transformToBackendFormat 的解构和返回对象
原解构(第47-59行)新增5个字段:
const {
id,
nickname,
gender,
zodiac,
profession,
mbti,
hobbies,
childhood,
joy,
low,
future,
city,
industry,
company,
personalityTags,
birthday
} = frontendData
原返回对象(第61-83行)在 idealLife 后新增:
return {
id,
nickname,
gender,
zodiac,
profession,
mbti,
// 兴趣爱好转为JSON字符串
hobbies: Array.isArray(hobbies) ? JSON.stringify(hobbies) : hobbies,
// 童年经历
childhoodDate: childhood?.date || null,
childhoodContent: childhood?.text || null,
// 高光时刻(对应前端的joy)
peakDate: joy?.date || null,
peakContent: joy?.text || null,
// 低谷时期(对应前端的low)
valleyDate: low?.date || null,
valleyContent: low?.text || null,
// 未来期许
futureVision: future?.vision || null,
// 理想生活状态
idealLife: future?.ideal || null,
// 新增字段
city: city || null,
industry: industry || null,
company: company || null,
personalityTags: Array.isArray(personalityTags) ? JSON.stringify(personalityTags) : personalityTags,
birthday: birthday || null
}
- Step 2: 扩展 transformToFrontendFormat 的解构和返回对象
原解构(第94-111行)新增5个字段:
const {
id,
userId,
nickname,
gender,
zodiac,
profession,
mbti,
hobbies,
childhoodDate,
childhoodContent,
peakDate,
peakContent,
valleyDate,
valleyContent,
futureVision,
idealLife,
city,
industry,
company,
personalityTags,
birthday
} = backendData
原返回对象(第113-143行)在 future 对象后新增:
return {
id,
userId,
nickname: nickname || '',
gender: gender || '',
zodiac: zodiac || '',
profession: profession || '',
mbti: mbti || '',
// 兴趣爱好从JSON字符串解析
hobbies: hobbies ? (typeof hobbies === 'string' ? JSON.parse(hobbies) : hobbies) : [],
// 童年经历
childhood: {
date: childhoodDate || '',
text: childhoodContent || ''
},
// 高光时刻
joy: {
date: peakDate || '',
text: peakContent || ''
},
// 低谷时期
low: {
date: valleyDate || '',
text: valleyContent || ''
},
// 未来期许
future: {
vision: futureVision || '',
ideal: idealLife || ''
},
// 新增字段
city: city || '',
industry: industry || '',
company: company || '',
personalityTags: personalityTags ? (typeof personalityTags === 'string' ? JSON.parse(personalityTags) : personalityTags) : [],
birthday: birthday || ''
}
- Step 3: Commit
git add mini-program/src/services/userProfile.js
git commit -m "feat: add city/industry/company/personalityTags/birthday to userProfile transforms"
Task 7: 前端 Store 扩展 — app.js
Files:
-
Modify:
mini-program/src/stores/app.js -
Step 1: 扩展 registrationData 初始结构
找到 registrationData 定义(第19-30行),在 future 对象后新增:
registrationData: {
nickname: '',
gender: '',
mbti: '',
zodiac: '',
profession: '',
hobbies: [],
childhood: { date: '', text: '' },
joy: { date: '', text: '' },
low: { date: '', text: '' },
future: { vision: '', ideal: '' },
city: '',
industry: '',
company: '',
personalityTags: [],
birthday: ''
}
注意:此文件使用 CRLF 行尾(\r\n),使用 Edit 工具时请按 Read 工具输出的格式匹配。
- Step 2: Commit
git add mini-program/src/stores/app.js
git commit -m "feat: extend registrationData with city/industry/company/personalityTags/birthday"
Task 8: 人生轨迹页去写死 — RecordView.vue
Files:
-
Modify:
mini-program/src/pages/main/RecordView.vue -
Step 1: 删除 sampleEvents 数组
删除第143-176行的 sampleEvents 数组定义。
- Step 2: 修改 events computed 去除 sampleEvents 回退
将第179-182行:
const events = computed(() => {
const list = store.events || []
return list.length ? list : sampleEvents
})
改为:
const events = computed(() => store.events || [])
- Step 3: 修改 heroTags computed 去除硬编码默认值
将第183-187行:
const heroTags = computed(() => {
const hobbies = profile.value.hobbies
if (Array.isArray(hobbies) && hobbies.length) return hobbies.slice(0, 4)
return ['阅读', '旅行', '音乐', '创作']
})
改为:
const heroTags = computed(() => {
const hobbies = profile.value.hobbies
if (Array.isArray(hobbies) && hobbies.length) return hobbies.slice(0, 4)
return []
})
- Step 4: 修改 avatar computed 去除硬编码昵称
将第189-192行:
const avatar = computed(() => {
const nickname = profile.value.nickname || 'Zoey'
return `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(nickname)}&backgroundColor=b982ff`
})
改为:
const avatar = computed(() => {
const nickname = profile.value.nickname || 'User'
return `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(nickname)}&backgroundColor=b982ff`
})
- Step 5: 修改模板中硬编码默认值
将第13行:
<text class="profile-name">{{ profile.nickname || 'Zoey' }}</text>
改为:
<text class="profile-name">{{ profile.nickname || '未设置' }}</text>
将第31行:
<text class="meta-value">{{ profile.zodiac || '巨蟹座' }}</text>
改为:
<text class="meta-value">{{ profile.zodiac || '未设置' }}</text>
将第38行:
<text class="meta-value">{{ profile.mbti || 'ENTJ' }}</text>
改为:
<text class="meta-value">{{ profile.mbti || '未设置' }}</text>
将第45行:
<text class="meta-value">{{ profile.profession || '产品经理' }}</text>
改为:
<text class="meta-value">{{ profile.profession || '未设置' }}</text>
将第56行:
<text class="hobby-text">{{ heroTags.join(' · ') }}</text>
改为:
<text class="hobby-text">{{ heroTags.length ? heroTags.join(' · ') : '未设置' }}</text>
- Step 6: Commit
git add mini-program/src/pages/main/RecordView.vue
git commit -m "fix: remove hardcoded defaults and sampleEvents from RecordView"
Task 9: 编辑资料页去写死 — onboarding/index.vue
Files:
-
Modify:
mini-program/src/pages/onboarding/index.vue -
Step 1: 修改 avatarUrl computed 去除硬编码昵称
将第234-238行:
const avatarUrl = computed(() => {
if (avatarLocal.value) return avatarLocal.value
const nickname = form.nickname || 'Zoey'
return `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(nickname)}&backgroundColor=b982ff`
})
改为:
const avatarUrl = computed(() => {
if (avatarLocal.value) return avatarLocal.value
const nickname = form.nickname || 'User'
return `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(nickname)}&backgroundColor=b982ff`
})
- Step 2: 修改 birthdayDisplay computed 去除硬编码日期
将第240-244行:
const birthdayDisplay = computed(() => {
if (!birthday.value) return '1998年06月18日'
const [year, month, day] = birthday.value.split('-')
return `${year}年${month}月${day}日`
})
改为:
const birthdayDisplay = computed(() => {
if (!birthday.value) return '请选择生日'
const [year, month, day] = birthday.value.split('-')
return `${year}年${month}月${day}日`
})
- Step 3: 修改 syncFromStore 去除所有硬编码默认值
将第246-265行的 syncFromStore 函数整体替换为:
const syncFromStore = () => {
const source = store.userProfile || store.registrationData || {}
Object.assign(form, {
nickname: source.nickname || '',
gender: source.gender || '',
zodiac: source.zodiac || '',
mbti: source.mbti || '',
profession: source.profession || '',
city: source.city || '',
industry: source.industry || '',
company: source.company || '',
personalityTags: Array.isArray(source.personalityTags) ? [...source.personalityTags] : [],
hobbies: Array.isArray(source.hobbies) ? [...source.hobbies] : [],
childhood: { date: source.childhood?.date || '', text: source.childhood?.text || '' },
joy: { date: source.joy?.date || '', text: source.joy?.text || '' },
low: { date: source.low?.date || '', text: source.low?.text || '' },
future: { vision: source.future?.vision || '', ideal: source.future?.ideal || '' }
})
birthday.value = source.birthday || ''
}
注意:此文件使用 CRLF 行尾(\r\n),使用 Edit 工具时请按 Read 工具输出的格式匹配。
- Step 4: Commit
git add mini-program/src/pages/onboarding/index.vue
git commit -m "fix: remove hardcoded defaults from profile edit page"
Task 10: 验证测试
Files:
-
无需修改,纯验证
-
Step 1: 后端编译验证
cd server && mvn compile -q
预期:编译成功,无错误。
- Step 2: 前端语法检查(如有 linter)
cd mini-program && npm run lint 2>/dev/null || echo "No lint script, skipping"
- Step 3: 手动功能验证清单
- 启动后端服务,确认无启动错误。
- 在数据库执行
DESCRIBE t_user_profile;,确认city、industry、company、personality_tags、birthday五列已存在。 - 小程序登录后进入"人生轨迹"页:
- 未设置资料时,昵称显示"未设置",星座/MBTI/职业显示"未设置",爱好显示"未设置"。
- 无人生事件时,显示"还没有人生轨迹"空状态,无 sampleEvents。
- 点击"编辑资料":
- 首次进入(无资料)时,所有输入框为空,生日显示"请选择生日",性格标签和爱好无选中项。
- 填写所有字段(包括城市、行业、公司、性格标签、生日)后保存。
- 保存成功后返回人生轨迹页,数据已更新。
- 再次进入编辑资料页,所有字段正确回显。
- 已有用户(老数据,无新字段)登录后,页面正常展示,无报错,新字段显示为空。
- Step 4: Commit(如测试通过)
git log --oneline -10
预期看到 9 个 commits(Task 1-9 各一个)。
Self-Review
1. Spec coverage:
- 数据库扩展5个字段 → Task 1 ✅
- 后端实体扩展 → Task 2 ✅
- 后端 DTO 扩展(Create/Update/Response)→ Task 3/4/5 ✅
- 前端 Service 双向转换扩展 → Task 6 ✅
- 前端 Store 扩展 → Task 7 ✅
- 人生轨迹页去除写死数据和 sampleEvents → Task 8 ✅
- 编辑资料页去除硬编码默认值 → Task 9 ✅
- 测试验证 → Task 10 ✅
2. Placeholder scan:
- 无 TBD/TODO/"implement later"/"fill in details" ✅
- 无 "Add appropriate error handling" 等模糊描述 ✅
- 无 "Similar to Task N" ✅
- 每个代码步骤都包含完整代码 ✅
3. Type consistency:
- 后端5个字段名在 Entity/CreateRequest/UpdateRequest/Response 中完全一致:
city,industry,company,personalityTags,birthday✅ - 前端 Service 中
transformToBackendFormat和transformToFrontendFormat字段名一致 ✅ - Store 中
registrationData字段名与 Service 一致 ✅ personalityTags在前端始终作为数组处理,后端作为 JSON 字符串,转换逻辑与hobbies保持一致 ✅birthday前后端均为yyyy-MM-dd字符串格式 ✅