初始化

This commit is contained in:
2026-08-08 18:28:49 +08:00
parent 9bef4420e0
commit f082037a6e
854 changed files with 217171 additions and 11 deletions
+176
View File
@@ -0,0 +1,176 @@
<?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();
}
}