软件研究之JeecgBoot二

标签: 无 分类: 未分类 创建时间:2023-08-20 09:04:21 更新时间:2024-11-23 10:24:26

1.自定义模块引入依赖冲突

在原先的时候,我使用的是Ruoyi的框架进行的部分代码的开发,后来我换成了 jeecg-boot,结果在移植代码的时候,发现我以前写的工具类好像有点问题。Annotation-specified bean name ‘redisService’ for bean class [org.jeecg.modules.monitor.service.RedisService] conflicts with existing, non-compatible bean definition of same name and class

这个问题还是因为基础不牢吧。

【解决方案】
在启动类上,将 MapperScan 扫描的路径进行修改。

1
2
3
4
5
// 错误
@MapperScan(basePackages = {"com.openmap.disaster.*","org.jeecg.*"})

// 正确
@MapperScan(basePackages = {"com.openmap.disaster.mapper"})

2.tinymce is not defined

我在macos上开发的时候,没有问题,可是到了 windows 进行安装配置之后,打开页面出现了:tinymce is not defined,这个问题。但是我明明就已经安装了 tinymce 了,为什么还是出现报错呢?

【尝试方案】
(1) 我尝试了使用 yarn 进行重新安装和运行,也还是不行。
(2) 尝试:“ yarn add @tinymce/tinymce-vue” 结果也还是不行。
(3) 尝试重新用英文命名文件夹,及其父路径,结果不行。
(4) 尝试找官方,结果没有人理会。
(5) 尝试重新git clone ,发现还是自己的某一个部分的代码有问题,因为官方的代码并没有什么问题。

【解决方案】
最后其实没有定位到问题到底出在哪里 我就重新的下载了官方的代码,然后在官方的基础上重新创建了一个分支,把自己的代码从另外一个目录中,重新拷贝复制,将代码进行了替换。竟然奇迹般的好了,但是问题到底出在哪里,还是不知道。

后来我发现在 src\components\Tinymce\src\Editor.vue 这个文件中使用了 tinymce 这个路径的引用,如果报错的话,可以看看这个路径里面有没有这几个资源,这是一个马后炮了。

