feat:添加OA系统对接相关代码

This commit is contained in:
aguanleishen
2026-07-22 17:09:39 +08:00
parent d4ef46bf97
commit 0970af239d
190 changed files with 10571 additions and 15 deletions

View File

@@ -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";
}

View File

@@ -0,0 +1,69 @@
package org.springblade.common.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 数据状态枚举
*
* @author vic
* @date 2024/8/26 6:45PM
* @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);
}
}

View File

@@ -0,0 +1,171 @@
package org.springblade.common.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统字典类型枚举
* 响功能和流程的字典
* @author vic
* @date 2024/9/9 8:56PM
* @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;
}
}

View File

@@ -14,3 +14,6 @@ knife4j:
- blade-admin
- blade-gateway
- blade-log
server:
port: 28080

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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,
;
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -38,6 +38,33 @@
<groupId>org.springblade</groupId>
<artifactId>blade-user-api</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-oa-api</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-mk-api</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-powerjob</artifactId>
</dependency>
<!--安全模块-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-oauth2</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -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<Boolean> pushCompanyAndDept() {
mkPushService.pushOrgAndRecord(null);
return R.data(true);
}
/**
* 推送人员
* @return
*/
@Operation(summary = "推送人员")
@PostMapping("/pushPerson")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<Boolean> pushPerson() {
mkPushService.pushPersonAndRecord(null);
return R.data(true);
}
}

View File

@@ -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<Boolean> syncCompanyAndDeptAndPushMK() {
oaService.syncCompanyAndDeptAndPushMK(false);
return R.data(true);
}
/**
* 全量同步公司和部门并推送MK
* @return
*/
@ApiLog("系统管理-机构管理-全量同步")
@Operation(summary = "全量同步公司和部门并推送MK")
@PostMapping("/syncAllCompanyAndDeptAndPushMK")
@PreAuth("hasPermission('oasync:org:all')")
public R<Boolean> syncAllCompanyAndDeptAndPushMK() {
oaService.syncCompanyAndDeptAndPushMK(true);
return R.data(true);
}
/**
* 增量同步人员并推送MK
* @return
*/
@ApiLog("系统管理-用户管理-增量同步")
@Operation(summary = "增量同步人员并推送MK")
@PostMapping("/syncPersonAndPushMK")
@PreAuth("hasPermission('oasync:person:inc')")
public R<Boolean> syncPersonAndPushMK() {
oaService.syncPersonAndPushMK(false);
return R.data(true);
}
/**
* 全量同步人员并推送MK
* @return
*/
@ApiLog("系统管理-用户管理-全量同步")
@Operation(summary = "全量同步人员并推送MK")
@PostMapping("/syncAllPersonAndPushMK")
@PreAuth("hasPermission('oasync:person:all')")
public R<Boolean> syncAllPersonAndPushMK() {
oaService.syncPersonAndPushMK(true);
return R.data(true);
}
}

View File

@@ -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<DeptListVO> entities2vos(List<Dept> 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<MKPushOrgDeptDTO> entities2dtos(List<Dept> 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<String, Long> 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;
}
}

View File

@@ -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<UserListVO> entities2vos(List<User> list);
default List<MKPushPersonDTO> entities2dtos(List<User> 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<String, Long> 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<MKAuxiliaryDeptDTO> fdAuxiliaryDept(String deptCodes) {
if (StringUtils.isBlank(deptCodes)) {
return null;
}
List<String> 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();
}
}

View File

