[Spring 框架学习之路篇] kkb内推管理平台项目总结

该项目主要完成了平台的后端工程微服务的搭建和后端接口的实现,以及后期项目调优等工作。

一、微服务部署

微服务模块如下:
项目微服务模块
其中,需要与前台对接的微服务的主要是 kkb-admin 和 kkb-portal 两个,也是我们需要实现业务的两个模块,很多代码都调用到了 kkb-common 中的工具,如 redis 存取工具等。
kkb-mbg-plus代码生成工具也是神器,自动生成 pojo、dao、service 层内的通用代码。
kkb-gateway 主要是我部署的,内部设置了很多转发、过滤的规则。
这些模块都是我主要负责的模块。

二、数据库设计

数据库设计是根据项目需求设计的,需要用到 PRD(Project Requirement Document,项目需求文档)。其中需要注意的一些点:

  • 删除采用逻辑删除,逻辑删除字段类型采用与主键类型一致(自增主键),0表示未被删除,其它表示已被删除。此外,由于逻辑删除的存在,导致主键索引无法过滤掉已被删除的记录,这里可以建立联合索引方便查找“活着”的用户。
  • 描述用户种类可以另起一张表,方便日后扩展用户类型;还要另起一张用户与用户种类对应的表,方便查询。

二、后端接口

