fix(backend): 补全多处 Scan/Count 错误未检查,私钥缺失改为 Fatal
- stock.go: ApproveStockIn 入库前库存 Scan 加错误检查 - license.go: Activate 阶段 Count 设备数加错误检查;createTrialLicense 私钥未配置由静默跳过改为 log.Fatalf,防止生产环境新注册门店无 license - inventory.go: List 接口两处 Raw Scan 加错误检查并返回 500 - finance.go: ArBalance 汇总 Raw Scan 加错误检查并返回 500
This commit is contained in:
@@ -173,7 +173,7 @@ func (h *FinanceHandler) Summary(c *gin.Context) {
|
|||||||
TotalAmount float64 `json:"total_amount"`
|
TotalAmount float64 `json:"total_amount"`
|
||||||
}
|
}
|
||||||
var rows []row
|
var rows []row
|
||||||
h.db.Raw(`
|
if err := h.db.Raw(`
|
||||||
SELECT f.partner_id,
|
SELECT f.partner_id,
|
||||||
COALESCE(p.name, '') AS partner_name,
|
COALESCE(p.name, '') AS partner_name,
|
||||||
f.type,
|
f.type,
|
||||||
@@ -186,7 +186,10 @@ func (h *FinanceHandler) Summary(c *gin.Context) {
|
|||||||
AND f.status = 'open'
|
AND f.status = 'open'
|
||||||
GROUP BY f.partner_id, f.type
|
GROUP BY f.partner_id, f.type
|
||||||
ORDER BY total_amount DESC
|
ORDER BY total_amount DESC
|
||||||
`, shopID).Scan(&rows)
|
`, shopID).Scan(&rows).Error; err != nil {
|
||||||
|
util.RespondError(c, http.StatusInternalServerError, "QUERY_ERROR", "查询失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
util.RespondSuccess(c, rows)
|
util.RespondSuccess(c, rows)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,10 @@ func (h *InventoryHandler) List(c *gin.Context) {
|
|||||||
WHERE ` + baseWhere
|
WHERE ` + baseWhere
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
h.db.Raw(countSQL, args...).Scan(&total)
|
if err := h.db.Raw(countSQL, args...).Scan(&total).Error; err != nil {
|
||||||
|
util.RespondError(c, http.StatusInternalServerError, "QUERY_ERROR", "查询失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Data query
|
// Data query
|
||||||
dataSQL := `
|
dataSQL := `
|
||||||
@@ -119,7 +122,10 @@ func (h *InventoryHandler) List(c *gin.Context) {
|
|||||||
dataArgs := append(args, pageSize, (page-1)*pageSize)
|
dataArgs := append(args, pageSize, (page-1)*pageSize)
|
||||||
|
|
||||||
var rows []inventoryRow
|
var rows []inventoryRow
|
||||||
h.db.Raw(dataSQL, dataArgs...).Scan(&rows)
|
if err := h.db.Raw(dataSQL, dataArgs...).Scan(&rows).Error; err != nil {
|
||||||
|
util.RespondError(c, http.StatusInternalServerError, "QUERY_ERROR", "查询失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"data": rows, "total": total, "page": page, "page_size": pageSize})
|
c.JSON(http.StatusOK, gin.H{"data": rows, "total": total, "page": page, "page_size": pageSize})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ func (s *LicenseService) Activate(shopID uint64, licenseKey, deviceID, deviceNam
|
|||||||
|
|
||||||
// New device — enforce max_devices
|
// New device — enforce max_devices
|
||||||
var count int64
|
var count int64
|
||||||
s.db.Model(&model.LicenseDevice{}).Where("license_id = ?", lic.ID).Count(&count)
|
if err := s.db.Model(&model.LicenseDevice{}).Where("license_id = ?", lic.ID).Count(&count).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if int(count) >= lic.MaxDevices {
|
if int(count) >= lic.MaxDevices {
|
||||||
return nil, ErrDeviceLimitExceed
|
return nil, ErrDeviceLimitExceed
|
||||||
}
|
}
|
||||||
@@ -142,12 +144,11 @@ func (s *LicenseService) Deactivate(shopID uint64, deviceID string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createTrialLicense 在注册事务中为新门店签发 30 天 trial license。
|
// createTrialLicense 在注册事务中为新门店签发 30 天 trial license。
|
||||||
// 若私钥未配置则静默跳过(开发/测试环境可不配置私钥)。
|
// 私钥未配置时 Fatal,防止生产环境静默跳过导致新注册门店无 license。
|
||||||
func createTrialLicense(tx *gorm.DB, shopID uint64) {
|
func createTrialLicense(tx *gorm.DB, shopID uint64) {
|
||||||
privKey := config.C.License.Ed25519PrivateKey
|
privKey := config.C.License.Ed25519PrivateKey
|
||||||
if privKey == "" {
|
if privKey == "" {
|
||||||
log.Printf("[license] Ed25519 private key not configured, skipping trial for shop %d", shopID)
|
log.Fatalf("[license] Ed25519 private key not configured — cannot issue trial for shop %d; set License.Ed25519PrivateKey in config", shopID)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expiresAt := time.Now().Add(30 * 24 * time.Hour)
|
expiresAt := time.Now().Add(30 * 24 * time.Hour)
|
||||||
|
|||||||
@@ -52,10 +52,12 @@ func (s *StockService) ApproveStockIn(shopID, orderID, reviewerID uint64) error
|
|||||||
|
|
||||||
// 计算入库前库存总量(用于流水记录)
|
// 计算入库前库存总量(用于流水记录)
|
||||||
var qtyBefore float64
|
var qtyBefore float64
|
||||||
tx.Model(&model.Inventory{}).
|
if err := tx.Model(&model.Inventory{}).
|
||||||
Where("shop_id = ? AND warehouse_id = ? AND product_id = ? AND deleted_at IS NULL",
|
Where("shop_id = ? AND warehouse_id = ? AND product_id = ? AND deleted_at IS NULL",
|
||||||
shopID, warehouseID, productID).
|
shopID, warehouseID, productID).
|
||||||
Select("COALESCE(SUM(quantity), 0)").Scan(&qtyBefore)
|
Select("COALESCE(SUM(quantity), 0)").Scan(&qtyBefore).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var unitPricePtr *float64
|
var unitPricePtr *float64
|
||||||
if itemCopy.UnitPrice != 0 {
|
if itemCopy.UnitPrice != 0 {
|
||||||
@@ -322,7 +324,7 @@ func (s *StockService) CheckInventoryAvailability(shopID, warehouseID uint64, it
|
|||||||
have := sumMap[item.ProductID]
|
have := sumMap[item.ProductID]
|
||||||
if have < item.Quantity {
|
if have < item.Quantity {
|
||||||
var p model.Product
|
var p model.Product
|
||||||
s.db.Where("id = ?", item.ProductID).First(&p)
|
s.db.Where("id = ?", item.ProductID).First(&p) //nolint:errcheck — best-effort name lookup for error message
|
||||||
name := p.Name
|
name := p.Name
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = fmt.Sprintf("商品ID %d", item.ProductID)
|
name = fmt.Sprintf("商品ID %d", item.ProductID)
|
||||||
|
|||||||
Reference in New Issue
Block a user