数据库课程设计——订餐系统(PowerBuilder+SQL Sever)
一、选题介绍
本系统要求学生对订餐系统进行设计,包括用户组设置(如餐厅管理员、顾客),订单管理(如增删改查)等功能,在此基础上对数据库进行设计,要求:符合数据库设计标准,减少冗余度
二、需求分析
(思维导图与系统功能图略,可根据以下文字自行绘制)
系统主要包含以下功能:
1. 用户管理:通过用户类型(餐厅管理员或顾客)实现不同权限的设置;用户可以登录系统,使用用户名和密码进行身份验证。
2. 餐厅管理:餐厅管理员可以创建与修改餐厅,提供餐厅名称和地址。
3. 菜单管理:餐厅管理员可以为餐厅添加、修改或删除菜单项,包括菜名、价格和描述。
4. 订单管理:用户可以浏览餐厅的菜单,并输入相应菜品信息下订单;餐厅管理员可以查看餐厅的订单。
5. 订单详情:订单详情将记录每个订单中的菜品信息,包括菜单ID和数量。
三、数据库设计
(一)E-R图(略,可根据以下文字自行绘制)
餐厅和用户之间的关系是多对多关系,一个餐厅可以有多个用户,一个用户可以选择多个餐厅。
用户和订单之间的关系为一对多关系,一个用户可以创建多个订单,而每个订单只属于一个用户。
订单和订单详情之间的关系是一对多关系,一个订单可以有多个订单详情,每个订单详情只属于一个订单。
菜单与订单详情之间的关系是多对多关系,一个订单详情可以对应一个或多个菜单项,而一个菜单项可以出现在多个订单详情中:一个订单可能包含多个菜品,每个菜品对应一个订单详情,而一个菜品可以在多个订单中出现。
餐厅与菜单之间的关系是一对多关系,一个餐厅可以有多个菜单,但每个菜单只属于一个餐厅。
(二)关系模型:
用户(用户ID,用户名,密码,用户类型)
餐厅(餐厅ID,餐厅名称,餐厅地址,餐厅管理员ID)
菜单(菜单ID,餐厅ID,菜名,价格,描述)
订单(订单ID,用户ID,订单日期,总价,状态)
订单详情(订单详情ID,订单ID,菜单ID,数量)
(三)表设计:
设计了五张表,分别是用户表,餐厅表,菜单表,订单表与订单详情表。
用户表(UUser)中包含了用户ID(UserID),用户名(Username),密码(Password),用户类型(UserType):餐厅管理员或顾客,其中用户ID为主键。
用户表(UUser)
列名 | 数据类型 | 是否允许为空 | 描述 |
UserID | int | false | 用户ID |
Username | varchar(50) | true | 用户名 |
Password | varchar(50) | true | 密码 |
UserType | varchar(20) | true | 用户类型 |
餐厅表(Restaurant)中包含了餐厅ID(RestaurantID),餐厅名称(RestaurantName),餐厅地址(RestaurantAddress),餐厅管理员ID(AdminID),其中餐厅管理员ID为外键,关联用户表中的用户ID。
餐厅表(Restaurant)
列名 | 数据类型 | 是否允许为空 | 描述 |
RestaurantID | int | false | 餐厅ID |
Restaurantname | varchar(50) | true | 餐厅名称 |
RestaurantAddress | varchar(100) | true | 餐厅地址 |
AdminID | int | true | 餐厅管理员ID |
菜单表(Menu)中包含了菜单ID(MenuID),餐厅ID(RestaurantID),菜名(DishName),价格(Price),描述(Description),其中菜单ID为主键,餐厅ID为外键,关联餐厅表中的ID。
菜单表(Menu)
列名 | 数据类型 | 是否允许为空 | 描述 |
MenuID | int | false | 菜单ID |
RestaurantID | int | true | 餐厅ID |
DishName | varchar(50) | true | 菜名 |
Price | decimal(8,2) | true | 价格 |
Description | varchar(200) | true | 描述 |
订单表(OOrder)中包含了订单ID(OrderID),用户ID(UserID),订单日期(OrderDate),总价(TotalPrice),地址(Address),其中订单ID为主键,用户ID为外键,关联用户表中的用户ID。
订单表(OOrder)
列名 | 数据类型 | 是否允许为空 | 描述 |
OrderID | int | false | 订单ID |
UserID | int | true | 用户ID |
OrderDate | date | true | 日期 |
TotalPrice | decimal(10,2) | true | 总价 |
Address | varchar(20) | true | 地址 |
订单详情表(OrderDetail)中包含了订单详情ID(DetailID),订单ID(OrderID),菜单ID(MenuID),数量(Quantity),其中订单详情ID为主键,订单ID为外键,关联订单表中的订单ID,菜单ID为外键,关联菜单表中的菜单ID。
订单详情表(OrderDetail)
列名 | 数据类型 | 是否允许为空 | 描述 |
DetailID | int | false | 订单详情ID |
OrderID | int | true | 订单ID |
MenuID | int | true | 菜单ID |
Quantity | int | true | 数量 |
(四)视图设计:
设计一个名为RestaurantMenuOrderView的视图,将餐厅表、菜单表连接在一起,提供餐厅名称、菜单号、菜名、价格的信息。使用这个视图可以方便地查看餐厅的菜单信息。
CREATE VIEW RestaurantMenuOrderView AS SELECT R.RestaurantName, M.MenuID, M.DishName, M.Price FROM Restaurant R JOIN Menu M ON R.RestaurantID = M.RestaurantID;
RestaurantMenuOrderView视图
还设计了一个名为OrderDetailView的视图,用来显示订单详情与订单的结合,该视图为顾客设计,提供了直观的订单详情。
CREATE VIEW OrderDetailView AS SELECT OD.detailid,OD.menuid,OD.quantity,O.orderid,O.userid,O.orderdate,O.address FROM OrderDetail OD JOIN OOrder O ON OD.orderid = O.orderid;
OrderDetailView视图
(五)存储过程设计:
设计一个名为GetMenuByRestaurantName的带参数的存储过程,当输入餐厅名称时返回对应的菜单信息。
CREATE PROCEDURE GetMenuByRestaurantName @Restaurantname VARCHAR(100) AS BEGIN SELECT M.MenuID, M.DishName, M.Price, M.Description FROM Menu AS M INNER JOIN Restaurant AS R ON M.RestaurantID=R.RestaurantID WHERE R.Restaurantname = @Restaurantname; END;
GetMenuByRestaurantName存储过程调用
(六)触发器设计:
设计一个名为UpdateTotalPriceOnOrderDetailInsert的触发器,根据订单详情表中的新增记录来更新订单表中的总价。通过内连接将订单表与新增的订单详情表关联起来,然后使用子查询计算订单详情的总价。子查询中使用 SUM函数计算每个菜单的总价。最后,将计算得出的新总价更新到订单表中。
CREATE TRIGGER UpdateTotalPriceOnOrderDetailInsert ON OrderDetail AFTER INSERT,DELETE AS BEGIN -- Update the TotalPrice in the Order table UPDATE OOrder SET TotalPrice = (SELECT SUM(D.Price * OD.Quantity) FROM OrderDetail OD INNER JOIN Menu D ON OD.MenuID = D.MenuID WHERE OD.OrderID = OOrder.OrderID) FROM OOrder INNER JOIN inserted ON OOrder.OrderID = inserted.OrderID; END;
执行以上语句,在订单详情表中对OrderID为1的订单增加了新菜品,再次查询订单表可以发现总价由原来的27.07更新为59.27。
增加语句前
增加语句后
还设计了一个触发器名为ADD_ORDER_DATE,实现增加订单时同步更新当前系统日期。
CREATE TRIGGER ADD_ORDER_DATE ON OOrder AFTER INSERT AS BEGIN UPDATE OOrder SET OrderDate=GETDATE() WHERE OrderDate is NULL; END;
执行insert语句
系统日期添加成功
(七)用户设计:
用户类型分为以下两种:
- 管理员用户,对餐厅、菜单信息进行增删改查,对餐厅订单与订单详情进行改查,但没有删除权限与增加权限,保证订单的真实性,不会出现商家刷单或恶意删除订单;
- 普通用户,对餐厅,菜单,订单信息进行查找,在餐厅与菜单表中没有增删改权限,但在订单表中可增加订单,无法修改或删除,也可在订单详情表中增加订单详情,但无法修改与删除,这意味着普通顾客想要修改订单信息需要联系餐厅管理员进行修改。
使用ODBC接口时仅使用数据库默认sa账号进行数据库连接,若想实现不同用户使用不同的数据库账号,可以设置两个ODBC数据源,分别使用餐厅管理员用户和普通顾客用户进行数据库连接,分为餐厅管理员客户端与普通顾客客户端。
由于制作两个程序工作量较大,且设计了用户表,用户表中包含了用户id,用户名,密码与用户类型,我们打算通过登录界面来识别用户类型,用户类型决定用户进入管理员窗口或顾客窗口,以达到不同用户不同权限的功能,因此只在此写出数据库用户授权的代码,本次课程设计中没有用到。
--创建餐厅管理员用户 CREATE LOGIN Restaurant_admin WITH PASSWORD='88888888'; CREATE USER Restaurant_admin FOR LOGIN Restaurant_admin;
--授予all权限,收回订单与订单详情表中的删除权限与增加权限 GRANT all privileges ON Menu TO restaurant_admin; GRANT all privileges ON OOrder TO restaurant_admin; REVOKE DELETE ON OOrder FROM restaurant_admin; REVOKE INSERT ON OrderDetail FROM restaurant_admin; GRANT all privileges ON OrderDetail TO restaurant_admin; REVOKE DELETE ON OrderDetail FROM restaurant_admin; REVOKE INSERT ON OOrder FROM restaurant_admin; GRANT all privileges ON Restaurant TO restaurant_admin;
普通顾客数据库用户创建于授权代码如下:
--创建普通顾客用户 CREATE LOGIN Restaurant_customer WITH PASSWORD='11111111'; CREATE USER Restaurant_customer FOR LOGIN Restaurant_customer;
--授予查看菜单表、餐厅表,订单表与订单详情,增加订单与订单详情权限 GRANT SELECT ON Menu TO restaurant_customer; GRANT SELECT ON OOrder TO restaurant_customer; GRANT INSERT ON OOrder TO restaurant_customer; GRANT SELECT ON OrderDetail TO restaurant_customer; GRANT INSERT ON OOrder TO restaurant_customer; GRANT SELECT ON Restaurant TO restaurant_customer;
四、数据库实现
--创建数据库 CREATE DATABASE RO; --创建用户表 CREATE TABLE UUser ( UserID INT PRIMARY KEY, Username VARCHAR(50), Password VARCHAR(50), UserType VARCHAR(20) ); --创建餐厅表 CREATE TABLE Restaurant ( RestaurantID INT PRIMARY KEY, RestaurantName VARCHAR(50), RestaurantAddress VARCHAR(100), AdminID INT, FOREIGN KEY (AdminID) REFERENCES UUser(UserID) ); --创建菜单表 CREATE TABLE Menu ( MenuID INT PRIMARY KEY, RestaurantID INT, DishName VARCHAR(50), Price DECIMAL(8, 2), Description VARCHAR(200), FOREIGN KEY (RestaurantID) REFERENCES Restaurant(RestaurantID) ); --创建订单表 CREATE TABLE OOrder ( OrderID INT PRIMARY KEY, UserID INT, OrderDate DATE, TotalPrice DECIMAL(10, 2), Address VARCHAR(20), FOREIGN KEY (UserID) REFERENCES UUser(UserID) ); --创建订单详情表 CREATE TABLE OrderDetail ( DetailID INT PRIMARY KEY, OrderID INT, MenuID INT, Quantity INT, FOREIGN KEY (OrderID) REFERENCES OOrder(OrderID), FOREIGN KEY (MenuID) REFERENCES Menu(MenuID) ); --插入用户表数据 INSERT INTO UUser (UserID, Username, Password, UserType) VALUES (1, 'admin', 'admin123', '餐厅管理员'); INSERT INTO UUser (UserID, Username, Password, UserType) VALUES (2, 'john_doe', 'password123', '顾客'); INSERT INTO UUser (UserID, Username, Password, UserType) VALUES (3, 'jane_smith', '123456', '顾客'); INSERT INTO UUser (UserID, Username, Password, UserType) VALUES (4, 'michelle_ang', '667798', '顾客'); INSERT INTO UUser (UserID, Username, Password, UserType) VALUES (5, 'adam_louis', 'adam145534', '顾客'); --插入餐厅表数据 INSERT INTO Restaurant (RestaurantID, RestaurantName, RestaurantAddress, AdminID) VALUES (1, '餐厅A', '地址A', 1); INSERT INTO Restaurant (RestaurantID, RestaurantName, RestaurantAddress, AdminID) VALUES (2, '餐厅B', '地址B', 1); INSERT INTO Restaurant (RestaurantID, RestaurantName, RestaurantAddress, AdminID) VALUES (3, '餐厅C', '地址C', 1); INSERT INTO Restaurant (RestaurantID, RestaurantName, RestaurantAddress, AdminID) VALUES (4, '餐厅D', '地址D', 1); INSERT INTO Restaurant (RestaurantID, RestaurantName, RestaurantAddress, AdminID) VALUES (5, '餐厅E', '地址E', 1); INSERT INTO Restaurant (RestaurantID, RestaurantName, RestaurantAddress, AdminID) VALUES (6, '餐厅F', '地址F', 1); --插入菜单表数据 INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (1, 1, '菜品1', 10.99, '这是菜品1的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (2, 1, '菜品2', 15.99, '这是菜品2的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (3, 2, '菜品3', 12.99, '这是菜品3的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (4, 2, '菜品4', 13.10, '这是菜品4的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (5, 3, '菜品5', 24.28, '这是菜品5的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (6, 3, '菜品6', 12.06, '这是菜品6的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (7, 4, '菜品7', 31.49, '这是菜品7的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (8, 4, '菜品8', 6.89, '这是菜品8的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (9, 5, '菜品9', 23.55, '这是菜品9的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (10, 5, '菜品10', 36.62, '这是菜品10的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (11, 6, '菜品11', 32.20, '这是菜品11的描述'); INSERT INTO Menu (MenuID, RestaurantID, DishName, Price, Description) VALUES (12, 6, '菜品12', 27.07, '这是菜品12的描述'); --插入订单表数据 INSERT INTO OOrder (OrderID, UserID, OrderDate, TotalPrice, Address) VALUES (1, 2, '2023-05-20', 27.07, '用户地址'); INSERT INTO OOrder (OrderID, UserID, OrderDate, TotalPrice, Address) VALUES (2, 3, '2023-05-21', 47.10, '用户地址'); INSERT INTO OOrder (OrderID, UserID, OrderDate, TotalPrice, Address) VALUES (3, 4, '2023-05-20', 47.97, '用户地址'); INSERT INTO OOrder (OrderID, UserID, OrderDate, TotalPrice, Address) VALUES (4, 5, '2023-05-17', 6.89, '用户地址'); --插入订单详情表数据 INSERT INTO OrderDetail (DetailID, OrderID, MenuID, Quantity) VALUES (1, 1, 12, 1); INSERT INTO OrderDetail (DetailID, OrderID, MenuID, Quantity) VALUES (2, 2, 9, 2); INSERT INTO OrderDetail (DetailID, OrderID, MenuID, Quantity) VALUES (3, 3, 2, 3); INSERT INTO OrderDetail (DetailID, OrderID, MenuID, Quantity) VALUES (4, 4, 8, 1);
--创建视图 CREATE VIEW RestaurantMenuOrderView AS SELECT R.RestaurantName, M.MenuID, M.DishName, M.Price FROM Restaurant R JOIN Menu M ON R.RestaurantID = M.RestaurantID;
CREATE VIEW OrderDetailView AS SELECT OD.detailid,OD.menuid,OD.quantity,O.orderid,O.userid,O.orderdate,O.address FROM OrderDetail OD JOIN OOrder O ON OD.orderid = O.orderid;
--创建存储过程 CREATE PROCEDURE GetMenuByRestaurantName @Restaurantname VARCHAR(100) AS BEGIN SELECT M.MenuID, M.DishName, M.Price, M.Description FROM Menu AS M INNER JOIN Restaurant AS R ON M.RestaurantID=R.RestaurantID WHERE R.Restaurantname = @Restaurantname; END;
--创建更新总价格触发器 CREATE TRIGGER UpdateTotalPriceOnOrderDetailInsert ON OrderDetail AFTER INSERT,DELETE AS BEGIN -- Update the TotalPrice in the Order table UPDATE OOrder SET TotalPrice = (SELECT SUM(D.Price * OD.Quantity) FROM OrderDetail OD INNER JOIN Menu D ON OD.MenuID = D.MenuID WHERE OD.OrderID = OOrder.OrderID) FROM OOrder INNER JOIN inserted ON OOrder.OrderID = inserted.OrderID; END;
--创建同步日期触发器 CREATE TRIGGER ADD_ORDER_DATE ON OOrder AFTER INSERT AS BEGIN UPDATE OOrder SET OrderDate=GETDATE() WHERE OrderDate is NULL; END;
五、系统实现
使用SQL Sever创建数据库,PowerBuilder9.0实现窗口,ODBC接口连接数据库,编程语言使用PB语言。
(一)窗口介绍
为实现订餐系统,创建了7个窗口:w_main为登录主界面,w_admin管理员界面与w_customer顾客界面为w_main的子页面,管理员界面中实现了餐厅管理与菜单管理功能,包括餐厅信息的增删改查,菜单信息的增删改查,w_order订单管理界面为w_admin的子界面,提供订单管理功能,包括对订单的修改与查找;w_customer_order顾客订单查看界面、w_address增加新订单界面与w_add加菜界面都为w_customer的子界面。
(二)功能实现
(1)用户登录
w_main窗口
通过与用户表中的用户类型对照,管理员登录需要勾选“管理员登录”按钮,否则提示“用户不存在”;普通用户登录若勾选了该按钮,同样也提示“用户不存在”;理所当然地,若输错用户名也将提示“用户不存在”,当用户名与密码与用户类型与用户表中数据一致时,提示登陆成功。
代码实现:在登录按钮的clicked事件中编辑代码如下:
string userPwd,userType GS_USERNAME=trim(sle_1.text) userPwd=trim(sle_2.text) if cbx_1.checked = true then userType="餐厅管理员" else userType="顾客" end if if GS_USERNAME ="" then messagebox("提示","用户名不能为空") else SELECT Username into :GS_USERNAME from "UUser" where "UUser"."Username"=:GS_USERNAME AND "UUser"."UserType"=:userType; //SQLCode=100证明没有找到记录,此用户不存在 if SQLCA.SQLCode=100 then messagebox("提示","用户不存在") else //该用户存在查询密码是否正确 SELECT Username into :GS_USERNAME from "UUser" where "UUser"."Username"=:GS_USERNAME and "UUser"."Password"=:userPwd; if SQLCA.SQLCode=100 then messagebox("提示","密码不正确") else messagebox("提示","登录成功") if userType="餐厅管理员" then open(w_admin) close(parent) else open(w_customer) close(parent) end if end if end if end if
值得注意的是,除了这里的userpwd和usertype变量之外,需要在全局中定义一个GS_username变量,将文本框中的值赋给该全局变量,以方便后面判断用户名输入。
在退出按钮的clicked事件中添加关闭窗口代码
close(w_main)
(2)顾客点餐
w_customer窗口
顾客点餐窗口展示了所有餐厅与餐厅对应的菜单,此处使用的是RestaurantMenuOrderView视图,在数据窗口中选择对应的数据,并在窗口页面的open事件中写入以下代码以显示。
dw_1.settransobject(sqlca) dw_1.retrieve()
(a)点餐
点击“去点餐”按钮进入下单页面:
在按钮的clicked事件中写入打开w_address窗口的代码打开下单窗口。
w_address窗口
在窗口中输入信息,若用户名与登录用户名不一致则提示“请输入与登陆时相同的登录名”,当用户名一致时提示“下单成功”。
代码实现:
string uaddress,userid,username,mmenuid,quantity username=string(sle_1.text) uaddress=string(sle_2.text) mmenuid=string(sle_3.text) quantity=string(sle_4.text) if username=gs_username then select userid into:userid from uuser where username=:username; int maxdetailid,maxorderid select max(detailid) into:maxdetailid from orderdetail; select max(orderid) into:maxorderid from orderdetail; int neworderid,newdetailid neworderid=maxorderid+1 newdetailid=maxdetailid+1 now_orderid=string(neworderid) string orderinsert,detailinsert orderinsert="insert into [oorder](orderid) values("+string(neworderid)+")" detailinsert="insert into [orderdetail](detailid) values("+string(newdetailid)+")" execute immediate:orderinsert using sqlca; execute immediate:detailinsert using sqlca; string orderupdate orderupdate="update oorder set userid ='"+userid+"',address='"+uaddress+"' where userid is null and address is null" execute immediate:orderupdate using sqlca; string detailupdate detailupdate="update orderdetail set orderid='"+now_orderid+"',menuid ='"+mmenuid+"',quantity='"+quantity+"' where orderid is null and menuid is null and quantity is null" execute immediate:detailupdate using sqlca; messagebox("提示","下单成功!") close(w_address) else messagebox("提示","请输入与登录时相同的用户名!") end if
此段代码的主要目的是获取文本框中的文字,将文本框中的文字添加到表中。首先获取当前订单详情表中订单详情号与订单号的最大值,然后在订单表与订单详情表中新插入一个最大值加1的订单详情号与订单号,插入完毕后表格中除了订单号与订单详情号其余都为NULL值,最后将文本框中获取的文本信息update到表中,条件为 xxx is NULL,由于已经写了自动添加日期的触发器,不需要特地输入日期,插入新的订单号时,触发insert事件,自动在表中加入了系统日期。
(b)加菜
点击顾客界面的“去加菜”进入加菜窗口。与“去点餐”按钮中的代码相似,打开w_add窗口。
w_add窗口
输入菜单号与数量,点击立即加菜按钮弹出加菜成功窗口。
代码实现:
string mmenuid,quantity mmenuid=string(sle_3.text) quantity=string(sle_4.text) int maxdetailid,maxorderid select max(detailid) into:maxdetailid from orderdetail; select max(orderid) into:maxorderid from orderdetail; int newdetailid newdetailid=maxdetailid+1 string orderinsert,detailinsert detailinsert="insert into [orderdetail](detailid) values("+string(newdetailid)+")" execute immediate:detailinsert using sqlca; string detailupdate detailupdate="update orderdetail set orderid='"+string(maxorderid)+"',menuid ='"+mmenuid+"',quantity='"+quantity+"' where orderid is null and menuid is null and quantity is null" execute immediate:detailupdate using sqlca; messagebox("提示","加菜成功!") close(w_add)
此段代码与“去下单”中的代码逻辑类似,获取当前订单详情表中的订单详情最大值与订单号最大值,然后在订单详情表中新插入一个订单详情号最大值加1的值,实现每按一次该按钮就插入新的行,除了订单详情号与订单号外其余字段都为NULL,然后将文本框中的值更新到表格中,条件为xxx is NULL,值得注意的是,加菜的订单号就为最大值,不加1。
(c)查看订单
在“查看订单”按钮中写入打开w_customer_order窗口的代码,打开窗口。
w_customer_order窗口
上图是初始只有一条信息的状态,经过了一轮下单,一轮加菜,窗口中的信息更新为:
可见订单号都为5号,地址与日期都为下单时的信息,但订单详情号不同,菜单号与数量是加菜时填入的菜单与数量。
数据窗口选择相应的数据,然后在此处在窗口的open事件中写入以下代码:
int uuserid select userid into:uuserid from uuser where username=:gs_username; dw_1.settransobject(sqlca) dw_1.retrieve(uuserid)
这里的gs_username是最开始定义的全局变量,存放了登录时输入的用户名的值,通过select语句查找条件为用户表中用户名等于gs_username的用户号,然后通过retrieve(uuserid)只显示该用户号的订单信息。
(3)管理员餐厅管理
w_admin窗口
在该界面中放置了7个按钮,其中“餐厅”与“菜单”按钮点击后可以查看餐厅表与菜单表
餐厅表 菜单表
点击添加可以添加新的一行,可在里面添加文字;点击保存即可保存到表格中;点击删除即可删除这一行,弹出警告框再次确认,点击确定即可删除。
添加 保存
删除警告 已删除
代码实现:
在数据窗口中的rowfocuschanged事件中添加代码如下:
dw_1.selectrow(0,false) dw_1.selectrow(dw_1.getrow(),true)
在餐厅按钮的clicked事件中添加代码如下:
dw_1.dataobject='dw_restaurant' dw_1.SetTransObject(sqlca) dw_1.Retrieve()
菜单按钮同理,只需要更换表格的数据窗口就行。
添加按钮的clicked事件代码如下:
integer i_newrow i_newrow=dw_1.insertrow(0) dw_1.scrolltorow(i_newrow)
删除按钮的clicked事件代码如下:
int i i=messagebox("警告","真的要删除吗?",exclamation!,Okcancel!,2) if i=1 then dw_1.deleterow(0) dw_1.update() dw_1.retrieve() end if
保存按钮的clicked事件代码如下:
dw_1.update() dw_1.retrieve()
订单按钮的clicked事件用于打开w_order窗口,退出按钮则是关闭当前w_admin窗口。
(4)管理员订单管理
w_order窗口
第一个数据窗口中显示订单概况,第二个数据窗口中显示第一个数据窗口中选中行的订单号相关订单详情。
点击排序的两个按钮,可实现按日期降序与按单号升序,点击按日期降序可以看到最顶上为刚刚顾客下的5号订单,第二个数据窗口中出现了两个订单详情,订单号都为5。
点击计算总金额,可以计算当前订单的总金额。
5号订单的总金额
2号订单的总金额
用户下的新订单总金额为空,管理员需要手动输入总金额并保存订单,若订单信息需要修改,可以一并修改后点击“保存”。
代码实现:
首先在窗口的open事件中写入以下代码以显示数据:
dw_1.settransobject(sqlca) dw_1.retrieve()
在第一个数据窗口中的rowfocuschanged事件中写入如下代码:
int i_row int s_num i_row=dw_1.getrow() dw_1.selectrow(0,false) dw_1.selectrow(i_row,true) s_num=dw_1.getitemnumber(i_row,"orderid") oorderid=s_num dw_2.settransobject(sqlca) dw_2.retrieve(oorderid)
此部分的代码用于获取鼠标选中行上的orderid,然后将值赋给oorderid,使用retrieve(oorderid)实现数据窗口的数据显示。
oorderid也是一个全局变量,存放了鼠标选中行的orderid,以便于计算对应订单总金额。
值得注意的是,第二个数据窗口中的数据需要设置好sql语句,如下图所示,要先定义一个number型变量orderid,然后写入查询条件中,否则无法实现第二个数据窗口的信息跟随鼠标选中的第一个数据窗口的行的特定值改变。
排序功能使用了setsort函数,A为升序,D为降序,按日期降序按钮代码如下:
dw_1.setsort("orderdate D") dw_1.sort()
同理,按单号升序代码如下:
dw_1.setsort("orderid A") dw_1.sort()
总金额的计算代码:
string price SELECT SUM(Menu.Price * OrderDetail.Quantity) into:price FROM OrderDetail,Menu WHERE OrderDetail.MenuID=Menu.MenuID AND OrderDetail.OrderID=:oorderid; sle_1.text=price
此处用到了前面定义的全局变量oorderid,通过select语句连接菜单表与订单详情表,找出订单详情表中的菜单id对应的价格,然后计算该价格与订单详情表中数量的乘积,用SUM聚合函数求与oorderid的值相等的订单详情表中的orderid的价格之和,即为总金额。
至此,该订餐系统的功能展示完毕。
订餐系统的流程为:管理员在管理员界面添加餐厅与菜单信息,顾客通过下单页面浏览菜单并下单,顾客可加菜并查看自己的订单,管理员可在订单管理处按日期降序优先查询到新的订单,新的订单未登记总金额,点击计算总金额后,顾客线下付款,管理员手动登入该订单总金额并保存,订单完成。
作者的话:该订餐系统还有许多地方需要完善,课设时间有限只能做到这个地步了,比如说创建的存储过程没用上,自动更新总价的触发器也没改好,其实这个是因为后面的加菜功能里我写的代码是update,而触发器的条件是insert和delete,我在编加菜功能的时候写insert死活报错,最后才选择使用update语句,这一点可以改一改,管理订单页面两个数据窗口的订单号同步那边有一点难度,需要好好摸索一番,bilibili上有我的演示视频,有大部分的代码讲解,可以去参考看看。数据库课程设计PowerBuilder9.0+SQL Sever_哔哩哔哩_bilibili