数据库大作业——基于C#和SQL Server的简单日常记账系统

功能演示视频

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

SQL代码

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

代码
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类性值。

登录界面

界面展示

在这里插入图片描述

代码

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

代码

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 = "xxxxxxxxxxxxxxxx";//邮箱授权码

            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)|*.bmp|gif文件(*.gif)|*.gif|jpeg文件(*.jpg)|*.jpg|png文件(*.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

代码

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. 通过验证账户邮箱的存在性和验证码的正确性,连接数据库进行用户密码更新操作。

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

主界面

界面展示

在这里插入图片描述

代码

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)|*.bmp|gif文件(*.gif)|*.gif|jpeg文件(*.jpg)|*.jpg|png文件(*.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 | 菜鸟教程.

  • 9
    点赞
  • 129
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
C# + SQL Server系统包括两类用户:学生、管理员。管理员可以通过系统来添加管理员信息、修改管理员信息、添加学生信息、修改学生信息;开设课程、查询课程、录入成绩、统计成绩 管理系统是一种通过计算机技术实现的用于组织、监控和控制各种活动的软件系统。这些系统通常被设计用来提高效率、减少错误、加强安全性,同时提供数据和信息支持。以下是一些常见类型的管理系统: 学校管理系统: 用于学校或教育机构的学生信息、教职员工信息、课程管理、成绩记录、考勤管理等。学校管理系统帮助提高学校的组织效率和信息管理水平。 人力资源管理系统(HRM): 用于处理组织内的人事信息,包括员工招聘、培训记录、薪资管理、绩效评估等。HRM系统有助于企业更有效地管理人力资源,提高员工的工作效率和满意度。 库存管理系统: 用于追踪和管理商品或原材料的库存。这种系统可以帮助企业避免库存过剩或不足的问题,提高供应链的效率。 客户关系管理系统(CRM): 用于管理与客户之间的关系,包括客户信息、沟通记录、销售机会跟踪等。CRM系统有助于企业更好地理解客户需求,提高客户满意度和保留率。 医院管理系统: 用于管理医院或医疗机构的患者信息、医生排班、药品库存等。这种系统可以提高医疗服务的质量和效率。 财务管理系统: 用于记录和管理组织的财务信息,包括会计凭证、财务报表、预算管理等。财务管理系统

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

到底文不文SAMA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值