代码改变世界

ATM

2018-05-02 17:23  钱先生  阅读(293)  评论(0编辑  收藏  举报

ATM 要求

 

 

示例代码: https://github.com/triaquae/py_training/tree/master/sample_code/day5-atm

 

================================================================================================================================= 

作业代码: https://github.com/cheese320/atm

 

bin

  • main.py 主程序
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)  # 这样这个路径会在最后面,每次都要遍历到最后才能找到(如果中途没有同名文件的话)
sys.path.insert(0, path)  # 将该路径插到前面去
from core import tools


def run():
    exit_flag = False
    # 普通用户
    if tools.username != "admin":
        # 能打印帐户信息的都是正常状态的帐户, 所以这里不显示帐户状态
        initial = '''
        ---------- {_name}帐户信息 --------- 
        credit: {_credit}
        balance: {_balance}
        enroll-date: {_enroll}
        expire_date: {_expire}
        pay_day: {_payday}
        ------------ The End ----------- 
        '''.format(_name=tools.username,
                   _credit=tools.info[tools.username]["credit"],
                   _balance=tools.info[tools.username]["balance"],
                   _enroll=tools.info[tools.username]["enroll_date"],
                   _expire=tools.info[tools.username]["expire_date"],
                   _payday=tools.info[tools.username]["pay_day"])
        print(initial)
        while not exit_flag:
            msg = '''
            1. 商城
            2. 还款
            3. 取款
            4. 转帐
            5. 帐单
            6. 退出
            '''
            print("功能列表".center(20, "*"))
            print(msg)
            print("The end".center(23, "*"))
            user_choice = input("请选择功能数字代码: ")
            if user_choice == "quit":
                exit_flag = True
            elif not user_choice.isdigit():
                print("输入有误, 请重输")
                break
            else:
                seq = int(user_choice)
                if seq <= 0 or seq > 6:
                    print("输入有误, 请重输")
                elif seq == 1:
                    tools.shopping_mall()
                elif seq == 2:
                    tools.repay()
                elif seq == 3:
                    tools.withdraw()
                elif seq == 4:
                    tools.transfer()
                elif seq == 5:
                    tools.bill()
                elif seq == 6:
                    exit()

    else:
        while not exit_flag:
            msg2 = '''
            1. 添加帐户
            2. 解锁帐户
            3. 修改用户额度
            4. 冻结帐户
            5. 解冻帐户
            6. 退出
            '''
            print("功能列表".center(20, "*"))
            print(msg2)
            print("The end".center(23, "*"))
            print("\r\n")
            user_choice2 = input("请选择功能数字代码: ")
            if user_choice2 == "quit":
                exit_flag = True
            elif not user_choice2.isdigit():
                print("输入有误, 请重输")
                break
            else:
                seq2 = int(user_choice2)
                if seq2 <= 0 or seq2 > 6:
                    print("输入有误, 请重输")
                elif seq2 == 1:
                    tools.new_account()
                elif seq2 == 2:
                    tools.unlock()
                elif seq2 == 3:
                    tools.adjustment()
                elif seq2 == 4:
                    tools.freeze()
                elif seq2 == 5:
                    tools.unfreeze()
                elif seq2 == 6:
                    exit()


run()
View Code
  • path_define.py 执行主程序
#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import sys


base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, base_dir)


from bin import main
if __name__ == '__main__':
    main.run()
View Code

 

conf

  • settings.py 数据库,log,路径等
#!/usr/bin/python
# -*- coding: utf-8 -*-
import logging
import os


BASE_DIR =os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


DATABASE = {
    'engine': 'file_storage',  # support mysql, postgresql in the future
    'name': 'account',
    'path': "%s\db" % BASE_DIR
}


LOG_LEVEL = logging.INFO
LOG_TYPES = {
    'transaction': 'transactions.log',
    'access': 'access.log'
}


