若依(芋道)框架完善记录
版本:芋道源码 2022年4月15日
由于芋道基于若依修改,所以此处改动可能也适用于若依。
修正系统bug
多租户模式无法登录
前端login.vue
增加tenantId !== null
判断:
validator: (rule, value, callback) => {
// debugger
getTenantIdByName(value).then((res) => {
const tenantId = res.data
// 修改下面代码
if (tenantId !== null && tenantId >= 0) {
// 设置租户
Cookies.set('tenantId', tenantId)
callback()
} else {
callback('租户不存在')
}
})
},
前端标签页切换后,无法保存标签页状态
表结构调整
system_menu
表增加一个字段:
alter table map.system_menu add component_name varchar(255);
同时在sql.vm
增加相应字段。
后端调整
MenuDO.java
增加
/**
* 组件名称
*/
private String componentName;
AuthMenuRespVO.java
增加
@ApiModelProperty(value = "组件名称")
private String componentName;
MenuBaseVO.java
增加
@ApiModelProperty(value = "组件名称", example = "post")
@Size(max = 255, message = "组件路径不能超过255个字符")
private String componentName;
前端调整
store/modules/permission.js
,修改filterAsyncRouter
函数:
function filterAsyncRouter() {
// ...
route.meta = {
title: route.name,
icon: route.icon,
// 下方增加新代码
componentName: route.componentName
}
// ...
}
store/modules/tagsView.js
增加代码:
ADD_CACHED_VIEW: (state, view) => {
if (state.cachedViews.includes(view.meta.componentName)) return
if (!view.meta.noCache) {
state.cachedViews.push(view.meta.componentName)
}
},
DEL_CACHED_VIEW: (state, view) => {
const index = state.cachedViews.indexOf(view.meta.componentName)
index > -1 && state.cachedViews.splice(index, 1)
},
system/menu/index.vue
增加两个输入框:
...
<el-table ...>
<!-- 权限标识下方增加 -->
<el-table-column
prop="componentName"
label="组件名称"
:show-overflow-tooltip="true"
/>
...
...
<!-- 添加或修改菜单对话框 -->
<!-- 菜单名称下增加 -->
<el-col :span="12">
<el-form-item label="组件名称" prop="componentName">
<el-input
v-model="form.componentName"
placeholder="请输入组件名称"
/>
</el-form-item>
</el-col>
...
功能调整
修改完成后,菜单管理的对话框中会多出一个“组件名称”输入框,将其设置为Vue组件的name即可:
export default {
name: '与此处name保持一致',
// ...
}
svg-icon图标颜色问题
定位到src/assets/icons/svg
,通过正则搜索所有svg中的fill=".*?"
,删除。
字体尺寸错误问题
定位到src/components/bpmnProcessDesigner/package/theme/process-designer.scss
,将其中的
@import "~bpmn-js-token-simulation/assets/css/normalize.css";
删除。
标签页“关闭左侧”不起作用
定位到store/modules/tagsView.js
,可以发现里面只有delRightTags
,缺少delLeftTags
,将其补全即可。
生成代码中出现编译错误
如果有DECIMAL类型字段,会出现编译错误,因为未import BigDecimal类型,将其补全即可。
修改src/resources/codegen/java/dal/do.vm
:
import java.math.BigDecimal;
再修改src/resources/codegen/java/controller/vo
中的每个vm:
import java.math.BigDecimal;
代码质量优化
.gitignore
使用工具重新生成
前端jsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
},
"target": "ES2015",
"lib": ["ES2021"]
},
"include": ["./src/**/*"],
"exclude": ["dist", "node_modules"]
}
前端代码格式化
使用eslint和prettier批量格式化,保持代码整洁
清理console.log
使用查找功能搜索代码中的console.log
,将其删除。
request超时
找到request.js
,增加超时时间的判断:
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API + '/admin-api/', // 此处的 /admin-api/ 地址,原因是后端的基础路径为 /admin-api/
// 超时
timeout: process.env.NODE_ENV === 'development' ? 1000000 : 10000
})
功能完善
提高系统响应速度
将动画速度调快,或者直接删除,从而提高感官上的响应速度。
前端index.html
把转圈动画直接删掉
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载系统资源,请耐心等待</div>
</div>
前端transition.scss
transition: all .5s;
全部替换成
transition: all .1s;
增加:
.el-loading-spinner .circular {
-webkit-animation: loading-rotate 0.25s linear infinite;
animation: loading-rotate 0.25s linear infinite;
}
加快按钮的转圈速度。
字典数据项维护
定位到views/system/dict/index.vue
,增加按钮:
<el-button
v-hasPermi="['system:dict:update']"
size="mini"
type="text"
icon="el-icon-notebook-2"
@click="$router.push('/dict/type/data/' + scope.row.id)"
>
数据项
</el-button>
完善系统报错与日志查询
前端报错提示优化
修改request.js
,根据实际情况调整报错内容,例如:
- 会话超时:您长时间未操作,已自动注销,请重新登录
- 网络异常:网络连接错误,请检查网络连接,然后重试
- 超时:系统响应超时,请稍后重试
- 502:
else if (message.includes('Request failed with status code')) {
if (message.includes('502')) {
message = `系统正在升级,请稍候。`
} else {
const code = message.substr(message.length - 3)
message = `系统访问异常,请向客服提供错误代码:HTTP ${code}`
}
}
后端报错向用户提供日志ID
GlobalExceptionHandler.java
修改代码:
/**
* 处理系统异常,兜底处理所有的一切
*/
@ExceptionHandler(value = Exception.class)
public CommonResult<?> defaultExceptionHandler(HttpServletRequest req, Throwable ex) {
log.error("[defaultExceptionHandler]", ex);
String message = INTERNAL_SERVER_ERROR.getMsg();
// 插入异常日志
// 如果能获取到事件id,则直接展示id,方便在系统中查询;如果获取不到id,则提供时间,方便去后台翻日志
Long errId = this.createExceptionLog(req, ex);
if (errId != null) {
message += "(日志编号:#" + errId + ")";
} else {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("ddHHmmss");
message += "(日志编号:$" + LocalDateTime.now().format(dtf) + ")";
}
// 返回 ERROR CommonResult
return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), message);
}
private Long createExceptionLog(HttpServletRequest req, Throwable e) {
// 插入错误日志
ApiErrorLogCreateReqDTO errorLog = new ApiErrorLogCreateReqDTO();
Long result = null;
try {
// 初始化 errorLog
initExceptionLog(errorLog, req, e);
// 执行插入 errorLog
Future<Long> errId = apiErrorLogFrameworkService.createApiErrorLogAsync(errorLog);
result = errId.get(3, TimeUnit.SECONDS);
} catch (TimeoutException ex) {
result = null;
} catch (Throwable th) {
log.error("[createExceptionLog][url({}) log({}) 发生异常]", req.getRequestURI(), JsonUtils.toJsonString(errorLog), th);
}
return result;
}
ApiErrorLogFrameworkService.java
修改createApiErrorLogAsync
返回类型:
/**
* 创建 API 错误日志
*
* @param createDTO 创建信息
* @return 日志ID
*/
Future<Long> createApiErrorLogAsync(@Valid ApiErrorLogCreateReqDTO createDTO);
ApiErrorLogServiceImpl.java
修改:
@Override
@Async
public Future<Long> createApiErrorLogAsync(ApiErrorLogCreateReqDTO createDTO) {
ApiErrorLogDO apiErrorLog = ApiErrorLogConvert.INSTANCE.convert(createDTO);
apiErrorLog.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus());
apiErrorLogMapper.insert(apiErrorLog);
return new AsyncResult<>(apiErrorLog.getId());
}
完善日志查询,允许按ID查询日志
后端
ApiAccessLogPageReqVO.java
、ApiErrorLogPageReqVO.java
、LoginLogPageReqVO.java
、OperateLogPageReqVO.java
增加:
@ApiModelProperty(value = "日志编号", example = "666")
private Long id;
ApiAccessLogMapper.java
、ApiErrorLogMapper.java
、LoginLogMapper.java
、OperateLogMapper.java
增加:
selectPage()内加一行
.eqIfPresent("id", reqVO.getId())
前端
src/views/infra/apiAccessLog/index.vue
、apiErrorLog/index.vue
、operatelog/index.vue
、loginlog/index.vue
分别加入:
<el-form-item label="日志编号" prop="id">
<el-input
v-model="queryParams.id"
placeholder="请输入日志编号"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
...
queryParams: {
...
id: null,
...
}
防止对话框意外关闭
前端全局搜索el-dialog
,在每个el-dialog
中加入
:close-on-click-modal="false"
修改后端的src/resources/codegen/vue/views/index.vue.vm
,在el-dialog
后面增加:
:close-on-click-modal="false"
这样再生成代码就不会遇到相同问题了。
处理前端列表页面,搜索条件文字宽度太窄问题
前端全局搜索label-width="68px"
,将数字改得大一些,例如75px。
修改后端的src/resources/codegen/vue/views/index.vue.vm
,将68px换成相同的数字。
向前端提供username字段
修改后端AuthPermissionInfoRespVO.java
:
public static class UserVO {
...
@ApiModelProperty(value = "用户名", required = true, example = "pdiwt源码")
private String username;
}
修改AuthConvert.java
,增加username
:
default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
return AuthPermissionInfoRespVO.builder()
.user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).username(user.getUsername()).build())
.roles(CollectionUtils.convertSet(roleList, RoleDO::getCode))
.permissions(CollectionUtils.convertSet(menuList, MenuDO::getPermission))
.build();
}
系统安全
log4j漏洞
定位pom.xml
,将log4j版本直接改为最新版即可。
生产系统禁用Swagger
修改后端的application-prod.yaml
,增加:
knife4j:
enable: true
production: true
如果是前后分离项目,也可以通过配置nginx等方式,将/doc.html
地址屏蔽掉。
初始化
替换图像
前端src/assets/images
,替换登录背景图片与默认头像。
删除admin头像:
update map.system_user set avatar='' where id=1;
改包名
在代码中改完包名之后,还需要执行一个sql(将下面的com.xxx
换成实际包名),以免启动报错:
update map.pay_channel set config=replace(config,'cn.iocoder', 'com.xxx');
修改租户名
update map.system_tenant set name='app' where id=1;
在前端login.vue
中将“租户”的输入框隐藏,并设置默认值。
前端nginx通用配置
upstream backend {
# TODO 后端服务器地址
server xxx:48080;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
gzip on;
gzip_min_length 1k;
gzip_comp_level 1;
gzip_vary on;
gzip_proxied any;
gzip_disable "MSIE [1-6]\.";
gzip_buffers 32 4k;
gzip_http_version 1.1;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php;
# TODO 设置成前端地址
root /var/www/html;
# 上传大小限制
client_max_body_size 500m;
# 错误页,让403返回404(以应对扫测)
error_page 403 =404 /404;
error_page 404 /404;
location = /404 {
internal;
}
# 安全设置
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection 1;
# 管理后台
location ^~ /api/ { try_files /dev/null @backend; }
location ^~ /admin-api/ { try_files /dev/null @backend; }
location ^~ /infra-api/ { try_files /dev/null @backend; }
location @backend {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_intercept_errors off;
}
# TODO 注意:以下是开发工具,应根据实际需要启停
# Spring Boot监测
# location /admin { try_files /dev/null @backend; }
# location /druid { try_files /dev/null @backend; }
# Swagger
# location = /doc.html { try_files /dev/null @backend; }
# location /webjars/ { try_files /dev/null @backend; }
# location = /swagger-resources { try_files /dev/null @backend; }
# location /v2/ { try_files /dev/null @backend; }
# 前端
location / {
try_files $uri $uri/ /index.html;
}
}