news 2026/9/16 19:40:04

Tandoor Recipes 全功能解析:开源菜谱管理、膳食规划与数据导入导出实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Tandoor Recipes 全功能解析:开源菜谱管理、膳食规划与数据导入导出实战指南

Tandoor Recipes 全功能解析:开源菜谱管理、膳食规划与数据导入导出实战指南

【免费下载链接】recipesApplication for managing recipes, planning meals, building shopping lists and much much more!项目地址: https://gitcode.com/GitHub_Trending/re/recipes

导读

本文以仓库主页文档 docs/index.md 为骨架,系统梳理 Tandoor Recipes 这一开源菜谱管理系统的完整能力:从菜谱管理、膳食计划、购物清单等核心功能,到全文搜索、标签系统、批量导入等进阶特性,并深入展开其引用的导入导出体系(对应 docs/features/import_export.md)。读者读完将掌握 Tandoor 的部署形态、功能边界、数据迁移到 Tandoor 的完整操作流程,以及导入导出模块的源码级实现原理。

Tandoor Recipes 项目主页展示的应用界面预览,覆盖菜谱浏览、搜索与组织等核心场景。

一、Tandoor Recipes 是什么:项目定位与核心目标

Tandoor Recipes 是一款"管理不断增长的数字化菜谱收藏"的开源应用,其定位明确:面向拥有大量菜谱、希望与家人朋友分享或有序收藏的个人用户与家庭。项目主页同时强调了一个重要边界——应用带有基础权限系统,但不打算作为公开网站对外运行(见 docs/index.md)。

从仓库结构可以确认其技术栈为典型的现代 Web 应用:

  • 后端:Django + Django REST Framework(入口配置见 recipes/settings.py、recipes/urls.py),核心业务模型集中在 cookbook/models.py;
  • 前端:Vue 3 单页应用(源码位于 vue3/src),配合 Vuetify 组件库(vue3/src/vuetify.ts);
  • 部署形态:官方推荐 Docker 容器化部署,仓库同时提供 Kubernetes、Unraid、Synology 等平台的示例与文档(见 docs/install 目录下的 docker.md、kubernetes.md、unraid.md、synology.md)。

项目主页将其定位概括为"由高级用户打造、为高级用户服务"(Made by and for power users),下文将逐项展开其核心能力。

二、核心功能:从菜谱到餐桌的一站式闭环

2.1 菜谱管理(Manage your recipes)

Tandoor 的核心是快速、直观的菜谱编辑器。从数据模型(cookbook/models.py)看,每条 Recipe 承载了完整的结构化信息:名称、描述、准备/等待时间、份数与份数文本、营养信息(按份或按菜谱计量)、步骤(Step)、食材(Ingredient)以及关键词标签(Keyword)等,足以支撑从菜谱录入到精细检索的全链路。

2.2 膳食计划(Plan multiple meals for each day)

应用支持每天规划多餐,MealPlan 数据模型将日期、膳食类型(MealType,如早餐/午餐/晚餐)与菜谱关联,为后续生成购物清单提供直接输入。

2.3 购物清单(Shopping lists)

购物清单存在两种生成路径:经由膳食计划自动汇总,或直接从菜谱添加。这一设计让"计划吃什么"与"去超市买什么"形成闭环。仓库中还扩展了超市(Supermarket)分组、食材忽略(ignore_shopping)等精细控制(相关模型与迁移可见 cookbook/models.py 与 cookbook/migrations 中 0075 之后的多次演进)。

2.4 食谱书(Cookbooks)

食谱书将散落的菜谱收集成书,RecipeBook 与 RecipeBookEntry 模型支持按顺序组织条目(cookbook/migrations/0239_add_ordering_property_recipebookentry.py),方便建立自己的"主题合集"。

2.5 分享与协作(Share and collaborate)

项目支持与家人朋友共享菜谱并协作:用户(User)、空间(Space)、邀请链接(InviteLink)等模型共同构成了多用户隔离与权限基础;同时,开发者明确提示项目"带基础权限系统,但不适合作为公共站点运行"。

2.6 更多实用特性(All the must haves)

