/api/logistics/list-c 新增这个接口,获取运费

This commit is contained in:
Administrator 2026-06-25 17:39:17 +08:00
parent ca5a652334
commit cd360c8866
12 changed files with 635 additions and 13 deletions

View File

@ -16,6 +16,7 @@ type LogisticsApi struct{}
var logisticsService = service.LogisticsService{} var logisticsService = service.LogisticsService{}
// GetLogisticsList 获取物流模板列表
// GetLogisticsList 获取物流模板列表 // GetLogisticsList 获取物流模板列表
func (r *LogisticsApi) GetLogisticsList(c *gin.Context) { func (r *LogisticsApi) GetLogisticsList(c *gin.Context) {
var req request.QueryLogisticsRequest var req request.QueryLogisticsRequest
@ -24,6 +25,10 @@ func (r *LogisticsApi) GetLogisticsList(c *gin.Context) {
return return
} }
// 从JWT中获取当前用户的about_id
userInfo := utils.GetUserInfo(c)
req.AboutID = userInfo.AboutID
list, total, err := logisticsService.GetLogisticsList(req, database.GetDB(c)) list, total, err := logisticsService.GetLogisticsList(req, database.GetDB(c))
if err != nil { if err != nil {
utils.FailWithRequestLog(constant.LoggerChannelWork, "查询物流模板列表异常", err, c, req) utils.FailWithRequestLog(constant.LoggerChannelWork, "查询物流模板列表异常", err, c, req)

View File

@ -275,6 +275,21 @@ func Init() {
log.Fatal("Config表迁移失败:", err) log.Fatal("Config表迁移失败:", err)
} }
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统计表'").AutoMigrate(&models.Statist{})
if err != nil {
log.Fatal("Statist表迁移失败:", err)
}
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仪表盘每日统计表'").AutoMigrate(&models.DashboardDailyStat{})
if err != nil {
log.Fatal("DashboardDailyStat表迁移失败:", err)
}
err = DB.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户每日统计表'").AutoMigrate(&models.UserDailyStat{})
if err != nil {
log.Fatal("UserDailyStat表迁移失败:", err)
}
// 初始化商品表 // 初始化商品表
InitProductBookTables() InitProductBookTables()
// 初始化默认管理员账号 // 初始化默认管理员账号

View File

@ -5,6 +5,7 @@ import (
"psi/config" "psi/config"
"psi/database" "psi/database"
router "psi/routes" router "psi/routes"
"psi/service"
"psi/utils" "psi/utils"
) )
@ -37,6 +38,10 @@ func main() {
// ocr.StartService() // ocr.StartService()
//}() //}()
// 启动统计任务调度器
scheduler := service.NewStatistTaskScheduler()
go scheduler.Start()
log.Println("统计任务调度器已启动")
// 设置路由并启动服务器 // 设置路由并启动服务器
log.Printf("[4/4] 启动服务器,端口: %s...", config.AppConfig.Server.Port) log.Printf("[4/4] 启动服务器,端口: %s...", config.AppConfig.Server.Port)
router.Run() router.Run()

View File

@ -0,0 +1,29 @@
package models
// DashboardDailyStat 仪表盘每日统计表
type DashboardDailyStat struct {
ID int64 `json:"id" gorm:"primarykey;comment:ID"`
StatDate int64 `json:"stat_date" gorm:"type:bigint;not null;default:0;comment:统计日期(YYYYMMDD格式)"`
TotalReceivingNum int64 `json:"total_receiving_num" gorm:"not null;default:0;comment:总入库次数"`
TotalOutboundNum int64 `json:"total_outbound_num" gorm:"not null;default:0;comment:总出库次数"`
TotalSalesCount int64 `json:"total_sales_count" gorm:"not null;default:0;comment:总销售订单数"`
ProductTotal int64 `json:"product_total" gorm:"not null;default:0;comment:商品总量"`
InventoryTotal int64 `json:"inventory_total" gorm:"not null;default:0;comment:库存总量"`
TodayInbound int64 `json:"today_inbound" gorm:"not null;default:0;comment:今日入库次数"`
TodayOutbound int64 `json:"today_outbound" gorm:"not null;default:0;comment:今日出库次数"`
YesterdayInbound int64 `json:"yesterday_inbound" gorm:"not null;default:0;comment:昨日入库次数"`
YesterdayOutbound int64 `json:"yesterday_outbound" gorm:"not null;default:0;comment:昨日出库次数"`
TodaySalesCount int64 `json:"today_sales_count" gorm:"not null;default:0;comment:今日销售订单数"`
YesterdaySalesCount int64 `json:"yesterday_sales_count" gorm:"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:更新时间戳(秒)"`
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
}
func (DashboardDailyStat) TableName() string {
return "dashboard_daily_stat"
}
func (DashboardDailyStat) TableOptions() string {
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仪表盘每日统计表'"
}

23
models/UserDailyStat.go Normal file
View File

@ -0,0 +1,23 @@
package models
// UserDailyStat 用户每日统计表
type UserDailyStat struct {
ID int64 `json:"id" gorm:"primarykey;comment:ID"`
UserID int64 `json:"user_id" gorm:"not null;default:0;comment:用户ID"`
UserName string `json:"user_name" gorm:"type:varchar(50);not null;default:'';comment:用户姓名"`
StatDate int64 `json:"stat_date" gorm:"type:bigint;not null;default:0;comment:统计日期(YYYYMMDD格式)"`
ReceivingNum int64 `json:"receiving_num" gorm:"not null;default:0;comment:入库次数"`
OutboundNum int64 `json:"outbound_num" gorm:"not null;default:0;comment:出库次数"`
SalesCount int64 `json:"sales_count" gorm:"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:更新时间戳(秒)"`
IsDel int8 `json:"is_del" gorm:"type:tinyint(1);not null;default:0;comment:逻辑删除标记(0:未删除,1:已删除)"`
}
func (UserDailyStat) TableName() string {
return "user_daily_stat"
}
func (UserDailyStat) TableOptions() string {
return "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户每日统计表'"
}

View File

@ -1,10 +1,11 @@
package request package request
type QueryLogisticsRequest struct { type QueryLogisticsRequest struct {
Keyword string `form:"keyword"` Keyword string `form:"keyword"` // 物流模板名称
Status *int `form:"status"` Status *int `form:"status"` // 物流模板状态
Page int `form:"page,default=1"` Page int `form:"page,default=1"` // 页码
PageSize int `form:"page_size,default=10"` PageSize int `form:"page_size,default=10"` // 每页数量
AboutID int64 `form:"-"` // 租户ID从JWT中获取不参与表单绑定
} }
type CreateLogisticsRequest struct { type CreateLogisticsRequest struct {

View File

@ -6,6 +6,12 @@ type DashboardStatistResponse struct {
TotalOutboundCount int64 `json:"total_outbound_count"` // 总出库次数 TotalOutboundCount int64 `json:"total_outbound_count"` // 总出库次数
TotalSaleCount int64 `json:"total_sale_count"` // 今日商品总数 TotalSaleCount int64 `json:"total_sale_count"` // 今日商品总数
UserStats []UserStatItem `json:"user_stats"` // 个人统计数据 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"` // 昨日出库
} }
// UserStatItem 个人统计项 // UserStatItem 个人统计项

View File

@ -91,11 +91,13 @@ func initRouter() (r *gin.Engine) {
public.GET("/split-account-deduction-log/list", splitAccountDeductionLogApi.GetSplitAccountDeductionLogList) // 获取分账扣钱日志列表 public.GET("/split-account-deduction-log/list", splitAccountDeductionLogApi.GetSplitAccountDeductionLogList) // 获取分账扣钱日志列表
public.GET("/split-account-deduction-log/detail/:id", splitAccountDeductionLogApi.GetSplitAccountDeductionLogDetail) // 获取分账扣钱日志详情 public.GET("/split-account-deduction-log/detail/:id", splitAccountDeductionLogApi.GetSplitAccountDeductionLogDetail) // 获取分账扣钱日志详情
public.GET("/open/split-account-deduction-log/list", splitAccountDeductionLogApi.GetOpenSplitAccountDeductionLogList) public.GET("/open/split-account-deduction-log/list", splitAccountDeductionLogApi.GetOpenSplitAccountDeductionLogList)
public.POST("/split-account-deduction-log/update", splitAccountDeductionLogApi.UpdateSplitAccountDeductionLog) // 更新分账扣钱日志 public.POST("/split-account-deduction-log/update", splitAccountDeductionLogApi.UpdateSplitAccountDeductionLog) // 更新分账扣钱日志
public.POST("/sales-order/unlock-inventory", processApi.UnlockSalesOrderInventory) // 解锁销售订单库存 public.POST("/sales-order/unlock-inventory", processApi.UnlockSalesOrderInventory) // 解锁销售订单库存
/* public.POST("/split-account-deduction-log/create", splitAccountDeductionLogApi.CreateSplitAccountDeductionLog) // 创建分账 public.POST("/split-account-deduction-log/create", splitAccountDeductionLogApi.CreateSplitAccountDeductionLog) // 创建分账
public.PUT("/split-account-deduction-log/update", splitAccountDeductionLogApi.UpdateSplitAccountDeductionLog) // 更新分账 public.PUT("/split-account-deduction-log/update", splitAccountDeductionLogApi.UpdateSplitAccountDeductionLog) // 更新分账
public.DELETE("/split-account-deduction-log/delete", splitAccountDeductionLogApi.DeleteSplitAccountDeductionLog) // 删除分账*/ public.DELETE("/split-account-deduction-log/delete", splitAccountDeductionLogApi.DeleteSplitAccountDeductionLog) // 删除分账
public.GET("/logistics/list-c", logisticsApi.GetLogisticsList) // 获取物流模板列表
} }
@ -269,9 +271,9 @@ func initRouter() (r *gin.Engine) {
auth.GET("/split-account-deduction-log/detail/:id", splitAccountDeductionLogApi.GetSplitAccountDeductionLogDetail) // 获取分账扣钱日志详情 auth.GET("/split-account-deduction-log/detail/:id", splitAccountDeductionLogApi.GetSplitAccountDeductionLogDetail) // 获取分账扣钱日志详情
//public.GET("/open/split-account-deduction-log/list", splitAccountDeductionLogApi.GetOpenSplitAccountDeductionLogList) // 公开获取分账扣钱日志列表(无需签名认证)*/ //public.GET("/open/split-account-deduction-log/list", splitAccountDeductionLogApi.GetOpenSplitAccountDeductionLogList) // 公开获取分账扣钱日志列表(无需签名认证)*/
auth.POST("/split-account-deduction-log/create", splitAccountDeductionLogApi.CreateSplitAccountDeductionLog) // 创建分账 //auth.POST("/split-account-deduction-log/create", splitAccountDeductionLogApi.CreateSplitAccountDeductionLog) // 创建分账
//auth.PUT("/split-account-deduction-log/update", splitAccountDeductionLogApi.UpdateSplitAccountDeductionLog) // 更新分账 //auth.PUT("/split-account-deduction-log/update", splitAccountDeductionLogApi.UpdateSplitAccountDeductionLog) // 更新分账
auth.DELETE("/split-account-deduction-log/delete", splitAccountDeductionLogApi.DeleteSplitAccountDeductionLog) // 删除分账*/ //auth.DELETE("/split-account-deduction-log/delete", splitAccountDeductionLogApi.DeleteSplitAccountDeductionLog) // 删除分账*/
// 产品日志管理 // 产品日志管理
auth.GET("/product_log/list", productApi.GetProductLogList) // 获取产品日志列表 auth.GET("/product_log/list", productApi.GetProductLogList) // 获取产品日志列表
auth.POST("/product_log/save", productApi.SaveProductLog) // 保存产品日志 auth.POST("/product_log/save", productApi.SaveProductLog) // 保存产品日志

View File

@ -0,0 +1,55 @@
package service
import (
"log"
"time"
)
// StatistTaskScheduler 统计任务调度器
type StatistTaskScheduler struct {
taskService *StatistTaskService
}
// NewStatistTaskScheduler 创建统计任务调度器
func NewStatistTaskScheduler() *StatistTaskScheduler {
return &StatistTaskScheduler{
taskService: &StatistTaskService{},
}
}
// Start 启动定时任务调度器
func (s *StatistTaskScheduler) Start() {
log.Println("统计任务调度器启动")
// 立即执行一次生成昨天的统计数据
go func() {
if err := s.taskService.GenerateDailyStat(); err != nil {
log.Printf("生成每日统计失败: %v", err)
}
}()
// 每天凌晨2点执行生成前一天的统计数据
ticker := time.NewTicker(1 * time.Hour) // 每小时检查一次是否到了凌晨2点
defer ticker.Stop()
var lastRunDate string
for range ticker.C {
now := time.Now()
currentDateStr := now.Format("2006-01-02")
hour := now.Hour()
// 如果是凌晨2点且今天还没运行过
if hour == 2 && currentDateStr != lastRunDate {
go func(date string) {
log.Printf("开始执行每日统计任务,日期: %s", date)
if err := s.taskService.GenerateDailyStat(); err != nil {
log.Printf("生成每日统计失败: %v", err)
} else {
log.Printf("每日统计任务执行成功,日期: %s", date)
}
}(currentDateStr)
lastRunDate = currentDateStr
}
}
}

View File

@ -0,0 +1,259 @@
package service
import (
"fmt"
"gorm.io/gorm"
"psi/database"
"psi/models"
"psi/utils"
"time"
)
type StatistTaskService struct{}
// GenerateDailyStat 生成每日统计数据(定时任务调用)
// GenerateDailyStat 生成每日统计数据(定时任务调用)
// GenerateDailyStat 生成每日统计数据(定时任务调用)
func (s *StatistTaskService) GenerateDailyStat(db ...*gorm.DB) error {
now := time.Now()
yesterday := now.AddDate(0, 0, -1)
statDateStr := yesterday.Format("20060102")
var statDate int64
if _, err := fmt.Sscanf(statDateStr, "%d", &statDate); err != nil {
return fmt.Errorf("日期格式化失败: %v", err)
}
yesterdayStart := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, yesterday.Location()).Unix()
yesterdayEnd := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 23, 59, 59, 0, yesterday.Location()).Unix()
mainDB := database.DB
if len(db) > 0 && db[0] != nil {
mainDB = db[0]
}
var aboutIDs []int64
if err := mainDB.Model(&models.Employee{}).Where("deleted_at = ?", 0).Distinct().Pluck("about_id", &aboutIDs).Error; err != nil {
return fmt.Errorf("查询租户列表失败: %v", err)
}
if len(aboutIDs) == 0 {
utils.InfoLog("work", map[string]interface{}{
"source": "定时任务-生成每日统计",
"stat_date": statDate,
"message": "没有找到任何租户",
})
return nil
}
for _, aboutID := range aboutIDs {
if aboutID == 0 {
continue
}
tenantDB, err := database.GetTenantDB(aboutID)
if err != nil {
utils.ErrorLog("work", map[string]interface{}{
"source": "定时任务-生成每日统计",
"about_id": aboutID,
"error": fmt.Sprintf("获取租户数据库失败: %v", err),
})
continue
}
tx := tenantDB.Begin()
func() {
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
_, err := s.generateDashboardDailyStat(tx, statDate, yesterdayStart, yesterdayEnd)
if err != nil {
tx.Rollback()
utils.ErrorLog("work", map[string]interface{}{
"source": "定时任务-生成每日统计",
"about_id": aboutID,
"stat_date": statDate,
"error": fmt.Sprintf("生成全局统计失败: %v", err),
})
return
}
if err := s.generateUserDailyStat(tx, aboutID, statDate, yesterdayStart, yesterdayEnd); err != nil {
tx.Rollback()
utils.ErrorLog("work", map[string]interface{}{
"source": "定时任务-生成每日统计",
"about_id": aboutID,
"stat_date": statDate,
"error": fmt.Sprintf("生成用户统计失败: %v", err),
})
return
}
if err := tx.Commit().Error; err != nil {
utils.ErrorLog("work", map[string]interface{}{
"source": "定时任务-生成每日统计",
"about_id": aboutID,
"stat_date": statDate,
"error": fmt.Sprintf("提交事务失败: %v", err),
})
return
}
utils.InfoLog("work", map[string]interface{}{
"source": "定时任务-生成每日统计",
"about_id": aboutID,
"stat_date": statDate,
"message": fmt.Sprintf("成功生成%s的统计数据", yesterday.Format("2006-01-02")),
})
}()
}
return nil
}
// generateDashboardDailyStat 生成全局每日统计
func (s *StatistTaskService) generateDashboardDailyStat(tx *gorm.DB, statDate int64, startDate, endDate int64) (*models.DashboardDailyStat, error) {
var existingStat models.DashboardDailyStat
err := tx.Where("stat_date = ? AND is_del = ?", statDate, 0).First(&existingStat).Error
if err == nil {
if err := tx.Model(&models.DashboardDailyStat{}).Where("id = ?", existingStat.ID).Update("is_del", 1).Error; err != nil {
return nil, err
}
}
var totalReceivingNum, totalOutboundNum int64
tx.Model(&models.Statist{}).
Where("stat_date <= ? AND is_del = ?", endDate, 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).
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).
Select("COALESCE(SUM(receiving_num), 0), COALESCE(SUM(outbound_num), 0)").
Row().Scan(&yesterdayInbound, &yesterdayOutbound)
var totalSalesCount int64
tx.Model(&models.SalesOrder{}).
Where("created_at <= ? AND is_del = ?", endDate, 0).
Count(&totalSalesCount)
var todaySalesCount int64
tx.Model(&models.SalesOrder{}).
Where("created_at >= ? AND created_at <= ? AND is_del = ?", startDate, endDate, 0).
Count(&todaySalesCount)
var yesterdaySalesCount int64
tx.Model(&models.SalesOrder{}).
Where("created_at >= ? AND created_at <= ? AND is_del = ?", beforeYesterdayStart, beforeYesterdayEnd, 0).
Count(&yesterdaySalesCount)
var productTotal int64
tx.Model(&models.Product{}).
Where("is_del = ?", 0).
Count(&productTotal)
var inventoryTotal int64
tx.Model(&models.Inventory{}).
Where("is_del = ?", 0).
Select("COALESCE(SUM(quantity), 0)").
Row().Scan(&inventoryTotal)
now := time.Now().Unix()
dashboardStat := &models.DashboardDailyStat{
StatDate: statDate,
TotalReceivingNum: totalReceivingNum,
TotalOutboundNum: totalOutboundNum,
TotalSalesCount: totalSalesCount,
ProductTotal: productTotal,
InventoryTotal: inventoryTotal,
TodayInbound: todayInbound,
TodayOutbound: todayOutbound,
YesterdayInbound: yesterdayInbound,
YesterdayOutbound: yesterdayOutbound,
TodaySalesCount: todaySalesCount,
YesterdaySalesCount: yesterdaySalesCount,
CreatedAt: now,
UpdatedAt: now,
IsDel: 0,
}
if err := tx.Create(dashboardStat).Error; err != nil {
return nil, err
}
return dashboardStat, nil
}
// generateUserDailyStat 生成用户每日统计
func (s *StatistTaskService) generateUserDailyStat(tx *gorm.DB, aboutID int64, statDate int64, startDate, endDate int64) error {
type UserStatGroup struct {
UserID int64 `gorm:"column:user_id"`
UserName string `gorm:"column:user_name"`
ReceivingNum int64 `gorm:"column:receiving_num"`
OutboundNum int64 `gorm:"column:outbound_num"`
SalesCount int64 `gorm:"column:sales_count"`
}
var userStats []UserStatGroup
tx.Table("employee").
Select(`
employee.id as user_id,
employee.username as user_name,
COALESCE((SELECT SUM(receiving_num) FROM statist WHERE create_by = employee.id AND stat_date >= ? AND stat_date <= ? AND is_del = ?), 0) as receiving_num,
COALESCE((SELECT SUM(outbound_num) FROM statist WHERE create_by = employee.id AND stat_date >= ? AND stat_date <= ? AND is_del = ?), 0) as outbound_num,
COALESCE((SELECT COUNT(*) FROM sales_order WHERE create_by = employee.id AND created_at >= ? AND created_at <= ? AND is_del = ?), 0) as sales_count
`, startDate, endDate, 0, startDate, endDate, 0, startDate, endDate, 0).
Where("employee.about_id = ? AND employee.is_del = ?", aboutID, 0).
Scan(&userStats)
if len(userStats) == 0 {
return nil
}
now := time.Now().Unix()
for _, userStat := range userStats {
var existingStat models.UserDailyStat
err := tx.Where("user_id = ? AND stat_date = ? AND is_del = ?", userStat.UserID, statDate, 0).First(&existingStat).Error
if err == nil {
if err := tx.Model(&models.UserDailyStat{}).Where("id = ?", existingStat.ID).Update("is_del", 1).Error; err != nil {
continue
}
}
newStat := &models.UserDailyStat{
UserID: userStat.UserID,
UserName: userStat.UserName,
StatDate: statDate,
ReceivingNum: userStat.ReceivingNum,
OutboundNum: userStat.OutboundNum,
SalesCount: userStat.SalesCount,
CreatedAt: now,
UpdatedAt: now,
IsDel: 0,
}
if err := tx.Create(newStat).Error; err != nil {
utils.ErrorLog("work", map[string]interface{}{
"source": "生成用户统计",
"user_id": userStat.UserID,
"error": fmt.Sprintf("创建用户统计记录失败: %v", err),
})
}
}
return nil
}

