本着懒人的原则,提供最简单的解决方案!Let\'s go~~(文中采用的RTX版本为3.61,ES版本为7.1.7)
方案思路:
1.首先做一张数据表来存储消息队列,并利用RTX的SDK写一个程序来每隔一段时间从该表中取出消息队列发送出去;
2.然后为ES的ESSys数据库里面存储待办工作流信息的数据表写一个触发器,让它有新增数据的时候自动往1楼的表里面写入消息队列。
设计方案:
1.首先要解决RTX帐号和ES帐号的映射问题。很简单,在RTX和ES里面新建用户的时候,帐号不都是可以手工定义的嘛,就统一用员工工号呗~
如果两个系统都已经有帐号了,而且还是不统一的,那就改ES里面的帐号吧,把它向RTX统一。(推荐做法)
如果觉得更改ES的帐号对用户影响太大,还要另发通知,那就不改帐号,把手机或电子邮件改成RTX帐号吧~~(懒人嘛)
2.接下来做消息队列的数据表,为了配置权限简单,就单独建一个数据库<RTX>吧,然后在数据库中建一个数据表<MSG_Queue>,数据字典如下:
列名 |
数据类型 |
长度 |
允许空 |
默认值 |
说明 |
MsgID |
bigint |
8 |
|
|
主键。自动编号。 |
DelayTime
|
smallint |
2 |
√ |
0 |
消息框在桌面停留时间(秒),默认为手动关闭。 |
Receiver |
varchar |
20 |
|
|
接收者,用户名称或号码。 |
MsgInfo |
varchar |
500 |
|
|
消息提醒内容。 |
Title |
varchar |
10 |
√ |
|
消息提醒标题。(建议5个汉字的宽度以内) |
AssType |
tinyint |
1 |
√ |
0 |
保留。 |
Type |
tinyint |
1 |
√ |
0 |
消息提醒类别。0:普通消息,1:紧急消息。 |
SendMode |
tinyint |
1 |
√ |
0 |
发送模式。0:普通模式,1:发送给所有人。 |
再创建一个独立的SQL帐号RTX,密码也是RTX,配置权限为可以读写数据表<MSG_Queue>,这个帐号用来在RTX接口程序中使用。
3. 开始写RTX接口程序。我用记事本写的VBS脚本,懒得配置软件开发环境,也不用编译,修改起来简单,充分体现开源精神!(懒人嘛)
不罗嗦,直接贴代码,有问题请教SDK~
SendNotify.vbs
1\'********************************************************************
2\'Author: 碟子 http://cancelpj.cnblogs.com/
3\'Date: 2008-02-17
4\'Detail: 定时从数据库取得消息队列,并调用RTX接口发送即时消息提醒。
5\'********************************************************************
6
7\'循环周期(分钟)
8Const CycleTime = 1
9\'日志文件
10Const LogFile = "RTX_Notify.log"
11\'追加写入模式
12Const ForAppending = 8
13
14Dim RTXObj, RTXParams
15Dim RTXResult
16Dim objFSO, objLogFile
17Dim cnSrc, rst
18
19Set RTXObj = CreateObject("rtxserver.rtxobj")
20Set RTXParams = CreateObject("rtxserver.collection")
21RTXObj.Name = "SYSTOOLS"
22
23Set objFSO = CreateObject("Scripting.FileSystemObject")
24set cnSrc = CreateObject("ADODB.Connection")
25set rst = CreateObject("ADODB.RecordSet")
26
27While 1
28 cnSrc.Open "Provider=sqloledb;server=RTX数据库地址;Uid=rtx;Pwd=rtx;Database=RTX"
29 set rst = cnSrc.Execute _
30 ("SELECT MsgID,DelayTime,Receiver,Title,MsgInfo,Type,SendMode FROM Msg_Queue")
31
32 Set objLogFile = objFSO.OpenTextFile(LogFile, ForAppending, True)
33
34 Do until rst.eof
35 \'Wscript.StdOut.WriteLine(rst("now() & VBTAB & MsgID : " & rst("MsgID") & VBTAB & rst("Title") & VBTAB & rst("MsgInfo"))
36 RTXParams.Remove "SENDMODE"
37
38 \'RTXParams.Add "MSGID", rst("MsgID")
39 \'RTXParams.Add "ASSTYPE", "0"
40 RTXParams.Add "DELAYTIME", rst("DelayTime")*1000
41 RTXParams.Add "USERNAME", rst("Receiver")
42 RTXParams.Add "TITLE", rst("Title")
43 RTXParams.Add "MSGINFO", rst("MsgInfo")
44 RTXParams.Add "TYPE", rst("Type") \'0 - 普通消息;1 - 紧急消息
45
46 iSendMode = 0
47 if rst("SendMode") = 1 then
48 iSendMode = iSendMode + &H1 \'发送给所有用户
49 end if
50 if iSendMode > 0 then
51 iSendMode = iSendMode + &H10 \'需要查询状态
52 RTXParams.Add "SENDMODE", iSendMode
53 end if
54
55 On error resume next
56 RTXResult = ""
57 RTXResult = RTXobj.Call2(&H2100, RTXParams)
58 if( err.number < 0) then
59 objLogFile.Write now() & VBTAB & err.Description
60 objLogFile.Write VBTAB & "MsgID : " & rst("MsgID") & VBTAB & rst("Title")
61 objLogFile.Write VBTAB & rst("MsgInfo")
62 objLogFile.Write VbCrLf
63 else
64 objLogFile.Write now() & VBTAB & "发送成功"
65 objLogFile.Write VBTAB & "MsgID : " & rst("MsgID") & VBTAB & rst("Title")
66 objLogFile.Write VBTAB & rst("MsgInfo")
67 objLogFile.Write VbCrLf
68
69 cnSrc.Execute "DELETE FROM Msg_Queue WHERE MsgID=" & rst("MsgID")
70 \'rst.Delete
71 Wscript.Sleep 10*1000
72 end if
73
74 rst.MoveNext
75 Loop
76
77 set rst = Nothing
78 cnSrc.close
79 objLogFile.Close
80 Wscript.Sleep CycleTime*1000*60
81WEnd
脚本需要在RTX服务器上运行,且SDK Server服务必须启动。(推荐做法)
如果在其它计算机上运行脚本,需要安装RTX的SDK,配置SDK安装目录下RTXServerAPI.ini中RTX服务器的IP地址,还要修改RTX SDK Server的IP限制(修改RTX服务器安装目录下 SDKProperty.XML)。
OK,改完了配置就把服务重启一下,然后往消息队列表随便写点什么内容,测试一下脚本运行是否正常,再歇一会儿~~
4.开始写触发器。打开查询分析器,登陆到ES数据库,选中ESSys数据库,然后执行以下SQL脚本。
FG_TR_MessageQueue
1 IF EXISTS (SELECT name
2 FROM sysobjects
3 WHERE name = N\'FG_TR_MessageQueue\'
4 AND type = \'TR\')
5 DROP TRIGGER FG_TR_MessageQueue
6 GO
7
8 CREATE TRIGGER FG_TR_MessageQueue
9 ON CFWiTodo
10 AFTER INSERT
11 AS
12 /***********************************************
13 Author: 碟子 http://cancelpj.cnblogs.com/
14 Date: 2008-02-17
15 Description: 当Excel服务器产生新的工作流待办事宜时,
16 将信息发送到RTX消息队列。
17 ***********************************************/
18 BEGIN
19
20 DECLARE @Receiver VarChar(20)
21 DECLARE @MsgInfo VarChar(500)
22
23 --获取接收人的RTX号,以及待发送的消息内容。
24 SELECT @Receiver = u.MobilePhone,
25 /**
26 p.pName+\'_\'+t.tName as [任务],
27 case wi.state1
28 when 2
29 then isnull(wi.untreadName,wi.creByName)
30 else wi.creByName
31 end as [交办人],
32 convert(smalldatetime,case wi.state1
33 when 2
34 then isnull(wi.UntreadTime,wi.creDate)
35 else wi.creDate
36 end,120) as [交办时间],
37 **/
38 @MsgInfo = case wi.state1
39 when 1
40 then N\'暂存:\'
41 when 2
42 then N\'被退回:\'
43 else \'\'
44 end + \'
45 任务:\'+
46 p.pName+\'_\'+t.tName + \'
47 交办人:\'+
48 case wi.state1
49 when 2
50 then isnull(wi.untreadName,wi.creByName)
51 else wi.creByName
52 end + \'
53 交办时间:\'+
54 convert(nvarchar,case wi.state1
55 when 2
56 then isnull(wi.UntreadTime,wi.creDate)
57 else wi.creDate
58 end,120)
59 -- + wi.wiDesc
60 FROM CFWorkitems wi, CFTasks t, CFwiTodo a, CFProcesses p, Users u, Inserted i
61 WHERE u.UserID = a.userId and wi.wiId = a.wiId --and wi.tId=t.tId and wi.pId=p.pId
62 and t.tType<>3 and wi.state =0 and a.userId = i.userId
63 ORDER BY (case wi.state1 when 2 then isnull(wi.Untreadtime,wi.creDate) else wi.creDate end) desc
64
65
66 --由于ES本身在存储表单数据的过程中使用了事务,再在触发器中操作远程数据库就会引起【嵌套事务】。
67 --以上是个人猜测,未经证实;以下是针对“无法在此会话中启动更多的事务”的解决方法。
68 --用于 SQL Server 的 Microsoft OLE DB 提供程序不支持嵌套事务。
69 --因此,对于隐性或显式事务的内部数据修改操作和分布式分区视图上的数据修改操作,
70 --应将 XACT_ABORT 设置为 ON。
71 SET XACT_ABORT ON
72
73 --用触发器修改远程数据的两种方式。
74 --方式一:较安全,配置较复杂。
75 -- 需要在源数据库服务器建立一个指向目标服务器的链接服务器,
76 -- 登陆方式保存在链接服务器中,不会在触发器中显示,比较安全。
77 /**
78 INSERT INTO [RTX数据库地址].RTX.dbo.Msg_Queue(Receiver,Title,MsgInfo)
79 VALUES(@Receiver,\'Excel服务器消息提醒\',@MsgInfo)
80 **/
81 --方式二:不安全,无需另外配置。
82 INSERT INTO OPENDATASOURCE(\'SQLOLEDB\',\'Data Source=RTX数据库地址;User ID=rtx;Password=rtx\'
83 ).RTX.dbo.Msg_Queue(Receiver,Title,MsgInfo)
84 VALUES(@Receiver,\'消息提醒\',\'Excel服务器消息提醒
85 \'+@MsgInfo)
86
87 END
88
89 GO
90
91 --设置该触发器为最后执行
92 EXEC sp_settriggerorder \'FG_TR_MessageQueue\',\'Last\',\'Insert\'
由于我的RTX数据库和ESSys数据库不在同一台服务器上,因此SQL脚本中涉及到用触发器修改远程数据库的问题,需要对服务器的MDTC安全选项进行配置;如果在同一台数据库上那就不需要了,简单修改一下SQL脚本的相关代码就可以。
MSDTC安全选项的配置如果不对,在ES中保存工作流表单时就会出现“新事务不能登录到指定的事务管理器中”的错误。我的配置方式如下图,安全性低,不推荐模仿,仅供参考。
管理工具—— 组件服务——我的电脑——属性
源服务器(ESSys所在的服务器)
目标服务器(RTX数据库所在服务器)
如果设置为“要求对双方进行验证”,那么安全性就有保障了,不过我不知道在这种情况下DTC登陆帐户应该如何设置,万望高手赐教!!
OK,基本上大功告成了,江湖惯例,上效果图~
相关代码下载:SendNotify_V1_fix.rar (最近发现消息不能群发所有人,原来是SendNotify.vbs中关于iSendMode的处理参数写错了,请重新下载,抱歉抱歉~)
PS:老大说我的这个设计架构对RTX服务器的耦合度太高,应该让RTX服务器对其它应用服务器透明,所有配置只在RTX服务器这边完成,其它应用服务器只提供各自的消息队列表,我觉得蛮有道理的,不过那样就不能用脚本来写程序了,所有应用服务器的后台数据库密码要明文写在脚本里面太不安全了,过段时间用其它语言写一个像模像样的程序吧,最好还是运行后在桌面右下角有图标的~~
抛砖引玉,如果看官从中获得了一点点启发,那我就很欣慰了,如果你把我后面想做的事都做了,就把代码发我一份吧~~ (懒人嘛)