TRANSACTION_TYPE = {
    'repay': {'action': 'plus', 'interest': 0},
    'withdraw': {'action': 'minus', 'interest': 0.05},
    'transfer': {'action': 'minus', 'interest': 0.05},
    'consume': {'action': 'minus', 'interest': 0},
}
View Code

 

core

  • db_handle.py 处理json文件(读, 写)
#!/usr/bin/python
# -*- coding: utf-8 -*-


def file_db_handle(database):
    """
    数据存于文件
    :param database:
    :return: 文件存储路径
    """
    db_path = "%s" % (database['path'])
    return db_path



def sql_db_handle(database):
    """
    数据存于数据库
    :param database:
    :return:
    """
    pass


def handle(database):
    """
    根据数据存储方式判断如何获取数据
    :param database:
    :return:
    """
    if database['engine'] == 'file_storage':
        return file_db_handle(database)
    elif database['engine'] == 'sql':
        return sql_db_handle(database)
    else:
        pass
View Code

 

db

  • account.json 帐户信息
  • repository.json 商品信息
  • shopping_list.json 已购买清单
{"Alex": {"password": "aaa", "credit": 78000, "balance": 15000.0, "enroll_date": "2017-01-01", "expire_date": "2021-01-01", "pay_day": 22, "status": 0}, "Lucy": {"password": "bbb", "credit": 15000, "balance": 15000, "enroll_date": "2017-02-02", "expire_date": "2022-02-02", "pay_day": 22, "status": 0}, "Jack": {"password": "ccc", "credit": 15000, "balance": 15000, "enroll_date": "2017-03-03", "expire_date": "2023-03-03", "pay_day": 22, "status": 2}, "admin": {"password": "root", "status": 0}, "David,999": {"password": "999", "credit": 150000, "balance": 15000, "enroll_date": "2017-01-01", "expire_date": "2021-01-01", "pay_day": 22, "status": 0}}
account.json
{"iphone": {"inventory": 20, "price": 9000}, "book": {"inventory": 3, "price": 50}, "bike": {"inventory": 90, "price": 600}, "cd": {"inventory": 100, "price": 83}, "fish": {"inventory": 90, "price": 40}}
repository.json
{"Alex": {"cd": {"buy_qty": 5, "spent": 249}, "book": {"buy_qty": 8, "spent": 100}, "iphone": {"buy_qty": 1, "spent": 9000}}, "Lucy": {"book": {"buy_qty": 3, "spent": 150}, "iphone": {"buy_qty": 1, "spent": 9000}}, "Jack": {}}
shopping_list.json

 

doc

  • flowchart
  • idea.txt 设计思路

1. 主程序
/*
错误提示字体颜色: 34
成功提示字体颜色: 31

帐户状态:
0 - normal
1 - locked
2 - frozen

退出 :
1. 在任一子程序, 输入quit返回上一级.
2. 在功能列表层级, 输入quit结束程序.
*/
    1.1, 判断用户是否登录, 并返回用户信息.
         def login()
         A. 普通用户 (进入循环菜单)
            1.1.1, 打印功能列表
                 1). 商城
                     def shopping_mall()
                 2). 还款
                     def repay()
                 3). 取款
                     def "withdraw()
                 4). 转帐
                     def transfer()
                 5). 帐单
                     def bill()
                 6). 退出
         B. 管理帐户 (进入循环菜单)
            1.1.2, 打印功能列表
                 1). 添加帐户
                     def new_account()
                 2). 解锁帐户
                     def unlock()
                 3). 修改用户额度
                     def adjustment()
                 4). 冻结帐户
                     def freeze()
                 5). 解冻帐户
                     def unfreeze()
                 6). 退出
