diff --git a/controllers/product_book.go b/controllers/product_book.go index 4bc60d3..a8848e8 100644 --- a/controllers/product_book.go +++ b/controllers/product_book.go @@ -1,3 +1,146 @@ package controllers +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "psi/constant" + "psi/models/request" + systemRes "psi/models/response" + "psi/service" + "psi/utils" +) + +var productBookService = &service.ProductBookService{} + type ProductBookApi struct{} + +// List 获取商品反射列表 +func (r *ProductBookApi) List(c *gin.Context) { + var req request.GetProductBookListRequest + + if err := c.ShouldBind(&req); err != nil { + utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields(gin.H{ + "source": "获取商品反射列表请求参数异常", + "err_msg": err.Error(), + })) + systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) + return + } + + result, err := productBookService.List(req) + if err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "获取商品反射列表异常", err, c, req) + return + } + + systemRes.OkWithDetailed(result, "获取成功", c) +} + +// Detail 获取商品反射详情 +func (r *ProductBookApi) Detail(c *gin.Context) { + id := c.Query("id") + isbn := c.Query("isbn") + + if id == "" || isbn == "" { + utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields(gin.H{ + "source": "获取商品反射详情请求参数异常", + "err_msg": "id和isbn不能为空", + })) + systemRes.FailWithValidateMessage("参数错误: id和isbn不能为空", c) + return + } + + var idInt int64 + if _, err := fmt.Sscanf(id, "%d", &idInt); err != nil || idInt <= 0 { + utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields(gin.H{ + "source": "获取商品反射详情请求参数异常", + "err_msg": "ID格式错误", + })) + systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c) + return + } + + result, err := productBookService.Detail(idInt, isbn) + if err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "获取商品反射详情异常", err, c, gin.H{"id": idInt, "isbn": isbn}) + return + } + + systemRes.OkWithDetailed(result, "获取成功", c) +} + +// Create 创建商品反射 +func (r *ProductBookApi) Create(c *gin.Context) { + var req request.ProductBookRequest + + if err := c.ShouldBind(&req); err != nil { + utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields(gin.H{ + "source": "创建商品反射请求参数异常", + "err_msg": err.Error(), + })) + systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) + return + } + + id, err := productBookService.Create(req) + if err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "创建商品反射异常", err, c, req) + return + } + + systemRes.OkWithDetailed(gin.H{"id": id}, "创建成功", c) +} + +// Update 更新商品反射 +func (r *ProductBookApi) Update(c *gin.Context) { + var req request.ProductBookRequest + + if err := c.ShouldBind(&req); err != nil { + utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields(gin.H{ + "source": "更新商品反射请求参数异常", + "err_msg": err.Error(), + })) + systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) + return + } + + if err := productBookService.Update(req); err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "更新商品反射异常", err, c, req) + return + } + + systemRes.OkWithMessage("更新成功", c) +} + +// Del 删除商品反射 +func (r *ProductBookApi) Del(c *gin.Context) { + id := c.Query("id") + isbn := c.Query("isbn") + + if id == "" || isbn == "" { + utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields(gin.H{ + "source": "删除商品反射请求参数异常", + "err_msg": "id和isbn不能为空", + })) + systemRes.FailWithValidateMessage("参数错误: id和isbn不能为空", c) + return + } + + var idInt int64 + if _, err := fmt.Sscanf(id, "%d", &idInt); err != nil || idInt <= 0 { + utils.ErrorLog(constant.LoggerChannelRequest, logrus.Fields(gin.H{ + "source": "删除商品反射请求参数异常", + "err_msg": "ID格式错误", + })) + systemRes.FailWithValidateMessage("参数错误: ID格式不正确", c) + return + } + + if err := productBookService.Del(idInt, isbn); err != nil { + utils.FailWithRequestLog(constant.LoggerChannelWork, "删除商品反射异常", err, c, gin.H{"id": idInt, "isbn": isbn}) + return + } + + systemRes.OkWithMessage("删除成功", c) +} diff --git a/models/request/product_book.go b/models/request/product_book.go new file mode 100644 index 0000000..1e717ba --- /dev/null +++ b/models/request/product_book.go @@ -0,0 +1,81 @@ +package request + +// ProductBookRequest 创建/更新商品反射请求 +type ProductBookRequest struct { + ID int64 `form:"id"` // 商品ID(更新时必填) + CategoryID int64 `form:"category_id"` // 分类ID + AboutId int64 `form:"about_id"` // 关联ID(租户ID) + WarehouseID int64 `form:"warehouse_id"` // 仓库ID + WarehouseName string `form:"warehouse_name"` // 仓库名称 + LocationID int64 `form:"location_id"` // 货位ID + LocationName string `form:"location_name"` // 货位名称 + StandardProductID int64 `form:"standard_product_id"` // 关联标品ID + Fid int64 `form:"fid"` // 父级ID + Type int8 `form:"type"` // 类型 1正常 2套装书 3一号多书 4无书号 + ISBN string `form:"isbn" binding:"required"` // ISBN + FISBN string `form:"f_isbn"` // FISBN + BookName string `form:"book_name"` // 书名 + FBookName string `form:"f_book_name"` // 副书名 + Author string `form:"author"` // 作者 + Publishing string `form:"publishing"` // 出版社 + PublicationTime int64 `form:"publication_time"` // 出版日期时间戳 + Binding string `form:"binding"` // 装帧 + PagesCount int64 `form:"pages_count"` // 页数 + WordsCount int64 `form:"words_count"` // 字数 + Format int64 `form:"format"` // 开本 + CatID string `form:"cat_id"` // 类目json字符串 + Name string `form:"name"` // 商品名称 + Appearance int64 `form:"appearance"` // 品相 + Barcode string `form:"barcode"` // 条码 + Price int64 `form:"price"` // 价格 + SalePrice int64 `form:"sale_price"` // 书价 + Cost int64 `form:"cost"` // 最低运费 + LiveImage []string `form:"live_image[]"` // 实拍图数组 + IsBatchManaged int8 `form:"is_batch_managed"` // 是否批次管理 + IsShelfLifeManaged int8 `form:"is_shelf_life_managed"` // 是否效期管理 + Status int8 `form:"status"` // 状态 +} + +// GetProductBookListRequest 获取商品反射列表请求 +type GetProductBookListRequest struct { + Page int `form:"page"` // 页码 + PageSize int `form:"page_size"` // 每页数量 + ID int64 `form:"id"` // 商品ID + CategoryID int64 `form:"category_id"` // 分类ID + AboutId int64 `form:"about_id"` // 关联ID + WarehouseID int64 `form:"warehouse_id"` // 仓库ID + WarehouseName string `form:"warehouse_name"` // 仓库名称 + LocationID int64 `form:"location_id"` // 货位ID + LocationName string `form:"location_name"` // 货位名称 + StandardProductID int64 `form:"standard_product_id"` // 关联标品ID + Fid int64 `form:"fid"` // 父级ID + Type *int8 `form:"type"` // 类型 + ISBN string `form:"isbn"` // ISBN + FISBN string `form:"f_isbn"` // FISBN + BookName string `form:"book_name"` // 书名 + FBookName string `form:"f_book_name"` // 副书名 + Author string `form:"author"` // 作者 + Publishing string `form:"publishing"` // 出版社 + PublicationTime int64 `form:"publication_time"` // 出版日期 + Binding string `form:"binding"` // 装帧 + PagesCount int64 `form:"pages_count"` // 页数 + WordsCount int64 `form:"words_count"` // 字数 + Format int64 `form:"format"` // 开本 + Name string `form:"name"` // 商品名称 + Appearance int64 `form:"appearance"` // 品相 + Barcode string `form:"barcode"` // 条码 + Price int64 `form:"price"` // 价格 + SalePrice int64 `form:"sale_price"` // 书价 + Cost int64 `form:"cost"` // 最低运费 + IsBatchManaged *int8 `form:"is_batch_managed"` // 批次管理 + IsShelfLifeManaged *int8 `form:"is_shelf_life_managed"` // 效期管理 + Status *int8 `form:"status"` // 状态 + StartCreatedAt int64 `form:"start_created_at"` // 创建时间开始 + EndCreatedAt int64 `form:"end_created_at"` // 创建时间结束 + Keyword string `form:"keyword"` // 关键词(搜索书名/作者/ISBN) +} + +// DeleteProductBookRequest 删除商品反射请求 +type DeleteProductBookRequest struct { + ID int64 `form:"id" binding:"required"` // 商品ID +} diff --git a/models/response/product_book.go b/models/response/product_book.go new file mode 100644 index 0000000..2a12789 --- /dev/null +++ b/models/response/product_book.go @@ -0,0 +1,102 @@ +package response + +import ( + "encoding/json" + "psi/models" +) + +// ProductBookListResponse 商品反射列表响应 +type ProductBookListResponse struct { + List []ProductBookItem `json:"list"` + Total int64 `json:"total"` + Page int `json:"page"` + PageSize int `json:"pageSize"` +} + +// ProductBookItem 商品反射项 +type ProductBookItem struct { + ID int64 `json:"id"` + CategoryID int64 `json:"category_id"` + AboutId int64 `json:"about_id"` + WarehouseID int64 `json:"warehouse_id"` + WarehouseName string `json:"warehouse_name"` + LocationID int64 `json:"location_id"` + LocationName string `json:"location_name"` + StandardProductID int64 `json:"standard_product_id"` + Fid int64 `json:"fid"` + Type int8 `json:"type"` + ISBN string `json:"isbn"` + FISBN string `json:"f_isbn"` + BookName string `json:"book_name"` + FBookName string `json:"f_book_name"` + Author string `json:"author"` + Publishing string `json:"publishing"` + PublicationTime int64 `json:"publication_time"` + Binding string `json:"binding"` + PagesCount int64 `json:"pages_count"` + WordsCount int64 `json:"words_count"` + Format int64 `json:"format"` + CatID interface{} `json:"cat_id"` + Name string `json:"name"` + Appearance int64 `json:"appearance"` + Barcode string `json:"barcode"` + Price int64 `json:"price"` + SalePrice int64 `json:"sale_price"` + Cost int64 `json:"cost"` + LiveImage []string `json:"live_image"` + IsBatchManaged int8 `json:"is_batch_managed"` + IsShelfLifeManaged int8 `json:"is_shelf_life_managed"` + Status int8 `json:"status"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` +} + +// ConvertProductBookToItem 转换模型为响应项 +func ConvertProductBookToItem(book models.ProductBook) ProductBookItem { + var liveImage []string + if len(book.LiveImage) > 0 { + json.Unmarshal(book.LiveImage, &liveImage) + } + + var catID interface{} + if len(book.CatID) > 0 { + json.Unmarshal(book.CatID, &catID) + } + + return ProductBookItem{ + ID: book.ID, + CategoryID: book.CategoryID, + AboutId: book.AboutId, + WarehouseID: book.WarehouseID, + WarehouseName: book.WarehouseName, + LocationID: book.LocationID, + LocationName: book.LocationName, + StandardProductID: book.StandardProductID, + Fid: book.Fid, + Type: book.Type, + ISBN: book.ISBN, + FISBN: book.FISBN, + BookName: book.BookName, + FBookName: book.FBookName, + Author: book.Author, + Publishing: book.Publishing, + PublicationTime: book.PublicationTime, + Binding: book.Binding, + PagesCount: book.PagesCount, + WordsCount: book.WordsCount, + Format: book.Format, + CatID: catID, + Name: book.Name, + Appearance: book.Appearance, + Barcode: book.Barcode, + Price: book.Price, + SalePrice: book.SalePrice, + Cost: book.Cost, + LiveImage: liveImage, + IsBatchManaged: book.IsBatchManaged, + IsShelfLifeManaged: book.IsShelfLifeManaged, + Status: book.Status, + CreatedAt: book.CreatedAt, + UpdatedAt: book.UpdatedAt, + } +} diff --git a/routes/routes.go b/routes/routes.go index 38c96c4..8705cc5 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -82,7 +82,7 @@ func initRouter() (r *gin.Engine) { public.POST("/product/pushToShop", productApi.PushProductToShop) // 推送商品到店铺 // 商品反射 public.GET("/product_book/list", productBookApi.List) // 获取商品反射列表 - public.GET("/product_book/list", productBookApi.Detail) // 获取商品反射详情 + public.GET("/product_book/detail", productBookApi.Detail) // 获取商品反射详情 public.POST("/product_book/create", productBookApi.Create) // 创建商品反射 public.POST("/product_book/update", productBookApi.Update) // 更新商品反射 public.GET("/product_book/del", productBookApi.Del) // 删除商品反射 diff --git a/service/product_book.go b/service/product_book.go new file mode 100644 index 0000000..aa5c933 --- /dev/null +++ b/service/product_book.go @@ -0,0 +1,370 @@ +package service + +import ( + "encoding/json" + "fmt" + "gorm.io/datatypes" + "psi/database" + "psi/models" + systemReq "psi/models/request" + systemRes "psi/models/response" + "psi/utils" + "time" +) + +type ProductBookService struct{} + +// List 获取商品反射列表 +func (s *ProductBookService) List(req systemReq.GetProductBookListRequest) (*systemRes.ProductBookListResponse, error) { + db := database.DB + + if req.Page < 1 { + req.Page = 1 + } + if req.PageSize < 1 || req.PageSize > 100 { + req.PageSize = 20 + } + + // 根据ISBN确定分表 + tableName := "product_book_00" + if req.ISBN != "" { + tableName = models.ProductBookTableName(req.ISBN) + } else if req.Barcode != "" { + tableName = models.ProductBookTableName(req.Barcode) + } + + query := db.Table(tableName).Where("is_del = ?", 0) + + // 添加查询条件 + if req.ID > 0 { + query = query.Where("id = ?", req.ID) + } + if req.CategoryID > 0 { + query = query.Where("category_id = ?", req.CategoryID) + } + if req.AboutId > 0 { + query = query.Where("about_id = ?", req.AboutId) + } + if req.WarehouseID > 0 { + query = query.Where("warehouse_id = ?", req.WarehouseID) + } + if req.WarehouseName != "" { + query = query.Where("warehouse_name LIKE ?", "%"+req.WarehouseName+"%") + } + if req.LocationID > 0 { + query = query.Where("location_id = ?", req.LocationID) + } + if req.LocationName != "" { + query = query.Where("location_name LIKE ?", "%"+req.LocationName+"%") + } + if req.StandardProductID > 0 { + query = query.Where("standard_product_id = ?", req.StandardProductID) + } + if req.Fid > 0 { + query = query.Where("fid = ?", req.Fid) + } + if req.Type != nil { + query = query.Where("type = ?", *req.Type) + } + if req.ISBN != "" { + query = query.Where("isbn = ?", req.ISBN) + } + if req.FISBN != "" { + query = query.Where("f_isbn = ?", req.FISBN) + } + if req.BookName != "" { + query = query.Where("book_name LIKE ?", "%"+req.BookName+"%") + } + if req.FBookName != "" { + query = query.Where("f_book_name LIKE ?", "%"+req.FBookName+"%") + } + if req.Author != "" { + query = query.Where("author LIKE ?", "%"+req.Author+"%") + } + if req.Publishing != "" { + query = query.Where("publishing LIKE ?", "%"+req.Publishing+"%") + } + if req.PublicationTime > 0 { + query = query.Where("publication_time = ?", req.PublicationTime) + } + if req.Binding != "" { + query = query.Where("binding = ?", req.Binding) + } + if req.PagesCount > 0 { + query = query.Where("pages_count = ?", req.PagesCount) + } + if req.WordsCount > 0 { + query = query.Where("words_count = ?", req.WordsCount) + } + if req.Format > 0 { + query = query.Where("format = ?", req.Format) + } + if req.Name != "" { + query = query.Where("name LIKE ?", "%"+req.Name+"%") + } + if req.Appearance > 0 { + query = query.Where("appearance = ?", req.Appearance) + } + if req.Barcode != "" { + query = query.Where("barcode = ?", req.Barcode) + } + if req.Price > 0 { + query = query.Where("price = ?", req.Price) + } + if req.SalePrice > 0 { + query = query.Where("sale_price = ?", req.SalePrice) + } + if req.Cost > 0 { + query = query.Where("cost = ?", req.Cost) + } + if req.IsBatchManaged != nil { + query = query.Where("is_batch_managed = ?", *req.IsBatchManaged) + } + if req.IsShelfLifeManaged != nil { + query = query.Where("is_shelf_life_managed = ?", *req.IsShelfLifeManaged) + } + if req.Status != nil { + query = query.Where("status = ?", *req.Status) + } + if req.StartCreatedAt > 0 { + query = query.Where("created_at >= ?", req.StartCreatedAt) + } + if req.EndCreatedAt > 0 { + query = query.Where("created_at <= ?", req.EndCreatedAt) + } + if req.Keyword != "" { + query = query.Where("(book_name LIKE ? OR author LIKE ? OR isbn LIKE ? OR barcode LIKE ?)", + "%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%") + } + + var total int64 + if err := query.Count(&total).Error; err != nil { + return nil, utils.NewError("查询总数失败") + } + + var books []models.ProductBook + offset := (req.Page - 1) * req.PageSize + if err := query.Order("created_at DESC").Offset(offset).Limit(req.PageSize).Find(&books).Error; err != nil { + return nil, utils.NewError("查询列表失败") + } + + items := make([]systemRes.ProductBookItem, 0, len(books)) + for _, book := range books { + items = append(items, systemRes.ConvertProductBookToItem(book)) + } + + return &systemRes.ProductBookListResponse{ + List: items, + Total: total, + Page: req.Page, + PageSize: req.PageSize, + }, nil +} + +// Detail 获取商品反射详情 +func (s *ProductBookService) Detail(id int64, isbn string) (*systemRes.ProductBookItem, error) { + db := database.DB + + // 根据ISBN确定分表 + tableName := models.ProductBookTableName(isbn) + + var book models.ProductBook + if err := db.Table(tableName).Where("id = ? AND is_del = ?", id, 0).First(&book).Error; err != nil { + return nil, utils.NewError("商品不存在") + } + + item := systemRes.ConvertProductBookToItem(book) + return &item, nil +} + +// Create 创建商品反射 +func (s *ProductBookService) Create(req systemReq.ProductBookRequest) (int64, error) { + db := database.DB + + now := time.Now().Unix() + + // 解析LiveImage + var liveImage datatypes.JSON + if len(req.LiveImage) > 0 { + jsonBytes, _ := json.Marshal(req.LiveImage) + liveImage = jsonBytes + } else { + liveImage = datatypes.JSON("[]") + } + + // 解析CatID + var catID datatypes.JSON + if req.CatID != "" { + catID = datatypes.JSON(req.CatID) + } else { + catID = datatypes.JSON("{}") + } + + // 根据ISBN确定分表 + tableName := models.ProductBookTableName(req.ISBN) + + book := models.ProductBook{ + CategoryID: req.CategoryID, + AboutId: req.AboutId, + WarehouseID: req.WarehouseID, + WarehouseName: req.WarehouseName, + LocationID: req.LocationID, + LocationName: req.LocationName, + StandardProductID: req.StandardProductID, + Fid: req.Fid, + Type: req.Type, + ISBN: req.ISBN, + FISBN: req.FISBN, + BookName: req.BookName, + FBookName: req.FBookName, + Author: req.Author, + Publishing: req.Publishing, + PublicationTime: req.PublicationTime, + Binding: req.Binding, + PagesCount: req.PagesCount, + WordsCount: req.WordsCount, + Format: req.Format, + CatID: catID, + Name: req.Name, + Appearance: req.Appearance, + Barcode: req.Barcode, + Price: req.Price, + SalePrice: req.SalePrice, + Cost: req.Cost, + LiveImage: liveImage, + IsBatchManaged: req.IsBatchManaged, + IsShelfLifeManaged: req.IsShelfLifeManaged, + Status: req.Status, + CreatedAt: now, + UpdatedAt: now, + IsDel: 0, + } + + if err := db.Table(tableName).Create(&book).Error; err != nil { + return 0, fmt.Errorf("创建商品反射失败: %w", err) + } + + return book.ID, nil +} + +// Update 更新商品反射 +func (s *ProductBookService) Update(req systemReq.ProductBookRequest) error { + db := database.DB + + if req.ID == 0 { + return utils.NewError("商品ID不能为空") + } + + now := time.Now().Unix() + + // 先查询获取ISBN以确定分表 + var existingBook models.ProductBook + + // 如果提供了ISBN,直接使用;否则需要遍历所有分表查找 + tableName := "" + if req.ISBN != "" { + tableName = models.ProductBookTableName(req.ISBN) + if err := db.Table(tableName).Where("id = ? AND is_del = ?", req.ID, 0).First(&existingBook).Error; err != nil { + return utils.NewError("商品不存在") + } + } else { + // 遍历所有分表查找 + allTables := models.ProductBookAllTableNames() + found := false + for _, tName := range allTables { + if err := db.Table(tName).Where("id = ? AND is_del = ?", req.ID, 0).First(&existingBook).Error; err == nil { + tableName = tName + found = true + break + } + } + if !found { + return utils.NewError("商品不存在") + } + } + + // 解析LiveImage + var liveImage datatypes.JSON + if len(req.LiveImage) > 0 { + jsonBytes, _ := json.Marshal(req.LiveImage) + liveImage = jsonBytes + } else { + liveImage = existingBook.LiveImage + } + + // 解析CatID + var catID datatypes.JSON + if req.CatID != "" { + catID = datatypes.JSON(req.CatID) + } else { + catID = existingBook.CatID + } + + updates := map[string]interface{}{ + "category_id": req.CategoryID, + "about_id": req.AboutId, + "warehouse_id": req.WarehouseID, + "warehouse_name": req.WarehouseName, + "location_id": req.LocationID, + "location_name": req.LocationName, + "standard_product_id": req.StandardProductID, + "fid": req.Fid, + "type": req.Type, + "f_isbn": req.FISBN, + "book_name": req.BookName, + "f_book_name": req.FBookName, + "author": req.Author, + "publishing": req.Publishing, + "publication_time": req.PublicationTime, + "binding": req.Binding, + "pages_count": req.PagesCount, + "words_count": req.WordsCount, + "format": req.Format, + "cat_id": catID, + "name": req.Name, + "appearance": req.Appearance, + "barcode": req.Barcode, + "price": req.Price, + "sale_price": req.SalePrice, + "cost": req.Cost, + "live_image": liveImage, + "is_batch_managed": req.IsBatchManaged, + "is_shelf_life_managed": req.IsShelfLifeManaged, + "status": req.Status, + "updated_at": now, + } + + if err := db.Table(tableName).Model(&existingBook).Updates(updates).Error; err != nil { + return fmt.Errorf("更新商品反射失败: %w", err) + } + + return nil +} + +// Del 删除商品反射(逻辑删除) +func (s *ProductBookService) Del(id int64, isbn string) error { + db := database.DB + + if id == 0 { + return utils.NewError("商品ID不能为空") + } + + now := time.Now().Unix() + + // 根据ISBN确定分表 + tableName := models.ProductBookTableName(isbn) + + var book models.ProductBook + if err := db.Table(tableName).Where("id = ? AND is_del = ?", id, 0).First(&book).Error; err != nil { + return utils.NewError("商品不存在") + } + + if err := db.Table(tableName).Model(&book).Updates(map[string]interface{}{ + "is_del": 1, + "updated_at": now, + }).Error; err != nil { + return fmt.Errorf("删除商品反射失败: %w", err) + } + + return nil +}