233 lines
9.1 KiB
Go
233 lines
9.1 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
app "haixun-backend/internal/library/errors"
|
|
"haixun-backend/internal/library/errors/code"
|
|
"haixun-backend/internal/model/content_ops/domain/entity"
|
|
domrepo "haixun-backend/internal/model/content_ops/domain/repository"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
type mongoRepository struct {
|
|
topics *mongo.Collection
|
|
plans *mongo.Collection
|
|
feedback *mongo.Collection
|
|
sources *mongo.Collection
|
|
chunks *mongo.Collection
|
|
formulas *mongo.Collection
|
|
}
|
|
|
|
func NewMongoRepository(db *mongo.Database) domrepo.Repository {
|
|
if db == nil {
|
|
return &mongoRepository{}
|
|
}
|
|
return &mongoRepository{
|
|
topics: db.Collection(entity.TopicCandidateCollectionName),
|
|
plans: db.Collection(entity.ContentPlanCollectionName),
|
|
feedback: db.Collection(entity.FeedbackEventCollectionName),
|
|
sources: db.Collection(entity.KnowledgeSourceCollectionName),
|
|
chunks: db.Collection(entity.KnowledgeChunkCollectionName),
|
|
formulas: db.Collection(entity.FormulaPoolCollectionName),
|
|
}
|
|
}
|
|
|
|
func (r *mongoRepository) EnsureIndexes(ctx context.Context) error {
|
|
if r.topics == nil || r.plans == nil || r.feedback == nil || r.sources == nil || r.chunks == nil || r.formulas == nil {
|
|
return nil
|
|
}
|
|
if _, err := r.topics.Indexes().CreateMany(ctx, []mongo.IndexModel{{Keys: bson.D{{Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "persona_id", Value: 1}, {Key: "create_at", Value: -1}}}}); err != nil {
|
|
return err
|
|
}
|
|
if _, err := r.plans.Indexes().CreateMany(ctx, []mongo.IndexModel{{Keys: bson.D{{Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "persona_id", Value: 1}, {Key: "create_at", Value: -1}}}, {Keys: bson.D{{Key: "topic_candidate_id", Value: 1}}}}); err != nil {
|
|
return err
|
|
}
|
|
_, err := r.feedback.Indexes().CreateMany(ctx, []mongo.IndexModel{{Keys: bson.D{{Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "persona_id", Value: 1}, {Key: "create_at", Value: -1}}}, {Keys: bson.D{{Key: "content_plan_id", Value: 1}}}, {Keys: bson.D{{Key: "draft_id", Value: 1}}}})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := r.sources.Indexes().CreateMany(ctx, []mongo.IndexModel{{Keys: bson.D{{Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "persona_id", Value: 1}, {Key: "create_at", Value: -1}}}}); err != nil {
|
|
return err
|
|
}
|
|
if _, err := r.chunks.Indexes().CreateMany(ctx, []mongo.IndexModel{{Keys: bson.D{{Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "persona_id", Value: 1}, {Key: "create_at", Value: -1}}}, {Keys: bson.D{{Key: "source_id", Value: 1}}}}); err != nil {
|
|
return err
|
|
}
|
|
_, err = r.formulas.Indexes().CreateMany(ctx, []mongo.IndexModel{{Keys: bson.D{{Key: "tenant_id", Value: 1}, {Key: "owner_uid", Value: 1}, {Key: "persona_id", Value: 1}, {Key: "type", Value: 1}}}})
|
|
return err
|
|
}
|
|
|
|
func personaFilter(tenantID, ownerUID, personaID string) bson.M {
|
|
return bson.M{"tenant_id": strings.TrimSpace(tenantID), "owner_uid": strings.TrimSpace(ownerUID), "persona_id": strings.TrimSpace(personaID)}
|
|
}
|
|
|
|
func normalizeLimit(limit int) int64 {
|
|
if limit <= 0 {
|
|
limit = 50
|
|
}
|
|
if limit > 200 {
|
|
limit = 200
|
|
}
|
|
return int64(limit)
|
|
}
|
|
|
|
func (r *mongoRepository) CreateTopicCandidate(ctx context.Context, item *entity.TopicCandidate) error {
|
|
if r.topics == nil {
|
|
return app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
_, err := r.topics.InsertOne(ctx, item)
|
|
return err
|
|
}
|
|
|
|
func (r *mongoRepository) ListTopicCandidates(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]entity.TopicCandidate, error) {
|
|
if r.topics == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
cur, err := r.topics.Find(ctx, personaFilter(tenantID, ownerUID, personaID), options.Find().SetSort(bson.D{{Key: "create_at", Value: -1}}).SetLimit(normalizeLimit(limit)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cur.Close(ctx)
|
|
var out []entity.TopicCandidate
|
|
return out, cur.All(ctx, &out)
|
|
}
|
|
|
|
func (r *mongoRepository) CreateContentPlan(ctx context.Context, item *entity.ContentPlan) error {
|
|
if r.plans == nil {
|
|
return app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
_, err := r.plans.InsertOne(ctx, item)
|
|
return err
|
|
}
|
|
|
|
func (r *mongoRepository) UpdateContentPlan(ctx context.Context, tenantID, ownerUID, personaID, id string, patch map[string]any) (*entity.ContentPlan, error) {
|
|
if r.plans == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
filter := personaFilter(tenantID, ownerUID, personaID)
|
|
filter["_id"] = strings.TrimSpace(id)
|
|
var out entity.ContentPlan
|
|
err := r.plans.FindOneAndUpdate(ctx, filter, bson.M{"$set": patch}, options.FindOneAndUpdate().SetReturnDocument(options.After)).Decode(&out)
|
|
if err == mongo.ErrNoDocuments {
|
|
return nil, app.For(code.Persona).ResNotFound("找不到內容計畫")
|
|
}
|
|
return &out, err
|
|
}
|
|
|
|
func (r *mongoRepository) GetContentPlan(ctx context.Context, tenantID, ownerUID, personaID, id string) (*entity.ContentPlan, error) {
|
|
if r.plans == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
filter := personaFilter(tenantID, ownerUID, personaID)
|
|
filter["_id"] = strings.TrimSpace(id)
|
|
var out entity.ContentPlan
|
|
err := r.plans.FindOne(ctx, filter).Decode(&out)
|
|
if err == mongo.ErrNoDocuments {
|
|
return nil, app.For(code.Persona).ResNotFound("找不到內容計畫")
|
|
}
|
|
return &out, err
|
|
}
|
|
|
|
func (r *mongoRepository) ListContentPlans(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]entity.ContentPlan, error) {
|
|
if r.plans == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
cur, err := r.plans.Find(ctx, personaFilter(tenantID, ownerUID, personaID), options.Find().SetSort(bson.D{{Key: "create_at", Value: -1}}).SetLimit(normalizeLimit(limit)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cur.Close(ctx)
|
|
var out []entity.ContentPlan
|
|
return out, cur.All(ctx, &out)
|
|
}
|
|
|
|
func (r *mongoRepository) CreateFeedbackEvent(ctx context.Context, item *entity.FeedbackEvent) error {
|
|
if r.feedback == nil {
|
|
return app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
_, err := r.feedback.InsertOne(ctx, item)
|
|
return err
|
|
}
|
|
|
|
func (r *mongoRepository) ListFeedbackEvents(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]entity.FeedbackEvent, error) {
|
|
if r.feedback == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
cur, err := r.feedback.Find(ctx, personaFilter(tenantID, ownerUID, personaID), options.Find().SetSort(bson.D{{Key: "create_at", Value: -1}}).SetLimit(normalizeLimit(limit)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cur.Close(ctx)
|
|
var out []entity.FeedbackEvent
|
|
return out, cur.All(ctx, &out)
|
|
}
|
|
|
|
func (r *mongoRepository) CreateKnowledgeSource(ctx context.Context, item *entity.KnowledgeSource, chunks []*entity.KnowledgeChunk) error {
|
|
if r.sources == nil || r.chunks == nil {
|
|
return app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
if _, err := r.sources.InsertOne(ctx, item); err != nil {
|
|
return err
|
|
}
|
|
if len(chunks) == 0 {
|
|
return nil
|
|
}
|
|
docs := make([]any, 0, len(chunks))
|
|
for _, c := range chunks {
|
|
docs = append(docs, c)
|
|
}
|
|
_, err := r.chunks.InsertMany(ctx, docs)
|
|
return err
|
|
}
|
|
|
|
func (r *mongoRepository) ListKnowledgeSources(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]entity.KnowledgeSource, error) {
|
|
if r.sources == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
cur, err := r.sources.Find(ctx, personaFilter(tenantID, ownerUID, personaID), options.Find().SetSort(bson.D{{Key: "create_at", Value: -1}}).SetLimit(normalizeLimit(limit)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cur.Close(ctx)
|
|
var out []entity.KnowledgeSource
|
|
return out, cur.All(ctx, &out)
|
|
}
|
|
|
|
func (r *mongoRepository) ListKnowledgeChunks(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]entity.KnowledgeChunk, error) {
|
|
if r.chunks == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
cur, err := r.chunks.Find(ctx, personaFilter(tenantID, ownerUID, personaID), options.Find().SetSort(bson.D{{Key: "create_at", Value: -1}}).SetLimit(normalizeLimit(limit)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cur.Close(ctx)
|
|
var out []entity.KnowledgeChunk
|
|
return out, cur.All(ctx, &out)
|
|
}
|
|
|
|
func (r *mongoRepository) CreateFormulaPool(ctx context.Context, item *entity.FormulaPool) error {
|
|
if r.formulas == nil {
|
|
return app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
_, err := r.formulas.InsertOne(ctx, item)
|
|
return err
|
|
}
|
|
|
|
func (r *mongoRepository) ListFormulaPools(ctx context.Context, tenantID, ownerUID, personaID string, limit int) ([]entity.FormulaPool, error) {
|
|
if r.formulas == nil {
|
|
return nil, app.For(code.Persona).DBUnavailable("Mongo is not configured")
|
|
}
|
|
cur, err := r.formulas.Find(ctx, personaFilter(tenantID, ownerUID, personaID), options.Find().SetSort(bson.D{{Key: "weight", Value: -1}, {Key: "create_at", Value: -1}}).SetLimit(normalizeLimit(limit)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cur.Close(ctx)
|
|
var out []entity.FormulaPool
|
|
return out, cur.All(ctx, &out)
|
|
}
|