# jeecg-boot-plus_ddd
**Repository Path**: zhouflye/jeecg-boot-plus_dd
## Basic Information
- **Project Name**: jeecg-boot-plus_ddd
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2022-06-19
- **Last Updated**: 2022-06-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
Jeecg-Boot-plus 快速开发平台 2.1.4.1
===============
基于当Jeecg-Boot版本: 2.1.4(发布日期:20200224)
## 与官方版本区别:
- 添加了对jpa的支持,以支持DDD(领域模型驱动)开发,自动更新表结构,
dao部分保持不变。
```
org.springframework.boot
spring-boot-starter-data-jpa
- 安全校验部分:
1、修改 ShiroRealm.java 和 JwtUtil.java ,基于jwt进行安全校验,最大限度降低对redis依赖。
token校验时,先按jwt规则校验,jwt校验通过,不再校验redis中token (确保redis中的token有效期大于jwt的有效期)
```
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
if (token == null) {
log.info("————————身份认证失败——————————IP地址: "+ oConvertUtils.getIpAddrByRequest(SpringContextUtils.getHttpServletRequest()));
throw new AuthenticationException("token为空!");
}
// 校验token有效性
// 解密获得username,用于和数据库进行对比
String username = JwtUtil.getUsername(token);
if (username == null) {
throw new AuthenticationException("token非法无效!");
}
// 查询用户信息
log.info("———校验token是否有效————checkUserTokenIsEffect——————— "+ token);
LoginUser loginUser = sysBaseAPI.getUserByName(username);
if (loginUser == null) {
throw new AuthenticationException("用户不存在!");
}
// 判断用户状态
if (loginUser.getStatus() != 1) {
throw new AuthenticationException("账号已被锁定,请联系管理员!");
}
// 校验token是否超时失效
int errCode = JwtUtil.verifyJWT(token);
if(errCode==2){ // toke合法
throw new AuthenticationException("Token非法无效!");
}
if(errCode==1){ //jwt过期。需要校验reids中的,并刷新redis
boolean exist = redisUtil.exists(CommonConstant.PREFIX_USER_TOKEN + token);
if(!exist){
throw new AuthenticationException("Token失效,请重新登录!");
}
//jwt 过期,但是redis未过期,生成新的继续进行会话
String newToken = JwtUtil.genJWT(username);
String newKey = CommonConstant.PREFIX_USER_TOKEN + newToken;
redisUtil.set(newKey,newToken);
redisUtil.expire(newKey, JwtUtil.EXPIRE_TIME_REDIS / 1000);
loginUser.setActiveToken(newToken);
String oldKey = CommonConstant.PREFIX_USER_TOKEN + token;
redisUtil.del(oldKey);
}
return new SimpleAuthenticationInfo(loginUser, token, getName());
}
```
2、添加拦截器 MyHandlerInterceptorAdapter.java ,当jwt校验超时时,redis未超时,生成新的token,同时删除reids中的旧token,并通过拦截器添加token到response的header中返回给客户端。
```
public class MyHandlerInterceptorAdapter extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request , HttpServletResponse response , Object handler) {
try {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
LoginUser loginUser = (LoginUser) subject.getPrincipal();
if (loginUser != null) {
response.setHeader("token", loginUser.getActiveToken());
}
}
}catch (Exception e){
}
return true;
}
}
```
- 客户端在拦截器中统一替换新的token,废弃旧token
```
service.interceptors.response.use((response) => {
//console.log(response) 更换新的token ssz -20200415
var newToken = response.headers['token']
if(newToken!=undefined && newToken!=''){
Vue.ls.set(ACCESS_TOKEN, newToken, 7 * 24 * 60 * 60 * 1000)
store.commit('SET_TOKEN', newToken)
}
return response.data
}, err)
```
- 支持Spring cloud Alibaba
(未发布)
## 后端技术架构 (以下内容与jeecg-boot保持一致)
- 基础框架:Spring Boot 2.1.3.RELEASE
- 持久层框架:Mybatis-plus_3.1.2
- 安全框架:Apache Shiro 1.4.0,Jwt_3.7.0
- 数据库连接池:阿里巴巴Druid 1.1.10
- 缓存框架:redis
- 日志打印:logback
- 其他:fastjson,poi,Swagger-ui,quartz, lombok(简化代码)等。
## 开发环境
- 语言:Java 8
- IDE(JAVA): Eclipse安装lombok插件 或者 IDEA
- 依赖管理:Maven
- 数据库:MySQL5.7 & Oracle 11g
- 缓存:Redis
## 技术文档
- 在线演示 : [http://boot.jeecg.com](http://boot.jeecg.com)
- 在线文档: [http://doc.jeecg.com/1273753](http://doc.jeecg.com/1273753)
- 常见问题: [入门常见问题大全](http://bbs.jeecg.com/forum.php?mod=viewthread&tid=7816&extra=page%3D1)
- QQ交流群 : ①284271917、②769925425
## 专项文档
#### 一、查询过滤器用法
```
QueryWrapper> queryWrapper = QueryGenerator.initQueryWrapper(?, req.getParameterMap());
```
代码示例:
```
@GetMapping(value = "/list")
public Result> list(JeecgDemo jeecgDemo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
Result> result = new Result>();
//调用QueryGenerator的initQueryWrapper
QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(jeecgDemo, req.getParameterMap());
Page page = new Page(pageNo, pageSize);
IPage pageList = jeecgDemoService.page(page, queryWrapper);
result.setSuccess(true);
result.setResult(pageList);
return result;
}
```
- 查询规则 (本规则不适用于高级查询,高级查询有自己对应的查询类型可以选择 )
| 查询模式 | 用法 | 说明 |
|---------- |-------------------------------------------------------|------------------|
| 模糊查询 | 支持左右模糊和全模糊 需要在查询输入框内前或后带\*或是前后全部带\* | |
| 取非查询 | 在查询输入框前面输入! 则查询该字段不等于输入值的数据(数值类型不支持此种查询,可以将数值字段定义为字符串类型的) | |
| \> \>= < <= | 同取非查询 在输入框前面输入对应特殊字符即表示走对应规则查询 | |
| in查询 | 若传入的数据带,(逗号) 则表示该查询为in查询 | |
| 多选字段模糊查询 | 上述4 有一个特例,若某一查询字段前后都带逗号 则会将其视为走这种查询方式 ,该查询方式是将查询条件以逗号分割再遍历数组 将每个元素作like查询 用or拼接,例如 现在name传入值 ,a,b,c, 那么结果sql就是 name like '%a%' or name like '%b%' or name like '%c%' | |
#### 二、AutoPoi(EXCEL工具类-EasyPOI衍变升级重构版本)
[在线文档](https://github.com/zhangdaiscott/autopoi)
#### 三、代码生成器
> 功能说明: 一键生成的代码(包括:controller、service、dao、mapper、entity、vue)
- 模板位置: src/main/resources/jeecg/code-template
- 技术文档: http://doc.jeecg.com
#### 四、编码排重使用示例
重复校验效果:

1.引入排重接口,代码如下:
```
import { duplicateCheck } from '@/api/api'
```
2.找到编码必填校验规则的前端代码,代码如下:
```
code: {
rules: [
{ required: true, message: '请输入编码!' },
{validator: this.validateCode}
]
},
```
3.找到rules里validator对应的方法在哪里,然后使用第一步中引入的排重校验接口.
以用户online表单编码为示例,其中四个必传的参数有:
```
{tableName:表名,fieldName:字段名,fieldVal:字段值,dataId:表的主键},
```
具体使用代码如下:
```
validateCode(rule, value, callback){
let pattern = /^[a-z|A-Z][a-z|A-Z|\d|_|-]{0,}$/;
if(!pattern.test(value)){
callback('编码必须以字母开头,可包含数字、下划线、横杠');
} else {
var params = {
tableName: "onl_cgreport_head",
fieldName: "code",
fieldVal: value,
dataId: this.model.id
};
duplicateCheck(params).then((res)=>{
if(res.success){
callback();
}else{
callback(res.message);
}
})
}
},
```