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
}
目前只是状态通知,之后会根据这些状态进行消息收发
浙公网安备 33010602011771号