diff --git a/internal/core/authority.go b/internal/core/authority.go index 35264485..c016c840 100644 --- a/internal/core/authority.go +++ b/internal/core/authority.go @@ -36,6 +36,7 @@ const ( type act uint8 type FriendFilter map[int64]types.Empty +type FriendSet map[string]types.Empty type Action struct { Act act @@ -47,13 +48,12 @@ type AuthorizationManageService interface { IsAllow(user *model.User, action *Action) bool BeFriendFilter(userId int64) FriendFilter BeFriendIds(userId int64) ([]int64, error) + MyFriendSet(userId int64) FriendSet } func (f FriendFilter) IsFriend(userId int64) bool { - // _, yesno := f[userId] - // return yesno - // so, you are friend with all world now - return true + _, yeah := f[userId] + return yeah } // IsAllow default true if user is admin diff --git a/internal/core/core.go b/internal/core/core.go index 0c86d5ed..f0c158b1 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -19,16 +19,15 @@ type DataService interface { TweetManageService TweetHelpService - // 附件检测服务 - AttachmentCheckService - // 评论服务 CommentService CommentManageService // 用户服务 UserManageService + ContactManageService // 安全服务 SecurityService + AttachmentCheckService } diff --git a/internal/core/user.go b/internal/core/user.go index 36ceabfc..cf0ebaa8 100644 --- a/internal/core/user.go +++ b/internal/core/user.go @@ -2,6 +2,7 @@ package core import ( "github.com/rocboss/paopao-ce/internal/model" + "github.com/rocboss/paopao-ce/internal/model/rest" ) // UserManageService 用户管理服务 @@ -13,5 +14,14 @@ type UserManageService interface { GetUsersByKeyword(keyword string) ([]*model.User, error) CreateUser(user *model.User) (*model.User, error) UpdateUser(user *model.User) error +} + +// ContactManageService 联系人管理服务 +type ContactManageService interface { + RequestingFriend(userId int64, friendId int64, greetings string) error + AddFriend(userId int64, friendId int64) error + RejectFriend(userId int64, friendId int64) error + DeleteFriend(userId int64, friendId int64) error + GetContacts(userId int64, offset int, limit int) (*rest.ContactsResp, error) IsFriend(userID int64, friendID int64) bool } diff --git a/internal/dao/jinzhu/authority.go b/internal/dao/jinzhu/authority.go index 63b85c68..c0f59556 100644 --- a/internal/dao/jinzhu/authority.go +++ b/internal/dao/jinzhu/authority.go @@ -3,6 +3,7 @@ package jinzhu import ( "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/model" + "github.com/rocboss/paopao-ce/pkg/types" "gorm.io/gorm" ) @@ -14,6 +15,12 @@ type authorizationManageServant struct { db *gorm.DB } +func newAuthorizationManageService(db *gorm.DB) core.AuthorizationManageService { + return &authorizationManageServant{ + db: db, + } +} + func (s *authorizationManageServant) IsAllow(user *model.User, action *core.Action) bool { // user is activation if had bind phone isActivation := (len(user.Phone) != 0) @@ -22,17 +29,40 @@ func (s *authorizationManageServant) IsAllow(user *model.User, action *core.Acti return action.Act.IsAllow(user, action.UserId, isFriend, isActivation) } +func (s *authorizationManageServant) MyFriendSet(userId int64) core.FriendSet { + ids, err := (&model.Contact{UserId: userId}).MyFriendIds(s.db) + if err != nil { + return core.FriendSet{} + } + + resp := make(core.FriendSet, len(ids)) + for _, id := range ids { + resp[id] = types.Empty{} + } + return resp +} + func (s *authorizationManageServant) BeFriendFilter(userId int64) core.FriendFilter { - // just empty now - return core.FriendFilter{} + ids, err := (&model.Contact{FriendId: userId}).BeFriendIds(s.db) + if err != nil { + return core.FriendFilter{} + } + + resp := make(core.FriendFilter, len(ids)) + for _, id := range ids { + resp[id] = types.Empty{} + } + return resp } func (s *authorizationManageServant) BeFriendIds(userId int64) ([]int64, error) { - // just empty now - return []int64{}, nil + return (&model.Contact{FriendId: userId}).BeFriendIds(s.db) } func (s *authorizationManageServant) isFriend(userId int64, friendId int64) bool { - // just true now - return true + contact, err := (&model.Contact{UserId: friendId, FriendId: userId}).GetByUserFriend(s.db) + if err == nil || contact.Status == model.ContactStatusAgree { + return true + } + return false } diff --git a/internal/dao/jinzhu/contacts.go b/internal/dao/jinzhu/contacts.go new file mode 100644 index 00000000..c55a94a0 --- /dev/null +++ b/internal/dao/jinzhu/contacts.go @@ -0,0 +1,268 @@ +package jinzhu + +import ( + "time" + + "github.com/rocboss/paopao-ce/internal/core" + "github.com/rocboss/paopao-ce/internal/model" + "github.com/rocboss/paopao-ce/internal/model/rest" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +var ( + _ core.ContactManageService = (*contactManageServant)(nil) +) + +type contactManageServant struct { + db *gorm.DB +} + +func newContactManageService(db *gorm.DB) core.ContactManageService { + return &contactManageServant{ + db: db, + } +} + +func (s *contactManageServant) fetchOrNewContact(db *gorm.DB, userId int64, friendId int64, status int8) (*model.Contact, error) { + contact := &model.Contact{ + UserId: userId, + FriendId: friendId, + } + contact, err := contact.FetchUser(db) + if err != nil { + contact = &model.Contact{ + UserId: userId, + FriendId: friendId, + Status: status, + } + if contact, err = contact.Create(db); err != nil { + logrus.Errorf("contactManageServant.fetchOrNewContact create new contact err:%s", err) + return nil, err + } + } + return contact, nil +} + +func (s *contactManageServant) RequestingFriend(userId int64, friendId int64, greetings string) (err error) { + db := s.db.Begin() + defer func() { + if err == nil { + db.Commit() + } else { + db.Rollback() + } + }() + + contact, e := s.fetchOrNewContact(db, userId, friendId, model.ContactStatusRequesting) + if e != nil { + err = e + return + } + + // 如果已经好友,啥也不干 + if contact.Status == model.ContactStatusAgree { + return nil + } else if contact.Status == model.ContactStatusReject || contact.Status == model.ContactStatusDeleted { + contact.Status = model.ContactStatusRequesting + contact.IsDel = 0 // remove deleted flag if needed + if err = contact.UpdateInUnscoped(db); err != nil { + logrus.Errorf("contactManageServant.RequestingFriend update exsit contact err:%s", err) + return + } + } + + msg := &model.Message{ + SenderUserID: userId, + ReceiverUserID: friendId, + Type: model.MsgTypeRequestingFriend, + Brief: "请求添加好友,并附言:", + Content: greetings, + ReplyID: int64(model.ContactStatusRequesting), + } + if _, err = msg.Create(db); err != nil { + logrus.Errorf("contactManageServant.RequestingFriend create message err:%s", err) + return + } + return nil +} + +func (s *contactManageServant) AddFriend(userId int64, friendId int64) (err error) { + db := s.db.Begin() + defer func() { + if err == nil { + db.Commit() + } else { + db.Rollback() + } + }() + + contact := &model.Contact{ + UserId: friendId, + FriendId: userId, + } + if contact, err = contact.GetByUserFriend(db); err != nil { + return + } + // 如果还不是请求好友,啥也不干 + if contact.Status != model.ContactStatusRequesting { + logrus.Debugf("contactManageServant.AddFriend not reuesting status now so skip") + return nil + } + contact.Status = model.ContactStatusAgree + if err = contact.Update(db); err != nil { + return err + } + + contact, err = s.fetchOrNewContact(db, userId, friendId, model.ContactStatusAgree) + if err != nil { + return + } + + // 如果已经好友,啥也不干 + if contact.Status != model.ContactStatusAgree { + contact.Status = model.ContactStatusAgree + contact.IsDel = 0 // remove deleted flag + if err = contact.UpdateInUnscoped(db); err != nil { + logrus.Errorf("contactManageServant.AddFriend update contact err:%s", err) + return + } + } + + args := []any{userId, friendId, friendId, userId, model.MsgTypeRequestingFriend, model.ContactStatusRequesting} + msgs, e := (&model.Message{}).FetchBy(db, model.Predicates{ + "((sender_user_id = ? AND receiver_user_id = ?) OR (sender_user_id = ? AND receiver_user_id = ?)) AND type = ? AND reply_id = ?": args, + }) + if e != nil { + err = e + return + } + for _, msg := range msgs { + msg.ReplyID = int64(model.ContactStatusAgree) + if err = msg.Update(db); err != nil { + return + } + } + return nil +} + +func (s *contactManageServant) RejectFriend(userId int64, friendId int64) (err error) { + db := s.db.Begin() + defer func() { + if err == nil { + db.Commit() + } else { + db.Rollback() + } + }() + + contact := &model.Contact{ + UserId: friendId, + FriendId: userId, + } + if contact, err = contact.GetByUserFriend(db); err != nil { + return + } + // 如果还不是请求好友,啥也不干 + if contact.Status != model.ContactStatusRequesting { + return nil + } + contact.Status = model.ContactStatusReject + if err = contact.Update(db); err != nil { + return err + } + + args := []any{friendId, userId, model.MsgTypeRequestingFriend, model.ContactStatusRequesting} + msgs, e := (&model.Message{}).FetchBy(db, model.Predicates{ + "sender_user_id = ? AND receiver_user_id = ? AND type = ? AND reply_id = ?": args, + }) + if e != nil { + err = e + return + } + for _, msg := range msgs { + msg.ReplyID = int64(model.ContactStatusReject) + if err = msg.Update(db); err != nil { + return + } + } + return nil +} + +func (s *contactManageServant) DeleteFriend(userId int64, friendId int64) (err error) { + db := s.db.Begin() + defer func() { + if err == nil { + db.Commit() + } else { + db.Rollback() + } + }() + + contact := &model.Contact{ + UserId: userId, + FriendId: friendId, + } + contacts, e := contact.FetchByUserFriendAll(db) + if e != nil { + return e + } + + for _, contact := range contacts { + // 如果还不是好友,啥也不干 + if contact.Status != model.ContactStatusAgree { + continue + } + contact.Status = model.ContactStatusDeleted + contact.DeletedOn = time.Now().Unix() + contact.IsDel = 1 + if err = contact.Update(db); err != nil { + return + } + } + return nil +} + +func (s *contactManageServant) GetContacts(userId int64, offset int, limit int) (*rest.ContactsResp, error) { + contact := &model.Contact{} + condition := model.ConditionsT{ + "user_id": userId, + "status": model.ContactStatusAgree, + } + contacts, err := contact.List(s.db, condition, offset, limit) + if err != nil { + return nil, err + } + total, err := contact.Count(s.db, condition) + if err != nil { + return nil, err + } + resp := &rest.ContactsResp{ + Contacts: make([]rest.ContactItem, 0, len(contacts)), + Total: total, + } + for _, c := range contacts { + if c.User != nil { + resp.Contacts = append(resp.Contacts, rest.ContactItem{ + UserId: c.FriendId, + UserName: c.User.Username, + Nickname: c.User.Nickname, + Avatar: c.User.Avatar, + Phone: c.User.Phone, + }) + } + } + return resp, nil +} + +func (s *contactManageServant) IsFriend(userId int64, friendId int64) bool { + contact := &model.Contact{ + UserId: friendId, + FriendId: userId, + } + contact, err := contact.GetByUserFriend(s.db) + if err == nil && contact.Status == model.ContactStatusAgree { + return true + } + return false +} diff --git a/internal/dao/jinzhu/jinzhu.go b/internal/dao/jinzhu/jinzhu.go index 736b4f43..65b1dc06 100644 --- a/internal/dao/jinzhu/jinzhu.go +++ b/internal/dao/jinzhu/jinzhu.go @@ -1,6 +1,6 @@ // Core service implement base gorm+mysql/postgresql/sqlite3. // Jinzhu is the primary developer of gorm so use his name as -// pakcage name as a saluter. +// package name as a saluter. package jinzhu @@ -29,6 +29,7 @@ type dataServant struct { core.CommentService core.CommentManageService core.UserManageService + core.ContactManageService core.SecurityService core.AttachmentCheckService } @@ -63,6 +64,7 @@ func NewDataService() (core.DataService, core.VersionInfo) { CommentService: newCommentService(db), CommentManageService: newCommentManageService(db), UserManageService: newUserManageService(db), + ContactManageService: newContactManageService(db), SecurityService: newSecurityService(db), AttachmentCheckService: security.NewAttachmentCheckService(), } @@ -80,5 +82,5 @@ func (s *dataServant) Name() string { } func (s *dataServant) Version() *semver.Version { - return semver.MustParse("v0.1.0") + return semver.MustParse("v0.2.0") } diff --git a/internal/model/contact.go b/internal/model/contact.go new file mode 100644 index 00000000..14049f77 --- /dev/null +++ b/internal/model/contact.go @@ -0,0 +1,119 @@ +package model + +import ( + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +const ( + ContactStatusRequesting int8 = iota + 1 + ContactStatusAgree + ContactStatusReject + ContactStatusDeleted +) + +type Contact struct { + *Model + User *User `json:"-" gorm:"foreignKey:ID;references:FriendId"` + UserId int64 `json:"user_id"` + FriendId int64 `json:"friend_id"` + GroupId int64 `json:"group_id"` + Remark string `json:"remark"` + Status int8 `json:"status"` // 1请求好友, 2已同意好友, 3已拒绝好友, 4已删除好友 + IsTop int8 `json:"is_top"` + IsBlack int8 `json:"is_black"` + NoticeEnable int8 `json:"notice_enable"` +} + +func (c *Contact) FetchUser(db *gorm.DB) (*Contact, error) { + var contact Contact + err := db.Omit("User").Unscoped().Where("user_id = ? AND friend_id = ?", c.UserId, c.FriendId).First(&contact).Error + if err != nil { + logrus.Debugf("Contact.FetchUser fetch user error:%s", err) + return nil, err + } + return &contact, nil +} + +func (c *Contact) GetByUserFriend(db *gorm.DB) (*Contact, error) { + var contact Contact + err := db.Omit("User").Where("user_id = ? AND friend_id = ?", c.UserId, c.FriendId).First(&contact).Error + if err != nil { + return nil, err + } + return &contact, nil +} + +func (c *Contact) FetchByUserFriendAll(db *gorm.DB) ([]*Contact, error) { + var contacts []*Contact + if err := db.Omit("User"). + Where("(user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)", + c.UserId, c.FriendId, c.FriendId, c.UserId). + Find(&contacts).Error; err != nil { + return nil, err + } + return contacts, nil +} + +func (c *Contact) List(db *gorm.DB, conditions ConditionsT, offset, limit int) ([]*Contact, error) { + var contacts []*Contact + var err error + tn := db.NamingStrategy.TableName("Contact") + "." + + if offset >= 0 && limit > 0 { + db = db.Offset(offset).Limit(limit) + } + for k, v := range conditions { + if k != "ORDER" { + db = db.Where(tn+k, v) + } + } + + db.Joins("User").Order("`User`.`nickname` ASC") + if err = db.Find(&contacts).Error; err != nil { + return nil, err + } + return contacts, nil +} + +func (c *Contact) BeFriendIds(db *gorm.DB) (ids []int64, err error) { + if err = db.Model(c).Omit("User").Select("user_id").Where("friend_id = ? AND status = ?", c.FriendId, ContactStatusAgree).Find(&ids).Error; err != nil { + return nil, err + } + return +} + +func (c *Contact) MyFriendIds(db *gorm.DB) (ids []string, err error) { + if err = db.Model(c).Omit("User").Select("friend_id").Where("user_id = ? AND status = ?", c.UserId, ContactStatusAgree).Find(&ids).Error; err != nil { + return nil, err + } + return +} + +func (m *Contact) Count(db *gorm.DB, conditions ConditionsT) (int64, error) { + var count int64 + + for k, v := range conditions { + if k != "ORDER" { + db = db.Where(k, v) + } + } + if err := db.Model(m).Omit("User").Count(&count).Error; err != nil { + return 0, err + } + + return count, nil +} + +func (c *Contact) Create(db *gorm.DB) (*Contact, error) { + err := db.Omit("User").Create(&c).Error + return c, err +} + +func (c *Contact) Update(db *gorm.DB) error { + return db.Model(&Contact{}).Omit("User").Where("id = ?", c.Model.ID).Save(c).Error +} + +func (c *Contact) UpdateInUnscoped(db *gorm.DB) error { + return db.Unscoped().Omit("User").Save(c).Error +} diff --git a/internal/model/message.go b/internal/model/message.go index 1c7f42db..c1277cf7 100644 --- a/internal/model/message.go +++ b/internal/model/message.go @@ -92,15 +92,24 @@ func (m *Message) Get(db *gorm.DB) (*Message, error) { if m.ReceiverUserID > 0 { db = db.Where("receiver_user_id = ?", m.ReceiverUserID) } - - err := db.First(&message).Error - if err != nil { - return &message, err + if err := db.First(&message).Error; err != nil { + return nil, err } - return &message, nil } +func (m *Message) FetchBy(db *gorm.DB, predicates Predicates) ([]*Message, error) { + var messages []*Message + for k, v := range predicates { + db = db.Where(k, v...) + } + db = db.Where("is_del = 0") + if err := db.Find(&messages).Error; err != nil { + return nil, err + } + return messages, nil +} + func (c *Message) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*Message, error) { var messages []*Message var err error diff --git a/internal/model/rest/contacts.go b/internal/model/rest/contacts.go new file mode 100644 index 00000000..bbf31e42 --- /dev/null +++ b/internal/model/rest/contacts.go @@ -0,0 +1,31 @@ +package rest + +type RequestingFriendReq struct { + UserId int64 `json:"user_id" binding:"required"` + Greetings string `json:"greetings" binding:"required"` +} + +type AddFriendReq struct { + UserId int64 `json:"user_id" binding:"required"` +} + +type RejectFriendReq struct { + UserId int64 `json:"user_id" binding:"required"` +} + +type DeleteFriendReq struct { + UserId int64 `json:"user_id"` +} + +type ContactItem struct { + UserId int64 `json:"user_id"` + UserName string `json:"username"` + Nickname string `json:"nickname"` + Avatar string `json:"avatar"` + Phone string `json:"phone"` +} + +type ContactsResp struct { + Contacts []ContactItem `json:"contacts"` + Total int64 `json:"total"` +} diff --git a/internal/model/rest/user.go b/internal/model/rest/user.go new file mode 100644 index 00000000..60ff467d --- /dev/null +++ b/internal/model/rest/user.go @@ -0,0 +1,11 @@ +package rest + +type UserProfileResp struct { + ID int64 `json:"id"` + Nickname string `json:"nickname"` + Username string `json:"username"` + Status int `json:"status"` + Avatar string `json:"avatar"` + IsAdmin bool `json:"is_admin"` + IsFriend bool `json:"is_friend"` +} diff --git a/internal/routers/api/user.go b/internal/routers/api/user.go index 18267d81..2ba1e6b7 100644 --- a/internal/routers/api/user.go +++ b/internal/routers/api/user.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/internal/model" + "github.com/rocboss/paopao-ce/internal/model/rest" "github.com/rocboss/paopao-ce/internal/service" "github.com/rocboss/paopao-ce/pkg/app" "github.com/rocboss/paopao-ce/pkg/convert" @@ -90,6 +91,132 @@ func Register(c *gin.Context) { }) } +func RequestingFriend(c *gin.Context) { + param := rest.RequestingFriendReq{} + response := app.NewResponse(c) + valid, errs := app.BindAndValid(c, ¶m) + if !valid { + logrus.Errorf("app.BindAndValid errs: %v", errs) + response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) + return + } + + user, ok := userFrom(c) + if !ok { + response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) + } + if user.ID == param.UserId { + response.ToErrorResponse(errcode.NoRequestingFriendToSelf) + return + } + + if err := service.RequestingFriend(user, ¶m); err != nil { + logrus.Errorf("service.RequestingFriend err: %v", err) + response.ToErrorResponse(errcode.SendRequestingFriendFailed) + return + } + + response.ToResponse(nil) +} + +func AddFriend(c *gin.Context) { + param := rest.AddFriendReq{} + response := app.NewResponse(c) + valid, errs := app.BindAndValid(c, ¶m) + if !valid { + logrus.Errorf("app.BindAndValid errs: %v", errs) + response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) + return + } + user, ok := userFrom(c) + if !ok { + response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) + } + if user.ID == param.UserId { + response.ToErrorResponse(errcode.NoActionToSelf) + return + } + + if err := service.AddFriend(user, ¶m); err != nil { + logrus.Errorf("service.AddFriend err: %v", err) + response.ToErrorResponse(errcode.AddFriendFailed) + return + } + + response.ToResponse(nil) +} + +func RejectFriend(c *gin.Context) { + param := rest.RejectFriendReq{} + response := app.NewResponse(c) + valid, errs := app.BindAndValid(c, ¶m) + if !valid { + logrus.Errorf("app.BindAndValid errs: %v", errs) + response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) + return + } + user, ok := userFrom(c) + if !ok { + response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) + } + if user.ID == param.UserId { + response.ToErrorResponse(errcode.NoActionToSelf) + return + } + + if err := service.RejectFriend(user, ¶m); err != nil { + logrus.Errorf("service.RejectFriend err: %v", err) + response.ToErrorResponse(errcode.RejectFriendFailed) + return + } + + response.ToResponse(nil) +} + +func DeleteFriend(c *gin.Context) { + param := rest.DeleteFriendReq{} + response := app.NewResponse(c) + valid, errs := app.BindAndValid(c, ¶m) + if !valid { + logrus.Errorf("app.BindAndValid errs: %v", errs) + response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) + return + } + user, ok := userFrom(c) + if !ok { + response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) + } + if user.ID == param.UserId { + response.ToErrorResponse(errcode.NoActionToSelf) + return + } + + if err := service.DeleteFriend(user, ¶m); err != nil { + logrus.Errorf("service.DeleteFriend err: %v", err) + response.ToErrorResponse(errcode.DeleteFriendFailed) + return + } + + response.ToResponse(nil) +} + +func GetContacts(c *gin.Context) { + response := app.NewResponse(c) + user, ok := userFrom(c) + if !ok { + response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) + } + + offset, limit := app.GetPageOffset(c) + resp, err := service.GetContacts(user, offset, limit) + if err != nil { + logrus.Errorf("service.DeleteFriend err: %v", err) + response.ToErrorResponse(errcode.GetContactsFailed) + return + } + response.ToResponseList(resp.Contacts, resp.Total) +} + // 获取用户基本信息 func GetUserInfo(c *gin.Context) { param := service.AuthRequest{} @@ -241,7 +368,10 @@ func BindUserPhone(c *gin.Context) { // 执行绑定 user.Phone = param.Phone - service.UpdateUserInfo(user) + if err := service.UpdateUserInfo(user); err != nil { + response.ToErrorResponse(err) + return + } response.ToResponse(nil) } @@ -279,29 +409,24 @@ func ChangeUserStatus(c *gin.Context) { func GetUserProfile(c *gin.Context) { response := app.NewResponse(c) username := c.Query("username") + user, _ := userFrom(c) - user, err := service.GetUserByUsername(username) + resp, err := service.GetUserByUsername(user, username) if err != nil { logrus.Errorf("service.GetUserByUsername err: %v\n", err) response.ToErrorResponse(errcode.NoExistUsername) return } - response.ToResponse(gin.H{ - "id": user.ID, - "nickname": user.Nickname, - "username": user.Username, - "status": user.Status, - "avatar": user.Avatar, - "is_admin": user.IsAdmin, - }) + response.ToResponse(resp) } func GetUserPosts(c *gin.Context) { response := app.NewResponse(c) username := c.Query("username") + user, exists := userFrom(c) - user, err := service.GetUserByUsername(username) + other, err := service.GetUserByUsername(user, username) if err != nil { logrus.Errorf("service.GetUserByUsername err: %v\n", err) response.ToErrorResponse(errcode.NoExistUsername) @@ -309,16 +434,15 @@ func GetUserPosts(c *gin.Context) { } visibilities := []model.PostVisibleT{model.PostVisitPublic} - if u, exists := c.Get("USER"); exists { - self := u.(*model.User) - if self.ID == user.ID || self.IsAdmin { + if exists { + if user.ID == other.ID || user.IsAdmin { visibilities = append(visibilities, model.PostVisitPrivate, model.PostVisitFriend) - } else if service.IsFriend(user.ID, self.ID) { + } else if other.IsFriend { visibilities = append(visibilities, model.PostVisitFriend) } } conditions := model.ConditionsT{ - "user_id": user.ID, + "user_id": other.ID, "visibility IN ?": visibilities, "ORDER": "latest_replied_on DESC", } diff --git a/internal/routers/router.go b/internal/routers/router.go index 3dbeee71..d31a202d 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -79,126 +79,131 @@ func NewRouter() *gin.Engine { authApi := r.Group("/").Use(middleware.JWT()) privApi := r.Group("/").Use(middleware.JWT()).Use(middleware.Priv()) adminApi := r.Group("/").Use(middleware.JWT()).Use(middleware.Admin()) - { - // 同步索引 - authApi.GET("/sync/index", api.SyncSearchIndex) - // 获取当前用户信息 - authApi.GET("/user/info", api.GetUserInfo) + // 核心路由注册 + routeCore(authApi, privApi, adminApi) - // 获取当前用户未读消息数量 - authApi.GET("/user/msgcount/unread", api.GetUnreadMsgCount) + // 支付宝路由注册 + routeFriendship(authApi) - // 获取消息列表 - authApi.GET("/user/messages", api.GetMessages) + // Friendship路由注册 + routeFriendship(authApi) - // 标记消息已读 - authApi.POST("/user/message/read", api.ReadMessage) + // 默认404 + e.NoRoute(func(c *gin.Context) { + c.JSON(http.StatusNotFound, gin.H{ + "code": 404, + "msg": "Not Found", + }) + }) - // 发送用户私信 - authApi.POST("/user/whisper", api.SendUserWhisper) + // 默认405 + e.NoMethod(func(c *gin.Context) { + c.JSON(http.StatusMethodNotAllowed, gin.H{ + "code": 405, + "msg": "Method Not Allowed", + }) + }) - // 获取用户收藏列表 - authApi.GET("/user/collections", api.GetUserCollections) + return e +} - // 获取用户点赞列表 - authApi.GET("/user/stars", api.GetUserStars) +func routeCore(authApi gin.IRoutes, privApi gin.IRoutes, adminApi gin.IRoutes) { + // 同步索引 + authApi.GET("/sync/index", api.SyncSearchIndex) - // 绑定用户手机号 - if conf.CfgIf("PhoneBind") { - authApi.POST("/user/phone", api.BindUserPhone) - } + // 获取当前用户信息 + authApi.GET("/user/info", api.GetUserInfo) - // 修改密码 - authApi.POST("/user/password", api.ChangeUserPassword) + // 获取当前用户未读消息数量 + authApi.GET("/user/msgcount/unread", api.GetUnreadMsgCount) - // 修改昵称 - authApi.POST("/user/nickname", api.ChangeNickname) + // 获取消息列表 + authApi.GET("/user/messages", api.GetMessages) - // 修改头像 - authApi.POST("/user/avatar", api.ChangeAvatar) + // 标记消息已读 + authApi.POST("/user/message/read", api.ReadMessage) - // 检索用户 - authApi.GET("/suggest/users", api.GetSuggestUsers) + // 发送用户私信 + authApi.POST("/user/whisper", api.SendUserWhisper) - // 检索标签 - authApi.GET("/suggest/tags", api.GetSuggestTags) + // 获取用户收藏列表 + authApi.GET("/user/collections", api.GetUserCollections) - // 上传资源 - privApi.POST("/attachment", api.UploadAttachment) + // 获取用户点赞列表 + authApi.GET("/user/stars", api.GetUserStars) - // 下载资源预检 - privApi.GET("/attachment/precheck", api.DownloadAttachmentPrecheck) + // 绑定用户手机号 + authApi.POST("/user/phone", api.BindUserPhone) - // 下载资源 - privApi.GET("/attachment", api.DownloadAttachment) + // 修改密码 + authApi.POST("/user/password", api.ChangeUserPassword) - // 发布动态 - privApi.POST("/post", api.CreatePost) + // 修改昵称 + authApi.POST("/user/nickname", api.ChangeNickname) - // 删除动态 - privApi.DELETE("/post", api.DeletePost) + // 修改头像 + authApi.POST("/user/avatar", api.ChangeAvatar) - // 获取动态点赞状态 - authApi.GET("/post/star", api.GetPostStar) + // 检索用户 + authApi.GET("/suggest/users", api.GetSuggestUsers) - // 动态点赞操作 - privApi.POST("/post/star", api.PostStar) + // 检索标签 + authApi.GET("/suggest/tags", api.GetSuggestTags) - // 获取动态收藏状态 - authApi.GET("/post/collection", api.GetPostCollection) + // 上传资源 + privApi.POST("/attachment", api.UploadAttachment) - // 动态收藏操作 - privApi.POST("/post/collection", api.PostCollection) + // 下载资源预检 + privApi.GET("/attachment/precheck", api.DownloadAttachmentPrecheck) - // 锁定动态 - privApi.POST("/post/lock", api.LockPost) + // 下载资源 + privApi.GET("/attachment", api.DownloadAttachment) - // 置顶动态 - privApi.POST("/post/stick", api.StickPost) + // 发布动态 + privApi.POST("/post", api.CreatePost) - // 修改动态可见度 - privApi.POST("/post/visibility", api.VisiblePost) + // 删除动态 + privApi.DELETE("/post", api.DeletePost) - // 发布动态评论 - privApi.POST("/post/comment", api.CreatePostComment) + // 获取动态点赞状态 + authApi.GET("/post/star", api.GetPostStar) - // 删除动态评论 - privApi.DELETE("/post/comment", api.DeletePostComment) + // 动态点赞操作 + privApi.POST("/post/star", api.PostStar) - // 发布评论回复 - privApi.POST("/post/comment/reply", api.CreatePostCommentReply) + // 获取动态收藏状态 + authApi.GET("/post/collection", api.GetPostCollection) - // 删除评论回复 - privApi.DELETE("/post/comment/reply", api.DeletePostCommentReply) + // 动态收藏操作 + privApi.POST("/post/collection", api.PostCollection) - // 管理·禁言/解封用户 - adminApi.POST("/admin/user/status", api.ChangeUserStatus) - } + // 锁定动态 + privApi.POST("/post/lock", api.LockPost) - // 支付宝路由注册 - alipayRoute(r, authApi) + // 置顶动态 + privApi.POST("/post/stick", api.StickPost) - // 默认404 - e.NoRoute(func(c *gin.Context) { - c.JSON(http.StatusNotFound, gin.H{ - "code": 404, - "msg": "Not Found", - }) - }) + // 修改动态可见度 + privApi.POST("/post/visibility", api.VisiblePost) - // 默认405 - e.NoMethod(func(c *gin.Context) { - c.JSON(http.StatusMethodNotAllowed, gin.H{ - "code": 405, - "msg": "Method Not Allowed", - }) - }) + // 发布动态评论 + privApi.POST("/post/comment", api.CreatePostComment) - return e + // 删除动态评论 + privApi.DELETE("/post/comment", api.DeletePostComment) + + // 发布评论回复 + privApi.POST("/post/comment/reply", api.CreatePostCommentReply) + + // 删除评论回复 + privApi.DELETE("/post/comment/reply", api.DeletePostCommentReply) + + // 管理·禁言/解封用户 + adminApi.POST("/admin/user/status", api.ChangeUserStatus) } -// routeLocalOSS register LocalOSS route if neeed +// routeLocalOSS register LocalOSS route if needed func routeLocalOSS(e *gin.Engine) { if !conf.CfgIf("LocalOSS") { return @@ -213,7 +218,8 @@ func routeLocalOSS(e *gin.Engine) { logrus.Infof("register LocalOSS route in /oss on save path: %s", savePath) } -func alipayRoute(public gin.IRoutes, authApi gin.IRoutes) { +// routeAlipay register Alipay feature releated route if needed +func routeAlipay(public gin.IRoutes, authApi gin.IRoutes) { if !conf.CfgIf("Alipay") { return } @@ -230,3 +236,25 @@ func alipayRoute(public gin.IRoutes, authApi gin.IRoutes) { // 获取用户账单 authApi.GET("/user/wallet/bills", api.GetUserWalletBills) } + +// routeFriendship register Friendship feature releated route if needed +func routeFriendship(authApi gin.IRoutes) { + if !conf.CfgIf("Friendship") { + return + } + + // 请求添加朋友 + authApi.POST("/friend/requesting", api.RequestingFriend) + + // 同意添加好友 + authApi.POST("/friend/add", api.AddFriend) + + // 拒绝添加好友 + authApi.POST("/friend/reject", api.RejectFriend) + + // 删除好友 + authApi.POST("/friend/delete", api.DeleteFriend) + + // 获取好友列表 + authApi.GET("/user/contacts", api.GetContacts) +} diff --git a/internal/service/message.go b/internal/service/message.go index e063e492..a025a849 100644 --- a/internal/service/message.go +++ b/internal/service/message.go @@ -82,6 +82,11 @@ func GetMessages(userID int64, offset, limit int) ([]*model.MessageFormated, int } + // 好友申请消息不需要获取其他信息 + if mf.Type == model.MsgTypeRequestingFriend { + continue + } + if mf.PostID > 0 { post, err := GetPost(mf.PostID) if err == nil { diff --git a/internal/service/user.go b/internal/service/user.go index e3faf1d3..7289c067 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -11,6 +11,7 @@ import ( "github.com/gofrs/uuid" "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/internal/model" + "github.com/rocboss/paopao-ce/internal/model/rest" "github.com/rocboss/paopao-ce/pkg/convert" "github.com/rocboss/paopao-ce/pkg/errcode" "github.com/rocboss/paopao-ce/pkg/util" @@ -217,6 +218,38 @@ func Register(username, password string) (*model.User, error) { return user, nil } +func RequestingFriend(user *model.User, param *rest.RequestingFriendReq) error { + if _, err := ds.GetUserByID(param.UserId); err != nil { + return errcode.NotExistFriendId + } + return ds.RequestingFriend(user.ID, param.UserId, param.Greetings) +} + +func AddFriend(user *model.User, param *rest.AddFriendReq) error { + if _, err := ds.GetUserByID(param.UserId); err != nil { + return errcode.NotExistFriendId + } + return ds.AddFriend(user.ID, param.UserId) +} + +func RejectFriend(user *model.User, param *rest.RejectFriendReq) error { + if _, err := ds.GetUserByID(param.UserId); err != nil { + return errcode.NotExistFriendId + } + return ds.RejectFriend(user.ID, param.UserId) +} + +func DeleteFriend(user *model.User, param *rest.DeleteFriendReq) error { + if _, err := ds.GetUserByID(param.UserId); err != nil { + return errcode.NotExistFriendId + } + return ds.DeleteFriend(user.ID, param.UserId) +} + +func GetContacts(user *model.User, offset int, limit int) (*rest.ContactsResp, error) { + return ds.GetContacts(user.ID, offset, limit) +} + // GetUserInfo 获取用户信息 func GetUserInfo(param *AuthRequest) (*model.User, error) { user, err := ds.GetUserByUsername(param.Username) @@ -246,18 +279,31 @@ func GetUserByID(id int64) (*model.User, error) { return nil, errcode.NoExistUsername } -func GetUserByUsername(username string) (*model.User, error) { - user, err := ds.GetUserByUsername(username) - +func GetUserByUsername(user *model.User, username string) (*rest.UserProfileResp, error) { + other, err := ds.GetUserByUsername(username) if err != nil { return nil, err } - if user.Model != nil && user.ID > 0 { - return user, nil + var resp *rest.UserProfileResp + if other.Model != nil && other.ID > 0 { + resp = &rest.UserProfileResp{ + ID: other.ID, + Nickname: other.Nickname, + Username: other.Username, + Status: other.Status, + Avatar: other.Avatar, + IsAdmin: other.IsAdmin, + IsFriend: !(user == nil || user.ID == other.ID), + } + } else { + return nil, errcode.NoExistUsername } - return nil, errcode.NoExistUsername + if user != nil && user.ID != other.ID { + resp.IsFriend = ds.IsFriend(user.ID, other.ID) + } + return resp, nil } // UpdateUserInfo 更新用户信息 @@ -285,7 +331,8 @@ func ChangeUserAvatar(user *model.User, avatar string) (err *errcode.Error) { } user.Avatar = avatar - return UpdateUserInfo(user) + err = UpdateUserInfo(user) + return } // GetUserCollections 获取用户收藏列表 diff --git a/pkg/errcode/module_code.go b/pkg/errcode/module_code.go index ea3b5e25..1c5b7215 100644 --- a/pkg/errcode/module_code.go +++ b/pkg/errcode/module_code.go @@ -57,4 +57,13 @@ var ( RechargeReqFail = NewError(70001, "充值请求失败") RechargeNotifyError = NewError(70002, "充值回调失败") GetRechargeFailed = NewError(70003, "充值详情获取失败") + + NoRequestingFriendToSelf = NewError(80001, "不允许添加自己为好友") + NotExistFriendId = NewError(80002, "好友id不存在") + SendRequestingFriendFailed = NewError(80003, "申请添加朋友请求发送失败") + AddFriendFailed = NewError(80004, "添加好友失败") + RejectFriendFailed = NewError(80005, "拒绝好友失败") + DeleteFriendFailed = NewError(80006, "删除好友失败") + GetContactsFailed = NewError(80007, "获取联系人列表失败") + NoActionToSelf = NewError(80008, "不允许对自己操作") )