@@ -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<OmsLogger> OMS_LOGGER_CONTEXT = new ThreadLocal<>();
/**
* 组合 logger
*/
private static final ThreadLocal<LinkedList<ComposeLogger>> 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<ComposeLogger> 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<ComposeLogger> list = CONTEXT.get();
if (list == null || list.isEmpty()) {
return DEFAULT;
}
return list.getLast();
}
/**
* 清除最后一个日志记录器
*/
public static void removeLastLog() {
LinkedList<ComposeLogger> list = CONTEXT.get();
if (list == null || list.isEmpty()) {
return;
}
list.removeLast();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<DataSyncRecord> {
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.springblade.system.mapper.DataSyncRecordMapper">
<!-- 通用查询映射结果 -->
<resultMap id="dataSyncRecordResultMap" type="org.springblade.system.pojo.entity.DataSyncRecord">
<result column="id" property="id"/>
<result column="system_code" property="systemCode"/>
<result column="data_type" property="dataType"/>
<result column="mode" property="mode"/>
<result column="result" property="result"/>
<result column="sync_time" property="syncTime"/>
<result column="tenant_id" property="tenantId"/>
<result column="create_time" property="createTime"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
</mapper>

View File

@@ -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<UserDept> {
/**
* 根据用户id列表查询对应的部门id列表拼接的字符串
* @param userIds
* @return
*/
List<UserDeptIdsVO> queryUserDeptIds(@Param("userIds") Collection<Long> userIds);
}

View File

@@ -2,4 +2,19 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.springblade.system.mapper.UserDeptMapper">
<select id="queryUserDeptIds" resultType="org.springblade.system.pojo.vo.UserDeptIdsVO">
select
group_concat(distinct bd.id) deptIds,
group_concat(distinct bd.dept_code) deptCodes,
bud.user_id
from
blade_user_dept bud
left join blade_dept bd on bd.dept_code = bud.dept_code
where
bd.id is not null
and bud.is_deleted = 0
and bud.user_id in <foreach collection="userIds" item="item" separator="," open="(" close=")">#{item}</foreach>
group by bud.user_id
</select>
</mapper>

View File

@@ -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<DataSyncRecord> {
/**
* 使用新事物提交
* @param entity
*/
void saveWithNewTransaction(DataSyncRecord entity);
/**
* 查询最后一次同步成功的记录
* @param param
* @return
*/
DataSyncRecord getLastSuccessRecord(DataSyncRecord param);
}

View File

@@ -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<Dept> {
*/
List<UserVO> deptLeaderInfo(Long deptId);
/**
* 更新部门祖级列表
* @param startTime 要更新的部门的同步时间或更新时间起始
*/
void updateAncestors(Date startTime);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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<UserDept> {
/**
* 根据用户id列表查询对应的部门id列表拼接的字符串
* @param userIds
* @return
*/
List<UserDeptIdsVO> queryUserDeptIds(Collection<Long> userIds);
}

View File

@@ -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<User> {
* @return 用户分页UserVO 包含角色、部门、岗位名称)
*/
IPage<UserVO> userPage(IPage<User> page, String account, String name);
/**
* 解密手机号
* @param user
*/
void decryptPhone(IPhone user);
/**
* 加密手机号
* @param user
*/
void encryptPhone(IPhone user);
}

View File

@@ -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<DataSyncRecordMapper, DataSyncRecord> 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.<DataSyncRecord>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")
);
}
}

View File

@@ -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<DeptMapper, Dept> implements IDeptService {
private static final String TENANT_ID = "tenantId";
@@ -265,4 +265,121 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateAncestors(Date startTime) {
// 1. 查询需要更新祖级列表的部门
List<Dept> list = this.list(Wrappers.<Dept>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<DeptVO> allList = this.list().stream()
.map(dept -> BeanUtil.copyProperties(dept, DeptVO.class))
.toList();
// id对应的部门map
Map<Long, DeptVO> idDeptMap = allList.stream()
.collect(Collectors.toMap(DeptVO::getId, Function.identity(), (a, b) -> b));
// 获取根节点列表
List<DeptVO> rootList = getRootList(allList, idDeptMap);
// 计算祖级列表
setAncestors(rootList);
// 3. 更新祖级列表
List<Dept> 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<DeptVO> getRootList(List<DeptVO> allList, Map<Long, DeptVO> idDeptMap) {
if (CollectionUtil.isEmpty(allList)) {
return Collections.emptyList();
}
// 根节点列表
List<DeptVO> 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<DeptVO> rootList) {
if (CollectionUtil.isEmpty(rootList)) {
return;
}
// 根节点的祖级列表是 0, 最后拼接一个逗号,防止查询的时候有包含的
String ancestors = OAConvertConstant.ROOT_PARENT_ID + ",";
setAncestors(rootList, ancestors);
}
/**
* 设置祖级列表
* @param list 节点列表
* @param ancestors 祖级列表
*/
private void setAncestors(List<DeptVO> 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);
}
}
}
}

