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

273 lines
7.2 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"
)
// KnowledgeTreeDAOMongo MongoDB 实现的知识树数据访问对象
type KnowledgeTreeDAOMongo struct {
client *database.MongoDBClient
collection *mongo.Collection
}
// NewKnowledgeTreeDAOMongo 创建 MongoDB 知识树 DAO
func NewKnowledgeTreeDAOMongo(client *database.MongoDBClient) *KnowledgeTreeDAOMongo {
return &KnowledgeTreeDAOMongo{
client: client,
collection: client.Collection("knowledge_trees"),
}
}
// KnowledgeTreeDocument MongoDB 文档结构
type KnowledgeTreeDocument struct {
ID string `bson:"_id" json:"id"`
Type string `bson:"type" json:"type"` // 知识树类型objective客观题或 subjective主观题
Title string `bson:"title" json:"title"`
ParentID string `bson:"parent_id" json:"parent_id"` // 根节点为空字符串
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 *KnowledgeTreeDAOMongo) Create(node *question.KnowledgeTree) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
doc := &KnowledgeTreeDocument{
ID: node.ID,
Type: string(node.Type),
Title: node.Title,
ParentID: node.ParentID,
CreatedAt: node.CreatedAt,
UpdatedAt: node.UpdatedAt,
}
_, err := dao.collection.InsertOne(ctx, doc)
if err != nil {
return fmt.Errorf("插入知识树节点失败: %v", err)
}
return nil
}
// GetByID 根据ID获取知识树节点
func (dao *KnowledgeTreeDAOMongo) GetByID(id string) (*question.KnowledgeTree, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
filter := bson.M{
"_id": id,
"deleted_at": bson.M{"$exists": false},
}
var doc KnowledgeTreeDocument
err := dao.collection.FindOne(ctx, filter).Decode(&doc)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, fmt.Errorf("知识树节点不存在: %s", id)
}
return nil, fmt.Errorf("查询知识树节点失败: %v", err)
}
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, fmt.Errorf("知识树节点不存在: %s", id)
}
return nil, fmt.Errorf("查询知识树节点失败: %v", err)
}
return &question.KnowledgeTree{
ID: doc.ID,
Type: question.KnowledgeTreeType(doc.Type),
Title: doc.Title,
ParentID: doc.ParentID,
CreatedAt: doc.CreatedAt,
UpdatedAt: doc.UpdatedAt,
}, nil
}
// GetAll 获取所有知识树节点(不包括已删除的)
func (dao *KnowledgeTreeDAOMongo) GetAll(treeType question.KnowledgeTreeType) ([]*question.KnowledgeTree, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
filter := bson.M{
"type": string(treeType),
"deleted_at": bson.M{"$exists": false},
}
opts := options.Find().SetSort(bson.D{{Key: "created_at", Value: 1}})
cursor, err := dao.collection.Find(ctx, filter, opts)
if err != nil {
return nil, fmt.Errorf("查询知识树节点失败: %v", err)
}
defer cursor.Close(ctx)
var nodes []*question.KnowledgeTree
for cursor.Next(ctx) {
var doc KnowledgeTreeDocument
if err := cursor.Decode(&doc); err != nil {
return nil, fmt.Errorf("解码知识树节点失败: %v", err)
}
nodes = append(nodes, &question.KnowledgeTree{
ID: doc.ID,
Type: question.KnowledgeTreeType(doc.Type),
Title: doc.Title,
ParentID: doc.ParentID,
CreatedAt: doc.CreatedAt,
UpdatedAt: doc.UpdatedAt,
})
}
if err := cursor.Err(); err != nil {
return nil, fmt.Errorf("遍历知识树节点失败: %v", err)
}
return nodes, nil
}
// GetByParentID 根据父节点ID获取子节点列表
func (dao *KnowledgeTreeDAOMongo) GetByParentID(parentID string, treeType question.KnowledgeTreeType) ([]*question.KnowledgeTree, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
filter := bson.M{
"parent_id": parentID,
"type": string(treeType),
"deleted_at": bson.M{"$exists": false},
}
opts := options.Find().SetSort(bson.D{{Key: "created_at", Value: 1}})
cursor, err := dao.collection.Find(ctx, filter, opts)
if err != nil {
return nil, fmt.Errorf("查询子节点失败: %v", err)
}
defer cursor.Close(ctx)
var nodes []*question.KnowledgeTree
for cursor.Next(ctx) {
var doc KnowledgeTreeDocument
if err := cursor.Decode(&doc); err != nil {
return nil, fmt.Errorf("解码子节点失败: %v", err)
}
nodes = append(nodes, &question.KnowledgeTree{
ID: doc.ID,
Type: question.KnowledgeTreeType(doc.Type),
Title: doc.Title,
ParentID: doc.ParentID,
CreatedAt: doc.CreatedAt,
UpdatedAt: doc.UpdatedAt,
})
}
if err := cursor.Err(); err != nil {
return nil, fmt.Errorf("遍历子节点失败: %v", err)
}
return nodes, nil
}
// Update 更新知识树节点
func (dao *KnowledgeTreeDAOMongo) Update(node *question.KnowledgeTree) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
filter := bson.M{
"_id": node.ID,
"deleted_at": bson.M{"$exists": false},
}
update := bson.M{
"$set": bson.M{
"type": string(node.Type),
"title": node.Title,
"parent_id": node.ParentID,
"updated_at": node.UpdatedAt,
},
}
result, err := dao.collection.UpdateOne(ctx, filter, update)
if err != nil {
return fmt.Errorf("更新知识树节点失败: %v", err)
}
if result.MatchedCount == 0 {
return fmt.Errorf("知识树节点不存在: %s", node.ID)
}
return nil
}
// Delete 删除知识树节点(软删除)
func (dao *KnowledgeTreeDAOMongo) Delete(id string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
now := time.Now().Unix()
filter := bson.M{
"_id": id,
"deleted_at": bson.M{"$exists": false},
}
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("知识树节点不存在: %s", id)
}
return nil
}
// GetTree 获取完整树形结构
func (dao *KnowledgeTreeDAOMongo) GetTree(treeType question.KnowledgeTreeType) ([]*question.KnowledgeTree, error) {
// 获取所有节点
allNodes, err := dao.GetAll(treeType)
if err != nil {
return nil, err
}
// 构建节点映射
nodeMap := make(map[string]*question.KnowledgeTree)
for _, node := range allNodes {
nodeMap[node.ID] = node
node.Children = []*question.KnowledgeTree{} // 初始化子节点数组
}
// 构建树形结构
var rootNodes []*question.KnowledgeTree
for _, node := range allNodes {
if node.ParentID == "" {
// 根节点
rootNodes = append(rootNodes, node)
} else {
// 子节点添加到父节点的Children中
if parent, exists := nodeMap[node.ParentID]; exists {
parent.Children = append(parent.Children, node)
}
}
}
return rootNodes, nil
}