From 2bdd357083e2c05fee2e7e36b18ba8003c52f550 Mon Sep 17 00:00:00 2001 From: xiaodongzhu825 <97694732@qq.com> Date: Wed, 17 Jun 2026 18:35:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=8F=8D=E5=B0=84=E5=95=86=E5=93=81=E8=A1=A8=E6=9C=AA=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E5=88=B0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/process_sync.go | 281 ++++++++++++++++++++++++++++++++++++ models/request/process.go | 16 +- 2 files changed, 289 insertions(+), 8 deletions(-) create mode 100644 controllers/process_sync.go diff --git a/controllers/process_sync.go b/controllers/process_sync.go new file mode 100644 index 0000000..3ce74ff --- /dev/null +++ b/controllers/process_sync.go @@ -0,0 +1,281 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "gorm.io/datatypes" + "gorm.io/gorm" + "psi/constant" + "psi/database" + "psi/es" + "psi/models" + systemReq "psi/models/request" + systemRes "psi/models/response" + "psi/service" + "psi/utils" + "time" +) + +type bookFieldData struct { + Fid int64 + BookType int8 + ISBN string + FISBN string + BookName string + FBookName string + Author string + Publishing string + PublicationTime int64 + Binding string + PagesCount int64 + WordsCount int64 + Format int64 + CatID datatypes.JSON +} + +// defaultCatID 默认分类ID +func defaultCatID() datatypes.JSON { + return datatypes.JSON(`{"xian_yu_cat_id": "", "kong_fu_zi_cat_id": "", "pin_duo_duo_cat_id": ""}`) +} + +// bookFieldsFromDB 从数据库中获取书籍信息 +func bookFieldsFromDB(bi models.BookInfo) bookFieldData { + catID := bi.CatID + if catID == nil { + catID = defaultCatID() + } + return bookFieldData{ + Fid: bi.Fid, BookType: bi.Type, ISBN: bi.ISBN, FISBN: bi.FISBN, + BookName: bi.BookName, FBookName: bi.FBookName, Author: bi.Author, + Publishing: bi.Publishing, PublicationTime: bi.PublicationTime, + Binding: bi.Binding, PagesCount: bi.PagesCount, WordsCount: bi.WordsCount, + Format: bi.Format, CatID: catID, + } +} + +// bookFieldsFromES 从ES中获取书籍信息 +func bookFieldsFromES(eb *systemRes.ESBook, isbn string) bookFieldData { + var bookType int8 + if eb.IsSuit > 0 { + bookType = 2 + } + var publicationTime int64 + if eb.PublicationTime != "" { + if v, err := strconv.ParseInt(eb.PublicationTime, 10, 64); err == nil { + publicationTime = v + } + } + var pagesCount int64 + if v, err := strconv.ParseInt(string(eb.PageCount), 10, 64); err == nil { + pagesCount = v + } + var wordsCount int64 + if v, err := strconv.ParseInt(string(eb.WordCount), 10, 64); err == nil { + wordsCount = v + } + var formatVal int64 + if v, err := strconv.ParseInt(string(eb.BookFormat), 10, 64); err == nil { + formatVal = v + } + catID := defaultCatID() + if catBytes, err := json.Marshal(eb.CatId); err == nil { + emptyCat := es.CatIdObject{} + if eb.CatId != emptyCat { + catID = catBytes + } + } + return bookFieldData{ + Fid: eb.Fid, BookType: bookType, ISBN: isbn, FISBN: eb.FISBN, + BookName: eb.BookName.Value, FBookName: eb.FBookName.Value, Author: eb.Author, + Publishing: eb.Publisher, PublicationTime: publicationTime, + Binding: eb.BindingLayout, PagesCount: pagesCount, WordsCount: wordsCount, + Format: formatVal, CatID: catID, + } +} + +// syncProductBookToMainDB 入库后同步商品信息到主库 product_book_xx 分表 +func (r *ProcessApi) syncProductBookToMainDB(req systemReq.ReceivingSubmitRequest, userInfo utils.UserInfo, c *gin.Context) { + itemProductIDs := make([]int64, len(req.Items)) + for i, item := range req.Items { + itemProductIDs[i] = item.ProductID + } + + tenantDB, err := database.GetTenantDB(userInfo.AboutID) + if err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "获取租户数据库连接失败", err, c, gin.H{"about_id": userInfo.AboutID}) + return + } + + var receivingOrder models.ReceivingOrder + if err := tenantDB.Where("id = ? AND is_del = 0", req.ReceivingOrderID).First(&receivingOrder).Error; err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "查询入库单失败", err, c, gin.H{"receiving_order_id": req.ReceivingOrderID}) + return + } + + var warehouse models.Warehouse + if err := tenantDB.Where("id = ? AND is_del = 0", receivingOrder.WarehouseID).First(&warehouse).Error; err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "查询仓库失败", err, c, gin.H{"warehouse_id": receivingOrder.WarehouseID}) + return + } + + var products []models.Product + if err := tenantDB.Where("id IN ? AND is_del = 0", itemProductIDs).Find(&products).Error; err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "查询商品列表失败", err, c, gin.H{"product_ids": itemProductIDs}) + return + } + + type locationInfo struct { + LocationID int64 + Code string + } + locationMap := make(map[int64]locationInfo) + for _, product := range products { + var invDetail models.InventoryDetail + if err := tenantDB.Where("product_id = ? AND warehouse_id = ? AND is_del = 0", product.ID, receivingOrder.WarehouseID).First(&invDetail).Error; err == nil { + var loc models.Location + if err := tenantDB.Where("id = ? AND is_del = 0", invDetail.LocationID).First(&loc).Error; err == nil { + locationMap[product.ID] = locationInfo{LocationID: loc.ID, Code: loc.Code} + } + } + } + + // 构建书籍信息:有StandardProductID查book_info表,否则查ES + bookSvc := service.BookService{} + dbBookMap := make(map[int64]models.BookInfo) + esBookMap := make(map[string]*systemRes.ESBook) + + for _, product := range products { + if product.Barcode == "" { + continue + } + if product.StandardProductID > 0 { + if _, exists := dbBookMap[product.StandardProductID]; !exists { + var bookInfo models.BookInfo + if err := database.DB.Where("id = ?", product.StandardProductID).First(&bookInfo).Error; err == nil { + dbBookMap[product.StandardProductID] = bookInfo + continue + } + } else { + continue + } + } + isbn := product.Barcode + if _, exists := esBookMap[isbn]; !exists { + esBook, esErr := bookSvc.GetBookInfo(systemReq.BookRequest{Isbn: isbn}) + if esErr != nil { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "ES查询BookInfo失败", "isbn": isbn, "err_msg": esErr.Error(), + }) + } + if esBook != nil { + esBookMap[isbn] = esBook + } + } + } + + // 逐个写入分表 + now := time.Now().Unix() + for _, product := range products { + isbn := product.Barcode + if isbn == "" { + continue + } + + var fields bookFieldData + var hasBookInfo bool + + if bi, exists := dbBookMap[product.StandardProductID]; product.StandardProductID > 0 && exists { + fields = bookFieldsFromDB(bi) + hasBookInfo = true + } else if eb, exists := esBookMap[isbn]; exists { + fields = bookFieldsFromES(eb, isbn) + hasBookInfo = true + } + + if !hasBookInfo { + utils.ErrorLog(constant.LoggerChannelWork, logrus.Fields{ + "source": "书籍信息未找到,跳过同步", + "product_id": product.ID, + "isbn": isbn, + "about_id": userInfo.AboutID, + }) + continue + } + + tableName := models.ProductBookTableName(isbn) + var locInfo locationInfo + if li, exists := locationMap[product.ID]; exists { + locInfo = li + } + + liveImage := product.LiveImage + if liveImage == nil { + liveImage = datatypes.JSON("[]") + } + + var existingBook models.ProductBook + err := database.DB.Table(tableName).Where("self_id = ? AND about_id = ? AND is_del = 0", product.ID, userInfo.AboutID).First(&existingBook).Error + + if err != nil && err == gorm.ErrRecordNotFound { + bookRecord := models.ProductBook{ + SelfID: product.ID, AboutId: userInfo.AboutID, + WarehouseID: warehouse.ID, WarehouseName: warehouse.Name, + LocationID: locInfo.LocationID, LocationName: locInfo.Code, + CategoryID: product.CategoryID, StandardProductID: product.StandardProductID, + Fid: fields.Fid, Type: fields.BookType, ISBN: isbn, FISBN: fields.FISBN, + BookName: fields.BookName, FBookName: fields.FBookName, + Author: fields.Author, Publishing: fields.Publishing, + PublicationTime: fields.PublicationTime, Binding: fields.Binding, + PagesCount: fields.PagesCount, WordsCount: fields.WordsCount, + Format: fields.Format, CatID: fields.CatID, + Name: product.Name, Appearance: product.Appearance, + Barcode: product.Barcode, Price: product.Price, + SalePrice: product.SalePrice, Cost: product.Cost, + LiveImage: liveImage, IsBatchManaged: product.IsBatchManaged, + IsShelfLifeManaged: product.IsShelfLifeManaged, Status: product.Status, + CreatedAt: now, UpdatedAt: now, IsDel: 0, + } + if createErr := database.DB.Table(tableName).Create(&bookRecord).Error; createErr != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "写入product_book分表失败", createErr, c, gin.H{ + "product_id": product.ID, "isbn": isbn, "table_name": tableName, + }) + return + } + } else if err == nil { + updateData := map[string]interface{}{ + "warehouse_id": warehouse.ID, "warehouse_name": warehouse.Name, + "location_id": locInfo.LocationID, "location_name": locInfo.Code, + "category_id": product.CategoryID, "standard_product_id": product.StandardProductID, + "fid": fields.Fid, "type": fields.BookType, + "f_isbn": fields.FISBN, "book_name": fields.BookName, + "f_book_name": fields.FBookName, "author": fields.Author, + "publishing": fields.Publishing, "publication_time": fields.PublicationTime, + "binding": fields.Binding, "pages_count": fields.PagesCount, + "words_count": fields.WordsCount, "format": fields.Format, "cat_id": fields.CatID, + "name": product.Name, "appearance": product.Appearance, + "barcode": product.Barcode, "price": product.Price, + "sale_price": product.SalePrice, "cost": product.Cost, + "live_image": liveImage, "is_batch_managed": product.IsBatchManaged, + "is_shelf_life_managed": product.IsShelfLifeManaged, "status": product.Status, + "updated_at": now, + } + if updateErr := database.DB.Table(tableName).Model(&existingBook).Updates(updateData).Error; updateErr != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "更新product_book分表失败", updateErr, c, gin.H{ + "product_id": product.ID, "isbn": isbn, "table_name": tableName, + }) + return + } + } else { + utils.FailWithRequestLog(constant.LoggerChannelWork, "查询product_book分表失败", err, c, gin.H{ + "product_id": product.ID, "isbn": isbn, "table_name": tableName, + }) + return + } + } + + fmt.Printf("同步product_book完成, 入库单ID: %d, 商品数: %d\n", req.ReceivingOrderID, len(products)) +} diff --git a/models/request/process.go b/models/request/process.go index 98c67e3..2aa22eb 100644 --- a/models/request/process.go +++ b/models/request/process.go @@ -34,10 +34,10 @@ type WaveItemRequest struct { } type BindWaveRequest struct { - WaveNo string `form:"wave_no" binding:"required"` - Operator string `form:"operator" binding:"required"` - OperatorID int64 `form:"operator_id" binding:"required"` - Remark string `form:"remark"` + WaveNo string `form:"wave_no" binding:"required"` // 波次号 + Operator string `form:"operator" binding:"required"` // 操作人 + OperatorID int64 `form:"operator_id" binding:"required"` // 操作人ID + Remark string `form:"remark"` // 备注 } type GetIdRequest struct { @@ -45,10 +45,10 @@ type GetIdRequest struct { } type ReceivingSubmitRequest struct { - ReceivingOrderID int64 `form:"receiving_order_id" binding:"required"` - WaveTaskID int64 `form:"wave_task_id" binding:"required"` - Force int8 `form:"force"` - Items []ReceivingItemRequest `form:"items[]"` + ReceivingOrderID int64 `form:"receiving_order_id" binding:"required"` // 入库单ID + WaveTaskID int64 `form:"wave_task_id" binding:"required"` // 波次ID + Force int8 `form:"force"` // 强制 + Items []ReceivingItemRequest `form:"items[]"` // 入库单项 } type ReceivingItemRequest struct {