虽然说是从0到1,但是程序员都知道,不可能重复造轮子。还是从ruoyi脚手架项目开始
一、ruoyi脚手架整体结构
1.1、原脚手架结构
- ruoyi-admin 所有controller有关的接口全放在这里,也叫Web模块,特有逻辑1,必需的
- ruoyi-system 除此之外的domain、mapper、service等CRUD相关逻辑,特有逻辑2
- ruoyi-quartz 定时器,特有逻辑3
- ruoyi-generator 代码生成器,特有逻辑4
- ruoyi-common 公共组件,如工具类、公共用到的,公共逻辑1,必需的
- ruoyi-framework 框架运行所需要的一些类,公共逻辑2,必需的
上述加黑字体的模块是主要模块,项目最终都会整合到 ruoyi-admin模块中,而ruoyi-admin是通过pom.xml把上述模块加入依赖进来的,各模块之间的依赖关系如下简图所示:
1.2、SQL表结构
重点的表主要是sys_ 开头的表:
- sys_config :对应的系统中“系统管理/参数设置”
- sys_dept:对应系统中“系统管理/部门管理”
- sys_dict_data:字典明细表,对应系统中“系统管理/字典管理”
- sys_dict_type:字典类型表,对应系统中“系统管理/字典管理/字典类型”
- sys_job / sys_job_log:定时任务,对应系统中“系统监控/定时任务”
- sys_logininfor:登录信息,对应于系统的“系统监控/在线用户”和“日志管理/登录日志”
- sys_menu:菜单管理表,对应于系统的所有菜单值,还对应于“系统管理/菜单管理”
- sys_notice:通知信息,对应于“系统管理/通知公告”
- sys_oper_log:操作日志,对应于“日志管理/操作日志”
- sys_post:岗位管理表,对应“系统管理/岗位管理”
- sys_role:角色管理表,对应“系统管理/角色管理”
- sys_user:用户表,对应“系统管理/用户管理”
- sys_role_dept / sys_role_menu /sys_user_role : 角色与部门、菜单、用户的对应表
- sys_user_post:用户与岗位的对应表
其中,用户相关的表的关系如下所示:
1.3 接口返回值
前后端分离的ruoyi框架共三个类型的返回值,分别是:
- void:主要是导出或下载等不需要返回值的接口
- TableDataInfo:所返回的数据结构如下,主要是页面的分页列表数据
- AjaxResult:所返回的数据结构如下,主要是基本添加、编辑、单个查询或删除等操作的返回值,一般是json格式数据
二、改造“通知公告”
目的:通过改造“通知公告”能够对系统有个基本了解。
目标:前端将“通知公告”拆分为两个页面:
- A页面主要是显示所有用户发布的所有公告,并可查看详情,查看完毕后标识已读状态
- B页面主要是用户个人自己的公告编辑,发起流程并发布的编辑页面
2.1 调整字典数据
通过前端页面调整字典数据,增加记录是否已读的sys_notice_isread数据字典类型,同时修改通知的类型sys_notice_status为流程类型、修改sys_notice_type的通知类型:
- sys_notice_type:
- sys_notice_status:
- sys_notice_isread(新增):
2.2 增加用户与通知的关联SQL表
针对每个用户的已读信息,在SQL中予以存储之用,表名命名为sys_user_notice:
2.3 生成新增SQL的后台操作代码
使用代码生成器生成新增的SQL后台操作代码,如下图,代码生成器生成的代码包括前端VUE文件和后端main文件以及针对前端菜单显示的SQL三部分。其中因前端是改造原通知公告页面,故不使用vue和SQL文件,仅导入后端操作的main文件,如图标识所示部分:
2.4 改造“全员信息”页面
复制备份前端的views/system/notice下的index.vue,命名为create.vue作为编辑公共页面备用。
首先改造index如下:
2.4.1 去掉编辑功能
注释掉本页面中所有的“新增、修改、删除”的前端代码
2.4.2 增加序号和公告编号
<el-table-column label="序号" align="center" prop="noId" width="100" />
<el-table-column label="公告编号" align="center" prop="noticeId" :show-overflow-tooltip="true"/>
2.4.3 操作中添加“查看”
<el-button size="mini" type="text" icon="el-icon-view" @click="openDetailDialog(scope.row.noticeId)">查看</el-button>
同时添加点击“查看”后的详情页面:
<!--通知公告详情 -->
<el-dialog :title="form.noticeTitle" :visible.sync="openDetail" width="800px" append-to-body @close="closeDetail(form.noticeId)">
<div style="margin-top:-20px;margin-bottom:10px;">
<el-tag size="mini" effect="dark"
v-for="mytype in dict.type.sys_notice_type" :key="dict.value"
:type="mytype.raw.listClass" v-if="form.noticeType==mytype.value">{{ mytype.label}}</el-tag>
<span style="margin-left:20px;">{{form.createTime}}</span>
</div>
<div v-loading="loadingDetail" class="content">
<div v-html="form.noticeContent" style="margin-left:0px;margin-right:76px" class="ql-editor"></div>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="closeDetail(form.noticeId)"> 关 闭 </el-button>
</div>
</el-dialog>
2.4.4 api和数据改造
api增加listPublishList路径,用以获取后端的所有发布的信息,index中改造如下:
import { listPublishNotice, getNotice,getNoticeAndRecord, delNotice, addNotice, updateNotice } from "@/api/system/notice";
api中改造如下:
// 查询所有发布的公告列表
export function listPublishNotice(query) {
return request({
url: '/system/notice/publishlist',
method: 'get',
params: query
})
}
index.vue中发生变动的数据如下:
dicts: ['sys_notice_status','sys_notice_isread', 'sys_notice_type'],
queryParams: {
pageNum: 1,
pageSize: 10,
noticeTitle: undefined,
createBy: undefined,
status: undefined,
orderByColumn:"create_time",
isAsc:"desc"
},
获取数据的方法变动如下:
/** 查询公告列表 */
getList() {
this.loading = true;
listPublishNotice(this.queryParams).then(response => {
this.noticeList = response.rows;
this.total = response.total;
this.loading = false;
});
},
增加打开详情信息的处理方法如下:
// 打开信息详情
openDetailDialog(id) {
this.openDetail = true;
this.loadingDetail = true;
getNoticeAndRecord(id).then(response => {
this.form = response.data;
this.openDetail = true;
this.loadingDetail = false;
});
},
// 取消按钮
closeDetail(id) {
this.titleDetail = "详情";
this.openDetail = false;
this.reset();
this.noticeList.forEach(element => {
if(element.noticeId == id) {
element.isRead = 1;
}
});
},
2.4.5 后端处理修改
SysNoticeCotroller中增加路由:
/**
* 获取发布类型通知公告列表
*/
@SaCheckPermission("system:notice:list")
@GetMapping("/publishlist")
public TableDataInfo<SysNotice> publishlist(SysNotice notice, PageQuery pageQuery) {
return noticeService.selectPageNoticePublishList(notice, pageQuery, getUserId());
}
在SysNoticeServerImpl中增加相关处理方法并添加接口:
/**
* 查询所有发布状态的公告
* @param notice
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<SysNotice> selectPageNoticePublishList(SysNotice notice, PageQuery pageQuery, Long currentUserId) {
LambdaQueryWrapper<SysNotice> lqw = new LambdaQueryWrapper<SysNotice>()
.like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())
.eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())
.like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy())
.eq(SysNotice::getStatus, dictDataService.selectDictValue("sys_notice_status", "发布"));
Page<SysNotice> page = baseMapper.selectPage(pageQuery.build(), lqw);
int i = PageQuery.DEFAULT_PAGE_NUM;
for( SysNotice one : page.getRecords()) {
one.setCreateBy(String.valueOf(sysUserService.selectUserByUserName(one.getCreateBy()).getNickName()));
SysUserNoticeVo isReadResult = sysUserNoticeService.queryByIdAndNoticeId(currentUserId, one.getNoticeId());
if(isReadResult != null){
one.setIsRead(Long.valueOf(dictDataService.selectDictValue("sys_notice_isread", "已读")));
} else {
one.setIsRead(Long.valueOf(dictDataService.selectDictValue("sys_notice_isread", "未读")));
}
one.setNoId(Long.valueOf((pageQuery.getPageNum()-1)*pageQuery.getPageSize()+i));
i++;
}
return TableDataInfo.build(page);
}
所有修改完毕后效果如下
2.5 改造“编辑公告”页面
针对上述2.4步复制出来的create.vue页面改造如下:
2.5.1 添加序号和流程发起
<el-table-column label="序号" align="center" prop="noId" width="100" />
<el-table-column label="公告编号" align="center" prop="noticeId" :show-overflow-tooltip="true"/>
<el-button type="text" size="mini" icon="el-icon-video-play" @click="handleStart(scope.row)" v-hasPermi="['workflow:process:start']">发起</el-button>
2.5.2 修改数据和方法
如2.4.4中改造“全员公告”的方式类似,分别修改api及本create.vue页面如下
// 查询当前用户的所有公告列表
export function listCurrUserNotice(query) {
return request({
url: '/system/notice/curruserlist',
method: 'get',
params: query
})
}
import { listCurrUserNotice,getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
noticeTitle: undefined,
createBy: undefined,
status: undefined,
orderByColumn:"create_time",
isAsc:"desc"
},
/** 查询公告列表 */
getList() {
this.loading = true;
listCurrUserNotice(this.queryParams).then(response => {
this.noticeList = response.rows;
this.total = response.total;
this.loading = false;
});
},
其中,流程操作中的“发起”按钮的操作方法暂未开发(20240304记录,待后续更新)
2.5.3 后端处理修改
分别修改后端对应的controller文件和service实现文件:
/**
* 获取当前用户的所有状态通知公告列表
*/
@SaCheckPermission("system:notice:list")
@GetMapping("/curruserlist")
public TableDataInfo<SysNotice> curruserlist(SysNotice notice, PageQuery pageQuery) {
return noticeService.selectPageCurrUserList(notice, pageQuery, getUserId());
}
/**
* 查询当前用户的所有状态的公告
* @param notice
* @param pageQuery
* @param userId
* @return
*/
@Override
public TableDataInfo<SysNotice> selectPageCurrUserList(SysNotice notice, PageQuery pageQuery, Long userId) {
LambdaQueryWrapper<SysNotice> lqw = new LambdaQueryWrapper<SysNotice>()
.like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())
.eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())
.eq(SysNotice::getCreateBy, sysUserService.selectUserById(userId).getUserName());
Page<SysNotice> page = baseMapper.selectPage(pageQuery.build(), lqw);
int i = PageQuery.DEFAULT_PAGE_NUM;
for( SysNotice one : page.getRecords()) {
one.setNoId(Long.valueOf((pageQuery.getPageNum()-1)*pageQuery.getPageSize()+i));
i++;
}
return TableDataInfo.build(page);
}
所有改造完毕后的效果如下:
2.6 过程遇到的问题
2.6.1 jdk17下cglib报错
修改后端的service时,报如下错误,经查是 jdk17下cglib未更新所致
Unable to make protected final java.lang.Class java.lang.ClassLoader
在项目运行的中增加项目VM运行前的参数解决
--add-opens java.base/java.lang=ALL-UNNAMED
2.6.2 SQL update相关问题
因本次修改仿照其他sql表建立的sys_user_notice关联表,在实际修改service过程中发生了以下几类错误,后期如遇到可快速避免:
- 新增表无id,updateById或selectById时报错。此类问题可使用updateByBo之类解决,增加一个lamdaQueryWrapper的表达式,用Bo对象实现查询或更新
- 默认的表都有create_by等系统默认字段,后端MybatisPlus的相关处理默认也是要操作这几个字段的,在尽量少更改代码的情况,在后期新建的SQL表中尽量考虑增加上这几个系统字段