From e17f9f2d1c1f80819dcd39675692b8a985e46bd3 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 21 Jul 2024 15:32:26 +0800
Subject: [PATCH 01/16] feat: add PersonalSignature field, UserActivities table
and several converter functions
---
.idea/.gitignore | 8 ++++++
.idea/jcourse_go.iml | 9 ++++++
.idea/modules.xml | 8 ++++++
.idea/vcs.xml | 6 ++++
model/converter/user.go | 61 ++++++++++++++++++++++++++++++++++++-----
model/domain/user.go | 21 +++++++++-----
model/dto/user.go | 50 +++++++++++++++++++++++++++++++++
model/po/user.go | 25 ++++++++++++-----
8 files changed, 167 insertions(+), 21 deletions(-)
create mode 100644 .idea/.gitignore
create mode 100644 .idea/jcourse_go.iml
create mode 100644 .idea/modules.xml
create mode 100644 .idea/vcs.xml
create mode 100644 model/dto/user.go
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/jcourse_go.iml b/.idea/jcourse_go.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/jcourse_go.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..89f009c
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/model/converter/user.go b/model/converter/user.go
index f16f5dc..9d8f2c5 100644
--- a/model/converter/user.go
+++ b/model/converter/user.go
@@ -19,13 +19,14 @@ func ConvertUserPOToDomain(userPO po.UserPO) domain.User {
func ConvertUserProfilePOToDomain(userProfile po.UserProfilePO) domain.UserProfile {
return domain.UserProfile{
- UserID: userProfile.UserID,
- Avatar: userProfile.Avatar,
- Department: userProfile.Department,
- Type: userProfile.Type,
- Major: userProfile.Major,
- Degree: userProfile.Degree,
- Grade: userProfile.Grade,
+ UserID: userProfile.UserID,
+ Avatar: userProfile.Avatar,
+ Department: userProfile.Department,
+ Type: userProfile.Type,
+ Major: userProfile.Major,
+ Degree: userProfile.Degree,
+ Grade: userProfile.Grade,
+ PersonalSignature: userProfile.PersonalSignature,
}
}
@@ -44,3 +45,49 @@ func ConvertUserDomainToReviewDTO(user domain.User) dto.UserInReviewDTO {
Avatar: user.Profile.Avatar,
}
}
+
+func ConvertToUserSummaryDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserSummaryDTO {
+ if userPO == nil {
+ return nil
+ }
+ return &dto.UserSummaryDTO{
+ ID: int64(userPO.ID),
+ Username: userPO.Username,
+ Avatar: userProfilePO.Avatar,
+ Role: userPO.UserRole,
+ }
+}
+
+func ConvertToUserDetailsDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserDetailsDTO {
+ if userPO == nil {
+ return nil
+ }
+ return &dto.UserDetailsDTO{
+ ID: int64(userPO.ID),
+ Username: userPO.Username,
+ Role: userPO.UserRole,
+ LastSeenAt: userPO.LastSeenAt,
+ Type: userPO.UserRole,
+ Avatar: userProfilePO.Avatar,
+ PersonalSignature: userProfilePO.PersonalSignature,
+ }
+}
+
+func ConvertToUserProfileDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserProfileDTO {
+ if userPO == nil {
+ return nil
+ }
+ return &dto.UserProfileDTO{
+ ID: int64(userPO.ID),
+ UserID: userProfilePO.UserID,
+ Avatar: userProfilePO.Avatar,
+ Department: userProfilePO.Department,
+ Type: userProfilePO.Type,
+ Major: userProfilePO.Major,
+ Degree: userProfilePO.Degree,
+ Grade: userProfilePO.Grade,
+ PersonalSignature: userProfilePO.PersonalSignature,
+ Username: userPO.Username,
+ Role: userPO.UserRole,
+ }
+}
diff --git a/model/domain/user.go b/model/domain/user.go
index 1cc3c1f..f0f97f6 100644
--- a/model/domain/user.go
+++ b/model/domain/user.go
@@ -28,11 +28,18 @@ type User struct {
}
type UserProfile struct {
- UserID int64
- Avatar string
- Department string
- Type UserType // 用户在学校的身份
- Major string
- Degree string
- Grade string
+ UserID int64
+ Avatar string
+ Department string
+ Type UserType // 用户在学校的身份
+ Major string
+ Degree string
+ Grade string
+ PersonalSignature string
+}
+
+type UserFilter struct {
+ Page int64
+ PageSize int64
+ // To be continued ... (add more fields)
}
diff --git a/model/dto/user.go b/model/dto/user.go
new file mode 100644
index 0000000..b0e8ca4
--- /dev/null
+++ b/model/dto/user.go
@@ -0,0 +1,50 @@
+package dto
+
+import (
+ "time"
+)
+
+type UserRole = string
+
+type UserType = string
+
+type UserListRequest struct {
+ Page int64 `json:"page" form:"page"`
+ PageSize int64 `json:"page_size" form:"page_size"`
+}
+
+type UserListResponse = BasePaginateResponse[UserSummaryDTO]
+
+// 用户概要信息(用于用户列表)
+type UserSummaryDTO struct {
+ ID int64 `json:"id"`
+ Username string `json:"username"`
+ Avatar string `json:"avatar"`
+ Role UserRole `json:"user_role"`
+}
+
+// 用户详细信息(用于用户详情页面)
+type UserDetailsDTO struct {
+ ID int64 `json:"id"`
+ Username string `json:"username"`
+ Role string `json:"user_role"`
+ LastSeenAt time.Time `json:"lastSeenAt"`
+ Type string `json:"user_type"`
+ Avatar string `json:"avatar"`
+ PersonalSignature string `json:"personal_signature"`
+}
+
+// 用户个人资料信息(用于个人资料页面)
+type UserProfileDTO struct {
+ ID int64 `json:"id"`
+ UserID int64 `json:"user_id"`
+ Avatar string `json:"avatar"`
+ Department string `json:"department"`
+ Type string `json:"type"`
+ Major string `json:"major"`
+ Degree string `json:"degree"`
+ Grade string `json:"grade"`
+ PersonalSignature string `json:"personal_signature"`
+ Username string `json:"username"`
+ Role string `json:"user_role"`
+}
diff --git a/model/po/user.go b/model/po/user.go
index 02b8610..2ed27db 100644
--- a/model/po/user.go
+++ b/model/po/user.go
@@ -21,15 +21,26 @@ func (po *UserPO) TableName() string {
type UserProfilePO struct {
gorm.Model
- UserID int64
- Avatar string
- Department string
- Type string // 用户在学校的身份
- Major string
- Degree string
- Grade string
+ UserID int64
+ Avatar string
+ Department string
+ Type string // 用户在学校的身份
+ Major string
+ Degree string
+ Grade string
+ PersonalSignature string
}
func (profile *UserProfilePO) TableName() string {
return "user_profiles"
}
+
+type UserActivityPO struct {
+ gorm.Model
+ UserID int64 // 用户ID
+ ActivityType string // 活动类型,如发布课程点评、点赞、回复、关注/屏蔽用户/课程等。
+ TargetID string // 活动对象ID
+ CreatedAt time.Time // 活动发生时间
+}
+
+func (userActivity *UserActivityPO) TableName() string { return "user_activities" }
From db26179f94c8c993aa19abee1a8a5740921bc361 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 21 Jul 2024 15:43:44 +0800
Subject: [PATCH 02/16] feat(user): add pagination and retrieval functions for
User and UserProfile
---
repository/user.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/repository/user.go b/repository/user.go
index f3d4d9c..dffc0c8 100644
--- a/repository/user.go
+++ b/repository/user.go
@@ -17,16 +17,27 @@ type DBOption func(*gorm.DB) *gorm.DB
type IUserQuery interface {
GetUserDetail(ctx context.Context, opts ...DBOption) (*po.UserPO, error)
GetUserList(ctx context.Context, opts ...DBOption) ([]po.UserPO, error)
+ GetUserCount(ctx context.Context, opts ...DBOption) (int64, error)
+ GetUserByID(ctx context.Context, userID int64) (*po.UserPO, error)
GetUserByIDs(ctx context.Context, userIDs []int64) (map[int64]po.UserPO, error)
WithID(id int64) DBOption
WithEmail(email string) DBOption
WithPassword(password string) DBOption
CreateUser(ctx context.Context, email string, password string) (*po.UserPO, error)
ResetUserPassword(ctx context.Context, userID int64, password string) error
+ WithLimit(limit int64) DBOption
+ WithOffset(offset int64) DBOption
}
type IUserProfileQuery interface {
GetUserProfileByIDs(ctx context.Context, userIDs []int64) (map[int64]po.UserProfilePO, error)
+ GetUserProfileByID(ctx context.Context, userID int64) (*po.UserProfilePO, error)
+ GetUserProfileList(ctx context.Context, opts ...DBOption) ([]po.UserProfilePO, error)
+ GetUserProfileCount(ctx context.Context, opts ...DBOption) (int64, error)
+ WithUserID(id int64) DBOption
+ WithLimit(limit int64) DBOption
+ WithOffset(offset int64) DBOption
+ //CreateUserProfile(ctx context.Context, userID int64) (*po.UserProfilePO, error)
}
type UserProfileQuery struct {
@@ -47,6 +58,16 @@ func (u *UserProfileQuery) GetUserProfileByIDs(ctx context.Context, userIDs []in
return userProfileMap, nil
}
+func (u *UserProfileQuery) GetUserProfileByID(ctx context.Context, userID int64) (*po.UserProfilePO, error) {
+ db := u.optionDB(ctx, u.WithUserID(userID))
+ userProfilePO := po.UserProfilePO{}
+ result := db.Find(&userProfilePO)
+ if result.Error != nil {
+ return &userProfilePO, result.Error
+ }
+ return &userProfilePO, nil
+}
+
func (u *UserProfileQuery) optionDB(ctx context.Context, opts ...DBOption) *gorm.DB {
db := u.db.WithContext(ctx).Model(&po.UserProfilePO{})
for _, opt := range opts {
@@ -61,6 +82,22 @@ func (u *UserProfileQuery) WithUserIDs(userIDs []int64) DBOption {
}
}
+func (q *UserProfileQuery) WithUserID(id int64) DBOption {
+ return func(db *gorm.DB) *gorm.DB {
+ return db.Where("user_id = ?", id)
+ }
+}
+
+func (q *UserProfileQuery) WithLimit(limit int64) DBOption {
+ return func(db *gorm.DB) *gorm.DB { return db.Limit(int(limit)) }
+}
+
+func (q *UserProfileQuery) WithOffset(offset int64) DBOption {
+ return func(db *gorm.DB) *gorm.DB {
+ return db.Offset(int(offset))
+ }
+}
+
func NewUserProfileQuery() IUserProfileQuery {
return &UserProfileQuery{db: dal.GetDBClient()}
}
@@ -71,6 +108,26 @@ func NewUserQuery() IUserQuery {
}
}
+func (q *UserProfileQuery) GetUserProfileList(ctx context.Context, opts ...DBOption) ([]po.UserProfilePO, error) {
+ db := q.optionDB(ctx, opts...)
+ userProfilePOs := make([]po.UserProfilePO, 0)
+ result := db.Find(&userProfilePOs)
+ if result.Error != nil {
+ return userProfilePOs, result.Error
+ }
+ return userProfilePOs, nil
+}
+
+func (q *UserProfileQuery) GetUserProfileCount(ctx context.Context, opts ...DBOption) (int64, error) {
+ db := q.optionDB(ctx, opts...)
+ count := int64(0)
+ result := db.Count(&count)
+ if result.Error != nil {
+ return 0, result.Error
+ }
+ return count, nil
+}
+
type UserQuery struct {
db *gorm.DB
}
@@ -89,6 +146,16 @@ func (q *UserQuery) GetUserByIDs(ctx context.Context, userIDs []int64) (map[int6
return userMap, nil
}
+func (q *UserQuery) GetUserByID(ctx context.Context, userID int64) (*po.UserPO, error) {
+ db := q.optionDB(ctx, q.WithID(userID))
+ userPO := po.UserPO{}
+ result := db.Find(&userPO)
+ if result.Error != nil {
+ return &userPO, result.Error
+ }
+ return &userPO, nil
+}
+
func (q *UserQuery) WithEmail(email string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("email = ?", email)
@@ -138,6 +205,16 @@ func (q *UserQuery) GetUserList(ctx context.Context, opts ...DBOption) ([]po.Use
return userPOs, nil
}
+func (q *UserQuery) GetUserCount(ctx context.Context, opts ...DBOption) (int64, error) {
+ db := q.optionDB(ctx, opts...)
+ count := int64(0)
+ result := db.Count(&count)
+ if result.Error != nil {
+ return 0, result.Error
+ }
+ return count, nil
+}
+
func (q *UserQuery) CreateUser(ctx context.Context, email string, passwordStore string) (*po.UserPO, error) {
user := po.UserPO{
Username: email,
@@ -157,3 +234,13 @@ func (q *UserQuery) ResetUserPassword(ctx context.Context, userID int64, passwor
result := q.optionDB(ctx, q.WithID(userID)).Debug().Update("password", passwordStore)
return result.Error
}
+
+func (q *UserQuery) WithLimit(limit int64) DBOption {
+ return func(db *gorm.DB) *gorm.DB { return db.Limit(int(limit)) }
+}
+
+func (q *UserQuery) WithOffset(offset int64) DBOption {
+ return func(db *gorm.DB) *gorm.DB {
+ return db.Offset(int(offset))
+ }
+}
From ac5a5d8f7f545ba58bb62d31b4210177bf4c295d Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 21 Jul 2024 15:51:53 +0800
Subject: [PATCH 03/16] feat: add /:userID/reviews, /:userID/watch and /me/info
routes in userGroup in route.go
---
handler/user.go | 4 ++++
router.go | 3 +++
2 files changed, 7 insertions(+)
diff --git a/handler/user.go b/handler/user.go
index 5360778..e7fe019 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -13,3 +13,7 @@ func GetUserDetailHandler(c *gin.Context) {}
func WatchUserHandler(c *gin.Context) {}
func UnWatchUserHandler(c *gin.Context) {}
+
+func UpdateUserInfoHandler(c *gin.Context) {}
+
+func GetUserReviewsHandler(c *gin.Context) {}
diff --git a/router.go b/router.go
index eb820f8..de89460 100644
--- a/router.go
+++ b/router.go
@@ -53,7 +53,10 @@ func registerRouter(r *gin.Engine) {
userGroup.GET("/suggest", handler.GetSuggestedUserHandler)
userGroup.GET("/me", handler.GetCurrentUserHandler)
userGroup.GET("/:userID", handler.GetUserDetailHandler)
+ userGroup.GET("/:userID/reviews", handler.GetUserReviewsHandler)
+ userGroup.POST("/:userID/watch", handler.WatchUserHandler)
userGroup.POST("/:userID/unwatch", handler.UnWatchUserHandler)
+ userGroup.PATCH("/me/info", handler.UpdateUserInfoHandler)
adminGroup := needAuthGroup.Group("/admin")
adminGroup.Use(middleware.RequireAdmin())
From b7bedcd71be95385e36812f8ef0c49ad5296c11f Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 21 Jul 2024 15:57:34 +0800
Subject: [PATCH 04/16] feat: achieve GetUserList and GetUserCount functions,
build UserDBoption for filter in \service\user.go
---
service/user.go | 109 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 109 insertions(+)
diff --git a/service/user.go b/service/user.go
index fb21f77..6049701 100644
--- a/service/user.go
+++ b/service/user.go
@@ -2,6 +2,8 @@ package service
import (
"context"
+ "jcourse_go/model/dto"
+ "jcourse_go/util"
"jcourse_go/model/converter"
"jcourse_go/model/domain"
@@ -36,3 +38,110 @@ func GetUserByIDs(ctx context.Context, userIDs []int64) (map[int64]domain.User,
}
return result, nil
}
+
+func GetUserSummaryByID(ctx context.Context, userID int64) (*dto.UserSummaryDTO, error) {
+ userQuery := repository.NewUserQuery()
+ userPO, err := userQuery.GetUserByID(ctx, userID)
+ if err != nil {
+ return nil, err
+ }
+
+ userProfileQuery := repository.NewUserProfileQuery()
+ userProfilePO, err := userProfileQuery.GetUserProfileByID(ctx, userID)
+ if err != nil {
+ return nil, err
+ }
+ userSummary := converter.ConvertToUserSummaryDTO(userPO, userProfilePO)
+ return userSummary, nil
+}
+
+func GetUserDetailsByID(ctx context.Context, userID int64) (*dto.UserDetailsDTO, error) {
+ userQuery := repository.NewUserQuery()
+ userPO, err := userQuery.GetUserByID(ctx, userID)
+ if err != nil {
+ return nil, err
+ }
+
+ userProfileQuery := repository.NewUserProfileQuery()
+ userProfilePO, err := userProfileQuery.GetUserProfileByID(ctx, userID)
+ if err != nil {
+ return nil, err
+ }
+ userDetails := converter.ConvertToUserDetailsDTO(userPO, userProfilePO)
+ return userDetails, nil
+}
+
+func GetUserProfileByID(ctx context.Context, userID int64) (*dto.UserProfileDTO, error) {
+ userQuery := repository.NewUserQuery()
+ userPO, err := userQuery.GetUserByID(ctx, userID)
+ if err != nil {
+ return nil, err
+ }
+
+ userProfileQuery := repository.NewUserProfileQuery()
+ userProfilePO, err := userProfileQuery.GetUserProfileByID(ctx, userID)
+ if err != nil {
+ return nil, err
+ }
+ userProfile := converter.ConvertToUserProfileDTO(userPO, userProfilePO)
+ return userProfile, nil
+}
+
+func buildUserDBOptionFromFilter(query repository.IUserQuery, filter domain.UserFilter) []repository.DBOption {
+ opts := make([]repository.DBOption, 0)
+ if filter.PageSize > 0 {
+ opts = append(opts, query.WithLimit(filter.PageSize))
+ }
+ if filter.Page > 0 {
+ opts = append(opts, query.WithOffset(util.CalcOffset(filter.Page, filter.PageSize)))
+ }
+ return opts
+}
+
+func GetUserList(ctx context.Context, filter domain.UserFilter) ([]dto.UserSummaryDTO, error) {
+ userQuery := repository.NewUserQuery()
+ userProfileQuery := repository.NewUserProfileQuery()
+ opts := buildUserDBOptionFromFilter(userQuery, filter)
+ userPOs, err := userQuery.GetUserList(ctx, opts...)
+ if err != nil {
+ return nil, err
+ }
+ userProfilePOs, err := userProfileQuery.GetUserProfileList(ctx, opts...)
+ if err != nil {
+ return nil, err
+ }
+ result := make([]dto.UserSummaryDTO, 0)
+
+ userProfileMap := make(map[int]string)
+ for _, profile := range userProfilePOs {
+ userProfileMap[int(profile.UserID)] = profile.Avatar
+ }
+
+ for _, userPO := range userPOs {
+ if avatar, exists := userProfileMap[int(userPO.ID)]; exists {
+ userSummaryDTO := dto.UserSummaryDTO{
+ ID: int64(userPO.ID),
+ Username: userPO.Username,
+ Role: userPO.UserRole,
+ Avatar: avatar,
+ }
+ result = append(result, userSummaryDTO)
+ } else {
+ userSummaryDTO := dto.UserSummaryDTO{
+ ID: int64(userPO.ID),
+ Username: userPO.Username,
+ Role: userPO.UserRole,
+ Avatar: "",
+ }
+ result = append(result, userSummaryDTO)
+ }
+ }
+ return result, nil
+}
+
+func GetUserCount(ctx context.Context, filter domain.UserFilter) (int64, error) {
+ userQuery := repository.NewUserQuery()
+ filter.Page, filter.PageSize = 0, 0
+ opts := buildUserDBOptionFromFilter(userQuery, filter)
+ return userQuery.GetUserCount(ctx, opts...)
+}
From cc287d8927c6aa24c102eb2dc72656aeaa8a26ab Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 21 Jul 2024 16:08:07 +0800
Subject: [PATCH 05/16] feat: implement GetUserListHandler,
GetCurrentUserHandler, GetUserDetailHandler and GetUserViewsHandler in
\handler\user.go which are partially declared in commit bcba52e
---
handler/user.go | 109 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 104 insertions(+), 5 deletions(-)
diff --git a/handler/user.go b/handler/user.go
index e7fe019..002c1f8 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -1,14 +1,77 @@
package handler
-import "github.com/gin-gonic/gin"
+import (
+ "github.com/gin-gonic/gin"
+ "jcourse_go/constant"
+ "jcourse_go/model/converter"
+ "jcourse_go/model/domain"
+ "jcourse_go/model/dto"
+ "jcourse_go/service"
+ "net/http"
+ "strconv"
+)
func GetSuggestedUserHandler(c *gin.Context) {}
-func GetUserListHandler(c *gin.Context) {}
+func GetUserListHandler(c *gin.Context) {
+ /*
+ 管理员权限验证
+ */
+ var request dto.UserListRequest
+ if err := c.ShouldBind(&request); err != nil {
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "参数错误"})
+ return
+ }
-func GetCurrentUserHandler(c *gin.Context) {}
+ filter := domain.UserFilter{
+ Page: request.Page,
+ PageSize: request.PageSize,
+ }
+ users, err := service.GetUserList(c, filter)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "内部错误。"})
+ }
+ total, _ := service.GetUserCount(c, filter)
+ response := dto.UserListResponse{
+ Page: request.Page,
+ PageSize: request.PageSize,
+ Total: total,
+ Data: users,
+ }
+ c.JSON(http.StatusOK, response)
+}
-func GetUserDetailHandler(c *gin.Context) {}
+func GetCurrentUserHandler(c *gin.Context) {
+ userInterface, exists := c.Get(constant.CtxKeyUser)
+ if !exists {
+ c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
+ return
+ }
+ user, _ := userInterface.(*domain.User)
+
+ me, err := service.GetUserDetailsByID(c, user.ID)
+ if err != nil {
+ c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
+ return
+ }
+ c.JSON(http.StatusOK, me)
+ return
+}
+
+func GetUserDetailHandler(c *gin.Context) {
+ userIDStr := c.Param("userID")
+ userID, err := strconv.Atoi(userIDStr)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "非法用户ID"})
+ return
+ }
+
+ userDetail, errDetail := service.GetUserDetailsByID(c, int64(userID))
+ if errDetail != nil {
+ c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
+ }
+ c.JSON(http.StatusOK, userDetail)
+}
func WatchUserHandler(c *gin.Context) {}
@@ -16,4 +79,40 @@ func UnWatchUserHandler(c *gin.Context) {}
func UpdateUserInfoHandler(c *gin.Context) {}
-func GetUserReviewsHandler(c *gin.Context) {}
+func GetUserReviewsHandler(c *gin.Context) {
+ userIDStr := c.Param("userID")
+
+ userID, err := strconv.Atoi(userIDStr)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "非法用户ID"})
+ return
+ }
+
+ var request dto.ReviewListRequest
+ if err := c.ShouldBind(&request); err != nil {
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "参数错误"})
+ return
+ }
+
+ filter := domain.ReviewFilter{
+ Page: request.Page,
+ PageSize: request.PageSize,
+ UserID: int64(userID),
+ }
+
+ reviews, err := service.GetReviewList(c, filter)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "内部错误。"})
+ return
+ }
+
+ total, err := service.GetReviewCount(c, filter)
+
+ response := dto.ReviewListResponse{
+ Page: request.Page,
+ PageSize: request.PageSize,
+ Total: total,
+ Data: converter.ConvertReviewDomainToListDTO(reviews, true),
+ }
+ c.JSON(http.StatusOK, response)
+}
From 65774d8c8f18dcb650502bc57e3c26bceea61251 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Tue, 6 Aug 2024 22:37:43 +0800
Subject: [PATCH 06/16] Reorganize routes in router.go and update model fields
in dto/user.go according to jcourse_v2_frontend model.ts
---
router.go | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/router.go b/router.go
index de89460..2bec19d 100644
--- a/router.go
+++ b/router.go
@@ -51,12 +51,13 @@ func registerRouter(r *gin.Engine) {
userGroup := needAuthGroup.Group("/user")
userGroup.GET("", handler.GetUserListHandler)
userGroup.GET("/suggest", handler.GetSuggestedUserHandler)
- userGroup.GET("/me", handler.GetCurrentUserHandler)
- userGroup.GET("/:userID", handler.GetUserDetailHandler)
+ userGroup.GET("/me", handler.GetCurrentUserSummaryHandler)
+ userGroup.GET("/:userID/detail", handler.GetUserDetailHandler)
userGroup.GET("/:userID/reviews", handler.GetUserReviewsHandler)
userGroup.POST("/:userID/watch", handler.WatchUserHandler)
userGroup.POST("/:userID/unwatch", handler.UnWatchUserHandler)
- userGroup.PATCH("/me/info", handler.UpdateUserInfoHandler)
+ userGroup.GET("/me/profile", handler.GetCurrentUserProfileHandler)
+ userGroup.PUT("/me/profile", handler.UpdateUserProfileHandler)
adminGroup := needAuthGroup.Group("/admin")
adminGroup.Use(middleware.RequireAdmin())
From 1db812015d49507ccdcb2b9b5cec72af81d70b63 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Tue, 6 Aug 2024 22:40:51 +0800
Subject: [PATCH 07/16] Update model fields in dto/user.go according to
jcourse_v2_frontend model.ts
---
model/dto/user.go | 52 +++++++++++++++++++----------------------------
1 file changed, 21 insertions(+), 31 deletions(-)
diff --git a/model/dto/user.go b/model/dto/user.go
index b0e8ca4..30f7979 100644
--- a/model/dto/user.go
+++ b/model/dto/user.go
@@ -1,9 +1,5 @@
package dto
-import (
- "time"
-)
-
type UserRole = string
type UserType = string
@@ -13,38 +9,32 @@ type UserListRequest struct {
PageSize int64 `json:"page_size" form:"page_size"`
}
-type UserListResponse = BasePaginateResponse[UserSummaryDTO]
+type UserListResponse = BasePaginateResponse[UserDetailDTO]
-// 用户概要信息(用于用户列表)
type UserSummaryDTO struct {
- ID int64 `json:"id"`
- Username string `json:"username"`
- Avatar string `json:"avatar"`
- Role UserRole `json:"user_role"`
+ ID int64 `json:"id"`
+ ReviewCount int64 `json:"review_count"`
+ LikeReceive int64 `json:"like_receive"`
+ TipReceive int64 `json:"tip_receive"`
+ FollowingCourseCount int64 `json:"following_course_count"`
}
-// 用户详细信息(用于用户详情页面)
-type UserDetailsDTO struct {
- ID int64 `json:"id"`
- Username string `json:"username"`
- Role string `json:"user_role"`
- LastSeenAt time.Time `json:"lastSeenAt"`
- Type string `json:"user_type"`
- Avatar string `json:"avatar"`
- PersonalSignature string `json:"personal_signature"`
+type UserDetailDTO struct {
+ ID int64 `json:"id"`
+ Username string `json:"username"`
+ Avatar string `json:"avatar"`
+ Bio string `json:"bio"`
}
-// 用户个人资料信息(用于个人资料页面)
type UserProfileDTO struct {
- ID int64 `json:"id"`
- UserID int64 `json:"user_id"`
- Avatar string `json:"avatar"`
- Department string `json:"department"`
- Type string `json:"type"`
- Major string `json:"major"`
- Degree string `json:"degree"`
- Grade string `json:"grade"`
- PersonalSignature string `json:"personal_signature"`
- Username string `json:"username"`
- Role string `json:"user_role"`
+ ID int64 `json:"id"`
+ UserID int64 `json:"user_id"`
+ Username string `json:"username"`
+ Bio string `json:"bio"`
+ Email string `json:"email"`
+ Avatar string `json:"avatar"`
+ Role string `json:"user_role"`
+ Department string `json:"department"`
+ Major string `json:"major"`
+ Grade string `json:"grade"`
}
From d6fce215a1b14768b89d872f5bf1645b3018ac00 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Tue, 6 Aug 2024 22:43:05 +0800
Subject: [PATCH 08/16] Related changes in model for commit d6d1d1d
---
model/converter/user.go | 58 +++++++++++++++--------------------------
model/domain/user.go | 16 ++++++------
model/po/user.go | 16 ++++++------
3 files changed, 37 insertions(+), 53 deletions(-)
diff --git a/model/converter/user.go b/model/converter/user.go
index 9d8f2c5..2f46936 100644
--- a/model/converter/user.go
+++ b/model/converter/user.go
@@ -19,14 +19,14 @@ func ConvertUserPOToDomain(userPO po.UserPO) domain.User {
func ConvertUserProfilePOToDomain(userProfile po.UserProfilePO) domain.UserProfile {
return domain.UserProfile{
- UserID: userProfile.UserID,
- Avatar: userProfile.Avatar,
- Department: userProfile.Department,
- Type: userProfile.Type,
- Major: userProfile.Major,
- Degree: userProfile.Degree,
- Grade: userProfile.Grade,
- PersonalSignature: userProfile.PersonalSignature,
+ UserID: userProfile.UserID,
+ Avatar: userProfile.Avatar,
+ Department: userProfile.Department,
+ Type: userProfile.Type,
+ Major: userProfile.Major,
+ Degree: userProfile.Degree,
+ Grade: userProfile.Grade,
+ Bio: userProfile.Bio,
}
}
@@ -46,30 +46,15 @@ func ConvertUserDomainToReviewDTO(user domain.User) dto.UserInReviewDTO {
}
}
-func ConvertToUserSummaryDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserSummaryDTO {
+func ConvertToUserDetailDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserDetailDTO {
if userPO == nil {
return nil
}
- return &dto.UserSummaryDTO{
+ return &dto.UserDetailDTO{
ID: int64(userPO.ID),
Username: userPO.Username,
Avatar: userProfilePO.Avatar,
- Role: userPO.UserRole,
- }
-}
-
-func ConvertToUserDetailsDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserDetailsDTO {
- if userPO == nil {
- return nil
- }
- return &dto.UserDetailsDTO{
- ID: int64(userPO.ID),
- Username: userPO.Username,
- Role: userPO.UserRole,
- LastSeenAt: userPO.LastSeenAt,
- Type: userPO.UserRole,
- Avatar: userProfilePO.Avatar,
- PersonalSignature: userProfilePO.PersonalSignature,
+ Bio: userProfilePO.Bio,
}
}
@@ -78,16 +63,15 @@ func ConvertToUserProfileDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO)
return nil
}
return &dto.UserProfileDTO{
- ID: int64(userPO.ID),
- UserID: userProfilePO.UserID,
- Avatar: userProfilePO.Avatar,
- Department: userProfilePO.Department,
- Type: userProfilePO.Type,
- Major: userProfilePO.Major,
- Degree: userProfilePO.Degree,
- Grade: userProfilePO.Grade,
- PersonalSignature: userProfilePO.PersonalSignature,
- Username: userPO.Username,
- Role: userPO.UserRole,
+ ID: int64(userPO.ID),
+ UserID: userProfilePO.UserID,
+ Username: userPO.Username,
+ Bio: userProfilePO.Bio,
+ Email: userPO.Email,
+ Avatar: userProfilePO.Avatar,
+ Role: userPO.UserRole,
+ Department: userProfilePO.Department,
+ Major: userProfilePO.Major,
+ Grade: userProfilePO.Grade,
}
}
diff --git a/model/domain/user.go b/model/domain/user.go
index f0f97f6..102c2ed 100644
--- a/model/domain/user.go
+++ b/model/domain/user.go
@@ -28,14 +28,14 @@ type User struct {
}
type UserProfile struct {
- UserID int64
- Avatar string
- Department string
- Type UserType // 用户在学校的身份
- Major string
- Degree string
- Grade string
- PersonalSignature string
+ UserID int64
+ Avatar string
+ Department string
+ Type UserType // 用户在学校的身份
+ Major string
+ Degree string
+ Grade string
+ Bio string
}
type UserFilter struct {
diff --git a/model/po/user.go b/model/po/user.go
index 2ed27db..8301320 100644
--- a/model/po/user.go
+++ b/model/po/user.go
@@ -21,14 +21,14 @@ func (po *UserPO) TableName() string {
type UserProfilePO struct {
gorm.Model
- UserID int64
- Avatar string
- Department string
- Type string // 用户在学校的身份
- Major string
- Degree string
- Grade string
- PersonalSignature string
+ UserID int64
+ Avatar string
+ Department string
+ Type string // 用户在学校的身份
+ Major string
+ Degree string
+ Grade string
+ Bio string
}
func (profile *UserProfilePO) TableName() string {
From e14ec8f835ff352f1671c26cdda723b4ea8eeadd Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Tue, 6 Aug 2024 22:45:31 +0800
Subject: [PATCH 09/16] Refined/Implemented several handlers in d6d1d1d
---
handler/user.go | 33 ++++++++++++++++++-------
service/user.go | 64 ++++++++++++++++++++++++++++++-------------------
2 files changed, 64 insertions(+), 33 deletions(-)
diff --git a/handler/user.go b/handler/user.go
index 002c1f8..f12148c 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -14,11 +14,11 @@ import (
func GetSuggestedUserHandler(c *gin.Context) {}
func GetUserListHandler(c *gin.Context) {
- /*
- 管理员权限验证
- */
+
+ // 管理员权限验证
+
var request dto.UserListRequest
- if err := c.ShouldBind(&request); err != nil {
+ if err := c.ShouldBindQuery(&request); err != nil {
c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "参数错误"})
return
}
@@ -41,7 +41,7 @@ func GetUserListHandler(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
-func GetCurrentUserHandler(c *gin.Context) {
+func GetCurrentUserSummaryHandler(c *gin.Context) {
userInterface, exists := c.Get(constant.CtxKeyUser)
if !exists {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
@@ -49,13 +49,12 @@ func GetCurrentUserHandler(c *gin.Context) {
}
user, _ := userInterface.(*domain.User)
- me, err := service.GetUserDetailsByID(c, user.ID)
+ me, err := service.GetUserSummaryByID(c, user.ID)
if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
return
}
c.JSON(http.StatusOK, me)
- return
}
func GetUserDetailHandler(c *gin.Context) {
@@ -66,18 +65,34 @@ func GetUserDetailHandler(c *gin.Context) {
return
}
- userDetail, errDetail := service.GetUserDetailsByID(c, int64(userID))
+ userDetail, errDetail := service.GetUserDetailByID(c, int64(userID))
if errDetail != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
}
c.JSON(http.StatusOK, userDetail)
}
+func GetCurrentUserProfileHandler(c *gin.Context) {
+ userInterface, exists := c.Get(constant.CtxKeyUser)
+ if !exists {
+ c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
+ return
+ }
+
+ user, _ := userInterface.(*domain.User)
+ me, err := service.GetUserProfileByID(c, user.ID)
+ if err != nil {
+ c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
+ return
+ }
+ c.JSON(http.StatusOK, me)
+}
+
func WatchUserHandler(c *gin.Context) {}
func UnWatchUserHandler(c *gin.Context) {}
-func UpdateUserInfoHandler(c *gin.Context) {}
+func UpdateUserProfileHandler(c *gin.Context) {}
func GetUserReviewsHandler(c *gin.Context) {
userIDStr := c.Param("userID")
diff --git a/service/user.go b/service/user.go
index 6049701..de47127 100644
--- a/service/user.go
+++ b/service/user.go
@@ -3,6 +3,7 @@ package service
import (
"context"
"jcourse_go/model/dto"
+ "jcourse_go/model/po"
"jcourse_go/util"
"jcourse_go/model/converter"
@@ -10,6 +11,27 @@ import (
"jcourse_go/repository"
)
+// 该函数如果放在converter包中会报错import cycle is not allowed
+// UserSummaryDTO的组装需要借助service/review.go中的GetReviewCount函数
+func ConvertToUserSummaryDTO(ctx context.Context, userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserSummaryDTO {
+ if userPO == nil {
+ return nil
+ }
+
+ filter := domain.ReviewFilter{
+ UserID: int64(userPO.ID),
+ }
+
+ total, _ := GetReviewCount(ctx, filter)
+
+ return &dto.UserSummaryDTO{
+ ID: int64(userPO.ID),
+ ReviewCount: total,
+ TipReceive: 0,
+ FollowingCourseCount: 0,
+ }
+}
+
func GetUserByIDs(ctx context.Context, userIDs []int64) (map[int64]domain.User, error) {
result := make(map[int64]domain.User)
if len(userIDs) == 0 {
@@ -51,11 +73,11 @@ func GetUserSummaryByID(ctx context.Context, userID int64) (*dto.UserSummaryDTO,
if err != nil {
return nil, err
}
- userSummary := converter.ConvertToUserSummaryDTO(userPO, userProfilePO)
+ userSummary := ConvertToUserSummaryDTO(ctx, userPO, userProfilePO)
return userSummary, nil
}
-func GetUserDetailsByID(ctx context.Context, userID int64) (*dto.UserDetailsDTO, error) {
+func GetUserDetailByID(ctx context.Context, userID int64) (*dto.UserDetailDTO, error) {
userQuery := repository.NewUserQuery()
userPO, err := userQuery.GetUserByID(ctx, userID)
if err != nil {
@@ -67,7 +89,7 @@ func GetUserDetailsByID(ctx context.Context, userID int64) (*dto.UserDetailsDTO,
if err != nil {
return nil, err
}
- userDetails := converter.ConvertToUserDetailsDTO(userPO, userProfilePO)
+ userDetails := converter.ConvertToUserDetailDTO(userPO, userProfilePO)
return userDetails, nil
}
@@ -98,7 +120,7 @@ func buildUserDBOptionFromFilter(query repository.IUserQuery, filter domain.User
return opts
}
-func GetUserList(ctx context.Context, filter domain.UserFilter) ([]dto.UserSummaryDTO, error) {
+func GetUserList(ctx context.Context, filter domain.UserFilter) ([]dto.UserDetailDTO, error) {
userQuery := repository.NewUserQuery()
userProfileQuery := repository.NewUserProfileQuery()
opts := buildUserDBOptionFromFilter(userQuery, filter)
@@ -110,31 +132,25 @@ func GetUserList(ctx context.Context, filter domain.UserFilter) ([]dto.UserSumma
if err != nil {
return nil, err
}
- result := make([]dto.UserSummaryDTO, 0)
+ result := make([]dto.UserDetailDTO, 0)
- userProfileMap := make(map[int]string)
- for _, profile := range userProfilePOs {
- userProfileMap[int(profile.UserID)] = profile.Avatar
+ userProfileMap := make(map[int]*po.UserProfilePO)
+ for _, userProfilePO := range userProfilePOs {
+ userProfileMap[int(userProfilePO.UserID)] = &userProfilePO
}
for _, userPO := range userPOs {
- if avatar, exists := userProfileMap[int(userPO.ID)]; exists {
- userSummaryDTO := dto.UserSummaryDTO{
- ID: int64(userPO.ID),
- Username: userPO.Username,
- Role: userPO.UserRole,
- Avatar: avatar,
- }
- result = append(result, userSummaryDTO)
- } else {
- userSummaryDTO := dto.UserSummaryDTO{
- ID: int64(userPO.ID),
- Username: userPO.Username,
- Role: userPO.UserRole,
- Avatar: "",
- }
- result = append(result, userSummaryDTO)
+ userDetailDTO := dto.UserDetailDTO{
+ ID: int64(userPO.ID),
+ Username: userPO.Username,
+ Avatar: "",
+ Bio: "",
+ }
+ if userProfilePO, exists := userProfileMap[int(userPO.ID)]; exists {
+ userDetailDTO.Avatar = userProfilePO.Avatar
+ userDetailDTO.Bio = userProfilePO.Bio
}
+ result = append(result, userDetailDTO)
}
return result, nil
}
From c036343ec6400028490604f9bc3a01a503c9ca52 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Wed, 7 Aug 2024 14:03:37 +0800
Subject: [PATCH 10/16] Implement UpdateUserProfileHandler in handler/user.go
and add Update feature to repository/user.go
---
handler/user.go | 14 ++++++++++++-
model/converter/user.go | 45 +++++++++++++++++++++++++++++++++++++++++
repository/user.go | 12 +++++++++++
service/user.go | 26 ++++++++++++++++++++++++
4 files changed, 96 insertions(+), 1 deletion(-)
diff --git a/handler/user.go b/handler/user.go
index f12148c..9eae4a8 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -92,7 +92,19 @@ func WatchUserHandler(c *gin.Context) {}
func UnWatchUserHandler(c *gin.Context) {}
-func UpdateUserProfileHandler(c *gin.Context) {}
+func UpdateUserProfileHandler(c *gin.Context) {
+ var request dto.UserProfileDTO
+ if err := c.ShouldBindJSON(&request); err != nil {
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "参数错误"})
+ return
+ }
+ err := service.UpdateUserProfileByID(c, &request)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "用户信息更新失败。"})
+ return
+ }
+ c.JSON(http.StatusOK, dto.BaseResponse{Message: "用户信息更新成功。"})
+}
func GetUserReviewsHandler(c *gin.Context) {
userIDStr := c.Param("userID")
diff --git a/model/converter/user.go b/model/converter/user.go
index 2f46936..44a87f4 100644
--- a/model/converter/user.go
+++ b/model/converter/user.go
@@ -1,9 +1,11 @@
package converter
import (
+ "gorm.io/gorm"
"jcourse_go/model/domain"
"jcourse_go/model/dto"
"jcourse_go/model/po"
+ "time"
)
func ConvertUserPOToDomain(userPO po.UserPO) domain.User {
@@ -75,3 +77,46 @@ func ConvertToUserProfileDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO)
Grade: userProfilePO.Grade,
}
}
+
+func ConvertUpdateUserProfileDTOToUserPO(userProfileDTO *dto.UserProfileDTO, userPO *po.UserPO) *po.UserPO {
+ if userProfileDTO == nil {
+ return nil
+ }
+ return &po.UserPO{
+ Model: gorm.Model{
+ ID: userPO.ID,
+ CreatedAt: userPO.CreatedAt,
+ UpdatedAt: time.Now(),
+ DeletedAt: userPO.DeletedAt,
+ },
+ Username: userProfileDTO.Username,
+ Email: userPO.Email,
+ Password: userPO.Password,
+ UserRole: userPO.UserRole,
+ LastSeenAt: time.Now(),
+ }
+}
+
+func ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO *dto.UserProfileDTO, userProfilePO *po.UserProfilePO) *po.UserProfilePO {
+ if userProfileDTO == nil {
+ return nil
+ }
+
+ // 保留一些immutable的属性(
+ return &po.UserProfilePO{
+ Model: gorm.Model{
+ ID: userProfilePO.ID,
+ CreatedAt: userProfilePO.CreatedAt,
+ UpdatedAt: time.Now(),
+ DeletedAt: userProfilePO.DeletedAt,
+ },
+ UserID: userProfilePO.UserID,
+ Avatar: userProfileDTO.Avatar,
+ Department: userProfileDTO.Department,
+ Type: userProfilePO.Type,
+ Major: userProfileDTO.Major,
+ Degree: userProfilePO.Degree,
+ Grade: userProfileDTO.Grade,
+ Bio: userProfileDTO.Bio,
+ }
+}
diff --git a/repository/user.go b/repository/user.go
index dffc0c8..708d642 100644
--- a/repository/user.go
+++ b/repository/user.go
@@ -20,6 +20,7 @@ type IUserQuery interface {
GetUserCount(ctx context.Context, opts ...DBOption) (int64, error)
GetUserByID(ctx context.Context, userID int64) (*po.UserPO, error)
GetUserByIDs(ctx context.Context, userIDs []int64) (map[int64]po.UserPO, error)
+ UpdateUserByID(ctx context.Context, user *po.UserPO) error
WithID(id int64) DBOption
WithEmail(email string) DBOption
WithPassword(password string) DBOption
@@ -34,6 +35,7 @@ type IUserProfileQuery interface {
GetUserProfileByID(ctx context.Context, userID int64) (*po.UserProfilePO, error)
GetUserProfileList(ctx context.Context, opts ...DBOption) ([]po.UserProfilePO, error)
GetUserProfileCount(ctx context.Context, opts ...DBOption) (int64, error)
+ UpdateUserProfileByID(ctx context.Context, userProfile *po.UserProfilePO) error
WithUserID(id int64) DBOption
WithLimit(limit int64) DBOption
WithOffset(offset int64) DBOption
@@ -128,6 +130,11 @@ func (q *UserProfileQuery) GetUserProfileCount(ctx context.Context, opts ...DBOp
return count, nil
}
+func (q *UserProfileQuery) UpdateUserProfileByID(ctx context.Context, userProfile *po.UserProfilePO) error {
+ result := q.optionDB(ctx, q.WithUserID(userProfile.UserID)).Save(userProfile).Error
+ return result
+}
+
type UserQuery struct {
db *gorm.DB
}
@@ -215,6 +222,11 @@ func (q *UserQuery) GetUserCount(ctx context.Context, opts ...DBOption) (int64,
return count, nil
}
+func (q *UserQuery) UpdateUserByID(ctx context.Context, user *po.UserPO) error {
+ result := q.optionDB(ctx, q.WithID(int64(user.ID))).Save(user).Error
+ return result
+}
+
func (q *UserQuery) CreateUser(ctx context.Context, email string, passwordStore string) (*po.UserPO, error) {
user := po.UserPO{
Username: email,
diff --git a/service/user.go b/service/user.go
index de47127..451e941 100644
--- a/service/user.go
+++ b/service/user.go
@@ -161,3 +161,29 @@ func GetUserCount(ctx context.Context, filter domain.UserFilter) (int64, error)
opts := buildUserDBOptionFromFilter(userQuery, filter)
return userQuery.GetUserCount(ctx, opts...)
}
+
+func UpdateUserProfileByID(ctx context.Context, userProfileDTO *dto.UserProfileDTO) error {
+ userQuery := repository.NewUserQuery()
+ oldUserPO, errQuery := userQuery.GetUserByID(ctx, userProfileDTO.UserID)
+ if errQuery != nil {
+ return errQuery
+ }
+ newUserPO := converter.ConvertUpdateUserProfileDTOToUserPO(userProfileDTO, oldUserPO)
+
+ errUpdate := userQuery.UpdateUserByID(ctx, newUserPO)
+ if errUpdate != nil {
+ return errUpdate
+ }
+
+ userProfileQuery := repository.NewUserProfileQuery()
+ oldUserProfilePO, errQuery2 := userProfileQuery.GetUserProfileByID(ctx, userProfileDTO.UserID)
+ if errQuery2 != nil {
+ return errQuery2
+ }
+ newUserProfilePO := converter.ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO, oldUserProfilePO)
+ errUpdate2 := userProfileQuery.UpdateUserProfileByID(ctx, newUserProfilePO)
+ if errUpdate2 != nil {
+ return errUpdate2
+ }
+ return nil
+}
From 5d4a053621c5bc9411d1c3cf5ffcec02e075fedc Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 11 Aug 2024 08:15:46 +0800
Subject: [PATCH 11/16] Remove .idea directory from version control and update
.gitignore
---
.gitignore | 2 ++
.idea/.gitignore | 8 --------
.idea/jcourse_go.iml | 9 ---------
.idea/modules.xml | 8 --------
.idea/vcs.xml | 6 ------
5 files changed, 2 insertions(+), 31 deletions(-)
delete mode 100644 .idea/.gitignore
delete mode 100644 .idea/jcourse_go.iml
delete mode 100644 .idea/modules.xml
delete mode 100644 .idea/vcs.xml
diff --git a/.gitignore b/.gitignore
index f140648..5e381ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,5 @@ go.work.sum
.env
*.sqlite
+
+.idea
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 35410ca..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# 默认忽略的文件
-/shelf/
-/workspace.xml
-# 基于编辑器的 HTTP 客户端请求
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/jcourse_go.iml b/.idea/jcourse_go.iml
deleted file mode 100644
index 5e764c4..0000000
--- a/.idea/jcourse_go.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 89f009c..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
From d4b53bb738fdb8393a3bd3dbd5b520b80c8cae77 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 11 Aug 2024 09:09:37 +0800
Subject: [PATCH 12/16] Refactor: remove direct gorm.Model dependency in
business logic
---
model/converter/user.go | 38 +++++++++++++-------------------------
service/user.go | 4 ++--
2 files changed, 15 insertions(+), 27 deletions(-)
diff --git a/model/converter/user.go b/model/converter/user.go
index 44a87f4..99cb9ef 100644
--- a/model/converter/user.go
+++ b/model/converter/user.go
@@ -1,7 +1,6 @@
package converter
import (
- "gorm.io/gorm"
"jcourse_go/model/domain"
"jcourse_go/model/dto"
"jcourse_go/model/po"
@@ -78,38 +77,23 @@ func ConvertToUserProfileDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO)
}
}
-func ConvertUpdateUserProfileDTOToUserPO(userProfileDTO *dto.UserProfileDTO, userPO *po.UserPO) *po.UserPO {
- if userProfileDTO == nil {
- return nil
- }
- return &po.UserPO{
- Model: gorm.Model{
- ID: userPO.ID,
- CreatedAt: userPO.CreatedAt,
- UpdatedAt: time.Now(),
- DeletedAt: userPO.DeletedAt,
- },
+func ConvertUpdateUserProfileDTOToUserPO(userProfileDTO *dto.UserProfileDTO, userPO *po.UserPO) po.UserPO {
+ updatedUserPo := po.UserPO{
Username: userProfileDTO.Username,
Email: userPO.Email,
Password: userPO.Password,
UserRole: userPO.UserRole,
LastSeenAt: time.Now(),
}
-}
-
-func ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO *dto.UserProfileDTO, userProfilePO *po.UserProfilePO) *po.UserProfilePO {
- if userProfileDTO == nil {
- return nil
+ if userProfileDTO.ID != 0 {
+ updatedUserPo.ID = userPO.ID
}
+ return updatedUserPo
+}
- // 保留一些immutable的属性(
- return &po.UserProfilePO{
- Model: gorm.Model{
- ID: userProfilePO.ID,
- CreatedAt: userProfilePO.CreatedAt,
- UpdatedAt: time.Now(),
- DeletedAt: userProfilePO.DeletedAt,
- },
+func ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO *dto.UserProfileDTO, userProfilePO *po.UserProfilePO) po.UserProfilePO {
+ // 保留一些immutable的属性
+ updatedUserProfilePO := po.UserProfilePO{
UserID: userProfilePO.UserID,
Avatar: userProfileDTO.Avatar,
Department: userProfileDTO.Department,
@@ -119,4 +103,8 @@ func ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO *dto.UserProfileDT
Grade: userProfileDTO.Grade,
Bio: userProfileDTO.Bio,
}
+ if updatedUserProfilePO.ID != 0 {
+ updatedUserProfilePO.ID = userProfilePO.ID
+ }
+ return updatedUserProfilePO
}
diff --git a/service/user.go b/service/user.go
index 451e941..105b7ad 100644
--- a/service/user.go
+++ b/service/user.go
@@ -170,7 +170,7 @@ func UpdateUserProfileByID(ctx context.Context, userProfileDTO *dto.UserProfileD
}
newUserPO := converter.ConvertUpdateUserProfileDTOToUserPO(userProfileDTO, oldUserPO)
- errUpdate := userQuery.UpdateUserByID(ctx, newUserPO)
+ errUpdate := userQuery.UpdateUserByID(ctx, &newUserPO)
if errUpdate != nil {
return errUpdate
}
@@ -181,7 +181,7 @@ func UpdateUserProfileByID(ctx context.Context, userProfileDTO *dto.UserProfileD
return errQuery2
}
newUserProfilePO := converter.ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO, oldUserProfilePO)
- errUpdate2 := userProfileQuery.UpdateUserProfileByID(ctx, newUserProfilePO)
+ errUpdate2 := userProfileQuery.UpdateUserProfileByID(ctx, &newUserProfilePO)
if errUpdate2 != nil {
return errUpdate2
}
From 51411a919a02ae4cef8db7f5559df7c0f9a94869 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Sun, 11 Aug 2024 11:23:43 +0800
Subject: [PATCH 13/16] Refactor: Update user-related API routes and improve
handlers
- Renamed routes in userGroup
- Added a new '/user' route in adminGroup for displaying the user list
- Refined user-related handlers to improve readability
- Implemented authentication checks across user-related endpoints
---
handler/admin.go | 34 ++++++++++++++++++++++++++
handler/user.go | 62 +++++++++++++++++++++++++++++++++++------------
model/dto/user.go | 2 ++
router.go | 7 +++---
service/user.go | 6 +++++
5 files changed, 93 insertions(+), 18 deletions(-)
create mode 100644 handler/admin.go
diff --git a/handler/admin.go b/handler/admin.go
new file mode 100644
index 0000000..f21ddd4
--- /dev/null
+++ b/handler/admin.go
@@ -0,0 +1,34 @@
+package handler
+
+import (
+ "github.com/gin-gonic/gin"
+ "jcourse_go/model/domain"
+ "jcourse_go/model/dto"
+ "jcourse_go/service"
+ "net/http"
+)
+
+func AdminGetUserList(c *gin.Context) {
+ var request dto.UserListRequest
+ if err := c.ShouldBindQuery(&request); err != nil {
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "参数错误"})
+ return
+ }
+
+ filter := domain.UserFilter{
+ Page: request.Page,
+ PageSize: request.PageSize,
+ }
+ users, err := service.AdminGetUserList(c, filter)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "内部错误。"})
+ }
+ total, _ := service.GetUserCount(c, filter)
+ response := dto.UserListResponseForAdmin{
+ Page: request.Page,
+ PageSize: request.PageSize,
+ Total: total,
+ Data: users,
+ }
+ c.JSON(http.StatusOK, response)
+}
diff --git a/handler/user.go b/handler/user.go
index 9eae4a8..9632a4e 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -1,6 +1,7 @@
package handler
import (
+ "errors"
"github.com/gin-gonic/gin"
"jcourse_go/constant"
"jcourse_go/model/converter"
@@ -14,9 +15,6 @@ import (
func GetSuggestedUserHandler(c *gin.Context) {}
func GetUserListHandler(c *gin.Context) {
-
- // 管理员权限验证
-
var request dto.UserListRequest
if err := c.ShouldBindQuery(&request); err != nil {
c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "参数错误"})
@@ -41,7 +39,24 @@ func GetUserListHandler(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
-func GetCurrentUserSummaryHandler(c *gin.Context) {
+func getUserIDFromRequest(c *gin.Context) (int64, error) {
+ userIDStr := c.Param("userID")
+ userID, err := strconv.Atoi(userIDStr)
+ if err != nil {
+ return -1, errors.New("非法用户ID")
+ }
+ return int64(userID), nil
+}
+
+// 非公开信息?
+func GetUserSummaryHandler(c *gin.Context) {
+ userID, err := getUserIDFromRequest(c)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "非法用户ID"})
+ return
+ }
+
+ // UserSummary鉴权
userInterface, exists := c.Get(constant.CtxKeyUser)
if !exists {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
@@ -49,6 +64,11 @@ func GetCurrentUserSummaryHandler(c *gin.Context) {
}
user, _ := userInterface.(*domain.User)
+ if user.ID != userID {
+ c.JSON(http.StatusForbidden, dto.BaseResponse{Message: "无权查看他人信息!"})
+ return
+ }
+
me, err := service.GetUserSummaryByID(c, user.ID)
if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
@@ -57,29 +77,43 @@ func GetCurrentUserSummaryHandler(c *gin.Context) {
c.JSON(http.StatusOK, me)
}
+// 公开信息
func GetUserDetailHandler(c *gin.Context) {
- userIDStr := c.Param("userID")
- userID, err := strconv.Atoi(userIDStr)
+ userID, err := getUserIDFromRequest(c)
if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "非法用户ID"})
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "非法用户ID"})
return
}
- userDetail, errDetail := service.GetUserDetailByID(c, int64(userID))
+ userDetail, errDetail := service.GetUserDetailByID(c, userID)
if errDetail != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
+ return
}
c.JSON(http.StatusOK, userDetail)
}
-func GetCurrentUserProfileHandler(c *gin.Context) {
+// 非公开信息
+func GetUserProfileHandler(c *gin.Context) {
+ userID, err := getUserIDFromRequest(c)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "非法用户ID"})
+ return
+ }
+
+ // UserPorfile鉴权
userInterface, exists := c.Get(constant.CtxKeyUser)
if !exists {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
return
}
-
user, _ := userInterface.(*domain.User)
+
+ if user.ID != userID {
+ c.JSON(http.StatusForbidden, dto.BaseResponse{Message: "无权查看他人信息!"})
+ return
+ }
+
me, err := service.GetUserProfileByID(c, user.ID)
if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
@@ -107,11 +141,9 @@ func UpdateUserProfileHandler(c *gin.Context) {
}
func GetUserReviewsHandler(c *gin.Context) {
- userIDStr := c.Param("userID")
-
- userID, err := strconv.Atoi(userIDStr)
+ userID, err := getUserIDFromRequest(c)
if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "非法用户ID"})
+ c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "非法用户ID"})
return
}
@@ -124,7 +156,7 @@ func GetUserReviewsHandler(c *gin.Context) {
filter := domain.ReviewFilter{
Page: request.Page,
PageSize: request.PageSize,
- UserID: int64(userID),
+ UserID: userID,
}
reviews, err := service.GetReviewList(c, filter)
diff --git a/model/dto/user.go b/model/dto/user.go
index 30f7979..28955c5 100644
--- a/model/dto/user.go
+++ b/model/dto/user.go
@@ -11,6 +11,8 @@ type UserListRequest struct {
type UserListResponse = BasePaginateResponse[UserDetailDTO]
+type UserListResponseForAdmin = BasePaginateResponse[UserProfileDTO]
+
type UserSummaryDTO struct {
ID int64 `json:"id"`
ReviewCount int64 `json:"review_count"`
diff --git a/router.go b/router.go
index 2bec19d..33b920c 100644
--- a/router.go
+++ b/router.go
@@ -51,16 +51,17 @@ func registerRouter(r *gin.Engine) {
userGroup := needAuthGroup.Group("/user")
userGroup.GET("", handler.GetUserListHandler)
userGroup.GET("/suggest", handler.GetSuggestedUserHandler)
- userGroup.GET("/me", handler.GetCurrentUserSummaryHandler)
+ userGroup.GET("/:userID/summary", handler.GetUserSummaryHandler)
userGroup.GET("/:userID/detail", handler.GetUserDetailHandler)
userGroup.GET("/:userID/reviews", handler.GetUserReviewsHandler)
userGroup.POST("/:userID/watch", handler.WatchUserHandler)
userGroup.POST("/:userID/unwatch", handler.UnWatchUserHandler)
- userGroup.GET("/me/profile", handler.GetCurrentUserProfileHandler)
- userGroup.PUT("/me/profile", handler.UpdateUserProfileHandler)
+ userGroup.GET("/:userID/profile", handler.GetUserProfileHandler)
+ userGroup.PUT("/:userID/profile", handler.UpdateUserProfileHandler)
adminGroup := needAuthGroup.Group("/admin")
adminGroup.Use(middleware.RequireAdmin())
+ adminGroup.GET("/user", handler.AdminGetUserList)
adminGroup.GET("")
}
diff --git a/service/user.go b/service/user.go
index 105b7ad..8b0204a 100644
--- a/service/user.go
+++ b/service/user.go
@@ -155,6 +155,12 @@ func GetUserList(ctx context.Context, filter domain.UserFilter) ([]dto.UserDetai
return result, nil
}
+func AdminGetUserList(ctx context.Context, filter domain.UserFilter) ([]dto.UserProfileDTO, error) {
+ // 视前端而定获取用户的哪些信息
+ // E.g. UserProfileDTO
+ return nil, nil
+}
+
func GetUserCount(ctx context.Context, filter domain.UserFilter) (int64, error) {
userQuery := repository.NewUserQuery()
filter.Page, filter.PageSize = 0, 0
From 16cae31b11df2911f44752bdf297bb09474ba2a7 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Mon, 12 Aug 2024 11:05:23 +0800
Subject: [PATCH 14/16] Refactor: service-domain, handler-dto; fix bug in
ConvertUserDomainToUserSummaryDTO func which caused cycle import
---
handler/user.go | 18 ++++++++-----
model/converter/user.go | 57 +++++++++++++++++++++++-----------------
model/dto/user.go | 1 -
service/user.go | 58 ++++++++---------------------------------
4 files changed, 55 insertions(+), 79 deletions(-)
diff --git a/handler/user.go b/handler/user.go
index 9632a4e..3a40bda 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -69,12 +69,12 @@ func GetUserSummaryHandler(c *gin.Context) {
return
}
- me, err := service.GetUserSummaryByID(c, user.ID)
+ userSummary, err := service.GetUserSummaryByID(c, user.ID)
if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
return
}
- c.JSON(http.StatusOK, me)
+ c.JSON(http.StatusOK, userSummary)
}
// 公开信息
@@ -85,11 +85,13 @@ func GetUserDetailHandler(c *gin.Context) {
return
}
- userDetail, errDetail := service.GetUserDetailByID(c, userID)
- if errDetail != nil {
+ userDomain, err := service.GetUserDomainByID(c, userID)
+ if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
return
}
+
+ userDetail := converter.ConvertUserDomainToUserDetailDTO(userDomain)
c.JSON(http.StatusOK, userDetail)
}
@@ -101,7 +103,7 @@ func GetUserProfileHandler(c *gin.Context) {
return
}
- // UserPorfile鉴权
+ // UserProfile鉴权
userInterface, exists := c.Get(constant.CtxKeyUser)
if !exists {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
@@ -114,12 +116,14 @@ func GetUserProfileHandler(c *gin.Context) {
return
}
- me, err := service.GetUserProfileByID(c, user.ID)
+ userDomain, err := service.GetUserDomainByID(c, user.ID)
if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
return
}
- c.JSON(http.StatusOK, me)
+
+ userProfile := converter.ConvertUserDomainToUserProfileDTO(userDomain)
+ c.JSON(http.StatusOK, userProfile)
}
func WatchUserHandler(c *gin.Context) {}
diff --git a/model/converter/user.go b/model/converter/user.go
index 99cb9ef..eea9dab 100644
--- a/model/converter/user.go
+++ b/model/converter/user.go
@@ -47,48 +47,57 @@ func ConvertUserDomainToReviewDTO(user domain.User) dto.UserInReviewDTO {
}
}
-func ConvertToUserDetailDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserDetailDTO {
- if userPO == nil {
+func ConvertUserDomainToUserSummaryDTO(id int64, reviewCount int64, likeReceive int64, tipReceive int64, followingCourseCount int64) *dto.UserSummaryDTO {
+ return &dto.UserSummaryDTO{
+ ID: id,
+ ReviewCount: reviewCount,
+ LikeReceive: likeReceive,
+ TipReceive: tipReceive,
+ FollowingCourseCount: followingCourseCount,
+ }
+}
+
+func ConvertUserDomainToUserDetailDTO(userDomain *domain.User) *dto.UserDetailDTO {
+ if userDomain == nil {
return nil
}
return &dto.UserDetailDTO{
- ID: int64(userPO.ID),
- Username: userPO.Username,
- Avatar: userProfilePO.Avatar,
- Bio: userProfilePO.Bio,
+ ID: userDomain.ID,
+ Username: userDomain.Username,
+ Avatar: userDomain.Profile.Avatar,
+ Bio: userDomain.Profile.Bio,
}
}
-func ConvertToUserProfileDTO(userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserProfileDTO {
- if userPO == nil {
+func ConvertUserDomainToUserProfileDTO(userDomain *domain.User) *dto.UserProfileDTO {
+ if userDomain == nil {
return nil
}
return &dto.UserProfileDTO{
- ID: int64(userPO.ID),
- UserID: userProfilePO.UserID,
- Username: userPO.Username,
- Bio: userProfilePO.Bio,
- Email: userPO.Email,
- Avatar: userProfilePO.Avatar,
- Role: userPO.UserRole,
- Department: userProfilePO.Department,
- Major: userProfilePO.Major,
- Grade: userProfilePO.Grade,
+ UserID: userDomain.ID,
+ Username: userDomain.Username,
+ Bio: userDomain.Profile.Bio,
+ Email: userDomain.Email,
+ Avatar: userDomain.Profile.Avatar,
+ Role: userDomain.Role,
+ Department: userDomain.Profile.Department,
+ Major: userDomain.Profile.Major,
+ Grade: userDomain.Profile.Grade,
}
}
func ConvertUpdateUserProfileDTOToUserPO(userProfileDTO *dto.UserProfileDTO, userPO *po.UserPO) po.UserPO {
- updatedUserPo := po.UserPO{
+ updatedUserPO := po.UserPO{
Username: userProfileDTO.Username,
Email: userPO.Email,
Password: userPO.Password,
UserRole: userPO.UserRole,
LastSeenAt: time.Now(),
}
- if userProfileDTO.ID != 0 {
- updatedUserPo.ID = userPO.ID
+ if userProfileDTO.UserID != 0 {
+ updatedUserPO.ID = uint(userProfileDTO.UserID)
}
- return updatedUserPo
+ return updatedUserPO
}
func ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO *dto.UserProfileDTO, userProfilePO *po.UserProfilePO) po.UserProfilePO {
@@ -103,8 +112,8 @@ func ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO *dto.UserProfileDT
Grade: userProfileDTO.Grade,
Bio: userProfileDTO.Bio,
}
- if updatedUserProfilePO.ID != 0 {
- updatedUserProfilePO.ID = userProfilePO.ID
+ if userProfileDTO.UserID != 0 {
+ updatedUserProfilePO.ID = uint(userProfileDTO.UserID)
}
return updatedUserProfilePO
}
diff --git a/model/dto/user.go b/model/dto/user.go
index 28955c5..257b704 100644
--- a/model/dto/user.go
+++ b/model/dto/user.go
@@ -29,7 +29,6 @@ type UserDetailDTO struct {
}
type UserProfileDTO struct {
- ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Username string `json:"username"`
Bio string `json:"bio"`
diff --git a/service/user.go b/service/user.go
index 8b0204a..1e2272b 100644
--- a/service/user.go
+++ b/service/user.go
@@ -11,25 +11,17 @@ import (
"jcourse_go/repository"
)
-// 该函数如果放在converter包中会报错import cycle is not allowed
-// UserSummaryDTO的组装需要借助service/review.go中的GetReviewCount函数
-func ConvertToUserSummaryDTO(ctx context.Context, userPO *po.UserPO, userProfilePO *po.UserProfilePO) *dto.UserSummaryDTO {
- if userPO == nil {
- return nil
- }
-
+func GetUserSummaryByID(ctx context.Context, userID int64) (*dto.UserSummaryDTO, error) {
filter := domain.ReviewFilter{
- UserID: int64(userPO.ID),
+ UserID: userID,
}
total, _ := GetReviewCount(ctx, filter)
+ // 过滤非匿名点评
- return &dto.UserSummaryDTO{
- ID: int64(userPO.ID),
- ReviewCount: total,
- TipReceive: 0,
- FollowingCourseCount: 0,
- }
+ // 获取用户收到的赞数、被打赏积分数、关注的课程数
+
+ return converter.ConvertUserDomainToUserSummaryDTO(userID, total, 0, 0, 0), nil
}
func GetUserByIDs(ctx context.Context, userIDs []int64) (map[int64]domain.User, error) {
@@ -61,23 +53,8 @@ func GetUserByIDs(ctx context.Context, userIDs []int64) (map[int64]domain.User,
return result, nil
}
-func GetUserSummaryByID(ctx context.Context, userID int64) (*dto.UserSummaryDTO, error) {
- userQuery := repository.NewUserQuery()
- userPO, err := userQuery.GetUserByID(ctx, userID)
- if err != nil {
- return nil, err
- }
-
- userProfileQuery := repository.NewUserProfileQuery()
- userProfilePO, err := userProfileQuery.GetUserProfileByID(ctx, userID)
- if err != nil {
- return nil, err
- }
- userSummary := ConvertToUserSummaryDTO(ctx, userPO, userProfilePO)
- return userSummary, nil
-}
-
-func GetUserDetailByID(ctx context.Context, userID int64) (*dto.UserDetailDTO, error) {
+// 共用函数,用于获取用户基本信息和详细资料并组装成domain.User
+func GetUserDomainByID(ctx context.Context, userID int64) (*domain.User, error) {
userQuery := repository.NewUserQuery()
userPO, err := userQuery.GetUserByID(ctx, userID)
if err != nil {
@@ -89,24 +66,11 @@ func GetUserDetailByID(ctx context.Context, userID int64) (*dto.UserDetailDTO, e
if err != nil {
return nil, err
}
- userDetails := converter.ConvertToUserDetailDTO(userPO, userProfilePO)
- return userDetails, nil
-}
-func GetUserProfileByID(ctx context.Context, userID int64) (*dto.UserProfileDTO, error) {
- userQuery := repository.NewUserQuery()
- userPO, err := userQuery.GetUserByID(ctx, userID)
- if err != nil {
- return nil, err
- }
+ user := converter.ConvertUserPOToDomain(*userPO)
+ converter.PackUserWithProfile(&user, *userProfilePO)
- userProfileQuery := repository.NewUserProfileQuery()
- userProfilePO, err := userProfileQuery.GetUserProfileByID(ctx, userID)
- if err != nil {
- return nil, err
- }
- userProfile := converter.ConvertToUserProfileDTO(userPO, userProfilePO)
- return userProfile, nil
+ return &user, nil
}
func buildUserDBOptionFromFilter(query repository.IUserQuery, filter domain.UserFilter) []repository.DBOption {
From 3ae2d298b8d6cfbfa6463c652ece9c7eb8bbe993 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Wed, 14 Aug 2024 10:51:51 +0800
Subject: [PATCH 15/16] feat: Add authentication to UpdateUserProfileHandler
and change save function parameter in repository/user.go from value to
reference
---
handler/user.go | 13 +++++++++++++
repository/user.go | 4 ++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/handler/user.go b/handler/user.go
index 3a40bda..407a536 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -131,11 +131,24 @@ func WatchUserHandler(c *gin.Context) {}
func UnWatchUserHandler(c *gin.Context) {}
func UpdateUserProfileHandler(c *gin.Context) {
+ userInterface, exists := c.Get(constant.CtxKeyUser)
+ if !exists {
+ c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
+ return
+ }
+ user, _ := userInterface.(*domain.User)
+
var request dto.UserProfileDTO
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "参数错误"})
return
}
+
+ if user.ID != request.UserID {
+ c.JSON(http.StatusForbidden, dto.BaseResponse{Message: "无权更新其他用户信息!"})
+ return
+ }
+
err := service.UpdateUserProfileByID(c, &request)
if err != nil {
c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "用户信息更新失败。"})
diff --git a/repository/user.go b/repository/user.go
index 708d642..aa9caee 100644
--- a/repository/user.go
+++ b/repository/user.go
@@ -131,7 +131,7 @@ func (q *UserProfileQuery) GetUserProfileCount(ctx context.Context, opts ...DBOp
}
func (q *UserProfileQuery) UpdateUserProfileByID(ctx context.Context, userProfile *po.UserProfilePO) error {
- result := q.optionDB(ctx, q.WithUserID(userProfile.UserID)).Save(userProfile).Error
+ result := q.optionDB(ctx, q.WithUserID(userProfile.UserID)).Save(&userProfile).Error
return result
}
@@ -223,7 +223,7 @@ func (q *UserQuery) GetUserCount(ctx context.Context, opts ...DBOption) (int64,
}
func (q *UserQuery) UpdateUserByID(ctx context.Context, user *po.UserPO) error {
- result := q.optionDB(ctx, q.WithID(int64(user.ID))).Save(user).Error
+ result := q.optionDB(ctx, q.WithID(int64(user.ID))).Save(&user).Error
return result
}
From 3d901c4fc52be3825fc2eab2e556b9fb05d18a45 Mon Sep 17 00:00:00 2001
From: Victor Zhu <2364305645@qq.com>
Date: Wed, 14 Aug 2024 11:30:32 +0800
Subject: [PATCH 16/16] fix: issues reported by golangci-lint
---
handler/admin.go | 3 ++-
handler/user.go | 7 ++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/handler/admin.go b/handler/admin.go
index f21ddd4..e5b4970 100644
--- a/handler/admin.go
+++ b/handler/admin.go
@@ -1,11 +1,12 @@
package handler
import (
- "github.com/gin-gonic/gin"
"jcourse_go/model/domain"
"jcourse_go/model/dto"
"jcourse_go/service"
"net/http"
+
+ "github.com/gin-gonic/gin"
)
func AdminGetUserList(c *gin.Context) {
diff --git a/handler/user.go b/handler/user.go
index 407a536..30a82a2 100644
--- a/handler/user.go
+++ b/handler/user.go
@@ -2,7 +2,6 @@ package handler
import (
"errors"
- "github.com/gin-gonic/gin"
"jcourse_go/constant"
"jcourse_go/model/converter"
"jcourse_go/model/domain"
@@ -10,6 +9,8 @@ import (
"jcourse_go/service"
"net/http"
"strconv"
+
+ "github.com/gin-gonic/gin"
)
func GetSuggestedUserHandler(c *gin.Context) {}
@@ -183,6 +184,10 @@ func GetUserReviewsHandler(c *gin.Context) {
}
total, err := service.GetReviewCount(c, filter)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "内部错误。"})
+ return
+ }
response := dto.ReviewListResponse{
Page: request.Page,