news 2026/9/15 17:11:21

医院挂号系统实战:Django+MySQL+Redis四层架构详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
医院挂号系统实战:Django+MySQL+Redis四层架构详解

简介:一套基于Python Django框架、搭配MySQL与Redis的医院挂号系统源码,面向正在学习Web开发的学生、初级开发者及需要快速搭建课设或毕设项目的群体。系统完整实现患者端(注册登录、按科室/医生/时间挂号、填写病情、支付宝支付、挂号单展示)、医生端(登录并处理挂号单)以及管理员后台的数据增删查改,覆盖典型预约挂号业务全流程。压缩包共收录73个文件,以Python源码为核心(21个py),同时包含前端页面模板(13个html)、样式文件(14个css)、交互脚本(3个js)、项目配置与数据迁移文件等,整体大小223KB,目录结构按Django项目规范组织,便于直接运行与二次开发。已有1001人学习下载。借助这份源码,读者可深入理解Django MTV架构、ORM模型设计、Redis缓存应用、支付接口集成思路,以及多角色权限控制等关键知识点;文件中的静态资源与媒体目录配置也提供了完整的前后端协作参考,适合作为课程设计或实战训练的基础工程。

1. 为什么医院挂号要 Python、Django、MySQL、Redis 四层配合

早上八点的三甲医院放号场景,同一秒几百个请求冲进来抢同一个专家号源。MySQL 的行锁在那一刻会迅速堆积,接口响应从几十毫秒变成十几秒,Django 的数据库连接池先被打满,随后整个服务开始超时重试。挂号系统的难点从来不是增删改查,而是把号源扣减在这个峰值并发窗口里做成原子操作。市面上的医院挂号系统源码包基本采用同一套技术组合:Python 写业务逻辑,Django 做 Web 框架和 ORM,MySQL 存科室、医生、号源和预约单,Redis 负责查询缓存和号源的并发扣减。下面按这个组合里最常见的实现思路,把数据建模、Redis 原子扣减、MySQL 慢查询优化和部署验证四块逐一讲清楚,适合正在做 Django 项目、想完整复现挂号闭环的后端开发。

2. Django + MySQL:先把挂号业务的数据模型立住

挂号系统的业务流本身不复杂:用户选科室、看医生、看某天的剩余号,确认后生成一条预约记录。真正的复杂度集中在“剩余号数”会被高频修改,每修改一次都必须和预约单的创建在同一个事务边界内完成。我习惯先把 MySQL 表结构草拟出来,再倒过去写 Django Model,因为 DDL 阶段就能把索引、唯一键、字符集这类问题暴露出来。

2.1 建库建表:挂号最小数据集的 MySQL 字段设计

一个最小可跑的挂号系统至少需要四张表:dept 科室表、doctor 医生表、schedule 号源表、appointment 预约表。下面是起步阶段的建表语句。

CREATE DATABASE IF NOT EXISTS hospital DEFAULT CHARACTER SET utf8mb4; CREATE TABLE dept ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, intro TEXT ) ENGINE=InnoDB; CREATE TABLE doctor ( id INT PRIMARY KEY AUTO_INCREMENT, dept_id INT NOT NULL, name VARCHAR(50) NOT NULL, title VARCHAR(30), INDEX idx_dept (dept_id) ) ENGINE=InnoDB; CREATE TABLE schedule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, doctor_id INT NOT NULL, work_date DATE NOT NULL, total_num INT NOT NULL DEFAULT 20, remain_num INT NOT NULL DEFAULT 20, version INT NOT NULL DEFAULT 0, UNIQUE KEY uk_doctor_date (doctor_id, work_date), INDEX idx_remain (remain_num) ) ENGINE=InnoDB; CREATE TABLE appointment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, schedule_id BIGINT NOT NULL, appoint_no VARCHAR(32) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT '0=已预约 1=已取消 2=已完成', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_schedule (schedule_id), INDEX idx_user (user_id), UNIQUE KEY uk_appoint_no (appoint_no) ) ENGINE=InnoDB;

