This commit is contained in:
sjk
2025-11-28 15:18:10 +08:00
parent ad4a600af9
commit 5683f35942
188 changed files with 53680 additions and 1062 deletions

View File

@@ -202,6 +202,22 @@ func (r *CommentRepository) CreateReply(reply *model.CommentReply) error {
return tx.Commit().Error
}
// GetHighRatingComments 获取高分评论(用于首页展示)
func (r *CommentRepository) GetHighRatingComments(limit int, minRating int) ([]model.Comment, error) {
var comments []model.Comment
// 获取评分>=minRating的评论按评分降序、创建时间降序排列
err := r.db.Model(&model.Comment{}).
Where("status = ? AND rating >= ?", 1, minRating).
Preload("User").
Preload("Product").
Order("rating DESC, created_at DESC").
Limit(limit).
Find(&comments).Error
return comments, err
}
// GetReplies 获取评论回复列表
func (r *CommentRepository) GetReplies(commentID uint) ([]model.CommentReply, error) {
var replies []model.CommentReply

View File

@@ -1,6 +1,7 @@
package repository
import (
"context"
"dianshang/internal/model"
"time"
@@ -121,3 +122,286 @@ func (r *CouponRepository) RestoreCoupon(userCouponID uint) error {
"used_time": nil, // 清除使用时间
}).Error
}
// ==================== 管理端方法 ====================
// GetCouponListForAdmin 获取优惠券列表(管理端)
func (r *CouponRepository) GetCouponListForAdmin(page, pageSize int, status, couponType, keyword string) ([]model.Coupon, int64, error) {
var coupons []model.Coupon
var total int64
query := r.db.Model(&model.Coupon{})
// 状态筛选
if status != "" {
query = query.Where("status = ?", status)
}
// 类型筛选
if couponType != "" {
query = query.Where("type = ?", couponType)
}
// 关键词搜索
if keyword != "" {
query = query.Where("name LIKE ? OR description LIKE ?", "%"+keyword+"%", "%"+keyword+"%")
}
// 获取总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询
offset := (page - 1) * pageSize
err := query.Order("created_at DESC").
Limit(pageSize).
Offset(offset).
Find(&coupons).Error
return coupons, total, err
}
// GetCouponUsageStats 获取优惠券使用统计
func (r *CouponRepository) GetCouponUsageStats(couponID uint) (int, int, error) {
// 获取领取数
var receivedCount int64
err := r.db.Model(&model.UserCoupon{}).Where("coupon_id = ?", couponID).Count(&receivedCount).Error
if err != nil {
return 0, 0, err
}
// 获取使用数
var usedCount int64
err = r.db.Model(&model.UserCoupon{}).Where("coupon_id = ? AND status = ?", couponID, 1).Count(&usedCount).Error
if err != nil {
return 0, 0, err
}
return int(receivedCount), int(usedCount), nil
}
// Create 创建优惠券
func (r *CouponRepository) Create(coupon *model.Coupon) error {
return r.db.Create(coupon).Error
}
// Update 更新优惠券
func (r *CouponRepository) Update(couponID uint, updates map[string]interface{}) error {
return r.db.Model(&model.Coupon{}).Where("id = ?", couponID).Updates(updates).Error
}
// Delete 删除优惠券
func (r *CouponRepository) Delete(couponID uint) error {
return r.db.Delete(&model.Coupon{}, couponID).Error
}
// CheckCouponHasUsers 检查优惠券是否有用户领取
func (r *CouponRepository) CheckCouponHasUsers(couponID uint) (bool, error) {
var count int64
err := r.db.Model(&model.UserCoupon{}).Where("coupon_id = ?", couponID).Count(&count).Error
return count > 0, err
}
// BatchDelete 批量删除优惠券
func (r *CouponRepository) BatchDelete(couponIDs []uint) error {
return r.db.Delete(&model.Coupon{}, couponIDs).Error
}
// CountTotalCoupons 统计总优惠券数
func (r *CouponRepository) CountTotalCoupons(ctx context.Context) (int64, error) {
var count int64
err := r.db.WithContext(ctx).Model(&model.Coupon{}).Count(&count).Error
return count, err
}
// CountActiveCoupons 统计启用的优惠券数
func (r *CouponRepository) CountActiveCoupons(ctx context.Context) (int64, error) {
var count int64
now := time.Now()
err := r.db.WithContext(ctx).Model(&model.Coupon{}).
Where("status = ? AND start_time <= ? AND end_time >= ?", 1, now, now).
Count(&count).Error
return count, err
}
// CountTotalReceived 统计总领取数
func (r *CouponRepository) CountTotalReceived(ctx context.Context, startTime, endTime time.Time) (int64, error) {
var count int64
query := r.db.WithContext(ctx).Model(&model.UserCoupon{})
if !startTime.IsZero() && !endTime.IsZero() {
query = query.Where("created_at BETWEEN ? AND ?", startTime, endTime)
}
err := query.Count(&count).Error
return count, err
}
// CountTotalUsed 统计总使用数
func (r *CouponRepository) CountTotalUsed(ctx context.Context, startTime, endTime time.Time) (int64, error) {
var count int64
query := r.db.WithContext(ctx).Model(&model.UserCoupon{}).Where("status = ?", 1)
if !startTime.IsZero() && !endTime.IsZero() {
query = query.Where("used_time BETWEEN ? AND ?", startTime, endTime)
}
err := query.Count(&count).Error
return count, err
}
// GetCouponTypeStats 获取各类型优惠券统计
func (r *CouponRepository) GetCouponTypeStats(ctx context.Context) ([]map[string]interface{}, error) {
var results []map[string]interface{}
rows, err := r.db.WithContext(ctx).Model(&model.Coupon{}).
Select("type, COUNT(*) as count").
Group("type").
Rows()
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var couponType, count int
if err := rows.Scan(&couponType, &count); err != nil {
return nil, err
}
typeName := ""
switch couponType {
case 1:
typeName = "满减券"
case 2:
typeName = "折扣券"
case 3:
typeName = "免邮券"
}
results = append(results, map[string]interface{}{
"type": couponType,
"type_name": typeName,
"count": count,
})
}
return results, nil
}
// GetTopCoupons 获取热门优惠券
func (r *CouponRepository) GetTopCoupons(ctx context.Context, limit int) ([]map[string]interface{}, error) {
var results []map[string]interface{}
err := r.db.WithContext(ctx).Model(&model.Coupon{}).
Select("id, name, type, received_count, used_count").
Order("received_count DESC").
Limit(limit).
Scan(&results).Error
return results, err
}
// GetUserCouponListForAdmin 获取用户优惠券列表(管理端)
func (r *CouponRepository) GetUserCouponListForAdmin(userID uint, page, pageSize int) ([]model.UserCoupon, int64, error) {
var userCoupons []model.UserCoupon
var total int64
query := r.db.Model(&model.UserCoupon{}).Where("user_id = ?", userID)
// 获取总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询
offset := (page - 1) * pageSize
err := query.Preload("Coupon").
Order("created_at DESC").
Limit(pageSize).
Offset(offset).
Find(&userCoupons).Error
return userCoupons, total, err
}
// GetDistributeHistory 获取优惠券发放历史
func (r *CouponRepository) GetDistributeHistory(page, pageSize int) ([]map[string]interface{}, int64, error) {
var history []map[string]interface{}
var total int64
// 先获取总数
type CountResult struct {
Count int64
}
var countResult CountResult
err := r.db.Model(&model.UserCoupon{}).
Select("COUNT(DISTINCT DATE(created_at), coupon_id) as count").
Scan(&countResult).Error
if err != nil {
return nil, 0, err
}
total = countResult.Count
// 分页查询分组数据
type DistributeRecord struct {
DistributeDate string `gorm:"column:distribute_date"`
CouponID uint `gorm:"column:coupon_id"`
TotalCount int `gorm:"column:total_count"`
UnusedCount int `gorm:"column:unused_count"`
UsedCount int `gorm:"column:used_count"`
CreatedAt time.Time `gorm:"column:created_at"`
}
var records []DistributeRecord
offset := (page - 1) * pageSize
err = r.db.Model(&model.UserCoupon{}).
Select(`
DATE(created_at) as distribute_date,
coupon_id,
COUNT(*) as total_count,
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as unused_count,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as used_count,
MIN(created_at) as created_at
`).
Group("DATE(created_at), coupon_id").
Order("created_at DESC").
Limit(pageSize).
Offset(offset).
Scan(&records).Error
if err != nil {
return nil, 0, err
}
// 处理结果
for i, record := range records {
// 获取优惠券信息
var coupon model.Coupon
r.db.First(&coupon, record.CouponID)
// 判断发放类型
distributeType := "batch"
if record.TotalCount == 1 {
distributeType = "single"
}
history = append(history, map[string]interface{}{
"id": i + 1 + offset,
"coupon_id": record.CouponID,
"coupon_name": coupon.Name,
"distribute_type": distributeType,
"distribute_date": record.DistributeDate,
"total_count": record.TotalCount,
"success_count": record.TotalCount,
"fail_count": 0,
"used_count": record.UsedCount,
"unused_count": record.UnusedCount,
"admin_name": "系统",
"created_at": record.CreatedAt,
})
}
return history, total, nil
}

