如何开发短信接口?
李小翔
2015-08-08 15:24

内容列表

供稿人：刘少林
现在的越来越多的项目都有发送短信的需求，FWK 中内置有短信发送的功能模块，开发中需要根据短信提供商的接口或短信网关接口，实现相应的短信服务即可。
首先，在应用中新建一个新的 ShortMessageService 服务类，并继承 bropen.framework.plugins.message.ShortMessageService，模板如下：
import bropen.framework.plugins.message.ShortMessage
import bropen.framework.plugins.message.MessageStatus
/**
 * XXX 短信服务
 */
class ShortMessageService extends bropen.framework.plugins.message.ShortMessageService { 
   static aliasOverridingOrder = bropen.framework.plugins.message.ShortMessageService.aliasOverridingOrder + 1
   static bootStrapInit() { 
       // 一些初始化配置可以放到这里
    } 
   
   @java.lang.Override
   protected MessageStatus send(ShortMessage sms) { 
       // 调用短信接口，发送短信，并且返回状态
        ...
       return MessageStatus.SENT
   } 
   
   @java.lang.Override
   protected ShortMessage bind(Map msg) { 
       // 将一个 Map 转换为 ShortMessage 对象
    } 
   
} 
上面的模板中，bootStrapInit 方法用于初始化一些系统参数，如系统参数：
bropen.framework.plugins.message.sms.enabled：是否启用短信，默认为否
bropen.framework.plugins.message.sms.http.url：第三方短信服务的地址
bropen.framework.plugins.message.sms.attempt.max：短信发送失败的最大重试次数，默认为 5
bropen.framework.plugins.message.sms.attempt.interval：短信发送失败的重试间隔，默认为 5 分钟
此外，还可以根据第三方短信服务的接口，自定义其他系统参数，如下例所示：
static bootStrapInit() { 
   if ( !settingService.exists(”bropen.framework.plugins.message.sms.http.sn”) ) { 
       // 短信服务地址
        settingService.createOrUpdate(”bropen.framework.plugins.message.sms.http.url”,
           ”http://www.jianzhou.sh.cn/JianzhouSMSWSServer/services/BusinessService”, ”string”).save()
       // 短信服务帐号
        settingService.createOrUpdate(”bropen.framework.plugins.message.sms.http.sn”,
           ”sdk_foobar”, ”string”).save()
       // 短信服务密码
        settingService.createOrUpdate(”bropen.framework.plugins.message.sms.http.pwd”,
           ”1234567890”, ”passwd”).save()
       // 短信服务开关
        settingService.createOrUpdate(”bropen.framework.plugins.message.sms.enabled”,
           ”false”, ”boolean”).save()
       // 短信服务帐号
        settingService.createOrUpdate(”bropen.framework.plugins.message.sms.http.sig”,
           ”【Foobar】”, ”string”).save()
   } 
} 
接口方法 send 是最重要的部分，该方法中，需要调用实际的第三方接口，将短信发送出去，如下例所示：
/**
 * 接口方法：通过短信网关发送短信，并返回状态。如果短信发送被禁用，则应直接返回 null。
 */
@java.lang.Override
protected MessageStatus send(ShortMessage sms) { 
   if ( !isEnabled() ) return null // 如果没有启用短信服务，则直接返回null
    
    String url = settingService.get(”bropen.framework.plugins.message.sms.http.url”)        // 服务地址
    String account = settingService.get(”bropen.framework.plugins.message.sms.http.sn”)     // 企业账户
    String pwd = settingService.get(”bropen.framework.plugins.message.sms.http.pwd”)        // 密码
    String sig = settingService.get(”bropen.framework.plugins.message.sms.http.sig”) ?: ””  // 短信后缀
   // 收件人
    List<String> phones = []
   for ( String to in sms.to ) { 
       if ( to.indexOf(”<”) )
            phones += to.replaceAll(”^[^<]*<”, ””).replaceAll(”>.*\ $”, ””)
   } 
   // 拼消息实体：接收人、短信内容
    String phone = phones.join(”;”)
    String message = sms.text + (sms.signature ? (”\ n”+ sms.signature) : ””)
    message += sig
   
   // 发送 (开发和测试环境不发短信)
    String resp = null
   if ( grails.util.Environment.current == grails.util.Environment.PRODUCTION ) { 
        Client c = new Client(url,account, pwd)
        resp = c.sendBatchMessage(phone, message)
        log.debug(”sms.id=” + sms.id + ”, resp=” + resp)
        resp = resp.replaceAll(/,.+/, ””)
   }  else { 
        resp = ’999999999’
        log.debug(”sms.id=” + sms.id + ”, resp=” + resp + ”, phone=” + phone + ”, message=” + message)
   } 
   
   // 检查返回值（服务反馈的状态码），并设置 sms 对象的 statusCode、statusMessage，以便审计和查找发送失败原因
    sms.statusCode = resp
   switch ( sms.statusCode ) { 
       case ”-1” : sms.statusMessage=”余额不足”; break
       case ”-2” : sms.statusMessage=”帐号或密码错误”; break
       ...
       case ”-19” : sms.statusMessage=”必须为POST提交”; break
       case ”-20” : sms.statusMessage=”超速提交(一般为每秒一次提交)”; break
       default: sms.statusMessage = sms.statusCode
   } 
   // 返回状态
    return sms.statusCode.toLong() > 0 ? MessageStatus.SENT : MessageStatus.ERROR
} 
/**
 * 从第三方接口的 demo 代码中拷贝出来的短信客户端
 */
public static class Client { 
   ....
} 
上面的例子中，注意：
第一行判断是否启用了短信，否则直接返回 null
调用短信客户端类发送完成后，可以设置 sms 对象的 statusCode、statusMessage 属性，以便审计和查找发送失败原因
此外，上面的例子中有一个内部类 Client，一般可以从短信服务商提供的 java 例子中找到，简单修改即可，如：
public static class Client { 
   
   // webservice服务器定义
    private String serviceURL = null;        // 入口地址：http://www.jianzhou.sh.cn/JianzhouSMSWSServer/http/sendBatchMessage
    private String account    = null;        // 账户
    private String pwd        = null;        // 密码
    
   /*
     * 构造函数
     */
   public Client(String url, String account, String pwd) throws UnsupportedEncodingException { 
       this.serviceURL = url;
       this.account = account;
       this.pwd = pwd;
   } 
   
   /*
     * 方法名称：sendBatchMessage
     * 功    能：发送个性短信
     * 参    数：mobile,content(手机号，内容)
     * 返 回 值：唯一标识，如果不填写rrid将返回系统生成的
     */
   public String sendBatchMessage(String mobile, String content) { 
       ....
   } 
   
} 
接口方法 bind 用来将 Map 对象转换成一个 ShortMessage 对象，默认情况下，Map 中包含下列数据（其中 from、to、text 不能为空）：
from：发送人名称/员工或用户对象
to：逗号或分号分隔的手机号、或手机号列表、员工列表
text：消息正文
signature：签名
sentDateSche：计划发送时间，空则立即发送
如有其他需求，可以覆盖 bind 方法实现自己的转换逻辑。
最后，接口开发完成后，可以调用 ShortMessageService 的 sendMessage 方法发送短信，如：
ctx.shortMessageService.flush = true    // 如果在 console 中执行，必须加上这句；否则不需要
// 发送一条新的短信
ctx.shortMessageService.sendMessage( [from:”abc”, to:”13312345678”, text:”test text”] )
// 重试一条老的短信
def sms = bropen.framework.plugins.message.ShortMessage.get(xxxx)
ctx.shortMessageService.sendMessage( sms )
如上例所示，sendMessage 方法有两个版本，一个版本的参数是 Map、一个版本的参数是 ShortMessage 对象，通常情况下我们使用 Map 来传递参数（新建一条短信并发送），而 Map 中的数据规范参见上文中 bind 方法的默认情况。
此外，启用短信配置、并实现短信接口后，用管理员登录到后台，可以查看短信发送情况，并进行重发、或直接发送短信。