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
+}

Reply via email to