当前位置: 首页 > news >正文

黑马微服务保险(一)

项目介绍

2.1、系统架构

四方保险属于一个保险销售平台;有两个终端:

  • 后台管理端

  • APP用户端

整体架构如下:

代理服务:保险平台和保险销售平台通过的nginx、gateway网关

路由处理:gateway网关负责路由通用服务和业务服务,同时做数据埋点和网关权限校验

服务治理:gateway网关、通用服务、业务服务、seata等服务注册和配置都放入nacos中统一管理

通用服务:抽离出加密安全、统一鉴权、消息系统、支付结算、配置存储、数据埋点、数字字典等基础微服务

基础支持:在线文档knife4j、时序数据库influxDB、redisson缓存客户端、xxl-job计划任务、spring-cloud-stream

外部服务:支付宝、微信支付、ocr、三方核保、批单、承保平台

2.2、核心业务

管理后端的核心流程:

暂时无法在飞书文档外展示此内容

app端核心流程:

暂时无法在飞书文档外展示此内容

2.3、课程内容

  • 项目介绍&环境搭建

  • 保险基础数据管理-分析与设计

  • 保险产品

  • 产品附件—对象存储

  • 产品详情-性能优化与数据脱敏

  • 投保试算-保障类

  • 投保试算-理财类

  • 投保处理-投保处理

  • 支付处理-一次性支付

  • 支付处理-周期性扣款

  • 数据埋点

  • 数据中心——时序数据库

  • 短信服务

4.1、熟悉项目

熟悉项目的第一步是熟悉项目的结构、用到的技术、编码的一些规范等。

4.1.1、项目结构

下面我们介绍一下各个模块的主要职能:

├── sfbx-cloud # 项目模块父工程,统一管理JAR版本和插件内容

│ ├── sfbx-apache-httpclient # 远程调用通信框架(第三方、保司接口调用)

│ ├── sfbx-dict # 数字字典:其他功能用到的常用字段维护

│ ├── sfbx-file # 对象存储:支持OSS、七牛云的文件存储服务

│ ├── sfbx-framework # 基础支持:缓存、消息中间件、分布式事务、线程池、脱敏、调度等支持

│ ├── sfbx-gateway # 网关路由:销售端、运营端、接口端网关路由

│ ├── sfbx-insurance # 保险业务:四方保险主业务服务包括用户端和管理端

│ ├── sfbx-points # 数据埋点:数据采集、存储、分析

│ ├── sfbx-rule # 规则引擎:规则引擎的UI支持服务

│ ├── sfbx-security # 权限系统:基于oauth2.0的权限管理

│ ├── sfbx-sms # 短信服务:支持阿里云短信、简单短信、腾讯云短信的短信微服务

│ ├── sfbx-task # 计划监听:统一调度响应和监听

│ ├── sfbx-trade # 交易服务:支持支付宝、微信支付的多种场景的支付平台

注意:

一般正式使用和发布的系统;如果所有的微服务加起来,数据库中的表大概 200+

例如:商城 300 - 500 张表; 四方保险类的大概 300 左右

4.1.2、开发规范

  • 在项目模块中;*-app的是针对app端的应用服务;*-mgt的是针对web后台管理的应用服务;

  • 项目中带有 *-interface的模块是feign客户端放置的模块工程;而应用到的 dto 放置在framework-commons模块中,该模块中的实体类以*VO结尾命名;

  • 有需要使用到其它微服务提供的接口的话就引入对应的 *-interface 即可;

  • 一般项目中,如 insurance-mgt 的 feign 包内的相当于当前服务中某些业务对外提供的接口功能(不是接口类);这些对外业务接口处理器的请求路径命名一般为 *-feign

保险基础数据-设计与开发

学习目标

  • 设计开发基础数据中的保障项、系数项、筛选项、保险分类功能

1、基础数据介绍

保险-基础数据。通过基础数据功能的学习我们主要解决下列问题:

  • 什么是基础数据,包含哪些,分别有什么作用?

  • 基础数据的表结构如何进行设计?

  • 分类与保障项、系数项、筛选项有什么关系?

上图是用户端功能截图,从中我们提炼并定义下列4个名词:

  • 保障项:通常是指在购买保险时所包含的具体风险或事件,以及在这些风险或事件发生时,保险公司愿意提供的经济赔偿或保障。不同类型的保险产品涵盖不同的保障项

  • 系数项:通常是指一系列用于计算保险费率的因子或系数。这些因子可以包括被保险人的年龄、性别、健康状况、驾驶记录、保险类型、地理位置等,它们会影响最终的保险费率计算。

  • 筛选项:通常是指一系列因素和选项,需要根据你的具体需求和情况来考虑,来选择自己需要的保险服务。

  • 分类项:通常是指保险根据不同的标准和覆盖范围进行的分类,不同的分类涵盖的方面不同,此处我们主要讨论下列几类保险:医疗、重疾、意外、养老金、旅游等分类;附录-保险分类

1)分页搜索

说明

请求信息

接口地址

http://sf.mgt.itheima.net/api/insurance-mgt/safeguard/page/{pageNum}/{pageSize}

请求方式

POST

请求参数

SafeguardVO 对应的数据结构如:{pageSize: 10, total: 1, pageNum: 1, safeguardKey: "", safeguardKeyName: "身故保险金"}

响应结果

ResponseResult<Page<SafeguardVO>> 返回的结构参考如下:

{

"code": 200,

"msg": "操作成功",

"data": {

"records": [

{

"id": "1753336375152783362",

"createTime": "2024-02-02 16:35:42",

"updateTime": "2024-05-13 18:44:03",

"createBy": "1371500419615895553",

"updateBy": "1371500419615895553",

"dataState": "0",

"creator": null,

"safeguardKey": "BZ056",

"safeguardKeyName": "身故保险金",

"safeguardVal": "[{\"val\": \"1\", \"name\": \"详见保险条款\", \"score\": \"100\"}]",

"safeguardType": "0",

"sortNo": null,

"remake": null,

"checkedIds": null

}

],

"total": 1,

"size": 10,

"current": 1,

"orders": [],

"optimizeCountSql": true,

"hitCount": false,

"countId": null,

"maxLimit": null,

"searchCount": true,

"pages": 1

},

"operatorId": 1371500419615895553,

"operatorName": "admin@qq.com",

"operatorSex": "0",

"_class": "com.baomidou.mybatisplus.extension.plugins.pagination.Page",

"tip": "本站点所有接口仅用于教学演示,所有数据均非真实数据,请勿用作其它用途!",

"operationTime": "2024-05-13 18:44:03"

}

这段代码是“保障项”业务的服务层实现类,核心功能是:

  1. 基于MyBatis-Plus框架,实现了“保障项”的分页查询逻辑:通过构建条件(按保障项编号精确查询、名称模糊查询、状态筛选)和按创建时间降序排序,执行数据库分页查询,再将查询结果从数据库实体类(Safeguard)转换为前端所需的视图对象(SafeguardVO)并返回。

  2. 同时定义了“保障项”相关的其他业务方法(如单条查询、新增、修改、删除等)的接口实现,但这些方法目前为空实现,待后续补充。

2)新建

说明

请求信息

接口地址

http://sf.mgt.itheima.net/api/insurance-mgt/safeguard

请求方式

PUT ---> 全量更新,如果不存在则新增

请求参数

{

"safeguardKeyName": "test111",

"safeguardKey": "111",

"safeguardVal": "[{\"name\":\"111\",\"val\":\"11\",\"score\":\"1\"}]",

"safeguardType": "1",

"icon": "",

"dataState": "0",

"resourceType": "F"

}

响应结果

ResponseResult<SafeguardVO> 数据结构参考如下:

{

"code": 200,

"msg": "操作成功",

"data": {

"id": "1789999088167677954",

"createTime": "2024-05-13 20:40:13",

"updateTime": "2024-05-13 20:40:13",

"createBy": "1371500419615895553",

"updateBy": "1371500419615895553",

"dataState": "0",

"creator": null,

"safeguardKey": "111",

"safeguardKeyName": "test111",

"safeguardVal": "[{\"name\":\"111\",\"val\":\"11\",\"score\":\"1\"}]",

"safeguardType": "1",

"sortNo": null,

"remake": null,

"checkedIds": null

},

"operatorId": 1371500419615895553,

"operatorName": "admin@qq.com",

"operatorSex": "0",

"_class": "com.itheima.sfbx.insurance.dto.SafeguardVO",

"tip": "本站点所有接口仅用于教学演示,所有数据均非真实数据,请勿用作其它用途!",

"operationTime": "2024-05-13 20:40:13"

}

