feat: 初始化智能物联网设备管理系统前后端基础项目

完成项目整体结构搭建,包含:
1. 前端Vue3+TS+Vite+Antd项目基础配置与核心依赖
2. 后端MidwayJS+TypeORM+JWT权限认证框架
3. 完整的用户、角色、权限、日志数据库表结构
4. 核心业务模块API与基础服务实现
5. 全局样式、路由、状态管理配置
This commit is contained in:
andyesfly committed 2026-05-28 16:23:15 +08:00
commit 3d7eb12103
89 files changed
+16085

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
import { Middleware, IMiddleware, Config } from '@midwayjs/core';
import { Context, NextFunction } from '@midwayjs/koa';
import { Provide, Inject } from '@midwayjs/core';
import { TokenUtil } from '../util/token';
@Provide()
export class AuthMiddleware implements IMiddleware<Context, NextFunction> {
@Inject()
tokenUtil!: TokenUtil;
resolve() {
return async (ctx: Context, next: NextFunction) => {
const authHeader = ctx.header.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
ctx.status = 401;
ctx.body = {
code: 401,
message: '未提供认证令牌',
};
return;
}
const token = authHeader.substring(7);
try {
const decoded = this.tokenUtil.verifyToken(token);
ctx.state.user = {
userId: decoded.userId,
username: decoded.username,
};
ctx.user = ctx.state.user;
await next();
} catch (error: any) {
if (error.name === 'TokenExpiredError') {
ctx.status = 401;
ctx.body = {
code: 401,
message: '登录已过期,请重新登录',
expired: true,
};
} else {
ctx.status = 401;
ctx.body = {
code: 401,
message: '无效的认证令牌',
};
}
}
};
}
}
+33
View File
@@ -0,0 +1,33 @@
import { Middleware } from '@midwayjs/core';
import { Context, NextFunction } from '@midwayjs/koa';
@Middleware()
export class ErrorMiddleware {
static getName(): string {
return 'error';
}
resolve() {
return async (ctx: Context, next: NextFunction) => {
try {
await next();
} catch (err: any) {
console.error('全局错误捕获:', err);
const status = err.status || 500;
const message = err.message || '服务器内部错误';
ctx.status = status;
ctx.body = {
code: status,
message: message,
data: null,
};
}
};
}
ignore(ctx: Context): boolean {
return false;
}
}
+119
View File
@@ -0,0 +1,119 @@
import { Middleware, IMiddleware } from '@midwayjs/core';
import { Context, NextFunction } from '@midwayjs/koa';
import { Provide, Inject } from '@midwayjs/core';
import { InjectRepository } from '@midwayjs/sequelize';
import OperationLog from '../entity/operationLog.entity';
@Provide()
export class LogMiddleware implements IMiddleware<Context, NextFunction> {
@InjectRepository(OperationLog)
logModel!: typeof OperationLog;
resolve() {
return async (ctx: Context, next: NextFunction) => {
const startTime = Date.now();
let logData: any = {};
try {
logData = {
method: ctx.method,
url: ctx.url,
ipAddress: ctx.ip,
userAgent: ctx.header['user-agent'],
requestParams: JSON.stringify(ctx.request.body || {}),
};
if (ctx.state.user) {
logData.userId = ctx.state.user.userId;
logData.username = ctx.state.user.username;
}
await next();
logData.statusCode = ctx.status;
logData.duration = Date.now() - startTime;
if (['POST', 'PUT', 'DELETE'].includes(ctx.method)) {
logData.action = this.getActionDescription(ctx);
logData.module = this.getModuleFromUrl(ctx.url);
this.saveLog(logData).catch(err => {
console.error('保存操作日志失败:', err);
});
}
} catch (error: any) {
logData.statusCode = 500;
logData.duration = Date.now() - startTime;
logData.errorMessage = error.message;
this.saveLog(logData).catch(() => {});
throw error;
}
};
}
private getActionDescription(ctx: Context): string {
const actionMap: Record<string, Record<string, string>> = {
'/api/auth/login': { POST: '用户登录' },
'/api/auth/logout': { POST: '用户登出' },
'/api/user': {
POST: '创建用户',
PUT: '更新用户',
DELETE: '删除用户',
},
'/api/role': {
POST: '创建角色',
PUT: '更新角色',
DELETE: '删除角色',
},
'/api/settings': {
PUT: '修改系统设置',
},
};
const basePath = ctx.url.split('?')[0];
if (actionMap[basePath]?.[ctx.method]) {
return actionMap[basePath][ctx.method];
}
for (const [path, actions] of Object.entries(actionMap)) {
if (basePath.startsWith(path) && actions[ctx.method]) {
return actions[ctx.method];
}
}
return `${ctx.method} ${basePath}`;
}
private getModuleFromUrl(url: string): string {
const moduleMap: Record<string, string> = {
'/api/auth': '认证',
'/api/user': '用户管理',
'/api/role': '角色管理',
'/api/settings': '系统设置',
'/api/log': '日志管理',
'/api/logs': '日志管理',
'/api/dashboard': '仪表盘',
};
for (const [path, module] of Object.entries(moduleMap)) {
if (url.startsWith(path)) {
return module;
}
}
return '其他';
}
private async saveLog(logData: any): Promise<void> {
try {
await this.logModel.create(logData);
} catch (error) {
console.error('写入操作日志失败:', error);
}
}
}
+74
View File
@@ -0,0 +1,74 @@
import { Middleware, IMiddleware } from '@midwayjs/core';
import { Context, NextFunction } from '@midwayjs/koa';
import { Provide, Inject } from '@midwayjs/core';
import { PermissionService } from '../service/permission.service';
@Provide()
export class RbacMiddleware implements IMiddleware<Context, NextFunction> {
@Inject()
permissionService!: PermissionService;
resolve() {
return async (ctx: Context, next: NextFunction) => {
const user = ctx.state.user;
if (!user) {
ctx.status = 401;
ctx.body = {
code: 401,
message: '未登录',
};
return;
}
// 获取当前请求路径和方法,转换为权限码
// 例如:GET /api/user/list -> user:list
const permissionCode = this.extractPermissionCode(ctx);
// 超级管理员拥有所有权限
if (user.roles && user.roles.includes('super_admin')) {
await next();
return;
}
// 检查用户是否拥有该权限
const hasPermission = await this.permissionService.checkUserPermission(
user.userId,
permissionCode
);
if (!hasPermission) {
ctx.status = 403;
ctx.body = {
code: 403,
message: '没有访问权限',
};
return;
}
await next();
};
}
private extractPermissionCode(ctx: Context): string {
const url = ctx.path.replace('/api/', '').replace(/^\//, '');
const method = ctx.method.toLowerCase();
const methodMap: Record<string, string> = {
post: 'create',
put: 'update',
delete: 'delete',
get: 'list',
};
const action = methodMap[method] || method;
const parts = url.split('/');
if (parts.length >= 2) {
return `${parts[0]}:${action}`;
}
return `${url}:${action}`;
}
}