固定文章编码

This commit is contained in:
2026-07-21 15:30:33 +08:00
parent d961538027
commit 83d38956ca
7 changed files with 149 additions and 24 deletions

View File

@@ -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
}