# 型号管理API **本文档引用的文件** - [backend/src/routes/models.js](file://backend/src/routes/models.js) - [backend/src/models/Model.js](file://backend/src/models/Model.js) - [backend/src/validators/model.js](file://backend/src/validators/model.js) - [backend/src/utils/response.js](file://backend/src/utils/response.js) - [backend/src/middleware/auth.js](file://backend/src/middleware/auth.js) - [frontend/src/api/model.js](file://frontend/src/api/model.js) - [backend/src/models/Ota.js](file://backend/src/models/Ota.js) - [backend/src/routes/ota.js](file://backend/src/routes/ota.js) - [backend/src/services/squiglink.js](file://backend/src/services/squiglink.js) - [frontend/src/views/model/components/ModelFormDialog.vue](file://frontend/src/views/model/components/ModelFormDialog.vue) - [backend/src/services/measurementStorage.js](file://backend/src/services/measurementStorage.js) ## 更新摘要 **变更内容** - 新增测量文件S3自动迁移功能,当更新型号的路径相关字段时自动迁移现有CSV文件 - 增强型号更新接口的智能文件处理逻辑 - 实现S3服务端文件复制与删除操作,确保数据一致性 - 优化型号管理流程,减少手动文件干预需求 ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构概览](#架构概览) 5. [详细组件分析](#详细组件分析) 6. [依赖分析](#依赖分析) 7. [性能考虑](#性能考虑) 8. [故障排除指南](#故障排除指南) 9. [结论](#结论) ## 简介 本文件为型号管理API的详细RESTful API文档,覆盖型号的CRUD操作、查询筛选与排序、数据验证规则,以及与OTA固件的一对多关联关系与级联操作。文档同时提供前端调用示例与后端实现细节,帮助开发者快速集成与维护。**最新更新**:新增squig.link外部数据源支持及测量文件S3自动迁移功能,显著提升型号数据管理的自动化程度和数据一致性保障。 ## 项目结构 型号管理API位于后端Express应用中,采用模块化设计: - 路由层:集中于 models.js,提供型号的增删改查、搜索推送、频响文件处理、**squig.link数据抓取**、**S3文件自动迁移**等接口 - 模型层:Model.js 定义数据库表结构 - 验证层:model.js 使用Zod进行请求体验证 - 工具层:response.js 统一响应格式 - 中间件:auth.js 提供鉴权保护 - **服务层**:squiglink.js 提供squig.link外部数据源抓取服务,measurementStorage.js 提供S3存储与文件迁移服务 - 前端封装:frontend/src/api/model.js 提供HTTP调用封装 - **前端组件**:ModelFormDialog.vue 集成squig.link导入功能 ```mermaid graph TB FE["前端应用
frontend/src/api/model.js
frontend/src/views/model/components/ModelFormDialog.vue"] --> R["路由层
backend/src/routes/models.js"] R --> M["模型层
backend/src/models/Model.js"] R --> V["验证层
backend/src/validators/model.js"] R --> U["工具层
backend/src/utils/response.js"] R --> A["中间件
backend/src/middleware/auth.js"] R --> S["服务层
backend/src/services/squiglink.js"] R --> MS["存储服务
backend/src/services/measurementStorage.js"] R -. 关联 .-> OTA["OTA模型
backend/src/models/Ota.js"] MS -. 存储 .-> S3["S3对象存储"] ``` **图表来源** - [backend/src/routes/models.js:1-668](file://backend/src/routes/models.js#L1-L668) - [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53) - [backend/src/validators/model.js:1-22](file://backend/src/validators/model.js#L1-L22) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:1-36](file://backend/src/middleware/auth.js#L1-L36) - [frontend/src/api/model.js:1-165](file://frontend/src/api/model.js#L1-L165) - [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97) - [backend/src/services/squiglink.js:1-322](file://backend/src/services/squiglink.js#L1-L322) - [backend/src/services/measurementStorage.js:1-165](file://backend/src/services/measurementStorage.js#L1-L165) - [frontend/src/views/model/components/ModelFormDialog.vue:1-555](file://frontend/src/views/model/components/ModelFormDialog.vue#L1-L555) **章节来源** - [backend/src/routes/models.js:1-668](file://backend/src/routes/models.js#L1-L668) - [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53) - [backend/src/validators/model.js:1-22](file://backend/src/validators/model.js#L1-L22) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:1-36](file://backend/src/middleware/auth.js#L1-L36) - [frontend/src/api/model.js:1-165](file://frontend/src/api/model.js#L1-L165) - [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97) - [backend/src/services/squiglink.js:1-322](file://backend/src/services/squiglink.js#L1-L322) - [backend/src/services/measurementStorage.js:1-165](file://backend/src/services/measurementStorage.js#L1-L165) - [frontend/src/views/model/components/ModelFormDialog.vue:1-555](file://frontend/src/views/model/components/ModelFormDialog.vue#L1-L555) ## 核心组件 - 路由控制器:models.js 提供型号的列表查询、详情获取、创建、更新、删除、搜索推送、频响文件处理、**squig.link数据抓取**、**S3文件自动迁移**等接口 - 数据模型:Model.js 定义型号字段及约束 - 请求验证:model.js 使用Zod Schema进行创建/更新的输入校验 - 统一响应:response.js 提供统一的响应结构 - 鉴权中间件:auth.js 实现Bearer Token鉴权 - **squig.link服务**:squiglink.js 提供外部数据源抓取功能 - **S3存储服务**:measurementStorage.js 提供文件上传、下载、迁移等存储服务 - 前端封装:frontend/src/api/model.js 提供HTTP调用封装 - **前端组件**:ModelFormDialog.vue 集成squig.link导入功能 **章节来源** - [backend/src/routes/models.js:133-181](file://backend/src/routes/models.js#L133-L181) - [backend/src/models/Model.js:4-50](file://backend/src/models/Model.js#L4-L50) - [backend/src/validators/model.js:3-19](file://backend/src/validators/model.js#L3-L19) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) - [backend/src/services/squiglink.js:1-322](file://backend/src/services/squiglink.js#L1-L322) - [backend/src/services/measurementStorage.js:1-165](file://backend/src/services/measurementStorage.js#L1-L165) - [frontend/src/api/model.js:13-165](file://frontend/src/api/model.js#L13-L165) - [frontend/src/views/model/components/ModelFormDialog.vue:158-464](file://frontend/src/views/model/components/ModelFormDialog.vue#L158-L464) ## 架构概览 型号管理API采用分层架构,路由层负责HTTP协议与参数解析,模型层负责数据持久化,验证层确保输入合法性,工具层统一输出格式,中间件提供安全控制,**服务层提供外部数据源集成与S3存储管理**。 ```mermaid sequenceDiagram participant C as "客户端" participant R as "路由(models.js)" participant V as "验证(model.js)" participant M as "模型(Model.js)" participant S as "服务(squiglink.js)" participant MS as "存储服务(measurementStorage.js)" participant U as "响应(response.js)" participant A as "鉴权(auth.js)" C->>R : "HTTP请求" R->>A : "鉴权检查" A-->>R : "通过/拒绝" R->>V : "请求体验证" V-->>R : "验证结果" R->>S : "外部数据源抓取可选" S-->>R : "抓取结果" R->>MS : "S3文件操作(上传/迁移)" MS-->>R : "操作结果" R->>M : "数据库操作" M-->>R : "结果" R->>U : "封装响应" U-->>C : "统一响应" ``` **图表来源** - [backend/src/routes/models.js:307-369](file://backend/src/routes/models.js#L307-L369) - [backend/src/validators/model.js:3-19](file://backend/src/validators/model.js#L3-L19) - [backend/src/models/Model.js:4-50](file://backend/src/models/Model.js#L4-L50) - [backend/src/services/squiglink.js:261-310](file://backend/src/services/squiglink.js#L261-L310) - [backend/src/services/measurementStorage.js:116-157](file://backend/src/services/measurementStorage.js#L116-L157) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) ## 详细组件分析 ### 型号数据模型 型号实体包含以下字段: - id:自增主键 - brand_name:品牌名称(必填,最大100字符) - name:型号名称(必填,最大100字符) - form:佩戴方式(可选,最大100字符) - rig:阻抗(可选,最大100字符) - source:来源(可选,最大100字符) - eq_key:EQ键(可选,最大255字符) - create_at:创建时间(默认当前时间) ```mermaid erDiagram MODEL { int id PK string brand_name string name string form string rig string source string eq_key datetime create_at } ``` **图表来源** - [backend/src/models/Model.js:4-50](file://backend/src/models/Model.js#L4-L50) **章节来源** - [backend/src/models/Model.js:4-50](file://backend/src/models/Model.js#L4-L50) ### 型号参数验证规则 - 创建请求体验证: - brand_name:字符串,长度1-100 - name:字符串,长度1-100 - form/rig/source/eq_key:字符串,最大长度分别为100、100、100、255,可为空 - 更新请求体验证: - 字段同上,但允许部分字段为空(表示不更新) **章节来源** - [backend/src/validators/model.js:3-19](file://backend/src/validators/model.js#L3-L19) ### 型号CRUD接口 #### 1) 型号列表查询 - 方法与路径:GET /api/models/ - 认证:需要登录(Bearer Token) - 查询参数: - skip:跳过数量,默认0 - limit:返回数量,默认100,上限1000 - brand_name:品牌名称(模糊匹配) - name:型号名称(模糊匹配) - sort_by:排序字段,id 或 create_at,默认id - sort_order:asc 或 desc,默认desc - 成功响应:包含items、total、skip、limit的分页数据 - 异常响应:统一错误码与消息 **章节来源** - [backend/src/routes/models.js:133-181](file://backend/src/routes/models.js#L133-L181) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) #### 2) 型号详情获取 - 方法与路径:GET /api/models/:model_id - 认证:需要登录 - 路径参数:model_id(整数) - 成功响应:型号基础字段 - 异常响应:未找到或通用错误 **章节来源** - [backend/src/routes/models.js:280-304](file://backend/src/routes/models.js#L280-L304) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) #### 3) 型号创建 - 方法与路径:POST /api/models/ - 认证:需要登录 - 内容类型:multipart/form-data - 表单字段: - measurement_file:频响文件(CSV/TXT/JSON),可选 - **squiglink_csv**:squig.link抓取的CSV内容,可选 - brand_name、name、form、rig、source、eq_key - 文件处理: - **优先级**:squiglink_csv > measurement_file > 无文件 - TXT文件自动转换为CSV - 仅允许.csv、.txt、.json - 上传至S3,键值包含source、form、brand_name、name - 成功响应:创建后的型号信息 - 异常响应:重复、格式不支持、通用错误 **更新** 新增squiglink_csv字段支持直接上传CSV内容 **章节来源** - [backend/src/routes/models.js:371-437](file://backend/src/routes/models.js#L371-L437) - [backend/src/validators/model.js:3-10](file://backend/src/validators/model.js#L3-L10) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) #### 4) 型号更新 - 方法与路径:PUT /api/models/:model_id - 认证:需要登录 - 内容类型:multipart/form-data - 路径参数:model_id(整数) - 表单字段:同创建,支持部分字段更新(传入'null'表示保持原值) - 文件处理:同创建,若上传需提供source与form - **智能文件迁移**:当未上传新文件且路径相关字段(source、form、brand_name、name)发生变更时,系统自动将现有CSV文件从旧路径迁移到新路径 - 成功响应:更新后的型号信息 - 异常响应:未找到、重复、格式不支持、通用错误 **新增** 智能S3文件自动迁移功能,确保数据一致性与完整性 **章节来源** - [backend/src/routes/models.js:439-535](file://backend/src/routes/models.js#L439-L535) - [backend/src/validators/model.js:12-19](file://backend/src/validators/model.js#L12-L19) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) #### 5) 型号删除 - 方法与路径:DELETE /api/models/:model_id - 认证:需要登录 - 路径参数:model_id(整数) - 删除流程: - 先从Meilisearch删除对应文档 - 再删除数据库记录 - 成功响应:删除成功 - 异常响应:未找到、Meilisearch删除失败、通用错误 **章节来源** - [backend/src/routes/models.js:537-563](file://backend/src/routes/models.js#L537-L563) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) ### 型号搜索、过滤与排序 - 支持按品牌名称与型号名称进行模糊过滤 - 支持按id或create_at排序,升序/降序 - 分页参数:skip、limit(上限1000) **章节来源** - [backend/src/routes/models.js:136-157](file://backend/src/routes/models.js#L136-L157) ### 型号与OTA固件的一对多关联 - 关联字段:OTA模型中的model字段用于标识对应设备型号 - 查询示例:可通过OTA路由按model字段过滤 - 级联建议:删除型号前应清理或迁移相关OTA记录,避免悬挂引用 ```mermaid erDiagram MODEL ||--o{ OTA : "一对多" MODEL { int id PK string brand_name string name } OTA { int id PK int verCode string verName string model } ``` **图表来源** - [backend/src/models/Model.js:4-50](file://backend/src/models/Model.js#L4-L50) - [backend/src/models/Ota.js:44-48](file://backend/src/models/Ota.js#L44-L48) **章节来源** - [backend/src/models/Ota.js:44-48](file://backend/src/models/Ota.js#L44-L48) ### Meilisearch搜索推送与校验 - 校验接口:POST /api/models/push-to-search/validate - 输入:model_ids数组 - 输出:通过校验的型号数量或具体校验失败项 - 推送接口:POST /api/models/push-to-search - 输入:model_ids数组 - 输出:推送成功数量、任务ID与推送数据 **章节来源** - [backend/src/routes/models.js:565-665](file://backend/src/routes/models.js#L565-L665) ### 频响文件上传与查询 - 上传:POST /api/models/(multipart/form-data),支持TXT自动转换为CSV,**新增squiglink_csv字段** - 查询:GET /api/models/:model_id/measurement(仅Eafonyoung来源且存在form时可用) **章节来源** - [backend/src/routes/models.js:371-437](file://backend/src/routes/models.js#L371-L437) - [backend/src/routes/models.js:246-278](file://backend/src/routes/models.js#L246-L278) ### EQ缓存与Meilisearch文档查询 - 获取Redis EQ缓存键:GET /api/models/:model_id/eq-cache - 获取指定Hash Field:GET /api/models/:model_id/eq-cache/field?key=... - 查询Meilisearch推送状态:GET /api/models/:model_id/meilisearch **章节来源** - [backend/src/routes/models.js:183-244](file://backend/src/routes/models.js#L183-L244) ### Squig.link外部数据源集成 #### Squig.link数据抓取接口 - 方法与路径:POST /api/models/squiglink-fetch - 认证:需要登录 - 请求体参数: - share_url:squig.link分享链接(必填) - selected_file:用户选择的特定文件名(可选) - 功能特性: - **自动抓取**:根据share_url自动解析并抓取频响数据 - **候选选择**:当存在多个匹配文件时,返回候选列表供用户选择 - **直接下载**:当提供selected_file时,直接下载指定文件 - **佩戴方式检测**:从squigsites.json自动检测佩戴方式 - 响应格式: - 单个匹配:返回brand_name、model_name、form、csv_content、data_url - 多个匹配:返回matches候选列表 - 错误:返回具体的抓取失败原因 #### Squig.link服务功能 - **URL解析**:解析squig.link分享URL,提取baseURL和share参数 - **站点发现**:从https://squig.link/squigsites.json获取站点配置 - **文件匹配**:在phone_book.json中查找匹配的测量文件 - **数据下载**:支持多种文件后缀的频响数据下载 - **格式转换**:将TXT文件转换为CSV格式 - **缓存机制**:squigsites.json内存缓存(1小时TTL) #### 前端集成 - **ModelFormDialog组件**:集成squig.link导入功能 - **自动填充**:抓取成功后自动填充品牌、型号、佩戴方式等字段 - **候选选择**:多文件匹配时提供选择对话框 - **实时预览**:显示数据URL便于验证 **新增** 完整的squig.link外部数据源支持 **章节来源** - [backend/src/routes/models.js:307-369](file://backend/src/routes/models.js#L307-369) - [backend/src/services/squiglink.js:1-322](file://backend/src/services/squiglink.js#L1-L322) - [frontend/src/views/model/components/ModelFormDialog.vue:158-464](file://frontend/src/views/model/components/ModelFormDialog.vue#L158-L464) ### S3文件自动迁移功能 #### 智能文件迁移机制 - **触发条件**:更新型号时未上传新文件且路径相关字段发生变更 - **路径字段**:source(来源)、form(佩戴方式)、brand_name(品牌名)、name(型号名) - **迁移策略**:服务端直接复制后删除,无需下载再上传 - **原子操作**:复制成功后才删除旧文件,确保数据完整性 #### S3存储服务功能 - **文件路径构建**:autoeq/measurements/{source}/data/{form}/{brandFirstChar}/{brand model}.csv - **上传服务**:uploadMeasurementToS3 - 支持CSV文件上传到S3 - **读取服务**:getMeasurementFromS3 - 从S3读取CSV文件内容 - **迁移服务**:moveMeasurementOnS3 - 实现S3文件的智能迁移 - **路径生成**:buildMeasurementKey - 根据参数生成标准S3 Key #### 迁移流程详解 ```mermaid flowchart TD A[更新型号请求] --> B{是否上传新文件?} B --> |是| C[上传新文件到S3] B --> |否| D{路径字段是否变更?} D --> |否| E[直接更新数据库] D --> |是| F[计算新旧S3路径] F --> G{路径是否相同?} G --> |是| H[跳过迁移] G --> |否| I[执行S3复制操作] I --> J[复制成功后删除旧文件] J --> K[更新数据库] C --> L[返回成功响应] E --> L H --> L K --> L ``` **图表来源** - [backend/src/routes/models.js:502-509](file://backend/src/routes/models.js#L502-L509) - [backend/src/services/measurementStorage.js:116-157](file://backend/src/services/measurementStorage.js#L116-L157) #### 错误处理与日志记录 - **文件不存在**:跳过迁移并记录日志,不影响更新操作 - **网络异常**:抛出明确错误信息,便于问题排查 - **权限问题**:详细的错误描述,指导权限配置 - **路径冲突**:自动检测路径变化,避免不必要的操作 **新增** 完整的S3文件自动迁移功能,提升数据管理自动化水平 **章节来源** - [backend/src/routes/models.js:502-509](file://backend/src/routes/models.js#L502-L509) - [backend/src/services/measurementStorage.js:110-165](file://backend/src/services/measurementStorage.js#L110-L165) ## 依赖分析 - 路由依赖:models.js 依赖Model模型、Zod验证器、响应工具、鉴权中间件,并与Meilisearch、S3服务交互,**新增squiglink服务依赖与measurementStorage服务依赖** - 模型依赖:Model.js 依赖Sequelize ORM - 前端依赖:frontend/src/api/model.js 依赖通用请求封装 - **服务依赖**:squiglink.js 依赖axios、logger,提供外部数据源抓取功能;measurementStorage.js 依赖AWS SDK、logger,提供S3存储服务 ```mermaid graph LR R["models.js"] --> M["Model.js"] R --> V["model.js(Zod)"] R --> U["response.js"] R --> A["auth.js"] R --> S["squiglink.js"] R --> MS["measurementStorage.js"] R -. 外部 .-> MS["Meilisearch"] R -. 外部 .-> S3["S3存储"] S -. 外部 .-> SL["squig.link"] MS -. 外部 .-> AWS["AWS SDK"] ``` **图表来源** - [backend/src/routes/models.js:1-668](file://backend/src/routes/models.js#L1-L668) - [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53) - [backend/src/validators/model.js:1-22](file://backend/src/validators/model.js#L1-L22) - [backend/src/utils/response.js:1-25](file://backend/src/utils/response.js#L1-L25) - [backend/src/middleware/auth.js:1-36](file://backend/src/middleware/auth.js#L1-L36) - [backend/src/services/squiglink.js:1-322](file://backend/src/services/squiglink.js#L1-L322) - [backend/src/services/measurementStorage.js:1-165](file://backend/src/services/measurementStorage.js#L1-L165) **章节来源** - [backend/src/routes/models.js:1-668](file://backend/src/routes/models.js#L1-L668) ## 性能考虑 - 分页限制:列表查询limit上限为1000,避免一次性返回过多数据 - Meilisearch超时:查询与删除文档设置超时,防止阻塞 - 文件上传:内存存储multer,建议在高并发场景下优化为流式处理或外部对象存储直传 - 验证前置:使用Zod在进入数据库操作前完成字段校验,减少无效请求 - **squig.link抓取超时**:设置15秒超时,避免外部服务影响系统性能 - **squigsites.json缓存**:1小时TTL减少对外部服务的频繁请求 - **S3迁移优化**:服务端直接复制,避免网络传输开销,提高迁移效率 - **路径变更检测**:智能判断是否需要迁移,避免不必要的S3操作 ## 故障排除指南 - 401 未登录/无效凭证:检查Authorization头是否为Bearer Token且有效 - 403 需要超级管理员权限:确认用户角色 - 404 未找到资源:确认model_id是否存在 - 重复型号:创建/更新时如提示品牌+型号已存在,请调整参数 - 文件格式不支持:仅允许.csv、.txt、.json,TXT会自动转换为CSV - Meilisearch异常:检查服务连通性与API密钥 - **squig.link抓取失败**:检查share_url格式、网络连通性、目标站点可用性 - **squig.link文件下载失败**:确认文件存在、权限正确、支持的文件后缀 - **S3迁移失败**:检查AWS凭证配置、S3 Bucket权限、网络连接状态 - **文件路径错误**:确认source、form、brand_name、name字段值符合规范 **章节来源** - [backend/src/middleware/auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26) - [backend/src/routes/models.js:314-317](file://backend/src/routes/models.js#L314-L317) - [backend/src/routes/models.js:322-325](file://backend/src/routes/models.js#L322-L325) - [backend/src/routes/models.js:450-455](file://backend/src/routes/models.js#L450-L455) - [backend/src/services/squiglink.js:33-47](file://backend/src/services/squiglink.js#L33-L47) - [backend/src/services/measurementStorage.js:149-156](file://backend/src/services/measurementStorage.js#L149-L156) ## 结论 型号管理API提供了完善的CRUD能力、灵活的查询过滤与排序、严格的输入验证、以及与搜索与存储系统的集成。**最新更新**:新增squig.link外部数据源支持及测量文件S3自动迁移功能,显著提升了型号数据的获取效率和数据一致性保障。智能文件迁移机制确保了在更新型号路径相关字段时,现有CSV文件能够自动迁移到新路径,无需人工干预。结合OTA模型的关联关系,可支撑从型号到固件升级的完整业务闭环。建议在生产环境中关注分页与超时配置、文件上传性能与安全策略、**squig.link抓取的超时与缓存策略**、**S3迁移的性能监控与错误处理**,并在删除型号前做好OTA关联清理。