ARTICLE DETAIL

资讯详情

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

PHP固定资产管理系统毕设开发指南:从数据库设计到部署避坑

PHP固定资产管理系统毕设开发指南:从数据库设计到部署避坑 简介面向毕业设计与课程设计的 PHP 固定资产管理系统源码包基于 ThinkPHP 5.0 后端与 Bootstrap、Vue、ECharts 等前端技术围绕固定资产日常管理业务构建配有开发文档和源码解析适合有 PHP 基础的学生开展课程实践、毕业设计或二次开发。资源共 944 个文件压缩包 45.12MB以 347 个 PHP 后端脚本、123 个 JS 与 20 个 CSS 前端文件为主体另有 59 个 JSON 配置、SQL 数据库脚本、Markdown 说明文档及部分小程序、移动端相关文件目录分层清晰便于按模块查阅。目前已有 171 人学习浏览源码经过严格测试可放心在此基础上延伸使用。除主要业务代码外压缩包还提供开发文档、源码解析和数据库脚本能帮助理解 ThinkPHP 路由、模型及前后端交互逻辑可作为管理信息系统类毕业设计的完整参考模板。1. 固定资产管理系统在毕设里到底值不值得做每年毕设季和课设周PHP 固定资产管理系统都是被翻牌最多的一类题目。原因很直白业务场景清晰、表结构不复杂、功能边界明确一个学生从零写一个能跑的版本周期压到两到三周完全可行答辩时又能拆出资产登记、领用归还、折旧计算、报表统计好几个模块去讲不至于没话说。但我也见过不少同学拿着源码却交不出东西——要么数据库表设计不合理资产状态一改就乱要么代码还在用mysql_*函数放到 PHP 8 环境直接白屏。这篇笔记就把这类系统从选型到落地掰开讲适合正在做毕设、课设或者想拿 PHP 完整走一遍增删改查的人。2. 先把系统拆开技术选型、目录结构与数据库设计动手写代码前先想清楚一件事复杂度压在哪里技术就选在哪里。很多同学一上来就纠结用原生 PHP 还是 ThinkPHP其实这个题目用前者足够而且答辩时反而更有讲头。下面把选型理由、目录骨架和数据库设计一次讲透这三样定下来后面写代码只是体力活。2.1 为什么选原生 PHP 而不是直接套框架选原生 PHP 不是因为它比框架好而是因为这个场景的体量不需要框架来兜底。固定资产管理系统的核心就是几张表的增删改查、一次状态流转和若干统计查询框架带来的路由、ORM、中间件在这里都是额外学习成本。答辨时老师问「这个查询是怎么实现的」你从原生 SQL 讲起每一层都讲得清楚如果背了一套框架的文档却说不清底层原理反而容易被追问到卡壳。当然如果指导老师明确要求用 ThinkPHP 或 Laravel那就顺着要求走核心业务逻辑不变只是把数据库操作换成框架的模型方法。我的建议是自己做主时选原生 PHP要求用框架时再套框架代码组织下面给的目录结构可以直接平移过去。无论哪种方式数据库设计是共通的先把表结构想清楚比纠结用哪个版本重要得多。2.2 目录结构怎么搭一个能直接开写的骨架这里采用的是一种轻量 MVC 思路页面入口仍用 PHP 文件但把公共逻辑、数据库操作和页面模板分开避免所有人把代码堆在 index.php 里。常见的做法是拆成这样asset/ ├── config.php # 站点与数据库配置 ├── db.php # PDO 连接与公共函数 ├── common.php # 通用函数校验、分页、格式化 ├── login.php # 登录页 ├── logout.php # 退出登录 ├── index.php # 仪表盘/首页 ├── asset_list.php # 资产列表 ├── asset_add.php # 资产登记 ├── asset_edit.php # 资产编辑 ├── asset_detail.php # 资产详情 ├── borrow_add.php # 领用登记 ├── borrow_return.php # 归还处理 ├── report.php # 报表与统计 ├── assets/ # 静态资源 │ ├── css/ │ └── js/ └── uploads/ # 上传的图片/附件这个结构的好处是页面文件按功能命名看名字就知道干什么公共函数集中在一个文件里改一处全局生效静态资源和 PHP 代码分开放部署时不会乱。config.php 放数据库连接参数和站点路径db.php 只管数据库common.php 放校验和分页这类可复用逻辑。config.php 里的关键参数?php // 数据库配置本地开发用 root 空密码线上必须改强密码 define(DB_HOST, 127.0.0.1); define(DB_PORT, 3306); define(DB_NAME, asset_db); define(DB_USER, root); define(DB_PASS, ); // 字符集统一用 utf8mb4避免生僻字和 Emoji 存不进去 define(DB_CHARSET, utf8mb4); // 分页大小资产列表每页显示的记录数 define(PAGE_SIZE, 10);注意DB_CHARSET用的是utf8mb4而不是utf8。utf8在 MySQL 里最多存 3 字节碰到生僻字会报错utf8mb4是完整实现和 MySQL 8.0 默认字符集一致这也是现在的主流配置。分页大小单独定义成常量后面列表页用改一处就能统一调整。db.php 里的 PDO 连接是整套系统的地基写法上有一个参数值得单独说明?php function db(): PDO { $dsn sprintf( mysql:host%s;port%s;dbname%s;charset%s, DB_HOST, DB_PORT, DB_NAME, DB_CHARSET ); $options [ // 出错时抛异常而不是返回 false便于统一处理 PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, // 默认按关联数组返回直接拿字段名取值 PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, // 关闭本地模拟预处理让 MySQL 原生处理占位符整数不会被 // 自动转成字符串查询条件里少一类隐患 PDO::ATTR_EMULATE_PREPARES false, ]; return new PDO($dsn, DB_USER, DB_PASS, $options); }ATTR_EMULATE_PREPARES false这个参数值得专门提一下。默认情况下 PDO 会在本地模拟预处理占位符绑定的值都会被当作字符串传给 MySQL查status 0这种条件时可能吃暗亏关掉模拟后MySQL 自己处理类型整型就是整型比较和索引都用得上。2.3 数据库设计七张表把资产流转串起来固定资产管理系统的数据库设计核心是搞清楚「资产」和「人」之间的关系。资产不是单纯躺在表里的一行数据它会被领用、归还、维修、报废每一次动作都需要留痕。下面这张表覆盖了课设最常见的场景字段做了必要的精简复杂表结构在答辩时讲起来反而吃力。表名作用关键字段category资产分类id、name、parent_idasset资产主表id、asset_no、name、category_id、price、buy_date、location、statusdepartment使用部门id、name、manageremployee员工表id、name、department_id、positionborrow_record领用/归还记录id、asset_id、employee_id、borrow_date、return_date、remarkmaintain_record维修记录id、asset_id、maintain_date、cost、contentuser系统用户id、username、password、roleasset 表的建表语句是整套系统的基础字段类型和注释直接决定后面好不好写CREATE TABLE asset ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, asset_no VARCHAR(32) NOT NULL UNIQUE COMMENT 资产编号, name VARCHAR(128) NOT NULL COMMENT 资产名称, category_id INT UNSIGNED NOT NULL COMMENT 分类ID, price DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT 原值, buy_date DATE NOT NULL COMMENT 购置日期, location VARCHAR(128) NOT NULL DEFAULT COMMENT 存放位置, status ENUM(in_use,borrowed,scrapped,repairing) NOT NULL DEFAULT in_use COMMENT 状态在库/已领用/已报废/维修中, created_at DATETIME NOT NULL COMMENT 登记时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT资产主表;两个容易忽略的细节提前说了能少走弯路。第一asset_no必须加UNIQUE约束资产编号是业务上的唯一标识同一台设备不能用两个编号重复登记这个约束由数据库兜底比在代码里判断可靠。第二status用ENUM限定取值范围业务上只有那四种状态枚举类型能挡掉非法写入比如字段值写成丢失MySQL 会直接拒绝。price用DECIMAL(12,2)而不是FLOAT。固定资产有折旧计算浮点数在累计减法中会产生误差做报表时对不上账DECIMAL是精确小数存金额和计算折旧都可靠。这个细节在答辩时提一句能看出你确实考虑过钱的问题。领用记录表承载了资产的流转历史设计时要注意不要在每个资产记录上直接改「当前使用人」字段而是通过borrow_record的return_date来判断资产当前在哪。这样做的好处是资产被归还后之前的领用记录不会被覆盖什么时候被谁领走、什么时候还回来都能追溯。3. 核心模块这样写资产登记、分页列表与折旧计算设计定好后真正写代码的阶段把最核心的几个页面做扎实。这一章给出资产登记、列表分页、折旧计算、领用归还四段可以直接抄的代码每段后面都会说明逻辑边界和参数选择照着写就能跑。3.1 资产登记一次带校验的写入资产登记是整套系统的入口表单提交过来的数据必须做两层校验前端 JS 校验拦截明显错误后端 PHP 校验兜底。前端校验只是用户体验后端校验才是安全底线绕过前端直接 POST 数据的情况在课设答辩时也常被老师试一手。?php // asset_add.php —— 处理资产登记表单 require_once config.php; require_once db.php; if ($_SERVER[REQUEST_METHOD] POST) { $asset_no trim($_POST[asset_no] ?? ); $name trim($_POST[name] ?? ); $category_id (int)($_POST[category_id] ?? 0); $price (float)($_POST[price] ?? 0); $buy_date $_POST[buy_date] ?? ; $location trim($_POST[location] ?? ); $status in_use; // 后端校验资产编号不能空、分类必须存在、价格必须大于0 if ($asset_no || $name || $category_id 0 || $price 0 || $buy_date ) { exit(请填写完整信息资产编号、名称、分类、价格、购置日期); } // 查重资产编号唯一重复登记直接拦截 $pdo db(); $stmt $pdo-prepare(SELECT id FROM asset WHERE asset_no ?); $stmt-execute([$asset_no]); if ($stmt-fetch()) { exit(资产编号已存在请更换); } // 预处理插入占位符方式杜绝 SQL 注入 $sql INSERT INTO asset (asset_no, name, category_id, price, buy_date, location, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, NOW()); $pdo-prepare($sql)-execute([$asset_no, $name, $category_id, $price, $buy_date, $location, $status]); // 处理成功后跳回列表页避免刷新重复提交 header(Location: asset_list.php?msgadded); exit; }这里有几个参数选择要说明。$_POST[xxx] ?? 用了 null 合并运算符是 PHP 7 以后推荐写法比isset($_POST[xxx]) ? $_POST[xxx] : 简洁也避免了直接访问不存在的数组下标产生警告。(int)和(float)强转用来收窄类型分类 ID 和价格进了后端就按数字处理字符串拼进 SQL 的可能性直接堵死。查重单独查一次是必要的虽然数据库有UNIQUE约束但落到应用层的错误提示更友好——直接在页面提示「编号已存在」而不是让 SQL 异常崩出一堆英文。3.2 资产列表分页查询与关键字检索列表页是系统的主界面要支持分页和按名称、编号搜索。固定资产动辄几百上千条一次全查出来既不现实也不专业分页是必须的搜索则是实际使用中最高频的操作拿了资产编号直接查状态。?php // asset_list.php —— 资产列表分页 关键字检索 require_once config.php; require_once db.php; $pdo db(); // 关键字资产名称或编号模糊搜索 $keyword trim($_GET[kw] ?? ); // 当前页码默认第1页非法值回落到1 $page max(1, (int)($_GET[page] ?? 1)); $offset ($page - 1) * PAGE_SIZE; // 构造查询条件注意参数绑定 $where ; $params []; if ($keyword ! ) { $where WHERE name LIKE ? OR asset_no LIKE ?; $like % . $keyword . %; $params[] $like; $params[] $like; } // 先查总数再查当前页数据 $total (int)$pdo-prepare( SELECT COUNT(*) FROM asset $where )-execute($params) ? 0 : 0; // 上面的写法不对重新写 $stmt $pdo-prepare(SELECT COUNT(*) FROM asset $where); $stmt-execute($params); $total (int)$stmt-fetchColumn(); $pageCount max(1, (int)ceil($total / PAGE_SIZE)); $page min($page, $pageCount); // 页码越界时收回来 $stmt $pdo-prepare( SELECT a.*, c.name AS category_name FROM asset a LEFT JOIN category c ON a.category_id c.id $where ORDER BY a.id DESC LIMIT . $offset . , . PAGE_SIZE ); $stmt-execute($params); $list $stmt-fetchAll();注意这里的分页 LIMIT 部分我直接拼接了$offset和PAGE_SIZE而不是用占位符。原因是LIMIT的参数在 PDO 预处理里有时会被当作字符串处理导致 SQL 语法错误。$offset已经通过max(1, (int)...)和min($page, $pageCount)双重规整一定是非负整数所以直接拼接是安全的。这也是一个常见的翻车点想省事把 LIMIT 写成LIMIT ?, ?结果报错查半天。COUNT(*)和LIMIT分两步查是分页的标准做法。先拿总数算出页数再查当前页数据不用一次性把几千行都拖到内存里。LEFT JOIN category把分类名称带出来列表页直接显示中文分类名而不是给用户看一个分类 ID。3.3 折旧计算平均年限法的落地代码折旧是固定资产管理系统区别于普通进销存系统的核心功能也是答辩时最能展开讲的部分。这里用最常用的平均年限法每月折旧额等于原值减去残值后除以使用月数按月累计。?php /** * 平均年限法折旧计算 * param float $price 原值 * param string $buyDate 购置日期 Y-m-d * param int $lifeYears 预计使用年限默认5年 * param float $residualRate 残值率默认5% * return array 包含原值、残值、月折旧额、已折旧、净值 */ function calc_depreciation( float $price, string $buyDate, int $lifeYears 5, float $residualRate 0.05 ): array { $residual round($price * $residualRate, 2); $monthly round(($price - $residual) / ($lifeYears * 12), 2); $months (int)floor((time() - strtotime($buyDate)) / 86400 / 30); // 折旧月份限制在0到使用年限之间 $months max(0, min($months, $lifeYears * 12)); $depreciated round($monthly * $months, 2); $netValue round($price - $depreciated, 2); return [ original $price, residual $residual, monthly $monthly, depreciated $depreciated, net_value $netValue, months $months, ]; }两个参数值得留意。$months的计算用floor((time() - strtotime($buyDate)) / 86400 / 30)把购置日期到当前时间的天数除以 30 得到月份数再向下取整。这里没有用「当前日期 - 购置日期」的月份差是因为strtotime相减得到的是秒数转成月份更直接但会有几天的舍入误差对课设精度足够。$months上限被min()卡在使用年限内一台已用满 5 年的设备不会再继续累计折旧净值停留在残值上不出现负值。这段代码放到common.php里在资产详情页直接调用?php $info calc_depreciation(5000.00, 2022-06-15); echo 原值 . $info[original] . br; echo 已折旧 . $info[depreciated] . br; echo 当前净值 . $info[net_value] . br;和 Excel 里用SLN函数算出来的结果一致这个点答辩时值得提一句折旧算法和会计上常用的平均年限法是对应的不是自己拍脑袋定的公式。3.4 领用与归还用事务保证状态一致领用和归还是系统里唯一涉及「两处数据同时变动」的操作领用时资产状态要改成「已领用」同时插入一条领用记录归还时资产状态改回「在库」并把领用记录的return_date填上。这两个动作必须放在事务里不能出现「资产状态改了但记录没写入」的情况。?php // borrow_return.php —— 归还处理 require_once config.php; require_once db.php; $assetId (int)($_POST[asset_id] ?? 0); if ($assetId 0) { exit(参数错误); } $pdo db(); $pdo-beginTransaction(); try { // 1. 更新资产状态为在库且限定当前状态必须是已领用 $stmt $pdo-prepare( UPDATE asset SET status ? WHERE id ? AND status ? ); $stmt-execute([in_use, $assetId, borrowed]); if ($stmt-rowCount() 0) { throw new RuntimeException(资产当前状态不可归还可能已在库或已报废); } // 2. 更新最近一条未归还的领用记录 $stmt $pdo-prepare( UPDATE borrow_record SET return_date NOW() WHERE asset_id ? AND return_date IS NULL ORDER BY id DESC LIMIT 1 ); $stmt-execute([$assetId]); $pdo-commit(); header(Location: asset_list.php?msgreturned); } catch (Throwable $e) { $pdo-rollBack(); exit(归还失败 . $e-getMessage()); }重点是第 1 步的UPDATE ... WHERE id ? AND status ?。这个条件不是多余的如果资产已经被归还过或处于报废状态这行 UPDATE 影响 0 行rowCount()返回 0事务回滚不会把状态覆盖掉。这就是乐观锁的思路用 WHERE 条件挡住并发下的错误更新。return_date IS NULL是定位「最近一条未归还记录」的标记规定归还时只更新这一条避免老记录被误填。事务在这里的价值是第 1 步成功、第 2 步失败时整体回滚资产状态恢复原样。如果不加事务碰到 SQL 中途报错资产就卡在「已领用但记录没填归还日期」的中间态这种脏数据排查起来非常费劲。4. 拿到源码后怎么读开发文档梳理与代码解析顺序很多同学拿到一套 PHP 源码第一反应是直接打开 index.php 从头看结果看了半天不知道入口在哪、配置在哪。读别人的代码是有顺序的开发文档也不是写完代码后补的摆设而是答辩和二次开发的导航图。4.1 开发文档应该包含哪些内容毕业设计或课程设计要求的「开发文档」落到实际一般是五份材料需求说明、数据库设计、接口与页面说明、部署手册、测试用例。这五份不是让老师看的摆设每一份都有自己的用途。需求说明讲清楚「系统要解决什么问题、有哪些角色、每个角色能干什么」。资产管理员管理资产、员工申请领用、系统自动算折旧把这三条主线写明白就够了。数据库设计文档对应第 2 章的建表 SQL但要额外画出表之间的关系asset 通过 category_id 关联 category通过 borrow_record 关联 employee。接口与页面说明列出每个页面的 URL、功能、表单提交到哪——这份文档在二次开发时最有用改功能时先查文档定位文件而不是到处翻代码。部署手册要写清楚环境要求、配置修改点、导入数据库的步骤。测试用例列出主要功能怎么验证比如「新增资产→列表可见→详情页折旧金额正确」。答辩时老师如果问「你测过吗」把测试用例表拿出来比嘴上说「我运行过没问题」有说服力得多。4.2 读源码的顺序从入口文件到业务页面拿到别人的 PHP 项目我一般按「入口 → 配置 → 公共函数 → 业务页面 → 模板」的顺序读。先打开入口文件看它 require 了哪些公共文件再打开 config.php看数据库和站点配置然后看公共函数文件了解有哪些现成工具函数最后才进入具体业务页面。这样读 20 分钟就能画出整个项目的结构图比自己一头扎进某个页面高效得多。读代码时优先关注三件事require_once引入了哪些文件、$_GET和$_POST从哪里取数据、SQL 语句操作了哪些表。跟着一条数据流走一遍用户访问列表页 → PHP 接收参数 → 拼接 SQL → 查询结果 → 循环输出到 HTML一个页面的完整链路就通了。4.3 二次开发时怎么定位修改点二次开发的核心不是通读全部代码而是精准定位「我要改的东西在哪」。想改资产列表显示的字段就在文件里搜asset_list.php打开后看 SELECT 查了哪些列HTML 里 echo 了哪些列两边对上就能改。想加一个「资产导出」功能先找现有的列表页参考它的查询逻辑加一个 CSV 导出分支。这里有一个血泪经验修改前一定先备份而且最好用 Git 管理哪怕是在本地用。课设周期里改崩代码是家常便饭没有版本回退的后悔药出了事只能手工找哪一行改坏了。用 Git 的话每次改动前git commit一下改崩了git checkout直接回到改之前的样子省下的时间远超学那几个命令的成本。5. 部署与常见问题避坑从白屏到中文乱码的五个翻车点部署环节是课设提交前最后一关也是翻车最密集的环节。这里把最常见的五个问题按「现象 → 原因 → 解决」写清楚每条都对应一个真实踩过的坑。5.1 环境搭建PHP 版本与扩展检查本地跑 PHP MySQL十个人里有八个用 phpStudy 或 XAMPP 这类集成环境直接装好后启动 Apache 和 MySQL 就行。第一个要确认的是 PHP 版本和扩展。打开命令行执行php -v看版本同时确认 PDO 扩展已启用运行php -m能看到pdo_mysql才算正常。php -v php -m | grep pdo_mysql如果pdo_mysql不在列表里找到 PHP 安装目录的php.ini去掉extensionpdo_mysql前面的分号重启服务再看。这一步漏了的话第 2 章的db()函数一执行就会报「找不到驱动」页面直接白屏。环境问题排查思路是先确认版本再确认扩展最后确认数据库服务有没有起来不要一上来就怀疑代码。5.2 踩坑记录一页面白屏错误却被藏起来了现象浏览器访问页面一片空白什么都不输出。原因PHP 配置里display_errors默认是 Off代码里的语法错误或异常被吞掉了不显示也不记录。解决临时打开错误显示定位问题后再关掉。display_errors On error_reporting E_ALL改完php.ini重启 Apache再访问页面具体的错误信息就会打印出来。这里有个细节display_errors On只适合开发环境部署到正式环境必须改回 Off并把log_errors On打开让错误写进日志文件否则错误信息会直接暴露给访问者。5.3 踩坑记录二中文乱码的三个不一致现象页面和数据库里的中文变成问号或乱码。原因PHP 文件编码、HTML 的 charset、MySQL 连接字符集、数据表 collation 四处不一致。解决全部统一到 utf8mb4。PHP 文件存成 UTF-8 无 BOM 格式HTML 头部写meta charsetutf-8连接字符集已在第 2 章的 DSN 里指定建表 SQL 里也用DEFAULT CHARSETutf8mb4。这四个地方只要有一个不一致就可能出现乱码尤其是用记事本编辑过 PHP 文件、存成了带 BOM 的 UTF-8 的情况页面顶部会输出一个不可见字符导致header()函数报错。5.4 踩坑记录三SQL 导入失败与默认值报错现象在 phpMyAdmin 里导入 .sql 文件提示某个字段默认值错误或语法错误。原因SQL 文件是从 MySQL 8.0 导出、导入到 MySQL 5.7或者反过来。两个版本对DEFAULT CURRENT_TIMESTAMP和sql_mode的处理不同导出的文件里带了高版本才支持的特性。解决用 phpMyAdmin 的「导入」功能而不是手动复制执行导入前确认 SQL 文件开头的版本注释如果还报错把报错信息里提到的sql_mode相关设置贴到 MySQL 配置文件里调整。最稳妥的做法是直接用第 2.3 节手工执行建表语句而不是依赖导入别人导出的文件。5.5 踩坑记录四PHP 8 下函数兼容性问题现象源码在 PHP 5.x 或 7.x 跑得好好的换到 PHP 8 之后页面直接报错或某个功能消失。原因老代码用了 PHP 8 里已经移除的函数或语法。影响最大的是mysql_*系列函数PHP 7 就移除了、each()函数、以及${var}字符串插值语法PHP 8.2 废弃。解决把mysql_connect、mysql_query这类调用全部换成 PDO 或 MySQLi 实现这也是第 2 章直接用 PDO 的原因之一。用 PHP 8 跑项目前先全局搜索一下代码里有没有已废弃的写法grep -rn mysql_query\|mysql_connect\|each( ./asset/搜出来的结果逐一替换成 PDO 写法。这个坑在课设里特别常见因为下载的老源码大多写在 PHP 5 时代而本地装的集成环境默认 PHP 8。6. 把课设往可落地再推一步报表导出、盘点与验收清单到这里系统已经能跑但还能再往前走一步。课设评审看的不是「能跑」而是「考虑到了实际使用中会碰到的需求」。报表导出和盘点模式是真实固定资产管理里最高频的两个功能代码改动不大却能显著提升系统的完成度。6.1 给系统加一个报表导出用fputcsv可以直接生成 CSV 文件不需要引入第三方库在 report.php 里加一个参数就能实现?php // report.php?export1 触发导出 if (isset($_GET[export]) $_GET[export] 1) { header(Content-Type: text/csv; charsetutf-8); header(Content-Disposition: attachment; filenameassets_ . date(Ymd) . .csv); $out fopen(php://output, w); // 表头 fputcsv($out, [资产编号, 名称, 分类, 原值, 净值, 状态]); $stmt $pdo-query(SELECT a.asset_no, a.name, c.name, a.price, a.buy_date, a.status FROM asset a LEFT JOIN category c ON a.category_id c.id); foreach ($stmt as $row) { // 计算净值复用第3章的折旧函数 $dep calc_depreciation((float)$row[price], $row[buy_date]); fputcsv($out, [$row[asset_no], $row[name], $row[c.name] ?? , $row[price], $dep[net_value], $row[status]]); } fclose($out); exit; }注意fputcsv输出的 CSV 用 Excel 打开时中文可能乱码原因是 Excel 默认按 GBK 读取而文件是 UTF-8 编码。解决办法是在输出前加一行echo \xEF\xBB\xBF;输出 BOMExcel 就会正确识别 UTF-8。这个细节不处理导出的文件在 Excel 里中文全乱印象分直接扣掉。6.2 盘点场景的轻量扫码改造真实场景里资产盘点用的是扫码枪但扫码枪的本质就是一个快速键盘输入设备——扫一下资产条码相当于把条码内容快速敲进输入框再回车。所以系统不需要真的接摄像头识别只需要做一个盘点页面一个输入框扫码后自动提交查询显示资产信息和当前状态核对无误后打个勾。代码上就是在输入框监听回车事件提交当前资产编号到后端查询返回 JSON 数据页面用 JavaScript 渲染结果。扫码枪的输入速度很快通常会自动带一个回车符所以表单的提交按钮甚至不需要点扫完直接出结果。这个改造量不大但答辩演示时扫一下条码、页面立刻显示资产信息的效果比对着截图讲有冲击力得多。6.3 交作业前按这份清单验证最后整理一份验收清单提交前过一遍基本能避开九成的低级问题检查项验证方法预期结果数据库连接登录系统打开资产列表页面正常显示无数据库报错新增资产提交完整表单列表页出现新记录编号重复时被拦截折旧计算查看一条超3年的资产详情净值 原值 - 已折旧金额与手算一致领用归还流程领用→状态变已领用→归还→状态变在库状态流转正确记录留痕中文显示新增一条含生僻字的资产页面和数据库均为正常文字无乱码分页与搜索录入20条数据按名称关键字搜索第2页可翻搜索结果准确导出报表点击导出CSV 文件下载Excel 打开中文不乱码这套系统做下来我的习惯是在提交前用一份全新数据库从头跑一遍完整流程装环境、导表、注册账号、录资产、领用、归还、导出。用新库跑一遍能暴露很多「我本地明明没问题」的隐藏 bug比如别人拿到源码后数据库连不上、表结构对不上、PHP 版本不兼容。这类问题在答辩现场翻车是最尴尬的提前花十分钟自测一遍就能把风险压到最低。希望这份笔记能帮你少踩几个坑把课设做成自己真正讲得清、改得动、拿得出手的东西。本文还有配套的精品资源点击获取
返回列表