作者 yangfu

内容安全

@@ -10,4 +10,5 @@ import "core/article_type.api" @@ -10,4 +10,5 @@ import "core/article_type.api"
10 import "core/article.api" 10 import "core/article.api"
11 import "core/role.api" 11 import "core/role.api"
12 import "core/department.api" 12 import "core/department.api"
13 -import "core/article_category.api"  
  13 +import "core/article_category.api"
  14 +import "core/article_security.api"
  1 +@server(
  2 + prefix: v1/system
  3 + group: secuirty
  4 + middleware: LoginStatusCheck,LogRequest
  5 + jwt: SystemAuth
  6 +)
  7 +service Core {
  8 + @doc "内容安全-搜索"
  9 + @handler articleSecuritySearch
  10 + post /article_security/search (ArticleSecuritySearchRequest) returns (ArticleSecuritySearchResponse)
  11 + @doc "详情"
  12 + @handler articleSecurityGet
  13 + get /article_security/:id (ArticleSecurityGetRequest) returns (ArticleSecurityGetResponse)
  14 + @doc "内容安全-审核"
  15 + @handler articleSecurityAudit
  16 + post /article_security/audit (ArticleSecurityAuditRequest) returns (ArticleSecurityAuditResponse)
  17 +}
  18 +
  19 +type (
  20 + ArticleSecurityGetRequest {
  21 + Id int64 `path:"id"`
  22 + }
  23 + ArticleSecurityGetResponse struct{
  24 + ArticleSecurity ArticleSecurityItem `json:"item"`
  25 + }
  26 +
  27 +// ArticleSecuritySaveRequest struct{
  28 +// ArticleSecurity ArticleSecurityItem `json:"article_security"`
  29 +// }
  30 +// ArticleSecuritySaveResponse struct{}
  31 +//
  32 +// ArticleSecurityDeleteRequest struct{
  33 +// Id int64 `path:"id"`
  34 +// }
  35 +// ArticleSecurityDeleteResponse struct{}
  36 +//
  37 +// ArticleSecurityUpdateRequest struct{
  38 +// Id int64 `path:"id"`
  39 +// ArticleSecurity ArticleSecurityItem `json:"article_security"`
  40 +// }
  41 +// ArticleSecurityUpdateResponse struct{}
  42 + ArticleSecurityAuditRequest struct{
  43 + Id int64 `json:"id"` //id
  44 + Status int `json:"status"` // 1:成功 0:失败
  45 + }
  46 + ArticleSecurityAuditResponse struct{
  47 +
  48 + }
  49 + ArticleSecuritySearchRequest struct{
  50 + Page int `json:"page,optional"`
  51 + Size int `json:"size,optional"`
  52 + ReviewStatus int `json:"reviewStatus,optional"` // 审核结果 1:待审核 2:通过 3:拒绝
  53 + Suggest string `json:"suggest"` // 建议 通过、风险、人工审核
  54 + ContentType int `json:"contentType"` // 内容类型 (1:文章 2:评论)
  55 + AuthorName string `json:"authorName"` // 作者名称
  56 + BeginTime int64 `json:"beginTime"` // 开始时间
  57 + EndTime int64 `json:"endTime"` // 结束时间
  58 + }
  59 + ArticleSecuritySearchResponse{
  60 + List []ArticleSecurityItem `json:"list"`
  61 + Total int64 `json:"total"`
  62 + }
  63 + ArticleSecurityItem struct{
  64 + Id int64 `json:"id"` // 唯一标识
  65 + ContentKeyWords string `json:"contentKeyWords"` // 内容关键字
  66 + Content ContentDetailItem `json:"content"` // 内容详情
  67 + Label string `json:"label"` // 风控标签
  68 + Prob int `json:"prob"` // 分值
  69 + Suggest string `json:"suggest"` // 建议 通过、风险、人工审核
  70 + Author string `json:"author"` // 发布人
  71 + ReleaseAt string `json:"releaseAt"` // 发布时间
  72 + ReviewStatus int `json:"reviewStatus"` // 审核结果 1:待审核 2:通过 3:拒绝
  73 +
  74 + Reviewer string `json:"reviewer"` // 审核人
  75 + ReviewAt int64 `json:"reviewAt"` // 审核时间(人工处置时间)
  76 + CheckList []CheckDetailItem `json:"checkList"` // 检查列表
  77 + }
  78 + CheckDetailItem struct{
  79 + Label string `json:"label"` // 命中标签
  80 + Prob uint `json:"prob"` // 置信度。0-100,越高代表越有可能属于当前返回的标签(label)
  81 + Suggest string `json:"suggest"` // 建议
  82 + }
  83 + ContentDetailItem struct{
  84 + Id int64 `json:"id"` // 内容ID
  85 + Type int `json:"type"` // 内容类型 (1:文章 2:评论)
  86 + Text string `json:"text"` // 内容文本
  87 + }
  88 +)
@@ -7,6 +7,7 @@ Timeout: 30000 @@ -7,6 +7,7 @@ Timeout: 30000
7 # CertFile: ./key/fjmaimaimai.com_bundle.crt 7 # CertFile: ./key/fjmaimaimai.com_bundle.crt
8 # KeyFile: ./key/fjmaimaimai.com.key 8 # KeyFile: ./key/fjmaimaimai.com.key
9 LogRequest: true # 记录详细请求日志 9 LogRequest: true # 记录详细请求日志
  10 +ContentSecurityCheck: true # 内容安全检查(调用微信接口)
