From f9d3221df095567fd09880ecce910cabe03559d5 Mon Sep 17 00:00:00 2001 From: hawklin2017 <32898629+hawklin2017@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:28:03 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=85=B1=E5=90=8C?= =?UTF-8?q?=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 + go.sum | 2 - internal/api/group.go | 4 ++ internal/api/router.go | 1 + internal/rpc/group/group.go | 44 ++++++++++++++ scripts/get_common_group.sh | 112 ++++++++++++++++++++++++++++++++++++ 6 files changed, 163 insertions(+), 2 deletions(-) create mode 100755 scripts/get_common_group.sh diff --git a/go.mod b/go.mod index 3e996b857..6b7bf7006 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/openimsdk/open-im-server/v3 go 1.25.0 +replace github.com/openimsdk/protocol => ../protocol + require ( firebase.google.com/go/v4 v4.14.1 github.com/dtm-labs/rockscache v0.1.1 diff --git a/go.sum b/go.sum index e9677acb9..c0aa7a720 100644 --- a/go.sum +++ b/go.sum @@ -354,8 +354,6 @@ github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/openimsdk/gomake v0.0.17 h1:q8haP48VOH45WhJRiLj1YSBJyUFJqD8CTedH65i1YH8= github.com/openimsdk/gomake v0.0.17/go.mod h1:nnjS8yCtrPJAt1knMbyPiUwCH2gpyBzj/EZAONfUOXg= -github.com/openimsdk/protocol v0.0.73-alpha.12 h1:2NYawXeHChYUeSme6QJ9pOLh+Empce2WmwEtbP4JvKk= -github.com/openimsdk/protocol v0.0.73-alpha.12/go.mod h1:WF7EuE55vQvpyUAzDXcqg+B+446xQyEba0X35lTINmw= github.com/openimsdk/tools v0.0.50-alpha.113 h1:rhLWaSJuhjgJFNVzmpChLCG7dPXS0+bte+CPI0008Us= github.com/openimsdk/tools v0.0.50-alpha.113/go.mod h1:x9i/e+WJFW4tocy6RNJQ9NofQiP3KJ1Y576/06TqOG4= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= diff --git a/internal/api/group.go b/internal/api/group.go index 926d19a8a..9a2ffda06 100644 --- a/internal/api/group.go +++ b/internal/api/group.go @@ -169,3 +169,7 @@ func (o *GroupApi) GetFullJoinGroupIDs(c *gin.Context) { func (o *GroupApi) GetGroupApplicationUnhandledCount(c *gin.Context) { a2r.Call(c, group.GroupClient.GetGroupApplicationUnhandledCount, o.Client) } + +func (o *GroupApi) GetCommonGroupsWithFriend(c *gin.Context) { + a2r.Call(c, group.GroupClient.GetCommonGroupsWithFriend, o.Client) +} diff --git a/internal/api/router.go b/internal/api/router.go index bad891fda..896d0b558 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -187,6 +187,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co groupRouterGroup.POST("/get_full_group_member_user_ids", g.GetFullGroupMemberUserIDs) groupRouterGroup.POST("/get_full_join_group_ids", g.GetFullJoinGroupIDs) groupRouterGroup.POST("/get_group_application_unhandled_count", g.GetGroupApplicationUnhandledCount) + groupRouterGroup.POST("/get_common_groups_with_friend", g.GetCommonGroupsWithFriend) } // certificate { diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 778316e5c..3f97927f3 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -358,6 +358,50 @@ func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbgroup.GetJo return &resp, nil } +func (g *groupServer) GetCommonGroupsWithFriend(ctx context.Context, req *pbgroup.GetCommonGroupsWithFriendReq) (*pbgroup.GetCommonGroupsWithFriendResp, error) { + if req.FriendUserID == "" { + return nil, errs.ErrArgs.WrapMsg("friendUserID empty") + } + opUserID := mcontext.GetOpUserID(ctx) + if opUserID == "" { + return nil, errs.ErrNoPermission.WrapMsg("op user id empty") + } + + selfGroupIDs, err := g.db.FindJoinGroupID(ctx, opUserID) + if err != nil { + return nil, err + } + if len(selfGroupIDs) == 0 { + return &pbgroup.GetCommonGroupsWithFriendResp{ + Total: 0, + Groups: []*sdkws.GroupInfo{}, + }, nil + } + + friendMembers, err := g.db.FindGroupMemberUser(ctx, selfGroupIDs, req.FriendUserID) + if err != nil { + return nil, err + } + if len(friendMembers) == 0 { + return &pbgroup.GetCommonGroupsWithFriendResp{ + Total: 0, + Groups: []*sdkws.GroupInfo{}, + }, nil + } + + commonGroupIDs := datautil.Distinct(datautil.Slice(friendMembers, func(e *model.GroupMember) string { + return e.GroupID + })) + groups, err := g.getGroupsInfo(ctx, commonGroupIDs) + if err != nil { + return nil, err + } + return &pbgroup.GetCommonGroupsWithFriendResp{ + Total: uint32(len(groups)), + Groups: groups, + }, nil +} + func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.InviteUserToGroupReq) (*pbgroup.InviteUserToGroupResp, error) { if len(req.InvitedUserIDs) == 0 { return nil, errs.ErrArgs.WrapMsg("user empty") diff --git a/scripts/get_common_group.sh b/scripts/get_common_group.sh new file mode 100755 index 000000000..bf98e82b9 --- /dev/null +++ b/scripts/get_common_group.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ====== 按需修改 ====== +API_BASE="${API_BASE:-http://127.0.0.1:10002}" # 你的 open-im-api 地址 +SELF_USER_ID="${SELF_USER_ID:-3932647710}" # 当前登录用户(拿 token 的用户) +#FRIEND_USER_ID="${FRIEND_USER_ID:-4391832441}" # 要查询共同群的好友 +FRIEND_USER_ID="${FRIEND_USER_ID:-9607566286}" # 要查询共同群的好友 +PLATFORM_ID="${PLATFORM_ID:-2}" # 1=iOS, 2=Android, 3=Windows... +ADMIN_USER_ID="${ADMIN_USER_ID:-imAdmin}" # 管理员账号(用于签发用户 token) +ADMIN_SECRET="${ADMIN_SECRET:-openIM123}" # 配置中的 share.secret +DEBUG="${DEBUG:-0}" # DEBUG=1 打印请求/响应明细 +# ===================== + +debug_log() { + if [[ "${DEBUG}" == "1" ]]; then + echo "[DEBUG] $*" + fi +} + +print_json_safe() { + local raw="${1:-}" + if echo "${raw}" | jq -e . >/dev/null 2>&1; then + echo "${raw}" | jq . + else + echo "${raw}" + fi +} + +# 1) 先拿 user token(如果你已有 token,可跳过这一步,直接 export TOKEN=xxx) +if [[ -z "${TOKEN:-}" ]]; then + if [[ -z "${ADMIN_SECRET}" ]]; then + echo "缺少 ADMIN_SECRET,请先导出:export ADMIN_SECRET='你的share.secret'" + exit 1 + fi + + echo "获取管理员 token: ${ADMIN_USER_ID}" + OP_ID_ADMIN="op_admin_$(date +%s)" + debug_log "POST ${API_BASE}/auth/get_admin_token" + debug_log "operationID: ${OP_ID_ADMIN}" + debug_log "admin req body: {\"userID\":\"${ADMIN_USER_ID}\",\"secret\":\"***\"}" + ADMIN_RESP=$( + curl -sS -X POST "${API_BASE}/auth/get_admin_token" \ + -H 'Content-Type: application/json' \ + -H "operationID: ${OP_ID_ADMIN}" \ + -d "$(cat <}" + if [[ -z "${ADMIN_TOKEN}" ]]; then + echo "获取管理员 token 失败,响应如下:" + print_json_safe "${ADMIN_RESP}" + exit 1 + fi + + echo "获取用户 token: ${SELF_USER_ID}" + OP_ID_USER="op_user_$(date +%s)" + debug_log "POST ${API_BASE}/auth/get_user_token" + debug_log "operationID: ${OP_ID_USER}" + debug_log "user req body: {\"userID\":\"${SELF_USER_ID}\",\"platformID\":${PLATFORM_ID}}" + USER_RESP=$( + curl -sS -X POST "${API_BASE}/auth/get_user_token" \ + -H 'Content-Type: application/json' \ + -H "operationID: ${OP_ID_USER}" \ + -H "token: ${ADMIN_TOKEN}" \ + -d "$(cat <}" +fi + +if [[ -z "${TOKEN}" ]]; then + echo "获取用户 token 失败,响应如下:" + print_json_safe "${USER_RESP:-}" + echo "提示:请确认 SELF_USER_ID 用户已注册存在,或手动传入 TOKEN 后重试。" + exit 1 +fi + +OP_ID="op_$(date +%s)" + +# 2) 调共同群接口 +echo "查询共同群: self=${SELF_USER_ID}, friend=${FRIEND_USER_ID}" +REQ_BODY="$(cat < Date: Sat, 28 Mar 2026 21:51:54 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=85=B1=E5=90=8C?= =?UTF-8?q?=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/get_common_group.sh | 54 ++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/scripts/get_common_group.sh b/scripts/get_common_group.sh index bf98e82b9..5936ec612 100755 --- a/scripts/get_common_group.sh +++ b/scripts/get_common_group.sh @@ -3,13 +3,21 @@ set -euo pipefail # ====== 按需修改 ====== API_BASE="${API_BASE:-http://127.0.0.1:10002}" # 你的 open-im-api 地址 -SELF_USER_ID="${SELF_USER_ID:-3932647710}" # 当前登录用户(拿 token 的用户) -#FRIEND_USER_ID="${FRIEND_USER_ID:-4391832441}" # 要查询共同群的好友 -FRIEND_USER_ID="${FRIEND_USER_ID:-9607566286}" # 要查询共同群的好友 +SELF_USER_ID="${SELF_USER_ID:-4642714021}" # 当前登录用户(拿 token 的用户) +#FRIEND_USER_ID="${FRIEND_USER_ID:-1971806090}" # 要查询共同群的好友 +FRIEND_USER_ID="${FRIEND_USER_ID:-3870738564}" # 要查询共同群的好友 PLATFORM_ID="${PLATFORM_ID:-2}" # 1=iOS, 2=Android, 3=Windows... ADMIN_USER_ID="${ADMIN_USER_ID:-imAdmin}" # 管理员账号(用于签发用户 token) ADMIN_SECRET="${ADMIN_SECRET:-openIM123}" # 配置中的 share.secret -DEBUG="${DEBUG:-0}" # DEBUG=1 打印请求/响应明细 +DEBUG="${DEBUG:-1}" # DEBUG=1 打印请求/响应明细 +# RecordNotFoundError(errCode=1004)常见于 get_user_token: +# 服务端会查用户是否存在(user RPC GetDesignateUsers);若 SELF_USER_ID 未注册, +# 返回空列表后 rpcli.firstValue 会包装为 ErrRecordNotFound(errDlt: record not found)。 +# 处理:先注册该用户,或 export SELF_USER_ID=已存在用户,或 export TOKEN=已有用户 token 跳过拉 token。 +# +# HTTP 404 + 响应体 "404 page not found"(Gin):当前连上的 API 进程路由表里没有该路径。 +# 本仓库已注册 POST /group/get_common_groups_with_friend(见 internal/api/router.go)。 +# 处理:用当前代码重新编译/替换镜像并重启 openim-api,或确认 API_BASE 指向的就是带该路由的实例(无错误路径前缀/反代截断)。 # ===================== debug_log() { @@ -86,7 +94,15 @@ fi if [[ -z "${TOKEN}" ]]; then echo "获取用户 token 失败,响应如下:" print_json_safe "${USER_RESP:-}" - echo "提示:请确认 SELF_USER_ID 用户已注册存在,或手动传入 TOKEN 后重试。" + USER_ERR_CODE="$(echo "${USER_RESP:-}" | jq -r '.errCode // empty')" + if [[ "${USER_ERR_CODE}" == "1004" ]]; then + echo "" + echo "【排查】errCode 1004 (RecordNotFoundError):当前请求的 userID 在用户库中不存在。" + echo " - 服务端路径:auth GetUserToken → user GetDesignateUsers → 未命中则空结果 → record not found" + echo " - 请先将 SELF_USER_ID=${SELF_USER_ID} 注册进系统,或改用已存在用户,或: export TOKEN='你的用户token'" + else + echo "提示:请确认 SELF_USER_ID 已注册、ADMIN_SECRET 与部署一致,或手动 export TOKEN 后重试。" + fi exit 1 fi @@ -103,10 +119,28 @@ JSON debug_log "POST ${API_BASE}/group/get_common_groups_with_friend" debug_log "operationID: ${OP_ID}" debug_log "group req body: ${REQ_BODY}" -GROUP_RESP="$(curl -sS -X POST "${API_BASE}/group/get_common_groups_with_friend" \ - -H 'Content-Type: application/json' \ - -H "token: ${TOKEN}" \ - -H "operationID: ${OP_ID}" \ - -d "${REQ_BODY}")" +GROUP_BODY="$(mktemp)" +GROUP_HTTP_CODE="$( + curl -sS -o "${GROUP_BODY}" -w "%{http_code}" -X POST "${API_BASE}/group/get_common_groups_with_friend" \ + -H 'Content-Type: application/json' \ + -H "token: ${TOKEN}" \ + -H "operationID: ${OP_ID}" \ + -d "${REQ_BODY}" +)" +GROUP_RESP="$(cat "${GROUP_BODY}")" +rm -f "${GROUP_BODY}" +debug_log "group HTTP status: ${GROUP_HTTP_CODE}" debug_log "group raw resp: ${GROUP_RESP}" print_json_safe "${GROUP_RESP}" +if [[ "${GROUP_HTTP_CODE}" == "404" ]] || [[ "${GROUP_RESP}" == "404 page not found" ]]; then + echo "" + echo "【排查】HTTP 404:Gin 未匹配到路由,通常表示当前运行的 openim-api 版本过旧,不含 get_common_groups_with_friend。" + echo " - 期望路径: POST ${API_BASE}/group/get_common_groups_with_friend" + echo " - 请用本仓库代码重新构建并重启 API,或核对 API_BASE / 网关是否多删、少拼了路径前缀。" + exit 1 +fi +if [[ "${GROUP_HTTP_CODE}" != "200" ]]; then + echo "" + echo "【提示】HTTP 状态码: ${GROUP_HTTP_CODE}(非 200),请结合响应体与网关/鉴权配置排查。" + exit 1 +fi From 98b6b249ee12be789596692079d9930f9625bec7 Mon Sep 17 00:00:00 2001 From: hawklin2017 <32898629+hawklin2017@users.noreply.github.com> Date: Wed, 1 Apr 2026 16:41:43 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=85=B1=E5=90=8C?= =?UTF-8?q?=E6=89=80=E5=9C=A8=E7=9A=84=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/openim-rpc-group.yml | 1 + internal/rpc/group/group.go | 21 ++++++++++++++++++++- pkg/common/config/config.go | 1 + scripts/get_common_group.sh | 4 ++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/config/openim-rpc-group.yml b/config/openim-rpc-group.yml index a8c2d5ec1..0f42f7661 100644 --- a/config/openim-rpc-group.yml +++ b/config/openim-rpc-group.yml @@ -18,3 +18,4 @@ prometheus: enableHistoryForNewMembers: true +commonGroupsLimitWithFriend: 3 diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 3f97927f3..c7880d360 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -19,6 +19,7 @@ import ( "fmt" "math/big" "math/rand" + "sort" "strconv" "strings" "time" @@ -371,6 +372,7 @@ func (g *groupServer) GetCommonGroupsWithFriend(ctx context.Context, req *pbgrou if err != nil { return nil, err } + if len(selfGroupIDs) == 0 { return &pbgroup.GetCommonGroupsWithFriendResp{ Total: 0, @@ -382,6 +384,7 @@ func (g *groupServer) GetCommonGroupsWithFriend(ctx context.Context, req *pbgrou if err != nil { return nil, err } + if len(friendMembers) == 0 { return &pbgroup.GetCommonGroupsWithFriendResp{ Total: 0, @@ -392,12 +395,28 @@ func (g *groupServer) GetCommonGroupsWithFriend(ctx context.Context, req *pbgrou commonGroupIDs := datautil.Distinct(datautil.Slice(friendMembers, func(e *model.GroupMember) string { return e.GroupID })) + groups, err := g.getGroupsInfo(ctx, commonGroupIDs) if err != nil { return nil, err } + + // Keep response deterministic by sorting common groups with member count descending. + sort.SliceStable(groups, func(i, j int) bool { + return groups[i].MemberCount > groups[j].MemberCount + }) + total := len(groups) + + limit := g.config.RpcConfig.CommonGroupsLimitWithFriend + if limit <= 0 { + limit = 3 + } + if len(groups) > limit { + groups = groups[:limit] + } + return &pbgroup.GetCommonGroupsWithFriendResp{ - Total: uint32(len(groups)), + Total: uint32(total), Groups: groups, }, nil } diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 4cd202db4..df92eed4c 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -278,6 +278,7 @@ type Group struct { } `mapstructure:"rpc"` Prometheus Prometheus `mapstructure:"prometheus"` EnableHistoryForNewMembers bool `mapstructure:"enableHistoryForNewMembers"` + CommonGroupsLimitWithFriend int `mapstructure:"commonGroupsLimitWithFriend"` } type Msg struct { diff --git a/scripts/get_common_group.sh b/scripts/get_common_group.sh index 5936ec612..8fc0ac22f 100755 --- a/scripts/get_common_group.sh +++ b/scripts/get_common_group.sh @@ -3,9 +3,9 @@ set -euo pipefail # ====== 按需修改 ====== API_BASE="${API_BASE:-http://127.0.0.1:10002}" # 你的 open-im-api 地址 -SELF_USER_ID="${SELF_USER_ID:-4642714021}" # 当前登录用户(拿 token 的用户) +SELF_USER_ID="${SELF_USER_ID:-5694418935}" # 当前登录用户(拿 token 的用户) #FRIEND_USER_ID="${FRIEND_USER_ID:-1971806090}" # 要查询共同群的好友 -FRIEND_USER_ID="${FRIEND_USER_ID:-3870738564}" # 要查询共同群的好友 +FRIEND_USER_ID="${FRIEND_USER_ID:-1011009748}" # 要查询共同群的好友 PLATFORM_ID="${PLATFORM_ID:-2}" # 1=iOS, 2=Android, 3=Windows... ADMIN_USER_ID="${ADMIN_USER_ID:-imAdmin}" # 管理员账号(用于签发用户 token) ADMIN_SECRET="${ADMIN_SECRET:-openIM123}" # 配置中的 share.secret