ac49e1458c
- 新增 marketing 模块:model/repository/service/handler 四层架构 - 数据模型:marketing_categories、marketing_templates、marketing_user_downloads - 小程序端接口:分类列表、模板列表/详情、下载记录、广告回调 - 管理后台接口:分类/模板 CRUD、下载统计(X-Admin-Token 鉴权) - 路由注册:接入现有 AuthMiddleware,新增 AdminTokenMiddleware - Web 管理后台:单页面 Vue3 + Element Plus(分类管理、模板管理、数据概览) Closes #37, #38, #39, #40 Made-with: Cursor
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package repository
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"wx_service/internal/marketing/model"
|
|
)
|
|
|
|
var ErrDownloadNotFound = errors.New("marketing download not found")
|
|
|
|
type DownloadRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewDownloadRepository(db *gorm.DB) *DownloadRepository {
|
|
return &DownloadRepository{db: db}
|
|
}
|
|
|
|
func (r *DownloadRepository) Create(dl *model.MarketingDownload) error {
|
|
if err := r.db.Create(dl).Error; err != nil {
|
|
return fmt.Errorf("create marketing download: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *DownloadRepository) FindByID(id uint) (*model.MarketingDownload, error) {
|
|
var dl model.MarketingDownload
|
|
err := r.db.First(&dl, id).Error
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, ErrDownloadNotFound
|
|
}
|
|
return nil, fmt.Errorf("find marketing download: %w", err)
|
|
}
|
|
return &dl, nil
|
|
}
|
|
|
|
func (r *DownloadRepository) MarkAdCompleted(id uint) error {
|
|
tx := r.db.Model(&model.MarketingDownload{}).Where("id = ?", id).Update("ad_completed", true)
|
|
if tx.Error != nil {
|
|
return fmt.Errorf("mark ad completed: %w", tx.Error)
|
|
}
|
|
if tx.RowsAffected == 0 {
|
|
return ErrDownloadNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *DownloadRepository) FindByUser(userID uint, page, pageSize int) ([]model.MarketingDownload, int64, error) {
|
|
if page <= 0 {
|
|
page = 1
|
|
}
|
|
if pageSize <= 0 {
|
|
pageSize = 20
|
|
}
|
|
if pageSize > 100 {
|
|
pageSize = 100
|
|
}
|
|
|
|
query := r.db.Model(&model.MarketingDownload{}).Where("user_id = ?", userID)
|
|
|
|
var total int64
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("count marketing downloads: %w", err)
|
|
}
|
|
|
|
var downloads []model.MarketingDownload
|
|
offset := (page - 1) * pageSize
|
|
if err := query.Preload("Template").Order("id DESC").Offset(offset).Limit(pageSize).Find(&downloads).Error; err != nil {
|
|
return nil, 0, fmt.Errorf("list marketing downloads: %w", err)
|
|
}
|
|
|
|
return downloads, total, nil
|
|
}
|
|
|
|
type DownloadStats struct {
|
|
TotalDownloads int64
|
|
TodayDownloads int64
|
|
}
|
|
|
|
func (r *DownloadRepository) GetStats() (*DownloadStats, error) {
|
|
var total int64
|
|
if err := r.db.Model(&model.MarketingDownload{}).Count(&total).Error; err != nil {
|
|
return nil, fmt.Errorf("count total downloads: %w", err)
|
|
}
|
|
|
|
var today int64
|
|
if err := r.db.Model(&model.MarketingDownload{}).
|
|
Where("DATE(created_at) = CURDATE()").
|
|
Count(&today).Error; err != nil {
|
|
return nil, fmt.Errorf("count today downloads: %w", err)
|
|
}
|
|
|
|
return &DownloadStats{TotalDownloads: total, TodayDownloads: today}, nil
|
|
}
|