10 11
11 Log: 12 Log:
12 # Mode: file 13 # Mode: file
@@ -10,13 +10,14 @@ import ( @@ -10,13 +10,14 @@ import (
10 type Config struct { 10 type Config struct {
11 rest.RestConf 11 rest.RestConf
12 config.Config 12 config.Config
13 - Redis redis.RedisConf `json:",optional"`  
14 - SystemAuth config.Auth  
15 - MiniAuth config.Auth  
16 - Migrate bool `json:",optional,default=true"`  
17 - ApiAuth ApiService  
18 - DebugSmsCode string `json:",optional,default=999512"`  
19 - LogRequest bool `json:",optional,default=true"` 13 + Redis redis.RedisConf `json:",optional"`
  14 + SystemAuth config.Auth
  15 + MiniAuth config.Auth
  16 + Migrate bool `json:",optional,default=true"`
  17 + ApiAuth ApiService
  18 + DebugSmsCode string `json:",optional,default=999512"`
  19 + LogRequest bool `json:",optional,default=true"`
  20 + ContentSecurityCheck bool `json:",optional,default=false"`
20 } 21 }
21 22
22 type ApiService struct { 23 type ApiService struct {
@@ -11,6 +11,7 @@ import ( @@ -11,6 +11,7 @@ import (
11 department "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/department" 11 department "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/department"
12 message "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/message" 12 message "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/message"
13 role "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/role" 13 role "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/role"
  14 + secuirty "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/secuirty"
14 tags "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/tags" 15 tags "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/tags"
15 user "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/user" 16 user "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/handler/user"
16 "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc" 17 "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
@@ -789,4 +790,29 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { @@ -789,4 +790,29 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
789 }, 790 },
790 rest.WithPrefix("/v1/system"), 791 rest.WithPrefix("/v1/system"),
791 ) 792 )
  793 +
  794 + server.AddRoutes(
  795 + rest.WithMiddlewares(
  796 + []rest.Middleware{serverCtx.LoginStatusCheck, serverCtx.LogRequest},
  797 + []rest.Route{
  798 + {
  799 + Method: http.MethodPost,
  800 + Path: "/article_security/search",
  801 + Handler: secuirty.ArticleSecuritySearchHandler(serverCtx),
  802 + },
  803 + {
  804 + Method: http.MethodGet,
  805 + Path: "/article_security/:id",
  806 + Handler: secuirty.ArticleSecurityGetHandler(serverCtx),
  807 + },
  808 + {
  809 + Method: http.MethodPost,
  810 + Path: "/article_security/audit",
  811 + Handler: secuirty.ArticleSecurityAuditHandler(serverCtx),
  812 + },
  813 + }...,
  814 + ),
  815 + rest.WithJwt(serverCtx.Config.SystemAuth.AccessSecret),
  816 + rest.WithPrefix("/v1/system"),
  817 + )
792 } 818 }
  1 +package secuirty
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/result"
  5 + "net/http"
  6 +
  7 + "github.com/zeromicro/go-zero/rest/httpx"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/secuirty"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  11 +)
  12 +
  13 +func ArticleSecurityAuditHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
  14 + return func(w http.ResponseWriter, r *http.Request) {
  15 + var req types.ArticleSecurityAuditRequest
  16 + if err := httpx.Parse(r, &req); err != nil {
  17 + httpx.ErrorCtx(r.Context(), w, err)
  18 + return
  19 + }
  20 +
  21 + l := secuirty.NewArticleSecurityAuditLogic(r.Context(), svcCtx)
  22 + resp, err := l.ArticleSecurityAudit(&req)
  23 + result.HttpResult(r, w, resp, err)
  24 + }
  25 +}
  1 +package secuirty
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/result"
  5 + "net/http"
  6 +
  7 + "github.com/zeromicro/go-zero/rest/httpx"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/secuirty"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  11 +)
  12 +
  13 +func ArticleSecurityGetHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
  14 + return func(w http.ResponseWriter, r *http.Request) {
  15 + var req types.ArticleSecurityGetRequest
  16 + if err := httpx.Parse(r, &req); err != nil {
  17 + httpx.ErrorCtx(r.Context(), w, err)
  18 + return
  19 + }
  20 +
  21 + l := secuirty.NewArticleSecurityGetLogic(r.Context(), svcCtx)
  22 + resp, err := l.ArticleSecurityGet(&req)
  23 + result.HttpResult(r, w, resp, err)
  24 + }
  25 +}
  1 +package secuirty
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/result"
  5 + "net/http"
  6 +
  7 + "github.com/zeromicro/go-zero/rest/httpx"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/secuirty"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  11 +)
  12 +
  13 +func ArticleSecuritySearchHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
  14 + return func(w http.ResponseWriter, r *http.Request) {
  15 + var req types.ArticleSecuritySearchRequest
  16 + if err := httpx.Parse(r, &req); err != nil {
  17 + httpx.ErrorCtx(r.Context(), w, err)
  18 + return
  19 + }
  20 +
  21 + l := secuirty.NewArticleSecuritySearchLogic(r.Context(), svcCtx)
  22 + resp, err := l.ArticleSecuritySearch(&req)
  23 + result.HttpResult(r, w, resp, err)
  24 + }
  25 +}
