Skip to content

Commit b967689

Browse files
authored
feat: 实现分页查询提案功能 (#50)
1 parent 5bd6f6c commit b967689

File tree

7 files changed

+250
-7
lines changed

7 files changed

+250
-7
lines changed

api/handler/proposal.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,45 @@
1414

1515
package handler
1616

17-
import "github.com/gin-gonic/gin"
17+
import (
18+
"github.com/Boyuan-IT-Club/Meowpick-Backend/api/token"
19+
"github.com/Boyuan-IT-Club/Meowpick-Backend/application/dto"
20+
"github.com/Boyuan-IT-Club/Meowpick-Backend/provider"
21+
"github.com/Boyuan-IT-Club/Meowpick-Backend/types/consts"
22+
"github.com/gin-gonic/gin"
23+
)
1824

1925
// CreateProposal 新建一个提案
2026
// @router /api/proposal/add [POST]
2127
func CreateProposal(c *gin.Context) {
2228
// TODO: not implemented
2329
}
2430

25-
// ListProposals 分页列出所有提案
26-
// @router /api/proposal/list [GET]
31+
// ListProposals godoc
32+
// @Summary 分页获取提案列表
33+
// @Description 分页查询提案列表数据
34+
// @Tags proposal
35+
// @Produce json
36+
// @Param page query int true "页码"
37+
// @Param pageSize query int true "每页数量"
38+
// @Success 200 {object} dto.ListProposalResp
39+
// @Router /api/proposal/query [get]
2740
func ListProposals(c *gin.Context) {
28-
// TODO: not implemented
41+
var (
42+
err error
43+
req dto.ListProposalReq
44+
resp *dto.ListProposalResp
45+
)
46+
47+
if err = c.ShouldBindQuery(&req); err != nil {
48+
PostProcess(c, &req, nil, err)
49+
return
50+
}
51+
c.Set(consts.CtxUserID, token.GetUserID(c))
52+
53+
resp, err = provider.Get().ProposalService.ListProposals(c, &req)
54+
PostProcess(c, &req, resp, err)
55+
2956
}
3057

3158
// GetProposal 获取提案详情

application/assembler/proposal.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2025 Boyuan-IT-Club
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package assembler
16+
17+
import (
18+
"context"
19+
20+
"github.com/Boyuan-IT-Club/Meowpick-Backend/application/dto"
21+
"github.com/Boyuan-IT-Club/Meowpick-Backend/infra/model"
22+
"github.com/Boyuan-IT-Club/go-kit/logs"
23+
"github.com/google/wire"
24+
)
25+
26+
var _ IProposalAssembler = (*ProposalAssembler)(nil)
27+
28+
type IProposalAssembler interface {
29+
ToProposalVO(ctx context.Context, db *model.Proposal) (*dto.ProposalVO, error)
30+
ToProposalVOArray(ctx context.Context, dbs []*model.Proposal) ([]*dto.ProposalVO, error)
31+
}
32+
33+
type ProposalAssembler struct {
34+
CourseAssembler *CourseAssembler
35+
}
36+
37+
var ProposalAssemblerSet = wire.NewSet(
38+
wire.Struct(new(ProposalAssembler), "*"),
39+
wire.Bind(new(IProposalAssembler), new(*ProposalAssembler)),
40+
)
41+
42+
// ToProposalVO 单个ProposalDB转ProposalVO (DB to VO)
43+
func (a *ProposalAssembler) ToProposalVO(ctx context.Context, db *model.Proposal) (*dto.ProposalVO, error) {
44+
var courseVO *dto.CourseVO
45+
if db.Course != nil {
46+
var err error
47+
courseVO, err = a.CourseAssembler.ToCourseVO(ctx, db.Course)
48+
if err != nil {
49+
logs.CtxErrorf(ctx, "[CourseAssembler] [ToCourseVO] error: %v", err)
50+
return nil, err
51+
}
52+
}
53+
54+
return &dto.ProposalVO{
55+
ID: db.ID,
56+
UserID: db.UserID,
57+
Title: db.Title,
58+
Content: db.Content,
59+
Course: courseVO,
60+
CreatedAt: db.CreatedAt,
61+
UpdatedAt: db.UpdatedAt,
62+
}, nil
63+
}
64+
65+
// ToProposalVOArray ProposalDB数组转ProposalVO数组 (DB Array to VO Array)
66+
func (a *ProposalAssembler) ToProposalVOArray(ctx context.Context, dbs []*model.Proposal) ([]*dto.ProposalVO, error) {
67+
if len(dbs) == 0 {
68+
logs.CtxWarnf(ctx, "[ProposalAssembler] [ToProposalVOArray] empty proposal db array")
69+
return []*dto.ProposalVO{}, nil
70+
}
71+
72+
vos := make([]*dto.ProposalVO, 0, len(dbs))
73+
for _, db := range dbs {
74+
vo, err := a.ToProposalVO(ctx, db)
75+
if err != nil {
76+
logs.CtxErrorf(ctx, "[ProposalAssembler] [ToProposalVO] error: %v", err)
77+
return nil, err
78+
}
79+
if vo != nil {
80+
vos = append(vos, vo)
81+
}
82+
}
83+
84+
return vos, nil
85+
}
86+

application/dto/proposal.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,26 @@
1414

1515
package dto
1616

17+
import "time"
18+
1719
type ProposalVO struct {
20+
ID string `json:"id"`
21+
UserID string `json:"userId"`
22+
Title string `json:"title"`
23+
Content string `json:"content"`
24+
Course *CourseVO `json:"course"`
25+
CreatedAt time.Time `json:"createdAt"`
26+
UpdatedAt time.Time `json:"updatedAt"`
1827
}
1928

20-
// type XxxProposalReq struct { }
29+
// ListProposalReq 对应 /api/proposal/list 的请求体(分页)
30+
type ListProposalReq struct {
31+
*PageParam
32+
}
2133

22-
// type XxxProposalResp struct { }
34+
// ListProposalResp 对应 /api/proposal/list 的响应体
35+
type ListProposalResp struct {
36+
*Resp
37+
Total int64 `json:"total"`
38+
Proposals []*ProposalVO `json:"proposals"`
39+
}

application/service/proposal.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,61 @@
1414

1515
package service
1616

17-
import "github.com/google/wire"
17+
import (
18+
"context"
19+
20+
"github.com/Boyuan-IT-Club/Meowpick-Backend/application/assembler"
21+
"github.com/Boyuan-IT-Club/Meowpick-Backend/application/dto"
22+
"github.com/Boyuan-IT-Club/Meowpick-Backend/infra/repo"
23+
"github.com/Boyuan-IT-Club/Meowpick-Backend/types/consts"
24+
"github.com/Boyuan-IT-Club/Meowpick-Backend/types/errno"
25+
"github.com/Boyuan-IT-Club/go-kit/errorx"
26+
"github.com/Boyuan-IT-Club/go-kit/logs"
27+
"github.com/google/wire"
28+
)
1829

1930
var _ IProposalService = (*ProposalService)(nil)
2031

2132
type IProposalService interface {
33+
ListProposals(ctx context.Context, req *dto.ListProposalReq) (*dto.ListProposalResp, error)
2234
}
2335

2436
type ProposalService struct {
37+
ProposalRepo repo.IProposalRepo
38+
ProposalAssembler *assembler.ProposalAssembler
2539
}
2640

2741
var ProposalServiceSet = wire.NewSet(
2842
wire.Struct(new(ProposalService), "*"),
2943
wire.Bind(new(IProposalService), new(*ProposalService)),
3044
)
45+
46+
// ListProposals 分页查询所有提案,用于投票列表或管理端审核
47+
func (s *ProposalService) ListProposals(ctx context.Context, req *dto.ListProposalReq) (*dto.ListProposalResp, error) {
48+
// 鉴权
49+
userId, ok := ctx.Value(consts.CtxUserID).(string)
50+
if !ok || userId == "" {
51+
return nil, errorx.New(errno.ErrUserNotLogin)
52+
}
53+
54+
// 查询提案列表
55+
proposals, total, err := s.ProposalRepo.FindMany(ctx, req.PageParam)
56+
if err != nil {
57+
logs.CtxErrorf(ctx, "[ProposalRepo] [FindMany] error: %v", err)
58+
return nil, errorx.WrapByCode(err, errno.ErrProposalFindFailed)
59+
}
60+
61+
// 转换为VO
62+
vos, err := s.ProposalAssembler.ToProposalVOArray(ctx, proposals)
63+
if err != nil {
64+
logs.CtxErrorf(ctx, "[ProposalAssembler] [ToProposalVOArray] error: %v", err)
65+
return nil, errorx.WrapByCode(err, errno.ErrProposalCvtFailed,
66+
errorx.KV("src", "database proposals"), errorx.KV("dst", "proposal vos"))
67+
}
68+
69+
return &dto.ListProposalResp{
70+
Resp: dto.Success(),
71+
Total: total,
72+
Proposals: vos,
73+
}, nil
74+
}

infra/repo/proposal.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@
1515
package repo
1616

1717
import (
18+
"context"
19+
20+
"github.com/Boyuan-IT-Club/Meowpick-Backend/application/dto"
1821
"github.com/Boyuan-IT-Club/Meowpick-Backend/infra/config"
22+
"github.com/Boyuan-IT-Club/Meowpick-Backend/infra/model"
23+
"github.com/Boyuan-IT-Club/Meowpick-Backend/infra/util/page"
24+
"github.com/Boyuan-IT-Club/Meowpick-Backend/types/consts"
1925
"github.com/zeromicro/go-zero/core/stores/monc"
26+
"go.mongodb.org/mongo-driver/bson"
2027
)
2128

2229
var _ IProposalRepo = (*ProposalRepo)(nil)
@@ -26,6 +33,7 @@ const (
2633
)
2734

2835
type IProposalRepo interface {
36+
FindMany(ctx context.Context, param *dto.PageParam) ([]*model.Proposal, int64, error)
2937
}
3038

3139
type ProposalRepo struct {
@@ -36,3 +44,25 @@ func NewProposalRepo(cfg *config.Config) *ProposalRepo {
3644
conn := monc.MustNewModel(cfg.Mongo.URL, cfg.Mongo.DB, ProposalCollectionName, cfg.Cache)
3745
return &ProposalRepo{conn: conn}
3846
}
47+
48+
// FindMany 分页查询所有未删除的提案
49+
func (r *ProposalRepo) FindMany(ctx context.Context, param *dto.PageParam) ([]*model.Proposal, int64, error) {
50+
proposals := []*model.Proposal{}
51+
filter := bson.M{consts.Deleted: bson.M{"$ne": true}}
52+
53+
total, err := r.conn.CountDocuments(ctx, filter)
54+
if err != nil {
55+
return nil, 0, err
56+
}
57+
58+
if err = r.conn.Find(
59+
ctx,
60+
&proposals,
61+
filter,
62+
page.FindPageOption(param).SetSort(page.DSort(consts.CreatedAt, -1)),
63+
); err != nil {
64+
return nil, 0, err
65+
}
66+
67+
return proposals, total, nil
68+
}

provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ var ApplicationSet = wire.NewSet(
6565
assembler.CommentAssemblerSet,
6666
assembler.CourseAssemblerSet,
6767
assembler.TeacherAssemblerSet,
68+
assembler.ProposalAssemblerSet,
6869
)
6970

7071
var InfraSet = wire.NewSet(

types/errno/proposal.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2025 Boyuan-IT-Club
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package errno
16+
17+
import "github.com/Boyuan-IT-Club/go-kit/errorx/code"
18+
19+
// proposal: 106 000 000 ~ 106 999 999
20+
21+
const (
22+
ErrProposalFindFailed = 106000001
23+
ErrProposalCvtFailed = 106000002
24+
)
25+
26+
func init() {
27+
code.Register(
28+
ErrProposalFindFailed,
29+
"failed to find proposals",
30+
code.WithAffectStability(false),
31+
)
32+
code.Register(
33+
ErrProposalCvtFailed,
34+
"failed to convert proposal from {src} to {dst}",
35+
code.WithAffectStability(false),
36+
)
37+
}
38+

0 commit comments

Comments
 (0)