Files
pig-farm-controller/internal/app/service/inventory_service.go

159 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"errors"
"fmt"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/inventory"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
)
// 定义库存应用服务特定的错误
var (
ErrInventoryRawMaterialNotFound = errors.New("原料不存在")
ErrInventoryInsufficientStock = errors.New("原料库存不足")
)
// InventoryService 定义了库存相关的应用服务接口
type InventoryService interface {
AdjustStock(ctx context.Context, req *dto.StockAdjustmentRequest) (*dto.StockLogResponse, error)
ListCurrentStock(ctx context.Context, req *dto.ListCurrentStockRequest) (*dto.ListCurrentStockResponse, error)
ListStockLogs(ctx context.Context, req *dto.ListStockLogRequest) (*dto.ListStockLogResponse, error)
}
// inventoryServiceImpl 是 InventoryService 接口的实现
type inventoryServiceImpl struct {
ctx context.Context
invSvc inventory.InventoryCoreService
rawMatRepo repository.RawMaterialRepository
}
// NewInventoryService 创建一个新的 InventoryService 实例
func NewInventoryService(ctx context.Context, invSvc inventory.InventoryCoreService, rawMatRepo repository.RawMaterialRepository) InventoryService {
return &inventoryServiceImpl{
ctx: ctx,
invSvc: invSvc,
rawMatRepo: rawMatRepo,
}
}
// AdjustStock 手动调整库存
func (s *inventoryServiceImpl) AdjustStock(ctx context.Context, req *dto.StockAdjustmentRequest) (*dto.StockLogResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "AdjustStock")
// 调用领域服务执行核心业务逻辑
log, err := s.invSvc.AdjustStock(serviceCtx, req.RawMaterialID, req.ChangeAmount, models.StockLogSourceManual, nil, req.Remarks)
if err != nil {
if errors.Is(err, inventory.ErrRawMaterialNotFound) {
return nil, ErrInventoryRawMaterialNotFound
}
if errors.Is(err, inventory.ErrInsufficientStock) {
return nil, ErrInventoryInsufficientStock
}
return nil, fmt.Errorf("调整库存失败: %w", err)
}
// 手动加载 RawMaterial 信息,因为 CreateRawMaterialStockLog 不会预加载它
rawMaterial, err := s.rawMatRepo.GetRawMaterialByID(serviceCtx, log.RawMaterialID)
if err != nil {
// 理论上不应该发生,因为 AdjustStock 内部已经检查过
return nil, fmt.Errorf("获取原料信息失败: %w", err)
}
log.RawMaterial = *rawMaterial
return dto.ConvertStockLogToDTO(log), nil
}
// ListCurrentStock 列出所有原料的当前库存
func (s *inventoryServiceImpl) ListCurrentStock(ctx context.Context, req *dto.ListCurrentStockRequest) (*dto.ListCurrentStockResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListCurrentStock")
// 1. 获取分页的原料列表
rawMatOpts := repository.RawMaterialListOptions{
Name: req.RawMaterialName,
OrderBy: req.OrderBy, // 注意:这里的排序可能需要调整,比如按原料名排序
HasStock: req.HasStock,
}
rawMaterials, total, err := s.rawMatRepo.ListRawMaterials(serviceCtx, rawMatOpts, req.Page, req.PageSize)
if err != nil {
return nil, fmt.Errorf("获取原料列表失败: %w", err)
}
if len(rawMaterials) == 0 {
return &dto.ListCurrentStockResponse{
List: []dto.CurrentStockResponse{},
Pagination: dto.PaginationDTO{Page: req.Page, PageSize: req.PageSize, Total: total},
}, nil
}
// 2. 提取原料ID并批量获取它们的最新库存日志
materialIDs := make([]uint32, len(rawMaterials))
for i, rm := range rawMaterials {
materialIDs[i] = rm.ID
}
latestLogMap, err := s.rawMatRepo.BatchGetLatestStockLogsForMaterials(serviceCtx, materialIDs)
if err != nil {
return nil, fmt.Errorf("批量获取最新库存日志失败: %w", err)
}
// 3. 组合原料信息和库存信息
stockDTOs := make([]dto.CurrentStockResponse, len(rawMaterials))
for i, rm := range rawMaterials {
log, _ := latestLogMap[rm.ID] // 如果找不到log会是零值
stockDTOs[i] = *dto.ConvertCurrentStockToDTO(&rm, &log)
}
return &dto.ListCurrentStockResponse{
List: stockDTOs,
Pagination: dto.PaginationDTO{Page: req.Page, PageSize: req.PageSize, Total: total},
}, nil
}
// ListStockLogs 列出库存变动历史
func (s *inventoryServiceImpl) ListStockLogs(ctx context.Context, req *dto.ListStockLogRequest) (*dto.ListStockLogResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListStockLogs")
// 解析时间字符串
var startTime, endTime *time.Time
if req.StartTime != nil && *req.StartTime != "" {
t, err := time.Parse(time.RFC3339, *req.StartTime)
if err != nil {
return nil, fmt.Errorf("无效的开始时间格式: %w", err)
}
startTime = &t
}
if req.EndTime != nil && *req.EndTime != "" {
t, err := time.Parse(time.RFC3339, *req.EndTime)
if err != nil {
return nil, fmt.Errorf("无效的结束时间格式: %w", err)
}
endTime = &t
}
// 转换 source types
sourceTypes := make([]models.StockLogSourceType, len(req.SourceTypes))
for i, st := range req.SourceTypes {
sourceTypes[i] = models.StockLogSourceType(st)
}
opts := repository.StockLogListOptions{
RawMaterialID: req.RawMaterialID,
SourceTypes: sourceTypes,
StartTime: startTime,
EndTime: endTime,
OrderBy: req.OrderBy,
}
logs, total, err := s.invSvc.ListStockLogs(serviceCtx, opts, req.Page, req.PageSize)
if err != nil {
return nil, fmt.Errorf("获取库存日志列表失败: %w", err)
}
return dto.ConvertStockLogListToDTO(logs, total, req.Page, req.PageSize), nil
}