daShangDao_psiServer/controllers/process.go
2026-06-17 15:29:47 +08:00

988 lines
31 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 controllers
import (
"bytes"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"psi/constant"
"psi/database"
"psi/models"
systemReq "psi/models/request"
systemRes "psi/models/response"
"psi/service"
"psi/utils"
"strconv"
)
type ProcessApi struct{}
var processService = &service.ProcessService{}
// CreatePurchaseOrderWithWave 创建采购单并生成入库波次
func (r *ProcessApi) CreatePurchaseOrderWithWave(c *gin.Context) {
var req systemReq.PurchaseOrderCreateRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "创建采购单请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
carService := &service.CarService{}
capacity, err := carService.GetCarCapacityByID(req.CarID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车容量异常", err, c, gin.H{"car_id": req.CarID})
return
}
if len(req.Items) == 0 {
items, err := bindPurchaseOrderItems(c, int(capacity))
if err != nil {
logAndFail(constant.LoggerChannelRequest, "绑定采购订单项异常", "参数错误: "+err.Error(), c)
return
}
req.Items = items
}
userInfo := utils.GetUserInfo(c)
purchaseOrderID, waveID, err := processService.CreatePurchaseOrderWithWave(req, userInfo.Username, userInfo.ID, int(capacity), database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建采购单及波次异常", err, c, req)
return
}
systemRes.OkWithDetailed(gin.H{"order_id": purchaseOrderID, "wave_id": waveID}, "采购单创建成功,波次已生成", c)
}
// ReleaseWave 提交波次,生成采购订单明细和波次任务明细
func (r *ProcessApi) ReleaseWave(c *gin.Context) {
var req systemReq.WaveRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "提交波次请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
carService := &service.CarService{}
capacity, err := carService.GetCarCapacityByID(req.CarID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询小车容量异常", err, c, gin.H{"car_id": req.CarID})
return
}
if len(req.Items) == 0 {
items, err := bindWaveItems(c, int(capacity))
if err != nil {
logAndFail(constant.LoggerChannelRequest, "绑定波次项异常", "参数错误: "+err.Error(), c)
return
}
req.Items = items
}
waveID, waveNo, err := processService.ReleaseWave(req, capacity, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交波次异常", err, c, req)
return
}
systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{
WaveID: waveID,
WaveNo: waveNo,
}, "波次提交成功,任务和明细已生成", c)
}
// BindWave 绑定波次,创建入库单
func (r *ProcessApi) BindWave(c *gin.Context) {
var req systemReq.BindWaveRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "绑定波次请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
receivingOrderID, waveTaskID, waveTaskBatchNo, err := processService.BindWave(req, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "绑定波次异常", err, c, req)
return
}
systemRes.OkWithDetailed(map[string]interface{}{
"receiving_order_id": receivingOrderID,
"wave_task_id": waveTaskID,
"wave_task_batch_no": waveTaskBatchNo,
}, "绑定波次成功,入库单已创建", c)
}
// GetWaveTaskInfo 获取波次任务信息
func (r *ProcessApi) GetWaveTaskInfo(c *gin.Context) {
id, err := parsePathID(c, "id", "获取波次任务信息")
if err != nil {
return
}
info, err := processService.GetWaveTaskInfo(id, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取波次任务信息异常", err, c, gin.H{"id": id})
return
}
systemRes.OkWithDetailed(info, "获取成功", c)
}
// SubmitReceiving 提交入库
func (r *ProcessApi) SubmitReceiving(c *gin.Context) {
var req systemReq.ReceivingSubmitRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "提交入库请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
if len(req.Items) == 0 {
databaseConn := database.GetDB(c)
var waveTask models.WaveTask
if err := databaseConn.Where("id = ? AND is_del = 0", req.WaveTaskID).First(&waveTask).Error; err != nil {
logAndFail(constant.LoggerChannelWork, "查询波次任务失败", "错误: "+err.Error(), c)
return
}
carCapacity := int(waveTask.CarCapacity)
if carCapacity <= 0 {
logAndFail(constant.LoggerChannelWork, "波次任务未设置小车容量", fmt.Sprintf("wave_task_id: %d", req.WaveTaskID), c)
return
}
items, err := bindReceivingItems(c, carCapacity)
if err != nil {
logAndFail(constant.LoggerChannelRequest, "绑定入库项异常", "参数错误: "+err.Error(), c)
return
}
req.Items = items
}
userInfo := utils.GetUserInfo(c)
err := processService.SubmitReceiving(req, userInfo.Username, userInfo.ID, userInfo.AboutID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交入库异常", err, c, req)
return
}
go func(receivingOrderID, aboutID, waveTaskID int64, itemProductIDs []int64) {
databaseConn := database.DB
var receivingOrder models.ReceivingOrder
if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrderID).First(&receivingOrder).Error; err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "查询入库单失败",
"receiving_order_id": receivingOrderID,
"err_msg": err.Error(),
})
return
}
var warehouse models.Warehouse
if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrder.WarehouseID).First(&warehouse).Error; err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "查询仓库失败",
"warehouse_id": receivingOrder.WarehouseID,
"err_msg": err.Error(),
})
return
}
// 同步给主库对应的product_book_xx中
/*var warehouse models.Warehouse
if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrder.WarehouseID).First(&warehouse).Error; err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "查询仓库失败",
"warehouse_id": receivingOrder.WarehouseID,
"err_msg": err.Error(),
})
return
}*/
// 同步给主库对应的product_book_xx中
// 获取租户数据库连接
tenantDB, err := database.GetTenantDB(aboutID)
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "获取租户数据库连接失败",
"user_id": aboutID,
"err_msg": err.Error(),
})
return
}
// 查询商品详细信息
var products []models.Product
if err := tenantDB.Where("id IN ? AND is_del = 0", itemProductIDs).Find(&products).Error; err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "查询商品列表失败",
"user_id": aboutID,
"product_ids": itemProductIDs,
"err_msg": err.Error(),
})
return
}
// 查询库位信息(用于获取库位名称)
locationMap := make(map[int64]string)
for _, product := range products {
// 通过库存明细表查询商品所在的库位
var inventoryDetail models.InventoryDetail
if err := tenantDB.Where("product_id = ? AND warehouse_id = ? AND is_del = 0", product.ID, receivingOrder.WarehouseID).First(&inventoryDetail).Error; err == nil {
var location models.Location
if err := tenantDB.Where("id = ? AND is_del = 0", inventoryDetail.LocationID).First(&location).Error; err == nil {
locationMap[product.ID] = location.Code
}
}
}
// 逐个同步商品到主库
for _, product := range products {
locationCode := ""
if code, exists := locationMap[product.ID]; exists {
locationCode = code
}
if syncErr := database.SyncProductToMainDB(aboutID, &product, warehouse.ID, warehouse.Name, 0, locationCode); syncErr != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "同步商品到主库失败",
"user_id": aboutID,
"product_id": product.ID,
"warehouse_id": warehouse.ID,
"location_code": locationCode,
"err_msg": syncErr.Error(),
})
} else {
utils.InfoLog(constant.LoggerChannelWork, logrus.Fields{
"source": "同步商品到主库成功",
"user_id": aboutID,
"product_id": product.ID,
"product_name": product.Name,
"barcode": product.Barcode,
"warehouse_id": warehouse.ID,
"location_code": locationCode,
})
}
}
warehouseId := warehouse.ID
//warehouseId := warehouse.ID
productIds := make([]string, len(itemProductIDs))
for i, pid := range itemProductIDs {
productIds[i] = fmt.Sprintf("%d", pid)
}
productIdsStr := strings.Join(productIds, ",")
url := "https://erp.buzhiyushu.cn/zhishu/product/releaseGoodsAuto"
method := "POST"
payload := &bytes.Buffer{}
writer := multipart.NewWriter(payload)
_ = writer.WriteField("userId", fmt.Sprintf("%d", aboutID))
_ = writer.WriteField("warehouseId", fmt.Sprintf("%d", warehouseId))
_ = writer.WriteField("productId", productIdsStr)
closeErr := writer.Close()
if closeErr != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "关闭multipart writer失败",
"err_msg": closeErr.Error(),
})
return
}
client := &http.Client{}
httpReq, err := http.NewRequest(method, url, payload)
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "创建HTTP请求失败",
"err_msg": err.Error(),
})
return
}
httpReq.Header.Add("Authorization", "Basic ZWxhc3RpYzo1bVJESVVnNTJWQzBmcDE0bnctRg==")
httpReq.Header.Set("Content-Type", writer.FormDataContentType())
res, err := client.Do(httpReq)
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "调用外部API失败",
"err_msg": err.Error(),
})
return
}
defer func(Body io.ReadCloser) {
closeBodyErr := Body.Close()
if closeBodyErr != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "调用外部API失败-Body关闭失败",
"err_msg": closeBodyErr.Error(),
})
}
}(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "读取响应失败",
"err_msg": err.Error(),
})
return
}
/*payload := &bytes.Buffer{}
writer := multipart.NewWriter(payload)
_ = writer.WriteField("userId", fmt.Sprintf("%d", aboutID))
_ = writer.WriteField("warehouseId", fmt.Sprintf("%d", warehouseId))
_ = writer.WriteField("productId", productIdsStr)
err := writer.Close()
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "关闭multipart writer失败",
"err_msg": err.Error(),
})
return
}
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "创建HTTP请求失败",
"err_msg": err.Error(),
})
return
}
req.Header.Add("Authorization", "Basic ZWxhc3RpYzo1bVJESVVnNTJWQzBmcDE0bnctRg==")
req.Header.Set("Content-Type", writer.FormDataContentType())
res, err := client.Do(req)
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "调用外部API失败",
"err_msg": err.Error(),
})
return
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "调用外部API失败-Body关闭失败",
"err_msg": err.Error(),
})
}
}(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{
"source": "读取响应失败",
"err_msg": err.Error(),
})
return
}*/
utils.InfoLog(constant.LoggerChannelWork, logrus.Fields{
"source": "外部API调用完成",
"user_id": aboutID,
"warehouse_id": warehouseId,
"product_ids": productIdsStr,
"status_code": res.StatusCode,
"response": string(body),
})
}(req.ReceivingOrderID, userInfo.AboutID, req.WaveTaskID, func() []int64 {
productIDs := make([]int64, len(req.Items))
for i, item := range req.Items {
productIDs[i] = item.ProductID
}
return productIDs
}())
systemRes.OkWithMessage("入库提交成功", c)
}
// GetReceivingDetail 获取入库单详情
func (r *ProcessApi) GetReceivingDetail(c *gin.Context) {
id, err := parsePathID(c, "id", "获取入库单详情")
if err != nil {
return
}
detail, err := processService.GetReceivingDetail(id, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取入库单详情异常", err, c, gin.H{"id": id})
return
}
systemRes.OkWithDetailed(detail, "获取成功", c)
}
// CreateSalesOrderWithDetail 创建销售订单和明细
func (r *ProcessApi) CreateSalesOrderWithDetail(c *gin.Context) {
var req systemReq.SalesOrderCreateRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "创建销售订单请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
if len(req.Items) == 0 {
items, err := bindSalesOrderItems(c)
if err != nil {
logAndFail(constant.LoggerChannelRequest, "绑定销售订单项异常", "参数错误: "+err.Error(), c)
return
}
req.Items = items
}
salesOrderID, err := processService.CreateSalesOrderWithDetail(req)
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建销售订单及波次异常", err, c, req)
return
}
systemRes.OkWithDetailed(gin.H{"order_id": salesOrderID}, "销售订单创建成功", c)
}
// CreateOutboundOrder 基于多个销售订单创建出库单
func (r *ProcessApi) CreateOutboundOrder(c *gin.Context) {
var req systemReq.CreateOutboundOrderRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "创建出库单请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
if len(req.SalesOrderIDs) == 0 {
ids, err := parseIdsFrom(c, req.Total)
if err != nil {
systemRes.FailWithMessage("参数错误: "+err.Error(), c)
return
}
req.SalesOrderIDs = ids
}
userInfo := utils.GetUserInfo(c)
outboundOrderID, outNo, err := processService.CreateOutboundOrder(req, userInfo.Username, userInfo.ID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建出库单异常", err, c, req)
return
}
systemRes.OkWithDetailed(gin.H{
"outbound_order_id": outboundOrderID,
"out_no": outNo,
}, "出库单创建成功", c)
}
// CreateOutboundWave 基于出库单创建出库波次
func (r *ProcessApi) CreateOutboundWave(c *gin.Context) {
var req systemReq.CreateOutboundWaveRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "创建出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
if req.OutboundOrderID == 0 {
systemRes.FailWithMessage("参数错误: 出库单ID不能为空", c)
return
}
userInfo := utils.GetUserInfo(c)
waveID, err := processService.CreateOutboundWave(req, userInfo.Username, userInfo.ID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建出库波次异常", err, c, req)
return
}
systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{
WaveID: waveID,
}, "出库波次创建成功", c)
}
// ReleaseOutboundWave 提交出库波次,生成波次任务明细
func (r *ProcessApi) ReleaseOutboundWave(c *gin.Context) {
var req systemReq.WaveRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "提交出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
waveID, waveNo, err := processService.ReleaseOutboundWave(req, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交出库波次异常", err, c, req)
return
}
systemRes.OkWithDetailed(systemRes.WaveReleaseResponse{
WaveID: waveID,
WaveNo: waveNo,
}, "出库波次提交成功,任务和明细已生成", c)
}
// BindOutboundWave 绑定出库波次
func (r *ProcessApi) BindOutboundWave(c *gin.Context) {
var req systemReq.BindWaveRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "绑定出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
outboundOrderID, waveTaskID, waveTaskBatchNo, err := processService.BindOutboundWave(req, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "绑定出库波次异常", err, c, req)
return
}
systemRes.OkWithDetailed(map[string]interface{}{
"out_order_id": outboundOrderID,
"wave_task_id": waveTaskID,
"wave_task_batch_no": waveTaskBatchNo,
}, "绑定出库波次成功", c)
}
// SubmitOutbound 提交出库
func (r *ProcessApi) SubmitOutbound(c *gin.Context) {
var req systemReq.OutboundSubmitRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "提交出库请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
if len(req.Items) == 0 {
databaseConn := database.GetDB(c)
var waveTask models.WaveTask
if err := databaseConn.Where("id = ? AND is_del = 0", req.WaveTaskID).First(&waveTask).Error; err != nil {
logAndFail(constant.LoggerChannelWork, "查询波次任务失败", "错误: "+err.Error(), c)
return
}
var count int64
if err := databaseConn.Model(&models.WaveTaskDetail{}).Where("wave_task_id = ? AND status = 1 AND is_del = 0", waveTask.ID).Count(&count).Error; err != nil {
logAndFail(constant.LoggerChannelWork, "查询波次任务明细数量失败", "错误: "+err.Error(), c)
return
}
items, err := bindOutboundItems(c, int(count))
if err != nil {
logAndFail(constant.LoggerChannelRequest, "绑定出库项异常", "参数错误: "+err.Error(), c)
return
}
req.Items = items
}
userInfo := utils.GetUserInfo(c)
err := processService.SubmitOutbound(req, userInfo.Username, userInfo.ID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "提交出库异常", err, c, req)
return
}
systemRes.OkWithMessage("出库提交成功", c)
}
// GetOutboundDetail 获取发货单详情
func (r *ProcessApi) GetOutboundDetail(c *gin.Context) {
id, err := parsePathID(c, "id", "获取发货单详情")
if err != nil {
return
}
detail, err := processService.GetOutboundDetail(id, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "获取发货单详情异常", err, c, gin.H{"id": id})
return
}
systemRes.OkWithDetailed(detail, "获取成功", c)
}
// CreateShippingOrder 基于多个出库单创建发货单
func (r *ProcessApi) CreateShippingOrder(c *gin.Context) {
var req systemReq.CreateShippingOrderRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "创建发货单请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
if len(req.OutboundOrderIDs) == 0 {
ids, err := parseIdsFrom(c, req.Total)
if err != nil {
systemRes.FailWithMessage("参数错误: "+err.Error(), c)
return
}
req.OutboundOrderIDs = ids
}
userInfo := utils.GetUserInfo(c)
shippingOrderID, shippingNo, err := processService.CreateShippingOrder(req, userInfo.Username, userInfo.ID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "创建发货单异常", err, c, req)
return
}
systemRes.OkWithDetailed(gin.H{
"shipping_order_id": shippingOrderID,
"shipping_no": shippingNo,
}, "发货单创建成功", c)
}
// UpdateShippingLogistics 更新发货单物流信息并回填销售订单明细
func (r *ProcessApi) UpdateShippingLogistics(c *gin.Context) {
var req systemReq.UpdateShippingLogisticsRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "更新发货单物流信息请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
userInfo := utils.GetUserInfo(c)
err := processService.UpdateShippingLogistics(req, userInfo.ID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "更新发货单物流信息异常", err, c, req)
return
}
systemRes.OkWithMessage("更新成功", c)
}
// CancelSalesOrder 取消销售订单
func (r *ProcessApi) CancelSalesOrder(c *gin.Context) {
var req systemReq.CancelSalesOrderRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "取消销售订单请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
userInfo := utils.GetUserInfo(c)
err := processService.CancelSalesOrder(req.OrderID, userInfo.Username, userInfo.ID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "取消销售订单异常", err, c, req)
return
}
systemRes.OkWithMessage("销售订单取消成功,库存已释放", c)
}
// CancelOutboundWave 取消出库波次
func (r *ProcessApi) CancelOutboundWave(c *gin.Context) {
var req systemReq.CancelOutboundWaveRequest
if err := c.ShouldBindJSON(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "取消出库波次请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
userInfo := utils.GetUserInfo(c)
err := processService.CancelOutboundWave(req.WaveID, userInfo.Username, userInfo.ID, database.GetDB(c))
if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "取消出库波次异常", err, c, req)
return
}
systemRes.OkWithMessage("出库波次取消成功,库存已释放", c)
}
// AdjustInventory 盘库调整(加库存/减库存)
func (r *ProcessApi) AdjustInventory(c *gin.Context) {
var req systemReq.StockCheckAdjustRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "盘库调整请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
userInfo := utils.GetUserInfo(c)
if err := processService.AdjustInventory(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库调整异常", err, c, req)
return
}
systemRes.OkWithMessage("盘库调整成功", c)
}
// ReturnInventory 盘库退货
func (r *ProcessApi) ReturnInventory(c *gin.Context) {
var req systemReq.StockCheckReturnRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "盘库退货请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
userInfo := utils.GetUserInfo(c)
if err := processService.ReturnInventory(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "盘库退货异常", err, c, req)
return
}
systemRes.OkWithMessage("盘库退货成功", c)
}
// ChangeLocation 出库单切换库位
func (r *ProcessApi) ChangeLocation(c *gin.Context) {
var req systemReq.ChangeLocationRequest
if err := c.ShouldBind(&req); err != nil {
ValidAndFail(constant.LoggerChannelRequest, "出库单切换库位请求参数异常", "参数错误: "+err.Error(), c, err)
return
}
userInfo := utils.GetUserInfo(c)
if err := processService.ChangeLocation(req, userInfo.Username, userInfo.ID, database.GetDB(c)); err != nil {
if errors.Is(err, utils.ErrNoAvailableLocation) {
c.Status(http.StatusNoContent)
return
}
utils.FailWithRequestLog(constant.LoggerChannelWork, "出库单切换库位异常", err, c, req)
return
}
systemRes.OkWithMessage("库位切换成功", c)
}
// parsePathID 从URL路径参数中获取并验证ID
func parsePathID(c *gin.Context, paramName, source string) (int64, error) {
idStr := c.Param(paramName)
if idStr == "" {
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
"source": source + "请求参数异常",
"err_msg": "ID参数不能为空",
})
systemRes.FailWithValidateMessage("参数错误: ID不能为空", c)
return 0, fmt.Errorf("ID参数不能为空")
}
var id int64
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil || id <= 0 {
utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields{
"source": source + "请求参数异常",
"err_msg": "ID格式错误: " + idStr,
})
systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c)
return 0, fmt.Errorf("ID格式错误")
}
return id, nil
}
// parseOptionalDate 解析可选日期字段
func parseOptionalDate(c *gin.Context, key string) int64 {
if str := c.PostForm(key); str != "" {
if val, err := strconv.ParseInt(str, 10, 64); err == nil {
return val
}
}
return 0
}
// bindSimpleItem 绑定简单项ProductID + Quantity + UnitPrice
func bindSimpleItem[T any](c *gin.Context, i int, creator func(int64, int64, int64) T) (T, bool, error) {
var zero T
productID, hasProduct, err := getPostFormInt64(c, fmt.Sprintf("items[%d][product_id]", i))
if err != nil || !hasProduct {
return zero, false, err
}
quantity, hasQuantity, err := getPostFormInt64(c, fmt.Sprintf("items[%d][quantity]", i))
if err != nil || !hasQuantity {
return zero, false, err
}
unitPrice, hasPrice, err := getPostFormInt64(c, fmt.Sprintf("items[%d][unit_price]", i))
if err != nil || !hasPrice {
return zero, false, err
}
return creator(productID, quantity, unitPrice), true, nil
}
// bindLocationItem 绑定带位置信息的项ProductID + LocationID + Quantity + 批次信息)
func bindLocationItem[T any](c *gin.Context, i int, creator func(int64, int64, string, int64, int64, int64) T) (T, bool, error) {
var zero T
productID, hasProduct, err := getPostFormInt64(c, fmt.Sprintf("items[%d][product_id]", i))
if err != nil || !hasProduct {
return zero, false, err
}
locationID, hasLocation, err := getPostFormInt64(c, fmt.Sprintf("items[%d][location_id]", i))
if err != nil || !hasLocation {
return zero, false, err
}
quantity, hasQuantity, err := getPostFormInt64(c, fmt.Sprintf("items[%d][quantity]", i))
if err != nil || !hasQuantity {
return zero, false, err
}
batchNo := c.PostForm(fmt.Sprintf("items[%d][batch_no]", i))
productionDate := parseOptionalDate(c, fmt.Sprintf("items[%d][production_date]", i))
expiryDate := parseOptionalDate(c, fmt.Sprintf("items[%d][expiry_date]", i))
return creator(productID, locationID, batchNo, productionDate, expiryDate, quantity), true, nil
}
// bindPurchaseOrderItems 绑定采购订单项
func bindPurchaseOrderItems(c *gin.Context, capacity int) ([]systemReq.PurchaseOrderItemRequest, error) {
return bindItemsWithForm(c, func(i int) (systemReq.PurchaseOrderItemRequest, bool, error) {
return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.PurchaseOrderItemRequest {
return systemReq.PurchaseOrderItemRequest{
ProductID: productID,
Quantity: quantity,
UnitPrice: unitPrice,
}
})
}, capacity)
}
// bindWaveItems 绑定波次项
func bindWaveItems(c *gin.Context, capacity int) ([]systemReq.WaveItemRequest, error) {
return bindItemsWithForm(c, func(i int) (systemReq.WaveItemRequest, bool, error) {
return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.WaveItemRequest {
return systemReq.WaveItemRequest{
ProductID: productID,
Quantity: quantity,
UnitPrice: unitPrice,
}
})
}, capacity)
}
// bindReceivingItems 绑定入库项
func bindReceivingItems(c *gin.Context, maxItems int) ([]systemReq.ReceivingItemRequest, error) {
return bindItemsWithForm(c, func(i int) (systemReq.ReceivingItemRequest, bool, error) {
return bindLocationItem(c, i, func(productID, locationID int64, batchNo string, productionDate, expiryDate, quantity int64) systemReq.ReceivingItemRequest {
return systemReq.ReceivingItemRequest{
ProductID: productID,
LocationID: locationID,
BatchNo: batchNo,
ProductionDate: productionDate,
ExpiryDate: expiryDate,
Quantity: quantity,
}
})
}, maxItems)
}
// bindSalesOrderItems 绑定销售订单项
func bindSalesOrderItems(c *gin.Context) ([]systemReq.SalesOrderItemRequest, error) {
return bindItemsWithForm(c, func(i int) (systemReq.SalesOrderItemRequest, bool, error) {
return bindSimpleItem(c, i, func(productID, quantity, unitPrice int64) systemReq.SalesOrderItemRequest {
return systemReq.SalesOrderItemRequest{
ProductID: productID,
Quantity: quantity,
UnitPrice: unitPrice,
}
})
}, 200)
}
// bindOutboundItems 绑定出库项
func bindOutboundItems(c *gin.Context, maxItems int) ([]systemReq.OutboundItemRequest, error) {
return bindItemsWithForm(c, func(i int) (systemReq.OutboundItemRequest, bool, error) {
return bindLocationItem(c, i, func(productID, locationID int64, batchNo string, productionDate, expiryDate, quantity int64) systemReq.OutboundItemRequest {
return systemReq.OutboundItemRequest{
ProductID: productID,
LocationID: locationID,
BatchNo: batchNo,
ProductionDate: productionDate,
ExpiryDate: expiryDate,
Quantity: quantity,
}
})
}, maxItems)
}
// bindItemsWithForm 通用表单项绑定函数
func bindItemsWithForm[T any](c *gin.Context, builder func(int) (T, bool, error), maxItems int) ([]T, error) {
items := make([]T, 0)
for i := 0; i < maxItems; i++ {
item, hasData, err := builder(i)
if err != nil {
return nil, err
}
if !hasData {
continue
}
items = append(items, item)
}
return items, nil
}
func parseIdsFrom(c *gin.Context, total int) ([]int64, error) {
var ids []int64
for i := 0; i < total; i++ {
idStr := c.PostForm(fmt.Sprintf("order_ids[%d]", i))
if idStr == "" {
break
}
var id int64
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
return nil, fmt.Errorf("第%d个ID格式错误", i)
}
if id <= 0 {
return nil, fmt.Errorf("第%d个ID必须大于0", i)
}
ids = append(ids, id)
}
if len(ids) == 0 {
return nil, fmt.Errorf("至少需要一个ID")
}
return ids, nil
}
// getPostFormInt64 获取表单中的int64值
func getPostFormInt64(c *gin.Context, key string) (int64, bool, error) {
str := c.PostForm(key)
if str == "" {
return 0, false, nil
}
val, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return 0, true, err
}
return val, true, nil
}
// logAndFail 业务统一的错误日志和响应函数
func logAndFail(channel string, source, errMsg string, c *gin.Context) {
utils.ErrorLog(channel, logrus.Fields{
"source": source,
"err_msg": errMsg,
})
systemRes.FailWithMessage(errMsg, c)
}
// ValidAndFail 参数统一的错误日志和响应函数
func ValidAndFail(channel string, source, errMsg string, c *gin.Context, err error) {
utils.ErrorLog(channel, logrus.Fields{
"source": source,
"err_msg": errMsg,
})
utils.HandleValidationError(c, err)
}