1. 项目定位与核心价值分析
1.1 这个项目到底解决了什么问题
第一次看到“广西旅游非遗文化传承系统”这个标题的时候,我脑子里浮现的第一个念头是:这不就是典型的毕业设计选题吗?但仔细拆解下来,这个项目其实比看上去更有嚼头——它把三个完全不同的方向粘合在了一起:Java后端开发、微信小程序前端、非物质文化遗产的数字化保护。这三块单独拎出来都不算新鲜,但组合在一起就形成了一个比较完整的全栈实践闭环。
先说它解决的实际问题。广西是少数民族聚居区,壮族、苗族、侗族、瑶族等世居民族留下了大量非物质文化遗产,从壮锦编织、绣球制作到刘三姐歌谣、铜鼓铸造,再到桂林米粉制作技艺,这些都是国家级或自治区级的非遗项目。但问题在于,非遗的传播半径非常有限——大部分非遗信息停留在学术论文和地方志里,年轻人不知道、游客找不到、传承人又缺乏展示渠道。这个系统做的事情,就是把非遗资料结构化、数字化,再通过微信小程序这个几乎零门槛的入口推送给用户,让游客到了广西能按图索骥找到非遗体验点,让普通用户躺在家里也能浏览非遗项目、了解传承人故事。
再说它的学习价值。如果你是计算机相关专业的学生,或者正在准备转行Java开发的从业者,这个项目等于给你搭好了一个标准化的全栈脚手架:前端是微信小程序原生开发,后端是Spring Boot + MyBatis Plus,数据库用MySQL,中间还涉及文件上传、接口鉴权、数据可视化等常用技能点。你不需要从零去设计一个复杂系统,只需要把这一套跑通、读透,就能把Java后端和小程序前端的主要技术栈串起来了。这一点对新手特别友好——比看碎片化的教程强得多,因为你能看到完整的数据流:小程序页面发起请求、后端接口接收参数、Service层处理业务逻辑、Mapper层读写数据库、结果再一层层返回渲染。
1.2 适合谁来看这份拆解
我把话先说清楚,这绝不是一个只有“毕设党”才需要的东西,虽然它确实是最标准的毕设选题形态。以下几类人建议认真读完:
- 计算机/软件工程专业学生,需要完成毕业设计或课程项目,尤其是选题方向涉及“Web开发”或“小程序开发”的;
- 正在自学Java Spring Boot,但缺乏完整项目练手的新手,想看看真实项目的目录结构、代码分层和接口设计长什么样;
- 对非遗数字化、文旅融合感兴趣的产品经理或运营人员,想了解这类系统通常包含哪些功能模块、数据模型如何设计;
- 需要做一个“带数据展示 + 后台管理 + 移动端浏览”的内容类小程序的企业或个人开发者,这个系统的设计模式可以直接套用改造。
当然,如果你是那种“只想要源码、不想动脑”的选手,我也给你提个醒:源码只能告诉你“怎么做”,文档和视频能告诉你“这是什么”,但只有你自己动手把它跑起来、改一改、加一个功能,你才能真正理解“为什么这么做”。我下面要拆解的,就是这个“为什么”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统整体架构与技术选型拆解
2.1 为什么是Spring Boot + 微信小程序,而不是别的组合
选技术栈这件事,很多人会凭感觉,但真正靠谱的选型逻辑是跟着场景走。这个项目之所以用Spring Boot做后端、微信小程序做前端,背后有几个非常实际的原因。
先看微信小程序。非遗传承系统的目标用户是游客和普通大众,尤其是年轻群体。对这类用户来说,最大的门槛是“要不要装一个App”——答案显然是不想装。而微信小程序依托微信生态,扫码即用、用完即走,没有任何安装成本,这正好契合旅游场景下的碎片化使用习惯。你到了一个景区,看到一块非遗展示牌,扫码打开小程序就能看详情、导航,这个体验是H5网页和独立App都给不了的。再加上微信小程序提供了比较完善的API——定位、地图导航、用户授权、支付(虽然这个项目不一定用到)——开发成本被压得很低。
再看Spring Boot。微信小程序只是一个前端壳子,它本身不连数据库,所有数据都通过HTTP接口从后端获取。这个后端用什么写?可以用Node.js、PHP、Python,但对于一个面向Java学习场景、需要体现企业级开发规范的项目来说,Spring Boot几乎是最优解。它的优势在于:自动配置帮我们省掉了大量XML配置;生态成熟,集成MyBatis Plus、Redis、JWT、文件上传都是开箱即用;分层架构清晰,Controller-Service-Mapper三层天然贴合业务开发习惯;而且Java语言对新手来说虽然啰嗦,但正因为啰嗦,反而能帮助你理解类型、对象、接口这些底层概念。
有人可能会问:那为什么不用Spring Cloud微服务?这个就属于过度设计了。一个非遗展示系统,业务体量根本到不了微服务的程度,单机架构完全够用。强行上微服务只会增加Nacos、Gateway、Feign这些组件的维护成本,对新手来说完全是灾难。做技术选型的第一原则永远是“够用就好”,而不是“越新越猛”。
2.2 前后端分离模式下,数据流是怎么走的
这个系统的交互模型非常典型,理解了它,你就理解了80%的互联网应用:
- 用户在微信小程序里打开页面,点击某个功能入口;
- 小程序通过微信的
wx.request向后端发起一个HTTPS请求,请求里携带参数(可能还有用户登录凭证); - 请求先到达Spring Boot的Controller层(接口层),Controller做参数校验和基础合法性校验;
- Controller调用Service层(业务逻辑层),Service负责处理核心业务规则,比如判断用户是否有权限、数据需要怎么组装;
- Service依赖Mapper层(数据访问层),Mapper通过MyBatis Plus操作MySQL数据库,返回查询或写入结果;
- 数据一层层返回,Controller将Java对象序列化为JSON,通过HTTP响应返回给小程序;
- 小程序拿到JSON数据,通过
setData更新页面视图,用户看到结果。
这套链路看起来简单,但很多细节是新手容易踩坑的。比如跨域问题:小程序端的域名校验很严格,如果你小程序后台配置的request合法域名和实际后端地址不一致,请求根本发不出去。开发环境下很多人选择“不校验合法域名”,但上线必须配置HTTPS域名,这跟本地联调是完全两套逻辑。再比如数据格式:小程序端拿到的时间字段可能是2025-06-01T12:00:00这种ISO格式,页面直接展示就很丑,后端需要统一返回格式化后的字符串,或者前端做一次封装处理。
2.3 核心功能模块一览
从“非遗文化传承”这个业务命题出发,系统功能基本可以锁定为以下六块:
- 用户端(小程序)
- 非遗项目浏览:按类别、地区、热度筛选非遗项目列表,查看详情
- 非遗项目搜索:按名称、关键词模糊查询
- 传承人展示:查看非遗传承人的故事、代表作品、所在区域
- 活动资讯:查看非遗相关的线下活动、展览、体验课程信息
- 个人中心:微信授权登录、收藏感兴趣的非遗项目、浏览足迹
- 管理端(后台)
- 非遗项目管理:新增、编辑、上下架非遗项目,上传图片和视频
- 传承人管理:维护传承人信息,关联对应的非遗项目
- 活动管理:发布、编辑、删除线下活动,管理报名信息
- 分类与地区管理:维护非遗分类(传统技艺、传统舞蹈、传统戏剧等)和广西各地市(南宁、柳州、桂林、北海等)
- 数据统计:非遗项目数量、用户访问量、收藏量等基础数据看板
这些功能看似常规,但放在“非遗传承”的场景下是有业务深意的。比如“传承人展示”这个模块,它的价值不仅仅是展示一个人,而是让非遗从“物”的层面上升到“人”的层面——用户会因为喜欢一位传承人的故事而专程去他的工作室体验。这其实是文旅融合的核心逻辑:用文化故事带动旅游消费。
3. 后端Spring Boot核心实现与实操要点
3.1 项目初始化与依赖选型
我见过太多人死在第一步:Spring Initializr上勾选了一堆依赖,结果一启动就报错,最后不知道是版本冲突还是配置缺失。这里我直接给你一套实测可用的组合,Java 8 + Spring Boot 2.7.x是最稳的,别一上来就上Spring Boot 3.x。
注意:Spring Boot 3.0以后强制要求Java 17,并且javax.servlet变成了jakarta.servlet,很多旧教程的写法直接失效。另外一个很常见的坑是Spring Boot 2.7.x和MyBatis Plus 3.5.3+搭配时,有时候会因为
mybatis-plus-boot-starter版本和Spring Boot版本不兼容导致Mapper扫描不到,建议直接锁死版本号,别用latest。
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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
简单解释一下这些依赖的用途:spring-boot-starter-web提供RESTful接口能力;mybatis-plus-boot-starter是数据访问层的核心,它把单表CRUD全部封装好了,写代码时你只需要定义实体类和Mapper接口,基本的增删改查不需要自己写SQL;hutool-all是个工具包,处理日期、文件上传、随机数之类的小功能特别方便;java-jwt用于用户登录后的Token签发与校验。
3.2 数据库表结构设计:非遗数据应该怎么建模
数据表设计是这类项目最见功力的地方。表设计得好,后续CRUD写起来行云流水;表设计得烂,每一个查询都像是在泥潭里挣扎。基于非遗传承的业务场景,我给出如下核心表设计(省略部分公共字段):
非遗项目表(intangible_cultural_heritage)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,雪花算法生成 |
| name | varchar(100) | 非遗项目名称 |
| category_id | bigint | 分类ID(关联分类表) |
| region_id | bigint | 地区ID(关联广西地市表) |
| level | varchar(20) | 非遗级别(国家级/自治区级/市级) |
| cover_image | varchar(255) | 封面图片URL |
| video_url | varchar(255) | 视频展示URL |
| content | text | 非遗详细介绍 |
| status | tinyint | 状态:0下架 1上架 |
| view_count | int | 浏览次数(用于热门排序) |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
传承人表(inheritor)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| name | varchar(50) | 传承人姓名 |
| avatar | varchar(255) | 头像 |
| description | text | 人物简介、传承经历 |
| region_id | bigint | 所在地区 |
| heritage_id | bigint | 关联的非遗项目ID |
| create_time | datetime | 创建时间 |
用户收藏表(user_favorite)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 用户ID |
| heritage_id | bigint | 非遗项目ID |
| create_time | datetime | 创建时间 |
活动公告表(activity)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| title | varchar(100) | 活动标题 |
| cover_image | varchar(255) | 活动封面 |
| content | text | 活动详情 |
| address | varchar(255) | 活动地点 |
| start_time | datetime | 开始时间 |
| end_time | datetime | 结束时间 |
| status | tinyint | 状态:0未开始 1进行中 2已结束 |
**用户表(user)**相对简单,存微信openid、昵称、头像、手机号就可以,openid是用户在某个微信小程序下的唯一标识,适合做用户主键。
这套设计的核心逻辑在于:非遗项目和传承人是一对多关系(一个项目可以有多位传承人,一个传承人可能参与多个项目,真实场景下是多对多,但为了降低复杂度,简化为一个传承人主要关联一个核心项目,这也是毕设级别的合理简化);用户最多收藏N个项目,用一张中间表搞定。分类和地区都单独建表,是为了方便后台管理端动态维护。
3.3 用户登录与Token鉴权:别再把openid返回给前端
微信小程序的登录流程,官方给的标准流程是:小程序端调用wx.login获取一个code(临时登录凭证,有效期5分钟),然后把code发到后端;后端拿着这个code加上小程序的AppID和AppSecret,调用微信的jscode2session接口,换取openid和session_key。其中openid是用户唯一标识,session_key用于后续解密手机号等敏感信息。
这里有个很多新手容易犯的错误——直接把openid返回给前端,甚至把openid明文存在小程序端。这存在两个问题:一是openid虽然是业务标识,但泄露后可能被恶意用户利用伪造请求;二是你还需要一个身份凭证来维持用户的登录态。正确做法是:后端拿到openid后,去user表查有没有这个用户,没有就自动注册一个,然后签发一个JWT Token返回给小程序。小程序后续所有请求都在Header里带上Authorization: Bearer <token>,后端通过拦截器统一校验。
核心代码长这样:
java复制@Service
public class WxAuthService {
@Autowired
private UserMapper userMapper;
@Value("${wx.appid}")
private String appid;
@Value("${wx.secret}")
private String secret;
public String login(String code) {
// 1. 调用微信接口换取openid
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 = userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getOpenid, openid));
if (user == null) {
user = new User();
user.setOpenid(openid);
user.setNickname("微信用户" + RandomUtil.randomNumbers(6));
user.setCreateTime(LocalDateTime.now());
userMapper.insert(user);
}
// 3. 签发JWT
HashMap<String, String> payload = new HashMap<>();
payload.put("userId", user.getId().toString());
return JWT.create()
.withPayload(payload)
.withExpiresAt(new Date(System.currentTimeMillis() + 7 * 24 * 3600 * 1000L))
.sign(Algorithm.HMAC256("your-secret-key"));
}
}
这里有一个“为什么”值得说透:为什么不直接把openid当Token用?因为openid是静态的、永不过期的。如果别人截获了openid,就能永久冒充你的身份;而JWT Token设置了7天过期时间,过期后需要重新登录。另外,JWT可以携带userId等业务信息,后端在拦截器里解析一下就知道当前请求是谁,不用每次查表换openid,性能上也有优势。
3.4 文件上传与静态资源映射:图片视频怎么存
非遗项目的数据里必然包含大量图片,可能还有视频。文件存储这块,最稳妥的方式是存到服务器本地磁盘,然后通过Nginx或Spring Boot的静态资源映射对外提供访问URL。别一上来就搞FastDFS、MinIO、OSS,那个复杂度对这个小项目来说是多余的。
在application.yml里配置:
yaml复制file:
upload-dir: /data/nonheritage/upload/
spring:
web:
resources:
static-locations: file:${file.upload-dir}, classpath:/static/
上传接口的核心逻辑:
java复制@PostMapping("/api/common/upload")
public Result<String> upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.error("上传文件不能为空");
}
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
String newFileName = UUID.randomUUID().toString().replace("-", "") + suffix;
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs();
}
File dest = new File(dir, newFileName);
file.transferTo(dest);
return Result.success("/files/" + newFileName);
}
我用UUID重命名文件的目的很简单:防止中文文件名乱码,防止重名覆盖,同时避免用户上传的文件名里包含恶意路径字符。这块如果要去折腾(比如后端校验文件类型、限制大小、生成缩略图),可以等基础功能跑通以后再加。
4. 微信小程序端的实现与联动
4.1 页面结构与底部导航设计
小程序的代码结构一般这样规划:
code复制pages/
├── index/ # 首页
├── category/ # 分类列表页
├── detail/ # 非遗项目详情页
├── inheritor/ # 传承人列表页
├── activity/ # 活动资讯页
├── user/ # 个人中心
└── login/ # 登录页(或引导弹窗)
底部导航栏一般放3-5个标签,这个项目我建议放在首页、分类、活动、我的,四个tab就够。为什么一定要有“分类”?因为非遗内容丰富,没有分类的话,用户进来面对上百条数据会无所适从;而“首页”则做搜索入口和运营位(比如精选非遗推荐、热门活动轮播)。这样的信息架构符合移动端的普遍使用习惯,也方便后续运营调整导航项。
4.2 请求封装:别在每个页面里写wx.request
新手最容易犯的毛病就是每个页面里直接调wx.request,结果代码重复、出错又难排查。正确的做法是把请求统一封装成一个request.js模块:
javascript复制const BASE_URL = 'http://localhost:8080';
function request(url, method, data) {
return new Promise((resolve, reject) => {
wx.request({
url: BASE_URL + url,
method: method || 'GET',
data: data || {},
header: {
'Content-Type': 'application/json',
'Authorization': wx.getStorageSync('token')
},
success: (res) => {
if (res.statusCode === 200 && res.data.code === 200) {
resolve(res.data.data);
} else if (res.statusCode === 401) {
// token过期,跳转登录
wx.navigateTo({ url: '/pages/login/login' });
reject(res);
} else {
wx.showToast({ title: res.data.msg, icon: 'none' });
reject(res);
}
},
fail: (err) => {
wx.showToast({ title: '网络异常', icon: 'none' });
reject(err);
}
});
});
}
module.exports = { request };
这里有两个容易被忽略的细节。第一,wx.getStorageSync('token')怎么来的?就是登录接口返回后存的。第二,每个请求都带token,是为了让后端拦截器能识别用户身份——比如收藏、浏览足迹这些功能必须登录才能用,后端拦截器统一判断,前端不需要在每个页面上做登录校验。
4.3 热词里说的“小程序获取登录后的微信用户失败”到底是怎么回事
这个问题的出现频率非常高,我在多个项目里都遇见过。典型表现是:用wx.getUserProfile或者wx.getUserInfo想拿用户的微信昵称和头像,结果返回失败或拿到的数据是灰色头像、默认昵称“微信用户”。
根本原因要往前追溯:微信官方在2021年后调整了用户隐私策略,wx.getUserInfo不再直接返回真实的昵称头像,而是返回一个“匿名”的默认值,除非用户主动点击“授权头像昵称填写”按钮。更麻烦的是,现在即使调wx.getUserProfile,也存在“仅在用户点击按钮时才能调用”的限制,而且越来越多的新版本直接提示“该接口已调整”。
所以现在的正确姿势是:
- 用
open-data组件展示用户头像和昵称(但open-data在很多场景也被限制,不如方案2直观); - 在个人中心放一个“头像昵称填写”的入口,引导用户手动选择头像图片并填写昵称,然后用
wx.uploadFile把头像上传到后端、昵称通过接口提交; - 如果只是需要一个“登录状态”,那用
wx.login拿code换openid就够了,不需要真名真头像。
javascript复制// 用户点击“点击登录”按钮时触发
wx.chooseImage({
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
wx.uploadFile({
url: BASE_URL + '/api/common/upload',
filePath: tempFilePath,
name: 'file',
success: (uploadRes) => {
const avatarUrl = JSON.parse(uploadRes.data).data;
// 然后调后端接口保存用户头像和昵称
request('/api/user/updateInfo', 'POST', {
nickname: this.data.nickname,
avatar: avatarUrl
}).then(() => {
wx.showToast({ title: '登录成功', icon: 'success' });
});
}
});
}
});
这个改动让“登录”这个动作从“偷偷获取用户信息”变成了“用户主动参与”,既符合微信平台规范,也能拿到真实可用的用户头像,后期做社区功能、用户中心都更灵活。
4.4 非遗项目详情页与地图定位的实现思路
非遗项目详情页是这个系统的核心页面,也是最能体现“旅游+非遗”结合点的位置。我建议详情页至少包含四个模块:
- 项目基础信息(名称、级别、分类、地区)
- 项目图文介绍(富文本或多图轮播)
- 传承人信息卡片(头像+简介+跳转)
- 地图定位(如果该非遗项目有体验点或展馆,可以通过
wx.openLocation打开地图导航)
地图定位是旅游场景下的硬需求。流程是:后端在非遗项目表里存latitude和longitude字段,详情页展示一个“查看地图”按钮,点击后调wx.openLocation:
javascript复制wx.openLocation({
latitude: Number(item.latitude),
longitude: Number(item.longitude),
name: item.address || item.name,
address: item.address || '',
scale: 18
});
这里有个小坑:latitude和longitude必须是数字类型,而后端返回JSON时经常是字符串,直接传进去会报错或无法定位。所以小程序端一定要Number(item.latitude)强转。另外,经纬度的精度很关键,建议用高德或腾讯地图后台标注出来的坐标,别用GPS原始坐标,否则在小程序里会有偏移。腾讯地图坐标和微信小程序的地图组件是兼容的,高德坐标通常也没问题,但务必先测试一个点。
5. 管理后台与内容运营的配合
5.1 管理端技术方案怎么选
虽然项目标题里没有明确提到“后台管理”模块,但我几乎可以肯定,这个系统80%的版本都会有一个后台,否则内容无法维护,非遗项目怎么上下架?总不能用SQL直接改数据库吧。后台的实现方式有几种:
- 独立的前后端分离项目:Vue + Element UI / React + Ant Design,后端共用Spring Boot
- 服务端渲染:Thymeleaf模板引擎,一个项目搞定后台和接口
- 直接用小程序管理端(不推荐,体验太差)
如果这是你的毕业设计,我建议用Vue 3 + Element Plus做一个独立后台,这样简历上能多写一个技术栈,而且Vue的后台管理系统是市场上最成熟的应用场景,学一遍不亏。
管理端功能上,至少需要:
- 登录:管理员账号密码,JWT鉴权
- 非遗项目管理:表格展示、搜索、新增/编辑弹窗、富文本编辑器、图片上传
- 传承人管理:关联非遗项目,支持一对多(下拉选择器)
- 活动管理:发布活动、设置时间地点、查看报名人数
- 分类与地区管理:维护下拉选项
- 数据看板:用ECharts展示非遗项目分类占比、区域分布、用户收藏排行
5.2 非遗传承载体的内容策略:不要只做“信息展示”
我见过很多非遗产类项目,做得和静态网站差不多——把所有非遗条目堆上去,用户看两眼就划走了。这样的系统做完就完了,没有什么实际传播价值。真正能让“传承”落地的,靠的是内容运营策略。
实操中我们会做这么几件事:
- 为每个非遗项目写“人话”介绍。别直接贴百科词条,改用“广西人都知道……”“这道美食背后……”这种有温度的口吻。
- 把传承人的故事作为重点内容。真实人物故事比干巴巴的描述更有感染力,用户会为“手艺人”的故事停留。
- 在首页做推荐位区分。比如“最热非遗”“即将失传的技艺”“亲子体验推荐”这样的招商位,把运营意图通过页面结构化表达。
- 把线下活动和线上内容打通。用户在详情页看到活动信息,可以直接跳转活动页查看时间和地址,甚至预留电话报名入口。
这套内容策略不是技术问题,但它决定了这个系统的“魂”。作为开发者也应该理解业务的这些诉求,返过来优化接口设计——比如首页推荐位需要后端支持“按排序字段批量查询”,活动列表需要支持“按城市筛选、按状态筛选”,这些都是提前在接口设计阶段就考虑好的。
6. 常见问题排查与部署避坑实录
6.1 运行项目时的3个高频问题
我把这类项目学员遇到的典型问题整理成一个清单,每条都是我确认过真实原因的。
问题1:Spring Boot启动后,访问接口报404
排查思路:先看Controller类上有没有加@RestController,有没有加@RequestMapping;再看Spring Boot启动类,@SpringBootApplication默认扫描启动类所在包及子包,如果你的Controller放在子包之外根本扫描不到。另外MyBatis Plus的Mapper接口要加@Mapper注解,或在启动类上加@MapperScan("com.xxx.mapper"),否则报Invalid bound statement。
问题2:小程序请求后端报request:fail
排查思路:绝大多数情况是域名或端口配置问题。开发模式下,小程序后台勾选“不校验合法域名”,并把BASE_URL里的地址改成你电脑的局域网IP而不是localhost——因为真机调试时,手机访问不了你电脑的localhost。如果是模拟器,localhost还能用;一旦用真机预览,必须改成局域网IP,并且确保手机和电脑在同一WiFi下。
问题3:数据库连不上,报Access denied for user 'root'@'localhost'
排查思路:99%是密码错了或权限没给够。建议在MySQL里单独建一个业务账号,不要用root连生产库:
sql复制CREATE USER 'nonheritage'@'%' IDENTIFIED BY 'Nonheritage@123';
GRANT ALL PRIVILEGES ON nonheritage.* TO 'nonheritage'@'%';
FLUSH PRIVILEGES;
6.2 上线部署的相对可靠方案
很多同学的项目止步于“本地能跑”。但如果你想让这个项目真正能给别人演示、或者作为求职作品展示,我建议至少把它部署到一台云服务器上。
最低配方案(成本可控):
- 一台2核4G的云服务器,装CentOS 7或Ubuntu 20.04
- JDK 8 / MySQL 8.0
- Nginx 作为反向代理
部署步骤的简化版:
- 本地把Spring Boot项目打包成jar:
mvn clean package -DskipTests - 把jar上传到服务器,写一个启动脚本:
bash复制#!/bin/bash
nohup java -jar nonheritage.jar --spring.profiles.active=prod > service.log 2>&1 &
- 配置Nginx反向代理,把
/api/转发到http://localhost:8080/api/,同时配置HTTPS证书 - 小程序端把
BASE_URL改成你的HTTPS域名,并在微信公众平台添加request合法域名 - 用凡科/阿里云等平台注册一个域名,配置SSL证书(微信小程序要求必须HTTPS)
这里有一个新手容易忽略的坑:Spring Boot默认的Tomcat端口是8080,如果你用Nginx做反向代理,Nginx监听443和80,转发到8080,那么后端代码里所有返回前端的URL前缀都要特别注意——是相对路径还是绝对路径。如果顺手写死了http://localhost:8080/files/xxx,用户手机上根本加载不出图片。
6.3 关于“Spring Boot版本太高”这个热搜词的补充
最近Java面试题和相关社群里,“Spring Boot版本太高”这个说法反复出现,说穿了就是版本迁移的成本问题。Spring Boot 3.x引入了很多破坏性变更,比如Java 17基线、Jakarta命名空间、Spring Security 6的配置变化等,对老项目来说升级代价不小。对于像本系统这样的学习性质项目,我的建议是按部就班:先啃透2.7.x,理解自动配置、Starter机制、Spring MVC的工作流程,之后再接触3.x的差异也不迟。没必要一上来就追新,除非你明确需要Spring Boot 3带来的新特性(比如GraalVM原生镜像)。
7. 关于这套体系后续还能怎么用
任何项目都值得思考“做完以后怎么扩展”,这句话不是套话,而是实打实的项目规划问题。这个非遗传承系统的架构决定了它能向好几个方向延伸,而且扩展成本都很低。
如果要做“电商变现”,在现有用户、非遗项目、活动的基础上,加一个商城模块——非遗文创产品展示、购物车、订单、支付(微信支付)——数据模型和现有体系天然衔接,后端也只需要增加订单相关表和接口就能撑起来。
如果要做“社交传播”,可以加一个用户的点赞、评论、分享功能,小程序自带的wx.showShareMenu开起转发功能,用户把好看的非遗手工艺分享给朋友,传播效果非常直观。
如果要做“在线学习”,非遗的传承不能只靠信息展示,可以加入视频课程、大师直播、在线预约学习的功能,这就变成了一个轻量级的在线教育系统。
但这些都是后话。把一个项目吃透,才是继续前进的底气。真正的收获不在于跑通一遍,而在于弄明白为什么要用Spring Boot做接口、为什么要用微信小程序做入口、为什么非遗这种文化数据需要结构化地沉淀下来——这些认知,比源码本身值钱得多。
最后分享一个我看源码时常用的笨办法:拿到一个项目,先把数据库的表全部列出来,用手画一遍表关系图,然后挑一条最完整的业务链路(比如“用户登录—浏览非遗—收藏”),从Controller代码第一行开始往底层追,一个方法一个方法地看懂它在干什么。这个方法很慢,但对理解代码结构极其有效。我每次接手新项目都这么干,跑完几轮之后,项目在你脑子里就是立体的了。
