泛微OA-E9与第三方系统集成开发企业级实战记录(九)

今天介绍的是封装泛微e9集成金蝶k3系统所需要的基础工具方法,具体使用参照之前章节。

工具类如下:

java 复制代码
package weaver.interfaces.workflow.action;

import okhttp3.*;
import weaver.conn.RecordSet;
import weaver.general.BaseBean;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 泛微OA对接ERP系统的通用工具类
 * 核心能力:文件Base64编码、ERP鉴权Cookie获取、ERP接口调用、数据库操作、日期转换等
 */
public class ErpIntegrationUtil {
    // 泛微日志工具
    private static final BaseBean BASE_BEAN = new BaseBean();
    // 全局OkHttpClient单例(复用连接池,避免资源泄漏)
    private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)  // 连接超时30s
            .readTimeout(30, TimeUnit.SECONDS)     // 读取超时30s
            .writeTimeout(30, TimeUnit.SECONDS)    // 写入超时30s
            .build();
    // 线程安全的日期格式化器(避免多线程并发问题)
    private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = ThreadLocal.withInitial(
            () -> new SimpleDateFormat("yyyy-MM-dd")
    );

    /**
     * 私有构造器:工具类禁止实例化
     */
    private ErpIntegrationUtil() {
        throw new UnsupportedOperationException("工具类禁止实例化");
    }

    /**
     * 将文件内容编码为Base64字符串
     * @param filePath 文件绝对路径(如D:\\泛微OA.cnf)
     * @return Base64编码字符串 | null(文件不存在/读取失败)
     */
    public static String encodeFileToBase64(String filePath) {
        // 空路径直接返回null
        if (filePath == null || filePath.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 文件路径为空,Base64编码失败");
            return null;
        }

        try {
            // 检查文件是否存在(避免Files.readAllBytes抛FileNotFoundException)
            if (!Files.exists(Paths.get(filePath))) {
                BASE_BEAN.writeLog("[ErpIntegrationUtil] 文件不存在:" + filePath);
                return null;
            }
            // 读取文件字节并编码为Base64
            byte[] fileBytes = Files.readAllBytes(Paths.get(filePath));
            String base64Str = Base64.getEncoder().encodeToString(fileBytes);
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 文件Base64编码成功,路径:" + filePath);
            return base64Str;
        } catch (IOException e) {
            // 泛微日志记录异常,便于排查
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 文件Base64编码失败,路径:" + filePath + ",异常:" + e.getMessage());
            return null;
        }
    }


    /**
     * 获取ERP登录鉴权的Cookie列表,此案例中ERP采用传统的Session-Cookie鉴权模式
     * @return List<String> Set-Cookie头列表 | null(请求失败/无Cookie)
     */
    public static List<String> getErpAuthCookies() {
        // 从泛微配置文件读取ERP登录参数(key在泛微配置文件ERP节点下)
        String loginJson = BASE_BEAN.getPropValue("ERP", "cookieJson");
        String loginUrl = BASE_BEAN.getPropValue("ERP", "url");

        // 空值检查:配置项缺失直接返回
        if (loginJson == null || loginJson.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP登录请求体(cookieJson)配置缺失");
            return null;
        }
        if (loginUrl == null || loginUrl.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP登录地址(url)配置缺失");
            return null;
        }

        // 构建登录请求
        MediaType jsonMediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody requestBody = RequestBody.create(jsonMediaType, loginJson);
        Request loginRequest = new Request.Builder()
                .url(loginUrl)
                .post(requestBody)
                .addHeader("Content-Type", "application/json")
                .build();

        // try-with-resources:自动关闭Response,避免资源泄漏
        try (Response response = OK_HTTP_CLIENT.newCall(loginRequest).execute()) {
            // 检查响应是否成功(200-299)
            if (!response.isSuccessful()) {
                BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP登录请求失败,状态码:" + response.code());
                return null;
            }
            // 安全获取Set-Cookie头列表
            List<String> setCookieList = response.headers().toMultimap().get("set-cookie");
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 获取ERP Cookie成功,数量:" + (setCookieList == null ? 0 : setCookieList.size()));
            return setCookieList;
        } catch (IOException e) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 获取ERP Cookie异常:" + e.getMessage());
            return null;
        }
    }

    /**
     * 携带鉴权Cookie调用ERP业务接口
     * @param requestJson 接口请求体JSON字符串
     * @param authCookies ERP鉴权Cookie列表(getErpAuthCookies返回)
     * @param apiUrl ERP业务接口地址
     * @return 接口响应体字符串 | 空字符串(请求失败/参数异常)
     */
    public static String callErpApiWithCookies(String requestJson, List<String> authCookies, String apiUrl) {
        // 空值检查:核心参数缺失直接返回
        if (requestJson == null || requestJson.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP接口请求体为空");
            return "";
        }
        if (authCookies == null || authCookies.isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP鉴权Cookie为空,接口调用失败");
            return "";
        }
        if (apiUrl == null || apiUrl.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP接口地址为空");
            return "";
        }

        try {
            // 拼接Cookie头:多个Cookie用;分隔,仅保留键值对(去掉Path/HttpOnly等属性)
            StringBuilder cookieHeader = new StringBuilder();
            for (String cookie : authCookies) {
                if (cookie == null || cookie.trim().isEmpty()) {
                    continue;
                }
                // 拆分Cookie键值对(如JSESSIONID=123; Path=/ → 取JSESSIONID=123)
                String cookieKeyValue = cookie.split(";")[0].trim();
                if (cookieHeader.length() > 0) {
                    cookieHeader.append("; ");
                }
                cookieHeader.append(cookieKeyValue);
            }

            // 构建接口请求
            MediaType jsonMediaType = MediaType.parse("application/json; charset=utf-8");
            RequestBody requestBody = RequestBody.create(jsonMediaType, requestJson);
            Request apiRequest = new Request.Builder()
                    .url(apiUrl)
                    .post(requestBody)
                    .addHeader("Content-Type", "application/json")
                    .addHeader("Cookie", cookieHeader.toString())  
                    .build();

            // 执行请求并自动关闭响应
            try (Response response = OK_HTTP_CLIENT.newCall(apiRequest).execute()) {
                if (!response.isSuccessful()) {
                    BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP接口调用失败,状态码:" + response.code());
                    return "";
                }
                // 安全读取响应体(避免body为null)
                String responseBody = response.body() != null ? response.body().string() : "";
                BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP接口调用成功,响应体:" + responseBody);
                return responseBody;
            }
        } catch (IOException e) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] ERP接口调用异常:" + e.getMessage());
            return "";
        }
    }

    /**
     * 根据人员ID查询姓名
     * @param userId 人员ID(泛微hrmresource表的id)
     * @return 人员姓名 | 空字符串(查询失败/ID不存在)
     */
    public static String getEmployeeNameById(String userId) {
        // 空值检查
        if (userId == null || userId.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 人员ID为空,查询姓名失败");
            return "";
        }

        RecordSet recordSet = new RecordSet();
        String sql;
        sql = "select lastname from hrmresource where id=?";   

        try {
            // 占位符执行SQL,避免注入
            recordSet.executeQuery(sql, new Object[]{userId});
            // 检查是否查询到数据
            if (recordSet.next()) {
                String lastName = recordSet.getString("lastname");
                BASE_BEAN.writeLog("[ErpIntegrationUtil] 查询人员姓名成功,ID:" + userId + ",姓名:" + lastName);
                return lastName == null ? "" : lastName;
            } else {
                BASE_BEAN.writeLog("[ErpIntegrationUtil] 未查询到人员姓名,ID:" + userId);
                return "";
            }
        } catch (Exception e) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 查询人员姓名异常,ID:" + userId + ",异常:" + e.getMessage());
            return "";
        }
    }

    /**
     * 根据人员ID查询工号
     * @param userId 人员ID
     * @return 工号 | 空字符串(查询失败/ID不存在)
     */
    public static String getEmployeeWorkCodeById(String userId) {
        if (userId == null || userId.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 人员ID为空,查询工号失败");
            return "";
        }

        RecordSet recordSet = new RecordSet();
        String sql = "select workcode from hrmresource where id=?";

        try {
            recordSet.executeQuery(sql, new Object[]{userId});
            if (recordSet.next()) {
                String workCode = recordSet.getString("workcode");
                BASE_BEAN.writeLog("[ErpIntegrationUtil] 查询人员工号成功,ID:" + userId + ",工号:" + workCode);
                return workCode == null ? "" : workCode;
            } else {
                BASE_BEAN.writeLog("[ErpIntegrationUtil] 未查询到人员工号,ID:" + userId);
                return "";
            }
        } catch (Exception e) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 查询人员工号异常,ID:" + userId + ",异常:" + e.getMessage());
            return "";
        }
    }

    /**
     * 日期字符串转换为秒级时间戳
     * @param dateStr 日期字符串(格式:yyyy-MM-dd)
     * @return 秒级时间戳 | null(转换失败)
     */
    public static Long convertDateToTimestamp(String dateStr) {
        // 空值检查(包含null和空字符串)
        if (dateStr == null || dateStr.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 日期字符串为空,转换时间戳失败");
            return 0L;
        }

        try {
            // 线程安全的日期格式化器
            Date date = DATE_FORMATTER.get().parse(dateStr.trim());
            // 返回Long类型,避免int溢出(2038年问题)
            Long timestamp = date.getTime() / 1000L;
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 日期转时间戳成功,日期:" + dateStr + ",时间戳:" + timestamp);
            return timestamp;
        } catch (ParseException e) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 日期转时间戳异常,日期:" + dateStr + ",异常:" + e.getMessage());
            return 0L;
        }
    }

    /**
     * 更新工作流表单主表的ERP返回编码
     * @param tableName 表单主表名
     * @param erpCode ERP返回的编码(如物料编码)
     * @param requestId 工作流请求ID(泛微requestid)
     * @return true-更新成功 | false-更新失败
     */
    public static boolean updateErpCodeToBill(String tableName, String erpCode, String requestId) {
        // 空值检查
        if (tableName == null || tableName.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 表单表名为空,更新ERP编码失败");
            return false;
        }
        if (requestId == null || requestId.trim().isEmpty()) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 工作流请求ID为空,更新ERP编码失败");
            return false;
        }
        // 编码为空则设为空字符串(避免SQL拼接错误)
        String code = erpCode == null ? "" : erpCode;

        RecordSet recordSet = new RecordSet();
        // 占位符SQL,避免注入风险
        String updateSql = "update " + tableName + " set jkfhbm = ? where requestid = ?";

        try {
            // 执行更新并返回结果
            boolean isSuccess = recordSet.executeUpdate(updateSql, new Object[]{code, requestId});
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 更新ERP编码" + (isSuccess ? "成功" : "失败") +
                    ",表名:" + tableName + ",请求ID:" + requestId + ",编码:" + code);
            return isSuccess;
        } catch (Exception e) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 更新ERP编码异常,表名:" + tableName + ",异常:" + e.getMessage());
            return false;
        }
    }

    /**
     * 物料类型编码转换为ERP对应的类型标识
     * @param materialType 物料类型编码(0/1)
     * @return ERP类型标识 | 空字符串(未知类型)
     */
    public static String convertMaterialTypeToErpCode(String materialType) {
        if (materialType == null) {
            BASE_BEAN.writeLog("[ErpIntegrationUtil] 物料类型编码为空,转换失败");
            return "";
        }

        String erpCode;
        switch (materialType.trim()) {
            case "0":
                erpCode = "BD_Empinfo";
                break;
            case "1":
                erpCode = "BD_Supplier";
                break;
            default:
                erpCode = "";
                BASE_BEAN.writeLog("[ErpIntegrationUtil] 未知物料类型编码:" + materialType);
                break;
        }
        return erpCode;
    }
}
相关推荐
逸Y 仙X2 小时前
文章十:ElasticSearch索引字段高级属性
java·大数据·elasticsearch·搜索引擎·全文检索
就叫飞六吧2 小时前
Tomcat /hvm类加载机制
java·笔记
共享家95272 小时前
Java入门( 日期类与 BigDecimal 工具类 )
java·开发语言
好好学习叭~2 小时前
正则表达式
java·开发语言
草莓熊Lotso2 小时前
MySQL 内置函数指南:日期、字符串、数学函数实战
android·java·linux·运维·数据库·c++·mysql
计算机学姐2 小时前
基于SpringBoot的蛋糕烘焙销售服务系统
java·spring boot·后端·spring·tomcat·intellij-idea·mybatis
hongtianzai2 小时前
Go vs Java:终极性能对决
java·开发语言·golang
程序员老乔2 小时前
Java 新纪元 — JDK 25 + Spring Boot 4 全栈实战(四):结构化并发 & 作用域值,订单聚合查询的新写法
java·数据库·spring boot
代码探秘者2 小时前
【算法篇】2.滑动窗口
java·数据结构·后端·python·算法·spring