package service import ( "psi/database" "psi/models" systemReq "psi/models/request" systemRes "psi/models/response" "psi/utils" "gorm.io/gorm" ) type ShippingService struct{} // GetShippingOrderList 获取发货单列表 func (s *ShippingService) GetShippingOrderList(req systemReq.GetShippingOrderListRequest, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.ShippingOrderListResponse, error) { databaseConn := database.OptionalDB(db...) if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 || req.PageSize > 100 { req.PageSize = 20 } query := databaseConn.Model(&models.ShippingOrder{}).Where("shipping_order.is_del = ?", 0) if req.Status > 0 { query = query.Where("shipping_order.status = ?", req.Status) } if req.CustomerID > 0 { query = query.Where("shipping_order.customer_id = ?", req.CustomerID) } if req.ShippingNo != "" { query = query.Where("shipping_order.shipping_no LIKE ?", "%"+req.ShippingNo+"%") } if req.StartDate > 0 { query = query.Where("shipping_order.created_at >= ?", req.StartDate) } if req.EndDate > 0 { query = query.Where("shipping_order.created_at <= ?", req.EndDate) } if req.AssociationOrderNo != "" { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN sales_order ON outbound_order_item.sales_order_id = sales_order.id AND sales_order.is_del = 0"). Where("shipping_order_item.is_del = 0 AND sales_order.association_order_no LIKE ?", "%"+req.AssociationOrderNo+"%") query = query.Where("shipping_order.id IN (?)", subQuery) } if req.LogisticsNo != "" { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN sales_order_item ON outbound_order_item.sales_order_id = sales_order_item.sales_order_id AND sales_order_item.is_del = 0"). Where("shipping_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%") query = query.Where("shipping_order.id IN (?)", subQuery) } if req.ShopType > 0 { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN sales_order ON outbound_order_item.sales_order_id = sales_order.id AND sales_order.is_del = 0"). Where("shipping_order_item.is_del = 0 AND sales_order.shop_type = ?", req.ShopType) query = query.Where("shipping_order.id IN (?)", subQuery) } if req.WarehouseID > 0 { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN location ON outbound_order_item.location_id = location.id AND location.is_del = 0"). Where("shipping_order_item.is_del = 0 AND location.warehouse_id = ?", req.WarehouseID) query = query.Where("shipping_order.id IN (?)", subQuery) } if req.LocationID > 0 { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Where("shipping_order_item.is_del = 0 AND outbound_order_item.location_id = ?", req.LocationID) query = query.Where("shipping_order.id IN (?)", subQuery) } var total int64 if err := query.Count(&total).Error; err != nil { return nil, utils.NewError("查询总数失败") } if total == 0 { return &systemRes.ShippingOrderListResponse{ List: []systemRes.ShippingOrderItem{}, Total: 0, Page: req.Page, PageSize: req.PageSize, }, nil } var orders []systemRes.ShippingOrderWithInfo offset := (req.Page - 1) * req.PageSize if err := query.Select("shipping_order.*, c.name as customer_name"). Joins("LEFT JOIN customer c ON shipping_order.customer_id = c.id AND c.is_del = 0"). Order("shipping_order.created_at DESC"). Offset(offset). Limit(req.PageSize). Find(&orders).Error; err != nil { return nil, utils.NewError("查询发货单列表失败") } // 收集订单ID用于批量查询 orderIDs := make([]int64, len(orders)) for i, order := range orders { orderIDs[i] = order.ID } // 批量查询店铺信息:按 shipping_order_id 分组 type shopRow struct { ShippingOrderID int64 `gorm:"column:shipping_order_id"` ShopName string `gorm:"column:shop_name"` ShopType int8 `gorm:"column:shop_type"` } var shopRows []shopRow databaseConn.Table("shipping_order_item"). Select("DISTINCT shipping_order_item.shipping_order_id, so.sales_person as shop_name, so.shop_type as shop_type"). Joins("INNER JOIN outbound_order_item ooi ON shipping_order_item.outbound_order_item_id = ooi.id AND ooi.is_del = 0"). Joins("INNER JOIN sales_order so ON ooi.sales_order_id = so.id AND so.is_del = 0"). Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ?", orderIDs, 0). Scan(&shopRows) shopsByOrderID := make(map[int64][]systemRes.OutboundShopInfo, len(orderIDs)) for _, row := range shopRows { shopsByOrderID[row.ShippingOrderID] = append(shopsByOrderID[row.ShippingOrderID], systemRes.OutboundShopInfo{ ShopName: row.ShopName, ShopType: row.ShopType, ShopTypeText: systemRes.GetShopTypeText(row.ShopType), }) } // 批量查询关联订单号:按 shipping_order_id 分组 type assocRow struct { ShippingOrderID int64 `gorm:"column:shipping_order_id"` AssociationOrderNo string `gorm:"column:association_order_no"` } var assocRows []assocRow databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id, COALESCE(GROUP_CONCAT(DISTINCT so.association_order_no SEPARATOR ', '), '') as association_order_no"). Joins("INNER JOIN outbound_order_item ooi ON shipping_order_item.outbound_order_item_id = ooi.id AND ooi.is_del = 0"). Joins("INNER JOIN sales_order so ON ooi.sales_order_id = so.id AND so.is_del = 0"). Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ? AND so.association_order_no != ''", orderIDs, 0). Group("shipping_order_item.shipping_order_id"). Scan(&assocRows) assocByOrderID := make(map[int64]string, len(assocRows)) for _, row := range assocRows { assocByOrderID[row.ShippingOrderID] = row.AssociationOrderNo } // 批量查询物流单号:按 shipping_order_id 分组 type logRow struct { ShippingOrderID int64 `gorm:"column:shipping_order_id"` LogisticsNos string `gorm:"column:logistics_nos"` } var logRows []logRow databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id, GROUP_CONCAT(DISTINCT soi.logistics_no SEPARATOR ', ') as logistics_nos"). Joins("INNER JOIN outbound_order_item ooi ON shipping_order_item.outbound_order_item_id = ooi.id AND ooi.is_del = 0"). Joins("INNER JOIN sales_order_item soi ON ooi.sales_order_id = soi.sales_order_id AND ooi.product_id = soi.product_id AND soi.is_del = 0"). Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ? AND soi.logistics_no != ''", orderIDs, 0). Group("shipping_order_item.shipping_order_id"). Scan(&logRows) logByOrderID := make(map[int64]string, len(logRows)) for _, row := range logRows { logByOrderID[row.ShippingOrderID] = row.LogisticsNos } // 组装结果:每个订单从 map 中查找 orderItems := make([]systemRes.ShippingOrderItem, 0, len(orders)) for _, order := range orders { shopList := shopsByOrderID[order.ID] if shopList == nil { shopList = []systemRes.OutboundShopInfo{} } orderItems = append(orderItems, systemRes.ConvertShippingOrderToItem( order.ShippingOrder, order.CustomerName, shopList, assocByOrderID[order.ID], logByOrderID[order.ID], )) } return &systemRes.ShippingOrderListResponse{ List: orderItems, Total: total, Page: req.Page, PageSize: req.PageSize, }, nil } // GetShippingOrderDetail 获取发货单详情 func (s *ShippingService) GetShippingOrderDetail(id int64, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.ShippingOrderDetailResponse, error) { databaseConn := database.OptionalDB(db...) query := databaseConn.Model(&models.ShippingOrder{}). Select("shipping_order.*, c.name as customer_name"). Joins("LEFT JOIN customer c ON shipping_order.customer_id = c.id AND c.is_del = 0"). Where("shipping_order.id = ? AND shipping_order.is_del = ?", id, 0) var order systemRes.ShippingOrderWithInfo result := query.First(&order) if result.Error != nil { return nil, utils.NewError("发货单不存在") } var items []systemRes.ShippingOrderItemWithProduct databaseConn.Model(&models.ShippingOrderItem{}). Select(`shipping_order_item.*, p.id as product_id, p.name as product_name, p.barcode as product_code, p.category_id, c.name as category_name, l.id as location_id, l.code as location_name, w.name as warehouse_name, w.code as warehouse_code, w.contact_person as warehouse_contact_person, w.contact_phone as warehouse_contact_phone, w.province as warehouse_province, w.city as warehouse_city, w.district as warehouse_district, w.address as warehouse_address, so.so_no as sales_order_no, oo.out_no as outbound_order_no, ooi.unit_price, so.created_at as sales_order_created_at, so.sales_person_id, so.association_order_id, so.association_order_no, soi.id as sales_order_item_id, soi.created_at as so_item_created_at, soi.receiver_name, soi.receiver_phone, soi.receiver_address, soi.logistics_company, soi.logistics_no`). Joins("LEFT JOIN outbound_order_item ooi ON shipping_order_item.outbound_order_item_id = ooi.id AND ooi.is_del = 0"). Joins("LEFT JOIN product p ON ooi.product_id = p.id AND p.is_del = 0"). Joins("LEFT JOIN product_category c ON p.category_id = c.id AND c.is_del = 0"). Joins("LEFT JOIN location l ON ooi.location_id = l.id AND l.is_del = 0"). Joins("LEFT JOIN warehouse w ON l.warehouse_id = w.id AND w.is_del = 0"). Joins("LEFT JOIN sales_order so ON ooi.sales_order_id = so.id AND so.is_del = 0"). Joins("LEFT JOIN sales_order_item soi ON ooi.sales_order_id = soi.sales_order_id AND ooi.product_id = soi.product_id AND soi.is_del = 0"). Joins("LEFT JOIN outbound_order oo ON ooi.out_order_id = oo.id AND oo.is_del = 0"). Where("shipping_order_item.shipping_order_id = ? AND shipping_order_item.is_del = ?", order.ID, 0). Order("l.sort ASC, l.id ASC"). Find(&items) detailItems := make([]systemRes.ShippingOrderDetailItem, 0, len(items)) for _, item := range items { detailItems = append(detailItems, systemRes.ShippingOrderDetailItem{ ID: item.ID, ShippingOrderID: item.ShippingOrderID, OutboundOrderItemID: item.OutboundOrderItemID, SalesOrderItemID: item.SalesOrderItemID, SalesOrderNo: item.SalesOrderNo, OutboundOrderNo: item.OutboundOrderNo, ProductID: item.ProductID, ProductName: item.ProductName, ProductCode: item.ProductCode, CategoryID: item.CategoryID, CategoryName: item.CategoryName, LocationID: item.LocationID, LocationName: item.LocationName, WarehouseName: item.WarehouseName, WarehouseCode: item.WarehouseCode, WarehouseContactPerson: item.WarehouseContactPerson, WarehouseContactPhone: item.WarehouseContactPhone, WarehouseProvince: item.WarehouseProvince, WarehouseCity: item.WarehouseCity, WarehouseDistrict: item.WarehouseDistrict, WarehouseAddress: item.WarehouseAddress, Quantity: item.Quantity, UnitPrice: item.UnitPrice, SalesOrderCreatedAt: item.SalesOrderCreatedAt, SalesPersonID: item.SalesPersonID, AssociationOrderID: item.AssociationOrderID, AssociationOrderNo: item.AssociationOrderNo, SoItemCreatedAt: item.SoItemCreatedAt, ReceiverName: item.ReceiverName, ReceiverPhone: item.ReceiverPhone, ReceiverAddress: item.ReceiverAddress, LogisticsCompany: item.LogisticsCompany, LogisticsNo: item.LogisticsNo, }) } detail := systemRes.ConvertShippingOrderToDetail(order.ShippingOrder, order.CustomerName, detailItems) return &detail, nil } // GetShippingOrderDetailList 获取发货单详情列表(按状态筛选,含全部明细字段) func (s *ShippingService) GetShippingOrderDetailList(req systemReq.GetShippingOrderDetailListRequest, creatorID int64, role int64, db ...*gorm.DB) (*systemRes.ShippingOrderDetailListResponse, error) { databaseConn := database.OptionalDB(db...) if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 || req.PageSize > 100 { req.PageSize = 20 } query := databaseConn.Model(&models.ShippingOrder{}).Where("shipping_order.is_del = ?", 0) if req.Status > 0 { query = query.Where("shipping_order.status = ?", req.Status) } if req.CustomerID > 0 { query = query.Where("shipping_order.customer_id = ?", req.CustomerID) } if req.ShippingNo != "" { query = query.Where("shipping_order.shipping_no LIKE ?", "%"+req.ShippingNo+"%") } if req.StartDate > 0 { query = query.Where("shipping_order.created_at >= ?", req.StartDate) } if req.EndDate > 0 { query = query.Where("shipping_order.created_at <= ?", req.EndDate) } if req.AssociationOrderNo != "" { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN sales_order ON outbound_order_item.sales_order_id = sales_order.id AND sales_order.is_del = 0"). Where("shipping_order_item.is_del = 0 AND sales_order.association_order_no LIKE ?", "%"+req.AssociationOrderNo+"%") query = query.Where("shipping_order.id IN (?)", subQuery) } if req.LogisticsNo != "" { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN sales_order_item ON outbound_order_item.sales_order_id = sales_order_item.sales_order_id AND sales_order_item.is_del = 0"). Where("shipping_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%") query = query.Where("shipping_order.id IN (?)", subQuery) } if req.ShopType > 0 { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN sales_order ON outbound_order_item.sales_order_id = sales_order.id AND sales_order.is_del = 0"). Where("shipping_order_item.is_del = 0 AND sales_order.shop_type = ?", req.ShopType) query = query.Where("shipping_order.id IN (?)", subQuery) } if req.WarehouseID > 0 { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Joins("INNER JOIN location ON outbound_order_item.location_id = location.id AND location.is_del = 0"). Where("shipping_order_item.is_del = 0 AND location.warehouse_id = ?", req.WarehouseID) query = query.Where("shipping_order.id IN (?)", subQuery) } if req.LocationID > 0 { subQuery := databaseConn.Table("shipping_order_item"). Select("shipping_order_item.shipping_order_id"). Joins("INNER JOIN outbound_order_item ON shipping_order_item.outbound_order_item_id = outbound_order_item.id AND outbound_order_item.is_del = 0"). Where("shipping_order_item.is_del = 0 AND outbound_order_item.location_id = ?", req.LocationID) query = query.Where("shipping_order.id IN (?)", subQuery) } var total int64 if err := query.Count(&total).Error; err != nil { return nil, utils.NewError("查询总数失败") } if total == 0 { return &systemRes.ShippingOrderDetailListResponse{ List: []systemRes.ShippingOrderDetailListItem{}, Total: 0, Page: req.Page, PageSize: req.PageSize, }, nil } var orders []systemRes.ShippingOrderWithInfo offset := (req.Page - 1) * req.PageSize if err := query.Select("shipping_order.*, c.name as customer_name"). Joins("LEFT JOIN customer c ON shipping_order.customer_id = c.id AND c.is_del = 0"). Order("shipping_order.created_at DESC"). Offset(offset). Limit(req.PageSize). Find(&orders).Error; err != nil { return nil, utils.NewError("查询发货单列表失败") } // 收集所有订单ID orderIDs := make([]int64, 0, len(orders)) for _, order := range orders { orderIDs = append(orderIDs, order.ID) } // 批量按订单分组查询店铺信息 type shopRow struct { ShippingOrderID int64 ShopName string `gorm:"column:shop_name"` ShopType int8 `gorm:"column:shop_type"` } var shopRows []shopRow databaseConn.Table("shipping_order_item"). Select("DISTINCT shipping_order_item.shipping_order_id, so.sales_person as shop_name, so.shop_type as shop_type"). Joins("INNER JOIN outbound_order_item ooi ON shipping_order_item.outbound_order_item_id = ooi.id AND ooi.is_del = 0"). Joins("INNER JOIN sales_order so ON ooi.sales_order_id = so.id AND so.is_del = 0"). Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ?", orderIDs, 0). Scan(&shopRows) shopsByOrderID := make(map[int64][]systemRes.OutboundShopInfo, len(orderIDs)) for _, row := range shopRows { shopsByOrderID[row.ShippingOrderID] = append(shopsByOrderID[row.ShippingOrderID], systemRes.OutboundShopInfo{ ShopName: row.ShopName, ShopType: row.ShopType, ShopTypeText: systemRes.GetShopTypeText(row.ShopType), }) } // 批量查询所有订单的明细(一次查询全部JOIN) var allItems []systemRes.ShippingOrderItemWithProduct databaseConn.Model(&models.ShippingOrderItem{}). Select(`shipping_order_item.*, p.id as product_id, p.name as product_name, p.barcode as product_code, p.category_id, c.name as category_name, l.id as location_id, l.code as location_name, w.name as warehouse_name, w.code as warehouse_code, w.contact_person as warehouse_contact_person, w.contact_phone as warehouse_contact_phone, w.province as warehouse_province, w.city as warehouse_city, w.district as warehouse_district, w.address as warehouse_address, so.so_no as sales_order_no, oo.out_no as outbound_order_no, ooi.unit_price, so.created_at as sales_order_created_at, so.sales_person_id, so.association_order_id, so.association_order_no, soi.id as sales_order_item_id, soi.created_at as so_item_created_at, soi.receiver_name, soi.receiver_phone, soi.receiver_address, soi.logistics_company, soi.logistics_no`). Joins("LEFT JOIN outbound_order_item ooi ON shipping_order_item.outbound_order_item_id = ooi.id AND ooi.is_del = 0"). Joins("LEFT JOIN product p ON ooi.product_id = p.id AND p.is_del = 0"). Joins("LEFT JOIN product_category c ON p.category_id = c.id AND c.is_del = 0"). Joins("LEFT JOIN location l ON ooi.location_id = l.id AND l.is_del = 0"). Joins("LEFT JOIN warehouse w ON l.warehouse_id = w.id AND w.is_del = 0"). Joins("LEFT JOIN sales_order so ON ooi.sales_order_id = so.id AND so.is_del = 0"). Joins("LEFT JOIN sales_order_item soi ON ooi.sales_order_id = soi.sales_order_id AND ooi.product_id = soi.product_id AND soi.is_del = 0"). Joins("LEFT JOIN outbound_order oo ON ooi.out_order_id = oo.id AND oo.is_del = 0"). Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ?", orderIDs, 0). Order("l.sort ASC, l.id ASC"). Find(&allItems) // 按订单ID分组明细 itemsByOrderID := make(map[int64][]systemRes.ShippingOrderItemWithProduct, len(orderIDs)) for _, item := range allItems { itemsByOrderID[item.ShippingOrderID] = append(itemsByOrderID[item.ShippingOrderID], item) } // 组装最终结果 detailListItems := make([]systemRes.ShippingOrderDetailListItem, 0, len(orders)) for _, order := range orders { orderItems := itemsByOrderID[order.ID] detailItems := make([]systemRes.ShippingOrderDetailItem, 0, len(orderItems)) for _, item := range orderItems { detailItems = append(detailItems, systemRes.ShippingOrderDetailItem{ ID: item.ID, ShippingOrderID: item.ShippingOrderID, OutboundOrderItemID: item.OutboundOrderItemID, SalesOrderItemID: item.SalesOrderItemID, SalesOrderNo: item.SalesOrderNo, OutboundOrderNo: item.OutboundOrderNo, ProductID: item.ProductID, ProductName: item.ProductName, ProductCode: item.ProductCode, CategoryID: item.CategoryID, CategoryName: item.CategoryName, LocationID: item.LocationID, LocationName: item.LocationName, WarehouseName: item.WarehouseName, WarehouseCode: item.WarehouseCode, WarehouseContactPerson: item.WarehouseContactPerson, WarehouseContactPhone: item.WarehouseContactPhone, WarehouseProvince: item.WarehouseProvince, WarehouseCity: item.WarehouseCity, WarehouseDistrict: item.WarehouseDistrict, WarehouseAddress: item.WarehouseAddress, Quantity: item.Quantity, UnitPrice: item.UnitPrice, SalesOrderCreatedAt: item.SalesOrderCreatedAt, SalesPersonID: item.SalesPersonID, AssociationOrderID: item.AssociationOrderID, AssociationOrderNo: item.AssociationOrderNo, SoItemCreatedAt: item.SoItemCreatedAt, ReceiverName: item.ReceiverName, ReceiverPhone: item.ReceiverPhone, ReceiverAddress: item.ReceiverAddress, LogisticsCompany: item.LogisticsCompany, LogisticsNo: item.LogisticsNo, }) } shopList := shopsByOrderID[order.ID] if shopList == nil { shopList = []systemRes.OutboundShopInfo{} } detailListItems = append(detailListItems, systemRes.ConvertShippingOrderToDetailListItem( order.ShippingOrder, order.CustomerName, shopList, detailItems, )) } return &systemRes.ShippingOrderDetailListResponse{ List: detailListItems, Total: total, Page: req.Page, PageSize: req.PageSize, }, nil }