feat: P6 어댑터 PoC 보강 — Resp 노출 / phone 매핑 / 재시도 배치 / SDK 분기
1. WithdrawRequestResp / VO / Mapper.xml / 화면에 transfer_tx_id/transfer_status/transfer_at 노출 2. UserMapper.selectFirstActiveContactByRoleCode — step.roleCode → user.phone 1차 매핑 - ApprovalService.sendStepNotification 이 실제 결재자 user_id/phone 사용 (기존 0L 하드코딩 해소) - phone 미확보 시 SMS skip (DB 알림은 유지) 3. 펌뱅킹 재시도 — WithdrawBankRetryService + POST /api/internal/bank-retry - WithdrawRequestMapper.selectFailedTransfers 추가 - @EnableScheduling + cron 옵션(기본 비활성), app.bank-retry.limit 4. 어댑터 프로파일 분기 — @ConditionalOnProperty - app.adapter.bank: mock(기본) | kftc → KftcBankTransferAdapter 스켈레톤 - app.adapter.message: mock(기본) | toast → ToastMessageAdapter 스켈레톤 - 활성화 시 UnsupportedOperationException 명시 5. V83(menu_code 오기로 0건 적용) + V84(실제 WITHDRAW 메뉴에 EXECUTE 권한 보강)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.ga.core.mapper.user;
|
||||
|
||||
import com.ga.core.vo.user.UserContactBrief;
|
||||
import com.ga.core.vo.user.UserResp;
|
||||
import com.ga.core.vo.user.UserSearchParam;
|
||||
import com.ga.core.vo.user.UserVO;
|
||||
@@ -30,4 +31,10 @@ public interface UserMapper {
|
||||
|
||||
int insertUserRole(@Param("userId") Long userId, @Param("roleId") Long roleId);
|
||||
int deleteUserRoles(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 특정 role_code 보유 + ACTIVE 상태 사용자 중 최선임(또는 가장 최근 가입) 1명의 연락처 요약.
|
||||
* approval 단계 알림 발송 시 phone 획득용. 없으면 null 반환.
|
||||
*/
|
||||
UserContactBrief selectFirstActiveContactByRoleCode(@Param("roleCode") String roleCode);
|
||||
}
|
||||
|
||||
@@ -27,4 +27,10 @@ public interface WithdrawRequestMapper {
|
||||
@Param("transferStatus") String transferStatus,
|
||||
@Param("nextStatus") String nextStatus,
|
||||
@Param("failReason") String failReason);
|
||||
|
||||
/**
|
||||
* 펌뱅킹 재시도 대상 조회 — transfer_status='FAILED' 이고 status='APPROVED' 상태.
|
||||
* account_no 는 복호화되어 반환.
|
||||
*/
|
||||
List<WithdrawRequestVO> selectFailedTransfers(@Param("limit") int limit);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ga.core.vo.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 알림/메시지 발송 등 외부 채널용 사용자 연락처 요약.
|
||||
*/
|
||||
@Data
|
||||
public class UserContactBrief {
|
||||
private Long userId;
|
||||
private String userName;
|
||||
private String phone;
|
||||
}
|
||||
@@ -24,4 +24,11 @@ public class WithdrawRequestResp {
|
||||
private LocalDateTime sentAt;
|
||||
private LocalDateTime completedAt;
|
||||
private String failReason;
|
||||
|
||||
/** 펌뱅킹 외부 거래 ID (멱등키) */
|
||||
private String transferTxId;
|
||||
/** ACCEPTED/SETTLED/FAILED — BankTransferStatus 코드 */
|
||||
private String transferStatus;
|
||||
/** 펌뱅킹 호출/응답 시각 */
|
||||
private LocalDateTime transferAt;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public class WithdrawRequestVO {
|
||||
private LocalDateTime sentAt;
|
||||
private LocalDateTime completedAt;
|
||||
private String failReason;
|
||||
private String transferTxId;
|
||||
private String transferStatus;
|
||||
private LocalDateTime transferAt;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
<resultMap id="UserVOMap" type="com.ga.core.vo.user.UserVO"/>
|
||||
<resultMap id="UserRespMap" type="com.ga.core.vo.user.UserResp"/>
|
||||
|
||||
<resultMap id="UserContactBriefMap" type="com.ga.core.vo.user.UserContactBrief">
|
||||
<id property="userId" column="user_id"/>
|
||||
<result property="userName" column="user_name"/>
|
||||
<result property="phone" column="phone"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="UserCols">
|
||||
u.user_id, u.login_id, u.password, u.user_name, u.email, u.phone, u.agent_id,
|
||||
u.status, u.fail_count, u.last_login_at, u.pwd_changed_at,
|
||||
@@ -119,4 +125,20 @@
|
||||
DELETE FROM user_role WHERE user_id = #{userId}
|
||||
</delete>
|
||||
|
||||
<!--
|
||||
role_code 보유 + ACTIVE 사용자 1명의 연락처 요약.
|
||||
결재 단계 알림 시 단계 담당자의 phone 획득용.
|
||||
여러 명일 경우: user_id 오름차순 (가장 먼저 등록된 담당자 우선)
|
||||
-->
|
||||
<select id="selectFirstActiveContactByRoleCode" resultMap="UserContactBriefMap">
|
||||
SELECT u.user_id, u.user_name, u.phone
|
||||
FROM users u
|
||||
JOIN user_role ur ON ur.user_id = u.user_id
|
||||
JOIN role r ON r.role_id = ur.role_id
|
||||
WHERE r.role_code = #{roleCode}
|
||||
AND u.status = 'ACTIVE'
|
||||
ORDER BY u.user_id
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
<result property="sentAt" column="sent_at"/>
|
||||
<result property="completedAt" column="completed_at"/>
|
||||
<result property="failReason" column="fail_reason"/>
|
||||
<result property="transferTxId" column="transfer_tx_id"/>
|
||||
<result property="transferStatus" column="transfer_status"/>
|
||||
<result property="transferAt" column="transfer_at"/>
|
||||
<result property="createdAt" column="created_at"/>
|
||||
<result property="updatedAt" column="updated_at"/>
|
||||
</resultMap>
|
||||
@@ -46,6 +49,9 @@
|
||||
<result property="sentAt" column="sent_at"/>
|
||||
<result property="completedAt" column="completed_at"/>
|
||||
<result property="failReason" column="fail_reason"/>
|
||||
<result property="transferTxId" column="transfer_tx_id"/>
|
||||
<result property="transferStatus" column="transfer_status"/>
|
||||
<result property="transferAt" column="transfer_at"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="joinCols">
|
||||
@@ -57,7 +63,8 @@
|
||||
wr.status,
|
||||
(SELECT cc.code_name FROM common_code cc
|
||||
WHERE cc.group_code = 'WITHDRAW_STATUS' AND cc.code = wr.status) AS status_name,
|
||||
wr.sent_at, wr.completed_at, wr.fail_reason
|
||||
wr.sent_at, wr.completed_at, wr.fail_reason,
|
||||
wr.transfer_tx_id, wr.transfer_status, wr.transfer_at
|
||||
</sql>
|
||||
|
||||
<select id="selectById" resultMap="RespMap">
|
||||
@@ -74,6 +81,7 @@
|
||||
wr.requested_by, wr.requested_at, wr.approval_request_id,
|
||||
wr.bank_code, wr.account_no, wr.status,
|
||||
wr.sent_at, wr.completed_at, wr.fail_reason,
|
||||
wr.transfer_tx_id, wr.transfer_status, wr.transfer_at,
|
||||
wr.created_at, wr.updated_at
|
||||
FROM withdraw_request wr
|
||||
WHERE wr.request_id = #{requestId}
|
||||
@@ -140,6 +148,21 @@
|
||||
AND status IN ('REQUESTED', 'APPROVED')
|
||||
</update>
|
||||
|
||||
<!-- 펌뱅킹 재시도 대상 — 어댑터 실패로 transfer_status='FAILED' 상태인 출금 -->
|
||||
<select id="selectFailedTransfers" resultMap="VOMap">
|
||||
SELECT wr.request_id, wr.settle_master_id, wr.agent_id, wr.amount,
|
||||
wr.requested_by, wr.requested_at, wr.approval_request_id,
|
||||
wr.bank_code, wr.account_no, wr.status,
|
||||
wr.sent_at, wr.completed_at, wr.fail_reason,
|
||||
wr.transfer_tx_id, wr.transfer_status, wr.transfer_at,
|
||||
wr.created_at, wr.updated_at
|
||||
FROM withdraw_request wr
|
||||
WHERE wr.transfer_status = 'FAILED'
|
||||
AND wr.status = 'APPROVED'
|
||||
ORDER BY wr.transfer_at NULLS FIRST, wr.request_id
|
||||
LIMIT #{limit}
|
||||
</select>
|
||||
|
||||
<!-- 펌뱅킹 어댑터 응답 저장 -->
|
||||
<update id="updateTransferResult">
|
||||
UPDATE withdraw_request
|
||||
|
||||
Reference in New Issue
Block a user