前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >趁热打铁,整一个新功能出来

趁热打铁,整一个新功能出来

作者头像
江南一点雨
发布2022-04-26 14:10:18
4600
发布2022-04-26 14:10:18
举报
文章被收录于专栏:玩转JavaEE

上篇文章中我们已经实现了自定义菜单了,我们可以根据自己的实际需求去定制自己需要的菜单,做好了这一步,接下来我们就可以开发新功能了。

我们就先从最简单的渠道管理开始。

❝还是老规矩,一个特别基础的细节我就不啰嗦了,如果大家阅读吃力,也可以先看看 vhr(https://github.com/lenve/vhr) 再看这个就容易多了。

1. 分配权限

我们依葫芦画瓢,首先在 sys_menu 中为渠道相关的操作添加权限,新增如下两条记录:

2008 就是渠道管理菜单项的 id。渠道管理将来就对应了这四个操作。

2. 渠道管理表

渠道管理比较简单,一张表,也不需要引用其他表,如下:

这个表很简单,没啥好说的。

3. 服务端接口开发

3.1 现有功能分析

用了这个脚手架,我也就懒得另起炉灶了,我们现在要写接口,接口该怎么写?我们可以参考一个他自己写好的,例如用户管理接口。

用户管理接口位置在 org.javaboy.web.controller.system.SysUserController#list 方法中:

代码语言:javascript
复制
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
    startPage();
    List<SysUser> list = userService.selectUserList(user);
    return getDataTable(list);
}

大家看,首先通过权限注解确保用户具备相应的权限。这个权限注解对应的方法是 org.javaboy.framework.web.service.PermissionService#hasPermi 方法,具体的逻辑也并不难,当用户登录成功后,会查询出来当前用户的所有权限,并放到 LoginUser 对象中(这个在本系列的第一篇文章中已经讲过了),然后将之存入到 Redis 中,现在这里就是从 Redis 中取回 LoginUser 对象,然后拿出来用户的权限字符串,跟这里需要的权限字符串做比对。

由于这个脚手架自定义了一个 BaseController,里边封装了很多常用的操作,所有的业务 Controller 都是继承自这个 BaseController,所以这里的 startPage 方法其实就是 BaseController 中的方法,这个方法会自动开启分页功能,会从当前请求中提取出分页参数,然后进行查询。如果前端没有传递分页参数,那么默认查询第一页,查询 10 条数据。

接下来就是一个常规的查询操作,没啥好说的。

最后的 getDataTable 方法则是将数据包装成一个分页的 JSON 对象。

还有一点要捋清楚,就是这个脚手架是一个多模块项目,所有的借口定义统一在 admin 中,不同的功能对应不同的模块,例如用户管理相关的功能都在 system 这个模块中。

好了,看懂这个,我们就照猫画虎。

3.2 创建工程

首先,我们新建一个自己的功能模块,这是一个 maven 项目,叫做 tienchin-channel。

这里我想用 MyBatis-Plus 来做,因此我先修改父工程的 dependencyManagement,将 mp 的版本号统一管理起来,同时也将新建模块加进去,方便后期引用的时候进行版本号统一管理:

代码语言:javascript
复制
<properties>
    <mybatis-plus-boot-starter.version>3.5.1</mybatis-plus-boot-starter.version>
    <mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus-generator.version}</version>
        </dependency>
        <dependency>
            <groupId>org.javaboy</groupId>
            <artifactId>tienchin-channel</artifactId>
            <version>${tienchin.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

创建完成后,我们手动修改一下 tienchin-channel 的 pom.xml 文件,照着脚手架 system 模块的改即可:

代码语言:javascript
复制
<description>
    渠道管理模块
</description>
<dependencies>
    <!-- 通用工具-->
    <dependency>
        <groupId>org.javaboy</groupId>
        <artifactId>tienchin-common</artifactId>
    </dependency>
</dependencies>

接下来,在 admin 模块中,依赖当前新建的 tienchin-channel 模块和 mp 的代码自动生成依赖,如下:

代码语言:javascript
复制
<dependency>
    <groupId>org.javaboy</groupId>
    <artifactId>tienchin-channel</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <scope>test</scope>
</dependency>

另外依赖我还有一些细微的调整,例如为父模块添加了 Spring Boot 作为其 parent 等,这些我就不逐一说明了,大家可以在文末下载源码查看。

3.3 配置 MP

这个脚手架中虽然用了 MyBatis 的 starter,但是实际上还是自己手动配置的 MyBatis,所以当我们使用 MP 的时候,并不能像在 Spring Boot 中使用 MP 那样,加个依赖就行了,我们还需要手动改一下配置。

首先我们将 mp 的依赖放到 common 模块中,毕竟将来无论是 framework 还是我们新建的 tienchin-channel 都依赖 common 模块,如下:

tienchin-common/pom.xml:

代码语言:javascript
复制
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>

然后,MyBatis 的配置是在 framework 模块中,具体代码在 tienchin-framework/src/main/java/org/javaboy/framework/config/MyBatisConfig.java 位置,我们直接在此进行修改即可。

代码语言:javascript
复制
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
    String mapperLocations = env.getProperty("mybatis.mapperLocations");
    String configLocation = env.getProperty("mybatis.configLocation");
    typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
    VFS.addImplClass(SpringBootVFS.class);
    final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
    sessionFactory.setDataSource(dataSource);
    sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
    sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
    sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
    return sessionFactory.getObject();
}

