Files
zkxw/docs/superpowers/plans/2026-08-05-post-list-name-link-bound-users.md
T
2026-08-05 14:29:48 +08:00

7.9 KiB
Raw Blame History

职位列表名称链接 + 关联人员 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: 在 list_posts 对本页职位批量查询 sys_user_post + sys_user,向每行附加只读 boundUserNames。前端职位名称用 el-button link,新增「关联人员」列全部展示并换行;关联用户保存后刷新列表。

Tech Stack: FastAPI / SQLAlchemy / Vue 3 / Element Plus / pytest

Spec: docs/superpowers/specs/2026-08-05-post-list-name-link-bound-users-design.md


File map

File Responsibility
backend/app/crud/post.py _row_to_dict 增加 boundUserNames;批量 enrichment;list_posts 调用
backend/tests/test_post_list_bound_users.py 空入参与展示名逻辑、列表 enrichment 调用回归
web-admin/src/types/api.d.ts PostItem.boundUserNames
web-admin/src/views/system/post/index.vue 名称链接、关联人员列、保存后刷新

Task 1: 后端批量查询关联人员姓名(TDD)

Files:

  • Create: backend/tests/test_post_list_bound_users.py

  • Modify: backend/app/crud/post.py

  • Step 1: Write failing tests

"""职位列表 boundUserNames enrichment。"""
import inspect

from app.crud import post as post_crud


class _DummySession:
    def query(self, *args, **kwargs):  # pragma: no cover
        raise AssertionError("空入参不应访问数据库")


def test_bound_user_names_by_post_ids_empty():
    db = _DummySession()
    assert post_crud._bound_user_names_by_post_ids(db, [], "zkxw") == {}
    assert post_crud._bound_user_names_by_post_ids(db, ["p1"], "") == {}


def test_user_display_name_prefers_nick():
    assert post_crud._user_display_name("张三", "zhangsan") == "张三"
    assert post_crud._user_display_name("", "zhangsan") == "zhangsan"
    assert post_crud._user_display_name("  ", "u1") == "u1"
    assert post_crud._user_display_name(None, None) == ""


def test_list_posts_enriches_bound_user_names():
    source = inspect.getsource(post_crud.list_posts)
    assert "_bound_user_names_by_post_ids" in source
    assert "bound_user_names" in source or "boundUserNames" in source
  • Step 2: Run tests — expect FAIL
cd backend && source venv/bin/activate && pytest tests/test_post_list_bound_users.py -v

Expected: FAIL(_bound_user_names_by_post_ids / _user_display_name 未定义)

  • Step 3: Implement helpers + list enrichment

在 backend/app/crud/post.py:

  1. Import SysUser, SysUserPost from app.models.models。
  2. 增加:
def _user_display_name(nick_name: Optional[str], user_no: Optional[str]) -> str:
    n = (nick_name or "").strip()
    if n:
        return n
    return (user_no or "").strip()


def _bound_user_names_by_post_ids(
    db: Session, post_ids: List[str], tenant_no: str
) -> Dict[str, List[str]]:
    """批量查询职位关联用户显示名(nick_name 优先,否则 user_no)。"""
    out: Dict[str, List[str]] = {}
    ids = [pid for pid in (post_ids or []) if pid]
    tenant = (tenant_no or "").strip()
    if not ids or not tenant:
        return out
    rows = (
        db.query(SysUserPost.post_id, SysUser.nick_name, SysUser.user_no)
        .join(
            SysUser,
            and_(
                SysUser.id == SysUserPost.user_id,
                SysUser.del_flag == 0,
            ),
        )
        .filter(
            and_(
                SysUserPost.post_id.in_(ids),
                SysUserPost.tenant_no == tenant,
                SysUserPost.del_flag == 0,
            )
        )
        .all()
    )
    for post_id, nick_name, user_no in rows:
        name = _user_display_name(nick_name, user_no)
        if not name:
            continue
        out.setdefault(post_id, []).append(name)
    return out
  1. 修改 _row_to_dict:
def _row_to_dict(p: SysPost, bound_user_names: Optional[List[str]] = None) -> Dict[str, Any]:
    return {
        "id": p.id,
        "postName": p.post_name,
        "postCode": p.post_code,
        "status": p.status,
        "tenantNo": p.tenant_no,
        "orderNum": p.order_num if p.order_num is not None else 0,
        "remark": p.remark or "",
        "boundUserNames": list(bound_user_names or []),
        "createTime": p.create_time.strftime("%Y-%m-%d %H:%M:%S") if p.create_time else "",
        "updateTime": p.update_time.strftime("%Y-%m-%d %H:%M:%S") if p.update_time else "",
    }
  1. 修改 list_posts 返回前:
    name_map = _bound_user_names_by_post_ids(
        db, [x.id for x in rows], tenant_no or ""
    )
    return [
        _row_to_dict(x, bound_user_names=name_map.get(x.id, [])) for x in rows
    ], int(total)
  • Step 4: Run tests — expect PASS
cd backend && source venv/bin/activate && pytest tests/test_post_list_bound_users.py -v
  • Step 5: Commit
git add backend/app/crud/post.py backend/tests/test_post_list_bound_users.py
git commit -m "feat(post): enrich post list with boundUserNames"

Task 2: 前端类型 + 列表 UI

Files:

  • Modify: web-admin/src/types/api.d.ts(Api.Post.PostItem)

  • Modify: web-admin/src/views/system/post/index.vue

  • Step 1: 扩展类型

在 PostItem 中增加:

boundUserNames?: string[]
  • Step 2: 职位名称改为链接

将「职位名称」列改为:

<el-table-column
  prop="postName"
  label="职位名称"
  min-width="140"
  align="center"
  header-align="center"
>
  <template #default="{ row }">
    <el-button link type="primary" @click="handleView(row)">
      {{ row.postName || '-' }}
    </el-button>
  </template>
</el-table-column>
  • Step 3: 新增「关联人员」列(编码与状态之间)
<el-table-column
  label="关联人员"
  min-width="180"
  align="center"
  header-align="center"
>
  <template #default="{ row }">
    <span class="post-bound-users-cell">{{
      (row.boundUserNames || []).length
        ? (row.boundUserNames || []).join('、')
        : '—'
    }}</span>
  </template>
</el-table-column>

scoped 样式:

.post-bound-users-cell {
  display: inline-block;
  max-width: 100%;
  white-space: normal;
  word-break: break-word;
  line-height: 1.45;
  text-align: center;
}
  • Step 4: 关联用户保存后刷新列表

在 handlePostBindSubmit 成功分支增加 await loadData():

await fetchUpdatePostBoundUsers(postBindPostId.value, [...postBindUserCheckedKeys.value])
postBindDialogVisible.value = false
await loadData()
  • Step 5: Commit
git add web-admin/src/types/api.d.ts web-admin/src/views/system/post/index.vue
git commit -m "feat(web-admin): post list name link and bound users column"

Task 3: 图谱更新与手工验收

  • Step 1: 更新 graphify
./scripts/update-graph.sh all
  • Step 2: 手工验收清单
  1. 打开 /#/system-manage/system/post。
  2. 点击职位名称 → 打开查看弹窗。
  3. 有关联用户的职位,「关联人员」列显示全部姓名;列窄时换行。
  4. 无关联用户显示 —。
  5. 「关联用户」保存后列表人名更新。
  • Step 3: Commit graphify(若有变更)
git add backend/graphify-out web-admin/graphify-out
git commit -m "chore: update graphify after post list enrichment"

Spec coverage

Spec 项 Task
名称快捷链接 → handleView Task 2
boundUserNames 列表 enrichment Task 1
全部姓名、换行 Task 2
关联用户保存后刷新 Task 2
Create/Update 不写该字段 无需改 API body(只读 enrichment)