bug修改
This commit is contained in:
1 parent
855327ebab
commit
1c2006d05c
12 files changed
+158
-2
No files matched your search
+6
@@ -387,6 +387,12 @@ public class GlobalExceptionHandler {
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
|
||||
}
|
||||
// 7.1 会员互动消息
|
||||
if (message.contains("member_interaction")) {
|
||||
log.error("[会员互动消息表 member_interaction 未导入][请执行 friend_back/sql/mysql/member/member_interaction.sql]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"会员互动消息表未导入,请执行 sql/mysql/member/member_interaction.sql");
|
||||
}
|
||||
// 8. AI 大模型
|
||||
if (message.contains("ai_")) {
|
||||
log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
|
||||
|
||||
+8
@@ -71,6 +71,14 @@ public class MemberUserController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新会员用户状态(禁用后踢出 App 登录,且无法再登录)")
|
||||
@PreAuthorize("@ss.hasPermission('member:user:update')")
|
||||
public CommonResult<Boolean> updateUserStatus(@Valid @RequestBody MemberUserUpdateStatusReqVO reqVO) {
|
||||
memberUserService.updateUserStatus(reqVO.getId(), reqVO.getStatus());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/fill-missing-user-codes")
|
||||
@Operation(summary = "为历史会员补齐用户 Code")
|
||||
// @PreAuthorize("@ss.hasPermission('member:user:update')")
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.member.controller.admin.user.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 会员用户更新状态 Request VO")
|
||||
@Data
|
||||
public class MemberUserUpdateStatusReqVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "状态,见 CommonStatusEnum 枚举。0 开启,1 禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "状态不能为空")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+17
-1
@@ -173,6 +173,11 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
|
||||
private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile,
|
||||
LoginLogTypeEnum logType, String openid) {
|
||||
// 统一拦截:已禁用账号不可登录 / 注册后自动登录
|
||||
if (CommonStatusEnum.isDisable(user.getStatus())) {
|
||||
createLoginLog(user.getId(), mobile, logType, LoginResultEnum.USER_DISABLED);
|
||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||
}
|
||||
// 插入登陆日志
|
||||
createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
|
||||
// 创建 Token 令牌
|
||||
@@ -339,6 +344,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
public AppAuthLoginRespVO refreshToken(String refreshToken) {
|
||||
OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
||||
|
||||
MemberUserDO user = userService.getUser(accessTokenDO.getUserId());
|
||||
if (user == null || CommonStatusEnum.isDisable(user.getStatus())) {
|
||||
// 禁用后禁止续期,并清掉该用户全部 token
|
||||
oauth2TokenApi.removeAccessTokenByUserId(accessTokenDO.getUserId(), getUserType().getValue());
|
||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||
}
|
||||
|
||||
MemberUserUpdateReqVO update = new MemberUserUpdateReqVO();
|
||||
update.setId(accessTokenDO.getUserId());
|
||||
update.setAppRefreshDate(LocalDateTime.now());
|
||||
@@ -371,9 +383,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO.ChatToken reissueChatToken(Long userId) {
|
||||
if (userService.getUser(userId) == null) {
|
||||
MemberUserDO user = userService.getUser(userId);
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
if (CommonStatusEnum.isDisable(user.getStatus())) {
|
||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||
}
|
||||
if (!userService.syncChatUser(userId)) {
|
||||
throw exception(CHAT_TOKEN_CREATE_FAILED);
|
||||
}
|
||||
|
||||
+8
@@ -147,6 +147,14 @@ public interface MemberUserService {
|
||||
*/
|
||||
void updateUser(@Valid MemberUserUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 【管理员】更新会员用户状态(禁用时踢出全部登录态)
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param status 状态,见 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
|
||||
*/
|
||||
void updateUserStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 【管理员】获得会员用户分页
|
||||
*
|
||||
|
||||
+42
-1
@@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.member.enums.tag.MemberRecentTagEnum;
|
||||
import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer;
|
||||
import cn.iocoder.yudao.module.member.service.user.BO.MemberExtBO;
|
||||
import cn.iocoder.yudao.module.member.service.user.BO.MemberWechatBO;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
|
||||
@@ -100,6 +101,8 @@ public class MemberUserServiceImpl implements MemberUserService {
|
||||
private ChatApi chatApi;
|
||||
@Resource
|
||||
private TradeBrokerageUserApi tradeBrokerageUserApi;
|
||||
@Resource
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
@Override
|
||||
public MemberUserDO getUserByMobile(String mobile) {
|
||||
@@ -416,7 +419,7 @@ public class MemberUserServiceImpl implements MemberUserService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateUser(MemberUserUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateUserExists(updateReqVO.getId());
|
||||
MemberUserDO dbUser = validateUserExists(updateReqVO.getId());
|
||||
// 校验手机唯一
|
||||
validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());
|
||||
|
||||
@@ -446,6 +449,44 @@ public class MemberUserServiceImpl implements MemberUserService {
|
||||
// 更新用户新人tag
|
||||
memberRecentTagService.createOrUpdateNewPersonTag(updateReqVO.getId());
|
||||
memberUserMapper.updateById(updateObj);
|
||||
|
||||
// 变为禁用:踢出全部 App 登录态
|
||||
Integer newStatus = updateObj.getStatus();
|
||||
if (newStatus == null && updateReqVO.getStatus() != null) {
|
||||
newStatus = updateReqVO.getStatus().intValue();
|
||||
}
|
||||
if (CommonStatusEnum.isDisable(newStatus) && !CommonStatusEnum.isDisable(dbUser.getStatus())) {
|
||||
kickMemberLogin(updateReqVO.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateUserStatus(Long id, Integer status) {
|
||||
MemberUserDO dbUser = validateUserExists(id);
|
||||
if (ObjectUtil.equal(dbUser.getStatus(), status)) {
|
||||
// 已禁用时再点一次,仍强制踢一次,避免漏踢
|
||||
if (CommonStatusEnum.isDisable(status)) {
|
||||
kickMemberLogin(id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
memberUserMapper.updateById(new MemberUserDO().setId(id).setStatus(status));
|
||||
if (CommonStatusEnum.isDisable(status)) {
|
||||
kickMemberLogin(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理会员全部 OAuth2 access + refresh,App 下次请求会 401 并退出登录。
|
||||
*/
|
||||
private void kickMemberLogin(Long userId) {
|
||||
try {
|
||||
oauth2TokenApi.removeAccessTokenByUserId(userId, UserTypeEnum.MEMBER.getValue());
|
||||
} catch (Exception e) {
|
||||
log.warn("踢出会员登录态失败, userId={}", userId, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
+9
@@ -37,6 +37,15 @@ public interface OAuth2TokenApi {
|
||||
*/
|
||||
OAuth2AccessTokenRespDTO removeAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 移除指定用户的全部访问令牌与刷新令牌(踢下线)
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userType 用户类型
|
||||
* @return 删除的访问令牌数量
|
||||
*/
|
||||
int removeAccessTokenByUserId(Long userId, Integer userType);
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*
|
||||
|
||||
+5
@@ -40,6 +40,11 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
|
||||
return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeAccessTokenByUserId(Long userId, Integer userType) {
|
||||
return oauth2TokenService.removeAccessTokenByUserId(userId, userType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId) {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
|
||||
|
||||
+6
@@ -23,6 +23,12 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO
|
||||
return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
|
||||
}
|
||||
|
||||
default List<OAuth2AccessTokenDO> selectListByUserIdAndUserType(Long userId, Integer userType) {
|
||||
return selectList(new LambdaQueryWrapperX<OAuth2AccessTokenDO>()
|
||||
.eq(OAuth2AccessTokenDO::getUserId, userId)
|
||||
.eq(OAuth2AccessTokenDO::getUserType, userType));
|
||||
}
|
||||
|
||||
default PageResult<OAuth2AccessTokenDO> selectPage(OAuth2AccessTokenPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2AccessTokenDO>()
|
||||
.eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId())
|
||||
|
||||
+6
@@ -14,6 +14,12 @@ public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshToken
|
||||
.eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
|
||||
}
|
||||
|
||||
default int deleteByUserIdAndUserType(Long userId, Integer userType) {
|
||||
return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
|
||||
.eq(OAuth2RefreshTokenDO::getUserId, userId)
|
||||
.eq(OAuth2RefreshTokenDO::getUserType, userType));
|
||||
}
|
||||
|
||||
@TenantIgnore // 获取 token 的时候,需要忽略租户编号。原因是:一些场景下,可能不会传递 tenant-id 请求头,例如说文件上传、积木报表等等
|
||||
default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
|
||||
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
||||
|
||||
+9
@@ -69,6 +69,15 @@ public interface OAuth2TokenService {
|
||||
*/
|
||||
OAuth2AccessTokenDO removeAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 移除指定用户的全部访问令牌与刷新令牌(踢下线)
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userType 用户类型
|
||||
* @return 删除的访问令牌数量
|
||||
*/
|
||||
int removeAccessTokenByUserId(Long userId, Integer userType);
|
||||
|
||||
/**
|
||||
* 获得访问令牌分页
|
||||
*
|
||||
|
||||
+19
@@ -153,6 +153,25 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int removeAccessTokenByUserId(Long userId, Integer userType) {
|
||||
if (userId == null || userType == null) {
|
||||
return 0;
|
||||
}
|
||||
List<OAuth2AccessTokenDO> accessTokenDOs =
|
||||
oauth2AccessTokenMapper.selectListByUserIdAndUserType(userId, userType);
|
||||
int removed = 0;
|
||||
if (CollUtil.isNotEmpty(accessTokenDOs)) {
|
||||
oauth2AccessTokenMapper.deleteByIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId));
|
||||
oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken));
|
||||
removed = accessTokenDOs.size();
|
||||
}
|
||||
// 清理可能残留的 refresh token(无对应 access 的场景)
|
||||
oauth2RefreshTokenMapper.deleteByUserIdAndUserType(userId, userType);
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) {
|
||||
return oauth2AccessTokenMapper.selectPage(reqVO);
|
||||
|
||||
Reference in new issue
Block a user