195 lines
5.4 KiB
Go
195 lines
5.4 KiB
Go
package model
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
|
||
"spark-server/internal/module/group/entity"
|
||
"spark-server/internal/pkg/errs"
|
||
)
|
||
|
||
// Group maps table groups.
|
||
type Group struct {
|
||
ID int64 `gorm:"column:id;primaryKey;autoIncrement;comment:群号,表内自增,对外群 ID"`
|
||
ConversationID int64 `gorm:"column:conversation_id;uniqueIndex;comment:对应 conversations.id(Snowflake),发消息与同步用"`
|
||
Name string `gorm:"column:name;comment:群名称"`
|
||
Avatar string `gorm:"column:avatar;comment:群头像对象存储 key,空表示未设置"`
|
||
Announcement string `gorm:"column:announcement;comment:群公告,仅群主/管理员可改"`
|
||
OwnerID int64 `gorm:"column:owner_id;comment:群主 user_id"`
|
||
MutedAll int `gorm:"column:muted_all;comment:0=未全员禁言 1=全员禁言(群主/管理员仍可发言)"`
|
||
MemberCount int `gorm:"column:member_count;comment:当前成员数,含群主"`
|
||
Status int `gorm:"column:status;comment:1=正常 2=已解散"`
|
||
CreatedAt time.Time `gorm:"column:created_at;comment:创建时间"`
|
||
UpdatedAt time.Time `gorm:"column:updated_at;comment:资料或成员变更时间"`
|
||
}
|
||
|
||
func (Group) TableName() string { return "groups" }
|
||
|
||
func (r *Group) entity() *entity.Group {
|
||
if r == nil {
|
||
return nil
|
||
}
|
||
g := &entity.Group{
|
||
ID: r.ID, ConversationID: r.ConversationID, Name: r.Name, Avatar: r.Avatar, Announcement: r.Announcement, OwnerID: r.OwnerID,
|
||
MemberCount: r.MemberCount, Status: entity.Status(r.Status),
|
||
CreatedAtMS: r.CreatedAt.UnixMilli(), UpdatedAtMS: r.UpdatedAt.UnixMilli(),
|
||
}
|
||
g.MutedAll = r.MutedAll != 0
|
||
return g
|
||
}
|
||
|
||
func groupRow(g *entity.Group) *Group {
|
||
now := msTime(g.CreatedAtMS)
|
||
updated := msTime(g.UpdatedAtMS)
|
||
if g.UpdatedAtMS <= 0 {
|
||
updated = now
|
||
}
|
||
row := &Group{
|
||
ID: g.ID, ConversationID: g.ConversationID, Name: g.Name, Avatar: g.Avatar, Announcement: g.Announcement, OwnerID: g.OwnerID,
|
||
MemberCount: g.MemberCount, Status: int(g.Status),
|
||
CreatedAt: now, UpdatedAt: updated,
|
||
}
|
||
if g.MutedAll {
|
||
row.MutedAll = 1
|
||
}
|
||
if row.Status == 0 {
|
||
row.Status = int(entity.StatusActive)
|
||
}
|
||
return row
|
||
}
|
||
|
||
func (m *Repo) Get(ctx context.Context, id int64) (*entity.Group, error) {
|
||
if id <= 0 {
|
||
return nil, errs.ErrInvalidArgument
|
||
}
|
||
db, err := m.session(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var row Group
|
||
if err := db.Where("id = ?", id).Take(&row).Error; err != nil {
|
||
return nil, mapDBErr(err)
|
||
}
|
||
return row.entity(), nil
|
||
}
|
||
|
||
func (m *Repo) GetByConversation(ctx context.Context, conversationID int64) (*entity.Group, error) {
|
||
if conversationID <= 0 {
|
||
return nil, errs.ErrInvalidArgument
|
||
}
|
||
db, err := m.session(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var row Group
|
||
if err := db.Where("conversation_id = ?", conversationID).Take(&row).Error; err != nil {
|
||
return nil, mapDBErr(err)
|
||
}
|
||
return row.entity(), nil
|
||
}
|
||
|
||
func (m *Repo) ListByUser(ctx context.Context, userID int64, limit int) ([]*entity.Group, error) {
|
||
if userID <= 0 {
|
||
return nil, errs.ErrInvalidArgument
|
||
}
|
||
db, err := m.session(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
limit = entity.ClampLimit(limit)
|
||
var rows []Group
|
||
err = db.Table("`groups` AS g").Select("g.*").
|
||
Joins("INNER JOIN group_members m ON m.group_id = g.id").
|
||
Where("m.user_id = ? AND g.status = ?", userID, entity.StatusActive).
|
||
Order("g.updated_at DESC").Limit(limit).Find(&rows).Error
|
||
if err != nil {
|
||
return nil, mapDBErr(err)
|
||
}
|
||
out := make([]*entity.Group, 0, len(rows))
|
||
for i := range rows {
|
||
out = append(out, rows[i].entity())
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
const purgeListCap = 10000
|
||
|
||
func (m *Repo) ListAllActiveByUser(ctx context.Context, userID int64) ([]*entity.Group, error) {
|
||
if userID <= 0 {
|
||
return nil, errs.ErrInvalidArgument
|
||
}
|
||
db, err := m.session(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var rows []Group
|
||
err = db.Table("`groups` AS g").Select("g.*").
|
||
Joins("INNER JOIN group_members m ON m.group_id = g.id").
|
||
Where("m.user_id = ? AND g.status = ?", userID, entity.StatusActive).
|
||
Order("g.updated_at DESC").Limit(purgeListCap).Find(&rows).Error
|
||
if err != nil {
|
||
return nil, mapDBErr(err)
|
||
}
|
||
out := make([]*entity.Group, 0, len(rows))
|
||
for i := range rows {
|
||
out = append(out, rows[i].entity())
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
func (m *Repo) UpdateProfile(ctx context.Context, g *entity.Group) error {
|
||
if g == nil || g.ID <= 0 {
|
||
return errs.ErrInvalidArgument
|
||
}
|
||
db, err := m.session(ctx)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
res := db.Model(&Group{}).Where("id = ? AND status = ?", g.ID, entity.StatusActive).Updates(map[string]any{
|
||
"name": g.Name,
|
||
"avatar": g.Avatar,
|
||
"announcement": g.Announcement,
|
||
"muted_all": boolInt(g.MutedAll),
|
||
"updated_at": msTime(g.UpdatedAtMS),
|
||
})
|
||
if res.Error != nil {
|
||
return mapDBErr(res.Error)
|
||
}
|
||
if res.RowsAffected == 0 {
|
||
return errs.ErrNotFound
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (m *Repo) UpdateLastMsg(ctx context.Context, conversationID, msgID, atMS int64) error {
|
||
db, err := m.session(ctx)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
res := db.Table("conversations").Where("id = ?", conversationID).Updates(map[string]any{
|
||
"last_msg_id": msgID,
|
||
"last_msg_at": msTime(atMS),
|
||
})
|
||
if res.Error != nil {
|
||
return mapDBErr(res.Error)
|
||
}
|
||
if res.RowsAffected == 0 {
|
||
return errs.ErrNotFound
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func msTime(ms int64) time.Time {
|
||
if ms <= 0 {
|
||
return time.Now()
|
||
}
|
||
return time.UnixMilli(ms)
|
||
}
|
||
|
||
func boolInt(v bool) int {
|
||
if v {
|
||
return 1
|
||
}
|
||
return 0
|
||
}
|