初始化

This commit is contained in:
尚美 2025-03-06 17:52:20 +08:00
commit 6283ea4afd
9 changed files with 530 additions and 0 deletions

3
.env Normal file
View File

@ -0,0 +1,3 @@
DATABASE_URL=mysql+pymysql://acore:acore@127.0.0.1:3316/sm_cdk
FLASK_ENV=development
FLASK_APP=app.py

35
README.md Normal file
View File

@ -0,0 +1,35 @@
# CDK宣传系统
这是一个用于管理游戏宣传和CDK发放的Web应用系统。
## 使用方法
1. 确保您的电脑已安装 Python 3.x 版本
- 如果没有安装,请从 https://www.python.org/downloads/ 下载并安装
- 安装时请勾选"Add Python to PATH"选项
2. 确保MySQL服务已启动并且配置正确
- 数据库名称sm_cdk
- 用户名acore
- 密码acore
- 端口3316
3. 启动系统
- 双击运行 `start.bat`
- 等待系统启动完成
- 在浏览器中访问http://127.0.0.1:5000
4. 使用说明
- 在"生成宣传内容"页面可以选择使用游戏角色名或游戏账号生成宣传内容
- 生成的内容可以一键复制
- 发布宣传内容后在CDK领取页面提交链接即可获取CDK
## 注意事项
1. 每个角色/账号只能领取一次CDK
2. 请确保完整复制并发布宣传内容
3. 发布链接必须可以公开访问
4. 如遇到问题,请检查:
- MySQL服务是否正常运行
- 数据库连接信息是否正确
- 网络连接是否正常

235
app.py Normal file
View File

