ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue+MyBatis+MySQL构建乐享田园认养农业平台

SpringBoot+Vue+MyBatis+MySQL构建乐享田园认养农业平台 1. 乐享田园系统到底做什么先聊聊这套系统的边界和业务模型先给没接触过农业信息化项目的朋友说清楚所谓“乐享田园系统管理系统”本质上是一套面向田园综合体、休闲农庄、认养农业场景的数字化管理平台。它的核心用户不是写代码的人而是农场运营者、认养会员以及少量的管理人员。简单来说你划出一块地把它按单元拆成认养地块会员线上认购后台记录种植过程、成熟采收、配送订单整个过程在系统里闭环。这套系统在当前乡村振兴和田园经济背景下属于典型的“农业互联网”项目既有传统管理系统的订单、会员、库存逻辑又带了农业特有的地块、种植记录、认养周期等业务概念。适合参考这套系统的人主要是这么几类一是准备做毕业设计或课设的同学SpringBoot Vue MyBatis MySQL这套组合既不过时又能在答辩时把业务亮点讲清楚二是中小型农庄或田园综合体的运营者想落地一套轻量级管理后台不想花大钱买重型ERP三是刚从前端或纯业务转后端开发的工程师拿这个项目练手理解订单状态机、权限体系、多表联查这些高频面试点。我在做这套系统时最深的体会是农业类管理系统的业务复杂度其实比表面看起来高不少。单纯做增删改查三天就能写完但一旦涉及“认养地块—种植任务—生长记录—订单配送”这样的多实体流转数据模型和状态管理就成了整个项目的硬骨头。1.1 需求拆解认养农业怎么把线下业务搬进系统先把线下场景捋一遍。一个典型的田园综合体运营方手上有几十亩地被划分成若干认养单元每个单元可能种的是蔬菜、草莓或者水稻。用户花一笔费用认养某个地块或某几棵果树认养期内农场按季节安排种植计划定期拍照片、记录生长情况反馈给用户。蔬菜成熟后可以选择配送到家也可以选择线下自提。另外很多农场还附带农产品商城、线下活动预约、会员充值等衍生业务。落到系统里我拆出了六个核心模块用户与会员管理区分管理端、农场员工端、会员端三种角色会员有等级和积分。地块管理维护地块编号、面积、种植品种、当前状态空闲、已认养、种植中、休整中。认养订单用户认购地块或单品生成订单记录认养周期、金额、支付状态。种植任务与生长记录农技员按计划填写种植记录上传照片形成一条可回溯的时间线。商品商城销售农产品、套餐支持购物车、下单、配送信息维护。数据统计与首页看板统计认养数量、订单金额、地块利用率。很多初学者容易犯的错是把地块当成普通商品表来设计。比如直接在商品表里加一个“字段类型地块”的标记这样短期内没问题但后续一旦要按面积、位置、种植作物筛选地块或者做地块空闲排期整个查询就会变得非常别扭。正确做法是把“地块资源”和“认养商品”分开建模商品只是地块的售卖包装二者通过关联字段联通。1.2 角色权限为什么不只是简单区分管理员和用户田园系统的角色权限比一般后台管理系统要多一层“租户”视角。我最终设计了三套角色体系系统管理员负责账号和全局配置、农场运营负责地块、订单、商品、会员只有认养记录、我的地块、订单查询的权限。权限这块我强烈建议用Spring Security JWT而不是自己手写拦截器。原因很简单手写拦截器判断登录状态容易但要处理“会员只能看自己的认养记录”“运营只能改自己管辖区块”这类数据级权限代码量会成倍增加。Spring Security配合注解PreAuthorize可以做到方法级控制数据级权限在Service层统一校验结构上更干净。我遇到的一个实际坑是Shiro还是Spring Security的选择。就这个项目体量来说两者都行但如果你是边做边学的状态Spring Security踩坑资料更多社区解决方案也多遇到CORS跨域时和SpringBoot集成更自然。最终我选了Spring Security整个认证流程大概省了两天时间。2. 技术选型为什么SpringBootVueMyBatisMySQL是这套系统的最优解技术栈的选择决定了项目开发节奏和后期维护成本。这套系统用到的四个核心件逐一拆开看SpringBoot负责后端基础框架自动配置省掉大量XML配置内嵌Tomcat让本地开发和测试极其方便。我这里用SpringBoot 2.7.xJDK用1.8。为什么不用SpringBoot 3因为3.0强制要求JDK17而且javax包迁移到jakarta很多老版本MyBatis插件会不兼容新手如果跟着旧教程走很容易卡在包名报错上。Vue负责前端界面。我选的是Vue2 Element-UI原因很现实Vue3 Element Plus虽然新但市面上很多成熟的管理系统模板还是基于Vue2。如果你的团队没有前端强手Vue2生态的组件示例更多开发时搜索“Element-UI 表格 编辑”基本都能直接抄作业。MyBatis负责数据访问。相比JPAMyBatis的SQL可控性更好特别适合多表关联查询和复杂统计场景。田园系统里“认养订单表 join 地块表 join 用户表 join 种植记录表”这类SQL很多用MyBatis写resultMap映射比Hibernate的实体关系映射更直观。MySQL负责数据存储。5.7或8.0都行我建议直接用8.0字符集选utf8mb4排序规则utf8mb4_general_ci或unicode_ci。2.1 前后端分离的工程结构项目物理结构我用了标准的前后端分离方式backendSpringBoot工程Maven构建端口设为8080。controller接收前端请求做参数校验和响应封装。service业务逻辑层订单状态流转、认养地块分配都在这里。mapperMyBatis接口层对应resources/mapper下的XML文件。entity数据库实体类。dto前端交互数据的传输对象避免实体直接暴露。config跨域配置、安全配置、MyBatis配置。common统一返回类、异常处理、常量定义。frontendVue工程Vite或Webpack构建。src/api按模块封装axios请求。src/router前端路由。src/storeVuex状态管理保存用户信息和权限标识。src/views按模块划分的页面组件。src/utils工具函数如日期格式化、文件下载。这套结构的最大好处就是“职责单一”。我见过很多半路出家的项目Service层写了上千行一个方法里又处理订单又写日志又更新库存出了问题只能靠print调试。拆成controller-service-mapper三层后每一层都能独立测试。前端同理api层封装请求和业务层解耦页面组件只关心渲染和交互改某个页面不会牵一发动全身。2.2 后端核心依赖与初始配置pom.xml里需要引入的依赖我直接列一个清单spring-boot-starter-webspring-boot-starter-securitymybatis-spring-boot-starter注意版本要和SpringBoot匹配用的2.3.xmysql-connector-javalombok实体类用Data简化spring-boot-starter-validation校验参数jjwtJWT生成和解析hutool工具类生成随机编号和日期处理可选application.yml里几个关键配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/campus?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.entity configuration: map-underscore-to-camel-case: true这里特别强调三个点第一url里的serverTimezone必须设置。MySQL 8.0默认时区是UTC不设置的话查询出来的时间会比北京时间慢8小时前端显示会非常奇怪。第二map-underscore-to-camel-case一定要打开。数据库字段是order_statusJava属性是orderStatus打开这个配置MyBatis自动映射不然每个字段都要写resultMap工作量巨大。第三spring.datasource和druid数据源别混用。很多人喜欢引入druid-spring-boot-starter但如果是springboot 2.7 druid 1.2.x配置项写法有差异容易导致启动时“不识别前缀”的报错。我只用HikariCPSpringBoot默认自带性能稳定不需要额外配置。2.3 MyBatis与MySQL搭配的几个细节用MyBatis操作MySQL核心就是把SQL写好映射配好。做这个项目时我总结出了几条实效经验多表查询别偷懒能join就join别在代码里做两层for循环过滤。一但数据量过千内存过滤的性能灾难立即显现。动态SQL用 和标签这俩能自动处理多余的AND和逗号拼条件时不至于报语法错误。插入数据时主键用数据库自增但业务编号如认养订单编号最好在代码里生成。我用的是“YYMMDD随机四位”比如RY202501150001这样前端展示和客户沟通都更方便。查询列表建议加limit分页别一次性把所有记录甩给前端。配合PageHelper插件一行代码完成分页查询还是值得引入的。实际开发中还有一个常被忽略的问题MySQL的int类型长度。很多小伙伴在设计表时会把id设成int(11)但如果你预期用户量或订单量会过十万建议直接使用bigint。因为int最大约21亿表面看着够用但一旦用到中间表统计join出来的临时表很容易超出int范围排查问题极为痛苦。这个项目里所有主键我全部用bigint一劳永逸。3. 数据库设计从认养地块到订单流转表结构一次讲透这套系统的数据库设计我花了大概两天时间反复调整。最开始按直觉建了用户表、地块表、订单表、商品表后来发现丢了很多关键字段比如认养开始时间和结束时间、生长记录对应的地块ID、配送地址快照。数据库设计如果不把业务故事讲通开发到一半就会疯狂加字段。3.1 核心表清单与实体关系梳理之后最终核心表如下表名用途关键字段sys_user系统用户管理员、运营、会员统一存储id, username, password, role, phone, avatarmember_info会员扩展信息id, user_id, level, points, balanceland_block地块信息id, block_code, name, area, crop_type, status, cover_imgland_adopt_order认养订单id, order_no, user_id, block_id, plan_id, amount, status, start_date, end_dateplant_task种植任务id, block_id, task_type, plan_date, content, operator_idgrow_record生长记录id, block_id, record_date, description, images, recorder_idproduct商品含认养套餐和普通农产品id, name, type, price, stock, image, descriptionproduct_order商城订单id, order_no, user_id, total_amount, status, address_snapshotdict_data数据字典id, dict_type, label, value, sort表之间核心关系sys_user和member_info是一对一会员资料永远和账号分开。land_block和land_adopt_order是一对多一个地块可以有多条认养记录但同一时间段只能有一个有效认养。land_adopt_order和grow_record通过block_id关联用户在前端看“我的认养地块”就是先查订单再根据block_id查生长记录。product表通过type区分“认养计划”和“普通商品”认养计划下单时联动地块分配逻辑。3.2 地块状态与认养周期的设计地块是整个系统的核心资源我给自己定了三条设计原则第一地块状态用枚举值存储不用中文。status字段是0空闲、1已认养、2种植中、3休整中。“休整中”这个状态很容易被忽略但实际农场里土地需要轮作休整不设计这个状态就无法在地块列表里表达“这块地暂时不能用”。第二认养周期必须拆成开始日期和结束日期两个字段。为什么不直接存一个小时间隔呢因为业务上要支持“一年期”这种以自然日计算的方案而且会员续费时旧的结束日期要作为新订单的开始日期精确到天更好计算。第三一个地块在同一时间段只能被一个认养订单绑定。这个约束在代码层面实现我写了一个校验方法查询该地块已有效订单中是否存在end_date 新订单start_date 且 start_date 新订单end_date的记录存在则拒绝创建。这块逻辑如果放数据库用触发器实现维护起来麻烦放在service层可以由产品经理随时调整规则。3.3 订单状态机的闭环设计订单状态是这类系统最容易写乱的地方。认养订单的状态我设计为0 待支付用户提交订单未支付1 已支付/认养生效支付成功后地块置为已认养2 配送中认养到期后订单自动生成配送单3 已完成用户收货并确认后4 已取消用户支付前取消或超时关闭5 售后中对配送的农产品不满意申请售后状态流转的约束放在service层我写了一个OrderStatusTransition工具类核心是一个Map维护“当前状态 → 允许执行的动作 → 目标状态”。这样做的好处是状态不可随意跳转比如“待支付”不能直接变成“已完成”必须经过支付动作。这个地方其实也是面试常考点订单状态机怎么设计我的回答思路就是两张表订单主表存当前状态订单流痕表存每一次状态变更的流水谁在什么时间把状态从A改成B。流痕表极大提高了问题排查效率用户说“我没收到货”你拉一下流痕就能看到他是否点了确认收货以及后台是否推送了发货记录。4. 后端核心功能实现SpringBoot里最值得细看的几个模块后端不是把所有CRUD写完就万事大吉真正花精力的是登录认证、订单流转和权限控制。这三个模块里任何一个写崩了都会直接影响前端页面的正常使用。4.1 基于JWT的登录认证完整流程登录接口的流程前端传username和password到/login接口。后端用UserDetailsService加载用户BCryptPasswordEncoder比对密码。比对成功生成JWT令牌令牌中放入userId、username、role三个核心信息有效期设置为24小时。后续请求前端在axios拦截器里给每个请求头添加Authorization: Bearer token。后端的SecurityFilterChain里配置JwtAuthenticationFilter每次请求先解析token再把用户信息设置到SecurityContextHolder。受保护接口通过注解PreAuthorize(hasRole(ADMIN))控制权限。写这个模块时我踩过一个低级但很耗时的坑Spring Security的BCryptPasswordEncoder默认加密强度是10生成的密码字符串很长我建表后手动往库里插了条测试数据用明文存密码结果登录永远失败。后来才意识到所有种子用户和初始化密码都必须通过同一个PasswordEncoder的encode方法生成。如果你是开发前期没有做数据初始化脚本直接在数据库工具里用SQL插入用户一定先用一个临时接口调encode方法把密文打印出来再插入。JWT密钥的存储也很关键别写在代码里。我建了一个application-secret.yml文件单独配置jwt.secret并且在.gitignore里排除它。否则密钥泄露出去了任何能拿到你token的人都可以伪造身份。4.2 认养订单申请与地块分配实现用户认养一块地后端实际要完成一系列操作接收请求参数包含blockId、认养套餐ID、预计开始日期。校验地块当前状态必须是空闲或休整中。校验用户在相同时间段是否已有有效认养订单防止刷单或重复认养。计算订单金额套餐表中存有单价认养时长一般为一周、一月、一季度、一年四档。创建订单记录状态设为待支付。生成订单编号。返回订单ID给前端前端跳转支付页面。支付模块在这个项目中我没有接入真实第三方支付因为大部分基于源码二次开发的场景里微信/支付宝支付都需要企业资质个人开发者很难申请。我做了一个模拟支付接口/pay/mock接收订单号直接修改订单状态为已支付同时触发“地块锁定”动作——把land_block表的status从0改成1。这样演示流程完整答辩或演示时不会被“支付失败”卡住。如果你要接真实支付建议把支付回调接口单独设计别和业务逻辑搅在一起核心是支付回调需要做幂等处理即同一个支付通知不能重复修改订单状态。最简单的方法是在回调方法里加一个Redis锁key是订单号value是随机值处理完以后删除锁且有expire时间兜底。4.3 MyBatis多表联查和动态SQL的实战写法以“查询我的认养地块列表”为例最终需要返回的数据包括地块名称、地块封面、认养开始结束日期、最新一条生长记录日期和图片、订单状态。这个接口要关联三张表land_adopt_order、land_block、grow_record。MyBatis XML里我是这么写的select idlistMyAdoptBlocks resultMapAdoptBlockResultMap SELECT o.id AS order_id, o.order_no, o.status AS order_status, o.start_date, o.end_date, b.block_code, b.name AS block_name, b.cover_img, g.record_date AS last_record_date, g.images AS last_record_images FROM land_adopt_order o INNER JOIN land_block b ON o.block_id b.id AND o.deleted 0 LEFT JOIN grow_record g ON g.id ( SELECT id FROM grow_record WHERE block_id b.id ORDER BY record_date DESC LIMIT 1 ) WHERE o.user_id #{userId} ORDER BY o.create_time DESC /select这里最核心也是最容易出错的是求“最新一条生长记录”。新手往往直接LEFT JOIN grow_record g ON g.block_id b.id这样会导致一行订单记录被拆成多行每个生长记录一行订单数据被重复展示。解决方式就是上面这种用子查询先取每块地最新记录ID再关联。这个技巧一定要记下来因为“取某分组最新一条”在几乎所有业务系统里都用得到。resultMap里需要手动配置字段映射因为查询结果用了别名Java属性的命名可能对不上。我索性把mybatis的map-underscore-to-camel-case打开查询列用snake_caseJava属性用camelCase大多数字段自动映射只有特殊情况才写resultMap。4.4 统一返回格式与全局异常处理我规定所有接口的返回结构统一为{ code: 200, message: success, data: {} }前端axios拦截器接收到非200的code统一弹出错误提示。这样有个明显的好处后端接口无论出什么错前端都只需处理一种格式不用在页面里try catch无数遍。全局异常处理用RestControllerAdvice实现针对以下几类异常分别返回参数校验异常MethodArgumentNotValidException返回400message取第一条校验失败信息。业务异常自定义BusinessException返回对应codemessage是提示文案。权限异常AccessDeniedException返回403“无权限访问”。兜底异常Exception返回500“系统繁忙请稍后重试”同时在日志里打印堆栈供排查。这里也分享一个自查心得logback日志级别务必在开发环境设置DEBUG生产环境设置INFO或WARN。我开发过程中很多奇怪的问题比如某条SQL查不出来、某次请求被拦截都是靠DEBUG日志定位的。MyBatis打印SQL配合日志是排查数据映射问题最有效的工具。5. 前端Vue实现页面交互里那些值得留意的细节Vue端我建议直接用Vue CLI或Vite创建项目引入Element-UI和axios。前端工作量主要是五块路由搭建、页面布局、请求封装、认养流程交互、数据展示。每块都有几个容易踩的坑单独讲才有价值。5.1 前端路由与动态菜单权限系统涉及三个角色菜单权限不同。管理员能看到用户管理和数据统计运营能看到地块管理、订单管理、生长记录管理会员只能看到我的认养和商城。功能上我做了动态路由核心思路登录成功后后端返回当前用户的菜单权限列表。前端将静态路由login、404、首页和动态路由分开存放。在router.beforeEach导航守卫中根据用户角色动态addRoute动态路由。页面刷新时从Vuex或localStorage重新获取路由权限再恢复动态路由。这个方案有些繁琐但对于练习项目而言很值得做一遍因为“动态权限路由”是前端面试题里的常客。实际项目中我也见过简化方案不做动态路由所有页面都注册好只是在菜单栏根据角色v-if控制显示。这种简化方案对小型内部系统是够用的但如果你的页面很多并且用户角色差异大还是推荐完整动态路由方案。5.2 图片上传与预览田园系统里生长记录和地块封面都需要上传图片。后端我写了一个通用文件上传接口接收MultipartFile保存到服务器本地或OSS。本项目没有买OSS直接保存到服务器/upload目录把访问路径映射到静态资源。前端上传组件我用Element-UI的el-upload一个关键配置是action属性指向后端上传地址name属性必须和Controller接收的参数名一致否则文件总是传不上去。我给一个简化版示例el-upload action/api/upload namefile :headersuploadHeaders :on-successhandleUploadSuccess el-button sizesmall点击上传/el-button /el-upload后端ControllerPostMapping(/api/upload) public Result upload(RequestParam(file) MultipartFile file) { String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String newName UUID.randomUUID().toString().replace(-, ) ext; // 保存到 /usr/local/upload file.transferTo(new File(/usr/local/upload/ newName)); return Result.success(/upload/ newName); }图片预览上Element-UI自带lightbox效果但要注意后端返回的图片路径如果是相对路径前端显示时需要拼上前端访问地址。我在axios响应拦截器里统一补全baseUrl这样后端路径不用存储完整域名日后迁移服务器也不用改库里的数据。5.3 认养流程的前端状态管理认养流程是前端最复杂的交互。整个页面分成三步第一步选择地块展示地块列表每块地显示图片、当前状态、认养价格。已认养地块要置灰且不可点击。第二步选择认养套餐一周、一月、一季度、一年切换时同步显示价格。第三步确认信息并提交确认认养人姓名手机号选择起始日期提交订单。这里我踩过的坑是用户选好地块后地块状态可能被其他用户实时抢走。因此在提交订单时后端必须再次校验地块是否仍可认养不能只依赖前端置灰状态。为此前端提交后要处理后端返回的“地块已被认养”提示同时刷新地块列表数据把最新状态同步过来。另一个值得注意的点提交订单时我设置了10分钟支付倒计时倒计时结束订单自动关闭。前端用setInterval轮询订单状态每30秒调用一次查询接口。这样做虽然轮询方式不够优雅但胜在实现简单稳定。如果你以后想优化可以改成WebSocket或SSE推送但作为管理系统的初版轮询完全够用数据库压力也不大。5.4 axios封装与跨域联调前端我统一封装了request.js配置baseURL为/api然后在Vue的vue.config.js里设置devServer代理把/api开头的请求转发到后端的http://localhost:8080。这样可以完美避开开发阶段的跨域问题不需要后端在Controller里加什么跨域注解。devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }注意一个细节后端接口路径如果已经是/api开头那么前端baseURL就不需要重复加/api否则请求会变成/api/api/xxx。我倒是见过很多初学者在这栽跟头。请求拦截器里统一加token、响应拦截器统一处理401跳转登录页、500弹出错误提示。这是axios三个最重要的功能点代码不多但能显著提升联调效率。6. 常见问题与排查技巧实录做这个项目的过程里我整理了一份问题速查表几乎每个都能对应到一个具体的开发踩坑经历。如果你照着源码搭环境遇到的八成问题都在下面。问题现象可能原因解决办法启动报Failed to configure a DataSourceapplication.yml没有配置数据源或配置项没生效检查spring.datasource.url、username、password是否拼写正确查询数据时日期少了8小时MySQL连接串缺少serverTimezone在url末尾加serverTimezoneAsia/Shanghai登录接口返回401但密码是对的BCrypt密文和明文不匹配重新用PasswordEncoder.encode生成密码再插入数据库列表接口很慢缺少索引或没有分页给user_id、block_id、status等字段加索引查询加limitMyBatis的resultMap映射的字段为null数据库列名和Java属性名对不上打开map-underscore-to-camel-case或检查resultMap的column前端访问接口跨域代理没配置或后端没允许CORS优先用devServer.proxy后端配置CorsFilter兜底上传文件失败提示404action地址不对或上传接口路径错误检查full请求路径确认Controller的RequestMapping完整路径部署到服务器时间不同步服务器时区不是Asia/Shanghai启动jar时加-Duser.timezoneAsia/Shanghai6.1 SpringBoot版本和JDK环境不匹配SpringBoot 2.7.x对应JDK8或JDK11JDK17也兼容但SpringBoot 3.x必须JDK17。如果是初学者按网上的老教程搭环境很容易下载了SpringBoot 3但还用JDK8一启动就报“无法加载主类”或“IllegalArgumentException”。建议直接锁版本JDK8 SpringBoot 2.7.18这是最经典稳定组合所有依赖基本都能搜到对应解决方案。如果你手里的机器只能装JDK17那就干脆用SpringBoot 3.2.x MyBatis 3.5.x MySQL驱动8.x尽量用较新教程来配。6.2 MySQL安装和字符集的坑MySQL安装这块Windows装8.0最常见的是“服务启动失败”多半是配置文件my.ini没建对。简单说MySQL 8.0安装版默认没有my.ini在C:\ProgramData\MySQL\MySQL Server 8.0\目录下手动建一个里面至少要有basedir和datadir两个路径。此外字符集务必在my.ini里写[client] default-character-setutf8mb4 [mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_general_ci不然数据库默认字符集是latin1存中文会变成问号。别问我怎么知道的我第一次建库就是因为漏了字符集配置所有中文全部成了????最后只能重建数据库。6.3 登录认证的小技巧JWT过期怎么办token有效期设置24小时但用户可能在第二天继续操作前端在axios响应拦截器里判断后端返回码为401时不是直接跳登录页而是先尝试调用刷新接口。刷新接口拿着旧的refreshToken换取新的accessToken如果refreshToken也过期了才强制跳转登录页。这样用户体验会好很多。不过这个项目里我没做refreshToken因为内部演示系统不需要长时间保持登录我仅加入了“记住我”功能——登录时勾选记住我token有效期延长到7天。6.4 MyBatis缓存问题MyBatis默认一级缓存是SqlSession级别二级缓存默认关闭。开发调试时如果开着二级缓存改了数据库数据但查询结果不变容易产生困惑。我建议在项目开发阶段把二级缓存彻底关掉在mybatis-config里设置cache-enabledfalse。等系统上线后如果数据访问压力大再按需对某些查询频率高、数据更新不频繁的表开启二级缓存。实际上这个项目我做下来感觉MySQL本地数据库查的够快了缓存优化放后面再说。7. 部署上线从本地跑通到服务器发布的完整流程本地开发完最终要把系统部署到服务器上前后端分离项目的发布流程虽然简单但也有一套固定步骤。这里分享一个我认为最省事的上线方案。7.1 后端打包与启动后端打包前先把application.yml里数据源改成服务器数据库地址再执行mvn clean package -DskipTests生成backend.jar。上传到服务器后用nohup命令启动nohup java -jar backend.jar --spring.profiles.activeprod backend.log 21 生产环境和本地不同建议新建application-prod.yml单独配置数据库、上传路径、日志级别。日志输出时用logback-spring.xml区分log目录和rolling策略防止日志文件无限增长。如果服务器内存不够JVM参数可以加-Xmx256m -Xms128m这个项目体量256m内存完全够跑。如果是256M内存的小型轻量服务器注意不要让MySQL和Java共用太多内存MySQL配置里innodb_buffer_pool_size可以调小一点。7.2 前端打包与Nginx配置前端执行npm run build生成dist目录。把dist目录整个传到服务器用Nginx做静态文件服务。server { listen 80; server_name 你的域名或IP; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /upload/ { alias /usr/local/upload/; } }这段配置解决两个关键问题第一前端路由使用history模式时用户刷新非首页路径会404用try_files把不存在路径都交给index.html处理路由就能在刷新后恢复正常第二/api/开头的请求通过Nginx反向代理到后端8080这样前端线上环境和后端接口就能同源访问不会出现跨域。7.3 上线前必须检查的清单上线前我列了一个自检清单照着走一遍基本没问题数据库备份是否完成是否在服务器上执行了最新的SQL脚本。生产环境配置文件里的数据库密码是否修改为强密码。上传目录和日志目录是否创建并授予了写权限。后端启动日志是否出现“Started Application in xxx seconds”字样。前端是否确认接口代理已改打包时baseURL是否为/api且线上Nginx已配置转发。用手机浏览器实际访问一遍确认页面渲染和响应式布局正常。测试普通用户和管理员两种角色的操作确认权限控制没有漏。把这套流程走完系统基本就稳定了。我个人实际使用中发现Nginx配置里的proxy_pass最好是带http://完整目标地址不要在末尾加 /api 之类的前缀否则后端路由匹配容易出偏差。很多教程写成proxy_pass http://127.0.0.1:8080/; 当你改造接口地址时就会懵。8. 最后分享几个实用的小技巧项目做完后如果不想让它停留在“能跑”的程度有几个提升点很值得做投入不大但收益明显。第一个建议是给页面加一个数据看板。用Vue ECharts在内页加几个统计图表认养数量趋势、地块利用率、订单金额分布。ECharts配置并不复杂最难的部分反而是统计数据接口的SQL。比如“近30天认养订单趋势”其实就一条SQLSELECT DATE(create_time) AS d, COUNT(*) AS cnt FROM land_adopt_order WHERE create_time DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY DATE(create_time) ORDER BY d有了这个看板整个系统在演示时的专业感立刻不同平时管理也确实能看出业务增长趋势。第二个建议是给所有的基础数据表加deleted逻辑删除字段。我以前一直觉得数据量小不需要逻辑删除但实际运营中用户误删除地块、运营误删订单这类情况防不胜防。加上deleted字段后所有查询条件默认加AND deleted0删除操作变成UPDATE这样即使误删也能从数据库里恢复。这个习惯让我少挨了好几次骂。第三个建议是定期备份数据库。可以用cron定时任务每天凌晨执行mysqldump0 2 * * * /usr/bin/mysqldump -uusername -ppassword campus /backup/campus_$(date \%Y\%m\%d).sql虽然看起来简单但对一个正式服务的系统来说这条命令可能是你最后的救命稻草。我做项目时常常遇到客户改完数据后悔如果没有备份就只能逐个字段手工恢复那叫一个崩溃。这套乐享田园系统从技术栈选型到具体实现整个过程做下来最大的收获不是“我会写SpringBoot了”而是我对一个完整业务系统从需求到上线有了整体认知。希望这篇文章的细节和踩坑经验能让你少走一些弯路。
返回列表