feat(대무자): 대무자 관리 기능 — 결재 위임 + 컨텍스트 주입 + UI #7
Reference in New Issue
Block a user
Delete Branch "johngreen"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
개요
직원 부재 시 다른 직원이 결재를 대신 처리하고, ProfileModal/UserFormModal/결재함 UI 까지 연결하는 대무자(代務者) 관리 기능을 추가합니다.
핵심 결정 (deep-dive Socratic 인터뷰 결과)
주요 변경
백엔드
USER_SUBSTITUTES테이블 (PK, CHECK 2개, EXCLUDE 제약, 인덱스 2개)SubstituteService/SubstituteController/mapper/substitute.xmlSubstituteContextFilter—/api/**요청에effective_user_ids = [user_id, ...active_original_ids]request attribute 주입 (TenantConsistencyGuard 뒤, ForcePasswordChangeGuard 앞)approval.xml어댑터 —selectActiveProxyForLine를USER_SUBSTITUTES참조로 교체 +selectRequests/countRequests/selectMyPendingLines3곳에IN (effective_user_ids)foreach 적용ApprovalService.processApproval마지막에AuditLogService.insertAuditLog호출 추가 (user_id=A,processor_id=B분리 기록)auditLog.xml에PROCESSOR_ID/PROCESSOR_NAME컬럼 추가 +AuditLogService에서 평시 fallback + 대무 시USER_INFO.USER_NAME단건 lookupStartupSchemaMigrator에 RUN_086 7개 idempotent DDL/DML 등록 — backend 부팅 시 메타 + 모든 활성 테넌트 DB 에 자동 적용프론트엔드
frontend/lib/api/substitute.ts(7개 API)components/admin/SubstituteSection.tsx— 관리자 대무자 지정 (v5 토큰, blur 금지)components/layout/MySubstituteView.tsx— ProfileModal read-only 조회 (양방향, D-day 카운트다운)UserFormModal/ProfileModal/approval/page.tsx(대무 뱃지)인프라
TenantConsistencyGuardFilter가 자동 차단 (추가 코드 0줄)StartupSchemaMigrator가 자동 적용 —db/migrations/RUN_086_MIGRATION.md는 runbook 문서적용된 critic 권장사항
validateUserInCompany)ensureEffectiveUserIdshelper (mybatis 빈 IN() 방어)검증 결과
compileJava통과tsc --noEmit통과산출 문서
.omc/specs/deep-dive-trace-user-substitute-management.md— trace 분석.omc/specs/deep-dive-user-substitute-management.md— 최종 spec.omc/plans/autopilot-impl.md— architect plan + critic 검증notes/johngreen/2026-05-11-domain-tables-created-by-scan.md— T15 broad scan남은 일
IN (effective_user_ids)적용 (별도 PR)- 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 분리 기록)- frontend/lib/api/substitute.ts: 7개 API 함수 (Record<string, any> 컨벤션) - components/admin/SubstituteSection.tsx (신규): 관리자용 대무자 지정 섹션 · 활성/예정 대무 관계 테이블, 사전 겹침 검증 · v5 토큰 (--v5-surface-solid, --v5-glow-sm) 사용, blur 금지 - components/admin/UserFormModal.tsx: 수정 모드일 때 SubstituteSection 노출 - components/layout/MySubstituteView.tsx (신규): ProfileModal 용 read-only 조회 · 내 대무자 + 내가 대무 중인 사람 양방향, D-day 카운트다운 - components/layout/ProfileModal.tsx: MySubstituteView 삽입 - app/(main)/approval/page.tsx: 대기함 행에 "대무 ← {원본 결재자}" 뱃지 · currentUser.user_id !== line.approver_id 비교 (별도 타입 필드 X)