设计类似Google日历的日历系统

时间:2020-03-05 18:39:57  来源:igfitidea点击:

我必须创建类似于Google日历的内容,因此我创建了一个事件表,其中包含用户的所有事件。

困难的部分是处理重复发生的事件,事件表中的行具有一个event_type字段,该字段告诉我们它是哪种事件,因为一个事件只能用于单个日期,或者每隔x天一次就可以发生一次。

设计的主要挑战是处理重复发生的事件。

当用户使用月份视图查看日历时,如何显示给定月份的所有事件?该查询将非常棘手,因此我认为创建另一个表并为每个事件(包括重复发生的事件)创建一行会更容易。

你们有什么感想?

解决方案

回答

我要说的是从ical标准开始。如果我们将其用作模型,那么我们将能够执行Google日历,Outlook,Macical(该程序)中的所有内容,并与它们进行即时集成。

从那里开始,我们有足够的时间来使用ajax和javascript cuz,如果没有大量的ajax和javascript,就无法拥有带有拖放功能和多个日历的华丽的Web ui。

回答

我想我理解第二段意味着我们正在考虑第二个事件表,该表在每次事件发生时都有一行。我会避免的。

重复发生的事件应具有开始日期和结束日期(对于"每隔X天"(永远)连续发生的事件,它可以为Null)。我们必须决定要允许的频率类型-每X天,即每月的某天,每个给定的工作日等

我可能倾向于使用两个表,一个用于一次事件,另一个用于重复事件。我们必须分别查询和显示两者。

如果我要解决这个问题(并且我会尽全力避免重新发明这个轮子),我会寻找开源库,或者至少寻找带有Calendars的开源项目,我们可以看看。有什么建议的家伙吗?

回答

根据过去的经验,我将为每个发生的事件创建一个新记录,然后在列中引用上一个事件,以便我们可以跟踪一系列事件。

这有两个优点:

  • 无需复杂的程序即可确定下一个活动日期
  • 单个事件可以编辑,而不会影响其余事件

希望这能给我们一些思考:)

回答

我们应该有一个开始日期,结束日期和到期日期。单日事件将具有相同的开始日期和结束日期,并且还允许我们执行部分日事件。对于重复发生的事件,开始日期和结束日期将是同一天,但时间不同,那么我们就有一个枚举或者表来指定重复频率(每天,每周,每月等)。

这样一来,我们每天可以说"该事件每天都出现","每周一次该事件都出现在每周的第二天",每月可以说"该事件出现在每月的第5天","每年的第215天",只要该日期小于到期日期即可。

回答

我必须同意@ChanChan才能阅读有关如何存储这些内容的ical规范。没有简单的方法可以处理重复,特别是那些有例外的情况。我已经建立,重建和重建了一个数据库来处理此问题,并且我继续回到ical。

根据使用情况创建一个下级表并不是一个坏主意。精确计算什么时候发生的算法。 。 。嗯,发生。 。 。确实可以很复杂。没有运行它的捷径,但是值得考虑缓存结果。

回答

@GateKiller

我没有想到我们要编辑单个事件的情况。在这种情况下,我们应该将事件分开存储是有意义的。

但是,当我们这样做时,我们会在将来存储事件多远?我们选择任意日期吗?我们是否在用户首次浏览未来的几个月/几年时自动生成新事件?

另外,如何处理用户要编辑整个系列的情况。 "我们每个星期二上午10:30开会,但我们将在星期三8点开始开会。"

回答

达伦

那就是我实际上设计事件表的方式,但是考虑一下,说我有10万个创建事件的用户,这个表将受到重创,尤其是如果我要发送电子邮件以提醒人们他们的事件时事件(事件也可能是一天中的特定时间!),因此我可能每15分钟轮询一次此表!

这就是为什么我想创建另一个表来扩展所有事件/重现事件的原因,这样我可以简单地点击该表并获得用户几个月的事件视图,而无需执行任何复杂的查询和业务逻辑,并且也使投票更加有效。

问题是,该辅助表应该用于第二天还是第二个月?什么更有意义?由于用户可以查看的最大值是一个月的视图,因此我倾向于使用一个表来写出给定月份的所有事件。

(当然,对于用户可能会对原始事件表进行的任何编辑,我都必须维护此辅助表)。

