Merge branch 'dev' into main
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
package com.sb.web.admin.controller;
|
||||||
|
|
||||||
|
import com.sb.web.admin.dto.RoleUpdateRequest;
|
||||||
|
import com.sb.web.admin.dto.StatusUpdateRequest;
|
||||||
|
import com.sb.web.admin.service.MemberAdminService;
|
||||||
|
import com.sb.web.auth.dto.MemberAdminResponse;
|
||||||
|
import com.sb.web.auth.dto.SessionUser;
|
||||||
|
import com.sb.web.auth.web.AuthInterceptor;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 관리자용 회원 관리 API. (/api/admin/members — AdminInterceptor 로 관리자만 접근)
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/admin/members")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MemberAdminController {
|
||||||
|
|
||||||
|
private final MemberAdminService memberAdminService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<MemberAdminResponse> list() {
|
||||||
|
return memberAdminService.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}/role")
|
||||||
|
public MemberAdminResponse updateRole(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Valid @RequestBody RoleUpdateRequest req,
|
||||||
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
||||||
|
return memberAdminService.updateRole(id, req.getRole(), current.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}/status")
|
||||||
|
public MemberAdminResponse updateStatus(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Valid @RequestBody StatusUpdateRequest req,
|
||||||
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
||||||
|
return memberAdminService.updateStatus(id, req.getStatus(), current.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Void> delete(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
||||||
|
memberAdminService.delete(id, current.getId());
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.sb.web.admin.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회원 역할 변경 요청.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RoleUpdateRequest {
|
||||||
|
|
||||||
|
@Pattern(regexp = "USER|ADMIN", message = "역할은 USER 또는 ADMIN 이어야 합니다.")
|
||||||
|
private String role;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.sb.web.admin.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회원 상태 변경 요청.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class StatusUpdateRequest {
|
||||||
|
|
||||||
|
@Pattern(regexp = "ACTIVE|SUSPENDED|WITHDRAWN", message = "상태 값이 올바르지 않습니다.")
|
||||||
|
private String status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.sb.web.admin.service;
|
||||||
|
|
||||||
|
import com.sb.web.auth.domain.Member;
|
||||||
|
import com.sb.web.auth.dto.MemberAdminResponse;
|
||||||
|
import com.sb.web.auth.mapper.MemberMapper;
|
||||||
|
import com.sb.web.common.exception.ApiException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 관리자용 회원 관리 서비스. (목록/역할/상태/삭제)
|
||||||
|
* 본인 계정에 대한 역할·상태·삭제는 잠금 방지를 위해 차단한다.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MemberAdminService {
|
||||||
|
|
||||||
|
private final MemberMapper memberMapper;
|
||||||
|
|
||||||
|
public List<MemberAdminResponse> list() {
|
||||||
|
return memberMapper.findAll().stream().map(MemberAdminResponse::from).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MemberAdminResponse updateRole(Long targetId, String role, Long currentAdminId) {
|
||||||
|
Member target = mustFind(targetId);
|
||||||
|
if (targetId.equals(currentAdminId)) {
|
||||||
|
throw new ApiException(HttpStatus.BAD_REQUEST, "본인의 역할은 변경할 수 없습니다.");
|
||||||
|
}
|
||||||
|
memberMapper.updateRole(targetId, role);
|
||||||
|
target.setRole(role);
|
||||||
|
return MemberAdminResponse.from(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MemberAdminResponse updateStatus(Long targetId, String status, Long currentAdminId) {
|
||||||
|
Member target = mustFind(targetId);
|
||||||
|
if (targetId.equals(currentAdminId)) {
|
||||||
|
throw new ApiException(HttpStatus.BAD_REQUEST, "본인의 상태는 변경할 수 없습니다.");
|
||||||
|
}
|
||||||
|
memberMapper.updateStatus(targetId, status);
|
||||||
|
target.setStatus(status);
|
||||||
|
return MemberAdminResponse.from(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void delete(Long targetId, Long currentAdminId) {
|
||||||
|
mustFind(targetId);
|
||||||
|
if (targetId.equals(currentAdminId)) {
|
||||||
|
throw new ApiException(HttpStatus.BAD_REQUEST, "본인 계정은 삭제할 수 없습니다.");
|
||||||
|
}
|
||||||
|
memberMapper.deleteById(targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Member mustFind(Long id) {
|
||||||
|
Member m = memberMapper.findById(id);
|
||||||
|
if (m == null) {
|
||||||
|
throw new ApiException(HttpStatus.NOT_FOUND, "회원을 찾을 수 없습니다.");
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.sb.web.auth.dto;
|
||||||
|
|
||||||
|
import com.sb.web.auth.domain.Member;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 관리자용 회원 정보 응답 (비밀번호 제외).
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class MemberAdminResponse {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String loginId;
|
||||||
|
private String name;
|
||||||
|
private String email;
|
||||||
|
private String role; // USER / ADMIN
|
||||||
|
private String status; // ACTIVE / SUSPENDED / WITHDRAWN
|
||||||
|
private String provider; // LOCAL / 소셜
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
public static MemberAdminResponse from(Member m) {
|
||||||
|
return MemberAdminResponse.builder()
|
||||||
|
.id(m.getId())
|
||||||
|
.loginId(m.getLoginId())
|
||||||
|
.name(m.getName())
|
||||||
|
.email(m.getEmail())
|
||||||
|
.role(m.getRole())
|
||||||
|
.status(m.getStatus())
|
||||||
|
.provider(m.getProvider())
|
||||||
|
.createdAt(m.getCreatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import com.sb.web.auth.domain.Member;
|
|||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회원 MyBatis 매퍼. SQL 은 resources/mapper/MemberMapper.xml 에 정의.
|
* 회원 MyBatis 매퍼. SQL 은 resources/mapper/MemberMapper.xml 에 정의.
|
||||||
*/
|
*/
|
||||||
@@ -12,6 +14,9 @@ public interface MemberMapper {
|
|||||||
|
|
||||||
Member findById(@Param("id") Long id);
|
Member findById(@Param("id") Long id);
|
||||||
|
|
||||||
|
/** 관리자: 전체 회원 목록 */
|
||||||
|
List<Member> findAll();
|
||||||
|
|
||||||
Member findByLoginId(@Param("loginId") String loginId);
|
Member findByLoginId(@Param("loginId") String loginId);
|
||||||
|
|
||||||
Member findByProvider(@Param("provider") String provider,
|
Member findByProvider(@Param("provider") String provider,
|
||||||
@@ -22,4 +27,10 @@ public interface MemberMapper {
|
|||||||
int insert(Member member);
|
int insert(Member member);
|
||||||
|
|
||||||
int updatePassword(@Param("id") Long id, @Param("password") String password);
|
int updatePassword(@Param("id") Long id, @Param("password") String password);
|
||||||
|
|
||||||
|
int updateRole(@Param("id") Long id, @Param("role") String role);
|
||||||
|
|
||||||
|
int updateStatus(@Param("id") Long id, @Param("status") String status);
|
||||||
|
|
||||||
|
int deleteById(@Param("id") Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
SELECT <include refid="columns"/> FROM member WHERE id = #{id}
|
SELECT <include refid="columns"/> FROM member WHERE id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="findAll" resultMap="memberResultMap">
|
||||||
|
SELECT <include refid="columns"/> FROM member ORDER BY id
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="findByLoginId" parameterType="string" resultMap="memberResultMap">
|
<select id="findByLoginId" parameterType="string" resultMap="memberResultMap">
|
||||||
SELECT <include refid="columns"/> FROM member WHERE login_id = #{loginId}
|
SELECT <include refid="columns"/> FROM member WHERE login_id = #{loginId}
|
||||||
</select>
|
</select>
|
||||||
@@ -50,4 +54,16 @@
|
|||||||
UPDATE member SET password = #{password}, updated_at = NOW() WHERE id = #{id}
|
UPDATE member SET password = #{password}, updated_at = NOW() WHERE id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<update id="updateRole">
|
||||||
|
UPDATE member SET role = #{role}, updated_at = NOW() WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="updateStatus">
|
||||||
|
UPDATE member SET status = #{status}, updated_at = NOW() WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<delete id="deleteById">
|
||||||
|
DELETE FROM member WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user