【简单的表白墙后端搭建】初步搭建

表白墙应用后端实现

功能完整的表白墙后端应用,使用Python的Flask框架实现!

设计思路

这个表白墙应用将包含以下核心功能:

  • 用户发布表白
  • 查看所有表白(分页功能)
  • 点赞功能
  • 简单的管理员功能(删除表白)
  • RESTful API接口

完整代码实现

from flask import Flask, jsonify, request, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import os

app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'confessions.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'your_secret_key_here'  # 生产环境中应使用更安全的密钥

db = SQLAlchemy(app)

# 定义表白模型
class Confession(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    sender = db.Column(db.String(50), nullable=False)
    receiver = db.Column(db.String(50), nullable=False)
    message = db.Column(db.Text, nullable=False)
    likes = db.Column(db.Integer, default=0)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)
    is_approved = db.Column(db.Boolean, default=True)  # 管理员审核
    
    def to_dict(self):
        return {
            'id': self.id,
            'sender': self.sender,
            'receiver': self.receiver,
            'message': self.message,
            'likes': self.likes,
            'timestamp': self.timestamp.strftime('%Y-%m-%d %H:%M:%S')
        }

# 创建数据库表
@app.before_first_request
def create_tables():
    db.create_all()

# 主页 - 显示所有表白
@app.route('/')
def index():
    page = request.args.get('page', 1, type=int)
    per_page = 10
    confessions = Confession.query.filter_by(is_approved=True).order_by(Confession.timestamp.desc()).paginate(page=page, per_page=per_page)
    return render_template('index.html', confessions=confessions)

# 发布表白页面
@app.route('/post', methods=['GET'])
def post_form():
    return render_template('post.html')

# 处理表白提交
@app.route('/post', methods=['POST'])
def post_confession():
    sender = request.form.get('sender', '匿名').strip()
    receiver = request.form.get('receiver', '').strip()
    message = request.form.get('message', '').strip()
    
    if not receiver or not message:
        return render_template('post.html', error='收件人和内容不能为空')
    
    if len(message) > 500:
        return render_template('post.html', error='内容过长,请限制在500字以内')
    
    new_confession = Confession(
        sender=sender or '匿名',
        receiver=receiver,
        message=message
    )
    
    db.session.add(new_confession)
    db.session.commit()
    
    return redirect(url_for('index'))

# 点赞功能
@app.route('/like/<int:confession_id>', methods=['POST'])
def like_confession(confession_id):
    confession = Confession.query.get_or_404(confession_id)
    confession.likes += 1
    db.session.commit()
    return jsonify({'likes': confession.likes})

# API接口 - 获取所有表白
@app.route('/api/confessions', methods=['GET'])
def get_confessions():
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 10, type=int)
    confessions = Confession.query.filter_by(is_approved=True).order_by(Confession.timestamp.desc()).paginate(page=page, per_page=per_page)
    
    result = {
        'confessions': [c.to_dict() for c in confessions.items],
        'total': confessions.total,
        'pages': confessions.pages,
        'current_page': confessions.page
    }
    
    return jsonify(result)

# API接口 - 发布表白
@app.route('/api/confessions', methods=['POST'])
def create_confession():
    data = request.get_json()
    sender = data.get('sender', '匿名').strip()
    receiver = data.get('receiver', '').strip()
    message = data.get('message', '').strip()
    
    if not receiver or not message:
        return jsonify({'error': '收件人和内容不能为空'}), 400
    
    if len(message) > 500:
        return jsonify({'error': '内容过长,请限制在500字以内'}), 400
    
    new_confession = Confession(
        sender=sender or '匿名',
        receiver=receiver,
        message=message
    )
    
    db.session.add(new_confession)
    db.session.commit()
    
    return jsonify(new_confession.to_dict()), 201

# 管理员页面
@app.route('/admin')
def admin():
    page = request.args.get('page', 1, type=int)
    per_page = 20
    confessions = Confession.query.order_by(Confession.timestamp.desc()).paginate(page=page, per_page=per_page)
    return render_template('admin.html', confessions=confessions)

