diff --git a/Dockerfile b/deploy/Dockerfile similarity index 99% rename from Dockerfile rename to deploy/Dockerfile index 0b97298..3f1c6db 100644 --- a/Dockerfile +++ b/deploy/Dockerfile @@ -4,7 +4,7 @@ FROM golang:1.22.3 AS build-stage ENV GO111MODULE=on ENV GOPROXY=https://goproxy.cn,direct WORKDIR /app -COPY . . +COPY .. . RUN go mod tidy # 静态编译可执行文件 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . diff --git a/docker-compose.yaml b/deploy/docker-compose.yaml similarity index 100% rename from docker-compose.yaml rename to deploy/docker-compose.yaml diff --git a/elasticsearch.yml b/deploy/elasticsearch.yml similarity index 100% rename from elasticsearch.yml rename to deploy/elasticsearch.yml diff --git a/yaml/linkme-etcd-deployment.yaml b/deploy/yaml/linkme-etcd-deployment.yaml similarity index 100% rename from yaml/linkme-etcd-deployment.yaml rename to deploy/yaml/linkme-etcd-deployment.yaml diff --git a/yaml/linkme-etcd-pv.yaml b/deploy/yaml/linkme-etcd-pv.yaml similarity index 100% rename from yaml/linkme-etcd-pv.yaml rename to deploy/yaml/linkme-etcd-pv.yaml diff --git a/yaml/linkme-etcd-pvc.yaml b/deploy/yaml/linkme-etcd-pvc.yaml similarity index 100% rename from yaml/linkme-etcd-pvc.yaml rename to deploy/yaml/linkme-etcd-pvc.yaml diff --git a/yaml/linkme-etcd-service.yaml b/deploy/yaml/linkme-etcd-service.yaml similarity index 100% rename from yaml/linkme-etcd-service.yaml rename to deploy/yaml/linkme-etcd-service.yaml diff --git a/yaml/linkme-grafana-deployment.yaml b/deploy/yaml/linkme-grafana-deployment.yaml similarity index 100% rename from yaml/linkme-grafana-deployment.yaml rename to deploy/yaml/linkme-grafana-deployment.yaml diff --git a/yaml/linkme-grafana-service.yaml b/deploy/yaml/linkme-grafana-service.yaml similarity index 100% rename from yaml/linkme-grafana-service.yaml rename to deploy/yaml/linkme-grafana-service.yaml diff --git a/yaml/linkme-kafka-deployment.yaml b/deploy/yaml/linkme-kafka-deployment.yaml similarity index 100% rename from yaml/linkme-kafka-deployment.yaml rename to deploy/yaml/linkme-kafka-deployment.yaml diff --git a/yaml/linkme-kafka-pv.yaml b/deploy/yaml/linkme-kafka-pv.yaml similarity index 100% rename from yaml/linkme-kafka-pv.yaml rename to deploy/yaml/linkme-kafka-pv.yaml diff --git a/yaml/linkme-kafka-pvc.yaml b/deploy/yaml/linkme-kafka-pvc.yaml similarity index 100% rename from yaml/linkme-kafka-pvc.yaml rename to deploy/yaml/linkme-kafka-pvc.yaml diff --git a/yaml/linkme-kafka-service.yaml b/deploy/yaml/linkme-kafka-service.yaml similarity index 100% rename from yaml/linkme-kafka-service.yaml rename to deploy/yaml/linkme-kafka-service.yaml diff --git a/yaml/linkme-mongodb-deployment.yaml b/deploy/yaml/linkme-mongodb-deployment.yaml similarity index 100% rename from yaml/linkme-mongodb-deployment.yaml rename to deploy/yaml/linkme-mongodb-deployment.yaml diff --git a/yaml/linkme-mongodb-pv.yaml b/deploy/yaml/linkme-mongodb-pv.yaml similarity index 100% rename from yaml/linkme-mongodb-pv.yaml rename to deploy/yaml/linkme-mongodb-pv.yaml diff --git a/yaml/linkme-mongodb-pvc.yaml b/deploy/yaml/linkme-mongodb-pvc.yaml similarity index 100% rename from yaml/linkme-mongodb-pvc.yaml rename to deploy/yaml/linkme-mongodb-pvc.yaml diff --git a/yaml/linkme-mongodb-service.yaml b/deploy/yaml/linkme-mongodb-service.yaml similarity index 100% rename from yaml/linkme-mongodb-service.yaml rename to deploy/yaml/linkme-mongodb-service.yaml diff --git a/yaml/linkme-mysql-deployment.yaml b/deploy/yaml/linkme-mysql-deployment.yaml similarity index 100% rename from yaml/linkme-mysql-deployment.yaml rename to deploy/yaml/linkme-mysql-deployment.yaml diff --git a/yaml/linkme-mysql-pv.yaml b/deploy/yaml/linkme-mysql-pv.yaml similarity index 100% rename from yaml/linkme-mysql-pv.yaml rename to deploy/yaml/linkme-mysql-pv.yaml diff --git a/yaml/linkme-mysql-pvc.yaml b/deploy/yaml/linkme-mysql-pvc.yaml similarity index 100% rename from yaml/linkme-mysql-pvc.yaml rename to deploy/yaml/linkme-mysql-pvc.yaml diff --git a/yaml/linkme-mysql-service.yaml b/deploy/yaml/linkme-mysql-service.yaml similarity index 100% rename from yaml/linkme-mysql-service.yaml rename to deploy/yaml/linkme-mysql-service.yaml diff --git a/yaml/linkme-prometheus-deployment.yaml b/deploy/yaml/linkme-prometheus-deployment.yaml similarity index 100% rename from yaml/linkme-prometheus-deployment.yaml rename to deploy/yaml/linkme-prometheus-deployment.yaml diff --git a/yaml/linkme-prometheus-pv.yaml b/deploy/yaml/linkme-prometheus-pv.yaml similarity index 100% rename from yaml/linkme-prometheus-pv.yaml rename to deploy/yaml/linkme-prometheus-pv.yaml diff --git a/yaml/linkme-prometheus-pvc.yaml b/deploy/yaml/linkme-prometheus-pvc.yaml similarity index 100% rename from yaml/linkme-prometheus-pvc.yaml rename to deploy/yaml/linkme-prometheus-pvc.yaml diff --git a/yaml/linkme-prometheus-service.yaml b/deploy/yaml/linkme-prometheus-service.yaml similarity index 100% rename from yaml/linkme-prometheus-service.yaml rename to deploy/yaml/linkme-prometheus-service.yaml diff --git a/yaml/linkme-redis-deployment.yaml b/deploy/yaml/linkme-redis-deployment.yaml similarity index 100% rename from yaml/linkme-redis-deployment.yaml rename to deploy/yaml/linkme-redis-deployment.yaml diff --git a/yaml/linkme-redis-service.yaml b/deploy/yaml/linkme-redis-service.yaml similarity index 100% rename from yaml/linkme-redis-service.yaml rename to deploy/yaml/linkme-redis-service.yaml diff --git a/yaml/prometheus.yml b/deploy/yaml/prometheus.yml similarity index 100% rename from yaml/prometheus.yml rename to deploy/yaml/prometheus.yml diff --git a/doc/function_module.md b/doc/function_module.md index e6699d2..dd64a2e 100644 --- a/doc/function_module.md +++ b/doc/function_module.md @@ -11,12 +11,12 @@ ## 论坛模块 - **发布帖子**:用户可以在论坛上发布新帖子,包括文本、图片、视频等✅ -- **评论管理**:用户可以对帖子进行评论,并管理自己的评论 +- **评论管理**:用户可以对帖子进行评论,并管理自己的评论✅ - **点赞功能**:用户可以对帖子或评论进行点赞,点赞后的帖子可在点赞列表中找到✅ - **收藏功能**:用户可以对帖子进行收藏,收藏后的帖子可以在收藏夹中找到✅ - **历史记录**:用户浏览过的帖子可在历史记录中查看✅ - **版块管理**:管理员可以创建、编辑和删除论坛的版块✅ -- **内容审核**:引入内容审核机制,接入ai辅助管理员进行审核✅ +- **内容审核**:引入内容审核机制,接入ai辅助管理员进行审核 ## 内容管理模块 diff --git a/docs/docs.go b/docs/docs.go deleted file mode 100644 index 6676994..0000000 --- a/docs/docs.go +++ /dev/null @@ -1,36 +0,0 @@ -// Package docs Code generated by swaggo/swag. DO NOT EDIT -package docs - -import "github.com/swaggo/swag" - -const docTemplate = `{ - "schemes": {{ marshal .Schemes }}, - "swagger": "2.0", - "info": { - "description": "{{escape .Description}}", - "title": "{{.Title}}", - "contact": {}, - "version": "{{.Version}}" - }, - "host": "{{.Host}}", - "basePath": "{{.BasePath}}", - "paths": {} -}` - -// SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "", - Host: "", - BasePath: "", - Schemes: []string{}, - Title: "", - Description: "", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - -func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} diff --git a/docs/swagger.json b/docs/swagger.json deleted file mode 100644 index ec416cd..0000000 --- a/docs/swagger.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "contact": {} - }, - "paths": {} -} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml deleted file mode 100644 index b64379c..0000000 --- a/docs/swagger.yaml +++ /dev/null @@ -1,4 +0,0 @@ -info: - contact: {} -paths: {} -swagger: "2.0" diff --git a/internal/api/activity.go b/internal/api/activity.go index 16c128c..5b065c3 100644 --- a/internal/api/activity.go +++ b/internal/api/activity.go @@ -1,36 +1,35 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/service" "github.com/GoSimplicity/LinkMe/middleware" . "github.com/GoSimplicity/LinkMe/pkg/ginp" "github.com/casbin/casbin/v2" "github.com/gin-gonic/gin" - "go.uber.org/zap" ) type ActivityHandler struct { ce *casbin.Enforcer svc service.ActivityService - l *zap.Logger } -func NewActivityHandler(svc service.ActivityService, ce *casbin.Enforcer, l *zap.Logger) *ActivityHandler { +func NewActivityHandler(svc service.ActivityService, ce *casbin.Enforcer) *ActivityHandler { return &ActivityHandler{ svc: svc, ce: ce, - l: l, } } func (ah *ActivityHandler) RegisterRoutes(server *gin.Engine) { - casbinMiddleware := middleware.NewCasbinMiddleware(ah.ce, ah.l) + casbinMiddleware := middleware.NewCasbinMiddleware(ah.ce) historyGroup := server.Group("/api/activity") historyGroup.GET("/recent", casbinMiddleware.CheckCasbin(), WrapQuery(ah.GetRecentActivity)) // 获取最近的活动记录 } -func (ah *ActivityHandler) GetRecentActivity(ctx *gin.Context, _ GetRecentActivityReq) (Result, error) { +// GetRecentActivity 获取最近的活动记录 +func (ah *ActivityHandler) GetRecentActivity(ctx *gin.Context, _ required_parameter.GetRecentActivityReq) (Result, error) { activity, err := ah.svc.GetRecentActivity(ctx) if err != nil { return Result{ diff --git a/internal/api/check.go b/internal/api/check.go index 4409b27..8c18c25 100644 --- a/internal/api/check.go +++ b/internal/api/check.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/domain" "github.com/GoSimplicity/LinkMe/internal/service" @@ -9,27 +10,24 @@ import ( ijwt "github.com/GoSimplicity/LinkMe/utils/jwt" "github.com/casbin/casbin/v2" "github.com/gin-gonic/gin" - "go.uber.org/zap" ) type CheckHandler struct { svc service.CheckService - l *zap.Logger ce *casbin.Enforcer biz string } -func NewCheckHandler(svc service.CheckService, l *zap.Logger, ce *casbin.Enforcer) *CheckHandler { +func NewCheckHandler(svc service.CheckService, ce *casbin.Enforcer) *CheckHandler { return &CheckHandler{ svc: svc, - l: l, ce: ce, biz: "check", } } func (ch *CheckHandler) RegisterRoutes(server *gin.Engine) { - casbinMiddleware := middleware.NewCasbinMiddleware(ch.ce, ch.l) + casbinMiddleware := middleware.NewCasbinMiddleware(ch.ce) checkGroup := server.Group("/api/checks") checkGroup.Use(casbinMiddleware.CheckCasbin()) checkGroup.POST("/approve", WrapBody(ch.ApproveCheck)) // 审核通过 @@ -39,7 +37,7 @@ func (ch *CheckHandler) RegisterRoutes(server *gin.Engine) { checkGroup.GET("/stats", WrapQuery(ch.GetCheckCount)) // 管理员使用 } -func (ch *CheckHandler) ApproveCheck(ctx *gin.Context, req ApproveCheckReq) (Result, error) { +func (ch *CheckHandler) ApproveCheck(ctx *gin.Context, req required_parameter.ApproveCheckReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) err := ch.svc.ApproveCheck(ctx, req.CheckID, req.Remark, uc.Uid) if err != nil { @@ -54,7 +52,7 @@ func (ch *CheckHandler) ApproveCheck(ctx *gin.Context, req ApproveCheckReq) (Res }, nil } -func (ch *CheckHandler) RejectCheck(ctx *gin.Context, req RejectCheckReq) (Result, error) { +func (ch *CheckHandler) RejectCheck(ctx *gin.Context, req required_parameter.RejectCheckReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) err := ch.svc.RejectCheck(ctx, req.CheckID, req.Remark, uc.Uid) if err != nil { @@ -69,7 +67,7 @@ func (ch *CheckHandler) RejectCheck(ctx *gin.Context, req RejectCheckReq) (Resul }, nil } -func (ch *CheckHandler) ListChecks(ctx *gin.Context, req ListCheckReq) (Result, error) { +func (ch *CheckHandler) ListChecks(ctx *gin.Context, req required_parameter.ListCheckReq) (Result, error) { checks, err := ch.svc.ListChecks(ctx, domain.Pagination{ Page: req.Page, Size: req.Size, @@ -87,7 +85,7 @@ func (ch *CheckHandler) ListChecks(ctx *gin.Context, req ListCheckReq) (Result, }, nil } -func (ch *CheckHandler) CheckDetail(ctx *gin.Context, req CheckDetailReq) (Result, error) { +func (ch *CheckHandler) CheckDetail(ctx *gin.Context, req required_parameter.CheckDetailReq) (Result, error) { check, err := ch.svc.CheckDetail(ctx, req.CheckID) if err != nil { return Result{ @@ -102,7 +100,7 @@ func (ch *CheckHandler) CheckDetail(ctx *gin.Context, req CheckDetailReq) (Resul }, nil } -func (ch *CheckHandler) GetCheckCount(ctx *gin.Context, _ GetCheckCount) (Result, error) { +func (ch *CheckHandler) GetCheckCount(ctx *gin.Context, _ required_parameter.GetCheckCount) (Result, error) { count, err := ch.svc.GetCheckCount(ctx) if err != nil { return Result{ diff --git a/internal/api/comment.go b/internal/api/comment.go index 507c8f6..3d885b6 100644 --- a/internal/api/comment.go +++ b/internal/api/comment.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/domain" "github.com/GoSimplicity/LinkMe/internal/service" @@ -31,7 +32,7 @@ func (ch *CommentHandler) RegisterRoutes(server *gin.Engine) { } // CreateComment 创建评论处理器方法 -func (ch *CommentHandler) CreateComment(ctx *gin.Context, req CreateCommentReq) (Result, error) { +func (ch *CommentHandler) CreateComment(ctx *gin.Context, req required_parameter.CreateCommentReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) comment := domain.Comment{ Content: req.Content, @@ -60,7 +61,7 @@ func (ch *CommentHandler) CreateComment(ctx *gin.Context, req CreateCommentReq) } // ListComments 列出评论处理器方法 -func (ch *CommentHandler) ListComments(ctx *gin.Context, req ListCommentsReq) (Result, error) { +func (ch *CommentHandler) ListComments(ctx *gin.Context, req required_parameter.ListCommentsReq) (Result, error) { comments, err := ch.svc.ListComments(ctx, req.PostId, req.MinId, req.Limit) if err != nil { return Result{ @@ -76,7 +77,7 @@ func (ch *CommentHandler) ListComments(ctx *gin.Context, req ListCommentsReq) (R } // DeleteComment 删除评论处理器方法 -func (ch *CommentHandler) DeleteComment(ctx *gin.Context, req DeleteCommentReq) (Result, error) { +func (ch *CommentHandler) DeleteComment(ctx *gin.Context, req required_parameter.DeleteCommentReq) (Result, error) { err := ch.svc.DeleteComment(ctx, req.CommentId) if err != nil { return Result{ @@ -91,7 +92,7 @@ func (ch *CommentHandler) DeleteComment(ctx *gin.Context, req DeleteCommentReq) } // GetMoreCommentReply 获取更多评论回复处理器方法 -func (ch *CommentHandler) GetMoreCommentReply(ctx *gin.Context, req GetMoreCommentReplyReq) (Result, error) { +func (ch *CommentHandler) GetMoreCommentReply(ctx *gin.Context, req required_parameter.GetMoreCommentReplyReq) (Result, error) { comments, err := ch.svc.GetMoreCommentsReply(ctx, req.RootId, req.MaxId, req.Limit) if err != nil { return Result{ diff --git a/internal/api/history.go b/internal/api/history.go index 15173f2..2a3f8f3 100644 --- a/internal/api/history.go +++ b/internal/api/history.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/domain" "github.com/GoSimplicity/LinkMe/internal/service" @@ -27,7 +28,7 @@ func (h *HistoryHandler) RegisterRoutes(server *gin.Engine) { historyGroup.DELETE("/delete/all", WrapBody(h.DeleteAllHistory)) } -func (h *HistoryHandler) GetHistory(ctx *gin.Context, req ListHistoryReq) (Result, error) { +func (h *HistoryHandler) GetHistory(ctx *gin.Context, req required_parameter.ListHistoryReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) history, err := h.svc.GetHistory(ctx, domain.Pagination{ Page: req.Page, @@ -47,7 +48,7 @@ func (h *HistoryHandler) GetHistory(ctx *gin.Context, req ListHistoryReq) (Resul }, err } -func (h *HistoryHandler) DeleteOneHistory(ctx *gin.Context, req DeleteHistoryReq) (Result, error) { +func (h *HistoryHandler) DeleteOneHistory(ctx *gin.Context, req required_parameter.DeleteHistoryReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if err := h.svc.DeleteOneHistory(ctx, req.PostId, uc.Uid); err != nil { return Result{ @@ -61,7 +62,7 @@ func (h *HistoryHandler) DeleteOneHistory(ctx *gin.Context, req DeleteHistoryReq }, nil } -func (h *HistoryHandler) DeleteAllHistory(ctx *gin.Context, req DeleteHistoryAllReq) (Result, error) { +func (h *HistoryHandler) DeleteAllHistory(ctx *gin.Context, req required_parameter.DeleteHistoryAllReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if req.IsDeleteAll == true { if err := h.svc.DeleteAllHistory(ctx, uc.Uid); err != nil { diff --git a/internal/api/mysql_job.go b/internal/api/mysql_job.go deleted file mode 100644 index 294f6e7..0000000 --- a/internal/api/mysql_job.go +++ /dev/null @@ -1,8 +0,0 @@ -package api - -/** - * @Author: Bamboo - * @Author: 13664854532@163.com - * @Date: 2024/6/15 19:42 - * @Desc: - */ diff --git a/internal/api/plate.go b/internal/api/plate.go index cc92b69..0c2a945 100644 --- a/internal/api/plate.go +++ b/internal/api/plate.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/domain" "github.com/GoSimplicity/LinkMe/internal/service" @@ -9,25 +10,22 @@ import ( ijwt "github.com/GoSimplicity/LinkMe/utils/jwt" "github.com/casbin/casbin/v2" "github.com/gin-gonic/gin" - "go.uber.org/zap" ) type PlateHandler struct { svc service.PlateService - l *zap.Logger ce *casbin.Enforcer } -func NewPlateHandler(svc service.PlateService, l *zap.Logger, ce *casbin.Enforcer) *PlateHandler { +func NewPlateHandler(svc service.PlateService, ce *casbin.Enforcer) *PlateHandler { return &PlateHandler{ svc: svc, - l: l, ce: ce, } } func (h *PlateHandler) RegisterRoutes(server *gin.Engine) { - casbinMiddleware := middleware.NewCasbinMiddleware(h.ce, h.l) + casbinMiddleware := middleware.NewCasbinMiddleware(h.ce) permissionGroup := server.Group("/api/plate") permissionGroup.Use(casbinMiddleware.CheckCasbin()) permissionGroup.POST("/create", WrapBody(h.CreatePlate)) @@ -36,7 +34,7 @@ func (h *PlateHandler) RegisterRoutes(server *gin.Engine) { permissionGroup.POST("/list", WrapBody(h.ListPlate)) } -func (h *PlateHandler) CreatePlate(ctx *gin.Context, req CreatePlateReq) (Result, error) { +func (h *PlateHandler) CreatePlate(ctx *gin.Context, req required_parameter.CreatePlateReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if err := h.svc.CreatePlate(ctx, domain.Plate{ Name: req.Name, @@ -54,7 +52,7 @@ func (h *PlateHandler) CreatePlate(ctx *gin.Context, req CreatePlateReq) (Result }, nil } -func (h *PlateHandler) UpdatePlate(ctx *gin.Context, req UpdatePlateReq) (Result, error) { +func (h *PlateHandler) UpdatePlate(ctx *gin.Context, req required_parameter.UpdatePlateReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if err := h.svc.UpdatePlate(ctx, domain.Plate{ ID: req.ID, @@ -73,7 +71,7 @@ func (h *PlateHandler) UpdatePlate(ctx *gin.Context, req UpdatePlateReq) (Result }, nil } -func (h *PlateHandler) DeletePlate(ctx *gin.Context, req DeletePlateReq) (Result, error) { +func (h *PlateHandler) DeletePlate(ctx *gin.Context, req required_parameter.DeletePlateReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) err := h.svc.DeletePlate(ctx, req.PlateID, uc.Uid) if err != nil { @@ -88,7 +86,7 @@ func (h *PlateHandler) DeletePlate(ctx *gin.Context, req DeletePlateReq) (Result }, nil } -func (h *PlateHandler) ListPlate(ctx *gin.Context, req ListPlateReq) (Result, error) { +func (h *PlateHandler) ListPlate(ctx *gin.Context, req required_parameter.ListPlateReq) (Result, error) { plates, err := h.svc.ListPlate(ctx, domain.Pagination{ Page: req.Page, Size: req.Size, diff --git a/internal/api/post.go b/internal/api/post.go index e9135cb..c0d893e 100644 --- a/internal/api/post.go +++ b/internal/api/post.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/domain" "github.com/GoSimplicity/LinkMe/internal/service" @@ -9,29 +10,26 @@ import ( ijwt "github.com/GoSimplicity/LinkMe/utils/jwt" "github.com/casbin/casbin/v2" "github.com/gin-gonic/gin" - "go.uber.org/zap" ) type PostHandler struct { svc service.PostService intSvc service.InteractiveService ce *casbin.Enforcer - l *zap.Logger biz string } -func NewPostHandler(svc service.PostService, intSvc service.InteractiveService, ce *casbin.Enforcer, l *zap.Logger) *PostHandler { +func NewPostHandler(svc service.PostService, intSvc service.InteractiveService, ce *casbin.Enforcer) *PostHandler { return &PostHandler{ svc: svc, intSvc: intSvc, ce: ce, - l: l, biz: "post", } } func (ph *PostHandler) RegisterRoutes(server *gin.Engine) { - casbinMiddleware := middleware.NewCasbinMiddleware(ph.ce, ph.l) + casbinMiddleware := middleware.NewCasbinMiddleware(ph.ce) postGroup := server.Group("/api/posts") postGroup.POST("/edit", WrapBody(ph.Edit)) // 编辑帖子 postGroup.POST("/update", WrapBody(ph.Update)) // 更新帖子 @@ -52,7 +50,7 @@ func (ph *PostHandler) RegisterRoutes(server *gin.Engine) { }) } -func (ph *PostHandler) Edit(ctx *gin.Context, req EditReq) (Result, error) { +func (ph *PostHandler) Edit(ctx *gin.Context, req required_parameter.EditReq) (Result, error) { // 获取当前登陆的用户信息 uc := ctx.MustGet("user").(ijwt.UserClaims) id, err := ph.svc.Create(ctx, domain.Post{ @@ -77,7 +75,7 @@ func (ph *PostHandler) Edit(ctx *gin.Context, req EditReq) (Result, error) { }, nil } -func (ph *PostHandler) Update(ctx *gin.Context, req UpdateReq) (Result, error) { +func (ph *PostHandler) Update(ctx *gin.Context, req required_parameter.UpdateReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if err := ph.svc.Update(ctx, domain.Post{ ID: req.PostId, @@ -99,7 +97,7 @@ func (ph *PostHandler) Update(ctx *gin.Context, req UpdateReq) (Result, error) { }, nil } -func (ph *PostHandler) Publish(ctx *gin.Context, req PublishReq) (Result, error) { +func (ph *PostHandler) Publish(ctx *gin.Context, req required_parameter.PublishReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if err := ph.svc.Publish(ctx, domain.Post{ ID: req.PostId, @@ -119,7 +117,7 @@ func (ph *PostHandler) Publish(ctx *gin.Context, req PublishReq) (Result, error) }, nil } -func (ph *PostHandler) Withdraw(ctx *gin.Context, req WithDrawReq) (Result, error) { +func (ph *PostHandler) Withdraw(ctx *gin.Context, req required_parameter.WithDrawReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if err := ph.svc.Withdraw(ctx, domain.Post{ ID: req.PostId, @@ -139,7 +137,7 @@ func (ph *PostHandler) Withdraw(ctx *gin.Context, req WithDrawReq) (Result, erro }, nil } -func (ph *PostHandler) List(ctx *gin.Context, req ListReq) (Result, error) { +func (ph *PostHandler) List(ctx *gin.Context, req required_parameter.ListReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) du, err := ph.svc.ListPosts(ctx, domain.Pagination{ Page: req.Page, @@ -159,7 +157,7 @@ func (ph *PostHandler) List(ctx *gin.Context, req ListReq) (Result, error) { }, nil } -func (ph *PostHandler) ListPub(ctx *gin.Context, req ListPubReq) (Result, error) { +func (ph *PostHandler) ListPub(ctx *gin.Context, req required_parameter.ListPubReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) du, err := ph.svc.ListPublishedPosts(ctx, domain.Pagination{ Page: req.Page, @@ -179,7 +177,7 @@ func (ph *PostHandler) ListPub(ctx *gin.Context, req ListPubReq) (Result, error) }, nil } -func (ph *PostHandler) Detail(ctx *gin.Context, req DetailReq) (Result, error) { +func (ph *PostHandler) Detail(ctx *gin.Context, req required_parameter.DetailReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) post, err := ph.svc.GetDraftsByAuthor(ctx, req.PostId, uc.Uid) if err != nil { @@ -201,7 +199,7 @@ func (ph *PostHandler) Detail(ctx *gin.Context, req DetailReq) (Result, error) { }, nil } -func (ph *PostHandler) DetailPub(ctx *gin.Context, req DetailReq) (Result, error) { +func (ph *PostHandler) DetailPub(ctx *gin.Context, req required_parameter.DetailReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) post, err := ph.svc.GetPublishedPostById(ctx, req.PostId, uc.Uid) if err != nil { @@ -217,7 +215,7 @@ func (ph *PostHandler) DetailPub(ctx *gin.Context, req DetailReq) (Result, error }, nil } -func (ph *PostHandler) DeletePost(ctx *gin.Context, req DeleteReq) (Result, error) { +func (ph *PostHandler) DeletePost(ctx *gin.Context, req required_parameter.DeleteReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if err := ph.svc.Delete(ctx, req.PostId, uc.Uid); err != nil { return Result{ @@ -232,7 +230,7 @@ func (ph *PostHandler) DeletePost(ctx *gin.Context, req DeleteReq) (Result, erro }, nil } -func (ph *PostHandler) Like(ctx *gin.Context, req LikeReq) (Result, error) { +func (ph *PostHandler) Like(ctx *gin.Context, req required_parameter.LikeReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if req.Liked { if err := ph.intSvc.Like(ctx, ph.biz, req.PostId, uc.Uid); err != nil { @@ -256,7 +254,7 @@ func (ph *PostHandler) Like(ctx *gin.Context, req LikeReq) (Result, error) { }, nil } -func (ph *PostHandler) Collect(ctx *gin.Context, req CollectReq) (Result, error) { +func (ph *PostHandler) Collect(ctx *gin.Context, req required_parameter.CollectReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) if req.Collectd { if err := ph.intSvc.Collect(ctx, ph.biz, req.PostId, req.CollectId, uc.Uid); err != nil { @@ -280,7 +278,7 @@ func (ph *PostHandler) Collect(ctx *gin.Context, req CollectReq) (Result, error) }, nil } -func (ph *PostHandler) ListPost(ctx *gin.Context, req ListPostReq) (Result, error) { +func (ph *PostHandler) ListPost(ctx *gin.Context, req required_parameter.ListPostReq) (Result, error) { du, err := ph.svc.ListAllPost(ctx, domain.Pagination{ Page: req.Page, Size: req.Size, @@ -298,7 +296,7 @@ func (ph *PostHandler) ListPost(ctx *gin.Context, req ListPostReq) (Result, erro }, nil } -func (ph *PostHandler) DetailPost(ctx *gin.Context, req DetailPostReq) (Result, error) { +func (ph *PostHandler) DetailPost(ctx *gin.Context, req required_parameter.DetailPostReq) (Result, error) { post, err := ph.svc.GetPost(ctx, req.PostId) if err != nil { return Result{ @@ -313,7 +311,7 @@ func (ph *PostHandler) DetailPost(ctx *gin.Context, req DetailPostReq) (Result, }, nil } -func (ph *PostHandler) GetPostCount(ctx *gin.Context, _ GetPostCountReq) (Result, error) { +func (ph *PostHandler) GetPostCount(ctx *gin.Context, _ required_parameter.GetPostCountReq) (Result, error) { count, err := ph.svc.GetPostCount(ctx) if err != nil { return Result{ diff --git a/internal/api/relation.go b/internal/api/relation.go index cd81549..926d675 100644 --- a/internal/api/relation.go +++ b/internal/api/relation.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/domain" "github.com/GoSimplicity/LinkMe/internal/service" @@ -29,7 +30,7 @@ func NewRelationHandler(svc service.RelationService) *RelationHandler { } // ListRelations 处理列出关注关系的请求 -func (r *RelationHandler) ListRelations(ctx *gin.Context, req ListRelationsReq) (Result, error) { +func (r *RelationHandler) ListRelations(ctx *gin.Context, req required_parameter.ListRelationsReq) (Result, error) { relations, err := r.svc.ListRelations(ctx, req.FollowerID, domain.Pagination{ Page: req.Page, Size: req.Size, @@ -48,7 +49,7 @@ func (r *RelationHandler) ListRelations(ctx *gin.Context, req ListRelationsReq) } // GetRelationInfo 处理获取关注关系信息的请求 -func (r *RelationHandler) GetRelationInfo(ctx *gin.Context, req GetRelationInfoReq) (Result, error) { +func (r *RelationHandler) GetRelationInfo(ctx *gin.Context, req required_parameter.GetRelationInfoReq) (Result, error) { relation, err := r.svc.GetRelationInfo(ctx, req.FollowerID, req.FolloweeID) if err != nil { return Result{ @@ -64,7 +65,7 @@ func (r *RelationHandler) GetRelationInfo(ctx *gin.Context, req GetRelationInfoR } // FollowUser 处理关注用户的请求 -func (r *RelationHandler) FollowUser(ctx *gin.Context, req FollowUserReq) (Result, error) { +func (r *RelationHandler) FollowUser(ctx *gin.Context, req required_parameter.FollowUserReq) (Result, error) { if err := r.svc.FollowUser(ctx, req.FollowerID, req.FolloweeID); err != nil { return Result{ Code: FollowUserERRORCode, @@ -77,7 +78,7 @@ func (r *RelationHandler) FollowUser(ctx *gin.Context, req FollowUserReq) (Resul }, nil } -func (r *RelationHandler) CancelFollowUser(ctx *gin.Context, req CancelFollowUserReq) (Result, error) { +func (r *RelationHandler) CancelFollowUser(ctx *gin.Context, req required_parameter.CancelFollowUserReq) (Result, error) { if err := r.svc.CancelFollowUser(ctx, req.FollowerID, req.FolloweeID); err != nil { return Result{ Code: CancelFollowUserERRORCode, @@ -91,7 +92,7 @@ func (r *RelationHandler) CancelFollowUser(ctx *gin.Context, req CancelFollowUse } // GetFolloweeCount 获取关注者的数量 -func (r *RelationHandler) GetFolloweeCount(ctx *gin.Context, req GetFolloweeCountReq) (Result, error) { +func (r *RelationHandler) GetFolloweeCount(ctx *gin.Context, req required_parameter.GetFolloweeCountReq) (Result, error) { count, err := r.svc.GetFolloweeCount(ctx, req.UserID) if err != nil { return Result{ @@ -107,7 +108,7 @@ func (r *RelationHandler) GetFolloweeCount(ctx *gin.Context, req GetFolloweeCoun } // GetFollowerCount 获取粉丝的数量 -func (r *RelationHandler) GetFollowerCount(ctx *gin.Context, req GetFollowerCountReq) (Result, error) { +func (r *RelationHandler) GetFollowerCount(ctx *gin.Context, req required_parameter.GetFollowerCountReq) (Result, error) { count, err := r.svc.GetFollowerCount(ctx, req.UserID) if err != nil { return Result{ diff --git a/internal/api/activity_req.go b/internal/api/required_parameter/activity_req.go similarity index 58% rename from internal/api/activity_req.go rename to internal/api/required_parameter/activity_req.go index a889e11..ec3aab8 100644 --- a/internal/api/activity_req.go +++ b/internal/api/required_parameter/activity_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type GetRecentActivityReq struct { } diff --git a/internal/api/check_req.go b/internal/api/required_parameter/check_req.go similarity index 97% rename from internal/api/check_req.go rename to internal/api/required_parameter/check_req.go index f266ccc..63cf246 100644 --- a/internal/api/check_req.go +++ b/internal/api/required_parameter/check_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter // SubmitCheckReq 定义了提交审核请求的结构体 type SubmitCheckReq struct { diff --git a/internal/api/comment_req.go b/internal/api/required_parameter/comment_req.go similarity index 95% rename from internal/api/comment_req.go rename to internal/api/required_parameter/comment_req.go index 846f6ae..1292db6 100644 --- a/internal/api/comment_req.go +++ b/internal/api/required_parameter/comment_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type CreateCommentReq struct { PostId int64 `json:"postId" binding:"required"` diff --git a/internal/api/history_req.go b/internal/api/required_parameter/history_req.go similarity index 91% rename from internal/api/history_req.go rename to internal/api/required_parameter/history_req.go index ceab220..7ee7056 100644 --- a/internal/api/history_req.go +++ b/internal/api/required_parameter/history_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type ListHistoryReq struct { Page int `json:"page,omitempty"` // 当前页码 diff --git a/internal/api/plate_req.go b/internal/api/required_parameter/plate_req.go similarity index 94% rename from internal/api/plate_req.go rename to internal/api/required_parameter/plate_req.go index feddd0c..b5975a7 100644 --- a/internal/api/plate_req.go +++ b/internal/api/required_parameter/plate_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type CreatePlateReq struct { Name string `json:"name"` diff --git a/internal/api/post_req.go b/internal/api/required_parameter/post_req.go similarity index 98% rename from internal/api/post_req.go rename to internal/api/required_parameter/post_req.go index b4e7b09..397bd12 100644 --- a/internal/api/post_req.go +++ b/internal/api/required_parameter/post_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type EditReq struct { PostId int64 `json:"postId,omitempty"` diff --git a/internal/api/relation_req.go b/internal/api/required_parameter/relation_req.go similarity index 96% rename from internal/api/relation_req.go rename to internal/api/required_parameter/relation_req.go index cb2ff5a..3e18038 100644 --- a/internal/api/relation_req.go +++ b/internal/api/required_parameter/relation_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type ListRelationsReq struct { FollowerID int64 `json:"followerId"` diff --git a/internal/api/role_req.go b/internal/api/required_parameter/role_req.go similarity index 97% rename from internal/api/role_req.go rename to internal/api/required_parameter/role_req.go index 6e42eb1..bf8ae08 100644 --- a/internal/api/role_req.go +++ b/internal/api/required_parameter/role_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter // ListPermissionsReq 是获取权限列表的请求 type ListPermissionsReq struct { diff --git a/internal/api/search_req.go b/internal/api/required_parameter/search_req.go similarity index 70% rename from internal/api/search_req.go rename to internal/api/required_parameter/search_req.go index ed37ade..24102c3 100644 --- a/internal/api/search_req.go +++ b/internal/api/required_parameter/search_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type SearchReq struct { Expression string `json:"expression"` diff --git a/internal/api/user_req.go b/internal/api/required_parameter/user_req.go similarity index 90% rename from internal/api/user_req.go rename to internal/api/required_parameter/user_req.go index 048e693..1dea4df 100644 --- a/internal/api/user_req.go +++ b/internal/api/required_parameter/user_req.go @@ -1,4 +1,4 @@ -package api +package required_parameter type SignUpReq struct { Email string `json:"email"` @@ -48,3 +48,12 @@ type ListUserReq struct { type GetUserCountReq struct { } + +type LogoutReq struct { +} + +type RefreshTokenReq struct { +} + +type GetProfileReq struct { +} diff --git a/internal/api/role.go b/internal/api/role.go index f7000b4..7bd0edf 100644 --- a/internal/api/role.go +++ b/internal/api/role.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" "github.com/GoSimplicity/LinkMe/internal/service" "github.com/GoSimplicity/LinkMe/middleware" . "github.com/GoSimplicity/LinkMe/pkg/ginp" @@ -16,16 +17,15 @@ type PermissionHandler struct { ce *casbin.Enforcer } -func NewPermissionHandler(svc service.PermissionService, l *zap.Logger, ce *casbin.Enforcer) *PermissionHandler { +func NewPermissionHandler(svc service.PermissionService, ce *casbin.Enforcer) *PermissionHandler { return &PermissionHandler{ svc: svc, - l: l, ce: ce, } } func (h *PermissionHandler) RegisterRoutes(server *gin.Engine) { - casbinMiddleware := middleware.NewCasbinMiddleware(h.ce, h.l) + casbinMiddleware := middleware.NewCasbinMiddleware(h.ce) permissionGroup := server.Group("/api/permissions") permissionGroup.Use(casbinMiddleware.CheckCasbin()) permissionGroup.GET("/list", WrapQuery(h.GetPermissions)) // 获取权限列表 @@ -36,7 +36,7 @@ func (h *PermissionHandler) RegisterRoutes(server *gin.Engine) { } // GetPermissions 处理获取权限列表的请求 -func (h *PermissionHandler) GetPermissions(ctx *gin.Context, req ListPermissionsReq) (Result, error) { +func (h *PermissionHandler) GetPermissions(ctx *gin.Context, req required_parameter.ListPermissionsReq) (Result, error) { permissions, err := h.svc.GetPermissions(ctx) if err != nil { return Result{ @@ -52,7 +52,7 @@ func (h *PermissionHandler) GetPermissions(ctx *gin.Context, req ListPermissions } // AssignPermission 处理分配权限的请求 -func (h *PermissionHandler) AssignPermission(ctx *gin.Context, req AssignPermissionReq) (Result, error) { +func (h *PermissionHandler) AssignPermission(ctx *gin.Context, req required_parameter.AssignPermissionReq) (Result, error) { if err := h.svc.AssignPermission(ctx, req.UserName, req.Path, req.Method); err != nil { return Result{ Code: http.StatusInternalServerError, @@ -66,7 +66,7 @@ func (h *PermissionHandler) AssignPermission(ctx *gin.Context, req AssignPermiss } // RemovePermission 处理移除权限的请求 -func (h *PermissionHandler) RemovePermission(ctx *gin.Context, req RemovePermissionReq) (Result, error) { +func (h *PermissionHandler) RemovePermission(ctx *gin.Context, req required_parameter.RemovePermissionReq) (Result, error) { if err := h.svc.RemovePermission(ctx, req.UserName, req.Path, req.Method); err != nil { return Result{ Code: http.StatusInternalServerError, @@ -79,7 +79,7 @@ func (h *PermissionHandler) RemovePermission(ctx *gin.Context, req RemovePermiss }, nil } -func (h *PermissionHandler) AssignRole(ctx *gin.Context, req AssignPermissionRoleReq) (Result, error) { +func (h *PermissionHandler) AssignRole(ctx *gin.Context, req required_parameter.AssignPermissionRoleReq) (Result, error) { if err := h.svc.AssignRoleToUser(ctx, req.UserName, req.RoleName); err != nil { return Result{ Code: http.StatusInternalServerError, @@ -92,7 +92,7 @@ func (h *PermissionHandler) AssignRole(ctx *gin.Context, req AssignPermissionRol }, nil } -func (h *PermissionHandler) RemovePermissionRole(ctx *gin.Context, req RemovePermissionRoleReq) (Result, error) { +func (h *PermissionHandler) RemovePermissionRole(ctx *gin.Context, req required_parameter.RemovePermissionRoleReq) (Result, error) { if err := h.svc.RemoveRoleFromUser(ctx, req.UserName, req.RoleName); err != nil { return Result{ Code: http.StatusInternalServerError, diff --git a/internal/api/search.go b/internal/api/search.go index 3bfff98..17d29d5 100644 --- a/internal/api/search.go +++ b/internal/api/search.go @@ -1,6 +1,7 @@ package api import ( + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/service" . "github.com/GoSimplicity/LinkMe/pkg/ginp" @@ -23,7 +24,7 @@ func (s *SearchHandler) RegisterRoutes(server *gin.Engine) { permissionGroup.POST("/search_post", WrapBody(s.SearchPost)) } -func (s *SearchHandler) SearchUser(ctx *gin.Context, req SearchReq) (Result, error) { +func (s *SearchHandler) SearchUser(ctx *gin.Context, req required_parameter.SearchReq) (Result, error) { users, err := s.svc.SearchUsers(ctx, req.Expression) if err != nil { return Result{ @@ -38,7 +39,7 @@ func (s *SearchHandler) SearchUser(ctx *gin.Context, req SearchReq) (Result, err }, nil } -func (s *SearchHandler) SearchPost(ctx *gin.Context, req SearchReq) (Result, error) { +func (s *SearchHandler) SearchPost(ctx *gin.Context, req required_parameter.SearchReq) (Result, error) { posts, err := s.svc.SearchPosts(ctx, req.Expression) if err != nil { return Result{ diff --git a/internal/api/user.go b/internal/api/user.go index a6271b5..779d00c 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -2,6 +2,7 @@ package api import ( "errors" + "github.com/GoSimplicity/LinkMe/internal/api/required_parameter" . "github.com/GoSimplicity/LinkMe/internal/constants" "github.com/GoSimplicity/LinkMe/internal/domain" "github.com/GoSimplicity/LinkMe/internal/domain/events/email" @@ -15,7 +16,6 @@ import ( regexp "github.com/dlclark/regexp2" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" - "go.uber.org/zap" ) const ( @@ -28,49 +28,59 @@ type UserHandler struct { PassWord *regexp.Regexp svc service.UserService ijwt ijwt.Handler - l *zap.Logger ce *casbin.Enforcer smsProducer sms.Producer emailProducer email.Producer } -func NewUserHandler(svc service.UserService, j ijwt.Handler, l *zap.Logger, smsProducer sms.Producer, emailProducer email.Producer, ce *casbin.Enforcer) *UserHandler { +func NewUserHandler(svc service.UserService, j ijwt.Handler, smsProducer sms.Producer, emailProducer email.Producer, ce *casbin.Enforcer) *UserHandler { return &UserHandler{ Email: regexp.MustCompile(emailRegexPattern, regexp.None), PassWord: regexp.MustCompile(passwordRegexPattern, regexp.None), svc: svc, ijwt: j, - l: l, ce: ce, smsProducer: smsProducer, emailProducer: emailProducer, } } +// RegisterRoutes 注册用户相关路由 func (uh *UserHandler) RegisterRoutes(server *gin.Engine) { - casbinMiddleware := middleware.NewCasbinMiddleware(uh.ce, uh.l) + // 初始化Casbin中间件 + casbinMiddleware := middleware.NewCasbinMiddleware(uh.ce) + // 创建用户组路由 userGroup := server.Group("/api/users") + // 用户注册 userGroup.POST("/signup", WrapBody(uh.SignUp)) + // 用户登录 userGroup.POST("/login", WrapBody(uh.Login)) + // 短信登录 userGroup.POST("/login_sms", WrapBody(uh.LoginSMS)) + // 发送短信验证码 userGroup.POST("/send_sms", WrapBody(uh.SendSMS)) + // 发送邮件验证码 userGroup.POST("/send_email", WrapBody(uh.SendEmail)) - userGroup.POST("/logout", uh.Logout) - userGroup.POST("/refresh_token", uh.RefreshToken) + // 用户登出 + userGroup.POST("/logout", WrapBody(uh.Logout)) + // 刷新令牌 + userGroup.POST("/refresh_token", WrapBody(uh.RefreshToken)) + // 修改密码 userGroup.POST("/change_password", WrapBody(uh.ChangePassword)) + // 注销用户 userGroup.DELETE("/write_off", WrapBody(uh.WriteOff)) - userGroup.GET("/profile", uh.GetProfile) + // 获取用户资料 + userGroup.GET("/profile", WrapQuery(uh.GetProfile)) + // 更新用户资料 userGroup.POST("/update_profile", WrapBody(uh.UpdateProfileByID)) - userGroup.POST("/list", casbinMiddleware.CheckCasbin(), WrapBody(uh.ListUser)) // 管理员使用 - userGroup.GET("/stats", casbinMiddleware.CheckCasbin(), WrapQuery(uh.GetUserCount)) // 管理员使用 - // 测试接口 - userGroup.GET("/hello", func(ctx *gin.Context) { - ctx.JSON(200, "hello world!") - }) + // 获取用户列表(管理员使用) + userGroup.POST("/list", casbinMiddleware.CheckCasbin(), WrapBody(uh.ListUser)) + // 获取用户统计(管理员使用) + userGroup.GET("/stats", casbinMiddleware.CheckCasbin(), WrapQuery(uh.GetUserCount)) } // SignUp 用户注册 -func (uh *UserHandler) SignUp(ctx *gin.Context, req SignUpReq) (Result, error) { +func (uh *UserHandler) SignUp(ctx *gin.Context, req required_parameter.SignUpReq) (Result, error) { // 验证邮箱格式 emailValid, err := uh.Email.MatchString(req.Email) if err != nil { @@ -132,39 +142,51 @@ func (uh *UserHandler) SignUp(ctx *gin.Context, req SignUpReq) (Result, error) { } // Login 登陆 -func (uh *UserHandler) Login(ctx *gin.Context, req LoginReq) (Result, error) { +func (uh *UserHandler) Login(ctx *gin.Context, req required_parameter.LoginReq) (Result, error) { du, err := uh.svc.Login(ctx, req.Email, req.Password) - if err == nil { - err = uh.ijwt.SetLoginToken(ctx, du.ID) + if err != nil { + if errors.Is(err, service.ErrInvalidUserOrPassword) { + return Result{ + Code: UserInvalidOrPasswordCode, + Msg: UserLoginFailure, + }, nil + } return Result{ - Code: RequestsOK, - Msg: UserLoginSuccess, - }, nil - } else if errors.Is(err, service.ErrInvalidUserOrPassword) { + Code: UserServerErrorCode, + Msg: UserLoginFailure, + }, err + } + token, er := uh.ijwt.SetLoginToken(ctx, du.ID) + if er != nil { return Result{ - Code: UserInvalidOrPasswordCode, + Code: UserServerErrorCode, Msg: UserLoginFailure, - }, nil + }, er } return Result{ - Code: UserServerErrorCode, - }, err + Code: RequestsOK, + Msg: UserLoginSuccess, + Data: token, + }, nil } // Logout 登出 -func (uh *UserHandler) Logout(ctx *gin.Context) { +func (uh *UserHandler) Logout(ctx *gin.Context, _ required_parameter.LogoutReq) (Result, error) { // 清除JWT令牌 if err := uh.ijwt.ClearToken(ctx); err != nil { - ctx.JSON(ServerERROR, gin.H{"error": UserLogoutFailure}) - return + return Result{ + Code: UserServerErrorCode, + Msg: UserLogoutFailure, + }, err } - ctx.JSON(RequestsOK, gin.H{"message": UserLogoutSuccess}) + return Result{ + Code: RequestsOK, + Msg: UserLogoutSuccess, + }, nil } // RefreshToken 刷新令牌 -func (uh *UserHandler) RefreshToken(ctx *gin.Context) { - // 该方法需配合前端使用,前端在Authorization中携带长token - // 长token只用于刷新短token,短token用于身份验证 +func (uh *UserHandler) RefreshToken(ctx *gin.Context, _ required_parameter.RefreshTokenReq) (Result, error) { var rc ijwt.RefreshClaims // 从前端的Authorization中取出token tokenString := uh.ijwt.ExtractToken(ctx) @@ -172,40 +194,45 @@ func (uh *UserHandler) RefreshToken(ctx *gin.Context) { token, err := jwt.ParseWithClaims(tokenString, &rc, func(token *jwt.Token) (interface{}, error) { return ijwt.Key2, nil }) - if err != nil { - ctx.AbortWithStatus(ServerERROR) - return - } - if token == nil || !token.Valid { - ctx.AbortWithStatus(ServerERROR) - return + if err != nil || token == nil || !token.Valid { + return Result{ + Code: UserServerErrorCode, + Msg: UserRefreshTokenFailure, + }, err } // 检查会话状态是否异常 if err = uh.ijwt.CheckSession(ctx, rc.Ssid); err != nil { - ctx.AbortWithStatus(ServerERROR) - return + return Result{ + Code: UserServerErrorCode, + Msg: UserRefreshTokenFailure, + }, err } // 刷新短token - if err = uh.ijwt.SetJWTToken(ctx, rc.Uid, rc.Ssid); err != nil { - ctx.AbortWithStatus(ServerERROR) - return + tokenStr, er := uh.ijwt.SetJWTToken(ctx, rc.Uid, rc.Ssid) + if er != nil { + return Result{ + Code: UserServerErrorCode, + Msg: UserRefreshTokenFailure, + }, er } - ctx.JSON(RequestsOK, gin.H{ - "message": UserRefreshTokenSuccess, - }) + return Result{ + Code: RequestsOK, + Msg: UserRefreshTokenSuccess, + Data: tokenStr, + }, nil } -func (uh *UserHandler) SendSMS(ctx *gin.Context, req SMSReq) (Result, error) { - valid := utils.IsValidNumber(req.Number) - if !valid { - uh.l.Error("电话号码无效", zap.String("number: ", req.Number)) +// SendSMS 发送短信验证码 +func (uh *UserHandler) SendSMS(ctx *gin.Context, req required_parameter.SMSReq) (Result, error) { + // 验证手机号码格式 + if !utils.IsValidNumber(req.Number) { return Result{ Code: SMSNumberErr, Msg: InvalidNumber, }, nil } + // 发送短信验证码 if err := uh.smsProducer.ProduceSMSCode(ctx, sms.SMSCodeEvent{Number: req.Number}); err != nil { - uh.l.Error("kafka produce sms failed", zap.Error(err)) return Result{}, err } return Result{ @@ -214,7 +241,8 @@ func (uh *UserHandler) SendSMS(ctx *gin.Context, req SMSReq) (Result, error) { }, nil } -func (uh *UserHandler) ChangePassword(ctx *gin.Context, req ChangeReq) (Result, error) { +// ChangePassword 修改密码 +func (uh *UserHandler) ChangePassword(ctx *gin.Context, req required_parameter.ChangeReq) (Result, error) { // 检查新密码和确认密码是否匹配 if req.NewPassword != req.ConfirmPassword { return Result{ @@ -222,15 +250,14 @@ func (uh *UserHandler) ChangePassword(ctx *gin.Context, req ChangeReq) (Result, Msg: UserPasswordMismatchError, }, nil } - err := uh.svc.ChangePassword(ctx.Request.Context(), req.Email, req.Password, req.NewPassword, req.ConfirmPassword) - if err != nil { + // 修改密码 + if err := uh.svc.ChangePassword(ctx.Request.Context(), req.Email, req.Password, req.NewPassword, req.ConfirmPassword); err != nil { if errors.Is(err, service.ErrInvalidUserOrPassword) { return Result{ Code: UserInvalidOrPasswordCode, Msg: UserLoginFailure, }, nil } - uh.l.Error("change password failed", zap.Error(err)) return Result{ Code: UserServerErrorCode, Msg: UserPasswordChangeFailure, @@ -241,18 +268,20 @@ func (uh *UserHandler) ChangePassword(ctx *gin.Context, req ChangeReq) (Result, Msg: UserPasswordChangeSuccess, }, nil } -func (uh *UserHandler) SendEmail(ctx *gin.Context, req EmailReq) (Result, error) { - emailValid, err := uh.Email.MatchString(req.Email) - if err != nil { + +// SendEmail 发送邮件 +func (uh *UserHandler) SendEmail(ctx *gin.Context, req required_parameter.EmailReq) (Result, error) { + // 验证邮箱格式 + if emailValid, err := uh.Email.MatchString(req.Email); err != nil { return Result{}, err - } - if !emailValid { + } else if !emailValid { return Result{ Code: UserInvalidInputCode, Msg: UserEmailFormatError, }, nil } - if err = uh.emailProducer.ProduceEmail(ctx, email.EmailEvent{Email: req.Email}); err != nil { + // 发送邮件 + if err := uh.emailProducer.ProduceEmail(ctx, email.EmailEvent{Email: req.Email}); err != nil { return Result{}, err } return Result{ @@ -261,23 +290,24 @@ func (uh *UserHandler) SendEmail(ctx *gin.Context, req EmailReq) (Result, error) }, nil } -func (uh *UserHandler) WriteOff(ctx *gin.Context, req DeleteUserReq) (Result, error) { +// WriteOff 注销用户 +func (uh *UserHandler) WriteOff(ctx *gin.Context, req required_parameter.DeleteUserReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) - err := uh.svc.DeleteUser(ctx, req.Email, req.Password, uc.Uid) - if err != nil { + // 删除用户 + if err := uh.svc.DeleteUser(ctx, req.Email, req.Password, uc.Uid); err != nil { if errors.Is(err, service.ErrInvalidUserOrPassword) { return Result{ Code: UserInvalidOrPasswordCode, Msg: UserLoginFailure, }, nil } - uh.l.Error("write off failed", zap.Error(err)) return Result{ Code: UserServerErrorCode, Msg: UserDeletedFailure, }, err } - if err = uh.ijwt.ClearToken(ctx); err != nil { + // 清除JWT令牌 + if err := uh.ijwt.ClearToken(ctx); err != nil { return Result{}, err } return Result{ @@ -286,22 +316,28 @@ func (uh *UserHandler) WriteOff(ctx *gin.Context, req DeleteUserReq) (Result, er }, nil } -func (uh *UserHandler) GetProfile(ctx *gin.Context) { +// GetProfile 获取用户资料 +func (uh *UserHandler) GetProfile(ctx *gin.Context, _ required_parameter.GetProfileReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) + // 获取用户资料 profile, err := uh.svc.GetProfileByUserID(ctx, uc.Uid) if err != nil { - ctx.JSON(RequestsOK, gin.H{ - "data": profile, - }) - return + return Result{ + Code: UserServerErrorCode, + Msg: UserProfileGetFailure, + }, err } - ctx.JSON(RequestsOK, gin.H{ - "data": profile, - }) + return Result{ + Code: RequestsOK, + Msg: UserProfileGetSuccess, + Data: profile, + }, nil } -func (uh *UserHandler) UpdateProfileByID(ctx *gin.Context, req UpdateProfileReq) (Result, error) { +// UpdateProfileByID 更新用户资料 +func (uh *UserHandler) UpdateProfileByID(ctx *gin.Context, req required_parameter.UpdateProfileReq) (Result, error) { uc := ctx.MustGet("user").(ijwt.UserClaims) + // 更新用户资料 err := uh.svc.UpdateProfile(ctx, domain.Profile{ NickName: req.NickName, Avatar: req.Avatar, @@ -315,17 +351,22 @@ func (uh *UserHandler) UpdateProfileByID(ctx *gin.Context, req UpdateProfileReq) Msg: UserProfileUpdateFailure, }, err } + return Result{ Code: RequestsOK, Msg: UserProfileUpdateSuccess, }, nil } -func (uh *UserHandler) LoginSMS(ctx *gin.Context, req LoginSMSReq) (Result, error) { +// LoginSMS 短信登录 +func (uh *UserHandler) LoginSMS(ctx *gin.Context, req required_parameter.LoginSMSReq) (Result, error) { + // 此处可以添加短信登录逻辑 return Result{}, nil } -func (uh *UserHandler) ListUser(ctx *gin.Context, req ListUserReq) (Result, error) { +// ListUser 获取用户列表(管理员使用) +func (uh *UserHandler) ListUser(ctx *gin.Context, req required_parameter.ListUserReq) (Result, error) { + // 分页查询用户列表 users, err := uh.svc.ListUser(ctx, domain.Pagination{ Page: req.Page, Size: req.Size, @@ -343,7 +384,9 @@ func (uh *UserHandler) ListUser(ctx *gin.Context, req ListUserReq) (Result, erro }, nil } -func (uh *UserHandler) GetUserCount(ctx *gin.Context, _ GetUserCountReq) (Result, error) { +// GetUserCount 获取用户统计(管理员使用) +func (uh *UserHandler) GetUserCount(ctx *gin.Context, _ required_parameter.GetUserCountReq) (Result, error) { + // 获取用户总数 count, err := uh.svc.GetUserCount(ctx) if err != nil { return Result{ diff --git a/internal/api/user_test.go b/internal/api/user_test.go deleted file mode 100644 index 3977aa7..0000000 --- a/internal/api/user_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package api - -// -//import ( -// . "LinkMe/internal/constants" -// "LinkMe/internal/domain" -// "LinkMe/internal/service" -// svcmocks "LinkMe/internal/service/mocks" -// "LinkMe/pkg/ginp" -// "bytes" -// "encoding/json" -// "github.com/gin-gonic/gin" -// "github.com/stretchr/testify/assert" -// "go.uber.org/mock/gomock" -// "net/http" -// "net/http/httptest" -// "testing" -//) -// -//func TestUserHandler_SignUp(t *testing.T) { -// testCases := []struct { -// name string -// mock func(ctrl *gomock.Controller) service.UserService -// reqBuilder func(t *testing.T) *http.Request -// wantCode int -// wantBody ginp.Result -// }{ -// { -// name: "注册成功", -// wantCode: 200, -// wantBody: ginp.Result{ -// Code: http.StatusOK, -// Msg: UserSignUpSuccess, -// }, -// mock: func(ctrl *gomock.Controller) service.UserService { -// userSvc := svcmocks.NewMockUserService(ctrl) -// userSvc.EXPECT(). -// SignUp(gomock.Any(), domain.User{ -// Email: "1234@qq.com", -// Password: "Abc@1234", -// }).Return(nil) -// return userSvc -// }, -// reqBuilder: func(t *testing.T) *http.Request { -// req, err := http.NewRequest(http.MethodPost, "/users/signup", bytes.NewReader([]byte(`{ -// "email": "1234@qq.com", -// "password": "Abc@1234", -// "confirmPassword": "Abc@1234" -// }`))) -// req.Header.Set("Content-Type", "application/json") -// assert.NoError(t, err) -// return req -// }, -// }, -// { -// name: "无效的请求负载", -// wantCode: http.StatusBadRequest, -// wantBody: ginp.Result{ -// Msg: "无效的请求负载", -// }, -// mock: func(ctrl *gomock.Controller) service.UserService { -// userSvc := svcmocks.NewMockUserService(ctrl) -// return userSvc -// }, -// reqBuilder: func(t *testing.T) *http.Request { -// req, err := http.NewRequest(http.MethodPost, "/users/signup", bytes.NewReader([]byte(`{ -// "email": "1234@qq.com", -// "password": "Abc@1234", -// }`))) -// req.Header.Set("Content-Type", "application/json") -// assert.NoError(t, err) -// return req -// }, -// }, -// { -// name: "非法邮箱格式", -// wantCode: http.StatusOK, -// wantBody: ginp.Result{ -// Code: UserInvalidInput, -// Msg: UserEmailFormatError, -// }, -// mock: func(ctrl *gomock.Controller) service.UserService { -// userSvc := svcmocks.NewMockUserService(ctrl) -// return userSvc -// }, -// reqBuilder: func(t *testing.T) *http.Request { -// req, err := http.NewRequest(http.MethodPost, "/users/signup", bytes.NewReader([]byte(`{ -// "email": "123", -// "password": "Abc@1234", -// "confirmPassword": "Abc@1234" -// }`))) -// req.Header.Set("Content-Type", "application/json") -// assert.NoError(t, err) -// return req -// }, -// }, -// { -// name: "两次输入密码不对", -// wantCode: http.StatusOK, -// wantBody: ginp.Result{ -// Code: UserInvalidInput, -// Msg: UserPasswordMismatchError, -// }, -// mock: func(ctrl *gomock.Controller) service.UserService { -// userSvc := svcmocks.NewMockUserService(ctrl) -// return userSvc -// }, -// reqBuilder: func(t *testing.T) *http.Request { -// req, err := http.NewRequest(http.MethodPost, "/users/signup", bytes.NewReader([]byte(`{ -// "email": "1234@qq.com", -// "password": "Abc@1234", -// "confirmPassword": "Abc@12345" -// }`))) -// req.Header.Set("Content-Type", "application/json") -// assert.NoError(t, err) -// return req -// }, -// }, -// { -// name: "密码必须包含字母、数字、特殊字符,并且不少于八位", -// wantCode: http.StatusOK, -// wantBody: ginp.Result{ -// Code: UserInvalidInput, -// Msg: UserPasswordFormatError, -// }, -// mock: func(ctrl *gomock.Controller) service.UserService { -// userSvc := svcmocks.NewMockUserService(ctrl) -// return userSvc -// }, -// reqBuilder: func(t *testing.T) *http.Request { -// req, err := http.NewRequest(http.MethodPost, "/users/signup", bytes.NewReader([]byte(`{ -// "email": "1234@qq.com", -// "password": "123", -// "confirmPassword": "123" -// }`))) -// req.Header.Set("Content-Type", "application/json") -// assert.NoError(t, err) -// return req -// }, -// }, -// { -// name: "邮箱冲突", -// wantCode: 200, -// wantBody: ginp.Result{ -// Code: UserDuplicateEmail, -// Msg: UserEmailConflictError, -// }, -// mock: func(ctrl *gomock.Controller) service.UserService { -// userSvc := svcmocks.NewMockUserService(ctrl) -// userSvc.EXPECT().SignUp(gomock.Any(), domain.User{ -// Email: "1234@qq.com", -// Password: "Abc@1234", -// }).Return(service.ErrDuplicateEmail) -// return userSvc -// }, -// reqBuilder: func(t *testing.T) *http.Request { -// req, err := http.NewRequest(http.MethodPost, "/users/signup", bytes.NewReader([]byte(`{ -// "email": "1234@qq.com", -// "password": "Abc@1234", -// "confirmPassword": "Abc@1234" -// }`))) -// req.Header.Set("Content-Type", "application/json") -// assert.NoError(t, err) -// return req -// }, -// }, -// } -// // 遍历所有测试场景并执行测试 -// for _, tc := range testCases { -// t.Run(tc.name, func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// userSvc := tc.mock(ctrl) -// hdl := NewUserHandler(userSvc, nil, nil) -// server := gin.Default() -// hdl.RegisterRoutes(server) -// req := tc.reqBuilder(t) -// // 创建httptest.Recorder以捕获服务器响应 -// recorder := httptest.NewRecorder() -// // 发送请求到服务器并接收响应 -// server.ServeHTTP(recorder, req) -// // 对响应进行断言,验证状态码和响应体是否符合预期 -// assert.Equal(t, tc.wantCode, recorder.Code) -// wantBody, err := json.Marshal(tc.wantBody) -// assert.NoError(t, err) -// assert.Equal(t, string(wantBody), recorder.Body.String()) -// }) -// } -//} diff --git a/internal/repository/dao/user.go b/internal/repository/dao/user.go index df5594b..b3c9ca5 100644 --- a/internal/repository/dao/user.go +++ b/internal/repository/dao/user.go @@ -15,21 +15,35 @@ import ( ) var ( + // ErrCodeDuplicateEmailNumber 表示邮箱重复的错误码 ErrCodeDuplicateEmailNumber uint16 = 1062 - ErrDuplicateEmail = errors.New("duplicate email") - ErrUserNotFound = errors.New("user not found") + // ErrDuplicateEmail 表示邮箱重复错误 + ErrDuplicateEmail = errors.New("duplicate email") + // ErrUserNotFound 表示用户未找到错误 + ErrUserNotFound = errors.New("user not found") ) +// UserDAO 定义用户数据访问对象接口 type UserDAO interface { + // CreateUser 创建用户 CreateUser(ctx context.Context, u User) error + // FindByID 根据用户ID查找用户 FindByID(ctx context.Context, id int64) (User, error) + // FindByEmail 根据邮箱查找用户 FindByEmail(ctx context.Context, email string) (User, error) + // FindByPhone 根据手机号查找用户 FindByPhone(ctx context.Context, phone string) (User, error) + // UpdatePasswordByEmail 根据邮箱更新密码 UpdatePasswordByEmail(ctx context.Context, email string, newPassword string) error + // DeleteUser 删除用户 DeleteUser(ctx context.Context, email string, uid int64) error + // UpdateProfile 更新用户资料 UpdateProfile(ctx context.Context, profile domain.Profile) error + // GetProfileByUserID 根据用户ID获取用户资料 GetProfileByUserID(ctx context.Context, userId int64) (domain.Profile, error) + // ListUser 获取用户列表 ListUser(ctx context.Context, pagination domain.Pagination) ([]domain.UserWithProfileAndRule, error) + // GetUserCount 获取用户总数 GetUserCount(ctx context.Context) (int64, error) } @@ -157,29 +171,20 @@ func (ud *userDAO) FindByPhone(ctx context.Context, phone string) (User, error) return user, nil } +// UpdatePasswordByEmail 根据邮箱更新密码 func (ud *userDAO) UpdatePasswordByEmail(ctx context.Context, email string, newPassword string) error { - tx := ud.db.WithContext(ctx).Begin() - if tx.Error != nil { - ud.l.Error("failed to begin transaction", zap.Error(tx.Error)) - return tx.Error - } - // 更新密码 - if err := tx.Model(&User{}).Where("email = ? AND deleted = ?", email, false).Update("password_hash", newPassword).Error; err != nil { - ud.l.Error("update password failed", zap.String("email", email), zap.Error(err)) - if rollbackErr := tx.Rollback().Error; rollbackErr != nil { - ud.l.Error("failed to rollback transaction", zap.Error(rollbackErr)) + // 使用事务更新密码 + return ud.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + // 更新密码 + if err := tx.Model(&User{}).Where("email = ? AND deleted = ?", email, false).Update("password_hash", newPassword).Error; err != nil { + ud.l.Error("update password failed", zap.String("email", email), zap.Error(err)) + return err } - return err - } - // 提交事务 - if err := tx.Commit().Error; err != nil { - ud.l.Error("failed to commit transaction", zap.Error(err)) - return err - } - ud.l.Info("password updated successfully", zap.String("email", email)) - return nil + return nil + }) } +// DeleteUser 删除用户 func (ud *userDAO) DeleteUser(ctx context.Context, email string, uid int64) error { tx := ud.db.WithContext(ctx).Begin() defer func() { @@ -188,16 +193,17 @@ func (ud *userDAO) DeleteUser(ctx context.Context, email string, uid int64) erro panic(r) } }() + // 将用户标记为已删除 if err := tx.Model(&User{}).Where("email = ? AND deleted = ? AND id = ?", email, false, uid).Update("deleted", true).Error; err != nil { tx.Rollback() ud.l.Error("failed to mark user as deleted", zap.String("email", email), zap.Error(err)) return err } + // 提交事务 if err := tx.Commit().Error; err != nil { ud.l.Error("failed to commit transaction", zap.String("email", email), zap.Error(err)) return err } - ud.l.Info("user marked as deleted", zap.String("email", email)) return nil } @@ -219,6 +225,7 @@ func (ud *userDAO) UpdateProfile(ctx context.Context, profile domain.Profile) er return nil } +// GetProfileByUserID 根据用户ID获取用户资料 func (ud *userDAO) GetProfileByUserID(ctx context.Context, userId int64) (domain.Profile, error) { var profile domain.Profile if err := ud.db.WithContext(ctx).Where("user_id = ?", userId).First(&profile).Error; err != nil { @@ -228,6 +235,7 @@ func (ud *userDAO) GetProfileByUserID(ctx context.Context, userId int64) (domain return profile, nil } +// ListUser 获取用户列表 func (ud *userDAO) ListUser(ctx context.Context, pagination domain.Pagination) ([]domain.UserWithProfileAndRule, error) { var usersWithProfiles []domain.UserWithProfileAndRule intSize := int(*pagination.Size) @@ -259,6 +267,16 @@ func (ud *userDAO) ListUser(ctx context.Context, pagination domain.Pagination) ( return usersWithProfiles, nil } +// GetUserCount 获取用户总数 +func (ud *userDAO) GetUserCount(ctx context.Context) (int64, error) { + var count int64 + err := ud.db.WithContext(ctx).Model(&User{}).Count(&count).Error + if err != nil { + return -1, err + } + return count, nil +} + // getUserRoleEmails 获取给定用户ID的角色电子邮件 func (ud *userDAO) getUserRoleEmails(ctx context.Context, userID int64) ([]string, error) { userIDStr := strconv.FormatInt(userID, 10) @@ -283,7 +301,6 @@ func (ud *userDAO) getUserRoleEmails(ctx context.Context, userID int64) ([]strin Select("email"). Where("id IN (?)", roleIDs). Scan(&roleUsers).Error - if err != nil { return nil, err } @@ -292,12 +309,3 @@ func (ud *userDAO) getUserRoleEmails(ctx context.Context, userID int64) ([]strin } return roleEmails, nil } - -func (ud *userDAO) GetUserCount(ctx context.Context) (int64, error) { - var count int64 - err := ud.db.WithContext(ctx).Model(&User{}).Count(&count).Error - if err != nil { - return -1, err - } - return count, nil -} diff --git a/internal/repository/user.go b/internal/repository/user.go index 640d21c..c7a5ba8 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -9,20 +9,33 @@ import ( ) var ( + // ErrDuplicateEmail 表示邮箱重复错误 ErrDuplicateEmail = dao.ErrDuplicateEmail - ErrUserNotFound = dao.ErrUserNotFound + // ErrUserNotFound 表示用户未找到错误 + ErrUserNotFound = dao.ErrUserNotFound ) +// UserRepository 定义用户仓库接口 type UserRepository interface { + // CreateUser 创建用户 CreateUser(ctx context.Context, u domain.User) error + // FindByID 根据用户ID查找用户 FindByID(ctx context.Context, id int64) (domain.User, error) + // FindByPhone 根据手机号查找用户 FindByPhone(ctx context.Context, phone string) (domain.User, error) + // FindByEmail 根据邮箱查找用户 FindByEmail(ctx context.Context, email string) (domain.User, error) + // ChangePassword 修改密码 ChangePassword(ctx context.Context, email string, newPassword string) error + // DeleteUser 删除用户 DeleteUser(ctx context.Context, email string, uid int64) error + // UpdateProfile 更新用户资料 UpdateProfile(ctx context.Context, profile domain.Profile) error + // GetProfile 根据用户ID获取用户资料 GetProfile(ctx context.Context, UserId int64) (domain.Profile, error) + // ListUser 获取用户列表 ListUser(ctx context.Context, pagination domain.Pagination) ([]domain.UserWithProfileAndRule, error) + // GetUserCount 获取用户总数 GetUserCount(ctx context.Context) (int64, error) } @@ -40,6 +53,7 @@ func NewUserRepository(dao dao.UserDAO, cache cache.UserCache, l *zap.Logger) Us } } +// ChangePassword 修改密码 func (ur *userRepository) ChangePassword(ctx context.Context, email string, newPassword string) error { return ur.dao.UpdatePasswordByEmail(ctx, email, newPassword) } @@ -63,6 +77,7 @@ func (ur *userRepository) FindByID(ctx context.Context, id int64) (domain.User, return domain.User{}, err } du = toDomainUser(u) + // 异步将用户信息写入缓存 go func() { if setErr := ur.cache.Set(ctx, du); setErr != nil { ur.l.Error("set cache failed", zap.Error(setErr)) @@ -73,7 +88,7 @@ func (ur *userRepository) FindByID(ctx context.Context, id int64) (domain.User, // FindByPhone 通过电话查询用户 func (ur *userRepository) FindByPhone(ctx context.Context, phone string) (domain.User, error) { - u, err := ur.dao.FindByEmail(ctx, phone) + u, err := ur.dao.FindByPhone(ctx, phone) return toDomainUser(u), err } @@ -83,34 +98,37 @@ func (ur *userRepository) FindByEmail(ctx context.Context, email string) (domain return toDomainUser(u), err } +// DeleteUser 删除用户 func (ur *userRepository) DeleteUser(ctx context.Context, email string, uid int64) error { - err := ur.dao.DeleteUser(ctx, email, uid) - if err != nil { - return err - } - return nil + return ur.dao.DeleteUser(ctx, email, uid) } + +// UpdateProfile 更新用户资料 func (ur *userRepository) UpdateProfile(ctx context.Context, profile domain.Profile) error { return ur.dao.UpdateProfile(ctx, profile) } + +// GetProfile 通过用户ID获取用户资料 func (ur *userRepository) GetProfile(ctx context.Context, UserId int64) (domain.Profile, error) { return ur.dao.GetProfileByUserID(ctx, UserId) } +// ListUser 获取用户列表 func (ur *userRepository) ListUser(ctx context.Context, pagination domain.Pagination) ([]domain.UserWithProfileAndRule, error) { users, err := ur.dao.ListUser(ctx, pagination) if err != nil { return nil, err } - return users, err + return users, nil } +// GetUserCount 获取用户总数 func (ur *userRepository) GetUserCount(ctx context.Context) (int64, error) { count, err := ur.dao.GetUserCount(ctx) if err != nil { return -1, err } - return count, err + return count, nil } // 将领域层对象转为dao层对象 diff --git a/internal/service/user.go b/internal/service/user.go index 0573f19..78546d6 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -10,18 +10,29 @@ import ( ) var ( - ErrDuplicateEmail = repository.ErrDuplicateEmail + // ErrDuplicateEmail 表示邮箱重复错误 + ErrDuplicateEmail = repository.ErrDuplicateEmail + // ErrInvalidUserOrPassword 表示用户名或密码错误 ErrInvalidUserOrPassword = errors.New("username or password is incorrect") ) +// UserService 定义用户服务接口 type UserService interface { + // SignUp 用户注册 SignUp(ctx context.Context, u domain.User) error + // Login 用户登录 Login(ctx context.Context, email string, password string) (domain.User, error) + // ChangePassword 修改密码 ChangePassword(ctx context.Context, email string, password string, newPassword string, confirmPassword string) error + // DeleteUser 删除用户 DeleteUser(ctx context.Context, email string, password string, uid int64) error - UpdateProfile(ctx context.Context, profile domain.Profile) (err error) - GetProfileByUserID(ctx context.Context, UserID int64) (profile domain.Profile, err error) + // UpdateProfile 更新用户资料 + UpdateProfile(ctx context.Context, profile domain.Profile) error + // GetProfileByUserID 通过用户ID获取用户资料 + GetProfileByUserID(ctx context.Context, UserID int64) (domain.Profile, error) + // ListUser 获取用户列表 ListUser(ctx context.Context, pagination domain.Pagination) ([]domain.UserWithProfileAndRule, error) + // GetUserCount 获取用户总数 GetUserCount(ctx context.Context) (int64, error) } @@ -41,11 +52,13 @@ func NewUserService(repo repository.UserRepository, l *zap.Logger, searchRepo re // SignUp 注册逻辑 func (us *userService) SignUp(ctx context.Context, u domain.User) error { + // 生成密码哈希值 hash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost) if err != nil { return err } u.Password = string(hash) + // 将用户信息输入到搜索库中 err = us.searchRepo.InputUser(ctx, domain.UserSearch{ Email: u.Email, Id: u.ID, @@ -55,19 +68,20 @@ func (us *userService) SignUp(ctx context.Context, u domain.User) error { if err != nil { us.l.Error("failed to input user to search repo", zap.Error(err)) } + // 创建用户 return us.repo.CreateUser(ctx, u) } // Login 登陆逻辑 func (us *userService) Login(ctx context.Context, email string, password string) (domain.User, error) { + // 根据邮箱查找用户 u, err := us.repo.FindByEmail(ctx, email) - // 如果用户没有找到(未注册),则返回空对象 if errors.Is(err, repository.ErrUserNotFound) { return domain.User{}, err } else if err != nil { return domain.User{}, err } - // 将密文密码转为明文 + // 验证密码 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) if err != nil { return domain.User{}, ErrInvalidUserOrPassword @@ -75,7 +89,9 @@ func (us *userService) Login(ctx context.Context, email string, password string) return u, nil } +// ChangePassword 修改密码 func (us *userService) ChangePassword(ctx context.Context, email string, password string, newPassword string, confirmPassword string) error { + // 根据邮箱查找用户 u, err := us.repo.FindByEmail(ctx, email) if err != nil { if errors.Is(err, repository.ErrUserNotFound) { @@ -83,20 +99,25 @@ func (us *userService) ChangePassword(ctx context.Context, email string, passwor } return err } + // 验证当前密码 if er := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)); er != nil { return ErrInvalidUserOrPassword } + // 生成新密码哈希值 newHash, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { return err } + // 更新密码 if er := us.repo.ChangePassword(ctx, email, string(newHash)); er != nil { return er } return nil } +// DeleteUser 删除用户 func (us *userService) DeleteUser(ctx context.Context, email string, password string, uid int64) error { + // 根据邮箱查找用户 u, err := us.repo.FindByEmail(ctx, email) if err != nil { if errors.Is(err, repository.ErrUserNotFound) { @@ -104,23 +125,31 @@ func (us *userService) DeleteUser(ctx context.Context, email string, password st } return err } + // 验证密码 if er := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)); er != nil { return ErrInvalidUserOrPassword } + // 删除用户 err = us.repo.DeleteUser(ctx, email, uid) if err != nil { return err } + // 从搜索库中删除用户索引 err = us.searchRepo.DeleteUserIndex(ctx, uid) if err != nil { - us.l.Error("failed to input user to search repo", zap.Error(err)) + us.l.Error("failed to delete user from search repo", zap.Error(err)) } + return nil } -func (us *userService) UpdateProfile(ctx context.Context, profile domain.Profile) (err error) { + +// UpdateProfile 更新用户资料 +func (us *userService) UpdateProfile(ctx context.Context, profile domain.Profile) error { + // 根据用户ID查找用户 user, _ := us.repo.FindByID(ctx, profile.UserID) + // 如果昵称发生变化,更新搜索库中的用户信息 if user.Profile.NickName != profile.NickName { - err = us.searchRepo.InputUser(ctx, domain.UserSearch{ + err := us.searchRepo.InputUser(ctx, domain.UserSearch{ Id: profile.UserID, Nickname: profile.NickName, }) @@ -128,21 +157,28 @@ func (us *userService) UpdateProfile(ctx context.Context, profile domain.Profile us.l.Error("failed to input user to search repo", zap.Error(err)) } } + // 更新用户资料 return us.repo.UpdateProfile(ctx, profile) } +// GetProfileByUserID 根据用户ID获取用户资料 func (us *userService) GetProfileByUserID(ctx context.Context, UserID int64) (profile domain.Profile, err error) { + // 从仓库中获取用户资料 return us.repo.GetProfile(ctx, UserID) } +// ListUser 获取用户列表 func (us *userService) ListUser(ctx context.Context, pagination domain.Pagination) ([]domain.UserWithProfileAndRule, error) { // 计算偏移量 offset := int64(pagination.Page-1) * *pagination.Size pagination.Offset = &offset + // 从仓库中获取用户列表 return us.repo.ListUser(ctx, pagination) } +// GetUserCount 获取用户总数 func (us *userService) GetUserCount(ctx context.Context) (int64, error) { + // 从仓库中获取用户总数 count, err := us.repo.GetUserCount(ctx) if err != nil { return -1, err diff --git a/middleware/role.go b/middleware/role.go index c42cc95..0d6cc6a 100644 --- a/middleware/role.go +++ b/middleware/role.go @@ -7,18 +7,15 @@ import ( "github.com/casbin/casbin/v2" "github.com/gin-gonic/gin" - "go.uber.org/zap" ) type CasbinMiddleware struct { enforcer *casbin.Enforcer - logger *zap.Logger } -func NewCasbinMiddleware(enforcer *casbin.Enforcer, logger *zap.Logger) *CasbinMiddleware { +func NewCasbinMiddleware(enforcer *casbin.Enforcer) *CasbinMiddleware { return &CasbinMiddleware{ enforcer: enforcer, - logger: logger, } } @@ -51,13 +48,11 @@ func (cm *CasbinMiddleware) CheckCasbin() gin.HandlerFunc { // 使用 Casbin 检查权限 ok, err := cm.enforcer.Enforce(userIDStr, obj, act) if err != nil { - cm.logger.Error("Error occurred when enforcing policy", zap.Error(err), zap.String("userID", userIDStr), zap.String("path", obj), zap.String("method", act)) c.JSON(http.StatusInternalServerError, gin.H{"message": "Error occurred when enforcing policy"}) c.Abort() return } if !ok { - cm.logger.Warn("Access denied", zap.String("userID", userIDStr), zap.String("path", obj), zap.String("method", act)) c.JSON(http.StatusForbidden, gin.H{"message": "You don't have permission to access this resource"}) c.Abort() return diff --git a/utils/jwt/jwt.go b/utils/jwt/jwt.go index 68ec500..91c1736 100644 --- a/utils/jwt/jwt.go +++ b/utils/jwt/jwt.go @@ -13,14 +13,14 @@ import ( ) var ( - Key1 = []byte("ebe3vxIP7sblVvUHXb7ZaiMPuz4oXo0l") - Key2 = []byte("sadfkhjlkkljKFJDSLAFUDASLFJKLjfj113d2") - k = "K5mBPBYNQeNWEBvCTE5msog3KSGTdhmI" + Key1 = []byte("ebe3vxIP7sblVvUHXb7ZaiMPuz4oXo0l") + Key2 = []byte("sadfkhjlkkljKFJDSLAFUDASLFJKLjfj113d2") + IssUer = "K5mBPBYNQeNWEBvCTE5msog3KSGTdhmI" ) type Handler interface { - SetLoginToken(ctx *gin.Context, uid int64) error - SetJWTToken(ctx *gin.Context, uid int64, ssid string) error + SetLoginToken(ctx *gin.Context, uid int64) (string, error) + SetJWTToken(ctx *gin.Context, uid int64, ssid string) (string, error) ExtractToken(ctx *gin.Context) string CheckSession(ctx *gin.Context, ssid string) error ClearToken(ctx *gin.Context) error @@ -56,16 +56,16 @@ func NewJWTHandler(c redis.Cmdable) Handler { } // SetLoginToken 设置长短Token -func (h *handler) SetLoginToken(ctx *gin.Context, uid int64) error { +func (h *handler) SetLoginToken(ctx *gin.Context, uid int64) (string, error) { ssid := uuid.New().String() if err := h.setRefreshToken(ctx, uid, ssid); err != nil { - return err + return "", err } return h.SetJWTToken(ctx, uid, ssid) } // SetJWTToken 设置短Token -func (h *handler) SetJWTToken(ctx *gin.Context, uid int64, ssid string) error { +func (h *handler) SetJWTToken(ctx *gin.Context, uid int64, ssid string) (string, error) { uc := UserClaims{ Uid: uid, Ssid: ssid, @@ -73,17 +73,16 @@ func (h *handler) SetJWTToken(ctx *gin.Context, uid int64, ssid string) error { ContentType: ctx.GetHeader("Content-Type"), RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 30)), - Issuer: k, + Issuer: IssUer, }, } token := jwt.NewWithClaims(h.signingMethod, uc) // 进行签名 signedString, err := token.SignedString(Key1) if err != nil { - return err + return "", err } - ctx.Header("X-JWT-Token", signedString) - return nil + return signedString, nil } // setRefreshToken 设置长Token @@ -135,10 +134,8 @@ func (h *handler) CheckSession(ctx *gin.Context, ssid string) error { // ClearToken 清空token func (h *handler) ClearToken(ctx *gin.Context) error { ctx.Header("X-Refresh-Token", "") - ctx.Header("X-JWT-Token", "") uc := ctx.MustGet("user").(UserClaims) // 获取 refresh token - //refreshTokenString := h.ExtractToken(ctx) refreshTokenString := ctx.GetHeader("X-Refresh-Token") if refreshTokenString == "" { return errors.New("missing refresh token")