This is an automated email from the ASF dual-hosted git repository.
linkinstar pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
The following commit(s) were added to refs/heads/dev by this push:
new 23cc755a feat(question): add question linking feature for closing
question
23cc755a is described below
commit 23cc755aa8ba7d536c8c5db36022264efc5b2201
Author: LinkinStars <[email protected]>
AuthorDate: Fri Oct 18 15:55:31 2024 +0800
feat(question): add question linking feature for closing question
---
cmd/wire_gen.go | 2 +-
internal/service/content/question_service.go | 4 +
internal/service/question_common/question.go | 76 ++++++++++++++++
plugin/importer.go | 124 +++++++++++++--------------
4 files changed, 143 insertions(+), 63 deletions(-)
diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index d8e6c744..801c5a77 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -177,7 +177,7 @@ func initApplication(debug bool, serverConf *conf.Server,
dbConf *data.Database,
answerCommon := answercommon.NewAnswerCommon(answerRepo)
metaRepo := meta.NewMetaRepo(dataData)
metaCommonService := metacommon.NewMetaCommonService(metaRepo)
- questionCommon := questioncommon.NewQuestionCommon(questionRepo,
answerRepo, voteRepo, followRepo, tagCommonService, userCommon,
collectionCommon, answerCommon, metaCommonService, configService,
activityQueueService, revisionRepo, dataData)
+ questionCommon := questioncommon.NewQuestionCommon(questionRepo,
answerRepo, voteRepo, followRepo, tagCommonService, userCommon,
collectionCommon, answerCommon, metaCommonService, configService,
activityQueueService, revisionRepo, siteInfoCommonService, dataData)
eventQueueService := event_queue.NewEventQueueService()
userService := content.NewUserService(userRepo, userActiveActivityRepo,
activityRepo, emailService, authService, siteInfoCommonService,
userRoleRelService, userCommon, userExternalLoginService,
userNotificationConfigRepo, userNotificationConfigService, questionCommon,
eventQueueService)
captchaRepo := captcha.NewCaptchaRepo(dataData)
diff --git a/internal/service/content/question_service.go
b/internal/service/content/question_service.go
index 7cfd39a0..51f0da9b 100644
--- a/internal/service/content/question_service.go
+++ b/internal/service/content/question_service.go
@@ -174,6 +174,9 @@ func (qs *QuestionService) CloseQuestion(ctx
context.Context, req *schema.CloseQ
if err != nil {
return err
}
+ if cf.Key == constant.ReasonADuplicate {
+ qs.questioncommon.AddQuestionLinkForCloseReason(ctx,
questionInfo, req.CloseMsg)
+ }
qs.activityQueueService.Send(ctx, &schema.ActivityMsg{
UserID: req.UserID,
@@ -199,6 +202,7 @@ func (qs *QuestionService) ReopenQuestion(ctx
context.Context, req *schema.Reope
if err != nil {
return err
}
+ qs.questioncommon.RemoveQuestionLinkForReopen(ctx, questionInfo)
qs.activityQueueService.Send(ctx, &schema.ActivityMsg{
UserID: req.UserID,
ObjectID: questionInfo.ID,
diff --git a/internal/service/question_common/question.go
b/internal/service/question_common/question.go
index 63c8f5a2..13df3389 100644
--- a/internal/service/question_common/question.go
+++ b/internal/service/question_common/question.go
@@ -23,6 +23,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "github.com/apache/incubator-answer/internal/service/siteinfo_common"
"math"
"strings"
"time"
@@ -98,6 +99,7 @@ type QuestionCommon struct {
configService *config.ConfigService
activityQueueService activity_queue.ActivityQueueService
revisionRepo revision.RevisionRepo
+ siteInfoService siteinfo_common.SiteInfoCommonService
data *data.Data
}
@@ -113,6 +115,7 @@ func NewQuestionCommon(questionRepo QuestionRepo,
configService *config.ConfigService,
activityQueueService activity_queue.ActivityQueueService,
revisionRepo revision.RevisionRepo,
+ siteInfoService siteinfo_common.SiteInfoCommonService,
data *data.Data,
) *QuestionCommon {
return &QuestionCommon{
@@ -128,6 +131,7 @@ func NewQuestionCommon(questionRepo QuestionRepo,
configService: configService,
activityQueueService: activityQueueService,
revisionRepo: revisionRepo,
+ siteInfoService: siteInfoService,
data: data,
}
}
@@ -795,3 +799,75 @@ func (qs *QuestionCommon) UpdateQuestionLink(ctx
context.Context, questionID, an
return parsedText, nil
}
+
+// AddQuestionLinkForCloseReason When the reason about close question is a
question link, add the link to the question
+func (qs *QuestionCommon) AddQuestionLinkForCloseReason(ctx context.Context,
+ questionInfo *entity.Question, closeMsg string) {
+ questionID := qs.tryToGetQuestionIDFromMsg(ctx, closeMsg)
+ if len(questionID) == 0 {
+ return
+ }
+
+ linkedQuestion, exist, err := qs.questionRepo.GetQuestion(ctx,
questionID)
+ if err != nil {
+ log.Errorf("get question error %s", err)
+ return
+ }
+ if !exist {
+ return
+ }
+ err = qs.questionRepo.LinkQuestion(ctx, &entity.QuestionLink{
+ FromQuestionID: questionInfo.ID,
+ ToQuestionID: linkedQuestion.ID,
+ Status: entity.QuestionLinkStatusAvailable,
+ })
+ if err != nil {
+ log.Errorf("link question error %s", err)
+ }
+}
+
+func (qs *QuestionCommon) RemoveQuestionLinkForReopen(ctx context.Context,
questionInfo *entity.Question) {
+ questionInfo.ID = uid.DeShortID(questionInfo.ID)
+ metaInfo, err := qs.metaCommonService.GetMetaByObjectIdAndKey(ctx,
questionInfo.ID, entity.QuestionCloseReasonKey)
+ if err != nil {
+ return
+ }
+
+ closeMsgMeta := &schema.CloseQuestionMeta{}
+ _ = json.Unmarshal([]byte(metaInfo.Value), closeMsgMeta)
+
+ linkedQuestionID := qs.tryToGetQuestionIDFromMsg(ctx,
closeMsgMeta.CloseMsg)
+ if len(linkedQuestionID) == 0 {
+ return
+ }
+ err = qs.questionRepo.RemoveQuestionLink(ctx, &entity.QuestionLink{
+ FromQuestionID: questionInfo.ID,
+ ToQuestionID: linkedQuestionID,
+ })
+ if err != nil {
+ log.Errorf("remove question link error %s", err)
+ }
+}
+
+func (qs *QuestionCommon) tryToGetQuestionIDFromMsg(ctx context.Context,
closeMsg string) (questionID string) {
+ siteGeneral, err := qs.siteInfoService.GetSiteGeneral(ctx)
+ if err != nil {
+ log.Errorf("get site general error %s", err)
+ return
+ }
+ if !strings.HasPrefix(closeMsg, siteGeneral.SiteUrl) {
+ return
+ }
+ // get question id from url
+ // the url may like: https://xxx.com/questions/D1401/xxx
+ // the D1401 is question id
+ questionID = strings.TrimPrefix(closeMsg, siteGeneral.SiteUrl)
+ questionID = strings.TrimPrefix(questionID, "/questions/")
+ t := strings.Split(questionID, "/")
+ if len(t) < 1 {
+ return ""
+ }
+ questionID = t[0]
+ questionID = uid.DeShortID(questionID)
+ return questionID
+}
diff --git a/plugin/importer.go b/plugin/importer.go
index 3893d54a..bfd7d36a 100644
--- a/plugin/importer.go
+++ b/plugin/importer.go
@@ -1,62 +1,62 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package plugin
-
-import (
- "context"
-)
-
-type QuestionImporterInfo struct {
- Title string `json:"title"`
- Content string `json:"content"`
- Tags []string `json:"tags"`
- UserEmail string `json:"user_email"`
-}
-
-type Importer interface {
- Base
- RegisterImporterFunc(ctx context.Context, importer ImporterFunc)
-}
-
-type ImporterFunc interface {
- AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo)
(err error)
-}
-
-var (
- // CallImporter is a function that calls all registered parsers
- CallImporter,
- registerImporter = MakePlugin[Importer](false)
-)
-
-func ImporterEnabled() (enabled bool) {
- _ = CallImporter(func(fn Importer) error {
- enabled = true
- return nil
- })
- return
-}
-func GetImporter() (ip Importer, ok bool) {
- _ = CallImporter(func(fn Importer) error {
- ip = fn
- ok = true
- return nil
- })
- return
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package plugin
+
+import (
+ "context"
+)
+
+type QuestionImporterInfo struct {
+ Title string `json:"title"`
+ Content string `json:"content"`
+ Tags []string `json:"tags"`
+ UserEmail string `json:"user_email"`
+}
+
+type Importer interface {
+ Base
+ RegisterImporterFunc(ctx context.Context, importer ImporterFunc)
+}
+
+type ImporterFunc interface {
+ AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo)
(err error)
+}
+
+var (
+ // CallImporter is a function that calls all registered parsers
+ CallImporter,
+ registerImporter = MakePlugin[Importer](false)
+)
+
+func ImporterEnabled() (enabled bool) {
+ _ = CallImporter(func(fn Importer) error {
+ enabled = true
+ return nil
+ })
+ return
+}
+func GetImporter() (ip Importer, ok bool) {
+ _ = CallImporter(func(fn Importer) error {
+ ip = fn
+ ok = true
+ return nil
+ })
+ return
+}