吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1046|回复: 35
上一主题 下一主题
收起左侧

[Python 转载] 用AI写的会议室预约程序网页版

[复制链接]
跳转到指定楼层
楼主
尖叫的体毛 发表于 2025-4-14 16:32 回帖奖励
本帖最后由 尖叫的体毛 于 2025-4-14 16:37 编辑

因个人公司需要,故有此项目,所有代码均为AI生成
涉及部分格式、语法、个人使用习惯问题等均可略过,关注关键代码及成品即可


首先展示下成品:
1.首页

2.管理员登录后审批、会议室管理

3.审批通过后首页右上角的预约日历会显示具体信息

4.申请人邮箱会收到确认邮件


关键代码:
[Python] 纯文本查看 复制代码
from flask import Flask, render_template, request, redirect, url_for, session, flash, abortfrom flask_mail import Mail, Message
from extensions import db
from models import Room, Booking
from config import Config
from datetime import datetime, timedelta
import re
import uuid

app = Flask(__name__)
app.config.from_object(Config)

# 初始化扩展
db.init_app(app)
mail = Mail(app)


@app.context_processor
def inject_global_vars():
    def generate_time_slots():
        return [f"{h:02d}:{m}" for h in range(8, 21) for m in ('00', '30')]

    min_date = (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%d')
    return dict(time_slots=generate_time_slots(), min_date=min_date)

# 错误处理
@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

# 用户路由
@app.route('/')
def index():
    rooms = Room.query.filter_by(is_active=True).all()
    # 计算最小可用日期(当前日期+1小时)
    min_date = (datetime.now() + timedelta(hours=1)).strftime('%Y-%m-%d')
    return render_template('index.html', rooms=rooms, min_date=min_date)


@app.route('/book', methods=['POST'])
def book_room():
    try:
        # 获取表单数据
        booking_date = request.form['booking_date']
        start_time_str = f"{booking_date} {request.form['start_time']}"
        end_time_str = f"{booking_date} {request.form['end_time']}"
        applicant_name = request.form.get('applicant_name', '').strip()
        company = request.form.get('company', '').strip()
        phone = request.form.get('phone', '').strip()
        purpose = request.form.get('purpose', '').strip()

        # 转换为datetime对象
        start_time = datetime.strptime(start_time_str, '%Y-%m-%d %H:%M')
        end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M')

        if len(applicant_name) < 2 or len(applicant_name) > 50:
            flash("姓名需为2-50个字符", "danger")
            return redirect(url_for('index'))

        if len(company) < 2 or len(company) > 100:
            flash("公司名称需为2-100个字符", "danger")
            return redirect(url_for('index'))

        if not re.match(r'^1[3-9]\d{9}$', phone):
            flash("请输入有效的中国大陆手机号码", "danger")
            return redirect(url_for('index'))

        # 验证时间有效性
        now = datetime.now()
        if start_time < now + timedelta(hours=1):
            flash("请至少提前1小时预约", "danger")
            return redirect(url_for('index'))

        if end_time <= start_time:
            flash("结束时间必须晚于开始时间", "danger")
            return redirect(url_for('index'))



        # 检查时间冲突
        conflicting_booking = Booking.query.filter(
            Booking.room_id == request.form['room_id'],
            Booking.start_time < end_time,
            Booking.end_time > start_time,
            Booking.status == '已批准'
        ).first()

        if conflicting_booking:
            flash("该时间段已被预约", "danger")
            return redirect(url_for('index'))

        # 创建预约
        booking = Booking(
            email=request.form['email'],
            room_id=request.form['room_id'],
            start_time=start_time,
            end_time=end_time,
            cancel_code=str(uuid.uuid4()),
            applicant_name = applicant_name,
            company = company,
            phone = phone,
            purpose = purpose
        )

        # 新增保存前的验证
        if not booking.validate_phone():
            flash("手机号码格式错误", "danger")
            return redirect(url_for('index'))

        db.session.add(booking)
        db.session.commit()
        send_confirmation_email(booking)
        flash("预约申请已提交,请查收邮件确认", "success")
        return redirect(url_for('index', booking_success='true'))

    except Exception as e:
        db.session.rollback()
        app.logger.error(f"预约错误: {str(e)}")
        flash("预约失败,请检查输入信息", "danger")

    return redirect(url_for('index'))


def send_confirmation_email(booking):
    try:
        msg = Message("会议室预约确认",
                      sender=app.config['MAIL_USERNAME'],
                      recipients=[booking.email])
        msg.body = render_template(
            'emails/confirmation.txt',
            booking=booking,
            applicant_name=booking.applicant_name,  # 新增
            company=booking.company,  # 新增
            phone=booking.phone,  # 新增
            purpose=booking.purpose,  # 新增
            cancel_url=url_for('cancel_booking', code=booking.cancel_code, _external=True)
        )
        mail.send(msg)
    except Exception as e:
        app.logger.error(f"邮件发送失败: {str(e)}")

def send_approval_email(booking):
    """发送审批通过确认邮件"""
    try:
        msg = Message(
            "会议室预约已通过",
            sender=app.config['MAIL_USERNAME'],
            recipients=[booking.email]
        )
        msg.html = render_template(
            'emails/approval.html',
            booking=booking,
            contact_email=app.config['MAIL_USERNAME']
        )
        mail.send(msg)
        app.logger.info(f"已发送审批通过邮件至 {booking.email}")
    except Exception as e:
        app.logger.error(f"审批通过邮件发送失败: {str(e)}")


@app.route('/admin/bookings/approve/<int:id>')
def approve_booking_reason(id):
    if not session.get('admin_logged_in'):
        abort(403)

    try:
        booking = Booking.query.get_or_404(id)
        booking.status = '已批准'
        db.session.commit()

        # 发送审批通过邮件
        send_approval_email(booking)

        flash("预约已批准并发送确认邮件", "success")
    except Exception as e:
        db.session.rollback()
        app.logger.error(f"审批操作失败: {str(e)}")
        flash("操作失败,请检查日志", "danger")

    return redirect(url_for('manage_bookings'))

def send_rejection_email(booking, reason=None):
    """发送拒绝通知邮件"""
    try:
        msg = Message("会议室预约未通过审批",
                      sender=app.config['MAIL_USERNAME'],
                      recipients=[booking.email])

        # 使用HTML模板
        msg.html = render_template(
            'emails/rejection.html',
            booking=booking,
            reason=reason,
            contact_email=app.config['MAIL_USERNAME']
        )

        mail.send(msg)
        app.logger.info(f"已发送拒绝通知至 {booking.email}")
    except Exception as e:
        app.logger.error(f"拒绝邮件发送失败: {str(e)}")


@app.route('/admin/bookings/reject/<int:id>', methods=['GET', 'POST'])
def reject_booking_with_reason(id):
    if not session.get('admin_logged_in'):
        abort(403)

    booking = Booking.query.get_or_404(id)

    if request.method == 'POST':
        # 获取拒绝原因
        rejection_reason = request.form.get('reason', '不符合会议室使用规定')

        booking.status = '已拒绝'
        db.session.commit()

        # 发送拒绝邮件
        send_rejection_email(booking, rejection_reason)

        flash("已拒绝该预约并通知申请人", "success")
        return redirect(url_for('manage_bookings'))

    # GET请求显示原因填写表单
    return render_template('admin/reject_reason.html', booking=booking)

@app.route('/cancel/<string:code>')
def cancel_booking(code):
    booking = Booking.query.filter_by(cancel_code=code).first()
    if booking:
        room_name = booking.room.name
        time_str = booking.start_time.strftime('%Y-%m-%d %H:%M')
        db.session.delete(booking)
        db.session.commit()

        # 发送取消确认邮件
        try:
            msg = Message("预约取消确认",
                          sender=app.config['MAIL_USERNAME'],
                          recipients=[booking.email])
            msg.body = f"""您的预约已取消:

                会议室:{room_name}
                原定时间:{time_str}

                如需重新预约,请访问:{url_for('index', _external=True)}"""
            mail.send(msg)
        except Exception as e:
            app.logger.error(f"取消确认邮件发送失败: {str(e)}")

        return render_template('bookings/cancel.html', booking=booking)

    return render_template('bookings/cancel.html', booking=None), 404

# 管理员路由
@app.route('/admin')
def admin_dashboard():
    if not session.get('admin_logged_in'):
        return redirect(url_for('admin_login'))
    room_count = Room.query.count()
    pending_booking_count = Booking.query.filter_by(status='待审批').count()
    return render_template('admin/dashboard.html',
        room_count = room_count,
    pending_booking_count = pending_booking_count)


@app.route('/admin/login', methods=['GET', 'POST'])
def admin_login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        if username == app.config['ADMIN_USERNAME'] and password == app.config['ADMIN_PASSWORD']:
            session['admin_logged_in'] = True
            return redirect(url_for('admin_dashboard'))
        flash("用户名或密码错误", "danger")
    return render_template('auth/login.html')


@app.route('/admin/logout')
def admin_logout():
    session.pop('admin_logged_in', None)
    return redirect(url_for('index'))


@app.route('/admin/rooms')
def manage_rooms():
    if not session.get('admin_logged_in'):
        return redirect(url_for('admin_login'))
    rooms = Room.query.all()
    return render_template('admin/rooms.html', rooms=rooms)


@app.route('/admin/room/add', methods=['POST'])
def add_room():
    if not session.get('admin_logged_in'):
        abort(403)
    try:
        room = Room(
            name=request.form['name'],
            capacity=int(request.form['capacity']),
            equipment=request.form.get('equipment', '')
        )
        db.session.add(room)
        db.session.commit()
        flash("会议室添加成功", "success")
    except Exception as e:
        db.session.rollback()
        app.logger.error(f"添加会议室失败: {str(e)}")
        flash("添加会议室失败", "danger")
    return redirect(url_for('manage_rooms'))


@app.route('/admin/room/delete/<int:id>')
def delete_room(id):
    if not session.get('admin_logged_in'):
        abort(403)
    room = Room.query.get_or_404(id)
    try:
        db.session.delete(room)
        db.session.commit()
        flash("会议室删除成功", "success")
    except Exception as e:
        db.session.rollback()
        app.logger.error(f"删除失败: {str(e)}")
        flash("删除失败,请先处理相关预约", "danger")
    return redirect(url_for('manage_rooms'))


@app.route('/admin/bookings')
def manage_bookings():
    if not session.get('admin_logged_in'):
        return redirect(url_for('admin_login'))
    # 添加排序和新字段查询
    bookings = Booking.query.order_by(Booking.start_time.desc()).all()
    return render_template('admin/bookings.html',
                         bookings=bookings,
                         datetime=datetime)  # 传递datetime到模板
    bookings = Booking.query.order_by(Booking.created_at.desc()).all()
    return render_template('admin/bookings.html', bookings=bookings)



@app.route('/admin/bookings/approve/<int:id>')
def approve_booking(id):
    if not session.get('admin_logged_in'):
        abort(403)
    booking = Booking.query.get_or_404(id)
    booking.status = '已批准'
    db.session.commit()
    return redirect(url_for('manage_bookings'))


@app.route('/admin/bookings/reject/<int:id>')
def reject_booking(id):
    if not session.get('admin_logged_in'):
        abort(403)
    booking = Booking.query.get_or_404(id)
    booking.status = '已拒绝'
    db.session.commit()
    return redirect(url_for('manage_bookings'))


# 添加在现有路由附近
@app.route('/calendar')
def booking_calendar():
    # 修正:从datetime模块获取当前时间
    current_time = datetime.now()
    # 获取所有已批准的预约
    approved_bookings = Booking.query.filter(
        Booking.status == '已批准',
        Booking.end_time > current_time
    ).order_by(Booking.start_time.asc()).all()
    # 按日期和会议室分组
    bookings_dict = {}
    for booking in approved_bookings:
        date_str = booking.start_time.strftime('%Y-%m-%d')
        if date_str not in bookings_dict:
            bookings_dict[date_str] = {}
        if booking.room.name not in bookings_dict[date_str]:
            bookings_dict[date_str][booking.room.name] = []
        bookings_dict[date_str][booking.room.name].append({
            'start': booking.start_time.strftime('%H:%M'),
            'end': booking.end_time.strftime('%H:%M'),
            'applicant': booking.applicant_name
        })

    return render_template('calendar.html',
                           bookings=bookings_dict,
                           dates=sorted(bookings_dict.keys()),
                           datetime=datetime)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(host='0.0.0.0', port=5000, debug=True)


使用前请先配置目录下的config文件:
[Python] 纯文本查看 复制代码
import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key')
    SQLALCHEMY_DATABASE_URI = 'sqlite:///meetings.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    MAIL_SERVER = os.getenv('MAIL_SERVER', 'smtp.qq.com')
    MAIL_PORT = int(os.getenv('MAIL_PORT', 465))
    MAIL_USE_SSL = True  # 必须启用SSL
    MAIL_USE_TLS = False  # 禁用TLS
    MAIL_USERNAME = os.getenv('MAIL_USERNAME', 'XXXXXX@qq.com')  # &#9989;我使用的是自己的邮箱
    MAIL_PASSWORD = os.getenv('MAIL_PASSWORD', 'XXXXXX')  # &#9989;这个就是邮箱的授权码,用来发邮件的
    ADMIN_USERNAME = 'admin'#管理员帐号
    ADMIN_PASSWORD = 'admin123'#管理员密码
    # 新增管理员邮箱配置
    ADMIN_EMAIL = 'XXXX@XXXX.com'  # 管理员联系邮箱,会显示的邮箱结尾,纯文字




最后,设置完py打开运行目录下的app.py,浏览器打开本机IP:5000访问

下载:https://wwre.lanzouq.com/i7qgI2tkr82j 密码:52pj
如有违规,管理请删,谢谢

预约成功.png (69.9 KB, 下载次数: 1)

预约成功.png

免费评分

参与人数 1吾爱币 +1 收起 理由
快乐的小驹 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
ZX0228 发表于 2025-4-14 18:09
klmt567 发表于 2025-4-14 17:01
老哥用的什么ai,我之前用ai搞东西,各种报错,修改一下就换地方报错

用deepseek一点一点搞,很简单啊  啥问题描述清楚  报错发给ai 修改就好了,我算是比0基础强一点,只会一点python,前端和小程序基本上算是0基础,用deepseek 给我媳妇搞了个小程序 网页端 服务端 小程序 ,从开发  到宝塔部署  再到域名备案  然后ssl 基本上都是用deepseek解决的,现在小程序基本上线了  就差备案卡在管局 估计要等1星期
推荐
 楼主| 尖叫的体毛 发表于 2025-4-14 17:03 |楼主
klmt567 发表于 2025-4-14 17:01
老哥用的什么ai,我之前用ai搞东西,各种报错,修改一下就换地方报错

deepseek
现在基本都是用这个吧
报错的话很正常,看得懂一点点或者问AI,一般都没啥问题
我也遇到报错很多次,把报错的点在问AI就好了
沙发
klmt567 发表于 2025-4-14 17:01
老哥用的什么ai,我之前用ai搞东西,各种报错,修改一下就换地方报错
4#
makaay 发表于 2025-4-14 17:05
这个功能太实用了,终于可以解决会议室管理问题了。感谢分享!!!
5#
rhci 发表于 2025-4-14 17:14
大神,基本运行正常,不过我这边出来像是简版的,没有图像,不知道是不是路径不对,还是啥问题,就是没皮肤
6#
 楼主| 尖叫的体毛 发表于 2025-4-14 17:16 |楼主
rhci 发表于 2025-4-14 17:14
大神,基本运行正常,不过我这边出来像是简版的,没有图像,不知道是不是路径不对,还是啥问题,就是没皮肤

没皮肤啥意思,截个图看看最好
7#
rhci 发表于 2025-4-14 17:19
尖叫的体毛 发表于 2025-4-14 17:16
没皮肤啥意思,截个图看看最好


就是这个样子的
8#
懒洋洋的池塘 发表于 2025-4-14 17:21
这个功能太实用了,终于可以解决会议室管理问题了。
9#
 楼主| 尖叫的体毛 发表于 2025-4-14 17:25 |楼主
rhci 发表于 2025-4-14 17:19
就是这个样子的

知识盲区了哈哈
看有没有大神知道吧

我在新电脑上设置的也是没问题的
10#
rhci 发表于 2025-4-14 17:27
尖叫的体毛 发表于 2025-4-14 17:25
知识盲区了哈哈
看有没有大神知道吧

找到原因了,浏览器适配的问题,谷歌浏览器就没问题,但是用edge就出这问题了。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-18 01:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表