2. 登录
   def login()
   2.1, 用户输入用户名密码
        1). 对用户输入进行处理strip()
        2). 判断
            A). 用户输入是否是quit?
                -- 是: 退出程序
                -- 不是: 进入下一步 ==>2.1.2.c
            B). 用户输入用户名是否存在?
                a). 不存在: 需重输.
                b). 存在: 判断密码是否正确
                   -- 不正确: 需重输, 重输超过三次, 锁定帐户 (提示联系管理员解锁并退出程序)
                   -- 正确: 判断用户是普通用户还是管理帐户, 返回相应帐户信息
                           -- 若帐户已冻结, 提示联系管理员解冻并退出程序

3. 商城
   def shopping_mall()
   3.1, 打印商品列表
   3.2, 用户输入商品编号和购买数量
        1). 判断输入的商品是否存在, 及购买数量是否合适
          -- 商品存在且购买数量<=库存 : 下一步 ==> 3.3
          -- else: 请用户输入商品编号及购买数量
        2). 用户输入quit退出
   3.3, 判断帐户余额是否足够支付
        -- 不足够支付: 重输商品编号和购买数量 (重复3.2判断)
        -- 足够支付: 下一步 ==> 3.4
   3.4, 打印用户购买商品清单
   3.5, 购买成功
        1). 扣减帐户余额, 并打印更新的帐户信息; 同时, 更新帐户信息写回account.py
            @ATM LOG
        2). 扣减库存, 并写回repository.py

4. 还款
   def repay()
   4.1, 打印当前欠款金额
   4.2, 用户输入还款金额
        1). 判断输入是否是quit
            -- 是: 退出
            -- 不是: 下一步 ==> 4.2.2
        2). 判断输入的金额是否是数字
            -- 是: 下一步 ==> 4.3
            -- 不是: 重输
   4.3, 还款成功
        1). 更新帐户余额, 并打印更新的帐户信息; 更新的帐户信息写回account.py
            @ATM LOG

5. 取款
   def withdraw()
   5.1, 用户输入取款金额
   5.2, 对用户输入进行处理strip()
   5.3, 判断输入是否是quit
            -- 是: 退出
            -- 不是: 下一步 ==> 5.4
   5.4, 判断输入是否是数字
        -- 是: 下一步 ==> 5.4
        -- 不是: 重输
   5.5, 判断输入的数字是否<当前帐户余额
        -- 是: 下一步 ==> 5.5
        -- 不是: 重输
   5.6, 借钱成功
        1). 扣减帐户余额, 并打印更新的帐户信息; 更新的帐户信息写回account.py
            @ATM LOG

6. 转帐
   def transfer()
   6.1, 用户输入收款用户名和转帐金额
   6.2, 判断输入是否是quit
            -- 是: 退出
            -- 不是: 下一步 ==> 6.3
   6.3, 判断用户名是否存在
        -- 存在: 判断转帐金额是否是数字
                -- 是数字且金额小于帐户余额: 下一步 ==> 6.3
                -- else: 重输
        -- 不存在: 重输
   6.4, 转帐成功
        1). 扣减帐户余额, 并打印更新的帐户信息; 更新的帐户信息写回account.py
            @ATM LOG

7. 帐单
   def bill()
   7.1, 用户输入年,月
   7.2, 判断输入是否是quit
        -- 是: 退出程序
        -- 不是: 下一步 ==> 7.3
   7.3, 判断用户输入是否有效
        -- 有效: 下一步 ==> 7.4
        -- 无效: 重输
   7.4, 打印帐单明细

8. 添加帐户
   def new_account()
   8.1, 用户输入用户名和密码及帐户信息
   8.2, 判断输入是否是quit
        -- 是: 退出程序
        -- 不是: 下一步 ==> 8.3
   8.3, 判断用户名是否存在
        -- 是: 重输
        -- 不是: 下一步 ==> 8.4
   8.4, 添加成功
        1). 将用户信息添加到account.py

9. 解锁帐户
   def unlock()
   9.1, 用户输入需要解锁的用户名
   9.2, 判断输入是否是quit
        -- 是: 退出程序
        -- 不是: 下一步 ==> 9.3
   9.3, 判断帐户是否已被锁定
        -- 是: 下一步
        -- 不是: 重输
   9.4, 解锁成功
        1). 将用户信息更新到account.py