View File

@@ -0,0 +1,124 @@
package repository
import (
"dianshang/internal/model"
"time"
"gorm.io/gorm"
)
type LiveStreamRepository interface {
GetList(page, pageSize int, title, platform string, status *int) ([]model.LiveStream, int64, error)
GetByID(id uint) (*model.LiveStream, error)
GetActiveLiveStreams() ([]model.LiveStream, error)
Create(stream *model.LiveStream) error
Update(id uint, stream *model.LiveStream) error
UpdateStatus(id uint, status int) error
Delete(id uint) error
BatchDelete(ids []uint) error
IncrementViewCount(id uint) error
}
type liveStreamRepository struct {
db *gorm.DB
}
func NewLiveStreamRepository(db *gorm.DB) LiveStreamRepository {
return &liveStreamRepository{db: db}
}
// GetList 获取投流源列表
func (r *liveStreamRepository) GetList(page, pageSize int, title, platform string, status *int) ([]model.LiveStream, int64, error) {
var streams []model.LiveStream
var total int64
query := r.db.Model(&model.LiveStream{})
// 标题筛选
if title != "" {
query = query.Where("title LIKE ?", "%"+title+"%")
}
// 平台筛选
if platform != "" {
query = query.Where("platform = ?", platform)
}
// 状态筛选
if status != nil {
query = query.Where("status = ?", *status)
}
// 统计总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询,按排序和创建时间排序
offset := (page - 1) * pageSize
if err := query.Order("sort DESC, created_at DESC").
Limit(pageSize).
Offset(offset).
Find(&streams).Error; err != nil {
return nil, 0, err
}
return streams, total, nil
}
// GetByID 根据ID获取投流源详情
func (r *liveStreamRepository) GetByID(id uint) (*model.LiveStream, error) {
var stream model.LiveStream
if err := r.db.First(&stream, id).Error; err != nil {
return nil, err
}
return &stream, nil
}
// GetActiveLiveStreams 获取所有启用且在有效期内的投流源
func (r *liveStreamRepository) GetActiveLiveStreams() ([]model.LiveStream, error) {
var streams []model.LiveStream
now := time.Now()
query := r.db.Where("status = ?", 1)
// 查询有效时间范围内的投流源
query = query.Where("(start_time IS NULL OR start_time <= ?) AND (end_time IS NULL OR end_time >= ?)", now, now)
if err := query.Order("sort DESC, created_at DESC").Find(&streams).Error; err != nil {
return nil, err
}
return streams, nil
}
// Create 创建投流源
func (r *liveStreamRepository) Create(stream *model.LiveStream) error {
return r.db.Create(stream).Error
}
// Update 更新投流源
func (r *liveStreamRepository) Update(id uint, stream *model.LiveStream) error {
return r.db.Model(&model.LiveStream{}).Where("id = ?", id).Updates(stream).Error
}
// UpdateStatus 更新投流源状态
func (r *liveStreamRepository) UpdateStatus(id uint, status int) error {
return r.db.Model(&model.LiveStream{}).Where("id = ?", id).Update("status", status).Error
}
// Delete 删除投流源
func (r *liveStreamRepository) Delete(id uint) error {
return r.db.Delete(&model.LiveStream{}, id).Error
}
// BatchDelete 批量删除投流源
func (r *liveStreamRepository) BatchDelete(ids []uint) error {
return r.db.Delete(&model.LiveStream{}, ids).Error
}
// IncrementViewCount 增加观看次数
func (r *liveStreamRepository) IncrementViewCount(id uint) error {
return r.db.Model(&model.LiveStream{}).Where("id = ?", id).
UpdateColumn("view_count", gorm.Expr("view_count + ?", 1)).Error
}

