ATM功能实现项目

模拟实现一个ATM + 购物商城程序

1、额度 15000或自定义
2、实现购物商城,买东西加入 购物车,调用信用卡接口结账
3、可以提现,手续费5%
4、支持多账户登录
5、支持账户间转账
6、记录每月日常消费流水
7、提供还款接口
8、ATM记录操作日志
9、提供管理接口,包括添加账户、用户额度,冻结账户等。。。
10、用户认证用装饰器
示例代码 https://github.com/triaquae/py3_training/tree/master/atm

简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329

 首先画一个流程图让思路更清晰:

《ATM功能实现项目》

 

目录结构图:

《ATM功能实现项目》

 

 

启动文件

 start.py源代码:

1 import os, sys
2 
3 path = os.path.dirname(__file__)
4 sys.path.append(path)
5 from core import src
6 
7 if __name__ == '__main__':
8     src.run()

配置文件相关

setting.py源代码:

 1 import os
 2 
 3 BASE_PATH = os.path.dirname(os.path.dirname(__file__))  # ATM的路径
 4 BASE_DB = os.path.join(BASE_PATH, 'db')  # db 文件的路径
 5 BASE_LOG = os.path.join(BASE_PATH, 'log')  # 日志路径
 6 
 7 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
 8                   '[%(levelname)s][%(message)s]'  # 其中name为getlogger指定的名字
 9 
10 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
11 
12 id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
13 
14 # 定义日志输出格式 结束
15 
16 # 如果不存在定义的日志目录就创建一个
17 if not os.path.isdir(BASE_LOG):
18     os.mkdir(BASE_LOG)
19 
20 # log文件的全路径
21 logfile_path = os.path.join(BASE_LOG, 'log.log')
22 
23 # log配置字典
24 LOGGING_DIC = {
25     'version': 1,
26     'disable_existing_loggers': False,
27     'formatters': {
28         'standard': {
29             'format': standard_format
30         },
31         'simple': {
32             'format': simple_format
33         },
34     },
35     'filters': {},
36     'handlers': {
37         # 打印到终端的日志
38         'console': {
39             'level': 'DEBUG',
40             'class': 'logging.StreamHandler',  # 打印到屏幕
41             'formatter': 'simple'
42         },
43         # 打印到文件的日志,收集info及以上的日志
44         'default': {
45             'level': 'DEBUG',
46             'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
47             'formatter': 'standard',
48             'filename': logfile_path,  # 日志文件
49             'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
50             'backupCount': 5,
51             'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
52         },
53 
54     },
55     'loggers': {
56         # logging.getLogger(__name__)拿到的logger配置
57         '': {
58             'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
59             'level': 'INFO',
60             'propagate': True,  # 向上(更高level的logger)传递
61         },
62     },
63 }

核心逻辑

src.py源代码:

  1 from interface import user, bank, shopping
  2 from lib import common
  3 
  4 user_data = {
  5     'name': None
  6     # 判断用户是否登入
  7 }
  8 
  9 
 10 def logout():
 11     '''
 12     退出.
 13     :return:
 14     '''
 15     user_data['name'] = None
 16 
 17 
 18 def login():
 19     '''
 20     登入.
 21     :return:
 22     '''
 23     print('登录。。。')
 24     if user_data['name']:
 25         print('你已经登入过了')
 26     count = 0
 27     while True:
 28         name = input('请输入用户名>>:').strip()
 29         if name.lower() == 'q': break
 30         password = input('请输入密码>>:').strip()
 31         flag, msg = user.login_interface(name, password)
 32         if flag:
 33             user_data['name'] = name
 34             print(msg)
 35             break
 36         else:
 37             count += 1
 38             if count == 3:
 39                 user.locked_interface(name)
 40                 print('错误次数过多,已锁定')
 41             else:
 42 
 43                 print(msg)
 44 
 45 
 46 def register():
 47     '''
 48     注册.
 49     :return:
 50     '''
 51     print('注册。。。')
 52     if user_data['name']:
 53         print('你已经登入过了')
 54     while True:
 55         name = input('请输入用户名>>:').strip()
 56         if name.lower() == 'q': break
 57         password = input('请输入密码>>:').strip()
 58         password2 = input('再次输入密码>>:').strip()
 59         if password == password2:
 60             flag, msg = user.register_interface(name, password)
 61             if flag:
 62                 print(msg)
 63                 break
 64             else:
 65                 print('用户已存在')
 66         else:
 67             print('两次密码不一致')
 68 
 69 
 70 @common.login_auth
 71 def check_balance():
 72     '''
 73     查看余额.
 74     :return:
 75     '''
 76     print('查看余额。。。')
 77     balance = bank.check_balance_interface(user_data['name'])
 78     print(balance)
 79 
 80 
 81 @common.login_auth
 82 def transfer():
 83     '''
 84     转账.
 85     :return:
 86     '''
 87     print('转账。。。')
 88     while True:
 89         to_name = input('输入转账的用户>>:').strip()
 90         balance = input('输入转账金额>>:').strip()
 91         if balance.isdigit():
 92             balance = int(balance)
 93             flag, msg = bank.transfer_interface(user_data['name'], to_name, balance)
 94             if flag:
 95                 print(msg)
 96                 break
 97             else:
 98                 print(msg)
 99         else:
