C#打造自己的企业内部沟通平台(上)

简介:
概述

    在现在的工作中,最重要的一个环节就是沟通,所谓“沟通”,是指工作中大家的互相交流,交流的方式有多种多样,可以通过电子邮件,电话或网上沟通工具,每种方式都有自己的特点,比如电子邮件具有归档功能,并可以方便的追查之前的交流记录,但是电子邮件的沟通速度稍慢;电话的沟通最为快捷,可以用最快的方式表达自己的思想,使大家的思路达成共识,但是电话沟通的结果很难被跟踪,如果没有一个正式的会议纪要,则会在短时间内再次被人淡忘;第三种常用的方式就是种用网上的一些沟通工具,如MSN,QQ等,针对这些沟通工具,我也大致做了个比较,仅代表我个人观点,无意替IM工具做广告,也无意诋毁任何厂商:

    1. YAHOO

    我们在实际项目中,最重要的沟通就是所谓的“群聊”的方式,即一个项目组在物理位置上并不能坐在一起,因此需要经常一起通过“聊天”的方式进行沟通。YAHOO在这方面提供了很好的群聊功能,并且支持语音群聊。但是它有一个不太方便的地方,就是接收者必须同意后才可以开始接收消息,否则是无法接收到群消息的,这样造成群组工作中消息传达容易出现丢失,影响工作。

    2. MSN

    MSN目前的市场占用量相当大,但是在中国来说,它的登录一直是一个让人头痛的问题,经常会出现无法登录的状况,特别是在我们公司内部,70%的人无法正常使用MSN,除了登录问题外,还有一个问题,就是MSN会偶尔丢失信息,在极端的时候,可以看到聊天的一方在拼命的回答,另一方只是不停的问“你收到了吗?”这个问题的不良后果非常可怕。

    3. QQ

    QQ是在国产IM工具中一个相当出色,相当具有代表性的老大哥,它的群聊功能相当不错,但是建立群组有一个限制,就是必须具有一个“太阳”才能建一个群,或者申请QQ会员才可以建立更多的群,这是与其它工具最大的区别,可能是它商业化运作的比较成功的缘故吧。如果公司内部有多个项目组,就要建立多个群组,应该感觉起来不太理想。

    4. Skype

    这是一款以语音效果见长的软件,我在进行语音聊天的时候,这是首选的工具,它的语音无论是从清晰度或延时方面,都比其它工具有很大的优势。Skype新版的群组功能也相当强健,但是Skype有一个最大的特点就是不支持离线发送消息,只有发送端和接收端同时在线的时候,消息才会被发出,这就很容易造成消息传达的失误,比如我们在和海外分公司沟通的时候,时差非常大,我发出消息后,对方不在线,而对方在线的时候,我又不在线,只有两端同时在线的时候,才会接收到消息,所以经常会收到很久之前的离线消息,对工作的负面影响较大。

    5. OfficeIM

    我在尝试了上述几个主要的工具后,才试图去寻找更合适的工具。

    上述四个工具均属于通用软件,需要internet支持,仅在局域网内,是无法使用的,对于有些公司来说,外网的访问权限需要开通,有些公司甚至不允许访问外网,而对于我们公司来说,访问外网需要经过严格的审察,而对QQ来说,应该是禁止使用的。于是我想找一个小型的沟通平台架设在自己的公司内部。

    这个工具属于收费软件,暂且不说费用如何,我发现了它的一个问题,就是一个员工只能隶属于组织结构的一个节点,这对于我们项目管理的方式来说,基本已经被否定,因为我们一个人需要属于多个项目组。

    6. 飞鸽传书UM

    这是一款源自于日本的软件,客户端免费使用,服务器端会产生费用,这款软件不但支持组织结构,而且一个人员可以同时隶属于多个项目组,我进了试用,效果不错,但是后来发现了一个比较严重的问题(说实话现在记不起具体的问题了),我直接电话与他们客服沟通,确认此问题无法解决。

    以上仅是我自己找的一些IM工具,我相信世界上有数不清的类似的工具,应该也有完全适合我的,只是我没有找到而已,不管这么多了,既然找不到,何不尝试自己写一个呢,哈,说起来有点天方夜谭,但是我有自知之明,我知道我需要什么样的功能,我不会去做很多比较花哨漂亮的效果出来。说干就开始准备吧,我首先要做的是找一个圈内的高人来指点一下IM的一般工作方式,得知最常用的方式是需要在聊天的客户端之间建立TCP连接,实现客户端之间的互相通信。天呢,我因为长时间只是在写C#程序,对这些底层通讯的方法基本没太多的概念,这样放弃?不行,在继续求教后得知,也有人直接利用WebService,建立客户端与服务器的通讯,客户端之间互相不通讯,全部通过服务器来中转,哈,其实这正是我想象的方式,没想到真有人这么做的。我也知道这种方式有很多的缺点,但是我想了一下我的实际场景,应该没有太大的问题。说了这么多,让我们出发吧!

