Refactor
This commit is contained in:
@@ -25,6 +25,10 @@ func InitDB() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err = DB.Exec("PRAGMA foreign_keys = ON;"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// make users table
|
||||
userTable := `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
@@ -43,7 +47,7 @@ func InitDB() {
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
expires_at DATETIME NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);`
|
||||
_, err = DB.Exec(sessionTable)
|
||||
if err != nil {
|
||||
@@ -60,7 +64,7 @@ func InitDB() {
|
||||
owner_id INTEGER NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
active_story_id INTEGER,
|
||||
FOREIGN KEY(owner_id) REFERENCES users(id)
|
||||
FOREIGN KEY(owner_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);`
|
||||
_, err = DB.Exec(roomTable)
|
||||
if err != nil {
|
||||
@@ -74,8 +78,8 @@ func InitDB() {
|
||||
user_id INTEGER NOT NULL,
|
||||
joined_at DATETIME NOT NULL,
|
||||
PRIMARY KEY (room_id, user_id),
|
||||
FOREIGN KEY(room_id) REFERENCES rooms(id),
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
FOREIGN KEY(room_id) REFERENCES rooms(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);`
|
||||
_, err = DB.Exec(memberTable)
|
||||
if err != nil {
|
||||
@@ -90,7 +94,7 @@ func InitDB() {
|
||||
title TEXT NOT NULL,
|
||||
points INTEGER,
|
||||
voted INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY(room_id) REFERENCES rooms(id)
|
||||
FOREIGN KEY(room_id) REFERENCES rooms(id) ON DELETE CASCADE
|
||||
);`
|
||||
_, err = DB.Exec(storyTable)
|
||||
if err != nil {
|
||||
@@ -104,11 +108,27 @@ func InitDB() {
|
||||
user_id INTEGER NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (story_id, user_id),
|
||||
FOREIGN KEY(story_id) REFERENCES stories(id),
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
FOREIGN KEY(story_id) REFERENCES stories(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);`
|
||||
_, err = DB.Exec(voteTable)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
indexes := []string{
|
||||
"CREATE INDEX IF NOT EXISTS idx_sessions_user_expires ON sessions(user_id, expires_at);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_rooms_owner ON rooms(owner_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_room_members_room ON room_members(room_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_room_members_user ON room_members(user_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_stories_room ON stories(room_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_votes_story ON votes(story_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_votes_user ON votes(user_id);",
|
||||
}
|
||||
for _, stmt := range indexes {
|
||||
if _, err = DB.Exec(stmt); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+120
-9
@@ -1,6 +1,8 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -22,6 +24,11 @@ type RoomMember struct {
|
||||
JoinedAt time.Time
|
||||
}
|
||||
|
||||
type RoomSummary struct {
|
||||
Room
|
||||
MemberCount int
|
||||
}
|
||||
|
||||
// generate unique room code
|
||||
func generateRoomCode() string {
|
||||
code := uuid.New().String()[:8]
|
||||
@@ -31,19 +38,42 @@ func generateRoomCode() string {
|
||||
// create room
|
||||
func CreateRoom(name string, ownerID int, scale string) (*Room, error) {
|
||||
code := generateRoomCode()
|
||||
_, err := DB.Exec("INSERT INTO rooms (name, code, scale, owner_id, created_at, active_story_id) VALUES (?, ?, ?, ?, ?, NULL)", name, code, scale, ownerID, time.Now())
|
||||
|
||||
tx, err := DB.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
room := &Room{Name: name, Code: code, Scale: scale, OwnerID: ownerID, ActiveStoryID: nil}
|
||||
row := DB.QueryRow("SELECT id, name, code, scale, owner_id, created_at, active_story_id FROM rooms WHERE code = ?", code)
|
||||
err = row.Scan(&room.ID, &room.Name, &room.Code, &room.Scale, &room.OwnerID, &room.CreatedAt, &room.ActiveStoryID)
|
||||
defer tx.Rollback()
|
||||
|
||||
createdAt := time.Now()
|
||||
res, err := tx.Exec("INSERT INTO rooms (name, code, scale, owner_id, created_at, active_story_id) VALUES (?, ?, ?, ?, ?, NULL)", name, code, scale, ownerID, createdAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add owner as member
|
||||
DB.Exec("INSERT INTO room_members (room_id, user_id, joined_at) VALUES (?, ?, ?)", room.ID, ownerID, time.Now())
|
||||
return room, nil
|
||||
|
||||
roomID64, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomID := int(roomID64)
|
||||
|
||||
if _, err := tx.Exec("INSERT INTO room_members (room_id, user_id, joined_at) VALUES (?, ?, ?)", roomID, ownerID, createdAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Room{
|
||||
ID: roomID,
|
||||
Name: name,
|
||||
Code: code,
|
||||
Scale: scale,
|
||||
OwnerID: ownerID,
|
||||
CreatedAt: createdAt,
|
||||
ActiveStoryID: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// get room by id
|
||||
@@ -70,6 +100,18 @@ func GetRoomByCode(code string) (*Room, error) {
|
||||
|
||||
// get rooms for user
|
||||
func GetRoomsForUser(userID int) ([]Room, error) {
|
||||
summaries, err := GetRoomSummariesForUser(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rooms := make([]Room, 0, len(summaries))
|
||||
for _, s := range summaries {
|
||||
rooms = append(rooms, s.Room)
|
||||
}
|
||||
return rooms, nil
|
||||
}
|
||||
|
||||
func GetRoomSummariesForUser(userID int) ([]RoomSummary, error) {
|
||||
rows, err := DB.Query(`
|
||||
SELECT r.id, r.name, r.code, r.scale, r.owner_id, r.created_at, r.active_story_id, COUNT(rm.user_id) as member_count
|
||||
FROM rooms r
|
||||
@@ -81,14 +123,15 @@ func GetRoomsForUser(userID int) ([]Room, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var rooms []Room
|
||||
var rooms []RoomSummary
|
||||
for rows.Next() {
|
||||
var r Room
|
||||
var r RoomSummary
|
||||
var memberCount int
|
||||
err := rows.Scan(&r.ID, &r.Name, &r.Code, &r.Scale, &r.OwnerID, &r.CreatedAt, &r.ActiveStoryID, &memberCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.MemberCount = memberCount
|
||||
rooms = append(rooms, r)
|
||||
}
|
||||
return rooms, nil
|
||||
@@ -135,3 +178,71 @@ func DeleteRoom(id int) error {
|
||||
_, err := DB.Exec("DELETE FROM rooms WHERE id = ?", id)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetVotesForStories(storyIDs []int) (map[int][]Vote, error) {
|
||||
votesByStory := make(map[int][]Vote)
|
||||
if len(storyIDs) == 0 {
|
||||
return votesByStory, nil
|
||||
}
|
||||
|
||||
placeholders := strings.TrimSuffix(strings.Repeat("?,", len(storyIDs)), ",")
|
||||
args := make([]interface{}, 0, len(storyIDs))
|
||||
for _, id := range storyIDs {
|
||||
args = append(args, id)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf("SELECT story_id, user_id, value FROM votes WHERE story_id IN (%s)", placeholders)
|
||||
rows, err := DB.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var v Vote
|
||||
if err := rows.Scan(&v.StoryID, &v.UserID, &v.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
votesByStory[v.StoryID] = append(votesByStory[v.StoryID], v)
|
||||
}
|
||||
|
||||
return votesByStory, nil
|
||||
}
|
||||
|
||||
func GetVoteViewsForStories(storyIDs []int) (map[int][]VoteView, error) {
|
||||
voteViewsByStory := make(map[int][]VoteView)
|
||||
if len(storyIDs) == 0 {
|
||||
return voteViewsByStory, nil
|
||||
}
|
||||
|
||||
placeholders := strings.TrimSuffix(strings.Repeat("?,", len(storyIDs)), ",")
|
||||
args := make([]interface{}, 0, len(storyIDs))
|
||||
for _, id := range storyIDs {
|
||||
args = append(args, id)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT v.story_id, u.username, v.value
|
||||
FROM votes v
|
||||
JOIN users u ON v.user_id = u.id
|
||||
WHERE v.story_id IN (%s)
|
||||
`, placeholders)
|
||||
rows, err := DB.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
storyID int
|
||||
vv VoteView
|
||||
)
|
||||
if err := rows.Scan(&storyID, &vv.Username, &vv.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
voteViewsByStory[storyID] = append(voteViewsByStory[storyID], vv)
|
||||
}
|
||||
|
||||
return voteViewsByStory, nil
|
||||
}
|
||||
|
||||
+15
-13
@@ -2,7 +2,7 @@ package lib
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Story struct {
|
||||
@@ -22,14 +22,21 @@ type Vote struct {
|
||||
|
||||
// create story
|
||||
func CreateStory(roomID int, title string) (*Story, error) {
|
||||
_, err := DB.Exec("INSERT INTO stories (room_id, title, voted) VALUES (?, ?, 0)", roomID, title)
|
||||
res, err := DB.Exec("INSERT INTO stories (room_id, title, voted) VALUES (?, ?, 0)", roomID, title)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Story{RoomID: roomID, Title: title}
|
||||
row := DB.QueryRow("SELECT id, room_id, title, points, voted FROM stories WHERE room_id = ? ORDER BY id DESC LIMIT 1", roomID)
|
||||
err = row.Scan(&s.ID, &s.RoomID, &s.Title, &s.Points, &s.Voted)
|
||||
return s, err
|
||||
id64, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Story{
|
||||
ID: int(id64),
|
||||
RoomID: roomID,
|
||||
Title: title,
|
||||
Points: nil,
|
||||
Voted: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// get stories for room
|
||||
@@ -138,11 +145,7 @@ func RenameStory(storyID int, title string) error {
|
||||
|
||||
// delete story and its votes
|
||||
func DeleteStory(storyID int) error {
|
||||
_, err := DB.Exec("DELETE FROM votes WHERE story_id = ?", storyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = DB.Exec("DELETE FROM stories WHERE id = ?", storyID)
|
||||
_, err := DB.Exec("DELETE FROM stories WHERE id = ?", storyID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -163,8 +166,7 @@ func tshirtToPoints(s string) float64 {
|
||||
case "?":
|
||||
return 0
|
||||
default:
|
||||
var n int
|
||||
fmt.Sscanf(s, "%d", &n)
|
||||
n, _ := strconv.Atoi(s)
|
||||
return float64(n)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user