去年帮某个城市的民间宠物救助组织搭了一套领养管理系统,前前后后折腾了大半个月,从需求梳理到部署上线,中间推翻过一次方案、重写过两次数据库模型,踩了不少坑。最后落地的技术栈是 Flask + Vue,开发工具用的 PyCharm,核心功能覆盖宠物领养和志愿者募捐两大块。这篇文章把整个项目的设计思路、技术选型、数据库建模、接口实现和排坑过程完整复盘一遍,给准备做类似管理系统的朋友一个可以直接参考的案例。
先交代一下项目背景。这家救助组织之前全靠人工登记:领养人信息记在表格里,宠物照片散落在微信群,募捐款项靠微信转账后手工记账。宠物数量一多,信息对不上、捐款对不齐、志愿者重复报名这些问题全冒出来了。所以这个系统要解决的核心痛点很明确:把领养流程、志愿者管理、募捐记录全部线上化,让每个环节都有据可查。
先说结论:技术栈最终定的是 Flask + Vue 前后端分离,开发工具 PyCharm,数据库用 MySQL。标题里同时提到 Flask 和 Django,是因为当时确实对两个框架都做了预研,各写了一个最小 demo 后才拍板的。下面把这个决策过程、系统设计、逐模块实现以及排查过的典型问题都展开讲。
1. 项目到底要解决什么问题——需求分析
1.1 宠物领养管理的核心痛点
救助站的宠物领养流程,听起来简单,实际上是一串很长的事务链。一只流浪猫从救助进站开始,要记录它的基本信息、健康状况、疫苗接种情况、绝育状态,然后拍摄照片发布到可领养列表里。意向领养人提交申请后,工作人员要审核申请材料、安排线下回访、确认领养条件,最后签协议、记录领养日期。宠物离站之后还得跟踪回访情况,防止弃养。
这套流程在表格里管理时,最怕三件事:一是宠物信息更新不及时,照片和实际状态对不上;二是同一只宠物被多人同时申请,审核状态靠口头沟通容易混乱;三是历史记录查不到,领养后回访无据可依。
所以在设计领养模块时,重点不是做个简单的宠物信息展示页,而是把“宠物在站状态机”和“领养申请审批流”两条核心链路做扎实。宠物的状态要能从“待领养”“审核中”“已领养”“已下架”几个状态清晰流转,每次流转都要有时间记录和操作人记录。领养申请也要有独立的审核状态:待审核、初审通过、等待回访、已完成、已驳回。
1.2 志愿者与募捐模块的业务闭环
志愿者募捐系统单独拎出来说,是因为它和领养模块是联动关系。救助站的大部分运营资金来自公众募捐,而募捐活动的筹款进度、收支明细需要公开透明,不然很难取得捐赠者的信任。同时,线下领养活动日又需要大量志愿者参与,志愿者报名、排班、服务时长记录也需要一套管理逻辑。
我的理解是,志愿者和募捐并不是两个孤立功能,它们共同构成了救助组织的良性运营闭环:募捐带来资金,资金支撑宠物救助,志愿者带来人力,人力支撑线下活动和日常照料。所以在设计数据库时,要把志愿者、募捐活动、捐款记录、领养事件放在同一个业务模型里考虑,避免出现信息孤岛。
这个需求分析的结论直接决定了系统的边界。如果只是做个“宠物信息展示站”,那用现成的建站工具就行;但需要的是“拥有审批流、募捐透明化、志愿者排班”的管理系统,就必须从零开发了。
2. 技术选型:flask和django的对比抉择
2.1 两个框架的预研对比
标题里同时出现 Flask 和 Django,其实是真实的选型过程。当时团队里有人倾向 Django,理由是它大而全,ORM、Admin后台、认证系统全都内置了;我则更倾向 Flask,原因是项目功能边界比较清晰,不需要 Django 那么大而全的组件,而且 Flask 的灵活度更高,接口设计可以完全定制。
为了不靠感觉决策,我把两个框架各写了一个最小 demo:同样实现宠物领养列表的增删改查 API。对比下来有几个实际感受:
| 对比维度 | Flask | Django |
|---|---|---|
| 项目初始化成本 | 轻量,几行代码就能起服务 | 需要 project/app 结构,体系较重 |
| 数据库操作 | Flask-SQLAlchemy 需自行配置,灵活度高 | Django ORM 自带 Migrations,开箱即用 |
| Admin 后台 | 需自行组合 Flask-Admin | 自带完整 Admin,管理大量数据方便 |
| 项目结构自由度 | 完全自由,适合小团队自定义 | 规范化约束强,适合大团队协同 |
| 学习曲线 | 平缓,上手快 | 较陡,概念多 |
最终选了 Flask,核心原因有两个。一是这个系统的业务逻辑虽然多,但都是轻量级的管理操作,Django 的很多能力在这个项目里派不上用场,属于带着重装备跑步。二是救助站后期可能会根据实际需求灵活调整业务规则,比如领养审核流、募捐活动玩法,Flask 的松耦合结构改起来更快,尤其适合快速迭代。
选型结论不是哪个框架绝对更好,而是要看项目体量和团队习惯。如果你做的是一个内容型平台,或者需要快速搭建带后台管理的项目,Django 的团队协作优势会更明显;如果像本系统这样以定制化 API 为核心、前端已经决定用 Vue 接管页面渲染,Flask 的轻量灵活是更舒服的选择。
2.2 前端为什么选择vue
选择 Vue 同样做过对比。系统前端需要实现几类页面:普通访客浏览宠物列表并提交领养申请、志愿者报名活动,管理员审核申请并维护宠物信息,财务人员录入募捐明细。这种多角色、多视图的应用,非常适合用 Vue 单页应用来组织——路由分层清晰,组件复用率高,状态管理方便。
Vue 相对传统服务端模板渲染(Jinja2)的优势太明显了。页面数据全部通过 API 异步获取,刷新列表时不用重新加载整个页面;组件化的写法让宠物卡片、募捐进度条、申请表单这些 UI 片段可以在多个页面复用;开发调试时前后端完全分离,后端只需保证接口正确。
2.3 开发环境:pycharm的配合
PyCharm 在这个项目里不只是代码编辑器。它的 Professional 版本对 Flask 的支持很完整,可以直接创建 Flask 项目模板,内置 Flask 的调试运行配置,断点调试时能直接看到请求上下文和数据库会话内容。前端部分虽然 Vue 文件是在 PyCharm 里写的,但实际调试还是依赖 Node.js 环境,建议用 pycharm 的终端跑npm run serve,这样前后端日志都在一个窗口里看,排查联调问题非常方便。
环境配置上有一个建议:尽量用虚拟环境管理依赖,不要往全局 Python 里装包。这个项目依赖 Flask、Flask-SQLAlchemy、PyMySQL、Flask-CORS、Vue CLI 等,依赖版本容易互相影响,虚拟环境可以保证每次重建环境都能恢复到可运行状态。
3. 系统架构与数据库设计
3.1 整体功能模块划分
先把系统拆成四个主要模块:宠物管理、领养管理、志愿者管理、募捐管理,再加上公共的用户认证和公告模块。
- 宠物管理:宠物信息录入、图片上传、状态更新、列表筛选。这个模块是基础数据源,领养申请、募捐活动都围绕着它来组织。
- 领养管理:领养申请提交、审核流处理、领养记录、回访计划。这是系统里流程最复杂的模块,状态机设计要花最多心思。
- 志愿者管理:志愿者注册、活动报名、排班记录、服务时长统计。
- 募捐管理:募捐活动发布、捐款登记、募捐进度统计、收支明细公开。
- 用户认证:管理员登录、志愿者账号与角色权限控制。
模块之间不是孤立的。比如一场领养活动,既会展示活动关联的宠物信息,也会关联活动的志愿者报名表,还会关联针对该活动的募捐项目,所以数据表的关联关系要提前规划好,否则后面写查询时会很痛苦。
3.2 数据库表设计详解
数据库我用的是 MySQL 8.0,表结构按业务模块拆分成核心业务表、流程表和配置表三类。这里挑几张重点表来详细说明设计思路。
宠物信息表(pet)是基石表。字段包含:宠物名称、宠物种类(猫/狗/其他)、品种、性别、年龄、毛色、健康状况描述、疫苗接种状态、绝育状态、入站时间、状态(0=待领养、1=审核中、2=已领养、3=已下架)、封面图片路径。该表的常见问题在于年龄字段——救助站经常有“约2个月大”这种模糊描述,所以我在表里保留了一个age_text字段专门存前端展示文案,同时再用birth_month存一个粗略的月份值用于按年龄筛选。
领养申请表(adoption_application)是流程核心。字段包括:关联宠物 ID、申请人姓名、联系电话、家庭住址、居住类型(自有/租房)、养宠经验、收入情况说明、身份证号、审核状态(BOOLEAN 或 ENUM 都可)、审核备注、提交时间、审核时间、审核人 ID。这里要注意,家庭环境、收入情况属于敏感信息,页面展示时要做权限控制,不能让管理员之外的人任意查看。
募捐记录表(donation_record)直接关系到资金透明,设计时要额外小心。字段包括:募捐活动 ID、捐赠人姓名、联系方式、捐款金额、支付方式(微信/支付宝/银行转账)、捐款时间、款项用途备注、公示状态(是否已在首页公开展示)。金额字段我强烈建议使用DECIMAL(10,2)而不要用 FLOAT,否则累加时会出现小数点精度问题,传出去会让捐赠者质疑平台的可靠性。
志愿者活动报名表(volunteer_activity_registration):关联志愿者 ID、活动 ID、报名状态(待确认/已确认/已取消)、报名时间、实际参与时长(小时)、服务评价。服务时长的统计会直接在志愿者个人页展示,这部分是志愿者的荣誉记录,后端要保证数据只允许管理员修改。
用户表(user)就是常规的账号密码、角色、手机号、状态字段。密码用 Werkzeug 自带的generate_password_hash加密,不要自己存明文。
数据库设计阶段容易踩的坑是关联关系设计过头。比如一开始我把宠物信息、领养人信息、回访记录都拆成七八张表,导致写一个领养申请的查询要 join 四张表,代码非常啰嗦。后来做了反范式处理,把回访记录直接以冗余字段的方式放到领养申请表里,只保留最核心的独立表,代码和维护都轻松了很多。这种经验其实就是一句话:数据表为业务服务,不要为了设计的“标准”而牺牲可读性。
3.3 接口设计规范
API 设计采用了 RESTful 风格,按资源命名路由。整体上分为认证类、宠物类、领养类、志愿者类、募捐类五个部分。接口地址统一以/api/v1开头,方便后续版本扩展。
一些实用的接口规范示例:
GET /api/v1/pets:分页获取宠物列表,支持按种类、状态筛选。POST /api/v1/adoption/apply:提交领养申请。GET /api/v1/adoption/pending:管理员获取待审核领养申请列表。POST /api/v1/adoption/review:管理员审核领养申请。POST /api/v1/donations/record:登记一笔捐款。GET /api/v1/donations/{activity_id}/summary:获取募捐活动的进度统计。
接口返回统一使用 JSON 格式,这个项目里我封装了一个响应类,成功时返回{code: 0, data: ...},失败时返回{code: 非0, message: 错误信息}。前端拿到这份统一格式的数据后,直接做处理,不用每次判断不同的字段结构,省了很多联调时间。
4. 核心功能实现细节
4.1 宠物领养流程的状态机管理
宠物领养这个模块,如果只是写几个 API 很简单,但要把状态流转做严谨,就得认真设计状态机。
翻开领养业务,宠物和领养申请的状态流转其实是两套并行的状态机。宠物本身有“待领养”、“审核中”等状态,领养申请也有自己的状态。我在设计时专门写了一个状态枚举类,避免代码里到处用魔法数字:
class PetStatus: PENDING = 0 # 待领养 REVIEWING = 1 # 审核中 ADOPTED = 2 # 已领养 OFF_SHELF = 3 # 已下架 class ApplicationStatus: PENDING = 0 # 待审核 FIRST_PASS = 1 # 初审通过 AWAIT_VISIT = 2 # 等待回访 COMPLETED = 3 # 已完成 REJECTED = 4 # 已驳回为什么单独提状态机?因为在实际业务流程中,如果不用状态机控制,宠物状态和申请状态很容易出现冲突——比如有人在“已领养”状态时又提交了新申请,或者宠物已被驳回的申请还在“待审核”状态挂着,页面显示就会自相矛盾。
具体实现时,我写了一个专门的 service 类来封装状态流转逻辑。比如提交领养申请时,会先检查宠物的当前状态必须是“待领养”,然后将宠物状态置为“审核中”,同时创建一条领养申请记录。审核驳回时,再将宠物状态回滚为“待领养”。这段流程用事务包裹,保证“修改宠物状态”和“更新申请状态”要么都成功,要么都回滚:
from flask_sqlalchemy import get_or_404 def submit_application(pet_id, applicant_data): pet = Pet.query.get_or_404(pet_id) # 关键检查:宠物必须是待领养状态才能发起申请 if pet.status != PetStatus.PENDING: raise BusinessError("该宠物当前不可申请领养") application = AdoptionApplication( pet_id=pet.id, applicant_name=applicant_data['name'], phone=applicant_data['phone'], # ... 其他字段 status=ApplicationStatus.PENDING ) pet.status = PetStatus.REVIEWING db.session.add(application) db.session.commit() return application事务保证这块不用多说,但是实际项目中我栽过跟头:最开始没把状态检查和状态修改放进同一个事务里,好几次出现了并发提交,同一只宠物被抢注的情况。后来加上了行级锁或者使用with_for_update(),才算彻底解决。
4.2 募捐模块与资金透明度设计
募捐模块看似只是简单的 INSERT 记录,但落到业务层面就涉及到透明性和可追溯性。救助站的日常运营资金来自公众,所以募捐的记录需要让公众能看到总进度和明细,同时又要保护好捐赠人的隐私。我设计了两种展示视角:
- 公共视角(前台首页):只展示募捐活动名称、目标金额、已筹金额、进度条、捐款人次,以及每笔捐款的“昵称+金额+时间”匿名化列表。
- 管理视角(后台):能看到捐赠人完整联系方式、支付备注、记录关联的活动ID、导入操作人ID。
实现进度统计时,不要在页面端临时把捐款记录拉出来在内存里求和。数据量大时性能会很难看。正确做法是在募捐活动表里维护一个冗余字段raised_amount,每次新增捐款记录时在同一次数据库事务里更新这个字段。查询进度时直接读取,不需要聚合运算:
def create_donation(activity_id, donor_name, amount, pay_method, anonymous=False): activity = Activity.query.get_or_404(activity_id) donation = DonationRecord( activity_id=activity_id, donor_name=donor_name, amount=amount, pay_method=pay_method, is_anonymous=anonymous, donation_time=datetime.now() ) activity.raised_amount = (activity.raised_amount or 0) + amount db.session.add(donation) db.session.commit() return donation募捐金额精度的问题在测试时出现过一次很典型的情况:测试时捐了 0.1 + 0.2,结果进度显示 0.30000000000000004,后来排查发现是字段类型用了 Float。定位后立刻改成DECIMAL(10,2),同时把前端显示封装成toFixed(2)格式化。这种事情对技术来说是个小问题,但对捐赠者的信任是大事。
4.3 志愿者管理模块的双向促进
志愿者模块的设计思路是“招募-沉淀-激励”的闭环。招募是指宠物救助站定期发布线下活动志愿者招募,志愿者在系统里报名;沉淀是指每次服务完成后,管理员录入服务时长和评价,形成志愿者的累计服务档案;激励是指服务时长和参与活动数在志愿者个人中心可视化展示,同时系统会为服务时长超过一定阈值的志愿者自动生成荣誉证书标识。
数据表结构上,我设计了三张关联表:志愿者信息表(志愿者编号、服务区域、可参与时间、特长标签)、活动表(活动名称、时间、地点、需要人数、已报名人数)、活动报名关联表。管理员报名确认的逻辑要控制“活动已报名人数不能超过所需人数”,这个校验同样要加在事务里。
志愿者模块容易忽略的是服务时长统计的准确性。最开始我直接用活动报名记录的服务时长字段做 SUM 聚合,后来发现管理员在改完某个活动的时长后,累计数据更新不同步的问题。最后同样采用冗余字段方案,在志愿者信息表里加了一个total_served_hours,每次修改服务时长时自动更新该字段,查询个人档案时直接读取,既快又准。
5. 实操记录:从零搭建到前后端联调
5.1 Flask 后端项目初始化
PyCharm 新建 Flask 项目后,我习惯先把项目结构整理成业务模块化形态,而不是所有代码堆在app.py里。这里给出本项目实际使用的目录布局:
pet_adoption/ ├── app.py # 应用入口,注册所有蓝图 ├── config.py # 配置:数据库地址、密钥、上传路径 ├── models/ │ ├── __init__.py # 初始化 db 实例 │ ├── pet.py │ ├── application.py │ ├── donation.py │ └── volunteer.py ├── views/ │ ├── pet_views.py │ ├── adoption_views.py │ ├── donation_views.py │ └── volunteer_views.py ├── utils/ │ ├── response.py # 统一响应封装 │ ├── decorators.py # 登录校验装饰器 │ └── storage.py # 图片上传处理 └── requirements.txt使用蓝图(Blueprint)来组织路由是个好习惯。比如所有宠物相关的接口都注册到pet_bp蓝图上,所有领养流程接口注册到adoption_bp上。这样做的直接好处是接口多了之后不会乱,每个模块的代码可以独立维护。
依赖安装命令,按项目实际方式记录为:
pip install flask flask-sqlalchemy pymysql flask-cors flask-jwt-extended python-dotenv数据库连接放在 config.py 里统一管理:
import os class Config: SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key') SQLALCHEMY_DATABASE_URI = ( 'mysql+pymysql://root:yourpassword@localhost:3306/' 'pet_adoption?charset=utf8mb4' ) SQLALCHEMY_TRACK_MODIFICATIONS = False MAX_CONTENT_LENGTH = 5 * 1024 * 1024 # 限制上传5MB这里需要提醒的是SQLALCHEMY_TRACK_MODIFICATIONS要设成 False,不然每次操作都会额外触发对象追踪,白白消耗性能。MySQL 连接串记得指定charset=utf8mb4,否则宠物信息里如果用户输入了 Emoji 字符,储存时会报错或变成乱码。
5.2 Vue 前端搭建与代理配置
前端用 Vue CLI 创建项目后,我按页面功能拆分了目录结构,主要页面包括:宠物列表页、宠物详情页、领养申请页、募捐活动列表页、募捐详情页、志愿者报名页、后台管理页(宠物审核、领养审核、捐款登记、活动管理)。
开发阶段最值得注意的配置是前端代理。前后端分离后,本地开发时前端的调试端口通常是 8080,后端是 5000,直接跨域请求会被浏览器拦截。除了在 Flask 后端安装 Flask-CORS 之外,我更推荐在 vue.config.js 里配置 devServer 代理,让前端请求的/api前缀自动转发到后端地址:
// vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://127.0.0.1:5000', changeOrigin: true } } } };配置好代理后,前端代码里所有请求都写相对路径/api/v1/pets,不需要写全栈地址。这样既能避免开发时的跨域问题,也能让部署时不用改太多代码——只要网关把同一域名下的/api转发到后端即可。
5.3 前后端联调的关键配合
联调阶段最容易出问题的是数据格式约定。前端要的宠物列表是带分页的 JSON,后端如果只返回数组,前端就没法做分页控件。所以我在设计列表接口时统一定义为:
{ "code": 0, "data": { "total": 100, "page": 1, "page_size": 10, "items": [...] } }这个约定一定要在第一轮开始写代码前就定下来,不然两边各写各的,最后对接时改来改去非常痛苦。
联调过程中,我会用 PyCharm 的 HTTP Client 进行接口自测,每个接口先确认返回结构和字段类型正确,再交给前端对接。包括图片上传接口,前端的 FormData 提交格式和后端的request.files处理要提前协商好字段名,避免名字对不上导致请求报错。
6. 常见问题与排查技巧实录
6.1 数据库编码与中文乱码问题
这个太常见了。项目开发时很多人会在 Mac 或 Windows 本机装 MySQL,默认字符集可能是latin1或utf8,遇到中文或者特殊字符直接变成问号。解决办法是在创建数据库时指定字符集:
CREATE DATABASE pet_adoption DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;同时连接串里也要带charset=utf8mb4。如果在项目开发中已经建了库,可以用ALTER DATABASE改字符集,但最稳妥还是建库时一步到位。
6.2 Flask-SQLAlchemy 模型循环引用
项目里 Pet 和 AdoptionApplication 之间存在外键关系,最开始的写法是在模型文件里直接 import 对方,导致循环导入。解决方法是把外键关系定义放在其中一个模型里,或者三方使用字符串引用方式db.relationship('AdoptionApplication', backref='pet')。这个坑新手容易踩,排查时看控制台会有一堆莫名其妙的 import 报错,实际上就是循环导入。
6.3 图片上传与访问路径问题
宠物图片上传是刚性需求,实现时的坑在于本地存储路径和访问路径的映射。Flask 后端把图片保存到static/uploads/目录后,返回给前端的 URL 要不要带域名?开发环境可以用相对路径,生产环境就要注意静态文件的托管方式。
本项目里我采用了 Flask 自带静态文件托管的方式,配置好app.config['UPLOAD_FOLDER']之后,通过返回/static/uploads/文件名让前端直接访问。如果后续部署在 Nginx 后面,Nginx 会把静态文件请求分流,方案依然不需要改动。
另外要注意检查上传文件的扩展名和后端对文件大小的限制,防止有人上传超大文件把磁盘塞满。本项目将MAX_CONTENT_LENGTH限制在 5MB,扩展名只允许jpg/png/gif/webp。
6.4 募捐进度刷新不及时
这个之前已经提到了,是因为直接在页面端聚合累加导致的问题。后来改为维护活动表的冗余字段,并确保在同一个数据库事务里同步更新。还有一个相关的小坑:如果项目里用了定时任务去汇总捐款数据,就会存在延迟窗口,捐赠者在前端看到的进度迟迟不变,会怀疑系统有问题。实时更新冗余字段的方式虽然牺牲了一点“设计纯洁性”,但换来了对用户反馈的最快响应,这种事我建议以用户体验优先。
6.5 领养审核状态同步问题
宠物状态和领养申请状态是两套状态机,我在一个实际场景里遇到状态不同步:初审通过后,宠物状态显示“审核中”,管理员在“回访环节”录入回访结论时误把申请状态改成了“已完成”,但宠物状态没变,导致同一只宠物在列表页再次显示为“可领养”。后来给状态流转加入了联动规则:申请状态为“完成”时强制将宠物状态置为“已领养”。这套联动规则用事务和状态校验函数来保证,避免管理员手工误操作造成的脏数据。
6.6 常见问题速查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 接口返回 JSON 中文乱码 | MySQL 字符集不是 utf8mb4 | 建库时指定字符集,连接串加 charset |
| 图片上传后访问 404 | 静态目录配置错误或文件名路径拼接问题 | 确认 upload 目录存在且返回路径正确 |
| 跨域请求被浏览器拦截 | 未配置 Flask-CORS 或前端代理未生效 | 安装 Flask-CORS 或配置 devServer proxy |
| 捐款金额小数位不准 | 使用了 FLOAT 字段 | 改为DECIMAL(10,2) |
| 宠物被并发重复申请 | 状态校验和状态修改不在一个事务 | 使用with_for_update()或事务隔离 |
| Flask 启动报循环导入 | 模型互相 import | 使用字符串引用或调整导入时机 |
| Vue 请求响应慢 | 可能是后端在视图里做了耗时聚合查询 | 优先用冗余字段预计算 |
写在最后的个人体会
这个项目做下来,最大的体会是“业务规则永远比技术框架复杂”。Flask 写写接口很容易,但真正难的是把领养审批流和募捐公开透明的业务逻辑想清楚。如果一开始没有把状态机设计和数据库关系理清,后面大概率会陷入改了一处坏另一处的泥潭。
我也是踩过几次坑之后才摸索出一套比较顺的做法:先画清楚业务状态流转图,再建表,最后才写接口。别急着上来就敲代码,尤其是这种带审批流和资金记录的系统,前期多花一天梳理模型,后期能省出好几天的查错时间。
另外有个小技巧想分享:开发这类系统时建议在本地引入一个模拟数据生成脚本,批量造几千条宠物和捐款记录,用来测试分页和统计接口的性能表现。很多问题在数据量小的时候看不出来,数据一大,聚合查询慢、冗余字段不一致这些问题就会集中爆发,早点用模拟数据压一遍,比上线后被动修复要省心得多。
如果后续这个系统要继续扩展,我建议的方向是做移动端适配或者干脆套一层小程序壳,因为救助站的很多工作人员和志愿者是在手机上处理日常事务的。再往后还可以接一个简单的公告推送,比如新宠物入站时按关注用户的筛选条件做消息触达。这套 Flask + Vue 的基础架构在支撑这些扩展上是完全够用的。