1. 修理厂的真实痛点:为什么要给汽修店做一套管理系统
做这个项目之前,我先讲一个我在汽修厂蹲点调研时遇到的情况。老板手里有三个技师、一个前台,每天进出车辆差不多十二三台。前台用的是Excel记录工单,一台车来了先记一行,项目写在备注里,换没换机油、换的什么型号机油、工时费多少全靠记忆和聊天记录。结果月底对账的时候,发现配件库存和成本根本对不上,有的客户三个月前该做二保了也没人提醒,老客户流失了大半自己都不知道原因。
这个场景在中小型汽修门店里太普遍了。很多店不是不想管,是Excel和微信根本扛不住业务数据之间的关联关系。客户的车辆信息、历史维修记录、保养周期、配件库存、工时费用、员工提成,这些数据彼此纠缠,用表格去记就会变成一张越拉越长的"万能表",最后谁也看不懂。
所以当我打算做一个"基于Python和django的汽车维修保养管理系统"时,目的不是做一个炫技的Demo,而是把汽修门店真正每天都在发生的业务流程——接车、检测、报价、维修、配件出库、结算、回访提醒——完整落到系统里。这套系统做出来,前台录入工单、技师查看任务、老板查看营收和配件库存,各自有各自的门户,数据是实时联动而不是各记各的。
从技术实现角度看,这个项目的核心价值在于:它不是一个简单的增删改查,而是涉及状态流转、库存联动、权限控制、定时任务这些真实业务场景的综合系统。你把它写明白,等于把Web开发里80%的中级知识全部串起来了。适合的人群也很清晰:学完Django基础但没做过完整项目的开发者、汽修行业相关软件公司想要参考业务流程的新人,以及打算给自家门店做信息化的技术型老板。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型分析:为什么是Django而不是Flask、Spring Boot甚至微信小程序
2.1 Django在管理类系统中的三个杀手级优势
先直接说结论:如果你要做的是一套多用户、多角色、数据模型关联复杂的管理系统,Django是当前技术栈里效率最高的选择,没有之一。市面上虽然还有Flask、FastAPI、Spring Boot这些选择,但放在这个具体场景里,Django的优势是碾压级的。
第一个优势是自带的Admin后台。汽修管理系统里,车辆信息、配件型号、供应商资料这些数据都需要人工维护,Django的Admin后台可以直接把这些做成一堆可筛选、可搜索的表格页面,不需要额外写一行前端代码。我在实际开发中,内部的车辆品牌车系管理、配件分类管理,直接挂在Admin里让老板自己去维护,省了大量开发时间。
第二个优势是ORM的对象关系映射。汽修业务的数据关联很复杂——一辆车关联一个客户,一次工单关联一辆车和多个维修项目,一个维修项目又可能关联多个配件的出库记录。Django的ORM能像操作Python对象一样操作这些数据库关系,尤其是我用ForeignKey和ManyToManyField来建模时,查询效率高又容易理解。
第三个优势是自带的用户认证和权限体系。汽修门店里老板、店长、前台、技师,每个人能看能操作的完全不一样。Django自带的User模型加Permission,再配合Group(分组),几分钟就能把权限框架搭起来。我后面会专门讲这一块怎么做。
2.2 项目目录结构与核心配置拆解
我创建项目用的命令是:
bash复制django-admin startproject autoshop
cd autoshop
python manage.py startapp vehicle
python manage.py startapp maintenance
python manage.py startapp parts
python manage.py startapp workorder
这里要解释一下为什么要拆这么多app,而不是把所有的表都塞进一个app里。Django里的app本质上是一个功能模块的边界,拆得清楚,后面写代码才能保持脑子清楚。我的划分思路是——vehicle只管车辆档案和客户车主的关联,maintenance只管保养计划、保养提醒,parts只管配件库存和入库出库,workorder负责维修工单的生命周期。实际写的时候会发现,业务越往后长,这种模块化的拆分越能救你一命。
在settings.py里,有几个配置是必须注意的。首先是数据库配置,我直接用Django默认的SQLite来跑开发环境,因为这项目的核心目标是功能完整,没有高并发需求;但部署到生产环境时会切换到MySQL,配置如下:
python复制DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'autoshop_db',
'USER': 'autoshop_user',
'PASSWORD': 'your_password',
'HOST': '127.0.0.1',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4',
},
}
}
另一个容易被新手忽略的是媒体文件路径。汽车维修系统里涉及到车辆照片、维修单据拍照、工单附件,这些文件不能存数据库,得存到服务器的指定目录。配置文件写成:
python复制MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
在urls.py里补上对应的静态服务:
python复制from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
2.3 Django版本选择与Python环境准备
这个项目我是在Python 3.10版本的虚拟环境下开发的,Django用的是4.2 LTS版本。这里有个经验:学习项目不要追最新的大版本,一定选LTS。Django 4.2在2023年发布,官方支持周期至少到2026年,你在这基础上二次开发或者部署上线,资料好找、坑也基本被人踩完了。
虚拟环境这块,我强烈建议每个项目建独立的虚拟环境,不要用系统全局的Python跑Django项目。常见的创建流程:
bash复制python -m venv venv
source venv/bin/activate # Windows下是 venv\Scripts\activate
pip install django==4.2 mysqlclient pillow
pillow是处理图片上传必需的库,因为车辆照片、行驶证照片都要经过ImageField。mysqlclient这个库在Windows上安装容易报错,建议直接下载对应Python版本的wheel文件安装,而Linux服务器上直接pip install一般没问题。
3. 数据模型设计:维修业务的表和字段究竟怎么定
3.1 核心实体关系:客户、车辆、工单、配件、保养计划
在做数据模型之前,我先理了一下门店的业务流。一台车进店,首先得关联到车主。车主可能是个人,也可能是企业客户。车辆本身有品牌、车系、车牌号、VIN码、当前里程、上次保养里程这些信息。维修工单就是一次进店服务的过程记录,工单里要包含客户反馈的问题、技师检查结果、维修项目列表、配件使用情况、工时费、总金额和支付状态。同时,车辆根据车型和里程会有一个保养计划,到时间了就要触发提醒。
基于这些业务关系,我最终设计了下面这组模型,这组模型是整个系统的骨架,如果抄作业建议先从这个开始。
python复制# vehicle/models.py
from django.db import models
class Customer(models.Model):
name = models.CharField(max_length=50)
phone = models.CharField(max_length=20, unique=True)
wechat = models.CharField(max_length=50, blank=True)
address = models.CharField(max_length=200, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Vehicle(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='vehicles')
license_plate = models.CharField(max_length=20, unique=True, verbose_name='车牌号')
vin = models.CharField(max_length=50, blank=True, verbose_name='车架号')
brand = models.CharField(max_length=30, verbose_name='品牌')
model_name = models.CharField(max_length=50, verbose_name='车型')
year = models.IntegerField(null=True, blank=True, verbose_name='年款')
current_mileage = models.IntegerField(default=0, verbose_name='当前里程(km)')
last_maintenance_mileage = models.IntegerField(default=0, verbose_name='上次保养里程(km)')
last_maintenance_date = models.DateField(null=True, blank=True, verbose_name='上次保养日期')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.license_plate} - {self.brand} {self.model_name}"
工单模型需要在另外的app里定义,我单独讲一下状态字段。工单的状态是这个系统的灵魂,它的流转路径是:待接车 → 检修中 → 待报价 → 已确认 → 维修中 → 待结算 → 已完成 → 已取消。这个状态我不建议用简单的CharField加choices,而是单独用一个字段存,后面做状态流转会好控制得多,后面会细讲。
python复制# workorder/models.py
from django.db import models
from django.contrib.auth.models import User
from vehicle.models import Customer, Vehicle
class WorkOrder(models.Model):
STATUS_CHOICES = [
('pending', '待接车'),
('inspecting', '检修中'),
('quoted', '待报价'),
('confirmed', '已确认'),
('repairing', '维修中'),
('pending_payment', '待结算'),
('completed', '已完成'),
('cancelled', '已取消'),
]
order_no = models.CharField(max_length=32, unique=True, verbose_name='工单号')
customer = models.ForeignKey(Customer, on_delete=models.PROTECT, verbose_name='客户')
vehicle = models.ForeignKey(Vehicle, on_delete=models.PROTECT, verbose_name='车辆')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name='状态')
reported_issue = models.TextField(verbose_name='客户描述问题')
assigned_to = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='负责技师')
total_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name='总金额')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
这里有一个设计上的取舍值得多说一句:工单里的客户和车辆我用了ForeignKey而不是直接复制一份数据,这是对的。因为工单需要实时看到车辆的最新里程、客户的最新联系方式,如果用复制数据的方式,后面同步就会变成噩梦。但是因此带来的一个问题是:如果客户改了资料,历史工单显示的内容也会变化,这就引出了快照字段的做法——如果你需要留存某次修车时的里程和报价,就在工单里单独加字段存快照值,而不是查关联表。我们这次为了控制复杂度,先不做快照,但大家做商业项目时一定要考虑。
3.2 配件表与维修项目表的取舍
配件库存这块,我单独建了parts这个app。这里就涉及到一个业务理解:汽修店的配件不是简单的"一物一库存",而是同一款配件可能对上多个车型。举个例子,机油滤芯可能有丰田专用、本田专用或者通用型号,就算是同一个"机油滤清器"品类,型号不同价格差很多。所以配件表必须含品牌、型号、适用车型这些维度。
我的配件模型如下:
python复制# parts/models.py
from django.db import models
class Part(models.Model):
name = models.CharField(max_length=100, verbose_name='配件名称')
part_no = models.CharField(max_length=100, unique=True, verbose_name='配件编号')
brand = models.CharField(max_length=50, verbose_name='品牌')
model = models.CharField(max_length=50, blank=True, verbose_name='配件型号')
applicable_models = models.CharField(max_length=200, blank=True, verbose_name='适用车型')
stock_quantity = models.IntegerField(default=0, verbose_name='当前库存')
safety_stock = models.IntegerField(default=5, verbose_name='安全库存')
purchase_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='进货价')
selling_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='销售价')
updated_at = models.DateTimeField(auto_now=True)
维修项目表可以跟工单做成多对多的关系,中间表带一个数量字段。我这里的处理是额外建一个WorkOrderItem表,记录工单里每个维修项目的名称、工时费、配件小计和项目小计。这样月底统计业绩的时候,直接对WorkOrderItem做聚合,能单独看出工时费赚了多少、配件赚了多少。
3.3 几个关键字段的取舍与踩坑经验
字段设计上,我踩过几个坑值得写出来。第一个是里程字段的类型。一开始我用的IntegerField,但实际业务里里程如果超过21亿公里才会溢出,家用车根本不可能,所以IntegerField完全够用。但是要注意,如果是从第三方设备读取里程,可能会有小数,这里建议直接用IntegerField加round处理,别用FloatField存钱一样的道理。
第二个是金额字段必须用DecimalField而不是FloatField。Python的浮点数有精度问题,你在MySQL里用Float存99.99,查出来可能是99.98999999999999,对账的时候会让人崩溃。价格相关的字段必须DecimalField(max_digits=10, decimal_places=2),到分。
第三个是电话号码的唯一约束。Customer.phone我加了unique=True,从业务角度说一个手机号对应一个客户,这没问题。但要注意汽修门店可能会遇到同一辆车来了两个人(夫妻俩),这时候应该通过新增关联表去解耦,而不是放开唯一约束。这个属于业务建模的边界,我们这次按简单场景处理,一个手机号一个客户。
4. 核心功能实现:从零到跑通整个业务闭环
4.1 维修工单的状态流转是一个典型状态机
工单管理的核心逻辑是状态流转,我在代码里实现了一个简单的状态机。一开始大家可能会想,直接在视图函数里用if判断状态然后修改就可以了,但很快你就会发现状态多了之后,if判断写得你头昏脑涨。我推荐用一个状态转移表来管理。
python复制# workorder/services.py
from django.core.exceptions import ValidationError
ALLOWED_TRANSITIONS = {
'pending': ['inspecting', 'cancelled'],
'inspecting': ['quoted', 'pending_payment', 'cancelled'],
'quoted': ['confirmed', 'cancelled'],
'confirmed': ['repairing', 'pending_payment', 'cancelled'],
'repairing': ['pending_payment', 'completed', 'cancelled'],
'pending_payment': ['completed'],
'completed': [],
'cancelled': [],
}
def transition_workorder(order, new_status):
if new_status not in ALLOWED_TRANSITIONS.get(order.status, []):
raise ValidationError(f'工单当前状态为{order.get_status_display()},不能变更为{new_status}')
order.status = new_status
order.save()
return order
这种集中式的状态转移表优势很明确:所有状态跳转的逻辑都在一个地方,想排查问题只需要看这一张表。视图函数里调用transition_workorder时,如果状态不合法直接抛异常,前端提示也方便。
实际页面里的操作按钮,我是根据工单当前状态动态渲染的。比如工单在"待接车"状态,只显示"开始检修"和"取消"两个按钮;到了"待报价",显示"确认报价""补充项目""取消"三个按钮。这样技师不会乱点,业务不容易出错。
4.2 保养提醒:利用自定义命令实现自动通知
保养提醒是汽修店的老大难。很多店舍不得用专业的CRM系统,因为贵也不一定匹配。我自己实现的这个功能,逻辑非常清晰,按时间或者按里程触发两种规则:一种是距上次保养满6个月或5000公里,另一种是当前里程减去上次保养里程超过设定阈值就提醒。
实现方式是用Django的自定义management command,先在app下建一个management/commands目录,然后写一个check_maintenance.py,代码如下:
python复制# maintenance/management/commands/check_maintenance.py
from datetime import timedelta
from django.core.management.base import BaseCommand
from django.utils import timezone
from vehicle.models import Vehicle
class Command(BaseCommand):
help = '检查需要保养的车辆,并生成提醒记录'
def handle(self, *args, **options):
today = timezone.now().date()
# 按时间触发:上次保养距今超过180天
time_threshold = today - timedelta(days=180)
vehicles_time = Vehicle.objects.filter(
last_maintenance_date__lte=time_threshold
).exclude(last_maintenance_date__isnull=True)
# 按里程触发:当前里程 - 上次保养里程 >= 5000公里
vehicles_mileage = Vehicle.objects.filter(
current_mileage__gte=5000
).exclude(last_maintenance_mileage__isnull=True)
vehicles_mileage = [v for v in vehicles_mileage
if v.current_mileage - v.last_maintenance_mileage >= 5000]
total = len(vehicles_time) + len(vehicles_mileage)
self.stdout.write(self.style.SUCCESS(f'共检测到{total}台车辆需要保养提醒'))
脚本跑出来的结果会写入一张MaintenanceReminder表,里面记录车辆、客户、提醒类型、提醒状态。前台员工看到待处理的提醒列表后,可以一键给客户发短信或者打电话,然后再把提醒标记为"已处理"。这里要说清楚:定时任务本身Django不做,我是在服务器上配Cron定时执行python manage.py check_maintenance,每天上午9点跑一次,这个方案最省事,不需要额外引入Celery。以后如果提醒量大了,再上Celery,但中小门店完全没必要。
4.3 库存台账:配件出库与回库的原子性设计
配件库存是最容易出问题的模块,尤其是并发情况下——多个工单同时出库,库存数量可能变负数。所以我这里花了比较大的篇幅处理这个问题。
出库操作必须放在一个事务里,同时锁定对应库存记录。Django里用select_for_update()来实现行级锁,这样可以防止两个人同时给同一个配件出库导致库存扣成负数。
python复制# parts/services.py
from django.db import transaction
from django.db.models import F
from django.core.exceptions import ValidationError
from .models import Part
@transaction.atomic
def stock_out(part_id, quantity):
if quantity <= 0:
raise ValidationError('出库数量必须大于0')
part = Part.objects.select_for_update().get(id=part_id)
if part.stock_quantity < quantity:
raise ValidationError(f'配件{part.name}库存不足,当前库存{part.stock_quantity}')
part.stock_quantity = F('stock_quantity') - quantity
part.save()
return part
这里有两个细节值得注意。第一是select_for_update()必须在事务内才能生效,所以函数头上直接加了@transaction.atomic。第二是用了F('stock_quantity')做原子更新,而不是先读出来在Python里算好再赋值,这样可以避免并发下的竞态条件。
有出库就有回库。如果维修计划取消或者配件装错了要退回,对应的回库操作也要做。而且业务上有个要求:回库的配件必须能追踪到是哪个工单退的,所以会在StockLog表里记录关联的工单号和操作类型。这样月底盘库存的时候,每一条出入记录都能对上号,账实相符。我后来在实际项目的月末对账中发现,几乎所有差异都源于"无记录出库"——员工直接拿了配件没开单,所以系统里必须强制所有的出入库都通过工单联动。
4.4 首页仪表盘与营收统计
系统没有数据统计就等于白做。我在首页仪表盘上放了四个核心指标:今日进店台数、今日营收、当前待维修工单数、低库存配件数。这背后是几个简单的聚合查询:
python复制from django.db.models import Sum
from django.utils import timezone
today = timezone.now().date()
today_orders = WorkOrder.objects.filter(created_at__date=today)
today_revenue = today_orders.filter(status='completed').aggregate(
total=Sum('total_amount')
)['total'] or 0
low_stock_parts = Part.objects.filter(stock_quantity__lte=F('safety_stock'))
这些统计不是实时刷新的,而是每次打开首页的时候实时查一次数据库。数据量小时这个方案完全没问题,但如果后期工单表到了几十万条,就需要把汇总结果做成一张缓存表或者用Redis做缓存。项目初期保持简单是最重要的原则。
5. 权限与多角色界面:老板、店长、技师各看各的
5.1 基于Django自带的Group和Permission做角色控制
这个系统上线之后,门店角色一共有四类:老板、店长、前台、技师。他们操作权限差异很大:
- 老板:只看统计分析,不操作具体工单,能看所有门店的营收。
- 店长:管理全部工单、审核报价、修改配件价格、查看报表、管理员工。
- 前台:录入客户和车辆信息、创建工单、结算收款。
- 技师:查看分配给自己的工单、填写维修项目和工时、标记完成。
Django的User模型自带is_staff和is_superuser,但这两个不够用。我采用的方式是创建三个Group:Manager(店长)、FrontDesk(前台)、Technician(技师),老板直接给is_superuser。然后在每个Group里分配权限:
python复制from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from workorder.models import WorkOrder
manager_group, _ = Group.objects.get_or_create(name='Manager')
ct = ContentType.objects.get_for_model(WorkOrder)
perm_change_order = Permission.objects.get(codename='change_workorder', content_type=ct)
manager_group.permissions.add(perm_change_order)
前端页面用模板里的权限判断来控制按钮是否展示。比如在工单详情页的模板里:
python复制{% if perms.workorder.can_approve_quote %}
<a href="{% url 'workorder:approve_quote' order.id %}" class="btn btn-primary">确认报价</a>
{% endif %}
需要说明的是,Django自带的Permission能覆盖大部分场景,但如果你想做更细的规则(比如技师只能看分配给自己的工单、前台不能改价格),就需要在视图函数里做二次过滤。这类"数据级权限"Django原生不提供,我在视图里用了一个通用套路:
python复制if request.user.groups.filter(name='Technician').exists():
orders = orders.filter(assigned_to=request.user)
这个模式简单直接,避免了引入django-guardian这种重量级库。门店业务规模有限,用户的角色很少变更,性能完全够。
6. 服务器部署与常见坑排查
6.1 宝塔面板部署Django项目完整步骤
部署这块我必须说,Django项目跑到云服务器上,实际操作中几乎每个人都会踩一遍坑。我把自己在Linux上部署的完整流程写在这里,照着做就能少走弯路。
服务器我用的是Ubuntu 22.04,配合宝塔面板做站点和MySQL的管理。Python环境的安装最好用系统的包管理器,但注意要装python3-venv,否则后面创建虚拟环境会失败。
bash复制apt update
apt install python3 python3-pip python3-venv nginx -y
代码传到服务器上之后,在项目目录里创建虚拟环境并安装依赖:
bash复制cd /www/wwwroot/autoshop
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install gunicorn mysqlclient
依赖文件requirements.txt是在本地生成的,具体内容大约是这样:
code复制django==4.2
mysqlclient==2.2.0
pillow==10.0.0
gunicorn==21.2.0
然后收集静态文件、执行迁移、创建超级管理员:
bash复制python manage.py collectstatic --noinput
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
用Gunicorn启动Django应用:
bash复制gunicorn autoshop.wsgi:application --bind 127.0.0.1:8000 --workers 3 --daemon
在宝塔面板里新建一个站点,把运行目录指向项目根目录,然后在站点配置里加反向代理:
nginx复制location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
alias /www/wwwroot/autoshop/static/;
}
location /media/ {
alias /www/wwwroot/autoshop/media/;
}
最后在settings.py里把ALLOWED_HOSTS加上你的域名,DEBUG设为False,然后把SECRET_KEY换成一个新的随机字符串。
6.2 部署后最容易踩的几个坑
坑一:静态文件404。这个太经典了,几乎每个新手都会遇到。原因是一个是忘了collectstatic,另一个是nginx配置里alias路径写错。检查顺序是:先看Django的STATIC_ROOT目录下有没有文件,再看nginx的alias路径是否存在,最后确认nginx配置后的nginx -t和service nginx reload。
坑二:部署后报DisallowedHost错误。这是ALLOWED_HOSTS没有加你的域名或者服务器IP,在settings.py里把域名加进去就好。开发时如果你只用IP访问,直接写ALLOWED_HOSTS = ['*'],生产环境建议严格写域名。
坑三:数据库编码问题导致中文乱码。建库时一定要指定UTF-8:在宝塔的phpMyAdmin里新建数据库时选择utf8mb4排序规则,而不是默认的latin1。然后在settings.py里配置OPTIONS: {'charset': 'utf8mb4'}。
坑四:Gunicorn进程在SSH断开后自动退出。这是因为没有用--daemon参数或者没有用supervisor守护。建议直接用Gunicorn的--daemon参数,或者全项目用supervisor管理进程,崩溃后自动拉起,省心很多。
6.3 数据库备份与日常运维
系统上线后,最怕的不是代码bug,而是数据库丢了。我强烈建议每天凌晨自动备份数据库,保留最近7天即可。在服务器上用Cron执行:
bash复制0 2 * * * mysqldump -u autoshop_user -p'密码' autoshop_db > /www/backup/autoshop_$(date +\%Y\%m\%d).sql
备份策略用最简单的方式就够:每天一条SQL文件,保留7天。之后想恢复,执行mysql -u autoshop_user -p autoshop_db < backup.sql就行。
7. 性能优化与功能扩展方向
系统跑起来之后,我回顾了一遍,发现几个可以继续优化的点。数据库层面,随着工单表数据量增长,一定要给外键和常用的查询字段加索引。在Django的模型Meta类里:
python复制class WorkOrder(models.Model):
# ...
class Meta:
indexes = [
models.Index(fields=['status']),
models.Index(fields=['created_at']),
models.Index(fields=['customer', 'status']),
]
数据量到十万级以上时,首页的Sum聚合查询会变慢,可以在每天凌晨用Cron把前一天汇总好的统计写入一张统计表,页面读统计表而不是实时聚合,体验立刻提升。
功能扩展方向上,我建议按这个优先级做:第一是接短信接口,保养提醒和工单进度通知直接发短信触达客户;第二是做微信小程序端的客户自助查询,车主能自己看维保记录、预约保养,这个对门店品牌提升特别大;第三是接第三方的汽车配件数据接口,比如通过VIN码自动识别车型并推荐匹配配件,能明显降低接待人员的操作门槛。
每个方向都有足够的深度去延伸。我第一次做完这套系统之后,最大的感受是:这个项目的难度不在于某个单一技术点,而在于如何把一堆互相有关联的业务模块,在有限时间内组织成一个逻辑自洽的整体。这个能力,恰恰是做项目最值钱的部分。
