diff --git a/pom.xml b/pom.xml index 5d3aaae6..ec3a433d 100644 --- a/pom.xml +++ b/pom.xml @@ -16,9 +16,7 @@ yudao-module-system yudao-module-infra - yudao-module-bpm - yudao-module-report yudao-module-mp diff --git a/sql/dm/e 办同步信息日志表结构.sql b/sql/dm/e 办同步信息日志表结构.sql new file mode 100644 index 00000000..985ac26c --- /dev/null +++ b/sql/dm/e 办同步信息日志表结构.sql @@ -0,0 +1,83 @@ +/* + Yudao Database Transfer Tool + + Source Server Type : MySQL + + Target Server Type : DM8 + + Date: 2025-08-28 14:12:23 +*/ + + +-- ---------------------------- +-- Table structure for system_sync_log +-- ---------------------------- +CREATE TABLE system_sync_log ( + id bigint NOT NULL PRIMARY KEY, + bim_request_id varchar(64) DEFAULT '' NULL, + service_name varchar(100) NOT NULL, + request_method varchar(16) DEFAULT 'POST' NOT NULL, + request_url varchar(500) NOT NULL, + client_ip varchar(50) NOT NULL, + user_agent varchar(512) DEFAULT NULL NULL, + request_time datetime NOT NULL, + response_time datetime DEFAULT NULL NULL, + duration bigint DEFAULT NULL NULL, + encrypted_request text NOT NULL, + decrypted_request text NULL, + status smallint DEFAULT 0 NOT NULL, + error_code varchar(100) DEFAULT NULL NULL, + error_message varchar(1000) DEFAULT NULL NULL, + exception_stack text NULL, + response_data text NULL, + encrypted_response text NULL, + auth_user varchar(100) DEFAULT NULL NULL, + decrypt_status smallint DEFAULT 0 NOT NULL, + signature_verify_status smallint DEFAULT 0 NOT NULL, + auth_status smallint DEFAULT 0 NOT NULL, + business_result varchar(100) DEFAULT NULL NULL, + extra text NULL, + creator varchar(64) DEFAULT '' NULL, + create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + updater varchar(64) DEFAULT '' NULL, + update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + deleted bit DEFAULT '0' NOT NULL +); + +CREATE INDEX idx_system_sync_log_01 ON system_sync_log (bim_request_id); +CREATE INDEX idx_system_sync_log_02 ON system_sync_log (service_name); +CREATE INDEX idx_system_sync_log_03 ON system_sync_log (request_time); +CREATE INDEX idx_system_sync_log_04 ON system_sync_log (status); +CREATE INDEX idx_system_sync_log_05 ON system_sync_log (client_ip); +CREATE INDEX idx_system_sync_log_06 ON system_sync_log (auth_user); + +COMMENT ON COLUMN system_sync_log.id IS '日志主键'; +COMMENT ON COLUMN system_sync_log.bim_request_id IS '外部请求ID'; +COMMENT ON COLUMN system_sync_log.service_name IS '接口名称/服务名称'; +COMMENT ON COLUMN system_sync_log.request_method IS '请求方法'; +COMMENT ON COLUMN system_sync_log.request_url IS '请求URL'; +COMMENT ON COLUMN system_sync_log.client_ip IS '客户端IP地址'; +COMMENT ON COLUMN system_sync_log.user_agent IS '用户代理字符串'; +COMMENT ON COLUMN system_sync_log.request_time IS '请求开始时间'; +COMMENT ON COLUMN system_sync_log.response_time IS '请求结束时间'; +COMMENT ON COLUMN system_sync_log.duration IS '请求处理耗时(毫秒)'; +COMMENT ON COLUMN system_sync_log.encrypted_request IS '原始加密请求体'; +COMMENT ON COLUMN system_sync_log.decrypted_request IS '解密后的请求体'; +COMMENT ON COLUMN system_sync_log.status IS '响应状态 0-成功 1-解密失败 2-签名验证失败 3-认证失败 4-业务处理失败 5-系统异常'; +COMMENT ON COLUMN system_sync_log.error_code IS '错误码'; +COMMENT ON COLUMN system_sync_log.error_message IS '错误信息'; +COMMENT ON COLUMN system_sync_log.exception_stack IS '异常堆栈'; +COMMENT ON COLUMN system_sync_log.response_data IS '响应数据(加密前)'; +COMMENT ON COLUMN system_sync_log.encrypted_response IS '加密后的响应数据'; +COMMENT ON COLUMN system_sync_log.auth_user IS '认证用户'; +COMMENT ON COLUMN system_sync_log.decrypt_status IS '解密状态 0-成功 1-失败'; +COMMENT ON COLUMN system_sync_log.signature_verify_status IS '签名验证状态 0-成功 1-失败'; +COMMENT ON COLUMN system_sync_log.auth_status IS '认证状态 0-成功 1-失败'; +COMMENT ON COLUMN system_sync_log.business_result IS '业务处理结果'; +COMMENT ON COLUMN system_sync_log.extra IS '额外信息'; +COMMENT ON COLUMN system_sync_log.creator IS '创建者'; +COMMENT ON COLUMN system_sync_log.create_time IS '创建时间'; +COMMENT ON COLUMN system_sync_log.updater IS '更新者'; +COMMENT ON COLUMN system_sync_log.update_time IS '更新时间'; +COMMENT ON COLUMN system_sync_log.deleted IS '是否删除'; +COMMENT ON TABLE system_sync_log IS '同步接口调用日志表'; diff --git a/sql/dm/在线文档管理菜单.sql b/sql/dm/在线文档管理菜单.sql new file mode 100644 index 00000000..ec698c8b --- /dev/null +++ b/sql/dm/在线文档管理菜单.sql @@ -0,0 +1,39 @@ +-- 在线文档管理功能菜单数据(DM 格式) +-- 说明:达梦脚本使用与 patch.sql 相同的精简列形式,省略布尔列与时间列,使用默认值。 + +-- 清理旧数据,保持可重复执行 +DELETE FROM system_menu WHERE id IN (6000,6001,6002,600101,600102,600103,600104,600105,600106,600107,600108,600109,600110); + +-- 顶级目录(父级假定已存在 id=2 的“基础设施/Infra”或同级目录) +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) VALUES ( + 6000, '在线文档', '', 1, 15, 2, + 'doc', 'fa:file-text-o', '', 0, NULL +); + +-- 文档管理主页面 +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) VALUES ( + 6001, '文档管理', 'infra:doc:query', 2, 1, 6000, + 'doc-file', 'fa:file-text', 'infra/doc/index', 0, 'DocFile' +); + +-- 按钮权限(操作项) +INSERT INTO system_menu ( + id, name, permission, type, sort, parent_id, + path, icon, component, status +) VALUES + (600101,'文档查询','infra:doc:query',3,1,6001,'','','',0), + (600102,'文档创建','infra:doc:create',3,2,6001,'','','',0), + (600103,'文档更新','infra:doc:update',3,3,6001,'','','',0), + (600104,'文档删除','infra:doc:delete',3,4,6001,'','','',0), + (600105,'文档导出','infra:doc:export',3,5,6001,'','','',0), + (600106,'文档上传','infra:doc:upload',3,6,6001,'','','',0), + (600107,'文档编辑','infra:doc:edit',3,7,6001,'','','',0), + (600108,'文档预览','infra:doc:preview',3,8,6001,'','','',0), + (600109,'文档下载','infra:doc:download',3,9,6001,'','','',0), + (600110,'权限管理','infra:doc:permission',3,10,6001,'','','',0); diff --git a/sql/dm/在线文档管理表结构.sql b/sql/dm/在线文档管理表结构.sql new file mode 100644 index 00000000..5dbde549 --- /dev/null +++ b/sql/dm/在线文档管理表结构.sql @@ -0,0 +1,144 @@ +/* + Yudao Database Transfer Tool + + Source Server Type : MySQL + + Target Server Type : DM8 + + Date: 2025-08-29 10:32:27 +*/ + + +-- ---------------------------- +-- Table structure for infra_doc_file +-- ---------------------------- +CREATE TABLE infra_doc_file ( + id bigint NOT NULL PRIMARY KEY, + title varchar(255) NOT NULL, + file_id bigint DEFAULT NULL NULL, + file_type varchar(10) NOT NULL, + space_type smallint DEFAULT '1' NOT NULL, + description varchar(500) DEFAULT NULL NULL, + latest_version_id bigint DEFAULT NULL NULL, + owner_user_id bigint NOT NULL, + status smallint DEFAULT '1' NOT NULL, + creator varchar(64) DEFAULT '' NULL, + create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + updater varchar(64) DEFAULT '' NULL, + update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + deleted bit DEFAULT '0' NOT NULL, + tenant_id bigint DEFAULT '0' NOT NULL +); + +CREATE INDEX idx_infra_doc_file_01 ON infra_doc_file (owner_user_id); +CREATE INDEX idx_infra_doc_file_02 ON infra_doc_file (space_type); +CREATE INDEX idx_infra_doc_file_03 ON infra_doc_file (file_type); + +COMMENT ON COLUMN infra_doc_file.id IS '文档编号'; +COMMENT ON COLUMN infra_doc_file.title IS '文档标题'; +COMMENT ON COLUMN infra_doc_file.file_id IS '文件编号'; +COMMENT ON COLUMN infra_doc_file.file_type IS '文件类型(docx/xlsx/pptx/pdf)'; +COMMENT ON COLUMN infra_doc_file.space_type IS '空间类型(1-个人空间 2-团队空间)'; +COMMENT ON COLUMN infra_doc_file.description IS '文档描述'; +COMMENT ON COLUMN infra_doc_file.latest_version_id IS '最新版本编号'; +COMMENT ON COLUMN infra_doc_file.owner_user_id IS '所有者用户编号'; +COMMENT ON COLUMN infra_doc_file.status IS '状态(0-禁用 1-启用)'; +COMMENT ON COLUMN infra_doc_file.creator IS '创建者'; +COMMENT ON COLUMN infra_doc_file.create_time IS '创建时间'; +COMMENT ON COLUMN infra_doc_file.updater IS '更新者'; +COMMENT ON COLUMN infra_doc_file.update_time IS '更新时间'; +COMMENT ON COLUMN infra_doc_file.deleted IS '是否删除'; +COMMENT ON COLUMN infra_doc_file.tenant_id IS '租户编号'; +COMMENT ON TABLE infra_doc_file IS '在线文档表'; + +-- ---------------------------- +-- Table structure for infra_doc_file_version +-- ---------------------------- +CREATE TABLE infra_doc_file_version ( + id bigint NOT NULL PRIMARY KEY, + doc_file_id bigint NOT NULL, + version_no varchar(50) NOT NULL, + file_id bigint NOT NULL, + change_description varchar(500) DEFAULT NULL NULL, + creator varchar(64) DEFAULT '' NULL, + create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + updater varchar(64) DEFAULT '' NULL, + update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + deleted bit DEFAULT '0' NOT NULL, + tenant_id bigint DEFAULT '0' NOT NULL +); + +CREATE INDEX idx_infra_doc_file_version_01 ON infra_doc_file_version (doc_file_id); +CREATE INDEX idx_infra_doc_file_version_02 ON infra_doc_file_version (version_no); + +COMMENT ON COLUMN infra_doc_file_version.id IS '版本编号'; +COMMENT ON COLUMN infra_doc_file_version.doc_file_id IS '文档编号'; +COMMENT ON COLUMN infra_doc_file_version.version_no IS '版本号'; +COMMENT ON COLUMN infra_doc_file_version.file_id IS '文件编号'; +COMMENT ON COLUMN infra_doc_file_version.change_description IS '变更说明'; +COMMENT ON COLUMN infra_doc_file_version.creator IS '创建者'; +COMMENT ON COLUMN infra_doc_file_version.create_time IS '创建时间'; +COMMENT ON COLUMN infra_doc_file_version.updater IS '更新者'; +COMMENT ON COLUMN infra_doc_file_version.update_time IS '更新时间'; +COMMENT ON COLUMN infra_doc_file_version.deleted IS '是否删除'; +COMMENT ON COLUMN infra_doc_file_version.tenant_id IS '租户编号'; +COMMENT ON TABLE infra_doc_file_version IS '文档版本表'; + +-- ---------------------------- +-- Table structure for infra_doc_file_permission +-- ---------------------------- +CREATE TABLE infra_doc_file_permission ( + id bigint NOT NULL PRIMARY KEY, + doc_file_id bigint NOT NULL, + role_id bigint NOT NULL, + permission_type smallint DEFAULT '1' NOT NULL, + expire_time datetime DEFAULT NULL NULL, + creator varchar(64) DEFAULT '' NULL, + create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + updater varchar(64) DEFAULT '' NULL, + update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + deleted bit DEFAULT '0' NOT NULL, + tenant_id bigint DEFAULT '0' NOT NULL +); + +CREATE INDEX idx_infra_doc_file_permission_01 ON infra_doc_file_permission (role_id); + +COMMENT ON COLUMN infra_doc_file_permission.id IS '权限编号'; +COMMENT ON COLUMN infra_doc_file_permission.doc_file_id IS '文档编号'; +COMMENT ON COLUMN infra_doc_file_permission.role_id IS '角色编号'; +COMMENT ON COLUMN infra_doc_file_permission.permission_type IS '权限类型(1-只读 2-编辑 3-管理)'; +COMMENT ON COLUMN infra_doc_file_permission.expire_time IS '过期时间'; +COMMENT ON COLUMN infra_doc_file_permission.creator IS '创建者'; +COMMENT ON COLUMN infra_doc_file_permission.create_time IS '创建时间'; +COMMENT ON COLUMN infra_doc_file_permission.updater IS '更新者'; +COMMENT ON COLUMN infra_doc_file_permission.update_time IS '更新时间'; +COMMENT ON COLUMN infra_doc_file_permission.deleted IS '是否删除'; +COMMENT ON COLUMN infra_doc_file_permission.tenant_id IS '租户编号'; +COMMENT ON TABLE infra_doc_file_permission IS '文档权限表'; + +-- ---------------------------- +-- Table structure for infra_doc_edit_history +-- ---------------------------- +CREATE TABLE infra_doc_edit_history ( + id bigint NOT NULL PRIMARY KEY, + doc_file_id bigint NOT NULL, + user_id bigint NOT NULL, + user_name varchar(100) NOT NULL, + edit_type smallint DEFAULT '1' NOT NULL, + description varchar(500) DEFAULT NULL NULL, + create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, + tenant_id bigint DEFAULT '0' NOT NULL +); + +CREATE INDEX idx_infra_doc_edit_history_01 ON infra_doc_edit_history (doc_file_id); +CREATE INDEX idx_infra_doc_edit_history_02 ON infra_doc_edit_history (user_id); + +COMMENT ON COLUMN infra_doc_edit_history.id IS '历史编号'; +COMMENT ON COLUMN infra_doc_edit_history.doc_file_id IS '文档编号'; +COMMENT ON COLUMN infra_doc_edit_history.user_id IS '编辑用户编号'; +COMMENT ON COLUMN infra_doc_edit_history.user_name IS '编辑用户名称'; +COMMENT ON COLUMN infra_doc_edit_history.edit_type IS '编辑类型(1-创建 2-编辑 3-删除 4-重命名)'; +COMMENT ON COLUMN infra_doc_edit_history.description IS '操作描述'; +COMMENT ON COLUMN infra_doc_edit_history.create_time IS '创建时间'; +COMMENT ON COLUMN infra_doc_edit_history.tenant_id IS '租户编号'; +COMMENT ON TABLE infra_doc_edit_history IS '文档编辑历史表'; diff --git a/sql/dm/新增用户、组织机构源表结构并迁移.sql b/sql/dm/新增用户、组织机构源表结构并迁移.sql new file mode 100644 index 00000000..d3dc16fb --- /dev/null +++ b/sql/dm/新增用户、组织机构源表结构并迁移.sql @@ -0,0 +1,42 @@ +-- ================================================ +-- 用户来源类型功能 - 数据库表结构修改脚本 +-- 创建时间: 2025-09-16 +-- 作者: ZT +-- 功能描述: 为 system_users 表添加 user_source 字段,用于区分用户来源 +-- ================================================ + +-- 为 system_users 表添加 user_source 字段 +ALTER TABLE system_users +ADD user_source TINYINT DEFAULT 1; + +-- 添加字段注释 +COMMENT ON COLUMN system_users.user_source IS '用户来源: 1-外部用户, 2-同步用户'; + +-- 数据迁移:将所有现有用户设置为同步用户(类型为2) +UPDATE system_users +SET user_source = 2 +WHERE user_source IS NULL OR user_source = 1; + +COMMIT; + + +-- ================================================ +-- 部门来源类型功能 - 数据库表结构修改脚本 +-- 创建时间: 2025-09-16 +-- 作者: ZT +-- 功能描述: 为 system_dept 表添加 dept_source 字段,用于区分部门来源 +-- ================================================ + +-- 1. 为 system_dept 表添加 dept_source 字段 +ALTER TABLE system_dept +ADD dept_source TINYINT DEFAULT 1; + +-- 2. 添加字段注释 +COMMENT ON COLUMN system_dept.dept_source IS '部门来源: 1-外部部门, 2-同步部门'; + +-- 3. 数据迁移:将所有现有部门设置为同步部门(类型为2) +UPDATE system_dept +SET dept_source = 2 +WHERE dept_source IS NULL OR dept_source = 1; + +COMMIT; \ No newline at end of file diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java index bfce9368..e0ceae16 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import java.util.List; +import java.util.Set; import java.util.function.Consumer; /** @@ -44,6 +45,21 @@ public class BeanUtils { return list; } + public static Set toBean(Set source, Class targetType) { + if (source == null) { + return null; + } + return CollectionUtils.convertSet(source, s -> toBean(s, targetType)); + } + + public static Set toBean(Set source, Class targetType, Consumer peek) { + Set set = toBean(source, targetType); + if (set != null) { + set.forEach(peek); + } + return set; + } + public static PageResult toBean(PageResult source, Class targetType) { return toBean(source, targetType, null); } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java index c894bc73..a75cd26c 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java @@ -20,8 +20,6 @@ import java.lang.reflect.Field; import java.time.LocalDateTime; import java.util.*; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.USER_NOT_SET_DEPT; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser; /** @@ -160,10 +158,11 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { Set postIds = new HashSet<>(JSONUtil.parseArray( loginUser.getInfo().getOrDefault(LoginUser.INFO_KEY_POST_IDS, "[]") ).toList(Long.class)); + // 阻碍部分场景的使用,不强制校验 // 如果 visitCompanyId 不存在,不能进行业务办理 - if (Objects.isNull(visitCompanyId) || Objects.isNull(visitDeptId)) { - throw exception(USER_NOT_SET_DEPT); - } +// if (Objects.isNull(visitCompanyId) || Objects.isNull(visitDeptId)) { +// throw exception(USER_NOT_SET_DEPT); +// } businessBaseDO.setCompanyId(visitCompanyId); businessBaseDO.setCompanyName(loginUser.getVisitCompanyName()); businessBaseDO.setDeptId(visitDeptId); diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java index 199f47fd..4bd73b82 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java @@ -1,8 +1,9 @@ package cn.iocoder.yudao.module.bpm.api.event; -import cn.hutool.core.util.StrUtil; import org.springframework.context.ApplicationListener; +import java.util.List; + /** * {@link BpmProcessInstanceStatusEvent} 的监听器 * @@ -13,15 +14,15 @@ public abstract class BpmProcessInstanceStatusEventListener @Override public final void onApplicationEvent(BpmProcessInstanceStatusEvent event) { - if (!StrUtil.equals(event.getProcessDefinitionKey(), getProcessDefinitionKey())) { - return; + if (getProcessDefinitionKey().contains(event.getProcessDefinitionKey())){ + onEvent(event); } } /** * @return 返回监听的流程定义 Key */ - protected abstract String getProcessDefinitionKey(); + protected abstract List getProcessDefinitionKey(); /** * 处理事件 diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java index 42ac23ee..e73ba3be 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveServiceImpl; import jakarta.annotation.Resource; import org.springframework.stereotype.Component; +import java.util.List; + /** * OA 请假单的结果的监听器实现类 * @@ -19,8 +21,8 @@ public class BpmOALeaveStatusListener extends BpmProcessInstanceStatusEventListe private BpmOALeaveService leaveService; @Override - protected String getProcessDefinitionKey() { - return BpmOALeaveServiceImpl.PROCESS_KEY; + protected List getProcessDefinitionKey() { + return List.of(BpmOALeaveServiceImpl.PROCESS_KEY); } @Override diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java index 2e3671ec..8ec28735 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java @@ -34,9 +34,9 @@ public interface DeptApi { @Operation(summary = "删除部门") CommonResult deleteDept(@RequestParam("id") Long id); - @GetMapping(PREFIX + "/list-all") + @PostMapping(PREFIX + "/list-all") @Operation(summary = "获得部门列表") - CommonResult> getDeptList(@RequestParam DeptListReqDTO reqVO); + CommonResult> getDeptList(@RequestBody DeptListReqDTO reqVO); @GetMapping(PREFIX + "/simple-list") @Operation(summary = "获得部门精简信息列表") @@ -77,4 +77,9 @@ public interface DeptApi { @Parameter(name = "id", description = "部门编号", example = "1024", required = true) CommonResult> getChildDeptList(@RequestParam("id") Long id); + @GetMapping(PREFIX + "/company-dept-info") + @Operation(summary = "获得指定用户的公司部门信息") + @Parameter(name = "userId", description = "用户编号", example = "1", required = true) + CommonResult> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId); + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/CompanyDeptInfoRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/CompanyDeptInfoRespDTO.java new file mode 100644 index 00000000..196cd9fe --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/CompanyDeptInfoRespDTO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.api.dept.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 公司部门信息 Response DTO + * + * @author ZT + */ +@Schema(description = "RPC 服务 - 公司部门信息 Response DTO") +@Data +public class CompanyDeptInfoRespDTO { + + @Schema(description = "公司编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long companyId; + + @Schema(description = "公司名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String companyName; + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long deptId; + + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") + private String deptName; + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java index 0d7888e9..b4d90e30 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java @@ -9,8 +9,8 @@ import cn.iocoder.yudao.module.system.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.cloud.openfeign.SpringQueryMap; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC 服务 - 操作日志") @@ -18,8 +18,8 @@ public interface OperateLogApi extends OperateLogCommonApi { String PREFIX = ApiConstants.PREFIX + "/operate-log"; - @GetMapping(PREFIX + "/page") + @PostMapping(PREFIX + "/page") @Operation(summary = "获取指定模块的指定数据的操作日志分页") - CommonResult> getOperateLogPage(@SpringQueryMap OperateLogPageReqDTO pageReqDTO); + CommonResult> getOperateLogPage(@RequestBody OperateLogPageReqDTO pageReqDTO); } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java index dec970bd..e0e1d2fb 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java @@ -1,20 +1,18 @@ package cn.iocoder.yudao.module.system.api.sms; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; import cn.iocoder.yudao.module.system.enums.ApiConstants; -import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; -import jakarta.validation.Valid; - @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC 服务 - 短信验证码") public interface SmsCodeApi { @@ -29,7 +27,7 @@ public interface SmsCodeApi { @Operation(summary = "验证短信验证码,并进行使用") CommonResult useSmsCode(@Valid @RequestBody SmsCodeUseReqDTO reqDTO); - @GetMapping(PREFIX + "/validate") + @PostMapping(PREFIX + "/validate") @Operation(summary = "检查验证码是否有效") CommonResult validateSmsCode(@Valid @RequestBody SmsCodeValidateReqDTO reqDTO); diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java index 5eeadadb..bc099115 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java @@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.cloud.openfeign.SpringQueryMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -52,9 +51,9 @@ public interface SocialClientApi { CommonResult getWxMaPhoneNumberInfo(@RequestParam("userType") Integer userType, @RequestParam("phoneCode") String phoneCode); - @GetMapping(PREFIX + "/get-wxa-qrcode") + @PostMapping(PREFIX + "/get-wxa-qrcode") @Operation(summary = "获得小程序二维码") - CommonResult getWxaQrcode(@SpringQueryMap SocialWxQrcodeReqDTO reqVO); + CommonResult getWxaQrcode(@RequestBody SocialWxQrcodeReqDTO reqVO); @GetMapping(PREFIX + "/get-wxa-subscribe-template-list") @Operation(summary = "获得微信小程订阅模板") diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java index 29e136c3..0ade83b1 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java @@ -2,18 +2,20 @@ package cn.iocoder.yudao.module.system.api.dept; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.CompanyDeptInfo; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.dept.dto.*; 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.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.service.dept.DeptService; +import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; import java.util.Collection; import java.util.List; +import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -88,4 +90,10 @@ public class DeptApiImpl implements DeptApi { return success(BeanUtils.toBean(depts, DeptRespDTO.class)); } + @Override + public CommonResult> getCompanyDeptInfoListByUserId(Long userId) { + Set companyDeptInfos = deptService.getCompanyDeptInfoListByUserId(userId); + return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class)); + } + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java index 58bca55a..7d764c0e 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java @@ -84,6 +84,23 @@ public class DeptController { return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); } + @GetMapping("/top-level-list") + @Operation(summary = "获取顶级部门列表", description = "用于懒加载,只返回没有父部门的顶级部门") + @PreAuthorize("@ss.hasPermission('system:dept:query')") + public CommonResult> getTopLevelDeptList() { + List list = deptService.getTopLevelDeptList(); + return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); + } + + @GetMapping("/children") + @Operation(summary = "根据父部门ID获取子部门列表", description = "用于懒加载,根据父部门ID返回直接子部门") + @Parameter(name = "parentId", description = "父部门ID", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:dept:query')") + public CommonResult> getChildrenDeptList(@RequestParam("parentId") Long parentId) { + List list = deptService.getDirectChildDeptList(parentId); + return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); + } + @GetMapping("/get") @Operation(summary = "获得部门信息") @Parameter(name = "id", description = "编号", required = true, example = "1024") diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java index 2a21be8a..2bf7c52e 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java @@ -49,4 +49,13 @@ public class SyncLogController { return success(BeanUtils.toBean(pageResult, SyncLogRespVO.class)); } + @PostMapping("/rerun") + @Operation(summary = "重新执行异常的同步接口") + @Parameter(name = "id", description = "日志编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:sync-log:rerun')") + public CommonResult rerunSyncLog(@RequestParam("id") Long id) { + boolean success = syncLogService.rerunSyncLog(id); + return success(success); + } + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java index 8b9d1135..b30459f3 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java @@ -58,6 +58,34 @@ public interface DeptMapper extends BaseMapperX { UpdateWrapper wrapper = new UpdateWrapper<>(); wrapper.in("id", ids).set("tenant_id", tenantId); update(null, wrapper); - }; + } + + /** + * 根据父部门ID和状态查询部门列表 + * + * @param parentId 父部门ID + * @param status 状态 + * @return 部门列表 + */ + default List selectList(Long parentId, Integer status) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(DeptDO::getParentId, parentId) + .eqIfPresent(DeptDO::getStatus, status) + ); + } + + /** + * 根据父部门ID和状态查询子部门列表 + * + * @param parentId 父部门ID + * @param status 状态 + * @return 子部门列表 + */ + default List selectListByParentId(Long parentId, Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(DeptDO::getParentId, parentId) + .eqIfPresent(DeptDO::getStatus, status) + ); + } } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java index fdac4c2a..0641c18b 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java @@ -118,4 +118,21 @@ public interface DeptService { List getUserCompanyList(); Set getCompanyDeptInfoListByUserId(Long userId); + + /** + * 获取顶级部门列表 + * 用于懒加载,只返回没有父部门的顶级部门 + * + * @return 顶级部门列表 + */ + List getTopLevelDeptList(); + + /** + * 根据父部门ID获取直接子部门列表 + * 用于懒加载,只返回指定父部门的直接子部门 + * + * @param parentId 父部门ID + * @return 直接子部门列表 + */ + List getDirectChildDeptList(Long parentId); } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 3b6c9164..78f31449 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.userdept.UserDeptDO; import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; import cn.iocoder.yudao.module.system.dal.mysql.userdept.UserDeptMapper; import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.system.enums.dept.DeptSourceEnum; import com.google.common.annotations.VisibleForTesting; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -30,8 +31,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import cn.iocoder.yudao.module.system.enums.dept.DeptSourceEnum; - /** * 部门 Service 实现类 * @@ -311,4 +310,19 @@ public class DeptServiceImpl implements DeptService { return result; } + @Override + public List getTopLevelDeptList() { + // 获取顶级部门:没有父部门的部门(parentId为null或0) + return deptMapper.selectList(DeptDO.PARENT_ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + } + + @Override + public List getDirectChildDeptList(Long parentId) { + if (parentId == null) { + return Collections.emptyList(); + } + // 根据父部门ID获取直接子部门,只返回启用状态的部门 + return deptMapper.selectListByParentId(parentId, CommonStatusEnum.ENABLE.getStatus()); + } + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java index a0c882c1..900c076e 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java @@ -99,4 +99,12 @@ public interface SyncLogService { String exceptionStack, String responseData, String encryptedResponse, String businessResult); + /** + * 重新执行异常的同步接口 + * + * @param logId 日志ID + * @return 重跑是否成功 + */ + boolean rerunSyncLog(Long logId); + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java index 3ac16602..8f1fd4d1 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java @@ -2,19 +2,33 @@ package cn.iocoder.yudao.module.system.service.sync; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncLogPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sync.vo.org.OrgCreateRequestVO; +import cn.iocoder.yudao.module.system.controller.admin.sync.vo.org.OrgDeleteRequestVO; +import cn.iocoder.yudao.module.system.controller.admin.sync.vo.org.OrgUpdateRequestVO; +import cn.iocoder.yudao.module.system.controller.admin.sync.vo.user.UserCreateRequestVO; +import cn.iocoder.yudao.module.system.controller.admin.sync.vo.user.UserDeleteRequestVO; +import cn.iocoder.yudao.module.system.controller.admin.sync.vo.user.UserUpdateRequestVO; import cn.iocoder.yudao.module.system.dal.dataobject.sync.SyncLogDO; import cn.iocoder.yudao.module.system.dal.mysql.sync.SyncLogMapper; import cn.iocoder.yudao.module.system.enums.sync.SyncLogStatusEnum; +import com.alibaba.fastjson.JSON; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.io.PrintWriter; +import java.io.StringWriter; import java.time.LocalDateTime; /** * 同步接口日志 Service 实现类 * + * 注意:系统现已调整为允许用户名重复,相关影响如下: + * 1. 移除了用户创建和更新时的用户名唯一性校验 + * 2. 用户删除操作改为通过用户ID进行,而非用户名 + * 3. 登录认证仍使用用户名,但可能需要额外的区分机制(如租户ID) + * * @author ZT */ @Service @@ -24,6 +38,10 @@ public class SyncLogServiceImpl implements SyncLogService { @Resource private SyncLogMapper syncLogMapper; + @Resource + private OrgSyncService orgSyncService; + @Resource + private UserSyncService userSyncService; @Override public Long createSyncLog(SyncLogDO syncLog) { @@ -145,4 +163,145 @@ public class SyncLogServiceImpl implements SyncLogService { updateSyncLog(syncLog); } + @Override + public boolean rerunSyncLog(Long logId) { + // 获取原始日志 + SyncLogDO originalLog = getSyncLog(logId); + if (originalLog == null) { + log.warn("同步日志不存在,无法重跑,logId: {}", logId); + return false; + } + + // 检查是否是失败的日志 + if (SyncLogStatusEnum.SUCCESS.getStatus().equals(originalLog.getStatus())) { + log.warn("同步日志状态为成功,无需重跑,logId: {}", logId); + return false; + } + + // 检查是否有解密后的请求数据 + if (originalLog.getDecryptedRequest() == null || originalLog.getDecryptedRequest().trim().isEmpty()) { + log.warn("同步日志缺少解密后的请求数据,无法重跑,logId: {}", logId); + return false; + } + + try { + // 重置相关状态,准备重新执行 + SyncLogDO updateLog = new SyncLogDO(); + updateLog.setId(logId); + updateLog.setResponseTime(LocalDateTime.now()); + updateLog.setErrorCode(null); + updateLog.setErrorMessage(null); + updateLog.setExceptionStack(null); + updateLog.setResponseData(null); + updateLog.setEncryptedResponse(null); + updateLog.setBusinessResult(null); + + // 根据服务名称重新执行业务逻辑 + boolean success = rerunBusinessLogic(originalLog.getServiceName(), + originalLog.getDecryptedRequest(), logId); + + if (success) { + log.info("同步接口重跑成功,logId: {}", logId); + } else { + log.error("同步接口重跑失败,logId: {}", logId); + } + + return success; + } catch (Exception e) { + log.error("重跑同步接口异常,logId: {}", logId, e); + // 记录重跑异常 + logRequestComplete(logId, SyncLogStatusEnum.SYSTEM_ERROR.getStatus(), + "RERUN_ERROR", "重跑过程中发生异常: " + e.getMessage(), getStackTrace(e), null, null, "500"); + return false; + } + } + + /** + * 根据服务名称重新执行业务逻辑 + */ + private boolean rerunBusinessLogic(String serviceName, String decryptedRequest, Long logId) { + try { + switch (serviceName) { + // 只支持写操作(创建、更新、删除)的重跑,查询操作通常是幂等的不需要重跑 + case "OrgCreateService": + OrgCreateRequestVO orgCreateReq = JSON.parseObject(decryptedRequest, OrgCreateRequestVO.class); + var orgCreateResp = orgSyncService.createOrg(orgCreateReq); + logRequestComplete(logId, SyncLogStatusEnum.SUCCESS.getStatus(), + null, null, null, JSON.toJSONString(orgCreateResp), null, orgCreateResp.getResultCode()); + return true; + + case "OrgDeleteService": + OrgDeleteRequestVO orgDeleteReq = JSON.parseObject(decryptedRequest, OrgDeleteRequestVO.class); + var orgDeleteResp = orgSyncService.deleteOrg(orgDeleteReq); + logRequestComplete(logId, SyncLogStatusEnum.SUCCESS.getStatus(), + null, null, null, JSON.toJSONString(orgDeleteResp), null, orgDeleteResp.getResultCode()); + return true; + + case "OrgUpdateService": + OrgUpdateRequestVO orgUpdateReq = JSON.parseObject(decryptedRequest, OrgUpdateRequestVO.class); + var orgUpdateResp = orgSyncService.updateOrg(orgUpdateReq); + logRequestComplete(logId, SyncLogStatusEnum.SUCCESS.getStatus(), + null, null, null, JSON.toJSONString(orgUpdateResp), null, orgUpdateResp.getResultCode()); + return true; + + case "UserCreateService": + UserCreateRequestVO userCreateReq = JSON.parseObject(decryptedRequest, UserCreateRequestVO.class); + var userCreateResp = userSyncService.createUser(userCreateReq); + logRequestComplete(logId, SyncLogStatusEnum.SUCCESS.getStatus(), + null, null, null, JSON.toJSONString(userCreateResp), null, userCreateResp.getResultCode()); + return true; + + case "UserDeleteService": + UserDeleteRequestVO userDeleteReq = JSON.parseObject(decryptedRequest, UserDeleteRequestVO.class); + var userDeleteResp = userSyncService.deleteUser(userDeleteReq); + logRequestComplete(logId, SyncLogStatusEnum.SUCCESS.getStatus(), + null, null, null, JSON.toJSONString(userDeleteResp), null, userDeleteResp.getResultCode()); + return true; + + case "UserUpdateService": + UserUpdateRequestVO userUpdateReq = JSON.parseObject(decryptedRequest, UserUpdateRequestVO.class); + var userUpdateResp = userSyncService.updateUser(userUpdateReq); + logRequestComplete(logId, SyncLogStatusEnum.SUCCESS.getStatus(), + null, null, null, JSON.toJSONString(userUpdateResp), null, userUpdateResp.getResultCode()); + return true; + + // 查询类服务不支持重跑,因为它们通常是幂等的且没有副作用 + case "SchemaService": + case "QueryOrgByIdService": + case "QueryAllOrgIdsService": + case "QueryUserByIdService": + case "QueryAllUserIdsService": + log.warn("查询类服务不支持重跑: {}", serviceName); + logRequestComplete(logId, SyncLogStatusEnum.SYSTEM_ERROR.getStatus(), + "QUERY_SERVICE_NOT_RERUNABLE", "查询类服务不支持重跑", null, null, null, "400"); + return false; + + default: + log.warn("不支持的服务类型重跑: {}", serviceName); + logRequestComplete(logId, SyncLogStatusEnum.SYSTEM_ERROR.getStatus(), + "UNSUPPORTED_SERVICE", "不支持的服务类型重跑", null, null, null, "500"); + return false; + } + } catch (Exception e) { + log.error("重跑业务逻辑异常,serviceName: {}", serviceName, e); + logRequestComplete(logId, SyncLogStatusEnum.SYSTEM_ERROR.getStatus(), + "RERUN_ERROR", e.getMessage(), getStackTrace(e), null, null, "500"); + return false; + } + } + + /** + * 获取异常堆栈信息 + */ + private String getStackTrace(Exception e) { + try { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + return sw.toString(); + } catch (Exception ex) { + return e.getMessage(); + } + } + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/UserSyncServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/UserSyncServiceImpl.java index d0925d30..fa69be23 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/UserSyncServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/UserSyncServiceImpl.java @@ -10,11 +10,11 @@ import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; 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.dal.mysql.dept.UserPostMapper; +import cn.iocoder.yudao.module.system.enums.user.UserSourceEnum; import cn.iocoder.yudao.module.system.service.dept.PostService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.userdept.UserDeptService; import cn.iocoder.yudao.module.system.util.sync.SyncVerifyUtil; -import cn.iocoder.yudao.module.system.enums.user.UserSourceEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -24,6 +24,15 @@ import java.util.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser; +/** + * 用户同步服务实现类 + * + * 注意:系统现已调整为允许用户名重复,相关调整: + * 1. deleteUser 方法改为通过用户ID直接查找和删除用户,而非通过用户名 + * 2. 用户创建时不再校验用户名唯一性 + * + * @author ZT + */ @Service public class UserSyncServiceImpl implements UserSyncService { @Resource @@ -60,7 +69,8 @@ public class UserSyncServiceImpl implements UserSyncService { @Override public UserDeleteResponseVO deleteUser(UserDeleteRequestVO requestVO) { - AdminUserDO user = adminUserService.getUserByUsername(requestVO.getBimRemoteUser()); + // 由于用户名可能重复,直接通过 ID 查找和删除用户 + AdminUserDO user = adminUserService.getUser(requestVO.getBimUid()); UserDeleteResponseVO resp = new UserDeleteResponseVO(); resp.setBimRequestId(requestVO.getBimRequestId()); if (user != null) { diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index 467a3166..86b9ad04 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -23,12 +23,12 @@ 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.dal.mysql.dept.UserPostMapper; import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; +import cn.iocoder.yudao.module.system.enums.user.UserSourceEnum; import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.dept.PostService; import cn.iocoder.yudao.module.system.service.permission.PermissionService; import cn.iocoder.yudao.module.system.service.tenant.TenantService; import cn.iocoder.yudao.module.system.service.userdept.UserDeptService; -import cn.iocoder.yudao.module.system.enums.user.UserSourceEnum; import com.google.common.annotations.VisibleForTesting; import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; @@ -52,6 +52,12 @@ import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; /** * 后台用户 Service 实现类 + * + * 注意:系统现已调整为允许用户名重复,已移除用户名唯一性校验。 + * 这意味着: + * 1. 用户创建和更新时不再检查用户名是否重复 + * 2. 通过用户名查询可能返回多个用户中的第一个 + * 3. 建议在需要精确查找时使用用户ID或其他唯一标识 * * @author ZT */ @@ -416,8 +422,8 @@ public class AdminUserServiceImpl implements AdminUserService { return DataPermissionUtils.executeIgnore(() -> { // 校验用户存在 AdminUserDO user = validateUserExists(id); - // 校验用户名唯一 - validateUsernameUnique(id, username); + // 校验用户名唯一 - 注释掉,允许用户名重复 + // validateUsernameUnique(id, username); // 校验手机号唯一 validateMobileUnique(id, mobile); // 校验邮箱唯一