@@ -2,6 +2,7 @@ package article @@ -2,6 +2,7 @@ package article
2 2
3 import ( 3 import (
4 "context" 4 "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/core"
5 "strconv" 6 "strconv"
6 "strings" 7 "strings"
7 "text/template" 8 "text/template"
@@ -198,6 +199,11 @@ func (l *MiniCreateArticleLogic) MiniCreateArticle(req *types.MiniArticleCreateR @@ -198,6 +199,11 @@ func (l *MiniCreateArticleLogic) MiniCreateArticle(req *types.MiniArticleCreateR
198 if err != nil { 199 if err != nil {
199 return xerr.NewErrMsgErr("创建文章失败", err) 200 return xerr.NewErrMsgErr("创建文章失败", err)
200 } 201 }
  202 +
  203 + // 内容安全检查
  204 + if err = core.ContentSecurityCheck(l.ctx, l.svcCtx, conn, "", core.NewContentFromArticle(newArticle, sectionList)); err != nil {
  205 + return err
  206 + }
201 return nil 207 return nil
202 }, true) 208 }, true)
203 if err != nil { 209 if err != nil {
@@ -2,6 +2,7 @@ package comment @@ -2,6 +2,7 @@ package comment
2 2
3 import ( 3 import (
4 "context" 4 "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/core"
5 6
6 "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/message" 7 "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/message"
7 8
@@ -207,6 +208,10 @@ func (l *MiniCreateArticleCommentLogic) MiniCreateArticleComment(req *types.Mini @@ -207,6 +208,10 @@ func (l *MiniCreateArticleCommentLogic) MiniCreateArticleComment(req *types.Mini
207 return err 208 return err
208 } 209 }
209 210
  211 + // 内容安全检查
  212 + if err = core.ContentSecurityCheck(l.ctx, l.svcCtx, conn, "", core.NewContentFromComment(&newComment)); err != nil {
  213 + return err
  214 + }
210 return nil 215 return nil
211 }, true) 216 }, true)
212 217
  1 +package core
  2 +
  3 +import (
  4 + "bytes"
  5 + "context"
  6 + "fmt"
  7 + "github.com/silenceper/wechat/v2/miniprogram/security"
  8 + "github.com/zeromicro/go-zero/core/logx"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  11 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  12 + "time"
  13 +)
  14 +
  15 +func ContentSecurityCheck(ctx context.Context, svcCtx *svc.ServiceContext, conn transaction.Conn, openId string, content ContentBody) error {
  16 + if !svcCtx.Config.ContentSecurityCheck {
  17 + return nil
  18 + }
  19 + var (
  20 + scene security.MsgScene
  21 + )
  22 + if content.Type == 1 {
  23 + scene = security.MsgSceneSocialLog
  24 + } else {
  25 + scene = security.MsgSceneComment
  26 + }
  27 + var (
  28 + resp domain.MsgCheckDetail
  29 + )
  30 + respTmp, err := svcCtx.MiniProgram.GetSecurity().MsgCheck(&security.MsgCheckRequest{
  31 + OpenID: openId,
  32 + Scene: scene,
  33 + Content: content.Content,
  34 + })
  35 + if err != nil {
  36 + logx.Error(err)
  37 + return nil
  38 + }
  39 + resp = domain.MsgCheckDetail(respTmp)
  40 + var (
  41 + keyWords []string
  42 + prob int
  43 + )
  44 + for i := range resp.Detail {
  45 + item := resp.Detail[i]
  46 + if prob == 0 {
  47 + prob = int(item.Prob)
  48 + }
  49 + keyWords = append(keyWords, item.Keyword)
  50 + }
  51 + dm := &domain.ArticleSecurity{
  52 + ContentKeyWords: keyWords,
  53 + ContentType: content.Type,
  54 + ContentId: content.Id,
  55 + AuthorId: content.AuthorId,
  56 + Reviewer: 0,
  57 + ReviewStatus: domain.ReviewStatusWait,
  58 + Label: resp.Result.Label.String(),
  59 + Prob: prob,
  60 + Suggest: string(resp.Result.Suggest),
  61 + Detail: resp,
  62 + AutoReviewAt: time.Now().Unix(),
  63 + AutoReviewErrorCode: fmt.Sprintf("%d", resp.ErrCode),
  64 + }
  65 + if resp.Result.Suggest == security.CheckSuggestPass {
  66 + dm.ReviewStatus = domain.ReviewStatusPass
  67 + }
  68 + if dm, err = svcCtx.ArticleSecurityRepository.Insert(ctx, conn, dm); err != nil {
  69 + logx.Error(err)
  70 + return nil
  71 + }
  72 + return HandlerSecurityContent(ctx, svcCtx, conn, content, dm.ReviewStatus)
  73 +}
  74 +
  75 +func HandlerSecurityContent(ctx context.Context, svcCtx *svc.ServiceContext, conn transaction.Conn, c ContentBody, status int) error {
  76 + if !svcCtx.Config.ContentSecurityCheck {
  77 + return nil
  78 + }
  79 + var (
  80 + article *domain.Article
  81 + comment *domain.ArticleComment
  82 + err error
  83 + )
  84 + if status == domain.ReviewStatusPass {
  85 + return nil
  86 + }
  87 + if c.Type == domain.TypeArticle {
  88 + if article, err = svcCtx.ArticleRepository.FindOne(ctx, conn, c.Id); err != nil {
  89 + return fmt.Errorf("文章不存在")
  90 + }
  91 + article.Show = domain.ArticleShowDisable
  92 + if _, err = svcCtx.ArticleRepository.UpdateWithVersion(ctx, conn, article); err != nil {
  93 + return err
  94 + }
  95 + } else if c.Type == domain.TypeComment {
  96 + if comment, err = svcCtx.ArticleCommentRepository.FindOne(ctx, conn, c.Id); err != nil {
  97 + return fmt.Errorf("评论不存在")
  98 + }
  99 + comment.Show = domain.CommentShowDisable
  100 + if _, err = svcCtx.ArticleCommentRepository.UpdateWithVersion(ctx, conn, comment); err != nil {
  101 + return err
  102 + }
  103 + }
  104 + return nil
  105 +}
  106 +
  107 +type ContentBody struct {
  108 + Id int64
  109 + Type int
  110 + Content string
  111 + AuthorId int64
  112 + Summary string
  113 +}
  114 +
  115 +func NewContentFromComment(c *domain.ArticleComment) ContentBody {
  116 + return ContentBody{
  117 + Id: c.Id,
  118 + Type: domain.TypeComment,
  119 + Content: c.Content,
  120 + AuthorId: c.FromUserId,
  121 + Summary: c.Content,
  122 + }
  123 +}
  124 +
  125 +func NewContentFromArticle(c *domain.Article, sections []*domain.ArticleSection) ContentBody {
  126 + content := bytes.NewBuffer(nil)
  127 + for _, sec := range sections {
  128 + content.WriteString(sec.Content)
  129 + }
  130 + return ContentBody{
  131 + Id: c.Id,
  132 + Type: domain.TypeComment,
  133 + Content: content.String(),
  134 + AuthorId: c.AuthorId,
  135 + Summary: c.Summary,
  136 + }
  137 +}
  1 +package secuirty
  2 +
  3 +import (
  4 + "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/logic/core"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  7 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"
  9 + "time"
  10 +
  11 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  12 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  13 +
  14 + "github.com/zeromicro/go-zero/core/logx"
  15 +)
  16 +
  17 +type ArticleSecurityAuditLogic struct {
  18 + logx.Logger
  19 + ctx context.Context
  20 + svcCtx *svc.ServiceContext
  21 +}
  22 +
  23 +func NewArticleSecurityAuditLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ArticleSecurityAuditLogic {
  24 + return &ArticleSecurityAuditLogic{
  25 + Logger: logx.WithContext(ctx),
  26 + ctx: ctx,
  27 + svcCtx: svcCtx,
  28 + }
  29 +}
  30 +
  31 +func (l *ArticleSecurityAuditLogic) ArticleSecurityAudit(req *types.ArticleSecurityAuditRequest) (resp *types.ArticleSecurityAuditResponse, err error) {
  32 + var (
  33 + conn = l.svcCtx.DefaultDBConn()
  34 + dm *domain.ArticleSecurity
  35 + )
  36 + if dm, err = l.svcCtx.ArticleSecurityRepository.FindOne(l.ctx, conn, req.Id); err != nil {
  37 + return nil, xerr.NewErrMsgErr("不存在", err)
  38 + }
  39 + // 不可编辑判断
  40 + if dm.ReviewStatus != domain.ReviewStatusWait {
  41 + return nil, xerr.NewErrMsgErr("内容已审核", err)
  42 + }
  43 + // 赋值
  44 + if req.Status == 1 {
  45 + dm.ReviewStatus = domain.ReviewStatusPass
  46 + } else {
  47 + dm.ReviewStatus = domain.ReviewStatusFail
  48 + }
  49 + dm.ReviewAt = time.Now().Unix()
  50 + // 更新
  51 + if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
  52 + dm, err = l.svcCtx.ArticleSecurityRepository.UpdateWithVersion(l.ctx, conn, dm)
  53 + if err != nil {
  54 + return err
  55 + }
  56 + if err = core.HandlerSecurityContent(l.ctx, l.svcCtx, conn, core.ContentBody{Id: dm.ContentId, Type: dm.ContentType}, dm.ReviewStatus); err != nil {
  57 + return err
  58 + }
  59 + // 更新文章/评论可见
  60 + return err
  61 + }, true); err != nil {
  62 + return nil, xerr.NewErrMsg("更新失败")
  63 + }
  64 + resp = &types.ArticleSecurityAuditResponse{}
  65 + return
  66 +}
  1 +package secuirty
  2 +
  3 +import (
  4 + "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/pkg/xerr"
  7 +
  8 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  10 +
  11 + "github.com/zeromicro/go-zero/core/logx"
  12 +)
  13 +
  14 +type ArticleSecurityGetLogic struct {
  15 + logx.Logger
  16 + ctx context.Context
  17 + svcCtx *svc.ServiceContext
  18 +}
  19 +
  20 +func NewArticleSecurityGetLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ArticleSecurityGetLogic {
  21 + return &ArticleSecurityGetLogic{
  22 + Logger: logx.WithContext(ctx),
  23 + ctx: ctx,
  24 + svcCtx: svcCtx,
  25 + }
  26 +}
  27 +
  28 +func (l *ArticleSecurityGetLogic) ArticleSecurityGet(req *types.ArticleSecurityGetRequest) (resp *types.ArticleSecurityGetResponse, err error) {
  29 + var (
  30 + conn = l.svcCtx.DefaultDBConn()
  31 + dm *domain.ArticleSecurity
  32 + reviewer *domain.User
  33 + )
  34 + // 货号唯一
  35 + if dm, err = l.svcCtx.ArticleSecurityRepository.FindOne(l.ctx, conn, req.Id); err != nil {
  36 + return nil, xerr.NewErrMsgErr("不存在", err)
  37 + }
  38 + if dm.Reviewer > 0 {
  39 + if reviewer, err = l.svcCtx.UserRepository.FindOne(l.ctx, conn, dm.Reviewer); err != nil {
  40 + return nil, err
  41 + }
  42 + }
  43 + resp = &types.ArticleSecurityGetResponse{
  44 + ArticleSecurity: NewTypesArticleSecurity(dm, reviewer),
  45 + }
  46 + return
  47 +}
  1 +package secuirty
  2 +
  3 +import (
  4 + "context"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/svc"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/api/internal/types"
  7 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  8 + "strings"
  9 +
  10 + "github.com/zeromicro/go-zero/core/logx"
  11 +)
  12 +
  13 +type ArticleSecuritySearchLogic struct {
  14 + logx.Logger
  15 + ctx context.Context
  16 + svcCtx *svc.ServiceContext
  17 +}
  18 +
  19 +func NewArticleSecuritySearchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ArticleSecuritySearchLogic {
  20 + return &ArticleSecuritySearchLogic{
  21 + Logger: logx.WithContext(ctx),
  22 + ctx: ctx,
  23 + svcCtx: svcCtx,
  24 + }
  25 +}
  26 +
  27 +func (l *ArticleSecuritySearchLogic) ArticleSecuritySearch(req *types.ArticleSecuritySearchRequest) (resp *types.ArticleSecuritySearchResponse, err error) {
  28 + var (
  29 + conn = l.svcCtx.DefaultDBConn()
  30 + dms []*domain.ArticleSecurity
  31 + total int64
  32 + )
  33 +
  34 + queryOptions := domain.NewQueryOptions().WithOffsetLimit(req.Page, req.Size).
  35 + WithKV("reviewStatus", req.ReviewStatus).
  36 + WithKV("suggest", req.Suggest).
  37 + WithKV("contentType", req.ContentType).
  38 + WithKV("authorName", req.AuthorName).
  39 + WithKV("beginTime", req.BeginTime).
  40 + WithKV("endTime", req.EndTime)
  41 +
  42 + total, dms, err = l.svcCtx.ArticleSecurityRepository.Find(l.ctx, conn, queryOptions)
  43 + list := make([]types.ArticleSecurityItem, 0)
  44 + for i := range dms {
  45 + list = append(list, NewTypesArticleSecurity(dms[i], nil))
  46 + }
  47 + resp = &types.ArticleSecuritySearchResponse{
  48 + List: list,
  49 + Total: total,
  50 + }
  51 + return
  52 +}
  53 +
  54 +func NewDomainArticleSecurity(item types.ArticleSecurityItem) *domain.ArticleSecurity {
  55 + return &domain.ArticleSecurity{}
  56 +}
  57 +
  58 +func NewTypesArticleSecurity(item *domain.ArticleSecurity, reviewer *domain.User) types.ArticleSecurityItem {
  59 + result := types.ArticleSecurityItem{
  60 + Id: item.Id,
  61 + ContentKeyWords: strings.Join(item.ContentKeyWords, ","),
  62 + Label: item.Label,
  63 + Prob: item.Prob,
  64 + Suggest: describeSuggest(item.Suggest),
  65 + Author: item.AuthorName,
  66 + ReviewAt: item.CreatedAt,
  67 + ReviewStatus: item.ReviewStatus,
  68 +
  69 + Reviewer: "",
  70 + }
  71 + if reviewer != nil {
  72 + result.Reviewer = reviewer.Name
  73 + }
  74 + for _, detail := range item.Detail.Detail {
  75 + result.CheckList = append(result.CheckList, types.CheckDetailItem{
  76 + Label: detail.Label.String(),
  77 + Prob: detail.Prob,
  78 + Suggest: describeSuggest(detail.Suggest),
  79 + })
  80 + }
  81 + return result
  82 +}
  83 +
  84 +func describeContentType(t int) string {
  85 + if t == domain.TypeArticle {
  86 + return "文本-帖子"
  87 + }
  88 + if t == domain.TypeComment {
  89 + return "文本-评论"
  90 + }
  91 + return ""
  92 +}
  93 +
  94 +func describeSuggest(s string) string {
  95 + if s == "risk" {
  96 + return "风险"
  97 + }
  98 + if s == "pass" {
  99 + return "通过"
  100 + }
  101 + if s == "review" {
  102 + return "人工审核"
  103 + }
  104 + return ""
  105 +}
