允许流转到的目标状态 * issued/reject 为终态 * @var array */ protected $statusFlow = [ 'wait' => ['issued', 'reject'], ]; public function _initialize() { parent::_initialize(); $this->model = new \app\admin\model\Invoice; $this->view->assign("typeList", $this->model->getTypeList()); $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(['user', 'merchant']) ->where($where) ->order($sort, $order) ->paginate($limit); $result = ['total' => $list->total(), 'rows' => $list->items()]; return json($result); } /** * 财务开票处理:申请信息只读,仅允许填写开票结果 * - 开票完成:必须填写发票号,状态置为已开票 * - 驳回:必须填写原因,额度自动释放 */ public function edit($ids = null) { $row = $this->model->get($ids); if (!$row) { $this->error(__('No Results were found')); } if (false === $this->request->isPost()) { $this->view->assign('row', $row); $this->view->assign('flowOptions', $this->statusFlow[$row['status']] ?? []); return $this->view->fetch(); } $status = (string)$this->request->post('row.status'); $invoiceNo = trim((string)$this->request->post('row.invoice_no')); $auditMsg = trim((string)$this->request->post('row.audit_msg')); // 状态机校验:终态禁止操作,流转目标必须合法 if (!isset($this->statusFlow[$row['status']])) { $this->error('该开票申请已处理完毕,不允许再次变更'); } if (!in_array($status, $this->statusFlow[$row['status']], true)) { $this->error('状态流转不合法,请刷新后重试'); } if ($status === 'issued' && $invoiceNo === '') { $this->error('标记开票完成必须填写发票号码'); } if ($status === 'reject' && $auditMsg === '') { $this->error('驳回时必须填写驳回原因'); } Db::startTrans(); try { // 乐观锁更新状态,防止并发重复处理 $affected = Db::name('invoice') ->where('id', $row['id']) ->where('status', $row['status']) ->update([ 'status' => $status, 'invoice_no' => $invoiceNo !== '' ? $invoiceNo : $row['invoice_no'], 'audit_msg' => $auditMsg, 'updatetime' => time(), ]); if (!$affected) { throw new \Exception('开票申请状态已被其他人变更,请刷新后重试'); } // 通知申请人(商户开票通知绑定账号用户) $this->sendUserMessage( (int)$row['user_id'], $status === 'issued' ? '发票已开具' : '开票申请已驳回', $status === 'issued' ? '您申请的开票 ¥' . $row['amount'] . ' 已开具完成,发票号码:' . $invoiceNo . ',电子发票已发送至 ' . $row['email'] : '您的开票申请 ¥' . $row['amount'] . ' 未通过审核:' . ($auditMsg !== '' ? $auditMsg : '无') ); 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('开票处理不支持批量操作,请逐单填写发票号后处理'); } /** * 向申请人发送站内消息 * @param int $userId 用户ID * @param string $title 消息标题 * @param string $content 消息内容 */ protected function sendUserMessage($userId, $title, $content) { if (!$userId) { return; } Db::name('message')->insert([ 'receive_type' => 'user', 'receive_id' => $userId, 'type' => 'system', 'title' => mb_substr((string)$title, 0, 100), 'content' => mb_substr((string)$content, 0, 500), 'order_id' => 0, 'is_read' => 0, 'createtime' => time(), ]); } }