如何寫ATM程序?

Python寫的ATM程序

 

需求說明:html

1
2
3
4
5
6
7
8
9
10
額度  15000 或自定義
實現購物商城,買東西加入 購物車,調用信用卡接口結帳
能夠提現,手續費 5 %
支持多帳戶登陸
支持帳戶間轉帳
記錄每個月平常消費流水
提供還款接口
ATM記錄操做日誌
提供管理接口,包括添加帳戶、用戶額度,凍結帳戶等。。。
用戶認證用裝飾器

  

程序說明:python

主菜單,進入主菜單顯示以下:git

【1】購物商城 【2】用戶中心 【3】信用卡中心 【4】後臺管理 【5】登陸系統 【6】退出json

購物商城:app

顯示各類商品和價格,選擇對應的序號進行購買函數

用戶中心:post

【1】修改密碼 【2】額度查詢 【3】消費記錄 【4】返回編碼

信用卡中心:加密

【1】額度查詢    【2】提現    【3】轉帳     【4】還款    【5】返回url

後臺管理,主要是admin用戶進行管理:

【1】建立用戶 【2】凍結信用卡 【3】解凍用戶

【4】額度調整 【5】退出後臺管理

登陸系統:

未登陸的用戶須要進行登陸

代碼結構

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# @Time    : 2017/10/19 22:18
# @Author  : lichuan
# @File    : creditcard.py

from config import template
import time
from datetime import datetime
from module import common
import pickle
from log import my_log_settings
import logging
from module.users import Users
import os,sys


#加在日誌配置模塊
my_log_settings.load_my_logging_cfg()
#訪問日誌logger,path=log/access.log
acess_logger=logging.getLogger(__name__)
#消費日誌logger,path=log/shop.log
shop_logger=logging.getLogger('shop')

#用戶認證函數
# @wraps
def auth(func):
    '''
    用戶是否已經登陸的認證裝飾器
    :param func:
    :return:
    '''
    def warpper(*args,**kwargs):
        import pickle
        # userinfo=pickle.loads(open('.json','rb').read())\
        userinfos = load_user()
        if len(userinfos)!=0:
            func(*args,**kwargs)
        else:
            login()
            userinfos = load_user()
            if len(userinfos)!=0:
                func(*args,**kwargs)
    return warpper

@auth
def shop_center():
    '''
    購物商城界面選擇
    :return:
    '''
    shop={'apple手機':7000,'魅族手機':2000,'小米手機':2500,'華爲手機':4000,'小米筆記本':4000}
    shop_list=['apple手機','魅族手機','小米手機','華爲手機','小米筆記本']
    salary=15000
    userinfo=load_user()
    # print(userinfo)
    buy_list={}
    salary=int(userinfo['salary'])
    shop_flag=True
    while shop_flag:
        print(template.shopping_index_menu)
        choice=input('請選擇:').strip()
        if not choice.isdigit() or int(choice) not in range(1,7):
            print('輸入錯誤,請重試!')
            continue
        if int(choice) == 6:
            Users[userinfo['name']]=userinfo
            dump_user(userinfo)
            print('退出購物商城,再見!')
            break
        else:
            key=shop_list[int(choice)-1]
            money=shop[key]
            if money > salary:
                print('剩餘額度不夠,請選擇別的商品!')
                continue
            else:
                salary=salary-money
                username=userinfo['name']
                shop_logger.info('[%s]購買%s,花費%d元!' % (username,key,money))
                print('%s,價值%d元,已購買!' % (key,money))
                print('剩餘額度:%d元' % salary)
                #更新信息到Users配置文件
                userinfo['salary']=salary
                if key in buy_list:
                    buy_list[key]+=1
                else:
                    buy_list[key] = 1
                userinfo['buy_list']=buy_list
                Users[userinfo['name']] = userinfo
                common.update_users(Users)

#從文件加載登陸用戶的信息
def load_user():
    '''
    從文件加載登陸用戶的信息
    :return: userinfo信息
    '''
    try:
        with open('.pkl', 'rb') as read_f:
            userinfo = {}
            userinfo = pickle.loads(read_f.read())
            # print(userinfo)
    except (FileNotFoundError,EOFError):
        pass
    return userinfo

#將登陸用戶信息從新寫入.pkl
def dump_user(users):
    '''
    將users信息從新寫入.pkl文件進行保存。
    :param users:users信息是個字典
    :return:
    '''
    with open('.pkl', 'w'):
        pass
    with open('.pkl', 'wb') as read_f:
        p = pickle.dumps(users)
        read_f.write(p)

