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 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 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 list = (List) map.get(ScheduleConstants.RECORD_LIST); + Object title = StringUtils.EMPTY; + Object param = list.get(0).get("title"); + if(param instanceof Formula){ + Formula formula = (Formula)param; + title = formula.getResult(); + }else { + title = param; + } + if(title ==null || StringUtils.isBlank(title.toString())){ + title = (String) map.get(ScheduleConstants.TASK_NAME); + } + return title; + } + + private String getDept(DataModel dataModel, String username) { + try { + for (int i = 0; i < dataModel.getRowCount(); i++) { + Object valueAt = dataModel.getValueAt(i, 1); + if (valueAt != null && StringUtils.equals(String.valueOf(valueAt), username)) { + return String.valueOf(dataModel.getValueAt(i, 0)); + } + } + } catch (Exception e) { + + } + return StringUtils.EMPTY; + } + +} diff --git a/src/main/java/com/fr/plugin/schedule/oa/js/FileDef.java b/src/main/java/com/fr/plugin/schedule/oa/js/FileDef.java new file mode 100644 index 0000000..e17cda1 --- /dev/null +++ b/src/main/java/com/fr/plugin/schedule/oa/js/FileDef.java @@ -0,0 +1,54 @@ +package com.fr.plugin.schedule.oa.js; + +import com.fr.plugin.transform.ExecuteFunctionRecord; +import com.fr.web.struct.Component; +import com.fr.web.struct.Filter; +import com.fr.web.struct.browser.RequestClient; +import com.fr.web.struct.category.ScriptPath; +import com.fr.web.struct.category.StylePath; + +/** + * @author fr.open + * @Date 2021/08/17 + */ +public class FileDef extends Component { + public static final FileDef KEY = new FileDef(); + private FileDef(){} + /** + * 返回需要引入的JS脚本路径 + * @param client 请求客户端描述 + * @return JS脚本路径 + */ + @Override + public ScriptPath script(RequestClient client ) { + //如果不需要就直接返回 ScriptPath.EMPTY + return ScriptPath.build("com/fr/plugin/schedule/oa/theme.js"); + } + + /** + * 返回需要引入的CSS样式路径 + * @param client 请求客户端描述 + * @return CSS样式路径 + */ + @Override + public StylePath style(RequestClient client ) { + //如果不需要就直接返回 StylePath.EMPTY; + return StylePath.EMPTY; + } + + /** + * 通过给定的资源过滤器控制是否加载这个资源 + * @return 资源过滤器 + */ + @ExecuteFunctionRecord + @Override + public Filter filter() { + return new Filter(){ + @Override + public boolean accept() { + //任何情况下我们都在平台组件加载时加载我们的组件 + return true; + } + }; + } +} diff --git a/src/main/java/com/fr/plugin/schedule/oa/js/JSCSSBridge.java b/src/main/java/com/fr/plugin/schedule/oa/js/JSCSSBridge.java new file mode 100644 index 0000000..d60e58b --- /dev/null +++ b/src/main/java/com/fr/plugin/schedule/oa/js/JSCSSBridge.java @@ -0,0 +1,25 @@ +package com.fr.plugin.schedule.oa.js; + +import com.fr.decision.fun.impl.AbstractWebResourceProvider; +import com.fr.decision.web.MainComponent; +import com.fr.plugin.transform.FunctionRecorder; +import com.fr.web.struct.Atom; + +/** + * @author fr.open + * @Date 2021/08/17 + */ +@FunctionRecorder +public class JSCSSBridge extends AbstractWebResourceProvider { + @Override + public Atom attach() { + //在平台主组件加载时添加我们自己的组件 + return MainComponent.KEY; + } + + @Override + public Atom client() { + //我们自己要引入的组件 + return FileDef.KEY; + } +} diff --git a/src/main/resources/com/fr/plugin/schedule/oa/locale/conf.properties b/src/main/resources/com/fr/plugin/schedule/oa/locale/conf.properties new file mode 100644 index 0000000..34eba19 --- /dev/null +++ b/src/main/resources/com/fr/plugin/schedule/oa/locale/conf.properties @@ -0,0 +1,11 @@ +Plugin-Config_Output=Plugin Schedule +Plugin-Config_Property_sfUrl=ip:port +Plugin-Config_Property_sfUrl_Description=ip port info example is http://xxxx:xxx +Plugin-Config_Property_username=username +Plugin-Config_Property_username_Description=get token username +Plugin-Config_Property_password=password +Plugin-Config_Property_password_Description=get token password +Plugin-Config_Property_account=get token account +Plugin-Config_Property_account_Description=account +Plugin-Config_Property_tabledata=user tabledata +Plugin-Config_Property_tabledata_Description=user tabledata \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/schedule/oa/locale/conf_zh_CN.properties b/src/main/resources/com/fr/plugin/schedule/oa/locale/conf_zh_CN.properties new file mode 100644 index 0000000..81c5f13 --- /dev/null +++ b/src/main/resources/com/fr/plugin/schedule/oa/locale/conf_zh_CN.properties @@ -0,0 +1,11 @@ +Plugin-Config_Output=\u5B9A\u65F6\u8C03\u5EA6\u63A8\u9001 +Plugin-Config_Property_sfUrl=\u5730\u5740\u4FE1\u606F +Plugin-Config_Property_sfUrl_Description=\u63A8\u9001\u5730\u5740\u7AEF\u53E3\u4FE1\u606F \u4F8B\u5982http://xxx:xxx +Plugin-Config_Property_username=\u8D26\u53F7 +Plugin-Config_Property_username_Description=\u83B7\u53D6token\u7684\u8D26\u53F7 +Plugin-Config_Property_password=\u5BC6\u7801 +Plugin-Config_Property_password_Description=\u83B7\u53D6token\u7684\u5BC6\u7801 +Plugin-Config_Property_account=\u767B\u5F55\u540D +Plugin-Config_Property_account_Description=\u83B7\u53D6token\u7684\u767B\u5F55\u540D +Plugin-Config_Property_tabledata=\u6620\u5C04\u6570\u636E\u96C6 +Plugin-Config_Property_tabledata_Description=\u6620\u5C04\u7528\u6237\u5173\u7CFB\u6570\u636E\u96C6 \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/schedule/oa/theme.js b/src/main/resources/com/fr/plugin/schedule/oa/theme.js new file mode 100644 index 0000000..a785d83 --- /dev/null +++ b/src/main/resources/com/fr/plugin/schedule/oa/theme.js @@ -0,0 +1,71 @@ +!(function () { + BI.config("dec.provider.schedule", function (provider) { + provider.registerTaskAttached({ + value: 64, + text: "png" + }, [DecCst.Schedule.TaskType.REPORT, DecCst.Schedule.TaskType.BI]); + }); + + var Plugin = BI.inherit(BI.Widget, { + props: { + baseCls: "", + value: { + pushImg: 0, + } + }, + render: function () { + var t = this, + e = this.options.value[0]==undefined?{pushImg: 0}: this.options.value[0]; + return { + type: "bi.flex_vertical", + tgap: 15, + items: [{ + type: "dec.label.editor.item", + errorTop: 16, + textCls: "dec-font-weight-bold", + text: "pushImg", + textWidth: 115, + editorWidth: 300, + value: e.pushImg, + ref: function(e) { + t.pushImg = e + } + }] + }; + }, + /** + * + * + * @returns {boolean} + */ + validation: function () { + var e = !0, + t = this.isVisible(); + return BI.isEmpty(this.pushImg.getValue()) && (t && this.pushImg.showError(BI.i18nText("Dec-Error_Null")), e = !1), + e + }, + /** + * + * outputActionList + * @returns {{}} + */ + getValue: function() { + var _self= this; + return {OutputWebHook: BI.extend(_self.value,{ + "@class": "com.fr.plugin.schedule.oa.entity.OaOutputAction", + actionName: "com.fr.plugin.schedule.oa.entity.OaOutputAction", + pushImg: _self.pushImg.getValue(), + })} + }, + }); + BI.shortcut("dec.schedule.task.file.handling.plugin", Plugin); + + BI.config("dec.provider.schedule", function (provider) { + provider.registerHandingWay({ + text: "oa推送", + value: "com.fr.plugin.schedule.oa.entity.OaOutputAction", // actionName + cardType: "dec.schedule.task.file.handling.plugin", + actions: [] // action + }, [DecCst.Schedule.TaskType.DEFAULT, DecCst.Schedule.TaskType.REPORT, DecCst.Schedule.TaskType.BI]); + }); +}());