Forráskód Böngészése

优化跳转和对话

liuchengsen 1 hónapja
szülő
commit
cde63416d3

+ 1 - 5
backend-java/src/main/java/com/pharmacopoeia/controller/ChatController.java

@@ -586,17 +586,13 @@ public class ChatController {
                         if (!sourceBuilder.isEmpty()) sourceBuilder.append(" ");
                         sourceBuilder.append(sourceVolume);
                     }
-                    String src = (String) d.getOrDefault("source", "");
-                    if (!src.isEmpty()) {
-                        if (!sourceBuilder.isEmpty()) sourceBuilder.append(" ");
-                        sourceBuilder.append(src);
-                    }
                     String fullSource = sourceBuilder.toString();
 
                     content = content.replaceAll("\\s*来源:.*$", "");
                     content = content.replaceAll("[\\r\\n]+", " ").trim();
                     String excerpt = content.length() > 500 ? content.substring(0, 500) + "…" : content;
                     return Map.<String, Object>of(
+                            "drug_id", d.getOrDefault("drug_id", ""),
                             "name", drugName,
                             "section", sectionDisplay,
                             "category", category != null ? category : "",

+ 5 - 7
data-pipeline/docx_ingest.py

@@ -415,8 +415,10 @@ def detect_section_header(text: str) -> str | None:
 
 
 def get_volume_group(filepath: str) -> str:
+    """从目录结构判断药典部别:检查路径中是否包含 output/output2/output3/output4 作为独立目录"""
     for key, vol in VOLUME_MAP.items():
-        if key in filepath:
+        # 用路径分隔符包围,避免 "output" 误匹配 "output2"
+        if os.sep + key + os.sep in filepath or filepath.endswith(os.sep + key):
             return vol
     return "一部"
 
@@ -617,12 +619,8 @@ async def ingest_docx_entries(entries: list[dict], engine, start_idx: int = 0):
 
 
 async def dedup_old_versions(engine):
-    """2025 版为主,2020 版保留为补充数据。
-    不在数据库层面标记 inactive,由检索层通过 source_version 优先级(+0.05)
-    自动将 2025 版排在前面,2020 版作为降级补充。
-    """
-    print("  📋 版本共存:2025 版(优先)+ 2020 版(补充),均保持 active")
-    # 不再自动标记旧版为 inactive
+    """2025 年版为唯一基准,旧版数据不保留。"""
+    pass
 
 
 async def main():

+ 46 - 1
docs/OPERATIONS_GUIDE.md

@@ -365,13 +365,58 @@ SELECT 'chunks', COUNT(*) FROM drug_chunks WHERE vec IS NOT NULL;
 
 ---
 
+## 十二、完全重导数据(含图片提取)
+
+> 适用:DOCX 解析逻辑更新、图片提取功能上线、数据清空重建
+
+```bash
+cd /opt/pharmacopoeia-ai
+
+# 1. 停 Java
+bash tools/java-deploy.sh stop
+
+# 2. 清空旧数据
+docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "
+TRUNCATE drug_chunks CASCADE;
+TRUNCATE drugs CASCADE;
+"
+
+# 3. 重建表结构
+docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia < database/schema.sql
+
+# 4. 创建图片目录
+mkdir -p static/images/drugs
+
+# 5. 导入 Wiki(~2 分钟)
+cd data-pipeline && source venv/bin/activate
+python import_all.py
+
+# 6. 导入 2025 药典 DOCX(~2-4 小时,含图片提取)
+screen -S reimport
+python docx_ingest.py
+
+# 7. 跑完后验证
+python verify_import.py
+
+# 8. 修复 vec 列
+docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "
+UPDATE drug_chunks SET vec = (embedding::text)::vector WHERE vec IS NULL AND embedding IS NOT NULL;
+"
+
+# 9. 重启 Java
+cd /opt/pharmacopoeia-ai && bash tools/java-deploy.sh restart && nginx -s reload
+```
+
+---
+
 ## 快速参考
 
 | 路径/命令 | 用途 |
 |-----------|------|
 | `/opt/pharmacopoeia-ai/` | 项目根目录 |
 | `/opt/2025/` | DOCX 药典数据源 |
+| `/opt/pharmacopoeia-ai/static/images/drugs/` | DOCX 提取的图片 |
 | `docker-compose ps` | 查看 PG/Redis 状态 |
-| `screen -r docx-ingest` | 重连 DOCX 导入任务 |
+| `screen -r reimport` | 重连重导任务 |
 | `http://localhost:9000/api/v1/health` | 健康检查 |
 | `/var/log/pharmacopoeia/backend.log` | Java 日志 |

+ 4 - 2
static/index.html

@@ -171,6 +171,8 @@ function esc(s){if(!s)return'';return s.replace(/&/g,'&amp;').replace(/</g,'&lt;
 function escAttr(s){if(!s)return'';return s.replace(/"/g,'&quot;').replace(/'/g,'&#39;')}
 function md2html(h){
   if(!h)return'';h=esc(h);
+// 保留图片标签不转义
+h=h.replace(/&lt;img\s+src=&quot;(.+?)&quot;\s*\/?&gt;/g,'<img src="$1" style="max-width:100%">');
   h=h.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>');
   h=h.replace(/⚠️?\s*([^\n<]+)/g,'<span class="warn">$1</span>');
   h=h.replace(/\[([^\]]+)\]\((https?:\/\/[^\s<>)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" class="ext-link">$1</a>');
@@ -339,7 +341,7 @@ function buildSourceTags(){
     var name=s.name||'未知';
     var ver=s.source||s.category||'';
     var gkey=name+'|'+ver;
-    if(!groups[gkey]){groups[gkey]={sections:[],seen:new Set(),name:name,source:ver};order.push(gkey);}
+    if(!groups[gkey]){groups[gkey]={sections:[],seen:new Set(),name:name,source:ver,drugId:s.drug_id||''};order.push(gkey);}
     var key=s.section;
     if(!groups[gkey].seen.has(key)){
       groups[gkey].seen.add(key);
@@ -354,7 +356,7 @@ function buildSourceTags(){
     var dn=order[g],grp=groups[dn],cnt=grp.sections.length;
     h+='<div style="margin-bottom:10px">';
     h+='<div class="src-title">📖 '+esc(dn)+' · '+esc(grp.source)+' ('+cnt+'个栏目)';
-    h+=' <a href="javascript:void(0)" onclick="switchTab(\'drugs\');loadDrugDetailBySrc(\''+escAttr(dn)+'\')" style="font-size:11px;color:#002FA7">查看详情 →</a></div>';
+    h+=' <a href="javascript:void(0)" onclick="switchTab(\'drugs\');loadDrugDetail(\''+escAttr(grp.drugId)+'\')" style="font-size:11px;color:#002FA7">查看详情 →</a></div>';
     for(var s=0;s<grp.sections.length;s++){
       var sec=grp.sections[s];
       h+='<span class="source-tag" onclick="var p=this.parentNode;this.classList.toggle(\'open\')">'+esc(sec.section)+'<div class="src-excerpt"><strong>'+esc(dn)+' · '+esc(sec.source)+'</strong><br><br>'+esc(sec.excerpt)+'</div></span>';