ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于Spring Boot的电脑硬件资产管理系统实战开发指南

基于Spring Boot的电脑硬件资产管理系统实战开发指南 你的公司或部门有多少台电脑这些电脑的硬件配置、采购时间、使用人、存放位置你能在5分钟内全部说清楚吗当一台电脑需要维修、升级或报废时你是否还在用Excel表格手动记录然后发现数据早已过时不同部门的表格还对不上这不是管理问题这是技术债。对于IT部门、行政后勤或任何拥有数十上百台电脑资产的组织来说手工管理硬件资产正成为效率的黑洞和风险的源头。数据分散、更新滞后、盘点困难、责任不清每一个痛点都在消耗管理成本。今天要讨论的正是用技术手段终结这种混乱。我们将基于当前企业级开发中最主流的Spring Boot框架从零开始构建一个专为电脑硬件资产管理设计的后台管理系统项目代号可参考为hx5416。这不是一个简单的CRUD演示而是一个融合了资产全生命周期管理、数据可视化和高效协同的实战项目。你将看到如何用一套轻量级的Java Web系统解决从资产入库、领用、变更、维修到报废的完整流程让每一台电脑的“生老病死”都清晰可控。本文将带你深入三个层面第一理解硬件资产管理系统的核心业务逻辑与架构设计第二手把手完成Spring Boot项目的环境搭建、数据库设计、核心功能开发第三剖析开发中的常见“坑点”与最佳实践确保你的系统健壮可用。无论你是想学习Spring Boot全栈开发还是迫切需要为团队解决资产管理难题这篇文章都将提供一条清晰的落地路径。1. 硬件资产管理从业务痛点倒推技术方案在敲代码之前我们必须先搞清楚要解决什么问题。一个典型的电脑硬件资产管理场景包含以下核心痛点资产信息孤岛采购信息在财务实物信息在行政使用信息在各业务部门数据不互通。状态追踪困难电脑是在库、在用、维修还是报废当前使用者是谁配置有无变更缺乏实时视图。生命周期管理缺失缺乏对资产从采购、领用、转移、维修到报废的全流程电子化记录历史追溯难。盘点效率低下人工盘点耗时耗力且容易出错账实不符的情况时有发生。决策支持不足无法快速分析资产折旧、设备利用率、故障率等难以支撑采购和运维决策。因此我们的技术方案必须瞄准这些痛点。一个合格的“电脑硬件资产管理系统”至少需要具备以下核心功能模块资产档案管理记录每台电脑的核心属性如资产编号、品牌、型号、CPU、内存、硬盘、显卡、采购日期、价格、供应商等。资产状态与流程管理实现资产状态的流转入库、领用、归还、维修、报废并记录每一次状态变更的详情、时间和责任人。使用者与部门关联将资产与具体的员工、部门绑定明确保管责任。查询与统计报表提供多条件的组合查询并生成各类统计报表如部门资产分布、资产类型统计、生命周期报告。系统管理用户、角色、权限管理确保数据安全。基于Spring Boot我们可以快速搭建一个分层清晰、易于维护的后端系统并通过Thymeleaf或前后端分离如Vue.js构建前端界面。项目代号hx5416可以理解为该系统的唯一标识或版本号。2. 技术栈选型与核心概念为什么是Spring Boot因为它极大地简化了基于Spring的初始搭建和开发过程提供了“开箱即用”的体验让我们能专注于业务逻辑而非配置。以下是本项目的核心技术栈后端框架Spring Boot 2.x。它是整个项目的基石整合了Web开发、数据访问、安全等众多模块。数据持久层MyBatis-Plus。在MyBatis的基础上进行了增强提供了强大的CRUD操作和条件构造器能极大减少SQL编写工作量。数据库MySQL 5.7/8.0。关系型数据库适合存储结构化的资产信息。前端模板引擎Thymeleaf。为了快速演示我们采用服务端渲染它语法自然能与Spring Boot完美集成。实际生产中也可采用前后端分离架构如Vue Spring Boot REST API。项目管理与构建Maven。管理项目依赖。其他工具Lombok简化Java Bean代码HutoolJava工具包PageHelper分页插件。核心概念澄清实体Entity对应数据库中的表如ComputerAsset电脑资产、Employee员工、Department部门。数据传输对象DTO用于前后端数据交互可能组合多个实体的字段如AssetDetailDTO。服务Service封装核心业务逻辑的层。控制器Controller接收HTTP请求调用Service并返回响应页面或JSON。MapperMyBatis-Plus的接口定义了数据访问操作。3. 开发环境准备与项目初始化环境要求JDK 8 或 11推荐11长期支持版本Maven 3.6IntelliJ IDEA 或 Eclipse (STS)MySQL 及可视化工具如Navicat、DBeaver步骤1使用Spring Initializr创建项目访问 start.spring.io 或直接在IDEA中使用Spring Initializr向导。 选择以下依赖Spring Web构建Web应用Thymeleaf模板引擎MyBatis Framework数据访问我们将手动引入MyBatis-PlusMySQL Driver数据库驱动Lombok简化代码生成项目并解压用IDE打开。步骤2手动添加MyBatis-Plus等依赖在pom.xml中我们需要将默认的MyBatis依赖替换为功能更强大的MyBatis-Plus并添加其他工具。!-- 在pom.xml的dependencies部分添加或修改 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version !-- 请使用最新稳定版 -- /dependency !-- 移除或注释掉 spring-boot-starter-data-jdbc/jpa 和 mybatis-spring-boot-starter -- dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version1.4.6/version /dependency dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.16/version !-- 请使用最新稳定版 -- /dependency步骤3配置数据库连接在src/main/resources/application.yml中配置# application.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/asset_management?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password thymeleaf: cache: false # 开发时关闭缓存修改模板立即生效 mode: HTML encoding: UTF-8 # MyBatis-Plus 配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 map-underscore-to-camel-case: true # 自动转换下划线命名到驼峰命名 global-config: db-config: id-type: auto # 主键策略数据库自增 logic-delete-field: isDeleted # 全局逻辑删除字段名如果启用 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值步骤4创建数据库在MySQL中执行以下SQL创建数据库和第一张核心表CREATE DATABASE IF NOT EXISTS asset_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE asset_management; -- 部门表 CREATE TABLE department ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, dept_name varchar(100) NOT NULL COMMENT 部门名称, dept_code varchar(50) DEFAULT NULL COMMENT 部门编码, parent_id bigint(20) DEFAULT 0 COMMENT 父部门ID, leader varchar(50) DEFAULT NULL COMMENT 部门负责人, phone varchar(20) DEFAULT NULL COMMENT 联系电话, email varchar(100) DEFAULT NULL COMMENT 邮箱, status tinyint(4) DEFAULT 1 COMMENT 状态1正常 0停用, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_dept_code (dept_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT部门表;4. 核心业务模块设计与实现我们将以最核心的电脑资产ComputerAsset模块为例详细拆解从实体设计到前端展示的全过程。4.1 实体类与数据库设计首先设计computer_asset表它需要记录资产的静态属性和动态状态。-- 电脑资产表 CREATE TABLE computer_asset ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, asset_number varchar(100) NOT NULL COMMENT 资产编号唯一, asset_name varchar(200) NOT NULL COMMENT 资产名称如张三的办公电脑, brand varchar(100) DEFAULT NULL COMMENT 品牌如Dell Lenovo, model varchar(200) DEFAULT NULL COMMENT 型号, sn varchar(100) DEFAULT NULL COMMENT 序列号, cpu_model varchar(200) DEFAULT NULL COMMENT CPU型号, cpu_cores int(11) DEFAULT NULL COMMENT CPU核心数, memory_size varchar(50) DEFAULT NULL COMMENT 内存大小如16GB, disk_size varchar(50) DEFAULT NULL COMMENT 硬盘大小如512GB SSD, gpu_model varchar(200) DEFAULT NULL COMMENT 显卡型号, os varchar(100) DEFAULT NULL COMMENT 操作系统, purchase_date date DEFAULT NULL COMMENT 采购日期, purchase_price decimal(10,2) DEFAULT NULL COMMENT 采购价格, supplier varchar(200) DEFAULT NULL COMMENT 供应商, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 资产状态1-在库2-领用3-维修4-报废, current_user_id bigint(20) DEFAULT NULL COMMENT 当前使用人ID关联employee表, current_dept_id bigint(20) DEFAULT NULL COMMENT 当前所在部门ID, location varchar(500) DEFAULT NULL COMMENT 存放位置, remark text COMMENT 备注, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, is_deleted tinyint(4) DEFAULT 0 COMMENT 逻辑删除0未删除1已删除, PRIMARY KEY (id), UNIQUE KEY uk_asset_number (asset_number), KEY idx_status (status), KEY idx_current_user (current_user_id), KEY idx_current_dept (current_dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT电脑资产表;对应的Java实体类ComputerAsset// 文件路径src/main/java/com/hx5416/asset/entity/ComputerAsset.java package com.hx5416.asset.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; Data TableName(computer_asset) // 指定表名 public class ComputerAsset { TableId(type IdType.AUTO) // 主键自增 private Long id; private String assetNumber; // 资产编号 private String assetName; // 资产名称 private String brand; private String model; private String sn; // 序列号 // 硬件配置 private String cpuModel; private Integer cpuCores; private String memorySize; private String diskSize; private String gpuModel; private String os; // 采购信息 private LocalDate purchaseDate; private BigDecimal purchasePrice; private String supplier; // 状态与归属 private Integer status; // 使用状态枚举 private Long currentUserId; private Long currentDeptId; private String location; private String remark; // 审计字段 TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; TableLogic // 逻辑删除注解 private Integer isDeleted; }状态枚举类为了代码可读性我们创建一个枚举类。// 文件路径src/main/java/com/hx5416/asset/enums/AssetStatusEnum.java package com.hx5416.asset.enums; import lombok.Getter; Getter public enum AssetStatusEnum { IN_STOCK(1, 在库), IN_USE(2, 领用), UNDER_MAINTENANCE(3, 维修), SCRAPPED(4, 报废); private final Integer code; private final String desc; AssetStatusEnum(Integer code, String desc) { this.code code; this.desc desc; } public static String getDescByCode(Integer code) { for (AssetStatusEnum value : AssetStatusEnum.values()) { if (value.getCode().equals(code)) { return value.getDesc(); } } return 未知; } }4.2 数据访问层Mapper使用MyBatis-Plus我们只需创建一个继承BaseMapper的接口即可获得通用的CRUD方法。// 文件路径src/main/java/com/hx5416/asset/mapper/ComputerAssetMapper.java package com.hx5416.asset.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.hx5416.asset.entity.ComputerAsset; import org.apache.ibatis.annotations.Mapper; Mapper // 标识为MyBatis的MapperSpring Boot会自动扫描 public interface ComputerAssetMapper extends BaseMapperComputerAsset { // 无需编写XML基本的增删改查已由BaseMapper提供 // 复杂查询可以使用注解Select或XML文件 }4.3 业务逻辑层ServiceService层封装核心业务逻辑如资产的新增、领用、归还、查询等。// 文件路径src/main/java/com/hx5416/asset/service/ComputerAssetService.java package com.hx5416.asset.service; import com.baomidou.mybatisplus.extension.service.IService; import com.hx5416.asset.entity.ComputerAsset; import com.hx5416.asset.vo.AssetQueryVO; import java.util.List; public interface ComputerAssetService extends IServiceComputerAsset { /** * 条件分页查询资产列表 */ ListComputerAsset listByCondition(AssetQueryVO queryVO); /** * 领用资产 * param assetId 资产ID * param userId 领用人ID * param deptId 领用部门ID * param remark 备注 * return 是否成功 */ boolean assignAsset(Long assetId, Long userId, Long deptId, String remark); /** * 归还资产 */ boolean returnAsset(Long assetId, String remark); /** * 生成资产编号规则示例ASSET-20240527-001 */ String generateAssetNumber(); }// 文件路径src/main/java/com/hx5416/asset/service/impl/ComputerAssetServiceImpl.java package com.hx5416.asset.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hx5416.asset.entity.ComputerAsset; import com.hx5416.asset.enums.AssetStatusEnum; import com.hx5416.asset.mapper.ComputerAssetMapper; import com.hx5416.asset.service.ComputerAssetService; import com.hx5416.asset.vo.AssetQueryVO; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.List; Service public class ComputerAssetServiceImpl extends ServiceImplComputerAssetMapper, ComputerAsset implements ComputerAssetService { Override public ListComputerAsset listByCondition(AssetQueryVO queryVO) { LambdaQueryWrapperComputerAsset wrapper new LambdaQueryWrapper(); // 按资产编号/名称模糊查询 if (StrUtil.isNotBlank(queryVO.getKeyword())) { wrapper.like(ComputerAsset::getAssetNumber, queryVO.getKeyword()) .or() .like(ComputerAsset::getAssetName, queryVO.getKeyword()); } // 按状态筛选 if (queryVO.getStatus() ! null) { wrapper.eq(ComputerAsset::getStatus, queryVO.getStatus()); } // 按品牌筛选 if (StrUtil.isNotBlank(queryVO.getBrand())) { wrapper.eq(ComputerAsset::getBrand, queryVO.getBrand()); } // 按部门筛选 if (queryVO.getDeptId() ! null) { wrapper.eq(ComputerAsset::getCurrentDeptId, queryVO.getDeptId()); } wrapper.orderByDesc(ComputerAsset::getCreateTime); return this.list(wrapper); } Override Transactional(rollbackFor Exception.class) // 添加事务确保操作原子性 public boolean assignAsset(Long assetId, Long userId, Long deptId, String remark) { ComputerAsset asset this.getById(assetId); if (asset null || !AssetStatusEnum.IN_STOCK.getCode().equals(asset.getStatus())) { throw new RuntimeException(资产不存在或非在库状态无法领用); } asset.setStatus(AssetStatusEnum.IN_USE.getCode()); asset.setCurrentUserId(userId); asset.setCurrentDeptId(deptId); asset.setRemark((asset.getRemark() null ? : asset.getRemark() \n) 【领用】 LocalDate.now() 领用人ID: userId remark); return this.updateById(asset); } Override Transactional(rollbackFor Exception.class) public boolean returnAsset(Long assetId, String remark) { ComputerAsset asset this.getById(assetId); if (asset null || !AssetStatusEnum.IN_USE.getCode().equals(asset.getStatus())) { throw new RuntimeException(资产不存在或非领用状态无法归还); } asset.setStatus(AssetStatusEnum.IN_STOCK.getCode()); asset.setCurrentUserId(null); asset.setCurrentDeptId(null); asset.setRemark((asset.getRemark() null ? : asset.getRemark() \n) 【归还】 LocalDate.now() remark); return this.updateById(asset); } Override public String generateAssetNumber() { // 简单示例ASSET-年月日-三位序号实际应从数据库获取最大序号 String dateStr DateUtil.format(LocalDate.now(), yyyyMMdd); // 这里应该查询当日已有多少资产然后1。为简化使用随机数模拟。 String seq String.format(%03d, (int)(Math.random() * 1000)); return ASSET- dateStr - seq; } }4.4 控制器层Controller与页面控制器负责接收请求调用Service并返回视图或数据。// 文件路径src/main/java/com/hx5416/asset/controller/ComputerAssetController.java package com.hx5416.asset.controller; import com.hx5416.asset.entity.ComputerAsset; import com.hx5416.asset.service.ComputerAssetService; import com.hx5416.asset.vo.AssetQueryVO; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.List; Controller RequestMapping(/asset/computer) public class ComputerAssetController { Autowired private ComputerAssetService computerAssetService; /** * 资产列表页 */ GetMapping(/list) public String listPage(AssetQueryVO queryVO, RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, Model model) { // 使用PageHelper分页 PageHelper.startPage(pageNum, pageSize); ListComputerAsset assetList computerAssetService.listByCondition(queryVO); PageInfoComputerAsset pageInfo new PageInfo(assetList); model.addAttribute(pageInfo, pageInfo); model.addAttribute(queryVO, queryVO); // 可以在这里添加品牌、部门等下拉框数据 // model.addAttribute(brandList, ...); // model.addAttribute(deptList, ...); return asset/computer_list; // 对应 src/main/resources/templates/asset/computer_list.html } /** * 跳转到新增资产页面 */ GetMapping(/add) public String addPage(Model model) { // 生成一个预填的资产编号 model.addAttribute(assetNumber, computerAssetService.generateAssetNumber()); return asset/computer_add; } /** * 保存新增资产 */ PostMapping(/save) public String saveAsset(ComputerAsset computerAsset) { // 这里可以添加数据校验如资产编号唯一性检查 computerAssetService.save(computerAsset); return redirect:/asset/computer/list; // 保存后重定向到列表页 } /** * 领用资产操作 */ PostMapping(/assign/{id}) public String assignAsset(PathVariable Long id, RequestParam Long userId, RequestParam Long deptId, RequestParam(required false) String remark) { computerAssetService.assignAsset(id, userId, deptId, remark); return redirect:/asset/computer/list; } }前端页面示例Thymeleaf 由于篇幅限制这里仅展示列表页的核心片段。!-- 文件路径src/main/resources/templates/asset/computer_list.html -- !DOCTYPE html html xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title电脑资产管理/title !-- 引入Bootstrap CSS -- link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/css/bootstrap.min.css relstylesheet /head body div classcontainer-fluid mt-3 h2电脑资产列表/h2 !-- 查询表单 -- form th:action{/asset/computer/list} methodget classrow g-3 mb-4 div classcol-auto input typetext classform-control namekeyword th:value${queryVO.keyword} placeholder资产编号/名称 /div div classcol-auto select classform-select namestatus option value全部状态/option option th:eachstatus : ${T(com.hx5416.asset.enums.AssetStatusEnum).values()} th:value${status.code} th:text${status.desc} th:selected${queryVO.status status.code} /option /select /div div classcol-auto button typesubmit classbtn btn-primary查询/button a th:href{/asset/computer/add} classbtn btn-success新增资产/a /div /form !-- 资产表格 -- table classtable table-hover table-bordered thead classtable-light tr th资产编号/th th资产名称/th th品牌/型号/th th配置/th th状态/th th使用人/部门/th th操作/th /tr /thead tbody tr th:eachasset : ${pageInfo.list} td th:text${asset.assetNumber}/td td th:text${asset.assetName}/td td span th:text${asset.brand}/span / span th:text${asset.model}/span /td td CPU: span th:text${asset.cpuModel}/span br 内存: span th:text${asset.memorySize}/span br 硬盘: span th:text${asset.diskSize}/span /td td !-- 根据状态码显示不同的标签 -- span th:switch${asset.status} span th:case1 classbadge bg-secondary在库/span span th:case2 classbadge bg-success领用/span span th:case3 classbadge bg-warning维修/span span th:case4 classbadge bg-danger报废/span /span /td td th:text${asset.currentUserId}--/td td a th:href{/asset/computer/detail/{id}(id${asset.id})} classbtn btn-sm btn-info详情/a a th:if${asset.status 1} th:href{/asset/computer/assign/{id}(id${asset.id})} classbtn btn-sm btn-primary领用/a a th:if${asset.status 2} th:href{/asset/computer/return/{id}(id${asset.id})} classbtn btn-sm btn-secondary归还/a /td /tr /tbody /table !-- 分页组件 -- nav ul classpagination justify-content-center li classpage-item th:classappend${pageInfo.isFirstPage} ? disabled : a classpage-link th:href{/asset/computer/list(pageNum${pageInfo.prePage}, pageSize${pageInfo.pageSize}, keyword${queryVO.keyword}, status${queryVO.status})}上一页/a /li li th:eachpageNum : ${pageInfo.navigatepageNums} classpage-item th:classappend${pageInfo.pageNum pageNum} ? active : a classpage-link th:href{/asset/computer/list(pageNum${pageNum}, pageSize${pageInfo.pageSize}, keyword${queryVO.keyword}, status${queryVO.status})} th:text${pageNum}/a /li li classpage-item th:classappend${pageInfo.isLastPage} ? disabled : a classpage-link th:href{/asset/computer/list(pageNum${pageInfo.nextPage}, pageSize${pageInfo.pageSize}, keyword${queryVO.keyword}, status${queryVO.status})}下一页/a /li /ul /nav /div !-- 引入Bootstrap JS -- script srchttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/js/bootstrap.bundle.min.js/script /body /html5. 运行与效果验证启动应用运行Spring Boot主类AssetManagementApplication需自行创建通常由Initializr生成。访问首页在浏览器中输入http://localhost:8080。你需要创建一个简单的首页控制器来跳转。访问资产列表直接访问http://localhost:8080/asset/computer/list。预期效果你应该能看到一个包含查询表单和资产列表的页面。点击“新增资产”可以跳转到表单页填写信息提交后数据应存入数据库并在列表页显示。对“在库”状态的资产可以点击“领用”状态会变为“领用”。关键验证点控制台是否打印出MyBatis-Plus执行的SQL语句确认数据库连接和操作正常页面是否正常渲染无Thymeleaf语法错误新增、查询、领用、归还功能是否按预期工作分页功能是否正常6. 常见问题与排查思路问题现象可能原因排查方式解决方案应用启动失败报DataSource相关错误1. 数据库连接URL、用户名、密码错误。2. MySQL服务未启动。3. 数据库asset_management不存在。1. 检查application.yml中的spring.datasource配置。2. 在命令行或工具中尝试连接MySQL。3. 检查数据库是否创建。1. 修正配置。2. 启动MySQL服务。3. 执行提供的SQL创建数据库和表。访问页面报4041. 控制器请求映射路径错误。2. 静态资源或模板文件位置不对。3. 未添加Controller注解。1. 检查浏览器地址栏URL与控制器RequestMapping是否匹配。2. 检查templates目录下是否有对应的.html文件。3. 确认控制器类是否被Spring扫描到在启动类同级或子包下。1. 修正控制器或访问URL。2. 将HTML文件放在src/main/resources/templates/下。3. 确保启动类有SpringBootApplication注解。页面显示Whitelabel Error Page通常是因为Thymeleaf模板解析出错。查看控制台详细的异常堆栈信息通常会指向模板文件的某一行。根据错误信息检查HTML中的Thymeleaf语法如th:*属性是否正确变量名是否与Model中传递的一致。插入数据中文乱码1. 数据库表字符集不是utf8mb4。2. JDBC连接字符串未指定字符集。1. 查看建表语句。2. 检查application.yml中JDBC URL的characterEncoding参数。1. 修改表字符集为utf8mb4。2. 确保JDBC URL中包含useUnicodetruecharacterEncodingutf-8。MyBatis-Plus的BaseMapper方法不生效1. Mapper接口未添加Mapper注解或启动类未加MapperScan。2. 实体类与数据库表名/字段名映射错误。1. 检查Mapper接口注解。2. 查看控制台SQL日志看生成的SQL语句是否正确。1. 在Mapper接口上加Mapper或在启动类上加MapperScan(com.hx5416.asset.mapper)。2. 使用TableName,TableField注解明确指定映射关系。分页功能无效1. PageHelper的startPage方法调用位置不对必须在查询执行前。2. 依赖冲突。1. 检查PageHelper.startPage(pageNum, pageSize);是否在computerAssetService.listByCondition(queryVO);之前调用。2. 检查pom.xml中PageHelper版本是否与其他插件冲突。1. 确保startPage紧邻查询语句。2. 统一MyBatis和PageHelper的版本或使用MyBatis-Plus自带的分页插件。7. 最佳实践与工程建议前后端分离对于更复杂的项目强烈建议采用前后端分离架构。后端提供RESTful API使用RestController前端使用Vue.js、React等框架。这能带来更好的用户体验和团队协作效率。API设计规范定义统一的响应体格式如{“code”: 200, “msg”: “success”, “data”: {}}。使用全局异常处理器ControllerAdvice来统一处理异常并返回规范格式。数据校验在Controller的方法参数或接收的DTO上使用javax.validation注解如NotBlank,Size进行数据校验并在Controller类上添加Validated注解。权限控制集成Spring Security或Shiro实现基于角色的访问控制RBAC。例如资产报废操作可能只有管理员才能执行。日志记录使用SLF4J Logback记录重要的业务操作日志如资产领用、报废便于审计和问题追踪。代码生成器MyBatis-Plus提供了强大的代码生成器AutoGenerator可以根据数据库表自动生成Entity、Mapper、Service、Controller代码极大提升开发效率。数据库设计优化索引为经常用于查询和关联的字段如asset_number,status,current_user_id建立索引。字段冗余在computer_asset表中我们直接存储了current_dept_id避免了频繁的联表查询。这是一种空间换时间的优化。历史表对于重要的状态变更如领用、维修、报废可以考虑创建单独的asset_history表记录每一次变更的完整快照便于深度追溯。部署与监控使用Docker容器化部署配合Jenkins或GitLab CI/CD实现自动化构建和部署。集成Actuator和Prometheus进行应用健康监控。8. 总结与后续方向通过本文我们完成了一个基于Spring Boot的电脑硬件资产管理系统的核心骨架。我们从真实的业务痛点出发设计了数据库表并使用Spring Boot MyBatis-Plus Thymeleaf的技术栈快速实现了资产的增删改查、状态流转和分页查询。这个项目hx5416麻雀虽小五脏俱全涵盖了企业级应用开发中的多个关键环节。本文真正讲清楚的核心点业务驱动设计如何从管理痛点推导出系统的核心功能与数据模型。高效技术选型为何选择Spring Boot和MyBatis-Plus这套组合来快速实现。分层架构实现清晰展示了Controller、Service、Mapper、Entity各层的职责与协作。关键功能落地详细实现了资产状态机在库、领用、维修、报废的流转逻辑。可复现的代码提供了从建表SQL到前端HTML的完整代码片段可直接运行验证。你可以继续深入的方向完善其他模块实现员工管理、部门管理、供应商管理、维修记录、报废审批等模块。引入工作流对于资产领用、报废等流程可以集成Flowable或Activiti实现可配置的审批流程。数据可视化使用ECharts等图表库在Dashboard页面展示资产统计、折旧分析、故障率等图表。资产标签与盘点结合二维码或RFID技术为每个资产生成唯一标签开发手机端盘点功能。系统集成与公司的OA系统、财务系统进行对接实现数据自动同步。将这个系统部署起来你就能彻底告别Excel表格管理资产的混乱时代。更重要的是通过亲手构建这样一个完整的项目你对Spring Boot全栈开发的理解将从理论真正走向实践。建议你将此项目作为基础根据实际需求不断扩展和优化它将成为你技术栈中一个极具说服力的实战案例。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进