fix(backend): 架构质量改进批次三 (#41-44)
- #41 CheckInventoryAvailability Scan 加错误检查,DB 故障不再误报库存不足 - #42 finance ListRecords 和 inventory Logs 补加 pageSize 上限(500) - #43 ReconcileInventory 两条 Scan 加错误检查,DB 故障返回 500 而非误导性空结果 - #44 license Info 改用 CountDevices(licenseID) 统计当前 license 绑定数,修复跨 license 计数偏差 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -119,8 +119,14 @@ func (h *AdminHandler) ReconcileInventory(c *gin.Context) {
|
|||||||
|
|
||||||
var invRows []invRow
|
var invRows []invRow
|
||||||
var logRows []logRow
|
var logRows []logRow
|
||||||
invQuery.Scan(&invRows)
|
if err := invQuery.Scan(&invRows).Error; err != nil {
|
||||||
logQuery.Scan(&logRows)
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "inventory query failed: " + err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := logQuery.Scan(&logRows).Error; err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "log query failed: " + err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 合并为 map 再求差
|
// 合并为 map 再求差
|
||||||
type key struct{ S, W, P uint64 }
|
type key struct{ S, W, P uint64 }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/wangjia/jiu/backend/internal/middleware"
|
"github.com/wangjia/jiu/backend/internal/middleware"
|
||||||
"github.com/wangjia/jiu/backend/internal/model"
|
"github.com/wangjia/jiu/backend/internal/model"
|
||||||
|
"github.com/wangjia/jiu/backend/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FinanceHandler struct {
|
type FinanceHandler struct {
|
||||||
@@ -33,9 +34,7 @@ func (h *FinanceHandler) ListRecords(c *gin.Context) {
|
|||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if q.PageSize <= 0 {
|
q.PageSize = util.ValidatePageSize(q.PageSize, 50, 500)
|
||||||
q.PageSize = 50
|
|
||||||
}
|
|
||||||
if q.Page <= 0 {
|
if q.Page <= 0 {
|
||||||
q.Page = 1
|
q.Page = 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/wangjia/jiu/backend/internal/middleware"
|
"github.com/wangjia/jiu/backend/internal/middleware"
|
||||||
"github.com/wangjia/jiu/backend/internal/model"
|
"github.com/wangjia/jiu/backend/internal/model"
|
||||||
|
"github.com/wangjia/jiu/backend/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InventoryHandler struct {
|
type InventoryHandler struct {
|
||||||
@@ -128,6 +129,7 @@ func (h *InventoryHandler) Logs(c *gin.Context) {
|
|||||||
shopID := middleware.GetShopID(c)
|
shopID := middleware.GetShopID(c)
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||||
|
pageSize = util.ValidatePageSize(pageSize, 20, 500)
|
||||||
|
|
||||||
query := h.db.Model(&model.InventoryLog{}).Where("shop_id = ?", shopID)
|
query := h.db.Model(&model.InventoryLog{}).Where("shop_id = ?", shopID)
|
||||||
|
|
||||||
|
|||||||
@@ -64,10 +64,11 @@ func (h *LicenseHandler) Info(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var deviceCount int64
|
deviceCount, err := h.svc.CountDevices(lic.ID)
|
||||||
// count active devices for this shop
|
if err != nil {
|
||||||
devs, _ := h.svc.ListDevices(shopID)
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
deviceCount = int64(len(devs))
|
return
|
||||||
|
}
|
||||||
|
|
||||||
phase := middleware.CalcLicensePhase(lic.ExpiresAt)
|
phase := middleware.CalcLicensePhase(lic.ExpiresAt)
|
||||||
c.JSON(http.StatusOK, gin.H{"data": gin.H{
|
c.JSON(http.StatusOK, gin.H{"data": gin.H{
|
||||||
|
|||||||
@@ -118,6 +118,13 @@ func (s *LicenseService) ShopInfo(shopID uint64) (*model.License, error) {
|
|||||||
return &lic, nil
|
return &lic, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountDevices 返回指定 license 下已绑定设备数。
|
||||||
|
func (s *LicenseService) CountDevices(licenseID uint64) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := s.db.Model(&model.LicenseDevice{}).Where("license_id = ?", licenseID).Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
// ListDevices 列出许可证下所有已绑定设备。
|
// ListDevices 列出许可证下所有已绑定设备。
|
||||||
func (s *LicenseService) ListDevices(shopID uint64) ([]model.LicenseDevice, error) {
|
func (s *LicenseService) ListDevices(shopID uint64) ([]model.LicenseDevice, error) {
|
||||||
var devs []model.LicenseDevice
|
var devs []model.LicenseDevice
|
||||||
|
|||||||
@@ -305,11 +305,13 @@ func (s *StockService) CheckInventoryAvailability(shopID, warehouseID uint64, it
|
|||||||
Total float64
|
Total float64
|
||||||
}
|
}
|
||||||
var sums []inventorySum
|
var sums []inventorySum
|
||||||
s.db.Model(&model.Inventory{}).
|
if err := s.db.Model(&model.Inventory{}).
|
||||||
Select("product_id, COALESCE(SUM(quantity), 0) AS total").
|
Select("product_id, COALESCE(SUM(quantity), 0) AS total").
|
||||||
Where("shop_id = ? AND warehouse_id = ? AND product_id IN ? AND deleted_at IS NULL",
|
Where("shop_id = ? AND warehouse_id = ? AND product_id IN ? AND deleted_at IS NULL",
|
||||||
shopID, warehouseID, productIDs).
|
shopID, warehouseID, productIDs).
|
||||||
Group("product_id").Scan(&sums)
|
Group("product_id").Scan(&sums).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
sumMap := make(map[uint64]float64, len(sums))
|
sumMap := make(map[uint64]float64, len(sums))
|
||||||
for _, s := range sums {
|
for _, s := range sums {
|
||||||
|
|||||||
Reference in New Issue
Block a user