[ self::STATUS_PAY_INIT => '未打款', self::STATUS_PAY_SUCCESS => '打款成功', self::STATUS_PAY_FAIL => '打款失败', self::STATUS_PAY_INNER_PROCESS => '打款中', // 内部打款中 self::STATUS_PAY_OUTER_PROCESS => '打款中', // 外部打款中 self::STATUS_PAY_EXCEPTION => '打款异常', ], ]; /** * 打款若干天后才可以更新状态为成功或失败 * @var int */ public $daysAfterPayTime = 1; /** * @inheritdoc */ public static function tableName() { return 'crm_remit_order'; } /** * @return \yii\db\Connection the database connection used by this AR class. */ public static function getDb() { return Yii::$app->get('dbXcrm'); } /** * @inheritdoc */ public function rules() { return [ [['remit_no', 'batch_no', 'biz_no', 'pay_type', 'payee_name', 'amount', 'bank_name', 'bank_card_no', 'create_time'], 'required'], [['remit_no', 'batch_no', 'biz_type', 'biz_no', 'pay_type', 'is_business', 'audit_status', 'audit_admin_id', 'audit_time', 'reaudit_admin_id', 'reaudit_time', 'pay_status', 'pay_time', 'complete_time', 'out_no_crc32', 'create_time'], 'integer'], [['amount'], 'number', 'min' => 0.01], [['last_modified'], 'safe'], [['payee_name', 'bank_name', 'bank_branch', 'bank_card_no', 'bank_province', 'bank_city', 'bank_code', 'bank_branch_code', 'audit_name', 'reaudit_name', 'ret_code', 'res_code', 'sign'], 'string', 'max' => 32], [['audit_reason', 'reaudit_reason', 'ret_msg'], 'string', 'max' => 255], [['out_no'], 'string', 'max' => 64], [['remit_no'], 'unique'], [['memo'], 'string', 'max' => '255'], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'remit_no' => '打款单号', 'batch_no' => '打款批次号', 'biz_type' => '业务类型 1出金', 'biz_no' => '业务单号(出金单号)', 'pay_type' => '打款方式', 'payee_name' => '收款人名字', 'amount' => '打款金额', 'bank_name' => '银行名称', 'bank_branch' => '银行支行', 'bank_card_no' => '银行卡号', 'bank_province' => '开户行所在省', 'bank_city' => '开户行所在市', 'bank_code' => '银行代码', 'bank_branch_code' => '银行支行代码', 'is_business' => '是否对公 0对私 1对公', 'audit_status' => '审核状态 0未审核 1初审通过 2复审通过 3初审未通过 4复审未通过', 'audit_admin_id' => '初审人id', 'audit_name' => '初审人名字', 'audit_reason' => '初审不通过原因', 'audit_time' => '初审时间', 'reaudit_admin_id' => '复审人id', 'reaudit_name' => '复审人名字', 'reaudit_reason' => '复审不通过原因', 'reaudit_time' => '复审时间', 'pay_status' => '打款状态 0未打款 1打款成功 2打款失败 3内部打款中 4外部打款中 5打款异常', 'pay_time' => '打款时间', 'complete_time' => '完成时间', 'out_no' => '外部交易号', 'out_no_crc32' => '外部交易号CRC32', 'ret_code' => '返回状态码', 'res_code' => '交易状态码', 'ret_msg' => '返回信息', 'sign' => '数据签名', 'memo' => '备注', 'create_time' => '创建时间', 'last_modified' => '最后修改时间', ]; } /** * 生成打款单号 100开头 * @return string */ public static function generateRemitNo() { /** @var Connection $redis */ $redis = Yii::$app->get('redis'); $i = 0; while (true) { $i++; list($usec, $sec) = explode(" ", microtime()); $remitNo = '100' . date('ymdHis', $sec) . sprintf('%03d', $usec * 1000); if ($redis->set("aike_service:remit_no:{$remitNo}", "1", 'EX', '30', 'NX')) { return $remitNo; } if ($i > 3) { break; } usleep(1000); // sleep 1ms } return false; } /** * @param int $bizNo * @param int $bizType * @return bool */ public static function checkBizExist($bizNo, $bizType) { return static::find()->where(['biz_no' => $bizNo, 'biz_type' => $bizType])->andWhere(['<>', 'audit_status', self::STATUS_AUDIT_FAIL])->exists(); } /** * @param int $remitNo * @return array|null|\yii\db\ActiveRecord|RemitOrder */ public static function findByRemitNo($remitNo) { return static::find()->where(['remit_no' => $remitNo])->limit(1)->one(); } /** * 通过财务打款批次号获取打款实例 * @param $batchNo * @return array|null|\yii\db\ActiveRecord|RemitOrder */ public static function findByBatchNo($batchNo) { return static::find()->where(['batch_no' => $batchNo])->limit(1)->one(); } /** * 批次初审 * @param array $data * @return bool */ public function doBatchAudit($data = []) { $saveData = []; if (!isset($data['audit_status']) || !in_array($data['audit_status'], [self::STATUS_AUDIT_SUCCESS, self::STATUS_AUDIT_FAIL])) { $this->addError('audit_status', '审核状态非法'); return false; } $saveData['audit_status'] = $data['audit_status']; if (isset($data['audit_reason']) && trim($data['audit_status']) != '') { $saveData['audit_reason'] = trim($data['audit_reason']); } if (isset($data['audit_admin_id']) && trim($data['audit_admin_id']) != '') { $saveData['audit_admin_id'] = intval($data['audit_admin_id']); } if (isset($data['audit_name']) && trim($data['audit_name']) != '') { $saveData['audit_name'] = trim($data['audit_name']); } $ip = isset($data['audit_ip']) ? trim($data['audit_ip']) : Utils::getClientIp(); unset($data); $saveData['audit_time'] = time(); if ($this->audit_status == self::STATUS_AUDIT_INIT || $this->audit_status == self::STATUS_REAUDIT_FAIL) { // 未审核 或者 复审失败 $affectRows = static::updateAll($saveData, ['batch_no' => $this->batch_no, 'audit_status' => $this->audit_status]); if ($affectRows == 0) { $this->addError('audit_status', '审核状态更新失败'); return false; } else { $logData = []; isset($saveData['audit_admin_id']) && $logData['admin_id'] = $saveData['audit_admin_id']; isset($saveData['audit_name']) && $logData['admin_name'] = $saveData['audit_name']; if ($saveData['audit_status'] == self::STATUS_AUDIT_SUCCESS) { $logData['memo'] = "财务打款审核状态: 初审通过"; } else { $logData['memo'] = "财务打款审核状态: 初审未通过"; } $logData['admin_ip'] = $ip; BatchRemitLog::addLog($this->getAttributes(), $logData); return true; } } else { $this->addError('audit_status', '审核状态必须是未审核或者复审失败'); return false; } } /** * @param array $data * @return bool */ public function doBatchReAudit($data = []) { $saveData = []; if (!isset($data['audit_status']) || !in_array($data['audit_status'], [self::STATUS_REAUDIT_SUCCESS, self::STATUS_REAUDIT_FAIL])) { $this->addError('audit_status', '审核状态非法'); return false; } $saveData['audit_status'] = $data['audit_status']; if (isset($data['reaudit_reason']) && trim($data['reaudit_reason']) != '') { $saveData['reaudit_reason'] = trim($data['reaudit_reason']); } if (isset($data['reaudit_admin_id']) && trim($data['reaudit_admin_id']) != '') { $saveData['reaudit_admin_id'] = intval($data['reaudit_admin_id']); } if (isset($data['reaudit_name']) && trim($data['reaudit_name']) != '') { $saveData['reaudit_name'] = trim($data['reaudit_name']); } $ip = isset($data['reaudit_ip']) ? trim($data['reaudit_ip']) : Utils::getClientIp(); unset($data); $saveData['reaudit_time'] = time(); if ($this->audit_status == self::STATUS_AUDIT_SUCCESS) { // 初审成功 $affectRows = static::updateAll($saveData, ['batch_no' => $this->batch_no, 'audit_status' => $this->audit_status]); if ($affectRows == 0) { $this->addError('audit_status', '审核状态更新失败'); return false; } else { $logData = $singleLog = []; isset($saveData['reaudit_admin_id']) && $logData['admin_id'] = $saveData['reaudit_admin_id']; isset($saveData['reaudit_admin_id']) && $singleLog['admin_id'] = $saveData['reaudit_admin_id']; isset($saveData['reaudit_name']) && $logData['admin_name'] = $saveData['reaudit_name']; isset($saveData['reaudit_name']) && $singleLog['admin_name'] = $saveData['reaudit_name']; $logData['admin_ip'] = $ip; $singleLog['admin_ip'] = $ip; if ($saveData['audit_status'] == self::STATUS_REAUDIT_SUCCESS) { $logData['memo'] = "财务打款审核状态: 复审通过"; $singleLog['memo'] = "创建打款单"; // 单条记录日志 $allData = static::find()->select('remit_no')->where(['batch_no' => $this->batch_no])->asArray()->all(); foreach ($allData as $item) { RemitLog::addLog($item, $singleLog); } } else { $logData['memo'] = "财务打款审核状态: 复审未通过"; } // 批次记载日志 BatchRemitLog::addLog($this->getAttributes(), $logData); return true; } } else { $this->addError('audit_status', '审核状态必须是初审通过'); return false; } } /** * @param array $data * @return bool */ public function doAudit($data = []) { $saveData = []; if (!isset($data['audit_status']) || !in_array($data['audit_status'], [self::STATUS_AUDIT_SUCCESS, self::STATUS_AUDIT_FAIL])) { $this->addError('audit_status', '审核状态非法'); return false; } $saveData['audit_status'] = $data['audit_status']; if (isset($data['audit_reason']) && trim($data['audit_status']) != '') { $saveData['audit_reason'] = trim($data['audit_reason']); } if (isset($data['audit_admin_id']) && trim($data['audit_admin_id']) != '') { $saveData['audit_admin_id'] = intval($data['audit_admin_id']); } if (isset($data['audit_name']) && trim($data['audit_name']) != '') { $saveData['audit_name'] = trim($data['audit_name']); } $ip = isset($data['audit_ip']) ? trim($data['audit_ip']) : Utils::getClientIp(); unset($data); $saveData['audit_time'] = time(); if ($this->audit_status == self::STATUS_AUDIT_INIT || $this->audit_status == self::STATUS_REAUDIT_FAIL) { // 未审核 或者 复审失败 $affectRows = static::updateAll($saveData, ['id' => $this->id, 'remit_no' => $this->remit_no, 'audit_status' => $this->audit_status]); if ($affectRows == 0) { $this->addError('audit_status', '审核状态更新失败'); return false; } else { $logData = []; isset($saveData['audit_admin_id']) && $logData['admin_id'] = $saveData['audit_admin_id']; isset($saveData['audit_name']) && $logData['admin_name'] = $saveData['audit_name']; if ($saveData['audit_status'] == self::STATUS_AUDIT_SUCCESS) { $logData['memo'] = "打款单审核状态: 初审通过"; } else { $logData['memo'] = "打款单审核状态: 初审失败"; } $logData['admin_ip'] = $ip; RemitLog::addLog($this->getAttributes(), $logData); return true; } } else { $this->addError('audit_status', '审核状态必须是未审核或者复审失败'); return false; } } /** * @param array $data * @return bool */ public function doReAudit($data = []) { $saveData = []; if (!isset($data['audit_status']) || !in_array($data['audit_status'], [self::STATUS_REAUDIT_SUCCESS, self::STATUS_REAUDIT_FAIL])) { $this->addError('audit_status', '审核状态非法'); return false; } $saveData['audit_status'] = $data['audit_status']; if (isset($data['reaudit_reason']) && trim($data['reaudit_reason']) != '') { $saveData['reaudit_reason'] = trim($data['reaudit_reason']); } if (isset($data['reaudit_admin_id']) && trim($data['reaudit_admin_id']) != '') { $saveData['reaudit_admin_id'] = intval($data['reaudit_admin_id']); } if (isset($data['reaudit_name']) && trim($data['reaudit_name']) != '') { $saveData['reaudit_name'] = trim($data['reaudit_name']); } $ip = isset($data['reaudit_ip']) ? trim($data['reaudit_ip']) : Utils::getClientIp(); unset($data); $saveData['reaudit_time'] = time(); if ($this->audit_status == self::STATUS_AUDIT_SUCCESS) { // 初审成功 $affectRows = static::updateAll($saveData, ['id' => $this->id, 'remit_no' => $this->remit_no, 'audit_status' => $this->audit_status]); if ($affectRows == 0) { $this->addError('audit_status', '审核状态更新失败'); return false; } else { $logData = []; isset($saveData['reaudit_admin_id']) && $logData['admin_id'] = $saveData['reaudit_admin_id']; isset($saveData['reaudit_name']) && $logData['admin_name'] = $saveData['reaudit_name']; if ($saveData['audit_status'] == self::STATUS_REAUDIT_SUCCESS) { $logData['memo'] = "打款单审核状态: 复审通过"; } else { $logData['memo'] = "打款单审核状态: 复审失败"; } $logData['admin_ip'] = $ip; RemitLog::addLog($this->getAttributes(), $logData); return true; } } else { $this->addError('audit_status', '审核状态必须是初审通过'); return false; } } /** * 列表数据 * @param array $post * @return array */ public function getList($post) { $result = ['code' => 0, 'data' => [], 'message' => '']; $status = isset($post['status']) ? $post['status'] : 'all'; $batch_no = isset($post['batch_no']) ? $post['batch_no'] : ''; $sTime = isset($post['sTime']) ? $post['sTime'] : ''; // 创建时间 $eTime = isset($post['eTime']) ? $post['eTime'] : ''; $sTime2 = isset($post['sTime2']) ? $post['sTime2'] : ''; // 操作时间 $eTime2 = isset($post['eTime2']) ? $post['eTime2'] : ''; $order = isset($post['order']) ? strtolower($post['order']) : ''; $orderBy = isset($post['orderBy']) ? strtolower($post['orderBy']) : 'desc'; $start = isset($post['start']) ? (int) $post['start'] : 0; $length = isset($post['length']) ? (int) $post['length'] : 20; $draw = isset($post['draw']) ? $post['draw'] : 1; $where = ['and']; // 只有复审成功后的才显示在打款列表 $where[] = ['=', 'audit_status', self::STATUS_REAUDIT_SUCCESS]; // 根据打款状态筛选 if ($status == 'first_trial') { // 待打款明细 $where[] = ['=', 'pay_status', self::STATUS_PAY_INIT]; } elseif ($status == 're_trial') { // 打款中明细 $where[] = ['in', 'pay_status', [self::STATUS_PAY_INNER_PROCESS, self::STATUS_PAY_OUTER_PROCESS]]; } elseif ($status == 'passed') { // 打款成功明细 $where[] = ['=', 'pay_status', self::STATUS_PAY_SUCCESS]; } elseif ($status == 'fail') { // 打款不成功明细 $where[] = ['=', 'pay_status', self::STATUS_PAY_FAIL]; } // 打款批次号 if ($batch_no) { $where[] = ['=', 'batch_no', $batch_no]; } // 时间 if ($sTime) { $startTime = strtotime($sTime); if ($startTime) { $where[] = ['>=', 'create_time', $startTime]; } } if ($eTime) { $eTime = trim($eTime); if (preg_match('/^\d+\-\d+\-\d+$/', $eTime)) { $endTime = strtotime($eTime) + 86400; } else { $endTime = strtotime($eTime); } if ($endTime) { $where[] = ['<=', 'create_time', $endTime]; } } // 在打款成功明细、打款不成功明细页面,可按完成时间搜索 if (in_array($status, ['passed', 'fail'])) { if ($sTime2) { $startTime2 = strtotime($sTime2); if ($startTime2) { $where[] = ['>=', 'complete_time', $startTime2]; } } if ($eTime2) { $eTime2 = trim($eTime2); if (preg_match('/^\d+\-\d+\-\d+$/', $eTime2)) { $endTime2 = strtotime($eTime2) + 86400; } else { $endTime2 = strtotime($eTime2); } if ($endTime2) { $where[] = ['<=', 'complete_time', $endTime2]; } } } // 排序 $allowOrderColumn = ['id', 'batch_no', 'pay_count', 'amount_sum', 'create_time', 'pay_status', 'pay_time']; if (in_array($order, $allowOrderColumn) && in_array($orderBy, ['asc', 'desc'])) { if ($orderBy == 'asc') { $orderCondition = [$order => SORT_ASC]; } else { $orderCondition = [$order => SORT_DESC]; } } else { $orderCondition = ['id' => SORT_DESC]; } // 字段 $select = ['remit_no', 'batch_no', 'COUNT(*) AS pay_count', 'SUM(amount) AS amount_sum', 'create_time', 'pay_status', 'pay_time', 'complete_time', 'ret_msg']; $query = static::find(); $query->select($select) ->where($where) ->groupBy('batch_no'); $count = $query->count(); if ($count) { $query->offset($start)->limit($length); $list = $query->orderBy($orderCondition)->asArray()->all(); // 操作人员的名字到日志表读取,因为用了GROUP BY,所以未使用聚合函数的字段要在子查询里排序 $remitNoArr = array_column($list, 'remit_no'); $remitNoStr = ''; foreach ($remitNoArr as $k => $v) { $remitNoStr .= '"' . $v . '",'; } $remitNoStr = rtrim($remitNoStr, ','); $sql = "SELECT remit_no, admin_name FROM (SELECT * FROM " . RemitLog::tableName() . " WHERE remit_no IN ({$remitNoStr}) ORDER BY id DESC) AS remit_log GROUP BY remit_no "; $logList = $this->getDb()->createCommand($sql)->queryAll(); foreach ($list as $k => $v) { $list[$k]['amount_sum'] = Utils::formatFloatOrInt($v['amount_sum']); $list[$k]['pay_status'] = isset(self::$statusTextMap['pay_status'][$v['pay_status']]) ? self::$statusTextMap['pay_status'][$v['pay_status']] : $v['pay_status']; $list[$k]['create_time'] = date('Y-m-d H:i:s', $v['create_time']); // 打款成功或不成功用完成时间,其他用支付时间 if (in_array($v['pay_status'], [self::STATUS_PAY_SUCCESS, self::STATUS_PAY_FAIL])) { $list[$k]['pay_time'] = $v['complete_time'] ? date('Y-m-d H:i:s', $v['complete_time']) : ($v['pay_time'] ? date('Y-m-d H:i:s', $v['pay_time']) : ''); } else { $list[$k]['pay_time'] = $v['pay_time'] ? date('Y-m-d H:i:s', $v['pay_time']) : ''; } unset($list[$k]['complete_time']); $list[$k]['admin_name'] = ''; foreach ($logList as $k2 => $v2) { if ($v['remit_no'] == $v2['remit_no']) { $list[$k]['admin_name'] = $v2['admin_name']; break; } } } // 计算笔数和总额,放到最后一行 $query = static::find(); $sum = $query->select(['COUNT(*) AS pay_count', 'SUM(amount) AS amount_sum'])->where($where)->asArray()->limit(1)->one(); $lastRow = [ 'batch_no' => '', 'pay_count' => $sum['pay_count'], 'amount_sum' => $sum['amount_sum'], 'create_time' => '', 'pay_status' => '', 'pay_time' => '', 'ret_msg' => '', 'admin_name' => '', ]; array_push($list, $lastRow); } else { // 没有数据则输出默认值 $list = [ [ 'batch_no' => '', 'pay_count' => 0, 'amount_sum' => 0, 'create_time' => '', 'pay_status' => '', 'pay_time' => '', 'ret_msg' => '', 'admin_name' => '', ], ]; } $data['data'] = $list; $data['draw'] = $draw; $data['recordsFiltered'] = $count; $data['recordsTotal'] = $count; $result['data'] = $data; $result['code'] = 1; return $result; } /** * 更改打款状态 * @param array $post * @return array */ public function updatePayStatus($post) { $result = ['code' => 0, 'data' => [], 'message' => '']; $batch_no = isset($post['batch_no']) ? $post['batch_no'] : ''; $status = isset($post['status']) ? $post['status'] : ''; $reason = isset($post['reason']) ? trim($post['reason']) : ''; $daysAfterPayTime = isset($post['daysAfterPayTime']) && intval($post['daysAfterPayTime']) ? intval($post['daysAfterPayTime']) : $this->daysAfterPayTime; $admin_id = isset($post['admin_id']) ? $post['admin_id'] : 0; $admin_name = isset($post['admin_name']) ? $post['admin_name'] : ''; $admin_ip = isset($post['admin_ip']) ? $post['admin_ip'] : ''; // 检查参数 if (!$batch_no || !in_array($status, ['success', 'fail']) || !$admin_id || !$admin_name) { $result['message'] = '参数错误'; return $result; } // 检查数据 $remitOrderList = static::find()->where(['batch_no' => $batch_no, 'pay_status' => [self::STATUS_PAY_INNER_PROCESS, self::STATUS_PAY_OUTER_PROCESS]])->asArray()->all(); if (!$remitOrderList) { $result['message'] = '没有对应的打款批次号'; return $result; } // 组装数据 $pay_status = $status == 'success' ? self::STATUS_PAY_SUCCESS : self::STATUS_PAY_FAIL; $time = time(); $saveData = [ 'pay_status' => $pay_status, 'complete_time' => $time, ]; if ($status == 'fail') { $saveData['ret_msg'] = $reason; } $todayBeginTime = strtotime(date('Y-m-d')); $affectRows = 0; $withinDaysCount = 0; $transaction = $this->getDb()->beginTransaction(); try { foreach ($remitOrderList as $k => $v) { // 打款若干天后才可以更新状态为成功或失败 $payTime = strtotime(date('Y-m-d', $v['pay_time'])); if (($todayBeginTime - $payTime) / 86400 < $daysAfterPayTime) { $withinDaysCount++; continue; } $updateResult = static::updateAll($saveData, ['id' => $v['id']]); if ($updateResult) { $affectRows++; // 记录日志 $logData = [ 'remit_no' => $v['remit_no'], 'memo' => '打款状态: ' . self::$statusTextMap['pay_status'][$v['pay_status']] . ' -> ' . self::$statusTextMap['pay_status'][$pay_status], 'admin_id' => $admin_id, 'admin_name' => $admin_name, 'admin_ip' => $admin_ip, 'create_time' => $time, ]; $remitLog = new RemitLog(); $remitLog->attributes = $logData; $remitLog->save(); } } $logData = [ 'batch_no' => $batch_no, 'memo' => '打款状态: ' . self::$statusTextMap['pay_status'][$v['pay_status']] . ' -> ' . self::$statusTextMap['pay_status'][$pay_status], 'admin_id' => $admin_id, 'admin_name' => $admin_name, 'admin_ip' => $admin_ip, 'create_time' => $time, ]; $batchRemitLog = new BatchRemitLog(); $batchRemitLog->attributes = $logData; $batchRemitLog->save(); $transaction->commit(); } catch (\Exception $e) { $transaction->rollBack(); } $result['code'] = $affectRows ? 1 : 0; $result['message'] = '更新' . $affectRows . '笔打款单'; return $result; } }