实现文章与分组管理

This commit is contained in:
2026-06-01 15:31:25 +08:00
parent 32e56b1a0f
commit 0dfbbe5939
15 changed files with 1554 additions and 3 deletions

View File

@@ -2,8 +2,6 @@
---
用反射实现环境变量解析,以简化函数签名
错误提示增强,展示整链路信息
交易信息持久化

View File

@@ -35,6 +35,8 @@ func main() {
m.Admin{},
m.AdminRole{},
m.Announcement{},
m.Article{},
m.ArticleGroup{},
m.Bill{},
m.Channel{},
m.Client{},

View File

@@ -128,7 +128,9 @@ insert into permission (name, description, sort) values
('bill', '账单', 13),
('balance_activity', '余额变动', 14),
('proxy', '代理', 15),
('coupon_user', '已发放优惠券', 16);
('coupon_user', '已发放优惠券', 16),
('article', '文档', 17),
('article_group', '文档分组', 18);
-- --------------------------
-- level 2
@@ -215,6 +217,16 @@ insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'coupon_user' and deleted_at is null), 'coupon_user:read', '读取已发放优惠券列表', 1),
((select id from permission where name = 'coupon_user' and deleted_at is null), 'coupon_user:write', '编辑已发放优惠券', 2);
-- article 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'article' and deleted_at is null), 'article:read', '读取文档列表', 1),
((select id from permission where name = 'article' and deleted_at is null), 'article:write', '编辑文档', 2);
-- article_group 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'article_group' and deleted_at is null), 'article_group:read', '读取文档分组列表', 1),
((select id from permission where name = 'article_group' and deleted_at is null), 'article_group:write', '编辑文档分组', 2);
-- --------------------------
-- level 3
-- --------------------------

View File

@@ -143,6 +143,62 @@ comment on column announcement.created_at is '创建时间';
comment on column announcement.updated_at is '更新时间';
comment on column announcement.deleted_at is '删除时间';
-- article_group
drop table if exists article_group cascade;
create table article_group (
id int generated by default as identity primary key,
name text not null,
code text not null,
sort int not null default 0,
status int not null default 1,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create unique index udx_article_group_code on article_group (code) where deleted_at is null;
create index idx_article_group_status on article_group (status) where deleted_at is null;
create index idx_article_group_sort on article_group (sort) where deleted_at is null;
-- article_group表字段注释
comment on table article_group is '文章分组表';
comment on column article_group.id is '分组ID';
comment on column article_group.name is '分组名称';
comment on column article_group.code is '分组编码';
comment on column article_group.sort is '分组排序';
comment on column article_group.status is '分组状态0-禁用1-正常';
comment on column article_group.created_at is '创建时间';
comment on column article_group.updated_at is '更新时间';
comment on column article_group.deleted_at is '删除时间';
-- article
drop table if exists article cascade;
create table article (
id int generated by default as identity primary key,
group_id int not null,
title text not null,
content text,
sort int not null default 0,
status int not null default 1,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create index idx_article_group_id on article (group_id) where deleted_at is null;
create index idx_article_status on article (status) where deleted_at is null;
create index idx_article_sort on article (sort) where deleted_at is null;
-- article表字段注释
comment on table article is '文章表';
comment on column article.id is '文章ID';
comment on column article.group_id is '分组ID';
comment on column article.title is '文章标题';
comment on column article.content is '文章内容';
comment on column article.sort is '文章排序';
comment on column article.status is '文章状态0-禁用1-正常';
comment on column article.created_at is '创建时间';
comment on column article.updated_at is '更新时间';
comment on column article.deleted_at is '删除时间';
-- inquiry
drop table if exists inquiry cascade;
create table inquiry (
@@ -1228,6 +1284,10 @@ alter table coupon_user
alter table coupon_user
add constraint fk_coupon_user_coupon_id foreign key (coupon_id) references coupon (id) on delete cascade;
-- article表外键
alter table article
add constraint fk_article_group_id foreign key (group_id) references article_group (id) on delete restrict;
-- product_sku表外键
alter table product_sku
add constraint fk_product_sku_product_id foreign key (product_id) references product (id) on delete cascade;

View File

@@ -74,6 +74,14 @@ const (
ScopeProxyWrite = string("proxy:write") // 写入代理
ScopeProxyWriteStatus = string("proxy:write:status") // 更改代理状态
ScopeArticle = string("article") // 文档
ScopeArticleRead = string("article:read") // 读取文档列表
ScopeArticleWrite = string("article:write") // 写入文档
ScopeArticleGroup = string("article_group") // 文档分组
ScopeArticleGroupRead = string("article_group:read") // 读取文档分组列表
ScopeArticleGroupWrite = string("article_group:write") // 写入文档分组
ScopeTrade = string("trade") // 交易
ScopeTradeRead = string("trade:read") // 读取交易列表
ScopeTradeReadOfUser = string("trade:read:of_user") // 读取指定用户的交易列表

128
web/handlers/article.go Normal file
View File

@@ -0,0 +1,128 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func NavArticle(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil {
return err
}
list, err := s.Article.Nav()
if err != nil {
return err
}
return c.JSON(list)
}
func GetArticle(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
article, err := s.Article.GetPublic(req.Id)
if err != nil {
return err
}
return c.JSON(article)
}
func PageArticleByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleRead)
if err != nil {
return err
}
var req s.PageArticleReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, total, err := s.Article.Page(&req)
if err != nil {
return err
}
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: list,
})
}
func GetArticleByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleRead)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
article, err := s.Article.GetByAdmin(req.Id)
if err != nil {
return err
}
return c.JSON(article)
}
func CreateArticle(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleWrite)
if err != nil {
return err
}
var req s.CreateArticleData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
return s.Article.Create(req)
}
func UpdateArticle(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleWrite)
if err != nil {
return err
}
var req s.UpdateArticleData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
return s.Article.Update(req)
}
func DeleteArticle(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
return s.Article.Delete(req.Id)
}

