十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于SpringBoot和微信小程序的宠物社交平台设计与实现

基于SpringBoot和微信小程序的宠物社交平台设计与实现 简介一份围绕微信小程序宠物社交平台的毕业设计论文面向计算机相关专业毕业生及小程序开发初学者用于解决课题研究、论文撰写与系统设计无从下手的问题。论文从课题背景与国内外研究现状切入完整梳理了微信开发者工具、Java、B/S模式、MySQL数据库、SpringBoot框架和Vue开发技术等环境构成并对系统功能需求、非功能性需求、系统特色及可行性研究进行了逐项分析。内容覆盖用户注册登录、宠物信息发布、评论点赞等典型社交功能模块并附有从概述、开发环境到系统设计、实现与测试的完整目录结构层次分明便于快速定位。资源采用doc格式单个文档大小3.71MB便于编辑修改。已有252人学习下载对正在准备宠物社区类小程序毕业设计的学生有较强的参考价值可作为论文框架搭建、技术选型和章节撰写的实用范本。1. 先给宠物社交平台做角色拆解管理员、商家、医生、用户都不是同一套逻辑宠物社交平台小程序这个选题看起来是把“社区论坛 宠物商城 地图 购物车”塞进微信小程序里实际上拆开之后会发现它的核心难点不在页面多而在角色多管理员在 Web 后台管分类、商品、医院、公告商家维护自己的商品和订单医生处理预约小程序端用户则是逛论坛、看地图、加购物车。四类人共用一套 SpringBoot MySQL 的后端数据结构必须一开始就按“主体 业务”拆开否则做到中间一定会被字段越加越乱。所以这篇会沿着架构选型、数据库设计、小程序端落地、后端权限控制这条线把这个项目完整拆一遍。2. SpringBoot Vue 微信小程序B/S 架构下前后端怎么分工2.1 为什么 B/S 模式对“微信小程序 后台管理”最合适现在网上很多微信小程序项目第一个困惑就是“小程序到底算不算 B/S”。从开发角度看小程序本身不是传统意义上的浏览器但它同样遵循 B/S 模式客户端只负责界面渲染和交互业务逻辑和存储放在服务端。我和多数做校园跑腿、二手交易这类微信小程序项目的人一样后端直接选了 SpringBoot管理端用 Vue小程序端用微信原生框架三端共享同一个 MySQL 数据库。选 SpringBoot 的理由比较直接它内嵌 Tomcat部署时不用单独装容器自动配置能省掉大量 SpringMVC 时代的 XML 配置和 MyBatis-Plus 或 JPA 配合写 CRUD 的速度很快。B/S 结构带来的好处是管理员不需要安装客户端打开浏览器就能维护宠物商品分类和公告。对用户来说小程序也是即点即用省掉应用市场的下载流程。2.2 微信开发者工具里的双线程模型用微信开发者工具写小程序和写普通 Vue 页面最大的不同是视图层和逻辑层分开运行。WXML 和 WXSS 跑在渲染层负责页面结构和样式JavaScript 逻辑跑在逻辑层负责数据、接口请求两层之间通过 setData 同步。这个机制导致开发时如果直接修改this.data是不生效的必须调用setData。也就是说小程序端不是直接在 DOM 上改节点而是把数据送给渲染层。这个特点在写购物车同步时尤其重要本地缓存改完要setData刷新界面否则购物车角标一直是旧值。我一般会在项目根目录下建一个utils/request.js把wx.request包成 Promise这样页面里就不需要一层层嵌套 success 回调。后端地址建议单独放在一个配置文件里方便切本地环境和正式环境server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/pet_social?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456这里的useUnicodetruecharacterEncodingutf8是保证中文不乱传的关键serverTimezoneAsia/Shanghai是避免 MySQL 8.x 的时间时区报错。本地联调时后端server.port用 8080 就好小程序端BASE_URL配置成http://localhost:8080/api或者局域网地址都能跑但发布之前必须换成已备案的 HTTPS 域名。// utils/request.js const BASE_URL http://localhost:8080/api function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL path, method: method, data: data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || }, success: (res) { if (res.statusCode 200) { resolve(res.data) } else if (res.statusCode 401) { wx.removeStorageSync(token) wx.navigateTo({ url: /pages/login/login }) reject(res.data) } else { reject(res.data) } }, fail: (err) reject(err) }) }) } module.exports { request }Authorization字段放登录后拿到的 token后端拦截器统一校验这样每个业务接口不需要单独判断“用户是否登录”。statusCode 401时清掉本地 token 并跳转登录页是微信小程序端比较常规的会话失效处理。2.3 后端分层Controller 只做转发Service 管业务后端起一个宠物社交平台项目我不会把所有逻辑都堆在 Controller 里。Controller 层只负责接收参数、调用 Service、返回统一结构Service 层处理业务规则比如下单时扣库存、医生预约时判断是否已经约满数据访问层处理 SQL 和表映射。层级主要职责常见约定Controller参数校验、路由分发、统一返回类上加 RestController方法上加 RequestMappingService业务逻辑、事务控制接口加 Impl事务注解 TransactionalMapper/DAOSQL 操作、结果映射MyBatis 的 Mapper 或 MyBatis-Plus 的 BaseMapper对应到宠物社交平台的场景宠物商品列表和宠物医院列表是典型的只读查询可以走 Service 直接查库订单和预约是写操作必须在 Service 层控制事务。比如商家在后台修改宠物商品库存时如果订单模块同时在下单容易出现超卖。真实项目里一般用乐观锁或者数据库行锁毕业设计阶段至少把Transactional加上避免一个方法里多个 SQL 只执行了一半。RestController RequestMapping(/api/pet-goods) public class PetGoodsController { private final PetGoodsService petGoodsService; public PetGoodsController(PetGoodsService petGoodsService) { this.petGoodsService petGoodsService; } GetMapping(/list) public Result list(RequestParam(required false) Long typeId, RequestParam(required false) String name) { return Result.ok(petGoodsService.listByCondition(typeId, name)); } }这段代码里的RequestParam(required false)表示商品分类 ID 和商品名称都是过滤条件可以一个都不传传了就在 SQL 里拼 where。Result.ok(...)是统一返回结构前端 request.js 里直接拿到{ code: 200, data: ... }判断起来比 SpringBoot 默认返回的字段结构要方便。3. 从 MySQL 表结构反推业务用户、商品、预约、订单、论坛怎么建3.1 账号体系一张 user 表容下四类角色宠物社交平台里的“用户”既有宠物主人也有商家和医生管理员则通常从后端初始化账号。我见过最省事的做法是把四个角色直接写死在一张 user 表里用一个 role 字段区分权限判断交给后端。这样做的好处是登录逻辑统一坏处是后续要给商家扩展店铺信息时这些字段只能往 user 表里塞表容易越来越臃肿。我更倾向于“一张账号表 几张角色扩展表”这种折中方案user 表只放公共字段商家扩展表、医生扩展表、用户扩展表再放各自独有的字段。对应到课程设计场景表设计到下面这个程度就够用了CREATE TABLE user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(255) NOT NULL COMMENT 加密密码, role TINYINT NOT NULL DEFAULT 4 COMMENT 1管理员 2商家 3医生 4用户, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像URL, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;role 字段是关键后端拦截器拿到 token 后解析出 userId 和 role再判断该接口允许哪些角色访问。密码不要用明文常见做法是 BCrypt 加密登录时比对哈希值。很多新手会直接放一个select * from user where username ? and password ?的接口这在演示时跑得通但拿到真实部署环境很容易被撞库。3.2 商品、订单和购物车电商部分怎么落表商城模块是宠物商品的售卖涉及商品类型、宠物商品、订单、订单明细。如果按照商城标准建模至少要四张表商品分类表、商品表、订单主表、订单明细表。这里不把购物车单独建表购物车放在小程序本地缓存提交订单时才把商品快照传到后端。以下是最核心的两张表CREATE TABLE pet_goods ( id BIGINT NOT NULL AUTO_INCREMENT, goods_name VARCHAR(100) NOT NULL, goods_type_id BIGINT NOT NULL COMMENT 商品分类ID, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, image VARCHAR(255) DEFAULT NULL, description TEXT, business_id BIGINT NOT NULL COMMENT 商家ID, status TINYINT DEFAULT 1 COMMENT 1上架 0下架, PRIMARY KEY (id), KEY idx_type (goods_type_id), KEY idx_business (business_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宠物商品表; CREATE TABLE orders ( id BIGINT NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, user_id BIGINT NOT NULL, business_id BIGINT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待付款 1已付款 2已发货 3已完成 4已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;订单表没有直接放商品快照简化时可以在订单详情表里存商品 ID 和购买数量更稳妥的做法是把商品名称、价格、图片冗余一份到订单明细表。因为商品下架或改价之后历史订单里的记录不应该跟着变。order_no用来给前端展示和调用支付回调时使用我一般用时间戳加随机数生成保证唯一性。3.3 医生预约和医院地图位置信息要和业务解耦宠物医院管理是另一个独立模块。医院表保存名称、地址、经度、纬度、电话、营业时间医生表保存姓名、职称、所属医院 ID、擅长领域。医生预约表表示一次预约行为包含预约人、医生、预约日期、时间段、状态。地图上展示医院不是直接从医院表查经纬度再标记而是在后端接口里返回完整的 markers 数组小程序端map组件直接消费。核心的业务表整体可以归纳成下面这个关系表业务归属关键关联字段角色user账号体系role管理员/商家/医生/用户pet_goods商城business_id商家orders交易user_id, business_id用户/商家/管理员pet_hospital医院地图longitude, latitude管理员/用户doctor_appointment医患预约doctor_id, user_id医生/用户forum_post社区论坛user_id用户可以说这几个表是宠物社交平台区别于普通论坛项目的重点论坛只需要 user 表和 post 表但这里还要处理商品库存、预约时间冲突、订单状态流转。设计数据库的时候先把“哪些字段是业务状态、哪些字段是业务主体”分清楚后面的编码速度会快很多。4. 小程序端落地tabBar、请求封装、购物车缓存和地图页4.1 tabBar 配置和页面结构微信小程序标准配置 5 个 tab 比较合适对应这个项目的首页、社区论坛、地图、购物车、我的。app.json里的 tabBar 如下{ pages: [ pages/index/index, pages/forum/forum, pages/map/map, pages/cart/cart, pages/mine/mine ], tabBar: { list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/forum/forum, text: 论坛 }, { pagePath: pages/map/map, text: 地图 }, { pagePath: pages/cart/cart, text: 购物车 }, { pagePath: pages/mine/mine, text: 我的 } ] } }注意pagePath必须写在 pages 数组里否则 tab 点击会提示找不到页面。微信开发者工具里图标可以用 iconPath 指定但 5 个 tab 全部配图标会占很多空间课程设计阶段只写文字也能用。各页面对应的数据来源如下页面主要数据接口路径首页轮播、宠物商品、公告/api/index社区论坛帖子列表、回复/api/forum/list地图宠物医院坐标列表/api/hospital/list购物车本地缓存商品无后端接口我的用户信息、订单入口/api/user/info4.2 论坛数据加载和医院地图展示社区论坛的帖子列表我用的是onPullDownRefresh和onReachBottom两个生命周期。页面下拉时重新加载第一页数据上拉到页面底部时把 pageNum 加一再请求下一页。后端参数pageNum和pageSize是两个必要的分页参数接口返回total、pages、records避免一次性把全表数据拖到小程序端。// pages/forum/forum.js const { request } require(../../utils/request) Page({ data: { posts: [], pageNum: 1, pageSize: 10 }, onLoad() { this.loadPosts() }, async loadPosts() { const data await request(/forum/list?pageNum${this.data.pageNum}pageSize${this.data.pageSize}) this.setData({ posts: data.records }) } })这里直接用模板字符串拼接分页参数后端接口返回的records数组会被渲染到 WXML 的wx:for里。需要注意的一点是onReachBottom触发时要把pageNum 1才能加载下一页同时要判断当前页是否已经大于pages否则会一直请求空数据。医院地图的展示则是在页面onLoad时调医院列表接口把后端经纬度转换成map组件的 markersasync loadHospitals() { const data await request(/hospital/list) const markers data.map(h ({ id: h.id, latitude: h.latitude, longitude: h.longitude, callout: { content: h.name, display: ALWAYS } })) this.setData({ markers }) }callout.display: ALWAYS表示地图上始终显示医院名称气泡如果不设置默认是点击后才显示用户打开地图会觉得定位点不明显。4.3 登录和购物车微信 code 换 token本地缓存做兜底登录方式是打开小程序后先调wx.login拿 code把 code 传给后端。后端拿 code 调用微信接口换取 openid再自己生成 token。这里有一个最常见的坑本地联调时后端无法真的调微信接口我通常会在配置里加一个开关本地环境直接用 code 模拟一个 userId正式发布时再切换成真实接口。购物车放在wx.setStorageSync(cart, cartList)里。加购一件商品先从本地 Storage 取出数组判断是否已存在同 ID 商品存在就把数量加一不存在就 push 一条新记录再setData更新页面显示。function addToCart(goods) { let cart wx.getStorageSync(cart) || [] const index cart.findIndex(item item.id goods.id) if (index -1) { cart[index].count 1 } else { cart.push({ ...goods, count: 1 }) } wx.setStorageSync(cart, cart) wx.setStorageSync(cartCount, cart.reduce((sum, item) sum item.count, 0)) }这里把商品名称、价格、图片快照都写入本地缓存下单时后端重新读数据库验证价格和库存。不要把前端传过来的价格直接当成最终金额后端接口需要根据商品 ID 重新查表得出总价。5. 后端接口与权限管理员、商家、医生、用户的登录态控制5.1 统一登录接口的设计登录接口接收 code返回 token 和角色信息。后端用 Redis 做 token 有效期控制没有 Redis 时可以用一个 map 存到内存里课程设计阶段够用。响应结构统一成 Result 对象code200表示成功。PostMapping(/auth/login) public Result login(RequestBody LoginDTO dto) { LoginUser user userService.loginByWeChatCode(dto.getCode()); String token TokenUtil.createToken(user.getId(), user.getRole()); return Result.ok(new LoginVO(token, user.getRole(), user.getNickname())); }LoginVO里带 role 字段小程序端拿到 role 之后可以在“我的”页面展示不同的功能入口。管理员和商家的后台不是小程序而是 Vue 管理端所以管理端的登录走的是用户名密码不调wx.login。5.2 用拦截器校验接口权限实现HandlerInterceptor在 preHandle 里从 header 读取 token解析 userId 和 role。业务接口通过自定义注解控制角色比如RequireRole(role RoleEnum.MERCHANT)不满足就返回 403。public class AuthInterceptor implements HandlerInterceptor { private final TokenUtil tokenUtil; public AuthInterceptor(TokenUtil tokenUtil) { this.tokenUtil tokenUtil; } Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || token.isEmpty()) { response.setStatus(401); return false; } LoginUser user tokenUtil.parse(token); if (user null) { response.setStatus(401); return false; } request.setAttribute(loginUser, user); return true; } }配置拦截器时要注意哪些路径放行。登录接口、首页聚合接口、医院列表这类公开数据应该放行订单、商品管理、医生预约必须拦截。静态资源如果是前后端分离到同一服务也要一并exclude。常见的接口角色分配如下接口路径示例允许角色登录/api/auth/login公共商品列表/api/pet-goods/list公共商品新增、上下架/api/pet-goods商家、管理员医生预约/api/appointment用户订单确认/api/orders/confirm用户用户管理、公告管理/api/admin/user管理员5.3 商品发布和医生预约的实现要点商家发布商品接口POST /api/pet-goodsService 层要把 status 默认成 1、businessId 从当前登录用户取而不是由前端传过来避免越权。医生预约接口POST /api/appointment需要先判断该医生在所选时间段内是否已有预约有冲突直接抛出业务异常。Service public class AppointmentServiceImpl implements AppointmentService { Override Transactional public void createAppointment(AppointmentDTO dto) { Long doctorId dto.getDoctorId(); String date dto.getAppointmentDate(); String timeSlot dto.getTimeSlot(); int count appointmentMapper.countByDoctorAndSlot(doctorId, date, timeSlot); if (count 0) { throw new BizException(该时段已被预约请选择其他时间); } appointmentMapper.insert(dto); } }这里的countByDoctorAndSlotSQL 要同时查医生 ID、预约日期、时间段三个字段只查医生 ID 会误伤其他日期的预约。并发场景下两个用户同时约同一个医生需要给医生预约表加唯一索引或者用select for update否则判断和插入之间会有间隙。事务注解必须加载 public 方法上同一个类内部调用this.createAppointment(...)时事务会失效新手经常会在这里踩坑。6. 测试和上线前要检查的几个点6.1 用 curl 先验证接口后端代码写完先不用反复在小程序里点用 curl 能更快确认接口状态。先登录拿 token再带 token 请求业务接口能直接定位到是后端问题还是前端问题。# 登录并提取 token curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {code:test-code} # 拿到 token 后请求商品列表 curl http://localhost:8080/api/pet-goods/list \ -H Authorization: eyJhbGciOiJIUzI1NiJ9...第一个接口返回里的 token 值要粘贴到第二个请求的 Authorization 里。如果返回 401先检查拦截器是否放行了/auth/login如果返回 403再看当前账号角色是否匹配接口权限。6.2 微信开发者工具里的域名和位置权限小程序真机预览前BASE_URL 必须是已备案的 HTTPS 域名并且在小程序管理后台加入 request 合法域名。本地开发可以勾选“不校验合法域名”但发布体验版时这个选项无效。地图功能如果只是展示后端返回的医院坐标就不需要调用wx.getLocation自然不需要申请用户位置信息接口如果要做“附近的医院”这类功能必须在小程序后台申请位置权限并且真机上要到设置里手动开启定位。6.3 订单和权限的回归测试分类审核、商品上下架、医生预约、购物车结算这条链路每个角色都要走一遍。建议保留一套测试账号管理员、商家、医生、用户各一个。检查用户能否乱调商家接口未登录状态下购物车能否提交订单。重点验证同一时段的医生预约冲突逻辑以及订单状态从待付款到已完成的流转。用 MySQL 的慢查询日志查超过 1 秒的 SQL或者在后端打印 SQL 参数能快速定位是索引缺失还是条件写错。本文还有配套的精品资源点击获取
返回列表