Commit a90b8d05 by 真的三个金的鑫

fix: clarify user-team membership and harden login/org APIs.

Show org names on user list, reclaim orphaned accounts into the current team, make device-org assignment idempotent, and fall back when token org is missing so members can log in with the correct team.

Co-authored-by: Cursor <cursoragent@cursor.com>
parent edb90765
......@@ -50,11 +50,19 @@ public class CustomClaim {
try {
Field[] declaredFields = this.getClass().getDeclaredFields();
for (Field field : declaredFields) {
// 跳过静态字段(如 @Slf4j 的 log)
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
JsonAlias annotation = field.getAnnotation(JsonAlias.class);
field.setAccessible(true);
Object value = field.get(this);
if (value == null) {
continue;
}
// The value of key is named underscore.
map.put(annotation != null ? annotation.value()[0] : field.getName(),
field.get(this).toString());
value.toString());
}
} catch (IllegalAccessException e) {
log.info("CustomClaim converts failed. {}", this.toString());
......@@ -70,10 +78,16 @@ public class CustomClaim {
public CustomClaim (Map<String, Claim> claimMap) {
Field[] declaredFields = this.getClass().getDeclaredFields();
for (Field field : declaredFields) {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
field.setAccessible(true);
JsonAlias annotation = field.getAnnotation(JsonAlias.class);
Claim value = claimMap.get(annotation == null ? field.getName() : annotation.value()[0]);
if (value == null || value.isNull()) {
continue;
}
try {
Class<?> type = field.getType();
if (Integer.class.equals(type)) {
......
......@@ -20,6 +20,8 @@ import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.Optional;
import org.springframework.util.StringUtils;
import static com.dji.sample.common.util.SecurityUtils.*;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
......@@ -31,16 +33,34 @@ public class OrgController {
private IOrgService orgService;
/**
* Gets information about the workspace that the current user is in.
* @param request
* @return
* Gets information about the org that the current user is in.
* Token 中的 org 若已被删除,回退到当前工作区下可用团队,避免直接 failed。
*/
@GetMapping("/current")
public HttpResultResponse getCurrentOrg(HttpServletRequest request) {
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
Optional<OrgDTO> orgOpt = orgService.getOrgByOrgId(customClaim.getOrgId());
CustomClaim customClaim = (CustomClaim) request.getAttribute(TOKEN_CLAIM);
if (customClaim == null) {
return HttpResultResponse.error("未登录或登录已失效");
}
return orgOpt.isEmpty() ? HttpResultResponse.error() : HttpResultResponse.success(orgOpt.get());
String orgId = customClaim.getOrgId();
if (StringUtils.hasText(orgId)) {
Optional<OrgDTO> orgOpt = orgService.getOrgByOrgId(orgId);
if (orgOpt.isPresent()) {
return HttpResultResponse.success(orgOpt.get());
}
}
// 回退:当前工作区下找一个可用团队(优先 geoSys)
String workspaceId = customClaim.getWorkspaceId();
if (!StringUtils.hasText(workspaceId)) {
workspaceId = getWorkspaceId();
}
Optional<OrgDTO> fallback = orgService.getFallbackOrg(workspaceId, customClaim.getId(), aboveSysAdminRole());
if (fallback.isPresent()) {
return HttpResultResponse.success(fallback.get());
}
return HttpResultResponse.error("当前工作区下没有可用团队,请先创建或加入团队");
}
@GetMapping("/getOne")
......
......@@ -92,8 +92,8 @@ public class UserController {
@PostMapping("/{workspace_id}/addUser")
public HttpResultResponse addUser(@RequestBody UserEntity user,
@PathVariable("workspace_id") String workspaceId) {
userService.addUser(workspaceId, user);
return HttpResultResponse.success();
String action = userService.addUserToCurrentOrg(workspaceId, user);
return HttpResultResponse.success(action);
}
/**
......
......@@ -19,6 +19,6 @@ public interface IUserOrgMapper extends BaseMapper<UserOrgEntity> {
*/
@Select("SELECT uo.*, o.org_name FROM manage_user_org uo " +
"LEFT JOIN manage_org o ON uo.org_id = o.org_id " +
"WHERE uo.user_id = #{userId} AND uo.status = 1")
"WHERE uo.user_id = #{userId} AND (uo.status IS NULL OR uo.status = 1)")
List<UserOrgDTO> selectUserOrgs(@Param("userId") String userId);
}
......@@ -32,6 +32,11 @@ public class UserListDTO {
private Integer roleType;
/**
* 所属团队名称,多个用顿号拼接
*/
private String orgNames;
private String mqttUsername;
private String mqttPassword;
......
......@@ -22,6 +22,12 @@ public interface IOrgService extends IService<OrgEntity> {
*/
Optional<OrgDTO> getOrgByOrgId(String orgId);
/**
* Token 中团队失效时,回退查找工作区下可用团队。
* 优先 geoSys;非超管优先返回自己加入过的团队。
*/
Optional<OrgDTO> getFallbackOrg(String workspaceId, String userId, boolean sysAdmin);
PaginationData<OrgDTO> getOrgPage(OrgSearchParam param, Long page, Long pageSize);
OrgDTO addOrg(OrgDTO orgDTO);
......
......@@ -88,6 +88,12 @@ public interface IUserService extends IService<UserEntity> {
Boolean addUser(String workspaceId, UserEntity user);
/**
* 添加用户到当前团队。
* @return created=新建账号;joined=账号已存在,已加入当前团队
*/
String addUserToCurrentOrg(String workspaceId, UserEntity user);
/**
* Query user's details based on userId
* @param username
* @param workspaceId
......
......@@ -2049,14 +2049,35 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
if (!StringUtils.hasText(workspaceId) || !StringUtils.hasText(orgId)) {
return new ArrayList<>();
}
// 同时兼容 workspace_id 为空的历史脏数据,避免已分配设备重复插入
LambdaQueryWrapper<DeviceOrgEntity> deviceOrgQueryWrapper = new LambdaQueryWrapper<>();
deviceOrgQueryWrapper.eq(DeviceOrgEntity::getWorkspaceId, workspaceId);
deviceOrgQueryWrapper.eq(DeviceOrgEntity::getOrgId, orgId);
return deviceOrgService.list(deviceOrgQueryWrapper).stream()
.map(DeviceOrgEntity::getDeviceSn)
.filter(StringUtils::hasText)
.distinct()
.collect(Collectors.toList());
deviceOrgQueryWrapper.and(w -> w.eq(DeviceOrgEntity::getWorkspaceId, workspaceId)
.or().isNull(DeviceOrgEntity::getWorkspaceId)
.or().eq(DeviceOrgEntity::getWorkspaceId, ""));
List<DeviceOrgEntity> relations = deviceOrgService.list(deviceOrgQueryWrapper);
if (CollectionUtils.isEmpty(relations)) {
return new ArrayList<>();
}
Set<String> sns = new HashSet<>();
Set<Integer> needLookupIds = new HashSet<>();
for (DeviceOrgEntity rel : relations) {
if (StringUtils.hasText(rel.getDeviceSn())) {
sns.add(rel.getDeviceSn());
} else if (rel.getDeviceId() != null) {
needLookupIds.add(rel.getDeviceId());
}
}
if (!needLookupIds.isEmpty()) {
List<DeviceEntity> devices = this.listByIds(needLookupIds);
for (DeviceEntity device : devices) {
if (StringUtils.hasText(device.getDeviceSn())) {
sns.add(device.getDeviceSn());
}
}
}
return new ArrayList<>(sns);
}
private List<String> listUserDeviceSns(String workspaceId, String orgId, String userId) {
......@@ -2106,13 +2127,6 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
if (!StringUtils.hasText(deviceSn) || !StringUtils.hasText(workspaceId) || !StringUtils.hasText(orgId)) {
return;
}
LambdaQueryWrapper<DeviceOrgEntity> existsQuery = new LambdaQueryWrapper<>();
existsQuery.eq(DeviceOrgEntity::getDeviceSn, deviceSn);
existsQuery.eq(DeviceOrgEntity::getOrgId, orgId);
existsQuery.eq(DeviceOrgEntity::getWorkspaceId, workspaceId);
if (deviceOrgService.count(existsQuery) > 0) {
return;
}
DeviceEntity dbDevice = this.getOne(new LambdaQueryWrapper<DeviceEntity>()
.eq(DeviceEntity::getDeviceSn, deviceSn));
......@@ -2120,19 +2134,80 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
throw new RuntimeException("device does not exist: " + deviceSn);
}
Integer sharedFlag = isShared == null ? 1 : isShared;
// 唯一键 uk_device_org 是 (device_id, org_id),必须以它为准做幂等,避免脏数据导致重复插入
DeviceOrgEntity existing = null;
if (dbDevice.getId() != null) {
existing = deviceOrgService.getOne(new LambdaQueryWrapper<DeviceOrgEntity>()
.eq(DeviceOrgEntity::getDeviceId, dbDevice.getId())
.eq(DeviceOrgEntity::getOrgId, orgId)
.last("LIMIT 1"), false);
}
if (existing == null) {
existing = deviceOrgService.getOne(new LambdaQueryWrapper<DeviceOrgEntity>()
.eq(DeviceOrgEntity::getDeviceSn, deviceSn)
.eq(DeviceOrgEntity::getOrgId, orgId)
.last("LIMIT 1"), false);
}
if (existing != null) {
boolean needUpdate = false;
if (!deviceSn.equals(existing.getDeviceSn())) {
existing.setDeviceSn(deviceSn);
needUpdate = true;
}
if (!workspaceId.equals(existing.getWorkspaceId())) {
existing.setWorkspaceId(workspaceId);
needUpdate = true;
}
if (existing.getDeviceId() == null || !existing.getDeviceId().equals(dbDevice.getId())) {
existing.setDeviceId(dbDevice.getId());
needUpdate = true;
}
if (existing.getIsShared() == null || !existing.getIsShared().equals(sharedFlag)) {
existing.setIsShared(sharedFlag);
needUpdate = true;
}
if (needUpdate) {
existing.setUpdaterId(getUserId());
existing.setUpdaterName(getUsername());
existing.setUpdateTime(System.currentTimeMillis());
deviceOrgService.updateById(existing);
}
return;
}
DeviceOrgEntity deviceOrgEntity = new DeviceOrgEntity();
deviceOrgEntity.setWorkspaceId(workspaceId);
deviceOrgEntity.setOrgId(orgId);
deviceOrgEntity.setDeviceSn(deviceSn);
deviceOrgEntity.setDeviceId(dbDevice.getId());
deviceOrgEntity.setIsShared(isShared == null ? 1 : isShared);
deviceOrgEntity.setIsShared(sharedFlag);
deviceOrgEntity.setCreatorId(getUserId());
deviceOrgEntity.setCreatorName(getUsername());
deviceOrgEntity.setCreateTime(System.currentTimeMillis());
deviceOrgEntity.setUpdaterId(getUserId());
deviceOrgEntity.setUpdaterName(getUsername());
deviceOrgEntity.setUpdateTime(System.currentTimeMillis());
try {
deviceOrgService.save(deviceOrgEntity);
} catch (org.springframework.dao.DuplicateKeyException e) {
// 并发下偶发撞唯一键:再查一次并修复字段即可
DeviceOrgEntity raced = deviceOrgService.getOne(new LambdaQueryWrapper<DeviceOrgEntity>()
.eq(DeviceOrgEntity::getDeviceId, dbDevice.getId())
.eq(DeviceOrgEntity::getOrgId, orgId)
.last("LIMIT 1"), false);
if (raced != null) {
raced.setDeviceSn(deviceSn);
raced.setWorkspaceId(workspaceId);
raced.setIsShared(sharedFlag);
raced.setUpdaterId(getUserId());
raced.setUpdaterName(getUsername());
raced.setUpdateTime(System.currentTimeMillis());
deviceOrgService.updateById(raced);
}
}
}
/**
......
......@@ -63,6 +63,9 @@ public class OrgServiceImpl extends ServiceImpl<IOrgMapper, OrgEntity> implement
@Override
public Optional<OrgDTO> getOrgByOrgId(String orgId) {
if (!StringUtils.hasText(orgId)) {
return Optional.empty();
}
LambdaQueryWrapper<OrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrgEntity::getOrgId, orgId);
......@@ -74,6 +77,44 @@ public class OrgServiceImpl extends ServiceImpl<IOrgMapper, OrgEntity> implement
return Optional.of(entityConvertToDto(entity));
}
@Override
public Optional<OrgDTO> getFallbackOrg(String workspaceId, String userId, boolean sysAdmin) {
if (!StringUtils.hasText(workspaceId)) {
return Optional.empty();
}
// 1) 优先默认团队 geoSys
Optional<OrgDTO> geoSys = getOrgByOrgId("geoSys");
if (geoSys.isPresent() && workspaceId.equals(geoSys.get().getWorkspaceId())) {
return geoSys;
}
// 2) 非超管:优先自己加入的团队
if (!sysAdmin && StringUtils.hasText(userId)) {
LambdaQueryWrapper<UserOrgEntity> userOrgWrapper = new LambdaQueryWrapper<>();
userOrgWrapper.eq(UserOrgEntity::getUserId, userId)
.eq(UserOrgEntity::getWorkspaceId, workspaceId)
.and(w -> w.isNull(UserOrgEntity::getStatus).or().eq(UserOrgEntity::getStatus, 1))
.orderByDesc(UserOrgEntity::getUpdateTime)
.last("LIMIT 1");
UserOrgEntity relation = userOrgService.getOne(userOrgWrapper, false);
if (relation != null && StringUtils.hasText(relation.getOrgId())) {
Optional<OrgDTO> joined = getOrgByOrgId(relation.getOrgId());
if (joined.isPresent()) {
return joined;
}
}
}
// 3) 工作区下任意一个团队
LambdaQueryWrapper<OrgEntity> orgWrapper = new LambdaQueryWrapper<>();
orgWrapper.eq(OrgEntity::getWorkspaceId, workspaceId)
.orderByAsc(OrgEntity::getCreateTime)
.last("LIMIT 1");
OrgEntity entity = getOne(orgWrapper, false);
return entity == null ? Optional.empty() : Optional.of(entityConvertToDto(entity));
}
@Transactional(rollbackFor = Exception.class)
@Override
public OrgDTO addOrg(OrgDTO orgDTO) {
......@@ -145,6 +186,7 @@ public class OrgServiceImpl extends ServiceImpl<IOrgMapper, OrgEntity> implement
userOrgEntity.setUpdaterId(getUserId());
userOrgEntity.setUpdaterName(getUsername());
userOrgEntity.setRoleType(RoleTypeEnum.ORG_ADMIN.getVal());
userOrgEntity.setStatus(1);
boolean userOrgSaveRes = userOrgService.save(userOrgEntity);
}
......
......@@ -45,9 +45,13 @@ import java.net.URL;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
......@@ -473,41 +477,16 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
return HttpResultResponse.unauthorized(INVALID_WORKSPACE);
}
// 查询 org
LambdaQueryWrapper<OrgEntity> orgQueryWrapper = new LambdaQueryWrapper<>();
orgQueryWrapper.eq(OrgEntity::getWorkspaceId, userEntity.getWorkspaceId());
if (StringUtils.hasText(orgId)) {
orgQueryWrapper.eq(OrgEntity::getOrgId, orgId);
} else if (StringUtils.hasText(orgName)) {
orgQueryWrapper.eq(OrgEntity::getOrgName, orgName);
} else {
loginLogService.addLoginLog(userEntity, false, "Invalid org info", ipAddress, userAgent);
return HttpResultResponse.unauthorized("invalid org info");
}
List<OrgEntity> orgEntityList = orgService.list(orgQueryWrapper);
if (CollectionUtils.isEmpty(orgEntityList)) {
loginLogService.addLoginLog(userEntity, false, "Invalid org id", ipAddress, userAgent);
return HttpResultResponse.unauthorized("invalid org id");
}
OrgEntity orgEntity = orgEntityList.get(0);
// 查询权限
// 解析登录团队:支持 org_id / org_name;成员不在所填团队时,自动落到其已加入团队
OrgEntity orgEntity;
Integer roleType;
// 系统管理员
if (userEntity.getRoleType() != null && userEntity.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
roleType = userEntity.getRoleType();
} else {
// 团队权限
LambdaQueryWrapper<UserOrgEntity> userOrgWrapper = new LambdaQueryWrapper<>();
userOrgWrapper.eq(UserOrgEntity::getUserId, userEntity.getUserId());
userOrgWrapper.eq(UserOrgEntity::getOrgId, orgEntity.getOrgId());
List<UserOrgEntity> userOrgEntityList = userOrgService.list(userOrgWrapper);
if (CollectionUtils.isEmpty(userOrgEntityList)) {
loginLogService.addLoginLog(userEntity, false, "Invalid org role", ipAddress, userAgent);
return HttpResultResponse.unauthorized("invalid org role");
}
UserOrgEntity userOrgEntity = userOrgEntityList.get(0);
roleType = userOrgEntity.getRoleType();
try {
LoginOrgContext loginOrg = resolveLoginOrg(userEntity, orgId, orgName);
orgEntity = loginOrg.org;
roleType = loginOrg.roleType;
} catch (RuntimeException ex) {
loginLogService.addLoginLog(userEntity, false, ex.getMessage(), ipAddress, userAgent);
return HttpResultResponse.unauthorized(ex.getMessage());
}
// 创建token
......@@ -543,6 +522,129 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
return HttpResultResponse.success(userDTO);
}
private static class LoginOrgContext {
private final OrgEntity org;
private final Integer roleType;
private LoginOrgContext(OrgEntity org, Integer roleType) {
this.org = org;
this.roleType = roleType;
}
}
/**
* 解析登录时使用的团队与角色。
* - 系统管理员:可进入工作区任意团队(按填写匹配,匹配不到则回退默认团队)
* - 普通用户:必须在团队内有成员关系;填错团队时自动落到其已加入团队,并提示
*/
private LoginOrgContext resolveLoginOrg(UserEntity userEntity, String orgId, String orgName) {
String workspaceId = userEntity.getWorkspaceId();
// 系统管理员:不强制 user_org
if (userEntity.getRoleType() != null && userEntity.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
OrgEntity org = findOrgInWorkspace(workspaceId, orgId, orgName);
if (org == null) {
org = orgService.getFallbackOrg(workspaceId, userEntity.getUserId(), true)
.map(dto -> {
OrgEntity e = new OrgEntity();
e.setOrgId(dto.getOrgId());
e.setOrgName(dto.getOrgName());
e.setWorkspaceId(dto.getWorkspaceId());
e.setLogo(dto.getLogo());
return e;
}).orElse(null);
}
if (org == null) {
throw new RuntimeException("当前工作区下没有可用团队");
}
return new LoginOrgContext(org, RoleTypeEnum.SYS_ADMIN.getVal());
}
// 普通用户:先查其全部有效团队关系
LambdaQueryWrapper<UserOrgEntity> allRelWrapper = new LambdaQueryWrapper<>();
allRelWrapper.eq(UserOrgEntity::getUserId, userEntity.getUserId())
.and(w -> w.isNull(UserOrgEntity::getStatus).or().eq(UserOrgEntity::getStatus, 1));
List<UserOrgEntity> allRelations = userOrgService.list(allRelWrapper);
if (CollectionUtils.isEmpty(allRelations)) {
throw new RuntimeException("该账号尚未加入任何团队,请联系管理员将其加入团队后再登录");
}
// 1) 按填写的组织匹配
OrgEntity matchedOrg = findOrgInWorkspace(workspaceId, orgId, orgName);
if (matchedOrg != null) {
Optional<UserOrgEntity> relOpt = allRelations.stream()
.filter(r -> matchedOrg.getOrgId().equals(r.getOrgId()))
.findFirst();
if (relOpt.isPresent()) {
Integer rt = relOpt.get().getRoleType();
return new LoginOrgContext(matchedOrg, rt != null ? rt : RoleTypeEnum.MEMBER.getVal());
}
}
// 2) 填错团队 / 大小写不一致:若只加入了一个团队,自动使用该团队
List<String> joinedOrgIds = allRelations.stream()
.map(UserOrgEntity::getOrgId)
.filter(StringUtils::hasText)
.distinct()
.collect(Collectors.toList());
if (joinedOrgIds.size() == 1) {
String onlyOrgId = joinedOrgIds.get(0);
Optional<OrgDTO> onlyOrg = orgService.getOrgByOrgId(onlyOrgId);
if (onlyOrg.isPresent()) {
OrgEntity e = new OrgEntity();
e.setOrgId(onlyOrg.get().getOrgId());
e.setOrgName(onlyOrg.get().getOrgName());
e.setWorkspaceId(onlyOrg.get().getWorkspaceId());
e.setLogo(onlyOrg.get().getLogo());
Integer rt = allRelations.get(0).getRoleType();
return new LoginOrgContext(e, rt != null ? rt : RoleTypeEnum.MEMBER.getVal());
}
}
// 3) 多团队且填写不匹配:给出可读提示
String joinedNames = joinedOrgIds.stream()
.map(id -> orgService.getOrgByOrgId(id)
.map(o -> o.getOrgName() + "(" + o.getOrgId() + ")")
.orElse(id))
.collect(Collectors.joining("、"));
String input = StringUtils.hasText(orgId) ? orgId : orgName;
throw new RuntimeException("该账号不属于组织「" + input + "」。已加入团队:" + joinedNames + "。请填写正确的组织ID后登录");
}
private OrgEntity findOrgInWorkspace(String workspaceId, String orgId, String orgName) {
if (!StringUtils.hasText(workspaceId)) {
return null;
}
LambdaQueryWrapper<OrgEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrgEntity::getWorkspaceId, workspaceId);
List<OrgEntity> orgs = orgService.list(wrapper);
if (CollectionUtils.isEmpty(orgs)) {
return null;
}
if (StringUtils.hasText(orgId)) {
Optional<OrgEntity> byId = orgs.stream()
.filter(o -> orgId.equalsIgnoreCase(o.getOrgId()))
.findFirst();
if (byId.isPresent()) {
return byId.get();
}
// 也允许用户把「团队名称」填到组织ID框
Optional<OrgEntity> byName = orgs.stream()
.filter(o -> orgId.equalsIgnoreCase(o.getOrgName()))
.findFirst();
if (byName.isPresent()) {
return byName.get();
}
}
if (StringUtils.hasText(orgName)) {
return orgs.stream()
.filter(o -> orgName.equalsIgnoreCase(o.getOrgName()) || orgName.equalsIgnoreCase(o.getOrgId()))
.findFirst()
.orElse(null);
}
return null;
}
@Override
public HttpResultResponse userLogin(String username, String password, Integer flag) {
// check user
......@@ -684,6 +786,7 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
.stream()
.map(this::entity2UserListDTO)
.collect(Collectors.toList());
fillUserTeamInfo(usersList, null);
return new PaginationData<>(usersList, new Pagination(userEntityPage.getCurrent(), userEntityPage.getSize(), userEntityPage.getTotal()));
}
......@@ -738,9 +841,81 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
.stream()
.map(this::entity2UserListDTO)
.collect(Collectors.toList());
String preferOrgId = StringUtils.hasText(param.getOrgId()) ? param.getOrgId() : getOrgId();
fillUserTeamInfo(usersList, preferOrgId);
return new PaginationData<>(usersList, new Pagination(userEntityPage.getCurrent(), userEntityPage.getSize(), userEntityPage.getTotal()));
}
/**
* 填充用户所属团队名称;若指定 preferOrgId,则用该团队内角色覆盖展示(系统管理员除外)。
*/
private void fillUserTeamInfo(List<UserListDTO> users, String preferOrgId) {
if (CollectionUtils.isEmpty(users)) {
return;
}
List<String> userIds = users.stream()
.map(UserListDTO::getUserId)
.filter(StringUtils::hasText)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(userIds)) {
return;
}
LambdaQueryWrapper<UserOrgEntity> userOrgQueryWrapper = new LambdaQueryWrapper<>();
userOrgQueryWrapper.in(UserOrgEntity::getUserId, userIds)
.and(w -> w.isNull(UserOrgEntity::getStatus).or().eq(UserOrgEntity::getStatus, 1));
List<UserOrgEntity> relations = userOrgService.list(userOrgQueryWrapper);
if (CollectionUtils.isEmpty(relations)) {
users.forEach(u -> u.setOrgNames(""));
return;
}
Set<String> orgIds = relations.stream()
.map(UserOrgEntity::getOrgId)
.filter(StringUtils::hasText)
.collect(Collectors.toSet());
Map<String, String> orgNameMap = new HashMap<>();
if (!CollectionUtils.isEmpty(orgIds)) {
List<OrgEntity> orgs = orgService.list(new LambdaQueryWrapper<OrgEntity>().in(OrgEntity::getOrgId, orgIds));
if (!CollectionUtils.isEmpty(orgs)) {
orgNameMap = orgs.stream()
.filter(o -> StringUtils.hasText(o.getOrgId()))
.collect(Collectors.toMap(OrgEntity::getOrgId, OrgEntity::getOrgName, (a, b) -> a));
}
}
Map<String, List<UserOrgEntity>> byUser = relations.stream()
.collect(Collectors.groupingBy(UserOrgEntity::getUserId));
Map<String, String> finalOrgNameMap = orgNameMap;
for (UserListDTO dto : users) {
List<UserOrgEntity> userOrgs = byUser.getOrDefault(dto.getUserId(), Collections.emptyList());
String names = userOrgs.stream()
.map(uo -> finalOrgNameMap.getOrDefault(uo.getOrgId(), uo.getOrgId()))
.filter(StringUtils::hasText)
.distinct()
.collect(Collectors.joining("、"));
dto.setOrgNames(names);
if (!StringUtils.hasText(preferOrgId)) {
continue;
}
// 系统管理员角色以用户表为准
if (dto.getRoleType() != null && dto.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
continue;
}
userOrgs.stream()
.filter(uo -> preferOrgId.equals(uo.getOrgId()))
.findFirst()
.ifPresent(uo -> {
if (uo.getRoleType() != null) {
dto.setRoleType(uo.getRoleType());
dto.setRoleTypeName(RoleTypeEnum.find(uo.getRoleType()).getDesc());
}
});
}
}
@Override
public Boolean updateUser(String workspaceId, String userId, UserListDTO user) {
UserEntity userEntity = mapper.selectOne(
......@@ -846,60 +1021,120 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
@Override
public Boolean addUser(String workspaceId, UserEntity user) {
String action = addUserToCurrentOrg(workspaceId, user);
return "created".equals(action) || "joined".equals(action);
}
@Override
public String addUserToCurrentOrg(String workspaceId, UserEntity user) {
// 管理员才能创建用户
aboveAdminRoleAndThrowError();
// 用户名不能重复
String username = user.getUsername();
if (!StringUtils.hasText(username)) {
throw new RuntimeException("用户名不能为空");
}
username = username.trim();
// 不能创建高级用户
if (user.getRoleType() != null && user.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
aboveSysAdminRoleAndThrowError();
}
Integer roleType = user.getRoleType() != null ? user.getRoleType() : RoleTypeEnum.MEMBER.getVal();
String currentOrgId = getOrgId();
if (!StringUtils.hasText(currentOrgId)) {
throw new RuntimeException("当前未选择团队,请先进入团队后再添加用户");
}
// 用户名全局唯一(登录按用户名查找)
LambdaQueryWrapper<UserEntity> userQueryWrapper = new LambdaQueryWrapper<>();
userQueryWrapper.eq(UserEntity::getUsername, username);
// userQueryWrapper.eq(UserEntity::getWorkspaceId, workspaceId);
List<UserEntity> nameUserList = this.mapper.selectList(userQueryWrapper);
if (!CollectionUtils.isEmpty(nameUserList)) {
throw new RuntimeException("the username is already existed");
UserEntity existing = nameUserList.get(0);
// 其他工作区占用:删团队不会删账号,容易留下「列表看不到但用户名被占」的历史账号
if (!workspaceId.equals(existing.getWorkspaceId())) {
if (!aboveSysAdminRole()) {
throw new RuntimeException("用户名「" + username + "」已被历史账号占用,且不在当前工作区列表中。"
+ "请更换用户名,或联系系统管理员在「添加用户」时回收该账号。");
}
// 系统管理员:回收到当前工作区并加入当前团队
existing.setWorkspaceId(workspaceId);
if (StringUtils.hasText(user.getPassword())) {
existing.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
}
if (user.getUserType() != null) {
existing.setUserType(user.getUserType());
}
// 非超管账号才同步角色到用户表
if (existing.getRoleType() == null || existing.getRoleType() != RoleTypeEnum.SYS_ADMIN.getVal()) {
existing.setRoleType(roleType);
}
existing.setUpdateTime(System.currentTimeMillis());
this.updateById(existing);
UserEntity userEntity = new UserEntity();
ensureUserInOrg(existing.getUserId(), workspaceId, currentOrgId, roleType);
return "reclaimed";
}
// 普通用户不能创建管理员
// if (user.getUserType() == UserTypeEnum.WEB.getVal()) {
// if (isNotAdmin()) {
// throw new RuntimeException("The current user is not an admin and has no permissions");
// }
// }
// 同工作区:检查是否已在当前团队
LambdaQueryWrapper<UserOrgEntity> existOrgWrapper = new LambdaQueryWrapper<>();
existOrgWrapper.eq(UserOrgEntity::getUserId, existing.getUserId())
.eq(UserOrgEntity::getOrgId, currentOrgId);
UserOrgEntity existRelation = userOrgService.getOne(existOrgWrapper);
if (existRelation != null && (existRelation.getStatus() == null || existRelation.getStatus() == 1)) {
throw new RuntimeException("用户「" + username + "」已在当前团队中,请在用户列表中搜索该账号查看。");
}
// 不能创建高级用户
if (user.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
aboveSysAdminRoleAndThrowError();
// 未在当前团队(或曾随团队删除被移出)→ 加入当前团队,不再新建账号
ensureUserInOrg(existing.getUserId(), workspaceId, currentOrgId, roleType);
return "joined";
}
UserEntity userEntity = new UserEntity();
userEntity.setUserType(user.getUserType() != null ? user.getUserType() : UserTypeEnum.PILOT.getVal());
userEntity.setRoleType(user.getRoleType() != null ? user.getRoleType() : RoleTypeEnum.MEMBER.getVal());
userEntity.setRoleType(roleType);
userEntity.setUserId(UUID.randomUUID().toString());
userEntity.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
userEntity.setUsername(user.getUsername());
userEntity.setUsername(username);
userEntity.setWorkspaceId(workspaceId);
userEntity.setNewAdd(true);
int insert = this.mapper.insert(userEntity);
if (insert <= 0) {
throw new RuntimeException("创建用户失败");
}
// 增加用户权限
UserOrgEntity userOrgEntity = new UserOrgEntity();
userOrgEntity.setWorkspaceId(workspaceId);
userOrgEntity.setOrgId(getOrgId());
userOrgEntity.setUserId(userEntity.getUserId());
userOrgEntity.setRoleType(user.getRoleType() != null ? user.getRoleType() : RoleTypeEnum.MEMBER.getVal());
// private Integer status;
userOrgEntity.setCreatorId(getUserId());
userOrgEntity.setCreatorName(getUsername());
userOrgEntity.setCreateTime(System.currentTimeMillis());
userOrgEntity.setUpdaterId(getUserId());
userOrgEntity.setUpdaterName(getUsername());
userOrgEntity.setUpdateTime(System.currentTimeMillis());
boolean userOrgSaveRes = userOrgService.save(userOrgEntity);
ensureUserInOrg(userEntity.getUserId(), workspaceId, currentOrgId, roleType);
return "created";
}
return insert > 0;
/**
* 确保用户在指定团队中(新建或重新启用)。
*/
private void ensureUserInOrg(String userId, String workspaceId, String orgId, Integer roleType) {
LambdaQueryWrapper<UserOrgEntity> existOrgWrapper = new LambdaQueryWrapper<>();
existOrgWrapper.eq(UserOrgEntity::getUserId, userId)
.eq(UserOrgEntity::getOrgId, orgId);
UserOrgEntity existRelation = userOrgService.getOne(existOrgWrapper);
if (existRelation != null) {
existRelation.setStatus(1);
existRelation.setRoleType(roleType);
existRelation.setWorkspaceId(workspaceId);
existRelation.setUpdaterId(getUserId());
existRelation.setUpdaterName(getUsername());
existRelation.setUpdateTime(System.currentTimeMillis());
userOrgService.updateById(existRelation);
return;
}
boolean joined = userOrgService.addUserToOrg(userId, orgId, roleType);
if (!joined) {
throw new RuntimeException("用户加入当前团队失败,请稍后重试");
}
}
@Transactional(rollbackFor = Exception.class)
......@@ -950,8 +1185,18 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
// userQueryWrapper.eq(UserEntity::getWorkspaceId, user.getWorkspaceId());
List<UserEntity> nameUserList = this.list(userQueryWrapper);
if (!CollectionUtils.isEmpty(nameUserList)) {
// throw new RuntimeException("the username is already existed");
return nameUserList.get(0);
// 用户名已存在:复用账号;若工作区不同则迁到当前工作区(避免删团队后账号悬空占名)
UserEntity existing = nameUserList.get(0);
if (StringUtils.hasText(user.getWorkspaceId())
&& !user.getWorkspaceId().equals(existing.getWorkspaceId())) {
existing.setWorkspaceId(user.getWorkspaceId());
if (StringUtils.hasText(user.getPassword())) {
existing.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
}
existing.setUpdateTime(System.currentTimeMillis());
this.updateById(existing);
}
return existing;
}
UserEntity userEntity = new UserEntity();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment