Merge branch 'dev-ruoyi-5.8.9'

This commit is contained in:
zhuangpeng.li
2025-04-28 10:44:57 +08:00
191 changed files with 26465 additions and 3057 deletions

View File

@@ -84,7 +84,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<version>2.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>

View File

@@ -2,14 +2,13 @@ package com.fastbee;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
* 启动程序
*
* @author ruoyi
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@SpringBootApplication
public class FastBeeApplication
{
public static void main(String[] args)

View File

@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -26,7 +27,7 @@ import com.fastbee.system.domain.SysCache;
/**
* 缓存监控
*
*
* @author ruoyi
*/
@Api(tags = "缓存监控")
@@ -87,7 +88,7 @@ public class CacheController
public AjaxResult getCacheKeys(@PathVariable String cacheName)
{
Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
return AjaxResult.success(cacheKeys);
return AjaxResult.success(new TreeSet<>(cacheKeys));
}
@ApiOperation("缓存内容")

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysUserOnlineService;
/**
* 在线用户监控
*
*
* @author ruoyi
*/
@Api(tags = "在线用户监控")
@@ -54,24 +54,15 @@ public class SysUserOnlineController extends BaseController
LoginUser user = redisCache.getCacheObject(key);
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
{
if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
{
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
}
else if (StringUtils.isNotEmpty(ipaddr))
{
if (StringUtils.equals(ipaddr, user.getIpaddr()))
{
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
}
else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
{
if (StringUtils.equals(userName, user.getUsername()))
{
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
}
else
{

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysConfigService;
/**
* 参数配置 信息操作处理
*
*
* @author ruoyi
*/
@Api(tags = "参数设置")
@@ -93,7 +93,7 @@ public class SysConfigController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysConfig config)
{
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
if (!configService.checkConfigKeyUnique(config))
{
return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
@@ -110,7 +110,7 @@ public class SysConfigController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysConfig config)
{
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config)))
if (!configService.checkConfigKeyUnique(config))
{
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}

View File

@@ -27,7 +27,7 @@ import com.fastbee.system.service.ISysDeptService;
/**
* 部门信息
*
*
* @author ruoyi
*/
@Api(tags = "部门管理")
@@ -84,7 +84,7 @@ public class SysDeptController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDept dept)
{
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
if (!deptService.checkDeptNameUnique(dept))
{
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
@@ -103,7 +103,7 @@ public class SysDeptController extends BaseController
{
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
if (!deptService.checkDeptNameUnique(dept))
{
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysDictTypeService;
/**
* 数据字典信息
*
*
* @author ruoyi
*/
@Api(tags = "字典管理")
@@ -80,7 +80,7 @@ public class SysDictTypeController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictType dict)
{
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
@@ -97,7 +97,7 @@ public class SysDictTypeController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictType dict)
{
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict)))
if (!dictTypeService.checkDictTypeUnique(dict))
{
return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}

View File

@@ -1,32 +1,30 @@
package com.fastbee.web.controller.system;
import java.util.List;
import java.util.Set;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.entity.SysMenu;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginBody;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.framework.web.service.SysLoginService;
import com.fastbee.framework.web.service.SysPermissionService;
import com.fastbee.framework.web.service.TokenService;
import com.fastbee.system.service.ISysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Set;
/**
* 登录验证
*
*
* @author ruoyi
*/
@Api(tags = "登录验证")
@RestController
public class SysLoginController
{
@@ -38,16 +36,16 @@ public class SysLoginController
@Autowired
private SysPermissionService permissionService;
@Value("${server.broker.enabled}")
private Boolean enabled;
@Autowired
private TokenService tokenService;
/**
* 登录方法
*
*
* @param loginBody 登录信息
* @return 结果
*/
@ApiOperation("用户登录")
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
@@ -61,32 +59,35 @@ public class SysLoginController
/**
* 获取用户信息
*
*
* @return 用户信息
*/
@ApiOperation("获取用户信息")
@GetMapping("getInfo")
public AjaxResult getInfo()
{
SysUser user = SecurityUtils.getLoginUser().getUser();
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions))
{
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
ajax.put("mqtt",enabled);
return ajax;
}
/**
* 获取路由信息
*
*
* @return 路由信息
*/
@ApiOperation("获取路由信息")
@GetMapping("getRouters")
public AjaxResult getRouters()
{

View File

@@ -26,7 +26,7 @@ import com.fastbee.system.service.ISysMenuService;
/**
* 菜单信息
*
*
* @author ruoyi
*/
@Api(tags = "菜单管理")
@@ -94,7 +94,7 @@ public class SysMenuController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu)
{
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
if (!menuService.checkMenuNameUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
@@ -115,7 +115,7 @@ public class SysMenuController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu)
{
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
if (!menuService.checkMenuNameUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
@@ -150,4 +150,4 @@ public class SysMenuController extends BaseController
}
return toAjax(menuService.deleteMenuById(menuId));
}
}
}

View File

@@ -28,7 +28,7 @@ import com.fastbee.system.service.ISysPostService;
/**
* 岗位信息操作处理
*
*
* @author ruoyi
*/
@Api(tags = "岗位管理")
@@ -83,11 +83,11 @@ public class SysPostController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysPost post)
{
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
if (!postService.checkPostNameUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
else if (!postService.checkPostCodeUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
@@ -104,11 +104,11 @@ public class SysPostController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post)
{
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post)))
if (!postService.checkPostNameUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post)))
else if (!postService.checkPostCodeUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}

View File

@@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
/**
* 个人信息 业务处理
@@ -78,29 +79,22 @@ public class SysProfileController extends BaseController
public AjaxResult updateProfile(@RequestBody SysUser user)
{
LoginUser loginUser = getLoginUser();
SysUser sysUser = loginUser.getUser();
user.setUserName(sysUser.getUserName());
if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
SysUser currentUser = loginUser.getUser();
currentUser.setNickName(user.getNickName());
currentUser.setEmail(user.getEmail());
currentUser.setPhonenumber(user.getPhonenumber());
currentUser.setSex(user.getSex());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
{
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
}
user.setUserId(sysUser.getUserId());
user.setPassword(null);
user.setAvatar(null);
user.setDeptId(null);
if (userService.updateUserProfile(user) > 0)
if (userService.updateUserProfile(currentUser) > 0)
{
// 更新缓存用户信息
sysUser.setNickName(user.getNickName());
sysUser.setPhonenumber(user.getPhonenumber());
sysUser.setEmail(user.getEmail());
sysUser.setSex(user.getSex());
tokenService.setLoginUser(loginUser);
return success();
}
@@ -113,8 +107,10 @@ public class SysProfileController extends BaseController
@ApiOperation("重置密码")
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword)
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
{
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
String password = loginUser.getPassword();
@@ -126,10 +122,11 @@ public class SysProfileController extends BaseController
{
return error("新密码不能与旧密码相同");
}
if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userName, newPassword) > 0)
{
// 更新缓存用户密码
loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
}

View File

@@ -37,7 +37,7 @@ import com.fastbee.system.service.ISysUserService;
/**
* 角色信息
*
*
* @author ruoyi
*/
@Api(tags = "角色管理")
@@ -102,11 +102,11 @@ public class SysRoleController extends BaseController
@PostMapping
public AjaxResult add(@Validated @RequestBody SysRole role)
{
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
if (!roleService.checkRoleNameUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
else if (!roleService.checkRoleKeyUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
@@ -126,24 +126,24 @@ public class SysRoleController extends BaseController
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
if (!roleService.checkRoleNameUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role)))
else if (!roleService.checkRoleKeyUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setUpdateBy(getUsername());
if (roleService.updateRole(role) > 0)
{
// 更新缓存用户权限
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
{
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return success();

View File

@@ -1,26 +1,6 @@
package com.fastbee.web.controller.system;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.constant.UserConstants;
import com.fastbee.common.core.controller.BaseController;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.entity.SysDept;
@@ -35,13 +15,22 @@ import com.fastbee.system.service.ISysDeptService;
import com.fastbee.system.service.ISysPostService;
import com.fastbee.system.service.ISysRoleService;
import com.fastbee.system.service.ISysUserService;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户信息
*
*
* @author ruoyi
*/
@Api(tags = "用户管理")
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController
@@ -61,7 +50,6 @@ public class SysUserController extends BaseController
/**
* 获取用户列表
*/
@ApiOperation("获取用户分页列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user)
@@ -71,7 +59,6 @@ public class SysUserController extends BaseController
return getDataTable(list);
}
@ApiOperation("导出用户列表")
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@PostMapping("/export")
@@ -82,7 +69,6 @@ public class SysUserController extends BaseController
util.exportExcel(response, list, "用户数据");
}
@ApiOperation("批量导入用户")
@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('system:user:import')")
@PostMapping("/importData")
@@ -95,8 +81,6 @@ public class SysUserController extends BaseController
return success(message);
}
@ApiOperation("下载用户导入模板")
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
@@ -107,46 +91,44 @@ public class SysUserController extends BaseController
/**
* 根据用户编号获取详细信息
*/
@ApiOperation("根据用户编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
userService.checkUserDataScope(userId);
AjaxResult ajax = AjaxResult.success();
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
if (StringUtils.isNotNull(userId))
{
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
/**
* 新增用户
*/
@ApiOperation("新增用户")
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user)))
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
@@ -158,7 +140,6 @@ public class SysUserController extends BaseController
/**
* 修改用户
*/
@ApiOperation("修改用户")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
@@ -166,17 +147,17 @@ public class SysUserController extends BaseController
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user)))
deptService.checkDeptDataScope(user.getDeptId());
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkUserNameUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail())
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
@@ -187,7 +168,6 @@ public class SysUserController extends BaseController
/**
* 删除用户
*/
@ApiOperation("删除用户")
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
@@ -203,7 +183,6 @@ public class SysUserController extends BaseController
/**
* 重置密码
*/
@ApiOperation("重置用户密码")
@PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
@@ -219,7 +198,6 @@ public class SysUserController extends BaseController
/**
* 状态修改
*/
@ApiOperation("修改用户状态")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
@@ -234,7 +212,6 @@ public class SysUserController extends BaseController
/**
* 根据用户编号获取授权角色
*/
@ApiOperation("根据用户编号获取授权角色")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping("/authRole/{userId}")
public AjaxResult authRole(@PathVariable("userId") Long userId)
@@ -250,13 +227,13 @@ public class SysUserController extends BaseController
/**
* 用户授权角色
*/
@ApiOperation("为用户授权角色")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
roleService.checkRoleDataScope(roleIds);
userService.insertUserAuth(userId, roleIds);
return success();
}
@@ -264,7 +241,6 @@ public class SysUserController extends BaseController
/**
* 获取部门树列表
*/
@ApiOperation("获取部门树列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/deptTree")
public AjaxResult deptTree(SysDept dept)

View File

@@ -1,57 +1,50 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
# 从库数据源
slave:
enabled: false # 从数据源开关/默认关闭
url:
username:
password:
initialSize: 5 # 初始连接数
minIdle: 10 # 最小连接池数量
maxActive: 20 # 最大连接池数量
maxWait: 60000 # 配置获取连接等待超时的时间
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validationQuery: SELECT 1 FROM DUAL # 配置检测连接是否有效
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: fastbee
login-password: fastbee
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic:
druid:
initial-size: 5
min-idle: 10
max-wait: 60000
max-active: 20
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validation-query: 'SELECT 1'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: fastbee
druid:
filters: stat,wall
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
none-base-statement-allow: true
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
# redis 配置
redis:
host: localhost # 地址
port: 6379 # 端口默认为6379
database: 1 # 数据库索引
password: 123456 # 密码
database: 15 # 数据库索引
password: fastbee # 密码
timeout: 10s # 连接超时时间
lettuce:
pool:

View File

@@ -1,52 +1,42 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://177.7.0.11/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: fastbee
# 从库数据源
slave:
enabled: false # 从数据源开关/默认关闭
url:
username:
password:
dynamic:
druid:
initial-size: 5
min-idle: 10
max-wait: 60000
max-active: 20
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validation-query: 'SELECT 1'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://177.7.0.11/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: fastbee
druid:
filters: stat,wall
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
none-base-statement-allow: true
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
initialSize: 5 # 初始连接数
minIdle: 10 # 最小连接池数量
maxActive: 20 # 最大连接池数量
maxWait: 60000 # 配置获取连接等待超时的时间
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validationQuery: SELECT 1 FROM DUAL # 配置检测连接是否有效
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: fastbee
login-password: fastbee
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# redis 配置
redis:
host: 177.7.0.10 # 地址

View File

@@ -0,0 +1,131 @@
# 数据源配置
spring:
datasource:
dynamic:
druid:
initial-size: 5
min-idle: 10
max-wait: 60000
max-active: 20
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
validation-query: 'SELECT 1'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: dm.jdbc.driver.DmDriver
url: jdbc:dm://192.168.5.12:5236/OPENSOURCE&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: OPENSOURCE
password: fastbee@123
druid:
filters: stat
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
none-base-statement-allow: true
# sqlServer: # 配置 SQLServer 数据源
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;databaseName=fastbee
# username: sa
# password: fastbee@123
# postgres: # 配置 postgres 数据源
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: org.postgresql.Driver
# url: jdbc:postgresql://101.33.237.12:5432/fastbee?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: fastbee@123
# dameng: # 配置达梦数据源
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: dm.jdbc.driver.DmDriver
# url: jdbc:dm://192.168.5.28:5236/fastbee&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
# username: root
# password: fastbee@123
# druid:
# filters: stat
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
# slave:
# type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: fastbee
# redis 配置
redis:
host: 81.71.97.58 # 地址
port: 6379 # 端口默认为6379
database: 11 # 数据库索引
password: fastbee # 密码
timeout: 10s # 连接超时时间
lettuce:
pool:
min-idle: 0 # 连接池中的最小空闲连接
max-idle: 8 # 连接池中的最大空闲连接
max-active: 8 # 连接池的最大数据库连接数
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
# sip 配置
sip:
enabled: false # 是否启用视频监控SIPtrue为启用
## 本地调试时绑定网卡局域网IP设备在同一局域网设备接入IP填写绑定IP
## 部署服务端时默认绑定容器IP设备接入IP填写服务器公网IP
#ip: 177.7.0.13
ip: 192.168.5.27
port: 5061 # SIP端口(保持默认)
domain: 3402000000 # 由省级、市级、区级、基层编号组成
id: 34020000002000000001 # 同上,另外增加编号,(可保持默认)
password: 12345678 # 监控设备接入的密码
# 日志配置
logging:
level:
com.fastbee: debug
org.springframework: warn
# Swagger配置
swagger:
enabled: true # 是否开启swagger
pathMapping: /dev-api # 请求前缀
liteflow:
rule-source-ext-data-map:
# 应用名称,规则链和脚本组件名称需要一致,不要修改
applicationName: fastbee
#是否开启SQL日志
sqlLogEnabled: true
# 规则多时,启用快速加载模式
fast-load: false
#是否开启SQL数据轮询自动刷新机制 默认不开启
pollingEnabled: false
pollingIntervalSeconds: 60
pollingStartSeconds: 60
#以下是chain表的配置
chainTableName: iot_scene
chainApplicationNameField: application_name
chainNameField: chain_name
elDataField: el_data
chainEnableField: enable
#以下是script表的配置
scriptTableName: iot_script
scriptApplicationNameField: application_name
scriptIdField: script_id
scriptNameField: script_name
scriptDataField: script_data
scriptTypeField: script_type
scriptLanguageField: script_language
scriptEnableField: enable

View File

