# tourism **Repository Path**: IwE_wtF/tourism ## Basic Information - **Project Name**: tourism - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-02 - **Last Updated**: 2026-07-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 景区管理系统 > 基于 Vue 3 + Spring Boot 3 的前后端分离景区管理系统 **课程验收入口:** [交付验收指南](交付验收指南.md)(推荐先阅读,包含启动、账号和演示路线) ## 一、项目简介 本系统是一个面向景区运营的综合性管理平台,提供景点管理、酒店管理、订单管理、用户管理、旅游线路管理等功能模块,支持管理员后台与普通用户前端两种角色访问。 ### 核心功能 - **用户管理**:管理员 / 普通用户角色,BCrypt 密码加密,Sa-Token 鉴权 - **景点管理**:景点信息 CRUD、图片展示、评论互动 - **酒店管理**:酒店信息维护、房型管理 - **订单管理**:景点 / 酒店 / 线路订单统一管理 - **旅游线路**:线路编排与展示 - **图片上传**:统一上传接口 + ImagePicker 组件,支持本地上传与 URL 输入预览 - **数据看板**:ECharts 数据可视化(柱状图 + 饼图) - **接口文档**:Knife4j 自动生成 API 文档 --- ## 二、技术栈 ### 整体架构 - **架构模式**:前后端分离 + RESTful API - **后端**:Spring Boot 3.2.5(Java 17) - **前端**:Vue 3.2.13 + Element Plus 2.14.2 - **数据库**:MySQL 5.7(UTF8mb4) - **构建工具**:Maven 3.9.x(后端)/ Vue CLI 5.x(前端) ### 后端技术栈 | 类别 | 技术 | 版本 | 说明 | |---|---|---|---| | 框架 | Spring Boot | 3.2.5 | 主框架 | | 语言 | Java | 17 | LTS 长期支持版 | | Web | Spring Web MVC | 6.1.x | 内嵌 Tomcat 10.1 | | ORM | MyBatis-Plus | 3.5.5 | 简化 CRUD + 分页插件 | | 数据库 | MySQL | 5.7 | 关系型数据库 | | 连接池 | HikariCP | 默认 | Spring Boot 默认池,max-pool-size=10 | | 鉴权 | Sa-Token | 1.38.0 | 登录会话 + Token + 路由拦截 | | 密码加密 | Spring Security Crypto | 6.x | BCrypt 哈希 | | 接口文档 | Knife4j | 4.5.0 | OpenAPI 3 + Swagger UI | | 工具库 | Hutool | 5.8.27 | 通用工具集 | | 参数校验 | Spring Validation | 3.x | `@Valid` + JSR-380 | | 简化代码 | Lombok | 1.18.x | `@Data` / `@Slf4j` | | 热重载 | Spring DevTools | 3.2.5 | 开发期热部署 | | 测试 | Spring Boot Test | 3.2.5 | JUnit 5 | ### 后端 20 个业务模块 | 模块 | Controller | Entity | 说明 | |---|---|---|---| | 用户 | SysUserController | SysUser | 用户、登录、注册、信息 | | 景点 | SysAttractionsController | SysAttractions | 景点信息 CRUD | | 酒店 | SysHotelController | SysHotel | 酒店信息 | | 酒店房型 | SysHotelItemController | SysHotelItem | 酒店房型明细 | | 旅游线路 | SysLineController | SysLine | 线路编排 | | 景点订单 | SysAttractionOrderController | SysAttractionOrder | 景点订单 | | 酒店订单 | SysHotelOrderController | SysHotelOrder | 酒店订单 | | 线路订单 | SysLineOrderController | SysLineOrder | 线路订单 | | 支付 | SysPaymentController | SysPayment | 支付记录 | | 优惠券 | SysCouponController | SysCoupon | 优惠券模板 | | 用户优惠券 | SysUserCouponController | SysUserCoupon | 用户领取的优惠券 | | 评论 | SysCommentsController | SysComments | 景点评论 | | 收藏 | SysFavorController | SysFavor | 用户收藏 | | 论坛 | SysForumController | SysForum | 社区论坛 | | 轮播图 | SysRotationsController | SysRotations | 首页轮播 | | 配置 | SysConfigController | SysConfig | 系统配置 | | 通知 | SysNotificationController | SysNotification | 站内通知 | | 操作日志 | SysOperationLogController | SysOperationLog | 操作日志 | | 客流统计 | SysTouristCountController | SysTouristCount | 客流统计 | | 图片上传 | SysUploadController | — | 通用图片上传(无 Entity) | ### 前端技术栈 | 类别 | 技术 | 版本 | 说明 | |---|---|---|---| | 框架 | Vue | 3.2.13 | Composition API | | 构建工具 | Vue CLI | 5.x | webpack 5 | | UI 组件库 | Element Plus | 2.14.2 | 饿了么 UI | | 路由 | Vue Router | 4.0.3 | SPA 路由 | | 状态管理 | Vuex | 4.0.0 | 全局状态 | | HTTP 客户端 | Axios | 1.18.1 | Promise 风格 | | Axios 适配 | vue-axios | 3.5.2 | Vue 插件封装 | | 浏览器兼容 | core-js | 3.8.3 | polyfill | | 预处理器 | Less | 4.0.0 | CSS 预处理 | | 图表 | ECharts | 5.5.0 | Dashboard 数据可视化 | | 代码检查 | ESLint | 8.0.1 | 配合 eslint-plugin-vue@9 | | 单元测试 | Jest | 27.0.5 | Vue Test Utils | ### 前端 17 个 API 模块 | API 文件 | 对应后端模块 | |---|---| | user.js | SysUserController | | attractions.js | SysAttractionsController | | hotel.js / hotelItem.js | SysHotelController / SysHotelItemController | | line.js | SysLineController | | attractionOrder.js / hotelOrder.js / lineOrder.js | 三个订单 Controller | | payment.js | SysPaymentController | | coupon.js / userCoupon.js | 优惠券 | | comments.js / favor.js / forum.js | 评论 / 收藏 / 论坛 | | rotations.js | 轮播图 | | config.js / notification.js | 系统 / 通知 | > 图片上传接口 `POST /api/upload/image` 由 `ImagePicker.vue` 组件内联调用(用 axios 直连 + satoken header),未单独建 api 模块。 --- ## 三、环境准备 ### 必需软件 | 软件 | 版本 | 说明 | |---|---|---| | JDK | 17 | 必须,Spring Boot 3 要求 | | Node.js | 18+ | 前端构建 | | MySQL | 5.7 / 8.0 | 数据库 | | Maven | 3.9+ | 后端构建(也可用项目自带 mvnw,无需单独安装) | ### 推荐 IDE - 后端:IntelliJ IDEA(社区版即可) - 前端:VS Code - 数据库:Navicat / DBeaver ### 默认端口 | 服务 | 端口 | |---|---| | 后端 Spring Boot | 8080 | | 前端 Vue CLI | 3000 | | MySQL | 3306 | ### 环境变量(可选,生产部署用) | 变量名 | 默认值 | 说明 | |---|---|---| | `DB_URL` | `jdbc:mysql://127.0.0.1:3306/db_tourism?...` | 数据库连接串 | | `DB_USERNAME` | `root` | 数据库账号 | | `DB_PASSWORD` | `123456` | 数据库密码 | | `IMAGES_DIR` | `./images/` | 图片静态资源目录 | --- ## 四、快速开始 ### 1. 初始化数据库 ```sql -- 登录 MySQL 后执行 source backend/demo/sql/db_tourism.sql; -- 建表 source backend/demo/sql/full_data.sql; -- 初始化数据 ``` ### 2. 启动后端 ```bash cd backend/demo ./mvnw spring-boot:run # 用自带的 Maven Wrapper # 或 mvn spring-boot:run # 用本地 Maven ``` 启动成功后访问: - 后端 API:http://localhost:8080 - 接口文档:http://localhost:8080/doc.html ### 3. 启动前端 ```bash cd frontend npm install npm run serve ``` 启动成功后访问:http://localhost:3000/login ### 4. Docker 一键部署(推荐生产/演示用) 项目已容器化,无需在宿主机安装 JDK/Node/Maven,一条命令拉起 MySQL + 后端 + 前端。 **前置要求**:Docker Engine 24+ 和 Docker Compose v2 **方式一:一键脚本(Linux,推荐)** ```bash chmod +x deploy.sh ./deploy.sh # 国内加速: ./deploy.sh --npm-mirror https://registry.npmmirror.com --maven-mirror https://maven.aliyun.com/repository/public # 本地调试跳过 git pull: ./deploy.sh --no-pull ./deploy.sh --help # 查看全部选项 ``` 脚本流程:环境检查 → git pull → 配置文件校验 → 多阶段 Docker 构建 → 启动容器 → 健康检查(带重试)。首次运行若 `docker/.env` 不存在,会自动从模板复制并提示修改密码。 **方式一(Windows):一键 PowerShell 脚本** ```powershell # 若提示无法运行脚本,先执行一次:Set-ExecutionPolicy -Scope Process Bypass .\build.ps1 # 构建镜像 + 启动容器(首次/源码改动后用) .\build.ps1 -SkipBuild # 用已有镜像直接启动(源码没改时用,最快) .\build.ps1 -Down # 停止容器 .\build.ps1 -Reset # 停止并清空数据库(下次启动重新导入 init.sql) ``` 与 `deploy.sh` 等价的 Windows 版本:多阶段 Docker 构建(宿主机无需装 JDK/Node)、自动创建 `docker/.env`、健康检查带重试。国内镜像源默认已开启(`NPM_MIRROR`/`MAVEN_MIRROR` 环境变量可覆盖)。 **方式二:手动 docker compose** ```bash # 1. 准备配置(首次) cp docker/.env.example docker/.env # 编辑 docker/.env,修改 MYSQL_ROOT_PASSWORD / DB_PASSWORD # 2. 构建并启动(必须带 --env-file,否则 MySQL 因密码为空启动失败) docker compose --env-file docker/.env build docker compose --env-file docker/.env up -d # 3. 查看状态/日志 docker compose --env-file docker/.env ps docker compose --env-file docker/.env logs -f ``` > ⚠️ **必须用 `--env-file docker/.env`**:本项目的环境变量文件在 `docker/` 子目录,`docker compose` 默认只读项目根目录的 `.env`,不指定会因 `MYSQL_ROOT_PASSWORD` 等变量缺失导致 MySQL 启动失败、后端连不上数据库。 **容器资源限制**(见 `docker-compose.yml`): | 服务 | 内存限制 | 说明 | |---|---|---| | mysql | 512M | 数据库 | | backend | 1024M | Spring Boot + JVM 实际需 ~600MB,512M 会 OOM | | frontend | 128M | nginx 静态托管 | **访问地址**: | 服务 | 地址 | |---|---| | 前端 | http://localhost:3000 | | 后端 | http://localhost:8080 | | API 文档 | http://localhost:8080/swagger-ui.html | 端口可在 `docker/.env` 的 `FRONTEND_PORT`/`BACKEND_PORT`/`MYSQL_PORT` 修改。 ### 默认账号 | 用户名 | 密码 | 角色 | |---|---|---| | admin | 123456 | 管理员 | | AAA | 123456 | 管理员 | | user1 | 123456 | 普通用户 | --- ## 五、项目结构 ``` 前后端分离的景区管理系统程序/ # 项目根目录 │ ├── fullstack.code-workspace # VS Code 工作区配置(路径: ./backend/demo + ./frontend) ├── README.md ├── DEPLOYMENT_LESSONS.md # 部署经验教训(9 类踩坑的根因分析与修复) ├── deploy.sh # Linux 一键部署脚本(Docker 化) ├── build.ps1 # Windows 一键部署脚本(PowerShell,与 deploy.sh 等价) ├── docker-compose.yml # Docker Compose 编排(mysql + backend + frontend) ├── docker/ # Docker 部署配置 │ ├── .env.example # 环境变量模板(复制为 .env 并修改密码) │ └── mysql/init.sql # MySQL 容器首次启动自动导入的建表+种子数据 │ ├── backend/ # 后端(Spring Boot 3) │ └── demo/ # Maven 项目根 │ ├── .mvn/wrapper/ # Maven Wrapper 配置 │ ├── images/ # 景区图片(后端静态资源,**打包必须包含**) │ │ ├── 三亚亚龙湾/ # 14 个景点目录(每个含 cover.png + 介绍.txt) │ │ ├── 丽江古城/ │ │ ├── 九寨沟风景区/ │ │ ├── 分界洲岛/ │ │ ├── 南山文化旅游区/ │ │ ├── 呀诺达雨林文化旅游区/ │ │ ├── 天涯海角/ │ │ ├── 张家界国家森林公园/ │ │ ├── 故宫博物院/ │ │ ├── 桂林漓江风景区/ │ │ ├── 苏州园林-拙政园/ │ │ ├── 蜈支洲岛/ │ │ ├── 黄山风景区/ │ │ ├── 如家酒店/ # 散图目录(1 张主图 + 10 张截图) │ │ ├── 酒店图片/ # 10 个酒店子目录(每个含 cover.png) │ │ └── 景区介绍.txt │ ├── sql/ # SQL 脚本 │ │ ├── db_tourism.sql # 建表脚本 │ │ ├── full_data.sql # 完整初始化数据 │ │ ├── fill_data.sql # 填充数据 │ │ ├── seed_data.sql # 种子数据 │ │ ├── seed_lines.sql # 线路数据 │ │ └── seed_rich.sql # 富文本数据 │ ├── src/ │ │ ├── main/ │ │ │ ├── java/com/example/demo/ │ │ │ │ ├── DemoApplication.java # 启动入口 │ │ │ │ ├── common/ │ │ │ │ │ └── Result.java # 统一返回结构 {code,message,data} │ │ │ │ ├── config/ # 配置类(4 个) │ │ │ │ │ ├── CorsConfig.java # CORS 跨域(Origin 白名单) │ │ │ │ │ ├── MybatisPlusConfig.java # 分页插件 │ │ │ │ │ ├── SaTokenConfig.java # 鉴权拦截(按 HTTP 方法精细化) │ │ │ │ │ └── WebResourceConfig.java # 静态资源映射(/images/** → ./images/) │ │ │ │ ├── controller/ # 控制层(20 个,含 SysUploadController 图片上传) │ │ │ │ ├── entity/ # 实体(19 个,SysUser 加 @JsonIgnore) │ │ │ │ ├── exception/ # 全局异常处理 │ │ │ │ │ ├── BusinessException.java │ │ │ │ │ └── GlobalExceptionHandler.java │ │ │ │ ├── mapper/ # Mapper 接口(19 个) │ │ │ │ └── service/ # 业务层 │ │ │ │ ├── (19 个 Service 接口) │ │ │ │ └── impl/ # Service 实现(19 个 Impl 类) │ │ │ └── resources/ │ │ │ └── application.yml │ │ └── test/java/com/example/demo/ │ │ └── DemoApplicationTests.java # 单元测试 │ ├── .gitattributes / .gitignore │ ├── mvnw / mvnw.cmd # Maven Wrapper │ └── pom.xml │ └── frontend/ # 前端(Vue 3) ├── public/ │ ├── favicon.ico │ └── index.html ├── src/ │ ├── api/ # 后端接口封装(17 个模块,上传内联在 ImagePicker) │ │ ├── attractionOrder.js / attractions.js │ │ ├── comments.js / config.js / coupon.js │ │ ├── favor.js / forum.js │ │ ├── hotel.js / hotelItem.js / hotelOrder.js │ │ ├── line.js / lineOrder.js │ │ ├── notification.js / payment.js / rotations.js │ │ ├── user.js / userCoupon.js │ ├── assets/ │ │ ├── css/reset.css │ │ └── logo.png │ ├── router/index.js # 路由配置 + 鉴权守卫(admin 正向白名单) │ ├── store/ │ │ ├── index.js │ │ └── modules/user.js # Vuex 用户状态 │ ├── utils/ │ │ ├── request.js # Axios 实例 + 拦截器 │ │ └── cover.js # 图片路径工具(normalizePath 智能识别多种格式) │ ├── components/ │ │ └── ImagePicker.vue # 图片选择组件(上传 + URL 输入 + 预览) │ ├── views/ │ │ ├── admin/ # 管理后台(11 个页面) │ │ │ ├── AdminLayout.vue # 后台布局 │ │ │ ├── AdminDashboard.vue # 数据看板(ECharts) │ │ │ ├── AdminUsers.vue │ │ │ ├── AdminAttractions.vue │ │ │ ├── AdminHotels.vue │ │ │ ├── AdminLines.vue │ │ │ ├── AdminOrders.vue │ │ │ ├── AdminForum.vue │ │ │ ├── AdminRotations.vue │ │ │ ├── AdminCoupons.vue │ │ │ └── AdminConfig.vue │ │ ├── LoginView.vue # 登录 │ │ ├── RegisterView.vue # 注册 │ │ ├── HomeView.vue # 首页 │ │ ├── UserCenter.vue # 个人中心 │ │ ├── AttractionsView.vue # 景点列表 │ │ ├── AttractionDetail.vue # 景点详情 │ │ ├── HotelView.vue # 酒店列表 │ │ ├── HotelDetail.vue # 酒店详情 │ │ ├── LinesView.vue # 线路列表 │ │ ├── LineDetail.vue # 线路详情 │ │ └── ForumDetail.vue # 论坛详情 │ ├── App.vue │ └── main.js ├── tests/unit/example.spec.js # 单元测试样例 ├── .editorconfig / .gitignore ├── README.md # Vue CLI 默认生成 ├── babel.config.js ├── jsconfig.json # JS 项目配置 ├── package.json / package-lock.json └── vue.config.js # 含 devServer 配置 ``` --- ## 六、API 接口清单 > 共 20 个 Controller,计 **87 个接口**。鉴权策略来自 SaTokenConfig。 ### 鉴权规则说明 | 路径 | 方法 | 鉴权 | |---|---|---| | `/api/user/login`、`/api/user/register` | POST | 公开 | | `/api/attractions/**`、`/api/hotel/**`、`/api/line/**`、`/api/rotations/**`、`/api/forum/**`、`/api/comments/**` | GET | 公开 | | 上述 6 个资源路径 | POST / PUT / DELETE | 需登录 | | `/api/upload/image` | POST | 需登录 | | 其余 `/api/**` | 全部方法 | 需登录 | ### 一、用户模块(SysUserController) **路径前缀**: `/api/user` | **返回格式**: `Result` | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | POST | `/api/user/login` | 用户登录 | 公开 | `Body: {username, password}` | | POST | `/api/user/register` | 用户注册 | 公开 | `Body: {username, password, nickname}` | | POST | `/api/user/logout` | 用户退出 | 需登录 | 无(通过 Token 识别) | | GET | `/api/user/info` | 获取当前用户信息 | 需登录 | 无(通过 Token 识别) | | GET | `/api/user` | 用户列表(分页) | 需登录 | `page=1&size=10` | | GET | `/api/user/{id}` | 用户详情 | 需登录 | `id`(路径参数) | | POST | `/api/user` | 新增用户 | 需登录 | `Body: SysUser 对象` | | PUT | `/api/user/{id}` | 修改用户 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/user/{id}` | 删除用户 | 需登录 | `id`(路径参数) | > 密码使用 BCrypt 加密存储,返回用户信息时自动脱敏 `password` 字段。 ### 二、景点模块(SysAttractionsController) **路径前缀**: `/api/attractions` | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/attractions` | 景点列表(分页) | 公开 | `page=1&size=10`、`name`(模糊)、`city`、`category` | | GET | `/api/attractions/{id}` | 景点详情 | 公开 | `id`(路径参数) | | POST | `/api/attractions` | 新增景点 | 需登录 | `Body: SysAttractions 对象` | | PUT | `/api/attractions/{id}` | 修改景点 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/attractions/{id}` | 删除景点 | 需登录 | `id`(路径参数) | ### 三、酒店模块(SysHotelController) **路径前缀**: `/api/hotel` | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/hotel` | 酒店列表(分页) | 公开 | `page=1&size=10`、`city`、`starLevel` | | GET | `/api/hotel/{id}` | 酒店详情 | 公开 | `id`(路径参数) | | POST | `/api/hotel` | 新增酒店 | 需登录 | `Body: SysHotel 对象` | | PUT | `/api/hotel/{id}` | 修改酒店 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/hotel/{id}` | 删除酒店 | 需登录 | `id`(路径参数) | ### 四、房型模块(SysHotelItemController) **路径前缀**: `/api/hotel-item` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/hotel-item/hotel/{hotelId}` | 按酒店查询房型列表 | 需登录 | `hotelId`(路径参数) | | GET | `/api/hotel-item/{id}` | 房型详情 | 需登录 | `id`(路径参数) | | POST | `/api/hotel-item` | 新增房型 | 需登录 | `Body: SysHotelItem 对象` | | PUT | `/api/hotel-item/{id}` | 修改房型 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/hotel-item/{id}` | 删除房型 | 需登录 | `id`(路径参数) | ### 五、旅游线路模块(SysLineController) **路径前缀**: `/api/line` | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/line` | 线路列表(分页) | 公开 | `page=1&size=10` | | GET | `/api/line/{id}` | 线路详情 | 公开 | `id`(路径参数) | | POST | `/api/line` | 新增线路 | 需登录 | `Body: SysLine 对象` | | PUT | `/api/line/{id}` | 修改线路 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/line/{id}` | 删除线路 | 需登录 | `id`(路径参数) | ### 六、景点订单模块(SysAttractionOrderController) **路径前缀**: `/api/attraction-order` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/attraction-order` | 订单列表(分页) | 需登录 | `page=1&size=10`、`userId`、`status` | | GET | `/api/attraction-order/{id}` | 订单详情 | 需登录 | `id`(路径参数) | | POST | `/api/attraction-order` | 创建订单 | 需登录 | `Body: SysAttractionOrder`(自动生成订单号 AO+时间戳) | | PUT | `/api/attraction-order/{id}/status` | 更新订单状态 | 需登录 | `id`(路径参数),`status`(Query) | | DELETE | `/api/attraction-order/{id}` | 删除订单 | 需登录 | `id`(路径参数) | ### 七、酒店订单模块(SysHotelOrderController) **路径前缀**: `/api/hotel-order` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/hotel-order` | 订单列表(分页) | 需登录 | `page=1&size=10`、`userId`、`status` | | GET | `/api/hotel-order/{id}` | 订单详情 | 需登录 | `id`(路径参数) | | POST | `/api/hotel-order` | 创建订单 | 需登录 | `Body: SysHotelOrder`(自动生成订单号 HO+时间戳) | | PUT | `/api/hotel-order/{id}/status` | 更新订单状态 | 需登录 | `id`(路径参数),`status`(Query) | | DELETE | `/api/hotel-order/{id}` | 删除订单 | 需登录 | `id`(路径参数) | ### 八、线路订单模块(SysLineOrderController) **路径前缀**: `/api/line-order` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/line-order` | 订单列表(分页) | 需登录 | `page=1&size=10`、`userId`、`status` | | GET | `/api/line-order/{id}` | 订单详情 | 需登录 | `id`(路径参数) | | POST | `/api/line-order` | 创建订单 | 需登录 | `Body: SysLineOrder`(自动生成订单号 LO+时间戳) | | PUT | `/api/line-order/{id}/status` | 更新订单状态 | 需登录 | `id`(路径参数),`status`(Query) | | DELETE | `/api/line-order/{id}` | 删除订单 | 需登录 | `id`(路径参数) | ### 九、支付模块(SysPaymentController) **路径前缀**: `/api/payment` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/payment` | 支付记录(分页) | 需登录 | `page=1&size=10`、`userId`、`status` | | GET | `/api/payment/{id}` | 支付详情 | 需登录 | `id`(路径参数) | | POST | `/api/payment` | 创建支付记录 | 需登录 | `Body: SysPayment 对象` | | PUT | `/api/payment/{id}` | 更新支付记录 | 需登录 | `id`(路径参数),`Body: 更新字段` | > 无删除接口。 ### 十、优惠券模块(SysCouponController) **路径前缀**: `/api/coupon` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/coupon` | 优惠券列表(分页) | 需登录 | `page=1&size=10` | | GET | `/api/coupon/{id}` | 优惠券详情 | 需登录 | `id`(路径参数) | | POST | `/api/coupon` | 新增优惠券 | 需登录 | `Body: SysCoupon 对象` | | PUT | `/api/coupon/{id}` | 修改优惠券 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/coupon/{id}` | 删除优惠券 | 需登录 | `id`(路径参数) | ### 十一、用户优惠券模块(SysUserCouponController) **路径前缀**: `/api/user-coupon` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/user-coupon/user/{userId}` | 用户优惠券列表 | 需登录 | `userId`(路径参数),`status` | | POST | `/api/user-coupon` | 领取优惠券 | 需登录 | `Body: SysUserCoupon 对象` | | PUT | `/api/user-coupon/{id}/use` | 使用优惠券 | 需登录 | `id`(路径参数),`orderType`、`orderId`(Query) | > 无通用列表、详情、删除接口。 ### 十二、评论模块(SysCommentsController) **路径前缀**: `/api/comments` | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/comments` | 评论列表(分页) | 公开 | `page=1&size=10`、`attractionId` | | POST | `/api/comments` | 发表评论 | 需登录 | `Body: SysComments 对象` | | PUT | `/api/comments/{id}/reply` | 管理员回复评论 | 需登录 | `id`(路径参数),`reply`(Query) | | DELETE | `/api/comments/{id}` | 隐藏评论 | 需登录 | `id`(路径参数) | > DELETE 为软删除(设置 status=0),非物理删除。 ### 十三、收藏模块(SysFavorController) **路径前缀**: `/api/favor` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/favor/user/{userId}` | 用户收藏列表 | 需登录 | `userId`(路径参数) | | GET | `/api/favor/check` | 检查是否已收藏 | 需登录 | `userId`、`itemType`、`itemId`(Query) | | POST | `/api/favor` | 添加收藏 | 需登录 | `Body: SysFavor 对象` | | DELETE | `/api/favor/{id}` | 取消收藏 | 需登录 | `id`(路径参数) | > 无修改接口。 ### 十四、论坛/资讯模块(SysForumController) **路径前缀**: `/api/forum` | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/forum` | 资讯列表(分页) | 公开 | `page=1&size=10`、`category` | | GET | `/api/forum/{id}` | 资讯详情 | 公开 | `id`(路径参数) | | POST | `/api/forum` | 发布资讯 | 需登录 | `Body: SysForum 对象` | | PUT | `/api/forum/{id}` | 修改资讯 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/forum/{id}` | 删除资讯 | 需登录 | `id`(路径参数) | ### 十五、轮播图模块(SysRotationsController) **路径前缀**: `/api/rotations` | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/rotations` | 轮播图列表 | 公开 | `position=home`(Query,默认值) | | GET | `/api/rotations/{id}` | 轮播图详情 | 公开 | `id`(路径参数) | | POST | `/api/rotations` | 新增轮播图 | 需登录 | `Body: SysRotations 对象` | | PUT | `/api/rotations/{id}` | 修改轮播图 | 需登录 | `id`(路径参数),`Body: 更新字段` | | DELETE | `/api/rotations/{id}` | 删除轮播图 | 需登录 | `id`(路径参数) | > GET 列表返回 `List` 非 `Page`(按 position 查询,默认 "home")。 ### 十六、系统配置模块(SysConfigController) **路径前缀**: `/api/config` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/config` | 配置列表 | 需登录 | 无 | | GET | `/api/config/{key}` | 按 key 查询配置值 | 需登录 | `key`(路径参数) | | POST | `/api/config` | 新增/更新配置 | 需登录 | `Body: {configKey, configValue}`(重复 key 自动更新) | | DELETE | `/api/config/{id}` | 删除配置 | 需登录 | `id`(路径参数) | > GET 列表返回 `List` 非 `Page`。 ### 十七、通知模块(SysNotificationController) **路径前缀**: `/api/notification` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/notification/user/{userId}` | 用户通知(分页) | 需登录 | `userId`(路径参数),`page=1&size=10` | | PUT | `/api/notification/{id}/read` | 标记已读 | 需登录 | `id`(路径参数) | | POST | `/api/notification` | 发送通知 | 需登录 | `Body: SysNotification 对象` | | DELETE | `/api/notification/{id}` | 删除通知 | 需登录 | `id`(路径参数) | > 无通用列表、详情接口。 ### 十八、操作日志模块(SysOperationLogController) **路径前缀**: `/api/operation-log` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/operation-log` | 日志列表(分页) | 需登录 | `page=1&size=20`、`userId`、`module` | | POST | `/api/operation-log` | 记录日志 | 需登录 | `Body: SysOperationLog 对象` | > 仅 GET 和 POST,无 PUT/DELETE。默认分页大小为 20。 ### 十九、客流统计模块(SysTouristCountController) **路径前缀**: `/api/tourist-count` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | GET | `/api/tourist-count` | 按条件查询统计 | 需登录 | `attractionId`、`startDate`、`endDate`(Query,必填) | | POST | `/api/tourist-count` | 新增统计记录 | 需登录 | `Body: SysTouristCount 对象` | > 仅 GET 和 POST。GET 返回 `List`,三个参数均为必填。 ### 二十、图片上传模块(SysUploadController) **路径前缀**: `/api/upload` | **全部需登录** | 方法 | 路径 | 说明 | 鉴权 | 参数 | |---|---|---|---|---| | POST | `/api/upload/image` | 通用图片上传 | 需登录 | `multipart/form-data`,字段 `file`;校验 image/* 类型 + 扩展名(jpg/jpeg/png/gif/webp/bmp);UUID 命名保存到 `images/`,返回 `/images/xxx.png` | > 后端用 `Files.copy` + 绝对路径写入,避免 `MultipartFile.transferTo` 把相对路径解析到 Tomcat 临时 work 目录。前端通过 `ImagePicker.vue` 组件调用,自动携带 satoken header。 ### 接口汇总 | 模块 | 路径前缀 | 接口数 | GET | POST | PUT | DELETE | 公开 GET | |---|---|---|---|---|---|---|---| | 用户 | `/api/user` | 9 | 3 | 3 | 1 | 1 | login/register (POST) | | 景点 | `/api/attractions` | 5 | 2 | 1 | 1 | 1 | 2(列表+详情) | | 酒店 | `/api/hotel` | 5 | 2 | 1 | 1 | 1 | 2(列表+详情) | | 房型 | `/api/hotel-item` | 5 | 2 | 1 | 1 | 1 | 0 | | 线路 | `/api/line` | 5 | 2 | 1 | 1 | 1 | 2(列表+详情) | | 景点订单 | `/api/attraction-order` | 5 | 2 | 1 | 1 | 1 | 0 | | 酒店订单 | `/api/hotel-order` | 5 | 2 | 1 | 1 | 1 | 0 | | 线路订单 | `/api/line-order` | 5 | 2 | 1 | 1 | 1 | 0 | | 支付 | `/api/payment` | 4 | 2 | 1 | 1 | 0 | 0 | | 优惠券 | `/api/coupon` | 5 | 2 | 1 | 1 | 1 | 0 | | 用户优惠券 | `/api/user-coupon` | 3 | 1 | 1 | 1 | 0 | 0 | | 评论 | `/api/comments` | 4 | 1 | 1 | 1 | 1 | 1(列表) | | 收藏 | `/api/favor` | 4 | 2 | 1 | 0 | 1 | 0 | | 论坛/资讯 | `/api/forum` | 5 | 2 | 1 | 1 | 1 | 2(列表+详情) | | 轮播图 | `/api/rotations` | 5 | 2 | 1 | 1 | 1 | 2(列表+详情) | | 配置 | `/api/config` | 4 | 2 | 1 | 0 | 1 | 0 | | 通知 | `/api/notification` | 4 | 1 | 1 | 1 | 1 | 0 | | 操作日志 | `/api/operation-log` | 2 | 1 | 1 | 0 | 0 | 0 | | 客流统计 | `/api/tourist-count` | 2 | 1 | 1 | 0 | 0 | 0 | | 图片上传 | `/api/upload` | 1 | 0 | 1 | 0 | 0 | 0 | | **合计** | | **87** | **34** | **22** | **16** | **14** | **11** | ### 鉴权统计 | 鉴权级别 | 数量 | |---|---| | 公开(无需登录) | 13(含 2 个公开 POST) | | 需登录 | 74 | 完整接口文档:启动后端后访问 http://localhost:8080/doc.html --- ## 七、安全设计 | 风险点 | 方案 | |---|---| | 密码明文存储 | BCrypt 哈希(自带盐) | | 密码泄露风险 | `SysUser.password` 字段加 `@JsonIgnore`,序列化时自动忽略 | | 鉴权状态丢失 | Sa-Token 统一管理,Token 写入 localStorage | | 跨域请求被拦截 | `CorsFilter`(高优先级 Filter)+ Origin 白名单(`localhost:3000` / `localhost:8080`) | | 异常返回不一致 | `GlobalExceptionHandler` + `Result` 统一结构 | | 接口越权访问 | `SaInterceptor` 拦截 `/api/**`,**精细化按 HTTP 方法放行**(GET 公开查询、POST/PUT/DELETE 需登录) | | 配置信息泄露 | 数据库账号密码支持环境变量覆盖(`DB_USERNAME` / `DB_PASSWORD` / `DB_URL`) | | 前端路由越权 | admin 后台正向白名单校验(必须是 `admin` / `super_admin` 角色) | | 测试账号泄露 | LoginView 已移除硬编码的测试账号提示 | ### Sa-Token 鉴权策略 | 路径 | HTTP 方法 | 鉴权 | |---|---|---| | `/api/user/login` | POST | 公开 | | `/api/user/register` | POST | 公开 | | `/api/attractions/**` | GET | 公开(查询) | | `/api/attractions/**` | POST / PUT / DELETE | 需登录(写操作) | | `/api/hotel/**` `/api/line/**` `/api/rotations/**` `/api/forum/**` `/api/comments/**` | GET | 公开(查询) | | 上述资源 | POST / PUT / DELETE | 需登录(写操作) | | 其它 `/api/**` | 所有方法 | 需登录 | **生产部署前还需**:禁用 Knife4j(`knife4j.production=true`)、移除 devtools、收紧 Sa-Token timeout。 --- ## 八、项目改进清单 ### 功能新增 | 新增功能 | 说明 | |---|---| | Sa-Token 鉴权系统 | 登录/注册/退出/Token 管理,细粒度权限控制 | | BCrypt 密码加密 | 明文密码 → BCrypt 哈希存储,spring-security-crypto | | 全局异常处理 | GlobalExceptionHandler + BusinessException 统一包装 | | 统一返回结构 | Result\ = {code, message, data},前后端约定 | | Knife4j 接口文档 | 自动生成 OpenAPI 3 文档,访问 /doc.html | | 用户注册 | 公开注册接口,新用户可自行注册 | | ECharts 数据看板 | AdminDashboard 柱状图 + 环形饼图 | | 图片本地化 | 48 个景区图片复制到 backend/demo/images/,随项目分发 | | 环境变量配置 | DB_USERNAME / DB_PASSWORD / DB_URL / IMAGES_DIR 可覆盖 | | 图片上传接口 | 后端新增 SysUploadController,POST /api/upload/image 统一上传,UUID 命名保存到 images/ | | ImagePicker 组件 | 前端新增组件,集成本地上传 + URL 输入 + 实时预览,5 个 admin 表单已接入 | | 图片路径规范化 | cover.js 新增 normalizePath,识别 http/data/`/images/`/`images/`/绝对路径等多种格式,无效时走 SVG 兜底 | | 景点开放时间 | AdminAttractions 表单新增 openTime/closeTime 字段(el-time-picker) | ### Bug 修复 | 序号 | 问题 | 修复方法 | |---|---|---| | 1 | 数据库中文乱码("?") | MySQL 导入时指定 UTF8mb4 | | 2 | 景区图片不显示(灰图) | WebResourceConfig 新增 /images/** 静态资源映射 | | 3 | 图片路径硬编码,同学电脑 404 | 绝对路径 → 相对路径 ./images/,图片随项目打包 | | 4 | 密码明文存储 | BCrypt 哈希替换,HashGen 生成有效哈希 | | 5 | 登录报"密码错误" | BCrypt 哈希不匹配 → 重新生成对应哈希 | | 6 | BCrypt 哈希返回前端 | SysUser.password 加 @JsonIgnore | | 7 | CorsConfig 导致 OPTIONS 预检 500 | WebMvcConfigurer → CorsFilter(最高优先级 Filter) | | 8 | CORS 请求报 Network Error | 同上,CorsFilter 在 SaToken 拦截器之前处理 | | 9 | SaToken 拦截器报异常 | 还原为干净的 SaInterceptor 配置 | | 10 | 注册功能不可用 | RegisterView 调用 addUser(需登录)→ 改为 register(公开接口) | | 11 | 景点页收藏无法取消 | 前端 toggle 逻辑修复 | | 12 | 个人中心取消订单弹窗报错 | 取消按钮回调函数修复 | | 13 | 订单取消无二次确认 | 增加 ElMessageBox 确认弹窗 | | 14 | ESLint dot-notation 报错 | request.js 中 `["satoken"]` → `.satoken` | | 15 | ESLint no-multi-spaces 报错 | UserCenter.vue 中双空格 → 单空格 | | 16 | ESLint plugin 'n' 找不到 | eslint-plugin-node → eslint-plugin-n@15 | | 17 | npm ERESOLVE 依赖冲突 | eslint 7→8, eslint-plugin-vue 8→9, eslint-config-standard 升级 | | 18 | TypeScript 隐式类型库报错 | jsconfig.json 加 types:[] + skipLibCheck | | 19 | 后台图片裂图:用户输入 `images/xxx`、绝对路径等非标准格式,imgUrl 忽略,4 个详情页直接用 coverImage 绕过兜底 | cover.js 新增 normalizePath 智能识别多种格式;4 个展示页改用 imgUrl/coverUrl computed,无效时走 SVG 渐变占位图 | | 20 | 图片上传报 FileNotFoundException,保存到 Tomcat 临时 work 目录 | MultipartFile.transferTo 对相对路径按 Tomcat work 解析;改用 Files.copy + 绝对路径写入 | | 21 | 7 个 admin 页面点"取消"弹 Uncaught runtime errors: cancel | ElMessageBox.confirm reject 'cancel' 未 catch;全部加 try/catch 静默返回 | | 22 | 新增景点表单缺开放时间字段 | AdminAttractions 表单新增 openTime/closeTime(el-time-picker,HH:mm:ss) | ### 架构改进 | 序号 | 改进项 | 改动 | |---|---|---| | 1 | 包结构归位 | CorsConfig 从根包 → config/ 包 | | 2 | 删除脚手架残留 | HelloController.java / HelloWorld.vue / AboutView.vue | | 3 | 删除临时文件 | export_desc.js / export_desc.sh / split_intro.js / HELP.md / update_desc.js | | 4 | 删除残留日志 | backend-run.log / frontend-run.log | | 5 | 删除冗余配置 | DataSourceConfig.java(Spring Boot 自动配置已覆盖) | | 6 | SQL 脚本归位 | 6 个 SQL 文件散落根目录 → 统一归入 sql/ 子目录 | | 7 | 目录重命名 | h\demo → backend\demo,hello → frontend | | 8 | 外层嵌套删除 | 前后端分离.../前后端分离.../ → 一层 | | 9 | workspace 路径修正 | 绝对路径 → 相对路径 ./backend/demo + ./frontend | | 10 | 前端依赖修复 | eslint 系列升级到兼容版本,解决 npm install 冲突 | | 11 | jsconfig 配置 | 修复 TS 隐式类型库报错 | ### 前端修改 | 文件 | 改动 | |---|---| | package.json | +echarts 5.5.0,eslint 7→8,eslint-plugin-vue 8→9,+eslint-plugin-n | | jsconfig.json | 修复 TS 类型报错 | | store/modules/user.js | 登录改用 /api/user/login,token 存 Sa-Token | | utils/request.js | 请求拦截器自动携带 satoken header,响应拦截器统一处理 Result | | router/index.js | admin 路由改为正向白名单 | | views/LoginView.vue | 删除测试账号提示 | | views/RegisterView.vue | 注册改用 POST /api/user/register | | views/AdminDashboard.vue | 新增 ECharts 柱状图 + 饼图 | | views/UserCenter.vue | 修复取消订单弹窗取消按钮报错 + ESLint | | views/AttractionsView.vue | 修复收藏按钮 toggle | | utils/cover.js | 新增 normalizePath/resolveImg,imgUrl 走规范化 + SVG 兜底 | | components/ImagePicker.vue | 新增图片选择组件(上传 + URL 输入 + 预览) | | views/AttractionDetail.vue / ForumDetail.vue / HotelDetail.vue / LinesView.vue | 改用 imgUrl/coverUrl computed,避免裂图 | | views/admin/AdminAttractions.vue | el-input→ImagePicker,新增开放时间字段,handleDelete 加 catch | | views/admin/AdminHotels.vue | el-input→ImagePicker,handleDelete + deleteRoom 加 catch | | views/admin/AdminLines.vue / AdminForum.vue / AdminRotations.vue | el-input→ImagePicker,handleDelete 加 catch | | views/admin/AdminCoupons.vue | handleDelete 加 catch | | views/admin/AdminUsers.vue | toggleStatus 加 catch | | 删除 | HelloWorld.vue / AboutView.vue | ### 后端修改 | 文件 | 改动 | |---|---| | pom.xml | +sa-token 1.38.0,+knife4j 4.5.0,+hutool 5.8.27,+spring-security-crypto,+validation | | application.yml | 删除无效逻辑删除配置,+Sa-Token 配置,+Knife4j 配置,DB 密码改为环境变量,+multipart 配置 | | CorsConfig.java | WebMvcConfigurer → CorsFilter,Origin 白名单 | | SaTokenConfig.java | 新增,按 HTTP 方法精细化鉴权 | | WebResourceConfig.java | 新增,/images/** → ./images/ 静态资源映射 | | SysUserController.java | +login/+register/+logout/+info 接口,BCrypt 密码验证 | | SysUser.java | password 字段 +@JsonIgnore | | Result.java | 新增,统一返回结构 | | BusinessException.java | 新增,业务异常 | | GlobalExceptionHandler.java | 新增,全局异常处理 | | SysUploadController.java | 新增,POST /api/upload/image 通用图片上传,Files.copy + 绝对路径 | | 删除 | HelloController.java / DataSourceConfig.java / HashGen.java | ### 新增文件清单 | 文件 | 说明 | |---|---| | backend/demo/sql/ | SQL 脚本统一目录 | | backend/demo/images/ | 景区图片本地化(48 个文件) | | common/Result.java | 统一返回结构 | | exception/BusinessException.java | 业务异常 | | exception/GlobalExceptionHandler.java | 全局异常处理 | | config/SaTokenConfig.java | Sa-Token 鉴权配置 | | config/WebResourceConfig.java | 静态资源映射 | | controller/SysUploadController.java | 图片上传接口 | | frontend/src/components/ImagePicker.vue | 图片选择组件(上传 + URL 输入 + 预览) | ### 删除文件清单 | 文件 | 原因 | |---|---| | HelloController.java | 脚手架残留 | | DataSourceConfig.java | 冗余配置 | | CorsConfig.java(旧位置) | 移到 config/ 包 | | HashGen.java | 临时工具 | | HelloWorld.vue | 脚手架残留 | | AboutView.vue | 脚手架残留 | | export_desc.js | 临时脚本 | | export_desc.sh | 临时脚本 | | split_intro.js | 临时脚本 | | HELP.md | Spring 脚手架自动生成 | | update_desc.js | 临时脚本 | | backend-run.log | 残留日志 | | frontend-run.log | 残留日志 | | 外层景区图片/ | 已复制到 backend/demo/images/ | --- ## 九、常见问题 > 📌 部署踩坑全过程与根因分析见 [DEPLOYMENT_LESSONS.md](DEPLOYMENT_LESSONS.md)(OOM、502、图片丢失、env-file 等 9 类问题)。 ### Q1:启动后端报"端口 8080 被占用" ```bash # Windows 查找占用进程 netstat -ano | findstr ":8080" taskkill /PID <进程号> /F ``` ### Q2:前端登录提示 Network Error - 确认后端已启动(http://localhost:8080 能访问) - 浏览器清空缓存(Ctrl + Shift + Delete) - 检查后端控制台是否启动成功(看到 `Started DemoApplication`) ### Q3:景区图片不显示(灰图/裂图) - 检查 `backend/demo/images/` 目录是否存在并包含景区图片 - 后端 `WebResourceConfig.java` 已配置 `/images/**` → `./images/` 相对路径映射 - 前端 `cover.js` 的 `normalizePath()` 可识别 `/images/xxx`、`images/xxx`、`./images/xxx`、绝对路径等多种格式,路径无效时显示渐变占位图(不再裂图) - 后台新增/编辑可通过 ImagePicker 组件点击上传图片,自动保存到 `images/` 并回填路径 - 打包给同学时务必包含 `backend/demo/images/` 目录 - 可通过环境变量 `IMAGES_DIR` 覆盖默认路径 ### Q3.1:后台图片上传报"图片保存失败" - `SysUploadController` 已用 `Files.copy` + 绝对路径写入,避免 `MultipartFile.transferTo` 把相对路径解析到 Tomcat 临时 work 目录 - 若仍失败,查看后端控制台日志 `图片上传成功,保存路径: xxx` 确认实际保存位置 - 如保存路径不是 `backend/demo/images/`,说明 `user.dir` 不对,可在 `application.yml` 显式指定绝对路径: ```yaml app: images-dir: d:/前后端分离的景区管理系统程序/backend/demo/images/ ``` ### Q4:同学运行 `npm install` 报 ERESOLVE 冲突 依赖版本已修复(eslint 升级到 8、`eslint-plugin-node` 改为 `eslint-plugin-n`)。如仍报错: ```bash Remove-Item -Recurse -Force node_modules npm install ``` ### Q5:注册功能不可用 - 前端注册走公开接口 `POST /api/user/register`(无需登录) - 若 401 错误,检查 `SaTokenConfig.java` 白名单是否包含 `/api/user/register` ### Q6:Docker 部署后所有接口 502 Bad Gateway 通常是后端容器没起来或正在重启,按顺序排查: ```bash docker compose --env-file docker/.env ps # 看容器状态 docker compose --env-file docker/.env logs backend # 看后端日志 ``` 常见原因: - **忘记带 `--env-file docker/.env`**:MySQL 因 `MYSQL_ROOT_PASSWORD` 为空启动失败,后端连不上数据库。必须用 `docker compose --env-file docker/.env up -d` - **后端 OOM 被杀**:`docker logs backend` 看到 Spring Boot 反复启动,`docker inspect tourism-backend` 的 `RestartCount` 持续增长。后端内存限制需 ≥ 1024M(见 `docker-compose.yml`) - **后端还在启动中**:Spring Boot 启动约需 12 秒,期间请求会 502,等日志出现 `Started DemoApplication` 即可 ### Q7:Docker 部署后后端启动失败(logback 报错) 报错 `Failed to create parent directories for /home/app/logs/app.log`:后端以非 root 用户 `app` 运行,其 home 目录的 `logs` 子目录需在镜像构建时创建(已在 `backend/demo/Dockerfile` 处理)。若自行修改了 Dockerfile 的 `USER` 指令,需同步创建该目录。 --- ## 十、开发说明 ### 代码规范 - 后端包名统一 `com.example.demo` - Controller 只做参数接收,业务逻辑放 Service - 接口返回 `Result` 统一结构(注:部分旧 Controller 仍直接返回 Page/boolean,待统一) - 实体密码字段加 `@JsonIgnore` 防止泄露 ### 前后端联调 - 前端 Axios 默认 baseURL `http://localhost:8080/api`(直连后端,CORS 已配) - `vue.config.js` 中的 `devServer.proxy` 作为备用方案 - 开发期跨域由 `CorsFilter` 统一处理 ### 关键文件速查 | 文件 | 作用 | |---|---| | `backend/demo/src/main/resources/application.yml` | 后端配置(数据源、Sa-Token、Knife4j) | | `backend/demo/src/main/java/.../config/SaTokenConfig.java` | 鉴权白名单(修改放行路径) | | `backend/demo/src/main/java/.../config/CorsConfig.java` | CORS 白名单(修改允许的 Origin) | | `backend/demo/src/main/java/.../config/WebResourceConfig.java` | 图片静态资源映射 | | `frontend/src/utils/request.js` | Axios 实例(baseURL、拦截器) | | `frontend/src/utils/cover.js` | 图片路径工具(normalizePath/imgUrl/resolveImg) | | `frontend/src/components/ImagePicker.vue` | 图片选择组件(上传 + URL 输入 + 预览) | | `frontend/src/router/index.js` | 路由 + 鉴权守卫 | | `frontend/src/store/modules/user.js` | 用户状态管理 | | `frontend/vue.config.js` | 前端构建配置 + devServer | --- ## License 仅供学习交流使用。