尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零实现洗衣店订单管理系统:SpringBoot+Vue前后端分离实战

从零实现洗衣店订单管理系统:SpringBoot+Vue前后端分离实战 说实话接这个项目之前我也没想到一个看似不起眼的洗衣店订单管理做起来居然能把前后端分离这套技术栈玩得这么完整。从用户需求梳理、数据库设计到SpringBoot后端接口、Vue前端交互再到最后的部署上线每一个环节都有值得展开的细节。这篇文章就从零到一拆解这套洗衣店订单管理系统的完整实现过程适合正在学前后端分离项目实战、准备课设或毕设、或者想承接小商户信息化需求的开发者参考。核心关键词是前后端分离、SpringBoot、Vue、MyBatis、MySQL下文所有代码和配置都基于这套技术栈。1. 从需求倒推系统设计洗衣店业务场景的数字化痛点1.1 洗衣店的日常困境与系统边界做这类小行业管理系统的第一课不是打开IDE写代码而是先搞清楚业务到底要解决什么问题。我调研过几个社区洗衣店他们的日常大概是这样的顾客送衣服过来店员手写一张票据记上姓名、电话、衣物品类、洗涤要求、约定取衣时间衣服洗完后店员翻本子找电话一个个打电话通知顾客来取衣服再翻本子核对。听起来很原始但这就是很多中小洗衣店的真实状态。这套系统要解决的痛点其实很明确会员信息散落在一堆纸质票据里订单状态黑盒化——顾客不知道衣服洗到哪一步老板也说不清今天积压了多少订单、哪些超期未取、哪些衣物容易丢失。再往深一层还牵扯到结算问题预存款余额、单次消费折扣、月结客户这些东西靠手工计算既慢又容易扯皮。所以系统边界就划出来了会员管理含储值余额、订单全流程管理从接单到取走、订单状态流转、简单的统计报表。洗衣项目本身是标准化的不需要像进销存那样搞复杂的商品SKU订单明细挂在订单下面即可。这个边界控制很重要很多同学做管理系统一上来就想把进销存、财务、员工考勤全塞进去最后每个模块都是半成品。边界越小完成度越高对学习前后端分离的实战价值反而更大。1.2 前后端分离架构如何匹配这个场景先解释一下什么是前后端分离用一个生活化的类比后端是餐厅的后厨对外提供菜单接口只管把菜做好端到传菜口前端是前厅服务员管顾客点单、摆盘、上菜顾客面前的一切交互都由前厅负责。两者通过一份约定好的API菜谱JSON接口规范协作部署时可以分开放在不同的服务器上各自伸缩。洗衣店订单管理系统选前后端分离架构有一个天然的理由——使用场景很杂。店里的前台电脑要用老板手机浏览器要看统计甚至以后可能做一个小程序给顾客自助下单。如果用传统的Thymeleaf、JSP模板渲染页面逻辑和后端耦合在一起换一个客户端就意味着后端也要跟着大改。前后端分离后后端就是一套纯API谁要来调都行前端项目独立维护改动UI不影响接口逻辑。这正是这套技术栈对未来扩展友好性的关键所在。技术选型方面SpringBoot负责提供RESTful API内置Tomcat省去一大堆XML配置Vue负责前端交互用轻量级的Vue Router管理页面路由用生命周期钩子处理数据加载MyBatis负责数据库访问XML里写SQL灵活好调优MySQL做数据持久化。这是一套非常经典的组合资料多、社区成熟、遇到问题搜一下基本都有答案。1.3 技术栈选型的理由可能有人会问为什么持久层不选Spring Data JPA或者MyBatis-Plus这里有几个实际考量。MyBatis的优势在于SQL完全可控订单管理系统的很多接口是多表关联加多条件组合查询比如查询所有手机号包含138、状态为洗涤中、下单时间在最近七天的订单这种动态SQL用MyBatis的where和if标签组合起来非常直观。另外这个系统里还有不少统计报表类的SQL按状态分组计数、按月汇总营业额直接手写SQL比JPA派生的查询方法更清晰。至于MyBatis-Plus它确实能简化单表CRUD但既然要做完整的项目实战我建议先完整掌握原生MyBatis的XML写法再谈增强工具。前端选Vue而不是React也是考虑到目标场景——中小型管理后台。Vue的单文件组件、指令系统、计算属性对这类表单密集型的页面非常友好模板语法符合传统HTML的习惯上手门槛比React的JSX低不少。Vue Router用动态路由配合导航守卫做登录拦截也很顺手。2. 数据库设计订单状态流转才是整个系统的灵魂2.1 核心表结构拆解这个系统一共四张核心表顾客信息表、洗衣项目表、订单主表、订单明细表。还附带一张管理员表用来登录后台如果需要记录操作日志可以再加一张日志表但核心业务就是这四张。先看顾客表的字段设计。id为主键自增name存姓名phone存手机号并建立普通索引因为店员查会员时最常见的动作就是输手机号。balance存储值余额类型用DECIMAL(10,2)。这里必须强调一个原则涉及金额的字段绝对不用FLOAT或DOUBLE因为浮点数的二进制表示会产生精度误差0.1加0.2会得到0.30000000000000004。DECIMAL是精确计算虽然存储空间大一点但做余额扣减、订单结算时不会有莫名其妙的尾差。level字段存会员等级可以用来映射不同的折扣率。还要加一个create_time记录注册时间。洗衣项目表就比较简单item_name存项目名称单洗、干洗、熨烫、加急等unit_price存标准单价category做项目分类。这张表的用途是给前台开单时选择洗衣项目自动带出单价。订单主表是整个系统的核心字段明显要丰富得多。除了常规的id、order_no、member_id和customer_name、customer_phone这里注意订单要冗余姓名和手机号不能只存member_id因为可能有人不办会员也来洗衣服还包括total_amount订单总额DECIMAL(10,2)discount_amount折扣金额paid_amount实际应收金额status订单状态用TINYINT整数表示receive_time接单时间promised_time约定取衣时间finish_time洗涤完成时间pickup_time实际取走时间remark备注比如衣物瑕疵、特殊洗护要求订单明细表挂在主表之下每一行对应一件衣物或一个洗衣项目order_id关联主表item_name冗余快照即使洗衣项目表以后改了名称订单里还能看到当时洗的是什么quantity为件数unit_price和amount也冗余单价和金额。为什么订单明细要做冗余因为订单是历史事实洗衣项目表是可变资料如果项目涨价了、改名了不能影响已下单的记录。这是订单类系统一个很容易忽略的设计原则。2.2 订单状态字段用状态机管理洗衣全流程订单的status字段是整个系统业务逻辑最集中的地方。洗衣业务流程可以拆成几个明确的状态节点1已接单→ 2洗涤中→ 3已完成待领取→ 4已取走加上一个可选的0已取消状态。用整数存储而不是字符串理由很务实存储空间小、查询走索引快、前端用映射表显示中文即可。前端页面可以定义一个字典对象{ 0: 已取消, 1: 已接单, 2: 洗涤中, 3: 已完成待领取, 4: 已取走 }接口里返回数字前端翻译成文字后端与前端完全解耦。这里我强烈建议在后端把状态流转做成一组受控的操作而不是暴露一个通用的修改状态接口让前端随便传值。比如定义这些动作receiveOrder(orderId)接单状态 0/新增 → 1startWash(orderId)开始洗涤状态 1 → 2completeOrder(orderId)洗涤完成状态 2 → 3pickupOrder(orderId)顾客取走并结算状态 3 → 4cancelOrder(orderId)取消订单状态 1 → 0每个动作在Service层校验当前状态是否合法。比如取走这个动作只有在状态为3已完成待领取时才能执行。如果业务量小也可以一个接口传status加action区分但状态机的方法调用更直观别人接手代码看一眼方法名就知道业务语义。2.3 关键的字段类型与索引取舍设计表时还有一些容易踩坑的细节这里集中整理。order_no订单号建议用字符串格式类似20250607001日期三位流水生成逻辑在后端用SimpleDateFormat加每日自增序号。不要用数据库自增ID直接给顾客看因为顾客拿到一张写着订单号10086的单据他会觉得你们一天才做几十单而一个带日期的单号显得规范也方便按日检索。promised_time这种时间字段用DATETIME不要用TIMESTAMP。TIMESTAMP的范围上限是2038年虽然对我们来说还很远但它还受MySQL时区设置影响在跨时区部署时会有意想不到的偏移问题。DATETIME则是纯记录不依赖时区适合业务时间。索引方面除了主键需要关注三类查询场景店员查订单时输入手机号要在这张表建一个idx_phone普通索引老板看当前所有洗涤中的订单对应status字段但单独给status建索引收益一般因为状态值就那么几个选择性太低最常用的组合查询是按日期范围查订单或者按手机号查历史订单推荐建联合索引idx_status_create_timestatus, create_time和idx_phone_create_timephone, create_time。这个属于MyBatis进阶中常见的索引优化手段也是面试时经常被追问的点。3. SpringBoot后端落地从登录鉴权到订单状态机的实现3.1 工程分层与核心依赖后端工程我按经典的四层结构来拆Controller接收请求、参数校验、返回统一响应、Service业务逻辑、事务控制、Mapper数据库访问接口、Entity实体类。另加一个common包放统一返回结果、异常处理、JWT工具类一个config包放拦截器配置。pom.xml核心依赖如下dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency这里有一个版本上的坑spring-boot-starter-parent如果你用了SpringBoot 2.7以上版本mysql-connector-j的groupId已经从mysql改成了com.mysqlartifactId也从mysql-connector-java简化成了mysql-connector-j。如果照着老教程引入mysql:mysql-connector-java:8.0.33在SpringBoot 3.x里是启动不起来的。另外SpringBoot 3.0对Java版本要求最低17很多人SpringBoot版本一升编译器设置还是Java 8启动就会报UnsupportedClassVersionError这两个问题在部署篇再细说。配置文件application.yml的核心内容server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/laundry_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.laundry.entity configuration: map-underscore-to-camel-case: truemap-underscore-to-camel-case: true这个配置一定要开。因为数据库字段是create_time下划线风格Java实体类属性是createTime驼峰风格开了这个映射后MyBatis自动把下划线转驼峰省去在XML里给每个字段写resultMap的重复劳动。实测下来这个配置能省下大约一半的Mapper工作量。3.2 基于JWT的登录鉴权洗衣店管理系统虽然是小项目但登录鉴权不能省。管理员账号固定写在数据库密码用BCrypt加密存储不允许明文。登录接口逻辑查数据库比对密码比对通过后用JWT生成一个带有效期建议2小时的token返回给前端。后续前端每次请求都在Authorization请求头带上这个token后端用一个登录拦截器统一校验。拦截器实现大致是public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 if (request.getRequestURI().contains(/auth/login)) { return true; } String token request.getHeader(Authorization); if (token null || !JwtUtil.validateToken(token)) { response.setStatus(401); response.getWriter().write(未登录或登录已过期); return false; } return true; } }注册拦截器时要把静态资源路径排除掉否则前端部署在同一个后端服务时访问CSS、JS文件也会被拦。这个细节我在部署篇还会提到前后端打包合并后有很多人遇到页面白屏但API正常的问题根源就在这。3.3 订单接口与状态流转的核心实现订单相关的接口是纯RESTful风格设计POST /api/orders创建订单同时插入主表和明细表开启事务GET /api/orders分页条件查询支持状态、手机号、日期范围参数GET /api/orders/{id}订单详情返回主表信息加明细列表POST /api/orders/{id}/pickup取走并结算扣减会员余额PUT /api/orders/{id}/status状态流转的通用入口来看最核心的状态流转Service方法以completeOrder为例Service public class OrderService { Transactional public void completeOrder(Long orderId) { Order order orderMapper.selectById(orderId); if (order null) { throw new BusinessException(订单不存在); } // 状态机校验只有洗涤中的订单才能完成 if (order.getStatus() ! 2) { throw new BusinessException(当前状态不允许执行此操作); } Order update new Order(); update.setId(orderId); update.setStatus(3); update.setFinishTime(new Date()); orderMapper.updateByIdSelective(update); } }注意几个细节。第一Transactional加在Service方法上创建订单这种涉及两张表写入的操作必须加事务否则明细插入失败主表却写入成功会造成脏数据。第二更新操作用了按主键选择性更新只更新非空字段的方式这里其实隐藏着一个经典坑如果直接把查出来的order实体改完再update整个实体会把createTime等字段也更新掉甚至把原来时间覆盖成null所以更新时要new一个只带要改字段的对象。第三并发问题如果两个窗口同时操作同一个订单怎么办严格的方案是更新语句里加status2条件做乐观锁UPDATE orders SET status3, finish_timeNOW() WHERE id#{id} AND status2返回受影响行数为1才说明状态流转成功否则说明订单状态已经被别人改了。这个小店场景并发量不高但理解这个写法以后做高并发系统的时候思路是相通的。3.4 MyBatis动态SQL多条件组合查询的正确打开方式订单列表查询是洗衣店最常用的功能也是MyBatis动态SQL的经典应用场景。后台要支持状态手机号时间范围分页任意组合查询如果给每个组合写一条SQL那组合爆炸根本维护不了。用where加if标签动态拼接一条SQL搞定select idselectOrderPage resultTypecom.laundry.entity.Order SELECT id, order_no, customer_name, customer_phone, total_amount, paid_amount, status, create_time, finish_time, pickup_time FROM orders where if teststatus ! null AND status #{status} /if if testphone ! null and phone ! AND customer_phone LIKE CONCAT(%, #{phone}, %) /if if teststartTime ! null AND create_time gt; #{startTime} /if if testendTime ! null AND create_time lt; #{endTime} /if /where ORDER BY create_time DESC LIMIT #{offset}, #{pageSize} /select这里的where标签有个好处如果所有条件都不成立它不会生成WHERE关键词SQL不会语法错误如果第一个条件成立它会自动去掉多余的AND。gt;是XML里大于号的转义写法用直接写在XML里会解析报错。分页这里用了最朴素的LIMIT #{offset}, #{pageSize}前端传入当前页码和每页条数后端计算offset。这个做法在小项目里够用也不引入PageHelper等插件降低学习负担。如果要统计总数另外写一个selectOrderCount同样用where拼接条件返回COUNT(*)。两个查询条件保持一致这里就暴露一个问题动态条件如果写在两处改一个忘记改另一个总数和列表就对不上了。解决方案是把条件封装成一个查询参数对象QueryParam两次查询都传同一个对象从源头保证一致。4. Vue前端开发与联调让订单状态看得见4.1 前端工程结构与页面规划前端我用Vue CLI构建的Vue 2版本项目如果你愿意用Vue 3 Vite做也可以但后文配置以Vue 2为主因为配套的Element UI组件库对管理系统太实用了。目录规划src/ api/ # 接口请求模块按业务拆分 order.js member.js auth.js assets/ components/ router/ index.js # 路由配置 store/ index.js # 登录状态管理Vuex views/ Login.vue OrderList.vue OrderDetail.vue OrderCreate.vue MemberList.vue Statistics.vue Layout.vue # 整体布局左侧菜单右侧内容区页面规划跟着业务走登录页、订单列表核心页面、创建订单、订单详情、会员列表、统计页。整体布局用左侧菜单栏顶部显示管理员信息和退出按钮内容区放路由视图。Element UI的el-table、el-form、el-dialog组件能大幅提升开发效率这个选型对管理系统非常合适不用从零写表格和表单。4.2 axios封装与跨域处理前后端联调的钥匙前后端联调第一个拦路虎是跨域。浏览器同源策略规定前端在http://localhost:8081访问后端的http://localhost:8080接口会被浏览器拦下来。开发环境最优雅的方案是配置Vue CLI的devServer代理前端把请求发给同源的/api路径由开发服务器转发到后端// vue.config.js module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端代码里请求路径写成/api/orders开发时由Webpack DevServer转发过去浏览器层面看不到跨域请求从根源上避开跨域问题。axios实例统一封装也是必备操作import axios from axios import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动带上token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) // 响应拦截器统一处理错误码 service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } ) export default service这个封装的核心价值是所有请求自动携带token遇到401过期自动跳登录页业务组件里再也不用重复写token取用和错误处理逻辑。写订单列表页时组件里只需要async loadOrders() { const res await getOrderList({ page: this.currentPage, pageSize: 10, status: this.filterStatus }) this.tableData res.data.records this.total res.data.total }代码干净很多这也是前后端分离项目里常见的axios二次封装模式面试时也是一个高频考点。4.3 订单状态流转的前端交互设计订单列表页要把状态字段渲染成标签Element UI的el-tag配合状态字典el-table-column label订单状态 width120 template slot-scopescope el-tag :typestatusTypeMap[scope.row.status] {{ statusTextMap[scope.row.status] }} /el-tag /template /el-table-column创建订单页面是表单交互的核心场景选择会员输入手机号自动带出会员姓名和余额、添加衣物明细多行每行选择洗衣项目后自动计算金额、显示合计金额、填写备注。这里的计算属性用得很顺手computed: { totalAmount() { return this.items.reduce((sum, item) sum item.quantity * item.unitPrice, 0) } }状态流转按钮的做法要比直接在表格里放el-button更好——用下拉菜单按当前状态显示可执行的操作。比如状态为洗涤中的行下拉菜单只显示洗涤完成状态为已完成待领取的行显示确认取走和取消订单。这样前端交互就嵌入了状态机的约束用户不会误操作。实测下来这种设计比把所有按钮一次性摆出来体验好很多哪怕后端已经做了状态校验用户在界面上根本接触不到非法操作减少报错给他带来的困惑。4.4 路由守卫与权限控制路由配置里把需要登录才能访问的页面包在Layout组件下登录页作为独立路由。用Vue Router的全局前置守卫控制访问router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path /login) { next() } else if (!token) { next(/login) } else { next() } })这个逻辑通俗地讲进任何页面先看有没有门票token。没有门票一律赶去登录页已经登录了想再看登录页随便看也拦不住。还可以加一个进入逻辑登录状态下访问登录页自动跳回首页但这属于体验优化不做也不影响功能。洗衣店这个场景权限比较简单一个管理员角色就够了。如果要做多角色老板、前台店员可以在token里加上角色信息路由守卫里校验角色权限再配合Vue Router动态路由实现菜单级控制。动态路由这块涉及登录后根据角色addRoutes属于Vue Router进阶玩法了后续扩展时可以考虑。5. 部署实操与踩坑记录从本地到可访问的完整路径5.1 启动步骤与验证清单整套系统在本地跑起来按下面这个顺序操作基本不会乱安装MySQL版本选5.7或8.0都可。注意MySQL 8.0默认字符集是utf8mb4不用额外配置就能存中文如果手动建库命令CREATE DATABASE laundry_db DEFAULT CHARACTER SET utf8mb4;执行初始化SQL建表灌入测试数据包括一个初始管理员账号。后端启动用IDEA打开SpringBoot工程等待Maven下载完依赖修改application.yml里的数据库账号密码运行LaundryApplication主类。看到Started LaundryApplication日志就说明启动成功。前端启动npm install安装依赖npm run serve启动开发服务器访问http://localhost:8081。验证清单建议按顺序过一遍用管理员账号登录能进首页、创建一个带会员的订单、把订单状态从已接单逐步流转到已取走、订单列表能按状态筛选出刚才的数据、会员余额在取走结算后正确扣减。这条链路走通了说明前后端联调成功业务逻辑完整。5.2 MySQL连接的那些坑这一节全是实战经验每个坑我都亲自踩过值得细看。时区问题MySQL 8.x 连接串如果不加serverTimezoneAsia/Shanghai大概率报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这是因为新版MySQL驱动对时区更严格。解决就是URL里显式指定serverTimezoneAsia/Shanghai。SSL报错useSSLfalse要加上否则驱动会尝试SSL连接控制台刷一堆警告。虽然5.7版本默认支持useSSLfalse但8.0驱动更啰嗦。驱动类名变化5.x用的com.mysql.jdbc.Driver8.x要用com.mysql.cj.jdbc.Driver。这个写错最常见的报错是ClassNotFoundException对照版本改一下就好。Navicat连接不上MySQL 8.0默认用了caching_sha2_password认证插件老版本Navicat连不上。解决要么把用户改成mysql_native_password要么升级Navicat版本。这一点在部署到客户机器时很常见值得记下。utf8mb4 vs utf8如果建表默认用了utf8存emoji表情或者生僻字会报错Incorrect string value。统一使用utf8mb4它是utf8的超集兼容性最好。5.3 生产环境部署方式对比分开部署还是打包合并本地开发完真正要交付给洗衣店老板得考虑怎么部署。两条主流路线各有适用场景。路线一前后端完全分离部署。前端npm run build产出dist静态目录后端打jar包用Nginx把前端静态页面和/api请求分别指向对应后端服务server { listen 80; # 前端静态资源 location / { root /var/www/laundry/dist; index index.html; try_files $uri $uri/ /index.html; } # 后端API反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这种方案的优点是前后端各自维护、扩展灵活。缺点是要多管理一个Nginx进程对小店来说运维成本偏高。路线二前端打包塞进SpringBoot。这正是最近很流行的vue打包放进springboot做法把dist目录复制到SpringBoot的src/main/resources/static下然后直接打成一个jar包java -jar app.jar一条命令搞定。好处是部署极其简单一台服务器、一个端口、一个进程坏处是前后端耦合在了一个包里后续前端单独改动就要重新打整个jar。如果按路线二做有个坑必须先说前后端路径合并后SpringBoot默认会把/请求转发到static/index.html但是Vue Router如果用的是history模式刷新一个非根路径比如/orders时SpringBoot会直接返回404。解决有两招第一招后端加一个视图控制器转发到index.html把所有未匹配路径都指过去第二招前端路由用hash模式URL带#比如http://xxx/#/orders浏览器请求的始终是根路径不需要后端配合。小项目图省事直接用hash模式最稳只是URL丑一点。我个人建议按路线一的Nginx方案交付理由是洗衣店后期几乎一定会加需求小程序查单、会员短信提醒之类的前后端分开部署结构清晰得多加一个前端服务不影响后端API。6. 项目复盘与扩展思路这套框架还能怎么玩6.1 可以从哪些方向扩展这套系统的核心架构跑通后往上加功能其实是在原有骨架上贴肉。我给几个真实可落地的扩展方向消息通知模块。订单洗涤完成后自动发短信通知顾客取衣。实现思路是在状态变为已完成待领取时触发一个异步任务调用短信服务商API。注意异步处理用Spring Async或消息队列不能阻塞主业务流程。热度词里提到的springboot整合activemq就是一个让消息通知和主流程解耦的思路。文件存储与上传。给订单增加衣物质检照片上传功能顾客送衣时拍几张照片存档取衣时核对避免纠纷。这就要用到MinIO这样的对象存储服务接一个minio加入到springboot的配置上传接口返回文件URL存入订单表。这非常符合洗衣店实际需求也是加分项。统计报表增强。目前的统计可能只是按日累计营业额、按状态计数。扩展方向是按时段对比营收、分析滞销项目和频次甚至用图表库ECharts做可视化大屏放在店里电视上滚动展示。这部分对前端提升很大。MyBatis相关进阶如果你的项目访问量上来缓存就值得考虑了。MyBatis一级缓存是SqlSession级别的默认开启二级缓存是跨SqlSession的要在Mapper XML中配置cache。这里提醒一句二级缓存在多表关联场景下特别容易出脏数据订单表这种高频更新业务建议直接关掉否则查出来的数据可能是别人改之前的旧状态这个我在做完多表联查后深有体会。6.2 个人体验这套系统作为学习案例的独特价值这套洗衣店订单管理系统体量恰到好处。它比那些纯粹的增删改查Demo多了订单状态机、会员余额结算这类真实业务逻辑做起来会逼你想清楚为什么要这样设计但又不至于像电商系统那样要处理复杂的库存、并发秒杀、支付回调对新手友好得多。从技术覆盖面上说它几乎横跨了前后端分离项目的所有关键环节JWT鉴权、动态SQL多条件查询、事务处理、跨域联调、打包部署、Nginx配置、状态机设计。任何一个环节单独拿出来都能对应到企业真实开发中的常见问题。做完这个项目再去看别的管理系统比如热词里提到的校园考勤系统架构上基本是相通的只是业务表不同、状态流不同罢了。最后分享一个我在实际部署中体会最深的小技巧联调阶段一定要学会看浏览器Network面板和浏览器控制台。前后端分离项目出问题90%都能在这两个地方找到线索——请求有没有发出去、响应状态码是什么、后端报错堆栈打印了什么。别一上来就猜是跨域问题还是后端问题看数据说话。我刚做前后端联调时习惯先开后端控制台确认SQL执行情况再回前端界面看交互反馈交叉验证定位问题比盲目改代码高效得多。这个项目做下来最大的感受是管理系统开发不等于机械地写代码真正花时间的是把业务流程理清楚、把状态流转设计对。架构和技术选型都是为业务服务的洗衣店这件小事里藏着的那些设计原则挪到任何一个行业管理系统里都通用。
返回列表