feat: implement wiki and wiki content management system including database schemas, DTOs, and API endpoints
All checks were successful
Build and Release / release (push) Successful in 1m33s
All checks were successful
Build and Release / release (push) Successful in 1m33s
This commit is contained in:
@@ -32,6 +32,10 @@ type WikiRepository interface {
|
||||
BulkDeleteEntityWikisByWikiID(ctx context.Context, wikiID pgtype.UUID) error
|
||||
DeleteEntityWiki(ctx context.Context, entityID pgtype.UUID, wikiID pgtype.UUID) error
|
||||
DeleteEntityWikisByProjectID(ctx context.Context, projectID pgtype.UUID) error
|
||||
CreateContent(ctx context.Context, params sqlc.CreateWikiContentParams) (*models.WikiContentEntity, error)
|
||||
GetContentCountByWikiID(ctx context.Context, wikiID pgtype.UUID) (int64, error)
|
||||
GetContentByID(ctx context.Context, id pgtype.UUID) (*models.WikiContentEntity, error)
|
||||
GetContentByIDs(ctx context.Context, ids []string) ([]*models.WikiContentEntity, error)
|
||||
WithTx(tx pgx.Tx) WikiRepository
|
||||
}
|
||||
|
||||
@@ -93,7 +97,6 @@ func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
@@ -101,6 +104,20 @@ func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
dbMap[item.ID] = &item
|
||||
}
|
||||
|
||||
samples, err := r.q.GetWikiContentByWikiIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
for _, sample := range samples {
|
||||
wikiID := convert.UUIDToString(sample.WikiID)
|
||||
if item, ok := dbMap[wikiID]; ok {
|
||||
item.ContentSample = append(item.ContentSample, models.WikiContentSample{
|
||||
ID: convert.UUIDToString(sample.ID),
|
||||
Title: sample.Title,
|
||||
CreatedAt: convert.TimeToPtr(sample.CreatedAt),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,12 +164,24 @@ func (r *wikiRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.W
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
|
||||
// Fetch content samples
|
||||
samples, err := r.q.GetWikiContentByWikiID(ctx, row.ID)
|
||||
if err == nil {
|
||||
for _, sample := range samples {
|
||||
wiki.ContentSample = append(wiki.ContentSample, models.WikiContentSample{
|
||||
ID: convert.UUIDToString(sample.ID),
|
||||
Title: sample.Title,
|
||||
CreatedAt: convert.TimeToPtr(sample.CreatedAt),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_ = r.c.Set(ctx, cacheId, wiki, constants.NormalCacheDuration)
|
||||
|
||||
return &wiki, nil
|
||||
@@ -178,7 +207,6 @@ func (r *wikiRepository) Search(ctx context.Context, params sqlc.SearchWikisPara
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
@@ -209,7 +237,6 @@ func (r *wikiRepository) Create(ctx context.Context, params sqlc.CreateWikiParam
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
@@ -228,7 +255,6 @@ func (r *wikiRepository) Update(ctx context.Context, params sqlc.UpdateWikiParam
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
@@ -285,7 +311,6 @@ func (r *wikiRepository) GetByProjectID(ctx context.Context, projectID pgtype.UU
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
@@ -350,7 +375,6 @@ func (r *wikiRepository) GetBySlug(ctx context.Context, slug string) (*models.Wi
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
@@ -390,7 +414,6 @@ func (r *wikiRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*mod
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
@@ -421,3 +444,117 @@ func (r *wikiRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*mod
|
||||
|
||||
return wikis, nil
|
||||
}
|
||||
|
||||
func (r *wikiRepository) CreateContent(ctx context.Context, params sqlc.CreateWikiContentParams) (*models.WikiContentEntity, error) {
|
||||
row, err := r.q.CreateWikiContent(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.WikiContentEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
WikiID: convert.UUIDToString(row.WikiID),
|
||||
Title: row.Title,
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetContentCountByWikiID(ctx context.Context, wikiID pgtype.UUID) (int64, error) {
|
||||
return r.q.GetWikiContentCount(ctx, wikiID)
|
||||
}
|
||||
|
||||
func (r *wikiRepository) getContentByIDsWithFallback(ctx context.Context, ids []string) ([]*models.WikiContentEntity, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*models.WikiContentEntity{}, nil
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("wiki_content:id:%s", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var contents []*models.WikiContentEntity
|
||||
missingToCache := make(map[string]any)
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
err := pgId.Scan(ids[i])
|
||||
if err == nil {
|
||||
missingPgIds = append(missingPgIds, pgId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.WikiContentEntity)
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetWikiContentByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
for _, row := range dbRows {
|
||||
item := models.WikiContentEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
WikiID: convert.UUIDToString(row.WikiID),
|
||||
Title: row.Title,
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
}
|
||||
dbMap[item.ID] = &item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, b := range raws {
|
||||
if len(b) > 0 {
|
||||
var u models.WikiContentEntity
|
||||
if err := json.Unmarshal(b, &u); err == nil {
|
||||
contents = append(contents, &u)
|
||||
}
|
||||
} else {
|
||||
if item, ok := dbMap[ids[i]]; ok {
|
||||
contents = append(contents, item)
|
||||
missingToCache[keys[i]] = item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingToCache) > 0 {
|
||||
_ = r.c.MSet(ctx, missingToCache, constants.NormalCacheDuration)
|
||||
}
|
||||
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetContentByID(ctx context.Context, id pgtype.UUID) (*models.WikiContentEntity, error) {
|
||||
cacheId := fmt.Sprintf("wiki_content:id:%s", convert.UUIDToString(id))
|
||||
var content models.WikiContentEntity
|
||||
err := r.c.Get(ctx, cacheId, &content)
|
||||
if err == nil {
|
||||
_ = r.c.Set(ctx, cacheId, content, constants.NormalCacheDuration)
|
||||
return &content, nil
|
||||
}
|
||||
|
||||
row, err := r.q.GetWikiContentById(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content = models.WikiContentEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
WikiID: convert.UUIDToString(row.WikiID),
|
||||
Title: row.Title,
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
}
|
||||
_ = r.c.Set(ctx, cacheId, content, constants.NormalCacheDuration)
|
||||
|
||||
return &content, nil
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetContentByIDs(ctx context.Context, ids []string) ([]*models.WikiContentEntity, error) {
|
||||
return r.getContentByIDsWithFallback(ctx, ids)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user