Explorar o código

提交待办集成和OA单点功能

Heyuan hai 7 meses
pai
achega
3920fe245c

+ 185 - 0
src/com/kingdee/eas/custom/messageWebService/OAMessageWebServiceDao.java

@@ -0,0 +1,185 @@
+package com.kingdee.eas.custom.messageWebService;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
+import com.kingdee.eas.base.message.*;
+import com.kingdee.eas.base.message.webservice.MessageWebServiceDao;
+import com.kingdee.eas.base.message.webservice.WSMessage;
+import com.kingdee.eas.base.message.webservice.WfrAssignMessage;
+import com.kingdee.eas.base.message.webservice.WfrProcMessage;
+import okhttp3.*;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+import java.io.FileInputStream;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import java.util.Properties;
+
+/**
+ * @Description OA待办消息  ProcessWebService
+ * @Date 2024/10/29 13:38
+ * @Created by Heyuan
+ */
+public class OAMessageWebServiceDao implements MessageWebServiceDao {
+    private static Logger logger = Logger.getLogger(OAMessageWebServiceDao.class);
+
+    /**
+     * 发送消息
+     *
+     * @param message
+     * @return
+     */
+    @Override
+    public boolean addMessage(WSMessage message) {
+        logger.error("*********OAMessageWebServiceDao 消息监听开始**********");
+        try {
+            Properties prop = new Properties();
+            String propPath = System.getProperty("EAS_HOME") + "/server/properties/scy/receiveOAConfig.properties";
+            prop.load(new FileInputStream(propPath));
+            String receiveOAPath = prop.getProperty("receiveOAPath");
+            String serverName = prop.getProperty("serverName");
+            if (StringUtils.isEmpty(receiveOAPath)) {
+                logger.error("发送OA待办消息,推送异步消息地址不能为空! 请检查配置文件: " + propPath);
+                return false;
+            }
+            JSONObject params = new JSONObject();
+            String syscode = prop.getProperty("syscode", "SHR");
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            Date date = new Date();
+            String now = sdf.format(date);
+            params.put("syscode", syscode);//异构系统标识
+            params.put("flowid", message.getMsgID());//流程实例id
+            params.put("requestname", message.getTitle());//标题
+            MsgType type = message.getType();
+            if (type != null) {
+                params.put("workflowname", type.getAlias());//流程类型名称
+            }
+            String billId = message.getBillId();
+            logger.error("billId: " + billId);
+            StringBuilder url = new StringBuilder();
+            StringBuilder redirectUrl = new StringBuilder();
+            url.append(serverName).append("/shr/api/oAToSHR?redirect=");
+            if (!StringUtils.isEmpty(message.getMsgID())) {
+                IAssignRead assignReadIns = AssignReadFactory.getLocalInstance(message.getContext());
+                AssignReadInfo assignReadInfo = assignReadIns.getAssignReadInfo(new ObjectUuidPK(message.getMsgID()));
+                String assignID = assignReadInfo.getAssignID().toString();
+                redirectUrl.append(serverName).append("/easweb/webviews/workflow/transferApprove.jsp?AssignmentId=")
+                        .append(assignID);
+            } else {
+                redirectUrl.append(serverName).append("/shr/dynamic.do?uipk=shr.perself.homepage");
+            }
+            url.append(URLEncoder.encode(redirectUrl.toString(), "UTF-8"));
+            params.put("pcurl", url);//PC地址(相对路径/开头)
+            params.put("appurl", url);//APP地址(相对路径/开头)
+            //params.put("nodename", message.get);//步骤名称(节点名称)
+            params.put("isremark", "0");//流程处理状态
+            params.put("viewtype", "1");//流程查看状态
+            params.put("creator", message.getSenderNumber());//创建人(原值)
+            params.put("createdatetime", now);//创建日期时间
+            params.put("receiver", message.getRecieveNumber());//接收人(原值)
+            params.put("receivedatetime", now);//接收日期时间
+            //时间戳字段,客户端使用线程调用接口的时候,根据此字段判断是否需要更新数据,防止后发的请求数据被之前的覆盖
+            params.put("receivets", String.valueOf(date.getTime()));
+            logger.error("addMessage -> params: " + params);
+            OkHttpClient client = new OkHttpClient();
+            MediaType mediaType = MediaType.parse("application/json");
+            RequestBody body = RequestBody.create(mediaType, params.toString());
+            Request request = new Request.Builder()
+                    .url(receiveOAPath)
+                    .post(body)
+                    .addHeader("content-type", "application/json")
+                    .build();
+            Response response = client.newCall(request).execute();
+            if (response.isSuccessful()) {
+                String result = response.body().string();
+                JSONObject jsonObject = JSONObject.parseObject(result);
+                String operResult = jsonObject.getString("operResult");
+                if ("1".equals(operResult)) {
+                    return true;
+                } else {
+                    logger.error(jsonObject.getString("message"));
+                }
+            } else {
+                logger.error("发送OA待办消息 超时");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    /**
+     * 删除消息
+     *
+     * @param s
+     * @return
+     */
+    @Override
+    public boolean removeMessage(String s) {
+        return false;
+    }
+
+    /**
+     * 删除消息
+     *
+     * @param s
+     * @param context
+     * @return
+     */
+    @Override
+    public boolean removeMessage(String s, Context context) {
+        return false;
+    }
+
+    /**
+     * 更新消息
+     *
+     * @param s
+     * @return
+     */
+    @Override
+    public boolean updateMessage(String s) {
+        return false;
+    }
+
+    /**
+     * 更新消息
+     *
+     * @param s
+     * @param context
+     * @return
+     */
+    @Override
+    public boolean updateMessage(String s, Context context) {
+        return false;
+    }
+
+    /**
+     * @param s
+     * @param msgStatus
+     * @return
+     */
+    @Override
+    public boolean updateMessages(String s, MsgStatus msgStatus) {
+        return false;
+    }
+
+    @Override
+    public boolean updateMessages(String s, MsgStatus msgStatus, Context context) {
+        return false;
+    }
+
+    @Override
+    public boolean addInitiatorMessage(WfrProcMessage wfrProcMessage) {
+        return false;
+    }
+
+    @Override
+    public boolean addCompletedMessage(WfrAssignMessage wfrAssignMessage) {
+        return false;
+    }
+}

+ 366 - 0
src/com/kingdee/eas/custom/sso/OAToSHR.java

@@ -0,0 +1,366 @@
+package com.kingdee.eas.custom.sso;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.eas.cp.eip.sso.ltpa.LtpaTokenManager;
+import com.kingdee.eas.cp.eip.sso.util.CASLoginConfigPropUtil;
+import com.kingdee.eas.cp.eip.sso.util.CloudParamUtil;
+import com.kingdee.eas.util.app.DbUtil;
+import com.kingdee.jdbc.rowset.IRowSet;
+import com.kingdee.shr.base.syssetting.exception.SHRWebException;
+import com.kingdee.util.StringUtils;
+import okhttp3.*;
+import org.apache.log4j.Logger;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * @Description OA单点sHR
+ * @Date 2024/10/30 11:22
+ * @Created by Heyuan
+ */
+public class OAToSHR extends HttpServlet {
+    private static Logger logger = Logger.getLogger(OAToSHR.class);
+
+    private Properties prop = new Properties();
+    private String propPath = System.getProperty("EAS_HOME") + "/server/properties/scy/OASSOConfig.properties";
+
+    private final String SECRETKEY = "jOK7MpIonY+/56ulnl6RGQ==";
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        logger.error("OAToSHR -> doGet");
+        doPost(req, resp);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        logger.error("OAToSHR -> doPost");
+        BufferedReader streamReader = null;
+        String resultStr = null;
+        prop.load(new FileInputStream(propPath));
+        logger.error("OAToSHR  requestUrl" + req.getRequestURL().toString());
+        try {
+            String ticket = req.getParameter("ticket");
+            logger.error("接收到的请求参数是:ticket " + ticket);
+            if (StringUtils.isEmpty(ticket)) {
+                //认证
+                authorize(req, resp);
+            } else {
+                //获取用户信息
+                callBack(req, resp, ticket);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            Map result = new HashMap();
+            result.put("msgType", "0");
+            result.put("reason", e.getMessage());
+            resultStr = JSON.toJSONString(result);
+            resp.setStatus(500);
+            PrintWriter writer = resp.getWriter();
+            resp.setContentType("application/json");
+            writer.write(resultStr);
+            writer.close();
+        } finally {
+            try {
+                if (streamReader != null) {
+                    streamReader.close();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 认证
+     * 拼接OA认证接口地址,转发
+     *
+     * @param resp
+     * @throws Exception
+     */
+    public void authorize(HttpServletRequest req, HttpServletResponse resp) throws Exception {
+        String authorizePath = prop.getProperty("authorizePath");
+        if (StringUtils.isEmpty(authorizePath)) {
+            throw new RuntimeException("authorizePath不能为空! 请检查配置文件: " + propPath);
+        }
+        String response_type = prop.getProperty("response_type");
+        if (StringUtils.isEmpty(response_type)) {
+            throw new RuntimeException("response_type不能为空! 请检查配置文件: " + propPath);
+        }
+        String client_id = prop.getProperty("client_id");
+        if (StringUtils.isEmpty(client_id)) {
+            throw new RuntimeException("client_id不能为空! 请检查配置文件: " + propPath);
+        }
+        String redirect_uri1 = prop.getProperty("redirect_uri1");
+        if (StringUtils.isEmpty(redirect_uri1)) {
+            throw new RuntimeException("redirect_uri1不能为空! 请检查配置文件: " + propPath);
+        }
+        String redirectUrl = req.getParameter("redirect");
+        logger.error("callBack redirectUrl" + redirectUrl);
+        String encrypt = encrypt(redirectUrl, SECRETKEY);
+        logger.error("callBack encrypt" + encrypt);
+        redirect_uri1 += "?redirect=" + URLEncoder.encode(encrypt, "UTF-8");
+        Map params = new HashMap();
+        params.put("client_id", client_id);
+        params.put("response_type", response_type);
+        params.put("redirect_uri", URLEncoder.encode(redirect_uri1, "UTF-8"));
+        String urlString = appendUrl(authorizePath, params);
+        resp.sendRedirect(urlString);
+        logger.error("authorize url" + urlString);
+    }
+
+    /**
+     * OA回调方法
+     * 获取Token和用户信息,单点到shr
+     *
+     * @param req
+     * @param resp
+     * @param ticket
+     * @throws SHRWebException
+     * @throws UnsupportedEncodingException
+     */
+    public void callBack(HttpServletRequest req, HttpServletResponse resp, String ticket) throws
+            SHRWebException, UnsupportedEncodingException {
+        logger.error("callback方法入参");
+        if (StringUtils.isEmpty(ticket)) {
+            throw new RuntimeException("ticket不能为空!");
+        }
+        String getAccessTokenPath = prop.getProperty("getAccessTokenPath");
+        if (StringUtils.isEmpty(getAccessTokenPath)) {
+            throw new RuntimeException("getAccessTokenPath不能为空! 请检查配置文件: " + propPath);
+        }
+        String client_secret = prop.getProperty("client_secret");
+        if (StringUtils.isEmpty(client_secret)) {
+            throw new RuntimeException("client_secret不能为空! 请检查配置文件: " + propPath);
+        }
+        String client_id = prop.getProperty("client_id");
+        if (StringUtils.isEmpty(client_id)) {
+            throw new RuntimeException("client_id不能为空! 请检查配置文件: " + propPath);
+        }
+        String redirect_uri2 = prop.getProperty("redirect_uri2");
+        if (StringUtils.isEmpty(redirect_uri2)) {
+            throw new RuntimeException("redirect_uri2不能为空! 请检查配置文件: " + propPath);
+        }
+        String getLoginIdPath = prop.getProperty("getLoginIdPath");
+        if (StringUtils.isEmpty(getLoginIdPath)) {
+            throw new RuntimeException("getLoginIdPath不能为空! 请检查配置文件: " + propPath);
+        }
+        String redirectUrl = req.getParameter("redirect");
+        logger.error("callBack redirectUrl" + redirectUrl);
+        Map params = new HashMap();
+        params.put("client_id", client_id);
+        params.put("client_secret", client_secret);
+        params.put("grant_type", "authorization_code");
+        params.put("code", ticket);
+        params.put("redirect_uri", URLEncoder.encode(redirect_uri2, "UTF-8"));
+        try {
+            String token = getAccessToken(getAccessTokenPath, params);
+            logger.error(token);
+            //从人员对象,获取纷享用户userId
+            String loginId = loginId2userId(getLoginIdPath, token);
+            String loginUrl = login(loginId, redirectUrl);
+            resp.sendRedirect(loginUrl);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    /**
+     * 获取携带账号和密码的登录地址
+     *
+     * @param userNumber
+     * @param redirectUrl
+     * @return
+     */
+    private String login(String userNumber, String redirectUrl) throws Exception {
+        String password = LtpaTokenManager.generate(userNumber, LtpaTokenManager.getDefaultLtpaConfig()).toString();
+        logger.error("login: password" + password);
+        String serverName = prop.getProperty("serverName");
+        if (StringUtils.isEmpty(serverName)) {
+            throw new RuntimeException("serverName不能为空! 请检查配置文件: " + propPath);
+        }
+        StringBuilder url = new StringBuilder();
+        url.append("/shr/index2sso.jsp?username=").append(userNumber)
+                .append("&password=").append(password).append("&redirectTo=");
+        StringBuilder redirectUrlStr = new StringBuilder();
+        if (StringUtils.isEmpty(redirectUrl)) {
+            redirectUrlStr.append(serverName).append("/shr/dynamic.do?uipk=shr.perself.homepage");
+        } else {
+            //解密
+            String decrpt = decrypt(redirectUrl, SECRETKEY);
+            if (decrpt.contains(serverName)) {
+                redirectUrlStr.append(decrpt);
+            } else {
+                redirectUrlStr.append(serverName).append(decrpt);
+            }
+        }
+        url.append(URLEncoder.encode(redirectUrlStr.toString(), "UTF-8"));
+        logger.error("login: url" + url);
+        return url.toString();
+    }
+
+    /**
+     * 获取泛微token方法
+     *
+     * @param getAccessTokenPath
+     * @param params
+     * @return
+     * @throws IOException
+     */
+    private String getAccessToken(String getAccessTokenPath, Map<String, String> params)
+            throws IOException {
+        logger.error("getAccessToken方法参数: " + params);
+        String url = appendUrl(getAccessTokenPath, params);
+        logger.error("access_token url" + url);
+        OkHttpClient client = new OkHttpClient();
+        Request request = new Request.Builder()
+                .url(url)
+                .get()
+                .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001")
+                .build();
+        Response response = client.newCall(request).execute();
+        if (response.isSuccessful()) {
+            String string = response.body().string();
+            JSONObject jsonObject = JSONObject.parseObject(string);
+            String code = jsonObject.getString("code");
+            if ("0".equals(code)) {
+                String access_token = jsonObject.getString("access_token");
+                logger.error("access_token " + access_token);
+                return access_token;
+            } else {
+                throw new RuntimeException(jsonObject.getString("msg"));
+            }
+        } else {
+            //网络超时
+            throw new RuntimeException("获取token超时");
+        }
+    }
+
+    /**
+     * 获取用户信息
+     *
+     * @param accessToken
+     * @return
+     * @throws IOException
+     * @throws BOSException
+     * @throws SQLException
+     */
+    private String loginId2userId(String getLoginIdPath, String accessToken)
+            throws IOException, BOSException, SQLException {
+        if (StringUtils.isEmpty(accessToken)) {
+            throw new RuntimeException("accessToken不能为空! ");
+        }
+        //获取第三方用户信息
+        Map params = new HashMap();
+        params.put("access_token", accessToken);
+        String url = appendUrl(getLoginIdPath, params);
+        logger.error("loginId2userId url" + url);
+        OkHttpClient client = new OkHttpClient();
+        Request request = new Request.Builder()
+                .url(url)
+                .get()
+                .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001")
+                .build();
+        Response response = client.newCall(request).execute();
+        if (response.isSuccessful()) {
+            String string = response.body().string();
+            JSONObject jsonObject = JSONObject.parseObject(string);
+            String code = jsonObject.getString("code");
+            if ("0".equals(code)) {
+                JSONObject attributes = jsonObject.getJSONObject("attributes");
+                //登录id
+                String loginid = attributes.getString("loginid");
+                String dataCenter = CASLoginConfigPropUtil.getDataCenter();
+                String locale = CASLoginConfigPropUtil.getLocale();
+                if (!StringUtils.isEmpty(dataCenter) && !StringUtils.isEmpty(locale)) {
+                    Context ctx = CloudParamUtil.getContext(dataCenter, locale, "administrator");
+                    String sql = "SELECT count(1) total FROM T_PM_USER WHERE fnumber=?";
+                    IRowSet rs = DbUtil.executeQuery(ctx, sql, new Object[]{loginid});
+                    int total = 0;
+                    if (rs.next()) {
+                        total = rs.getInt("total");
+                    }
+                    if (total <= 0) {
+                        logger.error("SHR找不到对应的用户, loginid:" + loginid);
+                        throw new RuntimeException("SHR找不到对应的用户, loginid: " + loginid);
+                        //("您无权限访问SHR系统,请联系管理员处理。")
+                    } else if (total > 1) {
+                        logger.error("SHR找到多个对应的用户, loginid:" + loginid);
+                        throw new RuntimeException("SHR找到多个对应的用户, loginid: " + loginid);
+                        //("您无权限访问SHR系统,请联系管理员处理。")
+                    } else {
+                        return loginid;
+                    }
+                }
+                logger.error("获取用户信息报错,数据中心没找到!");
+                throw new RuntimeException("获取用户信息报错,数据中心没找到!");
+            } else {
+                logger.error(jsonObject.getString("msg"));
+                throw new RuntimeException(jsonObject.getString("msg"));
+            }
+        } else {
+            //网络超时
+            logger.error("网络超时");
+            throw new RuntimeException("网络超时");
+        }
+    }
+
+
+    /**
+     * 拼接地址参数
+     */
+    private static String appendUrl(String url, Map<String, String> data) {
+        logger.error("appendUrl_url: " + url);
+        logger.error("appendUrl_data: " + data);
+        StringBuilder paramStr = new StringBuilder();
+        for (String key : data.keySet()) {
+            paramStr.append(key).append("=").append(data.get(key)).append("&");
+        }
+        paramStr.deleteCharAt(paramStr.lastIndexOf("&"));
+        String str = url.contains("?") ? (url + "&" + paramStr) : (url + "?" + paramStr);
+        logger.error("拼接后的地址为:" + str);
+        return str;
+    }
+
+    // 使用 AES 加密
+    public static String encrypt(String plainText, String encodedKey) throws Exception {
+        // 将Base64字符串解码为字节数组
+        byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
+        // 使用字节数组创建一个AES密钥
+        SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
+        Cipher cipher = Cipher.getInstance("AES");
+        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
+        return Base64.getEncoder().encodeToString(encryptedBytes);
+    }
+
+    // 使用 AES 解密
+    public static String decrypt(String encryptedText, String encodedKey) throws Exception {
+        // 将Base64字符串解码为字节数组
+        byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
+        // 使用字节数组创建一个AES密钥
+        SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
+        Cipher cipher = Cipher.getInstance("AES");
+        cipher.init(Cipher.DECRYPT_MODE, secretKey);
+        byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);
+        byte[] decryptedBytes = cipher.doFinal(decodedBytes);
+        return new String(decryptedBytes, "UTF-8");
+    }
+
+}