架构

    在概述部分我们曾提起过一点关于架构的问题,为了简化应用,我们必须在最短的时间内,用最快速的方式来实现一个最简单的聊天的功能,因为我们做这个工具是为了项目本身服务,而它本身并不是作为一个专门的项目来存在的。

clip_image002

     其中数据库采用Oracle10g,可以尽可能保证大的并发量及历史信息的存储,因为此系统已经明确为企业内部使用,并且全部是与工作相关的,为了防止客户端聊天记录的丢失,本架构要求在服务器上保留所有的聊天记录,以待后期审计。

    第二部分为WebService服务器,这个服务器是客户端与数据库之间交互的通道,在客户端与数据库服务器之间传递信息。在这种架构模式下,客户端之间没有互相的通信,全部需要通过服务器来中转。

    第三部分就是客户端,也就是企业内部用户。在本系统中,不提供公开注册的功能,所有的用户必须由后台进行注册,并分配一个初始密码,用户登录后可以自行修改自己的密码,这种方式保证系统的安全性及私密性。

    本系统在使用过程中,已经由项目组多个成员不断的完善,现在已经相当好用,并且新的功能也在不断的添加进来。下面分成三部分依次对数据库、WebService及客户端程序做一个介绍。

数据库说明

    数据库主要存储以下内容:

Ø 用户信息

Ø 组织结构信息

Ø 消息历史记录

Ø 其它辅助信息

下面对每个表进行一个详细的说明:

1. 用户表:ZR_USER

字段名

字段类型

说明

USER_ID

INTEGER

用户的内部ID,在和其它表关联的时候使用此字段

USER_ALIAS

VARCHAR2(255)

用户的登录名,在数据库中按大写字母来存储

NAME

VARCHAR2(100)

用户的真实姓名,在聊天工具中显示真实姓名

PASSWORD

VARCHAR2(255)

用户登录的密码,由系统进行初始化,用户登录后可以自己修改

STATUS

INTEGER

状态

0表示不可以登录

1表示可以登录

CREATION_USER

INTEGER

此记录的创建用户

CREATION_TIME

DATE

创建时间

LAST_MOD_TIME

DATE

此记录的最后修改时间

DELETED

INTEGER

删除标志

0:正常

1:已经删除

LAST_LOGIN_TIME

DATE

用户最后登录系统的时间,在本系统中,用这个字段的值来判断用户是否在线

2. 组织表:ZR_ORGANIZATION

字段名

字段类型

说明

ORG_ID

NUMBER

组织的内部ID,供关联使用

PARENT_ID

NUMBER

上级组织的ID

ORG_TYPE

NUMBER

组织类型

ORG_CODE

VARCHAR2(100)

组织代码

ORG_NAME

VARCHAR2(200)

组织名称

STATUS

INTEGER

状态

0表示临时群组

1表示永久生效的群组

CREATION_USER

INTEGER

创建者

CREATION_TIME

DATE

创建时间

LAST_MOD_TIME

DATE

最后修改时间

DELETED

INTEGER

删除标志

ORDERING

INTEGER

排序值

3. 用户-组织对应表:ZR_USER_ORGANIZATION

字段名

字段类型

说明

ORG_ID

NUMBER

组织的内部ID,供关联使用

USER_ID

NUMBER

用户ID,在这个表中,可以描述出用户与组织的多对多的关系

4. 消息表:ZR_MESSAGE,目前考虑所有的消息均在服务器上保存,直到需要删除为止。

字段名

字段类型

说明

OID

NUMBER

本表的主键

TO_ID

NUMBER

接收人的ID,可以是个人,也可是一个群组

TO_TYPE

NUMBER

接收者的类型

0表示个人

1表示群组

CREATION_TIME

DATE

发送的时间

FROM_ID

NUMBER

发送者的ID

CONTENT

VARCHAR2(2000)

发送的内容,根据字段宽度,不能超过1000个汉字

