From 50883a6a84553043e78804fc79442c7f32eac6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=9C=EA=B3=A8=EC=95=BD=EC=82=AC?= Date: Wed, 18 Feb 2026 14:41:40 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20100=EC=B2=98=EB=B0=A9=20=EA=B5=AC?= =?UTF-8?q?=EC=84=B1=EC=95=BD=EC=9E=AC=20=EC=88=98=20=ED=91=9C=EC=8B=9C,?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=20=EC=97=AC=EB=B6=80=20=EB=B0=B1=EC=97=94?= =?UTF-8?q?=EB=93=9C=20=ED=8C=90=EC=A0=95,=20=EC=9D=B4=EB=A6=84=EB=A7=A4?= =?UTF-8?q?=EC=B9=AD=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - official_formulas API에 ingredient_count 추가 (LEFT JOIN COUNT) - 등록 여부를 백엔드에서 판정 (official_formula_id FK 1차 + 이름 포함 매칭 fallback) - 내 처방 100처방 뱃지 매칭: startsWith → includes 변경 - 100처방 목록에 구성약재 수 뱃지 컬럼 추가 Co-Authored-By: Claude Opus 4.6 --- app.py | 63 +++++++++++++++++++++++++++++++++++--------- static/app.js | 45 ++++++++++++++++++------------- templates/index.html | 1 + 3 files changed, 79 insertions(+), 30 deletions(-) diff --git a/app.py b/app.py index c5fdc1b..b86ebea 100644 --- a/app.py +++ b/app.py @@ -455,23 +455,62 @@ def get_official_formulas(): search = request.args.get('search', '').strip() with get_db() as conn: cursor = conn.cursor() + base_query = """ + SELECT of2.official_formula_id, of2.formula_number, of2.formula_name, + of2.formula_name_hanja, of2.source_text, of2.description, of2.reference_notes, + COUNT(ofi.ingredient_id) as ingredient_count + FROM official_formulas of2 + LEFT JOIN official_formula_ingredients ofi ON of2.official_formula_id = ofi.official_formula_id + """ if search: - cursor.execute(""" - SELECT official_formula_id, formula_number, formula_name, - formula_name_hanja, source_text, description, reference_notes - FROM official_formulas - WHERE formula_name LIKE ? OR formula_name_hanja LIKE ? - OR source_text LIKE ? OR reference_notes LIKE ? - ORDER BY formula_number + cursor.execute(base_query + """ + WHERE of2.formula_name LIKE ? OR of2.formula_name_hanja LIKE ? + OR of2.source_text LIKE ? OR of2.reference_notes LIKE ? + GROUP BY of2.official_formula_id + ORDER BY of2.formula_number """, (f'%{search}%', f'%{search}%', f'%{search}%', f'%{search}%')) else: - cursor.execute(""" - SELECT official_formula_id, formula_number, formula_name, - formula_name_hanja, source_text, description, reference_notes - FROM official_formulas - ORDER BY formula_number + cursor.execute(base_query + """ + GROUP BY of2.official_formula_id + ORDER BY of2.formula_number """) formulas = [dict(row) for row in cursor.fetchall()] + + # 등록 여부 판정: official_formula_id FK 매칭 (1차) + 이름 매칭 (fallback) + # 1차: formulas.official_formula_id로 직접 연결된 처방 + cursor.execute(""" + SELECT official_formula_id, formula_name + FROM formulas + WHERE is_active = 1 AND official_formula_id IS NOT NULL + """) + registered_by_id = {} + for row in cursor.fetchall(): + oid = row['official_formula_id'] + if oid not in registered_by_id: + registered_by_id[oid] = [] + registered_by_id[oid].append(row['formula_name']) + + # 2차 fallback: 이름 기반 매칭용 + cursor.execute("SELECT formula_name FROM formulas WHERE is_active = 1") + my_formula_names = [row['formula_name'] for row in cursor.fetchall()] + + for formula in formulas: + oid = formula['official_formula_id'] + oname = formula['formula_name'] + + # 1차: FK 매칭 + if oid in registered_by_id: + formula['is_registered'] = True + formula['registered_names'] = registered_by_id[oid] + # 2차: 이름 매칭 (정확 매칭 또는 내 처방명에 원방명이 포함) + elif any(name == oname or oname in name for name in my_formula_names): + matched = [name for name in my_formula_names if name == oname or oname in name] + formula['is_registered'] = True + formula['registered_names'] = matched + else: + formula['is_registered'] = False + formula['registered_names'] = [] + return jsonify({'success': True, 'data': formulas}) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 diff --git a/static/app.js b/static/app.js index e5ef6bc..ec7ab48 100644 --- a/static/app.js +++ b/static/app.js @@ -590,11 +590,18 @@ $(document).ready(function() { tbody.empty(); response.data.forEach(formula => { - // 100처방 매칭: 정확 매칭 우선, 없으면 내 처방명이 100처방명으로 시작하는지 확인 - let officialNum = officialNames.get(formula.formula_name); + // 100처방 매칭: 1차 official_formula_id FK, 2차 이름 매칭 (원방명이 내 처방명에 포함) + let officialNum = null; + if (formula.official_formula_id) { + // FK로 연결된 경우 — official_name은 API에서 JOIN으로 내려옴 + officialNum = officialNames.get(formula.official_name); + } + if (officialNum == null) { + officialNum = officialNames.get(formula.formula_name); + } if (officialNum == null) { for (const [name, num] of officialNames) { - if (formula.formula_name.startsWith(name)) { + if (formula.formula_name.includes(name)) { officialNum = num; break; } @@ -697,13 +704,6 @@ $(document).ready(function() { function loadOfficialFormulas(search) { const params = search ? `?search=${encodeURIComponent(search)}` : ''; - // 내 처방 이름 목록을 API에서 가져와서 비교 - $.get('/api/formulas', function(formulasRes) { - const myFormulaNames = new Set(); - if (formulasRes.success) { - formulasRes.data.forEach(f => myFormulaNames.add(f.formula_name)); - } - $.get(`/api/official-formulas${params}`, function(response) { if (response.success) { const tbody = $('#officialFormulasList'); @@ -712,15 +712,24 @@ $(document).ready(function() { $('#officialFormulaCount').text(response.data.length); response.data.forEach(formula => { - // 등록 여부: 정확 매칭 또는 내 처방명이 100처방명으로 시작 - const isRegistered = myFormulaNames.has(formula.formula_name) - || [...myFormulaNames].some(name => name.startsWith(formula.formula_name)); - const statusBadge = isRegistered - ? '등록됨' - : '미등록'; + // 등록 여부: 백엔드에서 판정 (official_formula_id FK + 이름 fallback) + const isRegistered = formula.is_registered; + let statusBadge; + if (isRegistered && formula.registered_names && formula.registered_names.length > 0) { + const names = formula.registered_names.map(n => n.length > 12 ? n.substring(0, 12) + '…' : n).join(', '); + statusBadge = `${formula.registered_names.length > 1 ? formula.registered_names.length + '개 등록' : '등록됨'}`; + } else { + statusBadge = '미등록'; + } const hasNotes = formula.reference_notes ? '' : ''; + // 구성 약재 수 표시 + const ingCount = formula.ingredient_count || 0; + const ingBadge = ingCount > 0 + ? `${ingCount}종` + : `미입력`; + tbody.append(` ${formula.formula_name}${hasNotes} ${formula.formula_name_hanja || '-'} ${formula.source_text || '-'} + ${ingBadge} ${statusBadge} `); }); if (response.data.length === 0) { - tbody.html('검색 결과가 없습니다.'); + tbody.html('검색 결과가 없습니다.'); } } }); - }); // /api/formulas 콜백 닫기 } // 100처방 검색 이벤트 diff --git a/templates/index.html b/templates/index.html index 14cb441..d765ee4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -403,6 +403,7 @@ 처방명 한자명 출전 + 구성약재 상태