# MyShoppingMall **Repository Path**: lemzdo/shoppingMall ## Basic Information - **Project Name**: MyShoppingMall - **Description**: springboot搭建的商城项目 - **Primary Language**: Java - **License**: MulanPSL-1.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-16 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 微服务架构开发商城项目 开发文档 https://easydoc.xyz/s/78237135/ZUqEdvA4/OXTgKobR 1.git操作 在码云建好仓库之后,直接File-New-Project from version control 输入仓库url 直接从码云copy项目到idea 后续可以直接从idea put代码到码云 2.搭建微服务模块 在总project中建立各个module作为各个微服务模块 各个模块都先加上springboot web 起步依赖 模块之间需要通信 需要加上spring cloud routing的 OpenFeign用于通信 mail-product 商品模块 mail-coupon 优惠模块 mail-member 用户模块 mail-order 订单模块 mail-ware 购物车模块 3.完善我们的主服务mail(聚合其他模块用) 复制一份pom文件到主project下,进行修改 修改artifactId,name,description 删除父依赖,其他所有依赖 新增打包方式为pom 使用modules来进行聚合(一个一个module小标签集合这些module) 右侧maven管理中 将聚合模块的pom加进去 会变成root 对这个maven进行clean 小模块都会clean 4.git ignore 修改gitignore文件 只需要提交源码以及pom文件,保证项目的清洁性 5.初始化mysql环境 根据各个模块,初始化对应的表 6.前后端分离,管理上线的模块 使用码云上人人开源的项目renren-fast 和 renren-fast-vue renren-fast-vue 删除git文件夹,后面再前端软件中打开 (Visual Studio Code) renren-fast直接删除git文件夹,放到mail项目中 作为一个单独模块 也需要在mail总pom文件中进行管理 renren-fast 中db文件夹下有mysql的脚本文件,新建对应的数据库mail_admin以及表 修改配置文件中的数据库信息 renren-fast可以直接启动 配合renren-fast-vue到前端软件中启动 即可访问 8080/renren-fast 7.使用人人开源的逆向工程完成三层架构的快速搭建 clone人人开源的renren-generator 删除git文件夹,复制到项目里 将这个代码生成器也加入到pom文件的module中 generator的配置文件中修改需要生成代码对应的数据库 properties中修改包名,模块名等 生成的代码可以文件夹直接拷贝到响应的模块文件夹中 8.common 所有的微服务模块生成的代码都需要一些工具类,一些公共依赖 创建一个maven工程,mail-common 用来存放公共的依赖和工具类等 其他的微服务模块都需要依赖这个公共模块 公共模块中需要加入依赖 mybatis-plus lombok httpcomponents commons-lang 工具类等 9.整合mybatis plus 1)、导入依赖 2)、配置 配置数据源 导入数据库驱动(所有微服务都要 写在common中) 各个微服务配置自己的mysql参数 url driver username password 配置MyBatis-Plus 各个微服务模块 各自的主application.java中 MapperScan 扫描mapper文件(dao层) 声明xml文件位置(resources/mapper),mybatis-plus的mapper-location classpath*:/mapper/**/*.xml 这也是默认值 yml中设置主键自增 id-type设置成auto 10.spring-cloud-alibaba 通过spring-cloud-alibaba的一些组件用来搭建分布式服务架构 注册中心,网关等 spring cloud 部分组件停止维护和更新;部分环境搭建复杂,没有完善的可视化界面;配置复杂,难以上手 spring-cloud-alibaba 阿里使用的组件经历了考验,性能强,设计合理,开源;成套的产品搭配完善的可视化界面;搭建简单; SpringCloud Alibaba-Nacos:注册中心(服务发现,注册),配置中心(动态配置管理) SpringCloud-Ribbon:负载均衡 SpringCloud-Feign:声明式Http客户端(调用远程服务) SpringCloud Alibaba-Sentinel:服务容错(限流,降级,熔断) SpringCloud-GateWay:API网关(webflux编程模式) SpringCloud-Sleuth:调用链监控 SpringCloud Alibaba-Seata:原Fescar 即 分布式事务解决方案 多个微服务组件都会使用SpringCloud组件 所以依赖写在common里 product产品服务模块的 三级分类API(category分类表) 11.编写产品三级分类功能 查询出所有产品的分类,并根据父子关系,以树形结构存储 在实体类中添加自定义的属性时,需要加@TableField 注解 exist = false 表示该字段在表中不存在 12.逻辑删除功能 实际开发中很少使用物理删除(直接delete) 一般使用逻辑删除 即 表中数据有个字段代表是否显示 例如菜单表中的show_status,1表示显示,0表示不显示相当于删除 Mybatis-plus 逻辑删除功能配置 1.主配置文件中配置逻辑删除全局规则(和默认一样可以不配置) logic-delete-value: 1 //默认表示 1 代表删除 logic-not-delete-value: 0 2.配置逻辑删除组件(3.1.1以上不需要配置) 3.对应的实体类的字段 上面加上逻辑删除注解@TableLogic 代表该字段是一个逻辑删除字段 该字段的规则 和我们全局规则相反 我们是1显示0删除,可以在注解上自定义规则 value表示不删除 delval表示删除 配置完成之后 delete语句就会变成update,将逻辑删除字段更新 13.新增功能 直接使用逆向生成的代码即可,前端发post请求携带参数回来 14.修改功能 普通修改不用变,对于拖拽功能新增一个批量修改的方法,前端传递json数组回来,代码中批量根据id更新 product产品服务模块的 品牌管理API(pms_brand品牌表) 15.阿里云oss 云存储 微服务模块中,每个服务模块都可能是集群环境的,上传文件不能存到某一台具体服务器上(访问其他服务器就拿不到这个文件了) 选择云存储方式 可以前端上传文件,后端拿到流,上传到oss 对服务器压力比较大 推荐 前端上传文件,服务器签名后直传 即浏览器向服务器发请求,拿到包含很多信息的防伪令牌,再直接上传到oss 阿里云原生OSS使用 1.配置阿里云oss maven依赖 2.主配置文件中自定义oss需要的一些参数(避免代码写死),方便代码中直接@value引用 简单上传文件 https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.2.7.47b059aaqEHzB9#concept-84781-zh SpringCloud-alibaba 封装的oss使用(推荐使用) 1.配置maven依赖spring-cloud-starter-alicloud-oss(多个模块需要用,common的pom中配置即可) 2.配置文件中配置参数 access-key secret-key oss.endpoint 简单上传 直接注入OSSClient 不用自己根据参数去new了 直接使用注入的 来上传文件 16.实现获取签名,oss存储 将所有的第三方功能整合到新模块third-party中 比如我们的文件上传功能 spring-cloud-starter-alicloud-oss 这个依赖可以写在此模块中,然后此模块依赖common access的配置参数也写在这里,不用每个需要上传的模块都去配置了 服务端签名后直传 https://help.aliyun.com/document_detail/31926.html?spm=a2c4g.11186623.6.1567.221345dcuttInQ 1.编写获取policy的方法 返回一个map (json格式) 里面有需要的信息给浏览器 浏览器通过/oss/policy 访问此controller 2.后端返回的数据会给前端 前端带着这个policy去访问oss,需要设置oss的跨域访问 即第三步 修改CORS 17.JSR303校验 后端的校验功能 1.给需要校验的数据加上校验注解(@Email表示必须是一个邮箱,@NotNull,NotEmpty,NotBlank表示非空 等等) 例如 添加品牌信息的时候 名字非空(@NotBlank)等 注解都在javax.validation.constraints包下 注解都有message属性 可以自定义错误信息 不定义的时候使用默认信息 Pattern 自定义正则表达式校验 @Pattern(regexp = "/^[a-zA-Z]$/",message = "首字母必须是英文字母") 2.告知spring需要校验 添加信息也就是 save 保存的时候需要校验 对需要校验的数据加上@Valid 即:save(@Valid @RequestBody BrandEntity brand){} 3.紧跟校验数据后面,可以定义一个BindingResult 来获取校验的结果,可以根据结果的成功与否来做判断 18.统一异常处理 多个地方需要校验或者异常处理,可以统一进行异常处理,使用SpringMVC提供的@ControllerAdvice 1)、自定义一个异常类用来处理通用异常,加上@ControllerAdvice注解 通过属性basePackages 来指定哪个包下的controller 2)、类中定义方法处理相应的异常,通过@ExceptionHandler来表示处理哪些异常 通过属性value来制定异常 值是.class 3)、类上加@ResponseBody @Slf4j 项目全部统一返回json 所有方法都是返回json格式 还要打印错误日志 各个方法还是都返回我们定义的R(自定义返回值) 可以用 @RestControllerAdvice简化 = ControllerAdvice + ResponseBody 可以定义多个异常处理方法,优先匹配最精确的异常,最后才匹配最大的通用处理 19.枚举错误码及信息 有各种不同的错误码规范,不写死,定义在枚举里,多个模块都需要使用,所以写在common里 common里定义枚举 列举多种错误以及信息, 便于异常处理时使用 20.JSR303分组校验 各种情况下,校验的字段以及规则可能不同,需要分类 使用JSR303的分组校验功能 1)、指定groups 每个字段上的校验注解都有一个groups属性用来指定组别 groups的值是一个数组 必须是接口类型的class 多个模块都要校验,common中抽取一个包用来封装这些接口 接口空的就行 2)、@Validated 赋值组别 将controller参数前的@Validated修改成@Validated(有一个value属性可以指定一个或多个分组) *controller中参数指定了分组的情况 只有该分组下校验注解字段才会生效;没指定分组,只有没分组属性的生效 21.自定义校验功能 某些情况下,正则表达式方式也满足不了需求,想要自定义自己的校验注解 1)、编写自定义校验注解 2)、编写自定义校验器 ConstraintValidator 3)、关联自定义的校验器和校验注解 validatedBy 例如 显示属性(showStatus)只能是0,1 分别表示显示和不显示 希望自定义一个@ListValue(vals={0,1}) 只能是vals里面的值 common里面自定义ListValue注解 JSR303规定必须有message groups payload这三个基本属性 还必须要有一些源注解 @Documented @Constraint @Target @Retention @Constraint里面的validatedBy 用来指定一个或者多个校验器 自定义的注解校验器 需要实现ConstraintValidator 两个泛型分别是 注解,注解标注的数据类型 重写其中两个方法,一个初始化来获取注解定义的时数据,一个来获取用户的数据并判断 26.修复品牌管理功能的模糊查询功能 BrandController里面的/list 没有对key进行模糊查询匹配 27.关联分类功能 一个匹配会关联多个分类 小米关联手机,电器等 一个分类也关联多个品牌 是多对多关系 数据库中单独设置一张表 来配置关联关系 pms_category_brand_relation 根据 接口 获取品牌关联的分类,新增品牌与分类关联关系 完善代码 28.冗余数据的更新 当更新某些表数据时,例如修改品牌名,不仅要更新品牌表的name,还要将其他表使用了的冗余数据也更新 关系表中也有品牌name 品牌表做出修改必须也修改关系表中的品牌name 需要完善更新controller的代码 同理完善brand的update和category的update 电商项目的一些概念 SPU:Standard Product Unit 标准化产品单元 商品信息聚合的最小单位,是一组可复用,易检索的标准化信息的集合,描述了一个产品的特性 一个IPhoneX 就是一个SPU 聚合了所有特性(像素,分辨率等) SKU:Stock Keeping Unit 库存量单位 库存进出计量的基本单位,可以以件,盒等为单位。SKU是对于大型连锁超市DC(配送中心)物流管理的一个必要方法。 已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。 一个具体的IPhoneX 64G 黑色 就是一个SKU 相当于SPU类的具体对象 基本属性【规格参数】与销售属性 每个分类下的商品共享基本属性,每个不同的具体的SKU会改变的那些属性称之为销售属性 1.属性是以三级分类组织起来的(同一分类下基本属性K都一样 V不同,手机都有大小,芯片,处理器,品牌等) 2.规格参数中有些是可以提供检索的(例如直接检索5G,骁龙855的手机) 3.基本属性具有自己的分组(例如手机属性分为 主体,基本信息,主芯片,存储,摄像头等等) 4.属性的分组也是以三级分类组织起来的(手机的属性Key都一样,都分为上面那些) 5.属性名是确定的,具体值由不同的商品具体来决定 product产品服务模块的 属性分组API(pms_attr_group 属性分组表) 22.获取分类属性分组 根据不同的三级分类id,以及可能有的模糊检索条件key,返回不同的属性给前端 规定catelogId为0的时候 查询所有 这时也得判断key 模糊查询 23.修改children属性 前端新增分类属性的时候,希望三级分类id是以三级分类的形式展示出来的,点击即可选择 当分类是第三级分类的时候,children为空,希望不要展示这个属性了 避免空菜单栏 直接给children加一个注解@JsonInclude 设置属性JsonInclude.Include.NON_EMPTY即可 24.查询属性分组信息时回显catelogId的完整路径 修改属性分组的时候,catelogId 框内希望显示完整的三级分类路径 例如 手机/手机配件/手机壳 属性分组实体类中加入 完整路径属性 需要加@TableField 注解 exist = false 表示该字段在表中不存在 /info/{attrGroupId} 希望不仅返回属性分组信息,还返回自定义的完整路径 35.获取属性分组的关联的所有属性 /product/attrgroup/{attrgroupId}/attr/relation 例如从手机的基本信息分组中 关联拿到 长度,重量,机身材质工艺,机身厚度(mm)等attr信息 封装成数组返回给前端 36.删除属性与分组的关联关系 /product/attrgroup/attr/relation/delete 移除上面查询到的关联关系 参数是数组 多个关系需要移除 要进行mysql语句的拼接 37.获取属性分组没有关联的其他属性 /product/attrgroup/{attrgroupId}/noattr/relation 获取属性分组里面还没有关联的本分类里面的其他基本属性,方便添加新的关联 38.添加属性与分组关联关系 /product/attrgroup/attr/relation MyBatis 分页功能需要配置插件 25.自定义配置类来配置mybatis-plus的分页功能 https://mp.baomidou.com/guide/page.html 抽取VO对象 29.VO对象(Value Obj,View Obj) 通常用于业务层之间的数据传递,根据业务需要可以和表对应或者不对应 VO可以接受页面传递来的数据封装成对象,也可以将处理过后的数据封装成页面需要的对象 之前在表对应的实体类中添加非表字段(加@TableField)不规范 推荐使用自定义VO类来封装需要的字段 PO-持久对象 相当于与表对应的实体类 TO-数据传输对象 不同的应用程序之间传输的对象 例如各个微服务之间有调用关系,需要传输数据,这些需要发送出去的数据被封装成TO对象 DTO-数据传输对象 泛指用于展示层与服务层之间的数据传输对象 1、entity 里的每一个字段,与数据库相对应, 2、vo 里的每一个字段,是和你前台 html 页面相对应, 3、dto 这是用来转换从 entity 到 vo,或者从 vo 到 entity 的中间的东西 。(DTO中拥有的字段应该是entity中或者是vo中的一个子集) product产品服务模块的 规格参数(基本属性)API(pms_attr 属性表) 30.规格新增功能 规格新增功能,前端传回来数据会比数据库attr表多一个attrGroupId(因为新增规格属性有一个分组 例如新增的是基本信息里面的大小属性) 使用VO来作为接收以及返还对象 新建AttrVo 复制Attr实体类数据并加上attrGroupId 不仅要save attr表数据,还要save 关系表中的数据(attr和attr_group 的relation表) 修改controller里面的save部分业务代码 31.查询规格参数功能 带参,0代表查询全部,参数是指定的三级分类id,还要带上模糊查询功能 get请求 url /product/attr/base/list/{catelogId} 根据文档响应需要 "catelogName": "手机/数码/手机", //所属分类名字 "groupName": "主体", //所属分组名字 这两个属性attrVo中没有 新建一个专门处理响应的respVo 继承AttrVo 然后新加上这些多的属性即可 联表查询对数据压力大 尽量避免 所以先查询出attr的数据 再单独去其它表查询 封装需要的属性并返回 32.查询属性详情 /product/attr/info/{attrId} 使用respVo来接收数据返回 33.修改属性 /product/attr/update 修改关系表的时候 需要判断是否有数据 以此来判断新增或者更新 34.获取分类销售属性 /product/attr/sale/list/{catelogId} 销售属性和基本属性都在attr表中,通过字段attr_type来区分[0-销售属性,1-基本属性] 和之前的product/attr/base/list/{catelogId} 可以共用一个方法 动态的提供sale或者base字段 product/attr/{attrType}/list/{catelogId} service层方法中根据前端传回来的attrType来做判断 拼接查询条件即可 销售属性 没有分组(attr_group),所以存分组信息的时候需要判断 并且 关系表中保存或者更新数据的时候 也要判断 只有基本属性才存到关系表中 common中定义常量类,里面定义好attr相关的枚举 方便各个模块需要用的地方使用 以后业务修改 只需要改枚举值即可 避免业务代码做出大量修改 **发布商品步骤 录入基本信息,规格参数(基本属性),销售属性,SKU信息,保存完成 39.获取分类关联的品牌 /product/categorybrandrelation/brands/list 参数 catId 分类id 40.获取分类下所有分组&关联属性 /product/attrgroup/{catelogId}/withattr 根据三级分类id 查询出所有的分组(基本属性分组) 以及各个分组下的具体属性 例如查出手机分类下 的 主体(入网星号,上市年份)和基本信息(重量,长度,大小) 41.新增商品 /product/spuinfo/save 请求参数json格式很复杂 需要多个vo来联合封装请求参数 直接拷贝请求参数去网上转换json 转换成java类 拷贝到项目即可 商品的新增 设计到多张表联合保存 需要@Transactional spu保存 涉及到保存sku的优惠,满减信息以及保存spu的积分信息 这些是mall_sms数据库的 sms_spu_bounds\sms_sku_ladder\sms_sku_full_reduction\sms_member_price这些表 涉及到联调其他微服务模块 member模块 *想要远程调用别的服务 1)、引入open-feign 2)、编写一个接口,告诉SpringCloud这个接口需要调用远程服务 声明接口的每一个方法都是调用哪个远程服务的那个请求 3)、开启远程调用功能 单独建feign包 下面的接口都是远程调用的接口 接口上面@FeignClient("远程服务模块名字") Application主服务 需要开启远程调用功能 @EnableFeignClients(basePackages = "com.lemzdo.mail.product.feign") 接口的具体服务 需要指定远程模块的哪个请求(访问url PostMapping) 以及参数需要能转换为json(RequestBody)方法返回值也得一样 简而言之 直接复制远程方法过来 去掉方法体,修改方法名,修改参数对象 即可 A B服务 不需要用同一个对象来接收数据 json可以随意转化 只要字段名相同可以一一映射即可 A B 两个服务之间需要进行数据传输 都封装成对象 然后以JSON格式传输 称之为TO对象 因为两个服务公用 所以在common中新建这个TO对象用来传输需要的数据