Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/user #1

Merged
merged 16 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e17f9f2
feat: add PersonalSignature field, UserActivities table and several c…
victorzhu30 Jul 21, 2024
db26179
feat(user): add pagination and retrieval functions for User and UserP…
victorzhu30 Jul 21, 2024
ac5a5d8
feat: add /:userID/reviews, /:userID/watch and /me/info routes in use…
victorzhu30 Jul 21, 2024
b7bedcd
feat: achieve GetUserList and GetUserCount functions, build UserDBopt…
victorzhu30 Jul 21, 2024
cc287d8
feat: implement GetUserListHandler, GetCurrentUserHandler, GetUserDet…
victorzhu30 Jul 21, 2024
65774d8
Reorganize routes in router.go and update model fields in dto/user.go…
victorzhu30 Aug 6, 2024
1db8120
Update model fields in dto/user.go according to jcourse_v2_frontend m…
victorzhu30 Aug 6, 2024
d6fce21
Related changes in model for commit d6d1d1d
victorzhu30 Aug 6, 2024
e14ec8f
Refined/Implemented several handlers in d6d1d1d
victorzhu30 Aug 6, 2024
c036343
Implement UpdateUserProfileHandler in handler/user.go and add Update …
victorzhu30 Aug 7, 2024
5d4a053
Remove .idea directory from version control and update .gitignore
victorzhu30 Aug 11, 2024
d4b53bb
Refactor: remove direct gorm.Model dependency in business logic
victorzhu30 Aug 11, 2024
51411a9
Refactor: Update user-related API routes and improve handlers
victorzhu30 Aug 11, 2024
16cae31
Refactor: service-domain, handler-dto; fix bug in ConvertUserDomainTo…
victorzhu30 Aug 12, 2024
3ae2d29
feat: Add authentication to UpdateUserProfileHandler and change save …
victorzhu30 Aug 14, 2024
3d901c4
fix: issues reported by golangci-lint
victorzhu30 Aug 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ go.work.sum
.env

*.sqlite

.idea
35 changes: 35 additions & 0 deletions handler/admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package handler

import (
"jcourse_go/model/domain"
"jcourse_go/model/dto"
"jcourse_go/service"
"net/http"

"github.com/gin-gonic/gin"
)

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)
}
192 changes: 188 additions & 4 deletions handler/user.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,199 @@
package handler

import "github.com/gin-gonic/gin"
import (
"errors"
"jcourse_go/constant"
"jcourse_go/model/converter"
"jcourse_go/model/domain"
"jcourse_go/model/dto"
"jcourse_go/service"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
)

func GetSuggestedUserHandler(c *gin.Context) {}

func GetUserListHandler(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: "参数错误"})
return
}

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 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: "用户未登录!"})
return
}
user, _ := userInterface.(*domain.User)

if user.ID != userID {
c.JSON(http.StatusForbidden, dto.BaseResponse{Message: "无权查看他人信息!"})
return
}

userSummary, err := service.GetUserSummaryByID(c, user.ID)
if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
return
}
c.JSON(http.StatusOK, userSummary)
}

// 公开信息
func GetUserDetailHandler(c *gin.Context) {
userID, err := getUserIDFromRequest(c)
if err != nil {
c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "非法用户ID"})
return
}

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)
}

// 非公开信息
func GetUserProfileHandler(c *gin.Context) {
userID, err := getUserIDFromRequest(c)
if err != nil {
c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "非法用户ID"})
return
}

// UserProfile鉴权
userInterface, exists := c.Get(constant.CtxKeyUser)
if !exists {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "用户未登录!"})
return
}
user, _ := userInterface.(*domain.User)

func GetCurrentUserHandler(c *gin.Context) {}
if user.ID != userID {
c.JSON(http.StatusForbidden, dto.BaseResponse{Message: "无权查看他人信息!"})
return
}

func GetUserDetailHandler(c *gin.Context) {}
userDomain, err := service.GetUserDomainByID(c, user.ID)
if err != nil {
c.JSON(http.StatusNotFound, dto.BaseResponse{Message: "此用户不存在!"})
return
}

userProfile := converter.ConvertUserDomainToUserProfileDTO(userDomain)
c.JSON(http.StatusOK, userProfile)
}

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: "用户信息更新失败。"})
return
}
c.JSON(http.StatusOK, dto.BaseResponse{Message: "用户信息更新成功。"})
}

