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 }