@@ -36,6 +36,7 @@ type ServiceContext struct { @@ -36,6 +36,7 @@ type ServiceContext struct {
36 ArticleCategoryRepository domain.ArticleCategoryRepository 36 ArticleCategoryRepository domain.ArticleCategoryRepository
37 ArticleAndTagRepository domain.ArticleAndTagRepository 37 ArticleAndTagRepository domain.ArticleAndTagRepository
38 ArticleDraftOperationRepository domain.ArticleDraftOperationRepository 38 ArticleDraftOperationRepository domain.ArticleDraftOperationRepository
  39 + ArticleSecurityRepository domain.ArticleSecurityRepository
39 40
40 CompanyRepository domain.CompanyRepository 41 CompanyRepository domain.CompanyRepository
41 DepartmentRepository domain.DepartmentRepository 42 DepartmentRepository domain.DepartmentRepository
@@ -91,6 +92,7 @@ func NewServiceContext(c config.Config) *ServiceContext { @@ -91,6 +92,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
91 ArticleAndTagRepository: repository.NewArticleAndTagRepository(cache.NewCachedRepository(mlCache)), 92 ArticleAndTagRepository: repository.NewArticleAndTagRepository(cache.NewCachedRepository(mlCache)),
92 ArticleCategoryRepository: repository.NewArticleCategoryRepository(cache.NewCachedRepository(mlCache)), 93 ArticleCategoryRepository: repository.NewArticleCategoryRepository(cache.NewCachedRepository(mlCache)),
93 ArticleDraftOperationRepository: repository.NewArticleDraftOperationRepository(cache.NewCachedRepository(mlCache)), 94 ArticleDraftOperationRepository: repository.NewArticleDraftOperationRepository(cache.NewCachedRepository(mlCache)),
  95 + ArticleSecurityRepository: repository.NewArticleSecurityRepository(cache.NewCachedRepository(mlCache)),
94 96
95 CompanyRepository: repository.NewCompanyRepository(cache.NewCachedRepository(mlCache)), 97 CompanyRepository: repository.NewCompanyRepository(cache.NewCachedRepository(mlCache)),
96 DepartmentRepository: repository.NewDepartmentRepository(cache.NewCachedRepository(mlCache)), 98 DepartmentRepository: repository.NewDepartmentRepository(cache.NewCachedRepository(mlCache)),
@@ -1804,3 +1804,62 @@ type CategoryOptionValue struct { @@ -1804,3 +1804,62 @@ type CategoryOptionValue struct {
1804 Label string `json:"label"` // 名称 1804 Label string `json:"label"` // 名称
1805 Value int64 `json:"value"` // 分类ID 1805 Value int64 `json:"value"` // 分类ID
1806 } 1806 }
  1807 +
  1808 +type ArticleSecurityGetRequest struct {
  1809 + Id int64 `path:"id"`
  1810 +}
  1811 +
  1812 +type ArticleSecurityGetResponse struct {
  1813 + ArticleSecurity ArticleSecurityItem `json:"item"`
  1814 +}
  1815 +
  1816 +type ArticleSecurityAuditRequest struct {
  1817 + Id int64 `json:"id"` //id
  1818 + Status int `json:"status"` // 1:成功 0:失败
  1819 +}
  1820 +
  1821 +type ArticleSecurityAuditResponse struct {
  1822 +}
  1823 +
  1824 +type ArticleSecuritySearchRequest struct {
  1825 + Page int `json:"page,optional"`
  1826 + Size int `json:"size,optional"`
  1827 + ReviewStatus int `json:"reviewStatus,optional"` // 审核结果 1:待审核 2:通过 3:拒绝
  1828 + Suggest string `json:"suggest"` // 建议 通过、风险、人工审核
  1829 + ContentType int `json:"contentType"` // 内容类型 (1:文章 2:评论)
  1830 + AuthorName string `json:"authorName"` // 作者名称
  1831 + BeginTime int64 `json:"beginTime"` // 开始时间
  1832 + EndTime int64 `json:"endTime"` // 结束时间
  1833 +}
  1834 +
  1835 +type ArticleSecuritySearchResponse struct {
  1836 + List []ArticleSecurityItem `json:"list"`
  1837 + Total int64 `json:"total"`
  1838 +}
  1839 +
  1840 +type ArticleSecurityItem struct {
  1841 + Id int64 `json:"id"` // 唯一标识
  1842 + ContentKeyWords string `json:"contentKeyWords"` // 内容关键字
  1843 + Content ContentDetailItem `json:"content"` // 内容详情
  1844 + Label string `json:"label"` // 风控标签
  1845 + Prob int `json:"prob"` // 分值
  1846 + Suggest string `json:"suggest"` // 建议 通过、风险、人工审核
  1847 + Author string `json:"author"` // 发布人
  1848 + ReleaseAt string `json:"releaseAt"` // 发布时间
  1849 + ReviewStatus int `json:"reviewStatus"` // 审核结果 1:待审核 2:通过 3:拒绝
  1850 + Reviewer string `json:"reviewer"` // 审核人
  1851 + ReviewAt int64 `json:"reviewAt"` // 审核时间(人工处置时间)
  1852 + CheckList []CheckDetailItem `json:"checkList"` // 检查列表
  1853 +}
  1854 +
  1855 +type CheckDetailItem struct {
  1856 + Label string `json:"label"` // 命中标签
  1857 + Prob uint `json:"prob"` // 置信度。0-100,越高代表越有可能属于当前返回的标签(label)
  1858 + Suggest string `json:"suggest"` // 建议
  1859 +}
  1860 +
  1861 +type ContentDetailItem struct {
  1862 + Id int64 `json:"id"` // 内容ID
  1863 + Type int `json:"type"` // 内容类型 (1:文章 2:评论)
  1864 + Text string `json:"text"` // 内容文本
  1865 +}
  1 +
  2 +syntax = "v1"
  3 +
  4 +info(
  5 + title: "xx实例"
  6 + desc: "xx实例"
  7 + author: "author"
  8 + email: "email"
  9 + version: "v1"
  10 +)
  11 +
  12 +@server(
  13 + prefix: article_security/v1
  14 + group: article_security
  15 + jwt: JwtAuth
  16 +)
  17 +service Core {
  18 + @doc "详情"
  19 + @handler article_securityGet
  20 + get /article_security/:id (ArticleSecurityGetRequest) returns (ArticleSecurityGetResponse)
  21 + @doc "保存"
  22 + @handler article_securitySave
  23 + post /article_security (ArticleSecuritySaveRequest) returns (ArticleSecuritySaveResponse)
  24 + @doc "删除"
  25 + @handler article_securityDelete
  26 + delete /article_security/:id (ArticleSecurityDeleteRequest) returns (ArticleSecurityDeleteResponse)
  27 + @doc "更新"
  28 + @handler article_securityUpdate
  29 + put /article_security/:id (ArticleSecurityUpdateRequest) returns (ArticleSecurityUpdateResponse)
  30 + @doc "搜索"
  31 + @handler article_securitySearch
  32 + post /article_security/search (ArticleSecuritySearchRequest) returns (ArticleSecuritySearchResponse)
  33 +}
  34 +
  35 +type (
  36 + ArticleSecurityGetRequest {
  37 + Id int64 `path:"id"`
  38 + }
  39 + ArticleSecurityGetResponse struct{
  40 + ArticleSecurity ArticleSecurityItem `json:"article_security"`
  41 + }
  42 +
  43 + ArticleSecuritySaveRequest struct{
  44 + ArticleSecurity ArticleSecurityItem `json:"article_security"`
  45 + }
  46 + ArticleSecuritySaveResponse struct{}
  47 +
  48 + ArticleSecurityDeleteRequest struct{
  49 + Id int64 `path:"id"`
  50 + }
  51 + ArticleSecurityDeleteResponse struct{}
  52 +
  53 + ArticleSecurityUpdateRequest struct{
  54 + Id int64 `path:"id"`
  55 + ArticleSecurity ArticleSecurityItem `json:"article_security"`
  56 + }
  57 + ArticleSecurityUpdateResponse struct{}
  58 +
  59 + ArticleSecuritySearchRequest struct{
  60 + Page int `json:"page"`
  61 + Size int `json:"size"`
  62 + }
  63 + ArticleSecuritySearchResponse{
  64 + List []ArticleSecurityItem `json:"list"`
  65 + Total int64 `json:"total"`
  66 + }
  67 + ArticleSecurityItem struct{
  68 +
  69 + }
  70 +)
  71 +
  72 +// logic CRUD
  73 +// Save
  74 + //var (
  75 + // conn = l.svcCtx.DefaultDBConn()
  76 + // dm *domain.ArticleSecurity
  77 + //)
  78 + //// 唯一判断
  79 +
  80 + //dm = NewDomainArticleSecurity(req.ArticleSecurity)
  81 + //if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
  82 + // dm, err = l.svcCtx.ArticleSecurityRepository.Insert(l.ctx, conn, dm)
  83 + // return err
  84 + //}, true); err != nil {
  85 + // return nil, xerr.NewErrMsg("保存失败")
  86 + //}
  87 + ////resp = &types.ArticleSecuritySaveResponse{}
  88 + //return
  89 +
  90 +//func NewDomainArticleSecurity(item types.ArticleSecurityItem) *domain.ArticleSecurity {
  91 +// return &domain.ArticleSecurity{
  92 +
  93 +// }
  94 +//}
  95 +//
  96 +//func NewTypesArticleSecurity(item *domain.ArticleSecurity) types.ArticleSecurityItem {
  97 +// return types.ArticleSecurityItem{
  98 +// Id: item.Id,
  99 +// }
  100 +//}
  101 +
  102 +// Get
  103 + //var (
  104 + // conn = l.svcCtx.DefaultDBConn()
  105 + // dm *domain.ArticleSecurity
  106 + //)
  107 + //// 货号唯一
  108 + //if dm, err = l.svcCtx.ArticleSecurityRepository.FindOne(l.ctx, conn, req.Id); err != nil {
  109 + // return nil, xerr.NewErrMsgErr("不存在", err)
  110 + //}
  111 + //resp = &types.ArticleSecurityGetResponse{
  112 + // ArticleSecurity: NewTypesArticleSecurity(dm),
  113 + //}
  114 + //return
  115 +
  116 +// Delete
  117 + //var (
  118 + // conn = l.svcCtx.DefaultDBConn()
  119 + // dm *domain.ArticleSecurity
  120 + //)
  121 + //if dm, err = l.svcCtx.ArticleSecurityRepository.FindOne(l.ctx, conn, req.Id); err != nil {
  122 + // return nil, xerr.NewErrMsgErr("不存在", err)
  123 + //}
  124 + //if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
  125 + // if dm, err = l.svcCtx.ArticleSecurityRepository.Delete(l.ctx, conn, dm); err != nil {
  126 + // return err
  127 + // }
  128 + // return nil
  129 + //}, true); err != nil {
  130 + // return nil, xerr.NewErrMsgErr("移除失败", err)
  131 + //}
  132 + //return
  133 +
  134 +// Search
  135 + //var (
  136 + // conn = l.svcCtx.DefaultDBConn()
  137 + // dms []*domain.ArticleSecurity
  138 + // total int64
  139 + //)
  140 + //
  141 + //queryOptions := domain.NewQueryOptions().WithOffsetLimit(req.Page, req.Size).
  142 + // WithKV("", "")
  143 +
  144 + //total, dms, err = l.svcCtx.ArticleSecurityRepository.Find(l.ctx, conn, queryOptions)
  145 + //list := make([]types.ArticleSecurityItem, 0)
  146 + //for i := range dms {
  147 + // list = append(list, NewTypesArticleSecurity(dms[i]))
  148 + //}
  149 + //resp = &types.ArticleSecuritySearchResponse{
  150 + // List: list,
  151 + // Total: total,
  152 + //}
  153 + //return
  154 +
  155 +// Update
  156 + //var (
  157 + // conn = l.svcCtx.DefaultDBConn()
  158 + // dm *domain.ArticleSecurity
  159 + //)
  160 + //if dm, err = l.svcCtx.ArticleSecurityRepository.FindOne(l.ctx, conn, req.Id); err != nil {
  161 + // return nil, xerr.NewErrMsgErr("不存在", err)
  162 + //}
  163 + //// 不可编辑判断
  164 +
  165 + //// 赋值
  166 +
  167 + //// 更新
  168 + //if err = transaction.UseTrans(l.ctx, l.svcCtx.DB, func(ctx context.Context, conn transaction.Conn) error {
  169 + // dm, err = l.svcCtx.ArticleSecurityRepository.UpdateWithVersion(l.ctx, conn, dm)
  170 + // return err
  171 + //}, true); err != nil {
  172 + // return nil, xerr.NewErrMsg("更新失败")
  173 + //}
  174 + //resp = &types.ArticleSecurityUpdateResponse{}
  175 + //return
  1 +
  2 +syntax = "proto3";
  3 +
  4 +option go_package ="./pb";
  5 +
  6 +package pb;
  7 +
  8 +message ArticleSecurityGetReq {
  9 + int64 Id = 1;
  10 +}
  11 +message ArticleSecurityGetResp{
  12 + ArticleSecurityItem User = 1;
  13 +}
  14 +
  15 +message ArticleSecuritySaveReq {
  16 +
  17 +}
  18 +message ArticleSecuritySaveResp{
  19 +
  20 +}
  21 +
  22 +message ArticleSecurityDeleteReq {
  23 + int64 Id = 1;
  24 +}
  25 +message ArticleSecurityDeleteResp{
  26 +
  27 +}
  28 +
  29 +message ArticleSecurityUpdateReq {
  30 + int64 Id = 1;
  31 +}
  32 +message ArticleSecurityUpdateResp{
  33 +
  34 +}
  35 +
  36 +message ArticleSecuritySearchReq {
  37 + int64 PageNumber = 1;
  38 + int64 PageSize = 2;
  39 +}
  40 +message ArticleSecuritySearchResp{
  41 + repeated ArticleSecurityItem List =1;
  42 + int64 Total =2;
  43 +}
  44 +message ArticleSecurityItem {
  45 +
  46 +}
  47 +
  48 +service ArticleSecurityService {
  49 + rpc ArticleSecurityGet(ArticleSecurityGetReq) returns(ArticleSecurityGetResp);
  50 + rpc ArticleSecuritySave(ArticleSecuritySaveReq) returns(ArticleSecuritySaveResp);
  51 + rpc ArticleSecurityDelete(ArticleSecurityDeleteReq) returns(ArticleSecurityDeleteResp);
  52 + rpc ArticleSecurityUpdate(ArticleSecurityUpdateReq) returns(ArticleSecurityUpdateResp);
  53 + rpc ArticleSecuritySearch(ArticleSecuritySearchReq) returns(ArticleSecuritySearchResp);
  54 +}
@@ -26,6 +26,7 @@ func Migrate(db *gorm.DB) { @@ -26,6 +26,7 @@ func Migrate(db *gorm.DB) {
26 //&models.ArticleAndTag{}, 26 //&models.ArticleAndTag{},
27 &models.ArticleDraftOperation{}, 27 &models.ArticleDraftOperation{},
28 &models.ArticleCategory{}, 28 &models.ArticleCategory{},
  29 + &models.ArticleSecurity{},
29 } 30 }
30 31
31 db.AutoMigrate(modelsList...) 32 db.AutoMigrate(modelsList...)
  1 +package models
  2 +
  3 +import (
  4 + "fmt"
  5 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  6 + "gorm.io/gorm"
  7 + "gorm.io/plugin/soft_delete"
  8 + "time"
  9 +)
  10 +
  11 +type ArticleSecurity struct {
  12 + Id int64 // 唯一标识
  13 +
  14 + ContentKeyWords []string // 内容关键字
  15 + ContentType int `json:"contentType,omitempty"` // 内容类型 (1:文章 2:评论)
  16 + ContentId int64 `json:"contentId,omitempty"` // 内容ID
  17 + AuthorId int64 `json:"authorId,omitempty"` // 发布人
  18 + AuthorName string `json:"authorName,omitempty"` // 发布人
  19 + Reviewer int64 `json:"reviewer,omitempty"` // 审核人
  20 + ReviewStatus int `json:"reviewStatus,omitempty"` // 审核状态 0:待审核 1:通过 2:拒绝
  21 + Label string // 标签
  22 + Prob int // 分值
  23 + Suggest string // 建议 通过、风险、人工审核
  24 + Detail domain.MsgCheckDetail `gorm:"type:jsonb;serializer:json"`
  25 + AutoReviewAt int64 `json:"autoReviewAt,omitempty"` // 自动审核时间
  26 + AutoReviewErrorCode string `json:"autoReviewErrorCode,omitempty"` // 自动审核错误码
  27 + ReviewAt int64 `json:"reviewAt,omitempty"` // 审核时间(人工处置时间)
  28 +
  29 + CreatedAt int64
  30 + UpdatedAt int64
  31 + DeletedAt int64
  32 + IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt"`
  33 + Version int
  34 +}
  35 +
  36 +func (m *ArticleSecurity) TableName() string {
  37 + return "article_security"
  38 +}
  39 +
  40 +func (m *ArticleSecurity) BeforeCreate(tx *gorm.DB) (err error) {
  41 + m.CreatedAt = time.Now().Unix()
  42 + m.UpdatedAt = time.Now().Unix()
  43 + return
  44 +}
  45 +
  46 +func (m *ArticleSecurity) BeforeUpdate(tx *gorm.DB) (err error) {
  47 + m.UpdatedAt = time.Now().Unix()
  48 + return
  49 +}
  50 +
  51 +func (m *ArticleSecurity) CacheKeyFunc() string {
  52 + if m.Id == 0 {
  53 + return ""
  54 + }
  55 + return fmt.Sprintf("%v:cache:%v:id:%v", domain.ProjectName, m.TableName(), m.Id)
  56 +}
  57 +
  58 +func (m *ArticleSecurity) CacheKeyFuncByObject(obj interface{}) string {
  59 + if v, ok := obj.(*ArticleSecurity); ok {
  60 + return v.CacheKeyFunc()
  61 + }
  62 + return ""
  63 +}
  64 +
  65 +func (m *ArticleSecurity) CachePrimaryKeyFunc() string {
  66 + if len("") == 0 {
  67 + return ""
  68 + }
  69 + return fmt.Sprintf("%v:cache:%v:primarykey:%v", domain.ProjectName, m.TableName(), "key")
  70 +}
  1 +package repository
  2 +
  3 +import (
  4 + "context"
  5 + "fmt"
  6 + "github.com/jinzhu/copier"
  7 + "github.com/pkg/errors"
  8 + "github.com/tiptok/gocomm/pkg/cache"
  9 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/models"
  10 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  11 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/domain"
  12 + "gorm.io/gorm"
  13 +)
  14 +
  15 +type ArticleSecurityRepository struct {
  16 + *cache.CachedRepository
  17 +}
  18 +
  19 +func (repository *ArticleSecurityRepository) Insert(ctx context.Context, conn transaction.Conn, dm *domain.ArticleSecurity) (*domain.ArticleSecurity, error) {
  20 + var (
  21 + err error
  22 + m = &models.ArticleSecurity{}
  23 + tx = conn.DB()
  24 + )
  25 + if m, err = repository.DomainModelToModel(dm); err != nil {
  26 + return nil, err
  27 + }
  28 + if tx = tx.Model(m).Save(m); tx.Error != nil {
  29 + return nil, tx.Error
  30 + }
  31 + dm.Id = m.Id
  32 + return repository.ModelToDomainModel(m)
  33 +
  34 +}
  35 +
  36 +func (repository *ArticleSecurityRepository) Update(ctx context.Context, conn transaction.Conn, dm *domain.ArticleSecurity) (*domain.ArticleSecurity, error) {
  37 + var (
  38 + err error
  39 + m *models.ArticleSecurity
  40 + tx = conn.DB()
  41 + )
  42 + if m, err = repository.DomainModelToModel(dm); err != nil {
  43 + return nil, err
  44 + }
  45 + queryFunc := func() (interface{}, error) {
  46 + tx = tx.Model(m).Updates(m)
  47 + return nil, tx.Error
  48 + }
  49 + if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
  50 + return nil, err
  51 + }
  52 + return repository.ModelToDomainModel(m)
  53 +}
  54 +
  55 +func (repository *ArticleSecurityRepository) UpdateWithVersion(ctx context.Context, transaction transaction.Conn, dm *domain.ArticleSecurity) (*domain.ArticleSecurity, error) {
  56 + var (
  57 + err error
  58 + m *models.ArticleSecurity
  59 + tx = transaction.DB()
  60 + )
  61 + if m, err = repository.DomainModelToModel(dm); err != nil {
  62 + return nil, err
  63 + }
  64 + oldVersion := dm.Version
  65 + m.Version += 1
  66 + queryFunc := func() (interface{}, error) {
  67 + tx = tx.Model(m).Select("*").Where("id = ?", m.Id).Where("version = ?", oldVersion).Updates(m)
  68 + if tx.RowsAffected == 0 {
  69 + return nil, domain.ErrUpdateFail
  70 + }
  71 + return nil, tx.Error
  72 + }
  73 + if _, err = repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
  74 + return nil, err
  75 + }
  76 + return repository.ModelToDomainModel(m)
  77 +}
  78 +
  79 +func (repository *ArticleSecurityRepository) Delete(ctx context.Context, conn transaction.Conn, dm *domain.ArticleSecurity) (*domain.ArticleSecurity, error) {
  80 + var (
  81 + tx = conn.DB()
  82 + m = &models.ArticleSecurity{Id: dm.Identify().(int64)}
  83 + )
  84 + queryFunc := func() (interface{}, error) {
  85 + tx = tx.Where("id = ?", m.Id).Delete(m)
  86 + return m, tx.Error
  87 + }
  88 + if _, err := repository.Query(queryFunc, m.CacheKeyFunc()); err != nil {
  89 + return dm, err
  90 + }
  91 + return repository.ModelToDomainModel(m)
  92 +}
  93 +
  94 +func (repository *ArticleSecurityRepository) FindOne(ctx context.Context, conn transaction.Conn, id int64) (*domain.ArticleSecurity, error) {
  95 + var (
  96 + err error
  97 + tx = conn.DB()
  98 + m = new(models.ArticleSecurity)
  99 + )
  100 + queryFunc := func() (interface{}, error) {
  101 + tx = tx.Model(m).Where("id = ?", id).First(m)
  102 + if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
  103 + return nil, domain.ErrNotFound
  104 + }
  105 + return m, tx.Error
  106 + }
  107 + cacheModel := new(models.ArticleSecurity)
  108 + cacheModel.Id = id
  109 + if err = repository.QueryCache(cacheModel.CacheKeyFunc, m, queryFunc); err != nil {
  110 + return nil, err
  111 + }
  112 + return repository.ModelToDomainModel(m)
  113 +}
  114 +
  115 +func (repository *ArticleSecurityRepository) Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*domain.ArticleSecurity, error) {
  116 + var (
  117 + tx = conn.DB()
  118 + ms []*models.ArticleSecurity
  119 + dms = make([]*domain.ArticleSecurity, 0)
  120 + total int64
  121 + )
  122 + queryFunc := func() (interface{}, error) {
  123 + tx = tx.Model(&ms).Order("id desc")
  124 + if v, ok := queryOptions["authorName"]; ok {
  125 + tx.Where("author_name like ? ", fmt.Sprintf("%%%v%%", v))
  126 + }
  127 + if v, ok := queryOptions["contentType"]; ok {
  128 + tx.Where("content_type = ? ", v)
  129 + }
  130 + if v, ok := queryOptions["reviewStatus"]; ok {
  131 + tx.Where("review_status = ? ", v)
  132 + }
  133 + if v, ok := queryOptions["suggest"]; ok {
  134 + tx.Where("suggest = ? ", v)
  135 + }
  136 + if v, ok := queryOptions["beginTime"]; ok {
  137 + tx.Where("created_at >= ?", v)
  138 + }
  139 + if v, ok := queryOptions["endTime"]; ok {
  140 + tx.Where("created_at < ?", v)
  141 + }
  142 +
  143 + if total, tx = transaction.PaginationAndCount(ctx, tx, queryOptions, &ms); tx.Error != nil {
  144 + return dms, tx.Error
  145 + }
  146 + return dms, nil
  147 + }
  148 +
  149 + if _, err := repository.Query(queryFunc); err != nil {
  150 + return 0, nil, err
  151 + }
  152 +
  153 + for _, item := range ms {
  154 + if dm, err := repository.ModelToDomainModel(item); err != nil {
  155 + return 0, dms, err
  156 + } else {
  157 + dms = append(dms, dm)
  158 + }
  159 + }
  160 + return total, dms, nil
  161 +}
  162 +
  163 +func (repository *ArticleSecurityRepository) ModelToDomainModel(from *models.ArticleSecurity) (*domain.ArticleSecurity, error) {
  164 + to := &domain.ArticleSecurity{}
  165 + err := copier.Copy(to, from)
  166 + return to, err
  167 +}
  168 +
  169 +func (repository *ArticleSecurityRepository) DomainModelToModel(from *domain.ArticleSecurity) (*models.ArticleSecurity, error) {
  170 + to := &models.ArticleSecurity{}
  171 + err := copier.Copy(to, from)
  172 + return to, err
  173 +}
  174 +
  175 +func NewArticleSecurityRepository(cache *cache.CachedRepository) domain.ArticleSecurityRepository {
  176 + return &ArticleSecurityRepository{CachedRepository: cache}
  177 +}
  1 +package domain
  2 +
  3 +import (
  4 + "context"
  5 + "github.com/silenceper/wechat/v2/miniprogram/security"
  6 + "gitlab.fjmaimaimai.com/allied-creation/sumifcc-discuss/cmd/discuss/interanl/pkg/db/transaction"
  7 +)
  8 +
  9 +type ArticleSecurity struct {
  10 + Id int64 // 唯一标识
  11 +
  12 + ContentKeyWords []string // 内容关键字
  13 + ContentType int `json:"contentType,omitempty"` // 内容类型 (1:文章 2:评论)
  14 + ContentId int64 `json:"contentId,omitempty"` // 内容ID
  15 + AuthorId int64 `json:"authorId,omitempty"` // 发布人
  16 + AuthorName string `json:"authorName,omitempty"` // 发布人
  17 + Reviewer int64 `json:"reviewer,omitempty"` // 审核人
  18 + ReviewStatus int `json:"reviewStatus,omitempty"` // 审核状态 1:待审核 2:通过 3:拒绝
  19 + Label string // 标签
  20 + Prob int // 分值
  21 + Suggest string // 建议 通过、风险、人工审核
  22 + Detail MsgCheckDetail `json:"detail,omitempty"`
  23 + AutoReviewAt int64 `json:"autoReviewAt,omitempty"` // 自动审核时间
  24 + AutoReviewErrorCode string `json:"autoReviewErrorCode,omitempty"` // 自动审核错误码
  25 + ReviewAt int64 `json:"reviewAt,omitempty"` // 审核时间(人工处置时间)
  26 + CreatedAt int64 `json:"createdAt,omitempty"`
  27 + UpdatedAt int64 `json:"updatedAt,omitempty"`
  28 + DeletedAt int64 `json:"deletedAt,omitempty"`
  29 + Version int `json:"version,omitempty"`
  30 +}
  31 +
  32 +const (
  33 + ReviewStatusWait = iota + 1
  34 + ReviewStatusPass
  35 + ReviewStatusFail
  36 +)
  37 +
  38 +const (
  39 + TypeArticle = 1
  40 + TypeComment = 2
  41 +)
  42 +
  43 +type MsgCheckDetail security.MsgCheckResponse
  44 +
  45 +type ArticleSecurityRepository interface {
  46 + Insert(ctx context.Context, conn transaction.Conn, dm *ArticleSecurity) (*ArticleSecurity, error)
  47 + Update(ctx context.Context, conn transaction.Conn, dm *ArticleSecurity) (*ArticleSecurity, error)
  48 + UpdateWithVersion(ctx context.Context, conn transaction.Conn, dm *ArticleSecurity) (*ArticleSecurity, error)
  49 + Delete(ctx context.Context, conn transaction.Conn, dm *ArticleSecurity) (*ArticleSecurity, error)
  50 + FindOne(ctx context.Context, conn transaction.Conn, id int64) (*ArticleSecurity, error)
  51 + Find(ctx context.Context, conn transaction.Conn, queryOptions map[string]interface{}) (int64, []*ArticleSecurity, error)
  52 +}
  53 +
  54 +func (m *ArticleSecurity) Identify() interface{} {
  55 + if m.Id == 0 {
  56 + return nil
  57 + }
  58 + return m.Id
  59 +}
@@ -36,4 +36,9 @@ CREATE TABLE `article_draft_operation` ( @@ -36,4 +36,9 @@ CREATE TABLE `article_draft_operation` (
36 CREATE TABLE `article_category` ( 36 CREATE TABLE `article_category` (
37 `id` int(0) NOT NULL COMMENT '唯一标识', 37 `id` int(0) NOT NULL COMMENT '唯一标识',
38 PRIMARY KEY (`id`) USING BTREE 38 PRIMARY KEY (`id`) USING BTREE
  39 +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
  40 +
  41 +CREATE TABLE `article_security` (
  42 + `id` int(0) NOT NULL COMMENT '唯一标识',
  43 + PRIMARY KEY (`id`) USING BTREE
39 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 44 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;