Files
MES/app/models/Employee.php
T
2026-08-08 18:28:49 +08:00

177 lines
6.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\models;
use core\base\Model;
/**
* 员工模型
*/
class Employee extends Model
{
protected $table = 'employee';
// 根据员工编号查找
public function findByEmpNo($emp_no)
{
return $this->where(['emp_no = :emp_no'], [':emp_no' => $emp_no])->fetch();
}
/**
* 验证登录(支持 bcrypt 和旧版 MD5 兼容)
*/
public function validateLogin($emp_no, $password)
{
// 先按工号查找用户
$user = $this->where(['emp_no = :emp_no'], [':emp_no' => $emp_no])->fetch();
if (!$user) {
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? substr($_SERVER['HTTP_USER_AGENT'], 0, 150) : 'unknown';
$sess = session_id() ? hash('sha256', session_id()) : 'none';
error_log("[LOGIN_DIAG] emp_no_len=" . strlen($emp_no) . " emp_no_raw='" . $emp_no . "' found_user=0 ua={$ua} sess_hash={$sess}");
return false;
}
// 判断密码是否为 bcrypt 格式(bcrypt 哈希总是以 $2y$ 开头)
$isBcrypt = (substr($user['password'], 0, 4) === '$2y$');
$pwdFmt = $isBcrypt ? 'bcrypt' : (strlen($user['password']) === 32 ? 'md5' : 'other');
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? substr($_SERVER['HTTP_USER_AGENT'], 0, 150) : 'unknown';
$sess = session_id() ? hash('sha256', session_id()) : 'none';
$pwdLen = strlen($password);
error_log("[LOGIN_DIAG] emp_no_len=" . strlen($emp_no) . " emp_no_raw='" . $emp_no . "' found_user=1 pwd_fmt={$pwdFmt} pwd_len={$pwdLen} ua={$ua} sess_hash={$sess}");
if ($isBcrypt) {
if (password_verify($password, $user['password'])) {
// 如果算法需要升级,自动更新哈希
if (password_needs_rehash($user['password'], PASSWORD_BCRYPT)) {
$this->updatePasswordHash($user['id'], $password);
}
return $user;
}
error_log("[LOGIN_DIAG] PWD_VERIFY_FAIL pwd_len={$pwdLen} pwd_hash_len=" . strlen($user['password']) . " ua={$ua} sess_hash={$sess}");
return false;
}
// 兼容旧版 MD5 密码
if (md5($password) === $user['password']) {
// 自动升级为 bcrypt
$this->updatePasswordHash($user['id'], $password);
return $user;
}
return false;
}
/**
* 更新密码哈希为 bcrypt
*/
private function updatePasswordHash($id, $password)
{
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
$this->where(['id = :id'], [':id' => $id])->update(['password' => $hash]);
}
/**
* 生成密码哈希
*/
public function hashPassword($password)
{
return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
}
/**
* 密码复杂度校验(P1:强制强密码策略)
* 要求:≥12 位,且同时包含大写字母、小写字母、数字、特殊字符
* @throws \RuntimeException 不满足时抛出异常
*/
public function validatePasswordStrength($password)
{
if (!is_string($password) || $password === '') {
throw new \RuntimeException('密码不能为空');
}
if (mb_strlen($password) < 12) {
throw new \RuntimeException('密码长度至少 12 位');
}
if (!preg_match('/[A-Z]/', $password)) {
throw new \RuntimeException('密码必须包含至少一个大写字母');
}
if (!preg_match('/[a-z]/', $password)) {
throw new \RuntimeException('密码必须包含至少一个小写字母');
}
if (!preg_match('/[0-9]/', $password)) {
throw new \RuntimeException('密码必须包含至少一个数字');
}
if (!preg_match('/[^A-Za-z0-9]/', $password)) {
throw new \RuntimeException('密码必须包含至少一个特殊字符(如 !@#$%^&*');
}
// 拒绝常见弱密码
$weak = ['123456', 'password', 'admin123', 'qwerty', '111111', '12345678', 'abc123'];
if (in_array(strtolower($password), $weak, true)) {
throw new \RuntimeException('密码过于常见,请更换');
}
}
// 重置密码
public function resetPassword($id, $new_password)
{
$this->validatePasswordStrength($new_password);
$hash = $this->hashPassword($new_password);
return $this->where(['id = :id'], [':id' => $id])->update(['password' => $hash]);
}
// 获取所有员工
public function getAll()
{
return $this->order(['id ASC'])->fetchAll();
}
// 添加员工
public function addEmployee($data)
{
if (!isset($data['password']) || empty(trim($data['password']))) {
// 生成随机强密码
$randomPwd = bin2hex(random_bytes(8)); // 16位随机密码
$data['password'] = $this->hashPassword($randomPwd);
// 返回生成的密码供管理员告知用户
$data['_generated_password'] = $randomPwd;
} else {
// P1:密码复杂度校验(≥12位,含大小写+数字+特殊字符)
$this->validatePasswordStrength($data['password']);
$data['password'] = $this->hashPassword($data['password']);
}
// 移除 _generated_password 再写入
$generatedPwd = $data['_generated_password'] ?? null;
unset($data['_generated_password']);
$id = $this->add($data);
if ($id && $generatedPwd) {
// 返回生成的密码
$this->_lastGeneratedPwd = $generatedPwd;
}
return $id;
}
// 获取最后生成的密码
public function getLastGeneratedPwd()
{
return $this->_lastGeneratedPwd ?? null;
}
private $_lastGeneratedPwd = null;
// 更新员工
public function updateEmployee($id, $data)
{
unset($data['password']); // 不允许直接更新密码
return $this->where(['id = :id'], [':id' => $id])->update($data);
}
// 根据类目获取员工列表
public function getByCategory($category)
{
if ($category === 'all') {
return $this->order(['id ASC'])->fetchAll();
}
return $this->where(['category = :cat OR role = :super'], [
':cat' => $category,
':super' => 'super_admin'
])->order(['id ASC'])->fetchAll();
}
}