setting
 1 '''
 2 存放配置信息
 3 '''
 4 import os
 5 
 6 # 获取项目根目录路径
 7 BASE_PATH = os.path.dirname(
 8     os.path.dirname(__file__)
 9 )
10 
11 # 获取user_data文件夹目录路径
12 USER_DATA_PATH = os.path.join(
13     BASE_PATH, 'db', 'user_data'
14 )
15 # print(USER_DATA_PATH)
16 
17 # 管理员数据路径
18 ADMIN_DATA_PATH = os.path.join(
19     BASE_PATH, 'db', 'admin_data'
20 )
21 
22 # 商城目录路径
23 SHOP_DATA_PATH = os.path.join(
24     BASE_PATH,'db','shop_obj'
25 )
26 
27 """
28 logging配置
29 """
30 
31 # 定义三种日志输出格式 开始
32 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
33                   '[%(levelname)s][%(message)s]'  # 其中name为getlogger指定的名字
34 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
35 id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
36 
37 # 定义日志输出格式 结束
38 # ****************注意1: log文件的目录
39 BASE_PATH = os.path.dirname(os.path.dirname(__file__))
40 logfile_dir = os.path.join(BASE_PATH, 'log')
41 # print(logfile_dir)
42 
43 # ****************注意2: log文件名
44 logfile_name = 'atm.log'
45 
46 # 如果不存在定义的日志目录就创建一个
47 if not os.path.isdir(logfile_dir):
48     os.mkdir(logfile_dir)
49 
50 # log文件的全路径
51 logfile_path = os.path.join(logfile_dir, logfile_name)
52 
53 LOGGING_DIC = {
54     'version': 1,
55     'disable_existing_loggers': False,
56     'formatters': {
57         'standard': {
58             'format': standard_format
59         },
60         'simple': {
61             'format': simple_format
62         },
63     },
64     'filters': {},
65     'handlers': {
66         # 打印到终端的日志
67         'console': {
68             'level': 'DEBUG',
69             'class': 'logging.StreamHandler',  # 打印到屏幕
70             'formatter': 'simple'
71         },
72         # 打印到文件的日志,收集info及以上的日志
73         'default': {
74             'level': 'DEBUG',
75             'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
76             'formatter': 'standard',
77             'filename': logfile_path,  # 日志文件
78             'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
79             'backupCount': 5,
80             'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
81         },
82     },
83     'loggers': {
84         # logging.getLogger(__name__)拿到的logger配置
85         '': {
86             'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
87             'level': 'DEBUG',
88             'propagate': True,  # 向上(更高level的logger)传递
89         },
90     },
91 }

from core import src
from interface import admin_interface
def add_user():
    src.register()
def change_balance():
    while True:
        change_user = input('请输入需要修改额度的用户: ').strip()
        money = input('请输入需要修改的用户额度: ').strip()
        if not money.isdigit():
            continue
        flag, msg = admin_interface.change_balance_interface(change_user, money)
        if flag:
            print(msg)
            break
        else:
            print(msg)
def lock_user():
    while True:
        change_user = input('请输入需要修改额度的用户: ').strip()
        flag, msg = admin_interface.lock_user_interface(
            change_user)
        if flag:
            print(msg)
            break
        else:
            print(msg)
admin
'''
用户视图层
'''
from interface import user_interface
from interface import bank_interface
from interface import admin_interface
from interface import shop_interface
from lib import common
import os
import json
from conf import settings

# 全局变量,记录用户是否已登录
login_user = None
# 1、注册功能
def register():
    while True:
        username = input('请输入注册账号:').strip()
        userpwd = input('请输入密码:').strip()
        userpwd1 = input('请确认密码:').strip()

        if userpwd == userpwd1:
            flag,msg = user_interface.register_interface(username,userpwd)
            if flag == True:
                print(msg)
                break
            else:
                print(msg)

# 2、登录功能
def login():
    while True:
        username = input('请输入账号:').strip()
        userpwd = input('请输入密码:').strip()
        flag,msg = user_interface.login_interface(username,userpwd)
        if flag == True:
            print(msg)
            global login_user
            login_user = username
            break
        else:
            print(msg)


# 3、查看余额
@common.login_auth
def check_balance():
    balance = user_interface.user_check_balance(login_user)
    print(f'用户{login_user}余额为{balance}')


# 4、提现功能
@common.login_auth
def withdraw():
    while True:
        withdraw_money = input('请输入提现金额:').strip()
        if not withdraw_money.isdigit():
            print('请输入数字')
            continue
        flag,msg = bank_interface.withdraw_interface(login_user,withdraw_money)
        if flag == True:
            print(msg)
            break
        else:
            print(msg)

# 5、还款功能
@common.login_auth
def repay():
    while True:
        repay_money = input('请输入还款金额:').strip()
        if not repay_money.isdigit():
            print('请输入正确的金额')
            continue
        repay_money = float(repay_money)

        if repay_money > 0:
            flag,msg= bank_interface.repay_interface(login_user,repay_money)
            if flag:
                print(msg)
                break
        else:
            print('输入的金额必须大于0')


# 6、转账功能
@common.login_auth
def transfer():
   while True:
       user1 = input('请输入您需要转账的账户:').strip()
       transfer_money = input('请输入您需要转账的金额:').strip()
       if not transfer_money.isdigit():
           print('请输入正确的转账金额')
           continue
       transfer_money = float(transfer_money)
       flag, msg = bank_interface.transfer_interface(login_user,user1,transfer_money)
       if flag:
           print(msg)
           break
       else:
           print(msg)


# 7、查看流水
@common.login_auth
def check_flow():
    flow_check = bank_interface.check_flow_interface(login_user)
    if flow_check:
        for flow in flow_check:
            print(flow)
    else:
        print('当前用户没有流水!')

# 8、购物功能
@common.login_auth
def shopping():
    while True:
        list_kind_name = shop_interface.select_shop_interface()
        shopping_car = {}  # {'商品名称': ['单价', '数量']]}
        choice1 = input('请选择购物种类(不想购物请输入q):').strip()
        if choice1 == 'q':
            break
        if choice1 not in list_kind_name:
            print('种类错误请重新输入')
            continue
        list_obj_name = shop_interface.select_shop_interface(choice1)
        choice2 = input('请选择商品名(不想购物请输入q):').strip()
        msg, shopping_dic = shop_interface.select_obj_interface(choice1, choice2)
        print(msg)
        if choice2 == 'q':
            break
        if choice2 not in shopping_dic:
            print('商品名错误请重新输入')
            continue
        choice3 = input('您购买几件:').strip()
        if not choice3.isdigit():
            print('请输入数字:')
            continue
        choice4 = input('您是否添加到购物车:是/y,否/n').strip()
        if choice4 != 'y':
            continue
        # 添加购物车
        shopping_car[choice2] = [shopping_dic[choice2][0],int(choice3)]
        # 购物车接口
        flag, msg,shop_car = shop_interface.add_shop_car_interface(
            login_user, shopping_car, choice3
        )
        if flag:
            print(msg)
        print(f'当前购物车:{shop_car}')
        choice5 = input('是否继续购物?是/y,否/n').strip()
        if choice5 == 'n':
            choice6 = input('是否结账?是/y,否/n')
            if choice6 != 'y':
                break
            # 减库存
            flag, msg = shop_interface.reduce_inventory_interface(shopping_car)
            if flag:
                flag1, msg1 = shop_interface.pay_shop_interface(login_user, shopping_car)
                if flag1:
                    print(msg1)
                    # 清空购物车
                    user_interface.user_empty_shopcar(login_user)
                    break
                else:
                    print(msg1)
                break
            else:
                print(msg)
                user_interface.user_empty_shopcar(login_user)



# 9、查看购物车
@common.login_auth
def check_shop_car():
    shop_car = shop_interface.check_shop_car_interface(login_user)
    print(shop_car)


# 10、管理员功能
@common.admin_login_auth
def admin():
    from core import admin
    admin.run()