5. 消息查阅历史表:ZR_MESSAGE_HIS,在系统中需要记录哪些消息被哪些人看过,以免下次重复发送,因为发给某个群组的消息,需要群组中所有成员都有权阅读,所以不能简单地在消息中加一个“是否已经阅”的字段。

字段名

字段类型

说明

HIS_ID

NUMBER

本表的主键

USER_ID

NUMBER

接收人的ID

MESSAGE_ID

NUMBER

消息的ID

CREATION_TIME

DATE

发送的时间

BATCH_ID

NUMBER

批ID,某个会员可能同时接收一批的消息,而不是一个个的发送,在此记录批的信息,以减少与服务器的交互量

    利用Oracle作为存储数据库,除了它的大容量支持是一个很好的特性之外,能编写非常优秀的存储过程,也是它的一个非常大的优势。所谓存储过程,就是在数据库中写一些程序,以保证可以快速方便的操作数据库,而不用编写非常复杂的C#代码。

    本系统中有一个命名为ZR_TIM_PKG的包,里面包括以下几个主要的存储过程,在下面给出一个详细的说明:

    1. 检查用户的登录状态

function GetUserStatus(lastLoginTime in date) return number is
value number := 0;
begin
if (lastLoginTime is not null) then
if (sysdate - lastLoginTime < 2 / 24 / 60) then
value := 1;
end if;
end if;
return value;
end;
    本函数主要用于取出用户的登录状态,在本系统中,认为最后登录日期超过两分钟视为离线。

    2. 发送消息,此函数用于接收客户端发来的消息,并记录在数据库中,函数体如下:

function SendMessage(toID number,
toType number,
fromID number,
content varchar2) return number is
pragma autonomous_transaction;
value number := 0;
begin
select seq_message.nextval into value from dual;
insert into zr_message
(oid, to_id, to_type, creation_time, from_id, content)
values
(value, toID, toType, sysdate, fromID, content);
commit;
return value;
end;

    3. 取得消息,本函数主要用于判断哪些消息需要发送给指定的用户,即不能发送重复,也不能丢下任何有用的信息,如果信息是发送给某个群组,则群组里的所有成员都需要得到这条消息,程序如下:

function GetMessage(userID in number) return number is
pragma autonomous_transaction;
value number;
mBatchID number;
begin
select seq_message_batch.nextval into mBatchID from dual;
insert into zr_message_his
(his_id, user_id, message_id, creation_time, batch_id)
select 1, userID, oid, sysdate, mBatchID
from zr_message m
where (to_id in
(select org_id
from zr_organization
start with org_id in (select org_id
from zr_user_organization
where user_id = userID)
connect by prior parent_id = org_id) or to_id = userID)
and from_id != userID
and not exists (select 'x'
from zr_message_his h
where user_id = pUserID
and h.message_id = m.oid);
if (sql%rowcount = 0) then
value := 0;
else
value := mBatchID;
end if;
update zr_user set last_login_time = sysdate where user_id = userID;
commit;
return value;
end;

    段代码是整个存储过程中最长的一段,在判断是否已经读过的地方,有一个小技巧,代码片断如下:

not exists (select 'x' from zr_message_his h where user_id = pUserID and h.message_id = m.oid)

    在写程序的时候,一般都喜欢用not in的语法,这种语法写法简单,易懂,但是有一个很大的问题,就是效率不好保证,在要求表设计必须满足一系列的条件之后,有时还不能达到效果。而not exists则对效率的满足会更容易一些。

    上面还用到了一个“批”的概念,即一次可以向用户发送多条消息,依靠batch_id这个字段来保证批的完整性。

4. 创建临时的群聊

    本系统的操作方式是在后台协助建立好了相关的项目组或组织架构,并创建相关的用户,用户自己无法注册,也无法选择自己所在的组,在聊天的时候,可以选择一对一的聊天,也可以对项目组整个发起群聊。这两种方式基本满足了聊天的要求,但是对于一些特别的情况,比如一个项目组里有十多个人,而我只想和其中的两个或三个群聊,这时如果以整个项目组为接收对象,则很多人会收到无用的信息,甚至工作会受到打扰,为此,接受我们项目组团队成员的建议,加入了“临时群聊”的概念,就是可以临时组建一个群组,在这个小的群组里进行聊天,这个函数的作用就是建立这个群组,并标志这个群组为临时性。好了, 现在看一下代码部分:

function InviteUser(pUserID in number, pGuestIDs in varchar2)

return varchar2 is

pragma autonomous_transaction;

value varchar2(1000);

mGroupID number;

