6
0
Fork 0

添加后台推送

release
vine_liutk 2021-12-21 17:50:28 +08:00
parent f4beb2e5b3
commit 3b87d84079
13 changed files with 501 additions and 0 deletions

View File

@ -3,6 +3,8 @@
namespace App\Admin\Controllers;
use App\Admin\Repositories\Message;
use App\Models\Message as MessageModel;
use App\Services\Push\MallUnipushService;
use Dcat\Admin\Admin;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
@ -29,6 +31,7 @@ class MessageController extends AdminController
'2'=>__('admin_message.ad.jump_type.radio.2'),
])->label();
$grid->column('jump_link');
$grid->column('is_push')->bool();
$grid->column('created_at')->sortable();
$grid->model()->orderBy('created_at', 'desc');
@ -94,6 +97,19 @@ class MessageController extends AdminController
'2'=>__('admin_message.ad.jump_type.radio.2'),
])->default(0);
$form->text('jump_link');
if ($form->isEditing()) {
$form->switch('is_push')->disable();
} else {
$form->switch('is_push')->default(0);
}
$form->saved(function (Form $form, $result) {
// 判断是否是新增操作
if ($form->isCreating() && $form->is_push) {
$pushService = new MallUnipushService();
$pushService->pushAllMessage(MessageModel::findOrFail($result));
return;
}
});
$form->display('created_at');
$form->display('updated_at');

View File

@ -0,0 +1,47 @@
<?php
namespace App\Endpoint\Api\Http\Controllers;
use App\Exceptions\BizException;
use App\Models\UserCid;
use App\Services\Push\MallUnipushService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Throwable;
class PushController extends Controller
{
/**
* 绑定cid
*
* @param Request $request
* @return void
*/
public function bindUniCid(Request $request, MallUnipushService $mallUnipushService)
{
if (!$request->user()) {
throw new BizException('绑定失败,请稍后再试');
}
$input = $request->validate([
'cid' => ['bail', 'required', 'string', 'max:64'],
]);
try {
DB::beginTransaction();
//查询目前有没有人已绑定这个cid, 有就解绑
UserCid::where('u_cid', $input['cid'])->where('user_id', '<>', $request->user()->id)->update([
'u_cid' => null,
]);
$request->user()->cid()->updateOrCreate([
'u_cid'=>$input['cid'],
]);
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
report($th);
throw new BizException('绑定失败,请稍后再试');
}
return response()->noContent();
}
}

View File

