feat(backend): 入库每行新建独立产品发独立序列号(product 加生产日期/批次列)
Deploy Server / release-deploy-server (push) Successful in 59s

入库 Create/Update 改为对每条明细新建独立 product(createIndependentProduct,
nextProductCode 自增 + uk_shop_code 唯一约束兜底),回填 product_id;不再按名称复用。
product 表加 production_date/batch_no。向后兼容:保留明细/库存快照列。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
This commit is contained in:
wangjia
2026-06-21 16:30:11 +08:00
parent b42a70ff3b
commit c1febfffec
6 changed files with 91 additions and 25 deletions
+36
View File
@@ -85,6 +85,42 @@ func nextProductCode(tx *gorm.DB, shopID uint64) (string, error) {
return fmt.Sprintf("P%03d", maxN+1), nil
}
// createIndependentProduct 为入库明细新建一个独立产品(特有产品/序列号),返回新 product。
// "入库每行 = 一个特有产品"模型:每条明细建一个独立 product、发新序列号,不按名称复用。
// 含 nextProductCode 自增 + 撞 uk_shop_code 唯一约束时重试。必须在事务内调用。
func createIndependentProduct(tx *gorm.DB, shopID uint64, name, series, spec, batchNo string, prodDate *model.Date, price float64) (model.Product, error) {
namePinyin, nameInitials := util.ToPinyin(name)
var prod model.Product
var err error
for attempt := 0; attempt < 5; attempt++ {
code, e := nextProductCode(tx, shopID)
if e != nil {
return model.Product{}, e
}
prod = model.Product{
TenantBase: model.TenantBase{ShopID: shopID},
PublicID: uuid.New().String(),
Code: code,
Name: name,
Series: series,
Spec: spec,
BatchNo: batchNo,
ProductionDate: prodDate,
PurchasePrice: price,
NamePinyin: namePinyin,
NameInitials: nameInitials,
}
prod.ID = 0
if err = tx.Create(&prod).Error; err == nil || !errors.Is(err, gorm.ErrDuplicatedKey) {
break
}
}
if err != nil {
return model.Product{}, err
}
return prod, nil
}
// Create POST /api/v1/products
func (h *ProductHandler) Create(c *gin.Context) {
shopID := middleware.GetShopID(c)