feat: replace age integer with date_of_birth across backend and client

Store date_of_birth (DATE) instead of a static age integer so that age
is always computed dynamically from the stored date of birth.

- Migration 011: adds date_of_birth, backfills from age, drops age
- AgeFromDOB helper computes current age from YYYY-MM-DD string
- User model, repository SQL, and service validation updated
- Flutter: User.age becomes a computed getter; profile edit screen
  uses a date picker bounded to [today-120y, today-10y]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-09 23:37:58 +02:00
parent 765346d4e4
commit c9ddb708b1
13 changed files with 202 additions and 73 deletions

View File

@@ -1,6 +1,9 @@
package user
import "math"
import (
"math"
"time"
)
// Activity level multipliers (Mifflin-St Jeor).
var activityMultiplier = map[string]float64{
@@ -16,6 +19,23 @@ var goalAdjustment = map[string]float64{
"gain": 300,
}
// AgeFromDOB computes age in years from a "YYYY-MM-DD" string. Returns nil on error.
func AgeFromDOB(dob *string) *int {
if dob == nil {
return nil
}
t, err := time.Parse("2006-01-02", *dob)
if err != nil {
return nil
}
now := time.Now()
years := now.Year() - t.Year()
if now.Month() < t.Month() || (now.Month() == t.Month() && now.Day() < t.Day()) {
years--
}
return &years
}
// CalculateDailyCalories computes the daily calorie target using the
// Mifflin-St Jeor equation. Returns nil if any required parameter is missing.
func CalculateDailyCalories(heightCM *int, weightKG *float64, age *int, gender, activity, goal *string) *int {