Merge branch 'refs/heads/zt-test' into test
This commit is contained in:
@@ -2,6 +2,7 @@ package com.zt.plat.module.system.api.dept;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.collection.CollectionUtils;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.api.dept.dto.*;
|
||||
import com.zt.plat.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -15,6 +16,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO ZT:fallbackFactory =
|
||||
@Tag(name = "RPC 服务 - 部门")
|
||||
public interface DeptApi {
|
||||
@@ -86,6 +89,11 @@ public interface DeptApi {
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<Set<CompanyDeptInfoRespDTO>> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
@GetMapping(PREFIX+"/up-find-company-node")
|
||||
@Operation(summary = "获取公司节点信息", description = "通过部门编号,向上追溯部门信息,直到上级部门是公司,返回追溯到的部门信息列表")
|
||||
@Parameter(name = "deptId", description = "部门编号", required = true, example = "1024")
|
||||
CommonResult<List<DeptRespDTO>> upFindCompanyNode(@RequestParam("deptId") Long deptId);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@PostMapping(PREFIX + "/sync")
|
||||
|
||||
@@ -46,18 +46,18 @@ public interface IWorkIntegrationApi {
|
||||
|
||||
@PostMapping(PREFIX + "/hr/subcompany/page")
|
||||
@Operation(summary = "获取 iWork 分部列表")
|
||||
CommonResult<IWorkHrSubcompanyPageRespDTO> listSubcompanies(@RequestBody IWorkOrgPageReqDTO reqDTO);
|
||||
CommonResult<IWorkHrSubcompanyPageRespDTO> listSubcompanies(@RequestBody IWorkSubcompanyQueryReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/hr/department/page")
|
||||
@Operation(summary = "获取 iWork 部门列表")
|
||||
CommonResult<IWorkHrDepartmentPageRespDTO> listDepartments(@RequestBody IWorkOrgPageReqDTO reqDTO);
|
||||
CommonResult<IWorkHrDepartmentPageRespDTO> listDepartments(@RequestBody IWorkDepartmentQueryReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/hr/job-title/page")
|
||||
@Operation(summary = "获取 iWork 岗位列表")
|
||||
CommonResult<IWorkHrJobTitlePageRespDTO> listJobTitles(@RequestBody IWorkOrgPageReqDTO reqDTO);
|
||||
CommonResult<IWorkHrJobTitlePageRespDTO> listJobTitles(@RequestBody IWorkJobTitleQueryReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/hr/user/page")
|
||||
@Operation(summary = "获取 iWork 人员列表")
|
||||
CommonResult<IWorkHrUserPageRespDTO> listUsers(@RequestBody IWorkOrgPageReqDTO reqDTO);
|
||||
CommonResult<IWorkHrUserPageRespDTO> listUsers(@RequestBody IWorkUserQueryReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zt.plat.module.system.api.iwork.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* iWork 部门查询参数。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkDepartmentQueryReqDTO extends IWorkOrgBaseQueryReqDTO {
|
||||
|
||||
@JsonProperty("departmentcode")
|
||||
@Schema(description = "部门编号")
|
||||
private String departmentCode;
|
||||
|
||||
@JsonProperty("departmentname")
|
||||
@Schema(description = "部门名称")
|
||||
private String departmentName;
|
||||
|
||||
@JsonProperty("subcompanyid1")
|
||||
@Schema(description = "分部 ID")
|
||||
private String subcompanyId1;
|
||||
|
||||
@JsonProperty("created")
|
||||
@Schema(description = "创建时间戳(>=)")
|
||||
private String created;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("canceled")
|
||||
@Schema(description = "封存标志,默认查询非封存数据。1:封存")
|
||||
private String canceled;
|
||||
|
||||
@JsonProperty("custom_data")
|
||||
@Schema(description = "自定义字段列表(逗号分隔)")
|
||||
private String customData;
|
||||
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "OA 部门 ID")
|
||||
private String id;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.zt.plat.module.system.api.iwork.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* iWork 岗位查询参数。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkJobTitleQueryReqDTO extends IWorkOrgBaseQueryReqDTO {
|
||||
|
||||
@JsonProperty("jobtitlename")
|
||||
@Schema(description = "岗位名称")
|
||||
private String jobTitleName;
|
||||
|
||||
@JsonProperty("created")
|
||||
@Schema(description = "创建时间戳(>=)")
|
||||
private String created;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "岗位 ID")
|
||||
private String id;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.zt.plat.module.system.api.iwork.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* iWork 人力组织查询基础参数。
|
||||
*/
|
||||
@Data
|
||||
public class IWorkOrgBaseQueryReqDTO {
|
||||
|
||||
@Schema(description = "当前页码", example = "1")
|
||||
private Integer curpage;
|
||||
|
||||
@Schema(description = "每页条数", example = "20")
|
||||
private Integer pagesize;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.zt.plat.module.system.api.iwork.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* iWork 人力组织分页查询通用请求 DTO
|
||||
*/
|
||||
@Data
|
||||
public class IWorkOrgPageReqDTO {
|
||||
|
||||
@Schema(description = "页码", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer pageNo;
|
||||
|
||||
@Schema(description = "每页大小", example = "20", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer pageSize;
|
||||
|
||||
@Schema(description = "关键字过滤")
|
||||
private String keyword;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.zt.plat.module.system.api.iwork.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* iWork 分部查询参数。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkSubcompanyQueryReqDTO extends IWorkOrgBaseQueryReqDTO {
|
||||
|
||||
@JsonProperty("subcompanycode")
|
||||
@Schema(description = "分部编号")
|
||||
private String subcompanyCode;
|
||||
|
||||
@JsonProperty("subcompanyname")
|
||||
@Schema(description = "分部名称")
|
||||
private String subcompanyName;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("canceled")
|
||||
@Schema(description = "封存标志,默认查询非封存数据。1:封存")
|
||||
private String canceled;
|
||||
|
||||
@JsonProperty("custom_data")
|
||||
@Schema(description = "自定义字段列表(逗号分隔)")
|
||||
private String customData;
|
||||
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "OA 分部 ID")
|
||||
private String id;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.zt.plat.module.system.api.iwork.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* iWork 人员查询参数。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkUserQueryReqDTO extends IWorkOrgBaseQueryReqDTO {
|
||||
|
||||
@JsonProperty("workcode")
|
||||
@Schema(description = "人员编号")
|
||||
private String workCode;
|
||||
|
||||
@JsonProperty("subcompanyid1")
|
||||
@Schema(description = "分部 ID")
|
||||
private String subcompanyId1;
|
||||
|
||||
@JsonProperty("departmentid")
|
||||
@Schema(description = "部门 ID")
|
||||
private String departmentId;
|
||||
|
||||
@JsonProperty("jobtitleid")
|
||||
@Schema(description = "岗位 ID")
|
||||
private String jobTitleId;
|
||||
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "人员 ID")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("loginid")
|
||||
@Schema(description = "登录名")
|
||||
private String loginId;
|
||||
|
||||
@JsonProperty("created")
|
||||
@Schema(description = "创建时间戳(>=)")
|
||||
private String created;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("base_custom_data")
|
||||
@Schema(description = "基本信息自定义字段列表(逗号分隔)")
|
||||
private String baseCustomData;
|
||||
|
||||
@JsonProperty("person_custom_data")
|
||||
@Schema(description = "个人信息自定义字段列表(逗号分隔)")
|
||||
private String personCustomData;
|
||||
|
||||
@JsonProperty("work_custom_data")
|
||||
@Schema(description = "工作信息自定义字段列表(逗号分隔)")
|
||||
private String workCustomData;
|
||||
}
|
||||
@@ -36,9 +36,9 @@ public class IWorkWorkflowCreateReqDTO extends IWorkBaseReqDTO {
|
||||
|
||||
@Schema(description = "用印材料附件 URL(必填)")
|
||||
private String xyywjUrl;
|
||||
|
||||
@Schema(description = "用印材料附件文件名(必填)")
|
||||
private String xyywjFileName;
|
||||
|
||||
@Schema(description = "业务回调标识(回调分发使用,≤255 字符)")
|
||||
private String bizCallbackKey;
|
||||
|
||||
@Schema(description = "用印事项")
|
||||
private String yysx;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.zt.plat.module.system.api.push;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* 外部系统推送配置 Feign API
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
@FeignClient(name = ApiConstants.NAME)
|
||||
@Tag(name = "RPC 服务 - 外部系统推送配置")
|
||||
public interface ExternalPushConfigApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/external-push-config";
|
||||
|
||||
/**
|
||||
* 判断是否允许推送到外部系统
|
||||
*
|
||||
* @param companyId 公司编号(可选,为 null 时表示不限制公司)
|
||||
* @param deptId 部门编号(可选,为 null 时只按公司配置判断)
|
||||
* @param businessType 业务类型(可选:PURCHASE/SALE/PRODUCTION,为 null 时表示所有业务类型)
|
||||
* @param externalSystem 外部系统标识(可选:ERP/IWORK/等,为 null 时表示所有外部系统)
|
||||
* @return 是否允许推送(true=允许,false=禁止,默认 true)
|
||||
*/
|
||||
@GetMapping(PREFIX + "/is-push-enabled")
|
||||
@Operation(summary = "判断是否允许推送到外部系统")
|
||||
CommonResult<Boolean> isPushEnabled(
|
||||
@RequestParam(value = "companyId", required = false) @Parameter(description = "公司编号") Long companyId,
|
||||
@RequestParam(value = "deptId", required = false) @Parameter(description = "部门编号") Long deptId,
|
||||
@RequestParam(value = "businessType", required = false) @Parameter(description = "业务类型") String businessType,
|
||||
@RequestParam(value = "externalSystem", required = false) @Parameter(description = "外部系统标识") String externalSystem);
|
||||
}
|
||||
@@ -230,4 +230,11 @@ public interface ErrorCodeConstants {
|
||||
// ========== 门户网站 1-002-033-000 ==========
|
||||
ErrorCode PORTAL_NOT_EXISTS = new ErrorCode(1_002_033_000, "门户不存在");
|
||||
|
||||
// ========== 外部系统推送配置 1_002_034_000 ==========
|
||||
ErrorCode EXTERNAL_PUSH_CONFIG_NOT_EXISTS = new ErrorCode(1_002_034_001, "外部系统推送配置不存在");
|
||||
ErrorCode EXTERNAL_PUSH_CONFIG_EXISTS = new ErrorCode(1_002_034_002, "该配置已存在");
|
||||
ErrorCode EXTERNAL_PUSH_CONFIG_COMPANY_INVALID = new ErrorCode(1_002_034_003, "公司编号必须是公司节点(is_company=1)");
|
||||
ErrorCode EXTERNAL_PUSH_CONFIG_DEPT_INVALID = new ErrorCode(1_002_034_004, "部门编号必须是部门节点(is_company=0)");
|
||||
ErrorCode EXTERNAL_PUSH_CONFIG_BUSINESS_TYPE_INVALID = new ErrorCode(1_002_034_005, "业务类型无效,仅支持 PURCHASE/SALE/PRODUCTION");
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.zt.plat.module.system.enums.permission;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 数据规则条件枚举
|
||||
*
|
||||
* 用于菜单数据规则的条件类型
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataRuleConditionEnum {
|
||||
|
||||
EQ("=", "等于"),
|
||||
NE("!=", "不等于"),
|
||||
GT(">", "大于"),
|
||||
GE(">=", "大于等于"),
|
||||
LT("<", "小于"),
|
||||
LE("<=", "小于等于"),
|
||||
IN("IN", "包含"),
|
||||
NOT_IN("NOT_IN", "不包含"),
|
||||
LIKE("LIKE", "模糊匹配"),
|
||||
LEFT_LIKE("LEFT_LIKE", "左模糊"),
|
||||
RIGHT_LIKE("RIGHT_LIKE", "右模糊"),
|
||||
NOT_LIKE("NOT_LIKE", "不匹配"),
|
||||
IS_NULL("IS_NULL", "为空"),
|
||||
IS_NOT_NULL("IS_NOT_NULL", "不为空"),
|
||||
SQL_RULE("SQL_RULE", "自定义SQL");
|
||||
|
||||
/**
|
||||
* 条件符号
|
||||
*/
|
||||
private final String condition;
|
||||
|
||||
/**
|
||||
* 条件描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 根据条件符号查找枚举
|
||||
*
|
||||
* @param condition 条件符号
|
||||
* @return 枚举值
|
||||
*/
|
||||
public static DataRuleConditionEnum findByCondition(String condition) {
|
||||
if (condition == null) {
|
||||
return null;
|
||||
}
|
||||
for (DataRuleConditionEnum value : values()) {
|
||||
if (value.condition.equals(condition)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.zt.plat.module.system.enums.permission;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 数据规则变量枚举
|
||||
*
|
||||
* 用于菜单数据规则的变量替换
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataRuleVariableEnum {
|
||||
|
||||
USER_ID("#{userId}", "当前用户ID"),
|
||||
USERNAME("#{username}", "当前用户名"),
|
||||
DEPT_ID("#{deptId}", "当前用户部门ID"),
|
||||
DEPT_IDS("#{deptIds}", "当前用户所有部门ID"),
|
||||
ORG_CODE("#{orgCode}", "当前用户组织编码"),
|
||||
TENANT_ID("#{tenantId}", "当前租户ID"),
|
||||
CURRENT_DATE("#{currentDate}", "当前日期"),
|
||||
CURRENT_TIME("#{currentTime}", "当前时间");
|
||||
|
||||
/**
|
||||
* 变量名
|
||||
*/
|
||||
private final String variable;
|
||||
|
||||
/**
|
||||
* 变量描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 根据变量名查找枚举
|
||||
*
|
||||
* @param variable 变量名
|
||||
* @return 枚举值
|
||||
*/
|
||||
public static DataRuleVariableEnum findByVariable(String variable) {
|
||||
if (variable == null) {
|
||||
return null;
|
||||
}
|
||||
for (DataRuleVariableEnum value : values()) {
|
||||
if (value.variable.equals(variable)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.zt.plat.module.system.enums.push;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 业务类型枚举
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BusinessTypeEnum {
|
||||
|
||||
PURCHASE(1, "PURCHASE", "采购"),
|
||||
SALE(2, "SALE", "销售"),
|
||||
PRODUCTION(3, "PRODUCTION", "生产");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 根据编码获取枚举
|
||||
*/
|
||||
public static BusinessTypeEnum valueOfCode(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
for (BusinessTypeEnum value : BusinessTypeEnum.values()) {
|
||||
if (value.getCode().equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型获取枚举
|
||||
*/
|
||||
public static BusinessTypeEnum valueOfType(Integer type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (BusinessTypeEnum value : BusinessTypeEnum.values()) {
|
||||
if (value.getType().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证编码是否有效
|
||||
*/
|
||||
public static boolean isValidCode(String code) {
|
||||
return valueOfCode(code) != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.zt.plat.module.system.mq.iwork;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class IWorkBizCallbackMessage {
|
||||
|
||||
/** 统一回调主题 */
|
||||
public static final String TOPIC = "SYSTEM_IWORK_BIZ_CALLBACK";
|
||||
|
||||
/** requestId 唯一标识 */
|
||||
private String requestId;
|
||||
/** 业务回调标识 */
|
||||
private String bizCallbackKey;
|
||||
/** 回调负载对象(可为 Map) */
|
||||
private Object payload;
|
||||
/** 当前尝试次数,从 0 开始 */
|
||||
private int attempt;
|
||||
/** 最大尝试次数 */
|
||||
private int maxAttempts;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.zt.plat.module.system.mq.iwork;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class IWorkBizCallbackResultMessage {
|
||||
|
||||
/** 统一回调结果主题 */
|
||||
public static final String TOPIC = "SYSTEM_IWORK_BIZ_CALLBACK_RESULT";
|
||||
|
||||
/** requestId 唯一标识 */
|
||||
private String requestId;
|
||||
/** 业务回调标识,对应发送方设置的 tag */
|
||||
private String bizCallbackKey;
|
||||
/** 是否成功 */
|
||||
private boolean success;
|
||||
/** 错误消息 */
|
||||
private String errorMessage;
|
||||
/** 当前尝试次数(业务侧可回传) */
|
||||
private int attempt;
|
||||
/** 最大尝试次数 */
|
||||
private int maxAttempts;
|
||||
/** 回调负载(用于 system 端重试再投递) */
|
||||
private Object payload;
|
||||
}
|
||||
@@ -91,6 +91,10 @@
|
||||
<logger name="com.zt.plat.module.infra.dal.mysql" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
|
||||
<logger name="com.zt.plat.module.system.dal.mysql" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
</springProfile>
|
||||
|
||||
<!-- 其它环境 -->
|
||||
@@ -103,8 +107,4 @@
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<!-- <logger name="com.zt.plat.module.system.dal" level="DEBUG" additivity="false">-->
|
||||
<!-- <appender-ref ref="STDOUT"/>-->
|
||||
<!-- </logger>-->
|
||||
|
||||
</configuration>
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.CompanyDeptInfo;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.framework.datapermission.core.annotation.CompanyDataPermissionIgnore;
|
||||
import com.zt.plat.framework.datapermission.core.annotation.DeptDataPermissionIgnore;
|
||||
import com.zt.plat.module.system.api.dept.dto.*;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||
@@ -78,6 +80,8 @@ public class DeptApiImpl implements DeptApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
@CompanyDataPermissionIgnore
|
||||
@DeptDataPermissionIgnore
|
||||
public CommonResult<DeptRespDTO> getDept(Long id) {
|
||||
DeptDO dept = deptService.getDept(id);
|
||||
return success(BeanUtils.toBean(dept, DeptRespDTO.class));
|
||||
@@ -107,6 +111,12 @@ public class DeptApiImpl implements DeptApi {
|
||||
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<DeptRespDTO>> upFindCompanyNode(Long deptId) {
|
||||
List<DeptDO> depts = deptService.upFindCompanyNode(deptId);
|
||||
return success(BeanUtils.toBean(depts, DeptRespDTO.class));
|
||||
}
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@Override
|
||||
|
||||
@@ -77,7 +77,7 @@ public class IWorkIntegrationApiImpl implements IWorkIntegrationApi {
|
||||
// ----------------- 人力组织分页接口 -----------------
|
||||
|
||||
@Override
|
||||
public CommonResult<IWorkHrSubcompanyPageRespDTO> listSubcompanies(IWorkOrgPageReqDTO reqDTO) {
|
||||
public CommonResult<IWorkHrSubcompanyPageRespDTO> listSubcompanies(IWorkSubcompanyQueryReqDTO reqDTO) {
|
||||
IWorkSubcompanyQueryReqVO reqVO = BeanUtils.toBean(reqDTO, IWorkSubcompanyQueryReqVO.class);
|
||||
IWorkHrSubcompanyPageRespVO respVO = orgRestService.listSubcompanies(reqVO);
|
||||
IWorkHrSubcompanyPageRespDTO respDTO = BeanUtils.toBean(respVO, IWorkHrSubcompanyPageRespDTO.class);
|
||||
@@ -85,7 +85,7 @@ public class IWorkIntegrationApiImpl implements IWorkIntegrationApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<IWorkHrDepartmentPageRespDTO> listDepartments(IWorkOrgPageReqDTO reqDTO) {
|
||||
public CommonResult<IWorkHrDepartmentPageRespDTO> listDepartments(IWorkDepartmentQueryReqDTO reqDTO) {
|
||||
IWorkDepartmentQueryReqVO reqVO = BeanUtils.toBean(reqDTO, IWorkDepartmentQueryReqVO.class);
|
||||
IWorkHrDepartmentPageRespVO respVO = orgRestService.listDepartments(reqVO);
|
||||
IWorkHrDepartmentPageRespDTO respDTO = BeanUtils.toBean(respVO, IWorkHrDepartmentPageRespDTO.class);
|
||||
@@ -93,7 +93,7 @@ public class IWorkIntegrationApiImpl implements IWorkIntegrationApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<IWorkHrJobTitlePageRespDTO> listJobTitles(IWorkOrgPageReqDTO reqDTO) {
|
||||
public CommonResult<IWorkHrJobTitlePageRespDTO> listJobTitles(IWorkJobTitleQueryReqDTO reqDTO) {
|
||||
IWorkJobTitleQueryReqVO reqVO = BeanUtils.toBean(reqDTO, IWorkJobTitleQueryReqVO.class);
|
||||
IWorkHrJobTitlePageRespVO respVO = orgRestService.listJobTitles(reqVO);
|
||||
IWorkHrJobTitlePageRespDTO respDTO = BeanUtils.toBean(respVO, IWorkHrJobTitlePageRespDTO.class);
|
||||
@@ -101,7 +101,7 @@ public class IWorkIntegrationApiImpl implements IWorkIntegrationApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<IWorkHrUserPageRespDTO> listUsers(IWorkOrgPageReqDTO reqDTO) {
|
||||
public CommonResult<IWorkHrUserPageRespDTO> listUsers(IWorkUserQueryReqDTO reqDTO) {
|
||||
IWorkUserQueryReqVO reqVO = BeanUtils.toBean(reqDTO, IWorkUserQueryReqVO.class);
|
||||
IWorkHrUserPageRespVO respVO = orgRestService.listUsers(reqVO);
|
||||
IWorkHrUserPageRespDTO respDTO = BeanUtils.toBean(respVO, IWorkHrUserPageRespDTO.class);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.module.system.api.push;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.service.push.ExternalPushConfigService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 外部系统推送配置 Feign API 实现类
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
public class ExternalPushConfigApiImpl implements ExternalPushConfigApi {
|
||||
|
||||
@Resource
|
||||
private ExternalPushConfigService externalPushConfigService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> isPushEnabled(Long companyId, Long deptId, String businessType, String externalSystem) {
|
||||
Boolean result = externalPushConfigService.isPushEnabled(companyId, deptId, businessType, externalSystem);
|
||||
return success(result);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import com.zt.plat.module.system.enums.social.SocialTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -22,8 +21,8 @@ public class AuthLoginReqVO extends CaptchaVerificationReqVO {
|
||||
|
||||
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ztyuanma")
|
||||
@NotEmpty(message = "登录账号不能为空")
|
||||
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
@Length(min = 1, max = 16, message = "账号长度为 1-16 位")
|
||||
// @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.zt.plat.module.system.controller.admin.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -19,7 +18,7 @@ public class AuthTestLoginReqVO {
|
||||
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ztyuanma")
|
||||
@NotEmpty(message = "登录账号不能为空")
|
||||
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
// @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
|
||||
|
||||
@@ -165,4 +165,11 @@ public class DeptController {
|
||||
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/up-find-company-node")
|
||||
@Operation(summary = "获取公司节点信息", description = "通过部门编号,向上追溯部门信息,直到上级部门是公司,返回追溯到的部门信息列表")
|
||||
@Parameter(name = "deptId", description = "部门编号", required = true, example = "1024")
|
||||
public CommonResult<List<DeptRespVO>> upFindCompanyNode(@RequestParam("deptId") Long deptId) {
|
||||
List<DeptDO> list = deptService.upFindCompanyNode(deptId);
|
||||
return success(BeanUtils.toBean(list, DeptRespVO.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.zt.plat.module.system.controller.admin.integration.iwork;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkCallbackLogService;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
||||
@@ -19,6 +20,8 @@ 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 org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
/**
|
||||
* 提供统一 iWork 流程能力的管理端接口。
|
||||
@@ -34,6 +37,7 @@ public class IWorkIntegrationController {
|
||||
private final IWorkIntegrationService integrationService;
|
||||
private final IWorkOrgRestService orgRestService;
|
||||
private final IWorkSyncService syncService;
|
||||
private final IWorkCallbackLogService callbackLogService;
|
||||
|
||||
@PostMapping("/auth/register")
|
||||
@Operation(summary = "注册 iWork 凭证,获取服务端公钥与 secret")
|
||||
@@ -87,6 +91,39 @@ public class IWorkIntegrationController {
|
||||
return success(integrationService.voidWorkflow(reqVO));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermission('system:iwork:log:query')")
|
||||
@PostMapping("/log/page")
|
||||
@Operation(summary = "iWork 回调日志分页查询")
|
||||
public CommonResult<com.zt.plat.framework.common.pojo.PageResult<IWorkCallbackLogRespVO>> pageLogs(@Valid @RequestBody IWorkCallbackLogPageReqVO reqVO) {
|
||||
com.zt.plat.framework.common.pojo.PageResult<com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO> page = callbackLogService.page(reqVO);
|
||||
java.util.List<IWorkCallbackLogRespVO> mapped = new java.util.ArrayList<>();
|
||||
for (com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO log : page.getList()) {
|
||||
IWorkCallbackLogRespVO vo = new IWorkCallbackLogRespVO();
|
||||
vo.setId(log.getId());
|
||||
vo.setRequestId(log.getRequestId());
|
||||
vo.setBusinessCode(log.getBusinessCode());
|
||||
vo.setBizCallbackKey(log.getBizCallbackKey());
|
||||
vo.setStatus(log.getStatus());
|
||||
vo.setRetryCount(log.getRetryCount());
|
||||
vo.setMaxRetry(log.getMaxRetry());
|
||||
vo.setLastErrorMessage(log.getLastErrorMessage());
|
||||
vo.setRawCallback(log.getRawCallback());
|
||||
vo.setLastCallbackTime(log.getLastCallbackTime());
|
||||
vo.setCreateTime(log.getCreateTime());
|
||||
vo.setUpdateTime(log.getUpdateTime());
|
||||
mapped.add(vo);
|
||||
}
|
||||
return success(new com.zt.plat.framework.common.pojo.PageResult<>(mapped, page.getTotal(), page.getSummary()));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermission('system:iwork:log:retry')")
|
||||
@PostMapping("/log/retry")
|
||||
@Operation(summary = "iWork 回调手工重试")
|
||||
public CommonResult<Boolean> retry(@Valid @RequestBody IWorkWorkflowVoidReqVO reqVO) {
|
||||
callbackLogService.resetAndDispatch(reqVO.getRequestId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ----------------- 人力组织接口 -----------------
|
||||
|
||||
@PostMapping("/hr/subcompany/page")
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - iWork 用印回调日志分页查询")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkCallbackLogPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "requestId")
|
||||
private String requestId;
|
||||
|
||||
@Schema(description = "业务单号")
|
||||
private String businessCode;
|
||||
|
||||
@Schema(description = "业务回调标识")
|
||||
private String bizCallbackKey;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
@Schema(description = "最后回调时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] lastCallbackTime;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "iWork 用印回调日志响应 VO")
|
||||
@Data
|
||||
public class IWorkCallbackLogRespVO {
|
||||
|
||||
private Long id;
|
||||
private String requestId;
|
||||
private String businessCode;
|
||||
private String bizCallbackKey;
|
||||
private Integer status;
|
||||
private Integer retryCount;
|
||||
private Integer maxRetry;
|
||||
private String lastErrorMessage;
|
||||
private String rawCallback;
|
||||
private LocalDateTime lastCallbackTime;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -13,12 +14,35 @@ import lombok.ToString;
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkDepartmentQueryReqVO extends IWorkOrgBaseQueryReqVO {
|
||||
|
||||
@Schema(description = "部门编码")
|
||||
@JsonProperty("departmentcode")
|
||||
@Schema(description = "部门编号")
|
||||
private String departmentCode;
|
||||
|
||||
@JsonProperty("departmentname")
|
||||
@Schema(description = "部门名称")
|
||||
private String departmentName;
|
||||
|
||||
@Schema(description = "所属分部ID")
|
||||
private String subcompanyId;
|
||||
@JsonProperty("subcompanyid1")
|
||||
@Schema(description = "分部 ID")
|
||||
private String subcompanyId1;
|
||||
|
||||
@JsonProperty("created")
|
||||
@Schema(description = "创建时间戳(>=)")
|
||||
private String created;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("canceled")
|
||||
@Schema(description = "封存标志,默认查询非封存数据。1:封存")
|
||||
private String canceled;
|
||||
|
||||
@JsonProperty("custom_data")
|
||||
@Schema(description = "自定义字段列表(逗号分隔)")
|
||||
private String customData;
|
||||
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "OA 部门 ID")
|
||||
private String id;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,21 @@ package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(name = "IWorkFileCallbackReqVO", description = "iWork 文件回调请求 VO")
|
||||
@Data
|
||||
public class IWorkFileCallbackReqVO {
|
||||
|
||||
@Schema(description = "iWork requestId,唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "requestId 不能为空")
|
||||
private String requestId;
|
||||
|
||||
@Schema(description = "业务回调标识 bizCallbackKey,≤255 字符", example = "seal-flow-callback")
|
||||
@Size(max = 255, message = "bizCallbackKey 长度不能超过 255 字符")
|
||||
private String bizCallbackKey;
|
||||
|
||||
@Schema(description = "文件下载 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://example.com/file.pdf")
|
||||
@NotBlank(message = "文件 URL 不能为空")
|
||||
private String fileUrl;
|
||||
@@ -19,6 +28,9 @@ public class IWorkFileCallbackReqVO {
|
||||
@Schema(description = "文件名称,可选", example = "合同附件.pdf")
|
||||
private String fileName;
|
||||
|
||||
@Schema(description = "OA 单点下载使用的 ssoToken,可选", example = "6102A7C13F09DD6B1AF06CDA0E479AC8...")
|
||||
private String ssoToken;
|
||||
@Schema(description = "业务回调状态/结果码,可选")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "原始回调文本(可选,若不传则使用 payload 或请求体序列化)")
|
||||
private String rawBody;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
@@ -42,6 +43,70 @@ public class IWorkFullSyncReqVO {
|
||||
@Schema(description = "是否允许更新已存在的本地实体", example = "false")
|
||||
private Boolean allowUpdate = Boolean.FALSE;
|
||||
|
||||
@JsonProperty("departmentcode")
|
||||
@Schema(description = "部门编号")
|
||||
private String departmentCode;
|
||||
|
||||
@JsonProperty("departmentname")
|
||||
@Schema(description = "部门名称")
|
||||
private String departmentName;
|
||||
|
||||
@JsonProperty("subcompanycode")
|
||||
@Schema(description = "分部编号")
|
||||
private String subcompanyCode;
|
||||
|
||||
@JsonProperty("subcompanyname")
|
||||
@Schema(description = "分部名称")
|
||||
private String subcompanyName;
|
||||
|
||||
@JsonProperty("subcompanyid1")
|
||||
@Schema(description = "分部 ID")
|
||||
private String subcompanyId1;
|
||||
|
||||
@JsonProperty("jobtitleid")
|
||||
@Schema(description = "岗位 ID")
|
||||
private String jobTitleId;
|
||||
|
||||
@JsonProperty("jobtitlename")
|
||||
@Schema(description = "岗位名称")
|
||||
private String jobTitleName;
|
||||
|
||||
@JsonProperty("workcode")
|
||||
@Schema(description = "人员编号")
|
||||
private String workCode;
|
||||
|
||||
@JsonProperty("loginid")
|
||||
@Schema(description = "登录名")
|
||||
private String loginId;
|
||||
|
||||
@JsonProperty("created")
|
||||
@Schema(description = "创建时间戳(>=)")
|
||||
private String created;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("canceled")
|
||||
@Schema(description = "封存标志,默认查询非封存数据。1:封存")
|
||||
private String canceled;
|
||||
|
||||
@JsonProperty("custom_data")
|
||||
@Schema(description = "自定义字段列表(逗号分隔)")
|
||||
private String customData;
|
||||
|
||||
@JsonProperty("base_custom_data")
|
||||
@Schema(description = "基本信息自定义字段列表(逗号分隔)")
|
||||
private String baseCustomData;
|
||||
|
||||
@JsonProperty("person_custom_data")
|
||||
@Schema(description = "个人信息自定义字段列表(逗号分隔)")
|
||||
private String personCustomData;
|
||||
|
||||
@JsonProperty("work_custom_data")
|
||||
@Schema(description = "工作信息自定义字段列表(逗号分隔)")
|
||||
private String workCustomData;
|
||||
|
||||
public Set<IWorkSyncEntityTypeEnum> resolveScopes() {
|
||||
EnumSet<IWorkSyncEntityTypeEnum> defaults = EnumSet.allOf(IWorkSyncEntityTypeEnum.class);
|
||||
if (scopes == null || scopes.isEmpty()) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -13,9 +14,19 @@ import lombok.ToString;
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkJobTitleQueryReqVO extends IWorkOrgBaseQueryReqVO {
|
||||
|
||||
@Schema(description = "岗位编码")
|
||||
private String jobTitleCode;
|
||||
|
||||
@JsonProperty("jobtitlename")
|
||||
@Schema(description = "岗位名称")
|
||||
private String jobTitleName;
|
||||
|
||||
@JsonProperty("created")
|
||||
@Schema(description = "创建时间戳(>=)")
|
||||
private String created;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "岗位 ID")
|
||||
private String id;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,6 @@ public class IWorkOaTokenReqVO {
|
||||
@NotBlank(message = "loginId 不能为空")
|
||||
private String loginId;
|
||||
|
||||
@Schema(description = "应用 appid,未填则使用配置默认值", example = "a17ca6ca-88b0-463e-bffa-7995086bf225")
|
||||
@Schema(description = "应用 appid,已固定使用配置值,无需传递", example = "")
|
||||
private String appId;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* iWork 组织查询基础参数。
|
||||
@@ -16,7 +15,4 @@ public class IWorkOrgBaseQueryReqVO {
|
||||
|
||||
@Schema(description = "每页条数", example = "10")
|
||||
private Integer pagesize;
|
||||
|
||||
@Schema(description = "查询参数(扩展用),将被序列化为 params 传给 iWork")
|
||||
private Map<String, Object> params;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -13,9 +14,27 @@ import lombok.ToString;
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkSubcompanyQueryReqVO extends IWorkOrgBaseQueryReqVO {
|
||||
|
||||
@Schema(description = "分部编码")
|
||||
@JsonProperty("subcompanycode")
|
||||
@Schema(description = "分部编号")
|
||||
private String subcompanyCode;
|
||||
|
||||
@JsonProperty("subcompanyname")
|
||||
@Schema(description = "分部名称")
|
||||
private String subcompanyName;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("canceled")
|
||||
@Schema(description = "封存标志,默认查询非封存数据。1:封存")
|
||||
private String canceled;
|
||||
|
||||
@JsonProperty("custom_data")
|
||||
@Schema(description = "自定义字段列表(逗号分隔)")
|
||||
private String customData;
|
||||
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "OA 分部 ID")
|
||||
private String id;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -13,27 +14,47 @@ import lombok.ToString;
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkUserQueryReqVO extends IWorkOrgBaseQueryReqVO {
|
||||
|
||||
@Schema(description = "人员工号")
|
||||
@JsonProperty("workcode")
|
||||
@Schema(description = "人员编号")
|
||||
private String workCode;
|
||||
|
||||
@Schema(description = "人员姓名")
|
||||
private String lastName;
|
||||
@JsonProperty("subcompanyid1")
|
||||
@Schema(description = "分部 ID")
|
||||
private String subcompanyId1;
|
||||
|
||||
@Schema(description = "所属部门ID")
|
||||
@JsonProperty("departmentid")
|
||||
@Schema(description = "部门 ID")
|
||||
private String departmentId;
|
||||
|
||||
@Schema(description = "所属分部ID")
|
||||
private String subcompanyId;
|
||||
|
||||
@Schema(description = "所属岗位ID")
|
||||
@JsonProperty("jobtitleid")
|
||||
@Schema(description = "岗位 ID")
|
||||
private String jobTitleId;
|
||||
|
||||
@Schema(description = "人员状态 (0:试用, 1:正式, 2:临时, 3:试用延期, 4:解聘, 5:离职, 6:退休, 7:无效)")
|
||||
private String status;
|
||||
@JsonProperty("id")
|
||||
@Schema(description = "人员 ID")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String mobile;
|
||||
@JsonProperty("loginid")
|
||||
@Schema(description = "登录名")
|
||||
private String loginId;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
@JsonProperty("created")
|
||||
@Schema(description = "创建时间戳(>=)")
|
||||
private String created;
|
||||
|
||||
@JsonProperty("modified")
|
||||
@Schema(description = "修改时间戳(>=)")
|
||||
private String modified;
|
||||
|
||||
@JsonProperty("base_custom_data")
|
||||
@Schema(description = "基本信息自定义字段列表(逗号分隔)")
|
||||
private String baseCustomData;
|
||||
|
||||
@JsonProperty("person_custom_data")
|
||||
@Schema(description = "个人信息自定义字段列表(逗号分隔)")
|
||||
private String personCustomData;
|
||||
|
||||
@JsonProperty("work_custom_data")
|
||||
@Schema(description = "工作信息自定义字段列表(逗号分隔)")
|
||||
private String workCustomData;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "iWork 流程回调请求")
|
||||
@Data
|
||||
public class IWorkWorkflowCallbackReqVO {
|
||||
|
||||
@Schema(description = "iWork requestId,唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "requestId 不能为空")
|
||||
private String requestId;
|
||||
|
||||
@Schema(description = "业务单号 (ywxtdjbh)")
|
||||
private String businessCode;
|
||||
|
||||
@Schema(description = "业务回调标识 bizCallbackKey")
|
||||
private String bizCallbackKey;
|
||||
|
||||
@Schema(description = "回调状态/结果码")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "原始回调文本(可截断存储)")
|
||||
private String rawBody;
|
||||
}
|
||||
@@ -34,9 +34,9 @@ public class IWorkWorkflowCreateReqVO extends IWorkBaseReqVO {
|
||||
|
||||
@Schema(description = "用印材料附件 URL(必填)")
|
||||
private String xyywjUrl;
|
||||
|
||||
@Schema(description = "用印材料附件文件名(必填)")
|
||||
private String xyywjFileName;
|
||||
|
||||
@Schema(description = "业务回调标识(回调分发使用,≤255 字符)")
|
||||
private String bizCallbackKey;
|
||||
|
||||
@Schema(description = "用印事项")
|
||||
private String yysx;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.zt.plat.module.system.controller.admin.permission;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleSaveReqVO;
|
||||
import com.zt.plat.module.system.convert.permission.MenuDataRuleConvert;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
|
||||
import com.zt.plat.module.system.service.permission.MenuDataRuleService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 菜单数据规则 Controller
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Tag(name = "管理后台 - 菜单数据规则")
|
||||
@RestController
|
||||
@RequestMapping("/system/menu-data-rule")
|
||||
@Validated
|
||||
public class MenuDataRuleController {
|
||||
|
||||
@Resource
|
||||
private MenuDataRuleService menuDataRuleService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建菜单数据规则")
|
||||
@PreAuthorize("@ss.hasPermission('system:menu:update')")
|
||||
public CommonResult<Long> createMenuDataRule(@Valid @RequestBody MenuDataRuleSaveReqVO createReqVO) {
|
||||
return success(menuDataRuleService.createMenuDataRule(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新菜单数据规则")
|
||||
@PreAuthorize("@ss.hasPermission('system:menu:update')")
|
||||
public CommonResult<Boolean> updateMenuDataRule(@Valid @RequestBody MenuDataRuleSaveReqVO updateReqVO) {
|
||||
menuDataRuleService.updateMenuDataRule(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除菜单数据规则")
|
||||
@Parameter(name = "id", description = "规则ID", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:menu:update')")
|
||||
public CommonResult<Boolean> deleteMenuDataRule(@RequestParam("id") Long id) {
|
||||
menuDataRuleService.deleteMenuDataRule(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得菜单数据规则")
|
||||
@Parameter(name = "id", description = "规则ID", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:menu:query')")
|
||||
public CommonResult<MenuDataRuleRespVO> getMenuDataRule(@RequestParam("id") Long id) {
|
||||
MenuDataRuleDO rule = menuDataRuleService.getMenuDataRule(id);
|
||||
return success(MenuDataRuleConvert.INSTANCE.convert(rule));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得菜单的所有数据规则")
|
||||
@Parameter(name = "menuId", description = "菜单ID", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('system:menu:query')")
|
||||
public CommonResult<List<MenuDataRuleRespVO>> getMenuDataRuleList(@RequestParam("menuId") Long menuId) {
|
||||
List<MenuDataRuleDO> list = menuDataRuleService.getMenuDataRuleListByMenuId(menuId);
|
||||
return success(MenuDataRuleConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,9 @@ public class PermissionController {
|
||||
PermissionAssignRoleMenuItemReqVO reqVO = new PermissionAssignRoleMenuItemReqVO();
|
||||
reqVO.setId(menu.getMenuId());
|
||||
reqVO.setShowMenu(menu.getShowMenu());
|
||||
// 获取该角色在该菜单下的数据规则ID列表
|
||||
Set<Long> dataRuleIds = permissionService.getRoleMenuDataRules(roleId, menu.getMenuId());
|
||||
reqVO.setDataRuleIds(dataRuleIds != null ? new ArrayList<>(dataRuleIds) : null);
|
||||
return reqVO;
|
||||
}).collect(Collectors.toSet());
|
||||
return success(result);
|
||||
@@ -83,6 +86,10 @@ public class PermissionController {
|
||||
|
||||
// 更新菜单的显示状态
|
||||
permissionService.updateMenuDisplay(reqVO.getRoleId(), reqVO.getMenus());
|
||||
|
||||
// 保存菜单数据规则关联
|
||||
permissionService.assignRoleMenuDataRules(reqVO.getRoleId(), reqVO.getMenus());
|
||||
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.zt.plat.module.system.controller.admin.permission.vo.role.RolePageReq
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.role.RoleRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.RoleDO;
|
||||
import com.zt.plat.framework.datapermission.core.menudatapermission.annotation.PermissionData;
|
||||
import com.zt.plat.module.system.service.permission.RoleService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -78,6 +79,7 @@ public class RoleController {
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得角色分页")
|
||||
@PreAuthorize("@ss.hasPermission('system:role:query')")
|
||||
@PermissionData(pageComponent = "system/role/index")
|
||||
public CommonResult<PageResult<RoleRespVO>> getRolePage(RolePageReqVO pageReqVO) {
|
||||
PageResult<RoleDO> pageResult = roleService.getRolePage(pageReqVO);
|
||||
// 获取所有父级角色信息
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.zt.plat.module.system.controller.admin.permission.vo.menudatarule;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 菜单数据规则 Response VO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Schema(description = "管理后台 - 菜单数据规则 Response VO")
|
||||
@Data
|
||||
public class MenuDataRuleRespVO {
|
||||
|
||||
@Schema(description = "规则ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "菜单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long menuId;
|
||||
|
||||
@Schema(description = "规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "仅看本部门数据")
|
||||
private String ruleName;
|
||||
|
||||
@Schema(description = "规则字段", example = "dept_id")
|
||||
private String ruleColumn;
|
||||
|
||||
@Schema(description = "规则条件", requiredMode = Schema.RequiredMode.REQUIRED, example = "=")
|
||||
private String ruleConditions;
|
||||
|
||||
@Schema(description = "规则值", requiredMode = Schema.RequiredMode.REQUIRED, example = "#{deptId}")
|
||||
private String ruleValue;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注", example = "限制只能查看本部门数据")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.zt.plat.module.system.controller.admin.permission.vo.menudatarule;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 菜单数据规则创建/修改 Request VO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Schema(description = "管理后台 - 菜单数据规则创建/修改 Request VO")
|
||||
@Data
|
||||
public class MenuDataRuleSaveReqVO {
|
||||
|
||||
@Schema(description = "规则ID", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "菜单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "菜单ID不能为空")
|
||||
private Long menuId;
|
||||
|
||||
@Schema(description = "规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "仅看本部门数据")
|
||||
@NotBlank(message = "规则名称不能为空")
|
||||
@Size(max = 100, message = "规则名称长度不能超过 100 个字符")
|
||||
private String ruleName;
|
||||
|
||||
@Schema(description = "规则字段", example = "dept_id")
|
||||
@Size(max = 100, message = "规则字段长度不能超过 100 个字符")
|
||||
private String ruleColumn;
|
||||
|
||||
@Schema(description = "规则条件", requiredMode = Schema.RequiredMode.REQUIRED, example = "=")
|
||||
@NotBlank(message = "规则条件不能为空")
|
||||
@Size(max = 20, message = "规则条件长度不能超过 20 个字符")
|
||||
private String ruleConditions;
|
||||
|
||||
@Schema(description = "规则值", requiredMode = Schema.RequiredMode.REQUIRED, example = "#{deptId}")
|
||||
@NotBlank(message = "规则值不能为空")
|
||||
@Size(max = 500, message = "规则值长度不能超过 500 个字符")
|
||||
private String ruleValue;
|
||||
|
||||
@Schema(description = "状态", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "排序", example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注", example = "限制只能查看本部门数据")
|
||||
@Size(max = 500, message = "备注长度不能超过 500 个字符")
|
||||
private String remark;
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Schema(description = "管理后台 - 赋予角色菜单--菜单列表 Request VO")
|
||||
@Data
|
||||
@@ -19,4 +21,7 @@ public class PermissionAssignRoleMenuItemReqVO {
|
||||
@Schema(description = "是否显示菜单按钮是否点击过(避免大量更新数据,只更新点击过的)")
|
||||
private Boolean showMenuChanged = false;
|
||||
|
||||
@Schema(description = "菜单数据规则ID列表", example = "[1, 2, 3]")
|
||||
private List<Long> dataRuleIds;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.framework.excel.core.util.ExcelUtils;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import com.zt.plat.module.infra.api.file.FileApi;
|
||||
import com.zt.plat.module.infra.api.file.dto.FileRespDTO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalSaveReqVO;
|
||||
@@ -13,6 +16,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -20,7 +24,12 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
@@ -39,6 +48,9 @@ public class PortalController {
|
||||
@Resource
|
||||
private PortalService portalService;
|
||||
|
||||
@Resource
|
||||
private FileApi fileApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建门户网站")
|
||||
@PreAuthorize("@ss.hasPermission('system:portal:create')")
|
||||
@@ -97,9 +109,60 @@ public class PortalController {
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取我的门户列表")
|
||||
@PermitAll
|
||||
@TenantIgnore
|
||||
public CommonResult<List<PortalRespVO>> getMyPortalList() {
|
||||
Long userId = getLoginUserId();
|
||||
List<PortalDO> portals = portalService.getPortalListByUserId(userId);
|
||||
Long userId = null;
|
||||
try {
|
||||
userId = getLoginUserId();
|
||||
} catch (Exception ignored) {
|
||||
// 未登录时获取公开门户
|
||||
}
|
||||
List<PortalDO> portals = (userId == null)
|
||||
? portalService.getPublicPortalList()
|
||||
: portalService.getPortalListByUserId(userId);
|
||||
return success(BeanUtils.toBean(portals, PortalRespVO.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 匿名获取公开门户的图标文件信息
|
||||
* 仅允许访问门户中已配置的图标文件
|
||||
*/
|
||||
@GetMapping("/public-icon-files")
|
||||
@Operation(summary = "获取公开门户图标文件信息")
|
||||
@PermitAll
|
||||
@TenantIgnore
|
||||
public CommonResult<List<FileRespDTO>> getPublicPortalIconFiles(@RequestParam("fileIds") List<Long> fileIds) {
|
||||
if (fileIds == null || fileIds.isEmpty()) {
|
||||
return success(java.util.Collections.emptyList());
|
||||
}
|
||||
|
||||
List<PortalDO> portals = portalService.getPublicPortalList();
|
||||
Set<Long> allowedFileIds = new HashSet<>();
|
||||
for (PortalDO portal : portals) {
|
||||
if (portal.getIconType() == null || portal.getIconType() != 2) {
|
||||
continue;
|
||||
}
|
||||
if (!StringUtils.hasText(portal.getIconFileId())) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
allowedFileIds.add(Long.parseLong(portal.getIconFileId()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// ignore invalid fileId
|
||||
}
|
||||
}
|
||||
|
||||
List<FileRespDTO> result = new ArrayList<>();
|
||||
for (Long fileId : fileIds) {
|
||||
if (!allowedFileIds.contains(fileId)) {
|
||||
continue;
|
||||
}
|
||||
CommonResult<FileRespDTO> fileResult = fileApi.getFileInfo(fileId);
|
||||
if (fileResult != null && fileResult.isSuccess() && fileResult.getData() != null) {
|
||||
result.add(fileResult.getData());
|
||||
}
|
||||
}
|
||||
return success(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.zt.plat.module.system.controller.admin.push;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.collection.CollectionUtils;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.BusinessTypeRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO;
|
||||
import com.zt.plat.module.system.enums.push.BusinessTypeEnum;
|
||||
import com.zt.plat.module.system.service.dept.DeptService;
|
||||
import com.zt.plat.module.system.service.push.ExternalPushConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 外部系统推送配置 Controller
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
@Tag(name = "管理后台 - 外部系统推送配置")
|
||||
@RestController
|
||||
@RequestMapping("/system/external-push-config")
|
||||
@Validated
|
||||
public class ExternalPushConfigController {
|
||||
|
||||
@Resource
|
||||
private ExternalPushConfigService externalPushConfigService;
|
||||
@Resource
|
||||
private DeptService deptService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建推送配置")
|
||||
@PreAuthorize("@ss.hasPermission('system:external-push-config:create')")
|
||||
public CommonResult<Long> create(@Valid @RequestBody ExternalPushConfigSaveReqVO createReqVO) {
|
||||
Long id = externalPushConfigService.createExternalPushConfig(createReqVO);
|
||||
return success(id);
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改推送配置")
|
||||
@PreAuthorize("@ss.hasPermission('system:external-push-config:update')")
|
||||
public CommonResult<Boolean> update(@Valid @RequestBody ExternalPushConfigSaveReqVO updateReqVO) {
|
||||
externalPushConfigService.updateExternalPushConfig(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除推送配置")
|
||||
@PreAuthorize("@ss.hasPermission('system:external-push-config:delete')")
|
||||
public CommonResult<Boolean> delete(@RequestParam("id") Long id) {
|
||||
externalPushConfigService.deleteExternalPushConfig(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获取推送配置详情")
|
||||
@PreAuthorize("@ss.hasPermission('system:external-push-config:query')")
|
||||
public CommonResult<ExternalPushConfigRespVO> get(@RequestParam("id") Long id) {
|
||||
ExternalPushConfigDO entity = externalPushConfigService.getExternalPushConfig(id);
|
||||
ExternalPushConfigRespVO respVO = BeanUtils.toBean(entity, ExternalPushConfigRespVO.class);
|
||||
fillDeptInfo(List.of(respVO));
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页查询推送配置")
|
||||
@PreAuthorize("@ss.hasPermission('system:external-push-config:query')")
|
||||
public CommonResult<PageResult<ExternalPushConfigRespVO>> page(@Valid ExternalPushConfigPageReqVO reqVO) {
|
||||
PageResult<ExternalPushConfigDO> pageResult = externalPushConfigService.getExternalPushConfigPage(reqVO);
|
||||
PageResult<ExternalPushConfigRespVO> result = BeanUtils.toBean(pageResult, ExternalPushConfigRespVO.class);
|
||||
fillDeptInfo(result.getList());
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/is-push-enabled")
|
||||
@Operation(summary = "判断是否允许推送")
|
||||
@Parameter(name = "companyId", description = "公司编号(为空表示不限制公司)")
|
||||
@Parameter(name = "deptId", description = "部门编号")
|
||||
@Parameter(name = "businessType", description = "业务类型(为空表示所有业务类型)")
|
||||
@Parameter(name = "externalSystem", description = "外部系统标识(为空表示所有外部系统)")
|
||||
@PreAuthorize("@ss.hasPermission('system:external-push-config:query')")
|
||||
public CommonResult<Boolean> isPushEnabled(
|
||||
@RequestParam(value = "companyId", required = false) Long companyId,
|
||||
@RequestParam(value = "deptId", required = false) Long deptId,
|
||||
@RequestParam(value = "businessType", required = false) String businessType,
|
||||
@RequestParam(value = "externalSystem", required = false) String externalSystem) {
|
||||
Boolean result = externalPushConfigService.isPushEnabled(companyId, deptId, businessType, externalSystem);
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/business-types")
|
||||
@Operation(summary = "获取业务类型列表")
|
||||
public CommonResult<List<BusinessTypeRespVO>> getBusinessTypes() {
|
||||
List<BusinessTypeRespVO> result = Arrays.stream(BusinessTypeEnum.values())
|
||||
.map(e -> new BusinessTypeRespVO(e.getType(), e.getCode(), e.getName()))
|
||||
.collect(Collectors.toList());
|
||||
return success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充公司和部门名称
|
||||
*/
|
||||
private void fillDeptInfo(List<ExternalPushConfigRespVO> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 收集所有公司ID和部门ID
|
||||
Set<Long> deptIds = list.stream()
|
||||
.flatMap(item -> {
|
||||
Set<Long> ids = new java.util.HashSet<>();
|
||||
ids.add(item.getCompanyId());
|
||||
if (item.getDeptId() != null) {
|
||||
ids.add(item.getDeptId());
|
||||
}
|
||||
return ids.stream();
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (deptIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 批量查询部门信息
|
||||
Map<Long, DeptDO> deptMap = CollectionUtils.convertMap(
|
||||
deptService.getDeptList(deptIds), DeptDO::getId);
|
||||
|
||||
// 填充名称
|
||||
list.forEach(item -> {
|
||||
DeptDO company = deptMap.get(item.getCompanyId());
|
||||
if (company != null) {
|
||||
item.setCompanyName(company.getName());
|
||||
}
|
||||
if (item.getDeptId() != null) {
|
||||
DeptDO dept = deptMap.get(item.getDeptId());
|
||||
if (dept != null) {
|
||||
item.setDeptName(dept.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.zt.plat.module.system.controller.admin.push.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "管理后台 - 业务类型 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BusinessTypeRespVO {
|
||||
|
||||
@Schema(description = "类型", example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "编码", example = "PURCHASE")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "名称", example = "采购")
|
||||
private String name;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.zt.plat.module.system.controller.admin.push.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 外部系统推送配置基础信息")
|
||||
@Data
|
||||
public class ExternalPushConfigBaseVO {
|
||||
|
||||
@Schema(description = "公司编号(为空表示不限制公司)", example = "1024")
|
||||
private Long companyId;
|
||||
|
||||
@Schema(description = "部门编号(为空表示公司级配置)", example = "2048")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "业务类型(为空表示所有业务类型)", example = "PURCHASE")
|
||||
@Size(max = 32, message = "业务类型长度不能超过 32 个字符")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description = "外部系统标识(为空表示所有外部系统)", example = "ERP")
|
||||
@Size(max = 64, message = "外部系统标识长度不能超过 64 个字符")
|
||||
private String externalSystem;
|
||||
|
||||
@Schema(description = "是否启用推送", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "推送开关不能为空")
|
||||
private Boolean enablePush;
|
||||
|
||||
@Schema(description = "备注", example = "ERP 采购单推送配置")
|
||||
@Size(max = 512, message = "备注长度不能超过 512 个字符")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zt.plat.module.system.controller.admin.push.vo;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 外部系统推送配置分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ExternalPushConfigPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "公司编号", example = "1024")
|
||||
private Long companyId;
|
||||
|
||||
@Schema(description = "部门编号", example = "2048")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "业务类型", example = "PURCHASE")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description = "外部系统标识", example = "ERP")
|
||||
private String externalSystem;
|
||||
|
||||
@Schema(description = "是否启用推送", example = "true")
|
||||
private Boolean enablePush;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.module.system.controller.admin.push.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 外部系统推送配置 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ExternalPushConfigRespVO extends ExternalPushConfigBaseVO {
|
||||
|
||||
@Schema(description = "配置编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "公司名称", example = "浙江中天建设集团")
|
||||
private String companyName;
|
||||
|
||||
@Schema(description = "部门名称", example = "采购部")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "最后更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.zt.plat.module.system.controller.admin.push.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 外部系统推送配置创建/修改 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ExternalPushConfigSaveReqVO extends ExternalPushConfigBaseVO {
|
||||
|
||||
@Schema(description = "配置编号", example = "1024")
|
||||
private Long id;
|
||||
}
|
||||
@@ -10,7 +10,10 @@ import com.zt.plat.module.system.framework.operatelog.core.DeptParseFunction;
|
||||
import com.zt.plat.module.system.framework.operatelog.core.PostParseFunction;
|
||||
import com.zt.plat.module.system.framework.operatelog.core.SexParseFunction;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@@ -28,7 +31,7 @@ public class UserSaveReqVO {
|
||||
|
||||
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "zt")
|
||||
@NotBlank(message = "用户账号不能为空")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9]+$", message = "用户账号由 数字、字母 组成")
|
||||
// @Pattern(regexp = "^[a-zA-Z0-9]+$", message = "用户账号由 数字、字母 组成")
|
||||
@Size(min = 1, max = 30, message = "用户账号长度为 1-30 个字符")
|
||||
@DiffLogField(name = "用户账号")
|
||||
private String username;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.zt.plat.module.system.controller.app.portal;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalRespVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
|
||||
import com.zt.plat.module.system.service.portal.PortalService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
* 用户端 - 门户网站 Controller
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Tag(name = "用户端 - 门户网站")
|
||||
@RestController
|
||||
@RequestMapping("/system/portal")
|
||||
@Validated
|
||||
public class AppPortalController {
|
||||
|
||||
@Resource
|
||||
private PortalService portalService;
|
||||
|
||||
/**
|
||||
* 获取当前用户可访问的门户列表
|
||||
* 此接口无需权限验证,因为已经通过登录验证,
|
||||
* 返回的门户列表已经根据用户权限进行了过滤
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取我的门户列表")
|
||||
public CommonResult<List<PortalRespVO>> getMyPortalList() {
|
||||
Long userId = getLoginUserId();
|
||||
List<PortalDO> portals = portalService.getPortalListByUserId(userId);
|
||||
return success(BeanUtils.toBean(portals, PortalRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.zt.plat.module.system.convert.permission;
|
||||
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单数据规则 Convert
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface MenuDataRuleConvert {
|
||||
|
||||
MenuDataRuleConvert INSTANCE = Mappers.getMapper(MenuDataRuleConvert.class);
|
||||
|
||||
MenuDataRuleDO convert(MenuDataRuleSaveReqVO bean);
|
||||
|
||||
MenuDataRuleRespVO convert(MenuDataRuleDO bean);
|
||||
|
||||
List<MenuDataRuleRespVO> convertList(List<MenuDataRuleDO> list);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.iwork;
|
||||
|
||||
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 com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* iWork 用印流程回调日志。
|
||||
*/
|
||||
@TableName("system_iwork_seal_log")
|
||||
@KeySequence("system_iwork_seal_log_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class IWorkSealLogDO extends BaseDO {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* iWork 返回的请求编号,唯一业务标识。
|
||||
*/
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 业务单号(ywxtdjbh)。
|
||||
*/
|
||||
private String businessCode;
|
||||
|
||||
/**
|
||||
* 业务回调标识。
|
||||
*/
|
||||
private String bizCallbackKey;
|
||||
|
||||
/**
|
||||
* 状态枚举,参考 IWorkCallbackStatusEnum。
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 已执行的自动/手工重试次数。
|
||||
*/
|
||||
private Integer retryCount;
|
||||
|
||||
/**
|
||||
* 最大重试次数(快照)。
|
||||
*/
|
||||
private Integer maxRetry;
|
||||
|
||||
/**
|
||||
* 最后一次错误信息。
|
||||
*/
|
||||
private String lastErrorMessage;
|
||||
|
||||
/**
|
||||
* 回调原始负载(截断)。
|
||||
*/
|
||||
private String rawCallback;
|
||||
|
||||
/**
|
||||
* 最近一次回调时间。
|
||||
*/
|
||||
private LocalDateTime lastCallbackTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.permission;
|
||||
|
||||
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
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 lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 菜单数据规则 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("system_menu_data_rule")
|
||||
@KeySequence("system_menu_data_rule_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MenuDataRuleDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 规则ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
private Long menuId;
|
||||
|
||||
/**
|
||||
* 规则名称
|
||||
*/
|
||||
private String ruleName;
|
||||
|
||||
/**
|
||||
* 规则字段(数据库列名)
|
||||
*/
|
||||
private String ruleColumn;
|
||||
|
||||
/**
|
||||
* 规则条件(=、>、<、IN、LIKE等)
|
||||
*/
|
||||
private String ruleConditions;
|
||||
|
||||
/**
|
||||
* 规则值(支持变量如#{userId}、#{deptId})
|
||||
*/
|
||||
private String ruleValue;
|
||||
|
||||
/**
|
||||
* 状态(0=禁用 1=启用)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.permission;
|
||||
|
||||
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
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 lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 角色菜单数据规则关联 DO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@TableName("system_role_menu_data_rule")
|
||||
@KeySequence("system_role_menu_data_rule_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RoleMenuDataRuleDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 自增主键
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
private Long menuId;
|
||||
|
||||
/**
|
||||
* 数据规则ID
|
||||
*/
|
||||
private Long dataRuleId;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.push;
|
||||
|
||||
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 com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 外部系统推送配置 DO
|
||||
*
|
||||
* 用于配置不同公司/部门/业务类型下的外部系统推送开关
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
@TableName("system_external_push_config")
|
||||
@KeySequence("system_external_push_config_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ExternalPushConfigDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 公司编号(可为空)
|
||||
*
|
||||
* 关联 system_dept 表,is_company = 1
|
||||
* 为空表示不限制公司
|
||||
*/
|
||||
private Long companyId;
|
||||
|
||||
/**
|
||||
* 部门编号(可为空)
|
||||
*
|
||||
* 关联 system_dept 表,is_company = 0
|
||||
* 为空表示公司级配置
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 业务类型(可为空)
|
||||
*
|
||||
* 枚举值:PURCHASE, SALE, PRODUCTION
|
||||
* 为空表示所有业务类型
|
||||
* 枚举 {@link com.zt.plat.module.system.enums.push.BusinessTypeEnum}
|
||||
*/
|
||||
private String businessType;
|
||||
|
||||
/**
|
||||
* 外部系统标识(可为空)
|
||||
*
|
||||
* 如:ERP, IWORK
|
||||
* 为空表示所有外部系统
|
||||
* 枚举 {@link com.zt.plat.module.system.enums.dept.ExternalPlatformEnum}
|
||||
*/
|
||||
private String externalSystem;
|
||||
|
||||
/**
|
||||
* 是否启用推送
|
||||
*
|
||||
* true:启用推送
|
||||
* false:停用推送
|
||||
*/
|
||||
private Boolean enablePush;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -167,4 +169,9 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
|
||||
);
|
||||
}
|
||||
|
||||
@Select("""
|
||||
SELECT sd.* FROM SYSTEM_DEPT sd START WITH sd.id = #{deptId}
|
||||
CONNECT BY PRIOR sd.parent_id = sd.id AND PRIOR sd.is_company <> 1 ;
|
||||
""")
|
||||
List<DeptDO> upFindCompanyNode(@Param("deptId") Long deptId);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
|
||||
@Mapper
|
||||
public interface PostMapper extends BaseMapperX<PostDO> {
|
||||
@@ -35,4 +36,12 @@ public interface PostMapper extends BaseMapperX<PostDO> {
|
||||
return selectOne(PostDO::getCode, code);
|
||||
}
|
||||
|
||||
default List<PostDO> selectByCodes(Collection<String> codes) {
|
||||
if (codes == null || codes.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return selectList(new LambdaQueryWrapperX<PostDO>()
|
||||
.in(PostDO::getCode, codes));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zt.plat.module.system.dal.mysql.iwork;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface IWorkSealLogMapper extends BaseMapperX<IWorkSealLogDO> {
|
||||
|
||||
default IWorkSealLogDO selectByRequestId(String requestId) {
|
||||
return selectOne(IWorkSealLogDO::getRequestId, requestId);
|
||||
}
|
||||
|
||||
default PageResult<IWorkSealLogDO> selectPage(IWorkCallbackLogPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<IWorkSealLogDO>()
|
||||
.eqIfPresent(IWorkSealLogDO::getRequestId, reqVO.getRequestId())
|
||||
.eqIfPresent(IWorkSealLogDO::getBusinessCode, reqVO.getBusinessCode())
|
||||
.eqIfPresent(IWorkSealLogDO::getBizCallbackKey, reqVO.getBizCallbackKey())
|
||||
.eqIfPresent(IWorkSealLogDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(IWorkSealLogDO::getCreateTime, reqVO.getCreateTime())
|
||||
.betweenIfPresent(IWorkSealLogDO::getLastCallbackTime, reqVO.getLastCallbackTime())
|
||||
.orderByDesc(IWorkSealLogDO::getId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.zt.plat.module.system.dal.mysql.permission;
|
||||
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单数据规则 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface MenuDataRuleMapper extends BaseMapperX<MenuDataRuleDO> {
|
||||
|
||||
/**
|
||||
* 根据菜单ID查询规则列表
|
||||
*
|
||||
* @param menuId 菜单ID
|
||||
* @return 规则列表
|
||||
*/
|
||||
default List<MenuDataRuleDO> selectListByMenuId(Long menuId) {
|
||||
return selectList(MenuDataRuleDO::getMenuId, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色和菜单查询规则ID列表
|
||||
*
|
||||
* @param roleIds 角色ID集合
|
||||
* @param menuId 菜单ID
|
||||
* @return 规则ID列表
|
||||
*/
|
||||
@Select("<script>" +
|
||||
"SELECT DISTINCT rmdr.data_rule_id " +
|
||||
"FROM system_role_menu_data_rule rmdr " +
|
||||
"WHERE rmdr.role_id IN " +
|
||||
"<foreach collection='roleIds' item='roleId' open='(' separator=',' close=')'>" +
|
||||
"#{roleId}" +
|
||||
"</foreach>" +
|
||||
"AND rmdr.menu_id = #{menuId} " +
|
||||
"AND rmdr.deleted = 0" +
|
||||
"</script>")
|
||||
List<Long> selectRuleIdsByRoleAndMenu(@Param("roleIds") Collection<Long> roleIds,
|
||||
@Param("menuId") Long menuId);
|
||||
|
||||
/**
|
||||
* 批量查询菜单的规则
|
||||
*
|
||||
* @param menuIds 菜单ID集合
|
||||
* @return 规则列表
|
||||
*/
|
||||
default List<MenuDataRuleDO> selectListByMenuIds(Collection<Long> menuIds) {
|
||||
return selectList("menu_id", menuIds);
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,8 @@ public interface MenuMapper extends BaseMapperX<MenuDO> {
|
||||
return selectOne(MenuDO::getComponentName, componentName);
|
||||
}
|
||||
|
||||
default MenuDO selectByComponent(String component) {
|
||||
return selectOne(MenuDO::getComponent, component);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.zt.plat.module.system.dal.mysql.permission;
|
||||
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDataRuleDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色菜单数据规则关联 Mapper
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Mapper
|
||||
public interface RoleMenuDataRuleMapper extends BaseMapperX<RoleMenuDataRuleDO> {
|
||||
|
||||
/**
|
||||
* 根据角色ID和菜单ID查询规则关联
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param menuId 菜单ID
|
||||
* @return 规则关联列表
|
||||
*/
|
||||
default List<RoleMenuDataRuleDO> selectListByRoleAndMenu(Long roleId, Long menuId) {
|
||||
return selectList(new LambdaQueryWrapper<RoleMenuDataRuleDO>()
|
||||
.eq(RoleMenuDataRuleDO::getRoleId, roleId)
|
||||
.eq(RoleMenuDataRuleDO::getMenuId, menuId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色ID和菜单ID删除规则关联
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param menuId 菜单ID
|
||||
*/
|
||||
default void deleteByRoleAndMenu(Long roleId, Long menuId) {
|
||||
delete(new LambdaQueryWrapper<RoleMenuDataRuleDO>()
|
||||
.eq(RoleMenuDataRuleDO::getRoleId, roleId)
|
||||
.eq(RoleMenuDataRuleDO::getMenuId, menuId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.zt.plat.module.system.dal.mysql.push;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigPageReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 外部系统推送配置 Mapper
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
@Mapper
|
||||
public interface ExternalPushConfigMapper extends BaseMapperX<ExternalPushConfigDO> {
|
||||
|
||||
default PageResult<ExternalPushConfigDO> selectPage(ExternalPushConfigPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<ExternalPushConfigDO> wrapper = new LambdaQueryWrapperX<ExternalPushConfigDO>()
|
||||
.eqIfPresent(ExternalPushConfigDO::getCompanyId, reqVO.getCompanyId())
|
||||
.eqIfPresent(ExternalPushConfigDO::getBusinessType, reqVO.getBusinessType())
|
||||
.eqIfPresent(ExternalPushConfigDO::getExternalSystem, reqVO.getExternalSystem())
|
||||
.eqIfPresent(ExternalPushConfigDO::getEnablePush, reqVO.getEnablePush());
|
||||
|
||||
// 如果传了 companyId 但没传 deptId,则只查公司级配置(dept_id IS NULL)
|
||||
if (reqVO.getCompanyId() != null && reqVO.getDeptId() == null) {
|
||||
wrapper.isNull(ExternalPushConfigDO::getDeptId);
|
||||
} else if (reqVO.getDeptId() != null) {
|
||||
// 如果传了 deptId,则查指定部门的配置
|
||||
wrapper.eq(ExternalPushConfigDO::getDeptId, reqVO.getDeptId());
|
||||
}
|
||||
// 如果都没传,则查所有配置
|
||||
|
||||
wrapper.orderByDesc(ExternalPushConfigDO::getId);
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用查询配置方法
|
||||
*
|
||||
* @param companyId 公司ID(null 表示查询 company_id IS NULL 的记录)
|
||||
* @param deptId 部门ID(null 表示查询 dept_id IS NULL 的记录)
|
||||
* @param businessType 业务类型(null 表示查询 business_type IS NULL 的记录)
|
||||
* @param externalSystem 外部系统(null 表示查询 external_system IS NULL 的记录)
|
||||
* @return 配置对象
|
||||
*/
|
||||
default ExternalPushConfigDO selectByConfig(Long companyId, Long deptId, String businessType, String externalSystem) {
|
||||
LambdaQueryWrapperX<ExternalPushConfigDO> wrapper = new LambdaQueryWrapperX<>();
|
||||
|
||||
if (companyId == null) {
|
||||
wrapper.isNull(ExternalPushConfigDO::getCompanyId);
|
||||
} else {
|
||||
wrapper.eq(ExternalPushConfigDO::getCompanyId, companyId);
|
||||
}
|
||||
|
||||
if (deptId == null) {
|
||||
wrapper.isNull(ExternalPushConfigDO::getDeptId);
|
||||
} else {
|
||||
wrapper.eq(ExternalPushConfigDO::getDeptId, deptId);
|
||||
}
|
||||
|
||||
if (businessType == null) {
|
||||
wrapper.isNull(ExternalPushConfigDO::getBusinessType);
|
||||
} else {
|
||||
wrapper.eq(ExternalPushConfigDO::getBusinessType, businessType);
|
||||
}
|
||||
|
||||
if (externalSystem == null) {
|
||||
wrapper.isNull(ExternalPushConfigDO::getExternalSystem);
|
||||
} else {
|
||||
wrapper.eq(ExternalPushConfigDO::getExternalSystem, externalSystem);
|
||||
}
|
||||
|
||||
return selectOne(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询公司下所有配置
|
||||
*/
|
||||
default List<ExternalPushConfigDO> selectListByCompanyId(Long companyId) {
|
||||
return selectList(ExternalPushConfigDO::getCompanyId, companyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询部门下所有配置
|
||||
*/
|
||||
default List<ExternalPushConfigDO> selectListByDeptId(Long deptId) {
|
||||
return selectList(ExternalPushConfigDO::getDeptId, deptId);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ public class IWorkProperties {
|
||||
private final Client client = new Client();
|
||||
private final OrgRest org = new OrgRest();
|
||||
private final Workflow workflow = new Workflow();
|
||||
private final Callback callback = new Callback();
|
||||
private final Oa oa = new Oa();
|
||||
|
||||
@Data
|
||||
@@ -127,6 +128,26 @@ public class IWorkProperties {
|
||||
private String sealWorkflowId;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Callback {
|
||||
/**
|
||||
* 业务回调重试配置。
|
||||
*/
|
||||
private final Retry retry = new Retry();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Retry {
|
||||
/**
|
||||
* 最大重试次数,默认 3。
|
||||
*/
|
||||
private int maxAttempts = 3;
|
||||
/**
|
||||
* 重试延迟(秒),默认 5。
|
||||
*/
|
||||
private int delaySeconds = 5;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Oa {
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.zt.plat.module.system.job.sync;
|
||||
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import com.zt.plat.framework.tenant.core.job.TenantJob;
|
||||
import com.zt.plat.module.system.service.sync.SyncIWorkOrgChangeService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 用于定时同步 iWork 当日变更的组织数据
|
||||
* 同步时间:每日23:00
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SyncIWorkOrgChangeJob {
|
||||
|
||||
@Resource
|
||||
private SyncIWorkOrgChangeService syncIWorkOrgChangeService;
|
||||
|
||||
/**
|
||||
* 执行定时任务
|
||||
* 配置执行频率:每日23:00时执行一次
|
||||
* cron表达式:0 0 23 * * ?
|
||||
*/
|
||||
@XxlJob("syncIWorkOrgChangeJob")
|
||||
@TenantJob
|
||||
public void execute() {
|
||||
log.info("[syncIWorkOrgChangeJob][开始执行同步 iWork 当日变更组织任务]");
|
||||
try {
|
||||
int processedCount = syncIWorkOrgChangeService.process();
|
||||
if (processedCount > 0) {
|
||||
log.info("[syncIWorkOrgChangeJob][同步任务执行完成,处理了 {} 条记录]", processedCount);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[syncIWorkOrgChangeJob][同步任务执行失败]", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.zt.plat.module.system.job.sync;
|
||||
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import com.zt.plat.framework.tenant.core.job.TenantJob;
|
||||
import com.zt.plat.module.system.service.sync.SyncIWorkUserChangeService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 用于定时同步iWork当日修改的用户数据
|
||||
* 同步时间:每日23:00
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SyncIWorkUserChangeJob {
|
||||
|
||||
@Resource
|
||||
private SyncIWorkUserChangeService syncIWorkUserChangeService;
|
||||
|
||||
/**
|
||||
* 执行批量重跑任务
|
||||
* 配置执行频率:每日23:00时执行一次
|
||||
* cron表达式:0 0 23 * * ?
|
||||
*/
|
||||
@XxlJob("syncIWorkUserChangeJob")
|
||||
@TenantJob
|
||||
public void execute() {
|
||||
log.info("[syncLogBatchRerunJob][开始执行同步iWork当日变更用户任务]");
|
||||
try{
|
||||
int processedCount = syncIWorkUserChangeService.process();
|
||||
if (processedCount > 0) {
|
||||
log.info("[syncLogBatchRerunJob][同步任务执行完成,处理了 {} 条记录]", processedCount);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[syncLogBatchRerunJob][同步iWork当日变更用户任务执行异常]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.zt.plat.module.system.mq.iwork;
|
||||
|
||||
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkCallbackLogService;
|
||||
import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackMessage;
|
||||
import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackResultMessage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||
import org.apache.rocketmq.spring.core.RocketMQListener;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 监听业务模块回传的处理结果,并在失败时由 system 模块负责重试投递原始回调消息。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@RocketMQMessageListener(topic = IWorkBizCallbackResultMessage.TOPIC, consumerGroup = IWorkBizCallbackResultMessage.TOPIC + "_CONSUMER")
|
||||
public class IWorkBizCallbackListener implements RocketMQListener<IWorkBizCallbackResultMessage>, InitializingBean {
|
||||
|
||||
private final IWorkCallbackLogService logService;
|
||||
private final IWorkProperties properties;
|
||||
private final IWorkBizCallbackProducer producer;
|
||||
private ScheduledExecutorService scheduler;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
scheduler = Executors.newScheduledThreadPool(1, r -> new Thread(r, "iwork-callback-retry"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(IWorkBizCallbackResultMessage message) {
|
||||
String key = message.getBizCallbackKey();
|
||||
if (message.isSuccess()) {
|
||||
logService.markSuccess(message.getRequestId());
|
||||
return;
|
||||
}
|
||||
|
||||
int attempt = message.getAttempt() + 1;
|
||||
logService.incrementRetry(message.getRequestId());
|
||||
int maxAttempts = message.getMaxAttempts() > 0 ? message.getMaxAttempts() : properties.getCallback().getRetry().getMaxAttempts();
|
||||
|
||||
if (attempt < maxAttempts) {
|
||||
logService.markFailure(message.getRequestId(), message.getErrorMessage(), true, maxAttempts);
|
||||
|
||||
IWorkBizCallbackMessage next = IWorkBizCallbackMessage.builder()
|
||||
.requestId(message.getRequestId())
|
||||
.bizCallbackKey(key)
|
||||
.payload(message.getPayload())
|
||||
.attempt(attempt)
|
||||
.maxAttempts(maxAttempts)
|
||||
.build();
|
||||
|
||||
int delay = properties.getCallback().getRetry().getDelaySeconds();
|
||||
scheduler.schedule(() -> producer.send(next), delay, TimeUnit.SECONDS);
|
||||
} else {
|
||||
logService.markFailure(message.getRequestId(), message.getErrorMessage(), false, maxAttempts);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zt.plat.module.system.mq.iwork;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class IWorkBizCallbackProducer {
|
||||
|
||||
private final RocketMQTemplate rocketMQTemplate;
|
||||
|
||||
public void send(IWorkBizCallbackMessage message) {
|
||||
// 使用 tag=bizCallbackKey,方便业务侧按 key 订阅
|
||||
String destination = IWorkBizCallbackMessage.TOPIC + ":" + message.getBizCallbackKey();
|
||||
rocketMQTemplate.syncSend(destination, message);
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,9 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
|
||||
// 校验账号是否存在
|
||||
AdminUserDO user = userService.getUserByUsername(username);
|
||||
if (user == null) {
|
||||
user = userService.getUserByWorkcode(username);
|
||||
}
|
||||
if (user == null) {
|
||||
createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
|
||||
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||
|
||||
@@ -185,4 +185,11 @@ public interface DeptService {
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
void syncDept(DeptSaveReqVO syncReqVO);
|
||||
|
||||
/**
|
||||
* 向上查找公司节点信息
|
||||
* @param deptId
|
||||
* @return
|
||||
*/
|
||||
List<DeptDO> upFindCompanyNode(Long deptId);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,12 @@ import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.DeptMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.userdept.UserDeptMapper;
|
||||
import com.zt.plat.module.system.service.dept.DeptExternalCodeService;
|
||||
import com.zt.plat.module.system.dal.redis.RedisKeyConstants;
|
||||
import com.zt.plat.module.system.enums.dept.DeptSourceEnum;
|
||||
import com.zt.plat.module.system.service.permission.PermissionService;
|
||||
import org.apache.seata.spring.annotation.GlobalTransactional;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.seata.spring.annotation.GlobalTransactional;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -762,6 +761,7 @@ public class DeptServiceImpl implements DeptService {
|
||||
return deptIds;
|
||||
}
|
||||
|
||||
@DataPermission(enable = false)
|
||||
private Long resolveNearestCompanyId(Long deptId, Map<Long, DeptDO> deptCache) {
|
||||
DeptDO current = loadDept(deptId, deptCache);
|
||||
while (current != null) {
|
||||
@@ -960,4 +960,9 @@ public class DeptServiceImpl implements DeptService {
|
||||
// 注意:不发布变更事件,避免循环同步
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeptDO> upFindCompanyNode(Long deptId) {
|
||||
return deptMapper.upFindCompanyNode(deptId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.zt.plat.module.system.service.integration.iwork;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCallbackReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
|
||||
|
||||
public interface IWorkCallbackLogService {
|
||||
|
||||
IWorkSealLogDO upsertOnCallback(IWorkWorkflowCallbackReqVO reqVO, int maxRetry, String rawBody);
|
||||
|
||||
void markSuccess(String requestId);
|
||||
|
||||
void markFailure(String requestId, String error, boolean retrying, int maxRetry);
|
||||
|
||||
void incrementRetry(String requestId);
|
||||
|
||||
PageResult<IWorkSealLogDO> page(IWorkCallbackLogPageReqVO reqVO);
|
||||
|
||||
void resetAndDispatch(String requestId);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zt.plat.module.system.service.integration.iwork.callback;
|
||||
|
||||
public interface IWorkBizCallbackHandler {
|
||||
|
||||
/**
|
||||
* 返回 handler 能处理的 bizCallbackKey。
|
||||
*/
|
||||
String getBizCallbackKey();
|
||||
|
||||
/**
|
||||
* 处理业务回调。
|
||||
* @param requestId iWork requestId
|
||||
* @param payload 回调负载
|
||||
*/
|
||||
void handle(String requestId, Object payload) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.zt.plat.module.system.service.integration.iwork.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum IWorkCallbackStatusEnum {
|
||||
|
||||
CREATE_PENDING(0),
|
||||
CREATE_SUCCESS(1),
|
||||
CREATE_FAILED(2),
|
||||
CALLBACK_PENDING(3),
|
||||
CALLBACK_SUCCESS(4),
|
||||
CALLBACK_FAILED(5),
|
||||
CALLBACK_RETRYING(6),
|
||||
CALLBACK_RETRY_FAILED(7);
|
||||
|
||||
private final int status;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.json.JsonUtils;
|
||||
import com.zt.plat.framework.common.util.object.ObjectUtils;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCallbackReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
|
||||
import com.zt.plat.module.system.dal.mysql.iwork.IWorkSealLogMapper;
|
||||
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
|
||||
import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackMessage;
|
||||
import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackProducer;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkCallbackLogService;
|
||||
import com.zt.plat.module.system.service.integration.iwork.enums.IWorkCallbackStatusEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class IWorkCallbackLogServiceImpl implements IWorkCallbackLogService {
|
||||
|
||||
private static final int RAW_MAX = 2000;
|
||||
|
||||
private final IWorkSealLogMapper logMapper;
|
||||
private final IWorkBizCallbackProducer producer;
|
||||
private final IWorkProperties properties;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public IWorkSealLogDO upsertOnCallback(IWorkWorkflowCallbackReqVO reqVO, int maxRetry, String rawBody) {
|
||||
IWorkSealLogDO existing = logMapper.selectByRequestId(reqVO.getRequestId());
|
||||
IWorkSealLogDO log = Optional.ofNullable(existing).orElseGet(IWorkSealLogDO::new);
|
||||
log.setRequestId(reqVO.getRequestId());
|
||||
log.setBusinessCode(reqVO.getBusinessCode());
|
||||
log.setBizCallbackKey(reqVO.getBizCallbackKey());
|
||||
log.setStatus(IWorkCallbackStatusEnum.CALLBACK_PENDING.getStatus());
|
||||
log.setRetryCount(ObjectUtils.defaultIfNull(log.getRetryCount(), 0));
|
||||
log.setMaxRetry(maxRetry);
|
||||
log.setRawCallback(truncate(rawBody));
|
||||
log.setLastCallbackTime(LocalDateTime.now());
|
||||
if (log.getId() == null) {
|
||||
logMapper.insert(log);
|
||||
} else {
|
||||
logMapper.updateById(log);
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markSuccess(String requestId) {
|
||||
IWorkSealLogDO log = new IWorkSealLogDO();
|
||||
log.setRequestId(requestId);
|
||||
log.setStatus(IWorkCallbackStatusEnum.CALLBACK_SUCCESS.getStatus());
|
||||
log.setLastErrorMessage(null);
|
||||
log.setLastCallbackTime(LocalDateTime.now());
|
||||
logMapper.update(log, new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<IWorkSealLogDO>()
|
||||
.eq(IWorkSealLogDO::getRequestId, requestId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markFailure(String requestId, String error, boolean retrying, int maxRetry) {
|
||||
IWorkSealLogDO log = new IWorkSealLogDO();
|
||||
log.setRequestId(requestId);
|
||||
log.setStatus(retrying ? IWorkCallbackStatusEnum.CALLBACK_RETRYING.getStatus() : IWorkCallbackStatusEnum.CALLBACK_FAILED.getStatus());
|
||||
log.setLastErrorMessage(error);
|
||||
log.setLastCallbackTime(LocalDateTime.now());
|
||||
log.setMaxRetry(maxRetry);
|
||||
logMapper.update(log, new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<IWorkSealLogDO>()
|
||||
.eq(IWorkSealLogDO::getRequestId, requestId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementRetry(String requestId) {
|
||||
IWorkSealLogDO db = logMapper.selectByRequestId(requestId);
|
||||
if (db == null) {
|
||||
return;
|
||||
}
|
||||
IWorkSealLogDO log = new IWorkSealLogDO();
|
||||
log.setId(db.getId());
|
||||
log.setRetryCount(ObjectUtils.defaultIfNull(db.getRetryCount(), 0) + 1);
|
||||
log.setLastCallbackTime(LocalDateTime.now());
|
||||
logMapper.updateById(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<IWorkSealLogDO> page(IWorkCallbackLogPageReqVO reqVO) {
|
||||
return logMapper.selectPage(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAndDispatch(String requestId) {
|
||||
IWorkSealLogDO db = logMapper.selectByRequestId(requestId);
|
||||
if (db == null) {
|
||||
return;
|
||||
}
|
||||
IWorkSealLogDO log = new IWorkSealLogDO();
|
||||
log.setId(db.getId());
|
||||
log.setRetryCount(0);
|
||||
log.setStatus(IWorkCallbackStatusEnum.CALLBACK_RETRYING.getStatus());
|
||||
log.setLastCallbackTime(LocalDateTime.now());
|
||||
logMapper.updateById(log);
|
||||
|
||||
int maxAttempts = properties.getCallback().getRetry().getMaxAttempts();
|
||||
Object payload;
|
||||
try {
|
||||
payload = JsonUtils.parseObject(db.getRawCallback(), Object.class);
|
||||
} catch (Exception ex) {
|
||||
payload = db.getRawCallback();
|
||||
}
|
||||
producer.send(IWorkBizCallbackMessage.builder()
|
||||
.requestId(db.getRequestId())
|
||||
.bizCallbackKey(db.getBizCallbackKey())
|
||||
.payload(payload)
|
||||
.attempt(0)
|
||||
.maxAttempts(maxAttempts)
|
||||
.build());
|
||||
}
|
||||
|
||||
private String truncate(String raw) {
|
||||
if (!StringUtils.hasText(raw)) {
|
||||
return raw;
|
||||
}
|
||||
return raw.length() > RAW_MAX ? raw.substring(0, RAW_MAX) : raw;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,7 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
}
|
||||
|
||||
AtomicReference<Long> attachmentIdRef = new AtomicReference<>();
|
||||
TenantUtils.execute(tenantId, () -> attachmentIdRef.set(saveCallbackAttachment(fileUrl, reqVO.getFileName(), referenceBusinessFile, reqVO.getSsoToken())));
|
||||
TenantUtils.execute(tenantId, () -> attachmentIdRef.set(saveCallbackAttachment(fileUrl, reqVO.getFileName(), referenceBusinessFile)));
|
||||
return attachmentIdRef.get();
|
||||
}
|
||||
|
||||
@@ -251,14 +251,14 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
return executeOaRequest(request);
|
||||
}
|
||||
|
||||
private Long saveCallbackAttachment(String fileUrl, String overrideFileName, BusinessFileRespDTO referenceBusinessFile, String ssoToken) {
|
||||
private Long saveCallbackAttachment(String fileUrl, String overrideFileName, BusinessFileRespDTO referenceBusinessFile) {
|
||||
Long businessId = referenceBusinessFile.getBusinessId();
|
||||
|
||||
FileCreateReqDTO fileCreateReqDTO = new FileCreateReqDTO();
|
||||
fileCreateReqDTO.setName(resolveFileName(overrideFileName, fileUrl));
|
||||
fileCreateReqDTO.setDirectory(null);
|
||||
fileCreateReqDTO.setType(null);
|
||||
fileCreateReqDTO.setContent(downloadFileBytes(fileUrl, ssoToken));
|
||||
fileCreateReqDTO.setContent(downloadFileBytes(fileUrl));
|
||||
|
||||
CommonResult<FileRespDTO> fileResult = fileApi.createFileWithReturn(fileCreateReqDTO);
|
||||
if (fileResult == null || !fileResult.isSuccess() || fileResult.getData() == null) {
|
||||
@@ -297,10 +297,8 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
return businessFile;
|
||||
}
|
||||
|
||||
private byte[] downloadFileBytes(String fileUrl, String ssoToken) {
|
||||
// 如果回调已提供 ssoToken,按需拼接后下载 OA 附件
|
||||
String finalUrl = appendSsoTokenIfNeeded(fileUrl, ssoToken);
|
||||
|
||||
private byte[] downloadFileBytes(String fileUrl) {
|
||||
String finalUrl = fileUrl;
|
||||
OkHttpClient client = okHttpClient();
|
||||
Request request = new Request.Builder().url(finalUrl).get().build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
@@ -317,22 +315,6 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
}
|
||||
}
|
||||
|
||||
private String appendSsoTokenIfNeeded(String fileUrl, String ssoToken) {
|
||||
// 未提供 token 或 URL 为空,直接返回原链接
|
||||
if (!StringUtils.hasText(ssoToken) || !StringUtils.hasText(fileUrl)) {
|
||||
return fileUrl;
|
||||
}
|
||||
// 已包含 ssoToken(不区分大小写)则不重复添加
|
||||
String lower = fileUrl.toLowerCase();
|
||||
if (lower.contains("ssotoken=")) {
|
||||
return fileUrl;
|
||||
}
|
||||
// 简单拼接查询参数
|
||||
return fileUrl.contains("?")
|
||||
? fileUrl + "&ssoToken=" + ssoToken
|
||||
: fileUrl + "?ssoToken=" + ssoToken;
|
||||
}
|
||||
|
||||
private String resolveFileName(String overrideName, String fileUrl) {
|
||||
if (StringUtils.hasText(overrideName)) {
|
||||
return overrideName;
|
||||
|
||||
@@ -92,6 +92,18 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
if (StringUtils.hasText(reqVO.getSubcompanyName())) {
|
||||
params.put("subcompanyname", reqVO.getSubcompanyName());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getModified())) {
|
||||
params.put("modified", reqVO.getModified());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getCanceled())) {
|
||||
params.put("canceled", reqVO.getCanceled());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getCustomData())) {
|
||||
params.put("custom_data", reqVO.getCustomData());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getId())) {
|
||||
params.put("id", reqVO.getId());
|
||||
}
|
||||
JsonNode node = invokeParamsEndpoint(path, params);
|
||||
return buildSubcompanyPageResp(node);
|
||||
}
|
||||
@@ -106,8 +118,23 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
if (StringUtils.hasText(reqVO.getDepartmentName())) {
|
||||
params.put("departmentname", reqVO.getDepartmentName());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getSubcompanyId())) {
|
||||
params.put("subcompanyid", reqVO.getSubcompanyId());
|
||||
if (StringUtils.hasText(reqVO.getSubcompanyId1())) {
|
||||
params.put("subcompanyid1", reqVO.getSubcompanyId1());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getCreated())) {
|
||||
params.put("created", reqVO.getCreated());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getModified())) {
|
||||
params.put("modified", reqVO.getModified());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getCanceled())) {
|
||||
params.put("canceled", reqVO.getCanceled());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getCustomData())) {
|
||||
params.put("custom_data", reqVO.getCustomData());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getId())) {
|
||||
params.put("id", reqVO.getId());
|
||||
}
|
||||
JsonNode node = invokeParamsEndpoint(path, params);
|
||||
return buildDepartmentPageResp(node);
|
||||
@@ -117,12 +144,18 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
public IWorkHrJobTitlePageRespVO listJobTitles(IWorkJobTitleQueryReqVO reqVO) {
|
||||
String path = orgPaths().getJobTitlePage();
|
||||
Map<String, Object> params = buildBaseParams(reqVO);
|
||||
if (StringUtils.hasText(reqVO.getJobTitleCode())) {
|
||||
params.put("jobtitlecode", reqVO.getJobTitleCode());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getJobTitleName())) {
|
||||
params.put("jobtitlename", reqVO.getJobTitleName());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getCreated())) {
|
||||
params.put("created", reqVO.getCreated());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getModified())) {
|
||||
params.put("modified", reqVO.getModified());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getId())) {
|
||||
params.put("id", reqVO.getId());
|
||||
}
|
||||
JsonNode node = invokeParamsEndpoint(path, params);
|
||||
return buildJobTitlePageResp(node);
|
||||
}
|
||||
@@ -134,11 +167,8 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
if (StringUtils.hasText(reqVO.getWorkCode())) {
|
||||
params.put("workcode", reqVO.getWorkCode());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getLastName())) {
|
||||
params.put("lastname", reqVO.getLastName());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getSubcompanyId())) {
|
||||
params.put("subcompanyid", reqVO.getSubcompanyId());
|
||||
if (StringUtils.hasText(reqVO.getSubcompanyId1())) {
|
||||
params.put("subcompanyid1", reqVO.getSubcompanyId1());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getDepartmentId())) {
|
||||
params.put("departmentid", reqVO.getDepartmentId());
|
||||
@@ -146,14 +176,26 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
if (StringUtils.hasText(reqVO.getJobTitleId())) {
|
||||
params.put("jobtitleid", reqVO.getJobTitleId());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getStatus())) {
|
||||
params.put("status", reqVO.getStatus());
|
||||
if (StringUtils.hasText(reqVO.getId())) {
|
||||
params.put("id", reqVO.getId());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getMobile())) {
|
||||
params.put("mobile", reqVO.getMobile());
|
||||
if (StringUtils.hasText(reqVO.getLoginId())) {
|
||||
params.put("loginid", reqVO.getLoginId());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getEmail())) {
|
||||
params.put("email", reqVO.getEmail());
|
||||
if (StringUtils.hasText(reqVO.getCreated())) {
|
||||
params.put("created", reqVO.getCreated());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getModified())) {
|
||||
params.put("modified", reqVO.getModified());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getBaseCustomData())) {
|
||||
params.put("base_custom_data", reqVO.getBaseCustomData());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getPersonCustomData())) {
|
||||
params.put("person_custom_data", reqVO.getPersonCustomData());
|
||||
}
|
||||
if (StringUtils.hasText(reqVO.getWorkCustomData())) {
|
||||
params.put("work_custom_data", reqVO.getWorkCustomData());
|
||||
}
|
||||
JsonNode node = invokeParamsEndpoint(path, params);
|
||||
return buildUserPageResp(node);
|
||||
@@ -161,9 +203,6 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
|
||||
private Map<String, Object> buildBaseParams(IWorkOrgBaseQueryReqVO reqVO) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
if (reqVO.getParams() != null) {
|
||||
params.putAll(reqVO.getParams());
|
||||
}
|
||||
if (reqVO.getCurpage() != null) {
|
||||
params.put("curpage", reqVO.getCurpage());
|
||||
}
|
||||
|
||||
@@ -13,8 +13,11 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUs
|
||||
import com.zt.plat.module.system.controller.admin.user.vo.user.UserSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.PostDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.UserPostDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.PostMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.UserPostMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.user.AdminUserMapper;
|
||||
import com.zt.plat.module.system.enums.common.SexEnum;
|
||||
import com.zt.plat.module.system.enums.dept.DeptSourceEnum;
|
||||
@@ -24,6 +27,7 @@ import com.zt.plat.module.system.service.dept.DeptService;
|
||||
import com.zt.plat.module.system.service.dept.PostService;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
||||
import com.zt.plat.module.system.service.user.AdminUserService;
|
||||
import com.zt.plat.module.system.service.userdept.UserDeptService;
|
||||
import com.zt.plat.module.system.util.sync.SyncVerifyUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -33,6 +37,7 @@ import org.springframework.util.DigestUtils;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -47,8 +52,10 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
private final DeptService deptService;
|
||||
private final PostService postService;
|
||||
private final PostMapper postMapper;
|
||||
private final UserPostMapper userPostMapper;
|
||||
private final AdminUserService adminUserService;
|
||||
private final AdminUserMapper adminUserMapper;
|
||||
private final UserDeptService userDeptService;
|
||||
|
||||
private final Map<String, PostDO> postCache = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -322,7 +329,41 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
if (records.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
long batchStart = System.currentTimeMillis();
|
||||
result.increasePulled(records.size());
|
||||
// 预取已有用户,避免逐条查询
|
||||
long preloadUsersStart = System.currentTimeMillis();
|
||||
Map<Long, AdminUserDO> existingUsers = new HashMap<>();
|
||||
List<Long> userIds = records.stream()
|
||||
.map(IWorkHrUserPageRespVO.User::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Integer::longValue)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (!userIds.isEmpty()) {
|
||||
List<AdminUserDO> users = adminUserMapper.selectBatchIds(userIds);
|
||||
if (CollUtil.isNotEmpty(users)) {
|
||||
users.forEach(user -> existingUsers.put(user.getId(), user));
|
||||
}
|
||||
}
|
||||
long preloadUsersMs = System.currentTimeMillis() - preloadUsersStart;
|
||||
|
||||
// 预取岗位,避免逐条按编码查询
|
||||
long preloadPostsStart = System.currentTimeMillis();
|
||||
List<String> postCodes = records.stream()
|
||||
.map(IWorkHrUserPageRespVO.User::getJobtitleid)
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::buildJobCode)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (!postCodes.isEmpty()) {
|
||||
List<PostDO> posts = postMapper.selectByCodes(postCodes);
|
||||
if (CollUtil.isNotEmpty(posts)) {
|
||||
posts.forEach(post -> postCache.put(buildPostCacheKey(post.getCode()), post));
|
||||
}
|
||||
}
|
||||
long preloadPostsMs = System.currentTimeMillis() - preloadPostsStart;
|
||||
|
||||
for (IWorkHrUserPageRespVO.User user : records) {
|
||||
if (user == null) {
|
||||
continue;
|
||||
@@ -344,7 +385,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
CommonStatusEnum status = inactive ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
|
||||
// 直接沿用 iWork 原始密码,避免重复格式化造成校验偏差
|
||||
String externalPassword = trimToNull(user.getPassword());
|
||||
AdminUserDO existing = adminUserMapper.selectById(user.getId());
|
||||
AdminUserDO existing = user.getId() == null ? null : existingUsers.get(user.getId().longValue());
|
||||
UserSyncOutcome outcome;
|
||||
if (existing == null) {
|
||||
if (!options.isCreateIfMissing()) {
|
||||
@@ -377,6 +418,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
result.withMessage("同步人员失败: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
long totalMs = System.currentTimeMillis() - batchStart;
|
||||
log.info("[iWork] 人员批次同步完成 size={} preloadUsersMs={} preloadPostsMs={} totalMs={}",
|
||||
records.size(), preloadUsersMs, preloadPostsMs, totalMs);
|
||||
return result;
|
||||
}
|
||||
//TODO
|
||||
@@ -494,12 +538,49 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
String externalPassword) {
|
||||
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
|
||||
req.setId(existing.getId());
|
||||
Long iworkDeptId = resolveIWorkDeptId(deptId);
|
||||
req.setDeptIds(null);
|
||||
req.setPostIds(null);
|
||||
boolean disabledChanged = CommonStatusEnum.isDisable(status.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus());
|
||||
adminUserService.updateUser(req);
|
||||
syncPassword(existing, externalPassword);
|
||||
boolean infoChanged = isUserInfoChanged(existing, req);
|
||||
boolean passwordChanged = isPasswordChanged(existing, externalPassword);
|
||||
boolean deptChanged = syncIWorkUserDeptRelations(existing.getId(), iworkDeptId);
|
||||
boolean postChanged = syncIWorkUserPostRelations(existing.getId(), postId);
|
||||
|
||||
if (!infoChanged && !passwordChanged && !deptChanged && !postChanged) {
|
||||
return new UserSyncOutcome(SyncAction.SKIPPED, false, existing.getId());
|
||||
}
|
||||
|
||||
if (infoChanged) {
|
||||
adminUserService.updateUser(req);
|
||||
}
|
||||
if (passwordChanged) {
|
||||
syncPassword(existing, externalPassword);
|
||||
}
|
||||
return new UserSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
|
||||
}
|
||||
|
||||
private boolean isUserInfoChanged(AdminUserDO existing, UserSaveReqVO req) {
|
||||
if (existing == null || req == null) {
|
||||
return false;
|
||||
}
|
||||
return !Objects.equals(existing.getUsername(), req.getUsername())
|
||||
|| !Objects.equals(existing.getWorkcode(), req.getWorkcode())
|
||||
|| !Objects.equals(existing.getNickname(), req.getNickname())
|
||||
|| !Objects.equals(existing.getRemark(), req.getRemark())
|
||||
|| !Objects.equals(existing.getEmail(), req.getEmail())
|
||||
|| !Objects.equals(existing.getMobile(), req.getMobile())
|
||||
|| !Objects.equals(existing.getSex(), req.getSex())
|
||||
|| !Objects.equals(existing.getStatus(), req.getStatus());
|
||||
}
|
||||
|
||||
private boolean isPasswordChanged(AdminUserDO existing, String externalPassword) {
|
||||
if (existing == null || StrUtil.isBlank(externalPassword)) {
|
||||
return false;
|
||||
}
|
||||
return !StrUtil.equals(externalPassword, existing.getPassword());
|
||||
}
|
||||
|
||||
private DeptSaveReqVO buildSubcompanySaveReq(IWorkHrSubcompanyPageRespVO.Subcompany data,
|
||||
Long deptId,
|
||||
Long parentId,
|
||||
@@ -550,8 +631,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
req.setWorkcode(resolveWorkcode(source));
|
||||
req.setNickname(limitLength(StrUtil.blankToDefault(source.getLastname(), username), 30));
|
||||
req.setRemark(buildUserRemark(source));
|
||||
if (deptId != null) {
|
||||
req.setDeptIds(singletonSet(deptId));
|
||||
Long iworkDeptId = resolveIWorkDeptId(deptId);
|
||||
if (iworkDeptId != null) {
|
||||
req.setDeptIds(singletonSet(iworkDeptId));
|
||||
}
|
||||
if (postId != null) {
|
||||
req.setPostIds(singletonSet(postId));
|
||||
@@ -566,6 +648,74 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
return req;
|
||||
}
|
||||
|
||||
private boolean syncIWorkUserDeptRelations(Long userId, Long iworkDeptId) {
|
||||
if (userId == null) {
|
||||
return false;
|
||||
}
|
||||
List<UserDeptDO> relations = userDeptService.getValidUserDeptListByUserIds(Collections.singleton(userId));
|
||||
Set<Long> existingDeptIds = relations.stream()
|
||||
.map(UserDeptDO::getDeptId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
Set<Long> existingIworkDeptIds = new LinkedHashSet<>();
|
||||
if (CollUtil.isNotEmpty(existingDeptIds)) {
|
||||
Map<Long, DeptDO> deptMap = deptService.getDeptMap(existingDeptIds);
|
||||
existingIworkDeptIds = deptMap.values().stream()
|
||||
.filter(this::isIWorkDept)
|
||||
.map(DeptDO::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
Set<Long> desiredIworkDeptIds = iworkDeptId == null
|
||||
? Collections.emptySet()
|
||||
: Collections.singleton(iworkDeptId);
|
||||
if (existingIworkDeptIds.equals(desiredIworkDeptIds)) {
|
||||
return false;
|
||||
}
|
||||
Collection<Long> toDelete = CollUtil.subtract(existingIworkDeptIds, desiredIworkDeptIds);
|
||||
Collection<Long> toAdd = CollUtil.subtract(desiredIworkDeptIds, existingIworkDeptIds);
|
||||
if (CollUtil.isNotEmpty(toDelete)) {
|
||||
userDeptService.deleteUserDeptByUserIdAndDeptIds(userId, toDelete);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(toAdd)) {
|
||||
userDeptService.batchCreateUserDept(Collections.singletonList(
|
||||
new UserDeptDO().setUserId(userId).setDeptId(iworkDeptId)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean syncIWorkUserPostRelations(Long userId, Long postId) {
|
||||
if (userId == null || postId == null) {
|
||||
return false;
|
||||
}
|
||||
List<UserPostDO> relations = userPostMapper.selectListByUserId(userId);
|
||||
Set<Long> existingPostIds = relations.stream()
|
||||
.map(UserPostDO::getPostId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
if (existingPostIds.contains(postId)) {
|
||||
return false;
|
||||
}
|
||||
userPostMapper.insertBatch(Collections.singletonList(
|
||||
new UserPostDO().setUserId(userId).setPostId(postId)));
|
||||
return true;
|
||||
}
|
||||
|
||||
private Long resolveIWorkDeptId(Long deptId) {
|
||||
if (deptId == null) {
|
||||
return null;
|
||||
}
|
||||
DeptDO dept = deptService.getDept(deptId);
|
||||
return isIWorkDept(dept) ? deptId : null;
|
||||
}
|
||||
|
||||
private boolean isIWorkDept(DeptDO dept) {
|
||||
if (dept == null) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(dept.getDeptSource(), DeptSourceEnum.IWORK.getSource());
|
||||
}
|
||||
|
||||
private void mergeDeptDefaults(DeptSaveReqVO target, DeptDO existing) {
|
||||
target.setCode(StrUtil.blankToDefault(target.getCode(), existing.getCode()));
|
||||
target.setShortName(StrUtil.blankToDefault(target.getShortName(), existing.getShortName()));
|
||||
@@ -729,7 +879,12 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
if (StrUtil.isBlank(statusFlag)) {
|
||||
return false;
|
||||
}
|
||||
return !"0".equals(statusFlag.trim());
|
||||
Integer status = parseInteger(statusFlag);
|
||||
if (status == null) {
|
||||
return false;
|
||||
}
|
||||
// iWork 状态:0试用、1正式、2临时、3试用延期、4解聘、5离职、6退休、7无效
|
||||
return status >= 4;
|
||||
}
|
||||
|
||||
private Integer resolveSex(String sexFlag) {
|
||||
|
||||
@@ -11,14 +11,13 @@ import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.IWORK_ORG_REMOTE_FAILED;
|
||||
@@ -217,11 +216,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
|
||||
int processedPages = 0;
|
||||
for (int page = startPage; processedPages < pagesLimit; page++) {
|
||||
long pageStart = System.currentTimeMillis();
|
||||
BatchExecution execution = executor.execute(page, pageSize);
|
||||
long pageMs = System.currentTimeMillis() - pageStart;
|
||||
if (execution == null || execution.totalPulled == 0) {
|
||||
break;
|
||||
}
|
||||
processedPages++;
|
||||
log.info("[iWork] 全量同步 {} 页={} pulled={} costMs={}", type, page, execution.totalPulled, pageMs);
|
||||
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
|
||||
batchStat.setEntityType(type);
|
||||
batchStat.setPageNumber(page);
|
||||
@@ -258,15 +260,18 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
if (query == null || reqVO == null) {
|
||||
return;
|
||||
}
|
||||
copyQueryParameters(reqVO, query); // 设置查询条件
|
||||
if (StrUtil.isBlank(reqVO.getId())) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> params = query.getParams();
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
query.setParams(params);
|
||||
applyQueryId(query, reqVO.getId());
|
||||
}
|
||||
|
||||
private void copyQueryParameters(IWorkFullSyncReqVO reqVO, IWorkOrgBaseQueryReqVO query) {
|
||||
BeanUtils.copyProperties(reqVO, query);
|
||||
if (query instanceof IWorkUserQueryReqVO userQuery) {
|
||||
userQuery.setDepartmentId(reqVO.getDepartmentCode()); // 设置部门编号
|
||||
}
|
||||
params.put("id", reqVO.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,12 +390,28 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
if (StrUtil.isBlank(reqVO.getId())) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> params = query.getParams();
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
query.setParams(params);
|
||||
applyQueryId(query, reqVO.getId());
|
||||
}
|
||||
|
||||
private void applyQueryId(IWorkOrgBaseQueryReqVO query, String id) {
|
||||
if (query == null || StrUtil.isBlank(id)) {
|
||||
return;
|
||||
}
|
||||
if (query instanceof IWorkSubcompanyQueryReqVO subcompanyQuery) {
|
||||
subcompanyQuery.setId(id);
|
||||
return;
|
||||
}
|
||||
if (query instanceof IWorkDepartmentQueryReqVO departmentQuery) {
|
||||
departmentQuery.setId(id);
|
||||
return;
|
||||
}
|
||||
if (query instanceof IWorkJobTitleQueryReqVO jobTitleQuery) {
|
||||
jobTitleQuery.setId(id);
|
||||
return;
|
||||
}
|
||||
if (query instanceof IWorkUserQueryReqVO userQuery) {
|
||||
userQuery.setId(id);
|
||||
}
|
||||
params.put("id", reqVO.getId());
|
||||
}
|
||||
|
||||
|
||||
@@ -403,11 +424,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
|
||||
int processedPages = 0;
|
||||
for (int page = startPage; processedPages < pagesLimit; page++) {
|
||||
long pageStart = System.currentTimeMillis();
|
||||
BatchExecution execution = executor.execute(page, pageSize);
|
||||
long pageMs = System.currentTimeMillis() - pageStart;
|
||||
if (execution == null || execution.totalPulled == 0) {
|
||||
break;
|
||||
}
|
||||
processedPages++;
|
||||
log.info("[iWork] 手动同步 {} 页={} pulled={} costMs={}", type, page, execution.totalPulled, pageMs);
|
||||
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
|
||||
batchStat.setEntityType(type);
|
||||
batchStat.setPageNumber(page);
|
||||
|
||||
@@ -203,6 +203,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
.put(LoginUser.INFO_KEY_TENANT_ID, user.getTenantId().toString())
|
||||
.put(LoginUser.INFO_KEY_USERNAME, user.getUsername())
|
||||
.put(LoginUser.INFO_KEY_PHONE, user.getMobile())
|
||||
.put(LoginUser.INFO_KEY_WORK_CODE, user.getWorkcode())
|
||||
.put(LoginUser.INFO_KEY_POST_IDS, CollUtil.isEmpty(user.getPostIds()) ? "[]" : JsonUtils.toJsonString(user.getPostIds()))
|
||||
.build();
|
||||
} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.zt.plat.module.system.service.permission;
|
||||
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 菜单数据规则 Service 接口
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
public interface MenuDataRuleService {
|
||||
|
||||
/**
|
||||
* 创建菜单数据规则
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 规则ID
|
||||
*/
|
||||
Long createMenuDataRule(@Valid MenuDataRuleSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新菜单数据规则
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateMenuDataRule(@Valid MenuDataRuleSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除菜单数据规则
|
||||
*
|
||||
* @param id 规则ID
|
||||
*/
|
||||
void deleteMenuDataRule(Long id);
|
||||
|
||||
/**
|
||||
* 获取菜单数据规则
|
||||
*
|
||||
* @param id 规则ID
|
||||
* @return 规则信息
|
||||
*/
|
||||
MenuDataRuleDO getMenuDataRule(Long id);
|
||||
|
||||
/**
|
||||
* 获取菜单的所有数据规则
|
||||
*
|
||||
* @param menuId 菜单ID
|
||||
* @return 规则列表
|
||||
*/
|
||||
List<MenuDataRuleDO> getMenuDataRuleListByMenuId(Long menuId);
|
||||
|
||||
/**
|
||||
* 获取用户在指定菜单下的有效数据规则
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param menuId 菜单ID
|
||||
* @return 规则列表
|
||||
*/
|
||||
List<MenuDataRuleDO> getUserMenuDataRules(Long userId, Long menuId);
|
||||
|
||||
/**
|
||||
* 批量获取菜单的数据规则(带缓存)
|
||||
*
|
||||
* @param menuIds 菜单ID列表
|
||||
* @return 菜单ID -> 规则列表的映射
|
||||
*/
|
||||
Map<Long, List<MenuDataRuleDO>> getMenuDataRuleMap(Collection<Long> menuIds);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.zt.plat.module.system.service.permission;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.MenuDO;
|
||||
import com.zt.plat.module.system.dal.mysql.permission.MenuMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 页面组件映射服务
|
||||
* 根据pageComponent查询对应的菜单ID
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class PageComponentMappingService {
|
||||
|
||||
@Resource
|
||||
private MenuMapper menuMapper;
|
||||
|
||||
/**
|
||||
* 根据页面组件路径获取菜单ID
|
||||
*
|
||||
* @param pageComponent 页面组件路径,如:system/role/index
|
||||
* @return 菜单ID,如果未找到返回null
|
||||
*/
|
||||
public Long getMenuIdByPageComponent(String pageComponent) {
|
||||
if (StrUtil.isBlank(pageComponent)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
log.debug("[getMenuIdByPageComponent][查询pageComponent: {}]", pageComponent);
|
||||
|
||||
// 使用精确匹配查询菜单
|
||||
MenuDO menu = menuMapper.selectByComponent(pageComponent);
|
||||
|
||||
if (menu != null) {
|
||||
log.debug("[getMenuIdByPageComponent][找到匹配菜单: ID={}, Name={}, Component={}]",
|
||||
menu.getId(), menu.getName(), menu.getComponent());
|
||||
// 兼容达梦数据库,ID可能是Integer类型,需要转换为Long
|
||||
Object id = menu.getId();
|
||||
if (id instanceof Number) {
|
||||
return ((Number) id).longValue();
|
||||
}
|
||||
return (Long) id;
|
||||
}
|
||||
|
||||
log.warn("[getMenuIdByPageComponent][未找到匹配的菜单: {}]", pageComponent);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,23 @@ public interface PermissionService {
|
||||
*/
|
||||
Set<Long> getMenuRoleIdListByMenuIdFromCache(Long menuId);
|
||||
|
||||
/**
|
||||
* 批量设置角色-菜单-规则关联
|
||||
*
|
||||
* @param roleId 角色编号
|
||||
* @param menuDataRules 菜单和规则的映射关系
|
||||
*/
|
||||
void assignRoleMenuDataRules(Long roleId, Collection<PermissionAssignRoleMenuItemReqVO> menuDataRules);
|
||||
|
||||
/**
|
||||
* 获取角色在指定菜单下已选择的数据规则ID列表
|
||||
*
|
||||
* @param roleId 角色编号
|
||||
* @param menuId 菜单编号
|
||||
* @return 数据规则ID列表
|
||||
*/
|
||||
Set<Long> getRoleMenuDataRules(Long roleId, Long menuId);
|
||||
|
||||
// ========== 用户-角色的相关方法 ==========
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,6 +76,8 @@ public class PermissionServiceImpl implements PermissionService {
|
||||
private RoleMenuMapper roleMenuMapper;
|
||||
@Resource
|
||||
private UserRoleMapper userRoleMapper;
|
||||
@Resource
|
||||
private com.zt.plat.module.system.dal.mysql.permission.RoleMenuDataRuleMapper roleMenuDataRuleMapper;
|
||||
|
||||
private RoleService roleService;
|
||||
@Resource
|
||||
@@ -221,6 +223,45 @@ public class PermissionServiceImpl implements PermissionService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void assignRoleMenuDataRules(Long roleId, Collection<PermissionAssignRoleMenuItemReqVO> menuDataRules) {
|
||||
if (CollUtil.isEmpty(menuDataRules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历每个菜单,更新其数据规则关联
|
||||
for (PermissionAssignRoleMenuItemReqVO menuDataRule : menuDataRules) {
|
||||
Long menuId = menuDataRule.getId();
|
||||
List<Long> dataRuleIds = menuDataRule.getDataRuleIds();
|
||||
|
||||
// 删除该角色在该菜单下的旧规则关联
|
||||
roleMenuDataRuleMapper.deleteByRoleAndMenu(roleId, menuId);
|
||||
|
||||
// 如果有新规则,则插入
|
||||
if (CollUtil.isNotEmpty(dataRuleIds)) {
|
||||
List<com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDataRuleDO> entities =
|
||||
dataRuleIds.stream().map(ruleId -> {
|
||||
com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDataRuleDO entity =
|
||||
new com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDataRuleDO();
|
||||
entity.setRoleId(roleId);
|
||||
entity.setMenuId(menuId);
|
||||
entity.setDataRuleId(ruleId);
|
||||
return entity;
|
||||
}).collect(Collectors.toList());
|
||||
roleMenuDataRuleMapper.insertBatch(entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getRoleMenuDataRules(Long roleId, Long menuId) {
|
||||
List<com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDataRuleDO> list =
|
||||
roleMenuDataRuleMapper.selectListByRoleAndMenu(roleId, menuId);
|
||||
return CollectionUtils.convertSet(list,
|
||||
com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDataRuleDO::getDataRuleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Caching(evict = {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.zt.plat.module.system.service.permission.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleSaveReqVO;
|
||||
import com.zt.plat.module.system.convert.permission.MenuDataRuleConvert;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
|
||||
import com.zt.plat.module.system.dal.mysql.permission.MenuDataRuleMapper;
|
||||
import com.zt.plat.module.system.service.permission.MenuDataRuleService;
|
||||
import com.zt.plat.module.system.service.permission.PermissionService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.module.system.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 菜单数据规则 Service 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class MenuDataRuleServiceImpl implements MenuDataRuleService {
|
||||
|
||||
@Resource
|
||||
private MenuDataRuleMapper menuDataRuleMapper;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = "menuDataRule", key = "#createReqVO.menuId")
|
||||
public Long createMenuDataRule(MenuDataRuleSaveReqVO createReqVO) {
|
||||
MenuDataRuleDO rule = MenuDataRuleConvert.INSTANCE.convert(createReqVO);
|
||||
menuDataRuleMapper.insert(rule);
|
||||
return rule.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = "menuDataRule", key = "#updateReqVO.menuId")
|
||||
public void updateMenuDataRule(MenuDataRuleSaveReqVO updateReqVO) {
|
||||
validateMenuDataRuleExists(updateReqVO.getId());
|
||||
MenuDataRuleDO updateObj = MenuDataRuleConvert.INSTANCE.convert(updateReqVO);
|
||||
menuDataRuleMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMenuDataRule(Long id) {
|
||||
MenuDataRuleDO rule = validateMenuDataRuleExists(id);
|
||||
menuDataRuleMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuDataRuleDO getMenuDataRule(Long id) {
|
||||
return menuDataRuleMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "menuDataRule", key = "#menuId")
|
||||
public List<MenuDataRuleDO> getMenuDataRuleListByMenuId(Long menuId) {
|
||||
return menuDataRuleMapper.selectListByMenuId(menuId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MenuDataRuleDO> getUserMenuDataRules(Long userId, Long menuId) {
|
||||
Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(userId);
|
||||
if (CollUtil.isEmpty(roleIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<MenuDataRuleDO> allRules = getMenuDataRuleListByMenuId(menuId);
|
||||
if (CollUtil.isEmpty(allRules)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Long> ruleIds = menuDataRuleMapper.selectRuleIdsByRoleAndMenu(roleIds, menuId);
|
||||
|
||||
// 如果角色没有关联任何规则,返回空列表(不应用任何过滤)
|
||||
if (CollUtil.isEmpty(ruleIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return allRules.stream()
|
||||
.filter(rule -> ruleIds.contains(rule.getId()) && rule.getStatus() == 1)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, List<MenuDataRuleDO>> getMenuDataRuleMap(Collection<Long> menuIds) {
|
||||
List<MenuDataRuleDO> rules = menuDataRuleMapper.selectListByMenuIds(menuIds);
|
||||
return rules.stream().collect(Collectors.groupingBy(MenuDataRuleDO::getMenuId));
|
||||
}
|
||||
|
||||
private MenuDataRuleDO validateMenuDataRuleExists(Long id) {
|
||||
MenuDataRuleDO rule = menuDataRuleMapper.selectById(id);
|
||||
if (rule == null) {
|
||||
throw exception(MENU_NOT_EXISTS);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
@@ -60,4 +60,11 @@ public interface PortalService {
|
||||
*/
|
||||
List<PortalDO> getPortalListByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 获得公开门户列表(无需登录)
|
||||
*
|
||||
* @return 门户列表
|
||||
*/
|
||||
List<PortalDO> getPublicPortalList();
|
||||
|
||||
}
|
||||
|
||||
@@ -126,6 +126,11 @@ public class PortalServiceImpl implements PortalService {
|
||||
return portalMapper.selectListByPermissions(permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PortalDO> getPublicPortalList() {
|
||||
return portalMapper.selectListByPermissions(Collections.emptyList());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public PortalDO validatePortalExists(Long id) {
|
||||
if (id == null) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.zt.plat.module.system.service.push;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* 外部系统推送配置 Service 接口
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
public interface ExternalPushConfigService {
|
||||
|
||||
/**
|
||||
* 创建推送配置
|
||||
*/
|
||||
Long createExternalPushConfig(@Valid ExternalPushConfigSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 修改推送配置
|
||||
*/
|
||||
void updateExternalPushConfig(@Valid ExternalPushConfigSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除推送配置
|
||||
*/
|
||||
void deleteExternalPushConfig(Long id);
|
||||
|
||||
/**
|
||||
* 获取推送配置详情
|
||||
*/
|
||||
ExternalPushConfigDO getExternalPushConfig(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询推送配置
|
||||
*/
|
||||
PageResult<ExternalPushConfigDO> getExternalPushConfigPage(ExternalPushConfigPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 判断是否允许推送(核心业务逻辑)
|
||||
*
|
||||
* 优先级:部门配置 > 公司配置 > 默认允许
|
||||
*
|
||||
* @param companyId 公司编号(必填)
|
||||
* @param deptId 部门编号(可选)
|
||||
* @param businessType 业务类型(必填)
|
||||
* @param externalSystem 外部系统标识(必填)
|
||||
* @return 是否允许推送(true=允许,false=禁止,默认 true)
|
||||
*/
|
||||
Boolean isPushEnabled(Long companyId, Long deptId, String businessType, String externalSystem);
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package com.zt.plat.module.system.service.push;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.push.vo.ExternalPushConfigSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.push.ExternalPushConfigDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.DeptMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.push.ExternalPushConfigMapper;
|
||||
import com.zt.plat.module.system.enums.push.BusinessTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.module.system.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 外部系统推送配置 Service 实现类
|
||||
*
|
||||
* @author ZT Cloud
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class ExternalPushConfigServiceImpl implements ExternalPushConfigService {
|
||||
|
||||
@Resource
|
||||
private ExternalPushConfigMapper externalPushConfigMapper;
|
||||
@Resource
|
||||
private DeptMapper deptMapper;
|
||||
|
||||
@Override
|
||||
public Long createExternalPushConfig(ExternalPushConfigSaveReqVO createReqVO) {
|
||||
// 参数规范化
|
||||
normalizeRequest(createReqVO);
|
||||
|
||||
// 业务校验
|
||||
validateForCreateOrUpdate(null, createReqVO);
|
||||
|
||||
// 创建配置
|
||||
ExternalPushConfigDO entity = BeanUtils.toBean(createReqVO, ExternalPushConfigDO.class);
|
||||
if (entity.getEnablePush() == null) {
|
||||
entity.setEnablePush(true);
|
||||
}
|
||||
externalPushConfigMapper.insert(entity);
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateExternalPushConfig(ExternalPushConfigSaveReqVO updateReqVO) {
|
||||
// 参数规范化
|
||||
normalizeRequest(updateReqVO);
|
||||
|
||||
// 校验存在
|
||||
validateExists(updateReqVO.getId());
|
||||
// 业务校验
|
||||
validateForCreateOrUpdate(updateReqVO.getId(), updateReqVO);
|
||||
|
||||
// 更新配置
|
||||
ExternalPushConfigDO updateObj = BeanUtils.toBean(updateReqVO, ExternalPushConfigDO.class);
|
||||
externalPushConfigMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteExternalPushConfig(Long id) {
|
||||
validateExists(id);
|
||||
externalPushConfigMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalPushConfigDO getExternalPushConfig(Long id) {
|
||||
return externalPushConfigMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<ExternalPushConfigDO> getExternalPushConfigPage(ExternalPushConfigPageReqVO reqVO) {
|
||||
return externalPushConfigMapper.selectPage(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isPushEnabled(Long companyId, Long deptId, String businessType, String externalSystem) {
|
||||
// 规范化参数
|
||||
String normalizedBusinessType = StrUtil.isNotBlank(businessType) ? businessType.trim().toUpperCase() : null;
|
||||
String normalizedExternalSystem = StrUtil.isNotBlank(externalSystem) ? externalSystem.trim().toUpperCase() : null;
|
||||
|
||||
// 优先级 1:部门级配置(如果 deptId 不为空),递归查找父部门
|
||||
if (deptId != null) {
|
||||
ExternalPushConfigDO deptConfig = findDeptConfigRecursive(
|
||||
companyId, deptId, normalizedBusinessType, normalizedExternalSystem);
|
||||
if (deptConfig != null) {
|
||||
return deptConfig.getEnablePush() != null ? deptConfig.getEnablePush() : true;
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级 2:公司级配置(dept_id 为 null)
|
||||
if (companyId != null) {
|
||||
ExternalPushConfigDO companyConfig = externalPushConfigMapper.selectByConfig(
|
||||
companyId, null, normalizedBusinessType, normalizedExternalSystem);
|
||||
if (companyConfig != null) {
|
||||
return companyConfig.getEnablePush() != null ? companyConfig.getEnablePush() : true;
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级 3:全局配置(company_id 和 dept_id 都为空)
|
||||
ExternalPushConfigDO globalConfig = externalPushConfigMapper.selectByConfig(
|
||||
null, null, normalizedBusinessType, normalizedExternalSystem);
|
||||
if (globalConfig != null) {
|
||||
return globalConfig.getEnablePush() != null ? globalConfig.getEnablePush() : true;
|
||||
}
|
||||
|
||||
// 优先级 4:没有配置,默认允许推送
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归查找部门配置(包括父部门)
|
||||
*
|
||||
* @param companyId 公司ID
|
||||
* @param deptId 部门ID
|
||||
* @param businessType 业务类型
|
||||
* @param externalSystem 外部系统
|
||||
* @return 配置对象,如果找不到返回 null
|
||||
*/
|
||||
private ExternalPushConfigDO findDeptConfigRecursive(Long companyId, Long deptId,
|
||||
String businessType, String externalSystem) {
|
||||
if (deptId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 查询当前部门的配置
|
||||
ExternalPushConfigDO config = externalPushConfigMapper.selectByConfig(
|
||||
companyId, deptId, businessType, externalSystem);
|
||||
if (config != null) {
|
||||
return config;
|
||||
}
|
||||
|
||||
// 查询当前部门信息,获取父部门ID
|
||||
DeptDO dept = deptMapper.selectById(deptId);
|
||||
if (dept == null || dept.getParentId() == null || dept.getParentId() == 0L) {
|
||||
// 没有父部门了,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查父部门是否是公司节点
|
||||
DeptDO parentDept = deptMapper.selectById(dept.getParentId());
|
||||
if (parentDept != null && Boolean.TRUE.equals(parentDept.getIsCompany())) {
|
||||
// 父部门是公司,不再向上查找(公司级配置在外层处理)
|
||||
return null;
|
||||
}
|
||||
|
||||
// 递归查找父部门的配置
|
||||
return findDeptConfigRecursive(companyId, dept.getParentId(), businessType, externalSystem);
|
||||
}
|
||||
|
||||
//==================== 私有方法 ====================
|
||||
|
||||
/**
|
||||
* 校验配置是否存在
|
||||
*/
|
||||
private ExternalPushConfigDO validateExists(Long id) {
|
||||
if (id == null) {
|
||||
throw exception(EXTERNAL_PUSH_CONFIG_NOT_EXISTS);
|
||||
}
|
||||
ExternalPushConfigDO entity = externalPushConfigMapper.selectById(id);
|
||||
if (entity == null) {
|
||||
throw exception(EXTERNAL_PUSH_CONFIG_NOT_EXISTS);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务校验
|
||||
*/
|
||||
private void validateForCreateOrUpdate(Long id, ExternalPushConfigSaveReqVO reqVO) {
|
||||
// 1. 如果指定公司,校验公司存在且是公司节点
|
||||
if (reqVO.getCompanyId() != null) {
|
||||
DeptDO company = deptMapper.selectById(reqVO.getCompanyId());
|
||||
if (company == null) {
|
||||
throw exception(DEPT_NOT_FOUND);
|
||||
}
|
||||
if (!Boolean.TRUE.equals(company.getIsCompany())) {
|
||||
throw exception(EXTERNAL_PUSH_CONFIG_COMPANY_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 如果指定部门,校验部门存在且不是公司节点
|
||||
if (reqVO.getDeptId() != null) {
|
||||
DeptDO dept = deptMapper.selectById(reqVO.getDeptId());
|
||||
if (dept == null) {
|
||||
throw exception(DEPT_NOT_FOUND);
|
||||
}
|
||||
if (Boolean.TRUE.equals(dept.getIsCompany())) {
|
||||
throw exception(EXTERNAL_PUSH_CONFIG_DEPT_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 如果指定业务类型,校验业务类型有效性
|
||||
if (StrUtil.isNotBlank(reqVO.getBusinessType())) {
|
||||
if (!BusinessTypeEnum.isValidCode(reqVO.getBusinessType())) {
|
||||
throw exception(EXTERNAL_PUSH_CONFIG_BUSINESS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 校验唯一性:同一租户+公司+部门+业务类型+外部系统的配置唯一
|
||||
ExternalPushConfigDO existing = externalPushConfigMapper.selectByConfig(
|
||||
reqVO.getCompanyId(), reqVO.getDeptId(),
|
||||
reqVO.getBusinessType(), reqVO.getExternalSystem());
|
||||
|
||||
if (existing != null && (id == null || !existing.getId().equals(id))) {
|
||||
throw exception(EXTERNAL_PUSH_CONFIG_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数规范化
|
||||
*/
|
||||
private void normalizeRequest(ExternalPushConfigSaveReqVO reqVO) {
|
||||
if (reqVO == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果 companyId 为空但 deptId 不为空,自动查找并填充顶级公司ID
|
||||
if (reqVO.getCompanyId() == null && reqVO.getDeptId() != null) {
|
||||
Long topCompanyId = findTopCompanyId(reqVO.getDeptId());
|
||||
if (topCompanyId != null) {
|
||||
reqVO.setCompanyId(topCompanyId);
|
||||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(reqVO.getBusinessType())) {
|
||||
reqVO.setBusinessType(reqVO.getBusinessType().trim().toUpperCase());
|
||||
}
|
||||
if (StrUtil.isNotBlank(reqVO.getExternalSystem())) {
|
||||
reqVO.setExternalSystem(reqVO.getExternalSystem().trim().toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找部门的顶级公司ID
|
||||
*
|
||||
* @param deptId 部门ID
|
||||
* @return 顶级公司ID,如果不存在返回 null
|
||||
*/
|
||||
private Long findTopCompanyId(Long deptId) {
|
||||
if (deptId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DeptDO dept = deptMapper.selectById(deptId);
|
||||
if (dept == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 递归向上查找,直到找到公司节点或者parentId为空/0
|
||||
Long currentDeptId = deptId;
|
||||
while (currentDeptId != null) {
|
||||
DeptDO currentDept = deptMapper.selectById(currentDeptId);
|
||||
if (currentDept == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果当前节点是公司,返回其ID
|
||||
if (Boolean.TRUE.equals(currentDept.getIsCompany())) {
|
||||
return currentDept.getId();
|
||||
}
|
||||
|
||||
// 如果没有父节点或父节点为0,结束查找
|
||||
if (currentDept.getParentId() == null || currentDept.getParentId() == 0L) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 继续向上查找
|
||||
currentDeptId = currentDept.getParentId();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zt.plat.module.system.service.sync;
|
||||
|
||||
/**
|
||||
* 定时同步 iWork 组织变更服务
|
||||
*/
|
||||
public interface SyncIWorkOrgChangeService {
|
||||
|
||||
/**
|
||||
* 执行同步
|
||||
* @return 拉取记录数量
|
||||
*/
|
||||
int process();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.zt.plat.module.system.service.sync;
|
||||
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Service
|
||||
public class SyncIWorkOrgChangeServiceImpl implements SyncIWorkOrgChangeService {
|
||||
|
||||
@Resource
|
||||
private IWorkSyncService iWorkSyncService;
|
||||
|
||||
@Override
|
||||
public int process() {
|
||||
IWorkFullSyncReqVO reqVO = new IWorkFullSyncReqVO();
|
||||
reqVO.setPageSize(10);
|
||||
ZoneId zone = ZoneId.of("Asia/Shanghai");
|
||||
String startOfToday = LocalDate.now(zone)
|
||||
.atStartOfDay(zone)
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
reqVO.setModified(startOfToday);
|
||||
IWorkFullSyncRespVO subcompanyResp = iWorkSyncService.fullSyncSubcompanies(reqVO);
|
||||
IWorkFullSyncRespVO departmentResp = iWorkSyncService.fullSyncDepartments(reqVO);
|
||||
return countPulled(subcompanyResp) + countPulled(departmentResp);
|
||||
}
|
||||
|
||||
private int countPulled(IWorkFullSyncRespVO respVO) {
|
||||
if (respVO == null || respVO.getBatches() == null) {
|
||||
return 0;
|
||||
}
|
||||
return respVO.getBatches().stream()
|
||||
.mapToInt(batch -> batch.getPulled() == null ? 0 : batch.getPulled())
|
||||
.sum();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.zt.plat.module.system.service.sync;
|
||||
|
||||
/**
|
||||
* 同步iWork当日修改的用户数据
|
||||
*/
|
||||
public interface SyncIWorkUserChangeService {
|
||||
|
||||
/**
|
||||
* 同步入口
|
||||
* @return
|
||||
*/
|
||||
int process();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.zt.plat.module.system.service.sync;
|
||||
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Service
|
||||
public class SyncIWorkUserChangeServiceImpl implements SyncIWorkUserChangeService {
|
||||
|
||||
@Resource
|
||||
private IWorkSyncService iWorkSyncService;
|
||||
|
||||
@Override
|
||||
public int process() {
|
||||
IWorkFullSyncReqVO reqVO = new IWorkFullSyncReqVO();
|
||||
reqVO.setPageSize(10);
|
||||
// 设置修改日期的查询条件为当日0时起
|
||||
ZoneId zone = ZoneId.of("Asia/Shanghai");
|
||||
String startOfToday = LocalDate.now(zone)
|
||||
.atStartOfDay(zone)
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
reqVO.setModified(startOfToday);
|
||||
IWorkFullSyncRespVO respVO = iWorkSyncService.fullSyncUsers(reqVO);
|
||||
if(respVO!=null && respVO.getBatches()!=null) {
|
||||
return respVO.getBatches().stream()
|
||||
.mapToInt(b -> b.getPulled() == null ? 0 : b.getPulled())
|
||||
.sum();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,13 @@ public interface UserDeptService {
|
||||
*/
|
||||
void deleteUserDeptByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户ID与部门ID集合删除用户与部门关系
|
||||
* @param userId 用户ID
|
||||
* @param deptIds 部门ID集合
|
||||
*/
|
||||
void deleteUserDeptByUserIdAndDeptIds(Long userId, Collection<Long> deptIds);
|
||||
|
||||
/**
|
||||
* 批量创建用户与部门关系
|
||||
* @param createReqVOList 创建信息列表
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.zt.plat.module.system.service.userdept;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.framework.security.core.LoginUser;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
|
||||
import com.zt.plat.module.system.dal.mysql.userdept.UserDeptMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -128,10 +129,20 @@ public class UserDeptServiceImpl implements UserDeptService {
|
||||
@Override
|
||||
public void deleteUserDeptByUserId(Long userId) {
|
||||
if (userId == null) return;
|
||||
userDeptMapper.delete(new com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX<UserDeptDO>()
|
||||
userDeptMapper.delete(new LambdaQueryWrapperX<UserDeptDO>()
|
||||
.eq(UserDeptDO::getUserId, userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserDeptByUserIdAndDeptIds(Long userId, Collection<Long> deptIds) {
|
||||
if (userId == null || CollUtil.isEmpty(deptIds)) {
|
||||
return;
|
||||
}
|
||||
userDeptMapper.delete(new LambdaQueryWrapperX<UserDeptDO>()
|
||||
.eq(UserDeptDO::getUserId, userId)
|
||||
.in(UserDeptDO::getDeptId, deptIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void batchCreateUserDept(List<UserDeptDO> createReqVOList) {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCallbackReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
|
||||
import com.zt.plat.module.system.dal.mysql.iwork.IWorkSealLogMapper;
|
||||
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
|
||||
import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackProducer;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class IWorkCallbackLogServiceImplTest {
|
||||
|
||||
private IWorkSealLogMapper mapper;
|
||||
private IWorkBizCallbackProducer producer;
|
||||
private IWorkProperties properties;
|
||||
private IWorkCallbackLogServiceImpl service;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
mapper = mock(IWorkSealLogMapper.class);
|
||||
producer = mock(IWorkBizCallbackProducer.class);
|
||||
properties = new IWorkProperties();
|
||||
service = new IWorkCallbackLogServiceImpl(mapper, producer, properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void upsertOnCallback_shouldTruncateRaw() {
|
||||
String longRaw = "x".repeat(2100);
|
||||
IWorkWorkflowCallbackReqVO req = new IWorkWorkflowCallbackReqVO();
|
||||
req.setRequestId("REQ-1");
|
||||
req.setBizCallbackKey("key");
|
||||
|
||||
ArgumentCaptor<IWorkSealLogDO> captor = ArgumentCaptor.forClass(IWorkSealLogDO.class);
|
||||
when(mapper.selectByRequestId("REQ-1")).thenReturn(null);
|
||||
|
||||
service.upsertOnCallback(req, 3, longRaw);
|
||||
|
||||
verify(mapper).insert(captor.capture());
|
||||
IWorkSealLogDO saved = captor.getValue();
|
||||
assertThat(saved.getRawCallback()).hasSize(2000);
|
||||
assertThat(saved.getMaxRetry()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void incrementRetry_shouldIncreaseCount() {
|
||||
IWorkSealLogDO existing = new IWorkSealLogDO();
|
||||
existing.setId(1L);
|
||||
existing.setRequestId("REQ-2");
|
||||
existing.setRetryCount(1);
|
||||
when(mapper.selectByRequestId("REQ-2")).thenReturn(existing);
|
||||
|
||||
service.incrementRetry("REQ-2");
|
||||
|
||||
ArgumentCaptor<IWorkSealLogDO> captor = ArgumentCaptor.forClass(IWorkSealLogDO.class);
|
||||
verify(mapper).updateById(captor.capture());
|
||||
assertThat(captor.getValue().getRetryCount()).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user