添加了更多功能
This commit is contained in:
239
db.js
239
db.js
@@ -39,9 +39,10 @@ async function initDB() {
|
||||
id INT NOT NULL AUTO_INCREMENT COMMENT '用户ID (主键)',
|
||||
username VARCHAR(50) NOT NULL COMMENT '用户名 (登录账号)',
|
||||
password VARCHAR(255) NOT NULL COMMENT '登录密码 (应存储加密后的值)',
|
||||
real_name VARCHAR(50) DEFAULT NULL COMMENT '真实姓名',
|
||||
role VARCHAR(20) NOT NULL DEFAULT 'user' COMMENT '角色: admin-管理员, user-普通用户',
|
||||
status TINYINT(1) NOT NULL DEFAULT 1 COMMENT '账号状态: 0-禁用, 1-启用',
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '账号状态: 0-禁用, 1-启用',
|
||||
role_id INT DEFAULT NULL COMMENT '角色ID (关联roles表)',
|
||||
employee_id INT DEFAULT NULL COMMENT '员工ID (关联employees表)',
|
||||
department VARCHAR(100) DEFAULT NULL COMMENT '所属部门 (从employees同步的冗余字段)',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (id),
|
||||
@@ -152,20 +153,228 @@ async function initDB() {
|
||||
`)
|
||||
console.log('[init] 数据表初始化完成')
|
||||
|
||||
// 2.3 插 admin(仅当不存在时)
|
||||
const [rows] = await pool.query(
|
||||
'SELECT id FROM users WHERE username = ?',
|
||||
['admin']
|
||||
)
|
||||
if (rows.length === 0) {
|
||||
const hash = await bcrypt.hash('123456', 10)
|
||||
// ============ 2.3 RBAC 权限相关表 ============
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id INT NOT NULL AUTO_INCREMENT COMMENT '角色ID (主键)',
|
||||
name VARCHAR(50) NOT NULL COMMENT '角色标识 (英文)',
|
||||
description VARCHAR(200) DEFAULT NULL COMMENT '角色描述 (中文)',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_roles_name (name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表'
|
||||
`)
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS permissions (
|
||||
id INT NOT NULL AUTO_INCREMENT COMMENT '权限ID (主键)',
|
||||
name VARCHAR(100) NOT NULL COMMENT '权限标识 (resource:action)',
|
||||
description VARCHAR(200) DEFAULT NULL COMMENT '权限描述 (中文)',
|
||||
resource VARCHAR(50) NOT NULL COMMENT '资源名称',
|
||||
action VARCHAR(50) NOT NULL COMMENT '操作类型: read/create/update/delete',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_permissions_name (name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表'
|
||||
`)
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS role_permissions (
|
||||
role_id INT NOT NULL COMMENT '角色ID',
|
||||
permission_id INT NOT NULL COMMENT '权限ID',
|
||||
PRIMARY KEY (role_id, permission_id),
|
||||
KEY idx_rp_permission_id (permission_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色-权限关联表'
|
||||
`)
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS suppliers (
|
||||
id INT NOT NULL AUTO_INCREMENT COMMENT '供应商ID (主键)',
|
||||
name VARCHAR(200) NOT NULL COMMENT '供应商名称',
|
||||
contact VARCHAR(100) DEFAULT NULL COMMENT '联系人',
|
||||
phone VARCHAR(50) DEFAULT NULL COMMENT '联系电话',
|
||||
address VARCHAR(500) DEFAULT NULL COMMENT '地址',
|
||||
type VARCHAR(50) DEFAULT NULL COMMENT '类型: 原材料/包装/设备',
|
||||
content TEXT DEFAULT NULL COMMENT '备注说明',
|
||||
status TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态: 0-停用, 1-正常',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_suppliers_name (name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商信息表'
|
||||
`)
|
||||
|
||||
// ============ 2.4 旧表迁移(兼容已有数据库) ============
|
||||
// users 表:删除 real_name、role,新增 is_active、role_id、employee_id、department
|
||||
try { await pool.query(`ALTER TABLE users DROP COLUMN real_name`) } catch {}
|
||||
try { await pool.query(`ALTER TABLE users DROP COLUMN role`) } catch {}
|
||||
try { await pool.query(`ALTER TABLE users CHANGE COLUMN status is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '账号状态: 0-禁用, 1-启用'`) } catch {}
|
||||
try { await pool.query(`ALTER TABLE users ADD COLUMN role_id INT DEFAULT NULL COMMENT '角色ID' AFTER is_active`) } catch {}
|
||||
try { await pool.query(`ALTER TABLE users ADD COLUMN employee_id INT DEFAULT NULL COMMENT '员工ID' AFTER role_id`) } catch {}
|
||||
try { await pool.query(`ALTER TABLE users ADD COLUMN department VARCHAR(100) DEFAULT NULL COMMENT '所属部门' AFTER employee_id`) } catch {}
|
||||
|
||||
// customers 表:新增 responsible_user_id
|
||||
try { await pool.query(`ALTER TABLE customers ADD COLUMN responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID'`) } catch {}
|
||||
|
||||
// contracts 表:新增 type
|
||||
try { await pool.query(`ALTER TABLE contracts ADD COLUMN type VARCHAR(20) DEFAULT 'franchise' COMMENT '合同类型: franchise-加盟合同, supply-采购合同'`) } catch {}
|
||||
// contracts 表:新增 responsible_user_id(用于数据范围隔离)
|
||||
try { await pool.query(`ALTER TABLE contracts ADD COLUMN responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID'`) } catch {}
|
||||
|
||||
// after_sales 表:新增 responsible_user_id(用于数据范围隔离)
|
||||
try { await pool.query(`ALTER TABLE after_sales ADD COLUMN responsible_user_id INT DEFAULT NULL COMMENT '负责人用户ID'`) } catch {}
|
||||
|
||||
// ============ 2.5 种子数据 ============
|
||||
await seedRolesAndPermissions()
|
||||
await seedDefaultUsers()
|
||||
}
|
||||
|
||||
// ============ 3. 种子数据:角色与权限 ============
|
||||
async function seedRolesAndPermissions() {
|
||||
const [roles] = await pool.query('SELECT id FROM roles LIMIT 1')
|
||||
if (roles.length > 0) return // 已初始化过,跳过
|
||||
|
||||
const roleList = [
|
||||
{ name: 'admin', description: '系统管理员' },
|
||||
{ name: 'general_manager', description: '总经理' },
|
||||
{ name: 'franchise_manager', description: '招商经理' },
|
||||
{ name: 'operations_manager', description: '运营经理' },
|
||||
{ name: 'procurement_manager', description: '采购经理' },
|
||||
{ name: 'finance', description: '财务人员' },
|
||||
]
|
||||
for (const r of roleList) {
|
||||
await pool.query('INSERT INTO roles (name, description) VALUES (?, ?)', [r.name, r.description])
|
||||
}
|
||||
console.log('[init] 已初始化 6 个角色')
|
||||
|
||||
const permissionList = [
|
||||
{ name: 'customer:read', description: '查看加盟商', resource: 'customer', action: 'read' },
|
||||
{ name: 'customer:create', description: '新增加盟商', resource: 'customer', action: 'create' },
|
||||
{ name: 'customer:update', description: '修改加盟商', resource: 'customer', action: 'update' },
|
||||
{ name: 'customer:delete', description: '删除加盟商', resource: 'customer', action: 'delete' },
|
||||
{ name: 'contract:read', description: '查看合同', resource: 'contract', action: 'read' },
|
||||
{ name: 'contract:create', description: '新增合同', resource: 'contract', action: 'create' },
|
||||
{ name: 'contract:update', description: '修改合同', resource: 'contract', action: 'update' },
|
||||
{ name: 'contract:delete', description: '删除合同', resource: 'contract', action: 'delete' },
|
||||
{ name: 'after_sale:read', description: '查看售后', resource: 'after_sale', action: 'read' },
|
||||
{ name: 'after_sale:create',description: '新增售后', resource: 'after_sale', action: 'create' },
|
||||
{ name: 'after_sale:update',description: '修改售后', resource: 'after_sale', action: 'update' },
|
||||
{ name: 'after_sale:delete',description: '删除售后', resource: 'after_sale', action: 'delete' },
|
||||
{ name: 'product:read', description: '查看产品原料', resource: 'product', action: 'read' },
|
||||
{ name: 'product:create', description: '新增产品原料', resource: 'product', action: 'create' },
|
||||
{ name: 'product:update', description: '修改产品原料', resource: 'product', action: 'update' },
|
||||
{ name: 'product:delete', description: '删除产品原料', resource: 'product', action: 'delete' },
|
||||
{ name: 'supplier:read', description: '查看供应商', resource: 'supplier', action: 'read' },
|
||||
{ name: 'supplier:create', description: '新增供应商', resource: 'supplier', action: 'create' },
|
||||
{ name: 'supplier:update', description: '修改供应商', resource: 'supplier', action: 'update' },
|
||||
{ name: 'supplier:delete', description: '删除供应商', resource: 'supplier', action: 'delete' },
|
||||
{ name: 'employee:read', description: '查看员工', resource: 'employee', action: 'read' },
|
||||
{ name: 'employee:create', description: '新增员工', resource: 'employee', action: 'create' },
|
||||
{ name: 'employee:update', description: '修改员工', resource: 'employee', action: 'update' },
|
||||
{ name: 'employee:delete', description: '删除员工', resource: 'employee', action: 'delete' },
|
||||
{ name: 'user:manage', description: '管理用户账号', resource: 'user', action: 'manage' },
|
||||
]
|
||||
for (const p of permissionList) {
|
||||
await pool.query(
|
||||
'INSERT INTO users (username, password, real_name, role) VALUES (?, ?, ?, ?)',
|
||||
['admin', hash, '超级管理员', 'admin']
|
||||
'INSERT INTO permissions (name, description, resource, action) VALUES (?, ?, ?, ?)',
|
||||
[p.name, p.description, p.resource, p.action]
|
||||
)
|
||||
console.log('[init] 已创建默认账号 admin / 123456')
|
||||
} else {
|
||||
console.log('[init] admin 已存在,跳过')
|
||||
}
|
||||
console.log('[init] 已初始化 25 个权限')
|
||||
|
||||
// 角色-权限映射
|
||||
const allPerms = permissionList.map(p => p.name)
|
||||
const rolePermMap = {
|
||||
admin: allPerms, // 管理员拥有所有权限
|
||||
general_manager: [
|
||||
'customer:read', 'contract:read', 'after_sale:read',
|
||||
'product:read', 'supplier:read', 'employee:read',
|
||||
],
|
||||
franchise_manager: [
|
||||
'customer:read', 'customer:create', 'customer:update', 'customer:delete',
|
||||
'contract:read', 'contract:create', 'contract:update', 'contract:delete',
|
||||
'employee:read',
|
||||
],
|
||||
operations_manager: [
|
||||
'customer:read', 'customer:update',
|
||||
'contract:read',
|
||||
'after_sale:read', 'after_sale:create', 'after_sale:update', 'after_sale:delete',
|
||||
'employee:read',
|
||||
],
|
||||
procurement_manager: [
|
||||
'contract:read', 'contract:create', 'contract:update', 'contract:delete',
|
||||
'product:read', 'product:create', 'product:update', 'product:delete',
|
||||
'supplier:read', 'supplier:create', 'supplier:update', 'supplier:delete',
|
||||
'employee:read',
|
||||
],
|
||||
finance: [
|
||||
'customer:read', 'contract:read', 'after_sale:read', 'employee:read',
|
||||
],
|
||||
}
|
||||
|
||||
const [roleRows] = await pool.query('SELECT id, name FROM roles')
|
||||
const [permRows] = await pool.query('SELECT id, name FROM permissions')
|
||||
const roleIdMap = Object.fromEntries(roleRows.map(r => [r.name, r.id]))
|
||||
const permIdMap = Object.fromEntries(permRows.map(p => [p.name, p.id]))
|
||||
|
||||
for (const [roleName, permNames] of Object.entries(rolePermMap)) {
|
||||
for (const permName of permNames) {
|
||||
await pool.query(
|
||||
'INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)',
|
||||
[roleIdMap[roleName], permIdMap[permName]]
|
||||
)
|
||||
}
|
||||
}
|
||||
console.log('[init] 已初始化角色-权限映射')
|
||||
}
|
||||
|
||||
// ============ 4. 种子数据:默认用户 ============
|
||||
async function seedDefaultUsers() {
|
||||
const hash = await bcrypt.hash('123456', 10)
|
||||
|
||||
const [roleRows] = await pool.query('SELECT id, name FROM roles')
|
||||
const roleIdMap = Object.fromEntries(roleRows.map(r => [r.name, r.id]))
|
||||
|
||||
const seedEmployees = [
|
||||
{ name: '张超', gender: '男', age: 42, education: '硕士', department: '总经理办公室', entry_date: '2015-03-01', position: '总经理', salary: 50000, phone: '13800001001', email: 'zhangchao@mixue.com' },
|
||||
{ name: '李明', gender: '男', age: 35, education: '本科', department: '招商部', entry_date: '2018-06-15', position: '招商经理', salary: 18000, phone: '13800001002', email: 'liming@mixue.com' },
|
||||
{ name: '王丽', gender: '女', age: 33, education: '本科', department: '运营部', entry_date: '2019-01-10', position: '运营经理', salary: 18000, phone: '13800001003', email: 'wangli@mixue.com' },
|
||||
{ name: '赵强', gender: '男', age: 36, education: '本科', department: '采购部', entry_date: '2017-09-20', position: '采购经理', salary: 18000, phone: '13800001004', email: 'zhaoqiang@mixue.com' },
|
||||
{ name: '陈芳', gender: '女', age: 30, education: '本科', department: '财务部', entry_date: '2020-03-05', position: '财务主管', salary: 15000, phone: '13800001005', email: 'chenfang@mixue.com' },
|
||||
{ name: '系统管理员', gender: '男', age: 28, education: '本科', department: '信息技术部', entry_date: '2022-07-01', position: '系统管理员', salary: 12000, phone: '13800001006', email: 'admin@mixue.com' },
|
||||
]
|
||||
|
||||
const seedUsers = [
|
||||
{ username: 'zhangchao', roleName: 'general_manager' },
|
||||
{ username: 'liming', roleName: 'franchise_manager' },
|
||||
{ username: 'wangli', roleName: 'operations_manager' },
|
||||
{ username: 'zhaoqiang', roleName: 'procurement_manager' },
|
||||
{ username: 'chenfang', roleName: 'finance' },
|
||||
{ username: 'admin', roleName: 'admin' },
|
||||
]
|
||||
|
||||
for (let i = 0; i < seedUsers.length; i++) {
|
||||
const emp = seedEmployees[i]
|
||||
const usr = seedUsers[i]
|
||||
|
||||
// 逐个检查用户是否存在(不依赖全局检查,避免部分用户被误删后跳过全部重建)
|
||||
const [userCheck] = await pool.query('SELECT id FROM users WHERE username = ?', [usr.username])
|
||||
if (userCheck.length > 0) continue
|
||||
|
||||
// 先插入 employees
|
||||
const [empResult] = await pool.query(
|
||||
`INSERT INTO employees (name, gender, age, education, department, entry_date, position, salary, phone, email, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)`,
|
||||
[emp.name, emp.gender, emp.age, emp.education, emp.department, emp.entry_date, emp.position, emp.salary, emp.phone, emp.email]
|
||||
)
|
||||
const empId = empResult.insertId
|
||||
|
||||
// 再插入 users,关联 employee 和 role
|
||||
await pool.query(
|
||||
`INSERT INTO users (username, password, is_active, role_id, employee_id, department)
|
||||
VALUES (?, ?, 1, ?, ?, ?)`,
|
||||
[usr.username, hash, roleIdMap[usr.roleName], empId, emp.department]
|
||||
)
|
||||
console.log(`[init] 已创建用户 ${usr.username} (${emp.name} - ${emp.department})`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user