chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* 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 activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/service/activity_common"
|
||||
metacommon "github.com/apache/answer/internal/service/meta_common"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/base/handler"
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/apache/answer/internal/schema"
|
||||
"github.com/apache/answer/internal/service/comment_common"
|
||||
"github.com/apache/answer/internal/service/config"
|
||||
"github.com/apache/answer/internal/service/object_info"
|
||||
"github.com/apache/answer/internal/service/revision_common"
|
||||
"github.com/apache/answer/internal/service/tag_common"
|
||||
usercommon "github.com/apache/answer/internal/service/user_common"
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
"github.com/apache/answer/pkg/obj"
|
||||
"github.com/apache/answer/pkg/uid"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
// ActivityRepo activity repository
|
||||
type ActivityRepo interface {
|
||||
GetObjectAllActivity(ctx context.Context, objectID string, showVote bool) (activityList []*entity.Activity, err error)
|
||||
}
|
||||
|
||||
// ActivityService activity service
|
||||
type ActivityService struct {
|
||||
activityRepo ActivityRepo
|
||||
userCommon *usercommon.UserCommon
|
||||
activityCommonService *activity_common.ActivityCommon
|
||||
tagCommonService *tag_common.TagCommonService
|
||||
objectInfoService *object_info.ObjService
|
||||
commentCommonService *comment_common.CommentCommonService
|
||||
revisionService *revision_common.RevisionService
|
||||
metaService *metacommon.MetaCommonService
|
||||
configService *config.ConfigService
|
||||
}
|
||||
|
||||
// NewActivityService new activity service
|
||||
func NewActivityService(
|
||||
activityRepo ActivityRepo,
|
||||
userCommon *usercommon.UserCommon,
|
||||
activityCommonService *activity_common.ActivityCommon,
|
||||
tagCommonService *tag_common.TagCommonService,
|
||||
objectInfoService *object_info.ObjService,
|
||||
commentCommonService *comment_common.CommentCommonService,
|
||||
revisionService *revision_common.RevisionService,
|
||||
metaService *metacommon.MetaCommonService,
|
||||
configService *config.ConfigService,
|
||||
) *ActivityService {
|
||||
return &ActivityService{
|
||||
objectInfoService: objectInfoService,
|
||||
activityRepo: activityRepo,
|
||||
userCommon: userCommon,
|
||||
activityCommonService: activityCommonService,
|
||||
tagCommonService: tagCommonService,
|
||||
commentCommonService: commentCommonService,
|
||||
revisionService: revisionService,
|
||||
metaService: metaService,
|
||||
configService: configService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetObjectTimeline get object timeline
|
||||
func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.GetObjectTimelineReq) (
|
||||
resp *schema.GetObjectTimelineResp, err error) {
|
||||
if err = as.ensureTimelineObjectVisible(ctx, req.ObjectID, req.UserID, req.IsAdminModerator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp = &schema.GetObjectTimelineResp{
|
||||
ObjectInfo: &schema.ActObjectInfo{},
|
||||
Timeline: make([]*schema.ActObjectTimeline, 0),
|
||||
}
|
||||
|
||||
resp.ObjectInfo, err = as.getTimelineMainObjInfo(ctx, req.ObjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
activityList, err := as.activityRepo.GetObjectAllActivity(ctx, req.ObjectID, req.ShowVote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, act := range activityList {
|
||||
item := &schema.ActObjectTimeline{
|
||||
ActivityID: act.ID,
|
||||
RevisionID: converter.IntToString(act.RevisionID),
|
||||
CreatedAt: act.CreatedAt.Unix(),
|
||||
Cancelled: act.Cancelled == entity.ActivityCancelled,
|
||||
ObjectID: act.ObjectID,
|
||||
UserInfo: &schema.UserBasicInfo{},
|
||||
}
|
||||
item.ObjectType, _ = obj.GetObjectTypeStrByObjectID(act.ObjectID)
|
||||
if item.Cancelled {
|
||||
item.CancelledAt = act.CancelledAt.Unix()
|
||||
}
|
||||
|
||||
if item.ObjectType == constant.QuestionObjectType || item.ObjectType == constant.AnswerObjectType {
|
||||
if handler.GetEnableShortID(ctx) {
|
||||
item.ObjectID = uid.EnShortID(act.ObjectID)
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := as.configService.GetConfigByID(ctx, act.ActivityType)
|
||||
if err != nil {
|
||||
log.Errorf("fail to get config by id: %d, err: %v, act id is: %s", act.ActivityType, err, act.ID)
|
||||
} else {
|
||||
// database save activity type is number, change to activity type string is like "question.asked".
|
||||
// so we need to cut the front part of '.', only need string like 'asked'
|
||||
_, item.ActivityType, _ = strings.Cut(cfg.Key, ".")
|
||||
// format activity type string to show
|
||||
if isHidden, formattedActivityType := formatActivity(item.ActivityType); isHidden {
|
||||
continue
|
||||
} else {
|
||||
item.ActivityType = formattedActivityType
|
||||
}
|
||||
}
|
||||
|
||||
// if activity is down vote, only admin can see who does it.
|
||||
if item.ActivityType == constant.ActDownVote && !req.IsAdmin {
|
||||
item.UserInfo.Username = "N/A"
|
||||
item.UserInfo.DisplayName = "N/A"
|
||||
} else {
|
||||
if act.TriggerUserID > 0 {
|
||||
item.UserInfo.ID = fmt.Sprintf("%d", act.TriggerUserID)
|
||||
} else {
|
||||
item.UserInfo.ID = act.UserID
|
||||
}
|
||||
}
|
||||
|
||||
item.Comment = as.getTimelineActivityComment(ctx, item.ObjectID, item.ObjectType, item.ActivityType, item.RevisionID)
|
||||
resp.Timeline = append(resp.Timeline, item)
|
||||
}
|
||||
as.formatTimelineUserInfo(ctx, resp.Timeline)
|
||||
return
|
||||
}
|
||||
|
||||
func (as *ActivityService) getTimelineMainObjInfo(ctx context.Context, objectID string) (
|
||||
resp *schema.ActObjectInfo, err error) {
|
||||
resp = &schema.ActObjectInfo{}
|
||||
objInfo, err := as.objectInfoService.GetInfo(ctx, objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Title = objInfo.Title
|
||||
if objInfo.ObjectType == constant.TagObjectType {
|
||||
tag, exist, _ := as.tagCommonService.GetTagByID(ctx, objInfo.TagID)
|
||||
if exist {
|
||||
resp.Title = tag.SlugName
|
||||
resp.MainTagSlugName = tag.MainTagSlugName
|
||||
}
|
||||
}
|
||||
resp.ObjectType = objInfo.ObjectType
|
||||
resp.QuestionID = objInfo.QuestionID
|
||||
resp.AnswerID = objInfo.AnswerID
|
||||
if len(objInfo.ObjectCreatorUserID) > 0 {
|
||||
// get object creator user info
|
||||
userBasicInfo, exist, err := as.userCommon.GetUserBasicInfoByID(ctx, objInfo.ObjectCreatorUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
resp.Username = userBasicInfo.Username
|
||||
resp.DisplayName = userBasicInfo.DisplayName
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (as *ActivityService) getTimelineActivityComment(ctx context.Context, objectID, objectType,
|
||||
activityType, revisionID string) (comment string) {
|
||||
if objectType == constant.CommentObjectType {
|
||||
commentInfo, err := as.commentCommonService.GetComment(ctx, objectID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
return commentInfo.ParsedText
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if activityType == constant.ActEdited {
|
||||
revision, err := as.revisionService.GetRevision(ctx, revisionID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
return converter.Markdown2HTML(revision.Log)
|
||||
}
|
||||
return
|
||||
}
|
||||
if activityType == constant.ActClosed {
|
||||
// only question can be closed
|
||||
metaInfo, err := as.metaService.GetMetaByObjectIdAndKey(ctx, objectID, entity.QuestionCloseReasonKey)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
closeMsg := &schema.CloseQuestionMeta{}
|
||||
if err := json.Unmarshal([]byte(metaInfo.Value), closeMsg); err == nil {
|
||||
return converter.Markdown2HTML(closeMsg.CloseMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (as *ActivityService) formatTimelineUserInfo(ctx context.Context, timeline []*schema.ActObjectTimeline) {
|
||||
userExist := make(map[string]bool)
|
||||
userIDs := make([]string, 0)
|
||||
for _, info := range timeline {
|
||||
if len(info.UserInfo.ID) == 0 || userExist[info.UserInfo.ID] {
|
||||
continue
|
||||
}
|
||||
userIDs = append(userIDs, info.UserInfo.ID)
|
||||
}
|
||||
if len(userIDs) == 0 {
|
||||
return
|
||||
}
|
||||
userInfoMapping, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIDs)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
for _, info := range timeline {
|
||||
if len(info.UserInfo.ID) == 0 {
|
||||
continue
|
||||
}
|
||||
info.UserInfo = userInfoMapping[info.UserInfo.ID]
|
||||
}
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetail get object timeline
|
||||
func (as *ActivityService) GetObjectTimelineDetail(ctx context.Context, req *schema.GetObjectTimelineDetailReq) (
|
||||
resp *schema.GetObjectTimelineDetailResp, err error) {
|
||||
if err = as.ensureTimelineRevisionVisible(ctx, req.NewRevisionID, req.UserID, req.IsAdminModerator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = as.ensureTimelineRevisionVisible(ctx, req.OldRevisionID, req.UserID, req.IsAdminModerator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp = &schema.GetObjectTimelineDetailResp{}
|
||||
resp.OldRevision, _ = as.getOneObjectDetail(ctx, req.OldRevisionID)
|
||||
resp.NewRevision, _ = as.getOneObjectDetail(ctx, req.NewRevisionID)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (as *ActivityService) ensureTimelineRevisionVisible(ctx context.Context, revisionID, userID string,
|
||||
isAdminModerator bool) error {
|
||||
if revisionID == "0" {
|
||||
return nil
|
||||
}
|
||||
revisionInfo, err := as.revisionService.GetRevision(ctx, revisionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return as.ensureTimelineObjectVisible(ctx, revisionInfo.ObjectID, userID, isAdminModerator)
|
||||
}
|
||||
|
||||
func (as *ActivityService) ensureTimelineObjectVisible(ctx context.Context, objectID, userID string,
|
||||
isAdminModerator bool) error {
|
||||
objInfo, err := as.objectInfoService.GetInfo(ctx, objectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var parentQuestionInfo *schema.SimpleObjectInfo
|
||||
if objInfo.ObjectType != constant.QuestionObjectType && len(objInfo.QuestionID) > 0 && objInfo.QuestionID != "0" {
|
||||
parentQuestionInfo, err = as.objectInfoService.GetInfo(ctx, objInfo.QuestionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return validateTimelineObjectVisibility(objInfo, parentQuestionInfo, userID, isAdminModerator)
|
||||
}
|
||||
|
||||
func validateTimelineObjectVisibility(objInfo, parentQuestionInfo *schema.SimpleObjectInfo,
|
||||
userID string, isAdminModerator bool) error {
|
||||
if objInfo == nil {
|
||||
return errors.NotFound(reason.ObjectNotFound)
|
||||
}
|
||||
if isTimelineObjectRestricted(objInfo) &&
|
||||
!canViewRestrictedTimelineObject(objInfo.ObjectType, objInfo.ObjectCreatorUserID, userID, isAdminModerator) {
|
||||
return errors.NotFound(timelineNotFoundReasonByObjectType(objInfo.ObjectType))
|
||||
}
|
||||
if parentQuestionInfo != nil && isTimelineQuestionRestricted(parentQuestionInfo) &&
|
||||
!canViewRestrictedTimelineObject(parentQuestionInfo.ObjectType, parentQuestionInfo.ObjectCreatorUserID,
|
||||
userID, isAdminModerator) {
|
||||
return errors.NotFound(reason.QuestionNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func canViewRestrictedTimelineObject(objectType, creatorUserID, userID string, isAdminModerator bool) bool {
|
||||
if isAdminModerator {
|
||||
return true
|
||||
}
|
||||
switch objectType {
|
||||
case constant.QuestionObjectType, constant.AnswerObjectType, constant.CommentObjectType:
|
||||
return creatorUserID == userID
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isTimelineObjectRestricted(objInfo *schema.SimpleObjectInfo) bool {
|
||||
switch objInfo.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
return isTimelineQuestionRestricted(objInfo)
|
||||
case constant.AnswerObjectType:
|
||||
return objInfo.AnswerStatus == entity.AnswerStatusDeleted || objInfo.AnswerStatus == entity.AnswerStatusPending
|
||||
case constant.CommentObjectType:
|
||||
return objInfo.CommentStatus == entity.CommentStatusDeleted || objInfo.CommentStatus == entity.CommentStatusPending
|
||||
case constant.TagObjectType:
|
||||
return objInfo.TagStatus == entity.TagStatusDeleted
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isTimelineQuestionRestricted(questionInfo *schema.SimpleObjectInfo) bool {
|
||||
return questionInfo.QuestionStatus == entity.QuestionStatusDeleted ||
|
||||
questionInfo.QuestionStatus == entity.QuestionStatusPending ||
|
||||
questionInfo.QuestionShow == entity.QuestionHide
|
||||
}
|
||||
|
||||
func timelineNotFoundReasonByObjectType(objectType string) string {
|
||||
switch objectType {
|
||||
case constant.QuestionObjectType:
|
||||
return reason.QuestionNotFound
|
||||
case constant.AnswerObjectType:
|
||||
return reason.AnswerNotFound
|
||||
case constant.CommentObjectType:
|
||||
return reason.CommentNotFound
|
||||
case constant.TagObjectType:
|
||||
return reason.TagNotFound
|
||||
default:
|
||||
return reason.ObjectNotFound
|
||||
}
|
||||
}
|
||||
|
||||
// getOneObjectDetail get object detail
|
||||
func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID string) (
|
||||
resp *schema.ObjectTimelineDetail, err error) {
|
||||
resp = &schema.ObjectTimelineDetail{Tags: make([]*schema.ObjectTimelineTag, 0)}
|
||||
|
||||
// if request revision is 0, return null object detail.
|
||||
if revisionID == "0" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
revision, err := as.revisionService.GetRevision(ctx, revisionID)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return nil, nil
|
||||
}
|
||||
objInfo, err := as.objectInfoService.GetInfo(ctx, revision.ObjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch objInfo.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
data := &entity.QuestionWithTagsRevision{}
|
||||
if err = json.Unmarshal([]byte(revision.Content), data); err != nil {
|
||||
log.Errorf("revision parsing error %s", err)
|
||||
return resp, nil
|
||||
}
|
||||
for _, tag := range data.Tags {
|
||||
resp.Tags = append(resp.Tags, &schema.ObjectTimelineTag{
|
||||
SlugName: tag.SlugName,
|
||||
DisplayName: tag.DisplayName,
|
||||
MainTagSlugName: tag.MainTagSlugName,
|
||||
Recommend: tag.Recommend,
|
||||
Reserved: tag.Reserved,
|
||||
})
|
||||
}
|
||||
resp.Title = data.Title
|
||||
resp.OriginalText = data.OriginalText
|
||||
case constant.AnswerObjectType:
|
||||
data := &entity.Answer{}
|
||||
if err = json.Unmarshal([]byte(revision.Content), data); err != nil {
|
||||
log.Errorf("revision parsing error %s", err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.Title = objInfo.Title // answer show question title
|
||||
resp.OriginalText = data.OriginalText
|
||||
case constant.TagObjectType:
|
||||
data := &entity.Tag{}
|
||||
if err = json.Unmarshal([]byte(revision.Content), data); err != nil {
|
||||
log.Errorf("revision parsing error %s", err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.Title = data.DisplayName
|
||||
resp.OriginalText = data.OriginalText
|
||||
resp.SlugName = data.SlugName
|
||||
resp.MainTagSlugName = data.MainTagSlugName
|
||||
default:
|
||||
log.Errorf("unknown object type %s", objInfo.ObjectType)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func formatActivity(activityType string) (isHidden bool, formattedActivityType string) {
|
||||
if activityType == constant.ActVotedUp ||
|
||||
activityType == constant.ActVotedDown ||
|
||||
activityType == constant.ActFollow {
|
||||
return true, ""
|
||||
}
|
||||
if activityType == constant.ActVoteUp {
|
||||
return false, constant.ActUpVote
|
||||
}
|
||||
if activityType == constant.ActVoteDown {
|
||||
return false, constant.ActDownVote
|
||||
}
|
||||
if activityType == constant.ActAccepted {
|
||||
return false, constant.ActAccept
|
||||
}
|
||||
return false, activityType
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/apache/answer/internal/schema"
|
||||
"github.com/apache/answer/internal/service/activity_type"
|
||||
"github.com/apache/answer/internal/service/config"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
// AnswerActivityRepo answer activity
|
||||
type AnswerActivityRepo interface {
|
||||
SaveAcceptAnswerActivity(ctx context.Context, op *schema.AcceptAnswerOperationInfo) (err error)
|
||||
SaveCancelAcceptAnswerActivity(ctx context.Context, op *schema.AcceptAnswerOperationInfo) (err error)
|
||||
}
|
||||
|
||||
// AnswerActivityService answer activity service
|
||||
type AnswerActivityService struct {
|
||||
answerActivityRepo AnswerActivityRepo
|
||||
configService *config.ConfigService
|
||||
}
|
||||
|
||||
// NewAnswerActivityService new comment service
|
||||
func NewAnswerActivityService(
|
||||
answerActivityRepo AnswerActivityRepo,
|
||||
configService *config.ConfigService,
|
||||
) *AnswerActivityService {
|
||||
return &AnswerActivityService{
|
||||
answerActivityRepo: answerActivityRepo,
|
||||
configService: configService,
|
||||
}
|
||||
}
|
||||
|
||||
// AcceptAnswer accept answer change activity
|
||||
func (as *AnswerActivityService) AcceptAnswer(ctx context.Context,
|
||||
loginUserID, answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) (err error) {
|
||||
log.Debugf("user %s want to accept answer %s[%s] for question %s[%s]", loginUserID,
|
||||
answerObjID, answerUserID,
|
||||
questionObjID, questionUserID)
|
||||
operationInfo := as.createAcceptAnswerOperationInfo(ctx, loginUserID,
|
||||
answerObjID, questionObjID, questionUserID, answerUserID, isSelf)
|
||||
return as.answerActivityRepo.SaveAcceptAnswerActivity(ctx, operationInfo)
|
||||
}
|
||||
|
||||
// CancelAcceptAnswer cancel accept answer change activity
|
||||
func (as *AnswerActivityService) CancelAcceptAnswer(ctx context.Context,
|
||||
loginUserID, answerObjID, questionObjID, questionUserID, answerUserID string) (err error) {
|
||||
operationInfo := as.createAcceptAnswerOperationInfo(ctx, loginUserID,
|
||||
answerObjID, questionObjID, questionUserID, answerUserID, false)
|
||||
return as.answerActivityRepo.SaveCancelAcceptAnswerActivity(ctx, operationInfo)
|
||||
}
|
||||
|
||||
func (as *AnswerActivityService) createAcceptAnswerOperationInfo(ctx context.Context, loginUserID,
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) *schema.AcceptAnswerOperationInfo {
|
||||
operationInfo := &schema.AcceptAnswerOperationInfo{
|
||||
TriggerUserID: loginUserID,
|
||||
QuestionObjectID: questionObjID,
|
||||
QuestionUserID: questionUserID,
|
||||
AnswerObjectID: answerObjID,
|
||||
AnswerUserID: answerUserID,
|
||||
}
|
||||
operationInfo.Activities = as.getActivities(ctx, operationInfo)
|
||||
if isSelf {
|
||||
for _, activity := range operationInfo.Activities {
|
||||
activity.Rank = 0
|
||||
}
|
||||
}
|
||||
return operationInfo
|
||||
}
|
||||
|
||||
func (as *AnswerActivityService) getActivities(ctx context.Context, op *schema.AcceptAnswerOperationInfo) (
|
||||
activities []*schema.AcceptAnswerActivity) {
|
||||
activities = make([]*schema.AcceptAnswerActivity, 0)
|
||||
|
||||
for _, action := range []string{activity_type.AnswerAccept, activity_type.AnswerAccepted} {
|
||||
t := &schema.AcceptAnswerActivity{}
|
||||
cfg, err := as.configService.GetConfigByKey(ctx, action)
|
||||
if err != nil {
|
||||
log.Warnf("get config by key error: %v", err)
|
||||
continue
|
||||
}
|
||||
t.ActivityType, t.Rank = cfg.ID, cfg.GetIntValue()
|
||||
|
||||
if action == activity_type.AnswerAccept {
|
||||
t.ActivityUserID = op.QuestionUserID
|
||||
t.TriggerUserID = op.TriggerUserID
|
||||
t.OriginalObjectID = op.QuestionObjectID // if activity is 'accept' means this question is accept the answer.
|
||||
} else {
|
||||
t.ActivityUserID = op.AnswerUserID
|
||||
t.TriggerUserID = op.TriggerUserID
|
||||
t.OriginalObjectID = op.AnswerObjectID // if activity is 'accepted' means this answer was accepted.
|
||||
}
|
||||
activities = append(activities, t)
|
||||
}
|
||||
return activities
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/apache/answer/internal/schema"
|
||||
)
|
||||
|
||||
// ReviewActivityRepo interface
|
||||
type ReviewActivityRepo interface {
|
||||
Review(ctx context.Context, sct *schema.PassReviewActivity) (err error)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 activity
|
||||
|
||||
import "context"
|
||||
|
||||
// UserActiveActivityRepo interface
|
||||
type UserActiveActivityRepo interface {
|
||||
UserActive(ctx context.Context, userID string) (err error)
|
||||
}
|
||||
Reference in New Issue
Block a user