• Python实现ATM+购物商城


    Python实现ATM+购物商城

    一、程序介绍

    需求:

    模拟实现一个ATM + 购物商城程序
        额度 15000或自定义
        实现购物商城,买东西加入 购物车,调用信用卡接口结账
        可以提现,手续费5%
        每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息
        支持多账户登录
        支持账户间转账
        记录每月日常消费流水
        提供还款接口
        ATM记录操作日志 
        提供管理接口,包括添加账户、用户额度,冻结账户等。。。
        用户认证用装饰器
    
    实现功能:
        额度 15000或自定义
        实现购物商城,买东西加入 购物车,调用信用卡接口结账
        可以提现,手续费5%
        支持多账户登录
        记录每月日常消费流水
        提供还款接口
        ATM记录操作日志 
        提供管理接口,包括添加账户、用户额度,冻结账户等。。。
        用户认证用装饰器
    

    程序结构:

    atm/
    ├── README
    └── atm #ATM主程目录
       ├── __init__.py
       ├── bin #ATM 执行文件 目录
       │   ├── __init__.py
       │   ├── atm.py  #ATM 执行程序
       │   └── manage.py #ATM 管理端,未实现
       ├── conf #配置文件
       │   ├── __init__.py
       │   └── settings.py
       ├── core #主要程序逻辑都 在这个目录 里
       │   ├── __init__.py
       │   ├── accounts.py  #用于从文件里加载和存储账户数据
       │   ├── auth.py      #用户认证模块
       │   ├── db_handler.py   #数据库连接引擎
       │   ├── logger.py       #日志记录模块
       │   ├── main.py         #主逻辑交互程序
       │   └── transaction.py  #记账还钱取钱等所有的与账户金额相关的操作都 在这
       ├── db  #用户数据存储的地方
       │   ├── __init__.py
       │   ├── account_sample.py #生成一个初始的账户数据 ,把这个数据 存成一个 以这个账户id为文件名的文件,放在accounts目录 就行了,程序自己去会这里找
       │   └── accounts #存各个用户的账户数据 ,一个用户一个文件
       │       └── 1234.json #一个用户账户示例文件
       └── log #日志目录
           ├── __init__.py
           ├── access.log #用户访问和操作的相关日志
           └── transactions.log    #所有的交易日志

    二、流程图

    三、代码

    bin/atm.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 import os
     5 import sys
     6 base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
     7 print(base_dir)
     8 sys.path.append(base_dir)
     9 
    10 from core import main
    11 
    12 if __name__ == '__main__':
    13     main.run()
    View Code

    conf/settings.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 import os
     4 import sys
     5 import logging
     6 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
     7 
     8 
     9 DATABASE = {
    10     'engine': 'file_storage', #support mysql,postgresql in the future
    11     'name':'accounts',
    12     'path': "%s/db" % BASE_DIR
    13 }
    14 
    15 
    16 LOG_LEVEL = logging.INFO
    17 LOG_TYPES = {
    18     'transaction': 'transactions.log',
    19     'access': 'access.log',
    20 }
    21 
    22 TRANSACTION_TYPE = {
    23     'repay':{'action':'plus', 'interest':0.03},
    24     'withdraw':{'action':'minus', 'interest':0.05},
    25     'transfer':{'action':'minus', 'interest':0.05},
    26     'consume':{'action':'minus', 'interest':0},
    27 
    28 }
    View Code

    core/main.py

      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 
      4 '''
      5 main program handle module , handle all the user interaction stuff
      6 
      7 '''
      8 
      9 from core import auth
     10 from core import accounts
     11 from core import logger
     12 from core import accounts
     13 from core import transaction
     14 from core.auth import login_required
     15 import time
     16 
     17 #transaction logger
     18 trans_logger = logger.logger('transaction')
     19 #access logger
     20 access_logger = logger.logger('access')
     21 
     22 
     23 #temp account data ,only saves the data in memory
     24 user_data = {
     25     'account_id':None,
     26     'is_authenticated':False,
     27     'account_data':None
     28 
     29 }
     30 
     31 def account_info(acc_data):
     32     account_data = accounts.load_current_balance(acc_data['account_id'])
     33     data_info = u'''
     34     33[34;1m 账号ID:%s
     35     余额:  %s
     36     信用度:%s
     37     账号注册时间:%s
     38     账号过期时间:%s
     39     工资天数:%s
     40     33[0m'''%(acc_data['account_id'],
     41                 account_data['balance'],
     42                 account_data['credit'],
     43                 account_data['enroll_date'],
     44                 account_data['expire_date'],
     45                 account_data['pay_day'],)
     46     print(data_info)
     47 
     48 
     49 @login_required
     50 def repay(acc_data):
     51     '''
     52     print current balance and let user repay the bill
     53     :return:
     54     '''
     55     account_data = accounts.load_current_balance(acc_data['account_id'])
     56     #再从硬盘加载一次数据, 为了确保数据是最新的
     57     #for k,v in account_data.items():
     58     #    print(k,v )
     59     current_balance= ''' --------- BALANCE INFO --------
     60         Credit :    %s
     61         Balance:    %s''' %(account_data['credit'],account_data['balance'])
     62     print(current_balance)
     63     back_flag = False
     64     while not back_flag:
     65         repay_amount = input("33[33;1mInput repay amount:33[0m").strip()
     66         if len(repay_amount) >0 and repay_amount.isdigit():
     67             #print('ddd 00')
     68             new_balance = transaction.make_transaction(trans_logger,account_data,'repay', repay_amount)
     69             if new_balance:
     70                 print('''33[42;1mNew Balance:%s33[0m''' %(new_balance['balance']))
     71 
     72         else:
     73             print('33[31;1m[%s] is not a valid amount, only accept integer!33[0m' % repay_amount)
     74 
     75         if repay_amount == 'b':
     76             back_flag = True
     77 def withdraw(acc_data):
     78     '''
     79     print current balance and let user do the withdraw action
     80     :param acc_data:
     81     :return:
     82     '''
     83     account_data = accounts.load_current_balance(acc_data['account_id'])
     84     current_balance= ''' --------- BALANCE INFO --------
     85         Credit :    %s
     86         Balance:    %s''' %(account_data['credit'],account_data['balance'])
     87     print(current_balance)
     88     back_flag = False
     89     while not back_flag:
     90         withdraw_amount = input("33[33;1mInput withdraw amount:33[0m").strip()
     91         if len(withdraw_amount) >0 and withdraw_amount.isdigit():
     92             new_balance = transaction.make_transaction(trans_logger,account_data,'withdraw', withdraw_amount)
     93             if new_balance:
     94                 print('''33[42;1mNew Balance:%s33[0m''' %(new_balance['balance']))
     95 
     96         else:
     97             print('33[31;1m[%s] is not a valid amount, only accept integer!33[0m' % withdraw_amount)
     98 
     99         if withdraw_amount == 'b':
    100             back_flag = True
    101 
    102 def transfer(acc_data):
    103     pass
    104 def pay_check(acc_data):
    105     pass
    106 def logout(acc_data):
    107     exit()
    108 
    109 
    110 def shopping(acc_data):
    111     '''
    112 
    113     :param acc_data:
    114     :return:
    115     '''
    116     product_list = [
    117         ['Iphone7 Plus', 6500],
    118         ['Iphone8 ', 8200],
    119         ['MacBook Pro', 12000],
    120         ['Python Book', 99],
    121         ['Coffee', 33],
    122         ['Bike', 666],
    123         ['pen', 2]
    124     ]
    125     shopping_cart = []
    126     count = 0
    127     salary = acc_data['account_data']['balance']
    128     while True:
    129         account_data = accounts.load_current_balance(acc_data['account_id'])
    130         print(">> 欢迎来到电子商城 您的余额是 %s 元<<" % (salary))
    131         for index, i in enumerate(product_list):  # 循环商品列表,商品列表索引
    132             print("%s.	%s	%s" % (index, i[0], i[1]))  # 打印商品列表,显示商品列表索引
    133         choice = input(">>请输入商品序号或输入 exit 退出商城>>: ").strip()
    134         if len(choice) == 0:  # 判断输入字符串是否为空和字符串长度
    135             print('-->您没有选择商品<--')
    136             continue
    137         if choice.isdigit():  # 判断输入的choice是不是一个数字
    138             choice = int(choice)  # 把输入的字符串转成整型
    139             if choice < len(product_list) and choice >= 0:  # 输入的整数必须小于商品列表的数量
    140                 product_item = product_list[choice]  # 获取商品
    141                 if salary >= product_item[1]:  # 拿现有金额跟商品对比,是否买得起
    142                     salary -= product_item[1]  # 扣完商品的价格
    143                     shopping_cart.append(product_item)  # 把选着的商品加入购物车
    144                     print("添加 33[32;1m%s33[0m 到购物车,您目前的金额是 
    145     33[31;1m%s33[0m" % (product_item[0], salary))
    146                 else:
    147                     print("对不起,您的金额不足,还差 33[31;1m%s33[0m" % (product_item[1] - salary,))
    148             else:
    149                 print("-->没有此商品<--")
    150         elif choice == "exit":
    151             total_cost = 0
    152             print("您的购物车列表:")
    153             for i in shopping_cart:
    154                 print(i)
    155                 total_cost += i[1]
    156             print("您的购物车总价是: 33[31;1m%s33[0m" % (total_cost,))
    157             print("您目前的余额是: 33[31;1m%s33[0m" % (salary,))
    158             new_balance = transaction.make_transaction(trans_logger, account_data, 'withdraw', total_cost)
    159             if new_balance:
    160                 print('''33[42;1mNew Balance:%s33[0m''' % (new_balance['balance']))
    161             break
    162 
    163 
    164 def interactive(acc_data):
    165     '''
    166     interact with user
    167     :return:
    168     '''
    169     menu = u'''
    170     ------- hehe Bank ---------
    171     33[32;1m
    172     1.  账户信息(实现)
    173     2.  还款(实现)
    174     3.  取款(实现)
    175     4.  转账
    176     5.  账单
    177     6.  商城(实现)
    178     7.  退出(实现)
    179     33[0m'''
    180     menu_dic = {
    181         '1': account_info,
    182         '2': repay,
    183         '3': withdraw,
    184         '4': transfer,
    185         '5': pay_check,
    186         '6': shopping,
    187         '7': logout,
    188     }
    189     exit_flag = False
    190     while not exit_flag:
    191         print(menu)
    192         user_option = input(">>:").strip()
    193         if user_option in menu_dic:
    194             #print('accdata',acc_data)
    195             #acc_data['is_authenticated'] =False
    196             menu_dic[user_option](acc_data)
    197 
    198         else:
    199             print("33[31;1mOption does not exist!33[0m")
    200 def run():
    201     '''
    202     this function will be called right a way when the program started, here handles the user interaction stuff
    203     :return:
    204     '''
    205     acc_data = auth.acc_login(user_data,access_logger)
    206     if user_data['is_authenticated']:
    207         user_data['account_data'] = acc_data
    208         interactive(user_data)
    View Code

    core/transaction.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 from conf import settings
     5 from core import accounts
     6 from core import logger
     7 #transaction logger
     8 
     9 
    10 
    11 def make_transaction(log_obj,account_data,tran_type,amount,**others):
    12     '''
    13     deal all the user transactions
    14     :param account_data: user account data
    15     :param tran_type: transaction type
    16     :param amount: transaction amount
    17     :param others: mainly for logging usage
    18     :return:
    19     '''
    20     amount = float(amount)
    21     if tran_type in  settings.TRANSACTION_TYPE:
    22 
    23         interest =  amount * settings.TRANSACTION_TYPE[tran_type]['interest']
    24         old_balance = account_data['balance']
    25         if settings.TRANSACTION_TYPE[tran_type]['action'] == 'plus':
    26             new_balance = old_balance + amount + interest
    27         elif settings.TRANSACTION_TYPE[tran_type]['action'] == 'minus':
    28             new_balance = old_balance - amount - interest
    29             #check credit
    30             if  new_balance <0:
    31                 print('''33[31;1mYour credit [%s] is not enough for this transaction [-%s], your current balance is
    32                 [%s]''' %(account_data['credit'],(amount + interest), old_balance ))
    33                 return
    34         account_data['balance'] = new_balance
    35         accounts.dump_account(account_data) #save the new balance back to file
    36         log_obj.info("account:%s   action:%s    amount:%s   interest:%s" %
    37                           (account_data['id'], tran_type, amount,interest) )
    38         return account_data
    39     else:
    40         print("33[31;1mTransaction type [%s] is not exist!33[0m" % tran_type)
    View Code

    core/accounts.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 import json
     5 import time
     6 from core import db_handler
     7 from conf import settings
     8 
     9 
    10 def load_current_balance(account_id):
    11     '''
    12     return account balance and other basic info
    13     :param account_id:
    14     :return:
    15     '''
    16     # db_path = db_handler.db_handler(settings.DATABASE)
    17     # account_file = "%s/%s.json" %(db_path,account_id)
    18     #
    19     db_api = db_handler.db_handler()
    20     data = db_api("select * from accounts where account=%s" % account_id)
    21 
    22     return data
    23 
    24     # with open(account_file) as f:
    25     #     acc_data = json.load(f)
    26     #     return  acc_data
    27 def dump_account(account_data):
    28     '''
    29     after updated transaction or account data , dump it back to file db
    30     :param account_data:
    31     :return:
    32     '''
    33     db_api = db_handler.db_handler()
    34     data = db_api("update accounts where account=%s" % account_data['id'],account_data=account_data)
    35 
    36     # db_path = db_handler.db_handler(settings.DATABASE)
    37     # account_file = "%s/%s.json" %(db_path,account_data['id'])
    38     # with open(account_file, 'w') as f:
    39     #     acc_data = json.dump(account_data,f)
    40 
    41     return True
    View Code

    core/auth.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 import os
     4 from core import db_handler
     5 from conf import settings
     6 from core import logger
     7 import json
     8 import time
     9 
    10 
    11 
    12 def login_required(func):
    13     "验证用户是否登录"
    14 
    15     def wrapper(*args,**kwargs):
    16         #print('--wrapper--->',args,kwargs)
    17         if args[0].get('is_authenticated'):
    18             return func(*args,**kwargs)
    19         else:
    20             exit("User is not authenticated.")
    21     return wrapper
    22 
    23 
    24 def acc_auth(account,password):
    25     '''
    26     account auth func
    27     :param account: credit account number
    28     :param password: credit card password
    29     :return: if passed the authentication , retun the account object, otherwise ,return None
    30     '''
    31     db_path = db_handler.db_handler(settings.DATABASE)
    32     account_file = "%s/%s.json" %(db_path,account)
    33     print(account_file)
    34     if os.path.isfile(account_file):
    35         with open(account_file,'r') as f:
    36             account_data = json.load(f)
    37             if account_data['password'] == password:
    38                 exp_time_stamp = time.mktime(time.strptime(account_data['expire_date'], "%Y-%m-%d"))
    39                 if time.time() >exp_time_stamp:
    40                     print("33[31;1mAccount [%s] has expired,please contact the back to get a new card!33[0m" % account)
    41                 else: #passed the authentication
    42                     return  account_data
    43             else:
    44                 print("33[31;1mAccount ID or password is incorrect!33[0m")
    45     else:
    46         print("33[31;1mAccount [%s] does not exist!33[0m" % account)
    47 
    48 
    49 def acc_auth2(account,password):
    50     '''
    51     优化版认证接口
    52     :param account: credit account number
    53     :param password: credit card password
    54     :return: if passed the authentication , retun the account object, otherwise ,return None
    55 
    56     '''
    57     db_api = db_handler.db_handler()  #连接数据库 file_execute内存地址
    58     data = db_api("select * from accounts where account=%s" % account) #执行sql
    59     if data['password'] == password:
    60         exp_time_stamp = time.mktime(time.strptime(data['expire_date'], "%Y-%m-%d"))
    61         if time.time() > exp_time_stamp:
    62             print("33[31;1mAccount [%s] has expired,please contact the back to get a new card!33[0m" % account)
    63         else:  # passed the authentication
    64             return data
    65     else:
    66         print("33[31;1mAccount ID or password is incorrect!33[0m")
    67 
    68 def acc_login(user_data,log_obj):
    69     '''
    70     account login func
    71     :user_data: user info data , only saves in memory
    72     :return:
    73     '''
    74     retry_count = 0
    75     while user_data['is_authenticated'] is not True and retry_count < 3 :
    76         account = input("33[32;1maccount:33[0m").strip()
    77         password = input("33[32;1mpassword:33[0m").strip()
    78         auth = acc_auth2(account, password)
    79         if auth: #not None means passed the authentication
    80             user_data['is_authenticated'] = True
    81             user_data['account_id'] = account
    82             #print("welcome")
    83             return auth
    84         retry_count +=1
    85     else:
    86         log_obj.error("account [%s] too many login attempts" % account)
    87         exit()
    View Code

    core/db_handler.py

     1 #!_*_coding:utf-8_*_
     2 #__author__:"Alex Li"
     3 
     4 '''
     5 handle all the database interactions
     6 '''
     7 import json,time ,os
     8 from  conf import settings
     9 def file_db_handle(conn_params):
    10     '''
    11     parse the db file path
    12     :param conn_params: the db connection params set in settings
    13     :return:
    14     '''
    15     # print('file db:',conn_params)
    16     #db_path ='%s/%s' %(conn_params['path'],conn_params['name'])
    17     return file_execute
    18 def db_handler():
    19     '''
    20     connect to db
    21     :param conn_parms: the db connection params set in settings
    22     :return:a
    23     '''
    24     conn_params = settings.DATABASE
    25     if conn_params['engine'] == 'file_storage':
    26         return file_db_handle(conn_params)
    27     elif conn_params['engine'] == 'mysql':
    28         pass #todo
    29 
    30 
    31 
    32 def file_execute(sql,**kwargs):
    33     conn_params = settings.DATABASE
    34     db_path = '%s/%s' % (conn_params['path'], conn_params['name'])
    35 
    36     # print(sql,db_path)
    37     sql_list = sql.split("where")
    38     # print(sql_list)
    39     if sql_list[0].startswith("select") and len(sql_list)> 1:#has where clause
    40         column,val = sql_list[1].strip().split("=")
    41 
    42         if column == 'account':
    43             account_file = "%s/%s.json" % (db_path, val)
    44             print(account_file)
    45             if os.path.isfile(account_file):
    46                 with open(account_file, 'r') as f:
    47                     account_data = json.load(f)
    48                     return account_data
    49             else:
    50                 exit("33[31;1mAccount [%s] does not exist!33[0m" % val )
    51 
    52     elif sql_list[0].startswith("update") and len(sql_list)> 1:#has where clause
    53         column, val = sql_list[1].strip().split("=")
    54         if column == 'account':
    55             account_file = "%s/%s.json" % (db_path, val)
    56             #print(account_file)
    57             if os.path.isfile(account_file):
    58                 account_data = kwargs.get("account_data")
    59                 with open(account_file, 'w') as f:
    60                     acc_data = json.dump(account_data, f)
    61                 return True
    View Code

    core/logger.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 '''
     5 handle all the logging works
     6 '''
     7 
     8 import logging
     9 from conf import settings
    10 
    11 def logger(log_type):
    12 
    13     #create logger
    14     logger = logging.getLogger(log_type)
    15     logger.setLevel(settings.LOG_LEVEL)
    16 
    17 
    18     # create console handler and set level to debug
    19     ch = logging.StreamHandler()
    20     ch.setLevel(settings.LOG_LEVEL)
    21 
    22     # create file handler and set level to warning
    23     log_file = "%s/log/%s" %(settings.BASE_DIR, settings.LOG_TYPES[log_type])
    24     fh = logging.FileHandler(log_file)
    25     fh.setLevel(settings.LOG_LEVEL)
    26     # create formatter
    27     formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    28 
    29     # add formatter to ch and fh
    30     ch.setFormatter(formatter)
    31     fh.setFormatter(formatter)
    32 
    33     # add ch and fh to logger
    34     logger.addHandler(ch)
    35     logger.addHandler(fh)
    36 
    37     return logger
    38     # 'application' code
    39     '''logger.debug('debug message')
    40     logger.info('info message')
    41     logger.warn('warn message')
    42     logger.error('error message')
    43     logger.critical('critical message')'''
    View Code

    db/account_sample.py

     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 
     5 import json
     6 acc_dic = {
     7     'id': 1234,
     8     'password': 'abc',
     9     'credit': 15000,
    10     'balance': 15000,
    11     'enroll_date': '2016-01-02',
    12     'expire_date': '2021-01-01',
    13     'pay_day': 22,
    14     'status': 0 # 0 = normal, 1 = locked, 2 = disabled
    15 }
    16 
    17 print(json.dumps(acc_dic))
    View Code




  • 相关阅读:
    西卡编程教学 C语言教学视频(共32课更新完毕) 『 西卡教学 』 西卡学院 Powered by Pureing Labs!
    大图片新闻的体验还是不错的
    分享:用php抓取网页内容方法总结
    “深圳文献港”昨日正式开通_综合新闻_财经_腾讯网
    分享:EJDB 1.0.37 发布,嵌入式 JSON 数据库引擎
    分享:MetaModel 3.2.5 发布,数据库元模型
    RQ: Simple job queues for Python
    分享:开源主机项目 Ouya 发布 SDK
    RQ 简单的任务队列 品牌控
    信息论、推理与学习算法(翻译版)
  • 原文地址:https://www.cnblogs.com/zhuzhiwen/p/7664493.html
Copyright © 2020-2023  润新知