这段 DDL 里有三个设计点要解释清楚。第一个是 schedule 表上的 uk_doctor_date 联合唯一键,它保证同一医生同一天只存在一行号源记录,这是防止超卖的第一道数据库约束。第二个是 version 字段,它不做业务展示,只用来做乐观锁计数,扣减号源时 where 条件带上 version 可以避免脏写。第三个是 appointment.appoint_no 的唯一键,这个编号接管了幂等控制,同一个下单请求重复提交时,数据库层直接报唯一键冲突,业务代码捕获异常即可。字符集统一用 utf8mb4,否则用户姓名里的生僻字会直接写入失败。另外,doctor 表没有建物理外键,而是用普通索引关联 dept,因为 InnoDB 的物理外键在并发写入时会扩大锁范围,生产环境通常用应用层保证关联正确性,表结构只保留逻辑关联。

表名核心字段索引/约束在挂号链路上的作用
deptid, name, intro主键 id科室字典,列表页筛选依据
doctorid, dept_id, name, title索引 dept_id医生档案,关联排班
scheduleid, doctor_id, work_date, total_num, remain_num, version唯一(doctor_id, work_date),普通索引 remain_num每日号源与剩余号
appointmentid, user_id, schedule_id, appoint_no, status, created_at唯一 appoint_no,索引 schedule_id/user_id用户订单与退号状态

2.2 用 Django Model 定义科室、医生、号源和预约单

对应上面的 MySQL 表,models.py 可以这样写。这里没有做任何自定义管理器,字段名、类型和 DDL 保持一一对应。

from django.db import models class Dept(models.Model): name = models.CharField(max_length=50) intro = models.TextField(blank=True) class Doctor(models.Model): dept = models.ForeignKey(Dept, on_delete=models.CASCADE) name = models.CharField(max_length=50) title = models.CharField(max_length=30, blank=True) class Schedule(models.Model): doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE) work_date = models.DateField() total_num = models.IntegerField(default=20) remain_num = models.IntegerField(default=20) version = models.IntegerField(default=0) class Meta: constraints = [ models.UniqueConstraint( fields=['doctor', 'work_date'], name='uk_doctor_date' ) ] class Appointment(models.Model): STATUS_CHOICES = ( (0, '已预约'), (1, '已取消'), (2, '已完成'), ) user = models.ForeignKey('auth.User', on_delete=models.CASCADE) schedule = models.ForeignKey(Schedule, on_delete=models.CASCADE) appoint_no = models.CharField(max_length=32, unique=True) status = models.SmallIntegerField(choices=STATUS_CHOICES, default=0) created_at = models.DateTimeField(auto_now_add=True)

Model 里的 constraints 数组对应 DDL 的 uk_doctor_date,appoint_no 的 unique=True 对应 uk_appoint_no。Django 的 ForeignKey 默认会建物理外键,如果 DBA 不允许物理外键,可以在迁移时手动设置 db_constraint=False,只保留普通索引。status 字段用 SmallIntegerField 加 choices,不要存中文枚举,否则过滤和排序都要走字符比较,性能和可读性都差。user 字段直接关联 Django 自带 auth.User,省去单独建用户表的工作,后续对接 JWT 或 session 都很方便。

2.3 号源扣减的事务边界:哪些逻辑必须在一个原子操作里

项目里最常见的错误是把“判断剩余号数”和“插入预约单”拆到两个视图里。第一步查了 remain_num,第二步插入订单前,另一个请求已经把号扣走,于是出现超卖。事务边界必须把两步焊在一起,并用 select_for_update 锁住 schedule 行。

from django.db import transaction @transaction.atomic def create_appointment(user, schedule_id): schedule = Schedule.objects.select_for_update().get(pk=schedule_id) if schedule.remain_num <= 0: raise ValueError('号源已约满') schedule.remain_num -= 1 schedule.save(update_fields=['remain_num']) return Appointment.objects.create( user=user, schedule=schedule, appoint_no=generate_appoint_no(), )

select_for_update 在 InnoDB 下走主键行锁,同一行记录上的并发请求会排队执行,事务提交后释放。这个方案能保证正确,但吞吐量有限,放号高峰会把 MySQL 的行锁队列拉得很长。后续要配合 Redis 把大多数请求在数据库外面先拦一道。这里还要注意,事务函数内部不要调第三方接口,短信、推送这些动作要放到事务提交之后,通过消息队列异步消费,把 MySQL 事务的停留时间控制在十毫秒以内,是整个写路径的优化目标。

3. Redis 接管号源库存:从缓存到原子扣减的完整做法

