Commit df607f5c by guoxuejian

org branch init

parent 8e0d465b
......@@ -39,6 +39,9 @@ public class CustomClaim {
@JsonAlias("workspace_id")
private String workspaceId;
@JsonAlias("org_id")
private String orgId;
/**
* Convert the custom claim data type to the Map type.
* @return map
......
......@@ -92,6 +92,13 @@ public class SecurityUtils {
return customClaim.getId();
}
public static String getUsername() {
DecodedJWT jwt = JwtUtil.verifyToken(getToken());
CustomClaim customClaim = new CustomClaim(jwt.getClaims());
return customClaim.getUsername();
}
public static boolean isSysAdminRole() {
Integer roleType = getRoleType();
......@@ -120,7 +127,7 @@ public class SecurityUtils {
}
public static boolean isProjectAdminRole(Integer roleType) {
return roleType == RoleTypeEnum.PROJECT_ADMIN.getVal();
return roleType == RoleTypeEnum.ORG_ADMIN.getVal();
}
public static boolean isNotProjectAdminRole(Integer roleType) {
......@@ -189,7 +196,7 @@ public class SecurityUtils {
return aboveMemberRole(roleType);
}
public static boolean aboveMemberRole(Integer roleType) {
int[] roleArr = {RoleTypeEnum.MEMBER.getVal(), RoleTypeEnum.PILOT.getVal(), RoleTypeEnum.ADMIN.getVal(), RoleTypeEnum.PROJECT_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
int[] roleArr = {RoleTypeEnum.MEMBER.getVal(), RoleTypeEnum.PILOT.getVal(), RoleTypeEnum.ADMIN.getVal(), RoleTypeEnum.ORG_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
for(int role : roleArr){
if(roleType == role) {
......@@ -213,7 +220,7 @@ public class SecurityUtils {
}
public static boolean abovePilotRole(Integer roleType) {
int[] roleArr = {RoleTypeEnum.PILOT.getVal(), RoleTypeEnum.ADMIN.getVal(), RoleTypeEnum.PROJECT_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
int[] roleArr = {RoleTypeEnum.PILOT.getVal(), RoleTypeEnum.ADMIN.getVal(), RoleTypeEnum.ORG_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
for(int role : roleArr){
if(roleType == role) {
......@@ -237,7 +244,7 @@ public class SecurityUtils {
}
public static boolean aboveAdminRole(Integer roleType) {
int[] roleArr = {RoleTypeEnum.ADMIN.getVal(), RoleTypeEnum.PROJECT_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
int[] roleArr = {RoleTypeEnum.ADMIN.getVal(), RoleTypeEnum.ORG_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
for(int role : roleArr){
if(roleType == role) {
......@@ -261,7 +268,7 @@ public class SecurityUtils {
}
public static boolean aboveProjectAdminRole(Integer roleType) {
int[] roleArr = {RoleTypeEnum.PROJECT_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
int[] roleArr = {RoleTypeEnum.ORG_ADMIN.getVal(), RoleTypeEnum.SYS_ADMIN.getVal()};
for(int role : roleArr) {
if(roleType == role) {
......
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.DeviceOrgEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface IDeviceOrgMapper extends BaseMapper<DeviceOrgEntity> {
}
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.OrgEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface IOrgMapper extends BaseMapper<OrgEntity> {
}
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.dto.UserOrgDTO;
import com.dji.sample.manage.model.entity.UserOrgEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface IUserOrgMapper extends BaseMapper<UserOrgEntity> {
/**
* 查询用户所有关联的组织信息,包含组织名称
* @param userId 用户ID
* @return 用户组织列表(包含组织名称)
*/
@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")
List<UserOrgDTO> selectUserOrgs(@Param("userId") String userId);
}
......@@ -33,6 +33,8 @@ public class DeviceDTO {
private String workspaceId;
private String orgId;
private ControlSourceEnum controlSource;
private String deviceDesc;
......
package com.dji.sample.manage.model.dto;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class OrgDTO {
private Integer id;
private String orgId;
private String orgName;
private String orgDesc;
private String workspaceId;
// private Long createTime;
// private Long updateTime;
}
......@@ -6,6 +6,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
......@@ -37,4 +39,17 @@ public class UserDTO {
@JsonProperty("role_type")
private Integer roleType;
private String email;
private String phone;
@JsonProperty("is_super_admin")
private Integer isSuperAdmin;
// 新增字段,表示用户所有关联的组织
private List<UserOrgDTO> orgs;
// 新增字段,表示当前选中的组织
private String currentOrgId;
}
......@@ -18,7 +18,7 @@ import java.time.LocalDateTime;
@NoArgsConstructor
public class UserListDTO {
private Integer id;
private Long id;
private String userId;
......@@ -36,5 +36,11 @@ public class UserListDTO {
private String mqttPassword;
private String email;
private String phone;
private Integer isSuperAdmin;
private LocalDateTime createTime;
}
package com.dji.sample.manage.model.dto;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class UserOrgDTO {
private Integer id;
private String userId;
private String orgId;
private String orgName; // 组织名称
private Integer roleType;
private Integer status;
// private String createTime;
// private String updateTime;
}
......@@ -24,6 +24,9 @@ public class WorkspaceDTO implements Serializable {
@JsonProperty("workspace_id")
private String workspaceId;
@JsonProperty("org_id")
private String orgId;
private String workspaceName;
private String workspaceDesc;
......
......@@ -34,6 +34,9 @@ public class DeviceEntity implements Serializable {
@TableField(value = "workspace_id")
private String workspaceId;
@TableField(value = "org_id")
private String orgId;
@TableField(value = "device_type")
private Integer deviceType;
......
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("manage_device_org")
public class DeviceOrgEntity {
@TableId(type = IdType.AUTO)
private Integer id;
private Integer deviceId;
private String orgId;
private Integer isShared;
private Long createTime;
private Long updateTime;
private String creatorId;
private String creatorName;
private String updaterId;
private String updaterName;
// Getters and Setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getDeviceId() {
return deviceId;
}
public void setDeviceId(Integer deviceId) {
this.deviceId = deviceId;
}
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
public Integer getIsShared() {
return isShared;
}
public void setIsShared(Integer isShared) {
this.isShared = isShared;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public Long getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Long updateTime) {
this.updateTime = updateTime;
}
public String getCreatorId() {
return creatorId;
}
public void setCreatorId(String creatorId) {
this.creatorId = creatorId;
}
public String getCreatorName() {
return creatorName;
}
public void setCreatorName(String creatorName) {
this.creatorName = creatorName;
}
public String getUpdaterId() {
return updaterId;
}
public void setUpdaterId(String updaterId) {
this.updaterId = updaterId;
}
public String getUpdaterName() {
return updaterName;
}
public void setUpdaterName(String updaterName) {
this.updaterName = updaterName;
}
}
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("manage_org")
public class OrgEntity {
@TableId(type = IdType.AUTO)
private Integer id;
private String orgId;
private String orgName;
private String orgDesc;
private String workspaceId;
// private Long createTime;
// private Long updateTime;
private String creatorId;
private String creatorName;
private String updaterId;
private String updaterName;
// Getters and Setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public String getOrgDesc() {
return orgDesc;
}
public void setOrgDesc(String orgDesc) {
this.orgDesc = orgDesc;
}
public String getWorkspaceId() {
return workspaceId;
}
public void setWorkspaceId(String workspaceId) {
this.workspaceId = workspaceId;
}
// public Long getCreateTime() {
// return createTime;
// }
// public void setCreateTime(Long createTime) {
// this.createTime = createTime;
// }
// public Long getUpdateTime() {
// return updateTime;
// }
// public void setUpdateTime(Long updateTime) {
// this.updateTime = updateTime;
// }
public String getCreatorId() {
return creatorId;
}
public void setCreatorId(String creatorId) {
this.creatorId = creatorId;
}
public String getCreatorName() {
return creatorName;
}
public void setCreatorName(String creatorName) {
this.creatorName = creatorName;
}
public String getUpdaterId() {
return updaterId;
}
public void setUpdaterId(String updaterId) {
this.updaterId = updaterId;
}
public String getUpdaterName() {
return updaterName;
}
public void setUpdaterName(String updaterName) {
this.updaterName = updaterName;
}
}
......@@ -10,20 +10,23 @@ import java.io.Serializable;
public class UserEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
private Long id;
@TableField(value = "user_id")
private String userId;
@TableField(value = "workspace_id")
private String workspaceId;
@TableField(value = "org_id")
private String orgId;
@TableField(value = "username")
private String username;
@TableField(value = "password")
private String password;
@TableField(value = "workspace_id")
private String workspaceId;
@TableField(value = "user_type")
private Integer userType;
......@@ -42,4 +45,76 @@ public class UserEntity implements Serializable {
@TableField(value = "role_type")
private Integer roleType;
@TableField(value = "is_super_admin")
private Integer isSuperAdmin;
@TableField(value = "email")
private String email;
@TableField(value = "phone")
private String phone;
private String creatorId;
private String creatorName;
private String updaterId;
private String updaterName;
public Integer getIsSuperAdmin() {
return isSuperAdmin != null ? isSuperAdmin : 0;
}
public void setIsSuperAdmin(Integer isSuperAdmin) {
this.isSuperAdmin = isSuperAdmin;
}
public String getCreatorId() {
return creatorId;
}
public void setCreatorId(String creatorId) {
this.creatorId = creatorId;
}
public String getCreatorName() {
return creatorName;
}
public void setCreatorName(String creatorName) {
this.creatorName = creatorName;
}
public String getUpdaterId() {
return updaterId;
}
public void setUpdaterId(String updaterId) {
this.updaterId = updaterId;
}
public String getUpdaterName() {
return updaterName;
}
public void setUpdaterName(String updaterName) {
this.updaterName = updaterName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("manage_user_org")
public class UserOrgEntity {
@TableId(type = IdType.AUTO)
private Integer id;
private String userId;
private String orgId;
private Integer roleType;
private Integer status;
// private Long createTime;
// private Long updateTime;
private String creatorId;
private String creatorName;
private String updaterId;
private String updaterName;
// Getters and Setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
public Integer getRoleType() {
return roleType;
}
public void setRoleType(Integer roleType) {
this.roleType = roleType;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
// public Long getCreateTime() {
// return createTime;
// }
// public void setCreateTime(Long createTime) {
// this.createTime = createTime;
// }
// public Long getUpdateTime() {
// return updateTime;
// }
// public void setUpdateTime(Long updateTime) {
// this.updateTime = updateTime;
// }
public String getCreatorId() {
return creatorId;
}
public void setCreatorId(String creatorId) {
this.creatorId = creatorId;
}
public String getCreatorName() {
return creatorName;
}
public void setCreatorName(String creatorName) {
this.creatorName = creatorName;
}
public String getUpdaterId() {
return updaterId;
}
public void setUpdaterId(String updaterId) {
this.updaterId = updaterId;
}
public String getUpdaterName() {
return updaterName;
}
public void setUpdaterName(String updaterName) {
this.updaterName = updaterName;
}
}
......@@ -15,6 +15,9 @@ public class WorkspaceEntity implements Serializable {
@TableField(value = "workspace_id")
private String workspaceId;
@TableField(value = "org_id")
private String orgId;
@TableField(value = "workspace_name")
private String workspaceName;
......
......@@ -12,7 +12,7 @@ public enum RoleTypeEnum {
PILOT(2, "飞手"),
PROJECT_ADMIN(50, "项目管理员"),
ORG_ADMIN(50, "项目管理员"),
SYS_ADMIN(100, "系统管理员"),
......@@ -39,8 +39,8 @@ public enum RoleTypeEnum {
if (val == SYS_ADMIN.val) {
return SYS_ADMIN;
}
if (val == PROJECT_ADMIN.val) {
return PROJECT_ADMIN;
if (val == ORG_ADMIN.val) {
return ORG_ADMIN;
}
if (val == ADMIN.val) {
return ADMIN;
......
package com.dji.sample.manage.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dji.sample.manage.model.dto.OrgDTO;
import com.dji.sample.manage.model.entity.OrgEntity;
import java.util.List;
import java.util.Optional;
public interface IOrgService extends IService<OrgEntity> {
/**
* 根据组织ID获取组织信息
* @param orgId 组织ID
* @return 组织信息
*/
Optional<OrgDTO> getOrgByOrgId(String orgId);
/**
* 获取组织对应的workspaceId
* @param orgId 组织ID
* @return workspaceId
*/
String getWorkspaceIdByOrgId(String orgId);
/**
* 获取所有组织列表
* @return 组织列表
*/
List<OrgDTO> getAllOrgs();
/**
* 删除组织
* @param orgId 组织ID
* @return 是否删除成功
*/
boolean deleteOrg(String orgId);
/**
* 创建组织
* @param orgEntity 组织实体
* @return 是否创建成功
*/
boolean createOrg(OrgDTO orgDTO);
/**
* 更新组织
* @param orgEntity 组织实体
* @return 是否更新成功
*/
boolean updateOrg(OrgDTO orgDTO);
}
package com.dji.sample.manage.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dji.sample.manage.model.dto.UserOrgDTO;
import com.dji.sample.manage.model.entity.UserOrgEntity;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public interface IUserOrgService extends IService<UserOrgEntity> {
/**
* 获取用户在特定组织的角色信息
* @param userId 用户ID
* @param orgId 组织ID
* @return 用户组织关联信息
*/
Optional<UserOrgEntity> getUserOrg(String userId, String orgId);
/**
* 获取用户的所有组织信息
* @param userId 用户ID
* @return 用户所有组织关联信息列表
*/
List<UserOrgDTO> getUserOrgs(String userId);
/**
* 获取特定组织中所有用户ID
* @param orgId 组织ID
* @return 用户ID列表
*/
List<String> getUserIdsByOrgId(String orgId);
/**
* 获取组织中所有用户的角色映射
* @param orgId 组织ID
* @return 用户ID与角色类型的映射
*/
Map<String, Integer> getUserRoleMapByOrgId(String orgId);
/**
* 添加用户到组织
* @param userId 用户ID
* @param orgId 组织ID
* @param roleType 角色类型
* @return 操作是否成功
*/
boolean addUserToOrg(String userId, String orgId, Integer roleType);
/**
* 从组织移除用户
* @param userId 用户ID
* @param orgId 组织ID
* @return 操作是否成功
*/
boolean removeUserFromOrg(String userId, String orgId);
/**
* 更新用户在组织的角色
* @param userId 用户ID
* @param orgId 组织ID
* @param roleType 新的角色类型
* @return 操作是否成功
*/
boolean updateUserOrgRole(String userId, String orgId, Integer roleType);
}
......@@ -58,4 +58,12 @@ public interface IUserService extends IService<UserEntity> {
UserEntity addWorkspaceAdminUser(UserEntity user);
/**
* 根据组织ID获取用户列表
* @param page 页码
* @param pageSize 每页数量
* @param orgId 组织ID
* @return 分页用户列表
*/
PaginationData<UserListDTO> getUsersByOrgId(long page, long pageSize, String orgId);
}
......@@ -108,7 +108,7 @@ public class FleetUserServiceImpl extends ServiceImpl<IFleetUserMapper, FleetUse
LambdaQueryWrapper<UserEntity> userQueryWrapper = new LambdaQueryWrapper<>();
userQueryWrapper.in(UserEntity::getId, userIdList);
List<UserEntity> userEntityList = userService.list(userQueryWrapper);
Map<Integer, UserEntity> userEntityMap = userEntityList.stream().collect(Collectors.toMap(UserEntity::getId, Function.identity(), (key1, key2) -> key1));
Map<Long, UserEntity> userEntityMap = userEntityList.stream().collect(Collectors.toMap(UserEntity::getId, Function.identity(), (key1, key2) -> key1));
for (FleetUserEntity entity : entityList) {
FleetUserDTO fleetUserDTO = new FleetUserDTO();
......@@ -116,7 +116,7 @@ public class FleetUserServiceImpl extends ServiceImpl<IFleetUserMapper, FleetUse
fleetUserDTO.setFleetId(entity.getFleetId());
fleetUserDTO.setUserId(entity.getUserId());
UserEntity user = userEntityMap.getOrDefault(Integer.valueOf(entity.getUserId()), null);
UserEntity user = userEntityMap.getOrDefault(Long.valueOf(entity.getUserId()), null);
if (!ObjectUtils.isEmpty(user)) {
fleetUserDTO.setUsername(user.getUsername());
fleetUserDTO.setUserTypeName(UserTypeEnum.find(user.getUserType()).name());
......
package com.dji.sample.manage.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dji.sample.manage.dao.IOrgMapper;
import com.dji.sample.manage.model.dto.OrgDTO;
import com.dji.sample.manage.model.entity.OrgEntity;
import com.dji.sample.manage.service.IOrgService;
import org.springframework.stereotype.Service;
import com.dji.sample.common.util.SecurityUtils;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@Transactional
public class OrgServiceImpl extends ServiceImpl<IOrgMapper, OrgEntity> implements IOrgService {
@Override
public Optional<OrgDTO> getOrgByOrgId(String orgId) {
LambdaQueryWrapper<OrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrgEntity::getOrgId, orgId);
OrgEntity entity = getOne(queryWrapper);
if (entity == null) {
return Optional.empty();
}
return Optional.of(convertToDTO(entity));
}
@Override
public String getWorkspaceIdByOrgId(String orgId) {
LambdaQueryWrapper<OrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrgEntity::getOrgId, orgId)
.select(OrgEntity::getWorkspaceId);
OrgEntity entity = getOne(queryWrapper);
return entity != null ? entity.getWorkspaceId() : null;
}
@Override
public boolean deleteOrg(String orgId) {
LambdaQueryWrapper<OrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrgEntity::getOrgId, orgId);
return remove(queryWrapper);
}
@Override
public boolean createOrg(OrgDTO orgDTO) {
OrgEntity entity = new OrgEntity();
entity.setOrgId(orgDTO.getOrgId());
entity.setOrgName(orgDTO.getOrgName());
entity.setOrgDesc(orgDTO.getOrgDesc());
entity.setWorkspaceId(orgDTO.getWorkspaceId());
// 设置创建者和更新者信息
String currentUserId = SecurityUtils.getUserId();
String currentUserName = SecurityUtils.getUsername();
entity.setCreatorId(currentUserId);
entity.setCreatorName(currentUserName);
entity.setUpdaterId(currentUserId);
entity.setUpdaterName(currentUserName);
save(entity);
return entity.getOrgId() != null;
}
@Override
public boolean updateOrg(OrgDTO orgDTO) {
LambdaQueryWrapper<OrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrgEntity::getOrgId, orgDTO.getOrgId());
OrgEntity entity = getOne(queryWrapper);
if (entity == null) {
return false;
}
entity.setOrgName(orgDTO.getOrgName());
entity.setOrgDesc(orgDTO.getOrgDesc());
entity.setWorkspaceId(orgDTO.getWorkspaceId());
// 设置更新者信息
entity.setUpdaterId(SecurityUtils.getUserId());
entity.setUpdaterName(SecurityUtils.getUsername());
return updateById(entity);
}
@Override
public List<OrgDTO> getAllOrgs() {
List<OrgEntity> entities = list();
return entities.stream()
.map(this::convertToDTO)
.collect(Collectors.toList());
}
private OrgDTO convertToDTO(OrgEntity entity) {
return OrgDTO.builder()
.id(entity.getId())
.orgId(entity.getOrgId())
.orgName(entity.getOrgName())
.orgDesc(entity.getOrgDesc())
.workspaceId(entity.getWorkspaceId())
// .createTime(entity.getCreateTime())
// .updateTime(entity.getUpdateTime())
.build();
}
}
package com.dji.sample.manage.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dji.sample.common.util.SecurityUtils; // 修正导入包
import com.dji.sample.manage.dao.IUserOrgMapper;
import com.dji.sample.manage.model.dto.UserOrgDTO;
import com.dji.sample.manage.model.entity.UserOrgEntity;
import com.dji.sample.manage.service.IUserOrgService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class UserOrgServiceImpl extends ServiceImpl<IUserOrgMapper, UserOrgEntity> implements IUserOrgService {
@Autowired
private IUserOrgMapper userOrgMapper;
@Override
public Optional<UserOrgEntity> getUserOrg(String userId, String orgId) {
LambdaQueryWrapper<UserOrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserOrgEntity::getUserId, userId)
.eq(UserOrgEntity::getOrgId, orgId)
.eq(UserOrgEntity::getStatus, 1);
UserOrgEntity entity = getOne(queryWrapper);
return Optional.ofNullable(entity);
}
@Override
public List<UserOrgDTO> getUserOrgs(String userId) {
return userOrgMapper.selectUserOrgs(userId);
}
@Override
public List<String> getUserIdsByOrgId(String orgId) {
LambdaQueryWrapper<UserOrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserOrgEntity::getOrgId, orgId)
.eq(UserOrgEntity::getStatus, 1)
.select(UserOrgEntity::getUserId);
return this.list(queryWrapper)
.stream()
.map(UserOrgEntity::getUserId)
.collect(Collectors.toList());
}
@Override
public Map<String, Integer> getUserRoleMapByOrgId(String orgId) {
LambdaQueryWrapper<UserOrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserOrgEntity::getOrgId, orgId)
.eq(UserOrgEntity::getStatus, 1)
.select(UserOrgEntity::getUserId, UserOrgEntity::getRoleType);
return this.list(queryWrapper)
.stream()
.collect(Collectors.toMap(
UserOrgEntity::getUserId,
UserOrgEntity::getRoleType
));
}
@Override
public boolean addUserToOrg(String userId, String orgId, Integer roleType) {
// 检查是否已存在
LambdaQueryWrapper<UserOrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserOrgEntity::getUserId, userId)
.eq(UserOrgEntity::getOrgId, orgId);
UserOrgEntity entity = getOne(queryWrapper);
if (entity != null) {
// 如果已存在但被禁用,则启用
if (entity.getStatus() == 0) {
entity.setStatus(1);
entity.setRoleType(roleType);
// entity.setUpdateTime(System.currentTimeMillis());
return updateById(entity);
}
return false; // 已存在且启用状态
}
// 创建新关联
UserOrgEntity userOrg = new UserOrgEntity();
userOrg.setUserId(userId);
userOrg.setOrgId(orgId);
userOrg.setRoleType(roleType);
userOrg.setStatus(1);
// userOrg.setCreateTime(System.currentTimeMillis());
// userOrg.setUpdateTime(System.currentTimeMillis());
// 创建新关联时设置创建者和更新者信息
String currentUserId = SecurityUtils.getUserId();
String currentUserName = SecurityUtils.getUsername();
userOrg.setCreatorId(currentUserId);
userOrg.setCreatorName(currentUserName);
userOrg.setUpdaterId(currentUserId);
userOrg.setUpdaterName(currentUserName);
return save(userOrg);
}
@Override
public boolean removeUserFromOrg(String userId, String orgId) {
LambdaQueryWrapper<UserOrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserOrgEntity::getUserId, userId)
.eq(UserOrgEntity::getOrgId, orgId);
// 使用逻辑删除(将状态设置为禁用)
UserOrgEntity entity = getOne(queryWrapper);
if (entity != null) {
entity.setStatus(0);
// entity.setUpdateTime(System.currentTimeMillis());
return updateById(entity);
}
return false;
}
@Override
public boolean updateUserOrgRole(String userId, String orgId, Integer roleType) {
LambdaQueryWrapper<UserOrgEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserOrgEntity::getUserId, userId)
.eq(UserOrgEntity::getOrgId, orgId);
UserOrgEntity entity = getOne(queryWrapper);
if (entity != null) {
entity.setRoleType(roleType);
// entity.setUpdateTime(System.currentTimeMillis());
// 设置更新者信息
entity.setUpdaterId(SecurityUtils.getUserId());
entity.setUpdaterName(SecurityUtils.getUsername());
return updateById(entity);
}
return false;
}
}
......@@ -13,13 +13,19 @@ import com.dji.sample.common.util.JwtUtil;
import com.dji.sample.common.util.SecurityUtils;
import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration;
import com.dji.sample.manage.dao.IUserMapper;
import com.dji.sample.manage.model.dto.OrgDTO;
import com.dji.sample.manage.model.dto.UserDTO;
import com.dji.sample.manage.model.dto.UserListDTO;
import com.dji.sample.manage.model.dto.UserOrgDTO;
import com.dji.sample.manage.model.dto.WorkspaceDTO;
import com.dji.sample.manage.model.entity.OrgEntity;
import com.dji.sample.manage.model.entity.UserEntity;
import com.dji.sample.manage.model.entity.UserOrgEntity;
import com.dji.sample.manage.model.entity.WorkspaceEntity;
import com.dji.sample.manage.model.enums.RoleTypeEnum;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.service.IOrgService;
import com.dji.sample.manage.service.IUserOrgService;
import com.dji.sample.manage.service.IUserService;
import com.dji.sample.manage.service.IWorkspaceService;
import com.dji.sdk.common.HttpResultResponse;
......@@ -36,6 +42,7 @@ import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
......@@ -56,18 +63,38 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
@Autowired
private IWorkspaceService workspaceService;
@Override
public HttpResultResponse getUserByUsername(String username, String workspaceId) {
@Autowired
private IOrgService orgService;
@Autowired
private IUserOrgService userOrgService;
@Override
public HttpResultResponse getUserByUsername(String username, String orgId) {
UserEntity userEntity = this.getUserByUsername(username);
if (userEntity == null) {
return new HttpResultResponse()
.setCode(HttpStatus.UNAUTHORIZED.value())
.setMessage("invalid username");
}
// 检查用户是否有权限访问该组织
if (StringUtils.hasText(orgId) && (userEntity.getIsSuperAdmin() == null || userEntity.getIsSuperAdmin() != 1)) {
Optional<UserOrgEntity> userOrg = userOrgService.getUserOrg(userEntity.getUserId(), orgId);
if (userOrg.isEmpty()) {
return new HttpResultResponse()
.setCode(HttpStatus.FORBIDDEN.value())
.setMessage("User does not have access to this organization");
}
}
UserDTO user = this.entityConvertToDTO(userEntity);
// 获取组织对应的workspaceId
if (StringUtils.hasText(orgId)) {
String workspaceId = orgService.getWorkspaceIdByOrgId(orgId);
user.setWorkspaceId(workspaceId);
user.setCurrentOrgId(orgId);
}
return HttpResultResponse.success(user);
}
......@@ -88,25 +115,80 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
}
}
// 密码加密验证
// if (!password.equals(userEntity.getPassword())) {
if (!SecurityUtils.matchesPassword(password, userEntity.getPassword())) {
return new HttpResultResponse()
.setCode(HttpStatus.UNAUTHORIZED.value())
.setMessage("invalid password");
}
Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(userEntity.getWorkspaceId());
if (workspaceOpt.isEmpty()) {
// 获取用户所有的组织信息
List<UserOrgDTO> userOrgs = userOrgService.getUserOrgs(userEntity.getUserId());
// 如果没有组织关系且不是超级管理员,返回错误
if (CollectionUtils.isEmpty(userOrgs) && userEntity.getIsSuperAdmin() != 1) {
return new HttpResultResponse()
.setCode(HttpStatus.UNAUTHORIZED.value())
.setMessage("invalid workspace id");
.setMessage("User does not belong to any organization");
}
// 选择第一个组织作为当前组织
String selectedOrgId = "";
String selectedWorkspaceId = "";
Integer selectedRoleType = userEntity.getRoleType();
if (!CollectionUtils.isEmpty(userOrgs)) {
// 如果用户有关联的组织,使用第一个作为默认
UserOrgDTO firstOrg = userOrgs.get(0);
selectedOrgId = firstOrg.getOrgId();
selectedRoleType = firstOrg.getRoleType();
// 获取组织对应的workspaceId
selectedWorkspaceId = orgService.getWorkspaceIdByOrgId(selectedOrgId);
} else if (userEntity.getIsSuperAdmin() == 1) {
// 超级管理员可访问所有组织
// 获取第一个可用的组织作为默认组织
List<OrgDTO> allOrgs = orgService.getAllOrgs();
if (!CollectionUtils.isEmpty(allOrgs)) {
OrgDTO firstOrg = allOrgs.get(0);
selectedOrgId = firstOrg.getOrgId();
selectedWorkspaceId = firstOrg.getWorkspaceId();
selectedRoleType = RoleTypeEnum.SYS_ADMIN.getVal(); // 超级管理员在所有组织中都是最高权限
// 同时将所有组织信息添加到UserDTO中,使前端可以切换
userOrgs = allOrgs.stream()
.map(org -> UserOrgDTO.builder()
.orgId(org.getOrgId())
.orgName(org.getOrgName())
.roleType(RoleTypeEnum.SYS_ADMIN.getVal())
.build())
.collect(Collectors.toList());
} else {
// // 如果没有任何组织,则获取默认工作空间
// List<WorkspaceDTO> workspaces = workspaceService.getAllWorkspaces();
// if (!CollectionUtils.isEmpty(workspaces)) {
// selectedWorkspaceId = workspaces.get(0).getWorkspaceId();
// }
// 没有组织则为空
userOrgs = List.of();
}
}
// 创建token
// 验证工作空间
if (!StringUtils.hasText(selectedWorkspaceId)) {
return new HttpResultResponse()
.setCode(HttpStatus.UNAUTHORIZED.value())
.setMessage("No valid workspace available");
}
// 创建token - 使用当前选定的组织、工作空间和角色
CustomClaim customClaim = new CustomClaim(userEntity.getUserId(),
userEntity.getUsername(), userEntity.getUserType(),
userEntity.getRoleType(),
workspaceOpt.get().getWorkspaceId());
selectedRoleType,
selectedWorkspaceId, selectedOrgId);
// 添加orgId到claim
customClaim.setOrgId(selectedOrgId);
// create token
String token = JwtUtil.createToken(customClaim.convertToMap());
......@@ -114,7 +196,13 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
UserDTO userDTO = entityConvertToDTO(userEntity);
userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress());
userDTO.setAccessToken(token);
userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId());
userDTO.setWorkspaceId(selectedWorkspaceId);
userDTO.setRoleType(selectedRoleType);
userDTO.setCurrentOrgId(selectedOrgId);
// 添加用户所有组织信息
userDTO.setOrgs(userOrgs);
return HttpResultResponse.success(userDTO);
}
......@@ -141,87 +229,162 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
}
user.setWorkspaceId(customClaim.getWorkspaceId());
user.setAccessToken(refreshToken);
// 获取orgId
String orgId = (String)customClaim.getOrgId();
if (StringUtils.hasText(orgId)) {
user.setCurrentOrgId(orgId);
}
return Optional.of(user);
}
@Override
public PaginationData<UserListDTO> getUsersByWorkspaceId(long page, long pageSize, String workspaceId) {
public PaginationData<UserListDTO> getUsersByOrgId(long page, long pageSize, String orgId) {
// 使用新的关联查询方式,基于用户-组织关联表查询
List<String> userIds = userOrgService.getUserIdsByOrgId(orgId);
if (CollectionUtils.isEmpty(userIds)) {
return new PaginationData<>(
List.of(),
new Pagination(page, pageSize, 0L)
);
}
// 分页查询符合条件的用户
Page<UserEntity> userEntityPage = mapper.selectPage(
new Page<>(page, pageSize),
new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getWorkspaceId, workspaceId));
new LambdaQueryWrapper<UserEntity>().in(UserEntity::getUserId, userIds)
);
// 查询用户在此组织中的角色
Map<String, Integer> userRoleMap = userOrgService.getUserRoleMapByOrgId(orgId);
// 获取组织对应的workspaceId
String workspaceId = orgService.getWorkspaceIdByOrgId(orgId);
List<UserListDTO> usersList = userEntityPage.getRecords()
.stream()
.map(this::entity2UserListDTO)
.map(entity -> {
// 使用组织中的角色覆盖用户默认角色
Integer roleInOrg = userRoleMap.getOrDefault(entity.getUserId(), entity.getRoleType());
return entity2UserListDTO(entity, workspaceId, roleInOrg, orgId);
})
.collect(Collectors.toList());
return new PaginationData<>(usersList, new Pagination(userEntityPage.getCurrent(), userEntityPage.getSize(), userEntityPage.getTotal()));
return new PaginationData<>(
usersList,
new Pagination(userEntityPage.getCurrent(), userEntityPage.getSize(), userEntityPage.getTotal())
);
}
@Override
public Boolean updateUser(String workspaceId, String userId, UserListDTO user) {
public Boolean updateUser(String orgId, String userId, UserListDTO user) {
UserEntity userEntity = mapper.selectOne(
new LambdaQueryWrapper<UserEntity>()
.eq(UserEntity::getUserId, userId)
.eq(UserEntity::getWorkspaceId, workspaceId));
.eq(UserEntity::getUserId, userId));
if (userEntity == null) {
return false;
}
// 检查用户是否在该组织中
Optional<UserOrgEntity> userOrgOpt = userOrgService.getUserOrg(userId, orgId);
if (userOrgOpt.isEmpty()) {
return false;
}
userEntity.setMqttUsername(user.getMqttUsername());
userEntity.setMqttPassword(user.getMqttPassword());
userEntity.setEmail(user.getEmail());
userEntity.setPhone(user.getPhone());
userEntity.setUpdateTime(System.currentTimeMillis());
// 设置更新者信息
userEntity.setUpdaterId(SecurityUtils.getUserId());
userEntity.setUpdaterName(SecurityUtils.getUsername());
int id = mapper.update(userEntity, new LambdaUpdateWrapper<UserEntity>()
.eq(UserEntity::getUserId, userId)
.eq(UserEntity::getWorkspaceId, workspaceId));
.eq(UserEntity::getUserId, userId));
// 更新用户在组织中的角色
if (user.getRoleType() != null) {
userOrgService.updateUserOrgRole(userId, orgId, user.getRoleType());
}
return id > 0;
}
@Override
public Boolean deleteUser(String workspaceId, String userId) {
public Boolean deleteUser(String orgId, String userId) {
// 管理员以上才能删除
aboveAdminRoleAndThrowError();
// if (isNotAdmin()) {
// throw new RuntimeException("The current user is not an admin and has no permissions");
// }
LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserEntity::getWorkspaceId, workspaceId);
queryWrapper.eq(UserEntity::getUserId, userId);
// 需要先查询
UserEntity userEntity = this.mapper.selectOne(queryWrapper);
// 不能删除自身
if (userEntity.getUserId().equals(getUserId())) {
if (userId.equals(getUserId())) {
throw new RuntimeException("can't delete self");
}
UserEntity userEntity = this.mapper.selectOne(
new LambdaQueryWrapper<UserEntity>()
.eq(UserEntity::getUserId, userId));
if (userEntity == null) {
return false;
}
// 不能删除高级用户
if (userEntity.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
aboveSysAdminRoleAndThrowError();
}
// if (userEntity.getUserType() == UserTypeEnum.WEB.getVal()) {
// throw new RuntimeException("Failed to delete admin");
// }
// 然后 假如是删除管理员 不能删除 项目管理员
if (userEntity.getRoleType() == RoleTypeEnum.PROJECT_ADMIN.getVal()) {
throw new RuntimeException("can't delete workspace project admin");
// 如果用户是超级管理员,不允许删除
if (userEntity.getIsSuperAdmin() == 1) {
throw new RuntimeException("Cannot delete super admin user");
}
// 检查用户在组织中的角色
Optional<UserOrgEntity> userOrgOpt = userOrgService.getUserOrg(userId, orgId);
if (userOrgOpt.isPresent()) {
UserOrgEntity userOrg = userOrgOpt.get();
// 如果用户是组织管理员,检查是否是唯一的组织管理员
if (userOrg.getRoleType() == RoleTypeEnum.ADMIN.getVal() ||
userOrg.getRoleType() == RoleTypeEnum.ORG_ADMIN.getVal()) {
// 检查是否还有其他管理员
List<String> adminUserIds = userOrgService.list(
new LambdaQueryWrapper<UserOrgEntity>()
.eq(UserOrgEntity::getOrgId, orgId)
.eq(UserOrgEntity::getRoleType, RoleTypeEnum.ADMIN.getVal())
.or()
.eq(UserOrgEntity::getRoleType, RoleTypeEnum.ORG_ADMIN.getVal())
).stream().map(UserOrgEntity::getUserId).collect(Collectors.toList());
if (adminUserIds.size() <= 1) {
throw new RuntimeException("Cannot delete the last administrator of the organization");
}
if (userEntity.getRoleType() == RoleTypeEnum.ADMIN.getVal()) {
LambdaQueryWrapper<WorkspaceEntity> workspaceWrapper = new LambdaQueryWrapper<>();
workspaceWrapper.eq(WorkspaceEntity::getAdminUserId, userEntity.getUserId());
List<WorkspaceEntity> workspaceEntities = workspaceService.list(workspaceWrapper);
if (!CollectionUtils.isEmpty(workspaceEntities)) {
throw new RuntimeException("can't delete workspace admin");
}
// 从组织中移除用户
userOrgService.removeUserFromOrg(userId, orgId);
}
int delete = mapper.delete(queryWrapper);
// 检查用户是否还属于任何组织
List<UserOrgDTO> remainingOrgs = userOrgService.getUserOrgs(userId);
// 如果用户不再属于任何组织,删除用户
if (CollectionUtils.isEmpty(remainingOrgs)) {
int delete = mapper.delete(
new LambdaQueryWrapper<UserEntity>()
.eq(UserEntity::getUserId, userId));
return delete > 0;
}
return true;
}
@Override
public Boolean addUser(String workspaceId, UserEntity user) {
public Boolean addUser(String orgId, UserEntity user) {
// 管理员才能创建用户
aboveAdminRoleAndThrowError();
......@@ -229,73 +392,97 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
String username = user.getUsername();
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 userEntity = new UserEntity();
// 普通用户不能创建管理员
// if (user.getUserType() == UserTypeEnum.WEB.getVal()) {
// if (isNotAdmin()) {
// throw new RuntimeException("The current user is not an admin and has no permissions");
// }
// }
// 不能创建高级用户
if (user.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
aboveSysAdminRoleAndThrowError();
}
// 检查用户是否已存在
UserEntity userEntity;
if (!CollectionUtils.isEmpty(nameUserList)) {
// 如果用户已存在,检查是否已在该组织中
userEntity = nameUserList.get(0);
Optional<UserOrgEntity> userOrgOpt = userOrgService.getUserOrg(userEntity.getUserId(), orgId);
if (userOrgOpt.isPresent()) {
throw new RuntimeException("User already exists in this organization");
}
} else {
// 创建新用户
userEntity = new UserEntity();
userEntity.setUserType(user.getUserType() != null ? user.getUserType() : UserTypeEnum.PILOT.getVal());
userEntity.setRoleType(user.getRoleType() != null ? user.getRoleType() : RoleTypeEnum.MEMBER.getVal());
userEntity.setUserId(UUID.randomUUID().toString());
userEntity.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
userEntity.setUsername(user.getUsername());
userEntity.setWorkspaceId(workspaceId);
userEntity.setEmail(user.getEmail());
userEntity.setPhone(user.getPhone());
userEntity.setIsSuperAdmin(user.getIsSuperAdmin() != null ? user.getIsSuperAdmin() : 0);
userEntity.setCreateTime(System.currentTimeMillis());
userEntity.setUpdateTime(System.currentTimeMillis());
int insert = this.mapper.insert(userEntity);
return insert > 0;
}
// 设置创建者和更新者信息
String currentUserId = SecurityUtils.getUserId();
String currentUserName = SecurityUtils.getUsername();
@Transactional(rollbackFor = Exception.class)
@Override
public UserEntity addWorkspaceAdminUser(UserEntity user) {
userEntity.setCreatorId(currentUserId);
userEntity.setCreatorName(currentUserName);
userEntity.setUpdaterId(currentUserId);
userEntity.setUpdaterName(currentUserName);
// 系统管理员才能创建
aboveSysAdminRoleAndThrowError();
// 保存用户信息
this.mapper.insert(userEntity);
}
// 用户名不能重复
String username = user.getUsername();
LambdaQueryWrapper<UserEntity> userQueryWrapper = new LambdaQueryWrapper<>();
userQueryWrapper.eq(UserEntity::getUsername, username);
// userQueryWrapper.eq(UserEntity::getWorkspaceId, user.getWorkspaceId());
List<UserEntity> nameUserList = this.list(userQueryWrapper);
if (!CollectionUtils.isEmpty(nameUserList)) {
throw new RuntimeException("the username is already existed");
// 不能创建高级用户
if (user.getRoleType() == RoleTypeEnum.SYS_ADMIN.getVal()) {
aboveSysAdminRoleAndThrowError();
}
UserEntity userEntity = new UserEntity();
// 保存用户与组织的关联信息
if (StringUtils.hasText(orgId)) {
return userOrgService.addUserToOrg(
userEntity.getUserId(),
orgId,
user.getRoleType() != null ? user.getRoleType() : RoleTypeEnum.MEMBER.getVal()
);
}
userEntity.setUserType(UserTypeEnum.WEB.getVal());
userEntity.setRoleType(RoleTypeEnum.ADMIN.getVal());
userEntity.setUserId(UUID.randomUUID().toString());
userEntity.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
userEntity.setUsername(user.getUsername());
userEntity.setWorkspaceId(user.getWorkspaceId());
return true;
}
boolean save = this.save(userEntity);
/**
* Convert database entity objects into user data transfer object.
* @param entity 用户实体
* @param workspaceId 工作空间ID
* @param roleTypeInOrg 用户在指定组织中的角色
* @param orgId 组织ID
* @return 用户列表展示对象
*/
private UserListDTO entity2UserListDTO(UserEntity entity, String workspaceId, Integer roleTypeInOrg, String orgId) {
UserListDTO.UserListDTOBuilder builder = UserListDTO.builder();
if (entity != null) {
builder.userId(entity.getUserId())
.id(entity.getId())
.username(entity.getUsername())
.mqttUsername(entity.getMqttUsername())
.mqttPassword(entity.getMqttPassword())
.userType(UserTypeEnum.find(entity.getUserType()).getDesc())
.roleTypeName(RoleTypeEnum.find(roleTypeInOrg).getDesc()) // 使用组织内的角色
.roleType(roleTypeInOrg) // 使用组织内的角色
.email(entity.getEmail())
.phone(entity.getPhone())
.isSuperAdmin(entity.getIsSuperAdmin())
.createTime(LocalDateTime.ofInstant(
Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()));
return userEntity;
// 获取组织名称
Optional<OrgDTO> orgOpt = orgService.getOrgByOrgId(orgId);
orgOpt.ifPresent(org -> builder.workspaceName(org.getOrgName())); // 用组织名称代替工作空间名称
}
return builder.build();
}
/**
* Convert database entity objects into user data transfer object.
* @param entity
* @return
* 为了保持兼容性保留原方法
*/
private UserListDTO entity2UserListDTO(UserEntity entity) {
UserListDTO.UserListDTOBuilder builder = UserListDTO.builder();
......@@ -308,10 +495,11 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
.userType(UserTypeEnum.find(entity.getUserType()).getDesc())
.roleTypeName(RoleTypeEnum.find(entity.getRoleType()).getDesc())
.roleType(entity.getRoleType())
.email(entity.getEmail())
.phone(entity.getPhone())
.isSuperAdmin(entity.getIsSuperAdmin())
.createTime(LocalDateTime.ofInstant(
Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()));
Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(entity.getWorkspaceId());
workspaceOpt.ifPresent(workspace -> builder.workspaceName(workspace.getWorkspaceName()));
}
return builder.build();
......@@ -354,6 +542,9 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
.mqttUsername(entity.getMqttUsername())
.mqttPassword(entity.getMqttPassword())
.mqttAddr(MqttPropertyConfiguration.getBasicMqttAddress())
.email(entity.getEmail())
.phone(entity.getPhone())
.isSuperAdmin(entity.getIsSuperAdmin())
.build();
}
......@@ -378,4 +569,34 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
return HttpResultResponse.success(user);
}
// 更新方法签名以适应组织架构
@Override
public PaginationData<UserListDTO> getUsersByWorkspaceId(long page, long pageSize, String workspaceId) {
// 查找使用该workspaceId的所有组织
List<OrgDTO> orgs = orgService.list(
new LambdaQueryWrapper<OrgEntity>()
.eq(OrgEntity::getWorkspaceId, workspaceId)
).stream().map(org -> OrgDTO.builder()
.orgId(org.getOrgId())
.orgName(org.getOrgName())
.workspaceId(org.getWorkspaceId())
.build()).collect(Collectors.toList());
if (CollectionUtils.isEmpty(orgs)) {
return new PaginationData<>(
List.of(),
new Pagination(page, pageSize, 0L)
);
}
// 使用第一个组织的用户列表
return getUsersByOrgId(page, pageSize, orgs.get(0).getOrgId());
}
// addWorkspaceAdminUser
@Override
public UserEntity addWorkspaceAdminUser(UserEntity user) {
return user;
}
}
......@@ -120,6 +120,7 @@ public class WorkspaceServiceImpl extends ServiceImpl<IWorkspaceMapper, Workspac
// 新增workspace管理员
UserEntity workspaceAdmin = new UserEntity();
workspaceAdmin.setWorkspaceId(addWorkspace.getWorkspaceId());
workspaceAdmin.setOrgId(addWorkspace.getOrgId());
workspaceAdmin.setUsername(workspaceDTO.getAdminUserName());
workspaceAdmin.setPassword(workspaceDTO.getAdminUserPassword());
UserEntity saveAdmin = userService.addWorkspaceAdminUser(workspaceAdmin);
......
......@@ -31,10 +31,12 @@ public class MediaFileEntity implements Serializable {
@TableField("file_path")
private String filePath;
@TableField("workspace_id")
private String workspaceId;
@TableField("org_id")
private String orgId;
@TableField("fingerprint")
private String fingerprint;
......
......@@ -37,10 +37,12 @@ public class WaylineFileEntity implements Serializable {
@TableField("sign")
private String sign;
@TableField("workspace_id")
private String workspaceId;
@TableField("org_id")
private String orgId;
@TableField("favorited")
private Boolean favorited;
......
......@@ -34,10 +34,12 @@ public class WaylineJobEntity implements Serializable {
@TableField("dock_sn")
private String dockSn;
@TableField("workspace_id")
private String workspaceId;
@TableField("org_id")
private String orgId;
@TableField("task_type")
private Integer taskType;
......
......@@ -10,10 +10,12 @@ spring:
druid:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://geoair_mysql:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
# url: jdbc:mysql://geoair_mysql:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
# url: jdbc:mysql://cloud_api_sample_mysql:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true
# 深圳
# url: jdbc:mysql://cloud_api_sample_mysql:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true
# 本地
url: jdbc:mysql://localhost:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: root
initial-size: 10
......@@ -22,10 +24,12 @@ spring:
max-wait: 60000
redis:
host: geoair_redis
# host: geoair_redis
# host: cloud_api_sample_redis
# 深圳
# host: cloud_api_sample_redis
# 本地
host: localhost
port: 6379
database: 0
username: # if you enable
......@@ -65,10 +69,10 @@ mqtt:
# port: 54418
# host: 203.186.109.106
# host: emqx-broker
# host: 192.168.32.90
# port: 44418
host: 203.186.109.106
port: 54941
host: 192.168.32.90
port: 44418
# host: 203.186.109.106
# port: 54941
# host: emqx-broker
# port: 1883
username: JavaServer
......
-- 1. 组织表
CREATE TABLE `manage_org` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`org_id` VARCHAR(64) NOT NULL UNIQUE COMMENT '组织 UUID',
`org_name` VARCHAR(64) NOT NULL COMMENT '组织名称',
`org_desc` VARCHAR(256) DEFAULT '' COMMENT '组织描述',
`workspace_id` VARCHAR(64) NOT NULL COMMENT '统一 workspace ID,所有 org 共享同一个工作空间',
`create_time` BIGINT NOT NULL,
`update_time` BIGINT NOT NULL,
`creator_id` VARCHAR(64) COMMENT '创建者ID',
`creator_name` VARCHAR(64) COMMENT '创建者名称',
`updater_id` VARCHAR(64) COMMENT '更新者ID',
`updater_name` VARCHAR(64) COMMENT '更新者名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='组织信息表';
-- 2. 用户组织关系表
CREATE TABLE `manage_user_org` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` VARCHAR(64) NOT NULL COMMENT '用户 ID',
`org_id` VARCHAR(64) NOT NULL COMMENT '组织 ID',
`role_type` SMALLINT NOT NULL COMMENT '角色类型(100:SUPER_ADMIN, 50:ORG_ADMIN, 3:FLEET_ADMIN, 2:PILOT, 1:USER)',
`status` TINYINT(1) DEFAULT 1,
`create_time` BIGINT NOT NULL,
`update_time` BIGINT NOT NULL,
`creator_id` VARCHAR(64) COMMENT '创建者ID',
`creator_name` VARCHAR(64) COMMENT '创建者名称',
`updater_id` VARCHAR(64) COMMENT '更新者ID',
`updater_name` VARCHAR(64) COMMENT '更新者名称',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_user_org` (`user_id`, `org_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户与组织关系(含角色)';
-- 3. 设备组织归属表(支持设备所属组织和共享)
CREATE TABLE `manage_device_org` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`device_id` INT NOT NULL COMMENT '设备 ID',
`org_id` VARCHAR(64) NOT NULL COMMENT '组织 ID',
`is_shared` TINYINT(1) DEFAULT 0 COMMENT '是否共享给其他组织(0: 否,1: 是)',
`create_time` BIGINT NOT NULL DEFAULT 0 COMMENT '创建时间',
`update_time` BIGINT NOT NULL DEFAULT 0 COMMENT '更新时间',
`creator_id` VARCHAR(64) COMMENT '创建者ID',
`creator_name` VARCHAR(64) COMMENT '创建者名称',
`updater_id` VARCHAR(64) COMMENT '更新者ID',
`updater_name` VARCHAR(64) COMMENT '更新者名称',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_device_org` (`device_id`, `org_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='设备与组织关系表';
-- 4. 修改 manage_user 表字段
-- 添加超级管理员标志、联系信息,并统一角色字段类型
-- 注意:在新架构中,我们将同时保留用户的 workspace_id 字段以实现平滑过渡
ALTER TABLE `manage_user`
ADD COLUMN `org_id` VARCHAR(64) DEFAULT NULL COMMENT '用户主要关联的组织ID';
ALTER TABLE `manage_user`
ADD COLUMN `is_super_admin` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否为运营方超级管理员(0:否,1:是)';
ALTER TABLE `manage_user`
ADD COLUMN `email` VARCHAR(64) DEFAULT '' COMMENT '用户登录邮箱';
ALTER TABLE `manage_user`
ADD COLUMN `phone` VARCHAR(20) DEFAULT '' COMMENT '用户手机号';
ALTER TABLE `manage_user`
ADD COLUMN `creator_id` VARCHAR(64) COMMENT '创建者ID';
ALTER TABLE `manage_user`
ADD COLUMN `creator_name` VARCHAR(64) COMMENT '创建者名称';
ALTER TABLE `manage_user`
ADD COLUMN `updater_id` VARCHAR(64) COMMENT '更新者ID';
ALTER TABLE `manage_user`
ADD COLUMN `updater_name` VARCHAR(64) COMMENT '更新者名称';
-- 修改字段 role_type 的定义
ALTER TABLE `manage_user`
MODIFY COLUMN `role_type` SMALLINT NOT NULL DEFAULT 1 COMMENT '账户默认角色(100:SUPER_ADMIN, 50:ORG_ADMIN, 3:FLEET_ADMIN, 2:PILOT, 1:USER)';
-- 5. 修改 manage_fleet_user 表
-- 添加用户在机队中的角色字段(整型枚举)
-- ALTER TABLE `manage_fleet_user`
-- ADD COLUMN IF NOT EXISTS `role` SMALLINT NOT NULL DEFAULT 2 COMMENT '用户在该机队内的角色(3:FLEET_ADMIN, 2:PILOT)',
-- ADD COLUMN IF NOT EXISTS `creator_id` VARCHAR(64) COMMENT '创建者ID',
-- ADD COLUMN IF NOT EXISTS `creator_name` VARCHAR(64) COMMENT '创建者名称',
-- ADD COLUMN IF NOT EXISTS `updater_id` VARCHAR(64) COMMENT '更新者ID',
-- ADD COLUMN IF NOT EXISTS `updater_name` VARCHAR(64) COMMENT '更新者名称';
ALTER TABLE `manage_fleet_user`
ADD COLUMN `updater_name` VARCHAR(64) COMMENT '更新者名称';
ALTER TABLE `manage_fleet_user`
ADD COLUMN `creator_id` VARCHAR(64) COMMENT '创建者ID';
ALTER TABLE `manage_fleet_user`
ADD COLUMN `creator_name` VARCHAR(64) COMMENT '创建者名称';
ALTER TABLE `manage_fleet_user`
ADD COLUMN `updater_id` VARCHAR(64) COMMENT '更新者ID';
ALTER TABLE `manage_fleet_user`
ADD COLUMN `updater_name` VARCHAR(64) COMMENT '更新者名称';
-- 6. 关于旧架构字段的处理
-- 说明:为了最小化修改,我们保留现有的 workspace_id 字段和相关表结构
-- 在新架构中,用户可以通过组织间接关联到工作空间,也可以保留直接关联
-- 用户同时属于组织和工作空间,组织属于工作空间
-- 注释:如果未来确定完全迁移到新架构,可以考虑删除这些结构
-- ALTER TABLE `manage_user` DROP COLUMN IF EXISTS `workspace_id`;
-- DROP TABLE IF EXISTS `manage_user_workspace`;
-- 添加索引以提高关联查询效率
ALTER TABLE `manage_user`
ADD INDEX IF NOT EXISTS `idx_workspace_id` (`workspace_id`),
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
-- 7. 初始化数据(如果需要)
-- 7.1 创建默认工作空间
-- INSERT INTO `manage_workspace` (`workspace_id`, `workspace_name`, `create_time`, `update_time`)
-- VALUES ('default-workspace-id', '默认工作空间', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
-- 7.2 创建默认组织
-- INSERT INTO `manage_org` (`org_id`, `org_name`, `org_desc`, `workspace_id`, `create_time`, `update_time`, `creator_id`, `creator_name`, `updater_id`, `updater_name`)
-- VALUES (UUID(), '默认组织', '系统默认创建的组织', 'default-workspace-id', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'system', 'system', 'system', 'system');
-- 7.3 创建超级管理员用户
-- INSERT INTO `manage_user` (`user_id`, `username`, `password`, `user_type`, `role_type`, `workspace_id`, `org_id`, `is_super_admin`, `email`, `create_time`, `update_time`, `creator_id`, `creator_name`, `updater_id`, `updater_name`)
-- VALUES (UUID(), 'superadmin', 'YOUR_ENCRYPTED_PASSWORD', 1, 100, 'default-workspace-id', (SELECT `org_id` FROM `manage_org` WHERE `org_name` = '默认组织' LIMIT 1), 1, 'admin@example.com', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'system', 'system', 'system', 'system');
-- 7.4 将超级管理员添加到默认组织
-- INSERT INTO `manage_user_org` (`user_id`, `org_id`, `role_type`, `status`, `create_time`, `update_time`, `creator_id`, `creator_name`, `updater_id`, `updater_name`)
-- SELECT u.user_id, o.org_id, 100, 1, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'system', 'system', 'system', 'system'
-- FROM `manage_user` u, `manage_org` o
-- WHERE u.username = 'superadmin' AND o.org_name = '默认组织';
-- 7.5 保持 manage_user_workspace 表的内容同步(可选)
-- INSERT INTO `manage_user_workspace` (`user_id`, `workspace_id`, `role_type`, `status`, `create_time`, `update_time`)
-- SELECT u.user_id, u.workspace_id, u.role_type, 1, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000
-- FROM `manage_user` u
-- WHERE u.username = 'superadmin';
-- 8. 为其他相关表添加 org_id 字段
-- 8.1 设备相关表
ALTER TABLE `manage_device`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '设备所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
-- 8.2 媒体文件表
ALTER TABLE `media_file`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '媒体文件所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
-- 8.3 航线相关表
ALTER TABLE `wayline_file`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '航线文件所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `wayline_job`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '航线任务所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
-- 8.4 地图相关表
ALTER TABLE `map_group`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '地图组所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `map_group_element`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '地图元素所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `flight_area_file`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '飞行区域文件所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `device_flight_area`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '设备飞行区域所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
-- 8.5 日志相关表
ALTER TABLE `logs_file`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '日志文件所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `logs_file_index`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '日志文件索引所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `manage_device_logs`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '设备日志所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `manage_device_hms`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '设备HMS所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `manage_device_payload`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '设备载荷所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
ALTER TABLE `manage_device_firmware`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '设备固件所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
-- 8.6 机队相关表
-- 8.6.1 创建机队表(如果不存在)
CREATE TABLE IF NOT EXISTS `manage_fleet` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`fleet_id` VARCHAR(64) NOT NULL UNIQUE COMMENT '机队 UUID',
`fleet_name` VARCHAR(64) NOT NULL COMMENT '机队名称',
`fleet_desc` VARCHAR(256) DEFAULT '' COMMENT '机队描述',
`org_id` VARCHAR(64) NOT NULL COMMENT '机队所属组织ID',
`workspace_id` VARCHAR(64) NOT NULL COMMENT '机队所属工作空间ID',
`create_time` BIGINT NOT NULL,
`update_time` BIGINT NOT NULL,
`creator_id` VARCHAR(64) COMMENT '创建者ID',
`creator_name` VARCHAR(64) COMMENT '创建者名称',
`updater_id` VARCHAR(64) COMMENT '更新者ID',
`updater_name` VARCHAR(64) COMMENT '更新者名称',
PRIMARY KEY (`id`),
INDEX `idx_org_id` (`org_id`),
INDEX `idx_workspace_id` (`workspace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机队信息表';
-- 8.6.2 创建机队设备关系表(如果不存在)
CREATE TABLE IF NOT EXISTS `manage_fleet_device` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`fleet_id` VARCHAR(64) NOT NULL COMMENT '机队 ID',
`device_id` INT NOT NULL COMMENT '设备 ID',
`device_sn` VARCHAR(64) NOT NULL COMMENT '设备 SN',
`org_id` VARCHAR(64) NOT NULL COMMENT '关系所属组织ID',
`create_time` BIGINT NOT NULL,
`update_time` BIGINT NOT NULL,
`creator_id` VARCHAR(64) COMMENT '创建者ID',
`creator_name` VARCHAR(64) COMMENT '创建者名称',
`updater_id` VARCHAR(64) COMMENT '更新者ID',
`updater_name` VARCHAR(64) COMMENT '更新者名称',
PRIMARY KEY (`id`),
UNIQUE KEY `uq_fleet_device` (`fleet_id`, `device_id`),
INDEX `idx_org_id` (`org_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='机队与设备关系表';
-- 8.6.3 为已存在的 manage_fleet_user 表添加 org_id 字段
ALTER TABLE `manage_fleet_user`
ADD COLUMN IF NOT EXISTS `org_id` VARCHAR(64) DEFAULT NULL COMMENT '机队用户关系所属组织ID',
ADD INDEX IF NOT EXISTS `idx_org_id` (`org_id`);
-- 9. 数据迁移脚本(可选)
-- 将现有数据根据 workspace_id 关联到默认组织
-- 9.1 为设备数据关联组织
-- UPDATE `manage_device` d
-- SET d.org_id = (
-- SELECT o.org_id
-- FROM `manage_org` o
-- WHERE o.workspace_id = d.workspace_id
-- AND o.org_name = '默认组织'
-- LIMIT 1
-- )
-- WHERE d.org_id IS NULL;
-- 9.2 为媒体文件关联组织
-- UPDATE `media_file` m
-- SET m.org_id = (
-- SELECT o.org_id
-- FROM `manage_org` o
-- WHERE o.workspace_id = m.workspace_id
-- AND o.org_name = '默认组织'
-- LIMIT 1
-- )
-- WHERE m.org_id IS NULL;
-- 9.3 为航线文件关联组织
-- UPDATE `wayline_file` w
-- SET w.org_id = (
-- SELECT o.org_id
-- FROM `manage_org` o
-- WHERE o.workspace_id = w.workspace_id
-- AND o.org_name = '默认组织'
-- LIMIT 1
-- )
-- WHERE w.org_id IS NULL;
-- 9.4 为航线任务关联组织
-- UPDATE `wayline_job` j
-- SET j.org_id = (
-- SELECT o.org_id
-- FROM `manage_org` o
-- WHERE o.workspace_id = j.workspace_id
-- AND o.org_name = '默认组织'
-- LIMIT 1
-- )
-- WHERE j.org_id IS NULL;
-- 9.5 为地图组关联组织
-- UPDATE `map_group` g
-- SET g.org_id = (
-- SELECT o.org_id
-- FROM `manage_org` o
-- WHERE o.workspace_id = g.workspace_id
-- AND o.org_name = '默认组织'
-- LIMIT 1
-- )
-- WHERE g.org_id IS NULL;
-- 注意:执行数据迁移前请备份数据库!
\ No newline at end of file
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