forked from fanruan/demo-third-sms-v2
Roger.Chen
3 years ago
commit
2e3c918958
27 changed files with 881 additions and 0 deletions
@ -0,0 +1,5 @@ |
|||||||
|
*.iml |
||||||
|
.idea/ |
||||||
|
lib/report/*.jar |
||||||
|
target/ |
||||||
|
.DS_Store |
@ -0,0 +1,122 @@ |
|||||||
|
|
||||||
|
apply plugin: 'java' |
||||||
|
|
||||||
|
|
||||||
|
ext { |
||||||
|
/** |
||||||
|
* 项目中依赖的jar的路径 |
||||||
|
* 1.如果依赖的jar需要打包到zip中,放置在lib根目录下 |
||||||
|
* 2.如果依赖的jar仅仅是编译时需要,防止在lib下子目录下即可 |
||||||
|
*/ |
||||||
|
libPath = "$projectDir/../webroot/WEB-INF/lib" |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否对插件的class进行加密保护,防止反编译 |
||||||
|
*/ |
||||||
|
guard = false |
||||||
|
|
||||||
|
def pluginInfo = getPluginInfo() |
||||||
|
pluginPre = "fine-plugin" |
||||||
|
pluginName = pluginInfo.id |
||||||
|
pluginVersion = pluginInfo.version |
||||||
|
|
||||||
|
outputPath = "$projectDir/../webroot/WEB-INF/plugins/plugin-" + pluginName + "-1.0/classes" |
||||||
|
} |
||||||
|
|
||||||
|
group = 'com.fr.plugin' |
||||||
|
version = '10.0' |
||||||
|
sourceCompatibility = '8' |
||||||
|
|
||||||
|
sourceSets { |
||||||
|
main { |
||||||
|
java.outputDir = file(outputPath) |
||||||
|
output.resourcesDir = file(outputPath) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ant.importBuild("encrypt.xml") |
||||||
|
//定义ant变量 |
||||||
|
ant.projectDir = projectDir |
||||||
|
ant.references["compile.classpath"] = ant.path { |
||||||
|
fileset(dir: libPath, includes: '**/*.jar') |
||||||
|
fileset(dir: ".",includes:"**/*.jar" ) |
||||||
|
} |
||||||
|
|
||||||
|
classes.dependsOn('clean') |
||||||
|
|
||||||
|
task copyFiles(type: Copy,dependsOn: 'classes'){ |
||||||
|
from outputPath |
||||||
|
into "$projectDir/classes" |
||||||
|
} |
||||||
|
|
||||||
|
task preJar(type:Copy,dependsOn: guard ? 'compile_encrypt_javas' : 'compile_plain_javas'){ |
||||||
|
from "$projectDir/classes" |
||||||
|
into "$projectDir/transform-classes" |
||||||
|
include "**/*.*" |
||||||
|
} |
||||||
|
jar.dependsOn("preJar") |
||||||
|
|
||||||
|
task makeJar(type: Jar,dependsOn: preJar){ |
||||||
|
from fileTree(dir: "$projectDir/transform-classes") |
||||||
|
baseName pluginPre |
||||||
|
appendix pluginName |
||||||
|
version pluginVersion |
||||||
|
destinationDir = file("$buildDir/libs") |
||||||
|
|
||||||
|
doLast(){ |
||||||
|
delete file("$projectDir/classes") |
||||||
|
delete file("$projectDir/transform-classes") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
task copyFile(type: Copy,dependsOn: ["makeJar"]){ |
||||||
|
from "$buildDir/libs" |
||||||
|
from("$projectDir/lib") { |
||||||
|
include "*.jar" |
||||||
|
} |
||||||
|
from "$projectDir/plugin.xml" |
||||||
|
into file("$buildDir/temp/plugin") |
||||||
|
} |
||||||
|
|
||||||
|
task zip(type:Zip,dependsOn:["copyFile"]){ |
||||||
|
from "$buildDir/temp/plugin" |
||||||
|
destinationDir file("$buildDir/install") |
||||||
|
baseName pluginPre |
||||||
|
appendix pluginName |
||||||
|
version pluginVersion |
||||||
|
} |
||||||
|
|
||||||
|
//控制build时包含哪些文件,排除哪些文件 |
||||||
|
processResources { |
||||||
|
// exclude everything |
||||||
|
// 用*.css没效果 |
||||||
|
// exclude '**/*.css' |
||||||
|
// except this file |
||||||
|
// include 'xx.xml' |
||||||
|
} |
||||||
|
|
||||||
|
/*读取plugin.xml中的version*/ |
||||||
|
def getPluginInfo(){ |
||||||
|
def xmlFile = file("plugin.xml") |
||||||
|
if (!xmlFile.exists()) { |
||||||
|
return ["id":"none", "version":"1.0.0"] |
||||||
|
} |
||||||
|
def plugin = new XmlParser().parse(xmlFile) |
||||||
|
def version = plugin.version[0].text() |
||||||
|
def id = plugin.id[0].text() |
||||||
|
return ["id":id,"version":version] |
||||||
|
} |
||||||
|
|
||||||
|
repositories { |
||||||
|
mavenLocal() |
||||||
|
maven { |
||||||
|
url = uri('http://mvn.finedevelop.com/repository/maven-public/') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
//使用本地jar |
||||||
|
implementation fileTree(dir: 'lib', include: ['**/*.jar']) |
||||||
|
implementation fileTree(dir: libPath, include: ['**/*.jar']) |
||||||
|
} |
||||||
|
|
@ -0,0 +1,13 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||||
|
<project> |
||||||
|
<target name="compile_encrypt_javas" depends="copyFiles"> |
||||||
|
<echo message="加密文件"/> |
||||||
|
<echo message="${projectDir}"/> |
||||||
|
<taskdef name="pretreatment" classname="com.fr.plugin.pack.PluginPretreatmentTask"> |
||||||
|
<classpath refid="compile.classpath"/> |
||||||
|
</taskdef> |
||||||
|
<pretreatment baseDir="${projectDir}"/> |
||||||
|
</target> |
||||||
|
<target name="compile_plain_javas" depends="copyFiles"> |
||||||
|
</target> |
||||||
|
</project> |
@ -0,0 +1,20 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||||
|
<plugin> |
||||||
|
<id>com.fr.plugin.decision.third.sms.v11</id> |
||||||
|
<name><![CDATA[新第三方短信插件]]></name> |
||||||
|
<active>yes</active> |
||||||
|
<version>1.0</version> |
||||||
|
<env-version>11.0</env-version> |
||||||
|
<jartime>2019-11-06</jartime> |
||||||
|
<vendor>Lanlan</vendor> |
||||||
|
<description><![CDATA[新第三方短信插件]]></description> |
||||||
|
<extra-core> |
||||||
|
<SMSServiceProvider class="com.fr.plugin.decision.third.sms.ThirdSmsServiceProvider"/> |
||||||
|
<LocaleFinder class="com.fr.plugin.decision.third.sms.ThirdSmsLocaleFinder"/> |
||||||
|
</extra-core> |
||||||
|
<extra-decision> |
||||||
|
<ControllerRegisterProvider class="com.fr.plugin.decision.third.sms.ThirdSmsControllerProvider"/> |
||||||
|
<WebResourceProvider class="com.fr.plugin.decision.third.sms.ThirdSmsWebResourceProvider"/> |
||||||
|
</extra-decision> |
||||||
|
<function-recorder class="com.fr.plugin.decision.third.sms.ThirdSmsServiceProvider"/> |
||||||
|
</plugin> |
@ -0,0 +1,45 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<parent> |
||||||
|
<artifactId>plugin</artifactId> |
||||||
|
<groupId>com.fr.maven</groupId> |
||||||
|
<version>10.0</version> |
||||||
|
</parent> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<artifactId>fine-decision-sms-new-demo</artifactId> |
||||||
|
|
||||||
|
<build> |
||||||
|
<!---如果要更改调试插件,改这里的配置就可以了--> |
||||||
|
<outputDirectory> |
||||||
|
${web-inf-path}/plugins/plugin-com.fr.plugin.decision.third.sms.v11-2.0/classes |
||||||
|
</outputDirectory> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||||
|
<configuration> |
||||||
|
<source>8</source> |
||||||
|
<target>8</target> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>com.fr.core</groupId> |
||||||
|
<artifactId>core-plugin-interface</artifactId> |
||||||
|
<version>10.0</version> |
||||||
|
<scope>compile</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>com.fr.core</groupId> |
||||||
|
<artifactId>core-project-plugin-interface</artifactId> |
||||||
|
<version>10.0</version> |
||||||
|
<scope>compile</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,28 @@ |
|||||||
|
# 第三方短信平台插件 |
||||||
|
|
||||||
|
## 开启短信 |
||||||
|
|
||||||
|
使用该插件可以不使用帆软内置的短信平台,只需要在购买短信功能点后,通过插件使用自己的短信平台即可,为了方便展示客户自己的账号信息,可以自己在插件中提供短信配置信息的接口,如下图所示: |
||||||
|
![open](screenshots/open.png) |
||||||
|
|
||||||
|
给管理员绑定手机号: |
||||||
|
![open2](screenshots/open2.png) |
||||||
|
|
||||||
|
绑定好后如图: |
||||||
|
![open3](screenshots/open3.png) |
||||||
|
|
||||||
|
开启短信验证密码: |
||||||
|
![open4](screenshots/open4.png) |
||||||
|
|
||||||
|
## 测试方式 |
||||||
|
|
||||||
|
可以在登录决策平台后,访问管理系统->系统管理->短信,点击信息测试处的发送测试短信。 |
||||||
|
|
||||||
|
## 测试时获取验证码 |
||||||
|
|
||||||
|
可以直接在日志中看到要短信发送的内容。 |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 242 KiB |
@ -0,0 +1,16 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms; |
||||||
|
|
||||||
|
import com.fr.web.struct.Component; |
||||||
|
import com.fr.web.struct.browser.RequestClient; |
||||||
|
import com.fr.web.struct.category.ParserType; |
||||||
|
import com.fr.web.struct.category.ScriptPath; |
||||||
|
|
||||||
|
public class ThirdSmsComponent extends Component { |
||||||
|
|
||||||
|
public static final ThirdSmsComponent KEY = new ThirdSmsComponent(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public ScriptPath script(RequestClient req) { |
||||||
|
return ScriptPath.build("com/fr/plugin/decision/third/sms/web/sms.js", ParserType.PLAIN); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms; |
||||||
|
|
||||||
|
import com.fr.decision.fun.impl.AbstractControllerRegisterProvider; |
||||||
|
import com.fr.plugin.decision.third.sms.controller.ThirdSmsController; |
||||||
|
|
||||||
|
public class ThirdSmsControllerProvider extends AbstractControllerRegisterProvider { |
||||||
|
@Override |
||||||
|
public Class<?>[] getControllers() { |
||||||
|
return new Class[] { |
||||||
|
ThirdSmsController.class |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms; |
||||||
|
|
||||||
|
import com.fr.stable.fun.impl.AbstractLocaleFinder; |
||||||
|
|
||||||
|
public class ThirdSmsLocaleFinder extends AbstractLocaleFinder { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String find() { |
||||||
|
return "com/fr/plugin/decision/third/sms/locale/sms"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms; |
||||||
|
|
||||||
|
import com.fr.base.sms.SMSContext; |
||||||
|
import com.fr.json.JSONArray; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.plugin.decision.third.sms.fun.SMSXmlReader; |
||||||
|
import com.fr.plugin.decision.third.sms.fun.SmsClient; |
||||||
|
import com.fr.plugin.transform.ExecuteFunctionRecord; |
||||||
|
import com.fr.plugin.transform.FunctionRecorder; |
||||||
|
import com.fr.stable.fun.impl.AbstractSMSServiceProvider; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
@FunctionRecorder |
||||||
|
public class ThirdSmsServiceProvider extends AbstractSMSServiceProvider { |
||||||
|
|
||||||
|
private static final String SMS_PUBLIC_MODEL = "public-model"; |
||||||
|
private static final String SMS_PRIVATE_MODEL = "private-model"; |
||||||
|
|
||||||
|
public ThirdSmsServiceProvider() { |
||||||
|
startListener(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String sign() { |
||||||
|
return SmsClient.getInstance().getSign(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 必须大于1,且跟js文件中的优先级保持一致 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public int priority() { |
||||||
|
return 20; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isSMSAvailable() { |
||||||
|
//根据具体情况看短信服务是否可用
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 这里解析sms_template.xml文件中的模板返回,实际获取短信服务的模板 |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Map<String, List<SMSTemplateBean>> getSMSTemplate() { |
||||||
|
Map<String, List<SMSTemplateBean>> result = new HashMap<>(); |
||||||
|
Map<String, Map<String, Object>> smsTemplates = SmsClient.getInstance().getSMSTemplate(); |
||||||
|
result.put("publicModel", convertSMSTemplateBeanList(smsTemplates.get(SMS_PUBLIC_MODEL))); |
||||||
|
result.put("privateModel", convertSMSTemplateBeanList(smsTemplates.get(SMS_PRIVATE_MODEL))); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 读取sms_template_mapping.xml配置文件中的映射规则 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Map<String, String> mapping() { |
||||||
|
Map<String, String> map = new HashMap<>(); |
||||||
|
Map<String, Object> templateMappings = SMSXmlReader.getSMSTemplateMappings(); |
||||||
|
for (Map.Entry<String, Object> entry : templateMappings.entrySet()) { |
||||||
|
JSONObject jsonObject = JSONObject.mapFrom(entry.getValue()); |
||||||
|
if (jsonObject == null) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
map.put(entry.getKey(), jsonObject.getString("content")); |
||||||
|
} |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Response sendTest(String mobile) { |
||||||
|
SmsClient.getInstance().send(mobile); |
||||||
|
return Response.create(Response.RES_STATUS_SUCCESS, "新插件发送测试短信成功", JSONObject.create()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@ExecuteFunctionRecord |
||||||
|
public Response send(String template, String mobile, JSONObject para, String receiver) throws Exception { |
||||||
|
SmsClient.getInstance().send(template, mobile, para, receiver); |
||||||
|
FineLoggerFactory.getLogger().info("短信模板:{}, 手机号:{},参数:{}", template, mobile, para); |
||||||
|
return Response.create(Response.RES_STATUS_SUCCESS, "新插件发送普通短信成功", JSONObject.create()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Response batchSendSMS(String template, List<String> mobiles, JSONArray params, List<String> receivers) throws Exception { |
||||||
|
SmsClient.getInstance().batchSend(template, mobiles, params, receivers); |
||||||
|
return Response.create(Response.RES_STATUS_SUCCESS, "新插件发送批量短信成功", JSONObject.create()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动发送短信的监听器 |
||||||
|
*/ |
||||||
|
private void startListener() { |
||||||
|
SMSContext.addSmsListener(new Listener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void beforeSend(String text, List<String> mobiles, JSONArray params, List<String> receivers) { |
||||||
|
System.out.println("发送短信前监听事件"); |
||||||
|
FineLoggerFactory.getLogger().info("发送短信前监听事件"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void afterSend(String text, List<String> mobiles, JSONArray params, List<String> receivers, Response response) { |
||||||
|
System.out.println("发送短信后监听事件"); |
||||||
|
FineLoggerFactory.getLogger().info("发送短信后监听事件"); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private List<SMSTemplateBean> convertSMSTemplateBeanList(Map<String, Object> map) { |
||||||
|
List<SMSTemplateBean> beanList = new ArrayList<>(); |
||||||
|
for (Object data : map.values()) { |
||||||
|
JSONObject json = JSONObject.mapFrom(data); |
||||||
|
if (json == null) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
beanList.add(SMSTemplateBean.create(json.getString("id"), json.getString("content"), json.getString("language"))); |
||||||
|
} |
||||||
|
return beanList; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms; |
||||||
|
|
||||||
|
import com.fr.decision.fun.impl.AbstractWebResourceProvider; |
||||||
|
import com.fr.decision.web.MainComponent; |
||||||
|
import com.fr.web.struct.Atom; |
||||||
|
|
||||||
|
public class ThirdSmsWebResourceProvider extends AbstractWebResourceProvider { |
||||||
|
@Override |
||||||
|
public Atom attach() { |
||||||
|
return MainComponent.KEY; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Atom client() { |
||||||
|
return ThirdSmsComponent.KEY; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms.controller; |
||||||
|
|
||||||
|
import com.fr.decision.webservice.Response; |
||||||
|
import com.fr.decision.webservice.annotation.VisitRefer; |
||||||
|
import com.fr.plugin.decision.third.sms.fun.SmsClient; |
||||||
|
import com.fr.third.springframework.stereotype.Controller; |
||||||
|
import com.fr.third.springframework.web.bind.annotation.PathVariable; |
||||||
|
import com.fr.third.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import com.fr.third.springframework.web.bind.annotation.RequestMethod; |
||||||
|
import com.fr.third.springframework.web.bind.annotation.ResponseBody; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
|
||||||
|
@Controller |
||||||
|
@RequestMapping("/{version}/third/sms") |
||||||
|
public class ThirdSmsController { |
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.GET) |
||||||
|
@ResponseBody |
||||||
|
@VisitRefer(required = false) |
||||||
|
public Response getSMSConfig(HttpServletRequest req, |
||||||
|
HttpServletResponse res, |
||||||
|
@PathVariable("version") String version) throws Exception { |
||||||
|
|
||||||
|
return Response.ok(SmsClient.getInstance().getSmsConfig()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms.fun; |
||||||
|
|
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import org.w3c.dom.Document; |
||||||
|
import org.w3c.dom.Node; |
||||||
|
import org.w3c.dom.NodeList; |
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder; |
||||||
|
import javax.xml.parsers.DocumentBuilderFactory; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
public class SMSXmlReader { |
||||||
|
|
||||||
|
private static final Map<String, Map<String, Object>> SMS_TEMPLATES_MAP = new ConcurrentHashMap<>(); |
||||||
|
private static final String SMS_PUBLIC_MODEL = "public-model"; |
||||||
|
private static final String SMS_PRIVATE_MODEL = "private-model"; |
||||||
|
|
||||||
|
private static final String SMS_TEMPLATE_XML = "sms_template.xml"; |
||||||
|
private static final String SMS_TEMPLATE_MAPPING_XML = "sms_template_mapping.xml"; |
||||||
|
|
||||||
|
public static Map<String, Object> getPublicSMSTemplates() { |
||||||
|
return getSMSTemplates(SMS_TEMPLATE_XML, SMS_PUBLIC_MODEL); |
||||||
|
} |
||||||
|
|
||||||
|
public static Map<String, Object> getPrivateSMSTemplates() { |
||||||
|
return getSMSTemplates(SMS_TEMPLATE_XML, SMS_PRIVATE_MODEL); |
||||||
|
} |
||||||
|
|
||||||
|
public static Map<String, Object> getSMSTemplateMappings() { |
||||||
|
return getSMSTemplates(SMS_TEMPLATE_MAPPING_XML, SMS_PUBLIC_MODEL); |
||||||
|
} |
||||||
|
|
||||||
|
public static Map<String, Object> getSMSTemplates(String xmlPath, String type) { |
||||||
|
if (!SMS_TEMPLATES_MAP.containsKey(type)) { |
||||||
|
synchronized (com.fr.base.sms.SMSXmlReader.class) { |
||||||
|
Map<String, Object> map = new ConcurrentHashMap<>(); |
||||||
|
readXML(map, xmlPath, type); |
||||||
|
if (!map.isEmpty()) { |
||||||
|
SMS_TEMPLATES_MAP.put(type, map); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return SMS_TEMPLATES_MAP.get(type); |
||||||
|
} |
||||||
|
|
||||||
|
private static void readXML(Map<String, Object> map, String xmlFile, String nodeName) { |
||||||
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
||||||
|
String xmlPath = "com/fr/plugin/decision/third/sms/xml/".concat(xmlFile); |
||||||
|
try { |
||||||
|
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
||||||
|
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); |
||||||
|
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
||||||
|
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); |
||||||
|
dbf.setXIncludeAware(false); |
||||||
|
DocumentBuilder db = dbf.newDocumentBuilder(); |
||||||
|
Document document = db.parse(SMSXmlReader.class.getClassLoader().getResourceAsStream(xmlPath)); |
||||||
|
NodeList bookList = document.getElementsByTagName(nodeName); |
||||||
|
for (int i = 0; i < bookList.getLength(); i++) { |
||||||
|
Node node = bookList.item(i); |
||||||
|
String id = node.getAttributes().getNamedItem("id").getTextContent(); |
||||||
|
JSONObject jsonObject = JSONObject.create(); |
||||||
|
jsonObject.put("id", id); |
||||||
|
jsonObject.put("content", node.getAttributes().getNamedItem("content").getTextContent()); |
||||||
|
jsonObject.put("language", node.getAttributes().getNamedItem("language").getTextContent()); |
||||||
|
map.put(id, jsonObject); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
package com.fr.plugin.decision.third.sms.fun; |
||||||
|
|
||||||
|
import com.fr.base.sms.SMSTemplateType; |
||||||
|
import com.fr.json.JSONArray; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.fun.SMSServiceProvider; |
||||||
|
import org.w3c.dom.Document; |
||||||
|
import org.w3c.dom.Node; |
||||||
|
import org.w3c.dom.NodeList; |
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder; |
||||||
|
import javax.xml.parsers.DocumentBuilderFactory; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author richie |
||||||
|
* @version 10.0 |
||||||
|
* Created by richie on 2019/11/8 |
||||||
|
* 这个是短信发送的客户端,调用各家实际短信发送平台,这里只通过输出日志的方式提供一个示例 |
||||||
|
*/ |
||||||
|
public class SmsClient { |
||||||
|
|
||||||
|
private static final SmsClient instance = new SmsClient(); |
||||||
|
private static final String SMS_PUBLIC_MODEL = "public-model"; |
||||||
|
private static final String SMS_PRIVATE_MODEL = "private-model"; |
||||||
|
|
||||||
|
public static SmsClient getInstance() { |
||||||
|
return instance; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSign() { |
||||||
|
return "第三方短信"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取短信配置信息 |
||||||
|
* |
||||||
|
* @return 短信配置信息 |
||||||
|
*/ |
||||||
|
public Map<String, Object> getSmsConfig() { |
||||||
|
Map<String, Object> result = new HashMap<>(); |
||||||
|
result.put("username", "优先级20"); |
||||||
|
result.put("balance", 102.20); |
||||||
|
result.put("isSmsAvailable", true); |
||||||
|
result.put("accountType", 1); |
||||||
|
result.put("publicModel", JSONArray.create(SMSXmlReader.getPublicSMSTemplates())); |
||||||
|
result.put("privateModel", JSONArray.create(SMSXmlReader.getPrivateSMSTemplates())); |
||||||
|
result.put("sign", getSign()); |
||||||
|
|
||||||
|
initSmsModels(result, "publicModel"); |
||||||
|
initSmsModels(result, "privateModel"); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean send(String mobile) { |
||||||
|
System.out.println("新插件(20)发送测试短信成功"); |
||||||
|
FineLoggerFactory.getLogger().info("新插件(20)发送测试短信成功"); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean send(String template, String mobile, JSONObject para, String receiver) { |
||||||
|
System.out.println("新插件(20)发送普通短信成功"); |
||||||
|
FineLoggerFactory.getLogger().info("新插件(20)发送普通短信成功"); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean batchSend(String template, List<String> mobiles, JSONArray params, List<String> receivers) { |
||||||
|
System.out.println("新插件(20)发送批量短信成功"); |
||||||
|
FineLoggerFactory.getLogger().info("新插件(20)发送批量短信成功"); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private void initSmsModels(Map<String, Object> result, String key) { |
||||||
|
if (result.containsKey(key)) { |
||||||
|
List<Map<String, Object>> smsModels = new ArrayList<Map<String, Object>>(); |
||||||
|
JSONArray ja = (JSONArray) result.get(key); |
||||||
|
for (int i = 0; i < ja.length(); i++) { |
||||||
|
JSONObject jo = ja.optJSONObject(i); |
||||||
|
Map<String, Object> map = new HashMap<String, Object>(); |
||||||
|
String language = jo.optString("language"); |
||||||
|
if (StringUtils.isNotEmpty(language) && StringUtils.equals(key, "publicModel") && !StringUtils.equals(language, SMSTemplateType.MAINLAND_SMS_TEMPLATE.getLanguage())) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
map.put("id", jo.optString("id")); |
||||||
|
map.put("content", jo.optString("content")); |
||||||
|
map.put("language", language); |
||||||
|
smsModels.add(map); |
||||||
|
} |
||||||
|
result.put(key, smsModels); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, Map<String, Object>> getSMSTemplate() { |
||||||
|
Map<String, Map<String, Object>> result = new HashMap<>(); |
||||||
|
result.put(SMS_PUBLIC_MODEL, SMSXmlReader.getPublicSMSTemplates()); |
||||||
|
result.put(SMS_PRIVATE_MODEL, SMSXmlReader.getPrivateSMSTemplates()); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,236 @@ |
|||||||
|
// 邮箱配置组件的注册过程一共分为4步:
|
||||||
|
|
||||||
|
// 1.基础配置
|
||||||
|
var PRIORITY = 20; // 优先级(需要跟ThirdSmsServiceProvider#priority方法返回值一致),必须大于1,当有多个组件注册时,只有优先级最高的组件才会起作用
|
||||||
|
var WIDGET_TYPE = 'demo.system.sms.config'; // 组件名称
|
||||||
|
var MODEL_TYPE = 'demo.model.system.sms.config'; // 模型名称
|
||||||
|
|
||||||
|
// 2.定义组件
|
||||||
|
var LAYOUT = { |
||||||
|
ITEM_HEIGHT: 24, |
||||||
|
ITEM_GAP: 15, |
||||||
|
LABEL_WIDTH: 120, |
||||||
|
}; |
||||||
|
var Widget = BI.inherit(BI.Pane, { |
||||||
|
props: { |
||||||
|
baseCls: 'demo-system-sms-config', |
||||||
|
}, |
||||||
|
|
||||||
|
_store: function () { |
||||||
|
return BI.Models.getModel(MODEL_TYPE); |
||||||
|
}, |
||||||
|
|
||||||
|
watch: { |
||||||
|
'config.balance': function (value) { |
||||||
|
this.balanceLabel.setText(config.balance + BI.i18nText('Dec-Basic_RMB')); |
||||||
|
this.testButton.setEnable(value > 0); |
||||||
|
}, |
||||||
|
}, |
||||||
|
|
||||||
|
beforeRender: function (callback) { |
||||||
|
var self = this; |
||||||
|
self.loading(); |
||||||
|
this.store.requestGetConfig(function () { |
||||||
|
callback(); |
||||||
|
self.loaded(); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
render: function () { |
||||||
|
var self = this; |
||||||
|
var config = self.model.config; |
||||||
|
|
||||||
|
return { |
||||||
|
type: 'bi.vertical', |
||||||
|
bgap: LAYOUT.ITEM_GAP, |
||||||
|
items: [ |
||||||
|
// 账号绑定
|
||||||
|
{ |
||||||
|
type: 'bi.vertical_adapt', |
||||||
|
height: LAYOUT.ITEM_HEIGHT, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: 'bi.label', |
||||||
|
cls: 'dec-font-weight-bold', |
||||||
|
width: LAYOUT.LABEL_WIDTH, |
||||||
|
text: BI.i18nText('Dec-System_SMS_Account_Bind'), |
||||||
|
textAlign: 'left', |
||||||
|
title: BI.i18nText('Dec-System_SMS_Account_Bind'), |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'bi.label', |
||||||
|
text: config.username, |
||||||
|
textAlign: 'left', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
// 账号余额
|
||||||
|
{ |
||||||
|
type: 'bi.vertical_adapt', |
||||||
|
height: LAYOUT.ITEM_HEIGHT, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: 'bi.label', |
||||||
|
cls: 'dec-font-weight-bold', |
||||||
|
width: LAYOUT.LABEL_WIDTH, |
||||||
|
text: BI.i18nText('Dec-System_SMS_Account_Balance'), |
||||||
|
textAlign: 'left', |
||||||
|
title: BI.i18nText('Dec-System_SMS_Account_Balance'), |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'bi.label', |
||||||
|
ref: function (_ref) { |
||||||
|
self.balanceLabel = _ref; |
||||||
|
}, |
||||||
|
text: config.balance + BI.i18nText('Dec-Basic_RMB'), |
||||||
|
textAlign: 'left', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
// 短信签名
|
||||||
|
{ |
||||||
|
type: 'bi.vertical_adapt', |
||||||
|
height: LAYOUT.ITEM_HEIGHT, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: 'bi.label', |
||||||
|
cls: 'dec-font-weight-bold', |
||||||
|
width: LAYOUT.LABEL_WIDTH, |
||||||
|
text: BI.i18nText('Dec-System_SMS_Signatures'), |
||||||
|
textAlign: 'left', |
||||||
|
title: BI.i18nText('Dec-System_SMS_Signatures'), |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'bi.label', |
||||||
|
text: config.sign, |
||||||
|
textAlign: 'left', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
// 信息测试
|
||||||
|
{ |
||||||
|
type: 'bi.vertical_adapt', |
||||||
|
height: LAYOUT.ITEM_HEIGHT, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: 'bi.label', |
||||||
|
cls: 'dec-font-weight-bold', |
||||||
|
width: LAYOUT.LABEL_WIDTH, |
||||||
|
text: BI.i18nText('Dec-System_SMS_Test'), |
||||||
|
textAlign: 'left', |
||||||
|
title: BI.i18nText('Dec-System_SMS_Test'), |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'bi.button', |
||||||
|
ref: function (_ref) { |
||||||
|
self.testButton = _ref; |
||||||
|
}, |
||||||
|
text: BI.i18nText('Dec-System_Send_Test_SMS'), |
||||||
|
textAlign: 'left', |
||||||
|
clear: true, |
||||||
|
disabled: config.balance <= 0, |
||||||
|
handler: function () { |
||||||
|
self._sendTestMessage(); |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送测试短信 |
||||||
|
*/ |
||||||
|
_sendTestMessage: function () { |
||||||
|
var self = this; |
||||||
|
var popoverName = BI.UUID(); |
||||||
|
BI.Popovers.create(popoverName, { |
||||||
|
type: 'bi.popover', |
||||||
|
width: 450, |
||||||
|
height: 220, |
||||||
|
header: BI.i18nText('Dec-System_Send_Test_Sms'), |
||||||
|
body: { |
||||||
|
type: 'dec.send.test.msg.dialog', |
||||||
|
listeners: [ |
||||||
|
{ |
||||||
|
eventName: BI.Popover.EVENT_CONFIRM, |
||||||
|
action: function (phone) { |
||||||
|
self.store.requsetSendTestMessage(phone); |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
eventName: BI.Popover.EVENT_CLOSE, |
||||||
|
action: function () { |
||||||
|
BI.Popovers.remove(popoverName); |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}); |
||||||
|
BI.Popovers.show(popoverName); |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
// 3.定义模型
|
||||||
|
var Model = BI.inherit(Fix.Model, { |
||||||
|
state: function () { |
||||||
|
return { |
||||||
|
config: { |
||||||
|
isSmsAvailable: true, |
||||||
|
accountType: 1, |
||||||
|
username: '', |
||||||
|
balance: 0, |
||||||
|
sign: '', |
||||||
|
publicModel: [], |
||||||
|
privateModel: [], |
||||||
|
}, |
||||||
|
}; |
||||||
|
}, |
||||||
|
|
||||||
|
actions: { |
||||||
|
/** |
||||||
|
* 获取邮箱配置 |
||||||
|
* @param {Function} callback beforeRender的回调 |
||||||
|
*/ |
||||||
|
requestGetConfig: function (callback) { |
||||||
|
var self = this; |
||||||
|
// 发送请求,获取配置
|
||||||
|
Dec.reqGetHandle('/v10/third/sms', '', function (data) { |
||||||
|
self.model.config = data; |
||||||
|
BI.isFunction(callback) && callback(); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送测试邮件 |
||||||
|
* @param {string} phone 收件人号码 |
||||||
|
*/ |
||||||
|
requsetSendTestMessage: function (phone) { |
||||||
|
var self = this; |
||||||
|
Dec.reqPost( |
||||||
|
'/v10/config/sms/test', |
||||||
|
{ |
||||||
|
mobile: phone, |
||||||
|
}, |
||||||
|
function (result) { |
||||||
|
if (result && result.data && result.data.code === 'success') { |
||||||
|
BI.Msg.toast(BI.i18nText('Dec-Send_Success'), { level: 'success' }); |
||||||
|
} else { |
||||||
|
BI.Msg.toast(BI.i18nText('Dec-System_SMS_Send_Fail'), { |
||||||
|
level: 'error', |
||||||
|
}); |
||||||
|
} |
||||||
|
self.requestGetConfig(); |
||||||
|
} |
||||||
|
); |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
// 4.注册组件
|
||||||
|
BI.shortcut(WIDGET_TYPE, Widget); // 注册组件
|
||||||
|
BI.model(MODEL_TYPE, Model); // 注册模型
|
||||||
|
BI.config('dec.provider.system', function (provider) { |
||||||
|
provider.registerSmsConfigWidget(WIDGET_TYPE, PRIORITY); // 注册邮箱配置组件
|
||||||
|
}); |
@ -0,0 +1,10 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||||
|
<sms-template> |
||||||
|
<public-model id="10" content="【信息平台】尊敬的管理员,#taskname#于#time#运行失败,请知晓。" language="zh_CN"/> |
||||||
|
<public-model id="306" content="【信息平台】尊敬的管理員,#taskname#於#time#運行失敗,請知曉。" language="zh_TW"/> |
||||||
|
<public-model id="329" content="【信息平台】Dear administrator, #taskname# failed to run at #time#." language="en_US"/> |
||||||
|
<public-model id="1005" content="【信息平台】Dear administrator" language="en_US"/> |
||||||
|
|
||||||
|
<private-model id="101" content="【第三方短信】私人短信" language="zh_CN"/> |
||||||
|
<private-model id="102" content="【第三方短信】私人短信2" language="zh_CN"/> |
||||||
|
</sms-template> |
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||||
|
<sms-template> |
||||||
|
<public-model id="10" content="【我的短信映射】尊敬的管理员,#taskname#于#time#运行失败,请知晓。" language="zh_CN"/> |
||||||
|
<public-model id="306" content="【我的短信映射】尊敬的管理員,#taskname#於#time#運行失敗,請知曉。" language="zh_TW"/> |
||||||
|
<public-model id="329" content="【我的短信映射】Dear administrator, #taskname# failed to run at #time#." language="en_US"/> |
||||||
|
</sms-template> |
Loading…
Reference in new issue