111 lines
3.7 KiB
Go
111 lines
3.7 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/store/postgres/sqlcgen"
|
|
)
|
|
|
|
// UpdateStateStore 用 PostgreSQL 实现 store.UpdateStateStore。
|
|
type UpdateStateStore struct {
|
|
q *sqlcgen.Queries
|
|
db sqlcgen.DBTX
|
|
}
|
|
|
|
// NewUpdateStateStore 基于 pgx 连接池(或事务)创建 UpdateStateStore。
|
|
func NewUpdateStateStore(db sqlcgen.DBTX) *UpdateStateStore {
|
|
return &UpdateStateStore{q: sqlcgen.New(db), db: db}
|
|
}
|
|
|
|
func (s *UpdateStateStore) Get(ctx context.Context, id [8]byte, userID int64) (domain.UpdateState, bool, error) {
|
|
row, err := s.q.GetUpdateState(ctx, sqlcgen.GetUpdateStateParams{
|
|
AuthKeyID: authKeyIDToInt64(id),
|
|
UserID: userID,
|
|
})
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return domain.UpdateState{}, false, nil
|
|
}
|
|
return domain.UpdateState{}, false, fmt.Errorf("get update state: %w", err)
|
|
}
|
|
return domain.UpdateState{
|
|
Pts: int(row.Pts),
|
|
Qts: int(row.Qts),
|
|
Date: int(row.Date),
|
|
Seq: int(row.Seq),
|
|
}, true, nil
|
|
}
|
|
|
|
func (s *UpdateStateStore) Save(ctx context.Context, id [8]byte, userID int64, st domain.UpdateState) error {
|
|
if err := s.q.UpsertUpdateState(ctx, sqlcgen.UpsertUpdateStateParams{
|
|
AuthKeyID: authKeyIDToInt64(id),
|
|
UserID: userID,
|
|
Pts: int32(st.Pts),
|
|
Qts: int32(st.Qts),
|
|
Date: int32(st.Date),
|
|
Seq: int32(st.Seq),
|
|
}); err != nil {
|
|
return fmt.Errorf("upsert update state: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *UpdateStateStore) CommitDeliveredState(ctx context.Context, id [8]byte, userID int64, st domain.UpdateState, mode domain.UpdateStateCommitMode) error {
|
|
establishObserved := mode == domain.UpdateStateCommitDeliveredAndObservedBaseline
|
|
if mode != domain.UpdateStateCommitDeliveredOnly && !establishObserved {
|
|
return fmt.Errorf("commit delivered update state: invalid mode %d", mode)
|
|
}
|
|
if _, err := s.db.Exec(ctx, `
|
|
INSERT INTO update_states (auth_key_id, user_id, pts, qts, date, seq, observed_pts)
|
|
VALUES ($1, $2, $3, $4, $5, $6, CASE WHEN $7 THEN $3 ELSE 0 END)
|
|
ON CONFLICT (auth_key_id, user_id) DO UPDATE SET
|
|
pts = GREATEST(update_states.pts, EXCLUDED.pts),
|
|
qts = GREATEST(update_states.qts, EXCLUDED.qts),
|
|
date = GREATEST(update_states.date, EXCLUDED.date),
|
|
seq = GREATEST(update_states.seq, EXCLUDED.seq),
|
|
observed_pts = CASE
|
|
WHEN $7 THEN GREATEST(update_states.observed_pts, EXCLUDED.observed_pts)
|
|
ELSE update_states.observed_pts
|
|
END,
|
|
updated_at = now()`, authKeyIDToInt64(id), userID, st.Pts, st.Qts, st.Date, st.Seq, establishObserved); err != nil {
|
|
return fmt.Errorf("commit delivered update state: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *UpdateStateStore) ObserveClientState(ctx context.Context, id [8]byte, userID int64, st domain.UpdateState) error {
|
|
if st.Pts < 0 {
|
|
st.Pts = 0
|
|
}
|
|
if _, err := s.db.Exec(ctx, `
|
|
INSERT INTO update_states (auth_key_id, user_id, pts, qts, date, seq, observed_pts)
|
|
VALUES ($1, $2, 0, 0, 0, 0, $3)
|
|
ON CONFLICT (auth_key_id, user_id) DO UPDATE SET
|
|
observed_pts = GREATEST(update_states.observed_pts, EXCLUDED.observed_pts),
|
|
updated_at = now()`, authKeyIDToInt64(id), userID, st.Pts); err != nil {
|
|
return fmt.Errorf("observe client update state: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *UpdateStateStore) Delete(ctx context.Context, id [8]byte, userID int64) error {
|
|
if err := s.q.DeleteUpdateState(ctx, sqlcgen.DeleteUpdateStateParams{
|
|
AuthKeyID: authKeyIDToInt64(id),
|
|
UserID: userID,
|
|
}); err != nil {
|
|
return fmt.Errorf("delete update state: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *UpdateStateStore) DeleteAuthKey(ctx context.Context, id [8]byte) error {
|
|
if err := s.q.DeleteUpdateStatesByAuthKey(ctx, authKeyIDToInt64(id)); err != nil {
|
|
return fmt.Errorf("delete update states by auth key: %w", err)
|
|
}
|
|
return nil
|
|
}
|