273 lines
7.2 KiB
Go
273 lines
7.2 KiB
Go
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
|
||
}
|