@@ -55,6 +55,20 @@ spring:
max-size: 200 # 最大连接数
queue-capacity: 3000 # 最大容量
keep-alive: 60
datasource:
druid:
webStatFilter:
enabled: true
stat-view-servlet:
enabled: true
allow:
url-pattern: /druid/*
loginUsername: fastbee
loginPassword: fastbee
dynamic:
primary: master
strict: false
lazy: true
#集群配置
cluster:

View File

@@ -9,13 +9,14 @@ user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功

View File

@@ -14,7 +14,9 @@ PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="autoMappingBehavior" value="FULL"/>
<setting name="autoMappingUnknownColumnBehavior" value="NONE"/>
</settings>
</configuration>

View File

@@ -1,17 +1,18 @@
package com.fastbee.common.annotation;
import com.fastbee.common.utils.poi.ExcelHandlerAdapter;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.fastbee.common.utils.poi.ExcelHandlerAdapter;
/**
* 自定义导出Excel数据注解
*
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@@ -59,12 +60,12 @@ public @interface Excel
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**
* 导出时在excel中每个列的高度 单位为字符
* 导出时在excel中每个列的高度
*/
public double height() default 14;
/**
* 导出时在excel中每个列的宽 单位为字符
* 导出时在excel中每个列的宽
*/
public double width() default 16;
@@ -83,11 +84,21 @@ public @interface Excel
*/
public String prompt() default "";
/**
* 是否允许内容换行
*/
public boolean wrapText() default false;
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
@@ -114,7 +125,7 @@ public @interface Excel
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出列头背景色
* 导出列头背景
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
@@ -124,7 +135,7 @@ public @interface Excel
public IndexedColors headerColor() default IndexedColors.WHITE;
/**
* 导出单元格背景色
* 导出单元格背景
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE;
@@ -171,7 +182,7 @@ public @interface Excel
public enum ColumnType
{
NUMERIC(0), STRING(1), IMAGE(2);
NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
private final int value;
ColumnType(int value)
@@ -184,4 +195,4 @@ public @interface Excel
return this.value;
}
}
}
}

View File

@@ -1,16 +1,13 @@
package com.fastbee.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fastbee.common.enums.BusinessType;
import com.fastbee.common.enums.OperatorType;
import java.lang.annotation.*;
/**
* 自定义操作日志记录注解
*
*
* @author ruoyi
*
*/
@@ -20,7 +17,7 @@ import com.fastbee.common.enums.OperatorType;
public @interface Log
{
/**
* 模块
* 模块
*/
public String title() default "";
@@ -43,4 +40,9 @@ public @interface Log
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}

View File

@@ -0,0 +1,24 @@
package com.fastbee.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fastbee.common.config.serializer.SensitiveJsonSerializer;
import com.fastbee.common.enums.DesensitizedType;
/**
* 数据脱敏注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
DesensitizedType desensitizedType();
}

View File

@@ -0,0 +1,67 @@
package com.fastbee.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fastbee.common.annotation.Sensitive;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.enums.DesensitizedType;
import com.fastbee.common.utils.SecurityUtils;
/**
* 数据脱敏序列化过滤
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
private DesensitizedType desensitizedType;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
else
{
gen.writeString(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException
{
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
{
this.desensitizedType = annotation.desensitizedType();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
/**
* 是否需要脱敏处理
*/
private boolean desensitization()
{
try
{
LoginUser securityUser = SecurityUtils.getLoginUser();
// 管理员不脱敏
return !securityUser.getUser().isAdmin();
}
catch (Exception e)
{
return true;
}
}
}

View File

@@ -2,9 +2,11 @@ package com.fastbee.common.constant;
import io.jsonwebtoken.Claims;
import java.util.Locale;
/**
* 通用常量信息
*
*
* @author ruoyi
*/
public class Constants
@@ -19,6 +21,11 @@ public class Constants
*/
public static final String GBK = "GBK";
/**
* 系统语言
*/
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
/**
* www主域
*/
@@ -63,7 +70,27 @@ public class Constants
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
/**
* 所有权限标识
*/
public static final String ALL_PERMISSION = "*:*:*";
/**
* 管理员角色权限标识
*/
public static final String SUPER_ADMIN = "admin";
/**
* 角色权限分隔符
*/
public static final String ROLE_DELIMETER = ",";
/**
* 权限标识分隔符
*/
public static final String PERMISSION_DELIMETER = ",";
/**
* 验证码有效期(分钟)
*/
@@ -129,14 +156,19 @@ public class Constants
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.fastbee" };
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.fastbee.common.utils.file", "com.fastbee.common.config" };
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
}

View File

