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

회원탈퇴 API를 추가합니다. #47

Merged
merged 2 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions cmd/server/handler/user_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,28 @@ func (h *UserHandler) UpdateMyProfile(w http.ResponseWriter, r *http.Request) {
})
}

// DeleteMyAccount godoc
// @Summary 내 계정을 삭제합니다.
// @Description
// @Tags users
// @Security FirebaseAuth
// @Success 204
// @Router /users/me [delete]
func (h *UserHandler) DeleteMyAccount(w http.ResponseWriter, r *http.Request) {
user, err := h.authService.VerifyAuthAndGetUser(r.Context(), r)
if err != nil {
render.Render(w, r, err)
return
}

if err := h.userService.DeleteUserByUID(r.Context(), user.FirebaseUID); err != nil {
render.Render(w, r, err)
return
}

render.Status(r, http.StatusNoContent)
}

// AddMyPets godoc
// @Summary 내 반려동물을 등록합니다.
// @Description
Expand Down
1 change: 1 addition & 0 deletions cmd/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func NewRouter(app *firebaseinfra.FirebaseApp) *chi.Mux {
r.Get("/", userHandler.FindUsers)
r.Get("/me", userHandler.FindMyProfile)
r.Put("/me", userHandler.UpdateMyProfile)
r.Delete("/me", userHandler.DeleteMyAccount)
r.Get("/me/pets", userHandler.FindMyPets)
r.Put("/me/pets", userHandler.AddMyPets)
})
Expand Down
8 changes: 4 additions & 4 deletions db/migrations/000008_create_posts_table.down.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
DROP TABLE IF EXISTS sos_posts;
DROP TABLE IF EXISTS resource_media;

DROP TABLE IF EXISTS sos_conditions;
DROP TABLE IF EXISTS sos_posts_pets;

DROP TABLE IF EXISTS sos_posts_conditions;

DROP TABLE IF EXISTS sos_posts_pets;
DROP TABLE IF EXISTS sos_conditions;

DROP TABLE IF EXISTS resource_media;
DROP TABLE IF EXISTS sos_posts;
22 changes: 22 additions & 0 deletions db/migrations/000011_deleted_user_uix.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
DROP INDEX users_email_uix_null;

DROP INDEX users_fb_uid_uix_null;

DROP INDEX users_nickname_uix_null;

DROP INDEX users_email_idx;

DROP INDEX users_fb_uid_idx;

ALTER TABLE
users
ADD
CONSTRAINT users_email_uix UNIQUE (email),
ADD
CONSTRAINT users_fb_uid_uix UNIQUE (fb_uid),
ADD
CONSTRAINT users_nickname_uix UNIQUE (nickname);

CREATE INDEX users_email_idx ON users (email);

CREATE INDEX users_fb_uid_idx ON users (fb_uid);
26 changes: 26 additions & 0 deletions db/migrations/000011_deleted_user_uix.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- delete previous index and unique constraint
ALTER TABLE
users DROP CONSTRAINT users_email_uix,
DROP CONSTRAINT users_fb_uid_uix,
DROP CONSTRAINT users_nickname_uix;

DROP INDEX users_email_idx;

DROP INDEX users_fb_uid_idx;

-- add the index and unique constraint with deleted_at
CREATE UNIQUE INDEX users_email_uix_null ON users (email)
WHERE
deleted_at IS NULL;

CREATE UNIQUE INDEX users_fb_uid_uix_null ON users (fb_uid)
WHERE
deleted_at IS NULL;

CREATE UNIQUE INDEX users_nickname_uix_null ON users (nickname)
WHERE
deleted_at IS NULL;

CREATE INDEX users_email_idx ON users (email);

CREATE INDEX users_fb_uid_idx ON users (fb_uid);
18 changes: 18 additions & 0 deletions internal/domain/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ type UserWithProfileImage struct {
DeletedAt string `field:"deleted_at"`
}

func (u *UserWithProfileImage) ToUserWithoutPrivateInfo() *UserWithoutPrivateInfo {
if u.DeletedAt != "" {
return &UserWithoutPrivateInfo{
ID: u.ID,
Nickname: "탈퇴한 사용자",
ProfileImageURL: nil,
}
}

return &UserWithoutPrivateInfo{
ID: u.ID,
Nickname: u.Nickname,
ProfileImageURL: u.ProfileImageURL,
}
}

