AppRegister

This commit is contained in:
2025-06-20 08:23:00 +08:00
parent 27aea7e20d
commit bcaf594145
29 changed files with 725 additions and 148 deletions

View File

@ -27,23 +27,27 @@ import com.fuyuanshen.modules.security.config.LoginProperties;
import com.fuyuanshen.modules.security.config.SecurityProperties;
import com.fuyuanshen.modules.security.config.enums.LoginCodeEnum;
import com.fuyuanshen.modules.security.security.TokenProvider;
import com.fuyuanshen.modules.security.security.app.AppTokenProvider;
import com.fuyuanshen.modules.security.service.OnlineUserService;
import com.fuyuanshen.modules.security.service.UserDetailsServiceImpl;
import com.fuyuanshen.modules.security.service.dto.app.AppAuthUserQuery;
import com.fuyuanshen.modules.security.service.dto.app.AppAuthUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppRoleDto;
import com.fuyuanshen.modules.security.service.dto.AuthUserDto;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.query.APPUserDto;
import com.fuyuanshen.modules.system.domain.query.APPUserQuery;
import com.fuyuanshen.modules.system.mapper.app.APPUserMapper;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.utils.RedisUtils;
import com.fuyuanshen.utils.SecurityUtils;
import com.fuyuanshen.utils.StringUtils;
import com.wf.captcha.base.Captcha;
import io.netty.util.internal.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -57,7 +61,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@ -77,6 +81,7 @@ public class AuthController {
private final SecurityProperties properties;
private final RedisUtils redisUtils;
private final OnlineUserService onlineUserService;
private final AppTokenProvider appTokenProvider;
private final TokenProvider tokenProvider;
private final CaptchaConfig captchaConfig;
private final LoginProperties loginProperties;
@ -137,64 +142,24 @@ public class AuthController {
@Log("app用户注册")
@ApiOperation("app用户注册")
@AnonymousPostMapping(value = "/app/register")
public ResponseEntity<Boolean> APPRegister(@Validated @RequestBody AppAuthUserQuery authUser, HttpServletRequest request) throws Exception {
// 校验手机号是否为空
if (StringUtils.isBlank(authUser.getPhoneNumber())) {
throw new BadRequestException("手机号不能为空");
}
// 验证验证码(示例)
int verificationCode = 0000;
if (authUser.getVerificationCode() != verificationCode) {
throw new BadRequestException("验证码错误");
}
// 构建用户对象
APPUserDto appUserDTO = new APPUserDto();
appUserDTO.setUsername(authUser.getPhoneNumber());
appUserDTO.setPassword(authUser.getPassword());
appUserDTO.setPhoneNumber(authUser.getPhoneNumber());
LocalDateTime now = LocalDateTime.now();
appUserDTO.setCreateTime(now);
appUserDTO.setUpdateTime(now);
log.debug("注册参数: {}", appUserDTO);
// 检查手机号是否已注册
APPUser existingUser = appUserMapper.selectByQueryOne(appUserDTO);
if (existingUser != null) {
throw new BadRequestException("手机号已注册");
}
// 执行注册
boolean isRegistered = appUserMapper.createAppUser(appUserDTO);
if (!isRegistered) {
throw new BadRequestException("注册失败");
}
return ResponseEntity.ok(true);
}
@Log("app用户登录")
@ApiOperation("app用户登录")
@AnonymousPostMapping(value = "/app/login")
public ResponseEntity<Object> APPLogin(@Validated @RequestBody AppAuthUserQuery authUser, HttpServletRequest request) throws Exception {
public ResponseEntity<Object> APPLogin(@Validated @RequestBody AppAuthUserDto authUser, HttpServletRequest request) throws Exception {
// 1. 构建查询参数
APPUserDto appUserDTO = new APPUserDto();
appUserDTO.setPhoneNumber(authUser.getPhoneNumber());
appUserDTO.setPassword(authUser.getPassword());
log.debug("登录参数: {}", appUserDTO);
APPUserQuery appUserQuery = new APPUserQuery();
appUserQuery.setPhoneNumber(authUser.getPhoneNumber());
appUserQuery.setPassword(authUser.getPassword());
appUserQuery.setUsername(authUser.getPhoneNumber());
log.debug("登录参数: {}", appUserQuery);
// 2. 查询用户
APPUser appUser = appUserMapper.selectByQueryOne(appUserDTO);
APPUser appUser = appUserMapper.selectByQueryOne(appUserQuery);
log.debug("查询用户: {}", appUser);
if (appUser == null) {
log.debug("手机号未注册: {}", authUser.getPhoneNumber());
throw new BadRequestException("手机号未注册");
@ -205,24 +170,24 @@ public class AuthController {
throw new BadRequestException("登录密码错误");
}
// 4. 加载用户详情
JwtUserDto jwtUser = userDetailsService.loadUserByUsername(appUser.getUsername());
JwtUserDto jwtUser = userDetailsService.apploadUserByUsername(appUser.getUsername());
// 5. 创建认证信息
Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
// 6. 生成 Token
String token = tokenProvider.createToken(jwtUser);
String token = appTokenProvider.createToken(jwtUser);
// 7. 获取角色权限
AppRoleDto appRoleDto = appUserMapper.selectRoleByUserLevel(appUser.getUserLevel());
// 8. 构建响应数据
Map<String, Object> authInfo = new HashMap<>(3) {{
Map<String, Object> authInfo = new HashMap<>(2) {{
put("token", properties.getTokenStartWith() + token);
put("user", jwtUser);
put("role", appRoleDto);
}};
// 9. 单点登录踢人

View File

@ -0,0 +1,156 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fuyuanshen.modules.security.security.app;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.fuyuanshen.modules.security.config.SecurityProperties;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.utils.RedisUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author /
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AppTokenProvider implements InitializingBean {
private JwtParser jwtParser;
private JwtBuilder jwtBuilder;
private final RedisUtils redisUtils;
private final SecurityProperties properties;
public static final String AUTHORITIES_UUID_KEY = "uid";
public static final String AUTHORITIES_UID_KEY = "userId";
@Override
public void afterPropertiesSet() {
byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
Key key = Keys.hmacShaKeyFor(keyBytes);
jwtParser = Jwts.parserBuilder()
.setSigningKey(key)
.build();
jwtBuilder = Jwts.builder()
.signWith(key, SignatureAlgorithm.HS512);
}
/**
* 创建Token 设置永不过期,
* Token 的时间有效性转到Redis 维护
* @param user /
* @return /
*/
public String createToken(JwtUserDto user) {
// 设置参数
Map<String, Object> claims = new HashMap<>(6);
// 设置用户ID
// claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
if (user.getAppUser() != null){
claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
}else {
claims.put(AUTHORITIES_UID_KEY, 0);
}
// 设置UUID确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
return jwtBuilder
.setClaims(claims)
.setSubject(user.getUsername())
.compact();
}
/**
* 依据Token 获取鉴权信息
*
* @param token /
* @return /
*/
Authentication getAuthentication(String token) {
Claims claims = getClaims(token);
User principal = new User(claims.getSubject(), "******", new ArrayList<>());
return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>());
}
public Claims getClaims(String token) {
return jwtParser
.parseClaimsJws(token)
.getBody();
}
/**
* @param token 需要检查的token
*/
public void checkRenewal(String token) {
// 判断是否续期token,计算token的过期时间
String loginKey = loginKey(token);
long time = redisUtils.getExpire(loginKey) * 1000;
Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
// 判断当前时间与过期时间的时间差
long differ = expireDate.getTime() - System.currentTimeMillis();
// 如果在续期检查的范围内,则续期
if (differ <= properties.getDetect()) {
long renew = time + properties.getRenew();
redisUtils.expire(loginKey, renew, TimeUnit.MILLISECONDS);
}
}
public String getToken(HttpServletRequest request) {
final String requestHeader = request.getHeader(properties.getHeader());
if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
return requestHeader.substring(7);
}
return null;
}
/**
* 获取登录用户RedisKey
* @param token /
* @return key
*/
public String loginKey(String token) {
Claims claims = getClaims(token);
return properties.getOnlineKey() + claims.getSubject() + ":" + getId(token);
}
/**
* 获取会话编号
* @param token /
* @return /
*/
public String getId(String token) {
Claims claims = getClaims(token);
return claims.get(AUTHORITIES_UUID_KEY, String.class);
}
}

View File

@ -15,6 +15,7 @@
*/
package com.fuyuanshen.modules.security.service;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.modules.security.security.TokenProvider;
@ -51,7 +52,17 @@ public class OnlineUserService {
* @param request /
*/
public void save(JwtUserDto jwtUserDto, String token, HttpServletRequest request){
String dept = jwtUserDto.getUser().getDept() == null ? null : jwtUserDto.getUser().getDept().getName();
// String dept = jwtUserDto.getUser().getDept() == null ? null : jwtUserDto.getUser().getDept().getName();
String dept = null;
if (jwtUserDto.getUser() != null) {
dept = jwtUserDto.getUser().getDept() == null ? null : jwtUserDto.getUser().getDept().getName();
}else {
dept="";
}
String ip = StringUtils.getIp(request);
String id = tokenProvider.getId(token);
String browser = StringUtils.getBrowser(request);
@ -66,6 +77,7 @@ public class OnlineUserService {
redisUtils.set(loginKey, onlineUserDto, properties.getTokenValidityInSeconds(), TimeUnit.MILLISECONDS);
}
/**
* 查询全部数据
* @param username /

View File

@ -48,8 +48,20 @@ public class UserCacheManager {
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 获取数据
return redisUtils.get(LoginProperties.cacheKey + userName, JwtUserDto.class);
}
try {
JwtUserDto jwtUserDto = redisUtils.get(LoginProperties.cacheKey + userName, JwtUserDto.class);
if (jwtUserDto != null){
jwtUserDto.getUsername();
}
return jwtUserDto;
} catch (Exception e) {
// redisUtils.del(LoginProperties.cacheKey + userName);
cleanUserCache(userName);
return null;
}
}
return null;
}

View File

@ -15,6 +15,7 @@
*/
package com.fuyuanshen.modules.security.service;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.exception.BadRequestException;
@ -58,7 +59,31 @@ public class UserDetailsServiceImpl implements UserDetailsService {
// 获取用户的权限
List<AuthorityDto> authorities = roleService.buildPermissions(user);
// 初始化JwtUserDto
jwtUserDto = new JwtUserDto(user, dataService.getDeptIds(user), authorities);
jwtUserDto = new JwtUserDto(user, null,dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto);
}
}
return jwtUserDto;
}
public JwtUserDto apploadUserByUsername(String username) {
JwtUserDto jwtUserDto = userCacheManager.getUserCache(username);
if (jwtUserDto == null) {
APPUser user = userService.appGetLoginData(username);
if (user == null) {
throw new BadRequestException("用户不存在");
} else {
if (!user.getEnabled()) {
throw new BadRequestException("账号未激活!");
}
// 获取用户的权限
List<AuthorityDto> authorities = roleService.appBuildPermissions(user);
// 初始化JwtUserDto
jwtUserDto = new JwtUserDto(null,user, dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto);
}

View File

@ -16,12 +16,16 @@
package com.fuyuanshen.modules.security.service.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import com.fuyuanshen.modules.system.domain.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@ -30,56 +34,98 @@ import java.util.stream.Collectors;
* @author Zheng Jie
* @date 2018-11-23
*/
@NoArgsConstructor(force = true)
@Getter
@AllArgsConstructor
public class JwtUserDto implements UserDetails {
@Setter
public class JwtUserDto implements UserDetails, java.io.Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户")
private final User user;
@ApiModelProperty(value = "用户")
private final APPUser appUser;
@ApiModelProperty(value = "数据权限")
private final List<Long> dataScopes;
@ApiModelProperty(value = "角色")
private final List<AuthorityDto> authorities;
private String username;
private String password;
private boolean enabled = true;
public Set<String> getRoles() {
if (authorities== null){
return Collections.emptySet();
}
return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet());
}
@Override
@JSONField(serialize = false)
//@JSONField(serialize = false)
public String getPassword() {
if (appUser != null) {
return appUser.getPassword();
}
return user.getPassword();
}
public JwtUserDto(User user, APPUser appUser, List<Long> dataScopes, List<AuthorityDto> authorities) {
this.user = user;
this.appUser = appUser;
this.dataScopes = dataScopes;
this.authorities = authorities;
if (user != null) {
this.username = user.getUsername();
this.password = user.getPassword();
this.enabled = user.getEnabled();
} else if (appUser != null) {
this.username = appUser.getUsername();
this.password = appUser.getPassword();
this.enabled = appUser.getEnabled();
}
}
@Override
@JSONField(serialize = false)
//@JSONField(serialize = false)
public String getUsername() {
if (appUser != null) {
return appUser.getUsername();
}
return user.getUsername();
}
@JSONField(serialize = false)
//@JSONField(serialize = false)
public String appGetUsername() {
return appUser.getUsername();
}
//@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
@JSONField(serialize = false)
//@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
@JSONField(serialize = false)
//@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
@JSONField(serialize = false)
//@JSONField(serialize = false)
public boolean isEnabled() {
return user.getEnabled();
return enabled;
}
}

View File

@ -26,7 +26,7 @@ import javax.validation.constraints.NotBlank;
* @date 2025-06-18
*/
@Data
public class AppAuthUserQuery {
public class AppAuthUserDto {
@ApiModelProperty(value = "用户名")
private String username;

View File

@ -0,0 +1,86 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fuyuanshen.modules.security.service.dto.app;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fuyuanshen.modules.security.service.dto.AuthorityDto;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Zheng Jie
* @date 2018-11-23
*/
@Getter
@AllArgsConstructor
public class AppJwtUserDto implements UserDetails {
@ApiModelProperty(value = "用户")
private final APPUser appUser;
@ApiModelProperty(value = "数据权限")
private final List<Long> dataScopes;
@ApiModelProperty(value = "角色")
private final List<AuthorityDto> authorities;
public Set<String> getRoles() {
return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet());
}
@Override
@JSONField(serialize = false)
public String getPassword() {
return appUser.getPassword();
}
@Override
@JSONField(serialize = false)
public String getUsername() {
return appUser.getUsername();
}
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
@JSONField(serialize = false)
public boolean isEnabled() {
return appUser.getEnabled();
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fuyuanshen.modules.security.service.dto.app;
import cn.hutool.core.util.RandomUtil;
import com.fuyuanshen.modules.security.config.LoginProperties;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.utils.RedisUtils;
import com.fuyuanshen.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author Zheng Jie
* @description 用户缓存管理
* @date 2022-05-26
**/
@Component
public class AppUserCacheManager {
@Resource
private RedisUtils redisUtils;
@Value("${login.user-cache.idle-time}")
private long idleTime;
/**
* 返回用户缓存
* @param userName 用户名
* @return JwtUserDto
*/
public AppJwtUserDto getUserCache(String userName) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 获取数据
return redisUtils.get(LoginProperties.cacheKey + userName, AppJwtUserDto.class);
}
return null;
}
/**
* 添加缓存到Redis
* @param userName 用户名
*/
@Async
public void addUserCache(String userName, AppJwtUserDto user) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 添加数据, 避免数据同时过期
long time = idleTime + RandomUtil.randomInt(900, 1800);
redisUtils.set(LoginProperties.cacheKey + "a" + userName, user, time);
}
}
/**
* 清理用户缓存信息
* 用户信息变更时
* @param userName 用户名
*/
@Async
public void cleanUserCache(String userName) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 清除数据
redisUtils.del(LoginProperties.cacheKey + userName);
}
}
}

View File

@ -25,6 +25,7 @@ import com.fuyuanshen.modules.system.domain.Job;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.modules.system.domain.User;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@ -39,8 +40,7 @@ import java.util.Set;
* @author Zheng Jie
* @date 2018-11-22
*/
@Getter
@Setter
@Data
@TableName("app_user")
public class APPUser extends BaseEntity implements Serializable {
@ -53,6 +53,10 @@ public class APPUser extends BaseEntity implements Serializable {
@ApiModelProperty(hidden = true)
private Long deptId;
@TableField(exist = false)
@ApiModelProperty(value = "用户角色")
private Set<Role> roles;
@ApiModelProperty(hidden = true)
private Long pid;

View File

@ -0,0 +1,37 @@
package com.fuyuanshen.modules.system.domain.dto.app;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author: 默苍璃
* @date: 2025-06-1818:36
*/
@Data
public class APPUserDTO {
@ApiModelProperty(value = "用户名")
private String username;
@NotBlank(message = "手机号不能为空")
@ApiModelProperty(value = "手机号APP登录")
private String phoneNumber;
@NotBlank(message = "密码不能为空")
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
private String verificationCode;
/*@ApiModelProperty(value = "验证码")
private String code;
@ApiModelProperty(value = "验证码的key")
private String uuid = "";*/
}

View File

@ -1,21 +1,17 @@
package com.fuyuanshen.modules.system.domain.query;
import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
/**
* @author: 默苍璃
* @date: 2025-06-1213:49
*/
@Data
public class APPUserDto implements Serializable {
public class APPUserQuery implements Serializable {
@ApiModelProperty(value = "ID", hidden = true)
private Long id;

View File

@ -18,11 +18,15 @@ package com.fuyuanshen.modules.system.mapper.app;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.modules.security.service.dto.app.AppRoleDto;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.UserQueryCriteria;
import com.fuyuanshen.modules.system.domain.query.APPUserDto;
import com.fuyuanshen.modules.system.domain.query.APPUserQuery;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import javax.validation.constraints.NotBlank;
/**
* @author Zheng Jie
@ -44,14 +48,25 @@ public interface APPUserMapper extends BaseMapper<APPUser> {
/**
* 根据条件查询一个用户
*
* @param appUserDTO
* @param appUserQuery
* @return
*/
APPUser selectByQueryOne(@Param("userQuery") APPUserDto appUserDTO);
APPUser selectByQueryOne(@Param("appUserQuery") APPUserQuery appUserQuery);
AppRoleDto selectRoleByUserLevel(Byte userLevel);
boolean createAppUser(APPUserDto appUserDTO);
APPUser findByUsername(@Param("username") String username);
void setUsername(String phoneNumber);
APPUser appFindByUsername(@Param("username") String username);
@Select("select * from app_user where username=#{username}")
APPUserQuery getByUsername(@Param("username") String username);
@Select("select * from user where username=#{username} and password=#{password}")
User getByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
}

View File

@ -16,6 +16,8 @@
package com.fuyuanshen.modules.system.service;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import java.util.List;
/**
@ -31,4 +33,12 @@ public interface DataService {
* @return /
*/
List<Long> getDeptIds(User user);
/**
* 获取数据权限
* @param user /
* @return /
*/
List<Long> getDeptIds(APPUser user);
}

View File

@ -20,6 +20,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.fuyuanshen.modules.security.service.dto.AuthorityDto;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.RoleQueryCriteria;
import com.fuyuanshen.utils.PageResult;
@ -116,6 +117,14 @@ public interface RoleService extends IService<Role> {
*/
List<AuthorityDto> buildPermissions(User user);
/**
* 获取用户权限信息
* @param user 用户信息
* @return 权限信息
*/
List<AuthorityDto> appBuildPermissions(APPUser user);
/**
* 验证是否被用户关联
* @param ids /

View File

@ -18,6 +18,7 @@ package com.fuyuanshen.modules.system.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.UserQueryCriteria;
import com.fuyuanshen.modules.system.domain.vo.ConsumerVo;
import com.fuyuanshen.utils.PageResult;
@ -96,6 +97,8 @@ public interface UserService extends IService<User> {
*/
User getLoginData(String userName);
APPUser appGetLoginData(String userName);
/**
* 修改密码
*

View File

@ -19,6 +19,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.UserQueryCriteria;
import com.fuyuanshen.modules.system.domain.dto.app.APPUserDTO;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.utils.PageResult;
/**
@ -36,5 +38,14 @@ public interface APPUserService extends IService<APPUser> {
*/
PageResult<APPUser> queryAPPUser(UserQueryCriteria criteria, Page<APPUser> page);
/**
* 根据用户名查询
*
* @param userName /
* @return /
*/
APPUser getLoginData(String userName);
ResponseVO<Object> addUser(APPUserDTO user);
}

View File

@ -16,6 +16,7 @@
package com.fuyuanshen.modules.system.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import lombok.RequiredArgsConstructor;
import com.fuyuanshen.modules.system.domain.Dept;
import com.fuyuanshen.modules.system.domain.Role;
@ -93,4 +94,36 @@ public class DataServiceImpl implements DataService {
}
return deptIds;
}
/**
* 用户角色和用户部门改变时需清理缓存
* @param user /
* @return /
*/
@Override
public List<Long> getDeptIds(APPUser user) {
String key = CacheKey.DATA_USER + user.getId();
List<Long> ids = redisUtils.getList(key, Long.class);
if (CollUtil.isEmpty(ids)) {
Set<Long> deptIds = new HashSet<>();
// 查询用户角色
List<Role> roleList = roleService.findByUsersId(user.getId());
// 获取对应的部门ID
for (Role role : roleList) {
DataScopeEnum dataScopeEnum = DataScopeEnum.find(role.getDataScope());
switch (Objects.requireNonNull(dataScopeEnum)) {
case THIS_LEVEL:
deptIds.add(user.getDept().getId());
break;
case CUSTOMIZE:
deptIds.addAll(getCustomize(deptIds, role));
break;
default:
return new ArrayList<>();
}
}
ids = new ArrayList<>(deptIds);
redisUtils.set(key, ids, 1, TimeUnit.DAYS);
}
return ids;
}
}

View File

@ -19,6 +19,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import lombok.RequiredArgsConstructor;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.security.service.UserCacheManager;
@ -193,6 +194,26 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
return authorityDtos;
}
@Override
public List<AuthorityDto> appBuildPermissions(APPUser user) {
String key = CacheKey.ROLE_AUTH + user.getId();
List<AuthorityDto> authorityDtos = redisUtils.getList(key, AuthorityDto.class);
if (CollUtil.isEmpty(authorityDtos)) {
Set<String> permissions = new HashSet<>();
// 如果是管理员直接返回
if (user.getAdmin() == 1) {
permissions.add("admin");
return permissions.stream().map(AuthorityDto::new).collect(Collectors.toList());
}
List<Role> roles = roleMapper.findByUserId(user.getId());
permissions = roles.stream().flatMap(role -> role.getMenus().stream()).map(Menu::getPermission).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
authorityDtos = permissions.stream().map(AuthorityDto::new).collect(Collectors.toList());
redisUtils.set(key, authorityDtos, 1, TimeUnit.HOURS);
}
return authorityDtos;
}
@Override
public void download(List<Role> roles, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();

View File

@ -20,6 +20,8 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.mapper.app.APPUserMapper;
import lombok.RequiredArgsConstructor;
import com.fuyuanshen.config.properties.FileProperties;
import com.fuyuanshen.constants.RoleConstants;
@ -69,6 +71,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
private final UserCacheManager userCacheManager;
private final OnlineUserService onlineUserService;
private final RoleMapper roleMapper;
private final APPUserMapper appUserMapper;
@Override
@ -428,6 +431,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return userMapper.findByUsername(userName);
}
@Override
public APPUser appGetLoginData(String userName) {
return appUserMapper.appFindByUsername(userName);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updatePass(String username, String pass) {

View File

@ -15,46 +15,18 @@
*/
package com.fuyuanshen.modules.system.service.impl.app;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.config.properties.FileProperties;
import com.fuyuanshen.constants.RoleConstants;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.exception.EntityExistException;
import com.fuyuanshen.modules.security.service.OnlineUserService;
import com.fuyuanshen.modules.security.service.UserCacheManager;
import com.fuyuanshen.modules.system.constant.UserConstants;
import com.fuyuanshen.modules.system.domain.Job;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.UserQueryCriteria;
import com.fuyuanshen.modules.system.domain.query.UserQuery;
import com.fuyuanshen.modules.system.domain.vo.ConsumerVo;
import com.fuyuanshen.modules.system.mapper.RoleMapper;
import com.fuyuanshen.modules.system.mapper.UserJobMapper;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.modules.system.mapper.UserRoleMapper;
import com.fuyuanshen.modules.system.domain.dto.app.APPUserDTO;
import com.fuyuanshen.modules.system.mapper.app.APPUserMapper;
import com.fuyuanshen.modules.system.service.UserService;
import com.fuyuanshen.modules.system.service.app.APPUserService;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.utils.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @author Zheng Jie
@ -67,6 +39,7 @@ public class APPUserServiceImpl extends ServiceImpl<APPUserMapper, APPUser> impl
private final APPUserMapper appUserMapper;
/**
* 查询APP/小程序用户
*
@ -83,4 +56,33 @@ public class APPUserServiceImpl extends ServiceImpl<APPUserMapper, APPUser> impl
return pageResult ;
}
@Override
public APPUser getLoginData(String userName) {
return appUserMapper.appFindByUsername(userName);
}
@Override
public ResponseVO<Object> addUser(APPUserDTO user) {
String username = user.getPhoneNumber().toString();
if (appUserMapper.getByUsername(username) != null) {
throw new BadRequestException("该手机号已被注册");
}
APPUser appUser = new APPUser();
appUser.setUsername(user.getPhoneNumber().toString());
appUser.setPassword(user.getPassword());
appUser.setUserLevel((byte) 1);
appUser.setPhone(Long.valueOf(user.getPhoneNumber()));
appUser.setAdmin((byte) 1);
appUser.setEnabled(true);
appUser.setUserType(0);
System.out.println("--------------------"+appUser);
appUserMapper.insert(appUser);
return ResponseVO.success("注册成功");
}
}

View File

@ -26,6 +26,18 @@
<result column="user_type" property="userType"/>
</resultMap>
<sql id="Base_Column_List">
u1
.
app_user_id
as user_user_id, u1.dept_id as user_dept_id, u1.username as user_username,
u1.nick_name as user_nick_name, u1.email as user_email, u1.phone as user_phone,
u1.gender as user_gender, u1.avatar_name as user_avatar_name, u1.avatar_path as user_avatar_path,
u1.enabled as user_enabled, u1.pwd_reset_time as user_pwd_reset_time, u1.create_by as user_create_by,
u1.update_by as user_update_by, u1.create_time as user_create_time, u1.update_time as user_update_time,
u1.user_level, u1.pid as user_pid,u1.is_admin AS admin,
</sql>
<!-- 查询APP/小程序用户 -->
<select id="queryAPPUser" resultMap="BaseResultMap">
SELECT u.*
@ -43,16 +55,13 @@
</select>
<!-- 根据条件查询一个用户 -->
<select id="selectByQueryOne" resultType="com.fuyuanshen.modules.system.domain.app.APPUser">
select
u.*
from app_user u
<where>
<if test="userQuery.phoneNumber != null">
AND u.phone = #{userQuery.phoneNumber}
</if>
</where>
where u.username = #{appUserQuery.username}
</select>
@ -63,38 +72,13 @@
WHERE level = #{userLevel}
</select>
<!-- 插入用户(自动忽略空字段) -->
<insert id="createAppUser">
INSERT INTO app_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null and username != ''">username,</if>
<if test="password != null and password != ''">password,</if>
<if test="nickName != null and nickName != ''">nick_name,</if>
<if test="phone != null and phone != ''">phone,</if>
<if test="email != null and email != ''">email,</if>
<if test="gender != null and gender != ''">gender,</if>
<if test="deptId != null">dept_id,</if>
<if test="pwdResetTime != null">pwd_reset_time,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
<if test="tenantId != null">tenant_id,</if>
<if test="userType != null">user_type,</if>
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
<if test="username != null and username != ''">#{username},</if>
<if test="password != null and password != ''">#{password},</if>
<if test="nickName != null and nickName != ''">#{nickName},</if>
<if test="phone != null and phone != ''">#{phone},</if>
<if test="email != null and email != ''">#{email},</if>
<if test="gender != null and gender != ''">#{gender},</if>
<if test="deptId != null">#{deptId},</if>
<if test="pwdResetTime != null">#{pwdResetTime},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="tenantId != null">#{tenantId},</if>
<if test="userType != null">#{userType},</if>
</trim>
</insert>
<select id="findByUsername" resultMap="BaseResultMap">
select
u1.password user_password, u1.is_admin user_is_admin, u1.tenant_id,
<include refid="Base_Column_List"/>
from app_user u1
where u1.username = #{username}
</select>
<update id="setUsername">
UPDATE app_user
@ -105,4 +89,57 @@
</set>
WHERE phone = #{phoneNumber}
</update>
<resultMap id="APPUserWithRoleMap" type="com.fuyuanshen.modules.system.domain.app.APPUser">
<!-- 用户字段 -->
<id property="id" column="app_user_id"/>
<result property="username" column="username"/>
<result property="nickName" column="nick_name"/>
<result property="email" column="email"/>
<result property="phone" column="phone"/>
<result property="enabled" column="enabled"/>
<result property="gender" column="gender"/>
<result property="avatarPath" column="avatar_path"/>
<result property="pwdResetTime" column="pwd_reset_time"/>
<result property="userLevel" column="user_level"/>
<result property="pid" column="pid"/>
<result property="admin" column="is_admin"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<!-- 角色字段 -->
<collection property="roles" ofType="com.fuyuanshen.modules.system.domain.Role">
<id column="role_id" property="id"/>
<result column="role_name" property="name"/>
<result column="role_level" property="level"/>
<result column="role_data_scope" property="dataScope"/>
</collection>
</resultMap>
<select id="appFindByUsername" resultMap="APPUserWithRoleMap">
SELECT
u1.app_user_id,
u1.username,
u1.nick_name,
u1.email,
u1.phone,
u1.enabled,
u1.gender,
u1.avatar_path,
u1.pwd_reset_time,
u1.user_level,
u1.pid,
u1.is_admin,
u1.create_time,
u1.update_time,
r.name AS role_name,
r.level AS role_level,
r.description AS role_description,
r.data_scope AS role_data_scope
FROM app_user u1
LEFT JOIN app_role r ON u1.user_level = r.level
WHERE u1.username = #{username}
</select>
</mapper>