主页还罗列了一组高频需求特性:

  • 移动端优化:前端为移动设备做了适配,随时随地记录与查询;
  • 多语言本地化:仓库 recipes/locale 与 cookbook/locale 下收录了 30+ 语言的翻译文件(.po/.mo),并由社区持续维护;
  • 菜谱缩放:按份数自动换算食材用量;
  • 图片压缩:图片处理集中在 cookbook/helper/image_processing.py,导入时自动处理;
  • 打印视图:为纸质归档优化的打印页面;
  • 超市管理:购物清单可按超市维度整理。

三、面向高级用户的深度能力(Made by and for power users)

3.1 强大且可定制的全文搜索(TrigramSimilarity)

Tandoor 的搜索基于 PostgreSQL 全文检索,并叠加TrigramSimilarity(三元组相似度)支持模糊匹配——即使拼写有偏差也能召回结果。搜索相关实现位于 cookbook/helper/recipe_search.py,配套的测试覆盖见 cookbook/tests/other/test_recipe_search_* 系列。

3.2 标签系统与批量操作

用户可以创建、搜索标签,并按过滤条件批量给所有匹配菜谱打标签。标签在数据模型中体现为树状结构 Keyword(相关迁移为 0147_keyword_to_tree,见 cookbook/migrations/0147_keyword_to_tree.py),导入的菜谱也会被自动归入对应的 "Import N" 标签树,方便追溯来源。

3.3 快速合并与重命名

针对食材、标签和单位,Tandoor 提供快速合并与重命名能力,帮助清理历史数据中的重复与不一致,是长期使用者的高频效率工具。

3.4 从网页批量导入菜谱

支持从成千上万个网站抓取菜谱,前提是页面提供 schema.org 的 Recipe,文档说明见 docs/features/external_recipes.md。

3.5 分数与小数双格式

应用同时支持**分数(fractions)或小数(decimals)**两种食材计量显示,用户可通过偏好设置切换(对应 UserPreference 中的 use_fractions 等字段)。

3.6 界面主题定制

通过主题(themes)机制自定义界面外观,仓库内置浅色/深色主题(cookbook/static/themes),可满足不同使用环境与视觉偏好。

3.7 云存储同步

支持与Dropbox 和 Nextcloud同步文件。同步层由 provider 抽象实现(cookbook/provider 目录下的 dropbox.py、nextcloud.py、local.py),并提供命令行同步入口(cookbook/management/commands/rebuildindex.py 所在的 management/commands 目录)。

3.8 云端 AI 能力(当前版本扩展)

除主页所述功能外,当前仓库 README(README.md)还列出了 AI 辅助能力:识别图片、整理菜谱步骤、查找营养信息等,相关模型与配置见 cookbook/migrations/0224_space_ai_credits_balance_space_ai_credits_monthly_and_more.py 等迁移文件与 cookbook/helper/ai_helper.py。

四、数据导入导出体系:数据自由进出的核心通道

主页将"从其他菜谱管理器导入收藏"列为高频需求,官方为此构建了以插件式集成(Integration)为核心的导入导出体系。这也是全项目中文档最翔实的模块之一(docs/features/import_export.md),下面做源码级展开。

4.1 架构:Integration 基类与统一分发

所有格式的导入导出都继承自同一个抽象基类Integration(cookbook/integration/integration.py)。基类定义了统一的职责边界:

  • 导入侧:get_recipe_from_file()将任意文件对象转换为 Recipe、split_recipe_file()将包含多条菜谱的文件拆分为列表;
  • 导出侧:get_file_from_recipe()/get_files_from_recipes()将 Recipe 对象序列化为文件内容,并在多文件时打包为 ZIP;
  • 公共能力:自动创建 "Import N" 标签归类本次导入、基于菜谱名查重的handle_duplicates()、图片处理import_recipe_image()等。

具体格式的分发由工厂函数get_integration(request, export_type)完成(cookbook/views/import_export.py),而支持的格式清单定义在表单基类ImportExportBase中(cookbook/forms.py)。仓库 cookbook/integration 目录下目前维护着 27 个具体集成实现,覆盖主流的菜谱管理与桌面/移动应用。

实现事实:虽然ImportExportBase中仍保留PDF = 'PDF'常量以兼容数据库迁移,但get_integration()对 PDF 类型直接抛出NotImplementedError(cookbook/views/import_export.py),注释明确说明 pyppeteer 依赖已被移除——如需将菜谱存为 PDF,请使用浏览器自带的打印功能(Ctrl+P / Cmd+P)。

