Merge branch 'ai_0.3_dashboard' into 'test'

Ai 0.3 dashboard

See merge request opensource/answer!163
This commit is contained in:
linkinstar 2022-11-02 08:50:38 +00:00
commit 436ce151bc
24 changed files with 324 additions and 6 deletions

View File

@ -44,6 +44,7 @@ import (
auth2 "github.com/answerdev/answer/internal/service/auth"
"github.com/answerdev/answer/internal/service/collection_common"
comment2 "github.com/answerdev/answer/internal/service/comment"
"github.com/answerdev/answer/internal/service/dashboard"
export2 "github.com/answerdev/answer/internal/service/export"
"github.com/answerdev/answer/internal/service/follow"
meta2 "github.com/answerdev/answer/internal/service/meta"
@ -149,7 +150,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService)
questionController := controller.NewQuestionController(questionService, rankService)
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo)
answerController := controller.NewAnswerController(answerService, rankService)
dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configRepo)
answerController := controller.NewAnswerController(answerService, rankService, dashboardService)
searchRepo := search_common.NewSearchRepo(dataData, uniqueIDRepo, userCommon)
searchService := service.NewSearchService(searchRepo, tagRepo, userCommon, followRepo)
searchController := controller.NewSearchController(searchService)
@ -173,7 +175,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
notificationCommon := notificationcommon.NewNotificationCommon(dataData, notificationRepo, userCommon, activityRepo, followRepo, objService)
notificationService := notification2.NewNotificationService(dataData, notificationRepo, notificationCommon)
notificationController := controller.NewNotificationController(notificationService)
answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, controller_backyardReportController, userBackyardController, reasonController, themeController, siteInfoController, siteinfoController, notificationController)
dashboardController := controller.NewDashboardController(dashboardService)
answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, controller_backyardReportController, userBackyardController, reasonController, themeController, siteInfoController, siteinfoController, notificationController, dashboardController)
swaggerRouter := router.NewSwaggerRouter(swaggerConf)
uiRouter := router.NewUIRouter()
authUserMiddleware := middleware.NewAuthUserMiddleware(authService)

View File