View File

@@ -0,0 +1,90 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func PageArticleGroupByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleGroupRead)
if err != nil {
return err
}
var req s.PageArticleGroupReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, total, err := s.ArticleGroup.Page(&req)
if err != nil {
return err
}
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: list,
})
}
func ListArticleGroupByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleGroupRead)
if err != nil {
return err
}
list, err := s.ArticleGroup.All()
if err != nil {
return err
}
return c.JSON(list)
}
func CreateArticleGroup(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleGroupWrite)
if err != nil {
return err
}
var req s.CreateArticleGroupData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
return s.ArticleGroup.Create(req)
}
func UpdateArticleGroup(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleGroupWrite)
if err != nil {
return err
}
var req s.UpdateArticleGroupData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
return s.ArticleGroup.Update(req)
}
func DeleteArticleGroup(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeArticleGroupWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
return s.ArticleGroup.Delete(req.Id)
}

23
web/models/article.go Normal file
View File

@@ -0,0 +1,23 @@
package models
import "platform/web/core"
// Article 文章表
type Article struct {
core.Model
GroupID int32 `json:"group_id" gorm:"column:group_id"` // 分组ID
Title string `json:"title" gorm:"column:title"` // 文章标题
Content *string `json:"content,omitempty" gorm:"column:content"` // 文章内容
Sort int32 `json:"sort" gorm:"column:sort"` // 文章排序
Status ArticleStatus `json:"status" gorm:"column:status"` // 文章状态0-禁用1-正常
Group *ArticleGroup `json:"group,omitempty" gorm:"foreignKey:GroupID"` // 分组
}
// ArticleStatus 文章状态
type ArticleStatus int
const (
ArticleStatusDisabled ArticleStatus = 0 // 禁用
ArticleStatusEnabled ArticleStatus = 1 // 正常
)

View File

@@ -0,0 +1,20 @@
package models
import "platform/web/core"
// ArticleGroup 文章分组表
type ArticleGroup struct {
core.Model
Name string `json:"name" gorm:"column:name"` // 分组名称
Code string `json:"code" gorm:"column:code"` // 分组编码
Sort int32 `json:"sort" gorm:"column:sort"` // 分组排序
Status ArticleGroupStatus `json:"status" gorm:"column:status"` // 分组状态0-禁用1-正常
}
// ArticleGroupStatus 分组状态
type ArticleGroupStatus int
const (
ArticleGroupStatusDisabled ArticleGroupStatus = 0 // 禁用
ArticleGroupStatusEnabled ArticleGroupStatus = 1 // 正常
)

442
web/queries/article.gen.go Normal file
View File

@@ -0,0 +1,442 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package queries
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"platform/web/models"
)
func newArticle(db *gorm.DB, opts ...gen.DOOption) article {
_article := article{}
_article.articleDo.UseDB(db, opts...)
_article.articleDo.UseModel(&models.Article{})
tableName := _article.articleDo.TableName()
_article.ALL = field.NewAsterisk(tableName)
_article.ID = field.NewInt32(tableName, "id")
_article.CreatedAt = field.NewTime(tableName, "created_at")
_article.UpdatedAt = field.NewTime(tableName, "updated_at")
_article.DeletedAt = field.NewField(tableName, "deleted_at")
_article.GroupID = field.NewInt32(tableName, "group_id")
_article.Title = field.NewString(tableName, "title")
_article.Content = field.NewString(tableName, "content")
_article.Sort = field.NewInt32(tableName, "sort")
_article.Status = field.NewInt(tableName, "status")
_article.Group = articleBelongsToGroup{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Group", "models.ArticleGroup"),
}
_article.fillFieldMap()
return _article
}
type article struct {
articleDo
ALL field.Asterisk
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
GroupID field.Int32
Title field.String
Content field.String
Sort field.Int32
Status field.Int
Group articleBelongsToGroup
fieldMap map[string]field.Expr
}
func (a article) Table(newTableName string) *article {
a.articleDo.UseTable(newTableName)
return a.updateTableName(newTableName)
}
func (a article) As(alias string) *article {
a.articleDo.DO = *(a.articleDo.As(alias).(*gen.DO))
return a.updateTableName(alias)
}
func (a *article) updateTableName(table string) *article {
a.ALL = field.NewAsterisk(table)
a.ID = field.NewInt32(table, "id")
a.CreatedAt = field.NewTime(table, "created_at")
a.UpdatedAt = field.NewTime(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.GroupID = field.NewInt32(table, "group_id")
a.Title = field.NewString(table, "title")
a.Content = field.NewString(table, "content")
a.Sort = field.NewInt32(table, "sort")
a.Status = field.NewInt(table, "status")
a.fillFieldMap()
return a
}
func (a *article) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := a.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (a *article) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 10)
a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
a.fieldMap["group_id"] = a.GroupID
a.fieldMap["title"] = a.Title
a.fieldMap["content"] = a.Content
a.fieldMap["sort"] = a.Sort
a.fieldMap["status"] = a.Status
}
func (a article) clone(db *gorm.DB) article {
a.articleDo.ReplaceConnPool(db.Statement.ConnPool)
a.Group.db = db.Session(&gorm.Session{Initialized: true})
a.Group.db.Statement.ConnPool = db.Statement.ConnPool
return a
}
func (a article) replaceDB(db *gorm.DB) article {
a.articleDo.ReplaceDB(db)
a.Group.db = db.Session(&gorm.Session{})
return a
}
type articleBelongsToGroup struct {
db *gorm.DB
field.RelationField
}
func (a articleBelongsToGroup) Where(conds ...field.Expr) *articleBelongsToGroup {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a articleBelongsToGroup) WithContext(ctx context.Context) *articleBelongsToGroup {
a.db = a.db.WithContext(ctx)
return &a
}
func (a articleBelongsToGroup) Session(session *gorm.Session) *articleBelongsToGroup {
a.db = a.db.Session(session)
return &a
}
func (a articleBelongsToGroup) Model(m *models.Article) *articleBelongsToGroupTx {
return &articleBelongsToGroupTx{a.db.Model(m).Association(a.Name())}
}
func (a articleBelongsToGroup) Unscoped() *articleBelongsToGroup {
a.db = a.db.Unscoped()
return &a
}
type articleBelongsToGroupTx struct{ tx *gorm.Association }
func (a articleBelongsToGroupTx) Find() (result *models.ArticleGroup, err error) {
return result, a.tx.Find(&result)
}
func (a articleBelongsToGroupTx) Append(values ...*models.ArticleGroup) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a articleBelongsToGroupTx) Replace(values ...*models.ArticleGroup) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a articleBelongsToGroupTx) Delete(values ...*models.ArticleGroup) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a articleBelongsToGroupTx) Clear() error {
return a.tx.Clear()
}
func (a articleBelongsToGroupTx) Count() int64 {
return a.tx.Count()
}
func (a articleBelongsToGroupTx) Unscoped() *articleBelongsToGroupTx {
a.tx = a.tx.Unscoped()
return &a
}
type articleDo struct{ gen.DO }
func (a articleDo) Debug() *articleDo {
return a.withDO(a.DO.Debug())
}
func (a articleDo) WithContext(ctx context.Context) *articleDo {
return a.withDO(a.DO.WithContext(ctx))
}
func (a articleDo) ReadDB() *articleDo {
return a.Clauses(dbresolver.Read)
}
func (a articleDo) WriteDB() *articleDo {
return a.Clauses(dbresolver.Write)
}
func (a articleDo) Session(config *gorm.Session) *articleDo {
return a.withDO(a.DO.Session(config))
}
func (a articleDo) Clauses(conds ...clause.Expression) *articleDo {
return a.withDO(a.DO.Clauses(conds...))
}
func (a articleDo) Returning(value interface{}, columns ...string) *articleDo {
return a.withDO(a.DO.Returning(value, columns...))
}
func (a articleDo) Not(conds ...gen.Condition) *articleDo {
return a.withDO(a.DO.Not(conds...))
}
func (a articleDo) Or(conds ...gen.Condition) *articleDo {
return a.withDO(a.DO.Or(conds...))
}
func (a articleDo) Select(conds ...field.Expr) *articleDo {
return a.withDO(a.DO.Select(conds...))
}
func (a articleDo) Where(conds ...gen.Condition) *articleDo {
return a.withDO(a.DO.Where(conds...))
}
func (a articleDo) Order(conds ...field.Expr) *articleDo {
return a.withDO(a.DO.Order(conds...))
}
func (a articleDo) Distinct(cols ...field.Expr) *articleDo {
return a.withDO(a.DO.Distinct(cols...))
}
func (a articleDo) Omit(cols ...field.Expr) *articleDo {
return a.withDO(a.DO.Omit(cols...))
}
func (a articleDo) Join(table schema.Tabler, on ...field.Expr) *articleDo {
return a.withDO(a.DO.Join(table, on...))
}
func (a articleDo) LeftJoin(table schema.Tabler, on ...field.Expr) *articleDo {
return a.withDO(a.DO.LeftJoin(table, on...))
}
func (a articleDo) RightJoin(table schema.Tabler, on ...field.Expr) *articleDo {
return a.withDO(a.DO.RightJoin(table, on...))
}
func (a articleDo) Group(cols ...field.Expr) *articleDo {
return a.withDO(a.DO.Group(cols...))
}
func (a articleDo) Having(conds ...gen.Condition) *articleDo {
return a.withDO(a.DO.Having(conds...))
}
func (a articleDo) Limit(limit int) *articleDo {
return a.withDO(a.DO.Limit(limit))
}
func (a articleDo) Offset(offset int) *articleDo {
return a.withDO(a.DO.Offset(offset))
}
func (a articleDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *articleDo {
return a.withDO(a.DO.Scopes(funcs...))
}
func (a articleDo) Unscoped() *articleDo {
return a.withDO(a.DO.Unscoped())
}
func (a articleDo) Create(values ...*models.Article) error {
if len(values) == 0 {
return nil
}
return a.DO.Create(values)
}
func (a articleDo) CreateInBatches(values []*models.Article, batchSize int) error {
return a.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (a articleDo) Save(values ...*models.Article) error {
if len(values) == 0 {
return nil
}
return a.DO.Save(values)
}
func (a articleDo) First() (*models.Article, error) {
if result, err := a.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.Article), nil
}
}
func (a articleDo) Take() (*models.Article, error) {
if result, err := a.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.Article), nil
}
}
func (a articleDo) Last() (*models.Article, error) {
if result, err := a.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.Article), nil
}
}
func (a articleDo) Find() ([]*models.Article, error) {
result, err := a.DO.Find()
return result.([]*models.Article), err
}
func (a articleDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Article, err error) {
buf := make([]*models.Article, 0, batchSize)
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (a articleDo) FindInBatches(result *[]*models.Article, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return a.DO.FindInBatches(result, batchSize, fc)
}
func (a articleDo) Attrs(attrs ...field.AssignExpr) *articleDo {
return a.withDO(a.DO.Attrs(attrs...))
}
func (a articleDo) Assign(attrs ...field.AssignExpr) *articleDo {
return a.withDO(a.DO.Assign(attrs...))
}
func (a articleDo) Joins(fields ...field.RelationField) *articleDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Joins(_f))
}
return &a
}
func (a articleDo) Preload(fields ...field.RelationField) *articleDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Preload(_f))
}
return &a
}
func (a articleDo) FirstOrInit() (*models.Article, error) {
if result, err := a.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.Article), nil
}
}
func (a articleDo) FirstOrCreate() (*models.Article, error) {
if result, err := a.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.Article), nil
}
}
func (a articleDo) FindByPage(offset int, limit int) (result []*models.Article, count int64, err error) {
result, err = a.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = a.Offset(-1).Limit(-1).Count()
return
}
func (a articleDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = a.Count()
if err != nil {
return
}
err = a.Offset(offset).Limit(limit).Scan(result)
return
}
func (a articleDo) Scan(result interface{}) (err error) {
return a.DO.Scan(result)
}
func (a articleDo) Delete(models ...*models.Article) (result gen.ResultInfo, err error) {
return a.DO.Delete(models)
}
func (a *articleDo) withDO(do gen.Dao) *articleDo {
a.DO = *do.(*gen.DO)
return a
}

