model = new \app\admin\model\Settle; $this->view->assign("statusList", $this->model->getStatusList()); } /** * 结算单列表 */ public function index() { $this->request->filter(['strip_tags', 'trim']); if (false === $this->request->isAjax()) { return $this->view->fetch(); } if ($this->request->request('keyField')) { return $this->selectpage(); } [$where, $sort, $order, $offset, $limit] = $this->buildparams(); $list = $this->model ->with(['order', 'master']) ->where($where) ->order($sort, $order) ->paginate($limit); $result = ['total' => $list->total(), 'rows' => $list->items()]; return json($result); } /** * 结算对账详情(关联订单费用明细、师傅钱包快照与入账流水) */ public function detail($ids = null) { $row = $this->model->get($ids); if (!$row) { $this->error(__('No Results were found')); } $this->assignSettleContext($row); return $this->view->fetch(); } /** * 待结算单财务处理(严禁篡改结算金额,仅允许对 wait 状态执行线上入账或线下归档) */ public function edit($ids = null) { $row = $this->model->get($ids); if (!$row) { $this->error(__('No Results were found')); } if (false === $this->request->isPost()) { $this->assignSettleContext($row); return $this->view->fetch(); } if ($row['status'] !== 'wait') { $this->error('该结算单已归档完结,禁止重复结算或修改账务数据'); } $targetStatus = (string)$this->request->post('row.status', ''); if (!in_array($targetStatus, ['settled', 'offline'], true)) { $this->error('请选择有效的结算归档方式(线上划拨入账 / 线下特殊结算)'); } $masterId = (int)$row['master_id']; $orderId = (int)$row['order_id']; $settleAmount = round((float)$row['settle_amount'], 2); $now = time(); Db::startTrans(); try { // 乐观锁防并发重复点击结算 $affected = Db::name('settle') ->where('id', (int)$row['id']) ->where('status', 'wait') ->update([ 'status' => $targetStatus, 'settle_time' => $now, ]); if (!$affected) { throw new \Exception('结算单状态已变更,请刷新列表后重试'); } $master = Db::name('master')->where('id', $masterId)->lock(true)->find(); if (!$master) { throw new \Exception('关联结算师傅不存在'); } $orderNo = $orderId > 0 ? (string)Db::name('order')->where('id', $orderId)->value('order_no') : ''; if ($targetStatus === 'settled') { // 幂等校验:防止该订单此前已产生过 settle 流水 $existsLog = $orderId > 0 && Db::name('wallet_log') ->where('user_type', 'master') ->where('user_id', $masterId) ->where('order_id', $orderId) ->where('type', 'settle') ->find(); if ($existsLog) { throw new \Exception('检测到该订单已存在线上结算入账流水,请核对后再操作'); } $newMoney = round((float)$master['money'] + $settleAmount, 2); Db::name('master')->where('id', $masterId)->update([ 'money' => $newMoney, 'updatetime' => $now, ]); Db::name('wallet_log')->insert([ 'user_type' => 'master', 'user_id' => $masterId, 'type' => 'settle', 'amount' => $settleAmount, 'balance' => $newMoney, 'order_id' => $orderId, 'remark' => '后台财务确认结算入账' . ($orderNo !== '' ? '(订单 ' . $orderNo . ')' : ''), 'createtime' => $now, ]); $msgText = ($orderNo !== '' ? '订单 ' . $orderNo . ' ' : '') . '服务佣金 ¥' . number_format($settleAmount, 2, '.', '') . ' 已结算至您的平台钱包余额。'; } else { $msgText = ($orderNo !== '' ? '订单 ' . $orderNo . ' ' : '') . '服务佣金 ¥' . number_format($settleAmount, 2, '.', '') . ' 已由财务完成线下特殊结算归档。'; } if ($orderId > 0) { Db::name('order')->where('id', $orderId)->update([ 'settle_status' => 1, 'settle_amount' => $settleAmount, 'updatetime' => $now, ]); } Db::name('message')->insert([ 'receive_type' => 'master', 'receive_id' => $masterId, 'type' => 'order', 'title' => '服务佣金结算通知', 'content' => mb_substr($msgText, 0, 500), 'order_id' => $orderId, 'is_read' => 0, 'createtime' => $now, ]); Db::commit(); } catch (PDOException|\Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $this->success(); } /** * 结算单由订单核销系统生成,禁止手动添加 */ public function add() { $this->error('结算单由完工核销自动生成,后台不可手工录入'); } /** * 财务结算凭证禁止删除 */ public function del($ids = null) { $this->error('财务结算单据禁止删除'); } /** * 禁止跳过钱包流水的批量修改状态 */ public function multi($ids = null) { $this->error('结算单涉及资金划拨,不支持批量修改状态,请逐单核对处理'); } /** * 组装结算单对账上下文 */ protected function assignSettleContext($row) { $order = (int)$row['order_id'] > 0 ? Db::name('order')->where('id', (int)$row['order_id'])->find() : null; $master = (int)$row['master_id'] > 0 ? Db::name('master')->where('id', (int)$row['master_id'])->find() : null; $walletLogs = ((int)$row['master_id'] > 0 && (int)$row['order_id'] > 0) ? Db::name('wallet_log') ->where('user_type', 'master') ->where('user_id', (int)$row['master_id']) ->where('order_id', (int)$row['order_id']) ->order('id desc') ->select() : []; $this->view->assign([ 'row' => $row, 'order' => $order ?: [], 'master' => $master ?: [], 'walletLogs' => $walletLogs, ]); } }