#用戶登陸函數
def login():
    '''
    用戶登陸函數,對用戶名密碼進行校驗,用戶密碼採用加密模塊hashlib進行加鹽加密
    :return:
    '''
    err_count=0
    login_flag=True
    while login_flag:
        username=input('please input your username: ').strip()
        password=input('please input your password: ').strip()
        if username in Users:
            if Users[username]['password'] == common.encrypt(password) and Users[username]['islocked'] == 0:
                userinfo=Users[username]
                err_count = 0
                with open('.pkl','wb') as write_f:
                    p=pickle.dumps(userinfo)
                    write_f.write(p)
                acess_logger.info(str(username)+' login success!')
                print(str(username)+' login success!')
                login_flag=False
            elif Users[username]['islocked'] != 0:
                print('user is locked ! cannot login!')
                err_count = 0
                login_flag=False
                break
            else:
                print('name or password is wrong!!!')
                acess_logger.info(str(username)+' login Falied ,password is wrong')
                err_count+=1
                #錯誤登陸3次以上,鎖定用戶
                if err_count >= 3:
                    Users[username]['islocked']=1
                    acess_logger.info(str(username)+' user locked!')
                    print(str(username)+' user locked!')
                    common.update_users(Users)
                    break
        else:
            print('name or password is wrong!')
            # err_count+=1

@auth
def user_center(today,weekday):
    '''
    用戶登陸後進入的用戶我的中心界面
    :param name:用戶名稱
    :param today:
    :param weekday:星期幾
    :return:
    '''
    center_flag=True
    userinfo=load_user()
    name=userinfo['name']
    while center_flag:
        print(template.index_user_center.format(name,today,weekday))
        choice=input('please input your choice:').strip()
        if not choice.isdigit() or int(choice) not in range(1,5):
            print('input wrong,please try again!')
            continue
        if int(choice) == 4:
            print('用戶中心和您再見!')
            center_flag=False
            break
        elif int(choice) == 1:
            common.modify_passwd(userinfo)
        elif int(choice) == 2:
            query_salary()
        elif int(choice) == 3:
            buylist=common.buy_list(userinfo['name'])
            for b in buylist:
                print(b,end='')

#額度查詢函數,顯示信用卡基本信息
def query_salary():
    status_all=['正常','被鎖定']
    userinfo=load_user()
    salary=userinfo['salary']
    total_salary=userinfo['total_salary']
    cardno=userinfo['bindcard']
    name=userinfo['name']
    status=status_all[0] if userinfo['islocked'] ==0  else status_all[1]
    print(template.card_info.format(cardno,name,total_salary,salary,status))
    # print(template.card_info.format(str(cardno),name,str(total_salary),str(salary),status))

#轉帳函數
def forward_cash():
    userinfo = load_user()
    salary = userinfo['salary']
    u_card_no = userinfo['bindcard']
    card_list=[]
    print('您如今剩餘的可用額度爲:%d' %salary)
    card_no=input('請輸入對方的卡號:').strip()
    money=input('請輸入轉帳的金額:').strip()
    if not money.isdigit():
        print('金額輸入有誤!')
        return
    for k in Users:
        if Users[k]['bindcard'] != u_card_no:
            card_list.append(Users[k]['bindcard'])
    if card_no not in card_list:
        print('卡號有誤')
        return
    if int(money) > salary:
        print('轉帳金額超出你的信用額度!')
        return
    #減去本身的額度
    salary=salary-int(money)
    userinfo['salary']=salary
    dump_user(userinfo)
    Users[userinfo['name']]['salary']=salary
    #增長別人的額度
    for k in Users:
        if card_no == Users[k]['bindcard']:
            Users[k]['salary']+=int(money)
    common.update_users(Users)
    print('[%s]轉帳%d元給%s,手續費%d元' % (userinfo['name'],int(money),card_no))
    shop_logger.info('[%s]轉帳%d元給%s' % (userinfo['name'],int(money),card_no))

#提現函數
def draw_cash():
    cash=input('請輸入提現金額:').strip()
    if not cash.isdigit() or int(cash) < 0:
        print('金額輸入錯誤!')
        return
    userinfo=load_user()
    salary=userinfo['salary']
    if int(cash) > salary:
        print('你的額度是%s,額度不夠!' % salary)
        return
    else:
        salary = salary - int(cash)*1.05
        userinfo['salary']=salary
        dump_user(userinfo)
        Users[userinfo['name']]['salary'] = salary
        common.update_users(Users)
        query_salary()
        shop_logger.info('[%s]取現%d元,手續費%d元!' % (userinfo['name'],int(cash),int(cash)*0.05))
        print('[%s]取現%d元,手續費%d元!' % (userinfo['name'],int(cash),int(cash)*0.05))

