package service import ( "context" "gim/internal/business/comm/db" svc "gim/internal/business/comm/service" "gim/internal/business/comm/utils" repo2 "gim/internal/business/domain/user/repo" "gim/internal/logic/domain/message/md" "gim/internal/logic/domain/message/model" "gim/internal/logic/domain/message/repo" "gim/internal/logic/proxy" "gim/pkg/grpclib" "gim/pkg/grpclib/picker" "gim/pkg/logger" "gim/pkg/pb" "gim/pkg/rpc" "gim/pkg/util" "go.uber.org/zap" "google.golang.org/protobuf/proto" "strconv" "time" ) const MessageLimit = 50 // 最大消息同步数量 const MaxSyncBufLen = 65536 // 最大字节数组长度 type messageService struct{} var MessageService = new(messageService) // Sync 消息同步 func (*messageService) Sync(ctx context.Context, userId, seq int64) (*pb.SyncResp, error) { messages, hasMore, err := MessageService.ListByUserIdAndSeq(ctx, userId, seq) if err != nil { return nil, err } pbMessages := model.MessagesToPB(messages) length := len(pbMessages) resp := &pb.SyncResp{Messages: pbMessages, HasMore: hasMore} bytes, err := proto.Marshal(resp) if err != nil { return nil, err } // 如果字节数组大于一个包的长度,需要减少字节数组 for len(bytes) > MaxSyncBufLen { length = length * 2 / 3 resp = &pb.SyncResp{Messages: pbMessages[0:length], HasMore: true} bytes, err = proto.Marshal(resp) if err != nil { return nil, err } } var userIds = make(map[int64]int32, len(resp.Messages)) for i := range resp.Messages { if resp.Messages[i].Sender.SenderType == pb.SenderType_ST_USER { userIds[resp.Messages[i].Sender.SenderId] = 0 } } usersResp, err := rpc.GetBusinessIntClient().GetUsers(ctx, &pb.GetUsersReq{UserIds: userIds}) if err != nil { return nil, err } for i := range resp.Messages { if resp.Messages[i].Sender.SenderType == pb.SenderType_ST_USER { user, ok := usersResp.Users[resp.Messages[i].Sender.SenderId] if ok { resp.Messages[i].Sender.Nickname = user.Nickname resp.Messages[i].Sender.AvatarUrl = user.AvatarUrl resp.Messages[i].Sender.Extra = user.Extra } else { logger.Logger.Warn("get user failed", zap.Int64("user_id", resp.Messages[i].Sender.SenderId)) } } } return resp, nil } // ListByUserIdAndSeq 查询消息 func (*messageService) ListByUserIdAndSeq(ctx context.Context, userId, seq int64) ([]model.Message, bool, error) { var err error if seq == 0 { seq, err = DeviceAckService.GetMaxByUserId(ctx, userId) if err != nil { return nil, false, err } } return repo.MessageRepo.ListBySeq(userId, seq, MessageLimit) } // SendToUser 将消息发送给用户 func (*messageService) SendToUser(ctx context.Context, sender *pb.Sender, toUserId int64, req *pb.SendMessageReq) (int64, error) { masterId, _ := grpclib.GetCtxMasterId(ctx) logger.Logger.Debug("SendToUser", zap.String("master_id", masterId), zap.Int64("request_id", grpclib.GetCtxRequestId(ctx)), zap.Int64("to_user_id", toUserId)) var ( seq int64 = 0 err error ) if req.IsPersist { seq, err = SeqService.GetUserNext(ctx, toUserId) if err != nil { return 0, err } selfMessage := model.Message{ UserId: toUserId, RequestId: grpclib.GetCtxRequestId(ctx), SenderType: int32(sender.SenderType), SenderId: sender.SenderId, ReceiverType: int32(req.ReceiverType), ReceiverId: req.ReceiverId, ToUserIds: model.FormatUserIds(req.ToUserIds), Type: int(req.MessageType), Content: req.MessageContent, Seq: seq, SendTime: util.UnunixMilliTime(req.SendTime), Status: int32(pb.MessageStatus_MS_NORMAL), } err = repo.MessageRepo.Save(selfMessage) if err != nil { logger.Sugar.Error(err) return 0, err } if sender.SenderType == pb.SenderType_ST_USER && sender.SenderId == toUserId { // 用户需要增加自己的已经同步的序列号 err = repo.DeviceACKRepo.Set(sender.SenderId, sender.DeviceId, seq) if err != nil { return 0, err } } } message := pb.Message{ Sender: sender, ReceiverType: req.ReceiverType, ReceiverId: req.ReceiverId, ToUserIds: req.ToUserIds, MessageType: req.MessageType, MessageContent: req.MessageContent, Seq: seq, SendTime: req.SendTime, Status: pb.MessageStatus_MS_NORMAL, } // 查询用户在线设备 devices, err := proxy.DeviceProxy.ListOnlineByUserId(ctx, toUserId) if err != nil { logger.Sugar.Error(err) return 0, err } // 查询接受者用户在线设备 receiverDevices, err := proxy.DeviceProxy.ListOnlineByUserId(ctx, req.ReceiverId) if err != nil { logger.Sugar.Error(err) return 0, err } isOpenAppPush := "0" if masterId != "" && masterId != "0" { isOpenAppPush = svc.SysCfgGet(masterId, "is_open_app_push") } utils.FilePutContents("SendToUser", utils.SerializeStr(map[string]interface{}{ "devices_len": len(devices), "sender": sender, "toUserId": toUserId, "isOpenAppPush": isOpenAppPush, "req": req, "masterId": masterId, })) if len(receiverDevices) <= 0 { //isOpenAppPush := svc.SysCfgGet(masterId, "is_open_app_push") if sender.SenderType == pb.SenderType_ST_USER && req.ReceiverType == pb.ReceiverType_RT_USER && isOpenAppPush == "1" && req.ReceiverId != toUserId { uid := strconv.FormatInt(req.ReceiverId, 10) alia := db.DbUserPushForJg.UserPushForJgGetWithDb(masterId, uid) if alia != "" { if sender.Nickname == "" { sendUser, err := repo2.UserRepo.Get(sender.SenderId) if err != nil { logger.Sugar.Error(err) return 0, err } sender.Nickname = sendUser.Nickname sender.AvatarUrl = sendUser.AvatarUrl } var pushContent string if req.MessageType != pb.MessageType_MT_TEXT { switch req.MessageType { case pb.MessageType_MT_UNKNOWN: pushContent = "您收到一条\"未知\"消息" break case pb.MessageType_MT_FACE: pushContent = "您收到一条表情消息" break case pb.MessageType_MT_VOICE: pushContent = "您收到一条语音消息" break case pb.MessageType_MT_IMAGE: pushContent = "您收到一条图片消息" break case pb.MessageType_MT_FILE: pushContent = "您收到一条视频消息" break case pb.MessageType_MT_LOCATION: pushContent = "您收到一条地理位置消息" break case pb.MessageType_MT_COMMAND: pushContent = "您收到一条指令推送消息" break case pb.MessageType_MT_CUSTOM: pushContent = "您收到一条\"自定义\"推送消息" break } } else { pushContent = string(req.MessageContent) } //TODO::接收者类型为`user`, 进行极光推送 CommAddPush(md.PushParams{ MasterId: masterId, Uid: uid, PushAlia: alia, Title: sender.Nickname, Content: pushContent, //Content: "您收到一条新消息,来自会员\"[消息发送者-会员昵称]\",发送时间\"[时间]\"", //Content: "您收到一条新消息,来自会员\"[消息发送者-会员昵称]\",发送时间\"[时间]\",消息类型\"[消息类型]\",备注\"[备注]\"", PushType: "zhi_ying_gim", MessageType: req.MessageType.String(), SendUserNickname: sender.Nickname, SendUserAvatarUrl: sender.AvatarUrl, Memo: sender.SenderType.String(), Times: time.Now().Format("2006-01-02 15:04:05"), }) } } } for i := range devices { if sender.DeviceId == devices[i].DeviceId { // 消息不需要投递给发送消息的设备 continue } err = MessageService.SendToDevice(ctx, devices[i], &message) if err != nil { logger.Sugar.Error(err, zap.Any("SendToUser error", devices[i]), zap.Error(err)) } } return seq, nil } // RecallMessageSendToUser 撤回消息用户 func (*messageService) RecallMessageSendToUser(ctx context.Context, sender *pb.Sender, toUserId int64, req *pb.RecallMessageReq, sendTime time.Time) (int64, error) { masterId, _ := grpclib.GetCtxMasterId(ctx) logger.Logger.Debug("SendToUser", zap.String("master_id", masterId), zap.Int64("request_id", grpclib.GetCtxRequestId(ctx)), zap.Int64("to_user_id", toUserId)) var ( seq int64 = 0 err error ) //1、改变消息状态 _, err = repo.MessageRepo.UpdateStatus(toUserId, sendTime, int(pb.MessageStatus_MS_RECALL)) if err != nil { return 0, err } //3、发送一条新的消息 if req.IsPersist { seq, err = SeqService.GetUserNext(ctx, toUserId) if err != nil { return 0, err } selfMessage := model.Message{ UserId: toUserId, RequestId: grpclib.GetCtxRequestId(ctx), SenderType: int32(sender.SenderType), SenderId: sender.SenderId, ReceiverType: int32(req.ReceiverType), ReceiverId: req.ReceiverId, ToUserIds: model.FormatUserIds(req.ToUserIds), Type: int(req.MessageType), Content: req.MessageContent, Seq: seq, SendTime: util.UnunixMilliTime(req.SendTime), Status: int32(pb.MessageStatus_MS_NORMAL), } err = repo.MessageRepo.Save(selfMessage) if err != nil { logger.Sugar.Error(err) return 0, err } if sender.SenderType == pb.SenderType_ST_USER && sender.SenderId == toUserId { // 用户需要增加自己的已经同步的序列号 err = repo.DeviceACKRepo.Set(sender.SenderId, sender.DeviceId, seq) if err != nil { return 0, err } } } message := pb.Message{ Sender: sender, ReceiverType: req.ReceiverType, ReceiverId: req.ReceiverId, ToUserIds: req.ToUserIds, MessageType: req.MessageType, MessageContent: req.MessageContent, Seq: seq, SendTime: req.SendTime, Status: pb.MessageStatus_MS_NORMAL, } // 查询用户在线设备 devices, err := proxy.DeviceProxy.ListOnlineByUserId(ctx, toUserId) if err != nil { logger.Sugar.Error(err) return 0, err } for i := range devices { if sender.DeviceId == devices[i].DeviceId { // 消息不需要投递给发送消息的设备 continue } err = MessageService.SendToDevice(ctx, devices[i], &message) if err != nil { logger.Sugar.Error(err, zap.Any("SendToUser error", devices[i]), zap.Error(err)) } } return seq, nil } // SendRedPackageToUser 发送红包给用户 func (*messageService) SendRedPackageToUser(ctx context.Context, sender *pb.Sender, toUserId int64, req *pb.SendRedPacketReq) (int64, error) { masterId, _ := grpclib.GetCtxMasterId(ctx) logger.Logger.Debug("SendRedPackageToUser", zap.String("master_id", masterId), zap.Int64("request_id", grpclib.GetCtxRequestId(ctx)), zap.Int64("to_user_id", toUserId)) var ( seq int64 = 0 err error ) //1、发送一条新的消息 seq, err = SeqService.GetUserNext(ctx, toUserId) if err != nil { return 0, err } selfMessage := model.Message{ UserId: toUserId, RequestId: grpclib.GetCtxRequestId(ctx), SenderType: int32(sender.SenderType), SenderId: sender.SenderId, ReceiverType: int32(req.ReceiverType), ReceiverId: req.ReceiverId, //ToUserIds: model.FormatUserIds(req.ToUserIds), Type: int(req.MessageType), Content: req.MessageContent, Seq: seq, SendTime: util.UnunixMilliTime(req.SendTime), Status: int32(pb.MessageStatus_MS_NORMAL), } err = repo.MessageRepo.Save(selfMessage) if err != nil { logger.Sugar.Error(err) return 0, err } if sender.SenderType == pb.SenderType_ST_USER && sender.SenderId == toUserId { // 用户需要增加自己的已经同步的序列号 err = repo.DeviceACKRepo.Set(sender.SenderId, sender.DeviceId, seq) if err != nil { return 0, err } } message := pb.Message{ Sender: sender, ReceiverType: req.ReceiverType, ReceiverId: req.ReceiverId, //ToUserIds: req.ToUserIds, MessageType: pb.MessageType_MT_RED_PACKAGE, MessageContent: req.MessageContent, Seq: seq, SendTime: req.SendTime, Status: pb.MessageStatus_MS_NORMAL, } // 查询用户在线设备 devices, err := proxy.DeviceProxy.ListOnlineByUserId(ctx, toUserId) if err != nil { logger.Sugar.Error(err) return 0, err } for i := range devices { if sender.DeviceId == devices[i].DeviceId { // 消息不需要投递给发送消息的设备 continue } err = MessageService.SendToDevice(ctx, devices[i], &message) if err != nil { logger.Sugar.Error(err, zap.Any("SendToUser error", devices[i]), zap.Error(err)) } } return seq, nil } // SendToDevice 将消息发送给设备 func (*messageService) SendToDevice(ctx context.Context, device *pb.Device, message *pb.Message) error { messageSend := pb.MessageSend{Message: message} _, err := rpc.GetConnectIntClient().DeliverMessage(picker.ContextWithAddr(ctx, device.ConnAddr), &pb.DeliverMessageReq{ DeviceId: device.DeviceId, MessageSend: &messageSend, }) if err != nil { logger.Logger.Error("SendToDevice error", zap.Error(err)) return err } // todo 其他推送厂商 return nil } func (*messageService) AddSenderInfo(sender *pb.Sender) { if sender.SenderType == pb.SenderType_ST_USER { user, err := rpc.GetBusinessIntClient().GetUser(context.TODO(), &pb.GetUserReq{UserId: sender.SenderId}) if err == nil && user != nil { sender.AvatarUrl = user.User.AvatarUrl sender.Nickname = user.User.Nickname sender.Extra = user.User.Extra } } }