type UserWithoutPrivateInfo struct {
ID int `field:"id" json:"id"`
Nickname string `field:"nickname" json:"nickname"`
Expand All @@ -66,10 +82,12 @@ type UserStatus struct {
type UserStore interface {
CreateUser(ctx context.Context, request *RegisterUserRequest) (*User, *pnd.AppError)
FindUsers(ctx context.Context, page int, size int, nickname *string) (*UserWithoutPrivateInfoList, *pnd.AppError)
FindUserByID(ctx context.Context, id int, includeDeleted bool) (*UserWithProfileImage, *pnd.AppError)
FindUserByEmail(ctx context.Context, email string) (*UserWithProfileImage, *pnd.AppError)
FindUserByUID(ctx context.Context, uid string) (*UserWithProfileImage, *pnd.AppError)
FindUserIDByFbUID(ctx context.Context, fbUid string) (int, *pnd.AppError)
ExistsByNickname(ctx context.Context, nickname string) (bool, *pnd.AppError)
FindUserStatusByEmail(ctx context.Context, email string) (*UserStatus, *pnd.AppError)
UpdateUserByUID(ctx context.Context, uid string, nickname string, profileImageID *int) (*User, *pnd.AppError)
DeleteUserByUID(ctx context.Context, uid string) *pnd.AppError
}
68 changes: 68 additions & 0 deletions internal/postgres/user_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func (s *UserPostgresStore) FindUsers(ctx context.Context, page int, size int, n
return (&userQueries{conn: s.conn}).FindUsers(ctx, page, size, nickname)
}

func (s *UserPostgresStore) FindUserByID(ctx context.Context, id int, includeDeleted bool) (*user.UserWithProfileImage, *pnd.AppError) {
return (&userQueries{conn: s.conn}).FindUserByID(ctx, id, includeDeleted)
}

func (s *UserPostgresStore) FindUserByEmail(ctx context.Context, email string) (*user.UserWithProfileImage, *pnd.AppError) {
return (&userQueries{conn: s.conn}).FindUserByEmail(ctx, email)
}
Expand All @@ -50,6 +54,10 @@ func (s *UserPostgresStore) UpdateUserByUID(ctx context.Context, uid string, nic
return (&userQueries{conn: s.conn}).UpdateUserByUID(ctx, uid, nickname, profileImageID)
}

func (s *UserPostgresStore) DeleteUserByUID(ctx context.Context, uid string) *pnd.AppError {
return (&userQueries{conn: s.conn}).DeleteUserByUID(ctx, uid)
}

type userQueries struct {
conn database.DBTx
}
Expand Down Expand Up @@ -145,6 +153,49 @@ func (s *userQueries) FindUsers(ctx context.Context, page int, size int, nicknam
return userList, nil
}

func (s *userQueries) FindUserByID(ctx context.Context, id int, includeDeleted bool) (*user.UserWithProfileImage, *pnd.AppError) {
const sql = `
SELECT
users.id,
users.email,
users.nickname,
users.fullname,
media.url AS profile_image_url,
users.fb_provider_type,
users.fb_uid,
users.created_at,
users.updated_at
users.deleted_at
FROM
users
LEFT OUTER JOIN
media
ON
users.profile_image_id = media.id
WHERE
users.id = $1 AND
(users.deleted_at IS NULL OR $2)
`

var user user.UserWithProfileImage
if err := s.conn.QueryRowContext(ctx, sql, id, includeDeleted).Scan(
&user.ID,
&user.Email,
&user.Nickname,
&user.Fullname,
&user.ProfileImageURL,
&user.FirebaseProviderType,
&user.FirebaseUID,
&user.CreatedAt,
&user.UpdatedAt,
&user.DeletedAt,
); err != nil {
return nil, pnd.FromPostgresError(err)
}

return &user, nil
}

func (s *userQueries) FindUserByEmail(ctx context.Context, email string) (*user.UserWithProfileImage, *pnd.AppError) {
const sql = `
SELECT
Expand Down Expand Up @@ -335,3 +386,20 @@ func (s *userQueries) UpdateUserByUID(ctx context.Context, uid string, nickname

return &user, nil
}

func (s *userQueries) DeleteUserByUID(ctx context.Context, uid string) *pnd.AppError {
const sql = `
UPDATE
users
SET
deleted_at = NOW()
WHERE
fb_uid = $1
`

if _, err := s.conn.ExecContext(ctx, sql, uid); err != nil {
return pnd.FromPostgresError(err)
}

return nil
}
38 changes: 38 additions & 0 deletions internal/service/user_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,30 @@ func (service *UserService) FindUsers(ctx context.Context, page int, size int, n
return userList, nil
}

// FindMyProfile은 사용자의 프로필 정보를 조회한다.
// 삭제된 유저의 경우 삭제된 유저 정보를 반환한다.
func (service *UserService) FindPublicUserByID(ctx context.Context, id int) (*user.UserWithoutPrivateInfo, *pnd.AppError) {
var err *pnd.AppError

var user *user.UserWithProfileImage
err = database.WithTransaction(ctx, service.conn, func(tx *database.Tx) *pnd.AppError {
userStore := postgres.NewUserPostgresStore(tx)

user, err = userStore.FindUserByID(ctx, id, true)
if err != nil {
return err
}

return nil
})

if err != nil {
return nil, err
}

return user.ToUserWithoutPrivateInfo(), nil
}

func (service *UserService) FindUserByEmail(ctx context.Context, email string) (*user.UserWithProfileImage, *pnd.AppError) {
var user *user.UserWithProfileImage
var err *pnd.AppError
Expand Down Expand Up @@ -221,6 +245,20 @@ func (service *UserService) UpdateUserByUID(ctx context.Context, uid string, nic
return userView, nil
}

func (service *UserService) DeleteUserByUID(ctx context.Context, uid string) *pnd.AppError {
err := database.WithTransaction(ctx, service.conn, func(tx *database.Tx) *pnd.AppError {
userStore := postgres.NewUserPostgresStore(tx)

if err := userStore.DeleteUserByUID(ctx, uid); err != nil {
return err
}

return nil
})

return err
}

func (service *UserService) AddPetsToOwner(ctx context.Context, uid string, addPetsRequest pet.AddPetsToOwnerRequest) ([]pet.PetView, *pnd.AppError) {
var petViews []pet.PetView

Expand Down