289 lines
5.7 KiB
Go
289 lines
5.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"status/model"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type statusQueryBuilder struct {
|
|
where string
|
|
limit string
|
|
offset string
|
|
}
|
|
|
|
func (p statusQueryBuilder) build() string {
|
|
query := []string{`SELECT id, author, content, created_at, face, number from statuses`}
|
|
if p.where != "" {
|
|
query = append(query, `WHERE`, p.where)
|
|
}
|
|
query = append(query, `ORDER BY created_at desc`)
|
|
if p.limit != "" {
|
|
query = append(query, `LIMIT`, p.limit)
|
|
}
|
|
if p.offset != "" {
|
|
query = append(query, `OFFSET`, p.offset)
|
|
}
|
|
return strings.Join(query, " ")
|
|
}
|
|
|
|
func (s *Storage) populateStatus(rows *sql.Rows) (model.Status, error) {
|
|
var status model.Status
|
|
err := rows.Scan(
|
|
&status.Id,
|
|
&status.User,
|
|
&status.Content,
|
|
&status.CreatedAt,
|
|
&status.Face,
|
|
&status.Number,
|
|
)
|
|
if err != nil {
|
|
return status, err
|
|
}
|
|
return status, nil
|
|
}
|
|
|
|
func (s *Storage) CreateStatus(status model.Status) error {
|
|
var statusId int64
|
|
ctx := context.Background()
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.QueryRowContext(ctx,
|
|
`INSERT INTO statuses (author, content, face, number)
|
|
VALUES ($1, $2, $3, $4) RETURNING id`,
|
|
status.User, status.Content, status.Face, status.Number,
|
|
).Scan(&statusId); err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
if _, err := tx.ExecContext(ctx,
|
|
`UPDATE users SET status_id = $1 WHERE name = $2`,
|
|
statusId, status.User,
|
|
); err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (s *Storage) StatusById(id int64) (model.Status, error) {
|
|
var status model.Status
|
|
err := s.db.QueryRow(
|
|
`SELECT id, author, content, face, created_at, number
|
|
FROM statuses WHERE id=$1`, id,
|
|
).Scan(
|
|
&status.Id,
|
|
&status.User,
|
|
&status.Content,
|
|
&status.Face,
|
|
&status.CreatedAt,
|
|
&status.Number,
|
|
)
|
|
return status, err
|
|
}
|
|
|
|
func (s *Storage) StatusByUsername(user string, perPage int, page int64) ([]model.Status, bool, error) {
|
|
rows, err := s.db.Query(statusQueryBuilder{
|
|
where: `author = $1`,
|
|
limit: strconv.Itoa(perPage + 1),
|
|
offset: `$2`,
|
|
}.build(), user, page*int64(perPage))
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
var statuses []model.Status
|
|
for rows.Next() {
|
|
post, err := s.populateStatus(rows)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
statuses = append(statuses, post)
|
|
}
|
|
|
|
if len(statuses) > perPage {
|
|
return statuses[:perPage], true, nil
|
|
}
|
|
|
|
return statuses, false, nil
|
|
}
|
|
|
|
func (s *Storage) StatusFeed() ([]model.Status, error) {
|
|
rows, err := s.db.Query(statusQueryBuilder{
|
|
limit: strconv.Itoa(20),
|
|
}.build())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var statuses []model.Status
|
|
for rows.Next() {
|
|
post, err := s.populateStatus(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statuses = append(statuses, post)
|
|
}
|
|
|
|
return statuses, nil
|
|
}
|
|
|
|
func (s *Storage) Statuses(page int64, perPage int) ([]model.Status, bool, error) {
|
|
rows, err := s.db.Query(statusQueryBuilder{
|
|
limit: strconv.Itoa(perPage + 1),
|
|
offset: `$1`,
|
|
}.build(), page*int64(perPage))
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
var statuses []model.Status
|
|
for rows.Next() {
|
|
post, err := s.populateStatus(rows)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
statuses = append(statuses, post)
|
|
}
|
|
|
|
if len(statuses) > perPage {
|
|
return statuses[:perPage], true, nil
|
|
}
|
|
|
|
return statuses, false, nil
|
|
}
|
|
|
|
func (s *Storage) LatestStatuses() ([]model.Status, error) {
|
|
rows, err := s.db.Query(`
|
|
SELECT
|
|
statuses.id,
|
|
users.name,
|
|
statuses.content,
|
|
statuses.created_at,
|
|
statuses.face,
|
|
statuses.number
|
|
FROM users
|
|
INNER JOIN statuses
|
|
ON users.status_id = statuses.id
|
|
ORDER BY statuses.created_at DESC;
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var statuses []model.Status
|
|
for rows.Next() {
|
|
post, err := s.populateStatus(rows)
|
|
if err != nil {
|
|
return statuses, err
|
|
}
|
|
statuses = append(statuses, post)
|
|
}
|
|
|
|
return statuses, nil
|
|
}
|
|
|
|
func (s *Storage) UpdateStatus(status model.Status) error {
|
|
stmt, err := s.db.Prepare(`
|
|
UPDATE statuses
|
|
SET content = $1, face = $2, number = $3
|
|
WHERE id = $4 AND author = $5;
|
|
`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
|
|
_, err = stmt.Exec(
|
|
status.Content,
|
|
status.Face,
|
|
status.Number,
|
|
status.Id,
|
|
status.User,
|
|
)
|
|
|
|
return err
|
|
}
|
|
|
|
func (s *Storage) DeleteStatus(id int64, author string) error {
|
|
ctx := context.Background()
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var latestId int64
|
|
if err := tx.QueryRowContext(ctx,
|
|
`SELECT status_id FROM users WHERE name = $1`,
|
|
author,
|
|
).Scan(&latestId); err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
if _, err := tx.ExecContext(ctx,
|
|
`UPDATE users SET status_id = $1 WHERE name = $2`,
|
|
nil, author,
|
|
); err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
if _, err := tx.ExecContext(ctx,
|
|
`DELETE FROM statuses WHERE id = $1 AND author = $2`,
|
|
id, author,
|
|
); err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
if latestId == id {
|
|
var newId int64
|
|
err := tx.QueryRowContext(ctx,
|
|
`SELECT id FROM statuses
|
|
WHERE author = $1
|
|
ORDER BY created_at DESC
|
|
LIMIT 1`,
|
|
author,
|
|
).Scan(&newId)
|
|
|
|
if err == sql.ErrNoRows {
|
|
_, err = tx.ExecContext(ctx,
|
|
`UPDATE users SET status_id = NULL WHERE name = $1`,
|
|
author)
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
} else if err == nil {
|
|
_, err = tx.ExecContext(ctx,
|
|
`UPDATE users SET status_id = $1 WHERE name = $2`,
|
|
newId, author)
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
} else {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
} else {
|
|
_, err := tx.ExecContext(ctx,
|
|
`UPDATE users SET status_id = $1 WHERE name = $2`,
|
|
latestId, author)
|
|
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|