2026-05-22-family-member-channel-display.md 7.0 KB

家庭成员渠道展示 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: 在管理端「家庭成员」列表中新增「所属渠道」列,展示每个成员绑定的渠道名称。

Architecture: 后端在 FamilyMemberDTO 新增 channelNames 字段,FamilyMemberServiceImpl 重写 page() 方法,在基类查询结果后批量查渠道名并填充(一次查询,无 N+1)。前端 familyMember.vue 新增一列展示该字段。

Tech Stack: Java 17、MyBatis-Plus、Spring Boot(后端);Vue 2.6、Element UI(前端)


文件变更清单

操作 文件
Modify happytree-sys/src/main/java/com/ringzle/modules/core/dto/FamilyMemberDTO.java
Modify happytree-sys/src/main/java/com/ringzle/modules/core/service/impl/FamilyMemberServiceImpl.java
Modify SxsnPC/src/views/modules/member/components/familyMember.vue

Task 1: FamilyMemberDTO 新增 channelNames 字段

Files:

  • Modify: happytree-sys/src/main/java/com/ringzle/modules/core/dto/FamilyMemberDTO.java

  • [ ] Step 1: 在 FamilyMemberDTO 末尾新增字段

找到文件末尾的最后一个字段(updateDate),在其后、类的 } 之前插入:

@ApiModelProperty(value = "渠道名称,逗号分隔")
private String channelNames;

完整的 DTO 末尾应如下:

	@ApiModelProperty(value = "更新时间")
	private Date updateDate;

	@ApiModelProperty(value = "渠道名称,逗号分隔")
	private String channelNames;

}
  • Step 2: 编译验证
cd /workspace/sxsn/happytree
mvn compile -pl happytree-sys -am -q 2>&1 | tail -5

期望输出:无 ERROR,最后一行为 BUILD SUCCESS

  • Step 3: Commit
git add happytree-sys/src/main/java/com/ringzle/modules/core/dto/FamilyMemberDTO.java
git commit -m "feat: FamilyMemberDTO 新增 channelNames 字段"

Task 2: FamilyMemberServiceImpl 重写 page() 填充 channelNames

Files:

  • Modify: happytree-sys/src/main/java/com/ringzle/modules/core/service/impl/FamilyMemberServiceImpl.java

背景:FamilyMemberEntity.channelIds 是逗号分隔的渠道 ID 字符串(如 "1,2,3"),可能为 null 或空。ChannelEntitychannelName 字段,channelDao 已在 Service 中注入。

  • Step 1: 在 FamilyMemberServiceImpl 中重写 page() 方法

getWrapper() 方法之前插入以下方法:

@Override
public PageData<FamilyMemberDTO> page(Map<String, Object> params) {
    // 1. 调用基类查询
    PageData<FamilyMemberDTO> pageData = super.page(params);
    List<FamilyMemberDTO> list = pageData.getList();
    if (CollectionUtil.isEmpty(list)) {
        return pageData;
    }

    // 2. 收集所有非空 channelIds,解析出所有渠道 ID
    List<Long> allChannelIds = list.stream()
        .map(dto -> {
            // 从 Entity 中取 channelIds(DTO 没有该字段,需重新查)
            return dto.getId();
        })
        .collect(Collectors.toList());

    // 批量查 Entity 以获取 channelIds 字段
    List<FamilyMemberEntity> entities = baseDao.selectBatchIds(
        list.stream().map(FamilyMemberDTO::getId).collect(Collectors.toList())
    );

    // 3. 建立 memberId -> channelIds 的 Map
    Map<Long, String> memberChannelIdsMap = entities.stream()
        .filter(e -> StringUtils.isNotBlank(e.getChannelIds()))
        .collect(Collectors.toMap(FamilyMemberEntity::getId, FamilyMemberEntity::getChannelIds));

    // 4. 收集所有渠道 ID,去重,一次性查渠道名
    Set<Long> channelIdSet = memberChannelIdsMap.values().stream()
        .flatMap(ids -> Arrays.stream(ids.split(",")))
        .map(String::trim)
        .filter(StringUtils::isNotBlank)
        .map(Long::parseLong)
        .collect(Collectors.toSet());

    Map<Long, String> channelNameMap = new HashMap<>();
    if (!channelIdSet.isEmpty()) {
        Collection<com.ringzle.modules.core.entity.ChannelEntity> channels =
            channelDao.selectBatchIds(channelIdSet);
        channels.forEach(c -> channelNameMap.put(c.getId(), c.getChannelName()));
    }

    // 5. 逐行填充 channelNames
    list.forEach(dto -> {
        String channelIds = memberChannelIdsMap.get(dto.getId());
        if (StringUtils.isBlank(channelIds)) {
            dto.setChannelNames("");
            return;
        }
        String names = Arrays.stream(channelIds.split(","))
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .map(id -> channelNameMap.getOrDefault(Long.parseLong(id), ""))
            .filter(StringUtils::isNotBlank)
            .collect(Collectors.joining("、"));
        dto.setChannelNames(names);
    });

    return pageData;
}
  • Step 2: 确认 import 完整

文件顶部 import 区域需包含以下(若已有则跳过):

import com.ringzle.common.page.PageData;
import java.util.*;
import java.util.stream.Collectors;

ArraysHashMapSetCollection 均在 java.util.* 中,StringUtils 已有 org.apache.commons.lang3.StringUtilsCollectionUtil 已有 cn.hutool.core.collection.CollectionUtil

  • Step 3: 编译验证
cd /workspace/sxsn/happytree
mvn compile -pl happytree-sys -am -q 2>&1 | tail -5

期望:BUILD SUCCESS,无 ERROR。

  • Step 4: Commit
git add happytree-sys/src/main/java/com/ringzle/modules/core/service/impl/FamilyMemberServiceImpl.java
git commit -m "feat: 家庭成员列表分页接口填充渠道名称"

Task 3: 前端 familyMember.vue 新增「所属渠道」列

Files:

  • Modify: SxsnPC/src/views/modules/member/components/familyMember.vue

  • [ ] Step 1: 在「就读学校」列后插入「所属渠道」列

找到:

<el-table-column prop="currentSchool" label="就读学校"></el-table-column>

在其后插入:

<el-table-column label="所属渠道">
  <template slot-scope="scope">{{ scope.row.channelNames || '-' }}</template>
</el-table-column>
  • Step 2: 手动验证

打开管理端会员详情页,切换到「家庭成员」tab:

  • 有渠道的成员显示渠道名(如「渠道A、渠道B」)
  • 无渠道的成员显示「-」

  • [ ] Step 3: Commit

git add SxsnPC/src/views/modules/member/components/familyMember.vue
git commit -m "feat: 家庭成员列表新增所属渠道列"

自检清单

  • 规格「列表展示家庭成员绑定的渠道」→ Task 1 + Task 2 + Task 3
  • 无 N+1:批量查 Entity + 批量查 Channel,两次查询搞定 → Task 2 Step 1
  • channelIds 为 null/空时 channelNames 为空字符串,前端显示「-」→ Task 2 Step 1 + Task 3 Step 1
  • 渠道 ID 不存在于 channelNameMap 时用 getOrDefault 跳过,不抛异常 → Task 2 Step 1
  • 无 placeholder,所有步骤含完整代码