413 lines
12 KiB
Go
413 lines
12 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/LoveLosita/smartflow/backend/respond"
|
|
"github.com/LoveLosita/smartflow/backend/services/taskclassforum/rpc/pb"
|
|
forumsv "github.com/LoveLosita/smartflow/backend/services/taskclassforum/sv"
|
|
forumcontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/taskclassforum"
|
|
)
|
|
|
|
type Handler struct {
|
|
pb.UnimplementedTaskClassForumServiceServer
|
|
svc *forumsv.Service
|
|
}
|
|
|
|
func NewHandler(svc *forumsv.Service) *Handler {
|
|
return &Handler{svc: svc}
|
|
}
|
|
|
|
// service 负责统一校验 RPC 层依赖是否已经注入。
|
|
//
|
|
// 职责边界:
|
|
// 1. 只判断 handler 自身和业务 service 是否可用;
|
|
// 2. 不负责校验请求参数,也不处理具体业务规则;
|
|
// 3. 失败时返回可直接转成 gRPC status 的业务错误。
|
|
func (h *Handler) service() (*forumsv.Service, error) {
|
|
if h == nil || h.svc == nil {
|
|
return nil, errors.New("taskclassforum service dependency not initialized")
|
|
}
|
|
return h.svc, nil
|
|
}
|
|
|
|
// ListPosts 负责把计划广场列表请求从 gRPC 协议转成内部服务调用。
|
|
func (h *Handler) ListPosts(ctx context.Context, req *pb.ListForumPostsRequest) (*pb.ListForumPostsResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
items, page, err := svc.ListPosts(ctx, req.ActorUserId, int(req.Page), int(req.PageSize), req.Sort, req.Keyword, req.Tag)
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.ListForumPostsResponse{
|
|
Items: forumPostBriefsToPB(items),
|
|
Page: forumPageToPB(page),
|
|
}, nil
|
|
}
|
|
|
|
func (h *Handler) ListTags(ctx context.Context, req *pb.ListForumTagsRequest) (*pb.ListForumTagsResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
items, err := svc.ListTags(ctx, req.ActorUserId, int(req.Limit))
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.ListForumTagsResponse{Items: forumTagItemsToPB(items)}, nil
|
|
}
|
|
|
|
func (h *Handler) CreatePost(ctx context.Context, req *pb.CreateForumPostRequest) (*pb.CreateForumPostResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
post, err := svc.CreatePost(ctx, forumcontracts.CreateForumPostRequest{
|
|
ActorUserID: req.ActorUserId,
|
|
TaskClassID: req.TaskClassId,
|
|
Title: req.Title,
|
|
Summary: req.Summary,
|
|
Tags: append([]string(nil), req.Tags...),
|
|
IdempotencyKey: req.IdempotencyKey,
|
|
})
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.CreateForumPostResponse{Post: forumPostBriefToPB(post)}, nil
|
|
}
|
|
|
|
func (h *Handler) GetPost(ctx context.Context, req *pb.GetForumPostRequest) (*pb.GetForumPostResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
data, err := svc.GetPost(ctx, req.ActorUserId, req.PostId)
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.GetForumPostResponse{Data: forumPostDetailToPB(data)}, nil
|
|
}
|
|
|
|
func (h *Handler) LikePost(ctx context.Context, req *pb.LikeForumPostRequest) (*pb.LikeForumPostResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
counters, viewerState, err := svc.LikePost(ctx, req.ActorUserId, req.PostId)
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.LikeForumPostResponse{
|
|
Counters: forumPostCountersToPB(counters),
|
|
ViewerState: forumPostViewerStateToPB(viewerState),
|
|
}, nil
|
|
}
|
|
|
|
func (h *Handler) UnlikePost(ctx context.Context, req *pb.UnlikeForumPostRequest) (*pb.UnlikeForumPostResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
counters, viewerState, err := svc.UnlikePost(ctx, req.ActorUserId, req.PostId)
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.UnlikeForumPostResponse{
|
|
Counters: forumPostCountersToPB(counters),
|
|
ViewerState: forumPostViewerStateToPB(viewerState),
|
|
}, nil
|
|
}
|
|
|
|
func (h *Handler) ListComments(ctx context.Context, req *pb.ListForumCommentsRequest) (*pb.ListForumCommentsResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
items, page, err := svc.ListComments(ctx, req.ActorUserId, req.PostId, int(req.Page), int(req.PageSize), req.Sort)
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.ListForumCommentsResponse{
|
|
Items: forumCommentNodesToPB(items),
|
|
Page: forumPageToPB(page),
|
|
}, nil
|
|
}
|
|
|
|
func (h *Handler) CreateComment(ctx context.Context, req *pb.CreateForumCommentRequest) (*pb.CreateForumCommentResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
comment, err := svc.CreateComment(ctx, forumcontracts.CreateForumCommentRequest{
|
|
ActorUserID: req.ActorUserId,
|
|
PostID: req.PostId,
|
|
Content: req.Content,
|
|
ParentCommentID: forumUint64PtrFromPositive(req.ParentCommentId),
|
|
IdempotencyKey: req.IdempotencyKey,
|
|
})
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.CreateForumCommentResponse{Comment: forumCommentNodeToPB(comment)}, nil
|
|
}
|
|
|
|
func (h *Handler) DeleteComment(ctx context.Context, req *pb.DeleteForumCommentRequest) (*pb.DeleteForumCommentResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
result, err := svc.DeleteComment(ctx, req.ActorUserId, req.CommentId)
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.DeleteForumCommentResponse{
|
|
CommentId: result.CommentID,
|
|
Status: result.Status,
|
|
}, nil
|
|
}
|
|
|
|
func (h *Handler) ImportPost(ctx context.Context, req *pb.ImportForumPostRequest) (*pb.ImportForumPostResponse, error) {
|
|
svc, err := h.service()
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
if req == nil {
|
|
return nil, grpcErrorFromServiceError(respond.MissingParam)
|
|
}
|
|
|
|
result, err := svc.ImportPost(ctx, forumcontracts.ImportForumPostRequest{
|
|
ActorUserID: req.ActorUserId,
|
|
PostID: req.PostId,
|
|
TargetTitle: req.TargetTitle,
|
|
IdempotencyKey: req.IdempotencyKey,
|
|
})
|
|
if err != nil {
|
|
return nil, grpcErrorFromServiceError(err)
|
|
}
|
|
return &pb.ImportForumPostResponse{
|
|
ImportId: result.ImportID,
|
|
PostId: result.PostID,
|
|
NewTaskClassId: result.NewTaskClassID,
|
|
TaskClassTitle: result.TaskClassTitle,
|
|
ImportCount: result.ImportCount,
|
|
CreatedAt: result.CreatedAt,
|
|
}, nil
|
|
}
|
|
|
|
func forumPageToPB(page forumcontracts.PageResult) *pb.PageResponse {
|
|
return &pb.PageResponse{
|
|
Page: int32(page.Page),
|
|
PageSize: int32(page.PageSize),
|
|
Total: int32(page.Total),
|
|
HasMore: page.HasMore,
|
|
}
|
|
}
|
|
|
|
func forumUserToPB(user forumcontracts.UserBrief) *pb.UserBrief {
|
|
return &pb.UserBrief{
|
|
UserId: user.UserID,
|
|
Nickname: user.Nickname,
|
|
AvatarUrl: user.AvatarURL,
|
|
}
|
|
}
|
|
|
|
func forumTemplateSummaryToPB(summary forumcontracts.TemplateSummary) *pb.TemplateSummary {
|
|
return &pb.TemplateSummary{
|
|
TaskCount: int32(summary.TaskCount),
|
|
Mode: summary.Mode,
|
|
StartDate: summary.StartDate,
|
|
EndDate: summary.EndDate,
|
|
StrategyLabels: append([]string(nil), summary.StrategyLabels...),
|
|
}
|
|
}
|
|
|
|
func forumPostCountersToPB(counters forumcontracts.ForumPostCounters) *pb.ForumPostCounters {
|
|
return &pb.ForumPostCounters{
|
|
LikeCount: counters.LikeCount,
|
|
CommentCount: counters.CommentCount,
|
|
ImportCount: counters.ImportCount,
|
|
}
|
|
}
|
|
|
|
func forumPostViewerStateToPB(viewerState forumcontracts.ForumPostViewerState) *pb.ForumPostViewerState {
|
|
return &pb.ForumPostViewerState{
|
|
Liked: viewerState.Liked,
|
|
ImportedOnce: viewerState.ImportedOnce,
|
|
}
|
|
}
|
|
|
|
func forumPostBriefToPB(post *forumcontracts.ForumPostBrief) *pb.ForumPostBrief {
|
|
if post == nil {
|
|
return nil
|
|
}
|
|
return &pb.ForumPostBrief{
|
|
PostId: post.PostID,
|
|
Title: post.Title,
|
|
Summary: post.Summary,
|
|
Tags: append([]string(nil), post.Tags...),
|
|
Author: forumUserToPB(post.Author),
|
|
TemplateSummary: forumTemplateSummaryToPB(post.TemplateSummary),
|
|
Counters: forumPostCountersToPB(post.Counters),
|
|
ViewerState: forumPostViewerStateToPB(post.ViewerState),
|
|
Status: post.Status,
|
|
CreatedAt: post.CreatedAt,
|
|
}
|
|
}
|
|
|
|
func forumPostBriefsToPB(items []forumcontracts.ForumPostBrief) []*pb.ForumPostBrief {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]*pb.ForumPostBrief, 0, len(items))
|
|
for i := range items {
|
|
item := items[i]
|
|
result = append(result, forumPostBriefToPB(&item))
|
|
}
|
|
return result
|
|
}
|
|
|
|
func forumTemplateItemPreviewToPB(item forumcontracts.TemplateItemPreview) *pb.TemplateItemPreview {
|
|
return &pb.TemplateItemPreview{
|
|
ItemId: item.ItemID,
|
|
Order: int32(item.Order),
|
|
Content: item.Content,
|
|
}
|
|
}
|
|
|
|
func forumTemplateDetailToPB(detail forumcontracts.TemplateDetail) *pb.TemplateDetail {
|
|
preview := make([]*pb.TemplateItemPreview, 0, len(detail.ItemsPreview))
|
|
for i := range detail.ItemsPreview {
|
|
item := detail.ItemsPreview[i]
|
|
preview = append(preview, forumTemplateItemPreviewToPB(item))
|
|
}
|
|
return &pb.TemplateDetail{
|
|
Mode: detail.Mode,
|
|
StartDate: detail.StartDate,
|
|
EndDate: detail.EndDate,
|
|
StrategyLabels: append([]string(nil), detail.StrategyLabels...),
|
|
TaskCount: int32(detail.TaskCount),
|
|
ItemsPreview: preview,
|
|
}
|
|
}
|
|
|
|
func forumPostDetailToPB(detail *forumcontracts.ForumPostDetail) *pb.ForumPostDetail {
|
|
if detail == nil {
|
|
return nil
|
|
}
|
|
return &pb.ForumPostDetail{
|
|
Post: forumPostBriefToPB(&detail.Post),
|
|
Template: forumTemplateDetailToPB(detail.Template),
|
|
}
|
|
}
|
|
|
|
func forumTagItemsToPB(items []forumcontracts.ForumTagItem) []*pb.ForumTagItem {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]*pb.ForumTagItem, 0, len(items))
|
|
for i := range items {
|
|
item := items[i]
|
|
result = append(result, &pb.ForumTagItem{
|
|
Tag: item.Tag,
|
|
PostCount: int32(item.PostCount),
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
func forumCommentNodeToPB(node *forumcontracts.ForumCommentNode) *pb.ForumCommentNode {
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
children := make([]*pb.ForumCommentNode, 0, len(node.Children))
|
|
for i := range node.Children {
|
|
child := node.Children[i]
|
|
children = append(children, forumCommentNodeToPB(&child))
|
|
}
|
|
return &pb.ForumCommentNode{
|
|
CommentId: node.CommentID,
|
|
PostId: node.PostID,
|
|
ParentCommentId: forumUint64FromPtr(node.ParentCommentID),
|
|
Content: node.Content,
|
|
Status: node.Status,
|
|
Author: forumUserToPB(node.Author),
|
|
CanDelete: node.CanDelete,
|
|
CreatedAt: node.CreatedAt,
|
|
DeletedAt: forumStringFromPtr(node.DeletedAt),
|
|
Children: children,
|
|
}
|
|
}
|
|
|
|
func forumCommentNodesToPB(items []forumcontracts.ForumCommentNode) []*pb.ForumCommentNode {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]*pb.ForumCommentNode, 0, len(items))
|
|
for i := range items {
|
|
item := items[i]
|
|
result = append(result, forumCommentNodeToPB(&item))
|
|
}
|
|
return result
|
|
}
|
|
|
|
func forumUint64FromPtr(value *uint64) uint64 {
|
|
if value == nil {
|
|
return 0
|
|
}
|
|
return *value
|
|
}
|
|
|
|
func forumUint64PtrFromPositive(value uint64) *uint64 {
|
|
if value == 0 {
|
|
return nil
|
|
}
|
|
result := value
|
|
return &result
|
|
}
|
|
|
|
func forumStringFromPtr(value *string) string {
|
|
if value == nil {
|
|
return ""
|
|
}
|
|
return *value
|
|
}
|