feat(material): add material handling and routing functionality
Some checks failed
Build Backend / build (push) Failing after 12m1s
Build Backend / build-docker (push) Has been skipped

- Introduced MaterialHandler for managing learning materials and subjects.
- Updated router to include routes for public and admin material management.
- Enhanced database initialization with material subject and file models.
- Implemented seeding logic for material subjects and files.
- Updated wire generation to include MaterialHandler and related services.
This commit is contained in:
lafay
2026-03-25 20:44:12 +08:00
parent 28a45caad3
commit 0ef8e5a7e1
10 changed files with 1122 additions and 22 deletions

View File

@@ -0,0 +1,247 @@
package repository
import (
"carrot_bbs/internal/model"
"gorm.io/gorm"
)
// MaterialSubjectRepository 学科分类仓储
type MaterialSubjectRepository struct {
db *gorm.DB
}
func NewMaterialSubjectRepository(db *gorm.DB) *MaterialSubjectRepository {
return &MaterialSubjectRepository{db: db}
}
// ListActive 获取所有启用的学科分类
func (r *MaterialSubjectRepository) ListActive() ([]*model.MaterialSubject, error) {
var subjects []*model.MaterialSubject
err := r.db.
Where("is_active = ?", true).
Order("sort_order ASC, created_at ASC").
Find(&subjects).Error
return subjects, err
}
// ListAll 获取所有学科分类(管理端用)
func (r *MaterialSubjectRepository) ListAll() ([]*model.MaterialSubject, error) {
var subjects []*model.MaterialSubject
err := r.db.
Order("sort_order ASC, created_at ASC").
Find(&subjects).Error
return subjects, err
}
// GetByID 根据ID获取学科分类
func (r *MaterialSubjectRepository) GetByID(id string) (*model.MaterialSubject, error) {
var subject model.MaterialSubject
if err := r.db.First(&subject, "id = ?", id).Error; err != nil {
return nil, err
}
return &subject, nil
}
// Create 创建学科分类
func (r *MaterialSubjectRepository) Create(subject *model.MaterialSubject) error {
return r.db.Create(subject).Error
}
// Update 更新学科分类
func (r *MaterialSubjectRepository) Update(subject *model.MaterialSubject) error {
return r.db.Save(subject).Error
}
// Delete 删除学科分类
func (r *MaterialSubjectRepository) Delete(id string) error {
return r.db.Delete(&model.MaterialSubject{}, "id = ?", id).Error
}
// CountMaterials 统计学科下的资料数量
func (r *MaterialSubjectRepository) CountMaterials(subjectID string) (int64, error) {
var count int64
err := r.db.Model(&model.MaterialFile{}).Where("subject_id = ?", subjectID).Count(&count).Error
return count, err
}
// MaterialFileRepository 文件资料仓储
type MaterialFileRepository struct {
db *gorm.DB
}
func NewMaterialFileRepository(db *gorm.DB) *MaterialFileRepository {
return &MaterialFileRepository{db: db}
}
// ListQueryParams 查询参数
type MaterialFileQueryParams struct {
SubjectID string
FileType string
Status string
Keyword string
Page int
PageSize int
SortBy string
SortOrder string
}
// List 获取资料列表(支持筛选和分页)
func (r *MaterialFileRepository) List(params MaterialFileQueryParams) ([]*model.MaterialFile, int64, error) {
var files []*model.MaterialFile
var total int64
query := r.db.Model(&model.MaterialFile{})
// 筛选条件
if params.SubjectID != "" {
query = query.Where("subject_id = ?", params.SubjectID)
}
if params.FileType != "" {
query = query.Where("file_type = ?", params.FileType)
}
if params.Status != "" {
query = query.Where("status = ?", params.Status)
}
if params.Keyword != "" {
keyword := "%" + params.Keyword + "%"
query = query.Where("title LIKE ? OR description LIKE ?", keyword, keyword)
}
// 统计总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// 排序
sortBy := params.SortBy
if sortBy == "" {
sortBy = "created_at"
}
sortOrder := params.SortOrder
if sortOrder == "" {
sortOrder = "DESC"
}
orderClause := sortBy + " " + sortOrder
// 分页
page := params.Page
if page < 1 {
page = 1
}
pageSize := params.PageSize
if pageSize < 1 {
pageSize = 20
}
offset := (page - 1) * pageSize
// 查询
if err := query.Order(orderClause).Offset(offset).Limit(pageSize).Find(&files).Error; err != nil {
return nil, 0, err
}
return files, total, nil
}
// GetByID 根据ID获取资料详情
func (r *MaterialFileRepository) GetByID(id string) (*model.MaterialFile, error) {
var file model.MaterialFile
if err := r.db.First(&file, "id = ?", id).Error; err != nil {
return nil, err
}
return &file, nil
}
// GetByIDWithSubject 根据ID获取资料详情包含学科信息
func (r *MaterialFileRepository) GetByIDWithSubject(id string) (*model.MaterialFile, error) {
var file model.MaterialFile
if err := r.db.Preload("Subject").First(&file, "id = ?", id).Error; err != nil {
return nil, err
}
return &file, nil
}
// Create 创建资料
func (r *MaterialFileRepository) Create(file *model.MaterialFile) error {
return r.db.Create(file).Error
}
// Update 更新资料
func (r *MaterialFileRepository) Update(file *model.MaterialFile) error {
return r.db.Save(file).Error
}
// Delete 删除资料
func (r *MaterialFileRepository) Delete(id string) error {
return r.db.Delete(&model.MaterialFile{}, "id = ?", id).Error
}
// IncrementDownloadCount 增加下载次数
func (r *MaterialFileRepository) IncrementDownloadCount(id string) error {
return r.db.Model(&model.MaterialFile{}).Where("id = ?", id).
UpdateColumn("download_count", gorm.Expr("download_count + ?", 1)).Error
}
// GetBySubjectID 根据学科ID获取资料列表
func (r *MaterialFileRepository) GetBySubjectID(subjectID string, page, pageSize int) ([]*model.MaterialFile, int64, error) {
var files []*model.MaterialFile
var total int64
query := r.db.Model(&model.MaterialFile{}).Where("subject_id = ? AND status = ?", subjectID, model.MaterialStatusActive)
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&files).Error; err != nil {
return nil, 0, err
}
return files, total, nil
}
// Search 搜索资料
func (r *MaterialFileRepository) Search(keyword string, page, pageSize int) ([]*model.MaterialFile, int64, error) {
var files []*model.MaterialFile
var total int64
query := r.db.Model(&model.MaterialFile{}).Where("status = ?", model.MaterialStatusActive)
if keyword != "" {
kw := "%" + keyword + "%"
query = query.Where("title LIKE ? OR description LIKE ?", kw, kw)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&files).Error; err != nil {
return nil, 0, err
}
return files, total, nil
}
// GetHotMaterials 获取热门资料(按下载次数排序)
func (r *MaterialFileRepository) GetHotMaterials(limit int) ([]*model.MaterialFile, error) {
var files []*model.MaterialFile
err := r.db.
Where("status = ?", model.MaterialStatusActive).
Order("download_count DESC").
Limit(limit).
Find(&files).Error
return files, err
}
// GetLatestMaterials 获取最新资料
func (r *MaterialFileRepository) GetLatestMaterials(limit int) ([]*model.MaterialFile, error) {
var files []*model.MaterialFile
err := r.db.
Where("status = ?", model.MaterialStatusActive).
Order("created_at DESC").
Limit(limit).
Find(&files).Error
return files, err
}