1.前台页面展示
使用的技术
本项目采用前后端分离的技术实现。
后端技术:
- JDK11
- SpringBoot
- Redis
- MyBatisPlus
- JWT
- MySQL
前端技术:
- Vue2
- ElementUI
- Nodejs
创建项目
使用Spring Initializr创建SpringBoot工程,导入必要的pom依赖。
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xzj</groupId>
<artifactId>IMS</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>IMS</name>
<description>IMS</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.1-jre</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<configurationFile>src/main/resources/generatorConfiguration.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
<!--加入下面这个依赖-->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
src目录结构
各数据表结构
小技巧:在创建各表时,创建时间和更新时间都可以设置默认值CURRENT_TIMESTAMP,其中更新时间勾选上On Update(我是在Navicat定义表的),这样就不用MyBatisPlus的额外配置也能实现时间的自动创建和更新了。
-
用户表
-
部门表
-
菜单表
-
角色表
-
角色-菜单表
-
商品表
配置文件
application.yml
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ims?serverTimezone=GMT%2B8
username: root
password: 416527&&xzj123
mvc:
pathmatch:
matching-strategy: ant_path_matcher
redis:
host: 192.168.217.133 #Redis服务器连接端口
port: 6379
password: #Redis服务器连接密码(默认为空)
pool:
max-active: 8 #连接池最大连接数 使用负值表示没有限制)
max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
timeout: 30000 #连接超时时间 (毫秒)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#配置 jwt 生成 token 的secret
jwt:
auth:
secret: 123456
expired: 86400000 #毫秒
redis:
enable: false
logging:
config: classpath:log/logback.xml
实体类
位于com.xzj.model包
//Base.java
/**
* 其他表共有的属性
*/
@Data
public class Base {
private Long createUser;
private Long updateUser;
private Timestamp createTime;
@TableField("update_time")
private Timestamp editTime;
@TableField(exist = false)
private String createUserName;
@TableField(exist = false)
private String editUserName;
}
//Users.java
@Data
@TableName("user1")
public class Users extends Base{
@TableId(type = IdType.AUTO)
private Long id;
private Long roleId;
private String userName;
private String account;
private String gender;
private String password;
private Long deptId;
@TableField(value = "phone")
private String userMobile;
@TableField(exist = false)
private String roleName;
@TableField(exist = false)
private String deptName;
}
//Dept.java
@Data
@TableName("dept")
public class Dept extends Base{
@TableId(type = IdType.AUTO)
private Long id;
private String deptName;
@TableField(value = "dept_code")
private String deptNo;
}
//Role.java
@Data
public class Role extends Base{
@TableId(type = IdType.AUTO)
private Long id;
private String roleName;
@TableField(value = "role_code")
private String roleNo;
}
//Menu.java
@Data
@TableName("menu")
public class Menu {
@TableId(type = IdType.AUTO)
private Long id;
private String icon;
private String menuName;
@TableField(value = "has_Third")
private String hasThird;
private String url;
private Long pid;
private Integer orderValue;
@TableField(exist = false)
private List<Menu> menus;
}
//RoleMenu.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("role_menu")
public class RoleMenu {
@TableId(type = IdType.AUTO)
private Long id;
private Long menuId;
private Long roleId;
}
//Goods.java
@Data
@TableName("goods")
public class Goods extends Base{
@TableId(type = IdType.AUTO)
private Long id;
private String goodsName;
@TableField("goods_code")
private String goodsNo;
private Double price;
private Integer inventory;
private String stemPlace;
}
工具类
位于com.xzj.utils包
JsonUtil.java
public class JsonUtil {
public static ObjectMapper mapper = new ObjectMapper();
/**
* 对象转换为Json
* @param obj 对象
* @return Json串
*/
public static String getJsonString(Object obj){
String s = null;
try {
s = mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return s;
}
/**
* Json串转换为对象
* @param jsonString json串
* @param resultCls 结果类型字节码
* @param <T> 泛型T
* @return 对象
*/
public static <T> T getModel(String jsonString,Class<T> resultCls){
T t = null;
try {
t = mapper.readValue(jsonString, resultCls);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return t;
}
}
JwtUtil.java
public class JwtUtil {
//Base64负载,包括用户的相关信息
public static String USER_ID = "userId";
public static String USER_NAME = "userName";
public static String USER_ROLES = "roles";
/**
* 使用jwt生成token,Signature = HMAC256(Base64(头部)+"."+Base64(负载),secretKey)
* @param authInfo
* @param expireDate
* @param secret
* @return
*/
public static String getToken(AuthInfo authInfo,Date expireDate,String secret){
Preconditions.checkArgument(authInfo != null,"加密内容不能为空");
Preconditions.checkArgument(expireDate != null,"过期时间异常");
Preconditions.checkArgument(secret != null,"加密密码不能为null");
Map<String,Object> map = new HashMap<>();
map.put("alg","HS256");
map.put("typ","JWT");
String token;//签名
token = JWT.create()
.withClaim(USER_ID,authInfo.getUserId()) //Payload,key-value
.withClaim(USER_NAME,authInfo.getUserName())
.withClaim(USER_ROLES,authInfo.getRoleId())
.withIssuedAt(new Date())//签名时间
.withExpiresAt(expireDate)//过期时间
.sign(Algorithm.HMAC256(secret)); //签名使用的密钥secret
return token;
}
/**
* 验证token正确性并返回AuthInfo对象
* @param token
* @param secret
* @return
*/
public static AuthInfo verifyToken(String token,String secret){
/**
* 通过接收前端的token,用base64 URL解码出原文
* 从header中获取哈希签名的算法,从payload中获取有效的数据
* 再使用密钥加密一次对比原来的token,一致则通过
*/
//校验器
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build();
DecodedJWT jwt;
try {
//该函数会将token分为三部分,header | payload | signature,然后使用签名算法和密钥对前两部分再次签名,与signature对比
//相同则验签成功,否则失败
jwt = verifier.verify(token);
}catch (Exception e){
throw new ImsAuthException("凭证已过期,请重新登录");
}
AuthInfo authInfo = new AuthInfo();
authInfo.setUserId(jwt.getClaim(USER_ID).asLong());
authInfo.setUserName(jwt.getClaim(USER_NAME).asString());
authInfo.setRoleId(jwt.getClaim(USER_ROLES).asLong());
return authInfo;
}
}
RedisUtil.java
@AllArgsConstructor
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* 写入缓存
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 写入缓存设置时效时间
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime , TimeUnit timeUnit) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, timeUnit);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 批量删除对应的value
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量删除key
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0){
redisTemplate.delete(keys);
}
}
/**
* 删除对应的value
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判断缓存中是否有对应的value
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 读取缓存
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
}
使用ThreadLocal保存登录用户的信息
首先规定用户登录后需要保存哪些信息,定义AuthInfo类【com.xzj.auth包】:
@Data
public class AuthInfo {
private Long userId;
private String userName;
private Long roleId;
private Long expired; //过期时间
private String key; //密钥(对称加密)
}
再定义UserThreadLocal【com.xzj.local】:
public class UserThreadLocal {
private static final ThreadLocal<AuthInfo> threadLocal=new ThreadLocal<>();
public static void put(AuthInfo authInfo){
threadLocal.set(authInfo);
}
public static AuthInfo get(){
return threadLocal.get();
}
public static void remove(){
threadLocal.remove();
}
}
登录认证器(登录认证用)
认证器接口Authenticator.java【com.xzj.auth】
/**
* 认证器做认证使用
* @param token
* @return
*/
AuthInfo auth(String token);
认证器实现类1:普通实现
AuthticatorImpl.java
@Component //认证器注解注入
@AllArgsConstructor
@NoArgsConstructor
public class AuthenticatorImpl implements Authenticator{
@Value("${jwt.auth.secret}")
private String secret;
/**
* Bearer xxxxxx 客户端鉴权时添加的Bearer,说明鉴权类型
* @param token
* @return
*/
@Override
public AuthInfo auth(String token) {
String authToken;
int index = token.indexOf(" ");
if (index == -1){
authToken = token;
}else{
String tokenType = token.substring(0,index);
if (!"Bearer".equals(tokenType)){
throw new ImsAuthException(String.format("无法识别的token类型[%s]",token));
}else{
authToken = token.substring(index).trim();
}
}
AuthInfo authInfo = JwtUtil.verifyToken(authToken,secret);
return authInfo;
}
}
认证器实现2,Redis实现
AuthenticatorRedisImpl.java
@AllArgsConstructor
@NoArgsConstructor
public class AuthenticatorRedisImpl implements Authenticator{
private RedisUtil util;
/**
* Bearer xxxxxx 客户端鉴权时添加的Bearer,说明鉴权类型
* @param token
* @return
*/
@Override
public AuthInfo auth(String token) {
if (util == null){
return null;
}
Object obj = util.get(token);
if (obj == null){
throw new ImsAuthException("找不到token对应的用户信息");
}else{
AuthInfo authInfo = (AuthInfo)obj;
if (authInfo.getExpired() < System.currentTimeMillis()){
throw new ImsAuthException("token已过期");
}
return authInfo;
}
}
}
过滤器
过滤器用于过滤请求中未携带token的请求
AuthFilter.java
@Slf4j
@NoArgsConstructor
public class AuthFilter implements Filter {
private AuthenticatorImpl authenticator;
private AuthenticatorRedisImpl authenticatorRedis;
private Set<String> urifilter;
public AuthFilter(AuthenticatorImpl authenticator,AuthenticatorRedisImpl authenticatorRedis) {
this.authenticator = authenticator;
this.authenticatorRedis = authenticatorRedis;
}
public AuthFilter(AuthenticatorImpl authenticatorimpl) {
this.authenticator = authenticatorimpl;
}
@Override
public void init(FilterConfig filterConfig) {
//得到需要放行参数,例如登录
String initParameter = filterConfig.getInitParameter(Const.UN_FILTER_KEY);
List<String> list;
if (StringUtils.isEmpty(initParameter)){
list = Collections.emptyList();
}else{
list = Lists.newArrayList(initParameter.split(","));
list.removeIf(String::isEmpty);
}
urifilter= ImmutableSet.copyOf(list);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//得到请求的uri
String uri = request.getRequestURI();
//说明uri符合放行标准
if (urifilter.stream().anyMatch(uri::startsWith)){
//放行
filterChain.doFilter(servletRequest,servletResponse);
return;
}
//不符合放行标准,需要鉴权
try {
String header = request.getHeader("token"); //获取请求头中AUTHORIZATION值
//AUTHORIZATION为空时直接获取名为token的字段值,否则获取AUTHORIZATION字段值
String token = StringUtils.isEmpty(header) ? request.getParameter("token") : header;
if (StringUtils.isEmpty(token)) {
throw new ImsAuthException("未携带 token");
}
AuthInfo authInfo;
//拿得到token就说明验签成功
if (authenticatorRedis != null) {
authInfo = authenticatorRedis.auth(token);
}else{
authInfo = authenticator.auth(token);
}
if (authInfo == null){
throw new RuntimeException("鉴权失败");
}
//保存当前用户信息
UserThreadLocal.put(authInfo);
filterChain.doFilter(servletRequest, servletResponse);
}
catch (ImsAuthException e){
//定义响应格式
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//设置未鉴权状态码
response.setStatus(HttpStatus.UNAUTHORIZED.value());
String jsonString = JsonUtil.getJsonString(Resp.toReturn(e.getMessage(), false));
response.getOutputStream().write(jsonString.getBytes());
}catch (Exception e){
e.printStackTrace();
}
}
}
过滤器注册到Spring容器
AuthConfig.java【com.xzj.config】
@Configuration
public class AuthConfig {
@Autowired(required = false)
RedisTemplate redisTemplate;
@Value("${jwt.auth.redis.enable}")
private Boolean redisEnabled;
@Autowired
ApplicationContext context;
@Bean
public FilterRegistrationBean<AuthFilter> authFilter(){ //FilterRegistrationBean,Spring中用于注册和配置Filter的类
FilterRegistrationBean<AuthFilter> filterRegistrationBean = new FilterRegistrationBean<>();
AuthFilter authFilter;
if (redisEnabled){
authFilter = new AuthFilter(authenticatorimpl(),authenticatorRedis());
}else{
authFilter = new AuthFilter(authenticatorimpl());
}
filterRegistrationBean.setFilter(authFilter);
filterRegistrationBean.addUrlPatterns("/api/*");
/**
* 设置放行参数
*/
filterRegistrationBean.addInitParameter(Const.UN_FILTER_KEY, Joiner.on(",").join(Const.URL+"login",Const.URL+"xxx"));
filterRegistrationBean.setEnabled(true);
return filterRegistrationBean;
}
@Bean
public AuthenticatorImpl authenticatorimpl(){
String secret = context.getEnvironment().getProperty("jwt.auth.secret");
return new AuthenticatorImpl(secret);
}
@Bean
@ConditionalOnProperty(value = "jwt.auth.redis.enable",havingValue = "true") //当enable值为真时才构建这个bean
public AuthenticatorRedisImpl authenticatorRedis(){
return new AuthenticatorRedisImpl(redisUtil());
}
@Bean
@ConditionalOnProperty(value = "jwt.auth.redis.enable",havingValue = "true")
public RedisUtil redisUtil(){
return new RedisUtil(redisTemplate);
}
}
常量类:【com.xzj.constant】
public interface Const {
String URL = "/api/";
String UN_FILTER_KEY = "UN_FILTER_KEY";
}
Mapper类
位于com.xzj.mapper包
//UsersMapper
@Mapper
public interface UsersMapper extends BaseMapper<Users> {
@Select("select * from user1 where account = #{account} and password = #{password}")
UserResp login(@Param("account") String account, @Param("password") String password);
@Update("update user1 set password = '123456' where id=#{userId}")
int resetPwd(Long userId);
}
//DeptMapper
@Mapper
public interface DeptMapper extends BaseMapper<Dept> {
}
//RoleMapper
@Mapper
public interface RoleMapper extends BaseMapper<Role> {
}
//GoodsMapper
@Mapper
public interface GoodsMapper extends BaseMapper<Goods> {
}
//MenuMapper
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {
}
//RoleMenuMapper
@Mapper
public interface RoleMenuMapper extends BaseMapper<RoleMenu> {
@Select("select menu_id from role_menu where role_id = #{roleId}")
List<Long> selectByRoleId(Long roleId);
}
定义响应类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Resp {
private boolean success = true;
private String message;
public static Resp toReturn(String msg,Boolean success){
Resp resp = new Resp();
resp.setSuccess(success);
resp.setMessage(msg);
return resp;
}
}
@Data
public class UserResp {
private Long id;
private String userName;
private Long deptId;
private Long roleId;
private String phone;
private String token;
}
@Data
public class LoginResp extends Resp{
private UserResp data;
}
@Data
public class TreeResp<T> extends Resp{
private T data; //所有菜单
private List list; //用户拥有的菜单权限
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResp<T> extends Resp{
private T data;
private Long count;
}
@EqualsAndHashCode(callSuper = true)
@Data
public class ListResp<T> extends Resp{
private T data;
}
Service层
实现类(接口略):
@Service
public class UserServiceImpl extends ServiceImpl<UsersMapper,Users> implements IUserService {
@Resource
private UsersMapper mapper;
@Resource
private DeptMapper deptMapper;
@Resource
private RoleMapper roleMapper;
@Value("${jwt.auth.secret}")
private String secret;
@Value("${jwt.auth.expired}")
private Integer expired;
@Autowired(required = false)
private RedisUtil redisUtil;
@Override
public LoginResp login(String account, String password) {
LoginResp loginResp = new LoginResp();
UserResp userResp = mapper.login(account, password);
if (userResp == null){
loginResp.setMessage("账号或密码不存在");
loginResp.setSuccess(false);
return loginResp;
}
AuthInfo authInfo = new AuthInfo();
authInfo.setUserId(userResp.getId());
authInfo.setUserName(userResp.getUserName());
authInfo.setRoleId(userResp.getRoleId());
authInfo.setExpired(getExpiredTime());
Date date = new Date(getExpiredTime());
//根据authInfo生成token
String token = JwtUtil.getToken(authInfo, date, secret);
if (redisUtil != null){
redisUtil.set(token,authInfo);
}
userResp.setToken(token);
loginResp.setData(userResp);
return loginResp;
}
/**
* 根据当前时间+过期时间得到总时间戳
* @return
*/
public Long getExpiredTime(){
return new Date().getTime() + expired;
}
@Override
public PageResp<List<Users>> selectUserList(Integer page, Integer limit, String userName, String userMobile) {
QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
PageHelper.startPage(page,limit);
if(userName!=null&&!"".equals(userName)){
queryWrapper.eq("user_name",userName);
}
if (userMobile!=null && !"".equals(userMobile)){
queryWrapper.eq("phone",userMobile);
}
List<Users> list = mapper.selectList(queryWrapper);
list.forEach(li -> {
li.setRoleName(roleMapper.selectById(li.getRoleId()).getRoleName());
li.setDeptName(deptMapper.selectById(li.getDeptId()).getDeptName());
li.setCreateUserName(mapper.selectById(li.getCreateUser()).getUserName());
if(li.getUpdateUser()!=null){
li.setEditUserName(mapper.selectById(li.getUpdateUser()).getUserName());
}
});
PageInfo<Users> info = new PageInfo<>(list);
PageResp<List<Users>> resp = new PageResp<>();
resp.setCount(info.getTotal());
resp.setData(list);
return resp;
}
@Override
public Resp saveOrUpdate(Long userId, String account, String userName, String gender,String userMobile, Long roleId, Long deptId) {
Users users = new Users();
users.setAccount(account);
users.setUserName(userName);
users.setGender(gender);
users.setUserMobile(userMobile);
users.setDeptId(deptId);
users.setRoleId(roleId);
//新增
if (userId == null){
//初始密码
users.setPassword("123456");
users.setCreateUser(UserThreadLocal.get().getUserId());
int insert = mapper.insert(users);
Resp.toReturn(insert>0?"成功":"失败",insert>0);
}
//更新
users.setId(userId);
users.setUpdateUser(UserThreadLocal.get().getUserId());
int update = mapper.updateById(users);
return Resp.toReturn(update>0?"成功":"失败",update>0);
}
@Override
public Resp delete(Long userId) {
int ret = mapper.deleteById(userId);
return Resp.toReturn(ret>0?"成功":"失败",ret>0);
}
@Override
public Resp resetPwd(Long userId) {
int ret = mapper.resetPwd(userId);
return Resp.toReturn(ret>0?"成功":"失败",ret>0);
}
}
@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements IGoodsService {
@Resource
private GoodsMapper mapper;
@Resource
private UsersMapper usersMapper;
@Override
public PageResp<List<Goods>> selectGoodsList(Integer page, Integer limit, String goodsName, String goodsNo) {
QueryWrapper<Goods> queryWrapper = new QueryWrapper<>();
if (StringUtils.isNotBlank(goodsName)) {
queryWrapper.like("goods_name", goodsName);
}
if (StringUtils.isNotBlank(goodsNo)) {
queryWrapper.eq("goods_code", goodsNo);
}
PageHelper.startPage(page,limit);
List<Goods> goodsList = mapper.selectList(queryWrapper);
goodsList.forEach(li->{
li.setCreateUserName(usersMapper.selectById(li.getCreateUser()).getUserName());
if(li.getUpdateUser()!=null){
li.setEditUserName(usersMapper.selectById(li.getUpdateUser()).getUserName());
}
});
PageInfo<Goods> pageInfo = new PageInfo<>(goodsList);
return new PageResp<>(pageInfo.getList(), pageInfo.getTotal());
}
@Override
public Resp saveOrUpdate(Long goodsId, String goodsName, String goodsNo,Double price,Integer inventory,String stemPlace) {
Goods goods = new Goods();
goods.setGoodsNo(goodsNo);
goods.setGoodsName(goodsName);
if (price!=null){
goods.setPrice(price);
}
if (inventory!=null){
goods.setInventory(inventory);
}
if (stemPlace!=null){
goods.setStemPlace(stemPlace);
}
//新增
if (goodsId == null){
goods.setCreateUser(UserThreadLocal.get().getUserId());
int insert = mapper.insert(goods);
Resp.toReturn(insert>0?"成功":"失败",insert>0);
}
//更新,goodsId肯定不为空
goods.setId(goodsId);
goods.setUpdateUser(UserThreadLocal.get().getUserId());
int update = mapper.updateById(goods);
return Resp.toReturn(update>0?"成功":"失败",update>0);
}
@Override
public Resp delete(Long goodsId) {
int ret = mapper.deleteById(goodsId);
return Resp.toReturn(ret>0?"成功":"失败",ret>0);
}
}
@Service
public class DeptServiceImpl extends ServiceImpl<DeptMapper,Dept> implements IDeptService {
@Resource
private DeptMapper mapper;
@Autowired
private UsersMapper usersMapper;
@Override
public PageResp<List<Dept>> selectDeptList(Integer page, Integer limit, String deptName, String deptNo) {
QueryWrapper<Dept> queryWrapper = new QueryWrapper<>();
if (StringUtils.isNotBlank(deptName)) {
queryWrapper.like("dept_name", deptName);
}
if (StringUtils.isNotBlank(deptNo)) {
queryWrapper.eq("dept_code", deptNo);
}
List<Dept> list = mapper.selectList(queryWrapper);
list.forEach(li->{
li.setCreateUserName(usersMapper.selectById(li.getCreateUser()).getUserName());
li.setEditUserName(usersMapper.selectById(li.getUpdateUser()).getUserName());
});
PageHelper.startPage(page,limit);
PageInfo<Dept> info = new PageInfo<>(list);
PageResp<List<Dept>> resp = new PageResp<>();
resp.setCount(info.getTotal());
resp.setData(list);
return resp;
}
@Override
public Resp saveOrUpdate(Long deptId, String deptName, String deptNo) {
Dept dept = new Dept();
dept.setDeptNo(deptNo);
dept.setDeptName(deptName);
//新增
if (deptId == null){
//初始密码
dept.setCreateUser(UserThreadLocal.get().getUserId());
int insert = mapper.insert(dept);
Resp.toReturn(insert>0?"成功":"失败",insert>0);
}
//更新
dept.setId(deptId);
dept.setUpdateUser(UserThreadLocal.get().getUserId());
int update = mapper.updateById(dept);
return Resp.toReturn(update>0?"成功":"失败",update>0);
}
@Override
public Resp delete(Long deptId) {
int ret = mapper.deleteById(deptId);
return Resp.toReturn(ret>0?"成功":"失败",ret>0);
}
@Override
public ListResp<List<Dept>> selectDeptListNoParams() {
List<Dept> ret = mapper.selectList(new QueryWrapper<>());
ListResp<List<Dept>> resp = new ListResp<>();
resp.setData(ret);
return resp;
}
}
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper,Menu> implements IMenuService {
@Resource
private MenuMapper mapper;
@Resource
private RoleMenuMapper roleMenuMapper;
@Override
public ListResp<List<Menu>> selectMenuList() {
//得到当前用户在系统中的角色id
Long roleId = UserThreadLocal.get().getRoleId();
List<Long> menuIds = roleMenuMapper.selectByRoleId(roleId);
//查询出来的菜单列表
List<Menu> menus = new ArrayList<>();
menuIds.forEach(menuId ->{
menus.add(mapper.selectById(menuId));
});
/**
* 先找没有儿子的出来,再去找儿子
*/
//父级菜单
List<Menu> parentMenus = new ArrayList<>();
//子级菜单
List<Menu> sonMenus = new ArrayList<>();
for (Menu menu : menus) {
if (menu.getPid() == null){
//没有pid,说明没有父级,已经是顶级菜单,添加到父级菜单中
parentMenus.add(menu);
}
//添加到儿子菜单中
sonMenus.add(menu);
}
//排好序的父级菜单
//流式操作,加快集合处理速度
List<Menu> parentOrderMenus = parentMenus.stream().sorted(Comparator.
comparing(Menu::getOrderValue)).
collect(Collectors.toList());
for (Menu orderMenu : parentOrderMenus) {
//根据父级菜单的id和所有子菜单得到该父级菜单下的子菜单
List<Menu> child = getChild(orderMenu.getId(),sonMenus);
//排好序的子菜单
List<Menu> collect = child.stream().
sorted(Comparator.comparing(Menu::getOrderValue))
.collect(Collectors.toList());
orderMenu.setMenus(collect);
}
ListResp<List<Menu>> listResp = new ListResp<>();
listResp.setData(parentOrderMenus);
return listResp;
}
@Override
public ListResp<List<MenuVo>> nodesList() {
List<Menu> ret = mapper.selectList(new QueryWrapper<Menu>().isNull("pid"));
ArrayList<MenuVo> list = new ArrayList<>();
ret.forEach(r->{
Long id = r.getId();
String menuName = r.getMenuName();
list.add(new MenuVo(id,menuName));
});
ListResp<List<MenuVo>> resp = new ListResp<>();
resp.setData(list);
return resp;
}
@Override
public Resp saveOrUpdate(Long menuId, Long parentId, String menuIcon, String menuUrl, String menuName, Integer menuOrder) {
Menu menu = new Menu();
menu.setPid(parentId);
menu.setIcon(menuIcon);
menu.setUrl(menuUrl);
menu.setMenuName(menuName);
menu.setOrderValue(menuOrder);
//新增
if (menuId == null){
int insert = mapper.insert(menu);
Resp.toReturn(insert>0?"成功":"失败",insert>0);
}
//更新
menu.setId(menuId);
int update = mapper.updateById(menu);
return Resp.toReturn(update>0?"成功":"失败",update>0);
}
@Override
public Resp delete(List<Long> moduleIds) {
int sum = 0;
for (Long moduleId : moduleIds) {
int ret = mapper.deleteById(moduleId);
sum+=ret;
}
return Resp.toReturn(sum == moduleIds.size()?"成功":"失败",sum == moduleIds.size());
}
/**
* 找属于该菜单id的 儿子
* @param menuId
* @param allMenus
* @return
*/
public List<Menu> getChild(Long menuId, List<Menu> allMenus){
//找最近的儿子数据出来
List<Menu> childMenus = new ArrayList<>();
for (Menu allMenu : allMenus){
if (allMenu.getPid() == menuId){
childMenus.add(allMenu);
}
}
if (childMenus.size() == 0){
return childMenus;
}
//还要找儿子的儿子
for (Menu childMenu : childMenus) {
//说明还不是最低层的儿子
if (StringUtils.isEmpty(childMenu.getUrl())){
//递归
childMenu.setMenus(getChild(childMenu.getId(),allMenus));
}
}
return childMenus;
}
}
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper,Role> implements IRoleService {
@Resource
private RoleMapper mapper;
@Resource
private UsersMapper usersMapper;
@Resource
private MenuMapper menuMapper;
@Resource
private RoleMenuMapper roleMenuMapper;
@Override
public PageResp<List<Role>> selectRoleList(Integer page, Integer limit, String roleName, String roleNo) {
PageHelper.startPage(page,limit);
QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
if(roleName!=null && !"".equals(roleName)){
queryWrapper.eq("role_name",roleName);
}
if(roleNo!=null&&!"".equals(roleNo)){
queryWrapper.eq("role_code",roleNo);
}
List<Role> list = mapper.selectList(queryWrapper);
list.forEach(li->{
li.setCreateUserName(usersMapper.selectById(li.getCreateUser()).getUserName());
if(li.getUpdateUser()!=null){
li.setEditUserName(usersMapper.selectById(li.getUpdateUser()).getUserName());
}
});
PageInfo<Role> info = new PageInfo<>(list);
PageResp<List<Role>> resp = new PageResp<>();
resp.setCount(info.getTotal());
resp.setData(list);
return resp;
}
@Override
public Resp saveOrUpdate(Long roleId, String roleName, String roleNo) {
Role role = new Role();
role.setRoleNo(roleNo);
role.setRoleName(roleName);
//新增
if (roleId == null){
//初始密码
role.setCreateUser(UserThreadLocal.get().getUserId());
int insert = mapper.insert(role);
Resp.toReturn(insert>0?"成功":"失败",insert>0);
}
//更新
role.setId(roleId);
role.setUpdateUser(UserThreadLocal.get().getRoleId());
int update = mapper.updateById(role);
return Resp.toReturn(update>0?"成功":"失败",update>0);
}
//
@Override
@Transactional(rollbackFor = Exception.class)
public Resp delete(Long roleId) {
//1.删除角色
int ret = mapper.deleteById(roleId);
//2.删除角色所拥有的所有权限
QueryWrapper<RoleMenu> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("role_id",roleId);
roleMenuMapper.delete(queryWrapper);
return Resp.toReturn(ret>0?"成功":"失败",ret>0);
}
@Override
public TreeResp<List<Menu>> roleRight(Long roleId) {
List<Menu> menus = menuMapper.selectList(null);
TreeResp<List<Menu>> ret = new TreeResp<>();
ret.setData(menus);
List<RoleMenu> roleMenus = roleMenuMapper.selectList(new QueryWrapper<RoleMenu>().eq("role_id",roleId));
ret.setList(roleMenus);
return ret;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Resp roleRightSave(Long roleId, List<Long> menuIds) {
//删除之前的记录
roleMenuMapper.delete(new QueryWrapper<RoleMenu>().eq("role_id",roleId));
//批量插入数据
int ret = 0;
for (Long menuId : menuIds) {
ret+=roleMenuMapper.insert(new RoleMenu(null,menuId,roleId));
}
return Resp.toReturn(ret == menuIds.size()?"成功":"失败",ret == menuIds.size());
}
@Override
public ListResp<List<Role>> selectRoleListNoParams() {
List<Role> ret = mapper.selectList(null);
ListResp<List<Role>> rsp = new ListResp<>();
rsp.setData(ret);
return rsp;
}
}
定义MenuVO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MenuVo {
private Long id;
private String name;
}
Controller层
@RestController
@RequestMapping(Const.URL)
@Api("用户登录")
@Slf4j
public class LoginController {
@Resource
private IUserService service;
@ApiOperation("登录")
@PostMapping("login")
public LoginResp login(@RequestParam("username") String account,
@RequestParam("password") String password
){
return service.login(account, password);
}
}
@RestController
@RequestMapping(Const.URL)
public class UserController {
@Resource
IUserService service;
@PostMapping("User/list")
public PageResp<List<Users>> selectUserList(@RequestParam("page") Integer page,
@RequestParam("limit") Integer limit,
@RequestParam(value = "userName",required = false)String userName,
@RequestParam(value = "userMobile",required = false)String userMobile
){
return service.selectUserList(page, limit, userName, userMobile);
}
@PostMapping("User/save")
public Resp saveOrUpdate(@RequestParam(value = "userId",required = false) Long userId,
@RequestParam(value = "userName")String userName,
@RequestParam(value = "gender") String gender,
@RequestParam("account") String account,
@RequestParam(value = "userMobile")String userMobile,
@RequestParam(value = "roleId") Long roleId,
@RequestParam(value = "deptId") Long deptId
){
return service.saveOrUpdate(userId, account, userName, gender,userMobile, roleId, deptId);
}
@DeleteMapping("User/delete")
public Resp delete(@RequestParam(value = "ids") Long userId){
return service.delete(userId);
}
@PostMapping("User/pwd")
public Resp reset(@RequestParam("userId") Long userId){
return service.resetPwd(userId);
}
}
@RestController
@RequestMapping(Const.URL)
public class RoleController {
@Resource
IRoleService service;
@PostMapping("Role/list")
public PageResp selectRoleList(@RequestParam("page") Integer page,
@RequestParam("limit") Integer limit,
@RequestParam(value = "roleName",required = false) String roleName,
@RequestParam(value = "roleNo",required = false) String roleNo){
return service.selectRoleList(page,limit,roleName,roleNo);
}
@PostMapping("Role/list/no/parms")
public ListResp selectRoleListNoParams(){
return service.selectRoleListNoParams();
}
@PostMapping("Role/save")
public Resp saveOrUpdate(@RequestParam(value = "roleId",required = false) Long roleId,
@RequestParam(value = "roleName")String roleName,
@RequestParam("roleNo") String roleNo
){
return service.saveOrUpdate(roleId, roleName, roleNo);
}
@DeleteMapping("Role/delete")
public Resp delete(@RequestParam(value = "ids") Long roleId){
return service.delete(roleId);
}
@GetMapping("RoleRight/tree/{id}")
public TreeResp roleRight(@PathVariable(value = "id")Long id){
return service.roleRight(id);
}
@PostMapping("RoleRight/save")
public Resp roleRightSave(@RequestParam("roleId") Long roleId,
@RequestParam("moduleIds")List<Long>moduleIds){
return service.roleRightSave(roleId,moduleIds);
}
}
@RestController
@RequestMapping(Const.URL)
public class DeptController {
@Resource
IDeptService service;
@PostMapping("Dept/list")
public PageResp selectDeptList(@RequestParam("page") Integer page,
@RequestParam("limit") Integer limit,
@RequestParam(value = "deptName",required = false) String deptName,
@RequestParam(value = "deptNo",required = false) String deptNo){
return service.selectDeptList(page,limit,deptName,deptNo);
}
@PostMapping("Dept/save")
public Resp saveOrUpdate(@RequestParam(value = "deptId",required = false) Long deptId,
@RequestParam(value = "deptName")String deptName,
@RequestParam("deptNo") String deptNo
){
return service.saveOrUpdate(deptId, deptName, deptNo);
}
@PostMapping("Dept/list/no/params")
public ListResp selectDeptListNoParams(){
return service.selectDeptListNoParams();
}
@DeleteMapping("Dept/delete")
public Resp delete(@RequestParam(value = "ids") Long deptId){
return service.delete(deptId);
}
}
@RestController
@RequestMapping(Const.URL)
public class GoodsController {
@Resource
IGoodsService service;
@PostMapping("Goods/list")
public PageResp selectDeptList(@RequestParam("page") Integer page,
@RequestParam("limit") Integer limit,
@RequestParam(value = "goodsName",required = false) String goodsName,
@RequestParam(value = "goodsNo",required = false) String goodsNo){
return service.selectGoodsList(page,limit,goodsName,goodsNo);
}
@PostMapping("Goods/save")
public Resp saveOrUpdate(@RequestParam(value = "goodsId",required = false) Long goodsId,
@RequestParam(value = "goodsName")String goodsName,
@RequestParam("goodsNo") String goodsNo,
@RequestParam("price") Double price,
@RequestParam("inventory") Integer inventory,
@RequestParam("stemPlace") String stemPlace
){
return service.saveOrUpdate(goodsId, goodsName,goodsNo,price,inventory,stemPlace);
}
@DeleteMapping("Goods/delete")
public Resp delete(@RequestParam(value = "ids") Long goodsId){
return service.delete(goodsId);
}
}
@RestController
@RequestMapping(Const.URL)
public class MenuController {
@Resource
IMenuService service;
@GetMapping("menu")
public ListResp selectMenuList(){
return service.selectMenuList();
}
@PostMapping("Module/list")
public ListResp menuList(){
return service.selectMenuList();
}
@PostMapping("Module/nodes")
public ListResp nodesList(){
return service.nodesList();
}
@PostMapping("Module/save")
public Resp saveOrUpdate(@RequestParam(value = "moduleId",required = false) Long menuId,
@RequestParam(value = "parentId",required = false) Long parentId,
@RequestParam(value = "moduleIcon")String menuIcon,
@RequestParam(value = "moduleUrl")String menuUrl,
@RequestParam(value = "moduleName")String menuName,
@RequestParam("moduleOrder") Integer menuOrder
){
return service.saveOrUpdate(menuId, parentId, menuIcon, menuUrl, menuName, menuOrder);
}
@DeleteMapping("Module/delete")
public Resp delete(@RequestParam(value = "ids") List<Long> moduleIds){
return service.delete(moduleIds);
}
}
定义日志
在resources目录下面定义logback.xml
<configuration>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 输出到文件 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${user.dir}/src/main/resources/log/application.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 设置根logger的级别 -->
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
定义AOP
位于com.xzj.aspect包
1.数据库层的Asepect监控
@Aspect
@Slf4j
@Component
@Order(1)
public class DaoLogAspect {
@Pointcut("execution(* com.xzj.mapper.*Mapper.*(..))")
public void logPointCut(){}
@Around("logPointCut()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
long startTime = System.currentTimeMillis();
log.info("数据库开始执行:{}",point.getSignature().toShortString());
Object proceed = point.proceed();
log.info("数据库耗时:{} ms",(System.currentTimeMillis() - startTime));
return proceed;
}
}
2.网络层的Aspect监控
@Aspect
@Slf4j
@Component
@Order(1) //优先执行此切面,数字越小,优先级越高
public class WebLogAspect {
@Pointcut("execution(* com.xzj.controller..*.*(..))")
public void logPointCut(){}
@Before("logPointCut()")
public void doBefore(JoinPoint joinPoint){
//RequestContextHolder.getRequestAttributes()是Spring框架中的一个静态方法,用于获取当前线程中的请求属性对象。
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("请求地址:{}",request.getRequestURL().toString());
log.info("请求方法:{}",request.getMethod());
//getDeclaringTypeName()返回方法所在类的全名称,getName()返回方法名称
log.info("方法签名信息:{},{}",joinPoint.getSignature().getDeclaringTypeName(),joinPoint.getSignature().getName());
log.info("请求参数:{}", JsonUtil.getJsonString(joinPoint.getArgs()));
}
@AfterReturning(pointcut = "logPointCut()",returning = "ret")
public void doAfterReturn(Object ret){
log.debug("返回值:{}",JsonUtil.getJsonString(ret));
}
@Around("logPointCut()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = point.proceed();
log.info("业务层耗时:{} ms",(System.currentTimeMillis() - startTime));
return proceed;
}
}
自定义异常及全局捕获
给出全局捕获异常的示例
@Slf4j
@RestControllerAdvice //用于定义全局的异常处理器(Global Exception Handler)类
public class ImsExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(ImsAuthException.class) //通过使用@ExceptionHandler注解并指定相应的异常类型,可以为不同类型的异常提供不同的处理逻辑,以便进行精细的异常处理和错误响应生成。
public ResponseEntity<Resp> authExceptionHandler(ImsAuthException imsAuthException){
printErrLog(imsAuthException);
Resp resp = new Resp(false,imsAuthException.getMessage());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(resp);
}
public void printErrLog(Exception e){
log.error("异常:{}",e.getMessage());
}
@ExceptionHandler(ImsBadRequestException.class)
public ResponseEntity<Resp> badRequestExceptionHandler(ImsBadRequestException imsBadRequestException){
printErrLog(imsBadRequestException);
Resp resp = new Resp(false,imsBadRequestException.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resp);
}
@ExceptionHandler(ImsNotFoundException.class)
public ResponseEntity<Resp> notFoundExceptionHandler(ImsNotFoundException imsNotFoundException){
printErrLog(imsNotFoundException);
Resp resp = new Resp(false,imsNotFoundException.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(resp);
}
}