
简介面向泛微Ecology系统开发运维人员的通用短信接口实现文档解决短信设备供应商众多、逐一集成成本高的问题。文档基于中间表模式设计展开Ecology系统仅负责将待发送短信数据写入中间表由短信设备供应商或客户自行读取并发送从而与具体设备解耦。内容详细讲解sms.xml配置文件各参数含义包括数据库类型、服务器IP、端口、数据库名、账号、密码及插入SQL语句等核心配置项的设置方法并针对SQL Server与Oracle两种数据库给出差异说明与建表脚本。资源包含1个doc文档压缩包大小38KB结构简洁、讲解精炼。已有435人浏览学习适合需要快速对接短信能力、理解Ecology短信模块底层配置逻辑的二次开发与实施人员。1. Ecology 通用短信接口实现中间表方案打开短信通道的正确姿势泛微 Ecology OA 系统内置的短信功能并不绑定任何一家短信供应商Ecology 通用短信接口实现的核心思路只有一句话Ecology 不直接发短信它只往一张中间表里插入一条待发送记录剩下的事情交给短信供应商的程序去扫表、去提交网关。这个设计的直接好处是换供应商不用动 Ecology 代码改几行配置就能切换。做 OA 实施、系统集成或者企业 IT 运维的朋友只要碰上短信发不出、供应商接口五花八门、领导催着上线短信提醒这类场景这套配置方法能让你少走不少弯路。2. 先吃透中间表原理Ecology 只负责写数据发送交给供应商2.1 为什么中间表能通吃所有短信供应商Ecology 在发短信这条链路上原本的默认方案是走 RTX腾讯通但 RTX 和短信网关毕竟是两套系统实际项目里客户采购的短信设备五花八门有 GSM Modem 的有走 HTTP 接口的还有用数据库中间表对接的。如果每来一个供应商就做一次代码集成开发和维护成本完全失控。中间表方案聪明在把「发短信」这个动作拆成了两段Ecology 负责生成数据供应商负责消费数据。两段之间只通过一张数据库表通信表结构约定固定字段谁也不用依赖谁。这个方案能成立的前提是短信供应商愿意配合。实际上大多数短信设备供应商都已经实现了通过中间表方式发送短信换句话说供应商那边有个程序定时去查这张表查到新记录就把短信提交给网关。中间表存放在哪个库、表名叫什么、字段叫什么Ecology 都不强制要求因为往中间表插入数据的 SQL 是在配置文件里指定的你完全可以根据供应商的建表约定来调整。但有两件事必须满足第一中间表所在数据库必须是 SqlServer 或 Oracle其他数据库暂时不支持第二中间表里必须有用于存放短信内容、短信接收人的字段这两个字段的值由 Ecology 传入缺了它们整个链路就断了。2.2 sms.xml 七参数逐项拆解实现这个接口的核心配置文件是ecology/WEB-INF/service/sms.xml打开后默认内容大致如下module idsms version1.0.0 service-point idsmssender interfaceweaver.sms.SmsService invoke-factory construct classweaver.sms.JdbcSmsService set propertytype valuesqlserver/ set propertyhost value192.168.0.204/ set propertyport value1433/ set propertydbname valueecology3802/ set propertyusername valuesa/ set propertypassword value123/ set propertysql valueinsert into OutBox(ReceiverMobileNo,Msg,SendTime,IsChinese,ExpressLevel,Sender) values(?,?,getDate(),1,1,1)/ /construct /invoke-factory /service-point /module这段配置里classweaver.sms.JdbcSmsService是关键它表示用的是 JDBC 方式往中间表写数据。下面七个参数逐个说参数示例值作用注意点typesqlserver中间表所在数据库类型oracle 库改为 oraclehost192.168.0.204数据库服务器 IP不要写 localhost统一写 IPport1433数据库端口oracle 改为 1521dbnameecology3802数据库实例名oracle 下是 SID 或服务名usernamesa数据库账号建议用专用账号别用 sapassword123数据库密码密码里有特殊字符要转义sqlinsert into OutBox...插入中间表的 SQL 语句接收人和内容必须用 ? 占位这里的sql参数是整个配置的灵魂。?问号的位置是 Echo 系统运行时把手机号和短信内容填进去的地方其他字段的值都是写死的。示例 SQL 里SendTime用getDate()获取IsChinese、ExpressLevel、Sender直接写死为 1因为供应商不关心这些字段的真实含义只要不是空值就行。2.3 SQL Server 和 Oracle 两套配置的差异如果你要对接的是 Oracle 数据库配置差异集中在三处。我用一个客户的实际配置来说明module idsms version1.0.0 service-point idsmssender interfaceweaver.sms.SmsService invoke-factory construct classweaver.sms.JdbcSmsService set propertytype valueoracle/ set propertyhost value192.168.0.204/ set propertyport value1521/ set propertydbname valueweaver1/ set propertyusername valueecology40002/ set propertypassword valueecology/ set propertysql valueinsert into OutBox(ReceiverMobileNo,Msg,SendTime,IsChinese,ExpressLevel,Sender) values(?,?,(select sysdate from dual),1,1,1)/ /construct /invoke-factory /service-point /module差异点有三个。第一是type改成oracle端口从 1433 改成 1521第二是SendTime字段的取值从 SQL Server 的getDate()函数换成 Oracle 的(select sysdate from dual)这是因为 Oracle 不允许在 VALUES 子句里直接调用sysdate必须子查询第三是Sender字段的值从数字 1 改成了字符串 1对应 Oracle 建表脚本里 SENDER 字段长度为 50 的 VARCHAR2 类型。其余参数的含义和 SQL Server 版本完全一致。还有个容易忽略的细节接入 Oracle 中间表时IsChinese字段在 Oracle 表里是 NUMBER 类型SQL Server 里是 BIT 类型insert 语句里统一写 1 都可以但建表脚本不能照抄必须按各自数据库的语法来。3. 建中间表与配库SQL Server 和 Oracle 脚本跑通才算数3.1 SQL Server 中间表建表脚本与字段说明理解了配置之后接下来要做的是把中间表建出来。文件包里给了 SQL Server 和 Oracle 两套参考脚本先看 SQL Server 的CREATE TABLE outbox ( ID int IDENTITY (1, 1), ExpressLevel int, Sender varchar (50), ReceiverMobileNo varchar (50), Msg varchar (500), SendTime datetime, IsChinese bit )这张表的核心字段就两个ReceiverMobileNo存手机号Msg存短信内容。ID字段用了IDENTITY(1,1)自增这样 insert 语句里就不用手动指定 ID数据库会自动生成。剩下的ExpressLevel紧急程度、Sender发送人、SendTime发送时间、IsChinese是否中文都是辅助字段。注意实际对接时有一条隐藏的规则中间表里必须有存放短信内容和接收人的字段这是硬性条件。但从配置的灵活性来看这两个字段的表名你随便定只要改 sms.xml 里的sql参数去匹配就行。比如最简单的中间表可以只建两个字段CREATE TABLE someTable ( MobileNo varchar(50), messageBody varchar(500) )对应 sms.xml 里的 sql 参数就改成insert into someTable(MobileNo,messageBody) values(?,?)。字段名变少不会影响功能只是SendTime、IsChinese这些附加信息没有了供应商那边可能需要额外的默认配置。3.2 Oracle 中间表序列 触发器组合Oracle 的建表脚本比 SQL Server 麻烦一些因为 Oracle 没有自增列需要用序列加触发器实现create table OUTBOX( ID NUMBER not null, EXPRESSLEVEL NUMBER, SENDER VARCHAR2(50), RECEIVERMOBILENO VARCHAR2(50) not null, MSG VARCHAR2(500), SENDTIME DATE not null, ISCHINESE NUMBER not null ); create sequence OUTBOX_ID_SEQ minvalue 1 maxvalue 999999999 start with 141 increment by 1 cache 20; CREATE OR REPLACE TRIGGER SET_OUTBOX_ID BEFORE INSERT ON OUTBOX FOR EACH ROW DECLARE NEXT_OUTBOX_ID NUMBER; BEGIN SELECT OUTBOX_ID_SEQ.NEXTVAL INTO NEXT_OUTBOX_ID FROM DUAL; :NEW.ID : NEXT_OUTBOX_ID; END;这段脚本里OUTBOX_ID_SEQ是序列作用相当于 SQL Server 的 IDENTITY 自增触发器SET_OUTBOX_ID在每次 insert 前自动从序列取一个值赋给 ID 字段。如果只建表不建序列和触发器insert 语句执行时 ID 为空主键约束直接报错。实际实施中有些项目简化掉了 ID 主键但那样做供应商的扫表程序可能无法准确记录增量位点容易出现漏发。Oracle 的MSG字段用的是VARCHAR2(500)如果短信内容可能超过 500 字符建议改成长度更大的VARCHAR2(1000)或CLOB。不过国内短信单条长度一般 70 个汉字超长会拆分成多条计费500 长度基本够用。3.3 切换发送设备weaver_rtx.properties 里的关键开关Ecology 系统默认的短信通道指向 RTX 服务器要让系统的短信请求改走新配的中间表接口必须处理另一个文件ecology/WEB-INF/prop/weaver_rtx.properties。修改后的内容如下#config file #Fri Aug 13 11:30:56 CST 2004 IsInitRTXOrgtrue IsDownLineNotifytrue #CurSmsServerrtx CurSmsServerIsValidtrue RTXServerPort8036 RTXServerIP RTXServerOutIP这里最关键的一步就是把CurSmsServerrtx用#号注释掉。CurSmsServer参数是 Ecology 用来指定当前短信发送设备的它等于 rtx 的时候系统会把短信内容发给 RTX 服务器注释掉之后 Ecology 在发送短信时不再找 RTX而是走sms.xml里定义的接口。IsInitRTXOrg和IsDownLineNotify这两个参数是控制 RTX 组织架构初始化和下线通知的在只做短信对接的场景下保持原样即可。这里有一个很容易踩的坑改了weaver_rtx.properties后有部分 Ecology 版本并不会立刻重新读取该文件需要重启 Tomcat 或 OA 服务进程。而且如果CurSmsServerIsValidtrue保留某些版本会在系统启动时校验 CurSmsServer 的值注释掉 rtx 后如果校验逻辑没跳过会有报错。遇到这种情况可以把CurSmsServerIsValid一并改为 false或者在确认生态没问题后再恢复。4. 不想靠扫描用 Java 替换发送实现绕过中间表4.1 SmsService 接口一个方法就够中间表方案有个先天短板短信供应商通过定时扫描中间表来发送数据发送的实际时间取决于扫描频度实时性会差一些。如果客户对短信下发延迟敏感或者供应商不提供中间表对接能力还可以走第二条路自己实现发送短信的方法。Ecology 为此预留了一个接口weaver.sms.SmsServicepublic interface SmsService { public boolean sendSMS(String smsId, String number, String msg); }接口只有一个方法方法名叫sendSMS入参是三个短信唯一 ID 用于追踪记录、接收者手机号、短信内容。返回布尔值表示是否发送成功。这个设计把发送动作完全抽象出来了实现方不用关心 Ecology 内部怎么调用、怎么冲账只负责把一条短信真实地送到网关。4.2 自定义 TestService 实现在 sms.xml 中注册文件包给了个最简实现示例虽然只是打印不会真实发送但足以走通整个链路public class TestService implements SmsService { public boolean sendSMS(String smsId, String number, String msg) { System.out.println(接受人 number); System.out.println(测试短信 msg); return true; } }这个类实现了 SmsService 接口逻辑只有两行把接收人和短信内容打到控制台然后返回 true 表示发送成功。这么做的好处是在还没有接入真实短信网关前可以先在 Ecology 里触发一次短信发送验证系统是否调到了你的实现如果控制台能打印出信息说明整个调用链已经打通剩下的就是 Java 程序员把 TestService 里的System.out.println替换成真正的网关 HTTP 请求。实现类编译部署后需要把 sms.xml 里的construct部分改成指向自定义类module idsms version1.0.0 service-point idsmssender interfaceweaver.sms.SmsService invoke-factory construct classTestService /construct /invoke-factory /service-point /module改完这个文件之后Ecology 系统在发送短信时就会调用 TestService 的sendSMS方法。接口的调用方通过interfaceweaver.sms.SmsService来按接口规范调用具体调用哪个实现类只看construct标签里的class值。同一套 Ecology 系统同时只支持一个短信实现不能中间表和自定义实现并存。4.3 部署到 ecology 的 classpath 与启动注意事项写好的 Java 类不能直接在 sms.xml 里引用你得先把它编译成 class 文件放到 Ecology 应用的 classpath 下。常见的做法是打成 jar 包放到ecology/WEB-INF/lib目录或者直接把 class 文件丢到ecology/WEB-INF/classes对应包路径下。TestService 如果没有声明 package就放在 classes 根目录如果有 package 声明比如package com.customer.sms;那 sms.xml 里class也要改成com.customer.sms.TestService两者必须一一对应。部署后第一次测试时建议先在 TestService 方法入口加一行日志输出或者断点确认类被加载到。常见翻车点是类名写错、包名不一致、类重复存在于多个 jar 包导致版本冲突。另外注意编码问题Ecology 的短信内容默认是 UTF-8如果 TestService 里转发给 HTTP 网关时编码不对中文短信会变成问号或乱码这个锅经常被甩给短信供应商。5. 避坑指南中间表短信对接的五个现场5.1 中文变成问号字段类型与字符集不匹配现象Ecology 里发的短信内容到手机上一看全是问号或者乱码。 原因中间表Msg字段用的是varchar(500)SQL Server 的 varchar 只存 ASCII存不了中文。虽然 insert 语句带上了IsChinese1但存储层已经截断了。 解决把中间表的消息字段改成nvarchar(500)或者建表时直接上varchar但确保数据库排序规则是 Chinese_PRC 支持中文编码。Oracle 侧没有这个问题VARCHAR2 支持中文存储但要注意MSG字段长度至少给到 500某些低版本 Oracle 的 VARCHAR2 默认按字节计算中文占 3 字节。5.2 中间表一直没数据SQL 字段名和表结构对不上现象Ecology 里发短信提示成功但中间表里查不到任何 insert 记录。 原因sms.xml 里配置的 insert SQL 是你自己手写的字段名和真实表结构不一致。比如 OutBox 表里字段叫ReceiverMobileNoSQL 里写成ReceiverMobile数据库直接抛列名无效的错误但 Ecology 的异常被吞掉了前段界面只看返回结果。 解决先在数据库客户端手工执行一遍 insert确认 SQL 能插入成功再拿去配 sms.xml。用?占位的地方参数顺序必须是接收人在前、内容在后和values(?,?)的左右顺序一一对应。5.3 发送时间比现在早 8 小时getDate() 取的是数据库时间现象收到的短信没延迟但供应商后台记录的发送时间和实际时间对不上。 原因中间表里SendTime用的是数据库服务器时间。如果中间表数据库服务器和 Ecology 服务器不在同一时区或者数据库系统时间和业务时间差了几个小时记录的发送时间自然就偏了。 解决不要依赖数据库函数写发送时间让供应商的扫表程序在读取记录时自己打时间戳如果供应商必须用SendTime字段那把中间表所在数据库服务器的时间同步好NTP 对准别指望getDate()能做时区换算。5.4 Oracle 主键冲突序列和触发器少建一个现象Ecology 发第一条短信正常第二条起报唯一性约束违背短信全丢。 原因Oracle 表建了 ID 主键却没有配套的序列和触发器。第一次 insert 时手动写了 ID 或 ID 为空没触发约束第二次起因为没有序列提供递增 ID主键冲突了。 解决建完表后立即跟上序列和触发器两件套脚本就是第 3 章里那段。别只建表不建序列也别建了序列忘了触发器两者缺一不可。5.5 改完配置不生效缓存与进程重启顺序现象按说明改完 sms.xml 和 weaver_rtx.properties重新发短信还是老接口的路径。 原因Ecology 的 service 配置不是动态加载的甚至同一个应用进程里可能有缓存改了文件后没有正确触发重新加载。 解决改完配置文件后完整重启一次 Tomcat / OA 服务。顺序上先重启服务再测试不要改了文件立刻测。如果重启后还是走老路径检查WEB-INF/service下是不是有多个 sms 相关 xml 文件常见的是新旧版本共存配置被旧文件覆盖。6. 验证短信链路手动插一条记录确认供应商通道对接完成不代表短信通道是通的我最常用的一套验证动作是这样的先在数据库客户端往中间表手动插一条测试数据不走 Ecology 系统单纯验证供应商的扫表程序。INSERT INTO OutBox(ReceiverMobileNo, Msg) VALUES (13800000000, 这是一条链路测试短信)如果这条能收到说明中间表和供应商通道是通的。收不到就按这个顺序排查先看供应商扫表程序有没有启动再看程序配置的查询 SQL 是否只查特定状态字段最后看表里的 SENDTIME 是否为空导致程序过滤掉了记录。中间表通了之后再反向验证 Ecology 侧登录系统后台触发一条短信动作比如流程通知、密码找回然后立刻查中间表SELECT ID, ReceiverMobileNo, Msg, SendTime FROM OutBox ORDER BY ID DESC这里有个实战小技巧把 sms.xml 里 insert SQL 的字段顺序固定好ReceiverMobileNo放第一列、Msg放第二列然后每次排查只跑这条查询前两列对不对对一眼就知道是 Ecology 没写进去还是供应商扫走之后清了表。我自己的习惯是保留一张短信发送记录表Ecology 写完中间表之后再在业务表里落一条日志记录发送时间、手机号、状态。这样供应商扫表扫慢也好、发送失败也好都能从日志里找到原始数据不会被中间表清掉就彻底没证据。从第一次做短信对接被供应商坑过之后我每次做新项目都强制走一遍这套验证流程从手动插数据到查 ID 顺序一步不省。希望帮到你。本文还有配套的精品资源点击获取