第1章:数据库设计与建模——工单表结构设计与SQLite集成

各位同学,欢迎来到《客户服务工单系统开发实战》的第一章。

今天咱们要聊的,是整个系统的地基——数据库设计。说白了,就是工单长什么样,数据怎么存。我做了这么多年项目,见过太多系统上线后改表结构的惨案。所以这一章,咱们把地基打牢。

1.1 工单表结构设计——先想清楚再动手

工单系统最核心的,就是工单本身。你想想看,一张工单需要记录什么?

我个人习惯,先列业务需求,再定字段。一个客服工单,至少要有:

  • 唯一标识:ticket_id,每张工单的身份证
  • 标题:title,用户一眼能看到的问题概要
  • 描述:description,详细内容,可能很长
  • 状态:status,比如待处理、处理中、已关闭
  • 优先级:priority,紧急、高、中、低
  • 时间戳:create_time 和 update_time,记录生命周期

嗯,这里要注意。我在项目中遇到过,有人把时间戳只存了一个,结果排查问题时根本不知道什么时候更新的。所以,创建时间和更新时间,一个都不能少。

来看一下具体的字段设计表:

字段名 类型 说明 约束
ticket_id INTEGER 工单唯一编号 主键,自增
title VARCHAR(200) 工单标题 非空
description TEXT 工单详细描述 可空
status VARCHAR(20) 当前状态 默认 'open'
priority VARCHAR(10) 优先级 默认 'medium'
create_time DATETIME 创建时间 自动生成
update_time DATETIME 最后更新时间 自动更新

核心要点:status 和 priority 建议用字符串,而不是数字。为什么?因为代码里一眼就能看出含义,不用查字典。我曾经接手过一个项目,状态用 0、1、2 表示,每次都要翻文档,太痛苦了。

1.2 SQLite数据库创建——轻量级的选择

为什么选 SQLite?说白了,咱们这是教学项目,不想让大家折腾安装 MySQL 或 PostgreSQL。SQLite 就是一个文件,零配置,开箱即用。

但别小看它。我在一些小工具、原型验证项目里,经常用 SQLite。性能足够,而且部署简单。等系统真到了需要高并发的阶段,再迁移到 MySQL 也不迟。

创建 SQLite 数据库,其实就一行命令的事。但咱们用 Flask-SQLAlchemy,会更优雅。

小提示:SQLite 的数据库文件默认会生成在你的项目根目录下。建议在 config.py 里配置路径,方便后续迁移。

1.3 Flask-SQLAlchemy集成——让数据库操作变简单

Flask-SQLAlchemy 是 Flask 和 SQLAlchemy 的桥梁。SQLAlchemy 是 Python 里最强大的 ORM 框架,没有之一。ORM 是什么?就是让你用 Python 对象操作数据库,不用写原生 SQL。

我刚开始学的时候也觉得 ORM 多此一举,直到有一次要改表结构,几十条 SQL 语句要同步修改……嗯,从那以后我就老老实实用 ORM 了。

来看集成步骤:

  1. 安装依赖包
  2. 配置数据库 URI
  3. 初始化 SQLAlchemy 对象
  4. 定义模型类
  5. 创建表

先安装必要的包:

pip install flask flask-sqlalchemy

然后在 app.py 中配置:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)
# 配置数据库路径,/// 表示相对路径
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tickets.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

接下来,定义我们的工单模型。这就是把刚才的表结构,用 Python 类表达出来:

class Ticket(db.Model):
    __tablename__ = 'tickets'
    
    ticket_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(200), nullable=False)
    description = db.Column(db.Text, nullable=True)
    status = db.Column(db.String(20), default='open')
    priority = db.Column(db.String(10), default='medium')
    create_time = db.Column(db.DateTime, default=datetime.utcnow)
    update_time = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    
    def __repr__(self):
        return f'<Ticket {self.ticket_id}: {self.title}>'

注意:onupdate=datetime.utcnow 这个参数,很多人会漏掉。它的作用是:每次更新记录时,自动把 update_time 改成当前时间。没有它,你就得手动维护这个字段,很容易出错。

最后,创建表。在 Python 交互环境或应用启动时执行:

with app.app_context():
    db.create_all()

执行完后,你会看到项目目录下多了一个 tickets.db 文件。没错,这就是你的数据库。

1.4 验证一下——写点测试数据

光建表不行,得试试能不能用。我习惯写个小脚本验证:

from app import app, db, Ticket
from datetime import datetime

with app.app_context():
    # 创建一张测试工单
    ticket = Ticket(
        title='无法登录系统',
        description='用户反馈输入密码后提示"账号异常",已尝试重置密码无效',
        status='open',
        priority='high'
    )
    db.session.add(ticket)
    db.session.commit()
    
    # 查询所有工单
    tickets = Ticket.query.all()
    for t in tickets:
        print(f'工单 #{t.ticket_id}: {t.title} - {t.status}')

运行后,如果看到输出 工单 #1: 无法登录系统 - open,恭喜你,数据库已经跑通了。

避坑指南:我曾经犯过一个低级错误——在创建表之前忘了执行 with app.app_context(),结果报错说「没有应用上下文」。记住,Flask-SQLAlchemy 的所有操作,都需要在应用上下文中进行。

1.5 本章小结

这一章,咱们完成了三件事:

  • 设计了工单表的核心字段结构
  • 创建了 SQLite 数据库文件
  • 集成了 Flask-SQLAlchemy,并定义了 Ticket 模型

说白了,就是给工单系统搭好了数据库架子。下一章,咱们会在这个架子上,搭建 RESTful API,让前端能真正操作这些数据。

记住一句话:数据库设计是系统的灵魂。现在多花一分钟想清楚,后面能省一小时改代码。

好,这一章就到这儿。有问题随时交流。