1. 中铝e办组织机构人员同步联调接口

2. 设置默认文件预览水印为人员加日期
This commit is contained in:
chenbowen
2025-08-19 14:34:36 +08:00
parent 1555eb1085
commit 07c9ead358
51 changed files with 2011 additions and 47 deletions

View File

@@ -12,6 +12,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
@@ -31,6 +32,9 @@ public class BaseDbUnitTest {
@MockBean
private SequenceCommonApi sequenceCommonApi;
@MockBean
private FeignClientFactory feignClientFactory;
@Import({
// DB 配置类
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类

View File

@@ -8,6 +8,7 @@ import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
@@ -25,6 +26,9 @@ public class BaseRedisUnitTest {
@MockBean
private SequenceCommonApi sequenceCommonApi;
@MockBean
private FeignClientFactory feignClientFactory;
@Import({
// Redis 配置类
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer

View File

@@ -23,26 +23,18 @@ public class BannerApplicationRunner implements ApplicationRunner {
"项目启动成功!\n\t" +
"接口文档: \t{} \n\t" +
"开发文档: \t{} \n\t" +
"视频教程: \t{} \n" +
"----------------------------------------------------------",
"http://172.16.46.63:30888/api-doc/",
"http://172.16.46.63:30888",
"https://t.zsxq.com/02Yf6M7Qn");
"http://172.16.46.63:30888");
// 数据报表
System.out.println("[报表模块 yudao-module-report 教程][参考 http://172.16.46.63:30888/report/ 开启]");
// 工作流
System.out.println("[工作流模块 yudao-module-bpm 教程][参考 http://172.16.46.63:30888/bpm/ 开启]");
// 商城系统
System.out.println("[商城系统 yudao-module-mall 教程][参考 http://172.16.46.63:30888/mall/build/ 开启]");
// ERP 系统
System.out.println("[ERP 系统 yudao-module-erp - 教程][参考 http://172.16.46.63:30888/erp/build/ 开启]");
// CRM 系统
System.out.println("[CRM 系统 yudao-module-crm - 教程][参考 http://172.16.46.63:30888/crm/build/ 开启]");
// 微信公众号
System.out.println("[微信公众号 yudao-module-mp 教程][参考 http://172.16.46.63:30888/mp/build/ 开启]");
// 支付平台
System.out.println("[支付系统 yudao-module-pay - 教程][参考 http://172.16.46.63:30888/pay/build/ 开启]");
// AI 大模型
System.out.println("[AI 大模型 yudao-module-ai - 教程][参考 http://172.16.46.63:30888/ai/build/ 开启]");
});

View File

@@ -25,26 +25,18 @@ public class BannerApplicationRunner implements ApplicationRunner {
"项目启动成功!\n\t" +
"接口文档: \t{} \n\t" +
"开发文档: \t{} \n\t" +
"视频教程: \t{} \n" +
"----------------------------------------------------------",
"http://172.16.46.63:30888/api-doc/",
"http://172.16.46.63:30888",
"https://t.zsxq.com/02Yf6M7Qn");
"http://172.16.46.63:30888");
// 数据报表
System.out.println("[报表模块 yudao-module-report 教程][参考 http://172.16.46.63:30888/report/ 开启]");
// 工作流
System.out.println("[工作流模块 yudao-module-bpm 教程][参考 http://172.16.46.63:30888/bpm/ 开启]");
// 商城系统
System.out.println("[商城系统 yudao-module-mall 教程][参考 http://172.16.46.63:30888/mall/build/ 开启]");
// ERP 系统
System.out.println("[ERP 系统 yudao-module-erp - 教程][参考 http://172.16.46.63:30888/erp/build/ 开启]");
// CRM 系统
System.out.println("[CRM 系统 yudao-module-crm - 教程][参考 http://172.16.46.63:30888/crm/build/ 开启]");
// 微信公众号
System.out.println("[微信公众号 yudao-module-mp 教程][参考 http://172.16.46.63:30888/mp/build/ 开启]");
// 支付平台
System.out.println("[支付系统 yudao-module-pay - 教程][参考 http://172.16.46.63:30888/pay/build/ 开启]");
// AI 大模型
System.out.println("[AI 大模型 yudao-module-ai - 教程][参考 http://172.16.46.63:30888/ai/build/ 开启]");
// IOT 物联网

View File

@@ -3,7 +3,7 @@
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
server-addr: 172.16.46.63:30848 # Nacos 服务器地址
username: # Nacos 账号
password: # Nacos 密码
discovery: # 【配置中心】配置项
@@ -58,29 +58,20 @@ spring:
primary: master
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例
# url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
username: root
password: 123456
# username: sa # SQL Server 连接的示例
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例
# username: SYSDBA # DM 连接的示例
# password: SYSDBA # DM 连接的示例
url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO
username: SYSDBA
password: pgbsci6ddJ6Sqj@e
slave: # 模拟从库,可根据自己需要修改
lazy: true # 开启懒加载,保证启动速度
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
username: root
password: 123456
url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO
username: SYSDBA
password: pgbsci6ddJ6Sqj@e
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口
host: 172.16.46.63 # 地址
port: 30379 # 端口
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启

View File

@@ -165,4 +165,15 @@ public class FileController {
return CommonResult.customize(fileRespVO, HttpStatus.OK.value(), e.getMessage());
}
}
@GetMapping("/verify-code")
@Operation(summary = "校验验证码")
public CommonResult<String> verifyCode(@Valid @RequestParam Long fileId, @RequestParam String code) throws Exception {
Long userId = getLoginUserId();
byte[] content = fileService.verifyCodeAndGetFile(fileId, userId, code);
if(content == null || content.length == 0){
return CommonResult.customize(null, HttpStatus.INTERNAL_SERVER_ERROR.value(), "验证码校验失败");
}
return CommonResult.customize(null, HttpStatus.OK.value(), "验证码校验通过");
}
}

View File

