1、完善项目

2、完善合同
3、完善费用项
4、司机管理对接OCR
5、完善运输计划
6、完善临时额度
This commit is contained in:
2026-08-04 02:49:59 +08:00
387 changed files with 22482 additions and 110 deletions

View File

@@ -0,0 +1,22 @@
<?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-oa-api</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,23 @@
package org.springblade.thirdparty.oa.config;
import feign.RequestInterceptor;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
/**
* @author bfhuange
* @since 2024/12/18
*/
public class OAFeignClientConfig {
@Resource
OAProperties oaProperties;
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
// 空实现屏蔽 全局拦截器 BladeFeignRequestInterceptor
String authorization=oaProperties.getAuthorization();
template.header("Authorization",authorization);
};
}
}

View File

@@ -0,0 +1,23 @@
package org.springblade.thirdparty.oa.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* oa配置类
* @author bfhuange
* @date 2024/8/22
*/
@Data
@ConfigurationProperties(prefix = "third-party.oa")
public class OAProperties {
/**
* oa基础url
*/
private String baseUrl;
/**
* authorization
*/
private String authorization;
}

View File

@@ -0,0 +1,14 @@
package org.springblade.thirdparty.oa.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 第三方api oa配置类
* @author bfhuange
* @date 2024/8/27
*/
@EnableConfigurationProperties(OAProperties.class)
@Configuration
public class ThirdPartyOAAutoConfiguration {
}

View File

@@ -0,0 +1,17 @@
package org.springblade.thirdparty.oa.constant;
/**
* oa常量类
* @author bfhuange
* @date 2024/8/22
*/
public class OAConstant {
/**
* oa成功响应码
*/
public static final String OA_SUCCESS_CODE = "1";
/**
* 根公司或根部门id
*/
public static final String ROOT_COMPANY_ID = "0";
}

View File

@@ -0,0 +1,33 @@
package org.springblade.thirdparty.oa.constant;
/**
* OA转换常量类
* @author bfhuange
* @date 2024/8/27
*/
public class OAConvertConstant {
/**
* oa公司转部门编码前缀
*/
public static final String COMPANY_OA_PREFIX = "OAC";
/**
* oa部门转部门编码前缀
*/
public static final String DEPARTMENT_OA_PREFIX = "OAD";
/**
* 外部公司部门编码前缀
*/
public static final String COMPANY_EX_PREFIX = "EXC";
/**
* 外部部门编码前缀
*/
public static final String DEPARTMENT_EX_PREFIX = "EXD";
/**
* oa人员转用户编码前缀
*/
public static final String PERSON_OA_PREFIX = "OAP";
/**
* 根公司父id
*/
public static final Long ROOT_PARENT_ID = 0L;
}

View File

@@ -0,0 +1,47 @@
package org.springblade.thirdparty.oa.feign;
import org.springblade.thirdparty.oa.config.OAFeignClientConfig;
import org.springblade.thirdparty.oa.pojo.response.OACompanyResponse;
import org.springblade.thirdparty.oa.pojo.response.OADepartmentResponse;
import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse;
import org.springblade.thirdparty.oa.pojo.response.OAResponse;
import org.springblade.thirdparty.oa.pojo.search.OACompanySearch;
import org.springblade.thirdparty.oa.pojo.search.OADepartmentSearch;
import org.springblade.thirdparty.oa.pojo.search.OAPersonSearch;
import org.springblade.thirdparty.oa.pojo.search.OASearch;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* oa接口
* @author bfhuange
* @date 2024/8/22
*/
@FeignClient(name = "OA", url = "${thirdParty.oa.baseUrl}", configuration = OAFeignClientConfig.class)
public interface IOAClient {
/**
* 查询公司分页
* @param param
* @return
*/
@PostMapping("${thirdParty.oa.queryCompanyPageUrl:/api/hrm/resful/getHrmsubcompanyWithPage}")
OAResponse<OACompanyResponse> queryCompanyPage(@RequestBody OASearch<OACompanySearch> param);
/**
* 查询部门分页
* @param param
* @return
*/
@PostMapping("${thirdParty.oa.queryDepartmentPage:/api/hrm/resful/getHrmdepartmentWithPage}")
OAResponse<OADepartmentResponse> queryDepartmentPage(@RequestBody OASearch<OADepartmentSearch> param);
/**
* 查询人员分页,用户和公司、部门的关系,存在同一个用户,多条不同公司、部门数据
* @param param
* @return
*/
@PostMapping("${thirdParty.oa.queryPersonPageUrl:/api/hrm/resful/getHrmUserInfoWithPage}")
OAResponse<OAPersonResponse> queryPersonPage(@RequestBody OASearch<OAPersonSearch> param);
}

View File

