Skip to content

Commit e7e68c1

Browse files
feat: 实现删除提案功能 (#58)
Co-authored-by: Eagle233 <eagle233real@outlook.com>
1 parent 127ea76 commit e7e68c1

File tree

11 files changed

+282
-25
lines changed

11 files changed

+282
-25
lines changed

api/handler/proposal.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,29 @@ func UpdateProposal(c *gin.Context) {
124124
PostProcess(c, &req, resp, err)
125125
}
126126

127-
// DeleteProposal 删除提案
128-
// @router /api/proposal/{proposalId}/delete [POST]
127+
// DeleteProposal godoc
128+
// @Summary 删除提案
129+
// @Description 根据提案ID软删除提案(标记为已删除状态)
130+
// @Tags proposal
131+
// @Accept json
132+
// @Param proposalId path string true "提案ID"
133+
// @success 200 {object} dto.DeleteProposalResp
134+
// @Router /api/proposal/{proposalId}/delete [POST]
129135
func DeleteProposal(c *gin.Context) {
130-
// TODO: not implemented
136+
var err error
137+
var req dto.DeleteProposalReq
138+
var resp *dto.DeleteProposalResp
139+
140+
req.ProposalID = c.Param(consts.CtxProposalID)
141+
142+
if err = c.ShouldBindJSON(&req); err != nil {
143+
PostProcess(c, &req, nil, err)
144+
return
145+
}
146+
c.Set(consts.CtxUserID, token.GetUserID(c))
147+
148+
resp, err = provider.Get().ProposalService.DeleteProposal(c, &req)
149+
PostProcess(c, &req, resp, err)
131150
}
132151

133152
// GetProposalSuggestions 获取提案搜索建议

application/dto/proposal.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,24 @@ type ListProposalReq struct {
4848
*PageParam
4949
}
5050

51-
type ToggleProposalReq struct {
52-
ProposalID string `json:"proposalID"`
51+
// ListProposalResp 对应 /api/proposal/list 的响应体
52+
type ListProposalResp struct {
53+
*Resp
54+
Total int64 `json:"total"`
55+
Proposals []*ProposalVO `json:"proposals"`
5356
}
5457

5558
type GetProposalReq struct {
5659
ProposalID string `json:"proposalId"`
5760
}
5861

59-
// ListProposalResp 对应 /api/proposal/list 的响应体
60-
type ListProposalResp struct {
62+
type GetProposalResp struct {
6163
*Resp
62-
Total int64 `json:"total"`
63-
Proposals []*ProposalVO `json:"proposals"`
64+
Proposal *ProposalVO `json:"proposal"`
65+
}
66+
67+
type ToggleProposalReq struct {
68+
ProposalID string `json:"proposalID"`
6469
}
6570

6671
type ToggleProposalResp struct {
@@ -69,11 +74,17 @@ type ToggleProposalResp struct {
6974
*Resp
7075
}
7176

72-
type GetProposalResp struct {
73-
*Resp
74-
Proposal *ProposalVO `json:"proposal"`
77+
type DeleteProposalReq struct {
78+
ProposalID string `json:"proposalId"`
7579
}
7680

81+
type DeleteProposalResp struct {
82+
*Resp
83+
ProposalID string `json:"proposalId"`
84+
DeletedAt time.Time `json:"deletedAt"`
85+
OperatorID string `json:"operatorId"`
86+
Deleted bool `json:"deleted"`
87+
7788
// UpdateProposalReq 更新提案请求参数
7889
type UpdateProposalReq struct {
7990
ProposalID string `json:"-"`

application/service/proposal.go

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ var _ IProposalService = (*ProposalService)(nil)
3838
type IProposalService interface {
3939
CreateProposal(ctx context.Context, req *dto.CreateProposalReq) (*dto.CreateProposalResp, error)
4040
ListProposals(ctx context.Context, req *dto.ListProposalReq) (*dto.ListProposalResp, error)
41-
GetProposal(ctx context.Context, req *dto.GetProposalReq) (resp *dto.GetProposalResp, err error)
41+
GetProposal(ctx context.Context, req *dto.GetProposalReq) (*dto.GetProposalResp, error)
42+
DeleteProposal(ctx context.Context, req *dto.DeleteProposalReq) (*dto.DeleteProposalResp, error)
4243
UpdateProposal(ctx context.Context, req *dto.UpdateProposalReq) (*dto.UpdateProposalResp, error)
4344
}
4445

@@ -49,6 +50,7 @@ type ProposalService struct {
4950
ProposalAssembler *assembler.ProposalAssembler
5051
LikeRepo *repo.LikeRepo
5152
LikeCache *cache.LikeCache
53+
UserRepo *repo.UserRepo
5254
}
5355

5456
var ProposalServiceSet = wire.NewSet(
@@ -185,7 +187,7 @@ func (s *ProposalService) ListProposals(ctx context.Context, req *dto.ListPropos
185187
}
186188

187189
// GetProposal 获取提案详情
188-
func (s *ProposalService) GetProposal(ctx context.Context, req *dto.GetProposalReq) (resp *dto.GetProposalResp, err error) {
190+
func (s *ProposalService) GetProposal(ctx context.Context, req *dto.GetProposalReq) (*dto.GetProposalResp, error) {
189191
// 鉴权
190192
userId, ok := ctx.Value(consts.CtxUserID).(string)
191193
if !ok || userId == "" {
@@ -218,13 +220,68 @@ func (s *ProposalService) GetProposal(ctx context.Context, req *dto.GetProposalR
218220
}, nil
219221
}
220222

223+
// DeleteProposal 删除提案
224+
func (s *ProposalService) DeleteProposal(ctx context.Context, req *dto.DeleteProposalReq) (*dto.DeleteProposalResp, error) {
225+
// 鉴权
226+
userId, ok := ctx.Value(consts.CtxUserID).(string)
227+
if !ok || userId == "" {
228+
return nil, errorx.New(errno.ErrUserNotLogin)
229+
}
230+
231+
proposalId := req.ProposalID
232+
233+
// 检查提案是否存在
234+
proposal, err := s.ProposalRepo.FindByID(ctx, proposalId)
235+
if err != nil {
236+
logs.CtxErrorf(ctx, "[ProposalRepo] [FindByID] error: %v, proposalId: %s", err, proposalId)
237+
return nil, errorx.WrapByCode(err, errno.ErrProposalFindFailed)
238+
}
239+
if proposal == nil {
240+
logs.CtxWarnf(ctx, "[ProposalRepo] [FindByID] proposal not found, proposalId: %s", proposalId)
241+
return nil, errorx.New(errno.ErrProposalNotFound, errorx.KV("key", consts.ReqProposalID), errorx.KV("value", proposalId))
242+
}
243+
244+
//权限检查:非管理员只能删除自己的提案
245+
if proposal.UserID != userId {
246+
// 查询用户是否是管理员
247+
isAdmin, err := s.UserRepo.IsAdminByID(ctx, userId)
248+
if err != nil {
249+
logs.CtxErrorf(ctx, "[UserRepo] [GetByID] error: %v, userId: %s", err, userId)
250+
return nil, errorx.New(errno.ErrUserNotAdmin,
251+
errorx.KV("id", userId))
252+
}
253+
254+
if !isAdmin {
255+
return nil, errorx.New(errno.ErrUserNotOwner,
256+
errorx.KV("id", userId))
257+
}
258+
}
259+
260+
//执行删除提案
261+
err = s.ProposalRepo.DeleteProposal(ctx, proposalId, userId)
262+
if err != nil {
263+
logs.CtxErrorf(ctx, "[ProposalRepo] [Delete] error: %v", err)
264+
return nil, errorx.WrapByCode(err, errno.ErrProposalDeleteFailed,
265+
errorx.KV("proposal_id", proposalId))
266+
}
267+
268+
return &dto.DeleteProposalResp{
269+
Resp: dto.Success(),
270+
ProposalID: req.ProposalID,
271+
DeletedAt: time.Now(),
272+
OperatorID: userId,
273+
Deleted: true,
274+
}, nil
275+
}
276+
221277
// UpdateProposal 更新提案
222278
func (s *ProposalService) UpdateProposal(ctx context.Context, req *dto.UpdateProposalReq) (*dto.UpdateProposalResp, error) {
223-
//鉴权
279+
// 鉴权
224280
userId, ok := ctx.Value(consts.CtxUserID).(string)
225281
if !ok || userId == "" {
226282
return nil, errorx.New(errno.ErrUserNotLogin)
227283
}
284+
228285
//查询提案
229286
proposal, err := s.ProposalRepo.FindByID(ctx, req.ProposalID)
230287
if err != nil {

docs/docs.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,31 @@ const docTemplate = `{
438438
},
439439
"/api/proposal/{proposalId}/delete": {
440440
"post": {
441-
"responses": {}
441+
"description": "根据提案ID软删除提案(标记为已删除状态)",
442+
"consumes": [
443+
"application/json"
444+
],
445+
"tags": [
446+
"proposal"
447+
],
448+
"summary": "删除提案",
449+
"parameters": [
450+
{
451+
"type": "string",
452+
"description": "提案ID",
453+
"name": "proposalId",
454+
"in": "path",
455+
"required": true
456+
}
457+
],
458+
"responses": {
459+
"200": {
460+
"description": "OK",
461+
"schema": {
462+
"$ref": "#/definitions/dto.DeleteProposalResp"
463+
}
464+
}
465+
}
442466
}
443467
},
444468
"/api/proposal/{proposalId}/update": {
@@ -865,6 +889,29 @@ const docTemplate = `{
865889
}
866890
}
867891
},
892+
"dto.DeleteProposalResp": {
893+
"type": "object",
894+
"properties": {
895+
"code": {
896+
"type": "integer"
897+
},
898+
"deleted": {
899+
"type": "boolean"
900+
},
901+
"deletedAt": {
902+
"type": "string"
903+
},
904+
"msg": {
905+
"type": "string"
906+
},
907+
"operatorId": {
908+
"type": "string"
909+
},
910+
"proposalId": {
911+
"type": "string"
912+
}
913+
}
914+
},
868915
"dto.GetCourseCampusesResp": {
869916
"type": "object",
870917
"properties": {

docs/swagger.json

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,31 @@
417417
},
418418
"/api/proposal/{proposalId}/delete": {
419419
"post": {
420-
"responses": {}
420+
"description": "根据提案ID软删除提案(标记为已删除状态)",
421+
"consumes": [
422+
"application/json"
423+
],
424+
"tags": [
425+
"proposal"
426+
],
427+
"summary": "删除提案",
428+
"parameters": [
429+
{
430+
"type": "string",
431+
"description": "提案ID",
432+
"name": "proposalId",
433+
"in": "path",
434+
"required": true
435+
}
436+
],
437+
"responses": {
438+
"200": {
439+
"description": "OK",
440+
"schema": {
441+
"$ref": "#/definitions/dto.DeleteProposalResp"
442+
}
443+
}
444+
}
421445
}
422446
},
423447
"/api/proposal/{proposalId}/update": {
@@ -844,6 +868,29 @@
844868
}
845869
}
846870
},
871+
"dto.DeleteProposalResp": {
872+
"type": "object",
873+
"properties": {
874+
"code": {
875+
"type": "integer"
876+
},
877+
"deleted": {
878+
"type": "boolean"
879+
},
880+
"deletedAt": {
881+
"type": "string"
882+
},
883+
"msg": {
884+
"type": "string"
885+
},
886+
"operatorId": {
887+
"type": "string"
888+
},
889+
"proposalId": {
890+
"type": "string"
891+
}
892+
}
893+
},
847894
"dto.GetCourseCampusesResp": {
848895
"type": "object",
849896
"properties": {

docs/swagger.yaml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,21 @@ definitions:
177177
title:
178178
type: string
179179
type: object
180+
dto.DeleteProposalResp:
181+
properties:
182+
code:
183+
type: integer
184+
deleted:
185+
type: boolean
186+
deletedAt:
187+
type: string
188+
msg:
189+
type: string
190+
operatorId:
191+
type: string
192+
proposalId:
193+
type: string
194+
type: object
180195
dto.GetCourseCampusesResp:
181196
properties:
182197
campuses:
@@ -686,7 +701,23 @@ paths:
686701
responses: {}
687702
/api/proposal/{proposalId}/delete:
688703
post:
689-
responses: {}
704+
consumes:
705+
- application/json
706+
description: 根据提案ID软删除提案(标记为已删除状态)
707+
parameters:
708+
- description: 提案ID
709+
in: path
710+
name: proposalId
711+
required: true
712+
type: string
713+
responses:
714+
"200":
715+
description: OK
716+
schema:
717+
$ref: '#/definitions/dto.DeleteProposalResp'
718+
summary: 删除提案
719+
tags:
720+
- proposal
690721
/api/proposal/{proposalId}/update:
691722
post:
692723
consumes:

infra/model/proposal.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ import (
2020

2121
type Proposal struct {
2222
ID string `bson:"_id,omitempty" json:"id"`
23-
UserID string `bson:"userId" json:"userId"` // 提出Proposal的用户ID
24-
Title string `bson:"title" json:"title"` // 标题
25-
Content string `bson:"content" json:"content"` // 描述的内容
26-
Deleted bool `bson:"deleted" json:"deleted"` // 删除标记
27-
Status int32 `bson:"status" json:"status"` // 提案的状态,0: 待审核,1: 通过,2: 拒绝
28-
Course *Course `bson:"course" json:"course"` // 课程信息,包含教师的ID(未创建不需要ID)
23+
UserID string `bson:"userId" json:"userId"` // 提出Proposal的用户ID
24+
Title string `bson:"title" json:"title"` // 标题
25+
Content string `bson:"content" json:"content"` // 描述的内容
26+
Deleted bool `bson:"deleted" json:"deleted"` // 删除标记
27+
DeletedAt time.Time `bson:"deletedAt,omitempty" json:"deletedAt,omitempty"` //删除时间
28+
Status int32 `bson:"status" json:"status"` // 提案的状态,0: 待审核,1: 通过,2: 拒绝
29+
Course *Course `bson:"course" json:"course"` // 课程信息,包含教师的ID(未创建不需要ID)
2930
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
3031
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
3132
}

0 commit comments

Comments
 (0)