├── .github └── go.yml ├── .gitignore ├── README.md ├── app ├── conversation │ ├── client │ │ ├── conversationservice │ │ │ └── conversationService.go │ │ ├── friendservice │ │ │ └── friendService.go │ │ ├── groupservice │ │ │ └── groupService.go │ │ └── subscriptionservice │ │ │ └── subscriptionService.go │ ├── conversation.go │ ├── conversationmodel │ │ └── conversationmember.go │ ├── friendmodel │ │ ├── friend.go │ │ └── friendapplyrecord.go │ ├── groupmodel │ │ ├── group.go │ │ └── groupsubscribe.go │ ├── internal │ │ ├── config │ │ │ └── config.go │ │ ├── logic │ │ │ ├── conversationservice │ │ │ │ ├── conversationSettingUpdateLogic.go │ │ │ │ └── listJoinedConversationsLogic.go │ │ │ ├── friendservice │ │ │ │ ├── countFriendLogic.go │ │ │ │ ├── friendApplyHandleLogic.go │ │ │ │ ├── friendApplyLogic.go │ │ │ │ └── listFriendApplyLogic.go │ │ │ ├── groupservice │ │ │ │ ├── countCreateGroupLogic.go │ │ │ │ ├── countJoinGroupLogic.go │ │ │ │ ├── groupCreateLogic.go │ │ │ │ ├── groupSubscribeLogic.go │ │ │ │ └── listGroupSubscribersLogic.go │ │ │ └── subscriptionservice │ │ │ │ ├── deleteUserSubscriptionLogic.go │ │ │ │ ├── listSubscriptionSubscribersLogic.go │ │ │ │ ├── subscriptionAfterOfflineLogic.go │ │ │ │ ├── subscriptionAfterOnlineLogic.go │ │ │ │ ├── subscriptionSubscribeLogic.go │ │ │ │ └── upsertUserSubscriptionLogic.go │ │ ├── server │ │ │ ├── conversationservice │ │ │ │ └── conversationServiceServer.go │ │ │ ├── friendservice │ │ │ │ └── friendServiceServer.go │ │ │ ├── groupservice │ │ │ │ └── groupServiceServer.go │ │ │ └── subscriptionservice │ │ │ │ └── subscriptionServiceServer.go │ │ └── svc │ │ │ └── serviceContext.go │ ├── pb │ │ ├── common.proto │ │ └── conversation.proto │ └── subscriptionmodel │ │ ├── subscription.go │ │ ├── subscriptionsubscribe.go │ │ └── usersubscription.go ├── gateway │ ├── README.md │ ├── client │ │ └── gatewayservice │ │ │ ├── client.go │ │ │ └── gatewayService.go │ ├── cmd │ │ └── main.go │ ├── gateway.go │ ├── internal │ │ ├── config │ │ │ └── config.go │ │ ├── handler │ │ │ ├── connectionhandler │ │ │ │ └── connectionhandler.go │ │ │ ├── offerhandler.go │ │ │ ├── route.go │ │ │ ├── unifiedhandler.go │ │ │ └── wshandler.go │ │ ├── logic │ │ │ └── gatewayservice │ │ │ │ ├── authenticationConnectionLogic.go │ │ │ │ ├── gatewayBatchGetUserConnectionLogic.go │ │ │ │ ├── gatewayGetConnectionByFilterLogic.go │ │ │ │ ├── gatewayGetUserConnectionLogic.go │ │ │ │ ├── gatewayKeepAliveLogic.go │ │ │ │ ├── gatewayKickWsLogic.go │ │ │ │ ├── gatewayWriteDataToWsLogic.go │ │ │ │ ├── gatewayWriteDataToWsWrapperLogic.go │ │ │ │ ├── verifyConnectionLogic.go │ │ │ │ ├── x_connection.go │ │ │ │ ├── x_connection_p2p.go │ │ │ │ └── x_connection_ws.go │ │ ├── middleware │ │ │ ├── aes.go │ │ │ ├── apilog.go │ │ │ ├── cors.go │ │ │ ├── logger.go │ │ │ └── tracing.go │ │ ├── server │ │ │ ├── gatewayservice │ │ │ │ └── gatewayServiceServer.go │ │ │ ├── httpServer.go │ │ │ └── rtcServer.go │ │ ├── svc │ │ │ └── serviceContext.go │ │ └── types │ │ │ ├── loogConnection.go │ │ │ └── response.go │ └── pb │ │ ├── common.proto │ │ └── gateway.proto ├── message │ ├── README.md │ ├── client │ │ ├── messageservice │ │ │ └── messageService.go │ │ └── noticeservice │ │ │ └── noticeService.go │ ├── internal │ │ ├── config │ │ │ └── config.go │ │ ├── logic │ │ │ ├── messageservice │ │ │ │ ├── messageBatchSendLogic.go │ │ │ │ ├── messageInsertLogic.go │ │ │ │ ├── messagePushLogic.go │ │ │ │ └── messageSendLogic.go │ │ │ └── noticeservice │ │ │ │ ├── listNoticeLogic.go │ │ │ │ ├── noticeBatchSendLogic.go │ │ │ │ ├── noticeSendLogic.go │ │ │ │ └── x_consumer.go │ │ ├── server │ │ │ ├── consumerServer.go │ │ │ ├── messageservice │ │ │ │ └── messageServiceServer.go │ │ │ └── noticeservice │ │ │ │ └── noticeServiceServer.go │ │ └── svc │ │ │ └── serviceContext.go │ ├── message.go │ ├── messagemodel │ │ ├── message.go │ │ ├── message_test.go │ │ └── redisseq.go │ ├── noticemodel │ │ ├── broadcastnotice.go │ │ └── subscriptionnotice.go │ └── pb │ │ ├── common.proto │ │ └── message.proto ├── third │ ├── README.md │ ├── client │ │ ├── captchaservice │ │ │ └── captchaService.go │ │ ├── emailservice │ │ │ └── emailService.go │ │ └── smsservice │ │ │ └── smsService.go │ ├── internal │ │ ├── config │ │ │ └── config.go │ │ ├── logic │ │ │ ├── captchaservice │ │ │ │ ├── captchaVerifyLogic.go │ │ │ │ └── getCaptchaLogic.go │ │ │ ├── emailservice │ │ │ │ ├── emailCodeSendLogic.go │ │ │ │ └── emailCodeVerifyLogic.go │ │ │ └── smsservice │ │ │ │ ├── smsCodeSendLogic.go │ │ │ │ └── smsCodeVerifyLogic.go │ │ ├── server │ │ │ ├── captchaservice │ │ │ │ └── captchaServiceServer.go │ │ │ ├── emailservice │ │ │ │ └── emailServiceServer.go │ │ │ └── smsservice │ │ │ │ └── smsServiceServer.go │ │ └── svc │ │ │ └── serviceContext.go │ ├── pb │ │ ├── common.proto │ │ └── third.proto │ └── third.go ├── user │ ├── README.md │ ├── client │ │ ├── accountservice │ │ │ └── accountService.go │ │ ├── callbackservice │ │ │ └── callbackService.go │ │ └── infoservice │ │ │ └── infoService.go │ ├── internal │ │ ├── config │ │ │ └── config.go │ │ ├── logic │ │ │ ├── accountservice │ │ │ │ ├── createRobotLogic.go │ │ │ │ ├── refreshUserAccessTokenLogic.go │ │ │ │ ├── resetUserAccountMapLogic.go │ │ │ │ ├── revokeUserAccessTokenLogic.go │ │ │ │ ├── updateUserAccountMapLogic.go │ │ │ │ ├── userAccessTokenLogic.go │ │ │ │ ├── userDestroyLogic.go │ │ │ │ └── userRegisterLogic.go │ │ │ ├── callbackservice │ │ │ │ ├── userAfterKeepAliveLogic.go │ │ │ │ ├── userAfterOfflineLogic.go │ │ │ │ ├── userAfterOnlineLogic.go │ │ │ │ ├── userAfterRegisterLogic.go │ │ │ │ ├── userBeforeConnectLogic.go │ │ │ │ └── userBeforeRequestLogic.go │ │ │ └── infoservice │ │ │ │ ├── getSelfUserInfoLogic.go │ │ │ │ ├── getUserInfoLogic.go │ │ │ │ ├── getUserModelByIdLogic.go │ │ │ │ ├── getUserModelByIdsLogic.go │ │ │ │ ├── updateUserCountMapLogic.go │ │ │ │ ├── updateUserExtraMapLogic.go │ │ │ │ └── updateUserProfileMapLogic.go │ │ ├── server │ │ │ ├── accountservice │ │ │ │ └── accountServiceServer.go │ │ │ ├── callbackservice │ │ │ │ └── callbackServiceServer.go │ │ │ ├── consumerServer.go │ │ │ └── infoservice │ │ │ │ └── infoServiceServer.go │ │ └── svc │ │ │ └── serviceContext.go │ ├── pb │ │ ├── common.proto │ │ └── user.proto │ ├── user.go │ └── usermodel │ │ ├── user.go │ │ └── usersetting.go └── world │ ├── client │ └── worldservice │ │ └── worldService.go │ ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ └── worldservice │ │ │ └── worldPostSubmitLogic.go │ ├── server │ │ └── worldservice │ │ │ └── worldServiceServer.go │ └── svc │ │ └── serviceContext.go │ ├── pb │ ├── common.proto │ └── world.proto │ └── world.go ├── common ├── i18n │ └── i18n.go ├── pb │ ├── .gitignore │ ├── common.pb.go │ ├── common.proto │ ├── conversation.pb.go │ ├── conversation.proto │ ├── conversation_grpc.pb.go │ ├── gateway.pb.go │ ├── gateway.proto │ ├── gateway_grpc.pb.go │ ├── goctl.sh │ ├── message.pb.go │ ├── message.proto │ ├── message_grpc.pb.go │ ├── sdk.sh │ ├── third.pb.go │ ├── third.proto │ ├── third_grpc.pb.go │ ├── user.pb.go │ ├── user.proto │ ├── user_grpc.pb.go │ ├── world.pb.go │ ├── world.proto │ ├── world_grpc.pb.go │ ├── xx.common.go │ ├── xx.implement.go │ ├── xx.message.go │ ├── xx.third.go │ └── xx.user.go ├── utils │ ├── aes.go │ ├── aes_test.go │ ├── any.go │ ├── any_test.go │ ├── bytes.go │ ├── ecdh.go │ ├── ecdh │ │ └── main.go │ ├── ecdh_test.go │ ├── error.go │ ├── file.go │ ├── gorm.go │ ├── http.go │ ├── json.go │ ├── jwt.go │ ├── map.go │ ├── mongo.go │ ├── mq.go │ ├── number.go │ ├── proto.go │ ├── pwd.go │ ├── reandom.go │ ├── reandom_test.go │ ├── redis.go │ ├── regex.go │ ├── retry.go │ ├── sdp.go │ ├── snowflake.go │ ├── string.go │ ├── string_test.go │ ├── time.go │ ├── trace.go │ └── utils.go ├── values.go ├── xcache │ ├── goredis.go │ ├── lock.go │ ├── redis.go │ └── rediskey.go ├── xconf │ └── load.go ├── xmgo │ ├── index.go │ ├── mongo.go │ ├── utils.go │ └── utils_test.go ├── xmq │ ├── asynq.go │ ├── topic.go │ └── types.go └── xorm │ └── mysql.go ├── deploy ├── .gitignore └── local_xxim │ ├── .gitignore │ ├── README.md │ └── docker-compose.yaml ├── go.mod ├── go.sum └── sdk ├── client ├── client.go ├── config.go ├── friend.go ├── friend_test.go ├── gateway.go ├── gateway_test.go ├── group.go ├── group_test.go ├── message_test.go ├── nessage.go ├── notice.go ├── notice_test.go ├── user.go └── user_test.go ├── store ├── config.go └── sqlite.go └── types └── interface.go /.github/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ cloudx ] 6 | pull_request: 7 | branches: [ cloudx ] 8 | 9 | jobs: 10 | test-linux: 11 | name: Linux 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Set up Go 1.x 15 | uses: actions/setup-go@v3 16 | with: 17 | go-version: ^1.19 18 | id: go 19 | 20 | - name: Check out code into the Go module directory 21 | uses: actions/checkout@v3 22 | 23 | - name: Get dependencies 24 | run: | 25 | go get -v -t -d ./... 26 | 27 | - name: Lint 28 | run: | 29 | go vet -stdmethods=false $(go list ./...) 30 | go install mvdan.cc/gofumpt@latest 31 | test -z "$(gofumpt -l -extra .)" || echo "Please run 'gofumpt -l -w -extra .'" 32 | 33 | - name: Test 34 | run: go test -race -coverprofile=coverage.txt -covermode=atomic ./... 35 | 36 | - name: Codecov 37 | uses: codecov/codecov-action@v3 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | .idea/ 17 | *.png 18 | *.jpg 19 | *.jpeg 20 | *.gif 21 | *.bmp 22 | *.ico 23 | *.svg 24 | 25 | *local*.* 26 | *.log 27 | *.log.* 28 | 29 | *.xdb 30 | other 31 | *etc 32 | *.db 33 | logs 34 | -------------------------------------------------------------------------------- /app/conversation/conversation.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/zeromicro/go-zero/core/logx" 6 | 7 | "github.com/cherish-chat/xxim-server/app/conversation/internal/config" 8 | conversationserviceServer "github.com/cherish-chat/xxim-server/app/conversation/internal/server/conversationservice" 9 | friendserviceServer "github.com/cherish-chat/xxim-server/app/conversation/internal/server/friendservice" 10 | groupserviceServer "github.com/cherish-chat/xxim-server/app/conversation/internal/server/groupservice" 11 | subscriptionserviceServer "github.com/cherish-chat/xxim-server/app/conversation/internal/server/subscriptionservice" 12 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 13 | "github.com/cherish-chat/xxim-server/common/pb" 14 | 15 | "github.com/zeromicro/go-zero/core/conf" 16 | "github.com/zeromicro/go-zero/core/service" 17 | "github.com/zeromicro/go-zero/zrpc" 18 | "google.golang.org/grpc" 19 | "google.golang.org/grpc/reflection" 20 | ) 21 | 22 | var configFile = flag.String("f", "etc/conversation.yaml", "the config file") 23 | 24 | func main() { 25 | flag.Parse() 26 | 27 | var c config.Config 28 | conf.MustLoad(*configFile, &c) 29 | ctx := svc.NewServiceContext(c) 30 | 31 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 32 | pb.RegisterGroupServiceServer(grpcServer, groupserviceServer.NewGroupServiceServer(ctx)) 33 | pb.RegisterFriendServiceServer(grpcServer, friendserviceServer.NewFriendServiceServer(ctx)) 34 | pb.RegisterConversationServiceServer(grpcServer, conversationserviceServer.NewConversationServiceServer(ctx)) 35 | pb.RegisterSubscriptionServiceServer(grpcServer, subscriptionserviceServer.NewSubscriptionServiceServer(ctx)) 36 | 37 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 38 | reflection.Register(grpcServer) 39 | } 40 | }) 41 | defer s.Stop() 42 | 43 | logx.Infof("Starting rpc server at %s...\n", c.ListenOn) 44 | s.Start() 45 | } 46 | -------------------------------------------------------------------------------- /app/conversation/conversationmodel/conversationmember.go: -------------------------------------------------------------------------------- 1 | package conversationmodel 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | "github.com/qiniu/qmgo" 6 | opts "github.com/qiniu/qmgo/options" 7 | "github.com/zeromicro/go-zero/core/stores/redis" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/bson/primitive" 10 | "go.mongodb.org/mongo-driver/mongo/options" 11 | ) 12 | 13 | type ConversationType = pb.ConversationType 14 | type ConversationSettingKey = pb.ConversationSettingKey 15 | 16 | type ConversationMember struct { 17 | // ConversationId 群组ID 18 | ConversationId string `bson:"conversationId"` 19 | // ConversationType 会话类型 20 | ConversationType ConversationType `json:"conversationType"` 21 | // MemberUserId 群成员ID 22 | MemberUserId string `bson:"memberUserId"` 23 | // JoinTime 加入时间 24 | JoinTime primitive.DateTime `bson:"joinTime"` 25 | // JoinSource 加入来源 26 | JoinSource bson.M `bson:"joinSource,omitempty"` 27 | // Settings 群成员设置 28 | Settings bson.M ` bson:"settings,omitempty"` 29 | } 30 | 31 | func (m *ConversationMember) TableName() string { 32 | return "conversation_member" 33 | } 34 | 35 | // GetIndexes 索引 36 | func (m *ConversationMember) GetIndexes() []opts.IndexModel { 37 | return []opts.IndexModel{{ 38 | Key: []string{"conversationId", "conversationType", "memberUserId"}, 39 | IndexOptions: options.Index().SetName("unique_conversation_member").SetUnique(true), 40 | }, { 41 | Key: []string{"conversationId"}, 42 | }, { 43 | Key: []string{"memberUserId"}, 44 | }, { 45 | Key: []string{"memberUserId", "conversationType"}, 46 | }, { 47 | Key: []string{"joinTime"}, 48 | }, { 49 | Key: []string{"settings." + pb.ConversationSettingKey_IsBlocked.String()}, 50 | }, { 51 | Key: []string{"settings." + pb.ConversationSettingKey_IsMuted.String()}, 52 | }} 53 | } 54 | 55 | type xConversationMemberModel struct { 56 | coll *qmgo.QmgoClient 57 | rc *redis.Redis 58 | } 59 | 60 | var ConversationMemberModel *xConversationMemberModel 61 | 62 | func InitConversationMemberModel(coll *qmgo.QmgoClient, rc *redis.Redis) { 63 | ConversationMemberModel = &xConversationMemberModel{ 64 | coll: coll, 65 | rc: rc, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/conversation/friendmodel/friend.go: -------------------------------------------------------------------------------- 1 | package friendmodel 2 | 3 | import ( 4 | "github.com/qiniu/qmgo" 5 | opts "github.com/qiniu/qmgo/options" 6 | "github.com/zeromicro/go-zero/core/stores/redis" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | ) 10 | 11 | // Friend 好友 数据库模型 12 | type Friend struct { 13 | // UserId 用户ID 申请加好友的用户ID 14 | UserId string `json:"userId" gorm:"column:userId;type:char(32);primary_key;not null" bson:"userId"` 15 | // FriendId 好友ID 被申请加好友的用户ID 16 | FriendId string `json:"friendId" gorm:"column:friendId;type:char(32);primary_key;not null" bson:"friendId"` 17 | // BeFriendTime 成为好友时间 13位时间戳 18 | BeFriendTime primitive.DateTime `json:"beFriendTime" gorm:"column:beFriendTime;type:bigint(13);not null" bson:"beFriendTime"` 19 | } 20 | 21 | // TableName 表名 22 | func (m *Friend) TableName() string { 23 | return "friend" 24 | } 25 | 26 | // GetIndexes 获取索引 27 | func (m *Friend) GetIndexes() []opts.IndexModel { 28 | return []opts.IndexModel{{ 29 | Key: []string{"userId", "friendId"}, 30 | IndexOptions: options.Index().SetName("unique_friend").SetUnique(true), 31 | }, { 32 | Key: []string{"userId"}, 33 | }} 34 | } 35 | 36 | // xFriendModel 数据库操作实例 37 | type xFriendModel struct { 38 | coll *qmgo.QmgoClient 39 | rc *redis.Redis 40 | } 41 | 42 | var FriendModel *xFriendModel 43 | 44 | // InitFriendModel 初始化数据库操作实例 45 | func InitFriendModel(coll *qmgo.QmgoClient, rc *redis.Redis) { 46 | FriendModel = &xFriendModel{ 47 | coll: coll, 48 | rc: rc, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/conversation/friendmodel/friendapplyrecord.go: -------------------------------------------------------------------------------- 1 | package friendmodel 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | opts "github.com/qiniu/qmgo/options" 6 | "go.mongodb.org/mongo-driver/bson/primitive" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | ) 9 | 10 | type FriendApplyStatus = pb.FriendApplyStatus 11 | 12 | const ( 13 | FriendApplyStatusApplying = pb.FriendApplyStatus_Applying 14 | FriendApplyStatusAccepted = pb.FriendApplyStatus_Accepted 15 | FriendApplyStatusRejected = pb.FriendApplyStatus_Rejected 16 | ) 17 | 18 | // FriendApplyRecord 好友申请记录 数据库模型 19 | type FriendApplyRecord struct { 20 | ApplyId string `bson:"applyId" json:"applyId"` // 申请id 21 | FromId string `bson:"fromId" json:"fromId"` // 申请人id 22 | ToId string `bson:"toId" json:"toId"` // 被申请人id 23 | //附加验证信息 24 | Message string `bson:"message" json:"message"` // 附加验证信息 25 | //附加问题答案 26 | Answer string `bson:"answer" json:"answer"` // 附加答案 27 | //申请时间 28 | ApplyTime primitive.DateTime `bson:"applyTime" json:"applyTime"` // 申请时间 29 | //申请状态 30 | Status FriendApplyStatus `bson:"status" json:"status"` // 申请状态 31 | //From是否将此次请求删除 32 | FromDeleteTime primitive.DateTime `bson:"fromDeleteTime" json:"fromDeleteTime"` // From是否将此次请求删除 33 | //To是否将此次请求删除 34 | ToDeleteTime primitive.DateTime `bson:"toDeleteTime" json:"toDeleteTime"` // To是否将此次请求删除 35 | } 36 | 37 | func (m *FriendApplyRecord) GetIndexes() []opts.IndexModel { 38 | return []opts.IndexModel{{ 39 | Key: []string{"applyId"}, 40 | IndexOptions: options.Index().SetUnique(true), 41 | }, { 42 | Key: []string{"fromId"}, 43 | }, { 44 | Key: []string{"toId"}, 45 | }, { 46 | Key: []string{"status"}, 47 | }, { 48 | Key: []string{"fromDeleteTime"}, 49 | }, { 50 | Key: []string{"toDeleteTime"}, 51 | }} 52 | } 53 | -------------------------------------------------------------------------------- /app/conversation/groupmodel/groupsubscribe.go: -------------------------------------------------------------------------------- 1 | package groupmodel 2 | 3 | import ( 4 | opts "github.com/qiniu/qmgo/options" 5 | "go.mongodb.org/mongo-driver/bson/primitive" 6 | "go.mongodb.org/mongo-driver/mongo/options" 7 | ) 8 | 9 | type GroupSubscribe struct { 10 | GroupId string `bson:"groupId" json:"groupId"` 11 | MemberUserId string `bson:"memberUserId" json:"memberUserId"` 12 | SubscribeTime primitive.DateTime `bson:"subscribeTime" json:"subscribeTime"` 13 | } 14 | 15 | func (m *GroupSubscribe) GetIndexes() []opts.IndexModel { 16 | return []opts.IndexModel{{ 17 | Key: []string{"groupId", "memberUserId"}, 18 | IndexOptions: options.Index().SetUnique(true), 19 | }} 20 | } 21 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/conversationservice/conversationSettingUpdateLogic.go: -------------------------------------------------------------------------------- 1 | package conversationservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type ConversationSettingUpdateLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewConversationSettingUpdateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConversationSettingUpdateLogic { 19 | return &ConversationSettingUpdateLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // ConversationSettingUpdate 更新会话设置 27 | func (l *ConversationSettingUpdateLogic) ConversationSettingUpdate(in *pb.ConversationSettingUpdateReq) (*pb.ConversationSettingUpdateResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.ConversationSettingUpdateResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/friendservice/countFriendLogic.go: -------------------------------------------------------------------------------- 1 | package friendservicelogic 2 | 3 | import ( 4 | "context" 5 | "go.mongodb.org/mongo-driver/bson" 6 | 7 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 8 | "github.com/cherish-chat/xxim-server/common/pb" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type CountFriendLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewCountFriendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountFriendLogic { 20 | return &CountFriendLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | // CountFriend 统计好友数量 28 | func (l *CountFriendLogic) CountFriend(in *pb.CountFriendReq) (*pb.CountFriendResp, error) { 29 | count, err := l.svcCtx.FriendCollection.Find(l.ctx, bson.M{ 30 | "userId": in.Header.UserId, 31 | }).Count() 32 | if err != nil { 33 | l.Errorf("find friend error: %v", err) 34 | return &pb.CountFriendResp{}, err 35 | } 36 | return &pb.CountFriendResp{Count: count}, nil 37 | } 38 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/friendservice/listFriendApplyLogic.go: -------------------------------------------------------------------------------- 1 | package friendservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/conversation/friendmodel" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | "math" 9 | 10 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | 13 | "github.com/zeromicro/go-zero/core/logx" 14 | ) 15 | 16 | type ListFriendApplyLogic struct { 17 | ctx context.Context 18 | svcCtx *svc.ServiceContext 19 | logx.Logger 20 | } 21 | 22 | func NewListFriendApplyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListFriendApplyLogic { 23 | return &ListFriendApplyLogic{ 24 | ctx: ctx, 25 | svcCtx: svcCtx, 26 | Logger: logx.WithContext(ctx), 27 | } 28 | } 29 | 30 | // ListFriendApply 列出好友申请 31 | func (l *ListFriendApplyLogic) ListFriendApply(in *pb.ListFriendApplyReq) (*pb.ListFriendApplyResp, error) { 32 | filter := bson.M{} 33 | if in.Cursor > 0 { 34 | // < cursor 35 | filter["applyTime"] = bson.M{"$lt": primitive.DateTime(in.Cursor)} 36 | } 37 | if in.GetFilter().Status != nil { 38 | status := *in.GetFilter().Status 39 | filter["status"] = status 40 | } 41 | if in.GetOption().GetIncludeApplyByMe() { 42 | // fromId = in.Header.UserId or toId = in.Header.UserId 43 | filter["$or"] = bson.A{ 44 | bson.M{"fromId": in.Header.UserId}, 45 | bson.M{"toId": in.Header.UserId}, 46 | } 47 | } else { 48 | // toId = in.Header.UserId 49 | filter["toId"] = in.Header.UserId 50 | } 51 | var result []*friendmodel.FriendApplyRecord 52 | err := l.svcCtx.FriendApplyRecordCollection.Find(l.ctx, filter).Sort("-applyTime").Limit(in.Limit).All(&result) 53 | if err != nil { 54 | l.Errorf("find friend apply record error: %v", err) 55 | return &pb.ListFriendApplyResp{}, err 56 | } 57 | if len(result) == 0 { 58 | return &pb.ListFriendApplyResp{ 59 | Cursor: 0, 60 | FriendApplyList: nil, 61 | }, nil 62 | } 63 | var resp = &pb.ListFriendApplyResp{ 64 | Cursor: 0, 65 | FriendApplyList: make([]*pb.ListFriendApplyResp_FriendApply, 0), 66 | } 67 | minCursor := int64(math.MaxInt64) 68 | for _, record := range result { 69 | resp.FriendApplyList = append(resp.FriendApplyList, &pb.ListFriendApplyResp_FriendApply{ 70 | ApplyId: record.ApplyId, 71 | FromUserId: record.FromId, 72 | ToUserId: record.ToId, 73 | Message: record.Message, 74 | Answer: record.Answer, 75 | }) 76 | if int64(record.ApplyTime) < minCursor { 77 | minCursor = int64(record.ApplyTime) 78 | } 79 | } 80 | return resp, nil 81 | } 82 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/groupservice/countCreateGroupLogic.go: -------------------------------------------------------------------------------- 1 | package groupservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type CountCreateGroupLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewCountCreateGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountCreateGroupLogic { 19 | return &CountCreateGroupLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // CountCreateGroup 统计创建的群组数量 27 | func (l *CountCreateGroupLogic) CountCreateGroup(in *pb.CountCreateGroupReq) (*pb.CountCreateGroupResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.CountCreateGroupResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/groupservice/countJoinGroupLogic.go: -------------------------------------------------------------------------------- 1 | package groupservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type CountJoinGroupLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewCountJoinGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountJoinGroupLogic { 19 | return &CountJoinGroupLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // CountJoinGroup 统计加入的群组数量 27 | func (l *CountJoinGroupLogic) CountJoinGroup(in *pb.CountJoinGroupReq) (*pb.CountJoinGroupResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.CountJoinGroupResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/groupservice/groupSubscribeLogic.go: -------------------------------------------------------------------------------- 1 | package groupservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/conversation/groupmodel" 6 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | "github.com/zeromicro/go-zero/core/logx" 9 | "go.mongodb.org/mongo-driver/bson" 10 | "go.mongodb.org/mongo-driver/bson/primitive" 11 | "time" 12 | ) 13 | 14 | type GroupSubscribeLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewGroupSubscribeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GroupSubscribeLogic { 21 | return &GroupSubscribeLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | // GroupSubscribe 群组订阅 29 | func (l *GroupSubscribeLogic) GroupSubscribe(in *pb.GroupSubscribeReq) (*pb.GroupSubscribeResp, error) { 30 | // 1. 获取用户加入的群组 31 | listJoinedGroupsResp, err := l.svcCtx.ConversationService.ListJoinedConversations(l.ctx, &pb.ListJoinedConversationsReq{ 32 | Header: in.Header, 33 | ConversationType: pb.ConversationType_Group, 34 | Cursor: 0, 35 | Limit: int64(l.svcCtx.Config.Group.JoinedMaxCount), 36 | Filter: &pb.ListJoinedConversationsReq_Filter{ 37 | SettingList: []*pb.ListJoinedConversationsReq_Filter_SettingKV{{ 38 | //是否被屏蔽这个key != "true" or 不存在 39 | Key: pb.ConversationSettingKey_IsBlocked, // 是否被屏蔽 40 | Value: "true", 41 | Operator: pb.ListJoinedConversationsReq_Filter_SettingKV_NotEqual, 42 | OrNotExists: true, 43 | OrExists: false, 44 | }}, 45 | }, 46 | Option: &pb.ListJoinedConversationsReq_Option{ 47 | IncludeSelfMemberInfo: true, 48 | }, 49 | }) 50 | if err != nil { 51 | l.Errorf("listJoinedGroupsResp err: %v", err) 52 | return &pb.GroupSubscribeResp{}, err 53 | } 54 | 55 | if len(listJoinedGroupsResp.GetConversationList()) == 0 { 56 | return &pb.GroupSubscribeResp{}, nil 57 | } 58 | 59 | var groupIds []string 60 | for _, group := range listJoinedGroupsResp.GetConversationList() { 61 | groupIds = append(groupIds, group.GetConversationId()) 62 | } 63 | 64 | // 2. 批量更新用户的群组订阅时间 65 | // filter: {memberUserId: "xxx", groupId: {$in: ["xxx", "xxx"]}} 66 | // set: {subscribeTime: "xxx"} 67 | bulk := l.svcCtx.GroupSubscribeCollection.Bulk() 68 | for _, groupId := range groupIds { 69 | bulk.Upsert(bson.M{ 70 | "memberUserId": in.Header.UserId, 71 | "groupId": groupId, 72 | }, &groupmodel.GroupSubscribe{ 73 | GroupId: groupId, 74 | MemberUserId: in.Header.UserId, 75 | SubscribeTime: primitive.NewDateTimeFromTime(time.Now()), 76 | }) 77 | } 78 | _, err = bulk.Run(l.ctx) 79 | if err != nil { 80 | l.Errorf("bulk.Run err: %v", err) 81 | return &pb.GroupSubscribeResp{}, err 82 | } 83 | return &pb.GroupSubscribeResp{}, nil 84 | } 85 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/groupservice/listGroupSubscribersLogic.go: -------------------------------------------------------------------------------- 1 | package groupservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/conversation/groupmodel" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | 9 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 10 | "github.com/cherish-chat/xxim-server/common/pb" 11 | 12 | "github.com/zeromicro/go-zero/core/logx" 13 | ) 14 | 15 | type ListGroupSubscribersLogic struct { 16 | ctx context.Context 17 | svcCtx *svc.ServiceContext 18 | logx.Logger 19 | } 20 | 21 | func NewListGroupSubscribersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListGroupSubscribersLogic { 22 | return &ListGroupSubscribersLogic{ 23 | ctx: ctx, 24 | svcCtx: svcCtx, 25 | Logger: logx.WithContext(ctx), 26 | } 27 | } 28 | 29 | // ListGroupSubscribers 列出群组订阅者 30 | func (l *ListGroupSubscribersLogic) ListGroupSubscribers(in *pb.ListGroupSubscribersReq) (*pb.ListGroupSubscribersResp, error) { 31 | filter := bson.M{ 32 | "groupId": in.GroupId, 33 | } 34 | // filter 35 | { 36 | if in.GetFilter().GetSubscribeTimeGte() >= 0 { 37 | filter["subscribeTime"] = bson.M{ 38 | "$gte": primitive.DateTime(in.GetFilter().GetSubscribeTimeGte()), 39 | } 40 | } 41 | } 42 | queryI := l.svcCtx.GroupSubscribeCollection.Find(l.ctx, filter) 43 | if in.Limit > 0 { 44 | queryI = queryI.Limit(in.Limit) 45 | } 46 | if in.Cursor > 0 { 47 | queryI = queryI.Skip(in.Cursor) 48 | } 49 | var result []*groupmodel.GroupSubscribe 50 | err := queryI.All(&result) 51 | if err != nil { 52 | l.Errorf("find group subscribe error: %v", err) 53 | return &pb.ListGroupSubscribersResp{}, err 54 | } 55 | var resp = &pb.ListGroupSubscribersResp{ 56 | SubscriberList: make([]*pb.ListGroupSubscribersResp_Subscriber, 0), 57 | } 58 | for _, item := range result { 59 | resp.SubscriberList = append(resp.SubscriberList, &pb.ListGroupSubscribersResp_Subscriber{ 60 | UserId: item.MemberUserId, 61 | SubscribeTime: int64(item.SubscribeTime), 62 | }) 63 | } 64 | return resp, nil 65 | } 66 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/subscriptionservice/deleteUserSubscriptionLogic.go: -------------------------------------------------------------------------------- 1 | package subscriptionservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type DeleteUserSubscriptionLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewDeleteUserSubscriptionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteUserSubscriptionLogic { 19 | return &DeleteUserSubscriptionLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // DeleteUserSubscription 删除用户订阅的订阅号 27 | func (l *DeleteUserSubscriptionLogic) DeleteUserSubscription(in *pb.DeleteUserSubscriptionReq) (*pb.DeleteUserSubscriptionResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.DeleteUserSubscriptionResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/subscriptionservice/listSubscriptionSubscribersLogic.go: -------------------------------------------------------------------------------- 1 | package subscriptionservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/conversation/subscriptionmodel" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | 9 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 10 | "github.com/cherish-chat/xxim-server/common/pb" 11 | 12 | "github.com/zeromicro/go-zero/core/logx" 13 | ) 14 | 15 | type ListSubscriptionSubscribersLogic struct { 16 | ctx context.Context 17 | svcCtx *svc.ServiceContext 18 | logx.Logger 19 | } 20 | 21 | func NewListSubscriptionSubscribersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListSubscriptionSubscribersLogic { 22 | return &ListSubscriptionSubscribersLogic{ 23 | ctx: ctx, 24 | svcCtx: svcCtx, 25 | Logger: logx.WithContext(ctx), 26 | } 27 | } 28 | 29 | // ListSubscriptionSubscribers 列出订阅号订阅者 30 | func (l *ListSubscriptionSubscribersLogic) ListSubscriptionSubscribers(in *pb.ListSubscriptionSubscribersReq) (*pb.ListSubscriptionSubscribersResp, error) { 31 | filter := bson.M{ 32 | "subscriptionId": in.SubscriptionId, 33 | } 34 | // filter 35 | { 36 | if in.GetFilter().GetSubscribeTimeGte() >= 0 { 37 | filter["subscribeTime"] = bson.M{ 38 | "$gte": primitive.DateTime(in.GetFilter().GetSubscribeTimeGte()), 39 | } 40 | } 41 | } 42 | queryI := l.svcCtx.SubscriptionSubscribeCollection.Find(l.ctx, filter) 43 | if in.Limit > 0 { 44 | queryI = queryI.Limit(in.Limit) 45 | } 46 | if in.Cursor > 0 { 47 | queryI = queryI.Skip(in.Cursor) 48 | } 49 | var result []*subscriptionmodel.SubscriptionSubscribe 50 | err := queryI.All(&result) 51 | if err != nil { 52 | l.Errorf("find subscription subscribe error: %v", err) 53 | return &pb.ListSubscriptionSubscribersResp{}, err 54 | } 55 | var resp = &pb.ListSubscriptionSubscribersResp{ 56 | SubscriberList: make([]*pb.ListSubscriptionSubscribersResp_Subscriber, 0), 57 | } 58 | for _, item := range result { 59 | resp.SubscriberList = append(resp.SubscriberList, &pb.ListSubscriptionSubscribersResp_Subscriber{ 60 | UserId: item.MemberUserId, 61 | SubscribeTime: int64(item.SubscribeTime), 62 | }) 63 | } 64 | return resp, nil 65 | } 66 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/subscriptionservice/subscriptionAfterOfflineLogic.go: -------------------------------------------------------------------------------- 1 | package subscriptionservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/conversation/subscriptionmodel" 6 | "github.com/cherish-chat/xxim-server/common/utils" 7 | 8 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type SubscriptionAfterOfflineLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewSubscriptionAfterOfflineLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubscriptionAfterOfflineLogic { 21 | return &SubscriptionAfterOfflineLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | // SubscriptionAfterOffline 订阅号在做用户下线后的操作 29 | func (l *SubscriptionAfterOfflineLogic) SubscriptionAfterOffline(in *pb.SubscriptionAfterOfflineReq) (*pb.SubscriptionAfterOfflineResp, error) { 30 | //1. 使用订阅号发一条通知,告诉他的订阅者,他上线了 31 | { 32 | _, err := l.svcCtx.NoticeService.NoticeSend(l.ctx, &pb.NoticeSendReq{ 33 | Header: in.Header, 34 | Notice: &pb.Notice{ 35 | NoticeId: utils.Snowflake.String(), 36 | ConversationId: subscriptionmodel.UserDefaultSubscriptionId(in.Header.UserId), 37 | ConversationType: pb.ConversationType_Subscription, 38 | Content: utils.Json.MarshalToString(&pb.NoticeContentOnlineStatus{UserId: in.Header.UserId, Online: false}), 39 | ContentType: pb.NoticeContentType_OnlineStatus, 40 | UpdateTime: utils.Time.Now13(), 41 | Sort: 0, 42 | }, 43 | UserIds: nil, 44 | Broadcast: true, 45 | }) 46 | if err != nil { 47 | l.Errorf("notice send error: %v", err) 48 | } 49 | } 50 | return &pb.SubscriptionAfterOfflineResp{}, nil 51 | } 52 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/subscriptionservice/subscriptionAfterOnlineLogic.go: -------------------------------------------------------------------------------- 1 | package subscriptionservicelogic 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/cherish-chat/xxim-server/app/conversation/subscriptionmodel" 7 | "github.com/cherish-chat/xxim-server/common/utils" 8 | "go.mongodb.org/mongo-driver/bson" 9 | 10 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | 13 | "github.com/zeromicro/go-zero/core/logx" 14 | ) 15 | 16 | type SubscriptionAfterOnlineLogic struct { 17 | ctx context.Context 18 | svcCtx *svc.ServiceContext 19 | logx.Logger 20 | } 21 | 22 | func NewSubscriptionAfterOnlineLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubscriptionAfterOnlineLogic { 23 | return &SubscriptionAfterOnlineLogic{ 24 | ctx: ctx, 25 | svcCtx: svcCtx, 26 | Logger: logx.WithContext(ctx), 27 | } 28 | } 29 | 30 | // SubscriptionAfterOnline 订阅号在做用户上线后的操作 31 | func (l *SubscriptionAfterOnlineLogic) SubscriptionAfterOnline(in *pb.SubscriptionAfterOnlineReq) (*pb.SubscriptionAfterOnlineResp, error) { 32 | //1. 检查用户有没有创建他的默认订阅号 33 | count, err := l.svcCtx.SubscriptionCollection.Find(l.ctx, bson.M{ 34 | "subscriptionId": subscriptionmodel.UserDefaultSubscriptionId(in.Header.UserId), 35 | "subscriptionType": subscriptionmodel.SubscriptionTypeHidden, 36 | }).Count() 37 | if err != nil { 38 | l.Errorf("find subscription error: %v", err) 39 | return &pb.SubscriptionAfterOnlineResp{}, err 40 | } 41 | if count == 0 { 42 | //1.1. 如果没有创建,创建一个默认的订阅号 43 | _, err := l.svcCtx.SubscriptionCollection.InsertOne(l.ctx, &subscriptionmodel.Subscription{ 44 | SubscriptionType: subscriptionmodel.SubscriptionTypeHidden, 45 | SubscriptionId: subscriptionmodel.UserDefaultSubscriptionId(in.Header.UserId), 46 | Avatar: "", 47 | Nickname: "", 48 | Bio: fmt.Sprintf("系统帮用户[%s]创建的默认订阅号,用于向订阅者推送通知。如:上线下线、发布世界圈、更新资料等", in.Header.UserId), 49 | Cover: "", 50 | ExtraMap: bson.M{ 51 | "defaultForUser": "true", 52 | }, 53 | }) 54 | if err != nil { 55 | l.Errorf("insert subscription error: %v", err) 56 | return &pb.SubscriptionAfterOnlineResp{}, err 57 | } 58 | } 59 | //2. 使用订阅号发一条通知,告诉他的订阅者,他上线了 60 | { 61 | _, err := l.svcCtx.NoticeService.NoticeSend(l.ctx, &pb.NoticeSendReq{ 62 | Header: in.Header, 63 | Notice: &pb.Notice{ 64 | NoticeId: utils.Snowflake.String(), 65 | ConversationId: subscriptionmodel.UserDefaultSubscriptionId(in.Header.UserId), 66 | ConversationType: pb.ConversationType_Subscription, 67 | Content: utils.Json.MarshalToString(&pb.NoticeContentOnlineStatus{UserId: in.Header.UserId, Online: true}), 68 | ContentType: pb.NoticeContentType_OnlineStatus, 69 | UpdateTime: utils.Time.Now13(), 70 | Sort: 0, 71 | }, 72 | UserIds: nil, 73 | Broadcast: true, 74 | }) 75 | if err != nil { 76 | l.Errorf("notice send error: %v", err) 77 | } 78 | } 79 | return &pb.SubscriptionAfterOnlineResp{}, nil 80 | } 81 | -------------------------------------------------------------------------------- /app/conversation/internal/logic/subscriptionservice/upsertUserSubscriptionLogic.go: -------------------------------------------------------------------------------- 1 | package subscriptionservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/conversation/subscriptionmodel" 6 | "github.com/cherish-chat/xxim-server/common/utils" 7 | opts "github.com/qiniu/qmgo/options" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/bson/primitive" 10 | "go.mongodb.org/mongo-driver/mongo/options" 11 | 12 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 13 | "github.com/cherish-chat/xxim-server/common/pb" 14 | 15 | "github.com/zeromicro/go-zero/core/logx" 16 | ) 17 | 18 | type UpsertUserSubscriptionLogic struct { 19 | ctx context.Context 20 | svcCtx *svc.ServiceContext 21 | logx.Logger 22 | } 23 | 24 | func NewUpsertUserSubscriptionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpsertUserSubscriptionLogic { 25 | return &UpsertUserSubscriptionLogic{ 26 | ctx: ctx, 27 | svcCtx: svcCtx, 28 | Logger: logx.WithContext(ctx), 29 | } 30 | } 31 | 32 | // UpsertUserSubscription 更新用户订阅的订阅号 33 | func (l *UpsertUserSubscriptionLogic) UpsertUserSubscription(in *pb.UpsertUserSubscriptionReq) (*pb.UpsertUserSubscriptionResp, error) { 34 | userSubscription := &subscriptionmodel.UserSubscription{ 35 | SubscriptionId: in.UserSubscription.SubscriptionId, 36 | Subscriber: in.UserSubscription.Subscriber, 37 | SubscribeTime: primitive.DateTime(in.UserSubscription.SubscribeTime), 38 | ExtraMap: utils.Map.SS2SA(in.UserSubscription.ExtraMap), 39 | } 40 | _, err := l.svcCtx.UserSubscriptionCollection.Upsert(l.ctx, bson.M{ 41 | "subscriptionId": userSubscription.SubscriptionId, 42 | "subscriber": userSubscription.Subscriber, 43 | }, userSubscription, opts.UpsertOptions{ 44 | ReplaceOptions: options.Replace().SetUpsert(true), 45 | }) 46 | if err != nil { 47 | l.Errorf("upsert user subscription error: %v", err) 48 | return &pb.UpsertUserSubscriptionResp{}, err 49 | } 50 | return &pb.UpsertUserSubscriptionResp{}, nil 51 | } 52 | -------------------------------------------------------------------------------- /app/conversation/internal/server/conversationservice/conversationServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: conversation.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/conversation/internal/logic/conversationservice" 10 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type ConversationServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedConversationServiceServer 17 | } 18 | 19 | func NewConversationServiceServer(svcCtx *svc.ServiceContext) *ConversationServiceServer { 20 | return &ConversationServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // ConversationSettingUpdate 更新会话设置 26 | func (s *ConversationServiceServer) ConversationSettingUpdate(ctx context.Context, in *pb.ConversationSettingUpdateReq) (*pb.ConversationSettingUpdateResp, error) { 27 | l := conversationservicelogic.NewConversationSettingUpdateLogic(ctx, s.svcCtx) 28 | return l.ConversationSettingUpdate(in) 29 | } 30 | 31 | // ListJoinedConversations 列出加入的会话 32 | func (s *ConversationServiceServer) ListJoinedConversations(ctx context.Context, in *pb.ListJoinedConversationsReq) (*pb.ListJoinedConversationsResp, error) { 33 | l := conversationservicelogic.NewListJoinedConversationsLogic(ctx, s.svcCtx) 34 | return l.ListJoinedConversations(in) 35 | } 36 | -------------------------------------------------------------------------------- /app/conversation/internal/server/friendservice/friendServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: conversation.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/conversation/internal/logic/friendservice" 10 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type FriendServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedFriendServiceServer 17 | } 18 | 19 | func NewFriendServiceServer(svcCtx *svc.ServiceContext) *FriendServiceServer { 20 | return &FriendServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // FriendApply 添加好友 26 | func (s *FriendServiceServer) FriendApply(ctx context.Context, in *pb.FriendApplyReq) (*pb.FriendApplyResp, error) { 27 | l := friendservicelogic.NewFriendApplyLogic(ctx, s.svcCtx) 28 | return l.FriendApply(in) 29 | } 30 | 31 | // FriendApplyHandle 处理好友申请 32 | func (s *FriendServiceServer) FriendApplyHandle(ctx context.Context, in *pb.FriendApplyHandleReq) (*pb.FriendApplyHandleResp, error) { 33 | l := friendservicelogic.NewFriendApplyHandleLogic(ctx, s.svcCtx) 34 | return l.FriendApplyHandle(in) 35 | } 36 | 37 | // ListFriendApply 列出好友申请 38 | func (s *FriendServiceServer) ListFriendApply(ctx context.Context, in *pb.ListFriendApplyReq) (*pb.ListFriendApplyResp, error) { 39 | l := friendservicelogic.NewListFriendApplyLogic(ctx, s.svcCtx) 40 | return l.ListFriendApply(in) 41 | } 42 | 43 | // CountFriend 统计好友数量 44 | func (s *FriendServiceServer) CountFriend(ctx context.Context, in *pb.CountFriendReq) (*pb.CountFriendResp, error) { 45 | l := friendservicelogic.NewCountFriendLogic(ctx, s.svcCtx) 46 | return l.CountFriend(in) 47 | } 48 | -------------------------------------------------------------------------------- /app/conversation/internal/server/groupservice/groupServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: conversation.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/conversation/internal/logic/groupservice" 10 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type GroupServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedGroupServiceServer 17 | } 18 | 19 | func NewGroupServiceServer(svcCtx *svc.ServiceContext) *GroupServiceServer { 20 | return &GroupServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // GroupCreate 创建群组 26 | func (s *GroupServiceServer) GroupCreate(ctx context.Context, in *pb.GroupCreateReq) (*pb.GroupCreateResp, error) { 27 | l := groupservicelogic.NewGroupCreateLogic(ctx, s.svcCtx) 28 | return l.GroupCreate(in) 29 | } 30 | 31 | // CountJoinGroup 统计加入的群组数量 32 | func (s *GroupServiceServer) CountJoinGroup(ctx context.Context, in *pb.CountJoinGroupReq) (*pb.CountJoinGroupResp, error) { 33 | l := groupservicelogic.NewCountJoinGroupLogic(ctx, s.svcCtx) 34 | return l.CountJoinGroup(in) 35 | } 36 | 37 | // CountCreateGroup 统计创建的群组数量 38 | func (s *GroupServiceServer) CountCreateGroup(ctx context.Context, in *pb.CountCreateGroupReq) (*pb.CountCreateGroupResp, error) { 39 | l := groupservicelogic.NewCountCreateGroupLogic(ctx, s.svcCtx) 40 | return l.CountCreateGroup(in) 41 | } 42 | 43 | // GroupSubscribe 群组订阅 44 | func (s *GroupServiceServer) GroupSubscribe(ctx context.Context, in *pb.GroupSubscribeReq) (*pb.GroupSubscribeResp, error) { 45 | l := groupservicelogic.NewGroupSubscribeLogic(ctx, s.svcCtx) 46 | return l.GroupSubscribe(in) 47 | } 48 | 49 | // ListGroupSubscribers 列出群组订阅者 50 | func (s *GroupServiceServer) ListGroupSubscribers(ctx context.Context, in *pb.ListGroupSubscribersReq) (*pb.ListGroupSubscribersResp, error) { 51 | l := groupservicelogic.NewListGroupSubscribersLogic(ctx, s.svcCtx) 52 | return l.ListGroupSubscribers(in) 53 | } 54 | -------------------------------------------------------------------------------- /app/conversation/internal/server/subscriptionservice/subscriptionServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: conversation.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/conversation/internal/logic/subscriptionservice" 10 | "github.com/cherish-chat/xxim-server/app/conversation/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type SubscriptionServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedSubscriptionServiceServer 17 | } 18 | 19 | func NewSubscriptionServiceServer(svcCtx *svc.ServiceContext) *SubscriptionServiceServer { 20 | return &SubscriptionServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // SubscriptionSubscribe 订阅号订阅 26 | func (s *SubscriptionServiceServer) SubscriptionSubscribe(ctx context.Context, in *pb.SubscriptionSubscribeReq) (*pb.SubscriptionSubscribeResp, error) { 27 | l := subscriptionservicelogic.NewSubscriptionSubscribeLogic(ctx, s.svcCtx) 28 | return l.SubscriptionSubscribe(in) 29 | } 30 | 31 | // SubscriptionAfterOnline 订阅号在做用户上线后的操作 32 | func (s *SubscriptionServiceServer) SubscriptionAfterOnline(ctx context.Context, in *pb.SubscriptionAfterOnlineReq) (*pb.SubscriptionAfterOnlineResp, error) { 33 | l := subscriptionservicelogic.NewSubscriptionAfterOnlineLogic(ctx, s.svcCtx) 34 | return l.SubscriptionAfterOnline(in) 35 | } 36 | 37 | // SubscriptionAfterOffline 订阅号在做用户下线后的操作 38 | func (s *SubscriptionServiceServer) SubscriptionAfterOffline(ctx context.Context, in *pb.SubscriptionAfterOfflineReq) (*pb.SubscriptionAfterOfflineResp, error) { 39 | l := subscriptionservicelogic.NewSubscriptionAfterOfflineLogic(ctx, s.svcCtx) 40 | return l.SubscriptionAfterOffline(in) 41 | } 42 | 43 | // UpsertUserSubscription 更新用户订阅的订阅号 44 | func (s *SubscriptionServiceServer) UpsertUserSubscription(ctx context.Context, in *pb.UpsertUserSubscriptionReq) (*pb.UpsertUserSubscriptionResp, error) { 45 | l := subscriptionservicelogic.NewUpsertUserSubscriptionLogic(ctx, s.svcCtx) 46 | return l.UpsertUserSubscription(in) 47 | } 48 | 49 | // DeleteUserSubscription 删除用户订阅的订阅号 50 | func (s *SubscriptionServiceServer) DeleteUserSubscription(ctx context.Context, in *pb.DeleteUserSubscriptionReq) (*pb.DeleteUserSubscriptionResp, error) { 51 | l := subscriptionservicelogic.NewDeleteUserSubscriptionLogic(ctx, s.svcCtx) 52 | return l.DeleteUserSubscription(in) 53 | } 54 | 55 | // ListSubscriptionSubscribers 列出订阅号订阅者 56 | func (s *SubscriptionServiceServer) ListSubscriptionSubscribers(ctx context.Context, in *pb.ListSubscriptionSubscribersReq) (*pb.ListSubscriptionSubscribersResp, error) { 57 | l := subscriptionservicelogic.NewListSubscriptionSubscribersLogic(ctx, s.svcCtx) 58 | return l.ListSubscriptionSubscribers(in) 59 | } 60 | -------------------------------------------------------------------------------- /app/conversation/pb/common.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/common.proto -------------------------------------------------------------------------------- /app/conversation/pb/conversation.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/conversation.proto -------------------------------------------------------------------------------- /app/conversation/subscriptionmodel/subscriptionsubscribe.go: -------------------------------------------------------------------------------- 1 | package subscriptionmodel 2 | 3 | import ( 4 | opts "github.com/qiniu/qmgo/options" 5 | "go.mongodb.org/mongo-driver/bson/primitive" 6 | "go.mongodb.org/mongo-driver/mongo/options" 7 | ) 8 | 9 | type SubscriptionSubscribe struct { 10 | SubscriptionId string `bson:"subscriptionId" json:"subscriptionId"` 11 | MemberUserId string `bson:"memberUserId" json:"memberUserId"` 12 | SubscribeTime primitive.DateTime `bson:"subscribeTime" json:"subscribeTime"` 13 | } 14 | 15 | func (m *SubscriptionSubscribe) GetIndexes() []opts.IndexModel { 16 | return []opts.IndexModel{{ 17 | Key: []string{"subscriptionId", "memberUserId"}, 18 | IndexOptions: options.Index().SetUnique(true), 19 | }} 20 | } 21 | -------------------------------------------------------------------------------- /app/conversation/subscriptionmodel/usersubscription.go: -------------------------------------------------------------------------------- 1 | package subscriptionmodel 2 | 3 | import ( 4 | opts "github.com/qiniu/qmgo/options" 5 | "go.mongodb.org/mongo-driver/bson" 6 | "go.mongodb.org/mongo-driver/bson/primitive" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | ) 9 | 10 | type UserSubscription struct { 11 | SubscriptionId string `bson:"subscriptionId" json:"subscriptionId"` 12 | Subscriber string `bson:"subscriber" json:"subscriber"` 13 | SubscribeTime primitive.DateTime `bson:"subscribeTime" json:"subscribeTime"` 14 | ExtraMap bson.M `bson:"extraMap,omitempty" json:"extraMap"` 15 | } 16 | 17 | func (m *UserSubscription) GetIndexes() []opts.IndexModel { 18 | return []opts.IndexModel{{ 19 | Key: []string{"subscriptionId", "subscriber"}, 20 | IndexOptions: options.Index().SetUnique(true), 21 | }} 22 | } 23 | -------------------------------------------------------------------------------- /app/gateway/README.md: -------------------------------------------------------------------------------- 1 | # gateway xxim-server的网关服务 2 | 3 | - 该服务提供一个websocket的网关服务,接受客户端的连接,将消息转发到指定的服务。 4 | - 该服务可以注册到xxim-cloud的注册中心,你不必拥有云服务器和公网ip,只需要在本地启动该服务,就可以让你的客户端连接到该服务。 5 | -------------------------------------------------------------------------------- /app/gateway/client/gatewayservice/client.go: -------------------------------------------------------------------------------- 1 | package gatewayservice 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/core/logx" 5 | "github.com/zeromicro/go-zero/zrpc" 6 | "os" 7 | ) 8 | 9 | func MustNewGatewayService(config zrpc.RpcClientConf) GatewayService { 10 | client, err := zrpc.NewClient(config) 11 | if err != nil { 12 | logx.Errorf("zrpc.NewClient error: %v", err) 13 | os.Exit(1) 14 | } 15 | return NewGatewayService(client) 16 | } 17 | -------------------------------------------------------------------------------- /app/gateway/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/cherish-chat/xxim-server/app/gateway" 6 | ) 7 | 8 | var configFile = flag.String("f", "etc/gateway.yaml", "the config file") 9 | 10 | func main() { 11 | flag.Parse() 12 | 13 | gateway.Run(*configFile) 14 | } 15 | -------------------------------------------------------------------------------- /app/gateway/gateway.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/app/gateway/internal/server" 5 | "github.com/zeromicro/go-zero/core/logx" 6 | 7 | "github.com/cherish-chat/xxim-server/app/gateway/internal/config" 8 | gatewayserviceServer "github.com/cherish-chat/xxim-server/app/gateway/internal/server/gatewayservice" 9 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 10 | "github.com/cherish-chat/xxim-server/common/pb" 11 | 12 | "github.com/zeromicro/go-zero/core/conf" 13 | "github.com/zeromicro/go-zero/core/service" 14 | "github.com/zeromicro/go-zero/zrpc" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/reflection" 17 | ) 18 | 19 | func Run(configFile string) { 20 | var c config.Config 21 | conf.MustLoad(configFile, &c) 22 | ctx := svc.NewServiceContext(c) 23 | 24 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 25 | pb.RegisterGatewayServiceServer(grpcServer, gatewayserviceServer.NewGatewayServiceServer(ctx)) 26 | 27 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 28 | reflection.Register(grpcServer) 29 | } 30 | }) 31 | defer s.Stop() 32 | httpServer := server.NewHttpServer(ctx) 33 | go httpServer.Start() 34 | rtcServer := server.NewRtcServer(ctx) 35 | go rtcServer.Start() 36 | logx.Infof("rpc server start at %s", c.ListenOn) 37 | s.Start() 38 | } 39 | -------------------------------------------------------------------------------- /app/gateway/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/core/stores/redis" 5 | "github.com/zeromicro/go-zero/zrpc" 6 | ) 7 | 8 | type Config struct { 9 | zrpc.RpcServerConf 10 | Http struct { 11 | Cors struct { 12 | Enable bool `json:",optional"` 13 | AllowOrigins []string `json:",optional"` 14 | AllowHeaders []string `json:",optional"` 15 | AllowMethods []string `json:",optional"` 16 | ExposeHeaders []string `json:",optional"` 17 | AllowCredentials bool `json:",optional"` 18 | } `json:",optional"` 19 | ApiLog struct { 20 | Apis []string `json:",optional"` // 格式: GET r'^/api/v1/user/.*' 表示所有以 /api/v1/user/ 开头的 GET 请求都会被记录 21 | } 22 | Host string `json:",default=0.0.0.0"` 23 | Port int `json:",default=34500"` 24 | } 25 | Cloudx struct { 26 | StunUrls []string 27 | Host string 28 | Port int 29 | Ssl bool `json:",default=false"` 30 | AppId string 31 | ClientId string 32 | ClientSecret string 33 | KeepLiveSeconds int `json:",default=30"` 34 | } `json:",optional"` 35 | Websocket struct { 36 | KeepAliveTickerSecond int `json:",default=30"` // 定时器,每隔n秒检测连接是否存活 37 | KeepAliveSecond int `json:",default=60"` // 检测是否存活时,如果超过n秒没有收到客户端的消息,则关闭连接 38 | OfflineDeterminationSecond int `json:",default=180"` // 离线判定时间,如果超过n秒没有收到客户端的消息,则认为客户端离线,会回调离线通知 39 | } 40 | RpcClientConf struct { 41 | Dispatch zrpc.RpcClientConf 42 | User zrpc.RpcClientConf 43 | Conversation zrpc.RpcClientConf 44 | Third zrpc.RpcClientConf 45 | Message zrpc.RpcClientConf 46 | } 47 | RedisConf redis.RedisConf 48 | } 49 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/gatewayBatchGetUserConnectionLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GatewayBatchGetUserConnectionLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGatewayBatchGetUserConnectionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GatewayBatchGetUserConnectionLogic { 19 | return &GatewayBatchGetUserConnectionLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // GatewayBatchGetUserConnection 获取用户的连接 27 | // 二次开发人员建议不修改此处逻辑 28 | func (l *GatewayBatchGetUserConnectionLogic) GatewayBatchGetUserConnection(in *pb.GatewayBatchGetUserConnectionReq) (*pb.GatewayBatchGetUserConnectionResp, error) { 29 | connections := ConnectionLogic.GetConnectionsByUserIds(in.UserIds) 30 | var resp = &pb.GatewayBatchGetUserConnectionResp{} 31 | for _, connection := range connections { 32 | resp.Connections = append(resp.Connections, connection.ToPb()) 33 | } 34 | return resp, nil 35 | } 36 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/gatewayGetConnectionByFilterLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GatewayGetConnectionByFilterLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGatewayGetConnectionByFilterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GatewayGetConnectionByFilterLogic { 19 | return &GatewayGetConnectionByFilterLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // GatewayGetConnectionByFilter 通过条件获取用户的连接 27 | // 二次开发人员可以增加过滤条件 28 | func (l *GatewayGetConnectionByFilterLogic) GatewayGetConnectionByFilter(in *pb.GatewayGetConnectionByFilterReq) (*pb.GatewayGetConnectionByFilterResp, error) { 29 | if len(in.GetFilter().GetUserIds()) > 0 { 30 | connections := ConnectionLogic.GetConnectionsByUserIds(in.GetFilter().GetUserIds()) 31 | var resp = &pb.GatewayGetConnectionByFilterResp{} 32 | for _, connection := range connections { 33 | resp.Connections = append(resp.Connections, connection.ToPb()) 34 | } 35 | return resp, nil 36 | } else { 37 | // get all 38 | connections := ConnectionLogic.GetAllConnections() 39 | var resp = &pb.GatewayGetConnectionByFilterResp{} 40 | for _, connection := range connections { 41 | resp.Connections = append(resp.Connections, connection.ToPb()) 42 | } 43 | return resp, nil 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/gatewayGetUserConnectionLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GatewayGetUserConnectionLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGatewayGetUserConnectionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GatewayGetUserConnectionLogic { 19 | return &GatewayGetUserConnectionLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // GatewayGetUserConnection 获取用户的连接 27 | // 二次开发人员不建议修改此处逻辑 28 | func (l *GatewayGetUserConnectionLogic) GatewayGetUserConnection(in *pb.GatewayGetUserConnectionReq) (*pb.GatewayGetUserConnectionResp, error) { 29 | connections := ConnectionLogic.GetConnectionsByUserIds([]string{in.UserId}) 30 | if len(connections) == 0 { 31 | return &pb.GatewayGetUserConnectionResp{}, nil 32 | } 33 | var resp = &pb.GatewayGetUserConnectionResp{} 34 | for _, connection := range connections { 35 | resp.Connections = append(resp.Connections, connection.ToPb()) 36 | } 37 | return resp, nil 38 | } 39 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/gatewayKeepAliveLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | 8 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type GatewayKeepAliveLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewGatewayKeepAliveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GatewayKeepAliveLogic { 21 | l := &GatewayKeepAliveLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | if userKeepAliveMap == nil { 27 | userKeepAliveMap = new(sync.Map) 28 | go l.checkTimer() 29 | } 30 | return l 31 | } 32 | 33 | func (l *GatewayKeepAliveLogic) GatewayKeepAlive(in *pb.GatewayKeepAliveReq) (*pb.GatewayKeepAliveResp, error) { 34 | _, err := l.svcCtx.CallbackService.UserAfterKeepAlive(l.ctx, &pb.UserAfterKeepAliveReq{ 35 | Header: in.Header, 36 | }) 37 | if err != nil { 38 | l.Errorf("UserAfterKeepAlive error: %v", err) 39 | } 40 | if _, ok := userKeepAliveMap.Load(in.Header.UserId); !ok { 41 | _, _ = l.svcCtx.CallbackService.UserAfterOnline(l.ctx, &pb.UserAfterOnlineReq{Header: in.Header}) 42 | } 43 | userKeepAliveMap.Store(in.Header.UserId, time.Now()) 44 | 45 | return &pb.GatewayKeepAliveResp{}, nil 46 | } 47 | 48 | var userKeepAliveMap *sync.Map // key: userId, value: time.Time 49 | 50 | func (l *GatewayKeepAliveLogic) checkTimer() { 51 | ticker := time.NewTicker(time.Second * time.Duration(l.svcCtx.Config.Websocket.OfflineDeterminationSecond)) 52 | for { 53 | select { 54 | case <-ticker.C: 55 | now := time.Now() 56 | userKeepAliveMap.Range(func(key, value interface{}) bool { 57 | userId := key.(string) 58 | lastTime := value.(time.Time) 59 | if now.Sub(lastTime).Seconds() > float64(l.svcCtx.Config.Websocket.KeepAliveSecond) { 60 | // 用户下线 61 | _, _ = l.svcCtx.CallbackService.UserAfterOffline(l.ctx, &pb.UserAfterOfflineReq{UserId: userId}) 62 | userKeepAliveMap.Delete(userId) 63 | } 64 | return true 65 | }) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/gatewayKickWsLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 6 | "github.com/cherish-chat/xxim-server/common/pb" 7 | 8 | "github.com/zeromicro/go-zero/core/logx" 9 | ) 10 | 11 | type GatewayKickWsLogic struct { 12 | ctx context.Context 13 | svcCtx *svc.ServiceContext 14 | logx.Logger 15 | } 16 | 17 | func NewGatewayKickWsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GatewayKickWsLogic { 18 | return &GatewayKickWsLogic{ 19 | ctx: ctx, 20 | svcCtx: svcCtx, 21 | Logger: logx.WithContext(ctx), 22 | } 23 | } 24 | 25 | // GatewayKickWs 踢出用户的连接 26 | // 二次开发人员可以在此处修改踢出用户连接的逻辑 27 | // 比如踢出连接之前,先给用户发送一条消息 28 | func (l *GatewayKickWsLogic) GatewayKickWs(in *pb.GatewayKickWsReq) (*pb.GatewayKickWsResp, error) { 29 | if len(in.GetFilter().GetUserIds()) > 0 { 30 | connections := ConnectionLogic.GetConnectionsByUserIds(in.GetFilter().GetUserIds()) 31 | for _, connection := range connections { 32 | connection.Connection.CloseConnection(in.CloseCode, in.CloseReason) 33 | } 34 | } 35 | return &pb.GatewayKickWsResp{}, nil 36 | } 37 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/gatewayWriteDataToWsLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 6 | "github.com/cherish-chat/xxim-server/common/pb" 7 | "google.golang.org/protobuf/proto" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GatewayWriteDataToWsLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGatewayWriteDataToWsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GatewayWriteDataToWsLogic { 19 | return &GatewayWriteDataToWsLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // GatewayWriteDataToWs 向用户的连接写入数据 27 | // 二次开发人员不建议修改此处逻辑 28 | func (l *GatewayWriteDataToWsLogic) GatewayWriteDataToWs(in *pb.GatewayWriteDataToWsReq) (*pb.GatewayWriteDataToWsResp, error) { 29 | data, _ := proto.Marshal(in.Data) 30 | successConnections := make([]*pb.LongConnection, 0) 31 | if len(in.GetFilter().GetUserIds()) > 0 { 32 | connections := ConnectionLogic.GetConnectionsByUserIds(in.GetFilter().GetUserIds()) 33 | for _, connection := range connections { 34 | err := connection.SendMessage(l.ctx, data) 35 | if err != nil { 36 | l.Errorf("GatewayWriteDataToWs error: %v", err) 37 | } else { 38 | successConnections = append(successConnections, connection.ToPb()) 39 | } 40 | } 41 | } 42 | return &pb.GatewayWriteDataToWsResp{ 43 | SuccessConnections: successConnections, 44 | }, nil 45 | } 46 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/gatewayWriteDataToWsWrapperLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/gateway/client/gatewayservice" 6 | 7 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 8 | "github.com/cherish-chat/xxim-server/common/pb" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type GatewayWriteDataToWsWrapperLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewGatewayWriteDataToWsWrapperLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GatewayWriteDataToWsWrapperLogic { 20 | return &GatewayWriteDataToWsWrapperLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | // GatewayWriteDataToWsWrapper 向用户的连接写入数据 28 | func (l *GatewayWriteDataToWsWrapperLogic) GatewayWriteDataToWsWrapper(in *pb.GatewayWriteDataToWsWrapperReq) (*pb.GatewayWriteDataToWsResp, error) { 29 | pods, err := l.getGatewayPods() 30 | if err != nil { 31 | l.Errorf("get gateway pods error: %v", err) 32 | return &pb.GatewayWriteDataToWsResp{}, err 33 | } 34 | req := &pb.GatewayWriteDataToWsReq{ 35 | Header: in.Header, 36 | Filter: in.Filter, 37 | Data: in.Data, 38 | } 39 | for _, pod := range pods { 40 | gatewayWriteDataToWsResp, err := pod.GatewayWriteDataToWs(l.ctx, req) 41 | if err != nil { 42 | l.Errorf("gateway write data to ws error: %v", err) 43 | return &pb.GatewayWriteDataToWsResp{}, err 44 | } 45 | _ = gatewayWriteDataToWsResp 46 | } 47 | return &pb.GatewayWriteDataToWsResp{}, nil 48 | } 49 | 50 | func (l *GatewayWriteDataToWsWrapperLogic) getGatewayPods() ([]gatewayservice.GatewayService, error) { 51 | // todo 查询所有的gateway pod 52 | return []gatewayservice.GatewayService{ 53 | l.svcCtx.GatewayService(), 54 | }, nil 55 | } 56 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/verifyConnectionLogic.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | "crypto/elliptic" 6 | "errors" 7 | "github.com/cherish-chat/xxim-server/common/i18n" 8 | "github.com/cherish-chat/xxim-server/common/utils" 9 | 10 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | 13 | "github.com/zeromicro/go-zero/core/logx" 14 | ) 15 | 16 | type VerifyConnectionLogic struct { 17 | ctx context.Context 18 | svcCtx *svc.ServiceContext 19 | logx.Logger 20 | } 21 | 22 | func NewVerifyConnectionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VerifyConnectionLogic { 23 | return &VerifyConnectionLogic{ 24 | ctx: ctx, 25 | svcCtx: svcCtx, 26 | Logger: logx.WithContext(ctx), 27 | } 28 | } 29 | 30 | // VerifyConnection 验证连接 31 | func (l *VerifyConnectionLogic) VerifyConnection(in *pb.VerifyConnectionReq) (*pb.VerifyConnectionResp, error) { 32 | 33 | return &pb.VerifyConnectionResp{}, nil 34 | } 35 | 36 | func (l *VerifyConnectionLogic) VerifyConnection_(connection *Connection, in *pb.VerifyConnectionReq) (*pb.VerifyConnectionResp, error) { 37 | ecdh := utils.NewECDH(elliptic.P256()) 38 | publicKey, ok := ecdh.Unmarshal(in.PublicKey) 39 | if !ok { 40 | return &pb.VerifyConnectionResp{}, errors.New(i18n.PublicKeyError) 41 | } 42 | connection.PublicKeyLock.Lock() 43 | connection.ClientPublicKey = publicKey 44 | connection.PublicKeyLock.Unlock() 45 | oldHeader := connection.GetHeader() 46 | connection.headerLock.Lock() 47 | connection.header = &pb.RequestHeader{ 48 | AppId: in.Header.AppId, 49 | UserId: "", 50 | ClientIp: oldHeader.ClientIp, 51 | InstallId: in.Header.InstallId, 52 | Platform: in.Header.Platform, 53 | DeviceModel: in.Header.DeviceModel, 54 | OsVersion: in.Header.OsVersion, 55 | AppVersion: in.Header.AppVersion, 56 | Extra: in.Header.Extra, 57 | } 58 | connection.headerLock.Unlock() 59 | return &pb.VerifyConnectionResp{ 60 | PublicKey: ecdh.Marshal(connection.ServerPublicKey), 61 | }, nil 62 | } 63 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/x_connection_p2p.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | "github.com/cherish-chat/xxim-server/common/utils" 9 | "github.com/pion/webrtc/v2" 10 | "github.com/zeromicro/go-zero/core/logx" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | type p2pWrapper struct { 16 | dataChannel *webrtc.DataChannel 17 | } 18 | 19 | func (w *p2pWrapper) SendMessage(ctx context.Context, message []byte) error { 20 | return w.dataChannel.Send(message) 21 | } 22 | 23 | func (w *p2pWrapper) CloseConnection(code pb.WebsocketCustomCloseCode, reason string) { 24 | logx.Infof("p2pWrapper CloseConnection code: %d, reason: %s", code, reason) 25 | _ = w.dataChannel.Close() 26 | } 27 | 28 | func NewP2pConnection( 29 | ctx context.Context, 30 | header *pb.RequestHeader, 31 | dataChannel *webrtc.DataChannel, 32 | ) *Connection { 33 | ecdh := utils.NewECDH(elliptic.P256()) 34 | privateKey, publicKey, _ := ecdh.GenerateKey(rand.Reader) 35 | 36 | return &Connection{ 37 | ctx: ctx, 38 | header: header, 39 | headerLock: sync.RWMutex{}, 40 | ServerPrivateKey: privateKey, 41 | ServerPublicKey: publicKey, 42 | ClientPublicKey: nil, 43 | PublicKeyLock: sync.RWMutex{}, 44 | Connection: &p2pWrapper{ 45 | dataChannel: dataChannel, 46 | }, 47 | ConnectedTime: time.Now(), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/gateway/internal/logic/gatewayservice/x_connection_ws.go: -------------------------------------------------------------------------------- 1 | package gatewayservicelogic 2 | 3 | import ( 4 | "context" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | "github.com/cherish-chat/xxim-server/common/utils" 9 | "nhooyr.io/websocket" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | type websocketWrapper struct { 15 | conn *websocket.Conn 16 | } 17 | 18 | func (w *websocketWrapper) SendMessage(ctx context.Context, message []byte) error { 19 | return w.conn.Write(ctx, websocket.MessageBinary, message) 20 | } 21 | 22 | func (w *websocketWrapper) CloseConnection(code pb.WebsocketCustomCloseCode, reason string) { 23 | _ = w.conn.Close(websocket.StatusCode(code), reason) 24 | } 25 | 26 | // NewWebsocketConnect 创建websocket连接 27 | func NewWebsocketConnect( 28 | ctx context.Context, 29 | header *pb.RequestHeader, 30 | conn *websocket.Conn, 31 | ) *Connection { 32 | ecdh := utils.NewECDH(elliptic.P256()) 33 | privateKey, publicKey, _ := ecdh.GenerateKey(rand.Reader) 34 | 35 | connection := &Connection{ 36 | ctx: ctx, 37 | header: header, 38 | headerLock: sync.RWMutex{}, 39 | ServerPrivateKey: privateKey, 40 | ServerPublicKey: publicKey, 41 | ClientPublicKey: nil, 42 | PublicKeyLock: sync.RWMutex{}, 43 | Connection: &websocketWrapper{conn: conn}, 44 | ConnectedTime: time.Now(), 45 | } 46 | 47 | return connection 48 | } 49 | -------------------------------------------------------------------------------- /app/gateway/internal/middleware/aes.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | /* 4 | import ( 5 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 6 | "github.com/cherish-chat/xxim-server/common/utils" 7 | "github.com/gin-gonic/gin" 8 | "io" 9 | ) 10 | 11 | func Aes(svcCtx *svc.ServiceContext) gin.HandlerFunc { 12 | if !svcCtx.Config.Http.Encrypt.Enable { 13 | return func(c *gin.Context) { 14 | c.Next() 15 | } 16 | } else { 17 | aesKey := svcCtx.Config.Http.Encrypt.AesKey 18 | aesIv := svcCtx.Config.Http.Encrypt.AesIv 19 | return func(c *gin.Context) { 20 | // 把请求体解密 21 | var reqBody []byte 22 | // 是否没有请求体 23 | if c.Request.ContentLength == 0 { 24 | // 不必解密 25 | } else { 26 | bodyBytes, err := io.ReadAll(c.Request.Body) 27 | if err != nil { 28 | c.AbortWithError(500, err) 29 | return 30 | } 31 | reqBody = bodyBytes 32 | } 33 | if len(reqBody) > 0 { 34 | decryptedBytes, err := utils.Aes.Decrypt(aesKey, aesIv, reqBody) 35 | if err != nil { 36 | c.AbortWithError(500, err) 37 | return 38 | } 39 | c.Request.Body = io.NopCloser(utils.Bytes.NewNopCloser(decryptedBytes)) 40 | } 41 | rw := &responseWriter{ 42 | ResponseWriter: c.Writer, 43 | aesKey: aesKey, 44 | aesIv: aesIv, 45 | } 46 | c.Writer = rw 47 | c.Next() 48 | } 49 | } 50 | } 51 | 52 | type responseWriter struct { 53 | gin.ResponseWriter 54 | aesIv string 55 | aesKey string 56 | } 57 | 58 | func (w *responseWriter) Write(b []byte) (int, error) { 59 | encrypt := utils.Aes.Encrypt(w.aesKey, w.aesIv, b) 60 | return w.ResponseWriter.Write(encrypt) 61 | } 62 | */ 63 | -------------------------------------------------------------------------------- /app/gateway/internal/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func Cors(svcCtx *svc.ServiceContext) gin.HandlerFunc { 12 | if !svcCtx.Config.Http.Cors.Enable { 13 | return func(c *gin.Context) { 14 | c.Next() 15 | } 16 | } else { 17 | config := svcCtx.Config.Http.Cors 18 | return func(c *gin.Context) { 19 | method := c.Request.Method 20 | c.Header("Access-Control-Allow-Origin", strings.Join(config.AllowOrigins, ",")) 21 | c.Header("Access-Control-Allow-Headers", strings.Join(config.AllowHeaders, ",")) 22 | c.Header("Access-Control-Allow-Methods", strings.Join(config.AllowMethods, ",")) 23 | c.Header("Access-Control-Expose-Headers", strings.Join(config.ExposeHeaders, ",")) 24 | c.Header("Access-Control-Allow-Credentials", strconv.FormatBool(config.AllowCredentials)) 25 | if method == "OPTIONS" { 26 | c.AbortWithStatus(http.StatusNoContent) 27 | return 28 | } 29 | c.Next() 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/gateway/internal/server/httpServer.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cherish-chat/xxim-server/app/gateway/internal/handler" 6 | gatewayservicelogic "github.com/cherish-chat/xxim-server/app/gateway/internal/logic/gatewayservice" 7 | "github.com/cherish-chat/xxim-server/app/gateway/internal/middleware" 8 | "github.com/cherish-chat/xxim-server/app/gateway/internal/svc" 9 | "github.com/gin-gonic/gin" 10 | "github.com/zeromicro/go-zero/core/logx" 11 | "os" 12 | ) 13 | 14 | type logxWriter struct { 15 | } 16 | 17 | func (l *logxWriter) Write(p []byte) (n int, err error) { 18 | logx.Infof("%s", p) 19 | return len(p), nil 20 | } 21 | 22 | func NewHttpServer(svcCtx *svc.ServiceContext) *HttpServer { 23 | s := &HttpServer{svcCtx: svcCtx} 24 | gin.DefaultWriter = new(logxWriter) 25 | s.ginEngine = gin.New() 26 | s.ginEngine.Use( 27 | middleware.Tracing(svcCtx), // 链路追踪 28 | middleware.Logger(svcCtx), // 访问日志 29 | gin.Recovery(), // panic 恢复 30 | middleware.Cors(svcCtx), // 跨域 31 | middleware.ApiLog(svcCtx), // api 日志 32 | ) 33 | handler.SetupRoutes(s.svcCtx, s.ginEngine) 34 | gatewayservicelogic.InitConnectionLogic(s.svcCtx) 35 | if s.svcCtx.Config.Mode != "pro" { 36 | gin.SetMode(gin.DebugMode) 37 | } else { 38 | gin.SetMode(gin.ReleaseMode) 39 | } 40 | return s 41 | } 42 | 43 | type HttpServer struct { 44 | svcCtx *svc.ServiceContext 45 | ginEngine *gin.Engine 46 | } 47 | 48 | func (s *HttpServer) Start() { 49 | listenOn := fmt.Sprintf("%s:%d", s.svcCtx.Config.Http.Host, s.svcCtx.Config.Http.Port) 50 | logx.Infof("http server start at %s", listenOn) 51 | err := s.ginEngine.Run(listenOn) 52 | if err != nil { 53 | logx.Errorf("ginEngine.Run error: %v", err) 54 | os.Exit(1) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/gateway/internal/svc/serviceContext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/app/conversation/client/friendservice" 5 | "github.com/cherish-chat/xxim-server/app/conversation/client/groupservice" 6 | "github.com/cherish-chat/xxim-server/app/gateway/client/gatewayservice" 7 | "github.com/cherish-chat/xxim-server/app/gateway/internal/config" 8 | "github.com/cherish-chat/xxim-server/app/message/client/messageservice" 9 | "github.com/cherish-chat/xxim-server/app/message/client/noticeservice" 10 | "github.com/cherish-chat/xxim-server/app/user/client/accountservice" 11 | "github.com/cherish-chat/xxim-server/app/user/client/callbackservice" 12 | "github.com/cherish-chat/xxim-server/app/user/client/infoservice" 13 | "github.com/cherish-chat/xxim-server/common/xcache" 14 | "github.com/zeromicro/go-zero/core/stores/redis" 15 | "github.com/zeromicro/go-zero/zrpc" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | type ServiceContext struct { 21 | Config config.Config 22 | Redis *redis.Redis 23 | 24 | gatewayService gatewayservice.GatewayService 25 | //User 26 | CallbackService callbackservice.CallbackService 27 | AccountService accountservice.AccountService 28 | InfoService infoservice.InfoService 29 | //Conversation 30 | FriendService friendservice.FriendService 31 | GroupService groupservice.GroupService 32 | //Message 33 | NoticeService noticeservice.NoticeService 34 | MessageService messageservice.MessageService 35 | } 36 | 37 | func NewServiceContext(c config.Config) *ServiceContext { 38 | 39 | userClient := zrpc.MustNewClient( 40 | c.RpcClientConf.User, 41 | zrpc.WithNonBlock(), 42 | zrpc.WithTimeout(time.Duration(c.Timeout)*time.Millisecond), 43 | ) 44 | conversationClient := zrpc.MustNewClient( 45 | c.RpcClientConf.Conversation, 46 | zrpc.WithNonBlock(), 47 | zrpc.WithTimeout(time.Duration(c.Timeout)*time.Millisecond), 48 | ) 49 | messageClient := zrpc.MustNewClient( 50 | c.RpcClientConf.Message, 51 | zrpc.WithNonBlock(), 52 | zrpc.WithTimeout(time.Duration(c.Timeout)*time.Millisecond), 53 | ) 54 | 55 | s := &ServiceContext{ 56 | Config: c, 57 | CallbackService: callbackservice.NewCallbackService(userClient), 58 | AccountService: accountservice.NewAccountService(userClient), 59 | InfoService: infoservice.NewInfoService(userClient), 60 | FriendService: friendservice.NewFriendService(conversationClient), 61 | GroupService: groupservice.NewGroupService(conversationClient), 62 | NoticeService: noticeservice.NewNoticeService(messageClient), 63 | MessageService: messageservice.NewMessageService(messageClient), 64 | Redis: xcache.MustNewRedis(c.RedisConf), 65 | } 66 | return s 67 | } 68 | 69 | func (s *ServiceContext) GatewayService() gatewayservice.GatewayService { 70 | if s.gatewayService == nil { 71 | listenOnSplit := strings.Split(s.Config.ListenOn, ":") 72 | rpcPort := listenOnSplit[len(listenOnSplit)-1] 73 | s.gatewayService = gatewayservice.MustNewGatewayService(zrpc.RpcClientConf{ 74 | Endpoints: []string{"127.0.0.1:" + rpcPort}, 75 | NonBlock: true, 76 | }) 77 | } 78 | return s.gatewayService 79 | } 80 | -------------------------------------------------------------------------------- /app/gateway/internal/types/loogConnection.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/common/pb" 6 | ) 7 | 8 | type LongConnection interface { 9 | //发送消息 10 | SendMessage(ctx context.Context, message []byte) error 11 | //关闭连接 12 | CloseConnection(code pb.WebsocketCustomCloseCode, reason string) 13 | } 14 | -------------------------------------------------------------------------------- /app/gateway/internal/types/response.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | "google.golang.org/protobuf/proto" 6 | ) 7 | 8 | func MarshalResponse(data proto.Message) []byte { 9 | protobuf, _ := proto.Marshal(data) 10 | return protobuf 11 | } 12 | 13 | func MarshalWriteData(data *pb.GatewayApiResponse) []byte { 14 | writeData := &pb.GatewayWriteDataContent{ 15 | DataType: pb.GatewayWriteDataType_Response, 16 | Response: data, 17 | Message: nil, 18 | Notice: nil, 19 | } 20 | protobuf, _ := proto.Marshal(writeData) 21 | return protobuf 22 | } 23 | -------------------------------------------------------------------------------- /app/gateway/pb/common.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/common.proto -------------------------------------------------------------------------------- /app/gateway/pb/gateway.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/gateway.proto -------------------------------------------------------------------------------- /app/message/README.md: -------------------------------------------------------------------------------- 1 | # msg 消息服务 2 | 3 | - 该服务负责消息的相关逻辑 4 | - 消息是指用户之间的消息,和notice的区别是,msg允许按需拉取,但notice必须是顺序的,序列不能中断 5 | -------------------------------------------------------------------------------- /app/message/client/noticeservice/noticeService.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: message.proto 3 | 4 | package noticeservice 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/zrpc" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type ( 16 | ListNoticeReq = pb.ListNoticeReq 17 | ListNoticeReq_Conversation = pb.ListNoticeReq_Conversation 18 | ListNoticeResp = pb.ListNoticeResp 19 | MessageBatchSendReq = pb.MessageBatchSendReq 20 | MessageBatchSendResp = pb.MessageBatchSendResp 21 | MessageContentText = pb.MessageContentText 22 | MessageContentText_Item = pb.MessageContentText_Item 23 | MessageContentText_Item_At = pb.MessageContentText_Item_At 24 | MessageContentText_Item_Image = pb.MessageContentText_Item_Image 25 | MessageInsertReq = pb.MessageInsertReq 26 | MessageInsertResp = pb.MessageInsertResp 27 | MessagePushReq = pb.MessagePushReq 28 | MessagePushResp = pb.MessagePushResp 29 | MessageSendReq = pb.MessageSendReq 30 | MessageSendResp = pb.MessageSendResp 31 | NoticeBatchSendReq = pb.NoticeBatchSendReq 32 | NoticeBatchSendResp = pb.NoticeBatchSendResp 33 | NoticeContentJoinNewGroup = pb.NoticeContentJoinNewGroup 34 | NoticeContentNewFriendRequest = pb.NoticeContentNewFriendRequest 35 | NoticeContentOnlineStatus = pb.NoticeContentOnlineStatus 36 | NoticeSendReq = pb.NoticeSendReq 37 | NoticeSendResp = pb.NoticeSendResp 38 | 39 | NoticeService interface { 40 | // NoticeSend 通知发送 41 | NoticeSend(ctx context.Context, in *NoticeSendReq, opts ...grpc.CallOption) (*NoticeSendResp, error) 42 | // NoticeBatchSend 通知批量发送 43 | NoticeBatchSend(ctx context.Context, in *NoticeBatchSendReq, opts ...grpc.CallOption) (*NoticeBatchSendResp, error) 44 | // ListNotice 获取通知列表 45 | ListNotice(ctx context.Context, in *ListNoticeReq, opts ...grpc.CallOption) (*ListNoticeResp, error) 46 | } 47 | 48 | defaultNoticeService struct { 49 | cli zrpc.Client 50 | } 51 | ) 52 | 53 | func NewNoticeService(cli zrpc.Client) NoticeService { 54 | return &defaultNoticeService{ 55 | cli: cli, 56 | } 57 | } 58 | 59 | // NoticeSend 通知发送 60 | func (m *defaultNoticeService) NoticeSend(ctx context.Context, in *NoticeSendReq, opts ...grpc.CallOption) (*NoticeSendResp, error) { 61 | client := pb.NewNoticeServiceClient(m.cli.Conn()) 62 | return client.NoticeSend(ctx, in, opts...) 63 | } 64 | 65 | // NoticeBatchSend 通知批量发送 66 | func (m *defaultNoticeService) NoticeBatchSend(ctx context.Context, in *NoticeBatchSendReq, opts ...grpc.CallOption) (*NoticeBatchSendResp, error) { 67 | client := pb.NewNoticeServiceClient(m.cli.Conn()) 68 | return client.NoticeBatchSend(ctx, in, opts...) 69 | } 70 | 71 | // ListNotice 获取通知列表 72 | func (m *defaultNoticeService) ListNotice(ctx context.Context, in *ListNoticeReq, opts ...grpc.CallOption) (*ListNoticeResp, error) { 73 | client := pb.NewNoticeServiceClient(m.cli.Conn()) 74 | return client.ListNotice(ctx, in, opts...) 75 | } 76 | -------------------------------------------------------------------------------- /app/message/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/xmgo" 5 | "github.com/zeromicro/go-zero/core/stores/redis" 6 | "github.com/zeromicro/go-zero/zrpc" 7 | ) 8 | 9 | type Config struct { 10 | zrpc.RpcServerConf 11 | RedisConf redis.RedisConf 12 | RpcClientConf struct { 13 | Dispatch zrpc.RpcClientConf 14 | User zrpc.RpcClientConf 15 | Conversation zrpc.RpcClientConf 16 | Third zrpc.RpcClientConf 17 | Message zrpc.RpcClientConf 18 | Gateway zrpc.RpcClientConf 19 | } 20 | MongoCollection struct { 21 | BroadcastNotice xmgo.MongoCollectionConf 22 | SubscriptionNotice xmgo.MongoCollectionConf 23 | SubscriptionNoticeContent xmgo.MongoCollectionConf 24 | Message xmgo.MongoCollectionConf 25 | } 26 | SendMsgLimiter struct { 27 | Key string `json:",default=send_msg_limiter"` 28 | Rate int `json:",default=50"` //每秒钟生成的令牌数 29 | Burst int `json:",default=100"` //令牌桶的容量 30 | } 31 | //InsertMsgBuffer 插入消息缓冲区 32 | InsertMsgBuffer struct { 33 | Size int `json:",default=1000"` // 缓冲区大小 34 | LoopInterval int `json:",default=100"` // 循环间隔 单位(ms) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/message/internal/logic/messageservice/messageBatchSendLogic.go: -------------------------------------------------------------------------------- 1 | package messageservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "github.com/cherish-chat/xxim-server/common/xmq" 7 | 8 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type MessageBatchSendLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewMessageBatchSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MessageBatchSendLogic { 21 | return &MessageBatchSendLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | // MessageBatchSend 批量发送消息 29 | func (l *MessageBatchSendLogic) MessageBatchSend(in *pb.MessageBatchSendReq) (*pb.MessageBatchSendResp, error) { 30 | //验证 31 | var ( 32 | resp *pb.MessageBatchSendResp 33 | err error 34 | ) 35 | { 36 | resp, err = l.validate(in) 37 | if err != nil { 38 | return resp, err 39 | } 40 | if resp.GetHeader().GetCode() != pb.ResponseCode_SUCCESS { 41 | return resp, nil 42 | } 43 | } 44 | //判断是否禁走队列 45 | if !in.DisableQueue { 46 | if !l.svcCtx.SendMsgTokenLimiter.AllowCtx(l.ctx) { 47 | err := l.svcCtx.MQ.Produce(l.ctx, xmq.TopicMessageBatchSend, utils.Json.MarshalToBytes(&pb.MessageInsertReq{ 48 | Header: in.Header, 49 | Messages: in.Messages, 50 | })) 51 | if err != nil { 52 | l.Errorf("produce message error: %v", err) 53 | return &pb.MessageBatchSendResp{}, err 54 | } 55 | return &pb.MessageBatchSendResp{}, nil 56 | } 57 | } 58 | 59 | //直接插入 60 | _, _ = NewMessageInsertLogic(l.ctx, l.svcCtx).MessageInsert(&pb.MessageInsertReq{ 61 | Header: in.Header, 62 | Messages: in.Messages, 63 | }) 64 | return &pb.MessageBatchSendResp{}, nil 65 | } 66 | 67 | // validate 验证是否允许发送消息 68 | func (l *MessageBatchSendLogic) validate(in *pb.MessageBatchSendReq) (*pb.MessageBatchSendResp, error) { 69 | // TODO: 验证是否允许发送消息 70 | return &pb.MessageBatchSendResp{}, nil 71 | } 72 | -------------------------------------------------------------------------------- /app/message/internal/logic/messageservice/messageSendLogic.go: -------------------------------------------------------------------------------- 1 | package messageservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type MessageSendLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewMessageSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MessageSendLogic { 19 | return &MessageSendLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // MessageSend 发送消息 27 | func (l *MessageSendLogic) MessageSend(in *pb.MessageSendReq) (*pb.MessageSendResp, error) { 28 | messageBatchSendResp, err := NewMessageBatchSendLogic(l.ctx, l.svcCtx).MessageBatchSend(&pb.MessageBatchSendReq{ 29 | Header: in.Header, 30 | Messages: []*pb.Message{in.Message}, 31 | DisableQueue: in.DisableQueue, 32 | }) 33 | var header *pb.ResponseHeader 34 | if messageBatchSendResp != nil { 35 | header = messageBatchSendResp.GetHeader() 36 | } 37 | return &pb.MessageSendResp{ 38 | Header: header, 39 | }, err 40 | } 41 | -------------------------------------------------------------------------------- /app/message/internal/logic/noticeservice/noticeBatchSendLogic.go: -------------------------------------------------------------------------------- 1 | package noticeservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "github.com/cherish-chat/xxim-server/common/xmq" 7 | 8 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type NoticeBatchSendLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewNoticeBatchSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NoticeBatchSendLogic { 21 | return &NoticeBatchSendLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | // NoticeBatchSend 通知批量发送 29 | func (l *NoticeBatchSendLogic) NoticeBatchSend(in *pb.NoticeBatchSendReq) (*pb.NoticeBatchSendResp, error) { 30 | err := l.svcCtx.MQ.Produce(l.ctx, xmq.TopicNoticeBatchSend, utils.Json.MarshalToBytes(in)) 31 | if err != nil { 32 | l.Errorf("produce message error: %v", err) 33 | return &pb.NoticeBatchSendResp{}, err 34 | } 35 | return &pb.NoticeBatchSendResp{}, nil 36 | } 37 | -------------------------------------------------------------------------------- /app/message/internal/logic/noticeservice/noticeSendLogic.go: -------------------------------------------------------------------------------- 1 | package noticeservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "github.com/cherish-chat/xxim-server/common/xmq" 7 | 8 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type NoticeSendLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewNoticeSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NoticeSendLogic { 21 | return &NoticeSendLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | // NoticeSend 通知发送 29 | func (l *NoticeSendLogic) NoticeSend(in *pb.NoticeSendReq) (*pb.NoticeSendResp, error) { 30 | req := &pb.NoticeBatchSendReq{ 31 | Header: in.Header, 32 | Notices: []*pb.NoticeSendReq{in}, 33 | } 34 | err := l.svcCtx.MQ.Produce(l.ctx, xmq.TopicNoticeBatchSend, utils.Json.MarshalToBytes(req)) 35 | if err != nil { 36 | l.Errorf("produce message error: %v", err) 37 | return &pb.NoticeSendResp{}, err 38 | } 39 | return &pb.NoticeSendResp{}, nil 40 | } 41 | -------------------------------------------------------------------------------- /app/message/internal/server/consumerServer.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | messageservicelogic "github.com/cherish-chat/xxim-server/app/message/internal/logic/messageservice" 6 | noticeservicelogic "github.com/cherish-chat/xxim-server/app/message/internal/logic/noticeservice" 7 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 8 | "github.com/cherish-chat/xxim-server/common/xmq" 9 | ) 10 | 11 | type ConsumerServer struct { 12 | svcCtx *svc.ServiceContext 13 | } 14 | 15 | func NewConsumerServer(svcCtx *svc.ServiceContext) *ConsumerServer { 16 | return &ConsumerServer{svcCtx: svcCtx} 17 | } 18 | 19 | func (s *ConsumerServer) Start() { 20 | s.svcCtx.MQ.RegisterHandler(xmq.TopicNoticeBatchSend, func(ctx context.Context, topic string, msg []byte) error { 21 | return noticeservicelogic.NewConsumerLogic(ctx, s.svcCtx).NoticeBatchSend(topic, msg) 22 | }) 23 | s.svcCtx.MQ.RegisterHandler(xmq.TopicMessageBatchSend, func(ctx context.Context, topic string, msg []byte) error { 24 | return messageservicelogic.NewMessageInsertLogic(ctx, s.svcCtx).ConsumeMessage(topic, msg) 25 | }) 26 | go s.svcCtx.MQ.StartConsuming() 27 | } 28 | -------------------------------------------------------------------------------- /app/message/internal/server/messageservice/messageServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: message.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/message/internal/logic/messageservice" 10 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type MessageServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedMessageServiceServer 17 | } 18 | 19 | func NewMessageServiceServer(svcCtx *svc.ServiceContext) *MessageServiceServer { 20 | return &MessageServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // MessageInsert 插入消息 26 | func (s *MessageServiceServer) MessageInsert(ctx context.Context, in *pb.MessageInsertReq) (*pb.MessageInsertResp, error) { 27 | l := messageservicelogic.NewMessageInsertLogic(ctx, s.svcCtx) 28 | return l.MessageInsert(in) 29 | } 30 | 31 | // MessageSend 发送消息 32 | func (s *MessageServiceServer) MessageSend(ctx context.Context, in *pb.MessageSendReq) (*pb.MessageSendResp, error) { 33 | l := messageservicelogic.NewMessageSendLogic(ctx, s.svcCtx) 34 | return l.MessageSend(in) 35 | } 36 | 37 | // MessageBatchSend 批量发送消息 38 | func (s *MessageServiceServer) MessageBatchSend(ctx context.Context, in *pb.MessageBatchSendReq) (*pb.MessageBatchSendResp, error) { 39 | l := messageservicelogic.NewMessageBatchSendLogic(ctx, s.svcCtx) 40 | return l.MessageBatchSend(in) 41 | } 42 | 43 | // MessagePush 推送消息 44 | func (s *MessageServiceServer) MessagePush(ctx context.Context, in *pb.MessagePushReq) (*pb.MessagePushResp, error) { 45 | l := messageservicelogic.NewMessagePushLogic(ctx, s.svcCtx) 46 | return l.MessagePush(in) 47 | } 48 | -------------------------------------------------------------------------------- /app/message/internal/server/noticeservice/noticeServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: message.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/message/internal/logic/noticeservice" 10 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type NoticeServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedNoticeServiceServer 17 | } 18 | 19 | func NewNoticeServiceServer(svcCtx *svc.ServiceContext) *NoticeServiceServer { 20 | return &NoticeServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // NoticeSend 通知发送 26 | func (s *NoticeServiceServer) NoticeSend(ctx context.Context, in *pb.NoticeSendReq) (*pb.NoticeSendResp, error) { 27 | l := noticeservicelogic.NewNoticeSendLogic(ctx, s.svcCtx) 28 | return l.NoticeSend(in) 29 | } 30 | 31 | // NoticeBatchSend 通知批量发送 32 | func (s *NoticeServiceServer) NoticeBatchSend(ctx context.Context, in *pb.NoticeBatchSendReq) (*pb.NoticeBatchSendResp, error) { 33 | l := noticeservicelogic.NewNoticeBatchSendLogic(ctx, s.svcCtx) 34 | return l.NoticeBatchSend(in) 35 | } 36 | 37 | // ListNotice 获取通知列表 38 | func (s *NoticeServiceServer) ListNotice(ctx context.Context, in *pb.ListNoticeReq) (*pb.ListNoticeResp, error) { 39 | l := noticeservicelogic.NewListNoticeLogic(ctx, s.svcCtx) 40 | return l.ListNotice(in) 41 | } 42 | -------------------------------------------------------------------------------- /app/message/message.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/cherish-chat/xxim-server/app/message/internal/server" 6 | "github.com/zeromicro/go-zero/core/logx" 7 | 8 | "github.com/cherish-chat/xxim-server/app/message/internal/config" 9 | messageserviceServer "github.com/cherish-chat/xxim-server/app/message/internal/server/messageservice" 10 | noticeserviceServer "github.com/cherish-chat/xxim-server/app/message/internal/server/noticeservice" 11 | "github.com/cherish-chat/xxim-server/app/message/internal/svc" 12 | "github.com/cherish-chat/xxim-server/common/pb" 13 | 14 | "github.com/zeromicro/go-zero/core/conf" 15 | "github.com/zeromicro/go-zero/core/service" 16 | "github.com/zeromicro/go-zero/zrpc" 17 | "google.golang.org/grpc" 18 | "google.golang.org/grpc/reflection" 19 | ) 20 | 21 | var configFile = flag.String("f", "etc/message.yaml", "the config file") 22 | 23 | func main() { 24 | flag.Parse() 25 | 26 | var c config.Config 27 | conf.MustLoad(*configFile, &c) 28 | ctx := svc.NewServiceContext(c) 29 | consumerServer := server.NewConsumerServer(ctx) 30 | 31 | consumerServer.Start() 32 | 33 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 34 | pb.RegisterMessageServiceServer(grpcServer, messageserviceServer.NewMessageServiceServer(ctx)) 35 | pb.RegisterNoticeServiceServer(grpcServer, noticeserviceServer.NewNoticeServiceServer(ctx)) 36 | 37 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 38 | reflection.Register(grpcServer) 39 | } 40 | }) 41 | defer s.Stop() 42 | 43 | logx.Infof("Starting rpc server at %s...\n", c.ListenOn) 44 | s.Start() 45 | } 46 | -------------------------------------------------------------------------------- /app/message/messagemodel/message_test.go: -------------------------------------------------------------------------------- 1 | package messagemodel 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/utils" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestGenerateMessageId(t *testing.T) { 10 | type args struct { 11 | senderUserId string 12 | targetId string 13 | targetType TargetType 14 | seq int64 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want string 20 | }{ 21 | { 22 | name: "test1", 23 | args: args{ 24 | senderUserId: "1234567890abcdef1234567890abcdef", 25 | targetId: "1234567890abcdef1234567890abcdef", 26 | targetType: 1, 27 | seq: 1234567890123456789, 28 | }, 29 | want: utils.Md5("senderUserId=1234567890abcdef1234567890abcdef&seq=1234567890123456789&targetId=1234567890abcdef1234567890abcdef&targetType=1"), 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | if got := GenerateMessageId(tt.args.senderUserId, tt.args.targetId, tt.args.targetType, tt.args.seq); !reflect.DeepEqual(got, tt.want) { 35 | t.Errorf("GenerateMessageId() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/message/noticemodel/broadcastnotice.go: -------------------------------------------------------------------------------- 1 | package noticemodel 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | opts "github.com/qiniu/qmgo/options" 6 | "go.mongodb.org/mongo-driver/bson/primitive" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | ) 9 | 10 | type ConversationType = pb.ConversationType 11 | type NoticeContentType = pb.NoticeContentType 12 | 13 | // BroadcastNotice 广播通知 数据库模型 14 | // BroadcastNotice 是一个特殊的消息,用于通知用户,例如:邀请进群了、被踢出群了、群解散了、营销号推送了一条消息等等 15 | // 这种消息的特点 具有时效性,例如:发送了一条邀请进群的通知,但此时群已经解散了,那么这条通知就没有意义了,所以收到这种通知后,客户端应该同步服务端信息,再做判断 16 | type BroadcastNotice struct { 17 | NoticeId string `bson:"noticeId" json:"noticeId"` 18 | ConversationId string `bson:"conversationId" json:"conversationId"` 19 | ConversationType ConversationType `bson:"conversationType" json:"conversationType"` 20 | Content string `bson:"content" json:"content"` 21 | ContentType NoticeContentType `bson:"contentType" json:"contentType"` 22 | UpdateTime primitive.DateTime `bson:"updateTime" json:"updateTime"` 23 | Sort int64 `bson:"sort" json:"sort"` 24 | } 25 | 26 | func (m *BroadcastNotice) GetIndexes() []opts.IndexModel { 27 | return []opts.IndexModel{{ 28 | Key: []string{"noticeId"}, 29 | IndexOptions: options.Index().SetUnique(true), 30 | }, { 31 | Key: []string{"-sort"}, 32 | }, { 33 | Key: []string{"conversationId", "conversationType"}, 34 | }, { 35 | Key: []string{"conversationId", "conversationType", "contentType"}, 36 | IndexOptions: options.Index().SetUnique(true), 37 | }} 38 | } 39 | 40 | func (m *BroadcastNotice) ToPb() *pb.Notice { 41 | return &pb.Notice{ 42 | NoticeId: m.NoticeId, 43 | ConversationId: m.ConversationId, 44 | ConversationType: m.ConversationType, 45 | Content: m.Content, 46 | ContentType: m.ContentType, 47 | UpdateTime: int64(m.UpdateTime), 48 | Sort: m.Sort, 49 | } 50 | } 51 | 52 | func BroadcastNoticeFromPb(in *pb.Notice) *BroadcastNotice { 53 | return &BroadcastNotice{ 54 | NoticeId: in.NoticeId, 55 | ConversationId: in.ConversationId, 56 | ConversationType: in.ConversationType, 57 | Content: in.Content, 58 | ContentType: in.ContentType, 59 | UpdateTime: primitive.DateTime(in.UpdateTime), 60 | Sort: in.Sort, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/message/noticemodel/subscriptionnotice.go: -------------------------------------------------------------------------------- 1 | package noticemodel 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | opts "github.com/qiniu/qmgo/options" 6 | "go.mongodb.org/mongo-driver/bson/primitive" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | ) 9 | 10 | // SubscriptionNotice 订阅号通知 数据库模型 11 | type SubscriptionNotice struct { 12 | UserId string `bson:"userId" json:"userId"` 13 | SubscriptionId string `bson:"subscriptionId" json:"subscriptionId"` 14 | ContentType NoticeContentType `bson:"contentType" json:"contentType"` 15 | ContentId string `bson:"contentId" json:"contentId"` 16 | UpdateTime primitive.DateTime `bson:"updateTime" json:"updateTime"` 17 | Sort int64 `bson:"sort" json:"sort"` 18 | } 19 | 20 | type SubscriptionNoticeContent struct { 21 | ContentId string `bson:"contentId" json:"contentId"` 22 | Content string `bson:"content" json:"content"` 23 | } 24 | 25 | func (m *SubscriptionNotice) GetIndexes() []opts.IndexModel { 26 | return []opts.IndexModel{{ 27 | Key: []string{"subscriptionId", "userId", "contentType"}, 28 | IndexOptions: options.Index().SetUnique(true), 29 | }, { 30 | Key: []string{"-sort"}, 31 | }, { 32 | Key: []string{"subscriptionId"}, 33 | }, { 34 | Key: []string{"noticeType"}, 35 | IndexOptions: nil, 36 | }} 37 | } 38 | 39 | func (m *SubscriptionNotice) ToPb(content string) *pb.Notice { 40 | return &pb.Notice{ 41 | //NoticeId: "", 42 | ConversationId: m.SubscriptionId, 43 | ConversationType: pb.ConversationType_Subscription, 44 | Content: content, 45 | ContentType: m.ContentType, 46 | UpdateTime: int64(m.UpdateTime), 47 | Sort: m.Sort, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/message/pb/common.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/common.proto -------------------------------------------------------------------------------- /app/message/pb/message.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/message.proto -------------------------------------------------------------------------------- /app/third/README.md: -------------------------------------------------------------------------------- 1 | # third 第三方服务 2 | 3 | - 该服务负责第三方的相关逻辑 4 | - 第三方服务包括:短信、推送、邮件、支付、登录 5 | -------------------------------------------------------------------------------- /app/third/client/captchaservice/captchaService.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: third.proto 3 | 4 | package captchaservice 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/zrpc" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type ( 16 | CaptchaVerifyReq = pb.CaptchaVerifyReq 17 | CaptchaVerifyResp = pb.CaptchaVerifyResp 18 | EmailCodeSendReq = pb.EmailCodeSendReq 19 | EmailCodeSendResp = pb.EmailCodeSendResp 20 | EmailCodeVerifyReq = pb.EmailCodeVerifyReq 21 | EmailCodeVerifyResp = pb.EmailCodeVerifyResp 22 | GetCaptchaReq = pb.GetCaptchaReq 23 | GetCaptchaResp = pb.GetCaptchaResp 24 | SmsCodeSendReq = pb.SmsCodeSendReq 25 | SmsCodeSendResp = pb.SmsCodeSendResp 26 | SmsCodeVerifyReq = pb.SmsCodeVerifyReq 27 | SmsCodeVerifyResp = pb.SmsCodeVerifyResp 28 | 29 | CaptchaService interface { 30 | // GetCaptcha 获取图形验证码 31 | GetCaptcha(ctx context.Context, in *GetCaptchaReq, opts ...grpc.CallOption) (*GetCaptchaResp, error) 32 | // CaptchaVerify 验证图形验证码 33 | CaptchaVerify(ctx context.Context, in *CaptchaVerifyReq, opts ...grpc.CallOption) (*CaptchaVerifyResp, error) 34 | } 35 | 36 | defaultCaptchaService struct { 37 | cli zrpc.Client 38 | } 39 | ) 40 | 41 | func NewCaptchaService(cli zrpc.Client) CaptchaService { 42 | return &defaultCaptchaService{ 43 | cli: cli, 44 | } 45 | } 46 | 47 | // GetCaptcha 获取图形验证码 48 | func (m *defaultCaptchaService) GetCaptcha(ctx context.Context, in *GetCaptchaReq, opts ...grpc.CallOption) (*GetCaptchaResp, error) { 49 | client := pb.NewCaptchaServiceClient(m.cli.Conn()) 50 | return client.GetCaptcha(ctx, in, opts...) 51 | } 52 | 53 | // CaptchaVerify 验证图形验证码 54 | func (m *defaultCaptchaService) CaptchaVerify(ctx context.Context, in *CaptchaVerifyReq, opts ...grpc.CallOption) (*CaptchaVerifyResp, error) { 55 | client := pb.NewCaptchaServiceClient(m.cli.Conn()) 56 | return client.CaptchaVerify(ctx, in, opts...) 57 | } 58 | -------------------------------------------------------------------------------- /app/third/client/emailservice/emailService.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: third.proto 3 | 4 | package emailservice 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/zrpc" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type ( 16 | CaptchaVerifyReq = pb.CaptchaVerifyReq 17 | CaptchaVerifyResp = pb.CaptchaVerifyResp 18 | EmailCodeSendReq = pb.EmailCodeSendReq 19 | EmailCodeSendResp = pb.EmailCodeSendResp 20 | EmailCodeVerifyReq = pb.EmailCodeVerifyReq 21 | EmailCodeVerifyResp = pb.EmailCodeVerifyResp 22 | GetCaptchaReq = pb.GetCaptchaReq 23 | GetCaptchaResp = pb.GetCaptchaResp 24 | SmsCodeSendReq = pb.SmsCodeSendReq 25 | SmsCodeSendResp = pb.SmsCodeSendResp 26 | SmsCodeVerifyReq = pb.SmsCodeVerifyReq 27 | SmsCodeVerifyResp = pb.SmsCodeVerifyResp 28 | 29 | EmailService interface { 30 | // EmailCodeSend 发送邮件 31 | EmailCodeSend(ctx context.Context, in *EmailCodeSendReq, opts ...grpc.CallOption) (*EmailCodeSendResp, error) 32 | // EmailCodeVerify 验证邮件 33 | EmailCodeVerify(ctx context.Context, in *EmailCodeVerifyReq, opts ...grpc.CallOption) (*EmailCodeVerifyResp, error) 34 | } 35 | 36 | defaultEmailService struct { 37 | cli zrpc.Client 38 | } 39 | ) 40 | 41 | func NewEmailService(cli zrpc.Client) EmailService { 42 | return &defaultEmailService{ 43 | cli: cli, 44 | } 45 | } 46 | 47 | // EmailCodeSend 发送邮件 48 | func (m *defaultEmailService) EmailCodeSend(ctx context.Context, in *EmailCodeSendReq, opts ...grpc.CallOption) (*EmailCodeSendResp, error) { 49 | client := pb.NewEmailServiceClient(m.cli.Conn()) 50 | return client.EmailCodeSend(ctx, in, opts...) 51 | } 52 | 53 | // EmailCodeVerify 验证邮件 54 | func (m *defaultEmailService) EmailCodeVerify(ctx context.Context, in *EmailCodeVerifyReq, opts ...grpc.CallOption) (*EmailCodeVerifyResp, error) { 55 | client := pb.NewEmailServiceClient(m.cli.Conn()) 56 | return client.EmailCodeVerify(ctx, in, opts...) 57 | } 58 | -------------------------------------------------------------------------------- /app/third/client/smsservice/smsService.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: third.proto 3 | 4 | package smsservice 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/zrpc" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type ( 16 | CaptchaVerifyReq = pb.CaptchaVerifyReq 17 | CaptchaVerifyResp = pb.CaptchaVerifyResp 18 | EmailCodeSendReq = pb.EmailCodeSendReq 19 | EmailCodeSendResp = pb.EmailCodeSendResp 20 | EmailCodeVerifyReq = pb.EmailCodeVerifyReq 21 | EmailCodeVerifyResp = pb.EmailCodeVerifyResp 22 | GetCaptchaReq = pb.GetCaptchaReq 23 | GetCaptchaResp = pb.GetCaptchaResp 24 | SmsCodeSendReq = pb.SmsCodeSendReq 25 | SmsCodeSendResp = pb.SmsCodeSendResp 26 | SmsCodeVerifyReq = pb.SmsCodeVerifyReq 27 | SmsCodeVerifyResp = pb.SmsCodeVerifyResp 28 | 29 | SmsService interface { 30 | // SmsCodeSend 发送短信 31 | SmsCodeSend(ctx context.Context, in *SmsCodeSendReq, opts ...grpc.CallOption) (*SmsCodeSendResp, error) 32 | // SmsCodeVerify 验证短信 33 | SmsCodeVerify(ctx context.Context, in *SmsCodeVerifyReq, opts ...grpc.CallOption) (*SmsCodeVerifyResp, error) 34 | } 35 | 36 | defaultSmsService struct { 37 | cli zrpc.Client 38 | } 39 | ) 40 | 41 | func NewSmsService(cli zrpc.Client) SmsService { 42 | return &defaultSmsService{ 43 | cli: cli, 44 | } 45 | } 46 | 47 | // SmsCodeSend 发送短信 48 | func (m *defaultSmsService) SmsCodeSend(ctx context.Context, in *SmsCodeSendReq, opts ...grpc.CallOption) (*SmsCodeSendResp, error) { 49 | client := pb.NewSmsServiceClient(m.cli.Conn()) 50 | return client.SmsCodeSend(ctx, in, opts...) 51 | } 52 | 53 | // SmsCodeVerify 验证短信 54 | func (m *defaultSmsService) SmsCodeVerify(ctx context.Context, in *SmsCodeVerifyReq, opts ...grpc.CallOption) (*SmsCodeVerifyResp, error) { 55 | client := pb.NewSmsServiceClient(m.cli.Conn()) 56 | return client.SmsCodeVerify(ctx, in, opts...) 57 | } 58 | -------------------------------------------------------------------------------- /app/third/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/core/stores/redis" 5 | "github.com/zeromicro/go-zero/zrpc" 6 | ) 7 | 8 | type Config struct { 9 | zrpc.RpcServerConf 10 | RedisConf redis.RedisConf 11 | RpcClientConf struct { 12 | Dispatch zrpc.RpcClientConf 13 | User zrpc.RpcClientConf 14 | Conversation zrpc.RpcClientConf 15 | Third zrpc.RpcClientConf 16 | Message zrpc.RpcClientConf 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/third/internal/logic/captchaservice/captchaVerifyLogic.go: -------------------------------------------------------------------------------- 1 | package captchaservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type CaptchaVerifyLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewCaptchaVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CaptchaVerifyLogic { 19 | return &CaptchaVerifyLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // CaptchaVerify 验证图形验证码 27 | func (l *CaptchaVerifyLogic) CaptchaVerify(in *pb.CaptchaVerifyReq) (*pb.CaptchaVerifyResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.CaptchaVerifyResp{Success: true}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/third/internal/logic/captchaservice/getCaptchaLogic.go: -------------------------------------------------------------------------------- 1 | package captchaservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GetCaptchaLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGetCaptchaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCaptchaLogic { 19 | return &GetCaptchaLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // GetCaptcha 获取图形验证码 27 | func (l *GetCaptchaLogic) GetCaptcha(in *pb.GetCaptchaReq) (*pb.GetCaptchaResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.GetCaptchaResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/third/internal/logic/emailservice/emailCodeSendLogic.go: -------------------------------------------------------------------------------- 1 | package emailservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type EmailCodeSendLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewEmailCodeSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *EmailCodeSendLogic { 19 | return &EmailCodeSendLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // EmailCodeSend 发送邮件 27 | func (l *EmailCodeSendLogic) EmailCodeSend(in *pb.EmailCodeSendReq) (*pb.EmailCodeSendResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.EmailCodeSendResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/third/internal/logic/emailservice/emailCodeVerifyLogic.go: -------------------------------------------------------------------------------- 1 | package emailservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type EmailCodeVerifyLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewEmailCodeVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *EmailCodeVerifyLogic { 19 | return &EmailCodeVerifyLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // EmailCodeVerify 验证邮件 27 | func (l *EmailCodeVerifyLogic) EmailCodeVerify(in *pb.EmailCodeVerifyReq) (*pb.EmailCodeVerifyResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.EmailCodeVerifyResp{Success: true}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/third/internal/logic/smsservice/smsCodeSendLogic.go: -------------------------------------------------------------------------------- 1 | package smsservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type SmsCodeSendLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewSmsCodeSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SmsCodeSendLogic { 19 | return &SmsCodeSendLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // SmsCodeSend 发送短信 27 | func (l *SmsCodeSendLogic) SmsCodeSend(in *pb.SmsCodeSendReq) (*pb.SmsCodeSendResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.SmsCodeSendResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/third/internal/logic/smsservice/smsCodeVerifyLogic.go: -------------------------------------------------------------------------------- 1 | package smsservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type SmsCodeVerifyLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewSmsCodeVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SmsCodeVerifyLogic { 19 | return &SmsCodeVerifyLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // SmsCodeVerify 验证短信 27 | func (l *SmsCodeVerifyLogic) SmsCodeVerify(in *pb.SmsCodeVerifyReq) (*pb.SmsCodeVerifyResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.SmsCodeVerifyResp{Success: true}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/third/internal/server/captchaservice/captchaServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: third.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/third/internal/logic/captchaservice" 10 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type CaptchaServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedCaptchaServiceServer 17 | } 18 | 19 | func NewCaptchaServiceServer(svcCtx *svc.ServiceContext) *CaptchaServiceServer { 20 | return &CaptchaServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // GetCaptcha 获取图形验证码 26 | func (s *CaptchaServiceServer) GetCaptcha(ctx context.Context, in *pb.GetCaptchaReq) (*pb.GetCaptchaResp, error) { 27 | l := captchaservicelogic.NewGetCaptchaLogic(ctx, s.svcCtx) 28 | return l.GetCaptcha(in) 29 | } 30 | 31 | // CaptchaVerify 验证图形验证码 32 | func (s *CaptchaServiceServer) CaptchaVerify(ctx context.Context, in *pb.CaptchaVerifyReq) (*pb.CaptchaVerifyResp, error) { 33 | l := captchaservicelogic.NewCaptchaVerifyLogic(ctx, s.svcCtx) 34 | return l.CaptchaVerify(in) 35 | } 36 | -------------------------------------------------------------------------------- /app/third/internal/server/emailservice/emailServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: third.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/third/internal/logic/emailservice" 10 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type EmailServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedEmailServiceServer 17 | } 18 | 19 | func NewEmailServiceServer(svcCtx *svc.ServiceContext) *EmailServiceServer { 20 | return &EmailServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // EmailCodeSend 发送邮件 26 | func (s *EmailServiceServer) EmailCodeSend(ctx context.Context, in *pb.EmailCodeSendReq) (*pb.EmailCodeSendResp, error) { 27 | l := emailservicelogic.NewEmailCodeSendLogic(ctx, s.svcCtx) 28 | return l.EmailCodeSend(in) 29 | } 30 | 31 | // EmailCodeVerify 验证邮件 32 | func (s *EmailServiceServer) EmailCodeVerify(ctx context.Context, in *pb.EmailCodeVerifyReq) (*pb.EmailCodeVerifyResp, error) { 33 | l := emailservicelogic.NewEmailCodeVerifyLogic(ctx, s.svcCtx) 34 | return l.EmailCodeVerify(in) 35 | } 36 | -------------------------------------------------------------------------------- /app/third/internal/server/smsservice/smsServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: third.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/third/internal/logic/smsservice" 10 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type SmsServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedSmsServiceServer 17 | } 18 | 19 | func NewSmsServiceServer(svcCtx *svc.ServiceContext) *SmsServiceServer { 20 | return &SmsServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // SmsCodeSend 发送短信 26 | func (s *SmsServiceServer) SmsCodeSend(ctx context.Context, in *pb.SmsCodeSendReq) (*pb.SmsCodeSendResp, error) { 27 | l := smsservicelogic.NewSmsCodeSendLogic(ctx, s.svcCtx) 28 | return l.SmsCodeSend(in) 29 | } 30 | 31 | // SmsCodeVerify 验证短信 32 | func (s *SmsServiceServer) SmsCodeVerify(ctx context.Context, in *pb.SmsCodeVerifyReq) (*pb.SmsCodeVerifyResp, error) { 33 | l := smsservicelogic.NewSmsCodeVerifyLogic(ctx, s.svcCtx) 34 | return l.SmsCodeVerify(in) 35 | } 36 | -------------------------------------------------------------------------------- /app/third/internal/svc/serviceContext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/app/third/internal/config" 5 | "github.com/cherish-chat/xxim-server/common/xcache" 6 | "github.com/zeromicro/go-zero/core/stores/redis" 7 | ) 8 | 9 | type ServiceContext struct { 10 | Config config.Config 11 | Redis *redis.Redis 12 | } 13 | 14 | func NewServiceContext(c config.Config) *ServiceContext { 15 | return &ServiceContext{ 16 | Config: c, 17 | Redis: xcache.MustNewRedis(c.RedisConf), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/third/pb/common.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/common.proto -------------------------------------------------------------------------------- /app/third/pb/third.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/third.proto -------------------------------------------------------------------------------- /app/third/third.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/zeromicro/go-zero/core/logx" 6 | 7 | "github.com/cherish-chat/xxim-server/app/third/internal/config" 8 | captchaserviceServer "github.com/cherish-chat/xxim-server/app/third/internal/server/captchaservice" 9 | emailserviceServer "github.com/cherish-chat/xxim-server/app/third/internal/server/emailservice" 10 | smsserviceServer "github.com/cherish-chat/xxim-server/app/third/internal/server/smsservice" 11 | "github.com/cherish-chat/xxim-server/app/third/internal/svc" 12 | "github.com/cherish-chat/xxim-server/common/pb" 13 | 14 | "github.com/zeromicro/go-zero/core/conf" 15 | "github.com/zeromicro/go-zero/core/service" 16 | "github.com/zeromicro/go-zero/zrpc" 17 | "google.golang.org/grpc" 18 | "google.golang.org/grpc/reflection" 19 | ) 20 | 21 | var configFile = flag.String("f", "etc/third.yaml", "the config file") 22 | 23 | func main() { 24 | flag.Parse() 25 | 26 | var c config.Config 27 | conf.MustLoad(*configFile, &c) 28 | ctx := svc.NewServiceContext(c) 29 | 30 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 31 | pb.RegisterSmsServiceServer(grpcServer, smsserviceServer.NewSmsServiceServer(ctx)) 32 | pb.RegisterEmailServiceServer(grpcServer, emailserviceServer.NewEmailServiceServer(ctx)) 33 | pb.RegisterCaptchaServiceServer(grpcServer, captchaserviceServer.NewCaptchaServiceServer(ctx)) 34 | 35 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 36 | reflection.Register(grpcServer) 37 | } 38 | }) 39 | defer s.Stop() 40 | 41 | logx.Infof("Starting rpc server at %s...\n", c.ListenOn) 42 | s.Start() 43 | } 44 | -------------------------------------------------------------------------------- /app/user/README.md: -------------------------------------------------------------------------------- 1 | # user 用户服务 2 | 3 | - 该服务负责用户的相关逻辑 4 | 5 | ## logic 目录结构 6 | 7 | ```bash 8 | logic 9 | ├── README.md 10 | ├── userRegisterLogic.go # 用户注册逻辑 11 | ├── x_consumerLogic.go # 消息队列消费者逻辑 比如用户注册成功后逻辑 12 | -------------------------------------------------------------------------------- /app/user/internal/logic/accountservice/refreshUserAccessTokenLogic.go: -------------------------------------------------------------------------------- 1 | package accountservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 6 | "github.com/cherish-chat/xxim-server/common/pb" 7 | 8 | "github.com/zeromicro/go-zero/core/logx" 9 | ) 10 | 11 | type RefreshUserAccessTokenLogic struct { 12 | ctx context.Context 13 | svcCtx *svc.ServiceContext 14 | logx.Logger 15 | } 16 | 17 | func NewRefreshUserAccessTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RefreshUserAccessTokenLogic { 18 | return &RefreshUserAccessTokenLogic{ 19 | ctx: ctx, 20 | svcCtx: svcCtx, 21 | Logger: logx.WithContext(ctx), 22 | } 23 | } 24 | 25 | // deprecated 26 | // RefreshUserAccessToken 刷新用户token 27 | func (l *RefreshUserAccessTokenLogic) RefreshUserAccessToken(in *pb.RefreshUserAccessTokenReq) (*pb.RefreshUserAccessTokenResp, error) { 28 | return &pb.RefreshUserAccessTokenResp{}, nil 29 | } 30 | -------------------------------------------------------------------------------- /app/user/internal/logic/accountservice/resetUserAccountMapLogic.go: -------------------------------------------------------------------------------- 1 | package accountservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type ResetUserAccountMapLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewResetUserAccountMapLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetUserAccountMapLogic { 19 | return &ResetUserAccountMapLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // ResetUserAccountMap 重置用户账号信息 27 | func (l *ResetUserAccountMapLogic) ResetUserAccountMap(in *pb.ResetUserAccountMapReq) (*pb.ResetUserAccountMapResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.ResetUserAccountMapResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/logic/accountservice/revokeUserAccessTokenLogic.go: -------------------------------------------------------------------------------- 1 | package accountservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type RevokeUserAccessTokenLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewRevokeUserAccessTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RevokeUserAccessTokenLogic { 19 | return &RevokeUserAccessTokenLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // RevokeUserAccessToken 注销用户token 27 | func (l *RevokeUserAccessTokenLogic) RevokeUserAccessToken(in *pb.RevokeUserAccessTokenReq) (*pb.RevokeUserAccessTokenResp, error) { 28 | err := l.svcCtx.Jwt.RevokeToken(l.ctx, in.Header.UserId, in.Header.GetJwtUniqueKey()) 29 | if err != nil { 30 | l.Errorf("revoke token error: %v", err) 31 | return &pb.RevokeUserAccessTokenResp{}, err 32 | } 33 | return &pb.RevokeUserAccessTokenResp{}, nil 34 | } 35 | -------------------------------------------------------------------------------- /app/user/internal/logic/accountservice/updateUserAccountMapLogic.go: -------------------------------------------------------------------------------- 1 | package accountservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UpdateUserAccountMapLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUpdateUserAccountMapLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserAccountMapLogic { 19 | return &UpdateUserAccountMapLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UpdateUserAccountMap 更新用户账号信息 27 | func (l *UpdateUserAccountMapLogic) UpdateUserAccountMap(in *pb.UpdateUserAccountMapReq) (*pb.UpdateUserAccountMapResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.UpdateUserAccountMapResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/logic/accountservice/userDestroyLogic.go: -------------------------------------------------------------------------------- 1 | package accountservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UserDestroyLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUserDestroyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserDestroyLogic { 19 | return &UserDestroyLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UserDestroy 用户注销 27 | func (l *UserDestroyLogic) UserDestroy(in *pb.UserDestroyReq) (*pb.UserDestroyResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.UserDestroyResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/logic/callbackservice/userAfterKeepAliveLogic.go: -------------------------------------------------------------------------------- 1 | package callbackservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UserAfterKeepAliveLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUserAfterKeepAliveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserAfterKeepAliveLogic { 19 | return &UserAfterKeepAliveLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UserAfterKeepAlive 用户保活回调 27 | func (l *UserAfterKeepAliveLogic) UserAfterKeepAlive(in *pb.UserAfterKeepAliveReq) (*pb.UserAfterKeepAliveResp, error) { 28 | // todo: add your logic here and delete this line 29 | // 1. 订阅群聊消息 30 | { 31 | _, err := l.svcCtx.GroupService.GroupSubscribe(l.ctx, &pb.GroupSubscribeReq{ 32 | Header: in.Header, 33 | }) 34 | if err != nil { 35 | l.Errorf("subscribe group message error: %v", err) 36 | } 37 | } 38 | // 2. 订阅 订阅号 消息 39 | { 40 | _, err := l.svcCtx.SubscriptionService.SubscriptionSubscribe(l.ctx, &pb.SubscriptionSubscribeReq{ 41 | Header: in.Header, 42 | }) 43 | if err != nil { 44 | l.Errorf("subscribe subscription message error: %v", err) 45 | } 46 | } 47 | return &pb.UserAfterKeepAliveResp{}, nil 48 | } 49 | -------------------------------------------------------------------------------- /app/user/internal/logic/callbackservice/userAfterOfflineLogic.go: -------------------------------------------------------------------------------- 1 | package callbackservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UserAfterOfflineLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUserAfterOfflineLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserAfterOfflineLogic { 19 | return &UserAfterOfflineLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UserAfterOffline 用户下线回调 27 | func (l *UserAfterOfflineLogic) UserAfterOffline(in *pb.UserAfterOfflineReq) (*pb.UserAfterOfflineResp, error) { 28 | //1. 订阅号的逻辑 29 | { 30 | _, err := l.svcCtx.SubscriptionService.SubscriptionAfterOffline(l.ctx, &pb.SubscriptionAfterOfflineReq{ 31 | UserId: in.UserId, 32 | }) 33 | if err != nil { 34 | l.Errorf("subscription after online error: %v", err) 35 | } 36 | } 37 | return &pb.UserAfterOfflineResp{}, nil 38 | } 39 | -------------------------------------------------------------------------------- /app/user/internal/logic/callbackservice/userAfterOnlineLogic.go: -------------------------------------------------------------------------------- 1 | package callbackservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UserAfterOnlineLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUserAfterOnlineLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserAfterOnlineLogic { 19 | return &UserAfterOnlineLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UserAfterOnline 用户上线回调 27 | func (l *UserAfterOnlineLogic) UserAfterOnline(in *pb.UserAfterOnlineReq) (*pb.UserAfterOnlineResp, error) { 28 | //1. 订阅号的逻辑 29 | { 30 | _, err := l.svcCtx.SubscriptionService.SubscriptionAfterOnline(l.ctx, &pb.SubscriptionAfterOnlineReq{ 31 | Header: in.Header, 32 | }) 33 | if err != nil { 34 | l.Errorf("subscription after online error: %v", err) 35 | } 36 | } 37 | return &pb.UserAfterOnlineResp{}, nil 38 | } 39 | -------------------------------------------------------------------------------- /app/user/internal/logic/callbackservice/userAfterRegisterLogic.go: -------------------------------------------------------------------------------- 1 | package callbackservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 6 | "github.com/zeromicro/go-zero/core/logx" 7 | ) 8 | 9 | type UserAfterRegisterLogic struct { 10 | svcCtx *svc.ServiceContext 11 | ctx context.Context 12 | logx.Logger 13 | } 14 | 15 | func NewUserAfterRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserAfterRegisterLogic { 16 | return &UserAfterRegisterLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)} 17 | } 18 | 19 | // AfterRegister TODO: 补充注册后的逻辑 20 | func (l *UserAfterRegisterLogic) AfterRegister(topic string, msg []byte) error { 21 | l.Infof("topic: %s, msg: %s", topic, string(msg)) 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /app/user/internal/logic/callbackservice/userBeforeConnectLogic.go: -------------------------------------------------------------------------------- 1 | package callbackservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 6 | "github.com/cherish-chat/xxim-server/common/i18n" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | "github.com/cherish-chat/xxim-server/common/utils" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type UserBeforeConnectLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewUserBeforeConnectLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserBeforeConnectLogic { 20 | return &UserBeforeConnectLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | // UserBeforeConnect 用户连接前的回调 28 | func (l *UserBeforeConnectLogic) UserBeforeConnect(in *pb.UserBeforeConnectReq) (*pb.UserBeforeConnectResp, error) { 29 | if in.Token != "" { 30 | tokenObject, verifyTokenErr := l.svcCtx.Jwt.VerifyToken(l.ctx, in.Token, in.Header.GetJwtUniqueKey()) 31 | if verifyTokenErr != nil { 32 | l.Errorf("verifyTokenErr: %v", verifyTokenErr) 33 | var resp *pb.UserBeforeConnectResp 34 | switch verifyTokenErr { 35 | case utils.TokenExpiredError: 36 | resp = &pb.UserBeforeConnectResp{ 37 | Header: i18n.NewAuthError(pb.AuthErrorTypeExpired, ""), 38 | } 39 | case utils.TokenReplaceError: 40 | ssm := utils.Map.SSMFromString(tokenObject.Extra) 41 | deviceModel := ssm.Get("deviceModel") 42 | resp = &pb.UserBeforeConnectResp{ 43 | Header: i18n.NewAuthError(pb.AuthErrorTypeReplace, deviceModel), 44 | } 45 | default: 46 | resp = &pb.UserBeforeConnectResp{ 47 | Header: i18n.NewAuthError(pb.AuthErrorTypeInvalid, verifyTokenErr.Error()), 48 | } 49 | } 50 | return resp, nil 51 | } 52 | l.Debugf("tokenObject: %+v", tokenObject) 53 | // 验证权限 54 | return &pb.UserBeforeConnectResp{ 55 | Header: i18n.NewOkHeader(), 56 | UserId: tokenObject.UserId, 57 | Success: true, 58 | }, nil 59 | } else { 60 | // 验证权限 61 | l.Infof("in.Header: %+v", in.Header) 62 | return &pb.UserBeforeConnectResp{ 63 | Header: i18n.NewAuthError(pb.AuthErrorTypeInvalid, ""), 64 | Success: false, 65 | }, nil 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/user/internal/logic/callbackservice/userBeforeRequestLogic.go: -------------------------------------------------------------------------------- 1 | package callbackservicelogic 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 6 | "github.com/cherish-chat/xxim-server/common/i18n" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UserBeforeRequestLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUserBeforeRequestLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserBeforeRequestLogic { 19 | return &UserBeforeRequestLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UserBeforeRequest 用户请求前的回调 27 | func (l *UserBeforeRequestLogic) UserBeforeRequest(in *pb.UserBeforeRequestReq) (*pb.UserBeforeRequestResp, error) { 28 | return &pb.UserBeforeRequestResp{ 29 | Header: i18n.NewOkHeader(), 30 | }, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/logic/infoservice/getSelfUserInfoLogic.go: -------------------------------------------------------------------------------- 1 | package infoservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GetSelfUserInfoLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGetSelfUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSelfUserInfoLogic { 19 | return &GetSelfUserInfoLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // GetSelfUserInfo 获取自己的用户信息 27 | func (l *GetSelfUserInfoLogic) GetSelfUserInfo(in *pb.GetSelfUserInfoReq) (*pb.GetSelfUserInfoResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.GetSelfUserInfoResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/logic/infoservice/getUserInfoLogic.go: -------------------------------------------------------------------------------- 1 | package infoservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GetUserInfoLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserInfoLogic { 19 | return &GetUserInfoLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // GetUserInfo 获取用户信息 27 | func (l *GetUserInfoLogic) GetUserInfo(in *pb.GetUserInfoReq) (*pb.GetUserInfoResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.GetUserInfoResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/logic/infoservice/updateUserExtraMapLogic.go: -------------------------------------------------------------------------------- 1 | package infoservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UpdateUserExtraMapLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUpdateUserExtraMapLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserExtraMapLogic { 19 | return &UpdateUserExtraMapLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UpdateUserExtraMap 更新用户扩展信息 27 | func (l *UpdateUserExtraMapLogic) UpdateUserExtraMap(in *pb.UpdateUserExtraMapReq) (*pb.UpdateUserExtraMapResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.UpdateUserExtraMapResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/logic/infoservice/updateUserProfileMapLogic.go: -------------------------------------------------------------------------------- 1 | package infoservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UpdateUserProfileMapLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUpdateUserProfileMapLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserProfileMapLogic { 19 | return &UpdateUserProfileMapLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // UpdateUserProfileMap 更新用户个人信息 27 | func (l *UpdateUserProfileMapLogic) UpdateUserProfileMap(in *pb.UpdateUserProfileMapReq) (*pb.UpdateUserProfileMapResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.UpdateUserProfileMapResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/user/internal/server/accountservice/accountServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: user.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/user/internal/logic/accountservice" 10 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type AccountServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedAccountServiceServer 17 | } 18 | 19 | func NewAccountServiceServer(svcCtx *svc.ServiceContext) *AccountServiceServer { 20 | return &AccountServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // UserRegister 用户注册 26 | func (s *AccountServiceServer) UserRegister(ctx context.Context, in *pb.UserRegisterReq) (*pb.UserRegisterResp, error) { 27 | l := accountservicelogic.NewUserRegisterLogic(ctx, s.svcCtx) 28 | return l.UserRegister(in) 29 | } 30 | 31 | // UserDestroy 用户注销 32 | func (s *AccountServiceServer) UserDestroy(ctx context.Context, in *pb.UserDestroyReq) (*pb.UserDestroyResp, error) { 33 | l := accountservicelogic.NewUserDestroyLogic(ctx, s.svcCtx) 34 | return l.UserDestroy(in) 35 | } 36 | 37 | // UserAccessToken 用户登录 38 | func (s *AccountServiceServer) UserAccessToken(ctx context.Context, in *pb.UserAccessTokenReq) (*pb.UserAccessTokenResp, error) { 39 | l := accountservicelogic.NewUserAccessTokenLogic(ctx, s.svcCtx) 40 | return l.UserAccessToken(in) 41 | } 42 | 43 | // RefreshUserAccessToken 刷新用户token 44 | func (s *AccountServiceServer) RefreshUserAccessToken(ctx context.Context, in *pb.RefreshUserAccessTokenReq) (*pb.RefreshUserAccessTokenResp, error) { 45 | l := accountservicelogic.NewRefreshUserAccessTokenLogic(ctx, s.svcCtx) 46 | return l.RefreshUserAccessToken(in) 47 | } 48 | 49 | // RevokeUserAccessToken 注销用户token 50 | func (s *AccountServiceServer) RevokeUserAccessToken(ctx context.Context, in *pb.RevokeUserAccessTokenReq) (*pb.RevokeUserAccessTokenResp, error) { 51 | l := accountservicelogic.NewRevokeUserAccessTokenLogic(ctx, s.svcCtx) 52 | return l.RevokeUserAccessToken(in) 53 | } 54 | 55 | // UpdateUserAccountMap 更新用户账号信息 56 | func (s *AccountServiceServer) UpdateUserAccountMap(ctx context.Context, in *pb.UpdateUserAccountMapReq) (*pb.UpdateUserAccountMapResp, error) { 57 | l := accountservicelogic.NewUpdateUserAccountMapLogic(ctx, s.svcCtx) 58 | return l.UpdateUserAccountMap(in) 59 | } 60 | 61 | // ResetUserAccountMap 重置用户账号信息 62 | func (s *AccountServiceServer) ResetUserAccountMap(ctx context.Context, in *pb.ResetUserAccountMapReq) (*pb.ResetUserAccountMapResp, error) { 63 | l := accountservicelogic.NewResetUserAccountMapLogic(ctx, s.svcCtx) 64 | return l.ResetUserAccountMap(in) 65 | } 66 | 67 | // CreateRobot 创建机器人 68 | func (s *AccountServiceServer) CreateRobot(ctx context.Context, in *pb.CreateRobotReq) (*pb.CreateRobotResp, error) { 69 | l := accountservicelogic.NewCreateRobotLogic(ctx, s.svcCtx) 70 | return l.CreateRobot(in) 71 | } 72 | -------------------------------------------------------------------------------- /app/user/internal/server/callbackservice/callbackServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: user.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/user/internal/logic/callbackservice" 10 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type CallbackServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedCallbackServiceServer 17 | } 18 | 19 | func NewCallbackServiceServer(svcCtx *svc.ServiceContext) *CallbackServiceServer { 20 | return &CallbackServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // UserAfterOnline 用户上线回调 26 | func (s *CallbackServiceServer) UserAfterOnline(ctx context.Context, in *pb.UserAfterOnlineReq) (*pb.UserAfterOnlineResp, error) { 27 | l := callbackservicelogic.NewUserAfterOnlineLogic(ctx, s.svcCtx) 28 | return l.UserAfterOnline(in) 29 | } 30 | 31 | // UserAfterOffline 用户下线回调 32 | func (s *CallbackServiceServer) UserAfterOffline(ctx context.Context, in *pb.UserAfterOfflineReq) (*pb.UserAfterOfflineResp, error) { 33 | l := callbackservicelogic.NewUserAfterOfflineLogic(ctx, s.svcCtx) 34 | return l.UserAfterOffline(in) 35 | } 36 | 37 | // UserBeforeConnect 用户连接前的回调 38 | func (s *CallbackServiceServer) UserBeforeConnect(ctx context.Context, in *pb.UserBeforeConnectReq) (*pb.UserBeforeConnectResp, error) { 39 | l := callbackservicelogic.NewUserBeforeConnectLogic(ctx, s.svcCtx) 40 | return l.UserBeforeConnect(in) 41 | } 42 | 43 | // UserBeforeRequest 用户请求前的回调 44 | func (s *CallbackServiceServer) UserBeforeRequest(ctx context.Context, in *pb.UserBeforeRequestReq) (*pb.UserBeforeRequestResp, error) { 45 | l := callbackservicelogic.NewUserBeforeRequestLogic(ctx, s.svcCtx) 46 | return l.UserBeforeRequest(in) 47 | } 48 | 49 | // UserAfterKeepAlive 用户保活回调 50 | func (s *CallbackServiceServer) UserAfterKeepAlive(ctx context.Context, in *pb.UserAfterKeepAliveReq) (*pb.UserAfterKeepAliveResp, error) { 51 | l := callbackservicelogic.NewUserAfterKeepAliveLogic(ctx, s.svcCtx) 52 | return l.UserAfterKeepAlive(in) 53 | } 54 | -------------------------------------------------------------------------------- /app/user/internal/server/consumerServer.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/app/user/internal/logic/callbackservice" 6 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/xmq" 8 | ) 9 | 10 | type ConsumerServer struct { 11 | svcCtx *svc.ServiceContext 12 | } 13 | 14 | func NewConsumerServer(svcCtx *svc.ServiceContext) *ConsumerServer { 15 | return &ConsumerServer{svcCtx: svcCtx} 16 | } 17 | 18 | func (s *ConsumerServer) Start() { 19 | s.svcCtx.MQ.RegisterHandler(xmq.TopicAfterRegister, func(ctx context.Context, topic string, msg []byte) error { 20 | return callbackservicelogic.NewUserAfterRegisterLogic(ctx, s.svcCtx).AfterRegister(topic, msg) 21 | }) 22 | go s.svcCtx.MQ.StartConsuming() 23 | } 24 | -------------------------------------------------------------------------------- /app/user/internal/server/infoservice/infoServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: user.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/user/internal/logic/infoservice" 10 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type InfoServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedInfoServiceServer 17 | } 18 | 19 | func NewInfoServiceServer(svcCtx *svc.ServiceContext) *InfoServiceServer { 20 | return &InfoServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // UpdateUserProfileMap 更新用户个人信息 26 | func (s *InfoServiceServer) UpdateUserProfileMap(ctx context.Context, in *pb.UpdateUserProfileMapReq) (*pb.UpdateUserProfileMapResp, error) { 27 | l := infoservicelogic.NewUpdateUserProfileMapLogic(ctx, s.svcCtx) 28 | return l.UpdateUserProfileMap(in) 29 | } 30 | 31 | // UpdateUserExtraMap 更新用户扩展信息 32 | func (s *InfoServiceServer) UpdateUserExtraMap(ctx context.Context, in *pb.UpdateUserExtraMapReq) (*pb.UpdateUserExtraMapResp, error) { 33 | l := infoservicelogic.NewUpdateUserExtraMapLogic(ctx, s.svcCtx) 34 | return l.UpdateUserExtraMap(in) 35 | } 36 | 37 | // UpdateUserCountMap 更新用户计数信息 38 | func (s *InfoServiceServer) UpdateUserCountMap(ctx context.Context, in *pb.UpdateUserCountMapReq) (*pb.UpdateUserCountMapResp, error) { 39 | l := infoservicelogic.NewUpdateUserCountMapLogic(ctx, s.svcCtx) 40 | return l.UpdateUserCountMap(in) 41 | } 42 | 43 | // GetSelfUserInfo 获取自己的用户信息 44 | func (s *InfoServiceServer) GetSelfUserInfo(ctx context.Context, in *pb.GetSelfUserInfoReq) (*pb.GetSelfUserInfoResp, error) { 45 | l := infoservicelogic.NewGetSelfUserInfoLogic(ctx, s.svcCtx) 46 | return l.GetSelfUserInfo(in) 47 | } 48 | 49 | // GetUserInfo 获取用户信息 50 | func (s *InfoServiceServer) GetUserInfo(ctx context.Context, in *pb.GetUserInfoReq) (*pb.GetUserInfoResp, error) { 51 | l := infoservicelogic.NewGetUserInfoLogic(ctx, s.svcCtx) 52 | return l.GetUserInfo(in) 53 | } 54 | 55 | // GetUserModelById 获取用户模型 56 | func (s *InfoServiceServer) GetUserModelById(ctx context.Context, in *pb.GetUserModelByIdReq) (*pb.GetUserModelByIdResp, error) { 57 | l := infoservicelogic.NewGetUserModelByIdLogic(ctx, s.svcCtx) 58 | return l.GetUserModelById(in) 59 | } 60 | 61 | // GetUserModelByIds 批量获取用户模型 62 | func (s *InfoServiceServer) GetUserModelByIds(ctx context.Context, in *pb.GetUserModelByIdsReq) (*pb.GetUserModelByIdsResp, error) { 63 | l := infoservicelogic.NewGetUserModelByIdsLogic(ctx, s.svcCtx) 64 | return l.GetUserModelByIds(in) 65 | } 66 | -------------------------------------------------------------------------------- /app/user/internal/svc/serviceContext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/app/conversation/client/friendservice" 5 | "github.com/cherish-chat/xxim-server/app/conversation/client/groupservice" 6 | "github.com/cherish-chat/xxim-server/app/conversation/client/subscriptionservice" 7 | "github.com/cherish-chat/xxim-server/app/third/client/captchaservice" 8 | "github.com/cherish-chat/xxim-server/app/third/client/emailservice" 9 | "github.com/cherish-chat/xxim-server/app/third/client/smsservice" 10 | "github.com/cherish-chat/xxim-server/app/user/internal/config" 11 | "github.com/cherish-chat/xxim-server/app/user/usermodel" 12 | "github.com/cherish-chat/xxim-server/common/utils" 13 | "github.com/cherish-chat/xxim-server/common/xcache" 14 | "github.com/cherish-chat/xxim-server/common/xmgo" 15 | "github.com/cherish-chat/xxim-server/common/xmq" 16 | "github.com/qiniu/qmgo" 17 | "github.com/zeromicro/go-zero/core/stores/redis" 18 | "github.com/zeromicro/go-zero/zrpc" 19 | "time" 20 | ) 21 | 22 | type ServiceContext struct { 23 | Config config.Config 24 | Redis *redis.Redis 25 | 26 | UserCollection *qmgo.QmgoClient 27 | UserSettingCollection *qmgo.QmgoClient 28 | 29 | MQ xmq.MQ 30 | Jwt *utils.Jwt 31 | 32 | SmsService smsservice.SmsService 33 | EmailService emailservice.EmailService 34 | CaptchaService captchaservice.CaptchaService 35 | FriendService friendservice.FriendService 36 | GroupService groupservice.GroupService 37 | SubscriptionService subscriptionservice.SubscriptionService 38 | } 39 | 40 | func NewServiceContext(c config.Config) *ServiceContext { 41 | s := &ServiceContext{ 42 | Config: c, 43 | Redis: xcache.MustNewRedis(c.RedisConf), 44 | UserCollection: xmgo.MustNewMongoCollection(c.MongoCollection.User, &usermodel.User{}), 45 | UserSettingCollection: xmgo.MustNewMongoCollection(c.MongoCollection.UserSetting, &usermodel.UserSetting{}), 46 | } 47 | 48 | //third rpc 49 | { 50 | thirdClient := zrpc.MustNewClient( 51 | c.RpcClientConf.Third, 52 | zrpc.WithNonBlock(), 53 | zrpc.WithTimeout(time.Duration(c.Timeout)*time.Millisecond), 54 | ) 55 | s.SmsService = smsservice.NewSmsService(thirdClient) 56 | s.EmailService = emailservice.NewEmailService(thirdClient) 57 | s.CaptchaService = captchaservice.NewCaptchaService(thirdClient) 58 | } 59 | // conversation rpc 60 | { 61 | conversationClient := zrpc.MustNewClient( 62 | c.RpcClientConf.Conversation, 63 | zrpc.WithNonBlock(), 64 | zrpc.WithTimeout(time.Duration(c.Timeout)*time.Millisecond), 65 | ) 66 | s.FriendService = friendservice.NewFriendService(conversationClient) 67 | s.GroupService = groupservice.NewGroupService(conversationClient) 68 | s.SubscriptionService = subscriptionservice.NewSubscriptionService(conversationClient) 69 | } 70 | 71 | s.MQ = xmq.NewAsynq(s.Config.RedisConf, 1, s.Config.Log.Level) 72 | s.Jwt = utils.NewJwt(s.Config.Account.JwtConfig, s.Redis) 73 | return s 74 | } 75 | -------------------------------------------------------------------------------- /app/user/pb/common.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/common.proto -------------------------------------------------------------------------------- /app/user/pb/user.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/user.proto -------------------------------------------------------------------------------- /app/user/user.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/cherish-chat/xxim-server/app/user/internal/server" 6 | "github.com/zeromicro/go-zero/core/logx" 7 | 8 | "github.com/cherish-chat/xxim-server/app/user/internal/config" 9 | accountserviceServer "github.com/cherish-chat/xxim-server/app/user/internal/server/accountservice" 10 | callbackserviceServer "github.com/cherish-chat/xxim-server/app/user/internal/server/callbackservice" 11 | infoserviceServer "github.com/cherish-chat/xxim-server/app/user/internal/server/infoservice" 12 | "github.com/cherish-chat/xxim-server/app/user/internal/svc" 13 | "github.com/cherish-chat/xxim-server/common/pb" 14 | 15 | "github.com/zeromicro/go-zero/core/conf" 16 | "github.com/zeromicro/go-zero/core/service" 17 | "github.com/zeromicro/go-zero/zrpc" 18 | "google.golang.org/grpc" 19 | "google.golang.org/grpc/reflection" 20 | ) 21 | 22 | var configFile = flag.String("f", "etc/user.yaml", "the config file") 23 | 24 | func main() { 25 | flag.Parse() 26 | 27 | var c config.Config 28 | conf.MustLoad(*configFile, &c) 29 | ctx := svc.NewServiceContext(c) 30 | consumerServer := server.NewConsumerServer(ctx) 31 | 32 | consumerServer.Start() 33 | 34 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 35 | pb.RegisterAccountServiceServer(grpcServer, accountserviceServer.NewAccountServiceServer(ctx)) 36 | pb.RegisterInfoServiceServer(grpcServer, infoserviceServer.NewInfoServiceServer(ctx)) 37 | pb.RegisterCallbackServiceServer(grpcServer, callbackserviceServer.NewCallbackServiceServer(ctx)) 38 | 39 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 40 | reflection.Register(grpcServer) 41 | } 42 | }) 43 | defer s.Stop() 44 | 45 | logx.Infof("Starting rpc server at %s...\n", c.ListenOn) 46 | s.Start() 47 | } 48 | -------------------------------------------------------------------------------- /app/user/usermodel/usersetting.go: -------------------------------------------------------------------------------- 1 | package usermodel 2 | 3 | import ( 4 | opts "github.com/qiniu/qmgo/options" 5 | "go.mongodb.org/mongo-driver/mongo/options" 6 | ) 7 | 8 | type ValueType int32 9 | 10 | const ( 11 | // ValueTypeString 字符串 12 | ValueTypeString ValueType = iota 13 | // ValueTypeBool 布尔值 14 | ValueTypeBool 15 | // ValueTypeInt 整数 16 | ValueTypeInt 17 | // ValueTypeFloat 浮点数 18 | ValueTypeFloat 19 | // ValueTypeArrayJson 数组JSON 20 | ValueTypeArrayJson 21 | // ValueTypeMapJson MapJSON 22 | ValueTypeMapJson 23 | // ValueTypeFileUrl 文件url; 24 | // valueTypeExt会记录允许的拓展名; 25 | // example: [".png", ".jpg", ".jpeg", ".gif", ".bmp"] 26 | ValueTypeFileUrl 27 | ) 28 | 29 | // UserSetting 用户设置 数据库模型 30 | type UserSetting struct { 31 | UserId string `bson:"userId" json:"userId"` 32 | K string `bson:"k" json:"k"` 33 | V string `bson:"v" json:"v"` 34 | VT ValueType `bson:"vt" json:"vt"` 35 | } 36 | 37 | func (m *UserSetting) GetIndexes() []opts.IndexModel { 38 | return []opts.IndexModel{{ 39 | Key: []string{"userId", "k"}, 40 | IndexOptions: options.Index().SetUnique(true), 41 | }} 42 | } 43 | 44 | //UserSettingKey 用户设置key 45 | 46 | const ( 47 | //UserSettingKeyFriendApply 好友申请设置 48 | UserSettingKeyFriendApply = "friendApply" 49 | //UserSettingKeyAllowBeInvitedGroup 允许被邀请进群 50 | UserSettingKeyAllowBeInvitedGroup = "allowBeInvitedGroup" 51 | ) 52 | 53 | type UserSettingFriendApplyType int32 54 | 55 | //UserSettingFriendApplyType 用户设置好友申请类型 56 | //好友申请验证方式 57 | //0: 任何人都可以添加我为好友 58 | //1: 需要发送验证消息 59 | //2: 需要正确回答问题 60 | //3: 需要回答问题并由我确认(不需要正确回答问题) 61 | //4: 需要回答问题并由我确认(需要正确回答问题) 62 | //5: 不允许任何人添加我 63 | 64 | const ( 65 | UserSettingFriendApplyTypeAny = iota 66 | UserSettingFriendApplyTypeVerifyMessage 67 | UserSettingFriendApplyTypeAnswerQuestion 68 | UserSettingFriendApplyTypeAnswerQuestionAndConfirm 69 | UserSettingFriendApplyTypeAnswerQuestionAndConfirmWithRightAnswer 70 | UserSettingFriendApplyTypeNoOne 71 | ) 72 | 73 | // UserSettingFriendApply 用户设置好友申请 74 | type UserSettingFriendApply struct { 75 | //好友申请验证方式 76 | //0: 任何人都可以添加我为好友 77 | //1: 需要发送验证消息 78 | //2: 需要正确回答问题 79 | //3: 需要回答问题并由我确认(不需要正确回答问题) 80 | //4: 需要回答问题并由我确认(需要正确回答问题) 81 | //5: 不允许任何人添加我 82 | ApplyType UserSettingFriendApplyType `json:"applyType"` 83 | //设置的问题 84 | Question string `json:"question"` 85 | //设置的答案 86 | Answer string `json:"answer"` 87 | } 88 | -------------------------------------------------------------------------------- /app/world/client/worldservice/worldService.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: world.proto 3 | 4 | package worldservice 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/common/pb" 10 | 11 | "github.com/zeromicro/go-zero/zrpc" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type ( 16 | WorldPostSubmitReq = pb.WorldPostSubmitReq 17 | WorldPostSubmitResp = pb.WorldPostSubmitResp 18 | 19 | WorldService interface { 20 | // WorldPostSubmit 世界圈帖子发布 21 | WorldPostSubmit(ctx context.Context, in *WorldPostSubmitReq, opts ...grpc.CallOption) (*WorldPostSubmitResp, error) 22 | } 23 | 24 | defaultWorldService struct { 25 | cli zrpc.Client 26 | } 27 | ) 28 | 29 | func NewWorldService(cli zrpc.Client) WorldService { 30 | return &defaultWorldService{ 31 | cli: cli, 32 | } 33 | } 34 | 35 | // WorldPostSubmit 世界圈帖子发布 36 | func (m *defaultWorldService) WorldPostSubmit(ctx context.Context, in *WorldPostSubmitReq, opts ...grpc.CallOption) (*WorldPostSubmitResp, error) { 37 | client := pb.NewWorldServiceClient(m.cli.Conn()) 38 | return client.WorldPostSubmit(ctx, in, opts...) 39 | } 40 | -------------------------------------------------------------------------------- /app/world/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/zeromicro/go-zero/zrpc" 4 | 5 | type Config struct { 6 | zrpc.RpcServerConf 7 | } 8 | -------------------------------------------------------------------------------- /app/world/internal/logic/worldservice/worldPostSubmitLogic.go: -------------------------------------------------------------------------------- 1 | package worldservicelogic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cherish-chat/xxim-server/app/world/internal/svc" 7 | "github.com/cherish-chat/xxim-server/common/pb" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type WorldPostSubmitLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewWorldPostSubmitLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WorldPostSubmitLogic { 19 | return &WorldPostSubmitLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | // WorldPostSubmit 世界圈帖子发布 27 | func (l *WorldPostSubmitLogic) WorldPostSubmit(in *pb.WorldPostSubmitReq) (*pb.WorldPostSubmitResp, error) { 28 | // todo: add your logic here and delete this line 29 | 30 | return &pb.WorldPostSubmitResp{}, nil 31 | } 32 | -------------------------------------------------------------------------------- /app/world/internal/server/worldservice/worldServiceServer.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: world.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/cherish-chat/xxim-server/app/world/internal/logic/worldservice" 10 | "github.com/cherish-chat/xxim-server/app/world/internal/svc" 11 | "github.com/cherish-chat/xxim-server/common/pb" 12 | ) 13 | 14 | type WorldServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | pb.UnimplementedWorldServiceServer 17 | } 18 | 19 | func NewWorldServiceServer(svcCtx *svc.ServiceContext) *WorldServiceServer { 20 | return &WorldServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | // WorldPostSubmit 世界圈帖子发布 26 | func (s *WorldServiceServer) WorldPostSubmit(ctx context.Context, in *pb.WorldPostSubmitReq) (*pb.WorldPostSubmitResp, error) { 27 | l := worldservicelogic.NewWorldPostSubmitLogic(ctx, s.svcCtx) 28 | return l.WorldPostSubmit(in) 29 | } 30 | -------------------------------------------------------------------------------- /app/world/internal/svc/serviceContext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import "github.com/cherish-chat/xxim-server/app/world/internal/config" 4 | 5 | type ServiceContext struct { 6 | Config config.Config 7 | } 8 | 9 | func NewServiceContext(c config.Config) *ServiceContext { 10 | return &ServiceContext{ 11 | Config: c, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/world/pb/common.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/common.proto -------------------------------------------------------------------------------- /app/world/pb/world.proto: -------------------------------------------------------------------------------- 1 | ../../../common/pb/world.proto -------------------------------------------------------------------------------- /app/world/world.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/zeromicro/go-zero/core/logx" 6 | 7 | "github.com/cherish-chat/xxim-server/app/world/internal/config" 8 | worldserviceServer "github.com/cherish-chat/xxim-server/app/world/internal/server/worldservice" 9 | "github.com/cherish-chat/xxim-server/app/world/internal/svc" 10 | "github.com/cherish-chat/xxim-server/common/pb" 11 | 12 | "github.com/zeromicro/go-zero/core/conf" 13 | "github.com/zeromicro/go-zero/core/service" 14 | "github.com/zeromicro/go-zero/zrpc" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/reflection" 17 | ) 18 | 19 | var configFile = flag.String("f", "etc/world.yaml", "the config file") 20 | 21 | func main() { 22 | flag.Parse() 23 | 24 | var c config.Config 25 | conf.MustLoad(*configFile, &c) 26 | ctx := svc.NewServiceContext(c) 27 | 28 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 29 | pb.RegisterWorldServiceServer(grpcServer, worldserviceServer.NewWorldServiceServer(ctx)) 30 | 31 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 32 | reflection.Register(grpcServer) 33 | } 34 | }) 35 | defer s.Stop() 36 | 37 | logx.Infof("Starting rpc server at %s...\n", c.ListenOn) 38 | s.Start() 39 | } 40 | -------------------------------------------------------------------------------- /common/pb/.gitignore: -------------------------------------------------------------------------------- 1 | sdk 2 | -------------------------------------------------------------------------------- /common/pb/goctl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | # goctl.sh 使用go-zero工具生成代码 3 | 4 | function rpc() { 5 | service=$1 6 | filename="${service}.proto" 7 | common_pb_path=$(pwd) 8 | # shellcheck disable=SC2164 9 | cd "../../app/${service}" 10 | echo "pwd: $(pwd)" 11 | mkdir pb || true 12 | # shellcheck disable=SC2164 13 | cd pb 14 | ln -s "../../../common/pb/$filename" "$filename" || true 15 | ln -s "../../../common/pb/common.proto" "common.proto" || true 16 | goctl_v1.5.3 rpc protoc -I="." "$filename" -v --go_out=../../../common --go-grpc_out=../../../common --zrpc_out=.. --style=goZero -m 17 | # shellcheck disable=SC2164 18 | cd "${common_pb_path}" 19 | # shellcheck disable=SC2013 20 | # shellcheck disable=SC2006 21 | for i in `grep package -rl *.go` ; do 22 | #判断系统 如果是mac则不需要加"",如果是linux则需要加"" 23 | if [[ `uname` == "Darwin" ]]; then 24 | sed -i "" "s#,omitempty##g" "${i}" 25 | elif [[ `uname` == "Linux" ]]; then 26 | sed -i "s#,omitempty##g" "${i}" 27 | else 28 | echo "未知系统,请手动删除pb.go文件中的omitempty" 29 | fi 30 | done 31 | } 32 | 33 | # common.proto 34 | # shellcheck disable=SC2046 35 | protoc --proto_path=. common.proto --go_out=.. 36 | 37 | rpc "conversation" 38 | rpc "gateway" 39 | rpc "message" 40 | rpc "third" 41 | rpc "user" 42 | rpc "world" 43 | -------------------------------------------------------------------------------- /common/pb/sdk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # 此shell脚本用于生成pb文件 4 | 5 | ## 1.生成java文件 6 | rm -rf sdk/java 7 | mkdir -p sdk/java/pb 8 | protoc --java_out=./sdk/java/pb/ *.proto 9 | cp ./*.proto sdk/java/pb/ 10 | 11 | ## 2.生成go文件 12 | rm -rf sdk/go 13 | mkdir -p sdk/go/pb 14 | protoc --go_out=./sdk/go/pb/ *.proto 15 | cp ./*.proto sdk/go/pb/ 16 | 17 | ## 3.生成python文件 18 | rm -rf sdk/python 19 | mkdir -p sdk/python/pb 20 | protoc --python_out=./sdk/python/pb/ *.proto 21 | cp ./*.proto sdk/python/pb/ 22 | 23 | ## 4.生成c++文件 24 | rm -rf sdk/cpp 25 | mkdir -p sdk/cpp/pb 26 | protoc --cpp_out=./sdk/cpp/pb/ *.proto 27 | cp ./*.proto sdk/cpp/pb/ 28 | 29 | ## 5.生成c#文件 30 | rm -rf sdk/csharp 31 | mkdir -p sdk/csharp/pb 32 | protoc --csharp_out=./sdk/csharp/pb/ *.proto 33 | cp ./*.proto sdk/csharp/pb/ 34 | 35 | ## 6.生成objc文件 36 | rm -rf sdk/objc 37 | mkdir -p sdk/objc/pb 38 | protoc --objc_out=./sdk/objc/pb/ *.proto 39 | cp ./*.proto sdk/objc/pb/ 40 | 41 | ## 7.生成swift文件 42 | rm -rf sdk/swift 43 | mkdir -p sdk/swift/pb 44 | protoc --swift_out=./sdk/swift/pb/ *.proto 45 | cp ./*.proto sdk/swift/pb/ 46 | 47 | ## 8.生成ts文件 48 | #rm -rf sdk/ts 49 | #mkdir -p sdk/ts/pb 50 | #npm install -g ts-protoc-gen 51 | #protoc --ts_out=./sdk/ts/pb/ *.proto 52 | #cp ./*.proto sdk/ts/pb/ 53 | 54 | ## 9.生成rust文件 55 | rm -rf sdk/rust 56 | mkdir -p sdk/rust/pb 57 | #cargo install protobuf-codegen 58 | cp ./*.proto sdk/rust/pb/ 59 | protoc --rust_out=./sdk/rust/pb/ *.proto 60 | 61 | ## 10.生成dart文件 62 | rm -rf sdk/dart 63 | mkdir -p sdk/dart/pb 64 | #dart pub global activate protoc_plugin 65 | protoc --dart_out=./sdk/dart/pb/ *.proto 66 | cp ./*.proto sdk/dart/pb/ 67 | 68 | 69 | ### 每一个struct都加上#[derive(serde::Serialize,serde::Deserialize)],否则无法序列化和反序列化 70 | ### 在#[derive(PartialEq,Clone,Default,Debug)]的上面加 71 | # shellcheck disable=SC2164 72 | #cd sdk/rust/pb 73 | #for file in ./*.rs 74 | #do 75 | # #判断系统 如果是mac则不需要加"",如果是linux则需要加"" 76 | # if [[ `uname` == "Darwin" ]]; then 77 | # sed -i '' 's#fn from_i32(value: i32)#pub fn from_i32(value: i32)#g' "$file" 78 | # #sed -i '' 's#fn value(&self) -> i32#pub fn value(&self) -> i32#g' "$file" 79 | # elif [[ `uname` == "Linux" ]]; then 80 | # sed -i 's#fn from_i32(value: i32)#pub fn from_i32(value: i32)#g' "$file" 81 | # #sed -i 's#fn value(&self) -> i32#pub fn value(&self) -> i32#g' "$file" 82 | # else 83 | # echo "未知系统,请手动对pb.rs文件增加json序列化支持" 84 | # fi 85 | # 86 | #done 87 | ## shellcheck disable=SC2164 88 | #cd - 89 | -------------------------------------------------------------------------------- /common/pb/world.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package pb; 3 | option go_package = "./pb"; 4 | import "common.proto"; 5 | 6 | message WorldPostSubmitReq { 7 | RequestHeader header = 1; 8 | } 9 | 10 | message WorldPostSubmitResp { 11 | ResponseHeader header = 1; 12 | } 13 | 14 | service worldService { 15 | //WorldPostSubmit 世界圈帖子发布 16 | rpc WorldPostSubmit(WorldPostSubmitReq) returns (WorldPostSubmitResp); 17 | } 18 | -------------------------------------------------------------------------------- /common/pb/xx.common.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func PlatformFromString(s string) Platform { 8 | switch strings.ToLower(s) { 9 | case "ios": 10 | return Platform_IOS 11 | case "android": 12 | return Platform_ANDROID 13 | case "web": 14 | return Platform_WEB 15 | case "windows": 16 | return Platform_WINDOWS 17 | case "mac": 18 | return Platform_MAC 19 | case "linux": 20 | return Platform_LINUX 21 | case "ipad": 22 | return Platform_Ipad 23 | case "androidpad": 24 | return Platform_AndroidPad 25 | } 26 | return Platform_WEB 27 | } 28 | 29 | func (x Platform) ToString() string { 30 | switch x { 31 | case Platform_IOS: 32 | return "ios" 33 | case Platform_ANDROID: 34 | return "android" 35 | case Platform_WEB: 36 | return "web" 37 | case Platform_WINDOWS: 38 | return "windows" 39 | case Platform_MAC: 40 | return "mac" 41 | case Platform_LINUX: 42 | return "linux" 43 | case Platform_Ipad: 44 | return "ipad" 45 | case Platform_AndroidPad: 46 | return "androidpad" 47 | default: 48 | return "web" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /common/pb/xx.message.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | SingleChatConversationSeparator = "&" 10 | ) 11 | 12 | func GetSingleChatConversationId(id1 string, id2 string) string { 13 | if id1 < id2 { 14 | return fmt.Sprintf("%s%s%s", id1, SingleChatConversationSeparator, id2) 15 | } else { 16 | return fmt.Sprintf("%s%s%s", id2, SingleChatConversationSeparator, id1) 17 | } 18 | } 19 | 20 | func ParseSingleChatConversationId(convId string) (id1 string, id2 string) { 21 | split := strings.Split(convId, SingleChatConversationSeparator) 22 | if len(split) != 2 { 23 | return 24 | } 25 | id1 = split[0] 26 | id2 = split[1] 27 | return 28 | } 29 | 30 | func GetSingleChatOtherId(convId string, id1 string) (id2 string) { 31 | split := strings.Split(convId, SingleChatConversationSeparator) 32 | if len(split) != 2 { 33 | return 34 | } 35 | if split[0] == id1 { 36 | id2 = split[1] 37 | } else { 38 | id2 = split[0] 39 | } 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /common/pb/xx.third.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | const ( 4 | SmsSceneTypeRegister = "register" 5 | SmsSceneTypeUserToken = "userToken" 6 | ) 7 | 8 | const ( 9 | EmailSceneTypeRegister = "register" 10 | EmailSceneTypeUserToken = "userToken" 11 | ) 12 | -------------------------------------------------------------------------------- /common/pb/xx.user.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | const ( 4 | AccountTypeUsername = "username" 5 | AccountTypePassword = "password" 6 | AccountTypePasswordSalt = "passwordSalt" 7 | AccountTypePhone = "phone" 8 | AccountTypePhoneCode = "phoneCode" 9 | AccountTypeEmail = "email" 10 | 11 | AccountTypeStatus = "status" // 账号状态 12 | AccountTypeRole = "role" // 账号角色 13 | ) 14 | 15 | const ( 16 | AccountVerifyTypeSmsCode = "smsCode" 17 | AccountVerifyTypeEmailCode = "emailCode" 18 | AccountVerifyTypeCaptchaId = "captchaId" 19 | AccountVerifyTypeCaptchaCode = "captchaCode" 20 | ) 21 | 22 | const ( 23 | AccountExtraTypeRobotCreatedBy = "robotCreatedBy" // 机器人创建者 24 | ) 25 | 26 | // GetJwtUniqueKey 获取jwt唯一key 27 | // 验证时会先使用token字符串获取userId 28 | // 再使用userId redis> HGET token:$userId jwtUniqueKey 29 | // jwtUniqueKey 一个用户允许在多么多个设备上登录 取决于 jwtUniqueKey的生成规则 30 | func (x *RequestHeader) GetJwtUniqueKey() string { 31 | // 1. 单点登录 32 | // return "" 33 | // 2. 每个平台只能登录一个,意思是我在手机设备A上登录了,那么在手机设备B上登录时,设备A上的token会失效 34 | //switch x.Platform { 35 | //case Platform_IOS, Platform_ANDROID, Platform_Ipad, Platform_AndroidPad: 36 | // return "mobile" 37 | //case Platform_WINDOWS, Platform_MAC, Platform_LINUX: 38 | // return "pc" 39 | //case Platform_WEB: 40 | // return "web" 41 | //default: 42 | // return "other" 43 | //} 44 | // 3. 一个设备同时登录一次该账号,不能重复登录。 45 | // return x.InstallId 46 | // 4. 不限制,即使在同一个设备上,也可以登录多次 47 | //return uuid.New().String() 48 | 49 | // 这里我选择3 50 | return x.InstallId 51 | } 52 | 53 | // AuthErrorExtra 54 | 55 | type AuthErrorExtra struct { 56 | Type AuthErrorType `json:"type"` 57 | Message string `json:"message"` 58 | } 59 | 60 | type AuthErrorType string 61 | 62 | const ( 63 | AuthErrorTypeExpired AuthErrorType = "expired" 64 | AuthErrorTypeInvalid AuthErrorType = "invalid" 65 | AuthErrorTypeReplace AuthErrorType = "replace" 66 | ) 67 | -------------------------------------------------------------------------------- /common/utils/aes.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "errors" 8 | ) 9 | 10 | type xAes struct { 11 | } 12 | 13 | var Aes = &xAes{} 14 | 15 | var DecryptError = errors.New("aes decrypt error") 16 | 17 | func (x *xAes) Decrypt(key, iv []byte, data []byte) (decrypted []byte, err error) { 18 | aesKey := key 19 | aesIv := iv 20 | block, _ := aes.NewCipher(aesKey) 21 | blockMode := cipher.NewCBCDecrypter(block, aesIv) 22 | decrypted = make([]byte, len(data)) 23 | defer func() { 24 | if r := recover(); r != nil { 25 | err = DecryptError 26 | } 27 | }() 28 | blockMode.CryptBlocks(decrypted, data) 29 | decrypted = x.pkcs7UnPadding(decrypted) 30 | return 31 | } 32 | 33 | // Encrypt aes加密 34 | func (x *xAes) Encrypt(key, iv []byte, data []byte) []byte { 35 | aesKey := key 36 | aesIv := iv 37 | block, _ := aes.NewCipher(aesKey) 38 | blockSize := block.BlockSize() 39 | data = x.pkcs7Padding(data, blockSize) 40 | blockMode := cipher.NewCBCEncrypter(block, aesIv) 41 | encrypted := make([]byte, len(data)) 42 | blockMode.CryptBlocks(encrypted, data) 43 | return encrypted 44 | } 45 | 46 | // 使用PKCS7进行填充,IOS也是7 47 | func (x *xAes) pkcs7Padding(ciphertext []byte, blockSize int) []byte { 48 | padding := blockSize - len(ciphertext)%blockSize 49 | repeat := bytes.Repeat([]byte{byte(padding)}, padding) 50 | return append(ciphertext, repeat...) 51 | } 52 | 53 | func (x *xAes) pkcs7UnPadding(origData []byte) []byte { 54 | length := len(origData) 55 | unPadding := int(origData[length-1]) 56 | return origData[:(length - unPadding)] 57 | } 58 | -------------------------------------------------------------------------------- /common/utils/aes_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "testing" 4 | 5 | func TestAes(t *testing.T) { 6 | key := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 7 | 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} 8 | iv := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 9 | data := []byte{1, 2, 3, 4, 5, 6, 7, 8} 10 | encrypted := Aes.Encrypt(key, iv, data) 11 | t.Logf("encrypted: %v", encrypted) 12 | decrypted, err := Aes.Decrypt(key, iv, encrypted) 13 | if err != nil { 14 | t.Errorf("Aes.Decrypt() error = %v", err) 15 | return 16 | } 17 | t.Logf("decrypted: %v", decrypted) 18 | } 19 | -------------------------------------------------------------------------------- /common/utils/any.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func AnyPtr[T any](v T) *T { 4 | return &v 5 | } 6 | 7 | type EnumInSliceType interface { 8 | String() string 9 | } 10 | type AnyInSliceType interface { 11 | string | int8 | int16 | int32 | int64 | int | uint8 | uint16 | uint32 | uint64 | uint | float32 | float64 | bool 12 | } 13 | 14 | func EnumInSlice[T EnumInSliceType](v T, slice []T) bool { 15 | for _, item := range slice { 16 | if item.String() == v.String() { 17 | return true 18 | } 19 | } 20 | return false 21 | } 22 | 23 | func AnyInSlice[T AnyInSliceType](v T, slice []T) bool { 24 | for _, item := range slice { 25 | if item == v { 26 | return true 27 | } 28 | } 29 | return false 30 | } 31 | 32 | func AnySet[T AnyInSliceType](list []T) []T { 33 | // 去重 34 | set := make(map[T]int) 35 | for i, item := range list { 36 | _, ok := set[item] 37 | if !ok { 38 | set[item] = i 39 | } 40 | } 41 | result := make([]T, 0) 42 | for item, _ := range set { 43 | result = append(result, item) 44 | } 45 | //排序 根据 set[item] 的值 46 | for i := 0; i < len(result); i++ { 47 | for j := i + 1; j < len(result); j++ { 48 | if set[result[i]] > set[result[j]] { 49 | result[i], result[j] = result[j], result[i] 50 | } 51 | } 52 | } 53 | return result 54 | } 55 | 56 | func AnyString(o any) string { 57 | switch v := o.(type) { 58 | case string: 59 | return v 60 | case []byte: 61 | return string(v) 62 | case *string: 63 | if v == nil { 64 | return "" 65 | } 66 | return *v 67 | case int8: 68 | return Number.Int64ToString(int64(v)) 69 | case int16: 70 | return Number.Int64ToString(int64(v)) 71 | case int32: 72 | return Number.Int64ToString(int64(v)) 73 | case int64: 74 | return Number.Int64ToString(v) 75 | case int: 76 | return Number.Int64ToString(int64(v)) 77 | case uint8: 78 | return Number.Int64ToString(int64(v)) 79 | case uint16: 80 | return Number.Int64ToString(int64(v)) 81 | case uint32: 82 | return Number.Int64ToString(int64(v)) 83 | case uint64: 84 | return Number.Int64ToString(int64(v)) 85 | case uint: 86 | return Number.Int64ToString(int64(v)) 87 | case float32: 88 | return Number.Float64ToString(float64(v)) 89 | case float64: 90 | return Number.Float64ToString(v) 91 | case bool: 92 | if v { 93 | return "true" 94 | } else { 95 | return "false" 96 | } 97 | default: 98 | return Json.MarshalToString(v) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /common/utils/any_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAnySet(t *testing.T) { 8 | set := AnySet([]string{"a", "c", "c", "a", "d"}) 9 | t.Logf("%v", set) 10 | set = AnySet([]string{}) 11 | t.Logf("%v", set) 12 | } 13 | -------------------------------------------------------------------------------- /common/utils/bytes.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | ) 7 | 8 | type xBytes struct { 9 | } 10 | 11 | var Bytes = &xBytes{} 12 | 13 | func (x *xBytes) NewNopCloser(b []byte) io.ReadCloser { 14 | return io.NopCloser(bytes.NewReader(b)) 15 | } 16 | -------------------------------------------------------------------------------- /common/utils/ecdh/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/elliptic" 5 | "crypto/rand" 6 | "fmt" 7 | "github.com/cherish-chat/xxim-server/common/utils" 8 | ) 9 | 10 | func main() { 11 | e := utils.NewECDH(elliptic.P256()) 12 | privKey, pubKey, err := e.GenerateKey(rand.Reader) 13 | if err != nil { 14 | return 15 | } 16 | fmt.Println(e.HexEncodePublicKeyToString(pubKey)) 17 | 18 | publicKey := "0475de22a325d0e73f1beebdbbb4e2219a9e5bbe1efcbfa83c51ae1142ed202a2d89b5692c63ca30d2ceb795c7b4744fca9df11c1977246dd4e5076c74ae5fc930" 19 | // 等待用户输入 publicKey 20 | fmt.Println("请输入公钥:") 21 | fmt.Scanln(&publicKey) 22 | privateKey := e.HexEncodePrivateKeyToString(privKey) 23 | fmt.Println(privateKey) 24 | 25 | secret, err := utils.GetECDHSecret(privateKey, publicKey) 26 | if err != nil { 27 | return 28 | } 29 | fmt.Println(secret) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /common/utils/ecdh_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/elliptic" 7 | "crypto/rand" 8 | "fmt" 9 | "testing" 10 | ) 11 | 12 | func Test_1(t *testing.T) { 13 | e := NewECDH(elliptic.P256()) 14 | testECDH(e, t) 15 | } 16 | 17 | func testECDH(e ECDH, t testing.TB) { 18 | var privKey1, privKey2 crypto.PrivateKey 19 | var pubKey1, pubKey2 crypto.PublicKey 20 | var pubKey1Buf, pubKey2Buf []byte 21 | var err error 22 | var ok bool 23 | var secret1, secret2 []byte 24 | 25 | privKey1, pubKey1, err = e.GenerateKey(rand.Reader) 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | privKey2, pubKey2, err = e.GenerateKey(rand.Reader) 30 | if err != nil { 31 | t.Error(err) 32 | } 33 | 34 | pubKey1Buf = e.Marshal(pubKey1) 35 | pubKey2Buf = e.Marshal(pubKey2) 36 | 37 | pubKey1, ok = e.Unmarshal(pubKey1Buf) 38 | if !ok { 39 | t.Fatalf("Unmarshal does not work") 40 | } 41 | 42 | pubKey2, ok = e.Unmarshal(pubKey2Buf) 43 | if !ok { 44 | t.Fatalf("Unmarshal does not work") 45 | } 46 | 47 | secret1, err = e.GenerateSharedSecret(privKey1, pubKey2) 48 | if err != nil { 49 | t.Error(err) 50 | } 51 | secret2, err = e.GenerateSharedSecret(privKey2, pubKey1) 52 | if err != nil { 53 | t.Error(err) 54 | } 55 | 56 | if !bytes.Equal(secret1, secret2) { 57 | t.Fatalf("The two shared keys: %d, %d do not match", secret1, secret2) 58 | } 59 | t.Logf("The shared key is: %d, len: %d", secret1, len(secret1)) 60 | 61 | // iv 取中间16位 62 | iv := secret1[8:24] 63 | // 模拟服务端加密 64 | encryptString := Aes.Encrypt(secret1, iv, []byte("hello world")) 65 | fmt.Println(string(encryptString)) 66 | 67 | // 模拟客户端解密 68 | decryptString, err := Aes.Decrypt(secret1, iv, encryptString) 69 | if err != nil { 70 | t.Error(err) 71 | } 72 | fmt.Println(string(decryptString)) 73 | } 74 | 75 | func TestGetECDHSecret(t *testing.T) { 76 | publicKey := "04421006ef7fdfa44baa1df88bdff6be915f3e20938041604deec9e3f8ba532cf1248dfb90f874a905464a1e06e7154bb0850586d2c3e996e5260d97efc805d34a" 77 | // 等待用户输入 publicKey 78 | fmt.Println("请输入公钥:") 79 | fmt.Scanln(&publicKey) 80 | e := NewECDH(elliptic.P256()) 81 | privKey, pubKey, err := e.GenerateKey(rand.Reader) 82 | if err != nil { 83 | return 84 | } 85 | fmt.Println(e.HexEncodePublicKeyToString(pubKey)) 86 | 87 | privateKey := e.HexEncodePrivateKeyToString(privKey) 88 | fmt.Println(privateKey) 89 | 90 | secret, err := GetECDHSecret(privateKey, publicKey) 91 | if err != nil { 92 | return 93 | } 94 | fmt.Println(secret) 95 | 96 | //keyByte, err := hex.DecodeString(secret) 97 | //encryptString := Aes.Encrypt("1", "hello world", keyByte) 98 | //if err != nil { 99 | // return 100 | //} 101 | //fmt.Println(string(encryptString)) 102 | } 103 | -------------------------------------------------------------------------------- /common/utils/error.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "errors" 4 | 5 | type xError struct { 6 | } 7 | 8 | var Error = &xError{} 9 | 10 | func (x *xError) DeepUnwrap(err error) error { 11 | e := errors.Unwrap(err) 12 | if e == nil { 13 | return err 14 | } else { 15 | return x.DeepUnwrap(e) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /common/utils/gorm.go: -------------------------------------------------------------------------------- 1 | package utils 2 | -------------------------------------------------------------------------------- /common/utils/http.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | type xHttp struct { 10 | } 11 | 12 | var Http = &xHttp{} 13 | 14 | func (x *xHttp) GetClientIP(r *http.Request) string { 15 | // 先取 X-Real-IP 16 | ip := r.Header.Get("X-Real-IP") 17 | if ip == "" { 18 | // 取 X-Forwarded-For 19 | ip = r.Header.Get("X-Forwarded-For") 20 | if ip == "" { 21 | // 取 RemoteAddr 22 | ip = r.RemoteAddr 23 | } 24 | } 25 | return strings.Split(ip, ",")[0] 26 | } 27 | 28 | func (x *xHttp) GetResponseBody(response *http.Response) []byte { 29 | defer response.Body.Close() 30 | body, _ := io.ReadAll(response.Body) 31 | return body 32 | } 33 | -------------------------------------------------------------------------------- /common/utils/json.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type xJson struct { 9 | } 10 | 11 | var Json = &xJson{} 12 | 13 | func (x *xJson) Marshal(v interface{}) ([]byte, error) { 14 | return json.Marshal(v) 15 | } 16 | 17 | func (x *xJson) MarshalToString(v interface{}) string { 18 | data, err := json.Marshal(v) 19 | if err != nil { 20 | return fmt.Sprintf("%v", v) 21 | } 22 | return string(data) 23 | } 24 | 25 | func (x *xJson) Unmarshal(data []byte, v interface{}) error { 26 | return json.Unmarshal(data, v) 27 | } 28 | 29 | func (x *xJson) MarshalToBytes(v interface{}) []byte { 30 | data, err := json.Marshal(v) 31 | if err != nil { 32 | return []byte(fmt.Sprintf("%v", v)) 33 | } 34 | return data 35 | } 36 | -------------------------------------------------------------------------------- /common/utils/map.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "go.mongodb.org/mongo-driver/bson" 4 | 5 | type xMap struct { 6 | } 7 | 8 | var Map = &xMap{} 9 | 10 | func (x *xMap) SS2SA(ss map[string]string) map[string]any { 11 | m := make(map[string]any) 12 | for k, v := range ss { 13 | m[k] = v 14 | } 15 | return m 16 | } 17 | 18 | func (x *xMap) SSMFromString(s string) SSM { 19 | ssm := make(SSM) 20 | _ = Json.Unmarshal([]byte(s), &ssm) 21 | return ssm 22 | } 23 | 24 | type SSM map[string]string 25 | 26 | func (ssm SSM) ToSA() map[string]any { 27 | m := make(map[string]any) 28 | for k, v := range ssm { 29 | m[k] = v 30 | } 31 | return m 32 | } 33 | 34 | func (ssm SSM) ToSS() map[string]string { 35 | return ssm 36 | } 37 | 38 | func (ssm SSM) Get(k string) string { 39 | v, _ := ssm[k] 40 | return v 41 | } 42 | 43 | func (ssm SSM) GetInt64(k string) int64 { 44 | v, _ := ssm[k] 45 | return Number.Any2Int64(v) 46 | } 47 | 48 | func (ssm SSM) GetOrDefault(k string, def string) string { 49 | v, ok := ssm[k] 50 | if !ok { 51 | return def 52 | } 53 | return v 54 | } 55 | 56 | func (ssm SSM) Marshal() string { 57 | return Json.MarshalToString(ssm) 58 | } 59 | 60 | func NewSSMFromBsonM(m bson.M) SSM { 61 | ssm := make(SSM) 62 | for k, v := range m { 63 | ssm[k] = AnyString(v) 64 | } 65 | return ssm 66 | } 67 | -------------------------------------------------------------------------------- /common/utils/mongo.go: -------------------------------------------------------------------------------- 1 | package utils 2 | -------------------------------------------------------------------------------- /common/utils/mq.go: -------------------------------------------------------------------------------- 1 | package utils 2 | -------------------------------------------------------------------------------- /common/utils/number.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "strconv" 4 | 5 | type xNumber struct { 6 | } 7 | 8 | var Number = &xNumber{} 9 | 10 | func (x *xNumber) Int64ToString(i int64) string { 11 | return strconv.FormatInt(i, 10) 12 | } 13 | 14 | func (x *xNumber) Float64ToString(f float64) string { 15 | return strconv.FormatFloat(f, 'f', -1, 64) 16 | } 17 | 18 | func (x *xNumber) Any2Int64(v any) int64 { 19 | switch v.(type) { 20 | case int: 21 | return int64(v.(int)) 22 | case int8: 23 | return int64(v.(int8)) 24 | case int16: 25 | return int64(v.(int16)) 26 | case int32: 27 | return int64(v.(int32)) 28 | case int64: 29 | return v.(int64) 30 | case uint: 31 | return int64(v.(uint)) 32 | case uint8: 33 | return int64(v.(uint8)) 34 | case uint16: 35 | return int64(v.(uint16)) 36 | case uint32: 37 | return int64(v.(uint32)) 38 | case uint64: 39 | return int64(v.(uint64)) 40 | case float32: 41 | return int64(v.(float32)) 42 | case float64: 43 | return int64(v.(float64)) 44 | case string: 45 | i, _ := strconv.ParseInt(v.(string), 10, 64) 46 | return i 47 | default: 48 | return 0 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /common/utils/proto.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "google.golang.org/protobuf/proto" 4 | 5 | type xProto struct { 6 | } 7 | 8 | var Proto = &xProto{} 9 | 10 | func (x *xProto) Marshal(m proto.Message) []byte { 11 | data, _ := proto.Marshal(m) 12 | return data 13 | } 14 | -------------------------------------------------------------------------------- /common/utils/pwd.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "golang.org/x/crypto/pbkdf2" 7 | ) 8 | 9 | type xPwd struct { 10 | } 11 | 12 | var Pwd = &xPwd{} 13 | 14 | func (x *xPwd) GeneratePwd(original string, salt string) string { 15 | dk := pbkdf2.Key([]byte(original), []byte(salt), 1000, 32, sha256.New) 16 | return hex.EncodeToString(dk) 17 | } 18 | 19 | func (x *xPwd) VerifyPwd(input, db string, salt string) bool { 20 | return x.GeneratePwd(input, salt) == db 21 | } 22 | -------------------------------------------------------------------------------- /common/utils/reandom.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type xRandom struct { 10 | } 11 | 12 | var Random = &xRandom{} 13 | 14 | func (x *xRandom) Int(length int) int { 15 | rand.Seed(time.Now().UnixNano()) 16 | // 生成随机数 长度为length 17 | // 比如length为6,那么生成的随机数为100000~999999之间的数 18 | // start = 10 e length-1次方 19 | start := math.Pow10(length - 1) 20 | // end = 10 e length次方 - 1 21 | end := math.Pow10(length) - 1 22 | return rand.Intn(int(end)-int(start)) + int(start) 23 | } 24 | 25 | func (x *xRandom) SliceString(list []string) string { 26 | rand.Seed(time.Now().UnixNano()) 27 | return list[rand.Intn(len(list))] 28 | } 29 | -------------------------------------------------------------------------------- /common/utils/reandom_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "testing" 4 | 5 | func Test_xRandom_Int(t *testing.T) { 6 | type args struct { 7 | length int 8 | } 9 | tests := []struct { 10 | name string 11 | args args 12 | }{ 13 | { 14 | name: "test1", 15 | args: args{ 16 | length: 6, 17 | }, 18 | }, 19 | } 20 | for _, tt := range tests { 21 | for i := 0; i < 10000; i++ { 22 | t.Run(tt.name, func(t *testing.T) { 23 | x := &xRandom{} 24 | if got := x.Int(tt.args.length); len(Number.Int64ToString(int64(got))) != tt.args.length { 25 | t.Fatalf("Int() = %v, wantlen %v", got, tt.args.length) 26 | } 27 | }) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/utils/redis.go: -------------------------------------------------------------------------------- 1 | package utils 2 | -------------------------------------------------------------------------------- /common/utils/regex.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "regexp" 4 | 5 | type xRegex struct { 6 | } 7 | 8 | var Regex = &xRegex{} 9 | 10 | func (x *xRegex) Match(pattern string, s string) bool { 11 | matched, err := regexp.MatchString(pattern, s) 12 | if err != nil { 13 | return false 14 | } 15 | return matched 16 | } 17 | -------------------------------------------------------------------------------- /common/utils/retry.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/avast/retry-go" 5 | _ "github.com/avast/retry-go" 6 | ) 7 | 8 | type xRetry struct { 9 | } 10 | 11 | var Retry = xRetry{} 12 | 13 | func (x *xRetry) Do(f func() error) { 14 | _ = retry.Do(f, retry.Attempts(999)) 15 | } 16 | -------------------------------------------------------------------------------- /common/utils/sdp.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "github.com/pion/sdp/v2" 4 | 5 | type xSdp struct { 6 | } 7 | 8 | var Sdp = &xSdp{} 9 | 10 | func (x *xSdp) GetClientIp(sd *sdp.SessionDescription) string { 11 | for _, m := range sd.MediaDescriptions { 12 | return m.ConnectionInformation.Address.Address 13 | } 14 | return "" 15 | } 16 | -------------------------------------------------------------------------------- /common/utils/snowflake.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/bwmarrin/snowflake" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type xSnowflake struct { 10 | int64Chan chan int64 11 | stringChan chan string 12 | nd *snowflake.Node 13 | nodId int64 14 | uniquePodId string 15 | } 16 | 17 | var Snowflake *xSnowflake 18 | 19 | func init() { 20 | rand.Seed(time.Now().UnixNano()) 21 | Snowflake = &xSnowflake{ 22 | int64Chan: make(chan int64), 23 | stringChan: make(chan string), 24 | } 25 | Snowflake.nodId = int64(rand.Intn(128)) 26 | Snowflake.nd, _ = snowflake.NewNode(Snowflake.nodId) 27 | Snowflake.uniquePodId = Snowflake.nd.Generate().String() 28 | Snowflake.loop() 29 | } 30 | 31 | func (x *xSnowflake) Int64() int64 { 32 | return <-x.int64Chan 33 | } 34 | 35 | func (x *xSnowflake) String() string { 36 | return Md5(<-x.stringChan) 37 | } 38 | 39 | func (x *xSnowflake) loop() { 40 | go func() { 41 | for { 42 | x.int64Chan <- x.nd.Generate().Int64() 43 | } 44 | }() 45 | go func() { 46 | for { 47 | x.stringChan <- x.nd.Generate().String() 48 | } 49 | }() 50 | } 51 | -------------------------------------------------------------------------------- /common/utils/string.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | type xString struct { 4 | } 5 | 6 | var String = &xString{} 7 | 8 | // FirstUtf8 获取字符串的第一个utf8字符,如果字符串为空,则返回” 9 | // 如果字符串不是ascii编码,则返回第一个utf8字符,如果是ascii编码,则返回第一个字符 10 | func (x *xString) FirstUtf8(s string) string { 11 | if len(s) == 0 { 12 | return "" 13 | } 14 | if s[0] < 128 { 15 | return s[0:1] 16 | } else if len(s) >= 3 { 17 | return s[0:3] 18 | } else { 19 | return s 20 | } 21 | } 22 | 23 | func (x *xString) Utf8Split(s string, length int) string { 24 | if len(s) == 0 { 25 | return "" 26 | } 27 | var result string 28 | for i := 0; i < length; i++ { 29 | first := x.FirstUtf8(s) 30 | if first == "" { 31 | break 32 | } 33 | result += first 34 | //去掉第一个字符 35 | s = s[len(first):] 36 | } 37 | return result 38 | } 39 | -------------------------------------------------------------------------------- /common/utils/string_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "testing" 4 | 5 | func Test_xString_FirstUtf8(t *testing.T) { 6 | type args struct { 7 | s string 8 | } 9 | tests := []struct { 10 | name string 11 | args args 12 | want string 13 | }{{ 14 | name: "test1", 15 | args: args{ 16 | s: "test", 17 | }, 18 | want: "t", 19 | }, { 20 | name: "test2", 21 | args: args{ 22 | s: "你好", 23 | }, 24 | want: "你", 25 | }, { 26 | name: "test3", 27 | args: args{ 28 | s: "", 29 | }, 30 | want: "", 31 | }, { 32 | name: "test4", 33 | args: args{ 34 | //日语 35 | s: "こんにちは", 36 | }, 37 | want: "こ", 38 | }, { 39 | name: "test5", 40 | args: args{ 41 | //韩语 42 | s: "안녕하세요", 43 | }, 44 | want: "안", 45 | }, { 46 | name: "test6", 47 | args: args{ 48 | //数字 49 | s: "1办洒", 50 | }, 51 | want: "1", 52 | }} 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | x := &xString{} 56 | if got := x.FirstUtf8(tt.args.s); got != tt.want { 57 | t.Errorf("FirstUtf8() = %v, want %v", got, tt.want) 58 | } 59 | }) 60 | } 61 | } 62 | 63 | func Test_xString_Utf8Split(t *testing.T) { 64 | type args struct { 65 | s string 66 | length int 67 | } 68 | tests := []struct { 69 | name string 70 | args args 71 | want string 72 | }{ 73 | { 74 | name: "test1", 75 | args: args{ 76 | s: "赵钱孙李", 77 | length: 2, 78 | }, 79 | want: "赵钱", 80 | }, 81 | { 82 | name: "test1", 83 | args: args{ 84 | s: "赵钱孙李", 85 | length: 5, 86 | }, 87 | want: "赵钱孙李", 88 | }, 89 | { 90 | name: "test1", 91 | args: args{ 92 | s: "a赵钱孙李", 93 | length: 3, 94 | }, 95 | want: "a赵钱", 96 | }, 97 | } 98 | for _, tt := range tests { 99 | t.Run(tt.name, func(t *testing.T) { 100 | x := &xString{} 101 | if got := x.Utf8Split(tt.args.s, tt.args.length); got != tt.want { 102 | t.Errorf("Utf8Split() = %v, want %v", got, tt.want) 103 | } 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /common/utils/time.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "time" 4 | 5 | type xTime struct { 6 | } 7 | 8 | var Time = &xTime{} 9 | 10 | func (x *xTime) Now13() int64 { 11 | now := time.Now() 12 | return now.UnixMilli() 13 | } 14 | 15 | func (x *xTime) Int64ToString(i int64) string { 16 | // 判断是不是10位 17 | if i <= 9999999999 && i >= 1000000000 { 18 | i = i * 1000 19 | } 20 | // 判断是不是19位 21 | if i >= 1000000000000000000 { 22 | i = i / 1000000 23 | } 24 | if i == 0 { 25 | return "" 26 | } 27 | return time.UnixMilli(i).Format("2006-01-02 15:04:05") 28 | } 29 | -------------------------------------------------------------------------------- /common/utils/trace.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "github.com/zeromicro/go-zero/core/trace" 6 | "go.opentelemetry.io/otel" 7 | "go.opentelemetry.io/otel/attribute" 8 | "go.opentelemetry.io/otel/codes" 9 | oteltrace "go.opentelemetry.io/otel/trace" 10 | ) 11 | 12 | type xTrace struct { 13 | } 14 | 15 | var Trace = &xTrace{} 16 | 17 | func (x *xTrace) Span(ctx context.Context, spanName string, do func(context.Context) error, attributes map[string]string) { 18 | if do == nil { 19 | return 20 | } 21 | if attributes == nil { 22 | attributes = make(map[string]string) 23 | } 24 | var kvs []attribute.KeyValue 25 | for k, v := range attributes { 26 | kvs = append(kvs, attribute.String(k, v)) 27 | } 28 | spanKind := oteltrace.SpanKindInternal 29 | tracer := otel.GetTracerProvider().Tracer(trace.TraceName) 30 | spanCtx, span := tracer.Start(ctx, spanName, 31 | oteltrace.WithSpanKind(spanKind), 32 | oteltrace.WithAttributes(kvs...), 33 | ) 34 | defer span.End() 35 | err := do(spanCtx) 36 | if err != nil { 37 | span.SetStatus(codes.Error, err.Error()) 38 | } else { 39 | span.SetStatus(codes.Ok, "") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /common/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "github.com/zeromicro/go-zero/core/netx" 7 | "os" 8 | ) 9 | 10 | const ( 11 | envPodIp = "POD_IP" 12 | ) 13 | 14 | func Md5(s string) string { 15 | return Md5Bytes([]byte(s)) 16 | } 17 | 18 | func Md5Bytes(b []byte) string { 19 | h := md5.New() 20 | h.Write(b) 21 | cipher := h.Sum(nil) 22 | return hex.EncodeToString(cipher) 23 | } 24 | 25 | func Md516(s string) string { 26 | // 将中间的第9位到第24位提取出来 27 | return Md5(s)[8:24] 28 | } 29 | 30 | func GetPodIp() string { 31 | ip := os.Getenv(envPodIp) 32 | if len(ip) == 0 { 33 | ip = netx.InternalIp() 34 | } 35 | return ip 36 | } 37 | -------------------------------------------------------------------------------- /common/values.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | // Version 版本号 5 | Version = "0.0.1.rc.1" 6 | // TraceName 链路追踪名称 7 | TraceName = "xxim" 8 | ) 9 | -------------------------------------------------------------------------------- /common/xcache/goredis.go: -------------------------------------------------------------------------------- 1 | package xcache 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | goredis "github.com/go-redis/redis/v8" 7 | "github.com/zeromicro/go-zero/core/logx" 8 | "github.com/zeromicro/go-zero/core/stores/redis" 9 | "os" 10 | ) 11 | 12 | func NewGoRedis(redisConf *redis.RedisConf) goredis.UniversalClient { 13 | tlsConfig := &tls.Config{ 14 | InsecureSkipVerify: true, 15 | } 16 | if !redisConf.Tls { 17 | tlsConfig = nil 18 | } 19 | client := goredis.NewUniversalClient(&goredis.UniversalOptions{ 20 | Addrs: []string{ 21 | redisConf.Host, 22 | }, 23 | DB: 0, 24 | Dialer: nil, 25 | OnConnect: nil, 26 | Username: "", 27 | Password: redisConf.Pass, 28 | SentinelUsername: "", 29 | SentinelPassword: "", 30 | MaxRetries: 0, 31 | MinRetryBackoff: 0, 32 | MaxRetryBackoff: 0, 33 | DialTimeout: 0, 34 | ReadTimeout: 0, 35 | WriteTimeout: 0, 36 | PoolFIFO: false, 37 | PoolSize: 0, 38 | MinIdleConns: 0, 39 | MaxConnAge: 0, 40 | PoolTimeout: 0, 41 | IdleTimeout: 0, 42 | IdleCheckFrequency: 0, 43 | TLSConfig: tlsConfig, 44 | MaxRedirects: 0, 45 | ReadOnly: false, 46 | RouteByLatency: false, 47 | RouteRandomly: false, 48 | MasterName: "", 49 | }) 50 | ctx, cancel := context.WithTimeout(context.Background(), redisConf.PingTimeout) 51 | defer cancel() 52 | err := client.Ping(ctx).Err() 53 | if err != nil { 54 | logx.Errorf("ping redis failed: %s", err.Error()) 55 | os.Exit(1) 56 | return nil 57 | } 58 | return client 59 | } 60 | -------------------------------------------------------------------------------- /common/xcache/lock.go: -------------------------------------------------------------------------------- 1 | package xcache 2 | 3 | import ( 4 | "context" 5 | "github.com/zeromicro/go-zero/core/stores/redis" 6 | ) 7 | 8 | type xLock struct { 9 | } 10 | 11 | var Lock = &xLock{} 12 | 13 | func (x *xLock) Lock(ctx context.Context, rc *redis.Redis, key string, expireSecond int) (bool, error) { 14 | ok, err := rc.SetnxExCtx(ctx, key, "1", expireSecond) 15 | if err != nil { 16 | return false, err 17 | } 18 | return ok, nil 19 | } 20 | 21 | func (x *xLock) Unlock(ctx context.Context, rc *redis.Redis, key string) error { 22 | _, err := rc.DelCtx(ctx, key) 23 | if err != nil { 24 | return err 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /common/xcache/redis.go: -------------------------------------------------------------------------------- 1 | package xcache 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/core/logx" 5 | "github.com/zeromicro/go-zero/core/stores/redis" 6 | "os" 7 | ) 8 | 9 | func MustNewRedis(config redis.RedisConf) *redis.Redis { 10 | client, err := redis.NewRedis(config) 11 | if err != nil { 12 | logx.Errorf("redis.NewRedis error: %v", err) 13 | os.Exit(1) 14 | return nil 15 | } 16 | return client 17 | } 18 | -------------------------------------------------------------------------------- /common/xcache/rediskey.go: -------------------------------------------------------------------------------- 1 | package xcache 2 | 3 | import "fmt" 4 | 5 | type xRedisVal struct { 6 | IncrKeyGroupId string 7 | HashKeyConvMaxSeq string 8 | IncrKeyNoticeSort string 9 | } 10 | 11 | var RedisVal = &xRedisVal{ 12 | IncrKeyGroupId: "group:i:group_id", 13 | HashKeyConvMaxSeq: "max_seq", 14 | IncrKeyNoticeSort: "notice:i:sort", 15 | } 16 | 17 | func (x *xRedisVal) LockKeyUserUsername(username string) string { 18 | return fmt.Sprintf("user:l:username:%s", username) 19 | } 20 | 21 | func (x *xRedisVal) LockKeyUserPhone(phone string, phoneCode string) string { 22 | return fmt.Sprintf("user:l:phone:%s:%s", phone, phoneCode) 23 | } 24 | 25 | func (x *xRedisVal) LockKeyUserEmail(email string) string { 26 | return fmt.Sprintf("user:l:email:%s:%s", email) 27 | } 28 | 29 | func (x *xRedisVal) HashKeyConvKv(convId string, convTyp int32) string { 30 | return fmt.Sprintf("message:h:conv_kv:%s_%d", convId, convTyp) 31 | } 32 | 33 | func (x *xRedisVal) HashKeyConvMinSeq(userId string) string { 34 | return fmt.Sprintf("min_seq:%s", userId) 35 | } 36 | -------------------------------------------------------------------------------- /common/xconf/load.go: -------------------------------------------------------------------------------- 1 | package xconf 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/utils" 5 | "github.com/zeromicro/go-zero/core/conf" 6 | "log" 7 | "strings" 8 | ) 9 | 10 | // MustLoad loads config into v from path, exits on error. 11 | func MustLoad(path string, v any, opts ...conf.Option) { 12 | // path是否以http://或者https://开头 13 | // 如果是,则调用LoadRemote方法 14 | // 否则调用Load方法 15 | if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { 16 | if err := LoadRemote(path, v); err != nil { 17 | log.Fatalf("error: config file %s, %s", path, err.Error()) 18 | } 19 | return 20 | } 21 | if err := conf.Load(path, v, opts...); err != nil { 22 | log.Fatalf("error: config file %s, %s", path, err.Error()) 23 | } 24 | } 25 | 26 | // LoadRemote loads config into v from remote path. 27 | func LoadRemote(path string, v any) error { 28 | bytes, err := utils.File.Download(path) 29 | if err != nil { 30 | return err 31 | } 32 | filename := utils.File.FilenameFromUrl(path) 33 | // 存储到本地 34 | if err := utils.File.Save(filename, bytes, 0644); err != nil { 35 | return err 36 | } 37 | return conf.Load(filename, v) 38 | } 39 | -------------------------------------------------------------------------------- /common/xmgo/index.go: -------------------------------------------------------------------------------- 1 | package xmgo 2 | 3 | import opts "github.com/qiniu/qmgo/options" 4 | 5 | type Indexer interface { 6 | GetIndexes() []opts.IndexModel 7 | } 8 | -------------------------------------------------------------------------------- /common/xmgo/mongo.go: -------------------------------------------------------------------------------- 1 | package xmgo 2 | 3 | import ( 4 | "context" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "github.com/qiniu/qmgo" 7 | "github.com/zeromicro/go-zero/core/logx" 8 | "go.mongodb.org/mongo-driver/mongo/readpref" 9 | "os" 10 | "time" 11 | ) 12 | 13 | type MongoCollectionConf struct { 14 | // Uri is the connection URI for the mongo instance 15 | // example: mongodb://$user:$pwd@$host:$port 16 | // example: mongodb://172.88.10.41:27017/?replicaSet=rs0 17 | Uri string 18 | Database string 19 | Collection string 20 | MaxPoolSize uint64 `json:",default=100"` 21 | MinPoolSize uint64 `json:",optional"` 22 | SocketTimeoutMS int64 `json:",default=300000"` // default is 5 minutes 23 | //ReadPreference 读偏好模式 24 | //DefaultMode:0 默认不设置 25 | //PrimaryMode:1 从主节点读取数据 26 | //PrimaryPreferredMode:2 优先从主节点读取数据,如果主节点不可用,从从节点读取数据 27 | //SecondaryMode:3 从从节点读取数据 28 | //SecondaryPreferredMode:4 优先从从节点读取数据,如果从节点不可用,从主节点读取数据 29 | //NearestMode:5 从最近的成员读取数据 30 | ReadPreference int `json:",optional"` // default is primary 31 | } 32 | 33 | func MustNewMongoCollection(config MongoCollectionConf, model any) *qmgo.QmgoClient { 34 | ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5) 35 | defer cancelFunc() 36 | var readPreference *qmgo.ReadPref 37 | if config.ReadPreference > 0 && config.ReadPreference < 6 { 38 | readPreference = &qmgo.ReadPref{ 39 | Mode: readpref.Mode(config.ReadPreference), 40 | } 41 | } 42 | qmgoClient, err := qmgo.Open(ctx, &qmgo.Config{ 43 | Uri: config.Uri, 44 | Database: config.Database, 45 | Coll: config.Collection, 46 | ConnectTimeoutMS: utils.AnyPtr(int64(5000)), 47 | MaxPoolSize: utils.AnyPtr(config.MaxPoolSize), 48 | MinPoolSize: utils.AnyPtr(config.MinPoolSize), 49 | SocketTimeoutMS: utils.AnyPtr(config.SocketTimeoutMS), 50 | ReadPreference: readPreference, 51 | }) 52 | if err != nil { 53 | logx.Errorf("open mongo error: %v", err) 54 | os.Exit(1) 55 | return nil 56 | } 57 | if indexer, ok := model.(Indexer); ok { 58 | err = CreateIndex(ctx, qmgoClient, indexer) 59 | if err != nil { 60 | logx.Errorf("create index error: %v", err) 61 | os.Exit(1) 62 | return nil 63 | } 64 | } 65 | return qmgoClient 66 | } 67 | 68 | func CreateIndex(ctx context.Context, qmgoClient *qmgo.QmgoClient, indexer Indexer) error { 69 | indexes := indexer.GetIndexes() 70 | if len(indexes) == 0 { 71 | return nil 72 | } 73 | return qmgoClient.CreateIndexes(ctx, indexes) 74 | } 75 | -------------------------------------------------------------------------------- /common/xmgo/utils.go: -------------------------------------------------------------------------------- 1 | package xmgo 2 | 3 | import ( 4 | "context" 5 | "github.com/qiniu/qmgo" 6 | opts "github.com/qiniu/qmgo/options" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | ) 9 | 10 | // ErrIsDup 判断mongo error是不是唯一索引冲突 11 | func ErrIsDup(err error) bool { 12 | if writeException, ok := err.(mongo.WriteException); ok { 13 | for _, writeError := range writeException.WriteErrors { 14 | if writeError.Code == 11000 { 15 | return true 16 | } 17 | } 18 | } else { 19 | if bulkWriteException, ok := err.(*mongo.BulkWriteException); ok { 20 | for _, writeError := range bulkWriteException.WriteErrors { 21 | if writeError.Code == 11000 { 22 | return true 23 | } 24 | } 25 | } 26 | } 27 | return false 28 | } 29 | 30 | // BatchInsertMany 批量插入 防止一次插入太多数据 mongodb限制一次插入的数据量 31 | func BatchInsertMany[T any]( 32 | //集合 33 | collection *qmgo.QmgoClient, 34 | //上下文 35 | ctx context.Context, 36 | //数据 37 | models []T, 38 | //每次插入的数量 39 | batchSize int, 40 | //insertMany选项 41 | opts ...opts.InsertManyOptions, 42 | ) error { 43 | if batchSize <= 0 { 44 | batchSize = 1000 45 | } 46 | var ( 47 | startIndex int 48 | endIndex int 49 | ) 50 | for { 51 | startIndex = endIndex 52 | endIndex = startIndex + batchSize 53 | if endIndex > len(models) { 54 | endIndex = len(models) 55 | } 56 | if startIndex == endIndex { 57 | break 58 | } 59 | _, err := collection.InsertMany(ctx, models[startIndex:endIndex], opts...) 60 | if err != nil { 61 | return err 62 | } 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /common/xmq/topic.go: -------------------------------------------------------------------------------- 1 | package xmq 2 | 3 | const ( 4 | TopicAfterRegister Topic = "user:afterRegister" 5 | 6 | TopicMessageBatchSend Topic = "message:batchSend" 7 | TopicNoticeBatchSend Topic = "notice:batchSend" 8 | ) 9 | -------------------------------------------------------------------------------- /common/xmq/types.go: -------------------------------------------------------------------------------- 1 | package xmq 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | type Topic = string 9 | 10 | type ProduceOption struct { 11 | // Delay 延迟时间 12 | delay *time.Duration 13 | } 14 | 15 | type ProduceOptionFunction func(o *ProduceOption) 16 | 17 | // ProduceWithDelay 设置延迟时间 18 | func ProduceWithDelay(delay time.Duration) ProduceOptionFunction { 19 | return func(o *ProduceOption) { 20 | o.delay = &delay 21 | } 22 | } 23 | 24 | type HandlerFunc func(ctx context.Context, topic string, msg []byte) error 25 | 26 | type MQ interface { 27 | // Produce 生产消息 28 | Produce(ctx context.Context, topic Topic, msg []byte, opts ...ProduceOptionFunction) error 29 | RegisterHandler(topic Topic, handler HandlerFunc) 30 | StartConsuming() 31 | } 32 | -------------------------------------------------------------------------------- /common/xorm/mysql.go: -------------------------------------------------------------------------------- 1 | package xorm 2 | 3 | import ( 4 | "context" 5 | "github.com/zeromicro/go-zero/core/logx" 6 | "gorm.io/driver/mysql" 7 | "gorm.io/gorm" 8 | "gorm.io/gorm/logger" 9 | "gorm.io/gorm/utils" 10 | "os" 11 | "time" 12 | ) 13 | 14 | type MysqlConf struct { 15 | DataSource string 16 | MaxIdleConns int `json:",default=10"` 17 | MaxOpenConns int `json:",default=30"` 18 | } 19 | 20 | type logXLogger struct { 21 | } 22 | 23 | func (l *logXLogger) LogMode(level logger.LogLevel) logger.Interface { 24 | return l 25 | } 26 | 27 | func (l *logXLogger) Info(ctx context.Context, s string, i ...interface{}) { 28 | logx.WithContext(ctx).Infof(s, i...) 29 | } 30 | 31 | func (l *logXLogger) Warn(ctx context.Context, s string, i ...interface{}) { 32 | logx.WithContext(ctx).Infof(s, i...) 33 | } 34 | 35 | func (l *logXLogger) Error(ctx context.Context, s string, i ...interface{}) { 36 | logx.WithContext(ctx).Errorf(s, i...) 37 | } 38 | 39 | func (l *logXLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { 40 | elapsed := time.Since(begin) 41 | sql, rows := fc() 42 | if rows == -1 { 43 | logx.WithContext(ctx).Infof("[%s][%.3fms] [rows:%v] %s", utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql) 44 | } else { 45 | logx.WithContext(ctx).Infof("[%s][%.3fms] [rows:%v] %s", utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql) 46 | } 47 | } 48 | 49 | func MustNewMysql( 50 | cfg MysqlConf, 51 | ) *gorm.DB { 52 | var db *gorm.DB 53 | var err error 54 | done := make(chan *gorm.DB) 55 | go func() { 56 | defer close(done) 57 | db, err = gorm.Open(mysql.New(mysql.Config{ 58 | DSN: cfg.DataSource, 59 | SkipInitializeWithVersion: false, 60 | DefaultStringSize: 191, 61 | DontSupportRenameIndex: true, 62 | DontSupportRenameColumn: true, 63 | }), &gorm.Config{ 64 | Logger: new(logXLogger), 65 | }) 66 | if err != nil { 67 | logx.Errorf("Connect database failed: %v", err) 68 | os.Exit(1) 69 | } 70 | done <- db 71 | }() 72 | select { 73 | case tx := <-done: 74 | // 没事 75 | db = tx 76 | return db 77 | case <-time.After(3 * time.Second): 78 | // 超时 79 | logx.Errorf("Connect database timeout") 80 | os.Exit(1) 81 | return nil 82 | } 83 | return db 84 | } 85 | -------------------------------------------------------------------------------- /deploy/.gitignore: -------------------------------------------------------------------------------- 1 | testing_xxim 2 | -------------------------------------------------------------------------------- /deploy/local_xxim/.gitignore: -------------------------------------------------------------------------------- 1 | data 2 | -------------------------------------------------------------------------------- /deploy/local_xxim/README.md: -------------------------------------------------------------------------------- 1 | # local_xxim 本地调试环境 2 | 3 | ## 问题 4 | 5 | ### 1. 怎么连接docker内网 6 | 7 | #### MacOS 8 | 9 | > 原文章 https://www.haoyizebo.com/posts/fd0b9bd8/#docker-connector 10 | 11 | - 12 | 1. 安装 docker-connector 13 | 14 | > brew install wenjunxiao/brew/docker-connector 15 | 16 | - 17 | 2. docker-connector 配置文件 18 | 19 | ```bash 20 | rm -rf /opt/homebrew/etc/docker-connector.conf 21 | docker network ls --filter driver=bridge --format "{{.ID}}" | xargs docker network inspect --format "route {{range .IPAM.Config}}{{.Subnet}}{{end}}" >> /opt/homebrew/etc/docker-connector.conf 22 | ``` 23 | 24 | - 25 | 3. 启动服务 26 | 27 | ```bash 28 | sudo brew services restart docker-connector 29 | ``` 30 | 31 | - 32 | 4. docker启动mac-docker-connector 33 | 34 | > docker run -it -d --restart always --net host --cap-add NET_ADMIN --name connector wenjunxiao/mac-docker-connector 35 | -------------------------------------------------------------------------------- /sdk/client/friend.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "github.com/cherish-chat/xxim-server/common/pb" 4 | 5 | // FriendApply 申请好友 6 | func (c *HttpClient) FriendApply(req *pb.FriendApplyReq) (resp *pb.FriendApplyResp, err error) { 7 | resp = &pb.FriendApplyResp{} 8 | err = c.Request("/v1/friend/friendApply", req, resp) 9 | return 10 | } 11 | 12 | // FriendApply 申请好友 13 | func (c *WsClient) FriendApply(req *pb.FriendApplyReq) (resp *pb.FriendApplyResp, err error) { 14 | resp = &pb.FriendApplyResp{} 15 | err = c.Request("/v1/friend/friendApply", req, resp) 16 | return 17 | } 18 | 19 | // ListFriendApply 列出好友申请 20 | func (c *HttpClient) ListFriendApply(req *pb.ListFriendApplyReq) (resp *pb.ListFriendApplyResp, err error) { 21 | resp = &pb.ListFriendApplyResp{} 22 | err = c.Request("/v1/friend/listFriendApply", req, resp) 23 | return 24 | } 25 | 26 | // ListFriendApply 列出好友申请 27 | func (c *WsClient) ListFriendApply(req *pb.ListFriendApplyReq) (resp *pb.ListFriendApplyResp, err error) { 28 | resp = &pb.ListFriendApplyResp{} 29 | err = c.Request("/v1/friend/listFriendApply", req, resp) 30 | return 31 | } 32 | 33 | // FriendApplyHandle 处理好友申请 34 | func (c *HttpClient) FriendApplyHandle(req *pb.FriendApplyHandleReq) (resp *pb.FriendApplyHandleResp, err error) { 35 | resp = &pb.FriendApplyHandleResp{} 36 | err = c.Request("/v1/friend/friendApplyHandle", req, resp) 37 | return 38 | } 39 | 40 | // FriendApplyHandle 处理好友申请 41 | func (c *WsClient) FriendApplyHandle(req *pb.FriendApplyHandleReq) (resp *pb.FriendApplyHandleResp, err error) { 42 | resp = &pb.FriendApplyHandleResp{} 43 | err = c.Request("/v1/friend/friendApplyHandle", req, resp) 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /sdk/client/friend_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | // FriendApply 申请好友 11 | func TestHttpClient_FriendApply(t *testing.T) { 12 | client := getHttpClient(t, nil) 13 | //client := getWsClient(t, nil) 14 | time.Sleep(1 * time.Second) 15 | friendApplyResp, err := client.FriendApply(&pb.FriendApplyReq{ 16 | ToUserId: "6", 17 | }) 18 | if err != nil { 19 | t.Fatalf(err.Error()) 20 | } 21 | t.Logf("%s", utils.Json.MarshalToString(friendApplyResp)) 22 | } 23 | 24 | // ListFriendApply 列出好友申请 25 | func TestHttpClient_ListFriendApply(t *testing.T) { 26 | client := getHttpClient(t, nil) 27 | //client := getWsClient(t, nil) 28 | time.Sleep(1 * time.Second) 29 | listFriendApplyResp, err := client.ListFriendApply(&pb.ListFriendApplyReq{ 30 | Cursor: 0, 31 | Limit: 10, 32 | Filter: &pb.ListFriendApplyReq_Filter{ 33 | Status: utils.AnyPtr(pb.FriendApplyStatus_Applying), 34 | }, 35 | Option: &pb.ListFriendApplyReq_Option{ 36 | IncludeApplyByMe: true, 37 | }, 38 | }) 39 | if err != nil { 40 | t.Fatalf(err.Error()) 41 | } 42 | t.Logf("%s", utils.Json.MarshalToString(listFriendApplyResp)) 43 | } 44 | 45 | // FriendApplyHandle 处理好友申请 46 | func TestHttpClient_FriendApplyHandle(t *testing.T) { 47 | client := getHttpClient(t, nil) 48 | //client := getWsClient(t, nil) 49 | time.Sleep(1 * time.Second) 50 | friendApplyHandleResp, err := client.FriendApplyHandle(&pb.FriendApplyHandleReq{ 51 | ApplyId: "7893157afdaeac4b37a3ac7b9bf86c64", 52 | Agree: true, 53 | FirstMessage: nil, 54 | }) 55 | if err != nil { 56 | t.Fatalf(err.Error()) 57 | } 58 | t.Logf("%s", utils.Json.MarshalToString(friendApplyHandleResp)) 59 | } 60 | 61 | func TestOnline_1(t *testing.T) { 62 | _ = getWsClient(t, nil) 63 | time.Sleep(1 * time.Hour) 64 | } 65 | 66 | func TestOnline_2(t *testing.T) { 67 | _ = getWsClient(t, nil) 68 | time.Sleep(1 * time.Hour) 69 | } 70 | -------------------------------------------------------------------------------- /sdk/client/gateway.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | ) 6 | 7 | // GatewayGetUserConnection 获取用户连接 8 | func (c *HttpClient) GatewayGetUserConnection(req *pb.GatewayGetUserConnectionReq) (resp *pb.GatewayGetUserConnectionResp, err error) { 9 | resp = &pb.GatewayGetUserConnectionResp{} 10 | err = c.Request("/v1/gateway/getUserConnection", req, resp) 11 | return 12 | } 13 | 14 | // GatewayGetUserConnection 获取用户连接 15 | func (c *WsClient) GatewayGetUserConnection(req *pb.GatewayGetUserConnectionReq) (resp *pb.GatewayGetUserConnectionResp, err error) { 16 | resp = &pb.GatewayGetUserConnectionResp{} 17 | err = c.Request("/v1/gateway/getUserConnection", req, resp) 18 | return 19 | } 20 | 21 | // GatewayBatchGetUserConnection 批量获取用户连接 22 | func (c *HttpClient) GatewayBatchGetUserConnection(req *pb.GatewayBatchGetUserConnectionReq) (resp *pb.GatewayBatchGetUserConnectionResp, err error) { 23 | resp = &pb.GatewayBatchGetUserConnectionResp{} 24 | err = c.Request("/v1/gateway/batchGetUserConnection", req, resp) 25 | return 26 | } 27 | 28 | // GatewayBatchGetUserConnection 批量获取用户连接 29 | func (c *WsClient) GatewayBatchGetUserConnection(req *pb.GatewayBatchGetUserConnectionReq) (resp *pb.GatewayBatchGetUserConnectionResp, err error) { 30 | resp = &pb.GatewayBatchGetUserConnectionResp{} 31 | err = c.Request("/v1/gateway/batchGetUserConnection", req, resp) 32 | return 33 | } 34 | 35 | // GatewayGetConnectionByFilter 根据条件获取连接 36 | func (c *HttpClient) GatewayGetConnectionByFilter(req *pb.GatewayGetConnectionByFilterReq) (resp *pb.GatewayGetConnectionByFilterResp, err error) { 37 | resp = &pb.GatewayGetConnectionByFilterResp{} 38 | err = c.Request("/v1/gateway/getConnectionByFilter", req, resp) 39 | return 40 | } 41 | 42 | // GatewayGetConnectionByFilter 根据条件获取连接 43 | func (c *WsClient) GatewayGetConnectionByFilter(req *pb.GatewayGetConnectionByFilterReq) (resp *pb.GatewayGetConnectionByFilterResp, err error) { 44 | resp = &pb.GatewayGetConnectionByFilterResp{} 45 | err = c.Request("/v1/gateway/getConnectionByFilter", req, resp) 46 | return 47 | } 48 | 49 | // GatewayWriteDataToWs 写入数据到ws 50 | func (c *HttpClient) GatewayWriteDataToWs(req *pb.GatewayWriteDataToWsReq) (resp *pb.GatewayWriteDataToWsResp, err error) { 51 | resp = &pb.GatewayWriteDataToWsResp{} 52 | err = c.Request("/v1/gateway/writeDataToWs", req, resp) 53 | return 54 | } 55 | 56 | // GatewayWriteDataToWs 写入数据到ws 57 | func (c *WsClient) GatewayWriteDataToWs(req *pb.GatewayWriteDataToWsReq) (resp *pb.GatewayWriteDataToWsResp, err error) { 58 | resp = &pb.GatewayWriteDataToWsResp{} 59 | err = c.Request("/v1/gateway/writeDataToWs", req, resp) 60 | return 61 | } 62 | 63 | // GatewayKickWs 踢出ws 64 | func (c *HttpClient) GatewayKickWs(req *pb.GatewayKickWsReq) (resp *pb.GatewayKickWsResp, err error) { 65 | resp = &pb.GatewayKickWsResp{} 66 | err = c.Request("/v1/gateway/kickWs", req, resp) 67 | return 68 | } 69 | 70 | // GatewayKickWs 踢出ws 71 | func (c *WsClient) GatewayKickWs(req *pb.GatewayKickWsReq) (resp *pb.GatewayKickWsResp, err error) { 72 | resp = &pb.GatewayKickWsResp{} 73 | err = c.Request("/v1/gateway/kickWs", req, resp) 74 | return 75 | } 76 | -------------------------------------------------------------------------------- /sdk/client/group.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "github.com/cherish-chat/xxim-server/common/pb" 4 | 5 | // GroupCreate 创建群 6 | func (c *HttpClient) GroupCreate(req *pb.GroupCreateReq) (resp *pb.GroupCreateResp, err error) { 7 | resp = &pb.GroupCreateResp{} 8 | err = c.Request("/v1/group/groupCreate", req, resp) 9 | return 10 | } 11 | 12 | // GroupCreate 创建群 13 | func (c *WsClient) GroupCreate(req *pb.GroupCreateReq) (resp *pb.GroupCreateResp, err error) { 14 | resp = &pb.GroupCreateResp{} 15 | err = c.Request("/v1/group/groupCreate", req, resp) 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /sdk/client/group_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | // GroupCreate 创建群 11 | func TestHttpClient_GroupCreate(t *testing.T) { 12 | //client := getHttpClient(t, nil) 13 | client := getWsClient(t, nil) 14 | time.Sleep(1 * time.Second) 15 | groupCreateResp, err := client.GroupCreate(&pb.GroupCreateReq{ 16 | Name: nil, 17 | Avatar: nil, 18 | MemberList: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9"}, 19 | //InfoMap: map[string]string{ 20 | // 21 | //}, 22 | ExtraMap: nil, 23 | }) 24 | if err != nil { 25 | t.Fatalf(err.Error()) 26 | } 27 | t.Logf("%s", utils.Json.MarshalToString(groupCreateResp)) 28 | time.Sleep(300 * time.Second) 29 | } 30 | -------------------------------------------------------------------------------- /sdk/client/message_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/cherish-chat/xxim-server/common/pb" 6 | "github.com/cherish-chat/xxim-server/common/utils" 7 | "github.com/zeromicro/go-zero/core/mr" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | // MessageBatchSend 批量发送消息 13 | func TestHttpClient_MessageBatchSend(t *testing.T) { 14 | //client := getHttpClient(t, nil) 15 | client := getWsClient(t, nil) 16 | time.Sleep(1 * time.Second) 17 | var messages []*pb.Message 18 | for i := 0; i < 1; i++ { 19 | messages = append(messages, generateMessageToFriend(&pb.Message_Sender{ 20 | Id: "1", 21 | Name: "哈哈", 22 | Avatar: "头像", 23 | Extra: "xx", 24 | }, "5", "你好你好", map[string]string{ 25 | "platformSource": "Test", 26 | })) 27 | } 28 | messageBatchSendResp, err := client.MessageBatchSend(&pb.MessageBatchSendReq{ 29 | Messages: messages, 30 | DisableQueue: false, 31 | }) 32 | if err != nil { 33 | t.Fatalf(err.Error()) 34 | } 35 | t.Logf("%s", utils.Json.MarshalToString(messageBatchSendResp)) 36 | } 37 | 38 | // MessageBatchSend 压力测试 39 | func TestHttpClient_MessageBatchSend_Pressure(t *testing.T) { 40 | var fs []func() 41 | goroutineCount := 100 42 | loopCount := 1000 43 | client := getWsClient(t, nil) 44 | time.Sleep(1 * time.Second) 45 | message := generateMessageToFriend(&pb.Message_Sender{ 46 | Id: "1", 47 | Name: "哈哈", 48 | Avatar: "头像", 49 | Extra: "xx", 50 | }, "2", "你好你好", map[string]string{ 51 | "platformSource": "Test", 52 | }) 53 | for i := 0; i < goroutineCount; i++ { 54 | fs = append(fs, func() { 55 | for j := 0; j < loopCount; j++ { 56 | _, _ = client.MessageBatchSend(&pb.MessageBatchSendReq{ 57 | Messages: []*pb.Message{message}, 58 | DisableQueue: false, 59 | }) 60 | } 61 | }) 62 | } 63 | t.Logf("开始时间: %s", time.Now().Format("2006-01-02 15:04:05.000")) 64 | mr.FinishVoid(fs...) 65 | t.Logf("结束时间: %s", time.Now().Format("2006-01-02 15:04:05.000")) 66 | } 67 | 68 | func generateMessageToFriend( 69 | sender *pb.Message_Sender, 70 | friendId string, 71 | text string, 72 | extraMap map[string]string) *pb.Message { 73 | convId := pb.GetSingleChatConversationId(sender.Id, friendId) 74 | content := &pb.MessageContentText{Items: []*pb.MessageContentText_Item{{ 75 | Type: pb.MessageContentText_Item_TEXT, 76 | Text: text, 77 | Image: nil, 78 | At: nil, 79 | }}} 80 | contentBytes, _ := json.Marshal(content) 81 | return &pb.Message{ 82 | Uuid: utils.Snowflake.String(), 83 | ConversationId: convId, 84 | ConversationType: pb.ConversationType_Single, 85 | Sender: sender, 86 | Content: contentBytes, 87 | ContentType: pb.MessageContentType_Text, 88 | SendTime: time.Now().UnixMilli(), 89 | Option: &pb.Message_Option{ 90 | StorageForServer: true, 91 | StorageForClient: true, 92 | NeedDecrypt: false, 93 | CountUnread: true, 94 | }, 95 | ExtraMap: extraMap, 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /sdk/client/nessage.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "github.com/cherish-chat/xxim-server/common/pb" 4 | 5 | // MessageBatchSend 批量发送消息 6 | func (c *HttpClient) MessageBatchSend(req *pb.MessageBatchSendReq) (resp *pb.MessageBatchSendResp, err error) { 7 | resp = &pb.MessageBatchSendResp{} 8 | err = c.Request("/v1/message/messageBatchSend", req, resp) 9 | return 10 | } 11 | 12 | // MessageBatchSend 批量发送消息 13 | func (c *WsClient) MessageBatchSend(req *pb.MessageBatchSendReq) (resp *pb.MessageBatchSendResp, err error) { 14 | resp = &pb.MessageBatchSendResp{} 15 | err = c.Request("/v1/message/messageBatchSend", req, resp) 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /sdk/client/notice.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "github.com/zeromicro/go-zero/core/logx" 7 | ) 8 | 9 | // ListNotice 列出通知 10 | func (c *HttpClient) ListNotice(req *pb.ListNoticeReq) (resp *pb.ListNoticeResp, err error) { 11 | resp = &pb.ListNoticeResp{} 12 | err = c.Request("/v1/notice/listNotice", req, resp) 13 | return 14 | } 15 | 16 | // ListNotice 列出通知 17 | func (c *WsClient) ListNotice(req *pb.ListNoticeReq) (resp *pb.ListNoticeResp, err error) { 18 | resp = &pb.ListNoticeResp{} 19 | err = c.Request("/v1/notice/listNotice", req, resp) 20 | return 21 | } 22 | 23 | func (c *WsClient) onNewNotice(notice *pb.Notice) { 24 | switch notice.ContentType { 25 | case pb.NoticeContentType_NewFriendRequest: 26 | logx.Infof("收到好友申请: ") 27 | listFriendApplyResp, err := c.httpClient.ListFriendApply(&pb.ListFriendApplyReq{ 28 | Cursor: 0, 29 | Limit: 100, 30 | Filter: &pb.ListFriendApplyReq_Filter{ 31 | Status: nil, 32 | }, 33 | Option: &pb.ListFriendApplyReq_Option{ 34 | IncludeApplyByMe: false, 35 | }, 36 | }) 37 | if err != nil { 38 | logx.Errorf("ListFriendApply error: %v", err) 39 | return 40 | } 41 | logx.Infof("listFriendApplyResp: %s", utils.AnyString(listFriendApplyResp)) 42 | case pb.NoticeContentType_JoinNewGroup: 43 | logx.Infof("收到加入群组通知: ") 44 | //重新订阅群组 45 | c.KeepAlive() 46 | default: 47 | logx.Infof("onNewNotice: %s", utils.AnyString(notice)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sdk/client/notice_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | "github.com/cherish-chat/xxim-server/common/utils" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | // ListNotice 列出通知 11 | func TestHttpClient_ListNotice(t *testing.T) { 12 | client := getHttpClient(t, nil) 13 | //client := getWsClient(t, nil) 14 | time.Sleep(1 * time.Second) 15 | friendApplyResp, err := client.ListNotice(&pb.ListNoticeReq{ 16 | //ConversationId: subscriptionmodel.ConversationIdFriendNotification, 17 | //ConversationType: pb.ConversationType_Subscription, 18 | ConvList: []*pb.ListNoticeReq_Conversation{{ 19 | ConversationId: "1000006", 20 | ConversationType: pb.ConversationType_Group, 21 | }, { 22 | ConversationId: "friend_notification", 23 | ConversationType: pb.ConversationType_Subscription, 24 | }}, 25 | SortGt: 1, 26 | Limit: 100, 27 | }) 28 | if err != nil { 29 | t.Fatalf(err.Error()) 30 | } 31 | t.Logf("%s", utils.Json.MarshalToString(friendApplyResp)) 32 | } 33 | -------------------------------------------------------------------------------- /sdk/client/user.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "github.com/cherish-chat/xxim-server/common/pb" 4 | 5 | // UserRegister 注册用户 6 | func (c *HttpClient) UserRegister(req *pb.UserRegisterReq) (resp *pb.UserRegisterResp, err error) { 7 | resp = &pb.UserRegisterResp{} 8 | err = c.Request("/v1/user/white/userRegister", req, resp) 9 | return 10 | } 11 | 12 | // UserAccessToken 获取用户访问令牌 13 | func (c *HttpClient) UserAccessToken(req *pb.UserAccessTokenReq) (resp *pb.UserAccessTokenResp, err error) { 14 | resp = &pb.UserAccessTokenResp{} 15 | err = c.Request("/v1/user/white/userAccessToken", req, resp) 16 | return 17 | } 18 | 19 | // CreateRobot 创建机器人 20 | func (c *HttpClient) CreateRobot(req *pb.CreateRobotReq) (resp *pb.CreateRobotResp, err error) { 21 | resp = &pb.CreateRobotResp{} 22 | err = c.Request("/v1/user/createRobot", req, resp) 23 | return 24 | } 25 | 26 | // CreateRobot 创建机器人 27 | func (c *WsClient) CreateRobot(req *pb.CreateRobotReq) (resp *pb.CreateRobotResp, err error) { 28 | resp = &pb.CreateRobotResp{} 29 | err = c.Request("/v1/user/createRobot", req, resp) 30 | return 31 | } 32 | 33 | // RefreshUserAccessToken 刷新用户访问令牌 34 | func (c *HttpClient) RefreshUserAccessToken(req *pb.RefreshUserAccessTokenReq) (resp *pb.RefreshUserAccessTokenResp, err error) { 35 | resp = &pb.RefreshUserAccessTokenResp{} 36 | err = c.Request("/v1/user/refreshUserAccessToken", req, resp) 37 | return 38 | } 39 | 40 | // RefreshUserAccessToken 刷新用户访问令牌 41 | func (c *WsClient) RefreshUserAccessToken(req *pb.RefreshUserAccessTokenReq) (resp *pb.RefreshUserAccessTokenResp, err error) { 42 | resp = &pb.RefreshUserAccessTokenResp{} 43 | err = c.Request("/v1/user/refreshUserAccessToken", req, resp) 44 | return 45 | } 46 | 47 | // RevokeUserAccessToken 注销用户token 48 | func (c *HttpClient) RevokeUserAccessToken(req *pb.RevokeUserAccessTokenReq) (resp *pb.RevokeUserAccessTokenResp, err error) { 49 | resp = &pb.RevokeUserAccessTokenResp{} 50 | err = c.Request("/v1/user/revokeUserAccessToken", req, resp) 51 | return 52 | } 53 | 54 | // RevokeUserAccessToken 注销用户token 55 | func (c *WsClient) RevokeUserAccessToken(req *pb.RevokeUserAccessTokenReq) (resp *pb.RevokeUserAccessTokenResp, err error) { 56 | resp = &pb.RevokeUserAccessTokenResp{} 57 | err = c.Request("/v1/user/revokeUserAccessToken", req, resp) 58 | return 59 | } 60 | -------------------------------------------------------------------------------- /sdk/store/config.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | type ConfigModel struct { 4 | K string `gorm:"column:k;type:varchar(255);primary_key;not null"` 5 | V string `gorm:"column:v;type:varchar(255);not null"` 6 | } 7 | 8 | // TableName 表名 9 | func (m *ConfigModel) TableName() string { 10 | return "config" 11 | } 12 | 13 | type xConfig struct { 14 | } 15 | 16 | func (x xConfig) FindByK(k string) string { 17 | var model ConfigModel 18 | Database.sqlite.Table("config").Where("k = ?", k).First(&model) 19 | return model.V 20 | } 21 | 22 | func (x xConfig) Save(k string, v string) { 23 | var model ConfigModel 24 | model.K = k 25 | model.V = v 26 | Database.sqlite.Table("config").Save(&model) 27 | } 28 | -------------------------------------------------------------------------------- /sdk/store/sqlite.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/core/logx" 5 | "gorm.io/driver/sqlite" 6 | "gorm.io/gorm" 7 | "os" 8 | ) 9 | 10 | type xDatabase struct { 11 | sqlite *gorm.DB 12 | Config xConfig 13 | } 14 | 15 | var Database *xDatabase 16 | 17 | func init() { 18 | Database = &xDatabase{} 19 | db, err := gorm.Open(sqlite.Open("./imcloudx.db"), &gorm.Config{}) 20 | if err != nil { 21 | logx.Errorf("init sqlite error: %v", err) 22 | os.Exit(1) 23 | } 24 | Database.sqlite = db 25 | db.AutoMigrate( 26 | &ConfigModel{}, 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /sdk/types/interface.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cherish-chat/xxim-server/common/pb" 5 | "google.golang.org/protobuf/proto" 6 | ) 7 | 8 | type ReqInterface interface { 9 | proto.Message 10 | SetHeader(*pb.RequestHeader) 11 | } 12 | --------------------------------------------------------------------------------