100             print('必须输入数字')
101 
102 
103 @common.login_auth
104 def repay():
105     '''
106     还款.
107     :return:
108     '''
109     print('还款。。。')
110     balance = input('请输入还款金额>>:').strip()
111     if balance.isdigit():
112         balance = int(balance)
113         falg, msg = bank.repay_interface(user_data['name'], balance)
114         if falg:
115             print(msg)
116         else:
117             print(msg)
118     else:
119         print('必须输入数字')
120 
121 
122 @common.login_auth
123 def withdraw():
124     '''
125     取款.
126     :return:
127     '''
128     print('取款。。。')
129     balance = input('输入取款金额>>:').strip()
130     if balance.isdigit():
131         balance = int(balance)
132         falg, msg = bank.withdraw_interface(user_data['name'], balance)
133         if falg:
134             print(msg)
135         else:
136             print(msg)
137     else:
138         print('必须输入数字')
139 
140 
141 @common.login_auth
142 def check_record():
143     '''
144     查看流水.
145     :return:
146     '''
147     print('查看流水。。。')
148     bankflow = bank.check_bankflow_interface(user_data['name'])
149     for flow in bankflow:
150         print(flow)
151 
152 
153 @common.login_auth
154 def shop():
155     '''
156     1 先循环打印出商品
157     2 用户输入数字选择商品(判断是否是数字,判断输入的数字是否在范围内)
158     3 取出商品名,商品价格
159     4 判断用户余额是否大于商品价格
160     5 余额大于商品价格时,判断此商品是否在购物车里
161         5.1 在购物车里,个数加1
162         5.1 不在购物车里,拼出字典放入({‘good’:{‘price’:10,‘count’:1}})
163     6 用户余额减掉商品价格
164     7 花费加上商品价格
165     8 当输入 q时,购买商品
166         8.1 消费为0 ,直接退出
167         8.2 打印购物车
168         8.3 接受用户输入,是否购买 当输入y,直接调购物接口实现购物
169     :return:
170     '''
171     print('购物。。。')
172     goods_list = [
173         ['coffee', 10],
174         ['chicken', 20],
175         ['iphone', 8000],
176         ['macPro', 15000],
177         ['car', 100000]
178     ]
179     money = 0
180     user_balance = bank.check_balance_interface(user_data['name'])
181     shopping_cart = {}
182     while True:
183         for i, v in enumerate(goods_list):
184             print(f'{i}: {v}')
185         choice = input('请输入需要购买商品的编号(数字)(q退出)>>:').strip()
186         if choice.isdigit():
187             choice = int(choice)
188             if choice >= len(goods_list):
189                 print('商品不存在')
190                 continue
191             shop_name = goods_list[choice][0]
192             shop_price = goods_list[choice][1]
193             if user_balance >= shop_price:
194                 if shop_name in shopping_cart:
195                     shopping_cart[shop_name]['count'] += 1
196                 else:
197                     shopping_cart[shop_name] = {'price': shop_price, 'count': 1}
198                 user_balance -= shop_price
199                 money += shop_price
200                 print(f'{shop_name}已加入购物车')
201             else:
202                 print('余额不足')
203                 continue
204         elif choice.lower() == 'q':
205             if money == 0:
206                 break
207             print(shopping_cart)
208             user = input('是否购买Y/N>>:').strip()
209             if user.lower() == 'y':
210                 falg, msg = shopping.shopping_interface(user_data['name'], money, shopping_cart)
211                 if falg:
212                     print(msg)
213                     break
214                 else:
215                     print(msg)
216                     break
217             elif user.lower() == 'n':
218                 print('你什么都没有购买')
219                 break
220             else:
221                 print('无选项')
222                 continue
223 
224         else:
225             print('输入非法字符')
226 
227 
228 @common.login_auth
229 def check_shopping_cart():
230     '''
231     查看购物车.
232     :return:
233     '''
234     print('查看购物车。。。')
235     shoppingcart = shopping.check_shoppingcart(user_data['name'])
236     if shoppingcart:
237         print(shoppingcart)
238     else:
239         print('无商品')
240 
241 
242 func_dic = {
243     '1': login,
244     '2': register,
245     '3': check_balance,
246     '4': transfer,
247     '5': repay,
248     '6': withdraw,
249     '7': check_record,
250     '8': shop,
251     '9': check_shopping_cart,
252     '10': logout
253 }
254 
255 
256 def run():
257     '''
258     功能选择接口.
259     :return:
260     '''
261     while True:
262         print('''选择需要的功能:
263 1、登入
264 2、注册
265 3、查看余额
266 4、转账
267 5、还款
268 6、取款
269 7、查看流水
270 8、购物
271 9、查看购买商品
272 10、退出程序
273 ''')
274         choice = input('编号>>:').strip()
275         if choice in func_dic:
276             func_dic[choice]()