func GetUserReviewsHandler(c *gin.Context) {
userID, err := getUserIDFromRequest(c)
if err != nil {
c.JSON(http.StatusBadRequest, dto.BaseResponse{Message: "非法用户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: userID,
}

reviews, err := service.GetReviewList(c, filter)
if err != nil {
c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "内部错误。"})
return
}

total, err := service.GetReviewCount(c, filter)
if err != nil {
c.JSON(http.StatusInternalServerError, dto.BaseResponse{Message: "内部错误。"})
return
}

response := dto.ReviewListResponse{
Page: request.Page,
PageSize: request.PageSize,
Total: total,
Data: converter.ConvertReviewDomainToListDTO(reviews, true),
}
c.JSON(http.StatusOK, response)
}
73 changes: 73 additions & 0 deletions model/converter/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"jcourse_go/model/domain"
"jcourse_go/model/dto"
"jcourse_go/model/po"
"time"
)

func ConvertUserPOToDomain(userPO po.UserPO) domain.User {
Expand All @@ -26,6 +27,7 @@ func ConvertUserProfilePOToDomain(userProfile po.UserProfilePO) domain.UserProfi
Major: userProfile.Major,
Degree: userProfile.Degree,
Grade: userProfile.Grade,
Bio: userProfile.Bio,
}
}

Expand All @@ -44,3 +46,74 @@ func ConvertUserDomainToReviewDTO(user domain.User) dto.UserInReviewDTO {
Avatar: user.Profile.Avatar,
}
}

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: userDomain.ID,
Username: userDomain.Username,
Avatar: userDomain.Profile.Avatar,
Bio: userDomain.Profile.Bio,
}
}

func ConvertUserDomainToUserProfileDTO(userDomain *domain.User) *dto.UserProfileDTO {
if userDomain == nil {
return nil
}
return &dto.UserProfileDTO{
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{
Username: userProfileDTO.Username,
Email: userPO.Email,
Password: userPO.Password,
UserRole: userPO.UserRole,
LastSeenAt: time.Now(),
dujiajun marked this conversation as resolved.
Show resolved Hide resolved
}
if userProfileDTO.UserID != 0 {
updatedUserPO.ID = uint(userProfileDTO.UserID)
}
return updatedUserPO
}

func ConvertUpdateUserProfileDTOToUsrProfilePO(userProfileDTO *dto.UserProfileDTO, userProfilePO *po.UserProfilePO) po.UserProfilePO {
// 保留一些immutable的属性
updatedUserProfilePO := po.UserProfilePO{
UserID: userProfilePO.UserID,
Avatar: userProfileDTO.Avatar,
Department: userProfileDTO.Department,
Type: userProfilePO.Type,
Major: userProfileDTO.Major,
Degree: userProfilePO.Degree,
Grade: userProfileDTO.Grade,
Bio: userProfileDTO.Bio,
}
if userProfileDTO.UserID != 0 {
updatedUserProfilePO.ID = uint(userProfileDTO.UserID)
}
return updatedUserProfilePO
}
7 changes: 7 additions & 0 deletions model/domain/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,11 @@ type UserProfile struct {
Major string
Degree string
Grade string
Bio string
}

type UserFilter struct {
Page int64
PageSize int64
// To be continued ... (add more fields)
}
41 changes: 41 additions & 0 deletions model/dto/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dto

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[UserDetailDTO]

type UserListResponseForAdmin = BasePaginateResponse[UserProfileDTO]

type UserSummaryDTO struct {
ID int64 `json:"id"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个ID是啥

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

用户ID

ReviewCount int64 `json:"review_count"`
LikeReceive int64 `json:"like_receive"`
TipReceive int64 `json:"tip_receive"`
FollowingCourseCount int64 `json:"following_course_count"`
}

type UserDetailDTO struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detail 和 profile 区别是啥

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个我是根据前端的字段调整的,UserDetailPage中已经有UserSummaryProps和UserDetailProps,分别对应概览的数据和左上角个人信息的数据,我觉得还需要添加一个UserProfileProps作为设置部分UserProfileTab这个组件的props和处理表单,前端修改了还没commit

ID int64 `json:"id"`
Username string `json:"username"`
Avatar string `json:"avatar"`
Bio string `json:"bio"`
}

type UserProfileDTO struct {
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"`
}
Loading
Loading