# 服务层 **本文引用的文件** - [http.js](file://fms-vue/src/services/http.js) - [api.js](file://fms-vue/src/services/api.js) - [fileService.js](file://fms-vue/src/services/fileService.js) - [auth.js](file://fms-vue/src/stores/auth.js) - [FmsFile.vue](file://fms-vue/src/components/fms-file/FmsFile.vue) - [http.spec.js](file://fms-vue/tests/services/http.spec.js) - [api.spec.js](file://fms-vue/tests/services/api.spec.js) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能与并发](#性能与并发) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件面向 FMS 前端服务层,系统性说明 HTTP 请求封装、API 接口组织、文件上传下载服务、错误处理统一机制、日志记录方案、缓存策略与并发控制,以及 API 版本管理与向后兼容建议。文档以代码为依据,结合测试用例,帮助读者快速理解并安全扩展服务层能力。 ## 项目结构 服务层位于 fms-vue/src/services 下,包含: - http.js:基于 axios 的 HTTP 客户端封装,提供请求/响应拦截器、统一错误处理、登录态注入与跳转。 - api.js:业务 API 函数集合,按功能分组暴露方法,内部调用 http.post。 - fileService.js:文件服务,支持本地直传与阿里云 OSS 直传,含凭证缓存、批量上传、预览与删除。 ```mermaid graph TB subgraph "服务层" A["http.js
axios实例/拦截器"] B["api.js
业务API集合"] C["fileService.js
文件上传/下载/删除"] end subgraph "状态管理" D["auth.js
登录态/会话"] end subgraph "UI组件" E["FmsFile.vue
文件面板"] end B --> A C --> A C --> B A --> D E --> B E --> C ``` 图表来源 - [http.js:8-15](file://fms-vue/src/services/http.js#L8-L15) - [api.js:1-81](file://fms-vue/src/services/api.js#L1-L81) - [fileService.js:1-149](file://fms-vue/src/services/fileService.js#L1-L149) - [auth.js:1-49](file://fms-vue/src/stores/auth.js#L1-L49) - [FmsFile.vue:1-262](file://fms-vue/src/components/fms-file/FmsFile.vue#L1-L262) 章节来源 - [http.js:8-15](file://fms-vue/src/services/http.js#L8-L15) - [api.js:1-81](file://fms-vue/src/services/api.js#L1-L81) - [fileService.js:1-149](file://fms-vue/src/services/fileService.js#L1-L149) - [auth.js:1-49](file://fms-vue/src/stores/auth.js#L1-L49) - [FmsFile.vue:1-262](file://fms-vue/src/components/fms-file/FmsFile.vue#L1-L262) ## 核心组件 - HTTP 客户端(http.js) - 创建 axios 实例,设置 baseURL、超时、默认头。 - 请求拦截器:从 Pinia 读取 token,自动注入 Authorization 头。 - 响应拦截器:统一包装 ApiResult;对认证失败(401/40101)清理会话并跳转登录;网络异常时给出友好提示并标记 tosted 避免重复弹窗。 - 导出 post(path, data, config) 简化调用。 - API 接口(api.js) - 按功能分组:认证、数据加载、分页、保存、ID/编号生成、表结构描述等。 - 所有接口通过 http.post 发起,参数映射到后端约定字段。 - 文件服务(fileService.js) - initFileConfig/getStorageType:初始化存储模式(local 或 aliyun-oss)。 - upload:根据模式选择本地打包上传或 OSS 直传;OSS 路径下逐个回调 save-record 写入元数据。 - 凭证缓存:getOssCredential 带过期缓冲刷新。 - 工具:预览地址获取、删除文件、扩展名解析。 章节来源 - [http.js:8-110](file://fms-vue/src/services/http.js#L8-L110) - [api.js:1-81](file://fms-vue/src/services/api.js#L1-L81) - [fileService.js:1-149](file://fms-vue/src/services/fileService.js#L1-L149) ## 架构总览 下图展示一次典型的数据查询流程:页面调用 api.js 中的接口,经 http.js 的请求拦截器注入鉴权头,到达服务端后由响应拦截器统一处理成功/失败/认证失效分支。 ```mermaid sequenceDiagram participant UI as "页面/组件" participant API as "api.js" participant HTTP as "http.js" participant AUTH as "auth.js" participant S as "后端服务" UI->>API : 调用 loadDataApi(...) API->>HTTP : post('/data/loaddata', params) HTTP->>AUTH : 读取token AUTH-->>HTTP : token HTTP->>S : 发送请求(携带Authorization) S-->>HTTP : 返回{code,data,message} alt 认证成功且业务成功 HTTP-->>API : 透传response.data API-->>UI : 数据 else 业务错误 HTTP-->>API : 抛出Error(code/message/data/success=false) API-->>UI : 捕获并处理 else 认证失败(401/40101) HTTP->>AUTH : clearSession() HTTP-->>UI : 抛错(可被上层捕获) Note over HTTP,AUTH : 同时触发跳转登录页 end ``` 图表来源 - [api.js:17-24](file://fms-vue/src/services/api.js#L17-L24) - [http.js:41-104](file://fms-vue/src/services/http.js#L41-L104) - [auth.js:14-25](file://fms-vue/src/stores/auth.js#L14-L25) ## 详细组件分析 ### HTTP 客户端(http.js) - 实例配置 - baseURL 来自环境变量或默认 /api;timeout 15s;默认 JSON 头。 - 请求拦截器 - 动态读取 auth store 的 token,若存在则注入 Authorization: Bearer 。 - 响应拦截器 - 无 code 字段时按成功透传并包装为 {success:true, code:0, message:'', data}。 - code===0 视为成功,直接返回原 response。 - 业务错误:构造 Error 并附加 code、data、success=false。 - 认证错误:401/40101 时清除会话并跳转登录页,附带 reason=expired 或 session-replaced。 - 网络异常:区分超时与一般网络错误;当无 status 或 status>=500 时统一弹出全局 Message 并标记 tosted=true,避免与已有消息重复。 - 便捷方法 - post(path, data, config) 返回 response.data,简化调用方使用。 ```mermaid flowchart TD Start(["进入响应拦截器"]) --> CheckCode{"响应体是否有code?"} CheckCode --> |否| WrapSuccess["包装为ApiResult(success=true)"] CheckCode --> |是| SuccessCheck{"code===0 ?"} SuccessCheck --> |是| ReturnResp["返回原response"] SuccessCheck --> |否| AuthCheck{"是否认证错误(401/40101)?"} AuthCheck --> |是| HandleAuth["clearSession + 跳转登录"] AuthCheck --> |否| ThrowBizErr["抛业务错误Error(code/message/data/success=false)"] ReturnResp --> End(["结束"]) ThrowBizErr --> End HandleAuth --> End ``` 图表来源 - [http.js:49-104](file://fms-vue/src/services/http.js#L49-L104) 章节来源 - [http.js:8-110](file://fms-vue/src/services/http.js#L8-L110) - [http.spec.js:33-200](file://fms-vue/tests/services/http.spec.js#L33-L200) ### API 接口(api.js) - 模块化设计 - 认证:loginApi - 数据访问:loadDataApi、loadDataBySqlApi、pageDataApi - 数据变更:saveObjectApi - 标识与编码:nextIdApi、nextCodeApi - 元信息:describeApi - 参数约定 - 统一通过 http.post 发送 JSON;分页默认 page_no=1、page_size=20。 - 模块编号 moduleId 在 nextCodeApi 中转为字符串。 - 测试覆盖 - 针对每个接口的请求路径与参数进行断言,确保契约稳定。 ```mermaid graph LR P["页面/组件"] --> L["api.js"] L --> H["http.post"] H --> S["后端接口"] ``` 图表来源 - [api.js:1-81](file://fms-vue/src/services/api.js#L1-L81) - [http.js:106-108](file://fms-vue/src/services/http.js#L106-L108) 章节来源 - [api.js:1-81](file://fms-vue/src/services/api.js#L1-L81) - [api.spec.js:24-115](file://fms-vue/tests/services/api.spec.js#L24-L115) ### 文件服务(fileService.js) - 存储模式 - initFileConfig 调用 /file/config 获取 storageType(local 或 aliyun-oss),并在模块级缓存。 - 上传流程 - Local:一次性 FormData 打包多个文件,POST /file/upload,后端写磁盘并插入记录。 - OSS:共享凭证 → 逐个 XHR 直传到 OSS host → 逐个 POST /file/save-record 写入元数据。 - 凭证缓存 - getOssCredential 缓存最近一次凭证,credExpiring 根据 expire/Expiration 或 fetchedAt 计算是否过期,提前 5 分钟刷新。 - 其他能力 - getPreviewUrl:返回 mx_mapfilename。 - removeFile:POST /file/delete。 - extname:提取扩展名用于对象命名。 ```mermaid sequenceDiagram participant UI as "文件组件" participant FS as "fileService.js" participant API as "api.js" participant HTTP as "http.js" participant OSS as "OSS直传" UI->>FS : upload({files,father,moduleId,cateId}) FS->>FS : initFileConfig() alt storageType === 'aliyun-oss' FS->>FS : getOssCredential() loop 逐个文件 FS->>API : nextIdApi() API->>HTTP : post('/data/nextid') HTTP-->>API : id API-->>FS : id FS->>OSS : XHR直传(key,policy,signature,file) OSS-->>FS : 返回URL FS->>API : post('/file/save-record', record) API->>HTTP : post('/file/save-record') HTTP-->>API : 成功 API-->>FS : 成功 end else storageType === 'local' FS->>API : post('/file/upload', FormData) API->>HTTP : post('/file/upload') HTTP-->>API : List API-->>FS : 结果 end FS-->>UI : 文件记录列表 ``` 图表来源 - [fileService.js:25-85](file://fms-vue/src/services/fileService.js#L25-L85) - [api.js:64-66](file://fms-vue/src/services/api.js#L64-L66) - [http.js:106-108](file://fms-vue/src/services/http.js#L106-L108) 章节来源 - [fileService.js:1-149](file://fms-vue/src/services/fileService.js#L1-L149) ### 组件集成(FmsFile.vue) - 通过 loadDataApi 解析模块 ID,加载分类树与文件列表。 - 事件代理 uploaded/deleted/selected,便于上层处理。 - 与 fileService 配合完成上传、预览、删除等操作。 章节来源 - [FmsFile.vue:1-262](file://fms-vue/src/components/fms-file/FmsFile.vue#L1-L262) ## 依赖关系分析 - http.js 依赖 auth.js 的 Pinia store 获取 token,并在认证失败时调用 clearSession。 - api.js 仅依赖 http.js 的 post,保持轻量与高内聚。 - fileService.js 依赖 http.js 的 post 与 api.js 的 nextIdApi,形成“文件服务 -> 数据服务 -> HTTP”的层次。 - FmsFile.vue 作为 UI 入口,组合 api.js 与 fileService.js 的能力。 ```mermaid graph TB F["FmsFile.vue"] --> A["api.js"] F --> S["fileService.js"] A --> H["http.js"] S --> H S --> A H --> U["auth.js"] ``` 图表来源 - [FmsFile.vue:1-262](file://fms-vue/src/components/fms-file/FmsFile.vue#L1-L262) - [api.js:1-81](file://fms-vue/src/services/api.js#L1-L81) - [fileService.js:1-149](file://fms-vue/src/services/fileService.js#L1-L149) - [http.js:8-110](file://fms-vue/src/services/http.js#L8-L110) - [auth.js:1-49](file://fms-vue/src/stores/auth.js#L1-L49) 章节来源 - [http.js:8-110](file://fms-vue/src/services/http.js#L8-L110) - [api.js:1-81](file://fms-vue/src/services/api.js#L1-L81) - [fileService.js:1-149](file://fms-vue/src/services/fileService.js#L1-L149) - [FmsFile.vue:1-262](file://fms-vue/src/components/fms-file/FmsFile.vue#L1-L262) - [auth.js:1-49](file://fms-vue/src/stores/auth.js#L1-L49) ## 性能与并发 - 请求超时与重试 - 当前未实现全局重试;可在 http.js 中引入重试策略(如指数退避)以提升弱网稳定性。 - 并发控制 - 当前未实现全局并发限制;对于大量文件上传场景,可在 fileService.js 中对 OSS 直传增加并发度控制(如队列+最大并发数)。 - 缓存策略 - 凭证缓存:OSS 凭证带过期缓冲刷新,减少频繁请求。 - 页面级缓存:路由 keep-alive 通过 cacheKeyOf(fullPath) 生成唯一组件名,避免同组件不同参数的缓存冲突。 - 传输优化 - Local 模式一次性打包多文件上传,减少握手开销。 - OSS 直传绕过后端,降低服务器压力。 [本节为通用指导,不直接分析具体文件] ## 故障排查指南 - 认证失效 - 现象:401/40101 时自动清空会话并跳转登录页。 - 排查:检查 token 是否存在、是否过期;确认后端返回码;查看跳转 URL 是否携带 reason。 - 参考:[http.js:34-38](file://fms-vue/src/services/http.js#L34-L38)、[http.spec.js:101-157](file://fms-vue/tests/services/http.spec.js#L101-L157) - 网络异常 - 现象:ECONNABORTED 提示“请求超时”,其他网络错误提示“网络异常”。 - 排查:检查网络连通性、后端可用性;关注是否出现 tosted 标记的全局错误提示。 - 参考:[http.js:85-103](file://fms-vue/src/services/http.js#L85-L103)、[http.spec.js:159-200](file://fms-vue/tests/services/http.spec.js#L159-L200) - 业务错误 - 现象:后端返回非 0 的 code,错误信息优先使用后端 message。 - 排查:在调用处捕获 Error,读取 code/message/data 进行差异化处理。 - 参考:[http.js:64-74](file://fms-vue/src/services/http.js#L64-L74) - 文件上传失败 - 现象:OSS 直传失败或 save-record 失败。 - 排查:检查凭证是否有效、OSS 直传 host 是否正确、save-record 参数是否完整。 - 参考:[fileService.js:51-85](file://fms-vue/src/services/fileService.js#L51-L85) 章节来源 - [http.js:34-103](file://fms-vue/src/services/http.js#L34-L103) - [http.spec.js:101-200](file://fms-vue/tests/services/http.spec.js#L101-L200) - [fileService.js:51-85](file://fms-vue/src/services/fileService.js#L51-L85) ## 结论 FMS 前端服务层通过 http.js 实现了统一的请求封装与错误处理,api.js 提供了清晰分组的业务接口,fileService.js 支持灵活的本地/OSS 文件上传与凭证缓存。整体架构职责清晰、易于扩展。建议在后续迭代中补充全局重试、并发控制与更完善的日志采集,进一步提升健壮性与可观测性。 [本节为总结性内容,不直接分析具体文件] ## 附录 ### API 版本管理与向后兼容性建议 - 版本化策略 - 在 baseURL 或路径前缀中加入版本号(如 /api/v1),便于多版本并存与灰度发布。 - 在请求头中携带 clientVersion,便于服务端做兼容判断。 - 向后兼容 - 新增可选字段时保持旧字段可用;废弃字段保留一段时间并输出告警日志。 - 对关键接口增加 version 校验,不匹配时返回明确错误码与降级指引。 - 迁移与回滚 - 前端维护版本矩阵,支持按环境切换目标版本。 - 服务端提供兼容层,逐步淘汰旧逻辑。 [本节为通用指导,不直接分析具体文件]