@@ -0,0 +1,28 @@
package org.springblade.thirdparty.oa.pojo.response;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @Author alexbzhang
* @Date 25/11/2022 14 34
* @Version 1.0
*/
@Data
public class OACompanyResponse implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String subcompanydesc;
private String canceled;
private String subcompanycode;
private String supsubcomid;
private String created;
private String modified;
private String id;
private String subcompanyname;
private String url;
private String showorder;
}

View File

@@ -0,0 +1,29 @@
package org.springblade.thirdparty.oa.pojo.response;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @Author alexbzhang
* @Date 25/11/2022 14 40
* @Version 1.0
*/
@Data
public class OADepartmentResponse implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String canceled;
private String supdepid;
private String departmentmark;
private String departmentname;
private String created;
private String departmentcode;
private String modified;
private String id;
private String subcompanyid1;
private String showorder;
}

View File

@@ -0,0 +1,76 @@
package org.springblade.thirdparty.oa.pojo.response;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @Author alexbzhang
* @Date 25/11/2022 14:20
* @Version 1.0
*/
@Data
public class OAPersonResponse implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String companystartdate;
private String tempresidentnumber;
private String createdate;
private String language;
private String workstartdate;
private String subcompanyid1;
private String subcompanyname;
private String joblevel;
private String startdate;
private String password;
private String subcompanycode;
private String jobactivitydesc;
private String bememberdate;
private String modified;
private String id;
private String mobilecall;
private String nativeplace;
private String certificatenum;
private String height;
private String loginid;
private String created;
private String degree;
private String bepartydate;
private String weight;
private String telephone;
private String residentplace;
private String lastname;
private String healthinfo;
private String enddate;
private String maritalstatus;
private String departmentname;
private String folk;
/**
* 状态 0试用1正式2临时3试用延期4解聘5离职6退休7无效
*/
private String status;
private String birthday;
private String accounttype;
private String jobcall;
private String managerid;
private String assistantid;
private String departmentcode;
private String belongto;
private String email;
private String seclevel;
private String policy;
private String jobtitle;
private String workcode;
private String sex;
private String departmentid;
private String homeaddress;
private String mobile;
private String lastmoddate;
private String educationlevel;
private String islabouunion;
private String locationid;
private String regresidentplace;
private String dsporder;
}

View File

@@ -0,0 +1,21 @@
package org.springblade.thirdparty.oa.pojo.response;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @Author alexbzhang
* @Date 25/11/2022 13:52
* @Version 1.0
*/
@Data
public class OAResponse<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String code;
private OAResponseData<T> data;
}

View File

@@ -0,0 +1,26 @@
package org.springblade.thirdparty.oa.pojo.response;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @Author alexbzhang
* @Date 25/11/2022 13:58
* @Version 1.0
*/
@Data
public class OAResponseData<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long totalSize;
private List<T> dataList;
private Integer pageSize;
private Integer page;
}

View File

@@ -0,0 +1,26 @@
package org.springblade.thirdparty.oa.pojo.search;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* oa公司分页查询参数
* @author bfhuange
* @date 2024/8/23
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class OACompanySearch extends OAPageSearch {
@Serial
private static final long serialVersionUID = 1L;
private String subcompanyname;
private String subcompanycode;
private String created;
private String modified;
private String canceled;
private String id;
}

View File

@@ -0,0 +1,27 @@
package org.springblade.thirdparty.oa.pojo.search;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* OA部门分页查询参数
* @author bfhuange
* @date 2024/8/23
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class OADepartmentSearch extends OAPageSearch {
@Serial
private static final long serialVersionUID = 1L;
private String departmentname;
private String departmentcode;
private String subcompanyid1;
private String created;
private String modified;
private String canceled;
private String id;
}

View File

@@ -0,0 +1,29 @@
package org.springblade.thirdparty.oa.pojo.search;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* oa分页查询参数
* @author bfhuange
* @date 2024/8/23
*/
@Data
public class OAPageSearch implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 当前页,必填
*/
@JsonProperty("curpage")
protected Integer curPage = 1;
/**
* 每页大小,必填
*/
@JsonProperty("pagesize")
protected Integer pageSize = 10000;
}

View File

@@ -0,0 +1,30 @@
package org.springblade.thirdparty.oa.pojo.search;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* OA人员分页查询参数
* @author bfhuange
* @date 2024/8/23
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class OAPersonSearch extends OAPageSearch {
@Serial
private static final long serialVersionUID = 1L;
private String created;
private String modified;
private String workcode;
private String subcompanyid1;
private String departmentid;
private String jobtitleid;
private String id;
private String loginid;
private String isadaccount;
}

View File

@@ -0,0 +1,23 @@
package org.springblade.thirdparty.oa.pojo.search;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* oa查询参数
* @author bfhuange
* @date 2024/8/22
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class OASearch<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private T params;
}

View File

