G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현 G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건) G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST) G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직 G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트 G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트 G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트 G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트 G-10: core/push_notify.py + routers/push.py + PushSubscription 모델 G-11: approvals 다중승인 (위임/서명/기한초과/마감연장) G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh 하네스: guardia-orchestrator 확장기능 Phase 반영 봇명령어: /sr /status /license /bulk 슬래시 명령어 추가 설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
267 lines
10 KiB
Markdown
267 lines
10 KiB
Markdown
# [Specification] 작업 타임테이블 + Excel 다운로드
|
|
|
|
> SM 운영, 정기점검, PM, SR 기반 작업 등 모든 수행 이력을 타임테이블로 관리하고
|
|
> Excel 파일로 다운로드하는 기능 명세.
|
|
|
|
---
|
|
|
|
## 1. 타임테이블 개요
|
|
|
|
### 1.1. 목적
|
|
|
|
| 목적 | 설명 |
|
|
|------|------|
|
|
| 작업 이력 관리 | 정기점검·PM·SR·장애대응 모든 작업을 단일 화면에서 통합 관리 |
|
|
| 감사 자료 제출 | 기관·감사원 요청 시 Excel로 즉시 제출 가능한 형태 유지 |
|
|
| PM 보고 자료 | 월간/분기 활동 보고서 자동 생성 기반 데이터 |
|
|
| 작업 표준화 | 수행 명령어·쉘 스크립트를 기록해 재사용 가능하도록 관리 |
|
|
|
|
### 1.2. 작업 유형
|
|
|
|
| work_type | 한글명 | 설명 |
|
|
|-----------|--------|------|
|
|
| `REGULAR_CHECK` | 정기점검 | 월간·분기·반기·연간 계획된 점검 |
|
|
| `PM` | 예방 정비 | 하드웨어 교체, 패치 적용, 취약점 조치 |
|
|
| `SR` | SR 기반 작업 | ITSM SR과 연계된 작업 |
|
|
| `ADHOC` | 수시점검 | 비정기 확인, 고객 요청 확인 |
|
|
| `DEPLOY` | 배포 작업 | 소스 배포, 설정 변경 |
|
|
| `EMERGENCY` | 긴급 대응 | 장애 복구, 긴급 조치 |
|
|
|
|
---
|
|
|
|
## 2. 데이터 모델
|
|
|
|
### 2.1. 주요 컬럼 상세
|
|
|
|
| 컬럼 | 타입 | 필수 | 설명 |
|
|
|------|------|------|------|
|
|
| `timetable_id` | BIGINT | PK | |
|
|
| `work_type` | VARCHAR(30) | ✅ | 작업 유형 |
|
|
| `title` | VARCHAR(200) | ✅ | 작업 제목 (예: 2025 Q1 정기점검 - 기획재정부) |
|
|
| `inst_id` | VARCHAR(20) | | 대상 기관 |
|
|
| `server_id` | VARCHAR(30) | | 대상 서버 (복수면 JSON) |
|
|
| `sr_id` | VARCHAR(30) | | 연계 SR ID |
|
|
| `script_id` | BIGINT | | 사용 쉘 스크립트 ID |
|
|
| `scheduled_at` | TIMESTAMP | ✅ | 처리 예정 일시 |
|
|
| `started_at` | TIMESTAMP | | 실제 시작 일시 |
|
|
| `completed_at` | TIMESTAMP | | 완료 일시 |
|
|
| `content` | TEXT | ✅ | 처리 내용 (무엇을, 어떻게, 왜) |
|
|
| `command_or_shell` | TEXT | | 실행한 명령어 또는 쉘 이름 |
|
|
| `result` | TEXT | | 처리 결과 (성공/실패 내용) |
|
|
| `result_status` | VARCHAR(20) | | PENDING / SUCCESS / FAILED / PARTIAL |
|
|
| `assignee` | VARCHAR(50) | ✅ | 담당 엔지니어 사번 |
|
|
| `reviewer` | VARCHAR(50) | | PM/검토자 사번 |
|
|
| `attachments` | JSONB | | 첨부파일 목록 |
|
|
|
|
---
|
|
|
|
## 3. API 엔드포인트
|
|
|
|
| 메서드 | URL | 권한 | 설명 |
|
|
|--------|-----|------|------|
|
|
| `GET` | `/api/timetable` | ADMIN, PM, ENGINEER | 타임테이블 목록 (필터링 지원) |
|
|
| `GET` | `/api/timetable/{id}` | ADMIN, PM, ENGINEER | 상세 조회 |
|
|
| `POST` | `/api/timetable` | ENGINEER, PM, ADMIN | 새 작업 등록 |
|
|
| `PATCH` | `/api/timetable/{id}` | ENGINEER, PM, ADMIN | 수정 |
|
|
| `DELETE` | `/api/timetable/{id}` | ADMIN | 삭제 |
|
|
| `GET` | `/api/timetable/export/excel` | ADMIN, PM | Excel 다운로드 |
|
|
| `GET` | `/api/timetable/stats` | ADMIN, PM | 기간별 통계 |
|
|
|
|
### 3.1. 목록 조회 쿼리 파라미터
|
|
|
|
```
|
|
GET /api/timetable
|
|
?inst_code=MOF # 기관 필터
|
|
&work_type=REGULAR_CHECK # 작업 유형 필터
|
|
&result_status=SUCCESS # 결과 상태 필터
|
|
&assignee=EMP-0101 # 담당자 필터
|
|
&date_from=2025-01-01 # 시작일
|
|
&date_to=2025-03-31 # 종료일
|
|
&keyword=점검 # 제목/내용 검색
|
|
&skip=0&limit=50
|
|
```
|
|
|
|
### 3.2. Excel 다운로드 쿼리 파라미터
|
|
|
|
```
|
|
GET /api/timetable/export/excel
|
|
?inst_code=MOF
|
|
&date_from=2025-01-01
|
|
&date_to=2025-03-31
|
|
&work_type=REGULAR_CHECK
|
|
```
|
|
|
|
**응답**: `Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
|
|
**파일명**: `GUARDiA_작업이력_MOF_20250101_20250331.xlsx`
|
|
|
|
---
|
|
|
|
## 4. Excel 파일 구조
|
|
|
|
> `openpyxl` 라이브러리 사용 (on-premise, 외부 API 호출 없음)
|
|
|
|
### 4.1. 시트 구성
|
|
|
|
| 시트 | 내용 |
|
|
|------|------|
|
|
| `작업이력` | 전체 타임테이블 데이터 |
|
|
| `통계요약` | 작업 유형별·결과별 집계 |
|
|
| `서버현황` | 대상 서버 목록 (민감정보 제외) |
|
|
|
|
### 4.2. 작업이력 시트 컬럼
|
|
|
|
| # | 컬럼명 | 데이터 |
|
|
|---|--------|--------|
|
|
| A | 번호 | 순번 |
|
|
| B | 작업유형 | 정기점검 / PM / SR작업 / 수시점검 |
|
|
| C | 제목 | work timetable title |
|
|
| D | 기관명 | inst_name |
|
|
| E | 대상서버 | server_id (hostname) |
|
|
| F | 처리예정일시 | scheduled_at |
|
|
| G | 시작일시 | started_at |
|
|
| H | 완료일시 | completed_at |
|
|
| I | 소요시간(분) | completed_at - started_at |
|
|
| J | 처리내용 | content |
|
|
| K | 명령어/쉘 | command_or_shell |
|
|
| L | 처리결과 | result |
|
|
| M | 결과상태 | SUCCESS / FAILED / PARTIAL |
|
|
| N | 담당자 | assignee 이름 |
|
|
| O | 검토자 | reviewer 이름 |
|
|
| P | SR번호 | sr_id |
|
|
| Q | 비고 | note |
|
|
|
|
### 4.3. 스타일 규칙
|
|
|
|
```python
|
|
# 헤더 스타일
|
|
header_fill = PatternFill("solid", fgColor="1E3A5F") # 네이비
|
|
header_font = Font(name="맑은 고딕", bold=True, color="FFFFFF", size=11)
|
|
header_align = Alignment(horizontal="center", vertical="center")
|
|
|
|
# 데이터 행 교대 색상
|
|
row_fill_even = PatternFill("solid", fgColor="F2F6FA")
|
|
row_fill_odd = None # 흰색
|
|
|
|
# 결과 상태 강조 색상
|
|
status_colors = {
|
|
"SUCCESS": "C6EFCE", # 연초록
|
|
"FAILED": "FFC7CE", # 연빨강
|
|
"PARTIAL": "FFEB9C", # 연노랑
|
|
"PENDING": "F4F4F4", # 연회색
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 프론트엔드 화면 구성
|
|
|
|
### 5.1. 타임테이블 뷰 (`view-timetable`)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ 📅 작업 타임테이블 [+ 작업 등록] [📥 Excel] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ [기관▼] [작업유형▼] [결과▼] [날짜범위] [담당자] [검색] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ 날짜/시간 │ 유형 │ 제목 │ 기관 │
|
|
│ │ │ │ │
|
|
│ 2025-03-15 14:00 │ 정기점검 │ 1분기 정기점검 │ MOF │
|
|
│ │ │ - 기획재정부 │ ✅완료 │
|
|
│ │ │ │ │
|
|
│ 2025-03-20 09:00 │ SR작업 │ SR-20250320-001 │ MOIS │
|
|
│ │ │ WAS 재기동 │ ⚠일부 │
|
|
└───────────────────────────────────────────────────────--┘
|
|
```
|
|
|
|
### 5.2. 신규 등록 모달
|
|
|
|
```
|
|
┌─ 작업 등록 ──────────────────────────────────────────┐
|
|
│ 작업유형 [정기점검 ▼] 기관 [MOF ▼] │
|
|
│ 제목 [___________________________________] │
|
|
│ 대상서버 [MOF-WAS-01 ▼] SR연계 [선택 ▼] │
|
|
│ 처리예정 [2025-03-15] [14:00] │
|
|
│ │
|
|
│ 처리내용 │
|
|
│ [──────────────────────────────────────────] │
|
|
│ [ ] │
|
|
│ │
|
|
│ 명령어/쉘 스크립트 │
|
|
│ [쉘 선택 ▼] 또는 직접 입력 │
|
|
│ [──────────────────────────────────────────] │
|
|
│ │
|
|
│ 처리결과 [___________________________] │
|
|
│ 결과상태 [완료 ✅ / 부분완료 ⚠ / 실패 ❌] │
|
|
│ 담당자 [김엔지니어 ▼] 검토자 [이PM ▼] │
|
|
│ │
|
|
│ [취소] [저장] │
|
|
└───────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Excel 생성 코드 구조 (서버측)
|
|
|
|
```python
|
|
# routers/timetable.py
|
|
from openpyxl import Workbook
|
|
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
|
from fastapi.responses import StreamingResponse
|
|
import io
|
|
|
|
@router.get("/export/excel")
|
|
async def export_excel(
|
|
inst_code: str | None = Query(None),
|
|
date_from: date | None = Query(None),
|
|
date_to: date | None = Query(None),
|
|
work_type: str | None = Query(None),
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
# 1. 데이터 조회
|
|
rows = await _fetch_timetable(db, inst_code, date_from, date_to, work_type)
|
|
|
|
# 2. Workbook 생성
|
|
wb = Workbook()
|
|
ws = wb.active
|
|
ws.title = "작업이력"
|
|
|
|
# 3. 헤더 작성
|
|
headers = ["번호", "작업유형", "제목", "기관명", "대상서버",
|
|
"처리예정", "시작", "완료", "소요(분)",
|
|
"처리내용", "명령어/쉘", "처리결과", "결과상태",
|
|
"담당자", "검토자", "SR번호", "비고"]
|
|
_write_header(ws, headers)
|
|
|
|
# 4. 데이터 작성
|
|
for i, row in enumerate(rows, start=2):
|
|
_write_row(ws, i, row)
|
|
|
|
# 5. 통계 시트 추가
|
|
_add_stats_sheet(wb, rows)
|
|
|
|
# 6. StreamingResponse 반환
|
|
buf = io.BytesIO()
|
|
wb.save(buf)
|
|
buf.seek(0)
|
|
filename = f"GUARDiA_작업이력_{inst_code or 'ALL'}_{date.today():%Y%m%d}.xlsx"
|
|
return StreamingResponse(
|
|
buf,
|
|
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{filename}"}
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 의존성
|
|
|
|
```
|
|
openpyxl>=3.1.0 # Excel 생성 (on-premise, 외부 API 없음)
|
|
```
|
|
|
|
`requirements.txt`에 추가:
|
|
```
|
|
openpyxl==3.1.2
|
|
```
|