资讯动态

SpringBoot+Uniapp前后端分离校园圈综合平台设计与实战

发布时间:2026/8/31 13:18:59 来源:尧图企业网站定制
简介这是一套面向高校开发者与毕业设计学生的前后端分离校园社区实战项目聚焦校园集市、表白墙、论坛、失物招领、校园墙及跳蚤市场六大核心场景解决校园内信息互通、情感表达与资源流转的现实需求。资源包共564个文件涵盖113个Java后端服务类如AuthServiceImpl、PostingServiceImpl、127个Vue前端页面、253个JS逻辑脚本以及SQL建表语句、YML配置、SCSS样式等完整呈现SpringBootMyBatisPlusSpringSecurityRedisMongoDBMinIOQuartz后端架构与UniappuView跨端前端实现压缩包仅1.85MB轻量易部署。已有74人学习下载适合中高级开发者快速掌握多模块集成、权限控制、文件存储、定时任务与非结构化数据处理等工程实践能力项目结构清晰、模块解耦明确可直接用于课程设计、毕设开发或二次扩展。 大学里做完一个完整项目最怕的就是“写完就忘、复盘没货”。这段时间正好又翻看过往的项目源码发现 SpringBoot Uniapp 这种前后端分离的校园圈综合平台真的很适合拿来当毕设、课设或者个人练手的完整例子。它把校园集市、表白墙、论坛、失物招领、校园墙、跳蚤市场这些高频场景全部串在一个系统里无论是后端接口设计、数据库表关系还是前端跨端适配都有足够的深度可以聊。今天我以一个做这类项目的老开发身份把这套系统的设计思路、数据库建模、后端实现、前端落地、部署上线以及踩坑记录完整拆一遍。不管你是想直接参考源码跑起来还是想理解其中每个模块为什么要这么写这篇应该都能帮到你。1. 项目整体设计与技术选型思路1.1 为什么是 SpringBoot Uniapp 这个组合先说后端。SpringBoot 在这类项目里几乎成了默认选项原因是它把 SSM 那一套繁琐的 XML 配置全部收敛成了自动配置你只需要在pom.xml里引入对应 starter再写一个带SpringBootApplication的入口类项目就能跑起来。对于校园集市、论坛这类 CRUD 占大头、业务逻辑清晰但又不算复杂的场景SpringBoot 的 MVC 分层足够用而且生态成熟后期接 Redis、RabbitMQ、OSS 都很方便。前端选 Uniapp核心诉求是“一套代码多端运行”。同一个校园圈项目很多人的需求是既能跑微信小程序又能打包成 Android/iOS App还能发一个 H5 链接方便在浏览器里直接预览。Uniapp 基于 Vue 语法通过编译条件把同一套页面编译到不同平台这正好满足这种多端投放需求。虽然跨端方案在原生能力和性能上不如 Flutter、React Native 这类渲染级框架但校园类应用的交互以表单、列表、图片、详情页为主Uniapp 的坑相对可控开发效率也高。这里有个很现实的原因大多数校园项目的使用者是学生设备五花八门既有安卓也有 iPhone很多同学根本没有电脑配合扫码调试所以一个小程序端 H5 端就能覆盖绝大多数使用场景。技术选型从来不是越高级越好而是匹配你的用户群体和团队维护能力。SpringBoot Uniapp MySQL 这个组合成本低、资料多、门槛友好是这类项目管理成本最低的方案之一。1.2 前后端分离的结构给项目带来了什么前后端分离最直观的变化是后端不再返回页面只返回 JSON 数据前端通过 HTTP 接口拿到数据后再渲染页面。这意味着后端和前端可以完全独立开发、独立部署。在这套校园圈里后端只负责用户认证、帖子管理、评论互动、交易状态流转等业务逻辑前端只负责页面展示、交互反馈、数据请求分工非常清晰。实际开发中这个分离带来的好处很明显。比如表白墙模块后端只需要提供一个发布和查询接口前端自己去决定是展示成瀑布流还是时间线失物招领模块后端只需要维护物品状态字段前端根据状态渲染“未认领”“认领中”“已完成”的不同按钮。后续如果要给 Web 管理后台复用同一套接口后端代码几乎不用改动只需要前端新增一个管理端页面。但分离也引入了一个老生常谈的问题——跨域。开发阶段前端在localhost:8080跑后端在localhost:8081跑两者端口不同浏览器会拦截跨域请求。解决办法有两个一是后端配置 CORS 过滤器二是部署阶段用 Nginx 做反向代理把/api路径转发到后端服务这样前端页面和后端接口就变成了“同源”既解决了跨域也隐藏了后端真实端口。我建议开发阶段两种都配上省得来回切换。1.3 六类功能模块如何融入同一套用户与内容体系先给这些模块分个类。校园论坛和校园墙本质上是“内容展示 互动”用户发帖、回帖、点赞校园集市和跳蚤市场其实是同一个商业场景只是叫法不同学生发布闲置物品、设置价格、等待买家联系失物招领是特殊的帖子类型带物品状态和认领流程表白墙则是在普通帖子的基础上加了匿名属性。这六类功能如果每个都建一套独立表会让数据库变得非常冗余而且用户、评论、点赞这些公共模块要反复关联。实际项目中采用一种更聪明的做法用一张统一的post帖子表承载所有场景通过type字段区分是论坛帖、集市商品、失物招领还是表白墙内容再针对不同场景补充一到两个扩展字段。比如集市需要 price 和交易状态失物招领需要物品状态和认领状态这些都作为可空字段放在扩展列里。这种设计的好处是所有帖子的评论、点赞、图片、浏览数都可以复用同一套逻辑前端列表页也只需要根据type参数调用不同的查询接口。代价是post表字段会稍微杂一些需要写清楚注释避免维护的人搞混。但从整体代码量和可维护性来看这个取舍非常值得。2. 数据库设计与核心表结构拆解2.1 用户表与账号安全设计用户表是所有业务的基础。我在项目里的表结构大致长这样CREATE TABLE sys_user ( id int NOT NULL AUTO_INCREMENT COMMENT 用户ID, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(100) NOT NULL COMMENT BCrypt加密密码, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像地址, gender tinyint DEFAULT 0 COMMENT 性别 0未知 1男 2女, grade varchar(20) DEFAULT NULL COMMENT 年级, phone varchar(20) DEFAULT NULL COMMENT 联系电话, status tinyint DEFAULT 1 COMMENT 状态 1正常 0禁用, create_time datetime DEFAULT NULL COMMENT 创建时间, update_time datetime DEFAULT NULL COMMENT 更新时间, deleted tinyint DEFAULT 0 COMMENT 逻辑删除 0未删 1已删, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;密码字段绝对不能明文存储。项目中使用 BCrypt 加密也就是spring-security-crypto里自带的BCryptPasswordEncoder每次校验时用matches()比对明文和密文。之所以不用 MD5 或 SHA256是因为这两类摘要算法没有加盐机制相同密码会产生相同哈希配合彩虹表很容易被反查。BCrypt 每次加密会生成随机盐同一个密码两次加密结果都不同安全性不在一个量级。头像字段存的不是二进制也不是 base64而是图片的 URL 地址。这样数据库只记录字符串图片文件存储到磁盘或 OSS前端拿到 URL 直接渲染。逻辑删除字段deleted很重要用户注销时不能真删数据否则他发过的帖子、评论全部悬空所以统一用逻辑删除标记。2.2 帖子表用一套表承载五类场景这张表是整个系统的核心几乎所有内容型功能都围绕它展开。我给出一个简化但可用的结构CREATE TABLE post ( id int NOT NULL AUTO_INCREMENT COMMENT 帖子ID, user_id int DEFAULT NULL COMMENT 发布人ID匿名时为空, type int NOT NULL COMMENT 类型 1论坛 2表白墙 3失物 4寻物 5集市, title varchar(100) DEFAULT NULL COMMENT 标题, content text COMMENT 正文内容, images varchar(1000) DEFAULT NULL COMMENT 图片多张用逗号分隔, price decimal(10,2) DEFAULT NULL COMMENT 物品价格集市, original_price decimal(10,2) DEFAULT NULL COMMENT 原价集市, item_status tinyint DEFAULT 0 COMMENT 物品状态 0在售 1已售 2下架集市, claim_status tinyint DEFAULT 0 COMMENT 认领状态 0未认领 1已认领 2已结束失物, is_anonymous tinyint DEFAULT 0 COMMENT 是否匿名 0否 1是表白墙, contact varchar(50) DEFAULT NULL COMMENT 联系方式, view_count int DEFAULT 0 COMMENT 浏览量, status tinyint DEFAULT 1 COMMENT 审核状态 0待审核 1正常 2违规, create_time datetime DEFAULT NULL COMMENT 发布时间, update_time datetime DEFAULT NULL COMMENT 更新时间, deleted tinyint DEFAULT 0 COMMENT 逻辑删除, PRIMARY KEY (id), KEY idx_type (type), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT帖子表;type 字段承担了模块区分的作用这也是统一表中最重要的设计决策。论坛帖和表白墙帖基本只用公共字段集市帖额外使用 price、original_price、item_status失物招领帖使用 claim_status 和 contact。如果字段不够用比如表白墙的“标签”或集市的“成色”再单独建扩展表或者用 JSON 字段不会影响主体结构。匿名表白墙的特殊点在于user_id置空但为了后续如果出现违规内容还能追溯我前期设计时还是保留了一个叫做real_user_id的关联字段只对数据库管理员可见接口层永远不做返回。这里给在做匿名模块的开发者提个醒匿名不等于无痕日志和数据库里保留可追溯信息是底线否则一旦出现纠纷或者违规内容尴尬的不只是运营方。2.3 评论、点赞、收藏等互动数据怎么存互动数据属于高频写入但结构非常简单。评论表的关键是支持楼中楼也就是子评论。我采用自关联方式parent_id指向父评论reply_user_id记录被回复人CREATE TABLE comment ( id int NOT NULL AUTO_INCREMENT, post_id int NOT NULL COMMENT 帖子ID, user_id int DEFAULT NULL COMMENT 评论人ID匿名评论时为空, content varchar(500) NOT NULL COMMENT 评论内容, parent_id int DEFAULT 0 COMMENT 父评论ID0表示顶级评论, reply_user_id int DEFAULT NULL COMMENT 被回复人ID, create_time datetime DEFAULT NULL, deleted tinyint DEFAULT 0, PRIMARY KEY (id), KEY idx_post (post_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT评论表;点赞和收藏属于“用户-对象”型关系理论上用一张通用表就够但我在项目里拆成了两张独立的表理由很简单——点赞和收藏的业务语义不同点赞是一对多且无重复收藏存在“收藏后再次点击取消”的交互分开表结构后查询逻辑更清晰也不会互相影响。点赞表我用user_id target_type target_id做了联合唯一索引确保同一个用户对同一个帖子只能点赞一次。帖子浏览数这种高频数字我没有直接实时更新数据库中的view_count而是先在前端记录用户是否已经浏览过再异步调用增加接口。虽然并发量大时还是会有一点压力但校园项目量级完全够用。这里的一个小经验是view_count的更新语句一定要写成update post set view_count view_count 1 where id ?而不是先查出当前值再加一否则并发下会丢失更新。2.4 集市订单与失物认领的状态流转设计集市模块最关键的字段是item_status。我在设计时定义了三个状态0 在售、1 已售、2 下架。发布商品时默认在售买家联系卖家并线下完成交易后卖家在“我发布的”页面把商品标记为已售“下架”是卖家主动操作商品不再展示在列表中但记录保留。这套状态机很简单但能覆盖校园二手交易的绝大多数场景。失物招领的状态流转比集市稍微复杂一点因为涉及“认领”这个动作。我采用的简化方案是失主发布失物帖状态是“未认领”看到帖子的人如果捡到了对应物品通过帖子里留的联系方式联系失主失主确认后把状态改成“已认领”。如果是寻物启事状态流转逻辑反过来捡到者联系发布人。这里最需要注意的点是平台不应该直接提供“点击认领即完成”的交互一定要保留线下核对环节否则很容易产生冒领纠纷这也是业务设计上的一个安全考量。为了记录状态变更轨迹我额外建了一张trade_log表记录谁在什么时间把物品状态从 A 改成了 B同时保存操作人 ID 和备注。这张表在排查“物品明明已经被认领怎么又显示出来了”之类的问题时非常有价值也算是个被很多初学者忽略的经验。3. 后端实操从零搭建 SpringBoot 服务3.1 项目分层与初始化配置SpringBoot 项目的分层我习惯这样分com.campuscircle ├── common // 通用返回体、常量、异常定义 ├── config // 跨域、拦截器、文件配置 ├── controller // 接口层 ├── entity // 数据库实体 ├── mapper // MyBatis-Plus 数据访问层 ├── service // 业务逻辑层 └── utils // 工具类JWT、文件上传等Controller 只做参数接收和结果包装Service 做业务判断和事务控制Mapper 层使用 MyBatis-Plus 的BaseMapper提供基础增删改查复杂 SQL 就用Select注解或者 XML 写。校园项目不建议引入过重的微服务框架单体应用 清晰分层足够支撑业务快速迭代也方便后续维护。application.yml里比较重要的几个配置项有数据源、MyBatis-Plus 逻辑删除、jackson 日期格式和文件上传路径。我贴一段关键配置server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/campus_circle?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 configuration: map-underscore-to-camel-case: truemap-underscore-to-camel-case必须开启否则数据库的create_time无法自动映射成 Java 实体里的createTime。日期时区配置用Asia/Shanghai避免本地运行和服务器上出现 8 小时时差。3.2 统一返回体与全局异常处理前后端分离的接口设计里统一返回体非常关键。我在common包下定义了一个ResultT类所有接口都返回它Data public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMsg(success); result.setData(data); return result; } public static T ResultT error(Integer code, String msg) { ResultT result new Result(); result.setCode(code); result.setMsg(msg); return result; } }前端拿到这个结构后只需要判断code是不是 200就能决定走成功回调还是错误提示不用再各自解析各种乱糟糟的返回格式。全局异常处理通过RestControllerAdvice实现。我在这里统一捕获三类异常业务异常比如“商品已下架”、参数校验异常比如“标题不能为空”、系统异常兜底记录日志。这样的话Controller 里代码会非常干净不用到处写 try-catch业务异常只需要throw new BusinessException(商品不存在或已下架)剩下的交给全局处理器统一转成Result.error返回给前端。3.3 JWT 登录认证与登录白名单因为 Uniapp 要跨 H5、小程序、App 三端运行服务端登录认证最好不要依赖传统 Session 和 Cookie而是使用 JWTJSON Web Token。登录成功后后端生成 token 返回给前端前端把 token 存到uni.setStorageSync后续每个请求都在 Header 里带上Authorization: Bearer token。后端通过拦截器校验 token从 token 里解析出用户 ID放入ThreadLocal或请求参数中。JWT 工具类核心是生成和解析使用 jjwt 库代码大概这样public String createToken(Integer userId, String username) { long expireTime 7 * 24 * 60 * 60 * 1000; // 7天过期 return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(username, username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() expireTime)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }拦截器里要注意“白名单”的概念。登录、注册、验证码、首页已经审核通过的帖子列表这些不需要登录的接口要放行其余接口统一校验 token。这里踩过一个坑小程序端如果请求头没有带上 token后端不会返回 401而是直接返回“登录已过期”前端又会跳到登录页造成死循环。所以异常处理里对未登录情况要区分“还没有 token”和“token 已过期”前端根据不同的 code 分别处理一般就能规避。3.4 文件上传与静态资源映射校园集市和失物招领都离不开图片上传。我在后端实现了一个简单的文件上传接口接收MultipartFile把文件保存到服务器本地目录然后返回访问 URL。核心逻辑不复杂但要注意三件事一是文件类型和大小校验不能只靠前端限制后端也必须校验禁止.exe、.jsp等危险文件类型图片大小限制在 5MB 以内。二是文件名不能使用用户上传的原始文件名避免中文、特殊字符引发问题我用UUID.randomUUID().toString()重命名保留后缀。三是保存路径和访问路径要分离物理文件存放在/data/campus/upload/但对外访问 URL 是http://ip:8081/images/2025/xx.jpg这需要配置虚拟路径映射。SpringBoot 配置虚拟路径映射的方式是在WebMvcConfigurer里实现Override public void addResourceHandlers(ResourceHandlerRegistry registry) { String uploadPath file: System.getProperty(user.dir) /upload/; registry.addResourceHandler(/images/**).addResourceLocations(uploadPath); }这里请务必记得本地开发路径可以用相对路径但服务器部署时一定要让上传目录位于项目运行目录之外比如/home/campus/upload然后在启动命令里或者配置文件里指定。避免每次java -jar部署时因为目录被清理而丢图片这个坑我替大家踩过了。3.5 匿名表白墙如何在后端实现表白墙的匿名功能看着简单其实是全项目逻辑最微妙的部分之一。后端实现时我做了两个层面的设计。第一层是写入侧用户发布表白墙内容时请求参数里带一个isAnonymous标记。如果为 true后端在post表里把user_id置空同时把真实用户 ID 写入real_user_id字段。注意这个字段必须通过TableField(select false)或查询时手动排除否则 MyBatis-Plus 的默认查询会把真实用户 ID 一并返回给前端。第二层是读取侧前端展示帖子的作者信息时不能直接拿user_id去关联用户表。我后端封装了一个PostVO视图对象把作者信息、点赞数、评论数、是否已点赞等展示字段组装进去。匿名帖的PostVO里作者昵称置为“匿名同学”头像置为默认匿名头像。评论区和点赞区同样要注意匿名帖的点赞和评论不应展示真实昵称否则“匿名”就形同虚设。这里还要防一个常见漏洞新增评论接口带user_id参数时后端不能信任前端传的值而应该统一从 token 里解析。否则别人构造一个请求伪造user_id1就能冒充管理员或者任意用户评论这种越权问题在校园项目里其实很容易被抓到。4. 前端实操Uniapp 跨端开发的落地细节4.1 页面结构的规划与 tabBar 设计Uniapp 前端页面的组织方式直接影响后续维护成本。我把页面按模块拆分开pages/ ├── index/ // 首页帖子流/校园墙 ├── forum/ // 论坛列表与详情 ├── market/ // 校园集市 ├── lost/ // 失物招领 ├── confession/ // 表白墙 ├── publish/ // 发布中心 ├── mine/ // 个人中心 │ ├── myPost.vue // 我的发布 │ ├── myComment.vue // 我的评论 │ ├── favorite.vue // 我的收藏 │ └── settings.vue // 设置页 └── login/ // 登录注册tabBar 我选了四个比较高频的入口首页、集市、发布、我的。发布中心用中间凸起按钮是常见思路但这里有个小建议如果项目直接使用自定义 tabBar在小程序端要注意兼容性和性能如果赶进度用原生 tabBar 最稳发布入口可以用普通页面承接不一定要凸起按钮。发布页是整个系统的“输入中枢”也是页面跳转隐藏得最浅的地方。我从类型选择入手用户选择“集市发布”“表白墙发布”“失物发布”“论坛发帖”后页面展示对应表单字段比如集市发布会多一个“价格”和“物品成色”输入框失物发布会多一个“物品类型”和“丢失地点”输入框。这样一个页面服务多个模块减少重复代码但要求字段的显隐控制写得足够清晰。4.2 网络请求封装request token 持久化Uniapp 推荐使用uni.request但我不建议直接在页面里写uni.request而是封装一个公共的 request 方法。原因只有四个字统一管理。统一管理 baseURL、统一注入 token、统一处理登录过期、统一解析返回结果。我习惯在utils/request.js里做一个 Promise 封装的实例核心逻辑如下const BASE_URL http://localhost:8081/api; export function request(url, method GET, data {}) { return new Promise((resolve, reject) { const token uni.getStorageSync(token); uni.request({ url: BASE_URL url, method, data, header: { Content-Type: application/json, Authorization: token ? Bearer token : }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { uni.removeStorageSync(token); uni.navigateTo({ url: /pages/login/login }); reject(res.data); } else { uni.showToast({ title: res.data.msg || 请求失败, icon: none }); reject(res.data); } }, fail: (err) { uni.showToast({ title: 网络异常请稍后重试, icon: none }); reject(err); } }); }); }token 持久化用uni.getStorageSync和uni.setStorageSync在小程序、App、H5 三端表现一致。这里特别提醒一句H5 端刷新页面后uni.setStorageSync的默认存储可能依赖 LocalStorage如果用户关闭了浏览器隐私模式可能导致数据丢失所以封装里可以在 set 之后立刻同步到 Vuex 或 Pinia防止页面状态不一致。不过对校园项目而言直接读 Storage 已经够用。4.3 列表页的分页加载与下拉刷新校园圈这种信息流应用列表页的体验基本决定整个系统的评价。我统一使用“下拉刷新 触底加载更多”的组合这在小程序和 App 端是标准交互。分页接口固定传两个参数pageNum和pageSize。后端返回一个带total总数的分页对象前端根据total判断是否还有更多。触底加载通过 Uniapp 的onReachBottom生命周期实现每触发一次把 pageNum 加一请求新数据后追加到已有数组末尾。下拉刷新通过onPullDownRefresh实现重置 pageNum 为 1清空原有列表重新拉第一页。这里有三个很容易踩的细节。第一商品列表和帖子列表要加“加载中”状态防止用户连续触底时发出重复请求第二每次请求前判断this.loading是否还在执行中如果正在请求就 return第三某些 App 端下拉刷新动画不会自动消失必须在uni.stopPullDownRefresh()里手动关闭。不处理这几个细节App 端上下拉会出现卡顿和闪烁体验非常拉胯。4.4 图片上传与跨端兼容处理用户在发布页选择图片调用uni.chooseImage拿本地路径然后通过uni.uploadFile传到后端。这里有几个跨端差异要注意微信小程序端uni.chooseImage返回的 tempFilePaths 可以直接作为上传参数。App 端如果使用v-model绑定图片路径有时存的是blob:或者file://开头这种路径不能直接用于image标签展示需要用uni.getFileSystemManager转换为可展示的临时路径或者干脆用上传成功后的服务器 URL 回显。H5 端能选的图片格式最灵活但大图片容易导致内存溢出可以在前端先做一次压缩再上传能够明显提升集成度。上传进度条用uni.uploadFile的success回调中拿返回结果。接口返回图片URL后前端把 URL 存起来等用户点击“发布”时把多个 URL 用逗号拼接成字符串再传给后端。这种存储方式简单但要注意 URL 本身不要包含逗号否则解析会出错。4.5 H5、小程序、App 三端的差异点多端开发最大的挑战是“同一份代码在不同平台表现不一致”。我总结几个我在这个项目里实际遇到的差异第一个是路由跳转。小程序端uni.navigateTo的页面栈限制是 10 层超过后跳转失败所以深嵌套详情页时要考虑用uni.redirectTo替换或者uni.reLaunch重开页面H5 端则没有这个问题。第二个是请求 baseURL 的差异。开发时 H5 可以调localhost:8081但微信开发者工具需要勾选“不校验合法域名”Android 模拟器/真机不能访问宿主机的localhost要么用局域网 IP要么在 HBuilderX 里把 baseURL 封装成根据环境变量切换。我建议直接建一个config.js用uni.getSystemInfoSync().platform判断平台动态设置 baseURL但更稳妥的方案是统一用一个能访问到的服务器地址。第三个是原生组件层级问题。App 端在小程序里使用map、video这类原生组件时层级经常遮挡普通页面元素这也是热词里不少人问“uniapp 地图遮挡不适配”的原因。在这个项目里暂时不涉及地图但如果后续给失物招领加“拾获地点标注”功能就要提前考虑这个痛点用cover-view包一层或者改用组件库实现。第四个是分享。小程序端分享依赖onShareAppMessage不主动设置时分享卡片会没有标题和缩略图App 端分享需要配置对应的分享渠道 SDK否则分享按钮无效。校园墙这种传播属性强的功能分享接口值得认真配一遍。5. 从本地到上线部署流程与典型问题排查5.1 本地环境搭建十分钟跑起来如果你拿到的是“项目源代码 数据库”的压缩包第一步通常是先初始化数据库。我建议用 navicat 或者命令行执行 SQL 文件建一个名为campus_circle的数据库导入完成后确认表结构和数据没问题。然后打开后端项目在application.yml里修改数据库账号密码确认 Redis 是否被用到如果项目里有验证码功能或者缓存需求才会用到没有就直接忽略启动CampusCircleApplication。前端用 HBuilderX 打开项目目录第一次运行前先执行npm install安装依赖。如果项目使用了 uni_modules 组件库HBuilderX 会自动识别如果没有 package.json说明是传统目录结构通常不需要安装依赖直接运行到微信开发者工具或浏览器即可。这里遇到最多的问题就是“明明引入了组件页面却空白”绝大多数情况是没有安装对应插件或没有编译先在控制台看编译日志比盯着页面猜要快得多。5.2 打包部署后端 jar 前端 H5/小程序后端部署相对简单本地执行mvn clean package -DskipTests会生成一个可执行的 jar 包上传到服务器后运行nohup java -jar campus-circle-1.0.0.jar --spring.profiles.activeprod app.log 21 生产环境我建议用单独的application-prod.yml在里面配置正式的数据库地址、上传路径、日志级别。启动后先curl一下接口确认能返 JSON 再继续部署前端。前端如果发布 H5用 HBuilderX 的“发行——网站-PC Web或手机H5”功能生成静态文件后上传到 Nginx 的html目录。Nginx 配置上要做两件事一是location /指向前端静态文件二是location /api/反向代理到后端端口server { listen 80; server_name your.domain.com; location / { root /home/campus/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8081/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }如果发布微信小程序在 HBuilderX 里“发行——小程序-微信”把生成的dist/build/mp-weixin目录导入微信开发者工具填写 AppID配置合法 request 域名。域名必须是 HTTPS 且经过备案否则正式版会报“不在以下 request 合法域名列表中”开发阶段可以勾选“不校验合法域名”绕过。5.3 常见问题速查表我在开发过程中整理了一张问题排查表基本覆盖了这类项目最容易翻车的点现象可能原因处理方式前后端接口调用报跨域后端未配置 CORS或 Nginx 未做反向代理后端加CorsFilter或按上面 Nginx 配置转同源图片上传后访问 404静态资源路径映射没配好确认addResourceHandlers路径与实际保存路径一致微信小程序白屏未勾选不校验域名或使用未备案的 HTTP 接口开发工具勾选“不校验合法域名”正式版配置 HTTPS 域名App 真机访问不到本地接口手机访问localhost指向本机改用局域网 IP例如http://192.168.x.x:8081数据库时间相差 8 小时JDBC 链接未指定serverTimezone在application.yml的 URL 中加serverTimezoneAsia/Shanghai列表页重复加载未加loading状态控制在请求前判断this.loading请求中 return匿名帖子显示真实头像后端 PostVO 未对匿名作者做脱敏查询结果中把user_id为空的作者信息替换为默认匿名用户H5 打包后刷新 404前端路由 history 模式未做 try_files在 Nginx location 中配置try_files $uri $uri/ /index.htmltoken 过期后无限弹登录前端未做 401 统一跳转request 封装中统一处理 401清除 token 后跳转登录页这些问题的共性是前端页面报错往往不是真正原因控制台和后端日志才是第一现场。花 10 分钟看日志比在页面里反复刷新猜原因高效得多。5.4 扩展方向与后续维护跑通基础功能之后我再提供几个低成本高收益的扩展方向。第一个是内容审核。校园墙和表白墙发帖频率高如果不加审核很容易出现违规内容。最简单的方案是给post.status增加“待审核”状态发布后默认隐藏由管理员在后台通过后展示。后台不用重新开发一套系统直接用一个简单的 Vue 管理页面或者复用前端的小程序页面加个角色判断就行。第二个是消息通知。有人评论你的帖子、你发布的物品被收藏、失物被认领都需要通知作者。如果不想引入消息队列最简单的方式是在相关业务接口里直接调用通知服务向message表插入一条记录前端个人中心通过轮询或uni.$emit提示未读数。第三个是数据统计。校园集市可以统计每个分类的商品数量、成交转化率、热门捡漏价区间论坛可以按版块统计发帖量。这些数据直接通过 SQL 聚合查询就能实现不需要额外引入大数据组件。另外如果项目后期要上架安卓应用市场或申请软件著作权Uniapp 打包需要准备 Android 证书DCloud 云打包可以完成软著申请时源码和操作说明书建议提前整理尤其是源码页数和操作录像不然补正会很麻烦。这些虽然不属于代码范畴但往往是学生项目最后一步的隐形门槛。拿这套校园圈项目完整走下来我觉得它最大的价值不是“功能全”而是用一套清晰的业务模型把六个功能合理串联在一起让开发者能够理解数据库设计、后端接口、前端页面三者的协作方式。如果你正在做类似的项目我最后想给的建议是先把post表的设计吃透再开始写业务代码因为整个系统的骨架稳定了后面所有模块都是锦上添花。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价