陈灿

实际上,我已经使用相同的功能设计了它,但是我只是在指我将如何存储事件,特别是如何处理重复发生的事件。

回答

尝试存储每个事件的每个实例似乎确实是有问题的,而且,是不可能的。如果某人创建了一个"每周四,永远"发生的事件,则我们显然无法存储所有将来的事件。

我们可以尝试按需生成将来的事件,并仅在必要时填充将来的事件以显示它们或者发送有关它们的通知。但是,如果我们仍然要构建"按需"生成代码,为什么不一直使用它呢?不必从事件表中提取数据,而是必须使用按需事件生成来填充尚未添加到表中的任何新事件,而仅使用按需事件生成即可。最终结果将是相同的。使用此方案,我们只需要存储开始和结束日期以及事件发生频率。

我看不到有什么方法可以避免按需生成事件,因此在事件表中看不到该实用程序。如果我们出于缓存目的需要它,那么我认为我们使用的是错误的方法。首先,它的缓存很差,因为无论如何我们都无法避免按需生成事件。其次,无论如何,我们可能应该在更高级别进行缓存。如果要缓存,则缓存生成的页面,而不是事件。

就提高轮询效率而言,如果仅每15分钟轮询一次,而数据库和/或者服务器无法处理负载,那么我们已经注定要失败。如果数据库无法处理很多次,更频繁的轮询而又不费吹灰之力,则我们将无法处理用户。

回答

如前所述,不要重新发明轮子,而只是增强轮子。

Checkout VCalendar,它是开源的,并包含在PHP,ASP和ASP.Net(C#)中!

我们也可以查看Day Pilot,它提供了用Asp.Net 2.0编写的日历。他们提供了我们可以签出的精简版,如果它适合我们,则可以购买许可证。

更新(9/30/09):

当然,除非车轮坏了!另外,如果我们愿意,可以涂上一层新的光泽漆(即:使UI更好)。但是至少要设法找到基础,因为日历系统可能很棘手(带有重复事件),并且已经完成了数千次。

回答

undefined wrote:
  
  …this table will be hit pretty
  hard, especially if I am going to be
  sending out emails to remind people of
  their events (events can be for
  specific times of the day also!), so I could be polling this table every 15 minutes potentially!

创建用于通知的表。只轮询它。

更新事件(重复发生或者其他事件)时,更新通知表。

编辑:如果这是一个问题,数据库视图可能不会违反常规格式。但是,我们可能想要跟踪发送了哪些通知以及尚未发送到任何地方的通知。

回答

德里克公园
我将在表中创建事件的每个实例,并且每个月都会重新生成该表(因此,设置为"永久"重复发生的任何事件都可以提前一个月使用Windows服务或者在sql服务器级别)。
轮询将不仅每15分钟进行一次,可能仅用于与电子邮件通知相关的轮询。当某人想要查看一个月的事件时,我将不得不对所有事件进行fetech,然后重新发生事件并找出要显示的事件(因为可能在6个月前创建了重复发生的事件,但与用户查看的一个月)。

Zack,我不太想拥有一个完美标准化的数据库,我正在考虑创建一个辅助表这一事实已经违反了规则之一。我的核心数据库表遵循"规则",但是我不介意在对性能有好处的时候创建辅助表/列。

回答

That is how I have designed my events table actually, but when thinking about it, say I have 100K users who have create events, this table will be hit pretty hard, especially if I am going to be sending out emails to remind people of their events (events can be for specific times of the day also!), so I could be polling this table every 15 minutes potentially!

数据库会处理数据集的异常工作,所以我不会太担心这一点。我们应该将其用作主表,然后在事件到期时将其移至另一个表(如归档文件)中。

接下来要尝试的是尽可能少地查询数据库,因此将信息移到缓存层(如速度)中,然后将数据持久保存到数据库中。

然后,我们可以在多个数据库之间划分信息以进行扩展。例如,服务器1上存在用户1-10000日历,服务器2上存在10001 20000,依此类推。

这就是我将如何扩展这样的解决方案的方法,但是我仍然认为我提出的原始解决方案是要走的路,这就是我们如何扩展它的问题。

回答

暴力破解但仍然合理的方法是在单个事件表中为重复发生的事件的每个实例创建一个新行,所有行均不指向该系列之前的事件,而是指向该系列中的第一个事件。这可以简化选择和/或者删除特定系列中的所有元素的步骤,因为我们可以基于父ID进行选择。它还允许用户从系列中删除单个项目,而不会影响其余项目。

该查询为我们提供从元素3开始的系列:

SELECT * FROM events WHERE id = 3 OR parentid = 3

要获取本月的所有项目,假设我们在事件表中有开始日期和结束日期,我们要做的就是:

SELECT * FROM events WHERE startdate >= '2008-08-01' AND enddate <= '2008-08-31'

以编程方式处理系列的创建/修改并非难事,但这实际上取决于要提供的功能集以及使用方式。如果要区分系列和事件,则可以在事件上有一个单独的系列表和一个可为null的series_id,使我们可以随意处理各个事件,同时仍保持对系列的控制权。

回答

我正好解决了这个问题,在阅读该线程之前,我已经完全将iCalendar(rfc 2445)隔开,所以我不知道它会与它集成得多么好。无论如何,到目前为止我提出的设计看起来像这样:

  • 我们可能无法存储重复事件的所有实例,至少在事件发生之前不会存储,因此,我只拥有一个表,用于将事件的第一个实例存储为实际日期,可选的到期时间以及可空的repeat_unit和repeat_increment字段描述重复。对于单个实例,复制字段为空,否则单位将为"天","周","月","年",增量仅是要添加到下一次出现的开始日期的单位的倍数。
  • 仅当我们需要与模型中的其他实体建立关系时,存储过去的事件才看起来是有利的,即使那样,也不必在每种情况下都具有一个明确的"事件实例"表。如果其他实体已经具有日期/时间"实例"数据,则事件的外键(或者多对多联接表)很可能就足够了。
  • 为了"更改此实例" /"更改所有将来的实例",我计划仅复制事件并使过期事件过期。因此,要更改单个实例,我们要使旧实例在最后一次出现时过期,并为新的唯一实例进行复制并进行更改,且不进行任何重复,并在下一个出现时复制原始实例的另一个副本,然后重复到该实例中。未来。更改所有将来的实例都是类似的,我们只需使原始实例过期并使用更改和副本详细信息创建新副本。

到目前为止,我在此设计中看到的两个问题是:

  • 它使MWF类型的事件难以表示。有可能,但是强迫用户创建三个单独的事件,分别在M,W,F上每周重复一次,并且他们想要进行的任何更改也必须分别在每个事件上进行。这些事件在我的应用程序中并不是特别有用,但是确实在模型上留下了缺陷,使其不如我所希望的那样通用。
  • 通过复制事件进行更改,我们可以打破它们之间的关联,这在某些情况下可能很有用(或者可能偶尔会出现问题。)从理论上讲,事件表可以包含一个" copied_from" id字段来跟踪事件起源,但是我还没有完全考虑过类似的事情有多有用。一方面,父/子层次关系是从SQL查询的麻烦,因此,其收益必须非常庞大,以超过查询该数据的成本。我想,我们可以改用嵌套集。

最后,我认为可以使用直接SQL在给定的时间范围内计算事件,但是我还没有得出确切的细节,而且我认为查询通常过于繁琐而不值得。但是,为了便于讨论,我们可以使用以下表达式来计算事件在给定月份和年份之间的月份差异:

(:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12))

在最后一个示例的基础上,我们可以使用MOD来确定月份之间的差额是否是正确的倍数:

MOD(:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12), repeatIncrement) = 0

无论如何,这都不是完美的(它不会忽略过期的事件,不考虑事件的开始/结束时间等),因此,这仅是一个激励性的例子。一般来说,尽管我认为大多数查询最终都会变得过于复杂。我们最好查询在给定范围内发生的事件,或者在该范围之前不过期的事件,然后用代码而不是SQL计算实例本身。如果我们确实希望数据库进行处理,则存储过程可能会使生活变得容易得多。

回答

Ra-Ajax日历入门套件提供了处理RenderDate事件的示例,该事件可以专门修改日期。尽管"重复发生的事件"更多是算法问题,在这里我怀疑很少有日历能对我们有很大帮助。

回答

如果有人在做Ruby,那么有个很棒的库Runt可以做这种事情。值得一试。 http://runt.rubyforge.org/