• 一个群发站内信的设计


    开始做发送消息的时候没意识到群发很复杂,草草写了一个消息/公告表,然后在前台用两个菜单“个人消息”,“系统消息”取出不同的信息,然后两边用同样的逻辑做“已读,未读,删除”的操作,然后继续后面的流程,今天突然发现我用私信的方式设了公共消息为未读那也太搞了,才发现做群发还是值得仔细考虑番的。
    开始的表为:

    代码
    create table wgi_notice(
    id
    int identity primary key,--主键id
    title nvarchar(100) null,--公告标题
    notice ntext null,--公告内容
    pubdate datetime default getdate(),--发布时间
    unread int default 0, --默认未读
    publisher int default -1, --默认值表示未知系统管理员
    objid int default -1 --默认值表示公告消息,否则跟上接收人ID
    objtype int default -1 --表示用户组(不同的用户表)
    )

    这样,主表就是这一张了,用objid表示接收消息的对象,objtype表示接收消息的用户组(此系统有几个用户表,纯粹是系统需要,跟群发无关,可略过)。鉴于上面的考虑,unread就不能再用来作群发消息的个人阅读与否的状态了。

    但是显然我不会有多少用户就插入多少条消息纪录,因此我建了一个关系表,专门用来保存个人与消息是否阅读与删除的状态。

    代码
    1 create table wgi_noticestat (
    2 id int identity primary key ,
    3 noticeid int null,
    4 usertype int null,
    5 userid int null,
    6 unread int null default 0,
    7 deleted int null default 0
    8 )

    不好意思,建表的时候发现usertype,userid更容易理解,但是前面的代码不想改了,先这样吧。

    思维是:用户类型+用户ID+消息ID 对应一条具体的消息的“状态”,不用我的自增id为主键的方案的话,可把那三个字段作为联合主键,以下就用“联合主键”来指代这三个字段吧。
    但是此表初始是空的,也就是说查不到这个表有数据,那么证明这条纪录用于此用户是未读的,显然也是未删除的(显然,我的设计是删除只是打个标,当然要这么设计,事实上全站就这么一条,你不能真正删掉它)。
    而假如查询到“联合主键”存在,那么更改状态就简单多了。

    所以前期只是需要查出所有未标为“删除”的公共消息,然后来操作状态表,那么对于消息的各种打标(已读,未读,删除)就都能操作了,所以显然,想要正确地“查询”出想要的表,那就是:
    显示所有群发消息(还可以更复杂点,查询出属于该用户组的群发消息),
    过滤掉消息状态表中标记该消息为删除的记录
    假如消息状态表中没有此条记录,显然不需要被过滤
    把该条记录的已读未读状态查询到主表中,如果状态表中无此条记录,则记录成未读(此条主要是为了前台给已读未读加不同的样式)
    所以查询复杂了点,直接写sql的话就是

    代码
    1 select a.*,
    2 isnull((select d.unread from wgi_noticestat d where d.usertype=0 and d.userid=1 and d.noticeid=a.id),0) readed --从状态表读出已读未读状态
    3 from wgi_notice a
    4 where objid=-1 and --查询公共消息,如果要分用户组,在此加条件
    5 ((select (select top 1 deleted from wgi_noticestat b where b.usertype=0 and b.userid=1 and b.noticeid=a.id))<>1 --条件:删除状态不为真
    6 or
    7 (select count(c.noticeid) from wgi_noticestat c where c.usertype=0 and c.userid=1 and c.noticeid=a.id)=0) --条件:或在状态表中没有记录
    8 order by a.pubdate desc

    一个表就查询出来了,修改状态的时候怎么做呢?
    只需要根据“联合主键”查询状态库,假如没有记录,则新增一条记录,设状态值为需要的值;假如查到了这条记录,则update状态值。
    另外删除操作会简单些,只需要简单地新增/修改为1就行,因为一旦你”删除“了,你就没机会对它”恢复删除“的,所以你写后台sql的时候可以少送一个状态参数进去,当然,这个不重要了。

    最后,结果就是,不管这个系统有多少用户,不管这个系统有多少沉默用户,只有用户在”操作“某条公共消息的时候,才会insert一条公共消息的记录,这样数据库消耗是最少的。既不需要把一条消息重复插N次,也避免了沉默用户而产生的冗余信息,不操作则无记录。
    不知道朋友们看明白没有,只是初步设计,希望能提出改进建议。

  • 相关阅读:
    采样错误
    MathJax
    jupyter
    pip
    str操作
    Content-Type
    json转csv
    【tornado】静态文件
    dict 字典
    基于插件技术的GIS应用框架(C# + ArcEngine9.3)(一)
  • 原文地址:https://www.cnblogs.com/walkerwang/p/1728496.html
Copyright © 2020-2023  润新知