您现在的位置是:亿华云 > 数据库

趁热打铁,整一个新功能出来,你学会了吗?

亿华云2025-10-03 07:03:45【数据库】8人已围观

简介在​​上篇文章​​中我们已经实现了自定义菜单了,我们可以根据自己的实际需求去定制自己需要的菜单,做好了这一步,接下来我们就可以开发新功能了。我们就先从最简单的渠道管理开始。还是老规矩,一个特别基础的细

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

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

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

1. 分配权限

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

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

2. 渠道管理表

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

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

3. 服务端接口开发

3.1 现有功能分析

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

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

@PreAuthorize("@ss.hasPermi(system:user:list)")

@GetMapping("/list")

public TableDataInfo list(SysUser user) {

startPage();

Listlist = 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 的版本号统一管理起来,同时也将新建模块加进去,方便后期引用的时候进行版本号统一管理:

3.5.1

3.5.2

com.baomidou

mybatis-plus-boot-starter

${ mybatis-plus-boot-starter.version}

com.baomidou

mybatis-plus-generator

${ mybatis-plus-generator.version}

org.javaboy

tienchin-channel

${ tienchin.version}

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

渠道管理模块

org.javaboy

tienchin-common

渠道管理模块 org.javaboy tienchin-common

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

org.javaboy

tienchin-channel

com.baomidou

mybatis-plus-generator

test

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

3.3 配置 MP

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

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

tienchin-common/pom.xml:

com.baomidou

mybatis-plus-boot-starter

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

@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 对应的实体类啥的,如果大家对这个自动生成代码的不熟悉的话,可以看看这篇文章:自动生成实体类,哪个最佳?:

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,如下:

@RestController

@RequestMapping("/tienchin/channel")

public class ChannelController extends BaseController {

@Autowired

IChannelService channelService;

@PreAuthorize("@ss.hasPermi(tienchin:channel:query)")

@GetMapping("/list")

public TableDataInfo getChannelList() {

startPage();

Listlist = 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

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,这个工具最终被做成了一个自定义指令,这样,我们在展示每一个按钮的时候,可以加上这个指令,将来就会自动根据用户是否具备相应的权限来展示相应的按钮,例如下面这几个按钮:

type="primary"

plain

icon="el-icon-plus"

size="mini"

@click="handleAdd"

v-hasPermi="[tienchin:channel:add]"

>新增

type="success"

plain

icon="el-icon-edit"

size="mini"

:disabled="single"

@click="handleUpdate"

v-hasPermi="[tienchin:channel:edit]"

>修改

type="danger"

plain

icon="el-icon-delete"

size="mini"

:disabled="multiple"

@click="handleDelete"

v-hasPermi="[tienchin:channel:remove]"

>删除

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

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

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

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

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

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

最终弄出来的页面如下:

5. 小结

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

​​https://github.com/lenve/tienchin​​

很赞哦!(9)