model = new \app\admin\model\shopro\operating_company\Settlement; $this->companyModel = new CompanyModel; } public function index() { $this->request->filter(['strip_tags']); if ($this->request->isAjax()) { $nobuildfields = ['company_name', 'period_range']; list($where, $sort, $order, $offset, $limit) = $this->custombuildparams(null, $nobuildfields); $query = $this->buildSearch()->where($where); $total = $query->count(); $list = $this->buildSearch() ->with(['company']) ->where($where) ->order($sort, $order) ->limit($offset, $limit) ->select(); return $this->success('结算列表', null, ['total' => $total, 'rows' => $list]); } return $this->view->fetch(); } protected function buildSearch() { $filter = $this->request->get('filter', ''); $filter = (array) json_decode($filter, true); $query = $this->model; if (!empty($filter['company_name'])) { $query = $query->where('company_name', 'like', '%' . $filter['company_name'] . '%'); } if (!empty($filter['period_range'])) { $range = explode(' - ', $filter['period_range']); if (count($range) === 2) { $query = $query->where('period_start', '>=', $range[0]) ->where('period_end', '<=', $range[1]); } } return $query; } public function add() { if ($this->request->isPost()) { $params = $this->request->post('row/a'); $this->validateSettlement($params); Db::startTrans(); try { $company = $this->companyModel->get($params['company_id']); if (!$company) { throw new \Exception('运营公司不存在'); } $settlement = $this->model->create([ 'settlement_sn' => $this->generateSn(), 'company_id' => $company->id, 'company_name' => $company->name, 'amount' => $params['amount'], 'period_start' => $params['period_start'], 'period_end' => $params['period_end'], 'audit_status' => isset($params['submit']) ? 1 : 0, 'pay_status' => 0, 'admin_id' => $this->auth->id, 'source' => 'manual', ]); $this->saveItems($settlement->id, $params); SettlementLog::record($settlement->id, 'create', $this->auth->id, $settlement->toArray()); if (isset($params['submit'])) { SettlementLog::record($settlement->id, 'submit', $this->auth->id); } Db::commit(); } catch (\Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $this->success('添加成功'); } return $this->view->fetch(); } public function edit($ids = null) { $row = $this->model->get($ids, ['items']); if (!$row) { $this->error('记录不存在'); } if (!in_array($row->audit_status, [0, 3])) { $this->error('当前状态不可编辑'); } if ($this->request->isPost()) { $params = $this->request->post('row/a'); $this->validateSettlement($params); Db::startTrans(); try { $company = $this->companyModel->get($params['company_id']); if (!$company) { throw new \Exception('运营公司不存在'); } $row->save([ 'company_id' => $company->id, 'company_name' => $company->name, 'amount' => $params['amount'], 'period_start' => $params['period_start'], 'period_end' => $params['period_end'], 'audit_status' => isset($params['submit']) ? 1 : ($row->audit_status == 3 ? 0 : $row->audit_status), ]); SettlementItem::where('settlement_id', $row->id)->delete(); $this->saveItems($row->id, $params); SettlementLog::record($row->id, 'edit', $this->auth->id); if (isset($params['submit'])) { SettlementLog::record($row->id, 'submit', $this->auth->id); } Db::commit(); } catch (\Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $this->success('保存成功'); } $this->view->assign('row', $row); return $this->view->fetch(); } public function del($ids = null) { $row = $this->model->get($ids); if (!$row) { $this->error('记录不存在'); } if (!in_array($row->audit_status, [0, 3])) { $this->error('仅草稿或已驳回状态可删除'); } Db::startTrans(); try { SettlementItem::where('settlement_id', $row->id)->delete(); SettlementLog::where('settlement_id', $row->id)->delete(); $row->delete(); Db::commit(); } catch (\Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $this->success('删除成功'); } public function submit() { $id = $this->request->post('id'); $row = $this->model->get($id); if (!$row) { $this->error('记录不存在'); } if (!in_array($row->audit_status, [0, 3])) { $this->error('当前状态不可提交'); } $row->audit_status = 1; $row->save(); SettlementLog::record($row->id, 'submit', $this->auth->id); $this->success('已提交审核'); } public function audit() { $id = $this->request->post('id'); $action = $this->request->post('action'); $remark = $this->request->post('remark', ''); $row = $this->model->get($id); if (!$row) { $this->error('记录不存在'); } if ($row->audit_status != 1) { $this->error('当前状态不可审核'); } if ($action === 'approve') { $row->audit_status = 2; $row->audit_remark = $remark; $row->audit_admin_id = $this->auth->id; $row->save(); SettlementLog::record($row->id, 'approve', $this->auth->id, ['remark' => $remark]); $this->success('审核通过'); } elseif ($action === 'reject') { $row->audit_status = 3; $row->audit_remark = $remark; $row->audit_admin_id = $this->auth->id; $row->save(); SettlementLog::record($row->id, 'reject', $this->auth->id, ['remark' => $remark]); $this->success('已驳回'); } $this->error('无效操作'); } public function markPay() { $id = $this->request->post('id'); $payStatus = (int) $this->request->post('pay_status'); $remark = $this->request->post('remark', ''); $row = $this->model->get($id); if (!$row) { $this->error('记录不存在'); } if ($row->audit_status != 2) { $this->error('仅审核通过的结算单可标记打款'); } if (!in_array($payStatus, [1, 2])) { $this->error('无效的打款状态'); } $row->pay_status = $payStatus; $row->pay_remark = $remark; $row->pay_time = $payStatus == 1 ? time() : null; $row->save(); SettlementLog::record($row->id, $payStatus == 1 ? 'pay' : 'pay_fail', $this->auth->id, [ 'remark' => $remark, ]); $this->success('操作成功'); } public function import() { if (!$this->request->isPost()) { return $this->view->fetch(); } $file = $this->request->request('file'); if (!$file) { $this->error('请上传文件'); } $filePath = ROOT_PATH . 'public' . DS . $file; if (!is_file($filePath)) { $this->error('文件不存在'); } $ext = pathinfo($filePath, PATHINFO_EXTENSION); if (!in_array($ext, ['csv', 'xls', 'xlsx'])) { $this->error('不支持的文件格式'); } $rows = $this->readImportFile($filePath, $ext); if (count($rows) < 2) { $this->error('导入数据为空'); } $header = array_map('trim', $rows[0]); $map = $this->getImportColumnMap($header); $batch = 'IMP' . date('YmdHis'); $success = 0; $errors = []; Db::startTrans(); try { for ($i = 1; $i < count($rows); $i++) { $line = $rows[$i]; if (!$line || !array_filter($line)) { continue; } try { $data = $this->parseImportRow($line, $map); $company = $this->companyModel->where('name', $data['company_name'])->find(); if (!$company) { $company = $this->companyModel->create([ 'name' => $data['company_name'], 'status' => 'normal', ]); } $settlement = $this->model->create([ 'settlement_sn' => $this->generateSn(), 'company_id' => $company->id, 'company_name' => $company->name, 'amount' => $data['amount'], 'period_start' => $data['period_start'], 'period_end' => $data['period_end'], 'audit_status' => 1, 'pay_status' => 0, 'admin_id' => $this->auth->id, 'source' => 'import', 'import_batch' => $batch, ]); if (!empty($data['order_sn']) || !empty($data['remark'])) { SettlementItem::create([ 'settlement_id' => $settlement->id, 'order_sn' => $data['order_sn'] ?? '', 'item_name' => '分成导入', 'amount' => $data['amount'], 'remark' => $data['remark'] ?? '', ]); } SettlementLog::record($settlement->id, 'create', $this->auth->id, ['source' => 'import']); SettlementLog::record($settlement->id, 'submit', $this->auth->id); $success++; } catch (\Exception $e) { $errors[] = '第' . ($i + 1) . '行:' . $e->getMessage(); } } Db::commit(); } catch (\Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $msg = '成功导入 ' . $success . ' 条'; if ($errors) { $msg .= ',失败 ' . count($errors) . ' 条:' . implode(';', array_slice($errors, 0, 5)); } $this->success($msg); } public function export() { list($where, $sort, $order) = $this->custombuildparams(null, ['company_name', 'period_range']); $list = $this->buildSearch()->where($where)->order($sort, $order)->select(); $list = collection($list)->toArray(); $expCellName = [ 'settlement_sn' => '结算单号', 'company_name' => '结算对象', 'amount' => '金额', 'period_start' => '周期开始', 'period_end' => '周期结束', 'audit_status_text' => '审核状态', 'pay_status_text' => '打款状态', 'audit_remark' => '审核备注', 'pay_remark' => '打款备注', 'createtime' => '创建时间', ]; $newList = []; foreach ($list as $item) { $newList[] = [ 'settlement_sn' => $item['settlement_sn'], 'company_name' => $item['company_name'], 'amount' => $item['amount'], 'period_start' => $item['period_start'], 'period_end' => $item['period_end'], 'audit_status_text' => $this->model->getAuditStatusList()[$item['audit_status']] ?? '', 'pay_status_text' => $this->model->getPayStatusList()[$item['pay_status']] ?? '', 'audit_remark' => $item['audit_remark'], 'pay_remark' => $item['pay_remark'], 'createtime' => date('Y-m-d H:i:s', $item['createtime']), ]; } $export = new Export(); $export->exportExcel('运营公司结算-' . date('Y-m-d'), $expCellName, $newList); } public function downloadTemplate() { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $headers = ['运营公司名称', '结算金额', '周期开始', '周期结束', '订单号', '备注']; foreach ($headers as $col => $title) { $sheet->setCellValueByColumnAndRow($col + 1, 1, $title); } $sheet->setCellValueByColumnAndRow(1, 2, '示例运营公司'); $sheet->setCellValueByColumnAndRow(2, 2, '1000.00'); $sheet->setCellValueByColumnAndRow(3, 2, date('Y-m-01')); $sheet->setCellValueByColumnAndRow(4, 2, date('Y-m-d')); header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="运营公司结算导入模板.xlsx"'); header('Cache-Control: max-age=0'); $writer = new Xlsx($spreadsheet); $writer->save('php://output'); exit; } public function logs() { $id = $this->request->get('id'); $logs = SettlementLog::where('settlement_id', $id)->order('id desc')->select(); return $this->success('操作成功', null, $logs); } protected function generateSn() { $prefix = 'OCS' . date('Ymd'); $last = $this->model->where('settlement_sn', 'like', $prefix . '%') ->order('id desc')->value('settlement_sn'); $seq = $last ? ((int) substr($last, -6)) + 1 : 1; return $prefix . str_pad($seq, 6, '0', STR_PAD_LEFT); } protected function validateSettlement($params) { if (empty($params['company_id']) || empty($params['amount']) || empty($params['period_start']) || empty($params['period_end'])) { throw new \Exception('请填写完整结算信息'); } if ($params['period_start'] > $params['period_end']) { throw new \Exception('结算周期开始不能晚于结束'); } if ($params['amount'] <= 0) { throw new \Exception('结算金额必须大于0'); } } protected function saveItems($settlementId, $params) { if (empty($params['items']) || !is_array($params['items'])) { return; } foreach ($params['items'] as $item) { if (empty($item['amount'])) { continue; } SettlementItem::create([ 'settlement_id' => $settlementId, 'order_sn' => $item['order_sn'] ?? '', 'item_name' => $item['item_name'] ?? '', 'amount' => $item['amount'], 'remark' => $item['remark'] ?? '', ]); } } protected function getImportColumnMap($header) { $aliases = [ 'company_name' => ['运营公司名称', '结算对象'], 'amount' => ['结算金额', '金额'], 'period_start' => ['周期开始', '结算周期起'], 'period_end' => ['周期结束', '结算周期止'], 'order_sn' => ['订单号'], 'remark' => ['备注'], ]; $map = []; foreach ($header as $idx => $title) { foreach ($aliases as $key => $names) { if (in_array($title, $names)) { $map[$key] = $idx; } } } if (!isset($map['company_name'], $map['amount'], $map['period_start'], $map['period_end'])) { throw new \Exception('模板表头不正确'); } return $map; } protected function parseImportRow($line, $map) { $companyName = trim($line[$map['company_name']] ?? ''); $amount = trim($line[$map['amount']] ?? ''); $periodStart = trim($line[$map['period_start']] ?? ''); $periodEnd = trim($line[$map['period_end']] ?? ''); if (!$companyName || !$amount || !$periodStart || !$periodEnd) { throw new \Exception('必填项不能为空'); } if (!is_numeric($amount) || $amount <= 0) { throw new \Exception('结算金额无效'); } return [ 'company_name' => $companyName, 'amount' => $amount, 'period_start' => date('Y-m-d', strtotime($periodStart)), 'period_end' => date('Y-m-d', strtotime($periodEnd)), 'order_sn' => isset($map['order_sn']) ? trim($line[$map['order_sn']] ?? '') : '', 'remark' => isset($map['remark']) ? trim($line[$map['remark']] ?? '') : '', ]; } protected function readImportFile($filePath, $ext) { if ($ext === 'csv') { $rows = []; $handle = fopen($filePath, 'r'); while (($data = fgetcsv($handle)) !== false) { $rows[] = $data; } fclose($handle); return $rows; } $reader = $ext === 'xls' ? new \PhpOffice\PhpSpreadsheet\Reader\Xls() : new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); $spreadsheet = $reader->load($filePath); return $spreadsheet->getActiveSheet()->toArray(); } }