diff --git a/controllers/sales.go b/controllers/sales.go index 264441b..7abd570 100644 --- a/controllers/sales.go +++ b/controllers/sales.go @@ -77,3 +77,22 @@ func (r *SalesApi) GetSalesOrderDetailList(c *gin.Context) { "data": result, }) } + +// ModifySalesOrderQuantity 修改销售订单明细订购数量 +func (r *SalesApi) ModifySalesOrderQuantity(c *gin.Context) { + var req systemReq.ModifySalesOrderQuantityRequest + + if err := c.ShouldBind(&req); err != nil { + ValidAndFail(constant.LoggerChannelRequest, "修改订购数量请求参数异常", "参数错误: "+err.Error(), c, err) + return + } + + userInfo := utils.GetUserInfo(c) + err := salesService.ModifySalesOrderQuantity(req, userInfo.ID, userInfo.Role, database.GetDB(c)) + if err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "修改订购数量异常", err, c, req) + return + } + + systemRes.OkWithMessage("修改订购数量成功", c) +} diff --git a/database/tenant.go b/database/tenant.go index 2c7c5b0..218211a 100644 --- a/database/tenant.go +++ b/database/tenant.go @@ -337,6 +337,11 @@ func migrateTenantTables(db *gorm.DB) { log.Printf("UserDailyStat表迁移警告: %v", err) } + err = db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品销毁日志表'").AutoMigrate(&models.ProductDestroyLog{}) + if err != nil { + log.Printf("ProductDestroyLog表迁移警告: %v", err) + } + log.Println("租户业务表迁移完成") // 重置旺店通运行中的任务(服务重启后清理) @@ -458,6 +463,15 @@ func migrateIncrementalTenantTables(db *gorm.DB) { } } + if !db.Migrator().HasTable(&models.ProductDestroyLog{}) { + err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品销毁日志表'").AutoMigrate(&models.ProductDestroyLog{}) + if err != nil { + log.Printf("ProductDestroyLog表增量迁移警告: %v", err) + } else { + log.Println("ProductDestroyLog表增量迁移成功") + } + } + // 重置旺店通运行中的任务(服务重启后清理) resetWangdianRunningTasks(db) } diff --git a/models/request/sales.go b/models/request/sales.go index 0e28cf3..b9a8ef4 100644 --- a/models/request/sales.go +++ b/models/request/sales.go @@ -36,3 +36,11 @@ type GetSalesOrderDetailListRequest struct { LogisticsNo string `form:"logistics_no"` ShopType int `form:"shop_type" json:"shop_type"` } + +// ModifySalesOrderQuantityRequest 修改销售订单明细订购数量请求 +type ModifySalesOrderQuantityRequest struct { + SalesOrderID int64 `form:"sales_order_id" json:"sales_order_id" binding:"required"` + SalesOrderItemID int64 `form:"sales_order_item_id" json:"sales_order_item_id" binding:"required"` + AdditionalQty int64 `form:"additional_quantity" json:"additional_quantity" binding:"required,min=1"` + Remark string `form:"remark" json:"remark"` +} diff --git a/routes/routes.go b/routes/routes.go index 66f19d1..105e4a8 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -234,9 +234,10 @@ func initRouter() (r *gin.Engine) { auth.GET("/receiving-order/list", receivingApi.GetReceivingOrderList) // 获取入库订单列表 auth.GET("/receiving-order/detail", receivingApi.GetReceivingOrderDetail) // 获取入库订单详情 // 销售订单管理 - auth.GET("/sales-order/list", salesApi.GetSalesOrderList) // 获取销售订单列表 - auth.GET("/sales-order/detail", salesApi.GetSalesOrderDetail) // 获取销售订单详情 - auth.GET("/sales-order/detaillist", salesApi.GetSalesOrderDetailList) // 获取销售订单详情列表 + auth.GET("/sales-order/list", salesApi.GetSalesOrderList) // 获取销售订单列表 + auth.GET("/sales-order/detail", salesApi.GetSalesOrderDetail) // 获取销售订单详情 + auth.GET("/sales-order/detaillist", salesApi.GetSalesOrderDetailList) // 获取销售订单详情列表 + auth.POST("/sales-order/modify-quantity", salesApi.ModifySalesOrderQuantity) // 修改销售订单明细订购数量 // 出库单管理 auth.GET("/outbound-order/list", outboundApi.GetOutboundOrderList) // 获取出库订单列表 auth.GET("/outbound-order/detail", outboundApi.GetOutboundOrderDetail) // 获取出库订单详情 diff --git a/service/book.go b/service/book.go index 580e2fb..af9093f 100644 --- a/service/book.go +++ b/service/book.go @@ -7,6 +7,7 @@ import ( "fmt" "gorm.io/datatypes" "io" + "log" "net/http" "psi/database" "psi/models" @@ -23,6 +24,7 @@ type BookService struct{} func (s *BookService) GetBookInfo(req systemReq.BookRequest) (*systemRes.ESBook, error) { // 调用ES接口 isbn := req.Isbn + log.Printf("[GetBookInfo] 请求 ISBN: %s", isbn) apiURL := fmt.Sprintf("https://book.center.yushutx.com/api/es/searchByISBNtoPsi?isbn=%s", isbn) resp, err := http.Get(apiURL) if err != nil { @@ -36,15 +38,19 @@ func (s *BookService) GetBookInfo(req systemReq.BookRequest) (*systemRes.ESBook, return nil, fmt.Errorf("[ERROR] 读取响应失败:%v\n", err) } + log.Printf("[GetBookInfo] ES 返回原始数据: %s", string(body)) + var esResp systemRes.GetEsBookResponse if err := json.Unmarshal(body, &esResp); err != nil { return nil, fmt.Errorf("[ERROR] 解析响应失败:%v\n", err) } // 如果ES接口没有返回数据,返回空对象 if esResp.Data.BookName.Value == "" && esResp.Data.ISBN == "" { + log.Printf("[GetBookInfo] ES 无数据,返回 nil") return nil, nil } + log.Printf("[GetBookInfo] 解析成功, 书名: %s, ISBN: %s", esResp.Data.BookName.Value, esResp.Data.ISBN) return &esResp.Data, nil } diff --git a/service/car.go b/service/car.go index 2d48021..3e78a50 100644 --- a/service/car.go +++ b/service/car.go @@ -43,24 +43,29 @@ func (s *CarService) GetCarList(req systemReq.QueryCarRequest, db ...*gorm.DB) ( return nil, 0, errors.New("查询小车列表失败") } + // 批量查询所有车的关联店铺,避免 N+1 + carIDs := make([]int64, len(cars)) + for i, car := range cars { + carIDs[i] = car.ID + } + var allShops []models.CarShop + if len(carIDs) > 0 { + databaseConn.Where("car_id IN ? AND is_del = ?", carIDs, 0).Find(&allShops) + } + shopMap := make(map[int64][]systemRes.CarShopInfo) + for _, cs := range allShops { + shopMap[cs.CarID] = append(shopMap[cs.CarID], systemRes.CarShopInfo{ + ShopID: strconv.FormatInt(cs.ShopID, 10), + ShopName: cs.ShopName, + ShopType: cs.ShopType, + ShopTypeText: getShopTypeText(cs.ShopType), + }) + } + responses := make([]systemRes.CarResponse, 0, len(cars)) for _, car := range cars { resp := systemRes.ConvertCarToResponse(car) - - var carShops []models.CarShop - if err := databaseConn.Where("car_id = ? AND is_del = ?", car.ID, 0).Find(&carShops).Error; err == nil { - shops := make([]systemRes.CarShopInfo, 0, len(carShops)) - for _, cs := range carShops { - shops = append(shops, systemRes.CarShopInfo{ - ShopID: strconv.FormatInt(cs.ShopID, 10), - ShopName: cs.ShopName, - ShopType: cs.ShopType, - ShopTypeText: getShopTypeText(cs.ShopType), - }) - } - resp.Shops = shops - } - + resp.Shops = shopMap[car.ID] responses = append(responses, resp) } diff --git a/service/product.go b/service/product.go index c60c640..37a9a55 100644 --- a/service/product.go +++ b/service/product.go @@ -3265,10 +3265,19 @@ func (s *ProductService) AuditProductLog(req systemReq.AuditProductLogRequest, u // 审核通过时,同步更新 ES 中的商品字段 if req.Status == 1 { + fmt.Printf("[DEBUG] 审核通过, NewLiveImage=%v, NewPageCount=%d, NewWordCount=%d\n", req.NewLiveImage, req.NewPageCount, req.NewWordCount) if err := s.syncApprovedLogToES(log.Barcode, req); err != nil { // ES 同步失败只记日志,不阻断审核流程 fmt.Printf("[WARN] 审核后同步 ES 失败 (product_log_id=%d, barcode=%s): %v\r\n", req.ID, log.Barcode, err) } + + // 同步更新 product 表的 live_image,解决其他平台从 product 表读不到新图片的问题 + if len(req.NewLiveImage) > 0 { + liveImageJSON, _ := json.Marshal(req.NewLiveImage) + if err := database.DB.Model(&models.Product{}).Where("barcode = ?", log.Barcode).Update("live_image", datatypes.JSON(liveImageJSON)).Error; err != nil { + fmt.Printf("[WARN] 审核后更新 product 表 live_image 失败 (product_log_id=%d, barcode=%s): %v\r\n", req.ID, log.Barcode, err) + } + } } return nil @@ -3302,6 +3311,21 @@ func (s *ProductService) syncApprovedLogToES(barcode string, req systemReq.Audit data["publication_time"] = req.NewPublicationTime } data["is_suit"] = req.NewIsSuit + if len(req.NewLiveImage) > 0 { + data["book_pic"] = map[string]string{ + "localPath": "", + "pddPath": req.NewLiveImage[0], + } + data["book_pic_b"] = req.NewLiveImage[0] + } + if req.NewPageCount > 0 { + data["page_count"] = strconv.FormatInt(req.NewPageCount, 10) + data["pages"] = strconv.FormatInt(req.NewPageCount, 10) + } + if req.NewWordCount > 0 { + data["word_count"] = strconv.FormatInt(req.NewWordCount, 10) + data["words"] = strconv.FormatInt(req.NewWordCount, 10) + } payload := map[string]interface{}{ "isbn": barcode, @@ -3313,6 +3337,9 @@ func (s *ProductService) syncApprovedLogToES(barcode string, req systemReq.Audit return fmt.Errorf("序列化请求参数失败:%w", err) } + fmt.Printf("[DEBUG] ES更新请求 URL: %s\n", esURL) + fmt.Printf("[DEBUG] ES更新请求 payload: %s\n", string(jsonBytes)) + resp, err := http.Post(esURL, "application/json", bytes.NewReader(jsonBytes)) if err != nil { return fmt.Errorf("调用 ES 更新接口失败:%w", err) @@ -3320,6 +3347,8 @@ func (s *ProductService) syncApprovedLogToES(barcode string, req systemReq.Audit defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) + fmt.Printf("[DEBUG] ES更新响应 status=%d body: %s\n", resp.StatusCode, string(body)) + if resp.StatusCode != http.StatusOK { return fmt.Errorf("ES 更新接口返回非200状态码[%d]: %s", resp.StatusCode, string(body)) } diff --git a/service/sales.go b/service/sales.go index 4fb3634..438858c 100644 --- a/service/sales.go +++ b/service/sales.go @@ -394,3 +394,46 @@ func (s *SalesService) GetSalesOrderDetail(id int64, creatorID int64, role int64 detail := systemRes.ConvertSalesOrderToDetail(order.SalesOrder, order.CustomerName, order.WarehouseName, detailItems) return &detail, nil } + +// ModifySalesOrderQuantity 修改销售订单明细订购数量 +func (s *SalesService) ModifySalesOrderQuantity(req systemReq.ModifySalesOrderQuantityRequest, creatorID int64, role int64, db ...*gorm.DB) error { + databaseConn := database.OptionalDB(db...) + + var salesOrder models.SalesOrder + if err := databaseConn.Where("id = ? AND is_del = ?", req.SalesOrderID, 0).First(&salesOrder).Error; err != nil { + return utils.NewError("销售订单不存在") + } + + if role == 128 && salesOrder.SalesPersonID != creatorID { + return utils.NewError("无权操作该销售订单") + } + + if salesOrder.Status == 6 { + return utils.NewError("已取消的订单无法修改订购数量") + } + + var item models.SalesOrderItem + if err := databaseConn.Where("id = ? AND sales_order_id = ? AND is_del = ?", req.SalesOrderItemID, req.SalesOrderID, 0).First(&item).Error; err != nil { + return utils.NewError("订单明细不存在") + } + + newQuantity := req.AdditionalQty + newAmount := item.UnitPrice * newQuantity + amountDiff := newAmount - item.Amount + + return databaseConn.Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&item).Updates(map[string]interface{}{ + "quantity": newQuantity, + "amount": newAmount, + }).Error; err != nil { + return utils.NewError("更新订单明细失败") + } + + if err := tx.Model(&models.SalesOrder{}).Where("id = ?", req.SalesOrderID). + Update("total_amount", salesOrder.TotalAmount+amountDiff).Error; err != nil { + return utils.NewError("更新订单总金额失败") + } + + return nil + }) +}