feat: integrate dashboard modules and document history
This commit is contained in:
359
docs/AUTH_DB_DESIGN.md
Normal file
359
docs/AUTH_DB_DESIGN.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Auth DB Design
|
||||
|
||||
## Goal
|
||||
|
||||
현재 조직도 업무 데이터와 로그인 데이터를 분리한다.
|
||||
|
||||
분리 원칙:
|
||||
- 업무 데이터는 기존 `public.members`, `seat_maps`, `seat_positions` 중심으로 유지
|
||||
- 인증/권한 데이터는 별도 `auth` 스키마로 분리
|
||||
- 로그인 사용자는 필요할 때만 `members.id` 와 연결
|
||||
|
||||
이 방식이면 비밀번호, 세션, 감사로그를 업무 데이터와 분리해서 관리할 수 있고,
|
||||
엑셀 임포트로 `members` 가 갱신돼도 인증 체계가 직접 흔들리지 않는다.
|
||||
|
||||
## Scope
|
||||
|
||||
이번 설계의 대상:
|
||||
- 사용자 계정
|
||||
- 비밀번호 해시
|
||||
- 세션
|
||||
- 역할과 권한
|
||||
- 로그인 감사로그
|
||||
- 사용자와 조직 구성원 연결
|
||||
|
||||
이번 설계에서 제외:
|
||||
- SSO 연동
|
||||
- OAuth/OpenID Connect
|
||||
- MFA
|
||||
- 비밀번호 재설정 메일 발송
|
||||
|
||||
## Recommended Structure
|
||||
|
||||
권장 구조는 "같은 PostgreSQL, 다른 스키마" 이다.
|
||||
|
||||
- 업무 스키마: `public`
|
||||
- 인증 스키마: `auth`
|
||||
|
||||
초기 운영에서는 DB 인스턴스를 분리하지 않아도 된다.
|
||||
대신 아래 원칙은 바로 적용한다.
|
||||
|
||||
- 애플리케이션 계정도 가능하면 읽기/쓰기 범위를 분리
|
||||
- 인증 관련 쿼리는 `auth.*` 만 접근
|
||||
- 업무 API 는 `public.*` 중심으로 접근
|
||||
|
||||
## Core Tables
|
||||
|
||||
### `auth.users`
|
||||
|
||||
로그인 가능한 계정의 기준 테이블.
|
||||
|
||||
주요 컬럼:
|
||||
- `id BIGSERIAL PRIMARY KEY`
|
||||
- `username TEXT NOT NULL UNIQUE`
|
||||
- `password_hash TEXT NOT NULL`
|
||||
- `display_name TEXT NOT NULL`
|
||||
- `email TEXT`
|
||||
- `status TEXT NOT NULL DEFAULT 'active'`
|
||||
- `member_id INTEGER NULL REFERENCES public.members(id) ON DELETE SET NULL`
|
||||
- `last_login_at TIMESTAMPTZ`
|
||||
- `password_changed_at TIMESTAMPTZ`
|
||||
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
||||
- `updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
||||
|
||||
상태값 권장:
|
||||
- `active`
|
||||
- `locked`
|
||||
- `disabled`
|
||||
|
||||
원칙:
|
||||
- `username` 는 로그인 식별자
|
||||
- `member_id` 는 선택 연결
|
||||
- 구성원이 퇴사하거나 엑셀에서 빠져도 계정 자체는 바로 삭제하지 않음
|
||||
|
||||
### `auth.roles`
|
||||
|
||||
권한 묶음 정의.
|
||||
|
||||
주요 컬럼:
|
||||
- `id BIGSERIAL PRIMARY KEY`
|
||||
- `code TEXT NOT NULL UNIQUE`
|
||||
- `name TEXT NOT NULL`
|
||||
- `description TEXT`
|
||||
|
||||
초기 권장 역할:
|
||||
- `super_admin`
|
||||
- `org_admin`
|
||||
- `viewer`
|
||||
|
||||
### `auth.user_roles`
|
||||
|
||||
사용자와 역할의 다대다 연결.
|
||||
|
||||
주요 컬럼:
|
||||
- `user_id BIGINT NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE`
|
||||
- `role_id BIGINT NOT NULL REFERENCES auth.roles(id) ON DELETE CASCADE`
|
||||
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
||||
- `PRIMARY KEY (user_id, role_id)`
|
||||
|
||||
### `auth.permissions`
|
||||
|
||||
세분화된 권한 코드 정의.
|
||||
|
||||
주요 컬럼:
|
||||
- `id BIGSERIAL PRIMARY KEY`
|
||||
- `code TEXT NOT NULL UNIQUE`
|
||||
- `name TEXT NOT NULL`
|
||||
- `description TEXT`
|
||||
|
||||
초기 권장 권한:
|
||||
- `member.read`
|
||||
- `member.write`
|
||||
- `member.import`
|
||||
- `seatmap.read`
|
||||
- `seatmap.write`
|
||||
- `photo.upload`
|
||||
- `admin.user.manage`
|
||||
|
||||
### `auth.role_permissions`
|
||||
|
||||
역할과 권한의 다대다 연결.
|
||||
|
||||
주요 컬럼:
|
||||
- `role_id BIGINT NOT NULL REFERENCES auth.roles(id) ON DELETE CASCADE`
|
||||
- `permission_id BIGINT NOT NULL REFERENCES auth.permissions(id) ON DELETE CASCADE`
|
||||
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
||||
- `PRIMARY KEY (role_id, permission_id)`
|
||||
|
||||
### `auth.sessions`
|
||||
|
||||
서버 세션 저장 테이블.
|
||||
|
||||
주요 컬럼:
|
||||
- `id UUID PRIMARY KEY`
|
||||
- `user_id BIGINT NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE`
|
||||
- `refresh_token_hash TEXT`
|
||||
- `issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
||||
- `expires_at TIMESTAMPTZ NOT NULL`
|
||||
- `revoked_at TIMESTAMPTZ`
|
||||
- `ip_address INET`
|
||||
- `user_agent TEXT`
|
||||
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
||||
|
||||
원칙:
|
||||
- 브라우저 쿠키에는 세션 식별자만 저장
|
||||
- 토큰 자체를 평문으로 DB에 저장하지 않음
|
||||
- 만료와 강제 로그아웃을 DB에서 통제 가능하게 함
|
||||
|
||||
### `auth.login_audit_logs`
|
||||
|
||||
로그인 시도와 결과 기록.
|
||||
|
||||
주요 컬럼:
|
||||
- `id BIGSERIAL PRIMARY KEY`
|
||||
- `username TEXT NOT NULL`
|
||||
- `user_id BIGINT NULL REFERENCES auth.users(id) ON DELETE SET NULL`
|
||||
- `success BOOLEAN NOT NULL`
|
||||
- `failure_reason TEXT`
|
||||
- `ip_address INET`
|
||||
- `user_agent TEXT`
|
||||
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
||||
|
||||
용도:
|
||||
- 로그인 실패 추적
|
||||
- 계정 잠금 기준 판단
|
||||
- 보안 감사 대응
|
||||
|
||||
## Relationship To Current `members`
|
||||
|
||||
핵심은 `auth.users.member_id -> public.members.id` 연결이다.
|
||||
|
||||
의미:
|
||||
- 로그인 계정과 조직도 인원을 분리한다
|
||||
- 로그인하지 않는 구성원은 `members` 에만 있어도 된다
|
||||
- 외부 관리자 계정은 `member_id` 없이 운영할 수 있다
|
||||
|
||||
권장 규칙:
|
||||
- 일반 사내 사용자는 `employee_id` 기준으로 계정-구성원 연결
|
||||
- 엑셀 동기화 시 `members.id` 유지가 중요하므로 이미 반영한 비교 기반 동기화 방식을 유지
|
||||
- `member_id` 연결이 끊긴 계정은 자동 삭제하지 말고 관리자 검토 대상으로 둔다
|
||||
|
||||
## Login Flow
|
||||
|
||||
### 1. 로그인 요청
|
||||
|
||||
입력:
|
||||
- `username`
|
||||
- `password`
|
||||
|
||||
처리:
|
||||
- `auth.users` 에서 `username` 조회
|
||||
- `status != active` 이면 거부
|
||||
- `password_hash` 검증
|
||||
- 성공 시 `auth.sessions` 생성
|
||||
- `auth.login_audit_logs` 기록
|
||||
|
||||
응답 권장:
|
||||
- 사용자 기본 정보
|
||||
- 역할 목록
|
||||
- 권한 목록
|
||||
- 세션 만료 시각
|
||||
|
||||
### 2. 인증 확인
|
||||
|
||||
각 보호 API 요청 시:
|
||||
- 세션 쿠키 또는 Bearer 토큰 확인
|
||||
- `auth.sessions` 조회
|
||||
- 만료/폐기 여부 확인
|
||||
- 사용자 상태와 역할 재검증
|
||||
|
||||
### 3. 로그아웃
|
||||
|
||||
처리:
|
||||
- 현재 세션의 `revoked_at` 업데이트
|
||||
- 클라이언트 쿠키 제거
|
||||
|
||||
## Authorization Model
|
||||
|
||||
초기에는 RBAC 기반으로 충분하다.
|
||||
|
||||
권장 역할별 범위:
|
||||
|
||||
`super_admin`
|
||||
- 사용자 관리
|
||||
- 권한 관리
|
||||
- 조직도/사진/자리배치 전체 수정
|
||||
|
||||
`org_admin`
|
||||
- 조직도 조회/수정
|
||||
- 엑셀 임포트
|
||||
- 사진 업로드
|
||||
- 자리배치 수정
|
||||
|
||||
`viewer`
|
||||
- 조직도 조회
|
||||
- 자리배치 조회
|
||||
|
||||
API 보호 예시:
|
||||
- `GET /api/members`: `member.read`
|
||||
- `POST /api/members/import`: `member.import`
|
||||
- `POST /api/uploads/profile-photo`: `photo.upload`
|
||||
- `PUT /api/seat-maps/{seat_map_id}/layout`: `seatmap.write`
|
||||
- 사용자 관리 API: `admin.user.manage`
|
||||
|
||||
## Password Policy
|
||||
|
||||
비밀번호는 평문 저장 금지.
|
||||
|
||||
권장:
|
||||
- `Argon2id` 우선
|
||||
- 대안으로 `bcrypt`
|
||||
|
||||
추가 원칙:
|
||||
- 첫 구현부터 해시 알고리즘 버전 정보 포함
|
||||
- 비밀번호 변경 시 `password_changed_at` 갱신
|
||||
- 실패 횟수 기반 잠금은 앱 로직 또는 별도 컬럼으로 확장 가능
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1
|
||||
|
||||
인증 스키마와 기본 테이블만 추가.
|
||||
|
||||
작업:
|
||||
- `CREATE SCHEMA IF NOT EXISTS auth`
|
||||
- `auth.users`
|
||||
- `auth.roles`
|
||||
- `auth.user_roles`
|
||||
- `auth.permissions`
|
||||
- `auth.role_permissions`
|
||||
- `auth.sessions`
|
||||
- `auth.login_audit_logs`
|
||||
|
||||
이 단계에서는 기존 `/api/mock-login` 유지 가능.
|
||||
|
||||
### Phase 2
|
||||
|
||||
관리자 1명 이상을 수동 생성하고 실제 로그인 API 추가.
|
||||
|
||||
권장 추가 API:
|
||||
- `POST /api/auth/login`
|
||||
- `POST /api/auth/logout`
|
||||
- `GET /api/auth/me`
|
||||
|
||||
### Phase 3
|
||||
|
||||
기존 프론트엔드의 mock 로그인 제거.
|
||||
|
||||
변경 대상:
|
||||
- [frontend/public/app.js](/home/hyunho/projects/mh-dashboard-organization/frontend/public/app.js)
|
||||
- [backend/app/main.py](/home/hyunho/projects/mh-dashboard-organization/backend/app/main.py)
|
||||
- [backend/app/config.py](/home/hyunho/projects/mh-dashboard-organization/backend/app/config.py)
|
||||
|
||||
### Phase 4
|
||||
|
||||
권한 기반으로 API 보호 적용.
|
||||
|
||||
우선순위:
|
||||
1. 쓰기 API 보호
|
||||
2. 업로드 API 보호
|
||||
3. 읽기 API 권한 정리
|
||||
|
||||
## Recommended SQL Skeleton
|
||||
|
||||
```sql
|
||||
CREATE SCHEMA IF NOT EXISTS auth;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth.users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
email TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
member_id INTEGER NULL REFERENCES public.members(id) ON DELETE SET NULL,
|
||||
last_login_at TIMESTAMPTZ,
|
||||
password_changed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth.roles (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
code TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth.user_roles (
|
||||
user_id BIGINT NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
role_id BIGINT NOT NULL REFERENCES auth.roles(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (user_id, role_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth.sessions (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
refresh_token_hash TEXT,
|
||||
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
ip_address INET,
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
## Operational Notes
|
||||
|
||||
- 엑셀 임포트는 계속 `public.members` 기준으로 처리
|
||||
- 로그인 계정 생성은 엑셀 업로드와 분리
|
||||
- 프로필 사진은 현재처럼 파일 저장 + `members.photo_url` 참조 유지 가능
|
||||
- 감사로그와 세션은 삭제보다 보존 기간 정책으로 관리
|
||||
|
||||
## Decision
|
||||
|
||||
현재 프로젝트의 권장안은 아래 한 줄로 정리된다.
|
||||
|
||||
"로그인 DB는 `auth` 스키마로 분리하고, 업무 DB는 `public` 에 유지하며, 두 영역은 `auth.users.member_id` 로만 연결한다."
|
||||
177
docs/DEVELOPMENT_HISTORY.md
Normal file
177
docs/DEVELOPMENT_HISTORY.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# Development History
|
||||
|
||||
## Purpose
|
||||
|
||||
이 문서는 `total` 브랜치에서 진행한 통합 작업을 기능 단위로 정리한 개발 히스토리다.
|
||||
목표는 다음 두 가지다.
|
||||
|
||||
- 지금까지 어떤 기능을 어떤 방식으로 붙였는지 빠르게 파악
|
||||
- 이후 유지보수나 추가 개발 시, 왜 그렇게 구현했는지 추적 가능하게 하기
|
||||
|
||||
## 1. 대시보드 허브 통합
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- `조직 현황`, `프로젝트별 분석`, `팀/개인별 분석`, `자리배치도`를 하나의 메인 허브에서 오갈 수 있도록 통합
|
||||
- 공통 헤더, 로그인 정보, 탭 전환, 공통 기간 캘린더 구성
|
||||
- `payment.html`, `mh.html`은 초기에는 iframe 연결 방식으로 편입
|
||||
|
||||
### 해결 방식
|
||||
|
||||
- 기존 화면을 새로 재개발하지 않고, 먼저 메인 허브 안에 편입
|
||||
- 이후 데이터는 공통 API/DB 기준으로 전환하는 2단계 방식 채택
|
||||
|
||||
## 2. 조직현황 고도화
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- 조직도 레거시 화면을 메인 허브에 연결
|
||||
- 관리자/비관리자 모드 정리
|
||||
- 구성원 상세 프로필 개선
|
||||
- 프로필 사진 업로드 연동
|
||||
- `재석위치` 카드 추가
|
||||
|
||||
### 해결 방식
|
||||
|
||||
- 레거시 조직도 화면은 유지하되 API를 통해 `members`를 읽는 구조로 전환
|
||||
- 상세 프로필의 `재석위치`는 문자열이 아니라 실제 자리배치도 viewer preview를 붙이는 방식으로 변경
|
||||
|
||||
## 3. 자리배치도 기능 재구성
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- `기술개발센터` 고정 도면 기준 자리배치도 viewer 구성
|
||||
- 관리자 편집 화면과 비관리자 열람 화면 분리
|
||||
- 조직도에서 `+` 버튼으로 자리배치도 진입
|
||||
- 배치된 좌석의 이름/직급 라벨 표시
|
||||
- 좌석 hover, 클릭, 자리 비우기, 미배치 목록 복귀 흐름 구현
|
||||
|
||||
### 해결 방식
|
||||
|
||||
- DXF 업로드 기반 편집을 그대로 밀기보다, 실제 업무에 맞는 고정 도면 중심 구조로 정리
|
||||
- viewer 코드는 공통으로 쓰되, 화면은
|
||||
- 관리자 편집
|
||||
- 비관리자 열람
|
||||
- 구성원 상세 preview
|
||||
로 역할 분리
|
||||
|
||||
## 4. 자리배치도 저장 문제 해결
|
||||
|
||||
### 문제
|
||||
|
||||
- 관리자 화면에서는 배치가 된 것처럼 보여도 저장 후 조직도 상세 프로필에서는 `미배치`
|
||||
- 비관리자 자리배치도에서도 배치 결과가 보이지 않음
|
||||
|
||||
### 원인
|
||||
|
||||
- `seat_positions_map_cell_idx (seat_map_id, row_index, col_index)` 유니크 인덱스가 슬롯 기반 도면에도 그대로 적용됨
|
||||
- 고정 도면 배치는 실제로 `seat_slot_id` 기준 저장인데, 모든 배치가 `(row_index=0, col_index=0)`으로 들어가 충돌
|
||||
- 그 결과 두 번째 좌석부터 `500 Internal Server Error`로 저장 실패
|
||||
|
||||
### 해결
|
||||
|
||||
- 슬롯 기반 도면에서는 `(seat_map_id, row_index, col_index)` 인덱스가 적용되지 않도록 수정
|
||||
- `seat_positions` 저장 후 `members.seat_label`도 같이 동기화
|
||||
- 조직도 iframe은 저장 완료 후 `seatmap-layout-updated` 메시지를 받아 cache를 비우고 재조회
|
||||
|
||||
### 결과
|
||||
|
||||
- 관리자 자리배치 저장이 실제 DB까지 반영됨
|
||||
- 저장된 배치는 재접속 후에도 유지
|
||||
- 조직현황 상세 프로필과 비관리자 자리배치도에서도 같은 배치를 읽을 수 있는 구조가 됨
|
||||
|
||||
## 5. 통합 DB 구축
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- `organization.xlsx`
|
||||
- `MH.xlsx`
|
||||
- `payment.csv`
|
||||
|
||||
세 원본을 하나의 PostgreSQL 기준으로 적재
|
||||
|
||||
### 핵심 테이블
|
||||
|
||||
- `members`
|
||||
- `seat_maps`
|
||||
- `seat_slots`
|
||||
- `seat_positions`
|
||||
- `integration_raw_organization_rows`
|
||||
- `integration_raw_mh_rows`
|
||||
- `integration_raw_payment_rows`
|
||||
- `integration_work_logs`
|
||||
- `integration_work_log_segments`
|
||||
- `integration_vouchers`
|
||||
- `integration_projects`
|
||||
- `integration_project_category_mappings`
|
||||
|
||||
### 해결 방식
|
||||
|
||||
- 원본 보존용 raw 테이블과 운영용 표준 테이블을 분리
|
||||
- 화면이 직접 파일을 읽지 않고, DB와 API를 통해 같은 데이터를 보도록 정리
|
||||
|
||||
## 6. 프로젝트별 분석 통합
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- `opayment.html` 원본 기준으로 화면, 카테고리, 계산식 복원
|
||||
- `payment.csv`와 `MH.xlsx`를 함께 쓰는 구조로 정리
|
||||
- `payment.csv` 분류를 1순위, `ptj.csv` 프로젝트 매핑을 2순위로 적용
|
||||
|
||||
### 해결한 문제
|
||||
|
||||
- 연장근무 시간을 `실제`가 아니라 `가공` 기준으로 써야 원본과 총합이 맞음
|
||||
- 프로젝트 분류는 `payment.csv`만으로 부족해 `ptj.csv` 보정이 필요
|
||||
|
||||
### 결과
|
||||
|
||||
- 총합과 대부분의 프로젝트 집계가 원본 `opayment` 기준에 가깝게 정렬됨
|
||||
- 남은 차이는 소수점 또는 추가 매핑 보정 수준으로 축소
|
||||
|
||||
## 7. 팀/개인별 분석 통합
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- `omh.html` 원본 기준으로 화면, 카테고리, 계산식 복원
|
||||
- 업로드형이 아니라 통합 DB raw MH 데이터를 다시 공급하는 방식으로 전환
|
||||
|
||||
### 해결 방식
|
||||
|
||||
- 원본 `MH.xlsx` 시트 구조가 깨지지 않도록 row 배열 자체를 DB에 저장
|
||||
- 팀별 진행 프로젝트 등 원본 계산식이 기대하는 입력 구조를 그대로 재현
|
||||
|
||||
## 8. 조직 데이터 운영 정리
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- 이름 alias, 퇴사 제외, 조직 override를 코드 상수에서 제거
|
||||
- DB 테이블 기반 운영으로 전환
|
||||
|
||||
### 운영 테이블
|
||||
|
||||
- `member_aliases`
|
||||
- `member_retirements`
|
||||
- `member_overrides`
|
||||
|
||||
### 결과
|
||||
|
||||
- 이름 치환, 퇴사 제외, 팀/직책 예외를 코드 수정 없이 DB에서 관리 가능
|
||||
|
||||
## 9. 외부 접속 설정
|
||||
|
||||
### 작업 내용
|
||||
|
||||
- WSL 내부 `0.0.0.0:8080` 바인딩 확인
|
||||
- Windows host에서 `portproxy`와 방화벽 규칙으로 다른 PC 접속 가능하게 정리
|
||||
|
||||
### 유의사항
|
||||
|
||||
- Windows LAN IP 또는 WSL IP가 바뀌면 `portproxy`의 `connectaddress`는 다시 맞춰야 한다
|
||||
- 운영 안정성을 위해 향후 자동화 스크립트화가 필요함
|
||||
|
||||
## Next Focus
|
||||
|
||||
- 프로젝트별 분석의 남은 소수점/분류 오차 정리
|
||||
- 자리배치도 색상/조직 트리 등 추가 UX 기능 고도화
|
||||
- 실제 인증 체계 전환
|
||||
- 나머지 사무실 도면 추가
|
||||
263
docs/INTEGRATION_DB_PLAN.md
Normal file
263
docs/INTEGRATION_DB_PLAN.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# Integration DB Plan
|
||||
|
||||
## Goal
|
||||
|
||||
`organization.xlsx`, `MH.xlsx`, `payment.csv`를 하나의 통합 DB로 수용하고,
|
||||
`조직 현황`, `프로젝트별 분석`, `팀/개인별 분석`, `자리배치도`가 같은 기준 데이터를 바라보도록 정리한다.
|
||||
|
||||
## Source Summary
|
||||
|
||||
### 1. `organization.xlsx`
|
||||
|
||||
용도:
|
||||
- 인원 기본 정보
|
||||
- 조직 구조
|
||||
|
||||
주요 컬럼:
|
||||
- `name`
|
||||
- `tag`
|
||||
- `rank`
|
||||
- `pos`
|
||||
- `co`
|
||||
- `cell`
|
||||
- `team`
|
||||
- `div`
|
||||
- `gr`
|
||||
- `part`
|
||||
- `ph`
|
||||
- `mail`
|
||||
|
||||
해석:
|
||||
- `tag`는 사번 또는 내부 인원 식별자로 사용
|
||||
- 조직 정보는 `part > gr > div > team > cell` 구조
|
||||
|
||||
### 2. `MH.xlsx`
|
||||
|
||||
용도:
|
||||
- 일자별 인원 근무 실적
|
||||
- 프로젝트별 투입 시간
|
||||
- 연장근무 포함 세부 투입 슬롯
|
||||
|
||||
주요 컬럼:
|
||||
- `근무일자`
|
||||
- `팀 분류`
|
||||
- `팀`
|
||||
- `사원번호`
|
||||
- `이름`
|
||||
- `직책`
|
||||
- `user_state`
|
||||
- `시차시간`
|
||||
- `메인업무/추가업무1~5/연장근무`
|
||||
- `프로젝트 코드`
|
||||
- `프로젝트명`
|
||||
- `서브 코드`
|
||||
- `근무시간`
|
||||
|
||||
추가 시트:
|
||||
- `Sheet2`
|
||||
- 프로젝트 코드와 PM 이름 매핑으로 추정
|
||||
|
||||
### 3. `payment.csv`
|
||||
|
||||
용도:
|
||||
- 프로젝트별 수입/지출 전표
|
||||
|
||||
주요 컬럼:
|
||||
- `프로젝트코드`
|
||||
- `사업명`
|
||||
- `사업명(표출PJT)`
|
||||
- `사업명(인트라넷기준)`
|
||||
- `사업분야`
|
||||
- `세부분야`
|
||||
- `부서명`
|
||||
- `팀명`
|
||||
- `거래처`
|
||||
- `적요`
|
||||
- `차변공급가`
|
||||
- `대변공급가`
|
||||
- `지출`
|
||||
- `수입`
|
||||
- `구분`
|
||||
- `프로젝트성격`
|
||||
|
||||
## Recommended Model
|
||||
|
||||
### Raw Layer
|
||||
|
||||
원본을 그대로 적재하는 영역.
|
||||
|
||||
- `raw_organization_import`
|
||||
- `raw_mh_import`
|
||||
- `raw_payment_import`
|
||||
|
||||
원칙:
|
||||
- 원본 행을 최대한 손대지 않고 저장
|
||||
- 파일명, 업로드시각, 배치 ID 같이 저장
|
||||
|
||||
### Standard Layer
|
||||
|
||||
정규화된 운영 테이블.
|
||||
|
||||
#### Members
|
||||
|
||||
- `members`
|
||||
- `id`
|
||||
- `employee_id`
|
||||
- `name`
|
||||
- `company`
|
||||
- `rank`
|
||||
- `position`
|
||||
- `phone`
|
||||
- `email`
|
||||
- `active`
|
||||
|
||||
#### Organization
|
||||
|
||||
- `org_units`
|
||||
- `id`
|
||||
- `unit_type`
|
||||
- `name`
|
||||
- `parent_id`
|
||||
|
||||
- `member_org_assignments`
|
||||
- `id`
|
||||
- `member_id`
|
||||
- `part_name`
|
||||
- `group_name`
|
||||
- `division_name`
|
||||
- `team_name`
|
||||
- `cell_name`
|
||||
- `effective_from`
|
||||
- `effective_to`
|
||||
|
||||
#### Projects
|
||||
|
||||
- `projects`
|
||||
- `id`
|
||||
- `project_code`
|
||||
- `project_name`
|
||||
- `display_name`
|
||||
- `intranet_name`
|
||||
- `domain`
|
||||
- `subdomain`
|
||||
- `project_type`
|
||||
- `project_nature`
|
||||
|
||||
- `project_aliases`
|
||||
- `id`
|
||||
- `project_id`
|
||||
- `alias_type`
|
||||
- `alias_value`
|
||||
|
||||
- `project_pm_assignments`
|
||||
- `id`
|
||||
- `project_id`
|
||||
- `member_id`
|
||||
- `source`
|
||||
|
||||
#### Work Logs
|
||||
|
||||
- `work_logs`
|
||||
- `id`
|
||||
- `member_id`
|
||||
- `work_date`
|
||||
- `team_category`
|
||||
- `team_name`
|
||||
- `user_state`
|
||||
- `shift_hours`
|
||||
- `late_flag`
|
||||
|
||||
- `work_log_segments`
|
||||
- `id`
|
||||
- `work_log_id`
|
||||
- `project_id`
|
||||
- `activity_code`
|
||||
- `hours`
|
||||
- `is_overtime`
|
||||
- `slot_type`
|
||||
|
||||
#### Vouchers
|
||||
|
||||
- `vouchers`
|
||||
- `id`
|
||||
- `company_name`
|
||||
- `request_date`
|
||||
- `issue_date`
|
||||
- `issue_month`
|
||||
- `account_code`
|
||||
- `management_account_code`
|
||||
- `project_id`
|
||||
- `department_name`
|
||||
- `team_name`
|
||||
- `customer_name`
|
||||
- `summary`
|
||||
- `debit_amount`
|
||||
- `credit_amount`
|
||||
- `expense_amount`
|
||||
- `income_amount`
|
||||
- `voucher_type`
|
||||
- `project_nature`
|
||||
- `note`
|
||||
|
||||
#### Reference
|
||||
|
||||
- `member_cost_rates`
|
||||
- 직급별 표준 인건비
|
||||
|
||||
## Matching Rules
|
||||
|
||||
### Member Match
|
||||
|
||||
우선순위:
|
||||
1. `MH.xlsx.사원번호`
|
||||
2. `organization.xlsx.tag`
|
||||
3. 이름 단독 매칭은 보조 규칙으로만 사용
|
||||
|
||||
원칙:
|
||||
- `employee_id`가 있으면 그 값으로 병합
|
||||
- 이름만 같은 경우 자동 병합 금지
|
||||
|
||||
### Project Match
|
||||
|
||||
우선순위:
|
||||
1. `project_code`
|
||||
2. `사업명(인트라넷기준)`
|
||||
3. `사업명(표출PJT)`
|
||||
4. `프로젝트명`
|
||||
|
||||
원칙:
|
||||
- `project_code`를 정식 키로 사용
|
||||
- 이름 차이는 `project_aliases`로 흡수
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1
|
||||
|
||||
- `payment.html`, `mh.html`을 현재 대시보드 탭에 편입
|
||||
- 기존 HTML 기능은 유지
|
||||
- 파일은 backend route를 통해 iframe으로 연결
|
||||
|
||||
### Phase 2
|
||||
|
||||
- raw import 테이블 생성
|
||||
- 원본 3종 import 스크립트 작성
|
||||
- 파일별 업로드/재적재 배치 ID 관리
|
||||
|
||||
### Phase 3
|
||||
|
||||
- 표준 테이블 생성
|
||||
- raw -> standard 정규화 파이프라인 작성
|
||||
- 멤버/프로젝트 매핑 규칙 적용
|
||||
|
||||
### Phase 4
|
||||
|
||||
- `payment.html`, `mh.html`의 파일 직접 파싱 로직을 API 기반 조회로 전환
|
||||
- 프론트는 공통 DB 기준으로만 동작
|
||||
|
||||
## Immediate Next Tasks
|
||||
|
||||
1. Postgres 스키마 초안 SQL 작성
|
||||
2. `payment.csv` import 파서 작성
|
||||
3. `MH.xlsx` import 파서 작성
|
||||
4. `organization.xlsx` import 파서 작성
|
||||
5. 멤버/프로젝트 중복 병합 규칙 구현
|
||||
Reference in New Issue
Block a user