feat: 用户管理增强 - username字段、注销释放唯一索引、禁用清除缓存

This commit is contained in:
OpenCode
2026-07-22 18:22:16 +08:00
parent 453514017c
commit f5205ef0e9
7 changed files with 169 additions and 12 deletions
+7
View File
@@ -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 cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONObject;
import com.cool.core.annotation.CoolRestController;
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.service.UserInfoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
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 = "用户信息")
@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"})
public class AdminUserInfoController extends BaseController<UserInfoService, UserInfoEntity> {
private final UserInfoService userInfoService;
private final CoolCache coolCache;
@Override
protected void init(HttpServletRequest request, JSONObject requestParams) {
setPageOption(createOp().fieldEq(USER_INFO_ENTITY.STATUS, USER_INFO_ENTITY.GENDER,
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) {
UserInfoEntity infoEntity = requestParams.toBean(UserInfoEntity.class);
infoEntity.setId(CoolSecurityUtil.getCurrentUserId());
infoEntity.setUsername(null);
infoEntity.setEmail(null);
return R.ok(
userInfoService.updateById(infoEntity)
);
@@ -67,4 +69,20 @@ public class AppUserInfoController {
userInfoService.bindPhone(CoolSecurityUtil.getCurrentUserId(), phone, code);
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 = "邮箱")
private String email;
@UniIndex
@ColumnDefine(comment = "用户名")
private String username;
@ColumnDefine(comment = "性别 0-未知 1-男 2-女", defaultValue = "0")
private Integer gender;
@@ -25,4 +25,24 @@ public interface UserInfoService extends BaseService<UserInfoEntity> {
* 绑定手机号
*/
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;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.crypto.digest.MD5;
import com.cool.core.base.BaseServiceImpl;
import com.cool.core.exception.CoolPreconditions;
import com.cool.modules.user.entity.UserInfoEntity;
import com.cool.modules.user.mapper.UserInfoMapper;
import com.cool.modules.user.service.UserInfoService;
import com.cool.modules.user.util.UserSmsUtil;
import com.cool.modules.user.util.UserSmsUtil.SendSceneEnum;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -41,6 +44,29 @@ public class UserInfoServiceImpl extends BaseServiceImpl<UserInfoMapper, UserInf
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
public void bindPhone(Long userId, String phone, String code) {
userSmsUtil.checkVerifyCode(phone, code, SendSceneEnum.ALL);
@@ -49,4 +75,30 @@ public class UserInfoServiceImpl extends BaseServiceImpl<UserInfoMapper, UserInf
info.setPhone(phone);
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.setAvatarUrl(userWxEntity.getAvatarUrl());
userInfoEntity.setUnionid(unionId);
userInfoEntity.setStatus(1);
userInfoEntity.save();
}
return generateToken(userInfoEntity, null);
@@ -144,6 +145,7 @@ public class UserLoginServiceImpl implements UserLoginService {
UserInfoEntity userInfoEntity = userInfoService.getOne(
QueryWrapper.create().eq(UserInfoEntity::getEmail, email));
CoolPreconditions.checkEmpty(userInfoEntity, "邮箱或密码错误");
CoolPreconditions.check(!ObjUtil.equals(userInfoEntity.getStatus(), 1), "账号已被禁用或注销");
if (userInfoEntity.getPassword().equals(MD5.create().digestHex(password))) {
return generateToken(userInfoEntity, null);
}
@@ -156,8 +158,9 @@ public class UserLoginServiceImpl implements UserLoginService {
CoolPreconditions.checkEmpty(username, "用户名不能为空");
CoolPreconditions.checkEmpty(password, "密码不能为空");
UserInfoEntity userInfoEntity = userInfoService.getOne(
QueryWrapper.create().eq(UserInfoEntity::getNickName, username));
QueryWrapper.create().eq(UserInfoEntity::getUsername, username));
CoolPreconditions.checkEmpty(userInfoEntity, "用户名或密码错误");
CoolPreconditions.check(!ObjUtil.equals(userInfoEntity.getStatus(), 1), "账号已被禁用或注销");
if (userInfoEntity.getPassword().equals(MD5.create().digestHex(password))) {
return generateToken(userInfoEntity, null);
}
@@ -170,13 +173,15 @@ public class UserLoginServiceImpl implements UserLoginService {
CoolPreconditions.checkEmpty(email, "邮箱不能为空");
CoolPreconditions.checkEmpty(password, "密码不能为空");
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), "该邮箱已注册");
UserInfoEntity userInfoEntity = new UserInfoEntity();
userInfoEntity.setEmail(email);
userInfoEntity.setPassword(MD5.create().digestHex(password));
userInfoEntity.setNickName(ObjUtil.isNotEmpty(nickName) ? nickName : email.split("@")[0]);
userInfoEntity.setNickName(generateRandomNickname());
userInfoEntity.setLoginType("2");
userInfoEntity.setStatus(1);
userInfoEntity.save();
return generateToken(userInfoEntity, null);
}
@@ -186,12 +191,15 @@ public class UserLoginServiceImpl implements UserLoginService {
CoolPreconditions.checkEmpty(username, "用户名不能为空");
CoolPreconditions.checkEmpty(password, "密码不能为空");
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), "该用户名已注册");
UserInfoEntity userInfoEntity = new UserInfoEntity();
userInfoEntity.setNickName(username);
userInfoEntity.setUsername(username);
userInfoEntity.setPassword(MD5.create().digestHex(password));
userInfoEntity.setNickName(generateRandomNickname());
userInfoEntity.setLoginType("2");
userInfoEntity.setStatus(1);
userInfoEntity.save();
return generateToken(userInfoEntity, null);
}
@@ -206,8 +214,8 @@ public class UserLoginServiceImpl implements UserLoginService {
if (ObjUtil.isEmpty(userInfoEntity)) {
userInfoEntity = new UserInfoEntity();
userInfoEntity.setPhone(phone);
// 生成随机昵称
userInfoEntity.setNickName(generateRandomNickname());
userInfoEntity.setStatus(1);
userInfoEntity.save();
}
return generateToken(userInfoEntity, null);
@@ -218,10 +226,9 @@ public class UserLoginServiceImpl implements UserLoginService {
* @return 生成的昵称
*/
private String generateRandomNickname() {
// 定义昵称的长度
int length = 8;
// 生成随机字符串
return RandomUtil.randomString(length);
long timestamp = System.currentTimeMillis() % 10000000L;
int random = RandomUtil.randomInt(0, 10);
return "u" + String.format("%07d", timestamp) + random;
}
/**
* 生成token