This commit is contained in:
2026-07-21 03:49:47 +03:00
commit e8a0b2ba42
65 changed files with 40163 additions and 0 deletions

21
model/errors.go Executable file
View File

@@ -0,0 +1,21 @@
package model
import "gordenko.dev/dima/fixme"
const (
EmptyValue fixme.Code = "100"
UnknownZone fixme.Code = "101"
UnknownChamp fixme.Code = "102"
UnknownTeam fixme.Code = "103"
Duplicate fixme.Code = "104"
)
var errorMessages = map[fixme.Code]string{
EmptyValue: "Пустое значение недопустимо",
UnknownZone: "Неизвестная страна/регион",
UnknownChamp: "Неизвестный чемпионат",
UnknownTeam: "Неизвестная команда",
Duplicate: "Дубликат!",
}
var fix = fixme.New(errorMessages)

416
model/football.sgo Normal file
View File

@@ -0,0 +1,416 @@
package model
import (
"fmt"
"strings"
"web/api"
)
func (s *Model) GetFootballZone(zoneID string) (_ *FootballZone, err error) {
zone := FootballZone{
ZoneID: zoneID,
}
found, err := s.qx.One(&zone)
if err != nil {
return
}
if !found {
return
}
return &zone, nil
}
func (s *Model) GetFootballChamp(champID string) (_ *FootballChamp, err error) {
champ := FootballChamp{
ChampID: champID,
}
found, err := s.qx.One(&champ)
if err != nil {
return
}
if !found {
return
}
return &champ, nil
}
func (s *Model) GetFootballTeam(teamID string) (_ *FootballTeam, err error) {
team := FootballTeam{
TeamID: teamID,
}
found, err := s.qx.One(&team)
if err != nil {
return
}
if !found {
return
}
return &team, nil
}
func (s *Model) GetFootballMatch(matchID string) (_ *FootballMatch, err error) {
m := FootballMatch{
MatchID: matchID,
}
found, err := s.qx.One(&m)
if err != nil {
return
}
if !found {
return
}
return &m, nil
}
func (s *Model) AddFootballZone(req FootballZone) (err error) {
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) AddFootballChamp(req FootballChamp) (err error) {
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
has, err := tx.Has("footballZones", "WHERE zoneID=?", req.ZoneID)
if err != nil {
return
}
if !has {
err = api.Err{
Code: UnknownZone,
Field: "zoneID",
}
return
}
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) UpdateFootballChampName(req FootballChamp) (err error) {
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
err = s.qx.Exec("UPDATE footballChamps SET name=? WHERE champID=?", req.Name, req.ChampID)
if err != nil {
return
}
return
}
func (s *Model) AddFootballTeam(req FootballTeam) (err error) {
req.CanonicalName = NormalizeNameToAlias(req.CanonicalName)
if req.CanonicalName == "" {
err = api.Err{
Code: EmptyValue,
Field: "canonicalName",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
_, err = tx.Insert(req)
if err != nil {
return
}
// Добавляем alias
_, err = tx.Insert(FootballAlias{
Name: req.CanonicalName,
TeamID: req.TeamID,
})
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) AddFootballTeamAlias(req FootballAlias) (err error) {
req.AliasID = 0
req.Name = NormalizeNameToAlias(req.Name)
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
hasTeam, err := tx.Has("footballTeams", "WHERE teamID=?", req.TeamID)
if err != nil {
return
}
if !hasTeam {
err = api.Err{
Code: UnknownTeam,
Field: "teamID",
}
return
}
hasAlias, err := tx.Has("footballTeamAliases", "WHERE teamID=? AND name=?",
req.TeamID, req.Name)
if err != nil {
return
}
if hasAlias {
err = api.Err{
Code: Duplicate,
Field: "name",
}
return
}
// Добавляем alias
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) SetFootballTeamWasLinked(teamID string) (err error) {
err = s.qx.Exec(`UPDATE footballTeams SET wasLinked=true WHERE teamID=?`, teamID)
return
}
func (s *Model) RemoveFootballTeamAlias(aliasID int64) (err error) {
return s.qx.Exec(`DELETE FROM footballTeamAliases WHERE aliasID=?`, aliasID)
}
func (s *Model) AddFootballMatch(req FootballMatch) (err error) {
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
req.MatchID = strings.TrimSpace(req.MatchID)
if req.MatchID == "" {
err = api.Err{
Code: EmptyValue,
Field: "matchID",
}
return
}
hasChamp, err := tx.Has("footballChamps", "WHERE champID=?", req.ChampID)
if err != nil {
return
}
if !hasChamp {
err = api.Err{
Code: UnknownChamp,
Field: "champID",
}
return
}
hasHomeTeam, err := tx.Has("footballTeams", "WHERE teamID=?", req.HomeTeamID)
if err != nil {
return
}
if !hasHomeTeam {
err = api.Err{
Code: UnknownTeam,
Field: "teamID",
}
return
}
hasAwayTeam, err := tx.Has("footballTeams", "WHERE teamID=?", req.AwayTeamID)
if err != nil {
return
}
if !hasAwayTeam {
err = api.Err{
Code: UnknownTeam,
Field: "teamID",
}
return
}
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
type UpdateFootballTeamCanonicalNameIn struct {
TeamID string
CanonicalName string
NewCanonicalName string
}
func (s *Model) UpdateFootballTeamCanonicalName(in UpdateFootballTeamCanonicalNameIn) (err error) {
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
// Старый canonicalName не нормализуем, ибо предполагается что имя мы получили из БД
in.NewCanonicalName = NormalizeNameToAlias(in.NewCanonicalName)
if in.NewCanonicalName == "" {
err = api.Err{
Code: EmptyValue,
Field: "newCanonicalName",
}
return
}
// Удаляем старый alias
err = tx.Exec("DELETE FROM footballTeamAliases WHERE teamID=? AND name=?",
in.TeamID, in.CanonicalName)
if err != nil {
return
}
// Обновляем canonicalName
err = tx.Exec("UPDATE footballTeams SET canonicalName=? WHERE teamID=?",
in.NewCanonicalName, in.TeamID)
if err != nil {
fmt.Printf("old: %q, new: %q, teamID: %s\n", in.CanonicalName,
in.NewCanonicalName, in.TeamID)
//return
err = nil
}
hasAlias, err := tx.Has("footballTeamAliases", "WHERE teamID=? AND name=?",
in.TeamID, in.NewCanonicalName)
if err != nil {
return
}
if !hasAlias {
// Добавляем новый alias
_, err = tx.Insert(FootballAlias{
Name: in.NewCanonicalName,
TeamID: in.TeamID,
})
if err != nil {
return
}
}
err = tx.Commit()
return
}
func (s *Model) ListFootballZones() (list []FootballZone, err error) {
err = s.qx.ListBy(&list, `ORDER BY name ASC`)
if err != nil {
return
}
return
}
func (s *Model) ListFootballTeamCandidates(name string) (matchIDs []string, err error) {
name = NormalizeNameToAlias(name)
err = s.qx.ListQuery(&matchIDs,
`SELECT teamID FROM footballTeamAliases WHERE name=?`, name)
if err != nil {
return
}
return
}
func (s *Model) ListFootballTeamsByZone(zoneID string) (list []FootballTeam, err error) {
err = s.qx.ListBy(&list, `WHERE zoneID=? ORDER BY canonicalName ASC`, zoneID)
if err != nil {
return
}
return
}
func (s *Model) ListFootballTeamAliases(teamID string) (list []FootballAlias, err error) {
err = s.qx.ListBy(&list, `WHERE teamID=? ORDER BY name ASC`, teamID)
if err != nil {
return
}
return
}
func (s *Model) ListFootballTeams() (list []FootballTeam, err error) {
err = s.qx.List(&list)
if err != nil {
return
}
return
}

163
model/football.sql Executable file
View File

@@ -0,0 +1,163 @@
CREATE TABLE footballZones (
zoneID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(zoneID),
UNIQUE(name)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE footballChamps (
champID VARCHAR(255) NOT NULL,
zoneID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(champID),
UNIQUE(zoneID, name),
FOREIGN KEY (zoneID) REFERENCES footballZones(zoneID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE footballTeams (
teamID VARCHAR(255) NOT NULL,
zoneID VARCHAR(255) NOT NULL,
canonicalName VARCHAR(255) NOT NULL,
wasLinked BOOL NOT NULL,
PRIMARY KEY(teamID),
--UNIQUE(zoneID, canonicalName),
FOREIGN KEY (zoneID) REFERENCES footballZones(zoneID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE footballTeamAliases (
aliasID INT NOT NULL AUTO_INCREMENT,
teamID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(aliasID),
UNIQUE(teamID, name),
FOREIGN KEY (teamID) REFERENCES footballTeams(teamID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE footballMatches (
matchID VARCHAR(255) NOT NULL,
startTime INT NOT NULL,
champID VARCHAR(255) NOT NULL,
homeTeamID VARCHAR(255) NOT NULL,
awayTeamID VARCHAR(255) NOT NULL,
homeGoals INT NOT NULL,
awayGoals INT NOT NULL,
hasStats BOOL NOT NULL,
p1HomeGoals INT NOT NULL,
p1AwayGoals INT NOT NULL,
p2HomeGoals INT NOT NULL,
p2AwayGoals INT NOT NULL,
PRIMARY KEY(matchID),
FOREIGN KEY (champID) REFERENCES footballChamps(champID),
FOREIGN KEY (homeTeamID) REFERENCES footballTeams(teamID),
FOREIGN KEY (awayTeamID) REFERENCES footballTeams(teamID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
--INSERT INTO footballChamps VALUES (NULL, "Seria A (Italy)");
--INSERT INTO footballChamps VALUES (NULL, "Bundesliga (Germany)");
--INSERT INTO footballChamps VALUES (NULL, "Premier League (England)");
delete from footballTeamAliases;
delete from footballMatches;
delete from footballTeams;
delete from footballChamps;
delete from footballZones;
-- HANDBALL
CREATE TABLE handballZones (
zoneID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(zoneID),
UNIQUE(name)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE handballChamps (
champID VARCHAR(255) NOT NULL,
zoneID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(champID),
UNIQUE(zoneID, name),
FOREIGN KEY (zoneID) REFERENCES handballZones(zoneID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE handballTeams (
teamID VARCHAR(255) NOT NULL,
zoneID VARCHAR(255) NOT NULL,
canonicalName VARCHAR(255) NOT NULL,
wasLinked BOOL NOT NULL,
PRIMARY KEY(teamID),
FOREIGN KEY (zoneID) REFERENCES handballZones(zoneID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE handballTeamAliases (
aliasID INT NOT NULL AUTO_INCREMENT,
teamID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(aliasID),
UNIQUE(teamID, name),
FOREIGN KEY (teamID) REFERENCES handballTeams(teamID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE handballMatches (
matchID VARCHAR(255) NOT NULL,
startTime INT NOT NULL,
champID VARCHAR(255) NOT NULL,
homeTeamID VARCHAR(255) NOT NULL,
awayTeamID VARCHAR(255) NOT NULL,
homeGoals INT NOT NULL,
awayGoals INT NOT NULL,
hasStats BOOL NOT NULL,
p1HomeGoals INT NOT NULL,
p1AwayGoals INT NOT NULL,
p2HomeGoals INT NOT NULL,
p2AwayGoals INT NOT NULL,
PRIMARY KEY(matchID),
FOREIGN KEY (champID) REFERENCES handballChamps(champID),
FOREIGN KEY (homeTeamID) REFERENCES handballTeams(teamID),
FOREIGN KEY (awayTeamID) REFERENCES handballTeams(teamID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
--INSERT INTO footballChamps VALUES (NULL, "Seria A (Italy)");
--INSERT INTO footballChamps VALUES (NULL, "Bundesliga (Germany)");
--INSERT INTO footballChamps VALUES (NULL, "Premier League (England)");
delete from handballTeamAliases;
delete from handballMatches;
delete from handballTeams;
delete from handballChamps;
delete from handballZones;

416
model/handball.sgo Normal file
View File

@@ -0,0 +1,416 @@
package model
import (
"fmt"
"strings"
"web/api"
)
func (s *Model) GetHandballZone(zoneID string) (_ *HandballZone, err error) {
zone := HandballZone{
ZoneID: zoneID,
}
found, err := s.qx.One(&zone)
if err != nil {
return
}
if !found {
return
}
return &zone, nil
}
func (s *Model) GetHandballChamp(champID string) (_ *HandballChamp, err error) {
champ := HandballChamp{
ChampID: champID,
}
found, err := s.qx.One(&champ)
if err != nil {
return
}
if !found {
return
}
return &champ, nil
}
func (s *Model) GetHandballTeam(teamID string) (_ *HandballTeam, err error) {
team := HandballTeam{
TeamID: teamID,
}
found, err := s.qx.One(&team)
if err != nil {
return
}
if !found {
return
}
return &team, nil
}
func (s *Model) GetHandballMatch(matchID string) (_ *HandballMatch, err error) {
m := HandballMatch{
MatchID: matchID,
}
found, err := s.qx.One(&m)
if err != nil {
return
}
if !found {
return
}
return &m, nil
}
func (s *Model) AddHandballZone(req HandballZone) (err error) {
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) AddHandballChamp(req HandballChamp) (err error) {
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
has, err := tx.Has("handballZones", "WHERE zoneID=?", req.ZoneID)
if err != nil {
return
}
if !has {
err = api.Err{
Code: UnknownZone,
Field: "zoneID",
}
return
}
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) UpdateHandballChampName(req HandballChamp) (err error) {
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
err = s.qx.Exec("UPDATE handballChamps SET name=? WHERE champID=?", req.Name, req.ChampID)
if err != nil {
return
}
return
}
func (s *Model) AddHandballTeam(req HandballTeam) (err error) {
req.CanonicalName = NormalizeNameToAlias(req.CanonicalName)
if req.CanonicalName == "" {
err = api.Err{
Code: EmptyValue,
Field: "canonicalName",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
_, err = tx.Insert(req)
if err != nil {
return
}
// Добавляем alias
_, err = tx.Insert(HandballAlias{
Name: req.CanonicalName,
TeamID: req.TeamID,
})
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) AddHandballTeamAlias(req HandballAlias) (err error) {
req.AliasID = 0
req.Name = NormalizeNameToAlias(req.Name)
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
hasTeam, err := tx.Has("handballTeams", "WHERE teamID=?", req.TeamID)
if err != nil {
return
}
if !hasTeam {
err = api.Err{
Code: UnknownTeam,
Field: "teamID",
}
return
}
hasAlias, err := tx.Has("handballTeamAliases", "WHERE teamID=? AND name=?",
req.TeamID, req.Name)
if err != nil {
return
}
if hasAlias {
err = api.Err{
Code: Duplicate,
Field: "name",
}
return
}
// Добавляем alias
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) SetHandballTeamWasLinked(teamID string) (err error) {
err = s.qx.Exec(`UPDATE handballTeams SET wasLinked=true WHERE teamID=?`, teamID)
return
}
func (s *Model) RemoveHandballTeamAlias(aliasID int64) (err error) {
return s.qx.Exec(`DELETE FROM handballTeamAliases WHERE aliasID=?`, aliasID)
}
func (s *Model) AddHandballMatch(req HandballMatch) (err error) {
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
req.MatchID = strings.TrimSpace(req.MatchID)
if req.MatchID == "" {
err = api.Err{
Code: EmptyValue,
Field: "matchID",
}
return
}
hasChamp, err := tx.Has("handballChamps", "WHERE champID=?", req.ChampID)
if err != nil {
return
}
if !hasChamp {
err = api.Err{
Code: UnknownChamp,
Field: "champID",
}
return
}
hasHomeTeam, err := tx.Has("handballTeams", "WHERE teamID=?", req.HomeTeamID)
if err != nil {
return
}
if !hasHomeTeam {
err = api.Err{
Code: UnknownTeam,
Field: "teamID",
}
return
}
hasAwayTeam, err := tx.Has("handballTeams", "WHERE teamID=?", req.AwayTeamID)
if err != nil {
return
}
if !hasAwayTeam {
err = api.Err{
Code: UnknownTeam,
Field: "teamID",
}
return
}
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
type UpdateHandballTeamCanonicalNameIn struct {
TeamID string
CanonicalName string
NewCanonicalName string
}
func (s *Model) UpdateHandballTeamCanonicalName(in UpdateHandballTeamCanonicalNameIn) (err error) {
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
// Старый canonicalName не нормализуем, ибо предполагается что имя мы получили из БД
in.NewCanonicalName = NormalizeNameToAlias(in.NewCanonicalName)
if in.NewCanonicalName == "" {
err = api.Err{
Code: EmptyValue,
Field: "newCanonicalName",
}
return
}
// Удаляем старый alias
err = tx.Exec("DELETE FROM handballTeamAliases WHERE teamID=? AND name=?",
in.TeamID, in.CanonicalName)
if err != nil {
return
}
// Обновляем canonicalName
err = tx.Exec("UPDATE handballTeams SET canonicalName=? WHERE teamID=?",
in.NewCanonicalName, in.TeamID)
if err != nil {
fmt.Printf("old: %q, new: %q, teamID: %s\n", in.CanonicalName,
in.NewCanonicalName, in.TeamID)
//return
err = nil
}
hasAlias, err := tx.Has("handballTeamAliases", "WHERE teamID=? AND name=?",
in.TeamID, in.NewCanonicalName)
if err != nil {
return
}
if !hasAlias {
// Добавляем новый alias
_, err = tx.Insert(HandballAlias{
Name: in.NewCanonicalName,
TeamID: in.TeamID,
})
if err != nil {
return
}
}
err = tx.Commit()
return
}
func (s *Model) ListHandballZones() (list []HandballZone, err error) {
err = s.qx.ListBy(&list, `ORDER BY name ASC`)
if err != nil {
return
}
return
}
func (s *Model) ListHandballTeamCandidates(name string) (matchIDs []string, err error) {
name = NormalizeNameToAlias(name)
err = s.qx.ListQuery(&matchIDs,
`SELECT teamID FROM handballTeamAliases WHERE name=?`, name)
if err != nil {
return
}
return
}
func (s *Model) ListHandballTeamsByZone(zoneID string) (list []HandballTeam, err error) {
err = s.qx.ListBy(&list, `WHERE zoneID=? ORDER BY canonicalName ASC`, zoneID)
if err != nil {
return
}
return
}
func (s *Model) ListHandballTeamAliases(teamID string) (list []HandballAlias, err error) {
err = s.qx.ListBy(&list, `WHERE teamID=? ORDER BY name ASC`, teamID)
if err != nil {
return
}
return
}
func (s *Model) ListHandballTeams() (list []HandballTeam, err error) {
err = s.qx.List(&list)
if err != nil {
return
}
return
}

82
model/model.go Executable file
View File

@@ -0,0 +1,82 @@
package model
import (
"regexp"
"strings"
"gordenko.dev/dima/qx"
"gordenko.dev/dima/textutil"
)
func New(qxdb *qx.Db) *Model {
s := new(Model)
s.qx = qxdb
return s
}
var reCountryCode = regexp.MustCompile(`\([a-z]{3}\)`)
// NormalizeNameToAlias - удаляет лишние пробельные символы. Заменяет whitespace символы на пробел,
// приводит к нижнему регистру
func NormalizeNameToAlias(name string) string {
name = strings.ToLower(name)
// вырезаем код страны если есть (пример: (Aut))
name = reCountryCode.ReplaceAllString(name, "")
name = strings.TrimSpace(name)
return textutil.NormalizeWhitespaces(name)
}
/*
func (s *Model) ListFootballChamps() (list []FootballChamp, err error) {
err = s.qx.ListBy(&list, "ORDER BY name ASC")
return
}
func (s *Model) ListFootballChampTeams(champID int64) (list []Team, err error) {
err = s.qx.ListBy(&list, "WHERE champID=? ORDER BY canonicalName ASC", champID)
return
}
func (s *Model) ListFootballTeamAliases(teamID int64) (list []Alias, err error) {
err = s.qx.ListBy(&list, "WHERE teamID=? ORDER BY name ASC", teamID)
return
}
func (s *Model) RemoveFootballTeam(teamID int64) (err error) {
return s.qx.Exec("DELETE FROM footballTeams WHERE teamID=?", teamID)
}
func (s *Model) RemoveFootballAlias(aliasID int64) (err error) {
return s.qx.Exec("DELETE FROM footballTeamAliases WHERE aliasID=?", aliasID)
}
func (s *Model) AddFootballAlias(req AddFootballAliasReq) (aliasID int64, err error) {
if req.Name == "" {
err = api.Err{
Code: EmptyValue,
Field: "name",
}
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
aliasID, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
*/

422
model/team.go Normal file
View File

@@ -0,0 +1,422 @@
package model
import (
"fmt"
"strings"
)
func (s *Model) GetTeamZone(zoneID string) (_ *TeamZone, err error) {
zone := TeamZone{
ZoneID: zoneID,
}
found, err := s.qx.One(&zone)
if err != nil {
return
}
if !found {
return
}
return &zone, nil
}
func (s *Model) GetTeamChamp(champID string) (_ *TeamChamp, err error) {
champ := TeamChamp{
ChampID: champID,
}
found, err := s.qx.One(&champ)
if err != nil {
return
}
if !found {
return
}
return &champ, nil
}
func (s *Model) GetTeam(teamID string) (_ *Team, err error) {
team := Team{
TeamID: teamID,
}
found, err := s.qx.One(&team)
if err != nil {
return
}
if !found {
return
}
return &team, nil
}
func (s *Model) GetTeamMatch(matchID string) (_ *TeamMatch, err error) {
m := TeamMatch{
MatchID: matchID,
}
found, err := s.qx.One(&m)
if err != nil {
return
}
if !found {
return
}
return &m, nil
}
func (s *Model) AddTeamZone(req TeamZone) (err error) {
if req.Name == "" {
err = fix.Field(EmptyValue, "name")
return
}
req.Name = strings.Title(strings.ToLower(req.Name))
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) AddTeamChamp(req TeamChamp) (err error) {
if req.Name == "" {
err = fix.Field(EmptyValue, "name")
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
has, err := tx.Has("teamZones", "WHERE zoneID=?", req.ZoneID)
if err != nil {
return
}
if !has {
err = fix.Field(UnknownZone, "zoneID")
return
}
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
/*
func (s *Model) UpdateTeamChampName(req TeamChamp) (err error) {
if req.Name == "" {
err = fix.Field(EmptyValue, "name")
return
}
err = s.qx.Exec("UPDATE teamChamps SET name=? WHERE champID=?", req.Name, req.ChampID)
if err != nil {
return
}
return
}
*/
func (s *Model) AddTeam(req Team) (err error) {
aliasName := NormalizeNameToAlias(req.CanonicalName)
req.CanonicalName = strings.Title(strings.ToLower(aliasName))
if req.CanonicalName == "" {
err = fix.Field(EmptyValue, "canonicalName")
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
_, err = tx.Insert(req)
if err != nil {
return
}
zone := TeamZone{
ZoneID: req.ZoneID,
}
_, err = tx.One(&zone)
if err != nil {
return
}
// Добавляем alias
_, err = tx.Insert(TeamAlias{
Name: aliasName,
TeamID: req.TeamID,
SportID: zone.SportID,
})
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) AddTeamAlias(req TeamAlias) (err error) {
req.AliasID = 0
req.Name = NormalizeNameToAlias(req.Name)
if req.Name == "" {
err = fix.Field(EmptyValue, "name")
return
}
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
// Чтобы узнать sportID сперва достаем Team
team := Team{
TeamID: req.TeamID,
}
found, err := tx.One(&team)
if err != nil {
return
}
if !found {
err = fix.Field(UnknownTeam, "teamID")
return
}
// Чтобы узнать sportID затем достаем teamZone
zone := TeamZone{
ZoneID: team.ZoneID,
}
_, err = tx.One(&zone)
if err != nil {
return
}
req.SportID = zone.SportID
/*
hasTeam, err := tx.Has("teams", "WHERE teamID=?", req.TeamID)
if err != nil {
return
}
if !hasTeam {
err = fix.Field(UnknownTeam, "teamID")
return
}
*/
hasAlias, err := tx.Has("teamAliases", "WHERE teamID=? AND name=?",
req.TeamID, req.Name)
if err != nil {
return
}
if hasAlias {
err = fix.Field(Duplicate, "name")
return
}
// Добавляем alias
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
func (s *Model) SetTeamWasLinked(teamID string) (err error) {
err = s.qx.Exec(`UPDATE teams SET wasLinked=true WHERE teamID=?`, teamID)
return
}
func (s *Model) RemoveTeamAlias(aliasID int64) (err error) {
return s.qx.Exec(`DELETE FROM teamAliases WHERE aliasID=?`, aliasID)
}
func (s *Model) AddTeamMatch(req TeamMatch) (err error) {
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
req.MatchID = strings.TrimSpace(req.MatchID)
if req.MatchID == "" {
err = fix.Field(EmptyValue, "matchID")
return
}
/*
hasChamp, err := tx.Has("teamChamps", "WHERE champID=?", req.ChampID)
if err != nil {
return
}
if !hasChamp {
err = fix.Field(UnknownChamp, "champID")
return
}
hasHomeTeam, err := tx.Has("teams", "WHERE teamID=?", req.HomeTeamID)
if err != nil {
return
}
if !hasHomeTeam {
err = fix.Field(UnknownTeam, "teamID")
return
}
hasAwayTeam, err := tx.Has("teams", "WHERE teamID=?", req.AwayTeamID)
if err != nil {
return
}
if !hasAwayTeam {
err = fix.Field(UnknownTeam, "teamID")
return
}
*/
_, err = tx.Insert(req)
if err != nil {
return
}
err = tx.Commit()
return
}
type UpdateTeamCanonicalNameIn struct {
TeamID string
CanonicalName string
NewCanonicalName string
}
func (s *Model) UpdateTeamCanonicalName(in UpdateTeamCanonicalNameIn) (err error) {
tx, err := s.qx.Tx()
if err != nil {
return
}
defer tx.Close(&err)
// Старый canonicalName не нормализуем, ибо предполагается что имя мы получили из БД
in.NewCanonicalName = strings.Title(strings.ToLower(NormalizeNameToAlias(in.NewCanonicalName)))
if in.NewCanonicalName == "" {
err = fix.Field(EmptyValue, "newCanonicalName")
return
}
// Удаляем старый alias
err = tx.Exec("DELETE FROM teamAliases WHERE teamID=? AND name=?",
in.TeamID, in.CanonicalName)
if err != nil {
return
}
// Обновляем canonicalName
err = tx.Exec("UPDATE teams SET canonicalName=? WHERE teamID=?",
in.NewCanonicalName, in.TeamID)
if err != nil {
fmt.Printf("old: %q, new: %q, teamID: %s\n", in.CanonicalName,
in.NewCanonicalName, in.TeamID)
//return
err = nil
}
hasAlias, err := tx.Has("teamAliases", "WHERE teamID=? AND name=?",
in.TeamID, in.NewCanonicalName)
if err != nil {
return
}
if !hasAlias {
// Добавляем новый alias
_, err = tx.Insert(TeamAlias{
Name: in.NewCanonicalName,
TeamID: in.TeamID,
})
if err != nil {
return
}
}
err = tx.Commit()
return
}
func (s *Model) ListTeamZones(sportID int) (list []TeamZone, err error) {
err = s.qx.ListBy(&list, `WHERE sportID=? ORDER BY name ASC`, sportID)
if err != nil {
return
}
return
}
func (s *Model) ListTeamCandidates(sportID int, name string) (matchIDs []string, err error) {
name = NormalizeNameToAlias(name)
err = s.qx.ListQuery(&matchIDs,
`SELECT teamID FROM teamAliases WHERE sportID=? AND name=?`, sportID, name)
if err != nil {
return
}
return
}
func (s *Model) ListTeamsByZone(zoneID string) (list []Team, err error) {
err = s.qx.ListBy(&list, `WHERE zoneID=? ORDER BY canonicalName ASC`, zoneID)
if err != nil {
return
}
return
}
func (s *Model) ListTeamAliases(teamID string) (list []TeamAlias, err error) {
err = s.qx.ListBy(&list, `WHERE teamID=? ORDER BY name ASC`, teamID)
if err != nil {
return
}
return
}
func (s *Model) ListTeams() (list []Team, err error) {
err = s.qx.List(&list)
if err != nil {
return
}
return
}

77
model/team.sql Normal file
View File

@@ -0,0 +1,77 @@
CREATE TABLE teamZones (
zoneID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
sportID INT NOT NULL, -- для вывода
PRIMARY KEY(zoneID),
UNIQUE(name)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE teamChamps (
champID VARCHAR(255) NOT NULL,
zoneID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(champID),
UNIQUE(zoneID, name),
FOREIGN KEY (zoneID) REFERENCES teamZones(zoneID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE teams (
teamID VARCHAR(255) NOT NULL,
zoneID VARCHAR(255) NOT NULL,
canonicalName VARCHAR(255) NOT NULL,
wasLinked BOOL NOT NULL,
PRIMARY KEY(teamID),
FOREIGN KEY (zoneID) REFERENCES teamZones(zoneID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE teamAliases (
aliasID INT NOT NULL AUTO_INCREMENT,
teamID VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
sportID INT NOT NULL, -- для поиска названий кандидатов без JOIN-ов
PRIMARY KEY(aliasID),
UNIQUE(teamID, name),
FOREIGN KEY (teamID) REFERENCES teams(teamID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
-- это просто cache
CREATE TABLE teamMatches (
matchID VARCHAR(255) NOT NULL,
homeGoals INT NOT NULL,
awayGoals INT NOT NULL,
hasScoreByPeriods BOOL NOT NULL,
p1HomeGoals INT NOT NULL,
p1AwayGoals INT NOT NULL,
p2HomeGoals INT NOT NULL,
p2AwayGoals INT NOT NULL,
isMatchScoreLoaded BOOL NOT NULL,
isScoreByPeriodsLoaded BOOL NOT NULL,
PRIMARY KEY(matchID)
)
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
--INSERT INTO footballChamps VALUES (NULL, "Seria A (Italy)");
--INSERT INTO footballChamps VALUES (NULL, "Bundesliga (Germany)");
--INSERT INTO footballChamps VALUES (NULL, "Premier League (England)");
delete from teamAliases;
delete from teamMatches;
delete from teams;
delete from teamChamps;
delete from teamZones;

136
model/types.go Executable file
View File

@@ -0,0 +1,136 @@
package model
import "gordenko.dev/dima/qx"
type Model struct {
qx *qx.Db
}
// TEAM
type TeamZone struct {
ZoneID string `json:"zoneID" qx:"table=teamZones; pk"`
Name string `json:"name"`
SportID int `json:"sportID"`
}
type TeamMatch struct {
MatchID string `json:"matchID" qx:"table=teamMatches; pk"`
//SportID int `json:"sportID"`
//StartTime int64 `json:"startTime"`
//ChampID string `json:"champID"`
//HomeTeamID string `json:"homeTeamID"`
//AwayTeamID string `json:"awayTeamID"`
HomeGoals int `json:"homeGoals"`
AwayGoals int `json:"awayGoals"`
HasScoreByPeriods bool `json:"hasScoreByPeriods"`
P1HomeGoals int `json:"p1HomeGoals"`
P1AwayGoals int `json:"p1AwayGoals"`
P2HomeGoals int `json:"p2HomeGoals"`
P2AwayGoals int `json:"p2AwayGoals"`
IsMatchScoreLoaded bool `json:"isMatchScoreLoaded"`
IsScoreByPeriodsLoaded bool `json:"isScoreByPeriodsLoaded"`
}
type Team struct {
TeamID string `json:"teamID" qx:"table=teams; pk"`
ZoneID string `json:"zoneID"`
CanonicalName string `json:"canonicalName"`
WasLinked bool `json:"wasLinked"`
}
type TeamAlias struct {
AliasID int64 `json:"aliasID" qx:"table=teamAliases; pk; seq=auto"`
Name string `json:"name"`
TeamID string `json:"teamID"`
SportID int `json:"sportID"`
}
type TeamChamp struct {
ChampID string `json:"champID" qx:"table=teamChamps; pk"`
ZoneID string `json:"zoneID"`
Name string `json:"name"`
}
// FOOTBALL
/*
type FootballZone struct {
ZoneID string `json:"zoneID" qx:"table=footballZones; pk"`
Name string `json:"name"`
}
type FootballMatch struct {
MatchID string `json:"matchID" qx:"table=footballMatches; pk"`
StartTime int64 `json:"startTime"`
ChampID string `json:"champID"`
HomeTeamID string `json:"homeTeamID"`
AwayTeamID string `json:"awayTeamID"`
HomeGoals int `json:"homeGoals"`
AwayGoals int `json:"awayGoals"`
HasStats bool `json:"hasStats"`
P1HomeGoals int `json:"p1HomeGoals"`
P1AwayGoals int `json:"p1AwayGoals"`
P2HomeGoals int `json:"p2HomeGoals"`
P2AwayGoals int `json:"p2AwayGoals"`
}
type FootballTeam struct {
TeamID string `json:"teamID" qx:"table=footballTeams; pk"`
ZoneID string `json:"zoneID"`
CanonicalName string `json:"canonicalName"`
WasLinked bool `json:"wasLinked"`
}
type FootballAlias struct {
AliasID int64 `json:"aliasID" qx:"table=footballTeamAliases; pk; seq=auto"`
Name string `json:"name"`
TeamID string `json:"teamID"`
}
type FootballChamp struct {
ChampID string `json:"champID" qx:"table=footballChamps; pk"`
ZoneID string `json:"zoneID"`
Name string `json:"name"`
}
// HANDBALL
type HandballZone struct {
ZoneID string `json:"zoneID" qx:"table=handballZones; pk"`
Name string `json:"name"`
}
type HandballMatch struct {
MatchID string `json:"matchID" qx:"table=handballMatches; pk"`
StartTime int64 `json:"startTime"`
ChampID string `json:"champID"`
HomeTeamID string `json:"homeTeamID"`
AwayTeamID string `json:"awayTeamID"`
HomeGoals int `json:"homeGoals"`
AwayGoals int `json:"awayGoals"`
HasStats bool `json:"hasStats"`
P1HomeGoals int `json:"p1HomeGoals"`
P1AwayGoals int `json:"p1AwayGoals"`
P2HomeGoals int `json:"p2HomeGoals"`
P2AwayGoals int `json:"p2AwayGoals"`
}
type HandballTeam struct {
TeamID string `json:"teamID" qx:"table=handballTeams; pk"`
ZoneID string `json:"zoneID"`
CanonicalName string `json:"canonicalName"`
WasLinked bool `json:"wasLinked"`
}
type HandballAlias struct {
AliasID int64 `json:"aliasID" qx:"table=handballTeamAliases; pk; seq=auto"`
Name string `json:"name"`
TeamID string `json:"teamID"`
}
type HandballChamp struct {
ChampID string `json:"champID" qx:"table=handballChamps; pk"`
ZoneID string `json:"zoneID"`
Name string `json:"name"`
}
*/