158 lines
5.5 KiB
Go
158 lines
5.5 KiB
Go
|
|
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, // 注意:这里的排序可能需要调整,比如按原料名排序
|
|||
|
|
}
|
|||
|
|
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
|
|||
|
|
}
|