1, 'message' => $message, 'data' => $data, ], JSON_UNESCAPED_UNICODE); exit; } /** * 失败响应 */ private function fail($message, $code = 0, $data = []) { echo json_encode([ 'code' => $code, 'message' => $message, 'data' => $data, ], JSON_UNESCAPED_UNICODE); exit; } /** * 获取 JSON 请求体 */ private function getJsonInput() { $input = file_get_contents('php://input'); $data = json_decode($input, true); return is_array($data) ? $data : []; } /** * Token 认证 — 验证请求中的 Bearer Token * @return array 用户信息 {id, emp_no, emp_name, role} */ protected function tokenAuth() { $token = ''; $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''; if (preg_match('/^Bearer\s+(.+)$/i', $authHeader, $matches)) { $token = $matches[1]; } if (empty($token)) { $this->fail('未登录,请先登录'); } try { $pdo = \core\db\Db::pdo(); $stmt = $pdo->prepare( "SELECT t.*, e.emp_name, e.role FROM DGZXY_miniapp_token t JOIN DGZXY_employee e ON t.employee_id = e.id WHERE t.token = :token AND t.expires_at > NOW() AND t.status = 1" ); $stmt->execute([':token' => $token]); $tokenInfo = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$tokenInfo) { $this->fail('Token无效或已过期,请重新登录', 401); } return [ 'id' => (int)$tokenInfo['employee_id'], 'emp_no' => $tokenInfo['emp_no'], 'emp_name' => $tokenInfo['emp_name'], 'role' => $tokenInfo['role'], ]; } catch (\Throwable $e) { $this->fail('认证服务异常'); } } /** * 生成 Token(登录成功后调用) */ private function generateToken($employeeId, $empNo) { $token = bin2hex(random_bytes(32)); $expiresAt = date('Y-m-d H:i:s', time() + 7 * 24 * 3600); // 7天有效期 try { $pdo = \core\db\Db::pdo(); // 使旧 token 失效 $pdo->prepare("UPDATE DGZXY_miniapp_token SET status = 0 WHERE employee_id = :eid") ->execute([':eid' => $employeeId]); // 插入新 token $pdo->prepare( "INSERT INTO DGZXY_miniapp_token (token, employee_id, emp_no, expires_at, status, created_at) VALUES (:token, :eid, :emp_no, :expires, 1, NOW())" )->execute([ ':token' => $token, ':eid' => $employeeId, ':emp_no' => $empNo, ':expires' => $expiresAt, ]); } catch (\Throwable $e) { // Token 表不存在时创建 if (strpos($e->getMessage(), 'exist') !== false || strpos($e->getMessage(), 'not found') !== false) { $this->createTokenTable(); return $this->generateToken($employeeId, $empNo); } } return [ 'token' => $token, 'expires_at' => $expiresAt, ]; } /** * 创建 Token 表(首次运行时) */ private function createTokenTable() { $pdo = \core\db\Db::pdo(); $pdo->exec(" CREATE TABLE IF NOT EXISTS DGZXY_miniapp_token ( id INT AUTO_INCREMENT PRIMARY KEY, token VARCHAR(128) NOT NULL, employee_id INT NOT NULL, emp_no VARCHAR(50) NOT NULL, expires_at DATETIME NOT NULL, status TINYINT DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_token (token), INDEX idx_employee (employee_id), INDEX idx_expires (expires_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 "); } /** * 检查工位权限 */ private function checkStationPermission($user, $stationType) { if ($user['role'] === Controller::ROLE_SUPER_ADMIN || $user['role'] === Controller::ROLE_ADMIN) { return true; } $permModel = new EmployeePermission(); return $permModel->canAccess($user['id'], $user['role'], $stationType); } /** * 获取工位字段配置 */ private function getStationFieldConfig($stationType) { $ws = new Workstation(); $station = $ws->getByType($stationType); if ($station && !empty($station['fields_config'])) { return parseFieldsConfig($stationType, $station['fields_config']); } return getDefaultFieldsConfig($stationType); } /** * 获取用户可访问的工位列表 */ private function getAllowedStations($user) { $workstation = new Workstation(); $allStations = $workstation->getAll(); if ($user['role'] === Controller::ROLE_SUPER_ADMIN || $user['role'] === Controller::ROLE_ADMIN) { return $allStations; } $permModel = new EmployeePermission(); $allowedTypes = $permModel->getAllowedStations($user['id']); if (empty($allowedTypes)) { return $allStations; } return array_values(array_filter($allStations, function($s) use ($allowedTypes) { return in_array($s['station_type'], $allowedTypes); })); } // ==================== 1. 登录 ==================== /** * POST /MiniApp/login * 请求: { emp_no: "员工编号", password: "密码" } * 响应: { code:1, data: { token, expires_at, user: { id, emp_no, emp_name, role } } } */ public function login() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->fail('请使用 POST 请求'); } $data = $this->getJsonInput(); $empNo = trim($data['emp_no'] ?? ''); $password = $data['password'] ?? ''; if (empty($empNo) || empty($password)) { $this->fail('员工编号和密码不能为空'); } // 暴力破解防护(基于数据库记录) try { $pdo = \core\db\Db::pdo(); // 检查最近 15 分钟内的失败次数 $stmt = $pdo->prepare( "SELECT COUNT(*) as cnt FROM DGZXY_miniapp_login_log WHERE emp_no = :eno AND success = 0 AND created_at > DATE_SUB(NOW(), INTERVAL 15 MINUTE)" ); $stmt->execute([':eno' => $empNo]); $failCount = $stmt->fetch(\PDO::FETCH_ASSOC)['cnt'] ?? 0; if ($failCount >= 5) { $this->fail('登录尝试次数过多,请15分钟后再试'); } } catch (\Throwable $e) { // 日志表不存在时忽略 } $employee = new Employee(); $user = $employee->validateLogin($empNo, $password); if (!$user) { // 记录失败日志 try { $pdo = \core\db\Db::pdo(); $pdo->prepare( "INSERT INTO DGZXY_miniapp_login_log (emp_no, success, ip, created_at) VALUES (:eno, 0, :ip, NOW())" )->execute([':eno' => $empNo, ':ip' => $_SERVER['REMOTE_ADDR'] ?? '']); } catch (\Throwable $e) {} $this->fail('员工编号或密码错误'); } // 生成 Token $tokenInfo = $this->generateToken($user['id'], $user['emp_no']); // 记录成功日志 try { $pdo = \core\db\Db::pdo(); $pdo->prepare( "INSERT INTO DGZXY_miniapp_login_log (emp_no, success, ip, created_at) VALUES (:eno, 1, :ip, NOW())" )->execute([':eno' => $empNo, ':ip' => $_SERVER['REMOTE_ADDR'] ?? '']); } catch (\Throwable $e) {} $this->success([ 'token' => $tokenInfo['token'], 'expires_at' => $tokenInfo['expires_at'], 'user' => [ 'id' => (int)$user['id'], 'emp_no' => $user['emp_no'], 'emp_name' => $user['emp_name'], 'role' => $user['role'], ], ], '登录成功'); } // ==================== 2. 退出登录 ==================== /** * POST /MiniApp/logout * Headers: Authorization: Bearer */ public function logout() { $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''; $token = ''; if (preg_match('/^Bearer\s+(.+)$/i', $authHeader, $matches)) { $token = $matches[1]; } if (!empty($token)) { try { $pdo = \core\db\Db::pdo(); $pdo->prepare("UPDATE DGZXY_miniapp_token SET status = 0 WHERE token = :t") ->execute([':t' => $token]); } catch (\Throwable $e) {} } $this->success([], '已退出登录'); } // ==================== 3. 获取用户信息 ==================== /** * GET /MiniApp/userInfo * Headers: Authorization: Bearer */ public function userInfo() { $user = $this->tokenAuth(); $this->success(['user' => $user]); } // ==================== 4. 获取工位列表 ==================== /** * GET /MiniApp/stations * Headers: Authorization: Bearer * 响应: { stations: [{id, station_name, station_type, station_code, icon, sort_order}] } */ public function stations() { $user = $this->tokenAuth(); $stations = $this->getAllowedStations($user); // 只返回小程序需要的字段 $result = array_map(function($s) { return [ 'id' => (int)$s['id'], 'station_name' => $s['station_name'] ?? '', 'station_type' => $s['station_type'] ?? '', 'station_code' => $s['station_code'] ?? '', 'icon' => $s['icon'] ?? 'cube', 'sort_order' => (int)($s['sort_order'] ?? 0), ]; }, $stations); $this->success(['stations' => $result]); } // ==================== 5. 获取工位字段配置 ==================== /** * GET /MiniApp/stationConfig?type=inbound * Headers: Authorization: Bearer * 响应: { station_type, fields: [...], data_sources: { types, models, customers } } */ public function stationConfig() { $user = $this->tokenAuth(); $stationType = trim($_GET['type'] ?? ''); if (empty($stationType)) { $this->fail('缺少工位类型参数 type'); } // 权限检查 if (!$this->checkStationPermission($user, $stationType)) { $this->fail('无权限访问该工位', 403); } // 获取字段配置 $rawFieldConfigs = $this->getStationFieldConfig($stationType); $fieldMeta = $rawFieldConfigs['_meta'] ?? []; unset($rawFieldConfigs['_meta']); $fieldConfigs = array_values($rawFieldConfigs); // 获取数据源 $productModel = new ProductModel(); $customer = new Customer(); // 从 fields_config._meta.model_type_filter 读取型号筛选类型 $defaultProductType = $fieldMeta['model_type_filter'] ?? ''; // 向后兼容:bizConfig 硬编码兜底 if (empty($defaultProductType)) { if ($stationType === 'inbound') $defaultProductType = '半成品'; if ($stationType === 'warehouse') $defaultProductType = '成品'; } $models = !empty($defaultProductType) ? $productModel->getByProductType($defaultProductType) : $productModel->getAll(); $dataSources = [ 'types' => $productModel->getProductTypes(), 'models' => $models, 'customers' => ($stationType === 'delivery') ? $customer->getAll() : [], ]; $this->success([ 'station_type' => $stationType, 'fields' => $fieldConfigs, 'data_sources' => $dataSources, ]); } // ==================== 6. 获取型号列表(按产品类型筛选) ==================== /** * GET /MiniApp/models?product_type=半成品 * Headers: Authorization: Bearer */ public function models() { $this->tokenAuth(); $productType = trim($_GET['product_type'] ?? ''); $productModel = new ProductModel(); if ($productType === '') { $models = $productModel->getAll(); } else { $models = $productModel->getByProductType($productType); } $this->success(['models' => $models]); } // ==================== 7. 半成品入库 ==================== /** * POST /MiniApp/inbound * Headers: Authorization: Bearer * 请求: { product_type, product_model, serial_no } */ public function inbound() { $user = $this->tokenAuth(); if (!$this->checkStationPermission($user, 'inbound')) { $this->fail('无权限操作该工位', 403); } $data = $this->getJsonInput(); // 必填校验 $errors = []; if (empty($data['product_type'])) $errors[] = '产品类型不能为空'; if (empty($data['product_model'])) $errors[] = '产品型号不能为空'; if (empty($data['serial_no'])) $errors[] = '产品序列号不能为空'; if (!empty($errors)) { $this->fail(implode(';', $errors)); } // 检查序列号是否已存在 $stationDef = new StationDefinition(); $existing = $stationDef->findByField('inbound', 'serial_no', $data['serial_no']); if ($existing) { $this->fail('该序列号已入库'); } $record = [ 'product_type' => $data['product_type'], 'product_model' => $data['product_model'], 'serial_no' => $data['serial_no'], 'test_status' => 'pending', 'operator' => $user['emp_name'], ]; $result = $stationDef->addRecord('inbound', $record); if (!$result) { $this->fail('入库失败'); } $this->success(['id' => $id], '入库成功'); } // ==================== 8. PCB 测试 ==================== /** * POST /MiniApp/pcbTest * Headers: Authorization: Bearer * 请求: { product_type, product_model, serial_no, static_power?, input_output?, test_status? } */ public function pcbTest() { $user = $this->tokenAuth(); if (!$this->checkStationPermission($user, 'pcb_test')) { $this->fail('无权限操作该工位', 403); } $data = $this->getJsonInput(); if (empty($data['serial_no'])) { $this->fail('PCB序列号不能为空'); } $stationDef = new StationDefinition(); $record = [ 'product_type' => $data['product_type'] ?? '', 'product_model' => $data['product_model'] ?? '', 'serial_no' => $data['serial_no'], 'static_power' => isset($data['static_power']) && $data['static_power'] !== '' ? $data['static_power'] : null, 'input_output' => $data['input_output'] ?? null, 'test_status' => $data['test_status'] ?? null, 'operator' => $user['emp_name'], ]; $result = $stationDef->addRecord('pcb_test', $record); if (!$result) { $this->fail('提交失败'); } $this->success([], 'PCB测试数据提交成功'); } // ==================== 9. 电池状态 ==================== /** * POST /MiniApp/battery * Headers: Authorization: Bearer * 请求: { serial_no, voltage_platform?, no_load_voltage?, internal_resistance? } */ public function battery() { $user = $this->tokenAuth(); if (!$this->checkStationPermission($user, 'battery')) { $this->fail('无权限操作该工位', 403); } $data = $this->getJsonInput(); if (empty($data['serial_no'])) { $this->fail('产品序列号不能为空'); } $stationDef = new StationDefinition(); $record = [ 'serial_no' => $data['serial_no'], 'voltage_platform' => isset($data['voltage_platform']) && $data['voltage_platform'] !== '' ? $data['voltage_platform'] : null, 'no_load_voltage' => isset($data['no_load_voltage']) && $data['no_load_voltage'] !== '' ? $data['no_load_voltage'] : null, 'internal_resistance' => isset($data['internal_resistance']) && $data['internal_resistance'] !== '' ? $data['internal_resistance'] : null, 'operator' => $user['emp_name'], ]; $result = $stationDef->addRecord('battery', $record); if (!$result) { $this->fail('提交失败'); } $this->success([], '电池数据提交成功'); } // ==================== 10. 产品组装(三码关联) ==================== /** * POST /MiniApp/assembly * Headers: Authorization: Bearer * 请求: { product_model, finished_serial, battery_serial, pcb_serial } */ public function assembly() { $user = $this->tokenAuth(); if (!$this->checkStationPermission($user, 'assembly')) { $this->fail('无权限操作该工位', 403); } $data = $this->getJsonInput(); $errors = []; if (empty($data['finished_serial'])) $errors[] = '成品序列号不能为空'; if (empty($data['battery_serial'])) $errors[] = '电池序列号不能为空'; if (empty($data['pcb_serial'])) $errors[] = 'PCB序列号不能为空'; if (empty($data['product_model'])) $errors[] = '产品型号不能为空'; if (!empty($errors)) { $this->fail(implode(';', $errors)); } $stationDef = new StationDefinition(); // 检查成品序列号是否已组装 $existing = $stationDef->findByField('assembly', 'finished_serial', $data['finished_serial']); if ($existing) { $this->fail('该成品序列号(' . $data['finished_serial'] . ')已组装,不能重复提交'); } $record = [ 'finished_serial' => $data['finished_serial'], 'battery_serial' => $data['battery_serial'], 'pcb_serial' => $data['pcb_serial'], 'product_model' => $data['product_model'], 'operator' => $user['emp_name'], ]; $result = $stationDef->addRecord('assembly', $record); if (!$result) { $this->fail('组装记录提交失败'); } $this->success([], '组装记录提交成功'); } // ==================== 11. 成品测试 ==================== /** * POST /MiniApp/finishedTest * Headers: Authorization: Bearer * 请求: { finished_serial, test_item, test_result, test_steps?, product_model? } */ public function finishedTest() { $user = $this->tokenAuth(); if (!$this->checkStationPermission($user, 'finished_test')) { $this->fail('无权限操作该工位', 403); } $data = $this->getJsonInput(); if (empty($data['finished_serial'])) $this->fail('成品序列号不能为空'); if (empty($data['test_item'])) $this->fail('测试项目不能为空'); if (empty($data['test_result'])) $this->fail('测试结果不能为空'); $stationDef = new StationDefinition(); $record = [ 'finished_serial' => $data['finished_serial'], 'test_item' => $data['test_item'], 'test_result' => $data['test_result'], 'test_steps' => $data['test_steps'] ?? '', 'product_model' => $data['product_model'] ?? '', 'operator' => $user['emp_name'], ]; $result = $stationDef->addRecord('finished_test', $record); if (!$result) { $this->fail('提交失败'); } $this->success([], '测试数据提交成功'); } // ==================== 12. 成品入库 ==================== /** * POST /MiniApp/warehouse * Headers: Authorization: Bearer * 请求: { finished_serial, product_model } */ public function warehouse() { $user = $this->tokenAuth(); if (!$this->checkStationPermission($user, 'warehouse')) { $this->fail('无权限操作该工位', 403); } $data = $this->getJsonInput(); if (empty($data['finished_serial'])) $this->fail('成品序列号不能为空'); $stationDef = new StationDefinition(); $record = [ 'finished_serial' => $data['finished_serial'], 'product_model' => $data['product_model'] ?? '', 'operator' => $user['emp_name'], ]; $result = $stationDef->addRecord('warehouse', $record); if (!$result) { $this->fail('入库失败'); } $this->success([], '入库成功'); } // ==================== 13. 出货登记 ==================== /** * POST /MiniApp/delivery * Headers: Authorization: Bearer * 请求: { finished_serial, customer_name, product_model } */ public function delivery() { $user = $this->tokenAuth(); if (!$this->checkStationPermission($user, 'delivery')) { $this->fail('无权限操作该工位', 403); } $data = $this->getJsonInput(); if (empty($data['finished_serial'])) $this->fail('成品序列号不能为空'); if (empty($data['customer_name'])) $this->fail('客户名称不能为空'); $stationDef = new StationDefinition(); $record = [ 'finished_serial' => $data['finished_serial'], 'customer_name' => $data['customer_name'], 'product_model' => $data['product_model'] ?? '', 'operator' => $user['emp_name'], ]; $result = $stationDef->addRecord('delivery', $record); if (!$result) { $this->fail('出货登记失败'); } $this->success([], '出货登记成功'); } // ==================== 14. 工位记录查询 ==================== /** * GET /MiniApp/records?type=inbound&page=1&limit=20 * Headers: Authorization: Bearer */ public function records() { $user = $this->tokenAuth(); $stationType = trim($_GET['type'] ?? ''); $page = max(1, intval($_GET['page'] ?? 1)); $limit = min(100, max(1, intval($_GET['limit'] ?? 20))); $offset = ($page - 1) * $limit; if (empty($stationType)) { $this->fail('缺少工位类型参数 type'); } if (!$this->checkStationPermission($user, $stationType)) { $this->fail('无权限查看该工位记录', 403); } // 获取字段配置(用于表头信息) $rawFieldConfigs = $this->getStationFieldConfig($stationType); unset($rawFieldConfigs['_meta']); $fieldConfigs = array_values($rawFieldConfigs); try { $pdo = \core\db\Db::pdo(); $tableMap = [ 'inbound' => 'station_inbound', 'pcb_test' => 'pcb_test', 'battery' => 'station_battery', 'assembly' => 'product_assembly', 'finished_test' => 'station_finished_test', 'warehouse' => 'station_warehouse', 'delivery' => 'station_delivery', ]; $table = $tableMap[$stationType] ?? null; if (!$table) { $this->fail('无效的工位类型'); } $fullTable = 'DGZXY_' . $table; // 总数 $countSql = "SELECT COUNT(*) as total FROM {$fullTable}"; $countStmt = $pdo->query($countSql); $total = $countStmt->fetch(\PDO::FETCH_ASSOC)['total'] ?? 0; // 分页查询 $stmt = $pdo->prepare("SELECT * FROM {$fullTable} ORDER BY id DESC LIMIT :limit OFFSET :offset"); $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); $stmt->bindValue(':offset', $offset, \PDO::PARAM_INT); $stmt->execute(); $records = $stmt->fetchAll(\PDO::FETCH_ASSOC); } catch (\Throwable $e) { $this->fail('查询失败: ' . $e->getMessage()); } $this->success([ 'records' => $records, 'fields' => $fieldConfigs, 'total' => (int)$total, 'page' => $page, 'limit' => $limit, 'total_pages' => ceil($total / $limit), ]); } // ==================== 15. 今日统计 ==================== /** * GET /MiniApp/dashboard * Headers: Authorization: Bearer * 响应: { today: { inbound, pcb_test, battery, assembly, finished_test, warehouse, delivery }, total } */ public function dashboard() { $user = $this->tokenAuth(); try { $pdo = \core\db\Db::pdo(); $tables = [ 'inbound' => 'station_inbound', 'pcb_test' => 'pcb_test', 'battery' => 'station_battery', 'assembly' => 'product_assembly', 'finished_test' => 'station_finished_test', 'warehouse' => 'station_warehouse', 'delivery' => 'station_delivery', ]; $today = []; foreach ($tables as $key => $table) { $stmt = $pdo->prepare( "SELECT COUNT(*) as cnt FROM DGZXY_{$table} WHERE operator = :op AND DATE(created_at) = CURDATE()" ); $stmt->execute([':op' => $user['emp_name']]); $today[$key] = (int)($stmt->fetch(\PDO::FETCH_ASSOC)['cnt'] ?? 0); } $total = array_sum($today); } catch (\Throwable $e) { $today = []; $total = 0; } $this->success([ 'today' => $today, 'total' => $total, ]); } // ==================== 16. 产品追溯查询 ==================== /** * GET /MiniApp/productSearch?keyword=序列号 * Headers: Authorization: Bearer * 跨工位搜索序列号,返回产品全链路信息 */ public function productSearch() { $user = $this->tokenAuth(); $keyword = trim($_GET['keyword'] ?? ''); if (empty($keyword)) { $this->fail('请输入搜索关键词'); } $pdo = \core\db\Db::pdo(); $result = [ 'keyword' => $keyword, 'traces' => [], ]; // 每个表查询独立 try-catch,某个表不存在不影响其他查询 $queries = [ ['source' => 'inbound', 'sql' => "SELECT 'inbound' as source, id, serial_no, product_type, product_model, test_status, operator, created_at FROM DGZXY_station_inbound WHERE serial_no = :kw LIMIT 1", 'params' => [':kw' => $keyword]], ['source' => 'pcb_test', 'sql' => "SELECT 'pcb_test' as source, id, serial_no, product_type, product_model, static_power, input_output, test_status, operator, created_at FROM DGZXY_pcb_test WHERE serial_no = :kw LIMIT 1", 'params' => [':kw' => $keyword]], ['source' => 'battery', 'sql' => "SELECT 'battery' as source, id, serial_no, voltage_platform, no_load_voltage, internal_resistance, operator, created_at FROM DGZXY_station_battery WHERE serial_no = :kw LIMIT 1", 'params' => [':kw' => $keyword]], ['source' => 'assembly', 'sql' => "SELECT 'assembly' as source, id, finished_serial, battery_serial, pcb_serial, product_model, operator, created_at FROM DGZXY_product_assembly WHERE finished_serial = :kw OR battery_serial = :kw2 OR pcb_serial = :kw3 LIMIT 5", 'params' => [':kw' => $keyword, ':kw2' => $keyword, ':kw3' => $keyword]], ['source' => 'finished_test', 'sql' => "SELECT 'finished_test' as source, id, finished_serial, test_item, test_result, test_steps, product_model, operator, created_at FROM DGZXY_station_finished_test WHERE finished_serial = :kw LIMIT 5", 'params' => [':kw' => $keyword]], ['source' => 'warehouse', 'sql' => "SELECT 'warehouse' as source, id, finished_serial, product_model, operator, created_at as in_time FROM DGZXY_station_warehouse WHERE finished_serial = :kw LIMIT 1", 'params' => [':kw' => $keyword]], ['source' => 'delivery', 'sql' => "SELECT 'delivery' as source, id, finished_serial, customer_name, product_model, operator, created_at FROM DGZXY_station_delivery WHERE finished_serial = :kw LIMIT 1", 'params' => [':kw' => $keyword]], ]; foreach ($queries as $q) { try { $stmt = $pdo->prepare($q['sql']); $stmt->execute($q['params']); while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $result['traces'][] = $row; } } catch (\Throwable $e) { // 表不存在时跳过,继续查询其他表 continue; } } $result['count'] = count($result['traces']); $this->success($result); } // ==================== 17. 修改密码 ==================== /** * POST /MiniApp/changePassword * Headers: Authorization: Bearer * 请求: { old_password, new_password } */ public function changePassword() { $user = $this->tokenAuth(); $data = $this->getJsonInput(); $oldPassword = $data['old_password'] ?? ''; $newPassword = $data['new_password'] ?? ''; if (empty($oldPassword)) $this->fail('请输入旧密码'); if (empty($newPassword)) $this->fail('请输入新密码'); if (strlen($newPassword) < 8) $this->fail('新密码长度不能少于8位'); // 验证旧密码 $employee = new Employee(); $valid = $employee->validateLogin($user['emp_no'], $oldPassword); if (!$valid) { $this->fail('旧密码错误'); } // 更新密码 $employee->resetPassword($user['id'], $newPassword); // 使所有 token 失效(强制重新登录) try { $pdo = \core\db\Db::pdo(); $pdo->prepare("UPDATE DGZXY_miniapp_token SET status = 0 WHERE employee_id = :eid") ->execute([':eid' => $user['id']]); } catch (\Throwable $e) {} $this->success([], '密码修改成功,请重新登录'); } } // ==================== 辅助函数 ==================== /** * 解析工位字段配置(JSON字符串转数组) */ function parseFieldsConfig($stationType, $fieldsConfigJson) { $config = is_string($fieldsConfigJson) ? json_decode($fieldsConfigJson, true) : $fieldsConfigJson; if (!is_array($config)) { return getDefaultFieldsConfig($stationType); } return $config; } /** * 获取工位默认字段配置 * 根据工位类型返回对应字段定义 */ function getDefaultFieldsConfig($stationType) { $configs = [ 'inbound' => [ ['field' => 'product_type', 'label' => '产品类型', 'type' => 'select', 'required' => true, 'source' => 'types'], ['field' => 'product_model', 'label' => '产品型号', 'type' => 'select', 'required' => true, 'source' => 'models'], ['field' => 'serial_no', 'label' => '产品序列号', 'type' => 'text', 'required' => true], ], 'pcb_test' => [ ['field' => 'product_type', 'label' => '产品类型', 'type' => 'select', 'required' => false, 'source' => 'types'], ['field' => 'product_model', 'label' => '产品型号', 'type' => 'select', 'required' => false, 'source' => 'models'], ['field' => 'serial_no', 'label' => 'PCB序列号', 'type' => 'text', 'required' => true], ['field' => 'static_power', 'label' => '静态功耗', 'type' => 'number', 'required' => false], ['field' => 'input_output', 'label' => '输入输出', 'type' => 'text', 'required' => false], ['field' => 'test_status', 'label' => '测试状态', 'type' => 'select', 'required' => false, 'options' => ['pass', 'fail', 'pending']], ], 'battery' => [ ['field' => 'serial_no', 'label' => '产品序列号', 'type' => 'text', 'required' => true], ['field' => 'voltage_platform', 'label' => '电压平台(V)', 'type' => 'number', 'required' => false], ['field' => 'no_load_voltage', 'label' => '空载电压(V)', 'type' => 'number', 'required' => false], ['field' => 'internal_resistance', 'label' => '内阻(mOhm)', 'type' => 'number', 'required' => false], ], 'assembly' => [ ['field' => 'finished_serial', 'label' => '成品序列号', 'type' => 'text', 'required' => true], ['field' => 'battery_serial', 'label' => '电池序列号', 'type' => 'text', 'required' => true], ['field' => 'pcb_serial', 'label' => 'PCB序列号', 'type' => 'text', 'required' => true], ['field' => 'product_model', 'label' => '产品型号', 'type' => 'select', 'required' => true, 'source' => 'models'], ], 'finished_test' => [ ['field' => 'finished_serial', 'label' => '成品序列号', 'type' => 'text', 'required' => true], ['field' => 'test_item', 'label' => '测试项目', 'type' => 'text', 'required' => true], ['field' => 'test_result', 'label' => '测试结果', 'type' => 'select', 'required' => true, 'options' => ['pass', 'fail']], ['field' => 'test_steps', 'label' => '测试步骤', 'type' => 'textarea', 'required' => false], ['field' => 'product_model', 'label' => '产品型号', 'type' => 'select', 'required' => false, 'source' => 'models'], ], 'warehouse' => [ ['field' => 'finished_serial', 'label' => '成品序列号', 'type' => 'text', 'required' => true], ['field' => 'product_model', 'label' => '产品型号', 'type' => 'select', 'required' => false, 'source' => 'models'], ], 'delivery' => [ ['field' => 'finished_serial', 'label' => '成品序列号', 'type' => 'text', 'required' => true], ['field' => 'customer_name', 'label' => '客户名称', 'type' => 'select', 'required' => true, 'source' => 'customers'], ['field' => 'product_model', 'label' => '产品型号', 'type' => 'select', 'required' => false, 'source' => 'models'], ], ]; return $configs[$stationType] ?? []; }