View File

@@ -0,0 +1,347 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package queries
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"platform/web/models"
)
func newArticleGroup(db *gorm.DB, opts ...gen.DOOption) articleGroup {
_articleGroup := articleGroup{}
_articleGroup.articleGroupDo.UseDB(db, opts...)
_articleGroup.articleGroupDo.UseModel(&models.ArticleGroup{})
tableName := _articleGroup.articleGroupDo.TableName()
_articleGroup.ALL = field.NewAsterisk(tableName)
_articleGroup.ID = field.NewInt32(tableName, "id")
_articleGroup.CreatedAt = field.NewTime(tableName, "created_at")
_articleGroup.UpdatedAt = field.NewTime(tableName, "updated_at")
_articleGroup.DeletedAt = field.NewField(tableName, "deleted_at")
_articleGroup.Name = field.NewString(tableName, "name")
_articleGroup.Code = field.NewString(tableName, "code")
_articleGroup.Sort = field.NewInt32(tableName, "sort")
_articleGroup.Status = field.NewInt(tableName, "status")
_articleGroup.fillFieldMap()
return _articleGroup
}
type articleGroup struct {
articleGroupDo
ALL field.Asterisk
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Name field.String
Code field.String
Sort field.Int32
Status field.Int
fieldMap map[string]field.Expr
}
func (a articleGroup) Table(newTableName string) *articleGroup {
a.articleGroupDo.UseTable(newTableName)
return a.updateTableName(newTableName)
}
func (a articleGroup) As(alias string) *articleGroup {
a.articleGroupDo.DO = *(a.articleGroupDo.As(alias).(*gen.DO))
return a.updateTableName(alias)
}
func (a *articleGroup) updateTableName(table string) *articleGroup {
a.ALL = field.NewAsterisk(table)
a.ID = field.NewInt32(table, "id")
a.CreatedAt = field.NewTime(table, "created_at")
a.UpdatedAt = field.NewTime(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.Name = field.NewString(table, "name")
a.Code = field.NewString(table, "code")
a.Sort = field.NewInt32(table, "sort")
a.Status = field.NewInt(table, "status")
a.fillFieldMap()
return a
}
func (a *articleGroup) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := a.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (a *articleGroup) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 8)
a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
a.fieldMap["name"] = a.Name
a.fieldMap["code"] = a.Code
a.fieldMap["sort"] = a.Sort
a.fieldMap["status"] = a.Status
}
func (a articleGroup) clone(db *gorm.DB) articleGroup {
a.articleGroupDo.ReplaceConnPool(db.Statement.ConnPool)
return a
}
func (a articleGroup) replaceDB(db *gorm.DB) articleGroup {
a.articleGroupDo.ReplaceDB(db)
return a
}
type articleGroupDo struct{ gen.DO }
func (a articleGroupDo) Debug() *articleGroupDo {
return a.withDO(a.DO.Debug())
}
func (a articleGroupDo) WithContext(ctx context.Context) *articleGroupDo {
return a.withDO(a.DO.WithContext(ctx))
}
func (a articleGroupDo) ReadDB() *articleGroupDo {
return a.Clauses(dbresolver.Read)
}
func (a articleGroupDo) WriteDB() *articleGroupDo {
return a.Clauses(dbresolver.Write)
}
func (a articleGroupDo) Session(config *gorm.Session) *articleGroupDo {
return a.withDO(a.DO.Session(config))
}
func (a articleGroupDo) Clauses(conds ...clause.Expression) *articleGroupDo {
return a.withDO(a.DO.Clauses(conds...))
}
func (a articleGroupDo) Returning(value interface{}, columns ...string) *articleGroupDo {
return a.withDO(a.DO.Returning(value, columns...))
}
func (a articleGroupDo) Not(conds ...gen.Condition) *articleGroupDo {
return a.withDO(a.DO.Not(conds...))
}
func (a articleGroupDo) Or(conds ...gen.Condition) *articleGroupDo {
return a.withDO(a.DO.Or(conds...))
}
func (a articleGroupDo) Select(conds ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.Select(conds...))
}
func (a articleGroupDo) Where(conds ...gen.Condition) *articleGroupDo {
return a.withDO(a.DO.Where(conds...))
}
func (a articleGroupDo) Order(conds ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.Order(conds...))
}
func (a articleGroupDo) Distinct(cols ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.Distinct(cols...))
}
func (a articleGroupDo) Omit(cols ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.Omit(cols...))
}
func (a articleGroupDo) Join(table schema.Tabler, on ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.Join(table, on...))
}
func (a articleGroupDo) LeftJoin(table schema.Tabler, on ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.LeftJoin(table, on...))
}
func (a articleGroupDo) RightJoin(table schema.Tabler, on ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.RightJoin(table, on...))
}
func (a articleGroupDo) Group(cols ...field.Expr) *articleGroupDo {
return a.withDO(a.DO.Group(cols...))
}
func (a articleGroupDo) Having(conds ...gen.Condition) *articleGroupDo {
return a.withDO(a.DO.Having(conds...))
}
func (a articleGroupDo) Limit(limit int) *articleGroupDo {
return a.withDO(a.DO.Limit(limit))
}
func (a articleGroupDo) Offset(offset int) *articleGroupDo {
return a.withDO(a.DO.Offset(offset))
}
func (a articleGroupDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *articleGroupDo {
return a.withDO(a.DO.Scopes(funcs...))
}
func (a articleGroupDo) Unscoped() *articleGroupDo {
return a.withDO(a.DO.Unscoped())
}
func (a articleGroupDo) Create(values ...*models.ArticleGroup) error {
if len(values) == 0 {
return nil
}
return a.DO.Create(values)
}
func (a articleGroupDo) CreateInBatches(values []*models.ArticleGroup, batchSize int) error {
return a.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (a articleGroupDo) Save(values ...*models.ArticleGroup) error {
if len(values) == 0 {
return nil
}
return a.DO.Save(values)
}
func (a articleGroupDo) First() (*models.ArticleGroup, error) {
if result, err := a.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.ArticleGroup), nil
}
}
func (a articleGroupDo) Take() (*models.ArticleGroup, error) {
if result, err := a.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.ArticleGroup), nil
}
}
func (a articleGroupDo) Last() (*models.ArticleGroup, error) {
if result, err := a.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.ArticleGroup), nil
}
}
func (a articleGroupDo) Find() ([]*models.ArticleGroup, error) {
result, err := a.DO.Find()
return result.([]*models.ArticleGroup), err
}
func (a articleGroupDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.ArticleGroup, err error) {
buf := make([]*models.ArticleGroup, 0, batchSize)
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (a articleGroupDo) FindInBatches(result *[]*models.ArticleGroup, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return a.DO.FindInBatches(result, batchSize, fc)
}
func (a articleGroupDo) Attrs(attrs ...field.AssignExpr) *articleGroupDo {
return a.withDO(a.DO.Attrs(attrs...))
}
func (a articleGroupDo) Assign(attrs ...field.AssignExpr) *articleGroupDo {
return a.withDO(a.DO.Assign(attrs...))
}
func (a articleGroupDo) Joins(fields ...field.RelationField) *articleGroupDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Joins(_f))
}
return &a
}
func (a articleGroupDo) Preload(fields ...field.RelationField) *articleGroupDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Preload(_f))
}
return &a
}
func (a articleGroupDo) FirstOrInit() (*models.ArticleGroup, error) {
if result, err := a.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.ArticleGroup), nil
}
}
func (a articleGroupDo) FirstOrCreate() (*models.ArticleGroup, error) {
if result, err := a.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.ArticleGroup), nil
}
}
func (a articleGroupDo) FindByPage(offset int, limit int) (result []*models.ArticleGroup, count int64, err error) {
result, err = a.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = a.Offset(-1).Limit(-1).Count()
return
}
func (a articleGroupDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = a.Count()
if err != nil {
return
}
err = a.Offset(offset).Limit(limit).Scan(result)
return
}
func (a articleGroupDo) Scan(result interface{}) (err error) {
return a.DO.Scan(result)
}
func (a articleGroupDo) Delete(models ...*models.ArticleGroup) (result gen.ResultInfo, err error) {
return a.DO.Delete(models)
}
func (a *articleGroupDo) withDO(do gen.Dao) *articleGroupDo {
a.DO = *do.(*gen.DO)
return a
}