#信用卡還款
def repay_salary():
    repay_money=input('請輸入還款金額:').strip()
    if not repay_money.isdigit():
        print('金額有誤!')
        return
    else:
        repay_money=int(repay_money)
    userinfo = load_user()
    userinfo['salary'] = userinfo['salary']+repay_money
    dump_user(userinfo)
    Users[userinfo['name']]=userinfo
    common.update_users(Users)
    query_salary()
    shop_logger.info('[%s]還款%d元' % (userinfo['name'], repay_money))
    print('[%s]還款%d元' % (userinfo['name'], repay_money))

#信用卡中心程序
@auth
def card_center():
    '''
    信用卡中心程序
    :return:
    '''
    func={
        '1': query_salary,
        '2': draw_cash,
        '3': forward_cash,
        '4': repay_salary,
    }
    card_flag=True
    while card_flag:
        #初始化打印信息
        userinfo=load_user()
        user_name=userinfo['name']
        card_no=userinfo['bindcard']
        print(template.index_card_center.format(user_name,card_no))
        choice=input('請選擇:').strip()
        if not choice.isdigit() or int(choice) not in range(1,6):
            print('輸入錯誤,請重試!')
            continue
        if int(choice) == 5:
            print('信用卡中心和您再見!')
            break
        else:
            func[choice]()


#後臺管理程序
@auth
def manager():
    func={
        '1':common.create_card,
        '2':common.lock_card,
        '3':common.unlock_card,
        '4':common.modify_salary,
    }
    userinfo=load_user()
    if userinfo['name'] != 'admin':
        print('只有admin用戶能夠進入後臺管理!')
        return
    manager_flag=True
    while manager_flag:
        print(template.index_admin.format(userinfo['name']))
        choice=input('請選擇:').strip()
        if not choice.isdigit() or int(choice) not in range(1,6):
            print('輸入錯誤!')
            continue
        if int(choice) == 5:
            print('後臺管理和您再見!')
            manager_flag=False
            break
        else:
            func[choice]()

if __name__ == '__main__':
    Flag=True
    # Flag=False
    while Flag:
        userinfo = load_user()
        # print(userinfo)
        # print(userinfo['name'])
        username = ''
        # username = userinfo['name'] if len(userinfo) != 0 else ''
        today=time.strftime('%Y-%m-%d',time.localtime())
        weekday=common.numTo_characters(datetime.now().weekday())
        print(template.index_default_menu.format(username,today,weekday))
        choice = input("請選擇:").strip()
        if not choice.isdigit():
            print("輸入錯誤,請從新輸入")
            continue
        if int(choice) not in range(1,7):
            print("輸入錯誤,,請從新輸入")
            continue
        if int(choice) == 1:
            shop_center()
        elif int(choice) == 2:
            user_center(today,weekday)
        elif int(choice) == 3:
            card_center()
        elif int(choice) == 4:
            manager()
        elif int(choice) == 5:
            login()
        elif int(choice) == 6:
            with open('.pkl','w',encoding='utf-8'):
                pass
            print("再見!")
            Flag=False

creditcard.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# @Time    : 2017/10/20 15:28
# @Author  : lichuan
# @File    : template.py


"""
該模塊用來定義系統的菜單模板,用的網上別人的模板。
"""
# 主程序中的主菜單

index_default_menu = '''
-------------------------------------------------------------------------
                             ATM 模擬程序

{0}                                        今天 {1}   星期{2}
-------------------------------------------------------------------------
【1】購物商城 【2】用戶中心 【3】信用卡中心 【4】後臺管理 【5】登陸系統 【6】退出
'''

#購物商城界面
shopping_index_menu = '''
    ==================================================================================
    =                                                                                =
    =                                 信用卡購物商城                                   =
    =                                                                                =
    ==================================================================================

    【1】apple手機  7000元
    【2】魅族手機    2000元 
    【3】小米手機    2500元 
    【4】華爲手機    4000元 
    【5】小米筆記本  4000元 
    【6】退出
    '''

index_card_center = '''
------------------------------------------------------------------------------
                               信用卡管理中心

用戶:{0} 卡號:{1}
------------------------------------------------------------------------------
【1】額度查詢    【2】提現    【3】轉帳     【4】還款    【5】返回
'''
# 顯示信用卡基本信息模板
card_info = '''
-----------------------------------------------------------------------------------
                               信用卡基本信息

卡號:{0}   全部人:{1}  信用額度:{2}  剩餘額度:{3} 狀態:{4}
-----------------------------------------------------------------------------------
'''
index_user_center = '''
-------------------------------------------------------------------------
                                用戶中心

當前用戶:{0}                                   今天 {1}   星期{2}
-------------------------------------------------------------------------
【1】修改密碼 【2】額度查詢 【3】消費記錄 【4】返回

'''
# 後臺管理模板
index_admin = '''
------------------------------------------------------------------------------
                               後臺管理

用戶:{0}
------------------------------------------------------------------------------
【1】建立用戶               【2】凍結信用卡         【3】解凍用戶
【4】額度調整               【5】退出後臺管理
'''



