duidui_fiber/internal/question/dao/material_dao_mongo.go
2026-03-27 10:34:03 +08:00

248 lines
6.0 KiB
Go
Raw Permalink 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 dao
import (
"context"
"fmt"
"time"
"dd_fiber_api/internal/question"
"dd_fiber_api/pkg/database"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// MaterialDAOMongo MongoDB 实现的材料数据访问对象
type MaterialDAOMongo struct {
client *database.MongoDBClient
collection *mongo.Collection
}
// NewMaterialDAOMongo 创建 MongoDB 材料 DAO
func NewMaterialDAOMongo(client *database.MongoDBClient) *MaterialDAOMongo {
return &MaterialDAOMongo{
client: client,
collection: client.Collection("materials"),
}
}
// MaterialDocument MongoDB 文档结构
type MaterialDocument struct {
ID string `bson:"_id" json:"id"`
Type string `bson:"type" json:"type"` // 材料类型objective 或 subjective
Name string `bson:"name" json:"name"`
Content string `bson:"content" json:"content"`
CreatedAt int64 `bson:"created_at" json:"created_at"`
UpdatedAt int64 `bson:"updated_at" json:"updated_at"`
DeletedAt *int64 `bson:"deleted_at,omitempty" json:"deleted_at,omitempty"`
}
// Create 创建材料
func (dao *MaterialDAOMongo) Create(material *question.Material) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
doc := &MaterialDocument{
ID: material.ID,
Type: string(material.Type),
Name: material.Name,
Content: material.Content,
CreatedAt: material.CreatedAt,
UpdatedAt: material.UpdatedAt,
}
_, err := dao.collection.InsertOne(ctx, doc)
if err != nil {
return fmt.Errorf("插入材料失败: %v", err)
}
return nil
}
// GetByID 根据ID获取材料
func (dao *MaterialDAOMongo) GetByID(id string) (*question.Material, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var doc MaterialDocument
filter := bson.M{
"_id": id,
"deleted_at": bson.M{"$exists": false},
}
err := dao.collection.FindOne(ctx, filter).Decode(&doc)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, fmt.Errorf("材料不存在")
}
return nil, fmt.Errorf("查询材料失败: %v", err)
}
return dao.documentToMaterial(&doc), nil
}
// Search 搜索材料
func (dao *MaterialDAOMongo) Search(query string, materialType question.MaterialType, page, pageSize int32) ([]*question.Material, int32, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
filter := bson.M{
"deleted_at": bson.M{"$exists": false},
}
// 按类型过滤
if materialType != "" {
filter["type"] = string(materialType)
}
// 全文搜索(支持名称和内容)
if query != "" {
filter["$or"] = []bson.M{
{"name": bson.M{"$regex": query, "$options": "i"}},
{"content": bson.M{"$regex": query, "$options": "i"}},
}
}
// 查询总数
total, err := dao.collection.CountDocuments(ctx, filter)
if err != nil {
return nil, 0, fmt.Errorf("查询总数失败: %v", err)
}
// 查询数据
skip := int64((page - 1) * pageSize)
limit := int64(pageSize)
opts := options.Find().
SetSkip(skip).
SetLimit(limit).
SetSort(bson.M{"created_at": -1})
cursor, err := dao.collection.Find(ctx, filter, opts)
if err != nil {
return nil, 0, fmt.Errorf("查询材料失败: %v", err)
}
defer cursor.Close(ctx)
var docs []MaterialDocument
if err := cursor.All(ctx, &docs); err != nil {
return nil, 0, fmt.Errorf("解析材料数据失败: %v", err)
}
materials := make([]*question.Material, len(docs))
for i, doc := range docs {
materials[i] = dao.documentToMaterial(&doc)
}
return materials, int32(total), nil
}
// Update 更新材料
func (dao *MaterialDAOMongo) Update(material *question.Material) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
filter := bson.M{
"_id": material.ID,
"deleted_at": bson.M{"$exists": false},
}
update := bson.M{
"$set": bson.M{
"type": string(material.Type),
"name": material.Name,
"content": material.Content,
"updated_at": material.UpdatedAt,
},
}
result, err := dao.collection.UpdateOne(ctx, filter, update)
if err != nil {
return fmt.Errorf("更新材料失败: %v", err)
}
if result.MatchedCount == 0 {
return fmt.Errorf("材料不存在或已被删除")
}
return nil
}
// Delete 删除材料(软删除)
func (dao *MaterialDAOMongo) Delete(id string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
filter := bson.M{
"_id": id,
"deleted_at": bson.M{"$exists": false},
}
now := time.Now().Unix()
update := bson.M{
"$set": bson.M{
"deleted_at": now,
"updated_at": now,
},
}
result, err := dao.collection.UpdateOne(ctx, filter, update)
if err != nil {
return fmt.Errorf("删除材料失败: %v", err)
}
if result.MatchedCount == 0 {
return fmt.Errorf("材料不存在或已被删除")
}
return nil
}
// documentToMaterial 将 MongoDB 文档转换为 Material 对象
func (dao *MaterialDAOMongo) documentToMaterial(doc *MaterialDocument) *question.Material {
materialType := question.MaterialType(doc.Type)
// 兼容旧数据:如果没有 type 字段,默认为 objective
if materialType == "" {
materialType = question.MaterialTypeObjective
}
return &question.Material{
ID: doc.ID,
Type: materialType,
Name: doc.Name,
Content: doc.Content,
CreatedAt: doc.CreatedAt,
UpdatedAt: doc.UpdatedAt,
}
}
// CreateIndexes 创建索引
func (dao *MaterialDAOMongo) CreateIndexes() error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
indexes := []mongo.IndexModel{
{
Keys: bson.D{
{Key: "name", Value: 1},
},
},
{
Keys: bson.D{
{Key: "created_at", Value: -1},
},
},
{
Keys: bson.D{
{Key: "name", Value: "text"},
{Key: "content", Value: "text"},
},
Options: options.Index().SetName("text_index"),
},
}
_, err := dao.collection.Indexes().CreateMany(ctx, indexes)
if err != nil {
return fmt.Errorf("创建索引失败: %v", err)
}
return nil
}