# 管理员登录功能
def admin_login():
    while True:
        username = input('请输入账号:').strip()
        userpwd = input('请输入密码:').strip()
        flag,msg = admin_interface.admin_login_interface(username,userpwd)
        if flag == True:
            print(msg)
            global login_user
            login_user = username
            break
        else:
            print(msg)

# 创建函数功能字典
func_dic = {
    '1': register,
    '2': login,
    '3': check_balance,
    '4': withdraw,
    '5': repay,
    '6': transfer,
    '7': check_flow,
    '8': shopping,
    '9': check_shop_car,
    '10': admin,
}


# 视图层主程序
def run():
    while True:
        print('''
        ===== ATM + 购物车 =====
            1、注册功能
            2、登录功能
            3、查看余额
            4、提现功能
            5、还款功能
            6、转账功能
            7、查看流水
            8、购物功能
            9、查看购物车
            10、管理员功能
        ========= end =========
        ''')

        choice = input('请输入功能编号: ').strip()

        if choice not in func_dic:
            print('请输入正确的功能编号!')
            continue

        func_dic.get(choice)()  # func_dic.get('1')() ---> register()
src
{"username": "yuding", "password": "fdf8ef1cdba3bc9f0e434ede814c5490"}
admin.json
'''
数据处理层
    - 专门用户处理数据的
'''
from conf import settings
import os,json
# 读取数据
def select(username):
    user_path = os.path.join(
        settings.USER_DATA_PATH, f'{username}.json'
    )
    if os.path.exists(user_path):
        with open(user_path, mode='r', encoding='utf-8') as f:
            user_dic = json.load(f)
            return user_dic
# 保存数据
def save(user_dic):
    username = user_dic['username']
    user_path = os.path.join(
        settings.USER_DATA_PATH, f'{username}.json'
    )
    with open(user_path, mode='w', encoding='utf-8') as f:
        json.dump(user_dic, f, ensure_ascii=False)

# 读取管理员数据
def admin_select(username):
    user_path = os.path.join(
        settings.ADMIN_DATA_PATH, f'{username}.json'
    )
    if os.path.exists(user_path):
        with open(user_path, mode='r', encoding='utf-8') as f:
            user_dic = json.load(f)
            return user_dic

# 读取商城目录数据
def shop_select(i,path):
    with open(path, mode='r', encoding='utf-8') as f:
        shop_dic = json.load(f)
        l = []
        for key in shop_dic:
            print(f'商品名:{key}   单价:{shop_dic[key][0]}元   库存:{shop_dic[key][1]}件')
            l.append(key)
        return l
# 保存商品数据
def shop_save(user_dic,name):
    path = os.path.join(settings.SHOP_DATA_PATH,f'{name}.json')
    with open(path, mode='w', encoding='utf-8') as f:
         json.dump(user_dic,f,ensure_ascii=False)

# 读取商品信息
def obj_select(kind_name,obj_name,path):
    with open(path, mode='r', encoding='utf-8') as f:
        shopping_dic = json.load(f)
        if obj_name in shopping_dic:
            msg = f'商品名:{obj_name}   单价:{shopping_dic[obj_name][0]}元   库存:{shopping_dic[obj_name][1]}件'
            return msg,shopping_dic
        return '没有此类商品',shopping_dic
db_handler
#__author:"yuuding"
#date: 2020/4/3
from db import db_handler
from lib import common

# 根据不同的接口类型传入不同的日志对象
admin_logger = common.get_logger(log_type='admin')

# 管理员登录接口
def admin_login_interface(username,userpwd):
    user_dic = db_handler.admin_select(username)
    if user_dic:
        userpwd = common.md5_pwd(userpwd)
        if userpwd == user_dic['password']:
            msg = f'管理员{username}登录成功'
            admin_logger.info(msg)
            return True,msg
        else:
            return False, '密码错误'
    else:
        return False, '账号不存在,请重新输入'

# 修改\用户额度接口
def change_balance_interface(username, money):
    user_dic = db_handler.select(username)
    if user_dic:
        user_dic['balance'] = money
        db_handler.save(user_dic)
        msg = f'管理员修改用户: [{username}]额度修改成功!'
        admin_logger.info(msg)
        return True,msg
    return False,'修改额度的用户不存在'

