Golang 16 gin web项目实战 IM系统(5) 在线状态提醒 目录重构

43

2025/7/24 15:00 - 2025/7/24 23:00

目录重构,接口分离

写着写着发现ws包依赖service包,service包又依赖ws包导致循环依赖,查网给出的解决方案是使用接口包分离依赖,各自只依赖接口而不是具体结构体,具体做法如下:

新建internal/interfaces文件夹,将ws的handler、service、repository提取接口:

在具体实现结构体定义依赖,比如service:

然后在启动入口函数进行依赖注入:

package app

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	"go-chat/configs"
	_ "go-chat/docs"
	apiv1 "go-chat/internal/api/v1"
	"go-chat/internal/db"
	"go-chat/internal/manager"
	"go-chat/internal/timer"
)

func Start() {
    ...
	//配置依赖注入
	doWire()
    ...
	//启动服务
	router.Run(fmt.Sprintf(":%d", configs.AppConfig.Server.Port))
}

虽然麻烦多了,但是更清晰了,不会出现依赖循环

在线状态提醒

用户上线、下线、或手动修改在线状态时, 与他相关的用户,也就是他的好友、所在群组的其他成员(单个用户仅收到一次提醒即可)都要知道他的最新状态更新在线用户列表。

登录时上线

将用户状态改为在线后通知相关人员我的最新状态为在线,通知行为由wsHandler的OnlineStatusNotice实现,目前只有通知群组成员,好友功能还没有做。

func (u *UserService) Login(username, password *string) (token string, err error) {
	//根据username查询
	user, err := u.userRepository.GetByName(username)
	...
	if user.OnlineStatus == model.Offline {
		user.OnlineStatus = model.Online
		fields := map[string]interface{}{
			"online_status": user.OnlineStatus,
			"login_time":    time.Now().Unix(),
		}
		err = u.userRepository.UpdateFields(user.ID, fields, db.Mysql)
		if err != nil {
			return "", err
		}
		//ws通知与我相关的好友或群组在线状态
		onlineStatusNotice := model.OnlineStatusNotice{
			UserId:       user.ID,
			OnlineStatus: model.Online,
			ActionType:   model.LoginAction,
		}
		go u.wsHandler.OnlineStatusNotice(int64(user.ID), onlineStatusNotice)
	}
	return token, nil
}

OnlineStatusNotice

// OnlineStatusNotice 在线状态通知,我的在线状态改变时通知与我相关的朋友或群组
func (ws *WebSocketHandler) OnlineStatusNotice(sendId int64, onlineStatusNotice model.OnlineStatusNotice) {

	//todo  暂时先通知群组 之后还有好友
	memberList, _ := repository.GroupMemberRepositoryInstance.GetRelatedMemberByUserId(uint(sendId))

	var userIdList []int64
	seen := make(map[int64]bool)
	for _, member := range memberList {
		userId := int64(member.UserId)
		if !seen[userId] {
			seen[userId] = true
			userIdList = append(userIdList, userId)
		}
	}
	wsClient.WebSocketClient.SendMessageToMultiple(userIdList,
		&model.Response{
			Code:    http.StatusOK,
			Message: "success",
			Data: &wsMessage.Message{
				Type:   wsMessage.OnlineStatus,
				SendId: sendId,
				Data:   onlineStatusNotice,
				Time:   time.Now(),
			},
		})
}

登出时离线

以下仅service层代码

目前只有JWT,因此登录操作由客户端删除token即可,服务器的登出接口仅做通知

UserService.go:

func (u *UserService) Logout(id int64) {
	u.wsHandler.OnlineStatusNotice(id, model.OnlineStatusNotice{
		UserId:       uint(id),
		OnlineStatus: model.Offline,
		ActionType:   model.LogoutAction,
	})
}

修改状态 忙碌/离开/在线/离线

忙碌/离开仅仅用于展示,告诉好友自己没空不要发消息,但是离开可以和离线一样不接受新消息,直到状态改为在线。

func (u *UserService) OnlineStatusChange(id uint, onlineStatus model.OnlineStatus) error {
	updates := make(map[string]interface{})
	updates["online_status"] = onlineStatus
	err := u.userRepository.UpdateFields(id, updates)
	if err != nil {
		return err
	}
	go u.wsHandler.OnlineStatusNotice(int64(id), model.OnlineStatusNotice{
		UserId:       id,
		OnlineStatus: onlineStatus,
		ActionType:   model.StatusChangeAction,
	})
	return nil
}

目前只是状态通知,之后会根据这些状态进行消息收发

posted on 2025-07-25 12:44  依只  阅读(5)  评论(0)    收藏  举报

导航