jh-admin/addon/weapp/model/Weapp.php

712 lines
26 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* SaaSMall商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 成都SAAS云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.gobuysaas.com
* =========================================================
*/
namespace addon\weapp\model;
use addon\wechatpay\model\Config as ConfigModel;
use app\model\BaseModel;
use app\model\system\Api;
use DateTime;
use DateTimeZone;
use EasyWeChat\Factory;
use EasyWeChat\OpenPlatform\Auth\VerifyTicket;
use think\Exception;
use think\facade\Cache;
use addon\weapp\model\Config as WeappConfigModel;
use addon\wxoplatform\model\OpenPlatform;
use app\model\web\Config as WebConfig;
/**
* 微信小程序配置
*/
class Weapp extends BaseModel
{
public $app;//微信模型
public $platform;//微信模型
//小程序类型
public $service_type = array(
0 => "小程序",
);
//小程序认证类型
public $verify_type = array(
-1 => "未认证",
0 => "微信认证",
);
//business_info 说明
public $business_type = array(
'open_store' => "是否开通微信门店功能",
'open_scan' => "是否开通微信扫商品功能",
'open_pay' => "是否开通微信支付功能",
'open_card' => "是否开通微信卡券功能",
'open_shake' => "是否开通微信摇一摇功能",
);
// 站点ID
private $site_id;
private $app_id;
public $openConfig;
private $access_token;
public function __construct($site_id = 0)
{
$this->site_id = $site_id;
//微信小程序配置
$weapp_config_model = new WeappConfigModel();
$weapp_config = $weapp_config_model->getWeappConfig($site_id);
$weapp_config = $weapp_config["data"]["value"];
if (isset($weapp_config['is_authopen'])&&$weapp_config['is_authopen']&& addon_is_exit('wxoplatform')) {
$platform=new OpenPlatform($site_id);
$this->platform=$platform->openPlatform();
$this->app = $this->platform->app('weapp');
// $this->access_token = $this->app->componentAccessToken;
// var_dump($this->access_token);
} else {
$config = [
'app_id' => $weapp_config["appid"] ?? '',
'secret' => $weapp_config["appsecret"] ?? '',
'response_type' => 'array',
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => 'runtime/log/wechat/easywechat.logs',
],
];
$this->app = Factory::miniProgram($config);
$this->app->rebind('request', request());
$this->app->rebind('cache', Cache::store('redis_public'));
$accessToken = $this->app->access_token;
$accessTokenInfo = $accessToken->getToken();
$this->access_token = $accessTokenInfo['access_token'];
}
$this->app_id = $weapp_config["appid"] ?? '';
$this->app->rebind('getClient', new Client($this->app));
}
public function app()
{
return $this->app;
}
public function SetVerifyTicket()
{
$verify_ticket = new VerifyTicket($this->platform);
try {
$verify_ticket->getTicket();
} catch (\Exception $e) {
$where = [['site_id', '=', 0], ['app_module', '=', 'admin'], ['config_key', '=', 'OPLATFORM_CONFIG']];
$info = model('config')->getValue($where, 'value');
if ($info) {
$v = json_decode($info, true);
$verify_ticket->setTicket($v['ComponentVerifyTicket'] ?? '');
}
}
}
public function httpPostJson($url, array $data = [], array $query = [])
{
return $this->app->getClient->httpPostJson($url, $data, $query);
}
public function httpPost($url, array $data = [])
{
return $this->app->getClient->httpPost($url, $data);
}
public function httpGet($url, array $data = [])
{
return $this->app->getClient->httpGet($url, $data);
}
/**
* 获取快递公司列表
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getCompanyList()
{
$cache = Cache::store('redis_public')->get('weixinCompanyListDeliveryList');
if ($cache) return $cache;
try {
$result = $this->httpPostJson('cgi-bin/express/delivery/open_msg/get_delivery_list');
if (isset($result['errcode']) && $result['errcode'] == 0) {
$data = $this->success($result['delivery_list']);
Cache::store('redis_public')->set('weixinCompanyListDeliveryList', $data);
return $data;
} else {
return $this->error('', $result['errmsg']);
}
} catch (\Exception $e) {
return $this->error('', $e->getMessage());
}
}
public function isTradeManaged()
{
try {
$cache = Cache::store('redis_public')->get('isTradeManaged');
if ($cache) return $cache;
$result = $this->app->getClient->isTradeManaged($this->app_id);
if (isset($result['errcode']) && $result['errcode'] == 0) {
$data = $result['is_trade_managed'];
if ($data) {
Cache::store('redis_public')->set('isTradeManaged', $data);
}
return $data;
} else {
return false;
}
} catch (Exception $e) {
return false;
}
}
/**
* TODO
* 根据 jsCode 获取用户 session 信息
* @param $param
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function authCodeToOpenid($param)
{
try {
$result = $this->app->auth->session($param['code']);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->handleError($result['errcode'], $result['errmsg']);
} else {
Cache::store('redis_public')->set('weapp_' . $result['openid'], $result);
unset($result['session_key']);
return $this->success($result);
}
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']);
}
return $this->error('', $e->getMessage());
}
}
/**
* 生成二维码
* @param unknown $param
*/
public function createQrcode($param)
{
try {
$checkpath_result = $this->checkPath($param['qrcode_path']);
if ($checkpath_result["code"] != 0) return $checkpath_result;
// scene:场景值最大32个可见字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~
$scene = '';
if (!empty($param['data'])) {
foreach ($param['data'] as $key => $value) {
//防止参数过长source_member用m代替
if ($key == 'source_member') {
$key = 'm';
}
if ($scene == '') $scene .= $key . '-' . $value;
else $scene .= '&' . $key . '-' . $value;
}
}
$response = $this->app->app_code->getUnlimit($scene, [
'page' => substr($param['page'], 1),
'width' => isset($param['width']) ? $param['width'] : 120
]);
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $param['qrcode_path'] . '/';
$filename .= $response->saveAs($param['qrcode_path'], $param['qrcode_name'] . '_' . $param['app_type'] . '.png');
return $this->success(['path' => $filename]);
} else {
return $this->handleError($response['errcode'], $response['errmsg']);
}
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']);
}
return $this->error('', $e->getMessage());
}
}
/**
* 校验目录是否可写
* @param unknown $path
* @return multitype:number unknown |multitype:unknown
*/
private function checkPath($path)
{
if (is_dir($path) || mkdir($path, intval('0755', 8), true)) {
return $this->success();
}
return $this->error('', "directory {$path} creation failed");
}
/************************************************************* 数据统计与分析 start **************************************************************/
/**
* 访问日趋势
* @param $from 格式 20170313
* @param $to 格式 20170313
*/
public function dailyVisitTrend($from, $to)
{
try {
$result = $this->app->data_cube->dailyVisitTrend($from, $to);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->error([], $result["errmsg"]);
}
return $this->success($result["list"]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问周趋势
* @param $from
* @param $to
* @return array|\multitype
*/
public function weeklyVisitTrend($from, $to)
{
try {
$result = $this->app->data_cube->weeklyVisitTrend($from, $to);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->error([], $result["errmsg"]);
}
return $this->success($result["list"]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问月趋势
* @param $from
* @param $to
* @return array|\multitype
*/
public function monthlyVisitTrend($from, $to)
{
try {
$result = $this->app->data_cube->monthlyVisitTrend($from, $to);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->error([], $result["errmsg"]);
}
return $this->success($result["list"]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问分布
* @param $from
* @param $to
*/
public function visitDistribution($from, $to)
{
try {
$result = $this->app->data_cube->visitDistribution($from, $to);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->error($result, $result["errmsg"]);
}
return $this->success($result["list"]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问页面
* @param $from
* @param $to
*/
public function visitPage($from, $to)
{
try {
$result = $this->app->data_cube->visitPage($from, $to);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->error([], $result["errmsg"]);
}
return $this->success($result["list"]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/************************************************************* 数据统计与分析 end **************************************************************/
/**
* 下载小程序代码包
* @param $site_id
*/
public function download($site_id)
{
$source_file_path = $this->createTempPackage($site_id, 'public/weapp');
$file_arr = getFileMap($source_file_path);
if (!empty($file_arr)) {
$zipname = 'upload/weapp_' . $site_id . '_' . date('Ymd') . '.zip';
$zip = new \ZipArchive();
$res = $zip->open($zipname, \ZipArchive::CREATE);
if ($res === TRUE) {
foreach ($file_arr as $file_path => $file_name) {
if (is_dir($file_path)) {
$file_path = str_replace($source_file_path . '/', '', $file_path);
$zip->addEmptyDir($file_path);
} else {
$zip_path = str_replace($source_file_path . '/', '', $file_path);
$zip->addFile($file_path, $zip_path);
}
}
$zip->close();
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: Binary");
header("Content-Length: " . filesize($zipname));
header("Content-Disposition: attachment; filename=\"" . basename($zipname) . "\"");
readfile($zipname);
@unlink($zipname);
deleteDir($source_file_path);
}
}
}
/**
* 创建临时包
* @param $site_id
* @param $package_path
* @param string $to_path
* @return array
*/
private function createTempPackage($site_id, $package_path, $to_path = '')
{
if (is_dir($package_path)) {
$package = scandir($package_path);
if (empty($to_path)) {
$to_path = 'upload/temp/' . $site_id . '/';
dir_mkdir($to_path);
}
foreach ($package as $path) {
$temp_path = $package_path . '/' . $path;
if (is_dir($temp_path)) {
if ($path == '.' || $path == '..') {//判断是否为系统隐藏的文件.和.. 如果是则跳过否则就继续往下走,防止无限循环再这里。
continue;
}
dir_mkdir($to_path . $path);
$this->createTempPackage($site_id, $temp_path, $to_path . $path . '/');
} else {
if (file_exists($temp_path)) {
copy($temp_path, $to_path . $path);
if (stristr($temp_path, 'common/vendor.js')) {
$content = file_get_contents($to_path . $path);
$content = $this->paramReplace($site_id, $content);
file_put_contents($to_path . $path, $content);
}
}
}
}
return $to_path;
}
}
/**
* 参数替换
* @param $site_id
* @param $string
* @return null|string|string[]
*/
private function paramReplace($site_id, $string)
{
$api_model = new Api();
$api_config = $api_model->getApiConfig();
$api_config = $api_config['data'];
$web_config_model = new WebConfig();
$web_config = $web_config_model->getMapConfig();
$web_config = $web_config['data']['value'];
$socket_url = (strstr(ROOT_URL, 'https://') === false ? str_replace('http', 'ws', ROOT_URL) : str_replace('https', 'wss', ROOT_URL)) . '/wss';
$patterns = [
'/\{\{\$baseUrl\}\}/',
'/\{\{\$imgDomain\}\}/',
'/\{\{\$h5Domain\}\}/',
'/\{\{\$mpKey\}\}/',
'/\{\{\$apiSecurity\}\}/',
'/\{\{\$publicKey\}\}/',
'/\{\{\$webSocket\}\}/'
];
$replacements = [
ROOT_URL,
ROOT_URL,
ROOT_URL . '/h5',
$web_config['tencent_map_key'] ?? '',
$api_config['is_use'] ?? 0,
$api_config['value']['public_key'] ?? '',
$socket_url
];
$string = preg_replace($patterns, $replacements, $string);
return $string;
}
/**
* 消息解密
* @param array $param
*/
public function decryptData($param = [])
{
try {
$cache = Cache::store('redis_public')->get('weapp_' . $param['weapp_openid']);
$session_key = $cache['session_key'] ?? '';
$result = $this->app->encryptor->decryptData($session_key, $param['iv'], $param['encryptedData']);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->handleError($result['errcode'], $result['errmsg']);
}
return $this->success($result);
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']);
}
return $this->error([], $e->getMessage());
}
}
/**
* 获取用户手机号
* @param $code
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getUserPhoneNumber($code)
{
try {
$result = $this->app->getClient->phoneNumber($code);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->handleError($result['errcode'], $result['errmsg']);
}
return $this->success($result['phone_info']);
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse['errcode'], $e->formattedResponse['errmsg']);
}
return $this->error([], $e->getMessage());
}
}
/**
* 获取订阅消息template_id
* @param array $param
*/
public function getTemplateId(array $param)
{
try {
$result = $this->app->subscribe_message->addTemplate($param['tid'], $param['kidList'], $param['sceneDesc']);
return $result;
} catch (\Exception $e) {
return ['errcode' => -1, 'errmsg' => $e->getMessage()];
}
}
/**
* 发送订阅消息
* @param array $param
* @return array
*/
public function sendTemplateMessage(array $param)
{
$result = $this->app->subscribe_message->send([
'template_id' => $param['template_id'],// 模板id
'touser' => $param['openid'], // openid
'page' => $param['page'], // 点击模板卡片后的跳转页面 支持带参数
'data' => $param['data'] // 模板变量
]);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return $this->error($result, $result["errmsg"]);
}
return $this->success($result);
}
/**
* 消息推送
*/
public function relateWeixin()
{
$server = $this->app->server;
$message = $server->getMessage();
if (isset($message['MsgType'])) {
switch ($message['MsgType']) {
case 'event':
$this->app->server->push(function ($res) {
switch ($res['Event']) {
case 'open_product_spu_audit' && addon_is_exit('shopcomponent', $this->site_id):
model('shopcompoent_goods')->update([
'edit_status' => $res['OpenProductSpuAudit']['status'],
'reject_reason' => !empty($res['OpenProductSpuAudit']['reject_reason']) ?: '',
'audit_time' => time()
], [
['out_product_id', '=', $res['OpenProductSpuAudit']['out_product_id']]
]);
break;
case 'open_product_category_audit' && addon_is_exit('shopcomponent', $this->site_id):
model('shopcompoent_category_audit')->update([
'status' => $res['QualificationAuditResult']['status'],
'reject_reason' => !empty($res['QualificationAuditResult']['reject_reason']) ?: '',
'audit_time' => time()
], [
['audit_id', '=', $res['QualificationAuditResult']['audit_id']]
]);
break;
case 'open_product_order_pay' && addon_is_exit('shopcomponent', $this->site_id):
event("shopcomponentNotify", $res);
break;
case 'weapp_audit_fail':
case 'weapp_audit_success'://小程序审核
event('WeappAuditEvent', $res);
break;
case 'weapp_audit_delay':
case 'wxa_nickname_audit':
case 'wxa_privacy_apply':
}
});
break;
}
}
$response = $this->app->server->serve();
return $response->send();
}
/**
* 检查场景值是否在支付校验范围内
* @param $scene
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sceneCheck($scene)
{
try {
$result = $this->app->mini_store->checkScene($scene);
if (isset($result['errcode']) && $result['errcode'] == 0) {
return $this->success($result['is_matched']);
} else {
return $this->error('', $result['errmsg']);
}
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
public function createOrder($order_info)
{
try {
$result = $this->app->mini_store->addOrder($order_info);
if (isset($result['errcode']) && $result['errcode'] == 0) {
return $this->success($result['is_matched']);
} else {
return $this->error('', $result['errmsg']);
}
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 处理错误信息
* @param $errcode
* @param string $message
* @return array
*/
public function handleError($errcode, $message = '')
{
$error = require 'addon/weapp/config/weapp_error.php';
return $this->error([], $error[$errcode] ?? $message);
}
/**
* Common: 自动发货
* Author: wu-hui
* Time: 2024/07/17 11:23
* @param $params
*/
public function authSendGoods($params){
// 创建一个DateTime对象代表当前日期和时间
$now = new DateTime();
$timeZone = new DateTimeZone('Asia/Shanghai');
$now->setTimezone($timeZone);
// 用户信息
$memberInfo = model('member')->getInfo(['member_id' => $params['member_id']], 'weapp_openid,wx_openid');
$openId = $memberInfo['weapp_openid'] ?? $memberInfo['wx_openid'];
// 交易单号
$tradeNo = model('pay')->getValue([['out_trade_no', '=', $params['out_trade_no']]],'trade_no');
// 配置信息
$config_model = new ConfigModel();
$payConfig = $config_model->getPayConfig($params['site_id'])['data']['value'];
// 参数配置
$data = [
// 'access_token' => $this->access_token,
'order_key' => [
'order_number_type' => 2,// 订单单号类型用于确认需要上传详情的订单。枚举值1使用下单商户号和商户侧单号枚举值2使用微信支付单号。
'transaction_id' => $tradeNo,//原支付交易对应的微信订单号
'mchid' => $payConfig['mch_id'],
'out_trade_no' => $params['out_trade_no'],//商户系统内部订单号,只能是数字、大小写字母`_-*`且在同一个商户号下唯一
],
'logistics_type' => 3,//物流模式发货方式枚举值1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品 4、用户自提
'delivery_mode' => 1,// 发货模式发货模式枚举值1、UNIFIED_DELIVERY统一发货2、SPLIT_DELIVERY分拆发货 示例值: UNIFIED_DELIVERY
'shipping_list' => [
[
'item_desc' => $params['order_name'] ?? '收银台订单',// 商品信息,限120个字以内
]
],
'upload_time' => $now->format(DateTime::RFC3339_EXTENDED),// 上传时间,用于标识请求的先后顺序 示例值: `2022-12-15T13:29:35.120+08:00`
'payer' => [
'openid' => $openId,
],
];
// trace($data, '处理订单自动发货 - 请求信息');
$result = $this->app->httpPostJson('wxa/sec/order/upload_shipping_info',$data , ['access_token' => $this->access_token]);
if($result['errcode'] != 0){
trace([
'请求数据' => $data,
'请求结果' => $result
], '处理订单自动发货 - 请求结果错误');
}
}
// 获取错误详细信息
public function getRidInfo(){
// 参数配置
$data = [
'access_token' => $this->access_token,
'rid' => '66972cfa-62e04390-78e7cb02'
];
// trace($data, '处理订单自动发货 - 请求信息');
$result = $this->httpPostJson('/cgi-bin/openapi/rid/get', $data);
debug([
'请求数据' => $data,
'请求结果' => $result
]);
}
}