26940c4574
Features: - User authentication (login/register) - Ticket CRUD with status/priority/category - Rich text description with paste-to-image - File attachment upload (images/documents) - Multi-select CC recipients - Role-based permission control (admin/user) - In-app notifications - Statistics dashboard - Gunicorn production deployment
6.5 KiB
6.5 KiB
工单系统 (Ticket System)
轻量级工单系统,基于 Flask + SQLite,适合个人或小团队使用。
功能特性
工单管理
- 创建、编辑、删除、关闭工单
- 状态流转:新建 → 进行中 → 待处理 → 已完成 → 已关闭
- 分类管理:技术故障、功能需求、咨询、事件处理、其他
- 优先级设置:低、中、高、紧急
- 自由标签支持
富文本描述
- 内容可编辑:contenteditable 富文本编辑器
- 图片内联显示:支持 Ctrl+V 粘贴截图,图片直接嵌入描述中
- 插入图片按钮:📎 点击选择图片上传
- 基础格式:支持加粗、斜体
- 粘贴上传:粘贴的图片会自动上传到服务器
附件管理
- 支持多种文件类型:图片、文档、压缩包等
- 最大文件大小:20 MB
- 图片预览:支持点击放大查看
- 附件下载:点击即可下载
用户系统
- 用户注册、登录、登出
- 默认管理员账号:
admin / admin - 密码安全存储(哈希)
权限控制
- 管理员:可查看所有工单
- 普通用户:只能查看自己参与(提交人/处理人/抄送人)的工单
- 无权限访问时返回 403 错误
抄送功能
- 支持多选抄送人
- 自定义下拉选择器
- 抄送人自动收到通知
- 抄送人可查看工单和附件
通知系统
- 站内通知:工单分配、评论、关闭等操作自动通知相关人员
- 未读角标:导航栏显示未读通知数量
- 通知列表:可查看通知详情
统计看板
- 工单总数统计
- 按状态、优先级、分类聚合
- 最近工单列表
- 我的工单计数
技术栈
- 后端:Flask + SQLAlchemy
- 数据库:SQLite
- 前端:Jinja2 模板 + 原生 JavaScript
- 部署:Gunicorn + systemd
快速开始
安装依赖
pip install -r requirements.txt --break-system-packages
启动开发服务器
python app.py
启动生产服务器
gunicorn --workers 2 --bind 0.0.0.0:5000 wsgi:app
访问系统
打开浏览器访问:http://localhost:5000
默认管理员账号:
- 用户名:
admin - 密码:
admin
项目结构
ticket-system/
├── app.py # Flask 主程序(模型 + 路由)
├── wsgi.py # Gunicorn WSGI 入口
├── requirements.txt # 依赖列表
├── README.md # 项目说明
├── instance/
│ └── tickets.db # SQLite 数据库(自动创建)
├── uploads/ # 上传文件目录
│ └── <ticket_id>/ # 每个工单独立的附件目录
├── templates/ # Jinja2 模板
│ ├── base.html # 基础布局模板
│ ├── login.html # 登录页
│ ├── register.html # 注册页
│ ├── dashboard.html # 统计看板
│ ├── ticket_list.html # 工单列表
│ ├── ticket_form.html # 新建/编辑工单
│ ├── ticket_detail.html # 工单详情
│ ├── notifications.html # 通知列表
│ └── error.html # 错误页
└── static/
└── style.css # 样式文件
数据模型
User(用户)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Integer | 主键 |
| username | String(80) | 用户名(唯一) |
| password_hash | String(200) | 密码哈希 |
| role | String(20) | 角色(admin/user) |
| created_at | DateTime | 创建时间 |
Ticket(工单)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Integer | 主键 |
| title | String(200) | 标题 |
| description | Text | 描述(支持 HTML) |
| status | String(20) | 状态 |
| priority | String(20) | 优先级 |
| category | String(20) | 分类 |
| assignee | String(100) | 处理人 |
| requester | String(100) | 提交人 |
| cc_users | String(500) | 抄送人(逗号分隔) |
| tags | String(500) | 标签(逗号分隔) |
| created_at | DateTime | 创建时间 |
| updated_at | DateTime | 更新时间 |
Comment(评论)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Integer | 主键 |
| ticket_id | Integer | 所属工单 |
| author | String(100) | 评论人 |
| content | Text | 评论内容 |
| created_at | DateTime | 创建时间 |
Attachment(附件)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Integer | 主键 |
| ticket_id | Integer | 所属工单 |
| filename | String(200) | 存储文件名(UUID) |
| original_name | String(300) | 原始文件名 |
| content_type | String(100) | MIME 类型 |
| size | Integer | 文件大小(字节) |
| is_image | Boolean | 是否为图片 |
| uploaded_by | String(100) | 上传人 |
| uploaded_at | DateTime | 上传时间 |
Notification(通知)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Integer | 主键 |
| user_id | Integer | 接收用户 |
| title | String(200) | 通知标题 |
| content | Text | 通知内容 |
| ticket_id | Integer | 关联工单 |
| read | Boolean | 是否已读 |
| created_at | DateTime | 创建时间 |
部署说明
systemd 服务部署
- 创建服务文件:
/etc/systemd/system/ticket-system.service
[Unit]
Description=Ticket System
After=network.target
[Service]
Type=exec
WorkingDirectory=/path/to/ticket-system
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
Environment="PYTHONUNBUFFERED=1"
ExecStart=/usr/local/bin/gunicorn --workers 2 --bind 0.0.0.0:5000 --access-logfile /var/log/ticket-system/access.log --error-logfile /var/log/ticket-system/error.log wsgi:app
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
- 创建日志目录
mkdir -p /var/log/ticket-system
- 启动服务
systemctl daemon-reload
systemctl enable --now ticket-system
systemctl status ticket-system
Nginx 反向代理(可选)
server {
listen 80;
server_name ticket.example.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /uploads/ {
alias /path/to/ticket-system/uploads/;
}
}
注意事项
- 首次启动:会自动创建默认管理员账号(admin/admin)
- 数据库迁移:新增字段需要手动添加到 SQLite 数据库
- 文件上传:需要确保
uploads/目录存在且可写 - 安全配置:生产环境请修改
SECRET_KEY和默认管理员密码 - 最大上传大小:默认 20 MB,可在
app.py中修改MAX_CONTENT_LENGTH
许可证
MIT License