465 lines
12 KiB
Go
465 lines
12 KiB
Go
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{}
|
||
|
||
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,
|
||
})
|
||
}
|
||
|
||
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,
|
||
})
|
||
}
|
||
|
||
// SaveProduct 保存商品(添加或修改)
|
||
func (r *ProductApi) SaveProduct(c *gin.Context) {
|
||
var req systemReq.ProductRequest
|
||
if err := c.ShouldBind(&req); err != nil {
|
||
ValidAndFail(constant.LoggerChannelRequest, "保存商品请求参数异常", "参数错误: "+err.Error(), c, err)
|
||
return
|
||
}
|
||
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) 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)
|
||
}
|
||
|
||
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)
|
||
}
|
||
|
||
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,
|
||
})
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
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
|
||
}
|