我主要负责后台名人堂显示用户列表的接口,由于pojo(dto、vo是自己编写的)、dao、service(部分功能也要自己编写)层都是用mbg-plus生成好的,我们主要负责Controller业务逻辑的编写。

  1. 调用 mbg-plus 服务生成 pojo、dao、service
    这里主要是用到了第三方编写的 mybatis plus 生成器来生成相应的实体类、数据库接口类、服务类,源码如下:

    /**
     * @Description mybatisPlus配置(生成器)
     * @Author qtds
     * @Date 2021/8/11 , 15:31
     */
    public class KkbMbgPlusGenerator {
    
        public static void main(String[] args) {
            // 代码生成器
            AutoGenerator mpg = new AutoGenerator();
            Properties properties = new Properties();
            try {
               properties.load(KkbMbgPlusGenerator.class.getResourceAsStream("/mybatis-plus.properties")); // 配置文件配置即可
            } catch (IOException e) {
                e.printStackTrace();
            }
            MybatisPlusConfig mybatisPlusConfig = new MybatisPlusConfig(properties);
            mpg.setDataSource(mybatisPlusConfig.dataSourceConfig());
            mpg.setGlobalConfig(mybatisPlusConfig.globalConfig());
            mpg.setPackageInfo(mybatisPlusConfig.packageConfig());
            mpg.setStrategy(mybatisPlusConfig.strategyConfig());
            mpg.execute();
        }
    }
    

    只需要配置 mybatis-plus.properties 文件后运行启动类即可快速配置所需类。
    注意:运行前,需要在数据库中对应位置创建数据库和数据表
    mybatis-plus.properties 配置文件如下:

    #生成文件的输出目录
    globalConfig.outputDir=/kkb-admin/src/main/java
    #数据源配置
    url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    driverClassName=com.mysql.cj.jdbc.Driver
    username=root
    password=
    #父包名
    package.parent=com.kkb.kkbadmin
    #父包模块名
    package.moduleName=
    #Entity包名
    package.entity=domain
    #Mapper包名
    package.mapper=dao
    #Controller包名
    package.controller=controller
    package.xml=dao.xml
    #需要包含的表名,多个表使用逗号隔开,不写代表生成所有表
    strategy.include=carousel
    
  2. 编写适配业务的 service
    业务五花八门,需要的业务往往没有对应的 service 接口能直接用上。
    重新设计接口只需要在 service 包中找到对应 interface 接口,在其中添加相应的接口函数,然后在 impl 包找到对应的实现类实现相应方法,就可以供 controller 层调用了。
    举例 User 服务层接口 UserService:

    /**
     * 用户表 服务类
     *
     * @author 赵裕源
     * @since 2021-08-15
     */
    public interface UserService extends IService<User> {
    
        /**
         * 带条件的分页查询
         *
         * @param condition 条件
         * @param pageNum   页码
         * @param pageSize  每页显示行数
         * @return pageInfo, PageHelper的分页类
         */
        PageInfo<FameBGListVO> findPage(Long roleId, UserDto condition, Integer pageNum, Integer pageSize);
    
        /**
         * 根据用户Id列表List的条件返回用户列表List
         *
         * @param condition 条件
         * @param userIds   用户Id列表
         * @return 用户列表List
         */
        List<FameBGListVO> selectByConditionAndUserIds(UserDto condition, List<Long> userIds);
    }
    

    该接口的实现类 UserServiceImpl:

    /**
     * 用户表 服务实现类
     *
     * @author 赵裕源
     * @since 2021-08-15
     */
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
        @Autowired
        private UserRoleService userRoleService;
    
        @Autowired
        private ProjectRegisterService projectRegisterService;
    
        /**
         * 带条件的分页查询
         *
         * @param condition 条件
         * @param pageNum   页码
         * @param pageSize  每页显示行数
         * @return pageInfo, PageHelper的分页类
         */
        @Override
        public PageInfo<FameBGListVO> findPage(Long roleId, UserDto condition, Integer pageNum, Integer pageSize) {
            PageHelper.startPage(pageNum, pageSize);
            List<Long> userIds = userRoleService.findUserIdListByRoleId(roleId);
            List<FameBGListVO> fameBGListVOS = this.selectByConditionAndUserIds(condition, userIds);
            return new PageInfo<>(fameBGListVOS);
        }
    
        /**
         * 根据用户Id列表List的条件返回用户列表List
         *
         * @param condition 包含用户名的搜索条件
         * @param userIds   用户Id列表
         * @return 用户列表List
         */
        @Override
        public List<FameBGListVO> selectByConditionAndUserIds(UserDto condition, List<Long> userIds) {
    
            if (CollUtil.isEmpty(userIds)) Asserts.fail("没有查询到相关用户");
    
            String name = condition.getName();
            List<User> users = this.lambdaQuery()
                    .like(ObjectUtil.isNotNull(name), User::getName, name)
                    .in(User::getId, userIds)
                    .list();
    
    
            // 查找需要返回的用户信息放在vo中返回vo列表
            return users.stream().map(user -> {
                FameBGListVO vo = new FameBGListVO();
                Long id = user.getId();
                vo.setId(id);
                vo.setUrl(user.getHeadUrl());
                vo.setName(user.getName());
                vo.setJob(user.getJob());
                // 根据用户id查找已完成的项目数
                vo.setProjectNum(projectRegisterService.completedProjects(id));
                return vo;
            }).sorted((vo1, vo2) -> vo2.getProjectNum() - vo1.getProjectNum()).collect(Collectors.toList());
        }
    }
    

    注意的点:

    1. 这里用到的 vo 类是根据接口需要而设计的用于响应前端请求的实体类。
    2. 为了避免使用大量的 for 循环来填充 vo 对象,巧用 java 提供的 Stream 流对象来完成填充工作,非常方便。
      参考:Stream流操作参考
    3. 善用 Common 工程中的方法,增加程序的可读性。
      如:CollUtil.isEmpty(object) 判断对象是否为空等。
    4. 这里用到了分页显示的工具 PageHelper,只需要在用到的 Service 接口函数添加分页函数 PageHelper.startPage(pageNum, pageSize); 即可返回 PageInfo 分页对象,也很方便。当然也有其他分页方法可以使用。
  3. controller层业务接口编写
    这里由于需要生成 Swagger 接口文档,除了需要使用通常 Controller 层需要的注解如 @RestController@RequestMapping("总映射url"),还需要使用各种带有 api 字样的注解生成接口文档。另外,还有一些校验注解 @Validated 供校验参数使用。具体详见代码:

    /**
     * 用户表 前端控制器
     *
     * @author 赵裕源
     * @since 2021-08-15
     */
    @RestController
    @Api(tags = "用户管理", value = "名人堂后台管理")
    @RequestMapping("/user")
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        /**
         * 根据roleId角色编号返回用户列表List
         *
         * @param roleId    角色编号 1: 学员  2: 导师
         * @param condition 包含用户名的搜索条件
         * @param pageNum   页码
         * @param pageSize  每页显示数量
         * @return 分页结果
         */
        @ApiOperation("分页查询")
        @ApiImplicitParams({
                @ApiImplicitParam(name = "roleId", value = "用户角色id", dataType = "long"),
                @ApiImplicitParam(name = "condition", value = "用户筛选条件", dataType = "User"),
                @ApiImplicitParam(name = "pageNum", value = "当前页数", defaultValue = "1", dataType = "int"),
                @ApiImplicitParam(name = "pageSize", value = "每页显示数量", defaultValue = "5", dataType = "int")
        })
        @PostMapping("/page/{roleId}")
        public CommonResult<CommonPage<FameBGListVO>> listPage(@PathVariable Long roleId,
                                                               @Validated @RequestBody UserDto condition,
                                                               @RequestParam(defaultValue = "1") Integer pageNum,
                                                               @RequestParam(defaultValue = "5") Integer pageSize) {
            PageInfo<FameBGListVO> page = userService.findPage(roleId, condition, pageNum, pageSize);
            CommonPage<FameBGListVO> commonPage = CommonPage.restPage(page.getList());
            return CommonResult.success(commonPage);
        }
    }
    
  4. 接口调试
    编写完 Controller 层业务逻辑代码后,就需要启动 Application 应用程序类来调试,检验接口运行结果。
    需要注意的一些点:

    1. 该项目需要开启 nacos 注册中心服务才可正常调试代码,因为配置文件等需要从远程仓库调取,其中都需要注册 nacos 服务。
    2. 涉及 redis 接口调用时就需要开启 redis 服务,并配置好相应 application.yml 配置文件。
      redis 接口函数可以在 common 工程中获取。
    3. 记得创建好 MySQL 数据库和使用到的数据表,并配置好相应 application.yml 配置文件,才可以调试成功。