# 冻结账户接口
def lock_user_interface(username):
    user_dic = db_handler.select(username)
    if user_dic:
        # 将locked的默认值改为True
        user_dic['locked'] = True
        db_handler.save(user_dic)
        msg = f'用户{username}冻结成功!'
        msg = f'管理员{username}登录成功'
        admin_logger.info(msg)
        return True, msg
    return False, '冻结用户不存在!'
admin_interface
'''
银行相关业务的接口
'''
from db import db_handler
import time,datetime
from lib import  common

bank_logger = common.get_logger(log_type='bank')
# 提现接口
def withdraw_interface(username,money):
    user_dic = db_handler.select(username)
    balance = float(user_dic['balance'])
    money1 = float(money) * 1.05
    if balance >= money1:
        balance -= money1
        user_dic['balance'] = balance
        db_handler.save(user_dic)
        msg = f'用户{username}成功提现{money1}元'
        bank_logger.info(msg)
        return True,msg
    return False, '余额不足,请充值'

# 还款接口
def repay_interface(username,repay_money):
    user_dic = db_handler.select(username)
    user_dic['balance'] += repay_money
    db_handler.save(user_dic)
    msg = f'用户{username}还款成功,还款{repay_money}元'
    bank_logger.info(msg)
    return True,msg

# 转账接口
def transfer_interface(login_user,user1,transfer_money):
    user_dic1 = db_handler.select(login_user)
    user_dic2 = db_handler.select(user1)
    if not user_dic2:
        return False,'目标账户不存在'
    if user_dic1['balance'] >= transfer_money:
        user_dic1['balance'] -= transfer_money
        user_dic2['balance'] += transfer_money

        flow_user1 = f'用户{login_user}向用户{user1}转账{transfer_money}元'
        user_dic1['flow'].append(f'{datetime.datetime.fromtimestamp(time.time())} {flow_user1}')
        flow_user2 = f'用户{user1}接受用户{login_user}转账{transfer_money}元'
        user_dic2['flow'].append(f'{datetime.datetime.fromtimestamp(time.time())} {flow_user2}')

        db_handler.save(user_dic1)
        db_handler.save(user_dic2)

        bank_logger.info(flow_user1)
        return True,flow_user1
    return False,'当前用户余额不足'

# 查看流水接口
def check_flow_interface(login_user):
    user_dic = db_handler.select(login_user)
    return user_dic.get('flow')

# 商品支付结算接口
def pay_interface(login_user, cost):
    user_dic = db_handler.select(login_user)
    bal = float(user_dic['balance'])
    if bal >= float(cost):
        bal -= float(cost)
        user_dic['balance'] = bal
        flow = f'{datetime.datetime.fromtimestamp(time.time())} 用户消费金额: [{cost}]'
        user_dic['flow'].append(flow)
        db_handler.save(user_dic)

        return True
    return False
bank_interface
'''
购物商城接口
    
'''
from conf import settings
from db import db_handler
import os
import json
from lib import common

shop_logger = common.get_logger(log_type='shop')
# 读取商城物品目录接口
def select_shop_interface(choice=None):
    if not choice:
        l = []
        print('=========欢迎来到购物商城=========')
        for i in os.listdir(settings.SHOP_DATA_PATH):
            l.append(i.split('.')[0])
            path = os.path.join(settings.SHOP_DATA_PATH, i)
            db_handler.shop_select(i, path)
        print('==================================')
        return l
    else:
        print('=========欢迎来到购物商城=========')
        path = os.path.join(settings.SHOP_DATA_PATH, f'{choice}.json')
        l = db_handler.shop_select(choice, path)
        print('==================================')
        return l
# 读取商品接口
def select_obj_interface(choice1,choice2):
    path = os.path.join(settings.SHOP_DATA_PATH, f'{choice1}.json')
    msg,shopping_dic = db_handler.obj_select(choice1, choice2,path)
    return msg,shopping_dic

