['02、胶印机组', '03、卷凹机组', '06、单凹机组', '05、圆切机组', '04、圆烫机组', '10、模切机组', '09、烫金机组'], // 1楼编组 // '2' => ['01、切纸机组', '11、检品机组', '07、丝印机组', '12、覆膜机组', '08、喷码机组'], // 2楼编组 // ]; // // // 科目名称常量(简化版) // const SUBJECT_WASTE_GAS = '废气处理(RTO)'; // const SUBJECT_BOILER = '锅炉'; // const SUBJECT_AIR_COMPRESSOR = '空压机'; // const SUBJECT_HOT_WATER_BOILER = '热水锅炉'; // const SUBJECT_VACUUM_BLOWER = '真空鼓风机'; // const SUBJECT_CENTRAL_AIR_CONDITIONER = '中央空调'; // const SUBJECT_TOTAL_TO_APPORTION = '待分摊总额'; // // // 需要分配到卷凹机组的科目列表 // const ROLL_COATER_ONLY_SUBJECTS = [ // self::SUBJECT_BOILER, // self::SUBJECT_HOT_WATER_BOILER, // ]; // // // 简化科目列表 // const SIMPLIFIED_SUBJECTS = [ // self::SUBJECT_WASTE_GAS, // self::SUBJECT_BOILER, // self::SUBJECT_AIR_COMPRESSOR, // self::SUBJECT_HOT_WATER_BOILER, // self::SUBJECT_VACUUM_BLOWER, // self::SUBJECT_CENTRAL_AIR_CONDITIONER, // self::SUBJECT_TOTAL_TO_APPORTION, // ]; // // /** // * 根据设备编号获取楼层信息 // */ // protected function getFloorByMachineCode($machineCode) // { // static $machineFloorCache = []; // // if (isset($machineFloorCache[$machineCode])) { // return $machineFloorCache[$machineCode]; // } // // // 查询设备编组 // $group = Db::name('设备_基本资料') // ->where('sys_sbID', '<>','') // ->where('设备编号', $machineCode) // ->value('设备编组'); // // if (!$group) { // $machineFloorCache[$machineCode] = null; // return null; // } // // // 根据编组判断楼层 // foreach (self::FLOOR_GROUP_MAP as $floor => $groups) { // foreach ($groups as $groupName) { // if (strpos($group, $groupName) !== false) { // $machineFloorCache[$machineCode] = $floor; // return $floor; // } // } // } // // $machineFloorCache[$machineCode] = null; // return null; // } // // /** // * 根据楼层获取该楼层的所有机台 // */ // protected function getMachinesByFloor($floor, $month) // { // // 获取该楼层的所有编组 // $groups = self::FLOOR_GROUP_MAP[$floor] ?? []; // if (empty($groups)) { // return []; // } // // try { // // 方法1:使用更简单的查询条件构建方式 // $query = Db::name('成本v23_月度成本明细') // ->alias('c') // ->join('设备_基本资料 e', 'c.sczl_jtbh = e.设备编号', 'LEFT') // ->where('c.sys_ny', $month) // ->where('e.设备编号', '<>', '') // ->whereIn('e.设备编组', $groups); // // // $query->field([ // 'c.sczl_jtbh' => '机台编号', // 'c.占用机时', // 'e.设备编组', // 'c.sczl_gdbh' => '工单编号', // 'c.sczl_yjno' => '印件号', // 'c.sczl_gxh' => '工序号', // 'c.Uniqid', // 'c.车间名称' // ]); // // $machines = $query->select(); // // return $machines ?: []; // // } catch (\Exception $e) { // throw new \Exception('查询机台数据失败: ' . $e->getMessage()); // // } // } // // /** // * 获取所有机台(不分楼层) // */ // protected function getAllMachines($month) // { // return Db::name('成本v23_月度成本明细') // ->alias('c') // ->join('设备_基本资料 e', 'c.sczl_jtbh = e.设备编号') // ->where('c.sys_ny', $month) // ->where('e.设备编号', '<>', '') // ->field([ // 'c.sczl_jtbh' => '机台编号', // 'c.占用机时', // 'e.设备编组', // 'c.sczl_gdbh' => '工单编号', // 'c.sczl_yjno' => '印件号', // 'c.sczl_gxh' => '工序号', // 'c.Uniqid', // 'c.车间名称' // ]) // ->select(); // } // // /** // * 获取卷凹机组的所有机台 // */ // protected function getRollCoaterMachines($month) // { // return Db::name('成本v23_月度成本明细') // ->alias('c') // ->join('设备_基本资料 e', 'c.sczl_jtbh = e.设备编号') // ->where('c.sys_ny', $month) // ->where('e.设备编号', '<>', '') // ->where('e.设备编组', 'like', '%03、卷凹机组%') // ->field([ // 'c.sczl_jtbh' => '机台编号', // 'c.占用机时', // 'e.设备编组', // 'c.sczl_gdbh' => '工单编号', // 'c.sczl_yjno' => '印件号', // 'c.sczl_gxh' => '工序号', // 'c.Uniqid', // 'c.车间名称' // ]) // ->select(); // } // // /** // * 主计算方法 // */ // public function costCalculation() // { // if (!$this->request->isGet()) { // $this->error('请求错误'); // } // // $param = $this->request->param(); // if (empty($param['month'])) { // $this->error('请选择月份'); // } // // $month = $param['month']; // $sysId = $param['sys_id'] ?? ''; // // // // 计算分摊 // $apportionmentResults = $this->calculateApportionment($month); // // // 格式化结果并保存 // $formattedResults = $this->formatApportionmentResults($apportionmentResults, $month, $sysId); // $this->saveApportionmentCoefficients($formattedResults, $month); // // // 进行二次分配(到工单) // $this->allocateToWorkOrders($month); // // $this->success('成本分摊计算成功'); // } // // /** // * 计算分摊(核心方法) // */ // protected function calculateApportionment($month) // { // // 获取水电气分摊数据 // $utilityData = $this->getUtilityData($month); // // $results = []; // // foreach ($utilityData as $item) { // $subject = $this->simplifySubjectName($item['科目名称']); // $amount = $this->calculateAmount($item); // // if ($subject === self::SUBJECT_TOTAL_TO_APPORTION) { // // 待分摊总额按楼层分摊 // $floorResults = $this->allocateByFloor($amount, $month); // // foreach ($floorResults as $floor => $machineAllocations) { // foreach ($machineAllocations as $machineCode => $machineAmount) { // if (!isset($results[$machineCode])) { // $results[$machineCode] = []; // } // $results[$machineCode][$subject] = $machineAmount; // } // } // } elseif (in_array($subject, self::ROLL_COATER_ONLY_SUBJECTS)) { // // 锅炉和热水锅炉只分配到卷凹机组 // $rollCoaterResults = $this->allocateToRollCoaterOnly($amount, $month); // // foreach ($rollCoaterResults as $machineCode => $machineAmount) { // if (!isset($results[$machineCode])) { // $results[$machineCode] = []; // } // if (!isset($results[$machineCode][$subject])) { // $results[$machineCode][$subject] = $machineAmount; // } // } // } else { // // 其他科目按所有机台分摊 // $globalResults = $this->allocateGlobally($amount, $month); // foreach ($globalResults as $machineCode => $machineAmount) { // if (!isset($results[$machineCode])) { // $results[$machineCode] = []; // } // $results[$machineCode][$subject] = $machineAmount; // } // } // } // // return $results; // } // // /** // * 获取水电气数据 // */ // protected function getUtilityData($month) // { // return Db::name('成本_各月水电气') // ->where('Sys_ny', $month) // ->whereLike('费用类型', '%分摊%') // ->select(); // } // // /** // * 简化科目名称 // */ // protected function simplifySubjectName($subjectName) // { // $simplifiedMap = [ // '废气处理(RTO)' => self::SUBJECT_WASTE_GAS, // '锅炉' => self::SUBJECT_BOILER, // '热水锅炉' => self::SUBJECT_HOT_WATER_BOILER, // '空压机' => self::SUBJECT_AIR_COMPRESSOR, // '真空鼓风机' => self::SUBJECT_VACUUM_BLOWER, // '中央空调' => self::SUBJECT_CENTRAL_AIR_CONDITIONER, // '待分摊总额' => self::SUBJECT_TOTAL_TO_APPORTION, // ]; // // foreach ($simplifiedMap as $keyword => $simplified) { // if (strpos($subjectName, $keyword) !== false) { // return $simplified; // } // } // // return $subjectName; // } // // /** // * 计算金额 // */ // protected function calculateAmount($item) // { // $electricity = ($this->getSafeNumericValue($item['耗电量']) * $this->getSafeNumericValue($item['单位电价'])); // $gas = ($this->getSafeNumericValue($item['耗气量']) * $this->getSafeNumericValue($item['单位气价'])); // // return round($electricity + $gas, 2); // } // // /** // * 按楼层分摊 // */ // protected function allocateByFloor($totalAmount, $month) // { // $results = []; // // // 计算每个楼层的总机时 // $floorTotalHours = []; // foreach ([1, 2] as $floor) { // $machines = $this->getMachinesByFloor($floor, $month); // $totalHours = 0; // foreach ($machines as $machine) { // $totalHours += $machine['占用机时']; // } // $floorTotalHours[$floor] = $totalHours; // } // // $allFloorsTotal = array_sum($floorTotalHours); // if ($allFloorsTotal <= 0) { // return $results; // } // // // 按楼层比例分摊 // foreach ($floorTotalHours as $floor => $hours) { // $floorAmount = round($totalAmount * ($hours / $allFloorsTotal), 2); // // // 在楼层内按机台分摊 // $machines = $this->getMachinesByFloor($floor, $month); // $floorResults = $this->allocateWithinGroup($floorAmount, $machines); // // $results[$floor] = $floorResults; // } // // return $results; // } // // /** // * 全局分摊(不分楼层) // */ // protected function allocateGlobally($totalAmount, $month) // { // $machines = $this->getAllMachines($month); // return $this->allocateWithinGroup($totalAmount, $machines); // } // // /** // * 只分摊到卷凹机组 // */ // protected function allocateToRollCoaterOnly($totalAmount, $month) // { // $machines = $this->getRollCoaterMachines($month); // return $this->allocateWithinGroup($totalAmount, $machines); // } // // /** // * 在组内按机台运行时间分摊 // */ // protected function allocateWithinGroup($totalAmount, $machines) // { // $results = []; // // if (empty($machines)) { // return $results; // } // // $totalHours = 0; // $machineHours = []; // // foreach ($machines as $machine) { // $hours = floatval($machine['占用机时']); // $machineCode = $machine['机台编号']; // // if ($hours > 0) { // $totalHours += $hours; // if (!isset($machineHours[$machineCode])) { // $machineHours[$machineCode] = $hours; // } else { // $machineHours[$machineCode] += $hours; // } // } // } // // if ($totalHours <= 0) { // return $results; // } // // foreach ($machineHours as $machineCode => $hours) { // $results[$machineCode] = round($totalAmount * ($hours / $totalHours), 2); // } // // return $results; // } // // /** // * 格式化分摊结果 // */ // protected function formatApportionmentResults($results, $month, $sysId) // { // $formatted = []; // $now = date('Y-m-d H:i:s'); // // foreach ($results as $machineCode => $subjects) { // foreach ($subjects as $subject => $amount) { // $formatted[] = [ // 'Sys_ny' => $month, // '科目名称' => $subject, // '设备编号' => $machineCode, // '分摊系数' => 1, // '分摊金额' => $amount, // 'Sys_id' => $sysId, // 'Sys_rq' => $now, // ]; // } // } // // return $formatted; // } // // /** // * 保存分摊系数 // */ // protected function saveApportionmentCoefficients($data, $month) // { // // 删除旧数据 // Db::name('成本_各月分摊系数')->where('Sys_ny', $month)->delete(); // // if (!empty($data)) { // $sql = Db::name('成本_各月分摊系数')->fetchSql(true)->insertAll($data); // \db()->query($sql); // } // } // // /** // * 分摊到工单(二次分配) // */ // protected function allocateToWorkOrders($month) // { // // 获取所有机台的分摊系数(每机时费用) // $coefficients = $this->getMachineCoefficients($month); // // // 获取所有机台的工单 // $workOrders = $this->getWorkOrdersByMonth($month); // // $updates = []; // foreach ($workOrders as $order) { // $machineCode = $order['机台编号']; // // if (!isset($coefficients[$machineCode])) { // continue; // } // // $machineCoeffs = $coefficients[$machineCode]; // $hours = floatval($order['占用机时']); // // if ($hours <= 0) { // continue; // } // // // 计算各科目分摊金额 // $updateData = [ // '直接水电' => round($hours * 0.69, 2), // 直接水电费 // ]; // // foreach ($machineCoeffs as $subject => $rate) { // $subjectKey = ($subject === '待分摊总额') ? '分摊水电' : $subject; // $updateData[$subjectKey] = round($hours * $rate, 2); // } // // // 构建更新条件 // $where = [ // 'sys_ny' => $month, // 'sczl_gdbh' => $order['工单编号'], // 'sczl_yjno' => $order['印件号'], // 'sczl_gxh' => $order['工序号'], // 'sczl_jtbh' => $machineCode, // ]; // // $updates[] = [ // 'where' => $where, // 'data' => $updateData // ]; // } // // // 批量更新 // $this->batchUpdateWorkOrders($updates, $month); // } // // /** // * 获取机台分摊系数(每机时费用) // */ // protected function getMachineCoefficients($month) // { // // 查询分摊系数和机台运行时间 // $data = Db::name('成本_各月分摊系数') // ->alias('c') // ->join('成本v23_月度成本明细 d', 'd.sys_ny = c.Sys_ny AND d.sczl_jtbh = c.设备编号') // ->where('c.Sys_ny', $month) // ->field([ // 'c.设备编号', // 'c.科目名称', // 'c.分摊金额', // 'SUM(d.占用机时)' => 'total_hours' // ]) // ->group('c.设备编号, c.科目名称') // ->select(); // // $coefficients = []; // // foreach ($data as $item) { // $machineCode = $item['设备编号']; // $subject = $item['科目名称']; // $amount = floatval($item['分摊金额']); // $hours = floatval($item['total_hours']); // // if ($hours > 0) { // $rate = round($amount / $hours, 4); // // if (!isset($coefficients[$machineCode])) { // $coefficients[$machineCode] = []; // } // // $coefficients[$machineCode][$subject] = $rate; // } // } // // return $coefficients; // } // // /** // * 获取所有工单 // */ // protected function getWorkOrdersByMonth($month) // { // return Db::name('成本v23_月度成本明细') // ->where('sys_ny', $month) // ->field([ // 'sczl_gdbh' => '工单编号', // 'sczl_yjno' => '印件号', // 'sczl_gxh' => '工序号', // 'sczl_jtbh' => '机台编号', // '占用机时' // ]) // ->select(); // } // // /** // * 批量更新工单数据 // */ // protected function batchUpdateWorkOrders($updates, $month) // { // $db = Db::name('成本v23_月度成本明细'); // // foreach ($updates as $update) { // $sql = $db->where($update['where'])->fetchSql(true)->update($update['data']); // $db->query($sql); // } // } // // /** // * 安全数值获取 // */ // protected function getSafeNumericValue($value) // { // if ($value === null || $value === '' || $value === false) { // return 0; // } // return floatval($value); // } // app/controller/UnifiedCostController.php /** * 执行成本计算 * @ApiMethod POST * @param string month 年月 * @param string sys_id 系统ID */ public function calculate() { if (Request::instance()->isPost() == false) { $this->error('非法请求'); } $params = Request::instance()->param(); if (!isset($params['month']) || empty($params['month'])) { $this->error('月份参数错误'); } $month = trim($params['month']); $sysId = $params['sys_id'] ?? ''; // 检查是否有正在执行的任务 $runningTask = Db::name('queue_tasks') ->where('task_type', 'cost_calculation') ->where('task_data', 'like', '%"month":"' . $month . '"%') ->where('status', 'in', ['pending', 'processing']) ->find(); if ($runningTask) { $this->success('该月份的成本计算任务已在执行中,请勿重复提交'); } // 检查工资计算是否在执行中(可选) $salaryRunning = Db::name('queue_tasks') ->where('task_type', 'salary_calculation') ->where('task_data', 'like', '%"date":"' . $month . '"%') ->where('status', 'in', ['pending', 'processing']) ->find(); if ($salaryRunning) { $this->error('该月份的工资计算正在执行中,建议等待工资计算完成后再进行成本计算'); } // 准备任务数据 $taskData = [ 'month' => $month, 'sys_id' => $sysId, 'user_id' => session('user_id') ?? 0, 'user_name' => session('user_name') ?? '系统', 'request_time' => date('Y-m-d H:i:s') ]; // 先创建任务记录 $taskId = Db::name('queue_tasks')->insertGetId([ 'task_type' => 'cost_calculation', 'task_data' => json_encode($taskData, JSON_UNESCAPED_UNICODE), 'status' => 'pending', 'queue_name' => 'cost_calculation', 'create_time' => date('Y-m-d H:i:s') ]); // 添加到任务数据中 $taskData['task_id'] = $taskId; // 提交到成本计算队列 $jobHandlerClassName = 'app\job\CostCalculationJob'; $queueName = 'cost_calculation'; $jobId = Queue::push($jobHandlerClassName, $taskData, $queueName); if ($jobId) { // 更新任务记录 Db::name('queue_tasks') ->where('id', $taskId) ->update([ 'job_id' => $jobId, 'update_time' => date('Y-m-d H:i:s') ]); $this->success('成本计算任务已提交到队列,请稍后查看结果', null, [ 'task_id' => $taskId, 'job_id' => $jobId, 'month' => $month, 'queue_name' => $queueName ]); } else { // 更新任务状态为失败 Db::name('queue_tasks') ->where('id', $taskId) ->update([ 'status' => 'failed', 'error' => '任务提交到队列失败', 'update_time' => date('Y-m-d H:i:s') ]); $this->error('成本计算任务提交失败'); } } /** * 查询成本计算状态 * @ApiMethod GET * @param string month 年月 */ public function status() { $month = Request::instance()->param('month'); if (empty($month)) { $this->error('月份参数错误'); } // 查询任务记录 $task = Db::name('queue_tasks') ->where('task_type', 'cost_calculation') ->where('task_data', 'like', '%"month":"' . $month . '"%') ->order('id', 'desc') ->find(); if ($task) { $result = json_decode($task['result'] ?? '{}', true); $taskData = json_decode($task['task_data'] ?? '{}', true); $response = [ 'exists' => true, 'task_id' => $task['id'], 'month' => $month, 'status' => $task['status'], 'queue_name' => $task['queue_name'], 'job_id' => $task['job_id'], 'start_time' => $task['start_time'], 'end_time' => $task['end_time'], 'retry_count' => $task['retry_count'], 'result' => $result, 'error' => $task['error'] ?? '', 'create_time' => $task['create_time'], 'user_info' => [ 'user_id' => $taskData['user_id'] ?? 0, 'user_name' => $taskData['user_name'] ?? '' ] ]; } else { $response = ['exists' => false, 'month' => $month]; } $this->success('查询成功', null, $response); } /** * 获取成本计算任务列表 * @ApiMethod GET */ public function list() { $page = Request::instance()->param('page', 1); $limit = Request::instance()->param('limit', 20); $month = Request::instance()->param('month'); $status = Request::instance()->param('status'); $query = Db::name('queue_tasks') ->where('task_type', 'cost_calculation'); if ($month) { $query->where('task_data', 'like', '%"month":"' . $month . '"%'); } if ($status) { $query->where('status', $status); } $total = $query->count(); $list = $query->order('id', 'desc') ->page($page, $limit) ->select(); // 解析任务数据 foreach ($list as &$item) { $taskData = json_decode($item['task_data'] ?? '{}', true); $item['month'] = $taskData['month'] ?? ''; $item['user_name'] = $taskData['user_name'] ?? ''; $item['request_time'] = $taskData['request_time'] ?? ''; if (!empty($item['result'])) { $item['result_data'] = json_decode($item['result'], true); } } $this->success('查询成功', null, [ 'list' => $list, 'total' => $total, 'page' => $page, 'pages' => ceil($total / $limit) ]); } /** * 手动重试失败的任务 * @ApiMethod POST * @param int task_id 任务ID */ public function retry() { $taskId = Request::instance()->param('task_id'); if (empty($taskId)) { $this->error('任务ID不能为空'); } $task = Db::name('queue_tasks') ->where('id', $taskId) ->where('task_type', 'cost_calculation') ->where('status', 'failed') ->find(); if (!$task) { $this->error('任务不存在或无法重试'); } // 解析原任务数据 $taskData = json_decode($task['task_data'], true); $taskData['task_id'] = $taskId; $taskData['retry_time'] = date('Y-m-d H:i:s'); // 更新原任务状态 Db::name('queue_tasks') ->where('id', $taskId) ->update([ 'status' => 'retrying', 'update_time' => date('Y-m-d H:i:s') ]); // 提交到队列 $jobHandlerClassName = 'app\job\CostCalculationJob'; $queueName = 'cost_calculation'; $jobId = Queue::push($jobHandlerClassName, $taskData, $queueName); if ($jobId) { $this->success('任务已重新提交到队列', null, [ 'task_id' => $taskId, 'new_job_id' => $jobId, 'queue_name' => $queueName ]); } else { $this->error('任务重试失败'); } } }