View File

@@ -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<IPage<User>> queryUser = this.getQueryUser(startTime, endTime);
// 分页推送函数
BiConsumer<IPage<User>, String> pushConsumer = this::pushUser;
// 公共推送
pushCommon(queryUser, pushConsumer);
});
} finally {
ComposeLogUtil.removeLastLog();
}
}
/**
* 推送并记录
* @param oaStartTime
* @param recordSupplier
* @param pushHandler
*/
private void pushAndRecord(Date oaStartTime, Supplier<DataSyncRecord> recordSupplier, BiConsumer<Date, Date> 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<IPage<Dept>> queryDept = getQueryDept(deptCategory, startTime, endTime);
// 推送函数
BiConsumer<IPage<Dept>, String> pushDept = getPushDept(deptCategory);
// 公共推送
pushCommon(queryDept, pushDept);
}
/**
* 获取查询部门的方法
*
* @param deptCategory
* @param startTime
* @param endTime
* @return
*/
private UnaryOperator<IPage<Dept>> getQueryDept(DeptCategory deptCategory, Date startTime, Date endTime) {
return page -> deptService.page(page, Wrappers.<Dept>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<IPage<Dept>, String> getPushDept(DeptCategory deptCategory) {
return (page, batchId) -> {
List<Dept> records = page.getRecords();
if (CollectionUtil.isEmpty(records)) {
return;
}
// 当前页是否最后一页
boolean lastPage = page.getCurrent() == page.getPages();
MKPushDTO<MKPushOrgDeptDTO> param = new MKPushDTO<>();
param.setBatchId(batchId);
param.setFinish(lastPage);
List<MKPushOrgDeptDTO> 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<IPage<User>> getQueryUser(Date startTime, Date endTime) {
return page -> userService.page(page, Wrappers.<User>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<User> page, String batchId) {
List<User> records = page.getRecords();
if (CollectionUtil.isEmpty(records)) {
return;
}
// 当前页是否最后一页
boolean lastPage = page.getCurrent() == page.getPages();
MKPushDTO<MKPushPersonDTO> param = new MKPushDTO<>();
param.setBatchId(batchId);
param.setFinish(lastPage);
// 默认密码及参数转换
String defaultPassword = ParamCache.getValue(DEFAULT_PARAM_PASSWORD);
// 解密手机号
records.forEach(userService::decryptPhone);
List<MKPushPersonDTO> 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<Long> 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 <T>
*/
private <T> void pushCommon(UnaryOperator<IPage<T>> queryOperator, BiConsumer<IPage<T>, String> pushConsumer) {
IPage<T> 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());
}
}

View File

@@ -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<DataSyncRecord> recordSupplier, Consumer<Date> 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<Dept> 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<Dept> 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<Dept> 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<Dept> 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<OAPersonResponse> oaPersons) {
if (CollectionUtil.isEmpty(oaPersons)) {
// 数据为空,直接返回
return;
}
// 根公司id
String rootCompanyId = getRootCompanyId();
if (rootCompanyId == null) {
return;
}
// 根公司下要同步的部门id
Set<String> rootCompanyDeptIds = getRootCompanyDeptIds(rootCompanyId);
oaPersons = oaPersons.stream()
// 公司不是根公司,或者部门在根公司下要同步的部门列表中
.filter(oaPerson -> !rootCompanyId.equals(oaPerson.getSubcompanyid1()) || rootCompanyDeptIds.contains(oaPerson.getDepartmentid()))
.toList();
// 根据手机号转成set
TreeSet<OAPersonResponse> oaPersonSet = CollectionUtil.toTreeSet(oaPersons, Comparator.comparing(OAPersonResponse::getMobile));
// 默认密码
String defaultPassword = DigestUtil.encrypt(ParamCache.getValue(DEFAULT_PARAM_PASSWORD));
// 转换数据
List<User> users = oaPersonSet.stream()
// 只需要手机不为空的
.filter(person -> StringUtils.isNotBlank(person.getMobile()))
.map(person -> userConvert.person2user(person, defaultPassword))
.toList();
List<String> phones = users.stream()
.map(User::getPhone)
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
// 查询所有用户
Map<String, Long> userMap = userService.list(Wrappers.<User>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<Long> 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<User> addUsers = users.stream()
.filter(user -> !existsUserIds.contains(user.getId()))
.toList();
if (CollectionUtil.isNotEmpty(addUsers)) {
ComposeLogUtil.getLastLog().info("批量新增用户:{}", addUsers.size());
userService.saveBatch(addUsers);
}
// 存在的修改
List<User> 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<String, Long> userDeptMap = userDeptService.list(Wrappers.<UserDept>lambdaQuery()
.in(UserDept::getUserId, userMap.values())
).stream()
.collect(Collectors.toMap(this::getUserDeptKey, UserDept::getId, (a, b) -> b));
// 数据库存在的所有用户部门id
Set<Long> existsUserDeptIds = new HashSet<>(userDeptMap.values());
List<UserDept> 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<UserDept> addList = userDeptList.stream()
.filter(userDept -> !existsUserDeptIds.contains(userDept.getId()))
.toList();
if (CollectionUtil.isNotEmpty(addList)) {
ComposeLogUtil.getLastLog().info("批量新增用户部门:{}", addList.size());
userDeptService.saveBatch(addList);
}
// 存在的修改
List<UserDept> updateList = userDeptList.stream()
.filter(userDept -> existsUserDeptIds.contains(userDept.getId()))
.toList();
if (CollectionUtil.isNotEmpty(updateList)) {
ComposeLogUtil.getLastLog().info("批量修改用户部门:{}", updateList.size());
userDeptService.updateBatchById(updateList);
}
// 回写部门id到用户表
Collection<Long> userIds = userMap.values();
List<UserDeptIdsVO> list = userDeptService.queryUserDeptIds(userIds);
// 查询没有角色的用户id
Set<Long> noRoleUserIds = userService.list(Wrappers.<User>lambdaQuery()
.in(User::getId, userIds)
.isNull(User::getRoleId)
).stream()
.map(User::getId)
.collect(Collectors.toSet());
// 获取默认角色id
String defaultRoleId = getDefaultRoleId();
List<User> 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<Dept> handleCompany(List<OACompanyResponse> oaCompanies) {
if (CollectionUtil.isEmpty(oaCompanies)) {
// 数据为空,直接返回
return Collections.emptyList();
}
// 获取需要的公司名称
Set<String> companyNames = getCompanyNames();
// 转换数据
List<Dept> allParam = oaCompanies.stream()
// 筛选只要需要的公司
.filter(company -> companyNames.contains(company.getSubcompanyname()))
.map(deptConvert::company2dept)
.toList();
// 查询数据库的部门转换成map
Map<String, Long> deptMap = getAllCompanyDeptMap();
// 处理部门
return handleDept(allParam, deptMap, DeptCategory.COMPANY);
}
/**
* 处理oa部门
* @param oaDepts
* @return 未处理的数据
*/
private List<Dept> handleDept(List<OADepartmentResponse> oaDepts) {
if (CollectionUtil.isEmpty(oaDepts)) {
// 数据为空,直接返回
return Collections.emptyList();
}
// 查询所有公司的编码和id的map
Map<String, Long> companyDeptMap = getAllCompanyDeptMap();
// 根公司id
String rootCompanyId = getRootCompanyId();
if (rootCompanyId == null) {
return Collections.emptyList();
}
// 根公司下要同步的部门名称
Set<String> rootCompanyDeptNames = getRootCompanyDeptNames();
// 转换数据
List<Dept> 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<String> deptCodes = allParam.stream()
.map(Dept::getDeptCode)
.collect(Collectors.toSet());
// 查询数据库的部门转换成map
Map<String, Long> deptMap = deptService.list(Wrappers.<Dept>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<Dept> handleDept(List<Dept> allParam, Map<String, Long> deptMap, DeptCategory deptCategory) {
// 数据库存在的所有部门id
Set<Long> 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<Dept> list = allParam.stream()
.filter(dept -> dept.getParentId() != null)
.toList();
// 不存在的新增
List<Dept> 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<Dept> 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<String> companyNames = this.getCompanyNames();
if (companyNames.isEmpty()) {
return null;
}
return deptService.list(Wrappers.<Dept>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<Role> roleList = roleService.list(Wrappers.<Role>lambdaQuery()
.eq(Role::getRoleAlias, defaultRole)
);
if (CollectionUtil.isEmpty(roleList)) {
return null;
}
return roleList.get(0).getId().toString();
}
/**
* 获取所有公司的编码和id的map
* @return
*/
private Map<String, Long> getAllCompanyDeptMap() {
return deptService.list(Wrappers.<Dept>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<String> getCompanyNames() {
List<Dict> 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<String> getRootCompanyDeptNames() {
List<Dict> 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<String> getRootCompanyDeptIds(String rootCompanyId) {
Set<String> rootCompanyDeptNames = this.getRootCompanyDeptNames();
if (CollectionUtil.isEmpty(rootCompanyDeptNames)) {
return Collections.emptySet();
}
// 查询父部门编码是根公司的,部门类型的,根公司下部门名称的部门
return deptService.list(Wrappers.<Dept>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.<Dept>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();
}
}

View File

@@ -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<UserDeptMapper, UserDept> implements IUserDeptService {
@Override
public List<UserDeptIdsVO> queryUserDeptIds(Collection<Long> userIds) {
if (CollectionUtil.isEmpty(userIds)) {
return Collections.emptyList();
}
return baseMapper.queryUserDeptIds(userIds);
}
}

View File

@@ -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<UserMapper, User> implement
private final IRoleService roleService;
private final BladeTenantProperties tenantProperties;
private final OAuth2Properties properties;
@Override
public User getDetail(User user) {
QueryWrapper<User> queryWrapper = Condition.getQueryWrapper(user);
@@ -751,4 +757,25 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> 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())));
// }
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-third-party-api</artifactId>
<version>${revision}</version>
</parent>
<artifactId>blade-mk-api</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -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
};
}
}

View File

@@ -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;
}

View File

@@ -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 {
}

View File

@@ -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";
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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()));
}
}

View File

@@ -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<String> 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<Void> 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<Void> 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<List<MKNodeVO>> 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<MKAllHandlerVO> 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<List<MKManualNodeVO>> 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<Object> pushOrgDept(@RequestBody MKPushDTO<MKPushOrgDeptDTO> param, @RequestParam("access_token") String accessToken);
/**
* 推送人员
* @param param
* @param accessToken
* @return
*/
@PostMapping("${thirdParty.mk.pushPersonUrl:/openapi/sys-org/v2/push/person}")
MKResultVO<Object> pushPerson(@RequestBody MKPushDTO<MKPushPersonDTO> param, @RequestParam("access_token") String accessToken);
//===========================认证相关接口========================================
/**
* 获取oauth 认证code
* @param param
*/
@PostMapping("${thirdParty.mk.getOAuthCodeUrl:/openapi/sys-oauth/openOauth/getCode}")
MKResultVO<MKOAuthCodeVO> getOAuthCode(@RequestBody MKOAuthCodeDTO param, @RequestParam("access_token") String accessToken);
/**
* 获取oauth token
* @param param
*/
@PostMapping("${thirdParty.mk.getOAuthTokenUrl:/openapi/sys-oauth/openOauth/getToken}")
MKResultVO<MKOAuthTokenVO> getOAuthToken(@RequestBody MKOAuthTokenDTO param, @RequestParam("access_token") String accessToken);
/**
* 获取oauth 用户信息
* @param param
* @return
*/
@PostMapping("${thirdParty.mk.getOAuthUserInfoUrl:/openapi/sys-oauth/openOauth/getUserInfo}")
MKResultVO<MKOAuthUserVO> getOAuthUserInfo(@RequestBody MKOAuthUserDTO param, @RequestParam("access_token") String accessToken);
//===========================审批记录相关接口========================================
/**
* 流程审批意见信息API - 获取审批意见接口
* @param accessToken
* @return
*/
@PostMapping("${thirdParty.mk.queryAuditNotesUrl:/openapi/sys-lbpm/lbpmAuditNote/openSupport/listNote}")
MKResultVO<List<MKAuditNoteVO>> queryAuditNotes(@RequestBody MKAuditNoteDTO param, @RequestParam("access_token") String accessToken);
/**
* 流程卡片操作API- 获取抄送人列表
* @param accessToken
* @return
*/
@PostMapping("${thirdParty.mk.querySenderListUrl:/openapi/sys-lbpm/sysLbpmProcessCard/openSupport/getSenderList}")
MKResultVO<MKPageVO<MKSenderVO>> 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<MKPageVO<MKApprovalVO>> queryApprovalList(@RequestBody MKApprovalDTO param, @RequestParam("access_token") String accessToken);
/**
* 流程实例操作API-列表
* @param accessToken
* @return
*/
@PostMapping("${thirdParty.mk.queryProcessListUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/list}")
MKResultVO<MKPageVO<MKProcessVO>> queryProcessList(@RequestBody MKProcessDTO param, @RequestParam("access_token") String accessToken);
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
/**
* 授权codecode使⽤⼀次之后失效
*/
private String code;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<MKAdditionOperationParameterDTO> additionParameters;
/**
* 表单实例Model Name
*/
private String formInstanceModel;
/**
* 业务表单字段值集合
*/
// private Map<String, Object> formValues;
private Object formValues;
/**
* 临时变量
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private Object tempVarData;
}

View File

@@ -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<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 如果不传默认一次性推送mk这边生成批次如果一定量数据分页推送则第三方生成批次号批次号唯一
*/
private String batchId;
/**
* 结束标识 true-当前推送完毕false-还没推送完成。一次性推送的话,就传 true
*/
private boolean finish = true;
/**
* 推送的数据主体
*/
private List<T> message;
}

View File

@@ -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:1111dept: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<String> nullValueProps;
//=======================一般不用的字段开始=========================
/**
* 机构简称
*/
private String fdAbbreviation;
/**
* 地址1
*/
private String fdAddress1;
/**
* 地址2
*/
private String fdAddress2;
/**
* 管理员,例:["113","43424",..]
*/
private List<String> fdAdmins;
/**
* 营业执照编号
*/
private String fdBusinessLicenseNo;
/**
* 群组分类id有值情况当type为group时
*/
private String fdCategoryName;
/**
* 扩展属性未定义的字段并且需要同步的可以传到此json对象中同步前需要去后台管理增加对应key值的扩展字段。例{"birthDate":"19000101","education":"本科","isSocialRecruitment":"true"}
*/
private Object fdCustomProps;
/**
* 说明
*/
private String fdDesc;
/**
* 外部组织显示位置:默认情况是内部组织都在行政组织树外部组织都在生态组织树。组织架构开启一体化后外部组织既可以出现在行政树下面也可以出现在生态组织树下。但行政里面的外部组织必须要有顶级机构。是否显示在生态组织目录true/null-显示在生态组织目录false-显示在行政组织
*/
private Boolean fdEosShowType;
/**
* 外部组织分类codeMK有外部组织分类需要提前在管理端建好
*/
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;
}

