diff --git a/src/main/java/com/rymcu/forest/answer/AnswerController.java b/src/main/java/com/rymcu/forest/answer/AnswerController.java index 92df131..3e0a40a 100644 --- a/src/main/java/com/rymcu/forest/answer/AnswerController.java +++ b/src/main/java/com/rymcu/forest/answer/AnswerController.java @@ -21,7 +21,7 @@ import java.util.Map; @RequestMapping("/api/v1/answer") public class AnswerController { - private final static String ANSWER_API_URL = "http://101.132.237.86:8089/question"; + private final static String ANSWER_API_URL = "http://1.116.175.112:8089/question"; @GetMapping("/today") public GlobalResult today() throws BaseApiException { diff --git a/src/main/java/com/rymcu/forest/config/RedisKeyExpirationListener.java b/src/main/java/com/rymcu/forest/config/RedisKeyExpirationListener.java new file mode 100644 index 0000000..ce81763 --- /dev/null +++ b/src/main/java/com/rymcu/forest/config/RedisKeyExpirationListener.java @@ -0,0 +1,54 @@ +package com.rymcu.forest.config; + +import com.rymcu.forest.entity.User; +import com.rymcu.forest.jwt.def.JwtConstants; +import com.rymcu.forest.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + + +/** + * Created on 2021/10/9 9:25. + * + * @author ronger + * @email ronger-x@outlook.com + * @packageName com.rymcu.forest.config + */ +@Slf4j +@Component +public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { + + @Resource + private UserService userService; + + @Autowired + public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { + super(listenerContainer); + } + + /** + * 针对 redis 数据失效事件,进行数据处理 + * + * @param message key + * @param pattern pattern + */ + @Override + public void onMessage(Message message, byte[] pattern) { + + // 获取到失效的 key + String expiredKey = message.toString(); + if (expiredKey.contains(JwtConstants.LAST_ONLINE)) { + String email = expiredKey.replace(JwtConstants.LAST_ONLINE, ""); + log.info("拿到过期的数据:{}", expiredKey); + log.info("处理后的数据:{}", email); + userService.updateLastOnlineTimeByEmail(email); + } + super.onMessage(message, pattern); + } +} diff --git a/src/main/java/com/rymcu/forest/config/RedisListenerConfig.java b/src/main/java/com/rymcu/forest/config/RedisListenerConfig.java new file mode 100644 index 0000000..676ee27 --- /dev/null +++ b/src/main/java/com/rymcu/forest/config/RedisListenerConfig.java @@ -0,0 +1,24 @@ +package com.rymcu.forest.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; + +/** + * Created on 2021/10/9 9:23. + * + * @author ronger + * @email ronger-x@outlook.com + * @packageName com.rymcu.forest.config + */ +@Configuration +public class RedisListenerConfig { + + @Bean + public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + return container; + } +} diff --git a/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java b/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java index a510a12..60ff5e3 100644 --- a/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java +++ b/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java @@ -3,6 +3,7 @@ package com.rymcu.forest.dto; import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; +import javax.persistence.Column; import java.io.Serializable; import java.util.Date; @@ -37,4 +38,12 @@ public class UserInfoDTO implements Serializable { @JSONField(format = "yyyy-MM-dd HH:mm") private Date lastLoginTime; + @JSONField(format = "yyyy-MM-dd HH:mm") + private Date lastOnlineTime; + + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private Date createdTime; + + private Integer onlineStatus; + } diff --git a/src/main/java/com/rymcu/forest/entity/User.java b/src/main/java/com/rymcu/forest/entity/User.java index b13a264..8654f99 100644 --- a/src/main/java/com/rymcu/forest/entity/User.java +++ b/src/main/java/com/rymcu/forest/entity/User.java @@ -108,9 +108,16 @@ public class User implements Serializable,Cloneable { private Date createdTime; /** - * 创建时间 + * 更新时间 * */ @Column(name = "updated_time") @JSONField(format = "yyyy-MM-dd HH:mm:ss") private Date updatedTime; + + /** + * 最后在线时间 + * */ + @Column(name = "last_online_time") + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private Date lastOnlineTime; } \ No newline at end of file diff --git a/src/main/java/com/rymcu/forest/jwt/aop/RestAuthTokenInterceptor.java b/src/main/java/com/rymcu/forest/jwt/aop/RestAuthTokenInterceptor.java index 37928cc..52132eb 100644 --- a/src/main/java/com/rymcu/forest/jwt/aop/RestAuthTokenInterceptor.java +++ b/src/main/java/com/rymcu/forest/jwt/aop/RestAuthTokenInterceptor.java @@ -4,6 +4,7 @@ package com.rymcu.forest.jwt.aop; import com.rymcu.forest.jwt.def.JwtConstants; import com.rymcu.forest.jwt.model.TokenModel; import com.rymcu.forest.jwt.service.TokenManager; +import com.rymcu.forest.mapper.UserMapper; import com.rymcu.forest.web.api.exception.BaseApiException; import com.rymcu.forest.web.api.exception.ErrorCode; import io.jsonwebtoken.Claims; @@ -14,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Objects; @@ -28,6 +30,8 @@ public class RestAuthTokenInterceptor implements HandlerInterceptor { @Autowired private TokenManager manager; + @Resource + private UserMapper userMapper; @Override public void afterCompletion(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, Object obj, Exception exception) throws Exception { @@ -36,7 +40,6 @@ public class RestAuthTokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { - //从header中得到token String authHeader = request.getHeader(JwtConstants.AUTHORIZATION); if(StringUtils.isBlank(authHeader)){ @@ -63,6 +66,19 @@ public class RestAuthTokenInterceptor implements HandlerInterceptor { request.setAttribute(JwtConstants.CURRENT_TOKEN_CLAIMS, claims); //如果token验证成功,将token对应的用户id存在request中,便于之后注入 request.setAttribute(JwtConstants.CURRENT_USER_NAME, model.getUsername()); + // 判断是否为后台接口或财政划转接口 + String adminApi = "/admin"; + String transactionApi = "/transaction"; + String uri = request.getRequestURI(); + if (uri.contains(adminApi) || uri.contains(transactionApi)) { + // 判断管理员权限 + boolean hasPermission = userMapper.hasAdminPermission(model.getUsername()); + if (hasPermission) { + return true; + } else { + throw new BaseApiException(ErrorCode.ACCESS_DENIED); + } + } return true; } else { throw new BaseApiException(ErrorCode.TOKEN_); diff --git a/src/main/java/com/rymcu/forest/jwt/def/JwtConstants.java b/src/main/java/com/rymcu/forest/jwt/def/JwtConstants.java index 07fcf21..62162fe 100644 --- a/src/main/java/com/rymcu/forest/jwt/def/JwtConstants.java +++ b/src/main/java/com/rymcu/forest/jwt/def/JwtConstants.java @@ -14,6 +14,8 @@ public class JwtConstants { public static final String UPLOAD_TOKEN = "X-Upload-Token"; public static final String CURRENT_USER_NAME = "CURRENT_TOKEN_USER_NAME"; public static final String CURRENT_TOKEN_CLAIMS = "CURRENT_TOKEN_CLAIMS"; + public static final String LAST_ONLINE = "last_online:"; public static final long TOKEN_EXPIRES_HOUR = 2 * 60; + public static final long LAST_ONLINE_EXPIRES_MINUTE = 10; } diff --git a/src/main/java/com/rymcu/forest/jwt/service/RedisTokenManager.java b/src/main/java/com/rymcu/forest/jwt/service/RedisTokenManager.java index 5a20136..a875e98 100644 --- a/src/main/java/com/rymcu/forest/jwt/service/RedisTokenManager.java +++ b/src/main/java/com/rymcu/forest/jwt/service/RedisTokenManager.java @@ -9,6 +9,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.concurrent.TimeUnit; @@ -50,6 +52,9 @@ public class RedisTokenManager implements TokenManager { } //如果验证成功,说明此用户进行了一次有效操作,延长token的过期时间 redisTemplate.boundValueOps(model.getUsername()).expire(JwtConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); + StringBuilder key = new StringBuilder(); + key.append(JwtConstants.LAST_ONLINE).append(model.getUsername()); + redisTemplate.boundValueOps(key.toString()).set(LocalDateTime.now().toString(), JwtConstants.LAST_ONLINE_EXPIRES_MINUTE, TimeUnit.MINUTES); return true; } diff --git a/src/main/java/com/rymcu/forest/mapper/UserMapper.java b/src/main/java/com/rymcu/forest/mapper/UserMapper.java index 6b7df09..dc2d4c2 100644 --- a/src/main/java/com/rymcu/forest/mapper/UserMapper.java +++ b/src/main/java/com/rymcu/forest/mapper/UserMapper.java @@ -95,13 +95,11 @@ public interface UserMapper extends Mapper { * @param nickname * @param avatarType * @param avatarUrl - * @param email - * @param phone * @param signature * @param sex * @return */ - Integer updateUserInfo(@Param("idUser") Integer idUser, @Param("nickname") String nickname, @Param("avatarType") String avatarType, @Param("avatarUrl") String avatarUrl, @Param("email") String email, @Param("phone") String phone, @Param("signature") String signature, @Param("sex") String sex); + Integer updateUserInfo(@Param("idUser") Integer idUser, @Param("nickname") String nickname, @Param("avatarType") String avatarType, @Param("avatarUrl") String avatarUrl, @Param("signature") String signature, @Param("sex") String sex); /** * 验证昵称是否重复 @@ -145,5 +143,19 @@ public interface UserMapper extends Mapper { * @param searchDTO * @return */ - List selectUsers(@Param("searchDTO") UserSearchDTO searchDTO); + List selectUsers(@Param("searchDTO") UserSearchDTO searchDTO); + + /** + * 更新用户最后在线时间 + * @param email + * @return + */ + Integer updateLastOnlineTimeByEmail(@Param("email") String email); + + /** + * 判断用户是否拥有管理员权限 + * @param email + * @return + */ + boolean hasAdminPermission(@Param("email") String email); } \ No newline at end of file diff --git a/src/main/java/com/rymcu/forest/service/UserService.java b/src/main/java/com/rymcu/forest/service/UserService.java index 31ad8c3..161669b 100644 --- a/src/main/java/com/rymcu/forest/service/UserService.java +++ b/src/main/java/com/rymcu/forest/service/UserService.java @@ -142,5 +142,12 @@ public interface UserService extends Service { * @param searchDTO * @return */ - List findUsers(UserSearchDTO searchDTO); + List findUsers(UserSearchDTO searchDTO); + + /** + * 通过邮箱查询用户信息 + * @param email + * @return + */ + Integer updateLastOnlineTimeByEmail(String email); } diff --git a/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java index bdd47c1..eceeb58 100644 --- a/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java +++ b/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java @@ -6,6 +6,7 @@ import com.rymcu.forest.dto.*; import com.rymcu.forest.entity.Role; import com.rymcu.forest.entity.User; import com.rymcu.forest.entity.UserExtend; +import com.rymcu.forest.jwt.def.JwtConstants; import com.rymcu.forest.jwt.service.TokenManager; import com.rymcu.forest.lucene.model.UserLucene; import com.rymcu.forest.lucene.util.UserIndexUtil; @@ -198,8 +199,7 @@ public class UserServiceImpl extends AbstractService implements UserServic user.setAvatarUrl(avatarUrl); user.setAvatarType("0"); } - Integer result = userMapper.updateUserInfo(user.getIdUser(), user.getNickname(), user.getAvatarType(),user.getAvatarUrl(), - user.getEmail(),user.getPhone(),user.getSignature(), user.getSex()); + Integer result = userMapper.updateUserInfo(user.getIdUser(), user.getNickname(), user.getAvatarType(),user.getAvatarUrl(),user.getSignature(), user.getSex()); UserIndexUtil.addIndex(UserLucene.builder() .idUser(user.getIdUser()) .nickname(user.getNickname()) @@ -262,7 +262,7 @@ public class UserServiceImpl extends AbstractService implements UserServic String email = changeEmailDTO.getEmail(); String code = changeEmailDTO.getCode(); String vCode = redisService.get(email); - if(StringUtils.isNotBlank(vCode)){ + if(StringUtils.isNotBlank(vCode) && StringUtils.isNotBlank(code)){ if(vCode.equals(code)){ userMapper.updateEmail(idUser, email); map.put("message","更新成功!"); @@ -282,7 +282,24 @@ public class UserServiceImpl extends AbstractService implements UserServic } @Override - public List findUsers(UserSearchDTO searchDTO) { - return userMapper.selectUsers(searchDTO); + public List findUsers(UserSearchDTO searchDTO) { + List users = userMapper.selectUsers(searchDTO); + users.forEach(user -> { + user.setOnlineStatus(getUserOnlineStatus(user.getEmail())); + }); + return users; + } + + private Integer getUserOnlineStatus(String email) { + String lastOnlineTime = redisService.get(JwtConstants.LAST_ONLINE + email); + if (StringUtils.isBlank(lastOnlineTime)) { + return 0; + } + return 1; + } + + @Override + public Integer updateLastOnlineTimeByEmail(String email) { + return userMapper.updateLastOnlineTimeByEmail(email); } } diff --git a/src/main/java/com/rymcu/forest/web/api/admin/AdminController.java b/src/main/java/com/rymcu/forest/web/api/admin/AdminController.java index 216ea56..b4a297a 100644 --- a/src/main/java/com/rymcu/forest/web/api/admin/AdminController.java +++ b/src/main/java/com/rymcu/forest/web/api/admin/AdminController.java @@ -4,10 +4,7 @@ import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.rymcu.forest.core.result.GlobalResult; import com.rymcu.forest.core.result.GlobalResultGenerator; -import com.rymcu.forest.dto.ArticleDTO; -import com.rymcu.forest.dto.ArticleSearchDTO; -import com.rymcu.forest.dto.CommentDTO; -import com.rymcu.forest.dto.UserSearchDTO; +import com.rymcu.forest.dto.*; import com.rymcu.forest.dto.admin.TopicTagDTO; import com.rymcu.forest.dto.admin.UserRoleDTO; import com.rymcu.forest.entity.*; @@ -47,8 +44,8 @@ public class AdminController { @GetMapping("/users") public GlobalResult> users(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer rows, UserSearchDTO searchDTO){ PageHelper.startPage(page, rows); - List list = userService.findUsers(searchDTO); - PageInfo pageInfo = new PageInfo<>(list); + List list = userService.findUsers(searchDTO); + PageInfo pageInfo = new PageInfo<>(list); Map map = new HashMap(2); map.put("users", pageInfo.getList()); Map pagination = Utils.getPagination(pageInfo); diff --git a/src/main/java/com/rymcu/forest/web/api/exception/ErrorCode.java b/src/main/java/com/rymcu/forest/web/api/exception/ErrorCode.java index 45964a1..bdf4ce8 100644 --- a/src/main/java/com/rymcu/forest/web/api/exception/ErrorCode.java +++ b/src/main/java/com/rymcu/forest/web/api/exception/ErrorCode.java @@ -2,10 +2,11 @@ package com.rymcu.forest.web.api.exception; public enum ErrorCode { - UNAUTHORIZED(401, "请求要求用户的身份认证"),//未认证(签名错误) + UNAUTHORIZED(401, "请求要求用户的身份认证"), INVALID_TOKEN(402, "TOKEN验证失败,无效的TOKEN!"), TOKEN_(402, "TOKEN验证失败,无效的TOKEN!"), - NOT_FOUND(404, "此接口不存在"),//接口不存在 + ACCESS_DENIED(403, "服务器拒绝请求!"), + NOT_FOUND(404, "此接口不存在"), INTERNAL_SERVER_ERROR(500, "服务内部异常"); private int code; diff --git a/src/main/java/mapper/UserMapper.xml b/src/main/java/mapper/UserMapper.xml index 449e5f7..601adaf 100644 --- a/src/main/java/mapper/UserMapper.xml +++ b/src/main/java/mapper/UserMapper.xml @@ -17,6 +17,7 @@ + @@ -31,6 +32,8 @@ + + @@ -60,10 +63,7 @@ update forest_user set status = #{status} where id = #{idUser} - update forest_user set nickname = #{nickname},email = #{email},signature = #{signature},avatar_type = #{avatarType},avatar_url = #{avatarUrl},sex = #{sex} - - ,phone = #{phone} - + update forest_user set nickname = #{nickname},signature = #{signature},avatar_type = #{avatarType},avatar_url = #{avatarUrl},sex = #{sex} where id = #{idUser} @@ -75,12 +75,15 @@ update forest_user set password = #{password} where id = #{idUser} + + update forest_user set last_online_time = sysdate() where email = #{email} + select * from forest_user where id = #{id} - + select id, nickname, sex, avatar_type, avatar_url, email, account, status, last_login_time, created_time, last_online_time from forest_user and instr(nickname, #{searchDTO.nickname}) > 0 - order by last_login_time desc + order by last_online_time desc + + \ No newline at end of file