# 添加购物车接口
def add_shop_car_interface(login_user, shopping_car,shop_number):
    user_dic = db_handler.select(login_user)
    shop_car = user_dic.get('shop_car')
    for shop_name, price_number in shopping_car.items():
        # 每个商品的数量
        number = price_number[1]
        # 2.2) 若商品重复,则累加商品数量
        if shop_name in shop_car:
            # [单价, 数量][1] ---> 数量
            res = int(user_dic['shop_car'][shop_name][1])
            res += number
            user_dic['shop_car'][shop_name][1] = res
        else:
            # 2.3)若不是重复的,更新到商品字典中
            user_dic['shop_car'].update(
                {shop_name: price_number}
            )
        # 保存用户数据
    db_handler.save(user_dic)
    msg = '添加购物车成功!'
    shop_logger.info(msg)
    return True,msg,user_dic['shop_car']

# 商品准备支付接口
def pay_shop_interface(login_user, shopping_car):
    user_dic = db_handler.select(login_user)
    cost = 0
    for name in shopping_car:
        cost += (shopping_car[name][0] * shopping_car[name][1])
    from interface import bank_interface
    flag = bank_interface.pay_interface(login_user, cost)
    if flag:
        msg = f'用户:[{login_user}]支付 [{cost}] 成功, 准备发货!'
        # shop_logger.info(msg)
        return True, msg
    return False,'支付失败,金额不足'

# 减少库存
def reduce_inventory_interface(shopping_car):
    for name,price_number in shopping_car.items():
        price,number = price_number
        print(name,price,number,type(number))
        l = select_shop_interface()

        for kind_name in l:
            msg,shopping_dic = select_obj_interface(kind_name,name)
            if name not in shopping_dic:
                continue
            if shopping_dic[name][1] >= int(number):
                shopping_dic[name][1] -= int(number)
                db_handler.shop_save(shopping_dic,kind_name)
                return True,'支付成功,库存已更新'
            else:
                msg1 = f'{name}存货不足,请重新选择'
                return False,msg1

# 查看购物车接口
def check_shop_car_interface(username):
    user_dic = db_handler.select(username)
    return user_dic.get('shop_car')
shop_interface
'''
逻辑接口层
    用户接口
'''
from db import db_handler
from lib import common
# 注册接口
def register_interface(username, password, balance=15000):
    userdic = db_handler.select(username)
    if userdic:
        return False,'用户名已存在,请重新登陆'
    password = common.md5_pwd(password)
    userdic = {
        'username': username,
        'password': password,
        'balance': balance,
        # 用于记录用户流水的列表
        'flow': [],
        # 用于记录用户购物车
        'shop_car': {},
        # locked:用于记录用户是否被冻结
        # False: 未冻结   True: 已被冻结
        'locked': False
    }
    db_handler.save(userdic)
    return True, f'{username} 注册成功!'

# 注册接口
def login_interface(username,userpwd):
    user_dic = db_handler.select(username)
    if user_dic:
        if not user_dic['locked']:
            userpwd = common.md5_pwd(userpwd)
            if userpwd == user_dic['password']:
                return True,f'用户{username}登录成功'
            else:
                return False,'密码错误'
        else:
            print('账号被锁定')
    else:
        return False,'账号不存在,请重新输入'

# 查看接口
def user_check_balance(username):
    user_dic = db_handler.select(username)
    return user_dic['balance']

# 清空购物车接口

def user_empty_shopcar(login_user):
    user_dic = db_handler.select(login_user)
    user_dic['shop_car'] = {}
    db_handler.save(user_dic)
user_interface
'''
存放公共方法
'''
import hashlib
from conf import settings
from logging import config
from logging import getLogger

# 加密
def md5_pwd(userpwd):
    md5_obj = hashlib.md5()
    obj1 = '天王盖地虎'
    md5_obj.update(obj1.encode('utf-8'))
    md5_obj.update(userpwd.encode('utf-8'))
    obj2 = '宝塔镇河妖'
    md5_obj.update(obj2.encode('utf-8'))
    return md5_obj.hexdigest()

