ARTICLE DETAIL

资讯详情

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

Spring Boot旅游攻略平台实战:从数据库设计到JWT鉴权全解析

Spring Boot旅游攻略平台实战:从数据库设计到JWT鉴权全解析 在做旅游攻略类项目时我见过太多团队把精力放在景点文案和图片美化上结果后端一压测就崩、代码一接手就乱。今天这篇想和你聊聊一个基于Spring Boot的旅游攻略分享平台从需求拆解到数据库设计、从JWT鉴权到搜索优化把那些文档里不会写、但在真实开发中一定会踩的坑全部摊开讲。如果你正在做类似的内容分享类Web项目或者在准备Java方向的面试、毕业设计这篇文章的实操细节可以直接抄作业。平台的核心价值其实就一句话让用户既能浏览高质量的旅游攻略也能自己发布游记、收藏景点、评论互动。听起来不复杂但“内容发布社交互动后台管理”三块功能揉在一起对工程结构的考验一点都不小。我后续会依次说明完整的搭建思路、表结构设计、核心接口实现以及我实际开发中遇到的几个经典问题尽量做到每一步都有据可依。1. 项目需求与架构设计思路1.1 核心需求解析这个平台到底要做什么先别看技术我们先把业务讲透。旅游攻略分享平台从用户视角看核心场景就是四件事浏览景点信息和他人发布的攻略内容完成出游决策注册登录后发布自己的游记攻略上传图片、编辑正文对感兴趣的攻略进行收藏、点赞、评论等互动操作管理员维护景区基础数据、审核用户发布的攻略、管理用户状态。这四件事对应到后端系统就是四个核心子系统内容展示与检索、用户认证与个人中心、内容发布与管理、后台管理。排序上有讲究内容展示和检索是门面用户认证是基础内容发布是核心业务后台管理是运营保障。很多新手会把用户系统做得过重一上来就搞权限树、Redis Session实际上在项目初期JWT无状态鉴权加数据库存储用户信息已经能覆盖绝大部分场景。从用户分类来看系统涉及两类前台用户和一类后台用户。普通用户能看、能搜、能收藏、能发攻略管理员负责审核和基础数据维护。这里有一个容易被忽略的需求点为什么攻略发布需要审核旅游攻略不是随便写几句就算完它涉及目的地信息、交通住宿建议一旦存在虚假或者违规内容用户照着攻略出行出问题就是平台的责任。所以管理员审核环节是必须保留的这也是这个项目和普通博客系统的重要差异。1.2 技术选型为什么是Spring Boot Java版本怎么选技术选型这块我直接给结论附对比理由。后端框架选择Spring Boot这个问题基本不需要纠结。Java在Web服务端的生态成熟度摆在那里Spring Boot的自动配置让项目可以在几分钟内跑起来而且Spring全家桶对事务管理、参数校验、AOP切面、定时任务都有完善支持。做内容平台尤其是涉及用户、评论、收藏这些强一致性数据的场景Java Spring Boot比Node.js或Go更省心的地方在于你不需要自己去拼一堆第三方库来做ORM、权限、校验社区里都有现成且经过大规模验证的解决方案。版本选择上我强烈建议新项目不要一上来就追最新版。以Spring Boot为例2.7.x版本是目前最稳妥的选择原因有三个2.7.x是2.x系列的最后一个维护版本官方兼容性文档最全网上踩坑案例最多遇到问题基本都能搜到答案3.x版本开始强制要求Java 17及以上如果你本机还停留在Java 8升级成本会直接把项目进度拖垮很多生产环境的中间件依赖比如某些版本的MyBatis-Plus、OSS SDK对Spring Boot 3.x的兼容还没有完全跟上没必要做小白鼠。搭配方案我列一张表这也是我自己多次实践下来的“标准答案”组件选型说明构建工具Maven生态最成熟私服依赖解析方便适合团队协作持久层框架MyBatis-Plus单表CRUD不用写SQL复杂查询可以手写XML兼顾效率与可控性数据库MySQL 5.7或8.0稳定文档多内容平台数据量级完全够用鉴权方案JWT Spring Boot拦截器无状态适合前后端分离部署不依赖Session前端Vue 3 Element UI不是重点但打包后的静态资源可以直接放进Spring Boot的static目录对象存储本地存储 虚拟路径映射前期免费用后期可平滑迁移到云OSS这里要特别解释一下MyBatis-Plus的取舍。对比Spring Data JPAMyBatis-Plus对复杂查询的掌控感更强。攻略列表需要按景区、发布时间、浏览量、收藏数多维筛选这种查询写SQL比让JPA自动生成清晰得多而且SQL调优时可以直接拿日志里的语句去EXPLAIN。相比之下JPA的自动建表和Repository抽象在做简单CRUD时很爽但一旦涉及多表关联统计维护Entity关联关系的成本会成倍上升。1.3 项目结构规划包划分和启动流程工程结构沿用传统的单模块分层不搞微服务。很多初学者听到“项目”两个字就想拆成多个服务这是一个误区。微服务的核心目标是独立部署、独立扩展对攻略分享平台这种业务量级单体应用维护成本远低于微服务。拆服务只会给自己增加网络调用、数据一致性、服务治理的负担。包结构我习惯这样划分com.example.travel ├── common # 通用类统一返回结果、异常处理、常量定义 ├── config # 配置类拦截器注册、虚拟路径映射、跨域配置 ├── controller # 接口层接收请求参数校验调用服务 ├── service # 业务层核心业务逻辑事务控制 ├── mapper # 数据访问层MyBatis-Plus的Mapper接口 ├── entity # 数据库实体类 ├── dto # 数据传输对象接收前端参数、返回前端数据 └── utils # 工具类JWT工具、文件上传工具、日期工具这样的分层逻辑很直白Controller只干活不决策Service只做业务不碰SQLMapper只负责数据不写逻辑。新人最容易犯的错误是Controller里直接写业务代码页面一多就变意大利面条后期改一个需求要牵连十几个接口。启动流程上主启动类配置SpringBootApplication和MapperScan注解前者是Spring Boot的自动装配入口后者让MyBatis-Plus可以扫描到Mapper接口并自动创建代理实现类。如果用的是2.7.x版本注意JDK版本需要8或11不要配成17。2. 数据库设计一张好表能省一半开发时间2.1 设计思路从业务对象梳理数据关系数据库设计是这类项目的地基地基歪了后面全是返工。旅游攻略平台的业务对象可以梳理成六个核心实体用户、景区、攻略、攻略详情、评论、收藏。实体关系说复杂也复杂说简单也简单关键看你会不会拆。我用一句话概括设计核心攻略是内容主体景区是内容归属用户是行为发起者评论、收藏、点赞都是用户对攻略的行为记录。这六个实体的关系可以这样理解一个用户可以发布多篇攻略攻略和用户是多对一关系一篇攻略归属一个景区景区和攻略是一对多关系一个用户可以评论多篇攻略也评论别人的评论评论表需要设计成自关联一个用户可以收藏多篇攻略为了记录收藏时间需要单独建一张收藏关联表点赞同理虽然可以不建表用统计字段但做互动记录更稳妥。数据库命名上表名用下划线分隔字段名统一小写主键统一用id逻辑删除用deleted创建时间create_time、更新时间update_time这套规范从第一个表坚持到最后一个表后面写代码会非常顺手。2.2 核心表结构SQL与字段说明用户表t_userCREATE TABLE t_user ( id bigint(20) NOT NULL AUTO_INCREMENT, 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 头像地址, email varchar(100) DEFAULT NULL COMMENT 邮箱, phone varchar(20) DEFAULT NULL COMMENT 手机号, status tinyint(1) DEFAULT 1 COMMENT 状态1正常0禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;景区表t_scenic_spotCREATE TABLE t_scenic_spot ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT 景区名称, location varchar(255) DEFAULT NULL COMMENT 所在地, description text COMMENT 景区简介, cover_image varchar(255) DEFAULT NULL COMMENT 封面图, level varchar(20) DEFAULT NULL COMMENT 景区等级如AAAAA, ticket_price decimal(10,2) DEFAULT NULL COMMENT 门票参考价, open_time varchar(100) DEFAULT NULL COMMENT 开放时间, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT景区表;攻略表t_articleCREATE TABLE t_article ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 发布用户ID, scenic_id bigint(20) DEFAULT NULL COMMENT 关联景区ID, title varchar(200) NOT NULL COMMENT 攻略标题, summary varchar(500) DEFAULT NULL COMMENT 摘要, cover_image varchar(255) DEFAULT NULL COMMENT 封面图, view_count int(11) DEFAULT 0 COMMENT 浏览量, like_count int(11) DEFAULT 0 COMMENT 点赞数, favorite_count int(11) DEFAULT 0 COMMENT 收藏数, status tinyint(1) DEFAULT 0 COMMENT 状态0待审核1已发布2已驳回3已删除, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_scenic_id (scenic_id), KEY idx_status_create_time (status, create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT攻略表;攻略正文单独建表t_article_detail为了保证列表接口轻量不要把正文大字段放到主表。收藏表t_favorite只需要三个字段id、user_id、article_id加一个唯一索引uk_user_article防止重复收藏。评论表t_comment需要设计parent_id字段支持楼层回复同时用article_id和user_id做关联查询。这里有一个实用技巧浏览量、点赞数、收藏数这三个统计字段我的做法是直接在攻略表里冗余存储而不是每次实时COUNT(*)。原因很简单列表页一屏要显示十条攻略如果每条攻略实时去统计收藏表、点赞表就是二十多次关联查询数据库压力瞬间拉满。冗余计数在数据量不大时有立竿见影的效果等真的到了需要精确计数的高并发阶段再引入Redis或消息队列做异步计数也不迟。2.3 查询设计与索引优化查询压力主要集中在两个接口首页攻略列表和攻略详情。首页列表的查询逻辑是分页查询状态下为已发布status1的攻略按is_top置顶字段、create_time创建时间倒序排列同时携带发布者昵称、景区名称、封面图。这类查询在MyBatis-Plus里可以先用分页插件再通过自定义SQL联表查出冗余字段。索引设计的经验是攻略表的status create_time复合索引满足列表页的筛选排序需求收藏表、点赞表建立user_id article_id唯一索引既能加速查询又能从数据库层面防止重复操作评论表建立article_id create_time复合索引避免详情页的评论列表全表扫描。索引不是越多越好每个索引都会增加写入开销。实战中我的判断标准是只在where条件、order by排序、group by分组中高频出现的字段上建索引像description这种大文本字段绝对不能加索引。这里你可能会问MySQL的LIKE %关键词%走不了索引怎么办答案是量级小的时候无所谓同一张表数据量在几十万条以内模糊查询性能还是能接受的。项目起步阶段不需要上Elasticsearch等搜不出结果时再加不迟。3. 核心功能模块的Spring Boot实现与踩坑记录3.1 注册登录与JWT鉴权无状态方案的正确姿势注册登录是每个Java Web项目都绕不开的模块。密码存储原则就一句话绝不能明文存库也不能只用MD5加密。正确做法是用BCrypt算法它是一种加盐的哈希算法同一个密码每次加密的结果都不同可以有效对抗彩虹表攻击。Spring Security里集成了BCrypt但很多项目不想引入整个Spring Security那也可以单独引入spring-security-crypto依赖单独使用BCryptPasswordEncoder。JWT的逻辑不难核心就三步用户登录成功后后端生成一个Token返回给前端前端把Token存在本地每次请求在Header里带上Authorization: Bearer token后端通过拦截器校验Token是否有效有效则放行无效则返回401。JWT本身包含header.payload.signature三段其中payload可以存用户ID、用户名、过期时间。生成和校验的工具类我习惯用io.jsonwebtoken的jjwt库代码很简洁。拦截器配置是容易被忽视的地方放行名单一定要想清楚。哪些接口不需要登录景区的公开信息浏览、攻略列表和详情、验证码获取、登录注册接口。哪些必须登录发布攻略、评论、收藏、点赞、个人中心。用拦截器做校验时路径匹配规则要写白名单而不是黑名单也就是默认全部拦截再逐个放行公共接口。这样更安全新加的接口默认是受保护的不会出现忘加鉴权导致越权的低级事故。提示跨域配置和拦截器注册经常打架。如果你的前端是Vue开发服务器后端单体应用必须同时配置CORS跨域放行和拦截器放行。只配跨域不配拦截器结果是请求能发出去但被拦截器挡在门外。两个组件都要处理OPTIONS预检请求否则前端会一直报跨域错误。JWT还有一个容易踩的坑用户登出问题。Token一旦签发在过期之前都是有效的。有些项目要求用户修改密码后旧Token立即失效这个需求用纯JWT做就有些吃力。我的方案是先增加密码修改时间字段拦截器里判断Token签发时间是否早于密码修改时间如果早于则强制重新登录。这个方案成本低能覆盖大部分业务场景。3.2 攻略发布与图片上传虚拟路径映射的细节攻略发布是一个典型的多步骤表单提交场景前端先上传图片拿到图片URL后再和攻略的文本内容一起提交给后端。图片上传有两个方案我拿实际回报和你说。方案一本地存储加虚拟路径映射简单直接适合部署在单台服务器上的项目。方案二接入云OSS适合上线后有稳定运维保障的项目。如果是课程设计、毕业设计或个人项目本地存储完全够用云OSS还要实名认证、创建Bucket、配权限大题小做。本地存储落地时有三个细节必须处理保存路径不能写死在代码里要放到application.yml配置文件中要配置虚拟路径映射把外部的真实文件目录映射到Spring Boot可访问的URL路径上文件名不能直接用用户上传的原始文件名要重命名为UUID或时间戳防止中文乱码和文件名冲突。核心配置如下file: upload-dir: /data/travel-images/ # 真实存储路径 access-path: /images/** # 虚拟访问路径再写一个WebMvcConfigurer配置类重写addResourceHandlers方法Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(fileProperties.getAccessPath()) .addResourceLocations(file: fileProperties.getUploadDir()); }这样前端拿到/images/xxx.jpg就能直接访问到服务器上的图片文件。如果你在本地Windows环境开发上传目录建议放在项目的绝对路径下部署到Linux服务器时再改成/data/travel-images/这类目录。图片上传接口需要注意单个文件大小限制。Spring Boot默认限制单次请求1MB攻略配图通常都在1MB以上必须手动修改配置spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB网上很多帖子只写max-file-size漏掉了max-request-size。如果表单里一次提交多张图片总大小超过max-request-size也会报错这个坑我至少见过三次。3.3 攻略搜索与列表从LIKE查询到全文检索的演进路径攻略列表和搜索接口是用户浏览的高频入口也是后期优化空间最大的地方。第一阶段用MyBatis-Plus的Wrapper做条件查询。根据前端传来的关键词、景区ID、排序方式动态拼接查询条件。关键词搜索用LIKE %关键词%排序支持最新发布、浏览量最高、收藏数最多。这种方式代码量少数据量不大时完全扛得住。第二阶段当数据量和搜索精准度要求上来后再引入Elasticsearch做全文检索。但需要注意全文检索的引入不应该推翻现有的MySQL查询体系而是作为搜索入口的替代方案。攻略发布时双写MySQL和Elasticsearch搜索接口直接查Elasticsearch详情页还是走MySQL这是比较稳妥的演进路径。注意不要把Elasticsearch当成数据库用。ES的底层是Lucene倒排索引擅长的是搜索和聚合分析不擅长事务更新和复杂关联。项目初期在MySQL里做好索引优化才是性价比最高的选择。3.4 评论、收藏、点赞幂等与防重复这三个功能业务上都很简单但实现时都有“防重复”的细节要求。点赞接口最容易出问题的地方是前端重复点击。用户在弱网环境下快速点了三次点赞按钮如果后端不做控制就会产生三次点赞请求点赞数被错误累加。解决方案有两个层面。数据库层面点赞记录表增加user_id article_id的唯一索引插入重复数据时会直接报错由数据库兜底。业务层面点赞接口设计成“状态翻转”逻辑更合理前端传一个目标状态值后端根据当前状态判断是否执行插入或删除而不是每次请求都无脑加一。我见过很多项目的收藏功能是这么写的前端点击收藏按钮调接口后端在收藏表插入一条数据收藏数加一。前端再点一次调另一个接口删除收藏记录收藏数减一。这种方式接口拆分没问题但在设计上显得碎。更好的做法是接口只提供一个toggleFavorite接口后端自动判读当前状态并切换前端只需要负责页面图标的刷新。这个方案API数量更少语义也更清晰。评论模块要注意自关联设计。t_comment表的parent_id字段顶层评论为0回复某条评论时parent_id指向被回复评论的ID。查询列表时先查顶层评论再根据顶层评论ID批量查子评论避免在循环里逐条查询造成N1问题。3.5 管理员模块拦截与审核的关键实现管理员最核心的功能就是把内容关和用户关。内容关是审核攻略用户关是封禁异常账号。从技术含量上讲没有难点但有两点经验值得分享。一是攻略审核列表的查询条件项目落地后大概率要支持按时间、按状态、按景区筛选所以接口设计时一开始就要把查询参数做成可选不要写死。二是用户封禁操作必须配合JWT鉴权一起考虑。管理员把某个用户的状态改为禁用后这个用户当前已经登录的Token仍然是有效的他不会立刻被踢下线。如果业务上要求立即生效就需要在拦截器里增加状态查询逻辑每次请求带上用户ID从数据库或缓存中读取用户状态发现被禁用就直接拒绝。这个做法会多一次查询但业务安全性更高实测下来性能影响不大。4. 常见问题与排查技巧实录4.1 Spring Boot启动失败端口占用与依赖冲突最经典的启动报错就是Port 8080 was already in use。终端执行netstat -ano | findstr 8080找到占用进程的PID然后在任务管理器里结束进程。要注意的是开发机上经常有别的Java进程占用端口不要随便结束进程确认是残留的旧服务再处理。参数上想换端口直接在application.yml里改server: port: 8081依赖冲突的问题多见于Maven项目启动时报NoClassDefFoundError或ClassNotFoundException。排查步骤是先mvn依赖树分析再看是哪个依赖传递引入了冲突的版本。实操中最常见的冲突来源是spring-boot-starter-web自带的Jackson版本和项目中其他组件依赖的Jackson版本不一致统一在pom.xml的properties节点中锁定版本即可。4.2 中文乱码问题三个环节逐一排查中文乱码在Java Web项目里几乎是必踩的坑其问题可能出现在三个环节数据库、接口请求、文件上传。数据库环节建表时ENGINEInnoDB DEFAULT CHARSETutf8mb4连接串加characterEncodingutf8参数两步缺一不可。注意是从MySQL 5.5版本开始才支持真正的UTF-8编码的完整显示MySQL 8.0之后utf8mb4已经是默认选项但显式指定永远是最稳妥的。接口请求环节Spring Boot 2.7.x已经内置了UTF-8编码过滤器一般不会乱码。如果你在自定义过滤器里动了request.setCharacterEncoding反而容易出问题。文件上传环节最容易乱码的是文件名。解决方案是文件保存时强制重命名不要直接用前端传过来的原始文件名用一个UUID加原始后缀名替代。4.3 接口返回数据为nullJSON序列化与查询空值接口返回null先分清是数据库查询结果为空还是JSON序列化时被过滤掉了。MyBatis-Plus查询出来是空对象去检查SQL是否命中了数据查询正常但返回JSON为null去看实体类字段是否有JsonIgnore注解或者全局配置是否开启了非空字段序列化。Spring Boot默认的Jackson序列化机制下值为null的字段会被序列化为null。如果前端不喜欢这种输出可以配置spring: jackson: default-property-inclusion: non_null这样所有null字段就不会出现在JSON结果中接口文档看起来清爽很多。这在实际联调中能减少不少前端同事的报错沟通成本。4.4 页面404Vue打包资源放进Spring Boot的坑项目完成后前端Vue项目执行npm run build会在dist目录下生成静态文件。把这些文件拷贝到Spring Boot项目下的src/main/resources/static目录重新打包后Spring Boot就能直接托管前端页面。实际运行中常见两个问题前端路由用的是history模式直接访问某个子路径如/travel/3Spring Boot会返回404这个404背后是后端没有对应的Controller来处理这个路径解决方法是实现一个路由兜底Controller把未匹配的路径转发到index.html让前端路由自己去解析。注意静态资源托管在Spring Boot里只适合小规模项目或演示环境。生产环境部署更好的方案是用Nginx托管前端静态文件同时反向代理后端的/api请求。Spring Boot只负责提供接口Nginx负责静态文件与负载均衡各司其职才是正规军的玩法。4.5 数据库连接超时与连接池配置Spring Boot默认使用HikariCP连接池性能不错但默认配置对生产环境不够友好。常见问题就是服务器空闲后MySQL主动断开连接连接池里的连接没有及时清理请求报错Communications link failure。推荐一套经过验证的配置spring: datasource: hikari: maximum-pool-size: 20 # 最大连接数 minimum-idle: 5 # 最小空闲连接 connection-timeout: 30000 # 获取连接超时 idle-timeout: 600000 # 空闲超时 max-lifetime: 1800000 # 连接最大生命周期 connection-test-query: SELECT 1 # 心跳检测其中max-lifetime要小于MySQL的wait_timeout否则MySQL断开连接后连接池里的连接还是旧的就会报错。另外connection-test-query让连接池定期发心跳包检测连接是否还活着这是解决突然断连的常规手段。5. 部署方案与运维经验部署这块很多人觉得无所谓实际上部署方式直接决定了你后期维护的心态。推荐一个我用的最顺手的方案Spring Boot打包成可执行JAR包配合Nginx反向代理部署在Linux服务器上。这套方案支持直接双击执行不依赖外部Tomcat容器有Spring Boot版本升级时直接替换JAR包重启即可。部署流程如下本机执行mvn clean package -DskipTests打好JAR包把JAR包通过scp命令或宝塔面板上传到服务器服务器时区改为Asia/Shanghai保证MySQL和应用的时区一致执行nohup java -jar travel-platform.jar --spring.profiles.activeprod app.log 21 启动应用配置Nginx把域名或IP的请求代理到http://127.0.0.1:8080。配置文件建议拆分三份application-dev.yml用于本地开发、application-prod.yml用于线上运行、application.yml放公共配置。启动时通过--spring.profiles.active指定环境避免打包时手工改数据库连接。数据库备份提醒一句内容类平台的数据库是命根子。写个最粗粒度的定时任务脚本每天凌晨用mysqldump全量备份至少保留最近七天的备份文件。不需要搞多复杂的备份策略定时执行加异地存储已经能挡住绝大多数事故。6. 写完这个项目我踩过的坑和你分享一下个人开发这类项目过程中最大的一个认知变化是技术不难难的是把细节想全。JWT怎么签发、SQL怎么写这些上网搜都有答案真正拉开差距的是那些“再想一想”的地方。举例来说发布攻略时如果没有做幂等处理用户在弱网状态下双击提交按钮就会在数据库里产生两条一模一样的攻略。我第一次遇到这个问题时第一反应竟然是去改前端的按钮禁用后来才发现后端的并发控制才是根本解法。前端按钮禁用只能挡正常用户挡不住连点、重复提交这类情况。后端加一个简单的重复判断比如同一用户在同一分钟内发布了标题完全相同的攻略就拒绝这种业务层面的兜底逻辑才是真正保护数据的防线。图片上传的规范性也是一个容易偷懒的地方。如果直接保存用户上传的原始文件名文件名里带中文和特殊字符线上环境很容易出现部分图片访问失败。后来统一重命名后再存这类问题就没再犯过。还有一个关于监控的建议上线后至少有基本的日志体系。Spring Boot默认的logback把日志按天切分保存最近三十天。线上出了问题连日志都没有排查靠猜的话一次事故消耗的精力足够多做半个项目了。如果你也想做一个旅游攻略分享平台或者正在准备类似的Spring Boot全栈项目我的建议是先在纸上把业务对象画清楚再写代码。数据库设计阶段多想十分钟后面编码阶段能省下十小时。项目结构保持简洁功能做深做扎实比铺开一个大而全的架子有意义得多。这个平台后续还可以继续扩展智能推荐、行程规划、用户等级体系等方向但那些都是后话。“能跑起来、能维护、能上线”才是第一优先级希望这篇分享对你有所启发。
返回列表