@ -119,6 +119,34 @@ const docTemplate = `{
}
}
},
"/answer/admin/api/dashboard": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "DashboardInfo",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "DashboardInfo",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/language/options": {
"get": {
"description": "Get language options",

View File

@ -107,6 +107,34 @@
}
}
},
"/answer/admin/api/dashboard": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "DashboardInfo",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "DashboardInfo",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/language/options": {
"get": {
"description": "Get language options",

View File

@ -1457,6 +1457,23 @@ paths:
summary: AdminSetAnswerStatus
tags:
- admin
/answer/admin/api/dashboard:
get:
consumes:
- application/json
description: DashboardInfo
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: DashboardInfo
tags:
- admin
/answer/admin/api/language/options:
get:
description: Get language options

View File

@ -9,6 +9,7 @@ import (
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service"
"github.com/answerdev/answer/internal/service/dashboard"
"github.com/answerdev/answer/internal/service/rank"
"github.com/gin-gonic/gin"
"github.com/segmentfault/pacman/errors"
@ -16,13 +17,21 @@ import (
// AnswerController answer controller
type AnswerController struct {
answerService *service.AnswerService
rankService *rank.RankService
answerService *service.AnswerService
rankService *rank.RankService
dashboardService *dashboard.DashboardService
}
// NewAnswerController new controller
func NewAnswerController(answerService *service.AnswerService, rankService *rank.RankService) *AnswerController {
return &AnswerController{answerService: answerService, rankService: rankService}
func NewAnswerController(answerService *service.AnswerService,
rankService *rank.RankService,
dashboardService *dashboard.DashboardService,
) *AnswerController {
return &AnswerController{
answerService: answerService,
rankService: rankService,
dashboardService: dashboardService,
}
}
// RemoveAnswer delete answer

View File

@ -20,4 +20,5 @@ var ProviderSetController = wire.NewSet(
NewReasonController,
NewNotificationController,
NewSiteinfoController,
NewDashboardController,
)

View File

@ -0,0 +1,36 @@
package controller
import (
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/service/dashboard"
"github.com/gin-gonic/gin"
)
type DashboardController struct {
dashboardService *dashboard.DashboardService
}
// NewDashboardController new controller
func NewDashboardController(
dashboardService *dashboard.DashboardService,
) *DashboardController {
return &DashboardController{
dashboardService: dashboardService,
}
}
// DashboardInfo godoc
// @Summary DashboardInfo
// @Description DashboardInfo
// @Tags admin
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Router /answer/admin/api/dashboard [get]
// @Success 200 {object} handler.RespBody
func (ac *DashboardController) DashboardInfo(ctx *gin.Context) {
info, err := ac.dashboardService.Statistical(ctx)
handler.HandleResponse(ctx, err, gin.H{
"info": info,
})
}

View File

@ -4,8 +4,10 @@ import (
"context"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/activity_common"
"github.com/segmentfault/pacman/errors"
)
// VoteRepo activity repository
@ -39,3 +41,12 @@ func (vr *VoteRepo) GetVoteStatus(ctx context.Context, objectID, userID string)
}
return ""
}
func (vr *VoteRepo) GetVoteCount(ctx context.Context, activityTypes []int) (count int64, err error) {
list := make([]*entity.Activity, 0)
count, err = vr.data.DB.Where("cancelled =0").In("activity_type", activityTypes).FindAndCount(&list)
if err != nil {
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}

View File

@ -5,6 +5,7 @@ import (
"strings"
"time"
"unicode"
"xorm.io/builder"
"github.com/answerdev/answer/internal/base/constant"
@ -102,6 +103,16 @@ func (ar *answerRepo) GetAnswer(ctx context.Context, id string) (
return
}
// GetQuestionCount
func (ar *answerRepo) GetAnswerCount(ctx context.Context) (count int64, err error) {
list := make([]*entity.Answer, 0)
count, err = ar.data.DB.Where("status = ?", entity.AnswerStatusAvailable).FindAndCount(&list)
if err != nil {
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetAnswerList get answer list all
func (ar *answerRepo) GetAnswerList(ctx context.Context, answer *entity.Answer) (answerList []*entity.Answer, err error) {
answerList = make([]*entity.Answer, 0)

View File

@ -79,6 +79,15 @@ func (cr *commentRepo) GetComment(ctx context.Context, commentID string) (
return
}
func (cr *commentRepo) GetCommentCount(ctx context.Context) (count int64, err error) {
list := make([]*entity.Comment, 0)
count, err = cr.data.DB.Where("status = ?", entity.CommentStatusAvailable).FindAndCount(&list)
if err != nil {
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetCommentPage get comment page
func (cr *commentRepo) GetCommentPage(ctx context.Context, commentQuery *comment.CommentQuery) (
commentList []*entity.Comment, total int64, err error,

View File

@ -5,6 +5,7 @@ import (
"strings"
"time"
"unicode"
"xorm.io/builder"
"github.com/answerdev/answer/internal/base/constant"
@ -162,6 +163,16 @@ func (qr *questionRepo) GetQuestionList(ctx context.Context, question *entity.Qu
return
}
func (qr *questionRepo) GetQuestionCount(ctx context.Context) (count int64, err error) {
questionList := make([]*entity.Question, 0)
count, err = qr.data.DB.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusclosed}).FindAndCount(&questionList)
if err != nil {
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetQuestionPage get question page
func (qr *questionRepo) GetQuestionPage(ctx context.Context, page, pageSize int, question *entity.Question) (questionList []*entity.Question, total int64, err error) {
questionList = make([]*entity.Question, 0)

View File

@ -94,3 +94,12 @@ func (ar *reportRepo) UpdateByID(
}
return
}
func (vr *reportRepo) GetReportCount(ctx context.Context) (count int64, err error) {
list := make([]*entity.Report, 0)
count, err = vr.data.DB.Where("status =?", entity.ReportStatusPending).FindAndCount(&list)
if err != nil {
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}

View File

@ -157,3 +157,12 @@ func (ur *userRepo) GetByEmail(ctx context.Context, email string) (userInfo *ent
}
return
}
func (vr *userRepo) GetUserCount(ctx context.Context) (count int64, err error) {
list := make([]*entity.User, 0)
count, err = vr.data.DB.Where("mail_status =?", entity.EmailStatusAvailable).And("status =?", entity.UserStatusAvailable).FindAndCount(&list)
if err != nil {
return count, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}

View File

@ -27,6 +27,7 @@ type AnswerAPIRouter struct {
siteInfoController *controller_backyard.SiteInfoController
siteinfoController *controller.SiteinfoController
notificationController *controller.NotificationController
dashboardController *controller.DashboardController
}
func NewAnswerAPIRouter(
@ -50,6 +51,7 @@ func NewAnswerAPIRouter(
siteInfoController *controller_backyard.SiteInfoController,
siteinfoController *controller.SiteinfoController,
notificationController *controller.NotificationController,
dashboardController *controller.DashboardController,
) *AnswerAPIRouter {
return &AnswerAPIRouter{
@ -73,6 +75,7 @@ func NewAnswerAPIRouter(
siteInfoController: siteInfoController,
notificationController: notificationController,
siteinfoController: siteinfoController,
dashboardController: dashboardController,
}
}
@ -226,4 +229,7 @@ func (a *AnswerAPIRouter) RegisterAnswerCmsAPIRouter(r *gin.RouterGroup) {
r.PUT("/siteinfo/interface", a.siteInfoController.UpdateInterface)
r.GET("/setting/smtp", a.siteInfoController.GetSMTPConfig)
r.PUT("/setting/smtp", a.siteInfoController.UpdateSMTPConfig)
//dashboard
r.GET("/dashboard", a.dashboardController.DashboardInfo)
}

View File

@ -0,0 +1,15 @@
package schema
type DashboardInfo struct {
QuestionCount int64 `json:"question_count"`
AnswerCount int64 `json:"answer_count"`
CommentCount int64 `json:"comment_count"`
VoteCount int64 `json:"vote_count"`
UserCount int64 `json:"user_count"`
ReportCount int64 `json:"report_count"`
UploadingFiles string `json:"uploading_files"` //Allowed or Not allowed
SMTP string `json:"smtp"` //Enabled or Disabled
TimeZone string `json:"time_zone"`
OccupyingStorageSpace string `json:"occupying_storage_space"`
AppStartTime string `json:"app_start_time"`
}

View File

@ -7,4 +7,5 @@ import (
// VoteRepo activity repository
type VoteRepo interface {
GetVoteStatus(ctx context.Context, objectId, userId string) (status string)
GetVoteCount(ctx context.Context, activityTypes []int) (count int64, err error)
}

View File

@ -20,6 +20,7 @@ type AnswerRepo interface {
SearchList(ctx context.Context, search *entity.AnswerSearch) ([]*entity.Answer, int64, error)
CmsSearchList(ctx context.Context, search *entity.CmsAnswerSearch) ([]*entity.Answer, int64, error)
UpdateAnswerStatus(ctx context.Context, answer *entity.Answer) (err error)
GetAnswerCount(ctx context.Context) (count int64, err error)
}
// AnswerCommon user service

View File

@ -12,6 +12,7 @@ import (
// CommentCommonRepo comment repository
type CommentCommonRepo interface {
GetComment(ctx context.Context, commentID string) (comment *entity.Comment, exist bool, err error)
GetCommentCount(ctx context.Context) (count int64, err error)
}
// CommentCommonService user service

View File

@ -0,0 +1,105 @@
package dashboard
import (
"context"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/activity_common"
answercommon "github.com/answerdev/answer/internal/service/answer_common"
"github.com/answerdev/answer/internal/service/comment_common"
"github.com/answerdev/answer/internal/service/config"
questioncommon "github.com/answerdev/answer/internal/service/question_common"
"github.com/answerdev/answer/internal/service/report_common"
usercommon "github.com/answerdev/answer/internal/service/user_common"
)
type DashboardService struct {
questionRepo questioncommon.QuestionRepo
answerRepo answercommon.AnswerRepo
commentRepo comment_common.CommentCommonRepo
voteRepo activity_common.VoteRepo
userRepo usercommon.UserRepo
reportRepo report_common.ReportRepo
configRepo config.ConfigRepo
}
func NewDashboardService(
questionRepo questioncommon.QuestionRepo,
answerRepo answercommon.AnswerRepo,
commentRepo comment_common.CommentCommonRepo,
voteRepo activity_common.VoteRepo,
userRepo usercommon.UserRepo,
reportRepo report_common.ReportRepo,
configRepo config.ConfigRepo,
) *DashboardService {
return &DashboardService{
questionRepo: questionRepo,
answerRepo: answerRepo,
commentRepo: commentRepo,
voteRepo: voteRepo,
userRepo: userRepo,
reportRepo: reportRepo,
configRepo: configRepo,
}
}
// Statistical
func (ds *DashboardService) Statistical(ctx context.Context) (*schema.DashboardInfo, error) {
dashboardInfo := &schema.DashboardInfo{}
questionCount, err := ds.questionRepo.GetQuestionCount(ctx)
if err != nil {
return dashboardInfo, err
}
answerCount, err := ds.answerRepo.GetAnswerCount(ctx)
if err != nil {
return dashboardInfo, err
}
commentCount, err := ds.commentRepo.GetCommentCount(ctx)
if err != nil {
return dashboardInfo, err
}
typeKeys := []string{
"question.vote_up",
"question.vote_down",
"answer.vote_up",
"answer.vote_down",
}
var activityTypes []int
for _, typeKey := range typeKeys {
var t int
t, err = ds.configRepo.GetConfigType(typeKey)
if err != nil {
continue
}
activityTypes = append(activityTypes, t)
}
voteCount, err := ds.voteRepo.GetVoteCount(ctx, activityTypes)
if err != nil {
return dashboardInfo, err
}
userCount, err := ds.userRepo.GetUserCount(ctx)
if err != nil {
return dashboardInfo, err
}
reportCount, err := ds.reportRepo.GetReportCount(ctx)
if err != nil {
return dashboardInfo, err
}
dashboardInfo.QuestionCount = questionCount
dashboardInfo.AnswerCount = answerCount
dashboardInfo.CommentCount = commentCount
dashboardInfo.VoteCount = voteCount
dashboardInfo.UserCount = userCount
dashboardInfo.ReportCount = reportCount
dashboardInfo.UploadingFiles = "Allowed"
dashboardInfo.SMTP = "Enabled"
dashboardInfo.OccupyingStorageSpace = "1MB"
dashboardInfo.AppStartTime = "102"
return dashboardInfo, nil
}

View File

@ -0,0 +1 @@
package dashboard

View File

@ -8,6 +8,7 @@ import (
collectioncommon "github.com/answerdev/answer/internal/service/collection_common"
"github.com/answerdev/answer/internal/service/comment"
"github.com/answerdev/answer/internal/service/comment_common"
"github.com/answerdev/answer/internal/service/dashboard"
"github.com/answerdev/answer/internal/service/export"
"github.com/answerdev/answer/internal/service/follow"
"github.com/answerdev/answer/internal/service/meta"
@ -65,4 +66,5 @@ var ProviderSetService = wire.NewSet(
notficationcommon.NewNotificationCommon,
notification.NewNotificationService,
activity.NewAnswerActivityService,
dashboard.NewDashboardService,
)

View File

@ -38,6 +38,7 @@ type QuestionRepo interface {
UpdateLastAnswer(ctx context.Context, question *entity.Question) (err error)
FindByID(ctx context.Context, id []string) (questionList []*entity.Question, err error)
CmsSearchList(ctx context.Context, search *schema.CmsQuestionSearch) ([]*entity.Question, int64, error)
GetQuestionCount(ctx context.Context) (count int64, err error)
}
// QuestionCommon user service

View File

@ -2,6 +2,7 @@ package report_common
import (
"context"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
)
@ -12,4 +13,5 @@ type ReportRepo interface {
GetReportListPage(ctx context.Context, query schema.GetReportListPageDTO) (reports []entity.Report, total int64, err error)
GetByID(ctx context.Context, id string) (report entity.Report, exist bool, err error)
UpdateByID(ctx context.Context, id string, handleData entity.Report) (err error)
GetReportCount(ctx context.Context) (count int64, err error)
}

View File

@ -22,6 +22,7 @@ type UserRepo interface {
BatchGetByID(ctx context.Context, ids []string) ([]*entity.User, error)
GetByUsername(ctx context.Context, username string) (userInfo *entity.User, exist bool, err error)
GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error)
GetUserCount(ctx context.Context) (count int64, err error)
}
// UserCommon user service