要求:
模拟实现一个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 ---------------------------------------------------------------
做项目的步骤:
1.分析功能,需求
账户数据的增删改查 ,提现 转账 还款 --> 加钱 减钱
日志
模块间的互相调用
功能的重用
2.画流程图 https://www.processon.com/
https://www.processon.com/view/link/589eb841e4b0999184934329
3.搭框架 ---------------------------------------------------------------
注意事项:
1.一个账户一个文件(.json),防止多个用户同时读数据,修改文件后,会覆盖别人已经修改好的文件!
2.num_func = {'1': view_account, '2': withdraw, '3': pay_back, '4': transfer, '5': quit}
choice_num = input('num>>>:').strip()
num_func[choice_num](choice_num)
字典 value 的值可以放 (地址)
3.settings文件要重视,防止以后修改需求,可自定义配置
4. json.dump(account_info['account_data'], f)
f.close()
os.replace(path_tmp, path)
防止断电 .json 数据修改后,还未保存到硬盘上,所以一般写到 新文件中 再替换
5.注意写的 模块公用,模块内的参数 一定不能写成死的!
6.注意写的 函数公用,函数内的参数 一定要注意公用!
7.公共的 函数,不要涉及到与用户交互!
8.imoprt ***.py 引入模块时,引入模块得顺序:内置模块,第三方模块,自定义模块
引入模块,一般到函数级别from *** import (***,***),为了可维护,程序可读性。无论加载一个模块还是加载一个函数,函数所在得模块都会被加载到内存中!
9.load(open('**.py','r')) 和 with open('**.py','r') as f: 建议用后者 不需要关闭文件 python会自动关 但是f.close()是个好习惯 f.close()
f.flush() 会触发磁盘写操作 增加io压力 增加系统负载 但是 特殊情况下 可以这么做
10.变量在哪里使用就在哪里定义,因为函数传参是有一定代价的!
11.密码md5加盐salt 用户注册时:
用户输入【账号】和【密码】(以及其他用户信息);
系统为用户生成【Salt值】;
系统将【Salt值】和【用户密码】连接到一起;
对连接后的值进行散列,得到【Hash值】;
将【Hash值1】和【Salt值】分别放到数据库中。 用户登录时:
用户输入【账号】和【密码】;
系统通过用户名找到与之对应的【Hash值】和【Salt值】;
系统将【Salt值】和【用户输入的密码】连接到一起;
对连接后的值进行散列,得到【Hash值2】(注意是即时运算出来的值);
比较【Hash值1】和【Hash值2】是否相等,相等则表示密码正确,否则表示密码错误。
----------------------------------------------------------------------------
README:
1.管理员的账号:999 密码:123
2.普通账号:1000 / 1001 / 1002 密码:123
atm.py
# -*- coding:utf-8 -*-
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from core import(
main, manager, shopper
) def main_run():
main.run() def manager_run():
manager.run() def shopper_run():
shopper.run() def quit_func():
exit('bye bye ...') def choice():
choice_msg = '''
-----start-----
1.普通账户
2.管理员
3.购物者
4.退出
------end------
'''
dict_choice = {'': main_run, '': manager_run, '': shopper_run, '': quit_func}
while True:
print(choice_msg)
choice_num = input('num>>>:').strip()
if choice_num in dict_choice:
dict_choice[choice_num]()
else:
print('请输入正确的序号!') if __name__ == '__main__':
choice()
atm
settings.py
# -*- coding:utf-8 -*-
import os
import logging
import time
import datetime BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DATABASE = {
'engine:': 'file_storage',
'path': os.path.join(BASE_DIR, 'db/accounts')
} LOG_LEVEL = logging.INFO
LOG_FORMATTER = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %I:%M:%S %p') TRANSACTION_TYPE = {
'withdraw': {'action': 'minus', 'interest': 0.05},
'pay_back': {'action': 'plus', 'interest': 0},
'transfer': {'action': 'minus', 'interest': 0.05},
'consume': {'action': 'minus', 'interest': 0}
} GOODS = [
{"name": "电脑", "price": 1999},
{"name": "鼠标", "price": 10},
{"name": "游艇", "price": 20},
{"name": "美女", "price": 998}
] MANAGER_ID = '' datetime_tmp = datetime.datetime.now().replace(year=2050, month=1, day=1)
DEFAULT_ACCOUNT = {
'id': 0,
'password': '',
'salt': '',
'balance': 15000,
'credit': 15000,
'enroll_date': time.strftime('%Y-%m-%d', time.localtime()),
'expire_date': '%s-%s-%s' % (datetime_tmp.year, datetime_tmp.month, datetime_tmp.day),
'pay_day': 22,
'status': 0
}
settings
auth.py
# -*- coding:utf-8 -*-
import os
import json
from hashlib import md5 from conf.settings import DATABASE def auth_login(func):
# 装饰器
def inner(*args, **kwargs):
if args[0]['is_auth']:
return func(*args, **kwargs)
else:
exit('您还未登录!')
return inner def hash_password(password, salt):
# 用户输入的密码 + salt 得到hash值
hash_value = md5()
hash_value.update((password+salt).encode('utf-8'))
return hash_value.hexdigest() def judge_account(account):
# 判断用户是否存在 存在返回json数据
path_json = DATABASE['path']
file_path = os.path.join(path_json, account+'.json')
if os.path.isfile(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
account_data = json.load(f)
f.close()
return account_data def login(logger, *args):
# 判断账号密码是否正确
account_info = {
'is_auth': False,
'account': None,
'account_data': None
}
login_count = 0
while login_count < 3:
account = input('\033[1;32maccount>>>:\033[0m').strip()
password = input('\033[1;32mpassword>>>:\033[0m').strip()
account_data = judge_account(account)
if account_data:
if account_data.get('password') == hash_password(password, account_data['salt']):
# print('\033[1;33m登录成功!\033[0m'.center(25, '-'))
account_info['is_auth'] = True
account_info['account'] = account
account_info['account_data'] = account_data
if account_data['status'] == 1: # 表示账号 被冻结
print('\033[1;31m账号:%s 被冻结!\033[0m'.center(30, '-') % account)
else:
if len(args) != 0: # 管理员
if args[0] == account:
logger.info('管理员account: %s - 登录成功' % account)
return account_info
else:
print('\033[1;31m您不是管理员!\033[0m'.center(25, '-'))
else: # 普通账号
logger.info('account: %s - 登录成功' % account)
return account_info
else:
print('\033[1;31m密码错误!\033[0m'.center(25, '-'))
else:
print('\033[1;31m账号:%s 不存在\033[0m' % account)
login_count += 1
else:
logger.critical('account: %s - 登录失败次数超过3次' % account)
exit()
auth
db_handler.py
# -*- coding:utf-8 -*-
import os
import json from .logger import set_logger
from .auth import judge_account
from conf.settings import(
DATABASE, TRANSACTION_TYPE
) trans_logger = set_logger('transactions') def account_data_save(account, account_data):
# 修改文件的 公共方法
path = os.path.join(DATABASE['path'], account + '.json')
path_tmp = os.path.join(DATABASE['path'], account + '_tmp' + '.json')
if os.path.isfile(path):
with open(path_tmp, 'w', encoding='utf-8') as f:
json.dump(account_data, f)
os.replace(path_tmp, path)
return account_data def save_json_common(account, new_balance, account_data):
# 内存中 替换余额
account_data['balance'] = round(new_balance, 2)
return account_data_save(account, account_data) def transaction_logger(account, new_balance, account_data, arguments):
# 记录 日志的 公共方法
account_data_new = save_json_common(account, new_balance, account_data)
if account_data_new:
trans_logger.info('account: %s - account_type: %s - action: %s - interest: %s' % (account, arguments[0], arguments[1]['action'], arguments[1]['interest']))
return account_data_new def split_account(new_balance, account_info, *args, **kwargs):
# 区分开 是两个账号间的操作 还是一个账号
account = account_info['account']
if len(kwargs) != 0: # 转账 两个账号 间的操作
transfer_data = save_json_common(kwargs['transfer_account'], kwargs['transfer_new_balance'], kwargs['transfer_data'])
if transfer_data:
return transaction_logger(account, new_balance, account_info['account_data'], args)
else: # 一个 账号 的操作
return transaction_logger(account, new_balance, account_info['account_data'], args) def make_transaction(money, account_info, account_type, *args):
# 提现 还款 转账 公共方法
if account_type in TRANSACTION_TYPE:
transaction_type = TRANSACTION_TYPE[account_type]
interest = money * transaction_type['interest']
old_balance = account_info['account_data']['balance']
# 减钱的 操作
if transaction_type['action'] == 'minus':
if len(args) == 0: # 提现 或者 消费 没有附加的参数
new_balance = old_balance - money - interest
if new_balance < 0:
print('\033[1;31m交易失败,余额不足\033[0m')
else:
return split_account(new_balance, account_info, account_type, transaction_type)
else: # 转账
if args[0] == account_info['account']:
print('\033[1;31m注:同一账号,不允许转账\033[0m')
else:
transfer_data = judge_account(args[0])
if transfer_data:
if transfer_data['status'] == 1:
print('\033[1;31m账号:%s 被冻结!\033[0m'.center(30, '-') % args[0])
else:
new_balance = old_balance - money - interest
if new_balance < 0:
print('\033[1;31m交易失败,余额不足\033[0m')
else:
transfer_new_balance = transfer_data['balance'] + money
return split_account(new_balance, account_info, account_type, transaction_type, transfer_account = args[0], transfer_new_balance = transfer_new_balance, transfer_data = transfer_data)
else:
print('\033[1;31m账号:%s 不存在\033[0m' % args[0])
# 加钱的 操作
elif transaction_type['action'] == 'plus':
new_balance = old_balance + money + interest
return split_account(new_balance, account_info, account_type, transaction_type) else:
print('\033[1;31m%s 交易类型不存在!\033[0m' % account_type)
db_handler
logger.py
# -*- coding:utf-8 -*-
import os
import logging
from logging import handlers from conf.settings import(
LOG_LEVEL, BASE_DIR, LOG_FORMATTER
) def set_logger(name):
# 配置logger对象
logger = logging.getLogger(name)
logger.setLevel(LOG_LEVEL) # fh = logging.FileHandler(os.path.join(BASE_DIR, 'log/'+name+'.log'), encoding='utf-8')
# fh = handlers.RotatingFileHandler(filename = os.path.join(BASE_DIR, 'log/'+name+'.log'), maxBytes=10, backupCount=2, encoding='utf-8')
fh = handlers.TimedRotatingFileHandler(filename = os.path.join(BASE_DIR, 'log/'+name+'.log'), when='S', interval=2, backupCount=2, encoding='utf-8')
logger.addHandler(fh) fh_formatter = LOG_FORMATTER
fh.setFormatter(fh_formatter) return logger
logger
main.py
# -*- coding:utf-8 -*-
import re from .logger import set_logger
from .db_handler import make_transaction
from .auth import (
login, auth_login
) access_logger = set_logger('access') @auth_login
def checkout(account_info, money):
# 购买商品之后 结账
account_data = make_transaction(money, account_info, 'consume')
if account_data:
print('账户:%s ->> 消费 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance']))
exit()
else:
print('\033[1;31m结账失败!\033[0m') def judge_num(num):
# 判断 int float
str_num = re.sub('\.', '', num, count=1)
if str_num.isdigit():
return float(num) @auth_login
def view_account(account_info):
# 查看账户信息
msg_info = '''\033[1;34m
-----账户:%s 的信息:-----
balance: %10s
credit: %10s
pay_day: %10s\033[0m
''' % (account_info.get('account'), account_info['account_data']['balance'], account_info['account_data']['credit'], account_info['account_data']['pay_day'])
print(msg_info) @auth_login
def withdraw(account_info):
# 提现
while True:
money = input('withdraw_money(q表示退出)>>>:').strip()
if not money:
continue
if money == 'q':
break
float_money = judge_num(money)
if float_money:
account_data = make_transaction(float_money, account_info, 'withdraw')
if account_data:
print('账户:%s -> 提现成功 -> 提现 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance']))
else:
print('\033[1;31m提现失败!\033[0m')
else:
print('请输入\033[1;34m正确的\033[0m并\033[1;31m大于0\033[0m的金额'.center(45, '-')) @auth_login
def pay_back(account_info):
# 还款
while True:
money = input('pay_back_money(q表示退出)>>>:').strip()
if not money:
continue
if money == 'q':
break
float_money = judge_num(money)
if float_money:
account_data = make_transaction(float_money, account_info, 'pay_back')
if account_data:
print('账户:%s -> 还款成功 -> 还款 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance']))
else:
print('\033[1;31m还款失败!\033[0m')
else:
print('请输入\033[1;34m正确的\033[0m并\033[1;31m大于0\033[0m的金额'.center(45, '-')) @auth_login
def transfer(account_info):
# 转账
while True:
transfer_account = input('transfer_account(q表示退出)>>>:').strip()
if not transfer_account:
continue
if transfer_account == 'q':
break
while True:
money = input('transfer_money(q表示退出)>>>:').strip()
if not money:
continue
if money == 'q':
return
float_money = judge_num(money)
if float_money:
account_data = make_transaction(float_money, account_info, 'transfer', transfer_account)
if account_data:
print('账户:%s -> 转账成功 -> 转账 \033[1;34m%s\033[0m -> 余额 \033[1;31m%s\033[0m' % (account_info['account'], money, account_data['balance']))
else: # 交易失败余额不足的 或者 账号不存在的 或者 同一账号的 会直接退出
print('\033[1;31m转账失败!\033[0m')
return
else:
print('请输入\033[1;34m正确的\033[0m并\033[1;31m大于0\033[0m的金额'.center(45, '-')) def quit_func(account_info):
# 退出
exit('bye bye ...') def controller(account_info):
# 功能分发器
print('账户\033[0;34m%s\033[0m登录成功'.center(35, '*') % account_info.get('account'))
msg_num = '''\033[1;33m
--------start--------
1. 账户信息
2. 提现
3. 还款
4. 转账
5. 退出
---------end---------
\033[0m
'''
num_func = {'': view_account, '': withdraw, '': pay_back, '': transfer, '': quit_func} # 字典的值可存放 地址
while True:
print(msg_num)
choice_num = input('num>>>:').strip()
if not choice_num:
continue
if choice_num in num_func:
num_func[choice_num](account_info)
else:
print('\033[1;31m请输入正确的序号\033[0m') def run():
account_info = login(access_logger)
if access_logger:
controller(account_info)
main
manager.py
# -*- coding:utf-8 -*-
import os
import json
import string
import random
from hashlib import md5 from .logger import set_logger
from .main import judge_num
from .db_handler import account_data_save
from .auth import(
login, judge_account, auth_login
)
from conf.settings import(
MANAGER_ID, DATABASE, DEFAULT_ACCOUNT
) manager_logger = set_logger('manager') def set_salt():
# 每个用户的salt值不一样 库中的password就不一样
salt = ''.join(random.sample(string.digits + string.ascii_letters + string.punctuation, 8)) # 随机的8位盐 使得相同的密码 也有不同md5()值
md5_value = md5()
md5_value.update(b'' + salt.encode('utf-8'))
return salt, md5_value.hexdigest() def get_account():
# 输入 id 得到账户信息
while True:
id_account = input('id>>>:').strip()
if id_account.isdigit():
account_date = judge_account(id_account)
if account_date:
return account_date, id_account
else:
print('\033[1;31m账号:%s 不存在\033[0m' % id_account)
else:
print('\033[1;31m请输入整数的 id 账户\033[0m') @auth_login
def add_acount(m_account_info):
# 添加账户
print('\033[1;34m管理员 %s 开始添加账户\033[0m'.center(30, '-') % m_account_info['account'])
while True:
id_account = input('id>>>:').strip()
if id_account.isdigit():
account_date = judge_account(id_account)
if account_date:
print('\033[1;31m账号:%s 已存在\033[0m' % id_account)
else:
salt_pass = set_salt()
DEFAULT_ACCOUNT['id'] = int(id_account)
DEFAULT_ACCOUNT['password'] = salt_pass[1]
DEFAULT_ACCOUNT['salt'] = salt_pass[0]
with open(os.path.join(DATABASE['path'], id_account + '.json'), 'w', encoding='utf-8') as f:
json.dump(DEFAULT_ACCOUNT, f)
print('\033[1;32m账号:%s 添加成功\033[0m' % id_account)
manager_logger.info('管理员account:%s - 添加账户account:%s' % (m_account_info['account'], id_account))
break
else:
print('\033[1;31m请输入整数的 id 账户\033[0m') @auth_login
def credit_account(m_account_info):
# 设定用户 额度
account_tuple = get_account()
if account_tuple:
credit = input('new_credit>>>:').strip()
float_credit = judge_num(credit)
if float_credit:
account_tuple[0]['credit'] = round(float_credit, 2)
account_data = account_data_save(account_tuple[1], account_tuple[0])
if account_data:
print('修改账号: \033[1;33m%s\033[0m 额度成功,现在额度为:\033[1;34m%s\033[0m' % (account_tuple[1], account_data['credit']))
manager_logger.info('管理员account:%s - 修改账号account:%s - 修改额度credit:%s' % (m_account_info['account'], account_tuple[1], float_credit))
else:
print('账号:%s 文件修改失败!' % account_tuple[1])
else:
print('\033[1;31m注意:用户额度须为整数或小数\033[0m') @auth_login
def freeze_account(m_account_info):
# 冻结账户
account_tuple = get_account()
if account_tuple:
if account_tuple[1] == m_account_info['account']: # 是管理员 本人
print('\033[1;31m您是管理员,不能冻结自己!\033[0m')
else:
if account_tuple[0]['status'] == 1:
print('账号是 \033[1;32m冻结\033[0m 状态,将 \033[1;34m解冻\033[0m')
account_tuple[0]['status'] = 0
else:
print('账号是 \033[1;32m解冻\033[0m 状态,将 \033[1;34m冻结\033[0m')
account_tuple[0]['status'] = 1
account_data = account_data_save(account_tuple[1], account_tuple[0])
if account_data:
print('\033[1;33m修改账号的 冻结状态:%s 成功!\033[0m' % (account_tuple[1]))
manager_logger.info('管理员account:%s - 修改账号account:%s - 修改冻结状态成功' % (m_account_info['account'], account_tuple[1]))
else:
print('账号:%s 文件修改失败!' % account_tuple[1]) def quit_func(m_account_info):
# 退出
exit('bye bye ...') def controller(m_account_info):
# 功能分发器
print('\033[1;33m欢迎管理员 %s 登录\033[0m'.center(35, '-') % m_account_info.get('account'))
manager_choice = '''
------start------
1. 添加账户
2. 用户额度
3. 冻结账户
4. 退出
------end------
'''
dict_manager_info = {'': add_acount, '': credit_account, '': freeze_account, '': quit_func}
while True:
print(manager_choice)
num = input('num>>>:').strip()
if not num:
continue
if num in dict_manager_info:
dict_manager_info[num](m_account_info)
else:
print('\033[1;31m请重新选择\033[0m') def run():
manager_account_info = login(manager_logger, MANAGER_ID)
if manager_account_info:
controller(manager_account_info)
manager
shopper.py
# -*- coding:utf-8 -*-
from conf.settings import GOODS
from .auth import login
from .main import (
access_logger, checkout
) def show_buy_goods(goods_buyed):
# 展示购买的商品
all_money = 0
if len(goods_buyed) == 0:
return all_money
else:
print('您已购买以下商品:'.center(20, '-'))
for index, i in enumerate(goods_buyed, 1):
print('%s. %s %s' % (index, i['name'], i['price']))
all_money += int(i['price'])
return all_money def buy_goods(dict_goods):
# 购买商品
goods_buyed = []
while True:
choice = input('num(q退出并结算)>>>:').strip()
if not choice:
continue
if choice == 'q':
return show_buy_goods(goods_buyed)
if choice in dict_goods:
goods_buyed.append(dict_goods[choice])
print('\033[1;34m%s\033[0m 加入购物车' % dict_goods[choice]['name'])
else:
print('\033[1;31m重新选择\033[0m') def show_goods():
# 展示商品列表
dict_goods = {}
print('展示商品列表'.center(20,'-'))
for index, i in enumerate(GOODS):
dict_goods[str(index)] = i
print('%d. %s %d' % (index, i['name'], i['price']))
return dict_goods def run():
account_info = login(access_logger)
if account_info['is_auth'] is True:
all_money = buy_goods(show_goods())
checkout(account_info, all_money)
shopper
***.json
{"id": 999, "password": "4940e231b912f39abd7d98c81bed7053", "salt": "}fH>@&0o", "balance": 15000, "credit": 15000, "enroll_date": "2016-01-01", "expire_date": "2021-01-01", "pay_day": 22, "status": 0}
***.json
account_sample.py
# -*- coding:utf-8 -*-
import json
import random
import string
from hashlib import md5 salt = ''.join(random.sample(string.digits + string.ascii_letters + string.punctuation, 8))
md5_value = md5()
md5_value.update(b''+salt.encode('utf-8')) account_dic = {
'id': 999,
'password': md5_value.hexdigest(),
'salt': salt,
'balance': 15000, # 账户余额
'credit': 15000, # 信用额度
'enroll_date': '2016-01-01',
'expire_date': '2021-01-01',
'pay_day': 22,
'status': 0 # 0 = normal, 1 = locked, 2 = disabled
} print(json.dumps(account_dic))
# json.dump(account_dic, open('./accounts/999.json', 'w', encoding='utf-8'))
account_sample
access.log
2018-03-04 11:33:52 AM - access - CRITICAL - account: - 登录失败次数超过3次
2018-03-04 11:34:03 AM - access - INFO - account: 1000 - 登录成功
2018-03-04 11:34:50 AM - access - INFO - account: 999 - 登录成功
2018-03-04 11:35:57 AM - access - INFO - account: 1000 - 登录成功
2018-03-04 11:38:23 AM - access - INFO - account: 1008 - 登录成功
2018-03-04 11:39:29 AM - access - INFO - account: 1008 - 登录成功
2018-03-04 11:40:17 AM - access - INFO - account: 1008 - 登录成功
2018-03-04 11:41:40 AM - access - INFO - account: 1008 - 登录成功
2018-03-04 11:43:41 AM - access - INFO - account: 1008 - 登录成功
2018-03-04 11:46:12 AM - access - CRITICAL - account: - 登录失败次数超过3次
2018-03-04 11:46:19 AM - access - INFO - account: 1000 - 登录成功
2018-03-04 11:50:57 AM - access - CRITICAL - account: 1010 - 登录失败次数超过3次
2018-03-04 11:51:03 AM - access - INFO - account: 999 - 登录成功
2018-03-04 11:51:08 AM - access - INFO - account: 1010 - 登录成功
2018-03-04 11:51:49 AM - access - INFO - account: 999 - 登录成功
2018-03-04 11:52:00 AM - access - INFO - account: 1010 - 登录成功
2018-03-04 11:54:02 AM - access - INFO - account: 1000 - 登录成功
2018-03-04 11:54:56 AM - access - INFO - account: 1000 - 登录成功
2018-03-04 11:55:38 AM - access - INFO - account: 1000 - 登录成功
2018-03-04 11:57:30 AM - access - INFO - account: 1000 - 登录成功
2018-03-04 12:02:44 PM - access - INFO - account: 999 - 登录成功
2018-03-04 12:02:57 PM - access - CRITICAL - account: 1000 - 登录失败次数超过3次
2018-03-04 12:03:04 PM - access - INFO - account: 1000 - 登录成功
2018-03-04 12:03:14 PM - access - INFO - account: 999 - 登录成功
2018-03-04 12:05:04 PM - access - INFO - account: 999 - 登录成功
access
manager.log
2018-03-04 11:34:18 AM - manager - CRITICAL - account: 123 - 登录失败次数超过3次
2018-03-04 11:34:24 AM - manager - INFO - 管理员account: 999 - 登录成功
2018-03-04 11:34:37 AM - manager - INFO - 管理员account:999 - 修改账号account:1000 - 修改冻结状态成功
2018-03-04 11:35:25 AM - manager - INFO - 管理员account:999 - 添加账户account:900
2018-03-04 11:35:38 AM - manager - INFO - 管理员account: 999 - 登录成功
2018-03-04 11:35:52 AM - manager - INFO - 管理员account:999 - 修改账号account:1000 - 修改冻结状态成功
2018-03-04 11:37:39 AM - manager - INFO - 管理员account:999 - 添加账户account:1008
2018-03-04 11:38:03 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改额度credit:1200.0
2018-03-04 11:38:37 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改冻结状态成功
2018-03-04 11:41:48 AM - manager - INFO - 管理员account: 999 - 登录成功
2018-03-04 11:41:52 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改冻结状态成功
2018-03-04 11:43:50 AM - manager - INFO - 管理员account:999 - 修改账号account:1008 - 修改冻结状态成功
2018-03-04 11:48:13 AM - manager - CRITICAL - account: 1000 - 登录失败次数超过3次
2018-03-04 11:48:19 AM - manager - INFO - 管理员account: 999 - 登录成功
2018-03-04 11:48:26 AM - manager - INFO - 管理员account:999 - 添加账户account:1009
2018-03-04 11:48:32 AM - manager - INFO - 管理员account:999 - 添加账户account:23
2018-03-04 11:48:45 AM - manager - INFO - 管理员account:999 - 添加账户account:1010
2018-03-04 11:49:36 AM - manager - INFO - 管理员account:999 - 修改账号account:1009 - 修改额度credit:1000.0
2018-03-04 11:49:52 AM - manager - INFO - 管理员account:999 - 修改账号account:1009 - 修改冻结状态成功
2018-03-04 11:50:31 AM - manager - INFO - 管理员account: 999 - 登录成功
2018-03-04 11:50:38 AM - manager - INFO - 管理员account:999 - 添加账户account:123
2018-03-04 11:56:32 AM - manager - INFO - 管理员account: 999 - 登录成功
2018-03-04 11:56:38 AM - manager - INFO - 管理员account:999 - 修改账号account:1003 - 修改冻结状态成功
2018-03-04 11:56:53 AM - manager - INFO - 管理员account:999 - 修改账号account:1003 - 修改冻结状态成功
2018-03-04 12:03:37 PM - manager - INFO - 管理员account: 999 - 登录成功
2018-03-04 12:03:45 PM - manager - INFO - 管理员account:999 - 修改账号account:999 - 修改额度credit:123.0
2018-03-04 12:04:48 PM - manager - INFO - 管理员account:999 - 修改账号account:999 - 修改额度credit:15000.0
manager
transactions.log
2018-03-04 11:36:10 AM - transactions - INFO - account: 1000 - account_type: withdraw - action: minus - interest: 0.05
2018-03-04 11:36:31 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:36:34 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:37:02 AM - transactions - INFO - account: 1000 - account_type: transfer - action: minus - interest: 0.05
2018-03-04 11:38:58 AM - transactions - INFO - account: 1008 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:42:17 AM - transactions - INFO - account: 1008 - account_type: withdraw - action: minus - interest: 0.05
2018-03-04 11:46:34 AM - transactions - INFO - account: 1000 - account_type: withdraw - action: minus - interest: 0.05
2018-03-04 11:46:42 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:46:45 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:47:48 AM - transactions - INFO - account: 1000 - account_type: transfer - action: minus - interest: 0.05
2018-03-04 11:51:15 AM - transactions - INFO - account: 1010 - account_type: consume - action: minus - interest: 0
2018-03-04 11:52:08 AM - transactions - INFO - account: 1010 - account_type: withdraw - action: minus - interest: 0.05
2018-03-04 11:55:22 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:55:41 AM - transactions - INFO - account: 1000 - account_type: withdraw - action: minus - interest: 0.05
2018-03-04 11:55:47 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:56:04 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:56:06 AM - transactions - INFO - account: 1000 - account_type: pay_back - action: plus - interest: 0
2018-03-04 11:57:35 AM - transactions - INFO - account: 1000 - account_type: consume - action: minus - interest: 0
transactions
github:https://github.com/alice-bj/atm