# 认证装饰器
def login_auth(func):
    from core import src
    def wrap(*args,**kwargs):
        if src.login_user:
            res = func()
            return res
        else:
            print('账户未登录。请登录')
            src.login()
    return wrap


# 管理员认证装饰器
def admin_login_auth(func):
    from core import src
    def wrap(*args,**kwargs):
        while True:
            if src.login_user != 'admin':
                src.admin_login()
            elif src.login_user == 'admin':
                res = func()
                return res
    return wrap

# 添加日志
def get_logger(log_type):  # log_type ---> user
    # 1、加载日志配置信息
    config.dictConfig(settings.LOGGING_DIC)

    # 2、获取日志对象
    logger = getLogger(log_type)

    return logger
common
# 项目的说明书
## 项目:ATM + 购物车

# 项目需求:
    1.额度15000或自定义     -->  注册功能
    2.实现购物商城,买东西加入购物车,调用信用卡接口结账  --> 购物功能、支付功能
    3.可以提现,手续费5%   --> 提现功能
    4.支持多账户登录  --> 登录功能
    5.支持账户间转账  --> 转账功能
    6.记录日常消费 -->  记录流水功能
    7.提供还款接口 -->  还款功能
    8.ATM记录操作日志 --> 记录日志功能
    9.提供管理接口,包括添加账户、用户额度,冻结账户等。。。 ---> 管理员功能
    10.用户认证用装饰器  --> 登录认证装饰器
    

## "用户视图层" 展示给用户选择的功能
    1、注册功能
    2、登录功能
    3、查看余额
    4、提现功能
    5、还款功能
    6、转账功能
    7、查看流水
    8、购物功能
    9、查看购物车
    10、管理员功能

# 一个项目是如何从无到有
## 一 需求分析
    1.拿到项目,会先在客户那里一起讨论需求,
    商量项目的功能是否能实现,周期与价格,得到一个需求文档。
    
    2.最后在公司内部需要开一次会议,最终得到一个开发文档,
    交给不同岗位的程序员进行开发。
        - Python: 后端,爬虫
        
        - 不同的岗位:
            - UI界面设计:
                - 设计软件的布局,会分局软件的外观切成一张张图片。
            
            - 前端:
                - 拿到UI交给他的图片,然后去搭建网页面。
                - 设计一些页面中,哪些位置需要接收数据,需要进行数据交互。
            
            - 后端:
                - 直接核心的业务逻辑,调度数据库进行数据的增删查改。
            
            - 测试:
                - 会给代码进行全面测试,比如压力测试,界面测试(CF卡箱子)。
            
            - 运维:
                - 部署项目。
        
        
        
    
## 二 程序的架构设计
### 1、程序设计的好处
    1)思路清晰
    2)不会出现写一半代码时推翻重写
    3)方便自己或以后的同事更好维护
    
### 2、三层架构设计的好处
    1)把每个功能都分层三部分,逻辑清晰
    2)如果用户更换不同的用户界面或不同,
    的数据储存机制都不会影响接口层的核心
    逻辑代码,扩展性强。
    3)可以在接口层,准确的记录日志与流水。
    
### 3、三层架构
#### 一 用户视图层
    用于与用户交互的,可以接受用户的输入,打印接口返回的数据。
    
#### 二 逻辑接口层
    接受 用户视图层 传递过来的参数,根据逻辑判断调用数据层加以处理,
    并返回一个结果给 用户视图层。
    
#### 三 数据处理层
    接受接口层传递过来的参数,做数据的 
        - 保存数据  save()
        - 查看数据  select()
        - 更新数据
        - 删除数据

## 三 分任务开发
## 四 测试
## 五 上线



# 统计代码
file ---> settings ---> Plugins ---> 
readme
'''
程序的入口
'''
import sys
import os

# 添加解释器的环境变量
sys.path.append(
    os.path.dirname(__file__)
)
from core import src
# 开始执行项目函数
if __name__ == '__main__':
    # 1、先执行用户视图层
    src.run()
start