一不小心,国庆长假就结束了,上班第一天抓紧时间把其余的部分放上来吧。
首先进行数据库的结构设计,在我的设想中,最少应该有三个数据表,一个是发件箱(mail_outbox),用来存放准备发送的邮件,一个是已发送(mail_sent),发送正确的邮件都被写入这个表中,还有一个是发送错误(mail_error),发送时出现问题的邮件就存放在这里,就这个小程序而言,它最主要的功能是将mail_outbox中的邮件发送出去,具体数据是怎么被写入这个数据表的,我们就先不讨论了。
大体的思路确定以后,就可以开始进行设计了,数据表的字段应该包括发件人姓名、收件人地址、收件人姓名、主题、内容、是否HTML邮件、拟发送时间、邮件优先级等内容,本来还想再加入一个回复邮件地址的项目,后来发现不知道为什么,Jmail4.4中都还有的ReplyTo属性,在Jmail.Net 1.1中竟然没有了,也就不再加这个字段了。建表语句如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
CREATE TABLE [dbo].[mail_outbox]( [mail_id] [ int ] IDENTITY(1,1) NOT NULL , [mail_sendtime] [datetime] NULL , [mail_sendname] [nvarchar](30) NULL , [mail_toemail] [ varchar ](50) NULL , [mail_toname] [nvarchar](30) NULL , [mail_subject] [nvarchar](100) NULL , [mail_body] [nvarchar]( max ) NULL , [mail_html] [ bit ] NULL , [mail_Priority] [ char ](1) NULL , [mail_flag] [ char ](1) NULL , [mail_senduser] [ varchar ](14) NULL , CONSTRAINT [PK_mail_outbox] PRIMARY KEY CLUSTERED ( [mail_id] ASC ) WITH (PAD_INDEX = OFF , STATISTICS_NORECOMPUTE = OFF , IGNORE_DUP_KEY = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON ) ON [ PRIMARY ] ) ON [ PRIMARY ] |
在对该表进行读取时,最多的动作就是检查是否已经到了预定的发送时间,因此在这个表上对mail_sendtime增加了一条索引,加快处理速度。
1
2
|
create index idx_mailoutbox on mail_outbox (mail_sendtime) |
Mail_sent表、Mail_error表与Mail_outbox表的结构基本相同,只是增加了一个Mail_senttime字段记录实际的发送时间及Mail_error字段记录报错信息,同时去掉了Mail_id的主键及自动增长,并没有加上任何索引。
数据表建好后,开始增加程序功能,因为数据库使用的是微软自己的SQL SERVER 2005,因此就使用了SqlClient.SqlConnection来进行连接,新建一个名为sql_conn.vb的类,将主要功能都进入,然后在主程序中进行调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
Public Class sql_conn Private Shared Conn As SqlClient.SqlConnection Public Shared connStr As String Public Shared Function Open() As Boolean '连接数据库 Try Conn = New SqlClient.SqlConnection(connStr) Conn.Open() Return True Catch 'MsgBox("数据库无法连接1", MsgBoxStyle.OkOnly, "错误") Return False End Try End Function Public Shared Function Open( ByVal p_ServerName As String , ByVal p_UID As String , ByVal p_PassWord As String , ByVal p_DataBase As String ) As Boolean '连接数据库,四个参数是服务器名,用户名,密码,数据库名 Try connStr = "server=" + p_ServerName + ";User ID =" + p_UID + ";password=" + p_PassWord + ";database =" + p_DataBase Conn = New SqlClient.SqlConnection(connStr) Conn.Open() Return True Catch 'MsgBox("数据库无法连接2", MsgBoxStyle.OkOnly, "错误") Return False End Try End Function #Region "得到数据集" Public Shared Function GetDS( ByVal strSql As String ) As DataSet '通过Sql语句得到数据集 If Conn Is Nothing Then Open() End If Dim DS As New DataSet Dim sqlComm As New SqlClient.SqlCommand(strSql, Conn) Dim sqlDA As New SqlClient.SqlDataAdapter(sqlComm) Try sqlDA.Fill(DS) sqlComm.Cancel() Return DS Catch MsgBox(Err.Description, MsgBoxStyle.OkOnly, "错误" ) Return Nothing End Try End Function #End Region #Region "执行Sql语句" Public Shared Function Exec_Sql( ByVal strSql As String ) As Integer '执行Sql语句,不返回数据集 If Conn Is Nothing Then Open() End If Dim sqlComm As New SqlClient.SqlCommand(strSql, Conn) Dim SqlCount As Integer = 0 Try SqlCount = sqlComm.ExecuteNonQuery() '执行Sql语句 sqlComm.Cancel() Return SqlCount Catch 'MsgBox(Err.Description, "错误") Return -1 End Try End Function #End Region End Class |
在一般常见的代码中,对于exec_sql这类不需要返回数据集的sql执行语句一般都选择了返回Boolean值,但我选择了返回一个Integer值,最主要的功能是用来返回update数据库时受影响的记录条数,方便下一步的判断和继续操作。
好,现在数据库的结构和连接、读写数据库的代码都有了,要实现循环读取待发邮件的功能,我们还需要再加上一个Timer控件,将控件拖到Form中,在用户点击“开始”按钮执行程序时对Timer进行处理,我设定的Interval值为1000,也就是第一秒运行一次,这样的话就可以把扫描的间隔时间按秒数进行设定,十分方便,而且也比较精确。
下面是“开始”按钮及Timer_Tick的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
Private Sub Btn_Start_Click( ByVal sender As System. Object , ByVal e As System.EventArgs) Handles Btn_Start.Click If Running = False Then If Txt_ScanSec.Text < 30 Or Txt_ScanSec.Text > 86400 Then MessageBox.Show( "扫描的间隔时间不应小于30秒或大于86400秒,请重新设置!" , "注意" , MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Txt_ScanSec.Text = "180" Exit Sub End If If Not Open(Txt_DbServer.Text, Txt_DbUser.Text, DbPassword, Txt_DataBase.Text) Then MessageBox.Show( "数据库连接有误,请检查后再运行程序程序!" , "注意" , MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If Running = True RunStrat = Now() Btn_Start.Text = "停止(&S)" Btn_Exit.Enabled = False Btn_SaveIni.Enabled = False 'Tbl_Main.Enabled = False Txt_ScanSec. ReadOnly = True Chk_LogFile.Enabled = False Tbl_Main.TabPages(1).Enabled = False Tbl_Main.TabPages(2).Enabled = False 'ReadRow("select * from yn_login where user_role = '2'") Time_SendMail.Interval = 1000 Time_SendMail.Enabled = True Time_SendMail.Start() Txt_Message.Text = RunStrat + "程序开始运行……" + vbCrLf Else Time_SendMail. Stop () Time_SendMail.Enabled = False Running = False Btn_Start.Text = "开始(&S)" Btn_Exit.Enabled = True Btn_SaveIni.Enabled = True Txt_ScanSec. ReadOnly = False Chk_LogFile.Enabled = True Tbl_Main.TabPages(1).Enabled = True Tbl_Main.TabPages(2).Enabled = True End If End Sub Private Sub Time_SendMail_Tick( ByVal sender As System. Object , ByVal e As System.EventArgs) Handles Time_SendMail.Tick If ScanSec >= Txt_ScanSec.Text Then Time_SendMail. Stop () Txt_Message.Text = "正在进行邮件发送……" + vbCrLf + "启动时间:" + Now() SentOutBox() Application.DoEvents() ScanCount = ScanCount + 1 Txt_Message.Text = "本次扫描启动于" + RunStrat + vbCrLf + "共扫描了" + ScanCount.ToString + "次,发送成功邮件" + SendSuc.ToString + "封,失败" + SendErr.ToString + "封!" + vbCrLf + "暂停" + Txt_ScanSec.Text.ToString + "秒,等待下一次扫描……" + vbCrLf ScanSec = 0 Time_SendMail.Start() Else ScanSec = ScanSec + 1 Txt_Message.Text = Txt_Message.Text + "." End If End Sub |
这样的话,整个程序的主要步骤就已经完成了,剩下来的就是一些小细节方面的改动,将在下一节的时候再和大家一一交流。