mirror of
https://github.com/pure-admin/pure-admin-backend.git
synced 2025-04-24 23:37:17 +08:00
feat: 添加登录、用户管理和操作日志功能
This commit is contained in:
parent
f31bf052a1
commit
e45c367e15
2
.env
2
.env
@ -1,5 +1,5 @@
|
|||||||
# Port
|
# Port
|
||||||
PORT=3000
|
PORT=3001
|
||||||
|
|
||||||
# JWT_SECRET
|
# JWT_SECRET
|
||||||
JWT_SECRET = '708DD1DC5BC5A169'
|
JWT_SECRET = '708DD1DC5BC5A169'
|
||||||
|
BIN
data/teachers.xlsx
Normal file
BIN
data/teachers.xlsx
Normal file
Binary file not shown.
@ -33,5 +33,9 @@
|
|||||||
"typescript": "^4.8.4",
|
"typescript": "^4.8.4",
|
||||||
"winston": "^3.8.2"
|
"winston": "^3.8.2"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/xiaoxian521/pure-admin-backend"
|
"repository": "https://github.com/xiaoxian521/pure-admin-backend",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/xlsx": "^0.0.36",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
2984
pnpm-lock.yaml
generated
2984
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,7 @@
|
|||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
// Begin Swagger UI call region
|
// Begin Swagger UI call region
|
||||||
const ui = SwaggerUIBundle({
|
const ui = SwaggerUIBundle({
|
||||||
url: "http://localhost:3000/swagger.json",
|
url: "http://localhost:3001/swagger.json",
|
||||||
dom_id: '#swagger-ui',
|
dom_id: '#swagger-ui',
|
||||||
deepLinking: true,
|
deepLinking: true,
|
||||||
presets: [
|
presets: [
|
||||||
|
@ -15,7 +15,7 @@ export default {
|
|||||||
options: {
|
options: {
|
||||||
swaggerDefinition: {
|
swaggerDefinition: {
|
||||||
info: {
|
info: {
|
||||||
description: "pure-admin官方后端",
|
description: "HOUKONG",
|
||||||
title: "Swagger",
|
title: "Swagger",
|
||||||
version: require("../../package.json").version,
|
version: require("../../package.json").version,
|
||||||
},
|
},
|
||||||
@ -51,10 +51,11 @@ export default {
|
|||||||
concurrency: parseInt(process.env.AGENDA_CONCURRENCY, 10),
|
concurrency: parseInt(process.env.AGENDA_CONCURRENCY, 10),
|
||||||
},
|
},
|
||||||
mysql: {
|
mysql: {
|
||||||
host: "localhost",
|
host: "43.156.106.134",
|
||||||
charset: "utf8_general_ci",
|
charset: "utf8_general_ci",
|
||||||
user: "root",
|
user: "houkong",
|
||||||
password: "123456789",
|
password: "P@55w0rd",
|
||||||
|
database: "houkong",
|
||||||
},
|
},
|
||||||
mongodb: {},
|
mongodb: {},
|
||||||
sqlite: {},
|
sqlite: {},
|
||||||
|
@ -1,5 +1,61 @@
|
|||||||
/** 创建用户表 */
|
/** 创建用户表 */
|
||||||
const user =
|
const user = `
|
||||||
"CREATE TABLE if not EXISTS users(id int PRIMARY key auto_increment,username varchar(32),password varchar(32),time DATETIME)";
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
username VARCHAR(100) NOT NULL COMMENT '用户名',
|
||||||
|
userid VARCHAR(100) NOT NULL COMMENT '企业微信用户ID',
|
||||||
|
name VARCHAR(100) NOT NULL COMMENT '用户名称',
|
||||||
|
password VARCHAR(100) NOT NULL COMMENT '密码',
|
||||||
|
department VARCHAR(255) COMMENT '部门',
|
||||||
|
position VARCHAR(100) COMMENT '职位',
|
||||||
|
mobile VARCHAR(20) COMMENT '手机号',
|
||||||
|
gender VARCHAR(10) COMMENT '性别',
|
||||||
|
email VARCHAR(100) COMMENT '邮箱',
|
||||||
|
avatar VARCHAR(255) COMMENT '头像URL',
|
||||||
|
status TINYINT DEFAULT 1 COMMENT '状态 1:启用 0:禁用',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'
|
||||||
|
`;
|
||||||
|
|
||||||
|
/** 创建角色表 */
|
||||||
|
const role = `
|
||||||
|
CREATE TABLE IF NOT EXISTS roles (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(50) NOT NULL COMMENT '角色名称',
|
||||||
|
code VARCHAR(50) NOT NULL COMMENT '角色编码',
|
||||||
|
status TINYINT DEFAULT 1 COMMENT '状态 1:启用 0:禁用',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY idx_code (code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表'
|
||||||
|
`;
|
||||||
|
|
||||||
|
/** 创建用户角色关联表 */
|
||||||
|
const userRole = `
|
||||||
|
CREATE TABLE IF NOT EXISTS user_roles (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user_id INT NOT NULL COMMENT '用户ID',
|
||||||
|
role_id INT NOT NULL COMMENT '角色ID',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
KEY idx_user_id (user_id),
|
||||||
|
KEY idx_role_id (role_id)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表'
|
||||||
|
`;
|
||||||
|
|
||||||
|
/** 创建操作日志表 */
|
||||||
|
const operationLogs = `
|
||||||
|
CREATE TABLE IF NOT EXISTS operation_logs (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
username VARCHAR(255),
|
||||||
|
action VARCHAR(255) NOT NULL COMMENT '操作类型:新增、修改、删除等',
|
||||||
|
module VARCHAR(255) NOT NULL COMMENT '操作模块:用户管理、角色管理等',
|
||||||
|
description TEXT COMMENT '操作详细描述',
|
||||||
|
ip VARCHAR(50) COMMENT '操作IP',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='操作日志表'
|
||||||
|
`;
|
||||||
|
|
||||||
|
export { operationLogs, role, user, userRole };
|
||||||
|
|
||||||
export { user };
|
|
||||||
|
139
src/router/excel.ts
Normal file
139
src/router/excel.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { createHash } from "crypto";
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import * as path from 'path';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
import * as xlsx from 'xlsx';
|
||||||
|
import { connection } from "../utils/mysql";
|
||||||
|
|
||||||
|
const query = promisify(connection.query).bind(connection);
|
||||||
|
const beginTransaction = promisify(connection.beginTransaction).bind(connection);
|
||||||
|
const commit = promisify(connection.commit).bind(connection);
|
||||||
|
const rollback = promisify(connection.rollback).bind(connection);
|
||||||
|
|
||||||
|
export async function importUsersFromLocalExcel(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(__dirname, '../../data/teachers.xlsx');
|
||||||
|
console.log('Excel文件路径:', filePath);
|
||||||
|
|
||||||
|
const workbook = xlsx.readFile(filePath);
|
||||||
|
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||||
|
|
||||||
|
// 使用之前成功的格式
|
||||||
|
const rows = xlsx.utils.sheet_to_json<{
|
||||||
|
A: number; // 序号
|
||||||
|
B: string; // 姓名
|
||||||
|
C: string; // 工号
|
||||||
|
D: string; // 密码
|
||||||
|
E: string; // 角色
|
||||||
|
F: string; // 部门
|
||||||
|
}>(worksheet, {
|
||||||
|
range: 1, // 从第二行开始读取
|
||||||
|
header: 'A' // 使用字母作为键
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('读取到的数据行数:', rows.length);
|
||||||
|
console.log('第一行数据示例:', rows[0]);
|
||||||
|
|
||||||
|
await beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
let successCount = 0;
|
||||||
|
let errorCount = 0;
|
||||||
|
const errors: string[] = [];
|
||||||
|
|
||||||
|
// 获取默认教师角色ID
|
||||||
|
const DEFAULT_ROLE_ID = 2; // 教師角色ID
|
||||||
|
const roleMap = new Map<string, number>();
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
try {
|
||||||
|
// 验证必要字段
|
||||||
|
if (!row.B || !row.C || !row.D) {
|
||||||
|
throw new Error(`数据不完整: 姓名=${row.B}, 工号=${row.C}, 密码=${row.D}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取角色ID
|
||||||
|
let roleId = DEFAULT_ROLE_ID; // 默认使用教师角色
|
||||||
|
if (row.E) {
|
||||||
|
if (!roleMap.has(row.E)) {
|
||||||
|
const existingRoles: any[] = await query(
|
||||||
|
"SELECT id FROM roles WHERE name = ?",
|
||||||
|
[row.E]
|
||||||
|
);
|
||||||
|
if (existingRoles.length > 0) {
|
||||||
|
roleMap.set(row.E, existingRoles[0].id);
|
||||||
|
roleId = existingRoles[0].id;
|
||||||
|
} else {
|
||||||
|
console.log(`找不到角色 ${row.E},使用默认教师角色`);
|
||||||
|
roleMap.set(row.E, DEFAULT_ROLE_ID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
roleId = roleMap.get(row.E)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashedPassword = createHash("md5")
|
||||||
|
.update(row.D)
|
||||||
|
.digest("hex");
|
||||||
|
|
||||||
|
// 插入用户
|
||||||
|
const result: any = await query(
|
||||||
|
`INSERT INTO users (
|
||||||
|
username, name, password, department,
|
||||||
|
position, status
|
||||||
|
) VALUES (?, ?, ?, ?, ?, 1)`,
|
||||||
|
[
|
||||||
|
row.C, // username (工号)
|
||||||
|
row.B, // name (姓名)
|
||||||
|
hashedPassword, // password
|
||||||
|
row.F || '', // department
|
||||||
|
row.E || '教師', // position
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 插入用户角色关联
|
||||||
|
await query(
|
||||||
|
"INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)",
|
||||||
|
[result.insertId, roleId]
|
||||||
|
);
|
||||||
|
|
||||||
|
successCount++;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理用户错误:', error);
|
||||||
|
errorCount++;
|
||||||
|
errors.push(`行 ${successCount + errorCount}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await commit();
|
||||||
|
console.log('事务提交成功');
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
message: "导入完成",
|
||||||
|
stats: {
|
||||||
|
total: rows.length,
|
||||||
|
success: successCount,
|
||||||
|
error: errorCount
|
||||||
|
},
|
||||||
|
errors: errors
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理数据错误:', error);
|
||||||
|
await rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('导入用户错误:', error);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
data: {
|
||||||
|
message: "导入失败",
|
||||||
|
error: error.message
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,15 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import secret from "../config";
|
|
||||||
import * as mysql from "mysql2";
|
|
||||||
import * as jwt from "jsonwebtoken";
|
|
||||||
import { createHash } from "crypto";
|
import { createHash } from "crypto";
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as jwt from "jsonwebtoken";
|
||||||
|
import * as mysql from "mysql2";
|
||||||
|
import { OkPacket } from 'mysql2';
|
||||||
|
import { createMathExpr } from "svg-captcha";
|
||||||
|
import secret from "../config";
|
||||||
import Logger from "../loaders/logger";
|
import Logger from "../loaders/logger";
|
||||||
import { Message } from "../utils/enums";
|
import { Message } from "../utils/enums";
|
||||||
import getFormatDate from "../utils/date";
|
|
||||||
import { connection } from "../utils/mysql";
|
import { connection } from "../utils/mysql";
|
||||||
import { Request, Response } from "express";
|
import { logOperation, ModuleType, OperationType } from "../utils/operationLog";
|
||||||
import { createMathExpr } from "svg-captcha";
|
|
||||||
|
|
||||||
const utils = require("@pureadmin/utils");
|
const utils = require("@pureadmin/utils");
|
||||||
|
|
||||||
@ -56,78 +57,153 @@ let expiresIn = 60000;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const login = async (req: Request, res: Response) => {
|
const login = async (req: Request, res: Response) => {
|
||||||
// const { username, password, verify } = req.body;
|
|
||||||
// if (generateVerify !== verify) return res.json({
|
|
||||||
// success: false,
|
|
||||||
// data: {
|
|
||||||
// message: Message[0];
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
let sql: string =
|
|
||||||
"select * from users where username=" + "'" + username + "'";
|
console.log('Login attempt:', { username, password });
|
||||||
connection.query(sql, async function (err, data: any) {
|
|
||||||
if (data.length == 0) {
|
let sql = `
|
||||||
|
SELECT u.*, GROUP_CONCAT(r.code) as roles
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN user_roles ur ON u.id = ur.user_id
|
||||||
|
LEFT JOIN roles r ON ur.role_id = r.id
|
||||||
|
WHERE u.username = ? AND u.status = 1
|
||||||
|
GROUP BY u.id
|
||||||
|
`;
|
||||||
|
|
||||||
|
connection.query(sql, [username], async function (err, data: any) {
|
||||||
|
// console.log('Query result:', { err, data });
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
// Logger.error(err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "数据库查询错误" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
// 记录登录失败日志
|
||||||
|
logOperation({
|
||||||
|
userId: 0,
|
||||||
|
username: username,
|
||||||
|
action: OperationType.LOGIN,
|
||||||
|
module: ModuleType.AUTH,
|
||||||
|
description: `用户登录失败:用户不存在 (${username})`,
|
||||||
|
ip: req.ip || ''
|
||||||
|
});
|
||||||
|
|
||||||
await res.json({
|
await res.json({
|
||||||
success: false,
|
success: false,
|
||||||
data: { message: Message[1] },
|
data: { message: Message[1] } // 用户不存在
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (
|
const user = data[0];
|
||||||
createHash("md5").update(password).digest("hex") == data[0].password
|
|
||||||
) {
|
// 验证密码
|
||||||
const accessToken = jwt.sign(
|
const hashedPassword = createHash("md5").update(password).digest("hex");
|
||||||
{
|
if (hashedPassword !== user.password) {
|
||||||
accountId: data[0].id,
|
// 记录登录失败日志
|
||||||
},
|
logOperation({
|
||||||
secret.jwtSecret,
|
userId: 0,
|
||||||
{ expiresIn }
|
username: username,
|
||||||
);
|
action: OperationType.LOGIN,
|
||||||
if (username === "admin") {
|
module: ModuleType.AUTH,
|
||||||
await res.json({
|
description: `用户登录失败:密码错误 (${username})`,
|
||||||
success: true,
|
ip: req.ip || ''
|
||||||
data: {
|
});
|
||||||
message: Message[2],
|
return res.json({
|
||||||
username,
|
|
||||||
// 这里模拟角色,根据自己需求修改
|
|
||||||
roles: ["admin"],
|
|
||||||
accessToken,
|
|
||||||
// 这里模拟刷新token,根据自己需求修改
|
|
||||||
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
|
|
||||||
expires: new Date(new Date()).getTime() + expiresIn,
|
|
||||||
// 这个标识是真实后端返回的接口,只是为了演示
|
|
||||||
pureAdminBackend:
|
|
||||||
"这个标识是pure-admin-backend真实后端返回的接口,只是为了演示",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await res.json({
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
message: Message[2],
|
|
||||||
username,
|
|
||||||
// 这里模拟角色,根据自己需求修改
|
|
||||||
roles: ["common"],
|
|
||||||
accessToken,
|
|
||||||
// 这里模拟刷新token,根据自己需求修改
|
|
||||||
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
|
|
||||||
expires: new Date(new Date()).getTime() + expiresIn,
|
|
||||||
// 这个标识是真实后端返回的接口,只是为了演示
|
|
||||||
pureAdminBackend:
|
|
||||||
"这个标识是pure-admin-backend真实后端返回的接口,只是为了演示",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await res.json({
|
|
||||||
success: false,
|
success: false,
|
||||||
data: { message: Message[3] },
|
data: { message: "密码错误" }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accessToken = jwt.sign(
|
||||||
|
{
|
||||||
|
id: user.id, // 用户ID
|
||||||
|
username: user.username, // 用户名
|
||||||
|
name: user.name
|
||||||
|
},
|
||||||
|
secret.jwtSecret,
|
||||||
|
{ expiresIn }
|
||||||
|
);
|
||||||
|
// 记录登录成功日志
|
||||||
|
logOperation({
|
||||||
|
userId: user.id,
|
||||||
|
username: user.username,
|
||||||
|
action: OperationType.LOGIN,
|
||||||
|
module: ModuleType.AUTH,
|
||||||
|
description: `用户登录成功 (${username})`,
|
||||||
|
ip: req.ip || ''
|
||||||
|
});
|
||||||
|
await res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
message: Message[2],
|
||||||
|
username: user.name, // 返回 name 字段作为用户名
|
||||||
|
userid: user.userid, // 返回 userid
|
||||||
|
roles: user.roles ? user.roles.split(',') : [],
|
||||||
|
accessToken,
|
||||||
|
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
|
||||||
|
expires: new Date(new Date()).getTime() + expiresIn,
|
||||||
|
department: user.department,
|
||||||
|
position: user.position,
|
||||||
|
mobile: user.mobile,
|
||||||
|
email: user.email,
|
||||||
|
avatar: user.avatar
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
// 刷新 token
|
||||||
|
const refreshToken = async (req: Request, res: Response) => {
|
||||||
|
const { refreshToken } = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证 refresh token
|
||||||
|
const decoded = jwt.verify(refreshToken, secret.jwtSecret) as any;
|
||||||
|
|
||||||
|
// 查询用户是否还有效
|
||||||
|
const sql = `SELECT * FROM users WHERE id = ? AND status = 1`;
|
||||||
|
|
||||||
|
connection.query(sql, [decoded.id], async function (err, data: any) {
|
||||||
|
if (err || data.length === 0) {
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "用户不存在或已禁用" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = data[0];
|
||||||
|
// 生成新的 access token
|
||||||
|
const accessToken = jwt.sign(
|
||||||
|
{
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
name: user.name
|
||||||
|
},
|
||||||
|
secret.jwtSecret,
|
||||||
|
{ expiresIn }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
accessToken,
|
||||||
|
refreshToken: accessToken, // 简单起见,使用相同的 token
|
||||||
|
expires: new Date(new Date()).getTime() + expiresIn
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
data: {
|
||||||
|
message: "refresh token 无效"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
// /**
|
// /**
|
||||||
// * @typedef Register
|
// * @typedef Register
|
||||||
// * @property {string} username.required - 用户名
|
// * @property {string} username.required - 用户名
|
||||||
@ -153,58 +229,159 @@ const login = async (req: Request, res: Response) => {
|
|||||||
* @headers {string} 200.X-Expires-After
|
* @headers {string} 200.X-Expires-After
|
||||||
* @security JWT
|
* @security JWT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const register = async (req: Request, res: Response) => {
|
const register = async (req: Request, res: Response) => {
|
||||||
// const { username, password, verify } = req.body;
|
const userData = req.body;
|
||||||
const { username, password } = req.body;
|
|
||||||
// if (generateVerify !== verify)
|
if (userData.password.length < 6)
|
||||||
// return res.json({
|
|
||||||
// success: false,
|
|
||||||
// data: { message: Message[0] },
|
|
||||||
// });
|
|
||||||
if (password.length < 6)
|
|
||||||
return res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
data: { message: Message[4] },
|
data: { message: Message[4] },
|
||||||
});
|
});
|
||||||
let sql: string =
|
|
||||||
"select * from users where username=" + "'" + username + "'";
|
let sql = "SELECT * FROM users WHERE username = ?";
|
||||||
connection.query(sql, async (err, data: any) => {
|
|
||||||
|
connection.query(sql, [userData.username], async (err, data: any) => {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "数据库查询错误" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
await res.json({
|
await res.json({
|
||||||
success: false,
|
success: false,
|
||||||
data: { message: Message[5] },
|
data: { message: Message[5] },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let time = await getFormatDate();
|
const insertUserSql = `
|
||||||
let sql: string =
|
INSERT INTO users (
|
||||||
"insert into users (username,password,time) value(" +
|
username,
|
||||||
"'" +
|
userid,
|
||||||
username +
|
name,
|
||||||
"'" +
|
password,
|
||||||
"," +
|
department,
|
||||||
"'" +
|
position,
|
||||||
createHash("md5").update(password).digest("hex") +
|
mobile,
|
||||||
"'" +
|
gender,
|
||||||
"," +
|
email,
|
||||||
"'" +
|
avatar,
|
||||||
time +
|
status,
|
||||||
"'" +
|
created_at
|
||||||
")";
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW())
|
||||||
connection.query(sql, async function (err) {
|
`;
|
||||||
|
|
||||||
|
const values = [
|
||||||
|
userData.username,
|
||||||
|
userData.userid || null,
|
||||||
|
userData.name || userData.username,
|
||||||
|
createHash("md5").update(userData.password).digest("hex"),
|
||||||
|
userData.department || null,
|
||||||
|
userData.position || null,
|
||||||
|
userData.mobile || null,
|
||||||
|
userData.gender || null,
|
||||||
|
userData.email || null,
|
||||||
|
userData.avatar || null
|
||||||
|
];
|
||||||
|
|
||||||
|
connection.query(insertUserSql, values, async function (err, result: OkPacket) { // 指定 result 类型为 OkPacket
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.error(err);
|
Logger.error(err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "注册失败" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理角色关联
|
||||||
|
if (userData.roles && userData.roles.length > 0) {
|
||||||
|
const userId = result.insertId; // 现在 TypeScript 知道 result 有 insertId 属性
|
||||||
|
const insertRolesSql = `
|
||||||
|
INSERT INTO user_roles (user_id, role_id)
|
||||||
|
SELECT ?, id FROM roles WHERE code IN (?)
|
||||||
|
`;
|
||||||
|
|
||||||
|
connection.query(insertRolesSql, [userId, userData.roles], function(err) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "添加用户角色失败" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { message: Message[6] }
|
||||||
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: { message: Message[6] },
|
data: { message: Message[6] }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
// 添加获取用户列表的处理函数
|
||||||
|
const getUserList = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const authHeader = req.get("Authorization");
|
||||||
|
// console.log('收到的 Authorization header:', authHeader); // 添加日志看看请求头
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "未登录" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接查询用户列表,不做 token 验证
|
||||||
|
const sql = `
|
||||||
|
SELECT
|
||||||
|
u.*,
|
||||||
|
GROUP_CONCAT(r.code) as roles
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN user_roles ur ON u.id = ur.user_id
|
||||||
|
LEFT JOIN roles r ON ur.role_id = r.id
|
||||||
|
GROUP BY u.id
|
||||||
|
`;
|
||||||
|
|
||||||
|
connection.query(sql, (err, data: any) => {
|
||||||
|
if (err) {
|
||||||
|
// Logger.error('查询用户列表错误:', err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "获取用户列表失败" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('查询到的用户数据:', data); // 添加日志看看查询结果
|
||||||
|
|
||||||
|
const users = data.map(user => ({
|
||||||
|
...user,
|
||||||
|
roles: user.roles ? user.roles.split(',') : []
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
users
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// console.error('获取用户列表错误:', error);
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "获取用户列表失败" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* @typedef UpdateList
|
* @typedef UpdateList
|
||||||
* @property {string} username.required - 用户名 - eg: admin
|
* @property {string} username.required - 用户名 - eg: admin
|
||||||
@ -223,37 +400,135 @@ const register = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const updateList = async (req: Request, res: Response) => {
|
const updateList = async (req: Request, res: Response) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { username } = req.body;
|
const userData = req.body; // 获取所有更新字段
|
||||||
let payload = null;
|
|
||||||
try {
|
try {
|
||||||
|
// 验证 token
|
||||||
const authorizationHeader = req.get("Authorization") as string;
|
const authorizationHeader = req.get("Authorization") as string;
|
||||||
const accessToken = authorizationHeader.substr("Bearer ".length);
|
const accessToken = authorizationHeader.substr("Bearer ".length);
|
||||||
payload = jwt.verify(accessToken, secret.jwtSecret);
|
const payload = jwt.verify(accessToken, secret.jwtSecret);
|
||||||
} catch (error) {
|
|
||||||
return res.status(401).end();
|
// 构建更新字段
|
||||||
}
|
const updateFields = [];
|
||||||
let modifySql: string = "UPDATE users SET username = ? WHERE id = ?";
|
const updateValues = [];
|
||||||
let sql: string = "select * from users where id=" + id;
|
|
||||||
connection.query(sql, function (err, data) {
|
// 检查并添加每个可更新字段
|
||||||
connection.query(sql, function (err) {
|
if (userData.username) {
|
||||||
|
updateFields.push("username = ?");
|
||||||
|
updateValues.push(userData.username);
|
||||||
|
}
|
||||||
|
if (userData.name) {
|
||||||
|
updateFields.push("name = ?");
|
||||||
|
updateValues.push(userData.name);
|
||||||
|
}
|
||||||
|
if (userData.userid) {
|
||||||
|
updateFields.push("userid = ?");
|
||||||
|
updateValues.push(userData.userid);
|
||||||
|
}
|
||||||
|
if (userData.department) {
|
||||||
|
updateFields.push("department = ?");
|
||||||
|
updateValues.push(userData.department);
|
||||||
|
}
|
||||||
|
if (userData.position) {
|
||||||
|
updateFields.push("position = ?");
|
||||||
|
updateValues.push(userData.position);
|
||||||
|
}
|
||||||
|
if (userData.mobile) {
|
||||||
|
updateFields.push("mobile = ?");
|
||||||
|
updateValues.push(userData.mobile);
|
||||||
|
}
|
||||||
|
if (userData.gender) {
|
||||||
|
updateFields.push("gender = ?");
|
||||||
|
updateValues.push(userData.gender);
|
||||||
|
}
|
||||||
|
if (userData.email) {
|
||||||
|
updateFields.push("email = ?");
|
||||||
|
updateValues.push(userData.email);
|
||||||
|
}
|
||||||
|
if (userData.avatar) {
|
||||||
|
updateFields.push("avatar = ?");
|
||||||
|
updateValues.push(userData.avatar);
|
||||||
|
}
|
||||||
|
if (userData.status !== undefined) {
|
||||||
|
updateFields.push("status = ?");
|
||||||
|
updateValues.push(userData.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 ID 到 values 数组
|
||||||
|
updateValues.push(id);
|
||||||
|
|
||||||
|
const updateSql = `
|
||||||
|
UPDATE users
|
||||||
|
SET ${updateFields.join(", ")}
|
||||||
|
WHERE id = ?
|
||||||
|
`;
|
||||||
|
|
||||||
|
connection.query(updateSql, updateValues, async function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.error(err);
|
Logger.error(err);
|
||||||
} else {
|
return res.json({
|
||||||
let modifyParams: string[] = [username, id];
|
success: false,
|
||||||
// 改
|
data: { message: "更新用户失败" }
|
||||||
connection.query(modifySql, modifyParams, async function (err, result) {
|
});
|
||||||
|
}
|
||||||
|
// 直接使用更新的用户信息记录日志
|
||||||
|
logOperation({
|
||||||
|
userId: Number(id), // 转换为数字
|
||||||
|
username: userData.username || userData.name, // 使用更新的用户信息
|
||||||
|
action: OperationType.UPDATE,
|
||||||
|
module: ModuleType.USER,
|
||||||
|
description: `更新用户信息:ID=${id}, 字段:${updateFields.join(', ')}`,
|
||||||
|
ip: req.ip || ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果有角色信息,更新用户角色
|
||||||
|
if (userData.roles && userData.roles.length > 0) {
|
||||||
|
// 先删除原有角色
|
||||||
|
const deleteRolesSql = "DELETE FROM user_roles WHERE user_id = ?";
|
||||||
|
connection.query(deleteRolesSql, [id], async function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.error(err);
|
Logger.error(err);
|
||||||
} else {
|
return res.json({
|
||||||
await res.json({
|
success: false,
|
||||||
success: true,
|
data: { message: "更新用户角色失败" }
|
||||||
data: { message: Message[7] },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 插入新角色
|
||||||
|
const insertRolesSql = `
|
||||||
|
INSERT INTO user_roles (user_id, role_id)
|
||||||
|
SELECT ?, id FROM roles WHERE code IN (?)
|
||||||
|
`;
|
||||||
|
|
||||||
|
connection.query(insertRolesSql, [id, userData.roles], function(err) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "更新用户角色失败" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { message: Message[7] }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { message: Message[7] }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
} catch (error) {
|
||||||
|
Logger.error(error);
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "未授权" }
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,27 +548,126 @@ const updateList = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const deleteList = async (req: Request, res: Response) => {
|
const deleteList = async (req: Request, res: Response) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
let payload = null;
|
|
||||||
try {
|
try {
|
||||||
|
// 验证 token
|
||||||
const authorizationHeader = req.get("Authorization") as string;
|
const authorizationHeader = req.get("Authorization") as string;
|
||||||
const accessToken = authorizationHeader.substr("Bearer ".length);
|
const accessToken = authorizationHeader.substr("Bearer ".length);
|
||||||
payload = jwt.verify(accessToken, secret.jwtSecret);
|
const payload = jwt.verify(accessToken, secret.jwtSecret);
|
||||||
} catch (error) {
|
|
||||||
return res.status(401).end();
|
|
||||||
}
|
|
||||||
let sql: string = "DELETE FROM users where id=" + "'" + id + "'";
|
|
||||||
connection.query(sql, async function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
} else {
|
|
||||||
await res.json({
|
|
||||||
success: true,
|
|
||||||
data: { message: Message[8] },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// 开始事务
|
||||||
|
connection.beginTransaction(function(err) {
|
||||||
|
if (err) {
|
||||||
|
Logger.error(err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "删除失败" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先删除用户角色关联
|
||||||
|
const deleteRolesSql = "DELETE FROM user_roles WHERE user_id = ?";
|
||||||
|
connection.query(deleteRolesSql, [id], function(err) {
|
||||||
|
if (err) {
|
||||||
|
return connection.rollback(function() {
|
||||||
|
Logger.error(err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "删除用户角色失败" }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户
|
||||||
|
const deleteUserSql = "DELETE FROM users WHERE id = ?";
|
||||||
|
connection.query(deleteUserSql, [id], function(err) {
|
||||||
|
if (err) {
|
||||||
|
return connection.rollback(function() {
|
||||||
|
Logger.error(err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "删除用户失败" }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交事务
|
||||||
|
connection.commit(function(err) {
|
||||||
|
if (err) {
|
||||||
|
return connection.rollback(function() {
|
||||||
|
Logger.error(err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "删除失败" }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: { message: Message[8] }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error);
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "未授权" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @route GET /api/roles
|
||||||
|
* @summary 获取角色列表
|
||||||
|
* @group 角色管理
|
||||||
|
* @returns {object} 200 - 角色列表
|
||||||
|
* @security JWT
|
||||||
|
*/
|
||||||
|
const getRoleList = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const authorizationHeader = req.get("Authorization");
|
||||||
|
const accessToken = authorizationHeader.replace("Bearer ", "");
|
||||||
|
const decoded = jwt.verify(accessToken, secret.jwtSecret);
|
||||||
|
console.log('开始查询角色数据'); // 添加日志
|
||||||
|
|
||||||
|
// 查询所有角色
|
||||||
|
const sql = `
|
||||||
|
SELECT r.id, r.name, r.code, r.status, r.created_at
|
||||||
|
FROM roles r
|
||||||
|
WHERE r.status = 1
|
||||||
|
ORDER BY r.id ASC
|
||||||
|
`;
|
||||||
|
|
||||||
|
console.log('执行 SQL:', sql); // 添加日志
|
||||||
|
|
||||||
|
connection.query(sql, function(err, roles) {
|
||||||
|
if (err) {
|
||||||
|
console.error('数据库查询错误:', err);
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "获取角色列表失败" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('查询到的角色:', roles); // 添加日志
|
||||||
|
|
||||||
|
// 立即返回结果
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: { roles }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取角色列表错误:', error);
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
data: { message: "未授权" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* @typedef SearchPage
|
* @typedef SearchPage
|
||||||
* @property {integer} page.required - 第几页 - eg: 1
|
* @property {integer} page.required - 第几页 - eg: 1
|
||||||
@ -457,14 +831,38 @@ const captcha = async (req: Request, res: Response) => {
|
|||||||
res.type("svg"); // 响应的类型
|
res.type("svg"); // 响应的类型
|
||||||
res.json({ success: true, data: { text: create.text, svg: create.data } });
|
res.json({ success: true, data: { text: create.text, svg: create.data } });
|
||||||
};
|
};
|
||||||
|
// 添加获取动态路由的处理函数
|
||||||
export {
|
const getAsyncRoutes = async (req: Request, res: Response) => {
|
||||||
login,
|
// 这里返回你的动态路由配置
|
||||||
register,
|
res.json({
|
||||||
updateList,
|
success: true,
|
||||||
deleteList,
|
data: {
|
||||||
searchPage,
|
// 示例路由配置
|
||||||
searchVague,
|
routes: [
|
||||||
upload,
|
{
|
||||||
captcha,
|
path: "/permission",
|
||||||
|
meta: {
|
||||||
|
title: "权限管理",
|
||||||
|
icon: "lollipop"
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/permission/page/index",
|
||||||
|
name: "PermissionPage",
|
||||||
|
meta: {
|
||||||
|
title: "页面权限",
|
||||||
|
roles: ["admin"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
// ... 其他路由配置
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
export {
|
||||||
|
captcha, deleteList, getAsyncRoutes, getRoleList, getUserList, login, refreshToken, register, searchPage,
|
||||||
|
searchVague, updateList, upload
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -1,25 +1,29 @@
|
|||||||
import app from "./app";
|
import app from "./app";
|
||||||
// import * as open from "open";
|
// import * as open from "open";
|
||||||
import config from "./config";
|
|
||||||
import * as dayjs from "dayjs";
|
import * as dayjs from "dayjs";
|
||||||
import * as multer from "multer";
|
import * as multer from "multer";
|
||||||
import { user } from "./models/mysql";
|
import config from "./config";
|
||||||
import Logger from "./loaders/logger";
|
import Logger from "./loaders/logger";
|
||||||
import { queryTable } from "./utils/mysql";
|
import { importUsersFromLocalExcel } from "./router/excel";
|
||||||
|
import { getRoleList, getUserList } from "./router/http";
|
||||||
|
// import { queryTable } from "./utils/mysql";
|
||||||
const expressSwagger = require("express-swagger-generator")(app);
|
const expressSwagger = require("express-swagger-generator")(app);
|
||||||
expressSwagger(config.options);
|
expressSwagger(config.options);
|
||||||
|
// 初始化数据库表
|
||||||
queryTable(user);
|
// queryTable(user);
|
||||||
|
// queryTable(role);
|
||||||
|
// queryTable(userRole);
|
||||||
|
// queryTable(operationLogs);
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
captcha,
|
||||||
|
deleteList,
|
||||||
login,
|
login,
|
||||||
register,
|
register,
|
||||||
updateList,
|
|
||||||
deleteList,
|
|
||||||
searchPage,
|
searchPage,
|
||||||
searchVague,
|
searchVague,
|
||||||
|
updateList,
|
||||||
upload,
|
upload,
|
||||||
captcha,
|
|
||||||
} from "./router/http";
|
} from "./router/http";
|
||||||
|
|
||||||
app.post("/login", (req, res) => {
|
app.post("/login", (req, res) => {
|
||||||
@ -46,11 +50,27 @@ app.post("/searchVague", (req, res) => {
|
|||||||
searchVague(req, res);
|
searchVague(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 添加获取用户列表路由
|
||||||
|
app.get("/user/list", (req, res) => {
|
||||||
|
getUserList(req, res);
|
||||||
|
});
|
||||||
// 新建存放临时文件的文件夹
|
// 新建存放临时文件的文件夹
|
||||||
const upload_tmp = multer({ dest: "upload_tmp/" });
|
const upload_tmp = multer({ dest: "upload_tmp/" });
|
||||||
app.post("/upload", upload_tmp.any(), (req, res) => {
|
app.post("/upload", upload_tmp.any(), (req, res) => {
|
||||||
upload(req, res);
|
upload(req, res);
|
||||||
});
|
});
|
||||||
|
// 添加获取角色列表路由
|
||||||
|
app.get("/roles", (req, res) => { // 注意这里的路径
|
||||||
|
console.log("收到获取角色列表请求"); // 添加日志
|
||||||
|
getRoleList(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Excel导入用户路由
|
||||||
|
app.post("/api/excel/import-local", (req, res) => {
|
||||||
|
importUsersFromLocalExcel(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
app.get("/captcha", (req, res) => {
|
app.get("/captcha", (req, res) => {
|
||||||
captcha(req, res);
|
captcha(req, res);
|
||||||
|
@ -4,7 +4,7 @@ import Logger from "../loaders/logger";
|
|||||||
|
|
||||||
/** user数据库 */
|
/** user数据库 */
|
||||||
export const connection = mysql.createConnection(
|
export const connection = mysql.createConnection(
|
||||||
Object.assign({ database: "user" }, mysqlConfig.mysql)
|
Object.assign({ database: "houkong" }, mysqlConfig.mysql)
|
||||||
);
|
);
|
||||||
|
|
||||||
export function queryTable(s: string): void {
|
export function queryTable(s: string): void {
|
||||||
|
51
src/utils/operationLog.ts
Normal file
51
src/utils/operationLog.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { connection } from "./mysql";
|
||||||
|
import Logger from "../loaders/logger";
|
||||||
|
|
||||||
|
// 定义操作类型
|
||||||
|
export enum OperationType {
|
||||||
|
CREATE = 'CREATE',
|
||||||
|
UPDATE = 'UPDATE',
|
||||||
|
DELETE = 'DELETE',
|
||||||
|
QUERY = 'QUERY',
|
||||||
|
LOGIN = 'LOGIN',
|
||||||
|
UPLOAD = 'UPLOAD'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义模块类型
|
||||||
|
export enum ModuleType {
|
||||||
|
USER = '用户管理',
|
||||||
|
ROLE = '角色管理',
|
||||||
|
FILE = '文件管理',
|
||||||
|
AUTH = '认证管理'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录操作日志
|
||||||
|
export const logOperation = (params: {
|
||||||
|
userId: number;
|
||||||
|
username: string;
|
||||||
|
action: OperationType;
|
||||||
|
module: ModuleType;
|
||||||
|
description: string;
|
||||||
|
ip: string;
|
||||||
|
}) => {
|
||||||
|
const sql = `
|
||||||
|
INSERT INTO operation_logs
|
||||||
|
(user_id, username, action, module, description, ip)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
`;
|
||||||
|
|
||||||
|
const values = [
|
||||||
|
params.userId,
|
||||||
|
params.username,
|
||||||
|
params.action,
|
||||||
|
params.module,
|
||||||
|
params.description,
|
||||||
|
params.ip
|
||||||
|
];
|
||||||
|
|
||||||
|
connection.query(sql, values, (err) => {
|
||||||
|
if (err) {
|
||||||
|
Logger.error('记录操作日志失败:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user