Initial Commit
This commit is contained in:
commit
25ea18b871
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/frontend/node_modules/
|
3
.idea/.gitignore
vendored
Normal file
3
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
46
backend/app.py
Normal file
46
backend/app.py
Normal 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
29
backend/models.py
Normal 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
6
config_example.yaml
Normal 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
13
frontend/index.html
Normal 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
16
frontend/package.json
Normal 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
168
frontend/src/App.vue
Normal 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
4
frontend/src/main.js
Normal 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
6
frontend/vite.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user