Commit 74ca192a 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 5276873e
......@@ -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
......
......@@ -2044,14 +2044,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) {
......@@ -2101,13 +2122,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));
......@@ -2115,19 +2129,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());
deviceOrgService.save(deviceOrgEntity);
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);
}
......
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