chore: 同步本地 v9 整改与运营能力
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
package service
|
||||
|
||||
// ProductionLossRow 产量损失统计原始行。
|
||||
type ProductionLossRow struct {
|
||||
BatchID string `gorm:"column:batch_id"`
|
||||
RoomID string `gorm:"column:room_id"`
|
||||
DeathCount int64 `gorm:"column:death_count"`
|
||||
CulledCount int64 `gorm:"column:culled_count"`
|
||||
YieldKg float64 `gorm:"column:yield_kg"`
|
||||
LossKg float64 `gorm:"column:loss_kg"`
|
||||
CostAmount float64 `gorm:"column:cost_amount"`
|
||||
RecordCount int64 `gorm:"column:record_count"`
|
||||
}
|
||||
|
||||
// ProductionLossStat 按房间/批次聚合后的产量损失统计。
|
||||
type ProductionLossStat struct {
|
||||
BatchID string `json:"batchId"`
|
||||
RoomID string `json:"roomId"`
|
||||
RoomName string `json:"roomName,omitempty"`
|
||||
BatchName string `json:"batchName,omitempty"`
|
||||
DeathCount int64 `json:"deathCount"`
|
||||
CulledCount int64 `json:"culledCount"`
|
||||
YieldKg float64 `json:"yieldKg"`
|
||||
LossKg float64 `json:"lossKg"`
|
||||
CostAmount float64 `json:"costAmount"`
|
||||
RecordCount int64 `json:"recordCount"`
|
||||
}
|
||||
|
||||
// AggregateProductionLoss 聚合产量损失记录;空输入返回空数组。
|
||||
func AggregateProductionLoss(rows []ProductionLossRow) []ProductionLossStat {
|
||||
byKey := make(map[string]*ProductionLossStat)
|
||||
var order []string
|
||||
for _, r := range rows {
|
||||
key := r.RoomID + "|" + r.BatchID
|
||||
stat, ok := byKey[key]
|
||||
if !ok {
|
||||
stat = &ProductionLossStat{RoomID: r.RoomID, BatchID: r.BatchID}
|
||||
byKey[key] = stat
|
||||
order = append(order, key)
|
||||
}
|
||||
stat.DeathCount += r.DeathCount
|
||||
stat.CulledCount += r.CulledCount
|
||||
stat.YieldKg += r.YieldKg
|
||||
stat.LossKg += r.LossKg
|
||||
stat.CostAmount += r.CostAmount
|
||||
stat.RecordCount += r.RecordCount
|
||||
}
|
||||
result := make([]ProductionLossStat, 0, len(order))
|
||||
for _, key := range order {
|
||||
result = append(result, *byKey[key])
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAggregateProductionLoss(t *testing.T) {
|
||||
rows := []ProductionLossRow{
|
||||
{RoomID: "room-a", DeathCount: 10, CulledCount: 2, YieldKg: 5, LossKg: 1, CostAmount: 20, RecordCount: 1},
|
||||
{RoomID: "room-a", DeathCount: 3, CulledCount: 1, YieldKg: 2, LossKg: 0.5, CostAmount: 30, RecordCount: 1},
|
||||
}
|
||||
stats := AggregateProductionLoss(rows)
|
||||
if len(stats) != 1 {
|
||||
t.Fatalf("stats len = %d, want 1", len(stats))
|
||||
}
|
||||
if stats[0].DeathCount != 13 || stats[0].CulledCount != 3 || stats[0].CostAmount != 50 {
|
||||
t.Fatalf("unexpected aggregate: %+v", stats[0])
|
||||
}
|
||||
if stats[0].RecordCount != 2 {
|
||||
t.Fatalf("record count = %d, want 2", stats[0].RecordCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateProductionLossEmpty(t *testing.T) {
|
||||
if stats := AggregateProductionLoss(nil); len(stats) != 0 {
|
||||
t.Fatalf("empty input should return empty, got %d", len(stats))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user