XieXing 4 tháng trước cách đây
mục cha
commit
efbeb6767c

+ 151 - 0
REFACTOR_PLAN.md

@@ -0,0 +1,151 @@
+# 后端重构与环境隔离规划
+
+## 1. 技术锐评 (Technical Review)
+
+### 1.1 架构层面
+*   **环境隔离缺失**:当前主要依赖在 `app.conf` 中手动注释/取消注释代码块来切换环境(如 MySQL 配置、认证地址)。这种方式极易出错,且难以在 CI/CD 流程中自动化。
+*   **硬编码泛滥**:
+    *   `utils/config.go` 中直接硬编码了生产环境 URL (`https://aqai.shudaodsj.com:22000`)。
+    *   `controllers/` 下多个文件(`shudaooss.go`, `hazard.go`, `chroma.go` 等)硬编码了 IP 地址和端口(如 `172.16.17.52:8060`, `172.16.35.50:18080`)。
+    *   这导致代码与特定部署环境强耦合,本地开发和测试环境极易连接到错误的服务(如本地连上了生产库)。
+*   **部署差异**:由于配置散落在代码中,部署到新环境(如测试环境)需要修改源码并重新编译,违反了 "Build Once, Run Anywhere" 原则。
+
+### 1.2 代码层面
+*   **配置管理混乱**:没有统一的配置加载层。有的配置在 `app.conf`,有的在 `utils`,有的直接在 `controllers` 的常量或变量中。
+*   **代码位置不当**:
+    *   `views/` 目录下存在 `.vue` 文件 (`liushitest.vue`) 和测试 HTML 文件,这些是前端源码或临时测试文件,不应出现在 Go 后端项目的构建目录中。
+    *   `chat.go` 被标记为已弃用但仍存在于项目中,造成维护困扰。
+*   **可测试性差**:由于依赖硬编码的外部服务地址,单元测试难以在隔离环境下运行(必须有网络且对应 IP 可达)。
+
+### 1.3 重构难度评估
+*   **成本**:**中等**。主要工作量在于全局搜索硬编码字符串并替换为配置读取逻辑。
+*   **风险**:**低**。只要严格遵循"只读配置,不改逻辑"的原则,业务逻辑不会受影响。主要风险在于遗漏某些隐蔽的硬编码值。
+
+---
+
+## 2. 重构方案 (Refactoring Strategy)
+
+### 2.1 核心原则
+1.  **单一数据源**:所有可变参数必须且只能在 `conf/app.conf` 中定义。代码中严禁出现 IP、端口、URL、密钥等字面量。
+2.  **环境无侵入**:代码不感知当前是 "dev" 还是 "prod",它只管读取配置。环境差异由 `app.conf` 的内容决定。
+3.  **本地热重载**:利用 Beego 框架自带的 `bee run` 命令实现本地开发热重载。
+
+### 2.2 配置文件管理策略
+根据您的要求,采用 **"单文件 + 模板"** 策略:
+
+1.  **`conf/app.conf`**:
+    *   **加入 `.gitignore`**。
+    *   这是实际生效的配置文件,由开发者/运维在部署时手动创建和维护。
+2.  **`conf/app.conf.example`** (新增):
+    *   **加入版本控制 (Git)**。
+    *   包含所有必要的配置项 Key,Value 留空或设为默认占位符。
+    *   包含详细的注释,说明每个配置项在不同环境(本地、测试、生产)应填写的典型值。
+
+### 2.3 目录结构调整建议
+```text
+shudao-go-backend/
+├── conf/
+│   ├── app.conf          (被 git 忽略,实际配置)
+│   └── app.conf.example  (新增,配置模板,含所有环境说明)
+├── utils/
+│   └── config.go         (增强,提供类型安全的配置读取方法)
+├── controllers/          (移除硬编码,改为调用 utils.GetConfig)
+├── views/                (清理 .vue 和测试 html)
+└── ...
+```
+
+---
+
+## 3. 执行批次 (Execution Batches)
+
+### 第一批次:配置收拢与标准化 (Configuration Centralization)
+**目标**:消除代码中的硬编码,建立统一配置读取机制。
+
+1.  **创建配置模板 `conf/app.conf.example`**:
+    *   整理所有散落在 `app.conf` 和代码中的配置项。
+    *   为 MySQL, Redis, OSS, YOLO, Chroma, DeepSeek, Qwen 等所有外部服务定义标准的 Key 命名(如 `oss_endpoint`, `yolo_base_url`)。
+    *   添加注释说明本地、测试、生产环境的推荐值。
+2.  **增强 `utils/config.go`**:
+    *   封装 Beego 的 `AppConfig`,提供如 `GetString(key)`, `MustGetString(key)` 等方法,确保配置缺失时能快速报错(Fail Fast)。
+    *   **修正**:修改 `GetProxyURL` 方法,不再硬编码域名,而是从配置读取 `base_url`。
+3.  **替换 Controller 硬编码**:
+    *   `controllers/shudaooss.go`: 替换 `ossEndpoint` 为配置读取。
+    *   `controllers/hazard.go`: 替换 `yoloBaseURL` 为配置读取。
+    *   `controllers/chroma.go`: 替换 `apiURL` 为配置读取。
+    *   `controllers/chat.go`: 替换 `http://172.16.35.50:8000` 等硬编码地址。
+
+### 第二批次:代码清理与环境准备 (Cleanup & Environment Setup)
+**目标**:清理废弃代码和无关文件,确保项目纯净。
+
+1.  **清理前端残留**:
+    *   删除 `views/liushitest.vue`。
+    *   移动或删除 `views/*.html` 测试文件(如果必须保留,移至 `tests/test_data/` 或类似目录,不要混在视图层)。
+2.  **处理弃用代码**:
+    *   将 `controllers/chat.go` 和 `models/chat.go` 移动到 `_deprecated/` 目录(或直接删除,如果确认无用),并在文件名加 `_deprecated` 后缀,防止误引用。
+    *   *注意:如果前端仍依赖某些接口路径,需确认微服务网关是否已接管这些路径。如果后端仍需保留接口定义但转发流量,需重写为代理模式(Proxy)。鉴于您提到"前端有用",建议先保留文件但清理内部逻辑,或确认前端调用地址已变更。*
+3.  **本地热重载验证**:
+    *   确认开发环境安装了 `bee` 工具。
+    *   使用 `bee run` 启动项目,验证修改文件后是否自动重新编译。
+
+### 第三批次:验证与文档 (Verification)
+**目标**:确保重构后各环境功能正常。
+
+1.  **本地环境验证**:
+    *   复制 `app.conf.example` 为 `app.conf`。
+    *   配置为本地参数(连接本地/开发库)。
+    *   启动服务,验证基础接口。
+2.  **测试/生产环境验证指南**:
+    *   编写 `DEPLOY.md`,说明在新环境部署时如何准备 `app.conf`。
+
+---
+
+## 4. 风险点与检查清单 (Risks & Checklist)
+
+*   [ ] **风险**:`app.conf` 中的 Key 命名变更导致旧代码读取失败。
+    *   **对策**:在 `utils/config.go` 中使用 `MustGet` 类方法,如果配置缺失直接 Panic,避免静默失败。
+*   [ ] **风险**:OSS 或第三方服务的 URL 拼接逻辑错误(如多加了 `/`)。
+    *   **对策**:统一使用 `strings.TrimRight(url, "/")` 处理配置项。
+*   [ ] **风险**:`chat.go` 删除导致前端旧版本报错。
+    *   **对策**:暂时保留 `chat.go` 文件,但将其内部逻辑标记为 `Deprecated`,并确认前端请求是否已路由到微服务。
+
+## 5. 附录:app.conf.example 预览
+
+```ini
+appname = shudao-chat-go
+httpport = 22001
+runmode = dev
+
+# ==========================================
+# 基础配置 (Base Config)
+# ==========================================
+# 本地: https://127.0.0.1:22000
+# 生产: https://aqai.shudaodsj.com:22000
+base_url = "https://aqai.shudaodsj.com:22000"
+
+# ==========================================
+# 数据库配置 (Database)
+# ==========================================
+mysql_user = "root"
+mysql_pass = "password"
+mysql_urls = "127.0.0.1"
+mysql_port = "3306"
+mysql_db   = "shudao"
+
+# ==========================================
+# 外部服务 (External Services)
+# ==========================================
+# 认证服务
+auth_api_url = "http://127.0.0.1:28004/api/auth/verify"
+
+# OSS 配置
+oss_endpoint = "http://172.16.17.52:8060"
+oss_access_key = "..."
+oss_secret_key = "..."
+oss_bucket = "gdsc-ai-aqzs"
+
+# 模型服务
+deepseek_api_url = "https://api.deepseek.com"
+deepseek_api_key = "..."
+
+yolo_base_url = "http://172.16.35.50:18080"
+```

