package controllers import ( "fmt" "github.com/gin-gonic/gin" "net/http" "net/url" "psi/constant" "psi/database" systemReq "psi/models/request" systemRes "psi/models/response" "psi/service" "psi/utils" "strings" ) type ProductApi struct{} var productService = service.ProductService{} // GetProductList 获取商品列表 func (r *ProductApi) GetProductList(c *gin.Context) { var req systemReq.GetProductListRequest if err := c.ShouldBindQuery(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "商品列表请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.IDs) == 0 { ids, err := parseIds(c) if err != nil { systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) return } req.IDs = ids } result, err := productService.GetProductList(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "商品列表异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // GetDistributionProductList 获取分销商品列表 func (r *ProductApi) GetDistributionProductList(c *gin.Context) { var req systemReq.GetDistributionProductListRequest if err := c.ShouldBindQuery(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "分销商品列表请求参数异常", "参数错误: "+err.Error(), c, err) return } // Name支持双重编码:如果绑定后仍含%编码字符,再解码一次 if req.Name != "" && strings.Contains(req.Name, "%") { if decoded, err := url.QueryUnescape(req.Name); err == nil { req.Name = decoded } } // StockOperator支持双重编码 if req.StockOperator != "" && strings.Contains(req.StockOperator, "%") { if decoded, err := url.QueryUnescape(req.StockOperator); err == nil { req.StockOperator = decoded } } // SalePriceOperator支持双重编码 if req.SalePriceOperator != "" && strings.Contains(req.SalePriceOperator, "%") { if decoded, err := url.QueryUnescape(req.SalePriceOperator); err == nil { req.SalePriceOperator = decoded } } result, err := productService.GetDistributionProductList(req) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "分销商品列表异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // GetProductDetail 获取商品详情 func (r *ProductApi) GetProductDetail(c *gin.Context) { var req systemReq.GetProductDetailRequest if err := c.ShouldBindQuery(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "商品详情请求参数异常", "参数错误: "+err.Error(), c, err) return } result, err := productService.GetProductDetail(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "商品详情异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // GetProductFullInfo 获取商品完整信息(无需签名认证) - 从租户分库查询 func (r *ProductApi) GetProductFullInfo(c *gin.Context) { var req systemReq.GetProductFullInfoRequest fmt.Printf("========== 【接口入口】获取商品完整信息 ==========\n") if err := c.ShouldBindQuery(&req); err != nil { fmt.Printf("【参数验证失败】%v\n", err) utils.InfoLog(constant.LoggerChannelRequest, map[string]interface{}{ "action": "获取商品完整信息参数验证失败", "error": err.Error(), }) systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) return } fmt.Printf("【参数验证通过】UserID(租户): %d, ProductID: %d\n", req.UserID, req.ProductID) result, err := productService.GetProductFullInfo(req, database.GetDB(c)) if err != nil { fmt.Printf("【查询失败】%v\n", err) utils.FailWithRequestLog(constant.LoggerChannelWork, "获取商品完整信息异常", err, c, req) return } fmt.Printf("【查询成功】返回商品完整信息\n") fmt.Printf("========== 【接口完成】 ==========\n") c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // SaveProduct 保存商品(添加或修改) func (r *ProductApi) SaveProduct(c *gin.Context) { var req systemReq.ProductRequest fmt.Printf("【断点1】接收到保存商品请求\n") if err := c.ShouldBind(&req); err != nil { fmt.Printf("【断点2】参数绑定失败: %v\n", err) ValidAndFail(constant.LoggerChannelRequest, "保存商品请求参数异常", "参数错误: "+err.Error(), c, err) return } fmt.Printf("【断点3】参数绑定成功, 商品信息: %+v\n", req) if len(req.LiveImage) == 0 { image, err := parseImageFromForm(c) if err != nil { systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) return } req.LiveImage = image } id, err := productService.SaveProduct(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "保存商品异常", err, c, req) return } systemRes.OkWithDetailed(gin.H{"id": id}, "操作成功", c) } // UpdatePrice 修改商品售价 func (r *ProductApi) UpdatePrice(c *gin.Context) { var req systemReq.UpdatePriceRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "修改售价请求参数异常", "参数错误: "+err.Error(), c, err) return } if err := productService.UpdatePrice(req); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "修改售价异常", err, c, req) return } systemRes.OkWithMessage("售价修改成功", c) } // DeleteProduct 删除商品 func (r *ProductApi) DeleteProduct(c *gin.Context) { var req systemReq.DeleteProductRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "删除商品请求参数异常", "参数错误: "+err.Error(), c, err) return } err := productService.DeleteProduct(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "删除商品异常", err, c, req) return } systemRes.OkWithMessage("删除成功", c) } // GetProductInventory 获取商品库存数量 func (r *ProductApi) GetProductInventory(c *gin.Context) { var req systemReq.GetProductInventoryRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "获取商品库存请求参数异常", "参数错误: "+err.Error(), c, err) return } result, err := productService.GetProductInventory(req) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "获取商品库存异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // RetryOutTask 重试失败的外部任务 func (r *ProductApi) RetryOutTask(c *gin.Context) { var req systemReq.RetryOutTaskRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "重试任务请求参数异常", "参数错误: "+err.Error(), c, err) return } if err := productService.RetryOutTask(req, database.GetDB(c)); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "重试任务异常", err, c, req) return } systemRes.OkWithMessage("重试任务已提交", c) } // ExportProducts 导出商品到Excel func (r *ProductApi) ExportProducts(c *gin.Context) { var req systemReq.ExportProductRequest if err := c.ShouldBindQuery(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "导出商品请求参数异常", "参数错误: "+err.Error(), c, err) return } result, err := productService.ExportProducts(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "导出商品异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // PushProductToShop 上架到商铺:创建商品、库存记录,并推送到商铺 func (r *ProductApi) PushProductToShop(c *gin.Context) { var req systemReq.PushProductToShopRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "上架到商铺请求参数异常", "参数错误: "+err.Error(), c, err) return } // 处理photos参数: 如果表单未绑定,尝试从原始表单解析 if len(req.Photos) == 0 { var photos []string for i := 0; i < 20; i++ { photoStr := c.PostForm(fmt.Sprintf("photos[%d]", i)) if photoStr == "" { break } photos = append(photos, photoStr) } req.Photos = photos } result, err := productService.PushProductToShop(req) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "上架到商铺异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // BatchPushProducts 批量推送商品到多个店铺 func (r *ProductApi) BatchPushProducts(c *gin.Context) { var req systemReq.BatchPushProductRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "批量推送请求参数异常", "参数错误: "+err.Error(), c, err) return } if err := productService.BatchPushProducts(req); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "批量推送异常", err, c, req) return } systemRes.OkWithMessage("批量推送完成", c) } /*func (r *ProductApi) BatchPushProducts(c *gin.Context) { var req systemReq.BatchPushProductRequest if err := c.ShouldBind(&req); err != nil { utils.InfoLog(constant.LoggerChannelRequest, map[string]interface{}{ "action": "批量推送参数验证失败", "error": err.Error(), }) ValidAndFail(constant.LoggerChannelRequest, "批量推送请求参数异常", "参数错误: "+err.Error(), c, err) return } utils.InfoLog(constant.LoggerChannelWork, map[string]interface{}{ "action": "开始批量推送商品", "shop_ids": req.ShopIDs, "product_id": req.ProductID, }) if err := productService.BatchPushProducts(req); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "批量推送异常", err, c, req) return } utils.InfoLog(constant.LoggerChannelWork, map[string]interface{}{ "action": "批量推送完成", "product_id": req.ProductID, "success": true, }) systemRes.OkWithMessage("批量推送完成", c)*/ // GetProductLogList 商品日志列表 func (r *ProductApi) GetProductLogList(c *gin.Context) { var req systemReq.GetProductLogListRequest if err := c.ShouldBindQuery(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "商品日志列表请求参数异常", "参数错误: "+err.Error(), c, err) return } userInfo := utils.GetUserInfo(c) result, err := productService.GetProductLogList(req, userInfo.AboutID) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "商品日志列表异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // SaveProductLog 保存商品日志(添加或修改) func (r *ProductApi) SaveProductLog(c *gin.Context) { var req systemReq.ProductLogRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "保存商品日志请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.OldLiveImage) == 0 { image, err := parseOldImageFromForm(c) if err != nil { systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) return } req.OldLiveImage = image } if len(req.NewLiveImage) == 0 { image, err := parseNewImageFromForm(c) if err != nil { systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) return } req.NewLiveImage = image } userInfo := utils.GetUserInfo(c) id, err := productService.SaveProductLog(req, userInfo.ID) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "保存商品日志异常", err, c, req) return } systemRes.OkWithDetailed(gin.H{"id": id}, "操作成功", c) } // AuditProductLog 审核商品日志 func (r *ProductApi) AuditProductLog(c *gin.Context) { var req systemReq.AuditProductLogRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "审核商品日志请求参数异常", "参数错误: "+err.Error(), c, err) return } if len(req.NewLiveImage) == 0 { image, err := parseNewImageFromForm(c) if err != nil { systemRes.FailWithValidateMessage("参数错误: "+err.Error(), c) return } req.NewLiveImage = image } userInfo := utils.GetUserInfo(c) if err := productService.AuditProductLog(req, userInfo.ID); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "审核商品日志异常", err, c, req) return } systemRes.OkWithMessage("审核成功", c) } // DeleteProductLog 删除商品日志 func (r *ProductApi) DeleteProductLog(c *gin.Context) { var req systemReq.DeleteProductLogRequest if err := c.ShouldBind(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "删除商品日志请求参数异常", "参数错误: "+err.Error(), c, err) return } if err := productService.DeleteProductLog(req); err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "删除商品日志异常", err, c, req) return } systemRes.OkWithMessage("删除成功", c) } // GetShopProductDetail 获取店铺商品详情(包含商品数据和发送记录) func (r *ProductApi) GetShopProductDetail(c *gin.Context) { var req systemReq.GetShopProductDetailRequest if err := c.ShouldBindQuery(&req); err != nil { ValidAndFail(constant.LoggerChannelRequest, "获取店铺商品详情请求参数异常", "参数错误: "+err.Error(), c, err) return } result, err := productService.GetShopProductDetail(req, database.GetDB(c)) if err != nil { utils.FailWithRequestLog(constant.LoggerChannelWork, "获取店铺商品详情异常", err, c, req) return } c.JSON(http.StatusOK, gin.H{ "code": 200, "data": result, }) } // parseIds 解析ID列表 func parseIds(c *gin.Context) ([]int64, error) { var ids []int64 for i := 0; i < 100; i++ { idStr := c.Query(fmt.Sprintf("ids[%d]", i)) if idStr == "" { break } var id int64 if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil { return nil, fmt.Errorf("第%d个ID格式错误", i) } if id <= 0 { return nil, fmt.Errorf("第%d个ID必须大于0", i) } ids = append(ids, id) } return ids, nil } // parseImageFromForm 解析图片列表 func parseImageFromForm(c *gin.Context) ([]string, error) { var images []string for i := 0; i < 10; i++ { imageStr := c.PostForm(fmt.Sprintf("live_image[%d]", i)) if imageStr == "" { break } images = append(images, imageStr) } return images, nil } // parseOldImageFromForm 解析图片列表 func parseOldImageFromForm(c *gin.Context) ([]string, error) { var images []string for i := 0; i < 10; i++ { imageStr := c.PostForm(fmt.Sprintf("old_live_image[%d]", i)) if imageStr == "" { break } images = append(images, imageStr) } return images, nil } // parseNewImageFromForm 解析图片列表 func parseNewImageFromForm(c *gin.Context) ([]string, error) { var images []string for i := 0; i < 10; i++ { imageStr := c.PostForm(fmt.Sprintf("new_live_image[%d]", i)) if imageStr == "" { break } images = append(images, imageStr) } return images, nil }