@ -0,0 +1,235 @@
from flask import Flask, render_template, request, jsonify, flash, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
import requests
from bs4 import BeautifulSoup
import os
from dotenv import load_dotenv
import random
import string
import pymysql
import re
from config import AD_TEMPLATE, SERVER_INFO, VERIFICATION_CONFIG, NAME_TYPES
from sqlalchemy import create_engine
# 设置PyMySQL作为MySQL驱动
pymysql.install_as_MySQLdb()
load_dotenv()
app = Flask(__name__)
# 设置 Flask secret key
app.secret_key = os.getenv('SECRET_KEY', 'dev_key_123') # 在生产环境中使用环境变量设置
# 数据库配置
database_url = os.getenv('DATABASE_URL')
app.config['SQLALCHEMY_DATABASE_URI'] = database_url
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 创建数据库连接
def create_database():
try:
# 解析数据库URL
url_parts = database_url.split('/')
db_name = url_parts[-1]
base_url = '/'.join(url_parts[:-1])
# 创建数据库引擎(不指定数据库名)
engine = create_engine(base_url)
# 连接并创建数据库
with engine.connect() as conn:
conn.execute(f"CREATE DATABASE IF NOT EXISTS {db_name}")
print(f"数据库 {db_name} 创建成功")
except Exception as e:
print(f"创建数据库失败:{str(e)}")
db = SQLAlchemy(app)
# 定义CDK模型
class CDKCode(db.Model):
__tablename__ = '兑换码'
ID = db.Column(db.Integer, primary_key=True)
兑换码 = db.Column(db.String(20), unique=True, nullable=False)
奖励模板ID = db.Column(db.Integer, nullable=False)
# 定义已使用的CDK记录
class UsedCDK(db.Model):
__tablename__ = 'used_cdks'
id = db.Column(db.Integer, primary_key=True)
character_name = db.Column(db.String(50), nullable=False)
post_url = db.Column(db.String(255), nullable=False)
cdk = db.Column(db.String(20), nullable=False)
used_at = db.Column(db.DateTime, server_default=db.func.now())
def verify_ad_content(content, character_name):
# 检查必须包含的关键词
for keyword in VERIFICATION_CONFIG['required_keywords']:
if keyword not in content:
return False
# 检查必须匹配的模式
for pattern in VERIFICATION_CONFIG['required_patterns']:
if not re.search(pattern, content):
return False
# 确保包含角色名
if character_name not in content:
return False
return True
@app.route('/')
def index():
return render_template('index.html')
@app.route('/generate')
def generate():
return render_template('generate.html', server_info=SERVER_INFO)
@app.route('/create-ad', methods=['POST'])
def create_ad():
character_name = request.form.get('character_name')
name_type = request.form.get('name_type', 'character')
if not character_name:
return jsonify({'success': False, 'message': '请输入名称'})
ad_content = AD_TEMPLATE.format(
server_name=SERVER_INFO['server_name'],
name_type=NAME_TYPES[name_type],
name=character_name,
server_features=SERVER_INFO['server_features'],
website=SERVER_INFO['website']
)
return jsonify({
'success': True,
'ad_content': ad_content
})
@app.route('/verify', methods=['POST'])
def verify_post():
character_name = request.form.get('character_name')
post_url = request.form.get('post_url')
if not character_name or not post_url:
return jsonify({'success': False, 'message': '请填写所有必要信息'})
# 检查该角色是否已经领取过CDK
used = UsedCDK.query.filter_by(character_name=character_name).first()
if used:
return jsonify({'success': False, 'message': '该角色已经领取过CDK'})
try:
# 获取帖子内容
response = requests.get(post_url)
soup = BeautifulSoup(response.text, 'html.parser')
post_content = soup.get_text()
# 验证广告内容
if not verify_ad_content(post_content, character_name):
return jsonify({'success': False, 'message': '未找到有效的宣传内容,请确保完整复制宣传模板'})
# 获取未使用的CDK
cdk = CDKCode.query.filter(
~CDKCode.兑换码.in_(
db.session.query(UsedCDK.cdk)
)
).first()
if not cdk:
return jsonify({'success': False, 'message': 'CDK已经用完请联系管理员'})
# 记录使用情况
used_cdk = UsedCDK(
character_name=character_name,
post_url=post_url,
cdk=cdk.兑换码
)
db.session.add(used_cdk)
db.session.commit()
return jsonify({
'success': True,
'message': '验证成功!',
'cdk': cdk.兑换码
})
except Exception as e:
return jsonify({'success': False, 'message': f'验证失败:{str(e)}'})
@app.route('/redeem', methods=['GET', 'POST'])
def redeem():
if request.method == 'POST':
post_url = request.form.get('link')
if not post_url:
flash('请输入宣传链接', 'danger')
return redirect(url_for('redeem'))
try:
response = requests.get(post_url)
soup = BeautifulSoup(response.text, 'html.parser')
post_content = soup.get_text()
# 使用正则表达式提取角色名
character_match = re.search(r'推荐(角色|账号)([^\n\r]+)', post_content)
if not character_match:
flash('未在帖子中找到有效的推荐信息', 'danger')
return redirect(url_for('redeem'))
character_name = character_match.group(2).strip()
# 验证广告内容
if not verify_ad_content(post_content, character_name):
flash('未找到有效的宣传内容,请确保完整复制宣传模板', 'danger')
return redirect(url_for('redeem'))
# 检查是否已经领取过
used = UsedCDK.query.filter_by(character_name=character_name).first()
if used:
flash('该角色已经领取过CDK', 'danger')
return redirect(url_for('redeem'))
# 获取未使用的CDK
cdk = CDKCode.query.filter(
~CDKCode.兑换码.in_(
db.session.query(UsedCDK.cdk)
)
).first()
if not cdk:
flash('CDK已经用完请联系管理员', 'danger')
return redirect(url_for('redeem'))
# 记录使用情况
used_cdk = UsedCDK(
character_name=character_name,
post_url=post_url,
cdk=cdk.兑换码
)
db.session.add(used_cdk)
db.session.commit()
flash(f'恭喜您的CDK是{cdk.兑换码}', 'success')
return redirect(url_for('redeem'))
except Exception as e:
flash(f'验证失败:{str(e)}', 'danger')
return redirect(url_for('redeem'))
return render_template('redeem.html', server_info=SERVER_INFO)
if __name__ == '__main__':
# 创建数据库
create_database()
with app.app_context():
try:
# 创建所有表
db.create_all()
print("数据库表创建成功")
except Exception as e:
print(f"数据库表创建失败:{str(e)}")
app.run(debug=True)

40
config.py Normal file
View File

@ -0,0 +1,40 @@
# 广告语模板配置
AD_TEMPLATE = """
{server_name} - 魔兽世界3.3.5a服务器
{name_type}{name}
服务器特色
{server_features}
更多详情请访问{website}
"""
# 推荐人类型文本
NAME_TYPES = {
"character": "推荐角色",
"account": "推荐账号"
}
# 服务器基本信息
SERVER_INFO = {
"server_name": "[AzerothCore]",
"server_features": """1. 稳定流畅的游戏体验
2. 专业的技术团队支持
3. 独特的游戏玩法
4. 丰富的游戏内容""",
"website": "www.example.com"
}
# 验证配置
VERIFICATION_CONFIG = {
# 必须包含的关键词
"required_keywords": [
"魔兽世界3.3.5a",
"服务器特色",
"游戏体验"
],
# 广告语中必须包含的部分(正则表达式)
"required_patterns": [
r"推荐(角色|账号).*?[\n\r]", # 确保包含推荐人信息
r"服务器特色:[\s\S]*?游戏内容" # 确保包含服务器特色部分
]
}

