提交
This commit is contained in:
parent
bb6d91cbd0
commit
4317136295
33
controllers/store_info.go
Normal file
33
controllers/store_info.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"psi/constant"
|
||||||
|
"psi/database"
|
||||||
|
systemReq "psi/models/request"
|
||||||
|
systemRes "psi/models/response"
|
||||||
|
"psi/service"
|
||||||
|
"psi/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StoreInfoApi struct{}
|
||||||
|
|
||||||
|
var storeInfoService = service.StoreInfoService{}
|
||||||
|
|
||||||
|
// StoreInfo 店铺统计接口
|
||||||
|
func (i *StoreInfoApi) StoreInfo(c *gin.Context) {
|
||||||
|
var req systemReq.StoreInfoRequest
|
||||||
|
|
||||||
|
if err := c.ShouldBindQuery(&req); err != nil {
|
||||||
|
ValidAndFail(constant.LoggerChannelRequest, "店铺统计请求异常", "参数错误: "+err.Error(), c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := storeInfoService.StoreInfo(req, database.GetDB(c))
|
||||||
|
if err != nil {
|
||||||
|
utils.FailWithRequestLog(constant.LoggerChannelWork, "店铺统计异常", err, c, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
systemRes.OkWithDetailed(result, "查询成功", c)
|
||||||
|
}
|
||||||
@ -14,9 +14,9 @@ type OutboundOrder struct {
|
|||||||
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作员"`
|
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作员"`
|
||||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作员ID"`
|
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作员ID"`
|
||||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;index:idx_oo_del_shop_time;priority:3;index:idx_oo_del_time;priority:2;comment:创建时间戳(秒)"`
|
||||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;index:idx_oo_del_shop_time;priority:1;index:idx_oo_del_time;priority:1;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (OutboundOrder) TableName() string {
|
func (OutboundOrder) TableName() string {
|
||||||
|
|||||||
@ -25,7 +25,7 @@ type Product struct {
|
|||||||
Status int8 `json:"status" gorm:"type:tinyint(1);not null;default:1;comment:状态(0:禁用,1:启用)"`
|
Status int8 `json:"status" gorm:"type:tinyint(1);not null;default:1;comment:状态(0:禁用,1:启用)"`
|
||||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳"`
|
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳"`
|
||||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳"`
|
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳"`
|
||||||
IsDel int8 `json:"is_del" gorm:"not null;default:0;comment:逻辑删除"`
|
IsDel int8 `json:"is_del" gorm:"not null;default:0;index:idx_product_del;comment:逻辑删除"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Product) TableName() string {
|
func (Product) TableName() string {
|
||||||
|
|||||||
@ -7,16 +7,16 @@ type ReceivingOrder struct {
|
|||||||
PurchaseOrderID int64 `json:"purchase_order_id" gorm:"not null;default:0;index;comment:关联采购单ID(可为空,支持无来源入库)"`
|
PurchaseOrderID int64 `json:"purchase_order_id" gorm:"not null;default:0;index;comment:关联采购单ID(可为空,支持无来源入库)"`
|
||||||
WaveTaskID int64 `json:"wave_task_id" gorm:"not null;default:0;index;comment:关联波次任务ID"`
|
WaveTaskID int64 `json:"wave_task_id" gorm:"not null;default:0;index;comment:关联波次任务ID"`
|
||||||
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;index;comment:入库仓库ID"`
|
WarehouseID int64 `json:"warehouse_id" gorm:"not null;default:0;index;comment:入库仓库ID"`
|
||||||
ShopId int64 `json:"shop_id" gorm:"type:bigint(20);default:0;comment:店铺ID"`
|
ShopId int64 `json:"shop_id" gorm:"type:bigint(20);default:0;index:idx_ro_del_shop_time;priority:2;comment:店铺ID"`
|
||||||
SupplierID int64 `json:"supplier_id" gorm:"not null;default:0;comment:供应商ID"`
|
SupplierID int64 `json:"supplier_id" gorm:"not null;default:0;comment:供应商ID"`
|
||||||
ReceivingDate int64 `json:"receiving_date" gorm:"not null;default:0;comment:入库日期时间戳(秒)"`
|
ReceivingDate int64 `json:"receiving_date" gorm:"not null;default:0;comment:入库日期时间戳(秒)"`
|
||||||
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:待收货/pending, 2:验收中/checking, 3:已完成/completed, 4:已取消/cancelled)"`
|
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:待收货/pending, 2:验收中/checking, 3:已完成/completed, 4:已取消/cancelled)"`
|
||||||
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作人"`
|
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作人"`
|
||||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作人ID"`
|
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作人ID"`
|
||||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;index:idx_ro_del_time;priority:2;comment:创建时间戳(秒)"`
|
||||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;index:idx_ro_del_shop_time;priority:1;index:idx_ro_del_time;priority:1;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ReceivingOrder) TableName() string {
|
func (ReceivingOrder) TableName() string {
|
||||||
|
|||||||
@ -12,6 +12,7 @@ type GetOutboundOrderListRequest struct {
|
|||||||
EndDate int64 `form:"end_date"`
|
EndDate int64 `form:"end_date"`
|
||||||
AssociationOrderNo string `form:"association_order_no"`
|
AssociationOrderNo string `form:"association_order_no"`
|
||||||
LogisticsNo string `form:"logistics_no"`
|
LogisticsNo string `form:"logistics_no"`
|
||||||
|
ShopType int `form:"shop_type" json:"shop_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOutboundOrderDetailRequest 获取出库单详情请求
|
// GetOutboundOrderDetailRequest 获取出库单详情请求
|
||||||
|
|||||||
@ -12,6 +12,7 @@ type GetSalesOrderListRequest struct {
|
|||||||
EndDate int64 `form:"end_date"`
|
EndDate int64 `form:"end_date"`
|
||||||
AssociationOrderNo string `form:"association_order_no"`
|
AssociationOrderNo string `form:"association_order_no"`
|
||||||
LogisticsNo string `form:"logistics_no"`
|
LogisticsNo string `form:"logistics_no"`
|
||||||
|
ShopType int `form:"shop_type" json:"shop_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSalesOrderDetailRequest 获取销售订单详情请求
|
// GetSalesOrderDetailRequest 获取销售订单详情请求
|
||||||
@ -37,4 +38,5 @@ type GetSalesOrderDetailListRequest struct {
|
|||||||
EndDate int64 `form:"end_date"`
|
EndDate int64 `form:"end_date"`
|
||||||
AssociationOrderNo string `form:"association_order_no"`
|
AssociationOrderNo string `form:"association_order_no"`
|
||||||
LogisticsNo string `form:"logistics_no"`
|
LogisticsNo string `form:"logistics_no"`
|
||||||
|
ShopType int `form:"shop_type" json:"shop_type"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ type GetShippingOrderListRequest struct {
|
|||||||
EndDate int64 `form:"end_date" json:"end_date"`
|
EndDate int64 `form:"end_date" json:"end_date"`
|
||||||
AssociationOrderNo string `form:"association_order_no" json:"association_order_no"`
|
AssociationOrderNo string `form:"association_order_no" json:"association_order_no"`
|
||||||
LogisticsNo string `form:"logistics_no" json:"logistics_no"`
|
LogisticsNo string `form:"logistics_no" json:"logistics_no"`
|
||||||
|
ShopType int `form:"shop_type" json:"shop_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetShippingOrderDetailRequest 获取发货单详情请求
|
// GetShippingOrderDetailRequest 获取发货单详情请求
|
||||||
@ -29,4 +30,5 @@ type GetShippingOrderDetailListRequest struct {
|
|||||||
EndDate int64 `form:"end_date" json:"end_date"`
|
EndDate int64 `form:"end_date" json:"end_date"`
|
||||||
AssociationOrderNo string `form:"association_order_no" json:"association_order_no"`
|
AssociationOrderNo string `form:"association_order_no" json:"association_order_no"`
|
||||||
LogisticsNo string `form:"logistics_no" json:"logistics_no"`
|
LogisticsNo string `form:"logistics_no" json:"logistics_no"`
|
||||||
|
ShopType int `form:"shop_type" json:"shop_type"`
|
||||||
}
|
}
|
||||||
|
|||||||
7
models/request/store_info.go
Normal file
7
models/request/store_info.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
// StoreInfoRequest 店铺统计请求
|
||||||
|
type StoreInfoRequest struct {
|
||||||
|
TimeRange string `json:"time_range" form:"time_range"` // 时间范围:today/yesterday/7days/30days/90days/180days/365days
|
||||||
|
StoreName string `json:"store_name" form:"store_name"` // 店铺名称(模糊搜索)
|
||||||
|
}
|
||||||
@ -69,6 +69,7 @@ type SalesOrderDetailResponse struct {
|
|||||||
TotalAmount int64 `json:"total_amount"`
|
TotalAmount int64 `json:"total_amount"`
|
||||||
Status int8 `json:"status"`
|
Status int8 `json:"status"`
|
||||||
StatusText string `json:"status_text"`
|
StatusText string `json:"status_text"`
|
||||||
|
ShopTypeText string `json:"shop_type_text"`
|
||||||
SalesPerson string `json:"sales_person"`
|
SalesPerson string `json:"sales_person"`
|
||||||
SalesPersonID int64 `json:"sales_person_id"`
|
SalesPersonID int64 `json:"sales_person_id"`
|
||||||
AssociationOrderNo string `json:"association_order_no"`
|
AssociationOrderNo string `json:"association_order_no"`
|
||||||
@ -147,6 +148,7 @@ func ConvertSalesOrderToDetail(order models.SalesOrder, customerName string, war
|
|||||||
TotalAmount: order.TotalAmount,
|
TotalAmount: order.TotalAmount,
|
||||||
Status: order.Status,
|
Status: order.Status,
|
||||||
StatusText: GetSalesOrderStatusText(order.Status),
|
StatusText: GetSalesOrderStatusText(order.Status),
|
||||||
|
ShopTypeText: GetShopTypeStatusText(order.ShopType),
|
||||||
SalesPerson: order.SalesPerson,
|
SalesPerson: order.SalesPerson,
|
||||||
SalesPersonID: order.SalesPersonID,
|
SalesPersonID: order.SalesPersonID,
|
||||||
|
|
||||||
@ -189,7 +191,7 @@ func GetShopTypeStatusText(status int8) string {
|
|||||||
statusMap := map[int8]string{
|
statusMap := map[int8]string{
|
||||||
1: "拼多多",
|
1: "拼多多",
|
||||||
2: "孔夫子",
|
2: "孔夫子",
|
||||||
5: "咸鱼",
|
5: "闲鱼",
|
||||||
}
|
}
|
||||||
if text, ok := statusMap[status]; ok {
|
if text, ok := statusMap[status]; ok {
|
||||||
return text
|
return text
|
||||||
|
|||||||
@ -2,16 +2,17 @@ package response
|
|||||||
|
|
||||||
// DashboardStatistResponse 仪表盘统计响应
|
// DashboardStatistResponse 仪表盘统计响应
|
||||||
type DashboardStatistResponse struct {
|
type DashboardStatistResponse struct {
|
||||||
TotalReceivingCount int64 `json:"total_receiving_count"` // 总入库次数
|
TotalOrderCount int64 `json:"total_order_count"` // 今日订单数
|
||||||
TotalOutboundCount int64 `json:"total_outbound_count"` // 总出库次数
|
TotalReceivingCount int64 `json:"total_receiving_count"` // 今日入库数
|
||||||
|
TotalOutboundCount int64 `json:"total_outbound_count"` // 今日出库数
|
||||||
TotalSaleCount int64 `json:"total_sale_count"` // 今日销售数
|
TotalSaleCount int64 `json:"total_sale_count"` // 今日销售数
|
||||||
|
YesterdayOrderCount int64 `json:"yesterday_order_count"` // 昨日订单数
|
||||||
|
YesterdayReceivingCount int64 `json:"yesterday_receiving_count"` // 昨日入库数
|
||||||
|
YesterdayOutboundCount int64 `json:"yesterday_outbound_count"` // 昨日出库数
|
||||||
|
YesterdaySaleCount int64 `json:"yesterday_sale_count"` // 昨日销售数
|
||||||
UserStats []UserStatItem `json:"user_stats"` // 个人统计数据
|
UserStats []UserStatItem `json:"user_stats"` // 个人统计数据
|
||||||
ProductTotal int64 `json:"product_total"` // 商品总量
|
ProductTotal int64 `json:"product_total"` // 商品总量
|
||||||
InventoryTotal int64 `json:"inventory_total"` // 库存量
|
InventoryTotal int64 `json:"inventory_total"` // 库存量
|
||||||
TodayInbound int64 `json:"today_inbound"` // 今日入库
|
|
||||||
TodayOutbound int64 `json:"today_outbound"` // 今日出库
|
|
||||||
YesterdayInbound int64 `json:"yesterday_inbound"` // 昨日入库
|
|
||||||
YesterdayOutbound int64 `json:"yesterday_outbound"` // 昨日出库
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserStatItem 个人统计项
|
// UserStatItem 个人统计项
|
||||||
|
|||||||
12
models/response/store_info.go
Normal file
12
models/response/store_info.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
// StoreInfoResponse 店铺统计响应(每条记录对应一个店铺)
|
||||||
|
type StoreInfoResponse struct {
|
||||||
|
StoreName string `json:"store_name"` // 店铺名称
|
||||||
|
StoreType string `json:"store_type"` // 店铺类型描述
|
||||||
|
SaleCount int64 `json:"sale_count"` // 销售数量(时间范围内该店铺的销售订单数)
|
||||||
|
OutboundCount int64 `json:"outbound_count"` // 出库次数(当前表结构无店铺维度,暂返回 0)
|
||||||
|
ReceivingCount int64 `json:"receiving_count"` // 入库次数(当前表结构无店铺维度,暂返回 0)
|
||||||
|
OrderCount int64 `json:"order_count"` // 订单数量(同 sale_count,从 sales_order 表统计)
|
||||||
|
ShippingCount int64 `json:"shipping_count"` // 发货次数(当前表结构无店铺维度,暂返回 0)
|
||||||
|
}
|
||||||
@ -15,12 +15,12 @@ type SalesOrder struct {
|
|||||||
TotalAmount int64 `json:"total_amount" gorm:"not null;default:0;comment:订单总金额(分)"`
|
TotalAmount int64 `json:"total_amount" gorm:"not null;default:0;comment:订单总金额(分)"`
|
||||||
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:草稿/draft,2:已确认/confirmed,3:已分配库存/allocated,4:拣货完成/picking,5:已发货/shipped,6:已取消/cancelled)"`
|
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态(1:草稿/draft,2:已确认/confirmed,3:已分配库存/allocated,4:拣货完成/picking,5:已发货/shipped,6:已取消/cancelled)"`
|
||||||
SalesPerson string `json:"sales_person" gorm:"size:100;not null;default:'';comment:店铺名称"`
|
SalesPerson string `json:"sales_person" gorm:"size:100;not null;default:'';comment:店铺名称"`
|
||||||
SalesPersonID int64 `json:"sales_person_id" gorm:"not null;default:0;comment:店铺ID"`
|
SalesPersonID int64 `json:"sales_person_id" gorm:"not null;default:0;index:idx_so_del_person_time;priority:2;comment:店铺ID"`
|
||||||
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
Remark string `json:"remark" gorm:"size:255;not null;default:'';comment:备注"`
|
||||||
IsDistribution int8 `json:"is_distribution" gorm:"not null;default:0;comment:是否分销 0-正常 1-分销"`
|
IsDistribution int8 `json:"is_distribution" gorm:"not null;default:0;comment:是否分销 0-正常 1-分销"`
|
||||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间戳(秒)"`
|
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;index:idx_so_del_person_time;priority:3;index:idx_so_del_time;priority:2;comment:创建时间戳(秒)"`
|
||||||
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
UpdatedAt int64 `json:"updated_at" gorm:"type:bigint;not null;default:0;comment:更新时间戳(秒)"`
|
||||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;index:idx_so_del_person_time;priority:1;index:idx_so_del_time;priority:1;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (SalesOrder) TableName() string {
|
func (SalesOrder) TableName() string {
|
||||||
|
|||||||
@ -5,15 +5,15 @@ type ShippingOrder struct {
|
|||||||
ID int64 `json:"id" gorm:"primarykey;comment:主键ID"`
|
ID int64 `json:"id" gorm:"primarykey;comment:主键ID"`
|
||||||
ShippingNo string `json:"shipping_no" gorm:"size:64;not null;default:'';uniqueIndex;comment:发货单号"`
|
ShippingNo string `json:"shipping_no" gorm:"size:64;not null;default:'';uniqueIndex;comment:发货单号"`
|
||||||
CustomerID int64 `json:"customer_id" gorm:"not null;default:0;index;comment:客户ID"`
|
CustomerID int64 `json:"customer_id" gorm:"not null;default:0;index;comment:客户ID"`
|
||||||
ShopId int64 `json:"shop_id" gorm:"type:bigint(20);default:0;comment:店铺ID"`
|
ShopId int64 `json:"shop_id" gorm:"type:bigint(20);default:0;index:idx_sh_del_shop_time;priority:2;comment:店铺ID"`
|
||||||
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态:1=待发货 2=已发货 3=已签收 4=已取消"`
|
Status int8 `json:"status" gorm:"not null;default:1;index;comment:状态:1=待发货 2=已发货 3=已签收 4=已取消"`
|
||||||
ShippingTime *int64 `json:"shipping_time" gorm:"type:bigint;comment:发货时间(时间戳秒)"`
|
ShippingTime *int64 `json:"shipping_time" gorm:"type:bigint;comment:发货时间(时间戳秒)"`
|
||||||
ExpectedArriveTime *int64 `json:"expected_arrive_time" gorm:"type:bigint;comment:预计到达时间(时间戳秒)"`
|
ExpectedArriveTime *int64 `json:"expected_arrive_time" gorm:"type:bigint;comment:预计到达时间(时间戳秒)"`
|
||||||
ActualArriveTime *int64 `json:"actual_arrive_time" gorm:"type:bigint;comment:实际签收时间(时间戳秒)"`
|
ActualArriveTime *int64 `json:"actual_arrive_time" gorm:"type:bigint;comment:实际签收时间(时间戳秒)"`
|
||||||
Operator string `json:"operator" gorm:"size:50;comment:操作人"`
|
Operator string `json:"operator" gorm:"size:50;comment:操作人"`
|
||||||
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;comment:创建时间(时间戳秒)"`
|
CreatedAt int64 `json:"created_at" gorm:"type:bigint;not null;default:0;index:idx_sh_del_shop_time;priority:3;index:idx_sh_del_time;priority:2;comment:创建时间(时间戳秒)"`
|
||||||
UpdatedAt *int64 `json:"updated_at" gorm:"type:bigint;comment:更新时间(时间戳秒)"`
|
UpdatedAt *int64 `json:"updated_at" gorm:"type:bigint;comment:更新时间(时间戳秒)"`
|
||||||
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;index:idx_sh_del_shop_time;priority:1;index:idx_sh_del_time;priority:1;comment:逻辑删除标记(0:未删除,1:已删除)"`
|
||||||
Remark string `json:"remark" gorm:"size:500;comment:备注"`
|
Remark string `json:"remark" gorm:"size:500;comment:备注"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -125,23 +125,34 @@ func (s *StatistTaskService) generateDashboardDailyStat(tx *gorm.DB, statDate in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// statist.stat_date 存储 YYYYMMDD 格式 int64(如 20260626),非 Unix 时间戳
|
||||||
|
// statDate 已经是 YYYYMMDD 格式,直接用于查询
|
||||||
|
// 昨天的 YYYYMMDD = statDate;前天需从 startDate(Unix时间戳)反算
|
||||||
|
yesterdayTime := time.Unix(startDate, 0)
|
||||||
|
dayBeforeTime := yesterdayTime.AddDate(0, 0, -1)
|
||||||
|
dayBeforeStatDateStr := fmt.Sprintf("%04d%02d%02d", dayBeforeTime.Year(), dayBeforeTime.Month(), dayBeforeTime.Day())
|
||||||
|
var dayBeforeStatDate int64
|
||||||
|
fmt.Sscanf(dayBeforeStatDateStr, "%d", &dayBeforeStatDate)
|
||||||
|
|
||||||
|
// 前天的 Unix 时间戳范围(用于 sales_order 查询,sales_order.created_at 存储 Unix 时间戳)
|
||||||
|
dayBeforeStart := time.Date(dayBeforeTime.Year(), dayBeforeTime.Month(), dayBeforeTime.Day(), 0, 0, 0, 0, dayBeforeTime.Location()).Unix()
|
||||||
|
dayBeforeEnd := time.Date(dayBeforeTime.Year(), dayBeforeTime.Month(), dayBeforeTime.Day(), 23, 59, 59, 0, dayBeforeTime.Location()).Unix()
|
||||||
|
|
||||||
var totalReceivingNum, totalOutboundNum int64
|
var totalReceivingNum, totalOutboundNum int64
|
||||||
tx.Model(&models.Statist{}).
|
tx.Model(&models.Statist{}).
|
||||||
Where("stat_date <= ? AND is_del = ?", endDate, 0).
|
Where("stat_date <= ? AND is_del = ?", statDate, 0).
|
||||||
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
||||||
Row().Scan(&totalReceivingNum, &totalOutboundNum)
|
Row().Scan(&totalReceivingNum, &totalOutboundNum)
|
||||||
|
|
||||||
var todayInbound, todayOutbound int64
|
var todayInbound, todayOutbound int64
|
||||||
tx.Model(&models.Statist{}).
|
tx.Model(&models.Statist{}).
|
||||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", startDate, endDate, 0).
|
Where("stat_date = ? AND is_del = ?", statDate, 0).
|
||||||
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
||||||
Row().Scan(&todayInbound, &todayOutbound)
|
Row().Scan(&todayInbound, &todayOutbound)
|
||||||
|
|
||||||
beforeYesterdayStart := time.Date(time.Unix(startDate, 0).Year(), time.Unix(startDate, 0).Month(), time.Unix(startDate, 0).Day()-1, 0, 0, 0, 0, time.Unix(startDate, 0).Location()).Unix()
|
|
||||||
beforeYesterdayEnd := time.Date(time.Unix(startDate, 0).Year(), time.Unix(startDate, 0).Month(), time.Unix(startDate, 0).Day()-1, 23, 59, 59, 0, time.Unix(startDate, 0).Location()).Unix()
|
|
||||||
var yesterdayInbound, yesterdayOutbound int64
|
var yesterdayInbound, yesterdayOutbound int64
|
||||||
tx.Model(&models.Statist{}).
|
tx.Model(&models.Statist{}).
|
||||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", beforeYesterdayStart, beforeYesterdayEnd, 0).
|
Where("stat_date = ? AND is_del = ?", dayBeforeStatDate, 0).
|
||||||
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
||||||
Row().Scan(&yesterdayInbound, &yesterdayOutbound)
|
Row().Scan(&yesterdayInbound, &yesterdayOutbound)
|
||||||
|
|
||||||
@ -157,7 +168,7 @@ func (s *StatistTaskService) generateDashboardDailyStat(tx *gorm.DB, statDate in
|
|||||||
|
|
||||||
var yesterdaySalesCount int64
|
var yesterdaySalesCount int64
|
||||||
tx.Model(&models.SalesOrder{}).
|
tx.Model(&models.SalesOrder{}).
|
||||||
Where("created_at >= ? AND created_at <= ? AND is_del = ?", beforeYesterdayStart, beforeYesterdayEnd, 0).
|
Where("created_at >= ? AND created_at <= ? AND is_del = ?", dayBeforeStart, dayBeforeEnd, 0).
|
||||||
Count(&yesterdaySalesCount)
|
Count(&yesterdaySalesCount)
|
||||||
|
|
||||||
var productTotal int64
|
var productTotal int64
|
||||||
@ -231,7 +242,7 @@ func (s *StatistTaskService) generateUserDailyStat(tx *gorm.DB, mainDB *gorm.DB,
|
|||||||
}
|
}
|
||||||
var receivingStats []UserReceivingStat
|
var receivingStats []UserReceivingStat
|
||||||
tx.Model(&models.Statist{}).
|
tx.Model(&models.Statist{}).
|
||||||
Where("create_by IN ? AND stat_date >= ? AND stat_date <= ? AND is_del = ?", userIDs, startDate, endDate, 0).
|
Where("create_by IN ? AND stat_date = ? AND is_del = ?", userIDs, statDate, 0).
|
||||||
Select("create_by, COALESCE(SUM(receiving_num), 0) as receiving_num, COALESCE(SUM(outbound_num), 0) as outbound_num").
|
Select("create_by, COALESCE(SUM(receiving_num), 0) as receiving_num, COALESCE(SUM(outbound_num), 0) as outbound_num").
|
||||||
Group("create_by").
|
Group("create_by").
|
||||||
Find(&receivingStats)
|
Find(&receivingStats)
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import (
|
|||||||
systemRes "psi/models/response"
|
systemRes "psi/models/response"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BookService struct{}
|
type BookService struct{}
|
||||||
|
|||||||
@ -59,6 +59,13 @@ func (s *OutboundService) GetOutboundOrderList(req systemReq.GetOutboundOrderLis
|
|||||||
Where("outbound_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%")
|
Where("outbound_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%")
|
||||||
query = query.Where("outbound_order.id IN (?)", subQuery)
|
query = query.Where("outbound_order.id IN (?)", subQuery)
|
||||||
}
|
}
|
||||||
|
if req.ShopType > 0 {
|
||||||
|
subQuery := databaseConn.Table("outbound_order_item").
|
||||||
|
Select("outbound_order_item.out_order_id").
|
||||||
|
Joins("INNER JOIN sales_order ON outbound_order_item.sales_order_id = sales_order.id AND sales_order.is_del = 0").
|
||||||
|
Where("outbound_order_item.is_del = 0 AND sales_order.shop_type = ?", req.ShopType)
|
||||||
|
query = query.Where("outbound_order.id IN (?)", subQuery)
|
||||||
|
}
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
if err := query.Count(&total).Error; err != nil {
|
if err := query.Count(&total).Error; err != nil {
|
||||||
@ -86,50 +93,85 @@ func (s *OutboundService) GetOutboundOrderList(req systemReq.GetOutboundOrderLis
|
|||||||
return nil, utils.NewError("查询出库单列表失败")
|
return nil, utils.NewError("查询出库单列表失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
orderItems := make([]systemRes.OutboundOrderItem, 0, len(orders))
|
// 收集订单ID用于批量查询
|
||||||
for _, order := range orders {
|
orderIDs := make([]int64, len(orders))
|
||||||
var shopList []systemRes.OutboundShopInfo
|
for i, order := range orders {
|
||||||
|
orderIDs[i] = order.ID
|
||||||
|
}
|
||||||
|
|
||||||
var shops []struct {
|
// 批量查询店铺信息:按 out_order_id 分组
|
||||||
|
type shopRow struct {
|
||||||
|
OutOrderID int64 `gorm:"column:out_order_id"`
|
||||||
ShopName string `gorm:"column:shop_name"`
|
ShopName string `gorm:"column:shop_name"`
|
||||||
ShopType int8 `gorm:"column:shop_type"`
|
ShopType int8 `gorm:"column:shop_type"`
|
||||||
}
|
}
|
||||||
|
var shopRows []shopRow
|
||||||
databaseConn.Table("outbound_order_item").
|
databaseConn.Table("outbound_order_item").
|
||||||
Select("DISTINCT so.sales_person as shop_name, so.shop_type as shop_type").
|
Select("DISTINCT outbound_order_item.out_order_id, so.sales_person as shop_name, so.shop_type as shop_type").
|
||||||
Joins("INNER JOIN sales_order so ON outbound_order_item.sales_order_id = so.id AND so.is_del = 0").
|
Joins("INNER JOIN sales_order so ON outbound_order_item.sales_order_id = so.id AND so.is_del = 0").
|
||||||
Where("outbound_order_item.out_order_id = ? AND outbound_order_item.is_del = ?", order.ID, 0).
|
Where("outbound_order_item.out_order_id IN ? AND outbound_order_item.is_del = ?", orderIDs, 0).
|
||||||
Scan(&shops)
|
Scan(&shopRows)
|
||||||
|
|
||||||
for _, shop := range shops {
|
shopsByOrderID := make(map[int64][]systemRes.OutboundShopInfo, len(orderIDs))
|
||||||
shopList = append(shopList, systemRes.OutboundShopInfo{
|
for _, row := range shopRows {
|
||||||
ShopName: shop.ShopName,
|
shopsByOrderID[row.OutOrderID] = append(shopsByOrderID[row.OutOrderID], systemRes.OutboundShopInfo{
|
||||||
ShopType: shop.ShopType,
|
ShopName: row.ShopName,
|
||||||
ShopTypeText: systemRes.GetShopTypeText(shop.ShopType),
|
ShopType: row.ShopType,
|
||||||
|
ShopTypeText: systemRes.GetShopTypeText(row.ShopType),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var associationOrderNos string
|
// 批量查询关联订单号:按 out_order_id 分组
|
||||||
|
type assocRow struct {
|
||||||
|
OutOrderID int64 `gorm:"column:out_order_id"`
|
||||||
|
AssociationOrderNo string `gorm:"column:association_order_no"`
|
||||||
|
}
|
||||||
|
var assocRows []assocRow
|
||||||
databaseConn.Table("outbound_order_item").
|
databaseConn.Table("outbound_order_item").
|
||||||
Select("GROUP_CONCAT(DISTINCT so.association_order_no SEPARATOR ', ')").
|
Select("outbound_order_item.out_order_id, GROUP_CONCAT(DISTINCT so.association_order_no SEPARATOR ', ') as association_order_no").
|
||||||
Joins("INNER JOIN sales_order so ON outbound_order_item.sales_order_id = so.id AND so.is_del = 0").
|
Joins("INNER JOIN sales_order so ON outbound_order_item.sales_order_id = so.id AND so.is_del = 0").
|
||||||
Where("outbound_order_item.out_order_id = ? AND outbound_order_item.is_del = ? AND so.association_order_no != ''", order.ID, 0).
|
Where("outbound_order_item.out_order_id IN ? AND outbound_order_item.is_del = ? AND so.association_order_no != ''", orderIDs, 0).
|
||||||
Scan(&associationOrderNos)
|
Group("outbound_order_item.out_order_id").
|
||||||
|
Scan(&assocRows)
|
||||||
|
|
||||||
var logisticsNos string
|
assocByOrderID := make(map[int64]string, len(assocRows))
|
||||||
|
for _, row := range assocRows {
|
||||||
|
assocByOrderID[row.OutOrderID] = row.AssociationOrderNo
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量查询物流单号:按 out_order_id 分组
|
||||||
|
type logRow struct {
|
||||||
|
OutOrderID int64 `gorm:"column:out_order_id"`
|
||||||
|
LogisticsNos string `gorm:"column:logistics_nos"`
|
||||||
|
}
|
||||||
|
var logRows []logRow
|
||||||
databaseConn.Table("outbound_order_item").
|
databaseConn.Table("outbound_order_item").
|
||||||
Select("GROUP_CONCAT(DISTINCT soi.logistics_no SEPARATOR ', ')").
|
Select("outbound_order_item.out_order_id, COALESCE(GROUP_CONCAT(DISTINCT soi.logistics_no SEPARATOR ', '), '') as logistics_nos").
|
||||||
Joins("INNER JOIN sales_order_item soi ON outbound_order_item.sales_order_id = soi.sales_order_id AND outbound_order_item.product_id = soi.product_id AND soi.is_del = 0").
|
Joins("INNER JOIN sales_order_item soi ON outbound_order_item.sales_order_id = soi.sales_order_id AND outbound_order_item.product_id = soi.product_id AND soi.is_del = 0").
|
||||||
Where("outbound_order_item.out_order_id = ? AND outbound_order_item.is_del = ? AND soi.logistics_no != ''", order.ID, 0).
|
Where("outbound_order_item.out_order_id IN ? AND outbound_order_item.is_del = ? AND soi.logistics_no != ''", orderIDs, 0).
|
||||||
Scan(&logisticsNos)
|
Group("outbound_order_item.out_order_id").
|
||||||
|
Scan(&logRows)
|
||||||
|
|
||||||
|
logByOrderID := make(map[int64]string, len(logRows))
|
||||||
|
for _, row := range logRows {
|
||||||
|
logByOrderID[row.OutOrderID] = row.LogisticsNos
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组装结果:每个订单从 map 中查找
|
||||||
|
orderItems := make([]systemRes.OutboundOrderItem, 0, len(orders))
|
||||||
|
for _, order := range orders {
|
||||||
|
shopList := shopsByOrderID[order.ID]
|
||||||
|
if shopList == nil {
|
||||||
|
shopList = []systemRes.OutboundShopInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
orderItems = append(orderItems, systemRes.ConvertOutboundOrderToItem(
|
orderItems = append(orderItems, systemRes.ConvertOutboundOrderToItem(
|
||||||
order.OutboundOrder,
|
order.OutboundOrder,
|
||||||
order.CustomerName,
|
order.CustomerName,
|
||||||
order.WarehouseName,
|
order.WarehouseName,
|
||||||
shopList,
|
shopList,
|
||||||
associationOrderNos,
|
assocByOrderID[order.ID],
|
||||||
logisticsNos,
|
logByOrderID[order.ID],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -333,6 +333,15 @@ func (s *ProcessService) BindWave(req systemReq.BindWaveRequest, db ...*gorm.DB)
|
|||||||
return fmt.Errorf("采购订单不存在: %v", err)
|
return fmt.Errorf("采购订单不存在: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取小车关联的店铺ID
|
||||||
|
var shopId int64
|
||||||
|
if waveTask.CarId > 0 {
|
||||||
|
var carShop models.CarShop
|
||||||
|
if err := tx.Where("car_id = ? AND is_del = 0", waveTask.CarId).First(&carShop).Error; err == nil {
|
||||||
|
shopId = carShop.ShopID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
receivingNo := utils.GenerateReceivingNo()
|
receivingNo := utils.GenerateReceivingNo()
|
||||||
|
|
||||||
receivingOrder := models.ReceivingOrder{
|
receivingOrder := models.ReceivingOrder{
|
||||||
@ -340,6 +349,7 @@ func (s *ProcessService) BindWave(req systemReq.BindWaveRequest, db ...*gorm.DB)
|
|||||||
PurchaseOrderID: purchaseOrder.ID, //采购订单ID
|
PurchaseOrderID: purchaseOrder.ID, //采购订单ID
|
||||||
WaveTaskID: waveTaskID, //入库任务ID
|
WaveTaskID: waveTaskID, //入库任务ID
|
||||||
WarehouseID: purchaseOrder.WarehouseID, //仓库ID
|
WarehouseID: purchaseOrder.WarehouseID, //仓库ID
|
||||||
|
ShopId: shopId, //店铺ID(来自小车)
|
||||||
SupplierID: purchaseOrder.SupplierID, //供应商ID
|
SupplierID: purchaseOrder.SupplierID, //供应商ID
|
||||||
ReceivingDate: now, //入库日期时间戳(秒)
|
ReceivingDate: now, //入库日期时间戳(秒)
|
||||||
Status: constant.ReceivingStatusPending, //状态(1:待收货/pending, )
|
Status: constant.ReceivingStatusPending, //状态(1:待收货/pending, )
|
||||||
@ -1623,6 +1633,7 @@ func (s *ProcessService) CreateOutboundOrder(req systemReq.CreateOutboundOrderRe
|
|||||||
WaveTaskID: 0,
|
WaveTaskID: 0,
|
||||||
WarehouseID: warehouseID,
|
WarehouseID: warehouseID,
|
||||||
CustomerID: customerID,
|
CustomerID: customerID,
|
||||||
|
ShopId: salesOrders[0].SalesPersonID,
|
||||||
TotalQuantity: 0,
|
TotalQuantity: 0,
|
||||||
TotalAmount: 0,
|
TotalAmount: 0,
|
||||||
Status: constant.OutboundStatusCreated,
|
Status: constant.OutboundStatusCreated,
|
||||||
@ -2127,11 +2138,29 @@ func (s *ProcessService) CreateShippingOrder(req systemReq.CreateShippingOrderRe
|
|||||||
return fmt.Errorf("选中的出库单没有明细数据")
|
return fmt.Errorf("选中的出库单没有明细数据")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取店铺ID(从出库单明细追溯销售单的 sales_person_id)
|
||||||
|
var shopId int64
|
||||||
|
if outboundOrders[0].ShopId > 0 {
|
||||||
|
shopId = outboundOrders[0].ShopId
|
||||||
|
} else {
|
||||||
|
// 兜底:通过 outbound_order_item → sales_order → sales_person_id 获取
|
||||||
|
var salesPersonID int64
|
||||||
|
if err := tx.Raw(`
|
||||||
|
SELECT so.sales_person_id FROM outbound_order_item ooi
|
||||||
|
JOIN sales_order so ON ooi.sales_order_id = so.id AND so.is_del = 0
|
||||||
|
WHERE ooi.out_order_id IN ? AND ooi.is_del = 0
|
||||||
|
LIMIT 1
|
||||||
|
`, req.OutboundOrderIDs).Scan(&salesPersonID).Error; err == nil && salesPersonID > 0 {
|
||||||
|
shopId = salesPersonID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
shippingNo = utils.GenerateShippingNo()
|
shippingNo = utils.GenerateShippingNo()
|
||||||
|
|
||||||
shippingOrder := models.ShippingOrder{
|
shippingOrder := models.ShippingOrder{
|
||||||
ShippingNo: shippingNo,
|
ShippingNo: shippingNo,
|
||||||
CustomerID: customerID,
|
CustomerID: customerID,
|
||||||
|
ShopId: shopId,
|
||||||
Status: constant.ShippingStatusPending,
|
Status: constant.ShippingStatusPending,
|
||||||
ExpectedArriveTime: req.ExpectedArriveTime,
|
ExpectedArriveTime: req.ExpectedArriveTime,
|
||||||
Operator: operator,
|
Operator: operator,
|
||||||
|
|||||||
@ -57,6 +57,9 @@ func (s *SalesService) GetSalesOrderList(req systemReq.GetSalesOrderListRequest,
|
|||||||
Where("logistics_no LIKE ? AND is_del = 0", "%"+req.LogisticsNo+"%")
|
Where("logistics_no LIKE ? AND is_del = 0", "%"+req.LogisticsNo+"%")
|
||||||
query = query.Where("sales_order.id IN (?)", subQuery)
|
query = query.Where("sales_order.id IN (?)", subQuery)
|
||||||
}
|
}
|
||||||
|
if req.ShopType > 0 {
|
||||||
|
query = query.Where("sales_order.shop_type = ?", req.ShopType)
|
||||||
|
}
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
if err := query.Count(&total).Error; err != nil {
|
if err := query.Count(&total).Error; err != nil {
|
||||||
@ -84,19 +87,35 @@ func (s *SalesService) GetSalesOrderList(req systemReq.GetSalesOrderListRequest,
|
|||||||
return nil, utils.NewError("查询销售订单列表失败")
|
return nil, utils.NewError("查询销售订单列表失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 收集订单ID用于批量查询物流单号
|
||||||
|
orderIDs := make([]int64, len(orders))
|
||||||
|
for i, order := range orders {
|
||||||
|
orderIDs[i] = order.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type logRow struct {
|
||||||
|
SalesOrderID int64 `gorm:"column:sales_order_id"`
|
||||||
|
LogisticsNos string `gorm:"column:logistics_nos"`
|
||||||
|
}
|
||||||
|
var logRows []logRow
|
||||||
|
databaseConn.Table("sales_order_item").
|
||||||
|
Select("sales_order_id, COALESCE(GROUP_CONCAT(DISTINCT logistics_no SEPARATOR ', '), '') as logistics_nos").
|
||||||
|
Where("sales_order_id IN ? AND is_del = ? AND logistics_no != ''", orderIDs, 0).
|
||||||
|
Group("sales_order_id").
|
||||||
|
Scan(&logRows)
|
||||||
|
|
||||||
|
logByOrderID := make(map[int64]string, len(logRows))
|
||||||
|
for _, row := range logRows {
|
||||||
|
logByOrderID[row.SalesOrderID] = row.LogisticsNos
|
||||||
|
}
|
||||||
|
|
||||||
orderItems := make([]systemRes.SalesOrderItem, 0, len(orders))
|
orderItems := make([]systemRes.SalesOrderItem, 0, len(orders))
|
||||||
for _, order := range orders {
|
for _, order := range orders {
|
||||||
var logisticsNos string
|
|
||||||
databaseConn.Table("sales_order_item").
|
|
||||||
Select("GROUP_CONCAT(DISTINCT logistics_no SEPARATOR ', ')").
|
|
||||||
Where("sales_order_id = ? AND is_del = ? AND logistics_no != ''", order.ID, 0).
|
|
||||||
Scan(&logisticsNos)
|
|
||||||
|
|
||||||
orderItems = append(orderItems, systemRes.ConvertSalesOrderToItem(
|
orderItems = append(orderItems, systemRes.ConvertSalesOrderToItem(
|
||||||
order.SalesOrder,
|
order.SalesOrder,
|
||||||
order.CustomerName,
|
order.CustomerName,
|
||||||
order.WarehouseName,
|
order.WarehouseName,
|
||||||
logisticsNos,
|
logByOrderID[order.ID],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +187,9 @@ func (s *SalesService) GetSalesOrderDetailList(req systemReq.GetSalesOrderDetail
|
|||||||
Where("logistics_no LIKE ? AND is_del = 0", "%"+req.LogisticsNo+"%")
|
Where("logistics_no LIKE ? AND is_del = 0", "%"+req.LogisticsNo+"%")
|
||||||
query = query.Where("sales_order.id IN (?)", subQuery)
|
query = query.Where("sales_order.id IN (?)", subQuery)
|
||||||
}
|
}
|
||||||
|
if req.ShopType > 0 {
|
||||||
|
query = query.Where("sales_order.shop_type = ?", req.ShopType)
|
||||||
|
}
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
if err := query.Count(&total).Error; err != nil {
|
if err := query.Count(&total).Error; err != nil {
|
||||||
|
|||||||
@ -57,6 +57,14 @@ func (s *ShippingService) GetShippingOrderList(req systemReq.GetShippingOrderLis
|
|||||||
Where("shipping_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%")
|
Where("shipping_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%")
|
||||||
query = query.Where("shipping_order.id IN (?)", subQuery)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
if err := query.Count(&total).Error; err != nil {
|
if err := query.Count(&total).Error; err != nil {
|
||||||
@ -83,52 +91,87 @@ func (s *ShippingService) GetShippingOrderList(req systemReq.GetShippingOrderLis
|
|||||||
return nil, utils.NewError("查询发货单列表失败")
|
return nil, utils.NewError("查询发货单列表失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
orderItems := make([]systemRes.ShippingOrderItem, 0, len(orders))
|
// 收集订单ID用于批量查询
|
||||||
for _, order := range orders {
|
orderIDs := make([]int64, len(orders))
|
||||||
var shopList []systemRes.OutboundShopInfo
|
for i, order := range orders {
|
||||||
|
orderIDs[i] = order.ID
|
||||||
|
}
|
||||||
|
|
||||||
var shops []struct {
|
// 批量查询店铺信息:按 shipping_order_id 分组
|
||||||
|
type shopRow struct {
|
||||||
|
ShippingOrderID int64 `gorm:"column:shipping_order_id"`
|
||||||
ShopName string `gorm:"column:shop_name"`
|
ShopName string `gorm:"column:shop_name"`
|
||||||
ShopType int8 `gorm:"column:shop_type"`
|
ShopType int8 `gorm:"column:shop_type"`
|
||||||
}
|
}
|
||||||
|
var shopRows []shopRow
|
||||||
databaseConn.Table("shipping_order_item").
|
databaseConn.Table("shipping_order_item").
|
||||||
Select("DISTINCT so.sales_person as shop_name, so.shop_type as shop_type").
|
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 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").
|
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 = ? AND shipping_order_item.is_del = ?", order.ID, 0).
|
Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ?", orderIDs, 0).
|
||||||
Scan(&shops)
|
Scan(&shopRows)
|
||||||
|
|
||||||
for _, shop := range shops {
|
shopsByOrderID := make(map[int64][]systemRes.OutboundShopInfo, len(orderIDs))
|
||||||
shopList = append(shopList, systemRes.OutboundShopInfo{
|
for _, row := range shopRows {
|
||||||
ShopName: shop.ShopName,
|
shopsByOrderID[row.ShippingOrderID] = append(shopsByOrderID[row.ShippingOrderID], systemRes.OutboundShopInfo{
|
||||||
ShopType: shop.ShopType,
|
ShopName: row.ShopName,
|
||||||
ShopTypeText: systemRes.GetShopTypeText(shop.ShopType),
|
ShopType: row.ShopType,
|
||||||
|
ShopTypeText: systemRes.GetShopTypeText(row.ShopType),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var associationOrderNos string
|
// 批量查询关联订单号:按 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").
|
databaseConn.Table("shipping_order_item").
|
||||||
Select("GROUP_CONCAT(DISTINCT so.association_order_no SEPARATOR ', ')").
|
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 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").
|
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 = ? AND shipping_order_item.is_del = ? AND so.association_order_no != ''", order.ID, 0).
|
Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ? AND so.association_order_no != ''", orderIDs, 0).
|
||||||
Scan(&associationOrderNos)
|
Group("shipping_order_item.shipping_order_id").
|
||||||
|
Scan(&assocRows)
|
||||||
|
|
||||||
var logisticsNos string
|
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").
|
databaseConn.Table("shipping_order_item").
|
||||||
Select("GROUP_CONCAT(DISTINCT soi.logistics_no SEPARATOR ', ')").
|
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 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").
|
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 = ? AND shipping_order_item.is_del = ? AND soi.logistics_no != ''", order.ID, 0).
|
Where("shipping_order_item.shipping_order_id IN ? AND shipping_order_item.is_del = ? AND soi.logistics_no != ''", orderIDs, 0).
|
||||||
Scan(&logisticsNos)
|
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(
|
orderItems = append(orderItems, systemRes.ConvertShippingOrderToItem(
|
||||||
order.ShippingOrder,
|
order.ShippingOrder,
|
||||||
order.CustomerName,
|
order.CustomerName,
|
||||||
shopList,
|
shopList,
|
||||||
associationOrderNos,
|
assocByOrderID[order.ID],
|
||||||
logisticsNos,
|
logByOrderID[order.ID],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,6 +331,14 @@ func (s *ShippingService) GetShippingOrderDetailList(req systemReq.GetShippingOr
|
|||||||
Where("shipping_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%")
|
Where("shipping_order_item.is_del = 0 AND sales_order_item.logistics_no LIKE ?", "%"+req.LogisticsNo+"%")
|
||||||
query = query.Where("shipping_order.id IN (?)", subQuery)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
if err := query.Count(&total).Error; err != nil {
|
if err := query.Count(&total).Error; err != nil {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"psi/models"
|
"psi/models"
|
||||||
systemReq "psi/models/request"
|
systemReq "psi/models/request"
|
||||||
systemRes "psi/models/response"
|
systemRes "psi/models/response"
|
||||||
"psi/utils"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -152,6 +152,7 @@ type StatistService struct{}
|
|||||||
}, nil
|
}, nil
|
||||||
}*/
|
}*/
|
||||||
// DashboardStatist 获取仪表盘统计数据
|
// DashboardStatist 获取仪表盘统计数据
|
||||||
|
// 统一口径:所有统计字段改为实时查原始单据表,不再依赖 dashboard_daily_stat 预计算表
|
||||||
func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest, db ...*gorm.DB) (*systemRes.DashboardStatistResponse, error) {
|
func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest, db ...*gorm.DB) (*systemRes.DashboardStatistResponse, error) {
|
||||||
databaseConn := database.OptionalDB(db...)
|
databaseConn := database.OptionalDB(db...)
|
||||||
|
|
||||||
@ -159,8 +160,6 @@ func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest,
|
|||||||
startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
||||||
endOfDay := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()).Unix()
|
endOfDay := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()).Unix()
|
||||||
|
|
||||||
hasExplicitDate := req.EndDate != 0
|
|
||||||
|
|
||||||
if req.StartDate == 0 {
|
if req.StartDate == 0 {
|
||||||
req.StartDate = startOfDay
|
req.StartDate = startOfDay
|
||||||
}
|
}
|
||||||
@ -168,162 +167,209 @@ func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest,
|
|||||||
req.EndDate = endOfDay
|
req.EndDate = endOfDay
|
||||||
}
|
}
|
||||||
|
|
||||||
var statDateStr string
|
|
||||||
if hasExplicitDate {
|
|
||||||
statDateStr = time.Unix(req.EndDate, 0).Format("20060102")
|
|
||||||
} else {
|
|
||||||
statDateStr = now.Format("20060102")
|
|
||||||
}
|
|
||||||
var statDate int64
|
|
||||||
if _, err := fmt.Sscanf(statDateStr, "%d", &statDate); err != nil {
|
|
||||||
return s.getDashboardStatRealtime(databaseConn, req.StartDate, req.EndDate)
|
return s.getDashboardStatRealtime(databaseConn, req.StartDate, req.EndDate)
|
||||||
}
|
|
||||||
|
|
||||||
var dashboardStat models.DashboardDailyStat
|
|
||||||
err := databaseConn.Where("stat_date = ? AND is_del = ?", statDate, 0).First(&dashboardStat).Error
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return s.getDashboardStatRealtime(databaseConn, req.StartDate, req.EndDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
var userStats []systemRes.UserStatItem
|
|
||||||
var userDailyStats []models.UserDailyStat
|
|
||||||
if err := databaseConn.Where("stat_date = ? AND is_del = ?", statDate, 0).Find(&userDailyStats).Error; err == nil {
|
|
||||||
for _, userStat := range userDailyStats {
|
|
||||||
userStats = append(userStats, systemRes.UserStatItem{
|
|
||||||
UserID: userStat.UserID,
|
|
||||||
UserName: userStat.UserName,
|
|
||||||
ReceivingCount: userStat.ReceivingNum,
|
|
||||||
OutboundCount: userStat.OutboundNum,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &systemRes.DashboardStatistResponse{
|
|
||||||
TotalReceivingCount: dashboardStat.TotalReceivingNum,
|
|
||||||
TotalOutboundCount: dashboardStat.TotalOutboundNum,
|
|
||||||
TotalSaleCount: dashboardStat.TodaySalesCount,
|
|
||||||
UserStats: userStats,
|
|
||||||
ProductTotal: dashboardStat.ProductTotal,
|
|
||||||
InventoryTotal: dashboardStat.InventoryTotal,
|
|
||||||
TodayInbound: dashboardStat.TodayInbound,
|
|
||||||
TodayOutbound: dashboardStat.TodayOutbound,
|
|
||||||
YesterdayInbound: dashboardStat.YesterdayInbound,
|
|
||||||
YesterdayOutbound: dashboardStat.YesterdayOutbound,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDashboardStatRealtime 实时查询仪表盘统计数据(降级方案)
|
// getDashboardStatRealtime 实时查询仪表盘统计数据
|
||||||
|
// 统一口径:所有统计字段直接查原始单据表(sales_order / receiving_order / outbound_order)
|
||||||
|
// 所有独立查询通过 goroutine 并发执行,总耗时 = max(各查询耗时)
|
||||||
func (s *StatistService) getDashboardStatRealtime(databaseConn *gorm.DB, startDate, endDate int64) (*systemRes.DashboardStatistResponse, error) {
|
func (s *StatistService) getDashboardStatRealtime(databaseConn *gorm.DB, startDate, endDate int64) (*systemRes.DashboardStatistResponse, error) {
|
||||||
var totalSaleCount int64
|
|
||||||
saleOrderQuery := databaseConn.Model(&models.SalesOrder{}).
|
|
||||||
Where("created_at >= ? AND created_at <= ? AND is_del = ?", startDate, endDate, 0)
|
|
||||||
saleOrderQuery.Count(&totalSaleCount)
|
|
||||||
|
|
||||||
endTime := time.Unix(endDate, 0)
|
endTime := time.Unix(endDate, 0)
|
||||||
singleDayStart := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, endTime.Location()).Unix()
|
yesterdayTime := endTime.AddDate(0, 0, -1)
|
||||||
singleDayEnd := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 23, 59, 59, 0, endTime.Location()).Unix()
|
|
||||||
|
|
||||||
var totalReceivingCount, totalOutboundCount int64
|
todayStart := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, endTime.Location()).Unix()
|
||||||
statistQuery := databaseConn.Model(&models.Statist{}).
|
todayEnd := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 23, 59, 59, 0, endTime.Location()).Unix()
|
||||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", singleDayStart, singleDayEnd, 0)
|
yesterdayStart := time.Date(yesterdayTime.Year(), yesterdayTime.Month(), yesterdayTime.Day(), 0, 0, 0, 0, yesterdayTime.Location()).Unix()
|
||||||
|
yesterdayEnd := time.Date(yesterdayTime.Year(), yesterdayTime.Month(), yesterdayTime.Day(), 23, 59, 59, 0, yesterdayTime.Location()).Unix()
|
||||||
|
|
||||||
var statistList []models.Statist
|
// 结果结构体
|
||||||
if err := statistQuery.Find(&statistList).Error; err != nil {
|
type salesTodayYesterday struct {
|
||||||
return nil, utils.NewError("查询统计数据失败")
|
TodayCount int64 `gorm:"column:today_count"`
|
||||||
|
YesterdayCount int64 `gorm:"column:yesterday_count"`
|
||||||
}
|
}
|
||||||
|
type receivingTodayYesterday struct {
|
||||||
if len(statistList) == 0 {
|
TodayCount int64 `gorm:"column:today_count"`
|
||||||
return &systemRes.DashboardStatistResponse{
|
YesterdayCount int64 `gorm:"column:yesterday_count"`
|
||||||
TotalReceivingCount: 0,
|
|
||||||
TotalOutboundCount: 0,
|
|
||||||
TotalSaleCount: totalSaleCount,
|
|
||||||
UserStats: []systemRes.UserStatItem{},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
type outboundTodayYesterday struct {
|
||||||
for _, stat := range statistList {
|
TodayCount int64 `gorm:"column:today_count"`
|
||||||
totalReceivingCount += stat.ReceivingNum
|
YesterdayCount int64 `gorm:"column:yesterday_count"`
|
||||||
totalOutboundCount += stat.OutboundNum
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var userStats []systemRes.UserStatItem
|
|
||||||
|
|
||||||
type UserStatGroup struct {
|
type UserStatGroup struct {
|
||||||
CreateBy int64 `gorm:"column:create_by"`
|
CreateBy int64 `gorm:"column:create_by"`
|
||||||
TotalReceiving int64 `gorm:"column:total_receiving"`
|
TotalReceiving int64 `gorm:"column:total_receiving"`
|
||||||
TotalOutbound int64 `gorm:"column:total_outbound"`
|
TotalOutbound int64 `gorm:"column:total_outbound"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var userStatGroups []UserStatGroup
|
var (
|
||||||
databaseConn.Model(&models.Statist{}).
|
wg sync.WaitGroup
|
||||||
Select("create_by, SUM(receiving_num) as total_receiving, SUM(outbound_num) as total_outbound").
|
salesStat salesTodayYesterday
|
||||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", singleDayStart, singleDayEnd, 0).
|
receivingStat receivingTodayYesterday
|
||||||
Group("create_by").
|
outboundStat outboundTodayYesterday
|
||||||
Scan(&userStatGroups)
|
userStatGroups []UserStatGroup
|
||||||
|
productTotal int64
|
||||||
|
inventoryTotal int64
|
||||||
|
statErr error
|
||||||
|
receivingErr error
|
||||||
|
outboundErr error
|
||||||
|
userErr error
|
||||||
|
productErr error
|
||||||
|
inventoryErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
// 1. 销售订单统计(并发)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
statErr = databaseConn.Model(&models.SalesOrder{}).
|
||||||
|
Select(`
|
||||||
|
COALESCE(SUM(CASE WHEN created_at >= ? AND created_at <= ? THEN 1 ELSE 0 END), 0) as today_count,
|
||||||
|
COALESCE(SUM(CASE WHEN created_at >= ? AND created_at <= ? THEN 1 ELSE 0 END), 0) as yesterday_count
|
||||||
|
`, todayStart, todayEnd, yesterdayStart, yesterdayEnd).
|
||||||
|
Where("is_del = ?", 0).
|
||||||
|
Scan(&salesStat).Error
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 2. 入库单统计(并发)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
receivingErr = databaseConn.Model(&models.ReceivingOrder{}).
|
||||||
|
Select(`
|
||||||
|
COALESCE(SUM(CASE WHEN created_at >= ? AND created_at <= ? THEN 1 ELSE 0 END), 0) as today_count,
|
||||||
|
COALESCE(SUM(CASE WHEN created_at >= ? AND created_at <= ? THEN 1 ELSE 0 END), 0) as yesterday_count
|
||||||
|
`, todayStart, todayEnd, yesterdayStart, yesterdayEnd).
|
||||||
|
Where("is_del = ?", 0).
|
||||||
|
Scan(&receivingStat).Error
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 3. 出库单统计(并发)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
outboundErr = databaseConn.Model(&models.OutboundOrder{}).
|
||||||
|
Select(`
|
||||||
|
COALESCE(SUM(CASE WHEN created_at >= ? AND created_at <= ? THEN 1 ELSE 0 END), 0) as today_count,
|
||||||
|
COALESCE(SUM(CASE WHEN created_at >= ? AND created_at <= ? THEN 1 ELSE 0 END), 0) as yesterday_count
|
||||||
|
`, todayStart, todayEnd, yesterdayStart, yesterdayEnd).
|
||||||
|
Where("is_del = ?", 0).
|
||||||
|
Scan(&outboundStat).Error
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 4. 用户个人统计(并发)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
singleDayYYYYMMDD := fmt.Sprintf("%04d%02d%02d", endTime.Year(), endTime.Month(), endTime.Day())
|
||||||
|
var singleDayStatDate int64
|
||||||
|
fmt.Sscanf(singleDayYYYYMMDD, "%d", &singleDayStatDate)
|
||||||
|
userErr = databaseConn.Model(&models.Statist{}).
|
||||||
|
Select("create_by, SUM(receiving_num) as total_receiving, SUM(outbound_num) as total_outbound").
|
||||||
|
Where("stat_date = ? AND is_del = ?", singleDayStatDate, 0).
|
||||||
|
Group("create_by").
|
||||||
|
Scan(&userStatGroups).Error
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 5. 商品总数(并发)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
productErr = databaseConn.Model(&models.Product{}).Where("is_del = ?", 0).Count(&productTotal).Error
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 6. 库存总量(并发)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
inventoryErr = databaseConn.Model(&models.Inventory{}).Where("is_del = ?", 0).Select("COALESCE(SUM(quantity), 0)").Row().Scan(&inventoryTotal)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 等待所有 goroutine 完成
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// 检查关键查询错误(订单/入库/出库为核心统计,出错则返回)
|
||||||
|
if statErr != nil {
|
||||||
|
return nil, statErr
|
||||||
|
}
|
||||||
|
if receivingErr != nil {
|
||||||
|
return nil, receivingErr
|
||||||
|
}
|
||||||
|
if outboundErr != nil {
|
||||||
|
return nil, outboundErr
|
||||||
|
}
|
||||||
|
// 用户统计、商品、库存出错不阻塞,仅打印并置零
|
||||||
|
if userErr != nil {
|
||||||
|
userStatGroups = nil
|
||||||
|
}
|
||||||
|
if productErr != nil {
|
||||||
|
productTotal = 0
|
||||||
|
}
|
||||||
|
if inventoryErr != nil {
|
||||||
|
inventoryTotal = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户统计结果批量查询员工信息
|
||||||
|
var userStats []systemRes.UserStatItem
|
||||||
|
if len(userStatGroups) > 0 {
|
||||||
|
userIDs := make([]int64, len(userStatGroups))
|
||||||
|
for i, group := range userStatGroups {
|
||||||
|
userIDs[i] = group.CreateBy
|
||||||
|
}
|
||||||
|
var employees []models.Employee
|
||||||
|
if err := database.DB.Where("id IN ?", userIDs).Find(&employees).Error; err == nil {
|
||||||
|
empMap := make(map[int64]models.Employee, len(employees))
|
||||||
|
for _, emp := range employees {
|
||||||
|
empMap[emp.ID] = emp
|
||||||
|
}
|
||||||
for _, group := range userStatGroups {
|
for _, group := range userStatGroups {
|
||||||
var employee models.Employee
|
if emp, ok := empMap[group.CreateBy]; ok {
|
||||||
if err := databaseConn.Where("id = ?", group.CreateBy).First(&employee).Error; err == nil {
|
|
||||||
userStats = append(userStats, systemRes.UserStatItem{
|
userStats = append(userStats, systemRes.UserStatItem{
|
||||||
UserID: employee.ID,
|
UserID: emp.ID,
|
||||||
UserName: employee.Username,
|
UserName: emp.Username,
|
||||||
ReceivingCount: group.TotalReceiving,
|
ReceivingCount: group.TotalReceiving,
|
||||||
OutboundCount: group.TotalOutbound,
|
OutboundCount: group.TotalOutbound,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var productTotal int64
|
}
|
||||||
databaseConn.Model(&models.Product{}).Where("is_del = ?", 0).Count(&productTotal)
|
|
||||||
|
|
||||||
var inventoryTotal int64
|
|
||||||
databaseConn.Model(&models.Inventory{}).Where("is_del = ?", 0).Select("COALESCE(SUM(quantity), 0)").Row().Scan(&inventoryTotal)
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
yesterdayStart := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location()).Unix()
|
|
||||||
yesterdayEnd := time.Date(now.Year(), now.Month(), now.Day()-1, 23, 59, 59, 0, now.Location()).Unix()
|
|
||||||
|
|
||||||
var todayInbound, todayOutbound, yesterdayInbound, yesterdayOutbound int64
|
|
||||||
databaseConn.Model(&models.Statist{}).
|
|
||||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", startDate, endDate, 0).
|
|
||||||
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
|
||||||
Row().Scan(&todayInbound, &todayOutbound)
|
|
||||||
|
|
||||||
databaseConn.Model(&models.Statist{}).
|
|
||||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", yesterdayStart, yesterdayEnd, 0).
|
|
||||||
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
|
|
||||||
Row().Scan(&yesterdayInbound, &yesterdayOutbound)
|
|
||||||
|
|
||||||
return &systemRes.DashboardStatistResponse{
|
return &systemRes.DashboardStatistResponse{
|
||||||
TotalReceivingCount: totalReceivingCount,
|
TotalOrderCount: salesStat.TodayCount,
|
||||||
TotalOutboundCount: totalOutboundCount,
|
TotalReceivingCount: receivingStat.TodayCount,
|
||||||
TotalSaleCount: totalSaleCount,
|
TotalOutboundCount: outboundStat.TodayCount,
|
||||||
|
TotalSaleCount: salesStat.TodayCount,
|
||||||
|
YesterdayOrderCount: salesStat.YesterdayCount,
|
||||||
|
YesterdayReceivingCount: receivingStat.YesterdayCount,
|
||||||
|
YesterdayOutboundCount: outboundStat.YesterdayCount,
|
||||||
|
YesterdaySaleCount: salesStat.YesterdayCount,
|
||||||
UserStats: userStats,
|
UserStats: userStats,
|
||||||
ProductTotal: productTotal,
|
ProductTotal: productTotal,
|
||||||
InventoryTotal: inventoryTotal,
|
InventoryTotal: inventoryTotal,
|
||||||
TodayInbound: todayInbound,
|
|
||||||
TodayOutbound: todayOutbound,
|
|
||||||
YesterdayInbound: yesterdayInbound,
|
|
||||||
YesterdayOutbound: yesterdayOutbound,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWarehouseStatist 获取仓库统计数据
|
// GetWarehouseStatist 获取仓库统计数据
|
||||||
func (s *StatistService) GetWarehouseStatist(req systemReq.WarehouseStatistRequest, db ...*gorm.DB) (*systemRes.WarehouseStatistResponse, error) {
|
func (s *StatistService) GetWarehouseStatist(req systemReq.WarehouseStatistRequest, db ...*gorm.DB) (*systemRes.WarehouseStatistResponse, error) {
|
||||||
databaseConn := database.OptionalDB(db...)
|
databaseConn := database.OptionalDB(db...)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
yesterdayTime := now.AddDate(0, 0, -1)
|
||||||
endOfDay := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()).Unix()
|
|
||||||
yesterdayStart := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, now.Location()).Unix()
|
|
||||||
yesterdayEnd := time.Date(now.Year(), now.Month(), now.Day()-1, 23, 59, 59, 0, now.Location()).Unix()
|
|
||||||
|
|
||||||
if req.StartDate == 0 {
|
// statist.stat_date 存储 YYYYMMDD 格式 int64,需转换为 YYYYMMDD 再比较
|
||||||
req.StartDate = startOfDay
|
todayYYYYMMDD := fmt.Sprintf("%04d%02d%02d", now.Year(), now.Month(), now.Day())
|
||||||
}
|
yesterdayYYYYMMDD := fmt.Sprintf("%04d%02d%02d", yesterdayTime.Year(), yesterdayTime.Month(), yesterdayTime.Day())
|
||||||
if req.EndDate == 0 {
|
var todayStatDate, yesterdayStatDate int64
|
||||||
req.EndDate = endOfDay
|
fmt.Sscanf(todayYYYYMMDD, "%d", &todayStatDate)
|
||||||
|
fmt.Sscanf(yesterdayYYYYMMDD, "%d", &yesterdayStatDate)
|
||||||
|
|
||||||
|
// 若用户传了日期范围,从 req.EndDate 反算 YYYYMMDD;否则默认今天
|
||||||
|
var reqEndStatDate int64
|
||||||
|
if req.EndDate != 0 {
|
||||||
|
reqEndTime := time.Unix(req.EndDate, 0)
|
||||||
|
reqEndYYYYMMDD := fmt.Sprintf("%04d%02d%02d", reqEndTime.Year(), reqEndTime.Month(), reqEndTime.Day())
|
||||||
|
fmt.Sscanf(reqEndYYYYMMDD, "%d", &reqEndStatDate)
|
||||||
|
} else {
|
||||||
|
reqEndStatDate = todayStatDate
|
||||||
}
|
}
|
||||||
|
|
||||||
var productTotal int64
|
var productTotal int64
|
||||||
@ -339,14 +385,24 @@ func (s *StatistService) GetWarehouseStatist(req systemReq.WarehouseStatistReque
|
|||||||
YesterdayOutbound int64 `gorm:"column:yesterday_outbound"`
|
YesterdayOutbound int64 `gorm:"column:yesterday_outbound"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算昨天对应的 YYYYMMDD(基于 reqEndDate 或今天)
|
||||||
|
var yesterdayReqStatDate int64
|
||||||
|
if req.EndDate != 0 {
|
||||||
|
yesterdayReqTime := time.Unix(req.EndDate, 0).AddDate(0, 0, -1)
|
||||||
|
yesterdayReqYYYYMMDD := fmt.Sprintf("%04d%02d%02d", yesterdayReqTime.Year(), yesterdayReqTime.Month(), yesterdayReqTime.Day())
|
||||||
|
fmt.Sscanf(yesterdayReqYYYYMMDD, "%d", &yesterdayReqStatDate)
|
||||||
|
} else {
|
||||||
|
yesterdayReqStatDate = yesterdayStatDate
|
||||||
|
}
|
||||||
|
|
||||||
var dailyStat DailyStat
|
var dailyStat DailyStat
|
||||||
databaseConn.Model(&models.Statist{}).
|
databaseConn.Model(&models.Statist{}).
|
||||||
Select(`
|
Select(`
|
||||||
COALESCE(SUM(CASE WHEN stat_date >= ? AND stat_date <= ? THEN receiving_num ELSE 0 END), 0) as today_inbound,
|
COALESCE(SUM(CASE WHEN stat_date = ? THEN receiving_num ELSE 0 END), 0) as today_inbound,
|
||||||
COALESCE(SUM(CASE WHEN stat_date >= ? AND stat_date <= ? THEN outbound_num ELSE 0 END), 0) as today_outbound,
|
COALESCE(SUM(CASE WHEN stat_date = ? THEN outbound_num ELSE 0 END), 0) as today_outbound,
|
||||||
COALESCE(SUM(CASE WHEN stat_date >= ? AND stat_date <= ? THEN receiving_num ELSE 0 END), 0) as yesterday_inbound,
|
COALESCE(SUM(CASE WHEN stat_date = ? THEN receiving_num ELSE 0 END), 0) as yesterday_inbound,
|
||||||
COALESCE(SUM(CASE WHEN stat_date >= ? AND stat_date <= ? THEN outbound_num ELSE 0 END), 0) as yesterday_outbound
|
COALESCE(SUM(CASE WHEN stat_date = ? THEN outbound_num ELSE 0 END), 0) as yesterday_outbound
|
||||||
`, req.StartDate, req.EndDate, req.StartDate, req.EndDate, yesterdayStart, yesterdayEnd, yesterdayStart, yesterdayEnd).
|
`, reqEndStatDate, reqEndStatDate, yesterdayReqStatDate, yesterdayReqStatDate).
|
||||||
Where("is_del = ?", 0).
|
Where("is_del = ?", 0).
|
||||||
Scan(&dailyStat)
|
Scan(&dailyStat)
|
||||||
|
|
||||||
|
|||||||
312
service/store_info.go
Normal file
312
service/store_info.go
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"psi/database"
|
||||||
|
systemReq "psi/models/request"
|
||||||
|
systemRes "psi/models/response"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StoreInfoService struct{}
|
||||||
|
|
||||||
|
// StoreInfo 获取店铺统计数据
|
||||||
|
//
|
||||||
|
// 数据口径与 statist.getDashboardStatRealtime 完全对齐。
|
||||||
|
//
|
||||||
|
// 名称来源:sales/outbound/shipping 直接用 sales_order.sales_person(业务表存的实际店铺名);
|
||||||
|
// receiving 用 shop 表名称(通过 car_shop.shop_id 雪花 ID 匹配 shop.id)。
|
||||||
|
// 店铺类型:用名称去 shop 表匹配 shop_alias_name 获取,匹配不上显示"未知"。
|
||||||
|
//
|
||||||
|
// 时间处理方式与 statist 完全一致,today 范围为 00:00:00 ~ 23:59:59。
|
||||||
|
func (s *StoreInfoService) StoreInfo(req systemReq.StoreInfoRequest, db ...*gorm.DB) ([]systemRes.StoreInfoResponse, error) {
|
||||||
|
databaseConn := database.OptionalDB(db...)
|
||||||
|
|
||||||
|
startTime, endTime := s.calcTimeRange(req.TimeRange)
|
||||||
|
|
||||||
|
log.Printf("[store-info] timeRange=%s startTime=%d endTime=%d", req.TimeRange, startTime, endTime)
|
||||||
|
|
||||||
|
// Step 1: 获取 shop 表 → name→type 映射(仅用于补全 shop_type)
|
||||||
|
type shopRow struct {
|
||||||
|
ShopAliasName string `gorm:"column:shop_alias_name"`
|
||||||
|
ShopType int8 `gorm:"column:shop_type"`
|
||||||
|
ID int64 `gorm:"column:id"`
|
||||||
|
}
|
||||||
|
var shopRows []shopRow
|
||||||
|
if err := databaseConn.Raw(`SELECT id, shop_alias_name, shop_type FROM shop WHERE del_flag = 0`).Scan(&shopRows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nameToType := make(map[string]int8, len(shopRows))
|
||||||
|
idToName := make(map[int64]string, len(shopRows))
|
||||||
|
shopNames := make([]string, 0, len(shopRows))
|
||||||
|
for _, s := range shopRows {
|
||||||
|
nameToType[s.ShopAliasName] = s.ShopType
|
||||||
|
idToName[s.ID] = s.ShopAliasName
|
||||||
|
shopNames = append(shopNames, s.ShopAliasName)
|
||||||
|
}
|
||||||
|
log.Printf("[store-info] shop names in DB: %v", shopNames)
|
||||||
|
|
||||||
|
// 第三方订单,shop 表中无记录,手动补类型
|
||||||
|
nameToType["kw8750193"] = 2 // 孔夫子
|
||||||
|
nameToType["图书电商大全"] = 5 // 闲鱼
|
||||||
|
|
||||||
|
// Step 2: 获取 sales_person_id → sales_person 名称映射(用于 outbound/shipping 的 ID 转名称)
|
||||||
|
type idNameRow struct {
|
||||||
|
ID int64 `gorm:"column:sales_person_id"`
|
||||||
|
Name string `gorm:"column:sales_person"`
|
||||||
|
}
|
||||||
|
var idNames []idNameRow
|
||||||
|
if err := databaseConn.Raw(`
|
||||||
|
SELECT sales_person_id, MAX(sales_person) AS sales_person
|
||||||
|
FROM sales_order WHERE is_del = 0 AND sales_person_id > 0
|
||||||
|
GROUP BY sales_person_id
|
||||||
|
`).Scan(&idNames).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
salesIDToName := make(map[int64]string, len(idNames))
|
||||||
|
for _, r := range idNames {
|
||||||
|
salesIDToName[r.ID] = strings.TrimSpace(r.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: 4 goroutine 并发
|
||||||
|
type storeStat struct {
|
||||||
|
ShopName string
|
||||||
|
SaleCount int64
|
||||||
|
OutboundCount int64
|
||||||
|
ReceivingCount int64
|
||||||
|
ShippingCount int64
|
||||||
|
}
|
||||||
|
statByName := make(map[string]*storeStat)
|
||||||
|
var mu sync.Mutex
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var queryErr error
|
||||||
|
var errOnce sync.Once
|
||||||
|
|
||||||
|
// sales_order:按 sales_person 名称分组
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
type row struct {
|
||||||
|
Name string `gorm:"column:sales_person"`
|
||||||
|
Cnt int64 `gorm:"column:cnt"`
|
||||||
|
}
|
||||||
|
var rows []row
|
||||||
|
err := databaseConn.Raw(`
|
||||||
|
SELECT sales_person, COUNT(*) AS cnt
|
||||||
|
FROM sales_order
|
||||||
|
WHERE is_del = 0 AND created_at >= ? AND created_at <= ? AND sales_person != ''
|
||||||
|
GROUP BY sales_person
|
||||||
|
`, startTime, endTime).Scan(&rows).Error
|
||||||
|
if err != nil {
|
||||||
|
errOnce.Do(func() { queryErr = err })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
for _, r := range rows {
|
||||||
|
n := strings.TrimSpace(r.Name)
|
||||||
|
if n == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if statByName[n] == nil {
|
||||||
|
statByName[n] = &storeStat{ShopName: n}
|
||||||
|
}
|
||||||
|
statByName[n].SaleCount = r.Cnt
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// outbound_order:shop_id → salesIDToName → 名称
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
type row struct {
|
||||||
|
ShopID int64 `gorm:"column:shop_id"`
|
||||||
|
Cnt int64 `gorm:"column:cnt"`
|
||||||
|
}
|
||||||
|
var rows []row
|
||||||
|
err := databaseConn.Raw(`
|
||||||
|
SELECT shop_id, COUNT(*) AS cnt
|
||||||
|
FROM outbound_order
|
||||||
|
WHERE is_del = 0 AND created_at >= ? AND created_at <= ? AND shop_id > 0
|
||||||
|
GROUP BY shop_id
|
||||||
|
`, startTime, endTime).Scan(&rows).Error
|
||||||
|
if err != nil {
|
||||||
|
errOnce.Do(func() { queryErr = err })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
for _, r := range rows {
|
||||||
|
name := salesIDToName[r.ShopID]
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if statByName[name] == nil {
|
||||||
|
statByName[name] = &storeStat{ShopName: name}
|
||||||
|
}
|
||||||
|
statByName[name].OutboundCount = r.Cnt
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// receiving_order:car_shop.shop_id(雪花 ID)→ idToName → 名称
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
type row struct {
|
||||||
|
ShopID int64 `gorm:"column:shop_id"`
|
||||||
|
Cnt int64 `gorm:"column:cnt"`
|
||||||
|
}
|
||||||
|
var rows []row
|
||||||
|
err := databaseConn.Raw(`
|
||||||
|
SELECT COALESCE(cs.shop_id, 0) AS shop_id, COUNT(DISTINCT ro.id) AS cnt
|
||||||
|
FROM receiving_order ro
|
||||||
|
LEFT JOIN wave_task wt ON ro.wave_task_id = wt.id AND wt.is_del = 0
|
||||||
|
LEFT JOIN car_shop cs ON wt.car_id = cs.car_id AND cs.is_del = 0
|
||||||
|
WHERE ro.is_del = 0 AND ro.created_at >= ? AND ro.created_at <= ?
|
||||||
|
GROUP BY COALESCE(cs.shop_id, 0)
|
||||||
|
`, startTime, endTime).Scan(&rows).Error
|
||||||
|
if err != nil {
|
||||||
|
errOnce.Do(func() { queryErr = err })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
for _, r := range rows {
|
||||||
|
if r.ShopID <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := idToName[r.ShopID]
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if statByName[name] == nil {
|
||||||
|
statByName[name] = &storeStat{ShopName: name}
|
||||||
|
}
|
||||||
|
statByName[name].ReceivingCount = r.Cnt
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// shipping_order:shop_id → salesIDToName → 名称
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
type row struct {
|
||||||
|
ShopID int64 `gorm:"column:shop_id"`
|
||||||
|
Cnt int64 `gorm:"column:cnt"`
|
||||||
|
}
|
||||||
|
var rows []row
|
||||||
|
err := databaseConn.Raw(`
|
||||||
|
SELECT shop_id, COUNT(*) AS cnt
|
||||||
|
FROM shipping_order
|
||||||
|
WHERE is_del = 0 AND created_at >= ? AND created_at <= ? AND shop_id > 0
|
||||||
|
GROUP BY shop_id
|
||||||
|
`, startTime, endTime).Scan(&rows).Error
|
||||||
|
if err != nil {
|
||||||
|
errOnce.Do(func() { queryErr = err })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
for _, r := range rows {
|
||||||
|
name := salesIDToName[r.ShopID]
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if statByName[name] == nil {
|
||||||
|
statByName[name] = &storeStat{ShopName: name}
|
||||||
|
}
|
||||||
|
statByName[name].ShippingCount = r.Cnt
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
if queryErr != nil {
|
||||||
|
return nil, queryErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: 组装结果。名称用业务真实名称,类型从 shop 表匹配
|
||||||
|
statNames := make([]string, 0, len(statByName))
|
||||||
|
for n := range statByName {
|
||||||
|
statNames = append(statNames, n)
|
||||||
|
}
|
||||||
|
log.Printf("[store-info] stat names: %v", statNames)
|
||||||
|
shopTypeMap := map[int8]string{
|
||||||
|
1: "拼多多",
|
||||||
|
2: "孔夫子",
|
||||||
|
5: "闲鱼",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]systemRes.StoreInfoResponse, 0, len(statByName))
|
||||||
|
for name, st := range statByName {
|
||||||
|
if req.StoreName != "" && !containsIgnoreCase(name, req.StoreName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
storeType := "未知"
|
||||||
|
if t, ok := nameToType[name]; ok {
|
||||||
|
if s, exists := shopTypeMap[t]; exists {
|
||||||
|
storeType = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, systemRes.StoreInfoResponse{
|
||||||
|
StoreName: name,
|
||||||
|
StoreType: storeType,
|
||||||
|
SaleCount: st.SaleCount,
|
||||||
|
OutboundCount: st.OutboundCount,
|
||||||
|
ReceivingCount: st.ReceivingCount,
|
||||||
|
OrderCount: st.SaleCount,
|
||||||
|
ShippingCount: st.ShippingCount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsIgnoreCase(s, substr string) bool {
|
||||||
|
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcTimeRange 根据 time_range 计算时间范围
|
||||||
|
// 时间处理方式与 statist.getDashboardStatRealtime 完全一致:today 为 00:00:00 ~ 23:59:59
|
||||||
|
func (s *StoreInfoService) calcTimeRange(timeRange string) (int64, int64) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
switch timeRange {
|
||||||
|
case "today":
|
||||||
|
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()).Unix()
|
||||||
|
return start, end
|
||||||
|
case "yesterday":
|
||||||
|
yesterday := now.AddDate(0, 0, -1)
|
||||||
|
start := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 23, 59, 59, 0, now.Location()).Unix()
|
||||||
|
return start, end
|
||||||
|
case "7days":
|
||||||
|
start := time.Date(now.Year(), now.Month(), now.Day()-7, 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := now.Unix()
|
||||||
|
return start, end
|
||||||
|
case "30days":
|
||||||
|
start := time.Date(now.Year(), now.Month(), now.Day()-30, 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := now.Unix()
|
||||||
|
return start, end
|
||||||
|
case "90days":
|
||||||
|
start := time.Date(now.Year(), now.Month(), now.Day()-90, 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := now.Unix()
|
||||||
|
return start, end
|
||||||
|
case "180days":
|
||||||
|
start := time.Date(now.Year(), now.Month(), now.Day()-180, 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := now.Unix()
|
||||||
|
return start, end
|
||||||
|
case "365days":
|
||||||
|
start := time.Date(now.Year(), now.Month(), now.Day()-365, 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := now.Unix()
|
||||||
|
return start, end
|
||||||
|
default:
|
||||||
|
start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
end := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()).Unix()
|
||||||
|
return start, end
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user