挂号系统的流量特征是“读多写少但写集中在放号瞬间”。如果让所有请求都打到 MySQL,数据库连接会被先打满。Redis 在这种架构里承担两层角色:读取端的查询缓存,写入端的号源原子扣减。下面这套做法在源码包里最常见,也是面试里最常被追问的落地细节。

3.1 号源查询先走 Redis:缓存 Key 设计与穿透预防

挂号系统里最高频的接口是“某医生某天还剩多少号”,这个查询完全可以被 Redis 挡住。它用到的 Redis 数据类型是 String,Key 格式我统一写成schedule:remain:{schedule_id}。查询逻辑分两步:先读缓存,缓存 miss 才回源 MySQL 并回填。这一步最需要防的是缓存穿透,也就是大量请求带着不存在的 schedule_id 打过来,Redis 没数据,MySQL 被拖死。

import redis r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True) def get_remain_num(schedule_id): cache_key = f'schedule:remain:{schedule_id}' val = r.get(cache_key) if val is not None: return int(val) from app.models import Schedule try: s = Schedule.objects.get(pk=schedule_id) # 回填缓存并设置 TTL,nx=True 防止并发回源互相覆盖 r.set(cache_key, str(s.remain_num), ex=60, nx=True) return s.remain_num except Schedule.DoesNotExist: # 空值缓存 10 秒,挡住不存在 ID 的重复穿透 r.set(cache_key, '-1', ex=10, nx=True) return -1

这里三个参数很关键。ex=60 表示 60 秒过期,窗口期内 MySQL 最多被回源一次;nx=True 是 setnx,只在 Key 不存在时写入,避免多个线程同时回源后互相覆盖;空值缓存把不存在的 ID 也缓存 10 秒,调用方拿到 -1 后直接提示“号源不存在”。TTL 的长短要跟着后台排班修改频率走,如果运营后台允许临时改号源数,TTL 缩到 20 到 30 秒更保险。

Redis 操作命令/脚本使用场景
读剩余号GET schedule:remain:1001列表页展示号源
原子扣减EVAL DECR_SCRIPT下单时预占号源
回补号源EVAL INCR_SCRIPT取消订单时释放
直接失效DEL schedule:remain:1001后台修改排班后清缓存

3.2 用 Redis Lua 脚本扣号:把“读-改-写”压缩成一步

仅仅用 GET 再 SET 扣减号源,会出现两个进程同时读到 1,又各自写回 0,多扣了一次。Redis 的 Lua 脚本在单线程里执行,脚本中间不会被其他命令插入,因此可以用它实现真正的原子扣减。

DECR_SCRIPT = """ if tonumber(redis.call('get', KEYS[1])) <= 0 then return -1 end return redis.call('decr', KEYS[1]) """ def try_dec_remain(schedule_id): cache_key = f'schedule:remain:{schedule_id}' val = r.get(cache_key) if val is None: s = Schedule.objects.filter(pk=schedule_id).only('remain_num').first() if s is None: return -1 r.set(cache_key, str(s.remain_num), ex=60, nx=True) script = r.register_script(DECR_SCRIPT) return int(script(keys=[cache_key]))

脚本第一行先检查剩余数,小于等于 0 返回 -1,否则执行 decr 扣减。register_script 把脚本注册进连接对象,后续调用省去每次传递脚本源码的开销。返回值就是扣减后的剩余数,调用方拿到 -1 就知道号源已约满。这里要特别说明:扣的只是 Redis 里的数字,还没有落 MySQL,所以它只能当预占,不能替代订单。真正生成订单要靠下一节的 MySQL 落库。

3.3 预占成功后 MySQL 怎么落库:双写不一致怎么处理

Redis 扣减成功后要立刻写 MySQL。如果 MySQL 落库失败,必须回补号源,否则用户端看到“预约失败”,号源却被消耗掉了。我一般把下单写库拆成两步:条件 update 扣 MySQL 号源,成功后再插入预约单。

try: with transaction.atomic(): updated = Schedule.objects.filter( pk=schedule_id, remain_num__gt=0 ).update( remain_num=models.F('remain_num') - 1, version=models.F('version') + 1 ) if updated == 0: rollback_remain(schedule_id) raise ValueError('号源不足') Appointment.objects.create( user=user, schedule_id=schedule_id, appoint_no=generate_appoint_no() ) except Exception: rollback_remain(schedule_id) raise

update 配合 F 表达式构造 SQL 层原子更新,不用先把行读进 Django。updated 返回受影响行数,为 0 就说明 MySQL 的 remain_num 已经是 0,触发回补。rollback_remain 是 INCR_SCRIPT,把 Redis 里的数字加 1。这种做法在极端情况下会让 Redis 和 MySQL 短暂不一致,但最终以 MySQL 为准,缓存过期后自然回源纠正。如果业务要求强一致,就得把扣减逻辑全部放进 MySQL 存储过程或单事务,但存储过程里的行锁和事务边界一样难扩展,Redis 方案本质上是用最终一致换吞吐量。

注意:Redis 扣减返回成功只表示号源被临时占用,MySQL 落库失败后必须执行回补,否则会出现前端提示“预约失败”,号源却被消耗掉的假象。

4. Django 业务代码落地:预约下单、取消订单与退号

python django 搭建 web 项目时,新建 app 之后业务逻辑一般集中在 views 和 services 两个文件。挂号系统真正的难点不是 models.py 怎么写,而是预约、取消、退号三条链路怎么不互相干扰。下面把三条链路的代码路径串一遍,顺便处理两个高频踩坑点。

4.1 预约下单:Redis 先扣,MySQL 后落库的两阶段交付

下单接口把逻辑拆成两段:第一阶段在 Redis 里扣减号源,第二阶段写 MySQL 生成预约单。成功路径和补偿路径都要明确。

def book_appointment(user, schedule_id): if not Schedule.objects.filter(pk=schedule_id).exists(): return {'code': 404, 'msg': '号源不存在'} remain = try_dec_remain(schedule_id) # 第一阶段:Redis 原子扣减 if remain == -1: return {'code': 400, 'msg': '已约满'} try: with transaction.atomic(): # 第二阶段:MySQL 落库 updated = Schedule.objects.filter( pk=schedule_id, remain_num__gt=0 ).update( remain_num=models.F('remain_num') - 1, version=models.F('version') + 1 ) if updated == 0: raise ValueError('schedule locked') order = Appointment.objects.create( user=user, schedule_id=schedule_id, appoint_no=generate_appoint_no() ) except Exception: rollback_remain(schedule_id) # 补偿:把 Redis 加回去 return {'code': 500, 'msg': '下单失败'} cache.delete(f'schedule:remain:{schedule_id}') # 清缓存,等待回源 return {'code': 0, 'order_id': order.id}

第一阶段的核心是 try_dec_remain,它用 Lua 脚本扣减 Redis 里的剩余数,返回 -1 直接结束。第二阶段用条件 update 落 MySQL,受影响行为 0 或抛异常都走补偿,把 Redis 回补。最后删除缓存,让剩余号下次查询时从 MySQL 重新回源。这样做的好处是:Redis 挡掉绝大多数无效请求,MySQL 只处理真正下单的请求,事务短且锁竞争小。generate_appoint_no 建议用时间戳加随机数加用户 ID 拼接,保证全局唯一,长度控制在 32 位以内。

4.2 取消订单的正确姿势:状态更新而不是物理删除

取消订单是把预约单状态从 0 改成 1,同时把号源加回去。很多源码用 Appointment.objects.filter(...).delete() 直接删行,这是最典型的错误:号源没回补,历史数据也没了。django 执行查询-删除对象这类操作,在挂号这种有排队语义的场景里一定不能直接删除。正确做法是用状态字段标记取消,并在同一事务内回补号源。

@transaction.atomic def cancel_appointment(user, appoint_no): order = Appointment.objects.select_for_update().filter( appoint_no=appoint_no, user=user ).first() if order is None or order.status != 0: raise ValueError('订单不存在或已处理') order.status = 1 order.save(update_fields=['status']) Schedule.objects.filter(pk=order.schedule_id).update( remain_num=models.F('remain_num') + 1, version=models.F('version') + 1 ) cache.delete(f'schedule:remain:{order.schedule_id}')

select_for_update 先锁住订单行,避免同一订单被并发取消两次。update_fields 只更新 status 字段,避免 Django 默认把所有字段重写一遍。号源回补用 F 表达式,在数据库层做原子加 1,不用把旧值读出来再写。最后删缓存。退号的流程和取消类似,只是把 status 改成 2,并且可能要记录退号时间和操作人,可以在 Appointment 上再加 canceled_at 和 cancel_user 两个字段。