@@ -0,0 +1,168 @@
package org.springblade.thirdparty.oa.util;
import lombok.extern.slf4j.Slf4j;
import org.springblade.thirdparty.oa.constant.OAConstant;
import org.springblade.thirdparty.oa.pojo.response.OAResponse;
import org.springblade.thirdparty.oa.pojo.response.OAResponseData;
import org.springblade.thirdparty.oa.pojo.search.OAPageSearch;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* oa工具类
* @author bfhuange
* @date 2024/8/23
*/
@Slf4j
public class OAUtils {
private OAUtils() {}
/**
* 将浮点型字符串转换成整数字符串为空则转为1否则截取字符串小数点前面的部门解析
* @param doubleString 浮点类型字符串
* @return
*/
public static int parseInt(String doubleString) {
if (doubleString == null || doubleString.isEmpty()) {
return 1;
}
try {
return Integer.parseInt(doubleString.split("\\.")[0]);
} catch (Exception e) {
log.error("解析数值字符串[{}]异常", doubleString, e);
return 1;
}
}
/**
* 分页同步所有数据接口,返回所有数据无限制
* @param param 接口分页参数
* @param function 接口
* @param exceptionFunction 异常方法
* @return
* @param <T>
* @param <R>
*/
public static <T extends OAPageSearch, R> List<R> pageSyncAll(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction) {
return OAUtils.pageSync(param, function, exceptionFunction, Integer.MAX_VALUE);
}
/**
* 分页同步数据接口
* @param param 接口分页参数
* @param function 接口
* @param exceptionFunction 异常方法
* @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据
* @return 参数是处理数据的方法的方法
* @param <T>
* @param <R>
*/
public static <T extends OAPageSearch, R> Consumer<Consumer<List<R>>> pageSyncHandler(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold) {
return OAUtils.pageSyncHandler(param, function, exceptionFunction, threshold, log::info);
}
/**
* 分页同步数据接口
* @param param 接口分页参数
* @param function 接口
* @param exceptionFunction 异常方法
* @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据
* @return 参数是处理数据的方法的方法
* @param <T>
* @param <R>
*/
public static <T extends OAPageSearch, R> Consumer<Consumer<List<R>>> pageSyncHandler(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold, BiConsumer<String, Object[]> logMethod) {
return consumer -> {
int i = 0;
while (true) {
// 循环调用接口直到阈值或最后一页
List<R> data = OAUtils.pageSync(param, function, exceptionFunction, threshold, logMethod);
if (data.isEmpty()) {
// 数据为空,结束
return;
}
logMethod.accept("批量处理OA同步数据 第{}次,{}条", new Object[]{++i, data.size()});
// 数据不为空,处理数据
consumer.accept(data);
if (data.size() < threshold) {
// 数据比阈值小,说明到最后一页了,结束
return;
}
}
};
}
/**
* 分页同步数据接口
* @param param 接口分页参数
* @param function 接口
* @param exceptionFunction 异常方法
* @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据
* @return
* @param <T>
* @param <R>
*/
public static <T extends OAPageSearch, R> List<R> pageSync(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold) {
return OAUtils.pageSync(param, function, exceptionFunction, threshold, log::info);
}
/**
* 分页同步数据接口
* @param param 接口分页参数
* @param function 接口
* @param exceptionFunction 异常方法
* @param threshold 阈值,当数据总数超过该值时,不再调用接口,返回数据
* @param logMethod 日志输出
* @return
* @param <T>
* @param <R>
*/
public static <T extends OAPageSearch, R> List<R> pageSync(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold, BiConsumer<String, Object[]> logMethod) {
long totalPage = -1;
List<R> result = new ArrayList<>();
do {
// 调用接口
OAResponse<R> oaResponse = function.apply(param);
if (oaResponse== null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
// 状态不是成功,抛出异常
throw exceptionFunction.apply(oaResponse);
}
OAResponseData<R> data = oaResponse.getData();
if (!CollectionUtils.isEmpty(data.getDataList())) {
result.addAll(data.getDataList());
}
// 计算总页数
totalPage = calcTotalPage(data);
logMethod.accept("OA分页同步数据完成 {}/{}页", new Object[]{param.getCurPage(), totalPage});
// 当前页 + 1
param.setCurPage(param.getCurPage() + 1);
// 数据量小于阈值且当前页(下一次)小于等于总页数,循环调用接口
} while (result.size() < threshold && param.getCurPage() <= totalPage);
return result;
}
/**
* 计算总页数
* @param data
* @return
* @param <R>
*/
private static <R> long calcTotalPage(OAResponseData<R> data) {
// 总数
Long totalSize = data.getTotalSize();
// 每页大小
Integer pageSize = data.getPageSize();
// 总页数 = 总数 / 每页大小,这里会舍去余数
long totalPage = totalSize / pageSize;
if (totalSize % pageSize != 0) {
// 有余数总页数 +1
totalPage += 1;
}
return totalPage;
}
}

View File

@@ -0,0 +1 @@
org.springblade.thirdparty.oa.config.ThirdPartyOAAutoConfiguration