package services import ( "context" "database/sql" "errors" "evening_detective/internal/models" "log" _ "github.com/mattn/go-sqlite3" ) type Repository struct { db *sql.DB } func NewRepository(filepath string) (*Repository, error) { db, err := sql.Open("sqlite3", filepath) if err != nil { return nil, err } log.Printf("load db from: %s", filepath) _, err = db.Exec("CREATE TABLE IF NOT EXISTS teams (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL CHECK(length(trim(name)) > 0), password TEXT);") if err != nil { return nil, err } _, err = db.Exec("CREATE TABLE IF NOT EXISTS actions (id INTEGER PRIMARY KEY AUTOINCREMENT, place TEXT, teamId INTEGER, FOREIGN KEY (teamId) REFERENCES teams(id) ON DELETE CASCADE);") if err != nil { return nil, err } _, err = db.Exec("CREATE TABLE IF NOT EXISTS applications (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, teamId INTEGER, state TEXT, FOREIGN KEY (teamId) REFERENCES teams(id) ON DELETE CASCADE);") if err != nil { return nil, err } _, err = db.Exec("CREATE TABLE IF NOT EXISTS games (id INTEGER PRIMARY KEY AUTOINCREMENT, state TEXT, startAt TEXT, endAt TEXT);") if err != nil { return nil, err } return &Repository{db: db}, nil } func (r *Repository) Close() { r.db.Close() } func (r *Repository) GetTeams(ctx context.Context) ([]*models.Team, error) { rows, err := r.db.Query("select id, name, password from teams") if err != nil { return nil, err } defer rows.Close() teams := []*models.Team{} for rows.Next() { item := &models.Team{} err := rows.Scan(&item.ID, &item.Name, &item.Password) if err != nil { return nil, err } teams = append(teams, item) } return teams, nil } func (r *Repository) AddTeams(ctx context.Context, teams []*models.Team) ([]*models.Team, error) { for _, team := range teams { result, err := r.db.Exec("insert into teams (name, password) values ($1, $2)", team.Name, team.Password) if err != nil { return nil, err } team.ID, err = result.LastInsertId() if err != nil { return nil, err } } return teams, nil } func (r *Repository) GetActions(ctx context.Context, teamId int64) ([]*models.Action, error) { rows, err := r.db.Query("select id, place from actions where teamId = $1", teamId) if err != nil { return nil, err } defer rows.Close() actions := []*models.Action{} for rows.Next() { item := &models.Action{} err := rows.Scan(&item.ID, &item.Place) if err != nil { return nil, err } actions = append(actions, item) } return actions, nil } func (r *Repository) AddActions(ctx context.Context, teamId int64, actions []*models.Action) error { for _, action := range actions { _, err := r.db.Exec("insert into actions (place, teamId) values ($1, $2)", action.Place, teamId) if err != nil { return err } } return nil } func (r *Repository) GetTeam(ctx context.Context, teamId any, password any) (*models.Team, error) { rows, err := r.db.Query("select id, name from teams where LOWER(name) = LOWER($1) and password = $2", teamId, password) if err != nil { return nil, err } defer rows.Close() teams := []*models.Team{} for rows.Next() { item := &models.Team{} err := rows.Scan(&item.ID, &item.Name) if err != nil { return nil, err } teams = append(teams, item) } if len(teams) != 1 { return nil, errors.New("bad result") } return teams[0], nil } func (r *Repository) AddApplications(ctx context.Context, teamId int64, applications []*models.Application) error { for _, application := range applications { _, err := r.db.Exec("insert into applications (name, teamId, state) values ($1, $2, $3)", application.Name, teamId, application.State) if err != nil { return err } } return nil } func (r *Repository) GetApplications(ctx context.Context, teamId int64) ([]*models.Application, error) { rows, err := r.db.Query("select id, name from applications where teamId = $1", teamId) if err != nil { return nil, err } defer rows.Close() applications := []*models.Application{} for rows.Next() { item := &models.Application{} err := rows.Scan(&item.ID, &item.Name) if err != nil { return nil, err } applications = append(applications, item) } return applications, nil } func (r *Repository) GetApplicationsByState(ctx context.Context, teamId int64, state string) ([]*models.Application, error) { rows, err := r.db.Query("select id, name from applications where teamId = $1 and state = $2", teamId, state) if err != nil { return nil, err } defer rows.Close() applications := []*models.Application{} for rows.Next() { item := &models.Application{} err := rows.Scan(&item.ID, &item.Name) if err != nil { return nil, err } applications = append(applications, item) } return applications, nil } func (r *Repository) GiveApplications(ctx context.Context, teamId int64, applications []*models.Application) error { for _, application := range applications { _, err := r.db.Exec("update applications set state = \"gave\" where teamId = $1 and id = $2", teamId, application.ID) if err != nil { return err } } return nil } func (r *Repository) GetGame(ctx context.Context) (*models.Game, error) { rows, err := r.db.Query("select state, startAt, endAt from games limit 1") if err != nil { return nil, err } defer rows.Close() game := &models.Game{} if rows.Next() { err := rows.Scan(&game.State, &game.StartTime, &game.EndTime) if err != nil { return nil, err } return game, nil } state := "NEW" _, err = r.db.Exec("insert into games (state, startAt, endAt) values ($1, '', '')", state) if err != nil { return nil, err } game.State = state return game, nil } func (r *Repository) GameUpdateState(ctx context.Context, state string) error { game, err := r.GetGame(ctx) if err != nil { return err } switch state { case "RUN": if game.StartTime == "" { _, err := r.db.Exec("update games set state = $1, startAt = datetime('now', 'localtime')", state) return err } _, err := r.db.Exec("update games set state = $1", state) return err case "STOP": _, err := r.db.Exec("update games set state = $1, endAt = datetime('now', 'localtime')", state) return err } return nil } func (r *Repository) DeleteAllTeams(ctx context.Context) error { _, err := r.db.Exec("delete from teams where 1") return err }