用户数据

db_handler.py源代码:

 1 import os
 2 import json
 3 from conf import setting
 4 
 5 
 6 def save(user_dic):
 7     '''
 8     保存用户信息文件.
 9     :param user_dic: 用户信息
10     :return:
11     '''
12     user_path = os.path.join(setting.BASE_DB, '%s.json' % user_dic['name'])
13     with open(user_path, 'w', encoding='utf-8')as f:
14         json.dump(user_dic, f)
15         f.flush()
16 
17 
18 #
19 def select(name):
20     """
21    查询用户文件.
22     :param name: str --> 用户名
23     :return: None, user_dic
24     """
25     user_path = os.path.join(setting.BASE_DB, '%s.json' % name)
26     if os.path.exists(user_path):
27         with open(user_path, 'r', encoding='utf-8')as f:
28             user_dic = json.load(f)
29             return user_dic
30     else:
31         return None

银行接口

bank.py源代码:

  1 from db import db_handler
  2 from core import src
  3 from lib import common
  4 
  5 bank_logger = common.get_logger('bank')
  6 
  7 
  8 def check_balance_interface(name):
  9     '''
 10     查询余额接口.
 11     :param name:账户名
 12     :return:balance
 13     '''
 14     user_dic = db_handler.select(name)
 15     balance = user_dic['balance']
 16     return balance
 17 
 18 
 19 def transfer_interface(from_name, to_name, balance):
 20     '''
 21     转账接口.
 22     :param from_name:转账用户
 23     :param to_name: 收款用户
 24     :param balance: 转账金额
 25     :return:True,False
 26     '''
 27     if from_name == to_name:
 28         return False, '不能给自己转账'
 29     to_dic = db_handler.select(to_name)
 30     if to_dic:
 31         from_dic = db_handler.select(from_name)
 32         if from_dic['balance'] >= balance:
 33             to_dic['balance'] += balance
 34             from_dic['balance'] -= balance
 35             from_dic['bankflow'].append('你向%s转账%s元' % (to_name, balance))
 36             to_dic['bankflow'].append('你收到%s的转账%s元' % (from_name, balance))
 37             bank_logger.info('%s向%s转账%s元' % (from_name, to_name, balance))
 38             db_handler.save(from_dic)
 39             db_handler.save(to_dic)
 40             return True, '转账成功'
 41         else:
 42             return False, '余额不足'
 43     else:
 44         return False, '用户不存在'
 45 
 46 
 47 def repay_interface(name, balance):
 48     '''
 49     还款接口.
 50     :param name: 还款用户
 51     :param balance: 还款金额
 52     :return:True,False
 53     '''
 54     user_dic = db_handler.select(name)
 55     if user_dic['balance'] >= balance:
 56         user_dic['balance'] -= balance
 57         user_dic['bankflow'].append('还款%s' % balance)
 58         bank_logger.info('%s还款了%s元' % (name, balance))
 59         db_handler.save(user_dic)
 60         return True, '还款成功'
 61     else:
 62         return False, '余额不足以还款'
 63 
 64 
 65 def withdraw_interface(name, balance):
 66     '''
 67     取款接口.
 68     :param name: 取款用户
 69     :param balance: 取款金额
 70     :return:True,False
 71     '''
 72     user_dic = db_handler.select(name)
 73     if user_dic['balance'] >= balance * 1.05:  # 0.5%的手续费
 74         user_dic['balance'] -= balance * 1.05
 75         user_dic['bankflow'].append('取款%s,手续费%s' % (balance, balance * 0.05))
 76         bank_logger.info('你取款了%s元,手续费%s元' % (balance, balance * 0.05))
 77         db_handler.save(user_dic)
 78         return True, '取款成功,取出金额%s' % balance
 79     else:
 80         return False, '余额不足'
 81 
 82 
 83 def consume_interface(name, money):
 84     '''
 85     消费接口.
 86     :param name: 消费用户
 87     :param money: 消费金额
 88     :return:True,False
 89     '''
 90     user_dic = db_handler.select(name)
 91     if user_dic['balance'] >= money:
 92         user_dic['balance'] -= money
 93         db_handler.save(user_dic)
 94         return True, '扣款成功'
 95     else:
 96         return False, '余额不足'
 97 
 98 
 99 def check_bankflow_interface(name):