#'----------------------------------------------------------------------------------------------------------------------------------------------------------------------'

template.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# @Time    : 2017/10/20 15:46
# @Author  : lichuan
# @File    : common.py

import hashlib
from module.users import Users
import os
import re
from log import my_log_settings
import logging

my_log_settings.load_my_logging_cfg()
acess_logger=logging.getLogger(__name__)

def numTo_characters(num):
    '''
    傳入數字,轉換成星期幾中的幾
    :param num:
    :return:漢字
    '''
    if num in range(0,7):
        week=('一','二','三','四','五','六','日')
        return week[num]

def encrypt(str):
    '''
    對傳入字符串進行加鹽加密
    :param str: 須要進行加密的字符串
    :return: 返回加密過的字符串
    '''
    encrpt=hashlib.md5()
    encrpt.update(bytes('admin1234nnnnnn',encoding='utf-8'))
    encrpt.update(bytes(str,encoding='utf-8'))
    return encrpt.hexdigest()

def update_users(Users):
    '''
    更新Users信息的函數
    :param Users: 用戶信息,是個字典
    :return:
    '''
    import os
    user_path=os.path.dirname(os.path.abspath(__file__))+'\\users.py'
    with open(user_path,'w',encoding='utf-8') as write_f:
        Users_new='Users='+str(Users)
        write_f.write(Users_new)

#修改密碼
def modify_passwd(userinfo):
    '''
    用於更新Users用戶密碼信息
    :param userinfo: 傳入用戶信息
    :param new_passwd: 新的密碼信息
    :return:
    '''
    old_passwd=input('請輸入如今密碼:')
    new_passwd=input('請輸入新密碼:')
    pattern_new_passwd=input('請再次輸入新密碼:')
    old_passwd=encrypt(old_passwd)
    if new_passwd != pattern_new_passwd:
        print('兩次輸入密碼不一致!')
        return
    if old_passwd != userinfo['password']:
        print('密碼錯誤!')
        return
    encrypt_passwd=encrypt(new_passwd)
    userinfo['password']=encrypt_passwd
    Users[userinfo['name']]=userinfo
    update_users(Users)
    acess_logger.info('%s修改了用戶密碼!' % userinfo['name'])
    print('修改密碼成功!')

#用戶的購物消費信息
def buy_list(username):
    buy=[]
    shop_path=os.path.normpath(os.path.join(
        os.path.abspath(__file__),
        os.pardir,
        os.pardir,
        'log',
        'shop.log'
    ))
    with open(shop_path,'r',encoding='utf-8') as read_f:
        lines=read_f.readlines()
        r='.*\[%s\].*' %username
        patern = re.compile(r)
        for line in lines:
            if patern.match(line):
                buy.append(line)
        return buy

#建立信用卡用戶
def create_card():
    flag=True
    while True:
        username=input('請輸入name:').strip()
        if len(username) <= 0:
            print('輸入錯誤!')
            flag = False
            break
        if username in Users:
            print('用戶已存在!')
            continue
        mobile=input('請輸入手機號:').strip()
        if len(mobile) <= 0:
            print('輸入錯誤!')
            flag = False
            break
        for i in Users:
            if Users[i]['mobile'] == mobile:
                print('手機號已經存在!')
                flag=False
                break
        card_no = input('請輸入卡號:').strip()
        if len(card_no) <= 0 or not card_no.isdigit():
            print('輸入錯誤!')
            flag = False
            break
        for i in Users:
            if Users[i]['bindcard'] == card_no:
                print('卡號已經存在!')
                flag = False
                break
        passwd = input('請輸入密碼:').strip()
        encrypt_passwd=encrypt(passwd)
        userinfo={
         'isdel': 0,
         'name': username,
         'password': encrypt_passwd,
         'islocked': 0,
         'salary': 15000,
         'total_salary': 15000,
         'bindcard': card_no,
         'mobile': mobile,
         'buy_list': {}
         }
        Users[username]=userinfo
        update_users(Users)
        acess_logger.info('新建立用戶%s' %  username)
        print('新建立用戶%s已成功' %  username)
        flag=False

