实现库存管理相关逻辑
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/device"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/feed"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/health"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/inventory"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/management"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/monitor"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/plan"
|
||||
@@ -62,6 +63,7 @@ type API struct {
|
||||
pigTypeController *feed.PigTypeController // 猪种类控制器实例
|
||||
rawMaterialController *feed.RawMaterialController // 原料控制器实例
|
||||
recipeController *feed.RecipeController // 配方控制器实例
|
||||
inventoryController *inventory.InventoryController // 库存控制器实例
|
||||
listenHandler webhook.ListenHandler // 设备上行事件监听器
|
||||
analysisTaskManager *domain_plan.AnalysisPlanTaskManager // 计划触发器管理器实例
|
||||
}
|
||||
@@ -85,6 +87,7 @@ func NewAPI(cfg config.ServerConfig,
|
||||
pigAgeStageService service.PigAgeStageService,
|
||||
pigTypeService service.PigTypeService,
|
||||
recipeService service.RecipeService,
|
||||
inventoryService service.InventoryService,
|
||||
tokenGenerator token.Generator,
|
||||
listenHandler webhook.ListenHandler,
|
||||
) *API {
|
||||
@@ -122,6 +125,7 @@ func NewAPI(cfg config.ServerConfig,
|
||||
pigTypeController: feed.NewPigTypeController(logs.AddCompName(baseCtx, "PigTypeController"), pigTypeService),
|
||||
rawMaterialController: feed.NewRawMaterialController(logs.AddCompName(baseCtx, "RawMaterialController"), rawMaterialService),
|
||||
recipeController: feed.NewRecipeController(logs.AddCompName(baseCtx, "RecipeController"), recipeService),
|
||||
inventoryController: inventory.NewInventoryController(logs.AddCompName(baseCtx, "InventoryController"), inventoryService),
|
||||
}
|
||||
|
||||
api.setupRoutes() // 设置所有路由
|
||||
|
||||
@@ -261,6 +261,15 @@ func (a *API) setupRoutes() {
|
||||
feedGroup.GET("/recipes", a.recipeController.ListRecipes)
|
||||
}
|
||||
logger.Debug("饲料管理相关接口注册成功 (需要认证和审计)")
|
||||
|
||||
// 库存管理相关路由组
|
||||
inventoryGroup := authGroup.Group("/inventory")
|
||||
{
|
||||
inventoryGroup.POST("/stock/adjust", a.inventoryController.AdjustStock)
|
||||
inventoryGroup.GET("/stock/current", a.inventoryController.ListCurrentStock)
|
||||
inventoryGroup.GET("/stock/logs", a.inventoryController.ListStockLogs)
|
||||
}
|
||||
logger.Debug("库存管理相关接口注册成功 (需要认证和审计)")
|
||||
}
|
||||
|
||||
logger.Debug("所有接口注册成功")
|
||||
|
||||
121
internal/app/controller/inventory/inventory_controller.go
Normal file
121
internal/app/controller/inventory/inventory_controller.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// InventoryController 定义了库存相关的控制器
|
||||
type InventoryController struct {
|
||||
ctx context.Context
|
||||
inventoryService service.InventoryService
|
||||
}
|
||||
|
||||
// NewInventoryController 创建一个新的 InventoryController 实例
|
||||
func NewInventoryController(ctx context.Context, inventoryService service.InventoryService) *InventoryController {
|
||||
return &InventoryController{
|
||||
ctx: ctx,
|
||||
inventoryService: inventoryService,
|
||||
}
|
||||
}
|
||||
|
||||
// AdjustStock godoc
|
||||
// @Summary 调整原料库存
|
||||
// @Description 手动调整指定原料的库存量。
|
||||
// @Tags 库存管理
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.StockAdjustmentRequest true "库存调整请求"
|
||||
// @Success 200 {object} controller.Response{data=dto.StockLogResponse} "业务码为200代表调整成功"
|
||||
// @Router /api/v1/inventory/stock/adjust [post]
|
||||
func (c *InventoryController) AdjustStock(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "AdjustStock")
|
||||
var req dto.StockAdjustmentRequest
|
||||
const actionType = "调整原料库存"
|
||||
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
|
||||
}
|
||||
|
||||
resp, err := c.inventoryService.AdjustStock(reqCtx, &req)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层调整库存失败: %v", actionType, err)
|
||||
if errors.Is(err, service.ErrInventoryRawMaterialNotFound) {
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "原料不存在", req)
|
||||
}
|
||||
if errors.Is(err, service.ErrInventoryInsufficientStock) {
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "原料库存不足", req)
|
||||
}
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "调整库存失败: "+err.Error(), actionType, "服务层调整库存失败", req)
|
||||
}
|
||||
|
||||
logger.Infof("%s: 库存调整成功, 原料ID: %d", actionType, resp.RawMaterialID)
|
||||
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "库存调整成功", resp, actionType, "库存调整成功", resp)
|
||||
}
|
||||
|
||||
// ListCurrentStock godoc
|
||||
// @Summary 获取当前库存列表
|
||||
// @Description 获取所有原料的当前库存列表,支持分页和过滤。
|
||||
// @Tags 库存管理
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param query query dto.ListCurrentStockRequest false "查询参数"
|
||||
// @Success 200 {object} controller.Response{data=dto.ListCurrentStockResponse} "业务码为200代表成功获取列表"
|
||||
// @Router /api/v1/inventory/stock/current [get]
|
||||
func (c *InventoryController) ListCurrentStock(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListCurrentStock")
|
||||
const actionType = "获取当前库存列表"
|
||||
var req dto.ListCurrentStockRequest
|
||||
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
logger.Errorf("%s: 查询参数绑定失败: %v", actionType, err)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", req)
|
||||
}
|
||||
|
||||
resp, err := c.inventoryService.ListCurrentStock(reqCtx, &req)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层获取当前库存列表失败: %v", actionType, err)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取当前库存列表失败: "+err.Error(), actionType, "服务层获取当前库存列表失败", nil)
|
||||
}
|
||||
|
||||
logger.Infof("%s: 获取当前库存列表成功, 数量: %d", actionType, len(resp.List))
|
||||
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取当前库存列表成功", resp, actionType, "获取当前库存列表成功", resp)
|
||||
}
|
||||
|
||||
// ListStockLogs godoc
|
||||
// @Summary 获取库存变动日志
|
||||
// @Description 获取原料库存变动历史记录,支持分页、过滤和时间范围查询。
|
||||
// @Tags 库存管理
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param query query dto.ListStockLogRequest false "查询参数"
|
||||
// @Success 200 {object} controller.Response{data=dto.ListStockLogResponse} "业务码为200代表成功获取列表"
|
||||
// @Router /api/v1/inventory/stock/logs [get]
|
||||
func (c *InventoryController) ListStockLogs(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListStockLogs")
|
||||
const actionType = "获取库存变动日志"
|
||||
var req dto.ListStockLogRequest
|
||||
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
logger.Errorf("%s: 查询参数绑定失败: %v", actionType, err)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", req)
|
||||
}
|
||||
|
||||
resp, err := c.inventoryService.ListStockLogs(reqCtx, &req)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层获取库存变动日志失败: %v", actionType, err)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取库存变动日志失败: "+err.Error(), actionType, "服务层获取库存变动日志失败", nil)
|
||||
}
|
||||
|
||||
logger.Infof("%s: 获取库存变动日志成功, 数量: %d", actionType, len(resp.List))
|
||||
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取库存变动日志成功", resp, actionType, "获取库存变动日志成功", resp)
|
||||
}
|
||||
66
internal/app/dto/inventory_converter.go
Normal file
66
internal/app/dto/inventory_converter.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||
)
|
||||
|
||||
// ConvertCurrentStockToDTO 将原料及其最新库存日志转换为 CurrentStockResponse DTO
|
||||
func ConvertCurrentStockToDTO(material *models.RawMaterial, latestLog *models.RawMaterialStockLog) *CurrentStockResponse {
|
||||
if material == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
stock := float32(0)
|
||||
lastUpdated := material.CreatedAt.Format(time.RFC3339) // 默认使用创建时间
|
||||
|
||||
if latestLog != nil {
|
||||
stock = latestLog.AfterQuantity
|
||||
lastUpdated = latestLog.HappenedAt.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
return &CurrentStockResponse{
|
||||
RawMaterialID: material.ID,
|
||||
RawMaterialName: material.Name,
|
||||
Stock: stock,
|
||||
LastUpdated: lastUpdated,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertStockLogToDTO 将 models.RawMaterialStockLog 转换为 StockLogResponse DTO
|
||||
func ConvertStockLogToDTO(log *models.RawMaterialStockLog) *StockLogResponse {
|
||||
if log == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &StockLogResponse{
|
||||
ID: log.ID,
|
||||
RawMaterialID: log.RawMaterialID,
|
||||
RawMaterialName: log.RawMaterial.Name, // 假设 RawMaterial 已被预加载
|
||||
ChangeAmount: log.ChangeAmount,
|
||||
BeforeQuantity: log.BeforeQuantity,
|
||||
AfterQuantity: log.AfterQuantity,
|
||||
SourceType: string(log.SourceType),
|
||||
SourceID: log.SourceID,
|
||||
HappenedAt: log.HappenedAt,
|
||||
Remarks: log.Remarks,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertStockLogListToDTO 将 []models.RawMaterialStockLog 转换为 ListStockLogResponse DTO
|
||||
func ConvertStockLogListToDTO(logs []models.RawMaterialStockLog, total int64, page, pageSize int) *ListStockLogResponse {
|
||||
logDTOs := make([]StockLogResponse, len(logs))
|
||||
for i, log := range logs {
|
||||
logDTOs[i] = *ConvertStockLogToDTO(&log)
|
||||
}
|
||||
|
||||
return &ListStockLogResponse{
|
||||
List: logDTOs,
|
||||
Pagination: PaginationDTO{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Total: total,
|
||||
},
|
||||
}
|
||||
}
|
||||
67
internal/app/dto/inventory_dto.go
Normal file
67
internal/app/dto/inventory_dto.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
// =============================================================================================================
|
||||
// 库存 (Inventory) 相关 DTO
|
||||
// =============================================================================================================
|
||||
|
||||
// StockAdjustmentRequest 手动调整库存的请求体
|
||||
type StockAdjustmentRequest struct {
|
||||
RawMaterialID uint32 `json:"raw_material_id" validate:"required"` // 要调整的原料ID
|
||||
ChangeAmount float32 `json:"change_amount" validate:"required,ne=0"` // 变动数量, 正数为入库, 负数为出库, 单位: g
|
||||
Remarks string `json:"remarks" validate:"max=255"` // 备注
|
||||
}
|
||||
|
||||
// CurrentStockResponse 单个原料及其当前库存的响应体
|
||||
type CurrentStockResponse struct {
|
||||
RawMaterialID uint32 `json:"raw_material_id"` // 原料ID
|
||||
RawMaterialName string `json:"raw_material_name"` // 原料名称
|
||||
Stock float32 `json:"stock"` // 当前库存量, 单位: g
|
||||
LastUpdated string `json:"last_updated"` // 最后更新时间
|
||||
}
|
||||
|
||||
// ListCurrentStockRequest 定义了获取当前库存列表的请求参数
|
||||
type ListCurrentStockRequest struct {
|
||||
Page int `json:"page" query:"page"` // 页码
|
||||
PageSize int `json:"page_size" query:"page_size"` // 每页数量
|
||||
RawMaterialName *string `json:"raw_material_name" query:"raw_material_name"` // 按原料名称模糊查询
|
||||
OrderBy string `json:"order_by" query:"order_by"` // 排序字段, 例如 "stock DESC"
|
||||
}
|
||||
|
||||
// ListCurrentStockResponse 是获取当前库存列表的响应结构
|
||||
type ListCurrentStockResponse struct {
|
||||
List []CurrentStockResponse `json:"list"`
|
||||
Pagination PaginationDTO `json:"pagination"`
|
||||
}
|
||||
|
||||
// StockLogResponse 库存变动历史记录的响应体
|
||||
type StockLogResponse struct {
|
||||
ID uint32 `json:"id"`
|
||||
RawMaterialID uint32 `json:"raw_material_id"`
|
||||
RawMaterialName string `json:"raw_material_name"`
|
||||
ChangeAmount float32 `json:"change_amount"`
|
||||
BeforeQuantity float32 `json:"before_quantity"`
|
||||
AfterQuantity float32 `json:"after_quantity"`
|
||||
SourceType string `json:"source_type"`
|
||||
SourceID *uint32 `json:"source_id,omitempty"`
|
||||
HappenedAt time.Time `json:"happened_at"`
|
||||
Remarks string `json:"remarks"`
|
||||
}
|
||||
|
||||
// ListStockLogRequest 定义了获取库存变动历史的请求参数
|
||||
type ListStockLogRequest struct {
|
||||
Page int `json:"page" query:"page"` // 页码
|
||||
PageSize int `json:"page_size" query:"page_size"` // 每页数量
|
||||
RawMaterialID *uint32 `json:"raw_material_id" query:"raw_material_id"` // 按原料ID精确查询
|
||||
SourceTypes []string `json:"source_types" query:"source_types"` // 按来源类型查询
|
||||
StartTime *string `json:"start_time" query:"start_time"` // 开始时间 (RFC3339格式, e.g., "2023-01-01T00:00:00Z")
|
||||
EndTime *string `json:"end_time" query:"end_time"` // 结束时间 (RFC3339格式)
|
||||
OrderBy string `json:"order_by" query:"order_by"` // 排序字段
|
||||
}
|
||||
|
||||
// ListStockLogResponse 是获取库存变动历史列表的响应结构
|
||||
type ListStockLogResponse struct {
|
||||
List []StockLogResponse `json:"list"`
|
||||
Pagination PaginationDTO `json:"pagination"`
|
||||
}
|
||||
157
internal/app/service/inventory_service.go
Normal file
157
internal/app/service/inventory_service.go
Normal file
@@ -0,0 +1,157 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user