From 54032f6685604df64ff3d2f0ec98dc406799d255 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Sun, 26 Oct 2025 11:17:06 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8?= =?UTF-8?q?=EB=B3=84=20=EC=88=9C=EB=B2=88=20=EC=9E=90=EB=8F=99=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 핡심 κ°œμ„ μ‚¬ν•­: - μˆ˜μ‹ ν•¨μ—μ„œ 진행쀑/μ™„λ£Œλ‘œ μƒνƒœ λ³€κ²½μ‹œ project_sequence_no μžλ™ ν• λ‹Ή - ν”„λ‘œμ νŠΈλ³„λ‘œ 1λΆ€ν„° μ‹œμž‘ν•˜λŠ” κΉ”λ”ν•œ 순번 체계 πŸ”§ λ°±μ—”λ“œ μˆ˜μ •: - inbox.py: update_issue_status에 μžλ™ ν• λ‹Ή 둜직 μΆ”κ°€ - generate_project_sequence_no() DB ν•¨μˆ˜ ν™œμš© - 진행쀑/μ™„λ£Œ μƒνƒœ λ³€κ²½μ‹œμ—λ§Œ μ‹€ν–‰ πŸ“ DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜: - 017_fix_project_sequence_no.sql 생성 - κΈ°μ‘΄ 데이터 보정 (λˆ„λ½λœ 순번 0개 확인) - migration_log ν…Œμ΄λΈ” ꡬ쑰에 맞게 둜그 기둝 πŸ“‹ λ¬Έμ„œν™”: - DB_CHANGES_LOG.md 생성 및 μ—…λ°μ΄νŠΈ - 배포 κ°€μ΄λ“œ, 검증 방법, μ£Όμ˜μ‚¬ν•­ λͺ…μ‹œ - Docker ν™˜κ²½ κΈ°μ€€ μ‹€ν–‰ 방법 제곡 βœ… μ‹€ν–‰ μ™„λ£Œ μƒνƒœ: - λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 성곡 (2025-10-26 11:15:44+09:00) - λ°±μ—”λ“œ μ„œλΉ„μŠ€ μž¬μ‹œμž‘ μ™„λ£Œ - λͺ¨λ“  검증 ν•­λͺ© 톡과 Expected Result: 🎯 ν˜„ν™©νŒμ—μ„œ ν”„λ‘œμ νŠΈλ³„ No.1, No.2, No.3... ν‘œμ‹œ 🎯 6κ°œμ›” 후에도 각 ν”„λ‘œμ νŠΈ λ‚΄μ—μ„œ μž‘μ€ 번호 μœ μ§€ 🎯 전체 톡합 번호 λŒ€μ‹  ν”„λ‘œμ νŠΈλ³„ κΉ”λ”ν•œ 순번 체계 --- DB_CHANGES_LOG.md | 156 ++++++++++++++++++ .../017_fix_project_sequence_no.sql | 87 ++++++++++ .../routers/__pycache__/inbox.cpython-311.pyc | Bin 19080 -> 19547 bytes backend/routers/inbox.py | 10 ++ 4 files changed, 253 insertions(+) create mode 100644 DB_CHANGES_LOG.md create mode 100644 backend/migrations/017_fix_project_sequence_no.sql diff --git a/DB_CHANGES_LOG.md b/DB_CHANGES_LOG.md new file mode 100644 index 0000000..00af767 --- /dev/null +++ b/DB_CHANGES_LOG.md @@ -0,0 +1,156 @@ +# πŸ—„οΈ DB 변경사항 둜그 + +> **μ€‘μš”**: 배포 μ‹œ λ°˜λ“œμ‹œ 이 λ¬Έμ„œλ₯Ό ν™•μΈν•˜μ—¬ DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ μˆœμ„œλŒ€λ‘œ μ‹€ν–‰ν•˜μ„Έμš”. + +## πŸ“‹ 변경사항 λͺ©λ‘ + +### 2025.10.26 - ν”„λ‘œμ νŠΈλ³„ 순번 μžλ™ ν• λ‹Ή κ°œμ„  + +**🎯 λͺ©μ **: μˆ˜μ‹ ν•¨μ—μ„œ μ§„ν–‰ 쀑/μ™„λ£Œλ‘œ μƒνƒœ λ³€κ²½ μ‹œ ν”„λ‘œμ νŠΈλ³„ 순번이 μžλ™ ν• λ‹Ήλ˜λ„λ‘ κ°œμ„  + +#### πŸ“ 파일 변경사항 + +**1. λ°±μ—”λ“œ 둜직 μˆ˜μ •** +- **파일**: `backend/routers/inbox.py` +- **λ³€κ²½λ‚΄μš©**: `update_issue_status` ν•¨μˆ˜μ— `project_sequence_no` μžλ™ ν• λ‹Ή 둜직 μΆ”κ°€ +- **μ½”λ“œ**: + ```python + # μ§„ν–‰ 쀑 λ˜λŠ” μ™„λ£Œ μƒνƒœλ‘œ λ³€κ²½ μ‹œ ν”„λ‘œμ νŠΈλ³„ 순번 μžλ™ ν• λ‹Ή + if status_request.review_status in [ReviewStatus.in_progress, ReviewStatus.completed]: + if not issue.project_sequence_no: + from sqlalchemy import text + result = db.execute( + text("SELECT generate_project_sequence_no(:project_id)"), + {"project_id": issue.project_id} + ) + issue.project_sequence_no = result.scalar() + ``` + +**2. 데이터 보정 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜** +- **파일**: `backend/migrations/017_fix_project_sequence_no.sql` +- **λͺ©μ **: 기쑴에 μ§„ν–‰ 쀑/μ™„λ£Œ μƒνƒœμΈλ° `project_sequence_no`κ°€ λˆ„λ½λœ 데이터 보정 + +#### πŸš€ 배포 μ‹œ μ‹€ν–‰ μˆœμ„œ + +```bash +# 1. λ°±μ—”λ“œ μ½”λ“œ 배포 +git pull origin master + +# 2. DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ (Docker ν™˜κ²½) +docker-compose exec backend python -c " +import psycopg2 +import os + +database_url = os.getenv('DATABASE_URL', 'postgresql://postgres:password@db:5432/mproject') +conn = psycopg2.connect(database_url) +cur = conn.cursor() + +with open('migrations/017_fix_project_sequence_no.sql', 'r', encoding='utf-8') as f: + cur.execute(f.read()) +conn.commit() +conn.close() +print('βœ… λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ™„λ£Œ') +" + +# 3. μ„œλΉ„μŠ€ μž¬μ‹œμž‘ +docker-compose restart backend +``` + +#### πŸ” 검증 방법 + +**1. DBμ—μ„œ 직접 확인**: +```sql +-- 관리 쀑인 이슈 쀑 순번이 λˆ„λ½λœ 것이 μžˆλŠ”μ§€ 확인 +SELECT COUNT(*) as missing_count +FROM issues +WHERE review_status IN ('in_progress', 'completed') +AND project_sequence_no IS NULL; +-- κ²°κ³Ό: 0이어야 함 + +-- ν”„λ‘œμ νŠΈλ³„ 순번이 μ˜¬λ°”λ₯΄κ²Œ ν• λ‹Ήλ˜μ—ˆλŠ”μ§€ 확인 +SELECT project_id, COUNT(*) as total_issues, + MIN(project_sequence_no) as min_no, + MAX(project_sequence_no) as max_no +FROM issues +WHERE review_status IN ('in_progress', 'completed') +GROUP BY project_id +ORDER BY project_id; +-- κ²°κ³Ό: 각 ν”„λ‘œμ νŠΈλ³„λ‘œ 1λΆ€ν„° μ—°μ†λœ λ²ˆν˜Έκ°€ ν• λ‹Ήλ˜μ–΄μ•Ό 함 +``` + +**2. ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ 확인**: +- μˆ˜μ‹ ν•¨μ—μ„œ μƒˆλ‘œμš΄ 이슈λ₯Ό "μ§„ν–‰ 쀑"으둜 λ³€κ²½ +- ν˜„ν™©νŒμ—μ„œ "No.1, No.2..." ν˜•νƒœλ‘œ ν”„λ‘œμ νŠΈλ³„ 순번이 ν‘œμ‹œλ˜λŠ”μ§€ 확인 + +#### ⚠️ μ£Όμ˜μ‚¬ν•­ + +1. **λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ μ „ λ°±μ—…**: μ€‘μš”ν•œ λ°μ΄ν„°μ΄λ―€λ‘œ μ‹€ν–‰ μ „ DB λ°±μ—… ꢌμž₯ +2. **μˆœμ„œ μ€€μˆ˜**: λ°˜λ“œμ‹œ `016_add_management_fields.sql`이 λ¨Όμ € μ‹€ν–‰λœ μƒνƒœμ—¬μ•Ό 함 +3. **Docker ν™˜κ²½**: λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ€ Docker μ»¨ν…Œμ΄λ„ˆ λ‚΄μ—μ„œ μ‹€ν–‰ν•΄μ•Ό 함 +4. **migration_log ν…Œμ΄λΈ” ꡬ쑰**: + - 컬럼λͺ…: `migration_file` (migration_name μ•„λ‹˜) + - ν•„μˆ˜ 컬럼: `migration_file`, `executed_at`, `status`, `notes` +5. **λ‘€λ°± 방법**: 문제 λ°œμƒ μ‹œ `project_sequence_no` μ»¬λŸΌμ„ NULL둜 μ„€μ • ν›„ μž¬μ‹€ν–‰ + +#### πŸ“Š μ˜ˆμƒ κ²°κ³Ό + +**Before**: +``` +ν˜„ν™©νŒ: No. (λΉˆκ°’) +ν”„λ‘œμ νŠΈ A: μ΄μŠˆλ“€μ΄ 전체 톡합 번호둜 ν‘œμ‹œ (No.2, No.5, No.8...) +``` + +**After**: +``` +ν˜„ν™©νŒ: No.1, No.2, No.3... +ν”„λ‘œμ νŠΈ A: No.1, No.2, No.3 +ν”„λ‘œμ νŠΈ B: No.1, No.2, No.3 +각 ν”„λ‘œμ νŠΈλ³„λ‘œ 1λΆ€ν„° μ‹œμž‘ν•˜λŠ” κΉ”λ”ν•œ 순번 +``` + +--- + +## πŸ“ 이전 변경사항 + +### 2025.10.25 - 관리함 ν•„λ“œ μΆ”κ°€ +- **파일**: `backend/migrations/016_add_management_fields.sql` +- **λ‚΄μš©**: κ΄€λ¦¬ν•¨μ—μ„œ μ‚¬μš©ν•  μΆ”κ°€ ν•„λ“œλ“€ 및 `generate_project_sequence_no()` ν•¨μˆ˜ 생성 + +### 2025.10.24 - μ‚¬μš©μž λΆ€μ„œ 정보 μΆ”κ°€ +- **파일**: `backend/migrations/015_add_user_department.sql` +- **λ‚΄μš©**: μ‚¬μš©μž ν…Œμ΄λΈ”μ— λΆ€μ„œ 정보 컬럼 μΆ”κ°€ + +--- + +## πŸ”§ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ 도ꡬ + +**μžλ™ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 슀크립트** (μΆ”ν›„ 개발 μ˜ˆμ •): +```bash +#!/bin/bash +# run_migrations.sh +# λͺ¨λ“  λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ μˆœμ„œλŒ€λ‘œ μ‹€ν–‰ν•˜λŠ” 슀크립트 +``` + +**λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μƒνƒœ 확인**: +```sql +-- 졜근 μ‹€ν–‰λœ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 확인 +SELECT migration_file, executed_at, status, notes +FROM migration_log +ORDER BY executed_at DESC +LIMIT 10; + +-- νŠΉμ • λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 확인 +SELECT * FROM migration_log +WHERE migration_file = '017_fix_project_sequence_no.sql'; +``` + +#### πŸ“‹ μ‹€ν–‰ μ™„λ£Œ μƒνƒœ (2025.10.26) + +βœ… **λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ¨** +- **μ‹€ν–‰ μ‹œκ°„**: 2025-10-26 11:15:44+09:00 +- **μƒνƒœ**: SUCCESS +- **확인사항**: + - `generate_project_sequence_no()` ν•¨μˆ˜ 쑴재 βœ… + - `issues.project_sequence_no` 컬럼 쑴재 βœ… + - 순번이 λˆ„λ½λœ 관리 쀑인 이슈: 0개 βœ… + - λ°±μ—”λ“œ μ„œλΉ„μŠ€ μž¬μ‹œμž‘ μ™„λ£Œ βœ… diff --git a/backend/migrations/017_fix_project_sequence_no.sql b/backend/migrations/017_fix_project_sequence_no.sql new file mode 100644 index 0000000..7e97fcc --- /dev/null +++ b/backend/migrations/017_fix_project_sequence_no.sql @@ -0,0 +1,87 @@ +-- ν”„λ‘œμ νŠΈλ³„ 순번(project_sequence_no) μžλ™ ν• λ‹Ή κ°œμ„  +-- μˆ˜μ‹ ν•¨μ—μ„œ μ§„ν–‰ 쀑/μ™„λ£Œλ‘œ μƒνƒœ λ³€κ²½ μ‹œ ν”„λ‘œμ νŠΈλ³„ 순번이 μžλ™ ν• λ‹Ήλ˜λ„λ‘ κ°œμ„  + +DO $migration$ +DECLARE + issue_record RECORD; + seq_no INTEGER; + updated_count INTEGER := 0; +BEGIN + RAISE NOTICE '=== ν”„λ‘œμ νŠΈλ³„ 순번 μžλ™ ν• λ‹Ή κ°œμ„  λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹œμž‘ ==='; + + -- 1. generate_project_sequence_no ν•¨μˆ˜κ°€ μ‘΄μž¬ν•˜λŠ”μ§€ 확인 + IF NOT EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'generate_project_sequence_no') THEN + RAISE EXCEPTION '❌ generate_project_sequence_no ν•¨μˆ˜κ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 016_add_management_fields.sql을 λ¨Όμ € μ‹€ν–‰ν•˜μ„Έμš”.'; + END IF; + + -- 2. μ§„ν–‰ 쀑 λ˜λŠ” μ™„λ£Œ μƒνƒœμΈλ° project_sequence_noκ°€ NULL인 μ΄μŠˆλ“€ μ°ΎκΈ° + RAISE NOTICE 'πŸ” project_sequence_noκ°€ λˆ„λ½λœ μ΄μŠˆλ“€μ„ μ°ΎλŠ” 쀑...'; + + FOR issue_record IN + SELECT id, project_id, review_status + FROM issues + WHERE review_status IN ('in_progress', 'completed') + AND project_sequence_no IS NULL + ORDER BY project_id, reviewed_at NULLS LAST, report_date + LOOP + -- ν”„λ‘œμ νŠΈλ³„ 순번 생성 + SELECT generate_project_sequence_no(issue_record.project_id) INTO seq_no; + + -- 순번 ν• λ‹Ή + UPDATE issues + SET project_sequence_no = seq_no + WHERE id = issue_record.id; + + updated_count := updated_count + 1; + + RAISE NOTICE 'βœ… 이슈 ID: %, ν”„λ‘œμ νŠΈ ID: %, ν• λ‹Ήλœ 순번: %', + issue_record.id, issue_record.project_id, seq_no; + END LOOP; + + -- 3. κ²°κ³Ό μš”μ•½ + RAISE NOTICE '=== λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ™„λ£Œ ==='; + RAISE NOTICE 'πŸ“Š 총 %개의 μ΄μŠˆμ— ν”„λ‘œμ νŠΈλ³„ 순번이 ν• λ‹Ήλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', updated_count; + + -- 4. 검증 + DECLARE + missing_count INTEGER; + total_managed_count INTEGER; + BEGIN + -- 관리 쀑인 이슈 쀑 순번이 μ—†λŠ” 것듀 확인 + SELECT COUNT(*) INTO missing_count + FROM issues + WHERE review_status IN ('in_progress', 'completed') + AND project_sequence_no IS NULL; + + -- 전체 관리 쀑인 이슈 수 + SELECT COUNT(*) INTO total_managed_count + FROM issues + WHERE review_status IN ('in_progress', 'completed'); + + RAISE NOTICE '=== 검증 κ²°κ³Ό ==='; + RAISE NOTICE '전체 관리 쀑인 이슈: %개', total_managed_count; + RAISE NOTICE '순번이 λˆ„λ½λœ 이슈: %개', missing_count; + + IF missing_count > 0 THEN + RAISE WARNING '⚠️ μ—¬μ „νžˆ %개의 μ΄μŠˆμ— 순번이 λˆ„λ½λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.', missing_count; + ELSE + RAISE NOTICE 'βœ… λͺ¨λ“  관리 쀑인 μ΄μŠˆμ— 순번이 μ •μƒμ μœΌλ‘œ ν• λ‹Ήλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'; + END IF; + END; + +EXCEPTION + WHEN OTHERS THEN + RAISE EXCEPTION '❌ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹€ν–‰ 쀑 였λ₯˜ λ°œμƒ: %', SQLERRM; +END $migration$; + +-- λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 둜그 기둝 +INSERT INTO migration_log (migration_file, executed_at, status, notes) +VALUES ( + '017_fix_project_sequence_no.sql', + NOW(), + 'SUCCESS', + 'ν”„λ‘œμ νŠΈλ³„ 순번 μžλ™ ν• λ‹Ή κ°œμ„ : μˆ˜μ‹ ν•¨μ—μ„œ 진행쀑/μ™„λ£Œλ‘œ μƒνƒœ λ³€κ²½μ‹œ project_sequence_no μžλ™ ν• λ‹Ήλ˜λ„λ‘ λ°±μ—”λ“œ 둜직 κ°œμ„  및 κΈ°μ‘΄ 데이터 보정' +) ON CONFLICT (migration_file) DO UPDATE SET + executed_at = NOW(), + status = EXCLUDED.status, + notes = EXCLUDED.notes; diff --git a/backend/routers/__pycache__/inbox.cpython-311.pyc b/backend/routers/__pycache__/inbox.cpython-311.pyc index 6a2583c984ebb52af0ce4fdbf2384e8d88d5683e..71b14d9e718ad53c98b2be33fc2a79e8555e7fa4 100644 GIT binary patch delta 1087 zcmZ8fOH30%7@p~NcU$OVkphAsNTIY)9%@8FBS^JMBbp#;fJ=kjRUoCvbcqj&1c{0l zUlSt+Fc=d=gc#DqgC0B);Z{u#O-YCxJb787SE4h;<>6%J%lG}?_s{;B?C3MNG6qd= zjK&l|*7cE%&ei$_)0$0lhl{M+era&4vo^metnJ=QeY!#(F;z4KvU_p{IS9hc)IQvt zYN^qHFee(PApq~l*<7NDGzzNfg3}t}lC!x6`7l3~iyQK+X031Q`Y`Oy!#^BHhmNT^ z0BB8BqWQmDbYWds>&sXD;9)C}kE;rRB7kgzdYRp>h`cDsC$@E1zkBL&FsYf-;_=cn zHf>OUTaOn?+h8G`b4cs6s(oapB9$Y+S87X<)Ha9Rwq2<$MN-?G!OKD& z{#lj|_uxNe*)SbvI|q!GAQUsi%n7aqII{37=UM}zPwapRtt{P;Z&kH;nmi{i6kL^p z66%m8-!;_RE%{|%Nb2jCf_}*t?6udb#V*l-it$kSXXwE775A&tRbxjwkrL_)bOijJ zQqN5^LcW#Yr5lpJUzQSF$lnp@KzOlY(v^a0h=+zL*eN(DoTqS?Lb8)XQm8*5qerBY zfKrJ5h6M%qVWrjhg3wiRLYEi-@Nnime6ng~$U1L`*^e(eWA4^yNn4bsJ8rhb%ytEE z`kJ_*B)WUYGqRqyKW&c-mdDy?#_6v@V@zm_r{#_H$MP$qLe)CUWSV{h0;8uC9dJ~> zH_jU0n&-@;o|vIn;eakP#@iIYvX|jZUBrHH0A_uu&uz>FOSvgt1G8j1;N_WRp7k0c z9}j)guoGnF>d2ELH`$!0grZIgR|q7SuAmQ%QuzQExoq$yKI1ye&X7WLWYV?43a_bW zrtmK{+t8N!m}-{76KrztH=yT~C*N@lyWHjMJkg;@k9$8O&^JSL07P#^^LX5o13yJR zcwEf6ix6@Opcpr$DGVjwEl!0^);W-#rSM6r1A0NxCuuUh*|W|7jWq@f6;09!M(Vv@ F_!s}g0lNSI delta 728 zcmcaTgRx^OBj0jfUM>b85J~ux@xgW@-$ON~HLROIs(of+l$g9*%RJr~BntsGj9C&e zb_(}0CI*JpKnwx3Y&C2(Eb&q>ex9*1Lx~xT$H1V%P^4DNUc!nHNnuanDY2aF>m)z9 zKwF5Fx0bz*eR8{YB%|!)huW(3axj%ZTjgQwA|7NO&_1{fP+S2;6`TiDgKthwFNEO6JxLF&-H31U0*osn%OLIyf zzL+$b&qbGY1yDS5vxAEqvjE5kMIh%Cfov>VF*(m&igDZKPIqf&KW|1xwhs&-@&=#a z2WAlCfr!orR