feat(대무자): 결재 시스템 어댑터 통합 — USER_SUBSTITUTES read + IN(effective_user_ids)
- approval.xml selectActiveProxyForLine: APPROVAL_PROXY_SETTINGS → USER_SUBSTITUTES 참조
(BOOLEAN IS_ACTIVE, START_DATE NULL 허용)
- approval.xml 3곳 (selectRequests/countRequests/selectMyPendingLines):
APPROVER_ID = #{user_id} → APPROVER_ID IN (effective_user_ids) foreach
- ApprovalService.ensureEffectiveUserIds helper — mybatis 빈 IN() 방지
- ApprovalController: getRequests/getMyPendingLines 에 @RequestAttribute(effective_user_ids) 추가
- ApprovalService.processApproval 마지막에 AuditLogService.insertAuditLog 호출 추가
(user_id=A, processor_id=B 분리 기록)
This commit is contained in:
@@ -190,9 +190,11 @@ public class ApprovalController {
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getRequests(
|
||||
@RequestParam Map<String, Object> params,
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestAttribute(name = "effective_user_ids", required = false) List<String> effectiveUserIds) {
|
||||
params.put("company_code", companyCode);
|
||||
params.put("user_id", userId);
|
||||
params.put("effective_user_ids", effectiveUserIds);
|
||||
return ResponseEntity.ok(ApiResponse.success(approvalService.getRequests(params)));
|
||||
}
|
||||
|
||||
@@ -277,10 +279,12 @@ public class ApprovalController {
|
||||
@GetMapping("/my-pending")
|
||||
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getMyPendingLines(
|
||||
@RequestAttribute("company_code") String companyCode,
|
||||
@RequestAttribute("user_id") String userId) {
|
||||
@RequestAttribute("user_id") String userId,
|
||||
@RequestAttribute(name = "effective_user_ids", required = false) List<String> effectiveUserIds) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("company_code", companyCode);
|
||||
params.put("user_id", userId);
|
||||
params.put("effective_user_ids", effectiveUserIds);
|
||||
return ResponseEntity.ok(ApiResponse.success(approvalService.getMyPendingLines(params)));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,26 @@ public class ApprovalService extends BaseService {
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private AuditLogService auditLogService;
|
||||
|
||||
/**
|
||||
* IN (:effective_user_ids) 쿼리용 fallback.
|
||||
* SubstituteContextFilter 가 attribute 를 못 채운 경로(통합 테스트/배치 등) 에서도
|
||||
* 빈 IN () SQL 에러를 막기 위해 항상 최소 [user_id] 가 들어가도록 한다.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void ensureEffectiveUserIds(Map<String, Object> params) {
|
||||
Object v = params.get("effective_user_ids");
|
||||
boolean empty = v == null || (v instanceof Collection<?> && ((Collection<?>) v).isEmpty());
|
||||
if (empty) {
|
||||
Object userId = params.get("user_id");
|
||||
if (userId != null) {
|
||||
params.put("effective_user_ids", List.of(userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// approval_definitions
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
@@ -149,6 +169,7 @@ public class ApprovalService extends BaseService {
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
public Map<String, Object> getRequests(Map<String, Object> params) {
|
||||
ensureEffectiveUserIds(params);
|
||||
int page = toInt(params.getOrDefault("page", "1"));
|
||||
int limit = toInt(params.getOrDefault("limit", "20"));
|
||||
params.put("page_limit", limit);
|
||||
@@ -359,6 +380,7 @@ public class ApprovalService extends BaseService {
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
public List<Map<String, Object>> getMyPendingLines(Map<String, Object> params) {
|
||||
ensureEffectiveUserIds(params);
|
||||
return sqlSession.selectList("approval.selectMyPendingLines", params);
|
||||
}
|
||||
|
||||
@@ -456,6 +478,24 @@ public class ApprovalService extends BaseService {
|
||||
activateNextStep(requestId, stepOrder, totalSteps, lineCC, userId, comment);
|
||||
}
|
||||
}
|
||||
|
||||
// 결재 처리 audit log — 대무 시 user_id(A)와 processor_id(B) 분리 기록.
|
||||
// 실패는 본 처리를 막지 않음 (가용성 우선).
|
||||
try {
|
||||
Map<String, Object> auditP = new HashMap<>();
|
||||
auditP.put("company_code", lineCC);
|
||||
auditP.put("user_id", approverId); // 위임자 A
|
||||
auditP.put("user_name", line.get("approver_name"));
|
||||
auditP.put("processor_id", userId); // 실제 처리자 B
|
||||
// processor_name 은 AuditLogService 가 USER_INFO 에서 lookup (T14)
|
||||
auditP.put("action", "approval." + action);
|
||||
auditP.put("resource_type", "approval_line");
|
||||
auditP.put("resource_id", String.valueOf(lineId));
|
||||
auditP.put("summary", "결재 " + action + (proxyFor != null ? " (대무)" : ""));
|
||||
auditLogService.insertAuditLog(auditP);
|
||||
} catch (Exception e) {
|
||||
log.warn("결재 audit log 기록 실패 (line={}): {}", lineId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -214,7 +214,10 @@
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM APPROVAL_LINES L
|
||||
WHERE L.REQUEST_ID = R.REQUEST_ID
|
||||
AND L.APPROVER_ID = #{user_id}
|
||||
AND L.APPROVER_ID IN
|
||||
<foreach collection="effective_user_ids" item="uid" open="(" separator="," close=")">
|
||||
#{uid}
|
||||
</foreach>
|
||||
AND L.STATUS = 'pending'
|
||||
AND L.COMPANY_CODE = R.COMPANY_CODE
|
||||
)
|
||||
@@ -248,7 +251,10 @@
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM APPROVAL_LINES L
|
||||
WHERE L.REQUEST_ID = R.REQUEST_ID
|
||||
AND L.APPROVER_ID = #{user_id}
|
||||
AND L.APPROVER_ID IN
|
||||
<foreach collection="effective_user_ids" item="uid" open="(" separator="," close=")">
|
||||
#{uid}
|
||||
</foreach>
|
||||
AND L.STATUS = 'pending'
|
||||
AND L.COMPANY_CODE = R.COMPANY_CODE
|
||||
)
|
||||
@@ -463,7 +469,10 @@
|
||||
FROM APPROVAL_LINES L
|
||||
JOIN APPROVAL_REQUESTS R
|
||||
ON L.REQUEST_ID = R.REQUEST_ID AND L.COMPANY_CODE = R.COMPANY_CODE
|
||||
WHERE L.APPROVER_ID = #{user_id}
|
||||
WHERE L.APPROVER_ID IN
|
||||
<foreach collection="effective_user_ids" item="uid" open="(" separator="," close=")">
|
||||
#{uid}
|
||||
</foreach>
|
||||
AND L.STATUS = 'pending'
|
||||
AND (L.COMPANY_CODE = #{company_code} OR L.COMPANY_CODE = '*')
|
||||
ORDER BY R.CREATED_DATE ASC
|
||||
@@ -536,12 +545,14 @@
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</delete>
|
||||
|
||||
<!-- 어댑터: USER_SUBSTITUTES 참조 (T7, 086 마이그레이션 이후). -->
|
||||
<select id="selectActiveProxyForLine" parameterType="map" resultType="map">
|
||||
SELECT * FROM APPROVAL_PROXY_SETTINGS
|
||||
SELECT *
|
||||
FROM USER_SUBSTITUTES
|
||||
WHERE ORIGINAL_USER_ID = #{original_user_id}
|
||||
AND PROXY_USER_ID = #{proxy_user_id}
|
||||
AND IS_ACTIVE = 'Y'
|
||||
AND START_DATE <= CURRENT_DATE
|
||||
AND IS_ACTIVE = TRUE
|
||||
AND (START_DATE IS NULL OR START_DATE <= CURRENT_DATE)
|
||||
AND END_DATE >= CURRENT_DATE
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
LIMIT 1
|
||||
|
||||
Reference in New Issue
Block a user