分布式数据库课程设计与实现
——面向连锁超市管理系统的分布式数据库设计与实现
I
目录
1. 连锁超市管理系统概述 ......................................................................1
背景介绍 ........................................................................................1
可行性分析 ......................................................................................1
系统目标和先进之处 .......................................................................2
小组成员和任务分配 ......................................................................3
2. 需求分析 ..............................................................................................4
用户需求概述 ...................................................................................4
业务需求分析 ..................................................................................5
功能分析 ...........................................................................................6
3. 系统总体设计 ......................................................................................8
绘制用例图设计系统功能 ...............................................................8
绘制系统流程图 ...............................................................................9
系统开发环境 ................................................................................10
系统的运行环境 .............................................................................11
系统运行演示 ................................................................................11
系统类库设计 .................................................................................14
4. 分布式数据库分析与设计 ................................................................16
数据库分析 .....................................................................................16
数据库概念设计 .............................................................................16
数据库逻辑结构设计 .....................................................................20
分片与位置分配设计 ....................................................................24
站点通信模型 ...........................................................................24
数据表的分片与位置分配设计 ...............................................25
5. 基于 SQL 的 SMS 的详细设计(实现)........................................27
公共类设计 .....................................................................................27
文件操作类 CFileOperate........................................................27
时间格式转换类 CMyTime ......................................................29
数据操作类 CADOConn ..........................................................29
数据库表的操作类 CtablePartInfo..........................................37
界面窗口设计 .................................................................................41
系统启动登录开发 ...................................................................41
主界面对话框设计 ...................................................................43
通用报表对话框设计开发 .......................................................44
打印对话框设计开发 ...............................................................46
基本资料模块设计 .........................................................................48
基本资料管理功能开发 ..........................................................48
5.3.2 价格自动生成功能开发 ......................................................52
报表功能实现 ..........................................................................54
进货/销售模块设计 ........................................................................55
进货/销售登记设计 ..................................................................55
进货设计 ...................................................................................59
月度统计设计 ...........................................................................61
库存模块设计 .................................................................................62
库存查询功能开发 ..................................................................62
库存报警功能开发 ..................................................................63
6. 开发技巧和难点分析 ........................................................................64
技术使用 .................................................................................64
GRID CONTROL 控件的使用 ...................................................65
1
1.连锁超市管理系统概述
背景介绍
随着市场经济的发展和人民生活水平的提高,原来单一、小规模
的超市已无法满足人民对购物环境的要求,大规模、物品丰富的超市
正在蓬勃发展。超市销售数据规模的日益庞大,商品数目的迅速增长,
采用以往的手工管理已直接或间接地降低了工作效率,最终影响超市
的日常运转。
另外超市的发张壮大,尤其是连锁超市(如家乐福、美廉美)的
扩张,使得他们具有地域上分散而管理上又相对集中的特点,往往既
要有各门店的局部控制和分散管理,同时也要有整个组织的全局控制
和高层次的协同管理。
因此把这些门店和中心通过网络连接起来,设计开发一款基于分
布式数据库的连锁超市管理系统 SMS(Supermarket management
system)势在必行。
可行性分析
(1)经济可行性
使用连锁超市管理系统对超市连锁店进行信息化管理将直接提
高公司管理部门的工作效率。通过网络远程提交汇总各门店的信息,
节省了许多的时间和金钱。另一方面,用信息化管理统计大量数据,
节省了很多的人力和财力,为管理者提供更好的决策支持。
(2)技术可行性
网络应用基础设施完善,由于信息技术的发展,我国的计算机网
络飞速发展,先后建成了中国公众多媒体通信网、China Net、 中国
教育与科研计算机网络等组成了中国 Internet 主体,网络应用进入企
业与普通家庭,这为发展连锁超市网络办公提供了基础设施。
网络安全技术应用,包括加密算法、CA 数字认证、数字签名等,
为网络办公系统的应用提供安全保证,它实现了网络传输数据的安全
性、完整性等。
最后,就是网络技术的普及与掌握,如网络互联、网络安全技术、
网络数据库技术等,使我们有能力开发实现适合自己的管理系统。
因此,公司构建跨区域的管理系统的技术瓶颈问题(网络应用基
础设施、网络安全、开发技术)得到了有效解决,公司构建网络管理
系统技术上可行。
(3)操作可行性
由于 SMS 的操作是基于 C/S 的客户端的页面操作,简单明了,
用户无需学习,一般都能够很容易的知道如何操作。而管理员也无需
具备专业知识,只需要对一些数据进行输入以及平时的日常维护就够
了。
系统目标和先进之处
(1)系统目标
为连锁超市提高效率、降低成本;实现连锁超市管理的优化,简
化工作流程,节省人力物力,提高工作效率,极大地满足客户需要;
对各环节进行控制分析,实现统一调度。
满足连锁超市的基本管理功能,发挥信息系统的灵活性,减轻企
业管理人员和操作人员的工作负担,提高工作效率。
本系统是一项功能比较完善的连锁超市管理系统,对连锁店运作
过程中的后台数据可以随时进行分析,便于企业管理人员的经营、决
策。 全面体现了现代企业管理理论所倡导的工作高效、环境轻松的
氛围。
(2)系统先进之处
多数处理就地完成。
各地的计算机由数据通信网络相联系。
克服了中心数据库的弱点:降低了数据传输代价。
提高了系统的可靠性,局部系统发生故障,其他部分还可继续
工作。
各个数据库的位置是透明的,方便系统的扩充。
为了协调整个系统的事务活动,事务管理的性能花费高。
小组成员和任务分配
用户需求概述
下面是通过调查研究获得的关于连锁超市主要信息数据的需求
分析结果:
(1) 连锁超市通常由一个中心(公司总部)、多个远程连锁店
(门店)组成,并且每个连锁店分布在不同地域。
(2)连锁超市的各个部门之间、各个分店之间、分店与总部之
间需要交换数据,这种数据交换是通过局域网和广域网进行的。
(3)公司总部负责产生并管理该连锁超市的整体汇总数据,即
各门店的明细汇总表数据,如销售汇总表等。
(4)每一个远程站点(各门店和公司总部)分别有一个数据库
系统,各自组成一个独立的子系统,可以分别独立进行本部门业务处
理。
(5)总部为了便于对各店进行管理,同时也为了比较各店的销
售情况,要求门店将所有商品归入相应的商品类别,由总部统一管理
并提供各门店使用,而且,商品类别信息数据在各门店都要经常使用。
(6)有关商品信息、供应商信息、POS 机信息、进货信息和销
售信息等经营基础数据都是各门店单独管理和使用,门店之间互不相
关。
(7)整个连锁超市的职员信息由公司总部管理和维护,各门店
只可以查询本部门的职员信息。
业务需求分析
通过对超市的营业、管理业务流程的调查,得到下面的业务需求。
可以对商品类别、商品、供应商、POS 机等基础信息进行管
理。
可以实现采购开单、销售开单、采购退货、销售退货等基本
功能。
软件可根据商品资料中设置的最高库存、最低库存进行库存
报警。
商品的批发价、零售价可自动生成,减轻定价的工作量。
可以生成各类统计报表,提供详尽的营业报告,实现对商品
进、销、存及利润等财务状况了如指掌。
所有单据、报表均可以在打印前预览,并且可以导出为 Excel
文件,然后根据实际需要进行特别的编排处理。
为了系统安全,每次用户登录都创建相应的日志文件,记录
用户的所有操作。
此外,对系统的性能主要有以下几个方面的需求。
系统在设计过程中应充分考虑到可扩充性,要求操作界面美观大
方,容易上手。
功能分析
根据对系统的业务调查和用户的需求分析,结合计算机信息管理
的特点,设计系统实现的功能如下。
(1)基础信息管理
基础信息管理包括业种商品类别信息、商品信息、供应商信息、
POS 机信息和价格信息维护与管理,实现的功能包括:
基本信息的添加、删除和更新操作。
生成各类基础信息报表。
打印和导出报表。
(2)进货管理
进货管理主要实现对商品的采购入库信息进行管理,具体实现功
能如下:
进货开单,实现商品的进货结算、入库操作。
进货退货,退还商家相关的商品。
生成进货、退货的单据、商品报表。
打印和导出报表。
(3)销售管理
销售管理主要实现对商品的销售出库相关信息进行管理,具体实
现的功能如下:
销售开单,实现商品的销售结算、出库操作。
销售退货,允许客户退出相关的商品。
生成销售、销售退货的单据、商品报表。
打印和导出报表。
(4)库存管理
库存管理主要实现对商品的库存相关信息进行管理,具体实现的
功能如下:
库存查询,可以查看所有库存商品的相关信息。
库存报警,对库存过多或过少的商品进行报表统计。
库存盘点,可以修改商品的库存数量。
(5)帐务管理
帐务管理主要实现对营业员的销售商品、业务提成、营业收入等
情况进行分类报表统计。
(6)数据管理
数据管理主要实现对数据库数据进行备份、还原及清理等相关工
作。
(7)系统管理
系统管理主要实现登录用户(职员)管理、系统日志、修改登录
密码等相关工作。
2.系统总体设计
绘制用例图设计系统功能
用例图表示了角色和用例以及它们之间的关系。它描述了系统、
子系统和类的一致的功能集合,表现为系统和一个或多个外部交互者
(角色)的消息交互动作序列。也就是角色(用户或外部系统)和系统(要
设计的系统)为了实现一个目的交互,这个目的的描述通常是一个谓
词短语,例如签合同等。
系统设计包含超级管理员、管理员和营业员四种用户角色。超级
管理员具有所有的操作权限,其用例图如图 3-1 所示。
图 3-1 超级管理员角色系统用例图
管理员不具备系统设置模块中的用户管理和商品类别信息管理
功能,其他功能均具备。而销售人员则只具有销售开单管理功能。
绘制系统流程图
结合系统的具体设计要求,连锁超市管理系统的主要功能流程图
如图 3-2 所示。
图 3-2 系统主要功能流程图
进货管理
销售管理
库存管理
数据管理
超级管理员
进货开单
进货退货
生成报表
数据备份
数据恢复
数据清理
销售开单
销售退货
生成报表
库存查询
库存盘点
库存报警
系统管理
用户管理
系统日志
修改密码
商品进货开单 商品销售开单
商品进货退单
库存管理
商品销售退单
商品类别设置 商品信息设置
供应商信息设
置
POS机信息设
置
系统首先对登录用户身份进行验证,根据用户的权限激活相关功
能。超级管理员具有所有的操作权限,系统的功能结构图如图 3-3 所
示。
图 3-3 系统功能结构图
上图只是列出了一些主要功能,系统还能够根据不同需求生成各
种统计报表。
系统开发环境
本系统是在 Windows XP 中文版操作系统环境下,使用 Microsoft
Visual Studio 2008 中文版用 C++语言开发成功的。在开发过程中,使
用了 OLE 技术和 ActiveX 控件技术。
后台数据库系统设计采用的是 Microsoft 的 SQL Server 2005 和
Access 数据库系统,通过 ADO 数据库开发技术,直接操作数据库文
件。
系统的运行环境
系统可以直接在 Win98、Win2000、WinXP 环境下运行。系统预
设的超级管理员用户名为“admin”,密码为“admin”。
系统运行演示
程序启动,首先弹出如图 3-4 所示的“系统登录”对话框,只有输
入正确的用户 ID、登录密码才能进入系统能够。
图 3-4 “系统登录”对话框
如果登录用户为超级管理员或管理员,会进入系统的主界面窗口,
如图 3-5 所示。
图 3-5 系统的主界面窗口
如果登录用户权限为营业员,由于其只具有售货权限,因此系统
会直接弹出“销售开单”对话框,如图 3-6 所示。
图 3-6 “销售开单”对话框
在进行相关操作前,首先需要添加、设置一些基本资料,如商品
分类登记,其操作设置对话框,如图 3-7 所示。
图 3-7 “商品分类登记”对话框
另外,系统还提供了丰富的报表功能,如图 3-8 所示的商品分类
报表,同时对报表提供了导出到 Excel 文件和打印的功能。
图 3-8 商品分类报表对话框
系统类库设计
BITVRLab 超市管理系统主框架的设计是通过 MFC 创建向导
创建的基于对话框的窗口程序,在对话框程序中添加了菜单栏,系统
的主要类库设计如下。
自定义扩展类:为了提高开发效率、便于代码重用,自定义了
一些数据操作类和控件扩展类,如表 3-1 所示。
表 3-1 自定义扩展类及功能
类 说 明
CADOConn 通过 ADO 实现对 ACCESS、SqlServer 等数据库的访问
CtablePartInfo 根据数据库表的分配、分片信息,实现对数据库表的增、
删、改、查
CFileOperate 实现对硬盘文件的常用操作
CMyChiToLetter 实现根据逐字提取汉字拼音的首字母
CMyButton CButton 类的派生类,实现带有位图和文本的按钮
CMyMenu CMenu 类的派生类,用于定制自己的菜单
CMenuItemContext CMenu 类中用到此类,用于保存菜单项的信息
CMyTime 实现简单的时间格式的转换
CMyExcel 完成 VC 对 Excel 文件的操作
对话框窗口类:在系统中,用户所有的数据查询、操作都是通
过对话框窗口来实现的,系统开发的对话框类如表 3-2 所示。
表 3-2 对话框类及说明
类 说 明
CDlgFenJiBase POS 机基本信息管理对话框类
CShopManageDlg 主框架对话框类
CDlgFenLeiBase 商品分类基本信息管理对话框类
CDlgDanganBase 商品基本信息管理对话框类
CDlgGongYingShangBas
e
供应商基本信息管理对话框
GDlgJiaGeSheZhi 价格自动设置对话框类
CDlgJinHuoKanDan 商品进货开单管理对话框类
CDlgJinHuoTuiDan 商品进货退单管理对话框类
CDlgXiaoShouKanDan 商品销售开单管理对话框类
CDlgXiaoShouTuiDan 商品销售退单管理对话框类
CDlgAll 用于查找信息显示对话框类
CDlgReport 用于报表显示对话框类
CDlgLogo 用户登录对话框类
CDlgPwd 更改密码对话框类
CDlgQuit 退出系统提示对话框类
打印相关类:系统能够提供了报表的打印和打印预览功能,其
相关的设计类如表 3-3 所示。
表 3-3 打印相关类及说明
类 说 明
CPrintFrame CFrameWnd 派生类,用于构建打印框架类
CPrintView 打印视图类
另外,系统还包含一些主框架相关类、导入 ActiveX 控件(MFC
Grid Control)相关类和导入 OLE 对象(Excel)相关类。
3.分布式数据库分析与设计
数据库分析
考虑到总站点的信息量大采用 SQL Server2005 数据库,而区域
站点信息量相对较小采用 Access 数据库。当区域业务拓展,操作终
端增加时,Access 数据库也很容易的移植到 SQL Server 数据库系统
中。另外区域站点采用 Access 数据库可以实现方便的部署。
数据库概念设计
分析超市管理功能流程,系统的数据实体主要包括基本资料对象
实体、库存实体、进货/销售开单、进货/销售退单、进货/销售商品实
体、各站点数据库服务器 IP 信息实体、数据库表分配分片信息实体
等。
基本资料对象实体包括商品类别、商品明细、供应商、POS 机
和登录用户实体。
商品库存实体记录库存商品的数量和价格信息,其实体的 E-R
图如下。
商品进货单实体用于记录进货单的统计信息,其实体的 E-R 图
如下所示。
进货商品实体用于记录进货单对应的商品信息,其实体的 E-R
图如下所示。
进货退单实体用于记录进货退单信息,其实体的 E-R 图如下所
示。
与进货相对应的销售单实体、销售商品实体和销售退单实体的
E-R 图如下所示。
各站点数据库服务器 IP 信息,记录各个区域数据库服务器对
应的 IP 地址,其实体的 E-R 图,如下图所示。
数据库表分配分片信息,记录数据中所有的表的分布式分配和
分片信息,其实体 E-R 图,如图所示。
数据库逻辑结构设计
商品类别表、商品明细表、供应商表、POS 机表、登录用户表、
库存表、进货/销售开单表、进货/销售商品表、进货/销售退货商品表、
IP 地址登录信息表、数据库表的分配和分片信息。
商品类别表:
商品明细表:
供应商表:
POS 机表:
用户表:
库存表:
进货单表:
进货商品表:
进货商品退单表:
销售单表:
销售商品表:
销售商品退单表:
服务器 IP 信息表:
分片及分布信息表:
分片与位置分配设计
站点通信模型
在我们的连锁超市管理系统中,有一个总店并下分多个分店,总
店和分店之间或分店与分店之间都可以进行通信。每个分店是一个相
对独立的数据库服务系统,其可以连接任意数量的客户端。通信模型
如下图所示:
数据表的分片与位置分配设计
根据以上得到的关于主要信息数据的需求分析结果,为该连锁超
市系统的分布式数据库系统进行主要信息数据的分片和分配设计如
下:
1、数据的分片设计
(1)由于该连锁超市系统的各连锁店之间在经营上是独立的,
每个门店只关心自己的经营状况,有关供应商信息、POS 机信息、商
品信息和进货/销货信息等基础数据都是各门店单独管理和使用,门
店之间互不相关。因此,商品明细表、供应商表、POS 机表、库存表、
进货/销售开单表、进货/销售商品表、进货/销售退货商品表按照地域
(门店所在区域标志)采用水平分片的方法得到水平片段。
(2)这里我们对于商品信息进行了垂直分片,分成了商品明细
表和库存表,因为商品的某些属性,例如库存量等需要经常更新,因
此将这些属性划分出来构成单独的实体可以减少系统开销。
(3)由于整个连锁超市的职员信息由总公司管理和维护,各门
店只可以查询本店的职员信息。所以职员信息不必分片,可以采用视
图的形式提供给各门店查询本门店的职员信息。另外,各个区域数据
库服务器的 IP 地址信息也只是由总部管理、维护,所以也不必分片。
(4)由于商品分类数据由总部统一管理并提供各门店使用,而
且,商品类别信息数据在各门店都要经常使用。因此,商品类别信息
数据也不必分片。
(5)该分布式数据库系统实现了简单的目录管理,记录数据库
中各个表的分片和分配信息,以便数据更新时,维护各个站点上数据
的一致性。这个表由总部规划建立,各门店也会经常使用。因此,数
据库目录信息表也不必分片。
2、数据及其片段的分配设计
(1)对于只在各门店单独使用的除商品类别信息和数据库目录
信息以外的其他基础信息的片段,采用按区域分片然后分配到各个门
店的数据库服务器上。总站上有所有门店的所有信息。
(2)整个系统的职员信息、IP 地址信息由公司总部管理和维护,
所以只分配在总部站点中。
(3)商品类别信息、数据库目录信息是由总部统一规定并下发
到各门店的,由于各店经常会使用到这类基础信息,因此在各门店都
具有相同的副本。所以,商品类别信息、数据库目录信息都不会分片
但被复制,且复制的个数为门店的个数。
4.基于 SQL 的 SMS 的详细设计(实现)
公共类设计
为了提高程序代码的开发效率,便于代码重用,在系统开发中,
创建了一些数据操作类和控件扩展类。
文件操作类 CFileOperate
为了便于对硬盘文件的操作,开发了文件操作类 CFileOperate,
它通过调用 API 函数实现常用的文件操作。CFileOperate 类的声明代
码如下。
extern CString strTmpPath;
class CFileOperate
{
public:
//构造函数
CFileOperate();
//取得当前程序运行的路径
CString GetAppPath();
//判断是否存在 strFn 文件夹
BOOL IsFileExist(CString strFn, BOOL bDir);
//制作 strFloderName 文件夹返回文件夹名
CString MakeDirectory(CString strFloderName);
//自动生成文件夹
CString MakeDirectory();
//得到文件夹名为 strFloderName 的路径
CString GetDirectoryPath(CString strFloderName);
//删除 strFloderdName 文件夹
void delDirectory(CString strFloderdName);
//删除主文件夹(data)
void delMainDirectory();
//制作主文件夹
void MakeMainDirectory();
//判断 strIntDigit 是否为整数,是否小于 intBig
BOOL CheckIntDigit(CString strIntDigit,int intBig);
//判断 strFileName 是否可以做文件夹或文件的名字
BOOL CheckFileName(CString strFileName);
//判断 strText 是否为空
BOOL CheckEmpty(CString strText);
//删除 strFloderName 文件夹下名为 strFileName 的文件
void delFile(CString strFloderName,CString strFileName);
//得到 strFloderName 文件夹下名为 strFileName 的文件路径
CString GetFileName(CString strFolderName,CString strFileName);
//制作 strFloderName 文件夹下名为 strFileName 的文件
CString MakeFile(CString strFloderName,BOOL blnMake);
//显示文件夹对话框
BOOL GetFolder(CString* strSelectedFolder,const char* lpszTitle,const HWND
hwndOwner,const char* strRootFolder,const char* strStartFolder);
系统程序中主要用到了 CFileOperate 类中的 GetAppPath 函数。
GetAppPath 函数用于获取当前运行程序所在文件夹的路径,其实现
代码如下。
CString CFileOperate::GetAppPath()//取得当前运行程序所在文件夹路径
{
char lpFileName[MAX_PATH];//路径数组
//获取当前运行程序的全路径
GetModuleFileName(AfxGetInstanceHandle(),lpFileName,MAX_PATH);
CString strFileName = lpFileName;
//从字符串的最右边向左搜索'\\'串
int nIndex = ('\\');
CString strPath;
if (nIndex > 0)
strPath = (nIndex);//取'\\'串左边字符
else
strPath = "";
return strPath;//返回前运行程序的文件夹路径
}
时间格式转换类 CMyTime
系统开发中,需要频繁的对日期、时间数据进行操作,因此这里
设计了类 CMyTime,实现简单的时间日期转换,CMyTime 类的声明
如下。
class CMyTime
{
public:
CTime ValueTime;//CTime 类型的值
//blnChinese:TRUE-"年月日时分秒" FALSE-"-:"
CString GetAllString(BOOL blnChinese); //返回字符串形式的日期时间
//blnChinese:TRUE-"年月日" FALSE-"-"
CString GetDateString(BOOL blnChinese);//返回字符串形式的日期
//blnChinese:TRUE-"时分秒" FALSE-":"
CString GetTimeString(BOOL blnChinese);//返回字符串形式的时间
CString GetSimpleString();//返回简单字符串形式的日期时间
CString GetWeek();//返回星期"星期日"
//设置字符串形式的值
//blnSimple:TRUE-(%Y%m%d%H%M%S) FALSE-(年月日时分秒或-:)
void SetAllString(CString Value,BOOL blnSimple);
void SetNow();//设置成现在时间
CMyTime();//构造函数
};
SetNow 函数获取当前日期,时间,将其值赋予 ValueTime。
void CMyTime::SetNow()
{
ValueTime=CTime::GetCurrentTime();
}
数据操作类 CADOConn
系统开发了数据操作类 CADOConn,它是本系统的核心,实现
了对数据库数据的基本操作功能,CADOConn 类的声明代码如下。
#import "c:\program files\common files\system\ado\" \
no_namespace rename ("EOF", "adoEOF")
class CADOConn
{
// 定义变量
public:
_ConnectionPtr m_pConnection;//指向 Connection 对象指针:
//添加一个指向 Recordset 对象的指针:
_RecordsetPtr m_pRecordset;
_bstr_t m_strData;
int m_DataType;
CADOConn(int DataType);//构造函数
CADOConn();
virtual ~CADOConn();
void OnInitCADOConn();//初始化连接数据库
_RecordsetPtr& GetRecordSet(CString strSQL);//执行查询
BOOL ExecuteSQL(CString strSQL);//执行 SQL 语句,Insert Update delete
void ExitConnect();//退出连接
BOOL MoveFirst();//字段集移向开头
BOOL MoveNext();//字段集向下移
BOOL Open(CString strSQL);//打开记录集
BOOL OpenLogo(CString strSQL);//打开记录集
CString GetValueString(int index,int strSum);//返回记录集中某字段的字符串
byte GetValueByte(int index);//返回记录集中某字段的字节
int GetValueInt(int index);//返回记录集中某字段的短整数
double GetValueDouble(int index);//返回记录集中某字段的双精度数
float GetValueFloat(int index);//返回记录集中某字段的单精度数
long GetValueLong(int index);//返回记录集中某字段的长整型数
CTime GetValueDate(int index);//返回记录集中某字段的日期时间
//获取记录集某字段的 BYTE 值,并换为 CString 返回
CString GetValueByteStr(int index,int strSum);
//获取记录集某字段的 INT 值,并换为 CString 返回
CString GetValueIntStr(int index,int strSum);
//获取记录集某字段的 Double 值,并换为 CString 返回
CString GetValueDoubleStr(int index,int strLSum,int strRSum);
//获取记录集某字段的 Float 值,并换为 CString 返回
CString GetValueFloatStr(int index,int strLSum,int strRSum);
//获取记录集某字段的 Long 值,并换为 CString 返回
CString GetValueLongStr(int index,int strSum);
//获取记录集某字段的 CTime 值,并换为 CString 返回
CString GetValueDateStr(int index,CString strType);
//添加单项数据
BOOL AddItem(CString strTable,int strSum,LPCTSTR pszText, ... );
//得到字段中数据的类型
int GetValueType(int index);
BOOL adoEOF();//记录集的结束判断
BOOL FillList(CListCtrl *listMain,int ColOpenEnd);//填充列表(ColOpenEnd 代表展开多
少列)
BOOL InitList(CListCtrl *listMain,int colSum);//初始化列表
CString GetAppPath();//得到应用程序所在的文件夹
BOOL FillList(CListCtrl *listMain);//填充列表
CString GetFieldsName(int index);//返回字段名字
int GetFeildsCount();//返回字段数量
//返回数据集数
long GetRecordCount();
void WriteLog(CString strSql);//写日志文件
void WriteLog1(CString userName);//写日志文件,谁谁登录
};
1、数据库操作函数
对常用的数据库操作定义了相关函数。
(1)连接数据库
在类的构造函数 CADOConn 中,声明数据库的名称。
CADOConn::CADOConn()
{
m_DataType=1;//数据库类型 Acess
m_strData= _bstr_t("");//数据库名称
}
在 OnInitCADOConn 函数中,实现连接数据库。
void CADOConn::OnInitCADOConn()
{
// 初始化 OLE/COM 库环境
::CoInitialize(NULL);
try
{
//初始化指针
m_pConnection=NULL;
//初始化指针
m_pRecordset=NULL;
// 创建 Connection 对象
("");
// 设置连接字符串,必须是 BSTR 型或者_bstr_t 类型
_bstr_t strConnect;
switch(m_DataType)
{
case 1://ACCESS
strConnect=_bstr_t("Provider=;");
strConnect=strConnect+_bstr_t("Data Source=");
strConnect=strConnect+_bstr_t(IPAddress)+_bstr_t("\\data\\");
strConnect=strConnect+m_strData;
break;
case 2://EXCEL
strConnect=_bstr_t("Provider=;");
strConnect=strConnect+_bstr_t("Data Source=");
strConnect=strConnect+_bstr_t(GetAppPath())+_bstr_t("\\");
strConnect=strConnect+m_strData;
strConnect=strConnect+";Extended Properties=Excel ";
break;
case 3://SQLSERVER
strConnect="Provider=SQLOLEDB; Server="+MainIP+";Database=ShopData;
uid=admin; pwd=123456";
break;
}
m_pConnection->Open(strConnect,"","",adModeUnknown);
}
// 捕捉异常
catch(_com_error e)
{
// 显示错误信息
AfxMessageBox(());
}
ASSERT(m_pConnection != NULL);
}
(2)对数据库进行查询
GetRecordSet 函数实现执行 Select 查询语句,返回查询结果集
_RecordsetPtr& CADOConn::GetRecordSet(CString strSQL)
{
try
{
// 连接数据库,如果 Connection 对象为空,则重新连接数据库
if(m_pConnection==NULL)OnInitCADOConn();
();
();
// 创建记录集对象
(__uuidof(Recordset));
// 取得表中的记录
m_pRecordset->Open(_bstr_t(strSQL),(),adOpenDynam
ic,adLockOptimistic,adCmdText);
}
// 捕捉异常
catch(_com_error e)
{
// 显示错误信息
AfxMessageBox(());
}
ASSERT(m_pRecordset!= NULL);
// 返回记录集
return m_pRecordset;
}
CADOConn 类的 Open 函数的实现与 GetRecordSet 函数基本类
似,只是它不返回记录集,而是打开数据库表。
(3)执行数据库操作语句
ExecuteSQL 函数实现执行 SQL 数据操作语句,如 INSERT/UPDATE/DELETE 语句等。
BOOL CADOConn::ExecuteSQL(CString strSQL)
{
try
{
// 是否已经连接数据库
if(m_pConnection == NULL) OnInitCADOConn();
();
();
m_pConnection->Execute(_bstr_t(strSQL),NULL,adCmdText);
WriteLog(strSQL);
return TRUE;
}
catch(_com_error e)
{
AfxMessageBox(());
return FALSE;
}
}
2、记录集操作函数
为了便于对记录集进行操作,在 CADOConn 类中定义了常用的
记录集操作函数。GetValueInt 实现了从记录集中获取不同类型的字
段值函数,以获取整形字段值。
int CADOConn::GetValueInt(int index)
{
_variant_t vValue;//var 型返回值
_variant_t vIndex;//索引
int iValue;//数值返回值
=VT_I2;
=index;
vValue=m_pRecordset->Fields->GetItem(vIndex)->Value;//获取索引字段的值
switch()
{
case VT_NULL://为空值
iValue=0;//赋值
break;
case VT_ERROR://错误
iValue=0;//赋值
break;
case VT_EMPTY://不存在
iValue=0;//赋值
break;
default:
iValue=;//获取值
}
return iValue;//返回整数值
}
3、列表控件操作函数
在 CADOConn 类中定义了列表控件的操作函数,实现将记录集
数据添加到指定的列表控件中。其中 InitList 函数实现初始化列表控
件。
BOOL CADOConn::InitList(CListCtrl *listMain,int colSum)
{
long lMax=0;
_variant_t vIndex;//var 类型索引
=VT_I2;
int i;
lMax=m_pRecordset->Fields->Count;//获取记录集字段数
//设置列表框控件风格
listMain->SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT);
for(i=0;i<lMax;i++)//遍历各字段
{
CString strTitle="";
=i;//字段索引
//获取字段名称
strTitle=(LPCTSTR)m_pRecordset->Fields->GetItem(vIndex)->GetName();
//在列表框中添加列
listMain->InsertColumn(i,strTitle,LVCFMT_CENTER,100,0);
}
int intWidth=0;//列表框列的宽度
for(i=0;i<colSum;i++)//遍历列表框中添加列
{
//根据字段标题自动设置列宽
listMain->SetColumnWidth(i,LVSCW_AUTOSIZE_USEHEADER);
intWidth=intWidth+listMain->GetColumnWidth(i);//计算列宽总和
}
RECT rectList;
listMain->GetWindowRect(&rectList);//获取列表框的窗口区域
if(intWidth<())//使列表项充满列表框窗口
{
intWidth=(-intWidth)/colSum;
listMain->SetColumnWidth(i,listMain->GetColumnWidth(i)+intWidth);
}
return TRUE;
}
FillList 函数用于向列表框中添加记录
BOOL CADOConn::FillList(CListCtrl *listMain,int ColOpenEnd)
{
int i,iType,iRow=0,listWidth=0;
_variant_t vIndex;//var 类型索引
long lMax=0;
lMax=m_pRecordset->Fields->Count;//记录集字段数目
=VT_I2;
listMain->DeleteAllItems();//删除列表控件各列的值
if(!m_pRecordset->adoEOF)//遍历记录集
{
MoveFirst();//首条记录
while (!m_pRecordset->adoEOF)
{
for (i=0;i<lMax;i++)
{
CString strValue="";
=i;//字段索引
//获取字段类型
iType=m_pRecordset->Fields->GetItem(vIndex)->GetType();
switch(iType)
{
case ado_Field_Str:
case ado_Field_Text://文本
strValue=GetValueString(i,0);//获取文本值
break;
case ado_Field_Long://长整型
strValue=GetValueLongStr(i,0);//获取长整值并转换为文本
break;
case ado_Field_Int://Int 型
strValue=GetValueIntStr(i,0);//获取 INT 值并转换为文本
break;
case ado_Field_Float://Float 型
strValue=GetValueFloatStr(i,0,2);//获取 Float 值并转换为文本
break;
case ado_Field_Double://Double 型
strValue=GetValueDoubleStr(i,0,2);//获取 Double 值并转换为文本
break;
case ado_Field_Byte://Byte 型
strValue=GetValueByteStr(i,0);//获取 Byte 值并转换为文本
break;
case ado_Field_Date://日期时间型
strValue=GetValueString(i,0);//获取日期时间值并转换为文本
break;
default:
strValue="";
break;
}
if(m_DataType==2)//Excel 文件
strValue=GetValueString(i,0);
if(i==0)//第一列
listMain->InsertItem(iRow,strValue,0);//添加行
else
listMain->SetItemText(iRow,i,strValue);//设置行值
}
m_pRecordset->MoveNext();//下一记录
iRow=iRow+1;
}
MoveFirst();//移向记录集开头
}
if(listMain->GetItemCount()>0)//列表框含有列
{
if(ColOpenEnd>0)//要展开的列
{
for(int i=0;i<ColOpenEnd;i++)
{
listMain->SetColumnWidth(i,LVSCW_AUTOSIZE);//自动设置列宽
listWidth=listMain->GetColumnWidth(i);//获取列宽
listMain->SetColumnWidth(i,LVSCW_AUTOSIZE_USEHEADER);// 根 据
列标题设置列宽
if(listWidth<listMain->GetColumnWidth(i))//根据列标题设置的列宽若小于
自动设置的列宽
listMain->SetColumnWidth(i,LVSCW_AUTOSIZE_USEHEADER);
}
}
else
{
listMain->SetColumnWidth(i,LVSCW_AUTOSIZE_USEHEADER);// 根 据 列 标
题设置列宽
}
}
else//表中没有列
{
for(i=0;i<lMax;i++)
{
listMain->SetColumnWidth(i,LVSCW_AUTOSIZE_USEHEADER);
listWidth=listWidth+listMain->GetColumnWidth(i);
}
RECT rectList;
listMain->GetWindowRect(&rectList);
if(listWidth<())
{
listWidth=(-listWidth)/11;
listMain->SetColumnWidth(i,listMain->GetColumnWidth(i)+listWidth);
}
}
return TRUE;
}
数据库表的操作类 CtablePartInfo
由于本项目模拟分布式数据库,对数据库部分表进行了分片、分
配等冗余设计。因此,对数据库的增、删、改、查要制定一定的规则,
来保证数据的一致性,而这种规则不因为表的不同而不同。为此,我
们设计了 CtablePartInfo 来统一的执行这种规则。
class tablePartInfo :public CWnd
{
public:
tablePartInfo();
tablePartInfo(CString _tableName);
~tablePartInfo();
public:
//根据表名 tableName 获得表的分片和分配信息
void GetTablePartInfo();
bool OperationOpen(CString strSQL,int nColumn);
//根据表的分片和分配信息,按增加规则执行数据库插入操作
bool OperationAdd(CString strSQL);
//根据表的分片和分配信息,按增加规则执行数据库修改操作
bool OperationModify(CString strSQL);
//根据表的分片和分配信息,按增加规则执行数据库查询操作
bool OperationSelect(CString strSQL);
bool OperationSelect(CString strSQL,int nColumn);
bool OperationSelectInital(CString strSQL,int nColumn);
bool OperationSelectInital(CString strSQL);
//根据表的分片和分配信息,按增加规则执行数据库删除操作
bool OperationDelete(CString strSQL);
//处理总站或者区域站点故障
void HandleError(bool success,int area);
public:
int part;
int copy;
CListCtrl* m_list;
CString tableName;
CString strIP;
bool IsDone1;
bool IsDone2;
CString ErrorMessage;
};
下面以 bool OperationAdd(CString strSQL)为例介绍了为维护数
据库一致性,对数据库更新规则的详细实现。
bool tablePartInfo::OperationAdd(CString strSQL)
{
bool success=true;//用于判断 SQL 语句是否执行成功
//首先更新总站数据库中的相应记录,因为所有的信息都会在总公司中存在
CADOConn adoMain(3);
success=(strSQL,0);
HandleError(success,0);//处理执行结果,执行故障则给出报错信息
if(success)//总站执行成功,接着进行区域站点的操作
{
if(0==m_globalAreaInt)//主站用户添加数据
{
if(1==copy)//冗余分配的信息,同步各个区域站点数据
{
CADOConn adoIP(3);
CString sqlstr="select * from IPInfo where area!=0";
(sqlstr);
//这里只有海淀和朝阳两个站点,所以预先申请两个 CADIConn 对象
CADOConn adoAcess1;
CADOConn adoAcess2;
int i=0;
//循环更新每个区域站点
while (!())
{
i++;
strIP = IPAddress;
IPAddress ="\\\\"+(1,0);
if(1==i)//处理海淀区数据库
{
success=(strSQL,1);
HandleError(success,1);
if(!success)//区域站点没有添加成功,这时总站撤销事务
{
_pConnection->RollbackTrans();
(ErrorMessage,0);
}
//区域站点添加成功,需等待区域添加成功再提交事务
}
if(2==i&&success)//区域站点添加成功,处理朝阳区
{
success=(strSQL,2);
HandleError(success,2);
if(!success)//区域站点没有添加成功
{
_pConnection->RollbackTrans();//总站撤销事务
(ErrorMessage,0);
_pConnection->RollbackTrans();//区域撤销事务
(ErrorMessage,1);
}
else//区域站点添加成功
{
//总站和区域站点同时提交事务
_pConnection->CommitTrans();
_pConnection->CommitTrans();
_pConnection->CommitTrans();
}
}
();
IPAddress = strIP;
}
();
();//区域站点关闭数据库连接
();
}
else//数据没有 copy 则不用更新区域站点数据,总站提交事务
{
_pConnection->CommitTrans();
}
}
else//分站用户添加数据
{
if(1==part)//数据表分片,向本地添加数据
{
CADOConn adoAcess;
success=(strSQL);
HandleError(success,m_globalAreaInt);
if(!success)//区域站点没有添加成功
{//总站撤销事务,写下撤销事务日志
_pConnection->RollbackTrans();
(ErrorMessage,0);
}
else//区域站点添加成功
{
//总站和区域站点同时提交事务
_pConnection->CommitTrans();
_pConnection->CommitTrans();
}
();//区域站点关闭数据库连接
}
else//数据表没有分片,则提交总站事务
{
_pConnection->CommitTrans();
}
}
}
();//总站关闭数据库连接
return success;
}
界面窗口设计
系统主框架是使用 MFC 创建向导创建的基于对话框的应用程序,
工程名为“ShopManage”。
系统启动登录开发
系统启动时,首先弹出登录对话框,用户登录后,根据用户的权
限显示主界面窗口或是销售开单窗口。
系统启动时,首先运行的是 CShopManageApp 类的 InitInstance
函数,在这里创建登陆对话框,登陆后根据用户的权限弹出相应的对
话框窗口。InitInstance 函数的实现代码如下。
BOOL CShopManageApp::InitInstance()
{
MainIP="";
IPAddress=MainIP;
IPAddressOrigin=MainIP;
AfxEnableControlContainer();
CDlgLogo dlgLog;//登录对话框
CShopManageDlg dlg;//主对话框
CDlgXiaoShouKanDan dlgXiao;//销售开单对话框
int i;
CString strSql;
if(()!=IDOK) //登录失败
::exit(0);//退出程序
CADOConn adoMain(3);
strSql="select * from AdminInfo where code='";
strSql=strSql++"'";
(strSql);//执行查询语句
if(!())//结果集不为空
{
if(())
{
i=(2);
if(i==2)//销售员
{
m_pMainWnd = &dlgXiao;
();//销售开单对话框
}
else//管理员
{
=;//管理员 ID
=;//管理员姓名
m_pMainWnd = &dlg;
();//主对话框
}
}
}
return FALSE;
}
登录用户的验证操作在登录对话框类 CDlgLogo 的“确定”按钮响
应函数 OnOK 中实现。
void CDlgLogo::OnOK()
{
// TODO: Add extra validation here
UpdateData(TRUE);
CString strSql;
CADOConn adoMain(3);//数据连接对象,用户信息表只有总站上有,所以连接 SQL 数据
库
strSql="select * from AdminInfo where code='";
strSql=strSql+m_str1+"' and pwd='";
strSql=strSql+m_str2+"'";
(strSql);//执行查询
if(!())//结果不为空
{
strName=(1,0);//用户姓名
strNo=m_str1;//用户 ID
m_globalArea = (7,0);//用户区域
m_globalAreaInt=(7);
CDialog::OnOK();//关闭对话框
//根据用户区域标志获得区域站点的 ip 地址
CADOConn adoIP(3);
CString sqlstr="select * from IPInfo where area="+m_globalArea;
(sqlstr);//执行查询
if(!())//结果不为空
{
IPAddress="\\\\"+(1,0);
IPAddressOrigin="\\\\"+(1,0);
}
//写登录日志信息
(m_str1);
(strSql);
(sqlstr);
();
}
else
MessageBox("密码或用户 ID 错误!","提示",MB_OK|MB_ICONWARNING);
();
}
主界面对话框设计
系统创建的是基于对话框的应用程序,因此系统的主界面是一个
对话框窗口。系统中为对话框窗口添加了菜单项,为了方便进行常用
操作,在对话框中还添加了一些位图按钮。
为对话框添加菜单栏,首先需要在工程的资源管理器重添加设计
菜单项,然后在对话框的属性对话框中,在 Menu 下拉列表框中,选
择设计的菜单资源 ID 即可。按钮控件则直接添加到主对话框模板中,
需要设置 Flat 属性。
在 CShopManageDlg 类的头文件中,声明 CMyMenu 对象,并将
按钮控件的类型设置为 CMyButton,代码如下。
class CShopManageDlg : public CDialog
{
// Construction
public:
CShopManageDlg(CWnd* pParent = NULL); // standard constructor
CMyMenu m_menu;
CString strNo,strName;
CMyButton m_ctrWarning;
CMyButton m_ctrStore;
……
};
在 CShopManageDlg 类的初始化函数 OnInitDialog 中,初始化菜
单和位图按钮的设置,这里菜单和按钮的设置分别采用自定义扩展
类 CMyMenu 和 CMyButton,它们对基本菜单和按钮控件的外观作了
定制,从而美化了程序界面。
通用报表对话框设计开发
系统能够生成多种统计报表,在系统开发中,设计了一个通用的
报表对话框,这样就简化了程序开发。在通用报表对话框资源模板中,
添加了一个列表控件用于显示报表数据,一个静态文本控件用于显示
报表标题,还有两个按钮控件,即“导出”按钮和“打印”按钮,分别用
于导出报表和打印报表。
通用报表对话框对应的对话框类为 CDLgReport,在其初始化函
数 OnInitDialog 中,设置报表标题和报表数据,代码如下。
BOOL CDLgReport::OnInitDialog()
{
CDialog::OnInitDialog();
(m_globalAreaInt);
if(0==biaozhi||0!=m_globalAreaInt)
(FALSE);
CString area;
("%d",());
tablePartInfo Operate(strTableName);
(IDI_ICONBUTTON);//导出按钮
(IDI_ICONBUTTON);//打印按钮
GetDlgItem(IDC_STATIC1)->SetFont(&ftHeader);//设置标题文本字体
GetDlgItem(IDC_STATIC1)->SetWindowText(strTitle);//设置标题
_list=&m_listMain;
CString str=strSQL+"area="+area;
if(0==biaozhi)
{
str=strSql;
}
(str);
return TRUE;
}
在“打印”按钮响应函数中,实现弹出打印对话框,代码如下。
void CDLgReport::OnButton2() //打印
{
// TODO: Add your control notification handler code here
CDlgPrint dlg;//打印对话框
=strTitle;//对话框标题
=strSql;//查询语句
_ListMain=&m_listMain;//列表控件
();
}
“导出”按钮可实现将报表数据导出到 Excel 文件中,按钮响应函
数 实 现 代 码 如 下 。 函 数 主 要 通 过 自 定 义 的 Excel 对 象 操 作 类
CMyExcel 来实现导出数据到 Excel 文件中。
void CDLgReport::OnButton1()
{
// TODO: Add your control notification handler code here
int i=0,j=0;
CMyExcel excel1;//Excel 操作对象
CMyTime time1;//时间操作对象
//打开文件对话框
CFileDialog dlg(FALSE,"xls","C:\\*.xls",OFN_NOCHANGEDIR,"EXCEL 文件|*.xls");
CADOConn adoMain;//数据操作对象
();//获取当前时间
if(()==IDOK)
{
();//打开 Excel 文件
("导出");//添加 Sheet 页
();
(1,1,strTitle);//添加标题
(strSql);//执行查询
//遍历列表框的列
for(i=0;i<()->GetItemCount();i++)
{
//将列标题添加到 Excel 表单中
(2,i+1,(i));
}
();//断开连接
for(i=0;i<();i++)//遍历记录集
{
//依次将记录集的各列添加到 Excel 表单中
for(j=0;j<()->GetItemCount();j++)
{
(i+3,j+1,(i,j));
}
}
(i+3,1,(TRUE));//在 Excel 中添加时间
();//自动设置 Excel 列
(());//保存 Excel 文件
}
();//退出 Excel 对象
}
打印对话框设计开发
系统对所有的统计报表都提供了打印功能。在报表对话框中,单
击“打印”按钮,就会弹出对应的打印对话框,在对话框中,通过使用
CGridCtrl 控件显示报表数据,且可在控件中编辑数据。
系统设计了一个通过打印对话框,在其对话框资源模板中添加
了 3 个按钮控件和一个用户自定义控件,该自定义控件用于实现
CGridCtrl 控件,其对应的对话框类为 CDlgPrint。
使用 CGridCtrl 控件可以实现类似 EXCEL 的界面,可以在网上
获取 MFC Grid control 控件代码文件,并将这些文件添加到工程中。
在 CDlgPrint 类的头文件中,新创建一个 CGridCtrl 对象,代码
如下。
class CDlgPrint : public CDialog
{
// Construction
public:
CDlgPrint(CWnd* pParent = NULL); // standard constructor
CString strSql;
CString strTitle;
CListCtrl *m_ListMain;
CGridCtrl m_Grid;
……
};
在 DoDataExchange 函数中为用户自定义控件关联该对象,代码
如下。
void CDlgPrint::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDlgPrint)
DDX_Control(pDX, IDC_GRID, m_Grid);
//}}AFX_DATA_MAP
}
在对话框的初始化函数 OnInitDialog 中,设置 CGridCtrl 控件的
属性,并将报表数据添加到 CGridCtrl 控件中,代码如下。
BOOL CDlgPrint::OnInitDialog()
{
CDialog::OnInitDialog();
CADOConn adoMain;//数据操作对象
int i,j;
SetWindowText(strTitle);//设置对话框标题
(strSql);
(m_ListMain->GetItemCount()+1);//设置控件行
(());//设置控件列
(TRUE);//控件可编辑
(RGB(0xFF, 0xFF, 0xE0));//设置背景色
(1);//设置表的固定列数
(1);//设置表的固定行数
for(i=0;i<();i++)
{
//设置表的标题
(0,i,(i));
}
for(i=0;i<m_ListMain->GetItemCount();i++)
{
for(j=0;j<m_ListMain->GetHeaderCtrl()->GetItemCount();j++)
{
//向控件中添加记录数据
(i+1,j,m_ListMain->GetItemText(i,j));
}
}
(TRUE);//支持打印特性
();
return TRUE;
}
基本资料模块设计
系统的基本资料管理模块包括商品分类、商品明细、POS 机、供
应商信息的管理、生成相关统计报表,以及价格自动生成功能。
基本资料管理功能开发
在系统进行相关功能操作前,首先需要设置一些基本信息,如在
商品进货登机前,就必须设置进货商品的分类、明细和供应商信息。
因此系统开发了“基本资料”主菜单项,提供了相应的基本资料管理功
能。下面以商品明细管理为例,简单介绍开发过程。
当管理员执行“基本资料”->“商品信息登记”菜单命令或者单击
“商品信息”工具按钮时,系统就会弹出“商品信息登记”对话框,在其
中可以新增、修改和删除商品明细信息,如图 9-21 所示。
对话框初始创建时,在其中的列表框中列出了数据库中的所有商
品明细。双击其中的记录,该记录信息就会显示在相应的编辑控件中,
管理员可以在编辑框中修改,而后单击“删除”按钮,即从数据库中删
除了该记录。
如果要添加商品记录,需要单击“新增”按钮,此时会添加一条空
的商品明细记录,而后在编辑框中键入明细信息,单击“更新”按钮的
响应函数 OnAdd2()代码如下。
void CDlgDanganBase::OnAdd2()
{
// TODO: Add your control notification handler code here
CString area;
("%d",());
CString strSql;
int i=0;
UpdateData(TRUE);
strSql="insert into CommodityInfo(barcode,area) values('','"+area+"')";
tablePartInfo Operate("CommodityInfo");
(strSql);
strSql="insert into StoreInfo(barcode,area) values('','"+area+"')";
(strSql);
strSql="SELECT CommodityInfo.*, ,
,,, FROM
CommodityInfo INNER JOIN StoreInfo ON =
and = where ="+area;
_list=&m_listMain;
(strSql);
m_str1="";
m_str2="";
m_str3="";
m_str4="";
m_str5="";
m_str6="";
m_str7="";
m_str8="";
m_dbl9=0;
m_dbl10=0;
m_dbl11=0;
m_dbl12=0;
m_dbl13=0;
m_dbl14=0;
m_dbl15=0;
UpdateData(FALSE);
}
“更新”按钮的响应函数 OnModify()代码如下。
void CDlgDanganBase::OnModify()
{
// TODO: Add your control notification handler code here
CString area;
("%d",());
CString strSql,str;
POSITION pos;
int i;
pos=();
i=(pos);
UpdateData(TRUE);
//更新商品信息表
strSql="update CommodityInfo set typecode='";
strSql=strSql+m_str1+"',barcode='";
strSql=strSql+m_str2+"',commoditycode='";
strSql=strSql+m_str3+"',memcode='";
strSql=strSql+m_str4+"',spellcode='";
strSql=strSql+m_str5+"',name='";
strSql=strSql+m_str6+"',standard='";
strSql=strSql+m_str7+"',unit='";
strSql=strSql+m_str8+"',maxnum='";
("%d",(int)m_dbl9);
strSql=strSql+str+"',minnum='";
("%d",(int)m_dbl10);
//添加区域信息
strSql=strSql+str+"',area='";
strSql=strSql+area+"'";
str=(i,1);
strSql=strSql+" where barcode='"+str+"' and area = "+(i,10);//area
是表中的第十一列
tablePartInfo Operate("CommodityInfo");
(strSql);
//更新库存表
strSql="update StoreInfo set barcode='";
strSql=strSql+m_str2+"',stockprice='";
("%f",m_dbl11);
strSql=strSql+str+"',saleprice='";
("%f",m_dbl12);
strSql=strSql+str+"',insiderprice='";
("%f",m_dbl13);
strSql=strSql+str+"',bonus='";
("%f",m_dbl14);
strSql=strSql+str+"',storenum='";
("%d",(int)m_dbl15);
strSql=strSql+str+"',area='";
//更新区域信息
strSql=strSql+area+"'";
str=(i,1);
strSql=strSql+" where barcode='"+str+"' and area = "+(i,10);//area
是表中的第十一列
(strSql);
strSql="SELECT CommodityInfo.*, , ,";
strSql=strSql+", , " ;
strSql=strSql+" FROM CommodityInfo INNER JOIN StoreInfo ON ";
strSql=strSql+" = and =
where ="+area;
_list=&m_listMain;
(strSql);
while((i,1)!=m_str2&&i<())
{
i++;
}
if(()>0)
(i, LVIS_SELECTED, LVIS_SELECTED);
}
另外,库存盘点功能也是“商品信息登记”对话框中实现的,只需
在商品编辑中输入商品的数量,单击“更新”按钮,即实现了商品盘点
功能。
5.3.2 价格自动生成功能开发
在商品明细信息登记中,添加的商品明细子脑子默认的商品会员
价、零售价、业务提成均与进货价相同。为了便于统一管理,系统开
发了价格批量生成功能。
执行“基本资料”->“价格自动生成设置”菜单命令,系统会弹出
如下图所示的“价格生成”对话框。通过检索添加要生成价格的商品,
而后以进货价为基准,在会员价、零售价和业务提成。
“价格生成”对话框对应的对话框类 DlgJiaGeSheZhi,“生成”按钮
响应函数 OnBegin 的实现代码如下。
void CDlgJiaGeSheZhi::OnBegin()
{
// TODO: Add your control notification handler code here
CString area;
("%d",());
tablePartInfo Operate("CommodityInfo");
UpdateData(TRUE);
CString strSql,str;
int i=0;
while(i<())
{
strSql="update StoreInfo set stockprice=stockprice*";
("%f",m_flt1);
strSql=strSql+str+",saleprice=saleprice*";
("%f",m_flt2);
strSql=strSql+str+",insiderprice=insiderprice*";
("%f",m_flt3);
strSql=strSql+str+",bonus=bonus*";
("%f",m_flt4);
strSql=strSql+str+" where barcode='";
strSql=strSql+(i,1)+"' and
area="+(i,10);
(strSql);
str=(i,10);
("%f",atof(str)*m_flt1);
(i,10,str);
str=(i,11);
("%f",atof(str)*m_flt2);
(i,11,str);
str=(i,12);
("%f",atof(str)*m_flt3);
(i,12,str);
str=(i,13);
("%f",atof(str)*m_flt4);
(i,13,str);
i=i+1;
}
AfxMessageBox("价格生成完毕!");
}
报表功能实现
系统对基本资料提供了报表功能,管理员可以通过菜单命令生成
相应的统计报表,并对报表数据提供了导出到 Excel 文件和打印功能。
以商品明细报表为例,执行“基本资料”->“商品信息报表”菜单命令,
系统会弹出如下图所示的报表对话框。
而报表功能则通过报表对话框来实现,“商品信息报表”菜单响应
函数 OnMenuitemB008 的代码如下。
void CShopManageDlg::OnMenuitemB008() //商品明细报表
{
CDLgReport dlg;//报表对话框
="商品明细信息报表";//报表的标题
="select * from CommodityInfo";//构造查询语句
="select * from CommodityInfo where ";
=11;
(); //弹出报表对话框
}
进货/销售模块设计
进货/销售模块主要包括进货/销售登记、进货/销售退单,以及相
关的各种统计报表功能实现。
进货/销售登记设计
商品的进货、销售功能是超市管理系统的核心功能,进货、销售
商品首先要进行登记开单。以进货为例,当执行“进货管理”->“进货
开单”菜单命令后,系统会弹出下图所示的进货开单登记对话框。要
进行开单操作,首先单击“单击”按钮,此时系统会自动生成单据编号
和开单日期,并向 StockBill 表中添加一条新纪录。
“开单”按钮相应函数 OnButton4 的实现代码如下。
void CDlgJinHuoKanDan::OnButton4() //开单
{
CString area;
("%d",());
tablePartInfo Operate("PosInfo");
CADOConn adoMain;
int i;
CString strSql,str;
CMyTime time1;//时间操作对象
if(m_str1!="")//开单编号不为空
{
for(i=0;i<();i++)//遍历列表控件记录
{
//更新 StoreInfo 表中记录
strSql="update StoreInfo set stockprice=";
strSql=strSql+(i,4)+",storenum=";
("%",atof((i,5))+atof((i,
6)));
strSql=strSql+str+" where barcode='";
strSql=strSql+(i,0)+"'";
(strSql);//执行 update 语句
}
}
();//获取当前时间
m_str1=();//作为开单编码
m_str2=(FALSE);//日期字符串,开票日期
m_str3="";
m_str4="";
m_str5="";
m_flt6=0;
m_flt7=0;
UpdateData(FALSE);
strSql="insert into StockBill(stockcode,stockdate) values('";
strSql=strSql+m_str1+"','";
strSql=strSql+m_str2+"')";
(strSql);//执行 Insert 语句
();//断开连接
();//删除列表控件记录
}
生成新的开单纪录后,管理员就可以在开单对话框中,经过检索
添加进货商品到列表框,而后在列表框中双击选中要添商品,输入数
量、供应商、经办人等信息,单击“保存”按钮,系统会自动计算生成
总金额,同事更新相关表纪录。
“保存”按钮响应函数 OnButton3 的实现代码如下。
void CDlgJinHuoKanDan::OnButton3() //保存按钮
{
UpdateData(TRUE);//获取控件输入
if(m_str1=="") //单据编码为空
{
MessageBox("请先开单,后加载商品!","提示",MB_OK|MB_ICONWARNING);
return;//返回
}
POSITION pos;
CString str;
int i;
pos=();
i=(pos);//获取列表控件选中记录序号
if (i>=0)
{
("%",m_flt6);
(i,4,str);
("%",m_flt7);
(i,6,str);
UpdateData(FALSE);
}
CString strSql;
CADOConn adoMain;//数据操作对象
double dbl=0;
strSql="update StockBill set stockdate='";
strSql=strSql+m_str2+"',transactor='";
strSql=strSql+m_str3+"',providercode='";
strSql=strSql+m_str4+"',notes='";
strSql=strSql+m_str5+"' where stockcode='";
strSql=strSql+m_str1+"'";
(strSql);//执行 update 语句
for(i=0;i<();i++)
{
strSql="update StockCommodity set stockprice=";
strSql=strSql+(i,4)+",stocknum=";
strSql=strSql+(i,6)+" where stockcode='";
strSql=strSql+m_str1+"' and barcode='";
strSql=strSql+(i,0)+"'";
(strSql);
strSql="update StoreInfo set stockprice=";
strSql=strSql+(i,4)+",storenum=";
strSql=strSql+(i,5)+" where barcode='";
strSql=strSql+(i,0)+"'";
(strSql);
}
for(i=0;i<();i++)
{
dbl=dbl+atof((i,4))*atof((i,6));
}
("%",dbl);
GetDlgItem(IDC_EDIT19)->SetWindowText(str);
();//断开连接
}
需要注意的是,在“保存”按钮的实现函数中,并没有更新 StireInfo
表中的 storenum(库存数量)字段值,也就是没有进行商品入库操作。
当结清货款,单击“结算”按钮时,才完成商品的入库。“结算”按钮响
应函数 OnButton5 代码如下。
void CDlgJinHuoKanDan::OnButton5() //结算
{
// TODO: Add your control notification handler code here
int i;
CString strSql,str;
CADOConn adoMain;
if(m_str1!="")
{
for(i=0;i<();i++)
{
strSql="update StoreInfo set stockprice=";
strSql=strSql+(i,4)+",storenum=";
("%",atof((i,5))+atof((i,
6)));
strSql=strSql+str+" where barcode='";
strSql=strSql+(i,0)+"'";
(strSql);
}
}
m_str1="";
m_str2="";
m_str3="";
m_str4="";
m_str5="";
m_flt6=0;
m_flt7=0;
UpdateData(FALSE);
();
}
与进货开单对应的销售开单类似,这里就不再详细介绍。
进货设计
系统开发了进货/销售退单功能,同样以进货退单为例,讲解其
实现过程。当执行“进货管理”->“进货退单”菜单命令后,系统会弹
出如下图所示的进货退单对话框。
在对话框中,通过查找按钮选择要退单的进货单号、经办人等信
息,单击“退单”按钮响应函数 OnButton1 的实现代码如下。
void CDlgJinHuoTuiDan::OnButton1()
{
// TODO: Add your control notification handler code here
CDlgAll dlg;
CADOConn adoMain;//数据操作对象
CString strSql,str;
int i;
UpdateData(TRUE);
strSql="insert into StockReturn values('";
strSql=strSql+m_str1+"','";
strSql=strSql+m_str2+"','";
strSql=strSql+m_str3+"','";
strSql=strSql+m_str4+"')";
(strSql);//执行 Insert 语句
if(m_str1!="")
{
for(i=0;i<();i++)//遍历列表控件
{
strSql="update StoreInfo set storenum=";
("%",atof((i,5))-atof((i,6
)));
strSql=strSql+str+" where barcode='";
strSql=strSql+(i,1)+"'";
(strSql);//执行 Update 语句
}
}
();//断开连接
m_str1="";
m_str3="";
m_str4="";
();//清空列表控件
UpdateData(FALSE);
}
月度统计设计
系统提供了进货/销售商品的月度统计功能。以进货月度统计为
例,执行“进货管理”->“月度结算”菜单命令后,系统会弹出如下图
所示的月度进货商品统计报表对话框。在报表中,列出了当月所有的
进货商品信息。
“月度结算”菜单命令项的响应函数 OnMenuitemC008 的代码如
下。
void CShopManageDlg::OnMenuitemC008()
{
// TODO: Add your command handler code here
CDLgReport dlg;//通用报表对话框对象
CMyTime time1;//时间操作对象
CString str1,str2;
();//获取当前时间
str1=();//转换为字符串
str2=(6);//取年月字符串
="月度进货商品统计表";//报表标题
//构造查询语句
="select StockCommodity.* from StockCommodity,StockReturn ";
=+"where not like
and ";
=+" like '"+str2+"%'";
=+" and StockCommodity.";
=5;
(); //弹出报表对话框
}
当然,在进货/销售模块中,还设计了很多形式的报表统计功能,
具体实现与月度统计类似,这里就不一一介绍了。
库存模块设计
库存查询功能开发
系统提供了库存查询功能,管理员可以通过该功能查看超市库存
所有商品信息。执行“库存管理”->“库存查询”菜单命令,系统会弹
出如下图所示的商品库存报表对话框,在其中列出了所有库存商品信
息。
“库存查询”菜单命令项的响应函数 OnMenuitemE003 的代码如
下。
void CShopManageDlg::OnMenuitemE003()
{
// TODO: Add your command handler code here
CString strSql;
CDLgReport dlg;
strSql="SELECT CommodityInfo.*, , ,";
strSql=strSql+", , " ;
strSql=strSql+" FROM CommodityInfo INNER JOIN StoreInfo ON ";
strSql=strSql+" = ";
="商品库存统计报表";
=strSql;
=+" where CommodityInfo.";
=16;
();
}
库存报警功能开发
系统提供了库存报警功能,管理员可以查询库存的过多或过少商
品信息。以过少库存报警为例,执行“库存管理”->“过少报警商品报
表”菜单命令,会弹出过少报警商品统计报表对话框,如下图所示。
在报表中,列出了所有过少库存商品信息。
“过少报警商品报表”菜单项的响应函数 OnMenuitemE008 代码
如下。
void CShopManageDlg::OnMenuitemE008()
{
// TODO: Add your command handler code here
CDLgReport dlg;
="过少报警商品统计报表";
="select CommodityInfo.*,";
=+" from CommodityInfo,StoreInfo";
=+" where = and ";
=+" >";
=+" and CommodityInfo.";
=5;
();
}
6.开发技巧和难点分析
技术使用
OLE 技术是 Microsoft 公司对象技术的战略,它把多个应用程序
合为一体,将每个应用程序看做是一个对象进行链接和嵌入,是一种
应用程序一体化的技术。利用 OLE 技术,可以方便地建立复合式文
档,这种文档由来自多个不同应用程序的对象组成,文档中的每个对
象都与原来的应用程序相联系,并可执行与原来应用程序完全相同的
操作。
本系统开发中,对所生成的各种报表均提供了导出功能,将报表
数据导出到 Excel 文件,这就是采用了 OLE 技术。为了便于对 Excel
对象操作,系统定义了 Excel 对象操作,系统定义了 Excel 操作类
CMyExcel。
Grid Control 控件的使用
在数据库的操作系统中,经常要遇到对数据库的内容进行显示,
修改,添加和删除等功能,而一般情况下数据库的容量都是很大的,
为此通常选用以电子表格的形式来显示数据库的内容,并在其上完成
对数据库的修改,添加和删除,使对数据库的操作既直观又方便。
CGridCtrl 控件就是一个优秀的网格控件,它可以实现类似
EXCEL 的界面。CGridCtrl 类派生于 CWnd 类,该类主要包含以下 8
个方面的函数,其函数形式如下。
(1)CGridCtrl 类的构造函数
函数的原型如下:
CGridCtrl(int nRows = 0, int nCols = 0, int nFixedRows = 0, int nFixedCols = 0);
BOOL Create(const RECT& rect, CWnd* parent, UINT nID,
DWORD dwStyle = WS_CHILD | WS_BORDER | WS_TABSTOP | WS_VISIBLE);
(2)表格行列数方面的函数
int GetRowCount() const { return m_nRows; }
int GetColumnCount() const { return m_nCols; }
int GetFixedRowCount() const { return m_nFixedRows; }
int GetFixedColumnCount() const { return m_nFixedCols; }
BOOL SetRowCount(int nRows = 10);
BOOL SetColumnCount(int nCols = 10);
BOOL SetFixedRowCount(int nFixedRows = 1);
BOOL SetFixedColumnCount(int nFixedCols = 1);
(3)表格尺寸大小方面的函数
int GetRowHeight(int nRow) const;
BOOL SetRowHeight(int row, int height);
int GetColumnWidth(int nCol) const;
BOOL SetColumnWidth(int col, int width);
(4)表格显示与特征方面的函数
void SetImageList(CImageList* pList) { m_pImageList = pList; }
void SetEditable(BOOL bEditable = TRUE) { m_bEditable = bEditable; }
(5)设置表格颜色方面的函数
void SetTextColor(COLORREF clr) { (clr); }
void SetTextBkColor(COLORREF clr) { (clr); }
void SetFixedTextColor(COLORREF clr);
void SetFixedBkColor(COLORREF clr);
(6)表格信息函数
BOOL SetItem(const GV_ITEM* pItem);
BOOL SetItemText(int nRow,int nCol, LPCTSTR str);
BOOL SetItemImage(int nRow,int nCol,int iImage);
(7)编辑方面的函数
Virtual void OnEditCell(int nRow,int nCol,UINT nChar);
Virtual void OnEndEditCell(int nRow,int Ncol,CString str);
(8)表格打印函数
Void print();