小伙伴们看一下,在配置的过程中,将原本的 SqlSessionFactoryBean 改为 MybatisSqlSessionFactoryBean,其他都不变即可。

如此,我们的 MP 就配置好了。

3.4 生成代码

接下来,我们在 admin 模块的单元测试中,通过如下代码来生成一下 channel 对应的实体类啥的,如果大家对这个自动生成代码的不熟悉的话,可以看看这篇文章:自动生成实体类,哪个最佳?

代码语言:javascript
复制
public class Generator {
    @Test
    public void channelGenerator() {
        FastAutoGenerator.create("jdbc:mysql:///tienchin?serverTimezone=Asia/Shanghai&useSSL=false", "root", "123")
                .globalConfig(builder -> {
                    builder.author("javaboy") // 设置作者
                            .disableOpenDir()
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("/Users/sang/workspace/workspace02/tienchin/tienchin-channel/src/main/java"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("org.javaboy") // 设置父包名
                            .moduleName("channel") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, "/Users/sang/workspace/workspace02/tienchin/tienchin-channel/src/main/resources/mapper/channel")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("tienchin_channel") // 设置需要生成的表名
                            .addTablePrefix("tienchin_");
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
}

自动生成的源码自带 Controller,我们将其删除,重新在 admin 模块中创建对应的 ChannelController 即可。

对照现有的任意一个 Controller,我们写出来自己的 Controller,如下:

代码语言:javascript
复制
@RestController
@RequestMapping("/tienchin/channel")
public class ChannelController extends BaseController {

    @Autowired
    IChannelService channelService;

    @PreAuthorize("@ss.hasPermi('tienchin:channel:query')")
    @GetMapping("/list")
    public TableDataInfo getChannelList() {
        startPage();
        List<Channel> list = channelService.list();
        return getDataTable(list);
    }
    @PreAuthorize("@ss.hasPermi('tienchin:channel:add')")
    @Log(title = "渠道管理" , businessType = BusinessType.INSERT)
    @PostMapping("/")
    public AjaxResult add(@Validated @RequestBody Channel channel) {
        channel.setCreateBy(getUsername());
        return toAjax(channelService.saveChannel(channel));
    }

    @PreAuthorize("@ss.hasPermi('tienchin:channel:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable Long id) {
        return AjaxResult.success(channelService.getById(id));
    }

    @PreAuthorize("@ss.hasPermi('tienchin:channel:edit')")
    @Log(title = "渠道管理" , businessType = BusinessType.UPDATE)
    @PutMapping("/")
    public AjaxResult edit(@Validated @RequestBody Channel channel) {
        channel.setUpdateBy(getUsername());
        channel.setUpdateTime(LocalDateTime.now());
        return toAjax(channelService.saveOrUpdate(channel));
    }

    @PreAuthorize("@ss.hasPermi('tienchin:channel:remove')")
    @Log(title = "渠道管理" , businessType = BusinessType.DELETE)
    @DeleteMapping("/{channelIds}")
    public AjaxResult remove(@PathVariable Long[] channelIds) {
        return toAjax(channelService.removeBatchByIds(Arrays.asList(channelIds)));
    }

}

都是常规操作,没啥特别值得说的地方。

@Log 是脚手架中定义的日志记录注解,加一个这个注解,会自动将当前的操作记录到 sys_oper_log 表中,像下面这样:

@PreAuthorize 操作权限就按一开始在数据库中配置的内容即可。

照猫画虎,很快就写出来这样一个接口。

4. 开发前端页面

接下来我们来整前端页面,前端页面我们在第二篇文章中提到过,该功能对应的页面是 src/views/tienchin/channel/index.vue,所以我们只需要修改该页面即可,这个修改,我们也找一个参照物,找一个也是表格的页面改一下就行了,例如 src/views/system/dict/index.vue,这是字典管理的页面,我们就照着这个来改就行了,前端的代码量太大了,我就不全部贴出来了,我挑几个关键的地方来说一下。

4.1 网络请求

前端是每一个 .vue 文件都将自己所需的网络请求封装在一个 js 文件中,然后将来在 .vue 文件中直接引用。

例如关于数据字典的所有请求封装在 src/api/system/dict/type.js 文件中,我照猫画虎写了关于 channel 的所有网络请求:

src/api/channel/index.js

代码语言:javascript
复制
import request from '@/utils/request'

// 查询所有的渠道信息
export function listChannel(query) {
  return request({
    url: '/tienchin/channel/list',
    method: 'get',
    params: query
  })
}

// 根据 id 查询某一个渠道的信息
export function getChannel(channelId) {
  return request({
    url: '/tienchin/channel/' + channelId,
    method: 'get'
  })
}

// 添加渠道
export function addChannel(data) {
  return request({
    url: '/tienchin/channel/',
    method: 'post',
    data: data
  })
}

// 更新渠道信息
export function updateChannel(data) {
  return request({
    url: '/tienchin/channel/',
    method: 'put',
    data: data
  })
}

// 根据 id 删除渠道
export function delChannel(channelIds) {
  return request({
    url: '/tienchin/channel/' + channelIds,
    method: 'delete'
  })
}

4.2 页面展示

页面展示有一个地方需要和大家聊一聊。

就是当用户登录成功之后,前端会调用服务端的接口查看当前用户信息,包括用户的权限信息,而且前端还封装了一个空闲显示或者隐藏的工具,位置在 src/directive/permission/hasPermi.js,这个工具最终被做成了一个自定义指令,这样,我们在展示每一个按钮的时候,可以加上这个指令,将来就会自动根据用户是否具备相应的权限来展示相应的按钮,例如下面这几个按钮:

代码语言:javascript
复制
<el-col :span="1.5">
  <el-button
    type="primary"
    plain
    icon="el-icon-plus"
    size="mini"
    @click="handleAdd"
    v-hasPermi="['tienchin:channel:add']"
  >新增
  </el-button>
</el-col>
<el-col :span="1.5">
  <el-button
    type="success"
    plain
    icon="el-icon-edit"
    size="mini"
    :disabled="single"
    @click="handleUpdate"
    v-hasPermi="['tienchin:channel:edit']"
  >修改
  </el-button>
</el-col>
<el-col :span="1.5">
  <el-button
    type="danger"
    plain
    icon="el-icon-delete"
    size="mini"
    :disabled="multiple"
    @click="handleDelete"
    v-hasPermi="['tienchin:channel:remove']"
  >删除
  </el-button>
</el-col>

每个按钮上都有一个 v-hasPermi 标签来表述这个按钮将来显示的条件。

另外,前端也使用到了数据字典,也就是一些常见的字段取值我们将之固定下来了,在前端直接引用即可。数据字典本身对应的表是 sys_dict_datasys_dict_type,像下面这样(下图为 sys_dict_data  表,关于他这个里边的数据字典,后面有空了松哥可以再整一篇文章和大家分析具体用法):

需要用到哪条记录,就在 vue 文件定义的时候声明就行了,像下面这样:

这样,后期就可以直接引用这个变量了,如下:

代码语言:javascript
复制
<el-table-column label="渠道状态" align="center" prop="status">
  <template slot-scope="scope">
    <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
  </template>
</el-table-column>

options 其实就引用了数据字典中的值。

关于这个页面其他的内容就都是常规操作了,会 vhr 基本上都能看懂,我也就不啰嗦了。

最终弄出来的页面如下:

5. 小结

好啦,今天就先聊这么多,源码地址如下:

  • https://github.com/lenve/tienchin
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江南一点雨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 分配权限
  • 2. 渠道管理表
  • 3. 服务端接口开发
    • 3.1 现有功能分析
      • 3.2 创建工程
        • 3.3 配置 MP
          • 3.4 生成代码
          • 4. 开发前端页面
            • 4.1 网络请求
              • 4.2 页面展示
              • 5. 小结
              相关产品与服务
              云数据库 Redis
              腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档