三、项目调优

项目调优有三个方向:

  1. 主从复制+多数据源+Redis 缓存
    主从复制操作参考:MySql主从复制,从原理到实践!
    多数据源配置则只需要在相应 application.yml 配置文件中配置即可,如下:

    spring:
    	application:
    		name: kkb-portal
    	autoconfigure:
    		exclude:
    			com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
    	datasource:
    	dynamic:
    		primary: master #设置默认的数据源或者数据源组,默认值即为master
    		strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
    		########################
    		#    多数据源关键配置    #
    		########################
    		datasource:
    			master:
    				url: jdbc:mysql://192.168.200.128:3307/db2?
    				useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    				username: root
    				password: root
    				driver-class-name: com.mysql.jdbc.Driver
    			slave:
    				url: jdbc:mysql://192.168.200.128:3308/db2?
    				useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    				username: root
    				password: root
    				driver-class-name: com.mysql.jdbc.Driver
    	druid:
    		initial-size: 5 #连接池初始化大小
    		min-idle: 10 #最小空闲连接数
    		max-active: 20 #最大连接数
    

    Redis 缓存配置参考:MySql主从复制,从原理到实践!

  2. ELK:Elasticsearch + Logstash + Kibana 日志系统
    这组合是现在大型项目常用的一个日志收集并显示的系统。
    Logstash 负责收集日志;
    Elasticsearch 负责提供大文件关键字搜索服务,可以定位日志的出处等,方便排查bug;
    Kibana 负责显示查找结果,属于前端服务。
    ELK工作流程
    部署参考:SpringBoot应用整合ELK实现日志收集

  3. Elasticsearch 搜索项目
    ElasticSearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful web 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。
    部署参考:springboot+elasticsearch实现一个搜索引擎的功能

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值