// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\common\model; use cores\BaseModel; use cores\exception\BaseException; use think\facade\Cache; use think\model\relation\BelongsTo; use app\common\library\helper; use app\common\model\PaymentTemplate as PaymentTemplateModel; use app\common\enum\Client as ClientEnum; use app\common\enum\payment\Method as PaymentMethodEnum; /** * 模型类:支付方式记录 * Class Payment * @package app\common\model */ class Payment extends BaseModel { // 定义表名 protected $name = 'payment'; // 定义主键 protected $pk = 'payment_id'; /** * 关联支付模板记录表 * @return BelongsTo */ public function template(): BelongsTo { return $this->belongsTo('PaymentTemplate'); } /** * 获取器:其他选项 * @param $value * @return array */ public function getOthersAttr($value): array { return helper::jsonDecode($value); } /** * 修改器:其他选项 * @param $value * @return string */ public function setOthersAttr($value): string { return helper::jsonEncode($value); } /** * 支付方式详情 * @param int $paymentId * @return static|array|null */ public static function detail(int $paymentId) { return self::get($paymentId); } /** * 获取支付方式配置 * @throws \think\db\exception\ModelNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\DataNotFoundException */ public static function getAll(int $storeId, int $merchantId = null): array { // 实例化当前模型 $model = new static; // 默认的支付方式数据 $defaultData = $model->defaultData(); if (empty($merchantId)) { $merchantId = 0; } if (!$data = Cache::get("payment_{$storeId}_{$merchantId}")) { // 获取所有支付方式 $data = $model->dataByStorage($storeId, $defaultData, $merchantId); // 写入缓存中 Cache::tag('cache')->set("payment_{$storeId}_{$merchantId}", $data); } // 重组缓存数据 (多维) return static::reorganize2($defaultData, $data); } /** * 获取指定客户端的支付方式 * @param string $client * @param int $storeId * @return array * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public static function getItem(string $client, int $storeId, int $merchantId = null): array { return static::getAll($storeId, $merchantId)[$client]; } /** * 重组缓存数据 (多维) * @param array $defaultData * @param array $data * @return array */ private static function reorganize2(array $defaultData, array $data): array { $data = static::reorganize($defaultData, $data, $type = 'cache'); foreach ($data as &$item) { $item['methods'] = array_values($item['methods']); } unset($item); return $data; } /** * 获取所有支付方式 (从数据库中) * @param int $storeId * @param array $defaultData * @return array * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ private function dataByStorage(int $storeId, array $defaultData, int $merchantId = 0): array { // 获取数据库中所有的支付方式 $where = []; if (isset($merchantId) && $merchantId) { $where['merchant_id'] = $merchantId; } $list = $this->where($where)->where('store_id', '=', $storeId)->select(); if ($list->isEmpty()) { return []; } // 客户端标识合集 $clientKeys = helper::getArrayColumn($defaultData, 'client'); // 整理数据格式 $data = []; $listArr = $list->toArray(); foreach ($clientKeys as $client) { $listAsClient = $this->listAsClient($listArr, $client); $methods = empty($listAsClient) ? [] : $this->buildMethods($client, $listAsClient); if (!empty($methods)) { $data[$client] = ['client' => $client, 'methods' => $this->buildMethods($client, $listAsClient)]; } } return $data; } /** * 格式化支付方式数据 (赋值key用于合并默认数据) * @param string $client * @param array $listAsClient * @return array */ private function buildMethods(string $client, array $listAsClient): array { $methods = helper::getArrayColumns($listAsClient, [ 'method', 'client', 'is_must_template', 'template_id', 'is_enable', 'is_default', 'others' ]); $data = []; foreach ($methods as $item) { if ($item['client'] === $client) { $data["$client-{$item['method']}"] = $item; } } return $data; } /** * 默认的支付方式数据 * @return array[] */ protected function defaultData(): array { $data = [ ClientEnum::MP_WEIXIN => $this->defaultGroup(ClientEnum::MP_WEIXIN, [ PaymentMethodEnum::WECHAT, PaymentMethodEnum::BALANCE, ]), ClientEnum::H5 => $this->defaultGroup(ClientEnum::H5, [ PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY, PaymentMethodEnum::BALANCE, ]), ClientEnum::WXOFFICIAL => $this->defaultGroup(ClientEnum::WXOFFICIAL, [ PaymentMethodEnum::WECHAT, PaymentMethodEnum::BALANCE, ]), ClientEnum::APP => $this->defaultGroup(ClientEnum::APP, [ PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY, PaymentMethodEnum::BALANCE, ]), ]; if (\config('plugin.mpAlipay.enable', false)) { $data[ClientEnum::MP_ALIPAY] = $this->defaultGroup(ClientEnum::MP_ALIPAY, [ PaymentMethodEnum::ALIPAY, PaymentMethodEnum::BALANCE, ]); } return $data; } /** * 默认的支付客户端分组 * @param string $client * @param array $designated * @return array */ private function defaultGroup(string $client, array $designated): array { return [ 'client' => $client, 'name' => ClientEnum::getName($client), 'desc' => '在' . ClientEnum::getName($client) . '付款时使用', 'methods' => $this->defaultMethods($client, $designated) ]; } /** * 默认的methods数据 * @param string $client * @param array $designated * @return array */ private function defaultMethods(string $client, array $designated): array { $record = [ 'key' => 1, 'method' => PaymentMethodEnum::WECHAT, // 支付方式 'is_must_template' => true, // 是否必须使用模板 'template_id' => 0, // 模板ID 'is_enable' => false, // 是否启用该支付方式 'is_default' => false, // 是否为默认支付方式 'others' => [] // 其他配置 ]; $defaultMethods = []; foreach ($designated as $key => $method) { $defaultMethods["{$client}-{$method}"] = array_merge($record, [ 'key' => $key + 1, 'method' => $method, 'is_must_template' => $method != PaymentMethodEnum::BALANCE, 'is_enable' => $method == PaymentMethodEnum::BALANCE, ]); } return $defaultMethods; } /** * 根据client过滤list * @param array $listArr * @param string $client * @return array|iterable */ private function listAsClient(array $listArr, string $client) { $listAsClient = helper::arrayFilterAsVal($listArr, 'client', $client); foreach ($listAsClient as &$item) { $item['is_must_template'] = (bool)$item['is_must_template']; $item['is_enable'] = (bool)$item['is_enable']; $item['is_default'] = (bool)$item['is_default']; } return $listAsClient; } /** * 根据指定客户端获取可用的支付方式 * @param string $client 客户端来源 * @param bool $balance 是否支持余额支付 * @param int|null $storeId 商城ID * @return array * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException * @throws BaseException */ public function getMethodsByClient(string $client, bool $balance = true, int $storeId = null, int $merchantId = null): array { $storeId = $storeId ?: self::$storeId; $group = static::getItem($client, $storeId, $merchantId); $methods = []; foreach ($group['methods'] as $method) { if ($method['is_enable'] && ($balance || $method['method'] !== PaymentMethodEnum::BALANCE)) { $methods[] = $method; } } if (empty($methods)) { throwError('很抱歉,当前没有可用的支付方式,请检查后台支付设置'); } return $methods; } /** * 获取指定的支付方式及模板 * @param string $method 支付方式 * @param string $client 客户端来源 * @param int|null $storeId 商城ID * @return bool|mixed * @throws BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function getPaymentInfo(string $method, string $client, int $storeId = null, int $merchantId = null) { // 获取当前指定的支付方式 $methodInfo = $this->getCurrentMethod($method, $client, $storeId, $merchantId); // 获取支付模板信息 $methodInfo['template'] = !$methodInfo['is_must_template'] ? [] : $this->getTemplateInfo($methodInfo['template_id']); return $methodInfo; } /** * 获取支付模板 * @param int $templateId 支付模板ID * @return array * @throws BaseException */ private function getTemplateInfo(int $templateId): array { return (new PaymentTemplateModel)->getTemplateInfo($templateId); } /** * 获取当前指定的支付方式 * @param string $method 指定的支付方式 * @param string $client 客户端来源 * @param int|null $storeId 商城ID * @return bool|mixed * @throws BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ private function getCurrentMethod(string $method, string $client, int $storeId = null, int $merchantId = null) { $methods = $this->getMethodsByClient($client, true, $storeId, $merchantId); $method = helper::arraySearch($methods, 'method', $method); if (empty($method)) { throwError('很抱歉,当前未找到指定的支付方式'); } return $method; } }