Merge pull request #3 from sok-im/feature/user_common_group

Feature/user common group
pull/3727/head
haoyunlt 2 months ago committed by GitHub
commit 47821421d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -18,3 +18,4 @@ prometheus:
enableHistoryForNewMembers: true
commonGroupsLimitWithFriend: 3

@ -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

@ -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=

@ -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)
}

@ -210,6 +210,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
{

@ -19,6 +19,7 @@ import (
"fmt"
"math/big"
"math/rand"
"sort"
"strconv"
"strings"
"time"
@ -358,6 +359,68 @@ 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
}
// 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(total),
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")

@ -278,6 +278,7 @@ type Group struct {
} `mapstructure:"rpc"`
Prometheus Prometheus `mapstructure:"prometheus"`
EnableHistoryForNewMembers bool `mapstructure:"enableHistoryForNewMembers"`
CommonGroupsLimitWithFriend int `mapstructure:"commonGroupsLimitWithFriend"`
}
type Msg struct {

@ -0,0 +1,146 @@
#!/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:-5694418935}" # 当前登录用户(拿 token 的用户)
#FRIEND_USER_ID="${FRIEND_USER_ID:-1971806090}" # 要查询共同群的好友
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
DEBUG="${DEBUG:-1}" # DEBUG=1 打印请求/响应明细
# RecordNotFoundErrorerrCode=1004常见于 get_user_token
# 服务端会查用户是否存在user RPC GetDesignateUsers若 SELF_USER_ID 未注册,
# 返回空列表后 rpcli.firstValue 会包装为 ErrRecordNotFounderrDlt: 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() {
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 <<JSON
{
"userID": "${ADMIN_USER_ID}",
"secret": "${ADMIN_SECRET}"
}
JSON
)"
)
debug_log "admin raw resp: ${ADMIN_RESP}"
ADMIN_TOKEN="$(echo "${ADMIN_RESP}" | jq -r '.data.token // empty')"
debug_log "admin token parsed: ${ADMIN_TOKEN:-<empty>}"
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 <<JSON
{
"userID": "${SELF_USER_ID}",
"platformID": ${PLATFORM_ID}
}
JSON
)"
)
debug_log "user raw resp: ${USER_RESP}"
TOKEN="$(echo "${USER_RESP}" | jq -r '.data.token // empty')"
debug_log "user token parsed: ${TOKEN:-<empty>}"
fi
if [[ -z "${TOKEN}" ]]; then
echo "获取用户 token 失败,响应如下:"
print_json_safe "${USER_RESP:-}"
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
OP_ID="op_$(date +%s)"
# 2) 调共同群接口
echo "查询共同群: self=${SELF_USER_ID}, friend=${FRIEND_USER_ID}"
REQ_BODY="$(cat <<JSON
{
"friendUserID": "${FRIEND_USER_ID}"
}
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_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 404Gin 未匹配到路由,通常表示当前运行的 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
Loading…
Cancel
Save