diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index 904bfd7d..d73d53f1 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -164,7 +164,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, answerActivityRepo := activity.NewAnswerActivityRepo(dataData, activityRepo, userRankRepo) questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo) answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo) - questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService) + questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService, dataData) questionController := controller.NewQuestionController(questionService, rankService) answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo) dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configRepo, siteInfoCommonService, serviceConf, dataData) @@ -207,11 +207,11 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, uiRouter := router.NewUIRouter(siteinfoController) authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) - templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService, answerService, commentService) + templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService, answerService, commentService, dataData, siteInfoCommonService) templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService) templateRouter := router.NewTemplateRouter(templateController, templateRenderController, siteInfoController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) - scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService) + scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService, questionService) application := newApplication(serverConf, ginEngine, scheduledTaskManager) return application, func() { cleanup2() diff --git a/internal/base/cron/cron.go b/internal/base/cron/cron.go index 5ef52a72..f357eeb6 100644 --- a/internal/base/cron/cron.go +++ b/internal/base/cron/cron.go @@ -1,18 +1,31 @@ package cron -import "github.com/answerdev/answer/internal/service/siteinfo_common" +import ( + "context" + + "github.com/answerdev/answer/internal/service" + "github.com/answerdev/answer/internal/service/siteinfo_common" +) // ScheduledTaskManager scheduled task manager type ScheduledTaskManager struct { siteInfoService *siteinfo_common.SiteInfoCommonService + questionService *service.QuestionService } // NewScheduledTaskManager new scheduled task manager -func NewScheduledTaskManager(siteInfoService *siteinfo_common.SiteInfoCommonService) *ScheduledTaskManager { - manager := &ScheduledTaskManager{siteInfoService: siteInfoService} +func NewScheduledTaskManager( + siteInfoService *siteinfo_common.SiteInfoCommonService, + questionService *service.QuestionService, +) *ScheduledTaskManager { + manager := &ScheduledTaskManager{ + siteInfoService: siteInfoService, + questionService: questionService, + } return manager } func (s *ScheduledTaskManager) Run() { - + ctx := context.Background() + s.questionService.SitemapCron(ctx) } diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go index 20aa388f..bf5d5f8d 100644 --- a/internal/controller/template_render/controller.go +++ b/internal/controller/template_render/controller.go @@ -1,9 +1,12 @@ package templaterender import ( - "github.com/answerdev/answer/internal/service/comment" "math" + "github.com/answerdev/answer/internal/base/data" + "github.com/answerdev/answer/internal/service/comment" + "github.com/answerdev/answer/internal/service/siteinfo_common" + "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service" "github.com/answerdev/answer/internal/service/tag" @@ -21,6 +24,8 @@ type TemplateRenderController struct { tagService *tag.TagService answerService *service.AnswerService commentService *comment.CommentService + data *data.Data + siteInfoService *siteinfo_common.SiteInfoCommonService } func NewTemplateRenderController( @@ -29,6 +34,9 @@ func NewTemplateRenderController( tagService *tag.TagService, answerService *service.AnswerService, commentService *comment.CommentService, + data *data.Data, + siteInfoService *siteinfo_common.SiteInfoCommonService, + ) *TemplateRenderController { return &TemplateRenderController{ questionService: questionService, @@ -36,6 +44,8 @@ func NewTemplateRenderController( tagService: tagService, answerService: answerService, commentService: commentService, + data: data, + siteInfoService: siteInfoService, } } diff --git a/internal/controller/template_render/question.go b/internal/controller/template_render/question.go index ddc3fc20..e0362367 100644 --- a/internal/controller/template_render/question.go +++ b/internal/controller/template_render/question.go @@ -1,11 +1,14 @@ package templaterender import ( + "encoding/json" + "fmt" "html/template" "net/http" "github.com/answerdev/answer/internal/schema" "github.com/gin-gonic/gin" + "github.com/segmentfault/pacman/log" ) func (t *TemplateRenderController) Index(ctx *gin.Context, req *schema.QuestionSearch) ([]*schema.QuestionInfo, int64, error) { @@ -17,33 +20,71 @@ func (t *TemplateRenderController) QuestionDetail(ctx *gin.Context, id string) ( } func (t *TemplateRenderController) Sitemap(ctx *gin.Context) { - if 1 == 1 { + general, err := t.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Error("get site general failed:", err) + return + } + + sitemapInfo := &schema.SiteMapList{} + infoStr, err := t.data.Cache.GetString(ctx, schema.SitemapCachekey) + if err != nil { + log.Errorf("get Cache failed: %s", err) + return + } + if err = json.Unmarshal([]byte(infoStr), sitemapInfo); err != nil { + log.Errorf("get sitemap info failed: %s", err) + return + } + + if len(sitemapInfo.QuestionIDs) > 0 { + //question url list + ctx.Header("Content-Type", "application/xml") + ctx.HTML( + http.StatusOK, "sitemap.xml", gin.H{ + "xmlHeader": template.HTML(``), + "list": sitemapInfo.QuestionIDs, + "general": general, + }, + ) + } else { //question list page ctx.Header("Content-Type", "application/xml") ctx.HTML( http.StatusOK, "sitemap-list.xml", gin.H{ "xmlHeader": template.HTML(``), - "list": "string", + "page": sitemapInfo.MaxPageNum, + "general": general, }, ) return } - //question url list - ctx.Header("Content-Type", "application/xml") - ctx.HTML( - http.StatusOK, "sitemap.xml", gin.H{ - "xmlHeader": template.HTML(``), - "list": "string", - }, - ) } func (t *TemplateRenderController) SitemapPage(ctx *gin.Context, page int) error { + sitemapInfo := &schema.SiteMapPageList{} + general, err := t.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Error("get site general failed:", err) + return err + } + + cachekey := fmt.Sprintf(schema.SitemapPageCachekey, page) + infoStr, err := t.data.Cache.GetString(ctx, cachekey) + if err != nil { + log.Errorf("get Cache failed: %s", err) + return err + } + if err = json.Unmarshal([]byte(infoStr), sitemapInfo); err != nil { + log.Errorf("get sitemap info failed: %s", err) + return err + } ctx.Header("Content-Type", "application/xml") ctx.HTML( http.StatusOK, "sitemap.xml", gin.H{ "xmlHeader": template.HTML(``), - "list": "string", + "list": sitemapInfo.PageData, + "general": general, }, ) return nil diff --git a/internal/repo/question/question_repo.go b/internal/repo/question/question_repo.go index 82686fe5..fc03850a 100644 --- a/internal/repo/question/question_repo.go +++ b/internal/repo/question/question_repo.go @@ -16,6 +16,7 @@ import ( "github.com/answerdev/answer/internal/schema" questioncommon "github.com/answerdev/answer/internal/service/question_common" "github.com/answerdev/answer/internal/service/unique" + "github.com/answerdev/answer/pkg/htmltext" "github.com/segmentfault/pacman/errors" ) @@ -173,6 +174,36 @@ func (qr *questionRepo) GetQuestionCount(ctx context.Context) (count int64, err return } +func (qr *questionRepo) GetQuestionIDsPage(ctx context.Context, page, pageSize int) (questionIDList []*schema.SiteMapQuestionInfo, err error) { + questionIDList = make([]*schema.SiteMapQuestionInfo, 0) + rows := make([]*entity.Question, 0) + if page > 0 { + page = page - 1 + } else { + page = 0 + } + if pageSize == 0 { + pageSize = constant.DefaultPageSize + } + offset := page * pageSize + session := qr.data.DB.Table("question") + session = session.In("question.status", []int{entity.QuestionStatusAvailable, entity.QuestionStatusClosed}) + session = session.Limit(pageSize, offset) + session = session.OrderBy("question.created_at asc") + err = session.Select("id,title,post_update_time").Find(&rows) + if err != nil { + return questionIDList, err + } + for _, question := range rows { + item := &schema.SiteMapQuestionInfo{} + item.ID = question.ID + item.Title = htmltext.UrlTitle(question.Title) + item.UpdateTime = question.PostUpdateTime.Format("2006-01-02 15:04:05") + questionIDList = append(questionIDList, item) + } + return questionIDList, nil +} + // 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) diff --git a/internal/schema/question_schema.go b/internal/schema/question_schema.go index 5b3a5586..30fd4863 100644 --- a/internal/schema/question_schema.go +++ b/internal/schema/question_schema.go @@ -1,5 +1,12 @@ package schema +const ( + // SitemapMaxSize = 50000 + SitemapMaxSize = 10 + SitemapCachekey = "answer@sitemap" + SitemapPageCachekey = "answer@sitemap@page%d" +) + // RemoveQuestionReq delete question request type RemoveQuestionReq struct { // question id @@ -218,3 +225,18 @@ type AdminSetQuestionStatusRequest struct { StatusStr string `json:"status" form:"status"` QuestionID string `json:"question_id" form:"question_id"` } + +type SiteMapList struct { + QuestionIDs []*SiteMapQuestionInfo `json:"question_ids"` + MaxPageNum []int `json:"max_page_num"` +} + +type SiteMapPageList struct { + PageData []*SiteMapQuestionInfo `json:"page_data"` +} + +type SiteMapQuestionInfo struct { + ID string `json:"id"` + Title string `json:"title"` + UpdateTime string `json:"time"` +} diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go index 3be2c3f9..0f732bc7 100644 --- a/internal/service/question_common/question.go +++ b/internal/service/question_common/question.go @@ -41,6 +41,7 @@ type QuestionRepo interface { 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) + GetQuestionIDsPage(ctx context.Context, page, pageSize int) (questionIDList []*schema.SiteMapQuestionInfo, err error) } // QuestionCommon user service diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 3788c702..4d8c1b98 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -3,10 +3,12 @@ package service import ( "encoding/json" "fmt" + "math" "strings" "time" "github.com/answerdev/answer/internal/base/constant" + "github.com/answerdev/answer/internal/base/data" "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/translator" @@ -43,6 +45,7 @@ type QuestionService struct { metaService *meta.MetaService collectionCommon *collectioncommon.CollectionCommon answerActivityService *activity.AnswerActivityService + data *data.Data } func NewQuestionService( @@ -54,6 +57,8 @@ func NewQuestionService( metaService *meta.MetaService, collectionCommon *collectioncommon.CollectionCommon, answerActivityService *activity.AnswerActivityService, + data *data.Data, + ) *QuestionService { return &QuestionService{ questionRepo: questionRepo, @@ -64,6 +69,7 @@ func NewQuestionService( metaService: metaService, collectionCommon: collectionCommon, answerActivityService: answerActivityService, + data: data, } } @@ -1001,3 +1007,57 @@ func (qs *QuestionService) changeQuestionToRevision(ctx context.Context, questio } return questionRevision, nil } + +func (qs *QuestionService) SitemapCron(ctx context.Context) { + data := &schema.SiteMapList{} + questionNum, err := qs.questionRepo.GetQuestionCount(ctx) + if err != nil { + log.Error("GetQuestionCount error", err) + return + } + if questionNum <= schema.SitemapMaxSize { + questionIDList, err := qs.questionRepo.GetQuestionIDsPage(ctx, 0, int(questionNum)) + if err != nil { + log.Error("GetQuestionIDsPage error", err) + return + } + data.QuestionIDs = questionIDList + + } else { + nums := make([]int, 0) + totalpages := int(math.Ceil(float64(questionNum) / float64(schema.SitemapMaxSize))) + for i := 1; i <= totalpages; i++ { + siteMapPagedata := &schema.SiteMapPageList{} + nums = append(nums, i) + questionIDList, err := qs.questionRepo.GetQuestionIDsPage(ctx, i, int(schema.SitemapMaxSize)) + if err != nil { + log.Error("GetQuestionIDsPage error", err) + return + } + siteMapPagedata.PageData = questionIDList + if setCacheErr := qs.SetCache(ctx, fmt.Sprintf(schema.SitemapPageCachekey, i), siteMapPagedata); setCacheErr != nil { + log.Errorf("set sitemap cron SetCache failed: %s", setCacheErr) + } + } + data.MaxPageNum = nums + } + if setCacheErr := qs.SetCache(ctx, schema.SitemapCachekey, data); setCacheErr != nil { + log.Errorf("set sitemap cron SetCache failed: %s", setCacheErr) + } + + return + +} + +func (qs *QuestionService) SetCache(ctx context.Context, cachekey string, info interface{}) error { + infoStr, err := json.Marshal(info) + if err != nil { + return errors.InternalServer(reason.UnknownError).WithError(err).WithStack() + } + + err = qs.data.Cache.SetString(ctx, cachekey, string(infoStr), schema.DashBoardCacheTime) + if err != nil { + return errors.InternalServer(reason.UnknownError).WithError(err).WithStack() + } + return nil +} diff --git a/ui/template/sitemap-list.xml b/ui/template/sitemap-list.xml index 55d4a0c9..1d867434 100644 --- a/ui/template/sitemap-list.xml +++ b/ui/template/sitemap-list.xml @@ -1,6 +1,8 @@ {{ .xmlHeader }} + {{ range .page }} - http://www.example.com/sitemap1.xml + {{$.general.SiteUrl}}/sitemap/question-{{.}}.xml + {{ end }} diff --git a/ui/template/sitemap.xml b/ui/template/sitemap.xml index 0fbbcccf..b763c084 100644 --- a/ui/template/sitemap.xml +++ b/ui/template/sitemap.xml @@ -1,7 +1,9 @@ {{ .xmlHeader }} + {{ range .list }} - http://www.example.com/foo1 - 2018-06-04 + {{$.general.SiteUrl}}/questions/{{.ID}}/{{.Title}} + {{.UpdateTime}} + {{ end }}