feat(expiry): 完成 #21 物品与设置模型实现
This commit is contained in:
+82
-10
@@ -1,19 +1,91 @@
|
||||
package expiry
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
// ExpiryItem 表示保质期物品。
|
||||
// 详细字段会在后续 issue 中完善。
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
// 物品分类
|
||||
CategoryFood = "food"
|
||||
CategoryMedicine = "medicine"
|
||||
CategoryCosmetic = "cosmetic"
|
||||
CategoryOther = "other"
|
||||
)
|
||||
|
||||
const (
|
||||
// 物品状态
|
||||
StatusNormal = "normal"
|
||||
StatusExpiring = "expiring"
|
||||
StatusExpired = "expired"
|
||||
StatusUsed = "used"
|
||||
StatusDiscarded = "discarded"
|
||||
)
|
||||
|
||||
var defaultRemindDays = []int{7, 3, 1}
|
||||
|
||||
// ExpiryItem 表示保质期物品数据模型。
|
||||
type ExpiryItem struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID uint `json:"id" gorm:"primaryKey;comment:主键ID"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index:idx_user_expiry,priority:1;index:idx_user_category,priority:1;index:idx_user_status,priority:1;comment:用户ID"`
|
||||
MiniProgramID uint `json:"mini_program_id" gorm:"not null;comment:小程序ID"`
|
||||
Name string `json:"name" gorm:"size:100;not null;comment:物品名称"`
|
||||
Category string `json:"category" gorm:"size:20;not null;index:idx_user_category,priority:2;comment:分类"`
|
||||
ProductionDate *time.Time `json:"production_date" gorm:"type:date;comment:生产日期"`
|
||||
ExpiryDate time.Time `json:"expiry_date" gorm:"type:date;not null;index:idx_user_expiry,priority:2;comment:过期日期"`
|
||||
ShelfLifeDays *int `json:"shelf_life_days" gorm:"comment:保质期天数"`
|
||||
Quantity int `json:"quantity" gorm:"default:1;comment:数量"`
|
||||
Location string `json:"location" gorm:"size:50;comment:存放位置"`
|
||||
Remark string `json:"remark" gorm:"size:255;comment:备注"`
|
||||
Status string `json:"status" gorm:"size:20;default:'normal';index:idx_user_status,priority:2;comment:状态"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"comment:创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"comment:更新时间"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index:idx_deleted_at;comment:删除时间"`
|
||||
}
|
||||
|
||||
func (ExpiryItem) TableName() string {
|
||||
return "expiry_items"
|
||||
}
|
||||
|
||||
// CalculateDaysLeft 计算当前日期与过期日期之间的天数差。
|
||||
func (item *ExpiryItem) CalculateDaysLeft() int {
|
||||
now := dateOnly(time.Now())
|
||||
expiry := dateOnly(item.ExpiryDate)
|
||||
return int(expiry.Sub(now).Hours() / 24)
|
||||
}
|
||||
|
||||
// CalculateStatus 根据过期日期动态计算状态。
|
||||
// 若已被业务标记为 used/discarded,则保留该状态。
|
||||
func (item *ExpiryItem) CalculateStatus() string {
|
||||
if item.Status == StatusUsed || item.Status == StatusDiscarded {
|
||||
return item.Status
|
||||
}
|
||||
|
||||
daysLeft := item.CalculateDaysLeft()
|
||||
if daysLeft < 0 {
|
||||
return StatusExpired
|
||||
}
|
||||
if daysLeft <= 7 {
|
||||
return StatusExpiring
|
||||
}
|
||||
return StatusNormal
|
||||
}
|
||||
|
||||
// ExpiryUserSettings 表示用户提醒设置。
|
||||
// 详细字段会在后续 issue 中完善。
|
||||
type ExpiryUserSettings struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID uint `json:"id" gorm:"primaryKey;comment:主键ID"`
|
||||
UserID uint `json:"user_id" gorm:"not null;uniqueIndex;comment:用户ID"`
|
||||
RemindDays []int `json:"remind_days" gorm:"type:json;serializer:json;comment:提醒天数"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"comment:创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"comment:更新时间"`
|
||||
}
|
||||
|
||||
func (ExpiryUserSettings) TableName() string {
|
||||
return "expiry_user_settings"
|
||||
}
|
||||
|
||||
func dateOnly(t time.Time) time.Time {
|
||||
year, month, day := t.In(time.Local).Date()
|
||||
return time.Date(year, month, day, 0, 0, 0, 0, time.Local)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package expiry
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCalculateDaysLeft(t *testing.T) {
|
||||
base := dateOnly(time.Now())
|
||||
item := &ExpiryItem{
|
||||
ExpiryDate: base.AddDate(0, 0, 5),
|
||||
}
|
||||
|
||||
if daysLeft := item.CalculateDaysLeft(); daysLeft != 5 {
|
||||
t.Fatalf("expected daysLeft=5, got %d", daysLeft)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateStatus(t *testing.T) {
|
||||
base := dateOnly(time.Now())
|
||||
tests := []struct {
|
||||
name string
|
||||
item ExpiryItem
|
||||
wantStatus string
|
||||
}{
|
||||
{
|
||||
name: "已过期",
|
||||
item: ExpiryItem{
|
||||
ExpiryDate: base.AddDate(0, 0, -1),
|
||||
},
|
||||
wantStatus: StatusExpired,
|
||||
},
|
||||
{
|
||||
name: "今天过期",
|
||||
item: ExpiryItem{
|
||||
ExpiryDate: base,
|
||||
},
|
||||
wantStatus: StatusExpiring,
|
||||
},
|
||||
{
|
||||
name: "7天内即将过期",
|
||||
item: ExpiryItem{
|
||||
ExpiryDate: base.AddDate(0, 0, 7),
|
||||
},
|
||||
wantStatus: StatusExpiring,
|
||||
},
|
||||
{
|
||||
name: "正常状态",
|
||||
item: ExpiryItem{
|
||||
ExpiryDate: base.AddDate(0, 0, 8),
|
||||
},
|
||||
wantStatus: StatusNormal,
|
||||
},
|
||||
{
|
||||
name: "保留已使用状态",
|
||||
item: ExpiryItem{
|
||||
ExpiryDate: base.AddDate(0, 0, -10),
|
||||
Status: StatusUsed,
|
||||
},
|
||||
wantStatus: StatusUsed,
|
||||
},
|
||||
{
|
||||
name: "保留已丢弃状态",
|
||||
item: ExpiryItem{
|
||||
ExpiryDate: base.AddDate(0, 0, -10),
|
||||
Status: StatusDiscarded,
|
||||
},
|
||||
wantStatus: StatusDiscarded,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.item.CalculateStatus()
|
||||
if got != tt.wantStatus {
|
||||
t.Fatalf("expected status=%s, got %s", tt.wantStatus, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user