4.2 各集成能力总览

下表完整列出各集成当前的能力状态(✔️ = 已实现,❌ = 未实现且不计划/不可行,⌚ = 尚未实现):

集成导入导出图片
Default✔️✔️✔️
Nextcloud✔️✔️
Mealie✔️✔️
Chowdown✔️✔️
Saffron✔️✔️
Paprika✔️✔️
ChefTap✔️
Pepperplate✔️
RecipeSage✔️✔️✔️
Rezeptsuite.de✔️✔️
Domestica✔️✔️
MealMaster✔️
RezKonv✔️
OpenEats✔️
Plantoeat✔️✔️
CookBook Manager✔️✔️
Cooklang✔️
CopyMeThat✔️✔️
Mela✔️✔️
Cookmate✔️✔️
PDF (experimental)⌚️✔️✔️
Gourmet✔️✔️
Pestle✔️✔️

说明:官方明确采用"导入优先于导出"的策略——对大多数用户而言,"把既有收藏迁移进 Tandoor" 是第一优先级,因此各格式的导出能力(⌚)将随开发进度陆续补齐。WIP 提示:导入导出模块相对较新,大规模导出曾存在超时问题,正在修复中。

4.3 官方首选:Default 集成(Tandoor 原生格式)

Default 集成是内置且优先推荐的导入导出方式(实现见 cookbook/integration/default.py):

  • 它随新字段持续维护,包含将菜谱从一套 Tandoor 安装迁移到另一套所需的全部数据
  • 其数据采用结构化 JSON,便于机器读取或用于其他用途;
  • 导出时每条菜谱被封装为独立的<recipeId>.zip(内含recipe.json与可选image.*),全部菜谱再打包为一个总 ZIP;导入端逐层解包并读取recipe.json

重要约束:使用 Default 导入时,必须上传 Tandoor 导出的.zip归档文件。仅上传解压后的.json文件不被支持,可能导致导入错误;跨实例迁移请直接使用 ZIP 归档、不要先行解压。

从源码看,Default 导入通过RecipeExportSerializer(cookbook/serializer.py)完成反序列化与落库,该序列化器覆盖菜谱名称、描述、关键词、步骤、工作/等待时间、营养信息、份数与来源 URL 等核心字段。

4.4 各第三方格式导入实操指南

RecipeSage

在 RecipeSage 中进入Settings > Export Recipe Data,选择EXPORT AS JSON-LD (BEST),将导出文件直接上传到 Tandoor 导入。RecipeSage 集成同时支持导出:在 Tandoor 中选择 RecipeSage 类型导出,再把 json 文件导入 RecipeSage 即可实现反向迁移(注意:导出暂不支持图片)。

Domestica

在 Domestica 的Import/Export中选择Export Recipes,将导出的文件上传到 Tandoor。

Nextcloud Cookbook

Nextcloud Cookbook 提供规范、结构化的菜谱信息,导入后大多数字段可完整保留。操作步骤:

  1. 登录 Nextcloud Web 界面;
  2. 找到Recipes文件夹(通常位于账号根目录);
  3. 下载该文件夹,得到包含Recipes目录的Recipes.zip,其中每个菜谱一个子目录;
  4. Recipes.zip上传到 Tandoor 导入。

目录结构要求(若未使用标准路径或自定义压缩方式,务必保证结构一致):

Recipes.zip/ └── Recipes/ ├── Recipe1/ │ ├── recipe.json │ └── full.jpg └── Recipe2/ ├── recipe.json └── full.jpg
Mealie

Mealie 提供与 Nextcloud 类似的结构化数据。迁移步骤:

  1. 进入 Mealie 管理后台创建备份(注意:仅从数据管理区导出菜谱数据会导致导入不完整);
  2. 下载备份文件;
  3. 将整个.zip上传到 Tandoor 导入器。

需要注意两点:Mealie 存在两个版本导入器——1.0 之前的备份1.0 及之后的备份分别对应不同的导入实现(cookbook/integration/mealie1.py 与 cookbook/integration/mealie.py);此外,Mealie 界面不会标明营养信息是按"每份"还是"每菜谱"存储,导入时需由用户指定 Tandoor 应如何处理营养数据。

