作者:Chenyun_922 · 开源地址:https://github.com/974294341-bit/hebei-policy
一、缘起:一个"触摸大屏查询"的需求
某次需求里提到,要在科技厅做政策查询大屏,最终交付要同时有 PC 电脑端 和 手机端小程序,并且两端数据互通。
功能要求很具体:
- 左侧 政策分类树(3 级)
- 右侧 政策列表,每页 10 条
- 列表 按实际顺序倒序(最新发布的在最上面)
- 支持分类树点击过滤、关键词搜索、时间范围、制定主体筛选
参考对象是一个已上线的站点「科技政策一点通」。于是整个项目从逆向分析参考站点开始,再落地成自己的系统。
二、技术选型
层
技术
理由
后端
Spring Boot 3.4.5 + JdbcTemplate
28 字段的遗留表,range/rank/year 是保留字,JdbcTemplate 比 JPA 映射更可控
数据库
H2(演示)+ MySQL(生产)双 profile
开箱即跑,生产导入真实 policy.sql
PC 端
纯 HTML/CSS/JS
一个列表页 + 一个详情页,无框架更轻
小程序
原生微信小程序
与 PC 共用同一套 REST API
核心原则:PC 端和小程序复用同一套后端接口,天然互通。
三、第一步:逆向参考站点
参考站点 kjzc.hebsti.cn/technology 是 Vue 2 + Webpack + Element UI 的 SPA,数据全部异步加载。从主 bundle 的 axios 配置里确认了 API 前缀:
所有接口统一前缀:/prod-api → https://kjzc.hebsti.cn/prod-api
关键发现:政策类接口完全开放,无需登录鉴权。 这让我们能把接口范式直接对标:
GET /bus/pc/policyfulltext/getPolicyTypes 分类树(带各级数量)
GET /bus/pc/policyfulltext/list 列表(分页 + 筛选)
GET /bus/pc/policyfulltext/{id} 详情(含正文 HTML)
合规提醒:开放接口适合做检索研究,请勿高频请求或商用转售。
四、数据摸底:27.9MB 的 policy.sql 告诉我们什么
拿着用户的 policy.sql(734 条政策)逐行解析 INSERT 后,有几个反直觉的事实决定了分类树怎么建:
- type 字段存的是叶子分类名("科技计划管理" 93 条、"综合" 87 条……共 19 个),正好对上需求文档里的树形叶子节点——不是一级分类
- category 字段全是 "其他",没有信息量
- range 是"名称 + 区划码":河北省(130000 HE)、全国(000000 QG)——制定主体筛选靠区划码判断(000000=国家,130000=河北,其他=外省)
- pubdata 是批量导入日期(全是 2022-06-20 / 2022-10-13),不具时间线意义
- 真正的发文时间是 viadata——"倒序"排的就是它
- text 是 Word 转出的 HTML 全文
这一步踩过坑:最初以为 type 是一级分类,统计后才纠正。没做数据摸底,分类树就会建错层级。
五、系统架构与核心接口
┌─────────────┐ ┌──────────────────────┐ ┌──────────┐
│ PC 端 (Web) │ ──API──▶│ Spring Boot 后端 │ ──JDBC──▶│ H2/MySQL │
├─────────────┤ │ /api/categories │ │ policy │
│ 微信小程序 │ ──API──▶│ /api/policies │ │ 表(28列)│
└─────────────┘ │ /api/policies/{id} │ └──────────┘
└──────────────────────┘
REST 接口(PC 与小程序共用):
接口
作用
GET /api/categories
分类树(父级数量 = 自身 + 子类合计)
GET /api/policies
分页列表:pageNum/pageSize(默认10)/keyword/types/region/timeType
GET /api/policies/{id}
详情(含 text 全文 HTML)
排序:ORDER BY viadata DESC, id DESC —— 即需求里的"按实际顺序倒排序"。
六、功能亮点
PC 端(参考原站界面)
- 蓝色渐变 banner + 六个导航栏目
- 左侧分类树:父级可折叠,勾选叶子节点过滤(支持多选)
- 右侧:关键词搜索 + 时间(近一月/半年/一年)+ 制定主体(全部/国家/河北/外省)
- 列表倒序 + 数字分页/跳页,每页 10 条
- 详情页渲染全文 HTML
微信小程序(深蓝主题,同一后端) - 搜索框 + 国家/河北/外省 tabs + 分类树抽屉 + 卡片列表(每页 10 条,触底加载)
- 详情页用 rich-text 渲染政策全文
- config.js 的 BASE_URL 指向电脑局域网 IP,手机扫码即可预览
四个栏目页:政策动态 / 政策解读 / 政策问答 / 政策培训(目前为占位页,原站对应独立模块,后续可接入或由用户提供数据表)。
七、踩坑实录(6 个真问题)
坑 1:H2 保留字导致启动失败
policy 表的 range、rank、year 是保留字,INSERT 没加反引号直接报语法错。
修复:统一用range、rank、year。
坑 2:演示数据列数不匹配
生成抽样数据时 text 后漏了一个 NULL,值 27 个、列名 28 个。
修复:每行 28 个字段对应 28 个值。
坑 3:批量文本替换没生效
替换脚本跑了却 fixed rows: 0——因为 INSERT 行尾带分号,匹配模式没带分号。
教训:做替换前先 tail 看一行的真实结尾。
坑 4:端口被环境变量劫持
SQL 过了,Tomcat 却起在随机端口 51345。排查发现沙盒环境变量 SERVER__PORT 被 Spring 宽松绑定覆盖配置。
修复:启动显式指定 --server.port=8080(命令行优先级最高)。
坑 5:jar 被占用无法重新打包
改完代码 mvn package 报 Unable to rename ... .original。
原因:服务还在跑,jar 被进程锁定。
标准流程:先停服务 → 再打包 → 再启动。
坑 6:导航栏目点不开(用户实测反馈)
顶部"政策动态"等四个栏目是 javascript:void(0) 死链——当时只有"科技政策"有数据,其他先做了占位。
修复:新增四个栏目页,导航全部接真实链接。
彩蛋:GitHub 上传(环境特有的 TLS + OAuth 坑)
GitHub 连接器的 OAuth 弹窗在本环境被拦截,且 Windows 的 TLS 证书吊销检查报 CRYPT_E_NO_REVOCATION_CHECK。最终用 PAT + git 命令行绕过:
git 关闭 schannel 吊销检查(沙盒里全局配置不生效,用内联参数)
git -c http.sslVerify=false -c http.schannelCheckRevoke=false push -f origin master
curl 等效:curl -k --ssl-no-revoke ...
同时把提交里的真名改为 GitHub 用户名,避免个人信息泄露。
八、运行与部署
H2 演示模式(零配置)
cd hebei-policy && mvn spring-boot:run
浏览器打开 http://localhost:8080
MySQL 生产模式(导入完整 734 条)
CREATE DATABASE policy DEFAULT CHARACTER SET utf8;
SOURCE policy.sql;
改 application.yml 中 mysql 段密码,再:
mvn spring-boot:run -Dspring-boot.run.profiles=mysql
微信小程序
微信开发者工具导入 miniprogram/ → 改 config.js 的 BASE_URL 为本机局域网 IP → 点「真机预览」扫码即可。
九、开源
完整源码(后端 + PC 端 + 小程序 + 逆向分析 + 调试记录)已开源:
🔗 https://github.com/974294341-bit/hebei-policy
十、小结
这个项目不大,但把"逆向参考 → 数据建模 → 全栈落地 → 调试 → 发布"走了一遍,几个值得记住的点:
- 动手前先摸数据:字段含义往往和直觉相反,摸底能少走很多弯路。
- 保留字是遗留表的常态:写 SQL 第一件事就是检查字段名。
- 部署环境会有意外:环境变量覆盖配置、文件被锁、TLS 校验坏掉——遇到"配置不生效"先查环境变量和进程占用。
- 开放不等于随便用:参考站点的开放接口适合研究,商用需注意合规。
如果你也在做政策/文档类查询系统,欢迎参考或提 Issue。
浙公网安备 33010602011771号