做水上警务的基层民警,工作状态和陆上兄弟完全不一样。日常巡航一圈下来要核对几十条船的信息,遇到治安排查、非法捕捞查处、溺水救援这些现场处置时,往往一边拿手机拍照,一边往纸质台账上记,回到码头再手动录入电脑。这个“基于小程序的水上警务通”项目,就是我在实际接触这类需求后做的计算机毕业设计,前端用微信小程序,后端提供接口服务,配齐全套源码和LW文档,核心目标就一句话:让一线水上警务工作真正移动化。这篇博文把整个项目的设计思路、技术选型、核心模块实现、调试抓包经验,以及毕业设计文档和答辩准备,完整拆解一遍,给正在做同类题目的同学一条可以直接参考的路线。
1. 项目定位:这款小程序到底要解决什么实际问题
1.1 一线水上警务工作的真实痛点
水上警务和陆上警务最大的区别在于“环境”。陆上民警有稳定的网络覆盖、固定的办公场所、完整的电子化系统,而水上民警的工作平台是一艘巡逻艇,工作地点是航道、码头、锚地、湖泊这些地方。我调研时接触过几位基层水警,他们提到最多的几个问题很具体:船舶信息查询靠什么?靠一本印制船名册,现场翻纸质册子,效率低还容易过期;巡查记录怎么做的?先拿笔写,回码头再录入,当天如果巡航航线长,晚上补录到八九点很正常;遇到非法捕捞或违规作业,现场取证拍完照,回头要整理成规范材料,中间环节多,信息容易缺;各部门的数据又不互通,同一艘船的信息在港航、海事、公安那里各存一份,口径还不一样。
这些痛点归纳起来就是三类:信息查询慢、记录过程重、协同流转难。所以水上警务通这个题目,本质上不是做一个“炫酷的App”,而是把那些在岸上很容易实现、一旦上了船就变得困难的操作——查船、记录、上报、流转——通过一个轻量级小程序搬到手机里,让民警在水上就能完成大部分事务性工作。
1.2 为什么选小程序而不是原生App
这个题目换成“基于Android的水上警务系统”也是常见的毕业设计套路,但我最终选了微信小程序,有几个实际考量。第一是免安装,基层民警的手机里已经有大量政务类App,再装一个专用的原生App,安装率和使用率是很大的问题,而小程序扫码即用,用完即走,天然适合低频但刚需使用的工具型业务。第二是跨平台,iOS和Android统一覆盖,不用针对两个端分别开发和维护。第三是开发效率,小程序的技术栈贴近前端,页面更新走微信审核后即时发布,迭代速度比原生App快不少。
当然,小程序也有明显边界:包体大小受限、不适合做复杂离线计算、地图和后台定位能力比原生弱一些。但水上警务通的业务场景是“信息采集+查询+流转”,压力主要在接口请求和表单提交上,小程序完全扛得住。换句话说,选小程序不是因为它最先进,而是因为它最合适。
1.3 功能的边界:做好主线,不做花架子
毕业设计最容易犯的毛病是功能清单列了一大堆,最后每个功能都是半成品。我给这个项目定了一个“三条业务主线”的原则:巡查管理、船舶信息、事件流转。系统围绕这三条线展开,每个用户角色能做的事情都和各自身份绑定,不做复杂的数据大屏,也不做花哨的实时监控。把主线功能做实做透,比堆砌十个半成品功能要重要得多,这也是后续论文和答辩能讲深讲透的基础。
2. 技术选型与系统架构拆解
2.1 前端框架:微信原生还是uni-app
这个决策直接决定后面所有页面的写法。我对比了三条路:微信原生小程序、uni-app、Taro。微信原生小程序的优点是API最全、调试最直接、文档最丰富,适合单平台交付;uni-app用Vue语法,可以一套代码发布到微信、支付宝、H5等多个端,组件生态也成熟;Taro是React语法,多端能力同样优秀,但在小程序场景下社区资料略少。
我的建议是:如果这个项目后面没有打包成App或H5的硬性需求,直接用微信原生小程序最稳。原因很简单——毕业设计的时间本来就紧,原生写法碰到问题搜资料最快,微信开发者工具本身就是一套完整的联调环境。如果老师要求“可扩展性”或你个人想顺便积累跨端经验,用uni-app也行,但要注意uni-app在部分原生组件(比如地图、选择媒体)上的封装会有差异层,排查问题会多一道工序。
2.2 地图服务:天地图集成的方案与要点
水上警务绕不开地图。船舶在哪、巡查轨迹走到哪、事件发生在哪个航段,都要落到地图上。这个项目里我采用了“小程序内置地图组件为主、天地图服务为辅”的混合方案,这个点也是论文里一个值得展开的“为什么”。
先说结论逻辑:微信小程序的map组件默认使用的是腾讯或高德底图,城市道路信息很详细,但水域要素(航道、锚地、港口、航标)并不专业;天地图是国家测绘地理信息部门提供的权威地理信息服务,水域相关要素更规范,而且是免费开放的。所以如果想让系统更贴合“水上警务”这个主题,可以在方案里引入天地图。
具体实现路径有两条。第一条是轻量做法:直接用小程序map组件做底图,把船舶、码头、事件位置用marker标注出来,只是底图来源不是天地图,需要在文档里说明这是基于第三方地图SDK的简化实现;第二条是完整做法:用web-view容器加载天地图Web端页面,在小程序里通过postMessage与内嵌页面通信,实现定位、标注、轨迹绘制。第二条的体验和性能要差一些,但能体现出“天地图”这个关键词,论文里也有得写。我实际做的时候用的是第一条为主线,同时在功能设计里保留了天地图经纬度坐标转换的接口,把第二种做法作为系统扩展点写进了论文的“展望”部分。
2.3 后端接口与数据库设计
后端我选了Spring Boot 2.x + MyBatis-Plus,数据库用MySQL 5.7。这个组合在毕设里最稳妥,SSM那一套虽然也能跑,但配置繁琐,MyBatis-Plus的通用Mapper能省掉大量重复SQL。
数据库设计是这个项目特别值得花时间的地方。我设计了六张核心表:用户表(含角色字段)、巡查任务表(任务下发的实体)、巡查记录表(民警实际填写的内容)、船舶信息表(船名、船型、所有人、证书信息等)、事件信息表(上报、受理、处理、完成的状态流转)、附件表(图片和取证材料的统一存储)。这里有一个容易忽略的点:巡查记录和船舶信息之间要做关联,一次巡查可能涉及多艘船舶,所以中间加了一张关联表,而不是在巡查记录里直接放一个船名文本字段。数据规范化设计在论文里会被老师重点提问,关联表这种设计细节是能体现功力的地方。
接口风格统一走RESTful,返回格式统一为一个Result对象,包含code、message、data三段。登录鉴权用JWT,前端每次请求带token,后端用拦截器校验。小程序端把请求封装在一个request.js里,统一处理token注入、错误提示、401跳转,页面里不要到处写wx.request,这是保持代码整洁的关键习惯。
3. 核心功能模块的设计与实现
3.1 巡查记录:从纸质台账到电子工单
巡查模块的业务流程是这样的:管理员在小程序“工作台”里创建一条巡查任务,指定巡查水域和参与人员;巡查员收到任务后,点击“开始巡查”进入执行态,到达指定水域后定位打卡,系统记录经纬度和时间;巡查过程中可以随时新增巡查记录,填写巡查内容、发现问题、上传现场照片;回到码头后提交整个巡查批次,任务状态变为“已完成”。
这里最关键的技术点是定位打卡。不能只在页面上调用一次wx.getLocation就完事。我在实际开发中是这样处理的:进入巡查任务时启动一个“位置采集”逻辑,每隔30秒采集一次经纬度,把坐标追加到本地缓存数组里;提交巡查时,把坐标数组中的第一个点作为打卡点,末尾点作为结束点,中间的点可以在地图上绘制成巡查轨迹。这个设计让“巡查轨迹回放”成了一个很自然的展示功能,答辩演示时效果非常好。
页面实现上,巡查记录列表用“卡片式”布局,每条记录展示时间、位置摘要、巡查内容前两行文字、照片缩略图。新增巡查记录的页面需要考虑多次拍照的场景,我把“继续添加记录”和“提交本次巡查”做成两个独立按钮,避免用户在一条记录里反复纠结要不要结束。
3.2 船舶信息库:查询、登记与关联
船舶信息模块的逻辑很清晰,但细节容易丢。首页提供搜索框,支持按船名、船号模糊匹配;搜索结果列表展示船舶的简要信息,点击进入详情页;详情页展示基础信息、证书信息、维保记录(如果有)、历史巡查记录、违规事件。新增船舶信息做成一个表单页,字段包括船名、船型、总吨位、所有人、联系方式、证书编号、登记状态等。
有一个设计我踩过坑:船舶的“登记状态”。有些船只是长期运营船舶,有些是短期作业船舶,查询时如果不做状态筛选,列表里会混杂大量无效数据。我加了一个状态字段(正常/停航/注销),列表页提供一个筛选Tab切换,这个小改动让船舶查询的实用性提升了一个档次,而且在答辩时可以理直气壮地说“这个字段考虑了业务状态”。
拍照管理船舶时要注意图片处理。小程序wx.chooseMedia拿到的是临时文件路径,必须调用wx.uploadFile上传到后端换取正式URL,再把这个URL存到船舶信息字段里。千万别直接把临时路径存数据库,那是本机文件,换了手机就失效。这个坑在后期真机测试时会暴露得非常明显。
3.3 事件上报与流转:一条完整的状态链
水上警务事件的上报是高频场景。巡查员在水上发现异常情况,打开小程序填写事件描述、选择事件类型(非法捕捞、违规作业、人员落水、船舶故障等)、拍照取证、自动附带定位信息,然后一键提交。
事件处理的闭环我设计成一个状态机:待受理 → 已受理 → 处理中 → 已完成,中间可以加一个“已驳回”作为退回状态。状态变迁的权限有严格要求:待受理状态下只有管理员能受理;已受理之后可以分配处理人;处理人把事件处理完提交“已完成”;管理员也可以驳回,驳回必须填写原因。所有状态变更都记录一条操作日志,存到事件日志表里。
这个状态机的核心实现是前端根据用户角色渲染不同的操作按钮:管理员看到“受理”“驳回”,处理人看到“开始处理”“提交完成”,巡查员只能看到查看和新增。后端接口也要做同样的鉴权,不能前端藏了按钮后端就不查权限。很多毕设只做了前端控制,后端接口裸奔,这个在答辩时被追问是很尴尬的。
3.4 消息通知与任务提醒
小程序端的通知机制用微信订阅消息实现。订阅消息的特点是“用户点击授权一次,系统推送一条”,所以不要想着让订阅消息承担全部通知功能。我的设计是:事件状态变更时触发一条订阅消息推送给相应的上报人和处理人;任务创建完成后推送给任务执行人;至于站内消息记录,在系统里用一张message表做统一存储,列表页拉取展示。
这里有一个典型坑:订阅消息模板要先在小程序后台申请,类目不同能申请的模板也不一样,“警务”类目如果没有资质可能审核不通过。毕业设计阶段可以换一个思路,用“行政管理”或“社区服务”类目下的模板替代,比如“服务进度通知”“任务完成通知”,消息内容自己拼接事件概要和状态。别等到答辩前一晚才去申请模板,审核周期足够让人欲哭无泪。
4. 开发中的硬核细节:常规文档里不讲的东西
4.1 水域定位的精度问题与处理技巧
在岸上定位和在水上定位完全是两种体验。手机在开阔水面确实能收到GPS信号,但巡逻艇是金属外壳,人在船舱里或者船尾操作时,GPS信号会明显衰减,定位点经常乱跳。我在长江边的一条巡逻艇上实测过,同一分钟里定位点能飘出几百米。
这个问题的解决方案我总结成三层。第一层:定位类型用gcj02坐标系,这是国测局标准,直接对接小程序map组件和国内地图服务。第二层:做“漂移过滤”——拿到一个新定位点后,先和上一个可靠定位点计算直线距离,如果超过设定阈值(比如100米),就认为当前定位不可靠,继续沿用旧点,等下一个点再判断。第三层:手动纠偏——地图上把定位点做成一个可拖动的marker,用户觉得位置不对,可以手动拖到正确位置,这个纠正后的坐标在提交记录时优先采用。这个“自动过滤+手动纠正”的组合方式,无需引入卡尔曼滤波这类复杂算法,但实测下来效果靠谱,论文里写出来也比单纯调用API有内容。
经纬度转地址不要在小程序端单独做。我是在后端写了一个工具类,调用高德或天地图的逆地理编码服务,把经纬度换成“XX省XX市XX水域”这种文本描述,随巡查记录一起存储。这样列表页不用加载地图组件就能显示大致位置,体验和性能都好很多。
4.2 弱网环境下的数据缓存与同步策略
水上网络状况普遍一般,尤其在航道深处或湖区,4G信号时有时无。如果所有操作都强依赖网络,系统在真实环境下根本没法用。我设计了一套“本地优先、定时同步”的容错机制。
核心思路是这样的:巡查记录、事件上报这两个提交类操作,不再直接异步请求后端,而是先写入一个本地的“待同步队列”。这个队列用wx.setStorageSync持久化,数组元素包含接口地址、请求参数、创建时间。前台同时启动一个定时器,每隔15秒尝试把队列里的第一个请求发出去,发送成功就从队列移除,发送失败就留在队列里等下一次。
这个方案看起来简单,但有一个关键的坑要处理:接口幂等。如果请求发出去了,后端也处理成功了,但响应因为网络问题没回到前端,前端会认为发送失败,然后重试,导致同一份记录被插入两次。解决方式是前端生成一个本地唯一ID(比如时间戳+随机数),提交时带上这个ID,后端在插入前先查一下这个ID是否已存在,存在就直接返回成功。这个“幂等操作”的设计细节,在论文的系统设计部分写出来,老师的印象分会明显不一样。
4.3 列表加载与顶部导航栏适配
小程序页面列表加载更多,最标准的做法是用onReachBottom生命周期钩子翻页。我在船舶列表和事件列表里都是这样处理的:请求参数带pageNum和pageSize,后端返回总条数和当前页数据,前端根据“当前已加载条数小于总条数”判断还有没有下一页,有就在列表底部显示“加载中”,没有就显示“没有更多了”。
几个容易出问题的点,我逐个说。第一,onReachBottom在内容不满一屏时可能不触发,这是小程序的机制问题,列表加载完成后如果发现总高不足一屏,需要主动再请求下一页。第二,翻页时要防止重复请求,我在请求前加了一个isLoading的布尔锁,请求完成后才解锁。第三,下拉刷新和分页加载要考虑数据错位,刷新成功要把列表重置到第一页、清空原有数据,再拼接新数据,否则会重复。
顶部导航栏适配也是真机调试常见的坑。不同型号手机的刘海屏、状态栏高度不一样,用自定义导航栏时一定要用wx.getWindowInfo()获取状态栏高度和菜单按钮位置,计算实际导航栏高度。如果偷懒写固定高度,在iPhone X以后的机型上布局会顶到状态栏,特别难看。页面标题如果需要动态变化,比如“事件详情”改成“事件详情-编号001”,用wx.setNavigationBarTitle即可,这个API很基础但很多初学者不知道。
4.4 图片处理与上传链路
巡查取证、事件上报都涉及图片。完整的处理链路是:wx.chooseMedia选择图片 → wx.compressImage压缩 → wx.uploadFile上传到后端 → 后端返回图片URL → 前端把URL随表单一起提交。压缩这一步特别关键,手机拍出来的照片动辄3-5MB,不压缩直接传,弱网环境下能把人等的失去耐心。我实测过,压缩到单张200KB左右,清晰度在手机屏幕上完全够用,上传速度提升立竿见影。
后端接收图片后,毕设阶段可以存到服务器本地目录或OSS(如果自己有OSS权限的话),存本地要注意配置静态资源映射,否则图片URL访问不到。推荐用一个独立的附件表管理图片,字段包括业务类型、业务表ID、文件路径、上传时间,而不是在每个业务表里塞一个img_url字段。这样同一个业务可以关联多张图,功能扩展性更好。
5. 调试与自测:小程序上线的最后一道关卡
5.1 用Charles抓包排查接口联调问题
小程序开发到联调阶段,最痛苦的事情是“前端以为传对了,后端说没收到;后端说返回了,前端说没解析到”。这时候用Charles抓包是最有效的定位手段。Charles本质上是一个HTTP代理服务器,手机和电脑连同一个局域网,把手机的代理指向电脑的IP和默认端口8888,小程序的所有请求就会经过Charles,你就能看到每个请求的URL、请求头、请求体、响应内容。
在Charles里配置SSL Proxying后才能看到HTTPS请求的明文内容,需要在小程序后台或者开发者工具里把“不校验合法域名”打开,同时要把证书装到手机上并信任。这里面有一个容易卡住的地方:Android手机安装根证书后,微信小程序进程可能不会立即读取,需要先完全杀掉微信再重新打开,让微信走系统代理读取新证书。iOS的信任开关在“设置-通用-关于本机-证书信任设置”里,新证书默认是不信任的,需要手动打开。
抓包时有两个实用技巧。第一,在Charles的Recording Settings里勾选Include,只记录你自己后端域名的请求,过滤掉大量无关流量,不然看半天全是微信本身的请求,眼睛会瞎。第二,发现前端参数和后端不一致时,直接在Charles里右键这个请求,选择“Repeat”或编辑重放,不用反复在页面上操作,联调效率能快一倍。这个工具我在项目里用了很多次——有一次事件上报接口在真机上一直报500,抓包一看,是前端把eventTime传成了“2025-03-18 14:30”,后端定义的字段是EventDate,一个字段名不匹配的问题,肉眼在代码里翻了半个小时,用Charles十秒钟就定位了。
5.2 真机预览与体验版分发的实操流程
很多同学开发小程序只知道在模拟器里跑,从来不测真机,这会导致大量“模拟器好好的、真机就崩了”的问题。微信开发者工具里右上角有一个“预览”按钮,点击后会生成一个二维码,用微信扫码就能在真机上打开项目,这是第一步。但预览版有一个限制:手机和电脑必须在同一网络情况下才能正常加载,而且预览版的代码是实时编译的,手机端体验不够稳定。
要真正把小程序发给同学、老师试用几天收集反馈,正确的路径是走“上传+体验版”:开发者工具点击“上传”,把代码提交到微信小程序管理后台,然后在后台的“版本管理-开发版本”里把上传的版本选为“体验版”,生成体验二维码。把这个二维码发给别人,对方在“小程序”页面搜索对应的小程序名称进不去,但用微信扫码打开体验码就能正常使用了。体验版的有效期并不像开发者工具提示的那么短,实际上可以维持较长时间,足够完成一轮试用反馈收集。
收集反馈时建议给试用的同学列一个简单的测试清单:正常登录、新增巡查、提交事件、离线提交、拍照上传。别只让人家“随便看看”,没有明确操作路径的试用是收集不到有效反馈的。
5.3 常见问题排查速查表
把调试阶段我实际踩过且让身边同学也踩过的坑整理成一张表,遇到问题可以按图索骥。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 真机定位失败或没反应 | 未调用wx.authorize申请定位权限,或手机定位服务未开启 | 调用 authorize 前先检查系统设置;授权失败后引导用户去设置页打开 |
| 请求接口报“url not in domain list” | 用了非法域名或没配白名单 | 开发阶段勾选“不校验合法域名”,上线前在后台配置request合法域名 |
| 登录后请求仍返回401 | token过期或未正确放入请求头 | 在request.js统一处理token注入和401跳转;后端检查拦截器放行路径 |
| 上传图片失败 | 图片过大或后端接口路径错误 | 先wx.compressImage压缩;检查uploadFile的url是否和服务端对得上 |
| 事件上报后列表里没有 | 同步队列未发送成功,或幂等ID冲突 | 查看本地队列状态,断网重试;检查后端幂等逻辑 |
| onReachBottom不触发 | 页面高度不足一屏 | 加载第一页后主动检测,不满一屏则再请求下一页 |
| 订阅消息发不出去 | 模板未审核通过或用户未授权 | 提前申请模板;在业务页面引导用户点击“允许订阅” |
5.4 上线发布前的资质与合规检查
小程序上线正式版必须经过微信审核,审核会检查类目、隐私协议、用户授权等。水上警务这个主题天然涉及政务和警务属性,用个人主体账号申请“警务”类目几乎不可能通过,所以毕设阶段不需要追求正式发布,能用体验版完成演示和测试就足够。但文档里需要把这个点写清楚:系统的上线运行需要相应主体资质,这是一个客观的限制条件,不影响设计本身的完整性。
如果后续真的想部署试用,建议走两个方向:一是以“企业内部工具”的形式嵌入企业微信,使用企微的小程序能力;二是更换类目描述,重点强调“水上巡查信息管理”而非“警务执法”,以普通工具类目提交审核。当然,毕设重点还是在设计和实现,资质问题点到为止即可。
6. 毕业设计的文档写作与答辩准备
6.1 LW文档的结构设计与写作重点
LW文档(也就是论文/设计文档)的结构不要太创新,按学校要求的规范模板来,核心章节一般是这几章:绪论(研究背景、意义、国内外现状、论文结构)、相关技术介绍(微信小程序、Spring Boot、MySQL、天地图API)、系统需求分析(可行性分析、用例图、功能性需求、非功能性需求)、系统设计(总体架构图、功能模块划分、数据库ER图和数据字典)、系统实现(每个模块的页面截图+核心代码说明+实现思路)、系统测试(测试环境、功能测试用例表、测试结果、发现问题的解决过程)、总结与展望。
写文档有一个关键技巧:不要等到代码写完再写,而是边开发边记录。我在开发每个模块时,会随手截图页面效果、记录关键代码的片段和实现思路,写文档时直接把这些素材整理进去,比从头回忆高效太多了。另一个重点:文档里的架构图、流程图、ER图一定要和代码实际对应。很多同学代码用的是MyBatis-Plus,ER图却是老掉牙的SSH结构,答辩时老师一眼就能看出文档和代码是脱节的。
数据库设计在文档里要单独丰富。不仅仅是列几个表名和字段,而是要画出完整的ER图,并解释每个字段的用途、表与表之间的关联关系、为什么这样设计。巡查记录和船舶信息的关联表、事件日志表、待同步队列相关的幂等设计,这些都是可以深入写的点。
6.2 答辩演示的节奏安排与素材准备
答辩演示是整个毕业设计的临门一脚。金字塔原理在这里很实用:先抛出一个实际问题(水上巡查效率低、记录易丢失),再说你的系统怎么解决(移动端随时记录+自动同步+事件闭环),最后演示核心功能。演示顺序我建议这样设计:登录进入系统 → 展示首页概览 → 新建一条巡查记录(包含定位打卡) → 展示列表和详情 → 船舶搜索和登记 → 上报一条事件 → 切换管理员账号完成受理和处理闭环。
有一个防意外措施值得提前准备:录一段完整的演示视频,本地存储一份。答辩现场经常出现网络不通、演示账号登录不上、小程序审核出问题等突发状况,有视频兜底,哪怕现场翻车也能顺利讲完整个流程。
6.3 答辩老师最爱问的几个问题
根据我带过的毕设答辩经验,水上警务通这个题目,老师主要会在四个方向发问。第一,技术选型问题:为什么用Spring Boot不用SSH?为什么不直接用高德地图而要用天地图?这些问题的回答思路是把“场景”放前面——弱网、水域环境、跨端需求,所有的选择都围绕场景需求展开。第二,架构问题:你的系统总体架构是怎样的?前端小程序和后端服务之间如何通信?token鉴权流程是什么?把架构图记熟,讲清楚请求就能从服务端到客户端走一个来回。第三,数据设计问题:数据库的第三范式满足吗?为什么还要关联表?这个问题考察的是数据规范化意识。第四,业务理解问题:你觉得这个系统用起来有没有什么不足?给出1-2个真实的待改进点,比如“实时视频回传没有做”“离线同步的冲突解决还不够完善”,比回答“没有不足”更讨喜,也更能体现你的思考深度。
写在最后
做这个项目,我最大的体会是:毕业设计真正的难点不在写代码,而在把需求看清楚、把文档写明白、把流程讲通畅。水上警务通的题目自带清晰的业务场景,只要抓住“巡查记录、船舶查询、事件流转”这三条主线,把每个功能的完整链路打通,再配合扎实的文档和一场流畅的演示,就已经是一份很能打的作品了。最后再分享一个实实在在的建议:开发过程中记得多用git做版本管理,每完成一个模块就提交一次。我就曾在改地图模块时把巡查列表的代码覆盖丢了,没有版本管理几乎要重写。保持好这个习惯,后面的路会顺很多。