100     '''
101     银行流水.
102     :param name: 账户名
103     :return:user_bankflow
104     '''
105     user_dic = db_handler.select(name)
106     user_bankflow = user_dic['bankflow']
107     return user_bankflow

购物接口

shopping.p源代码:

 1 from db import db_handler
 2 from interface import bank
 3 
 4 
 5 def shopping_interface(name, money, shoppingcart):
 6     '''
 7     购物接口.
 8     :param name:用户名
 9     :param money: 消费金额
10     :param shoppingcart: 购物车清单
11     :return:True,False
12     '''
13     flag, msg = bank.consume_interface(name, money)
14     if flag:
15         user_dic = db_handler.select(name)
16         user_dic['shoppingcart'] = shoppingcart
17         db_handler.save(user_dic)
18         return True, '购买成功'
19     else:
20         return False, '余额不足'
21 
22 
23 def check_shoppingcart(name):
24     '''
25     查看购物车接口.
26     :param name: 用户名
27     :return:user_dic['shoppingcart']
28     '''
29     user_dic = db_handler.select(name)
30 
31     return user_dic['shoppingcart']

用户信息接口

user.py源代码:

 1 from db import db_handler
 2 from lib import common
 3 
 4 user_logger = common.get_logger('user')
 5 
 6 
 7 def login_interface(name, password):
 8     '''
 9     登入接口.
10     :param name:用户名
11     :param password: 用户密码
12     :return:True,False
13     '''
14     user_dic = db_handler.select(name)
15     if user_dic:  # {'name': 'song', 'password': '123'}
16         if password == user_dic['password'] and not user_dic['locked']:
17             user_logger.info('用户%s登入了账户' % name)
18             return True, '登入成功'
19         else:
20             return False, '用户名密码错误或被锁定'
21     else:
22         return False, '用户不存在'
23 
24 
25 def register_interface(name, password, balance=15000):
26     '''
27     注册接口.
28     :param name:用户名
29     :param password: 密码
30     :param balance: 确认密码
31     :return:True,False
32     '''
33     user_dic = db_handler.select(name)
34     if user_dic:
35         return False, '用户已存在'
36     else:
37         user_dic = {'name': name, 'password': password, 'balance': balance,
38                     'locked': False, 'bankflow': [], 'shoppingcart': {}}
39         db_handler.save(user_dic)
40         user_logger.info('用户%s注册成功' % name)
41         return True, '注册成功'
42 
43 
44 def locked_interface(name):
45     '''
46     锁定接口.
47     :param name:用户名
48     :return:
49     '''
50     user_dic = db_handler.select(name)
51     if user_dic:
52         user_dic['locked'] = True
53         db_handler.save(user_dic)
54 
55 
56 def un_locked_interface(name):
57     '''
58     解锁用户.
59     :param name:用户名
60     :return:
61     '''
62     user_dic = db_handler.select(name)
63     if user_dic:
64         user_dic['locked'] = False
65         db_handler.save(user_dic)

模块工具

common.py源代码:

 1 from core import src
 2 import logging.config
 3 from conf import setting
 4 
 5 
 6 def login_auth(func):
 7     '''
 8     装饰器
 9     :param func: 函数名
10     :return: wrapper
11     '''
12 
13     def wrapper(*args, **kwargs):
14         if not src.user_data['name']:
15             src.login()
16         else:
17             return func(*args, **kwargs)
18 
19     return wrapper
20 
21 
22 def get_logger(name):
23     '''
24     盗用日志字典.
25     :param name:日志名字
26     :return:
27     '''
28     logging.config.dictConfig(setting.LOGGING_DIC)  # 使用这个日志字典
29     logger = logging.getLogger('name')
30     return logger

功能演示:

1、注册:

《ATM功能实现项目》

 

 2、登入:

《ATM功能实现项目》

 

 3、查看余额:

《ATM功能实现项目》

 

 4、转账:

《ATM功能实现项目》

 

 log文件记录的日志:

《ATM功能实现项目》

 

 5、还款:

《ATM功能实现项目》

 

 6、取款:

《ATM功能实现项目》

 

 7、查看流水:

《ATM功能实现项目》

查看song的流水:

《ATM功能实现项目》

 8、购物:

 

 《ATM功能实现项目》

 

 《ATM功能实现项目》

 

 《ATM功能实现项目》

 

 9、查看购物车:

《ATM功能实现项目》

 

 10、退出登入(退出后需要重新登入)

感谢观看!记得双击么么哒!

 
点赞