Chowdown

Chowdown 将菜谱存储在纯文本 Markdown 文件中,目录名为_recipes,图片存放在images目录。将这两个目录打包为.zip后导入即可:

Recipes.zip/ ├── _recipes/ │ ├── recipe one.md │ ├── recipe two.md │ └── ... └── images/ ├── image-name.jpg ├── second-image-name.jpg └── ...

兼容性细节:Chowdown 使用带下划线的_recipes目录,为避免混淆,导入器同时支持_recipesrecipes两种写法。

Saffron

在 Saffron 设置页导出菜谱,将整个.zip上传到 Tandoor 即可。注意:Saffron 导出不含图片,图片将在导入过程中丢失。

Paprika

Paprika 的导出包含一个 HTML 表示的文件夹和一个.paprikarecipes文件——该文件本质上是 gzip 压缩的 ZIP。直接上传整个文件并导入全部菜谱即可。

Pepperplate

Pepperplate 提供包含全部菜谱.txt文件的.zip包,这些文件结构良好,可无损导入所有数据。从 Pepperplate 导出后上传 ZIP 即可;导出不含图片,因此图片无法导入

ChefTap

ChefTap 的导出是包含cheftap_export文件夹的 ZIP,其中是菜谱.txt文件。由于该格式基本无结构化规范、每次导出形态各异,导入质量有限;通常导入器能识别出食材,其余内容归入步骤。图片不在导出内容中,因此不支持。由于 ChefTap 本身无法导入这类文件,Tandoor 也不会实现其导出器。

MealMaster

MealMaster 支持一次上传一个或多个文件,格式可为.txt.MMF.MM。目前仅支持单列格式的食材;食材的第二行注释不会被导入为注释,而是并入步骤文本。无法导入的 MealMaster 文件可提交 Issue 反馈。

RezKonv(RezKonv Suite)

该格式主要用于德国菜谱管理器 RezKonv Suite。迁移方式:在 RezKonv Suite 中选择Export > Gesamtes Kochbuch exportieren(导出菜单最后一项),将生成的文件直接导入 Tandoor。

Recipe Keeper

Recipe Keeper 可通过其应用导出包含菜谱与图片的 ZIP 文件,直接导入 Tandoor。

OpenEats

OpenEats 界面不提供导出功能,但可在命令行导出。在 OpenEats API 容器内执行:

python manage.py dumpdata recipe ingredient

若采用默认安装方式,可运行:

docker-compose -f docker-prod.yml run --rm --entrypoint 'sh' api ./manage.py dumpdata recipe ingredient

或:

docker exec -it openeats_api_1 ./manage.py dumpdata recipe ingredient rating recipe_groups > recipe_ingredients.json

将输出的 JSON 字符串保存为.json文件并上传导入,格式大致如下:

[ { "model":"recipe.recipe", "pk":1, "fields":{ "title":"Tasty Chili", ... } }, ... { "model":"ingredient.ingredientgroup", "pk":1, "fields":{ "title":"Veges", "recipe":1 } }, ... { "model":"ingredient.ingredient", "pk":1, "fields":{ "title":"black pepper", "numerator":1.0, "denominator":1.0, "measurement":"dash", "ingredient_group":1 } } ]

图片导入:在 Tandoor 的recipes媒体目录(通常为/opt/recipes/mediafiles)下创建openeats-import文件夹,将 OpenEats API 容器中/code/site-media/upload目录复制进去,最终应得到/opt/recipes/mediafiles/recipes/openeats-import/upload/...路径结构。

Plan to Eat

Plan to Eat 可导出包含全部菜谱的文本文件,直接上传该文本文件即可导入所有菜谱。

CookBook Manager(CookBookApp)

CookBook Manager 可导出包含 YAML 文件的.zip包:在Settings -> Backup中选择 YAML 选项导出,将整个 ZIP 上传到 Tandoor。

CopyMeThat

CopyMeThat 可导出包含一个.html文件与图片文件夹的.zip包,上传整个 ZIP 导入全部菜谱。

Cookmate

Cookmate 可导出.mcb文件,直接上传到 Tandoor 导入全部菜谱。

