feat: add im relationship flows
This commit is contained in:
@@ -2514,6 +2514,252 @@ func (a *adapter) TenantSystemNotificationMarkRead(id t.TenantID, uid t.Uid, eve
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *adapter) TenantFriendRequestCreate(id t.TenantID, req *t.FriendRequest) (*t.FriendRequest, error) {
|
||||
if err := validTenant(id); err != nil || req == nil || req.TenantID != id || req.ApplicantID.IsZero() || req.TargetID.IsZero() {
|
||||
return nil, t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
_, err := a.db.ExecContext(ctx,
|
||||
"INSERT INTO im_friend_requests(tenant_id,request_id,applicant_id,target_id,message,source,source_topic,status,idempotency_key,expires_at,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
id, req.RequestID, decodeUID(req.ApplicantID), decodeUID(req.TargetID), req.Message, req.Source, req.SourceTopic, req.Status, req.IdempotencyKey, req.ExpiresAt, req.CreatedAt, req.UpdatedAt)
|
||||
if err != nil {
|
||||
if isDupe(err) && req.IdempotencyKey != "" {
|
||||
return a.friendRequestByIdempotency(ctx, id, req.ApplicantID, req.IdempotencyKey)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return a.friendRequestByRequestID(ctx, id, req.RequestID)
|
||||
}
|
||||
|
||||
func (a *adapter) TenantFriendRequestLatestBetween(id t.TenantID, uid1, uid2 t.Uid) (*t.FriendRequest, error) {
|
||||
if err := validTenant(id); err != nil || uid1.IsZero() || uid2.IsZero() {
|
||||
return nil, t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
return a.friendRequestByQuery(ctx,
|
||||
"SELECT * FROM im_friend_requests WHERE tenant_id=? AND ((applicant_id=? AND target_id=?) OR (applicant_id=? AND target_id=?)) ORDER BY created_at DESC,id DESC LIMIT 1",
|
||||
id, decodeUID(uid1), decodeUID(uid2), decodeUID(uid2), decodeUID(uid1))
|
||||
}
|
||||
|
||||
func (a *adapter) TenantFriendRequestResolve(id t.TenantID, requestID string, actor t.Uid, status string, handledAt time.Time) (*t.FriendRequest, error) {
|
||||
if err := validTenant(id); err != nil || requestID == "" || actor.IsZero() {
|
||||
return nil, t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
req, err := a.friendRequestByRequestID(ctx, id, requestID)
|
||||
if err != nil || req == nil {
|
||||
return req, err
|
||||
}
|
||||
if req.TargetID != actor {
|
||||
return nil, t.ErrPermissionDenied
|
||||
}
|
||||
if req.Status != t.FriendRequestPending {
|
||||
return req, nil
|
||||
}
|
||||
if _, err = a.db.ExecContext(ctx,
|
||||
"UPDATE im_friend_requests SET status=?,handled_at=?,updated_at=? WHERE tenant_id=? AND request_id=? AND status=?",
|
||||
status, handledAt, handledAt, id, requestID, t.FriendRequestPending); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.friendRequestByRequestID(ctx, id, requestID)
|
||||
}
|
||||
|
||||
func (a *adapter) TenantFriendshipUpsertPair(id t.TenantID, uid1, uid2 t.Uid, topic string, now time.Time) error {
|
||||
if err := validTenant(id); err != nil || uid1.IsZero() || uid2.IsZero() || uid1 == uid2 {
|
||||
return t.ErrMalformed
|
||||
}
|
||||
if topic == "" {
|
||||
topic = uid1.P2PName(uid2)
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
for _, pair := range [][2]t.Uid{{uid1, uid2}, {uid2, uid1}} {
|
||||
if _, err := a.db.ExecContext(ctx,
|
||||
"INSERT INTO im_friendships(tenant_id,user_id,peer_id,status,topic,created_at,updated_at) VALUES(?,?,?,?,?,?,?) "+
|
||||
"ON DUPLICATE KEY UPDATE status=VALUES(status),topic=VALUES(topic),updated_at=VALUES(updated_at)",
|
||||
id, decodeUID(pair[0]), decodeUID(pair[1]), t.RelationshipFriend, topic, now, now); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *adapter) TenantFriendshipGet(id t.TenantID, userID, peerID t.Uid) (*t.Friendship, error) {
|
||||
if err := validTenant(id); err != nil || userID.IsZero() || peerID.IsZero() {
|
||||
return nil, t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
row := struct {
|
||||
TenantID t.TenantID `db:"tenant_id"`
|
||||
UserID int64 `db:"user_id"`
|
||||
PeerID int64 `db:"peer_id"`
|
||||
Status string
|
||||
Remark string
|
||||
Topic string
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}{}
|
||||
err := a.db.GetContext(ctx, &row, "SELECT * FROM im_friendships WHERE tenant_id=? AND user_id=? AND peer_id=?",
|
||||
id, decodeUID(userID), decodeUID(peerID))
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &t.Friendship{
|
||||
TenantID: row.TenantID,
|
||||
UserID: store.EncodeUid(row.UserID),
|
||||
PeerID: store.EncodeUid(row.PeerID),
|
||||
Status: row.Status,
|
||||
Remark: row.Remark,
|
||||
Topic: row.Topic,
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *adapter) TenantFriendshipDeletePair(id t.TenantID, uid1, uid2 t.Uid, now time.Time) error {
|
||||
if err := validTenant(id); err != nil || uid1.IsZero() || uid2.IsZero() {
|
||||
return t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
_, err := a.db.ExecContext(ctx,
|
||||
"UPDATE im_friendships SET status=?,updated_at=? WHERE tenant_id=? AND ((user_id=? AND peer_id=?) OR (user_id=? AND peer_id=?))",
|
||||
t.RelationshipNone, now, id, decodeUID(uid1), decodeUID(uid2), decodeUID(uid2), decodeUID(uid1))
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *adapter) TenantUserBlockGet(id t.TenantID, blockerID, blockedID t.Uid) (*t.UserBlock, error) {
|
||||
if err := validTenant(id); err != nil || blockerID.IsZero() || blockedID.IsZero() {
|
||||
return nil, t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
row := struct {
|
||||
TenantID t.TenantID `db:"tenant_id"`
|
||||
BlockerID int64 `db:"blocker_id"`
|
||||
BlockedID int64 `db:"blocked_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}{}
|
||||
err := a.db.GetContext(ctx, &row, "SELECT * FROM im_user_blocks WHERE tenant_id=? AND blocker_id=? AND blocked_id=?",
|
||||
id, decodeUID(blockerID), decodeUID(blockedID))
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &t.UserBlock{
|
||||
TenantID: row.TenantID,
|
||||
BlockerID: store.EncodeUid(row.BlockerID),
|
||||
BlockedID: store.EncodeUid(row.BlockedID),
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *adapter) TenantUserBlockUpsert(id t.TenantID, blockerID, blockedID t.Uid, now time.Time) (*t.UserBlock, error) {
|
||||
if err := validTenant(id); err != nil || blockerID.IsZero() || blockedID.IsZero() {
|
||||
return nil, t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
_, err := a.db.ExecContext(ctx,
|
||||
"INSERT INTO im_user_blocks(tenant_id,blocker_id,blocked_id,created_at,updated_at) VALUES(?,?,?,?,?) "+
|
||||
"ON DUPLICATE KEY UPDATE updated_at=VALUES(updated_at)",
|
||||
id, decodeUID(blockerID), decodeUID(blockedID), now, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.TenantUserBlockGet(id, blockerID, blockedID)
|
||||
}
|
||||
|
||||
func (a *adapter) TenantUserBlockDelete(id t.TenantID, blockerID, blockedID t.Uid) error {
|
||||
if err := validTenant(id); err != nil || blockerID.IsZero() || blockedID.IsZero() {
|
||||
return t.ErrMalformed
|
||||
}
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
_, err := a.db.ExecContext(ctx, "DELETE FROM im_user_blocks WHERE tenant_id=? AND blocker_id=? AND blocked_id=?",
|
||||
id, decodeUID(blockerID), decodeUID(blockedID))
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *adapter) friendRequestByRequestID(ctx context.Context, tenantID t.TenantID, requestID string) (*t.FriendRequest, error) {
|
||||
return a.friendRequestByQuery(ctx, "SELECT * FROM im_friend_requests WHERE tenant_id=? AND request_id=?", tenantID, requestID)
|
||||
}
|
||||
|
||||
func (a *adapter) friendRequestByIdempotency(ctx context.Context, tenantID t.TenantID, applicantID t.Uid, key string) (*t.FriendRequest, error) {
|
||||
return a.friendRequestByQuery(ctx, "SELECT * FROM im_friend_requests WHERE tenant_id=? AND applicant_id=? AND idempotency_key=?", tenantID, decodeUID(applicantID), key)
|
||||
}
|
||||
|
||||
func (a *adapter) friendRequestByQuery(ctx context.Context, query string, args ...any) (*t.FriendRequest, error) {
|
||||
row := struct {
|
||||
ID int64
|
||||
TenantID t.TenantID `db:"tenant_id"`
|
||||
RequestID string `db:"request_id"`
|
||||
ApplicantID int64 `db:"applicant_id"`
|
||||
TargetID int64 `db:"target_id"`
|
||||
Message string
|
||||
Source string
|
||||
SourceTopic string `db:"source_topic"`
|
||||
Status string
|
||||
IdempotencyKey string `db:"idempotency_key"`
|
||||
ExpiresAt time.Time `db:"expires_at"`
|
||||
HandledAt *time.Time `db:"handled_at"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}{}
|
||||
err := a.db.GetContext(ctx, &row, query, args...)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &t.FriendRequest{
|
||||
ID: row.ID,
|
||||
TenantID: row.TenantID,
|
||||
RequestID: row.RequestID,
|
||||
ApplicantID: store.EncodeUid(row.ApplicantID),
|
||||
TargetID: store.EncodeUid(row.TargetID),
|
||||
Message: row.Message,
|
||||
Source: row.Source,
|
||||
SourceTopic: row.SourceTopic,
|
||||
Status: row.Status,
|
||||
IdempotencyKey: row.IdempotencyKey,
|
||||
ExpiresAt: row.ExpiresAt,
|
||||
HandledAt: row.HandledAt,
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func nullableUID(uid t.Uid) any {
|
||||
if uid.IsZero() {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user