# 删除表白
@app.route('/admin/delete/<int:confession_id>', methods=['POST'])
def delete_confession(confession_id):
    confession = Confession.query.get_or_404(confession_id)
    db.session.delete(confession)
    db.session.commit()
    return redirect(url_for('admin'))

# 审核表白
@app.route('/admin/approve/<int:confession_id>', methods=['POST'])
def approve_confession(confession_id):
    confession = Confession.query.get_or_404(confession_id)
    confession.is_approved = not confession.is_approved
    db.session.commit()
    return redirect(url_for('admin'))

if __name__ == '__main__':
    app.run(debug=True)

模板文件

创建 templates 文件夹,添加以下HTML文件:

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>校园表白墙</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #f8f9fa;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        .header {
            background: linear-gradient(135deg, #ff758c 0%, #ff7eb3 100%);
            color: white;
            padding: 2rem 0;
            margin-bottom: 2rem;
            text-align: center;
        }
        .confession-card {
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
            margin-bottom: 1.5rem;
            transition: transform 0.3s;
            background-color: white;
        }
        .confession-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 6px 12px rgba(0,0,0,0.15);
        }
        .card-header {
            background-color: #fff0f3;
            border-bottom: 1px solid rgba(0,0,0,0.05);
            font-weight: 600;
        }
        .btn-like {
            background-color: #ff6b6b;
            border-color: #ff6b6b;
            color: white;
        }
        .btn-like:hover {
            background-color: #ff5252;
            border-color: #ff5252;
        }
        .btn-post {
            background: linear-gradient(135deg, #ff758c 0%, #ff7eb3 100%);
            border: none;
            padding: 10px 25px;
            font-weight: 600;
            margin-bottom: 2rem;
        }
        .pagination {
            margin-top: 2rem;
            justify-content: center;
        }
        .footer {
            text-align: center;
            padding: 2rem 0;
            color: #6c757d;
            margin-top: 2rem;
            background-color: #f1f3f5;
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="container">
            <h1>❤️ 校园表白墙 ❤️</h1>
            <p class="lead">说出你不敢当面说的话</p>
            <a href="{{ url_for('post_form') }}" class="btn btn-light btn-lg mt-3">发布表白</a>
        </div>
    </div>

    <div class="container">
        {% for confession in confessions.items %}
        <div class="confession-card card mb-4">
            <div class="card-header">
                <div class="d-flex justify-content-between">
                    <span>表白 #{{ confession.id }}</span>
                    <small class="text-muted">{{ confession.timestamp.strftime('%Y-%m-%d %H:%M') }}</small>
                </div>
            </div>
            <div class="card-body">
                <h5 class="card-title">To: {{ confession.receiver }}</h5>
                <p class="card-text">{{ confession.message }}</p>
                <div class="d-flex justify-content-between align-items-center">
                    <span>From: {{ confession.sender }}</span>
                    <div>
                        <button class="btn btn-sm btn-like" onclick="likeConfession({{ confession.id }}, this)">
                            ❤️ 点赞 <span class="badge bg-light text-dark">{{ confession.likes }}</span>
                        </button>
                    </div>
                </div>
            </div>
        </div>
        {% else %}
        <div class="text-center py-5">
            <h4>还没有表白,快来发布第一条吧!</h4>
            <a href="{{ url_for('post_form') }}" class="btn btn-post mt-3">发布表白</a>
        </div>
        {% endfor %}

        <!-- 分页导航 -->
        {% if confessions.pages > 1 %}
        <nav aria-label="Page navigation">
            <ul class="pagination">
                {% if confessions.has_prev %}
                <li class="page-item"><a class="page-link" href="{{ url_for('index', page=confessions.prev_num) }}">上一页</a></li>
                {% endif %}
                
                {% for page_num in confessions.iter_pages() %}
                    {% if page_num %}
                        <li class="page-item {% if page_num == confessions.page %}active{% endif %}">
                            <a class="page-link" href="{{ url_for('index', page=page_num) }}">{{ page_num }}</a>
                        </li>
                    {% else %}
                        <li class="page-item disabled"><span class="page-link">...</span></li>
                    {% endif %}
                {% endfor %}
                
                {% if confessions.has_next %}
                <li class="page-item"><a class="page-link" href="{{ url_for('index', page=confessions.next_num) }}">下一页</a></li>
                {% endif %}
            </ul>
        </nav>
        {% endif %}
    </div>

    <div class="footer">
        <div class="container">
            <p>校园表白墙 &copy; {{ now.year }} - 让爱传递</p>
            <p><small>所有表白内容均来自网友投稿,不代表本站观点</small></p>
        </div>
    </div>

    <script>
        function likeConfession(confessionId, button) {
            fetch(`/like/${confessionId}`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                }
            })
            .then(response => response.json())
            .then(data => {
                const badge = button.querySelector('.badge');
                badge.textContent = data.likes;
                button.disabled = true;
                button.classList.add('disabled');
            });
        }
    </script>
