基于COM技术的ATM自动存款机实现(C#版)
说明:程序是在.NET 4.0下用C#语言编写
COM组件也是用C#编写
一. 功能需求:
1. 能存款
2. 能取款
3. 能查询余额及交易明细
4. 能转账
5. 能修改密码
二. 总体设计计:
1. 客户端设计:
(1) Admin类(登录窗体)
(2) MainForm类(主窗体)
(3) WithDraw类(取款窗体)
(4) Deposit类(存款窗体)
(5) TransForm类(转账窗体)
(6) Query类(查询余额及明细窗体)
(7) ModifyPwd类(修改密码窗体)
(8) OpenAccountInfo类(开户窗体)
2. COM组件端的函数设计:
(1) 连接数据库函数组
A. ReadSqlConnString()(读取连接字符串)
B. GetConnection()(打开与数据库的连接)
C. Con_Close()(关闭与数据库的连接)
D. GetDataSet()(将查询结果表填充在DataSet中)
(2) Proc_Admin()(用户登录验证)
(3) Manager_Admin(管理员登录验证)
(4) WhithDraw()(取款)
(5) Deposit()(存款)
(6) TransForm()(转账)
(7) Query()(查询)
(8) ModifyPwd()(修改密码)
(9) Proc_CreateAccount()(开户)
3. 数据库设计:
(1) 表设计
userInfo(用户信息表)
cardInfo(卡信息表)
transInfo(交易信息表)
manager(管理员信息表)
(2) 增加约束条件
如:设置主键
电话号码必须为数字
身份证号为18位
存款类型:活期、定期及定活两便三者之一
等约束。
(3) 创建存储过程
(proc_createAccount)开户存储过程
(proc_takeMoney)存取款存储过程
(proc_randCardNo)随机产生卡号存储过程
(proc_tranMoney)转账存储过程
三. 详细设计:
1. 客户端设计:
(1) 登录窗体
(2)主窗体
(3)取款窗体
(4)存款窗体
(5)查询余额及明细窗体
(6)修改密码窗体
(7)开户窗体
2.COM组件端的函数设计:
(8) 连接数据库函数组
A. ReadSqlConnString()(读取连接字符串)
#region 读取连接字符串 ///<summary> /// ///</summary> public static string ReadSqlConString() { StreamReader sr = new StreamReader("DatabaseLinkString.ini"); string DatabaseLinkString = ""; while (!sr.EndOfStream) { DatabaseLinkString += sr.ReadLine(); } sr.Close(); return DatabaseLinkString; } #endregionB.GetConnection()(打开与数据库的连接)#region 打开与数据库的连接 ///<summary> ///打?¨°开a与??数oy据Y库a的ì?连¢?接¨? ///</summary> ///<returns>返¤|ì回?SqlConnection对?象¨?</returns> public static SqlConnection GetConnection() { try { // StrConn = ReadSqlConString1(ID,Pwd); StrConn = ReadSqlConString(); conn = new SqlConnection(StrConn); //用??SqlConnection对? if (conn.State != ConnectionState.Open) conn.Open(); //打开与数据库的连接¨? return conn; //返¤|ì回?SqlConnection对?象¨? } catch (Exception ex) { throw new Exception(ex.Message); } } #endregionC.Con_Close()(关闭与数据库的连接) #region 关?闭à?数oy据Y库a连¢?接¨? ///<summary> ///关?闭à?数oy据Y库a连¢?接¨? ///</summary> public void Con_Close() { if (conn.State == ConnectionState.Open) //判D断?数oy据Y库a是o?否¤ { conn.Close(); //关?闭à?数oy据Y库a连¢?接¨? conn.Dispose(); //释o¨a放¤?My_con } } #endregionD.GetDataSet()(将查询结果表填充在DataSet中)#region 执??行DSQL语??句?,返¤|ì回?DataSet对?象¨? /// <summary> /// 执??行DSQL语??句?,返¤|ì回?DataSet对?象¨? /// </summary> /// <param name="sql">Sql语??句?</param> /// <returns>DataSet对?象¨?</returns> public DataSet GetDataSet(string sql) { GetConnection(); try { var da = new SqlDataAdapter(sql, conn); var ds = new DataSet(); // da.Fill(ds); Con_Close(); return ds; } catch (Exception ex) { throw new Exception(ex.Message); } finally { Con_Close(); } } #endregion(9)Proc_Admin()(用户登录验证) #region 用户登录验证 /// <summary> /// 用??户?ì登ì?录?验¨|证?è /// </summary> /// <param name="userName">用??户?ì名?</param> /// <param name="passWord">密¨1码?</param> /// <param name="cardId">卡?§号?</param> /// <returns>返¤|ì回?值|ì为a 1 则¨°登ì?录?成¨|功|,ê?为a 0 则¨°登ì?录?失o?ì败?¨1</returns> public DataTable Proc_Admin(string userName, string passWord, string cardId) { string sql = "Select * from cardInfo where cardID = '" + cardId + "' and password = '" + passWord + "'"; var getds = GetDataSet(sql); var dt = getds.Tables[0]; return dt; } #endregion(10)Manager_Admin(管理员登录验证) #region 管¨1理¤¨a员?à登ì?录?验¨|证?è /// <summary> /// 管¨1理¤¨a员?à登ì?录?验¨|证?è /// </summary> /// <param name="userName">管¨1理¤¨a员?à名?</param> /// <param name="passWord">密¨1码?</param> /// <param name="cardId">卡?§号?</param> /// <returns>返¤|ì回?值|ì为a 1 则¨°登ì?录?成¨|功|,ê?为a 0 则¨°登ì?录?失o?ì败?¨1</returns> public DataTable Manager_Admin(string managerName, string passWord, string cardId) { string sql = "Select * from manager where managerId = '" + cardId + "' and managePwd = '" + passWord + "' and managerName = '"+managerName+"'"; var getds = GetDataSet(sql); var dt = getds.Tables[0]; return dt; } #endregion (11)Proc_TransForm(转账) #region 转áa账? /// <summary> /// 转áa账? /// </summary> /// <param name="fromCardId">转áa出?的ì?卡?§号?</param> /// <param name="toCardId">转áa入¨?的ì?卡?§号?</param> /// <param name="money">金e额?</param> /// <param name="passWord">转áa出?卡?§的ì?密¨1码?</param> /// <returns>返¤|ì回?存??储??é过y程¨?的ì?提?¨¢示o?信?息?é</returns> public string Proc_TransForm(string fromCardId,string toCardId,string money,string passWord) { string message = "";//提?¨¢示o?信?息?é try { GetConnection(); cmd = new SqlCommand("proc_tranMoney", conn); //设|¨¨置?命¨1令¢?的ì?类¤¨¤型¨a为a存??储??é过y程¨? cmd.CommandType = CommandType.StoredProcedure; //设|¨¨置?参?数oy cmd.Parameters.Add("@fromCard", SqlDbType.VarChar,19).Value = fromCardId; //注á?é意°a输o?出?参?数oy要°a设|¨¨置?大?¨?小?,否¤?则¨°size默?认¨?为a0, cmd.Parameters.Add("@toCard", SqlDbType.VarChar, 19).Value = toCardId; cmd.Parameters.Add("@money", SqlDbType.Money).Value = money; cmd.Parameters.Add("@pass", SqlDbType.VarChar, 6).Value = passWord; cmd.Parameters.Add("@Tips", SqlDbType.NVarChar, 50); //设|¨¨置?参?数oy的ì?类¤¨¤型¨a为a输o?出?参?数oy,默?认¨?情¨|况?下?是o?输o?入¨?, cmd.Parameters["@Tips"].Direction = ParameterDirection.Output; //执??行D cmd.ExecuteNonQuery(); //得ì?到ì?输o?出?参?数oy的ì?值|ì,把??赋3值|ì给?name,注á?é意°a,这a里¤?得ì?到ì?的ì?是o?object类¤¨¤型¨a的ì?,要°a进?行D相¨¤应?|的ì?类¤¨¤型¨a轮?换? message = cmd.Parameters["@Tips"].Value.ToString(); Con_Close(); } catch (Exception ex) { throw new Exception(ex.Message); } return message; } #endregion(11)WhithDraw()(取款) #region 取¨?款? /// <summary> /// 取¨?款? /// </summary> /// <param name="cardId">卡?§号?</param> /// <param name="balance">金e额?</param> /// <param name="passWord">密¨1码?</param> /// <returns>返¤|ì回?查¨|询?¥的ì?表à¨a</returns> public DataTable WithDraw(string cardId, string balance,string passWord) { //查¨|询?¥的ì?Sql语??句? string sql = "EXEC proc_takeMoney '" + cardId + "'," + balance + ",'支?ì出?','" + passWord + "'"; sql += "Select * from cardInfo where cardID = '" + cardId + "'"; var getds = GetDataSet(sql); //将?查¨|询?¥结¨¢果?存??入¨?DataSet对?象¨?中D var dt = getds.Tables[0]; //将?查¨|询?¥结¨¢果?存??入¨?DataTable对?象¨?中D return dt; } #endregion(12)Deposit(存款) #region 存??款? /// <summary> /// 存??款? /// </summary> /// <param name="cardId">卡?§号?</param> /// <param name="balance">金e额?</param> /// <returns>返¤|ì回?查¨|询?¥表à¨a</returns> public DataTable Deposit(string cardId, string balance) { //Sql查¨|询?¥语??句? string sql = "Update cardInfo set balance = balance +" + balance + " where cardID = '" + cardId + "'"; sql += " Insert Into transInfo(transType,cardID,transMoney) values ('存??入¨?','" + cardId + "'," + balance + ")"; sql += " Select * from cardInfo where cardID = '" + cardId + "'"; var getds = GetDataSet(sql); var dt = getds.Tables[0]; return dt; } #endregion(13)Query(查询) #region 查¨|询?¥ /// <summary> /// 查¨|询?¥ /// </summary> /// <param name="tableType">查¨|询?¥那?个?表à¨a</param> /// <param name="cardID">卡?§号?</param> /// <returns>查¨|询?¥结¨¢果?</returns> public DataTable QueryData(string tableType, string cardID) { string sql; if (tableType == "cardInfo")//查¨|询?¥卡?§信?息?é表à¨a { sql = "Select customerID 客¨a户?ì编ਤ号?,balance 账?户?ì余?¨¤额?,cardType 币ਰ种?,savingType 存??款?类¤¨¤型¨a,openDate 开a户?ì日¨?期¨2" + " from " + tableType + " where cardID = '" + cardID + "'"; } else //查¨|询?¥transInfo (转áa账?及??余?¨¤额?表à¨a)ê? { sql = "Select transID 交?易°?á明??细?编ਤ号?,transDate 交?易°?á日¨?期¨2,transType 交?易°?á类¤¨¤型¨a,transMoney 交?易°?á金e额?" + " from " + tableType + " where cardID = '" + cardID + "' Order by transID"; } var connect = new ConnectDB(); var dt = connect.GetDataSet(sql).Tables[0]; return dt; } #endregion(14)ModifyPwd(修改密码) #region 修T改?密¨1码? /// <summary> /// 修T改?密¨1码? /// </summary> /// <param name="cardId">卡?§号?</param> /// <param name="newPwd">新?密¨1码?</param> /// <returns>返¤|ì回?查¨|询?¥设|¨¨定?§新?密¨1码?的ì?结¨¢果?</returns> public DataTable ModifyPwd(string cardId, string newPwd) { string sql = "Update cardInfo set password = '" + newPwd + "' where cardID = '" + cardId + "'"; sql += " Select * from cardInfo where cardID = '" + cardId + "' and password = '" + newPwd + "'"; var getds = GetDataSet(sql); var dt = getds.Tables[0]; return dt; } #endregion(15)Proc_CreateAccount(开户) #region 开a户?ì public string Proc_CreateAccount(string customerName, string customerPid, string telephone, string openMoney, string savingType, string address) { string message = "";//提?¨¢示o?信?息?é try { GetConnection(); cmd = new SqlCommand("proc_createAccount", conn); //设|¨¨置?命¨1令¢?的ì?类¤¨¤型¨a为a存??储??é过y程¨? cmd.CommandType = CommandType.StoredProcedure; //设|¨¨置?参?数oy cmd.Parameters.Add("@customerName", SqlDbType.VarChar, 10).Value = customerName; //注á?é意°a输o?出?参?数oy要°a设|¨¨置?大?¨?小?,否¤?则¨°size默?认¨?为a0, cmd.Parameters.Add("@customerPID", SqlDbType.VarChar, 18).Value = customerPid; cmd.Parameters.Add("@telephone", SqlDbType.VarChar,13).Value = telephone; cmd.Parameters.Add("@openMoney", SqlDbType.Money).Value = openMoney; cmd.Parameters.Add("@savingType", SqlDbType.VarChar, 8).Value = savingType; cmd.Parameters.Add("@address", SqlDbType.VarChar, 50).Value = address; cmd.Parameters.Add("@Tips", SqlDbType.NVarChar, 500); //设|¨¨置?参?数oy的ì?类¤¨¤型¨a为a输o?出?参?数oy,默?认¨?情¨|况?下?是o?输o?入¨?, cmd.Parameters["@Tips"].Direction = ParameterDirection.Output; //执??行D cmd.ExecuteNonQuery(); //得ì?到ì?输o?出?参?数oy的ì?值|ì,把??赋3值|ì给?name,注á?é意°a,这a里¤?得ì?到ì?的ì?是o?object类¤¨¤型¨a的ì?,要°a进?行D相¨¤应?|的ì?类¤¨¤型¨a轮?换? message = cmd.Parameters["@Tips"].Value.ToString(); Con_Close(); } catch (Exception ex) { throw new Exception(ex.Message); } return message; } #endregion
3.数据库设计:
(1) 表设计
A.userInfo(用户信息表)
B.cardInfo(卡信息表)
C.transInfo(交易信息表)
D.manager(管理员信息表)
(2) 增加约束条件
A.为UserInfo表增加约束
--为UserInfo表增加约束ALTER TABLE UserInfo --为UserInfo表customerID列增加主键约束 ADD constraint PK_ID PRIMARY KEY(customerID), --为customerPID列增加检查约束,用到系统函数len() constraint CK_PID CHECK(len(customerPID) = 15 or len(customerPID) = 18), --确保身份证列数据唯一 constraint UQ_PID UNIQUE(customerPID), --约束电话号码为-2530102或 constraint CK_TELEPHONE CHECK(telephone like '[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9][0-9]' or telephone like '1[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]') GO
B.为cardInfo表增加约束
--为cardInfo表增加约束ALTER TABLE cardInfo ADD CONSTRAINT PK_CARDID PRIMARY KEY(cardID), CONSTRAINT CK_CARDID CHECK(cardID like '1234[0-9][0-9][0-9][0-9]'), --增加默认约束,默认值为“RMB” CONSTRAINT DF_CARDTYPE DEFAULT('RMB') FOR cardType, --增加检查约束,用IN限制可以输入的数据 CONSTRAINT CK_SAVINGTYPE CHECK(savingType IN ('活期','定活两便','定期')), --调用日期函数获得当前时间设为默认值 CONSTRAINT DF_OPENTYPE DEFAULT(GETDATE()) FOR openDate, CONSTRAINT CK_OPENMONEY CHECK(openMoney>1), CONSTRAINT CK_BALANCE CHECK(balance>1), CONSTRAINT CK_PASS CHECK(passWord like '[0-9][0-9][0-9][0-9][0-9][0-9]'), CONSTRAINT DF_PASS DEFAULT('888888') FOR password, CONSTRAINT DF_LOSS DEFAULT(0) FOR isLossbit, CONSTRAINT FK_customerID FOREIGN KEY(customerID) REFERENCES UserInfo(customerID) GO
C.为表transInfo表增加约束
--为表transInfo表增加约束ALTER TABLE transInfo ADD CONSTRAINT PK_ID PRIMARY KEY(transID), --增加检查约束,设置该列的值只能是“存入","支出” CONSTRAINT CK_TRANSTYPE CHECK(transType IN ('存入','支出')), CONSTRAINT CK_TRANSMONEY CHECK(transMoney>1), CONSTRAINT DF_TRANSDATE DEFAULT(GETDATE()) FOR transDate, --增加外键约束,transInfo表的cardID受cardID的cardID列约束 CONSTRAINT FK_CARDID FOREIGN KEY(cardID) REFERENCES cardInfo(cardID) GO
(3) 创建存储过程
(proc_createAccount)开户存储过程--系统对象中存在此存储过程名则删除if exists(SELECT * FROM sysobjects WHERE name = 'proc_createAccount') drop PROC proc_createAccount GO--创建卡户存储过程CREATE PROC proc_createAccount @customerName char(10),@customerPID char(18),@telephone varchar(13) ,@openMoney money,@savingType char(8),@address varchar(50) = '' ,@Tips varchar(500)='' outputAS --声明局部变量 DECLARE @mycardID nvarchar(19),@cur_customerID int --调用产生随机卡号的存储过程,生成随机卡号 EXEC dbo.proc_randCardNo @mycardID output --产生的卡号已存在,就一直产生,知道没有重复 while exists(SELECT * FROM cardInfo WHERE cardID = @mycardID) EXEC proc_randCardNo @mycardID output SET @Tips = '尊敬的用户,您开户成功!系统为您产生的随机卡号为:'+@mycardID + '开户日期:'+CONVERT(char(10),getdate(),111) + '开户金额:'+CONVERT(varchar(20),@openMoney) PRINT @Tips IF not exists(SELECT * FROM UserInfo WHERE customerPID = @customerPID) BEGIN --插入客户信息 INSERT INTO UserInfo(customerName,customerPID,telephone,address) VALUES(@customerName,@customerPID,@telephone,@address) --查询出最后一次插入的自增列值 SELECT @cur_customerID = @@IDENTITY FROM UserInfo --插入卡信息 INSERT INTO cardInfo(cardID,savingType,openMoney,balance,customerID) VALUES(@mycardID,@savingType,@openMoney,@openMoney,@cur_customerID) --插入明细账 INSERT INTO transInfo(transType,cardID,transMoney) VALUES('存入',@mycardID,@openMoney) ENDGO (proc_takeMoney)存取款存储过程if exists(select * from sysobjects where name = 'proc_takemoney') drop proc proc_takemoneygocreate procedure proc_takeMoney(@card char(19),@money money,@type char(4),@passWord char(6)='')AS print '交易正在进行,请稍后...' if((select isLoss From carcInfo where cardID = @card) = 1) --检测卡是否被锁定 begin raiserror('卡已被锁定,请联系工作人员',16,1) return end if(@type = '支出') if((select passWord from cardID where cardID = @card) <> @passWord) --检测密码是否正确 begin raiserror('密码错误后卡已被锁定!',16,1) return end DECLARE @balance money select @balance = balance From cardID where cardID = @card if(@type = '支出') if(@balance > @money+1) --判断余额是否足够 update cardInfo set balance = balance - @money where cardID = @card else begin raiserror('余额不足!不能交易!',16,1) print '卡号'+@card+' 余额:'+convert(varchar(20),@balance) return end else update cardInfo set balance = balance +@money where cardID = @card INSERT INTO transInfo(transType,cardID,transMoney) VALUES(@type,@card,@money) --插入交易明细 print '交易成功! 交易金额:'+convert(varchar(20),@money) select @balance = balance from cardInfo where cardID = @card print '卡号'+@card+' 的余额是:'+convert(varchar(20),@balance) GO(proc_randCardNo)随机产生卡号存储过程--系统对象中存在此存储过程名则删除if exists(SELECT * FROM sysobjects WHERE name = 'proc_randCardNo') drop PROC proc_randCardNoGO--创建产生卡号的存储过程CREATE PROC proc_randCardNo(@randCardID nvarchar(19) output)ASDECLARE @str char(10)DECLARE @r numeric(10,8)--在单个查询中反复调用RAND()函数将产生相同的值,所以使用rand([seed])时,每次要修改种子seed的值--才能得出不一样的随机数--这里采用毫秒做种子来取随机数SELECT @r = RAND(DATEPART(ms,GETDATE()))SELECT @str = CAST(@r as CHAR(10)) --转化为char型--截取.XXXXXXXX的小数点后-4为组成为规定格式的卡号SET @randCardID = '1234'+SUBSTRING(@str,3,4)GO(proc_tranMoney)转账存储过程 --系统对象中存在此存储过程名则删除if exists(SELECT * FROM sysobjects WHERE name = 'proc_createAccount') drop PROC proc_createAccount GO--创建卡户存储过程CREATE PROC proc_createAccount @customerName char(10),@customerPID char(18),@telephone varchar(13) ,@openMoney money,@savingType char(8),@address varchar(50) = '' ,@Tips varchar(500)='' outputAS --声明局部变量 DECLARE @mycardID nvarchar(19),@cur_customerID int --调用产生随机卡号的存储过程,生成随机卡号 EXEC dbo.proc_randCardNo @mycardID output --产生的卡号已存在,就一直产生,知道没有重复 while exists(SELECT * FROM cardInfo WHERE cardID = @mycardID) EXEC proc_randCardNo @mycardID output SET @Tips = '尊敬的用户,您开户成功!系统为您产生的随机卡号为:'+@mycardID + '开户日期:'+CONVERT(char(10),getdate(),111) + '开户金额:'+CONVERT(varchar(20),@openMoney) PRINT @Tips IF not exists(SELECT * FROM UserInfo WHERE customerPID = @customerPID) BEGIN --插入客户信息 INSERT INTO UserInfo(customerName,customerPID,telephone,address) VALUES(@customerName,@customerPID,@telephone,@address) --查询出最后一次插入的自增列值 SELECT @cur_customerID = @@IDENTITY FROM UserInfo --插入卡信息 INSERT INTO cardInfo(cardID,savingType,openMoney,balance,customerID) VALUES(@mycardID,@savingType,@openMoney,@openMoney,@cur_customerID) --插入明细账 INSERT INTO transInfo(transType,cardID,transMoney) VALUES('存入',@mycardID,@openMoney) ENDGO
四. 测试与实现:
五. 总结:
通过对基于COM组件技术的练习,是我对COM组件的通用性有了进一步的了解,这样的技术的使用为代码更高级别的重用提供了良好的契机。同时,我也掌握了用.NET下C#语言创建组件及ADO.NET技术连接并操作数据库的技术,是我受益匪浅
本程序的优点:实现了ATM的基本功能,界面友好,用户输入限制较完善。
本程序的缺点:对数据库的操作使用了sa登录,安全性有待提高,其他功能不完善等。