4.2.1 用 Django admin 快速核对订单状态

开发期和排查期打开 admin 页面看订单状态,比写 SQL 更直观。这也是 django admin 界面美化与实用化的起点:注册 Appointment 到 admin 之后,用 list_display 控制列表列,用 list_filter 提供状态筛选,用 search_fields 支持订单号搜索。

from django.contrib import admin from app.models import Appointment @admin.register(Appointment) class AppointmentAdmin(admin.ModelAdmin): list_display = ['appoint_no', 'user', 'schedule', 'status', 'created_at'] list_filter = ['status'] search_fields = ['appoint_no']

admin 里还可以通过 list_editable 直接改 status,做人工补偿时很顺手。需要注意的是,多对多字段不要放进 list_display,渲染开销大,页面会明显变慢。这个配置在联调阶段能省下大量时间,也是验收源码包是否跑通的第一道检查点。

5. MySQL 慢查询排查与 ORM 改写:挂号列表页的百毫秒优化

挂号列表页要联科室、医生、号源三张表,再带上日期和可用号筛选条件。数据量到几十万行时接口开始变慢,第一反应不该是加缓存,而是先看 SQL 的执行计划。InnoDB 的索引结构是 B+ 树,写不好联合索引,等值过滤和排序都会全表扫描。

5.1 先用 EXPLAIN 看执行计划再动手

用 django.db.connection.queries 或者直接把 SQL 拿到 MySQL 命令行回放。EXPLAIN 输出里最值得看的是 type 和 rows 两列。

EXPLAIN SELECT s.*, d.name, p.name FROM schedule s JOIN doctor d ON d.id = s.doctor_id JOIN dept p ON p.id = d.dept_id WHERE s.work_date = '2025-06-18' AND s.remain_num > 0 ORDER BY s.doctor_id;

type 从好到坏大致是 eq_ref、ref、range、index、ALL。看到 ALL 说明 schedule 表在整表扫描,没有可用索引;rows 是预估扫描行数,数字越大优化空间越大。如果 type 是 range,说明已经用上了 work_date 相关索引。先记录执行计划再决定加什么索引,不要拿一个索引试一下看快不快,数据量变化后结论往往不可靠。

5.2 组合索引设计:日期条件放最左,再谈排序

列表页过滤条件用 work_date,排序用 doctor_id。在这个场景里,联合索引 (work_date, doctor_id) 比 (doctor_id, work_date) 更合适,因为等值条件在最左能命中索引前缀,B+ 树扫出来的数据天然按 doctor_id 有序,ORDER BY 就不需要额外做文件排序。

ALTER TABLE schedule ADD INDEX idx_date_doctor (work_date, doctor_id);

这个索引和 DDL 里的唯一键 uk_doctor_date(doctor_id, work_date) 看起来很像,但列顺序不同。唯一键服务的是“一个医生一天只有一行”的约束,查询索引服务的是“按日期查可用号”的路径,两者目的不同,可以同时存在。把组合索引列顺序和最左前缀原则对齐后,列表接口的耗时通常能降到原来的十分之一以下。如果查询条件里还带了科室过滤,可以把 dept_id 放在 work_date 后面,做成三列联合索引。

5.3 select_related 消除 N+1,别让 ORM 把查询放大

模板里访问 order.schedule.doctor.name 时,Django ORM 默认每访问一个外键就发起一条 SQL。100 行数据可能产生 300 条 SQL,数据库往返时间占掉接口耗时大头。select_related 通过一次 join 把关联数据拉齐。

from app.models import Schedule schedule_list = Schedule.objects.filter( work_date='2025-06-18', remain_num__gt=0 ).select_related('doctor__dept').order_by('doctor_id')

select_related 的参数顺序对应外键链:doctor 关联医生表,doctor__dept 继续联到科室表。执行后 Django 的 SQL 日志里应该只有一条带两个 join 的语句。select_related 只适用于外键和一对一这类单值关联,多对多关系要用 prefetch_related 另开二次查询再合入。这两者差异是 Django 面试常考点,也是改写慢查询时第一个要检查的地方。

6. 部署到生产前必做的三个验证:压测、缓存回源、Redis 连接数

最后落到一个可复现的验收流程。源码包在手部署完,不能只看系统能登录就算完,我一般按三条线验证它是否能扛住放号场景。

6.1 用 ab 压测号源查询接口并观察命中率

用 gunicorn 起 4 个 worker,再对上线的列表接口做 2000 次请求、50 并发压测:

ab -n 2000 -c 50 "http://127.0.0.1:8000/api/schedules/?date=2025-06-18"

压测后通过 redis-cli 执行 INFO stats,观察 keyspace_hits 和 keyspace_misses 两个计数器。命中率低于 90% 说明缓存 Key 设计还有问题,多半是查询参数没有完整拼进 Key,或者 TTL 设得太短。命中率到 95% 以上时,读接口基本不构成 MySQL 压力。想看直观变化,用 redis desktop manager 连接 Redis,观察 schedule:remain 前缀的 Key 在请求前后的数值变化。

6.2 核对 Redis 连接池与 gunicorn worker 的乘积

gunicorn 每个 worker 是独立进程,各自持有 Redis 连接池。worker 数是 4、池大小是 10 时,Redis 最大连接数就是 40。生产环境如果用宝塔面板部署 Django,要特别注意面板生成的 gunicorn 配置往往只有单进程单线程,调整 worker 数时 Redis 连接数会同步变化,这两个参数必须一起核算。保守的池配置:

redis.ConnectionPool( max_connections=20, socket_timeout=5, socket_connect_timeout=5 )

max_connections 按“单 worker 峰值并发”除以 worker 数再加 20% 余量设置。socket_timeout 和 socket_connect_timeout 必须设置,否则 Redis 卡住时 Django 请求会一直挂到系统默认超时,拖垮整个进程。

6.3 用一条脚本验证预约-取消闭环

写一个总验证脚本按顺序跑五步:登录取 token、查剩余号、下单、取消订单、再查剩余号。两次查询的剩余号应该一致,中间任何一步数据不一致,直接定位到缓存和数据库的同步逻辑。这个脚本建议放进 CI 里,每次改 Redis 或排班逻辑都跑一遍,比手工点页面可靠得多。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 17:10:19

GLM-5拿下Vending Bench 2开源第一:一年模拟经营的4432美元

GLM-5拿下Vending Bench 2开源第一&#xff1a;一年模拟经营的4432美元 【免费下载链接】GLM-5 GLM-5: From Vibe Coding to Agentic Engineering 项目地址: https://gitcode.com/GitHub_Trending/gl/GLM-5 GLM-5 是面向复杂系统工程与长周期智能体任务&#xff08;Agen…

作者头像 李华
网站建设 2026/9/15 17:08:47

MATLAB GUI实现的模板匹配车牌识别方法详解

简介&#xff1a;基于 MATLAB GUI 的车牌识别项目资料包&#xff0c;采用模板匹配算法完成车牌识别任务&#xff0c;完整覆盖车牌定位、字符分割、汉字识别、数字与字母识别等关键环节&#xff0c;适合正在做课程设计、毕业设计或希望入门图像识别的 MATLAB 学习者参照使用。整…

作者头像 李华
网站建设 2026/9/15 17:08:43

CNN手写数字识别APP开发:从模型训练到zip打包部署全流程

简介&#xff1a;基于CNN的手写数字识别完整项目&#xff0c;面向深度学习初学者、课程设计或毕业设计人员&#xff0c;涵盖从模型训练到桌面应用部署的全流程。压缩包共30个文件&#xff0c;主要包含Python源码、预编译pyc、训练好的模型参数pkl、界面截图png、Windows可执行e…

作者头像 李华
网站建设 2026/9/15 17:07:02

域名申请的流程速查手册:避坑指南

域名申请的流程速查手册:避坑指南 网站被黑挂马不知道怎么办?别慌,这往往是基础不牢的连锁反应。很多站长在初期为了省事,没搞清 域名申请的流程 ,导致解析混乱、备案缺失,给黑客留了后门。今天这份 速查手册…

作者头像 李华
网站建设 2026/9/15 17:05:52

UQLab概率分布转换指南:非高斯变量到标准高斯空间的完整实现

从事不确定度量化这块工作的朋友&#xff0c;对UQLab应该不陌生。这个MATLAB工具箱把输入建模、抽样、灵敏度分析、可靠性分析串成了一条流水线&#xff0c;上手确实快。但真到了自己搭模型的时候&#xff0c;很多人会卡在一个看似基础的问题上&#xff1a;手里明明是一堆威布尔…

作者头像 李华