@@ -2,7 +2,7 @@ package com.fastbee.common.constant;
/**
* 用户常量信息
*
*
* @author ruoyi
*/
public class UserConstants
@@ -21,6 +21,9 @@ public class UserConstants
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色正常状态 */
public static final String ROLE_NORMAL = "0";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";
@@ -53,16 +56,16 @@ public class UserConstants
/** Layout组件标识 */
public final static String LAYOUT = "Layout";
/** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验返回结果码 */
public final static String UNIQUE = "0";
public final static String NOT_UNIQUE = "1";
/** 校验是否唯一的返回标识 */
public final static boolean UNIQUE = true;
public final static boolean NOT_UNIQUE = false;
/**
* 用户名长度限制

View File

@@ -1,12 +1,13 @@
package com.fastbee.common.core.domain;
import java.util.HashMap;
import java.util.Objects;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.utils.StringUtils;
/**
* 操作消息提醒
*
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object>
@@ -31,7 +32,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 初始化一个新创建的 AjaxResult 对象
*
*
* @param code 状态码
* @param msg 返回内容
*/
@@ -43,7 +44,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 初始化一个新创建的 AjaxResult 对象
*
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
@@ -79,7 +80,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功消息
*
*
* @return 成功消息
*/
public static AjaxResult success()
@@ -89,7 +90,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功数据
*
*
* @return 成功消息
*/
public static AjaxResult success(Object data)
@@ -109,7 +110,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功消息
*
*
* @param msg 返回内容
* @return 成功消息
*/
@@ -120,7 +121,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回成功消息
*
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
@@ -155,7 +156,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @return 错误消息
*/
public static AjaxResult error()
@@ -165,7 +166,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @param msg 返回内容
* @return 错误消息
*/
@@ -176,7 +177,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @param msg 返回内容
* @param data 数据对象
* @return 错误消息
@@ -188,7 +189,7 @@ public class AjaxResult extends HashMap<String, Object>
/**
* 返回错误消息
*
*
* @param code 状态码
* @param msg 返回内容
* @return 错误消息
@@ -198,6 +199,36 @@ public class AjaxResult extends HashMap<String, Object>
return new AjaxResult(code, msg, null);
}
/**
* 是否为成功消息
*
* @return 结果
*/
public boolean isSuccess()
{
return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
}
/**
* 是否为警告消息
*
* @return 结果
*/
public boolean isWarn()
{
return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG));
}
/**
* 是否为错误消息
*
* @return 结果
*/
public boolean isError()
{
return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG));
}
/**
* 方便链式调用
*

View File

@@ -1,126 +1,175 @@
package com.fastbee.common.core.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Entity基类
*
*
* @author ruoyi
*/
public class BaseEntity implements Serializable
{
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/** 搜索值 */
/**
* 搜索值
*/
@TableField(exist = false)
@ApiModelProperty("搜索值")
@JsonIgnore
private String searchValue;
/** 创建者 */
/**
* 创建者
*/
@ApiModelProperty("创建者")
private String createBy;
/** 创建时间 */
/**
* 创建时间
*/
@ApiModelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新者 */
/**
* 更新者
*/
@ApiModelProperty("更新者")
private String updateBy;
/** 更新时间 */
/**
* 更新时间
*/
@ApiModelProperty("更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 备注 */
/**
* 备注
*/
@ApiModelProperty("备注")
private String remark;
/** 请求参数 */
/**
* 当前记录起始索引 默认值
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
/**
* 构建分页对象
*/
public <T> Page<T> buildPage() {
Integer pageNum = ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
return page;
}
public Integer getPageNum() {
return ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
@TableField(exist = false)
private Integer pageNum;
@TableField(exist = false)
private Integer pageSize;
/**
* 请求参数
*/
@TableField(exist = false)
@ApiModelProperty("请求参数")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
public String getSearchValue()
{
public String getSearchValue() {
return searchValue;
}
public void setSearchValue(String searchValue)
{
public void setSearchValue(String searchValue) {
this.searchValue = searchValue;
}
public String getCreateBy()
{
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy)
{
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime()
{
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime)
{
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy()
{
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy)
{
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime()
{
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime)
{
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getRemark()
{
public String getRemark() {
return remark;
}
public void setRemark(String remark)
{
public void setRemark(String remark) {
this.remark = remark;
}
public Map<String, Object> getParams()
{
if (params == null)
{
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();
}
return params;
}
public void setParams(Map<String, Object> params)
{
public void setParams(Map<String, Object> params) {
this.params = params;
}
}

View File

@@ -0,0 +1,76 @@
package com.fastbee.common.core.domain;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @author admin
* @version 1.0
* @description: 分页参数基础类
* @date 2024-11-15 18:00
*/
@Data
public class PageEntity implements Serializable {
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@TableField(exist = false)
private Integer pageNum;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@TableField(exist = false)
private Integer pageSize;
/**
* 当前记录起始索引 默认值
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
/**
* 构建分页对象
*/
public <T> Page<T> buildPage() {
Integer pageNum = ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
return page;
}
/**
* 请求参数
*/
@TableField(exist = false)
@ApiModelProperty("请求参数")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();
}
return params;
}
public Integer getPageNum() {
return ObjectUtil.defaultIfNull(this.pageNum, DEFAULT_PAGE_NUM);
}
public Integer getPageSize() {
return ObjectUtil.defaultIfNull(this.pageSize, DEFAULT_PAGE_SIZE);
}
}

View File

@@ -14,7 +14,7 @@ import com.fastbee.common.core.domain.BaseEntity;
/**
* 菜单权限表 sys_menu
*
*
* @author ruoyi
*/
@ApiModel(value = "SysMenu", description = "菜单权限表 sys_menu")
@@ -54,6 +54,9 @@ public class SysMenu extends BaseEntity
@ApiModelProperty("路由参数")
private String query;
/** 路由名称默认和路由地址相同的驼峰格式注意因为vue3版本的router会删除名称相同路由为避免名字的冲突特殊情况可以自定义 */
private String routeName;
/** 是否为外链0是 1否 */
@ApiModelProperty("是否为外链0是 1否")
private String isFrame;
@@ -69,7 +72,7 @@ public class SysMenu extends BaseEntity
/** 显示状态0显示 1隐藏 */
@ApiModelProperty("显示状态0显示 1隐藏")
private String visible;
/** 菜单状态0正常 1停用 */
@ApiModelProperty("菜单状态0正常 1停用")
private String status;
@@ -171,6 +174,16 @@ public class SysMenu extends BaseEntity
this.query = query;
}
public String getRouteName()
{
return routeName;
}
public void setRouteName(String routeName)
{
this.routeName = routeName;
}
public String getIsFrame()
{
return isFrame;
@@ -252,7 +265,7 @@ public class SysMenu extends BaseEntity
{
this.children = children;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -262,6 +275,8 @@ public class SysMenu extends BaseEntity
.append("orderNum", getOrderNum())
.append("path", getPath())
.append("component", getComponent())
.append("query", getQuery())
.append("routeName", getRouteName())
.append("isFrame", getIsFrame())
.append("IsCache", getIsCache())
.append("menuType", getMenuType())

View File

@@ -17,7 +17,7 @@ import com.fastbee.common.xss.Xss;
/**
* 用户对象 sys_user
*
*
* @author ruoyi
*/
@ApiModel(value = "SysUser", description = "用户对象 sys_user")
@@ -27,7 +27,7 @@ public class SysUser extends BaseEntity
/** 用户ID */
@ApiModelProperty("用户ID")
@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
/** 部门ID */
@@ -52,7 +52,7 @@ public class SysUser extends BaseEntity
/** 手机号码 */
@ApiModelProperty("手机号码")
@Excel(name = "手机号码")
@Excel(name = "手机号码", cellType = ColumnType.TEXT)
private String phonenumber;
/** 用户性别 */

View File

@@ -1,19 +1,19 @@
package com.fastbee.common.core.domain.model;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fastbee.common.core.domain.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fastbee.common.core.domain.entity.SysUser;
/**
* 登录用户身份权限
*
*
* @author ruoyi
*/
public class LoginUser implements UserDetails
{
public class LoginUser implements UserDetails {
private static final long serialVersionUID = 1L;
/**
@@ -31,6 +31,9 @@ public class LoginUser implements UserDetails
*/
private String token;
private String requestToken;
/**
* 登录时间
*/
@@ -71,64 +74,100 @@ public class LoginUser implements UserDetails
*/
private SysUser user;
public Long getUserId()
{
private String language;
private Long deptUserId;
private Boolean neverExpire = Boolean.FALSE;
public LoginUser() {
}
public LoginUser(SysUser user, Set<String> permissions) {
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions) {
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, String language, SysUser user, Set<String> permissions) {
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.language = language;
this.permissions = permissions;
}
public Boolean getNeverExpire() {
return neverExpire;
}
public void setNeverExpire(Boolean neverExpire) {
this.neverExpire = neverExpire;
}
public String getLanguage() {
return language;
}
public Long getDeptUserId() {
return deptUserId;
}
public void setDeptUserId(Long deptUserId) {
this.deptUserId = deptUserId;
}
public void setLanguage(String language) {
this.language = language;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId)
{
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getDeptId()
{
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId)
{
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public String getToken()
{
public String getToken() {
return token;
}
public void setToken(String token)
{
public void setToken(String token) {
this.token = token;
}
public LoginUser()
{
public String getRequestToken() {
return requestToken;
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
public void setRequestToken(String requestToken) {
this.requestToken = requestToken;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
@Override
public String getPassword()
{
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername()
{
public String getUsername() {
return user.getUserName();
}
@@ -137,130 +176,109 @@ public class LoginUser implements UserDetails
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired()
{
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked()
{
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired()
{
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled()
{
public boolean isEnabled() {
return true;
}
public Long getLoginTime()
{
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime)
{
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public String getIpaddr()
{
public String getIpaddr() {
return ipaddr;
}
public void setIpaddr(String ipaddr)
{
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public String getLoginLocation()
{
public String getLoginLocation() {
return loginLocation;
}
public void setLoginLocation(String loginLocation)
{
public void setLoginLocation(String loginLocation) {
this.loginLocation = loginLocation;
}
public String getBrowser()
{
public String getBrowser() {
return browser;
}
public void setBrowser(String browser)
{
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs()
{
public String getOs() {
return os;
}
public void setOs(String os)
{
public void setOs(String os) {
this.os = os;
}
public Long getExpireTime()
{
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime)
{
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public Set<String> getPermissions()
{
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions)
{
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public SysUser getUser()
{
public SysUser getUser() {
return user;
}
public void setUser(SysUser user)
{
public void setUser(SysUser user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
}

View File

@@ -1,6 +1,15 @@
package com.fastbee.common.core.page;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.sql.SqlUtil;
import java.util.ArrayList;
import java.util.List;
/**
* 分页数据
@@ -24,6 +33,16 @@ public class PageDomain
/** 分页参数合理化 */
private Boolean reasonable = true;
/**
* 当前记录起始索引 默认值
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
public String getOrderBy()
{
if (StringUtils.isEmpty(orderByColumn))
@@ -33,6 +52,64 @@ public class PageDomain
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
}
/**
* 构建分页对象
*/
public <T> Page<T> build() {
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
List<OrderItem> orderItems = buildOrderItem();
if (CollUtil.isNotEmpty(orderItems)) {
page.addOrder(orderItems);
}
return page;
}
/**
* 构建排序
*
* 支持的用法如下:
* {isAsc:"asc",orderByColumn:"id"} order by id asc
* {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc
* {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc
* {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc
*/
private List<OrderItem> buildOrderItem() {
if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) {
return null;
}
String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
orderBy = StringUtils.toUnderScoreCase(orderBy);
// 兼容前端排序类型
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
String[] orderByArr = orderBy.split(StringUtils.SLASH);
String[] isAscArr = isAsc.split(StringUtils.SLASH);
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
throw new ServiceException("排序参数有误");
}
List<OrderItem> list = new ArrayList<>();
// 每个字段各自排序
for (int i = 0; i < orderByArr.length; i++) {
String orderByStr = orderByArr[i];
String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i];
if ("asc".equals(isAscStr)) {
list.add(OrderItem.asc(orderByStr));
} else if ("desc".equals(isAscStr)) {
list.add(OrderItem.desc(orderByStr));
} else {
throw new ServiceException("排序参数有误");
}
}
return list;
}
public Integer getPageNum()
{
return pageNum;

View File

@@ -1,5 +1,8 @@
package com.fastbee.common.core.page;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.List;
@@ -43,6 +46,40 @@ public class TableDataInfo implements Serializable
this.total = total;
}
/**
* 根据分页对象构建表格分页数据对象
*/
public static <T> TableDataInfo build(IPage<T> page) {
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(page.getRecords());
rspData.setTotal(page.getTotal());
return rspData;
}
/**
* 根据数据列表构建表格分页数据对象
*/
public static <T> TableDataInfo build(List<T> list) {
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(list.size());
return rspData;
}
/**
* 构建表格分页数据对象
*/
public static <T> TableDataInfo build() {
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
return rspData;
}
public long getTotal()
{
return total;

View File

@@ -1,13 +1,14 @@
package com.fastbee.common.core.text;
import com.fastbee.common.utils.StringUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.Set;
import com.fastbee.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
/**
* 类型转换器
@@ -364,6 +365,10 @@ public class Convert
*/
public static String[] toStrArray(String str)
{
if (StringUtils.isEmpty(str))
{
return new String[] {};
}
return toStrArray(",", str);
}
@@ -536,7 +541,7 @@ public class Convert
/**
* 转换为boolean<br>
* String支持的值为true、false、yes、ok、no1,0 如果给定的值为空,或者转换失败,返回默认值<br>
* String支持的值为true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值<br>
* 转换失败不会报错
*
* @param value 被转换的值
@@ -565,10 +570,12 @@ public class Convert
case "yes":
case "ok":
case "1":
case "":
return true;
case "false":
case "no":
case "0":
case "":
return false;
default:
return defaultValue;
@@ -791,14 +798,23 @@ public class Convert
{
return (String) obj;
}
else if (obj instanceof byte[])
else if (obj instanceof byte[] || obj instanceof Byte[])
{
return str((byte[]) obj, charset);
}
else if (obj instanceof Byte[])
{
byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
return str(bytes, charset);
if (obj instanceof byte[])
{
return str((byte[]) obj, charset);
}
else
{
Byte[] bytes = (Byte[]) obj;
int length = bytes.length;
byte[] dest = new byte[length];
for (int i = 0; i < length; i++)
{
dest[i] = bytes[i];
}
return str(dest, charset);
}
}
else if (obj instanceof ByteBuffer)
{
@@ -954,9 +970,7 @@ public class Convert
c[i] = (char) (c[i] - 65248);
}
}
String returnString = new String(c);
return returnString;
return new String(c);
}
/**
@@ -977,7 +991,12 @@ public class Convert
String s = "";
for (int i = 0; i < fraction.length; i++)
{
s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
// 优化double计算精度丢失问题
BigDecimal nNum = new BigDecimal(n);
BigDecimal decimal = new BigDecimal(10);
BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
double d = scale.doubleValue();
s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
}
if (s.length() < 1)
{

View File

@@ -0,0 +1,60 @@
package com.fastbee.common.enums;
import com.fastbee.common.utils.DesensitizedUtil;
import java.util.function.Function;
/**
* 脱敏类型
*
* @author ruoyi
*/
public enum DesensitizedType
{
/**
* 姓名第2位星号替换
*/
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
/**
* 密码,全部字符都用*代替
*/
PASSWORD(DesensitizedUtil::password),
/**
* 身份证中间10位星号替换
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),
/**
* 手机号中间4位星号替换
*/
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
/**
* 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
*/
EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
/**
* 银行卡号保留最后4位其他星号替换
*/
BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
/**
* 车牌号码,包含普通车辆、新能源车辆
*/
CAR_LICENSE(DesensitizedUtil::carLicense);
private final Function<String, String> desensitizer;
DesensitizedType(Function<String, String> desensitizer)
{
this.desensitizer = desensitizer;
}
public Function<String, String> desensitizer()
{
return desensitizer;
}
}

View File

@@ -0,0 +1,61 @@
package com.fastbee.common.exception.file;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* 文件上传异常类
*
* @author ruoyi
*/
public class FileUploadException extends Exception
{
private static final long serialVersionUID = 1L;
private final Throwable cause;
public FileUploadException()
{
this(null, null);
}
public FileUploadException(final String msg)
{
this(msg, null);
}
public FileUploadException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
@Override
public void printStackTrace(PrintStream stream)
{
super.printStackTrace(stream);
if (cause != null)
{
stream.println("Caused by:");
cause.printStackTrace(stream);
}
}
@Override
public void printStackTrace(PrintWriter writer)
{
super.printStackTrace(writer);
if (cause != null)
{
writer.println("Caused by:");
cause.printStackTrace(writer);
}
}
@Override
public Throwable getCause()
{
return cause;
}
}

View File

@@ -0,0 +1,16 @@
package com.fastbee.common.exception.user;
/**
* 黑名单IP异常类
*
* @author ruoyi
*/
public class BlackListException extends UserException
{
private static final long serialVersionUID = 1L;
public BlackListException()
{
super("login.blocked", null);
}
}

View File

@@ -0,0 +1,16 @@
package com.fastbee.common.exception.user;
/**
* 用户不存在异常类
*
* @author ruoyi
*/
public class UserNotExistsException extends UserException
{
private static final long serialVersionUID = 1L;
public UserNotExistsException()
{
super("user.not.exists", null);
}
}

View File

@@ -16,7 +16,7 @@ import com.fastbee.common.enums.HttpMethod;
/**
* 防止XSS攻击的过滤器
*
*
* @author ruoyi
*/
public class XssFilter implements Filter
@@ -32,10 +32,10 @@ public class XssFilter implements Filter
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++)
String[] urls = tempExcludes.split(",");
for (String url : urls)
{
excludes.add(url[i]);
excludes.add(url);
}
}
}
@@ -72,4 +72,4 @@ public class XssFilter implements Filter
{
}
}
}

View File

@@ -0,0 +1,64 @@
package com.fastbee.common.mybatis.enums;
import com.fastbee.common.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 数据库类型
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum DataBaseType {
/**
* MySQL
*/
MY_SQL("MySQL"),
/**
* Oracle
*/
ORACLE("Oracle"),
/**
* PostgreSQL
*/
POSTGRE_SQL("PostgreSQL"),
/**
* SQL Server
*/
SQL_SERVER("Microsoft SQL Server"),
/**
* 达梦
*/
DM("DM DBMS");
/**
* 数据库类型
*/
private final String type;
/**
* 根据数据库产品名称查找对应的数据库类型
*
* @param databaseProductName 数据库产品名称
* @return 对应的数据库类型枚举值,如果未找到则返回 null
*/
public static DataBaseType find(String databaseProductName) {
if (StringUtils.isBlank(databaseProductName)) {
return null;
}
for (DataBaseType type : values()) {
if (type.getType().equals(databaseProductName)) {
return type;
}
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package com.fastbee.framework.mybatis.mapper;
package com.fastbee.common.mybatis.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -9,7 +9,7 @@ import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.fastbee.common.core.domain.PageParam;
import com.fastbee.common.core.domain.PageResult;
import com.fastbee.framework.mybatis.utils.MyBatisUtils;
import com.fastbee.common.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;

View File

@@ -1,4 +1,4 @@
package com.fastbee.framework.mybatis.utils;
package com.fastbee.common.mybatis.utils;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;

View File

@@ -5,7 +5,7 @@ import java.math.RoundingMode;
/**
* 精确的浮点数运算
*
*
* @author ruoyi
*/
public class Arith
@@ -108,7 +108,6 @@ public class Arith
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = BigDecimal.ONE;
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
return b.divide(BigDecimal.ONE, scale, RoundingMode.HALF_UP).doubleValue();
}
}

View File

@@ -0,0 +1,49 @@
package com.fastbee.common.utils;
/**
* 脱敏工具类
*
* @author ruoyi
*/
public class DesensitizedUtil
{
/**
* 密码的全部字符都用*代替,比如:******
*
* @param password 密码
* @return 脱敏后的密码
*/
public static String password(String password)
{
if (StringUtils.isBlank(password))
{
return StringUtils.EMPTY;
}
return StringUtils.repeat('*', password.length());
}
/**
* 车牌中间用*代替,如果是错误的车牌,不处理
*
* @param carLicense 完整的车牌号
* @return 脱敏后的车牌
*/
public static String carLicense(String carLicense)
{
if (StringUtils.isBlank(carLicense))
{
return StringUtils.EMPTY;
}
// 普通车牌
if (carLicense.length() == 7)
{
carLicense = StringUtils.hide(carLicense, 3, 6);
}
else if (carLicense.length() == 8)
{
// 新能源车牌
carLicense = StringUtils.hide(carLicense, 3, 7);
}
return carLicense;
}
}

View File

@@ -1,13 +1,14 @@
package com.fastbee.common.utils;
import java.util.Collection;
import java.util.List;
import com.alibaba.fastjson2.JSONArray;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.core.domain.entity.SysDictData;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.utils.spring.SpringUtils;
import java.util.Collection;
import java.util.List;
/**
* 字典工具类
*
@@ -56,6 +57,10 @@ public class DictUtils
*/
public static String getDictLabel(String dictType, String dictValue)
{
if (StringUtils.isEmpty(dictValue))
{
return StringUtils.EMPTY;
}
return getDictLabel(dictType, dictValue, SEPARATOR);
}
@@ -68,6 +73,10 @@ public class DictUtils
*/
public static String getDictValue(String dictType, String dictLabel)
{
if (StringUtils.isEmpty(dictLabel))
{
return StringUtils.EMPTY;
}
return getDictValue(dictType, dictLabel, SEPARATOR);
}
@@ -83,31 +92,31 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNotNull(datas))
if (StringUtils.isNull(datas))
{
if (StringUtils.containsAny(separator, dictValue))
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictValue))
{
for (SysDictData dict : datas)
{
for (SysDictData dict : datas)
for (String value : dictValue.split(separator))
{
for (String value : dictValue.split(separator))
if (value.equals(dict.getDictValue()))
{
if (value.equals(dict.getDictValue()))
{
propertyString.append(dict.getDictLabel()).append(separator);
break;
}
propertyString.append(dict.getDictLabel()).append(separator);
break;
}
}
}
else
}
else
{
for (SysDictData dict : datas)
{
for (SysDictData dict : datas)
if (dictValue.equals(dict.getDictValue()))
{
if (dictValue.equals(dict.getDictValue()))
{
return dict.getDictLabel();
}
return dict.getDictLabel();
}
}
}
@@ -126,8 +135,11 @@ public class DictUtils
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas))
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictLabel))
{
for (SysDictData dict : datas)
{
@@ -154,6 +166,48 @@ public class DictUtils
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型获取字典所有值
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictValues(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictValue()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 根据字典类型获取字典所有标签
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictLabels(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictData dict : datas)
{
propertyString.append(dict.getDictLabel()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 删除指定字典缓存
*

View File

@@ -1,19 +1,27 @@
package com.fastbee.common.utils;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.exception.ServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.exception.ServiceException;
import org.springframework.util.PatternMatchUtils;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* 安全服务工具类
*
*
* @author ruoyi
*/
public class SecurityUtils
{
/**
* 用户ID
**/
@@ -43,7 +51,7 @@ public class SecurityUtils
throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取用户账户
**/
@@ -109,7 +117,7 @@ public class SecurityUtils
/**
* 是否为管理员
*
*
* @param userId 用户ID
* @return 结果
*/
@@ -117,4 +125,55 @@ public class SecurityUtils
{
return userId != null && 1L == userId;
}
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(String permission)
{
return hasPermi(getLoginUser().getPermissions(), permission);
}
/**
* 判断是否包含权限
*
* @param authorities 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(Collection<String> authorities, String permission)
{
return authorities.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission));
}
/**
* 验证用户是否拥有某个角色
*
* @param role 角色标识
* @return 用户是否具备某角色
*/
public static boolean hasRole(String role)
{
List<SysRole> roleList = getLoginUser().getUser().getRoles();
Collection<String> roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet());
return hasRole(roles, role);
}
/**
* 判断是否包含角色
*
* @param roles 角色列表
* @param role 角色
* @return 用户是否具备某角色权限
*/
public static boolean hasRole(Collection<String> roles, String role)
{
return roles.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role));
}
}

View File

@@ -1,43 +1,37 @@
package com.fastbee.common.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import org.springframework.util.AntPathMatcher;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.text.StrFormatter;
import org.springframework.util.AntPathMatcher;
import java.util.*;
/**
* 字符串工具类
*
* @author ruoyi
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 空字符串
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
/** 空字符串 */
private static final String NULLSTR = "";
/**
* 下划线
*/
/** 下划线 */
private static final char SEPARATOR = '_';
/** 星号 */
private static final char ASTERISK = '*';
public static final String SLASH = "/";
/**
* 获取参数不为空值
*
* @param value defaultValue 要判断的value
* @return value 返回值
*/
public static <T> T nvl(T value, T defaultValue) {
public static <T> T nvl(T value, T defaultValue)
{
return value != null ? value : defaultValue;
}
@@ -47,7 +41,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param coll 要判断的Collection
* @return true为空 false非空
*/
public static boolean isEmpty(Collection<?> coll) {
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}
@@ -57,7 +52,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param coll 要判断的Collection
* @return true非空 false
*/
public static boolean isNotEmpty(Collection<?> coll) {
public static boolean isNotEmpty(Collection<?> coll)
{
return !isEmpty(coll);
}
@@ -65,9 +61,10 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* * 判断一个对象数组是否为空
*
* @param objects 要判断的对象数组
* * @return true为空 false非空
** @return true为空 false非空
*/
public static boolean isEmpty(Object[] objects) {
public static boolean isEmpty(Object[] objects)
{
return isNull(objects) || (objects.length == 0);
}
@@ -77,7 +74,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param objects 要判断的对象数组
* @return true非空 false
*/
public static boolean isNotEmpty(Object[] objects) {
public static boolean isNotEmpty(Object[] objects)
{
return !isEmpty(objects);
}
@@ -87,7 +85,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param map 要判断的Map
* @return true为空 false非空
*/
public static boolean isEmpty(Map<?, ?> map) {
public static boolean isEmpty(Map<?, ?> map)
{
return isNull(map) || map.isEmpty();
}
@@ -97,7 +96,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param map 要判断的Map
* @return true非空 false
*/
public static boolean isNotEmpty(Map<?, ?> map) {
public static boolean isNotEmpty(Map<?, ?> map)
{
return !isEmpty(map);
}
@@ -107,7 +107,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param str String
* @return true为空 false非空
*/
public static boolean isEmpty(String str) {
public static boolean isEmpty(String str)
{
return isNull(str) || NULLSTR.equals(str.trim());
}
@@ -117,7 +118,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param str String
* @return true非空串 false空串
*/
public static boolean isNotEmpty(String str) {
public static boolean isNotEmpty(String str)
{
return !isEmpty(str);
}
@@ -127,7 +129,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param object Object
* @return true为空 false非空
*/
public static boolean isNull(Object object) {
public static boolean isNull(Object object)
{
return object == null;
}
@@ -137,7 +140,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param object Object
* @return true非空 false
*/
public static boolean isNotNull(Object object) {
public static boolean isNotNull(Object object)
{
return !isNull(object);
}
@@ -147,37 +151,87 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param object 对象
* @return true是数组 false不是数组
*/
public static boolean isArray(Object object) {
public static boolean isArray(Object object)
{
return isNotNull(object) && object.getClass().isArray();
}
/**
* 去空格
*/
public static String trim(String str) {
public static String trim(String str)
{
return (str == null ? "" : str.trim());
}
/**
* 替换指定字符串的指定区间内字符为"*"
*
* @param str 字符串
* @param startInclude 开始位置(包含)
* @param endExclude 结束位置(不包含)
* @return 替换后的字符串
*/
public static String hide(CharSequence str, int startInclude, int endExclude)
{
if (isEmpty(str))
{
return NULLSTR;
}
final int strLength = str.length();
if (startInclude > strLength)
{
return NULLSTR;
}
if (endExclude > strLength)
{
endExclude = strLength;
}
if (startInclude > endExclude)
{
// 如果起始位置大于结束位置,不替换
return NULLSTR;
}
final char[] chars = new char[strLength];
for (int i = 0; i < strLength; i++)
{
if (i >= startInclude && i < endExclude)
{
chars[i] = ASTERISK;
}
else
{
chars[i] = str.charAt(i);
}
}
return new String(chars);
}
/**
* 截取字符串
*
* @param str 字符串
* @param str 字符串
* @param start 开始
* @return 结果
*/
public static String substring(final String str, int start) {
if (str == null) {
public static String substring(final String str, int start)
{
if (str == null)
{
return NULLSTR;
}
if (start < 0) {
if (start < 0)
{
start = str.length() + start;
}
if (start < 0) {
if (start < 0)
{
start = 0;
}
if (start > str.length()) {
if (start > str.length())
{
return NULLSTR;
}
@@ -187,41 +241,99 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 截取字符串
*
* @param str 字符串
* @param str 字符串
* @param start 开始
* @param end 结束
* @param end 结束
* @return 结果
*/
public static String substring(final String str, int start, int end) {
if (str == null) {
public static String substring(final String str, int start, int end)
{
if (str == null)
{
return NULLSTR;
}
if (end < 0) {
if (end < 0)
{
end = str.length() + end;
}
if (start < 0) {
if (start < 0)
{
start = str.length() + start;
}
if (end > str.length()) {
if (end > str.length())
{
end = str.length();
}
if (start > end) {
if (start > end)
{
return NULLSTR;
}
if (start < 0) {
if (start < 0)
{
start = 0;
}
if (end < 0) {
if (end < 0)
{
end = 0;
}
return str.substring(start, end);
}
/**
* 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串
*
* @param str 要截取的字符串
* @param open 起始字符串
* @param close 结束字符串
* @return 截取结果
*/
public static String substringBetweenLast(final String str, final String open, final String close)
{
if (isEmpty(str) || isEmpty(open) || isEmpty(close))
{
return NULLSTR;
}
final int start = str.indexOf(open);
if (start != INDEX_NOT_FOUND)
{
final int end = str.lastIndexOf(close);
if (end != INDEX_NOT_FOUND)
{
return str.substring(start + open.length(), end);
}
}
return NULLSTR;
}
/**
* 判断是否为空,并且不是空白字符
*
* @param str 要判断的value
* @return 结果
*/
public static boolean hasText(String str)
{
return (str != null && !str.isEmpty() && containsText(str));
}
private static boolean containsText(CharSequence str)
{
int strLen = str.length();
for (int i = 0; i < strLen; i++)
{
if (!Character.isWhitespace(str.charAt(i)))
{
return true;
}
}
return false;
}
/**
* 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
@@ -232,11 +344,13 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* 转义\ format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
*
* @param template 文本模板,被替换的部分用 {} 表示
* @param params 参数值
* @param params 参数值
* @return 格式化后的文本
*/
public static String format(String template, Object... params) {
if (isEmpty(params) || isEmpty(template)) {
public static String format(String template, Object... params)
{
if (isEmpty(params) || isEmpty(template))
{
return template;
}
return StrFormatter.format(template, params);
@@ -248,7 +362,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param link 链接
* @return 结果
*/
public static boolean ishttp(String link) {
public static boolean ishttp(String link)
{
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
}
@@ -259,35 +374,54 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param sep 分隔符
* @return set集合
*/
public static final Set<String> str2Set(String str, String sep) {
public static final Set<String> str2Set(String str, String sep)
{
return new HashSet<String>(str2List(str, sep, true, false));
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @param filterBlank 过滤纯空白
* @param trim 去掉首尾空白
* @param str 字符串
* @param sep 分隔符
* @return list集合
*/
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
public static final List<String> str2List(String str, String sep)
{
return str2List(str, sep, true, false);
}
/**
* 字符串转list
*
* @param str 字符串
* @param sep 分隔符
* @param filterBlank 过滤纯空白
* @param trim 去掉首尾空白
* @return list集合
*/
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
{
List<String> list = new ArrayList<String>();
if (StringUtils.isEmpty(str)) {
if (StringUtils.isEmpty(str))
{
return list;
}
// 过滤空白字符串
if (filterBlank && StringUtils.isBlank(str)) {
if (filterBlank && StringUtils.isBlank(str))
{
return list;
}
String[] split = str.split(sep);
for (String string : split) {
if (filterBlank && StringUtils.isBlank(string)) {
for (String string : split)
{
if (filterBlank && StringUtils.isBlank(string))
{
continue;
}
if (trim) {
if (trim)
{
string = string.trim();
}
list.add(string);
@@ -297,18 +431,24 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
}
/**
* 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
* 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
*
* @param set 给定的集合
* @param collection 给定的集合
* @param array 给定的数组
* @return boolean 结果
*/
public static boolean containsAny(Collection<String> collection, String... array) {
if (isEmpty(collection) || isEmpty(array)) {
public static boolean containsAny(Collection<String> collection, String... array)
{
if (isEmpty(collection) || isEmpty(array))
{
return false;
} else {
for (String str : array) {
if (collection.contains(str)) {
}
else
{
for (String str : array)
{
if (collection.contains(str))
{
return true;
}
}
@@ -319,16 +459,20 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
*
* @param cs 指定字符串
* @param cs 指定字符串
* @param searchCharSequences 需要检查的字符串数组
* @return 是否包含任意一个字符串
*/
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
if (isEmpty(cs) || isEmpty(searchCharSequences)) {
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
{
if (isEmpty(cs) || isEmpty(searchCharSequences))
{
return false;
}
for (CharSequence testStr : searchCharSequences) {
if (containsIgnoreCase(cs, testStr)) {
for (CharSequence testStr : searchCharSequences)
{
if (containsIgnoreCase(cs, testStr))
{
return true;
}
}
@@ -338,8 +482,10 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 驼峰转下划线命名
*/
public static String toUnderScoreCase(String str) {
if (str == null) {
public static String toUnderScoreCase(String str)
{
if (str == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
@@ -349,23 +495,31 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
boolean curreCharIsUpperCase = true;
// 下一字符是否大写
boolean nexteCharIsUpperCase = true;
for (int i = 0; i < str.length(); i++) {
for (int i = 0; i < str.length(); i++)
{
char c = str.charAt(i);
if (i > 0) {
if (i > 0)
{
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
} else {
}
else
{
preCharIsUpperCase = false;
}
curreCharIsUpperCase = Character.isUpperCase(c);
if (i < (str.length() - 1)) {
if (i < (str.length() - 1))
{
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
}
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
{
sb.append(SEPARATOR);
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
}
else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
{
sb.append(SEPARATOR);
}
sb.append(Character.toLowerCase(c));
@@ -377,14 +531,18 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs) {
if (str != null && strs != null) {
for (String s : strs) {
if (str.equalsIgnoreCase(trim(s))) {
public static boolean inStringIgnoreCase(String str, String... strs)
{
if (str != null && strs != null)
{
for (String s : strs)
{
if (str.equalsIgnoreCase(trim(s)))
{
return true;
}
}
@@ -398,21 +556,27 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name) {
public static String convertToCamelCase(String name)
{
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty()) {
if (name == null || name.isEmpty())
{
// 没必要转换
return "";
} else if (!name.contains("_")) {
}
else if (!name.contains("_"))
{
// 不含下划线,仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
for (String camel : camels) {
for (String camel : camels)
{
// 跳过原始字符串中开头、结尾的下换线或双重下划线
if (camel.isEmpty()) {
if (camel.isEmpty())
{
continue;
}
// 首字母大写
@@ -423,24 +587,37 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
}
/**
* 驼峰式命名法 例如user_name->userName
* 驼峰式命名法
* 例如user_name->userName
*/
public static String toCamelCase(String s) {
if (s == null) {
public static String toCamelCase(String s)
{
if (s == null)
{
return null;
}
if (s.indexOf(SEPARATOR) == -1)
{
return s;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (c == SEPARATOR) {
if (c == SEPARATOR)
{
upperCase = true;
} else if (upperCase) {
}
else if (upperCase)
{
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
}
else
{
sb.append(c);
}
}
@@ -450,16 +627,20 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
*
* @param str 指定字符串
* @param str 指定字符串
* @param strs 需要检查的字符串数组
* @return 是否匹配
*/
public static boolean matches(String str, List<String> strs) {
if (isEmpty(str) || isEmpty(strs)) {
public static boolean matches(String str, List<String> strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String pattern : strs) {
if (isMatch(pattern, str)) {
for (String pattern : strs)
{
if (isMatch(pattern, str))
{
return true;
}
}
@@ -473,202 +654,67 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* ** 表示任意层路径;
*
* @param pattern 匹配规则
* @param url 需要匹配的url
* @param url 需要匹配的url
* @return
*/
public static boolean isMatch(String pattern, String url) {
public static boolean isMatch(String pattern, String url)
{
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
@SuppressWarnings("unchecked")
public static <T> T cast(Object obj) {
public static <T> T cast(Object obj)
{
return (T) obj;
}
/**
* 数字左边补齐0使之达到指定长度。注意如果数字转换为字符串后长度大于size则只保留 最后size个字符。
*
* @param num 数字对象
* @param num 数字对象
* @param size 字符串指定长度
* @return 返回数字的字符串格式,该字符串为指定长度。
*/
public static final String padl(final Number num, final int size) {
public static final String padl(final Number num, final int size)
{
return padl(num.toString(), size, '0');
}
/**
* 字符串左补齐。如果原始字符串s长度大于size则只保留最后size个字符。
*
* @param s 原始字符串
* @param s 原始字符串
* @param size 字符串指定长度
* @param c 用于补齐的字符
* @param c 用于补齐的字符
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
*/
public static final String padl(final String s, final int size, final char c) {
public static final String padl(final String s, final int size, final char c)
{
final StringBuilder sb = new StringBuilder(size);
if (s != null) {
if (s != null)
{
final int len = s.length();
if (s.length() <= size) {
for (int i = size - len; i > 0; i--) {
if (s.length() <= size)
{
for (int i = size - len; i > 0; i--)
{
sb.append(c);
}
sb.append(s);
} else {
}
else
{
return s.substring(len - size, len);
}
} else {
for (int i = size; i > 0; i--) {
}
else
{
for (int i = size; i > 0; i--)
{
sb.append(c);
}
}
return sb.toString();
}
/*将字符串转小写,首字母大写,其他小写*/
public static String upperCase(String str) {
char[] ch = str.toLowerCase().toCharArray();
if (ch[0] >= 'a' && ch[0] <= 'z') {
ch[0] = (char) (ch[0] - 32);
}
return new String(ch);
}
public static String toString(Object value) {
if (value == null) {
return "null";
}
if (value instanceof ByteBuf) {
return ByteBufUtil.hexDump((ByteBuf) value);
}
if (!value.getClass().isArray()) {
return value.toString();
}
StringBuilder root = new StringBuilder(32);
toString(value, root);
return root.toString();
}
public static StringBuilder toString(Object value, StringBuilder builder) {
if (value == null) {
return builder;
}
builder.append('[');
int start = builder.length();
if (value instanceof long[]) {
long[] array = (long[]) value;
for (long t : array) {
builder.append(t).append(',');
}
} else if (value instanceof int[]) {
int[] array = (int[]) value;
for (int t : array) {
builder.append(t).append(',');
}
} else if (value instanceof short[]) {
short[] array = (short[]) value;
for (short t : array) {
builder.append(t).append(',');
}
} else if (value instanceof byte[]) {
byte[] array = (byte[]) value;
for (byte t : array) {
builder.append(t).append(',');
}
} else if (value instanceof char[]) {
char[] array = (char[]) value;
for (char t : array) {
builder.append(t).append(',');
}
} else if (value instanceof double[]) {
double[] array = (double[]) value;
for (double t : array) {
builder.append(t).append(',');
}
} else if (value instanceof float[]) {
float[] array = (float[]) value;
for (float t : array) {
builder.append(t).append(',');
}
} else if (value instanceof boolean[]) {
boolean[] array = (boolean[]) value;
for (boolean t : array) {
builder.append(t).append(',');
}
} else if (value instanceof String[]) {
String[] array = (String[]) value;
for (String t : array) {
builder.append(t).append(',');
}
} else if (isArray1(value)) {
Object[] array = (Object[]) value;
for (Object t : array) {
toString(t, builder).append(',');
}
} else if (value instanceof Object[]) {
Object[] array = (Object[]) value;
for (Object t : array) {
builder.append(t).append(',');
}
}
int end = builder.length();
if (end <= start) {
builder.append(']');
} else {
builder.setCharAt(end - 1, ']');
}
return builder;
}
private static boolean isArray1(Object value) {
Class<?> componentType = value.getClass().getComponentType();
if (componentType == null) {
return false;
}
return componentType.isArray();
}
public static String leftPad(String str, int size, char ch) {
int length = str.length();
int pads = size - length;
if (pads > 0) {
char[] result = new char[size];
str.getChars(0, length, result, pads);
while (pads > 0) {
result[--pads] = ch;
}
return new String(result);
}
return str;
}
/**
* 获取字符串中的数字
* @param str
* @return
*/
public static Integer matcherNum(String str){
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
return Integer.parseInt(matcher.group());
}
return 0;
}
}

View File

@@ -21,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.utils.StringUtils;
import org.springframework.http.MediaType;
/**
* 通用http发送方法
@@ -74,7 +75,7 @@ public class HttpUtils
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
@@ -121,10 +122,23 @@ public class HttpUtils
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 JSON String格式 的形式。
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
{
return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数
* @param contentType 内容类型
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param, String contentType)
{
PrintWriter out = null;
BufferedReader in = null;
@@ -136,9 +150,9 @@ public class HttpUtils
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
@@ -190,6 +204,11 @@ public class HttpUtils
}
public static String sendSSLPost(String url, String param)
{
return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
public static String sendSSLPost(String url, String param, String contentType)
{
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
@@ -202,9 +221,9 @@ public class HttpUtils
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);

View File

@@ -1,20 +1,39 @@
package com.fastbee.common.utils.ip;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import com.fastbee.common.utils.StringUtils;
/**
* 获取IP方法
*
*
* @author ruoyi
*/
public class IpUtils
{
public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
// 匹配 ip
public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
// 匹配网段
public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
/**
* 获取客户端IP
*
*
* @return IP地址
*/
public static String getIpAddr()
{
return getIpAddr(ServletUtils.getRequest());
}
/**
* 获取客户端IP
*
* @param request 请求对象
* @return IP地址
*/
@@ -52,7 +71,7 @@ public class IpUtils
/**
* 检查是否为内部IP地址
*
*
* @param ip IP地址
* @return 结果
*/
@@ -64,7 +83,7 @@ public class IpUtils
/**
* 检查是否为内部IP地址
*
*
* @param addr byte地址
* @return 结果
*/
@@ -107,7 +126,7 @@ public class IpUtils
/**
* 将IPv4地址转换成字节
*
*
* @param text IPv4地址
* @return byte 字节
*/
@@ -195,7 +214,7 @@ public class IpUtils
/**
* 获取IP地址
*
*
* @return 本地IP地址
*/
public static String getHostIp()
@@ -212,7 +231,7 @@ public class IpUtils
/**
* 获取主机名
*
*
* @return 本地主机名
*/
public static String getHostName()
@@ -248,7 +267,7 @@ public class IpUtils
}
}
}
return ip;
return StringUtils.substring(ip, 0, 255);
}
/**
@@ -261,4 +280,104 @@ public class IpUtils
{
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
}
/**
* 是否为IP
*/
public static boolean isIP(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
}
/**
* 是否为IP或 *为间隔的通配符地址
*/
public static boolean isIpWildCard(String ip)
{
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
}
/**
* 检测参数是否在ip通配符里
*/
public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip)
{
String[] s1 = ipWildCard.split("\\.");
String[] s2 = ip.split("\\.");
boolean isMatchedSeg = true;
for (int i = 0; i < s1.length && !s1[i].equals("*"); i++)
{
if (!s1[i].equals(s2[i]))
{
isMatchedSeg = false;
break;
}
}
return isMatchedSeg;
}
/**
* 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
*/
public static boolean isIPSegment(String ipSeg)
{
return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
}
/**
* 判断ip是否在指定网段中
*/
public static boolean ipIsInNetNoCheck(String iparea, String ip)
{
int idx = iparea.indexOf('-');
String[] sips = iparea.substring(0, idx).split("\\.");
String[] sipe = iparea.substring(idx + 1).split("\\.");
String[] sipt = ip.split("\\.");
long ips = 0L, ipe = 0L, ipt = 0L;
for (int i = 0; i < 4; ++i)
{
ips = ips << 8 | Integer.parseInt(sips[i]);
ipe = ipe << 8 | Integer.parseInt(sipe[i]);
ipt = ipt << 8 | Integer.parseInt(sipt[i]);
}
if (ips > ipe)
{
long t = ips;
ips = ipe;
ipe = t;
}
return ips <= ipt && ipt <= ipe;
}
/**
* 校验ip是否符合过滤串规则
*
* @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
* @param ip 校验IP地址
* @return boolean 结果
*/
public static boolean isMatchedIp(String filter, String ip)
{
if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip))
{
return false;
}
String[] ips = filter.split(";");
for (String iStr : ips)
{
if (isIP(iStr) && iStr.equals(ip))
{
return true;
}
else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip))
{
return true;
}
else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip))
{
return true;
}
}
return false;
}
}

View File

@@ -1,19 +1,24 @@
package com.fastbee.common.utils.poi;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Excel数据格式处理适配器
*
*
* @author ruoyi
*/
public interface ExcelHandlerAdapter
{
/**
* 格式化
*
*
* @param value 单元格数据值
* @param args excel注解args参数组
* @param cell 单元格对象
* @param wb 工作簿对象
*
* @return 处理后的值
*/
Object format(Object value, String[] args);
Object format(Object value, String[] args, Cell cell, Workbook wb);
}

View File

@@ -1,5 +1,6 @@
package com.fastbee.common.utils.spring;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -18,11 +19,11 @@ import java.util.Map;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
*
* @author ruoyi
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
@@ -30,13 +31,13 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
@@ -119,14 +120,19 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC
/**
* 获取aop代理对象
*
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
Object proxy = AopContext.currentProxy();
if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass())
{
return (T) proxy;
}
return invoker;
}
/**

View File

@@ -5,7 +5,7 @@ import com.fastbee.common.utils.StringUtils;
/**
* sql操作工具类
*
*
* @author ruoyi
*/
public class SqlUtil
@@ -13,13 +13,18 @@ public class SqlUtil
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
*/
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
* 限制orderBy最大长度
*/
private static final int ORDER_BY_MAX_LENGTH = 500;
/**
* 检查字符,防止注入绕过
*/
@@ -29,6 +34,10 @@ public class SqlUtil
{
throw new UtilException("参数不符合规范,不能进行查询");
}
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH)
{
throw new UtilException("参数已超过最大限制,不能进行查询");
}
return value;
}

View File

@@ -22,21 +22,21 @@ public class Seq
private static AtomicInteger uploadSeq = new AtomicInteger(1);
// 机器标识
private static String machineCode = "A";
private static final String machineCode = "A";
/**
* 获取通用序列号
*
*
* @return 序列值
*/
public static String getId()
{
return getId(commSeqType);
}
/**
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
*
*
* @return 序列值
*/
public static String getId(String type)
@@ -51,7 +51,7 @@ public class Seq
/**
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
*
*
* @param atomicInt 序列数
* @param length 数值长度
* @return 序列值
@@ -66,7 +66,7 @@ public class Seq
/**
* 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
*
*
* @return 序列值
*/
private synchronized static String getSeq(AtomicInteger atomicInt, int length)

View File

@@ -8,7 +8,7 @@ import java.util.regex.Pattern;
/**
* 自定义xss校验注解实现
*
*
* @author ruoyi
*/
public class XssValidator implements ConstraintValidator<Xss, String>
@@ -27,8 +27,13 @@ public class XssValidator implements ConstraintValidator<Xss, String>
public static boolean containsHtml(String value)
{
StringBuilder sHtml = new StringBuilder();
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
return matcher.matches();
while (matcher.find())
{
sHtml.append(matcher.group());
}
return pattern.matcher(sHtml).matches();
}
}
}

View File

@@ -16,6 +16,10 @@
</description>
<dependencies>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-common</artifactId>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
@@ -59,6 +63,13 @@
<artifactId>oshi-core</artifactId>
</dependency>
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -7,6 +7,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.fastbee.common.annotation.DataScope;
import com.fastbee.common.constant.UserConstants;
import com.fastbee.common.core.domain.BaseEntity;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.entity.SysUser;
@@ -73,8 +74,7 @@ public class DataScopeAspect
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
{
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias(), permission);
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
}
}
}
@@ -92,29 +92,42 @@ public class DataScopeAspect
{
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
List<String> scopeCustomIds = new ArrayList<String>();
user.getRoles().forEach(role -> {
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
}
});
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
{
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
conditions.add(dataScope);
break;
}
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
role.getRoleId()));
if (scopeCustomIds.size() > 1)
{
// 多个自定数据权限使用in查询避免多次拼接。
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
}
else
{
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
}
}
else if (DATA_SCOPE_DEPT.equals(dataScope))
{
@@ -122,9 +135,7 @@ public class DataScopeAspect
}
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
{
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getDeptId(), user.getDeptId()));
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
}
else if (DATA_SCOPE_SELF.equals(dataScope))
{
@@ -141,6 +152,12 @@ public class DataScopeAspect
conditions.add(dataScope);
}
// 角色都不包含传递过来的权限字符这个时候sqlString也会为空所以要限制一下,不查询任何数据
if (StringUtils.isEmpty(conditions))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
}
if (StringUtils.isNotBlank(sqlString.toString()))
{
Object params = joinPoint.getArgs()[0];

View File

@@ -1,24 +1,14 @@
package com.fastbee.framework.aspectj;
import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.text.Convert;
import com.fastbee.common.enums.BusinessStatus;
import com.fastbee.common.enums.HttpMethod;
import com.fastbee.common.filter.PropertyPreExcludeFilter;
import com.fastbee.common.utils.ExceptionUtil;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
@@ -26,10 +16,27 @@ import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.framework.manager.AsyncManager;
import com.fastbee.framework.manager.factory.AsyncFactory;
import com.fastbee.system.domain.SysOperLog;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
/**
* 操作日志记录处理
*
*
* @author ruoyi
*/
@Aspect
@@ -41,6 +48,18 @@ public class LogAspect
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
/**
* 处理请求前执行
*/
@Before(value = "@annotation(controllerLog)")
public void doBefore(JoinPoint joinPoint, Log controllerLog)
{
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
/**
* 处理完请求后执行
*
@@ -54,7 +73,7 @@ public class LogAspect
/**
* 拦截异常操作
*
*
* @param joinPoint 切点
* @param e 异常
*/
@@ -75,18 +94,23 @@ public class LogAspect
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
String ip = IpUtils.getIpAddr();
operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
if (loginUser != null)
{
operLog.setOperName(loginUser.getUsername());
SysUser currentUser = loginUser.getUser();
if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept()))
{
operLog.setDeptName(currentUser.getDept().getDeptName());
}
}
if (e != null)
{
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
@@ -96,6 +120,8 @@ public class LogAspect
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
// 保存数据库
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}
@@ -105,11 +131,15 @@ public class LogAspect
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
finally
{
TIME_THREADLOCAL.remove();
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
*
* @param log 日志
* @param operLog 操作日志
* @throws Exception
@@ -126,7 +156,7 @@ public class LogAspect
if (log.isSaveRequestData())
{
// 获取参数的信息,传入到数据库中。
setRequestValue(joinPoint, operLog);
setRequestValue(joinPoint, operLog, log.excludeParamNames());
}
// 是否需要保存response参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
@@ -137,29 +167,29 @@ public class LogAspect
/**
* 获取请求的参数放到log中
*
*
* @param operLog 操作日志
* @throws Exception 异常
*/
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
{
String params = argsArrayToString(joinPoint.getArgs());
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
}
else
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter()), 0, 2000));
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray)
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
@@ -170,7 +200,7 @@ public class LogAspect
{
try
{
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter());
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
params += jsonObj.toString() + " ";
}
catch (Exception e)
@@ -185,14 +215,14 @@ public class LogAspect
/**
* 忽略敏感属性
*/
public PropertyPreExcludeFilter excludePropertyPreFilter()
public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
{
return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES);
return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
}
/**
* 判断是否需要过滤的对象。
*
*
* @param o 对象信息。
* @return 如果是需要过滤的对象则返回true否则返回false。
*/

View File

@@ -79,7 +79,7 @@ public class RateLimiterAspect
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP)
{
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
stringBuffer.append(IpUtils.getIpAddr()).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();

View File

@@ -1,7 +1,6 @@
package com.fastbee.framework.config;
import java.util.TimeZone;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -15,8 +14,6 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.fastbee.**.mapper")
public class ApplicationConfig
{
/**

View File

@@ -0,0 +1,61 @@
package com.fastbee.framework.config;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class DataSourceConfig {
private final DynamicDataSourceProperties properties;
private final DefaultDataSourceCreator dataSourceCreator;
public DataSourceConfig(DynamicDataSourceProperties properties,
DefaultDataSourceCreator dataSourceCreator) {
this.properties = properties;
this.dataSourceCreator = dataSourceCreator;
}
@Bean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
return new AbstractDataSourceProvider(dataSourceCreator) {
@Override
public Map<String, DataSource> loadDataSources() {
Map<String, DataSource> dataSourceMap = new HashMap<>();
return dataSourceMap;
}
};
}
@Primary
@Bean
public DataSource dataSource(List<DynamicDataSourceProvider> providers) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(providers);
dataSource.setPrimary(properties.getPrimary());
dataSource.setStrict(properties.getStrict());
dataSource.setStrategy(properties.getStrategy());
dataSource.setP6spy(properties.getP6spy());
dataSource.setSeata(properties.getSeata());
return dataSource;
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}

View File

@@ -1,89 +1,23 @@
package com.fastbee.framework.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.fastbee.common.enums.DataSourceType;
import com.fastbee.common.utils.spring.SpringUtils;
import com.fastbee.framework.config.properties.DruidProperties;
import com.fastbee.framework.datasource.DynamicDataSource;
/**
* druid 配置多数据源
*
* @author ruoyi
*/
import javax.servlet.*;
import java.io.IOException;
@Configuration
public class DruidConfig
{
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
return new DynamicDataSource(masterDataSource, targetDataSources);
}
/**
* 设置数据源
*
* @param targetDataSources 备选数据源集合
* @param sourceName 数据源名称
* @param beanName bean名称
*/
public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
{
try
{
DataSource dataSource = SpringUtils.getBean(beanName);
targetDataSources.put(sourceName, dataSource);
}
catch (Exception e)
{
}
}
public class DruidConfig {
/**
* 去除监控页面底部的广告
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
@ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true")
public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
{
// 获取web监控页面的参数

View File

@@ -9,7 +9,7 @@ import com.alibaba.fastjson2.JSONWriter;
/**
* Redis使用FastJson序列化
*
*
* @author ruoyi
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>

View File

@@ -1,16 +1,17 @@
package com.fastbee.framework.config;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import com.fastbee.common.filter.RepeatableFilter;
import com.fastbee.common.filter.XssFilter;
import com.fastbee.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fastbee.common.filter.RepeatableFilter;
import com.fastbee.common.filter.XssFilter;
import com.fastbee.common.utils.StringUtils;
import javax.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;
/**
* Filter配置

View File

@@ -0,0 +1,43 @@
package com.fastbee.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import com.fastbee.common.constant.Constants;
/**
* 资源文件配置加载
*
* @author ruoyi
*/
@Configuration
public class I18nConfig implements WebMvcConfigurer
{
@Bean
public LocaleResolver localeResolver()
{
SessionLocaleResolver slr = new SessionLocaleResolver();
// 默认语言
slr.setDefaultLocale(Constants.DEFAULT_LOCALE);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor()
{
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
// 参数名
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(localeChangeInterceptor());
}
}

View File

@@ -1,177 +0,0 @@
package com.fastbee.framework.config;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.fastbee.common.utils.StringUtils;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.ClassUtils;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/**
* Mybatis支持*匹配扫描包
*
* @author ruoyi
*/
@Configuration
public class MyBatisConfig
{
@Autowired
private Environment env;
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
public static String setTypeAliasesPackage(String typeAliasesPackage)
{
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
List<String> allResult = new ArrayList<String>();
try
{
for (String aliasesPackage : typeAliasesPackage.split(","))
{
List<String> result = new ArrayList<String>();
aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = resolver.getResources(aliasesPackage);
if (resources != null && resources.length > 0)
{
MetadataReader metadataReader = null;
for (Resource resource : resources)
{
if (resource.isReadable())
{
metadataReader = metadataReaderFactory.getMetadataReader(resource);
try
{
result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
}
if (result.size() > 0)
{
HashSet<String> hashResult = new HashSet<String>(result);
allResult.addAll(hashResult);
}
}
if (allResult.size() > 0)
{
typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
}
else
{
throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
}
}
catch (IOException e)
{
e.printStackTrace();
}
return typeAliasesPackage;
}
public Resource[] resolveMapperLocations(String[] mapperLocations)
{
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList<Resource>();
if (mapperLocations != null)
{
for (String mapperLocation : mapperLocations)
{
try
{
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
}
catch (IOException e)
{
// ignore
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
/**
* mybatis 配置
*/
// @Bean(name = "mysqlSessionFactory")
// @Primary
// 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 SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// sessionFactory.setDataSource(dataSource);
// sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
// sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
// sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
// return sessionFactory.getObject();
// }
/**
* mybatis-plus 配置:把 SqlSessionFactoryBean 换成 MybatisSqlSessionFactoryBean就行
* @param dataSource 数据源
* @return
*/
@Bean(name = "mysqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
{
String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
String configLocation = env.getProperty("mybatis-plus.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();
}
@Bean(name = "mysqlTransactionManager")
@Primary
public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "mysqlSqlSessionTemplate")
@Primary
public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

View File

@@ -11,7 +11,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置
*
*
* @author ruoyi
*/
@Configuration

View File

@@ -17,7 +17,7 @@ import com.fastbee.framework.interceptor.RepeatSubmitInterceptor;
/**
* 通用配置
*
*
* @author ruoyi
*/
@Configuration
@@ -36,7 +36,7 @@ public class ResourcesConfig implements WebMvcConfigurer
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
}
/**
@@ -70,4 +70,4 @@ public class ResourcesConfig implements WebMvcConfigurer
// 返回新的CorsFilter
return new CorsFilter(source);
}
}
}

View File

@@ -1,32 +1,35 @@
package com.fastbee.framework.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;
import com.fastbee.framework.config.properties.PermitAllUrlProperties;
import com.fastbee.framework.security.filter.JwtAuthenticationTokenFilter;
import com.fastbee.framework.security.handle.AuthenticationEntryPointImpl;
import com.fastbee.framework.security.handle.LogoutSuccessHandlerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;
/**
* spring security配置
*
* @author ruoyi
*/
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
public class SecurityConfig
{
/**
* 自定义用户认证逻辑
@@ -65,16 +68,15 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
private PermitAllUrlProperties permitAllUrl;
/**
* 解决 无法直接注入 AuthenticationManager
*
* @return
* @throws Exception
* 身份验证实现
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
public AuthenticationManager authenticationManager()
{
return super.authenticationManagerBean();
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider);
}
/**
@@ -92,44 +94,45 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
@Bean
@Primary
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
{
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
httpSecurity
// CSRF禁用因为不使用session
.csrf().disable()
// 禁用HTTP响应标头
.headers().cacheControl().disable().and()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
return httpSecurity
// CSRF禁用因为不使用session
.csrf(csrf -> csrf.disable())
// 禁用HTTP响应标头
.headers((headersCustomizer) -> {
headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
})
// 认证失败处理类
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
// 基于token所以不需要session
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/register", "/captchaImage","/iot/tool/register","/iot/tool/ntp","/iot/tool/download",
"/iot/tool/mqtt/auth","/iot/tool/mqtt/authv5","/iot/tool/mqtt/webhook","/iot/tool/mqtt/webhookv5","/auth/**/**",
"/wechat/mobileLogin", "/wechat/miniLogin", "/wechat/wxBind/callback").permitAll()
.antMatchers("/zlmhook/**").permitAll()
.antMatchers("/goview/sys/login","/goview/project/getData").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
requests.antMatchers("/login", "/register", "/captchaImage","/iot/tool/register","/iot/tool/ntp","/iot/tool/download",
"/iot/tool/mqtt/auth","/iot/tool/mqtt/authv5","/iot/tool/mqtt/webhook","/iot/tool/mqtt/webhookv5","/auth/**/**",
"/wechat/mobileLogin", "/wechat/miniLogin", "/wechat/wxBind/callback").permitAll()
.antMatchers("/zlmhook/**").permitAll()
.antMatchers("/goview/sys/login","/goview/project/getData").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
// 添加Logout filter
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
// 添加JWT filter
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
// 添加CORS filter
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
.addFilterBefore(corsFilter, LogoutFilter.class)
.build();
}
/**
@@ -140,13 +143,4 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
{
return new BCryptPasswordEncoder();
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}

View File

@@ -1,12 +1,13 @@
package com.fastbee.framework.config;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.fastbee.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 服务相关配置
*
*
* @author ruoyi
*/
@Component
@@ -14,7 +15,7 @@ public class ServerConfig
{
/**
* 获取完整的请求路径,包括:域名,端口,上下文访问路径
*
*
* @return 服务地址
*/
public String getUrl()

View File

@@ -0,0 +1,92 @@
package com.fastbee.framework.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 解决Mybatis Plus Order By SQL注入问题
* @author admin
*/
@Slf4j
public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver {
private final static String[] KEYWORDS = { "master", "truncate", "insert", "select", "delete", "update", "declare",
"alter", "drop", "sleep" };
/**
* 判断Controller是否包含page 参数
* @param parameter 参数
* @return 是否过滤
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(Page.class);
}
/**
* @param parameter 入参集合
* @param mavContainer model 和 view
* @param webRequest web相关
* @param binderFactory 入参解析
* @return 检查后新的page对象
* <p>
* page 只支持查询 GET .如需解析POST获取请求报文体处理
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String[] ascs = request.getParameterValues("ascs");
String[] descs = request.getParameterValues("descs");
String current = request.getParameter("current");
String size = request.getParameter("size");
Page<?> page = new Page<>();
if (StrUtil.isNotBlank(current)) {
page.setCurrent(Long.parseLong(current));
}
if (StrUtil.isNotBlank(size)) {
page.setSize(Long.parseLong(size));
}
List<OrderItem> orderItemList = new ArrayList<>();
Optional.ofNullable(ascs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::asc).collect(Collectors.toList())));
Optional.ofNullable(descs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::desc).collect(Collectors.toList())));
page.addOrder(orderItemList);
return page;
}
/**
* 判断用户输入里面有没有关键字
* @return Predicate
*/
private Predicate<String> sqlInjectPredicate() {
return sql -> {
for (String keyword : KEYWORDS) {
if (StrUtil.containsIgnoreCase(sql, keyword)) {
return false;
}
}
return true;
};
}
}

View File

@@ -1,50 +1,31 @@
package com.fastbee.framework.config.properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* druid 配置属性
*
*
* @author ruoyi
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.dynamic.druid")
public class DruidProperties
{
@Value("${spring.datasource.druid.initialSize}")
private int initialSize;
@Value("${spring.datasource.druid.minIdle}")
private int minIdle;
@Value("${spring.datasource.druid.maxActive}")
private int maxActive;
@Value("${spring.datasource.druid.maxWait}")
private int maxWait;
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
private int maxEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.validationQuery}")
private Integer initialSize;
private Integer minIdle;
private Integer maxActive;
private Integer maxWait;
private Long timeBetweenEvictionRunsMillis;
private Long minEvictableIdleTimeMillis;
private Long maxEvictableIdleTimeMillis;
private String validationQuery;
@Value("${spring.datasource.druid.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.druid.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.druid.testOnReturn}")
private boolean testOnReturn;
private Boolean testWhileIdle;
private Boolean testOnBorrow;
private Boolean testOnReturn;
public DruidDataSource dataSource(DruidDataSource datasource)
{
/** 配置初始化大小、最小、最大 */

View File

@@ -3,6 +3,7 @@ package com.fastbee.framework.config.properties;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import org.apache.commons.lang3.RegExUtils;
@@ -19,7 +20,7 @@ import com.fastbee.common.annotation.Anonymous;
/**
* 设置Anonymous注解允许匿名访问的url
*
*
* @author ruoyi
*/
@Configuration
@@ -44,12 +45,12 @@ public class PermitAllUrlProperties implements InitializingBean, ApplicationCont
// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
// 获取类上边的注解, 替代path variable 为 *
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
});
}

View File

@@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory;
/**
* 数据源切换处理
*
*
* @author ruoyi
*/
public class DynamicDataSourceContextHolder
@@ -14,7 +14,7 @@ public class DynamicDataSourceContextHolder
/**
* 使用ThreadLocal维护变量ThreadLocal为每个使用该变量的线程提供独立的变量副本
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

View File

@@ -47,8 +47,9 @@ public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
/**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param request
* @return
* @param request 请求信息
* @param annotation 防重复注解参数
* @return 结果
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);

View File

@@ -18,7 +18,7 @@ import eu.bitwalker.useragentutils.UserAgent;
/**
* 异步工厂(产生任务用)
*
*
* @author ruoyi
*/
public class AsyncFactory
@@ -27,7 +27,7 @@ public class AsyncFactory
/**
* 记录登录信息
*
*
* @param username 用户名
* @param status 状态
* @param message 消息
@@ -38,7 +38,7 @@ public class AsyncFactory
final Object... args)
{
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
final String ip = IpUtils.getIpAddr();
return new TimerTask()
{
@Override
@@ -82,7 +82,7 @@ public class AsyncFactory
/**
* 操作日志记录
*
*
* @param operLog 操作日志信息
* @return 任务task
*/

View File

@@ -0,0 +1,33 @@
package com.fastbee.framework.mybatis.config;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import org.apache.ibatis.executor.Executor;
import java.sql.Connection;
import java.sql.SQLException;
public class AutoPaginationInnerInterceptor extends PaginationInnerInterceptor {
@Override
protected IDialect findIDialect(Executor executor) {
Connection conn = null;
try {
conn = executor.getTransaction().getConnection();
String jdbcUrl = conn.getMetaData().getURL();
} catch (SQLException e) {
throw new RuntimeException(e);
}
if (this.getDialect() != null) {
return this.getDialect();
} else if (this.getDbType() != null) {
this.setDialect(DialectFactory.getDialect(this.getDbType()));
return this.getDialect();
} else {
return DialectFactory.getDialect(JdbcUtils.getDbType(executor));
}
}
}

View File

@@ -0,0 +1,67 @@
package com.fastbee.framework.mybatis.config;
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fastbee.framework.config.SqlFilterArgumentResolver;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @author admin
*/
@Configuration(proxyBeanMethods = false)
@MapperScan("com.fastbee.**.mapper")
public class MybatisPlusConfig implements WebMvcConfigurer {
/**
* SQL 过滤器避免SQL 注入
* @param argumentResolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SqlFilterArgumentResolver());
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(paginationInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型
*/
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new AutoPaginationInnerInterceptor();
// 分页合理化
paginationInnerInterceptor.setOverflow(true);
return paginationInnerInterceptor;
}
/**
* 使用网卡信息绑定雪花生成器
* 防止集群雪花ID重复
*/
@Bean
public IdentifierGenerator idGenerator() {
return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
}
/**
* 审计字段自动填充
* @return {@link MybatisPlusMetaObjectHandler}
*/
@Bean
public MybatisPlusMetaObjectHandler mybatisPlusMetaObjectHandler() {
return new MybatisPlusMetaObjectHandler();
}
}

View File

@@ -0,0 +1,75 @@
package com.fastbee.framework.mybatis.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.util.ClassUtils;
import java.nio.charset.Charset;
import java.util.Date;
/**
* MybatisPlus 自动填充配置
*
* @author admin
*/
@Slf4j
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.debug("mybatisPlus插入填充--------------");
Date date = new Date();
fillValIfNullByName("createTime", date, metaObject, false);
fillValIfNullByName("updateTime", date, metaObject, false);
fillValIfNullByName("createBy", getUserName(), metaObject, false);
fillValIfNullByName("updateBy", getUserName(), metaObject, false);
fillValIfNullByName("deleteFlag", "1", metaObject, false);
}
@Override
public void updateFill(MetaObject metaObject) {
log.debug("mybatisPlus更新填充--------------");
fillValIfNullByName("updateTime", new Date(), metaObject, true);
fillValIfNullByName("updateBy", getUserName(), metaObject, true);
}
/**
* 填充值先判断是否有手动设置优先手动设置的值例如job必须手动设置
* @param fieldName 属性名
* @param fieldVal 属性值
* @param metaObject MetaObject
* @param isCover 是否覆盖原有值,避免更新操作手动入参
*/
private static void fillValIfNullByName(String fieldName, Object fieldVal, MetaObject metaObject, boolean isCover) {
// 1. 没有 get 方法
if (!metaObject.hasSetter(fieldName)) {
return;
}
// 2. 如果用户有手动设置的值
Object userSetValue = metaObject.getValue(fieldName);
String setValueStr = StrUtil.str(userSetValue, Charset.defaultCharset());
if (StrUtil.isNotBlank(setValueStr) && !isCover) {
return;
}
// 3. field 类型相同时设置
Class<?> getterType = metaObject.getGetterType(fieldName);
if (ClassUtils.isAssignableValue(getterType, fieldVal)) {
metaObject.setValue(fieldName, fieldVal);
}
}
/**
* 获取 spring security 当前的用户名
* @return 当前用户名
*/
private String getUserName() {
LoginUser loginUser = SecurityUtils.getLoginUser();
return loginUser.getUsername();
}
}

View File

@@ -0,0 +1,164 @@
package com.fastbee.framework.mybatis.helper;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.mybatis.enums.DataBaseType;
import com.fastbee.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 数据库助手
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DataBaseHelper {
private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);
public static final String DEFAULT_DATASOURCE_NAME = "master";
/**
* 获取当前数据库类型
*/
public static DataBaseType getDataBaseType(String dataName) {
DataSource dataSource = DS.getDataSources().get(dataName);
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new ServiceException(e.getMessage());
}
}
public static boolean isMySql() {
return DataBaseType.MY_SQL == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isOracle() {
return DataBaseType.ORACLE == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isPostgerSql() {
return DataBaseType.POSTGRE_SQL == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isSqlServer() {
return DataBaseType.SQL_SERVER == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isDm() {
return DataBaseType.DM == getDataBaseType(DEFAULT_DATASOURCE_NAME);
}
public static boolean isMySql(String dataName) {
return DataBaseType.MY_SQL == getDataBaseType(dataName);
}
public static boolean isOracle(String dataName) {
return DataBaseType.ORACLE == getDataBaseType(dataName);
}
public static boolean isPostgerSql(String dataName) {
return DataBaseType.POSTGRE_SQL == getDataBaseType(dataName);
}
public static boolean isSqlServer(String dataName) {
return DataBaseType.SQL_SERVER == getDataBaseType(dataName);
}
public static boolean isDm(String dataName) {
return DataBaseType.DM == getDataBaseType(dataName);
}
public static String findInSet(Object var1, String var2) {
DataBaseType dataBasyType = getDataBaseType(DEFAULT_DATASOURCE_NAME);
String var = Convert.toStr(var1);
if (dataBasyType == DataBaseType.SQL_SERVER) {
// charindex(',100,' , ',0,100,101,') <> 0
return String.format("charindex(',%s,' , ','+%s+',') <> 0", var, var2);
} else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
// (select strpos(',0,100,101,' , ',100,')) <> 0
return String.format("(select strpos(','||%s||',' , ',%s,')) <> 0", var2, var);
} else if (dataBasyType == DataBaseType.ORACLE || dataBasyType == DataBaseType.DM) {
// instr(',0,100,101,' , ',100,') <> 0
return String.format("instr(','||%s||',' , ',%s,') <> 0", var2, var);
}
// find_in_set(100 , '0,100,101')
return String.format("find_in_set(%s , %s) <> 0", var, var2);
}
public static String findInSetColumn(String var1, String var2) {
DataBaseType dataBasyType = getDataBaseType(DEFAULT_DATASOURCE_NAME);
String var = Convert.toStr(var1);
if (dataBasyType == DataBaseType.SQL_SERVER) {
// charindex(','+de.dept_id+',' , ',0,100,101,') <> 0
return String.format("charindex(',' + %s + ',' , ',' + %s + ',') <> 0", var, var2);
} else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
// (select strpos(',0,100,101,' , ',' || de.dept_id || ',')) <> 0
return String.format("(select strpos(','||%s||',' , ','|| %s ||',')) <> 0", var2, var);
} else if (dataBasyType == DataBaseType.ORACLE|| dataBasyType == DataBaseType.DM) {
// instr(',0,100,101,' , ','||de.dept_id||',') <> 0
return String.format("instr(','||%s||',' , ','||%s||',') <> 0", var2, var);
}
// find_in_set(de.dept_id , '0,100,101')
return String.format("find_in_set(%s , '%s') <> 0", var, var2);
}
/**
* 获取当前加载的数据库名
*/
public static List<String> getDataSourceNameList() {
return new ArrayList<>(DS.getDataSources().keySet());
}
public static String getDeptCondition(Long deptId) {
if (deptId == null || deptId == 0) {
// 无效条件,确保查询不会返回结果
return "1=1";
}
if (isPostgerSql()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE " + deptId + "::text = ANY(string_to_array(ancestors, ',')) OR dept_id = " + deptId + ")";
} else if (isSqlServer()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE CHARINDEX(',' + CAST(" + deptId + " AS VARCHAR) + ',', ',' + ancestors + ',') > 0 OR dept_id = " + deptId + ")";
} else if (isOracle()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE INSTR(',' || ancestors || ',', ',' || " + deptId + " || ',') > 0 OR dept_id = " + deptId + ")";
} else if (isDm()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE INSTR(',' || ancestors || ',', ',' || " + deptId + " || ',') > 0 OR dept_id = " + deptId + ")";
} else if (isMySql()) {
return "SELECT u.user_id FROM sys_user u WHERE u.dept_id IN (SELECT dept_id FROM sys_dept WHERE FIND_IN_SET(" + deptId + ", ancestors) > 0 OR dept_id = " + deptId + ")";
} else {
throw new UnsupportedOperationException("Unsupported database type");
}
}
public static String checkTime(Integer timeout) {
if (timeout == null || timeout == 0) {
// 无效条件,确保查询不会返回结果
return "";
}
if (isPostgerSql()) {
return "CURRENT_TIMESTAMP > last_connect_time + interval '1 seconds' * " + timeout;
} else if (isSqlServer()) {
return "CURRENT_TIMESTAMP > DATEADD(SECOND, " + timeout + " last_connect_time)";
} else if (isOracle()) {
return "CURRENT_TIMESTAMP > last_connect_time + (" + timeout + " / 86400)";
} else if (isDm()) {
return "CURRENT_TIMESTAMP > DATEADD(SECOND, " + timeout + ", last_connect_time)";
} else if (isMySql()) {
return "CURRENT_TIMESTAMP > DATE_ADD(last_connect_time, INTERVAL " + timeout + " SECOND)";
} else {
throw new UnsupportedOperationException("Unsupported database type");
}
}
}

View File

@@ -4,6 +4,8 @@ import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fastbee.common.utils.MessageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
@@ -20,7 +22,7 @@ import com.fastbee.framework.web.service.TokenService;
/**
* 自定义退出处理类 返回成功
*
*
* @author ruoyi
*/
@Configuration
@@ -31,7 +33,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
/**
* 退出处理
*
*
* @return
*/
@Override
@@ -45,8 +47,8 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功")));
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success"))));
}
}

View File

@@ -7,17 +7,21 @@ import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.text.Convert;
import com.fastbee.common.exception.DemoModeException;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.html.EscapeUtil;
/**
* 全局异常处理器
*
*
* @author ruoyi
*/
@RestControllerAdvice
@@ -59,6 +63,33 @@ public class GlobalExceptionHandler
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}
/**
* 请求路径中缺少必需的路径变量
*/
@ExceptionHandler(MissingPathVariableException.class)
public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
}
/**
* 请求参数类型不匹配
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
String value = Convert.toStr(e.getValue());
if (StringUtils.isNotEmpty(value))
{
value = EscapeUtil.clean(value);
}
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value));
}
/**
* 拦截未知的运行时异常
*/

View File

@@ -3,6 +3,7 @@ package com.fastbee.framework.web.service;
import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
@@ -11,25 +12,15 @@ import com.fastbee.framework.security.context.PermissionContextHolder;
/**
* RuoYi首创 自定义权限实现ss取自SpringSecurity首字母
*
*
* @author ruoyi
*/
@Service("ss")
public class PermissionService
{
/** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin";
private static final String ROLE_DELIMETER = ",";
private static final String PERMISSION_DELIMETER = ",";
/**
* 验证用户是否具备某权限
*
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
@@ -78,7 +69,7 @@ public class PermissionService
}
PermissionContextHolder.setContext(permissions);
Set<String> authorities = loginUser.getPermissions();
for (String permission : permissions.split(PERMISSION_DELIMETER))
for (String permission : permissions.split(Constants.PERMISSION_DELIMETER))
{
if (permission != null && hasPermissions(authorities, permission))
{
@@ -90,7 +81,7 @@ public class PermissionService
/**
* 判断用户是否拥有某个角色
*
*
* @param role 角色字符串
* @return 用户是否具备某角色
*/
@@ -108,7 +99,7 @@ public class PermissionService
for (SysRole sysRole : loginUser.getUser().getRoles())
{
String roleKey = sysRole.getRoleKey();
if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
{
return true;
}
@@ -144,7 +135,7 @@ public class PermissionService
{
return false;
}
for (String role : roles.split(ROLE_DELIMETER))
for (String role : roles.split(Constants.ROLE_DELIMETER))
{
if (hasRole(role))
{
@@ -156,13 +147,13 @@ public class PermissionService
/**
* 判断是否包含权限
*
*
* @param permissions 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
private boolean hasPermissions(Set<String> permissions, String permission)
{
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
}

View File

@@ -2,17 +2,15 @@ package com.fastbee.framework.web.service;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.constant.UserConstants;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.enums.UserStatus;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.exception.user.CaptchaException;
import com.fastbee.common.exception.user.CaptchaExpireException;
import com.fastbee.common.exception.user.UserPasswordNotMatchException;
import com.fastbee.common.exception.user.*;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.MessageUtils;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.framework.manager.AsyncManager;
@@ -70,12 +68,10 @@ public class SysLoginService
*/
public String login(String username, String password, String code, String uuid)
{
boolean captchaEnabled = configService.selectCaptchaEnabled();
// 验证码开关
if (captchaEnabled)
{
validateCaptcha(username, code, uuid);
}
// 验证码校验
validateCaptcha(username, code, uuid);
// 登录前置校验
loginPreCheck(username, password);
// 用户验证
Authentication authentication = null;
try
@@ -186,18 +182,58 @@ public class SysLoginService
*/
public void validateCaptcha(String username, String code, String uuid)
{
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)
boolean captchaEnabled = configService.selectCaptchaEnabled();
if (captchaEnabled)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
if (captcha == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException();
}
redisCache.deleteObject(verifyKey);
if (!code.equalsIgnoreCase(captcha))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
}
if (!code.equalsIgnoreCase(captcha))
}
/**
* 登录前置校验
* @param username 用户名
* @param password 用户密码
*/
public void loginPreCheck(String username, String password)
{
// 用户名或密码为空 错误
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
throw new UserNotExistsException();
}
// 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// IP黑名单校验
String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
throw new BlackListException();
}
}
@@ -210,7 +246,7 @@ public class SysLoginService
{
SysUser sysUser = new SysUser();
sysUser.setUserId(userId);
sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
sysUser.setLoginIp(IpUtils.getIpAddr());
sysUser.setLoginDate(DateUtils.getNowDate());
userService.updateUserProfile(sysUser);
}

View File

@@ -5,14 +5,17 @@ import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.fastbee.common.constant.UserConstants;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.system.service.ISysMenuService;
import com.fastbee.system.service.ISysRoleService;
/**
* 用户权限处理
*
*
* @author ruoyi
*/
@Component
@@ -26,7 +29,7 @@ public class SysPermissionService
/**
* 获取角色数据权限
*
*
* @param user 用户信息
* @return 角色权限信息
*/
@@ -47,7 +50,7 @@ public class SysPermissionService
/**
* 获取菜单数据权限
*
*
* @param user 用户信息
* @return 菜单权限信息
*/
@@ -62,14 +65,17 @@ public class SysPermissionService
else
{
List<SysRole> roles = user.getRoles();
if (!roles.isEmpty() && roles.size() > 1)
if (!CollectionUtils.isEmpty(roles))
{
// 多角色设置permissions属性以便数据权限匹配权限
for (SysRole role : roles)
{
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
role.setPermissions(rolePerms);
perms.addAll(rolePerms);
if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin())
{
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
role.setPermissions(rolePerms);
perms.addAll(rolePerms);
}
}
}
else

View File

@@ -1,7 +1,5 @@
package com.fastbee.framework.web.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.constant.UserConstants;
@@ -17,10 +15,12 @@ import com.fastbee.framework.manager.AsyncManager;
import com.fastbee.framework.manager.factory.AsyncFactory;
import com.fastbee.system.service.ISysConfigService;
import com.fastbee.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 注册校验方法
*
*
* @author ruoyi
*/
@Component
@@ -69,7 +69,7 @@ public class SysRegisterService
{
msg = "密码长度必须在5到20个字符之间";
}
else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser)))
else if (!userService.checkUserNameUnique(sysUser))
{
msg = "保存用户'" + username + "'失败,注册账号已存在";
}
@@ -92,7 +92,7 @@ public class SysRegisterService
/**
* 校验验证码
*
*
* @param username 用户名
* @param code 验证码
* @param uuid 唯一标识

View File

@@ -2,6 +2,7 @@ package com.fastbee.framework.web.service;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.utils.ServletUtils;
@@ -9,27 +10,33 @@ import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.ip.AddressUtils;
import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.common.utils.uuid.IdUtils;
import com.fastbee.system.domain.SysClient;
import com.fastbee.system.service.ISysClientService;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* token验证处理
*
* @author ruoyi
*/
@Slf4j
@Component
public class TokenService
{
public class TokenService {
// 令牌自定义标识
@Value("${token.header}")
private String header;
@@ -46,39 +53,42 @@ public class TokenService
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
private static final Long MILLIS_MINUTE_TWENTY = 20 * 60 * 1000L;
@Autowired
private RedisCache redisCache;
@Autowired
private ISysClientService sysClientService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request)
{
public LoginUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token))
{
try
{
if (StringUtils.isNotEmpty(token)) {
try {
Claims claims = parseToken(token);
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser user = redisCache.getCacheObject(userKey);
user.setRequestToken(token);
return user;
}
catch (Exception e)
{
} catch (Exception e) {
log.info("获取缓存报错:{}", e.getMessage());
}
}
return null;
}
/**
/**
* 获取用户身份信息
*
* @return 用户信息
@@ -93,6 +103,7 @@ public class TokenService
LoginUser user = redisCache.getCacheObject(userKey);
return user;
} catch (Exception e) {
log.info("获取缓存报错:{}", e.getMessage());
}
}
return null;
@@ -101,6 +112,7 @@ public class TokenService
/**
* 根据用户id获取用户身份信息
* 由于多端登录根据token获取的用户信息不一样所以增加一个根据用户id获取用户信息的缓存key以后多端需要获取用户最新信息就用这个方法吧
*
* @return 用户信息
*/
public LoginUser getLoginUserByUserId(Long userId) {
@@ -109,6 +121,7 @@ public class TokenService
String userKey = getUserIdKey(userId);
return redisCache.getCacheObject(userKey);
} catch (Exception e) {
log.info("获取缓存报错:{}", e.getMessage());
}
}
return null;
@@ -117,10 +130,8 @@ public class TokenService
/**
* 设置用户身份信息
*/
public void setLoginUser(LoginUser loginUser)
{
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
{
public void setLoginUser(LoginUser loginUser) {
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
refreshToken(loginUser);
}
}
@@ -128,10 +139,8 @@ public class TokenService
/**
* 删除用户身份信息
*/
public void delLoginUser(String token)
{
if (StringUtils.isNotEmpty(token))
{
public void delLoginUser(String token) {
if (StringUtils.isNotEmpty(token)) {
String userKey = getTokenKey(token);
redisCache.deleteObject(userKey);
}
@@ -143,8 +152,7 @@ public class TokenService
* @param loginUser 用户信息
* @return 令牌
*/
public String createToken(LoginUser loginUser)
{
public String createToken(LoginUser loginUser) {
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser);
@@ -152,21 +160,65 @@ public class TokenService
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
claims.put(Constants.JWT_USERNAME, loginUser.getUsername());
return createToken(claims);
}
public int addToken(SysUser user, SysClient sysClient) {
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(user);
LoginUser loginUser = (LoginUser) userDetails;
sysClient.setToken(Constants.TOKEN_PREFIX + createToken(loginUser, sysClient.getClientSecret(), Math.toIntExact(sysClient.getTimeout())));
return sysClientService.insertSysClient(sysClient);
}
public int updateToken(SysUser loginuser, SysClient sysClient) {
LoginUser user;
Claims claims = parseToken(sysClient);
if (claims == null) {
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(loginuser);
user = (LoginUser) userDetails;
sysClient.setToken(Constants.TOKEN_PREFIX + createToken(user, sysClient.getClientSecret(), Math.toIntExact(sysClient.getTimeout())));
} else {
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
user = redisCache.getCacheObject(userKey);
if (user == null) {
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(loginuser);
user = (LoginUser) userDetails;
}
log.debug("loginUser:{}", user);
if (sysClient.getEnable() != null && "1".equals(sysClient.getEnable())) {
delLoginUser(uuid);
sysClient.setToken(Constants.TOKEN_PREFIX + createToken(user, sysClient.getClientSecret(), Math.toIntExact(sysClient.getTimeout())));
} else if ("0".equals(sysClient.getEnable())) {
delLoginUser(uuid);
}
}
return sysClientService.updateSysClient(sysClient);
}
public String createToken(LoginUser loginUser, String secret, int expireTime) {
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser);
refreshToken(loginUser, expireTime);
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims, secret);
}
/**
* 验证令牌有效期相差不足20分钟自动刷新缓存
*
* @param loginUser
* @return 令牌
*/
public void verifyToken(LoginUser loginUser)
{
public void verifyToken(LoginUser loginUser) {
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
{
if (expireTime - currentTime <= MILLIS_MINUTE_TWENTY) {
refreshToken(loginUser);
}
}
@@ -176,8 +228,7 @@ public class TokenService
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser)
{
public void refreshToken(LoginUser loginUser, int expireTime) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
@@ -188,13 +239,29 @@ public class TokenService
redisCache.setCacheObject(userIdKey, loginUser, expireTime, TimeUnit.MINUTES);
}
public void refreshToken(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
String userIdKey = getUserIdKey(loginUser.getUserId());
if (Boolean.TRUE.equals(loginUser.getNeverExpire())) {
redisCache.setCacheObject(userKey, loginUser);
// 使用token作为用户信息缓存key多端不能同步最新信息需要重新登录因此添加一个使用用户id作为缓存key
redisCache.setCacheObject(userIdKey, loginUser);
} else {
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
// 使用token作为用户信息缓存key多端不能同步最新信息需要重新登录因此添加一个使用用户id作为缓存key
redisCache.setCacheObject(userIdKey, loginUser, expireTime, TimeUnit.MINUTES);
}
}
/**
* 设置用户代理信息
*
* @param loginUser 登录信息
*/
public void setUserAgent(LoginUser loginUser)
{
public void setUserAgent(LoginUser loginUser) {
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
loginUser.setIpaddr(ip);
@@ -209,8 +276,14 @@ public class TokenService
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map<String, Object> claims)
{
private String createToken(Map<String, Object> claims) {
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
private String createToken(Map<String, Object> claims, String secret) {
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
@@ -223,12 +296,23 @@ public class TokenService
* @param token 令牌
* @return 数据声明
*/
private Claims parseToken(String token)
{
private Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
private Claims parseToken(SysClient sysClient) {
if (sysClient.getClientSecret() != null && !Objects.equals(sysClient.getToken(), "")) {
return Jwts.parser()
.setSigningKey(sysClient.getClientSecret())
.parseClaimsJws(sysClient.getToken().substring(Constants.TOKEN_PREFIX.length()))
.getBody();
} else {
return null;
}
}
/**
@@ -237,8 +321,7 @@ public class TokenService
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token)
{
public String getUsernameFromToken(String token) {
Claims claims = parseToken(token);
return claims.getSubject();
}
@@ -249,18 +332,15 @@ public class TokenService
* @param request
* @return token
*/
private String getToken(HttpServletRequest request)
{
private String getToken(HttpServletRequest request) {
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
{
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
token = token.replace(Constants.TOKEN_PREFIX, "");
}
return token;
}
private String getTokenKey(String uuid)
{
private String getTokenKey(String uuid) {
return CacheConstants.LOGIN_TOKEN_KEY + uuid;
}

View File

@@ -11,6 +11,7 @@ import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.enums.UserStatus;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.MessageUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.system.service.ISysUserService;
@@ -26,7 +27,7 @@ public class UserDetailsServiceImpl implements UserDetailsService
@Autowired
private ISysUserService userService;
@Autowired
private SysPasswordService passwordService;
@@ -40,17 +41,17 @@ public class UserDetailsServiceImpl implements UserDetailsService
if (StringUtils.isNull(user))
{
log.info("登录用户:{} 不存在.", username);
throw new ServiceException("登录用户:" + username + " 不存在");
throw new ServiceException(MessageUtils.message("user.not.exists"));
}
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
log.info("登录用户:{} 已被删除.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
throw new ServiceException(MessageUtils.message("user.password.delete"));
}
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
log.info("登录用户:{} 已被停用.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
throw new ServiceException(MessageUtils.message("user.blocked"));
}
passwordService.validate(user);

View File

@@ -2,6 +2,7 @@ package com.fastbee.data.service.impl;
import com.fastbee.base.session.Session;
import com.fastbee.common.enums.DeviceStatus;
import com.fastbee.framework.mybatis.helper.DataBaseHelper;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.model.DeviceStatusVO;
import com.fastbee.iot.service.IDeviceService;
@@ -42,7 +43,8 @@ public class DeviceJob {
private Boolean enabled;
public void updateSipDeviceOnlineStatus(Integer timeout) {
List<SipDevice> devs = sipDeviceMapper.selectOfflineSipDevice(timeout);
String checkTimeCondition = DataBaseHelper.checkTime(timeout);
List<SipDevice> devs = sipDeviceMapper.selectOfflineSipDevice(checkTimeCondition);
devs.forEach(item -> {
if (!Objects.equals(item.getDeviceSipId(), "")) {
//更新iot设备状态

View File

@@ -33,6 +33,48 @@
<groupId>com.fastbee</groupId>
<artifactId>fastbee-common</artifactId>
</dependency>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-framework</artifactId>
</dependency>
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-environment-spring-data-jdbc</artifactId>
<version>${anyline.version}</version>
</dependency>
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-data-jdbc-mysql</artifactId>
<version>${anyline.version}</version>
</dependency>
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-data-jdbc-dm</artifactId>
<version>${anyline.version}</version>
</dependency>
<!-- anyline支持100+种类型数据库 添加对应的jdbc依赖与anyline对应数据库依赖包即可 -->
<!-- <dependency>-->
<!-- <groupId>org.anyline</groupId>-->
<!-- <artifactId>anyline-data-jdbc-oracle</artifactId>-->
<!-- <version>${anyline.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.anyline</groupId>-->
<!-- <artifactId>anyline-data-jdbc-postgresql</artifactId>-->
<!-- <version>${anyline.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.anyline</groupId>-->
<!-- <artifactId>anyline-data-jdbc-mssql</artifactId>-->
<!-- <version>${anyline.version}</version>-->
<!-- </dependency>-->
</dependencies>

View File

@@ -6,6 +6,9 @@ import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import com.fastbee.common.core.page.PageDomain;
import com.fastbee.common.core.page.TableSupport;
import com.fastbee.framework.mybatis.helper.DataBaseHelper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
@@ -86,9 +89,8 @@ public class GenController extends BaseController
@GetMapping("/db/list")
public TableDataInfo dataList(GenTable genTable)
{
startPage();
List<GenTable> list = genTableService.selectDbTableList(genTable);
return getDataTable(list);
PageDomain pageDomain = TableSupport.buildPageRequest();
return genTableService.selectDbTableList(genTable,pageDomain);
}
/**
@@ -113,12 +115,12 @@ public class GenController extends BaseController
@PreAuthorize("@ss.hasPermi('tool:gen:import')")
@Log(title = "代码生成", businessType = BusinessType.IMPORT)
@PostMapping("/importTable")
public AjaxResult importTableSave(String tables)
public AjaxResult importTableSave(String tables,String dataName)
{
String[] tableNames = Convert.toStrArray(tables);
// 查询表信息
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
genTableService.importGenTable(tableList);
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames,dataName);
genTableService.importGenTable(tableList,dataName);
return success();
}
@@ -227,4 +229,13 @@ public class GenController extends BaseController
response.setContentType("application/octet-stream; charset=UTF-8");
IOUtils.write(data, response.getOutputStream());
}
/**
* 查询数据源名称列表
*/
@PreAuthorize("@ss.hasPermi('tool:gen:list')")
@GetMapping(value = "/getDataNames")
public AjaxResult getCurrentDataSourceNameList() {
return success(DataBaseHelper.getDataSourceNameList());
}
}

View File

@@ -4,6 +4,9 @@ import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.ArrayUtils;
@@ -17,14 +20,24 @@ import com.fastbee.common.utils.StringUtils;
* @author ruoyi
*/
@ApiModel(value = "GenTable", description = "业务表 gen_table")
@TableName("gen_table")
public class GenTable extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 编号 */
@ApiModelProperty("编号")
@TableId(value = "table_id")
private Long tableId;
/**
* 数据源名称
*/
@ApiModelProperty("数据源名称")
@NotBlank(message = "数据源名称不能为空")
private String dataName;
/** 表名称 */
@ApiModelProperty(value = "表名称", required = true)
@NotBlank(message = "表名称不能为空")
@@ -122,6 +135,20 @@ public class GenTable extends BaseEntity
@ApiModelProperty("上级菜单名称字段")
private String parentMenuName;
/*
* 菜单id列表
*/
@TableField(exist = false)
private List<Long> menuIds;
public List<Long> getMenuIds() {
return menuIds;
}
public void setMenuIds(List<Long> menuIds) {
this.menuIds = menuIds;
}
public Long getTableId()
{
return tableId;
@@ -396,4 +423,12 @@ public class GenTable extends BaseEntity
}
return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
}
public String getDataName() {
return dataName;
}
public void setDataName(String dataName) {
this.dataName = dataName;
}
}

View File

@@ -1,6 +1,8 @@
package com.fastbee.generator.mapper;
import java.util.List;
import com.fastbee.common.mybatis.mapper.BaseMapperX;
import com.fastbee.generator.domain.GenTableColumn;
/**
@@ -8,7 +10,7 @@ import com.fastbee.generator.domain.GenTableColumn;
*
* @author ruoyi
*/
public interface GenTableColumnMapper
public interface GenTableColumnMapper extends BaseMapperX<GenTableColumn>
{
/**
* 根据表名称查询列信息
@@ -57,4 +59,6 @@ public interface GenTableColumnMapper
* @return 结果
*/
public int deleteGenTableColumnByIds(Long[] ids);
boolean batchInsert(List<GenTableColumn> list);
}

View File

@@ -1,6 +1,9 @@
package com.fastbee.generator.mapper;
import java.util.List;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.fastbee.common.mybatis.mapper.BaseMapperX;
import com.fastbee.generator.domain.GenTable;
/**
@@ -8,7 +11,7 @@ import com.fastbee.generator.domain.GenTable;
*
* @author ruoyi
*/
public interface GenTableMapper
public interface GenTableMapper extends BaseMapperX<GenTable>
{
/**
* 查询业务列表
@@ -80,4 +83,7 @@ public interface GenTableMapper
* @return 结果
*/
public int deleteGenTableByIds(Long[] ids);
@DS("")
List<String> selectTableNameList(String dataName);
}

View File

@@ -4,13 +4,24 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fastbee.common.core.page.PageDomain;
import com.fastbee.common.core.page.TableDataInfo;
import com.fastbee.common.utils.MessageUtils;
import com.fastbee.common.utils.spring.SpringUtils;
import org.anyline.metadata.Column;
import org.anyline.metadata.Table;
import org.anyline.proxy.ServiceProxy;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
@@ -37,6 +48,8 @@ import com.fastbee.generator.util.GenUtils;
import com.fastbee.generator.util.VelocityInitializer;
import com.fastbee.generator.util.VelocityUtils;
import javax.annotation.Resource;
/**
* 业务 服务层实现
*
@@ -53,6 +66,11 @@ public class GenTableServiceImpl implements IGenTableService
@Autowired
private GenTableColumnMapper genTableColumnMapper;
@Resource
private IdentifierGenerator identifierGenerator;
private static final String[] TABLE_IGNORE = new String[]{"sj_", "act_", "flw_", "gen_"};
/**
* 查询业务信息
*
@@ -85,10 +103,61 @@ public class GenTableServiceImpl implements IGenTableService
* @param genTable 业务信息
* @return 数据库表集合
*/
@DS("#genTable.dataName")
@Override
public List<GenTable> selectDbTableList(GenTable genTable)
public TableDataInfo selectDbTableList(GenTable genTable, PageDomain pageDomain)
{
return genTableMapper.selectDbTableList(genTable);
// 获取查询条件
String tableName = genTable.getTableName();
String tableComment = genTable.getTableComment();
LinkedHashMap<String, Table<?>> tablesMap = ServiceProxy.metadata().tables();
if (CollUtil.isEmpty(tablesMap)) {
return TableDataInfo.build();
}
List<String> tableNames = genTableMapper.selectTableNameList(genTable.getDataName());
String[] tableArrays;
if (CollUtil.isNotEmpty(tableNames)) {
tableArrays = tableNames.toArray(new String[0]);
} else {
tableArrays = new String[0];
}
// 过滤并转换表格数据
List<GenTable> tables = tablesMap.values().stream()
.filter(x -> !StringUtils.containsAnyIgnoreCase(x.getName(), TABLE_IGNORE))
.filter(x -> {
if (CollUtil.isEmpty(tableNames)) {
return true;
}
return !StringUtils.equalsAnyIgnoreCase(x.getName(), tableArrays);
})
.filter(x -> {
boolean nameMatches = true;
boolean commentMatches = true;
// 进行表名称的模糊查询
if (StringUtils.isNotBlank(tableName)) {
nameMatches = StringUtils.containsIgnoreCase(x.getName(), tableName);
}
// 进行表描述的模糊查询
if (StringUtils.isNotBlank(tableComment)) {
commentMatches = StringUtils.containsIgnoreCase(x.getComment(), tableComment);
}
// 同时匹配名称和描述
return nameMatches && commentMatches;
})
.map(x -> {
GenTable gen = new GenTable();
gen.setTableName(x.getName());
gen.setTableComment(x.getComment());
gen.setCreateTime(x.getCreateTime());
gen.setUpdateTime(x.getUpdateTime());
return gen;
}).collect(Collectors.toList());
IPage<GenTable> page = pageDomain.build();
page.setTotal(tables.size());
// 手动分页 set数据
page.setRecords(CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tables));
return TableDataInfo.build(page);
}
/**
@@ -97,10 +166,33 @@ public class GenTableServiceImpl implements IGenTableService
* @param tableNames 表名称组
* @return 数据库表集合
*/
@DS("#dataName")
@Override
public List<GenTable> selectDbTableListByNames(String[] tableNames)
public List<GenTable> selectDbTableListByNames(String[] tableNames,String dataName)
{
return genTableMapper.selectDbTableListByNames(tableNames);
Set<String> tableNameSet = new HashSet<>(Arrays.asList(tableNames));
LinkedHashMap<String, Table<?>> tablesMap = ServiceProxy.metadata().tables();
if (CollUtil.isEmpty(tablesMap)) {
return new ArrayList<>();
}
List<Table<?>> tableList = tablesMap.values().stream()
.filter(x -> !StringUtils.containsAnyIgnoreCase(x.getName(), TABLE_IGNORE))
.filter(x -> tableNameSet.contains(x.getName())).collect(Collectors.toList());
if (CollUtil.isEmpty(tableList)) {
return new ArrayList<>();
}
return tableList.stream().map(x -> {
GenTable gen = new GenTable();
gen.setDataName(dataName);
gen.setTableName(x.getName());
gen.setTableComment(x.getComment());
gen.setCreateTime(x.getCreateTime());
gen.setUpdateTime(x.getUpdateTime());
return gen;
}).collect(Collectors.toList());
}
/**
@@ -156,33 +248,57 @@ public class GenTableServiceImpl implements IGenTableService
* @param tableList 导入表列表
*/
@Override
@Transactional
public void importGenTable(List<GenTable> tableList)
@DSTransactional
public void importGenTable(List<GenTable> tableList, String dataName)
{
String operName = SecurityUtils.getUsername();
try
{
for (GenTable table : tableList)
{
try {
for (GenTable table : tableList) {
String tableName = table.getTableName();
GenUtils.initTable(table, operName);
int row = genTableMapper.insertGenTable(table);
if (row > 0)
{
int row = genTableMapper.insert(table);
if (row > 0) {
// 保存列信息
List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
for (GenTableColumn column : genTableColumns)
{
List<GenTableColumn> genTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(tableName, dataName);
List<GenTableColumn> saveColumns = new ArrayList<>();
for (GenTableColumn column : genTableColumns) {
GenUtils.initColumnField(column, table);
genTableColumnMapper.insertGenTableColumn(column);
saveColumns.add(column);
}
if (CollUtil.isNotEmpty(saveColumns)) {
genTableColumnMapper.batchInsert(saveColumns);
}
}
}
} catch (Exception e) {
throw new ServiceException(StringUtils.format(MessageUtils.message("import.fail.[{}]"), e.getMessage()));
}
catch (Exception e)
{
throw new ServiceException("导入失败:" + e.getMessage());
}
}
/**
* 根据表名称查询列信息
*
* @param tableName 表名称
* @param dataName 数据源名称
* @return 列信息
*/
@DS("#dataName")
@Override
public List<GenTableColumn> selectDbTableColumnsByName(String tableName, String dataName) {
LinkedHashMap<String, Column> columns = ServiceProxy.metadata().columns(tableName);
List<GenTableColumn> tableColumns = new ArrayList<>();
columns.forEach((columnName, column) -> {
GenTableColumn tableColumn = new GenTableColumn();
tableColumn.setIsPk(String.valueOf(column.isPrimaryKey()));
tableColumn.setColumnName(column.getName());
tableColumn.setColumnComment(column.getComment());
tableColumn.setColumnType(column.getTypeName().toLowerCase());
tableColumn.setSort(column.getPosition());
tableColumn.setIsRequired(column.isNullable() == 0 ? "1" : "0");
tableColumn.setIsIncrement(column.isAutoIncrement() == -1 ? "0" : "1");
tableColumns.add(tableColumn);
});
return tableColumns;
}
/**
@@ -197,6 +313,12 @@ public class GenTableServiceImpl implements IGenTableService
Map<String, String> dataMap = new LinkedHashMap<>();
// 查询表信息
GenTable table = genTableMapper.selectGenTableById(tableId);
List<Long> menuIds = new ArrayList<>();
for (int i = 0; i < 6; i++) {
menuIds.add(identifierGenerator.nextId(null).longValue());
}
table.setMenuIds(menuIds);
// 设置主子表信息
setSubTable(table);
// 设置主键列信息
@@ -206,7 +328,7 @@ public class GenTableServiceImpl implements IGenTableService
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(),table.getDataName());
for (String template : templates)
{
// 渲染模板
@@ -254,7 +376,7 @@ public class GenTableServiceImpl implements IGenTableService
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(),table.getDataName());
for (String template : templates)
{
if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm"))
@@ -367,7 +489,7 @@ public class GenTableServiceImpl implements IGenTableService
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(),table.getDataName());
for (String template : templates)
{
// 渲染模板

View File

@@ -2,7 +2,11 @@ package com.fastbee.generator.service;
import java.util.List;
import java.util.Map;
import com.fastbee.common.core.page.PageDomain;
import com.fastbee.common.core.page.TableDataInfo;
import com.fastbee.generator.domain.GenTable;
import com.fastbee.generator.domain.GenTableColumn;
/**
* 业务 服务层
@@ -25,7 +29,7 @@ public interface IGenTableService
* @param genTable 业务信息
* @return 数据库表集合
*/
public List<GenTable> selectDbTableList(GenTable genTable);
public TableDataInfo selectDbTableList(GenTable genTable, PageDomain page);
/**
* 查询据库列表
@@ -33,7 +37,7 @@ public interface IGenTableService
* @param tableNames 表名称组
* @return 数据库表集合
*/
public List<GenTable> selectDbTableListByNames(String[] tableNames);
public List<GenTable> selectDbTableListByNames(String[] tableNames,String dataName);
/**
* 查询所有表信息
@@ -71,7 +75,16 @@ public interface IGenTableService
*
* @param tableList 导入表列表
*/
public void importGenTable(List<GenTable> tableList);
public void importGenTable(List<GenTable> tableList,String dataName);
/**
* 根据表名称查询列信息
*
* @param tableName 表名称
* @param dataName 数据源名称
* @return 列信息
*/
List<GenTableColumn> selectDbTableColumnsByName(String tableName, String dataName);
/**
* 预览代码

View File

@@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.fastbee.framework.mybatis.helper.DataBaseHelper;
import org.apache.velocity.VelocityContext;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@@ -126,7 +128,7 @@ public class VelocityUtils
*
* @return 模板列表
*/
public static List<String> getTemplateList(String tplCategory)
public static List<String> getTemplateList(String tplCategory,String dataName)
{
List<String> templates = new ArrayList<String>();
templates.add("vm/java/domain.java.vm");
@@ -135,7 +137,17 @@ public class VelocityUtils
templates.add("vm/java/serviceImpl.java.vm");
templates.add("vm/java/controller.java.vm");
templates.add("vm/xml/mapper.xml.vm");
templates.add("vm/sql/sql.vm");
if (DataBaseHelper.isOracle(dataName)) {
templates.add("vm/sql/oracle/sql.vm");
} else if (DataBaseHelper.isPostgerSql(dataName)) {
templates.add("vm/sql/postgres/sql.vm");
} else if (DataBaseHelper.isSqlServer(dataName)) {
templates.add("vm/sql/sqlserver/sql.vm");
} else if (DataBaseHelper.isDm(dataName)) {
templates.add("vm/sql/dameng/sql.vm");
} else {
templates.add("vm/sql/sql.vm");
}
templates.add("vm/js/api.js.vm");
if (GenConstants.TPL_CRUD.equals(tplCategory))
{

View File

@@ -124,4 +124,34 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</delete>
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="columnId">
insert into gen_table_column (
table_id,column_name,column_comment,column_type,java_type,java_field,is_pk,is_increment,is_required,
is_insert,is_edit,is_list,is_query,query_type,html_type,dict_type,sort,create_by,create_time
)values
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.tableId},
#{item.columnName},
#{item.columnComment},
#{item.columnType},
#{item.javaType},
#{item.javaField},
COALESCE(#{item.isPk}, '1'),
COALESCE(#{item.isIncrement}, '1'),
COALESCE(#{item.isRequired}, '1'),
COALESCE(#{item.isInsert}, '1'),
COALESCE(#{item.isEdit}, '1'),
COALESCE(#{item.isList}, '1'),
COALESCE(#{item.isQuery}, '1'),
COALESCE(#{item.queryType}, 'EQ'),
COALESCE(#{item.htmlType}, 'input'),
COALESCE(#{item.dictType}, 'default_dict_type'),
COALESCE(#{item.sort}, 0),
COALESCE(#{item.createBy}, 'admin'),
CURRENT_TIMESTAMP
)
</foreach>
</insert>
</mapper>

View File

@@ -6,6 +6,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<resultMap type="GenTable" id="GenTableResult">
<id property="tableId" column="table_id" />
<result property="dataName" column="data_name" />
<result property="tableName" column="table_name" />
<result property="tableComment" column="table_comment" />
<result property="subTableName" column="sub_table_name" />
@@ -54,7 +55,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectGenTableVo">
select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
select table_id, data_name, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
</sql>
<select id="selectGenTableList" parameterType="GenTable" resultMap="GenTableResult">
@@ -111,7 +112,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
SELECT t.table_id, t.data_name, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
@@ -119,7 +120,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
SELECT t.table_id, t.data_name, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
@@ -127,15 +128,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
SELECT t.table_id, t.data_name, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
FROM gen_table t
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
order by c.sort
</select>
<insert id="insertGenTable" parameterType="GenTable" useGeneratedKeys="true" keyProperty="tableId">
insert into gen_table (
<if test="dataName != null">data_name,</if>
<if test="tableName != null">table_name,</if>
<if test="tableComment != null and tableComment != ''">table_comment,</if>
<if test="className != null and className != ''">class_name,</if>
@@ -151,6 +153,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createBy != null and createBy != ''">create_by,</if>
create_time
)values(
<if test="dataName != null">#{dataName},</if>
<if test="tableName != null">#{tableName},</if>
<if test="tableComment != null and tableComment != ''">#{tableComment},</if>
<if test="className != null and className != ''">#{className},</if>
@@ -171,6 +174,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="updateGenTable" parameterType="GenTable">
update gen_table
<set>
<if test="dataName != null">data_name = #{dataName},</if>
<if test="tableName != null">table_name = #{tableName},</if>
<if test="tableComment != null and tableComment != ''">table_comment = #{tableComment},</if>
<if test="subTableName != null">sub_table_name = #{subTableName},</if>
@@ -199,4 +203,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</delete>
<select id="selectTableNameList" resultType="java.lang.String">
select table_name from gen_table where data_name = #{dataName,jdbcType=VARCHAR}
</select>
</mapper>

View File

@@ -0,0 +1,19 @@
-- 菜单 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate, null, null, '${functionName}菜单');
-- 按钮 SQL
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate, null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate, null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate, null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate, null, null, '');
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate, null, null, '');

Some files were not shown because too many files have changed in this diff Show More