news 2026/10/9 3:22:41

Spring Boot+Vue进销存系统实战:从业务拆分到库存流水设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot+Vue进销存系统实战:从业务拆分到库存流水设计

接连做了三套进销存相关的项目之后,我得先泼一盆冷水:springboot+vue仓库进销存采购管理系统,听起来像是“一个后台管理页面再加几张表”,但当采购、销售、库存、仓库、供应商、客户这些词凑在一起时,真正难的不是写增删改查,而是如何让业务数据在多个环节流转时不错、不重、不丢。尤其是月末要对账、仓库要盘点的时候,系统里任何一个环节偷懒,最后都会变成Excel表之间对不上的痛苦。

这套项目适合谁?如果你是在校生准备做毕业设计,或者公司内部需要一个能支撑日常业务的小型管理系统,又或者你想用Spring Boot + Vue3把一套真实业务场景完整串一遍,那这篇文章的思路基本可以直接拿来用。我会从业务模块、后端骨架、数据库设计、前端页面、核心流程实现到部署踩坑,把我认为最重要、也最容易忽略的部分都展开讲一遍,不只是给你看代码,更会告诉你每个决定背后的理由。

1. 项目定位与模块设计:先想清楚“进销存”不是一堆CRUD

很多新手拿到这类需求,第一反应就是打开数据库设计工具,直接建商品表、订单表、库存表,然后开始写接口。但这样做十有八九会在开发到一半时发现:采购入库和销售出库都改了库存,月底却对不上账。所以第一步不是建表,而是把业务模块拆清楚。

1.1 主数据和业务单据要分开

进销存系统里存在两类完全不同的东西。一类是长期稳定、很少变动的基础资料,比如商品、供应商、客户、仓库、系统用户,我习惯叫它们主数据;另一类是每天都在发生、一旦发生就要影响库存和财务的凭证,比如采购订单、采购入库单、销售订单、销售出库单、库存盘点单,这些就是业务单据。

主数据通常只做增删改查和启用禁用,操作门槛低。但业务单据一旦提交,往往不能随意删除修改,只能通过“作废”“冲销”“红冲”等方式处理,目的就是保留业务痕迹。这个理念如果不在建表前想清楚,后面代码逻辑会非常别扭。

我在这个系统里把功能模块划分成下面几块:

模块核心页面关键作用
基础资料商品管理、供应商管理、客户管理、仓库管理维护所有业务流转的主体信息
采购管理采购订单、采购入库记录向谁采购、以什么价格采购、实际收到多少
销售管理销售订单、销售出库记录卖给谁、以什么价格卖、实际发出多少
库存管理库存查询、库存流水、库存调整实时反映各仓库各商品的可售数量
报表统计采购汇总、销售汇总、库存台账给老板和对账人员提供月度数据
系统管理用户、角色、权限控制不同岗位能看到的菜单和操作按钮

1.2 角色权限别一开始就做复杂

很多项目死在权限设计上。如果是小公司或毕设项目,我强烈建议先做四种角色:管理员、采购员、销售员、仓管员。权限粒度做到“菜单级+按钮级”就够用了,不需要上规则引擎。

  • 管理员:全部菜单和按钮,能看到所有单据和报表。
  • 采购员:能管理供应商、采购订单、采购入库,但看不到销售毛利。
  • 销售员:能管理客户、销售订单、销售出库,不能随便改采购价格。
  • 仓管员:只能看到商品、仓库、库存查询、入库单和出库单的执行,不能创建采购订单。

这种设计既贴合真实业务,又不会让开发周期翻倍。等系统跑起来之后,如果需要更细的数据权限(比如只能看本仓库数据),再在查询条件上增加仓库字段过滤,比一开始就铺开做要稳得多。

2. 技术选型与Spring Boot后端骨架:稳定比新功能更值钱

技术选型没有标准答案,但我要说一句大实话:做业务管理系统,稳定和生态比技术新潮更重要。你不需要在这类项目里硬上微服务、高并发中间件,一个单体应用加一台普通服务器就完全够用了。

2.1 为什么我坚持用 Spring Boot + MyBatis-Plus + MySQL

后端用Spring Boot毋庸置疑,生态成熟、部署简单、招人也好招。ORM层面我首推MyBatis-Plus,原因很直接:进销存系统里有大量条件查询、分页报表和动态SQL,MyBatis-Plus能保持SQL可控,同时自带分页插件和逻辑删除,比JPA在复杂查询上省心得多。JPA的学习曲线和隐性SQL问题,在这种业务场景下容易把人逼疯。

