forked from github/cool-admin-java
feat: 用户管理增强 - username字段、注销释放唯一索引、禁用清除缓存
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
.vscode
|
||||||
|
.DS_Store
|
||||||
@@ -2,23 +2,72 @@ package com.cool.modules.user.controller.admin;
|
|||||||
|
|
||||||
import static com.cool.modules.user.entity.table.UserInfoEntityTableDef.USER_INFO_ENTITY;
|
import static com.cool.modules.user.entity.table.UserInfoEntityTableDef.USER_INFO_ENTITY;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import com.cool.core.annotation.CoolRestController;
|
import com.cool.core.annotation.CoolRestController;
|
||||||
import com.cool.core.base.BaseController;
|
import com.cool.core.base.BaseController;
|
||||||
|
import com.cool.core.cache.CoolCache;
|
||||||
|
import com.cool.core.request.R;
|
||||||
|
import com.cool.core.security.jwt.JwtUser;
|
||||||
import com.cool.modules.user.entity.UserInfoEntity;
|
import com.cool.modules.user.entity.UserInfoEntity;
|
||||||
import com.cool.modules.user.service.UserInfoService;
|
import com.cool.modules.user.service.UserInfoService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestAttribute;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
@Tag(name = "用户信息", description = "用户信息")
|
@Tag(name = "用户信息", description = "用户信息")
|
||||||
@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"})
|
@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"})
|
||||||
public class AdminUserInfoController extends BaseController<UserInfoService, UserInfoEntity> {
|
public class AdminUserInfoController extends BaseController<UserInfoService, UserInfoEntity> {
|
||||||
|
|
||||||
|
private final UserInfoService userInfoService;
|
||||||
|
private final CoolCache coolCache;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||||
|
|
||||||
setPageOption(createOp().fieldEq(USER_INFO_ENTITY.STATUS, USER_INFO_ENTITY.GENDER,
|
setPageOption(createOp().fieldEq(USER_INFO_ENTITY.STATUS, USER_INFO_ENTITY.GENDER,
|
||||||
USER_INFO_ENTITY.LOGIN_TYPE)
|
USER_INFO_ENTITY.LOGIN_TYPE)
|
||||||
.keyWordLikeFields(USER_INFO_ENTITY.NICK_NAME, USER_INFO_ENTITY.PHONE));
|
.keyWordLikeFields(USER_INFO_ENTITY.NICK_NAME, USER_INFO_ENTITY.PHONE,
|
||||||
|
USER_INFO_ENTITY.USERNAME, USER_INFO_ENTITY.EMAIL));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PostMapping("/update")
|
||||||
|
protected R update(@RequestBody UserInfoEntity t, @RequestAttribute JSONObject requestParams) {
|
||||||
|
Long id = t.getId();
|
||||||
|
if (id != null) {
|
||||||
|
UserInfoEntity before = userInfoService.getById(id);
|
||||||
|
if (before != null && !ObjUtil.equals(before.getStatus(), t.getStatus())) {
|
||||||
|
coolCache.del("app:userDetails:" + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.update(t, requestParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "注销用户")
|
||||||
|
@PostMapping("/logoff")
|
||||||
|
public R logoff(@RequestBody JSONObject requestParams) {
|
||||||
|
Long id = requestParams.getLong("id");
|
||||||
|
userInfoService.adminLogoff(id);
|
||||||
|
coolCache.del("app:userDetails:" + id);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PostMapping("/delete")
|
||||||
|
protected R delete(HttpServletRequest request, @RequestBody Map<String, Object> params,
|
||||||
|
@RequestAttribute JSONObject requestParams) {
|
||||||
|
Long[] ids = Convert.toLongArray(getIds(params));
|
||||||
|
if (ids != null) {
|
||||||
|
Arrays.stream(ids).forEach(userInfoService::checkLogoffBeforeDelete);
|
||||||
|
}
|
||||||
|
return super.delete(request, params, requestParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class AppUserInfoController {
|
|||||||
public R updatePerson(@RequestAttribute JSONObject requestParams) {
|
public R updatePerson(@RequestAttribute JSONObject requestParams) {
|
||||||
UserInfoEntity infoEntity = requestParams.toBean(UserInfoEntity.class);
|
UserInfoEntity infoEntity = requestParams.toBean(UserInfoEntity.class);
|
||||||
infoEntity.setId(CoolSecurityUtil.getCurrentUserId());
|
infoEntity.setId(CoolSecurityUtil.getCurrentUserId());
|
||||||
|
infoEntity.setUsername(null);
|
||||||
|
infoEntity.setEmail(null);
|
||||||
return R.ok(
|
return R.ok(
|
||||||
userInfoService.updateById(infoEntity)
|
userInfoService.updateById(infoEntity)
|
||||||
);
|
);
|
||||||
@@ -67,4 +69,20 @@ public class AppUserInfoController {
|
|||||||
userInfoService.bindPhone(CoolSecurityUtil.getCurrentUserId(), phone, code);
|
userInfoService.bindPhone(CoolSecurityUtil.getCurrentUserId(), phone, code);
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "绑定邮箱")
|
||||||
|
@PostMapping("/bindEmail")
|
||||||
|
public R bindEmail(@RequestAttribute JSONObject requestParams) {
|
||||||
|
String email = requestParams.getStr("email");
|
||||||
|
userInfoService.bindEmail(CoolSecurityUtil.getCurrentUserId(), email);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "绑定用户名")
|
||||||
|
@PostMapping("/bindUsername")
|
||||||
|
public R bindUsername(@RequestAttribute JSONObject requestParams) {
|
||||||
|
String username = requestParams.getStr("username");
|
||||||
|
userInfoService.bindUsername(CoolSecurityUtil.getCurrentUserId(), username);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ public class UserInfoEntity extends BaseEntity<UserInfoEntity> {
|
|||||||
@ColumnDefine(comment = "邮箱")
|
@ColumnDefine(comment = "邮箱")
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
|
@UniIndex
|
||||||
|
@ColumnDefine(comment = "用户名")
|
||||||
|
private String username;
|
||||||
|
|
||||||
@ColumnDefine(comment = "性别 0-未知 1-男 2-女", defaultValue = "0")
|
@ColumnDefine(comment = "性别 0-未知 1-男 2-女", defaultValue = "0")
|
||||||
private Integer gender;
|
private Integer gender;
|
||||||
|
|
||||||
|
|||||||
@@ -25,4 +25,24 @@ public interface UserInfoService extends BaseService<UserInfoEntity> {
|
|||||||
* 绑定手机号
|
* 绑定手机号
|
||||||
*/
|
*/
|
||||||
void bindPhone(Long currentUserId, String phone, String code);
|
void bindPhone(Long currentUserId, String phone, String code);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定邮箱
|
||||||
|
*/
|
||||||
|
void bindEmail(Long currentUserId, String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定用户名
|
||||||
|
*/
|
||||||
|
void bindUsername(Long currentUserId, String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销(管理端)
|
||||||
|
*/
|
||||||
|
void adminLogoff(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除前校验:必须已注销
|
||||||
|
*/
|
||||||
|
void checkLogoffBeforeDelete(Long userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package com.cool.modules.user.service.impl;
|
package com.cool.modules.user.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.crypto.digest.MD5;
|
import cn.hutool.crypto.digest.MD5;
|
||||||
import com.cool.core.base.BaseServiceImpl;
|
import com.cool.core.base.BaseServiceImpl;
|
||||||
|
import com.cool.core.exception.CoolPreconditions;
|
||||||
import com.cool.modules.user.entity.UserInfoEntity;
|
import com.cool.modules.user.entity.UserInfoEntity;
|
||||||
import com.cool.modules.user.mapper.UserInfoMapper;
|
import com.cool.modules.user.mapper.UserInfoMapper;
|
||||||
import com.cool.modules.user.service.UserInfoService;
|
import com.cool.modules.user.service.UserInfoService;
|
||||||
import com.cool.modules.user.util.UserSmsUtil;
|
import com.cool.modules.user.util.UserSmsUtil;
|
||||||
import com.cool.modules.user.util.UserSmsUtil.SendSceneEnum;
|
import com.cool.modules.user.util.UserSmsUtil.SendSceneEnum;
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -41,6 +44,29 @@ public class UserInfoServiceImpl extends BaseServiceImpl<UserInfoMapper, UserInf
|
|||||||
info.updateById();
|
info.updateById();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void adminLogoff(Long userId) {
|
||||||
|
UserInfoEntity info = getById(userId);
|
||||||
|
CoolPreconditions.checkEmpty(info, "用户不存在");
|
||||||
|
CoolPreconditions.check(ObjUtil.equals(info.getStatus(), 2), "该用户已注销");
|
||||||
|
info.setStatus(2);
|
||||||
|
info.setNickName("已注销-00" + userId);
|
||||||
|
if (info.getUsername() != null) {
|
||||||
|
info.setUsername("__logoff__" + userId + "__" + System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
if (info.getEmail() != null) {
|
||||||
|
info.setEmail("__logoff__" + userId + "__" + System.currentTimeMillis() + "@deleted");
|
||||||
|
}
|
||||||
|
info.updateById();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkLogoffBeforeDelete(Long userId) {
|
||||||
|
UserInfoEntity info = getById(userId);
|
||||||
|
CoolPreconditions.checkEmpty(info, "用户不存在");
|
||||||
|
CoolPreconditions.check(!ObjUtil.equals(info.getStatus(), 2), "只能删除已注销的用户");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindPhone(Long userId, String phone, String code) {
|
public void bindPhone(Long userId, String phone, String code) {
|
||||||
userSmsUtil.checkVerifyCode(phone, code, SendSceneEnum.ALL);
|
userSmsUtil.checkVerifyCode(phone, code, SendSceneEnum.ALL);
|
||||||
@@ -49,4 +75,30 @@ public class UserInfoServiceImpl extends BaseServiceImpl<UserInfoMapper, UserInf
|
|||||||
info.setPhone(phone);
|
info.setPhone(phone);
|
||||||
info.updateById();
|
info.updateById();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindEmail(Long userId, String email) {
|
||||||
|
UserInfoEntity info = getById(userId);
|
||||||
|
CoolPreconditions.checkEmpty(info, "用户不存在");
|
||||||
|
CoolPreconditions.check(ObjUtil.isNotEmpty(info.getEmail()), "该用户已绑定邮箱");
|
||||||
|
UserInfoEntity existing = getOne(QueryWrapper.create()
|
||||||
|
.eq(UserInfoEntity::getEmail, email)
|
||||||
|
.ne(UserInfoEntity::getStatus, 2));
|
||||||
|
CoolPreconditions.check(ObjUtil.isNotEmpty(existing), "该邮箱已被其他用户使用");
|
||||||
|
info.setEmail(email);
|
||||||
|
info.updateById();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindUsername(Long userId, String username) {
|
||||||
|
UserInfoEntity info = getById(userId);
|
||||||
|
CoolPreconditions.checkEmpty(info, "用户不存在");
|
||||||
|
CoolPreconditions.check(ObjUtil.isNotEmpty(info.getUsername()), "该用户已绑定用户名");
|
||||||
|
UserInfoEntity existing = getOne(QueryWrapper.create()
|
||||||
|
.eq(UserInfoEntity::getUsername, username)
|
||||||
|
.ne(UserInfoEntity::getStatus, 2));
|
||||||
|
CoolPreconditions.check(ObjUtil.isNotEmpty(existing), "该用户名已被其他用户使用");
|
||||||
|
info.setUsername(username);
|
||||||
|
info.updateById();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ public class UserLoginServiceImpl implements UserLoginService {
|
|||||||
userInfoEntity.setGender(userWxEntity.getGender());
|
userInfoEntity.setGender(userWxEntity.getGender());
|
||||||
userInfoEntity.setAvatarUrl(userWxEntity.getAvatarUrl());
|
userInfoEntity.setAvatarUrl(userWxEntity.getAvatarUrl());
|
||||||
userInfoEntity.setUnionid(unionId);
|
userInfoEntity.setUnionid(unionId);
|
||||||
|
userInfoEntity.setStatus(1);
|
||||||
userInfoEntity.save();
|
userInfoEntity.save();
|
||||||
}
|
}
|
||||||
return generateToken(userInfoEntity, null);
|
return generateToken(userInfoEntity, null);
|
||||||
@@ -144,6 +145,7 @@ public class UserLoginServiceImpl implements UserLoginService {
|
|||||||
UserInfoEntity userInfoEntity = userInfoService.getOne(
|
UserInfoEntity userInfoEntity = userInfoService.getOne(
|
||||||
QueryWrapper.create().eq(UserInfoEntity::getEmail, email));
|
QueryWrapper.create().eq(UserInfoEntity::getEmail, email));
|
||||||
CoolPreconditions.checkEmpty(userInfoEntity, "邮箱或密码错误");
|
CoolPreconditions.checkEmpty(userInfoEntity, "邮箱或密码错误");
|
||||||
|
CoolPreconditions.check(!ObjUtil.equals(userInfoEntity.getStatus(), 1), "账号已被禁用或注销");
|
||||||
if (userInfoEntity.getPassword().equals(MD5.create().digestHex(password))) {
|
if (userInfoEntity.getPassword().equals(MD5.create().digestHex(password))) {
|
||||||
return generateToken(userInfoEntity, null);
|
return generateToken(userInfoEntity, null);
|
||||||
}
|
}
|
||||||
@@ -156,8 +158,9 @@ public class UserLoginServiceImpl implements UserLoginService {
|
|||||||
CoolPreconditions.checkEmpty(username, "用户名不能为空");
|
CoolPreconditions.checkEmpty(username, "用户名不能为空");
|
||||||
CoolPreconditions.checkEmpty(password, "密码不能为空");
|
CoolPreconditions.checkEmpty(password, "密码不能为空");
|
||||||
UserInfoEntity userInfoEntity = userInfoService.getOne(
|
UserInfoEntity userInfoEntity = userInfoService.getOne(
|
||||||
QueryWrapper.create().eq(UserInfoEntity::getNickName, username));
|
QueryWrapper.create().eq(UserInfoEntity::getUsername, username));
|
||||||
CoolPreconditions.checkEmpty(userInfoEntity, "用户名或密码错误");
|
CoolPreconditions.checkEmpty(userInfoEntity, "用户名或密码错误");
|
||||||
|
CoolPreconditions.check(!ObjUtil.equals(userInfoEntity.getStatus(), 1), "账号已被禁用或注销");
|
||||||
if (userInfoEntity.getPassword().equals(MD5.create().digestHex(password))) {
|
if (userInfoEntity.getPassword().equals(MD5.create().digestHex(password))) {
|
||||||
return generateToken(userInfoEntity, null);
|
return generateToken(userInfoEntity, null);
|
||||||
}
|
}
|
||||||
@@ -170,13 +173,15 @@ public class UserLoginServiceImpl implements UserLoginService {
|
|||||||
CoolPreconditions.checkEmpty(email, "邮箱不能为空");
|
CoolPreconditions.checkEmpty(email, "邮箱不能为空");
|
||||||
CoolPreconditions.checkEmpty(password, "密码不能为空");
|
CoolPreconditions.checkEmpty(password, "密码不能为空");
|
||||||
UserInfoEntity existing = userInfoService.getOne(
|
UserInfoEntity existing = userInfoService.getOne(
|
||||||
QueryWrapper.create().eq(UserInfoEntity::getEmail, email));
|
QueryWrapper.create().eq(UserInfoEntity::getEmail, email)
|
||||||
|
.ne(UserInfoEntity::getStatus, 2));
|
||||||
CoolPreconditions.check(ObjUtil.isNotEmpty(existing), "该邮箱已注册");
|
CoolPreconditions.check(ObjUtil.isNotEmpty(existing), "该邮箱已注册");
|
||||||
UserInfoEntity userInfoEntity = new UserInfoEntity();
|
UserInfoEntity userInfoEntity = new UserInfoEntity();
|
||||||
userInfoEntity.setEmail(email);
|
userInfoEntity.setEmail(email);
|
||||||
userInfoEntity.setPassword(MD5.create().digestHex(password));
|
userInfoEntity.setPassword(MD5.create().digestHex(password));
|
||||||
userInfoEntity.setNickName(ObjUtil.isNotEmpty(nickName) ? nickName : email.split("@")[0]);
|
userInfoEntity.setNickName(generateRandomNickname());
|
||||||
userInfoEntity.setLoginType("2");
|
userInfoEntity.setLoginType("2");
|
||||||
|
userInfoEntity.setStatus(1);
|
||||||
userInfoEntity.save();
|
userInfoEntity.save();
|
||||||
return generateToken(userInfoEntity, null);
|
return generateToken(userInfoEntity, null);
|
||||||
}
|
}
|
||||||
@@ -186,12 +191,15 @@ public class UserLoginServiceImpl implements UserLoginService {
|
|||||||
CoolPreconditions.checkEmpty(username, "用户名不能为空");
|
CoolPreconditions.checkEmpty(username, "用户名不能为空");
|
||||||
CoolPreconditions.checkEmpty(password, "密码不能为空");
|
CoolPreconditions.checkEmpty(password, "密码不能为空");
|
||||||
UserInfoEntity existing = userInfoService.getOne(
|
UserInfoEntity existing = userInfoService.getOne(
|
||||||
QueryWrapper.create().eq(UserInfoEntity::getNickName, username));
|
QueryWrapper.create().eq(UserInfoEntity::getUsername, username)
|
||||||
|
.ne(UserInfoEntity::getStatus, 2));
|
||||||
CoolPreconditions.check(ObjUtil.isNotEmpty(existing), "该用户名已注册");
|
CoolPreconditions.check(ObjUtil.isNotEmpty(existing), "该用户名已注册");
|
||||||
UserInfoEntity userInfoEntity = new UserInfoEntity();
|
UserInfoEntity userInfoEntity = new UserInfoEntity();
|
||||||
userInfoEntity.setNickName(username);
|
userInfoEntity.setUsername(username);
|
||||||
userInfoEntity.setPassword(MD5.create().digestHex(password));
|
userInfoEntity.setPassword(MD5.create().digestHex(password));
|
||||||
|
userInfoEntity.setNickName(generateRandomNickname());
|
||||||
userInfoEntity.setLoginType("2");
|
userInfoEntity.setLoginType("2");
|
||||||
|
userInfoEntity.setStatus(1);
|
||||||
userInfoEntity.save();
|
userInfoEntity.save();
|
||||||
return generateToken(userInfoEntity, null);
|
return generateToken(userInfoEntity, null);
|
||||||
}
|
}
|
||||||
@@ -206,8 +214,8 @@ public class UserLoginServiceImpl implements UserLoginService {
|
|||||||
if (ObjUtil.isEmpty(userInfoEntity)) {
|
if (ObjUtil.isEmpty(userInfoEntity)) {
|
||||||
userInfoEntity = new UserInfoEntity();
|
userInfoEntity = new UserInfoEntity();
|
||||||
userInfoEntity.setPhone(phone);
|
userInfoEntity.setPhone(phone);
|
||||||
// 生成随机昵称
|
|
||||||
userInfoEntity.setNickName(generateRandomNickname());
|
userInfoEntity.setNickName(generateRandomNickname());
|
||||||
|
userInfoEntity.setStatus(1);
|
||||||
userInfoEntity.save();
|
userInfoEntity.save();
|
||||||
}
|
}
|
||||||
return generateToken(userInfoEntity, null);
|
return generateToken(userInfoEntity, null);
|
||||||
@@ -218,10 +226,9 @@ public class UserLoginServiceImpl implements UserLoginService {
|
|||||||
* @return 生成的昵称
|
* @return 生成的昵称
|
||||||
*/
|
*/
|
||||||
private String generateRandomNickname() {
|
private String generateRandomNickname() {
|
||||||
// 定义昵称的长度
|
long timestamp = System.currentTimeMillis() % 10000000L;
|
||||||
int length = 8;
|
int random = RandomUtil.randomInt(0, 10);
|
||||||
// 生成随机字符串
|
return "u" + String.format("%07d", timestamp) + random;
|
||||||
return RandomUtil.randomString(length);
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 生成token
|
* 生成token
|
||||||
|
|||||||
Reference in New Issue
Block a user