url/internal/module/url/repository/url.go

181 lines
4.6 KiB
Go

package repository
import (
mgo "code.30cm.net/digimon/library-go/mongo"
"context"
"errors"
"fmt"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"time"
"url_generate/internal/module/url/domain"
"url_generate/internal/module/url/domain/entity"
"url_generate/internal/module/url/domain/repository"
)
type URLRepositoryParam struct {
Conf *mgo.Conf
CacheConf cache.CacheConf
DbOpts []mon.Option
CacheOpts []cache.Option
}
type URLRepository struct {
DB mgo.DocumentDBWithCacheUseCase
}
func NewURLRepository(param URLRepositoryParam) repository.URLRepository {
e := entity.URLTable{}
documentDB, err := mgo.MustDocumentDBWithCache(
param.Conf,
e.CollectionName(),
param.CacheConf,
param.DbOpts,
param.CacheOpts,
)
if err != nil {
panic(err)
}
return &URLRepository{
DB: documentDB,
}
}
func (repo *URLRepository) BindOne(ctx context.Context, url string) (string, error) {
filter := bson.M{"url": bson.M{"$eq": ""}}
// 排序條件:按 `create_at` 降序排列
sort := bson.D{{"create_at", -1}}
// 定義變量存儲查詢結果
var existing entity.URLTable
// 查詢 MongoDB
err := repo.DB.GetClient().FindOne(ctx, &existing, filter, options.FindOne().SetSort(sort))
if errors.Is(err, mon.ErrNotFound) {
// 如果沒有符合條件的記錄
return "", fmt.Errorf("no available empty records found")
}
if err != nil {
// 處理其他錯誤
return "", fmt.Errorf("failed to query empty record: %w", err)
}
// 更新該記錄的 URL 並返回短碼
rk := domain.GetURLCodeRedisKey(existing.ShortCode)
_, err = repo.DB.UpdateOne(ctx, rk, bson.M{"_id": existing.ID}, bson.M{"$set": bson.M{
"url": url,
"update_at": time.Now().UTC().UnixNano(),
}})
if err != nil {
return "", fmt.Errorf("failed to update URL record: %w", err)
}
// 返回分配的短碼
return existing.ShortCode, nil
}
func (repo *URLRepository) Insert(ctx context.Context, data *entity.URLTable) error {
if data.ID.IsZero() {
now := time.Now().UTC().UnixNano()
data.ID = primitive.NewObjectID()
data.CreateAt = &now
data.UpdateAt = &now
}
rk := domain.GetURLCodeRedisKey(data.ShortCode)
_, err := repo.DB.InsertOne(ctx, rk, data)
return err
}
func (repo *URLRepository) FindOne(ctx context.Context, shortCode string) (*entity.URLTable, error) {
var data entity.URLTable
rk := domain.GetURLCodeRedisKey(shortCode)
err := repo.DB.FindOne(ctx, rk, &data, bson.M{"short_code": shortCode})
switch {
case err == nil:
return &data, nil
case errors.Is(err, mon.ErrNotFound):
return nil, ErrNotFound
default:
return nil, err
}
}
func (repo *URLRepository) Delete(ctx context.Context, shortCode string) (int64, error) {
rk := domain.GetURLCodeRedisKey(shortCode)
return repo.DB.DeleteOne(ctx, rk, bson.M{"short_code": shortCode})
}
func (repo *URLRepository) InsertMany(ctx context.Context, urls []*entity.URLTable) error {
if len(urls) == 0 {
return nil
}
now := time.Now().UTC().UnixNano()
var documents []interface{}
for _, url := range urls {
u := url
u.ID = primitive.NewObjectID()
u.CreateAt = &now
u.UpdateAt = &now
documents = append(documents, u)
}
_, err := repo.DB.GetClient().InsertMany(ctx, documents)
if err != nil {
return fmt.Errorf("failed to insert many URLs: %w", err)
}
return nil
}
func (repo *URLRepository) Update(ctx context.Context, shortCode string, newURL string) error {
filter := bson.M{"short_code": shortCode}
update := bson.M{
"$set": bson.M{
"url": newURL,
"update_at": time.Now().UTC().UnixNano(),
},
}
rk := domain.GetURLCodeRedisKey(shortCode)
result, err := repo.DB.UpdateOne(ctx, rk, filter, update, options.Update().SetUpsert(false))
if err != nil {
return fmt.Errorf("failed to update URL for short code %s: %w", shortCode, err)
}
if result.MatchedCount == 0 {
return ErrNotFound
}
return nil
}
func (repo *URLRepository) EmptyCount(ctx context.Context) (int, error) {
filter := bson.M{"url": ""}
count, err := repo.DB.GetClient().CountDocuments(ctx, filter)
if err != nil {
return 0, fmt.Errorf("failed to count empty URLs: %w", err)
}
return int(count), nil
}
func (repo *URLRepository) Index20241226001UP(ctx context.Context) (*mongo.Cursor, error) {
// 等價於 db.createIndex({"short_code": 1},{unique: true})
repo.DB.PopulateIndex(ctx, "short_code", 1, true)
return repo.DB.GetClient().Indexes().List(ctx)
}
// ptr 是一個幫助函數,用於生成指針
func ptr[T any](v T) *T {
return &v
}