View File

@@ -20,6 +20,8 @@ var (
Admin *admin
AdminRole *adminRole
Announcement *announcement
Article *article
ArticleGroup *articleGroup
BalanceActivity *balanceActivity
Bill *bill
Channel *channel
@@ -59,6 +61,8 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
Admin = &Q.Admin
AdminRole = &Q.AdminRole
Announcement = &Q.Announcement
Article = &Q.Article
ArticleGroup = &Q.ArticleGroup
BalanceActivity = &Q.BalanceActivity
Bill = &Q.Bill
Channel = &Q.Channel
@@ -99,6 +103,8 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
Admin: newAdmin(db, opts...),
AdminRole: newAdminRole(db, opts...),
Announcement: newAnnouncement(db, opts...),
Article: newArticle(db, opts...),
ArticleGroup: newArticleGroup(db, opts...),
BalanceActivity: newBalanceActivity(db, opts...),
Bill: newBill(db, opts...),
Channel: newChannel(db, opts...),
@@ -140,6 +146,8 @@ type Query struct {
Admin admin
AdminRole adminRole
Announcement announcement
Article article
ArticleGroup articleGroup
BalanceActivity balanceActivity
Bill bill
Channel channel
@@ -182,6 +190,8 @@ func (q *Query) clone(db *gorm.DB) *Query {
Admin: q.Admin.clone(db),
AdminRole: q.AdminRole.clone(db),
Announcement: q.Announcement.clone(db),
Article: q.Article.clone(db),
ArticleGroup: q.ArticleGroup.clone(db),
BalanceActivity: q.BalanceActivity.clone(db),
Bill: q.Bill.clone(db),
Channel: q.Channel.clone(db),
@@ -231,6 +241,8 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
Admin: q.Admin.replaceDB(db),
AdminRole: q.AdminRole.replaceDB(db),
Announcement: q.Announcement.replaceDB(db),
Article: q.Article.replaceDB(db),
ArticleGroup: q.ArticleGroup.replaceDB(db),
BalanceActivity: q.BalanceActivity.replaceDB(db),
Bill: q.Bill.replaceDB(db),
Channel: q.Channel.replaceDB(db),
@@ -270,6 +282,8 @@ type queryCtx struct {
Admin *adminDo
AdminRole *adminRoleDo
Announcement *announcementDo
Article *articleDo
ArticleGroup *articleGroupDo
BalanceActivity *balanceActivityDo
Bill *billDo
Channel *channelDo
@@ -309,6 +323,8 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
Admin: q.Admin.WithContext(ctx),
AdminRole: q.AdminRole.WithContext(ctx),
Announcement: q.Announcement.WithContext(ctx),
Article: q.Article.WithContext(ctx),
ArticleGroup: q.ArticleGroup.WithContext(ctx),
BalanceActivity: q.BalanceActivity.WithContext(ctx),
Bill: q.Bill.WithContext(ctx),
Channel: q.Channel.WithContext(ctx),

View File

@@ -160,6 +160,10 @@ func clientRouter(api fiber.Router) {
channel := client.Group("/channel")
channel.Post("/remove", handlers.RemoveChannels)
// 文章查看
article := api.Group("/article")
article.Post("/nav", handlers.NavArticle)
article.Post("/get", handlers.GetArticle)
}
// 管理员接口路由
@@ -286,4 +290,20 @@ func adminRouter(api fiber.Router) {
couponUser.Post("/create", handlers.CreateCouponUserByAdmin)
couponUser.Post("/update", handlers.UpdateCouponUserByAdmin)
couponUser.Post("/remove", handlers.DeleteCouponUserByAdmin)
// article 文档
var article = api.Group("/article")
article.Post("/page", handlers.PageArticleByAdmin)
article.Post("/get", handlers.GetArticleByAdmin)
article.Post("/create", handlers.CreateArticle)
article.Post("/update", handlers.UpdateArticle)
article.Post("/remove", handlers.DeleteArticle)
// article-group 文档分组
var articleGroup = api.Group("/article-group")
articleGroup.Post("/page", handlers.PageArticleGroupByAdmin)
articleGroup.Post("/list", handlers.ListArticleGroupByAdmin)
articleGroup.Post("/create", handlers.CreateArticleGroup)
articleGroup.Post("/update", handlers.UpdateArticleGroup)
articleGroup.Post("/remove", handlers.DeleteArticleGroup)
}

257
web/services/article.go Normal file
View File

@@ -0,0 +1,257 @@
package services
import (
"errors"
"platform/pkg/u"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"gorm.io/gen/field"
"gorm.io/gorm"
)
var Article = &articleService{}
type articleService struct{}
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 + "%"))
}
if req.GroupID != nil {
do = do.Where(q.Article.GroupID.Eq(*req.GroupID))
}
if req.Status != nil {
do = do.Where(q.Article.Status.Eq(int(*req.Status)))
}
return q.Article.
Preload(q.Article.Group).
Where(do).
Omit(q.Article.Content).
Order(q.Article.Sort, q.Article.CreatedAt).
FindByPage(req.GetOffset(), req.GetLimit())
}
type PageArticleReq struct {
core.PageReq
Keyword *string `json:"keyword,omitempty"`
GroupID *int32 `json:"group_id,omitempty"`
Status *m.ArticleStatus `json:"status,omitempty"`
}
func (s *articleService) GetByAdmin(id int32) (*m.Article, error) {
article, err := q.Article.
Preload(q.Article.Group).
Where(q.Article.ID.Eq(id)).
Take()
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, core.NewBizErr("文档不存在")
}
if err != nil {
return nil, err
}
return article, nil
}
func (s *articleService) Create(data CreateArticleData) error {
if err := s.ensureGroupExists(data.GroupID); err != nil {
return err
}
return q.Article.Create(&m.Article{
GroupID: data.GroupID,
Title: data.Title,
Content: data.Content,
Sort: u.Else(data.Sort, 0),
Status: u.Else(data.Status, m.ArticleStatusEnabled),
})
}
type CreateArticleData struct {
GroupID int32 `json:"group_id" validate:"required"`
Title string `json:"title" validate:"required"`
Content *string `json:"content"`
Sort *int32 `json:"sort"`
Status *m.ArticleStatus `json:"status"`
}
func (s *articleService) Update(data UpdateArticleData) error {
if data.GroupID != nil {
if err := s.ensureGroupExists(*data.GroupID); err != nil {
return err
}
}
do := make([]field.AssignExpr, 0)
if data.GroupID != nil {
do = append(do, q.Article.GroupID.Value(*data.GroupID))
}
if data.Title != nil {
do = append(do, q.Article.Title.Value(*data.Title))
}
if data.Content != nil {
do = append(do, q.Article.Content.Value(*data.Content))
}
if data.Sort != nil {
do = append(do, q.Article.Sort.Value(*data.Sort))
}
if data.Status != nil {
do = append(do, q.Article.Status.Value(int(*data.Status)))
}
if len(do) == 0 {
return nil
}
r, err := q.Article.Where(q.Article.ID.Eq(data.ID)).UpdateSimple(do...)
if err != nil {
return err
}
if r.RowsAffected == 0 {
return core.NewBizErr("文档状态已过期")
}
return nil
}
type UpdateArticleData struct {
ID int32 `json:"id" validate:"required"`
GroupID *int32 `json:"group_id"`
Title *string `json:"title"`
Content *string `json:"content"`
Sort *int32 `json:"sort"`
Status *m.ArticleStatus `json:"status"`
}
func (s *articleService) Delete(id int32) error {
r, err := q.Article.Where(q.Article.ID.Eq(id)).UpdateColumn(q.Article.DeletedAt, time.Now())
if err != nil {
return err
}
if r.RowsAffected == 0 {
return core.NewBizErr("文档状态已过期")
}
return nil
}
func (s *articleService) Nav() ([]*ArticleNavGroup, error) {
groups, err := q.ArticleGroup.
Where(q.ArticleGroup.Status.Eq(int(m.ArticleGroupStatusEnabled))).
Order(q.ArticleGroup.Sort, q.ArticleGroup.CreatedAt).
Find()
if err != nil {
return nil, err
}
if len(groups) == 0 {
return []*ArticleNavGroup{}, nil
}
groupIDs := make([]int32, 0, len(groups))
result := make([]*ArticleNavGroup, 0, len(groups))
groupMap := make(map[int32]*ArticleNavGroup, len(groups))
for _, group := range groups {
groupIDs = append(groupIDs, group.ID)
item := &ArticleNavGroup{
ID: group.ID,
Name: group.Name,
Code: group.Code,
Articles: []*ArticleNavArticle{},
}
result = append(result, item)
groupMap[group.ID] = item
}
articles, err := q.Article.
Where(
q.Article.GroupID.In(groupIDs...),
q.Article.Status.Eq(int(m.ArticleStatusEnabled)),
).
Omit(q.Article.Content).
Order(q.Article.Sort, q.Article.CreatedAt).
Find()
if err != nil {
return nil, err
}
for _, article := range articles {
group := groupMap[article.GroupID]
if group == nil {
continue
}
group.Articles = append(group.Articles, &ArticleNavArticle{
ID: article.ID,
Title: article.Title,
UpdatedAt: article.UpdatedAt,
})
}
return result, nil
}
type ArticleNavGroup struct {
ID int32 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
Articles []*ArticleNavArticle `json:"articles"`
}
type ArticleNavArticle struct {
ID int32 `json:"id"`
Title string `json:"title"`
UpdatedAt time.Time `json:"updated_at"`
}
func (s *articleService) GetPublic(id int32) (*ArticlePublicDetail, error) {
article, err := q.Article.
Preload(q.Article.Group).
Where(
q.Article.ID.Eq(id),
q.Article.Status.Eq(int(m.ArticleStatusEnabled)),
).
Take()
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, core.NewBizErr("文档不存在")
}
if err != nil {
return nil, err
}
if article.Group == nil || article.Group.Status != m.ArticleGroupStatusEnabled {
return nil, core.NewBizErr("文档不存在")
}
return &ArticlePublicDetail{
ID: article.ID,
Title: article.Title,
Content: article.Content,
UpdatedAt: article.UpdatedAt,
Group: &ArticlePublicGroup{
ID: article.Group.ID,
Name: article.Group.Name,
Code: article.Group.Code,
},
}, nil
}
type ArticlePublicDetail struct {
ID int32 `json:"id"`
Title string `json:"title"`
Content *string `json:"content,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
Group *ArticlePublicGroup `json:"group"`
}
type ArticlePublicGroup struct {
ID int32 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
}
func (s *articleService) ensureGroupExists(groupID int32) error {
_, err := q.ArticleGroup.Where(q.ArticleGroup.ID.Eq(groupID)).Take()
if errors.Is(err, gorm.ErrRecordNotFound) {
return core.NewBizErr("文档分组不存在")
}
return err
}

View File

@@ -0,0 +1,128 @@
package services
import (
"errors"
"platform/pkg/u"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"gorm.io/gen/field"
"gorm.io/gorm"
)
var ArticleGroup = &articleGroupService{}
type articleGroupService struct{}
func (s *articleGroupService) All() (result []*m.ArticleGroup, err error) {
return q.ArticleGroup.
Order(q.ArticleGroup.Sort, q.ArticleGroup.CreatedAt).
Find()
}
func (s *articleGroupService) Page(req *PageArticleGroupReq) (result []*m.ArticleGroup, count int64, err error) {
do := q.ArticleGroup.Where()
if req.Keyword != nil && *req.Keyword != "" {
do = do.Where(
q.ArticleGroup.Where(
q.ArticleGroup.Name.Like("%" + *req.Keyword + "%"),
).Or(
q.ArticleGroup.Code.Like("%" + *req.Keyword + "%"),
),
)
}
if req.Status != nil {
do = do.Where(q.ArticleGroup.Status.Eq(int(*req.Status)))
}
return q.ArticleGroup.
Where(do).
Order(q.ArticleGroup.Sort, q.ArticleGroup.CreatedAt).
FindByPage(req.GetOffset(), req.GetLimit())
}
type PageArticleGroupReq struct {
core.PageReq
Keyword *string `json:"keyword,omitempty"`
Status *m.ArticleGroupStatus `json:"status,omitempty"`
}
func (s *articleGroupService) Create(data CreateArticleGroupData) error {
err := q.ArticleGroup.Create(&m.ArticleGroup{
Name: data.Name,
Code: data.Code,
Sort: u.Else(data.Sort, 0),
Status: u.Else(data.Status, m.ArticleGroupStatusEnabled),
})
if errors.Is(err, gorm.ErrDuplicatedKey) {
return core.NewBizErr("文档分组编码已存在")
}
return err
}
type CreateArticleGroupData struct {
Name string `json:"name" validate:"required"`
Code string `json:"code" validate:"required"`
Sort *int32 `json:"sort"`
Status *m.ArticleGroupStatus `json:"status"`
}
func (s *articleGroupService) Update(data UpdateArticleGroupData) error {
do := make([]field.AssignExpr, 0)
if data.Name != nil {
do = append(do, q.ArticleGroup.Name.Value(*data.Name))
}
if data.Code != nil {
do = append(do, q.ArticleGroup.Code.Value(*data.Code))
}
if data.Sort != nil {
do = append(do, q.ArticleGroup.Sort.Value(*data.Sort))
}
if data.Status != nil {
do = append(do, q.ArticleGroup.Status.Value(int(*data.Status)))
}
if len(do) == 0 {
return nil
}
r, err := q.ArticleGroup.Where(q.ArticleGroup.ID.Eq(data.ID)).UpdateSimple(do...)
if errors.Is(err, gorm.ErrDuplicatedKey) {
return core.NewBizErr("文档分组编码已存在")
}
if err != nil {
return err
}
if r.RowsAffected == 0 {
return core.NewBizErr("文档分组状态已过期")
}
return nil
}
type UpdateArticleGroupData struct {
ID int32 `json:"id" validate:"required"`
Name *string `json:"name"`
Code *string `json:"code"`
Sort *int32 `json:"sort"`
Status *m.ArticleGroupStatus `json:"status"`
}
func (s *articleGroupService) Delete(id int32) error {
count, err := q.Article.Where(q.Article.GroupID.Eq(id)).Count()
if err != nil {
return err
}
if count > 0 {
return core.NewBizErr("分组下仍有关联文章,无法删除")
}
r, err := q.ArticleGroup.Where(q.ArticleGroup.ID.Eq(id)).UpdateColumn(q.ArticleGroup.DeletedAt, time.Now())
if err != nil {
return err
}
if r.RowsAffected == 0 {
return core.NewBizErr("文档分组状态已过期")
}
return nil
}