固定文章编码
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"platform/web/core"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -33,6 +34,8 @@ var articleUploadMimeExt = map[string]string{
|
||||
"image/webp": ".webp",
|
||||
}
|
||||
|
||||
var articleCodePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]{0,63}$`)
|
||||
|
||||
type ArticleUploadResult struct {
|
||||
URL string `json:"url"`
|
||||
Path string `json:"path"`
|
||||
@@ -151,7 +154,13 @@ func formatUploadSizeLimit(bytes int) string {
|
||||
func (s *articleService) Page(req *PageArticleReq) (result []*m.Article, count int64, err error) {
|
||||
do := q.Article.Where()
|
||||
if req.Keyword != nil && *req.Keyword != "" {
|
||||
do = do.Where(q.Article.Title.Like("%" + *req.Keyword + "%"))
|
||||
do = do.Where(
|
||||
q.Article.Where(
|
||||
q.Article.Title.Like("%" + *req.Keyword + "%"),
|
||||
).Or(
|
||||
q.Article.Code.Like("%" + *req.Keyword + "%"),
|
||||
),
|
||||
)
|
||||
}
|
||||
if req.GroupID != nil {
|
||||
do = do.Where(q.Article.GroupID.Eq(*req.GroupID))
|
||||
@@ -194,17 +203,28 @@ func (s *articleService) Create(data CreateArticleData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.Article.Create(&m.Article{
|
||||
code, err := optionalArticleCode(data.Code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.Article.Create(&m.Article{
|
||||
GroupID: data.GroupID,
|
||||
Code: code,
|
||||
Title: data.Title,
|
||||
Content: data.Content,
|
||||
Sort: u.Else(data.Sort, 0),
|
||||
Status: u.Else(data.Status, m.ArticleStatusEnabled),
|
||||
})
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return core.NewBizErr("文档编码已存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type CreateArticleData struct {
|
||||
GroupID int32 `json:"group_id" validate:"required"`
|
||||
Code *string `json:"code"`
|
||||
Title string `json:"title" validate:"required"`
|
||||
Content *string `json:"content"`
|
||||
Sort *int32 `json:"sort"`
|
||||
@@ -222,6 +242,13 @@ func (s *articleService) Update(data UpdateArticleData) error {
|
||||
if data.GroupID != nil {
|
||||
do = append(do, q.Article.GroupID.Value(*data.GroupID))
|
||||
}
|
||||
if data.Code != nil {
|
||||
code, err := normalizeArticleCode(*data.Code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
do = append(do, q.Article.Code.Value(code))
|
||||
}
|
||||
if data.Title != nil {
|
||||
do = append(do, q.Article.Title.Value(*data.Title))
|
||||
}
|
||||
@@ -239,6 +266,9 @@ func (s *articleService) Update(data UpdateArticleData) error {
|
||||
}
|
||||
|
||||
r, err := q.Article.Where(q.Article.ID.Eq(data.ID)).UpdateSimple(do...)
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return core.NewBizErr("文档编码已存在")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -251,6 +281,7 @@ func (s *articleService) Update(data UpdateArticleData) error {
|
||||
type UpdateArticleData struct {
|
||||
ID int32 `json:"id" validate:"required"`
|
||||
GroupID *int32 `json:"group_id"`
|
||||
Code *string `json:"code"`
|
||||
Title *string `json:"title"`
|
||||
Content *string `json:"content"`
|
||||
Sort *int32 `json:"sort"`
|
||||
@@ -314,6 +345,7 @@ func (s *articleService) Nav() ([]*ArticleNavGroup, error) {
|
||||
}
|
||||
group.Articles = append(group.Articles, &ArticleNavArticle{
|
||||
ID: article.ID,
|
||||
Code: article.Code,
|
||||
Title: article.Title,
|
||||
UpdatedAt: article.UpdatedAt,
|
||||
})
|
||||
@@ -331,18 +363,40 @@ type ArticleNavGroup struct {
|
||||
|
||||
type ArticleNavArticle struct {
|
||||
ID int32 `json:"id"`
|
||||
Code *string `json:"code,omitempty"`
|
||||
Title string `json:"title"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (s *articleService) GetPublic(id int32) (*ArticlePublicDetail, error) {
|
||||
article, err := q.Article.
|
||||
type GetArticleReq struct {
|
||||
ID *int32 `json:"id"`
|
||||
Code *string `json:"code"`
|
||||
}
|
||||
|
||||
func (s *articleService) GetPublic(req GetArticleReq) (*ArticlePublicDetail, error) {
|
||||
do := q.Article.
|
||||
Preload(q.Article.Group).
|
||||
Where(
|
||||
q.Article.ID.Eq(id),
|
||||
q.Article.Status.Eq(int(m.ArticleStatusEnabled)),
|
||||
).
|
||||
Take()
|
||||
Where(q.Article.Status.Eq(int(m.ArticleStatusEnabled)))
|
||||
|
||||
switch {
|
||||
case req.ID != nil && req.Code != nil:
|
||||
return nil, core.NewBizErr("文档 ID 和编码不能同时提供")
|
||||
case req.ID != nil:
|
||||
if *req.ID <= 0 {
|
||||
return nil, core.NewBizErr("文档 ID 不正确")
|
||||
}
|
||||
do = do.Where(q.Article.ID.Eq(*req.ID))
|
||||
case req.Code != nil:
|
||||
code, err := normalizeArticleCode(*req.Code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
do = do.Where(q.Article.Code.Eq(code))
|
||||
default:
|
||||
return nil, core.NewBizErr("必须提供文档 ID 或编码")
|
||||
}
|
||||
|
||||
article, err := do.Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, core.NewBizErr("文档不存在")
|
||||
}
|
||||
@@ -355,6 +409,7 @@ func (s *articleService) GetPublic(id int32) (*ArticlePublicDetail, error) {
|
||||
|
||||
return &ArticlePublicDetail{
|
||||
ID: article.ID,
|
||||
Code: article.Code,
|
||||
Title: article.Title,
|
||||
Content: article.Content,
|
||||
UpdatedAt: article.UpdatedAt,
|
||||
@@ -368,6 +423,7 @@ func (s *articleService) GetPublic(id int32) (*ArticlePublicDetail, error) {
|
||||
|
||||
type ArticlePublicDetail struct {
|
||||
ID int32 `json:"id"`
|
||||
Code *string `json:"code,omitempty"`
|
||||
Title string `json:"title"`
|
||||
Content *string `json:"content,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
@@ -387,3 +443,22 @@ func (s *articleService) ensureGroupExists(groupID int32) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func optionalArticleCode(code *string) (*string, error) {
|
||||
if code == nil || strings.TrimSpace(*code) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
normalized, err := normalizeArticleCode(*code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &normalized, nil
|
||||
}
|
||||
|
||||
func normalizeArticleCode(code string) (string, error) {
|
||||
normalized := strings.TrimSpace(code)
|
||||
if !articleCodePattern.MatchString(normalized) {
|
||||
return "", core.NewBizErr("文档编码只能包含小写字母、数字、下划线或连字符,长度为 1-64 位")
|
||||
}
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user