diff --git a/dao/db.go b/dao/db.go index 7e3004b..8fcae33 100644 --- a/dao/db.go +++ b/dao/db.go @@ -51,6 +51,11 @@ func Init() error { fmt.Println("message table:", err) } + err = db.AutoMigrate(&File{}) + if err != nil { + fmt.Println("file table:", err) + } + err = db.AutoMigrate(&Group{}) if err != nil { fmt.Println("usergroup table:", err) diff --git a/dao/file.go b/dao/file.go new file mode 100644 index 0000000..f2843fd --- /dev/null +++ b/dao/file.go @@ -0,0 +1,61 @@ +package dao + +import "gorm.io/gorm" + +type File struct { + gorm.Model + // 存储文件名 + FileStoreName string `gorm:"column:file_store_name;uniqueIndex:idx_file_name"` + FileName string `gorm:"column:file_name"` + FileSize int `gorm:"column:file_size"` + FileType string `gorm:"column:file_type"` + FilePath string `gorm:"column:file_path"` + AuthID int `gorm:"column:auth_id"` +} + +func CreateFile(fileStoreName, fileName, fileType, filePath string, fileSize, authID int) uint { + file := File{FileStoreName: fileStoreName, FileName: fileName, FileType: fileType, FilePath: filePath, FileSize: fileSize, AuthID: authID} + result := DB.Debug().Create(&file) + if result.Error != nil { + return 0 + } + return file.ID +} + +func DeleteFileByID(id, user int) bool { + res := DB.Debug().Model(&File{}).Where("id = ? and auth_id = ?", id, user).Delete(&File{}) + if res.Error != nil { + return false + } + return true +} + +func FindFileByID(id, auth_id int) File { + var file File + DB.Debug().Where("id = ? and auth_id = ?", id, auth_id).First(&file) + return file +} + +func FindFileByNames(fileName string, auth_id int) File { + var file File + DB.Debug().Where("file_name = ? and auth_id = ?", fileName, auth_id).First(&file) + return file +} + +func FindFileByAuthID(auth_id int) []File { + var files []File + DB.Debug().Where("auth_id = ?", auth_id).Find(&files) + return files +} + +func UpdateFileByID(id, auth_id int, fileStoreName, fileName, fileType, filePath string, fileSize int) bool { + pd := FindFileByID(id, auth_id) + if pd.ID == 0 { + return false + } + result := DB.Debug().Model(&File{}).Where("id = ? and auth_id = ?", id, auth_id).Updates(File{FileStoreName: fileStoreName, FileName: fileName, FileType: fileType, FilePath: filePath, FileSize: fileSize}) + if result.Error != nil { + return false + } + return true +} diff --git a/dao/im.go b/dao/im.go index d8d2268..81ff6c0 100644 --- a/dao/im.go +++ b/dao/im.go @@ -78,6 +78,18 @@ func GetMsgGroupByIndex(group_id, index int) ([]GroupMessage, error) { } +func GetGroupRequestUsers(user_id int) []FriendRequest { + var users []FriendRequest + DB.Debug().Raw("select id,im_id,name,email FROM (SELECT im_id,from_user_id,group_id FROM (( SELECT id as im_id,from_user_id,group_id FROM messages WHERE type=? and status=? ) as m JOIN groups as g on g.id=m.group_id ) where g.auth_id=? ) as e JOIN users as u ON e.from_user_id=u.id", proto.MSG_TYPE_GROUP_INVI, 0, user_id).Scan(&users) + return users +} + +func GetMsgUserGroupReq(from_user_id, group_id int) ([]Message, error) { + var msgs []Message + res := DB.Debug().Where("from_user_id = ? and group_id = ? and type = ? and status = ?", from_user_id, group_id, 5, 0).Find(&msgs) + return msgs, res.Error +} + // 获取邀请消息 func GetFriendGroupReq(user_id int) ([]Message, error) { var msgs []Message @@ -193,6 +205,25 @@ func QuitGroup(group_id, user_id int) error { return res.Error } +// 根据群id查找群 +func FindGroup(group_id int) []Group { + var groups []Group + DB.Debug().Where("id = ?", group_id).Find(&groups) + return groups +} + +// 删除群聊 +func DeleteGroup(group_id int, auth_id int) error { + res := DB.Debug().Delete(&Group{}, "id = ? and auth_id = ?", group_id, auth_id) + return res.Error +} + +// 删除群里的用户 +func DeleteGroupUsers(group_id int) error { + res := DB.Debug().Delete(&GroupUser{}, "group_id = ?", group_id) + return res.Error +} + func FindFriend(from_user_id, to_user_id int) []Friend { var friends []Friend DB.Debug().Where("user_id = ? and friend_id = ?", from_user_id, to_user_id).Find(&friends) @@ -252,3 +283,9 @@ func FindGroupUsers(group_id int) []GroupUser { DB.Debug().Where("group_id = ?", group_id).Find(&groupUsers) return groupUsers } + +func FindGroupByNameLike(groupName string) []Group { + var groups []Group + DB.Debug().Where("group_name like ?", "%"+groupName+"%").Limit(20).Find(&groups) + return groups +} diff --git a/dao/user.go b/dao/user.go index b36d550..d7aae5f 100644 --- a/dao/user.go +++ b/dao/user.go @@ -16,6 +16,7 @@ type User struct { Role string `gorm:"column:role"` Redis bool `gorm:"column:redis"` Run bool `gorm:"column:run"` + Upload bool `gorm:"column:upload"` CreateTime string `gorm:"column:create_time"` UpdateTime string `gorm:"column:update_time"` } diff --git a/handler/im.go b/handler/im.go index 6f11ffa..aaa4b5d 100644 --- a/handler/im.go +++ b/handler/im.go @@ -68,6 +68,7 @@ func SetUpIMGroup(router *gin.Engine) { imGroup.POST("/accept_invite", AcceptInvite) imGroup.POST("/create_group", CreateGroup) imGroup.POST("/get_group", GetGroups) + imGroup.POST("/get_group_req_user", GetFriendRequest) imGroup.GET("/sse_msg", ServerSendMsg) imGroup.GET("/ws_v2", ServerSsendMsgV2) imGroup.POST("/get_friend_list", GetFriendList) //获取好友列表,包括群聊 @@ -112,12 +113,25 @@ func DelFriendOrGroup(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"error": "parameter error", "code": proto.ParameterError, "message": "failed"}) return } + //退出群聊 err2 := service.QuitGroupService(cid, req.GroupID) if err2 == nil { c.JSON(http.StatusOK, gin.H{"code": proto.SuccessCode, "message": "success"}) } else { c.JSON(http.StatusOK, gin.H{"error": err2.Error(), "code": proto.OperationFailed, "message": "failed"}) } + } else if req.Type == 3 { + //群主解散群 + if req.GroupID == 0 { + c.JSON(http.StatusOK, gin.H{"error": "parameter error", "code": proto.ParameterError, "message": "failed"}) + return + } + err2 := service.DelGroupService(cid, req.GroupID) + if err2 == nil { + c.JSON(http.StatusOK, gin.H{"code": proto.SuccessCode, "message": "success"}) + } else { + c.JSON(http.StatusOK, gin.H{"error": err2.Error(), "code": proto.OperationFailed, "message": "failed"}) + } } else { c.JSON(http.StatusOK, gin.H{"error": "parameter error", "code": proto.ParameterError, "message": "failed"}) } @@ -162,10 +176,22 @@ func GetMessage(c *gin.Context) { } func GetFriendRequest(c *gin.Context) { + var req Message id, _ := c.Get("id") user_id := int(id.(float64)) - data := service.GetFriendRequest(user_id) - c.JSON(http.StatusOK, gin.H{"code": proto.SuccessCode, "data": data, "message": "success"}) + + if err := c.ShouldBind(&req); err == nil { + if req.Type == 1 { + data := service.GetGroupRequestUsers(user_id) + c.JSON(http.StatusOK, gin.H{"code": proto.SuccessCode, "data": data, "message": "success"}) + } else { + data := service.GetFriendRequest(user_id) + c.JSON(http.StatusOK, gin.H{"code": proto.SuccessCode, "data": data, "message": "success"}) + } + } else { + c.JSON(http.StatusOK, gin.H{"error": err.Error(), "code": proto.ParameterError, "message": "failed"}) + } + } func CreateGroup(c *gin.Context) { diff --git a/handler/tool.go b/handler/tool.go index e566e76..f9c593d 100644 --- a/handler/tool.go +++ b/handler/tool.go @@ -19,6 +19,66 @@ func SetUpToolGroup(router *gin.Engine) { toolGroup := router.Group("/tool") toolGroup.POST("/set_redis", SetRedis) toolGroup.POST("/get_redis", GetRedis) + + //文件上传、下载 + toolGroup.POST("/upload", UploadFile) + toolGroup.GET("/download/:filename", DownloadFile) +} + +func UploadFile(c *gin.Context) { + //先查看是否有权限 + id, _ := c.Get("id") + id1 := int(id.(float64)) + //从请求头获取upload_type + uploadType := c.GetHeader("upload_type") + if uploadType == "" { + c.JSON(http.StatusOK, gin.H{"error": "upload_type is empty", "code": proto.ParameterError, "message": "failed"}) + return + } + + user := dao.FindUserByUserID(id1) + if user.Upload == false { + c.JSON(http.StatusOK, gin.H{"error": "no upload Permissions", "code": proto.NoUploadPermissions, "message": "failed"}) + return + } + //上传文件 + file, err := c.FormFile("file") + if err != nil { + c.JSON(http.StatusOK, gin.H{"error": "upload file failed", "code": proto.UploadFileFailed, "message": "failed"}) + return + } + + //保存文件 + filePath, fileStoreName, err := service.SaveFile(file, uploadType) + if err != nil { + c.JSON(http.StatusOK, gin.H{"error": "save file failed", "code": proto.SaveFileFailed, "message": "failed"}) + return + } + //保存文件信息 + fileSize := int(file.Size) + fileName := file.Filename + fileType := file.Header.Get("file_type") + fileID := dao.CreateFile(fileStoreName, fileName, fileType, filePath, fileSize, id1) + if fileID == 0 { + c.JSON(http.StatusOK, gin.H{"error": "save file info failed", "code": proto.SaveFileInfoFailed, "message": "failed"}) + return + } + +} + +func DownloadFile(c *gin.Context) { + //参数 + filename := c.Param("filename") + //file_id, _ := strconv.Atoi(c.Query("id")) + id, _ := c.Get("id") + //查询文件信息 + file := dao.FindFileByNames(filename, int(id.(float64))) + if file.ID == 0 { + c.JSON(http.StatusOK, gin.H{"error": "file not found", "code": proto.FileNotFound, "message": "failed"}) + return + } + //下载文件 + c.File(file.FilePath + file.FileStoreName) } func SetRedis(c *gin.Context) { diff --git a/handler/user.go b/handler/user.go index ef7fb8f..b990ba2 100644 --- a/handler/user.go +++ b/handler/user.go @@ -194,11 +194,13 @@ func SearchHandler(c *gin.Context) { if err := c.ShouldBind(&req_data); err == nil { if req_data.ID != -1 { user := service.GetUserByID(req_data.ID) - c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": user}) + group := service.GetGroupByID(req_data.ID) + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": user, "group": group}) return } else if req_data.Keyword != "" { users := service.GetUserByNameLike(req_data.Keyword) - c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": users}) + groups := service.GetGroupByNameLike(req_data.Keyword) + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": users, "group": groups}) return } else { c.JSON(200, gin.H{"code": proto.ParameterError, "message": "error", "data": "无ID 与 关键字"}) diff --git a/proto/conf.go b/proto/conf.go index bc578cb..9027aa1 100644 --- a/proto/conf.go +++ b/proto/conf.go @@ -23,6 +23,9 @@ const ( // 以下是持续集成、部署的配置 CID_BASE_DIR = "/home/lijun/cid/" + + // 以下是文件上传的配置 + FILE_BASE_DIR = "/home/lijun/file/" ) const ( @@ -39,6 +42,13 @@ const ( MSG_STATUS_UNREAD = 0 // 未读 ) +const ( + //文件上传类型 + File_TYPE = 1 // 通用文件 + //用于视频解析 + Video_TYPE = 2 // 视频文件 +) + type User struct { gorm.Model Name string `gorm:"column:name"` diff --git a/proto/status.go b/proto/status.go index a0f71cb..2677aa4 100644 --- a/proto/status.go +++ b/proto/status.go @@ -49,4 +49,12 @@ const ( //消息错误码 MsgSendFailed = 61 // 消息发送失败 + + //文件错误码 + FileNotFound = 71 // 文件不存在 + FileUploadFailed = 72 // 文件上传失败 + SaveFileInfoFailed = 73 // 保存文件信息失败 + SaveFileFailed = 74 // 保存文件失败 + UploadFileFailed = 75 // 上传文件失败 + NoUploadPermissions = 76 // 无上传权限 ) diff --git a/service/fileService.go b/service/fileService.go new file mode 100644 index 0000000..b5de17a --- /dev/null +++ b/service/fileService.go @@ -0,0 +1,60 @@ +package service + +import ( + "github.com/google/uuid" + "io" + "mime/multipart" + "os" + "path" + "time" + "videoplayer/proto" + "videoplayer/worker" +) + +// 检查path是否存在当前日期文件夹如(2024-08-09),不存在则path下当前日期文件夹创建,存在则返回 +func getFilePath(path string) string { + //当前日期,格式为2024-08-09 + date := time.Now().Format("2006-01-02") + //拼接文件路径 + filePath := path + "/" + date + //判断文件夹是否存在 + _, err := os.Stat(filePath) + if err != nil { + //不存在则创建 + os.MkdirAll(filePath, os.ModePerm) + } + return filePath +} + +func SaveFile(file *multipart.FileHeader, uploadType string) (string, string, error) { + //获取文件后缀 + fileSuffix := path.Ext(file.Filename) + //生成文件名 + fileStoreName := uuid.NewString() + fileSuffix + //生成文件路径 + path_ := getFilePath(proto.FILE_BASE_DIR) + filePath := path_ + "/" + fileStoreName + + //保存文件file + dst, err := os.Create(filePath) + if err != nil { + return "", "", err + } + defer dst.Close() + + // 复制文件内容 + file_, err := file.Open() + if err != nil { + return "", "", err + } + _, err = io.Copy(dst, file_) + if err != nil { + return "", "", err + } + + if uploadType == "2" { + worker.PushRedisList("video_need_handle", filePath) + } + + return path_, fileStoreName, nil +} diff --git a/service/imService.go b/service/imService.go index 13c878d..ec95b22 100644 --- a/service/imService.go +++ b/service/imService.go @@ -66,6 +66,17 @@ func CreateGeneralMessageService(from_id, to_id, msg_type, group_id int, content err, id = dao.CreateGeneralMessage(from_id, to_id, msg_type, 0, group_id, content) case proto.MSG_TYPE_GROUP_ADD: //加入群聊请求 + //判断是否在群里 + groupUser := dao.FindGroupUser(from_id, group_id) + if len(groupUser) > 0 { + return errors.New("已在群里"), 0 + } + //查看是否已经发送过请求 + res, _ := dao.GetMsgUserGroupReq(from_id, group_id) + if len(res) > 0 { + return errors.New("已发送请求"), res[0].ID + } + err, id = dao.CreateGeneralMessage(from_id, to_id, msg_type, 0, group_id, content) case proto.MSG_TYPE_GROUP_INVI: //邀请加群,直接加入 @@ -110,6 +121,9 @@ func GetMsgUserByIndexService(from_id, to_id, index, msq_type, from_user_id, gro func AddFriendService(id, from_user_id, to_user_id int) error { // 业务逻辑 res := dao.FindMessageByID(uint(id)) + if len(res) == 0 { + return errors.New("no such message") + } if res[0].FromUserID == to_user_id && res[0].ToUserID == from_user_id { friend := dao.FindFriend(from_user_id, to_user_id) if len(friend) > 0 { @@ -179,9 +193,41 @@ func QuitGroupService(user_id, group_id int) error { err := dao.QuitGroup(group_id, user_id) return err } +func DelGroupService(user_id, group_id int) error { + //查找群聊 + groups := dao.FindGroup(group_id) + if len(groups) == 0 { + return errors.New("no such group") + } + if groups[0].AuthID != user_id { + return errors.New("no permission") + } + //删除群聊 + err := dao.DeleteGroup(group_id, user_id) + err = dao.DeleteGroupUsers(group_id) + return err +} func GetGroups(user_id int) []dao.Group { //获取群聊 groups := dao.GetGroups(user_id) return groups } + +func GetGroupByID(group_id int) []dao.Group { + //获取群聊 + groups := dao.FindGroupByID(group_id) + return groups +} + +func GetGroupByNameLike(name string) []dao.Group { + //获取群聊 + groups := dao.FindGroupByNameLike(name) + return groups +} + +func GetGroupRequestUsers(user_id int) []dao.FriendRequest { + //获取群聊请求 + users := dao.GetGroupRequestUsers(user_id) + return users +} diff --git a/worker/redis.go b/worker/redis.go index da00887..bc19b5f 100644 --- a/worker/redis.go +++ b/worker/redis.go @@ -17,9 +17,9 @@ func InitRedis() error { ctx := context.Background() // 连接redis redisClient = redis.NewClient(&redis.Options{ - Addr: proto.REDIS_ADDR, // Redis 服务器地址 - //Password: proto.REDIS_PASSWORD, // 如果 Redis 设置了密码 - DB: proto.REIDS_DB, // 使用的数据库编号 + Addr: proto.REDIS_ADDR, // Redis 服务器地址 + Password: proto.REDIS_PASSWORD, // 如果 Redis 设置了密码 + DB: proto.REIDS_DB, // 使用的数据库编号 }) // 验证 Redis 客户端是否可以正常工作