Cooklang

Cooklang 支持导入单个.cook文件(仓库配套测试见 cookbook/tests/other/test_cooklang_integration.py)。目前尚不支持附加图片或导入 ZIP 文件

RecetteTek

RecetteTek 导出为.rtk文件,直接上传到 Tandoor 导入全部菜谱。

Rezeptsuite.de

Rezeptsuite.de 导出为.xml文件,直接上传导入。部分客户端可能导出包含.cml文件的.zip包——此时只需解压.zip并把.cml改名为.xml即可导入。

Mela

Mela 提供多种导出格式,但只有MelaRecipes格式能导出完整收藏。操作流程:执行该导出得到.melarecipes文件后,用解压工具(如 7-Zip)打开;若其中仍包含.melarecipes文件则继续解压,直到获得一个或多个.melarecipe文件;将需要导入的.melarecipe文件全部上传到 Tandoor 并开始导入。

PDF

基于 pyppeteer 的 PDF 导出已被移除。如需将菜谱保存为 PDF,请使用浏览器内置打印功能(Ctrl+P / Cmd+P)。

Gourmet

导入器针对 Gourmet(.grmt文件缺少食材单位的问题)设计为接收包含.htm.jpg.zip包:在 Gourmet 中导出为 HTML 并压缩生成文件夹。不支持导入菜单;由于.grmt格式存在问题,也不支持导出。

Pestle

从 Pestle 应用设置中点击 "Export",选择 "Pestle Recipe Format",得到mm-dd-yyyy.pestle文件用于导入。可导入的信息包括:名称、描述、份数、等待时间、工作时间、分类(关键词)、标签、食材(作为附加步骤)、步骤、营养、图片与来源。其他字段(评分、作者、备注、菜系、视频)将丢失。导出不支持,因为 Pestle 需要图片 URL,而导入数据中不包含。

4.5 ZIP 导入的安全边界与性能参数

由于多数格式以 ZIP 为载体,基类Integration(cookbook/integration/integration.py)内置了完整的解压安全防护,对应环境变量定义于 recipes/settings.py:

环境变量默认值作用
MAX_ZIP_FILE_COUNT2000ZIP 内最大文件数量
MAX_ZIP_TOTAL_SIZE500(MB)解压后总大小上限
MAX_ZIP_FILE_SIZE10(MB)单个文件解压后大小上限
MAX_ZIP_NESTING_DEPTH2嵌套 ZIP 的最大层数

超过任一限制,导入器会中止并给出明确错误提示,有效防止"zip bomb"式恶意或误操作文件拖垮服务器。

4.6 命令行导入导出

除 Web 界面外,仓库还提供两个 Django management 命令:manage.py importmanage.py export(见 cookbook/management/commands/import.py 与 cookbook/management/commands/export.py)。二者分别继承 Django 内置的loaddata/dumpdata命令,并额外以scopes_disabled包装,从而绕过空间的django-scopes隔离限制,方便在脚本化迁移、备份恢复等场景下跨空间读写数据。

五、部署与运维要点

Tandoor 强调"使用 Docker 轻松搭建",仓库提供的部署资源包括:

  • Docker Compose 示例:见 docs/install/docker.md,以及docs/install下针对反向代理(nginx、traefik、apache)等场景的 compose 模板;
  • Kubernetes:完整 K8s 资源清单位于 docs/install/k8s(ConfigMap、Secret、PVC、StatefulSet、Deployment、Ingress 等);
  • Unraid / Synology:分别提供安装指南(docs/install/unraid.md、docs/install/synology.md);
  • Nginx 配置示例:仓库自带 nginx/conf.d/Recipes.conf 与模板文件 http.d/Recipes.conf.template。

典型部署依赖 PostgreSQL 数据库(全文检索与 TrigramSimilarity 依赖其能力),前端为 Vue 3 编译产物,由后端静态托管。生产环境建议按 docs/system/configuration.md 配置环境变量,其中上文提到的MAX_ZIP_*系列与EXPORT_FILE_CACHE_DURATION(默认 600 秒,控制导出文件的缓存时长,recipes/settings.py)均在此统一管理。

六、项目现状与路线图