10. 修改用户额度
    def adjustment()
    10.1, 用户输入用户名
    10.2, 判断输入是否是quit
        -- 是: 退出程序
        -- 不是: 下一步 ==> 10.3
    10.3, 修改成功
        1). 将用户信息更新到account.py

11. 冻结帐户
    def freeze()
    11.1, 用户输入用户名
    11.2, 判断输入是否是quit
        -- 是: 退出程序
        -- 不是: 下一步 ==> 11.3
    11.3, 冻结成功
        1). 将用户信息更新到account.py

12. 解冻帐户
    def unfreeze()
    12.1, 用户输入用户名
    12.2, 判断输入是否是quit
        -- 是: 退出程序
        -- 不是: 下一步 ==> 12.3
    12.3, 解冻成功
        1). 将用户信息更新到account.py


common:
    a. 判断输入是否是quit
    b. 判断用户名是否存在
    c. 更新account.py
idea.txt

 

log

  • log.py 记录access和transactions的日志
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import json
import logging

path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

sys.path.append(path)  # 这样这个路径会在最后面,每次都要遍历到最后才能找到(如果中途没有同名文件的话)
sys.path.insert(0, path)  # 将该路径插到前面去


from conf import settings


def log(logging_type):
    """
    去掉打印在屏幕上的功能
    :param logging_type:
    :return:
    """
    # 传日志用例, 生成日志对象
    logger = logging.getLogger(logging_type)
    # 设置日志级别
    logger.setLevel(settings.LOG_LEVEL)

    # # 日志打印到屏幕上
    # ch = logging.StreamHandler()
    # ch.setLevel(settings.LOG_LEVEL)

    # 获取文件日志对象及日志文件
    log_file = "%s\log\%s" % (settings.BASE_DIR, settings.LOG_TYPES[logging_type])
    # 默认log文件编译用GBK, 这里指定编码utf-8
    fh = logging.FileHandler(log_file, "a", encoding="UTF-8")
    fh.setLevel(settings.LOG_LEVEL)

    # 日志格式
    formatter = logging.Formatter("%(asctime)s-%(name)s-%(levelname)s-%(message)s")

    # 输出格式
    # ch.setFormatter(formatter)
    fh.setFormatter(formatter)

    # 把日志打印到指定的handler
    # logger.addHandler(ch)
    logger.addHandler(fh)

    return logger
log.py

 

 

readme.txt

Module2-atm/
├── README
├── atm #ATM主程目录
│ ├── __init__.py
│ ├── bin #ATM 执行文件 目录
│ │ ├── __init__.py
│ │ ├── main.py # 主程序
│ │ └── path_define.py # 执行程序
│ ├── conf #配置文件
│ │ ├── __init__.py
│ │ └── settings.py
│ ├── core #主要程序逻辑都在这个目录 里
│ │ ├── __init__.py
│ │ ├── db_handle.py #数据库连接引擎
│ │ └── tools.py #所有子程序都在这儿,a包括商城程序.
│ ├── db #用户数据存储的地方
│ │ ├── __init__.py
│ │ ├── account.py #便于迅速恢复同名json文件数据初始化状态.
│ │ ├── account.json #用户帐户信息.
│ │ ├── repository.py #便于迅速恢复同名json文件数据初始化状态.
│ │ ├── repository.json #商品库存信息
│ │ └── shopping_list.json #存各个用户的商城购买清单
│ │
│ │── doc #文档
│ │ ├── __init__.py
│ │ ├── idea.txt #代码思路
│ │ └── flowchart.jpg #流程图
│ └── log #日志目录
│ ├── __init__.py
│ ├── access.log #用户访问和操作的相关日志
└── └── transactions.log #所有的交易日志