#凍結信用卡
def lock_card():
    name=input('請輸入要凍結的用戶名:').strip()
    if name == 'admin':
        print('不能凍結admin帳號!')
        return
    if name in Users:
        Users[name]['islocked'] = 1
        update_users(Users)
        acess_logger.info('%s用戶被凍結' %name)
        print('%s用戶被成功凍結' %name)
    else:
        print('用戶不存在!')


#解凍信用卡
def unlock_card():
    name = input('請輸入要解凍的用戶名:').strip()
    if name in Users:
        Users[name]['islocked'] = 0
        update_users(Users)
        acess_logger.info('%s用戶被解凍' % name)
        print('%s用戶被成功解凍' % name)
    else:
        print('用戶不存在!')

#調整額度
def modify_salary():
    name = input('請輸入要調整額度的用戶名:').strip()
    total_salary=input('請輸入新額度:').strip()
    if not total_salary.isdigit():
        print('額度錯誤!')
        return
    if name in Users:
        Users[name]['salary'] = int(total_salary) - (Users[name]['total_salary']-Users[name]['salary'])
        Users[name]['total_salary'] = int(total_salary)
        update_users(Users)
        acess_logger.info('%s用戶額度調整爲%s' % (name,total_salary))
        print('%s用戶額度調整爲%s' % (name,total_salary))
    else:
        print('用戶不存在!')

if __name__ == '__main__':
    # modify_passwd({},'bbb')
    # print('it is common')
    r=buy_list('alex')
    for i in r:
        print(i,end='')

common.py

 

Users={'alex': {'isdel': 0, 'name': 'alex', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 52187.4, 'total_salary': 15000, 'bindcard': '1001012345', 'mobile': '13511111111', 'buy_list': {'apple手機': 1, '魅族手機': 1, '小米手機': 1, '華爲手機': 1, '小米筆記本': 2}}, 'egon': {'isdel': 0, 'name': 'egon', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 16950, 'total_salary': 15000, 'bindcard': '1001012346', 'mobile': '13511111112', 'buy_list': {}}, 'admin': {'isdel': 0, 'name': 'admin', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 15000, 'total_salary': 15000, 'role': 'admin', 'bindcard': '1001010002', 'mobile': '15257157418'}, 'lit': {'isdel': 0, 'name': 'lit', 'password': 'bc5b9cb3e4ab483335edab3347f3c102', 'islocked': 0, 'salary': 50000, 'total_salary': 50000, 'bindcard': '1001012347', 'mobile': '13520381333', 'buy_list': {}}}

users.py
"""
logging配置
"""

import os
import logging.config

# 定義三種日誌輸出格式 開始

standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

shop_format= '[%(asctime)s]%(message)s'

# 定義日誌輸出格式 結束

logfile_dir = os.path.dirname(os.path.abspath(__file__))  # log文件的目錄

logfile_name = 'access.log'  # log文件名

# 若是不存在定義的日誌目錄就建立一個
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

# log文件的全路徑
logfile_path = os.path.join(logfile_dir, logfile_name)
shop_path = os.path.join(logfile_dir, 'shop.log')

# log配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
        'id_simple': {
            'format': id_simple_format
        },
        'shop_format': {
            'format': shop_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到終端的日誌
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日誌,收集info及以上的日誌
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日誌文件
            'maxBytes': 1024*1024*5,  # 日誌大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日誌文件的編碼,不再用擔憂中文log亂碼了
        },
        'boss': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': 'boss.log',  # 日誌文件
            'maxBytes': 1024 * 1024 * 5,  # 日誌大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日誌文件的編碼,不再用擔憂中文log亂碼了
        },
        'shop': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'shop_format',
            'filename': shop_path,  # 日誌文件
            'encoding': 'utf-8',  # 日誌文件的編碼,不再用擔憂中文log亂碼了
        },
    },
    'loggers': {
        #logger1=logging.getLogger(__name__)拿到的logger配置
        '': {
            'handlers': ['default',],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
        #logger1=logging.getLogger('collect')拿到的logger配置
        'collect': {
            'handlers': ['boss',],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
        'shop': {
            'handlers': ['shop'],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'INFO',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
    },
}


def load_my_logging_cfg():
    logging.config.dictConfig(LOGGING_DIC)  # 導入上面定義的logging配置
    # logger = logging.getLogger(__name__)  # 生成一個log實例
    # logger = logging.getLogger('shopping')  # 生成一個log實例
    # logger.info('It works2!')  # 記錄該文件的運行狀態

if __name__ == '__main__':
    load_my_logging_cfg()

my_log_settings.py
相關文章
相關標籤/搜索