Python系列(7)——使用openpyxl写银行系统
1
# -*-coding:utf-8 -*-
2 # 使用openpyxl操作xlsx文件数据,可以在同一excel中新建其他sheet
3 import pandas as pd
4 import time
5 import openpyxl
6 import openpyxl.styles
7 file_path = r'E:\学习\Python\Pandas模块的导入及学习-数据分析\bank.xlsx' # 若是同一项目下的文件,可直接使用'./bank.xlsx'文件路径即可
8 bank_file = openpyxl.load_workbook(file_path)
9 bank = bank_file['user']
10 bank_error = bank_file['input_error']
11
12
13 def home_page(): # 主界面
14 print('欢迎使用ATN银行自助系统'.center(40, '='))
15 print('1.登录账户'.center(40, ' '))
16 print('2.注册账户'.center(40, ' '))
17 print('3.退出系统'.center(40, ' '))
18 print('欢迎使用ATM银行自助系统'.center(40, '='))
19
20
21 def welcome_page(): # 登录成功界面
22 print('欢迎使用银行ATM自助服务系统'.center(60, '='))
23 print('* 您好 %s *'.center(60, ' ') % input_name)
24 print()
25 print('1.查询余额 2.自助取款'.center(60, ' '))
26 print('3.自助存款 4.修改密码'.center(60, ' '))
27 print('5.查询操作历史 6.回主菜单'.center(60, ' '))
28 print(' 7.退出系统 '.center(60, ' '))
29 print()
30 print('欢迎使用银行ATM自助服务系统'.center(60, '='))
31
32
33 def judge(input_date): # 判断输入的时间是否正确
34 date = input_date.split('-') # 用-符将数据分隔开
35 list1 = []
36 if len(date) == 3:
37 for j in range(len(date)):
38 if date[j].isdigit():
39 list1.append(int(date[j]))
40 else:
41 print('输入数据非纯数字,请重新输入')
42 break
43 else: # 都是数字执行完后,执行的内容
44 year, month, day = list1[0], list1[1], list1[2]
45 if len(str(year)) == 4: # int型数据无len
46 if 0 < month <= 12:
47 if (month in (1, 3, 5, 7, 8, 10, 12)) and (day > 31 or day <= 0):
48 print(f'输入日期有误,{month}为大月,天数最多为31天,请重新输入!')
49 elif (month in (4, 6, 9, 11)) and (day > 30 or day <= 0):
50 print(f'输入日期有误,{month}为小月,天数最多为30天,请重新输入!')
51 elif month == 2:
52 if ((year % 4 == 0) and (year % 100 != 0)) or (year % 400 == 0):
53 if (day > 29) or (day <= 0):
54 print('输入的日期有误,该年2月为闰年2月,最多29天。请重新输入!')
55 else:
56 if (day > 28) or (day <= 0):
57 print('输入的日期有误,该年2月为平年2月,最多28天。请重新输入!')
58 else:
59 return True
60 else:
61 print('输入的月份错误,每年只有12个月,请重新输入')
62 else:
63 print('输入日期有误,年份必须是四位数,请重新输入')
64 else:
65 print('输入的日期长度有误,请重新输入')
66
67
68 def bank_list(): # 将name/password列表化
69 global name_list, password_list,error_name_list
70 name_list, password_list, error_name_list = [], [], []
71 error_row = bank_error.max_row
72 row = bank.max_row
73 # bank_file中各sheet的行和列写在此处,否则excel中的数据不是即时的数据,保存后继续执行可能无法立即查询到
74 # 此步骤涉及到login()处bank_error添加登录错误的新成员、add_user()处bank添加账户的新成员问题
75 for i in range(1, row + 1): # 将password这一列转为字符串类型
76 str(bank.cell(i, 2).value)
77 for j in range(2, row+1):
78 name_list.append(bank.cell(j, 1).value) # name第一列数据,从第2行开始读取
79 password_list.append(bank.cell(j, 2).value) # password第二列数据,....
80 for k in range(2, error_row+1):
81 # 增加一个日期判断条件,即前面日期输入错误的数据不进行覆盖,只将今日错误的数据名字进行列表化
82 # 不建议进行先遍历名字,再去判断日期,因为名字在之前可能也登录错误过,会造成索引有很多,匹配时只认第一个符合项
83 if bank_error.cell(k, 3).value == time.strftime('%Y-%m-%d', time.localtime()):
84 error_name_list.append(bank_error.cell(k, 1).value) # error_name第一列数据...
85
86
87 def re_password(): # 修改密码
88 old_password = input('请输入原6位数密码:')
89 if old_password == password_list[new_index]:
90 while True:
91 new_password = input('请输入新6位数密码:')
92 again_password = input('请再次输入新6位数密码:')
93 if new_password == '' or again_password == '':
94 print('密码不能为空,请重新输入')
95 elif new_password == again_password and new_password != '':
96 if new_password.isdigit(): # 判断是否是纯数字
97 if len(new_password) == 6: # 判断长度是否为6
98 print('密码修改成功')
99 bank.cell(new_index + 2, 2, new_password)
100 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 修改密码')
101 bank.cell(new_index + 2, 4, str(history_list))
102 bank_file.save(file_path)
103 break
104 else:
105 print('密码长度错误,请重新输入6位有效数字密码!')
106 else:
107 print('密码应为纯数字密码,请输入6位有效数字密码!')
108 elif new_password != again_password:
109 print('两次密码输入不一致,请重新输入')
110
111 elif old_password != password_list[new_index]:
112 print('原密码输入错误,请重新输入')
113 re_password()
114 else:
115 print('请输入有效的数字')
116
117
118 def search_record(): # 查询历史记录
119 while True:
120 start_date = input('请输入查询开始时间Y-m-d:') # 若想精确到时分秒,切片10变成取到19即可
121 if judge(start_date):
122 while True:
123 end_date = input('请输入查询结束时间Y-m-d:')
124 if judge(end_date):
125 start_time = pd.to_datetime(start_date) # 将输入的文本转化为时间格式
126 end_time = pd.to_datetime(end_date)
127 if start_time > end_time:
128 print('开始时间大于结束时间,请重新输入时间')
129 break
130 else:
131 print(f'查询开始时间为:{start_date}')
132 print(f'查询结束时间为:{end_date}')
133 record = []
134 for j in history_list:
135 if start_time <= pd.to_datetime(j[0:10:]) <= end_time: # 对列表中的时间进行切片处理并转为时间格式
136 print(j)
137 record.append(j)
138 else: # for正常执行完,会执行else内容,不正常结束,不执行else内容--可避免筛选出一条记录跟着打印出一条提示内容
139 if len(record) != 0:
140 print(f'该时间段的操作记录有{len(record)}条')
141 # 该时间段的历史记录导出到新excel中
142 out_put = input('是否导出到新excel?1.是,2.否--')
143 if out_put == '1': # 导出
144 new_excel = openpyxl.Workbook()
145 new_sheet = new_excel.active # 当前活跃的sheet,默认第一个sheet,也可使用create_sheet
146 new_sheet.title = f'{start_date}--{end_date}时间段的历史记录'
147 new_sheet.append(['时间', '操作记录'])
148 font = openpyxl.styles.Font('宋体', size=11, bold=True, color='000000')
149 new_sheet['A1'].font = font
150 new_sheet['B1'].font = font
151 list_record = []
152 for i in record:
153 list_record.append(i[0:19])
154 list_record.append(i[23::]) # 将时间字段与操作名称拆开放入列表中
155 temp_list = zip(*(iter(list_record),) * 2) # 将列表中的数据拆成2个元素组成一个列表
156 for j in temp_list:
157 new_sheet.append(list(j)) # 数据写入到new_sheet中
158 """
159 将列表中的数据拆成2个元素组成一个列表也可写成下面这种代码:
160 for k in range(0, len(list_record), 2):
161 list2 = list()
162 list2.append(list1[k])
163 list2.append(list1[k+1])
164 new_sheet.append(list2)
165 """
166 filename = 'C://Users//Administrator//Desktop//' \
167 + f'{input_name}的{start_date}-{end_date}间的操作记录' + '.xlsx'
168 # 这里的路径是进行组合的,因此不能写成r‘/’的形式,这种形式无法组合,写成//路径即可实现组合
169 new_excel.save(filename)
170 print('历史记录导出成功')
171 else:
172 input('>>按确定键继续为您服务<<')
173 else:
174 print('该时间段无操作记录')
175 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 查询历史')
176 bank.cell(new_index + 2, 4, str(history_list))
177 bank_file.save(file_path)
178 break
179 break
180
181
182 def work(): # 主界面程序
183 while True:
184 welcome_page()
185 action_str2 = input('请输入您的操作:')
186 if action_str2 == '1': # 查询余额
187 print(f'你的账户余额是:{bank.cell(new_index+2, 3).value} 元')
188 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 查询余额')
189 bank.cell(new_index+2, 4, str(history_list))
190 # bank.cell(new_index+2, 4).value = str(history_list)
191 bank_file.save(file_path)
192 input('>>按确定键继续为您服务<<')
193 elif action_str2 == '2': # 取款
194 money_out = int(input('请输入取款金额:'))
195 if money_out <= bank.cell(new_index+2, 3).value:
196 bank.cell(new_index+2, 3).value -= money_out
197 print(f"您的取款金额为{money_out}元,剩余金额为{bank.cell(new_index+2, 3).value}元")
198 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 取款'
199 + str(money_out)+'元')
200 bank.cell(new_index + 2, 4, str(history_list))
201 bank_file.save(file_path)
202 else:
203 print('您的余额不足,请重新选择金额')
204 input('>>按确定键继续为您服务<<')
205 elif action_str2 == '3': # 存款
206 money_in = int(input('请输入存款金额:'))
207 bank.cell(new_index+2, 3).value += money_in
208 print(f"您的存款金额为{money_in}元,剩余金额为{bank.cell(new_index+2, 3).value}元")
209 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 存款' + str(money_in)+'元')
210 bank.cell(new_index + 2, 4, str(history_list))
211 bank_file.save(file_path)
212 input('>>按确定键继续为您服务<<')
213 elif action_str2 == '4': # 修改密码
214 re_password()
215 print('即将退出当前界面,重新登录')
216 input('>>按确定键继续为您服务<<')
217 main_menu()
218 elif action_str2 == '5': # 查询操作记录
219 # 历史记录太多,也可再建一个sheet专门放历史记录,列名为客户名、时间、操作记录,(建议再加一个客户id号,同名的人查询不会出错)
220 # 也就是每一步操作后再sheet.append([input_name,操作的时间,操作的记录]),相应的查询操作历史处代码也要跟着改变
221 search_record()
222 while True:
223 action_str3 = input('请输入您的操作1.继续查询历史;2.回到操作界面---')
224 if action_str3 == '1':
225 search_record()
226 elif action_str3 == '2':
227 break
228 else:
229 print('输入错误,请重新输入')
230 input('>>按确定键继续为您服务<<')
231 elif action_str2 == '6': # 回主菜单
232 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 退回主界面')
233 bank.cell(new_index + 2, 4, str(history_list))
234 bank_file.save(file_path)
235 main_menu()
236 elif action_str2 == '7': # 退出系统
237 input('>>按确定键退出系统<<')
238 print('欢迎您的下次光临')
239 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 退出系统')
240 bank.cell(new_index + 2, 4, str(history_list))
241 bank_file.save(file_path)
242 exit()
243 else:
244 print('您的输入有误,请重新输入')
245 input('>>按确定键继续为您服务<<')
246
247
248 def login(): # 登录账户
249 global input_name,history_list,new_index
250 while True:
251 bank_list()
252 input_name = input('请输入您的姓名:')
253 input_password = input('请输入您的密码:')
254 if input_name in name_list and input_password != '':
255 new_index = name_list.index(input_name)
256 # 增加一个判断,即若当日的错误次数已经达到3次,不能再进行登录,就算密码输入正确也不可以登录
257 if input_name in error_name_list:
258 error_index = error_name_list.index(input_name)
259 row = bank_error.max_row-len(error_name_list)+error_index+1 # 不是加2,因为前面的行数-时已经包含行标题那一行了
260 # 总共的行数-今日错误的长度=原先错误的次数行数,再+对应今日错误的索引行数即变成了真正需要改变数值处所在的行数
261 if bank_error.cell(row, 2).value == 3:
262 print('该账户今日已经输入错误3次,禁止再登录')
263 history_list = eval(bank.cell(new_index + 2, 4).value) # # 转化为原列表形式
264 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 尝试登录系统,禁止登录')
265 bank.cell(new_index + 2, 4, str(history_list))
266 bank_file.save(file_path)
267 break
268 else:
269 if input_password == password_list[new_index]:
270 # ps:单纯的姓名与密码登录不够严谨,因为若有同名的,只要保证输入的密码是其中一个就能登录成功,因此可再加入身份证,id等字段验证
271 history_list = eval(bank.cell(new_index + 2, 4).value) # # 转化为原列表形式
272 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 登录系统')
273 bank.cell(new_index + 2, 4, str(history_list))
274 bank_file.save(file_path)
275 work()
276 else:
277 # 输入名字在今日错误列表中
278 print('账户和密码不匹配,请重新输入')
279 bank_error.cell(row, 2).value += 1
280 if bank_error.cell(row, 2).value == 2:
281 print('今日已输入错误2次,还剩1次机会')
282 history_list = eval(bank.cell(new_index + 2, 4).value)
283 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 尝试登录系统第2次')
284 bank.cell(new_index + 2, 4, str(history_list))
285 bank_file.save(file_path)
286 elif bank_error.cell(error_index + 2, 2).value == 3:
287 print('今日已输入错误3次,无输入机会,请明日再来')
288 history_list = eval(bank.cell(new_index + 2, 4).value)
289 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 尝试登录系统第3次')
290 bank.cell(new_index + 2, 4, str(history_list))
291 bank_file.save(file_path)
292 elif input_name not in error_name_list:
293 if input_password == password_list[new_index]:
294 history_list = eval(bank.cell(new_index + 2, 4).value)
295 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 登录系统')
296 bank.cell(new_index + 2, 4, str(history_list))
297 bank_file.save(file_path)
298 work()
299 else:
300 # 输入名字不在今日错误列表中,创建数据
301 print('账户和密码不匹配,今日已输错1次,还剩2次机会')
302 bank_error.append([input_name, 1, time.strftime('%Y-%m-%d', time.localtime())])
303 history_list = eval(bank.cell(new_index + 2, 4).value)
304 history_list.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 尝试登录系统第1次')
305 bank.cell(new_index + 2, 4, str(history_list))
306 bank_file.save(file_path)
307 # 加入的名字,执行后,不在error_name_list里是因为bank_error表的行数没有跟着更新,因此在bank_list()处写行数,而不是开头
308 elif input_name == '' or input_password == '':
309 print('姓名或密码不能为空,请重新输入')
310 elif input_name not in name_list:
311 print('此用户不存在,请重新输入')
312
313
314 def add_user(): # 添加账户
315 while True:
316 while True:
317 bank_list()
318 new_name = input('请输入新账户名:')
319 if new_name in name_list:
320 print('该账户名已存在,请重新输入')
321 elif new_name == '':
322 print('账户名不得为空,请重新输入')
323 elif new_name not in name_list and new_name != '':
324 new_user = {'name': new_name}
325 break
326
327 while True:
328 set_new_password = input('请输入6位数密码:')
329 re_set_new_password = input('请再次确认6位数密码:')
330 if set_new_password == '' or re_set_new_password == '':
331 print('密码不能为空,请重新输入')
332 elif set_new_password == re_set_new_password and set_new_password != '':
333 if set_new_password.isdigit():
334 if len(set_new_password) == 6:
335 print('恭喜您,新用户注册成功')
336 new_user['password'] = set_new_password
337 break
338 else:
339 print('密码长度错误,请重新输入6位有效数字密码!')
340 else:
341 print('密码应为纯数字密码,请输入6位有效数字密码!')
342 elif set_new_password != re_set_new_password:
343 print('两次密码输入不一致,请重新输入')
344
345 # 新用户预存金额
346 select = input('是否需要预存金额,需要请输入“1”后按确认键:')
347 if select == '1':
348 new_balance = int(input('请输入您的预存金额:'))
349 new_user['balance'] = new_balance
350 print(f'预存款成功,存款金额为:{new_balance}元')
351 else:
352 new_user['balance'] = 0
353
354 new_history = list() # 先声明其是空列表,若直接用[]赋值,会警告
355 # 或者直接这种赋值 new_history = [time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 注册账户']
356 new_history.append(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' 注册账户')
357 new_user['history'] = str(new_history)
358 new_user_list = []
359 for key, value in new_user.items():
360 new_user_list.append(value)
361 bank.append(new_user_list)
362 bank_file.save(file_path) # 时效性问题,即刚注册完接着登录时是找不到该用户的,因为登录时读取的bank_file是之前打开的
363 # 因此在name_list()处读取各sheet的行列,此时的数据都是添加保存后的数据,而不是把行列写在开头处
364 input('>>按确定键继续为您服务<<')
365 break
366
367
368 def main_menu(): # 主函数
369 while True:
370 home_page()
371 action_str = input('请输入您的选择1-登录账户,2-注册账户,3-退出系统: ')
372 if action_str == '1':
373 # 登录账户
374 login()
375 elif action_str == '2':
376 # 注册账户
377 add_user()
378 elif action_str == '3':
379 input('>>按确定键退出系统<<')
380 print('欢迎您的下次光临')
381 bank_file.close() # 关闭文件
382 exit()
383 else:
384 print('您的输入有误,请重新输入')
385 input('>>按确定键继续为您服务<<')
386
387
388 if __name__ == '__main__':
389 main_menu()