From 978116cf99b29638e7dc4fd574f8bdbd48b8462e Mon Sep 17 00:00:00 2001 From: Frankie Jun Date: Thu, 2 Nov 2023 19:15:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0profile=E6=8E=A5=E5=8F=A3=20(?= =?UTF-8?q?#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * email service 简单的 failvover 策略 * e2e * test * 增加zap日志 * 增加zap日志 * 增加zap日志 * 实现个人信息编辑接口 * 实现个人信息编辑接口 * 实现个人信息编辑接口 * 实现个人信息编辑接口-补测试 * 实现profile接口 * try --------- Co-authored-by: frankiejun --- internal/repository/dao/mocks/user.mock.go | 15 +++++ internal/repository/dao/user.go | 7 +++ internal/repository/dao/user_test.go | 44 +++++++++++++++ internal/repository/mocks/user.mock.go | 15 +++++ internal/repository/user.go | 12 ++++ internal/repository/user_test.go | 48 ++++++++++++++++ internal/service/mocks/user.mock.go | 15 +++++ internal/service/user.go | 5 ++ internal/service/user_test.go | 37 +++++++++++++ internal/web/init_web.go | 1 + internal/web/user.go | 23 ++++++++ internal/web/user_test.go | 64 ++++++++++++++++++++++ 12 files changed, 286 insertions(+) diff --git a/internal/repository/dao/mocks/user.mock.go b/internal/repository/dao/mocks/user.mock.go index 61af189f..9c1f51ad 100644 --- a/internal/repository/dao/mocks/user.mock.go +++ b/internal/repository/dao/mocks/user.mock.go @@ -50,6 +50,21 @@ func (mr *MockUserDAOMockRecorder) FindByEmail(ctx, email interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByEmail", reflect.TypeOf((*MockUserDAO)(nil).FindByEmail), ctx, email) } +// FindById mocks base method. +func (m *MockUserDAO) FindById(ctx context.Context, id int64) (dao.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindById", ctx, id) + ret0, _ := ret[0].(dao.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindById indicates an expected call of FindById. +func (mr *MockUserDAOMockRecorder) FindById(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindById", reflect.TypeOf((*MockUserDAO)(nil).FindById), ctx, id) +} + // Insert mocks base method. func (m *MockUserDAO) Insert(ctx context.Context, u dao.User) error { m.ctrl.T.Helper() diff --git a/internal/repository/dao/user.go b/internal/repository/dao/user.go index a3887629..6413f4ed 100644 --- a/internal/repository/dao/user.go +++ b/internal/repository/dao/user.go @@ -19,6 +19,7 @@ type UserDAO interface { UpdateEmailVerified(ctx context.Context, email string) error UpdateUserProfile(ctx context.Context, u User) error FindByEmail(ctx context.Context, email string) (User, error) + FindById(ctx context.Context, id int64) (User, error) } type User struct { @@ -85,3 +86,9 @@ func (dao *GormUserDAO) UpdateUserProfile(ctx context.Context, u User) error { "update_time": now}).Error } + +func (dao *GormUserDAO) FindById(ctx context.Context, id int64) (User, error) { + var u User + err := dao.db.WithContext(ctx).First(&u, "id = ?", id).Error + return u, err +} diff --git a/internal/repository/dao/user_test.go b/internal/repository/dao/user_test.go index 70f6f5dc..2962d669 100644 --- a/internal/repository/dao/user_test.go +++ b/internal/repository/dao/user_test.go @@ -275,3 +275,47 @@ func TestGormUserDAO_UpdateUserProfile(t *testing.T) { }) } } + +func TestGormUserDAO_FindById(t *testing.T) { + testCases := []struct { + name string + ctx context.Context + id int64 + mock func(t *testing.T) *sql.DB + wantErr error + }{ + { + name: "查找成功", + ctx: context.Background(), + id: 1, + mock: func(t *testing.T) *sql.DB { + mockDB, mock, err := sqlmock.New() + require.NoError(t, err) + rows := sqlmock.NewRows([]string{"id", "email", "about_me", "nick_name", "birthday"}) + rows.AddRow(1, "abc@qq.com", "nice man", "abc", "2000-01-01") + mock.ExpectQuery("^SELECT \\* FROM `users` WHERE id = \\?").WillReturnRows(rows) + return mockDB + }, + wantErr: nil, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + db, err := gorm.Open(gormMysql.New(gormMysql.Config{ + Conn: tt.mock(t), + // 如果为 false ,则GORM在初始化时,会先调用 show version + SkipInitializeWithVersion: true, + }), &gorm.Config{ + // 如果为 true ,则不允许 Ping数据库 + DisableAutomaticPing: true, + // 如果为 false ,则即使是单一语句,也会开启事务 + SkipDefaultTransaction: true, + }) + require.NoError(t, err) + dao := NewUserInfoDAO(db) + _, err = dao.FindById(tt.ctx, tt.id) + assert.Equal(t, tt.wantErr, err) + }) + } + +} diff --git a/internal/repository/mocks/user.mock.go b/internal/repository/mocks/user.mock.go index fcdd1cee..7a4554d2 100644 --- a/internal/repository/mocks/user.mock.go +++ b/internal/repository/mocks/user.mock.go @@ -64,6 +64,21 @@ func (mr *MockUserRepositoryMockRecorder) FindByEmail(ctx, email interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByEmail", reflect.TypeOf((*MockUserRepository)(nil).FindByEmail), ctx, email) } +// FindById mocks base method. +func (m *MockUserRepository) FindById(ctx context.Context, id int64) (domain.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindById", ctx, id) + ret0, _ := ret[0].(domain.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindById indicates an expected call of FindById. +func (mr *MockUserRepositoryMockRecorder) FindById(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindById", reflect.TypeOf((*MockUserRepository)(nil).FindById), ctx, id) +} + // UpdateEmailVerified mocks base method. func (m *MockUserRepository) UpdateEmailVerified(ctx context.Context, email string) error { m.ctrl.T.Helper() diff --git a/internal/repository/user.go b/internal/repository/user.go index ea1948a2..2833db70 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -17,6 +17,7 @@ type UserRepository interface { UpdateEmailVerified(ctx context.Context, email string) error FindByEmail(ctx context.Context, email string) (domain.User, error) UpdateUserProfile(ctx context.Context, u domain.User) error + FindById(ctx context.Context, id int64) (domain.User, error) } type UserInfoRepository struct { @@ -35,6 +36,9 @@ func (ur *UserInfoRepository) userToDomain(u dao.User) domain.User { EmailVerified: u.EmailVerified, Email: u.Email, Password: u.Password, + AboutMe: u.AboutMe.String, + Birthday: u.Birthday.String, + NickName: u.NickName.String, } } @@ -77,3 +81,11 @@ func (ur *UserInfoRepository) UpdateUserProfile(ctx context.Context, u domain.Us }, }) } + +func (ur *UserInfoRepository) FindById(ctx context.Context, id int64) (domain.User, error) { + user, err := ur.dao.FindById(ctx, id) + if err != nil { + return domain.User{}, err + } + return ur.userToDomain(user), err +} diff --git a/internal/repository/user_test.go b/internal/repository/user_test.go index 08d01041..f6a4d5d0 100644 --- a/internal/repository/user_test.go +++ b/internal/repository/user_test.go @@ -2,6 +2,7 @@ package repository import ( "context" + "database/sql" "errors" "testing" "time" @@ -185,3 +186,50 @@ func TestUserInfoRepository_UpdateUserProfile(t *testing.T) { }) } } + +func TestUserInfoRepository_FindById(t *testing.T) { + testCases := []struct { + name string + mock func(*gomock.Controller) dao.UserDAO + ctx context.Context + id int64 + wantErr error + }{ + { + name: "查找成功!", + ctx: context.Background(), + mock: func(ctrl *gomock.Controller) dao.UserDAO { + d := daomocks.NewMockUserDAO(ctrl) + d.EXPECT().FindById(gomock.Any(), gomock.Any()).Return(dao.User{ + Id: 1, + Email: "abc@qq.com", + NickName: sql.NullString{ + String: "frankiejun", + Valid: true, + }, + Birthday: sql.NullString{ + String: "1999-01-01", + Valid: true, + }, + AboutMe: sql.NullString{ + String: "I am a good boy", + Valid: true, + }, + }, nil) + return d + }, + id: 1, + wantErr: nil, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + repo := NewUserInfoRepository(tc.mock(ctrl)) + _, err := repo.FindById(tc.ctx, tc.id) + assert.Equal(t, tc.wantErr, err) + }) + } +} diff --git a/internal/service/mocks/user.mock.go b/internal/service/mocks/user.mock.go index afc287b3..33972e6f 100644 --- a/internal/service/mocks/user.mock.go +++ b/internal/service/mocks/user.mock.go @@ -49,6 +49,21 @@ func (mr *MockUserServiceMockRecorder) EditUserProfile(ctx, u interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EditUserProfile", reflect.TypeOf((*MockUserService)(nil).EditUserProfile), ctx, u) } +// Profile mocks base method. +func (m *MockUserService) Profile(ctx context.Context, id int64) (domain.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Profile", ctx, id) + ret0, _ := ret[0].(domain.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Profile indicates an expected call of Profile. +func (mr *MockUserServiceMockRecorder) Profile(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Profile", reflect.TypeOf((*MockUserService)(nil).Profile), ctx, id) +} + // SendVerifyEmail mocks base method. func (m *MockUserService) SendVerifyEmail(ctx context.Context, email string) error { m.ctrl.T.Helper() diff --git a/internal/service/user.go b/internal/service/user.go index 304de05d..78f29fbb 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -26,6 +26,7 @@ type UserService interface { SendVerifyEmail(ctx context.Context, email string) error VerifyEmail(ctx context.Context, tokenStr string) error EditUserProfile(ctx context.Context, u domain.User) error + Profile(ctx context.Context, id int64) (domain.User, error) } type userService struct { @@ -84,3 +85,7 @@ func (svc *userService) VerifyEmail(ctx context.Context, tokenStr string) error func (svc *userService) EditUserProfile(ctx context.Context, u domain.User) error { return svc.r.UpdateUserProfile(ctx, u) } + +func (svc *userService) Profile(ctx context.Context, id int64) (domain.User, error) { + return svc.r.FindById(ctx, id) +} diff --git a/internal/service/user_test.go b/internal/service/user_test.go index 99073308..3fca7dd4 100644 --- a/internal/service/user_test.go +++ b/internal/service/user_test.go @@ -173,3 +173,40 @@ func Test_userService_EditUserProfile(t *testing.T) { }) } } + +func Test_userService_Profile(t *testing.T) { + testCases := []struct { + name string + ctx context.Context + mock func(*gomock.Controller) repository.UserRepository + id int64 + wantErr error + }{ + { + name: "查找成功", + ctx: context.Background(), + mock: func(ctrl *gomock.Controller) repository.UserRepository { + mock := repomocks.NewMockUserRepository(ctrl) + mock.EXPECT().FindById(gomock.Any(), gomock.Any()).Return(domain.User{ + Id: 1, + NickName: "frankiejun", + Birthday: "2020-01-01", + AboutMe: "I am a good boy", + }, nil) + return mock + }, + id: 1, + wantErr: nil, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + svc := NewUserService(tc.mock(ctrl), nil) + _, err := svc.Profile(tc.ctx, tc.id) + assert.Equal(t, tc.wantErr, err) + }) + } +} diff --git a/internal/web/init_web.go b/internal/web/init_web.go index 82480365..e4c8e04b 100644 --- a/internal/web/init_web.go +++ b/internal/web/init_web.go @@ -7,4 +7,5 @@ func (u *UserHandler) RegisterRoutes(server *gin.Engine) { server.POST("/users/email/verify/:token", u.EmailVerify) server.POST("/users/login", u.Login) server.POST("/users/edit", u.Edit) + server.POST("/users/profile", u.Profile) } diff --git a/internal/web/user.go b/internal/web/user.go index 010391e5..3b3d3e8e 100644 --- a/internal/web/user.go +++ b/internal/web/user.go @@ -128,6 +128,7 @@ func (u *UserHandler) Login(ctx *gin.Context) { ctx.String(http.StatusBadRequest, "系统错误") return } + ctx.Set("userid", uid) ctx.String(http.StatusOK, "登陆成功") } @@ -213,3 +214,25 @@ func (c *UserHandler) Edit(ctx *gin.Context) { } ctx.String(http.StatusOK, "更新成功") } + +func (c *UserHandler) Profile(ctx *gin.Context) { + type Profile struct { + Email string + NickName string + Birthday string + AboutMe string + } + + id := ctx.MustGet("userid").(int64) + u, err := c.svc.Profile(ctx, id) + if err != nil { + ctx.String(http.StatusOK, "系统错误") + return + } + ctx.JSON(http.StatusOK, Profile{ + Email: u.Email, + NickName: u.NickName, + Birthday: u.Birthday, + AboutMe: u.AboutMe, + }) +} diff --git a/internal/web/user_test.go b/internal/web/user_test.go index 3160bf06..47e781b1 100644 --- a/internal/web/user_test.go +++ b/internal/web/user_test.go @@ -465,3 +465,67 @@ func TestUserHandler_Edit(t *testing.T) { }) } } + +func TestUserHandler_Profile(t *testing.T) { + testCases := []struct { + name string + mock func(ctrl *gomock.Controller) service.UserService + body string + wantCode int + wantBody string + }{ + { + name: "查看详细资料", + mock: func(ctrl *gomock.Controller) service.UserService { + userSvc := svcmocks.NewMockUserService(ctrl) + userSvc.EXPECT().Profile(gomock.Any(), gomock.Any()).Return(domain.User{ + Email: "abc@qq.com", + NickName: "abc", + Birthday: "2020-01-01", + AboutMe: "i am a good boy", + }, nil) + return userSvc + }, + wantCode: http.StatusOK, + wantBody: `{"Email":"abc@qq.com","NickName":"abc","Birthday":"2020-01-01","AboutMe":"i am a good boy"}`, + }, + { + name: "查不到资料", + mock: func(ctrl *gomock.Controller) service.UserService { + userSvc := svcmocks.NewMockUserService(ctrl) + userSvc.EXPECT().Profile(gomock.Any(), gomock.Any()).Return(domain.User{}, errors.New("data not found")) + return userSvc + }, + wantCode: http.StatusOK, + wantBody: "系统错误", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + r := gin.Default() + h := NewUserHandler(tc.mock(ctrl)) + + // 添加一个 路由 + r.POST("/users/profile", func(ctx *gin.Context) { + ctx.Set("userid", int64(1)) // 模拟设置用户ID + h.Profile(ctx) + }) + + req, err := http.NewRequest(http.MethodPost, "/users/profile", nil) + + require.NoError(t, err) + + req.Header.Set("Content-Type", "application/json") + resp := httptest.NewRecorder() + + r.ServeHTTP(resp, req) + + //body, err := json.Marshal(resp.Body) + assert.Equal(t, tc.wantCode, resp.Code) + assert.Equal(t, tc.wantBody, resp.Body.String()) + }) + } +}