diff --git a/blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java b/blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java index a3e1cfa..e6cda7e 100644 --- a/blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java +++ b/blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java @@ -72,4 +72,35 @@ public interface CommonConstant { */ Integer API_SCOPE_CATEGORY = 2; + + /** + * 是 2 + */ + Integer YES = 2; + /** + * 不是 1 + */ + Integer NO = 1; + /** + * 系统名称 + */ + String SYSTEM_CODE = "CK-ERP"; + /** + * 默认密码参数值 + */ + String DEFAULT_PARAM_PASSWORD = "account.initPassword"; + /** + * 默认角色参数值 + */ + String DEFAULT_PARAM_ROLE = "account.initRole"; + /** + * 默认角色 + */ + String DEFAULT_ROLE = "default"; + /** + * 同步的子公司名称根公司的值 + */ + String DICT_KEY_SYNC_ROOT_COMPANY_NAME = "root_company"; + + } diff --git a/blade-common/src/main/java/org/springblade/common/constant/DataStatusEnum.java b/blade-common/src/main/java/org/springblade/common/constant/DataStatusEnum.java new file mode 100644 index 0000000..f7c8adb --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/constant/DataStatusEnum.java @@ -0,0 +1,69 @@ +package org.springblade.common.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据状态枚举 + * + * @author vic + * @date 2024/8/26 6:45 PM + * @description DataStatusEnum + */ +@Getter +@AllArgsConstructor +public enum DataStatusEnum { + + /** + * + */ + ENABLE(1, "启用"), + + DISABLE(2, "禁用"), + + + ; + + + private final Integer code; + + private final String name; + + + /** + * 匹配枚举值 + * + * @param code + * @return + */ + public static boolean match(Integer code) { + for (DataStatusEnum state : values()) { + if (state.code.equals(code)) { + return true; + } + } + return false; + } + + public static String getNameStr(Integer code) { + for (DataStatusEnum state : values()) { + if (state.code.equals(code)) { + return state.name; + } + } + return null; + + } + + + /** + * 是否可以编辑修改表单 .没有审批业务的使用此方法, 系统设计的是反的 + * true:可编辑 false:不可编辑 + * @param value + * @return + */ + public static boolean canEdit(Integer value) { + return DISABLE.getCode().equals(value); + } + +} diff --git a/blade-common/src/main/java/org/springblade/common/constant/DictTypeEnum.java b/blade-common/src/main/java/org/springblade/common/constant/DictTypeEnum.java new file mode 100644 index 0000000..78dd418 --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/constant/DictTypeEnum.java @@ -0,0 +1,171 @@ +package org.springblade.common.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 系统字典类型枚举 + * 响功能和流程的字典 + * @author vic + * @date 2024/9/9 8:56 PM + * @description DictTypeEnum + */ +@Getter +@AllArgsConstructor +public enum DictTypeEnum { + /** + * + */ + DATA_STATUS("data_status", "数据状态"), + APPROVE_STATUS("approve_state", "通用审批状态"), + COOPERATE_STATUS("cooperate_status", "合作状态"), + CUSTOMER_TYPE("customer_type", "客户类型"), + DELIVERER_TYPE("deliverer_type", "投送人类型"), + ENTERPRISE_TYPE("enterprise_type", "企业类型"), + SETTLEMENT_TYPE("settlement_type", "结算中心类型"), + CUSTOMER_COMPANY_TYPE("company_type","客户管理的企业类型"), + PARTNER_TYPE("partner_type","合作伙伴类型"), + SYSTEM_YES_NO("yes_no","系统是否"), + PLATFORM_TYPE("settlement_type","平台类型/结算中心类型"), + TAXPAYER_TYPE("taxpayer_type","纳税人分类"), + //部门简称 + DEPARTMENT_SHORT_NAME("department_short_name","部门简称"), + PROCESS_TYPE("process_type","流程类型"), + + //评级维度 + RATING_DIMENSION("rating_dimension","评级维度"), + // oa公司部门同步 + SYNC_COMPANY_NAME("sync_company_name","同步公司名称"), + SYNC_ROOT_COMPANY_DEPT_NAME("sync_root_company_dept_name","同步根公司下部门名称"), + DEPARTMENT_CODE("department_code","部门编号"), + + //公式管理 + FORMULA_TYPE("formula_type","公式类型"), + + //财务管理 + FINANCIAL_TYPE("financial_type","财务类型"), + SAP_TYPE("sap_type","财务结算类型"), + SETTLEMENT_CLASSIFICATION("settlement_classification","财务结算分类"),//内部结算、外部结算 + SAP_DATA_STATUS("sap_data_status","财务结算单状态"), + SETTLEMENT_VOUCHER_TYPE("settlement_voucher_type","结算凭证类型"), + SETTLEMENT_NEXT_STATUS("settlement_next_status","下一步结算状态"), + + PAYMENT_TYPE("payment_type","财务付款分类"), + PAYMENT_DATA_STATUS("payment_data_status","财务付款单状态"), + PAYEMENT_CLASSIFICATION("payment_classification","财务付款分类"), //内部付款、外部付款 + PAYMENT_METHOD_TYPE("payment_method_type","财务付款支付方式"), + + SAP_VERIFICATION_METHOD("verification_method","验证方式"), + + /** 结算管理列表页的开票状态、付款状态 **/ + SETT_PAYMENT_STATUS("payment_status","付款状态"), + SETT_INVOICE_STATUS("invoice_status","开票状态"), + + + // 车辆运输凭证 + CERTIFICATE_BATCH_STATUS("certificate_batch_status","车辆运输凭证批次状态"), + CERTIFICATE_IDENTIFICATION_STATUS("certificate_identification_status","车辆运输凭证识别状态"), + CERTIFICATE_IDENTIFICATION_RESULT("certificate_identification_result","车辆运输凭证识别结果"), + + // 文件任务状态 + FILE_TASK_STATUS("file_task_status", "文件任务状态"), + // 运单管理-差异项 总净重 + WEIGHT_STATUS("weight_status", "总净重"), + // 运单管理-比对结果 差异类型 + DIFFERENCE_TYPE("difference_type", "差异类型"), + // 收款 + CLAIM_STATUS("claim_status", "认领状态"), + RECEIPT_SOURCE("receipt_source", "收款来源"), + CURRENCY("currency", "币种"), + + TRAJECTORY_STATUS("trajectory_status", "轨迹状态"), + + HOME_BASE_TYPE("base_type","基地类型"), + + FEE_NAME_TYPE("fee_name_type","费用类型"), + + TAX_CODE("taxCode","税收编码"), + INPUT_INVOICE_STATUS("input_invoice_status","录入开票状态"), + + /** + * 城矿结算公司财务负责人 + */ + SETTLEMENT_FINANCE_CONTROLLER("settlement_finance_controller", "城矿结算公司财务负责人"), + + /** + * 付款单据类型 + */ + PAYMENT_DOCUMENT_TYPE("payment_document_type", "付款单据类型"), + + /** + * 归档状态 + */ + ARCHIVE_STATUS("archive_status", "归档状态"), + + /** + * 预警类型 + */ + WARNING_TYPE("warning_type", "预警类型"), + + /** + * 消息状态 + */ + MESSAGE_STATUS("message_status", "消息状态"), + + /** + * 通知类型 + */ + NOTICE_TYPE("notice_type", "通知类型"), + + /** + * 通知对象类型 + */ + NOTICE_OBJECT_TYPE("notice_object_type", "通知对象类型"), + + /** + * 项目磅单校验类型 + */ + PROJECT_POUND_VALID_TYPE("project_pound_valid_type", "项目磅单校验类型"), + + /** + * 推送单据类型 + */ + PUSH_DOC_TYPE("push_doc_type", "推送单据类型"), + + /** + * mk流程状态 + */ + MK_STATUS("mk_status", "mk流程状态"), + + ; + + final String type; + + final String text; + + + /** + * 匹配枚举值 + * + * @param type + * @return + */ + public static boolean match(String type) { + for (DictTypeEnum state : values()) { + if (state.type.equals(type)) { + return true; + } + } + return false; + } + + public static String getNameStr(String type) { + for (DictTypeEnum state : values()) { + if (state.type.equals(type)) { + return state.text; + } + } + return null; + } + +} diff --git a/blade-gateway/src/main/resources/application.yml b/blade-gateway/src/main/resources/application.yml index d0c3371..bbcb4bd 100644 --- a/blade-gateway/src/main/resources/application.yml +++ b/blade-gateway/src/main/resources/application.yml @@ -14,3 +14,6 @@ knife4j: - blade-admin - blade-gateway - blade-log + +server: + port: 28080 diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/DataSyncRecord.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/DataSyncRecord.java new file mode 100644 index 0000000..6c8de03 --- /dev/null +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/DataSyncRecord.java @@ -0,0 +1,88 @@ +package org.springblade.system.pojo.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Date; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.experimental.Accessors; +import org.springblade.core.tool.utils.DateUtil; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.io.Serial; + +/** + * 数据同步记录表 实体类 + * + * @author BladeX + * @since 2024-09-27 + */ +@Accessors(chain = true) +@Data +@TableName("blade_data_sync_record") +@Schema(description = "DataSyncRecord对象") +public class DataSyncRecord implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @JsonSerialize(using = ToStringSerializer.class) + @Schema(description = "主键") + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 系统编码 + */ + @Schema(description = "系统编码") + private String systemCode; + /** + * 数据类型 + */ + @Schema(description = "数据类型") + private String dataType; + /** + * 模式 + */ + @Schema(description = "模式") + private String mode; + /** + * 结果 + */ + @Schema(description = "结果") + private String result; + /** + * 同步时间 + */ + @Schema(description = "同步时间") + private Date syncTime; + /** + * 租户ID + */ + @Schema(description = "租户ID") + @TableField(fill = FieldFill.INSERT) + private String tenantId; + /** + * 创建时间 + */ + @DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME) + @JsonFormat(pattern = DateUtil.PATTERN_TIME_MINI) + @Schema(description = "创建时间", hidden = true) + @TableField(fill = FieldFill.INSERT) + private Date createTime; + /** + * 状态[0:未删除,1:删除] + */ + @TableLogic + @Schema(description = "是否已删除", hidden = true) + @TableField(fill = FieldFill.INSERT) + private Integer isDeleted; +} diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java index 32da844..53002eb 100644 --- a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/Dept.java @@ -33,9 +33,11 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import org.springblade.core.tenant.mp.TenantEntity; import java.io.Serial; import java.io.Serializable; +import java.util.Date; /** * 实体类 @@ -45,7 +47,7 @@ import java.io.Serializable; @Data @TableName("blade_dept") @Schema(description = "Dept对象") -public class Dept implements Serializable { +public class Dept extends TenantEntity { @Serial private static final long serialVersionUID = 1L; @@ -127,4 +129,30 @@ public class Dept implements Serializable { private Integer isDeleted; + /** + * 部门编码 + */ + @Schema(description = "部门编码") + private String deptCode; + /** + * 父部门编码 + */ + @Schema(description = "父部门编码") + private String parentCode; + /** + * 同步时间 + */ + @Schema(description = "同步时间") + private Date syncTime; + /** + * 所属公司编码 + */ + @Schema(description = "所属公司编码") + private String belongCompanyCode; + /** + * 是否是oa的部门 + */ + @Schema(description = "是否是oa的部门") + private Integer isOa; + } diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/DataSync.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/DataSync.java new file mode 100644 index 0000000..2ea2bfa --- /dev/null +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/DataSync.java @@ -0,0 +1,69 @@ +package org.springblade.system.pojo.enums; + +/** + * 数据同步枚举类 + * @author bfhuange + * @date 2024/9/27 + */ +public class DataSync { + + /** + * 系统编码 + */ + public enum SystemCode { + /** + * oa 系统 + */ + OA, + /** + * mk 系统 + */ + MK, + ; + } + + /** + * 数据类型 + */ + public enum DataType { + /** + * 组织,包含公司和部门 + */ + ORG, + /** + * 人员 + */ + PERSON, + ; + } + + /** + * 模式 + */ + public enum Mode { + /** + * 获取 + */ + FETCH, + /** + * 推送 + */ + PUSH, + ; + } + + /** + * 结果 + */ + public enum Result { + /** + * 成功 + */ + SUCCESS, + /** + * 失败 + */ + FAIL, + ; + } +} diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/DeptCategory.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/DeptCategory.java new file mode 100644 index 0000000..cf21c31 --- /dev/null +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/DeptCategory.java @@ -0,0 +1,32 @@ +package org.springblade.system.pojo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 部门类型枚举 + * @author bfhuange + * @date 2024/8/26 + */ +@AllArgsConstructor +@Getter +public enum DeptCategory { + /** + * 公司 + */ + COMPANY(1, "公司"), + /** + * 部门 + */ + DEPT(2, "部门"), + ; + + /** + * 部门类型编码 + */ + private final Integer code; + /** + * 部门类型名称 + */ + private final String name; +} diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/UserSex.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/UserSex.java new file mode 100644 index 0000000..3b37571 --- /dev/null +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/UserSex.java @@ -0,0 +1,43 @@ +package org.springblade.system.pojo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.stream.Stream; + +/** + * 用户性别枚举 + * @author bfhuange + * @date 2024/8/27 + */ +@Getter +@AllArgsConstructor +public enum UserSex { + /** + * 男 + */ + MAN(1, "男"), + /** + * 女 + */ + WOMAN(2, "女"), + ; + /** + * 编码 + */ + private final Integer code; + /** + * 名称 + */ + private final String name; + + public static Integer getCodeByName(String name) { + if (name == null || name.isEmpty()) { + return null; + } + return Stream.of(values()) + .filter(e -> e.getName().equals(name)) + .map(UserSex::getCode) + .findFirst().orElse(null); + } +} diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/DeptListVO.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/DeptListVO.java new file mode 100644 index 0000000..9346dd4 --- /dev/null +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/DeptListVO.java @@ -0,0 +1,52 @@ +package org.springblade.system.pojo.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @date 2024/8/27 + */ +@Data +@Schema(description = "DeptListVO") +public class DeptListVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 主键id + */ + @JsonSerialize(using = ToStringSerializer.class) + @Schema(description = "主键id") + private Long id; + /** + * 部门编码 + */ + @Schema(description = "部门编码") + private String deptCode; + /** + * 机构名 + */ + @Schema(description = "机构名") + private String deptName; + /** + * 父主键 + */ + @JsonSerialize(using = ToStringSerializer.class) + @Schema(description = "父主键") + private Long parentId; + /** + * 父部门编码 + */ + @Schema(description = "父部门编码") + private String parentCode; + /** + * 部门类型 + */ + @Schema(description = "部门类型") + private Integer deptCategory; +} diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/UserDeptIdsVO.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/UserDeptIdsVO.java new file mode 100644 index 0000000..9486204 --- /dev/null +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/UserDeptIdsVO.java @@ -0,0 +1,37 @@ +package org.springblade.system.pojo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户部门id + * @author bfhuange + * @date 2024/9/12 + */ +@Data +@Schema(description = "UserDeptIdsVO对象") +public class UserDeptIdsVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 部门id,多个用逗号拼接 + */ + @Schema(description = "部门id,多个用逗号拼接") + private String deptIds; + + /** + * 部门编码,多个用逗号拼接 + */ + @Schema(description = "部门编码,多个用逗号拼接") + private String deptCodes; +} diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/util/DataSyncRecordUtils.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/util/DataSyncRecordUtils.java new file mode 100644 index 0000000..aca6cea --- /dev/null +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/util/DataSyncRecordUtils.java @@ -0,0 +1,83 @@ +package org.springblade.system.util; + +import org.springblade.system.pojo.entity.DataSyncRecord; +import org.springblade.system.pojo.enums.DataSync; + +/** + * 数据同步记录工具类 + * @author bfhuange + * @date 2024/9/27 + */ +public class DataSyncRecordUtils { + private DataSyncRecordUtils() {} + + /** + * 创建获取oa组织的数据同步记录 + * @return + */ + public static DataSyncRecord createOAOrgFetch() { + return DataSyncRecordUtils.createOAFetch() + // 数据类型是组织 + .setDataType(DataSync.DataType.ORG.name()); + } + + /** + * 创建获取oa人员的数据同步记录 + * @return + */ + public static DataSyncRecord createOAPersonFetch() { + return DataSyncRecordUtils.createOAFetch() + // 数据类型是人员 + .setDataType(DataSync.DataType.PERSON.name()); + } + + /** + * 创建推送mk组织的数据同步记录 + * @return + */ + public static DataSyncRecord createMKOrgPush() { + return DataSyncRecordUtils.createMKPush() + // 数据类型是组织 + .setDataType(DataSync.DataType.ORG.name()); + } + + /** + * 创建推送mk人员的数据同步记录 + * @return + */ + public static DataSyncRecord createMKPersonPush() { + return DataSyncRecordUtils.createMKPush() + // 数据类型是人员 + .setDataType(DataSync.DataType.PERSON.name()); + } + + /** + * 创建oa 获取数据记录 + * + * @return + */ + private static DataSyncRecord createOAFetch() { + return new DataSyncRecord() + // oa 系统 + .setSystemCode(DataSync.SystemCode.OA.name()) + // 获取数据 + .setMode(DataSync.Mode.FETCH.name()) + // 默认结果是成功 + .setResult(DataSync.Result.SUCCESS.name()); + } + + /** + * 创建mk 推送数据记录 + * + * @return + */ + private static DataSyncRecord createMKPush() { + return new DataSyncRecord() + // mk 系统 + .setSystemCode(DataSync.SystemCode.MK.name()) + // 推送数据 + .setMode(DataSync.Mode.PUSH.name()) + // 默认结果是成功 + .setResult(DataSync.Result.SUCCESS.name()); + } +} diff --git a/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/context/IPhone.java b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/context/IPhone.java new file mode 100644 index 0000000..7e3f6bd --- /dev/null +++ b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/context/IPhone.java @@ -0,0 +1,19 @@ +package org.springblade.system.pojo.context; + +/** + * 获取设置手机号接口 + * @author bfhuange + * @since 2026/4/1 + */ +public interface IPhone { + /** + * 设置手机号 + * @return + */ + void setPhone(String phone); + /** + * 获取手机号 + * @return + */ + String getPhone(); +} diff --git a/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/User.java b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/User.java index 9da8101..63ee3cc 100644 --- a/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/User.java +++ b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/User.java @@ -31,6 +31,7 @@ import lombok.EqualsAndHashCode; import org.springblade.core.tenant.mp.TenantEntity; import org.springblade.core.tool.jackson.Sensitive; import org.springblade.core.tool.sensitive.SensitiveType; +import org.springblade.system.pojo.context.IPhone; import java.io.Serial; import java.util.Date; @@ -43,7 +44,7 @@ import java.util.Date; @Data @TableName("blade_user") @EqualsAndHashCode(callSuper = true) -public class User extends TenantEntity { +public class User extends TenantEntity implements IPhone { @Serial private static final long serialVersionUID = 1L; @@ -116,4 +117,22 @@ public class User extends TenantEntity { private Integer isLeader; + /** + * 部门编码 + */ + private String deptCodes; + /** + * 同步时间 + */ + private Date syncTime; + /** + * 是否是oa的账号 + */ + private Integer isOa; + /** + * 是否推送mk + */ + private Integer isPushMk; + + } diff --git a/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/UserDept.java b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/UserDept.java index 3ccc986..ff76ad7 100644 --- a/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/UserDept.java +++ b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/entity/UserDept.java @@ -36,6 +36,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; +import java.util.Date; /** * 实体类 @@ -85,4 +86,45 @@ public class UserDept implements Serializable { @Schema(description = "是否已删除") private Integer isDeleted; + + /** + * 公司编码 + */ + @Schema(description = "公司编码") + private String companyCode; + /** + * 公司名 + */ + @Schema(description = "公司名") + private String companyName; + /** + * 部门编码 + */ + @Schema(description = "部门编码") + private String deptCode; + /** + * 部门名 + */ + @Schema(description = "部门名") + private String deptName; + /** + * 排序 + */ + @Schema(description = "排序") + private Integer sort; + /** + * oaid + */ + @Schema(description = "oaid") + private String oaId; + /** + * oa的loginId + */ + @Schema(description = "oa的loginId") + private String oaLoginId; + /** + * 同步时间 + */ + @Schema(description = "同步时间") + private Date syncTime; } diff --git a/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/vo/UserListVO.java b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/vo/UserListVO.java new file mode 100644 index 0000000..680eeda --- /dev/null +++ b/blade-service-api/blade-user-api/src/main/java/org/springblade/system/pojo/vo/UserListVO.java @@ -0,0 +1,38 @@ +package org.springblade.system.pojo.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @date 2024/8/28 + */ +@Data +@Schema(description = "UserListVO对象") +public class UserListVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + /** + * 用户编号 + */ + private String code; + /** + * 昵称 + */ + private String name; + /** + * 手机 + */ + private String phone; +} diff --git a/blade-service/blade-system/pom.xml b/blade-service/blade-system/pom.xml index 12b0c16..87f4b33 100644 --- a/blade-service/blade-system/pom.xml +++ b/blade-service/blade-system/pom.xml @@ -38,6 +38,33 @@ org.springblade blade-user-api + + + org.springblade + blade-oa-api + + + org.springblade + blade-mk-api + + + + org.mapstruct + mapstruct + + + + org.springblade + blade-starter-powerjob + + + + + org.springblade + blade-core-oauth2 + + + diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/MKPushController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/MKPushController.java new file mode 100644 index 0000000..42fe7ae --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/MKPushController.java @@ -0,0 +1,50 @@ +package org.springblade.system.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springblade.core.secure.annotation.PreAuth; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.constant.RoleConstant; +import org.springblade.system.service.IMKPushService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * mk数据推送 + * @author bfhuange + * @date 2024/9/24 + */ +@Tag(name = "mk数据推送", description = "mk数据推送") +@RequestMapping("/mk") +@RestController +@RequiredArgsConstructor +public class MKPushController { + + private final IMKPushService mkPushService; + + /** + * 推送公司和部门 + * @return + */ + @Operation(summary = "推送公司和部门") + @PostMapping("/pushCompanyAndDept") + @PreAuth(RoleConstant.HAS_ROLE_ADMIN) + public R pushCompanyAndDept() { + mkPushService.pushOrgAndRecord(null); + return R.data(true); + } + + /** + * 推送人员 + * @return + */ + @Operation(summary = "推送人员") + @PostMapping("/pushPerson") + @PreAuth(RoleConstant.HAS_ROLE_ADMIN) + public R pushPerson() { + mkPushService.pushPersonAndRecord(null); + return R.data(true); + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/OAController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/OAController.java new file mode 100644 index 0000000..c862504 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/OAController.java @@ -0,0 +1,78 @@ +package org.springblade.system.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springblade.core.log.annotation.ApiLog; +import org.springblade.core.secure.annotation.PreAuth; +import org.springblade.core.tool.api.R; +import org.springblade.system.service.IOASyncService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * oa数据同步 + * @author bfhuange + * @date 2024/8/23 + */ +@Tag(name = "oa数据同步", description = "oa数据同步") +@RequestMapping("/oa") +@RestController +@RequiredArgsConstructor +public class OAController { + + private final IOASyncService oaService; + + /** + * 增量同步公司和部门并推送MK + * @return + */ + @ApiLog("系统管理-机构管理-增量同步") + @Operation(summary = "增量同步公司和部门并推送MK") + @PostMapping("/syncCompanyAndDeptAndPushMK") + @PreAuth("hasPermission('oasync:org:inc')") + public R syncCompanyAndDeptAndPushMK() { + oaService.syncCompanyAndDeptAndPushMK(false); + return R.data(true); + } + + /** + * 全量同步公司和部门并推送MK + * @return + */ + @ApiLog("系统管理-机构管理-全量同步") + @Operation(summary = "全量同步公司和部门并推送MK") + @PostMapping("/syncAllCompanyAndDeptAndPushMK") + @PreAuth("hasPermission('oasync:org:all')") + public R syncAllCompanyAndDeptAndPushMK() { + oaService.syncCompanyAndDeptAndPushMK(true); + return R.data(true); + } + + /** + * 增量同步人员并推送MK + * @return + */ + @ApiLog("系统管理-用户管理-增量同步") + @Operation(summary = "增量同步人员并推送MK") + @PostMapping("/syncPersonAndPushMK") + @PreAuth("hasPermission('oasync:person:inc')") + public R syncPersonAndPushMK() { + oaService.syncPersonAndPushMK(false); + return R.data(true); + } + + /** + * 全量同步人员并推送MK + * @return + */ + @ApiLog("系统管理-用户管理-全量同步") + @Operation(summary = "全量同步人员并推送MK") + @PostMapping("/syncAllPersonAndPushMK") + @PreAuth("hasPermission('oasync:person:all')") + public R syncAllPersonAndPushMK() { + oaService.syncPersonAndPushMK(true); + return R.data(true); + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/convert/DeptConvert.java b/blade-service/blade-system/src/main/java/org/springblade/system/convert/DeptConvert.java new file mode 100644 index 0000000..3199b7e --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/convert/DeptConvert.java @@ -0,0 +1,142 @@ +package org.springblade.system.convert; + +import org.mapstruct.*; +import org.springblade.common.constant.CommonConstant; +import org.springblade.common.constant.DataStatusEnum; +import org.springblade.system.pojo.entity.Dept; +import org.springblade.system.pojo.enums.DeptCategory; +import org.springblade.system.pojo.vo.DeptListVO; +import org.springblade.thirdparty.mk.constant.MKConstant; +import org.springblade.thirdparty.mk.pojo.dto.MKPushOrgDeptDTO; +import org.springblade.thirdparty.oa.constant.OAConstant; +import org.springblade.thirdparty.oa.constant.OAConvertConstant; +import org.springblade.thirdparty.oa.pojo.response.OACompanyResponse; +import org.springblade.thirdparty.oa.pojo.response.OADepartmentResponse; +import org.springblade.thirdparty.oa.util.OAUtils; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * oa公司、部门到本系统部门转换工具 + * @author bfhuange + * @date 2024/8/26 + */ +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DeptConvert { + + DeptListVO entity2vo(Dept entity); + + List entities2vos(List entities); + + @Mapping(source = "deptCode", target = "fdId") + @Mapping(source = "deptName", target = "fdName") + @Mapping(source = "deptCode", target = "fdNo") + @Mapping(source = "sort", target = "fdOrder") + @Mapping(source = "status", target = "fdIsAvailable", qualifiedByName = "fdIsAvailable") + @Mapping(source = "deptCategory", target = "fdOrgType", qualifiedByName = "fdOrgType") + @Mapping(source = "deptCategory", target = "fdSourceSystem", qualifiedByName = "fdSourceSystem") + @Mapping(source = "parentCode", target = "fdParent") + MKPushOrgDeptDTO entity2dto(Dept entity); + + List entities2dtos(List entities); + + @Mapping(source = "subcompanyname", target = "deptName") + @Mapping(source = "subcompanydesc", target = "fullName") + // 父公司id转为父部门编码,要拼接前缀 + @Mapping(source = "supsubcomid", target = "parentCode") + @Mapping(source = "created", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss") + @Mapping(source = "modified", target = "updateTime", dateFormat = "yyyy-MM-dd HH:mm:ss") + Dept baseConvert(OACompanyResponse company); + + @Mapping(source = "departmentname", target = "deptName") + @Mapping(source = "departmentmark", target = "fullName") + // 父部门id转为父部门编码,要拼接前缀 + @Mapping(source = "supdepid", target = "parentCode") + // 所属子公司id转为所属公司编码,要拼接前缀 + @Mapping(source = "subcompanyid1", target = "belongCompanyCode") + @Mapping(source = "created", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss") + @Mapping(source = "modified", target = "updateTime", dateFormat = "yyyy-MM-dd HH:mm:ss") + Dept baseConvert(OADepartmentResponse department); + + /** + * oa公司转本系统部门 + * @param company + * @return + */ + default Dept company2dept(OACompanyResponse company) { + Dept dept = this.baseConvert(company); + // 部门编码添加前缀 + dept.setDeptCode(OAConvertConstant.COMPANY_OA_PREFIX + company.getId()); + String parentCode = dept.getParentCode(); + if (!OAConstant.ROOT_COMPANY_ID.equals(parentCode)) { + // 父部门编码不是0,拼接前缀 + dept.setParentCode(OAConvertConstant.COMPANY_OA_PREFIX + parentCode); + // 父部门id后续处理 + } else { + // 父部门编码是0,父id也设置为0 + dept.setParentId(OAConvertConstant.ROOT_PARENT_ID); + } + // 公司所属公司编码就是他自己的部门编码 + dept.setBelongCompanyCode(dept.getDeptCode()); + // 显示顺序处理 + dept.setSort(OAUtils.parseInt(company.getShoworder())); + // 部门类型,公司 + dept.setDeptCategory(DeptCategory.COMPANY.getCode()); + dept.setSyncTime(new Date()); + // 是oa的 + dept.setIsOa(CommonConstant.YES); + return dept; + } + + /** + * oa部门转本系统部门 + * @param oaDept + * @param companyDeptMap 所有公司的部门编码与id map + * @return + */ + default Dept dept2dept(OADepartmentResponse oaDept, Map companyDeptMap) { + Dept dept = this.baseConvert(oaDept); + // 部门编码添加前缀 + dept.setDeptCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + oaDept.getId()); + // 部门所属公司编码就是他所属公司编码加前缀 + dept.setBelongCompanyCode(OAConvertConstant.COMPANY_OA_PREFIX + dept.getBelongCompanyCode()); + String parentCode = dept.getParentCode(); + if (!OAConstant.ROOT_COMPANY_ID.equals(parentCode)) { + // 父部门编码不是0,拼接前缀 + dept.setParentCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + parentCode); + // 父部门id后续处理 + } else { + // 父部门编码是0,设置为公司编码 + dept.setParentCode(dept.getBelongCompanyCode()); + // 根据父部门编码获取对应的id + dept.setParentId(companyDeptMap.get(dept.getParentCode())); + } + + // 显示顺序处理 + dept.setSort(OAUtils.parseInt(oaDept.getShoworder())); + // 部门类型,部门 + dept.setDeptCategory(DeptCategory.DEPT.getCode()); + dept.setSyncTime(new Date()); + // 是oa的 + dept.setIsOa(CommonConstant.YES); + return dept; + } + + @Named("fdIsAvailable") + default Boolean fdIsAvailable(Integer status) { + return DataStatusEnum.ENABLE.getCode().equals(status); + } + + @Named("fdOrgType") + default String fdOrgType(Integer deptCategory) { + return DeptCategory.COMPANY.getCode().equals(deptCategory) ? MKConstant.ORG_TYPE_ORG : MKConstant.ORG_TYPE_DEPT; + } + + @Named("fdSourceSystem") + default String fdSourceSystem(Integer deptCategory) { + return CommonConstant.SYSTEM_CODE; + } + +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/convert/UserConvert.java b/blade-service/blade-system/src/main/java/org/springblade/system/convert/UserConvert.java new file mode 100644 index 0000000..924daee --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/convert/UserConvert.java @@ -0,0 +1,200 @@ +package org.springblade.system.convert; + +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.*; +import org.springblade.common.constant.CommonConstant; +import org.springblade.common.constant.DataStatusEnum; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.Func; +import org.springblade.system.pojo.entity.User; +import org.springblade.system.pojo.entity.UserDept; +import org.springblade.system.pojo.enums.UserSex; +import org.springblade.system.pojo.enums.UserType; +import org.springblade.system.pojo.vo.UserListVO; +import org.springblade.thirdparty.mk.constant.MKConstant; +import org.springblade.thirdparty.mk.pojo.dto.MKAuxiliaryDeptDTO; +import org.springblade.thirdparty.mk.pojo.dto.MKPushPersonDTO; +import org.springblade.thirdparty.oa.constant.OAConvertConstant; +import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse; +import org.springblade.thirdparty.oa.util.OAUtils; + +import java.util.*; + +/** + * @author bfhuange + * @date 2024/8/26 + */ +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface UserConvert { + + List entities2vos(List list); + + default List entities2dtos(List records, String defaultPassword) { + if (CollectionUtil.isEmpty(records)) { + return Collections.emptyList(); + } + return records.stream() + .map(user -> { + MKPushPersonDTO personDTO = entity2dto(user); + if (personDTO == null) { + return null; + } + if (CommonConstant.NO.equals(user.getIsPushMk())) { + // 未推送mk的才设置默认密码 + personDTO.setFdPassword(defaultPassword); + } + return personDTO; + }).toList(); + } + + @Mapping(source = "account", target = "fdId") + @Mapping(source = "account", target = "fdLoginName") + @Mapping(source = "name", target = "fdName") + @Mapping(source = "phone", target = "fdMobileNo") + @Mapping(source = "code", target = "fdNo") + @Mapping(source = "sex", target = "fdSex", qualifiedByName = "fdSex") + @Mapping(source = "status", target = "fdIsAvailable", qualifiedByName = "fdIsAvailable") + @Mapping(source = "account", target = "fdSourceSystem", qualifiedByName = "fdSourceSystem") + @Mapping(source = "deptCodes", target = "fdParent", qualifiedByName = "fdParent") + @Mapping(source = "deptCodes", target = "fdAuxiliaryDept", qualifiedByName = "fdAuxiliaryDept") + MKPushPersonDTO entity2dto(User entity); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "status", ignore = true) + @Mapping(target = "password", ignore = true) + @Mapping(target = "birthday", ignore = true) + @Mapping(target = "sex", ignore = true) + @Mapping(source = "workcode", target = "code") + @Mapping(source = "lastname", target = "name") + @Mapping(source = "lastname", target = "realName") + @Mapping(source = "mobile", target = "phone") + @Mapping(source = "mobile", target = "account") + @Mapping(source = "email", target = "email") + User baseConvert(OAPersonResponse person); + + @Mapping(source = "subcompanyname", target = "companyName") + @Mapping(source = "departmentname", target = "deptName") + @Mapping(source = "id", target = "oaId") + @Mapping(source = "loginid", target = "oaLoginId") + UserDept userDeptConvert(OAPersonResponse person); + + /** + * oa人员转本系统用户 + * @param person + * @param defaultPassword 默认密码 + * @return + */ + default User person2user(OAPersonResponse person, String defaultPassword) { + User user = baseConvert(person); + + // 密码 + user.setPassword(defaultPassword); + // 性别 + user.setSex(UserSex.getCodeByName(person.getSex())); + // 状态转换 + Integer status = getStatus(person); + user.setStatus(status); + // web平台 + user.setUserType(UserType.WEB.getCategory()); + user.setSyncTime(new Date()); + // 从oa同步的 + user.setIsOa(CommonConstant.YES); + return user; + } + + /** + * oa人员转本系统用户部门 + * @param person + * @param userMap 所有用户手机号与id map + * @return + */ + default UserDept person2userDept(OAPersonResponse person, Map userMap) { + UserDept userDept = userDeptConvert(person); + // 公司编码添加前缀 + userDept.setCompanyCode(OAConvertConstant.COMPANY_OA_PREFIX + person.getSubcompanyid1()); + // 部门编码添加前缀 + userDept.setDeptCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + person.getDepartmentid()); + // 状态转换 + Integer status = getStatus(person); + userDept.setStatus(status); + // 排序 + userDept.setSort(OAUtils.parseInt(person.getDsporder())); + // 用户id + userDept.setUserId(userMap.get(person.getMobile())); + userDept.setSyncTime(new Date()); + return userDept; + } + + /** + * 转换状态 + * @param person + * @return + */ + private Integer getStatus(OAPersonResponse person) { + // 过滤状态 0-试用 1-正式 2-临时 3-试用延期 4-解聘 5-离职 6-退休 7-无效 + return switch (person.getStatus()) { + case "0", "1", "2", "3" -> DataStatusEnum.ENABLE.getCode(); + default -> DataStatusEnum.DISABLE.getCode(); + }; + } + + @Named("fdIsAvailable") + default Boolean fdIsAvailable(Integer status) { + return DataStatusEnum.ENABLE.getCode().equals(status); + } + + @Named("fdSourceSystem") + default String fdSourceSystem(String account) { + return CommonConstant.SYSTEM_CODE; + } + + @Named("fdSex") + default String fdSex(Integer sex) { + if (sex == null) { + return null; + } + if (UserSex.MAN.getCode().equals(sex)) { + return MKConstant.SEX_MAN; + } + if (UserSex.WOMAN.getCode().equals(sex)) { + return MKConstant.SEX_WOMAN; + } + return MKConstant.SEX_UNKONW; + } + + @Named("fdParent") + default String fdParent(String deptCodes) { + if (StringUtils.isBlank(deptCodes)) { + return null; + } + // 第一个部门编码作为主部门id + return Func.toStrList(deptCodes).stream() + // 非外部组织编码 + .filter(deptCode -> !deptCode.startsWith(OAConvertConstant.COMPANY_EX_PREFIX) && !deptCode.startsWith(OAConvertConstant.DEPARTMENT_EX_PREFIX)) + .findFirst() + .orElse(null); + } + + @Named("fdAuxiliaryDept") + default List fdAuxiliaryDept(String deptCodes) { + if (StringUtils.isBlank(deptCodes)) { + return null; + } + List deptCodeList = Func.toStrList(deptCodes).stream() + // 非外部组织编码 + .filter(deptCode -> !deptCode.startsWith(OAConvertConstant.COMPANY_EX_PREFIX) && !deptCode.startsWith(OAConvertConstant.DEPARTMENT_EX_PREFIX)) + .toList(); + if (deptCodeList.size() <= 1) { + return null; + } + deptCodeList = new ArrayList<>(deptCodeList); + // 移除第一个部门编码,其他的作为辅部门 + deptCodeList.remove(0); + return deptCodeList.stream() + .map(deptCode -> { + MKAuxiliaryDeptDTO deptDTO = new MKAuxiliaryDeptDTO(); + deptDTO.setFdId(deptCode); + return deptDTO; + }).toList(); + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/log/ComposeLogUtil.java b/blade-service/blade-system/src/main/java/org/springblade/system/log/ComposeLogUtil.java new file mode 100644 index 0000000..92f9eb2 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/log/ComposeLogUtil.java @@ -0,0 +1,89 @@ +package org.springblade.system.log; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import tech.powerjob.worker.log.OmsLogger; + +import java.util.LinkedList; + +/** + * 日志工具类 + * @author bfhuange + * @date 2024/9/25 + */ +@Slf4j +public class ComposeLogUtil { + /** + * oms logger + */ + private static final ThreadLocal OMS_LOGGER_CONTEXT = new ThreadLocal<>(); + /** + * 组合 logger + */ + private static final ThreadLocal> CONTEXT = new ThreadLocal<>(); + /** + * 默认日志记录器 + */ + private static final ComposeLogger DEFAULT = new ComposeLogger(log, null); + private ComposeLogUtil() {} + + /** + * 设置oms logger + * @param omsLogger + */ + public static void setOmsLogger(OmsLogger omsLogger) { + OMS_LOGGER_CONTEXT.set(omsLogger); + } + + /** + * 获取oms logger + * @return + */ + public static OmsLogger getOmsLogger() { + return OMS_LOGGER_CONTEXT.get(); + } + + /** + * 移除oms logger + */ + public static void removeOmsLogger() { + OMS_LOGGER_CONTEXT.remove(); + } + + /** + * 添加日志记录器 + * @param log + */ + public static void addLog(Logger log) { + LinkedList list = CONTEXT.get(); + if (list == null) { + list = new LinkedList<>(); + CONTEXT.set(list); + } + ComposeLogger omsLog = new ComposeLogger(log, ComposeLogUtil.getOmsLogger()); + list.addLast(omsLog); + } + + /** + * 获取最后一个日志记录器 + * @return + */ + public static ComposeLogger getLastLog() { + LinkedList list = CONTEXT.get(); + if (list == null || list.isEmpty()) { + return DEFAULT; + } + return list.getLast(); + } + + /** + * 清除最后一个日志记录器 + */ + public static void removeLastLog() { + LinkedList list = CONTEXT.get(); + if (list == null || list.isEmpty()) { + return; + } + list.removeLast(); + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/log/ComposeLogger.java b/blade-service/blade-system/src/main/java/org/springblade/system/log/ComposeLogger.java new file mode 100644 index 0000000..b822e81 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/log/ComposeLogger.java @@ -0,0 +1,48 @@ +package org.springblade.system.log; + +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import tech.powerjob.worker.log.OmsLogger; + +/** + * 组合日志,兼容 powerjob 日志 + * @author bfhuange + * @date 2024/8/30 + */ +@RequiredArgsConstructor +public class ComposeLogger implements OmsLogger { + private final Logger log; + private final OmsLogger omsLog; + + @Override + public void debug(String s, Object... objects) { + log.debug(s, objects); + if (omsLog != null) { + omsLog.debug(s, objects); + } + } + + @Override + public void info(String s, Object... objects) { + log.info(s, objects); + if (omsLog != null) { + omsLog.info(s, objects); + } + } + + @Override + public void warn(String s, Object... objects) { + log.warn(s, objects); + if (omsLog != null) { + omsLog.warn(s, objects); + } + } + + @Override + public void error(String s, Object... objects) { + log.error(s, objects); + if (omsLog != null) { + omsLog.error(s, objects); + } + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DataSyncRecordMapper.java b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DataSyncRecordMapper.java new file mode 100644 index 0000000..ba8e43e --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DataSyncRecordMapper.java @@ -0,0 +1,13 @@ +package org.springblade.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.springblade.system.pojo.entity.DataSyncRecord; + +/** + * 数据同步记录表 Mapper 接口 + * + * @author BladeX + * @since 2024-09-27 + */ +public interface DataSyncRecordMapper extends BaseMapper { +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DataSyncRecordMapper.xml b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DataSyncRecordMapper.xml new file mode 100644 index 0000000..1293836 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/DataSyncRecordMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.java b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.java index 14e6eec..287c9e2 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.java @@ -26,7 +26,12 @@ package org.springblade.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; import org.springblade.system.pojo.entity.UserDept; +import org.springblade.system.pojo.vo.UserDeptIdsVO; + +import java.util.Collection; +import java.util.List; /** * Mapper 接口 @@ -35,5 +40,11 @@ import org.springblade.system.pojo.entity.UserDept; */ public interface UserDeptMapper extends BaseMapper { + /** + * 根据用户id列表查询对应的部门id列表拼接的字符串 + * @param userIds + * @return + */ + List queryUserDeptIds(@Param("userIds") Collection userIds); } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.xml b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.xml index 843e083..03182d8 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.xml +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/UserDeptMapper.xml @@ -2,4 +2,19 @@ + + diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IDataSyncRecordService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IDataSyncRecordService.java new file mode 100644 index 0000000..92b5f4c --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IDataSyncRecordService.java @@ -0,0 +1,26 @@ +package org.springblade.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.springblade.system.pojo.entity.DataSyncRecord; + +/** + * 数据同步记录表 服务类 + * + * @author BladeX + * @since 2024-09-27 + */ +public interface IDataSyncRecordService extends IService { + + /** + * 使用新事物提交 + * @param entity + */ + void saveWithNewTransaction(DataSyncRecord entity); + + /** + * 查询最后一次同步成功的记录 + * @param param + * @return + */ + DataSyncRecord getLastSuccessRecord(DataSyncRecord param); +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java index 022c6dc..84138bb 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IDeptService.java @@ -30,6 +30,7 @@ import org.springblade.system.pojo.entity.Dept; import org.springblade.system.pojo.vo.DeptVO; import org.springblade.system.pojo.vo.UserVO; +import java.util.Date; import java.util.List; import java.util.Map; @@ -157,4 +158,11 @@ public interface IDeptService extends IService { */ List deptLeaderInfo(Long deptId); + + /** + * 更新部门祖级列表 + * @param startTime 要更新的部门的同步时间或更新时间起始 + */ + void updateAncestors(Date startTime); + } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IMKPushService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IMKPushService.java new file mode 100644 index 0000000..cafb90a --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IMKPushService.java @@ -0,0 +1,24 @@ +package org.springblade.system.service; + +import java.util.Date; + +/** + * mk 推送接口 + * @author bfhuange + * @date 2024/9/24 + */ +public interface IMKPushService { + /** + * 推送所有公司和部门并记录 + * + * @param oaStartTime oa同步查询开始时间 + */ + void pushOrgAndRecord(Date oaStartTime); + + /** + * 推送人员并记录 + * + * @param oaStartTime oa同步查询开始时间 + */ + void pushPersonAndRecord(Date oaStartTime); +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IOASyncService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IOASyncService.java new file mode 100644 index 0000000..1c7c1d9 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IOASyncService.java @@ -0,0 +1,21 @@ +package org.springblade.system.service; + +/** + * oa同步接口 + * @author bfhuange + * @date 2024/8/23 + */ +public interface IOASyncService { + + /** + * 从oa同步公司和部门并且推送到mk + * @param syncAll 是否同步所有 + */ + void syncCompanyAndDeptAndPushMK(boolean syncAll); + + /** + * 从oa同步人员并且推送到mk + * @param syncAll 是否同步所有 + */ + void syncPersonAndPushMK(boolean syncAll); +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserDeptService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserDeptService.java index 87593ce..e3628cf 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserDeptService.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserDeptService.java @@ -28,6 +28,10 @@ package org.springblade.system.service; import com.baomidou.mybatisplus.extension.service.IService; import org.springblade.system.pojo.entity.UserDept; +import org.springblade.system.pojo.vo.UserDeptIdsVO; + +import java.util.Collection; +import java.util.List; /** * 服务类 @@ -36,4 +40,11 @@ import org.springblade.system.pojo.entity.UserDept; */ public interface IUserDeptService extends IService { + /** + * 根据用户id列表查询对应的部门id列表拼接的字符串 + * @param userIds + * @return + */ + List queryUserDeptIds(Collection userIds); + } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserService.java index d1db254..b749959 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserService.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IUserService.java @@ -30,6 +30,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import org.springblade.core.mp.base.BaseService; import org.springblade.core.mp.support.Query; +import org.springblade.system.pojo.context.IPhone; import org.springblade.system.pojo.entity.User; import org.springblade.system.pojo.entity.UserInfo; import org.springblade.system.pojo.entity.UserOauth; @@ -336,4 +337,18 @@ public interface IUserService extends BaseService { * @return 用户分页(UserVO 包含角色、部门、岗位名称) */ IPage userPage(IPage page, String account, String name); + + + /** + * 解密手机号 + * @param user + */ + void decryptPhone(IPhone user); + + /** + * 加密手机号 + * @param user + */ + void encryptPhone(IPhone user); + } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DataSyncRecordServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DataSyncRecordServiceImpl.java new file mode 100644 index 0000000..48ae208 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DataSyncRecordServiceImpl.java @@ -0,0 +1,42 @@ +package org.springblade.system.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springblade.system.mapper.DataSyncRecordMapper; +import org.springblade.system.pojo.entity.DataSyncRecord; +import org.springblade.system.pojo.enums.DataSync; +import org.springblade.system.service.IDataSyncRecordService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +/** + * 数据同步记录表 服务实现类 + * + * @author BladeX + * @since 2024-09-27 + */ +@Service +public class DataSyncRecordServiceImpl extends ServiceImpl implements IDataSyncRecordService { + + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) + @Override + public void saveWithNewTransaction(DataSyncRecord entity) { + this.save(entity); + } + + @Override + public DataSyncRecord getLastSuccessRecord(DataSyncRecord param) { + // 根据系统编码,数据类型,模式 查询最新一条成功的记录 + return this.getOne(Wrappers.lambdaQuery() + .eq(DataSyncRecord::getSystemCode, param.getSystemCode()) + .eq(DataSyncRecord::getDataType, param.getDataType()) + .eq(DataSyncRecord::getMode, param.getMode()) + // 只查成功的 + .eq(DataSyncRecord::getResult, DataSync.Result.SUCCESS.name()) + // 同步时间倒序排列 + .orderByDesc(DataSyncRecord::getSyncTime) + .last("limit 1") + ); + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java index 39a6f0b..3fba915 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java @@ -30,15 +30,14 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.mp.support.Condition; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tenant.TenantGuard; import org.springblade.core.tool.constant.BladeConstant; import org.springblade.core.tool.node.ForestNodeMerger; -import org.springblade.core.tool.utils.Func; -import org.springblade.core.tool.utils.StringPool; -import org.springblade.core.tool.utils.StringUtil; +import org.springblade.core.tool.utils.*; import org.springblade.system.cache.SysCache; import org.springblade.system.mapper.DeptMapper; import org.springblade.system.pojo.entity.Dept; @@ -49,12 +48,12 @@ import org.springblade.system.service.IDeptService; import org.springblade.system.service.IUserService; import org.springblade.system.wrapper.DeptWrapper; import org.springblade.system.wrapper.UserWrapper; +import org.springblade.thirdparty.oa.constant.OAConvertConstant; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT; @@ -67,6 +66,7 @@ import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT_PARENT; */ @Service @RequiredArgsConstructor +@Slf4j public class DeptServiceImpl extends ServiceImpl implements IDeptService { private static final String TENANT_ID = "tenantId"; @@ -265,4 +265,121 @@ public class DeptServiceImpl extends ServiceImpl implements ID } } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updateAncestors(Date startTime) { + // 1. 查询需要更新祖级列表的部门 + List list = this.list(Wrappers.lambdaQuery() + // 祖级列表为空 + .isNull(Dept::getAncestors) + // 开始时间不为空,查询同步时间大于开始时间或修改时间大于开始时间的数据 + .or(startTime != null, wrapper -> wrapper.ge(Dept::getSyncTime, startTime)) + .or(startTime != null, wrapper -> wrapper.ge(Dept::getUpdateTime, startTime)) + ); + if (CollectionUtil.isEmpty(list)) { + log.info("需要更新祖级列表的部门为空"); + return; + } + // 所有公司部门列表 + List allList = this.list().stream() + .map(dept -> BeanUtil.copyProperties(dept, DeptVO.class)) + .toList(); + // id对应的部门map + Map idDeptMap = allList.stream() + .collect(Collectors.toMap(DeptVO::getId, Function.identity(), (a, b) -> b)); + // 获取根节点列表 + List rootList = getRootList(allList, idDeptMap); + // 计算祖级列表 + setAncestors(rootList); + + // 3. 更新祖级列表 + List updateParams = list.stream() + .filter(dept -> { + DeptVO vo = idDeptMap.get(dept.getId()); + if (vo == null) { + return false; + } + if (vo.getAncestors() == null) { + return false; + } + // 祖级列表和数据库的不一样的才需要更新 + return !StringUtil.equals(vo.getAncestors(), dept.getAncestors()); + }) + .map(dept -> { + Dept updateParam = new Dept(); + updateParam.setId(dept.getId()); + DeptVO vo = idDeptMap.get(dept.getId()); + updateParam.setAncestors(vo.getAncestors()); + return updateParam; + }).toList(); + if (CollectionUtil.isNotEmpty(updateParams)) { + this.updateBatchById(updateParams); + } + } + + /** + * 获取根节点列表 + * @param allList + * @param idDeptMap + * @return + */ + private List getRootList(List allList, Map idDeptMap) { + if (CollectionUtil.isEmpty(allList)) { + return Collections.emptyList(); + } + // 根节点列表 + List rootList = new ArrayList<>(); + for (DeptVO vo : allList) { + Long parentId = vo.getParentId(); + if (OAConvertConstant.ROOT_PARENT_ID.equals(parentId)) { + // 父节点是根节点,添加到根节点列表 + rootList.add(vo); + continue; + } + DeptVO parent = idDeptMap.get(parentId); + if (parent != null) { + // 父节点存在,放入父节点的子节点列表中 + if (parent.getChildren() == null) { + parent.setChildren(new ArrayList<>()); + } + parent.getChildren().add(vo); + } + } + return rootList; + } + + /** + * 计算祖级列表 + * @param rootList 根节点列表 + */ + private void setAncestors(List rootList) { + if (CollectionUtil.isEmpty(rootList)) { + return; + } + // 根节点的祖级列表是 0, 最后拼接一个逗号,防止查询的时候有包含的 + String ancestors = OAConvertConstant.ROOT_PARENT_ID + ","; + setAncestors(rootList, ancestors); + } + + /** + * 设置祖级列表 + * @param list 节点列表 + * @param ancestors 祖级列表 + */ + private void setAncestors(List list, String ancestors) { + if (CollectionUtil.isEmpty(list)) { + return; + } + for (DeptVO vo : list) { + vo.setAncestors(ancestors); + if (CollectionUtil.isNotEmpty(vo.getChildren())) { + // 子节点列表不为空,设置子节点的祖级列表 + String newAncestors = ancestors + vo.getId() + ","; + setAncestors(vo.getChildren(), newAncestors); + } + } + } + + } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/MKPushServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/MKPushServiceImpl.java new file mode 100644 index 0000000..f04b413 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/MKPushServiceImpl.java @@ -0,0 +1,280 @@ +package org.springblade.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.UUID; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.common.constant.CommonConstant; +import org.springblade.core.tool.utils.DigestUtil; +import org.springblade.system.cache.ParamCache; +import org.springblade.system.convert.DeptConvert; +import org.springblade.system.convert.UserConvert; +import org.springblade.system.log.ComposeLogUtil; +import org.springblade.system.pojo.entity.DataSyncRecord; +import org.springblade.system.pojo.entity.Dept; +import org.springblade.system.pojo.entity.User; +import org.springblade.system.pojo.enums.DataSync; +import org.springblade.system.pojo.enums.DeptCategory; +import org.springblade.system.service.IDataSyncRecordService; +import org.springblade.system.service.IDeptService; +import org.springblade.system.service.IMKPushService; +import org.springblade.system.service.IUserService; +import org.springblade.system.util.DataSyncRecordUtils; +import org.springblade.thirdparty.mk.pojo.dto.MKPushDTO; +import org.springblade.thirdparty.mk.pojo.dto.MKPushOrgDeptDTO; +import org.springblade.thirdparty.mk.pojo.dto.MKPushPersonDTO; +import org.springblade.thirdparty.mk.service.IMKService; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_PASSWORD; + +/** + * mk 推送实现 + * @author bfhuange + * @date 2024/9/24 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class MKPushServiceImpl implements IMKPushService { + /** + * 分页查询每页最大大小 + */ + private static final long MAX_SIZE_PER_PAGE = 1000; + + private final IDeptService deptService; + private final IUserService userService; + private final IMKService mkService; + private final DeptConvert deptConvert; + private final UserConvert userConvert; + private final IDataSyncRecordService dataSyncRecordService; + + @Override + public void pushOrgAndRecord(Date oaStartTime) { + try { + ComposeLogUtil.addLog(log); + // 推送所有公司和部门并记录 + this.pushAndRecord(oaStartTime, DataSyncRecordUtils::createMKOrgPush, (startTime, endTime) -> { + // 1. 推送公司 + pushDept(DeptCategory.COMPANY, startTime, endTime); + // 2. 推送部门 + pushDept(DeptCategory.DEPT, startTime, endTime); + }); + } finally { + ComposeLogUtil.removeLastLog(); + } + } + + @Override + public void pushPersonAndRecord(Date oaStartTime) { + try { + ComposeLogUtil.addLog(log); + // 推送人员并记录 + this.pushAndRecord(oaStartTime, DataSyncRecordUtils::createMKPersonPush, (startTime, endTime) -> { + // 分页查询函数 + UnaryOperator> queryUser = this.getQueryUser(startTime, endTime); + // 分页推送函数 + BiConsumer, String> pushConsumer = this::pushUser; + // 公共推送 + pushCommon(queryUser, pushConsumer); + }); + } finally { + ComposeLogUtil.removeLastLog(); + } + } + + /** + * 推送并记录 + * @param oaStartTime + * @param recordSupplier + * @param pushHandler + */ + private void pushAndRecord(Date oaStartTime, Supplier recordSupplier, BiConsumer pushHandler) { + // 创建记录 + DataSyncRecord record = recordSupplier.get(); + // 查询最新一条成功记录 + DataSyncRecord lastSuccessRecord = dataSyncRecordService.getLastSuccessRecord(record); + // 获取推送数据的开始时间 + Date startTime = getStartTime(lastSuccessRecord, oaStartTime); + // 推送开始时间作为截止时间,只同步这个时间段的更新的数据 + Date endTime = new Date(); + record.setSyncTime(endTime); + try { + pushHandler.accept(startTime, endTime); + } catch (Exception e) { + // 有异常,结果改成失败 + record.setResult(DataSync.Result.FAIL.name()); + throw e; + } finally { + // 新事务保存同步记录 + dataSyncRecordService.saveWithNewTransaction(record); + } + } + + /** + * 获取推送开始时间 + * @param lastSuccessRecord 最后一条同步成功的记录 + * @param oaStartTime oa同步查询开始时间 + * @return + */ + private Date getStartTime(DataSyncRecord lastSuccessRecord, Date oaStartTime) { + if (lastSuccessRecord == null || lastSuccessRecord.getSyncTime() == null) { + // 最后一条推送成功记录为空,返回空推送全部 + return null; + } + if (oaStartTime == null) { + // oa 同步查询开始时间为空,且最后一条推送成功记录不为空 + return lastSuccessRecord.getSyncTime(); + } + // 否则取2个时间较小的那个 + Date lastSyncTime = lastSuccessRecord.getSyncTime(); + return oaStartTime.before(lastSyncTime) ? oaStartTime : lastSyncTime; + } + + /** + * 同步公司/部门 + * + * @param deptCategory 类型 + * @param startTime 数据更新开始时间 + * @param endTime 数据更新截止时间 + */ + private void pushDept(DeptCategory deptCategory, Date startTime, Date endTime) { + // 分页查询函数 + UnaryOperator> queryDept = getQueryDept(deptCategory, startTime, endTime); + // 推送函数 + BiConsumer, String> pushDept = getPushDept(deptCategory); + // 公共推送 + pushCommon(queryDept, pushDept); + } + + /** + * 获取查询部门的方法 + * + * @param deptCategory + * @param startTime + * @param endTime + * @return + */ + private UnaryOperator> getQueryDept(DeptCategory deptCategory, Date startTime, Date endTime) { + return page -> deptService.page(page, Wrappers.lambdaQuery() + .eq(Dept::getDeptCategory, deptCategory.getCode()) + .eq(Dept::getIsOa, CommonConstant.YES) + // 查询同步时间小于结束时间且修改时间小于结束时间的部门 + .lt(Dept::getSyncTime, endTime) + .lt(Dept::getUpdateTime, endTime) + // 开始时间不为空,查询同步时间大于开始时间或修改时间大于开始时间的数据 + .and(startTime != null, wrapper -> wrapper.ge(Dept::getSyncTime, startTime) + .or(w -> w.ge(Dept::getUpdateTime, startTime))) + ); + } + + /** + * 获取推送部门的方法 + * @param deptCategory + * @return + */ + private BiConsumer, String> getPushDept(DeptCategory deptCategory) { + return (page, batchId) -> { + List records = page.getRecords(); + if (CollectionUtil.isEmpty(records)) { + return; + } + // 当前页是否最后一页 + boolean lastPage = page.getCurrent() == page.getPages(); + MKPushDTO param = new MKPushDTO<>(); + param.setBatchId(batchId); + param.setFinish(lastPage); + List list = deptConvert.entities2dtos(records); + param.setMessage(list); + // 推送公司 + boolean result = mkService.pushOrgDept(param); + ComposeLogUtil.getLastLog().info("mk推送{} 批次号:{} 当前页:{} 当前页数量:{} 结果:{}", deptCategory.getName(), batchId, page.getCurrent(), page.getSize(), result); + }; + } + + + /** + * 获取查询人员的方法 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + private UnaryOperator> getQueryUser(Date startTime, Date endTime) { + return page -> userService.page(page, Wrappers.lambdaQuery() + .eq(User::getIsOa, CommonConstant.YES) + // 查询同步时间小于结束时间且修改时间小于结束时间的用户 + .lt(User::getSyncTime, endTime) + .lt(User::getUpdateTime, endTime) + // 开始时间不为空,查询同步时间大于更新时间或修改时间大于更新时间的数据 + .and(startTime != null, wrapper -> wrapper.ge(User::getSyncTime, startTime) + .or(w -> w.ge(User::getUpdateTime, startTime))) + ); + } + + /** + * 推送人员 + * @param page + * @param batchId + */ + private void pushUser(IPage page, String batchId) { + List records = page.getRecords(); + if (CollectionUtil.isEmpty(records)) { + return; + } + // 当前页是否最后一页 + boolean lastPage = page.getCurrent() == page.getPages(); + MKPushDTO param = new MKPushDTO<>(); + param.setBatchId(batchId); + param.setFinish(lastPage); + // 默认密码及参数转换 + String defaultPassword = ParamCache.getValue(DEFAULT_PARAM_PASSWORD); + // 解密手机号 + records.forEach(userService::decryptPhone); + List list = userConvert.entities2dtos(records, DigestUtil.md5Hex(defaultPassword)); + param.setMessage(list); + // 推送人员 + boolean result = mkService.pushPerson(param); + ComposeLogUtil.getLastLog().info("mk推送人员 批次号:{} 当前页:{} 当前页数量:{} 结果:{}", batchId, page.getCurrent(), page.getSize(), result); + if (result) { + // 推送成功,修改用户推送状态 + List updateIds = records.stream() + .map(User::getId) + .toList(); + userService.lambdaUpdate() + // 设置是否推送mk为是 + .set(User::getIsPushMk, CommonConstant.YES) + .in(User::getId, updateIds) + // 筛选未推送mk的 + .eq(User::getIsPushMk, CommonConstant.NO) + .update(); + } + } + + /** + * 公共推送 + * @param queryOperator 分页查询 + * @param pushConsumer 数据推送 + * @param + */ + private void pushCommon(UnaryOperator> queryOperator, BiConsumer, String> pushConsumer) { + IPage page = new Page<>(1, MAX_SIZE_PER_PAGE); + String batchId = UUID.fastUUID().toString(true); + do { + // 分页查询数据 + page = queryOperator.apply(page); + // 推送数据 + pushConsumer.accept(page, batchId); + page.setCurrent(page.getCurrent() + 1); + } while (page.getCurrent() <= page.getPages()); + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java new file mode 100644 index 0000000..38e7eb9 --- /dev/null +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java @@ -0,0 +1,649 @@ +package org.springblade.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springblade.common.constant.DictTypeEnum; +import org.springblade.core.cache.utils.CacheUtil; +import org.springblade.core.log.exception.ServiceException; +import org.springblade.core.tool.utils.DateUtil; +import org.springblade.core.tool.utils.DigestUtil; +import org.springblade.system.cache.DictCache; +import org.springblade.system.cache.ParamCache; +import org.springblade.system.convert.DeptConvert; +import org.springblade.system.convert.UserConvert; +import org.springblade.system.log.ComposeLogUtil; +import org.springblade.system.pojo.entity.*; +import org.springblade.system.pojo.enums.DataSync; +import org.springblade.system.pojo.enums.DeptCategory; +import org.springblade.system.pojo.vo.UserDeptIdsVO; +import org.springblade.system.service.*; +import org.springblade.system.util.DataSyncRecordUtils; +import org.springblade.thirdparty.oa.constant.OAConstant; +import org.springblade.thirdparty.oa.constant.OAConvertConstant; +import org.springblade.thirdparty.oa.feign.IOAClient; +import org.springblade.thirdparty.oa.pojo.response.OACompanyResponse; +import org.springblade.thirdparty.oa.pojo.response.OADepartmentResponse; +import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse; +import org.springblade.thirdparty.oa.pojo.search.OACompanySearch; +import org.springblade.thirdparty.oa.pojo.search.OADepartmentSearch; +import org.springblade.thirdparty.oa.pojo.search.OAPersonSearch; +import org.springblade.thirdparty.oa.pojo.search.OASearch; +import org.springblade.thirdparty.oa.util.OAUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.springblade.common.constant.CommonConstant.*; +import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE; +import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE; + +/** + * @author bfhuange + * @date 2024/8/23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class OASyncServiceImpl implements IOASyncService { + /** + * 用户表部门id最大长度 + */ + private static final int MAX_DEPT_ID_LENGTH = 2000; + + private final IOAClient oaClient; + private final DeptConvert deptConvert; + private final IDeptService deptService; + private final IUserService userService; + private final UserConvert userConvert; + private final IUserDeptService userDeptService; + private final IRoleService roleService; + private final IMKPushService mkPushService; + private final IDataSyncRecordService dataSyncRecordService; + + @Transactional(rollbackFor = Exception.class) + @Override + public void syncCompanyAndDeptAndPushMK(boolean syncAll) { + try { + ComposeLogUtil.addLog(log); + // 1. 同步公司和部门并记录 + Date startTime = this.syncAndRecord(DataSyncRecordUtils::createOAOrgFetch, queryStartTime -> { + // 1. 同步公司 + this.syncCompany(queryStartTime); + // 2. 同步部门 + this.syncDept(queryStartTime); + // 2. 更新部门祖级列表 + deptService.updateAncestors(queryStartTime); + }, syncAll); + // 2. 推送mk + mkPushService.pushOrgAndRecord(startTime); + } finally { + ComposeLogUtil.removeLastLog(); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void syncPersonAndPushMK(boolean syncAll) { + try { + ComposeLogUtil.addLog(log); + // 1. 同步人员并记录 + Date startTime = this.syncAndRecord(DataSyncRecordUtils::createOAPersonFetch, this::syncPerson, syncAll); + // 2. 推送mk + mkPushService.pushPersonAndRecord(startTime); + } finally { + ComposeLogUtil.removeLastLog(); + } + } + + /** + * 同步并记录 + * + * @param recordSupplier 同步记录提供 + * @param syncHandler 同步处理器 + * @param syncAll 是否同步所有 + * @return + */ + private Date syncAndRecord(Supplier recordSupplier, Consumer syncHandler, boolean syncAll) { + // 创建记录 + DataSyncRecord record = recordSupplier.get(); + // 查询最新一条成功记录 + DataSyncRecord lastSuccessRecord = syncAll ? null : dataSyncRecordService.getLastSuccessRecord(record); + // 同步所有,开始时间设置为null,否则最新成功记录的同步时间作为查询开始时间,第一次为null同步所有 + Date startTime = syncAll ? null : Optional.ofNullable(lastSuccessRecord) + .map(DataSyncRecord::getSyncTime) + .orElse(null); + // 同步时间设置早一些,可能下次增量同步会多同步一些数据 + // 设置晚了,增量同步可能会漏同步数据 + record.setSyncTime(new Date()); + try { + // 1. 同步处理 + syncHandler.accept(startTime); + } catch (Exception e) { + // 有异常,结果改成失败 + record.setResult(DataSync.Result.FAIL.name()); + throw e; + } finally { + // 新事务保存同步记录 + dataSyncRecordService.saveWithNewTransaction(record); + } + return startTime; + } + + /** + * 同步公司 + * @param startTime 查询开始时间 + */ + private void syncCompany(Date startTime) { + // 未处理的数据 + List notHandleList = new ArrayList<>(); + // 1. 设置查询参数 + OACompanySearch companySearch = new OACompanySearch(); + companySearch.setCurPage(1); + if (startTime != null) { + // 开始时间不为空,设置修改时间参数 + companySearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME)); + } + // 2. 分页查询并处理数据 + OAUtils.pageSyncHandler(companySearch, param -> oaClient.queryCompanyPage(new OASearch<>(param)), response -> { + ComposeLogUtil.getLastLog().error("调用OA接口查询公司信息失败 {}", JSON.toJSONString(response)); + return new ServiceException("调用OA接口查询公司信息失败"); + }, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> { + // 处理数据 + List deptList = handleCompany(list); + notHandleList.addAll(deptList); + }); + // 3. 未处理的数据 + if (CollectionUtil.isNotEmpty(notHandleList)) { + ComposeLogUtil.getLastLog().warn("同步公司,还有未处理的数据:{}", JSON.toJSONString(notHandleList)); + } + // 清除部门缓存 + CacheUtil.clear(SYS_CACHE); + CacheUtil.clear(SYS_CACHE, Boolean.FALSE); + } + + /** + * 同步部门 + * @param startTime 查询开始时间 + */ + private void syncDept(Date startTime) { + String subCompanyIds = getSubCompanyIds(); + if (StringUtils.isEmpty(subCompanyIds)) { + ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数"); + return; + } + // 未处理的数据 + List notHandleList = new ArrayList<>(); + // 1. 设置查询参数 + OADepartmentSearch departmentSearch = new OADepartmentSearch(); + departmentSearch.setCurPage(1); + departmentSearch.setSubcompanyid1(subCompanyIds); + if (startTime != null) { + // 开始时间不为空,设置修改时间参数 + departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME)); + } + // 2. 分页查询并处理数据 + OAUtils.pageSyncHandler(departmentSearch, param -> oaClient.queryDepartmentPage(new OASearch<>(param)), response -> { + ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(response)); + return new ServiceException("调用OA接口查询部门信息失败"); + }, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> { + // 处理数据 + List deptList = handleDept(list); + notHandleList.addAll(deptList); + }); + // 3. 未处理的数据 + if (CollectionUtil.isNotEmpty(notHandleList)) { + ComposeLogUtil.getLastLog().warn("同步部门,还有未处理的数据:{}", JSON.toJSONString(notHandleList)); + } + // 清除部门缓存 + CacheUtil.clear(SYS_CACHE); + CacheUtil.clear(SYS_CACHE, Boolean.FALSE); + } + + /** + * 同步人员 + * @param startTime 查询开始时间 + */ + private void syncPerson(Date startTime) { + String subCompanyIds = getSubCompanyIds(); + if (StringUtils.isEmpty(subCompanyIds)) { + ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数"); + return; + } + // 1. 设置查询参数 + OAPersonSearch personSearch = new OAPersonSearch(); + personSearch.setCurPage(1); + personSearch.setSubcompanyid1(subCompanyIds); + if (startTime != null) { + // 开始时间不为空,设置修改时间参数 + personSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME)); + } + // 2. 分页查询并处理数据 + OAUtils.pageSyncHandler(personSearch, param -> oaClient.queryPersonPage(new OASearch<>(param)), response -> { + ComposeLogUtil.getLastLog().error("调用OA接口查询人员信息失败 {}", JSON.toJSONString(response)); + return new ServiceException("调用OA接口查询人员信息失败"); + }, 10000, ComposeLogUtil.getLastLog()::info).accept(this::handlePerson); + // 清除用户缓存 + CacheUtil.clear(USER_CACHE); + } + + /** + * 处理oa人员 + * @param oaPersons + */ + private void handlePerson(List oaPersons) { + if (CollectionUtil.isEmpty(oaPersons)) { + // 数据为空,直接返回 + return; + } + // 根公司id + String rootCompanyId = getRootCompanyId(); + if (rootCompanyId == null) { + return; + } + // 根公司下要同步的部门id + Set rootCompanyDeptIds = getRootCompanyDeptIds(rootCompanyId); + oaPersons = oaPersons.stream() + // 公司不是根公司,或者部门在根公司下要同步的部门列表中 + .filter(oaPerson -> !rootCompanyId.equals(oaPerson.getSubcompanyid1()) || rootCompanyDeptIds.contains(oaPerson.getDepartmentid())) + .toList(); + // 根据手机号转成set + TreeSet oaPersonSet = CollectionUtil.toTreeSet(oaPersons, Comparator.comparing(OAPersonResponse::getMobile)); + // 默认密码 + String defaultPassword = DigestUtil.encrypt(ParamCache.getValue(DEFAULT_PARAM_PASSWORD)); + // 转换数据 + List users = oaPersonSet.stream() + // 只需要手机不为空的 + .filter(person -> StringUtils.isNotBlank(person.getMobile())) + .map(person -> userConvert.person2user(person, defaultPassword)) + .toList(); + List phones = users.stream() + .map(User::getPhone) + .filter(StringUtils::isNotBlank) + .distinct() + .toList(); + // 查询所有用户 + Map userMap = userService.list(Wrappers.lambdaQuery() + //.eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED) + .in(User::getPhone, phones) + ).stream() + // 解密手机号 + .peek(userService::decryptPhone) + .collect(Collectors.toMap(User::getPhone, User::getId, (a, b) -> b)); + // 数据库存在的所有用户id + Set existsUserIds = new HashSet<>(userMap.values()); + users.forEach(user -> { + if (userMap.containsKey(user.getPhone())) { + // 根据手机号获取对应的用户id + user.setId(userMap.get(user.getPhone())); + // 清空密码,不修改密码 + user.setPassword(null); + } else { + // 没有就生成一个id + user.setId(DefaultIdentifierGenerator.getInstance().nextId(null)); + userService.encryptPhone(user); + userMap.put(user.getPhone(), user.getId()); + } + }); + + // 不存在的新增 + List addUsers = users.stream() + .filter(user -> !existsUserIds.contains(user.getId())) + .toList(); + if (CollectionUtil.isNotEmpty(addUsers)) { + ComposeLogUtil.getLastLog().info("批量新增用户:{}", addUsers.size()); + userService.saveBatch(addUsers); + } + // 存在的修改 + List updateUsers = users.stream() + .filter(user -> existsUserIds.contains(user.getId())) + .toList(); + if (CollectionUtil.isNotEmpty(updateUsers)) { + ComposeLogUtil.getLastLog().info("批量修改用户:{}", updateUsers.size()); + userService.updateBatchById(updateUsers); + } + // 没有手机号的数据 = 手机号为空的数量 + long noPhoneNum = oaPersons.stream() + .map(OAPersonResponse::getMobile) + .filter(StringUtils::isBlank) + .count(); + ComposeLogUtil.getLastLog().info("没有手机号的数据:{}", noPhoneNum); + + Map userDeptMap = userDeptService.list(Wrappers.lambdaQuery() + .in(UserDept::getUserId, userMap.values()) + ).stream() + .collect(Collectors.toMap(this::getUserDeptKey, UserDept::getId, (a, b) -> b)); + // 数据库存在的所有用户部门id + Set existsUserDeptIds = new HashSet<>(userDeptMap.values()); + + List userDeptList = oaPersons.stream() + // 只要包含用户手机号的 + .filter(oaPerson -> userMap.containsKey(oaPerson.getMobile())) + .map(oaPerson -> userConvert.person2userDept(oaPerson, userMap)) + .toList(); + userDeptList.forEach(userDept -> { + String userDeptKey = getUserDeptKey(userDept); + if (userDeptMap.containsKey(userDeptKey)) { + // 根据key获取用户部门id + userDept.setId(userDeptMap.get(userDeptKey)); + } else { + // 没有就生成一个id + userDept.setId(DefaultIdentifierGenerator.getInstance().nextId(null)); + } + }); + // 不存在的新增 + List addList = userDeptList.stream() + .filter(userDept -> !existsUserDeptIds.contains(userDept.getId())) + .toList(); + if (CollectionUtil.isNotEmpty(addList)) { + ComposeLogUtil.getLastLog().info("批量新增用户部门:{}", addList.size()); + userDeptService.saveBatch(addList); + } + // 存在的修改 + List updateList = userDeptList.stream() + .filter(userDept -> existsUserDeptIds.contains(userDept.getId())) + .toList(); + if (CollectionUtil.isNotEmpty(updateList)) { + ComposeLogUtil.getLastLog().info("批量修改用户部门:{}", updateList.size()); + userDeptService.updateBatchById(updateList); + } + // 回写部门id到用户表 + Collection userIds = userMap.values(); + List list = userDeptService.queryUserDeptIds(userIds); + // 查询没有角色的用户id + Set noRoleUserIds = userService.list(Wrappers.lambdaQuery() + .in(User::getId, userIds) + .isNull(User::getRoleId) + ).stream() + .map(User::getId) + .collect(Collectors.toSet()); + // 获取默认角色id + String defaultRoleId = getDefaultRoleId(); + List updateUserParams = list.stream() + // 过滤掉空部门id及长度超长的 + .filter(userDeptIds -> StringUtils.isNotBlank(userDeptIds.getDeptIds()) && userDeptIds.getDeptIds().length() <= MAX_DEPT_ID_LENGTH) + .map(userDeptIds -> { + User user = new User(); + user.setId(userDeptIds.getUserId()); + user.setDeptId(userDeptIds.getDeptIds()); + user.setDeptCodes(userDeptIds.getDeptCodes()); + if (noRoleUserIds.contains(userDeptIds.getUserId())) { + user.setRoleId(defaultRoleId); + } + return user; + }).toList(); + userService.updateBatchById(updateUserParams); + } + + /** + * 处理oa公司 + * @param oaCompanies + * @return 未处理的数据 + */ + private List handleCompany(List oaCompanies) { + if (CollectionUtil.isEmpty(oaCompanies)) { + // 数据为空,直接返回 + return Collections.emptyList(); + } + // 获取需要的公司名称 + Set companyNames = getCompanyNames(); + // 转换数据 + List allParam = oaCompanies.stream() + // 筛选只要需要的公司 + .filter(company -> companyNames.contains(company.getSubcompanyname())) + .map(deptConvert::company2dept) + .toList(); + + // 查询数据库的部门,转换成map + Map deptMap = getAllCompanyDeptMap(); + + // 处理部门 + return handleDept(allParam, deptMap, DeptCategory.COMPANY); + } + + /** + * 处理oa部门 + * @param oaDepts + * @return 未处理的数据 + */ + private List handleDept(List oaDepts) { + if (CollectionUtil.isEmpty(oaDepts)) { + // 数据为空,直接返回 + return Collections.emptyList(); + } + // 查询所有公司的编码和id的map + Map companyDeptMap = getAllCompanyDeptMap(); + // 根公司id + String rootCompanyId = getRootCompanyId(); + if (rootCompanyId == null) { + return Collections.emptyList(); + } + // 根公司下要同步的部门名称 + Set rootCompanyDeptNames = getRootCompanyDeptNames(); + // 转换数据 + List allParam = oaDepts.stream() + // 部门的公司不是根公司,或者部门名称在根公司下需要同步的部门列表中,且是根部门 + .filter(oaDept -> !rootCompanyId.equals(oaDept.getSubcompanyid1()) || (OAConstant.ROOT_COMPANY_ID.equals(oaDept.getSupdepid()) && rootCompanyDeptNames.contains(oaDept.getDepartmentname()))) + .map(dept -> deptConvert.dept2dept(dept, companyDeptMap)) + .toList(); + if (CollectionUtil.isEmpty(allParam)) { + return Collections.emptyList(); + } + Set deptCodes = allParam.stream() + .map(Dept::getDeptCode) + .collect(Collectors.toSet()); + // 查询数据库的部门,转换成map + Map deptMap = deptService.list(Wrappers.lambdaQuery() + .eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode()) + .in(Dept::getDeptCode, deptCodes) + ).stream() + .filter(dept -> StringUtils.isNotEmpty(dept.getDeptCode())) + .collect(Collectors.toMap(Dept::getDeptCode, Dept::getId, (a, b) -> b)); + // 处理部门 + return this.handleDept(allParam, deptMap, DeptCategory.DEPT); + } + + /** + * 处理部门 + * @param allParam 待处理的部门 + * @param deptMap 数据库所有部门编码和id map + * @return 未处理的数据 + */ + private List handleDept(List allParam, Map deptMap, DeptCategory deptCategory) { + // 数据库存在的所有部门id + Set existsDeptIds = new HashSet<>(deptMap.values()); + allParam.forEach(dept -> { + if (deptMap.containsKey(dept.getDeptCode())) { + // 根据部门编码获取对应的部门id + dept.setId(deptMap.get(dept.getDeptCode())); + } else { + // 没有就生成一个id + dept.setId(DefaultIdentifierGenerator.getInstance().nextId(null)); + deptMap.put(dept.getDeptCode(), dept.getId()); + } + }); + + // 重新把没有父id的数据设置一下父id + allParam.stream() + .filter(dept -> dept.getParentId() == null && deptMap.containsKey(dept.getDeptCode())) + .forEach(dept -> dept.setParentId(deptMap.get(dept.getParentCode()))); + + // 父id不为空,保存数据 + List list = allParam.stream() + .filter(dept -> dept.getParentId() != null) + .toList(); + // 不存在的新增 + List addList = list.stream() + .filter(dept -> !existsDeptIds.contains(dept.getId())) + .toList(); + if (CollectionUtil.isNotEmpty(addList)) { + ComposeLogUtil.getLastLog().info("批量新增{}:{}", deptCategory.getName(), addList.size()); + deptService.saveBatch(addList); + } + // 存在的修改 + List updateList = list.stream() + .filter(dept -> existsDeptIds.contains(dept.getId())) + .toList(); + if (CollectionUtil.isNotEmpty(updateList)) { + ComposeLogUtil.getLastLog().info("批量修改{}:{}", deptCategory.getName(), updateList.size()); + deptService.updateBatchById(updateList); + } + // 返回未处理的 + return allParam.stream() + .filter(dept -> dept.getParentId() == null) + .toList(); + } + + /** + * 获取oa查询参数,子公司id参数 + * @return + */ + private String getSubCompanyIds() { + Set companyNames = this.getCompanyNames(); + if (companyNames.isEmpty()) { + return null; + } + return deptService.list(Wrappers.lambdaQuery() + // 根据名称查询公司 + .in(Dept::getDeptName, companyNames) + .eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode()) + ).stream() + // 只要部门编码不为空,且包含了oa公司前缀的 + .filter(dept -> StringUtils.isNotBlank(dept.getDeptCode()) && dept.getDeptCode().contains(OAConvertConstant.COMPANY_OA_PREFIX)) + // 去掉oa公司前缀 + .map(dept -> dept.getDeptCode().replace(OAConvertConstant.COMPANY_OA_PREFIX, "")) + .collect(Collectors.joining(",")); + } + + /** + * 获取默认角色id + */ + private String getDefaultRoleId() { + // 从参数管理查询,没有取默认值,作为角色别名,查询角色 + String defaultRole = ParamCache.getValue(DEFAULT_PARAM_ROLE); + if (defaultRole == null) { + defaultRole = DEFAULT_ROLE; + } + List roleList = roleService.list(Wrappers.lambdaQuery() + .eq(Role::getRoleAlias, defaultRole) + ); + if (CollectionUtil.isEmpty(roleList)) { + return null; + } + return roleList.get(0).getId().toString(); + } + + /** + * 获取所有公司的编码和id的map + * @return + */ + private Map getAllCompanyDeptMap() { + return deptService.list(Wrappers.lambdaQuery() + .eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode()) + ).stream() + .filter(dept -> StringUtils.isNotEmpty(dept.getDeptCode())) + .collect(Collectors.toMap(Dept::getDeptCode, Dept::getId, (a, b) -> b)); + } + + /** + * 获取需要的公司id + * @return + */ + private Set getCompanyNames() { + List list = DictCache.getList(DictTypeEnum.SYNC_COMPANY_NAME.getType()); + if (CollectionUtil.isEmpty(list)) { + ComposeLogUtil.getLastLog().warn("未查询到要同步的公司名称"); + return Collections.emptySet(); + } + return list.stream() + .filter(Objects::nonNull) + .map(Dict::getDictValue) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + } + + /** + * 获取根公司下需要同步的部门名称 + * @return + */ + private Set getRootCompanyDeptNames() { + List list = DictCache.getList(DictTypeEnum.SYNC_ROOT_COMPANY_DEPT_NAME.getType()); + if (CollectionUtil.isEmpty(list)) { + ComposeLogUtil.getLastLog().warn("未查询到要同步的根公司下的部门名称"); + return Collections.emptySet(); + } + return list.stream() + .filter(Objects::nonNull) + .map(Dict::getDictValue) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + } + + /** + * 获取根公司下需要同步的部门id + * @param rootCompanyId + * @return + */ + private Set getRootCompanyDeptIds(String rootCompanyId) { + Set rootCompanyDeptNames = this.getRootCompanyDeptNames(); + if (CollectionUtil.isEmpty(rootCompanyDeptNames)) { + return Collections.emptySet(); + } + // 查询父部门编码是根公司的,部门类型的,根公司下部门名称的部门 + return deptService.list(Wrappers.lambdaQuery() + .in(Dept::getDeptName, rootCompanyDeptNames) + .eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode()) + .eq(Dept::getParentCode, OAConvertConstant.COMPANY_OA_PREFIX + rootCompanyId) + ).stream() + .filter(dept -> StringUtils.isNotBlank(dept.getDeptCode())) + .map(dept -> dept.getDeptCode().replace(OAConvertConstant.DEPARTMENT_OA_PREFIX, "")) + .collect(Collectors.toSet()); + } + + /** + * 根公司id + * @return + */ + private String getRootCompanyId() { + String companyName = DictCache.getValue(DictTypeEnum.SYNC_COMPANY_NAME.getType(), DICT_KEY_SYNC_ROOT_COMPANY_NAME); + if (StringUtils.isBlank(companyName)) { + ComposeLogUtil.getLastLog().warn("未查询到配置的根公司名称"); + return null; + } + Dept dept = deptService.getOne(Wrappers.lambdaQuery() + .eq(Dept::getDeptName, companyName) + .eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode()) + .last("limit 1") + ); + if (dept == null) { + ComposeLogUtil.getLastLog().warn("未查询到根公司 名称:{}", companyName); + return null; + } + if (StringUtils.isBlank(dept.getDeptCode())) { + ComposeLogUtil.getLastLog().warn("根公司编码为空:{}", companyName); + return null; + } + // 部门编码去掉前缀,就是oa的id + return dept.getDeptCode().replace(OAConvertConstant.COMPANY_OA_PREFIX, ""); + } + + /** + * 获取用户部门唯一标识,用户id+公司编码+部门编码 + * @param userDept + * @return + */ + private String getUserDeptKey(UserDept userDept) { + if (userDept == null) { + return null; + } + return userDept.getUserId() + userDept.getCompanyCode() + userDept.getDeptCode(); + } +} diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserDeptServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserDeptServiceImpl.java index b882a88..88b1773 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserDeptServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserDeptServiceImpl.java @@ -26,12 +26,18 @@ package org.springblade.system.service.impl; +import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springblade.system.pojo.entity.UserDept; import org.springblade.system.mapper.UserDeptMapper; +import org.springblade.system.pojo.vo.UserDeptIdsVO; import org.springblade.system.service.IUserDeptService; import org.springframework.stereotype.Service; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + /** * 服务实现类 * @@ -40,4 +46,12 @@ import org.springframework.stereotype.Service; @Service public class UserDeptServiceImpl extends ServiceImpl implements IUserDeptService { + @Override + public List queryUserDeptIds(Collection userIds) { + if (CollectionUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + return baseMapper.queryUserDeptIds(userIds); + } + } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserServiceImpl.java index f74abc6..9c10c35 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/UserServiceImpl.java @@ -32,6 +32,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.AllArgsConstructor; +import org.bouncycastle.util.encoders.Hex; import org.springblade.common.constant.ParamConstant; import org.springblade.common.constant.TenantConstant; import org.springblade.core.cache.utils.CacheUtil; @@ -40,6 +41,7 @@ import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.core.mp.enums.StatusType; import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; +import org.springblade.core.oauth2.props.OAuth2Properties; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tenant.BladeTenantProperties; import org.springblade.core.tenant.TenantGuard; @@ -54,6 +56,7 @@ import org.springblade.system.cache.SysCache; import org.springblade.system.cache.UserCache; import org.springblade.system.excel.UserExcel; import org.springblade.system.mapper.UserMapper; +import org.springblade.system.pojo.context.IPhone; import org.springblade.system.pojo.entity.*; import org.springblade.system.pojo.enums.DictEnum; import org.springblade.system.pojo.enums.UserType; @@ -91,6 +94,9 @@ public class UserServiceImpl extends BaseServiceImpl implement private final IRoleService roleService; private final BladeTenantProperties tenantProperties; + private final OAuth2Properties properties; + + @Override public User getDetail(User user) { QueryWrapper queryWrapper = Condition.getQueryWrapper(user); @@ -751,4 +757,25 @@ public class UserServiceImpl extends BaseServiceImpl implement } } + + @Override + public void decryptPhone(IPhone user) { +// if (user == null) { +// return; +// } +// if (user.getPhone() != null && user.getPhone().length() > 20) { +// user.setPhone(SM2Util.decrypt(user.getPhone(), properties.getPublicKey(), properties.getPrivateKey())); +// } + } + + @Override + public void encryptPhone(IPhone user) { +// if (user == null) { +// return; +// } +// if(StringUtil.isNotBlank(user.getPhone())) { +// user.setPhone(Hex.toHexString(SM2Util.encrypt(user.getPhone(),properties.getPublicKey()))); +// } + } + } diff --git a/blade-third-party-api/blade-mk-api/pom.xml b/blade-third-party-api/blade-mk-api/pom.xml new file mode 100644 index 0000000..de6fe1d --- /dev/null +++ b/blade-third-party-api/blade-mk-api/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + org.springblade + blade-third-party-api + ${revision} + + + blade-mk-api + ${project.artifactId} + jar + + + + org.springblade + blade-starter-redis + + + + commons-io + commons-io + 2.11.0 + + + + diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/MKFeignClientConfig.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/MKFeignClientConfig.java new file mode 100644 index 0000000..309cc4f --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/MKFeignClientConfig.java @@ -0,0 +1,17 @@ +package org.springblade.thirdparty.mk.config; + +import feign.RequestInterceptor; +import org.springframework.context.annotation.Bean; + +/** + * @author bfhuange + * @since 2024/12/18 + */ +public class MKFeignClientConfig { + @Bean + public RequestInterceptor requestInterceptor() { + return template -> { + // 空实现屏蔽 全局拦截器 BladeFeignRequestInterceptor + }; + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/MKProperties.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/MKProperties.java new file mode 100644 index 0000000..7b884c7 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/MKProperties.java @@ -0,0 +1,61 @@ +package org.springblade.thirdparty.mk.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * mk配置类 + * @author bfhuange + * @date 2024/9/11 + */ +@Data +@ConfigurationProperties(prefix = "third-party.mk") +public class MKProperties { + /** + * 应用key mk-集成平台-本系统API网关-账号管理 + */ + private String appKey; + /** + * 应用secret mk-集成平台-本系统API网关-账号管理 + */ + private String appSecret; + /** + * mk基础url,接口调用地址,内网地址 + */ + private String baseUrl; + /** + * mk 登录地址,外网地址 + */ + private String loginUrl; + + //======================== mk 认证相关配置================================ + + /** + * oauth 应用id mk-系统管理-安全管理-认证服务 + */ + private String oauthAppId; + /** + * oauth 应用秘钥 mk-系统管理-安全管理-认证服务 + */ + private String oauthAppSecret; + /** + * mk 流程提交标题前缀 + */ + private String subjectPrefix; + /** + * mk 流程模板编码前缀 + */ + private String templateCodePrefix; + /** + * sso登录地址 + */ + private String mkSsoLoginUrl = "/data/sys-oauth/ssoLogin"; + /** + * 校验请求登录页面的来源地址, + */ + private boolean checkReferer = true; + /** + * erp系统基础链接,用于校验referer是否是erp的,仅在 checkReferer 为true生效 + */ + private String erpBaseUrls; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/ThirdPartyMKAutoConfiguration.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/ThirdPartyMKAutoConfiguration.java new file mode 100644 index 0000000..6cc5a72 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/config/ThirdPartyMKAutoConfiguration.java @@ -0,0 +1,16 @@ +package org.springblade.thirdparty.mk.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * 第三方api mk配置类 + * @author bfhuange + * @date 2024/9/11 + */ +@ComponentScan("org.springblade.thirdparty.mk") +@EnableConfigurationProperties(MKProperties.class) +@Configuration +public class ThirdPartyMKAutoConfiguration { +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/constant/MKConstant.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/constant/MKConstant.java new file mode 100644 index 0000000..7baee0d --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/constant/MKConstant.java @@ -0,0 +1,73 @@ +package org.springblade.thirdparty.mk.constant; + +/** + * mk常量类 + * @author bfhuange + * @date 2024/9/11 + */ +public class MKConstant { + /** + * 获取token 成功状态码 + */ + public static final int TOKEN_SUCCESS_CODE = 0; + + /** + * mk token缓存key + */ + public static final String MK_TOKEN_KEY = "blade:third:mk:token"; + + /** + * 组织架构类型: org-机构 + */ + public static final String ORG_TYPE_ORG = "org"; + /** + * 组织架构类型: dept-部门 + */ + public static final String ORG_TYPE_DEPT = "dept"; + /** + * 性别 男性 M表示男性,F表示女性,UN-未知 + */ + public static final String SEX_MAN = "M"; + /** + * 性别 女性 M表示男性,F表示女性,UN-未知 + */ + public static final String SEX_WOMAN = "F"; + /** + * 性别 未知 M表示男性,F表示女性,UN-未知 + */ + public static final String SEX_UNKONW = "UN"; + /** + * 操作类型 起草人撤销 + */ + public static final String OPERATE_TYPE_REVOKE = "drafter_retract"; + /** + * 操作类型 起草人废弃 + */ + public static final String OPERATE_TYPE_ABANDON = "drafter_abandon"; + /** + * 起草节点id + */ + public static final String DAFTER_NODE_ID = "N2"; + /** + * 抄送节点 + */ + public static final String NODE_TYPE_SEND = "send"; + /** + * 电子签名类型 + */ + public static final String FILE_TYPE_SIGN = "electronicSign"; + + /** + * 排序 + */ + public static class Sort { + /** + * 升序 + */ + public static final String ASC = "asc"; + /** + * 降序 + */ + public static final String DESC = "desc"; + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/constant/MKDoc.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/constant/MKDoc.java new file mode 100644 index 0000000..5ca4d16 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/constant/MKDoc.java @@ -0,0 +1,56 @@ +package org.springblade.thirdparty.mk.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 文档类型 + * myDraft 我的起草 + * myCreated 我发起的 + * myCreatedAndDraft 我发起的和我起草的 + * myApproving 我的待审 + * myApproved 我的已审 + * myReading 我的待阅 + * myReaded 我的已阅 + * myFocus 我关注的 + * myAll 我所有的( 含我发起的,我审批的,待我审的) + * @author bfhuange + * @since 2025/4/2 + */ +@AllArgsConstructor +@Getter +public enum MKDoc { + /** + * 我的待审 + */ + APPROVING("myApproving", "我的待审"), + /** + * 我的已审 + */ + APPROVED("myApproved", "我的已审"), + /** + * 我的待阅 + */ + READING("myReading", "我的待阅"), + /** + * 我的已阅 + */ + READ("myReaded", "我的已阅"), + /** + * 我发起的 + */ + CREATE("myCreated", "我发起的"), + /** + * 我参与的 + */ + RELATED("myRelated", "我参与的"), + ; + /** + * 编码 + */ + private final String code; + /** + * 名称 + */ + private final String name; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/exception/MKException.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/exception/MKException.java new file mode 100644 index 0000000..b693ea2 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/exception/MKException.java @@ -0,0 +1,17 @@ +package org.springblade.thirdparty.mk.exception; + +/** + * mk 接口异常 + * @author bfhuange + * @date 2024/9/13 + */ +public class MKException extends RuntimeException { + + public MKException(String message) { + super(message); + } + + public MKException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/exception/MKExceptionTranslator.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/exception/MKExceptionTranslator.java new file mode 100644 index 0000000..6f4280f --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/exception/MKExceptionTranslator.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.mk.exception; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.api.ResultCode; +import org.springblade.core.tool.utils.Func; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author bfhuange + * @since 2025/3/29 + */ +@AllArgsConstructor +@Slf4j +@RestControllerAdvice +public class MKExceptionTranslator { + + @ExceptionHandler(MKException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public R handleError(MKException e) { + log.error("mk异常", e); + return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage())); + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/feign/IMKClient.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/feign/IMKClient.java new file mode 100644 index 0000000..75dbed4 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/feign/IMKClient.java @@ -0,0 +1,170 @@ +package org.springblade.thirdparty.mk.feign; + +import feign.Response; +import org.springblade.thirdparty.mk.config.MKFeignClientConfig; +import org.springblade.thirdparty.mk.pojo.dto.*; +import org.springblade.thirdparty.mk.pojo.dto.approval.MKApprovalDTO; +import org.springblade.thirdparty.mk.pojo.dto.approval.MKProcessDTO; +import org.springblade.thirdparty.mk.pojo.vo.*; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * MK接口 + * @author bfhuange + * @date 2024/9/11 + */ +@FeignClient(name = "MK", url = "${thirdParty.mk.baseUrl}", configuration = MKFeignClientConfig.class) +public interface IMKClient { + + /** + * 获取mk token + * @param param + * @return + */ + @PostMapping("${thirdParty.mk.getTokenUrl:/authapi/getToken}") + MKTokenVO getToken(@RequestBody MKTokenDTO param); + + /** + * 流程提交接口 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.processSubmitUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/submit}") + MKResultVO processSubmit(@RequestBody MKProcessCreateDTO param, @RequestParam("access_token") String accessToken); + + /** + * 流程执行接口 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.processExecuteUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/execute}") + MKResultVO processExecute(@RequestBody MKProcessExecuteDTO param, @RequestParam("access_token") String accessToken); + + /** + * 流程删除接口 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.processDeleteUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/delete}") + MKResultVO processDelete(@RequestBody MKProcessDeleteDTO param, @RequestParam("access_token") String accessToken); + + /** + * 获取流程当前节点详情 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.getCurrentNodesUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getCurNodesInfo}") + MKResultVO> getCurrentNodes(@RequestBody MKCurrentNodesDTO param, @RequestParam("access_token") String accessToken); + + /** + * 获取节点处理人信息 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.getNodeHandlersUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getNodeHandlerInfos}") + MKResultVO getNodeHandlers(@RequestBody MKNodeHandlersDTO param, @RequestParam("access_token") String accessToken); + + /** + * 根据流程模板获取该模板下所有的人工节点信息 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.getManualNodeUrl:/openapi/sys-lbpm/sysLbpmTemplate/openSupport/getManualNode}") + MKResultVO> getManualNode(@RequestBody MKManualNodeDTO param, @RequestParam("access_token") String accessToken); + + //===========================人员组织推送接口============================================ + + /** + * 推送公司部门 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.pushOrgDeptUrl:/openapi/sys-org/v2/push/orgDept}") + MKResultVO pushOrgDept(@RequestBody MKPushDTO param, @RequestParam("access_token") String accessToken); + + /** + * 推送人员 + * @param param + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.pushPersonUrl:/openapi/sys-org/v2/push/person}") + MKResultVO pushPerson(@RequestBody MKPushDTO param, @RequestParam("access_token") String accessToken); + + //===========================认证相关接口======================================== + + /** + * 获取oauth 认证code + * @param param + */ + @PostMapping("${thirdParty.mk.getOAuthCodeUrl:/openapi/sys-oauth/openOauth/getCode}") + MKResultVO getOAuthCode(@RequestBody MKOAuthCodeDTO param, @RequestParam("access_token") String accessToken); + + /** + * 获取oauth token + * @param param + */ + @PostMapping("${thirdParty.mk.getOAuthTokenUrl:/openapi/sys-oauth/openOauth/getToken}") + MKResultVO getOAuthToken(@RequestBody MKOAuthTokenDTO param, @RequestParam("access_token") String accessToken); + + /** + * 获取oauth 用户信息 + * @param param + * @return + */ + @PostMapping("${thirdParty.mk.getOAuthUserInfoUrl:/openapi/sys-oauth/openOauth/getUserInfo}") + MKResultVO getOAuthUserInfo(@RequestBody MKOAuthUserDTO param, @RequestParam("access_token") String accessToken); + + //===========================审批记录相关接口======================================== + /** + * 流程审批意见信息API - 获取审批意见接口 + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.queryAuditNotesUrl:/openapi/sys-lbpm/lbpmAuditNote/openSupport/listNote}") + MKResultVO> queryAuditNotes(@RequestBody MKAuditNoteDTO param, @RequestParam("access_token") String accessToken); + + /** + * 流程卡片操作API- 获取抄送人列表 + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.querySenderListUrl:/openapi/sys-lbpm/sysLbpmProcessCard/openSupport/getSenderList}") + MKResultVO> querySenderList(@RequestBody MKSenderDTO param, @RequestParam("access_token") String accessToken); + + /** + * 附件文件流操作开放API- 附件下载 + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.downloadFileUrl:/openapi/sys-attach/fileStream/download}") + Response downloadFile(@RequestBody MKFileDTO param, @RequestParam("access_token") String accessToken); + + //===========================待办相关接口======================================== + /** + * 审批中心-流程门户API-审批中心查询列表 + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.queryApprovalListUrl:/openapi/lbpm-approval/lbpmApproval/portal/list}") + MKResultVO> queryApprovalList(@RequestBody MKApprovalDTO param, @RequestParam("access_token") String accessToken); + /** + * 流程实例操作API-列表 + * @param accessToken + * @return + */ + @PostMapping("${thirdParty.mk.queryProcessListUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/list}") + MKResultVO> queryProcessList(@RequestBody MKProcessDTO param, @RequestParam("access_token") String accessToken); +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAdditionOperationParameterDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAdditionOperationParameterDTO.java new file mode 100644 index 0000000..f8deac1 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAdditionOperationParameterDTO.java @@ -0,0 +1,32 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 附加操作信息 + * + * @author linbb + */ +@Data +public class MKAdditionOperationParameterDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 操作类型 + */ + private String operationType; + + /** + * 操作身份 + */ + private String operationIdentity; + + /** + * 操作参数 + */ + private String parameter; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAuditNoteDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAuditNoteDTO.java new file mode 100644 index 0000000..7796293 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAuditNoteDTO.java @@ -0,0 +1,26 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +@AllArgsConstructor +@Data +public class MKAuditNoteDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 登录账号 登录用户账号/手机号 + */ + private String loginName; + /** + * 流程实例id + */ + private String processInstanceId; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAuxiliaryDeptDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAuxiliaryDeptDTO.java new file mode 100644 index 0000000..a36a014 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKAuxiliaryDeptDTO.java @@ -0,0 +1,25 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk 辅部门参数 + * @author bfhuange + * @date 2024/9/24 + */ +@Data +public class MKAuxiliaryDeptDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 对象主键 + */ + private String fdId; + /** + * 对象显示名 + */ + private String fdName; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKCurrentNodesDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKCurrentNodesDTO.java new file mode 100644 index 0000000..302ed0a --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKCurrentNodesDTO.java @@ -0,0 +1,24 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @date 2024/9/19 + */ +@Data +public class MKCurrentNodesDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 流程实例id + */ + private String processInstanceId; + /** + * 登录名 + */ + private String loginName; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKFileDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKFileDTO.java new file mode 100644 index 0000000..f6106d3 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKFileDTO.java @@ -0,0 +1,24 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +@AllArgsConstructor +@Data +public class MKFileDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 附件id + */ + @JsonProperty("fdId") + private String fileId; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKManualNodeDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKManualNodeDTO.java new file mode 100644 index 0000000..0b8223e --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKManualNodeDTO.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 人工节点查询参数 + * @author bfhuange + * @since 2025/3/21 + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +public class MKManualNodeDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模板id + */ + @JsonProperty("fdId") + private String templateId; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKNodeHandlersDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKNodeHandlersDTO.java new file mode 100644 index 0000000..d099c96 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKNodeHandlersDTO.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @date 2024/9/19 + */ +@Data +public class MKNodeHandlersDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 流程实例id + */ + private String processInstanceId; + /** + * 登录名 + */ + private String loginName; + /** + * 节点(定义)ID + */ + private String nodeId; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthCodeDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthCodeDTO.java new file mode 100644 index 0000000..da449cb --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthCodeDTO.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 获取oauth 认证code参数 + * @author bfhuange + * @date 2024/9/27 + */ +@Data +public class MKOAuthCodeDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 申请的应⽤Id 认证 + */ + private String appId; + /** + * 应⽤秘钥 + */ + private String appSecret; + /** + * 登录名 + */ + private String loginName; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthTokenDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthTokenDTO.java new file mode 100644 index 0000000..1a9976c --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthTokenDTO.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 获取oauth token参数 + * @author bfhuange + * @date 2024/9/14 + */ +@Data +public class MKOAuthTokenDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 申请的应⽤Id 认证 + */ + private String appId; + /** + * 应⽤秘钥 + */ + private String appSecret; + /** + * 授权code,code使⽤⼀次之后失效 + */ + private String code; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthUserDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthUserDTO.java new file mode 100644 index 0000000..5829717 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKOAuthUserDTO.java @@ -0,0 +1,23 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk oauth 获取用户信息参数 + * @author bfhuange + * @date 2024/9/14 + */ +@AllArgsConstructor +@Data +public class MKOAuthUserDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * token + */ + private String token; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPageDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPageDTO.java new file mode 100644 index 0000000..9f1bdb4 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPageDTO.java @@ -0,0 +1,25 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk 分页参数 + * @author bfhuange + * @since 2025/2/24 + */ +@Data +public class MKPageDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 从第?条开始查询,起始值为0 + */ + private Integer offset = 0; + /** + * 每页?条 + */ + private Integer pageSize = 20; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessCreateDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessCreateDTO.java new file mode 100644 index 0000000..8f7b9c1 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessCreateDTO.java @@ -0,0 +1,47 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk 流程创建参数 + * @author bfhuange + * @date 2024/9/11 + */ +@Data +public class MKProcessCreateDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 表单实例id,业务id + */ + private String formInstanceId; + /** + * 标题 + */ + private String subject; + /** + * 提交人 登录用户账号/手机号 + */ + private String submitIdentity; + /** + * 登录账号 登录用户账号/手机号 + */ + private String loginName; + /** + * 模板编码 ProcessTypeEnum#templateCode + */ + private String templateCode; + /** + * 默认表单值 + */ + private Object formValues; + /** + * 临时变量 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Object tempVarData; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessDeleteDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessDeleteDTO.java new file mode 100644 index 0000000..3233094 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessDeleteDTO.java @@ -0,0 +1,25 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk 流程删除参数 + * @author bfhuange + * @since 2024/11/26 + */ +@Data +public class MKProcessDeleteDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 登录账号 登录用户账号/手机号 + */ + private String loginName; + /** + * 流程实例id + */ + private String processInstanceId; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessExecuteDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessExecuteDTO.java new file mode 100644 index 0000000..68997b7 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKProcessExecuteDTO.java @@ -0,0 +1,79 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk 流程执行参数 + * @author bfhuange + * @date 2024/9/26 + */ +@Data +public class MKProcessExecuteDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 表单实例id,业务id + */ + private String formInstanceId; + /** + * 登录账号 登录用户账号/手机号 + */ + private String loginName; + /** + * 操作类型 + */ + private String operationType; + + //==========================mk 流程执行需要的参数,前端透传========================= + /** + * 流程标题 + */ + private String subject; + /** + * 任务ID + */ + private String taskId; + /** + * 任务类型 + */ + private String activityType; + /** + * 操作详细参数(json) + */ + private String parameter; + /** + * 流程实例ID + */ + private String processId; + /** + * 操作标识(相同操作类型和相同操作身份可能存在多个操作配置) + */ + private String operationId; + /** + * 操作身份 + */ + private String operationIdentity; + /** + * 附加操作参数信息 + */ + private List additionParameters; + /** + * 表单实例Model Name + */ + private String formInstanceModel; + /** + * 业务表单字段值集合 + */ + // private Map formValues; + private Object formValues; + /** + * 临时变量 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Object tempVarData; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushDTO.java new file mode 100644 index 0000000..24f217a --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushDTO.java @@ -0,0 +1,30 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk 推送参数 + * @author bfhuange + * @date 2024/9/24 + */ +@Data +public class MKPushDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 如果不传,默认一次性推送,mk这边生成批次,如果一定量数据分页推送,则第三方生成批次号,批次号唯一 + */ + private String batchId; + /** + * 结束标识 : true-当前推送完毕,false-还没推送完成。一次性推送的话,就传 true + */ + private boolean finish = true; + /** + * 推送的数据主体 + */ + private List message; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushOrgDeptDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushOrgDeptDTO.java new file mode 100644 index 0000000..72657ec --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushOrgDeptDTO.java @@ -0,0 +1,144 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk 推送机构部门参数 + * @author bfhuange + * @date 2024/9/24 + */ +@Data +public class MKPushOrgDeptDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + //=================必填项开始================ + /** + * 唯一标识 必填,不能保证唯一的情况下,建议id前面拼接上组织类型。 例:org:1111,dept:22222,person:333333,group:555555,post:6666666 进阶情况,组织来源多个不同系统,还可以拼接上来源系统唯一id + */ + private String fdId; + /** + * 是否有效,该属性决定该组织架构是否删除 必填 + */ + private Boolean fdIsAvailable; + /** + * 名称 必填 + */ + private String fdName; + /** + * 组织架构类型: org-机构,dept-部门 + */ + private String fdOrgType; + /** + * 来源系统标识:例如:EKP + */ + private String fdSourceSystem; + //=================必填项结束================ + + /** + * 所在部门-主部门,只能一个,有值情况:当type为org,dept,post,person时,为空表示在根节点 + */ + private String fdParent; + /** + * 编号 + */ + private String fdNo; + /** + * 排序号 + */ + private Integer fdOrder; + + /** + * 映射MK的fdId,用来和Mk数据进行绑定 + */ + private String fdElementId; + /** + * 指定同步到mk的哪个节点下,有parent则以parent为准,没有则以mkParent为准。只能选择机构和部门,同步对象如果是机构,则mkParent也只能是机构,机构不能挂部门下 + */ + private String mkParent; + /** + * 部门领导,有值情况:当type为org,dept,post + */ + private String fdThisLeader; + /** + * 置空属性:字段传空过来,不想Mk这边也更新为空?可以指定这个属性。包含的字段如果为空则会被置为空,不包含的如果为空则不会更新。例如:["fdName","fdNo","fdEmail"] + */ + private List nullValueProps; + + //=======================一般不用的字段开始========================= + /** + * 机构简称 + */ + private String fdAbbreviation; + /** + * 地址1 + */ + private String fdAddress1; + /** + * 地址2 + */ + private String fdAddress2; + /** + * 管理员,例:["113","43424",..] + */ + private List fdAdmins; + /** + * 营业执照编号 + */ + private String fdBusinessLicenseNo; + /** + * 群组分类id,有值情况:当type为group时 + */ + private String fdCategoryName; + /** + * 扩展属性,未定义的字段并且需要同步的可以传到此json对象中,同步前需要去后台管理增加对应key值的扩展字段。例:{"birthDate":"1900‐01‐01","education":"本科","isSocialRecruitment":"true"} + */ + private Object fdCustomProps; + /** + * 说明 + */ + private String fdDesc; + /** + * 外部组织显示位置:默认情况是内部组织都在行政组织树,外部组织都在生态组织树。组织架构开启一体化后,外部组织既可以出现在行政树下面也可以出现在生态组织树下。但行政里面的外部组织必须要有顶级机构。是否显示在生态组织目录:true/null-显示在生态组织目录,false-显示在行政组织 + */ + private Boolean fdEosShowType; + /** + * 外部组织分类code:MK有外部组织分类,需要提前在管理端建好 + */ + private String fdExternalCode; + /** + * 是否外部组织:默认不填表示内部组织,true表示外部组织,外部组织需要externalCode + */ + private Boolean fdIsExternal; + /** + * 关键字 + */ + private String fdKeyword; + /** + * 多语言字段:例:{"fdNameUs":"Tom","fdNameCn":"张三"} + */ + private Object fdLangProps; + /** + * 是否法人 + */ + private Boolean fdLegalPerson; + /** + * 上级领导,有值情况:当type为org,dept + */ + private String fdSuperLeader; + /** + * 多维分类编号,当fdOrgType为org(机构),dept(部门) 时生效,表示当前组织/部门是该多维分类下的顶层组织。注:后续不允许变更。 + */ + private String fdVirtualizeNo; + /** + * 可见性:fdVisibleType:1-仅自己 2-所在组织同级及下级 3-指定组织/人员:例: {"fdVisibleType":1,"visibleOrgList":[{"fdId":"1feiglp6sw132weiw37cucsm1kqoe29hu4w0","fdName":"刘芬"}]} + */ + private Object fdVisible; + /** + * 机构网站地址 + */ + private String fdWebAddress; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushPersonDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushPersonDTO.java new file mode 100644 index 0000000..236f5de --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKPushPersonDTO.java @@ -0,0 +1,167 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk 推送人员参数 + * @author bfhuange + * @date 2024/9/24 + */ +@Data +public class MKPushPersonDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + //=================必填项开始================ + /** + * 唯一标识 必填,不能保证唯一的情况下,建议id前面拼接上组织类型。 例:org:1111,dept:22222,person:333333,group:555555,post:6666666 进阶情况,组织来源多个不同系统,还可以拼接上来源系统唯一id + */ + private String fdId; + /** + * 是否有效,该属性决定该组织架构是否删除 必填 + */ + private Boolean fdIsAvailable; + /** + * 组织类型为用户时,登录名,有此信息且必填 + */ + private String fdLoginName; + /** + * 名称 必填 + */ + private String fdName; + /** + * 来源系统标识:例如:EKP + */ + private String fdSourceSystem; + //=================必填项结束================ + + /** + * 辅部门,可以多个,只有用户才传此值,不能和主部门重复:例:[{"fdId":"222222222","fdName":"A部门"},{"fdId":"333333333","fdName":"B部门"}] + */ + private List fdAuxiliaryDept; + /** + * 映射MK的fdId,用来和Mk数据进行绑定 + */ + private String fdElementId; + /** + * 手机号 + */ + private String fdMobileNo; + /** + * 编号 + */ + private String fdNo; + /** + * 所在部门-主部门,只能一个,有值情况:当type为org,dept,post,person时,为空表示在根节点 + */ + private String fdParent; + /** + * 排序号 + */ + private Integer fdOrder; + /** + * 密码,Md5加密后的信息,可以为空 + */ + private String fdPassword; + /** + * 性别,M表示男性,F表示女性,UN-未知 + */ + private String fdSex; + /** + * 指定同步到mk的哪个节点下,有parent则以parent为准,没有则以mkParent为准。只能选择机构和部门,同步对象如果是机构,则mkParent也只能是机构,机构不能挂部门下 + */ + private String mkParent; + /** + * 置空属性:字段传空过来,不想Mk这边也更新为空?可以指定这个属性。包含的字段如果为空则会被置为空,不包含的如果为空则不会更新。例如:["fdName","fdNo","fdEmail"] + */ + private List nullValueProps; + + //=======================一般不用的字段开始========================= + /** + * 群组分类id,有值情况:当type为group时 + */ + private String fdCategoryName; + /** + * 扩展属性,未定义的字段并且需要同步的可以传到此json对象中,同步前需要去后台管理增加对应key值的扩展字段。例:{"birthDate":"1900‐01‐01","education":"本科","isSocialRecruitment":"true"} + */ + private Object fdCustomProps; + /** + * 默认语言 + */ + private String fdDefaultLang; + /** + * 说明 + */ + private String fdDesc; + /** + * 邮件地址 + */ + private String fdEmail; + /** + * 外部组织显示位置:默认情况是内部组织都在行政组织树,外部组织都在生态组织树。组织架构开启一体化后,外部组织既可以出现在行政树下面也可以出现在生态组织树下。但行政里面的外部组织必须要有顶级机构。是否显示在生态组织目录:true/null-显示在生态组织目录,false-显示在行政组织 + */ + private Boolean fdEosShowType; + /** + * 外部组织分类code:MK有外部组织分类,需要提前在管理端建好 + */ + private String fdExternalCode; + /** + * 是否外部组织:默认不填表示内部组织,true表示外部组织,外部组织需要externalCode + */ + private Boolean fdIsExternal; + /** + * 关键字 + */ + private String fdKeyword; + /** + * 多语言字段:例:{"fdNameUs":"Tom","fdNameCn":"张三"} + */ + private Object fdLangProps; + /** + * 职级名称 + */ + private String fdLevelName; + /** + * 职级oriId + */ + private String fdLevelOriId; + /** + * 主岗,只有一个 + */ + private String fdMainPost; + /** + * 昵称 + */ + private String fdNickName; + /** + * 辅岗,可以多个,例:["cbda73","bac67843"..] + */ + private List fdPosts; + /** + * 短号 + */ + private String fdShortNo; + /** + * 职务名称 + */ + private String fdStaffingLevelName; + /** + * 职务oriId + */ + private String fdStaffingLevelOriId; + /** + * 多维分类编号,当fdOrgType为org(机构),dept(部门) 时生效,表示当前组织/部门是该多维分类下的顶层组织。注:后续不允许变更。 + */ + private String fdVirtualizeNo; + /** + * 可见性:fdVisibleType:1-仅自己 2-所在组织同级及下级 3-指定组织/人员:例: {"fdVisibleType":1,"visibleOrgList":[{"fdId":"1feiglp6sw132weiw37cucsm1kqoe29hu4w0","fdName":"刘芬"}]} + */ + private Object fdVisible; + /** + * 办公电话 + */ + private String fdWorkPhone; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKSenderDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKSenderDTO.java new file mode 100644 index 0000000..b99a58d --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKSenderDTO.java @@ -0,0 +1,26 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class MKSenderDTO extends MKPageDTO { + /** + * 流程实例id + */ + private String processInstanceId; + /** + * 节点实例id + */ + private String nodeInstanceId; + + public MKSenderDTO(String processInstanceId, String nodeInstanceId) { + this.processInstanceId = processInstanceId; + this.nodeInstanceId = nodeInstanceId; + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKTokenDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKTokenDTO.java new file mode 100644 index 0000000..306ea1a --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/MKTokenDTO.java @@ -0,0 +1,25 @@ +package org.springblade.thirdparty.mk.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 获取token参数 + * @author bfhuange + * @date 2024/9/11 + */ +@Data +public class MKTokenDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 应用key + */ + private String appKey; + /** + * 应用secret + */ + private String appSecret; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKApprovalConditionDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKApprovalConditionDTO.java new file mode 100644 index 0000000..2c12e20 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKApprovalConditionDTO.java @@ -0,0 +1,86 @@ +package org.springblade.thirdparty.mk.pojo.dto.approval; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; +import org.springblade.thirdparty.mk.support.MultiKeySerializer; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk审批中心查询条件 + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKApprovalConditionDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 单据类型 + */ + private MKConditionDTO mydoc; + /** + * 关键字 + */ + @JsonSerialize(using = MultiKeySerializer.class) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List keyword; + /** + * 申请时间 + */ + @JsonProperty("fdStartTime") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO startTime; + /** + * 接收时间 + */ + @JsonProperty("fdReceiveTime") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO receiveTime; + /** + * 模板名称 + */ + @JsonProperty("fdTemplateName") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO templateName; + + //======================已处理参数================================ + /** + * 流程状态 + */ + @JsonProperty("fdStatus") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO status; + /** + * 任务结束时间 + */ + @JsonProperty("fdFinishTime") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO finishTime; + /** + * 最后处理时间(待审任务) + */ + @JsonProperty("fdLastHandleTime") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO lastHandleTime; + + //======================已阅参数================================ + /** + * 阅读时间 + */ + @JsonProperty("fdReadTime") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO readTime; + + //======================我参与的参数================================ + /** + * 创建时间 + */ + @JsonProperty("fdCreateTime") + @JsonInclude(JsonInclude.Include.NON_NULL) + private MKConditionDTO createTime; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKApprovalDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKApprovalDTO.java new file mode 100644 index 0000000..0d6bb94 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKApprovalDTO.java @@ -0,0 +1,70 @@ +package org.springblade.thirdparty.mk.pojo.dto.approval; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import org.springblade.thirdparty.mk.pojo.dto.sort.ISort; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk审批中心查询参数 + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKApprovalDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 查询条件 + */ + private MKApprovalConditionDTO conditions; + /** + * 登录名 + */ + private String loginName; + /** + * 获取内容 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean content; + /** + * 获取条目数 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean count; + /** + * 获取总数时最大条目数 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Integer maxCount; + /** + * 从第?条开始查询,起始值为0 + */ + private Integer offset; + /** + * 第?页 + */ + private Integer pageNo; + /** + * 每页?条 + */ + private Integer pageSize; + /** + * 排序列 + */ + private ISort sorts; + /** + * 返回列 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List columns; + + public void setPage(Integer pageNo, Integer pageSize) { + this.pageNo = pageNo; + this.pageSize = pageSize; + this.offset = (this.pageNo -1) * this.pageSize; + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKConditionDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKConditionDTO.java new file mode 100644 index 0000000..a46e4bc --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKConditionDTO.java @@ -0,0 +1,50 @@ +package org.springblade.thirdparty.mk.pojo.dto.approval; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 条件 + * @author bfhuange + * @since 2025/4/2 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class MKConditionDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 相等 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("$eq") + private String eq; + /** + * 大于等于 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("$gte") + private String gte; + /** + * 小于等于 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("$lte") + private String lte; + /** + * 包含 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonProperty("$contains") + private String contains; + +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKProcessDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKProcessDTO.java new file mode 100644 index 0000000..67e813e --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/approval/MKProcessDTO.java @@ -0,0 +1,124 @@ +package org.springblade.thirdparty.mk.pojo.dto.approval; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk流程列表查询参数 + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKProcessDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 登录名 + */ + private String loginName; + /** + * 查询数据模型 myAll(我的所有),myCreated(我创建的),myApproving(我的待办),myApproved(我的已办),myFollowed(我跟踪的),myReading(我待阅读),myReaded(我已阅读),myTransfer(我的转办),myCommunicate(我的沟通),myAuthorization(我的授权),my,myManage(我有特权人权限的) + */ + private String mydoc; + /** + * 流程状态 + */ + private String status; + /** + * 搜索关键字 + */ + private String keyword; + /** + * 创建时间:起始 + */ + private Long createBeginTime; + /** + * 创建时间:截止 + */ + private Long createEndTime; + /** + * 处理时间筛选(开始) + */ + @JsonProperty("fdHandleStartTime") + private Long handleStartTime; + /** + * 处理时间筛选(结束) + */ + @JsonProperty("fdHandleEndTime") + private Long handleEndTime; + /** + * 阅读时间筛选(开始) + */ + @JsonProperty("fdReadStartTime") + private Long readStartTime; + /** + * 阅读时间筛选(结束) + */ + @JsonProperty("fdReadEndTime") + private Long readEndTime; + /** + * 接收时间筛选(开始) + */ + @JsonProperty("fdReceiveStartTime") + private Long receiveStartTime; + /** + * 接收时间筛选(结束) + */ + @JsonProperty("fdReceiveEndTime") + private Long receiveEndTime; + /** + * 当前处理人 ','隔开, 支持Mk系统的组织架构id和loginName + */ + private String curHandlerIds; + /** + * 当前节点名称 + */ + @JsonProperty("fdCurNodeName") + private String curNodeName; + /** + * 创建人, 支持Mk系统的组织架构id和loginName + */ + @JsonProperty("fdCreator") + private String creator; + /** + * 从第?条开始查询,起始值为0 + */ + private Integer offset; + /** + * 第?页 + */ + private Integer pageNo; + /** + * 每页?条 + */ + private Integer pageSize; + /** + * 创建时间正序(true)/倒序(false) + */ + private Boolean asc = false; + /** + * 阅读时间正序(true)/倒序(false) + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean readTimeAsc; + /** + * 接收时间正序(true)/倒序(false) + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean receiveTimeAsc; + /** + * 处理时间正序(true)/倒序(false) + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean handleTimeAsc; + + public void setPage(Integer pageNo, Integer pageSize) { + this.pageNo = pageNo; + this.pageSize = pageSize; + this.offset = (this.pageNo -1) * this.pageSize; + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/ISort.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/ISort.java new file mode 100644 index 0000000..4b48332 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/ISort.java @@ -0,0 +1,8 @@ +package org.springblade.thirdparty.mk.pojo.dto.sort; + +/** + * @author bfhuange + * @since 2025/4/2 + */ +public interface ISort { +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKApprovedSortDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKApprovedSortDTO.java new file mode 100644 index 0000000..0140de5 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKApprovedSortDTO.java @@ -0,0 +1,24 @@ +package org.springblade.thirdparty.mk.pojo.dto.sort; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springblade.thirdparty.mk.constant.MKConstant; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 已处理排序 + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKApprovedSortDTO implements Serializable, ISort { + @Serial + private static final long serialVersionUID = 1L; + /** + * 结束时间 + */ + @JsonProperty("fdFinishTime") + private String finishTime = MKConstant.Sort.DESC; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKApprovingSortDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKApprovingSortDTO.java new file mode 100644 index 0000000..fe256c0 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKApprovingSortDTO.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.mk.pojo.dto.sort; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springblade.thirdparty.mk.constant.MKConstant; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 待处理、待阅排序 + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKApprovingSortDTO implements Serializable, ISort { + @Serial + private static final long serialVersionUID = 1L; + /** + * 级别 + */ + @JsonProperty("fdLevel") + private String level = MKConstant.Sort.ASC; + /** + * 接收时间 + */ + @JsonProperty("fdReceiveTime") + private String receiveTime = MKConstant.Sort.DESC; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKCreateSortDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKCreateSortDTO.java new file mode 100644 index 0000000..2fe9011 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKCreateSortDTO.java @@ -0,0 +1,24 @@ +package org.springblade.thirdparty.mk.pojo.dto.sort; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springblade.thirdparty.mk.constant.MKConstant; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 我发起的排序 + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKCreateSortDTO implements Serializable, ISort { + @Serial + private static final long serialVersionUID = 1L; + /** + * 创建时间 + */ + @JsonProperty("fdCreateTime") + private String createTime = MKConstant.Sort.DESC; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKReadSortDTO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKReadSortDTO.java new file mode 100644 index 0000000..abb3219 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/dto/sort/MKReadSortDTO.java @@ -0,0 +1,24 @@ +package org.springblade.thirdparty.mk.pojo.dto.sort; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springblade.thirdparty.mk.constant.MKConstant; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 已阅排序 + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKReadSortDTO implements Serializable, ISort { + @Serial + private static final long serialVersionUID = 1L; + /** + * 已读时间 + */ + @JsonProperty("fdReadTime") + private String readTime = MKConstant.Sort.DESC; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAllHandlerVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAllHandlerVO.java new file mode 100644 index 0000000..3b1e22b --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAllHandlerVO.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * @author bfhuange + * @date 2024/9/20 + */ +@Data +public class MKAllHandlerVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 已审批的处理人 + */ + private List approvedHandlers; + /** + * 审批中的处理人 + */ + private List approvingHandlers; + /** + * 解析的处理人 + */ + private List parseHandlers; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKApprovalVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKApprovalVO.java new file mode 100644 index 0000000..71a07a7 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKApprovalVO.java @@ -0,0 +1,153 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKApprovalVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @JsonProperty("fdId") + private String id; + /** + * 流程所属应用 + */ + @JsonProperty("fdAppName") + private String appName; + /** + * 申请人名称 + */ + @JsonProperty("fdApplicantName") + private String applicantName; + /** + * 申请人登录名 + */ + @JsonProperty("fdApplicantLoginName") + private String applicantLoginName; + /** + * 发起人名称 + */ + @JsonProperty("fdCreator") + private String creator; + /** + * 处理人名称 + */ + @JsonProperty("fdHandlerName") + private String handlerName; + /** + * 当前处理人名称 + */ + @JsonProperty("fdCurrentHandler") + private String currentHandler; + /** + * 处理人登录名 + */ + @JsonProperty("fdHandlerLoginName") + private String handlerLoginName; + /** + * 节点名称 + */ + @JsonProperty("fdNodeName") + private String nodeName; + /** + * 流程id + */ + @JsonProperty("fdProcessId") + private String processId; + /** + * 创建时间 + */ + @JsonProperty("fdCreateTime") + private Date createTime; + /** + * 流程发起时间 + */ + @JsonProperty("fdStartTime") + private Date startTime; + /** + * 如果是待办:待办接收时间 如果是待阅:传阅接收时间 + */ + @JsonProperty("fdReceiveTime") + private Date receiveTime; + /** + * 任务结束时间 + */ + @JsonProperty("fdFinishTime") + private Date finishTime; + /** + * 阅读时间(待阅任务) + */ + @JsonProperty("fdReadTime") + private Date readTime; + /** + * 最后处理时间(待审任务) + */ + @JsonProperty("fdLastHandleTime") + private Date lastHandleTime; + /** + * 流程结束时间 + */ + @JsonProperty("fdProcessFinishTime") + private Date processFinishTime; + /** + * 流程状态 00 – 废弃、10 – 草稿、11 – 驳回 20 – 待审、21 – 异常、40 – 挂起 30 – 结束 + */ + @JsonProperty("fdStatus") + private String status; + /** + * 流程主题 + */ + @JsonProperty("fdSubject") + private String subject; + /** + * 模板编码 + */ + @JsonProperty("fdTemplateCode") + private String templateCode; + /** + * 任务id + */ + @JsonProperty("fdTaskId") + private String taskId; + /** + * 任务状态 20 - 激活、30 - 结束、40 - 挂起、50 - 自动跳过 + */ + @JsonProperty("fdTaskStatus") + private String taskStatus; + /** + * 任务标题 + */ + @JsonProperty("fdTaskSubject") + private String taskSubject; + /** + * 催办标记 + */ + @JsonProperty("fdUrgeTab") + private String urgeTab; + /** + * 任务类型 1待办,2待阅 + */ + @JsonProperty("fdTaskType") + private Integer taskType; + /** + * 待办优先级 + */ + @JsonProperty("fdLevel") + private Integer level; + /** + * 动态扩展属性 + */ + private MKDynamicPropsVO dynamicProps; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKApprovedHandlerVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKApprovedHandlerVO.java new file mode 100644 index 0000000..f4e1943 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKApprovedHandlerVO.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author bfhuange + * @date 2024/9/20 + */ +@Data +public class MKApprovedHandlerVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 名称 + */ + private String name; + /** + * 登录名 + */ + private String loginName; + /** + * 操作时间 + */ + private Date operationTime; + /** + * 操作名称 + */ + private String operationName; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAttachmentVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAttachmentVO.java new file mode 100644 index 0000000..cda15f4 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAttachmentVO.java @@ -0,0 +1,32 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +@Data +public class MKAttachmentVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + private String id; + /** + * 附件名称 + */ + private String name; + /** + * 附件类型 electronicSign 电子签名,attachment 附件 + */ + private String type; + /** + * 附件ID + */ + private String fileId; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAuditNoteVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAuditNoteVO.java new file mode 100644 index 0000000..d1233c7 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKAuditNoteVO.java @@ -0,0 +1,102 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +@Data +public class MKAuditNoteVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 记录主键 + */ + @JsonProperty("fdId") + private String id; + /** + * 处理人 + */ + @JsonProperty("fdHandler") + private String handler; + /** + * 操作 + */ + @JsonProperty("fdAction") + private String action; + /** + * 操作编码 + */ + @JsonProperty("fdActionCode") + private String actionCode; + /** + * 操作描述(系统操作) + */ + @JsonProperty("fdActionDesc") + private String actionDesc; + /** + * 操作名称 + */ + @JsonProperty("fdActionName") + private String actionName; + /** + * 处理意见 + */ + @JsonProperty("fdMessage") + private String message; + /** + * 创建时间 + */ + @JsonProperty("fdCreateTime") + private Date createTime; + /** + * 流程实例id + */ + @JsonProperty("fdProcessInstanceId") + private String processInstanceId; + /** + * 节点实例id + */ + @JsonProperty("fdNodeInstanceId") + private String nodeInstanceId; + /** + * 节点类型 + */ + @JsonProperty("fdNodeType") + private String nodeType; + /** + * 节点id + */ + @JsonProperty("fdNodeId") + private String nodeId; + /** + * 节点名称 + */ + @JsonProperty("fdNodeName") + private String nodeName; + /** + * 节点编号 + */ + @JsonProperty("fdNodeNumber") + private String nodeNumber; + /** + * 附件参数 + */ + private List attachmentParameter; + /** + * 流程附言 + */ + @JsonProperty("lbpmCommentPostscriptVOS") + private List processComments; + /** + * 机制数据 + */ + private MKMechanismsVO mechanisms; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKDynamicPropsVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKDynamicPropsVO.java new file mode 100644 index 0000000..c4754f8 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKDynamicPropsVO.java @@ -0,0 +1,23 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKDynamicPropsVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模板名称中文 + */ + @JsonProperty("fdTemplateNameCn") + private String templateNameCn; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKErrorVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKErrorVO.java new file mode 100644 index 0000000..cbe48bf --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKErrorVO.java @@ -0,0 +1,30 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk 返回结果错误堆栈 + * @author bfhuange + * @date 2024/9/24 + */ +@Data +public class MKErrorVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 服务名称 + */ + private String app; + /** + * 错误编码 + */ + private String code; + /** + * 错误描述 + */ + private String message; + +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKManualNodeVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKManualNodeVO.java new file mode 100644 index 0000000..cce3102 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKManualNodeVO.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 人工节点 + * @author bfhuange + * @since 2025/3/21 + */ +@Data +public class MKManualNodeVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 节点id + */ + private String nodeId; + /** + * 节点名称 + */ + private String nodeName; + /** + * 节点编号 + */ + private String nodeNumber; + /** + * 节点类型 + */ + private String nodeType; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKMechanismsVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKMechanismsVO.java new file mode 100644 index 0000000..4fd5036 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKMechanismsVO.java @@ -0,0 +1,21 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * @author bfhuange + * @since 2025/2/25 + */ +@Data +public class MKMechanismsVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 附件信息 + */ + private List attachment; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKMetaAttachmentVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKMetaAttachmentVO.java new file mode 100644 index 0000000..241664d --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKMetaAttachmentVO.java @@ -0,0 +1,31 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @since 2025/2/25 + */ +@Data +public class MKMetaAttachmentVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + @JsonProperty("fdId") + private String id; + /** + * 附件名称 + */ + @JsonProperty("fdFileName") + private String fileName; + /** + * 附件全名 + */ + private String fullName; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeHandlerOrgVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeHandlerOrgVO.java new file mode 100644 index 0000000..16c8aae --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeHandlerOrgVO.java @@ -0,0 +1,20 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @date 2024/9/20 + */ +@Data +public class MKNodeHandlerOrgVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 登录名 + */ + private String loginName; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeHandlerVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeHandlerVO.java new file mode 100644 index 0000000..a74cd61 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeHandlerVO.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author bfhuange + * @date 2024/9/20 + */ +@Data +public class MKNodeHandlerVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 处理人名称 + */ + private String handlerName; + /** + * 接收时间 + */ + private Date receiveTime; + /** + * 处理人组织信息 + */ + private MKNodeHandlerOrgVO fdHandlerOrgInfo; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeVO.java new file mode 100644 index 0000000..2ef4123 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKNodeVO.java @@ -0,0 +1,42 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk 节点信息 + * @author bfhuange + * @date 2024/9/20 + */ +@Data +public class MKNodeVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 节点id + */ + private String nodeId; + /** + * 节点名称 + */ + private String nodeName; + /** + * 节点编号 + */ + private String nodeNumber; + /** + * 流程实例id + */ + private String processInstanceId; + /** + * 节点实例id + */ + private String nodeInstanceId; + /** + * 节点处理人 + */ + private List nodeHandlers; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthCodeVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthCodeVO.java new file mode 100644 index 0000000..7376cf4 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthCodeVO.java @@ -0,0 +1,21 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk oauth 认证code响应参数 + * @author bfhuange + * @date 2024/9/27 + */ +@Data +public class MKOAuthCodeVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 会话code + */ + private String code; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthTokenVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthTokenVO.java new file mode 100644 index 0000000..e25437d --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthTokenVO.java @@ -0,0 +1,21 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk oauth token响应参数 + * @author bfhuange + * @date 2024/9/14 + */ +@Data +public class MKOAuthTokenVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * token + */ + private String token; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthUserVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthUserVO.java new file mode 100644 index 0000000..cccab92 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKOAuthUserVO.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * mk oauth 用户信息 + * @author bfhuange + * @date 2024/9/14 + */ +@Data +public class MKOAuthUserVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 登录名 + */ + private String loginName; + /** + * ⽤户名 + */ + private String name; + /** + * 编号信息 + */ + private String fdNo; + /** + * 部⻔信息 + */ + private String deptName; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKPageVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKPageVO.java new file mode 100644 index 0000000..864653f --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKPageVO.java @@ -0,0 +1,50 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +@NoArgsConstructor +@Data +public class MKPageVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 分页位移量 + */ + private Integer offset; + /** + * 每页数目 + */ + private Integer pageSize; + /** + * 总数 + */ + private Integer totalSize; + /** + * 返回内容 + */ + private List content; + + public MKPageVO(Integer totalSize) { + this.totalSize = totalSize; + this.content = Collections.emptyList(); + } + + /** + * 空的结果 + * @return + * @param + */ + public static MKPageVO empty() { + return new MKPageVO<>(0); + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKProcessCommentVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKProcessCommentVO.java new file mode 100644 index 0000000..bfc6887 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKProcessCommentVO.java @@ -0,0 +1,51 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 流程附言 + * @author bfhuange + * @since 2025/2/24 + */ +@Data +public class MKProcessCommentVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + private String id; + /** + * 附言内容 + */ + private String content; + /** + * 创建时间 + */ + private Date createTime; + /** + * 更新时间 + */ + private Date updateTime; + /** + * 用户id + */ + private String userId; + /** + * 处理人信息 + */ + private MKUserOrgVO userOrgInfo; + /** + * 附言附件 + */ + private List attachments; + /** + * 机制数据 + */ + private MKMechanismsVO mechanisms; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKProcessVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKProcessVO.java new file mode 100644 index 0000000..6474d8a --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKProcessVO.java @@ -0,0 +1,94 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * @author bfhuange + * @since 2025/4/2 + */ +@Data +public class MKProcessVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @JsonProperty("fdId") + private String id; + /** + * 发起人名称 + */ + @JsonProperty("fdCreator") + private String creator; + /** + * 当前处理人名称 + */ + @JsonProperty("fdCurrentHandler") + private String currentHandler; + /** + * 当前节点 - 仅我参与的 + */ + @JsonProperty("fdCurrentNode") + private String currentNode; + /** + * 流程id + */ + @JsonProperty("fdProcessId") + private String processId; + /** + * 创建时间 + */ + @JsonProperty("fdCreateTime") + private Date createTime; + /** + * 如果是待办:待办接收时间 如果是待阅:传阅接收时间 + */ + @JsonProperty("fdReceiveTime") + private Date receiveTime; + /** + * 阅读时间(待阅任务) + */ + @JsonProperty("fdReadTime") + private Date readTime; + /** + * 最后处理时间(待审任务) + */ + @JsonProperty("fdLastHandleTime") + private Date lastHandleTime; + /** + * 流程结束时间 + */ + @JsonProperty("fdProcessFinishTime") + private Date processFinishTime; + /** + * 流程状态 00 – 废弃、10 – 草稿、11 – 驳回 20 – 待审、21 – 异常、40 – 挂起 30 – 结束 + */ + @JsonProperty("fdStatus") + private String status; + /** + * 流程主题 + */ + @JsonProperty("fdSubject") + private String subject; + /** + * 模板编码 + */ + @JsonProperty("fdTemplateCode") + private String templateCode; + /** + * 模板名称 - 仅我参与的 + */ + @JsonProperty("fdTemplateName") + private String templateName; + /** + * 催办标记 + */ + @JsonProperty("fdUrgeTab") + private String urgeTab; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKResultVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKResultVO.java new file mode 100644 index 0000000..76273ec --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKResultVO.java @@ -0,0 +1,43 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import lombok.Data; +import org.springblade.core.tool.support.NoErrorFallback; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * mk 返回结果 + * @author bfhuange + * @date 2024/9/11 + */ +@Data +public class MKResultVO implements Serializable, NoErrorFallback { + @Serial + private static final long serialVersionUID = 1L; + /** + * 是否成功 + */ + private boolean success; + /** + * 响应码 + */ + private String code; + /** + * 消息 + */ + private String msg; + /** + * traceId + */ + private String traceId; + /** + * 数据 + */ + private T data; + /** + * 错误堆栈 + */ + private List errStacks; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKSenderVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKSenderVO.java new file mode 100644 index 0000000..23edbab --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKSenderVO.java @@ -0,0 +1,27 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 抄送人 + * @author bfhuange + * @since 2025/2/24 + */ +@Data +public class MKSenderVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + private String id; + /** + * 处理人名称 + */ + @JsonProperty("fdName") + private String name; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKTokenVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKTokenVO.java new file mode 100644 index 0000000..58093ff --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKTokenVO.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springblade.core.tool.support.NoErrorFallback; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @date 2024/9/11 + */ +@Data +public class MKTokenVO implements Serializable, NoErrorFallback { + @Serial + private static final long serialVersionUID = 1L; + /** + * token + */ + @JsonProperty("access_token") + private String accessToken; + /** + * 过期时间 + */ + @JsonProperty("expires_in") + private int expiresIn; + /** + * 错误码 + */ + @JsonProperty("errcode") + private int errCode; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKUserOrgVO.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKUserOrgVO.java new file mode 100644 index 0000000..d2e1fcd --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/pojo/vo/MKUserOrgVO.java @@ -0,0 +1,22 @@ +package org.springblade.thirdparty.mk.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +@Data +public class MKUserOrgVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 名称 + */ + @JsonProperty("fdName") + private String name; +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/service/IMKService.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/service/IMKService.java new file mode 100644 index 0000000..79c6efb --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/service/IMKService.java @@ -0,0 +1,148 @@ +package org.springblade.thirdparty.mk.service; + +import jakarta.servlet.http.HttpServletResponse; +import org.springblade.thirdparty.mk.pojo.dto.*; +import org.springblade.thirdparty.mk.pojo.dto.approval.MKApprovalDTO; +import org.springblade.thirdparty.mk.pojo.dto.approval.MKProcessDTO; +import org.springblade.thirdparty.mk.pojo.vo.*; + +import java.util.List; + +/** + * @author bfhuange + * @date 2024/9/11 + */ +public interface IMKService { + /** + * 提交流程 + * @param param + * @return + */ + String processSubmit(MKProcessCreateDTO param); + + /** + * 执行流程 + * @param param + * @return + */ + boolean processExecute(MKProcessExecuteDTO param); + + /** + * 撤回流程,起草人撤回 + * @param bizId + * @param loginName MK要的是手机号,不是登录名 + * @return + */ + boolean processRevoke(String bizId, String loginName); + + /** + * 删除流程 + * @param processInstanceId 流程实例id + * @param loginName MK要的是手机号,不是登录名 + * @return + */ + boolean processDelete(String processInstanceId, String loginName); + + /** + * 废弃流程,起草人废弃 + * @param bizId 业务id + * @param loginName MK要的是手机号,不是登录名 + * @return + */ + boolean processAbandon(String bizId, String loginName); + + /** + * 根据 mk code 获取mk账号 + * @param mkCode + * @return + */ + String getMKAccount(String mkCode); + + /** + * 获取流程当前节点详情 + * @param processInstanceId + * @param loginName + * @return + */ + List getCurrentNodes(String processInstanceId, String loginName); + + /** + * 获取节点处理人信息 + * @param processInstanceId + * @param loginName + * @param nodeId + * @return + */ + MKAllHandlerVO getNodeHandlers(String processInstanceId, String loginName, String nodeId); + + /** + * 推送机构部门 + * @param param + * @return + */ + boolean pushOrgDept(MKPushDTO param); + + /** + * 推送人员 + * @param param + * @return + */ + boolean pushPerson(MKPushDTO param); + + /** + * 获取mk审批链接 + * @param processInstanceId + * @param loginName + * @return + */ + String getMKApprovalUrl(String processInstanceId, String loginName); + + /** + * 查询流程审批记录 + * @param param + * @return + */ + List queryAuditNotes(MKAuditNoteDTO param); + + /** + * 查询抄送人列表 + * @param param + * @return + */ + List querySenderList(MKSenderDTO param); + + /** + * 下载文件 + * @param response + * @param fileId + */ + void downloadFile(HttpServletResponse response, String fileId); + + /** + * 下载文件 + * @param fileId + */ + String getFileBase64(String fileId); + + /** + * 根据流程模板id和节点id查询节点编号 + * @param templateId + * @param nodeId + * @return + */ + String getNodeNumber(String templateId, String nodeId); + + /** + * 查询审批中心列表 + * @param param + * @return + */ + MKPageVO queryApprovalList(MKApprovalDTO param); + + /** + * 查询流程列表,我参与的 待办 + * @param param + * @return + */ + MKPageVO queryProcessList(MKProcessDTO param); +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/service/impl/MKServiceImpl.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/service/impl/MKServiceImpl.java new file mode 100644 index 0000000..f0f3fb5 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/service/impl/MKServiceImpl.java @@ -0,0 +1,508 @@ +package org.springblade.thirdparty.mk.service.impl; + +import cn.hutool.json.JSONUtil; +import feign.Response; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.springblade.core.redis.cache.BladeRedis; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.StringPool; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.core.tool.utils.UrlUtil; +import org.springblade.thirdparty.mk.config.MKProperties; +import org.springblade.thirdparty.mk.constant.MKConstant; +import org.springblade.thirdparty.mk.exception.MKException; +import org.springblade.thirdparty.mk.feign.IMKClient; +import org.springblade.thirdparty.mk.pojo.dto.*; +import org.springblade.thirdparty.mk.pojo.dto.approval.MKApprovalDTO; +import org.springblade.thirdparty.mk.pojo.dto.approval.MKProcessDTO; +import org.springblade.thirdparty.mk.pojo.vo.*; +import org.springblade.thirdparty.mk.service.IMKService; +import org.springblade.thirdparty.mk.util.AssertUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author bfhuange + * @date 2024/9/11 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class MKServiceImpl implements IMKService { + + /** + * mk审批页面链接 + */ + private static final String APPROVAL_URL_FORMAT = "%s/web//#/current/sys-lbpm/lbpmIntegrate/formInFlow/view/%s?processInstanceId=%s"; + /** + * mk sso回调地址 + */ + private static final String SSO_CALLBACK_URL_FORMAT = "%s/data/sys-auth/ssoCallBack?mkCode=%s&redirectUrl=%s"; + + private final MKProperties properties; + private final IMKClient client; + private final BladeRedis bladeRedis; + + @Override + public String processSubmit(MKProcessCreateDTO param) { + if (param == null) { + log.warn("调用mk接口 创建并提交流程参数为空"); + return null; + } + return commonCall("创建并提交流程", token -> { + // 设置标题 + String actualSubject = getActualSubject(param.getSubject()); + param.setSubject(actualSubject); + log.info("调用mk提交流程开始"); + long start = System.currentTimeMillis(); + MKResultVO result = client.processSubmit(param, token); + long end = System.currentTimeMillis(); + log.info("调用mk提交流程结束 耗时:{}", end - start); + return result; + }, result -> Optional.ofNullable(result) + .map(MKResultVO::getData) + .orElse(null) + ); + } + + @Override + public boolean processExecute(MKProcessExecuteDTO param) { + if (param == null) { + log.warn("调用mk接口 执行流程参数为空"); + return false; + } + return commonCall("执行流程", token -> client.processExecute(param, token)); + } + + @Override + public boolean processRevoke(String bizId, String loginName) { + if (StringUtil.isBlank(bizId) || StringUtil.isBlank(loginName)) { + log.warn("调用mk接口 撤回流程参数为空"); + return false; + } + return commonCall("撤回流程", token -> { + MKProcessExecuteDTO param = new MKProcessExecuteDTO(); + param.setFormInstanceId(bizId); + param.setLoginName(loginName); + param.setOperationType(MKConstant.OPERATE_TYPE_REVOKE); + return client.processExecute(param, token); + }); + } + + @Override + public boolean processDelete(String processInstanceId, String loginName) { + if (StringUtil.isBlank(processInstanceId) || StringUtil.isBlank(loginName)) { + log.warn("调用mk接口 删除流程参数为空"); + return false; + } + return commonCall("删除流程", token -> { + MKProcessDeleteDTO param = new MKProcessDeleteDTO(); + param.setProcessInstanceId(processInstanceId); + param.setLoginName(loginName); + return client.processDelete(param, token); + }); + } + + @Override + public boolean processAbandon(String bizId, String loginName) { + if (StringUtil.isBlank(bizId) || StringUtil.isBlank(loginName)) { + log.warn("调用mk接口 废弃流程参数为空"); + return false; + } + return commonCall("废弃流程", token -> { + MKProcessExecuteDTO param = new MKProcessExecuteDTO(); + param.setFormInstanceId(bizId); + param.setLoginName(loginName); + param.setOperationType(MKConstant.OPERATE_TYPE_ABANDON); + return client.processExecute(param, token); + }); + } + + /** + * 公共调用方法 + * @param type + * @param function + * @return + */ + private boolean commonCall(String type, Function> function) { + return commonCall(type, function, Objects::nonNull); + } + + /** + * 公共调用方法 + * @param type + * @param function + * @return + */ + private R commonCall(String type, Function> function, Function, R> resultMapper) { + if (function == null) { + log.warn("实际调用接口为空"); + return resultMapper.apply(null); + } + String token = this.getToken(); + if (token == null) { + return resultMapper.apply(null); + } + try { + MKResultVO result = function.apply(token); + if (result != null && result.isSuccess()) { + return resultMapper.apply(result); + } + log.error("调用mk接口 {}异常响应参数:{}", type, JSONUtil.toJsonStr(result)); + String message = getMessage(result); + throw new MKException("调用mk接口" + type + "异常 " + message); + } catch (MKException e) { + throw e; + } catch (Exception e) { + log.error("调用mk接口 {}异常", type, e); + throw new MKException("调用mk接口" + type + "异常"); + } + } + + /** + * 获取消息 + * @param result + * @return + */ + private String getMessage(MKResultVO result) { + return result == null ? "" : result.getMsg(); + } + + /** + * 获取实际的标题,拼接配置的前缀 + * @param subject + * @return + */ + private String getActualSubject(String subject) { + if (StringUtil.isBlank(subject)) { + log.warn("调用mk接口 创建并提交流程标题为空"); + return subject; + } + String subjectPrefix = properties.getSubjectPrefix(); + if (StringUtil.isBlank(subjectPrefix)) { + return subject; + } + return subjectPrefix + subject; + } + + @Override + public String getMKAccount(String mkCode) { + if (!StringUtil.hasText(mkCode)) { + log.warn("获取mk账号参数为空"); + return null; + } + String accessToken = getToken(); + MKOAuthTokenDTO tokenParam = new MKOAuthTokenDTO(); + tokenParam.setAppId(properties.getOauthAppId()); + tokenParam.setAppSecret(properties.getOauthAppSecret()); + tokenParam.setCode(mkCode); + MKResultVO tokenResult = client.getOAuthToken(tokenParam, accessToken); + if (tokenResult == null || !tokenResult.isSuccess()) { + log.error("调用mk接口 获取oauth token 异常:{}", JSONUtil.toJsonStr(tokenResult)); + return null; + } + MKOAuthTokenVO tokenVO = tokenResult.getData(); + if (tokenVO == null || tokenVO.getToken() == null) { + log.error("获取oauth token 为空:{}", JSONUtil.toJsonStr(tokenResult)); + return null; + } + + String token = tokenVO.getToken(); + MKResultVO userResult = client.getOAuthUserInfo(new MKOAuthUserDTO(token), accessToken); + if (userResult == null || !userResult.isSuccess()) { + log.error("调用mk接口 获取oauth 用户信息异常:{}", JSONUtil.toJsonStr(userResult)); + return null; + } + MKOAuthUserVO userVO = userResult.getData(); + if (userVO != null) { + return userVO.getLoginName(); + } + log.error("获取oauth 用户信息为空:{}", JSONUtil.toJsonStr(userResult)); + return null; + } + + @Override + public List getCurrentNodes(String processInstanceId, String loginName) { + String accessToken = getToken(); + MKCurrentNodesDTO param = new MKCurrentNodesDTO(); + param.setProcessInstanceId(processInstanceId); + param.setLoginName(loginName); + MKResultVO> nodesResult = client.getCurrentNodes(param, accessToken); + log.info("调用mk接口 获取流程当前节点详情 :{}", JSONUtil.toJsonStr(nodesResult)); + if (nodesResult == null || !nodesResult.isSuccess()) { + log.error("调用mk接口 获取流程当前节点详情异常:{}", JSONUtil.toJsonStr(nodesResult)); + return null; + } + return nodesResult.getData(); + } + + @Override + public MKAllHandlerVO getNodeHandlers(String processInstanceId, String loginName, String nodeId) { + String accessToken = getToken(); + MKNodeHandlersDTO param = new MKNodeHandlersDTO(); + param.setProcessInstanceId(processInstanceId); + param.setLoginName(loginName); + param.setNodeId(nodeId); + MKResultVO nodeHandlers = client.getNodeHandlers(param, accessToken); + log.info("调用mk接口 获取节点处理人详情 :{}", JSONUtil.toJsonStr(nodeHandlers)); + if (nodeHandlers == null || !nodeHandlers.isSuccess()) { + log.error("调用mk接口 获取节点处理人详情异常:{}", JSONUtil.toJsonStr(nodeHandlers)); + return null; + } + return nodeHandlers.getData(); + } + + @Override + public boolean pushOrgDept(MKPushDTO param) { + String accessToken = getToken(); + MKResultVO resultVO = client.pushOrgDept(param, accessToken); + log.info("调用mk接口 推送机构部门结果 :{}", JSONUtil.toJsonStr(resultVO)); + if (resultVO == null || !resultVO.isSuccess()) { + log.error("调用mk接口 推送机构部门异常:{}", JSONUtil.toJsonStr(resultVO)); + return false; + } + return true; + } + + @Override + public boolean pushPerson(MKPushDTO param) { + String accessToken = getToken(); + MKResultVO resultVO = client.pushPerson(param, accessToken); + log.info("调用mk接口 推送人员结果 :{}", JSONUtil.toJsonStr(resultVO)); + if (resultVO == null || !resultVO.isSuccess()) { + log.error("调用mk接口 推送人员异常:{}", JSONUtil.toJsonStr(resultVO)); + return false; + } + return true; + } + + @Override + public String getMKApprovalUrl(String processInstanceId, String loginName) { + String accessToken = getToken(); + MKOAuthCodeDTO codeParam = new MKOAuthCodeDTO(); + codeParam.setAppId(properties.getOauthAppId()); + codeParam.setAppSecret(properties.getOauthAppSecret()); + codeParam.setLoginName(loginName); + MKResultVO codeResult = client.getOAuthCode(codeParam, accessToken); + if (codeResult == null || !codeResult.isSuccess()) { + log.error("调用mk接口 获取oauth 认证code 异常:{}", JSONUtil.toJsonStr(codeResult)); + String message = Optional.ofNullable(codeResult) + // 这个接口错误消息在 code,没有 msg + .map(MKResultVO::getCode) + .orElse(StringPool.EMPTY); + throw new MKException(message); + } + MKOAuthCodeVO codeVO = codeResult.getData(); + if (codeVO == null || codeVO.getCode() == null) { + log.error("获取oauth 认证code 为空:{}", JSONUtil.toJsonStr(codeVO)); + return null; + } + String code = codeVO.getCode(); + // 审批页面地址 + String approvalUrl = String.format(APPROVAL_URL_FORMAT, properties.getLoginUrl(), processInstanceId, processInstanceId); + // sso回调地址 + return String.format(SSO_CALLBACK_URL_FORMAT, properties.getLoginUrl(), code, UrlUtil.encode(approvalUrl)); + } + + @Override + public List queryAuditNotes(MKAuditNoteDTO param) { + AssertUtils.notNull(param, "参数不能为空"); + AssertUtils.notBlank(param.getLoginName(),"登录名不能为空"); + AssertUtils.notBlank(param.getProcessInstanceId(),"流程实例id不能为空"); + List notes = commonCall("查询审批记录", + token -> client.queryAuditNotes(param, token), + result -> Optional.ofNullable(result) + .map(MKResultVO::getData) + .orElse(null)); + if (CollectionUtil.isEmpty(notes)) { + return notes; + } + for (MKAuditNoteVO note : notes) { + // 处理审批意见的附件全名 + handleFullName(note.getAttachmentParameter(), note.getMechanisms()); + if (CollectionUtil.isNotEmpty(note.getProcessComments())) { + for (MKProcessCommentVO processComment : note.getProcessComments()) { + // 处理流程附言的附件全名 + handleFullName(processComment.getAttachments(), processComment.getMechanisms()); + } + } + } + return notes; + } + + /** + * 处理附件全名 + * @param attachments + * @param mechanisms + */ + private void handleFullName(List attachments, MKMechanismsVO mechanisms) { + if (CollectionUtil.isNotEmpty(attachments) && mechanisms != null && CollectionUtil.isNotEmpty(mechanisms.getAttachment())) { + Map fileIdFullNameMap = mechanisms.getAttachment().stream() + .collect(Collectors.toMap(MKMetaAttachmentVO::getId, MKMetaAttachmentVO::getFullName, (a, b) -> a)); + for (MKAttachmentVO attachment : attachments) { + String fullName = fileIdFullNameMap.get(attachment.getFileId()); + if (StringUtil.isNotBlank(fullName)) { + // 附件名替换为全名 + attachment.setName(fullName); + } + } + } + } + + @Override + public List querySenderList(MKSenderDTO param) { + AssertUtils.notNull(param, "参数不能为空"); + AssertUtils.notBlank(param.getNodeInstanceId(),"节点实例id不能为空"); + AssertUtils.notBlank(param.getProcessInstanceId(),"流程实例id不能为空"); + return commonCall("查询抄送人列表", token -> { + // 查询100,一次都查完 + param.setPageSize(100); + return client.querySenderList(param, token); + }, result -> Optional.ofNullable(result) + .map(MKResultVO::getData) + .map(MKPageVO::getContent) + .orElse(Collections.emptyList())); + } + + @Override + public void downloadFile(HttpServletResponse response, String fileId) { + AssertUtils.notBlank(fileId,"附件id不能为空"); + String accessToken = getToken(); + try (OutputStream outputStream = response.getOutputStream();) { + try (Response feignResponse = client.downloadFile(new MKFileDTO(fileId), accessToken);) { + InputStream inputStream = feignResponse.body().asInputStream(); + Map> responseHeaders = feignResponse.headers(); + response.setHeader("Content-disposition", getHeader(responseHeaders, "Content-disposition")); + // response.setHeader("Content-Type", getHeader(responseHeaders, "Content-Type")); + response.setHeader("Content-Type", "image/png"); + IOUtils.copy(inputStream, outputStream); + } catch (MKException e) { + response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE); + outputStream.write(Optional.ofNullable(e.getMessage()).orElse("调用mk接口 下载附件异常").getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + log.error("调用mk接口 下载附件异常", e); + response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE); + outputStream.write("调用mk接口 下载附件异常".getBytes(StandardCharsets.UTF_8)); + } + } catch (Exception e) { + log.error("下载mk附件异常", e); + } + + } + + @Override + public String getFileBase64(String fileId) { + AssertUtils.notBlank(fileId,"附件id不能为空"); + String accessToken = getToken(); + try (Response feignResponse = client.downloadFile(new MKFileDTO(fileId), accessToken);) { + InputStream inputStream = feignResponse.body().asInputStream(); + byte[] bytes = IOUtils.toByteArray(inputStream); + return Base64.getEncoder().encodeToString(bytes); + } catch (MKException e) { + throw e; + } catch (Exception e) { + log.error("调用mk接口 下载附件异常", e); + } + return null; + } + + @Override + public String getNodeNumber(String templateId, String nodeId) { + if (StringUtil.isBlank(templateId) || StringUtil.isBlank(nodeId)) { + log.warn("查询节点编号模板id或节点id为空 模板id:{} 节点id:{}", templateId, nodeId); + return null; + } + return commonCall("查询模板节点信息", token -> { + return client.getManualNode(new MKManualNodeDTO(templateId), token); + }, result -> Optional.ofNullable(result) + .map(MKResultVO::getData) + .flatMap(nodes -> nodes.stream() + .filter(node -> StringUtil.equals(node.getNodeId(), nodeId)) + .findFirst() + .map(MKManualNodeVO::getNodeNumber) + ).orElse(null)); + } + + @Override + public MKPageVO queryApprovalList(MKApprovalDTO param) { + AssertUtils.notNull(param, "参数不能为空"); + AssertUtils.notNull(param.getConditions(),"条件不能为空"); + AssertUtils.notNull(param.getConditions().getMydoc(),"类型不能为空"); + return commonCall("查询审批中心列表", token -> client.queryApprovalList(param, token), + result -> Optional.ofNullable(result) + .map(MKResultVO::getData) + .orElse(MKPageVO.empty())); + } + + @Override + public MKPageVO queryProcessList(MKProcessDTO param) { + AssertUtils.notNull(param, "参数不能为空"); + AssertUtils.notBlank(param.getLoginName(),"登录名不能为空"); + AssertUtils.notNull(param.getMydoc(),"类型不能为空"); + return commonCall("查询流程列表", token -> client.queryProcessList(param, token), + result -> Optional.ofNullable(result) + .map(MKResultVO::getData) + .orElse(MKPageVO.empty())); + } + + /** + * 获取响应头 + * @param responseHeaders + * @param header + * @return + */ + private String getHeader(Map> responseHeaders, String header) { + Collection collection = responseHeaders.get(header); + if (CollectionUtil.isEmpty(collection)) { + return null; + } + return collection.stream().findFirst().orElse(null); + } + + /** + * 获取token + * + * @return + */ + private String getToken() { + String token = bladeRedis.get(MKConstant.MK_TOKEN_KEY); + if (token != null) { + return token; + } + try { + MKTokenDTO param = new MKTokenDTO(); + param.setAppKey(properties.getAppKey()); + param.setAppSecret(properties.getAppSecret()); + long start = System.currentTimeMillis(); + log.info("调用mk获取token开始"); + MKTokenVO tokenResult = client.getToken(param); + long end = System.currentTimeMillis(); + log.info("调用mk获取token结束 耗时:{}", end - start); + if (tokenResult != null && tokenResult.getErrCode() == MKConstant.TOKEN_SUCCESS_CODE) { + // 成功 + String accessToken = tokenResult.getAccessToken(); + int expiresIn = tokenResult.getExpiresIn(); + // 过期时间 - 100秒,防止刚好过期 + bladeRedis.getValueOps().setIfAbsent(MKConstant.MK_TOKEN_KEY, accessToken, Duration.ofSeconds(expiresIn - 100)); + return accessToken; + } + log.error("调用mk接口 获取token 异常响应参数:{}", JSONUtil.toJsonStr(tokenResult)); + throw new MKException("调用mk接口获取token异常"); + } catch (MKException e) { + throw e; + } catch (Exception e) { + log.error("调用mk接口 获取token 异常", e); + throw new MKException("调用mk接口获取token异常"); + } + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/support/MultiKeySerializer.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/support/MultiKeySerializer.java new file mode 100644 index 0000000..72fb7ba --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/support/MultiKeySerializer.java @@ -0,0 +1,37 @@ +package org.springblade.thirdparty.mk.support; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.thirdparty.mk.pojo.dto.approval.MKConditionDTO; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +/** + * @author bfhuange + * @since 2025/5/21 + */ +public class MultiKeySerializer extends JsonSerializer> { + @Override + public void serialize(List values, JsonGenerator gen, SerializerProvider serializers) throws IOException { + values = values.stream() + .filter(Objects::nonNull) + .toList(); + if (CollectionUtil.isNotEmpty(values)) { + // 获取当前字段名 + String fieldName = gen.getOutputContext().getCurrentName(); + boolean writeValue = false; + for (MKConditionDTO value : values) { + if (writeValue) { + // 写过值了,要再添加一个字段名 + gen.writeFieldName(fieldName); + } + gen.writeObject(value); + writeValue = true; + } + } + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/util/AssertUtils.java b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/util/AssertUtils.java new file mode 100644 index 0000000..5b640f1 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/java/org/springblade/thirdparty/mk/util/AssertUtils.java @@ -0,0 +1,266 @@ +package org.springblade.thirdparty.mk.util; + +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.mk.exception.MKException; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author bfhuange + * @since 2025/2/24 + */ +public class AssertUtils { + + /** + * 对象非空 + * @param object + * @param message + */ + public static void notNull(Object object, String message) { + if (object == null) { + throw new MKException(message); + } + } + + /** + * Map非空 + * @param map + * @param message + */ + public static void notEmpty(Map map, String message) { + if (CollectionUtils.isEmpty(map)) { + throw new MKException(message); + } + } + + /** + * 列表非空 + * @param collection + * @param message + */ + public static void notEmpty(Collection collection, String message) { + if (CollectionUtils.isEmpty(collection)) { + throw new MKException(message); + } + } + + /** + * 数组非空 + * @param array + * @param message + */ + public static void notEmpty(Object[] array, String message) { + if (ObjectUtils.isEmpty(array)) { + throw new MKException(message); + } + } + + /** + * 字符串非空 + * @param value + * @param message + */ + public static void notEmpty(String value, String message) { + if (StringUtil.hasLength(value)) { + throw new MKException(message); + } + } + + /** + * 字符串去除空格非空 + * @param value + * @param message + */ + public static void notBlank(String value, String message) { + if (StringUtil.isBlank(value)) { + throw new MKException(message); + } + } + + /** + * 获取重复数据 + * @param dataList + * @return + * @throws MKException + */ + public static List getRepeatData(List dataList) { + if(CollectionUtils.isEmpty(dataList)) { + return null; + } + //判断是否有重复数据 + List distinctDataList = dataList.stream().distinct().toList(); + if(dataList.size() == distinctDataList.size()) { + return null; + } + List repeatList = new ArrayList<>(); + Map dataMap = dataList.stream().collect(Collectors.groupingBy(p -> p,Collectors.counting())); + //处理重复数据 + for(Map.Entry entry:dataMap.entrySet()) { + if(entry.getValue() > 1) { + repeatList.add(entry.getKey()); + } + } + return repeatList; + } + + /** + * 数据不允许重复 + * @param dataList + * @param message + * @throws MKException + */ + public static void notRepeat(List dataList,String message) { + //获取重复数据 + List repeatList = getRepeatData(dataList); + //若有重复数据,则校验不通过处理 + if(!CollectionUtils.isEmpty(repeatList)) { + String repeatData = String.join(",",repeatList); + throw new MKException(String.format(message,repeatData)); + } + } + + /** + * 字符串相等校验 + * @param str1 + * @param str2 + * @param message + * @throws MKException + */ + public static void equals(String str1, String str2, String message) { + if (!StringUtil.equals(str1,str2)) { + throw new MKException(message); + } + } + + /** + * 数字相等校验 + * @param num1 + * @param num2 + * @param message + * @throws MKException + */ + public static void equals(BigDecimal num1, BigDecimal num2, String message) { + if (num1.compareTo(num2) != 0) { + throw new MKException(message); + } + } + + /** + * 数字不大于校验 + * @param num1 + * @param num2 + * @param message + * @throws MKException + */ + public static void notGreaterThan(BigDecimal num1, BigDecimal num2, String message) { + if(num1.compareTo(num2) > 0) { + throw new MKException(message); + } + } + + /** + * 数字不大于校验 + * @param num1 + * @param num2 + * @param message + * @throws MKException + */ + public static void notGreaterThan(Integer num1, Integer num2, String message) { + if(num1 > num2) { + throw new MKException(message); + } + } + + /** + * 数字不小于校验 + * @param num1 + * @param num2 + * @param message + * @throws MKException + */ + public static void notLessThan(BigDecimal num1, BigDecimal num2, String message) { + if(num1.compareTo(num2) < 0) { + throw new MKException(message); + } + } + + /** + * 表达式为真判断校验 + * @param expression + * @param message + * @throws MKException + */ + public static void isTrue(boolean expression, String message) { + if(!expression) { + throw new MKException(message); + } + } + + /** + * 表达式判断为假校验 + * @param expression + * @param message + * @throws MKException + */ + public static void isFalse(boolean expression, String message) { + if(expression) { + throw new MKException(message); + } + } + + /** + * 字符串不相等校验 + * @param str1 + * @param str2 + * @param message + * @throws MKException + */ + public static void notEquals(String str1, String str2, String message) { + if (StringUtil.equals(str1,str2)) { + throw new MKException(message); + } + } + + /** + * 对象为空 + * @param object + * @param message + */ + public static void isNull(Object object, String message) { + if (object != null) { + throw new MKException(message); + } + } + + /** + * 对象为数值 + * @param value + * @param message + * @throws MKException + */ + public static void isInteger(String value,String message) { + try { + Integer.valueOf(value); + } catch (NumberFormatException e) { + throw new MKException(message); + } + } + + /** + * 列表为空 + * @param collection + * @param message + */ + public static void isEmpty(Collection collection, String message) { + if (!CollectionUtils.isEmpty(collection)) { + throw new MKException(message); + } + } +} diff --git a/blade-third-party-api/blade-mk-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/blade-third-party-api/blade-mk-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..cd2c109 --- /dev/null +++ b/blade-third-party-api/blade-mk-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springblade.thirdparty.mk.config.ThirdPartyMKAutoConfiguration \ No newline at end of file diff --git a/blade-third-party-api/blade-oa-api/pom.xml b/blade-third-party-api/blade-oa-api/pom.xml new file mode 100644 index 0000000..857e2bb --- /dev/null +++ b/blade-third-party-api/blade-oa-api/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + org.springblade + blade-third-party-api + ${revision} + + + blade-oa-api + ${project.artifactId} + jar + + + + org.springblade + blade-core-tool + + + \ No newline at end of file diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/OAFeignClientConfig.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/OAFeignClientConfig.java new file mode 100644 index 0000000..c0b9cf4 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/OAFeignClientConfig.java @@ -0,0 +1,23 @@ +package org.springblade.thirdparty.oa.config; + +import feign.RequestInterceptor; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Bean; + +/** + * @author bfhuange + * @since 2024/12/18 + */ +public class OAFeignClientConfig { + @Resource + OAProperties oaProperties; + + @Bean + public RequestInterceptor requestInterceptor() { + return template -> { + // 空实现屏蔽 全局拦截器 BladeFeignRequestInterceptor + String authorization=oaProperties.getAuthorization(); + template.header("Authorization",authorization); + }; + } +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/OAProperties.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/OAProperties.java new file mode 100644 index 0000000..bfcbae1 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/OAProperties.java @@ -0,0 +1,23 @@ +package org.springblade.thirdparty.oa.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * oa配置类 + * @author bfhuange + * @date 2024/8/22 + */ +@Data +@ConfigurationProperties(prefix = "third-party.oa") +public class OAProperties { + /** + * oa基础url + */ + private String baseUrl; + + /** + * authorization + */ + private String authorization; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/ThirdPartyOAAutoConfiguration.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/ThirdPartyOAAutoConfiguration.java new file mode 100644 index 0000000..d79e5d2 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/config/ThirdPartyOAAutoConfiguration.java @@ -0,0 +1,14 @@ +package org.springblade.thirdparty.oa.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 第三方api oa配置类 + * @author bfhuange + * @date 2024/8/27 + */ +@EnableConfigurationProperties(OAProperties.class) +@Configuration +public class ThirdPartyOAAutoConfiguration { +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/constant/OAConstant.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/constant/OAConstant.java new file mode 100644 index 0000000..18579fb --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/constant/OAConstant.java @@ -0,0 +1,17 @@ +package org.springblade.thirdparty.oa.constant; + +/** + * oa常量类 + * @author bfhuange + * @date 2024/8/22 + */ +public class OAConstant { + /** + * oa成功响应码 + */ + public static final String OA_SUCCESS_CODE = "1"; + /** + * 根公司或根部门id + */ + public static final String ROOT_COMPANY_ID = "0"; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/constant/OAConvertConstant.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/constant/OAConvertConstant.java new file mode 100644 index 0000000..2182784 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/constant/OAConvertConstant.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.oa.constant; + +/** + * OA转换常量类 + * @author bfhuange + * @date 2024/8/27 + */ +public class OAConvertConstant { + /** + * oa公司转部门编码前缀 + */ + public static final String COMPANY_OA_PREFIX = "OAC"; + /** + * oa部门转部门编码前缀 + */ + public static final String DEPARTMENT_OA_PREFIX = "OAD"; + /** + * 外部公司部门编码前缀 + */ + public static final String COMPANY_EX_PREFIX = "EXC"; + /** + * 外部部门编码前缀 + */ + public static final String DEPARTMENT_EX_PREFIX = "EXD"; + /** + * oa人员转用户编码前缀 + */ + public static final String PERSON_OA_PREFIX = "OAP"; + /** + * 根公司父id + */ + public static final Long ROOT_PARENT_ID = 0L; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/feign/IOAClient.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/feign/IOAClient.java new file mode 100644 index 0000000..b571416 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/feign/IOAClient.java @@ -0,0 +1,47 @@ +package org.springblade.thirdparty.oa.feign; + +import org.springblade.thirdparty.oa.config.OAFeignClientConfig; +import org.springblade.thirdparty.oa.pojo.response.OACompanyResponse; +import org.springblade.thirdparty.oa.pojo.response.OADepartmentResponse; +import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse; +import org.springblade.thirdparty.oa.pojo.response.OAResponse; +import org.springblade.thirdparty.oa.pojo.search.OACompanySearch; +import org.springblade.thirdparty.oa.pojo.search.OADepartmentSearch; +import org.springblade.thirdparty.oa.pojo.search.OAPersonSearch; +import org.springblade.thirdparty.oa.pojo.search.OASearch; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * oa接口 + * @author bfhuange + * @date 2024/8/22 + */ +@FeignClient(name = "OA", url = "${thirdParty.oa.baseUrl}", configuration = OAFeignClientConfig.class) +public interface IOAClient { + + /** + * 查询公司分页 + * @param param + * @return + */ + @PostMapping("${thirdParty.oa.queryCompanyPageUrl:/api/hrm/resful/getHrmsubcompanyWithPage}") + OAResponse queryCompanyPage(@RequestBody OASearch param); + + /** + * 查询部门分页 + * @param param + * @return + */ + @PostMapping("${thirdParty.oa.queryDepartmentPage:/api/hrm/resful/getHrmdepartmentWithPage}") + OAResponse queryDepartmentPage(@RequestBody OASearch param); + + /** + * 查询人员分页,用户和公司、部门的关系,存在同一个用户,多条不同公司、部门数据 + * @param param + * @return + */ + @PostMapping("${thirdParty.oa.queryPersonPageUrl:/api/hrm/resful/getHrmUserInfoWithPage}") + OAResponse queryPersonPage(@RequestBody OASearch param); +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OACompanyResponse.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OACompanyResponse.java new file mode 100644 index 0000000..785b06f --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OACompanyResponse.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.oa.pojo.response; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @Author alexbzhang + * @Date 25/11/2022 14 34 + * @Version 1.0 + */ +@Data +public class OACompanyResponse implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private String subcompanydesc; + private String canceled; + private String subcompanycode; + private String supsubcomid; + private String created; + private String modified; + private String id; + private String subcompanyname; + private String url; + private String showorder; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OADepartmentResponse.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OADepartmentResponse.java new file mode 100644 index 0000000..709be7f --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OADepartmentResponse.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.oa.pojo.response; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @Author alexbzhang + * @Date 25/11/2022 14 40 + * @Version 1.0 + */ +@Data +public class OADepartmentResponse implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private String canceled; + private String supdepid; + private String departmentmark; + private String departmentname; + private String created; + private String departmentcode; + private String modified; + private String id; + private String subcompanyid1; + private String showorder; + +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAPersonResponse.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAPersonResponse.java new file mode 100644 index 0000000..5a2fb5c --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAPersonResponse.java @@ -0,0 +1,76 @@ +package org.springblade.thirdparty.oa.pojo.response; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @Author alexbzhang + * @Date 25/11/2022 14:20 + * @Version 1.0 + */ +@Data +public class OAPersonResponse implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private String companystartdate; + private String tempresidentnumber; + private String createdate; + private String language; + private String workstartdate; + private String subcompanyid1; + private String subcompanyname; + private String joblevel; + private String startdate; + private String password; + private String subcompanycode; + private String jobactivitydesc; + private String bememberdate; + private String modified; + private String id; + private String mobilecall; + private String nativeplace; + private String certificatenum; + private String height; + private String loginid; + private String created; + private String degree; + private String bepartydate; + private String weight; + private String telephone; + private String residentplace; + private String lastname; + private String healthinfo; + private String enddate; + private String maritalstatus; + private String departmentname; + private String folk; + /** + * 状态 :0:试用1:正式2:临时3:试用延期4:解聘5:离职6:退休7:无效 + */ + private String status; + private String birthday; + private String accounttype; + private String jobcall; + private String managerid; + private String assistantid; + private String departmentcode; + private String belongto; + private String email; + private String seclevel; + private String policy; + private String jobtitle; + private String workcode; + private String sex; + private String departmentid; + private String homeaddress; + private String mobile; + private String lastmoddate; + private String educationlevel; + private String islabouunion; + private String locationid; + private String regresidentplace; + private String dsporder; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAResponse.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAResponse.java new file mode 100644 index 0000000..01dab76 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAResponse.java @@ -0,0 +1,22 @@ +package org.springblade.thirdparty.oa.pojo.response; + +import lombok.Data; +import org.springblade.core.tool.support.NoErrorFallback; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @Author alexbzhang + * @Date 25/11/2022 13:52 + * @Version 1.0 + */ +@Data +public class OAResponse implements Serializable, NoErrorFallback { + @Serial + private static final long serialVersionUID = 1L; + + private String code; + + private OAResponseData data; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAResponseData.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAResponseData.java new file mode 100644 index 0000000..4e3ed79 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/response/OAResponseData.java @@ -0,0 +1,26 @@ +package org.springblade.thirdparty.oa.pojo.response; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * @Author alexbzhang + * @Date 25/11/2022 13:58 + * @Version 1.0 + */ +@Data +public class OAResponseData implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long totalSize; + + private List dataList; + + private Integer pageSize; + + private Integer page; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OACompanySearch.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OACompanySearch.java new file mode 100644 index 0000000..cb9c9e2 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OACompanySearch.java @@ -0,0 +1,26 @@ +package org.springblade.thirdparty.oa.pojo.search; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * oa公司分页查询参数 + * @author bfhuange + * @date 2024/8/23 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class OACompanySearch extends OAPageSearch { + @Serial + private static final long serialVersionUID = 1L; + + private String subcompanyname; + private String subcompanycode; + private String created; + private String modified; + private String canceled; + private String id; + +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OADepartmentSearch.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OADepartmentSearch.java new file mode 100644 index 0000000..6e061b2 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OADepartmentSearch.java @@ -0,0 +1,27 @@ +package org.springblade.thirdparty.oa.pojo.search; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * OA部门分页查询参数 + * @author bfhuange + * @date 2024/8/23 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class OADepartmentSearch extends OAPageSearch { + @Serial + private static final long serialVersionUID = 1L; + + private String departmentname; + private String departmentcode; + private String subcompanyid1; + private String created; + private String modified; + private String canceled; + private String id; + +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OAPageSearch.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OAPageSearch.java new file mode 100644 index 0000000..a5190be --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OAPageSearch.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.oa.pojo.search; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * oa分页查询参数 + * @author bfhuange + * @date 2024/8/23 + */ +@Data +public class OAPageSearch implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 当前页,必填 + */ + @JsonProperty("curpage") + protected Integer curPage = 1; + /** + * 每页大小,必填 + */ + @JsonProperty("pagesize") + protected Integer pageSize = 10000; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OAPersonSearch.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OAPersonSearch.java new file mode 100644 index 0000000..d725c57 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OAPersonSearch.java @@ -0,0 +1,30 @@ +package org.springblade.thirdparty.oa.pojo.search; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * OA人员分页查询参数 + * @author bfhuange + * @date 2024/8/23 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class OAPersonSearch extends OAPageSearch { + @Serial + private static final long serialVersionUID = 1L; + + private String created; + private String modified; + private String workcode; + private String subcompanyid1; + private String departmentid; + private String jobtitleid; + private String id; + private String loginid; + + private String isadaccount; + +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OASearch.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OASearch.java new file mode 100644 index 0000000..75b4892 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/pojo/search/OASearch.java @@ -0,0 +1,23 @@ +package org.springblade.thirdparty.oa.pojo.search; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * oa查询参数 + * @author bfhuange + * @date 2024/8/22 + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +public class OASearch implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private T params; +} diff --git a/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/util/OAUtils.java b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/util/OAUtils.java new file mode 100644 index 0000000..14fd1c3 --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/java/org/springblade/thirdparty/oa/util/OAUtils.java @@ -0,0 +1,168 @@ +package org.springblade.thirdparty.oa.util; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.thirdparty.oa.constant.OAConstant; +import org.springblade.thirdparty.oa.pojo.response.OAResponse; +import org.springblade.thirdparty.oa.pojo.response.OAResponseData; +import org.springblade.thirdparty.oa.pojo.search.OAPageSearch; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * oa工具类 + * @author bfhuange + * @date 2024/8/23 + */ +@Slf4j +public class OAUtils { + private OAUtils() {} + + /** + * 将浮点型字符串转换成整数,字符串为空则转为1,否则截取字符串小数点前面的部门解析 + * @param doubleString 浮点类型字符串 + * @return + */ + public static int parseInt(String doubleString) { + if (doubleString == null || doubleString.isEmpty()) { + return 1; + } + try { + return Integer.parseInt(doubleString.split("\\.")[0]); + } catch (Exception e) { + log.error("解析数值字符串[{}]异常", doubleString, e); + return 1; + } + } + + /** + * 分页同步所有数据接口,返回所有数据无限制 + * @param param 接口分页参数 + * @param function 接口 + * @param exceptionFunction 异常方法 + * @return + * @param + * @param + */ + public static List pageSyncAll(T param, Function> function, Function, RuntimeException> exceptionFunction) { + return OAUtils.pageSync(param, function, exceptionFunction, Integer.MAX_VALUE); + } + + /** + * 分页同步数据接口 + * @param param 接口分页参数 + * @param function 接口 + * @param exceptionFunction 异常方法 + * @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据 + * @return 参数是处理数据的方法的方法 + * @param + * @param + */ + public static Consumer>> pageSyncHandler(T param, Function> function, Function, RuntimeException> exceptionFunction, int threshold) { + return OAUtils.pageSyncHandler(param, function, exceptionFunction, threshold, log::info); + } + + /** + * 分页同步数据接口 + * @param param 接口分页参数 + * @param function 接口 + * @param exceptionFunction 异常方法 + * @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据 + * @return 参数是处理数据的方法的方法 + * @param + * @param + */ + public static Consumer>> pageSyncHandler(T param, Function> function, Function, RuntimeException> exceptionFunction, int threshold, BiConsumer logMethod) { + return consumer -> { + int i = 0; + while (true) { + // 循环调用接口直到阈值或最后一页 + List data = OAUtils.pageSync(param, function, exceptionFunction, threshold, logMethod); + if (data.isEmpty()) { + // 数据为空,结束 + return; + } + logMethod.accept("批量处理OA同步数据 第{}次,{}条", new Object[]{++i, data.size()}); + // 数据不为空,处理数据 + consumer.accept(data); + if (data.size() < threshold) { + // 数据比阈值小,说明到最后一页了,结束 + return; + } + } + }; + } + + /** + * 分页同步数据接口 + * @param param 接口分页参数 + * @param function 接口 + * @param exceptionFunction 异常方法 + * @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据 + * @return + * @param + * @param + */ + public static List pageSync(T param, Function> function, Function, RuntimeException> exceptionFunction, int threshold) { + return OAUtils.pageSync(param, function, exceptionFunction, threshold, log::info); + } + + /** + * 分页同步数据接口 + * @param param 接口分页参数 + * @param function 接口 + * @param exceptionFunction 异常方法 + * @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据 + * @param logMethod 日志输出 + * @return + * @param + * @param + */ + public static List pageSync(T param, Function> function, Function, RuntimeException> exceptionFunction, int threshold, BiConsumer logMethod) { + long totalPage = -1; + List result = new ArrayList<>(); + do { + // 调用接口 + OAResponse oaResponse = function.apply(param); + if (oaResponse== null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) { + // 状态不是成功,抛出异常 + throw exceptionFunction.apply(oaResponse); + } + OAResponseData data = oaResponse.getData(); + if (!CollectionUtils.isEmpty(data.getDataList())) { + result.addAll(data.getDataList()); + } + // 计算总页数 + totalPage = calcTotalPage(data); + logMethod.accept("OA分页同步数据完成 {}/{}页", new Object[]{param.getCurPage(), totalPage}); + // 当前页 + 1 + param.setCurPage(param.getCurPage() + 1); + // 数据量小于阈值且当前页(下一次)小于等于总页数,循环调用接口 + } while (result.size() < threshold && param.getCurPage() <= totalPage); + return result; + } + + /** + * 计算总页数 + * @param data + * @return + * @param + */ + private static long calcTotalPage(OAResponseData data) { + // 总数 + Long totalSize = data.getTotalSize(); + // 每页大小 + Integer pageSize = data.getPageSize(); + // 总页数 = 总数 / 每页大小,这里会舍去余数 + long totalPage = totalSize / pageSize; + if (totalSize % pageSize != 0) { + // 有余数总页数 +1 + totalPage += 1; + } + return totalPage; + } +} diff --git a/blade-third-party-api/blade-oa-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/blade-third-party-api/blade-oa-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..48aa73d --- /dev/null +++ b/blade-third-party-api/blade-oa-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springblade.thirdparty.oa.config.ThirdPartyOAAutoConfiguration \ No newline at end of file diff --git a/blade-third-party-api/blade-ocr-api/pom.xml b/blade-third-party-api/blade-ocr-api/pom.xml new file mode 100644 index 0000000..28cda3f --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.springblade + blade-third-party-api + ${revision} + + + blade-ocr-api + ${project.artifactId} + jar + + + + org.springblade + blade-core-tool + + + org.springblade + blade-starter-log + true + + + \ No newline at end of file diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/config/OCRFeignClientConfig.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/config/OCRFeignClientConfig.java new file mode 100644 index 0000000..0a14ac2 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/config/OCRFeignClientConfig.java @@ -0,0 +1,16 @@ +package org.springblade.thirdparty.ocr.config; + +import feign.RequestInterceptor; +import org.springblade.thirdparty.ocr.interceptor.OCRRequestInterceptor; +import org.springframework.context.annotation.Bean; + +/** + * @author bfhuange + * @since 2024/12/18 + */ +public class OCRFeignClientConfig { + @Bean + public RequestInterceptor requestInterceptor() { + return new OCRRequestInterceptor(); + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/config/ThirdPartyOCRAutoConfiguration.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/config/ThirdPartyOCRAutoConfiguration.java new file mode 100644 index 0000000..7a721a4 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/config/ThirdPartyOCRAutoConfiguration.java @@ -0,0 +1,14 @@ +package org.springblade.thirdparty.ocr.config; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * 第三方api ocr配置类 + * @author bfhuange + * @date 2024/12/18 + */ +@ComponentScan("org.springblade.thirdparty.ocr") +@Configuration +public class ThirdPartyOCRAutoConfiguration { +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRConstant.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRConstant.java new file mode 100644 index 0000000..2da10f5 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRConstant.java @@ -0,0 +1,38 @@ +package org.springblade.thirdparty.ocr.constant; + +/** + * ocr 常量类 + * @author bfhuange + * @date 2024/11/4 + */ +public class OCRConstant { + /** + * 成功状态码 + */ + public static final Integer SUCCESS_CODE = 200; + + /** + * 重量单位 kg + */ + public static final String UNIT_KG = "kg"; + + /** + * 重量大卫 t + */ + public static final String UNIT_T = "t"; + + /** + * 重量单位 吨 + */ + public static final String UNIT_T_ZH = "吨"; + + /** + * 请求头,请求时间 + */ + public static final String REQUEST_HEADER_TIME = "request_time"; + /** + * 请求头,trace id + */ + public static final String REQUEST_HEADER_TRACE_ID = "trace_id"; + +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRImageType.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRImageType.java new file mode 100644 index 0000000..f854024 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRImageType.java @@ -0,0 +1,105 @@ +package org.springblade.thirdparty.ocr.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Collections; +import java.util.List; + +/** + * ocr 图像类型枚举 + * @author bfhuange + * @date 2024/11/4 + */ +@AllArgsConstructor +@Getter +public enum OCRImageType { + /** + * 身份证-正⻚ + */ + ID_CARD_MAIN("IDCard-Main", "身份证-正⻚", OCRKey.ID_CARD_MAIN_KEYS), + /** + * 身份证-副⻚ + */ + ID_CARD_SIDE("IDCard-Side", "身份证-副⻚", OCRKey.ID_CARD_SIDE_KEYS), + /** + * 驾驶证-电子版 + */ + DRIVER_LICENSE_ELEC("DriverLicense-Elec", "驾驶证-电子版", OCRKey.DRIVER_LICENSE_MAIN_KEYS), + /** + * 驾驶证-正⻚ + */ + DRIVER_LICENSE_MAIN("DriverLicense-Main", "驾驶证-正⻚", OCRKey.DRIVER_LICENSE_MAIN_KEYS), + /** + * 驾驶证-副⻚ + */ + DRIVER_LICENSE_SIDE("DriverLicense-Side", "驾驶证-副⻚", Collections.emptyList()), + /** + * ⾏驶证-正⻚-挂车 + */ + VEHICLE_LICENSE_MAIN_GUA("VehicleLicense-Main-Gua", "⾏驶证-正⻚-挂车", OCRKey.VEHICLE_LICENSE_MAIN_KEYS), + /** + * ⾏驶证-正⻚-其他 + */ + VEHICLE_LICENSE_MAIN_OTHERS("VehicleLicense-Main-Others", "⾏驶证-正⻚-其他", OCRKey.VEHICLE_LICENSE_MAIN_KEYS), + /** + * ⾏驶证-正⻚ + */ + VEHICLE_LICENSE_MAIN("VehicleLicense-Main", "⾏驶证-正⻚", OCRKey.VEHICLE_LICENSE_MAIN_KEYS), + /** + * ⾏驶证-副⻚ + */ + VEHICLE_LICENSE_SIDE("VehicleLicense-Side", "⾏驶证-副⻚", OCRKey.VEHICLE_LICENSE_SIDE_KEYS), + /** + * ⻋头 + */ + CAR_FRONT("Car-Front", "⻋头", OCRKey.CAR_FRONT_KEYS), + /** + * ⻋尾 + */ + CAR_BACK("Car-Back", "⻋尾", OCRKey.CAR_BACK_KEYS), + // =======================下面是非erp识别所需信息,录入配置非磅单类型==================== + /** + * 未知 + */ + UNKNOWN("UNK", "未知"), + /** + * 其他 + */ + OTHERS("Others", "其他"), + /** + * 证件 + */ + CARD("CARD", "证件"), + /** + * 碎片 + */ + SCRAP("Scrap", "碎片"), + /** + * 有⻋辆没有检测到⻋牌 + */ + NO_PLATE("NoPlate", "有⻋辆没有检测到⻋牌"), + /** + * ⽆⻋ + */ + NO_CAR("NoCar", "⽆⻋"), + ; + /** + * 类型标识 + */ + private final String id; + /** + * 类型 + */ + private final String type; + /** + * key列表 + */ + private final List keys; + + OCRImageType(String id, String type) { + this.id = id; + this.type = type; + this.keys = Collections.emptyList(); + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRKey.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRKey.java new file mode 100644 index 0000000..65b69a1 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/constant/OCRKey.java @@ -0,0 +1,322 @@ +package org.springblade.thirdparty.ocr.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +/** + * ocr 各类型字段枚举 + * @author bfhuange + * @date 2024/11/4 + */ +public class OCRKey { + /** + * 驾驶证-正⻚ 字段列表 + */ + public static final List DRIVER_LICENSE_MAIN_KEYS = List.of( + // 证号 + KeyInfo.ID_NO, + // 姓名 + KeyInfo.NAME, + // 性别 + KeyInfo.SEX, + // 出⽣⽇期 + KeyInfo.DATE_BIRTH, + // 初次领证⽇期 + KeyInfo.DATE_FIRST_ISSUE, + // 准驾⻋型 + KeyInfo.CLASS, + // 有效期限(起始时间) + // KeyInfo.DATE_VALID_START, + // 有效期限(终⽌时间) + // KeyInfo.DATE_VALID_END, + // 有效期限 + KeyInfo.DATE_VALID, + // 住址 + KeyInfo.ADDRESS + ); + /** + * ⾏驶证-正⻚ 字段列表 + */ + public static final List VEHICLE_LICENSE_MAIN_KEYS = List.of( + // 车牌号 + KeyInfo.PLATE_NO, + // ⻋辆类型 + KeyInfo.VEHICLE_TYPE, + // 姓名 + KeyInfo.NAME, + // 使⽤性质 + KeyInfo.USE_CHARACTER, + // 品牌型号 + KeyInfo.MODEL, + // ⻋架号 + KeyInfo.VIN, + // 发动机号码 + KeyInfo.ENGINE_NO, + // 注册⽇期 + KeyInfo.DATE_REGISTER, + // 发证⽇期 + KeyInfo.DATE_ISSUE, + // 住址 + KeyInfo.ADDRESS + ); + /** + * ⾏驶证-副⻚ 字段列表 + */ + public static final List VEHICLE_LICENSE_SIDE_KEYS = List.of( + // 检验有效期 + KeyInfo.DATE_VALID_INSPECTION + ); + /** + * ⻋头 字段列表 + */ + public static final List CAR_FRONT_KEYS = List.of( + // ⻋牌号 + KeyInfo.PLATE_NO + ); + /** + * ⻋尾 字段列表 + */ + public static final List CAR_BACK_KEYS = List.of( + // ⻋牌号 + KeyInfo.PLATE_NO + ); + /** + * 过磅单 字段列表 + */ + public static final List POUND_KEYS = List.of( + // ⽇期 + KeyInfo.DATE, + // 单位 + KeyInfo.UNIT_MEASURE, + // ⻋牌号 + KeyInfo.PLATE_NO, + // 货品 + KeyInfo.NAME_GOODS, + // 收货单位 + KeyInfo.COMPANY_RECEIPT, + // 发货单位 + KeyInfo.COMPANY_SHIP, + // ⽑重 + KeyInfo.WEIGHT_GROSS, + // ⽪重 + KeyInfo.WEIGHT_TARE, + // 净重 + KeyInfo.WEIGHT_NET, + // 扣重 + KeyInfo.WEIGHT_DEDUCT, + // 实际净重 + KeyInfo.WEIGHT_RECEIVED + ); + /** + * 身份证-正⻚ 字段列表 + */ + public static final List ID_CARD_MAIN_KEYS = List.of( + // 姓名 + KeyInfo.NAME, + // 公民身份号码 + KeyInfo.ID_NO, + // 性别 + KeyInfo.ID_CARD_MAIN_GENDER, + // 民族 + KeyInfo.ID_CARD_MAIN_ETHNIC_GROUP, + // 出生 + KeyInfo.DATE, + // 住址 + KeyInfo.ID_CARD_MAIN_ADD + ); + /** + * 身份证-副⻚ 字段列表 + */ + public static final List ID_CARD_SIDE_KEYS = List.of( + // 签发机关 + KeyInfo.ISSUING_AUTHORITY, + // 有效期限 + KeyInfo.DATE_VALID + ); + + /** + * 所有类型 + */ + @AllArgsConstructor + @Getter + public enum KeyInfo { + /** + * 住址 + */ + ADDRESS("address", "住址"), + /** + * 证号 + */ + ID_NO("ID_no", "证号"), + /** + * 发动机号码 + */ + ENGINE_NO("engine_no", "发动机号码"), + /** + * 档案编号 + */ + FILE_NO("file_no", "档案编号"), + /** + * 供货单位、客户名称、发货单位 + */ + COMPANY_SHIP("company_ship", "供货单位、客户名称、发货单位"), + /** + * 收货单位 + */ + COMPANY_RECEIPT("company_receipt", "收货单位"), + /** + * ⽇期 + */ + DATE("date", "⽇期"), + /** + * 出⽣⽇期 + */ + DATE_BIRTH("date_birth", "出⽣⽇期"), + /** + * 注册⽇期 + */ + DATE_REGISTER("date_register", "注册⽇期"), + /** + * 初次领证⽇期 + */ + DATE_FIRST_ISSUE("date_first_issue", "初次领证⽇期"), + /** + * 注册⽇期 + */ + DATE_ISSUE("date_issue", "发证⽇期"), + /** + * 有效期限(起始时间) + */ + DATE_VALID_START("date_vaild_start", "有效期限(起始时间)"), + /** + * 有效期限(终⽌时间) + */ + DATE_VALID_END("date_vaild_end", "有效期限(终⽌时间)"), + /** + * ⾏驶证副⻚-备注 -> 检验有效期 + */ + DATE_VALID_INSPECTION("date_vaild_inspection", "⾏驶证副⻚-备注 -> 检验有效期"), + /** + * 性别 + */ + SEX("six", "性别"), + /** + * 国籍 + */ + NATIONALITY("nationality", "国籍"), + /** + * ⺠族 + */ + NATION("nation", "⺠族"), + /** + * 姓名、所有⼈ + */ + NAME("name", "姓名、所有⼈"), + /** + * 货物名称、货品名称 + */ + NAME_GOODS("name_goods", "货物名称、货品名称"), + /** + * 物料 + */ + NAME_MATERIAL("name_material", "物料"), + /** + * ⻋辆类型 + */ + VEHICLE_TYPE("vehicle_type", "⻋辆类型"), + /** + * 使⽤性质 + */ + USE_CHARACTER("use_character", "使⽤性质"), + /** + * 品牌型号 + */ + MODEL("model", "品牌型号"), + /** + * 准驾⻋型 + */ + CLASS("class", "准驾⻋型"), + /** + * ⻋架号 + */ + VIN("vin", "⻋架号"), + /** + * ⻋牌号、号牌号码、⻋号 + */ + PLATE_NO("plate_no", "⻋牌号、号牌号码、⻋号"), + /** + * 扣重、扣杂 + */ + WEIGHT_DEDUCT("weight_deduct", "扣重、扣杂"), + /** + * ⽑重、总重 + */ + WEIGHT_GROSS("weight_gross", "⽑重、总重"), + /** + * ⽪重、空重 + */ + WEIGHT_TARE("weight_tare", "⽪重、空重"), + /** + * 净重 + */ + WEIGHT_NET("weight_net", "净重"), + /** + * 实际净重 + */ + WEIGHT_RECEIVED("weight_received", "实际净重"), + /** + * 杂质 + */ + WEIGHT_IMPURITY("weight_impurity", "杂质"), + /** + * 粉末 + */ + WEIGHT_POWDER("weight_powder", "粉末"), + /** + * 明⽔ + */ + WEIGHT_CWATER("weight_cwater", "明⽔"), + /** + * 单位 + */ + UNIT_MEASURE("unit_measure", "单位"), + /** + * 识别了但是还未⽀持整合的结构化字段 + */ + UN_SUPPORT("unsupport", "识别了但是还未⽀持整合的结构化字段"), + + //===================身份证的有些字段不一样============================== + /** + * 身份证-性别 + */ + ID_CARD_MAIN_GENDER("gender", "性别"), + /** + * 身份证-民族 + */ + ID_CARD_MAIN_ETHNIC_GROUP("ethnic_group", "民族"), + /** + * 身份证-住址 + */ + ID_CARD_MAIN_ADD("add", "住址"), + /** + * 签发机关 + */ + ISSUING_AUTHORITY("issuing_authority", "签发机关"), + /** + * 有效期限 + */ + DATE_VALID("date_vaild", "有效期限"), + ; + /** + * 字段编码 + */ + private final String key; + /** + * 字段描述 + */ + private final String description; + } + +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/AttachmentIndexConvert.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/AttachmentIndexConvert.java new file mode 100644 index 0000000..cc0b63c --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/AttachmentIndexConvert.java @@ -0,0 +1,141 @@ +package org.springblade.thirdparty.ocr.convert; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springblade.thirdparty.ocr.constant.OCRImageType; +import org.springblade.thirdparty.ocr.exception.OCRException; +import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 附件索引转换器 + * @author bfhuange + * @date 2024/11/8 + */ +@Slf4j +@AllArgsConstructor +@Getter +public enum AttachmentIndexConvert { + /** + * 车头 + */ + CAR_FRONT(List.of(OCRImageType.CAR_FRONT), TransportCertificateVO::getCarFrontIndexes, TransportCertificateVO::getOtherIndexes), + /** + * 车尾 + */ + CAR_BACK(List.of(OCRImageType.CAR_BACK), TransportCertificateVO::getCarBackIndexes, TransportCertificateVO::getOtherIndexes), + /** + * 行驶证 只从 VEHICLE_LICENSE_MAIN、VEHICLE_LICENSE_MAIN_OTHERS 取车牌号,VEHICLE_LICENSE_SIDE、VEHICLE_LICENSE_MAIN_GUA,没有车牌,但是要分类成行驶证 + */ + VEHICLE_LICENSE(List.of(OCRImageType.VEHICLE_LICENSE_MAIN, OCRImageType.VEHICLE_LICENSE_MAIN_OTHERS, OCRImageType.VEHICLE_LICENSE_SIDE, OCRImageType.VEHICLE_LICENSE_MAIN_GUA), TransportCertificateVO::getVehicleLicenseIndexes, TransportCertificateVO::getOtherIndexes, false), + /** + * 驾驶证 只从 DRIVER_LICENSE_MAIN、DRIVER_LICENSE_ELEC 取有效期,DRIVER_LICENSE_SIDE 没有有效期,但是要分类驾驶证 + */ + DRIVER_LICENSE(List.of(OCRImageType.DRIVER_LICENSE_MAIN, OCRImageType.DRIVER_LICENSE_ELEC, OCRImageType.DRIVER_LICENSE_SIDE), TransportCertificateVO::getDriverLicenseIndexes, TransportCertificateVO::getOtherIndexes, true), + /** + * 其他 + */ + OTHER(List.of(OCRImageType.ID_CARD_MAIN, OCRImageType.ID_CARD_SIDE, OCRImageType.UNKNOWN, OCRImageType.OTHERS, OCRImageType.CARD, + OCRImageType.SCRAP, OCRImageType.NO_PLATE, OCRImageType.NO_CAR), TransportCertificateVO::getOtherIndexes, TransportCertificateVO::getOtherIndexes, true), + /** + * 磅单 + */ + POUND(Collections.emptyList(), TransportCertificateVO::getPoundIndexes, TransportCertificateVO::getOtherIndexes), + ; + + /** + * 图像类型列表 + */ + private final List imageTypes; + /** + * 是否多个,除了其他,都是单个 + */ + private final boolean multi; + /** + * 默认附件索引列表 + */ + private final Function> defaultListGetter; + /** + * 单个的情况下,其他的附件放的索引列表 + */ + private final Function> otherListGetter; + /** + * 多个的情况下,是否设置多次,后面的会覆盖前面的 + */ + private final boolean multiSet; + + AttachmentIndexConvert(List imageTypes, Function> defaultListGetter, Function> otherListGetter) { + this(imageTypes, false, defaultListGetter, otherListGetter, true); + } + + AttachmentIndexConvert(List imageTypes, Function> defaultListGetter, Function> otherListGetter, boolean multiSet) { + this(imageTypes, true, defaultListGetter, otherListGetter, multiSet); + } + + /** + * 添加索引到list + * @param certificate 凭证 + * @param index 索引 + * @return + */ + public boolean addIndex2list(TransportCertificateVO certificate, Integer index) { + List list = defaultListGetter.apply(certificate); + // 是否放入默认list + boolean defaultList = true; + if (!multi && !list.isEmpty() && !list.contains(index)) { + // 单个且默认list已经有数据了且待放入的数据不在默认list,放入其他 + // 处理同一张图包含能取字段的驾驶证或行驶证以及不能取字段的驾驶证或行驶证时,不能取字段的驾驶证或行驶证类型先识别,导致后续把能取字段的驾驶证或行驶证类型放到其他 + list = otherListGetter.apply(certificate); + defaultList = false; + } + if (list.contains(index)) { + // 如果已经包含了图片,设置多个,返回true,不设置多个,返回false + return multiSet; + } + // 没有当前索引才添加,防止一张图片识别出多个结果重复添加索引 + list.add(index); + return defaultList; + } + + /** + * 图像类型对应的附件转换 + */ + private static final Map CONVERT_MAP = initConvertMap(); + + /** + * 初始化 图像类型对应的附件转换 + * @return + */ + private static Map initConvertMap() { + return Stream.of(values()) + .flatMap(convert -> convert.imageTypes.stream() + .collect(Collectors.toMap(OCRImageType::getId, e -> convert)) + .entrySet().stream() + ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> { + throw new OCRException("图像类型对应的附件转换重复"); + })); + } + + /** + * 根据typeId 查询转换器,没找到的是磅单 + * @param typeId + * @return + */ + public static AttachmentIndexConvert getConvertByTypeId(String typeId) { + // 磅单的typeId过多,还在新增,所以没有配置的都认为是磅单 + return CONVERT_MAP.getOrDefault(typeId, AttachmentIndexConvert.POUND); + } + + public List getImageTypeIds() { + return imageTypes.stream() + .map(OCRImageType::getId) + .collect(Collectors.toList()); + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/IDCardConvert.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/IDCardConvert.java new file mode 100644 index 0000000..40106bd --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/IDCardConvert.java @@ -0,0 +1,49 @@ +package org.springblade.thirdparty.ocr.convert; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springblade.thirdparty.ocr.constant.OCRImageType; +import org.springblade.thirdparty.ocr.constant.OCRKey; +import org.springblade.thirdparty.ocr.pojo.vo.IDCardVO; +import org.springblade.thirdparty.ocr.util.ConvertUtil; + +import java.util.function.BiConsumer; + +/** + * 身份证转换 + * @author bfhuange + * @date 2024/11/6 + */ +@Slf4j +@AllArgsConstructor +@Getter +public enum IDCardConvert { + /** + * 姓名 + */ + NAME(OCRImageType.ID_CARD_MAIN, OCRKey.KeyInfo.NAME, IDCardVO::setName), + /** + * 身份证号 + */ + ID_NO(OCRImageType.ID_CARD_MAIN, OCRKey.KeyInfo.ID_NO, IDCardVO::setIdNo), + /** + * 有效期止 + */ + VALID_DATE_END(OCRImageType.ID_CARD_SIDE, OCRKey.KeyInfo.DATE_VALID, (idCard, dateString) -> ConvertUtil.extractEndDate(dateString) + .accept(idCard::setValidDateEnd)), + ; + /** + * 图像类型列表 + */ + private final OCRImageType imageType; + /** + * 字段配置 + */ + private final OCRKey.KeyInfo keyInfo; + /** + * setter方法 + */ + private final BiConsumer setter; + +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/TransportCertificateConvert.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/TransportCertificateConvert.java new file mode 100644 index 0000000..e929380 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/convert/TransportCertificateConvert.java @@ -0,0 +1,109 @@ +package org.springblade.thirdparty.ocr.convert; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springblade.thirdparty.ocr.constant.OCRImageType; +import org.springblade.thirdparty.ocr.constant.OCRKey; +import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO; + +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +import static org.springblade.thirdparty.ocr.util.ConvertUtil.*; + +/** + * 车辆运输凭证转换 + * @author bfhuange + * @date 2024/11/6 + */ +@Slf4j +@AllArgsConstructor +@Getter +public enum TransportCertificateConvert { + /** + * 车头车牌号 + */ + CAR_FRONT_PLATE_NO(OCRImageType.CAR_FRONT, OCRKey.KeyInfo.PLATE_NO, TransportCertificateVO::setCarFrontPlateNo), + /** + * 车尾车牌号 + */ + CAR_BACK_PLATE_NO(OCRImageType.CAR_BACK, OCRKey.KeyInfo.PLATE_NO, TransportCertificateVO::setCarBackPlateNo), + /** + * 行驶证上的车牌号 + */ + DRIVING_PLATE_NO(List.of(OCRImageType.VEHICLE_LICENSE_MAIN, OCRImageType.VEHICLE_LICENSE_MAIN_OTHERS), OCRKey.KeyInfo.PLATE_NO, TransportCertificateVO::setDrivingPlateNo), + /** + * 驾驶证有效期限(终⽌时间) + */ + DRIVER_LICENSE_VALID_DATE_END(List.of(OCRImageType.DRIVER_LICENSE_MAIN, OCRImageType.DRIVER_LICENSE_ELEC), OCRKey.KeyInfo.DATE_VALID, (transportCertificate, dateString) -> convertDateValid(dateString) + .accept(transportCertificate::setDriverLicenseValidDateEnd)), + /** + * 磅单车牌号 + */ + POUND_PLATE_NO(Collections.emptyList(), OCRKey.KeyInfo.PLATE_NO, TransportCertificateVO::setPoundPlateNo), + /** + * 磅单供货日期 + */ + POUND_DATE(Collections.emptyList(), OCRKey.KeyInfo.DATE, (transportCertificate, dateString) -> convertDate(dateString) + .accept(transportCertificate::setSupplyMaterialDate)), + /** + * 单位 + */ + UNIT_MEASURE(Collections.emptyList(), OCRKey.KeyInfo.UNIT_MEASURE, TransportCertificateVO::setUnitMeasure), + /** + * 货品名称 + */ + NAME_GOODS(Collections.emptyList(), OCRKey.KeyInfo.NAME_GOODS, TransportCertificateVO::setProductName), + /** + * 物料规格 + */ + NAME_MATERIAL(Collections.emptyList(), OCRKey.KeyInfo.NAME_MATERIAL, TransportCertificateVO::setSpecificationMaterial), + /** + * ⽑重 + */ + GROSS_WEIGHT(Collections.emptyList(), OCRKey.KeyInfo.WEIGHT_GROSS, (transportCertificate, string) -> convertWeightBigDecimal(string) + .accept(transportCertificate::setGrossWeight)), + /** + * ⽪重 + */ + TARE_WEIGHT(Collections.emptyList(), OCRKey.KeyInfo.WEIGHT_TARE, (transportCertificate, string) -> convertWeightBigDecimal(string) + .accept(transportCertificate::setSkinWeight)), + /** + * 净重 + */ + NET_WEIGHT(Collections.emptyList(), OCRKey.KeyInfo.WEIGHT_NET, (transportCertificate, string) -> convertWeightBigDecimal(string) + .accept(transportCertificate::setNetWeight)), + /** + * 扣重 + */ + DEDUCT_WEIGHT(Collections.emptyList(), OCRKey.KeyInfo.WEIGHT_DEDUCT, (transportCertificate, string) -> convertWeightBigDecimal(string) + .accept(transportCertificate::setMiscellaneousWeight)), + /** + * 实际净重 + */ + RECEIVED_WEIGHT(Collections.emptyList(), OCRKey.KeyInfo.WEIGHT_RECEIVED, (transportCertificate, string) -> convertWeightBigDecimal(string) + .accept(transportCertificate::setRealityWeight)), + + ; + /** + * 图像类型列表 + */ + private final List imageTypes; + /** + * 字段配置 + */ + private final OCRKey.KeyInfo keyInfo; + /** + * setter方法 + */ + private final BiConsumer setter; + + TransportCertificateConvert(OCRImageType imageType, OCRKey.KeyInfo keyInfo, BiConsumer setter) { + this.imageTypes = List.of(imageType); + this.keyInfo = keyInfo; + this.setter = setter; + } + +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/exception/OCRException.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/exception/OCRException.java new file mode 100644 index 0000000..e635ae3 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/exception/OCRException.java @@ -0,0 +1,16 @@ +package org.springblade.thirdparty.ocr.exception; + +/** + * ocr 接口异常 + * @author bfhuange + * @date 2024/11/6 + */ +public class OCRException extends RuntimeException { + public OCRException(String message) { + super(message); + } + + public OCRException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/exception/OCRExceptionTranslator.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/exception/OCRExceptionTranslator.java new file mode 100644 index 0000000..a3d0790 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/exception/OCRExceptionTranslator.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.ocr.exception; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.api.ResultCode; +import org.springblade.core.tool.utils.Func; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author bfhuange + * @since 2025/3/29 + */ +@AllArgsConstructor +@Slf4j +@RestControllerAdvice +public class OCRExceptionTranslator { + + @ExceptionHandler(OCRException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public R handleError(OCRException e) { + log.error("ocr异常", e); + return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage())); + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/feign/IOCRClient.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/feign/IOCRClient.java new file mode 100644 index 0000000..216a6d7 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/feign/IOCRClient.java @@ -0,0 +1,53 @@ +package org.springblade.thirdparty.ocr.feign; + +import org.springblade.thirdparty.ocr.config.OCRFeignClientConfig; +import org.springblade.thirdparty.ocr.pojo.dto.OCRBigCommonCardBatchDTO; +import org.springblade.thirdparty.ocr.pojo.dto.OCRBigCommonCardDTO; +import org.springblade.thirdparty.ocr.pojo.dto.OCRSingleDTO; +import org.springblade.thirdparty.ocr.pojo.vo.OCRBigCommonCardVO; +import org.springblade.thirdparty.ocr.pojo.vo.OCRResultVO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; + +import java.util.List; + +/** + * ocr接口 磅单接口 + * @author bfhuange + * @date 2024/11/4 + */ +@FeignClient(name = "OCR", url = "${thirdParty.ocr.baseUrl}", configuration = OCRFeignClientConfig.class) +public interface IOCRClient { + + /** + * 批量磅单卡证识别(⼀个⽂件夹的数据)功能接⼝说明 + * @param param + * @param code + */ + @PostMapping("/BigCommonCardBatch") + OCRResultVO>> batchRecognition(@RequestBody OCRBigCommonCardBatchDTO param, @RequestHeader(value = "code", required = false) String code); + + /** + * 磅单卡证识别功能接⼝ + * @param param + */ + @PostMapping("/BigCommonCard") + OCRResultVO singleRecognition(@RequestBody OCRBigCommonCardDTO param, @RequestHeader(value = "code", required = false) String code); + + /** + * 磅单单独识别功能接⼝ + * @param param + */ + @PostMapping("/logisticRec") + OCRResultVO poundRecognition(@RequestBody OCRSingleDTO param, @RequestHeader(value = "code", required = false) String code); + + /** + * ⻋牌识别单独接⼝ + * @param param + */ + @PostMapping("/CLPR") + OCRResultVO plateNoRecognition(@RequestBody OCRSingleDTO param, @RequestHeader(value = "code", required = false) String code); + +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/feign/IOCROtherClient.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/feign/IOCROtherClient.java new file mode 100644 index 0000000..4dcdda6 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/feign/IOCROtherClient.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.ocr.feign; + +import org.springblade.thirdparty.ocr.config.OCRFeignClientConfig; +import org.springblade.thirdparty.ocr.pojo.dto.OCRCardBankDetRecDTO; +import org.springblade.thirdparty.ocr.pojo.vo.OCRBigCommonCardVO; +import org.springblade.thirdparty.ocr.pojo.vo.OCRResultVO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + * ocr接口 证件接口,因为端口不同,要新建一个 + * @author bfhuange + * @date 2024/11/4 + */ +@FeignClient(name = "OCRO", url = "${thirdParty.ocr.baseOtherUrl}", configuration = OCRFeignClientConfig.class) +public interface IOCROtherClient { + + /** + * 磅单业务-证件定位+结构化信息提取接⼝ + * @param param + * @return imageInfos 没有 + */ + @PostMapping("/CardBankDetRec") + OCRResultVO cardBankDetRec(@RequestBody OCRCardBankDetRecDTO param); +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/interceptor/OCRRequestInterceptor.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/interceptor/OCRRequestInterceptor.java new file mode 100644 index 0000000..270a874 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/interceptor/OCRRequestInterceptor.java @@ -0,0 +1,32 @@ +package org.springblade.thirdparty.ocr.interceptor; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.log.utils.LogTraceUtil; +import org.springblade.core.tool.utils.DateUtil; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.ocr.constant.OCRConstant; + +import java.util.Date; + +/** + * @author bfhuange + * @since 2024/12/26 + */ +@Slf4j +@AllArgsConstructor +public class OCRRequestInterceptor implements RequestInterceptor { + @Override + public void apply(RequestTemplate template) { + // 请求时间 + template.header(OCRConstant.REQUEST_HEADER_TIME, DateUtil.formatDateTime(new Date())); + String traceId = LogTraceUtil.getTraceIdFromMDC(); + if (StringUtil.isBlank(traceId)) { + traceId = LogTraceUtil.getTraceId(); + } + // traceId + template.header(OCRConstant.REQUEST_HEADER_TRACE_ID, traceId); + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/CertificateBatchRecognitionDTO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/CertificateBatchRecognitionDTO.java new file mode 100644 index 0000000..54eb4f9 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/CertificateBatchRecognitionDTO.java @@ -0,0 +1,34 @@ +package org.springblade.thirdparty.ocr.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 凭证批量识别参数,所有图像链接统一识别,不分类 + * @author bfhuange + * @since 2025/3/13 + */ +@Data +public class CertificateBatchRecognitionDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 所有图像链接列表 + */ + private List urls; + /** + * 项目简称 + */ + private String projectAbbreviation; + /** + * 凭证编码 + */ + private String code; + /** + * 目录名称 + */ + private String dirName; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/CertificateRecognitionDTO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/CertificateRecognitionDTO.java new file mode 100644 index 0000000..331fbb1 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/CertificateRecognitionDTO.java @@ -0,0 +1,37 @@ +package org.springblade.thirdparty.ocr.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 凭证识别参数,依次调用单张识别 + * @author bfhuange + * @since 2024/12/13 + */ +@Data +public class CertificateRecognitionDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 磅单url + */ + private String poundUrl; + /** + * 车头url + */ + private String carFrontUrl; + /** + * 驾驶证url + */ + private String driverLicenseUrl; + /** + * 行驶证url + */ + private String vehicleLicenseUrl; + /** + * 项目简称 + */ + private String name; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRBigCommonCardBatchDTO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRBigCommonCardBatchDTO.java new file mode 100644 index 0000000..3b9acde --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRBigCommonCardBatchDTO.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * ocr批量磅单卡证识别参数 + * @author bfhuange + * @date 2024/11/4 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class OCRBigCommonCardBatchDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 图像本地路径或者远程服务器地址列表 + */ + @JsonProperty("img_path") + private List imgPaths; + /** + * 项目简称 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String name; + /** + * 目录名称 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String dirName; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRBigCommonCardDTO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRBigCommonCardDTO.java new file mode 100644 index 0000000..1066136 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRBigCommonCardDTO.java @@ -0,0 +1,54 @@ +package org.springblade.thirdparty.ocr.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.util.List; + +/** + * ocr批量磅单卡证识别参数 + * @author bfhuange + * @date 2024/11/4 + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Data +public class OCRBigCommonCardDTO extends OCRSingleDTO { + @Serial + private static final long serialVersionUID = 1L; + /** + * 非必填 + * 可选值 -1, 0, 1, 2, 3 + * 输⼊图⽚的⽂本⽅向, 如果需要系统识别则为默认的-1(可不传), 0: 正⽅向, 1:顺时针90度, 2: 180, 3: -90 + */ + @JsonProperty("direction_id") + private Integer directionId; + /** + * 非必填 + * 可选值 True, False + * 是否是区域图, 如果True: 表⽰不需要调⽤证件定位模型获取区域 + */ + @JsonProperty("is_rect") + private String isRect; + /** + * 非必填 + * ["VehicleLicense-Main", "IDCard-Main", "IDCard-Side", "DriverLicense-Main"] + * 需要提取信息的ID列表, 不填写表⽰都要提取, 如果填写则只返回所需要列表中的信息, 返回结构不变 + */ + @JsonProperty("need_data_type") + private List needDataTypes; + + public OCRBigCommonCardDTO(String imgPath, String name) { + super(imgPath, name); + } + + public OCRBigCommonCardDTO(String imgPath, String name, List needDataTypes) { + super(imgPath, name); + this.needDataTypes = needDataTypes; + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRCardBankDetRecDTO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRCardBankDetRecDTO.java new file mode 100644 index 0000000..5c1695a --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRCardBankDetRecDTO.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.NullSerializer; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * ocr 证件识别参数 + * @author bfhuange + * @date 2024/11/6 + */ +@NoArgsConstructor +@Data +public class OCRCardBankDetRecDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 图像本地路径或者远程服务器地址,必填 + */ + @JsonProperty("img_path") + private String imgPath; + /** + * 图像内容类型,位置为"", ["", "BankCard-Main","BankCard-Side", "IDCard-Main", "IDCard-Side"] 其他依次对应: 银⾏卡-正⾯、银⾏卡-背⾯、身份证-信息⻚、身份证-国徽⻚,非必填 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("data_type") + private String dataType; + + public OCRCardBankDetRecDTO(String imgPath) { + this.imgPath = imgPath; + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRSingleDTO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRSingleDTO.java new file mode 100644 index 0000000..05b9dd7 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/dto/OCRSingleDTO.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.ocr.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * ocr单张识别参数 + * @author bfhuange + * @date 2024/12/13 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class OCRSingleDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 图像本地路径或者远程服务器地址 + */ + @JsonProperty("img_path") + private String imgPath; + /** + * 项目简称 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String name; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/IDCardVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/IDCardVO.java new file mode 100644 index 0000000..78c09ba --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/IDCardVO.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springblade.core.tool.utils.DateUtil; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 身份证识别结果 + * @author bfhuange + * @date 2024/11/6 + */ +@Data +public class IDCardVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 姓名 + */ + private String name; + /** + * 身份证号 + */ + private String idNo; + /** + * 有效期止 + */ + @JsonFormat(pattern = DateUtil.PATTERN_DATE, timezone = "GMT+8") + private Date validDateEnd; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRBigCommonCardVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRBigCommonCardVO.java new file mode 100644 index 0000000..b351934 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRBigCommonCardVO.java @@ -0,0 +1,29 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * ocr 磅单卡证识别响应参数 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRBigCommonCardVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 原始图像的识别信息-图像相关 + */ + // ocr返回的结构有点问题,先注释掉,这个对象没用到 + //@JsonProperty("image_info") + //private OCRImageInfoSummaryVO imageInfos; + /** + * 每⼀个证件⽬标的识别结果,列表顺序对应type_descriptions中的顺序 + */ + private List objects; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRImageHashVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRImageHashVO.java new file mode 100644 index 0000000..d7fc541 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRImageHashVO.java @@ -0,0 +1,23 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * ocr 磅单卡证识别响应参数 图⽚hash值 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRImageHashVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 感知hash,⽀持轻微旋转移动和轻微质量丢失 + */ + @JsonProperty("phash") + private String phash; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRImageInfoSummaryVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRImageInfoSummaryVO.java new file mode 100644 index 0000000..9a0005d --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRImageInfoSummaryVO.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * ocr 磅单卡证识别响应参数 原始图像的识别信息-图像相关 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRImageInfoSummaryVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 图像评分,⽬前只需要取list中的第⼀位即可,1: 正常图像、0: 图像 质量差, 其他值: 算法还需要进⼀步判断 + */ + @JsonProperty("QA_scores") + private List qaScores; + /** + * 图像中包含的数据类型(这⾥的顺序对应下⾯结构化结果的顺序) + */ + @JsonProperty("type_descriptions") + private List typeDescriptions; + /** + * 图像中包含的数据类型id(这⾥的顺序对应下⾯结构化结果的顺序) + */ + @JsonProperty("type_ids") + private List typeIds; + /** + * 图⽚hash值 + */ + @JsonProperty("img_hash") + private OCRImageHashVO imageHash; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRKeyInfoVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRKeyInfoVO.java new file mode 100644 index 0000000..87f42d4 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRKeyInfoVO.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * ocr 磅单卡证识别响应参数 结构化提取后的key-value-description信息 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRKeyInfoVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * ?字段编码 + */ + @JsonProperty("key") + private String key; + /** + * ?字段说明 + */ + @JsonProperty("description") + private String description; + /** + * ?字段值 + */ + @JsonProperty("value") + private String value; + /** + * ?匹配度,有些没有 + */ + @JsonProperty("rec_score") + private BigDecimal recScore; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRLineInfoVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRLineInfoVO.java new file mode 100644 index 0000000..f66ea53 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRLineInfoVO.java @@ -0,0 +1,46 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** + * ocr 磅单卡证识别响应参数 行信息 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRLineInfoVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * ?? 区域坐标 + */ + @JsonProperty("bbox") + private List bBoxes; + /** + * ?? + */ + private String direct; + /** + * 匹配度 ? + */ + private BigDecimal score; + /** + * 文本? + */ + private String text; + /** + * ?? 有些没有 + */ + private List> polygons; + /** + * ?? 有些没有 + */ + @JsonProperty("position") + private List positions; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRRecognitionImageInfoVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRRecognitionImageInfoVO.java new file mode 100644 index 0000000..dbf5001 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRRecognitionImageInfoVO.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * ocr 磅单卡证识别响应参数 ⼦⽬标证件的基本识别信息 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRRecognitionImageInfoVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 证件⽬标对于原始图像的坐标位置[x, y, w, h] + */ + // @JsonProperty("bbox") + // private List bBoxes; + /** + * 证件⽬标在原始图像中的⻆度 + */ + // @JsonProperty("image_angle") + // private Integer imageAngle; + /** + * 证件⽬标的证件类型 + */ + // @JsonProperty("type_description") + // private String typeDescription; + /** + * 磅单对应的ID号(⽬前只有磅单返回) + */ + @JsonProperty("type_id") + private String typeId; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRRecognitionResultVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRRecognitionResultVO.java new file mode 100644 index 0000000..136b77e --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRRecognitionResultVO.java @@ -0,0 +1,34 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * ocr 磅单卡证识别响应参数 证件⽬标的识别结果 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRRecognitionResultVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 证件⽬标结构化结果(系统调⽤主要使⽤的信息) + */ + @JsonProperty("structured") + private OCRStructureVO structured; + /** + * ⼦⽬标证件的基本识别信息 + */ + @JsonProperty("image_info") + private OCRRecognitionImageInfoVO imageInfo; + /** + * 所有的识别结果,非必须 + */ + //@JsonProperty("lines") + //private List lines; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRResultVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRResultVO.java new file mode 100644 index 0000000..ad41338 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRResultVO.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import lombok.Data; +import org.springblade.core.tool.support.NoErrorFallback; + +import java.io.Serial; +import java.io.Serializable; + +/** + * ocr 返回结果 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRResultVO implements Serializable, NoErrorFallback { + @Serial + private static final long serialVersionUID = 1L; + /** + * 程序执⾏状态(200: 成功且为卡证数据, + * -1:图像是⻋牌,但质量有问题, + * -2: 没有检测出⻋牌或证件⽬标, + * -3: 输⼊的img_bas64有问题, + * -4: 没有检测出证件, + * -5: 输⼊的数据类型不⽀持(⽀持img_bas64/img_path), + * -6: C++接⼝问题, + * -7: 图像质量问题,有⻋辆没有⻋牌, + * -8: 运⾏成功但程序识别内容为空, + * -9: json数据异常) + */ + private Integer code; + /** + * code的解释说明 + */ + private String message; + /** + * 返回的识别结果 + */ + private T result; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRStructureVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRStructureVO.java new file mode 100644 index 0000000..fe8a254 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/OCRStructureVO.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * ocr 磅单卡证识别响应参数 证件⽬标结构化结果 + * @author bfhuange + * @date 2024/11/4 + */ +@Data +public class OCRStructureVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 磅单对应的ID号(⽬前只有磅单返回) + */ + @JsonProperty("type_id") + private String typeId; + /** + * 证件⽬标的证件类型 + */ + @JsonProperty("type_description") + private String typeDescription; + /** + * 磅单对应的ID号的单位(⽬前只有磅单返回), 当未知单位的时候返回"", ⽬前会返回的字段有: t, T, 吨, kg, 公⽄, 千克 + */ + @JsonProperty("unit_measure") + private String unitMeasure; + /** + * 结构化提取后的key-value-description信息 + */ + @JsonProperty("key_info") + private List keyInfos; +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/TransportCertificateVO.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/TransportCertificateVO.java new file mode 100644 index 0000000..0392e78 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/pojo/vo/TransportCertificateVO.java @@ -0,0 +1,135 @@ +package org.springblade.thirdparty.ocr.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.NullSerializer; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springblade.core.tool.utils.DateUtil; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 车辆运输凭证 + * @author bfhuange + * @date 2024/11/6 + */ +@Data +@Schema(description = "车辆运输凭证识别结果") +public class TransportCertificateVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 车头车牌号 + */ + @Schema(description = "车头车牌号") + private String carFrontPlateNo; + /** + * 车尾车牌号 + */ + @Schema(description = "车尾车牌号") + private String carBackPlateNo; + /** + * 行驶证上的车牌号 + */ + @Schema(description = "行驶证上的车牌号") + private String drivingPlateNo; + /** + * 驾驶证有效期限(终⽌时间) + */ + @JsonFormat(pattern = DateUtil.PATTERN_DATE,timezone = "GMT+8") + @Schema(description = "驾驶证有效期限(终⽌时间)") + private Date driverLicenseValidDateEnd; + /** + * 磅单车牌号 + */ + @Schema(description = "磅单车牌号") + private String poundPlateNo; + /** + * 磅单供货日期 + */ + @JsonFormat(pattern = DateUtil.PATTERN_DATE,timezone = "GMT+8") + @Schema(description = "磅单供货日期") + private Date supplyMaterialDate; + /** + * 货物名称 + */ + @Schema(description = "货物名称") + private String productName; + /** + * 规格料型 + */ + @Schema(description = "规格料型") + private String specificationMaterial; + /** + * 单位 + */ + @Schema(description = "单位") + private String unitMeasure; + /** + * ⽑重 + */ + @JsonSerialize(using = ToStringSerializer.class, nullsUsing = NullSerializer.class) + @Schema(description = "⽑重") + private BigDecimal grossWeight; + /** + * ⽪重 + */ + @JsonSerialize(using = ToStringSerializer.class, nullsUsing = NullSerializer.class) + @Schema(description = "⽪重") + private BigDecimal skinWeight; + /** + * 净重 + */ + @JsonSerialize(using = ToStringSerializer.class, nullsUsing = NullSerializer.class) + @Schema(description = "净重") + private BigDecimal netWeight; + /** + * 扣重 + */ + @JsonSerialize(using = ToStringSerializer.class, nullsUsing = NullSerializer.class) + @Schema(description = "扣重") + private BigDecimal miscellaneousWeight; + /** + * 实际净重 + */ + @JsonSerialize(using = ToStringSerializer.class, nullsUsing = NullSerializer.class) + @Schema(description = "实际净重") + private BigDecimal realityWeight; + /** + * 磅单附件索引,只取一个,其余的丢弃 + */ + @Schema(description = "磅单附件索引,只取一个,其余的丢弃") + private List poundIndexes = new ArrayList<>(); + /** + * 驾驶证附件索引,只取一个,其余的丢弃 + */ + @Schema(description = "驾驶证附件索引,只取一个,其余的丢弃") + private List driverLicenseIndexes = new ArrayList<>(); + /** + * 行驶证附件索引,只取一个,其余的丢弃 + */ + @Schema(description = "行驶证附件索引,只取一个,其余的丢弃") + private List vehicleLicenseIndexes = new ArrayList<>(); + /** + * 车头附件索引,只取一个,其余的丢弃 + */ + @Schema(description = "车头附件索引,只取一个,其余的丢弃") + private List carFrontIndexes = new ArrayList<>(); + /** + * 车尾附件索引,只取一个,其余的丢弃 + */ + @Schema(description = "车尾附件索引,只取一个,其余的丢弃") + private List carBackIndexes = new ArrayList<>(); + /** + * 其他附件索引 + */ + @Schema(description = "其他附件索引") + private List otherIndexes = new ArrayList<>(); +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/service/IOCRService.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/service/IOCRService.java new file mode 100644 index 0000000..7cf74af --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/service/IOCRService.java @@ -0,0 +1,39 @@ +package org.springblade.thirdparty.ocr.service; + +import org.springblade.thirdparty.ocr.pojo.dto.CertificateBatchRecognitionDTO; +import org.springblade.thirdparty.ocr.pojo.dto.CertificateRecognitionDTO; +import org.springblade.thirdparty.ocr.pojo.vo.IDCardVO; +import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO; + +import java.util.List; + +/** + * ocr 接口 + * @author bfhuange + * @date 2024/11/6 + */ +public interface IOCRService { + + /** + * 识别车辆运输凭证,同一个车辆的图片地址批量传入,不知道每张图片类型 + * @param param 参数 + * @return + */ + TransportCertificateVO recognitionTransportCertificate(CertificateBatchRecognitionDTO param); + + /** + * 识别车辆运输凭证,依次调用单独识别接口,知道每张图片类型 + * + * @param param 各个类型图片链接 + * @param code + * @return + */ + TransportCertificateVO recognitionTransportCertificate(CertificateRecognitionDTO param, String code); + + /** + * 识别身份证,支持同时传同一身份证的正反面,不同身份证的会覆盖 + * @param urls 图片链接列表 + * @return + */ + IDCardVO recognitionIDCard(List urls); +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/service/impl/OCRServiceImpl.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/service/impl/OCRServiceImpl.java new file mode 100644 index 0000000..63b7788 --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/service/impl/OCRServiceImpl.java @@ -0,0 +1,369 @@ +package org.springblade.thirdparty.ocr.service.impl; + +import cn.hutool.core.lang.TypeReference; +import cn.hutool.json.JSONUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Nullable; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.ObjectUtil; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.ocr.constant.OCRConstant; +import org.springblade.thirdparty.ocr.constant.OCRImageType; +import org.springblade.thirdparty.ocr.convert.AttachmentIndexConvert; +import org.springblade.thirdparty.ocr.exception.OCRException; +import org.springblade.thirdparty.ocr.feign.IOCRClient; +import org.springblade.thirdparty.ocr.feign.IOCROtherClient; +import org.springblade.thirdparty.ocr.pojo.dto.*; +import org.springblade.thirdparty.ocr.pojo.vo.*; +import org.springblade.thirdparty.ocr.service.IOCRService; +import org.springblade.thirdparty.ocr.util.ConvertUtil; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +/** + * @author bfhuange + * @date 2024/11/6 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class OCRServiceImpl implements IOCRService { + /** + * 限制 200,超过转为kg,否则为t + */ + private static final BigDecimal WEIGHT_LIMIT = BigDecimal.valueOf(200); + + private final IOCRClient client; + private final IOCROtherClient otherClient; + + @Override + public TransportCertificateVO recognitionTransportCertificate(CertificateBatchRecognitionDTO param) { + log.info("ocr 批量识别参数:{}", JSONUtil.toJsonStr(param)); + if (param == null || CollectionUtils.isEmpty(param.getUrls())) { + throw new OCRException("图像链接不能为空"); + } + List> result = this.getCardResult(() -> client.batchRecognition(new OCRBigCommonCardBatchDTO(param.getUrls(), param.getProjectAbbreviation(), param.getDirName()), param.getCode()), "批量磅单卡证识别"); + List cards = result.stream() + .map(OCRResultVO::getResult) + .toList(); + return convertTransportCertificate(cards); + } + + @Override + public TransportCertificateVO recognitionTransportCertificate(CertificateRecognitionDTO param, String code) { + log.info("ocr 单张识别参数:{}", JSONUtil.toJsonStr(param)); + if (param == null || ObjectUtil.isEmpty(param)) { + throw new OCRException("凭证识别参数不能为空"); + } + List cards = new ArrayList<>(4); + if (StringUtil.isNotBlank(param.getPoundUrl())) { + // 磅单 + OCRBigCommonCardVO card = this.getCardResult(() -> client.poundRecognition(new OCRSingleDTO(param.getPoundUrl(), param.getName()), code), "磅单单独识别"); + if (card != null) { + cards.add(card); + } + } + if (StringUtil.isNotBlank(param.getDriverLicenseUrl())) { + // 驾驶证 + OCRBigCommonCardVO card = this.getCardResult(() -> client.singleRecognition(new OCRBigCommonCardDTO(param.getDriverLicenseUrl(), param.getName(), + // 驾驶证-正页 + List.of(OCRImageType.DRIVER_LICENSE_MAIN.getId(), + // 驾驶证-副页 + OCRImageType.DRIVER_LICENSE_SIDE.getId(), + // 驾驶证-电子版 + OCRImageType.DRIVER_LICENSE_ELEC.getId())), code + ), "驾驶证识别"); + if (card != null) { + cards.add(card); + } + } + if (StringUtil.isNotBlank(param.getVehicleLicenseUrl())) { + // 行驶证 + OCRBigCommonCardVO card = this.getCardResult(() -> client.singleRecognition(new OCRBigCommonCardDTO(param.getVehicleLicenseUrl(), param.getName(), + // ⾏驶证-正⻚-挂车 + List.of(OCRImageType.VEHICLE_LICENSE_MAIN_GUA.getId(), + // ⾏驶证-正⻚ + OCRImageType.VEHICLE_LICENSE_MAIN.getId(), + // ⾏驶证-副⻚ + OCRImageType.VEHICLE_LICENSE_SIDE.getId())), code + ), "行驶证识别"); + if (card != null) { + cards.add(card); + } + } + if (StringUtil.isNotBlank(param.getCarFrontUrl())) { + // 车头 + OCRBigCommonCardVO card = this.getCardResult(() -> client.plateNoRecognition(new OCRSingleDTO(param.getCarFrontUrl(), param.getName()), code), "⻋牌单独识别"); + if (card != null) { + cards.add(card); + } + } + return CollectionUtil.isEmpty(cards) ? null : convertTransportCertificate(cards); + } + + /** + * 获取卡证结果 + * @param resultSupplier + * @param name + * @return + */ + private T getCardResult(Supplier> resultSupplier, String name) { + try { + OCRResultVO resultVO = resultSupplier.get(); + log.info("调用ocr接口 {} 响应参数:{}", name, JSONUtil.toJsonStr(resultVO)); + if (resultVO != null && OCRConstant.SUCCESS_CODE.equals(resultVO.getCode())) { + // 成功 + return resultVO.getResult(); + } + log.error("调用ocr接口 {} 异常响应参数:{}", name, JSONUtil.toJsonStr(resultVO)); + String message = getMessage(resultVO); + throw new OCRException("调用ocr接口" + name + "异常 " + message); + } catch (OCRException e) { + throw e; + } catch (Exception e) { + log.error("调用ocr接口 {} 异常", name, e); + throw new OCRException("调用ocr接口" + name + "异常"); + } + } + + /** + * 转换车辆凭证 + * @param cards + * @return + */ + private static TransportCertificateVO convertTransportCertificate(List cards) { + TransportCertificateVO result = new TransportCertificateVO(); + if (CollectionUtils.isEmpty(cards)) { + return result; + } + OCRBigCommonCardVO card = null; + for (int index = 0; index < cards.size(); index++) { + card = cards.get(index); + convert(result, card, ConvertUtil::getTransportCertificateSetter, index); + } + // 处理重量单位 + handleWeightUnit(result); + return result; + } + + /** + * 处理重量单位 + * @param result + */ + private static void handleWeightUnit(TransportCertificateVO result) { + if (result == null) { + return; + } + // 重量超过200,默认kg,否则t + if (overLimit(result.getGrossWeight()) || overLimit(result.getSkinWeight()) || overLimit(result.getNetWeight())) { + // 毛重、皮重、净重 超过限制,是kg + result.setGrossWeight(divideThousand(result.getGrossWeight())); + result.setSkinWeight(divideThousand(result.getSkinWeight())); + result.setNetWeight(divideThousand(result.getNetWeight())); + result.setMiscellaneousWeight(divideThousand(result.getMiscellaneousWeight())); + result.setRealityWeight(divideThousand(result.getRealityWeight())); + return; + } + String unitMeasure = result.getUnitMeasure(); + if (OCRConstant.UNIT_T.equalsIgnoreCase(unitMeasure) || OCRConstant.UNIT_T_ZH.equals(unitMeasure)) { + // t或者吨,无需处理 + return; + } + if (OCRConstant.UNIT_KG.equalsIgnoreCase(unitMeasure)) { + // kg,转成吨保留5位小数 + result.setGrossWeight(divideThousand(result.getGrossWeight())); + result.setSkinWeight(divideThousand(result.getSkinWeight())); + result.setNetWeight(divideThousand(result.getNetWeight())); + result.setMiscellaneousWeight(divideThousand(result.getMiscellaneousWeight())); + result.setRealityWeight(divideThousand(result.getRealityWeight())); + return; + } + if (StringUtil.isNotBlank(unitMeasure)) { + log.warn("未知的单位类型:{}", unitMeasure); + } + } + + /** + * 是否超过限制 + * @param weight + * @return + */ + private static boolean overLimit(BigDecimal weight) { + return weight != null && weight.compareTo(WEIGHT_LIMIT) > 0; + } + + /** + * 除以1000保留5位小数 + * @param num + * @return + */ + private static BigDecimal divideThousand(BigDecimal num) { + if (num == null) { + return null; + } + return num.divide(BigDecimal.valueOf(1000), 5, RoundingMode.HALF_UP); + } + + @Override + public IDCardVO recognitionIDCard(List urls) { + if (CollectionUtils.isEmpty(urls)) { + throw new OCRException("图像链接不能为空"); + } + IDCardVO result = new IDCardVO(); + try { + for (String url : urls) { + OCRCardBankDetRecDTO param = new OCRCardBankDetRecDTO(url); + OCRResultVO resultVO = otherClient.cardBankDetRec(param); + if (resultVO != null && OCRConstant.SUCCESS_CODE.equals(resultVO.getCode())) { + // 成功 + OCRBigCommonCardVO card = resultVO.getResult(); + convertIDCard(result, card); + continue; + } + log.error("调用ocr接口证件定位+结构化信息提取异常响应参数:{}", JSONUtil.toJsonStr(resultVO)); + String message = getMessage(resultVO); + throw new OCRException("调用ocr接口证件定位+结构化信息提取异常 " + message); + } + } catch (OCRException e) { + throw e; + } catch (Exception e) { + log.error("调用ocr接口证件定位+结构化信息提取异常", e); + throw new OCRException("调用ocr接口证件定位+结构化信息提取异常"); + } + return result; + } + + /** + * 转换身份证 + * @param result + * @param card + * @return + */ + private void convertIDCard(IDCardVO result, OCRBigCommonCardVO card) { + convert(result, card, ConvertUtil::getIDCardSetter, null); + } + + /** + * 转换 + * + * @param + * @param result + * @param card + * @param setterGetter + * @param index + */ + private static void convert(T result, OCRBigCommonCardVO card, BiFunction> setterGetter, Integer index) { + if (result == null || card == null || setterGetter == null) { + return; + } + + List objects = card.getObjects(); + if (CollectionUtils.isEmpty(objects)) { + return; + } + // 排序类型 + sortByTypeId(objects); + for (OCRRecognitionResultVO object : objects) { + OCRStructureVO structured = object.getStructured(); + String typeId = getTypeId(object); + if (typeId == null) { + continue; + } + // 是否处理数据 + boolean handleData = true; + if (index != null && result instanceof TransportCertificateVO transportCertificate) { + // 车辆凭证类型,添加索引到list + // 添加到默认list要处理数据,添加到其他list,不处理数据 + handleData = AttachmentIndexConvert.getConvertByTypeId(typeId) + .addIndex2list(transportCertificate, index); + } + if (structured == null || !handleData) { + // 提取的数据为空或添加到了其他附件,不处理数据了,保持图片和数据一致 + continue; + } + List keyInfos = structured.getKeyInfos(); + if (CollectionUtils.isEmpty(keyInfos)) { + continue; + } + for (OCRKeyInfoVO keyInfo : keyInfos) { + // 根据typeId和key查找setter,找到了执行 + Optional.ofNullable(setterGetter.apply(typeId, keyInfo.getKey())) + .ifPresent(consumer -> consumer.accept(result, keyInfo.getValue())); + } + } + } + + /** + * 按类型排序 + * @param objects + */ + private static void sortByTypeId(List objects) { + // 行驶证类型的,把 VehicleLicense-Main 放到第一个,优先取它的数据 + List imageTypeIds = AttachmentIndexConvert.VEHICLE_LICENSE.getImageTypeIds(); + boolean vehicleLicense = objects.stream() + .map(OCRServiceImpl::getTypeId) + .filter(Objects::nonNull) + .anyMatch(imageTypeIds::contains); + if (vehicleLicense) { + objects.sort(Comparator.comparing(OCRServiceImpl::getTypeId, Comparator.comparing(typeId -> { + if (typeId == null) { + return Integer.MAX_VALUE; + } + int index = imageTypeIds.indexOf(typeId); + if (index < 0) { + return Integer.MAX_VALUE - 1; + } + return index; + }))); + } + } + + @Nullable + private static String getTypeId(OCRRecognitionResultVO object) { + OCRStructureVO structured = object.getStructured(); + String typeId = Optional.ofNullable(structured) + .map(OCRStructureVO::getTypeId) + .orElse(null); + if (typeId == null) { + // typeId为空,从 imageInfo 中取 + OCRRecognitionImageInfoVO imageInfo = object.getImageInfo(); + typeId = Optional.ofNullable(imageInfo) + .map(OCRRecognitionImageInfoVO::getTypeId) + .orElse(null); + } + return typeId; + } + + /** + * 获取错误消息 + * @param resultVO + * @return + * @param + */ + private String getMessage(OCRResultVO resultVO) { + return resultVO == null ? "" : resultVO.getMessage(); + } + + public static void main(String[] args) { + String json = ""; + OCRResultVO>> resultVO = JSONUtil.toBean(json, new TypeReference>>>() { + }, true); + if (resultVO != null && OCRConstant.SUCCESS_CODE.equals(resultVO.getCode())) { + // 成功 + List> result = resultVO.getResult(); + List cards = result.stream() + .map(OCRResultVO::getResult) + .toList(); + TransportCertificateVO transportCertificateVO = convertTransportCertificate(cards); + System.out.println(JSONUtil.toJsonStr(transportCertificateVO)); + } + } +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/util/ConvertUtil.java b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/util/ConvertUtil.java new file mode 100644 index 0000000..0c2680b --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/java/org/springblade/thirdparty/ocr/util/ConvertUtil.java @@ -0,0 +1,244 @@ +package org.springblade.thirdparty.ocr.util; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.utils.DateUtil; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.ocr.constant.OCRImageType; +import org.springblade.thirdparty.ocr.convert.IDCardConvert; +import org.springblade.thirdparty.ocr.convert.TransportCertificateConvert; +import org.springblade.thirdparty.ocr.pojo.vo.IDCardVO; +import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 转换工具类 + * @author bfhuange + * @date 2024/11/7 + */ +@Slf4j +public class ConvertUtil { + private ConvertUtil() {} + + /** + * 有效期分隔符 + */ + private static final String DATE_VALID_SPLIT = "至"; + /** + * 有效期至 长期 + */ + private static final String DATE_VALID_END_LONG_TERM = "长期"; + + /** + * 非磅单类型set + */ + private static final Set NONE_POUND_TYPE_SET = initNonePoundTypeSet(); + /** + * 车辆凭证setter map + */ + private static final Map> TRANSPORT_CERTIFICATE_SETTER_MAP = initTransportCertificateSetterMap(); + /** + * 身份证setter map + */ + private static final Map>> ID_CARD_SETTER_MAP = initIDCardSetterMap(); + + /** + * 获取车辆凭证setter + * @param typeId + * @param key + * @return + */ + public static BiConsumer getTransportCertificateSetter(String typeId, String key) { + String mapKey = ConvertUtil.generateKey(typeId, key); + return TRANSPORT_CERTIFICATE_SETTER_MAP.get(mapKey); + } + + /** + * 获取身份证setter + * @param typeId + * @param key + * @return + */ + public static BiConsumer getIDCardSetter(String typeId, String key) { + Map> setterMap = ID_CARD_SETTER_MAP.get(typeId); + if (setterMap == null) { + return null; + } + BiConsumer setter = setterMap.get(key); + if (setter == null) { + return null; + } + return setter; + } + + /** + * 转换日期 + * @param dateString + * @return + */ + public static Consumer> convertDate(String dateString) { + Date date = ConvertUtil.convert2date(dateString); + return consumer -> consumer.accept(date); + } + + /** + * 转换有效期 + * @param dateString + * @return + */ + public static Consumer> convertDateValid(String dateString) { + Date date = ConvertUtil.convert2dateValid(dateString); + return consumer -> consumer.accept(date); + } + + /** + * 转换有效期 + * @param dateString 日期字符串,2015-05-31至2025-05-31 + * @return + */ + private static Date convert2dateValid(String dateString) { + if (StringUtil.isBlank(dateString)) { + return null; + } + if (dateString.contains(DATE_VALID_END_LONG_TERM)) { + // 如果有效期至是长期,设置为10年后 + return DateUtil.plusYears(new Date(), 10); + } + // 按至分隔,解析最后一截日期 + String[] splitDate = dateString.split(DATE_VALID_SPLIT); + return ConvertUtil.convert2date(splitDate[splitDate.length -1]); + } + + /** + * 截取有效期止 + * @param dateString + * @return + */ + public static Consumer> extractEndDate(String dateString) { + Date date = ConvertUtil.parseEndDate(dateString); + return consumer -> consumer.accept(date); + } + + /** + * 解析有效期止 + * @param dateString 日期字符串,格式 2020.12.15-2030.12.15 + * @return + */ + private static Date parseEndDate(String dateString) { + if (dateString == null || dateString.isBlank()) { + return null; + } + dateString = dateString.substring(dateString.lastIndexOf("-") + 1); + return DateUtil.parse(dateString, "yyyy.MM.dd"); + } + + /** + * 转换重量数值 + * + * @param numString 重量字符串 + * @return + */ + public static Consumer> convertWeightBigDecimal(String numString) { + BigDecimal bigDecimal = ConvertUtil.convert2WeightBigDecimal(numString); + return consumer -> consumer.accept(bigDecimal); + } + + /** + * 字符串转日期 + * @param dateString + * @return + */ + private static Date convert2date(String dateString) { + if (dateString == null || dateString.isBlank()) { + return null; + } + try { + return DateUtil.parse(dateString, "yyyy-MM-dd"); + } catch (Exception e) { + log.error("解析日期异常", e); + return null; + } + } + + /** + * 字符串转数值 + * + * @param numString + * @return + */ + public static BigDecimal convert2WeightBigDecimal(String numString) { + if (numString == null || numString.isBlank()) { + return null; + } + try { + return new BigDecimal(numString); + } catch (Exception e) { + log.error("解析数值异常", e); + return null; + } + } + + /** + * 初始化 非磅单类型set + * @return + */ + private static Set initNonePoundTypeSet() { + return Stream.of(OCRImageType.values()) + .map(OCRImageType::getId) + .collect(Collectors.toSet()); + } + + /** + * 初始化setter map + * @return + */ + private static Map> initTransportCertificateSetterMap() { + Map> setterMap = new HashMap<>(); + for (TransportCertificateConvert convert : TransportCertificateConvert.values()) { + if (convert.getImageTypes().isEmpty()) { + // 图像类型列表为空,设置类型id为空,只用key + String key = ConvertUtil.generateKey(null, convert.getKeyInfo().getKey()); + setterMap.put(key, convert.getSetter()); + } else { + for (OCRImageType imageType : convert.getImageTypes()) { + // 图像类型列表不为空,每个类型id和key重新生成key + String key = ConvertUtil.generateKey(imageType.getId(), convert.getKeyInfo().getKey()); + setterMap.put(key, convert.getSetter()); + } + } + } + return setterMap; + } + + /** + * 初始化 身份证 setter map + * @return + */ + private static Map>> initIDCardSetterMap() { + return Stream.of(IDCardConvert.values()) + // 先按类型id分组 + .collect(Collectors.groupingBy(convert -> convert.getImageType().getId(), + // 再按key分组 + Collectors.toMap(convert -> convert.getKeyInfo().getKey(), IDCardConvert::getSetter, (a, b) -> a) + )); + } + + /** + * 生成key,typeId + - + key + * @param typeId + * @param key + * @return + */ + private static String generateKey(String typeId, String key) { + return typeId == null || !NONE_POUND_TYPE_SET.contains(typeId)? key : typeId + "-" + key; + } + +} diff --git a/blade-third-party-api/blade-ocr-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/blade-third-party-api/blade-ocr-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..2c6637b --- /dev/null +++ b/blade-third-party-api/blade-ocr-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springblade.thirdparty.ocr.config.ThirdPartyOCRAutoConfiguration \ No newline at end of file diff --git a/blade-third-party-api/blade-track-api/pom.xml b/blade-third-party-api/blade-track-api/pom.xml new file mode 100644 index 0000000..e2a07cf --- /dev/null +++ b/blade-third-party-api/blade-track-api/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + org.springblade + blade-third-party-api + ${revision} + + + blade-track-api + ${project.artifactId} + jar + + + + org.springblade + blade-core-tool + + + \ No newline at end of file diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/ThirdPartyTrackAutoConfiguration.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/ThirdPartyTrackAutoConfiguration.java new file mode 100644 index 0000000..d90e722 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/ThirdPartyTrackAutoConfiguration.java @@ -0,0 +1,16 @@ +package org.springblade.thirdparty.track.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * 第三方api 轨迹配置类 + * @author bfhuange + * @date 2024/12/18 + */ +@ComponentScan("org.springblade.thirdparty.track") +@EnableConfigurationProperties(TrackProperties.class) +@Configuration +public class ThirdPartyTrackAutoConfiguration { +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/TrackFeignClientConfig.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/TrackFeignClientConfig.java new file mode 100644 index 0000000..ecb5b1a --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/TrackFeignClientConfig.java @@ -0,0 +1,17 @@ +package org.springblade.thirdparty.track.config; + +import feign.RequestInterceptor; +import org.springframework.context.annotation.Bean; + +/** + * @author bfhuange + * @since 2024/12/18 + */ +public class TrackFeignClientConfig { + @Bean + public RequestInterceptor requestInterceptor() { + return template -> { + // 空实现屏蔽 全局拦截器 BladeFeignRequestInterceptor + }; + } +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/TrackProperties.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/TrackProperties.java new file mode 100644 index 0000000..8cd8a8b --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/config/TrackProperties.java @@ -0,0 +1,31 @@ +package org.springblade.thirdparty.track.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 轨迹 配置 + * @author bfhuange + * @since 2024/12/16 + */ +@Data +@ConfigurationProperties(prefix = "third-party.track") +public class TrackProperties { + /** + * 是否启用推送到轨迹系统 + */ + private boolean enabled = false; + /** + * 轨迹基础路径 + */ + private String baseUrl; + /** + * 系统标识 + */ + private Integer bpsType; + /** + * 轨迹url + */ + private String trackUrlFormat = "/#/home/location-path?id=%s"; +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/constant/TrackConstant.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/constant/TrackConstant.java new file mode 100644 index 0000000..75f3957 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/constant/TrackConstant.java @@ -0,0 +1,13 @@ +package org.springblade.thirdparty.track.constant; + +/** + * 轨迹常量 + * @author bfhuange + * @since 2024/12/16 + */ +public class TrackConstant { + /** + * 响应成功状态码 + */ + public static final Integer SUCCESS_CODE = 200; +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/exception/TrackException.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/exception/TrackException.java new file mode 100644 index 0000000..17d1655 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/exception/TrackException.java @@ -0,0 +1,16 @@ +package org.springblade.thirdparty.track.exception; + +/** + * 轨迹异常 + * @author bfhuange + * @since 2024/12/16 + */ +public class TrackException extends RuntimeException { + public TrackException(String message) { + super(message); + } + + public TrackException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/exception/TrackExceptionTranslator.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/exception/TrackExceptionTranslator.java new file mode 100644 index 0000000..958246a --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/exception/TrackExceptionTranslator.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.track.exception; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.api.ResultCode; +import org.springblade.core.tool.utils.Func; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author bfhuange + * @since 2025/3/29 + */ +@AllArgsConstructor +@Slf4j +@RestControllerAdvice +public class TrackExceptionTranslator { + + @ExceptionHandler(TrackException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public R handleError(TrackException e) { + log.error("track异常", e); + return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage())); + } +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/feign/ITrackClient.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/feign/ITrackClient.java new file mode 100644 index 0000000..6c74e95 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/feign/ITrackClient.java @@ -0,0 +1,25 @@ +package org.springblade.thirdparty.track.feign; + +import org.springblade.thirdparty.track.config.TrackFeignClientConfig; +import org.springblade.thirdparty.track.pojo.dto.TrackWaybillSendDTO; +import org.springblade.thirdparty.track.pojo.vo.TrackResultVO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * 轨迹接口 + * @author bfhuange + * @date 2024/12/16 + */ +@FeignClient(name = "TRACK", url = "${thirdParty.track.baseUrl}", configuration = TrackFeignClientConfig.class) +public interface ITrackClient { + + /** + * 发送运单 + * @param param + * @return + */ + @PostMapping("/baseapi/nnl_waybill/send") + TrackResultVO sendWaybills(@RequestBody TrackWaybillSendDTO param); +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/pojo/dto/TrackWaybillSendDTO.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/pojo/dto/TrackWaybillSendDTO.java new file mode 100644 index 0000000..4ce9f49 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/pojo/dto/TrackWaybillSendDTO.java @@ -0,0 +1,32 @@ +package org.springblade.thirdparty.track.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 运单发送参数 + * @author bfhuange + * @since 2024/12/16 + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +public class TrackWaybillSendDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 历史运单id列表 + */ + private List ids; + /** + * 系统标识 + */ + @JsonProperty("ibps_type") + private Integer bpsType; +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/pojo/vo/TrackResultVO.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/pojo/vo/TrackResultVO.java new file mode 100644 index 0000000..2ad26ed --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/pojo/vo/TrackResultVO.java @@ -0,0 +1,26 @@ +package org.springblade.thirdparty.track.pojo.vo; + +import lombok.Data; +import org.springblade.core.tool.support.NoErrorFallback; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 轨迹响应结果 + * @author bfhuange + * @date 2024/12/16 + */ +@Data +public class TrackResultVO implements Serializable, NoErrorFallback { + @Serial + private static final long serialVersionUID = 1L; + /** + * 响应代码。200为正确 + */ + private Integer code; + /** + * 对应的提示信息 + */ + private String msg; +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/service/ITrackService.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/service/ITrackService.java new file mode 100644 index 0000000..9d7bed1 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/service/ITrackService.java @@ -0,0 +1,30 @@ +package org.springblade.thirdparty.track.service; + +import java.util.List; + +/** + * 轨迹 接口 + * @author bfhuange + * @since 2024/12/16 + */ +public interface ITrackService { + + /** + * 发送运单 + * @param ids + */ + void sendWaybills(List ids); + + /** + * 获取轨迹url + * @param id 轨迹id + * @return + */ + String getTrackUrl(String id); + + /** + * 获取轨迹url前缀,后面拼接id就是正常url + * @return + */ + String getTrackUrlPrefix(); +} diff --git a/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/service/impl/TrackServiceImpl.java b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/service/impl/TrackServiceImpl.java new file mode 100644 index 0000000..49be639 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/java/org/springblade/thirdparty/track/service/impl/TrackServiceImpl.java @@ -0,0 +1,84 @@ +package org.springblade.thirdparty.track.service.impl; + +import cn.hutool.json.JSONUtil; +import com.google.common.collect.Lists; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.StringPool; +import org.springblade.thirdparty.track.config.TrackProperties; +import org.springblade.thirdparty.track.constant.TrackConstant; +import org.springblade.thirdparty.track.exception.TrackException; +import org.springblade.thirdparty.track.feign.ITrackClient; +import org.springblade.thirdparty.track.pojo.dto.TrackWaybillSendDTO; +import org.springblade.thirdparty.track.pojo.vo.TrackResultVO; +import org.springblade.thirdparty.track.service.ITrackService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * @author bfhuange + * @since 2024/12/16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class TrackServiceImpl implements ITrackService { + + private final TrackProperties trackProperties; + private final ITrackClient client; + + @Override + public void sendWaybills(List ids) { + if (!trackProperties.isEnabled()) { + log.warn("未启用轨迹系统"); + return; + } + log.info("推送运单到轨迹,参数:{}", JSONUtil.toJsonStr(ids)); + if (CollectionUtil.isEmpty(ids)) { + log.warn("推送运单到轨迹 参数为空"); + return; + } + List list = ids.stream() + .filter(Objects::nonNull) + .distinct() + .map(String::valueOf) + .toList(); + if (CollectionUtil.isEmpty(ids)) { + log.warn("推送运单到轨迹 参数为空"); + return; + } + TrackResultVO result = null; + try { + for (List subList : Lists.partition(list, 100)) { + result = client.sendWaybills(new TrackWaybillSendDTO(subList, trackProperties.getBpsType())); + if (result == null || !TrackConstant.SUCCESS_CODE.equals(result.getCode())) { + // 结果为空,或不是成功,抛出异常 + log.error("推送运单到轨迹异常 响应参数:{}", JSONUtil.toJsonStr(result)); + String message = Optional.ofNullable(result) + .map(TrackResultVO::getMsg) + .orElse(StringPool.EMPTY); + throw new TrackException("推送运单到轨迹异常 " + message); + } + } + } catch (TrackException e) { + throw e; + } catch (Exception e) { + log.error("推送运单到轨迹 异常", e); + throw new TrackException("推送运单到轨迹异常"); + } + } + + @Override + public String getTrackUrl(String id) { + return trackProperties.getBaseUrl() + trackProperties.getTrackUrlFormat().formatted(id); + } + + @Override + public String getTrackUrlPrefix() { + return trackProperties.getBaseUrl() + trackProperties.getTrackUrlFormat().replace("%s", ""); + } +} diff --git a/blade-third-party-api/blade-track-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/blade-third-party-api/blade-track-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..13ef128 --- /dev/null +++ b/blade-third-party-api/blade-track-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springblade.thirdparty.track.config.ThirdPartyTrackAutoConfiguration \ No newline at end of file diff --git a/blade-third-party-api/blade-wps-api/pom.xml b/blade-third-party-api/blade-wps-api/pom.xml new file mode 100644 index 0000000..c66fb0b --- /dev/null +++ b/blade-third-party-api/blade-wps-api/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + org.springblade + blade-third-party-api + ${revision} + + + blade-wps-api + ${project.artifactId} + jar + + + + org.springblade + blade-core-tool + + + \ No newline at end of file diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/ThirdPartyWpsAutoConfiguration.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/ThirdPartyWpsAutoConfiguration.java new file mode 100644 index 0000000..364edf3 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/ThirdPartyWpsAutoConfiguration.java @@ -0,0 +1,17 @@ +package org.springblade.thirdparty.wps.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * 第三方api wps配置类 + * @author bfhuange + * @date 2024/10/31 + */ +@ComponentScan("org.springblade.thirdparty.wps") +@EnableConfigurationProperties(WpsProperties.class) +@Configuration +public class ThirdPartyWpsAutoConfiguration { + +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/WpsFeignClientConfig.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/WpsFeignClientConfig.java new file mode 100644 index 0000000..99fa4a0 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/WpsFeignClientConfig.java @@ -0,0 +1,15 @@ +package org.springblade.thirdparty.wps.config; + +import org.springblade.thirdparty.wps.interceptor.WpsRequestInterceptor; +import org.springframework.context.annotation.Bean; + +/** + * @author bfhuange + * @since 2024/12/18 + */ +public class WpsFeignClientConfig { + @Bean + public WpsRequestInterceptor requestInterceptor(WpsProperties wpsProperties) { + return new WpsRequestInterceptor(wpsProperties); + } +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/WpsProperties.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/WpsProperties.java new file mode 100644 index 0000000..fd2db56 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/config/WpsProperties.java @@ -0,0 +1,26 @@ +package org.springblade.thirdparty.wps.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * wps 配置类 + * @author bfhuange + * @date 2024/10/31 + */ +@Data +@ConfigurationProperties(prefix = "third-party.wps") +public class WpsProperties { + /** + * wps基础路径 + */ + private String baseUrl; + /** + * ak + */ + private String accessKey; + /** + * sk + */ + private String secretKey; +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/constant/WpsConstant.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/constant/WpsConstant.java new file mode 100644 index 0000000..78d58bb --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/constant/WpsConstant.java @@ -0,0 +1,33 @@ +package org.springblade.thirdparty.wps.constant; + +/** + * wps 常量类 + * @author bfhuange + * @date 2024/10/30 + */ +public class WpsConstant { + /** + * 签名方法 + */ + public static final String SIGN_METHOD = "WPS-4"; + /** + * 签名请求头 + */ + public static final String AUTHORIZATION_HEADER = "Wps-Docs-Authorization"; + /** + * 日期请求头 + */ + public static final String DATE_HEADER = "Wps-Docs-Date"; + /** + * 成功状态码 + */ + public static final Integer SUCCESS_CODE = 200; + /** + * 透传token + */ + public static final int TOKEN_TYPE_YES = 1; + /** + * 简单模式参数 + */ + public static final String MODE_PARAM = "&simple"; +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/exception/WpsException.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/exception/WpsException.java new file mode 100644 index 0000000..0a7d581 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/exception/WpsException.java @@ -0,0 +1,17 @@ +package org.springblade.thirdparty.wps.exception; + +/** + * wps 异常 + * @author bfhuange + * @date 2024/10/30 + */ +public class WpsException extends RuntimeException { + + public WpsException(String message) { + super(message); + } + + public WpsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/exception/WpsExceptionTranslator.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/exception/WpsExceptionTranslator.java new file mode 100644 index 0000000..cb9ecd6 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/exception/WpsExceptionTranslator.java @@ -0,0 +1,28 @@ +package org.springblade.thirdparty.wps.exception; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.api.ResultCode; +import org.springblade.core.tool.utils.Func; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author bfhuange + * @since 2025/3/29 + */ +@AllArgsConstructor +@Slf4j +@RestControllerAdvice +public class WpsExceptionTranslator { + + @ExceptionHandler(WpsException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public R handleError(WpsException e) { + log.error("wps异常", e); + return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage())); + } +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/feign/IWpsClient.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/feign/IWpsClient.java new file mode 100644 index 0000000..a0742c0 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/feign/IWpsClient.java @@ -0,0 +1,42 @@ +package org.springblade.thirdparty.wps.feign; + +import org.springblade.thirdparty.wps.config.WpsFeignClientConfig; +import org.springblade.thirdparty.wps.pojo.dto.WpsFilePreviewDTO; +import org.springblade.thirdparty.wps.pojo.vo.WpsFilePreviewVO; +import org.springblade.thirdparty.wps.pojo.vo.WpsResultVO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.openfeign.SpringQueryMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * wps接口 + * @author bfhuange + * @date 2024/10/30 + */ +@FeignClient(name = "WPS", url = "${thirdParty.wps.baseUrl}", configuration = WpsFeignClientConfig.class) +public interface IWpsClient { + + /** + * 获取文件预览链接 + * @param fileId + * @return + */ + @GetMapping(value = "/open/api/preview/v1/files/{fileId}/link", headers = "Content-Type=application/json") + WpsResultVO getFilePreviewUrl(@PathVariable("fileId") String fileId, + @RequestParam("preview_mode") String previewMode, + @RequestParam("_w_tokentype") int tokenType, + @SpringQueryMap WpsFilePreviewDTO param); + + /** + * 获取文件编辑链接 + * @param fileId + * @return + */ + @GetMapping(value = "/open/api/edit/v1/files/{fileId}/link", headers = "Content-Type=application/json") + WpsResultVO getFileEditUrl(@PathVariable("fileId") String fileId, + @RequestParam("preview_mode") String previewMode, + @RequestParam("_w_tokentype") int tokenType, + @SpringQueryMap WpsFilePreviewDTO param); +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/interceptor/WpsRequestInterceptor.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/interceptor/WpsRequestInterceptor.java new file mode 100644 index 0000000..bbcec37 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/interceptor/WpsRequestInterceptor.java @@ -0,0 +1,116 @@ +package org.springblade.thirdparty.wps.interceptor; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.DigestUtil; +import org.springblade.thirdparty.wps.config.WpsProperties; +import org.springblade.thirdparty.wps.constant.WpsConstant; +import org.springblade.thirdparty.wps.exception.WpsException; +import org.springframework.util.StringUtils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +/** + * wps请求拦截器 + * @author bfhuange + * @date 2024/10/30 + */ +@Slf4j +@AllArgsConstructor +public class WpsRequestInterceptor implements RequestInterceptor { + + private final WpsProperties wpsProperties; + + @Override + public void apply(RequestTemplate template) { + //获取uri路径 + String path = template.path(); + if(StringUtils.hasText(template.queryLine())) { + path = path + template.queryLine(); + } + + //获取Content-type,“application/json" + Collection contentTypes = template.headers().get("Content-Type"); + String contentType = CollectionUtil.isEmpty(contentTypes) ? "" : contentTypes.iterator().next(); + + //日期格式化 + DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + String date = dateFormat.format(new Date()); + + //open不参与签名,做替换处理 + if (path.startsWith("/open")) { + path = path.replace("/open", ""); + } + + String sha256body; + + //body为空则为空,否则返回sha256(body) + byte[] body = template.body(); + if (body != null && body.length > 0) { + sha256body = this.getSHA256StrJava(body); + } else { + sha256body = ""; + } + + //hmac-sha256(secret_key, Ver + HttpMethod + URI + Content-Type + Wps-Date + sha256(HttpBody)) + String signature = null; + try { + signature = DigestUtil.hmacSha256Hex(WpsConstant.SIGN_METHOD + template.method().toUpperCase(Locale.ROOT) + path + contentType + date + sha256body, wpsProperties.getSecretKey()); + } catch (Exception e) { + log.error("签名异常", e); + throw new WpsException("请求签名异常"); + } + + template.header(WpsConstant.DATE_HEADER, date); + template.header(WpsConstant.AUTHORIZATION_HEADER, String.format(WpsConstant.SIGN_METHOD + " %s:%s", wpsProperties.getAccessKey(), signature)); + } + + /** + * 利用java原生的摘要实现SHA256加密 + * @param str 加密后的报文 + * @return + */ + public String getSHA256StrJava(byte[] str){ + MessageDigest messageDigest; + String encodeStr = ""; + try { + messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(str); + encodeStr = byte2Hex(messageDigest.digest()); + } catch (NoSuchAlgorithmException e) { + log.error("加密异常", e); + throw new WpsException("加密异常"); + } + return encodeStr; + } + + /** + * 将byte转为16进制 + * @param bytes + * @return + */ + private static String byte2Hex(byte[] bytes){ + StringBuilder builder = new StringBuilder(); + String temp = null; + for (byte aByte : bytes) { + temp = Integer.toHexString(aByte & 0xFF); + if (temp.length() == 1) { + //1得到一位的进行补0操作 + builder.append("0"); + } + builder.append(temp); + } + return builder.toString(); + } +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/dto/WpsFilePreviewDTO.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/dto/WpsFilePreviewDTO.java new file mode 100644 index 0000000..e958894 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/dto/WpsFilePreviewDTO.java @@ -0,0 +1,58 @@ +package org.springblade.thirdparty.wps.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * wps文件预览参数 + * @author bfhuange + * @date 2024/10/30 + */ +@Data +public class WpsFilePreviewDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 文件格式,可选值: + * w:文字文件 + * s:表格文件 + * p:演示文件 + * f:PDF文件 + * x:文件类型(图片、压缩包、md、eml、代码、其他),传空时默认为该类型 ,必填 + */ + private String type; + /** + * 适用版本 >= v6.0.2207.20220729 + * 预览模式,可选值: + * high_definition:高清预览 + * ordinary:普通预览 + * cache:缓存预览 + * official:公文极速预览(>=v6.1.2302.20230220)不传默认official,非必填 + */ + //@JsonProperty("preview_mode") + //private String previewMode; + /** + * 高清预览支持控制修订痕迹、评论是否显示等参数。高清预览参数wpsPreview=1111111从左到右依次代表: + * 第0位:格式修订:0不显示;1:显示 + * 第1位:插入和删除:0不显示;1:显示 + * 第2位:评论 0不显示;1:显示 + * 第3位:以嵌入模式显示修订 + * 第4位:以气泡模式显示修订 + * 第5位:0不显示标记;1:显示标记 + * 第6位:0最终状态;1:原始状态 + * 注:(第3、4位组合:01及11为批注框方式;10是嵌入方式 00是批注框显示修订者),非必填 + */ + private String wpsPreview; + /** + * 适用版本>=v7.0.2309a.20230907 + * 是否透传token,传1时透传 + */ + //@JsonProperty("_w_tokentype") + //private int tokenType = 1; + /* + 合作方自定义参数,要求以 _w_third_ 作为前缀,回调时会一并返回给开发者。 + _w_third_appid 、 _w_third_file_id 字段已被使用,对接方请合理避开。 + */ +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/enums/WpsFile.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/enums/WpsFile.java new file mode 100644 index 0000000..1026a39 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/enums/WpsFile.java @@ -0,0 +1,114 @@ +package org.springblade.thirdparty.wps.pojo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.util.StringUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; + +/** + * wps 文件枚举 + * @author bfhuange + * @date 2024/10/30 + */ +public class WpsFile { + /** + * 预览模式 + */ + @AllArgsConstructor + @Getter + public enum PreviewMode { + /** + * 高清预览 + */ + HIGH_DEFINITION("high_definition", "高清预览"), + /** + * 普通预览 + */ + ORDINARY("ordinary", "普通预览"), + /** + * 缓存预览 + */ + CACHE("cache", "缓存预览"), + /** + * 公文极速预览 + */ + OFFICIAL("official", "公文极速预览"), + ; + /** + * 编码 + */ + private final String code; + /** + * 名称 + */ + private final String name; + } + /** + * 类型枚举 + */ + @AllArgsConstructor + @Getter + public enum Type { + /** + * 文字文件 高清模式 + */ + WORD("w", "文字文件", PreviewMode.HIGH_DEFINITION.getCode(), List.of("doc,dot,wps,wpt,docx,dotx,docm,dotm,rtf,txt,mht,mhtml,htm,html,uot3".split(","))), + /** + * 表格文件 普通模式 + */ + EXCEL("s", "表格文件", PreviewMode.ORDINARY.getCode(), List.of("xls,xlt,et,xlsx,xltx,csv,xlsm,xltm,ett".split(","))), + /** + * 演示文件 普通模式 + */ + PPT("p", "演示文件", PreviewMode.ORDINARY.getCode(), List.of("ppt,pptx,pptm,ppsx,ppsm,pps,potx,potm,dpt,dps,pot".split(","))), + /** + * PDF文件 普通模式 + */ + PDF("f", "PDF文件", PreviewMode.ORDINARY.getCode(), List.of("pdf,ofd".split(","))), + /** + * 文件类型(图片、压缩包、md、eml、代码、其他)普通模式 + */ + OTHER("x", "文件类型(图片、压缩包、md、eml、代码、其他)", PreviewMode.ORDINARY.getCode(), Collections.emptyList()), + ; + + /** + * 编码 + */ + private final String code; + /** + * 名称 + */ + private final String name; + /** + * 预览模式 + */ + private final String previewMode; + /** + * 文件扩展名列表 + */ + private final List exts; + + /** + * 根据文件名获取类型 + * @param fileName + * @return + */ + public static Type getByFileName(String fileName) { + if (!StringUtils.hasText(fileName) || !fileName.contains(".")) { + // 文件名为空,或文件名不包含. 返回null + return OTHER; + } + // 获取文件扩展名 + String ext = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(Locale.ROOT); + return Stream.of(values()) + // 扩展名包含在内的 + .filter(type -> type.exts.contains(ext)) + .findFirst() + .orElse(OTHER); + } + } +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/vo/WpsFilePreviewVO.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/vo/WpsFilePreviewVO.java new file mode 100644 index 0000000..2c880a9 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/vo/WpsFilePreviewVO.java @@ -0,0 +1,21 @@ +package org.springblade.thirdparty.wps.pojo.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * wps 文件预览链接返回结果 + * @author bfhuange + * @date 2024/10/30 + */ +@Data +public class WpsFilePreviewVO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 文件在线预览链接,示例:配置域名+/weboffice/office/{file_type}/{file_id}?_w_appid=11&用户自定义参数 +&wpsPreview=1111111&simple&hidecmb + */ + private String link; +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/vo/WpsResultVO.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/vo/WpsResultVO.java new file mode 100644 index 0000000..3c7eac8 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/pojo/vo/WpsResultVO.java @@ -0,0 +1,46 @@ +package org.springblade.thirdparty.wps.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springblade.core.tool.support.NoErrorFallback; + +import java.io.Serial; +import java.io.Serializable; + +/** + * wps响应结果 + * @author bfhuange + * @date 2024/10/30 + */ +@Data +public class WpsResultVO implements Serializable, NoErrorFallback { + @Serial + private static final long serialVersionUID = 1L; + /** + * 请求标识id + */ + @JsonProperty("request_id") + private String requestId; + /** + * 错误码 + */ + private Integer code; + /** + * 状态信息 + */ + private String msg; + /** + * 请求时间 + */ + @JsonProperty("request_time") + private long requestTime; + /** + * 响应时间 + */ + @JsonProperty("response_time") + private long responseTime; + /** + * 参数 + */ + private T data; +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/service/IWpsService.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/service/IWpsService.java new file mode 100644 index 0000000..c8db6fb --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/service/IWpsService.java @@ -0,0 +1,23 @@ +package org.springblade.thirdparty.wps.service; + +/** + * @author bfhuange + * @date 2024/10/30 + */ +public interface IWpsService { + /** + * 根据文件id获取文件预览链接 + * @param fileId + * @param fileName + * @return + */ + String getFilePreviewUrl(String fileId, String fileName); + + /** + * 根据文件id获取文件编辑链接 + * @param fileId + * @param fileName + * @return + */ + String getWpsFileEditUrl(String fileId, String fileName); +} diff --git a/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/service/impl/WpsServiceImpl.java b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/service/impl/WpsServiceImpl.java new file mode 100644 index 0000000..49f4538 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/java/org/springblade/thirdparty/wps/service/impl/WpsServiceImpl.java @@ -0,0 +1,86 @@ +package org.springblade.thirdparty.wps.service.impl; + +import cn.hutool.json.JSONUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.wps.constant.WpsConstant; +import org.springblade.thirdparty.wps.exception.WpsException; +import org.springblade.thirdparty.wps.feign.IWpsClient; +import org.springblade.thirdparty.wps.pojo.dto.WpsFilePreviewDTO; +import org.springblade.thirdparty.wps.pojo.enums.WpsFile; +import org.springblade.thirdparty.wps.pojo.vo.WpsFilePreviewVO; +import org.springblade.thirdparty.wps.pojo.vo.WpsResultVO; +import org.springblade.thirdparty.wps.service.IWpsService; +import org.springframework.stereotype.Service; + +/** + * @author bfhuange + * @date 2024/10/30 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class WpsServiceImpl implements IWpsService { + + private final IWpsClient client; + + @Override + public String getFilePreviewUrl(String fileId, String fileName) { + if (StringUtil.isBlank(fileId) || StringUtil.isBlank(fileName)) { + log.info("获取wps预览链接 参数 fileId:{} fileName:{}", fileId, fileName); + throw new WpsException("参数不能为空"); + } + WpsFilePreviewDTO previewDTO = new WpsFilePreviewDTO(); + // 根据文件名获取文件格式和预览模式 + WpsFile.Type type = WpsFile.Type.getByFileName(fileName); + previewDTO.setType(type.getCode()); + try { + // get请求,对象参数不支持别名,需要提取出来 + WpsResultVO resultVO = client.getFilePreviewUrl(fileId, type.getPreviewMode(), WpsConstant.TOKEN_TYPE_YES, previewDTO); + if (resultVO != null && resultVO.getData() != null && WpsConstant.SUCCESS_CODE.equals(resultVO.getCode())) { + // 成功,返回预览链接 + String link = resultVO.getData().getLink(); + if (WpsFile.Type.EXCEL.equals(type)) { + // excel,去掉simple参数,以便查看单元格的公式 + return link.replace(WpsConstant.MODE_PARAM, ""); + } + return link; + } + log.error("获取wps预览链接异常 {}", JSONUtil.toJsonStr(resultVO)); + throw new WpsException("获取wps预览链接异常"); + } catch (WpsException e) { + throw e; + } catch (Exception e) { + log.error("获取wps预览链接异常", e); + throw new WpsException("获取wps预览链接异常"); + } + } + + @Override + public String getWpsFileEditUrl(String fileId, String fileName) { + if (StringUtil.isBlank(fileId) || StringUtil.isBlank(fileName)) { + log.info("获取wps编辑链接 参数 fileId:{} fileName:{}", fileId, fileName); + throw new WpsException("参数不能为空"); + } + WpsFilePreviewDTO previewDTO = new WpsFilePreviewDTO(); + // 根据文件名获取文件格式和预览模式 + WpsFile.Type type = WpsFile.Type.getByFileName(fileName); + previewDTO.setType(type.getCode()); + try { + // get请求,对象参数不支持别名,需要提取出来 + WpsResultVO resultVO = client.getFileEditUrl(fileId, type.getPreviewMode(), WpsConstant.TOKEN_TYPE_YES, previewDTO); + if (resultVO != null && resultVO.getData() != null && WpsConstant.SUCCESS_CODE.equals(resultVO.getCode())) { + // 成功,返回预览链接 + return resultVO.getData().getLink(); + } + log.error("获取wps编辑链接异常 {}", JSONUtil.toJsonStr(resultVO)); + throw new WpsException("获取wps编辑链接异常:"+JSONUtil.toJsonStr(resultVO)); + } catch (WpsException e) { + throw e; + } catch (Exception e) { + log.error("获取wps编辑链接异常", e); + throw new WpsException("获取wps编辑链接异常"); + } + } +} diff --git a/blade-third-party-api/blade-wps-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/blade-third-party-api/blade-wps-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..ef53fc2 --- /dev/null +++ b/blade-third-party-api/blade-wps-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springblade.thirdparty.wps.config.ThirdPartyWpsAutoConfiguration \ No newline at end of file diff --git a/blade-third-party-api/pom.xml b/blade-third-party-api/pom.xml new file mode 100644 index 0000000..af739c4 --- /dev/null +++ b/blade-third-party-api/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + org.springblade + BladeX + ${revision} + + + blade-third-party-api + ${project.artifactId} + + blade-oa-api + blade-mk-api + blade-wps-api + blade-ocr-api + blade-track-api + + pom + BladeX 第三方API集合 + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + com.fasterxml.jackson.core + jackson-annotations + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + ${project.name} + + + + + \ No newline at end of file diff --git a/doc/nacos/blade.yaml b/doc/nacos/blade.yaml index 157e47b..ff7d56b 100644 --- a/doc/nacos/blade.yaml +++ b/doc/nacos/blade.yaml @@ -121,9 +121,9 @@ blade: #启用 oauth2 enabled: true #使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2验签,需和前端保持一致 - public-key: ${BLADE_OAUTH2_PUBLIC_KEY} + public-key: 0478d11564f262ccde9d52dd0930ed19ec7ba0fc024ce8f6f11f6d1231af132a78bdbf26238d16dd6c87ec5cbdcdecf052b6d97e2a73a49196aaa7a6d3abdc98c1 #使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2解密,前端无需配置 - private-key: ${BLADE_OAUTH2_PRIVATE_KEY} + private-key: 60f5528ab1970eb7ce1bfa02452c02cbb5b5c7cdbfc17c8e86d4ce43966caf3a #token配置 token: #是否有状态 @@ -133,15 +133,15 @@ blade: #单用户登录范围 single-level: all #token签名 使用blade-auth服务 @org.springblade.test.SignKeyGenerator 获取 - sign-key: ${BLADE_TOKEN_SIGN_KEY} + sign-key: gQ9VgtzUjMkOJdavBVVahVTQt0MNxLQ7 #token加密 使用blade-auth服务 @org.springblade.test.CryptoKeyGenerator 获取 - crypto-key: ${BLADE_TOKEN_CRYPTO_KEY} + crypto-key: dpWDmAPQZnbaGCCe80MHlaW1IAHyshWa #超级密钥配置 key: #启用超级密钥 enabled: false #密钥加密 使用 @org.springblade.test.CryptoKeyGenerator 获取 - crypto-key: ${BLADE_KEY_CRYPTO_KEY} + crypto-key: dpWDmAPQZnbaGCCe80MHlaW1IAHyshWa #接口配置 api: #报文加密配置 @@ -149,9 +149,9 @@ blade: #启用报文加密配置 enabled: false #使用blade-auth服务 @org.springblade.test.CryptoKeyGenerator 获取,需和前端保持一致 - aes-key: ${BLADE_API_CRYPTO_AES_KEY} + aes-key: 6DkKV7zsaNdUwkc3zRLE5Mz4tqzn91OI #使用blade-auth服务 @org.springblade.test.CryptoKeyGenerator 获取,需和前端保持一致 - des-key: ${BLADE_API_CRYPTO_DES_KEY} + des-key: RANSz6Q9CgOntaoW #jackson配置 jackson: #null自动转空值 diff --git a/pom.xml b/pom.xml index 8d8a30c..554de4e 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,11 @@ blade false 0.42.0 + + + 1.18.30 + 1.5.5.Final + @@ -37,6 +42,7 @@ blade-plugin-api blade-service blade-service-api + blade-third-party-api @@ -109,6 +115,40 @@ blade-system-api ${revision} + + + org.springblade + blade-oa-api + ${revision} + + + org.springblade + blade-open-api + ${revision} + + + org.springblade + blade-mk-api + ${revision} + + + org.springblade + blade-wps-api + ${revision} + + + org.springblade + blade-ocr-api + ${revision} + + + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + @@ -252,6 +292,19 @@ -parameters + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + +