news 2026/10/6 5:32:10

基于Python与Vue3的新生报到系统设计:从数据库到状态机实战复盘

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Python与Vue3的新生报到系统设计:从数据库到状态机实战复盘

新生报到这件事,每年开学季都是学校信息化部门最头疼的环节。纸质表格满天飞、家长学生排长队、各院系数据对不上,这些场景我接手过不下五次。所以当拿到一个“基于Python的新生报到系统管理的设计与实现”需求时,我第一反应不是急着写代码,而是先把报到现场的真实流程摸清楚。这项目最终我用了Python做后端接口层、Vue3做管理端页面,从数据库设计到状态机流转再到前端看板,整套系统花了两周时间落地。这篇文章就完整记录一下我的设计思路和实现过程,包括那些常规文档里不会写的坑,希望能给正在做类似校园管理系统的朋友一些参考。

1. 报到现场的真实痛点与需求边界划分

1.1 传统手工报到流程到底乱在哪

我特意去观察过两所高校的报到现场。新生手里攥着录取通知书,在体育馆里一排排找自己院系的摊位,找到之后先交纸质档案,再领宿舍钥匙,然后去另一个窗口排队缴体检费,中间还要办校园卡。整个过程涉及教务处、学工部、财务处、后勤集团、校医院至少五个部门,每个部门都有一份独立的Excel表。等到下午汇总数据时,光是把各个表的“是否已报到”状态对齐就要花两个多小时,还经常出现某人交了费但没领宿舍钥匙的漏项。

这个系统的第一个核心需求很明确:把“报到”这个动作从一次线下排队,变成一条线上的状态流转。新生一到校,扫录取通知书上的二维码,系统立刻显示这名新生的院系、专业、班级、缴费状态、宿舍分配情况,工作人员点击“确认报到”后,数据自动同步到所有部门的后台。听起来简单,但要把这条链路做稳,牵扯到的数据模型和状态设计相当多。

1.2 需求边界:哪些必须做、哪些先不做

跟教务处反复确认之后,我把项目边界收敛成六个模块:

  • 新生基础信息管理:录取数据的导入、查询、修改、导出,这是所有业务的地基
  • 报到流程管理:报到确认、绿色通道(缓缴费用)、信息核验三大入口
  • 缴费状态对接:学费、住宿费、体检费在系统中展示缴纳状态,支持线下缴费后手工标记
  • 宿舍分配:按院系和专业维度预分配宿舍,报到时直接绑定床位
  • 数据统计看板:实时展示报到率、各院系进度、缴费情况
  • 系统权限:区分超级管理员、院系管理员、财务人员、宿管人员四种角色

很多人第一次做这类系统时容易犯一个毛病:想把迎新门户、缴费系统、宿舍选房全揉在一起,结果开发周期无限拉长。我的建议是先把“报到确认”这条主链路做通,其他功能用二期迭代补上。因为报到当天现场的使用压力是巨大的,主流程不稳定,后面的功能再花哨也没用。

2. 后端框架选型与工程结构:Flask还是FastAPI

2.1 为什么没选Django

Python做后端管理系统的三个主流选择是Django、Flask和FastAPI。Django自带Admin后台和ORM,开发传统功能很省事,但它的重量级设计对这类需要快速迭代、前后端完全分离的项目有点拖沓。我的选型标准有三条:一是上手快、文档全,团队里哪怕是初级工程师也能快速接手;二是路由和蓝图结构清晰,方便按业务模块拆文件;三是跟Vue3前端的联调要顺畅,尤其是接口文档和参数校验这块不能太随意。

最终选了Flask + Flask-SQLAlchemy + Flask-Migrate这套组合,理由很实际:Flask的路由装饰器写起来灵活,ORM延续了SQLAlchemy的声明式风格,数据模型改完用一条migrate命令就能同步表结构。FastAPI虽然自带OpenAPI文档和Pydantic校验,性能也更好,但考虑到学校服务器环境普遍是老旧的物理机,Python版本可能还停留在3.6到3.8,FastAPI需要3.7+才能跑得顺,Flask在兼容性上更稳妥。

2.2 按业务模块拆分的工程目录

项目结构直接决定后续维护成本,我按“工厂模式 + 蓝图”的方式组织:

new_student_system/ ├── app/ │ ├── __init__.py # 创建app实例,注册蓝图 │ ├── extensions.py # 初始化db、migrate、cors等扩展 │ ├── models/ │ │ ├── __init__.py │ │ ├── student.py # 新生信息模型 │ │ ├── enrollment.py # 报到记录模型 │ │ ├── dormitory.py # 宿舍分配模型 │ │ └── user.py # 系统用户模型 │ ├── api/ │ │ ├── __init__.py # 蓝图统一注册入口 │ │ ├── auth.py # 登录认证接口 │ │ ├── student.py # 新生信息接口 │ │ ├── checkin.py # 报到流程接口 │ │ ├── stats.py # 统计看板接口 │ │ └── upload.py # Excel批量导入接口 │ ├── services/ │ │ ├── checkin_service.py # 报到业务逻辑层 │ │ └── import_service.py # 数据导入服务 │ └── utils/ │ ├── response.py # 统一返回结构 │ └── auth_decorator.py # JWT鉴权装饰器 ├── migrations/ # 数据库迁移脚本 ├── config.py # 环境配置 └── run.py # 启动入口

这套结构最好的一点是api层只负责参数接收和HTTP状态码,真正的业务规则写在services层。比如“报到确认”的动作,controller只读request里的student_id,然后调用checkin_service里的confirm方法,事务提交和状态校验都在service里完成。这样代码不会变成一坨纠缠不清的大杂烩,后面二期的工位分配、迎新大屏都能往里加。

3. 数据库设计:一张报到总表拆成五张核心表

3.1 核心表字段设计要点

新生报到系统的数据量其实不算大,一届新生满打满算也就一万到两万人,但这不意味着表结构可以随便拍脑袋设计。我讲究的原则是:能关联的不要嵌套,能枚举的不要写死字符串。

四张核心表的设计如下:

新生信息表(tb_student)

字段类型说明
idint PK自增主键
admission_idvarchar(20)录取编号,全国统一的考生号
namevarchar(50)姓名
gendertinyint1男 2女
id_cardvarchar(18)身份证号,做加密存储
major_idint FK专业ID
class_idint FK班级ID
phonevarchar(20)联系电话
hometownvarchar(100)生源地
photo_urlvarchar(200)证件照地址
statustinyint1未报到 2已报到 3缓报 4请假

报到记录表(tb_enrollment)

字段类型说明
idint PK自增主键
student_idint FK关联新生ID
check_in_timedatetime实际报到时间
operator_idint FK操作人(工作人员)
channeltinyint报到渠道:1现场 2电脑批量
remarkvarchar(255)备注

缴费表(tb_payment)

字段类型说明
idint PK自增主键
student_idint FK关联新生ID
tuition_statustinyint学费状态 0未缴 1已缴 2缓缴
accommodation_fee_statustinyint住宿费状态
medical_fee_statustinyint体检费状态
update_timedatetime最近更新时间

宿舍分配表(tb_dormitory_assign)

字段类型说明
idint PK自增主键
student_idint FK关联新生ID
building_novarchar(10)楼栋号
room_novarchar(10)房间号
bed_novarchar(5)床位号
assign_timedatetime分配时间

这里有个关键设计经验:不要给新生表里塞一堆状态字段。刚开始有人建议直接在tb_student里加“是否缴费”“是否分配宿舍”的布尔值,听着方便,但实际上报到进度是需要留痕和追溯的。缴费状态变化要记录到什么时间、是谁改的,这时候孤儿表就比冗余字段靠谱得多。把“状态”拆到各自的业务表里,再用视图或接口聚合,后续出报表时灵活度完全不同。

3.2 数据权限的库表规划

系统用户表我单独建了一张,没有像某些项目那样把工作人员和新生塞进同一张用户表,因为这两类人的权限模型完全不同。tb_user里存的是管理员账号,绑定角色ID,角色表里用不同的菜单权限码区分:

  • 超级管理员:能看到所有院系、所有模块
  • 院系管理员:只能看本院系学生数据
  • 财务人员:只能看缴费模块和统计数据
  • 宿管人员:只能看宿舍分配模块

这个权限隔离不是在路由层硬编码的,而是在创建数据库查询时,根据当前登录用户的role_id自动添加院系的过滤条件。写起来就是SQLAlchemy里拼接filter逻辑,但要注意的是建索引——按院系查学生的频率极高,major_id和class_id必须建联合索引,否则数据量上万之后接口就会明显变慢。

4. 后端核心逻辑:报到状态机、Excel批量导入与事务处理

4.1 报到状态机的流转设计

报到这件事表面看只有“报到”和“未报到”两个状态,但我深入跟辅导员聊过之后发现,真实场景要复杂得多。有新生在系统里录入了信息但人还在路上,有新生到了现场但没带全材料需要延后办理,有新生因为身体原因需要保留学籍一年。如果只用两个状态描述,现场工作人员就得靠Excel备注去记,跟系统脱节。

我用了一个简单的状态机:

未报到(1) ──确认报到──> 已报到(2) 未报到(1) ──登记延迟──> 延迟报到(3) 已报到(2) ──取消报到──> 已取消(4) 延迟报到(3) ──确认报到──> 已报到(2) 延迟报到(3) ──放弃入学──> 已取消(4)

后端在service层做状态流转校验,不允许跳状态更新。比如已取消的新生不能再变回已报到,这通过一个字典来定义合法迁移路径:

