资讯动态

SSM框架搭建微信健身小程序后端实战指南

发布时间:2026/10/22 10:28:14 来源:尧图企业网站定制
简介这是一套面向计算机专业本科生的毕业设计实战项目聚焦微信生态下的健身服务场景采用SSM后端Vue管理端微信小程序前端的主流全栈架构帮助学生快速完成从需求分析、系统开发到论文答辩的全流程交付。资源包共1554个文件76.39MB涵盖152个Java后端逻辑类、161个Vue管理页面组件、237个小程序JS逻辑与122个WXML视图模板辅以MySQL建表脚本、319张界面截图及2个MP4演示视频结构完整、模块清晰。已有151人学习下载适合Java Web与小程序双轨学习者参考。内含可直接运行的源码、数据库脚本、开题报告、论文全文、答辩PPT、环境工具包及详细安装部署教程特别提供3个批处理脚本install/run/build和多份.bak备份文件便于调试回溯与工程复现显著降低环境配置与代码理解门槛。1. 为什么一个健身小助手小程序成了毕业设计里“改得最勤、跑得最稳、答辩最扛打”的选题不是因为功能多炫酷——它不接体感硬件、不跑实时姿态估计算法、不连智能手环API恰恰相反它胜在边界清晰、链路完整、问题真实用户扫码进小程序→选课程/约教练/记运动日志→后台用SSMSpring SpringMVC MyBatis管数据、做权限、发通知→前端微信原生组件搭界面连登录态都走微信官方鉴权流程。这种“小而全”的闭环把Web开发核心能力——接口设计、数据库建模、前后端联调、微信生态适配、部署验证——全串起来了。某高校连续三年把“健身类小程序”列为毕设推荐方向原因很实在需求易理解、数据结构不复杂、测试路径可穷举、出错有迹可循。尤其对Java后端初学者SSM不是黑匣子每层职责分明MyBatis管SQL怎么写、SpringMVC管URL怎么映射、Spring管对象怎么创建——改一行配置就能看到控制台日志跳出来。这不是玩具项目是能塞进简历、经得起导师现场点开看的“最小可运行系统”。2. 从零搭起SSM后端三步落地拒绝“源码导入就报错”毕业设计最怕什么不是功能难做是环境搭不起来——JDK版本不对、Maven仓库拉不到依赖、Tomcat启动报NoClassDefFoundError。我带过几届学生90%的卡点都在这一步。下面这套流程是我压箱底的“三步落地法”不依赖IDE自动配置纯手工校验关键节点。2.1 环境基线JDK、Maven、Tomcat 版本必须咬死提示别信“最新版最稳定”。毕业设计要的是可复现不是尝鲜。# 推荐组合实测兼容性最强 JDK: 1.8.0_291 # 注意必须是8u291或相近小版本高版本会触发Spring 4.x反射异常 Maven: 3.6.3 # 3.8默认禁用HTTP仓库而部分国内镜像仍用HTTP Tomcat: 8.5.94 # Tomcat 9要求Servlet 4.0但微信小程序后端用Servlet 3.1足够且8.5更轻量为什么咬死这些数字Spring 4.3.28SSM常用版本编译目标为Java 8但某些JDK 8u300的内部类加载策略变更会导致org.springframework.web.servlet.DispatcherServlet找不到。Maven 3.6.3的settings.xml里镜像源必须显式配置为阿里云HTTPS地址!-- ~/.m2/settings.xml -- mirrors mirror idaliyunmaven/id mirrorOf*/mirrorOf nameAliyun Maven/name urlhttps://maven.aliyun.com/repository/public/url /mirror /mirrors2.2 SSM核心依赖pom.xml里只留这7个坐标删掉所有“看起来有用”的包很多同学一上来就加spring-boot-starter-web、mybatis-spring-boot-starter——这是大忌。毕业设计用SSM不是Spring Boot混淆框架栈会导致DispatcherServlet注册失败、MyBatis扫描不到Mapper。以下是精简后的pom.xml关键片段仅保留必需项!-- pom.xml -- properties spring.version4.3.28.RELEASE/spring.version mybatis.version3.4.6/mybatis.version mybatis-spring.version1.3.2/mybatis-spring.version /properties dependencies !-- 1. Spring核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency !-- 2. SpringMVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- 3. MyBatis -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency !-- 4. MyBatis-Spring桥接 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version${mybatis-spring.version}/version /dependency !-- 5. 数据库驱动MySQL 5.7 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.47/version /dependency !-- 6. 连接池HikariCP比Druid更轻启动快 -- dependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId version3.4.5/version /dependency !-- 7. 日志SLF4J Logback避免log4j漏洞 -- dependency groupIdch.qos.logback/groupId artifactIdlogback-classic/artifactId version1.2.11/version /dependency /dependencies逻辑说明spring-context提供IoC容器是所有Spring模块的基础spring-webmvc包含DispatcherServlet和注解驱动支持没有它Controller根本不会被扫描mybatis-spring是关键粘合剂——它让MyBatis的SqlSessionFactory能被Spring管理否则Mapper接口无法注入MySQL驱动必须用5.1.x系列8.0驱动需要额外配置时区参数毕业设计阶段徒增复杂度HikariCP连接池默认配置即开即用无需druid.properties等额外文件减少配置遗漏点。2.3 web.xml与SpringMVC配置两份XML文件决定请求能不能进到你的ControllerSSM不是Spring Boot没有SpringBootApplication自动装配。所有入口靠XML定义。必须确保以下两个文件存在且内容精准①src/main/webapp/WEB-INF/web.xmlServlet容器入口?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 1. 加载Spring根容器 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- 2. 配置SpringMVC前端控制器 -- servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping !-- 3. 解决POST中文乱码 -- filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping /web-app②src/main/resources/spring/spring-mvc.xmlMVC专属配置?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:mvchttp://www.springframework.org/schema/mvc xsi:schemaLocation http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd !-- 扫描Controller包 -- context:component-scan base-packagecom.example.controller use-default-filtersfalse context:include-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan !-- 启用注解驱动 -- mvc:annotation-driven / !-- 静态资源放行小程序JS/CSS/图片 -- mvc:resources mapping/static/** location/static/ / !-- 视图解析器虽然后端主要返回JSON但错误页需要 -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views/ / property namesuffix value.jsp / /bean /beans参数说明contextConfigLocation指向applicationContext.xml它负责加载Service、DAO、数据源——千万别把MVC配置也写进去否则Controller会被当成普通Bean初始化失败mvc:annotation-driven /是开关没有它RequestMapping、ResponseBody全部失效use-default-filtersfalse配合include-filter确保只扫描Controller避免把Service误注入到MVC层/static/**放行静态资源否则小程序app.js加载404前端直接白屏——这是答辩现场最高频翻车点。3. 微信小程序前端只做三件事就让登录、数据、渲染全通小程序前端不是炫技场。毕业设计里它只需稳稳完成三件事安全获取用户OpenID、规范调用后端API、无感渲染列表数据。任何过度封装比如自己写Promise封装wx.request、自定义组件如轮播图、复杂状态管理Redux都是给自己挖坑。下面直给可抄代码。3.1 登录态管理用wx.login()换code后端用js_code换openid别碰unionid微信登录最常踩的坑是前端试图自己解密encryptedData——这完全没必要且极易出错。正确姿势是前端只负责拿code后端用code向微信服务器换openid再存库关联用户。小程序端代码极简// pages/index/index.js Page({ data: { userInfo: null, hasLogin: false }, // 1. 页面加载时尝试静默登录 onLoad() { this.checkLogin() }, // 2. 检查是否已登录本地缓存有token则跳过 checkLogin() { const token wx.getStorageSync(auth_token) if (token) { this.setData({ hasLogin: true }) return } // 无token走登录流程 this.doLogin() }, // 3. 真正的登录动作只拿code不处理用户信息 doLogin() { wx.login({ success: (res) { if (res.code) { // 将code传给后端由后端调用微信接口换取openid wx.request({ url: https://your-domain.com/api/login, method: POST, data: { code: res.code }, success: (loginRes) { const { token, userInfo } loginRes.data wx.setStorageSync(auth_token, token) this.setData({ userInfo: userInfo, hasLogin: true }) } }) } } }) } })关键逻辑说明wx.login()只返回code有效期5分钟必须立刻传给后端前端绝不调用wx.getUserProfile()或wx.getUserInfo()——这两个接口需用户主动授权且2023年起已废弃新版基础库强制要求button open-typegetUserInfoauth_token是后端生成的JWT或随机字符串不是微信的session_key避免前端接触敏感凭证用户头像昵称等信息由后端在换取openid成功后一并查库返回前端只负责渲染。3.2 API调用规范统一拦截器处理token、错误、loading小程序里每个wx.request都手动写header.token、fail回调三天就累垮。必须用拦截器封装。新建utils/request.js// utils/request.js const BASE_URL https://your-domain.com/api function request(options) { // 1. 自动注入token const token wx.getStorageSync(auth_token) options.header { ...options.header, Authorization: Bearer ${token} } // 2. 统一loading非必要接口可关闭 if (options.showLoading ! false) { wx.showLoading({ title: 加载中... }) } return new Promise((resolve, reject) { wx.request({ ...options, url: BASE_URL options.url, success: (res) { wx.hideLoading() if (res.statusCode 200) { resolve(res.data) } else if (res.statusCode 401) { // token过期跳转登录 wx.removeStorageSync(auth_token) wx.navigateTo({ url: /pages/login/login }) } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }) reject(res.data) } }, fail: (err) { wx.hideLoading() wx.showToast({ title: 网络错误, icon: none }) reject(err) } }) }) } // 导出常用方法 export const get (url, data, options) request({ url, data, method: GET, ...options }) export const post (url, data, options) request({ url, data, method: POST, ...options }) export const put (url, data, options) request({ url, data, method: PUT, ...options }) export const del (url, data, options) request({ url, data, method: DELETE, ...options })使用示例获取课程列表// pages/course/list.js const { get } require(../../utils/request) Page({ data: { courses: [] }, onLoad() { this.loadCourses() }, loadCourses() { get(/courses, { type: fitness }) .then(res { this.setData({ courses: res.data }) }) } })参数说明showLoading: false可关闭loading如搜索框输入时频繁请求就不该弹窗401统一处理token失效跳转登录页避免每个页面重复写逻辑res.data.msg是后端约定的错误提示字段前端不拼接字符串保持一致性。3.3 列表渲染用wx:forwx:key禁用scroll-view做长列表健身课程、训练计划、运动日志全是列表。新手最爱用scroll-view滚动加载结果性能崩盘。微信原生view wx:for在千条数据内毫无压力且支持wx:key提升diff效率!-- pages/course/list.wxml -- view classcourse-list view wx:for{{courses}} wx:keyid classcourse-item bindtaponCourseTap >/* pages/course/list.wxss */ .course-list { padding: 20rpx; } .course-item { display: flex; padding: 20rpx 0; border-bottom: 1rpx solid #eee; } .cover { width: 160rpx; height: 120rpx; border-radius: 8rpx; margin-right: 20rpx; } .info { flex: 1; } .title { font-weight: bold; font-size: 32rpx; display: block; margin-bottom: 10rpx; } .coach, .duration { font-size: 24rpx; color: #666; }避坑点wx:keyid必须是唯一字段不能用index否则删除中间项时UI错乱image组件必须设宽高否则首次加载闪动border-bottom用1rpx而非1px适配高清屏不要用scroll-view包裹整个列表——它会劫持滚动事件导致下拉刷新失效、iOS卡顿。4. SSM后端实战三个核心接口覆盖健身场景主干链路毕业设计答辩时老师最可能现场让你演示“用户约课”或“提交日志”。这三个接口登录、课程列表、预约提交必须100%可跑通。下面给出完整实现含SQL、Mapper、Service、Controller全部基于标题中的SSM技术栈不引入任何额外框架。4.1 登录接口/api/login—— 用code换openid查库建用户数据库表MySQL 5.7-- 用户表 CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, openid varchar(64) NOT NULL COMMENT 微信openid, nick_name varchar(50) DEFAULT NULL, avatar_url varchar(255) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 课程表 CREATE TABLE course ( id bigint(20) NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL, coach_name varchar(50) NOT NULL, duration int(11) NOT NULL COMMENT 分钟, cover varchar(255) DEFAULT NULL, status tinyint(4) DEFAULT 1 COMMENT 1-上架,0-下架, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;MyBatis Mapper XMLsrc/main/resources/mapper/UserMapper.xml?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.mapper.UserMapper !-- 根据openid查询用户 -- select idselectByOpenid resultTypecom.example.entity.User SELECT * FROM user WHERE openid #{openid} /select !-- 插入新用户 -- insert idinsert parameterTypecom.example.entity.User useGeneratedKeystrue keyPropertyid INSERT INTO user (openid, nick_name, avatar_url, create_time) VALUES (#{openid}, #{nickName}, #{avatarUrl}, NOW()) /insert /mapperService层com.example.service.UserService.javaService public class UserService { Autowired private UserMapper userMapper; Autowired private WeChatUtil weChatUtil; // 封装微信接口调用的工具类 /** * 小程序登录主流程 * param code 小程序wx.login()返回的临时登录凭证 * return 包含token和用户信息的响应 */ Transactional public LoginResponse login(String code) { // 1. 调用微信接口用code换openid String appId wx1234567890abcdef; // 替换为你的AppID String appSecret your_app_secret; // 替换为你的AppSecret MapString, Object wxResp weChatUtil.jsCode2Session(appId, appSecret, code); if (wxResp null || wxResp.get(openid) null) { throw new RuntimeException(微信登录失败 wxResp); } String openid (String) wxResp.get(openid); // 2. 查询用户是否存在 User user userMapper.selectByOpenid(openid); if (user null) { // 3. 新用户插入数据库此时nick_name/avatar_url为空后续由小程序补充 user new User(); user.setOpenid(openid); userMapper.insert(user); } // 4. 生成token简单用UUID生产环境应换JWT String token UUID.randomUUID().toString().replace(-, ); // 5. 返回响应 return LoginResponse.builder() .token(token) .userInfo(UserInfoVO.builder() .id(user.getId()) .nickName(user.getNickName()) .avatarUrl(user.getAvatarUrl()) .build()) .build(); } }Controller层com.example.controller.LoginController.javaRestController RequestMapping(/api) public class LoginController { Autowired private UserService userService; PostMapping(/login) public ResultLoginResponse login(RequestBody LoginRequest request) { try { LoginResponse response userService.login(request.getCode()); return Result.success(response); } catch (Exception e) { return Result.fail(登录失败 e.getMessage()); } } }关键参数说明weChatUtil.jsCode2Session()是封装好的微信调用方法内部用RestTemplate请求https://api.weixin.qq.com/sns/jscode2sessionTransactional保证插入用户和后续操作原子性LoginResponse是自定义DTO含token和userInfo前端直接存token用于后续请求Result是统一响应包装类格式为{code:200,msg:success,data:{...}}避免前端到处判res.data。4.2 课程列表接口/api/courses—— 分页、条件查询、状态过滤Mapper XMLsrc/main/resources/mapper/CourseMapper.xmlmapper namespacecom.example.mapper.CourseMapper !-- 查询课程列表带分页和状态过滤 -- select idlistCourses resultTypecom.example.entity.Course SELECT id, title, coach_name, duration, cover, status FROM course WHERE status 1 if testtype ! null and type ! AND type #{type} /if ORDER BY create_time DESC LIMIT #{offset}, #{limit} /select !-- 查询总数用于分页 -- select idcountCourses resultTypejava.lang.Integer SELECT COUNT(*) FROM course WHERE status 1 if testtype ! null and type ! AND type #{type} /if /select /mapperService层com.example.service.CourseService.javaService public class CourseService { Autowired private CourseMapper courseMapper; /** * 获取课程列表分页 * param type 课程类型如fitness、yoga * param page 页码从1开始 * param size 每页数量 * return 分页结果 */ public PageResultCourse listCourses(String type, Integer page, Integer size) { if (page null || page 1) page 1; if (size null || size 1) size 10; int offset (page - 1) * size; ListCourse list courseMapper.listCourses(type, offset, size); int total courseMapper.countCourses(type); return PageResult.Coursebuilder() .list(list) .total(total) .page(page) .size(size) .pages((int) Math.ceil((double) total / size)) .build(); } }Controller层com.example.controller.CourseController.javaRestController RequestMapping(/api) public class CourseController { Autowired private CourseService courseService; GetMapping(/courses) public ResultPageResultCourse listCourses( RequestParam(required false) String type, RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { PageResultCourse result courseService.listCourses(type, page, size); return Result.success(result); } }前端调用示例WXML中!-- 课程筛选 -- view classfilter button bindtapswitchType>// pages/course/list.js switchType(e) { const type e.currentTarget.dataset.type this.setData({ currentType: type }) this.loadCourses(type) }, loadCourses(type) { get(/courses, { type: type all ? null : type }) .then(res { this.setData({ courses: res.data.list }) }) }避坑点LIMIT #{offset}, #{limit}中offset必须是整数page参数要校验避免SQL注入type参数用if动态拼接不用WHERE type #{type}否则typenull时SQL语法错误前端传typeall后端识别后不传入SQL避免WHERE status1 AND typeall查不到数据。4.3 预约提交接口/api/reservations—— 幂等性、状态校验、事务回滚数据库表续建-- 预约表 CREATE TABLE reservation ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, course_id bigint(20) NOT NULL, status tinyint(4) DEFAULT 1 COMMENT 1-预约中,2-已完成,3-已取消, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_course (user_id,course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;Mapper XMLsrc/main/resources/mapper/ReservationMapper.xmlmapper namespacecom.example.mapper.ReservationMapper !-- 插入预约记录 -- insert idinsert parameterTypecom.example.entity.Reservation useGeneratedKeystrue keyPropertyid INSERT INTO reservation (user_id, course_id, status, create_time) VALUES (#{userId}, #{courseId}, #{status}, NOW()) /insert !-- 根据用户和课程查询是否存在预约 -- select idselectByUserAndCourse resultTypecom.example.entity.Reservation SELECT * FROM reservation WHERE user_id #{userId} AND course_id #{courseId} AND status IN (1,2) /select /mapperService层com.example.service.ReservationService.javaService public class ReservationService { Autowired private ReservationMapper reservationMapper; Autowired private CourseMapper courseMapper; Autowired private UserMapper userMapper; /** * 提交预约幂等设计同一用户对同一课程只允许一个有效预约 * param userId 用户ID * param courseId 课程ID * return 预约ID */ Transactional public Long createReservation(Long userId, Long courseId) { // 1. 校验用户和课程是否存在 User user userMapper.selectById(userId); if (user null) { throw new RuntimeException(用户不存在); } Course course courseMapper.selectById(courseId); if (course null) { throw new RuntimeException(课程不存在); } // 2. 检查是否已预约防止重复提交 Reservation existing reservationMapper.selectByUserAndCourse(userId, courseId); if (existing ! null (existing.getStatus() 1 || existing.getStatus() 2)) { return existing.getId(); // 直接返回已有ID前端可提示“已预约” } // 3. 插入新预约 Reservation reservation new Reservation(); reservation.setUserId(userId); reservation.setCourseId(courseId); reservation.setStatus(1); // 预约中 reservationMapper.insert(reservation); return reservation.getId(); } }Controller层com.example.controller.ReservationController.javaRestController RequestMapping(/api) public class ReservationController { Autowired private ReservationService reservationService; PostMapping(/reservations) public ResultMapString, Object createReservation( RequestBody ReservationRequest request, HttpServletRequest httpRequest) { // 从token解析出userId实际项目应从JWT解析此处简化 Long userId (Long) httpRequest.getAttribute(userId); // 由拦截器注入 if (userId null) { return Result.fail(未登录); } try { Long reservationId reservationService.createReservation(userId, request.getCourseId()); return Result.success(Map.of(reservationId, reservationId)); } catch (Exception e) { return Result.fail(预约失败 e.getMessage()); } } }关键设计点幂等性先查后插避免同一用户多次点击提交多条记录状态校验只允许对status1预约中或status2已完成的记录视为“已存在”status3已取消可重新预约事务控制Transactional保证校验和插入原子性中间出错自动回滚前端友好返回reservationId前端可跳转详情页或更新列表状态。5. 避坑指南毕业设计答辩前必查的5个血泪现场翻车点毕业设计最惨的不是功能没做完是答辩时老师点开小程序第一屏就报错。下面5个坑是我带学生答辩时高频出现的“当场沉默三秒”时刻按现象、原因、解决三步拆解照着检查保你答辩丝滑。5.1 现象小程序打开白屏控制台报VMxxxx:1 Failed to load resource: the server responded with a status of 404 (Not Found)原因后端部署后/static/目录下的app.js、app.css等静态资源未被Tomcat正确映射。常见于两种情况一是web.xml里没配mvc:resources二是打包成WAR后/static/文件夹被IDE错误排除。解决检查web.xml是否包含mvc:resources mapping/static/** location/static/ /在IDE中右键项目 →Properties→Deployment Assembly确认/src/main/webapp/static已加入部署路径部署后直接浏览器访问https://your-domain.com/static/app.js能下载到文件即正常。5.2 现象点击“立即预约”按钮控制台报401 Unauthorized但登录流程明明走通了原因前端wx.request的header.Authorization值格式错误。常见错误漏写Bearer前缀、token字符串里有空格、wx.setStorageSync存的是{token: xxx}对象而非纯字符串。解决在request.js的success回调里加console.log(token:, token)确认取到的是纯字符串检查header构造Authorization: Bearer token注意空格不能少后端拦截器打印request.getHeader(Authorization)确认收到的是Bearer xxx而非xxx。5.3 现象课程列表显示“加载中...”后一直转圈Network面板看到/api/courses返回500原因MyBatis的resultMap未配置而数据库字段名如coach_name与Java实体属性名如coachName不一致导致SqlSession无法自动映射抛BindingException。解决在CourseMapper.xml中添加resultMapresultMap idCourseResultMap typecom.example.entity.Course id propertyid columnid/ result propertytitle columntitle/ result propertycoachName columncoach_name/ result property p a hrefhttps://download.csdn.net/download/yy521chxu/89535085 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p

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

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

免费获取报价 →
↑