8
requirements.txt Normal file
View File

@ -0,0 +1,8 @@
Flask==2.0.1
Flask-SQLAlchemy==2.5.1
PyMySQL==1.0.2
requests==2.26.0
beautifulsoup4==4.9.3
python-dotenv==0.19.0
Werkzeug==2.0.1
SQLAlchemy==1.4.23

3
start.bat Normal file
View File

@ -0,0 +1,3 @@
@echo off
chcp 65001
python app.py

112
templates/generate.html Normal file
View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>生成宣传内容</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h2 class="text-center">生成宣传内容</h2>
</div>
<div class="card-body">
<form id="generateForm">
<div class="mb-3">
<label class="form-label">推荐人类型</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="name_type" id="character" value="character" checked>
<label class="form-check-label" for="character">
游戏角色名
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="name_type" id="account" value="account">
<label class="form-check-label" for="account">
游戏账号
</label>
</div>
</div>
<div class="mb-3">
<label for="character_name" class="form-label" id="nameLabel">游戏角色名</label>
<input type="text" class="form-control" id="character_name" name="character_name" required>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary">生成内容</button>
</div>
</form>
<div id="result" class="mt-4" style="display: none;">
<h4>生成的宣传内容[格式需保持一致]</h4>
<div class="card">
<div class="card-body">
<pre id="generatedContent" class="mb-3"></pre>
<button id="copyButton" class="btn btn-success">复制内容</button>
</div>
</div>
</div>
</div>
</div>
<div class="mt-3 text-center">
<a href="/" class="btn btn-secondary">返回首页</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 更新输入框标签
document.querySelectorAll('input[name="name_type"]').forEach(radio => {
radio.addEventListener('change', function() {
const label = document.getElementById('nameLabel');
label.textContent = this.value === 'character' ? '游戏角色名' : '游戏账号';
});
});
// 处理表单提交
document.getElementById('generateForm').addEventListener('submit', async function(e) {
e.preventDefault();
const form = e.target;
const submitButton = form.querySelector('button[type="submit"]');
submitButton.disabled = true;
try {
const formData = new FormData(form);
const response = await fetch('/create-ad', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
document.getElementById('generatedContent').textContent = data.ad_content;
document.getElementById('result').style.display = 'block';
} else {
alert(data.message);
}
} catch (error) {
alert('系统错误,请稍后再试');
} finally {
submitButton.disabled = false;
}
});
// 复制按钮功能
document.getElementById('copyButton').addEventListener('click', function() {
const content = document.getElementById('generatedContent').textContent;
navigator.clipboard.writeText(content).then(() => {
this.textContent = '已复制';
setTimeout(() => {
this.textContent = '复制内容';
}, 2000);
}).catch(() => {
alert('复制失败,请手动复制');
});
});
</script>
</body>
</html>

45
templates/index.html Normal file
View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CDK兑换系统</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h2 class="text-center">CDK兑换系统</h2>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="card mb-3">
<div class="card-body text-center">
<h5 class="card-title">生成宣传内容</h5>
<p class="card-text">生成您的专属宣传内容</p>
<a href="/generate" class="btn btn-primary">开始生成</a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card mb-3">
<div class="card-body text-center">
<h5 class="card-title">兑换CDK</h5>
<p class="card-text">提交宣传链接兑换CDK</p>
<a href="/redeem" class="btn btn-success">立即兑换</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

49
templates/redeem.html Normal file
View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CDK兑换</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h2 class="text-center">CDK兑换</h2>
</div>
<div class="card-body">
<form method="post" action="/redeem">
<div class="mb-3">
<label for="link" class="form-label">宣传链接</label>
<input type="url" class="form-control" id="link" name="link" required>
<div class="form-text">请输入您发布宣传内容的帖子链接</div>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary">兑换CDK</button>
</div>
</form>
</div>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} mt-3" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="mt-3 text-center">
<a href="/" class="btn btn-secondary">返回首页</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>