5.6.0: Учёт баллов из драфтов в статистике недели
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m26s

This commit is contained in:
poignatov
2026-03-04 10:13:58 +03:00
parent c04422ed69
commit e457113fc9
3 changed files with 174 additions and 18 deletions

View File

@@ -1 +1 @@
5.5.0
5.6.0

View File

@@ -353,6 +353,14 @@ type Reward struct {
UseProgression bool `json:"use_progression"`
}
// calculateRewardScore вычисляет score для награды на основе progression
func calculateRewardScore(reward Reward, progressionValue *float64, progressionBase *float64) float64 {
if reward.UseProgression && progressionBase != nil && progressionValue != nil && *progressionBase != 0 {
return (*progressionValue / *progressionBase) * reward.Value
}
return reward.Value
}
type Subtask struct {
Task Task `json:"task"`
Rewards []Reward `json:"rewards"`
@@ -2769,6 +2777,18 @@ func (a *App) getWeeklyStatsHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Получаем pending баллы из драфтов с auto_complete=true
draftPendingScores, err := a.getDraftPendingScores(userID)
if err != nil {
log.Printf("Error getting draft pending scores: %v", err)
// Не прерываем выполнение, продолжаем без pending scores
} else {
// Добавляем pending scores к currentWeekScores
for projectID, pendingScore := range draftPendingScores {
currentWeekScores[projectID] += pendingScore
}
}
// Получаем сегодняшние приросты
todayScores, err := a.getTodayScores(userID)
if err != nil {
@@ -3202,6 +3222,146 @@ func (a *App) getCurrentWeekScores(userID int) (map[int]float64, error) {
return scores, nil
}
// getDraftPendingScores получает потенциальные баллы из драфтов с auto_complete=true
// Эти баллы будут начислены при автовыполнении задач в конце дня
// Возвращает map[project_id]pending_score
func (a *App) getDraftPendingScores(userID int) (map[int]float64, error) {
// Получаем все драфты с auto_complete=true для пользователя
// Включаем progression_base из задачи для расчёта score
query := `
SELECT
td.task_id,
td.progression_value,
t.progression_base
FROM task_drafts td
JOIN tasks t ON td.task_id = t.id
WHERE td.user_id = $1 AND td.auto_complete = TRUE AND t.deleted = FALSE
`
rows, err := a.DB.Query(query, userID)
if err != nil {
log.Printf("Error querying draft pending scores: %v", err)
return nil, fmt.Errorf("error querying draft pending scores: %w", err)
}
defer rows.Close()
scores := make(map[int]float64)
for rows.Next() {
var taskID int
var progressionValue sql.NullFloat64
var progressionBase sql.NullFloat64
if err := rows.Scan(&taskID, &progressionValue, &progressionBase); err != nil {
log.Printf("Error scanning draft row: %v", err)
continue
}
// Получаем reward_configs для основной задачи
rewardRows, err := a.DB.Query(`
SELECT rc.project_id, rc.value, rc.use_progression
FROM reward_configs rc
WHERE rc.task_id = $1
`, taskID)
if err != nil {
log.Printf("Error querying task rewards for draft: %v", err)
continue
}
var progressionValuePtr *float64
if progressionValue.Valid {
progressionValuePtr = &progressionValue.Float64
}
var progressionBasePtr *float64
if progressionBase.Valid {
progressionBasePtr = &progressionBase.Float64
}
for rewardRows.Next() {
var projectID int
var rewardValue float64
var useProgression bool
if err := rewardRows.Scan(&projectID, &rewardValue, &useProgression); err != nil {
log.Printf("Error scanning reward row: %v", err)
continue
}
reward := Reward{
Value: rewardValue,
UseProgression: useProgression,
}
score := calculateRewardScore(reward, progressionValuePtr, progressionBasePtr)
scores[projectID] += score
}
rewardRows.Close()
// Получаем отмеченные подзадачи из драфта
subtaskRows, err := a.DB.Query(`
SELECT tds.subtask_id, t.progression_base
FROM task_draft_subtasks tds
JOIN task_drafts td ON tds.task_draft_id = td.id
JOIN tasks t ON tds.subtask_id = t.id
WHERE td.task_id = $1 AND td.user_id = $2
`, taskID, userID)
if err != nil {
log.Printf("Error querying draft subtasks: %v", err)
continue
}
for subtaskRows.Next() {
var subtaskID int
var subtaskProgressionBase sql.NullFloat64
if err := subtaskRows.Scan(&subtaskID, &subtaskProgressionBase); err != nil {
log.Printf("Error scanning subtask row: %v", err)
continue
}
// Определяем progression_base для подзадачи
var subtaskProgressionBasePtr *float64
if subtaskProgressionBase.Valid {
subtaskProgressionBasePtr = &subtaskProgressionBase.Float64
} else if progressionBase.Valid {
subtaskProgressionBasePtr = &progressionBase.Float64
}
// Получаем награды подзадачи
subtaskRewardRows, err := a.DB.Query(`
SELECT rc.project_id, rc.value, rc.use_progression
FROM reward_configs rc
WHERE rc.task_id = $1
`, subtaskID)
if err != nil {
log.Printf("Error querying subtask rewards: %v", err)
continue
}
for subtaskRewardRows.Next() {
var projectID int
var rewardValue float64
var useProgression bool
if err := subtaskRewardRows.Scan(&projectID, &rewardValue, &useProgression); err != nil {
log.Printf("Error scanning subtask reward row: %v", err)
continue
}
reward := Reward{
Value: rewardValue,
UseProgression: useProgression,
}
score := calculateRewardScore(reward, progressionValuePtr, subtaskProgressionBasePtr)
scores[projectID] += score
}
subtaskRewardRows.Close()
}
subtaskRows.Close()
}
return scores, nil
}
// getTodayScores получает сумму score всех нод, созданных сегодня для конкретного пользователя
// Возвращает map[project_id]today_score для сегодняшнего дня
func (a *App) getTodayScores(userID int) (map[int]float64, error) {
@@ -9471,20 +9631,18 @@ func (a *App) executeTask(taskID int, userID int, req CompleteTaskRequest) error
// Вычисляем score для каждой награды и формируем строки для подстановки
rewardStrings := make(map[int]string)
for _, reward := range rewards {
var score float64
if reward.UseProgression && progressionBase.Valid && req.Value != nil {
score = (*req.Value / progressionBase.Float64) * reward.Value
} else {
score = reward.Value
var progressionBasePtr *float64
if progressionBase.Valid {
progressionBasePtr = &progressionBase.Float64
}
for _, reward := range rewards {
score := calculateRewardScore(reward, req.Value, progressionBasePtr)
// Формируем строку награды
var rewardStr string
if score >= 0 {
rewardStr = fmt.Sprintf("**%s+%.4g**", reward.ProjectName, score)
} else {
// Убираем знак минуса из числа (используем абсолютное значение)
rewardStr = fmt.Sprintf("**%s-%.4g**", reward.ProjectName, math.Abs(score))
}
rewardStrings[reward.Position] = rewardStr
@@ -9617,16 +9775,14 @@ func (a *App) executeTask(taskID int, userID int, req CompleteTaskRequest) error
// Вычисляем score для наград подзадачи
subtaskRewardStrings := make(map[int]string)
for _, reward := range subtaskRewards {
var score float64
if reward.UseProgression && subtaskProgressionBase.Valid && req.Value != nil {
score = (*req.Value / subtaskProgressionBase.Float64) * reward.Value
} else if reward.UseProgression && progressionBase.Valid && req.Value != nil {
// Если у подзадачи нет progression_base, используем основной
score = (*req.Value / progressionBase.Float64) * reward.Value
} else {
score = reward.Value
var subtaskProgressionBasePtr *float64
if subtaskProgressionBase.Valid {
subtaskProgressionBasePtr = &subtaskProgressionBase.Float64
} else if progressionBase.Valid {
subtaskProgressionBasePtr = &progressionBase.Float64
}
for _, reward := range subtaskRewards {
score := calculateRewardScore(reward, req.Value, subtaskProgressionBasePtr)
var rewardStr string
if score >= 0 {

View File

@@ -1,6 +1,6 @@
{
"name": "play-life-web",
"version": "5.5.0",
"version": "5.6.0",
"type": "module",
"scripts": {
"dev": "vite",