diff --git a/yunke-server/app/api/routers/groups.py b/yunke-server/app/api/routers/groups.py index 0e823c1..0794290 100644 --- a/yunke-server/app/api/routers/groups.py +++ b/yunke-server/app/api/routers/groups.py @@ -101,14 +101,33 @@ async def list_groups( list_stmt = sa.select(ChatGroup) if conditions: list_stmt = list_stmt.where(*conditions) - list_stmt = ( - list_stmt.order_by( - sa.nullslast(ChatGroup.last_chat_time.desc()), - ChatGroup.room_id.asc(), + if (channel or "wechat") == "wechat": + # 云客「微信群统计」同款排序(2026-09-10 真机判别):员工分组—— + # 按云客员工列表序(wechat_account.list_order,NULL 垫底按 wx_id)、 + # 组内按 room_id(chatRoomId)字符串升序。WA 保持最近活跃优先。 + list_stmt = ( + list_stmt.outerjoin( + WechatAccount, + WechatAccount.wx_id == ChatGroup.owner_account_id, + ) + .order_by( + WechatAccount.list_order.is_(None), + WechatAccount.list_order.asc(), + ChatGroup.owner_account_id.asc(), + ChatGroup.room_id.asc(), + ) + .limit(page_size) + .offset((page - 1) * page_size) + ) + else: + list_stmt = ( + list_stmt.order_by( + sa.nullslast(ChatGroup.last_chat_time.desc()), + ChatGroup.room_id.asc(), + ) + .limit(page_size) + .offset((page - 1) * page_size) ) - .limit(page_size) - .offset((page - 1) * page_size) - ) rows = (await session.execute(list_stmt)).scalars().all() # ---- 员工微信/员工信息列(wechat_account/staff 关联,本页行集) ---- diff --git a/yunke-server/tests/test_api_groups.py b/yunke-server/tests/test_api_groups.py index d949a67..4522c12 100644 --- a/yunke-server/tests/test_api_groups.py +++ b/yunke-server/tests/test_api_groups.py @@ -64,17 +64,20 @@ async def _seed(session: AsyncSession) -> None: async def test_default_list_returns_all_ordered_desc(make_client): - """No filters -> 3 groups, ordered by ``last_chat_time desc`` (nulls last).""" + """No filters -> 3 groups;微信排序=员工分组(list_order NULL 按 owner asc) + +组内 room_id 字符串升序(云客群统计同款,2026-09-10)。 + seed 无 wechat_account 行 → list_order 全 NULL → owner 字符串序: + wa_owner_1 < wx_owner_1 < wx_owner_2 → [wa_room_x, room_a, room_b]。 + """ client = await make_client(_seed) r = await client.get("/api/groups") assert r.status_code == 200 body = r.json() assert body["total"] == 3 - # 2000, 1000, null -> [room_a, room_b, wa_room_x] assert [it["room_id"] for it in body["items"]] == [ + "wa_room_x@s.whatsapp.net", "room_a@chatroom", "room_b@chatroom", - "wa_room_x@s.whatsapp.net", ] for key in ( "room_id", diff --git a/yunkes/src/utils/groupFields.ts b/yunkes/src/utils/groupFields.ts index f3edc25..0266d30 100644 --- a/yunkes/src/utils/groupFields.ts +++ b/yunkes/src/utils/groupFields.ts @@ -105,7 +105,11 @@ export const GROUP_FIELD_GROUPS: GroupFieldGroup[] = [ ] /** Every key already surfaced via a group (so 其他 can skip duplicates). */ -const KNOWN_KEYS = new Set(GROUP_FIELD_GROUPS.flatMap((g) => g.keys)) +const KNOWN_KEYS = new Set([ + ...GROUP_FIELD_GROUPS.flatMap((g) => g.keys), + // 原始 wxid 串由群详情成员墙替代,不在任何区(含「其他」)显示(2026-09-10) + 'memberIds', +]) /** Resolve the 中文 label for a raw_json key (falls back to the key). */ export function fieldLabel(key: string): string {