数据库用MySQL就够。字段名尽量统一为下划线风格,比如purchase_order_no、received_quantity,实体里用驼峰命名,MyBatis-Plus的map-underscore-to-camel-case默认开启,省去一堆映射配置。

这里必须提一下版本问题,我踩过springboot版本太高的坑。Spring Boot 3.x确实很新,但它要求JDK 17,并且很多包名从javax.*换成了jakarta.*。如果你用的旧版插件、代码生成器或者第三方依赖没有跟进,启动阶段就会遇到各种类找不到。所以如果是给公司快速交付,我倾向用Spring Boot 2.7.x + JDK 8;如果是新项目并且愿意折腾,用3.x也没问题,但一定要确认所有依赖版本兼容。

2.2 Maven依赖怎么配

如果用Maven,核心依赖大概是这样:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> </dependencies>

如果你用Gradle,其实就是把依赖坐标从xml换成kotlin dsl,核心技术栈不变。对于毕设或者内部项目,Maven的可见性和资料丰富度会更高。

2.3 后端分层的习惯

我习惯把工程分成这五层:

  • controller:接收参数、做基础校验、返回统一结果。
  • service:写业务逻辑,比如库存加减、状态流转。
  • mapper:只做数据库操作。
  • entity:数据库表对应的实体。
  • dto/vo:接收前端参数的对象,以及返回给前端展示的对象。

这里有个重要原则:Controller里不写库存逻辑,Mapper里不写if-else。所有业务规则都要在Service层完成,这样单元测试好写,出问题也好排查。

封装的统一返回结果一般是这样的:

@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> ok(T data) { Result<T> r = new Result<>(); r.setCode(200); r.setMessage("success"); r.setData(data); return r; } public static <T> Result<T> fail(String message) { Result<T> r = new Result<>(); r.setCode(500); r.setMessage(message); return r; } }

再配一个全局异常处理器,把业务异常统一抛给前端提示,而不是让Spring默认返回一大段堆栈信息。否则前端弹窗会非常难看,也容易把内部数据结构暴露出去。

3. 数据库表设计:库存不能乱改,单据必须留痕

表设计是整个系统的地基。我经历过系统上线后库存对不上,最根本原因就是库存表被各种逻辑直接改来改去,没有任何痕迹。所以这一章值得多看几遍。

3.1 核心表拆分成哪些

我建议从基础表、单据表、库存流水表三层来做:

表名用途关键字段
product商品主数据id, product_no, product_name, spec, unit, price
supplier供应商主数据id, supplier_code, supplier_name, contact, phone
customer客户主数据id, customer_code, customer_name, contact, phone
warehouse仓库主数据id, warehouse_code, warehouse_name, address
stock当前库存快照id, product_id, warehouse_id, quantity
stock_ledger库存流水id, product_id, warehouse_id, change_type, change_quantity, before_quantity, after_quantity, biz_no, create_time
purchase_order采购主表id, order_no, supplier_id, order_status, total_amount, create_time
purchase_order_item采购明细表id, order_id, product_id, order_quantity, received_quantity, price
sales_order销售主表id, order_no, customer_id, order_status, total_amount, create_time
sales_order_item销售明细表id, order_id, product_id, order_quantity, delivered_quantity, price
sys_user系统用户id, username, password, role_id, status, warehouse_id

注意,我特意在采购和销售明细表里加了received_quantity和delivered_quantity,而不是只存订货数量。这是为了支持“一张采购单分两批到货”的真实场景。很多新手直接把订单数量当入库数量用,遇到分批到货就傻了。

3.2 库存表的唯一约束和锁

stock表必须有唯一索引:

ALTER TABLE stock ADD UNIQUE KEY uk_product_warehouse (product_id, warehouse_id);

这条唯一约束非常关键。它防止同一个商品在同一个仓库里出现多行库存记录,也让你能用“查不到就插入,插到冲突就更新”的方式简化代码。

库存流水的设计原则是:只能插入,不能修改,不能删除。所有库存变化,无论是采购入库、销售出库、盘点调整还是手工修正,都必须插入一条stock_ledger流水。这样任何时间点都能通过流水重新算出期末库存,出了问题也查得到是哪一笔业务搞坏的。