1
2
3
4
{
language_url: publicPath + 'resource/tinymce/langs/' + langName.value + '.js',
skin_url: publicPath + 'resource/tinymce/skins/ui/' + skinName.value
,

3.首屏优化

使用了vite4之后,好像真的变得快了。

4.打包部署

(1)后端

修改 jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml 配置文件,然后在根目录下执行

1
mvn clean package -Pprod

(2)前端
修改项目根路径下的 .env.production 文件,然后执行打包命令

1
pnpm build 
参考文章:
1.JAR部署方案

5.扫码登录

我把二维码扫码登录进行了修改,通过集成钉钉的接口,实现了钉钉的二维码扫描登录。主要就是下面几步:
(1) 创建一个二维码,前端实时监听二维码的扫码状态
(2) 用户扫码后跳转到二维码扫码接口回调,实现登录,并保存用户信息。
(3) 前端获取到了扫码后的 token,实现登录。

1.修改配置

1
2
3
4
5
6
7
8
9
10
11
12
#第三方APP对接
third-app:
enabled: false
type:
#钉钉
DINGTALK:
enabled: true
# appKey
client-id: xx
# appSecret
client-secret: xx
agent-id: 2682671764

2.修改获取二维码的接口

修改前端获取二维码的地址接口 jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java,返回新的二维码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 登录二维码
*/
@ApiOperation(value = "登录二维码", notes = "登录二维码")
@GetMapping("/getLoginQrcode")
public Result<?> getLoginQrcode(HttpServletRequest request) throws Exception{
String qrcodeId = CommonConstant.LOGIN_QRCODE_PRE+IdWorker.getIdStr();
//定义二维码参数
Map params = new HashMap(5);
params.put("qrcodeId", qrcodeId);

/**
* 创建钉钉扫码登陆url
*/
ThirdAppTypeItemVo config = thirdAppConfig.getDingtalk();
StringBuilder builder = new StringBuilder();
// 构造钉钉OAuth2登录授权地址
builder.append("https://login.dingtalk.com/oauth2/auth");
// 授权通过/拒绝后回调地址。
// 注意 需要与注册应用时登记的域名保持一致。
// String redirectUri = CommonUtils.getBaseUrl(request) + "/sys/thirdLogin/oauth2/dingtalk/callback";
String redirectUri ="http://openmap.tech:10001/jeecg-boot/sys/thirdLogin/oauth2/dingtalk/scanlogin";

builder.append("?redirect_uri=").append(URLEncoder.encode(redirectUri, "UTF-8"));
// 固定值为code。
// 授权通过后返回authCode。
builder.append("&response_type=code");
// 步骤一中创建的应用详情中获取。
// 企业内部应用:client_id为应用的AppKey。
builder.append("&client_id=").append(config.getClientId());
// 授权范围,授权页面显示的授权信息以应用注册时配置的为准。
// openid:授权后可获得用户userid
builder.append("&scope=openid");
// 跟随authCode原样返回。
String state = qrcodeId;
builder.append("&state=").append(state);
//update-begin---author:wangshuai ---date:20220613 for:[issues/I5BOUF]oauth2 钉钉无法登录------------
builder.append("&prompt=").append("consent");
//update-end---author:wangshuai ---date:20220613 for:[issues/I5BOUF]oauth2 钉钉无法登录--------------
String url = builder.toString();

// 返回url
params.put("url", url);

//存放二维码唯一标识30秒有效
redisUtil.set(CommonConstant.LOGIN_QRCODE + qrcodeId, qrcodeId, 30);
return Result.OK(params);
}

3.修改扫码登录回调

修改扫码登录后的回调方法,主要就是手机端展示的页面,同时保存用户扫码信息,文件路径:jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/ThirdLoginController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 企业微信/钉钉 扫码登陆回调
*
* @param code
* @param state
* @return
*/
@GetMapping("/oauth2/{source}/scanlogin")
public String scanLoginCallback(
@PathVariable("source") String source,
// 企业微信返回的code
@RequestParam(value = "code", required = false) String code,
// 钉钉返回的code
@RequestParam(value = "authCode", required = false) String authCode,
@RequestParam("state") String state,ModelMap modelMap) {
SysUser loginUser=null;
modelMap.addAttribute("message","登陆中...");

if (ThirdAppConfig.WECHAT_ENTERPRISE.equalsIgnoreCase(source)) {
log.info("【企业微信】OAuth2登录进入callback:code=" + code + ", state=" + state);
loginUser = thirdAppWechatEnterpriseService.oauth2Login(code);
if (loginUser == null) {
modelMap.addAttribute("message","用户不存在");
}
} else if (ThirdAppConfig.DINGTALK.equalsIgnoreCase(source)) {
log.info("【钉钉】OAuth2登录进入callback:authCode=" + authCode + ", state=" + state);
loginUser = thirdAppDingtalkService.oauth2Login(authCode);
if (loginUser == null) {
modelMap.addAttribute("message","用户不存在");
}
} else {
modelMap.addAttribute("message","不支持的source");
}
try {
/**
* 扫码登陆
*/
// 二维码扫码登陆
Object check = redisUtil.get(CommonConstant.LOGIN_QRCODE +state);
if (oConvertUtils.isNotEmpty(check)) {
//存放token给前台读取
String token = saveToken(loginUser);
redisUtil.set(CommonConstant.LOGIN_QRCODE_TOKEN +state, token, 60);

modelMap.addAttribute("message","扫码登陆成功");
} else {
modelMap.addAttribute("message","二维码已过期,请刷新后重试");
}
} catch (Exception e) {
e.printStackTrace();
modelMap.addAttribute("message","未知错误");
}

return "scanLogin";
}

4.扫码后保存的 token

用户扫码后,会将token保存到内存中,供前端进行获取,前端获取到了相关的信息之后,就可以实现登录了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 获取用户扫码后保存的token
*/
@ApiOperation(value = "获取用户扫码后保存的token", notes = "获取用户扫码后保存的token")
@GetMapping("/getQrcodeToken")
public Result getQrcodeToken(@RequestParam String qrcodeId) {
Object token = redisUtil.get(CommonConstant.LOGIN_QRCODE_TOKEN + qrcodeId);
Map result = new HashMap(5);
Object qrcodeIdExpire = redisUtil.get(CommonConstant.LOGIN_QRCODE + qrcodeId);
if (oConvertUtils.isEmpty(qrcodeIdExpire)) {
//二维码过期通知前台刷新
result.put("token", "-2");
return Result.OK(result);
}
if (oConvertUtils.isNotEmpty(token)) {
result.put("success", true);
result.put("token", token);
} else {
result.put("token", "-1");
}
return Result.OK(result);
}

5.修改前端页面

前端使用的是 src/views/system/loginmini/MiniCodelogin.vue 中的文件,基本上不用动,根据上面的修改接口进行调整,修改获取 二维码的地址,以及定时获取后台二维码状态的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//加载二维码信息
function loadQrCode() {
state.value = '0';
getLoginQrcode().then((res) => {
qrCodeUrl.value = res.url;
if (res.qrcodeId) {
openTimer(res.qrcodeId);
}
});
}
//监控扫码状态
function watchQrcodeToken(qrcodeId) {
getQrcodeToken({ qrcodeId: qrcodeId }).then((res) => {
let token = res.token;
if (token == '-2') {
//二维码过期重新获取
loadQrCode();
clearInterval(timer);
}
//扫码成功
if (res.success) {
state.value = '2';
clearInterval(timer);
setTimeout(() => {
userStore.qrCodeLogin(token);
}, 500);
}
});
}

问题

(1)Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream
这个问题应该不是 jeecg-boot 引起的,是我单独引入了 poi-ooxml 5.2.3 版本导致的。

【解决方法】
将common-io的版本进行升级

1
2
3
4
5
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
参考文章:
1.刚更新完版本就炸了:java.lang.NoClassDefFoundError: org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream 两种方法,一种就是降级poi,一种就是升级 commons-io ,我选择了升级。

(2) Invalid prop: type check failed for prop “value”. Expected String | Number | Symbol, got Array
在使用 CountdownInput.vue 的时候,在浏览器出现了这个警告,应该是说 input 本来应该绑定一个字符串,最后绑定的却是一个数组。应该是这个 CountdownInput 组件有问题。

主要就是在路径:src\components\CountDown\src\CountdownInput.vue,文件中的 state 上面。

6.定时任务

(1) 自定义job(实现类org.quartz.Job)
(2) 在线配置定时任务
(3) 支持在线管理,启停

7.查询过滤器

参考文章:
1.查询过滤器用法
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。