(开源)水果蔬菜商城实战指南 基于 Java + SSM + Vue + MySQL

目录

    • [🌟 项目简介](#🌟 项目简介)
      • 技术栈说明
      • [🧩 核心功能模块](#🧩 核心功能模块)
      • [🖼️ 系统界面预览](#🖼️ 系统界面预览)
    • [⚙️ 运行环境与工具要求](#⚙️ 运行环境与工具要求)
      • [✅ 推荐配置](#✅ 推荐配置)
      • [🛠️ 开发工具推荐](#🛠️ 开发工具推荐)
    • [📁 项目目录结构](#📁 项目目录结构)
    • [🚀 快速部署指南](#🚀 快速部署指南)
      • [1️⃣ 导入数据库](#1️⃣ 导入数据库)
      • [2️⃣ 启动后端服务](#2️⃣ 启动后端服务)
      • [3️⃣ 启动前端页面](#3️⃣ 启动前端页面)
    • [⚡ 加速技巧 & 常见问题解答](#⚡ 加速技巧 & 常见问题解答)
      • [🔄 国内镜像加速配置](#🔄 国内镜像加速配置)
        • [NPM 镜像(永久生效)](#NPM 镜像(永久生效))
        • [Maven 镜像(修改 `~/.m2/settings.xml`)](#Maven 镜像(修改 ~/.m2/settings.xml))
      • [🚫 端口冲突解决方案](#🚫 端口冲突解决方案)
    • [🔧 核心代码解读](#🔧 核心代码解读)
      • [实体层(Entity)------ 数据模型定义](#实体层(Entity)—— 数据模型定义)
      • [数据访问层(Mapper + XML)](#数据访问层(Mapper + XML))
      • 业务逻辑层(Service)
      • [控制层(Controller)------ 接口定义](#控制层(Controller)—— 接口定义)
      • [前端调用示例(Vue + axios)](#前端调用示例(Vue + axios))
      • 统一响应对象(R)
    • [🎯 实战案例:完整的业务流程演示](#🎯 实战案例:完整的业务流程演示)
    • [🔧 踩坑复盘:我部署时遇到的真实问题](#🔧 踩坑复盘:我部署时遇到的真实问题)
      • [坑1:启动后端时报 `Access denied for user 'root'@'localhost'`](#坑1:启动后端时报 Access denied for user 'root'@'localhost')
      • [坑2:npm install 报 `npm ERR! code ERESOLVE`](#坑2:npm install 报 npm ERR! code ERESOLVE)
      • 坑3:前端启动后页面白屏
      • 坑4:前后端联调时接口报跨域(CORS)
      • [坑5:Tomcat 起来了,但所有接口都是 404](#坑5:Tomcat 起来了,但所有接口都是 404)
      • [坑6:SSM 老驱动连 MySQL 8,启动正常但一查询就报错](#坑6:SSM 老驱动连 MySQL 8,启动正常但一查询就报错)
    • [📦 项目源码地址](#📦 项目源码地址)
    • [⚖️ 法律声明](#⚖️ 法律声明)
    • [📋 适用场景与边界说明](#📋 适用场景与边界说明)
      • [✅ 适用场景](#✅ 适用场景)
      • [⚠️ 边界说明](#⚠️ 边界说明)
      • [🔄 版本兼容性](#🔄 版本兼容性)

🌟 项目简介

为什么需要这个系统? 传统手工管理存在以下痛点:

  • 数据分散在 Excel 表格中,汇总统计耗时耗力
  • 多人协作时数据版本混乱,容易重复录入
  • 缺乏权限控制,任何人都能修改数据

本项目基于 Java + SSM + Vue + MySQL 构建,解决了这些问题,适合毕业设计、课程设计或企业级开发学习参考。

技术栈说明

技术 说明 版本要求
Java 后端核心语言 JDK 1.8+
SSM 后端框架(Spring + SpringMVC + MyBatis-Plus) Spring 5.0 / MyBatis-Plus 2.3
Tomcat Servlet 容器,项目以 war 包部署 9.x
Vue 前端框架 2.x/3.x
ElementUI 后台 UI 组件库 2.x
MySQL 数据库 8.0+

🧩 核心功能模块

  • 用户登录 / 注册
  • 用户管理
  • 商家管理
  • 商品类别管理
  • 品牌管理
  • 果蔬商品管理
  • 购物车 / 订单管理
  • 收货地址管理
  • 商品收藏
  • 交流论坛
  • 公告资讯 / 系统简介
  • 在线客服
  • 商品评论
  • 其它功能...

🖼️ 系统界面预览


⚙️ 运行环境与工具要求

为什么强调版本? Java 生态中,不同大版本之间存在不兼容变更。例如 MySQL 8.0 默认身份验证插件与 5.7 不同,直接用 5.7 的连接方式会报错。使用经过验证的版本组合可以避免这类问题。

✅ 推荐配置

工具 版本 说明
Java JDK 1.8 长期支持版本,兼容性最佳
Tomcat 9.x 外置 Servlet 容器,本系列用 war 包部署
MySQL 8.0.41 本文测试版本,建议使用 8.0 系列
Node.js 16.20.2 前端构建依赖
Maven 3.6+ 后端依赖管理

⚠️ 版本兼容性说明

  • 已验证:上述版本组合可稳定运行
  • 未验证:更高版本(如 JDK 17、MySQL 8.4)可能存在兼容问题
  • 建议:首次部署请严格按照推荐版本配置

🛠️ 开发工具推荐

  • 后端: IntelliJ IDEA 2022+(推荐社区版,免费)
  • 容器: Tomcat 9.x(需要在 IDEA 里配置 Artifacts 与 Run/Debug Configuration)
  • 前端: VS Code
  • 数据库: Navicat / DBeaver / MySQL Workbench

📁 项目目录结构

解压后,核心目录结构如下:

复制代码
📁 System/
├── JavaSSM/              ← 后端源码(war 工程,建议使用 IDEA 打开)
├── VueAdmin/             ← 管理后台前端(建议使用 VS Code 打开)
├── VueUser/              ← 用户端前端(建议使用 VS Code 打开)
└── MysqlDatabase/
    └── *.sql             ← 数据库脚本(建议使用 Navicat 导入)

📌 SSM 工程结构提醒

后端是 war 工程:src/main/webapp/ 下有 WEB-INF/web.xml 和 upload/(图片上传目录),两处都必须保留 ;数据库连接参数不在 application.yml,而在 src/main/resources/config.properties。
📌 路径规范提醒

  • ✅ 正确示例 :D:\projects\my_app
  • ❌ 错误示例 :D:\我的项目\app(中文路径会导致编码问题)
  • ❌ 错误示例 :D:\Program Files\my_app(空格可能导致路径解析错误)

🚀 快速部署指南

1️⃣ 导入数据库

为什么先导入数据库? 后端启动时需要连接数据库,如果数据库不存在或表结构不完整,启动会直接报错。所以必须先完成数据库初始化。

操作步骤:

  1. 创建新数据库(例如 system),字符集设置为 utf8mb4。

为什么用 utf8mb4 而不是 utf8? MySQL 的 utf8 最大只支持 3 字节字符,无法存储 emoji 等 4 字节字符。utf8mb4 是真正的 UTF-8 编码,推荐统一使用。

  1. 执行 MysqlDatabase/*.sql 脚本完成数据初始化。

验证方法:

  • 打开数据库客户端,确认数据库已创建
  • 执行 SHOW TABLES; 检查表结构是否完整

预期输出:

text 复制代码
+----------------------+
| Tables_in_system     |
+----------------------+
| config               |
| token                |
| users                |
| ...   |
+----------------------+

常见问题:

问题 原因 解决方案
字符集错误 使用了 utf8 而非 utf8mb4 删除数据库,重新创建并指定 utf8mb4
表已存在 重复导入 先执行 DROP DATABASE system; 再重新创建

2️⃣ 启动后端服务

为什么还要单独装 Tomcat? SSM 是传统 war 工程,没有内嵌容器、也没有 main 方法,必须由外置 Tomcat 加载 webapp/WEB-INF/web.xml 之后才对外提供接口。

操作步骤:

  1. 使用 IntelliJ IDEA 打开 JavaSSM 目录(是后端目录本身,不是项目根目录)。
  2. 等待 Maven 自动下载依赖项(首次加载时间较长,约 5-15 分钟)。

为什么首次加载慢? Maven 需要从中央仓库下载所有依赖包到本地。配置阿里云镜像可以加速,见下文。

  1. 修改 src/main/resources/config.properties 里的三个数据库连接参数:
properties 复制代码
jdbc_url=jdbc:mysql://127.0.0.1:3306/库名?useUnicode=true&characterEncoding=UTF-8&tinyInt1isBit=false&useSSL=false
jdbc_username=root
jdbc_password=root

⚠️ 本系列部分项目带的是 mysql-connector-java 5.1.x,连 MySQL 8 时 jdbc_url 必须保留 useSSL=false,不要删。

  1. 打开 Run → Edit Configurations → + → Tomcat Server → Local,切到 Deployment 页签,点 + 添加 Artifact,必须选 JavaSSM:war exploded (选成 war 会出现"能启动但全是 404")。
  2. 在同一个页签把 Application context 改成与数据库同名(例如 /ssm1llwymes,与 4.MysqlDatabase/*.sql 里的库名一致)------前端就是按这个上下文地址发起请求的。
  3. 运行该 Tomcat 配置,控制台出现 Server startup in xxxx ms 即表示成功。

验证方法:

  • 浏览器访问 http://localhost:8080/{Application context}/,Tomcat 的应用页能打开,说明容器已部署成功
  • 再访问任意分页接口(如 http://localhost:8080/{Application context}/address/page),应返回 {"code":0,...} 格式的 JSON
  • 查看控制台日志,确认无异常抛出

常见问题:

问题 原因 解决方案
启动成功但接口 404 Artifact 选成 war,或 Application context 与前端代理不一致 重选 war exploded,context 与 vue.config.js 的代理目标逐字对齐
启动报 ClassNotFoundException 依赖没有打进 WEB-INF/lib Project Structure → Artifacts,把 Available Elements 全部加入 WEB-INF/lib
端口被占用 其他程序占用了 8080 端口 在 Tomcat 配置的 Server 页签改 HTTP port(见下文端口冲突方案)
依赖下载失败 Maven 配置问题 配置阿里云镜像(见下文加速技巧)
数据库连接失败 config.properties 参数错或数据库未启动 核对 jdbc_url 的库名、端口与账号密码

3️⃣ 启动前端页面

为什么用 Vue? Vue 采用组件化开发,页面复用性强。管理后台和用户端两个项目共享同一套 API 接口,开发效率和可维护性更高。

操作步骤:

分别进入 VueAdmin 和 VueUser 目录,依次执行以下命令:

bash 复制代码
npm install     # 安装依赖(仅首次运行需要)
npm run serve   # 启动本地服务器

管理后台默认运行在 http://localhost:8081,用户端默认运行在 http://localhost:8082。

验证方法:

  • 分别访问两个地址,确认页面能正常加载
  • 登录页可正常显示,无控制台报错

常见问题:

问题 原因 解决方案
npm install 失败 网络问题或依赖版本冲突 使用国内镜像(见下文加速技巧)
页面白屏 接口地址配置错误 检查 vue.config.js 的 devServer.proxy,目标地址需带上下文,如 http://localhost:8080/ssm1llwymes/

⚡ 加速技巧 & 常见问题解答

🔄 国内镜像加速配置

为什么要配置镜像? npm 和 Maven 默认从国外服务器下载依赖,国内访问速度慢且不稳定。配置国内镜像可以显著提升下载速度。

NPM 镜像(永久生效)
bash 复制代码
npm config set registry https://registry.npmmirror.com

验证方法:

bash 复制代码
npm config get registry
# 应输出:https://registry.npmmirror.com
Maven 镜像(修改 ~/.m2/settings.xml)
xml 复制代码
<mirror>
  <id>aliyun</id>
  <mirrorOf>*</mirrorOf>
  <name>阿里云仓库</name>
  <url>https://maven.aliyun.com/repository/public</url>
</mirror>

🚫 端口冲突解决方案

若遇到端口被占用的情况,请尝试:

方案一:修改后端端口(Tomcat)

在 IDEA 的 Run/Debug Configurations → Tomcat Server → Server 页签里修改 HTTP port (默认 8080),直接改 Tomcat 的 conf/server.xml 也可以。改完有两处必须同步:

  • 前端 vue.config.js 里 devServer.proxy 的目标地址(端口和上下文都要对上)
  • 浏览器地址栏里的 localhost:8080

方案二:修改前端端口

javascript 复制代码
// vue.config.js
module.exports = {
  devServer: {
    port: 8082  # 改为其他未被占用的端口
  }
}

验证方法:

  • 重启服务后,访问新端口地址
  • 确认页面能正常加载

🔧 核心代码解读

以下以用户管理模块为例,展示系统从数据库到接口的完整实现链路。其他业务模块的代码结构与用户管理完全一致,只需替换实体字段即可复用。

实体层(Entity)------ 数据模型定义

java 复制代码
@TableName("system_users")
public class SystemUserEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    @TableId
    private Long id;          // 主键
    private String username;  // 用户名
    private String name;      // 真实姓名
    private String password;  // 密码
    private String sex;       // 性别
    private String avatar;    // 头像
    private String phone;     // 手机号
    private String role;      // 所属角色

    @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat
    private Date addtime;     // 创建时间

    // getter / setter 方法(略)
}

实体类通过 @TableName 注解关联数据库表。MyBatis-Plus 会根据实体字段自动生成基础 SQL,无需手动编写 CRUD 语句。

对应数据库表结构:

sql 复制代码
CREATE TABLE system_users (
  id bigint(20) NOT NULL COMMENT '主键',
  username varchar(100) DEFAULT NULL COMMENT '用户名',
  name varchar(100) DEFAULT NULL COMMENT '真实姓名',
  password varchar(100) DEFAULT NULL COMMENT '密码',
  sex varchar(10) DEFAULT NULL COMMENT '性别',
  avatar varchar(255) DEFAULT NULL COMMENT '头像',
  phone varchar(20) DEFAULT NULL COMMENT '手机号',
  role varchar(50) DEFAULT NULL COMMENT '角色',
  addtime datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (id)
);

接口返回的 JSON 格式(序列化后):

json 复制代码
{
  "id": 1,
  "username": "admin",
  "name": "管理员",
  "sex": "男",
  "phone": "13800138000",
  "role": "系统管理员",
  "addtime": "2026-06-01 10:00:00"
}

数据访问层(Mapper + XML)

Mapper 接口:

java 复制代码
public interface SystemUserDao extends BaseMapper<SystemUserEntity> {
    // ${ew.sqlSegment} 会自动拼接前端传入的查询条件
    List<SystemUserView> selectListView(@Param("ew") Wrapper<SystemUserEntity> wrapper);
}

Mapper XML 映射:

xml 复制代码
<mapper namespace="com.dao.SystemUserDao">
    <resultMap type="com.entity.SystemUserEntity" id="userMap">
        <result property="username" column="username"/>
        <result property="name" column="name"/>
        <result property="password" column="password"/>
        <result property="sex" column="sex"/>
        <result property="avatar" column="avatar"/>
        <result property="phone" column="phone"/>
        <result property="role" column="role"/>
    </resultMap>

    <select id="selectListView" resultType="com.entity.view.SystemUserView">
        SELECT * FROM system_users
        <where> 1=1 ${ew.sqlSegment}</where>
    </select>

    <!-- 自定义统计查询示例 -->
    <select id="selectScore" resultType="java.lang.Integer">
        select sum(score) from user_scores
        where username = #{u.username}
    </select>
</mapper>

${ew.sqlSegment} 是 MyBatis-Plus 的核心动态 SQL 机制------前端传递的分页、排序、模糊搜索等条件会自动拼接到 SQL 末尾,每个模块都无需重复写 SQL。


业务逻辑层(Service)

java 复制代码
@Service("systemUserService")
public class SystemUserServiceImpl
        extends ServiceImpl<SystemUserDao, SystemUserEntity>
        implements SystemUserService {

    @Override
    public PageUtils queryPage(Map<String, Object> params,
                               Wrapper<SystemUserEntity> wrapper) {
        Page<SystemUserView> page = new Query<SystemUserView>(params).getPage();
        List<SystemUserView> records = baseMapper.selectListView(page, wrapper);
        page.setRecords(records);
        return new PageUtils(page);
    }
}

ServiceImpl 已内置 insert、selectById、updateById、deleteBatchIds 等基础方法,业务层只需关注查询条件拼装和特殊逻辑处理。


控制层(Controller)------ 接口定义

java 复制代码
@RestController
@RequestMapping("/system/user")
public class SystemUserController {

    @Autowired
    private SystemUserService systemUserService;

    @Autowired
    private TokenService tokenService;

    /**
     * 登录接口
     */
    @IgnoreAuth
    @RequestMapping("/login")
    public R login(String username, String password) {
        SystemUserEntity user = systemUserService.selectOne(
            new EntityWrapper<SystemUserEntity>().eq("username", username));
        if (user == null || !user.getPassword().equals(password)) {
            return R.error("账号或密码不正确");
        }
        String token = tokenService.generateToken(
            user.getId(), username, "system_user", "系统用户");
        return R.ok().put("token", token);
    }

    /**
     * 注册接口
     */
    @IgnoreAuth
    @RequestMapping("/register")
    public R register(@RequestBody SystemUserEntity entity) {
        // ✅ 正确做法:注册前先检查用户名是否已被占用
        SystemUserEntity exist = systemUserService.selectOne(
            new EntityWrapper<SystemUserEntity>().eq("username", entity.getUsername()));
        if (exist != null) {
            return R.error("用户已存在");
        }
        entity.setId(new Date().getTime());
        systemUserService.insert(entity);
        return R.ok();
    }

    /**
     * ❌ 反例:注册时不检查用户是否已存在
     * 后果:如果数据库中 username 字段有唯一索引,直接 insert 会抛出
     * DataIntegrityViolationException,最终返回 500 + 堆栈信息,而不是友好的提示。
     * 前端只能显示"系统异常",用户不知道具体原因。
     */
    // @IgnoreAuth
    // @RequestMapping("/register_wrong")
    // public R registerWrong(@RequestBody SystemUserEntity entity) {
    //     entity.setId(new Date().getTime());
    //     systemUserService.insert(entity);  // 如果用户名重复,这里会报错
    //     return R.ok();
    // }

    /**
     * 分页查询
     */
    @RequestMapping("/page")
    public R page(@RequestParam Map<String, Object> params,
                  SystemUserEntity entity) {
        EntityWrapper<SystemUserEntity> ew = new EntityWrapper<>();
        PageUtils page = systemUserService.queryPage(params,
            MPUtil.sort(MPUtil.likeOrEq(ew, entity), params));
        return R.ok().put("data", page);
    }

    /**
     * 详情查询
     */
    @RequestMapping("/info/{id}")
    public R info(@PathVariable("id") Long id) {
        SystemUserEntity entity = systemUserService.selectById(id);
        return R.ok().put("data", entity);
    }

    /**
     * 新增
     */
    @RequestMapping("/save")
    public R save(@RequestBody SystemUserEntity entity) {
        entity.setId(new Date().getTime());
        systemUserService.insert(entity);
        return R.ok();
    }

    /**
     * 修改
     */
    @RequestMapping("/update")
    public R update(@RequestBody SystemUserEntity entity) {
        systemUserService.updateById(entity);
        return R.ok();
    }

    /**
     * 删除
     */
    @RequestMapping("/delete")
    public R delete(@RequestBody Long[] ids) {
        systemUserService.deleteBatchIds(Arrays.asList(ids));
        return R.ok();
    }
}

前端调用示例(Vue + axios)

以上接口在后端定义好后,前端通过 axios 调用。以下以登录功能为例。

前端 API 封装:

javascript 复制代码
// api.js - 统一接口封装
import axios from 'axios'

const request = axios.create({
  baseURL: 'http://localhost:8080',
  timeout: 10000
})

// 接口调用:用户登录
export function loginApi(data) {
  return request({
    url: '/system/user/login',
    method: 'post',
    data
  })
}

// 接口调用:分页查询
export function pageApi(params) {
  return request({
    url: '/system/user/page',
    method: 'get',
    params
  })
}

登录页调用逻辑(login.vue):

javascript 复制代码
// login.vue <script> 核心部分
export default {
  data() {
    return {
      form: {
        username: '',
        password: ''
      }
    }
  },
  methods: {
    handleLogin() {
      loginApi(this.form).then(res => {
        if (res.data.code === 0) {
          // 登录成功,存储 token
          localStorage.setItem('token', res.data.token)
          // 跳转到首页
          this.$router.push('/home')
        } else {
          // 登录失败,显示错误提示
          this.$message.error(res.data.msg)
        }
      }).catch(err => {
        console.error('请求异常', err)
      })
    }
  }
}

前后端通过统一的 JSON 格式交互。前端只需关注 code 和 data,所有接口都遵循相同的响应结构(R 对象),减少了大量重复的错误处理代码。


统一响应对象(R)

系统所有接口返回统一格式的 JSON,前端根据 code 判断请求状态:

java 复制代码
public class R extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    public R() {
        put("code", 0);  // 0 表示成功
    }

    public static R ok() {
        return new R();
    }

    public static R ok(String msg) {
        return new R().put("msg", msg);
    }

    public static R error(String msg) {
        return error(500, msg);
    }

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

接口响应格式速查:

场景 code msg data
操作成功 0 --- 实际数据
操作成功(含提示) 0 "操作成功" 实际数据
登录失败 500 "账号或密码不正确" ---
注册冲突 500 "用户已存在" ---
未登录 401 "未登录" ---

🎯 实战案例:完整的业务流程演示

以下是系统的一个完整业务流程,展示从接口请求到数据库写入的全过程,包含输入数据、接口调用、预期响应和数据库验证。

案例场景

管理员在后台新增一个用户账号,该用户使用账号密码登录系统,然后修改个人资料。

步骤1:管理员新建用户

操作:管理员登录后台 → 进入用户管理 → 点击新增 → 填写信息 → 提交

接口请求:

http 复制代码
POST http://localhost:8080/system/user/save
Content-Type: application/json

输入数据:

json 复制代码
{
  "username": "zhangsan",
  "name": "张三",
  "password": "123456",
  "sex": "男",
  "phone": "13800138000",
  "role": "普通用户"
}

预期响应:

json 复制代码
{
  "code": 0
}

步骤2:用户登录系统

操作:用户打开登录页 → 输入账号密码 → 点击登录

接口请求:

http 复制代码
POST http://localhost:8080/system/user/login
Content-Type: application/x-www-form-urlencoded

username=zhangsan&password=123456

预期响应:

json 复制代码
{
  "code": 0,
  "token": "eyJhbGciOiJIUzI1NiIs..."
}

步骤3:用户修改个人信息

操作:登录成功后 → 进入个人中心 → 修改手机号 → 保存

接口请求:

http 复制代码
POST http://localhost:8080/system/user/update
Content-Type: application/json
token: {{上一步返回的token}}

{
  "id": 1,
  "phone": "13900139000"
}

预期响应:

json 复制代码
{
  "code": 0
}

步骤4:数据库验证

sql 复制代码
SELECT id, username, name, phone FROM system_users WHERE username = 'zhangsan';

预期输出:

text 复制代码
+----+----------+--------+--------------+
| id | username | name   | phone        |
+----+----------+--------+--------------+
|  1 | zhangsan | 张三   | 13900139000  |
+----+----------+--------+--------------+

步骤5:失败场景------注册重复用户

真实的系统不仅要处理成功路径,还要处理异常情况。以下展示用户注册时用户名已被占用的错误处理流程。

操作:用户在前端填写注册信息,点击提交,但用户名已被其他用户注册。

接口请求:

http 复制代码
POST http://localhost:8080/system/user/register
Content-Type: application/json

{
  "username": "zhangsan",
  "name": "张三",
  "password": "123456"
}

预期响应:

json 复制代码
{
  "code": 500,
  "msg": "用户已存在"
}

前端表现:页面弹出错误提示"用户已存在",用户输入框保留,可修改后重新提交。

❌ 反例------没有检查重复时的后果:

json 复制代码
{
  "code": 500,
  "msg": "DataIntegrityViolationException: Duplicate entry 'zhangsan' for key 'username'"
}

如果不做重复检查,直接插入数据库,MySQL 的唯一索引会抛出异常。最终返回给前端的是堆栈信息而不是友好的提示,用户体验极差,还存在 SQL 注入等安全风险。


这个流程覆盖了系统三个核心能力 :新增(写入)→ 登录(鉴权)→ 修改(更新),每一步都有明确的输入数据、接口请求和可验证的输出结果,同时包含了正常路径和异常路径的处理。


🔧 踩坑复盘:我部署时遇到的真实问题

以下是我在实际部署过程中遇到的问题,按现象→定位→原因→解决方案的结构整理。

坑1:启动后端时报 Access denied for user 'root'@'localhost'

现象:控制台报错,无法连接数据库。

定位 :检查 src/main/resources/config.properties 中的 jdbc_username / jdbc_password。

原因 :MySQL 8.0 默认使用 caching_sha2_password 身份验证插件,而旧版 Navicat 或 JDBC 驱动可能不支持。

解决方案:

sql 复制代码
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';
FLUSH PRIVILEGES;

坑2:npm install 报 npm ERR! code ERESOLVE

现象 :执行 npm install 时提示依赖版本冲突。

定位:查看报错信息中的依赖树。

原因:某些依赖包版本之间存在不兼容。

解决方案:

bash 复制代码
npm install --legacy-peer-deps

坑3:前端启动后页面白屏

现象:浏览器访问前端地址显示空白页面,控制台有接口 404 错误。

定位:打开浏览器开发者工具 → Network 面板,查看请求地址。

原因:前端请求地址指向后端 8080 端口,但后端未启动或地址配置错误。

解决方案:

  1. 确认后端已启动并正常运行
  2. 检查 vue.config.js 中的 devServer.proxy 配置
  3. 确认后端接口地址正确

正确配置参考:

javascript 复制代码
// vue.config.js
module.exports = {
  devServer: {
    port: 8081,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
};

坑4:前后端联调时接口报跨域(CORS)

现象 :前端请求后端接口时浏览器控制台提示 No 'Access-Control-Allow-Origin' header。

定位:查看浏览器 Network 面板,确认请求已发出但响应被拦截。

原因:前后端分离部署使用不同端口(如前端 8081、后端 8080),浏览器同源策略阻止了跨端口请求。

解决方案(后端添加 CORS 全局配置):

java 复制代码
package com.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

坑5:Tomcat 起来了,但所有接口都是 404

现象 :控制台明明打印了 Server startup in xxxx ms,浏览器访问接口却是 Tomcat 默认的 404 页。

定位 :访问 http://localhost:8080/manager/html(或看 IDEA Deployment 列表),确认应用的上下文路径。

原因 :Artifact 选成了 war(Tomcat 拿到的是压缩包,目录结构没展开),或者 Application context 与前端代理地址不一致。

解决方案:

  1. File → Project Structure → Artifacts,删掉旧的产物
  2. 重新添加:+ → Web Application → Archive Exploded
  3. Deployment 页选 JavaSSM:war exploded,Application context 填成与库名一致的上下文(如 /ssm1llwymes)

坑6:SSM 老驱动连 MySQL 8,启动正常但一查询就报错

现象 :Tomcat 启动没有任何异常,页面一请求数据就报 getServerCharset 空指针或时区相关错误。

定位 :看 pom.xml 里 mysql-connector-java 的版本,再看 config.properties 的 jdbc_url。

原因:SSM 项目普遍带 5.1.3x 的驱动,与 MySQL 8 不兼容,且 5.1.x 默认尝试 SSL 握手。

解决方案:

xml 复制代码
<!-- pom.xml:同系列内升到修复版本,改动最小 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

同时保证 jdbc_url 末尾带 useSSL=false:

properties 复制代码
jdbc_url=jdbc:mysql://127.0.0.1:3306/库名?useUnicode=true&characterEncoding=UTF-8&useSSL=false

📦 项目源码地址

平台 链接 说明
网盘 》》本项目下载地址《《 本开源项目的下载地址,项目更新后网盘会同步更新,如果网盘失效请联系我重新发布!

⚖️ 法律声明

本项目基于开源项目进行二次开发,仅供 个人学习与技术交流 使用。

  • 版权说明

    • 原项目版权归其作者所有
    • 本项目仅对原项目进行了功能扩展和界面优化
    • 未改变原项目的授权协议
  • 使用限制

    • ❌ 禁止用于商业用途
    • ❌ 禁止转售或冒充原创作品
    • ❌ 禁止用于任何违反法律法规的用途
    • ✅ 可用于个人学习、课程设计、毕业设计
    • ✅ 可用于技术交流和经验分享
  • 商业使用

    • 如需商业使用,请联系原作者获得授权
    • 本项目作者不承担任何商业使用产生的法律责任

📋 适用场景与边界说明

✅ 适用场景

  • 新手、学生的开发学习
  • Java + SSM + Vue + MySQL 技术栈学习
  • 毕业设计、课程设计参考
  • 企业级开发学习参考

⚠️ 边界说明

  • 技术栈限制:本文示例基于上述技术栈,不适用于其他技术组合
  • 功能限制:仅展示核心功能模块,扩展功能需自行开发
  • 环境限制:已在推荐环境下测试,其他环境可能存在兼容问题

🔄 版本兼容性

  • Java:适用于 JDK 1.8,JDK 11/17 未验证
  • Spring / MyBatis-Plus :适用于 Spring 5.0 + MyBatis-Plus 2.3,Spring 6 的 jakarta.* 命名空间不适用
  • Tomcat :适用于 9.x,10.x 因 javax→jakarta 变更不可用
  • MySQL:适用于 8.0+ 版本,5.x 版本未验证
  • Node.js:适用于 16.x 版本,更高版本可能需要调整

💡 温馨提示:如果在部署过程中遇到问题,欢迎在评论区留言讨论,我会尽快回复!喜欢本项目请点赞 👍 收藏 ⭐ 支持一下!

相关推荐
wang_shu_mo_ran1 小时前
Spring IoC和DI概念篇
java·后端·spring
小狼154541 小时前
拼多多订单数据导出 CSV 实战:接口分页、字段平铺与 5 个数据坑,多多开票助手
java·前端·javascript
Joe_Wang52 小时前
【从0到1学习JVM · 25】同样都要停顿所有线程,Parallel比Serial到底强在哪
java·jvm·学习·垃圾回收
wuminyu2 小时前
JVM虚拟线程的底层实现原理分析
java·linux·c语言·jvm·c++
做运维的阿瑞2 小时前
mysql数据库分组查询:GROUP BY 与 HAVING 的执行逻辑
数据库·sql·mysql
ss2732 小时前
AI全栈实战 | 2.2-02 React 思维模型:数据不可变 vs Vue 数据可变,两种哲学把复杂度推给谁
前端·vue.js·react.js
天衍四九-2 小时前
Docker Compose企业实战系列(三):Vue/React前端项目容器化完整部署
前端·vue.js·docker
弹简特2 小时前
【Java项目-企悦抽】13-奖品管理模块-奖品列表和创建奖品的实现
java·开发语言·网络·springboot
Rosanci2 小时前
从零到合入主线:我的 DeepSeek Harness 开源贡献实战手记
开发语言·前端·开源