3.3 单据状态用什么

业务单据的状态不要用0和1这种布尔值,因为进销存的单据状态往往不止两个。采购单我一般用:

  • DRAFT:草稿
  • PARTIAL_RECEIVED:部分收货
  • RECEIVED:全部收货
  • CANCELLED:作废

销售单类似:

  • PENDING:待出库
  • PARTIAL_DELIVERED:部分出库
  • DELIVERED:全部出库
  • CANCELLED:作废

用枚举或字符串常量维护,数据库里存英文状态码,前端展示时再翻译成中文。这样以后加状态只改一个地方,比在代码里到处写“=1代表完成”要可靠得多。

4. Vue3 前端落地:路由、请求封装与页面复用的细节

前端部分我用的是Vue3 + Vite + Element Plus + Pinia + Axios。这套组合最大的好处是组件生态成熟,表格、弹窗、表单、分页这些后台管理页面常见元素都有现成方案,开发效率非常高。

4.1 目录结构怎么摆

我习惯把前端工程按业务模块而不是按文件类型组织:

src ├── api │ ├── purchase.js │ ├── sale.js │ └── inventory.js ├── components ├── layout │ └── index.vue ├── router │ └── index.js ├── store │ └── modules ├── utils │ └── request.js └── views ├── dashboard ├── purchase ├── sale └── inventory

api目录下每个文件对应一个后端模块,里面只写接口请求函数;views按业务页面放文件。这样当你要找“采购入库”相关代码时,直接进views/purchase就能定位。

路由配置用懒加载,避免首屏加载太多无用代码:

{ path: '/purchase/order', name: 'PurchaseOrderList', component: () => import('@/views/purchase/PurchaseOrderList.vue'), meta: { title: '采购订单', permission: 'purchase:order:list' } }

4.2 Axios 封装一定要做拦截器

后台管理系统最常见的需求是带Token请求接口、401自动跳登录、接口报错弹提示。所以我强烈建议封装一个request.js:

import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' import { useUserStore } from '@/store/user' const request = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) request.interceptors.request.use(config => { const userStore = useUserStore() if (userStore.token) { config.headers.Authorization = 'Bearer ' + userStore.token } return config }) request.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response?.status === 401) { const userStore = useUserStore() userStore.logout() router.push('/login') } else { ElMessage.error(error.response?.data?.message || '网络异常') } return Promise.reject(error) } ) export default request

这里有几件事必须提前想好:后端返回结构统一成{code, message, data},前端才好在拦截器里统一处理;跨域问题在开发环境用Vite的proxy解决,生产环境用Nginx转发;import.meta.env.VITE_API_BASE_URL要放到.env.development和.env.production文件中,不要把环境地址写死在代码里。

4.3 表格页复用套路

后台页面90%都是“顶部查询表单 + 中间表格 + 底部翻页”的结构。以采购订单列表为例:

<el-form :inline="true" :model="queryParams"> <el-form-item label="订单号"> <el-input v-model="queryParams.orderNo" placeholder="请输入订单号" clearable /> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery">查询</el-button> <el-button @click="handleReset">重置</el-button> </el-form-item> </el-form> <el-table :data="tableData" v-loading="loading"> <el-table-column prop="orderNo" label="订单号" /> <el-table-column prop="supplierName" label="供应商" /> <el-table-column prop="orderStatus" label="状态"> <template #default="{ row }"> <el-tag :type="statusTagType(row.orderStatus)">{{ statusText(row.orderStatus) }}</el-tag> </template> </el-table-column> <el-table-column prop="totalAmount" label="总金额" /> <el-table-column label="操作"> <template #default="{ row }"> <el-button link type="primary" @click="handleDetail(row)">查看</el-button> <el-button v-if="row.orderStatus === 'DRAFT'" link type="primary" @click="handleEdit(row)">编辑</el-button> <el-button v-if="row.orderStatus === 'DRAFT'" link type="danger" @click="handleCancel(row)">作废</el-button> </template> </el-table-column> </el-table>

这里用到了el-table-column的插槽,也就是Vue3里常说的作用域插槽,它让你能拿到当前行数据,然后在单元格里自定义标签和按钮。很多新手不知道这点,会把状态列直接绑定成英文代码,看起来非常不友好。

4.4 按钮权限怎么控制

简单项目可以用自定义指令控制按钮显示。先定义一个v-permission指令:

