18.Python实战:实现年会抽奖系统
目录结构
python/
├── sql/
│ └── table.sql # 创建数据库及数据表
├── config/
│ └── __init__.py # 数据库和Flask配置
├── static/
│ ├── style.css # 样式文件
│ └── script.js # JavaScript脚本
├── templates/
│ └── index.html # 主页面模板
└── lucky_draw.py # 主应用程序
1.table.sql
table.sql
CREATE DATABASE lucky_draw;
USE lucky_draw;CREATE TABLE participants (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(50) NOT NULL,department VARCHAR(50),employee_id VARCHAR(20),join_time DATETIME DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE winners (id INT AUTO_INCREMENT PRIMARY KEY,participant_id INT,draw_time DATETIME DEFAULT CURRENT_TIMESTAMP,FOREIGN KEY (participant_id) REFERENCES participants(id)
);
2.__init__.py
DB_CONFIG = {'host': 'localhost','user': 'your_username','password': 'your_password','database': 'lucky_draw'
}
3.style.css
body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;margin: 0;padding: 20px;background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);min-height: 100vh;
}.container {max-width: 1000px;margin: 0 auto;padding: 30px;background-color: white;border-radius: 15px;box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}header {text-align: center;margin-bottom: 40px;
}h1 {color: #2c3e50;font-size: 2.5em;margin-bottom: 10px;
}.subtitle {color: #7f8c8d;font-size: 1.2em;margin: 0;
}h2 {color: #34495e;font-size: 1.5em;margin-bottom: 20px;
}.section {margin: 30px 0;padding: 25px;border-radius: 10px;background-color: #f8f9fa;box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}.draw-section {background: linear-gradient(to right, #fff5f5, #fff0f0);
}.form-inline {display: flex;justify-content: center;
}.input-group {display: flex;align-items: center;gap: 10px;
}.input-group input {padding: 12px;border: 2px solid #e0e0e0;border-radius: 8px;font-size: 1em;transition: all 0.3s ease;
}.input-group input:focus {border-color: #3498db;outline: none;box-shadow: 0 0 5px rgba(52,152,219,0.3);
}.draw-controls {display: flex;flex-wrap: wrap;gap: 20px;align-items: center;justify-content: center;
}.checkbox-group {display: flex;align-items: center;gap: 5px;
}.btn {padding: 12px 24px;border: none;border-radius: 8px;cursor: pointer;font-size: 1em;transition: all 0.3s ease;display: flex;align-items: center;gap: 8px;
}.btn-primary {background-color: #3498db;color: white;
}.btn-primary:hover {background-color: #2980b9;transform: translateY(-2px);
}.btn-danger {background-color: #e74c3c;color: white;
}.btn-danger:hover {background-color: #c0392b;
}.reset-form {text-align: center;margin-top: 20px;
}.dashboard {display: grid;grid-template-columns: 1fr;gap: 30px;margin-top: 40px;
}.name-list {display: flex;flex-wrap: wrap;gap: 12px;padding: 15px;background-color: white;border-radius: 8px;min-height: 50px;
}.name-tag {padding: 8px 16px;background-color: #f0f2f5;border-radius: 20px;font-size: 0.9em;display: flex;align-items: center;gap: 8px;transition: all 0.3s ease;
}.name-tag:hover {transform: translateY(-2px);
}.winner {background: linear-gradient(45deg, #ffd700, #ffa500);color: #000;box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}.alert {padding: 15px 20px;margin: 20px 0;border-radius: 8px;background-color: #d4edda;color: #155724;text-align: center;display: flex;align-items: center;justify-content: center;gap: 10px;animation: slideIn 0.5s ease;
}.count {font-size: 0.8em;color: #666;font-weight: normal;
}footer {text-align: center;margin-top: 40px;color: #7f8c8d;
}@keyframes slideIn {from {transform: translateY(-20px);opacity: 0;}to {transform: translateY(0);opacity: 1;}
}@media (max-width: 768px) {.container {padding: 20px;}.draw-controls {flex-direction: column;}.input-group {width: 100%;}
}/* 深色模式 */
[data-theme="dark"] {background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
}[data-theme="dark"] .container {background-color: #2c3e50;color: #ecf0f1;
}[data-theme="dark"] .section {background-color: #34495e;
}[data-theme="dark"] .draw-section {background: linear-gradient(to right, #2c3e50, #34495e);
}[data-theme="dark"] h1,
[data-theme="dark"] h2 {color: #ecf0f1;
}[data-theme="dark"] .name-tag {background-color: #465c74;color: #ecf0f1;
}/* 主题切换开关 */
.theme-switch {position: fixed;top: 20px;right: 20px;display: flex;align-items: center;gap: 10px;
}.switch {position: relative;display: inline-block;width: 60px;height: 34px;
}.switch input {opacity: 0;width: 0;height: 0;
}.slider {position: absolute;cursor: pointer;top: 0;left: 0;right: 0;bottom: 0;background-color: #ccc;transition: .4s;
}.slider:before {position: absolute;content: "";height: 26px;width: 26px;left: 4px;bottom: 4px;background-color: white;transition: .4s;
}input:checked + .slider {background-color: #2196F3;
}input:checked + .slider:before {transform: translateX(26px);
}.slider.round {border-radius: 34px;
}.slider.round:before {border-radius: 50%;
}/* 抽奖动画 */
.lottery-animation {margin: 20px 0;padding: 20px;text-align: center;
}.lottery-box {position: relative;overflow: hidden;display: inline-block;padding: 30px 60px;background: linear-gradient(45deg, #f1c40f, #f39c12);border-radius: 15px;box-shadow: 0 4px 15px rgba(0,0,0,0.2);animation: pulse 1.5s infinite;
}.rolling-name-text {font-size: 2.5em;color: #fff;text-shadow: 2px 2px 4px rgba(0,0,0,0.3);margin-bottom: 10px;
}.rolling-dept-text {font-size: 1.2em;color: rgba(255, 255, 255, 0.9);text-shadow: 1px 1px 2px rgba(0,0,0,0.2);
}@keyframes pulse {0% {transform: scale(1);box-shadow: 0 4px 15px rgba(0,0,0,0.2);}50% {transform: scale(1.05);box-shadow: 0 8px 25px rgba(0,0,0,0.3);}100% {transform: scale(1);box-shadow: 0 4px 15px rgba(0,0,0,0.2);}
}/* 优化中奖名单样式 */
.winners-section .name-tag {font-size: 1.2em;padding: 10px 20px;background: linear-gradient(45deg, #ffd700, #ffa500);box-shadow: 0 2px 8px rgba(0,0,0,0.1);transition: all 0.3s ease;
}.winners-section .name-tag:hover {transform: translateY(-3px) rotate(3deg);box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}/* 统计图表 */
.charts-container {display: flex;justify-content: center;margin-top: 20px;
}.chart-wrapper {width: 300px;height: 300px;
}/* 响应式调整 */
@media (max-width: 768px) {.chart-wrapper {width: 100%;height: auto;}
}/* 中奖高亮效果 */
.winner-highlight {animation: winner-glow 1s ease-in-out infinite alternate;transform: scale(1.1);transition: all 0.3s ease;
}@keyframes winner-glow {from {box-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #e60073, 0 0 40px #e60073;}to {box-shadow: 0 0 20px #fff, 0 0 30px #ff4da6, 0 0 40px #ff4da6, 0 0 50px #ff4da6;}
}/* 庆祝效果 */
.celebration {position: absolute;top: 0;left: 0;right: 0;bottom: 0;display: flex;justify-content: center;align-items: center;background: rgba(0, 0, 0, 0.8);animation: fadeIn 0.3s ease-out;border-radius: 15px;
}.celebration-content {text-align: center;animation: popIn 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}.celebration-icon {font-size: 4em;margin-bottom: 10px;animation: bounce 1s infinite;
}.winner-name {font-size: 2.5em;color: #fff;text-shadow: 2px 2px 4px rgba(0,0,0,0.5);margin-bottom: 5px;
}.winner-dept {font-size: 1.2em;color: rgba(255, 255, 255, 0.9);
}@keyframes fadeIn {from { opacity: 0; }to { opacity: 1; }
}@keyframes popIn {0% {transform: scale(0.3);opacity: 0;}100% {transform: scale(1);opacity: 1;}
}@keyframes bounce {0%, 100% {transform: translateY(0);}50% {transform: translateY(-20px);}
}
4.script.js
// 主题切换
const themeToggle = document.getElementById('theme-toggle');
const html = document.documentElement;themeToggle.addEventListener('change', () => {if (themeToggle.checked) {html.setAttribute('data-theme', 'dark');localStorage.setItem('theme', 'dark');} else {html.setAttribute('data-theme', 'light');localStorage.setItem('theme', 'light');}
});// 加载保存的主题
const savedTheme = localStorage.getItem('theme') || 'light';
html.setAttribute('data-theme', savedTheme);
themeToggle.checked = savedTheme === 'dark';// 抽奖动画
const startDrawBtn = document.getElementById('start-draw');
const drawForm = document.getElementById('draw-form');
const rollingName = document.getElementById('rolling-name');
const rollingSound = document.getElementById('rolling-sound');
const winnerSound = document.getElementById('winner-sound');let isRolling = false;function getAvailableParticipants() {const excludeWinners = document.querySelector('input[name="exclude_winners"]').checked;if (excludeWinners) {return participants.filter(p => !p.is_winner);}return participants;
}function showParticipant(participant) {rollingName.innerHTML = `<div class="rolling-name-text">${participant.name}</div><div class="rolling-dept-text">${participant.department}</div>`;
}function getRandomParticipant(available) {const randomIndex = Math.floor(Math.random() * available.length);return available[randomIndex];
}// 随机抽取指定数量的参与者
function getRandomWinners(available, count) {const shuffled = [...available].sort(() => 0.5 - Math.random());return shuffled.slice(0, Math.min(count, available.length));
}startDrawBtn.addEventListener('click', async () => {if (isRolling) return;const available = getAvailableParticipants();if (available.length === 0) {alert('没有合适的抽奖人选!');return;}isRolling = true;startDrawBtn.disabled = true;rollingSound.currentTime = 0;rollingSound.play();// 先决定中奖者const numWinners = parseInt(document.querySelector('input[name="num_winners"]').value);const winners = getRandomWinners(available, numWinners);const finalWinner = winners[0]; // 显示第一个中奖者的动画// 开始动画let duration = 3000; // 总持续时间let interval = 50; // 初始间隔时间let startTime = Date.now();function roll() {let currentTime = Date.now();let elapsed = currentTime - startTime;// 逐渐减慢滚动速度interval = Math.min(500, 50 + (elapsed / duration) * 450);// 最后一次显示实际中奖者if (elapsed >= duration - interval) {// 显示最终中奖者showParticipant(finalWinner);// 添加中奖效果rollingName.classList.add('winner-highlight');// 停止动画rollingSound.pause();winnerSound.play();// 显示庆祝效果showCelebration(finalWinner);// 将中奖者ID添加到表单const winnerIdsInput = document.createElement('input');winnerIdsInput.type = 'hidden';winnerIdsInput.name = 'winner_ids';winnerIdsInput.value = winners.map(w => w.id).join(',');drawForm.appendChild(winnerIdsInput);// 延迟提交表单setTimeout(() => {rollingName.classList.remove('winner-highlight');drawForm.submit();}, 2000);return;}// 随机显示参与者showParticipant(getRandomParticipant(available));if (elapsed < duration) {setTimeout(roll, interval);}}roll();
});// 显示庆祝效果
function showCelebration(winner) {// 创建庆祝动画容器const celebration = document.createElement('div');celebration.className = 'celebration';// 添加中奖信息celebration.innerHTML = `<div class="celebration-content"><div class="celebration-icon">🎉</div><div class="winner-name">${winner.name}</div><div class="winner-dept">${winner.department}</div></div>`;// 添加到页面document.querySelector('.lottery-animation').appendChild(celebration);// 2秒后移除庆祝效果setTimeout(() => {celebration.remove();}, 2000);
}// 数据可视化
const ctx = document.getElementById('winnersPieChart').getContext('2d');
new Chart(ctx, {type: 'pie',data: {labels: ['已中奖', '未中奖'],datasets: [{data: [winners.length, participants.length - winners.length],backgroundColor: ['rgba(255, 206, 86, 0.8)','rgba(75, 192, 192, 0.8)']}]},options: {responsive: true,plugins: {legend: {position: 'bottom'},title: {display: true,text: '中奖情况统计'}}}
});// 添加动画效果
document.querySelectorAll('.name-tag').forEach(tag => {tag.addEventListener('mouseover', () => {tag.style.transform = 'scale(1.1) rotate(5deg)';});tag.addEventListener('mouseout', () => {tag.style.transform = 'translateY(-2px)';});
});
5.index.html
<!DOCTYPE html>
<html data-theme="light">
<head><!-- 设置网页标题 --><title>年会抽奖系统</title><!-- 引入自定义的 CSS 样式文件,使用 Flask 的 url_for 函数生成静态文件的 URL --><link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"><!-- 引入 Font Awesome 图标库,用于显示各种图标 --><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"><!-- 引入 Chart.js 库,用于绘制图表 --><script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body><!-- 主题切换区域 --><div class="theme-switch"><!-- 显示月亮图标,代表暗色模式 --><i class="fas fa-moon"></i><!-- 切换开关 --><label class="switch"><!-- 复选框,用于切换主题 --><input type="checkbox" id="theme-toggle"><!-- 开关滑块 --><span class="slider round"></span></label><!-- 显示太阳图标,代表亮色模式 --><i class="fas fa-sun"></i></div><div class="container"><header><h1><i class="fas fa-gift"></i> 年会抽奖系统</h1><p class="subtitle">让我们看看谁是今天的幸运儿!</p></header><!-- 获取并显示 Flask 闪现消息 -->{% with messages = get_flashed_messages() %}<!-- 如果有闪现消息 -->{% if messages %}<!-- 遍历每条消息 -->{% for message in messages %}<!-- 显示消息的提示框 --><div class="alert"><!-- 显示铃铛图标 --><i class="fas fa-bell"></i><!-- 显示消息内容 -->{{ message }}</div>{% endfor %}{% endif %}{% endwith %}<div class="section"><h2><i class="fas fa-user-plus"></i> 添加参与者</h2><form method="POST" action="{{ url_for('add_participant') }}" class="form-inline"><div class="input-group"><input type="text" name="name" placeholder="姓名" required><input type="text" name="department" placeholder="部门" required><input type="text" name="employee_id" placeholder="工号" required><button type="submit" class="btn"><i class="fas fa-plus"></i> 添加</button></div></form></div><div class="section draw-section"><h2><i class="fas fa-random"></i> 抽奖</h2><div id="lottery-animation" class="lottery-animation"><div class="lottery-box"><div class="lottery-name" id="rolling-name">准备开始</div></div></div><form id="draw-form" method="POST" action="{{ url_for('draw') }}" class="form-inline"><div class="draw-controls"><div class="input-group"><label>抽取人数:</label><input type="number" name="num_winners" value="1" min="1" required></div><div class="checkbox-group"><label><input type="checkbox" name="exclude_winners" value="true" checked><span>排除已中奖者</span></label></div><button type="button" class="btn btn-primary" id="start-draw"><i class="fas fa-dice"></i> 开始抽奖</button></div></form><form method="POST" action="{{ url_for('reset') }}" class="reset-form"><button type="submit" class="btn btn-danger"><i class="fas fa-redo"></i> 重置中奖记录</button></form></div><div class="dashboard"><div class="section participants-section"><h2><i class="fas fa-users"></i>参与者名单<span class="count">({{ participants|length }}人)</span></h2>{% for dept, members in participants|groupby('department') %}<div class="department-group"><h3>{{ dept }} ({{ members|length }}人)</h3><div class="name-list">{% for p in members %}<span class="name-tag {% if p.is_winner %}winner{% endif %}"><i class="fas fa-user"></i>{{ p.name }}<small>{{ p.employee_id }}</small></span>{% endfor %}</div></div>{% endfor %}</div><div class="section winners-section"><h2><i class="fas fa-crown"></i>中奖名单<span class="count">({{ winners|length }}人)</span></h2><div class="name-list">{% for winner in winners %}<span class="name-tag winner"><i class="fas fa-star"></i>{{ winner.name }}</span>{% endfor %}</div></div><div class="section stats-section"><h2><i class="fas fa-chart-pie"></i> 数据统计</h2><div class="charts-container"><div class="chart-wrapper"><canvas id="winnersPieChart"></canvas></div><div class="chart-wrapper"><canvas id="departmentChart"></canvas></div></div></div></div></div><footer><p>祝大家好运!</p></footer><audio id="rolling-sound" preload="auto"></audio><audio id="winner-sound" preload="auto"></audio><script>// 传递Python数据到JavaScriptconst participants = {{ participants|tojson|safe }};const winners = {{ winners|tojson|safe }};</script><script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>
5.lucky_draw.py
# 从 flask 模块导入必要的类和函数
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash
# 导入 mysql.connector 模块用于连接和操作 MySQL 数据库
import mysql.connector
# 导入 random 模块用于生成随机数,用于抽奖功能
import random
# 从 config 模块导入 DB_CONFIG,DB_CONFIG 存储了数据库连接信息
from config import DB_CONFIG # 创建配置文件存储数据库连接信息# 创建 Flask 应用实例
app = Flask(__name__)
# 设置应用的密钥,用于会话管理和闪现消息
app.secret_key = 'your_secret_key'# 定义 Database 类,用于管理数据库连接
class Database:def __init__(self):# 初始化数据库连接对象self.conn = None# 调用 connect 方法建立数据库连接self.connect()def connect(self):try:# 使用 DB_CONFIG 中的配置信息建立数据库连接self.conn = mysql.connector.connect(**DB_CONFIG)except Exception as e:# 若连接失败,打印错误信息print(f"数据库连接错误:{str(e)}")def get_connection(self):try:# 检查数据库连接是否正常,若不正常则尝试重连,最多尝试 3 次,每次间隔 5 秒self.conn.ping(reconnect=True, attempts=3, delay=5)except:# 若重连失败,重新调用 connect 方法建立连接self.connect()# 返回数据库连接对象return self.conn# 定义 LuckyDraw 类,封装抽奖系统的主要功能
class LuckyDraw:def __init__(self):# 创建 Database 类的实例,用于管理数据库连接self.db = Database()def get_all_participants(self):"""获取所有参与者"""# 获取数据库连接对象conn = self.db.get_connection()# 创建游标对象,设置返回结果为字典形式cursor = conn.cursor(dictionary=True)try:# 执行 SQL 查询,获取所有参与者信息,并标记是否为中奖者cursor.execute("""SELECT p.*, CASE WHEN w.id IS NOT NULL THEN 1 ELSE 0 END as is_winner FROM participants p LEFT JOIN winners w ON p.id = w.participant_idORDER BY p.department, p.name""")# 获取查询结果return cursor.fetchall()except Exception as e:# 若查询失败,打印错误信息print(f"获取参与者失败:{str(e)}")# 返回空列表return []finally:# 关闭游标cursor.close()def get_winners(self):"""获取所有中奖者"""# 获取数据库连接对象conn = self.db.get_connection()# 创建游标对象,设置返回结果为字典形式cursor = conn.cursor(dictionary=True)try:# 执行 SQL 查询,获取所有中奖者信息,并按抽奖时间降序排列cursor.execute("""SELECT p.*, w.draw_time FROM winners w JOIN participants p ON w.participant_id = p.id ORDER BY w.draw_time DESC""")# 获取查询结果return cursor.fetchall()except Exception as e:# 若查询失败,打印错误信息print(f"获取中奖者失败:{str(e)}")# 返回空列表return []finally:# 关闭游标cursor.close()def add_participant(self, name, department, employee_id):"""添加参与者"""# 获取数据库连接对象conn = self.db.get_connection()# 创建游标对象cursor = conn.cursor()try:# 执行 SQL 插入语句,将参与者信息插入到 participants 表中cursor.execute("""INSERT INTO participants (name, department, employee_id) VALUES (%s, %s, %s)""", (name, department, employee_id))# 提交数据库事务conn.commit()# 返回添加成功标志return Trueexcept Exception as e:# 若插入失败,打印错误信息print(f"添加参与者失败:{str(e)}")# 回滚数据库事务conn.rollback()# 返回添加失败标志return Falsefinally:# 关闭游标cursor.close()def draw(self, num_winners=1, exclude_winners=True):"""抽奖"""# 获取数据库连接对象conn = self.db.get_connection()# 创建游标对象,设置返回结果为字典形式cursor = conn.cursor(dictionary=True)try:# 获取可抽奖的参与者if exclude_winners:# 若排除已中奖者,执行 SQL 查询,获取未中奖的参与者cursor.execute("""SELECT p.* FROM participants p LEFT JOIN winners w ON p.id = w.participant_id WHERE w.id IS NULL""")else:# 若不排除已中奖者,执行 SQL 查询,获取所有参与者cursor.execute("SELECT * FROM participants")# 获取查询结果available = cursor.fetchall()if not available:# 若没有可抽奖的参与者,返回空列表return []# 随机抽取# 从可抽奖的参与者中随机抽取指定数量的中奖者winners = random.sample(available, min(num_winners, len(available)))# 记录中奖者for winner in winners:# 执行 SQL 插入语句,将中奖者信息插入到 winners 表中cursor.execute("""INSERT INTO winners (participant_id) VALUES (%s)""", (winner['id'],))# 提交数据库事务conn.commit()# 返回中奖者列表return winnersexcept Exception as e:# 若抽奖失败,打印错误信息print(f"抽奖失败:{str(e)}")# 回滚数据库事务conn.rollback()# 返回空列表return []finally:# 关闭游标cursor.close()def reset_winners(self):"""重置中奖记录"""# 获取数据库连接对象conn = self.db.get_connection()# 创建游标对象cursor = conn.cursor()try:# 执行 SQL 语句,清空 winners 表中的数据cursor.execute("TRUNCATE TABLE winners")# 提交数据库事务conn.commit()# 返回重置成功标志return Trueexcept Exception as e:# 若重置失败,打印错误信息print(f"重置中奖记录失败:{str(e)}")# 回滚数据库事务conn.rollback()# 返回重置失败标志return Falsefinally:# 关闭游标cursor.close()# 创建抽奖系统实例
lucky_draw = LuckyDraw()# 定义首页路由
@app.route('/')
def index():"""首页"""# 获取所有参与者信息participants = lucky_draw.get_all_participants()# 获取所有中奖者信息winners = lucky_draw.get_winners()# 按部门统计数据department_stats = {}for p in participants:# 获取参与者所在部门dept = p['department']if dept not in department_stats:# 若部门不在统计字典中,初始化该部门的统计信息department_stats[dept] = {'total': 0, 'winners': 0}# 该部门总人数加 1department_stats[dept]['total'] += 1if p['is_winner']:# 若参与者为中奖者,该部门中奖人数加 1department_stats[dept]['winners'] += 1# 渲染 index.html 模板,并传递参与者、中奖者和部门统计信息return render_template('index.html',participants=participants,winners=winners,department_stats=department_stats)# 定义添加参与者的路由,只接受 POST 请求
@app.route('/add_participant', methods=['POST'])
def add_participant():"""添加参与者"""# 获取表单提交的姓名,并去除前后空格name = request.form.get('name', '').strip()# 获取表单提交的部门,并去除前后空格department = request.form.get('department', '').strip()# 获取表单提交的员工编号,并去除前后空格employee_id = request.form.get('employee_id', '').strip()if name and department and employee_id:# 若姓名、部门和员工编号都不为空if lucky_draw.add_participant(name, department, employee_id):# 若添加参与者成功,闪现成功消息flash(f'成功添加参与者:{name}')else:# 若添加参与者失败,闪现失败消息flash('添加参与者失败')else:# 若信息不完整,闪现提示消息flash('请填写完整信息')# 重定向到首页return redirect(url_for('index'))# 定义抽奖的路由,只接受 POST 请求
@app.route('/draw', methods=['POST'])
def draw():"""进行抽奖"""# 获取表单提交的中奖人数,默认为 1num_winners = int(request.form.get('num_winners', 1))# 获取表单提交的是否排除已中奖者的标志,默认为 trueexclude_winners = request.form.get('exclude_winners', 'true') == 'true'# 获取表单提交的中奖者 ID 列表,以逗号分隔winner_ids = request.form.get('winner_ids', '').split(',')if winner_ids and winner_ids[0]:# 若中奖者 ID 列表不为空# 获取数据库连接对象conn = lucky_draw.db.get_connection()# 创建游标对象cursor = conn.cursor()try:# 记录中奖者for winner_id in winner_ids:# 执行 SQL 插入语句,将中奖者信息插入到 winners 表中cursor.execute("""INSERT INTO winners (participant_id) VALUES (%s)""", (int(winner_id),))# 提交数据库事务conn.commit()# 获取中奖者信息cursor.execute("""SELECT name FROM participants WHERE id IN (%s)""" % ','.join(['%s'] * len(winner_ids)), tuple(map(int, winner_ids)))# 获取中奖者姓名列表winner_names = [row[0] for row in cursor.fetchall()]# 闪现中奖消息flash(f'恭喜中奖者:{", ".join(winner_names)}')except Exception as e:# 若记录中奖失败,打印错误信息print(f"记录中奖失败:{str(e)}")# 回滚数据库事务conn.rollback()# 闪现错误消息flash('抽奖过程出现错误')finally:# 关闭游标cursor.close()else:# 若没有合适的抽奖人选,闪现提示消息flash('没有合适的抽奖人选')# 重定向到首页return redirect(url_for('index'))# 定义重置中奖记录的路由,只接受 POST 请求
@app.route('/reset', methods=['POST'])
def reset():"""重置中奖记录"""if lucky_draw.reset_winners():# 若重置中奖记录成功,闪现成功消息flash('已重置所有中奖记录')else:# 若重置中奖记录失败,闪现失败消息flash('重置失败')# 重定向到首页return redirect(url_for('index'))# 若该脚本作为主程序运行
if __name__ == '__main__':# 启动 Flask 应用,开启调试模式app.run(debug=True)
相关文章:
18.Python实战:实现年会抽奖系统
目录结构 python/ ├── sql/ │ └── table.sql # 创建数据库及数据表 ├── config/ │ └── __init__.py # 数据库和Flask配置 ├── static/ │ ├── style.css # 样式文件 │ └── script.js # JavaScript脚本…...
微信小程序地图标记点,安卓手机一次性渲染不出来的问题
问题描述: 如果微信小程序端,渲染的标记物太多,安卓手机存在标记物不显示的问题,原因初步判断是地图还没有渲染完,标记物数据已经加载完了,导致没有在地图上显示。 解决办法: 使用map组件的b…...
机器翻译同样的文本,是从英语翻译成日语更准确还是中文翻译成日语更准确
在大多数情况下,从英语翻译成日语会比从中文翻译成日语更准确,原因如下: 1. 语言结构的相似性 英语和日语的句子结构更接近,特别是在语法、从句使用、定语位置等方面。例如,日语和英语都使用 SVO 结构(主…...
ROS基本功能
1.Topic话题与Message消息(主要通讯方式) 基本规则 发布消息的步骤 常用工具 话题的订阅 使用launch启动多个节点...
C++字符串处理指南:从基础操作到性能优化——基于std::string的全面解析
博主将从C标准库中的 std::string 出发,详细探讨字符串的处理方法,涵盖常见操作、性能优化和实际应用场景。以下内容将围绕std::string 的使用展开,结合代码示例进行说明。 一、std::string 的基本操作 1.1 创建与初始化 std::string 提供了…...
语言大模型基础概念 一(先了解听说过的名词都是什么)
SFT(监督微调)和RLHF(基于人类反馈的强化学习)的区别 STF(Supervised Fine-Tuning)和RLHF(Reinforcement Learning from Human Feedback)是两种不同的模型训练方法,分别…...
sql语句的执行顺序
完整的sql语句执行顺序 SELECT DISTINCT column1, column2, aggregate_function(column3) FROM table1JOIN table2 ON table1.column table2.column WHERE condition GROUP BY column1, column2 HAVING group_condition ORDER BY column1, column2 LIMIT offset, count;FROM子…...
更高效实用 vscode 的常用设置
VSCode 可以说是文本编辑神器, 不止程序员使用, 普通人用其作为文本编辑工具, 更是效率翻倍. 这里分享博主对于 VSCode 的好用设置, 让 VSCode 如虎添翼 进入设置 首先进入设置界面, 后续都在这里进行配置修改 具体设置 每项配置通过搜索关键字, 来快速定位配置项 自动保存…...
Next.js【详解】CSS 样式方案
全局样式 Global CSS 默认已创建,即 src\app\globals.css,可根据需要修改 默认在全局布局中导入 src\app\layout.tsx import "./globals.css";组件样式 CSS Modules 新建文件 src\app\test\styles.module.css .red {color: red;}导入目标页面…...
铁塔电单车协议对接电单车TCP json协议对接成熟充电桩系统搭建低速充电桩TCP 接口规范
低速充电桩 TCP 接口规范 2019 年 9 月 10 日 目录 低速充电桩 1 2) 概述 2 • 协议服务器(IOT_Server) 6 • 通讯报文原则 6 • 报文协议说明 6 报文中所有数值类型为十进制; 6报文中不允许使用转义字符,如\n、\t、\r、\s; 6 • 报文类型…...
CTF-web:java-h2 堆叠注入rce -- N1ctf Junior EasyDB
代码存在sql注入 // 处理登录表单的POST请求PostMapping({"/login"})public String handleLogin(RequestParam String username, RequestParam String password, HttpSession session, Model model) throws SQLException {// 验证用户凭据if (this.userService.valid…...
【异或数列——博弈论】
题目 思路 异或和为0(即每一位都有偶数个1):平局最高有效位只有唯一的1:先手必胜最高有效位有奇数个1,偶数个0:先手必胜 若先选1产生优势,则剩下偶数个1,偶数个0:对手选…...
2024 年 9 月青少年软编等考 C 语言三级真题解析
目录 T1. 爆气球思路分析T2. 乘法小宇宙思路分析T3. 有多少坑思路分析T4. 势均力敌思路分析T5. 买地攻略思路分析T1. 爆气球 爆气球对孩子们来说是很好玩的游戏。假设有 n n n 只气球被布置在一条直线上,游戏的目标很简单,就是爆掉尽可能多的气球。但是这里我们加一条特殊的…...
JavaScript设计模式 -- 外观模式
在实际开发中,往往会遇到多个子系统协同工作时,直接操作各个子系统不仅接口繁琐,还容易导致客户端与内部实现紧密耦合。**外观模式(Facade Pattern)**通过为多个子系统提供一个统一的高层接口,将复杂性隐藏…...
在 Vue 项目中使用 SQLite 数据库的基础应用
目录 一、环境准备二、数据库连接与操作1. 创建数据库连接2. 创建表3. 插入数据4. 查询数据5. 更新数据6. 删除数据 三、在 Vue 组件中使用 SQLite 一、环境准备 安装 Node.js 和 npm:确保已安装 Node.js 和 npm。 创建 Vue 项目:使用 Vue CLI 创建一个…...
《安富莱嵌入式周报》第350期:Google开源Pebble智能手表,开源模块化机器人平台,开源万用表,支持10GHz HRTIM的单片机,开源CNC控制器
周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版: https://www.bilibili.com/video/BV1YPKEeyEeM/ 《安富莱嵌入式周报》第350期:Google开…...
多线程基础面试题剖析
一、线程的创建方式有几种 创建线程的方式有两种,一种是继承Thread,一种是实现Runable 在这里推荐使用实现Runable接口,因为java是单继承的,一个类继承了Thread将无法继承其他的类,而java可以实现多个接口࿰…...
C++ Primer 函数重载
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
10bit VS 8bit 视频:色彩深度的较量,谁才是视觉盛宴的王者?
10bit 和 8bit 视频 10bit 视频和 8bit 视频的主要区别在于色彩深度和细节表现能力。10bit 视频具有更高的色彩深度和更丰富的细节表现,能够提供更平滑的色彩过渡和更真实的图像质量,但需要更多的存储空间和带宽。8bit 视频则在存储和传输方面更加高效,适合于对存储空间和带…...
从零开始-将小爱接入大模型
文章目录 前言一、学习教程二、docker安装二、项目下载和配置三、文件修改文件.env.exampledeepseek模型 注册gitee并获取密钥 文件.migpt.example.js连接小爱同学 三、运行项目创建目录启动docker容器 总结 前言 基于当前人工智能的发展,大模型使用越来越方便&…...
关于 IoT DC3 中设备(Device)的理解
在物联网系统中,设备(Device)是一个非常宽泛的概念,它可以指代任何能够接入系统并进行数据交互的实体。包括但不限于手机、电脑、服务器、网关、硬件设备甚至是某些软件程序等所有能接入到该平台的媒介。 内容 定义 目的 示例 …...
北斗导航 | 周跳探测算法(matlab源码)
===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 周跳 1. 高次差法2. 相位减伪距法3. TurboEdit算法(MW+GF组合)4. 多项…...
Centos搭建python环境
在 CentOS 上配置 Python 环境可以通过以下步骤完成: 1. 检查系统自带 Python 版本 CentOS 7/8 可能已经自带了 Python: python3 --version 如果没有,或者版本过低,可以手动安装。 2. 安装 Python(推荐࿰…...
Python 字典思维导图
在本章中,你将学习能够将相关信息关联起来的Python字典。你将学习如何访问和修改字典中的信息。鉴于字典可存储的信息量几乎不受限制,因此我们会演示如何遍 历字典中的数据。另外,你还将学习存储字典的列表、存储列表的字典和存储字典的字典。…...
springboot与springcloud对应版本
Spring Boot 与 Spring Cloud Alibaba 版本对应关系 请根据您使用的 Spring Boot 版本,选择兼容的 Spring Cloud Alibaba 版本: Spring Boot Version Spring Cloud Alibaba Version Spring Cloud Version 3.0.2 2022.0.0.0 Spring Cloud 2022.0.0 3.0.2…...
verilog程序设计及SystemVerilog验证
1.Verilog测试程序设计基础 1.1Testbench及其结构 在仿真的时候Testbench用来产生测试激励给待验证设计( Design Under Verification, DUV),或者称为待测设计(Design UnderTest, DUT) 。 测试程序的一般结构: Testbench是一个测试平台,信号…...
游戏引擎学习第104天
Z轴有点悬而未决 我们正在进行渲染器相关的工作。之前我们已经完成了法线贴图的代码,虽然基本完成,但还有一些与光照相关的工作待完成。目前,天空和地面部分的光照处理已经相对正常,但局部光源的处理还没有做好。 为了进行光照部…...
OpenAI GPT-4.5技术详解与未来展望
一、GPT-4.5的技术突破 OpenAI在人工智能领域的持续创新再次引领了技术潮流。近期,OpenAI内部已经成功实现了GPT-4.5的开发,这一版本相较于前代在多个方面实现了显著的技术突破。 GPT-4.5在算法优化和数据处理上进行了多项创新,使得模型在对自然语言的理解上,尤其是在…...
【科技革命】颠覆性力量与社会伦理的再平衡
目录 2025年科技革命:颠覆性力量与社会伦理的再平衡目录技术突破全景图认知智能的范式转移量子霸权实现路径生物编程技术革命能源结构重构工程 产业生态链重构医疗健康新范式教育系统智能进化金融基础设施变革制造范式革命 科技伦理与文明演进 2025年科技革命&#…...
好好说话:深度学习扫盲
大创项目是和目标检测算法YOLO相关的,浅浅了解了一些有关深度学习的知识。在这里根据本人的理解做一些梳理。 深度学习是什么? 之前经常听到AI,机器学习,深度学习这三个概念,但是对于三者的区别一直很模糊。 AI&…...
一款基于若依的wms系统
Wms-Ruoyi-仓库库存管理 若依wms是一套基于若依的wms仓库管理系统,支持lodop和网页打印入库单、出库单。毫无保留给个人及企业免费使用。 前端采用Vue、Element UI。后端采用Spring Boot、Spring Security、Redis & Jwt。权限认证使用Jwt,支持多终…...
【ISO 14229-1:2023 UDS诊断(会话控制0x10服务)测试用例CAPL代码全解析⑥】
ISO 14229-1:2023 UDS诊断【会话控制0x10服务】_TestCase06 作者:车端域控测试工程师 更新日期:2025年02月14日 关键词:UDS诊断、0x10服务、诊断会话控制、ECU测试、ISO 14229-1:2023 TC10-006测试用例 用例ID测试场景验证要点参考条款预期…...
img标签的title和alt
img标签的title和alt 显示上 title:鼠标移入到图片上时候显示的内容; alt:图片无法加载时候显示的内容; <div class"box"><div><!-- title --><h3>title</h3><img src"./image/poster.jpg" title"这是封…...
redis cluster测试
集群节点信息这时候停掉一个master 172.30.60.31 从集群信息集中我们可以看到172.30.60.31的slave是172.30.60.41,查看41的日志,发现他成为了新的master 这时候我们在将172.30.60.41也杀死,会发现集群异常了 尝试把172.30.60.31启动ÿ…...
harmonyOS的文件的增、删、读、写相关操作(fs/content)
注意: 操作harmonyOS的文件只能对app沙箱内的文件进行操作 牵扯到两个支持点: fs和content这两个API; 具体的操作方法看下图: 创建文件 //js 引入 import fs from "ohos.files.fs" import featureAbility from "ohos.ability.featureAbility"; // 上下…...
12. Function Call实践
本文主要讲解大模型的function call能力,包括使用国内外在线模型和本地模型,以及如何微调以增强大模型的function call能力。 回顾下大模型学习思路: 1. 环境准备 1.1 平台上【模型调试】中创建 2. 调用国内外大模型测试 2.1 调用GPT-4 代…...
随机生成多孔介质matlab程序
matlab生成随机分布纤维代码,用于后续模拟处理 资源文件列表 随机生成多孔介质matlab程序.pdf , 5821...
华为云kubernetes基于keda自动伸缩deployment副本(监听redis队列长度)
1 概述 KEDA(Kubernetes-based Event-Driven Autoscaler,网址是https://keda.sh)是在 Kubernetes 中事件驱动的弹性伸缩器,功能非常强大。不仅支持根据基础的CPU和内存指标进行伸缩,还支持根据各种消息队列中的长度、…...
是时候说再见了
说再见 2018 to 2025 2018:学习 2018年开始读研。师兄师姐们说可以写写CSDN博客,对找工作也有帮助。于是在12月4日,发布了自己的第一篇文章[翻译] 神经网络与深度学习 首页 - Index。当时还在学习各种基础知识,看到了这个英文文…...
数据结构与算法-栈与队列的应用递归表达式求值
参考学习:B站-逊哥带你学编程 栈与队列的应用 题1: 答案:B 题2: 答案:C 题3: 答案:D 题4: 答案:C 题5: 答案:B 题6: 答案&…...
vue3.x 的shallowReactive 与 shallowRef 详细解读
在 Vue 3.x 中,shallowReactive 和 shallowRef 是两个用于创建浅层响应式数据的 API。它们与 reactive 和 ref 类似,但在处理嵌套对象时的行为有所不同。以下是它们的详细解读和示例。 1. shallowReactive 作用 shallowReactive 创建一个浅层响应式对…...
前缀和(Prefix Sum)算法笔记C++
前缀和(Prefix Sum)算法介绍 前缀和是一种预处理技术, 用于快速计算数组中任意子区间的元素之和. 它通过一次遍历创建一个辅助数组来存储从数组起始位置到当前索引位置所有元素的累加和, 从而使得后续查询操作的时间复杂度降低至 O ( 1 ) O(1) O(1). 定义 对于给定的数组 n…...
负载测试工具有哪些?
Apache JMeter Apache JMeter 是一款开源的性能测试工具,主要用于对 Web 应用程序进行功能、负载和压力测试。JMeter 支持多种协议和技术,包括 HTTP, HTTPS, FTP 和 WebSocket 等。通过模拟大量并发用户访问来评估应用程序的表现1。 jmeter -n -t testp…...
ROS进阶:使用URDF和Xacro构建差速轮式机器人模型
前言 本篇文章介绍的是ROS高效进阶内容,使用URDF 语言(xml格式)做一个差速轮式机器人模型,并使用URDF的增强版xacro,对机器人模型文件进行二次优化。 差速轮式机器人:两轮差速底盘由两个动力轮位于底盘左…...
windows编译使用gtest
下载: Release v1.12.1 google/googletest GitHub 编译: 编译源码中的CMakeLists.txt: 编译后生成的目录是BUILD: *(编译之前需要安装cmake: ) 关注下面这两个目录: 在vs中打开: 配置属性: 点击编译&a…...
C++编程,#include <iostream>详解,以及using namespace std;作用
在C编程中,#include <iostream> 是用来包含输入/输出流头文件的预处理指令。它允许程序使用标准的输入/输出对象如 std::cout 和 std::cin,以便与标准输入和输出流进行交互。这一头文件是编写输入输出操作时必不可少的部分。 讲到这里,…...
【愚公系列】《Python网络爬虫从入门到精通》008-正则表达式基础
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…...
Golang并发编程最佳实践:协程与通道
Golang并发编程最佳实践:协程与通道 本文旨在介绍Golang并发编程的最佳实践,重点讨论协程和通道的使用方法,以及相关的实际案例和代码示例。 一、Golang并发编程简介 又称Go语言)是一种由Google开发的编程语言,旨在提供…...
neural_gcm模型进行气象预测教程
简介 NeuralGCM (General circulation models)是Google开发的一种新型的天气和气候模型,它结合了传统的物理建模与机器学习技术,相关论文于2024年发表在nature上。它既可以做短期的天气预报,也可以做长期的气候预测。…...
怎么让DeepSeek自动化写作文案
在数字化时代,内容创作已成为企业争夺用户注意力的核心竞争力。面对海量信息需求,企业往往面临内容创作效率低下、质量参差不齐、周期长等问题。如何用技术手段解决这些痛点,成为企业迫切需要破解的难题。今天,我们将以DeepSeek为…...