实现库存管理相关逻辑

This commit is contained in:
2025-11-25 18:10:28 +08:00
parent ae27eb142d
commit 44ff3b19d6
15 changed files with 1531 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
@@ -18,6 +19,16 @@ type RawMaterialListOptions struct {
OrderBy string
}
// StockLogListOptions 定义了查询库存日志列表时的筛选条件
type StockLogListOptions struct {
RawMaterialID *uint32
RawMaterialName *string
SourceTypes []models.StockLogSourceType
StartTime *time.Time
EndTime *time.Time
OrderBy string
}
// RawMaterialRepository 定义了与原料相关的数据库操作接口
type RawMaterialRepository interface {
CreateRawMaterial(ctx context.Context, rawMaterial *models.RawMaterial) error
@@ -32,6 +43,10 @@ type RawMaterialRepository interface {
// 库存日志相关方法
CreateRawMaterialStockLog(ctx context.Context, log *models.RawMaterialStockLog) error
GetLatestRawMaterialStockLog(ctx context.Context, rawMaterialID uint32) (*models.RawMaterialStockLog, error)
// BatchGetLatestStockLogsForMaterials 批量获取一组原料的最新库存日志
BatchGetLatestStockLogsForMaterials(ctx context.Context, materialIDs []uint32) (map[uint32]models.RawMaterialStockLog, error)
// ListStockLogs 分页列出库存变动日志
ListStockLogs(ctx context.Context, opts StockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error)
}
// gormRawMaterialRepository 是 RawMaterialRepository 的 GORM 实现
@@ -219,3 +234,87 @@ func (r *gormRawMaterialRepository) GetLatestRawMaterialStockLog(ctx context.Con
}
return &latestLog, nil
}
// BatchGetLatestStockLogsForMaterials 批量获取一组原料的最新库存日志
func (r *gormRawMaterialRepository) BatchGetLatestStockLogsForMaterials(ctx context.Context, materialIDs []uint32) (map[uint32]models.RawMaterialStockLog, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "BatchGetLatestStockLogsForMaterials")
if len(materialIDs) == 0 {
return make(map[uint32]models.RawMaterialStockLog), nil
}
var latestLogs []models.RawMaterialStockLog
// 使用窗口函数 ROW_NUMBER() 来为每个原料的日志分区,并按时间倒序排名。
// 这样可以高效地一次性查询出每个原料的最新一条日志。
subQuery := r.db.Model(&models.RawMaterialStockLog{}).
Select("*, ROW_NUMBER() OVER(PARTITION BY raw_material_id ORDER BY happened_at DESC, id DESC) as rn").
Where("raw_material_id IN ?", materialIDs)
err := r.db.WithContext(repoCtx).
Table("(?) as sub", subQuery).
Where("rn = 1").
Find(&latestLogs).Error
if err != nil {
return nil, fmt.Errorf("批量获取最新库存日志失败: %w", err)
}
// 将结果转换为 map[uint32]models.RawMaterialStockLog 以方便查找
logMap := make(map[uint32]models.RawMaterialStockLog, len(latestLogs))
for _, log := range latestLogs {
logMap[log.RawMaterialID] = log
}
return logMap, nil
}
// ListStockLogs 分页列出库存变动日志
func (r *gormRawMaterialRepository) ListStockLogs(ctx context.Context, opts StockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListStockLogs")
var logs []models.RawMaterialStockLog
var total int64
db := r.db.WithContext(repoCtx).Model(&models.RawMaterialStockLog{})
// 应用筛选条件
if opts.RawMaterialID != nil {
db = db.Where("raw_material_id = ?", *opts.RawMaterialID)
}
// 新增:按原料名称模糊搜索
if opts.RawMaterialName != nil && *opts.RawMaterialName != "" {
// 使用子查询找到匹配的原料ID
subQuery := r.db.Model(&models.RawMaterial{}).Select("id").Where("name LIKE ?", "%"+*opts.RawMaterialName+"%")
db = db.Where("raw_material_id IN (?)", subQuery)
}
if len(opts.SourceTypes) > 0 {
db = db.Where("source_type IN ?", opts.SourceTypes)
}
if opts.StartTime != nil {
db = db.Where("happened_at >= ?", *opts.StartTime)
}
if opts.EndTime != nil {
db = db.Where("happened_at <= ?", *opts.EndTime)
}
// 首先计算总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("统计库存日志总数失败: %w", err)
}
// 然后应用排序、分页并获取数据
if opts.OrderBy != "" {
db = db.Order(opts.OrderBy)
} else {
// 默认排序
db = db.Order("happened_at DESC, id DESC")
}
offset := (page - 1) * pageSize
if err := db.Preload("RawMaterial").Offset(offset).Limit(pageSize).Find(&logs).Error; err != nil {
return nil, 0, fmt.Errorf("查询库存日志列表失败: %w", err)
}
return logs, total, nil
}