Files
ai_dianshang/server/internal/service/product.go
2025-11-17 14:11:46 +08:00

869 lines
23 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"dianshang/internal/model"
"dianshang/internal/repository"
"dianshang/pkg/utils"
"errors"
"fmt"
"strconv"
"time"
)
// ProductService 产品服务
type ProductService struct {
productRepo *repository.ProductRepository
userRepo *repository.UserRepository
}
// NewProductService 创建产品服务
func NewProductService(productRepo *repository.ProductRepository, userRepo *repository.UserRepository) *ProductService {
return &ProductService{
productRepo: productRepo,
userRepo: userRepo,
}
}
// GetProductList 获取产品列表(前端用户)
func (s *ProductService) GetProductList(page, pageSize int, categoryID uint, keyword string, minPrice, maxPrice float64, sort, sortType string) ([]model.Product, *utils.Pagination, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
offset := (page - 1) * pageSize
conditions := make(map[string]interface{})
if categoryID > 0 {
conditions["category_id"] = categoryID
}
if keyword != "" {
conditions["keyword"] = keyword
}
if minPrice > 0 {
conditions["min_price"] = minPrice
}
if maxPrice > 0 {
conditions["max_price"] = maxPrice
}
if sort != "" {
conditions["sort"] = sort
}
if sortType != "" {
conditions["sort_type"] = sortType
}
products, total, err := s.productRepo.GetList(offset, pageSize, conditions)
if err != nil {
return nil, nil, err
}
pagination := utils.NewPagination(page, pageSize)
pagination.Total = total
return products, pagination, nil
}
// GetProductListForAdmin 获取产品列表(管理系统)
func (s *ProductService) GetProductListForAdmin(page, pageSize int, categoryID uint, keyword string, minPrice, maxPrice float64, sort, sortType, status, isHot, isNew, isRecommend string) ([]model.Product, *utils.Pagination, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
offset := (page - 1) * pageSize
conditions := make(map[string]interface{})
if categoryID > 0 {
conditions["category_id"] = categoryID
}
if keyword != "" {
conditions["keyword"] = keyword
}
if minPrice > 0 {
conditions["min_price"] = minPrice
}
if maxPrice > 0 {
conditions["max_price"] = maxPrice
}
if sort != "" {
conditions["sort"] = sort
}
if sortType != "" {
conditions["sort_type"] = sortType
}
// 添加状态条件,支持获取所有状态的商品
if status != "" {
conditions["status"] = status
}
// 添加热门、新品、推荐筛选条件
if isHot != "" {
conditions["is_hot"] = isHot
}
if isNew != "" {
conditions["is_new"] = isNew
}
if isRecommend != "" {
conditions["is_recommend"] = isRecommend
}
products, total, err := s.productRepo.GetList(offset, pageSize, conditions)
if err != nil {
return nil, nil, err
}
pagination := utils.NewPagination(page, pageSize)
pagination.Total = total
return products, pagination, nil
}
// GetProductDetail 获取产品详情
func (s *ProductService) GetProductDetail(id uint) (*model.Product, error) {
return s.productRepo.GetByID(id)
}
// CreateProduct 创建产品
func (s *ProductService) CreateProduct(product *model.Product) error {
// 验证分类是否存在
if product.CategoryID > 0 {
_, err := s.productRepo.GetCategoryByID(product.CategoryID)
if err != nil {
return errors.New("分类不存在")
}
}
return s.productRepo.Create(product)
}
// UpdateProduct 更新产品
func (s *ProductService) UpdateProduct(id uint, updates map[string]interface{}) error {
// 检查产品是否存在
_, err := s.productRepo.GetByID(id)
if err != nil {
return errors.New("产品不存在")
}
// 如果更新分类,验证分类是否存在
if categoryID, ok := updates["category_id"]; ok {
var catID uint
switch v := categoryID.(type) {
case uint:
catID = v
case float64:
catID = uint(v)
case int:
catID = uint(v)
default:
return errors.New("分类ID格式错误")
}
if catID > 0 {
_, err := s.productRepo.GetCategoryByID(catID)
if err != nil {
return errors.New("分类不存在")
}
}
}
// 处理 detail_images 字段 - 确保正确转换为 JSONSlice 类型
if detailImages, ok := updates["detail_images"]; ok {
switch v := detailImages.(type) {
case []interface{}:
// 将 []interface{} 转换为 []string
var stringSlice []string
for _, item := range v {
if str, ok := item.(string); ok {
stringSlice = append(stringSlice, str)
}
}
updates["detail_images"] = model.JSONSlice(stringSlice)
case []string:
updates["detail_images"] = model.JSONSlice(v)
}
}
// 处理 images 字段 - 确保正确转换为 JSONSlice 类型
if images, ok := updates["images"]; ok {
switch v := images.(type) {
case []interface{}:
// 将 []interface{} 转换为 []string
var stringSlice []string
for _, item := range v {
if str, ok := item.(string); ok {
stringSlice = append(stringSlice, str)
}
}
updates["images"] = model.JSONSlice(stringSlice)
case []string:
updates["images"] = model.JSONSlice(v)
}
}
// 处理SKU数据
var skusData []interface{}
if skus, ok := updates["skus"]; ok {
skusData, _ = skus.([]interface{})
// 从updates中移除skus避免直接更新到Product表
delete(updates, "skus")
}
// 更新商品基本信息
if err := s.productRepo.Update(id, updates); err != nil {
return err
}
// 处理SKU数据
if len(skusData) > 0 {
if err := s.handleProductSKUs(id, skusData); err != nil {
return err
}
}
return nil
}
// handleProductSKUs 处理商品SKU数据
func (s *ProductService) handleProductSKUs(productID uint, skusData []interface{}) error {
// 获取当前商品的所有现有SKU
existingSKUs, err := s.productRepo.GetProductSKUs(productID)
if err != nil {
return err
}
// 收集前端发送的SKU ID列表
var submittedSKUIDs []uint
// 处理前端发送的SKU数据
for _, skuData := range skusData {
skuMap, ok := skuData.(map[string]interface{})
if !ok {
continue
}
// 创建SKU对象
sku := &model.ProductSKU{
ProductID: productID,
}
// 处理SKU字段
if skuCode, ok := skuMap["sku_code"].(string); ok {
sku.SKUCode = skuCode
}
if price, ok := skuMap["price"].(float64); ok {
sku.Price = price
}
if stock, ok := skuMap["stock"]; ok {
switch v := stock.(type) {
case float64:
sku.Stock = int(v)
case int:
sku.Stock = v
case string:
if stockInt, err := strconv.Atoi(v); err == nil {
sku.Stock = stockInt
}
}
}
// 处理spec_values
if specValues, ok := skuMap["spec_values"]; ok {
if specMap, ok := specValues.(map[string]interface{}); ok {
sku.SpecValues = model.JSONMap(specMap)
}
}
// 处理image字段
if image, ok := skuMap["image"].(string); ok {
sku.Image = image
}
// 检查是否是更新还是创建
var isUpdate bool
var skuIDValue uint
if skuID, ok := skuMap["id"]; ok && skuID != nil {
switch v := skuID.(type) {
case float64:
if v > 0 {
isUpdate = true
skuIDValue = uint(v)
submittedSKUIDs = append(submittedSKUIDs, skuIDValue)
}
case int:
if v > 0 {
isUpdate = true
skuIDValue = uint(v)
submittedSKUIDs = append(submittedSKUIDs, skuIDValue)
}
}
}
if isUpdate {
// 更新现有SKU
updates := make(map[string]interface{})
if sku.SKUCode != "" {
updates["sku_code"] = sku.SKUCode
}
updates["price"] = sku.Price
updates["stock"] = sku.Stock
// 直接传递JSONMap类型让GORM处理序列化
updates["spec_values"] = sku.SpecValues
// 添加image字段的更新
if sku.Image != "" {
updates["image"] = sku.Image
}
if err := s.productRepo.UpdateSKU(skuIDValue, updates); err != nil {
return err
}
} else {
// 创建新SKU - 确保不设置ID字段
sku.ID = 0 // 明确设置为0让数据库自动生成
if sku.SKUCode == "" {
// 生成默认SKU代码
sku.SKUCode = fmt.Sprintf("SKU-%d-%d", productID, time.Now().Unix())
}
if err := s.productRepo.CreateSKU(sku); err != nil {
return err
}
}
}
// 删除不在前端提交列表中的现有SKU
for _, existingSKU := range existingSKUs {
shouldDelete := true
for _, submittedID := range submittedSKUIDs {
if existingSKU.ID == submittedID {
shouldDelete = false
break
}
}
if shouldDelete {
if err := s.productRepo.DeleteSKU(existingSKU.ID); err != nil {
return fmt.Errorf("删除SKU失败 - SKU ID: %d, 错误: %v", existingSKU.ID, err)
}
}
}
// 处理完所有SKU后同步商品库存
if err := s.productRepo.SyncProductStockFromSKUs(productID); err != nil {
// 记录错误但不阻止操作
fmt.Printf("同步商品库存失败 - 商品ID: %d, 错误: %v\n", productID, err)
}
return nil
}
// DeleteProduct 删除产品
func (s *ProductService) DeleteProduct(id uint) error {
// 检查产品是否存在
_, err := s.productRepo.GetByID(id)
if err != nil {
return errors.New("产品不存在")
}
return s.productRepo.Delete(id)
}
// GetCategories 获取分类列表
func (s *ProductService) GetCategories() ([]model.Category, error) {
return s.productRepo.GetCategories()
}
// CreateCategory 创建分类
func (s *ProductService) CreateCategory(category *model.Category) error {
return s.productRepo.CreateCategory(category)
}
// UpdateCategory 更新分类
func (s *ProductService) UpdateCategory(id uint, updates map[string]interface{}) error {
// 检查分类是否存在
_, err := s.productRepo.GetCategoryByID(id)
if err != nil {
return errors.New("分类不存在")
}
return s.productRepo.UpdateCategory(id, updates)
}
// DeleteCategory 删除分类
func (s *ProductService) DeleteCategory(id uint) error {
// 检查分类是否存在
_, err := s.productRepo.GetCategoryByID(id)
if err != nil {
return errors.New("分类不存在")
}
// 检查分类下是否有商品
productCount, err := s.productRepo.CountProductsByCategory(id)
if err != nil {
return errors.New("检查分类商品数量失败")
}
if productCount > 0 {
return errors.New("该分类下还有商品,无法删除")
}
// 检查是否有子分类
var childCategories []model.Category
err = s.productRepo.GetDB().Where("parent_id = ?", id).Find(&childCategories).Error
if err != nil {
return errors.New("检查子分类失败")
}
if len(childCategories) > 0 {
return errors.New("该分类下还有子分类,请先删除子分类")
}
return s.productRepo.DeleteCategory(id)
}
// GetProductReviews 获取产品评价列表
func (s *ProductService) GetProductReviews(productID uint, page, pageSize int) ([]model.ProductReview, *utils.Pagination, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
offset := (page - 1) * pageSize
reviews, total, err := s.productRepo.GetReviews(productID, offset, pageSize)
if err != nil {
return nil, nil, err
}
pagination := utils.NewPagination(page, pageSize)
pagination.Total = total
return reviews, pagination, nil
}
// CreateReview 创建评价
func (s *ProductService) CreateReview(userID uint, review *model.ProductReview) error {
// 检查用户是否存在
_, err := s.userRepo.GetByID(userID)
if err != nil {
return errors.New("用户不存在")
}
// 检查产品是否存在
_, err = s.productRepo.GetByID(review.ProductID)
if err != nil {
return errors.New("产品不存在")
}
// 检查是否已经评价过
if review.OrderID != nil {
existingReview, _ := s.productRepo.GetReviewByOrderID(userID, *review.OrderID)
if existingReview != nil {
return errors.New("已经评价过该商品")
}
}
review.UserID = userID
return s.productRepo.CreateReview(review)
}
// GetHotProducts 获取热门产品
func (s *ProductService) GetHotProducts(limit int) ([]model.Product, error) {
if limit <= 0 || limit > 50 {
limit = 10
}
return s.productRepo.GetHotProducts(limit)
}
// GetRecommendProducts 获取推荐产品
func (s *ProductService) GetRecommendProducts(limit int) ([]model.Product, error) {
if limit <= 0 || limit > 50 {
limit = 10
}
return s.productRepo.GetRecommendProducts(limit)
}
// SearchProducts 搜索产品(支持价格与排序)
func (s *ProductService) SearchProducts(keyword string, page, pageSize int, minPrice, maxPrice float64, sort, sortType string) ([]model.Product, *utils.Pagination, error) {
if keyword == "" {
return []model.Product{}, utils.NewPagination(page, pageSize), nil
}
return s.GetProductList(page, pageSize, 0, keyword, minPrice, maxPrice, sort, sortType)
}
// UpdateStock 更新库存
func (s *ProductService) UpdateStock(id uint, quantity int) error {
// 检查产品是否存在
product, err := s.productRepo.GetByID(id)
if err != nil {
return errors.New("产品不存在")
}
// 检查库存是否足够(减库存时)
if quantity < 0 && product.Stock < -quantity {
return errors.New("库存不足")
}
return s.productRepo.UpdateStock(id, quantity)
}
// GetProductSKUs 获取产品SKU列表
func (s *ProductService) GetProductSKUs(productID uint) ([]model.ProductSKU, error) {
return s.productRepo.GetProductSKUs(productID)
}
// GetSKUByID 根据SKU ID获取SKU详情
func (s *ProductService) GetSKUByID(skuID uint) (*model.ProductSKU, error) {
return s.productRepo.GetSKUByID(skuID)
}
// GetProductTags 获取产品标签列表
func (s *ProductService) GetProductTags() ([]model.ProductTag, error) {
return s.productRepo.GetProductTags()
}
// GetStores 获取店铺列表
func (s *ProductService) GetStores() ([]model.Store, error) {
return s.productRepo.GetStores()
}
// GetStoreByID 根据ID获取店铺信息
func (s *ProductService) GetStoreByID(id uint) (*model.Store, error) {
return s.productRepo.GetStoreByID(id)
}
// GetProductReviewCount 获取产品评价统计
func (s *ProductService) GetProductReviewCount(productID uint) (map[string]interface{}, error) {
// 检查产品是否存在
_, err := s.productRepo.GetByID(productID)
if err != nil {
return nil, errors.New("产品不存在")
}
return s.productRepo.GetReviewCount(productID)
}
// GetProductStatistics 获取产品统计
func (s *ProductService) GetProductStatistics() (map[string]interface{}, error) {
// 使用ProductRepository的GetProductStatistics方法
return s.productRepo.GetProductStatistics()
}
// GetProductSalesRanking 获取产品销售排行
func (s *ProductService) GetProductSalesRanking(startDate, endDate, limit string) ([]map[string]interface{}, error) {
// 简化实现,返回基础排行数据
var results []map[string]interface{}
// 这里应该根据订单数据统计产品销量,暂时返回模拟数据
products, _, err := s.productRepo.GetList(0, 10, map[string]interface{}{"status": 1})
if err != nil {
return nil, err
}
for i, product := range products {
if i >= 10 { // 限制返回数量
break
}
results = append(results, map[string]interface{}{
"product_id": product.ID,
"product_name": product.Name,
"sales_count": 100 - i*5, // 模拟销量数据
"sales_amount": float64(1000 - i*50),
})
}
return results, nil
}
// GetCategorySalesRanking 获取分类销售排行
func (s *ProductService) GetCategorySalesRanking(startDate, endDate, limit string) ([]map[string]interface{}, error) {
// 解析limit参数
limitInt := 10 // 默认值
if limit != "" {
if parsedLimit, err := strconv.Atoi(limit); err == nil && parsedLimit > 0 {
limitInt = parsedLimit
}
}
// 如果没有提供日期范围使用最近30天
if startDate == "" || endDate == "" {
now := time.Now()
endDate = now.Format("2006-01-02")
startDate = now.AddDate(0, 0, -30).Format("2006-01-02")
}
// 使用真实的数据库查询
return s.productRepo.GetCategorySalesStatistics(startDate, endDate, limitInt)
}
// BatchUpdateProductStatus 批量更新商品状态
func (s *ProductService) BatchUpdateProductStatus(ids []uint, status int) error {
if len(ids) == 0 {
return errors.New("商品ID列表不能为空")
}
return s.productRepo.BatchUpdateStatus(ids, status)
}
// BatchUpdateProductPrice 批量更新商品价格
func (s *ProductService) BatchUpdateProductPrice(updates []map[string]interface{}) error {
if len(updates) == 0 {
return errors.New("更新数据不能为空")
}
return s.productRepo.BatchUpdatePrice(updates)
}
// BatchDeleteProducts 批量删除商品
func (s *ProductService) BatchDeleteProducts(ids []uint) error {
if len(ids) == 0 {
return errors.New("商品ID列表不能为空")
}
return s.productRepo.BatchDelete(ids)
}
// CreateProductSKU 创建商品SKU
func (s *ProductService) CreateProductSKU(sku *model.ProductSKU) error {
// 验证商品是否存在
_, err := s.productRepo.GetByID(sku.ProductID)
if err != nil {
return errors.New("商品不存在")
}
return s.productRepo.CreateSKU(sku)
}
// UpdateProductSKU 更新商品SKU
func (s *ProductService) UpdateProductSKU(id uint, updates map[string]interface{}) error {
// 检查SKU是否存在
_, err := s.productRepo.GetSKUByID(id)
if err != nil {
return errors.New("SKU不存在")
}
return s.productRepo.UpdateSKU(id, updates)
}
// DeleteProductSKU 删除商品SKU
func (s *ProductService) DeleteProductSKU(id uint) error {
// 检查SKU是否存在包括已软删除的
var sku model.ProductSKU
err := s.productRepo.GetDB().Where("id = ?", id).First(&sku).Error
if err != nil {
return errors.New("SKU不存在")
}
// 如果SKU已经被软删除直接返回成功
if sku.Status == 0 {
fmt.Printf("SKU ID %d 已经被软删除,无需重复操作\n", id)
return nil
}
// 检查SKU是否被订单引用
var count int64
err = s.productRepo.GetDB().Table("order_items").Where("sk_uid = ?", id).Count(&count).Error
if err != nil {
return fmt.Errorf("检查SKU引用关系失败: %v", err)
}
if count > 0 {
// 如果被订单引用,执行软删除
err = s.productRepo.DeleteSKU(id)
if err != nil {
return fmt.Errorf("删除SKU失败: %v", err)
}
// 软删除成功,记录日志但不返回错误
fmt.Printf("SKU ID %d 已被 %d 个订单引用,已执行软删除(设置为不可用状态)\n", id, count)
return nil
}
// 如果没有被引用,执行硬删除
err = s.productRepo.DeleteSKU(id)
if err != nil {
return fmt.Errorf("删除SKU失败: %v", err)
}
// 同步更新商品库存
if err := s.productRepo.SyncProductStockFromSKUs(sku.ProductID); err != nil {
// 记录错误但不阻止删除操作
fmt.Printf("同步商品库存失败 - 商品ID: %d, 错误: %v\n", sku.ProductID, err)
}
return nil
}
// GetProductImages 获取商品图片列表
func (s *ProductService) GetProductImages(productID uint) ([]model.ProductImage, error) {
return s.productRepo.GetProductImages(productID)
}
// CreateProductImage 创建商品图片
func (s *ProductService) CreateProductImage(image *model.ProductImage) error {
// 验证商品是否存在
_, err := s.productRepo.GetByID(image.ProductID)
if err != nil {
return errors.New("商品不存在")
}
return s.productRepo.CreateProductImage(image)
}
// UpdateProductImageSort 更新商品图片排序
func (s *ProductService) UpdateProductImageSort(id uint, sort int) error {
return s.productRepo.UpdateProductImageSort(id, sort)
}
// DeleteProductImage 删除商品图片
func (s *ProductService) DeleteProductImage(id uint) error {
return s.productRepo.DeleteProductImage(id)
}
// CreateProductSpec 创建商品规格
func (s *ProductService) CreateProductSpec(spec *model.ProductSpec) error {
// 验证商品是否存在
_, err := s.productRepo.GetByID(spec.ProductID)
if err != nil {
return errors.New("商品不存在")
}
return s.productRepo.CreateProductSpec(spec)
}
// UpdateProductSpec 更新商品规格
func (s *ProductService) UpdateProductSpec(id uint, updates map[string]interface{}) error {
return s.productRepo.UpdateProductSpec(id, updates)
}
// DeleteProductSpec 删除商品规格
func (s *ProductService) DeleteProductSpec(id uint) error {
return s.productRepo.DeleteProductSpec(id)
}
// GetProductSpecs 获取商品规格列表
func (s *ProductService) GetProductSpecs(productID uint) ([]model.ProductSpec, error) {
return s.productRepo.GetProductSpecs(productID)
}
// CreateProductTag 创建商品标签
func (s *ProductService) CreateProductTag(tag *model.ProductTag) error {
return s.productRepo.CreateProductTag(tag)
}
// UpdateProductTag 更新商品标签
func (s *ProductService) UpdateProductTag(id uint, updates map[string]interface{}) error {
return s.productRepo.UpdateProductTag(id, updates)
}
// DeleteProductTag 删除商品标签
func (s *ProductService) DeleteProductTag(id uint) error {
return s.productRepo.DeleteProductTag(id)
}
// AssignTagsToProduct 为商品分配标签
func (s *ProductService) AssignTagsToProduct(productID uint, tagIDs []uint) error {
// 验证商品是否存在
_, err := s.productRepo.GetByID(productID)
if err != nil {
return errors.New("商品不存在")
}
return s.productRepo.AssignTagsToProduct(productID, tagIDs)
}
// GetLowStockProducts 获取低库存商品
func (s *ProductService) GetLowStockProducts(threshold int) ([]model.Product, error) {
if threshold <= 0 {
threshold = 10 // 默认阈值
}
return s.productRepo.GetLowStockProducts(threshold)
}
// GetInventoryStatistics 获取库存统计
func (s *ProductService) GetInventoryStatistics() (map[string]interface{}, error) {
return s.productRepo.GetInventoryStatistics()
}
// ExportProducts 导出商品数据
func (s *ProductService) ExportProducts(conditions map[string]interface{}) ([]model.Product, error) {
return s.productRepo.GetProductsForExport(conditions)
}
// ImportProducts 导入商品数据
func (s *ProductService) ImportProducts(products []model.Product) (map[string]interface{}, error) {
successCount := 0
failCount := 0
var errors []string
for _, product := range products {
// 验证商品数据
if product.Name == "" {
errors = append(errors, "商品名称不能为空")
failCount++
continue
}
if product.Price <= 0 {
errors = append(errors, "商品价格必须大于0")
failCount++
continue
}
// 创建商品
err := s.productRepo.Create(&product)
if err != nil {
errors = append(errors, err.Error())
failCount++
} else {
successCount++
}
}
return map[string]interface{}{
"success_count": successCount,
"fail_count": failCount,
"errors": errors,
}, nil
}
// SyncProductStock 同步商品库存根据SKU库存计算
func (s *ProductService) SyncProductStock(productID uint) error {
return s.productRepo.SyncProductStockFromSKUs(productID)
}
// SyncAllProductsStock 同步所有商品库存
func (s *ProductService) SyncAllProductsStock() error {
// 获取所有有SKU的商品
products, _, err := s.productRepo.GetList(0, 0, map[string]interface{}{})
if err != nil {
return err
}
var syncErrors []string
for _, product := range products {
// 检查商品是否有SKU
skus, err := s.productRepo.GetProductSKUs(product.ID)
if err != nil {
continue
}
if len(skus) > 0 {
// 如果有SKU同步库存
err = s.productRepo.SyncProductStockFromSKUs(product.ID)
if err != nil {
syncErrors = append(syncErrors, fmt.Sprintf("商品ID %d 同步失败: %v", product.ID, err))
}
}
}
if len(syncErrors) > 0 {
return fmt.Errorf("部分商品同步失败: %v", syncErrors)
}
return nil
}