View File

@ -27,8 +27,28 @@ func (s *LogisticsService) GetLogisticsList(req request.QueryLogisticsRequest, d
req.PageSize = 20 req.PageSize = 20
} }
// 如果提供了 about_id先查询该租户下的所有仓库
var warehouseIDs []int64
if req.AboutID > 0 {
if err := databaseConn.Model(&models.Warehouse{}).
Where("about_id = ? AND is_del = ?", req.AboutID, 0).
Pluck("id", &warehouseIDs).Error; err != nil {
return nil, 0, errors.New("查询仓库信息失败")
}
// 如果没有找到仓库,直接返回空结果
if len(warehouseIDs) == 0 {
return []response.LogisticsResponse{}, 0, nil
}
}
query := databaseConn.Model(&models.Logistics{}).Where("del_flag = ?", "0") query := databaseConn.Model(&models.Logistics{}).Where("del_flag = ?", "0")
// 如果有仓库ID列表只查询这些仓库关联的物流模板
if len(warehouseIDs) > 0 {
query = query.Where("id IN (?)", warehouseIDs)
}
if req.Keyword != "" { if req.Keyword != "" {
query = query.Where("template_name LIKE ? OR delivery_province LIKE ? OR delivery_city LIKE ?", query = query.Where("template_name LIKE ? OR delivery_province LIKE ? OR delivery_city LIKE ?",
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%") "%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
@ -39,7 +59,7 @@ func (s *LogisticsService) GetLogisticsList(req request.QueryLogisticsRequest, d
} }
// === debug: 打印完整SQL === // === debug: 打印完整SQL ===
log.Printf("[GetLogisticsList] 请求参数 page=%d pageSize=%d keyword=%q", req.Page, req.PageSize, req.Keyword) log.Printf("[GetLogisticsList] 请求参数 page=%d pageSize=%d keyword=%q about_id=%d", req.Page, req.PageSize, req.Keyword, req.AboutID)
// === debug end === // === debug end ===
var total int64 var total int64

View File

@ -1,6 +1,7 @@
package service package service
import ( import (
"fmt"
"gorm.io/gorm" "gorm.io/gorm"
"psi/database" "psi/database"
"psi/models" "psi/models"
@ -13,7 +14,62 @@ import (
type StatistService struct{} type StatistService struct{}
// DashboardStatist 获取仪表盘统计数据 // DashboardStatist 获取仪表盘统计数据
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...)
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()
if req.StartDate == 0 {
req.StartDate = startOfDay
}
if req.EndDate == 0 {
req.EndDate = endOfDay
}
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
}*/
// DashboardStatist 获取仪表盘统计数据
/*func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest, db ...*gorm.DB) (*systemRes.DashboardStatistResponse, error) {
databaseConn := database.OptionalDB(db...) databaseConn := database.OptionalDB(db...)
// 计算今日的时间范围(如果未指定) // 计算今日的时间范围(如果未指定)
@ -94,6 +150,152 @@ func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest,
TotalSaleCount: totalSaleCount, TotalSaleCount: totalSaleCount,
UserStats: userStats, UserStats: userStats,
}, nil }, nil
}*/
// DashboardStatist 获取仪表盘统计数据
func (s *StatistService) DashboardStatist(req systemReq.DashboardStatistRequest, db ...*gorm.DB) (*systemRes.DashboardStatistResponse, 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()
if req.StartDate == 0 {
req.StartDate = startOfDay
}
if req.EndDate == 0 {
req.EndDate = endOfDay
}
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
}
// getDashboardStatRealtime 实时查询仪表盘统计数据(降级方案)
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)
var totalReceivingCount, totalOutboundCount int64
statistQuery := databaseConn.Model(&models.Statist{}).
Where("stat_date >= ? AND stat_date <= ? AND is_del = ?", startDate, endDate, 0)
var statistList []models.Statist
if err := statistQuery.Find(&statistList).Error; err != nil {
return nil, utils.NewError("查询统计数据失败")
}
if len(statistList) == 0 {
return &systemRes.DashboardStatistResponse{
TotalReceivingCount: 0,
TotalOutboundCount: 0,
TotalSaleCount: totalSaleCount,
UserStats: []systemRes.UserStatItem{},
}, nil
}
for _, stat := range statistList {
totalReceivingCount += stat.ReceivingNum
totalOutboundCount += stat.OutboundNum
}
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 = ?", startDate, endDate, 0).
Group("create_by").
Scan(&userStatGroups)
for _, group := range userStatGroups {
var employee models.Employee
if err := database.DB.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,
})
}
}
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,
}, nil
} }
// GetWarehouseStatist 获取仓库统计数据 // GetWarehouseStatist 获取仓库统计数据