View File

@@ -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:1111dept: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<MKAuxiliaryDeptDTO> 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<String> nullValueProps;
//=======================一般不用的字段开始=========================
/**
* 群组分类id有值情况当type为group时
*/
private String fdCategoryName;
/**
* 扩展属性未定义的字段并且需要同步的可以传到此json对象中同步前需要去后台管理增加对应key值的扩展字段。例{"birthDate":"19000101","education":"本科","isSocialRecruitment":"true"}
*/
private Object fdCustomProps;
/**
* 默认语言
*/
private String fdDefaultLang;
/**
* 说明
*/
private String fdDesc;
/**
* 邮件地址
*/
private String fdEmail;
/**
* 外部组织显示位置:默认情况是内部组织都在行政组织树外部组织都在生态组织树。组织架构开启一体化后外部组织既可以出现在行政树下面也可以出现在生态组织树下。但行政里面的外部组织必须要有顶级机构。是否显示在生态组织目录true/null-显示在生态组织目录false-显示在行政组织
*/
private Boolean fdEosShowType;
/**
* 外部组织分类codeMK有外部组织分类需要提前在管理端建好
*/
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<String> 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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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<MKConditionDTO> 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;
}

View File

@@ -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<String> columns;
public void setPage(Integer pageNo, Integer pageSize) {
this.pageNo = pageNo;
this.pageSize = pageSize;
this.offset = (this.pageNo -1) * this.pageSize;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,8 @@
package org.springblade.thirdparty.mk.pojo.dto.sort;
/**
* @author bfhuange
* @since 2025/4/2
*/
public interface ISort {
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<MKApprovedHandlerVO> approvedHandlers;
/**
* 审批中的处理人
*/
private List<Object> approvingHandlers;
/**
* 解析的处理人
*/
private List<Object> parseHandlers;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<MKAttachmentVO> attachmentParameter;
/**
* 流程附言
*/
@JsonProperty("lbpmCommentPostscriptVOS")
private List<MKProcessCommentVO> processComments;
/**
* 机制数据
*/
private MKMechanismsVO mechanisms;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<MKMetaAttachmentVO> attachment;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<MKNodeHandlerVO> nodeHandlers;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分页位移量
*/
private Integer offset;
/**
* 每页数目
*/
private Integer pageSize;
/**
* 总数
*/
private Integer totalSize;
/**
* 返回内容
*/
private List<T> content;
public MKPageVO(Integer totalSize) {
this.totalSize = totalSize;
this.content = Collections.emptyList();
}
/**
* 空的结果
* @return
* @param <T>
*/
public static <T> MKPageVO<T> empty() {
return new MKPageVO<>(0);
}
}

View File

@@ -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<MKAttachmentVO> attachments;
/**
* 机制数据
*/
private MKMechanismsVO mechanisms;
}

View File

@@ -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;
}

View File

@@ -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<T> 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<MKErrorVO> errStacks;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<MKNodeVO> 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<MKPushOrgDeptDTO> param);
/**
* 推送人员
* @param param
* @return
*/
boolean pushPerson(MKPushDTO<MKPushPersonDTO> param);
/**
* 获取mk审批链接
* @param processInstanceId
* @param loginName
* @return
*/
String getMKApprovalUrl(String processInstanceId, String loginName);
/**
* 查询流程审批记录
* @param param
* @return
*/
List<MKAuditNoteVO> queryAuditNotes(MKAuditNoteDTO param);
/**
* 查询抄送人列表
* @param param
* @return
*/
List<MKSenderVO> 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<MKApprovalVO> queryApprovalList(MKApprovalDTO param);
/**
* 查询流程列表,我参与的 待办
* @param param
* @return
*/
MKPageVO<MKProcessVO> queryProcessList(MKProcessDTO param);
}

Some files were not shown because too many files have changed in this diff Show More