diff --git a/JSD-8415-需求确认书V1.docx b/JSD-8415-需求确认书V1.docx
new file mode 100644
index 0000000..718e50f
Binary files /dev/null and b/JSD-8415-需求确认书V1.docx differ
diff --git a/JSD-8415配置使用文档.docx b/JSD-8415配置使用文档.docx
new file mode 100644
index 0000000..f7d6b2f
Binary files /dev/null and b/JSD-8415配置使用文档.docx differ
diff --git a/README.md b/README.md
index 34035a3..ac1d100 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
# open-JSD-8415
-JSD-8415 开源任务材料
\ No newline at end of file
+JSD-8415 定时任务OA推送\
+免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
+仅作为开发者学习参考使用!禁止用于任何商业用途!\
+为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。
\ No newline at end of file
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..8435ad3
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,26 @@
+
+ com.fr.plugin.schedule.oa
+
+ yes
+ 1.4
+ 10.0
+ 2018-07-31
+ fr.open
+
+
+ [2021-08-17]【1.1】增加标题参数逻辑,修改提交表单的问题。
+ [2021-08-17]【1.2】增加标题参数逻辑,修改提交表单文本编码的问题。
+ [2021-08-17]【1.3】修改获取标题的逻辑,支持公式。
+ [2021-08-17]【1.4】增加支持多用户独立调度。
+ ]]>
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/schedule/oa/HttpUtil.java b/src/main/java/com/fr/plugin/schedule/oa/HttpUtil.java
new file mode 100644
index 0000000..4904775
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/HttpUtil.java
@@ -0,0 +1,310 @@
+package com.fr.plugin.schedule.oa;
+
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.third.org.apache.http.HttpResponse;
+import com.fr.third.org.apache.http.HttpStatus;
+import com.fr.third.org.apache.http.client.HttpClient;
+import com.fr.third.org.apache.http.client.methods.HttpPost;
+import com.fr.third.org.apache.http.config.Registry;
+import com.fr.third.org.apache.http.config.RegistryBuilder;
+import com.fr.third.org.apache.http.conn.socket.ConnectionSocketFactory;
+import com.fr.third.org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import com.fr.third.org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import com.fr.third.org.apache.http.entity.StringEntity;
+import com.fr.third.org.apache.http.impl.client.HttpClients;
+import com.fr.third.org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import com.fr.third.org.apache.http.util.EntityUtils;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.cert.CertificateException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+public class HttpUtil {
+
+ private static HostnameVerifier hv = new HostnameVerifier() {
+ @Override
+ public boolean verify(String urlHostName, SSLSession session) {
+ System.out.println("Warning: URL Host: " + urlHostName + " vs. "
+ + session.getPeerHost());
+ return true;
+ }
+ };
+
+ /**
+ * 发送get请求
+ *
+ * @param url
+ * @param param
+ * @param header
+ * @return
+ * @throws IOException
+ */
+ public static String sendGet(String url, Map param, Map header) {
+ String result = "";
+ BufferedReader in = null;
+ String urlNameString = url;
+ try {
+ if (param != null) {
+ urlNameString += "?";
+ urlNameString += param.entrySet()
+ .stream()
+ .map(entry -> entry.getKey() + "=" + entry.getValue())
+ .collect(Collectors.joining("&"));
+ }
+
+ URL realUrl = new URL(urlNameString);
+ // 打开和URL之间的连接
+ HttpURLConnection connection;
+ if (url.startsWith("https")) {
+ trustAllHttpsCertificates();
+ HttpsURLConnection.setDefaultHostnameVerifier(hv);
+ connection = (HttpURLConnection) realUrl.openConnection();
+ } else {
+ connection = (HttpURLConnection) realUrl.openConnection();
+ }
+ //设置超时时间
+ connection.setDoInput(true);
+ connection.setRequestMethod("GET");
+ connection.setConnectTimeout(5000);
+ connection.setReadTimeout(15000);
+ // 设置通用的请求属性
+ if (header != null) {
+ Iterator> it = header.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = it.next();
+ System.out.println(entry.getKey() + ":::" + entry.getValue());
+ connection.setRequestProperty(entry.getKey(), entry.getValue());
+ }
+ }
+ connection.setRequestProperty("accept", "*/*");
+ connection.setRequestProperty("connection", "Keep-Alive");
+ connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ // 建立实际的连接
+ connection.connect();
+ // 定义 BufferedReader输入流来读取URL的响应,设置utf8防止中文乱码
+ in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result += line;
+ }
+ if (in != null) {
+ in.close();
+ }
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e, "get url error ,url is:{},error is {}", urlNameString, e.getMessage());
+ }
+ return result;
+ }
+
+ public static String sendPost(String url, Map header, JSONObject body) {
+ PrintWriter out = null;
+ BufferedReader in = null;
+ String result = null;
+ String res = null;
+ try {
+ String urlNameString = url;
+
+ URL realUrl = new URL(urlNameString);
+ // 打开和URL之间的连接
+ HttpURLConnection conn;
+ if (url.startsWith("https")) {
+ trustAllHttpsCertificates();
+ HttpsURLConnection.setDefaultHostnameVerifier(hv);
+ conn = (HttpURLConnection) realUrl.openConnection();
+ } else {
+ conn = (HttpURLConnection) realUrl.openConnection();
+ }
+ // 设置通用的请求属性
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+// conn.setRequestProperty("user-agent",
+// "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ if (header != null) {
+ header.forEach((k, v) -> {
+ conn.setRequestProperty(k, String.valueOf(v));
+ });
+ }
+ // 发送POST请求必须设置如下两行
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ //获取请求头
+
+ // 获取URLConnection对象对应的输出流
+ out = new PrintWriter(conn.getOutputStream());
+ // 发送请求参数
+ if (body != null) {
+ FineLoggerFactory.getLogger().error("content data: {}", body.toString());
+ FineLoggerFactory.getLogger().error("content cover data: {}", new String(body.toString().getBytes("UTF-8"), "UTF-8"));
+ out.print(new String(body.toString().getBytes("UTF-8"), "UTF-8"));
+ }
+ // flush输出流的缓冲
+ out.flush();
+ // 定义BufferedReader输入流来读取URL的响应
+ in = new BufferedReader(
+ new InputStreamReader(conn.getInputStream(),"UTF-8"));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result += line;
+ }
+ res = result;
+ if (res.startsWith("null")) {
+ res = res.replace("null", "");
+ }
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ //使用finally块来关闭输出流、输入流
+ finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ }
+ return res;
+ }
+
+
+ public static String doPost(String url, Map header, JSONObject json) {
+ HttpClient client = HttpClients.createDefault();
+ if (url.startsWith("https")) {
+ SSLContext sslcontext = createIgnoreVerifySSL();
+ Registry socketFactoryRegistry = RegistryBuilder.create()
+ .register("http", PlainConnectionSocketFactory.INSTANCE)
+ .register("https", new SSLConnectionSocketFactory(sslcontext))
+ .build();
+ PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+ HttpClients.custom().setConnectionManager(connManager);
+ client = HttpClients.custom().setConnectionManager(connManager).build();
+ }
+ HttpPost post = new HttpPost(url);
+ post.setHeader("accept", "*/*");
+ post.setHeader("connection", "Keep-Alive");
+ post.setHeader("Content-Type", "application/json");
+ if (header != null) {
+ header.forEach((k, v) -> {
+ post.setHeader(k, String.valueOf(v));
+ });
+ }
+ try {
+ StringEntity s = new StringEntity(json.toString(),"UTF-8");
+ s.setContentEncoding("UTF-8");
+ s.setContentType("application/json; charset=UTF-8");//发送json数据需要设置contentType
+ post.setEntity(s);
+ HttpResponse res = client.execute(post);
+ if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+ String result = EntityUtils.toString(res.getEntity());// 返回json格式:
+ return result;
+ }
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(),e);
+ }
+ return null;
+ }
+
+ private static void trustAllHttpsCertificates() throws Exception {
+ TrustManager[] trustAllCerts = new TrustManager[1];
+ TrustManager tm = new miTM();
+ trustAllCerts[0] = tm;
+ SSLContext sc = SSLContext.getInstance("SSL", "SunJSSE");
+ sc.init(null, trustAllCerts, null);
+ HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+ }
+
+
+ /**
+ * encode url by UTF-8
+ *
+ * @param url url before encoding
+ * @return url after encoding
+ */
+ public static String encodeUrl(String url) {
+ String eurl = url;
+ try {
+ eurl = URLEncoder.encode(url, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ }
+ return eurl;
+ }
+
+ private static class miTM implements TrustManager,
+ X509TrustManager {
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ public boolean isServerTrusted(
+ java.security.cert.X509Certificate[] certs) {
+ return true;
+ }
+
+ public boolean isClientTrusted(
+ java.security.cert.X509Certificate[] certs) {
+ return true;
+ }
+
+ @Override
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs, String authType)
+ throws CertificateException {
+ return;
+ }
+
+ @Override
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs, String authType)
+ throws CertificateException {
+ return;
+ }
+ }
+
+ public static SSLContext createIgnoreVerifySSL() {
+ try {
+ SSLContext sc = SSLContext.getInstance("SSLv3");
+
+ // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
+ X509TrustManager trustManager = new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
+ String paramString) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
+ String paramString) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ };
+
+ sc.init(null, new TrustManager[]{trustManager}, null);
+ return sc;
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/OaOutPutDao.java b/src/main/java/com/fr/plugin/schedule/oa/OaOutPutDao.java
new file mode 100644
index 0000000..c5e8cb9
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/OaOutPutDao.java
@@ -0,0 +1,15 @@
+package com.fr.plugin.schedule.oa;
+
+import com.fr.plugin.schedule.oa.entity.OaOutPutEntity;
+import com.fr.stable.db.dao.BaseDAO;
+import com.fr.stable.db.session.DAOSession;
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+public class OaOutPutDao extends BaseDAO {
+ public OaOutPutDao(DAOSession daoSession) {
+ super(daoSession);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/OaOutputDBAccess.java b/src/main/java/com/fr/plugin/schedule/oa/OaOutputDBAccess.java
new file mode 100644
index 0000000..edcaa03
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/OaOutputDBAccess.java
@@ -0,0 +1,41 @@
+package com.fr.plugin.schedule.oa;
+
+import com.fr.decision.plugin.db.AbstractDecisionDBAccessProvider;
+import com.fr.plugin.schedule.oa.entity.OaOutPutEntity;
+import com.fr.stable.db.accessor.DBAccessor;
+import com.fr.stable.db.dao.BaseDAO;
+import com.fr.stable.db.dao.DAOProvider;
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+public class OaOutputDBAccess extends AbstractDecisionDBAccessProvider {
+
+ private static DBAccessor dbAccessor;
+ public DBAccessor getDbAccessor() {
+ return dbAccessor;
+ }
+
+ @Override
+ public DAOProvider[] registerDAO() {
+ return new DAOProvider[]{
+ new DAOProvider() {
+ @Override
+ public Class getEntityClass() {
+ return OaOutPutEntity.class;
+ }
+
+ @Override
+ public Class extends BaseDAO> getDAOClass() {
+ return OaOutPutDao.class;
+ }
+ }
+ };
+ }
+
+ @Override
+ public void onDBAvailable(DBAccessor dbAccessor) {
+ OaOutputDBAccess.dbAccessor = dbAccessor;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/OutputPluginLifecycleMonitor.java b/src/main/java/com/fr/plugin/schedule/oa/OutputPluginLifecycleMonitor.java
new file mode 100644
index 0000000..946da0f
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/OutputPluginLifecycleMonitor.java
@@ -0,0 +1,33 @@
+package com.fr.plugin.schedule.oa;
+
+import com.fr.plugin.context.PluginContext;
+import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor;
+import com.fr.plugin.schedule.oa.config.OutputSimpleConfig;
+import com.fr.plugin.schedule.oa.entity.OaOutPutEntity;
+import com.fr.plugin.schedule.oa.entity.OaOutputAction;
+import com.fr.plugin.schedule.oa.format.PNGOutputFormat;
+import com.fr.plugin.schedule.oa.handle.OaOutPutHandle;
+import com.fr.schedule.extension.report.job.output.BaseOutputFormat;
+import com.fr.schedule.feature.ScheduleOutputActionEntityRegister;
+import com.fr.schedule.feature.output.OutputActionHandler;
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+public class OutputPluginLifecycleMonitor extends AbstractPluginLifecycleMonitor {
+ @Override
+ public void afterRun(PluginContext pluginContext) {
+ OutputSimpleConfig.getInstance();
+ BaseOutputFormat.registerOutputFormat(new PNGOutputFormat());
+ OutputActionHandler.registerHandler(new OaOutPutHandle(), OaOutputAction.class.getName());
+ ScheduleOutputActionEntityRegister.getInstance().addClass(OaOutPutEntity.class);
+ }
+
+ @Override
+ public void beforeStop(PluginContext pluginContext) {
+ BaseOutputFormat.removeOutputFormat(PNGOutputFormat.CONVERT_TO_PNG);
+ OutputActionHandler.removeOutputHandler(OaOutputAction.class.getName());
+ ScheduleOutputActionEntityRegister.getInstance().removeClass(OaOutPutEntity.class);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/config/OutputSimpleConfig.java b/src/main/java/com/fr/plugin/schedule/oa/config/OutputSimpleConfig.java
new file mode 100644
index 0000000..a025147
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/config/OutputSimpleConfig.java
@@ -0,0 +1,117 @@
+package com.fr.plugin.schedule.oa.config;
+
+import com.fr.config.*;
+import com.fr.config.holder.Conf;
+import com.fr.config.holder.factory.Holders;
+import com.fr.intelli.record.Focus;
+import com.fr.intelli.record.Original;
+import com.fr.record.analyzer.EnableMetrics;
+import com.fr.stable.StringUtils;
+
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+@Visualization(category = "Plugin-Config_Output")
+@EnableMetrics
+public class OutputSimpleConfig extends DefaultConfiguration {
+
+ private static final String TOKEN_URL="{sfurl}/seeyon/rest/token/{username}/{password}?loginName={account}";
+
+ private static final String ATTACH_URL="{sfurl}/seeyon/rest/attachment";
+
+ private static final String FORM_URL="{sfurl}/seeyon/rest/initia/flowFrom";
+
+ private static volatile OutputSimpleConfig config = null;
+ @Identifier(value = "sfUrl", name = "Plugin-Config_Property_sfUrl", description = "Plugin-Config_Property_sfUrl_Description", status = Status.SHOW)
+ private Conf sfUrl = Holders.simple(StringUtils.EMPTY);
+ @Identifier(value = "username", name = "Plugin-Config_Property_username", description = "Plugin-Config_Property_username_Description", status = Status.SHOW)
+ private Conf username = Holders.simple(StringUtils.EMPTY);
+
+ @Identifier(value = "password", name = "Plugin-Config_Property_password", description = "Plugin-Config_Property_password_Description", status = Status.SHOW)
+ private Conf password = Holders.simple(StringUtils.EMPTY);
+
+ @Identifier(value = "account", name = "Plugin-Config_Property_account", description = "Plugin-Config_Property_account_Description", status = Status.SHOW)
+ private Conf account = Holders.simple(StringUtils.EMPTY);
+
+ @Identifier(value = "tabledata", name = "Plugin-Config_Property_tabledata", description = "Plugin-Config_Property_tabledata_Description", status = Status.SHOW)
+ private Conf tabledata = Holders.simple(StringUtils.EMPTY);
+
+
+ @Focus(id = "com.fr.plugin.schedule.oa", text = "Plugin-Config_Output", source = Original.PLUGIN)
+ public static OutputSimpleConfig getInstance() {
+ if (config == null) {
+ config = ConfigContext.getConfigInstance(OutputSimpleConfig.class);
+ }
+ return config;
+ }
+
+ public String getSfUrl() {
+ return sfUrl.get();
+ }
+
+ public void setSfUrl(String sfUrl) {
+ this.sfUrl.set(sfUrl);
+ }
+
+ public String getUsername() {
+ return username.get();
+ }
+
+ public void setUsername(String username) {
+ this.username.set(username);
+ }
+
+ public String getPassword() {
+ return password.get();
+ }
+
+ public void setPassword(String password) {
+ this.password.set(password);
+ }
+
+ public String getAccount() {
+ return account.get();
+ }
+
+ public void setAccount(String account) {
+ this.account.set(account);
+ }
+
+ public String getTabledata() {
+ return tabledata.get();
+ }
+
+ public void setTabledata(String tabledata) {
+ this.tabledata.set(tabledata);
+ }
+
+ public boolean isAccept() {
+ return StringUtils.isNotBlank(getSfUrl()) && StringUtils.isNotBlank(getUsername()) && StringUtils.isNotBlank(getPassword())
+ && StringUtils.isNotBlank(getAccount()) && StringUtils.isNotBlank(getTabledata());
+ }
+
+ public String tokenUrl(){
+ return TOKEN_URL.replace("{sfurl}",getSfUrl()).replace("{username}",getUsername()).replace("{password}",getPassword())
+ .replace("{account}",getAccount());
+ }
+
+ public String attachUrl(){
+ return ATTACH_URL.replace("{sfurl}",getSfUrl());
+ }
+ public String formUrl(){
+ return FORM_URL.replace("{sfurl}",getSfUrl());
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ OutputSimpleConfig cloned = (OutputSimpleConfig) super.clone();
+ cloned.sfUrl = (Conf) sfUrl.clone();
+ cloned.username = (Conf) username.clone();
+ cloned.password = (Conf) password.clone();
+ cloned.account = (Conf) account.clone();
+ cloned.tabledata = (Conf) tabledata.clone();
+ return cloned;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/config/PluginLocaleFinderBridge.java b/src/main/java/com/fr/plugin/schedule/oa/config/PluginLocaleFinderBridge.java
new file mode 100644
index 0000000..4e458c3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/config/PluginLocaleFinderBridge.java
@@ -0,0 +1,14 @@
+package com.fr.plugin.schedule.oa.config;
+
+import com.fr.stable.fun.impl.AbstractLocaleFinder;
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+public class PluginLocaleFinderBridge extends AbstractLocaleFinder {
+ @Override
+ public String find() {
+ return "com/fr/plugin/schedule/oa/locale/conf";
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/entity/OaOutPutEntity.java b/src/main/java/com/fr/plugin/schedule/oa/entity/OaOutPutEntity.java
new file mode 100644
index 0000000..a2124a2
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/entity/OaOutPutEntity.java
@@ -0,0 +1,43 @@
+package com.fr.plugin.schedule.oa.entity;
+
+import com.fr.schedule.base.bean.BaseBean;
+import com.fr.schedule.base.entity.AbstractScheduleEntity;
+import com.fr.stable.db.entity.TableAssociation;
+import com.fr.third.javax.persistence.Entity;
+import com.fr.third.javax.persistence.Table;
+
+/**
+ * @Author fr.open
+ * @Date 2021/8/14
+ * @Description
+ **/
+@Entity
+@Table(name = "fine_output_oa") //表名
+@TableAssociation(associated = true)
+public class OaOutPutEntity extends AbstractScheduleEntity {
+
+ private Integer pushImg;
+
+ @Override
+ public BaseBean createBean() {
+ return new OaOutputAction().id(this.getId()).pushImg(getPushImg());
+ }
+
+ public OaOutPutEntity id(String id) {
+ setId(id);
+ return this;
+ }
+
+ public OaOutPutEntity pushImg(Integer pushImg) {
+ setPushImg(pushImg);
+ return this;
+ }
+
+ public Integer getPushImg() {
+ return pushImg;
+ }
+
+ public void setPushImg(Integer pushImg) {
+ this.pushImg = pushImg;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/entity/OaOutputAction.java b/src/main/java/com/fr/plugin/schedule/oa/entity/OaOutputAction.java
new file mode 100644
index 0000000..75e8370
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/entity/OaOutputAction.java
@@ -0,0 +1,58 @@
+package com.fr.plugin.schedule.oa.entity;
+
+import com.fr.schedule.base.bean.output.BaseOutputAction;
+import com.fr.schedule.base.entity.AbstractScheduleEntity;
+import com.fr.schedule.base.type.RunType;
+import com.fr.third.fasterxml.jackson.annotation.JsonSubTypes;
+
+/**
+ * @Author fr.open
+ * @Date 2021/8/14
+ * @Description
+ **/
+@JsonSubTypes.Type(value = OaOutputAction.class, name = "OaOutputAction")
+public class OaOutputAction extends BaseOutputAction {
+
+ private static final long serialVersionUID = -2455866100756916542L;
+
+ private Integer pushImg;
+
+ @Override
+ public boolean willExecuteByUser() {
+ return true;
+ }
+
+ @Override
+ public RunType runType() {
+ return RunType.SEND_FILE;
+ }
+
+ @Override
+ public OaOutputAction id(String id) {
+ setId(id);
+ return this;
+ }
+
+ public OaOutputAction pushImg(Integer pushImg){
+ setPushImg(pushImg);
+ return this;
+ }
+
+ public Integer getPushImg() {
+ return pushImg;
+ }
+
+ public void setPushImg(Integer pushImg) {
+ this.pushImg = pushImg;
+ }
+
+ @Override
+ public Class extends AbstractScheduleEntity> outputActionEntityClass() {
+ return OaOutPutEntity.class;
+ }
+
+ @Override
+ public AbstractScheduleEntity createOutputActionEntity() {
+ return new OaOutPutEntity().id(this.getId()).pushImg(getPushImg());
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/format/PNGOutputFormat.java b/src/main/java/com/fr/plugin/schedule/oa/format/PNGOutputFormat.java
new file mode 100644
index 0000000..b9fc26e
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/format/PNGOutputFormat.java
@@ -0,0 +1,39 @@
+package com.fr.plugin.schedule.oa.format;
+
+import com.fr.io.exporter.ImageExporter;
+import com.fr.main.workbook.ResultWorkBook;
+import com.fr.schedule.extension.report.job.output.BaseOutputFormat;
+
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+public class PNGOutputFormat extends BaseOutputFormat {
+ public static final int CONVERT_TO_PNG = 64;
+
+ public PNGOutputFormat() {
+ }
+
+ @Override
+ public int getFormat() {
+ return CONVERT_TO_PNG;
+ }
+
+ @Override
+ public String getFileSuffix() {
+ return ".png";
+ }
+
+ @Override
+ public boolean withParentPath() {
+ return true;
+ }
+
+ @Override
+ public void flushWithParentPath(OutputStream outputStream, ResultWorkBook workBook, String name, final List list) throws Exception {
+ new ImageExporter("png", 96).export(outputStream, workBook);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/schedule/oa/handle/OaOutPutHandle.java b/src/main/java/com/fr/plugin/schedule/oa/handle/OaOutPutHandle.java
new file mode 100644
index 0000000..ed44911
--- /dev/null
+++ b/src/main/java/com/fr/plugin/schedule/oa/handle/OaOutPutHandle.java
@@ -0,0 +1,145 @@
+package com.fr.plugin.schedule.oa.handle;
+
+import com.fr.base.Formula;
+import com.fr.base.TableData;
+import com.fr.file.TableDataConfig;
+import com.fr.general.data.DataModel;
+import com.fr.general.http.HttpRequest;
+import com.fr.general.http.HttpToolbox;
+import com.fr.io.utils.ResourceIOUtils;
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.plugin.schedule.oa.config.OutputSimpleConfig;
+import com.fr.plugin.schedule.oa.entity.OaOutputAction;
+import com.fr.schedule.base.constant.ScheduleConstants;
+import com.fr.schedule.feature.output.OutputActionHandler;
+import com.fr.script.Calculator;
+import com.fr.stable.StringUtils;
+import com.fr.third.org.apache.http.entity.ContentType;
+import com.fr.third.org.apache.http.entity.StringEntity;
+import com.fr.third.org.apache.http.entity.mime.HttpMultipartMode;
+import com.fr.third.org.apache.http.entity.mime.MultipartEntityBuilder;
+
+import java.nio.charset.Charset;
+import java.util.*;
+
+/**
+ * @author fr.open
+ * @Date 2021/08/17
+ */
+public class OaOutPutHandle extends OutputActionHandler {
+
+ @Override
+ public void doAction(OaOutputAction output, Map map) throws Exception {
+ OutputSimpleConfig instance = OutputSimpleConfig.getInstance();
+ //Object aaa = getTitle(map);
+ if (!instance.isAccept()) {
+ FineLoggerFactory.getLogger().error("current task config is null");
+ return;
+ }
+ String tokenRes = HttpToolbox.get(instance.tokenUrl());
+ FineLoggerFactory.getLogger().info("get token res is {}", tokenRes);
+ JSONObject object = new JSONObject(tokenRes);
+ String token = object.getString("id");
+ String[] files = (String[]) map.get(ScheduleConstants.OUTPUT_FILES);
+ List ids = new ArrayList<>();
+ String attach = instance.attachUrl() + "?token=" + token;
+ String imageFile = StringUtils.EMPTY;
+ for (String file : files) {
+ MultipartEntityBuilder entity = MultipartEntityBuilder.create();
+ entity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+ entity.setCharset(Charset.forName("UTF-8"));
+ //内容类型,用于定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件
+ ContentType OCTEC_STREAM = ContentType.create("application/octet-stream", Charset.forName("UTF-8"));
+ //添加文件
+ entity.addBinaryBody("file", ResourceIOUtils.readBytes(file), OCTEC_STREAM, file.substring(file.lastIndexOf("/")+ 1));
+ String res = HttpToolbox.executeAndParse(HttpRequest.custom().url(attach).post(entity.build()).build());
+ FineLoggerFactory.getLogger().info("upload attach res is {}", res);
+ JSONObject attachRes = new JSONObject(res);
+ String fileUrl = attachRes.getJSONArray("atts").getJSONObject(0).getString("fileUrl");
+ if (file.endsWith(".png")) {
+ imageFile = fileUrl;
+ } else {
+ ids.add(fileUrl);
+ }
+ }
+
+ String formUrl = instance.formUrl() + "?token=" + token;
+ String[] usernames = (String[]) map.get(ScheduleConstants.USERNAMES);
+ String username = (String) map.get(ScheduleConstants.USERNAME);
+ Set users;
+ if (StringUtils.isNotBlank(username)) {
+ users = new HashSet<>();
+ users.add(username);
+ } else {
+ users = new HashSet(Arrays.asList(usernames));
+ }
+ Iterator it = users.iterator();
+ String tabledata = instance.getTabledata();
+ TableData tableData = TableDataConfig.getInstance().getTableData(tabledata);
+ DataModel dataModel = tableData.createDataModel(Calculator.createCalculator());
+ while (it.hasNext()) {
+ String next = it.next();
+ String dept = getDept(dataModel, next);
+ if (StringUtils.isBlank(dept)) {
+ FineLoggerFactory.getLogger().warn("current user {} has not dept", next);
+ continue;
+ }
+ JSONObject formBody = new JSONObject();
+ formBody.put("undertakeDept", dept).put("undertakeName", next)
+ .put("title",getTitle(map));
+ if (StringUtils.isNotBlank(imageFile)) {
+ if (output.getPushImg() == 0) {
+ formBody.put("imageFile", imageFile);
+ } else if (output.getPushImg() == 1) {
+ ids.add(imageFile);
+ formBody.put("attacFile", com.fr.third.org.apache.commons.lang3.StringUtils.join(ids, ","));
+ } else if (output.getPushImg() == 2) {
+ formBody.put("imageFile", imageFile);
+ ids.add(imageFile);
+ formBody.put("attacFile", com.fr.third.org.apache.commons.lang3.StringUtils.join(ids, ","));
+ }
+ }
+ StringEntity entity = new StringEntity(formBody.toString(),"UTF-8");
+ entity.setContentEncoding("UTF-8");
+ entity.setContentType("application/json; charset=UTF-8");
+ FineLoggerFactory.getLogger().info("send form url is{},body is {}", formUrl, formBody);
+ String res = HttpToolbox.executeAndParse(HttpRequest.custom().url(formUrl).post(entity).build());
+ FineLoggerFactory.getLogger().info("send form res is {}", res);
+ FineLoggerFactory.getLogger().info("execute finish");
+ }
+
+
+ }
+
+ private Object getTitle(Map map) {
+ List