Initial Commit

This commit is contained in:
SoulGateKey 2024-10-05 08:14:31 +08:00
commit 25ea18b871
10 changed files with 292 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/frontend/node_modules/

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

46
backend/app.py Normal file
View File

@ -0,0 +1,46 @@
from flask import Flask, jsonify, request
from flask_cors import CORS
from models import db, AimeCard, Mai2ProfileDetail, Mai2ItemCard
import yaml
with open('config.yaml', 'r') as file:
config = yaml.safe_load(file)
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = config['database']['uri']
db.init_app(app)
CORS(app)
@app.route('/api/query', methods=['GET'])
def query_info():
access_code = request.args.get('accesscode')
card = AimeCard.query.filter_by(access_code=access_code).first()
if not card:
return jsonify({'message': 'Access code not found'}), 404
latest_detail = Mai2ProfileDetail.query.filter_by(user=card.user).order_by(Mai2ProfileDetail.id.desc()).first()
item_cards = Mai2ItemCard.query.filter_by(user=card.user).all()
items = [
{'cardId': item.cardId, 'cardTypeId': item.cardTypeId, 'startDate': item.startDate, 'endDate': item.endDate} for
item in item_cards]
response_data = {
'user': card.user,
'details': {
'userName': latest_detail.userName if latest_detail else None,
'playerRating': latest_detail.playerRating if latest_detail else None,
'playCount': latest_detail.playCount if latest_detail else None,
'lastRomVersion': latest_detail.lastRomVersion if latest_detail else None,
'lastDataVersion': latest_detail.lastDataVersion if latest_detail else None,
'banstate': latest_detail.banstate if latest_detail else None,
},
'items': items,
}
return jsonify(response_data)
if __name__ == '__main__':
app.run(host=config['flask']['host'], port=config['flask']['port'], debug=True) # 允许从外部访问

29
backend/models.py Normal file
View File

@ -0,0 +1,29 @@
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class AimeCard(db.Model):
__tablename__ = 'aime_card'
id = db.Column(db.Integer, primary_key=True)
access_code = db.Column(db.String(50), unique=True)
user = db.Column(db.String(50))
class Mai2ProfileDetail(db.Model):
__tablename__ = 'mai2_profile_detail'
id = db.Column(db.Integer, primary_key=True)
user = db.Column(db.Integer, nullable=False)
userName = db.Column(db.String, nullable=False)
playerRating = db.Column(db.Integer, nullable=False)
playCount = db.Column(db.Integer, nullable=False)
lastRomVersion = db.Column(db.String, nullable=True)
lastDataVersion = db.Column(db.String, nullable=True)
banstate = db.Column(db.Integer, nullable=False)
class Mai2ItemCard(db.Model):
__tablename__ = 'mai2_item_card'
id = db.Column(db.Integer, primary_key=True)
cardId = db.Column(db.String, nullable=False)
cardTypeId = db.Column(db.Integer, nullable=False)
startDate = db.Column(db.String, nullable=False)
endDate = db.Column(db.String, nullable=False)
user = db.Column(db.Integer, nullable=False)

6
config_example.yaml Normal file
View File

@ -0,0 +1,6 @@
database:
uri: 'mysql+pymysql://aime:YOURPASSWORD@DATABASEIP:3306/aime'
flask:
host: '0.0.0.0'
port: 5000

13
frontend/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据查询</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

16
frontend/package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "frontend",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"vue": "^3.0.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"vite": "^5.4.8"
}
}

168
frontend/src/App.vue Normal file
View File

@ -0,0 +1,168 @@
<template>
<div class="container">
<h1>Artemis 账户查询</h1>
<div class="input-container">
<input v-model="accessCode" class="input" placeholder="输入 Access Code" />
<button @click="fetchData" class="btn">查询</button>
</div>
<div v-if="userData" class="result">
<div v-if="userData.details !== 'No profile details found'" class="card">
<h2>成功找到 Maimai DX 账户数据</h2>
<p><strong>用户唯一Id:</strong> {{ userData.user }}</p>
<p><strong>用户名:</strong> {{ userData.details.userName }}</p>
<p><strong>Rating:</strong> {{ userData.details.playerRating }}</p>
<p><strong>游玩次数:</strong> {{ userData.details.playCount }}</p>
<p><strong>最后ROM版本:</strong> {{ userData.details.lastRomVersion }}</p>
<p><strong>最后DATA版本:</strong> {{ userData.details.lastDataVersion }}</p>
<p><strong>封禁状态:</strong> {{ userData.details.banstate }}</p>
</div>
<div v-else>
<p>{{ userData.details }}</p>
</div>
<div v-if="userData.items && userData.items.length > 0">
<h3>DXPASS 信息:</h3>
<div class="card-list">
<div v-for="item in userData.items" :key="item.cardId" class="card-item">
<p><strong>DXPASS ID:</strong> {{ item.cardId }}</p>
<p><strong>DXPASS 类型:</strong> {{ getCardType(item.cardTypeId) }}</p>
<p><strong>开始日期:</strong> {{ formatDate(item.startDate) }}</p>
<p><strong>结束日期:</strong> {{ formatDate(item.endDate) }}</p>
</div>
</div>
</div>
<div v-else-if="userData.items && userData.items.length === 0">
<h3>DXPASS 信息:</h3>
<p>没有 DXPASS</p>
</div>
</div>
<div v-else-if="errorMessage">
<p class="error">{{ errorMessage }}</p>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const accessCode = ref('');
const userData = ref(null);
const errorMessage = ref('');
const fetchData = async () => {
userData.value = null; //
errorMessage.value = ''; //
try {
if (!/^\d{20}$/.test(accessCode.value)) {
alert('Access Code 必须是 20 位数字');
return;
}
const response = await fetch(`http://127.0.0.1:5000/api/query?accesscode=${accessCode.value}`);
if (!response.ok) {
throw new Error('获取用户数据时出错!请检查 Access Code 是否存在或从未使用过');
}
userData.value = await response.json();
} catch (error) {
errorMessage.value = error.message || '获取数据时出错';
}
};
const getCardType = (cardTypeId) => {
if (cardTypeId === 6) {
return 'FreedomPass';
} else if (cardTypeId === 4) {
return 'GoldPass';
}
return cardTypeId; //
};
const formatDate = (dateString) => {
if (!dateString) return '';
const date = new Date(dateString);
return date.toISOString().split('T')[0]; // yyyy-mm-dd
};
return { accessCode, userData, fetchData, errorMessage, getCardType, formatDate };
},
};
</script>
<style scoped>
.container {
max-width: 500px;
margin: 0 auto;
padding: 20px;
background-color: #f4f4f9;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
color: #333;
}
.input-container {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.input {
flex: 1;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
margin-right: 10px;
}
.btn {
padding: 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.btn:hover {
background-color: #0056b3;
}
.result {
margin-top: 20px;
}
.card {
background-color: #ffffff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.card-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.card-item {
background-color: #e9ecef;
padding: 10px;
border-radius: 5px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.1);
}
.error {
color: red;
text-align: center;
}
</style>

4
frontend/src/main.js Normal file
View File

@ -0,0 +1,4 @@
import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

6
frontend/vite.config.js Normal file
View File

@ -0,0 +1,6 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
});