ALLOWED_TRANSITIONS = { 1: {2, 3}, # 未报到 -> 已报到 / 延迟报到 2: {4}, # 已报到 -> 已取消 3: {2, 4}, # 延迟报到 -> 已报到 / 已取消 }

这个设计看起来简单,但价值很大。报到当天会有专门的文员对着电脑操作,如果状态可以随便乱改,很容易出现“先点成已报到,后面发现人还没到齐又改回去”的乌龙。有了状态机,所有状态变化都走同一个校验函数,变化记录直接入库,事后出了问题能精准回溯是谁、几点、把谁的状态从什么改成了什么。

4.2 Excel批量导入踩到的编码坑

新生录取数据在教务系统里导出后通常是一份Excel,里面有上万行。批量导入功能是系统上线的第一道坎,导不进去,后续所有功能都用不起来。

我最初写的导入逻辑很简单:pandas读Excel,逐行插入数据库。结果第一轮测试就挂了,问题出现在三个地方:

第一个坑是Excel里的身份证号被转成了科学计数法,后几位变成了0。解决方法是读文件时把该列指定为字符串类型:

import pandas as pd df = pd.read_excel(file_path, dtype={'身份证号': str, '录取编号': str})

第二个坑是数据里混着全角空格和换行符。Excel从教务系统导出时,某些字段末尾会带不可见字符,直接入库后前端查出来显示没问题,但做精确匹配时就是匹配不上。我在导入服务里加了一层清洗函数,strip掉空格、把全角逗号和括号转成半角,姓名里的·这个间隔符单独处理:

def clean_cell(value): if pd.isna(value): return "" text = str(value).strip() text = text.replace("\u3000", "").replace("\n", "").replace("\r", "") text = text.replace("(", "(").replace(")", ")") return text

第三个坑才是真正的坑:Excel里有重复数据。同一个考生号可能因为教务系统的补录操作出现两次,直接插入会违反唯一约束,导致整个事务回滚,前面几千条白导了。我在导入服务里加了“先查重、再分组”的逻辑,用临时表先把文件数据装载,再和数据库里的admission_id做对比,重复的数据单独生成一份错误报告下载给用户。这个功能虽然不起眼,但现场老师直呼救命。

导入接口的完整逻辑是:上传文件 -> 用openpyxl读取前10行做格式校验 -> 将数据写入临时表 -> 执行存储过程或循环逐行查重、清洗 -> 批量提交事务 -> 生成错误清单。这里建议批量插入用db.session.bulk_insert_mappings,实测两万条数据的导入时间从90秒降到了6秒,差别非常大。

4.3 接口统一返回与异常处理

前后端分离项目最容易出现的问题是后端返回格式不统一,前端判个错要写一堆if-else。我从一开始就定义了统一的响应结构:

{ "code": 0, "message": "success", "data": {} }

code为0是成功,非0对应具体的业务错误码。比如1001是参数校验失败,1002是数据不存在,2001是状态流转不合法。全局异常处理器捕获所有未处理的Exception,返回code=500,避免直接把Python堆栈信息抛给前端。这个规范定好之后,Vue3前端那边用axios拦截器统一处理,代码干净很多。

5. Vue3管理端:从登录到报到单的完整实现

5.1 为什么从Vue2切换到Vue3的组合式API

管理后台我最初用Vue2写过一个原型,后来推到重来换成了Vue3。原因主要有两点:一是项目要用Element Plus组件库,它对Vue3的支持是原生级别的,Vue2那边是坑坑洼洼的兼容方案;二是新生报到系统的页面状态关联度极高,报到现场页面既要显示学生信息卡,又要同步显示缴费状态和宿舍分配结果,组合式API里的响应式状态管理比Options API的data/computed/methods分离要直观得多。

组合式API的核心就是用ref和reactive管理状态,用computed做派生数据。比如报到单页面,我只需要维护一个studentId,其他所有展示数据都是基于这个id拉取后计算出来的:

import { ref, computed, onMounted } from 'vue' const studentId = ref('') const studentInfo = ref(null) const loading = ref(false) const fullStatus = computed(() => { if (!studentInfo.value) return { text: '', type: 'info' } if (studentInfo.value.status === 2) return { text: '已报到', type: 'success' } if (studentInfo.value.status === 3) return { text: '延迟报到', type: 'warning' } return { text: '未报到', type: 'danger' } }) async function fetchStudentDetail() { loading.value = true const res = await api.getStudentDetail(studentId.value) studentInfo.value = res.data loading.value = false } onMounted(() => { // 从路由参数或扫码框获取studentId })

代码组织也不再需要把data函数里面的十几个字段按类型分组,而是按“这个组件是什么业务”来聚合。比如报到相关的状态、接口、逻辑放在一个useCheckin的函数里,宿舍相关的放另一个useDormitory里,互不干扰,多人协同开发时冲突概率小很多。

5.2 动态表单与报到单组件设计

报到现场有一个高频场景:核对新生信息时,发现联系电话填错了、宿舍床位想调换。如果每个字段都去编辑页面改,工作效率极低。我做了两个关键组件:

一个是可编辑信息卡。新生信息卡把字段分成只读区和可编辑区,只读区展示姓名、身份证号、考生号;可编辑区展示电话、联系人、生源地,点击“编辑”后表格的行直接切换成输入框。这里用到了动态表单的思路,给每个字段配置一个meta对象,标明它的类型(输入框/下拉框/日期选择器)、是否必填、校验规则。渲染时统一循环meta数组。

另一个是宿舍分配弹窗。弹窗里显示当前楼栋的剩余床位,点一个床位号,右侧实时展示该学生的信息卡。这个页面的交互反馈要求非常高,要求点击“确认分配”后,床位号区域立即变成不可选状态,同时学生信息里的宿舍字段跟着刷新。我在设计时把床位列表用reactive(new Map())维护,key是"楼栋-房间-床位",value是status,前端控制乐观更新,接口失败时回滚。

5.3 Vuex还是Pinia,状态管理怎么选

Vue3生态下状态管理我已经全面转向Pinia,Vuex虽然在维护但组合式API下用起来总觉得拧巴。这个项目的全局状态不多,一个当前登录用户信息、一个权限路由的菜单列表、一个报到现场的当日统计数字,用Pinia的defineStore来写非常简单:

import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: localStorage.getItem('token') || '', userInfo: null }), getters: { isSuperAdmin: (state) => state.userInfo?.role === 1, }, actions: { async login(credentials) { const res = await api.login(credentials) this.token = res.data.token this.userInfo = res.data.userInfo localStorage.setItem('token', this.token) }, logout() { this.token = '' this.userInfo = null localStorage.removeItem('token') } } })

路由权限我是用路由守卫做的,登录后根据角色ID动态添加路由。比如财务人员的路由表里不包含“宿舍分配”这个页面,路由守卫里直接next到404。这个方案比把所有页面全部注册、再用按钮级权限隐藏的做法更安全,因为路由层面的隔离能挡住直接输URL访问。

6. 前后端联调的实战问题:跨域、鉴权与并发

6.1 跨域配置不能只开CORS就完事

开发和部署阶段都躲不开跨域问题。Vue3的开发服务器默认跑在localhost:5173,Flask接口跑在localhost:5000,跨域是必然的。我在Flask里用了flask-cors扩展:

from flask_cors import CORS CORS(app, resources={ r"/api/*": { "origins": ["http://localhost:5173", "https://checkin.example.edu.cn"], "supports_credentials": True } })

它解决的问题是浏览器的同源限制,但真正的安全闸门在Token鉴权上。我用JWT(PyJWT库)做登录认证,生成的token有效期设成12个小时,覆盖报到当天的工作时长。接口需要登录才能访问的,用一个装饰器统一处理:

def token_required(f): @wraps(f) def decorated(*args, **kwargs): token = request.headers.get("Authorization", "") if not token.startswith("Bearer "): return jsonify(code=401, message="未登录或token已过期"), 401 try: payload = jwt.decode(token.split(" ")[1], current_app.config["SECRET_KEY"], algorithms=["HS256"]) request.user_id = payload["user_id"] except jwt.ExpiredSignatureError: return jsonify(code=401, message="登录已过期,请重新登录"), 401 except jwt.InvalidTokenError: return jsonify(code=401, message="无效的token"), 401 return f(*args, **kwargs) return decorated

轮子造得虽然简单,但好用。有个细节要注意:前端必须在axios请求拦截器里把token加到Authorization头,否则所有接口都会401。而且需要处理401响应时自动跳登录页的逻辑,避免用户在页面傻等。

6.2 并发报到导致宿舍重复分配的防御

报到高峰时段,多个窗口同时操作,最危险的问题就是同一个房间的最后一个床位被两个人同时分配。数据库层面只靠“先查再插”是挡不住的,必须加锁或者用乐观锁。

我采用的是“数据库唯一约束 + 前置状态检查”的双保险。在宿舍分配表里,给“楼栋+房间+床位”建联合唯一索引,这样即使两个请求同时执行,数据库也只会允许一条插入成功。与此同时,分配接口在事务里先锁定学生记录,检查当前报到状态必须是“未报到”,确认后再执行床位更新。这套方案代码不算复杂,但它把最坏情况的概率降到了零,报到当天没出过一次重复分配事故。

6.3 一个小而关键的体验:扫码枪输入

报到现场基本都用扫码枪扫录取通知书上的条形码,扫码枪的本质是一个快速键盘输入设备,扫完后会敲一个回车。所以前端的搜索框要监听Enter事件,回车后立即触发展开搜索,而不是等用户点“查询”按钮。这个细节我觉得比某些花哨功能更重要,现场老师用下来反馈很一致:蜻蜓点水式的交互在这里行不通,报到效率每分钟都在刷新,扫完码就出信息是底线体验。

7. 部署上线时踩到的Python环境坑

7.1 千万别直接裸奔跑Flask

很多教学项目喜欢直接python run.py启动服务,这在校园服务器上是非常危险的。Flask自带的开发服务器是一个单进程的Werkzeug服务器,不仅并发能力弱,而且错误信息直接暴露在回显里。我的部署方案是gunicorn作为WSGI服务,Nginx做反向代理:

gunicorn -w 4 -b 127.0.0.1:8000 run:app

Nginx配置里把/api路径的请求转发到8000端口,静态页面交给前端打包后的dist目录。这里有一个必须注意的坑:gunicorn的worker数不是越多越好。它跟CPU核心数有关,一般2到4个就够了。因为Python的GIL限制,开8个worker反而会因为频繁切换进程切换造成资源浪费,而且数据库连接池的占用也会直线上升。

7.2 Python版本导致的生产事故

我这次部署到学校机房时,那台CentOS服务器上装的是Python 3.6,而我在开发机上的Python 3.10写了一个walrus运算符:=和f-string的等号调试语法f"{name=}",结果代码直接跑不起来。当时离正式报到只剩三天,整个人都麻了。后来花了一晚上把所有高版本语法全部改成兼容写法,同时把本地项目的runtime.txt或者requirements文件里明确锁定Python版本范围,并要求运维在目标机器上创建虚拟环境。这个教训非常深刻,开发环境和部署环境的Python版本必须从项目第一天就对齐。

7.3 数据备份与手动兜底方案

