You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
3.8 KiB
Go
146 lines
3.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"github.com/denisovdennis/autohero/internal/storage"
|
|
)
|
|
|
|
// DailyTaskHandler serves daily/weekly task API endpoints.
|
|
type DailyTaskHandler struct {
|
|
taskStore *storage.DailyTaskStore
|
|
heroStore *storage.HeroStore
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// NewDailyTaskHandler creates a new DailyTaskHandler.
|
|
func NewDailyTaskHandler(taskStore *storage.DailyTaskStore, heroStore *storage.HeroStore, logger *slog.Logger) *DailyTaskHandler {
|
|
return &DailyTaskHandler{
|
|
taskStore: taskStore,
|
|
heroStore: heroStore,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// ListHeroTasks returns current daily and weekly tasks with progress.
|
|
// GET /api/v1/hero/tasks
|
|
func (h *DailyTaskHandler) ListHeroTasks(w http.ResponseWriter, r *http.Request) {
|
|
telegramID, ok := resolveTelegramID(r)
|
|
if !ok {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{
|
|
"error": "missing telegramId",
|
|
})
|
|
return
|
|
}
|
|
|
|
hero, err := h.heroStore.GetByTelegramID(r.Context(), telegramID)
|
|
if err != nil {
|
|
h.logger.Error("failed to get hero for tasks", "error", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{
|
|
"error": "failed to load hero",
|
|
})
|
|
return
|
|
}
|
|
if hero == nil {
|
|
writeJSON(w, http.StatusNotFound, map[string]string{
|
|
"error": "hero not found",
|
|
})
|
|
return
|
|
}
|
|
|
|
// Lazily create task rows for the current period.
|
|
now := time.Now()
|
|
if err := h.taskStore.EnsureHeroTasks(r.Context(), hero.ID, now); err != nil {
|
|
h.logger.Error("failed to ensure hero tasks", "hero_id", hero.ID, "error", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{
|
|
"error": "failed to initialize tasks",
|
|
})
|
|
return
|
|
}
|
|
|
|
tasks, err := h.taskStore.ListHeroTasks(r.Context(), hero.ID)
|
|
if err != nil {
|
|
h.logger.Error("failed to list hero tasks", "hero_id", hero.ID, "error", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{
|
|
"error": "failed to load tasks",
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, tasks)
|
|
}
|
|
|
|
// ClaimTask claims a completed task's reward.
|
|
// POST /api/v1/hero/tasks/{taskId}/claim
|
|
func (h *DailyTaskHandler) ClaimTask(w http.ResponseWriter, r *http.Request) {
|
|
telegramID, ok := resolveTelegramID(r)
|
|
if !ok {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{
|
|
"error": "missing telegramId",
|
|
})
|
|
return
|
|
}
|
|
|
|
taskID := chi.URLParam(r, "taskId")
|
|
if taskID == "" {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{
|
|
"error": "taskId is required",
|
|
})
|
|
return
|
|
}
|
|
|
|
hero, err := h.heroStore.GetByTelegramID(r.Context(), telegramID)
|
|
if err != nil {
|
|
h.logger.Error("failed to get hero for task claim", "error", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{
|
|
"error": "failed to load hero",
|
|
})
|
|
return
|
|
}
|
|
if hero == nil {
|
|
writeJSON(w, http.StatusNotFound, map[string]string{
|
|
"error": "hero not found",
|
|
})
|
|
return
|
|
}
|
|
|
|
reward, err := h.taskStore.ClaimTask(r.Context(), hero.ID, taskID)
|
|
if err != nil {
|
|
h.logger.Warn("failed to claim task", "hero_id", hero.ID, "task_id", taskID, "error", err)
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
// Apply reward to hero.
|
|
switch reward.RewardType {
|
|
case "gold":
|
|
hero.Gold += int64(reward.RewardAmount)
|
|
case "potion":
|
|
hero.Potions += reward.RewardAmount
|
|
}
|
|
|
|
if err := h.heroStore.Save(r.Context(), hero); err != nil {
|
|
h.logger.Error("failed to save hero after task claim", "hero_id", hero.ID, "error", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{
|
|
"error": "failed to save hero",
|
|
})
|
|
return
|
|
}
|
|
|
|
h.logger.Info("task reward claimed", "hero_id", hero.ID, "task_id", taskID,
|
|
"reward_type", reward.RewardType, "reward_amount", reward.RewardAmount)
|
|
|
|
now := time.Now()
|
|
hero.RefreshDerivedCombatStats(now)
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"reward": reward,
|
|
"hero": hero,
|
|
})
|
|
}
|