fix: resolve go vet error and dayStart logic in ComputeDailyTotals

- Fix 'string(d)' int-to-rune conversion in rpg_test.go by using fmt.Sprintf
- Fix ComputeDailyTotals dayStart logic: initial state should be StateWorking
  when dayStart > 0, correctly counting work from dayStart to first out event
- Update README project structure to reflect new domain/handler/repo layout
- Implement missing achievements: rpg_pioneer (on RPG enable) and
  salary_setter (on first rate set)
- Add tryUnlockAchievement helper to Handler
This commit is contained in:
2026-06-25 09:54:22 +03:30
parent 3835b002a7
commit d15ed46066
45 changed files with 4947 additions and 5430 deletions

68
internal/domain/totals.go Normal file
View File

@@ -0,0 +1,68 @@
package domain
import "sort"
// ComputeDailyTotals calculates the total work and break time from a list of events.
func ComputeDailyTotals(events []Event, minBreakThreshold int64, dayStart, dayEnd int64) DailyTotals {
if len(events) == 0 {
return DailyTotals{}
}
sorted := make([]Event, len(events))
copy(sorted, events)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].OccurredAt < sorted[j].OccurredAt
})
var totalWork, totalBreak int64
state := StateIdle
var workStart, breakStart int64
if dayStart > 0 {
state = StateWorking
workStart = dayStart
} else if sorted[0].EventType == "out" {
state = StateOnBreak
}
for _, e := range sorted {
switch {
case e.EventType == "in" && state == StateIdle:
state = StateWorking
workStart = e.OccurredAt
case e.EventType == "in" && state == StateOnBreak:
breakElapsed := e.OccurredAt - breakStart
if breakElapsed >= minBreakThreshold {
totalBreak += breakElapsed
} else {
totalWork += breakElapsed
}
state = StateWorking
workStart = e.OccurredAt
case e.EventType == "in" && state == StateWorking:
case e.EventType == "out" && state == StateWorking:
totalWork += e.OccurredAt - workStart
state = StateOnBreak
breakStart = e.OccurredAt
case e.EventType == "out" && state == StateOnBreak:
breakStart = e.OccurredAt
case e.EventType == "out" && state == StateIdle:
state = StateOnBreak
breakStart = e.OccurredAt
}
}
if state == StateWorking && dayEnd > 0 {
totalWork += dayEnd - workStart
}
if state == StateOnBreak && dayEnd > 0 {
breakElapsed := dayEnd - breakStart
if breakElapsed >= minBreakThreshold {
totalBreak += breakElapsed
} else {
totalWork += breakElapsed
}
}
return DailyTotals{TotalSeconds: totalWork, BreakSeconds: totalBreak}
}