修改后端接口
This commit is contained in:
@@ -23,8 +23,17 @@ const accessLogStream = fs.createWriteStream(
|
||||
);
|
||||
app.use(morgan('combined', { stream: accessLogStream }));
|
||||
|
||||
// 数据库连接(暂时注释掉,使用内存数据)
|
||||
// const sequelize = require('./config/database');
|
||||
// 数据库连接
|
||||
const sequelize = require('./config/database');
|
||||
|
||||
// 测试数据库连接
|
||||
sequelize.authenticate()
|
||||
.then(() => {
|
||||
console.log('数据库连接成功');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('数据库连接失败:', err);
|
||||
});
|
||||
|
||||
// 路由
|
||||
app.use('/api/auth', require('./routes/auth'));
|
||||
@@ -58,5 +67,4 @@ app.use((err, req, res, next) => {
|
||||
const PORT = process.env.PORT || 5352;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`政府管理系统后端服务已启动,端口: ${PORT}`);
|
||||
console.log(`使用内存数据,无需数据库连接`);
|
||||
});
|
||||
@@ -1,14 +1,14 @@
|
||||
require('dotenv').config();
|
||||
const { Sequelize } = require('sequelize');
|
||||
const config = require('./index.js');
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
process.env.DB_NAME,
|
||||
process.env.DB_USER,
|
||||
process.env.DB_PASSWORD,
|
||||
config.DB_CONFIG.database,
|
||||
config.DB_CONFIG.user,
|
||||
config.DB_CONFIG.password,
|
||||
{
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
dialect: process.env.DB_DIALECT,
|
||||
host: config.DB_CONFIG.host,
|
||||
port: config.DB_CONFIG.port,
|
||||
dialect: config.DB_CONFIG.dialect,
|
||||
logging: process.env.NODE_ENV === 'development' ? console.log : false,
|
||||
pool: {
|
||||
max: 5,
|
||||
@@ -31,12 +31,14 @@ async function testConnection() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('数据库连接失败:', error);
|
||||
process.exit(1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
testConnection();
|
||||
// 导出连接测试函数,但不自动执行
|
||||
module.exports.testConnection = testConnection;
|
||||
|
||||
module.exports = sequelize;
|
||||
@@ -1,11 +1,19 @@
|
||||
const DB_DIALECT = process.env.DB_DIALECT || 'mysql';
|
||||
const DB_HOST = process.env.DB_HOST || '129.211.213.226';
|
||||
const DB_PORT = process.env.DB_PORT || 9527;
|
||||
const DB_NAME = process.env.DB_NAME || 'ningxia_zhengfu';
|
||||
const DB_USER = process.env.DB_USER || 'root';
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD || 'aiotAiot123!';
|
||||
|
||||
module.exports = {
|
||||
JWT_SECRET: 'your-secret-key-here', // 请在生产环境中替换为强密钥
|
||||
DB_CONFIG: {
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: '',
|
||||
database: 'government_db',
|
||||
port: 3306
|
||||
host: DB_HOST,
|
||||
user: DB_USER,
|
||||
password: DB_PASSWORD,
|
||||
database: DB_NAME,
|
||||
port: DB_PORT,
|
||||
dialect: DB_DIALECT
|
||||
},
|
||||
PORT: 5352
|
||||
}
|
||||
@@ -1,51 +1,63 @@
|
||||
const jwt = require('jsonwebtoken')
|
||||
const jwt = require('jsonwebtoken');
|
||||
const User = require('../models/User');
|
||||
const AdminStaff = require('../models/AdminStaff');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
// JWT配置
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'
|
||||
|
||||
// 临时用户数据(实际项目中应该从数据库获取)
|
||||
const users = [
|
||||
{
|
||||
id: 1,
|
||||
username: 'admin',
|
||||
password: '123456',
|
||||
name: '系统管理员',
|
||||
role: 'admin',
|
||||
email: 'admin@example.com'
|
||||
}
|
||||
]
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
|
||||
exports.login = async (req, res) => {
|
||||
try {
|
||||
const { username, password } = req.body
|
||||
const { username, password } = req.body;
|
||||
|
||||
// 查找用户
|
||||
const user = users.find(u => u.username === username && u.password === password)
|
||||
// 从数据库查找用户
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
username,
|
||||
status: 'active'
|
||||
}
|
||||
});
|
||||
|
||||
if (user) {
|
||||
const token = jwt.sign({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
role: user.role
|
||||
}, JWT_SECRET, { expiresIn: '2h' })
|
||||
|
||||
return res.json({
|
||||
code: 200,
|
||||
message: '登录成功',
|
||||
data: { token }
|
||||
})
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '用户名或密码错误'
|
||||
});
|
||||
}
|
||||
|
||||
res.status(401).json({
|
||||
code: 401,
|
||||
message: '用户名或密码错误'
|
||||
})
|
||||
// 验证密码
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password);
|
||||
|
||||
if (!isPasswordValid) {
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '用户名或密码错误'
|
||||
});
|
||||
}
|
||||
|
||||
// 更新最后登录时间
|
||||
await user.update({
|
||||
last_login: new Date()
|
||||
});
|
||||
|
||||
const token = jwt.sign({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
role: user.role
|
||||
}, JWT_SECRET, { expiresIn: '2h' });
|
||||
|
||||
return res.json({
|
||||
code: 200,
|
||||
message: '登录成功',
|
||||
data: { token }
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('登录错误:', err);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
message: '服务器错误',
|
||||
error: err.message
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,51 +65,104 @@ exports.login = async (req, res) => {
|
||||
exports.getUserInfo = async (req, res) => {
|
||||
try {
|
||||
// 从token中解析用户信息
|
||||
const token = req.headers.authorization?.replace('Bearer ', '')
|
||||
const token = req.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '未提供认证令牌'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, JWT_SECRET)
|
||||
const user = users.find(u => u.id === decoded.id)
|
||||
const decoded = jwt.verify(token, JWT_SECRET);
|
||||
|
||||
if (user) {
|
||||
const userInfo = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
avatar: '',
|
||||
email: user.email,
|
||||
permissions: ['dashboard', 'users', 'settings']
|
||||
}
|
||||
|
||||
return res.json({
|
||||
code: 200,
|
||||
message: '获取用户信息成功',
|
||||
data: userInfo
|
||||
})
|
||||
} else {
|
||||
// 从数据库获取用户信息
|
||||
const user = await User.findByPk(decoded.id, {
|
||||
include: [
|
||||
{
|
||||
model: AdminStaff,
|
||||
as: 'staffInfo'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!user || user.status !== 'active') {
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '用户不存在'
|
||||
})
|
||||
message: '用户不存在或已禁用'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取员工信息
|
||||
let staffInfo = null;
|
||||
try {
|
||||
staffInfo = await AdminStaff.findOne({
|
||||
where: {
|
||||
phone: user.username
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('获取员工信息失败:', error);
|
||||
}
|
||||
|
||||
// 根据角色设置权限
|
||||
const permissions = getPermissionsByRole(user.role);
|
||||
|
||||
const userInfo = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
name: staffInfo?.name || user.username,
|
||||
role: user.role,
|
||||
avatar: '',
|
||||
email: '',
|
||||
phone: staffInfo?.phone || user.username,
|
||||
department: staffInfo?.department_id ? {
|
||||
id: staffInfo.department_id,
|
||||
name: ''
|
||||
} : null,
|
||||
position: staffInfo?.position_id ? {
|
||||
id: staffInfo.position_id,
|
||||
name: ''
|
||||
} : null,
|
||||
permissions
|
||||
};
|
||||
|
||||
return res.json({
|
||||
code: 200,
|
||||
message: '获取用户信息成功',
|
||||
data: userInfo
|
||||
});
|
||||
} catch (jwtError) {
|
||||
console.error('JWT验证错误:', jwtError);
|
||||
return res.status(401).json({
|
||||
code: 401,
|
||||
message: '认证令牌无效'
|
||||
})
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取用户信息错误:', err);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
message: '服务器错误',
|
||||
error: err.message
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 根据角色获取权限
|
||||
exports.getPermissionsByRole = (role) => {
|
||||
const basePermissions = ['dashboard'];
|
||||
|
||||
switch (role) {
|
||||
case 'admin':
|
||||
return [...basePermissions, 'users', 'settings', 'supervision', 'approval', 'personnel', 'warehouse', 'epidemic', 'service', 'visualization'];
|
||||
case 'manager':
|
||||
return [...basePermissions, 'supervision', 'approval', 'personnel', 'warehouse', 'epidemic', 'service'];
|
||||
case 'inspector':
|
||||
return [...basePermissions, 'supervision', 'epidemic'];
|
||||
case 'clerk':
|
||||
return [...basePermissions, 'approval', 'personnel', 'warehouse', 'service'];
|
||||
default:
|
||||
return basePermissions;
|
||||
}
|
||||
};
|
||||
248
government-backend/controllers/farmerController.js
Normal file
248
government-backend/controllers/farmerController.js
Normal file
@@ -0,0 +1,248 @@
|
||||
const Farmer = require('../models/Farmer');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取养殖户列表
|
||||
const getFarmers = async (req, res) => {
|
||||
try {
|
||||
const { page = 1, pageSize = 10, search = '', farmType = '', animalType = '', status = '' } = req.query;
|
||||
|
||||
const whereCondition = {};
|
||||
|
||||
// 搜索条件
|
||||
if (search) {
|
||||
whereCondition[Op.or] = [
|
||||
{ nickname: { [Op.like]: `%${search}%` } },
|
||||
{ real_name: { [Op.like]: `%${search}%` } },
|
||||
{ farm_name: { [Op.like]: `%${search}%` } },
|
||||
{ account: { [Op.like]: `%${search}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
// 养殖场类型筛选
|
||||
if (farmType) {
|
||||
whereCondition.farm_type = farmType;
|
||||
}
|
||||
|
||||
// 养殖种类筛选
|
||||
if (animalType) {
|
||||
whereCondition.animal_type = animalType;
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (status) {
|
||||
whereCondition.status = status;
|
||||
}
|
||||
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const { count, rows } = await Farmer.findAndCountAll({
|
||||
where: whereCondition,
|
||||
offset,
|
||||
limit: parseInt(pageSize),
|
||||
order: [['register_time', 'DESC']]
|
||||
});
|
||||
|
||||
// 格式化数据以便前端使用
|
||||
const formattedData = rows.map(farmer => ({
|
||||
id: farmer.id,
|
||||
key: farmer.id.toString(),
|
||||
account: farmer.account,
|
||||
nickname: farmer.nickname,
|
||||
real_name: farmer.real_name,
|
||||
farm_name: farmer.farm_name,
|
||||
farm_type: farmer.farm_type,
|
||||
animal_type: farmer.animal_type,
|
||||
animal_count: farmer.animal_count,
|
||||
address: farmer.address,
|
||||
register_time: farmer.register_time.toLocaleString('zh-CN'),
|
||||
registrar: farmer.registrar,
|
||||
status: farmer.status === 'active' ? '正常' : farmer.status === 'inactive' ? '暂停' : '关闭',
|
||||
status_value: farmer.status
|
||||
}));
|
||||
|
||||
res.json({
|
||||
data: formattedData,
|
||||
total: count,
|
||||
page: parseInt(page),
|
||||
pageSize: parseInt(pageSize)
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取养殖户列表失败:', error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 新增养殖户
|
||||
const createFarmer = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
account,
|
||||
nickname,
|
||||
real_name,
|
||||
farm_name,
|
||||
farm_type,
|
||||
animal_type,
|
||||
animal_count,
|
||||
address,
|
||||
status = 'active'
|
||||
} = req.body;
|
||||
|
||||
// 检查账号是否已存在
|
||||
const existingFarmer = await Farmer.findOne({ where: { account } });
|
||||
if (existingFarmer) {
|
||||
return res.status(400).json({ message: '该账号已存在' });
|
||||
}
|
||||
|
||||
const farmer = await Farmer.create({
|
||||
account,
|
||||
nickname,
|
||||
real_name,
|
||||
farm_name,
|
||||
farm_type,
|
||||
animal_type,
|
||||
animal_count,
|
||||
address,
|
||||
registrar: req.user?.username || 'system',
|
||||
status
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: '养殖户创建成功',
|
||||
data: farmer
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('创建养殖户失败:', error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 编辑养殖户
|
||||
const updateFarmer = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
nickname,
|
||||
real_name,
|
||||
farm_name,
|
||||
farm_type,
|
||||
animal_type,
|
||||
animal_count,
|
||||
address,
|
||||
status
|
||||
} = req.body;
|
||||
|
||||
const farmer = await Farmer.findByPk(id);
|
||||
if (!farmer) {
|
||||
return res.status(404).json({ message: '养殖户不存在' });
|
||||
}
|
||||
|
||||
await farmer.update({
|
||||
nickname,
|
||||
real_name,
|
||||
farm_name,
|
||||
farm_type,
|
||||
animal_type,
|
||||
animal_count,
|
||||
address,
|
||||
status
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '养殖户信息更新成功',
|
||||
data: farmer
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('更新养殖户信息失败:', error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 删除养殖户
|
||||
const deleteFarmer = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const farmer = await Farmer.findByPk(id);
|
||||
if (!farmer) {
|
||||
return res.status(404).json({ message: '养殖户不存在' });
|
||||
}
|
||||
|
||||
await farmer.destroy();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '养殖户删除成功'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('删除养殖户失败:', error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 重置养殖户密码(如果需要)
|
||||
const resetFarmerPassword = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const farmer = await Farmer.findByPk(id);
|
||||
if (!farmer) {
|
||||
return res.status(404).json({ message: '养殖户不存在' });
|
||||
}
|
||||
|
||||
// 在实际应用中,这里应该生成一个临时密码并发送给用户
|
||||
// 为了演示,我们只返回成功信息
|
||||
res.json({
|
||||
success: true,
|
||||
message: '密码重置成功,新密码已发送到用户手机'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('重置养殖户密码失败:', error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 获取养殖类型列表
|
||||
const getFarmTypes = async (req, res) => {
|
||||
try {
|
||||
const farmTypes = [
|
||||
{ value: '规模', label: '规模' },
|
||||
{ value: '散养', label: '散养' },
|
||||
{ value: '其他', label: '其他' }
|
||||
];
|
||||
|
||||
res.json(farmTypes);
|
||||
} catch (error) {
|
||||
console.error('获取养殖类型列表失败:', error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// 获取养殖种类列表
|
||||
const getAnimalTypes = async (req, res) => {
|
||||
try {
|
||||
const animalTypes = [
|
||||
{ value: '牛', label: '牛' },
|
||||
{ value: '羊', label: '羊' },
|
||||
{ value: '猪', label: '猪' },
|
||||
{ value: '鸡', label: '鸡' },
|
||||
{ value: '其他', label: '其他' }
|
||||
];
|
||||
|
||||
res.json(animalTypes);
|
||||
} catch (error) {
|
||||
console.error('获取养殖种类列表失败:', error);
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getFarmers,
|
||||
createFarmer,
|
||||
updateFarmer,
|
||||
deleteFarmer,
|
||||
resetFarmerPassword,
|
||||
getFarmTypes,
|
||||
getAnimalTypes
|
||||
};
|
||||
113
government-backend/models/Farmer.js
Normal file
113
government-backend/models/Farmer.js
Normal file
@@ -0,0 +1,113 @@
|
||||
const sequelize = require('../config/database');
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
const Farmer = sequelize.define('Farmer', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
account: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '账号'
|
||||
},
|
||||
org_code: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
comment: '机构识别码'
|
||||
},
|
||||
nickname: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '账号昵称'
|
||||
},
|
||||
real_name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '真实姓名'
|
||||
},
|
||||
farm_name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '养殖场名称'
|
||||
},
|
||||
farm_type: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '养殖场类型',
|
||||
validate: {
|
||||
isIn: [['规模', '散养', '其他']]
|
||||
}
|
||||
},
|
||||
animal_type: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '养殖场种类',
|
||||
validate: {
|
||||
isIn: [['牛', '羊', '猪', '鸡', '其他']]
|
||||
}
|
||||
},
|
||||
animal_count: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
comment: '养殖数量'
|
||||
},
|
||||
address: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '养殖场地址'
|
||||
},
|
||||
register_time: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
comment: '登记时间'
|
||||
},
|
||||
registrar: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '登记人'
|
||||
},
|
||||
update_time: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
comment: '更新时间'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('active', 'inactive', 'closed'),
|
||||
allowNull: false,
|
||||
defaultValue: 'active',
|
||||
comment: '状态:active-正常,inactive-暂停,closed-关闭'
|
||||
}
|
||||
}, {
|
||||
tableName: 'farmers',
|
||||
timestamps: true,
|
||||
createdAt: 'register_time',
|
||||
updatedAt: 'update_time',
|
||||
paranoid: false,
|
||||
indexes: [
|
||||
{
|
||||
name: 'idx_farmer_account',
|
||||
fields: ['account']
|
||||
},
|
||||
{
|
||||
name: 'idx_farmer_farm_name',
|
||||
fields: ['farm_name']
|
||||
},
|
||||
{
|
||||
name: 'idx_farmer_status',
|
||||
fields: ['status']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 钩子函数,在保存前更新更新时间
|
||||
Farmer.beforeSave((farmer) => {
|
||||
farmer.update_time = new Date();
|
||||
});
|
||||
|
||||
module.exports = Farmer;
|
||||
59
government-backend/models/User.js
Normal file
59
government-backend/models/User.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const sequelize = require('../config/database');
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
const User = sequelize.define('User', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
username: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '用户名'
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '密码'
|
||||
},
|
||||
role: {
|
||||
type: DataTypes.ENUM('admin', 'manager', 'inspector', 'clerk'),
|
||||
allowNull: false,
|
||||
comment: '角色'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('active', 'inactive'),
|
||||
allowNull: false,
|
||||
defaultValue: 'active',
|
||||
comment: '状态'
|
||||
},
|
||||
last_login: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '最后登录时间'
|
||||
}
|
||||
}, {
|
||||
tableName: 'users',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
paranoid: false,
|
||||
indexes: [
|
||||
{
|
||||
name: 'idx_username',
|
||||
fields: ['username']
|
||||
},
|
||||
{
|
||||
name: 'idx_role',
|
||||
fields: ['role']
|
||||
},
|
||||
{
|
||||
name: 'idx_status',
|
||||
fields: ['status']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
module.exports = User;
|
||||
120
government-backend/package-lock.json
generated
120
government-backend/package-lock.json
generated
@@ -20,6 +20,7 @@
|
||||
"sequelize": "^6.37.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^1.12.2",
|
||||
"nodemon": "^3.1.0"
|
||||
}
|
||||
},
|
||||
@@ -86,6 +87,13 @@
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-ssl-profiles": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
|
||||
@@ -95,6 +103,18 @@
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.12.2.tgz",
|
||||
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -268,6 +288,19 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -333,6 +366,16 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz",
|
||||
@@ -447,6 +490,22 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@@ -539,6 +598,44 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@@ -674,6 +771,22 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
|
||||
@@ -1268,6 +1381,13 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
|
||||
@@ -8,19 +8,19 @@
|
||||
"dev": "nodemon app.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"mysql2": "^3.6.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.20.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"body-parser": "^1.20.2",
|
||||
"express": "^4.18.2",
|
||||
"helmet": "^7.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"morgan": "^1.10.0",
|
||||
"sequelize": "^6.37.1",
|
||||
"mysql2": "^3.9.7"
|
||||
"mysql2": "^3.9.7",
|
||||
"sequelize": "^6.37.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^1.12.2",
|
||||
"nodemon": "^3.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const governmentController = require('../controllers/governmentController');
|
||||
const farmerController = require('../controllers/farmerController');
|
||||
|
||||
// 数据览仓接口
|
||||
router.get('/data-center', governmentController.getDataCenterStats);
|
||||
@@ -52,4 +53,26 @@ router.delete('/admin-staff/:id', governmentController.deleteAdminStaff);
|
||||
// 重置行政人员密码
|
||||
router.post('/admin-staff/:id/reset-password', governmentController.resetAdminStaffPassword);
|
||||
|
||||
// 养殖户管理接口
|
||||
// 获取养殖户列表
|
||||
router.get('/farmers', farmerController.getFarmers);
|
||||
|
||||
// 新增养殖户
|
||||
router.post('/farmers', farmerController.createFarmer);
|
||||
|
||||
// 编辑养殖户
|
||||
router.put('/farmers/:id', farmerController.updateFarmer);
|
||||
|
||||
// 删除养殖户
|
||||
router.delete('/farmers/:id', farmerController.deleteFarmer);
|
||||
|
||||
// 重置养殖户密码
|
||||
router.post('/farmers/:id/reset-password', farmerController.resetFarmerPassword);
|
||||
|
||||
// 获取养殖类型列表
|
||||
router.get('/farm-types', farmerController.getFarmTypes);
|
||||
|
||||
// 获取养殖种类列表
|
||||
router.get('/animal-types', farmerController.getAnimalTypes);
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,12 +1,20 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const governmentController = require('../controllers/governmentController');
|
||||
|
||||
// 人员列表
|
||||
router.get('/', (req, res) => {
|
||||
res.json({
|
||||
code: 200,
|
||||
data: []
|
||||
});
|
||||
});
|
||||
router.get('/', governmentController.getAdminStaff);
|
||||
|
||||
// 新增行政人员
|
||||
router.post('/', governmentController.createAdminStaff);
|
||||
|
||||
// 编辑行政人员
|
||||
router.put('/:id', governmentController.updateAdminStaff);
|
||||
|
||||
// 删除行政人员
|
||||
router.delete('/:id', governmentController.deleteAdminStaff);
|
||||
|
||||
// 重置行政人员密码
|
||||
router.post('/:id/reset-password', governmentController.resetAdminStaffPassword);
|
||||
|
||||
module.exports = router;
|
||||
55
government-backend/scripts/init-admin-user.js
Normal file
55
government-backend/scripts/init-admin-user.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const User = require('../models/User');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
// 初始化管理员用户
|
||||
async function initAdminUser() {
|
||||
try {
|
||||
console.log('正在初始化管理员用户...');
|
||||
|
||||
// 检查是否已存在管理员用户
|
||||
const existingAdmin = await User.findOne({
|
||||
where: {
|
||||
username: 'admin',
|
||||
role: 'admin'
|
||||
}
|
||||
});
|
||||
|
||||
if (existingAdmin) {
|
||||
console.log('管理员用户已存在,更新密码...');
|
||||
// 加密密码
|
||||
const hashedPassword = await bcrypt.hash('123456', 10);
|
||||
|
||||
// 更新用户密码
|
||||
await existingAdmin.update({
|
||||
password: hashedPassword,
|
||||
status: 'active'
|
||||
});
|
||||
|
||||
console.log('管理员用户密码更新成功');
|
||||
} else {
|
||||
console.log('创建新的管理员用户...');
|
||||
// 加密密码
|
||||
const hashedPassword = await bcrypt.hash('123456', 10);
|
||||
|
||||
// 创建管理员用户
|
||||
await User.create({
|
||||
username: 'admin',
|
||||
password: hashedPassword,
|
||||
role: 'admin',
|
||||
status: 'active'
|
||||
});
|
||||
|
||||
console.log('管理员用户创建成功');
|
||||
}
|
||||
|
||||
console.log('初始化管理员用户完成');
|
||||
} catch (error) {
|
||||
console.error('初始化管理员用户失败:', error);
|
||||
} finally {
|
||||
// 关闭数据库连接
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行初始化
|
||||
initAdminUser();
|
||||
95
government-backend/scripts/syncFarmersModel.js
Normal file
95
government-backend/scripts/syncFarmersModel.js
Normal file
@@ -0,0 +1,95 @@
|
||||
// 数据库模型同步脚本
|
||||
const sequelize = require('../config/database');
|
||||
const Farmer = require('../models/Farmer');
|
||||
|
||||
// 同步数据库模型并添加测试数据
|
||||
async function syncAndSeed() {
|
||||
try {
|
||||
// 同步模型到数据库
|
||||
await sequelize.sync({ alter: true });
|
||||
console.log('数据库模型同步成功');
|
||||
|
||||
// 检查是否已存在测试数据
|
||||
const existingCount = await Farmer.count();
|
||||
|
||||
if (existingCount === 0) {
|
||||
// 添加测试数据
|
||||
const testFarmers = [
|
||||
{
|
||||
account: 'farmer001',
|
||||
nickname: '牛场小王',
|
||||
real_name: '王小明',
|
||||
farm_name: '明辉养殖场',
|
||||
farm_type: '规模',
|
||||
animal_type: '牛',
|
||||
animal_count: 200,
|
||||
address: '内蒙古自治区通辽市科尔沁区',
|
||||
registrar: 'admin',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
account: 'farmer002',
|
||||
nickname: '草原小李',
|
||||
real_name: '李草原',
|
||||
farm_name: '草原牧业',
|
||||
farm_type: '规模',
|
||||
animal_type: '羊',
|
||||
animal_count: 500,
|
||||
address: '内蒙古自治区通辽市开鲁县',
|
||||
registrar: 'admin',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
account: 'farmer003',
|
||||
nickname: '家庭养殖户老张',
|
||||
real_name: '张家庭',
|
||||
farm_name: '张记养殖场',
|
||||
farm_type: '散养',
|
||||
animal_type: '猪',
|
||||
animal_count: 50,
|
||||
address: '内蒙古自治区通辽市扎鲁特旗',
|
||||
registrar: 'admin',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
account: 'farmer004',
|
||||
nickname: '家禽养殖',
|
||||
real_name: '刘家禽',
|
||||
farm_name: '刘家养殖场',
|
||||
farm_type: '规模',
|
||||
animal_type: '鸡',
|
||||
animal_count: 2000,
|
||||
address: '内蒙古自治区通辽市霍林郭勒市',
|
||||
registrar: 'admin',
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
account: 'farmer005',
|
||||
nickname: '特种养殖',
|
||||
real_name: '赵特种',
|
||||
farm_name: '特种养殖场',
|
||||
farm_type: '其他',
|
||||
animal_type: '其他',
|
||||
animal_count: 100,
|
||||
address: '内蒙古自治区通辽市库伦旗',
|
||||
registrar: 'admin',
|
||||
status: 'inactive'
|
||||
}
|
||||
];
|
||||
|
||||
await Farmer.bulkCreate(testFarmers);
|
||||
console.log('测试数据添加成功');
|
||||
} else {
|
||||
console.log('已存在测试数据,跳过添加');
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
await sequelize.close();
|
||||
} catch (error) {
|
||||
console.error('同步数据库模型或添加测试数据失败:', error);
|
||||
// 确保即使出错也关闭连接
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
syncAndSeed();
|
||||
31
government-backend/test-db-connection.js
Normal file
31
government-backend/test-db-connection.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// 测试数据库连接
|
||||
const sequelize = require('./config/database');
|
||||
|
||||
async function testDbConnection() {
|
||||
try {
|
||||
console.log('正在尝试连接数据库...');
|
||||
console.log('连接配置:', {
|
||||
host: sequelize.config.host,
|
||||
port: sequelize.config.port,
|
||||
database: sequelize.config.database,
|
||||
username: sequelize.config.username,
|
||||
dialect: sequelize.config.dialect
|
||||
});
|
||||
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功!');
|
||||
|
||||
// 尝试查询数据库版本信息
|
||||
const [results] = await sequelize.query('SELECT VERSION() AS version');
|
||||
console.log('数据库版本:', results[0].version);
|
||||
|
||||
// 关闭连接
|
||||
await sequelize.close();
|
||||
console.log('数据库连接已关闭');
|
||||
} catch (error) {
|
||||
console.error('❌ 数据库连接失败:', error.message);
|
||||
console.error('详细错误:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testDbConnection();
|
||||
50
government-backend/test-personnel-api.js
Normal file
50
government-backend/test-personnel-api.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// 测试行政人员列表接口
|
||||
const axios = require('axios');
|
||||
|
||||
// 政府后端服务地址
|
||||
const BASE_URL = 'http://localhost:5352/api';
|
||||
|
||||
// 测试行政人员列表接口
|
||||
async function testAdminStaffList() {
|
||||
try {
|
||||
// 先登录获取token
|
||||
const loginResponse = await axios.post(`${BASE_URL}/auth/login`, {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
const token = loginResponse.data.token;
|
||||
console.log('登录成功,获取到token');
|
||||
|
||||
// 使用token访问行政人员列表接口
|
||||
const response = await axios.get(`${BASE_URL}/personnel`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
console.log('行政人员列表接口测试结果:');
|
||||
console.log(`- 状态码: ${response.status}`);
|
||||
console.log(`- 返回数据结构:`, Object.keys(response.data));
|
||||
console.log(`- 行政人员总数: ${response.data.total}`);
|
||||
console.log(`- 返回的行政人员列表长度: ${response.data.data ? response.data.data.length : 0}`);
|
||||
|
||||
if (response.data.data && response.data.data.length > 0) {
|
||||
console.log(`- 第一条行政人员数据:`, response.data.data[0]);
|
||||
} else {
|
||||
console.log('- 行政人员列表为空');
|
||||
}
|
||||
|
||||
console.log('\n测试完成!');
|
||||
} catch (error) {
|
||||
console.error('测试失败:', error.message);
|
||||
if (error.response) {
|
||||
console.error('错误状态码:', error.response.status);
|
||||
console.error('错误数据:', error.response.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
console.log('开始测试行政人员列表接口...');
|
||||
testAdminStaffList();
|
||||
Reference in New Issue
Block a user