|
|
|
@@ -20,12 +20,12 @@ const (
|
|
|
|
|
groupSettingsSelectColumns = `tenant_id,topic,owner_user_id,name,avatar_file_id,description,group_status,join_approval,mute_all,
|
|
|
|
|
member_upload_files,member_private_chat,member_invite,max_member_count,max_admin_count,member_count,version,
|
|
|
|
|
created_by,dissolved_by,dissolved_at,created_at,updated_at`
|
|
|
|
|
groupAnnouncementSelectColumnsBase = `id,announcement_id,topic,author_user_id,content,status,version,published_at,idempotency_key,created_at,updated_at,
|
|
|
|
|
(SELECT COUNT(*) FROM im_group_announcement_reads AS ar WHERE ar.topic=im_group_announcements.topic AND ar.announcement_id=im_group_announcements.announcement_id) AS read_count,
|
|
|
|
|
(SELECT COUNT(*) FROM im_group_members AS gm WHERE gm.topic=im_group_announcements.topic AND gm.member_status='active') AS member_count`
|
|
|
|
|
groupAnnouncementSelectColumnsBase = `tenant_id,id,announcement_id,topic,author_user_id,content,status,version,published_at,idempotency_key,created_at,updated_at,
|
|
|
|
|
(SELECT COUNT(*) FROM im_group_announcement_reads AS ar WHERE ar.tenant_id=im_group_announcements.tenant_id AND ar.topic=im_group_announcements.topic AND ar.announcement_id=im_group_announcements.announcement_id) AS read_count,
|
|
|
|
|
(SELECT COUNT(*) FROM im_group_members AS gm WHERE gm.tenant_id=im_group_announcements.tenant_id AND gm.topic=im_group_announcements.topic AND gm.member_status='active') AS member_count`
|
|
|
|
|
groupMemberSelectColumns = `id,topic,user_id,role,member_status,nickname,pinned,mute_notifications,
|
|
|
|
|
join_source,joined_by,joined_at,left_at,removed_by,version,created_at,updated_at`
|
|
|
|
|
groupMuteSelectColumns = `topic,user_id,muted_by,muted_at,expires_at,reason,status,revoked_by,revoked_at,version,created_at,updated_at`
|
|
|
|
|
groupMuteSelectColumns = `tenant_id,topic,user_id,muted_by,muted_at,expires_at,reason,status,revoked_by,revoked_at,version,created_at,updated_at`
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func groupAnnouncementSelectColumns(tenantID t.TenantID) string {
|
|
|
|
@@ -488,13 +488,13 @@ func (a *adapter) TenantGroupInvitationCreate(id t.TenantID, inv *t.GroupInvitat
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
if strings.TrimSpace(inv.JoinRequestID) != "" {
|
|
|
|
|
existing, err := a.groupInvitationByQuery(ctx, `SELECT inv.id,inv.invitation_id,inv.topic,inv.inviter_user_id,inv.target_user_id,inv.message,inv.status,inv.result,
|
|
|
|
|
existing, err := a.groupInvitationByQuery(ctx, `SELECT inv.tenant_id,inv.id,inv.invitation_id,inv.topic,inv.inviter_user_id,inv.target_user_id,inv.message,inv.status,inv.result,
|
|
|
|
|
inv.join_request_id,inv.idempotency_key,inv.expires_at,inv.handled_at,inv.created_at,inv.updated_at
|
|
|
|
|
FROM im_group_invitations AS inv
|
|
|
|
|
INNER JOIN im_group_join_requests AS req ON req.request_id=inv.join_request_id
|
|
|
|
|
WHERE inv.topic=? AND inv.target_user_id=? AND inv.status=? AND req.status=?
|
|
|
|
|
INNER JOIN im_group_join_requests AS req ON req.tenant_id=inv.tenant_id AND req.request_id=inv.join_request_id
|
|
|
|
|
WHERE inv.tenant_id=? AND inv.topic=? AND inv.target_user_id=? AND inv.status=? AND req.status=?
|
|
|
|
|
ORDER BY req.id DESC LIMIT 1`,
|
|
|
|
|
inv.Topic, decodeUID(inv.TargetUserID), t.GroupInvitationStatusPending, t.GroupJoinRequestStatusPending)
|
|
|
|
|
id, inv.Topic, decodeUID(inv.TargetUserID), t.GroupInvitationStatusPending, t.GroupJoinRequestStatusPending)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return existing, nil
|
|
|
|
|
}
|
|
|
|
@@ -507,16 +507,16 @@ func (a *adapter) TenantGroupInvitationCreate(id t.TenantID, inv *t.GroupInvitat
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_invitations(invitation_id,topic,inviter_user_id,target_user_id,message,status,result,join_request_id,idempotency_key,expires_at,handled_at,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
|
|
|
inv.InvitationID, inv.Topic, decodeUID(inv.InviterUserID), decodeUID(inv.TargetUserID), inv.Message, inv.Status, inv.Result, inv.JoinRequestID, inv.IdempotencyKey, inv.ExpiresAt, inv.HandledAt, now, now)
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_invitations(tenant_id,invitation_id,topic,inviter_user_id,target_user_id,message,status,result,join_request_id,idempotency_key,expires_at,handled_at,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
|
|
|
id, inv.InvitationID, inv.Topic, decodeUID(inv.InviterUserID), decodeUID(inv.TargetUserID), inv.Message, inv.Status, inv.Result, inv.JoinRequestID, inv.IdempotencyKey, inv.ExpiresAt, inv.HandledAt, now, now)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if strings.TrimSpace(inv.JoinRequestID) != "" {
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_join_requests(request_id,topic,applicant_user_id,inviter_user_id,source,qr_token_hash,message,status,reject_reason,expires_at,idempotency_key,version,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
|
|
|
inv.JoinRequestID, inv.Topic, decodeUID(inv.TargetUserID), nullableUID(inv.InviterUserID), "invitation", "", inv.Message, t.GroupJoinRequestStatusPending, "", inv.ExpiresAt, inv.IdempotencyKey, 1, now, now)
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_join_requests(tenant_id,request_id,topic,applicant_user_id,inviter_user_id,source,qr_token_hash,message,status,reject_reason,expires_at,idempotency_key,version,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
|
|
|
id, inv.JoinRequestID, inv.Topic, decodeUID(inv.TargetUserID), nullableUID(inv.InviterUserID), "invitation", "", inv.Message, t.GroupJoinRequestStatusPending, "", inv.ExpiresAt, inv.IdempotencyKey, 1, now, now)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
@@ -548,10 +548,10 @@ func (a *adapter) TenantGroupInvitationAccept(id t.TenantID, invitationID string
|
|
|
|
|
|
|
|
|
|
var inv t.GroupInvitation
|
|
|
|
|
var handledAt sql.NullTime
|
|
|
|
|
err = tx.QueryRowxContext(ctx, `SELECT id,invitation_id,topic,inviter_user_id,target_user_id,message,status,result,
|
|
|
|
|
err = tx.QueryRowxContext(ctx, `SELECT tenant_id,id,invitation_id,topic,inviter_user_id,target_user_id,message,status,result,
|
|
|
|
|
join_request_id,idempotency_key,expires_at,handled_at,created_at,updated_at
|
|
|
|
|
FROM im_group_invitations WHERE invitation_id=? FOR UPDATE`, strings.TrimSpace(invitationID)).
|
|
|
|
|
Scan(&inv.ID, &inv.InvitationID, &inv.Topic, &inv.InviterUserRaw, &inv.TargetUserRaw, &inv.Message, &inv.Status,
|
|
|
|
|
FROM im_group_invitations WHERE tenant_id=? AND invitation_id=? FOR UPDATE`, id, strings.TrimSpace(invitationID)).
|
|
|
|
|
Scan(&inv.TenantID, &inv.ID, &inv.InvitationID, &inv.Topic, &inv.InviterUserRaw, &inv.TargetUserRaw, &inv.Message, &inv.Status,
|
|
|
|
|
&inv.Result, &inv.JoinRequestID, &inv.IdempotencyKey, &inv.ExpiresAt, &handledAt, &inv.CreatedAt, &inv.UpdatedAt)
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, t.ErrNotFound
|
|
|
|
@@ -606,15 +606,15 @@ func (a *adapter) TenantGroupInvitationAccept(id t.TenantID, invitationID string
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if _, err = tx.ExecContext(ctx, `UPDATE im_group_invitations
|
|
|
|
|
SET status=?,result=?,handled_at=?,updated_at=? WHERE invitation_id=?`,
|
|
|
|
|
t.GroupInvitationStatusJoined, "joined", now, now, inv.InvitationID); err != nil {
|
|
|
|
|
SET status=?,result=?,handled_at=?,updated_at=? WHERE tenant_id=? AND invitation_id=?`,
|
|
|
|
|
t.GroupInvitationStatusJoined, "joined", now, now, id, inv.InvitationID); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if strings.TrimSpace(inv.JoinRequestID) != "" {
|
|
|
|
|
if _, err = tx.ExecContext(ctx, `UPDATE im_group_join_requests
|
|
|
|
|
SET status=?,handled_by=?,handled_at=?,updated_at=?,version=version+1
|
|
|
|
|
WHERE topic=? AND request_id=? AND status=?`,
|
|
|
|
|
t.GroupJoinRequestStatusAccepted, decodeUID(uid), now, now, inv.Topic, inv.JoinRequestID, t.GroupJoinRequestStatusPending); err != nil {
|
|
|
|
|
WHERE tenant_id=? AND topic=? AND request_id=? AND status=?`,
|
|
|
|
|
t.GroupJoinRequestStatusAccepted, decodeUID(uid), now, now, id, inv.Topic, inv.JoinRequestID, t.GroupJoinRequestStatusPending); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -651,8 +651,8 @@ func (a *adapter) TenantGroupJoinRequestCreate(id t.TenantID, req *t.GroupJoinRe
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
existing, err := a.groupJoinRequestByQuery(ctx, "SELECT * FROM im_group_join_requests WHERE topic=? AND applicant_user_id=? AND status=? ORDER BY id DESC LIMIT 1",
|
|
|
|
|
req.Topic, decodeUID(req.ApplicantUserID), t.GroupJoinRequestStatusPending)
|
|
|
|
|
existing, err := a.groupJoinRequestByQuery(ctx, "SELECT * FROM im_group_join_requests WHERE tenant_id=? AND topic=? AND applicant_user_id=? AND status=? ORDER BY id DESC LIMIT 1",
|
|
|
|
|
id, req.Topic, decodeUID(req.ApplicantUserID), t.GroupJoinRequestStatusPending)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return existing, nil
|
|
|
|
|
}
|
|
|
|
@@ -664,16 +664,16 @@ func (a *adapter) TenantGroupJoinRequestCreate(id t.TenantID, req *t.GroupJoinRe
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_join_requests(request_id,topic,applicant_user_id,inviter_user_id,source,qr_token_hash,message,status,reject_reason,expires_at,idempotency_key,version,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
|
|
|
req.RequestID, req.Topic, decodeUID(req.ApplicantUserID), nullableUID(req.InviterUserID), req.Source, req.QRTokenHash, req.Message, req.Status, req.RejectReason, req.ExpiresAt, req.IdempotencyKey, req.Version, now, now)
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_join_requests(tenant_id,request_id,topic,applicant_user_id,inviter_user_id,source,qr_token_hash,message,status,reject_reason,expires_at,idempotency_key,version,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
|
|
|
id, req.RequestID, req.Topic, decodeUID(req.ApplicantUserID), nullableUID(req.InviterUserID), req.Source, req.QRTokenHash, req.Message, req.Status, req.RejectReason, req.ExpiresAt, req.IdempotencyKey, req.Version, now, now)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return a.groupJoinRequestByQuery(ctx, "SELECT * FROM im_group_join_requests WHERE request_id=?", req.RequestID)
|
|
|
|
|
return a.groupJoinRequestByQuery(ctx, "SELECT * FROM im_group_join_requests WHERE tenant_id=? AND request_id=?", id, req.RequestID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupJoinRequestList(id t.TenantID, topic, status, cursor string, limit int) ([]t.GroupJoinRequest, string, error) {
|
|
|
|
@@ -682,15 +682,15 @@ func (a *adapter) TenantGroupJoinRequestList(id t.TenantID, topic, status, curso
|
|
|
|
|
} else if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupJoinRequestList(id, topic, status, cursor, limit)
|
|
|
|
|
}
|
|
|
|
|
where := []string{"r.topic=?"}
|
|
|
|
|
args := []any{topic}
|
|
|
|
|
where := []string{"r.tenant_id=?", "r.topic=?"}
|
|
|
|
|
args := []any{id, topic}
|
|
|
|
|
if status != "" && status != "all" {
|
|
|
|
|
where = append(where, "r.status=?")
|
|
|
|
|
args = append(args, status)
|
|
|
|
|
if status == t.GroupJoinRequestStatusPending {
|
|
|
|
|
where = append(where, `NOT EXISTS (
|
|
|
|
|
SELECT 1 FROM im_group_join_requests AS newer
|
|
|
|
|
WHERE newer.topic=r.topic AND newer.applicant_user_id=r.applicant_user_id
|
|
|
|
|
WHERE newer.tenant_id=r.tenant_id AND newer.topic=r.topic AND newer.applicant_user_id=r.applicant_user_id
|
|
|
|
|
AND newer.status=? AND newer.id>r.id
|
|
|
|
|
)`)
|
|
|
|
|
args = append(args, status)
|
|
|
|
@@ -751,7 +751,7 @@ func (a *adapter) TenantGroupJoinRequestResolve(id t.TenantID, topic, requestID
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
var current t.GroupJoinRequest
|
|
|
|
|
err = tx.QueryRowxContext(ctx, "SELECT * FROM im_group_join_requests WHERE topic=? AND request_id=? FOR UPDATE", topic, requestID).StructScan(¤t)
|
|
|
|
|
err = tx.QueryRowxContext(ctx, "SELECT * FROM im_group_join_requests WHERE tenant_id=? AND topic=? AND request_id=? FOR UPDATE", id, topic, requestID).StructScan(¤t)
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, t.ErrNotFound
|
|
|
|
|
}
|
|
|
|
@@ -780,7 +780,7 @@ func (a *adapter) TenantGroupJoinRequestResolve(id t.TenantID, topic, requestID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if _, err = tx.ExecContext(ctx, `UPDATE im_group_join_requests SET status=?,reject_reason=?,handled_by=?,handled_at=?,updated_at=?,version=version+1
|
|
|
|
|
WHERE topic=? AND request_id=? AND status=?`, status, rejectReason, decodeUID(actor), now, now, topic, requestID, t.GroupJoinRequestStatusPending); err != nil {
|
|
|
|
|
WHERE tenant_id=? AND topic=? AND request_id=? AND status=?`, status, rejectReason, decodeUID(actor), now, now, id, topic, requestID, t.GroupJoinRequestStatusPending); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if action == "accept" {
|
|
|
|
@@ -794,7 +794,7 @@ func (a *adapter) TenantGroupJoinRequestResolve(id t.TenantID, topic, requestID
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return a.groupJoinRequestByQuery(ctx, "SELECT * FROM im_group_join_requests WHERE topic=? AND request_id=?", topic, requestID)
|
|
|
|
|
return a.groupJoinRequestByQuery(ctx, "SELECT * FROM im_group_join_requests WHERE tenant_id=? AND topic=? AND request_id=?", id, topic, requestID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupAnnouncementLatest(id t.TenantID, topic string) (*t.GroupAnnouncement, error) {
|
|
|
|
@@ -807,7 +807,7 @@ func (a *adapter) TenantGroupAnnouncementLatest(id t.TenantID, topic string) (*t
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
return a.groupAnnouncementByQuery(ctx, "SELECT "+groupAnnouncementSelectColumns(id)+" FROM im_group_announcements WHERE topic=? AND status=? ORDER BY version DESC,id DESC LIMIT 1", topic, t.GroupAnnouncementStatusCurrent)
|
|
|
|
|
return a.groupAnnouncementByQuery(ctx, "SELECT "+groupAnnouncementSelectColumns(id)+" FROM im_group_announcements WHERE tenant_id=? AND topic=? AND status=? ORDER BY version DESC,id DESC LIMIT 1", id, topic, t.GroupAnnouncementStatusCurrent)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupAnnouncementGet(id t.TenantID, topic, announcementID string) (*t.GroupAnnouncement, error) {
|
|
|
|
@@ -820,7 +820,7 @@ func (a *adapter) TenantGroupAnnouncementGet(id t.TenantID, topic, announcementI
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
return a.groupAnnouncementByQuery(ctx, "SELECT "+groupAnnouncementSelectColumns(id)+" FROM im_group_announcements WHERE topic=? AND announcement_id=? LIMIT 1", topic, announcementID)
|
|
|
|
|
return a.groupAnnouncementByQuery(ctx, "SELECT "+groupAnnouncementSelectColumns(id)+" FROM im_group_announcements WHERE tenant_id=? AND topic=? AND announcement_id=? LIMIT 1", id, topic, announcementID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupAnnouncementPut(id t.TenantID, ann *t.GroupAnnouncement, expectedVersion int) (*t.GroupAnnouncement, error) {
|
|
|
|
@@ -843,7 +843,7 @@ func (a *adapter) TenantGroupAnnouncementPut(id t.TenantID, ann *t.GroupAnnounce
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
var currentVersion int
|
|
|
|
|
err = tx.QueryRowContext(ctx, "SELECT COALESCE(MAX(version),0) FROM im_group_announcements WHERE topic=?", ann.Topic).Scan(¤tVersion)
|
|
|
|
|
err = tx.QueryRowContext(ctx, "SELECT COALESCE(MAX(version),0) FROM im_group_announcements WHERE tenant_id=? AND topic=?", id, ann.Topic).Scan(¤tVersion)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
@@ -855,7 +855,7 @@ func (a *adapter) TenantGroupAnnouncementPut(id t.TenantID, ann *t.GroupAnnounce
|
|
|
|
|
if now.IsZero() {
|
|
|
|
|
now = time.Now().UTC().Round(time.Millisecond)
|
|
|
|
|
}
|
|
|
|
|
if _, err = tx.ExecContext(ctx, "UPDATE im_group_announcements SET status=?,updated_at=? WHERE topic=? AND status=?", t.GroupAnnouncementStatusSuperseded, now, ann.Topic, t.GroupAnnouncementStatusCurrent); err != nil {
|
|
|
|
|
if _, err = tx.ExecContext(ctx, "UPDATE im_group_announcements SET status=?,updated_at=? WHERE tenant_id=? AND topic=? AND status=?", t.GroupAnnouncementStatusSuperseded, now, id, ann.Topic, t.GroupAnnouncementStatusCurrent); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if ann.AnnouncementID == "" {
|
|
|
|
@@ -864,8 +864,8 @@ func (a *adapter) TenantGroupAnnouncementPut(id t.TenantID, ann *t.GroupAnnounce
|
|
|
|
|
if ann.IdempotencyKey == "" {
|
|
|
|
|
ann.IdempotencyKey = ann.AnnouncementID
|
|
|
|
|
}
|
|
|
|
|
if _, err = tx.ExecContext(ctx, `INSERT INTO im_group_announcements(announcement_id,topic,author_user_id,content,status,version,published_at,idempotency_key,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?)`, ann.AnnouncementID, ann.Topic, decodeUID(ann.AuthorUserID), ann.Content, t.GroupAnnouncementStatusCurrent, version, now, ann.IdempotencyKey, now, now); err != nil {
|
|
|
|
|
if _, err = tx.ExecContext(ctx, `INSERT INTO im_group_announcements(tenant_id,announcement_id,topic,author_user_id,content,status,version,published_at,idempotency_key,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?)`, id, ann.AnnouncementID, ann.Topic, decodeUID(ann.AuthorUserID), ann.Content, t.GroupAnnouncementStatusCurrent, version, now, ann.IdempotencyKey, now, now); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
@@ -888,8 +888,8 @@ func (a *adapter) TenantGroupAnnouncementRead(id t.TenantID, topic, announcement
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, err = a.db.ExecContext(ctx, `INSERT INTO im_group_announcement_reads(id,announcement_id,topic,user_id,read_at)
|
|
|
|
|
VALUES(?,?,?,?,?) ON DUPLICATE KEY UPDATE read_at=VALUES(read_at),updated_at=CURRENT_TIMESTAMP(3)`, readID, announcementID, topic, decodeUID(uid), readAt)
|
|
|
|
|
_, err = a.db.ExecContext(ctx, `INSERT INTO im_group_announcement_reads(id,tenant_id,announcement_id,topic,user_id,read_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?) ON DUPLICATE KEY UPDATE read_at=VALUES(read_at),updated_at=CURRENT_TIMESTAMP(3)`, readID, id, announcementID, topic, decodeUID(uid), readAt)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -899,8 +899,8 @@ func (a *adapter) TenantGroupMuteList(id t.TenantID, topic, cursor string, limit
|
|
|
|
|
} else if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMuteList(id, topic, cursor, limit)
|
|
|
|
|
}
|
|
|
|
|
where := []string{"topic=?", "status=?"}
|
|
|
|
|
args := []any{topic, t.GroupMuteStatusActive}
|
|
|
|
|
where := []string{"tenant_id=?", "topic=?", "status=?"}
|
|
|
|
|
args := []any{id, topic, t.GroupMuteStatusActive}
|
|
|
|
|
if cursor != "" {
|
|
|
|
|
parsed, err := strconv.ParseInt(cursor, 10, 64)
|
|
|
|
|
if err != nil || parsed <= 0 {
|
|
|
|
@@ -950,8 +950,8 @@ func (a *adapter) TenantGroupMuteGet(id t.TenantID, topic string, uid t.Uid) (*t
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
return a.groupMuteByQuery(ctx, "SELECT "+groupMuteSelectColumns+" FROM im_group_mutes WHERE topic=? AND user_id=? AND status=? AND (expires_at IS NULL OR expires_at>?)",
|
|
|
|
|
topic, decodeUID(uid), t.GroupMuteStatusActive, now)
|
|
|
|
|
return a.groupMuteByQuery(ctx, "SELECT "+groupMuteSelectColumns+" FROM im_group_mutes WHERE tenant_id=? AND topic=? AND user_id=? AND status=? AND (expires_at IS NULL OR expires_at>?)",
|
|
|
|
|
id, topic, decodeUID(uid), t.GroupMuteStatusActive, now)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupMuteUpsert(id t.TenantID, mute *t.GroupMute) (*t.GroupMute, error) {
|
|
|
|
@@ -980,17 +980,17 @@ func (a *adapter) TenantGroupMuteUpsert(id t.TenantID, mute *t.GroupMute) (*t.Gr
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_mutes(id,topic,user_id,muted_by,muted_at,expires_at,reason,status,version)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,1)
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_mutes(id,tenant_id,topic,user_id,muted_by,muted_at,expires_at,reason,status,version)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,1)
|
|
|
|
|
ON DUPLICATE KEY UPDATE muted_by=VALUES(muted_by),muted_at=VALUES(muted_at),expires_at=VALUES(expires_at),reason=VALUES(reason),status=VALUES(status),revoked_by=NULL,revoked_at=NULL,version=version+1,updated_at=CURRENT_TIMESTAMP(3)`,
|
|
|
|
|
muteID, mute.Topic, decodeUID(mute.UserID), decodeUID(mute.MutedBy), now, mute.ExpiresAt, mute.Reason, mute.Status)
|
|
|
|
|
muteID, id, mute.Topic, decodeUID(mute.UserID), decodeUID(mute.MutedBy), now, mute.ExpiresAt, mute.Reason, mute.Status)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return a.groupMuteByQuery(ctx, "SELECT "+groupMuteSelectColumns+" FROM im_group_mutes WHERE topic=? AND user_id=?", mute.Topic, decodeUID(mute.UserID))
|
|
|
|
|
return a.groupMuteByQuery(ctx, "SELECT "+groupMuteSelectColumns+" FROM im_group_mutes WHERE tenant_id=? AND topic=? AND user_id=?", id, mute.Topic, decodeUID(mute.UserID))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupMuteDelete(id t.TenantID, topic string, uid, actor t.Uid, now time.Time) error {
|
|
|
|
@@ -1008,8 +1008,8 @@ func (a *adapter) TenantGroupMuteDelete(id t.TenantID, topic string, uid, actor
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
res, err := tx.ExecContext(ctx, "UPDATE im_group_mutes SET status=?,revoked_by=?,revoked_at=?,version=version+1,updated_at=? WHERE topic=? AND user_id=? AND status=?",
|
|
|
|
|
t.GroupMuteStatusRevoked, decodeUID(actor), now, now, topic, decodeUID(uid), t.GroupMuteStatusActive)
|
|
|
|
|
res, err := tx.ExecContext(ctx, "UPDATE im_group_mutes SET status=?,revoked_by=?,revoked_at=?,version=version+1,updated_at=? WHERE tenant_id=? AND topic=? AND user_id=? AND status=?",
|
|
|
|
|
t.GroupMuteStatusRevoked, decodeUID(actor), now, now, id, topic, decodeUID(uid), t.GroupMuteStatusActive)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
@@ -1028,8 +1028,8 @@ func (a *adapter) TenantGroupFileList(id t.TenantID, topic, fileType, query, cur
|
|
|
|
|
if strings.TrimSpace(topic) == "" {
|
|
|
|
|
return nil, "", t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
where := []string{"topic=?", "status='active'"}
|
|
|
|
|
args := []any{strings.TrimSpace(topic)}
|
|
|
|
|
where := []string{"tenant_id=?", "topic=?", "status='active'"}
|
|
|
|
|
args := []any{id, strings.TrimSpace(topic)}
|
|
|
|
|
if fileType != "" {
|
|
|
|
|
where = append(where, "file_type=?")
|
|
|
|
|
args = append(args, strings.TrimSpace(fileType))
|
|
|
|
@@ -1051,7 +1051,7 @@ func (a *adapter) TenantGroupFileList(id t.TenantID, topic, fileType, query, cur
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
rows, err := a.db.QueryxContext(ctx, `SELECT id,group_file_id,topic,file_id,source_topic,source_seq,uploader_id,
|
|
|
|
|
rows, err := a.db.QueryxContext(ctx, `SELECT tenant_id,id,group_file_id,topic,file_id,source_topic,source_seq,uploader_id,
|
|
|
|
|
file_name,mime_type,file_type,file_size,status,deleted_by,deleted_at,idempotency_key,created_at,updated_at
|
|
|
|
|
FROM im_group_files WHERE `+strings.Join(where, " AND ")+` ORDER BY id DESC LIMIT ?`, args...)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -1108,15 +1108,15 @@ func (a *adapter) TenantGroupFileCreate(id t.TenantID, file *t.GroupFile) (*t.Gr
|
|
|
|
|
}
|
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_group_files
|
|
|
|
|
(group_file_id,topic,file_id,source_topic,source_seq,uploader_id,file_name,mime_type,file_type,file_size,status,idempotency_key,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
|
|
|
(tenant_id,group_file_id,topic,file_id,source_topic,source_seq,uploader_id,file_name,mime_type,file_type,file_size,status,idempotency_key,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
|
|
|
ON DUPLICATE KEY UPDATE
|
|
|
|
|
file_name=IF(file_name='', VALUES(file_name), file_name),
|
|
|
|
|
mime_type=IF(mime_type='', VALUES(mime_type), mime_type),
|
|
|
|
|
file_size=IF(file_size=0, VALUES(file_size), file_size),
|
|
|
|
|
file_type=IF(file_type='' OR file_type='file', VALUES(file_type), file_type),
|
|
|
|
|
updated_at=updated_at`,
|
|
|
|
|
file.GroupFileID, file.Topic, file.FileID, file.SourceTopic, file.SourceSeq, decodeUID(file.UploaderID),
|
|
|
|
|
id, file.GroupFileID, file.Topic, file.FileID, file.SourceTopic, file.SourceSeq, decodeUID(file.UploaderID),
|
|
|
|
|
file.FileName, file.MIMEType, file.FileType, file.FileSize, file.Status, file.IdempotencyKey, now, now)
|
|
|
|
|
if err != nil && !isDupe(err) {
|
|
|
|
|
return nil, err
|
|
|
|
@@ -1127,15 +1127,15 @@ func (a *adapter) TenantGroupFileCreate(id t.TenantID, file *t.GroupFile) (*t.Gr
|
|
|
|
|
}
|
|
|
|
|
if fid := t.ParseUid(uploadID); !fid.IsZero() {
|
|
|
|
|
if a.tenantScopedDB {
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_filemsglinks(createdat,fileid,topic)
|
|
|
|
|
SELECT ?,?,? WHERE NOT EXISTS (
|
|
|
|
|
SELECT 1 FROM im_filemsglinks WHERE fileid=? AND topic=?)`,
|
|
|
|
|
now, decodeUID(fid), file.Topic, decodeUID(fid), file.Topic)
|
|
|
|
|
} else {
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_filemsglinks(tenant_id,createdat,fileid,topic)
|
|
|
|
|
SELECT ?,?,?,? WHERE NOT EXISTS (
|
|
|
|
|
SELECT 1 FROM im_filemsglinks WHERE tenant_id=? AND fileid=? AND topic=?)`,
|
|
|
|
|
id, now, decodeUID(fid), file.Topic, id, decodeUID(fid), file.Topic)
|
|
|
|
|
} else {
|
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO im_filemsglinks(createdat,fileid,topic)
|
|
|
|
|
SELECT ?,?,? WHERE NOT EXISTS (
|
|
|
|
|
SELECT 1 FROM im_filemsglinks WHERE fileid=? AND topic=?)`,
|
|
|
|
|
now, decodeUID(fid), file.Topic, decodeUID(fid), file.Topic)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
@@ -1144,7 +1144,7 @@ func (a *adapter) TenantGroupFileCreate(id t.TenantID, file *t.GroupFile) (*t.Gr
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
created, err := a.groupFileByQuery(ctx, "SELECT * FROM im_group_files WHERE topic=? AND file_id=?", file.Topic, file.FileID)
|
|
|
|
|
created, err := a.groupFileByQuery(ctx, "SELECT * FROM im_group_files WHERE tenant_id=? AND topic=? AND file_id=?", id, file.Topic, file.FileID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
@@ -1200,8 +1200,8 @@ func (a *adapter) TenantGroupFileDelete(id t.TenantID, topic, groupFileID string
|
|
|
|
|
}
|
|
|
|
|
query := `UPDATE im_group_files
|
|
|
|
|
SET status='deleted',deleted_by=?,deleted_at=?,updated_at=?
|
|
|
|
|
WHERE topic=? AND group_file_id=? AND status='active'`
|
|
|
|
|
args := []any{decodeUID(actor), now, now, strings.TrimSpace(topic), strings.TrimSpace(groupFileID)}
|
|
|
|
|
WHERE tenant_id=? AND topic=? AND group_file_id=? AND status='active'`
|
|
|
|
|
args := []any{decodeUID(actor), now, now, id, strings.TrimSpace(topic), strings.TrimSpace(groupFileID)}
|
|
|
|
|
if !canManage {
|
|
|
|
|
query += " AND uploader_id=?"
|
|
|
|
|
args = append(args, decodeUID(actor))
|
|
|
|
@@ -1213,7 +1213,7 @@ func (a *adapter) TenantGroupFileDelete(id t.TenantID, topic, groupFileID string
|
|
|
|
|
if affected, _ := res.RowsAffected(); affected == 0 {
|
|
|
|
|
return nil, t.ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return a.groupFileByQuery(ctx, "SELECT * FROM im_group_files WHERE topic=? AND group_file_id=?", strings.TrimSpace(topic), strings.TrimSpace(groupFileID))
|
|
|
|
|
return a.groupFileByQuery(ctx, "SELECT * FROM im_group_files WHERE tenant_id=? AND topic=? AND group_file_id=?", id, strings.TrimSpace(topic), strings.TrimSpace(groupFileID))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupCreate(id t.TenantID, topic *t.Topic, owner t.Uid, ownerPrivate any,
|
|
|
|
@@ -1319,8 +1319,8 @@ func (a *adapter) TenantGroupInviteMember(id t.TenantID, topic string, inviter,
|
|
|
|
|
idempotencyKey += ":" + target.UserId()
|
|
|
|
|
}
|
|
|
|
|
if _, err = tx.ExecContext(ctx, `INSERT INTO im_group_invitations
|
|
|
|
|
(invitation_id,topic,inviter_user_id,target_user_id,message,status,result,idempotency_key,expires_at,handled_at,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?)`, invitationID, topic, decodeUID(inviter), decodeUID(target), message,
|
|
|
|
|
(tenant_id,invitation_id,topic,inviter_user_id,target_user_id,message,status,result,idempotency_key,expires_at,handled_at,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)`, id, invitationID, topic, decodeUID(inviter), decodeUID(target), message,
|
|
|
|
|
t.GroupInvitationStatusJoined, "joined", idempotencyKey, now.Add(7*24*time.Hour), now, now, now); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
@@ -1490,8 +1490,8 @@ func (a *adapter) TenantGroupCanPost(id t.TenantID, topic string, uid t.Uid, adm
|
|
|
|
|
var muteAll, memberMuted bool
|
|
|
|
|
err := a.db.QueryRowContext(ctx, `SELECT
|
|
|
|
|
COALESCE((SELECT mute_all FROM im_group_settings WHERE tenant_id=? AND topic=? AND group_status=? LIMIT 1),0),
|
|
|
|
|
EXISTS(SELECT 1 FROM im_group_mutes WHERE topic=? AND user_id=? AND status=? AND (expires_at IS NULL OR expires_at>?))`,
|
|
|
|
|
id, topic, t.GroupStatusActive, topic, decodeUID(uid), t.GroupMuteStatusActive, now).Scan(&muteAll, &memberMuted)
|
|
|
|
|
EXISTS(SELECT 1 FROM im_group_mutes WHERE tenant_id=? AND topic=? AND user_id=? AND status=? AND (expires_at IS NULL OR expires_at>?))`,
|
|
|
|
|
id, topic, t.GroupStatusActive, id, topic, decodeUID(uid), t.GroupMuteStatusActive, now).Scan(&muteAll, &memberMuted)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
@@ -1549,7 +1549,7 @@ func syncGroupMuteAllTx(tx *sqlx.Tx, tenantID t.TenantID, topic string, muteAll
|
|
|
|
|
mode = t.ModeCPublic &^ t.ModeWrite
|
|
|
|
|
}
|
|
|
|
|
_, err := tx.Exec(tenantShardSQL(tenantID, `UPDATE im_subscriptions AS s
|
|
|
|
|
JOIN im_group_members AS gm ON gm.topic=s.topic AND gm.user_id=s.userid
|
|
|
|
|
JOIN im_group_members AS gm ON gm.tenant_id=s.tenant_id AND gm.topic=s.topic AND gm.user_id=s.userid
|
|
|
|
|
SET s.modewant=?,s.modegiven=?,s.updatedat=?,
|
|
|
|
|
gm.version=gm.version+1,gm.updated_at=?
|
|
|
|
|
WHERE s.topic=? AND s.deletedat IS NULL
|
|
|
|
@@ -1611,7 +1611,7 @@ func (a *adapter) groupJoinRequestByQuery(ctx context.Context, query string, arg
|
|
|
|
|
func (a *adapter) groupInvitationByQuery(ctx context.Context, query string, args ...any) (*t.GroupInvitation, error) {
|
|
|
|
|
var item t.GroupInvitation
|
|
|
|
|
var handledAt sql.NullTime
|
|
|
|
|
err := a.db.QueryRowxContext(ctx, query, args...).Scan(&item.ID, &item.InvitationID, &item.Topic, &item.InviterUserRaw, &item.TargetUserRaw,
|
|
|
|
|
err := a.db.QueryRowxContext(ctx, query, args...).Scan(&item.TenantID, &item.ID, &item.InvitationID, &item.Topic, &item.InviterUserRaw, &item.TargetUserRaw,
|
|
|
|
|
&item.Message, &item.Status, &item.Result, &item.JoinRequestID, &item.IdempotencyKey, &item.ExpiresAt, &handledAt, &item.CreatedAt, &item.UpdatedAt)
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, t.ErrNotFound
|
|
|
|
|