</body>
</html>

post.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>发布表白 - 校园表白墙</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #f8f9fa;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        .header {
            background: linear-gradient(135deg, #ff758c 0%, #ff7eb3 100%);
            color: white;
            padding: 2rem 0;
            margin-bottom: 2rem;
            text-align: center;
        }
        .form-container {
            max-width: 600px;
            margin: 0 auto;
            background-color: white;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        .btn-submit {
            background: linear-gradient(135deg, #ff758c 0%, #ff7eb3 100%);
            border: none;
            padding: 10px 25px;
            font-weight: 600;
            width: 100%;
            margin-top: 1rem;
        }
        .character-count {
            text-align: right;
            font-size: 0.9rem;
            color: #6c757d;
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="container">
            <h1>发布表白</h1>
            <p class="lead">勇敢说出你的心里话</p>
        </div>
    </div>

    <div class="container">
        <div class="form-container">
            {% if error %}
            <div class="alert alert-danger">
                {{ error }}
            </div>
            {% endif %}
            
            <form method="POST" action="{{ url_for('post_confession') }}">
                <div class="mb-3">
                    <label for="sender" class="form-label">你的名字(可选)</label>
                    <input type="text" class="form-control" id="sender" name="sender" placeholder="匿名">
                </div>
                
                <div class="mb-3">
                    <label for="receiver" class="form-label">收件人 *</label>
                    <input type="text" class="form-control" id="receiver" name="receiver" required placeholder="你想表白的人">
                </div>
                
                <div class="mb-3">
                    <label for="message" class="form-label">表白内容 *</label>
                    <textarea class="form-control" id="message" name="message" rows="5" required placeholder="写下你想说的话..."></textarea>
                    <div class="character-count">
                        <span id="charCount">0</span>/500
                    </div>
                </div>
                
                <button type="submit" class="btn btn-submit">发布表白</button>
            </form>
        </div>
    </div>

    <script>
        const messageTextarea = document.getElementById('message');
        const charCount = document.getElementById('charCount');
        
        messageTextarea.addEventListener('input', function() {
            const currentLength = this.value.length;
            charCount.textContent = currentLength;
            
            if (currentLength > 500) {
                charCount.style.color = 'red';
            } else {
                charCount.style.color = '#6c757d';
            }
        });
    </script>
</body>
</html>

admin.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙管理后台</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #f8f9fa;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        .header {
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            color: white;
            padding: 2rem 0;
            margin-bottom: 2rem;
        }
        .admin-card {
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
            margin-bottom: 1.5rem;
            background-color: white;
        }
        .btn-delete {
            background-color: #dc3545;
            color: white;
        }
        .btn-approve {
            background-color: #198754;
            color: white;
        }
        .status-badge {
            padding: 0.35em 0.65em;
            border-radius: 50rem;
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="container">
            <h1>表白墙管理后台</h1>
            <p class="lead">审核和管理表白内容</p>
        </div>
    </div>

    <div class="container">
        {% for confession in confessions.items %}
        <div class="admin-card card mb-4">
            <div class="card-header d-flex justify-content-between align-items-center">
                <div>
                    <span>表白 #{{ confession.id }}</span>
                    <span class="badge {% if confession.is_approved %}bg-success{% else %}bg-warning{% endif %} status-badge ms-2">
                        {% if confession.is_approved %}已审核{% else %}待审核{% endif %}
                    </span>
                </div>
                <small class="text-muted">{{ confession.timestamp.strftime('%Y-%m-%d %H:%M') }}</small>
            </div>
            <div class="card-body">
                <h5 class="card-title">To: {{ confession.receiver }}</h5>
                <p class="card-text">{{ confession.message }}</p>
                <div class="d-flex justify-content-between align-items-center">
                    <span>From: {{ confession.sender }}</span>
                    <span class="badge bg-primary">❤️ {{ confession.likes }} 赞</span>
                </div>
            </div>
            <div class="card-footer bg-light d-flex justify-content-end">
                <form method="POST" action="{{ url_for('approve_confession', confession_id=confession.id) }}" class="me-2">
                    <button type="submit" class="btn btn-sm btn-approve">
                        {% if confession.is_approved %}取消审核{% else %}通过审核{% endif %}
                    </button>
                </form>
                <form method="POST" action="{{ url_for('delete_confession', confession_id=confession.id) }}">
                    <button type="submit" class="btn btn-sm btn-delete">删除</button>
                </form>
            </div>
        </div>
        {% else %}
        <div class="text-center py-5">
            <h4>没有表白内容</h4>
        </div>
        {% endfor %}

        <!-- 分页导航 -->
        {% if confessions.pages > 1 %}
        <nav aria-label="Page navigation">
            <ul class="pagination justify-content-center">
                {% if confessions.has_prev %}
                <li class="page-item"><a class="page-link" href="{{ url_for('admin', page=confessions.prev_num) }}">上一页</a></li>
                {% endif %}
                
                {% for page_num in confessions.iter_pages() %}
                    {% if page_num %}
                        <li class="page-item {% if page_num == confessions.page %}active{% endif %}">
                            <a class="page-link" href="{{ url_for('admin', page=page_num) }}">{{ page_num }}</a>
                        </li>
                    {% else %}
                        <li class="page-item disabled"><span class="page-link">...</span></li>
                    {% endif %}
                {% endfor %}
                
                {% if confessions.has_next %}
                <li class="page-item"><a class="page-link" href="{{ url_for('admin', page=confessions.next_num) }}">下一页</a></li>
                {% endif %}
            </ul>
        </nav>
        {% endif %}
    </div>
</body>
</html>

部署指南

  1. 创建虚拟环境并安装依赖:
python -m venv venv
source venv/bin/activate  # Linux/Mac
venv\Scripts\activate    # Windows
pip install flask flask-sqlalchemy
  1. 创建数据库:
python
>>> from app import db
>>> db.create_all()
>>> exit()
  1. 运行应用:
flask run
  1. 访问网页:
  • 主页:http://127.0.0.1:5000/
  • 发布页面:http://127.0.0.1:5000/post
  • 管理后台:http://127.0.0.1:5000/admin

功能特点

  1. 用户界面

    • 美观的表白卡片展示
    • 响应式设计,适配移动设备
    • 分页功能
    • 点赞功能(AJAX实现)
  2. 发布功能

    • 表单验证
    • 字符数限制
    • 匿名发布选项
  3. 管理功能

    • 审核表白内容
    • 删除不当内容
    • 分页浏览所有表白
  4. API接口

    • 获取表白列表(支持分页)
    • 提交新表白
    • 点赞功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SK_独白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值