5.1 사용자 초대 기능: - UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger - 초대 발송/수락/취소/재발송 API 5.2 알림설정 확장: - NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger - 채널별/유형별 알림 설정 관리 5.3 계정정보 수정 API: - 회원탈퇴, 사용중지, 약관동의 관리 - AccountService, AccountController, Swagger 5.4 매출 거래명세서 API: - 거래명세서 조회/발행/이메일발송 - SaleService 확장, Swagger 문서화
312 lines
12 KiB
PHP
312 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Swagger\v1;
|
|
|
|
/**
|
|
* @OA\Tag(
|
|
* name="UserInvitation",
|
|
* description="사용자 초대 관리"
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="UserInvitation",
|
|
* type="object",
|
|
* required={"id", "tenant_id", "email", "token", "status", "invited_by", "expires_at"},
|
|
*
|
|
* @OA\Property(property="id", type="integer", example=1),
|
|
* @OA\Property(property="tenant_id", type="integer", example=1),
|
|
* @OA\Property(property="email", type="string", format="email", example="user@example.com"),
|
|
* @OA\Property(property="role_id", type="integer", nullable=true, example=2),
|
|
* @OA\Property(property="message", type="string", nullable=true, example="회사에 합류해 주세요!"),
|
|
* @OA\Property(property="token", type="string", example="abc123..."),
|
|
* @OA\Property(property="status", type="string", enum={"pending", "accepted", "expired", "cancelled"}, example="pending"),
|
|
* @OA\Property(property="status_label", type="string", example="대기중"),
|
|
* @OA\Property(property="invited_by", type="integer", example=1),
|
|
* @OA\Property(property="expires_at", type="string", format="date-time"),
|
|
* @OA\Property(property="accepted_at", type="string", format="date-time", nullable=true),
|
|
* @OA\Property(property="created_at", type="string", format="date-time"),
|
|
* @OA\Property(property="updated_at", type="string", format="date-time"),
|
|
* @OA\Property(
|
|
* property="role",
|
|
* type="object",
|
|
* nullable=true,
|
|
* @OA\Property(property="id", type="integer"),
|
|
* @OA\Property(property="name", type="string")
|
|
* ),
|
|
* @OA\Property(
|
|
* property="inviter",
|
|
* type="object",
|
|
* @OA\Property(property="id", type="integer"),
|
|
* @OA\Property(property="name", type="string")
|
|
* )
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="UserInvitationPagination",
|
|
* type="object",
|
|
*
|
|
* @OA\Property(
|
|
* property="data",
|
|
* type="array",
|
|
*
|
|
* @OA\Items(ref="#/components/schemas/UserInvitation")
|
|
* ),
|
|
*
|
|
* @OA\Property(property="current_page", type="integer", example=1),
|
|
* @OA\Property(property="last_page", type="integer", example=5),
|
|
* @OA\Property(property="per_page", type="integer", example=20),
|
|
* @OA\Property(property="total", type="integer", example=100)
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="InviteUserRequest",
|
|
* type="object",
|
|
* required={"email"},
|
|
*
|
|
* @OA\Property(property="email", type="string", format="email", example="newuser@example.com", description="초대할 사용자 이메일"),
|
|
* @OA\Property(property="role_id", type="integer", nullable=true, example=2, description="부여할 역할 ID"),
|
|
* @OA\Property(property="message", type="string", nullable=true, example="SAM 시스템에 합류해 주세요!", description="초대 메시지"),
|
|
* @OA\Property(property="expires_days", type="integer", nullable=true, example=7, minimum=1, maximum=30, description="만료 기간(일)")
|
|
* )
|
|
*
|
|
* @OA\Schema(
|
|
* schema="AcceptInvitationRequest",
|
|
* type="object",
|
|
* required={"name", "password", "password_confirmation"},
|
|
*
|
|
* @OA\Property(property="name", type="string", example="홍길동", description="사용자 이름"),
|
|
* @OA\Property(property="password", type="string", format="password", example="password123", description="비밀번호 (8자 이상)"),
|
|
* @OA\Property(property="password_confirmation", type="string", format="password", example="password123", description="비밀번호 확인"),
|
|
* @OA\Property(property="phone", type="string", nullable=true, example="010-1234-5678", description="연락처")
|
|
* )
|
|
*/
|
|
class UserInvitationApi
|
|
{
|
|
/**
|
|
* @OA\Get(
|
|
* path="/api/v1/users/invitations",
|
|
* operationId="getUserInvitations",
|
|
* tags={"UserInvitation"},
|
|
* summary="초대 목록 조회",
|
|
* description="테넌트의 사용자 초대 목록을 조회합니다.",
|
|
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
|
*
|
|
* @OA\Parameter(
|
|
* name="status",
|
|
* in="query",
|
|
* description="상태 필터",
|
|
* required=false,
|
|
*
|
|
* @OA\Schema(type="string", enum={"pending", "accepted", "expired", "cancelled"})
|
|
* ),
|
|
*
|
|
* @OA\Parameter(
|
|
* name="search",
|
|
* in="query",
|
|
* description="이메일 검색",
|
|
* required=false,
|
|
*
|
|
* @OA\Schema(type="string")
|
|
* ),
|
|
*
|
|
* @OA\Parameter(
|
|
* name="sort_by",
|
|
* in="query",
|
|
* description="정렬 기준",
|
|
* required=false,
|
|
*
|
|
* @OA\Schema(type="string", enum={"created_at", "expires_at", "email"}, default="created_at")
|
|
* ),
|
|
*
|
|
* @OA\Parameter(
|
|
* name="sort_dir",
|
|
* in="query",
|
|
* description="정렬 방향",
|
|
* required=false,
|
|
*
|
|
* @OA\Schema(type="string", enum={"asc", "desc"}, default="desc")
|
|
* ),
|
|
*
|
|
* @OA\Parameter(
|
|
* name="per_page",
|
|
* in="query",
|
|
* description="페이지당 항목 수",
|
|
* required=false,
|
|
*
|
|
* @OA\Schema(type="integer", default=20, minimum=1, maximum=100)
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
*
|
|
* @OA\Property(property="success", type="boolean", example=true),
|
|
* @OA\Property(property="message", type="string", example="조회 완료"),
|
|
* @OA\Property(property="data", ref="#/components/schemas/UserInvitationPagination")
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=401, description="인증 실패"),
|
|
* @OA\Response(response=403, description="권한 없음")
|
|
* )
|
|
*/
|
|
public function index() {}
|
|
|
|
/**
|
|
* @OA\Post(
|
|
* path="/api/v1/users/invite",
|
|
* operationId="inviteUser",
|
|
* tags={"UserInvitation"},
|
|
* summary="사용자 초대",
|
|
* description="새로운 사용자를 테넌트에 초대합니다. 초대 이메일이 발송됩니다.",
|
|
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
|
*
|
|
* @OA\RequestBody(
|
|
* required=true,
|
|
*
|
|
* @OA\JsonContent(ref="#/components/schemas/InviteUserRequest")
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="초대 발송 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
*
|
|
* @OA\Property(property="success", type="boolean", example=true),
|
|
* @OA\Property(property="message", type="string", example="초대가 발송되었습니다"),
|
|
* @OA\Property(property="data", ref="#/components/schemas/UserInvitation")
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="이미 가입된 사용자 또는 대기 중인 초대 존재"),
|
|
* @OA\Response(response=401, description="인증 실패"),
|
|
* @OA\Response(response=422, description="유효성 검증 실패")
|
|
* )
|
|
*/
|
|
public function invite() {}
|
|
|
|
/**
|
|
* @OA\Post(
|
|
* path="/api/v1/users/invitations/{token}/accept",
|
|
* operationId="acceptInvitation",
|
|
* tags={"UserInvitation"},
|
|
* summary="초대 수락",
|
|
* description="초대를 수락하고 사용자 계정을 생성합니다. 인증 불필요.",
|
|
* security={{"ApiKeyAuth": {}}},
|
|
*
|
|
* @OA\Parameter(
|
|
* name="token",
|
|
* in="path",
|
|
* description="초대 토큰",
|
|
* required=true,
|
|
*
|
|
* @OA\Schema(type="string")
|
|
* ),
|
|
*
|
|
* @OA\RequestBody(
|
|
* required=true,
|
|
*
|
|
* @OA\JsonContent(ref="#/components/schemas/AcceptInvitationRequest")
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="초대 수락 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
*
|
|
* @OA\Property(property="success", type="boolean", example=true),
|
|
* @OA\Property(property="message", type="string", example="초대가 수락되었습니다"),
|
|
* @OA\Property(property="data", type="object",
|
|
* @OA\Property(property="id", type="integer"),
|
|
* @OA\Property(property="name", type="string"),
|
|
* @OA\Property(property="email", type="string")
|
|
* )
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="만료된 초대 또는 잘못된 상태"),
|
|
* @OA\Response(response=404, description="초대를 찾을 수 없음"),
|
|
* @OA\Response(response=422, description="유효성 검증 실패")
|
|
* )
|
|
*/
|
|
public function accept() {}
|
|
|
|
/**
|
|
* @OA\Delete(
|
|
* path="/api/v1/users/invitations/{id}",
|
|
* operationId="cancelInvitation",
|
|
* tags={"UserInvitation"},
|
|
* summary="초대 취소",
|
|
* description="대기 중인 초대를 취소합니다.",
|
|
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
|
*
|
|
* @OA\Parameter(
|
|
* name="id",
|
|
* in="path",
|
|
* description="초대 ID",
|
|
* required=true,
|
|
*
|
|
* @OA\Schema(type="integer")
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="취소 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
*
|
|
* @OA\Property(property="success", type="boolean", example=true),
|
|
* @OA\Property(property="message", type="string", example="초대가 취소되었습니다"),
|
|
* @OA\Property(property="data", type="boolean", example=true)
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="취소할 수 없는 상태"),
|
|
* @OA\Response(response=401, description="인증 실패"),
|
|
* @OA\Response(response=404, description="초대를 찾을 수 없음")
|
|
* )
|
|
*/
|
|
public function cancel() {}
|
|
|
|
/**
|
|
* @OA\Post(
|
|
* path="/api/v1/users/invitations/{id}/resend",
|
|
* operationId="resendInvitation",
|
|
* tags={"UserInvitation"},
|
|
* summary="초대 재발송",
|
|
* description="대기 중인 초대 이메일을 재발송합니다. 만료 기간이 연장됩니다.",
|
|
* security={{"ApiKeyAuth": {}}, {"BearerAuth": {}}},
|
|
*
|
|
* @OA\Parameter(
|
|
* name="id",
|
|
* in="path",
|
|
* description="초대 ID",
|
|
* required=true,
|
|
*
|
|
* @OA\Schema(type="integer")
|
|
* ),
|
|
*
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="재발송 성공",
|
|
*
|
|
* @OA\JsonContent(
|
|
*
|
|
* @OA\Property(property="success", type="boolean", example=true),
|
|
* @OA\Property(property="message", type="string", example="초대가 재발송되었습니다"),
|
|
* @OA\Property(property="data", ref="#/components/schemas/UserInvitation")
|
|
* )
|
|
* ),
|
|
*
|
|
* @OA\Response(response=400, description="재발송할 수 없는 상태"),
|
|
* @OA\Response(response=401, description="인증 실패"),
|
|
* @OA\Response(response=404, description="초대를 찾을 수 없음")
|
|
* )
|
|
*/
|
|
public function resend() {}
|
|
}
|