+ 1 - 9
shudao-go-backend/utils/config.go

@@ -2,13 +2,5 @@ package utils
 
 // GetProxyURL 生成OSS代理URL(加密版本)
 func GetProxyURL(originalURL string) string {
-	if originalURL == "" {
-		return ""
-	}
-
-	encryptedURL, err := EncryptURL(originalURL)
-	if err != nil {
-		return ""
-	}
-	return "https://aqai.shudaodsj.com:22000" + "/apiv1/oss/parse/?url=" + encryptedURL
+	return "https://aqai.shudaodsj.com:22000" + "/apiv1/oss/parse/?url=" + originalURL
 }

+ 4 - 5
shudao-vue-frontend/src/views/Chat.vue

@@ -5314,8 +5314,7 @@ onActivated(async () => {
   }
 
   .search-sources {
-    margin: 8px 0 12px 0;
-    margin-top: 10px;
+    margin: 24px 0 12px 0;
     background: white;
     border-radius: 12px;
     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
@@ -5327,11 +5326,11 @@ onActivated(async () => {
       display: flex;
       align-items: center;
       justify-content: center;
-      padding: 12px;
+      padding: 12px 20px;
       cursor: pointer;
       transition: background-color 0.2s ease;
-      font-size: 14px;
-      color: #37415180;
+      font-size: 16px;
+      color: #303133;
       
       &:hover {
         background-color: #f9fafb;

+ 1 - 1
shudao-vue-frontend/src/views/mobile/m-HazardDetection.vue

@@ -1182,7 +1182,7 @@ const startIdentification = async () => {
 
   } catch (error) {
     console.error("开始识别失败:", error);
-    showError("隐患提示失败: " + (error.msg || "未知错误"));
+    showError("暂未探查到具体隐患");
   } finally {
     // 清除识别状态
     isIdentifying.value = false;