主页文档(docs/index.md)坦诚地说明了项目所处阶段:应用在过去一年经历了快速迭代,引入了 Vue.js 等新技术,功能大量扩张的同时,部分细节与技术实现的品质仍有提升空间。因此,除持续更新的功能与里程碑(见仓库 Issues & Milestones)外,未来主要目标包括:

  • 改善 UI:当前设计风格不一致,许多页面可用但观感欠佳;
  • 拥抱开放数据与开放系统:为所有相关菜谱管理系统补齐导入器与导出器;
  • 前端工程化清理:将所有 JavaScript 库迁移到包管理器统一管理,清理早期积累的技术债;
  • 提升测试质量与覆盖率:改进既有测试并扩充用例;
  • 完善文档:为全部功能与使用场景补齐文档,并增加应用内帮助。

项目最初的诞生动机是"索引、打标签和搜索个人收藏的数字化(PDF)菜谱",随着功能持续叠加,如今已成长为功能全面的菜谱管理系统。

结语

Tandoor Recipes 的价值在于把"菜谱收藏 → 膳食计划 → 购物清单 → 分享协作"这条生活化链路完整打通,同时以插件式集成架构(cookbook/integration)对 20 余种第三方格式提供导入能力,让用户的既有数据资产可以低成本迁移。对于希望深入了解实现细节的读者,推荐继续阅读:

  • 导入导出全量文档:docs/features/import_export.md;
  • 网页抓取导入文档:docs/features/external_recipes.md;
  • 集成基类实现:cookbook/integration/integration.py;
  • 原生格式序列化:cookbook/serializer.py(RecipeExportSerializer);
  • 各平台部署指南:docs/install。

【免费下载链接】recipesApplication for managing recipes, planning meals, building shopping lists and much much more!项目地址: https://gitcode.com/GitHub_Trending/re/recipes

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

CSRF本质是浏览器信任机制的副产品

1. CSRF不是“跨站请求伪造”的缩写&#xff0c;而是浏览器信任机制的意外副产品很多人一看到CSRF就条件反射背出那句教科书定义&#xff1a;“Cross-Site Request Forgery&#xff0c;跨站请求伪造”。但这句话本身已经埋下了理解偏差的种子——它把CSRF描述成一种“主动攻击行…

作者头像 李华
网站建设 2026/9/16 19:39:17

自考学习AI工具全攻略:8大高效应用与避坑指南

1. 自考学习中的AI工具应用现状作为一名自考过来人&#xff0c;我深刻理解自考生面临的三大困境&#xff1a;时间碎片化、资料繁杂、缺乏学习监督。近年来AI工具的爆发式发展为自考学习带来了全新可能&#xff0c;但同时也出现了工具选择困难、使用效率低下等新问题。根据2023年…

作者头像 李华
网站建设 2026/9/16 19:39:13

HBase Shell命令全攻略:从建表、数据读写到运维实战

做HBase开发或者运维的人&#xff0c;应该都有过这种经历&#xff1a;查资料看官方文档&#xff0c;满屏的英文命令看得头晕&#xff1b;要么就是上网搜教程&#xff0c;结果搜出来的文章要么太浅&#xff0c;只贴个命令不给解释&#xff0c;要么太散&#xff0c;东一块西一块根…

作者头像 李华
网站建设 2026/9/16 19:37:54

Typora个性化设置全攻略:主题换肤、CSS定制与图片路径管理

Typora 的默认界面&#xff0c;第一眼确实干净&#xff0c;但真拿它当主力写作工具用上两三个月&#xff0c;你多半会跟我一样冒出几个念头&#xff1a;代码块背景能不能再深一点&#xff1f;标题颜色这个蓝色实在晃眼&#xff1b;插图老被复制到乱七八糟的目录里&#xff1b;导…

作者头像 李华
网站建设 2026/9/16 19:37:20

BWAPP靶场SQL注入通关实战:从联合查询到盲注绕过

提起Web安全入门&#xff0c;绕不开的就是SQL注入&#xff1b;绕不开SQL注入练习&#xff0c;自然就绕不开BWAPP这个靶场。我刚入行那会儿&#xff0c;白天看了一堆SQL注入的原理文章&#xff0c;晚上回去就想找个地方练手。DVWA的界面太老&#xff0c;Pikachu的题目量少&#…

作者头像 李华