app.directive('permission', { mounted(el, binding) { const userStore = useUserStore() const permissions = userStore.permissions if (!permissions.includes(binding.value)) { el.parentNode?.removeChild(el) } } })

然后在页面按钮上这样用:

<el-button v-permission="'purchase:order:create'" type="primary">新建采购订单</el-button>

这个方案唯一的缺点是页面创建按钮时指令才执行,如果按钮被动态渲染,需要再注意一下。但对付后台管理项目已经足够。

5. 把四条核心业务串起来:从采购到出库,怎么保证库存不丢

这是整个系统最关键的部分。采购入库、销售出库、库存调整、月末盘点,每一条业务线最终都会落到“操作库存表”和“写库存流水表”这两步上。如果这里设计乱了,后面全是坑。

5.1 先画一张状态机图在脑子里

我在实现前会在纸上画出单据状态和库存操作的关系:

  • 采购单从“草稿”到“部分收货”到“完成”,每次收货都要增加库存,并写一条类型为“采购入库”的流水。
  • 销售单从“待出库”到“部分出库”到“完成”,每次出库都要减少库存,并写一条类型为“销售出库”的流水。
  • 仓库盘点或人为修正,走“库存调整”流程,直接写一条调整流水。

状态机的好处是:没有“已完成”的订单不能被再次入库,没有“待出库”的订单不能被重复出库。所有入口都必须先判断状态,再决定能不能继续操作。

5.2 采购入库事务怎么实现

以采购入库为例,核心代码如下:

