提交
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:操作员"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作员ID"`
|
||||
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:更新时间戳(秒)"`
|
||||
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 {
|
||||
|
||||
@ -25,7 +25,7 @@ type Product struct {
|
||||
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:创建时间戳"`
|
||||
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 {
|
||||
|
||||
@ -7,16 +7,16 @@ type ReceivingOrder struct {
|
||||
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"`
|
||||
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"`
|
||||
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)"`
|
||||
Operator string `json:"operator" gorm:"size:100;not null;default:'';comment:操作人"`
|
||||
OperatorID int64 `json:"operator_id" gorm:"not null;default:0;comment:操作人ID"`
|
||||
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:更新时间戳(秒)"`
|
||||
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 {
|
||||
|
||||
@ -12,6 +12,7 @@ type GetOutboundOrderListRequest struct {
|
||||
EndDate int64 `form:"end_date"`
|
||||
AssociationOrderNo string `form:"association_order_no"`
|
||||
LogisticsNo string `form:"logistics_no"`
|
||||
ShopType int `form:"shop_type" json:"shop_type"`
|
||||
}
|
||||
|
||||
// GetOutboundOrderDetailRequest 获取出库单详情请求
|
||||
|
||||
@ -12,6 +12,7 @@ type GetSalesOrderListRequest struct {
|
||||
EndDate int64 `form:"end_date"`
|
||||
AssociationOrderNo string `form:"association_order_no"`
|
||||
LogisticsNo string `form:"logistics_no"`
|
||||
ShopType int `form:"shop_type" json:"shop_type"`
|
||||
}
|
||||
|
||||
// GetSalesOrderDetailRequest 获取销售订单详情请求
|
||||
@ -37,4 +38,5 @@ type GetSalesOrderDetailListRequest struct {
|
||||
EndDate int64 `form:"end_date"`
|
||||
AssociationOrderNo string `form:"association_order_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"`
|
||||
AssociationOrderNo string `form:"association_order_no" json:"association_order_no"`
|
||||
LogisticsNo string `form:"logistics_no" json:"logistics_no"`
|
||||
ShopType int `form:"shop_type" json:"shop_type"`
|
||||
}
|
||||
|
||||
// GetShippingOrderDetailRequest 获取发货单详情请求
|
||||
@ -29,4 +30,5 @@ type GetShippingOrderDetailListRequest struct {
|
||||
EndDate int64 `form:"end_date" json:"end_date"`
|
||||
AssociationOrderNo string `form:"association_order_no" json:"association_order_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"`
|
||||
Status int8 `json:"status"`
|
||||
StatusText string `json:"status_text"`
|
||||
ShopTypeText string `json:"shop_type_text"`
|
||||
SalesPerson string `json:"sales_person"`
|
||||
SalesPersonID int64 `json:"sales_person_id"`
|
||||
AssociationOrderNo string `json:"association_order_no"`
|
||||
@ -147,6 +148,7 @@ func ConvertSalesOrderToDetail(order models.SalesOrder, customerName string, war
|
||||
TotalAmount: order.TotalAmount,
|
||||
Status: order.Status,
|
||||
StatusText: GetSalesOrderStatusText(order.Status),
|
||||
ShopTypeText: GetShopTypeStatusText(order.ShopType),
|
||||
SalesPerson: order.SalesPerson,
|
||||
SalesPersonID: order.SalesPersonID,
|
||||
|
||||
@ -189,7 +191,7 @@ func GetShopTypeStatusText(status int8) string {
|
||||
statusMap := map[int8]string{
|
||||
1: "拼多多",
|
||||
2: "孔夫子",
|
||||
5: "咸鱼",
|
||||
5: "闲鱼",
|
||||
}
|
||||
if text, ok := statusMap[status]; ok {
|
||||
return text
|
||||
|
||||
@ -2,16 +2,17 @@ package response
|
||||
|
||||
// DashboardStatistResponse 仪表盘统计响应
|
||||
type DashboardStatistResponse struct {
|
||||
TotalReceivingCount int64 `json:"total_receiving_count"` // 总入库次数
|
||||
TotalOutboundCount int64 `json:"total_outbound_count"` // 总出库次数
|
||||
TotalSaleCount int64 `json:"total_sale_count"` // 今日销售数
|
||||
UserStats []UserStatItem `json:"user_stats"` // 个人统计数据
|
||||
ProductTotal int64 `json:"product_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"` // 昨日出库
|
||||
TotalOrderCount int64 `json:"total_order_count"` // 今日订单数
|
||||
TotalReceivingCount int64 `json:"total_receiving_count"` // 今日入库数
|
||||
TotalOutboundCount int64 `json:"total_outbound_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"` // 个人统计数据
|
||||
ProductTotal int64 `json:"product_total"` // 商品总量
|
||||
InventoryTotal int64 `json:"inventory_total"` // 库存量
|
||||
}
|
||||
|
||||
// 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:订单总金额(分)"`
|
||||
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:店铺名称"`
|
||||
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:备注"`
|
||||
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:更新时间戳(秒)"`
|
||||
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 {
|
||||
|
||||
@ -5,15 +5,15 @@ type ShippingOrder struct {
|
||||
ID int64 `json:"id" gorm:"primarykey;comment:主键ID"`
|
||||
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"`
|
||||
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=已取消"`
|
||||
ShippingTime *int64 `json:"shipping_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:实际签收时间(时间戳秒)"`
|
||||
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:更新时间(时间戳秒)"`
|
||||
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:备注"`
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
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)").
|
||||
Row().Scan(&totalReceivingNum, &totalOutboundNum)
|
||||
|
||||
var todayInbound, todayOutbound int64
|
||||
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)").
|
||||
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
|
||||
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)").
|
||||
Row().Scan(&yesterdayInbound, &yesterdayOutbound)
|
||||
|
||||
@ -157,7 +168,7 @@ func (s *StatistTaskService) generateDashboardDailyStat(tx *gorm.DB, statDate in
|
||||
|
||||
var yesterdaySalesCount int64
|
||||
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)
|
||||
|
||||
var productTotal int64
|
||||
@ -231,7 +242,7 @@ func (s *StatistTaskService) generateUserDailyStat(tx *gorm.DB, mainDB *gorm.DB,
|
||||
}
|
||||
var receivingStats []UserReceivingStat
|
||||
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").
|
||||
Group("create_by").
|
||||
Find(&receivingStats)
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
systemRes "psi/models/response"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
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+"%")
|
||||
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
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
@ -86,50 +93,85 @@ func (s *OutboundService) GetOutboundOrderList(req systemReq.GetOutboundOrderLis
|
||||
return nil, utils.NewError("查询出库单列表失败")
|
||||
}
|
||||
|
||||
// 收集订单ID用于批量查询
|
||||
orderIDs := make([]int64, len(orders))
|
||||
for i, order := range orders {
|
||||
orderIDs[i] = order.ID
|
||||
}
|
||||
|
||||
// 批量查询店铺信息:按 out_order_id 分组
|
||||
type shopRow struct {
|
||||
OutOrderID int64 `gorm:"column:out_order_id"`
|
||||
ShopName string `gorm:"column:shop_name"`
|
||||
ShopType int8 `gorm:"column:shop_type"`
|
||||
}
|
||||
var shopRows []shopRow
|
||||
databaseConn.Table("outbound_order_item").
|
||||
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").
|
||||
Where("outbound_order_item.out_order_id IN ? AND outbound_order_item.is_del = ?", orderIDs, 0).
|
||||
Scan(&shopRows)
|
||||
|
||||
shopsByOrderID := make(map[int64][]systemRes.OutboundShopInfo, len(orderIDs))
|
||||
for _, row := range shopRows {
|
||||
shopsByOrderID[row.OutOrderID] = append(shopsByOrderID[row.OutOrderID], systemRes.OutboundShopInfo{
|
||||
ShopName: row.ShopName,
|
||||
ShopType: row.ShopType,
|
||||
ShopTypeText: systemRes.GetShopTypeText(row.ShopType),
|
||||
})
|
||||
}
|
||||
|
||||
// 批量查询关联订单号:按 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").
|
||||
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").
|
||||
Where("outbound_order_item.out_order_id IN ? AND outbound_order_item.is_del = ? AND so.association_order_no != ''", orderIDs, 0).
|
||||
Group("outbound_order_item.out_order_id").
|
||||
Scan(&assocRows)
|
||||
|
||||
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").
|
||||
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").
|
||||
Where("outbound_order_item.out_order_id IN ? AND outbound_order_item.is_del = ? AND soi.logistics_no != ''", orderIDs, 0).
|
||||
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 {
|
||||
var shopList []systemRes.OutboundShopInfo
|
||||
|
||||
var shops []struct {
|
||||
ShopName string `gorm:"column:shop_name"`
|
||||
ShopType int8 `gorm:"column:shop_type"`
|
||||
shopList := shopsByOrderID[order.ID]
|
||||
if shopList == nil {
|
||||
shopList = []systemRes.OutboundShopInfo{}
|
||||
}
|
||||
|
||||
databaseConn.Table("outbound_order_item").
|
||||
Select("DISTINCT 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").
|
||||
Where("outbound_order_item.out_order_id = ? AND outbound_order_item.is_del = ?", order.ID, 0).
|
||||
Scan(&shops)
|
||||
|
||||
for _, shop := range shops {
|
||||
shopList = append(shopList, systemRes.OutboundShopInfo{
|
||||
ShopName: shop.ShopName,
|
||||
ShopType: shop.ShopType,
|
||||
ShopTypeText: systemRes.GetShopTypeText(shop.ShopType),
|
||||
})
|
||||
}
|
||||
|
||||
var associationOrderNos string
|
||||
databaseConn.Table("outbound_order_item").
|
||||
Select("GROUP_CONCAT(DISTINCT so.association_order_no SEPARATOR ', ')").
|
||||
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).
|
||||
Scan(&associationOrderNos)
|
||||
|
||||
var logisticsNos string
|
||||
databaseConn.Table("outbound_order_item").
|
||||
Select("GROUP_CONCAT(DISTINCT soi.logistics_no SEPARATOR ', ')").
|
||||
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).
|
||||
Scan(&logisticsNos)
|
||||
|
||||
orderItems = append(orderItems, systemRes.ConvertOutboundOrderToItem(
|
||||
order.OutboundOrder,
|
||||
order.CustomerName,
|
||||
order.WarehouseName,
|
||||
shopList,
|
||||
associationOrderNos,
|
||||
logisticsNos,
|
||||
assocByOrderID[order.ID],
|
||||
logByOrderID[order.ID],
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@ -333,6 +333,15 @@ func (s *ProcessService) BindWave(req systemReq.BindWaveRequest, db ...*gorm.DB)
|
||||
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()
|
||||
|
||||
receivingOrder := models.ReceivingOrder{
|
||||
@ -340,6 +349,7 @@ func (s *ProcessService) BindWave(req systemReq.BindWaveRequest, db ...*gorm.DB)
|
||||
PurchaseOrderID: purchaseOrder.ID, //采购订单ID
|
||||
WaveTaskID: waveTaskID, //入库任务ID
|
||||
WarehouseID: purchaseOrder.WarehouseID, //仓库ID
|
||||
ShopId: shopId, //店铺ID(来自小车)
|
||||
SupplierID: purchaseOrder.SupplierID, //供应商ID
|
||||
ReceivingDate: now, //入库日期时间戳(秒)
|
||||
Status: constant.ReceivingStatusPending, //状态(1:待收货/pending, )
|
||||
@ -1623,6 +1633,7 @@ func (s *ProcessService) CreateOutboundOrder(req systemReq.CreateOutboundOrderRe
|
||||
WaveTaskID: 0,
|
||||
WarehouseID: warehouseID,
|
||||
CustomerID: customerID,
|
||||
ShopId: salesOrders[0].SalesPersonID,
|
||||
TotalQuantity: 0,
|
||||
TotalAmount: 0,
|
||||
Status: constant.OutboundStatusCreated,
|
||||
@ -2127,11 +2138,29 @@ func (s *ProcessService) CreateShippingOrder(req systemReq.CreateShippingOrderRe
|
||||
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()
|
||||
|
||||
shippingOrder := models.ShippingOrder{
|
||||
ShippingNo: shippingNo,
|
||||
CustomerID: customerID,
|
||||
ShopId: shopId,
|
||||
Status: constant.ShippingStatusPending,
|
||||
ExpectedArriveTime: req.ExpectedArriveTime,
|
||||
Operator: operator,
|
||||
|
||||
@ -57,6 +57,9 @@ func (s *SalesService) GetSalesOrderList(req systemReq.GetSalesOrderListRequest,
|
||||
Where("logistics_no LIKE ? AND is_del = 0", "%"+req.LogisticsNo+"%")
|
||||
query = query.Where("sales_order.id IN (?)", subQuery)
|
||||
}
|
||||
if req.ShopType > 0 {
|
||||
query = query.Where("sales_order.shop_type = ?", req.ShopType)
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
@ -84,19 +87,35 @@ func (s *SalesService) GetSalesOrderList(req systemReq.GetSalesOrderListRequest,
|
||||
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))
|
||||
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(
|
||||
order.SalesOrder,
|
||||
order.CustomerName,
|
||||
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+"%")
|
||||
query = query.Where("sales_order.id IN (?)", subQuery)
|
||||
}
|
||||
if req.ShopType > 0 {
|
||||
query = query.Where("sales_order.shop_type = ?", req.ShopType)
|
||||
}
|
||||
|
||||
var total int64
|
||||
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+"%")
|
||||
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
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
@ -83,52 +91,87 @@ func (s *ShippingService) GetShippingOrderList(req systemReq.GetShippingOrderLis
|
||||
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 {
|
||||
var shopList []systemRes.OutboundShopInfo
|
||||
|
||||
var shops []struct {
|
||||
ShopName string `gorm:"column:shop_name"`
|
||||
ShopType int8 `gorm:"column:shop_type"`
|
||||
shopList := shopsByOrderID[order.ID]
|
||||
if shopList == nil {
|
||||
shopList = []systemRes.OutboundShopInfo{}
|
||||
}
|
||||
|
||||
databaseConn.Table("shipping_order_item").
|
||||
Select("DISTINCT 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 = ? AND shipping_order_item.is_del = ?", order.ID, 0).
|
||||
Scan(&shops)
|
||||
|
||||
for _, shop := range shops {
|
||||
shopList = append(shopList, systemRes.OutboundShopInfo{
|
||||
ShopName: shop.ShopName,
|
||||
ShopType: shop.ShopType,
|
||||
ShopTypeText: systemRes.GetShopTypeText(shop.ShopType),
|
||||
})
|
||||
}
|
||||
|
||||
var associationOrderNos string
|
||||
databaseConn.Table("shipping_order_item").
|
||||
Select("GROUP_CONCAT(DISTINCT so.association_order_no SEPARATOR ', ')").
|
||||
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 = ? AND shipping_order_item.is_del = ? AND so.association_order_no != ''", order.ID, 0).
|
||||
Scan(&associationOrderNos)
|
||||
|
||||
var logisticsNos string
|
||||
databaseConn.Table("shipping_order_item").
|
||||
Select("GROUP_CONCAT(DISTINCT soi.logistics_no SEPARATOR ', ')").
|
||||
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 = ? AND shipping_order_item.is_del = ? AND soi.logistics_no != ''", order.ID, 0).
|
||||
Scan(&logisticsNos)
|
||||
|
||||
orderItems = append(orderItems, systemRes.ConvertShippingOrderToItem(
|
||||
order.ShippingOrder,
|
||||
order.CustomerName,
|
||||
shopList,
|
||||
associationOrderNos,
|
||||
logisticsNos,
|
||||
assocByOrderID[order.ID],
|
||||
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+"%")
|
||||
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
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"psi/models"
|
||||
systemReq "psi/models/request"
|
||||
systemRes "psi/models/response"
|
||||
"psi/utils"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -152,6 +152,7 @@ type StatistService struct{}
|
||||
}, nil
|
||||
}*/
|
||||
// DashboardStatist 获取仪表盘统计数据
|
||||
// 统一口径:所有统计字段改为实时查原始单据表,不再依赖 dashboard_daily_stat 预计算表
|
||||
func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest, db ...*gorm.DB) (*systemRes.DashboardStatistResponse, error) {
|
||||
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()
|
||||
endOfDay := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()).Unix()
|
||||
|
||||
hasExplicitDate := req.EndDate != 0
|
||||
|
||||
if req.StartDate == 0 {
|
||||
req.StartDate = startOfDay
|
||||
}
|
||||
@ -168,162 +167,209 @@ func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest,
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
return s.getDashboardStatRealtime(databaseConn, req.StartDate, req.EndDate)
|
||||
}
|
||||
|
||||
// getDashboardStatRealtime 实时查询仪表盘统计数据(降级方案)
|
||||
// getDashboardStatRealtime 实时查询仪表盘统计数据
|
||||
// 统一口径:所有统计字段直接查原始单据表(sales_order / receiving_order / outbound_order)
|
||||
// 所有独立查询通过 goroutine 并发执行,总耗时 = max(各查询耗时)
|
||||
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)
|
||||
singleDayStart := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, endTime.Location()).Unix()
|
||||
singleDayEnd := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 23, 59, 59, 0, endTime.Location()).Unix()
|
||||
yesterdayTime := endTime.AddDate(0, 0, -1)
|
||||
|
||||
var totalReceivingCount, totalOutboundCount int64
|
||||
statistQuery := databaseConn.Model(&models.Statist{}).
|
||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", singleDayStart, singleDayEnd, 0)
|
||||
todayStart := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, endTime.Location()).Unix()
|
||||
todayEnd := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 23, 59, 59, 0, endTime.Location()).Unix()
|
||||
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 {
|
||||
return nil, utils.NewError("查询统计数据失败")
|
||||
// 结果结构体
|
||||
type salesTodayYesterday struct {
|
||||
TodayCount int64 `gorm:"column:today_count"`
|
||||
YesterdayCount int64 `gorm:"column:yesterday_count"`
|
||||
}
|
||||
|
||||
if len(statistList) == 0 {
|
||||
return &systemRes.DashboardStatistResponse{
|
||||
TotalReceivingCount: 0,
|
||||
TotalOutboundCount: 0,
|
||||
TotalSaleCount: totalSaleCount,
|
||||
UserStats: []systemRes.UserStatItem{},
|
||||
}, nil
|
||||
type receivingTodayYesterday struct {
|
||||
TodayCount int64 `gorm:"column:today_count"`
|
||||
YesterdayCount int64 `gorm:"column:yesterday_count"`
|
||||
}
|
||||
|
||||
for _, stat := range statistList {
|
||||
totalReceivingCount += stat.ReceivingNum
|
||||
totalOutboundCount += stat.OutboundNum
|
||||
type outboundTodayYesterday struct {
|
||||
TodayCount int64 `gorm:"column:today_count"`
|
||||
YesterdayCount int64 `gorm:"column:yesterday_count"`
|
||||
}
|
||||
|
||||
var userStats []systemRes.UserStatItem
|
||||
|
||||
type UserStatGroup struct {
|
||||
CreateBy int64 `gorm:"column:create_by"`
|
||||
TotalReceiving int64 `gorm:"column:total_receiving"`
|
||||
TotalOutbound int64 `gorm:"column:total_outbound"`
|
||||
}
|
||||
|
||||
var userStatGroups []UserStatGroup
|
||||
databaseConn.Model(&models.Statist{}).
|
||||
Select("create_by, SUM(receiving_num) as total_receiving, SUM(outbound_num) as total_outbound").
|
||||
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", singleDayStart, singleDayEnd, 0).
|
||||
Group("create_by").
|
||||
Scan(&userStatGroups)
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
salesStat salesTodayYesterday
|
||||
receivingStat receivingTodayYesterday
|
||||
outboundStat outboundTodayYesterday
|
||||
userStatGroups []UserStatGroup
|
||||
productTotal int64
|
||||
inventoryTotal int64
|
||||
statErr error
|
||||
receivingErr error
|
||||
outboundErr error
|
||||
userErr error
|
||||
productErr error
|
||||
inventoryErr error
|
||||
)
|
||||
|
||||
for _, group := range userStatGroups {
|
||||
var employee models.Employee
|
||||
if err := databaseConn.Where("id = ?", group.CreateBy).First(&employee).Error; err == nil {
|
||||
userStats = append(userStats, systemRes.UserStatItem{
|
||||
UserID: employee.ID,
|
||||
UserName: employee.Username,
|
||||
ReceivingCount: group.TotalReceiving,
|
||||
OutboundCount: group.TotalOutbound,
|
||||
})
|
||||
// 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 {
|
||||
if emp, ok := empMap[group.CreateBy]; ok {
|
||||
userStats = append(userStats, systemRes.UserStatItem{
|
||||
UserID: emp.ID,
|
||||
UserName: emp.Username,
|
||||
ReceivingCount: group.TotalReceiving,
|
||||
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{
|
||||
TotalReceivingCount: totalReceivingCount,
|
||||
TotalOutboundCount: totalOutboundCount,
|
||||
TotalSaleCount: totalSaleCount,
|
||||
UserStats: userStats,
|
||||
ProductTotal: productTotal,
|
||||
InventoryTotal: inventoryTotal,
|
||||
TodayInbound: todayInbound,
|
||||
TodayOutbound: todayOutbound,
|
||||
YesterdayInbound: yesterdayInbound,
|
||||
YesterdayOutbound: yesterdayOutbound,
|
||||
TotalOrderCount: salesStat.TodayCount,
|
||||
TotalReceivingCount: receivingStat.TodayCount,
|
||||
TotalOutboundCount: outboundStat.TodayCount,
|
||||
TotalSaleCount: salesStat.TodayCount,
|
||||
YesterdayOrderCount: salesStat.YesterdayCount,
|
||||
YesterdayReceivingCount: receivingStat.YesterdayCount,
|
||||
YesterdayOutboundCount: outboundStat.YesterdayCount,
|
||||
YesterdaySaleCount: salesStat.YesterdayCount,
|
||||
UserStats: userStats,
|
||||
ProductTotal: productTotal,
|
||||
InventoryTotal: inventoryTotal,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWarehouseStatist 获取仓库统计数据
|
||||
func (s *StatistService) GetWarehouseStatist(req systemReq.WarehouseStatistRequest, db ...*gorm.DB) (*systemRes.WarehouseStatistResponse, error) {
|
||||
databaseConn := database.OptionalDB(db...)
|
||||
|
||||
now := time.Now()
|
||||
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()
|
||||
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()
|
||||
yesterdayTime := now.AddDate(0, 0, -1)
|
||||
|
||||
if req.StartDate == 0 {
|
||||
req.StartDate = startOfDay
|
||||
}
|
||||
if req.EndDate == 0 {
|
||||
req.EndDate = endOfDay
|
||||
// statist.stat_date 存储 YYYYMMDD 格式 int64,需转换为 YYYYMMDD 再比较
|
||||
todayYYYYMMDD := fmt.Sprintf("%04d%02d%02d", now.Year(), now.Month(), now.Day())
|
||||
yesterdayYYYYMMDD := fmt.Sprintf("%04d%02d%02d", yesterdayTime.Year(), yesterdayTime.Month(), yesterdayTime.Day())
|
||||
var todayStatDate, yesterdayStatDate int64
|
||||
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
|
||||
@ -339,14 +385,24 @@ func (s *StatistService) GetWarehouseStatist(req systemReq.WarehouseStatistReque
|
||||
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
|
||||
databaseConn.Model(&models.Statist{}).
|
||||
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 >= ? AND 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 >= ? AND stat_date <= ? THEN outbound_num ELSE 0 END), 0) as yesterday_outbound
|
||||
`, req.StartDate, req.EndDate, req.StartDate, req.EndDate, yesterdayStart, yesterdayEnd, yesterdayStart, yesterdayEnd).
|
||||
COALESCE(SUM(CASE WHEN stat_date = ? THEN receiving_num ELSE 0 END), 0) as today_inbound,
|
||||
COALESCE(SUM(CASE WHEN stat_date = ? THEN outbound_num ELSE 0 END), 0) as today_outbound,
|
||||
COALESCE(SUM(CASE WHEN stat_date = ? THEN receiving_num ELSE 0 END), 0) as yesterday_inbound,
|
||||
COALESCE(SUM(CASE WHEN stat_date = ? THEN outbound_num ELSE 0 END), 0) as yesterday_outbound
|
||||
`, reqEndStatDate, reqEndStatDate, yesterdayReqStatDate, yesterdayReqStatDate).
|
||||
Where("is_del = ?", 0).
|
||||
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