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)) }