1. 为什么是“海洋环保+小程序”这个组合
先说一个我自己的判断:海洋环保类系统,真正缺的不是环保知识,而是“让用户愿意打开”的入口。以前很多学校、社区做环保宣传,搞一个网页,挂一串科普文章,结果访问量惨不忍睹。网页时代的环保系统,最大的问题在于用户根本不会主动去访问,内容做得再好也是自嗨。
而小程序的场景逻辑完全不一样。用户用完即走,不需要下载安装,微信扫一扫就能打开,分享到群里也就一秒钟的事。海洋环保这种偏公益、偏科普的题材,天然适合靠社交传播。比如你做一个“垃圾分类查询”,用户在厨房处理垃圾时随手打开小程序查一下,比PC端打开浏览器输入网址方便太多。再比如“海洋垃圾随手拍”这种功能,用户在海边玩的时候拍一张照片上传,这个动作在手机上就是自然的,在电脑上就非常违和。
所以这个项目选“SpringBoot + 微信小程序”的组合,不是随大流,是符合业务场景的。SpringBoot负责提供稳定的后端服务,小程序负责触达用户。两者通过HTTP接口通信,前端的轻量和小程序的即用即走,正好匹配环保科普+轻互动的定位。
如果你是做毕业设计,或者接了一个类似的小程序外包项目,这个切入点也很有价值。海洋环保这个题材,既有公益性,又有明确的业务边界——不会像“智慧城市”那样大而空,做出来的功能模块能落地,答辩也有故事可讲。再加上SpringBoot是当前后端开发的主流框架,小程序又是前端就业的重要方向,这个项目放简历上,面试官不会觉得是玩具项目。
接下来我把这个项目从技术选型到模块设计、从登录流程到部署交付,一条线讲清楚,包括我实际开发中踩过的坑和解决思路,照着做基本能跑通。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与版本选择:SpringBoot 3.x还是2.x,配套组件怎么搭
2.1 版本选型:不要无脑上新版本
热词里有一条“springboot版本太高”,这个我太有发言权了。很多新手搭项目,打开Spring Initializr直接选最新版,结果导入项目后MyBatis Plus、Knife4j、jwt解析库各种不兼容,光是版本冲突就能折腾两三天。SpringBoot 3.x是基于Jakarta EE 9的,javax.包全部改成了jakarta.,很多老教程里的代码直接复制过来是编译不过的。
如果你做的是毕业设计或者快速交付项目,我建议用SpringBoot 2.7.x。理由很直接:
- 2.7.x是2.x系列的最终维护版本,稳定性经过大量项目验证
- 老教程、老资料基本都是2.x的生态,遇到问题搜解决方案容易
- 大部分讲师录制的视频课、网上的配套代码都是基于2.x的
- JDK 8 + SpringBoot 2.7,是当前国内中小项目最稳妥的组合
如果你确实想用3.x,那就要接受两个事实:JDK最低要17,部分中间件客户端需要升级版本。网上那些“新项目为什么要用Boot 3”的讨论帖,大部分是技术博主在蹭流量,真放到交付场景下,2.7已经完全够用。
我这个项目的搭建方式很朴素:Maven + SpringBoot 2.7.18 + JDK 1.8 + MyBatis Plus 3.5.x + MySQL 5.7/8.0 + Redis(可选,不是必须)。只做接口服务,不用微服务那套,因为一个单体的SpringBoot应用足以支撑小程序几万用户量级,引入微服务反而把复杂度抬高,没有实际收益。
2.2 核心依赖清单与版本搭配
下面是我实际用的依赖组合,可以直接照着抄到pom.xml里:
xml复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 微信小程序登录、HTTP 请求 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<!-- MyBatis Plus 增强 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 接口文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>
依赖选择上我重点解释两个。
第一个是Hutool。它是Java工具类库,提供了HTTP请求、加密、日期处理等一揽子工具。微信登录时需要后端调用code2Session接口,用Hutool的HttpUtil几行代码就完成了,不需要再单独引一个OkHttp或者RestTemplate去封装。Hutool的JwtUtil虽然也有,但JWT签发我还是建议用JJWT,控制更精细,后面做token过期、刷新都会方便。
第二个是Knife4j。它是在Swagger基础上的增强UI,生成的接口文档界面比原生Swagger好看,而且支持导出离线文档。做交付项目时,这个工具能帮你省很多事——答辩老师或者验收方想看接口清单,直接打开http://localhost:8080/doc.html,一眼就能看清所有接口。后面我会讲怎么用它来辅助编写部署文档。
2.3 Redis到底要不要引入
这个项目里我原计划是不用Redis的,因为核心业务里没有高并发抢购、实时热点数据这种需求。但后来我在做积分排行榜功能时,发现MySQL的ORDER BY直接查也行,但每次请求都全表扫描,用户量上来后性能不好看。
权衡了一下,我最终引入了Redis,但只用来做两件事:
- 缓存微信登录时获取到的
session_key,设置过期时间 - 缓存首页新闻轮播图的数据,减少数据库压力
如果你只是做课设,不引入Redis完全可行,所有数据都查MySQL,功能不会有任何问题。引入Redis的意义在于:让项目看起来有技术含量,同时提前体验缓存这种东西在实际项目中的使用方式。我建议是引入,但如果你的服务器内存只有1G,那就要慎重,尽量别让Redis和MySQL挤在一台小内存机器上。
3. 数据库模型与核心模块拆解:这个小程序的业务骨架怎么搭
3.1 业务模块全景
海洋环保小程序,功能不能瞎堆。用户打开这个小程序,最想做什么?结合实际的环保业务,我把功能收敛为六大模块:
- 用户中心:包括微信登录、个人信息、我的积分、我的上报记录
- 环保资讯:提供海洋环保科普文章、新闻动态
- 垃圾分类:查询垃圾属于什么类别,以及对应的处理方式
- 随手拍上报:用户在海边发现垃圾或污染,拍照上传,后台审核
- 积分商城:用户通过上报、每日签到等行为获取积分,兑换小礼品
- 个人中心:包括反馈意见、关于我们、设置
这样设计的好处是:每个模块都有明确的业务闭环,不会出现“做了个功能但没人用”的尴尬。比如垃圾分类,它不是一个孤立的功能,用户只要查询一次,就能获得积分,积分能兑换礼品,这就形成了一个激励闭环,用户才愿意持续使用。
3.2 核心数据表设计
数据库是整个系统的地基,我直接把我使用的表结构逻辑梳理出来,你可以根据自己的业务增减字段。
用户表 user:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| openid | varchar(50) | 微信用户唯一标识 |
| nickname | varchar(50) | 昵称 |
| avatar_url | varchar(255) | 头像地址 |
| phone | varchar(20) | 手机号(可选) |
| points | int | 积分 |
| status | tinyint | 状态:0禁用,1正常 |
| create_time | datetime | 创建时间 |
垃圾分类表 garbage_category:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| name | varchar(50) | 垃圾名称 |
| category | varchar(20) | 类别:可回收、有害、湿垃圾、干垃圾 |
| description | varchar(500) | 详细说明 |
| image_url | varchar(255) | 示例图片 |
| search_count | int | 搜索次数,用于排行榜 |
污染上报表 pollution_report:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 上报用户ID |
| image_url | varchar(255) | 现场图片 |
| description | varchar(500) | 描述 |
| location | varchar(255) | 位置描述 |
| latitude | decimal(10,6) | 纬度 |
| longitude | decimal(10,6) | 经度 |
| status | tinyint | 0待审核,1已通过,2已驳回 |
| audit_msg | varchar(255) | 审核意见 |
| create_time | datetime | 上报时间 |
积分记录表 points_log:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 用户ID |
| change_points | int | 积分变动,正负均可 |
| reason | varchar(50) | 变动原因:签到、上报、兑换 |
| create_time | datetime | 变动时间 |
设计这些表的时候有个细节容易忽略:pollution_report里我加了latitude和longitude字段,类型用了decimal(10,6)而不是double。原因是小数经纬度用double会有精度误差,虽然这个误差在展现层面上基本感知不到,但做数据归档的时候,精确的经纬度是有必要保留的。实际上我们在测试中发现,double的精度丢失在展示到地图上时,偏移能达到几十米,所以这个字段一定要用定点数。
3.3 表关系的取舍
对于这个项目,我只保留了一对多关联。用户和上报记录是一对多,用户和积分记录是一对多。其他像资讯、分类这些表,基本就是单表查询,不需要外键关联。
我在代码中也没有使用数据库物理外键,而是通过逻辑关联在Service层做判断。为什么不用物理外键?一是小程序项目本身并发不算高,但物理外键在插入、更新时会产生额外校验开销;二是后续如果需要分库分表,外键会成为瓶颈。更关键的是,MyBatis Plus写代码时,物理外键带来的约束会体现在实体类上,处理起来比较麻烦。
在实际开发中,我建议少用物理外键,多用逻辑外键,配合代码层面的校验来保证数据一致性。如果在答辩时有人问你为什么不用外键,这个回答完全够用。
4. 微信登录与用户身份体系:最容易踩坑的一环
4.1 微信小程序登录的完整流程
微信小程序登录,是很多新手第一次接触时最容易懵的地方。它的完整流程是这样的:
- 小程序端调用
wx.login()获取一个临时凭证code,这个code有效期只有5分钟,且只能使用一次 - 小程序把这个
code通过后端接口传给SpringBoot服务 - 后端拿着
code+appid+appsecret调用微信的code2Session接口,换取openid和session_key - 后端根据
openid查数据库,如果没有这条用户记录,说明是新用户,自动注册;如果已有记录,直接返回登录成功 - 后端生成一个token(我用的JWT),返回给小程序
- 小程序把token存在本地缓存
wx.setStorageSync里,后续所有需要登录态的请求都在请求头带上token
注意,code2Session这个接口的作用是把code换成openid,不是直接把用户的手机号、昵称等资料一并返回。openid是用户在微信平台内的唯一ID,一个用户在一个小程序里只有唯一的openid,但不同小程序之间openid不同。真正能跨小程序标识用户的是unionid,只有在微信开放平台绑定后才会有,这里我们只用到openid就够了。
4.2 后端代码实现:Controller、Service到拦截器
后端实现我拆成三部分讲。
第一步,小程序端调用wx.login()后拿到code,然后请求后端接口:
javascript复制// 小程序端 login.js
wx.login({
success: async (res) => {
if (res.code) {
const loginRes = await wx.request({
url: 'https://你的域名/api/user/login',
method: 'POST',
data: { code: res.code }
});
if (loginRes.data.success) {
wx.setStorageSync('token', loginRes.data.data.token);
wx.setStorageSync('userInfo', loginRes.data.data.userInfo);
}
}
}
});
第二步,后端Controller接收code并调用Service:
java复制@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public Result login(@RequestBody LoginDTO dto) {
return Result.success(userService.login(dto.getCode()));
}
}
第三步,Service里的核心逻辑:
java复制@Service
public class UserServiceImpl implements UserService {
@Value("${wx.appid}")
private String appid;
@Value("${wx.secret}")
private String secret;
@Override
public LoginVO login(String code) {
// 1. 调用微信 code2Session 接口
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid
+ "&secret=" + secret
+ "&js_code=" + code
+ "&grant_type=authorization_code";
String result = HttpUtil.get(url);
JSONObject json = JSONUtil.parseObj(result);
String openid = json.getStr("openid");
if (StrUtil.isBlank(openid)) {
throw new BusinessException("微信登录失败:" + json.getStr("errmsg"));
}
// 2. 查找用户,不存在则注册
User user = this.getOne(new LambdaQueryWrapper<User>()
.eq(User::getOpenid, openid));
if (user == null) {
user = new User();
user.setOpenid(openid);
user.setNickname("微信用户" + RandomUtil.randomNumbers(6));
user.setPoints(0);
user.setStatus(1);
this.save(user);
}
// 3. 生成JWT token
String token = JwtUtil.createToken(user.getId());
return new LoginVO(token, user);
}
}
这里有个细节:微信code2Session接口返回的session_key是用于解密用户手机号等敏感信息的密钥。如果你只是做基础登录,不需要解密用户信息,这个session_key可以不用保存。但你如果要用wx.getUserProfile获取头像昵称,需要把前端传过来的encryptedData和iv用session_key去解密,解密完得到的就是用户授权的真实资料。刚开始我做的时候,就是没搞明白session_key的用处,导致明明调用了wx.getUserProfile,后端却拿不到用户信息,查了半天才明白是少了解密这一步。
4.3 JWT拦截器与token校验
登录态不只要发出去,还要在后续每个需要身份的接口上进行校验。我在SpringBoot里写了一个拦截器,拦截除了登录接口之外的所有/api/**请求。
java复制@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 放行登录等白名单接口
if (request.getRequestURI().contains("/login")) {
return true;
}
String token = request.getHeader("Authorization");
if (StrUtil.isBlank(token)) {
throw new BusinessException(401, "未登录");
}
try {
Long userId = JwtUtil.parseToken(token);
request.setAttribute("userId", userId);
} catch (Exception e) {
throw new BusinessException(401, "token无效或已过期");
}
return true;
}
}
写拦截器的时候我踩过一个坑:只配置了拦截器,忘了在WebMvcConfig里把OPTIONS请求放行。结果小程序端发起了预检请求OPTIONS,直接被拦截器拦住了,前端怎么调都不通。后来在拦截器里加了判断:if ("OPTIONS".equals(request.getMethod())) return true;,问题就解决了。
另外,JWT的token有效期建议设置成7天。项目里用户不需要频繁登录,微信小程序的特点就是打开就能用,如果token设置太短,用户每次打开都要重新登录,体验很差,这不符合小程序的轻量特性。如果要做更细的过期控制,可以配合Redis存储token,每次请求判断Redis中是否存在,不存在就强制重新登录,所有在线用户也能随时强制下线。
4.4 前端用户信息的获取方式
关于“小程序获取登录后的微信用户失败”这个热词,我多说几句。2021年后微信官方调整了用户信息接口的规则,wx.getUserInfo接口不再弹出授权窗口,直接返回默认的灰色头像和“微信用户”昵称。现在获取用户头像昵称的正规方式有两个:
- 使用
wx.getUserProfile接口,但需要在用户点击按钮的触发事件中调用,不能在页面加载时自动调用 - 使用头像昵称填写能力:通过
button的open-type="chooseAvatar"和input的type="nickname",让用户自行填写
我第一次做的时候,按老资料写的wx.getUserInfo,结果用户信息拿不到,登录流程直接卡死。后来改成在个人信息页放一个“点击授权”的按钮,用户点击后调用wx.getUserProfile,成功后再把头像昵称上传给后端,才算解决了问题。
这里也体现了小程序的隐私保护思路:用户数据不是你想拿就能拿的,必须让用户主动、明确地点击授权按钮,每次进入页面都要重新触发授权流程。理解了这一点,你就不会在“获取用户信息失败”上死磕了。
5. 部署与交付:源码+lw+部署文档+讲解,怎么做成一个完整的交付物
5.1 开发完成后的“最后一公里”
项目标题里提到“源码+lw+部署文档+讲解”,这其实是典型的毕业设计或课程设计交付物格式。很多同学代码写完了,却不知道怎么把这套东西交付得完整、专业。根据我的经验,一套合格的交付物至少要有四部分:
- 源码工程:后端SpringBoot项目 + 前端小程序工程
- lw(论文/设计报告):从选题背景到系统设计、功能实现、总结,完整的一份文档
- 部署文档:从环境准备到启动成功的完整步骤,傻瓜式操作
- 讲解视频/演示:录屏展示系统功能,或者PPT演讲稿
很多人的误区是重代码、轻文档。代码写得再漂亮,验收老师不知道你怎么启动、怎么测试,一样扣分。反过来,代码一般但文档清晰、部署顺畅,给人的印象反而更好。文档本身就是项目的一部分,不是为了应付检查而做的形式主义。
5.2 部署全流程:从零到能跑起来
一整套部署步骤,我按“后端 → 数据库 → 小程序端”的顺序来讲。
本机环境准备:
- JDK 1.8(配置好
JAVA_HOME环境变量) - Maven 3.6+
- MySQL 5.7或8.0
- 微信开发者工具(最新稳定版即可)
- 一个微信小程序AppID(注册小程序账号后获取)
后端配置文件修改:
在application.yml中,重点配置三块:
yaml复制server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/ocean_environment?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 你的数据库密码
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
knife4j:
enable: true
wx:
appid: 你的小程序AppID
secret: 你的小程序AppSecret
数据库初始化:在MySQL里创建一个名为ocean_environment的数据库,然后把项目里的sql/init.sql(这是部署文档中最重要的文件)导入。
后端启动顺序:先启动MySQL,确认数据库表已创建;然后在项目根目录执行mvn spring-boot:run,或者在IDE里直接运行OceanApplication.java。启动成功后在浏览器访问http://localhost:8080/doc.html,能看到Knife4j的接口文档页面,说明后端OK。
小程序端配置:用微信开发者工具打开项目中的miniprogram目录,在app.js或config.js里,把baseUrl改成后端接口地址。如果手机预览,不能使用http://localhost:8080,因为手机访问不到电脑的localhost,需要改成电脑在局域网内的IP,比如http://192.168.1.100:8080。并且在微信开发者工具里勾选“不校验合法域名”,否则真机调试时接口会被拦截。
5.3 部署文档怎么写才算合格
部署文档的核心目标是:一个完全没接触过你项目的人,照着文档能一步步把项目跑起来。所以文档里不要省略任何一个细节。
我的文档结构一般是:
- 环境要求:列出JDK版本、MySQL版本、Maven版本,并说明为什么需要这些版本(比如JDK太低SpringBoot 2.7无法运行)
- 获取源码:说明源码目录结构,哪些部分属于后端,哪些属于前端
- 数据库初始化:详细说明导入SQL的每一步
- 运行后端:从IDE启动和命令行启动两种方式都写清楚
- 运行小程序端:微信开发者工具的导入步骤,以及怎么改接口地址
- 常见问题FAQ:端口占用、数据库密码错误、跨域问题、网络超时等
这中间最容易忽略的,是“如何修改自己的配置”。很多文档只写了默认配置,用户拿到手一脸懵,不知道用户名密码在哪里改。所以我在部署文档里每个关键配置项旁边都会加注释,写明这个配置对应哪个环境、改错了会有什么后果。
5.4 lw(论文/设计报告)的章节组织建议
这部分我只说干货,直接列章节建议:
- 第一章 绪论:背景与意义、国内外研究现状、研究内容与方法
- 第二章 相关技术介绍:SpringBoot、微信小程序、MyBatis Plus、MySQL、Redis
- 第三章 系统分析:可行性分析、需求分析、用例分析
- 第四章 系统设计:总体架构设计、功能模块设计、数据库设计
- 第五章 系统实现:每个核心模块的实现思路和关键代码
- 第六章 系统测试:功能测试、接口测试、测试用例表
- 第七章 总结与展望:完成的工作、存在的不足、未来改进方向
写lw的时候,我的建议是“先写数据库和功能设计,再补背景和现状”。因为设计部分是你已经完成的代码,写起来顺手;绪论部分如果一开始就写,很容易写得虚。相关的技术介绍不需要长篇大论,重点是说明“为什么选这个技术”,例如选SpringBoot是因为生态好、配置简单、适合快速开发,而不是因为它“流行”这种废话。
6. 开发中躲不开的坑:版本、图片、跨域、小程序审核
6.1 SpringBoot版本陷阱:从“2.4之后的路由匹配”说起
除了Javax到Jakarta的迁移坑,SpringBoot跨版本还有一个容易被忽视的差异——路由匹配规则。SpringBoot 2.4之前,PathPatternParser和AntPathMatcher混用也没什么大问题;2.4之后,Spring MVC默认改用PathPatternParser,导致一些包含**通配符的拦截器路径匹配方式发生变化。
举个例子,拦截器配置里有些人会写/api/**/*,在旧版本中能正常匹配,到了新版本可能就把路径拦截错了,导致接口404或者拦截器不生效。解决办法是统一改成/api/**,或者显式配置spring.mvc.pathmatch.matching-strategy=ant_path_matcher。
写进本文,是因为这个坑特别隐蔽。你如果只是跟着视频敲代码,敲出来的代码在2.7能跑,换到3.x可能就莫名其妙不行了。做项目前先锁定版本,所有依赖都用配套的稳定版本,能省掉大量排错时间。
6.2 图片上传:本地存还是用云存储
“随手拍上报”功能需要图片上传。很多人第一版实现会把图片传到后端本地磁盘,然后通过http://localhost:8080/upload/xxx.jpg访问。这个方案在本地测试没问题,但会带来几个隐患:
- 小程序真机调试时,
localhost指的是手机本身,不是电脑,图片加载不出来 - 部署到云服务器后,如果用的是多实例部署,图片会分散在多台机器上,访问不稳定
- 后端重启时,如果上传目录配置不对,图片文件可能会丢
解决方法是使用对象存储服务,比如阿里云OSS、腾讯云COS、七牛云。小程序端上传的流程是:先把图片传给后端,后端再上传到OSS,返回一个公网URL给前端使用。
这里有一个很多人不知道的经验:实际上小程序端可以直接把图片上传到OSS,不走后端中转。但有个前提是前端需要向后端请求一个临时上传凭证,也就是STS token或者签名URL。后端的价值在于统一管理上传权限、控制文件合法性和大小。如果项目赶进度,最简单的做法就是先走后端中转,代码简单,而且不用担心安全性。
我实际用的是本地存储+访问路径配置的方式,因为交付环境是单台服务器,不需要太复杂;但我在部署文档中写清楚了,如果要上生产环境,建议把图片迁移到OSS。这个“留有余地”的写法,在答辩的时候会显得你考虑问题周全。
6.3 跨域问题:为什么小程序端明明配置了却还是报错
开发小程序时经常遇到“request:fail”的报错,很多人第一反应是跨域。实际上,微信小程序的wx.request是不受浏览器跨域限制的,因为小程序的运行环境是微信客户端里的JS引擎,不是浏览器。所以后端不需要配置CORS,小程序也能正常请求。
但有一个例外:如果你在微信开发者工具里勾选了“不使用域名校验”,又用了https://的地址,请求会被拦截。这是开发者工具本地的安全策略,不是后端的问题。
真正需要处理跨域的情况是在Web管理后台。比如你给管理员做了个Web端,用http://localhost:8080访问后端,前端运行在http://localhost:3000,这时候才需要在后端加CORS配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
我见过有同学在小程序项目里硬加了CORS配置,结果毫无意义,还在那排查了半天的案例。所以先弄清楚请求是从哪发出的,再决定要不要处理跨域。
6.4 小程序发布与审核:容易被忽略的合规问题
如果你要把这个小程序正式发布,微信审核是一个绕不开的环节。审核不过的原因主要有这几类:
- 涉及新闻资讯的,需要提供相关资质,或者把资讯模块在正式版中隐藏
- 涉及用户上传内容的,必须有内容审核机制
- 类目选择不对,小程序可能被驳回
海洋环保小程序里如果有“垃圾随手拍”这种UGC功能,审核时可能会被要求提供内容安全能力,不然无法上线。处理方案有两个:把UGC功能在体验版开放,正式版暂时关闭入口;或者接入微信内容安全接口security.msgSecCheck和mediaCheckAsync,对用户上传的图片和文字做合规校验。
如果你的主要目的是交付课设或毕设,这一步其实不需要做,本地开发者工具跑通就行。但如果你想把它做成一个真实可运营的产品,那这些合规问题早晚要面对。我在项目的开发过程中,先是在本地把功能全部跑通,然后申请了一个体验版给测试同学用,发现问题再迭代,最后才考虑正式上线的事。这个节奏对你的时间管理会友好很多。
7. 实际运行效果与答辩演示时的几个建议
整个项目跑通之后,我的实测效果是这样的:小程序端打开首页,加载环保资讯和垃圾分类热门搜索;点击“随手拍”功能,选择拍照或者从相册上传,填写描述和位置,提交后能在后台(我做了个简易的Web管理页面)看到待审核的记录;后台审核通过后,用户在小程序的“我的上报”里能看到状态变为“已通过”,同时积分+1。
这里面有一个值得注意的设计:后台审核功能。很多同学做项目只做了用户端,管理员端要么没有,要么只做个表意思一下。但实际上,只要有UGC上报功能,就一定要有后台审核,这既是业务闭环的一部分,也是体现你系统完整度的地方。我在后台实现了概览统计、用户管理、上报审核、积分记录查询四个基本模块,哪怕UI简单一点,功能完整度就直接拉上来了。
答辩演示的时候,我建议按这三个流程走:
- 先演示用户端:登录、浏览资讯、查询垃圾分类、做一条上报记录
- 再切到后台:审核刚才上报的记录,展示审核结果
- 回到用户端:刷新,展示积分变化和状态更新
这个流程是完整闭环,评委能一眼看到系统各个模块是连通的,不是各做个的。这也是我踩过几次坑后总结出的经验——提前把所有演示数据和账号准备好,避免现场演示时查不到数据、网络超时等尴尬。
最后再分享一个提升交付质量的小技巧:做项目的时候,把每个接口的测试用例整理到一张表里,包含接口地址、请求参数、返回结果、是否通过。这张表放到lw的测试章节里,就是现成的测试报告。放到部署文档里,别人也能按这张表快速验证系统是否部署成功。一鱼两吃,工作量没增加,但交付物专业度瞬间提升一个档次。
按照这个流程把源码、论文、部署文档、演示视频都准备好,这个海洋环保小程序项目就能形成一个完整闭环。不管你是拿它做学业交付,还是作为自己的作品集项目,整个过程走一遍,SpringBoot后端开发和小程序前端开发的核心链路基本就都掌握得差不多了。