@ -22,6 +22,7 @@ use App\Endpoint\Api\Http\Controllers\Product\ProductCategoryController;
use App\Endpoint\Api\Http\Controllers\Product\ProductFavoriteController;
use App\Endpoint\Api\Http\Controllers\Product\ProductSkuController;
use App\Endpoint\Api\Http\Controllers\Product\ProductViewLogController;
use App\Endpoint\Api\Http\Controllers\PushController;
use App\Endpoint\Api\Http\Controllers\ShippingAddressController;
use App\Endpoint\Api\Http\Controllers\ShoppingCartItemController;
use App\Endpoint\Api\Http\Controllers\SmsCodeController;
@ -66,6 +67,8 @@ Route::group([
Route::get('article-config', [ArticleController::class, 'config']);
//获取最新app版本信息
Route::get('app-version', [AppVersionController::class, 'index']);
//绑定用户端cid
Route::post('push-bind-uni', [PushController::class, 'bindUniCid']);
Route::middleware(['auth:api'])->group(function () {
// 我的信息

View File

@ -19,6 +19,7 @@ class Message extends Model
protected $casts = [
'ext'=>JsonArray::class,
'is_push'=>'bool',
];
protected $fillable = [
@ -30,6 +31,11 @@ class Message extends Model
return $this->hasMany(MessageReadLog::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
public static function userMessages(User $user)
{
return self::where(function ($q) use ($user) {

View File

@ -118,6 +118,14 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac
return $this->hasMany(ProductSkuFavorite::class);
}
/**
* 属于此用户的推送cid
*/
public function cid()
{
return $this->hasOne(UserCid::class, 'user_id');
}
/**
* 确认此用户是否是 VIP
*

View File

@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class UserCid extends Model
{
use HasFactory;
protected $table = 'user_cids';
protected $fillable = [
'user_id', 'u_cid', 'm_cid',
];
}

View File

@ -0,0 +1,61 @@
<?php
namespace App\Services\Push;
use App\Models\Message;
class MallUnipushService extends UniPushService
{
protected $appId;
protected $appKey;
protected $appSecret;
protected $masterSecret;
public function __construct()
{
$this->appId = config('settings.mall_push_app_id', '');
$this->appKey = config('settings.mall_push_app_key', '');
$this->appSecret = config('settings.mall_push_app_secret', '');
$this->masterSecret = config('settings.mall_push_master_secret', '');
parent::__construct($this->appId, $this->appKey, $this->appSecret, $this->masterSecret);
}
/**
* 推送公告消息
*
* @param [type] $message
* @return void
*/
public function pushAllMessage(Message $message)
{
$this->pushAll($message->title, $message->content, [
'jump_type' => $message->jump_type == 0 ? 1 : $message->jump_type,
'jump_link' => $message->type == 1 || empty($message->jump_link) ? '/pages/news/index' : $message->jump_link,
]);
}
/**
* 推送单条消息
*
* @param Message $message
* @return void
*/
public function pushMessage(Message $message)
{
//如果不是指定消息,直接退出;
if (is_null($message->user)) {
return;
}
//如果拿不到u_cid直接退出
if (!$message->user->cid->u_cid) {
return;
}
$this->pushCid($message->user->cid->u_cid, $message->title, $message->content, [
'jump_type' => $message->jump_type == 0 ? 1 : $message->jump_type,
'jump_link' => $message->type == 1 || empty($message->jump_link) ? '/pages/news/index' : $message->jump_link,
]);
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Services\Push;
class MerchantUnipushService
{
public function __construct()
{
}
}

View File

@ -0,0 +1,251 @@
<?php
namespace App\Services\Push;
use App\Helpers\Order as OrderHelper;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
class UniPushService
{
protected $appId;
protected $appKey;
protected $appSecret;
protected $masterSecret;
protected $cacheKey;
protected $uri = 'https://restapi.getui.com/v2/';
public function __construct(string $appId, string $appKey, string $appSecret, string $masterSecret)
{
$this->appId = $appId;
$this->appKey = $appKey;
$this->appSecret = $appSecret;
$this->masterSecret = $masterSecret;
$this->cacheKey = 'uni-push-'.$this->appId;
}
/**
* 获取当前实例token
*
* @return void
*/
public function getToken()
{
if (Cache::has($this->cacheKey)) {
return Cache::get($this->cacheKey);
} else {
return $this->getTokenFromServer();
}
}
protected function getUri($uri)
{
return $this->uri.$this->appId.'/'.$uri;
}
/**
* 获取unix毫秒时间戳
*
* @return void
*/
protected function msectime()
{
list($msec, $sec) = explode(' ', microtime());
$msectime = (float) sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
/**
* 创建签名
*
* @return void
*/
protected function makeSign($param)
{
return hash('sha256', $param['appkey'].$param['timestamp'].$this->masterSecret);
}
/**
* 重新获取token
*
* @return void
*/
protected function getTokenFromServer()
{
$token = '';
$param = [
'appkey'=>$this->appKey,
'timestamp'=>$this->msectime(),
];
$param['sign'] = $this->makeSign($param);
$response = Http::withHeaders([
'Accept' => 'application/json',
])->post($this->getUri('auth'), $param);
if ($response->successful()) {
$data = $response->json();
$token = $data['data']['token'];
Cache::put($this->cacheKey, $data['data']['token'], $data['data']['expire_time']);
}
return $token;
}
/**
* 封装公共请求
*
* @return void
*/
protected function post($uri, $params)
{
$token = $this->getToken();
// dd($params);
$response = Http::withHeaders([
'Accept' => 'application/json',
'token' => $token,
])->post($this->getUri($uri), $params);
$resData = [];
if ($response->successful()) {
$resData = $response->json();
}
return $resData;
}
protected function get($uri, $params = null)
{
$token = $this->getToken();
$response = Http::withHeaders([
'Accept' => 'application/json',
'token' => $token,
])->get($this->getUri($uri), $params);
$resData = [];
if ($response->successful()) {
$resData = $response->json();
}
return $resData;
}
protected function createPushData($title, $body, $params = [])
{
$payload = urlencode(json_encode($params));
return [
'request_id' => OrderHelper::serialNumber(),
'settings'=>[
'ttl'=> 3600000,
'strategy'=> [
'default'=>1,
'ios'=>4,
],
],
'push_message'=>[
'transmission'=> json_encode([
'title' => $title,
'content'=> $body,
'payload'=> $payload,
]),
// 'notification'=>[
// 'title' => $title,
// 'body' => $body,
// 'click_type'=> 'payload', //默认打开首页
// 'payload'=> $payload,
// ],
],
'push_channel'=>[
'android'=>[
'ups'=> [
'transmission'=> json_encode([
'title' => $title,
'content'=> $body,
'payload'=> $payload,
]),
// 'notification'=>[
// 'title' => $title,
// 'body' => $body,
// 'click_type'=> 'payload', //默认打开首页
// 'payload'=> $payload,
// ],
],
],
'ios'=>[
'type'=>'notify',
'payload' => $payload,
'aps'=>[
'alert'=>[
'title'=>$title,
'body'=>$body,
],
'content-available'=> 0,
],
],
],
];
}
/**
* 绑定cid和user_id
*
* @return void
*/
public function bindCid($userId, $cid)
{
$this->post('user/alias', [
'data_list'=>[
'cid' => $cid,
'alias'=>$userId,
],
]);
}
/**
* 解绑所有与该别名绑定的cid
*/
public function unbindCid($userId)
{
$res = $this->get('user/alias/'.$userId);
if (Arr::get($res, 'code', 1) === 0) {
return true;
}
return false;
}
/**
* 根据cid单推
*
* @return void
*/
public function pushCid(string $cid, string $title, string $body, array $params = [])
{
$this->post('push/single/cid', array_merge([
'audience'=>[
'cid'=>[
$cid,
],
],
], $this->createPushData($title, $body, $params)));
}
/**
* 给所有人推送
*
* @param string $title
* @param string $body
* @param array $params
* @return void
*/
public function pushAll(string $title, string $body, array $params = [])
{
$this->post('push/all', array_merge([
'audience'=>'all',
], $this->createPushData($title, $body, $params)));
}
}

View File

@ -32,4 +32,18 @@ return [
'kuaidi100_secret'=> '1bd287d1981749f2a30ea74cac0ab99c',
'kuaidi100_userid'=> 'ec0b6ec7729d4f22824cfd3c519dd45b',
//推送
'mall_push_app_id'=>'iikmCoESID8bC1LhOPG1r8',
'mall_push_app_key'=>'JX33P0wP8bAQprI953hpN6',
'mall_push_app_secret'=>'a3u3B6lXjq6fPTBlOGiOc9',
'mall_push_master_secret'=>'MAxmqomwo597xJeDuMCvx1',
'merchant_push_app_id'=>'',
'merchant_push_app_key'=>'',
'merchant_push_app_secret'=>'',
'merchant_push_master_secret'=>'',
//app配置
'user_center_is_open'=>true,
];

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsPushToMessagesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('messages', function (Blueprint $table) {
//
$table->unsignedTinyInteger('is_push')->default(0)->comment('是否推送');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('messages', function (Blueprint $table) {
//
$table->dropColumn('is_push');
});
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserCidsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_cids', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id')->comment('用户ID');
$table->string('u_cid')->nullable()->comment('用户推送cid');
$table->string('m_cid')->nullable()->comment('商户推送cid');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_cids');
}
}

View File

@ -12,6 +12,7 @@ return [
'ext' => '扩展内容',
'jump_type' => '跳转类型',
'jump_link' => '跳转地址',
'is_push'=>'推送',
],
'options' => [
],