From 31f300e0b3d96af5cdde1aed2c5fe95074574640 Mon Sep 17 00:00:00 2001 From: Administrator <1269936630@qq.com> Date: Wed, 17 Jun 2026 15:29:47 +0800 Subject: [PATCH] 11 --- controllers/process.go | 135 ++++++++++++++++++++++++++++++- controllers/product_book.go | 3 + database/product_sync.go | 157 ++++++++++++++++++++++++++++++++++++ routes/routes.go | 8 ++ 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 controllers/product_book.go create mode 100644 database/product_sync.go diff --git a/controllers/process.go b/controllers/process.go index be85f72..b7185e1 100644 --- a/controllers/process.go +++ b/controllers/process.go @@ -195,8 +195,87 @@ func (r *ProcessApi) SubmitReceiving(c *gin.Context) { return } + // 同步给主库对应的product_book_xx中 + /*var warehouse models.Warehouse + if err := databaseConn.Where("id = ? AND is_del = 0", receivingOrder.WarehouseID).First(&warehouse).Error; err != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "查询仓库失败", + "warehouse_id": receivingOrder.WarehouseID, + "err_msg": err.Error(), + }) + return + }*/ + + // 同步给主库对应的product_book_xx中 + // 获取租户数据库连接 + tenantDB, err := database.GetTenantDB(aboutID) + if err != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "获取租户数据库连接失败", + "user_id": aboutID, + "err_msg": err.Error(), + }) + return + } + + // 查询商品详细信息 + var products []models.Product + if err := tenantDB.Where("id IN ? AND is_del = 0", itemProductIDs).Find(&products).Error; err != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "查询商品列表失败", + "user_id": aboutID, + "product_ids": itemProductIDs, + "err_msg": err.Error(), + }) + return + } + + // 查询库位信息(用于获取库位名称) + locationMap := make(map[int64]string) + for _, product := range products { + // 通过库存明细表查询商品所在的库位 + var inventoryDetail models.InventoryDetail + if err := tenantDB.Where("product_id = ? AND warehouse_id = ? AND is_del = 0", product.ID, receivingOrder.WarehouseID).First(&inventoryDetail).Error; err == nil { + var location models.Location + if err := tenantDB.Where("id = ? AND is_del = 0", inventoryDetail.LocationID).First(&location).Error; err == nil { + locationMap[product.ID] = location.Code + } + } + } + + // 逐个同步商品到主库 + for _, product := range products { + locationCode := "" + if code, exists := locationMap[product.ID]; exists { + locationCode = code + } + + if syncErr := database.SyncProductToMainDB(aboutID, &product, warehouse.ID, warehouse.Name, 0, locationCode); syncErr != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "同步商品到主库失败", + "user_id": aboutID, + "product_id": product.ID, + "warehouse_id": warehouse.ID, + "location_code": locationCode, + "err_msg": syncErr.Error(), + }) + } else { + utils.InfoLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "同步商品到主库成功", + "user_id": aboutID, + "product_id": product.ID, + "product_name": product.Name, + "barcode": product.Barcode, + "warehouse_id": warehouse.ID, + "location_code": locationCode, + }) + } + } + warehouseId := warehouse.ID + //warehouseId := warehouse.ID + productIds := make([]string, len(itemProductIDs)) for i, pid := range itemProductIDs { productIds[i] = fmt.Sprintf("%d", pid) @@ -211,6 +290,60 @@ func (r *ProcessApi) SubmitReceiving(c *gin.Context) { _ = writer.WriteField("userId", fmt.Sprintf("%d", aboutID)) _ = writer.WriteField("warehouseId", fmt.Sprintf("%d", warehouseId)) _ = writer.WriteField("productId", productIdsStr) + closeErr := writer.Close() + if closeErr != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "关闭multipart writer失败", + "err_msg": closeErr.Error(), + }) + return + } + + client := &http.Client{} + httpReq, err := http.NewRequest(method, url, payload) + + if err != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "创建HTTP请求失败", + "err_msg": err.Error(), + }) + return + } + httpReq.Header.Add("Authorization", "Basic ZWxhc3RpYzo1bVJESVVnNTJWQzBmcDE0bnctRg==") + httpReq.Header.Set("Content-Type", writer.FormDataContentType()) + + res, err := client.Do(httpReq) + if err != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "调用外部API失败", + "err_msg": err.Error(), + }) + return + } + defer func(Body io.ReadCloser) { + closeBodyErr := Body.Close() + if closeBodyErr != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "调用外部API失败-Body关闭失败", + "err_msg": closeBodyErr.Error(), + }) + } + }(res.Body) + + body, err := io.ReadAll(res.Body) + if err != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "读取响应失败", + "err_msg": err.Error(), + }) + return + } + + /*payload := &bytes.Buffer{} + writer := multipart.NewWriter(payload) + _ = writer.WriteField("userId", fmt.Sprintf("%d", aboutID)) + _ = writer.WriteField("warehouseId", fmt.Sprintf("%d", warehouseId)) + _ = writer.WriteField("productId", productIdsStr) err := writer.Close() if err != nil { utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ @@ -258,7 +391,7 @@ func (r *ProcessApi) SubmitReceiving(c *gin.Context) { "err_msg": err.Error(), }) return - } + }*/ utils.InfoLog(constant.LoggerChannelWork, logrus.Fields{ "source": "外部API调用完成", diff --git a/controllers/product_book.go b/controllers/product_book.go new file mode 100644 index 0000000..4bc60d3 --- /dev/null +++ b/controllers/product_book.go @@ -0,0 +1,3 @@ +package controllers + +type ProductBookApi struct{} diff --git a/database/product_sync.go b/database/product_sync.go new file mode 100644 index 0000000..9e55737 --- /dev/null +++ b/database/product_sync.go @@ -0,0 +1,157 @@ +package database + +import ( + "fmt" + "psi/models" + + "gorm.io/gorm" +) + +// SyncProductToMainDB 将租户库的商品信息同步到主库的 product 表和 product_book 表 +// aboutID: 租户ID +// product: 商品对象 +// warehouseID: 仓库ID +// warehouseName: 仓库名称 +// locationID: 库位ID +// locationName: 库位名称 +func SyncProductToMainDB(aboutID int64, product *models.Product, warehouseID int64, warehouseName string, locationID int64, locationName string) error { + if aboutID == 0 { + return nil + } + + // 1. 同步到 product 表 + if err := syncToProductTable(aboutID, product, warehouseID, warehouseName, locationID, locationName); err != nil { + return fmt.Errorf("同步到product表失败: %v", err) + } + + // 2. 同步到 product_book 表(如果有条码/ISBN) + if product.Barcode != "" { + if err := syncToProductBookTable(aboutID, product, warehouseID, warehouseName, locationID, locationName); err != nil { + return fmt.Errorf("同步到product_book表失败: %v", err) + } + } + + return nil +} + +// syncToProductTable 同步到主库 product 表 +func syncToProductTable(aboutID int64, product *models.Product, warehouseID int64, warehouseName string, locationID int64, locationName string) error { + var existingProduct models.Product + err := DB.Where("barcode = ? AND about_id = ? AND is_del = ?", product.Barcode, aboutID, 0).First(&existingProduct).Error + + if err != nil && err == gorm.ErrRecordNotFound { + mainProduct := models.Product{ + AboutId: aboutID, + WarehouseID: warehouseID, + WarehouseName: warehouseName, + LocationID: locationID, + LocationName: locationName, + CategoryID: product.CategoryID, + Name: product.Name, + Appearance: product.Appearance, + Barcode: product.Barcode, + Price: product.Price, + SalePrice: product.SalePrice, + Cost: product.Cost, + LiveImage: product.LiveImage, + Status: product.Status, + CreatedAt: product.CreatedAt, + UpdatedAt: product.UpdatedAt, + IsDel: 0, + } + + if createErr := DB.Create(&mainProduct).Error; createErr != nil { + return fmt.Errorf("创建主库商品失败: %v", createErr) + } + } else if err == nil { + updateData := map[string]interface{}{ + "warehouse_id": warehouseID, + "warehouse_name": warehouseName, + "location_id": locationID, + "location_name": locationName, + "name": product.Name, + "appearance": product.Appearance, + "price": product.Price, + "sale_price": product.SalePrice, + "cost": product.Cost, + "live_image": product.LiveImage, + "status": product.Status, + "updated_at": product.UpdatedAt, + } + + if updateErr := DB.Model(&existingProduct).Updates(updateData).Error; updateErr != nil { + return fmt.Errorf("更新主库商品失败: %v", updateErr) + } + } else { + return fmt.Errorf("查询主库商品失败: %v", err) + } + + return nil +} + +// syncToProductBookTable 同步到主库 product_book 分表 +func syncToProductBookTable(aboutID int64, product *models.Product, warehouseID int64, warehouseName string, locationID int64, locationName string) error { + isbn := product.Barcode + tableName := models.ProductBookTableName(isbn) + + var existingBook models.ProductBook + err := DB.Table(tableName).Where("isbn = ? AND about_id = ? AND is_del = ?", isbn, aboutID, 0).First(&existingBook).Error + + if err != nil && err == gorm.ErrRecordNotFound { + bookRecord := models.ProductBook{ + ID: product.ID, + AboutId: aboutID, + WarehouseID: warehouseID, + WarehouseName: warehouseName, + LocationID: locationID, + LocationName: locationName, + CategoryID: product.CategoryID, + StandardProductID: product.StandardProductID, + ISBN: isbn, + Barcode: product.Barcode, + Name: product.Name, + BookName: product.Name, + Appearance: product.Appearance, + Price: product.Price, + SalePrice: product.SalePrice, + Cost: product.Cost, + LiveImage: product.LiveImage, + Status: product.Status, + CreatedAt: product.CreatedAt, + UpdatedAt: product.UpdatedAt, + IsDel: 0, + } + + if createErr := DB.Table(tableName).Create(&bookRecord).Error; createErr != nil { + return fmt.Errorf("创建product_book记录失败: %v", createErr) + } + } else if err == nil { + updateData := map[string]interface{}{ + "id": product.ID, + "warehouse_id": warehouseID, + "warehouse_name": warehouseName, + "location_id": locationID, + "location_name": locationName, + "category_id": product.CategoryID, + "standard_product_id": product.StandardProductID, + "name": product.Name, + "book_name": product.Name, + "appearance": product.Appearance, + "barcode": product.Barcode, + "price": product.Price, + "sale_price": product.SalePrice, + "cost": product.Cost, + "live_image": product.LiveImage, + "status": product.Status, + "updated_at": product.UpdatedAt, + } + + if updateErr := DB.Table(tableName).Model(&existingBook).Updates(updateData).Error; updateErr != nil { + return fmt.Errorf("更新product_book记录失败: %v", updateErr) + } + } else { + return fmt.Errorf("查询product_book记录失败: %v", err) + } + + return nil +} diff --git a/routes/routes.go b/routes/routes.go index c688624..38c96c4 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -16,6 +16,7 @@ var warehouseApi = &controllers.WarehouseApi{} var ocrApi = &controllers.OcrApi{} var supplierApi = &controllers.SupplierApi{} var productApi = &controllers.ProductApi{} +var productBookApi = &controllers.ProductBookApi{} var processApi = &controllers.ProcessApi{} var barcodeApi = &controllers.BarcodeApi{} var purchaseApi = &controllers.PurchaseApi{} @@ -79,6 +80,13 @@ func initRouter() (r *gin.Engine) { public.GET("/product/distribution_list", productApi.GetDistributionProductList) // 获取分销商品列表 public.POST("/product/batchPush", productApi.BatchPushProducts) // 批量推送商品 public.POST("/product/pushToShop", productApi.PushProductToShop) // 推送商品到店铺 + // 商品反射 + public.GET("/product_book/list", productBookApi.List) // 获取商品反射列表 + public.GET("/product_book/list", productBookApi.Detail) // 获取商品反射详情 + public.POST("/product_book/create", productBookApi.Create) // 创建商品反射 + public.POST("/product_book/update", productBookApi.Update) // 更新商品反射 + public.GET("/product_book/del", productBookApi.Del) // 删除商品反射 + } sign := api.Group("")