尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MiroFish实战:从零构建钓鱼记录与鱼情预测工具

MiroFish实战:从零构建钓鱼记录与鱼情预测工具 1. 项目背景钓鱼记录这件事为什么值得单独做个工具1.1 钓鱼老手最需要的是一面“记录镜子”MiroFish 这个名字我解释过很多次了。Miro 取 mirror 的意思给自己每一次出钓留一面镜子。FISH 不用多说钓鱼人一眼就懂。现在它是我手机里使用频率最高的工具之一而当初做它的时候纯粹是被自己坑怕了——钓鱼这项爱好最大的隐形敌人不是没口而是“记不住”。你看今天在哪里下竿、用的什么饵、水温多少、气压多少、鱼在几点开口隔两周再回忆基本只剩一个模糊印象“上次好像在河边钓得还行”。这种模糊记忆积累三年五年你会发现自己的钓技提升特别慢因为缺少可复盘的数据。钓鱼圈常说一句话钓龄不等于钓技只有被记录下来的经验才会复利生长。MiroFish 就是冲着这个痛点去的。它能做什么一句话总结把每次出钓变成一条结构化记录沉淀钓点、天气、温度、气压、饵料、鱼获等关键变量再基于这些数据反哺下次出钓建议。适合谁一是喜欢野钓、水库、黑坑的台钓手二是想系统进步但不想折腾笔记本记录的新手三是已经用过钓鱼社区但觉得信息太碎的人。1.2 现成工具的痛点我全踩过一遍动手之前我先把市面上主流钓鱼类应用翻了一遍结论是有两类工具各自缺一块关键拼图。第一类是社区社交型特点是钓点种草、渔获晒图、评论区求位置。这类平台内容很热闹但数据密度太低——一条动态就一张渔获照加一句“今天爆护”气压水温什么的全都没有复盘价值接近零。第二类是资讯工具型有天气、有潮汐表、有钓技文章可它跟你个人的出钓行为完全不关联不会告诉你“这个钓点你在上次降温时打龟了”。所以我对 MiroFish 的定位非常明确不做社区不做资讯门户只做一个钓鱼人自己的“出钓数据库”。它的核心价值不在于让你看到别人的鱼获而在于让你看清自己的数据。市面上的工具把精力花在让你分享我把精力花在让你记录。1.3 我给自己定的三条设计原则在动工之前我给自己立了三条规矩后面所有功能决策都以此为准。第一条记录要快最好十秒内完成。钓鱼人手上又水又泥又饵料表单但凡复杂一点第二次就不想打开了。第二条离线可用优先。水库河边很多地方信号差不能做强依赖网络的交互。第三条不搞社交关系链。点赞、评论、关注这些功能一概不做避免自己把精力耗在运营而不是产品上。这三条原则帮我在后续做了一堆减法。比如有段时间我想做“附近钓友实时位置”一想到第二条原则就直接砍了。后来事实证明这个决策是对的工具类项目功能越克制留存反而越好。2. 整体方案设计与数据模型2.1 功能模块怎么拆才不失控MiroFish 的第一个版本我只做了四个模块再多的需求全部丢进“以后再说”清单。这四个模块分别是出钓记录、钓点库、鱼情预测、简单统计。记录模块负责把每次出钓的关键变量沉淀下来钓点库负责空间位置和钓点信息管理预测模块负责基于气象数据给一个出钓建议统计模块负责回答“哪个钓点对我最友好”“哪种饵料上鱼率更高”这类问题。为什么先做这四个因为前两个解决数据有没有后两个解决数据怎么用。社区、排行榜、装备库这些功能等数据攒够三个月再聊也不迟。很多人开发个人项目时容易犯一个毛病就是希望第一个版本功能特别全结果每个功能都做得半生不熟。我这次刻意控制范围就是要快速跑通“记录—存储—展示—建议”的闭环。2.2 技术栈选型的真实原因技术选型上我走的是一条非常保守的路线。客户端用 uni-app一套代码能跑 iOS、Android 和微信小程序理由很现实钓鱼人手机型号五花八门有的用苹果有的用安卓千元机还有不少人习惯小程序拉起。为每个端单独写原生代码成本太高uni-app 的跨端能力虽然偶尔有渲染差异但整体可控。后端选了 Node.js Express部署在一个轻量云服务器上进程管理用 PM2。数据库用 SQLite数据量到几万条完全没压力而且零配置个人项目不用动不动就上 MySQL。地图服务接高德天气数据接和风天气的免费开发版。整条链路没有用到任何重架构组件好处是维护成本极低我一个人凌晨两点改 bug 也毫无压力。不少朋友问为什么不用更“现代”的方案比如微服务、容器编排这些。我的回答很直接个人工具项目的核心诉求是能把功能跑起来、数据不丢、维护简单而不是架构简历好看。技术栈越重迭代就越慢钓鱼记录这类轻量工具根本没必要上重量级武器。2.3 出钓记录与钓点的表结构设计数据模型是整个项目的根基我在这里反复调整过好几版最后沉淀下来的表结构长这样-- 钓点表 CREATE TABLE fishing_spots ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, -- 钓点名称允许手动命名 longitude REAL NOT NULL, -- 经度 latitude REAL NOT NULL, -- 纬度 water_type TEXT, -- 水域类型江河/湖库/黑坑/海钓 description TEXT, -- 环境描述比如水草、走水、水深 created_at TEXT DEFAULT (datetime(now,localtime)) ); -- 出钓记录表 CREATE TABLE fishing_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, spot_id INTEGER NOT NULL, -- 关联钓点 fish_species TEXT NOT NULL, -- 鱼种 fish_size REAL, -- 体长单位 cm fish_weight REAL, -- 重量单位 kg bait TEXT, -- 饵料/窝料说明 weather TEXT, -- 天气现象晴/阴/小雨等 temperature REAL, -- 气温℃ pressure REAL, -- 气压hPa wind_level INTEGER, -- 风力等级 start_time TEXT NOT NULL, -- 出钓开始时间 end_time TEXT NOT NULL, -- 结束时间 total_catch INTEGER DEFAULT 0, -- 总渔获尾数 note TEXT, -- 自由备注 created_at TEXT DEFAULT (datetime(now,localtime)) );这里有个容易忽略的设计细节我在出钓记录里冗余了天气、温度和气压字段而不是单独建一张气象表去关联。为什么因为历史天气数据如果靠查询某个气象服务去回补不仅慢还经常对不上。每次记录时把现场实际天气直接存进记录本身查询统计的时候就非常省事这就是典型的“以空间换时间”思路在个人项目里特别划算。另外每条记录必须关联一个钓点结构上强制避免“无地点记录”。有了这个约束后面做钓点维度的统计就不用考虑空值问题。3. 核心功能实现记录、标注与预测3.1 定位与钓点标注怎么做才不漂钓鱼人去的地方普遍比市区难定位河边、水库边 GPS 信号经常漂移有时同一位置两次定位能差出二三十米。如果直接把第一次定位结果存成钓点坐标后面应用就很容易把钓点标到水中间去。我在这个环节踩坑最深最后总结出一套组合方案。第一定位要连续取多次做均值滤波。我在客户端连续获取五个定位点去掉明显偏离的野值再取经纬度均值。代码逻辑大致是这样async function locateSpot() { const points []; for (let i 0; i 5; i) { const p await getCurrentPosition(); points.push({ lng: p.longitude, lat: p.latitude }); await sleep(300); } // 简单去野值剔除与中位数差超过 0.0002 度的点 const filtered removeOutliers(points); return averageOf(filtered); // 返回均值坐标 }第二钓点允许手动微调。地图上拖一下红点比反复定位十次都快这是我从实际使用里得出的真实结论自动定位负责快速定位到大概区域手动纠偏负责精确。第三逆地理编码返回的往往是“XX路XX号”对河边野钓点一点用没有所以钓点名称必须允许用户手动编辑“老李屋后芦苇荡”这种名字比任何地图 POI 都管用。3.2 鱼情预测的加权评分实现一提到预测很多人第一反应是上机器学习。我明确告诉你对一个钓鱼工具来说最简单的加权评分模型就已经有非常可用的效果了关键是权重分配要贴近真实经验。我最终用的评分公式是综合评分 温度分 × 0.30 气压分 × 0.25 风力分 × 0.20 时段分 × 0.15 季节分 × 0.10每一项都映射到 0~100 分再按权重加权最后归一化成 1~5 星。具体规则我列在下面分项评分依据分档温度分15~25℃ 记 100 分5~15℃ 或 25~30℃ 记 60 分低于 5℃ 或高于 30℃ 记 30 分0~100气压分1008~1023 hPa 且近 3 小时平稳记 100 分24 小时内下降超过 5 hPa 记 40 分0~100风力分1~3 级记 100 分4~5 级记 60 分6 级以上记 20 分0~100时段分凌晨 4~8 点、傍晚 17~20 点记 100 分其他时段按比例折算0~100季节分春秋两季记 100 分夏季 80 分冬季 50 分0~100这套模型的经验在于它不追求“每次都预测准”而追求“出钓条件相对排序准”。比如这周七天里系统说周六的评分最高这个判断比“今天一定爆护”要靠谱得多。我实测下来在有三年以上钓龄的朋友那里认可度较高因为他们会结合自己的经验判断模型只是帮他们排除明显不适合出钓的日子。3.3 统计口径与 SQL 聚合统计模块看起来简单但口径定义一致性能少很多麻烦。我统一用这几个指标单次出钓鱼获尾数、平均单尾重量、爆护率单次超过 5 斤或超过 10 尾、打龟率渔获为 0 的次数占比。这些指标在记录结构确定之后就很容易用 SQL 聚合。SELECT spot_id, COUNT(*) AS total_trips, ROUND(AVG(total_catch), 2) AS avg_catch, SUM(CASE WHEN total_catch 0 THEN 1 ELSE 0 END) AS blank_trips, ROUND(AVG(fish_weight), 2) AS avg_weight FROM fishing_records GROUP BY spot_id ORDER BY avg_catch DESC;统计结果展示在一块“我的数据”页面上用最朴素的柱状图列出去过最多的钓点、上鱼最多的鱼种、使用次数最多的饵料。没有花哨的可视化也没有词云热力图这一类点缀功能第一版做出来就是这么素但能直接回答最重要的那个问题下次出钓我应该去哪里、用什么。4. 完整实操从零搭起 MiroFish4.1 后端环境搭建与依赖安装如果你也想照着搭一套我给你一个完整可复现的流程。先说环境我本机是 Windows 11装了 VSCode 和 HBuilderX云服务器是 Ubuntu 22.04Node.js 用的是 18 LTS 版本。后端项目初始化如下mkdir mirofish-server cd mirofish-server npm init -y npm install express better-sqlite3 cors axios这里我特意用 better-sqlite3 而不是 sqlite3。原因是 better-sqlite3 提供同步 API写业务代码时不用嵌套回调或 async/await 满天飞对一个小型个人项目来说开发效率高得多。数据库文件直接放在项目目录下备份时把文件拷走就行非常省心。依赖装好之后顺手把 express 的骨架搭起来并在 app.js 里注册 cors 中间件否则后面前端调试跨域问题会让人崩溃。4.2 后端接口设计与入参校验我把接口切得很克制总共就 6 个 RESTful 接口POST /api/spots 新增钓点 GET /api/spots 获取钓点列表 POST /api/records 新增出钓记录 GET /api/records 按钓点/时间/鱼种筛选 PUT /api/records/:id 修改出钓记录 DELETE /api/records/:id 删除出钓记录 GET /api/weather/forecast 获取天气与出钓评分写接口的时候我吃过不少脏数据的亏最典型的是鱼体重填成字符串、气压字段漏填导致前端显示 NaN。所以我给每个接口加了一层手工校验中间件不引入 joi 这类库就用 if 判断代码更直白。校验的规则也不复杂数字字段必须 isFinite必填字段不能为空经纬度必须在合法范围内。就这几行判断把 90% 的脏数据挡在了数据库门外。4.3 前端页面的三个体验关键点前端我用 uni-app 实现页面一共四张首页大按钮页、记录填写页、钓点地图页、我的数据页。这里我只讲三个最影响体验的细节。第一记录填写页不要一屏堆满字段要做分步表单。第一步选钓点系统自动匹配半径 100 米内已有钓点第二步填鱼种、体长、重量和饵料第三步补天气和备注。分步表单虽然步骤多但每一步都足够简单实际填写完成率远高于一屏长表单。第二地图页要有“只看我去过的钓点”和“看全部标注点”两个模式前者是个人数据后者可以拉到区域内大家共享的公开标注。第三首页就是一个占据屏幕三分之一的“”号按钮因为“出钓后快速记录”才是最高频诉求手上都是水、泥和饵料的时候按钮不大真的点不准。4.4 一次新增出钓记录的完整链路我把“新增出钓记录”这个按钮背后发生的事完整拆一遍你就能理解整个系统是怎么协作的。用户点击按钮后客户端先调起定位连续取五个经纬度点做均值滤波然后把结果和已有钓点做距离匹配如果在 100 米内直接带出钓点名称否则提示用户新建钓点。接下来天气数据不是直接取当前实时数据而是给用户一个时间选择器默认是当前时间但允许回填到实际出钓时段。这个细节非常重要因为很多人是上午钓鱼晚上回家才记录如果取记录时刻的天气历史数据里的温度和气压就和实际出钓对不上了。我的做法是用户选定时间后再调用天气服务拿到该时间段的气象数据展示给用户确认可以手动修正。最后用户填入鱼种、体长、重量、饵料点击提交后端写入 fishing_records 表同时更新对应钓点的统计缓存。5. 常见问题与排查技巧5.1 定位偏移导致钓点错位这是我在开发期和内侧期被吐槽最多的问题。水边定位漂移的原因有很多最常见的是 Android 设备定位模式没切成高精度只开了 GPS 没开网络辅助。排查定位问题时我建议按这个顺序来先看定位模式是不是 HighAccuracy再看高德 Key 的包名和签名是否匹配最后在代码层加均值滤波。还有一种情况是手机本身的硬件问题同一部手机在开阔地定位正常到峡谷型水库边就漂这种靠软件很难完全消除所以手动拖点纠偏必须做得足够顺手。实测下来90% 以上用户在第三次使用后会直接手动拖点比反复定位快得多。5.2 天气数据对不上出钓时间这个问题的根源在于我把“记录时间”和“出钓时间”混为一谈了。第一版代码里用户点击新增记录天气接口取的就是当时当地的数据结果是白天钓鱼晚上补记的人气温字段记录的是夜间温度气压也跟白天完全对不上。后来改成时间选择器方案数据准确性才真正解决。所以如果你也要接天气 API一定记住天气数据要和出钓时间对齐不是和记录时间对齐。5.3 预测评分和实际鱼情不符怎么办被问得最多的问题就是“今天评分四星怎么还是打龟了”。这里我要说句实话任何基于气象的预测都无法保证一定爆护因为鱼情还受水情、钓位、饵料、前一日人类活动等多重因素影响。MiroFish 的评分要做的是帮你筛选整体适宜的出钓窗口它给出的星级更应该在“同一周哪几天适合出门”这个语境里理解而不是“出门一定爆护”。排查预测不准时先看天气源是否准确再看时段分有没有按当地日出日落修正最后看有没有区分目标鱼种。鲫鱼和翘嘴的适温区间完全不同强行用一套模型预测所有鱼种结果必然是两边都有偏差。5.4 多端数据同步怎么做才省心最早版本我把 SQLite 文件存在本机换手机时数据迁移痛苦到怀疑人生。后来试过把数据导出成 JSON 塞网盘能用但很别扭。最终方案是客户端本地保留一份 SQLite 作为缓存所有写入操作走后端 API云数据库作为权威数据源。这样既解决了多端同步问题又保证了弱网环境下基本功能不受影响。对这个项目来说实时同步完全没必要拉取间隔设成启动时同步一次就够了。6. 后续扩展与一年真实体会6.1 钓点鱼情档案数据攒够之后的进阶玩法当记录量过百条之后MiroFish 的价值会发生质变。我现在正在做的一个功能是给每个钓点生成一张“鱼情小档案”内容包含该钓点出现频率最高的三种鱼、最适合出钓的两个月份、历史上饵料使用的分布和对应的上鱼率、平均单次出钓时长和最佳时段。这张档案完全由用户自己的记录聚合而来比任何网上的钓点攻略都贴合实际情况。再往后我想接入实时水位数据把水文变化也纳入预测模块。国内很多钓点受水电站调峰影响明显水位一天内涨落一米的情况并不罕见如果能把水位变化速率作为一个变量加入评分公式预测的实用价值会再上一个台阶。不过这个需求需要的信息源比较多还在评估阶段。可以确定的是这些扩展都建立在稳定的记录习惯之上所以我现在首要任务还是把记录体验做顺畅。6.2 用了一年后我最想告诉新手的三件事从原型到现在我自己已经实际用了快一年最深的感受是这工具给我带来的最大收获不是“预测准”而是“复盘有据”。翻看去年春季的记录我能清楚想起第一次涨水后钓到鳊鱼时用的饵料配方也能从数据里看出哪类天气我最容易打龟这种自我了解是任何钓鱼视频都给不了的。如果你也想做一个类似的个人记录工具或者想用数据化思路提升钓技我给你三条建议。第一条先手动记录至少二十次真实出钓数据然后再决定功能优先级你会比自己想象中更清楚哪些是刚需哪些是自嗨。第二条不要在第一个版本做太多社交功能记录工具就应该先把记录做到极致。第三条数据冗余不可怕可怕的是数据缺失。宁可把天气、气压这些字段存两份也不要在需要分析时找不到历史数据。还是那句话工具永远替代不了经验但好的工具能让经验快速沉淀成你的竞争力。
返回列表