这段代码实现了“新增保障项”的完整功能,涉及控制器层(Controller)和服务层(Service)的协作,具体如下:

  1. 控制器层(SafeguardController#createSafeguard)定义了一个处理“新增保障项”请求的接口:

    1. 通过@PutMapping指定接收PUT请求,访问路径为/safeguard(基于类的@RequestMapping);

    2. 接收前端传入的SafeguardVO对象(包含新增所需的保障项信息,如类型、编号、值等)作为请求体;

    3. 调用服务层的save方法处理业务逻辑,将结果用统一响应格式ResponseResult包装后返回给前端;

    4. 通过Swagger注解(@ApiOperation等)生成API文档,明确接口功能和参数说明。

  2. 服务层(SafeguardServiceImpl#save)实现了新增保障项的核心业务逻辑:

    1. 将前端传入的SafeguardVO(视图对象)转换为Safeguard实体类(与数据库表映射的对象);

    2. 调用MyBatis-Plus的save方法将实体类数据保存到数据库;

    3. 若保存失败(返回false),抛出运行时异常;若成功,将保存后的实体类再转换为SafeguardVO返回;

    4. 异常处理:捕获保存过程中的所有异常,打印详细日志,并抛出项目自定义异常ProjectException(携带“保存失败”的枚举标识),便于统一异常处理和返回明确的错误信息。

整体流程:前端发送包含保障项信息的PUT请求 → 控制器接收并转发给服务层 → 服务层完成对象转换、数据库保存、异常处理 → 最终返回新增结果(成功则返回新增的保障项信息,失败则返回统一错误响应)。

3)保存编辑

说明

请求信息

接口地址

http://sf.mgt.itheima.net/api/insurance-mgt/safeguard

请求方式

PATCH ---》部分更新,不存在的话更新失败

请求参数

SafeguardVO 数据结构参考如下:

{

"id": "1789999088167677954",

"createTime": "2024-05-13 20:40:14",

"updateTime": "2024-05-13 20:40:14",

"createBy": "1371500419615895553",

"updateBy": "1371500419615895553",

"dataState": "0",

"creator": null,

"safeguardKey": "111",

"safeguardKeyName": "test1112",

"safeguardVal": "[{\"val\":\"11\",\"name\":\"111\",\"score\":\"1\"}]",

"safeguardType": "1",

"sortNo": null,

"remake": null,

"checkedIds": null

}

响应结果

ResponseResult<Boolean> 数据结构参考如下:

{

"code": 200,

"msg": "操作成功",

"data": true,

"operatorId": 1371500419615895553,

"operatorName": "admin@qq.com",

"operatorSex": "0",

"_class": "java.lang.Boolean",

"tip": "本站点所有接口仅用于教学演示,所有数据均非真实数据,请勿用作其它用途!",

"operationTime": "2024-05-13 20:45:02"

}

这段代码实现了“修改保障项”的完整功能,包含控制器层(Controller)和服务层(Service)的协作,专门用于处理保障项的更新操作,具体如下:

1. 控制器层(SafeguardController#updateSafeguard)

定义了处理“修改保障项”请求的接口,核心作用是接收前端更新请求并转发给服务层:

  • 请求方式与路径:通过@PatchMapping指定接收HTTP PATCH请求,访问路径为/safeguard(基于类的@RequestMapping)。PATCH方法通常用于“部分更新”(只需传递需要修改的字段,无需传递完整对象),区别于PUT方法的“完整更新”。

  • 参数接收:通过@RequestBody接收前端传入的SafeguardVO对象,包含需要更新的保障项信息(必须包含id以确定更新哪条记录,其他字段如保障项类型、编号、值等为可选更新项)。

  • Swagger文档说明:通过@ApiOperation等注解说明接口功能,并指定SafeguardVO中需要展示的参数(尤其强调id为必需,用于定位要更新的记录)。

  • 业务调用与响应:调用服务层的update方法执行更新逻辑,将返回的布尔结果(true表示成功,false表示失败)用统一响应格式ResponseResult包装后返回给前端。

2. 服务层(SafeguardServiceImpl#update)

实现了修改保障项的核心业务逻辑,负责具体的数据库更新操作和异常处理:

  • 对象转换:将前端传入的SafeguardVO(视图对象)转换为Safeguard实体类(与数据库表映射的对象),因为数据库操作需要基于实体类执行。

  • 数据库更新:调用MyBatis-Plus提供的updateById方法,根据实体类中的id(主键)定位到具体记录,并更新其字段值(仅更新传入的非空字段,符合PATCH“部分更新”的特性)。

  • 结果校验:若updateById返回false(表示更新失败,可能因id不存在或数据库异常),抛出运行时异常提示“更新失败”。

  • 异常处理:捕获更新过程中的所有异常,通过日志记录详细堆栈信息,并抛出项目自定义的ProjectException(携带SafeguardEnum.UPDATE_FAIL枚举标识),便于全局异常处理器统一返回错误响应。

整体流程

前端发送包含“需要更新的保障项信息(含id)”的PATCH请求 → 控制器接收请求并调用服务层 → 服务层完成对象转换、数据库更新、结果校验和异常处理 → 最终返回更新是否成功的布尔结果(成功则true,失败则通过异常返回错误信息)。

该功能专门用于“部分更新”场景,允许前端仅传递需要修改的字段,减少数据传输量,同时通过id精准定位更新对象,保证操作的准确性。

4)启用/禁用

这个功能与 保存编辑是一模一样的接口;所以并不需要再写,调用 保存编辑 的接口即可。

5)删除

说明

请求信息

接口地址

http://sf.mgt.itheima.net/api/insurance-mgt/safeguard

请求方式

DELETE

请求参数

SafeguardVO 只传递了要删除的 Id 数组;数据结构参考:{checkedIds: ["1789999088167677954"]}

响应结果

ResponseResult<Boolean>

{

"code": 200,

"msg": "操作成功",

"data": true,

"operatorId": 1371500419615895553,

"operatorName": "admin@qq.com",

"operatorSex": "0",

"_class": "java.lang.Boolean",

"tip": "本站点所有接口仅用于教学演示,所有数据均非真实数据,请勿用作其它用途!",

"operationTime": "2024-05-13 21:04:36"

}

这段代码实现了保障项的批量删除功能,通过控制器层(Controller)接收删除请求、服务层(Service)执行数据库批量删除操作,同时包含参数转换、结果校验和异常处理,具体拆解如下:

  1. 控制器层(SafeguardController#deleteSafeguard)

核心作用是接收前端批量删除请求,转发给服务层,遵循 RESTful 风格设计:

  • 请求方式与路径:通过@DeleteMapping指定接收 HTTP DELETE请求,访问路径为/safeguard(继承类级别的@RequestMapping("/safeguard")),DELETE方法语义上对应 “删除资源”,符合 REST 规范。

  • 参数接收:通过@RequestBody接收前端传入的SafeguardVO对象,实际核心参数是SafeguardVO中的checkedIds(即 “选中要删除的保障项 ID 数组”)—— 前端通常会将用户勾选的多条保障项 ID 封装成数组传入。

  • Swagger 文档说明:@ApiOperationSupport(includeParameters = {"safeguardVO.checkedIds"})明确指定 API 文档中仅展示checkedIds参数,避免冗余,同时提示前端 “需传递要删除的 ID 数组”。

  • 业务调用与响应:从SafeguardVO中提取checkedIds(String 类型数组),调用服务层的delete方法执行删除逻辑,最终将服务层返回的布尔结果(true成功 /false失败)用统一响应格式ResponseResult包装后返回给前端。

  1. 服务层(SafeguardServiceImpl#delete)

核心作用是处理批量删除的业务逻辑,执行数据库操作并处理异常:

  • 参数类型转换前端传入的checkedIds是String[](字符串数组,如["1","2","3"]),但数据库中保障项的主键(id)通常是Long类型(数值型,如1、2、3),因此需要通过 Stream 流转换 Arrays.asList(checkedIds).stream().map(Long::new).collect(Collectors.toList()) 将String[]先转成 List<String>,再通过map(Long::new)将每个字符串 ID 转成 Long 类型,最终得到List<Long>(符合 MyBatis-Plus 批量删除方法的参数要求)。

  • 数据库批量删除:调用 MyBatis-Plus 提供的removeByIds(List<Long> ids)方法,实现 “根据主键数组批量删除记录”—— 该方法内部会自动拼接DELETE FROM 表名 WHERE id IN (?, ?, ?)的 SQL,比循环单条删除更高效。

  • 结果校验:若removeByIds返回false(表示删除失败,可能原因:1. 传入的id不存在;2. 数据库执行异常),则主动抛出RuntimeException,提示 “批量删除保障项失败”。

  • 异常处理:捕获删除过程中的RuntimeException(含主动抛出的失败异常、数据库异常等),通过log.error打印详细的异常堆栈信息(便于排查问题),再抛出项目自定义的ProjectException(携带SafeguardEnum.DEL_FAIL枚举标识)—— 统一交给项目的 “全局异常处理器” 处理,最终返回给前端标准化的错误响应(如错误码 + 提示信息)。

整体流程
  1. 前端:用户勾选多条保障项,将选中的 ID 数组(如["101","102"])封装到SafeguardVO的checkedIds中,发送DELETE /safeguard请求;

  2. 控制器:接收SafeguardVO,提取checkedIds,调用服务层delete方法;

  3. 服务层:

    1. 将String[]的 ID 转成List<Long>;

    2. 调用removeByIds批量删除数据库记录;

    3. 校验删除结果,失败则抛异常;

    4. 捕获异常并打日志,抛自定义异常;

  4. 响应:若删除成功,返回ResponseResult<Boolean>(true);若失败,通过自定义异常返回 “删除失败” 的错误信息。

关键细节
  • 批量删除效率:使用 MyBatis-Plus 的removeByIds而非循环单条删除,减少数据库连接次数,提升效率;

  • 参数类型适配:解决 “前端传字符串 ID、数据库存数值 ID” 的类型不匹配问题,避免 SQL 执行错误;

  • 异常规范化:通过自定义异常ProjectException统一错误码,便于前端统一处理错误场景(如弹窗提示 “删除失败,请重试”)。


关于该保险的初步总结:

一个保险包括: 多个保险方案➕多个保障项➕多个系数项➕多个筛选项➕多个附件

同时保险还会分为几个大类:医疗险,重疾险,意外(出行,运动),旅游(境内境外),人寿(终期,定期,双全),年金(养老,教育,财富)。

保险方案指的是:若保险名称是肺癌保险,会有多个保险方案,例如:

方案一报销肺癌检查费用和住院费。

方案二报销肺癌治疗费用和挂号费。

每个方案包含多个保障项:检查费(保障项),医药费(保障项),挂号费(保障项)。

每个保险同时也包含多个系数项:付款方式:年付/月付(系数项),是否自动续保(系数项)。

筛选项:特色保障:终身给,为谁买?保多久?已有健康状况? 这些都是筛选项,一款保险会绑定一些筛选项,这样筛选的时候就可以看到对应的保险。

添加保险时运用到了分布表单:也就是

一步步将这个保险的所有信息全部输入完毕。

新建保险的分布表单实现技术:

Session会话存储(最常用、最简单) 这是最直观的实现方式,利用服务器端的Session来临时存储整个表单的数据对象。 第一步:定义模型(Model) 创建一个Java类(例如  ProductFormData ),它包含所有步骤需要填写的字段(如  name ,  description ,  features ,  price  等)。这个对象将作为你整个多步表单的数据容器。 第二步:控制器(Controller)处理每一步 Step 1 (/create-product/step1): GET请求:返回第一步的视图(比如一个输入产品名称的页面)。 POST请求:接收用户输入的名称。从Session中获取或创建一个新的  ProductFormData  对象,将名称存入该对象。然后将此对象存回Session。最后重定向到第二步的URL。 Step 2 (/create-product/step2): GET请求:从Session中取出  ProductFormData  对象,用它来预填充第二步的视图(如果需要)。 POST请求:接收用户输入的功能描述,更新Session中的  ProductFormData  对象。重定向到下一步或完成页。 ...后续步骤以此类推... 最终步 (/create-product/finish): POST请求:从Session中取出完整的  ProductFormData  对象。 执行最终的业务逻辑验证。 调用Service层方法,将数据持久化到数据库。 清除Session中存储的临时  ProductFormData  对象。 返回一个成功页面或结果页。 优点:实现简单,逻辑清晰。 缺点:数据存储在服务器内存中,对大量用户并发时可能占用较多内存。用户如果开多个浏览器标签同时创建产品可能会互相干扰。

新建保险时附件的上传(简单/分片):

好的,根据您提供的图片信息,这是一个清晰、标准的文件上传流程。它展示了文件(如图片)如何从用户的前端界面最终被保存到云端对象存储,并在数据库中留下记录的全过程。

下面我将为您分步详细讲解这个流程:

流程总览

这个流程涉及四个核心角色,他们各司其职:

  1. 前端:用户直接交互的界面,负责选择文件并发起上传。

  2. 文件微服务:后端业务逻辑的处理中心,是连接前端、数据库和对象存储的桥梁。

  3. 对象存储(阿里云OSS:专业的云端文件存储服务,负责安全、可靠地存放文件本身。

  4. 数据库(MySQL:存储与文件相关的记录信息(元数据),如文件名称、大小、在OSS上的存储路径等。

整个流程是一个顺序执行的链条,如下图所示: 前端 -> 文件微服务 -> 对象存储 -> 文件微服务 -> 数据库 -> 文件微服务 -> 前端


分步详解

第1步:前端发起上传请求

  • 发生什么:用户在网页或App上选择了要上传的图片,点击“上传”按钮。

  • 细节:前端会将文件数据(二进制内容)和相关的表单信息(如文件名)通过HTTP请求(通常是POST)发送给后端的文件微服务。

第2步:文件微服务接收并转发

  • 发生什么:文件微服务的接口接收到前端发送过来的文件数据。

  • 细节:微服务会对请求进行一些初步处理,如身份认证(验证用户是否有权限上传)、校验文件大小和类型等。验证通过后,它再将文件数据转发给专业的对象存储服务(阿里云OSS)。

第3步:对象存储保存文件

  • 发生什么:阿里云OSS接收到文件数据,并将其持久化地存储在自己的服务器集群中。

  • 细节:OSS会为每个文件生成一个全局唯一的访问地址(URL)。这个地址就像是文件在互联网上的“门牌号”,后续可以通过这个地址来访问或下载该文件。

第4步:对象存储返回存储信息

  • 发生什么:文件成功保存后,OSS会向文件微服务发送一个成功响应。

  • 细节:这个响应中包含了关键信息,最主要的就是上一步生成的文件存储地址(URL),还可能包含文件哈希值(ETag)用于校验文件完整性。

第5步:文件微服务保存文件记录

  • 发生什么:文件微服务在收到OSS的成功响应后,并不会保存文件本身,而是将文件的“元信息”记录到数据库中。

  • 细节:它会向MySQL数据库的某张表(例如 file_record)插入一条新记录。这条记录通常包含:文件ID、原始文件名、在OSS中的存储路径/URL、上传用户ID、上传时间、文件大小等信息。这样做的好处是:应用服务器(微服务)轻量化,只管理业务数据,而耗费磁盘空间的大文件则由专业的OSS负责,架构更清晰、成本更低、扩展性更好。

第6步:文件微服务返回图片地址

  • 发生什么:文件微服务将最终的文件访问地址返回给前端。

  • 细节:这个地址通常就是直接从OSS获取的那个URL,或者是由微服务包装过的一个更友好的地址。

第7步:前端展示文件

  • 发生什么:前端接收到上传成功的响应和文件地址后,通知用户上传成功。

  • 细节:前端可以立即使用这个返回的图片地址来渲染、展示刚上传的图片,完成整个上传体验。

总结

这个流程是一个非常经典和高效的现代Web应用架构:

  • 职责分离:业务逻辑(微服务)、数据存储(MySQL)和文件存储(OSS)各司其职,系统更稳定,易于维护和扩展。

  • 性能与成本:利用专业的OSS服务,节省了自建文件服务器的成本和带宽压力,并且能获得更好的性能和可靠性。

  • 安全可控:所有操作都通过后端微服务中转,便于进行统一的权限验证、日志记录和安全审计。

简单来说,这个流程就是:前端传数据 -> 服务端转存到OSS -> 服务端记下文件的“户口” -> 告诉前端文件存好了在哪看


好的,这张图清晰地展示了基于分片上传技术,并整合了对象存储(阿里云OSS数据库(MySQL 的完整文件上传架构流程。

这个流程的核心思想是:将一个大文件分割成多个小块(分片)逐个上传,最后在服务端合并。这样做的好处非常明显:

  • 克服大文件上传问题:避免因网络不稳定或超时导致整个大文件上传失败。

  • 实现断点续传:即使网络中断,下次也可以只传失败的分片,而不用重头开始。

  • 充分利用带宽:可以并发上传多个分片,提高上传速度。

下面我们按照图中编号的步骤,详细讲解这个流程:


流程分步讲解

整个流程可以分为三大阶段:1. 初始化 -> 2. 分片上传循环 -> 3. 合并收尾

角色说明:

  • 前端:用户浏览器或客户端应用。

  • 文件微服务 (file-web):处理文件相关业务逻辑的后端服务,是连接前端、OSS和数据库的核心枢纽。

  • 对象存储 (阿里云OSS):负责存储文件内容的云服务,性价比高,无限扩展。

  • 数据库 (MySQL):用于存储文件的上传记录、分片信息、最终地址等元数据。


第一阶段:初始化 (步骤 1-6)

这是上传开始前的准备工作。

  1. 前端选择要上传的文件后,首先向文件微服务发送一个“初始化分片上传”的请求。

  2. 文件微服务接收到这个请求。

  3. 文件微服务随即向阿里云OSS发起一个初始化分片上传的请求。OSS为此文件上传会话创建一个唯一的标识。

  4. OSS返回响应,其中包含一个重要的凭证:分片上传标识符 (uploadId)。这个uploadId将用于标识本次整个文件的上传会话,后续所有分片的上传都必须带上它。

  5. 文件微服务将文件信息(如文件名、大小、uploadId、状态等)保存到MySQL数据库中(图中未明确表名,通常是tab_file或类似表)。这一步是为了记录“有一个文件开始上传了”,便于后续查询和管理。

  6. 文件微服务将OSS返回的uploadId等信息返回给前端

至此,前端拿到了“通行证”uploadId,可以开始上传分片了。


第二阶段:分片上传与循环 (步骤 7-12)

这是上传的核心环节,前端将文件切块,并循环上传每一个分片。

  1. 前端根据预设的分片大小(如每片5MB)将文件切割成多个分片。

  2. 对于每一个分片前端都向文件微服务发起上传请求。请求中会携带分片参数(如分片序号partNumber、uploadId)和分片文件数据。

  3. 文件微服务收到请求后,将分片文件内容转发给OSS

  4. OSS成功接收到一个分片后,会返回一个分片上传结果(partETag)。ETag是OSS为该分片生成的唯一标识,用于后续合并时验证分片的有效性和顺序。

  5. 文件微服务将这个分片的上传记录(包括uploadId, partNumber, partETag等)保存到数据库的分片记录表(tab_file_part) 中。这一步至关重要,是实现断点续传的关键。即使服务重启,也能从数据库里查询到哪些分片已经上传成功。

  6. 文件微服务将分片上传成功的结果返回给前端

步骤 8 到 12 会循环执行,直到文件的所有分片都上传完毕。


第三阶段:合并与收尾 (步骤 13-18)

所有分片上传完成后,需要通知OSS将它们合并成一个完整的文件。

  1. 前端在所有分片上传成功后,向文件微服务发送一个“汇总并合并分片”的请求。

  2. 文件微服务OSS发起“合并分片”的请求。这个请求会携带uploadId以及从数据库查询到的所有分片的partNumber和partETag列表。

  3. OSS根据收到的列表,按分片序号顺序将所有分片合并成一个完整的文件。合并成功后,OSS会返回一个最终的、完整的文件访问地址(URL)。

  4. OSS将合并结果返回给文件微服务。

  5. 文件微服务执行清理和更新操作:

    1. 删除分片记录:从数据库的tab_file_part表中删除本次上传的所有分片记录,因为它们已经没用了。

    2. 保存完整文件地址:在数据库的主文件表(如tab_file)中更新该文件的状态为“上传成功”,并保存OSS返回的最终文件地址。

  6. 文件微服务通知前端文件合并成功,并返回文件的展示地址。前端随即向用户展示上传成功的文件。

总结

这个架构的优点在于:

  1. 职责分离:业务服务器(微服务)只处理业务逻辑和元数据管理,沉重的文件存储和传输工作由专业的OSS负责,减轻了主服务器的压力。

  2. 安全可控:所有对OSS的请求都通过微服务中转,避免了前端直接与OSS交互可能带来的安全密钥泄露风险,也便于做权限校验、日志记录等操作。

  3. 可靠性高:通过数据库记录上传状态,完美支持了断点续传和错误重试。

  4. 扩展性强:轻松应对大文件上传和高速上传的需求。

希望这个讲解能帮助你完全理解这张流程图!

好的,这份讲义详细介绍了阿里云OSS的分片上传(Multipart Upload) 功能。下面我将为您分步详细讲解其核心概念、适用场景、流程和限制。

一、什么是分片上传?

分片上传是一种将大文件分割成多个较小部分(称为分片或Part)分别上传,最后在服务端将这些分片组合成一个完整文件的技术。

  • 类比:想象一下你要搬运一个巨大的衣柜上楼。因为衣柜太大无法直接通过楼梯,你会把它拆解成木板、抽屉、门板等多个部分,分批搬上楼,最后再重新组装起来。分片上传的过程与此非常相似。

二、为什么要使用分片上传?(使用场景)

讲义中明确了三种主要场景,这些都是简单上传(直接PutObject)无法很好解决的问题:

  1. 大文件加速上传(核心场景)

    1. 问题:单个超大文件(如5GB以上的视频、数据库备份、镜像文件)使用简单上传,耗时极长,且容易因网络波动或超时而失败,导致前功尽弃。

    2. 解决方案:分片上传允许并发上传多个分片,充分利用带宽,极大缩短总上传时间。

  2. 网络环境较差

    1. 问题:在弱网环境下,上传大文件极易中断。

    2. 解决方案:分片上传支持断点续传。如果某个分片上传失败,只需重新上传这个失败的分片即可,而无需重传整个文件。这节省了大量时间和流量。

  3. 文件大小不确定(流式上传)

    1. 问题:在某些场景下(如视频监控、直播录制),文件还在生成中,最终大小未知,但需要立即开始上传。

    2. 解决方案:可以初始化一个分片上传任务,然后一边生成数据,一边上传一个个分片,最后在所有数据生成完毕后再完成合并。

三、分片上传的流程(核心步骤)

讲义中描述的流程是使用OSS API的标准流程,整个过程如下图所示:

  1. 初始化分片上传(InitiateMultipartUpload)

    1. 作用:告诉OSS:“我要开始一个新的分片上传任务了”。OSS会为这个任务创建一个唯一的标识符:UploadId

    2. 后续作用:后续所有针对这个文件的分片上传、合并等操作,都必须带上这个 UploadId,以便OSS知道这些操作属于同一个任务。

  2. 上传分片(UploadPart)

    1. 作用:将文件切分后的各个部分依次或并发地上传到OSS。

    2. 关键参数

      • UploadId:来自第一步的响应。

      • PartNumber(分片序号):必须从1开始顺序编号(1, 2, 3, ...)。这是OSS最后合并分片的唯一依据。

      • PartData:分片的具体数据内容。

    3. 重要特性

      • 并发上传:各个分片之间没有依赖关系,可以同时上传,这是加速的关键。

      • 返回ETag:每个分片上传成功后,OSS都会返回一个ETag(该分片的唯一标识)。客户端必须妥善保存每个分片的 PartNumber 和其对应的 ETag,这是最后合并的凭证。

  3. 完成/合并分片(CompleteMultipartUpload)

    1. 作用:当所有分片都成功上传后,调用此接口通知OSS:“所有分片都传完了,请根据我提供的 PartNumber 顺序和 ETag 列表,把它们合并成一个完整的文件”。

    2. 输入:UploadId 和保存好的 (PartNumber, ETag) 列表。

    3. 输出:合并成功后,OSS会返回一个完整的Object,可以像普通文件一样访问。

  4. 放弃上传(AbortMultipartUpload)

    1. 作用:如果在上传过程中想终止任务(例如用户取消了操作),可以调用此接口。

    2. 结果:OSS会释放所有已经上传的分片占用的空间。这是一个清理操作,可以避免为未完成的任务支付存储费用。

四、分片上传的限制(非常重要)

在使用分片上传时,必须遵守以下规则,否则请求会被拒绝:

限制项具体规则说明
​​文件大小​​不超过 ​​48.8 TB​​几乎满足了所有场景下的需求。
​​分片数量​​​​1 到 10,000​​ 个一个上传任务最多只能有1万个分片。
​​分片大小​​​​除最后一个分片外​​,每个分片大小​​必须 >= 100 KB​​,​​且 <= 5 GB​​。这是最重要的规则。需要根据文件大小提前规划好分片策略。

分片策略举例: 假设你有一个 12 GB 的文件。

  • 错误策略:分成 2 个 6 GB 的分片。→ 错误!每个分片不能超过 5 GB。

  • 错误策略:分成 12000 个 1 MB 的分片。→ 错误!分片数量不能超过 10000 个。

  • 正确策略:分成 3 个分片:Part1 = 5 GB, Part2 = 5 GB, Part3 = 2 GB。最后一个分片(2GB)虽然小于5GB,但大于100KB,这是完全符合规则的。

总结

分片上传是处理大文件或不稳定网络上传的必备方案。它通过分治、并发、断点续传的思想,完美解决了简单上传的痛点。理解其流程(初始化->上传分片->合并)和关键概念(UploadId, PartNumber, ETag)以及严格遵守分片大小和数量的限制,是成功实现这一功能的关键。 UploadId是OSS产生返回给客户端,然后客户端每次上传时都要携带它。

PartNumber是客户端自己产生的,每个分片给一个序号按顺序,最后传输结束后全部发给OSS。

ETag是OSS产生返回给客户端的,每成功接收一个分片后就返回给客户端,最后由客户端统一汇总发给OSS。

上传技术代码说明:

好的,我们来详细讲解一下这段 FileUpLoadController.java 代码的功能。

这段代码是“四方保险”系统中文件微服务(file-web) 的核心控制器(Controller)。它对外提供了一组完整的RESTful API,专门用于处理所有类型的文件上传请求,是前端与后端文件存储系统(阿里云OSS)之间的桥梁。

核心功能总览

这个控制器主要实现了两种文件上传模式:

  1. 简单上传:适用于小文件、图片等。一次性将整个文件上传到服务器。

  2. 分片上传:适用于大文件。将文件切割成多个小块(分片)逐一上传,最后在服务器端合并。这种方式支持断点续传,克服网络不稳定问题。


分方法详细讲解

  1. 简单上传:upLoad 方法

  • 对应API路径:POST /file/up-load

  • 功能:接收一个完整的文件,直接上传到阿里云OSS。

  • 流程解析

    • 接收参数:通过 @RequestParam("file") MultipartFile file 接收前端传来的文件流,通过 FileVO fileVO 对象接收额外的元数据(如业务类型、存储目录等)。

    • 设置参数

      1. fileVO.setBucketName(...):从统一配置中获取存储桶名称,覆盖前端可能传递的值,保证安全性和一致性。

      2. fileVO.setCompanyNo(...):设置公司编号,用于数据隔离(多租户)。

    • 构建请求对象:将Spring的 MultipartFile 转换为系统内部定义的 UploadMultipartFile 对象,便于后续服务层统一处理。

    • 调用服务:调用 fileService.upLoad(...) 方法,将具体的上传逻辑交给服务层处理。服务层会负责与OSS交互、保存文件记录到数据库等。

    • 返回结果:将上传成功后的文件信息(如文件ID、访问URL等,封装在 FileVO 中)返回给前端。

  • 适用场景:用户头像、保险产品小图片、文档等大小在几MB以内的文件。

  1. 分片上传 - 初始化:initiateMultipartUpload 方法

  • 对应API路径:POST /file/initiate-multipart-up-load

  • 功能:开始一个分片上传任务前,向OSS申请一个本次上传任务的唯一标识(uploadId)。

  • 流程解析

    • 接收一个 FileVO 对象,包含文件的基本信息。

    • 同样设置存储桶和公司编号。

    • 调用 fileService.initiateMultipartUpload(...),服务层会与OSS通信,获取一个 uploadId。

    • 将这个 uploadId 返回给前端。

  • 重要目的:前端拿到 uploadId 后,在后续上传每一个分片时都必须带上它,这样OSS才知道这些分片属于同一个文件。

  1. 分片上传 - 上传分片:uploadPart 方法

  • 对应API路径:POST /file/up-load-part

  • 功能:上传文件的一个分片。

  • 流程解析

    • 接收一个文件分片(MultipartFile file)和分片信息(FilePartVO filePartVO)。FilePartVO 中应包含 uploadId 和当前分片的序号(partNumber)等。

    • 设置存储桶和公司编号。

    • 构建分片文件对象。

    • 调用 fileService.uploadPart(...),服务层会将这个分片上传到 OSS。OSS 会为每个成功上传的分片返回一个唯一标识(ETag)。

    • 将当前分片的 ETag 信息返回给前端。前端需要缓存所有这些 ETag,在最后合并时会用到。

  • 循环执行:前端会根据文件大小和分片大小,循环调用此接口,直到所有分片上传完毕。

  1. 分片上传 - 合并分片:completeMultipartUpload 方法

  • 对应 API 路径:POST /file/complete-multipart-up-load

  • 功能:在所有分片都上传成功后,通知 OSS 将所有分片按顺序合并成一个完整的文件。

  • 流程解析

    • 接收 FileVO,其中应包含 uploadId 和前端收集的所有分片的 ETag 列表。

    • 设置存储桶。

    • 调用 fileService.completeMultipartUpload(...)。服务层会告诉 OSS:“请根据这个 uploadId,找到所有分片,并按 partNumber 顺序和提供的 ETag 列表将它们合并”。

    • 合并成功后,OSS 会返回完整文件的 ETag。同时,服务层也会在数据库中创建该文件的最终记录。

    • 将结果返回给前端,表示整个大文件上传成功。


代码设计与架构亮点

  1. 清晰的职责分离

    1. Controller 层只负责:接收参数、校验参数、调用服务、返回结果。它不包含任何具体的业务逻辑。

    2. 所有与 OSS 的交互、数据库操作等核心逻辑都封装在 IFileService 接口及其实现中,符合“单一职责”原则。

  2. 安全性与一致性

    1. 使用 ossAliyunConfigProperties.getBucketName() 覆盖前端传递的 bucketName,防止前端恶意指定存储到其他位置,保证了配置的集中管理和安全。

    2. 自动设置 CompanyNo,实现了数据层面的多租户隔离。

  3. 良好的 API 设计

    1. 使用了 @ApiOperation, @ApiImplicitParam 等 Swagger 注解,自动生成 API 文档,便于前后端协作。

    2. 返回值统一包装为 ResponseResult<T>,格式标准,便于前端处理。

  4. 支持多种上传模式

    1. 同时提供了简单上传和分片上传两套接口,满足了不同场景下的需求,使系统更健壮。

总结

这段 FileUpLoadController 代码是一个设计优良的微服务接口层实现。它通过四个核心方法,为整个“四方保险”系统提供了一个统一、安全、高效且可扩展的文件上传入口。无论是小图片还是大文件,都能通过调用它提供的 API 得以妥善处理,完美践行了将“文件功能”抽离为独立微服务的架构思想。

http://www.dtcms.com/a/509241.html

相关文章:

  • 包头网站优化怎样做自己的网络平台
  • var func 和 func func(){}
  • 制作一个静态网站的步骤南京网站建设公司大全
  • 重庆工程建设造价信息网站天津通用网站建设收费
  • 网站保障体系建设wordpress淘宝
  • 标志设计网站推荐什么是电子商务网站推广
  • 标准IO-文件IO(缓冲区)
  • 做个简单的企业小网站企业邮箱怎么申请注册账号
  • 网站建设 自适应禄劝网络推广外包
  • [特殊字符] 神奇的二维世界:信息与意识的未来宇宙
  • 无线通信眼镜专利拆解:骨传导换能器与镜腿集成的声学传输机制研究
  • 网站域名是不是就是网址代理网页是干什么的
  • 网站可以换域名吗网站设计中常见的错误
  • 微机课做网站单页面网站模板怎么做
  • 万江建筑培训中心泰州seo公司
  • 如何设计一个完整的网站jsp网站开发难吗
  • 个人网站建设方案书 范文怎么做自己的代刷网站
  • 旅游网站做模板素材购物网站建设需要多少钱
  • 【十年后台管理系统】准备工作
  • 废旧网站哪个做的最好百度首页网站的设计
  • Progressbar组件自定义样式
  • 31.下一个排列
  • Moectf2025-webmisccrypto (持续更新
  • 济宁网站建设找哪家室内设计师工作室
  • 昆明网站建设注意事项seo咨询推广
  • 个人网站模板 html天猫优惠券网站怎么做
  • 建房城乡建设部网站wordpress 菜单保存在哪里
  • 珠海网站建设zhkmkj中国采购与招标网官网首页
  • Python开发可视化音乐播放器教程(附代码)
  • 【u-boot】u-boot支持的文件系统