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 (
- [ ]) for tracking.
Goal: Add horizontal-scrolling family member card strip to four tab pages (home/body/mind/action), with per-member data filtering and show_to_family visibility control.
Architecture: Add show_to_family column to children and users tables via DatabaseInitializer. Create FamilyMemberCard + FamilyMemberStrip components. Two new API endpoints under FamilyUserController for visible members query and visibility toggle. Integrate strip into all four dimension pages above FamilyEnergyBar.
Tech Stack: Spring Boot 2.7.18 / MyBatis-Plus / uni-app Vue 2 (Options API, no ?. operator, no CSS Grid)
Files:
Modify: cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java
[ ] Step 1: Add migration block after existing family_member_attributes migration (~line 1556)
// ===== 迁移: children + users 表添加 show_to_family 字段(v1.3 家庭成员可见性) =====
try {
String checkChildCol = "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'children' AND COLUMN_NAME = 'show_to_family'";
if (jdbcTemplate.queryForObject(checkChildCol, Integer.class) == 0) {
jdbcTemplate.execute("ALTER TABLE children ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可见: 1=是, 0=否'");
log.info("已添加 show_to_family 列到 children 表");
} else {
log.info("children.show_to_family 已存在,跳过");
}
} catch (Exception e) {
log.warn("添加 children.show_to_family 列失败: {}", e.getMessage());
}
try {
String checkUserCol = "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'show_to_family'";
if (jdbcTemplate.queryForObject(checkUserCol, Integer.class) == 0) {
jdbcTemplate.execute("ALTER TABLE users ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可见: 1=是, 0=否'");
log.info("已添加 show_to_family 列到 users 表");
} else {
log.info("users.show_to_family 已存在,跳过");
}
} catch (Exception e) {
log.warn("添加 users.show_to_family 列失败: {}", e.getMessage());
}
[ ] Step 2: Verify compilation
Run: cd cfc-backend && mvn clean compile -q
Expected: BUILD SUCCESS
[ ] Step 3: Commit
git add cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java
git commit -m "feat: add show_to_family column migration to children and users tables"
Files:
cfc-backend/src/main/java/com/etotem/cfc/dto/UpdateChildDTO.javaModify: cfc-backend/src/main/java/com/etotem/cfc/dto/ChildInfoDTO.java
[ ] Step 1: Add showToFamily to UpdateChildDTO
// After line 17 (private String theme;)
private Integer showToFamily; // 是否对家庭成员可见: 1=是, 0=否
[ ] Step 2: Add showToFamily to ChildInfoDTO
// After line 21 (private Integer focusRemaining;)
private Integer showToFamily; // 是否对家庭成员可见: 1=是, 0=否
[ ] Step 3: Verify compilation
Run: cd cfc-backend && mvn clean compile -q
Expected: BUILD SUCCESS
[ ] Step 4: Commit
git add cfc-backend/src/main/java/com/etotem/cfc/dto/UpdateChildDTO.java cfc-backend/src/main/java/com/etotem/cfc/dto/ChildInfoDTO.java
git commit -m "feat: add showToFamily field to ChildInfoDTO and UpdateChildDTO"
Files:
cfc-backend/src/main/java/com/etotem/cfc/service/UserService.javaCreate: cfc-backend/src/main/java/com/etotem/cfc/dto/FamilyMemberVO.java
[ ] Step 1: Create FamilyMemberVO DTO
package com.etotem.cfc.dto;
import lombok.Data;
@Data
public class FamilyMemberVO {
private Long id;
private String nickname;
private String avatar;
private String memberType; // 'child' 或 'parent'
private String roleLabel; // '孩子' 或 '家长'
private Integer totalPoints;
private Integer showToFamily; // 1=可见, 0=不可见
}
[ ] Step 2: Add getVisibleMembers method to UserService
Insert after getChildren() method (after line 749):
public List<FamilyMemberVO> getVisibleMembers(Long userId) {
User user = userMapper.selectById(userId);
if (user == null || user.getFamilyId() == null) {
return Collections.emptyList();
}
Long familyId = user.getFamilyId();
List<FamilyMemberVO> members = new ArrayList<>();
// 查询可见的家长成员
List<User> parents = userMapper.selectList(
new LambdaQueryWrapper<User>()
.eq(User::getFamilyId, familyId)
.eq(User::getRole, "parent")
.eq(User::getShowToFamily, 1)
);
for (User p : parents) {
FamilyMemberVO vo = new FamilyMemberVO();
vo.setId(p.getId());
vo.setNickname(p.getNickname());
vo.setAvatar(p.getAvatar());
vo.setMemberType("parent");
vo.setRoleLabel("家长");
vo.setTotalPoints(p.getTotalPoints());
vo.setShowToFamily(p.getShowToFamily());
members.add(vo);
}
// 查询可见的孩子成员
List<Child> children = childMapper.selectList(
new LambdaQueryWrapper<Child>()
.eq(Child::getFamilyId, familyId)
.eq(Child::getShowToFamily, 1)
);
for (Child c : children) {
FamilyMemberVO vo = new FamilyMemberVO();
vo.setId(c.getId());
vo.setNickname(c.getNickname());
vo.setAvatar(null);
vo.setMemberType("child");
vo.setRoleLabel("孩子");
vo.setTotalPoints(c.getTotalPoints());
vo.setShowToFamily(c.getShowToFamily());
members.add(vo);
}
return members;
}
Add imports at top of file:
import com.etotem.cfc.dto.FamilyMemberVO;
import java.util.ArrayList;
import java.util.Collections;
updateChildVisibility method to UserServiceInsert after getVisibleMembers:
public boolean updateVisibility(Long userId, Long memberId, String memberType, Integer showToFamily) {
User user = userMapper.selectById(userId);
if (user == null) return false;
if ("child".equals(memberType)) {
// 只有家长可以修改孩子的可见性
if (!"parent".equals(user.getRole())) return false;
Child child = childMapper.selectById(memberId);
if (child == null || !child.getFamilyId().equals(user.getFamilyId())) return false;
child.setShowToFamily(showToFamily);
child.setUpdatedAt(new Date());
childMapper.updateById(child);
return true;
} else if ("parent".equals(memberType)) {
// 用户只能修改自己的可见性
if (!memberId.equals(userId)) return false;
user.setShowToFamily(showToFamily);
user.setUpdatedAt(new Date());
userMapper.updateById(user);
return true;
}
return false;
}
toChildInfoDTO to include showToFamilyIn the existing toChildInfoDTO method (line 802+), add after line ~819:
dto.setShowToFamily(child.getShowToFamily());
updateChild method to handle showToFamilyIn updateChild method (line 751), add before child.setUpdatedAt(new Date()):
if (dto.getShowToFamily() != null) {
child.setShowToFamily(dto.getShowToFamily());
}
Run: cd cfc-backend && mvn clean compile -q
Expected: BUILD SUCCESS
[ ] Step 7: Commit
git add cfc-backend/src/main/java/com/etotem/cfc/dto/FamilyMemberVO.java cfc-backend/src/main/java/com/etotem/cfc/service/UserService.java
git commit -m "feat: add getVisibleMembers and updateVisibility to UserService"
Files:
Modify: cfc-backend/src/main/java/com/etotem/cfc/controller/family/FamilyUserController.java
[ ] Step 1: Add getVisibleFamilyMembers endpoint
Insert before getFamilyMembers (before line 167):
@Operation(summary = "获取对当前用户可见的家庭成员列表")
@PostMapping("/members/visible")
public Result<List<FamilyMemberVO>> getVisibleFamilyMembers(javax.servlet.http.HttpServletRequest request) {
Long userId = getUserId(request);
List<FamilyMemberVO> members = userService.getVisibleMembers(userId);
return Result.success(members);
}
updateMemberVisibility endpointInsert after the above:
@Operation(summary = "更新家庭成员可见性")
@PostMapping("/members/update-visibility")
public Result<Boolean> updateMemberVisibility(javax.servlet.http.HttpServletRequest request,
@RequestBody Map<String, Object> body) {
Long userId = getUserId(request);
Long memberId = Long.valueOf(body.get("memberId").toString());
String memberType = (String) body.get("memberType");
Integer showToFamily = Integer.valueOf(body.get("showToFamily").toString());
boolean success = userService.updateVisibility(userId, memberId, memberType, showToFamily);
return Result.success(success);
}
Ensure FamilyMemberVO is imported via the existing import com.etotem.cfc.dto.*; on line 5 — it's already a wildcard import, so FamilyMemberVO will be auto-included.
Run: cd cfc-backend && mvn clean compile -q
Expected: BUILD SUCCESS
[ ] Step 5: Commit
git add cfc-backend/src/main/java/com/etotem/cfc/controller/family/FamilyUserController.java
git commit -m "feat: add /family/user/members/visible and /members/update-visibility endpoints"
Files:
Modify: cfc-frontend/utils/api.js
[ ] **Step 1: Add getVisibleFamilyMembers and updateMemberVisibility exports
Insert after getFamilyMembers (after line 146):
// 获取对当前用户可见的家庭成员列表(已过滤 show_to_family=0 的成员)
export const getVisibleFamilyMembers = () => {
return request('/api/family/user/members/visible', 'POST')
}
// 更新成员可见性
export const updateMemberVisibility = (memberId, memberType, showToFamily) => {
return request('/api/family/user/members/update-visibility', 'POST', { memberId, memberType, showToFamily })
}
[ ] Step 2: Commit
git add cfc-frontend/utils/api.js
git commit -m "feat: add getVisibleFamilyMembers and updateMemberVisibility API functions"
Files:
Create: cfc-frontend/components/FamilyMemberCard.vue
[ ] Step 1: Create component
<template>
<view class="family-member-card" :class="{ 'is-active': isSelected }" @click="onSelect">
<view class="member-avatar-wrap">
<image v-if="member.avatar" class="member-avatar" :src="member.avatar" mode="aspectFill" />
<view v-else class="member-avatar-default">
<text class="avatar-letter">{{ displayLetter }}</text>
</view>
<text class="member-role-badge">{{ member.memberType === 'parent' ? '家长' : '孩子' }}</text>
</view>
<text class="member-name">{{ member.nickname }}</text>
<text class="member-dan" v-if="member.danLevel">{{ member.danLevel }}</text>
<view class="selected-check" v-if="isSelected">
<text class="check-icon">✓</text>
</view>
</view>
</template>
<script>
export default {
props: {
member: { type: Object, required: true },
isSelected: { type: Boolean, default: false }
},
computed: {
displayLetter: function() {
if (!this.member.nickname) return '?'
return this.member.nickname[0]
}
},
methods: {
onSelect: function() {
this.$emit('select', this.member)
}
}
}
</script>
<style scoped>
.family-member-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 16rpx 20rpx;
margin-right: 16rpx;
background: #fff;
border-radius: 20rpx;
min-width: 130rpx;
position: relative;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
border: 3rpx solid transparent;
transition: all 0.2s;
}
.family-member-card.is-active {
border-color: #F97316;
background: #FFF7ED;
}
.member-avatar-wrap {
position: relative;
margin-bottom: 8rpx;
}
.member-avatar {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
}
.member-avatar-default {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: #FDE68A;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-letter {
font-size: 32rpx;
color: #92400E;
font-weight: bold;
}
.member-role-badge {
position: absolute;
bottom: -4rpx;
right: -8rpx;
font-size: 18rpx;
color: #fff;
background: #F97316;
padding: 2rpx 8rpx;
border-radius: 12rpx;
line-height: 1.4;
transform: scale(0.85);
}
.member-name {
font-size: 24rpx;
color: #333;
font-weight: 500;
text-align: center;
max-width: 100rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.member-dan {
font-size: 18rpx;
color: #F97316;
margin-top: 2rpx;
}
.selected-check {
position: absolute;
top: 4rpx;
right: 4rpx;
width: 32rpx;
height: 32rpx;
background: #F97316;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.check-icon {
color: #fff;
font-size: 20rpx;
font-weight: bold;
}
</style>
[ ] Step 2: Commit
git add cfc-frontend/components/FamilyMemberCard.vue
git commit -m "feat: add FamilyMemberCard component"
Files:
Create: cfc-frontend/components/FamilyMemberStrip.vue
[ ] Step 1: Create component
<template>
<view class="member-strip" v-if="members && members.length > 0">
<scroll-view class="strip-scroll" scroll-x :show-scrollbar="false" :enhanced="true">
<view class="strip-inner">
<FamilyMemberCard
v-for="m in members"
:key="m.memberType + '-' + m.id"
:member="m"
:isSelected="m.id === selectedMemberId && m.memberType === selectedMemberType"
@select="onSelectMember"
/>
</view>
</scroll-view>
</view>
</template>
<script>
import FamilyMemberCard from './FamilyMemberCard.vue'
export default {
components: { FamilyMemberCard },
props: {
members: { type: Array, default: function() { return [] } },
selectedMemberId: { type: Number, default: null },
selectedMemberType: { type: String, default: '' }
},
methods: {
onSelectMember: function(member) {
this.$emit('select', member)
}
}
}
</script>
<style scoped>
.member-strip {
margin: 0 30rpx 16rpx;
}
.strip-scroll {
width: 100%;
white-space: nowrap;
}
.strip-inner {
display: flex;
flex-direction: row;
padding: 4rpx 0;
}
</style>
[ ] Step 2: Commit
git add cfc-frontend/components/FamilyMemberStrip.vue
git commit -m "feat: add FamilyMemberStrip horizontal scrolling component"
Files:
Modify: cfc-frontend/pages/index/parent-index.vue
[ ] Step 1: Add component import (after line ~361)
import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
[ ] Step 2: Add to components block (around line ~375)
FamilyMemberStrip,
[ ] Step 3: Add data properties (in data() around line ~390)
familyMembersVisible: [],
selectedMemberId: null,
selectedMemberType: 'child',
[ ] Step 4: Insert FamilyMemberStrip in template after PageBanner
Insert between PageBanner (line ~10) and the children-row section (line ~13):
<!-- ===== 家庭成员卡片条 ===== -->
<FamilyMemberStrip
v-if="isLoggedIn"
:members="familyMembersVisible"
:selectedMemberId="selectedMemberId"
:selectedMemberType="selectedMemberType"
@select="onFamilyMemberSelect" />
After the existing getFamilyMembers() call (~line 488), add:
try {
const visibleRes = await getVisibleFamilyMembers()
const visibleMembers = visibleRes.data || []
this.familyMembersVisible = visibleMembers
// 默认选中第一个孩子
var firstChild = visibleMembers.find(function(m) { return m.memberType === 'child' })
if (firstChild) {
this.selectedMemberId = firstChild.id
this.selectedMemberType = firstChild.memberType
}
} catch (e) {
console.log('获取可见家庭成员失败', e)
}
[ ] Step 6: Add onFamilyMemberSelect method
onFamilyMemberSelect: function(member) {
this.selectedMemberId = member.id
this.selectedMemberType = member.memberType
// 切换选中孩子以刷新数据
if (member.memberType === 'child') {
this.selectedChild = { id: member.id, nickname: member.nickname }
this.loadChildData(member.id)
}
// 如果是家长,切换为家长视图
}
[ ] Step 7: Add import for getVisibleFamilyMembers
Find the existing import line ~354:
import { getFamilyMembers, getChildren, ... } from '../../utils/api.js'
Change to:
import { getFamilyMembers, getChildren, getVisibleFamilyMembers, getPendingReviewTasks, ... } from '../../utils/api.js'
[ ] Step 8: Commit
git add cfc-frontend/pages/index/parent-index.vue
git commit -m "feat: integrate FamilyMemberStrip into parent-index page"
Files:
Modify: cfc-frontend/pages/body/index.vue
[ ] Step 1: Add component import after existing imports (~line 1-5)
import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
import { getVisibleFamilyMembers } from '../../utils/api.js'
[ ] Step 2: Add to components block
FamilyMemberStrip,
[ ] Step 3: Add data properties (in data())
familyMembersVisible: [],
selectedMemberId: null,
selectedMemberType: '',
[ ] Step 4: Insert FamilyMemberStrip in template after PageBanner (after line 6)
<!-- ===== 家庭成员卡片条 ===== -->
<FamilyMemberStrip
v-if="isLoggedIn"
:members="familyMembersVisible"
:selectedMemberId="selectedMemberId"
:selectedMemberType="selectedMemberType"
@select="onFamilyMemberSelect" />
[ ] Step 5: Add data loading and onFamilyMemberSelect method
In onShow or a dedicated loadFamilyMembers method:
loadVisibleMembers: function() {
var self = this
getVisibleFamilyMembers().then(function(res) {
if (res && res.data) {
self.familyMembersVisible = res.data
}
}).catch(function(e) {
console.log('获取可见家庭成员失败', e)
})
},
onFamilyMemberSelect: function(member) {
this.selectedMemberId = member.id
this.selectedMemberType = member.memberType
// 切换该成员的健康数据
this.loadHealthData(member)
}
[ ] Step 6: Commit
git add cfc-frontend/pages/body/index.vue
git commit -m "feat: integrate FamilyMemberStrip into body page"
Files:
Modify: cfc-frontend/pages/mind/index.vue
[ ] Step 1: Add import + component + data + template insertion
Same pattern as Task 9 — insert FamilyMemberStrip after PageBanner, load getVisibleFamilyMembers, add onFamilyMemberSelect.
Find the template block and add after PageBanner closing tag (after line ~6):
<FamilyMemberStrip
v-if="isLoggedIn"
:members="familyMembersVisible"
:selectedMemberId="selectedMemberId"
:selectedMemberType="selectedMemberType"
@select="onFamilyMemberSelect" />
[ ] Step 2: Commit
git add cfc-frontend/pages/mind/index.vue
git commit -m "feat: integrate FamilyMemberStrip into mind page"
Files:
Modify: cfc-frontend/pages/action/index.vue
[ ] Step 1: Same pattern as Task 9/10
Insert after PageBanner (~line 6):
<FamilyMemberStrip
v-if="isLoggedIn"
:members="familyMembersVisible"
:selectedMemberId="selectedMemberId"
:selectedMemberType="selectedMemberType"
@select="onFamilyMemberSelect" />
[ ] Step 2: Commit
git add cfc-frontend/pages/action/index.vue
git commit -m "feat: integrate FamilyMemberStrip into action page"
Files:
Modify: cfc-frontend/pages/profile/edit-child.vue
[ ] Step 1: Add visibility toggle switch after the theme picker (after line ~65)
<!-- 对家庭成员可见 -->
<view class="form-item">
<text class="label">对家庭成员可见</text>
<switch :checked="form.showToFamily !== 0" @change="onVisibilityChange" color="#F97316" />
<text class="hint">关闭后其他家庭成员看不到该孩子</text>
</view>
[ ] Step 2: Add change handler and form default
onVisibilityChange: function(e) {
this.form.showToFamily = e.detail.value ? 1 : 0
}
In data(), set form default:
form: {
// ... existing fields
showToFamily: 1
}
In onLoad, when loading child data:
// after setting form fields
self.form.showToFamily = child.showToFamily !== undefined ? child.showToFamily : 1
[ ] Step 3: Commit
git add cfc-frontend/pages/profile/edit-child.vue
git commit -m "feat: add showToFamily toggle to child edit page"
Files:
Modify: cfc-frontend/pages/profile/profile.vue
[ ] Step 1: Add visibility toggle in settings section
Find the profile settings area and add:
<view class="setting-item">
<text class="setting-label">对家庭成员可见</text>
<switch :checked="showToFamily !== 0" @change="onSelfVisibilityChange" color="#F97316" />
</view>
[ ] Step 2: Add handler
onSelfVisibilityChange: function(e) {
var show = e.detail.value ? 1 : 0
var self = this
updateMemberVisibility(self.userId, 'parent', show).then(function(res) {
if (res && res.code === 200) {
uni.showToast({ title: '已更新', icon: 'success' })
}
})
}
[ ] Step 3: Commit
git add cfc-frontend/pages/profile/profile.vue
git commit -m "feat: add showToFamily toggle to profile page"
Run: cd cfc-backend && mvn clean compile -q
Expected: BUILD SUCCESS
Check for template syntax issues (no ?., no CSS Grid).
[ ] Step 3: Verify file checklist
[ ] DatabaseInitializer.java — migration added
[ ] UpdateChildDTO.java — showToFamily field
[ ] ChildInfoDTO.java — showToFamily field
[ ] FamilyMemberVO.java — new DTO
[ ] UserService.java — getVisibleMembers + updateVisibility
[ ] FamilyUserController.java — 2 new endpoints
[ ] api.js — 2 new API functions
[ ] FamilyMemberCard.vue — new component
[ ] FamilyMemberStrip.vue — new component
[ ] parent-index.vue — strip integrated
[ ] body/index.vue — strip integrated
[ ] mind/index.vue — strip integrated
[ ] action/index.vue — strip integrated
[ ] edit-child.vue — visibility toggle
[ ] profile.vue — visibility toggle (self)