diff --git a/App/__init__.py b/App/__init__.py index 6cfdb1f..7e83c96 100644 --- a/App/__init__.py +++ b/App/__init__.py @@ -6,7 +6,7 @@ from . import * from .views import blus def create_app(): - app = Flask(__name__, static_folder='D:\\2025\\fraud-detection-ml\\uploads') + app = Flask(__name__) # 注册蓝图 app.register_blueprint(blueprint=blus) # MySQL所在主机名,默认127.0.0.1 diff --git a/App/models.py b/App/models.py index 25a68e5..1c331d2 100644 --- a/App/models.py +++ b/App/models.py @@ -52,8 +52,9 @@ class FinancialTransaction(db.Model): browser_info = db.Column(db.String(255), nullable=True) mobile = db.Column(db.Integer, nullable=True) is_fraud = db.Column(db.Boolean, nullable=False) + status = db.Column(db.Boolean, nullable=False) def __init__(self, user_name, transaction_amount, transaction_time, transaction_location, device_info, ip_address,transaction_status,mobile, - browser_info, is_fraud): + browser_info, is_fraud,status): self.user_name = user_name self.transaction_amount = transaction_amount self.transaction_time = transaction_time @@ -64,10 +65,12 @@ class FinancialTransaction(db.Model): self.is_fraud = is_fraud self.transaction_status = transaction_status self.mobile = mobile + self.status = status def to_dict(self): return { 'transaction_id': self.transaction_id, + 'status': self.status, 'user_name': self.user_name, 'transaction_amount': self.transaction_amount, 'transaction_time': self.transaction_time.strftime('%Y-%d-%m %H:%M:%S'), diff --git a/App/views.py b/App/views.py index 7c0e782..f33c0ec 100644 --- a/App/views.py +++ b/App/views.py @@ -7,14 +7,16 @@ from flask import Blueprint import hashlib from math import ceil +from sqlalchemy import and_ + from .utils.api_utils import APIUtils from .models import * blus = Blueprint("user", __name__) db_config = { - 'host': 'localhost', + 'host': '192.168.15.2', 'user': 'root', - 'password': '123456', - 'database': 'job', + 'password': 'minxianrui', + 'database': 'fraud_detection_ml', 'charset': 'utf8mb4' } # 注册 @@ -112,14 +114,11 @@ def get_users(): # 获取 username 参数,如果没有则为 None username = request.args.get('username', type=str) - # 构建查询,先查询所有用户 query = User.query - # 如果提供了 username,则根据 username 进行筛选 if username: query = query.filter(User.username.like(f'%{username}%')) - # 执行分页查询 users_pagination = query.paginate(page=page, per_page=per_page, error_out=False) @@ -150,42 +149,10 @@ def get_users(): return APIUtils.success_response(data=response, message="获取用户列表成功") - -# 文件上传 -@blus.route('/api/upload', methods=['POST']) -def upload(): - # 检查是否有文件上传 - if 'file' not in request.files: - return APIUtils.error_response(message="没有上传文件!") - file = request.files['file'] - # 如果用户没有选择文件,浏览器也会提交一个空文件 - if file.filename == '': - return APIUtils.error_response(message="没有上传文件!") - # 保存文件 - upload_folder = "uploads" - if not os.path.exists(upload_folder): - os.makedirs(upload_folder) # 如果不存在则创建目录 - # 保存文件 - file_path = os.path.join(upload_folder, file.filename) - file.save(file_path) - - # 构建文件的可访问 URL - file_url = f"http://127.0.0.1:5000/{upload_folder}/{file.filename}" - - # 返回上传路径和文件名 - response_data = { - "name": file.filename.split(".")[0], - "path": file_path, # 保存的完整路径 - "url": file_url # 可访问的 URL - } - return APIUtils.success_response(data=response_data, message="上传成功") - - # 增:添加新的交易记录 @blus.route('/api/transactions', methods=['POST']) def add_transaction(): data = request.get_json() - new_transaction = FinancialTransaction( user_id=data['user_id'], transaction_amount=data['transaction_amount'], @@ -196,7 +163,6 @@ def add_transaction(): browser_info=data.get('browser_info', ''), is_fraud=data['is_fraud'] ) - db.session.add(new_transaction) db.session.commit() @@ -209,16 +175,27 @@ def get_transactions(): page = request.args.get('page', 1, type=int) # 默认第一页 page_size = request.args.get('page_size', 10, type=int) # 默认每页10条 + query = FinancialTransaction.query + transactionStatus = request.args.get('transactionStatus') + status = request.args.get('status') + + + if transactionStatus and status: + query = query.filter(and_( + FinancialTransaction.is_fraud.like(f'%{transactionStatus}%'), + FinancialTransaction.status.like(f'%{status}%') + )) + elif transactionStatus: + query = query.filter(FinancialTransaction.is_fraud.like(f'%{transactionStatus}%')) + elif status: + query = query.filter(FinancialTransaction.status.like(f'%{status}%')) # 计算分页偏移量 offset = (page - 1) * page_size - # 查询交易记录,使用 limit 和 offset 实现分页 - transactions = FinancialTransaction.query.offset(offset).limit(page_size).all() - + transactions = query.offset(offset).limit(page_size).all() # 获取总记录数,用于计算总页数 - total_count = FinancialTransaction.query.count() + total_count = query.count() total_pages = ceil(total_count / page_size) - # 构建响应数据,包括分页信息 response = { 'data': [transaction.to_dict() for transaction in transactions], @@ -229,15 +206,11 @@ def get_transactions(): "total_pages": total_pages } } - # 返回分页数据,包括当前页的记录和总信息 return APIUtils.success_response( data=response, message="成功", - ) - - # 查:获取单个交易记录 @blus.route('/api/transactions/', methods=['GET']) def get_transaction(transaction_id): @@ -252,12 +225,9 @@ def get_transaction(transaction_id): @blus.route('/api/transactions/', methods=['PUT']) def update_transaction(transaction_id): transaction = FinancialTransaction.query.get(transaction_id) - if transaction is None: return jsonify({'message': 'Transaction not found'}), 404 - data = request.get_json() - transaction.user_id = data.get('user_id', transaction.user_id) transaction.transaction_amount = data.get('transaction_amount', transaction.transaction_amount) transaction.transaction_time = data.get('transaction_time', transaction.transaction_time) @@ -266,12 +236,23 @@ def update_transaction(transaction_id): transaction.ip_address = data.get('ip_address', transaction.ip_address) transaction.browser_info = data.get('browser_info', transaction.browser_info) transaction.is_fraud = data.get('is_fraud', transaction.is_fraud) - db.session.commit() - return jsonify(transaction.to_dict()) + +# 改:更新交易记录 +@blus.route('/api/utransactions/', methods=['PUT']) +def update_transaction1(transaction_id): + transaction = FinancialTransaction.query.get(transaction_id) + + print(transaction_id) + transaction.status = 1 + db.session.commit() + return jsonify(transaction.to_dict()) + + + # 删:删除交易记录 @blus.route('/api/transactions/', methods=['DELETE']) def delete_transaction(transaction_id): @@ -289,7 +270,6 @@ def delete_transaction(transaction_id): @blus.route('/api/mysql', methods=['POST']) def mysql(): data = request.get_json() - # 检查 SQL 参数是否存在 if not data['sql']: return APIUtils.error_response(message="没有sql参数") diff --git a/data/model.py b/data/model.py new file mode 100644 index 0000000..b5fb198 --- /dev/null +++ b/data/model.py @@ -0,0 +1,158 @@ +import pandas as pd +import matplotlib.pyplot as plt +import numpy as np +from sklearn.linear_model import LogisticRegression # 逻辑回归 +from sklearn.tree import DecisionTreeClassifier # 决策树 +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import StandardScaler +from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score +import joblib + +# pandas的显示设置来增加可以显示的列数 +pd.set_option('display.max_columns', None) +# 读取数据 +data = pd.read_csv('creditcard.csv') +# 查看默认的前5行数据 +data.head(5) + +# 查看数据的信息 +print(data.shape) +data.info() +# 查看数据的描述 +data.describe() +# 检查是否有空置 +data.isnull().sum() +# 查看没类的个数 +data['Class'].value_counts() +# 划分特征和标签 +X = data.drop('Class', axis=1) +y = data['Class'] +# 划分训练集和测试集 +X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) +# 特征标准化 +scaler = StandardScaler() +X_train = scaler.fit_transform(X_train) +X_test = scaler.transform(X_test) +print("原始数据训练-----") +# 使用逻辑回归算法进行训练和评估 +print("\nLogistic Regression:") +# 初始化逻辑回归模型 +model = LogisticRegression(max_iter=1000, random_state=42) +model.fit(X_train, y_train) +y_pred = model.predict(X_test) +# 打印性能指标 +print(f'Accuracy: {accuracy_score(y_test, y_pred)}') +print(f'Precision: {precision_score(y_test, y_pred)}') +print(f'Recall: {recall_score(y_test, y_pred)}') +print(f'F1 Score: {f1_score(y_test, y_pred)}') + +print("\nDecisionTreeClassifier:") +# 初始化决策树模型 +model = DecisionTreeClassifier() +model.fit(X_train, y_train) +y_pred = model.predict(X_test) +# 打印性能指标 +print(f'Accuracy: {accuracy_score(y_test, y_pred)}') +print(f'Precision: {precision_score(y_test, y_pred)}') +print(f'Recall: {recall_score(y_test, y_pred)}') +print(f'F1 Score: {f1_score(y_test, y_pred)}') + +from imblearn.under_sampling import RandomUnderSampler + +print("下采样以平衡数据") +rus = RandomUnderSampler(random_state=42) +X_res, y_res = rus.fit_resample(X, y) + +# 分割数据为训练集和测试集 +X_train, X_test, y_train, y_test = train_test_split(X_res, y_res, test_size=0.2, random_state=42) + +# 特征缩放 +scaler = StandardScaler() +X_train = scaler.fit_transform(X_train) +X_test = scaler.transform(X_test) + +# 使用逻辑回归算法进行训练和评估 +print("\nLogistic Regression:") +# 初始化逻辑回归模型 +model = LogisticRegression(max_iter=1000, random_state=42) +model.fit(X_train, y_train) +y_pred = model.predict(X_test) +# 打印性能指标 +print(f'Accuracy: {accuracy_score(y_test, y_pred)}') +print(f'Precision: {precision_score(y_test, y_pred)}') +print(f'Recall: {recall_score(y_test, y_pred)}') +print(f'F1 Score: {f1_score(y_test, y_pred)}') + +print("\nDecisionTreeClassifier:") +# 初始化决策树模型 +model = DecisionTreeClassifier() +model.fit(X_train, y_train) +y_pred = model.predict(X_test) +# 打印性能指标 +print(f'Accuracy: {accuracy_score(y_test, y_pred)}') +print(f'Precision: {precision_score(y_test, y_pred)}') +print(f'Recall: {recall_score(y_test, y_pred)}') +print(f'F1 Score: {f1_score(y_test, y_pred)}') + +from imblearn.over_sampling import SMOTE + +print("上采样以平衡数据") +smote = SMOTE(random_state=42) +X_res, y_res = smote.fit_resample(X, y) +# 分割数据为训练集和测试集 +X_train, X_test, y_train, y_test = train_test_split(X_res, y_res, + test_size=0.2, random_state=42) +# 特征缩放 +scaler = StandardScaler() +X_train = scaler.fit_transform(X_train) +X_test = scaler.transform(X_test) + +# 模型调优 +print("\nDecisionTreeClassifier:") +# 初始化决策树模型 +model = DecisionTreeClassifier() +model.fit(X_train, y_train) +y_pred = model.predict(X_test) +# 打印性能指标 +print(f'Accuracy: {accuracy_score(y_test, y_pred)}') +print(f'Precision: {precision_score(y_test, y_pred)}') +print(f'Recall: {recall_score(y_test, y_pred)}') +print(f'F1 Score: {f1_score(y_test, y_pred)}') + +# 使用逻辑回归算法进行训练和评估 +print("\nLogistic Regression:") +# 初始化逻辑回归模型 +model = LogisticRegression(max_iter=1000, random_state=42) +model.fit(X_train, y_train) +y_pred = model.predict(X_test) +# 打印性能指标 +print(f'Accuracy: {accuracy_score(y_test, y_pred)}') +print(f'Precision: {precision_score(y_test, y_pred)}') +print(f'Recall: {recall_score(y_test, y_pred)}') +print(f'F1 Score: {f1_score(y_test, y_pred)}') +from sklearn.model_selection import learning_curve +# 获取学习曲线数据 +train_sizes, train_scores, test_scores = learning_curve(model, X, y, cv=5, n_jobs=-1, + train_sizes=np.linspace(0.1, 1.0, 5)) +# 计算训练和测试分数的平均值与标准差 +train_scores_mean = np.mean(train_scores, axis=1) +train_scores_std = np.std(train_scores, axis=1) +test_scores_mean = np.mean(test_scores, axis=1) +test_scores_std = np.std(test_scores, axis=1) + +# 绘制学习曲线 +plt.figure() +plt.title("Learning Curve") +plt.xlabel("Training Examples") +plt.ylabel("Score") +plt.grid() +plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training Score") +plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Cross-validation Score") + +plt.legend(loc="best") +plt.show() + +# 加载逻辑回归模型 +logistic_model = joblib.load('logistic_regression_model.pkl') +# 加载决策树模型 +decision_tree_model = joblib.load('decision_tree_model.pkl') diff --git a/ui/src/components/Heads.vue b/ui/src/components/Heads.vue index f56c037..bc5c571 100644 --- a/ui/src/components/Heads.vue +++ b/ui/src/components/Heads.vue @@ -78,10 +78,8 @@ const state = reactive({ * 退出登录 */ const logout = () => { - logoutAdmin().then(()=>{ - toast.success("退出成功~") + toast.success("退出成功~") router.push('/login'); - }) } /** diff --git a/ui/src/pages/admin/view1.vue b/ui/src/pages/admin/data.vue similarity index 61% rename from ui/src/pages/admin/view1.vue rename to ui/src/pages/admin/data.vue index e7615d2..226bd70 100644 --- a/ui/src/pages/admin/view1.vue +++ b/ui/src/pages/admin/data.vue @@ -1,24 +1,26 @@