madness continues
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"synlotto-website/helpers"
|
||||
"synlotto-website/services"
|
||||
services "synlotto-website/services/tickets"
|
||||
)
|
||||
|
||||
func AdminTriggersHandler(db *sql.DB) http.HandlerFunc {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
"synlotto-website/helpers"
|
||||
"synlotto-website/models"
|
||||
draws "synlotto-website/services/draws"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/csrf"
|
||||
@@ -312,8 +313,13 @@ func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
||||
continue
|
||||
}
|
||||
|
||||
t.Ball1, t.Ball2, t.Ball3 = int(b1.Int64), int(b2.Int64), int(b3.Int64)
|
||||
t.Ball4, t.Ball5, t.Ball6 = int(b4.Int64), int(b5.Int64), int(b6.Int64)
|
||||
// Build primary number + bonus fields
|
||||
t.Ball1 = int(b1.Int64)
|
||||
t.Ball2 = int(b2.Int64)
|
||||
t.Ball3 = int(b3.Int64)
|
||||
t.Ball4 = int(b4.Int64)
|
||||
t.Ball5 = int(b5.Int64)
|
||||
t.Ball6 = int(b6.Int64)
|
||||
t.Bonus1 = helpers.IntPtrIfValid(bo1)
|
||||
t.Bonus2 = helpers.IntPtrIfValid(bo2)
|
||||
|
||||
@@ -330,9 +336,19 @@ func GetMyTickets(db *sql.DB) http.HandlerFunc {
|
||||
t.IsWinner = isWinner.Bool
|
||||
}
|
||||
|
||||
// Build balls slices (for template use)
|
||||
t.Balls = helpers.BuildBallsSlice(t)
|
||||
t.BonusBalls = helpers.BuildBonusSlice(t)
|
||||
|
||||
// 🎯 Get the actual draw info (used to show which numbers matched)
|
||||
draw := draws.GetDrawResultForTicket(db, t.GameType, t.DrawDate)
|
||||
t.MatchedDraw = draw
|
||||
|
||||
// ✅ DEBUG
|
||||
log.Printf("✅ Ticket #%d", t.Id)
|
||||
log.Printf("Balls: %v", t.Balls)
|
||||
log.Printf("DrawResult: %+v", draw)
|
||||
|
||||
tickets = append(tickets, t)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ func BuildBallsSlice(t models.Ticket) []int {
|
||||
if t.GameType == "Lotto" && t.Ball6 > 0 {
|
||||
balls = append(balls, t.Ball6)
|
||||
}
|
||||
|
||||
return balls
|
||||
}
|
||||
|
||||
@@ -18,5 +19,6 @@ func BuildBonusSlice(t models.Ticket) []int {
|
||||
if t.Bonus2 != nil {
|
||||
bonuses = append(bonuses, *t.Bonus2)
|
||||
}
|
||||
|
||||
return bonuses
|
||||
}
|
||||
|
||||
16
helpers/match.go
Normal file
16
helpers/match.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package helpers
|
||||
|
||||
func CountMatches(a, b []int) int {
|
||||
m := make(map[int]bool)
|
||||
for _, n := range b {
|
||||
m[n] = true
|
||||
}
|
||||
match := 0
|
||||
for _, n := range a {
|
||||
if m[n] {
|
||||
match++
|
||||
}
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
@@ -69,5 +69,6 @@ func InSlice(n int, list []int) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"synlotto-website/helpers"
|
||||
"synlotto-website/models"
|
||||
)
|
||||
|
||||
func MatchTicketToDraw(ticket models.MatchTicket, draw models.DrawResult, rules []models.PrizeRule) models.MatchResult {
|
||||
mainMatches := helpers.CountMatches(ticket.Balls, draw.Balls)
|
||||
bonusMatches := helpers.CountMatches(ticket.BonusBalls, draw.BonusBalls)
|
||||
|
||||
prizeTier := getPrizeTier(ticket.GameType, mainMatches, bonusMatches, rules)
|
||||
isWinner := prizeTier != ""
|
||||
|
||||
return models.MatchResult{
|
||||
MatchedDrawID: draw.DrawID,
|
||||
MatchedMain: mainMatches,
|
||||
MatchedBonus: bonusMatches,
|
||||
PrizeTier: prizeTier,
|
||||
IsWinner: isWinner,
|
||||
}
|
||||
}
|
||||
|
||||
func getPrizeTier(game string, main, bonus int, rules []models.PrizeRule) string {
|
||||
for _, rule := range rules {
|
||||
if rule.Game == game && rule.MainMatches == main && rule.BonusMatches == bonus {
|
||||
return rule.Tier
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -24,4 +24,5 @@ type Ticket struct {
|
||||
// Used only for display these are not stored in the DB, they mirror MatchTicket structure but are populated on read.
|
||||
Balls []int
|
||||
BonusBalls []int
|
||||
MatchedDraw DrawResult
|
||||
}
|
||||
|
||||
@@ -8,22 +8,31 @@ import (
|
||||
|
||||
func GetDrawResultForTicket(db *sql.DB, game string, drawDate string) models.DrawResult {
|
||||
var result models.DrawResult
|
||||
|
||||
if game != "Thunderball" {
|
||||
log.Printf("Draw lookup for unsupported game type: %s", game)
|
||||
return result
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT id, ball1, ball2, ball3, ball4, ball5, bonus1
|
||||
SELECT id, ball1, ball2, ball3, ball4, ball5, thunderball
|
||||
FROM results_thunderball
|
||||
WHERE draw_date = ?
|
||||
`
|
||||
var b1, b2, b3, b4, b5, bonus sql.NullInt64
|
||||
err := db.QueryRow(query, drawDate).Scan(&result.DrawID, &b1, &b2, &b3, &b4, &b5, &bonus)
|
||||
|
||||
var b1, b2, b3, b4, b5, tb sql.NullInt64
|
||||
err := db.QueryRow(query, drawDate).Scan(&result.DrawID, &b1, &b2, &b3, &b4, &b5, &tb)
|
||||
if err != nil {
|
||||
log.Printf("No draw found for %s %s: %v", game, drawDate, err)
|
||||
return result
|
||||
}
|
||||
|
||||
result.GameType = game
|
||||
result.DrawDate = drawDate
|
||||
result.Balls = []int{int(b1.Int64), int(b2.Int64), int(b3.Int64), int(b4.Int64), int(b5.Int64)}
|
||||
if bonus.Valid {
|
||||
result.BonusBalls = []int{int(bonus.Int64)}
|
||||
if tb.Valid {
|
||||
result.BonusBalls = []int{int(tb.Int64)}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"synlotto-website/helpers"
|
||||
"synlotto-website/models"
|
||||
"synlotto-website/rules"
|
||||
draws "synlotto-website/services/draws"
|
||||
)
|
||||
|
||||
func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, error) {
|
||||
@@ -24,6 +25,9 @@ func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, er
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Buffer results to avoid writing while iterating
|
||||
var pending []models.Ticket
|
||||
|
||||
for rows.Next() {
|
||||
var t models.Ticket
|
||||
var b1, b2, b3, b4, b5, b6, bo1, bo2 sql.NullInt64
|
||||
@@ -45,6 +49,10 @@ func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, er
|
||||
t.Bonus1 = helpers.IntPtrIfValid(bo1)
|
||||
t.Bonus2 = helpers.IntPtrIfValid(bo2)
|
||||
|
||||
pending = append(pending, t)
|
||||
}
|
||||
|
||||
for _, t := range pending {
|
||||
matchTicket := models.MatchTicket{
|
||||
ID: t.Id,
|
||||
GameType: t.GameType,
|
||||
@@ -53,14 +61,14 @@ func RunTicketMatching(db *sql.DB, triggeredBy string) (models.MatchRunStats, er
|
||||
BonusBalls: helpers.BuildBonusSlice(t),
|
||||
}
|
||||
|
||||
draw := GetDrawResultForTicket(db, t.GameType, t.DrawDate)
|
||||
draw := draws.GetDrawResultForTicket(db, t.GameType, t.DrawDate)
|
||||
result := handlers.MatchTicketToDraw(matchTicket, draw, rules.ThunderballPrizeRules)
|
||||
|
||||
if result.MatchedDrawID == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = db.Exec(`
|
||||
_, err := db.Exec(`
|
||||
UPDATE my_tickets
|
||||
SET matched_main = ?, matched_bonus = ?, prize_tier = ?, is_winner = ?
|
||||
WHERE id = ?
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
margin-right: 6px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.ball-container {
|
||||
@@ -45,16 +46,50 @@
|
||||
}
|
||||
|
||||
.ball.bonus {
|
||||
background-color: #d8b4fe; /* Light purple */
|
||||
background-color: #d8b4fe;
|
||||
}
|
||||
|
||||
.ball.matched {
|
||||
background-color: #4ade80; /* Green */
|
||||
background-color: #4ade80 !important;
|
||||
animation: pulse 1s ease-in-out infinite;
|
||||
color: white !important;
|
||||
border: 2px solid red !important;
|
||||
}
|
||||
|
||||
.ball.game-thunderball {
|
||||
background-color: #1e3a8a; /* Dark blue */
|
||||
color: white;
|
||||
border: 2px solid white;
|
||||
border-radius: 12px; /* Remove side rounding */
|
||||
}
|
||||
|
||||
/* Lotto main balls – Orange */
|
||||
.ball.game-lotto {
|
||||
background-color: #f97316; /* Bright orange */
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* Optional: Euromillions, Set For Life – define as needed */
|
||||
.ball.game-euromillions {
|
||||
background-color: #2563eb; /* Royal blue */
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.ball.game-setforlife {
|
||||
background-color: #6b7280; /* Slate gray */
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
50% { transform: scale(1.15); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 0.8s ease-in-out infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
@@ -24,14 +24,16 @@
|
||||
<td>{{ .DrawDate }}</td>
|
||||
<td>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{{ range .Balls }}
|
||||
<div class="ball">{{ . }}</div>
|
||||
{{ range $i, $ball := .Balls }}
|
||||
<div class="ball {{ if inSlice $ball $.MatchedDraw.Balls }}matched pulse{{ end }}">{{ $ball }}</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div style="color: red; font-size: 0.75rem; margin-top: 0.25rem;">
|
||||
MatchedMain: {{ .MatchedMain }} | MatchedBonus: {{ .MatchedBonus }}
|
||||
{{ if or (gt .MatchedMain 0) (gt .MatchedBonus 0) }}
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ .MatchedMain }} match{{ if ne .MatchedMain 1 }}es{{ end }}
|
||||
{{ if gt .MatchedBonus 0 }}, {{ .MatchedBonus }} bonus{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
{{ if eq .GameType "Lotto" }}
|
||||
@@ -39,12 +41,21 @@
|
||||
{{ else if gt (intVal .Bonus2) 0 }}
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{{ if gt (intVal .Bonus1) 0 }}
|
||||
<div class="ball bonus">{{ intVal .Bonus1 }}</div>
|
||||
{{ $b1 := intVal .Bonus1 }}
|
||||
<div class="ball bonus {{ if inSlice $b1 $.MatchedDraw.BonusBalls }}matched-bonus{{ end }}">
|
||||
{{ $b1 }}
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="ball bonus">{{ intVal .Bonus2 }}</div>
|
||||
{{ $b2 := intVal .Bonus2 }}
|
||||
<div class="ball bonus {{ if inSlice $b2 $.MatchedDraw.BonusBalls }}matched-bonus{{ end }}">
|
||||
{{ $b2 }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else if gt (intVal .Bonus1) 0 }}
|
||||
<div class="ball bonus">{{ intVal .Bonus1 }}</div>
|
||||
{{ $b := intVal .Bonus1 }}
|
||||
<div class="ball bonus {{ if inSlice $b $.MatchedDraw.BonusBalls }}matched-bonus{{ end }}">
|
||||
{{ $b }}
|
||||
</div>
|
||||
{{ else }}
|
||||
<span style="color: lightgray;">–</span>
|
||||
{{ end }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{ define "layout" }}
|
||||
<!DOCTYPE html>
|
||||
<link rel="stylesheet" href="/static/css/site.css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.3.5/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/css/site.css">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
Reference in New Issue
Block a user