mGroupName varchar2(1000) := ' ';

mName varchar2(100); -- 每个人的名字,临时变量

cursor cursor_ is select name from zr_user where user_id

in (select user_id from zr_user_organization where org_id = mGroupID);

begin

select seq_group_temp.nextval into mGroupID from dual;

insert into zr_organization (org_id, parent_id, status,deleted,

creation_time, creation_user, ordering)

values (mGroupID, 0, 0, 0, sysdate, pUserID, 0);

insert into zr_user_organization (user_id, org_id)

select to_number(a.column_value), mGroupID

from table(split(pGuestIDs, ',')) a

union all

select pUserID, mGroupID from dual;

open cursor_;

fetch cursor_ into mName;

while (not cursor_%notfound) loop

mGroupName := mGroupName || mName || ',';

fetch cursor_ into mName;

end loop;

close cursor_;

mGroupName := rtrim(mGroupName, ',');

update zr_organization set org_name = mGroupName where org_id = mGroupID;

commit;

value := mGroupID || '~' || mGroupName;

return value;

end;

    到此为止,数据库部分已经大致介绍完成,虽然代码比较冗长,但是为了不让读者产生任何误解,所以还是贴了大部分代码出来。下面的一个环节就是WebService的编写,敬请待等下期。


本文转自Aicken(李鸣)博客园博客,原文链接:http://www.cnblogs.com/isline/archive/2010/04/12/1710343.html,如需转载请自行联系原作者

相关文章
|
4天前
|
缓存 编译器 API
C# 一分钟浅谈:Roslyn 编译器平台介绍
【10月更文挑战第27天】Roslyn 是 Microsoft 开发的开源编译器平台,支持 C# 和 VB.NET。它将编译过程分解为多个阶段,并提供丰富的 API 供开发者分析、生成和修改代码。本文介绍了 Roslyn 的基本概念、安装配置、基础示例和高级应用,帮助开发者更好地理解和使用这一强大工具。
14 0
|
2月前
|
SQL API 定位技术
基于C#使用winform技术的游戏平台的实现【C#课程设计】
本文介绍了基于C#使用WinForms技术开发的游戏平台项目,包括项目结构、运行截图、实现功能、部分代码说明、数据库设计和完整代码资源。项目涵盖了登录注册、个人信息修改、游戏商城列表查看、游戏管理、用户信息管理、数据分析等功能。代码示例包括ListView和ImageList的使用、图片上传、图表插件使用和SQL工具类封装,以及高德地图天气API的调用。
基于C#使用winform技术的游戏平台的实现【C#课程设计】
|
2月前
|
编译器 C# Android开发
Uno Platform 是一个用于构建跨平台应用程序的强大框架,它允许开发者使用 C# 和 XAML 来创建适用于多个平台的应用
Uno Platform 是一个用于构建跨平台应用程序的强大框架,它允许开发者使用 C# 和 XAML 来创建适用于多个平台的应用
204 8
|
6月前
|
JSON API C#
C# 通过阿里云 API 实现企业工商数据查询
C# 通过阿里云 API 实现企业工商数据查询
|
6月前
|
JSON 文字识别 算法
C# 通过阿里云 API 实现企业营业执照OCR识别
C# 通过阿里云 API 实现企业营业执照OCR识别
|
数据处理 开发工具 C#
Baumer工业相机堡盟工业相机如何在C#平台使用BGAPI SDK实现相机资源的正确释放(C#)
Baumer工业相机堡盟工业相机如何在C#平台使用BGAPI SDK实现相机资源的正确释放(C#)
128 0
|
6月前
|
前端开发 JavaScript BI
【C# 6.0】云LIS平台源码
【C# 6.0】云LIS平台源码
86 0
用C#操作类读写WinCE平台Mac地址
前段时间在测试我们触摸屏联网性能的时候,由于屏的Mac地址通过注册表设置,而PB定制WinCE内核的时候就有一个默认值,所以一批下来Mac地址都一样,两台以上的机器一联网准出问题,通过注册表软件去修改Mac地址太麻烦了,所以做了一个小的Mac地址软件。
595 0
|
C# Android开发 iOS开发
C#使用Xamarin开发可移植移动应用进阶篇(6.使用渲染器针对单个平台自定义控件..很很很很重要..),附源码
原文:C#使用Xamarin开发可移植移动应用进阶篇(6.使用渲染器针对单个平台自定义控件..很很很很重要..),附源码 前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.
1296 0
下一篇
无影云桌面