报到系统的数据重要性不用多强调。我做了两层保障:第一是MySQL主库每天凌晨3点自动mysqldump备份,备份文件保留30天;第二是写了一个简单的“手动导入数据”入口,万一报到当天系统或网络出问题,现场可以通过另一个离线通道把已报到名单导出成Excel,事后补录进系统。这套兜底方案看起来土,但在真实校园场景里非常实用。

8. 实测数据与经验复盘

系统上线当天,我盯了一整天实时监控。从早上7点半到下午5点半,累计处理了1240名新生报到,峰值时段每分钟约有15次请求,全部接口的P99响应时间稳定在300ms以内。CPU峰值不到40%,内存占用也远低于预期。这套系统的瓶颈从始至终都不在后端性能,而在于现场的工作人员操作是否熟练、网络是否稳定。

给正在做类似项目的同行三个建议:

  • 状态机比数据表结构更值得花时间设计,报到现场真正乱的是状态,不是字段
  • 联调阶段一定要模拟并发请求,至少用JMeter或locust压一下报到确认和宿舍分配这两个接口,找数据库锁的问题要趁早
  • 前端页面不需要华丽,需要的是大字号和高对比度,报到现场的电脑屏幕可能反光,字体小了老师根本看不清

最后分享一个我个人的心得:这类系统的技术难点并不深,真正考验项目负责人的是现场业务理解能力和兜底意识。你把教务处老师和辅导员的真实工作流程吃透了,系统自然就好用;反过来,只顾着堆技术栈、炫组件库,做出来的东西只能在演示的时候好看,到了真刀真枪的报到现场,分分钟出事故。这套系统的代码我至今还留着,每年开学季前都会翻出来改一版,Vue3那边上了新特性、Python换了新版本,也顺手小步迭代一次。新生报到系统做到后面,已经不只是代码项目,而是对高校业务流程的一种沉淀。

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

OpenShell:还原Windows经典开始菜单与资源管理器增强实战指南

这几年凡是折腾过Windows开始菜单的老用户,多半都听过OpenShell这个名字。简单说,它就是把Windows 8/10/11那套开始菜单按回XP/Win7手感的开源工具,同时还能顺手给资源管理器加上状态栏、直接进“计算机”这类偏门功能。我是从Classic Shell时…

作者头像 李华
网站建设 2026/10/6 5:31:46

视频私域专栏搭建指南:企业培训、课程教学与客户交付的实操方案

做私域视频内容这几年,我最大的感触是:视频单独丢一个链接给学员或员工,几乎等于白做。看没看不知道,看到哪一节不知道,学没学会更不知道。后来我开始把视频内容做成结构化的私域专栏,不管是企业内部培训、…

作者头像 李华
网站建设 2026/10/6 5:30:30

OpenShell完全指南:Windows 11经典开始菜单定制与效率提升

如果你还在为Windows 11那套居中的开始菜单、砍掉的快捷跳转列表和越来越难找的“关机”按钮而烦躁,那OpenShell绝对值得你花半小时折腾一下。这个开源项目的前身是很多老玩家都熟悉的Classic Shell,后来改名为Open-Shell(社区里一般还是叫Op…

作者头像 李华
网站建设 2026/10/6 5:30:00

想明白微积分在说什么:从变化率到现代科学视野

能把导数算对的人不少,真正想明白导数在说什么的人不多。这句话我憋了很多年。身边太多朋友学完微积分,考完试立刻把工具扔了,心里只留下一个印象:微积分别扭、抽象、和真实世界没什么关系。但恰恰相反,微积分是人类思…

作者头像 李华
网站建设 2026/10/6 5:27:56

博士同事代码烂却受领导重用?揭秘评价体系错位与职场解法

这个标题我看了好几遍,心里的感受挺复杂的。一方面觉得真实得让人窒息,另一方面又觉得,能把这个现象描述得这么准确,说明你已经在职场现场摸爬滚打有一阵子了。代码写得像坨屎、需求没搞清就闷头开干、到处埋坑,结果领…

作者头像 李华
网站建设 2026/10/6 5:27:43

基于经验阻力公式的柔性板重构减阻简化模型

先把场景摆出来:水下潜航器浮出水面充电,太阳能柔性帆板像床单一样在水流里展开,这时候整块板子的阻力可不是小事。我最早接触这个题目,是因为一块高柔性的光伏板在来流中出现了肉眼可见的波浪变形,原本以为是结构坏了…

作者头像 李华