@Transactional(rollbackFor = Exception.class) public void receivePurchase(Long orderId) { PurchaseOrder order = purchaseOrderMapper.selectById(orderId); if (order == null || !"PARTIAL_RECEIVED".equals(order.getOrderStatus()) && !"DRAFT".equals(order.getOrderStatus())) { throw new BusinessException("当前采购单状态不允许入库"); } List<PurchaseOrderItem> items = purchaseOrderItemMapper.selectByOrderId(orderId); for (PurchaseOrderItem item : items) { Long productId = item.getProductId(); Long warehouseId = order.getWarehouseId(); int quantity = item.getOrderQuantity() - item.getReceivedQuantity(); if (quantity <= 0) { continue; } // 锁住库存行,防止并发重复入库 Stock stock = stockMapper.selectByProductAndWarehouseForUpdate(productId, warehouseId); if (stock == null) { stock = new Stock(); stock.setProductId(productId); stock.setWarehouseId(warehouseId); stock.setQuantity(0); stockMapper.insert(stock); } int beforeQuantity = stock.getQuantity(); stock.setQuantity(beforeQuantity + quantity); stockMapper.updateById(stock); // 写流水 StockLedger ledger = new StockLedger(); ledger.setProductId(productId); ledger.setWarehouseId(warehouseId); ledger.setChangeType("PURCHASE_IN"); ledger.setChangeQuantity(quantity); ledger.setBeforeQuantity(beforeQuantity); ledger.setAfterQuantity(stock.getQuantity()); ledger.setBizNo(order.getOrderNo()); stockLedgerMapper.insert(ledger); // 更新明细已收数量 item.setReceivedQuantity(item.getReceivedQuantity() + quantity); purchaseOrderItemMapper.updateById(item); } // 更新主单状态 if (allItemsReceived(items)) { order.setOrderStatus("RECEIVED"); } else { order.setOrderStatus("PARTIAL_RECEIVED"); } purchaseOrderMapper.updateById(order); }

这里有几个点值得解释。selectByProductAndWarehouseForUpdate是数据库行锁,for update会让同一商品、同一仓库的并发操作串行执行,避免两个请求同时读到库存为0然后都插入重复数据。@Transactional保证库存表、流水表、单据表要么同时成功,要么同时回滚,不会出现“库存加了但单据没更新”的脏数据。

5.3 销售出库如何防止超卖

出库不能用“先查再改”的老办法,因为两个销售员同时下单时,可能都查到剩余库存是10,结果都出库8,最后库存变成负数。更稳妥的方式是用条件更新:

UPDATE stock SET quantity = quantity - #{quantity}, update_time = NOW() WHERE product_id = #{productId} AND warehouse_id = #{warehouseId} AND quantity >= #{quantity}

在Service里判断更新行数:

int rows = stockMapper.deductStock(productId, warehouseId, quantity); if (rows == 0) { throw new BusinessException("库存不足"); }

这样做的好处是原子操作。数据库在更新这一行时已经加了行锁,根本不需要你手动select for update再update,代码更短也更不容易出错。更新成功后,再插入流水表记录。流水表里的before_quantity需要重新查一次库存,不过只要流水和库存变更放在同一事务里,就不会有太大偏差。

5.4 分批收货和分批出库要怎么做

我在前面表设计里专门加过received_quantity和delivered_quantity字段,就是为了处理分批场景。核心逻辑是:每次入库的数量是本次实际到货数量,而不是订单剩余数量。

判断整张订单是否完成,要看所有明细行的received_quantity之和是否大于等于order_quantity。同理,销售出库时判断delivered_quantity是否达到order_quantity。状态从DRAFT到PARTIAL_RECEIVED到RECEIVED的跳转,也要在事务里重新计算,而不是靠着前端传一个isDone上来。

5.5 月末库存台账怎么出

库存报表不能直接查stock表,因为历史某一时刻的库存已经被后续业务改掉了。更好的做法是基于流水汇总:

SELECT product_id, warehouse_id, SUM(CASE WHEN change_type = 'PURCHASE_IN' THEN change_quantity ELSE 0 END) AS total_in, SUM(CASE WHEN change_type = 'SALE_OUT' THEN change_quantity ELSE 0 END) AS total_out, SUM(change_quantity) AS final_stock FROM stock_ledger WHERE create_time < #{endTime} GROUP BY product_id, warehouse_id

如果想算“期初库存加本期入库减本期出库”,就把时间条件拆成两个区间,或者把期初数存到月结表里。总之,流水表是唯一可信的数据源,库存表只是方便日常查询的“快照”。这套思路一定要贯穿到底。

6. 打包部署与那些常见的环境坑

项目写完之后,部署又会碰到一批与环境相关的破事。我把前后端打包和常见的坑列在这里,省得你到了上线那天还在一行行查日志。

6.1 后端打包和启动

后端我用Maven打包:

mvn clean package -DskipTests java -jar target/warehouse-system.jar --spring.profiles.active=prod

生产环境的配置我建议全放到application-prod.yml里,不要在启动命令里写一堆--server.port参数。数据库密码、Redis地址等敏感信息在服务器上用环境变量注入,尽量不要提交到Git仓库。

一个容易忽略的点是MySQL连接串。非常常见的问题是时区报错,我一般这么配:

spring: datasource: url: jdbc:mysql://127.0.0.1:3306/warehouse_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: ${DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver

6.2 前端构建和Nginx转发

前端先构建:

npm run build

生成dist目录,然后配置Nginx。这里有一个特别容易踩的坑:Vue Router如果是history模式,前端刷新除首页之外的页面时会404,必须配置try_files回退到index.html。

server { listen 80; server_name your-domain.com; location / { root /var/www/warehouse-ui; index index.html; 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_pass后面有没有/api/的区别。如果后端接口统一访问前缀是/api,那么建议后端也设置server.servlet.context-path=/api,让Nginx把带/api的请求完整转发给后端,前端开发环境也用同一个前缀,这样跨环境时最省心。

6.3 部署排查对照表

我把实际开发中碰到最多的几类问题整理成一张表:

问题现象常见原因解决办法
后端无法启动,提示ClassNotFoundException: javax.servlet...Spring Boot 3.x下依赖还没适配jakarta包检查依赖版本,或退回Spring Boot 2.7.x
前端npm install报错,找不到@vue/tsconfigNode版本太老或太新,幽灵依赖锁定Node 18 LTS,删除node_modules重新安装
接口报401,前端不跳登录axios封装没处理response.status在拦截器里统一判断401并清空Token
页面能看到,但接口一直404前后端context-path不一致统一/api前缀,Nginx转发规则匹配
数据查询中文乱码数据库连接串没加characterEncoding=utf8修改连接串,并确认库表排序规则为utf8mb4

6.4 上线后数据库备份

不要等到出了事故才想起备份。我一般用crontab每天凌晨备份一次MySQL:

mysqldump -uroot -p${DB_PASSWORD} warehouse_db > /backup/warehouse_db_$(date +%Y%m%d).sql

再配合保留最近7天的压缩包,基本能覆盖绝大多数误删数据的场景。

7. 上线之后最该盯的几件事:对账、日志、权限

系统上线不是结束,反而是一堆新问题的开始。我在这类系统上线后的第一个月,基本只盯三件事:对账、日志、权限调整。

对账这件事,我强烈建议每天都跑一次“当日库存流水汇总”,和订单模块的入库/出库单做核对。如果发现不一致,立刻看流水表,哪一笔业务没有写流水、哪一笔库存变更缺少单据,很快就能定位。等系统跑稳了,再改成每周核对也可以。

日志方面,后端至少把StockLedger这种关键流水表保留三年以上。另外,Spring Boot自带的logback要配置滚动日志,按天生成,保留30天,避免日志文件无限增长。出问题的时候,error级别的堆栈一定会用到。

权限调整则是一个容易被忽视的长期需求。公司员工的角色会换来换去,仓库范围也可能调整。所以用户表里最好带上warehouse_id甚至多个关联仓库,避免以后要给某个人单独开一个仓库权限时还得改代码。

最后再分享一个小经验:这类系统不要急着加太多花哨功能。先把采购、销售、库存三件事做到账实相符,再把报表做准,就已经能帮使用者省下大量Excel时间。很多项目翻车,不是因为技术不行,而是因为一开始业务边界没划清。这套springboot+vue仓库进销存采购管理系统,看着平平无奇,但真正值得花时间研究的,恰恰是那些“不起眼”的库存流水和单据状态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 3:21:42

SpringBoot+Vue+MySQL课表管理系统:从数据库设计到部署全流程解析

每年一到毕业设计季&#xff0c;后台私信里问得最多的就是“SpringBootVueMySQL课表系统怎么做”。这个西安工商学院的课表管理平台&#xff0c;我看着像是一套标准的全栈毕设项目&#xff1a;Java后端负责接口和业务逻辑&#xff0c;Vue前端做页面交互&#xff0c;MySQL存课表…

作者头像 李华
网站建设 2026/10/9 3:20:27

用SVM构建垃圾短信识别系统:中文文本分类从入门到实战

简介&#xff1a;这套基于机器学习支持向量机&#xff08;SVM&#xff09;的垃圾短信识别系统源码&#xff0c;面向计算机相关专业学生与开发者&#xff0c;用于解决短信自动分类与过滤问题&#xff0c;适合作为课程设计、毕业设计或大作业的完整参考。压缩包约107.89MB&#x…

作者头像 李华
网站建设 2026/10/9 3:19:11

Linux底层逻辑与运维实战:从内核机制到系统排障

1. Linux到底是什么&#xff1a;先把底层逻辑搞明白很多新人学Linux一上来就死磕命令&#xff0c;折腾两天发现全忘了。我干了这么多年运维和开发&#xff0c;见过太多人卡在同一个地方——对Linux的底层逻辑没概念&#xff0c;所有知识点都是零散记忆。这就好比你连发动机原理…

作者头像 李华
网站建设 2026/10/9 3:19:10

开源Text-to-SQL引擎WrenAI:用语义层解决自然语言查数难题

聊到 Text-to-SQL&#xff0c;身边不少团队其实早就不买“直接用大模型连数据库”的账了。最典型的一幕&#xff1a;业务同学问“上个月华东区退货率超过 5% 的 SKU 有哪些”&#xff0c;模型张口就给你写了一段带RETURN_RATE的 SQL&#xff0c;可是你的库里根本没有这个字段&a…

作者头像 李华
网站建设 2026/10/9 3:19:06

Excel MATCH函数进阶指南:通配符与数组定位的实战应用

MATCH函数在Excel里属于那种"名气不大、但会的人都当宝"的函数。VLOOKUP人人会用&#xff0c;但一旦涉及反向查找、多条件定位、模糊匹配&#xff0c;VLOOKUP就开始卡壳&#xff0c;而MATCH作为定位神器&#xff0c;反而能把这些问题轻松化解。更关键的是&#xff0c…

作者头像 李华
网站建设 2026/10/9 3:19:06

C#学生管理系统带数据库实战:从建库到CRUD完整指南

简介&#xff1a;这是一套面向C#初学者与课程设计学习者的学生管理系统完整源码&#xff0c;采用C#语言结合MySQL数据库开发&#xff0c;覆盖学生、教师、管理员三类角色的教务管理场景。系统按表示层、业务逻辑层、数据访问层三层架构组织&#xff0c;包含登录、选课、成绩管理…

作者头像 李华