@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient;
import cn.iocoder.yudao.module.infra.service.file.FileConfigService;
@@ -11,6 +13,7 @@ import lombok.experimental.Accessors;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Date;
/**
* @author chenbowen
@@ -78,7 +81,9 @@ public class FileRespVO {
}
String base64PresignedUrl = Base64.getEncoder().encodeToString(presignedUrl.getBytes(StandardCharsets.UTF_8));
String timestamp = String.valueOf(System.currentTimeMillis());
String watermark = SpringUtils.getProperty("aj.captcha.water-mark", "中国铜业");
String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();
String format = DateUtil.format(new Date(), "yyyy-MM-dd");
String watermark = SpringUtils.getProperty("aj.captcha.water-mark", loginUserNickname+" "+ format);
return onlinePreview + base64PresignedUrl + "&t=" + timestamp + "&watermarkTxt=" + watermark;
}
@Schema(description = "是否加密", example = "false")

View File

@@ -54,7 +54,7 @@ public interface ErrorCodeConstants {
// ========== 部门模块 1-002-004-000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在");
ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在");
ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "机构不存在或当前账号无权限修改");
ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除");
ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门");
ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择");
@@ -153,6 +153,13 @@ public interface ErrorCodeConstants {
ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在");
ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期");
// ========== 同步验证工具 1-002-019-000 ==========
ErrorCode SYNC_DECRYPT_TYPE = new ErrorCode(1_002_019_000, "解密失败");
ErrorCode SYNC_ENCRYPT_TYPE = new ErrorCode(1_002_019_001, "加密失败");
ErrorCode SYNC_SIGNATURE_UNSUPPORTED_TYPE = new ErrorCode(1_002_019_002, "签名类型不存在");
// 同步签名验证失败
ErrorCode SYNC_SIGNATURE_VERIFY_FAILED = new ErrorCode(1_002_019_003, "签名验证失败");
// ========== 邮箱账号 1-002-023-000 ==========
ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1_002_023_000, "邮箱账号不存在");
ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1_002_023_001, "无法删除,该邮箱账号还有邮件模板");

View File

@@ -44,7 +44,6 @@ public class DeptSaveReqVO {
private String email;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;

View File

@@ -0,0 +1,293 @@
package cn.iocoder.yudao.module.system.controller.admin.sync;
import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginReqVO;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.org.*;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaRequestVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaResponseVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.user.*;
import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
import cn.iocoder.yudao.module.system.service.sync.OrgSyncService;
import cn.iocoder.yudao.module.system.service.sync.SchemaSyncService;
import cn.iocoder.yudao.module.system.service.sync.UserSyncService;
import cn.iocoder.yudao.module.system.util.sync.SyncVerifyUtil;
import com.alibaba.fastjson.JSON;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getRequest;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
* 对中铝 e 办 BIM 接口 - 统一 Sync Controller
*/
@RestController
@RequestMapping("/system/sync")
public class SyncController {
@Value("${sync.encrypt-key}")
private String encryptKey;
@Resource
private SchemaSyncService schemaService;
@Resource
private OrgSyncService organizationService;
@Resource
private UserSyncService accountService;
@Resource
private AdminAuthService adminAuthService;
@Resource
private OAuth2TokenCommonApi oauth2TokenApi;
@PostMapping("/SchemaService")
@TenantIgnore
@PermitAll
public SchemaResponseVO getSchema(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
SchemaRequestVO req = decryptRequest(encryptedBody, SchemaRequestVO.class);
bimRequestId = req.getBimRequestId();
return schemaService.getSchema(req);
} catch (Exception e) {
SchemaResponseVO errorResp = new SchemaResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/OrgCreateService")
@TenantIgnore
@PermitAll
public OrgCreateResponseVO createOrg(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
OrgCreateRequestVO req = decryptRequest(encryptedBody, OrgCreateRequestVO.class);
bimRequestId = req.getBimRequestId();
return organizationService.createOrg(req);
} catch (Exception e) {
OrgCreateResponseVO errorResp = new OrgCreateResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/OrgDeleteService")
@TenantIgnore
@PermitAll
public OrgDeleteResponseVO deleteOrg(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
OrgDeleteRequestVO req = decryptRequest(encryptedBody, OrgDeleteRequestVO.class);
bimRequestId = req.getBimRequestId();
return organizationService.deleteOrg(req);
} catch (Exception e) {
OrgDeleteResponseVO errorResp = new OrgDeleteResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/OrgUpdateService")
@TenantIgnore
@PermitAll
public OrgUpdateResponseVO updateOrg(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
OrgUpdateRequestVO req = decryptRequest(encryptedBody, OrgUpdateRequestVO.class);
bimRequestId = req.getBimRequestId();
return organizationService.updateOrg(req);
} catch (Exception e) {
OrgUpdateResponseVO errorResp = new OrgUpdateResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/QueryAllOrgIdsService")
@TenantIgnore
@PermitAll
public OrgListResponseVO listOrg(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
OrgListRequestVO req = decryptRequest(encryptedBody, OrgListRequestVO.class);
bimRequestId = req.getBimRequestId();
return organizationService.listOrgIds(req);
} catch (Exception e) {
OrgListResponseVO errorResp = new OrgListResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/QueryOrgByIdService")
@TenantIgnore
@PermitAll
public OrgGetResponseVO getOrg(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
OrgGetRequestVO req = decryptRequest(encryptedBody, OrgGetRequestVO.class);
bimRequestId = req.getBimRequestId();
return organizationService.getOrgById(req);
} catch (Exception e) {
OrgGetResponseVO errorResp = new OrgGetResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/UserCreateService")
@TenantIgnore
@PermitAll
public UserCreateResponseVO createUser(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
UserCreateRequestVO req = decryptRequest(encryptedBody, UserCreateRequestVO.class);
bimRequestId = req.getBimRequestId();
return accountService.createUser(req);
} catch (Exception e) {
UserCreateResponseVO errorResp = new UserCreateResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/UserDeleteService")
@TenantIgnore
@PermitAll
public UserDeleteResponseVO deleteUser(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
UserDeleteRequestVO req = decryptRequest(encryptedBody, UserDeleteRequestVO.class);
bimRequestId = req.getBimRequestId();
return accountService.deleteUser(req);
} catch (Exception e) {
UserDeleteResponseVO errorResp = new UserDeleteResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/UserUpdateService")
@TenantIgnore
@PermitAll
public UserUpdateResponseVO updateUser(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
UserUpdateRequestVO req = decryptRequest(encryptedBody, UserUpdateRequestVO.class);
bimRequestId = req.getBimRequestId();
return accountService.updateUser(req);
} catch (Exception e) {
UserUpdateResponseVO errorResp = new UserUpdateResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/QueryAllUserIdsService")
@TenantIgnore
@PermitAll
public UserListResponseVO listUser(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
UserListRequestVO req = decryptRequest(encryptedBody, UserListRequestVO.class);
bimRequestId = req.getBimRequestId();
return accountService.listUserIds(req);
} catch (Exception e) {
UserListResponseVO errorResp = new UserListResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
@PostMapping("/QueryUserByIdService")
@TenantIgnore
@PermitAll
public UserGetResponseVO getUser(@RequestBody String encryptedBody) {
String bimRequestId = null;
try {
UserGetRequestVO req = decryptRequest(encryptedBody, UserGetRequestVO.class);
bimRequestId = req.getBimRequestId();
return accountService.getUserById(req);
} catch (Exception e) {
UserGetResponseVO errorResp = new UserGetResponseVO();
errorResp.setBimRequestId(bimRequestId);
errorResp.setResultCode("500");
errorResp.setMessage(e.getMessage());
return errorResp;
}
}
private <T> T decryptRequest(String encryptedBody, Class<T> clazz) {
String bodyJson;
try {
bodyJson = SyncVerifyUtil.decrypt(encryptedBody, encryptKey, "AES");
} catch (Exception e) {
throw exception(SYNC_DECRYPT_TYPE);
}
Map<String, Object> map = JSON.parseObject(bodyJson, Map.class);
if (!SyncVerifyUtil.verifySignature(map, "MD5")) {
throw exception(SYNC_SIGNATURE_VERIFY_FAILED);
}
AuthLoginReqVO authLoginReqVO = new AuthLoginReqVO();
authLoginReqVO.setUsername((String) map.getOrDefault("bimRemoteUser", ""));
authLoginReqVO.setPassword((String) map.getOrDefault("bimRemotePwd", ""));
AuthLoginRespVO login = adminAuthService.login(authLoginReqVO);
// 校验访问令牌
OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(login.getAccessToken()).getCheckedData();
if (accessToken == null) {
throw exception(AUTH_LOGIN_USER_DISABLED);
}
LoginUser loginUser = new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
.setInfo(accessToken.getUserInfo())
.setTenantId(accessToken.getTenantId())
.setScopes(accessToken.getScopes())
.setExpiresTime(accessToken.getExpiresTime());
SecurityFrameworkUtils.setLoginUser(loginUser, getRequest());
return JSON.parseObject(bodyJson, clazz);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo;
import lombok.Data;
/**
* @author chenbowen
*/
@Data
public class SyncBaseResponseVO {
private String bimRequestId;
private String resultCode;
private String message;
public SyncBaseResponseVO() {}
public SyncBaseResponseVO(String bimRequestId, String resultCode, String message) {
this.bimRequestId = bimRequestId;
this.resultCode = resultCode;
this.message = message;
}
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "对外接口 - 机构创建 请求 VO")
@Data
public class OrgCreateRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "机构 名称", required = true)
private String name;
@Schema(description = "机构 父Id", required = true)
private Long parentId;
@Schema(description = "机构 负责人Id")
private Long leaderUserId;
@Schema(description = "机构 电话")
private String phone;
@Schema(description = "机构 邮件")
private String email;
@Schema(description = "机构 状态", required = true)
private Integer status;
@Schema(description = "机构 是否公司")
private Boolean isCompany;
@Schema(description = "机构 是否集团")
private Boolean isGroup;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 对外接口机构创建响应 VO
*/
@Data
@Schema(description = "对外接口机构创建响应 VO")
@JsonPropertyOrder({"bimRequestId", "uid"})
public class OrgCreateResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "机构ID", required = true)
private String uid;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "对外接口 - 机构删除 请求 VO")
@Data
public class OrgDeleteRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
@Schema(description = "组织 ID", required = true)
private Long bimOrgId;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 对外接口机构删除响应 VO
*/
@Data
@Schema(description = "对外接口机构删除响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "message"})
public class OrgDeleteResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "描述信息", required = true)
private String message;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author chenbowen
*/
@Schema(description = "对外接口 - 根据 ID 查询机构 请求 VO")
@Data
public class OrgGetRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
@Schema(description = "组织 UID", required = true)
private String bimOrgId;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Map;
/**
* 对外接口根据 ID 查询机构 响应 VO
*/
@Data
@Schema(description = "对外接口根据 ID 查询机构 响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "organization", "message"})
public class OrgGetResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "机构信息 Map", required = true)
private Map<String, Object> organization;
@Schema(description = "描述信息", required = true)
private String message;
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "对外接口 - 查询所有机构 ID 请求 VO")
@Data
public class OrgListRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 对外接口查询所有机构 ID 响应 VO
*/
@Data
@Schema(description = "对外接口查询所有机构 ID 响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "orgIdList", "message"})
public class OrgListResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "机构ID 列表", required = true)
private List<String> orgIdList;
@Schema(description = "描述信息", required = true)
private String message;
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author chenbowen
*/
@Schema(description = "对外接口 - 机构更新 请求 VO")
@Data
public class OrgUpdateRequestVO extends DeptDO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
@Schema(description = "请求 ID", required = true)
private Long bimOrgId;
@Schema(description = "机构 名称", required = true)
private String name;
@Schema(description = "机构 父Id", required = true)
private Long parentId;
@Schema(description = "机构 负责人Id")
private Long leaderUserId;
@Schema(description = "机构 电话")
private String phone;
@Schema(description = "机构 邮件")
private String email;
@Schema(description = "机构 状态", required = true)
private Integer status;
@Schema(description = "机构 是否公司")
private Boolean isCompany;
@Schema(description = "机构 是否集团")
private Boolean isGroup;
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.org;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 对外接口机构更新响应 VO
*/
@Data
@Schema(description = "对外接口机构更新响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "message"})
public class OrgUpdateResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "描述信息", required = true)
private String message;
@Schema(description = "是否启用", required = true)
private Boolean __ENABLE__;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 对外接口 Schema 属性 VO
*/
@Data
@Schema(description = "对外接口 Schema 属性 VO")
@JsonPropertyOrder({"name", "type", "required", "multivalued"})
public class SchemaAttributeVO implements Serializable {
@Schema(description = "属性名", required = true)
private String name;
@Schema(description = "属性类型", required = true)
private String type;
@Schema(description = "是否必需", required = true)
private Boolean required;
@Schema(description = "是否多值", required = true)
private Boolean multivalued;
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "对外接口 - Schema 请求 VO")
@Data
public class SchemaRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 对外接口 Schema 响应 VO
*/
@Data
@Schema(description = "对外接口 Schema 响应 VO")
@JsonPropertyOrder({"bimRequestId", "account", "organization"})
public class SchemaResponseVO extends SyncBaseResponseVO implements Serializable {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "账户属性列表", required = true)
private List<SchemaAttributeVO> account;
@Schema(description = "组织属性列表", required = true)
private List<SchemaAttributeVO> organization;
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author chenbowen
*/
@Schema(description = "对外接口账号创建请求 VO")
@Data
public class UserCreateRequestVO extends AdminUserDO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求ID", required = true)
private String bimRequestId;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 对外接口账号创建响应 VO
*/
@Data
@Schema(description = "对外接口账号创建响应 VO")
@JsonPropertyOrder({"bimRequestId", "uid"})
public class UserCreateResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "用户ID", required = true)
private String uid;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "描述信息", required = true)
private String message;
}

View File

@@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "对外接口 - 删除用户 请求 VO")
@Data
public class UserDeleteRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
@Schema(description = "用户 ID", required = true)
private Long bimUid;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 对外接口用户删除响应 VO
*/
@Data
@Schema(description = "对外接口用户删除响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "message"})
public class UserDeleteResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "描述信息", required = true)
private String message;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Schema(description = "对外接口 - 根据 ID 查询用户 请求 VO")
@Data
public class UserGetRequestVO {
@Schema(description = "用户名", required = true)
@NotBlank
private String bimRemoteUser;
@Schema(description = "密码", required = true)
@NotBlank
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
@NotBlank
private String bimRequestId;
@Schema(description = "用户 UID", required = true)
@NotBlank
private String bimUid;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Map;
/**
* 对外接口根据 ID 查询用户 响应 VO
*/
@Data
@Schema(description = "对外接口根据 ID 查询用户 响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "account", "message"})
public class UserGetResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "用户信息 Map", required = true)
private Map<String, Object> account;
@Schema(description = "描述信息", required = true)
private String message;
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "对外接口 - 查询所有用户 ID 请求 VO")
@Data
public class UserListRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 对外接口查询所有用户 ID 响应 VO
*/
@Data
@Schema(description = "对外接口查询所有用户 ID 响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "userIdList", "message"})
public class UserListResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "用户ID 列表", required = true)
private List<String> userIdList;
@Schema(description = "描述信息", required = true)
private String message;
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Set;
@Schema(description = "对外接口 - 更新用户 请求 VO")
@Data
public class UserUpdateRequestVO {
@Schema(description = "用户名", required = true)
private String bimRemoteUser;
@Schema(description = "密码", required = true)
private String bimRemotePwd;
@Schema(description = "请求 ID", required = true)
private String bimRequestId;
@Schema(description = "用户 ID", required = true)
private Long bimUid;
@Schema(description = "用户账号")
private String username;
@Schema(description = "用户密码")
private String password;
@Schema(description = "昵称")
private String nickname;
@Schema(description = "备注")
private String remark;
@Schema(description = "归属部门Id列表")
private Set<Long> deptIds;
@Schema(description = "邮箱")
private String email;
@Schema(description = "移动电话")
private String mobile;
@Schema(description = "性别")
private Integer sex;
@Schema(description = "头像 URL")
private String avatar;
@Schema(description = "账号状态")
private Integer status;
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.system.controller.admin.sync.vo.user;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncBaseResponseVO;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 对外接口用户更新响应 VO
*/
@Data
@Schema(description = "对外接口用户更新响应 VO")
@JsonPropertyOrder({"bimRequestId", "resultCode", "message"})
public class UserUpdateResponseVO extends SyncBaseResponseVO {
@Schema(description = "请求ID", required = true)
private String bimRequestId;
@Schema(description = "结果编码", required = true)
private String resultCode;
@Schema(description = "描述信息", required = true)
private String message;
@Schema(description = "是否启用", required = true)
private Boolean __ENABLE__;
}

View File

@@ -63,6 +63,9 @@ public class UserSaveReqVO {
@DiffLogField(name = "用户性别", function = SexParseFunction.NAME)
private Integer sex;
@Schema(description = "用户状态", example = "1")
private Integer status;
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
@DiffLogField(name = "用户头像")
private String avatar;

View File

@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -31,12 +33,14 @@ public class DeptDO extends TenantBaseDO {
/**
* 部门名称
*/
@NotEmpty
private String name;
/**
* 父部门ID
*
* 关联 {@link #id}
*/
@NotNull
private Long parentId;
/**
* 显示顺序

View File

@@ -6,6 +6,8 @@ import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -34,16 +36,19 @@ public class AdminUserDO extends TenantBaseDO {
/**
* 用户账号
*/
@NotEmpty
private String username;
/**
* 加密后的密码
*
* 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐
*/
@NotEmpty
private String password;
/**
* 用户昵称
*/
@NotEmpty
private String nickname;
/**
* 备注
@@ -53,6 +58,7 @@ public class AdminUserDO extends TenantBaseDO {
* 部门 ID 列表
*/
@TableField(exist = false, typeHandler = JacksonTypeHandler.class )
@NotEmpty
private Set<Long> deptIds;
/**
* 公司 ID 列表
@@ -76,12 +82,14 @@ public class AdminUserDO extends TenantBaseDO {
/**
* 手机号码
*/
@NotEmpty
private String mobile;
/**
* 用户性别
*
* 枚举类 {@link SexEnum}
*/
@NotNull
private Integer sex;
/**
* 用户头像

View File

@@ -52,6 +52,8 @@ public class DeptServiceImpl implements DeptService {
if (createReqVO.getParentId() == null) {
createReqVO.setParentId(DeptDO.PARENT_ID_ROOT);
}
// 创建时默认有效
createReqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 校验父部门的有效性
validateParentDept(null, createReqVO.getParentId());
// 校验部门名的唯一性

View File

@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.org.*;
/**
* 统一机构相关 Service 接口
*/
public interface OrgSyncService {
/**
* 机构创建
*/
OrgCreateResponseVO createOrg(OrgCreateRequestVO requestVO);
/**
* 机构删除
*/
OrgDeleteResponseVO deleteOrg(OrgDeleteRequestVO requestVO);
/**
* 机构更新
*/
OrgUpdateResponseVO updateOrg(OrgUpdateRequestVO requestVO);
/**
* 根据 ID 查询机构
*/
OrgGetResponseVO getOrgById(OrgGetRequestVO requestVO);
/**
* 查询所有机构 ID
*/
OrgListResponseVO listOrgIds(OrgListRequestVO requestVO);
}

View File

@@ -0,0 +1,144 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.org.*;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 统一机构相关 Service 实现
* @author chenbowen
*/
@Service
public class OrgSyncServiceImpl implements OrgSyncService {
@Resource
private DeptService deptService;
@Override
public OrgCreateResponseVO createOrg(OrgCreateRequestVO requestVO) {
DeptSaveReqVO bean = BeanUtils.toBean(requestVO, DeptSaveReqVO.class);
Long deptId = deptService.createDept(bean);
OrgCreateResponseVO resp = new OrgCreateResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
resp.setUid(String.valueOf(deptId));
resp.setResultCode("0");
resp.setMessage("success");
return resp;
}
@Override
public OrgDeleteResponseVO deleteOrg(OrgDeleteRequestVO requestVO) {
Long deptId = requestVO.getBimOrgId();
deptService.deleteDept(deptId);
OrgDeleteResponseVO resp = new OrgDeleteResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
resp.setResultCode("0");
resp.setMessage("success");
return resp;
}
@Override
public OrgUpdateResponseVO updateOrg(OrgUpdateRequestVO requestVO) {
OrgUpdateResponseVO resp = new OrgUpdateResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
DeptSaveReqVO updateReqVO = new DeptSaveReqVO();
Long orgId = requestVO.getBimOrgId() != null ? requestVO.getBimOrgId() : null;
DeptDO originDept = (orgId != null && deptService != null) ? deptService.getDept(orgId) : null;
if (originDept == null) {
resp.setResultCode("500");
resp.setMessage("机构不存在或当前账号无权限修改");
resp.set__ENABLE__(false);
return resp;
}
updateReqVO.setId(orgId);
resp.set__ENABLE__(originDept.getStatus() != null && originDept.getStatus() == 0);
if (requestVO.getName() != null && !requestVO.getName().isEmpty()) {
updateReqVO.setName(requestVO.getName());
}
if (requestVO.getParentId() != null) {
updateReqVO.setParentId(requestVO.getParentId());
}
if (requestVO.getLeaderUserId() != null) {
updateReqVO.setLeaderUserId(requestVO.getLeaderUserId());
}
if (requestVO.getPhone() != null && !requestVO.getPhone().isEmpty()) {
updateReqVO.setPhone(requestVO.getPhone());
}
if (requestVO.getEmail() != null && !requestVO.getEmail().isEmpty()) {
updateReqVO.setEmail(requestVO.getEmail());
}
if (requestVO.getStatus() != null) {
updateReqVO.setStatus(requestVO.getStatus());
}
if (requestVO.getIsCompany() != null) {
updateReqVO.setIsCompany(requestVO.getIsCompany());
}
if (requestVO.getIsGroup() != null) {
updateReqVO.setIsGroup(requestVO.getIsGroup());
}
deptService.updateDept(updateReqVO);
resp.setResultCode("0");
resp.setMessage("success");
return resp;
}
@Override
public OrgGetResponseVO getOrgById(OrgGetRequestVO requestVO) {
OrgGetResponseVO resp = new OrgGetResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
try {
Long deptId = Long.valueOf(requestVO.getBimOrgId());
DeptDO dept = deptService.getDept(deptId);
if (dept != null) {
Map<String, Object> organization = new HashMap<>();
PropertyDescriptor[] pds = org.springframework.beans.BeanUtils.getPropertyDescriptors(DeptDO.class);
for (PropertyDescriptor pd : pds) {
String name = pd.getName();
if ("class".equals(name) || "id".equals(name)) {
continue;
}
try {
Object value = pd.getReadMethod().invoke(dept);
organization.put(name, value);
} catch (Exception ignore) {}
}
organization.put("uid", dept.getId());
resp.setResultCode("0");
resp.setOrganization(organization);
resp.setMessage("success");
} else {
resp.setResultCode("500");
resp.setMessage("机构不存在或当前账号无权限修改");
}
} catch (Exception e) {
resp.setResultCode("500");
resp.setMessage("参数错误");
}
return resp;
}
@Override
public OrgListResponseVO listOrgIds(OrgListRequestVO requestVO) {
List<DeptDO> deptList = deptService.getDeptList(new DeptListReqVO());
List<String> list = new ArrayList<>();
for (DeptDO dept : deptList) {
list.add(String.valueOf(dept.getId()));
}
OrgListResponseVO resp = new OrgListResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
resp.setResultCode("0");
resp.setOrgIdList(list);
resp.setMessage("success");
return resp;
}
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaRequestVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaResponseVO;
/**
* 对外接口 - Schema 服务
*/
public interface SchemaSyncService {
/**
* 获取 Schema 配置
*
* @param requestVO 请求 VO
* @return 响应 VO
*/
SchemaResponseVO getSchema(SchemaRequestVO requestVO);
}

View File

@@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaAttributeVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaRequestVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaResponseVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
public class SchemaSyncServiceImpl implements SchemaSyncService {
// Service method now accepts decrypted RequestVO
@Override
public SchemaResponseVO getSchema(SchemaRequestVO requestVO) {
SchemaResponseVO resp = new SchemaResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
resp.setAccount(buildSchemaAttributes(AdminUserDO.class));
resp.setOrganization(buildSchemaAttributes(DeptDO.class));
return resp;
}
/**
* 使用 ReflectUtil 构建 SchemaAttributeVO 列表
*/
private List<SchemaAttributeVO> buildSchemaAttributes(Class<?> clazz) {
List<SchemaAttributeVO> list = new ArrayList<>();
// 使用 Spring ReflectionUtils 遍历字段
// 遍历类及其父类字段,过滤静态、常量、以及不返回的字段
// 优化排除逻辑:支持批量、可扩展、忽略大小写和前缀
final String[] excludeFields = {"companyDeptInfos", "postIds", "tenantId", "createTime", "updateTime", "creator", "updater", "companyIds", "avatar"};
final String[] excludePrefixes = {"internal", "_"};
ReflectionUtils.doWithFields(clazz, field -> {
field.setAccessible(true);
SchemaAttributeVO vo = new SchemaAttributeVO();
vo.setName(field.getName());
vo.setType(field.getType().getSimpleName());
// 如果字段上标注 @NotNull 或 @NotEmpty 注解,则认为是必需的
boolean required = field.isAnnotationPresent(NotNull.class) || field.isAnnotationPresent(NotEmpty.class);
vo.setRequired(required);
boolean multivalued = Collection.class.isAssignableFrom(field.getType()) || field.getType().isArray();
vo.setMultivalued(multivalued);
list.add(vo);
}, field -> {
int mod = field.getModifiers();
if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
return false;
}
String name = field.getName();
// 排除指定字段(忽略大小写)
for (String exclude : excludeFields) {
if (exclude.equalsIgnoreCase(name)) {
return false;
}
}
// 排除指定前缀字段
for (String prefix : excludePrefixes) {
if (name.startsWith(prefix)) {
return false;
}
}
return true;
});
return list;
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.user.*;
import org.springframework.validation.annotation.Validated;
/**
* 统一账号相关 Service 接口
*/
@Validated
public interface UserSyncService {
/**
* 账号创建
*/
UserCreateResponseVO createUser(UserCreateRequestVO requestVO);
/**
* 账号删除
*/
UserDeleteResponseVO deleteUser(UserDeleteRequestVO requestVO);
/**
* 账号更新
*/
UserUpdateResponseVO updateUser(UserUpdateRequestVO requestVO);
/**
* 根据 ID 查询账号
*/
UserGetResponseVO getUserById(UserGetRequestVO requestVO);
/**
* 查询所有账号 ID
*/
UserListResponseVO listUserIds(UserListRequestVO requestVO);
}

View File

@@ -0,0 +1,159 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.user.*;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.userdept.UserDeptDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.userdept.UserDeptService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.beans.PropertyDescriptor;
import java.util.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
@Service
public class UserSyncServiceImpl implements UserSyncService {
@Resource
private AdminUserService adminUserService;
@Resource
private UserDeptService userDeptService;
@Override
public UserCreateResponseVO createUser(UserCreateRequestVO requestVO) {
TenantContextHolder.setTenantId(Objects.requireNonNull(getLoginUser()).getTenantId());
UserSaveReqVO saveReqVO = BeanUtils.toBean(requestVO, UserSaveReqVO.class);
Long userId = adminUserService.createUser(saveReqVO);
UserCreateResponseVO resp = new UserCreateResponseVO();
resp.setUid(String.valueOf(userId));
resp.setBimRequestId(requestVO.getBimRequestId());
resp.setResultCode("0");
resp.setMessage("success");
return resp;
}
@Override
public UserDeleteResponseVO deleteUser(UserDeleteRequestVO requestVO) {
AdminUserDO user = adminUserService.getUserByUsername(requestVO.getBimRemoteUser());
UserDeleteResponseVO resp = new UserDeleteResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
if (user != null) {
adminUserService.deleteUser(requestVO.getBimUid());
resp.setResultCode("0");
resp.setMessage("success");
} else {
resp.setResultCode("500");
resp.setMessage("用户不存在");
}
return resp;
}
@Override
public UserUpdateResponseVO updateUser(UserUpdateRequestVO requestVO) {
AdminUserDO user = adminUserService.getUser(requestVO.getBimUid());
UserUpdateResponseVO resp = new UserUpdateResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
if (user != null) {
resp.set__ENABLE__(user.getStatus() != null && user.getStatus() == 0);
UserSaveReqVO updateReqVO = new UserSaveReqVO();
updateReqVO.setId(requestVO.getBimUid());
if (requestVO.getUsername() != null && !requestVO.getUsername().isEmpty()) {
updateReqVO.setUsername(requestVO.getUsername());
}
if (requestVO.getNickname() != null && !requestVO.getNickname().isEmpty()) {
updateReqVO.setNickname(requestVO.getNickname());
}
if (requestVO.getRemark() != null && !requestVO.getRemark().isEmpty()) {
updateReqVO.setRemark(requestVO.getRemark());
}
// 部门更新逻辑修复:先删除原有部门关系,再插入新部门关系
if (requestVO.getDeptIds() != null && !requestVO.getDeptIds().isEmpty()) {
// 删除原有部门关系
userDeptService.deleteUserDeptByUserId(user.getId());
// 插入新部门关系
java.util.List<UserDeptDO> deptList = new java.util.ArrayList<>();
for (Long deptId : requestVO.getDeptIds()) {
UserDeptDO deptDO = new UserDeptDO();
deptDO.setUserId(user.getId());
deptDO.setDeptId(deptId);
deptList.add(deptDO);
}
userDeptService.batchCreateUserDept(deptList);
}
if (requestVO.getEmail() != null && !requestVO.getEmail().isEmpty()) {
updateReqVO.setEmail(requestVO.getEmail());
}
if (requestVO.getMobile() != null && !requestVO.getMobile().isEmpty()) {
updateReqVO.setMobile(requestVO.getMobile());
}
if (requestVO.getSex() != null) {
updateReqVO.setSex(requestVO.getSex());
}
if (requestVO.getAvatar() != null && !requestVO.getAvatar().isEmpty()) {
updateReqVO.setAvatar(requestVO.getAvatar());
}
adminUserService.updateUser(updateReqVO);
resp.setResultCode("0");
resp.setMessage("success");
} else {
resp.setResultCode("500");
resp.setMessage("用户不存在");
resp.set__ENABLE__(false);
}
return resp;
}
@Override
public UserGetResponseVO getUserById(UserGetRequestVO requestVO) {
UserGetResponseVO resp = new UserGetResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
try {
Long userId = Long.valueOf(requestVO.getBimUid());
AdminUserDO user = adminUserService.getUser(userId);
if (user != null) {
Map<String, Object> account = new HashMap<>();
PropertyDescriptor[] pds = org.springframework.beans.BeanUtils.getPropertyDescriptors(AdminUserDO.class);
for (PropertyDescriptor pd : pds) {
String name = pd.getName();
if ("class".equals(name) || "id".equals(name)) {
continue;
}
try {
Object value = pd.getReadMethod().invoke(user);
account.put(name, value);
} catch (Exception ignore) {}
}
account.put("uid", user.getId());
resp.setResultCode("0");
resp.setAccount(account);
resp.setMessage("success");
} else {
resp.setResultCode("500");
resp.setMessage("用户不存在");
}
} catch (Exception e) {
resp.setResultCode("500");
resp.setMessage("参数错误");
}
return resp;
}
@Override
public UserListResponseVO listUserIds(UserListRequestVO requestVO) {
List<AdminUserDO> users = adminUserService.getUserListByStatus(0);
List<String> userIdList = new ArrayList<>();
for (AdminUserDO user : users) {
userIdList.add(String.valueOf(user.getId()));
}
UserListResponseVO resp = new UserListResponseVO();
resp.setBimRequestId(requestVO.getBimRequestId());
resp.setResultCode("0");
resp.setUserIdList(userIdList);
resp.setMessage("success");
return resp;
}
}

View File

@@ -153,14 +153,43 @@ public class AdminUserServiceImpl implements AdminUserService {
AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptIds(), updateReqVO.getPostIds());
// 2.1 更新用户
AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class);
// 2.1 更新非空字段
AdminUserDO updateObj = new AdminUserDO();
updateObj.setId(updateReqVO.getId());
if (StrUtil.isNotBlank(updateReqVO.getUsername())) {
updateObj.setUsername(updateReqVO.getUsername());
}
if (StrUtil.isNotBlank(updateReqVO.getNickname())) {
updateObj.setNickname(updateReqVO.getNickname());
}
if (StrUtil.isNotBlank(updateReqVO.getMobile())) {
updateObj.setMobile(updateReqVO.getMobile());
}
if (StrUtil.isNotBlank(updateReqVO.getEmail())) {
updateObj.setEmail(updateReqVO.getEmail());
}
if (updateReqVO.getSex() != null) {
updateObj.setSex(updateReqVO.getSex());
}
if (updateReqVO.getStatus() != null) {
updateObj.setStatus(updateReqVO.getStatus());
}
if (CollectionUtil.isNotEmpty(updateReqVO.getDeptIds())) {
updateObj.setDeptIds(updateReqVO.getDeptIds());
}
if (CollectionUtil.isNotEmpty(updateReqVO.getPostIds())) {
updateObj.setPostIds(updateReqVO.getPostIds());
}
if (StrUtil.isNotBlank(updateReqVO.getRemark())) {
updateObj.setRemark(updateReqVO.getRemark());
}
userMapper.updateById(updateObj);
// 2.2 更新部门
if (CollectionUtil.isNotEmpty(updateObj.getDeptIds())) {
// 删除原有的部门
userDeptService.deleteUserDeptByUserId(updateObj.getId());
userDeptService.deleteUserDeptByUserId(updateObj.getId());
// 插入新的部门
userDeptService.batchCreateUserDept(convertList(updateObj.getDeptIds(),
deptId -> new UserDeptDO().setUserId(updateObj.getId()).setDeptId(deptId)));

View File

@@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.system.util.sync;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.DES;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
/**
* @author chenbowen
*/
public class SyncVerifyUtil {
public static String decrypt(String ciphertext, String key, String type) {
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
if ("AES".equalsIgnoreCase(type)) {
byte[] aesKey = new byte[16];
System.arraycopy(keyBytes, 0, aesKey, 0, Math.min(keyBytes.length, aesKey.length));
AES aes = SecureUtil.aes(aesKey);
return aes.decryptStr(ciphertext);
} else if ("DES".equalsIgnoreCase(type)) {
byte[] desKey = new byte[8];
System.arraycopy(keyBytes, 0, desKey, 0, Math.min(keyBytes.length, desKey.length));
DES des = SecureUtil.des(desKey);
return des.decryptStr(ciphertext);
} else {
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
}
/**
* 对称加密
* @param plaintext 明文内容
* @param key 密钥
* @param type 加密类型,支持 AES、DES
* @return 密文Hex 格式)
*/
public static String encrypt(String plaintext, String key, String type) {
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
if ("AES".equalsIgnoreCase(type)) {
byte[] aesKey = new byte[16];
System.arraycopy(keyBytes, 0, aesKey, 0, Math.min(keyBytes.length, aesKey.length));
AES aes = SecureUtil.aes(aesKey);
return aes.encryptHex(plaintext);
} else if ("DES".equalsIgnoreCase(type)) {
byte[] desKey = new byte[8];
System.arraycopy(keyBytes, 0, desKey, 0, Math.min(keyBytes.length, desKey.length));
DES des = SecureUtil.des(desKey);
return des.encryptHex(plaintext);
} else {
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
}
public static boolean verifySignature(Map<String, Object> reqMap, String type) {
// 排序并拼接参数,忽略 signature 字段
Map<String, Object> sortedMap = new TreeMap<>(reqMap);
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : sortedMap.entrySet()) {
String key = entry.getKey();
if ("signature".equals(key) || entry.getValue() == null) {
continue;
}
sb.append(key).append("=").append(entry.getValue()).append("&");
}
if (!sb.isEmpty()) {
sb.deleteCharAt(sb.length() - 1);
}
// 取出请求中的 signature
String provided = (String) reqMap.get("signature");
if (provided == null) {
return false;
}
// 计算签名
String computed;
if ("MD5".equalsIgnoreCase(type)) {
computed = SecureUtil.md5(sb.toString());
} else if ("SHA256".equalsIgnoreCase(type)) {
computed = SecureUtil.sha256(sb.toString());
} else {
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
return provided.equalsIgnoreCase(computed);
}
}

View File

@@ -189,3 +189,6 @@ yudao:
end-code: 9999 # 这里配置 9999 的原因是,测试方便。
debug: false
sync:
encrypt-key: 25@jygk # 中铝 加密 key

View File

@@ -0,0 +1,209 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.org.*;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class OrgSyncServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private OrgSyncServiceImpl orgSyncService;
@Mock
private DeptService deptService;
@Test
void testCreateOrg() {
// Arrange
OrgCreateRequestVO requestVO = randomPojo(OrgCreateRequestVO.class);
Long newDeptId = randomLongId();
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(newDeptId);
// Act
OrgCreateResponseVO response = orgSyncService.createOrg(requestVO);
// Assert
assertNotNull(response);
assertEquals(requestVO.getBimRequestId(), response.getBimRequestId());
assertEquals(String.valueOf(newDeptId), response.getUid());
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
verify(deptService).createDept(captor.capture());
DeptSaveReqVO capturedVO = captor.getValue();
assertEquals(requestVO.getName(), capturedVO.getName());
assertEquals(requestVO.getParentId(), capturedVO.getParentId());
}
@Test
void testDeleteOrg() {
// Arrange
OrgDeleteRequestVO requestVO = new OrgDeleteRequestVO();
requestVO.setBimOrgId(123L);
requestVO.setBimRequestId("req-456");
// Act
OrgDeleteResponseVO response = orgSyncService.deleteOrg(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertEquals(requestVO.getBimRequestId(), response.getBimRequestId());
verify(deptService).deleteDept(123L);
}
@Test
void testUpdateOrg_Success() {
// Arrange
OrgUpdateRequestVO requestVO = randomPojo(OrgUpdateRequestVO.class);
requestVO.setBimOrgId(randomLongId());
DeptDO existingDept = randomPojo(DeptDO.class);
existingDept.setStatus(0); // Active
when(deptService.getDept(requestVO.getBimOrgId())).thenReturn(existingDept);
// Act
OrgUpdateResponseVO response = orgSyncService.updateOrg(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertTrue(response.get__ENABLE__());
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
verify(deptService).updateDept(captor.capture());
DeptSaveReqVO capturedVO = captor.getValue();
assertEquals(requestVO.getBimOrgId(), capturedVO.getId());
assertEquals(requestVO.getName(), capturedVO.getName());
assertEquals(requestVO.getParentId(), capturedVO.getParentId());
}
@Test
void testUpdateOrg_NotFound() {
// Arrange
OrgUpdateRequestVO requestVO = randomPojo(OrgUpdateRequestVO.class);
requestVO.setBimOrgId(randomLongId());
when(deptService.getDept(requestVO.getBimOrgId())).thenReturn(null);
// Act
OrgUpdateResponseVO response = orgSyncService.updateOrg(requestVO);
// Assert
assertNotNull(response);
assertEquals("500", response.getResultCode());
assertEquals("机构不存在或当前账号无权限修改", response.getMessage());
assertFalse(response.get__ENABLE__());
verify(deptService, never()).updateDept(any());
}
@Test
void testGetOrgById_Success() {
// Arrange
OrgGetRequestVO requestVO = new OrgGetRequestVO();
Long deptId = randomLongId();
requestVO.setBimOrgId(String.valueOf(deptId));
requestVO.setBimRequestId("req-789");
DeptDO dept = randomPojo(DeptDO.class);
dept.setId(deptId);
when(deptService.getDept(deptId)).thenReturn(dept);
// Act
OrgGetResponseVO response = orgSyncService.getOrgById(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertNotNull(response.getOrganization());
assertEquals(dept.getId(), response.getOrganization().get("uid"));
assertEquals(dept.getName(), response.getOrganization().get("name"));
}
@Test
void testGetOrgById_NotFound() {
// Arrange
OrgGetRequestVO requestVO = new OrgGetRequestVO();
Long deptId = randomLongId();
requestVO.setBimOrgId(String.valueOf(deptId));
when(deptService.getDept(deptId)).thenReturn(null);
// Act
OrgGetResponseVO response = orgSyncService.getOrgById(requestVO);
// Assert
assertNotNull(response);
assertEquals("500", response.getResultCode());
assertEquals("机构不存在或当前账号无权限修改", response.getMessage());
assertNull(response.getOrganization());
}
@Test
void testGetOrgById_InvalidId() {
// Arrange
OrgGetRequestVO requestVO = new OrgGetRequestVO();
requestVO.setBimOrgId("invalid-id");
// Act
OrgGetResponseVO response = orgSyncService.getOrgById(requestVO);
// Assert
assertNotNull(response);
assertEquals("500", response.getResultCode());
assertEquals("参数错误", response.getMessage());
assertNull(response.getOrganization());
}
@Test
void testListOrgIds() {
// Arrange
OrgListRequestVO requestVO = randomPojo(OrgListRequestVO.class);
DeptDO dept1 = randomPojo(DeptDO.class);
DeptDO dept2 = randomPojo(DeptDO.class);
List<DeptDO> deptList = List.of(dept1, dept2);
when(deptService.getDeptList(any(DeptListReqVO.class))).thenReturn(deptList);
// Act
OrgListResponseVO response = orgSyncService.listOrgIds(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertEquals(2, response.getOrgIdList().size());
assertTrue(response.getOrgIdList().contains(String.valueOf(dept1.getId())));
assertTrue(response.getOrgIdList().contains(String.valueOf(dept2.getId())));
}
@Test
void testListOrgIds_Empty() {
// Arrange
OrgListRequestVO requestVO = randomPojo(OrgListRequestVO.class);
when(deptService.getDeptList(any(DeptListReqVO.class))).thenReturn(Collections.emptyList());
// Act
OrgListResponseVO response = orgSyncService.listOrgIds(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertTrue(response.getOrgIdList().isEmpty());
}
}

View File

@@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaAttributeVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaRequestVO;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.schema.SchemaResponseVO;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
public class SchemaSyncServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private SchemaSyncServiceImpl schemaSyncService;
@Test
void testGetSchema() {
// Arrange
SchemaRequestVO requestVO = randomPojo(SchemaRequestVO.class);
// Act
SchemaResponseVO response = schemaSyncService.getSchema(requestVO);
// Assert
assertNotNull(response);
assertEquals(requestVO.getBimRequestId(), response.getBimRequestId());
// Validate Account Schema
List<SchemaAttributeVO> accountSchema = response.getAccount();
assertNotNull(accountSchema);
assertFalse(accountSchema.isEmpty());
// Check for a few expected fields in AdminUserDO
assertTrue(accountSchema.stream().anyMatch(a -> "username".equals(a.getName()) && "String".equals(a.getType())));
assertTrue(accountSchema.stream().anyMatch(a -> "nickname".equals(a.getName()) && "String".equals(a.getType())));
assertTrue(accountSchema.stream().anyMatch(a -> "deptIds".equals(a.getName()) && "Set".equals(a.getType()) && a.getMultivalued()));
// Check that excluded fields are not present
assertTrue(accountSchema.stream().noneMatch(a -> "tenantId".equals(a.getName())));
assertTrue(accountSchema.stream().noneMatch(a -> "avatar".equals(a.getName())));
// Validate Organization Schema
List<SchemaAttributeVO> orgSchema = response.getOrganization();
assertNotNull(orgSchema);
assertFalse(orgSchema.isEmpty());
// Check for a few expected fields in DeptDO
assertTrue(orgSchema.stream().anyMatch(a -> "name".equals(a.getName()) && "String".equals(a.getType())));
assertTrue(orgSchema.stream().anyMatch(a -> "parentId".equals(a.getName()) && "Long".equals(a.getType())));
assertTrue(orgSchema.stream().anyMatch(a -> "leaderUserId".equals(a.getName()) && "Long".equals(a.getType())));
// Check that excluded fields are not present
assertTrue(orgSchema.stream().noneMatch(a -> "creator".equals(a.getName())));
assertTrue(orgSchema.stream().noneMatch(a -> "updateTime".equals(a.getName())));
}
}

View File

@@ -0,0 +1,221 @@
package cn.iocoder.yudao.module.system.service.sync;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.sync.vo.user.*;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.userdept.UserDeptService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
public class UserSyncServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private UserSyncServiceImpl userSyncService;
@Mock
private AdminUserService adminUserService;
@Mock
private UserDeptService userDeptService;
private MockedStatic<SecurityFrameworkUtils> securityFrameworkUtilsMock;
@BeforeEach
void setUp() {
securityFrameworkUtilsMock = Mockito.mockStatic(SecurityFrameworkUtils.class);
LoginUser mockLoginUser = new LoginUser();
mockLoginUser.setTenantId(1L);
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(mockLoginUser);
}
@AfterEach
void tearDown() {
securityFrameworkUtilsMock.close();
}
@Test
void testCreateUser() {
// Arrange
UserCreateRequestVO requestVO = randomPojo(UserCreateRequestVO.class);
Long newUserId = randomLongId();
when(adminUserService.createUser(any(UserSaveReqVO.class))).thenReturn(newUserId);
// Act
UserCreateResponseVO response = userSyncService.createUser(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertEquals(String.valueOf(newUserId), response.getUid());
assertEquals(requestVO.getBimRequestId(), response.getBimRequestId());
verify(adminUserService).createUser(any(UserSaveReqVO.class));
}
@Test
void testDeleteUser_Success() {
// Arrange
UserDeleteRequestVO requestVO = randomPojo(UserDeleteRequestVO.class);
AdminUserDO existingUser = randomPojo(AdminUserDO.class);
when(adminUserService.getUserByUsername(requestVO.getBimRemoteUser())).thenReturn(existingUser);
// Act
UserDeleteResponseVO response = userSyncService.deleteUser(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
verify(adminUserService).deleteUser(requestVO.getBimUid());
}
@Test
void testDeleteUser_NotFound() {
// Arrange
UserDeleteRequestVO requestVO = randomPojo(UserDeleteRequestVO.class);
when(adminUserService.getUserByUsername(requestVO.getBimRemoteUser())).thenReturn(null);
// Act
UserDeleteResponseVO response = userSyncService.deleteUser(requestVO);
// Assert
assertNotNull(response);
assertEquals("500", response.getResultCode());
assertEquals("用户不存在", response.getMessage());
verify(adminUserService, never()).deleteUser(any());
}
@Test
void testUpdateUser_Success() {
// Arrange
UserUpdateRequestVO requestVO = randomPojo(UserUpdateRequestVO.class, o -> o.setDeptIds(Set.of(randomLongId(), randomLongId())));
AdminUserDO existingUser = randomPojo(AdminUserDO.class);
existingUser.setStatus(0); // Active
when(adminUserService.getUser(requestVO.getBimUid())).thenReturn(existingUser);
// Act
UserUpdateResponseVO response = userSyncService.updateUser(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertTrue(response.get__ENABLE__());
verify(userDeptService).deleteUserDeptByUserId(existingUser.getId());
verify(userDeptService).batchCreateUserDept(anyList());
verify(adminUserService).updateUser(any(UserSaveReqVO.class));
}
@Test
void testUpdateUser_NotFound() {
// Arrange
UserUpdateRequestVO requestVO = randomPojo(UserUpdateRequestVO.class);
when(adminUserService.getUser(requestVO.getBimUid())).thenReturn(null);
// Act
UserUpdateResponseVO response = userSyncService.updateUser(requestVO);
// Assert
assertNotNull(response);
assertEquals("500", response.getResultCode());
assertEquals("用户不存在", response.getMessage());
assertFalse(response.get__ENABLE__());
verify(adminUserService, never()).updateUser(any());
}
@Test
void testGetUserById_Success() {
// Arrange
UserGetRequestVO requestVO = new UserGetRequestVO();
Long userId = randomLongId();
requestVO.setBimUid(String.valueOf(userId));
AdminUserDO user = randomPojo(AdminUserDO.class);
user.setId(userId);
when(adminUserService.getUser(userId)).thenReturn(user);
// Act
UserGetResponseVO response = userSyncService.getUserById(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertNotNull(response.getAccount());
assertEquals(user.getId(), response.getAccount().get("uid"));
assertEquals(user.getUsername(), response.getAccount().get("username"));
}
@Test
void testGetUserById_NotFound() {
// Arrange
UserGetRequestVO requestVO = new UserGetRequestVO();
Long userId = randomLongId();
requestVO.setBimUid(String.valueOf(userId));
when(adminUserService.getUser(userId)).thenReturn(null);
// Act
UserGetResponseVO response = userSyncService.getUserById(requestVO);
// Assert
assertNotNull(response);
assertEquals("500", response.getResultCode());
assertEquals("用户不存在", response.getMessage());
assertNull(response.getAccount());
}
@Test
void testGetUserById_InvalidId() {
// Arrange
UserGetRequestVO requestVO = new UserGetRequestVO();
requestVO.setBimUid("invalid-id");
// Act
UserGetResponseVO response = userSyncService.getUserById(requestVO);
// Assert
assertNotNull(response);
assertEquals("500", response.getResultCode());
assertEquals("参数错误", response.getMessage());
assertNull(response.getAccount());
}
@Test
void testListUserIds() {
// Arrange
UserListRequestVO requestVO = randomPojo(UserListRequestVO.class);
AdminUserDO user1 = randomPojo(AdminUserDO.class);
AdminUserDO user2 = randomPojo(AdminUserDO.class);
when(adminUserService.getUserListByStatus(0)).thenReturn(List.of(user1, user2));
// Act
UserListResponseVO response = userSyncService.listUserIds(requestVO);
// Assert
assertNotNull(response);
assertEquals("0", response.getResultCode());
assertEquals("success", response.getMessage());
assertEquals(2, response.getUserIdList().size());
assertTrue(response.getUserIdList().contains(String.valueOf(user1.getId())));
assertTrue(response.getUserIdList().contains(String.valueOf(user2.getId())));
}
}

View File

@@ -127,7 +127,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
Long userId = userService.createUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user, "password", "id","deptIds");
assertPojoEquals(reqVO, user, "password", "id","deptIds","status");
assertEquals("yudaoyuanma", user.getPassword());
assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus());
// 断言关联岗位
@@ -181,7 +181,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
userService.updateUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, user, "password","deptIds");
assertPojoEquals(reqVO, user, "avatar","password","deptIds");
// 断言关联岗位
List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId());
assertEquals(2L, userPosts.get(0).getPostId());