功能演示视频

b展示视频: 数据库大作业—日常记账系统(C# winform + SQL Server).

SQL代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
USE Application
DROP TABLE IF EXISTS UserData; /*用户信息*/
DROP TABLE IF EXISTs Account; /*用户账本数据*/
CREATE TABLE UserData
(
ID NCHAR(10) PRIMARY KEY, /*用户ID*/
UserPassword NCHAR(32), /*用户密码*/
UserMail NCHAR(30), /*用户邮箱*/
UserPhoto image, /*用户照片*/
UserName NCHAR(30), /*用户名*/
);
CREATE TABLE Account
(
UserID NCHAR(10),
InOrOut NCHAR(10),
ConsumeType NCHAR(10),
Price FLOAT,
NOTE NCHAR(200),
RecordDate DATE,
);
/*管理员初始账号*/
INSERT INTO UserData(ID,UserPassword,UserMail,UserName) VALUES('100001',' ','wenwen@txw.com','小田txw');
GO

SQL代码说明

创建了2个表:

  1. UserData表

用于存储用户的信息,包括用户名、用户ID、用户密码、用户邮箱、用户头像。

  1. Application表

用于存储用户的记账信息,包括用户ID、账单类型、金额、备注、时间。

自定义类

MailVeriCode

代码

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Database
{
public class MailVeriCode
{
/// <summary>
/// 生成随机验证码
/// </summary>
/// <param name="CodeLength">验证码长度</param>
public static string CreateRandomMailCode(int CodeLength)
{
int randNum;
char code;
string randomCode = String.Empty;//随机验证码

//生成一定长度的随机验证码
//Random random = new Random();//生成随机数对象
for (int i = 0; i < CodeLength; i++)
{
//利用GUID生成6位随机数
byte[] buffer = Guid.NewGuid().ToByteArray();//生成字节数组
int seed = BitConverter.ToInt32(buffer, 0);//利用BitConvert方法把字节数组转换为整数
Random random = new Random(seed);//以生成的整数作为随机种子
randNum = random.Next();

//randNum = random.Next();
if (randNum % 3 == 1)
{
code = (char)('A' + (char)(randNum % 26));//随机大写字母
}
else if (randNum % 3 == 2)
{
code = (char)('a' + (char)(randNum % 26));//随机小写字母
}
else
{
code = (char)('0' + (char)(randNum % 10));//随机数字
}
randomCode += code.ToString();
}
return randomCode;
}


/// <summary>
/// 发送邮件验证码
/// </summary>
/// <param name="MyEmailAddress">发件人邮箱地址</param>
/// <param name="RecEmailAddress">收件人邮箱地址</param>
/// <param name="Subject">邮件主题</param>
/// <param name="MailContent">邮件内容</param>
/// <param name="AuthorizationCode">邮箱授权码</param>
/// <returns></returns>
public static bool SendMailMessage(string MyEmailAddress, string RecEmailAddress, string Subject, string Body, string AuthorizationCode)
{
MailMessage mail = new MailMessage();
mail.From = new MailAddress(MyEmailAddress);//发件人邮箱地址
mail.To.Add(new MailAddress(RecEmailAddress));//收件人邮箱地址
mail.Subject = Subject;//邮件标题
mail.Body = Body; //邮件内容
mail.Priority = MailPriority.High;//优先级

SmtpClient client = new SmtpClient();//qq邮箱:smtp.qq.com;126邮箱:smtp.126.com
client.Host = "smtp.qq.com";
client.Port = 587;//SMTP端口465或587
client.EnableSsl = true;//使用安全加密SSL连接
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Credentials = new NetworkCredential(MyEmailAddress, AuthorizationCode);//验证发件人身份(发件人邮箱,邮箱授权码);

try
{
client.Send(mail);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "发送失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
return true;
}


/// <summary>
/// 验证QQ邮箱
/// </summary>
/// <param name="mail">邮箱</param>
/// <returns></returns>
public static bool CheckMail(string mail)
{
string str = @"^[1-9][0-9]{4,}@qq.com$";
Regex mReg = new Regex(str);

if (mReg.IsMatch(mail))
{
return true;
}
return false;
}
}
}

方法概述

  1. CreateRandomMailCode

用于生成要发送的验证码,将验证码长度作为形参,返回字符串型的验证码。

  1. SendMailMessage

用于发送邮件,使用了C#自带的类MailAddressSmtpClientNetworkCredential等。收件人邮箱、发件人邮箱、发件人邮箱授权码、邮件内容、邮件标题作为形参,完成邮箱验证码的发送,完成发送后,返回true的bool值,否则返回false

  1. CheckMail

用于检测用户填入的邮箱验证码与发送的是否一致,返回bool类性值。

登录界面

界面展示

在这里插入图片描述

代码

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Database
{
public partial class Login : Form
{
public string code; //储存生成验证码
public Login()
{
InitializeComponent();
init();
}

#region 初始化参数
private void init()
{
this.MaximizeBox = false; //禁止最大化
//使标签透明化
title_lab.BackColor = Color.Transparent;
account_lab.BackColor = Color.Transparent;
password_lab.BackColor = Color.Transparent;
vericode_lab.BackColor = Color.Transparent;
veri_lab.BackColor= Color.Transparent;

//使按钮透明化
login_btn.FlatStyle = FlatStyle.Flat;
login_btn.ForeColor = Color.Transparent;
login_btn.BackColor = Color.Transparent;
login_btn.FlatAppearance.BorderSize = 1;
login_btn.FlatAppearance.MouseOverBackColor = Color.Transparent;
login_btn.FlatAppearance.MouseDownBackColor = Color.Transparent;

register_btn.FlatStyle = FlatStyle.Flat;
register_btn.ForeColor = Color.Transparent;
register_btn.BackColor = Color.Transparent;
register_btn.FlatAppearance.BorderSize = 1;
register_btn.FlatAppearance.MouseOverBackColor = Color.Transparent;
register_btn.FlatAppearance.MouseDownBackColor = Color.Transparent;

forget_linkbtn.FlatStyle = FlatStyle.Flat;
forget_linkbtn.ForeColor = Color.Transparent;
forget_linkbtn.BackColor = Color.Transparent;

checkBox1.BackColor = Color.Transparent;

//验证码生成
CreatVeriCode();
}
#endregion

#region 生成随机验证码
private void CreatVeriCode()
{
//随机实例化
code = "";
Random ran = new Random();
int number;
char code1;
//取五个数
for (int i = 0; i < 5; i++)
{
number = ran.Next();
if (number % 2 == 0)
code1 = (char)('0' + (char)(number % 10));
else
code1 = (char)('A' + (char)(number % 26)); //转化为字符

this.code += code1.ToString();
}

vericode_lab.Text = code;

}
#endregion

#region 登录按钮事件(连接数据库验证用户名和密码)
private void login_btn_Click(object sender, EventArgs e)
{

string username = account_txt.Text.Trim();
string password = EncryptWithMD5(password_txt.Text.Trim());
//创建数据库连接对象
string sqlconf = "Data Source = .;Initial Catalog = Application;Integrated Security = SSPI";
SqlConnection sqlConnection = new SqlConnection(sqlconf);
sqlConnection.Open();

string sql = "select ID,UserPassword from UserData where UserName = '" + username + "'and UserPassword = '" + password + "'";
SqlCommand cmd = new SqlCommand(sql, sqlConnection);
SqlDataReader reader = cmd.ExecuteReader();

if(reader.HasRows && vericode_txt.Text.ToLower() == code.ToLower())
{
new Main(username).Show();

this.Hide();
}
else if(reader.HasRows && vericode_txt.Text.ToLower()!=code.ToLower())
{
MessageBox.Show("验证码有误,登录失败!","警告");
CreatVeriCode();
return;
}
else
{
MessageBox.Show("账号密码有误,登录失败!", "警告");
return;
}
reader.Close();
sqlConnection.Close();
}
#endregion

#region 注册界面
private void register_btn_Click(object sender, EventArgs e)
{
new Register().Show();
}
#endregion

#region MD5加密算法
private string EncryptWithMD5(string source)//MD5加密
{
byte[] sor = Encoding.UTF8.GetBytes(source);
MD5 md5 = MD5.Create();
byte[] result = md5.ComputeHash(sor);
StringBuilder strbul = new StringBuilder(40);
for (int i = 0; i < result.Length; i++)
{
strbul.Append(result[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位
}
return strbul.ToString();
}
#endregion

#region 点击验证码标签刷新验证码
private void vericode_lab_Click(object sender, EventArgs e)
{
CreatVeriCode();
}
#endregion

private void forget_linkbtn_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
new Forget().Show();
}

private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
//复选框被勾选,显示密码
password_txt.PasswordChar = new char();
}
else
{
//复选框为被勾选,隐藏密码
password_txt.PasswordChar = '*';
}
}
}
}

方法概述

主要方法:initCreatVeriCodelogin_btn_Clickregister_btn_ClickEncryptWithMD5vericode_lab_Clickforget_linkbtn_LinkClickedcheckBox1_CheckedChanged

其中

init:初始化方法,初始化界面和生成初始的验证码。

CreatVeriCode:验证码生成方法。

login_btn_Click:点击登录按钮事件,具体的事件后面描述。

register_btn_Click:点击注册按钮事件,调出注册界面。

EncryptWithMD5:MD5加密算法方法,实际的密码经过该方法后,返回经MD5加密的密码。

vericode_lab_Click:点击验证码标签事件,点击验证码标签后,会刷新验证码。

forget_linkbtn_LinkClicked:点击忘记密码事件,调出找回密码界面。

checkBox1_CheckedChanged:复选框改变事件,用于改变密码文本框的显示(显示密码和隐藏密码)。

主要控件及具体事件说明

  1. “登录”按钮

按下“登录”按钮后,程序会与Application数据库连接,根据输入的用户名和密码,从UserData表中查询相应的数据,若存在用户名且密码正确,则会验证输入的验证码是否正确,若正确,则调出主界面。

总结说明

登录界面的主要功能:

  1. 有调出注册界面和找回密码界面按钮;
  2. 登录时连接数据库,验证账号和密码的有效性,完成登录;
  3. 为让用户确认密码,附加显示密码复选框,用户可勾选复选框显示具体密码,也可取消勾选隐藏密码。

登录界面较为简单,要实现的功能不多,主要就是为了调出其他界面。

注册界面

界面展示

image-20211229141710684

代码

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Database
{
public partial class Register : Form
{
public static string pathPicture;
public Byte[] mybyte = new byte[0];
public Register()
{
InitializeComponent();
init();
}
#region 参数初始化
private void init()
{
this.MaximizeBox = false;//禁止最大化
//使标签透明化
mail_lab.BackColor = Color.Transparent;
passwordreg_lab.BackColor = Color.Transparent;
vericode_lab.BackColor = Color.Transparent;
register_lab.BackColor = Color.Transparent;
username_lab.BackColor = Color.Transparent;
pic_lab.BackColor = Color.Transparent;
//使按钮透明化
Mail_btn.FlatStyle = FlatStyle.Flat;
Mail_btn.ForeColor = Color.Transparent;
Mail_btn.BackColor = Color.Transparent;
Mail_btn.FlatAppearance.BorderSize = 1;
Mail_btn.FlatAppearance.MouseOverBackColor = Color.Transparent;
Mail_btn.FlatAppearance.MouseDownBackColor = Color.Transparent;
register_btn.FlatStyle = FlatStyle.Flat;
register_btn.ForeColor = Color.Transparent;
register_btn.BackColor = Color.Transparent;
register_btn.FlatAppearance.BorderSize = 1;
register_btn.FlatAppearance.MouseOverBackColor = Color.Transparent;
register_btn.FlatAppearance.MouseDownBackColor = Color.Transparent;
pictureBox1.BackColor = Color.Transparent;
checkBox1.BackColor = Color.Transparent;
}
#endregion
#region 获取邮箱验证码(MailMessage类和SmtpClient类)
int seconds1 = 60;//倒计时60s
int seconds2 = 60 * 5;//验证码有效时间5分钟
string strMailVeriCode;
private void Mail_btn_Click(object sender, EventArgs e)
{
string recEMailAddress = Mail_txt.Text.Trim();//收件人邮箱
strMailVeriCode = MailVeriCode.CreateRandomMailCode(6);
string strBody = "验证码:" + strMailVeriCode + ",5分钟内有效,请勿泄漏于他人。如非本人操作,请忽略。系统邮件请勿回复。";//邮件内容
string strSubject = "【小田账本】注册验证";//邮件标题
string strMyEmailAddress = "xxxxxxxxx@qq.com";//发件人邮箱
string strAuthorizationCode = "xxxxxxxxxxxx";//邮箱授权码
if (string.IsNullOrEmpty(recEMailAddress))//判断是否输入了邮箱
{
MessageBox.Show("请输入邮箱!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
Mail_txt.Focus();
}
else if (MailVeriCode.CheckMail(recEMailAddress) == false)//判断邮箱格式是否正确
{
MessageBox.Show("您输入的QQ邮箱有误,请重新输入!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Mail_txt.Focus();
return;
}
else//发送验证码
{
//发送
if (MailVeriCode.SendMailMessage(strMyEmailAddress, recEMailAddress, strSubject, strBody, strAuthorizationCode) == true)
{
Mail_btn.Enabled = false;
//计时器初始化
timer1.Interval = 1000;
timer1.Start();
timer2.Interval = 1000;
timer2.Start();
}
else
{
vericode_txt.Focus();
}
}
}
#endregion
#region 倒计时1方法
private void timer1_Tick(object sender, EventArgs e)
{
if (seconds1 > 0)
{
seconds1--;
Mail_btn.Text = "剩余" + seconds1.ToString() + "秒";
}
else
{
timer1.Stop();
Mail_btn.Text = "获取验证码";
Mail_btn.Enabled = true;
}
}
#endregion
#region 倒计时2方法
private void timer2_Tick(object sender, EventArgs e)
{
if (seconds2 == 0)
{
timer2.Stop();
//旧的验证码过期,生成一个新的验证码
strMailVeriCode = MailVeriCode.CreateRandomMailCode(6);
}
}
#endregion
#region 注册按钮事件
private void register_btn_Click(object sender, EventArgs e)
{
string mailVeriCode = vericode_txt.Text.Trim();//邮箱验证码
//先确认一些重要信息为非空
if (username_txt.Text.Trim() != "" && passwordreg_txt.Text.Trim() != "")
{
if(pathPicture==null)
{
//为避免登录后在主界面读取数据库头像报错,强制要求选择初始头像
//还有一种思路就是在数据库中存放一个初始头像,当注册界面打开时,自动将初始头像导入,也可以防止无头像报错问题
MessageBox.Show("请选择头像!");
return;
}
else
{
string sqlconf = "Data Source = .;Initial Catalog = Application;Integrated Security = SSPI";
SqlConnection sqlConnection = new SqlConnection(sqlconf);
string username = username_txt.Text.Trim();
string mail = Mail_txt.Text.Trim();
sqlConnection.Open();
string sql1 = "select UserName from UserData where UserName = '" + username + "'";
SqlCommand sqlCommand1 = new SqlCommand(sql1, sqlConnection); //确认用户名是否已被注册
SqlDataReader sqlDataReader1 = sqlCommand1.ExecuteReader();
bool a = sqlDataReader1.HasRows;
sqlDataReader1.Close();
if (a)
{
MessageBox.Show("用户名已注册!");
return;
}
else
{
string sql2 = "select UserName from UserData where UserMail = '" + mail + "'";
SqlCommand sqlCommand2 = new SqlCommand(sql2, sqlConnection); //确认该邮箱是否已被注册
SqlDataReader sqlDataReader2 = sqlCommand2.ExecuteReader();
bool b = sqlDataReader2.HasRows;
sqlDataReader2.Close();
if (b)
{
MessageBox.Show("该邮箱已注册!");
return;
}
else
{
if (string.IsNullOrEmpty(mailVeriCode) == true)
{
MessageBox.Show("请输入验证码", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
vericode_txt.Focus();
return;
}
else if (mailVeriCode.ToLower() != strMailVeriCode.ToLower())//判断邮箱验证码是否输入正确;不区分字母大小写
{
MessageBox.Show("您输入的验证码有误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
vericode_txt.Focus();
return;
}
//查找最大的ID值
string sql4 = "select max(ID) as maxid from UserData";
SqlCommand command1 = new SqlCommand(sql4, sqlConnection);
SqlDataReader maxid = command1.ExecuteReader();
maxid.Read();
int newid = Convert.ToInt32(maxid["maxid"]) + 1;
maxid.Close();
string password = EncryptWithMD5(passwordreg_txt.Text.Trim());
string sql3 = "insert into UserData(ID,UserPassword,UserMail,UserName,UserPhoto)" + "values (@userid, @userpassword,@usermail,@username,@userphoto)";
SqlCommand command = new SqlCommand(sql3, sqlConnection);
SqlParameter sqlParameter = new SqlParameter("@userid", newid);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@userpassword", password);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@usermail", Mail_txt.Text);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@username", username_txt.Text);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@userphoto", System.Data.SqlDbType.Image);
sqlParameter.Value = mybyte;
command.Parameters.Add(sqlParameter);
command.ExecuteNonQuery();
sqlConnection.Close();
MessageBox.Show("注册成功!");
this.Close();
}
}

}
}
else
{
MessageBox.Show("请先将信息填写完整!");
}
}
#endregion
#region MD5加密算法
private string EncryptWithMD5(string source)//MD5加密
{
byte[] sor = Encoding.UTF8.GetBytes(source);
MD5 md5 = MD5.Create();
byte[] result = md5.ComputeHash(sor);
StringBuilder strbul = new StringBuilder(40);
for (int i = 0; i < result.Length; i++)
{
strbul.Append(result[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位
}
return strbul.ToString();
}
#endregion
#region 添加头像
private void pictureBox1_Click(object sender, EventArgs e)
{
//设置文件对话框显示的初始目录为系统桌面
this.openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
//设置当前选定筛选器字符串以决定对话框中“文档类型”选项
this.openFileDialog1.Filter = "bmp文件(*.bmp)*.bmpgif文件(*.gif)*.gifjpeg文件(*.jpg)*.jpgpng文件(*.png)*.png";
//设置对话框中当前选定筛选器的索引
this.openFileDialog1.FilterIndex = 3;
//关闭对话框,还原当前目录
this.openFileDialog1.RestoreDirectory = true;
//设置对话框标题
this.openFileDialog1.Title = "选择头像照片";
if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
{
pathPicture = this.openFileDialog1.FileName;
FileStream fs = new FileStream(pathPicture, FileMode.Open, FileAccess.Read);
mybyte = new byte[fs.Length];

fs.Read(mybyte, 0, mybyte.Length);
pictureBox1.Image = Image.FromStream(fs);
//将照片位于标签上方,以免被标签覆盖遮挡
pictureBox1.BringToFront();
fs.Close();
}
}
#endregion
//因为不想再添加再次确认密码的功能,直接添加复选框显示密码,让用户确认输入的密码
#region 显示/隐藏密码
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if(checkBox1.Checked)
{
//复选框被勾选,显示密码
passwordreg_txt.PasswordChar = new char();
}
else
{
//复选框为被勾选,隐藏密码
passwordreg_txt.PasswordChar = '*';
}
}
#endregion
}
}

方法概述

主要方法:initMail_btn_Clicktimer1_Ticktimer2_Tickregister_btn_ClickEncryptWithMD5pictureBox1_ClickcheckBox1_CheckedChanged

其中

init:初始化方法,初始化界面。

Mail_btn_Click:获取验证码按钮事件,用于获取邮箱验证码。

timer1_Tick:计时器1计时事件,用于将计时器1的时间显示在“获取验证码”上,提示用户还有多久才能再次获取验证码。

timer2_Tick:时间2计时事件,用于计时已发送验证码的有效时间。

register_btn_Click:点击“注册”按钮的事件,具体的事件后面描述。

EncryptWithMD5:MD5加密算法方法,实际的密码经过该方法后,返回经MD5加密的密码。

pictureBox1_Click:点击图片框触发的事件,打开本地文件对话框,选择要上传的头像图片。

checkBox1_CheckedChanged:复选框改变事件,用于改变密码文本框的显示(显示密码和隐藏密码)。

主要控件及具体事件说明

  1. 图片框pictureBox1

用于触发打开文件对话框,选择要上传的新头像。

  1. “获取验证码”按钮Mail_btn

这里主要使用了自定义类MailVeriCode,详细方法上面已给出。只需要向MailVeriCode的SendMailMessage方法里传参:收件人邮箱、发件人邮箱、发件人邮箱授权码、邮件内容、邮件标题,即可完成发送。但在此之前,也需要用MailVeriCode类中的CheckMail方法检验收件人邮箱的有效性(只支持发送qq邮箱)。

同时,打开两计时器的计时,一个作为获取新验证码的时间放在“获取验证码”按钮上,另一个作为验证码的有效性计时。

  1. “注册”按钮register_btn

这里主要是判断用户所有输入信息的正确性,包括用户名是否已被注册、邮箱是否已被注册、头像是否已上传、邮箱验证码是否正确。通过连接Application数据库,查询UserData表,若用户名和邮箱均未被注册、头像已上传,且邮箱验证码正确,则将用户名、用户邮箱、新生成的ID、用户密码以及邮箱插入UserData表中,完成用户的注册。

总结说明

注册界面的主要功能:

  1. 获取邮箱验证码;
  2. 创建邮箱和用户名未被注册的用户,其中创建用户需要上传到数据库的数据包括用户id、用户名、用户邮箱、用户密码(经MD5加密后)、用户头像;
  3. 为让用户确认密码,附加显示密码复选框,用户可勾选复选框显示具体密码,也可取消勾选隐藏密码。

注册界面的较登录界面复杂一些,登录界面主要是对数据库进行查询操作,而注册界面要完成数据库的查询、插入操作。除此之外,还要完成邮箱验证码的发送和规定有效时间。

找回密码界面

界面展示

image-20211229141750940

代码

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Database
{
public partial class Forget : Form
{
public Forget()
{
InitializeComponent();
init();
}
#region 参数初始化
private void init()
{
this.MaximizeBox = false; //禁止最大化
//使标签透明化
forget_lab.BackColor = Color.Transparent;
mail_lab.BackColor = Color.Transparent;
password_lab.BackColor = Color.Transparent;
vericode_lab.BackColor = Color.Transparent;
//使按钮透明化
mail_btn.FlatStyle = FlatStyle.Flat;
mail_btn.ForeColor = Color.Transparent;
mail_btn.BackColor = Color.Transparent;
mail_btn.FlatAppearance.BorderSize = 1;
mail_btn.FlatAppearance.MouseOverBackColor = Color.Transparent;
mail_btn.FlatAppearance.MouseDownBackColor = Color.Transparent;
showpw_btn.BackColor = Color.Transparent;
}
#endregion
#region 获取验证码按钮事件
int seconds1 = 60;//倒计时60s
int seconds2 = 60 * 5;//验证码有效时间5分钟
string strMailVeriCode;
private void mail_btn_Click(object sender, EventArgs e)
{
string recEMailAddress = mail_txt.Text.Trim();//收件人邮箱
if(!mail_exist(recEMailAddress))
{
MessageBox.Show("邮箱未注册!");
return;
}
strMailVeriCode = MailVeriCode.CreateRandomMailCode(6);
string strBody = "验证码:" + strMailVeriCode + ",5分钟内有效,请勿泄漏于他人。如非本人操作,请忽略。系统邮件请勿回复。";//邮件内容
string strSubject = "【小田账本】找回密码";//邮件标题
string strMyEmailAddress = "xxxxxxxxxx@qq.com";//发件人邮箱
string strAuthorizationCode = "xxxxxxxxxxxx";//邮箱授权码
if (string.IsNullOrEmpty(recEMailAddress))//判断是否输入了邮箱
{
MessageBox.Show("请输入邮箱!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
mail_txt.Focus();
}
else if (MailVeriCode.CheckMail(recEMailAddress) == false)//判断邮箱格式是否正确
{
MessageBox.Show("您输入的QQ邮箱有误,请重新输入!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
mail_txt.Focus();
return;
}
else//发送验证码
{
//发送
if (MailVeriCode.SendMailMessage(strMyEmailAddress, recEMailAddress, strSubject, strBody, strAuthorizationCode) == true)
{
mail_btn.Enabled = false;
//计时器初始化
timer1.Interval = 1000;
timer1.Start();
timer2.Interval = 1000;
timer2.Start();
}
else
{
vericode_txt.Focus();
}
}
}
#endregion
#region 时钟1计时事件
private void timer1_Tick(object sender, EventArgs e)
{
if (seconds1 > 0)
{
seconds1--;
mail_btn.Text = "剩余" + seconds1.ToString() + "秒";
}
else
{
timer1.Stop();
mail_btn.Text = "获取验证码";
mail_btn.Enabled = true;
}
}
#endregion
#region 时钟2计时事件
private void timer2_Tick(object sender, EventArgs e)
{
if (seconds2 == 0)
{
timer2.Stop();
//旧的验证码过期,生成一个新的验证码
strMailVeriCode = MailVeriCode.CreateRandomMailCode(6);
}
}
#endregion
#region 查找邮箱存在性
private bool mail_exist(string mail)
{
string sqlconf = "Data Source = .;Initial Catalog = Application;Integrated Security = SSPI";
SqlConnection sqlConnection = new SqlConnection(sqlconf);
sqlConnection.Open();
string sql1 = "select UserMail from UserData where UserMail = '" + mail + "'";
SqlCommand sqlCommand1 = new SqlCommand(sql1, sqlConnection); //确认邮箱是否已被注册
SqlDataReader sqlDataReader = sqlCommand1.ExecuteReader();
bool a = sqlDataReader.HasRows;
sqlDataReader.Close();
sqlConnection.Close();
if (a)
{
return true; //邮箱存在
}
else
{
return false; //邮箱不存在
}
}
#endregion
#region 找回密码确认按钮事件
private void confirm_btn_Click(object sender, EventArgs e)
{
string mailVeriCode = vericode_txt.Text.Trim();//邮箱验证码
//先确认一些重要信息为非空
if (mail_txt.Text.Trim() != "" && password_txt.Text.Trim() != "")
{
string mail = mail_txt.Text.Trim();
if(!mail_exist(mail))
{
MessageBox.Show("邮箱未注册!");
return;
}
if (string.IsNullOrEmpty(mailVeriCode) == true)
{
MessageBox.Show("请输入验证码", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
vericode_txt.Focus();
return;
}
else if (mailVeriCode.ToLower() != strMailVeriCode.ToLower())//判断邮箱验证码是否输入正确;不区分字母大小写
{
MessageBox.Show("您输入的验证码有误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
vericode_txt.Focus();
return;
}
string sqlconf = "Data Source = .;Initial Catalog = Application;Integrated Security = SSPI";
SqlConnection sqlConnection = new SqlConnection(sqlconf);
string password = EncryptWithMD5(password_txt.Text.Trim());
sqlConnection.Open();
string sqlStr = "update UserData Set UserPassword = '" + password + "'where UserMail = '"+ mail +"'";
SqlCommand cmd = new SqlCommand(sqlStr, sqlConnection);
cmd.ExecuteNonQuery();
MessageBox.Show("修改密码成功!");
sqlConnection.Close();
this.Close();
}
else if (mail_txt.Text.Trim() == "" && password_txt.Text.Trim() != "")
{
MessageBox.Show("请填写邮箱!","提示");
mail_txt.Focus();
}
else if (mail_txt.Text.Trim() != "" && password_txt.Text.Trim() == "")
{
MessageBox.Show("请填写新密码!", "提示");
password_txt.Focus();
}
else
{
MessageBox.Show("请完善相关信息!", "提示");
mail_txt.Focus();
}
}
#endregion
#region MD5加密算法
private string EncryptWithMD5(string source)//MD5加密
{
byte[] sor = Encoding.UTF8.GetBytes(source);
MD5 md5 = MD5.Create();
byte[] result = md5.ComputeHash(sor);
StringBuilder strbul = new StringBuilder(40);
for (int i = 0; i < result.Length; i++)
{
strbul.Append(result[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位
}
return strbul.ToString();
}
#endregion
#region 显示/隐藏密码
private void showpw_btn_CheckedChanged(object sender, EventArgs e)
{
if (showpw_btn.Checked)
{
//复选框被勾选,显示密码
password_txt.PasswordChar = new char();
}
else
{
//复选框为被勾选,隐藏密码
password_txt.PasswordChar = '*';
}
}
#endregion
}
}

方法概述

主要方法:initmail_btn_Clicktimer1_Ticktimer2_Tickconfirm_btn_ClickEncryptWithMD5showpw_btn_CheckedChangedmail_exist

其中

init:初始化方法,初始化界面。

mail_btn_Click:获取验证码按钮事件,用于获取邮箱验证码。

timer1_Tick:计时器1计时事件,用于将计时器1的时间显示在“获取验证码”上,提示用户还有多久才能再次获取验证码。

timer2_Tick:时间2计时事件,用于计时已发送验证码的有效时间。

confirm_btn_Click:点击“确认”按钮的事件,具体的事件后面描述。

EncryptWithMD5:MD5加密算法方法,实际的密码经过该方法后,返回经MD5加密的密码。

showpw_btn_CheckedChanged:复选框改变事件,用于改变密码文本框的显示(显示密码和隐藏密码)。

mail_exist:连接数据库,查询账户邮箱的存在性。

主要控件及具体事件说明

  1. “获取验证码”按钮mail_btn

这里就不再赘述了,和注册界面的“获取验证码”按钮出发的事件完全一样,就是将邮件的标题改为“找回密码”。

  1. “确认”按钮confirm_btn

首先,连接Application数据库通过UserData表确认邮箱是否已注册,若已注册,则检查输入验证码的正确性,若上述信息均正确,则将输入的新密码更新到UserData表中相应邮箱的用户密码。

总结说明

找回界面的主要功能:

  1. 获取邮箱验证码;
  2. 通过验证账户邮箱的存在性和验证码的正确性,连接数据库进行用户密码更新操作。

找回密码的实现和注册账户差不多,可以说大部分代码可以直接从注册界面照搬,唯一的区别在于,找回密码需要的数据交互更少,不需要向数据库表插入数据,只需要查询数据和更新数据。

主界面

界面展示

在这里插入图片描述

代码

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Database
{
public partial class Main : Form
{
private static float inmoney;
private static float outmoney;
public static string pathPicture;
static string userid;
static string username;
static string usermail;
static string password;
public Main()
{
InitializeComponent();
}
#region 有参构造窗体初始化
public Main(string Username)
{
InitializeComponent();
init(Username);
}
#endregion
#region 初始化参数和导入数据库数据
public void init(string Username)
{
inmoney = 0;
outmoney = 0;
this.MaximizeBox = false; //禁止最大化
//创建数据库连接对象
string sqlconf = "Data Source = .;Initial Catalog = Application;Integrated Security = SSPI";
SqlConnection sqlConnection = new SqlConnection(sqlconf);
sqlConnection.Open();
string sqlStr = "select *from UserData where UserName = '" + Username + "'";
SqlCommand cmd = new SqlCommand(sqlStr, sqlConnection);
SqlDataReader sqlDataReader = cmd.ExecuteReader();
//获取查询到的内容
if (sqlDataReader.Read())
{
usermail = sqlDataReader["UserMail"].ToString().Trim();
userid = sqlDataReader["ID"].ToString();
username = Username;
password = sqlDataReader["UserPassword"].ToString();
}
sqlDataReader.Close();
//加载用户信息
id_lab.Text = userid;
username_lab.Text = username;
mail_lab.Text = usermail;
//加载头像
sqlStr = "select UserPhoto from UserData where UserName = '" + Username + "'";
cmd = new SqlCommand(sqlStr, sqlConnection);
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
sqlDataAdapter.Fill(ds, "UserData");
int c = ds.Tables["UserData"].Rows.Count;
if (c > 0)
{
Byte[] mybyte = new byte[0];
mybyte = (Byte[])(ds.Tables["UserData"].Rows[c - 1]["UserPhoto"]);
MemoryStream ms = new MemoryStream(mybyte);
pictureBox1.Image = Image.FromStream(ms);
}
else
pictureBox1.Image = null;
//加载记账数据
sqlStr = "select *from Account where UserID = '" + Main.userid + "'";
cmd = new SqlCommand(sqlStr, sqlConnection);
sqlDataReader = cmd.ExecuteReader();
while(sqlDataReader.Read())
{
string inorout_tmp = sqlDataReader["InOrOut"].ToString().Trim();
string conType_tmp = sqlDataReader["ConsumeType"].ToString();
float price_tmp = (float)Convert.ToDouble(sqlDataReader["Price"]);
string note_tmp = sqlDataReader["NOTE"].ToString();
string time_tmp = Convert.ToDateTime(sqlDataReader["RecordDate"]).Date.ToString("D");
if(inorout_tmp.Equals("收入"))
{
income_list.Items.Add(conType_tmp + "\t" + price_tmp.ToString() + "元\t" + time_tmp + "\t" + note_tmp);
innum_lab.Text = income_list.Items.Count.ToString();
inmoney = inmoney + price_tmp;
inmoney_lab.Text = Convert.ToString(inmoney);
}
else
{
expend_list.Items.Add(conType_tmp + "\t" + price_tmp.ToString() + "元\t" + time_tmp + "\t" + note_tmp);
outnum_lab.Text = expend_list.Items.Count.ToString();
outmoney = outmoney + price_tmp;
outmoney_lab.Text = Convert.ToString(outmoney);
}
}
sqlConnection.Close();
}
#endregion
#region 收入单选按钮事件
private void income_btn_CheckedChanged(object sender, EventArgs e)
{
matter_box.Items.Clear();
matter_box.Items.Add("工资");
matter_box.Items.Add("理财");
matter_box.Items.Add("礼金");
matter_box.Items.Add("其他");
matter_box.Text = "";
}
#endregion
#region 支出单选按钮事件
private void expend_btn_CheckedChanged(object sender, EventArgs e)
{
matter_box.Items.Clear();
matter_box.Items.Add("餐饮");
matter_box.Items.Add("购物");
matter_box.Items.Add("交通");
matter_box.Items.Add("娱乐");
matter_box.Items.Add("医疗");
matter_box.Items.Add("彩票");
matter_box.Items.Add("旅行");
matter_box.Items.Add("运动");
matter_box.Items.Add("通讯");
matter_box.Items.Add("社交");
matter_box.Items.Add("学习");
matter_box.Items.Add("其他");
matter_box.Text = "";
}
#endregion
#region 记录按钮事件
private void record_btn_Click(object sender, EventArgs e)
{
//创建数据库连接对象
string sqlconf = "Data Source = .;Initial Catalog = Application;Integrated Security = SSPI";
SqlConnection sqlConnection = new SqlConnection(sqlconf);
string inorout;
if (income_btn.Checked == true)
{
income_list.Items.Add(matter_box.Text+"\t"+money_txt.Text+"元\t"+ dateTimePicker1.Text+"\t"+note_txt.Text);
innum_lab.Text = income_list.Items.Count.ToString();
inmoney = inmoney + float.Parse(money_txt.Text);
inmoney_lab.Text = Convert.ToString(inmoney);
inorout = "收入";
}
else
{
expend_list.Items.Add(matter_box.Text + "\t" + money_txt.Text + "元\t" + dateTimePicker1.Text + "\t" + note_txt.Text);
outnum_lab.Text = expend_list.Items.Count.ToString();
outmoney_lab.Text = Convert.ToString(float.Parse(outmoney_lab.Text) + float.Parse(money_txt.Text));
inorout = "支出";
}
//更新数据库
string consumeType = matter_box.Text;
string note = note_txt.Text;
float price = float.Parse(money_txt.Text.Trim());
string time = dateTimePicker1.Text;
sqlConnection.Open();
string insertStr = "insert into Account(UserID,InOrOut,ConsumeType,NOTE,Price,RecordDate)" + "values('" + userid + "','" + inorout + "','" + consumeType + "','" + note + "','" + price + "','" + Convert.ToDateTime(time) + "')";
SqlCommand cmd = new SqlCommand(insertStr, sqlConnection);
cmd.ExecuteNonQuery();
sqlConnection.Close();
}
#endregion
#region 点击头像事件(修改头像)
private void pictureBox1_Click(object sender, EventArgs e)
{
//设置文件对话框显示的初始目录为系统桌面
this.openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
//设置当前选定筛选器字符串以决定对话框中“文档类型”选项
this.openFileDialog1.Filter = "bmp文件(*.bmp)*.bmpgif文件(*.gif)*.gifjpeg文件(*.jpg)*.jpgpng文件(*.png)*.png";
//设置对话框中当前选定筛选器的索引
this.openFileDialog1.FilterIndex = 3;
//关闭对话框,还原当前目录
this.openFileDialog1.RestoreDirectory = true;
//设置对话框标题
this.openFileDialog1.Title = "选择头像照片";
if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
{
Byte[] picbyte = new byte[0];
pathPicture = this.openFileDialog1.FileName;
FileStream fs = new FileStream(pathPicture, FileMode.Open, FileAccess.Read);
picbyte = new byte[fs.Length];
fs.Read(picbyte, 0, picbyte.Length);
pictureBox1.Image = Image.FromStream(fs);
fs.Close();
//连接数据库,更换头像信息(因为不会更新sql server的二进制数据流,用了一个比较笨的方法:直接删除重写账号信息)
string sqlconf = "Data Source = .;Initial Catalog = Application;Integrated Security = SSPI";
SqlConnection sqlConnection = new SqlConnection(sqlconf);
string userid = id_lab.Text.Trim();
sqlConnection.Open();
string sqlStr = "delete from UserData where ID = '" +userid+ "'";
SqlCommand sqlCommand = new SqlCommand(sqlStr,sqlConnection);
sqlCommand.ExecuteNonQuery();
sqlStr = "insert into UserData(ID,UserPassword,UserMail,UserName,UserPhoto)" + "values (@userid, @userpassword,@usermail,@username,@userphoto)";
SqlCommand command = new SqlCommand(sqlStr, sqlConnection);
SqlParameter sqlParameter = new SqlParameter("@userid", userid);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@userpassword", password);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@usermail", usermail);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@username", username);
command.Parameters.Add(sqlParameter);
sqlParameter = new SqlParameter("@userphoto", System.Data.SqlDbType.Image);
sqlParameter.Value = picbyte;
command.Parameters.Add(sqlParameter);
command.ExecuteNonQuery();
sqlConnection.Close();
MessageBox.Show("更改头像成功!");
}
}
#endregion
#region 关闭窗口事件
private void Main_FormClosed(object sender, FormClosedEventArgs e)
{
System.Environment.Exit(0);
}
#endregion
}
}

方法概述

主要方法:

initincome_btn_CheckedChangedexpend_btn_CheckedChangedrecord_btn_ClickpictureBox1_ClickMain_FormClosed

其中

init:初始化方法,初始化界面,以及将用户数据从数据库导入。

income_btn_CheckedChangedexpend_btn_CheckedChanged:单选框选择改变事件,用于更新下拉栏的内容。

record_btn_Click:“记录”按钮事件,具体的事件后面描述。

pictureBox1_Click:点击图片框触发的事件,打开本地文件对话框,选择要上传的头像图片,用于更改头像。

Main_FormClosed:窗口关闭事件,当窗口关闭后,使所有程序结束运行。

主要控件及具体事件说明

  1. “记录按钮record_btn”

当点击”记录按钮后”,会将用户选择的记录类型、金额、时间以及备注附加到相应种类的ListBox上,并连接Application数据库,将当前用户ID和记账的数据添加到Account表上。

  1. 图片框PictureBox

用于触发打开文件对话框,选择要上传的新头像。选择成功后,删除UserData上对应用户的数据,重新给该用户创建新账号,附上新头像(其他信息不变)(感觉这是比较笨的方法了,但是本人初学数据库太菜,不知道怎么单独更新照片的数据)。

总结说明

这是所有窗体中,需要实现数据库交互功能最多的窗体(但是论实现的功能和复杂性,还是比注册界面略逊一筹),也是整个系统主要的窗体,用于实现记账功能。其中需要与数据库的进行数据交互的操作就有:查询、插入、删除。

个人认为不足的是没有删除记录项的功能,这导致整个系统很累赘,但由于时间关系,最终没能完善,有能力的各位大佬可以再次基础上继续完善,日后我自己若是有机会完善的话会再次更新。

【参考资料】

数据库大作业-代码展示(C#).
C# 数据库大作业-学生管理系统.
ASP.NET(C#)操作SQL Server数据库.
SQL 菜鸟教程.

工程文件下载

链接:https://pan.baidu.com/s/1xtowjV39Ara3ozPuAW9YQw
提取码:7gju