去年搬家时,我发现自己有23箱书、5台显示器支架和无数找不到的数据线。这种混乱促使我开发了这套个人物品管理系统。它采用主流的前后端分离架构,后端基于Node.js+Express提供API服务,前端使用Vue3+Element Plus构建响应式界面,数据库选用MySQL 8.0。系统上线半年后,我的物品找回率提升87%,闲置物品利用率提高42%。
这套系统特别适合以下场景:
选择Node.js+Express而非Java/SpringBoot主要基于:
关键中间件配置示例:
javascript复制// 身份验证中间件
app.use(jwt({
secret: process.env.JWT_SECRET,
algorithms: ['HS256'],
getToken: req => req.cookies.token
}).unless({
path: ['/api/login', '/api/register']
}));
采用Vue3组合式API相比选项式API的优势:
典型组件结构:
vue复制<template>
<el-card class="item-card">
<img :src="item.image" />
<div class="meta">
<h3>{{ item.name }}</h3>
<el-tag>{{ item.category }}</el-tag>
</div>
</el-card>
</template>
<script setup>
defineProps({
item: {
type: Object,
required: true
}
})
</script>
实现原理:
sql复制ALTER TABLE items
ADD INDEX idx_search (name, category, location);
javascript复制const searchItems = async (keyword) => {
return await Item.find({
$or: [
{ name: { $regex: keyword, $options: 'i' } },
{ description: { $regex: keyword, $options: 'i' } }
]
}).sort({ updateTime: -1 });
}
采用的技术组合:
javascript复制const upload = multer({
storage: multer.diskStorage({
destination: 'uploads/',
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`)
}
}),
fileFilter: (req, file, cb) => {
if (!file.mimetype.match(/\/(jpg|jpeg|png)$/)) {
return cb(new Error('仅支持JPG/PNG格式'))
}
cb(null, true)
},
limits: { fileSize: 5 * 1024 * 1024 } // 5MB
})
核心表关系图:
code复制users
└── items (1:n)
├── categories (n:1)
├── locations (n:1)
└── tags (n:m)
关键字段示例:
sql复制CREATE TABLE `items` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`description` TEXT,
`quantity` INT DEFAULT 1,
`category_id` INT,
`location_id` INT,
`user_id` BIGINT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
实测优化效果对比:
| 优化措施 | 查询耗时(ms) | QPS提升 |
|---|---|---|
| 无索引 | 320 | 基准 |
| 添加单列索引 | 85 | 276% |
| 使用覆盖索引 | 42 | 662% |
| 添加内存缓存 | 18 | 1677% |
缓存实现代码片段:
javascript复制const cache = new NodeCache({
stdTTL: 3600, // 1小时过期
checkperiod: 600
});
router.get('/items', async (req, res) => {
const cacheKey = `user_${req.user.id}_items`;
let data = cache.get(cacheKey);
if (!data) {
data = await Item.findAll({ where: { userId: req.user.id } });
cache.set(cacheKey, data);
}
res.json(data);
});
推荐架构:
code复制Nginx (负载均衡)
├── Node.js PM2集群 (4核CPU启4实例)
└── MySQL主从复制
PM2配置示例:
json复制{
"apps": [{
"name": "item-mgr",
"script": "app.js",
"instances": "max",
"exec_mode": "cluster",
"env": {
"NODE_ENV": "production",
"PORT": 3000
}
}]
}
必备监控指标:
使用PM2内建监控:
bash复制pm2 monit
pm2 logs --lines 200
问题现象:服务器内存持续增长直至崩溃
根本原因:未处理multipart/form-data的临时文件
解决方案:
javascript复制app.use('/upload',
upload.single('image'),
(req, res, next) => {
// 必须消费完文件流
req.on('data', () => {});
req.on('end', next);
}
);
问题现象:列表页快速滚动时卡顿
优化方案:
vue复制<div v-for="item in items" v-memo="[item.id]">
<!-- 子组件 -->
</div>
javascript复制import { VirtualScroller } from 'vue-virtual-scroller'
components: { VirtualScroller }
核心流程设计:
code复制借出流程:
1. 扫描物品二维码
2. 选择借用人
3. 设置归还时间
4. 生成借出记录
归还流程:
1. 扫描物品或借出记录
2. 确认物品状态
3. 完成归还
数据模型变更:
sql复制ALTER TABLE items ADD COLUMN `status` ENUM('available', 'lent', 'maintenance');
CREATE TABLE `lending_records` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`item_id` BIGINT NOT NULL,
`lender_id` BIGINT NOT NULL,
`borrower_id` BIGINT NOT NULL,
`lend_time` DATETIME NOT NULL,
`due_time` DATETIME NOT NULL,
`return_time` DATETIME,
`notes` TEXT,
FOREIGN KEY (`item_id`) REFERENCES `items`(`id`)
);
实施要点:
javascript复制// vite.config.js
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
manifest: {
name: '物品管理',
short_name: 'ItemMgr'
}
})
]
})
这套系统经过三次迭代后,代码行数达到12,000+,包含58个Vue组件和32个API端点。实际运行中,单台2核4G服务器可支撑800+日活用户。对于想深入学习的开发者,建议从物品分类模块入手,逐步扩展到用户权限和数据分析模块。