View File

@@ -192,9 +192,12 @@ func (r *OrderRepository) UpdateOrderItem(id uint, updates map[string]interface{
}
// GetCart 获取购物车
// 优化: 减少不必要的Preload,只加载必需的关联数据
func (r *OrderRepository) GetCart(userID uint) ([]model.Cart, error) {
var cart []model.Cart
err := r.db.Preload("Product").Preload("Product.SKUs", "status = ?", 1).Preload("SKU").Where("user_id = ?", userID).Find(&cart).Error
// 移除 Product.SKUs 的预加载,因为购物车已经有单独的SKU字段
// 只保留必要的Product和SKU信息
err := r.db.Preload("Product").Preload("SKU").Where("user_id = ?", userID).Find(&cart).Error
return cart, err
}

View File

@@ -0,0 +1,55 @@
package repository
import (
"dianshang/internal/model"
"gorm.io/gorm"
)
type PlatformRepository struct {
db *gorm.DB
}
func NewPlatformRepository(db *gorm.DB) *PlatformRepository {
return &PlatformRepository{db: db}
}
// GetAll 获取所有平台
func (r *PlatformRepository) GetAll() ([]model.Platform, error) {
var platforms []model.Platform
err := r.db.Where("status = ?", 1).Order("sort DESC, created_at ASC").Find(&platforms).Error
return platforms, err
}
// GetByID 根据ID获取平台
func (r *PlatformRepository) GetByID(id uint) (*model.Platform, error) {
var platform model.Platform
err := r.db.Where("id = ?", id).First(&platform).Error
return &platform, err
}
// GetByCode 根据代码获取平台
func (r *PlatformRepository) GetByCode(code string) (*model.Platform, error) {
var platform model.Platform
err := r.db.Where("code = ? AND status = ?", code, 1).First(&platform).Error
return &platform, err
}
// Create 创建平台
func (r *PlatformRepository) Create(platform *model.Platform) error {
return r.db.Create(platform).Error
}
// Update 更新平台
func (r *PlatformRepository) Update(id uint, updates map[string]interface{}) error {
return r.db.Model(&model.Platform{}).Where("id = ?", id).Updates(updates).Error
}
// Delete 删除平台
func (r *PlatformRepository) Delete(id uint) error {
return r.db.Delete(&model.Platform{}, id).Error
}
// GetDB 获取数据库连接
func (r *PlatformRepository) GetDB() *gorm.DB {
return r.db
}

View File

@@ -2,6 +2,8 @@ package repository
import (
"dianshang/internal/model"
"fmt"
"strings"
"gorm.io/gorm"
)
@@ -46,7 +48,7 @@ func (r *ProductRepository) GetList(offset, limit int, conditions map[string]int
for key, value := range conditions {
switch key {
case "category_id":
// 支持包含子分类的筛选
// category_id 现在是 JSON 数组,使用 JSON_CONTAINS 查询
var catID uint
switch v := value.(type) {
case uint:
@@ -57,12 +59,21 @@ func (r *ProductRepository) GetList(offset, limit int, conditions map[string]int
catID = uint(v)
}
if catID > 0 {
// 获取包含子分类的所有分类ID
categoryIDs, err := r.getCategoryIDsIncludingChildren(catID)
if err == nil && len(categoryIDs) > 0 {
query = query.Where("category_id IN (?)", categoryIDs)
// 使用 JSON_CONTAINS 查询包含任意一个分类ID的商品
// 构建 OR 条件JSON_CONTAINS(category_id, '1') OR JSON_CONTAINS(category_id, '2') ...
conditions := make([]string, len(categoryIDs))
args := make([]interface{}, len(categoryIDs))
for i, id := range categoryIDs {
conditions[i] = "JSON_CONTAINS(category_id, ?)"
args[i] = fmt.Sprintf("%d", id)
}
query = query.Where(strings.Join(conditions, " OR "), args...)
} else {
// 兜底:如果获取子分类失败,退化为当前分类
query = query.Where("category_id = ?", catID)
// 兜底:如果获取子分类失败,只查询当前分类
query = query.Where("JSON_CONTAINS(category_id, ?)", fmt.Sprintf("%d", catID))
}
}
case "keyword":
@@ -71,6 +82,15 @@ func (r *ProductRepository) GetList(offset, limit int, conditions map[string]int
query = query.Where("price >= ?", value)
case "max_price":
query = query.Where("price <= ?", value)
case "in_stock":
// 库存筛选true=有货false=缺货
if inStockValue, ok := value.(bool); ok {
if inStockValue {
query = query.Where("stock > ?", 0)
} else {
query = query.Where("stock = ?", 0)
}
}
case "is_hot":
if value.(string) == "true" {
query = query.Where("is_hot = ?", true)
@@ -129,16 +149,15 @@ func (r *ProductRepository) GetList(offset, limit int, conditions map[string]int
}
}
// 获取列表,预加载分类
err := query.Preload("Category").
Offset(offset).Limit(limit).Order(orderBy).Find(&products).Error
// 获取列表
err := query.Offset(offset).Limit(limit).Order(orderBy).Find(&products).Error
return products, total, err
}
// GetByID 根据ID获取产品详情
func (r *ProductRepository) GetByID(id uint) (*model.Product, error) {
var product model.Product
err := r.db.Preload("Category").Preload("Specs").Preload("SKUs", "status = ?", 1).
err := r.db.Preload("Specs").Preload("SKUs", "status = ?", 1).
Where("id = ?", id).First(&product).Error
return &product, err
}
@@ -216,8 +235,21 @@ func (r *ProductRepository) RestoreStock(id uint, quantity int) error {
// GetCategories 获取分类列表
func (r *ProductRepository) GetCategories() ([]model.Category, error) {
return r.GetCategoriesByPlatform("")
}
// GetCategoriesByPlatform 根据平台获取分类列表
func (r *ProductRepository) GetCategoriesByPlatform(platformCode string) ([]model.Category, error) {
var allCategories []model.Category
err := r.db.Where("status = ?", 1).Order("level ASC, sort DESC, created_at ASC").Find(&allCategories).Error
query := r.db.Where("status = ?", 1)
// 如果指定了平台,筛选包含该平台的分类
if platformCode != "" {
// 使用 JSON_CONTAINS 查询包含指定平台的分类
query = query.Where("JSON_CONTAINS(platform, ?)", `"`+platformCode+`"`)
}
err := query.Order("level ASC, sort DESC, created_at ASC").Find(&allCategories).Error
if err != nil {
return nil, err
}
@@ -409,8 +441,7 @@ func (r *ProductRepository) DeleteProductSpec(id uint) error {
// GetHotProducts 获取热门产品
func (r *ProductRepository) GetHotProducts(limit int) ([]model.Product, error) {
var products []model.Product
err := r.db.Preload("Category").
Where("status = ? AND is_hot = ?", 1, 1).
err := r.db.Where("status = ? AND is_hot = ?", 1, 1).
Order("sales DESC, created_at DESC").Limit(limit).Find(&products).Error
return products, err
}
@@ -418,8 +449,7 @@ func (r *ProductRepository) GetHotProducts(limit int) ([]model.Product, error) {
// GetRecommendProducts 获取推荐产品
func (r *ProductRepository) GetRecommendProducts(limit int) ([]model.Product, error) {
var products []model.Product
err := r.db.Preload("Category").
Where("status = ? AND is_recommend = ?", 1, 1).
err := r.db.Where("status = ? AND is_recommend = ?", 1, 1).
Order("sort DESC, created_at DESC").Limit(limit).Find(&products).Error
return products, err
}
@@ -660,7 +690,7 @@ func (r *ProductRepository) AssignTagsToProduct(productID uint, tagIDs []uint) e
func (r *ProductRepository) GetLowStockProducts(threshold int) ([]model.Product, error) {
var products []model.Product
err := r.db.Where("stock <= ? AND status = ?", threshold, 1).
Preload("Category").Find(&products).Error
Find(&products).Error
return products, err
}
@@ -698,7 +728,7 @@ func (r *ProductRepository) GetInventoryStatistics() (map[string]interface{}, er
// GetProductsForExport 获取用于导出的商品数据
func (r *ProductRepository) GetProductsForExport(conditions map[string]interface{}) ([]model.Product, error) {
var products []model.Product
query := r.db.Model(&model.Product{}).Preload("Category")
query := r.db.Model(&model.Product{})
// 添加查询条件
for key, value := range conditions {

View File

@@ -35,6 +35,13 @@ func (r *UserRepository) GetByOpenID(openID string) (*model.User, error) {
return &user, err
}
// GetByEmail 根据邮箱获取用户
func (r *UserRepository) GetByEmail(email string) (*model.User, error) {
var user model.User
err := r.db.Where("email = ?", email).First(&user).Error
return &user, err
}
// Update 更新用户
func (r *UserRepository) Update(id uint, updates map[string]interface{}) error {
return r.db.Model(&model.User{}).Where("id = ?", id).Updates(updates).Error