no message

This commit is contained in:
kuaifan 2022-05-13 15:25:29 +08:00
parent 6dbbcff0b6
commit 8f58797256
16 changed files with 560 additions and 13 deletions

View File

@ -153,6 +153,51 @@ class SystemController extends AbstractController
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
/**
* @api {get} api/system/setting/apppush 02. 获取APP推送设置、保存APP推送设置限管理员
*
* @apiVersion 1.0.0
* @apiGroup system
* @apiName setting__apppush
*
* @apiParam {String} type
* - get: 获取(默认)
* - save: 保存设置(参数:['push', 'ios_key', 'ios_secret', 'android_key', 'android_secret']
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function setting__apppush()
{
User::auth('admin');
//
$type = trim(Request::input('type'));
if ($type == 'save') {
if (env("SYSTEM_SETTING") == 'disabled') {
return Base::retError('当前环境禁止修改');
}
$all = Request::input();
foreach ($all as $key => $value) {
if (!in_array($key, [
'push',
'ios_key',
'ios_secret',
'android_key',
'android_secret'
])) {
unset($all[$key]);
}
}
$setting = Base::setting('appPushSetting', Base::newTrim($all));
} else {
$setting = Base::setting('appPushSetting');
}
//
$setting['push'] = $setting['push'] ?: 'close';
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
/**
* @api {get} api/system/demo 03. 获取演示帐号
*

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers\Api;
use App\Models\AbstractModel;
use App\Models\UmengAlias;
use App\Models\User;
use App\Models\UserEmailVerification;
use App\Models\UserTransfer;
@ -684,4 +685,53 @@ class UsersController extends AbstractController
return Base::retSuccess('绑定邮箱成功');
}
/**
* @api {get} api/users/umeng/alias 13. 设置友盟别名
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName umeng__alias
*
* @apiParam {String} alias 别名
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据(同"获取我的信息"接口)
*/
public function umeng__alias()
{
$data = Request::input();
// 表单验证
Base::validator($data, [
'alias.required' => '别名不能为空',
'alias.between:2,20' => '别名的长度在2-20个字符',
]);
//
$agent = strtolower(Request::server('HTTP_USER_AGENT'));
if (str_contains($agent, 'android')) {
$platform = 'android';
} elseif (str_contains($agent, 'iphone') || str_contains($agent, 'ipad')) {
$platform = 'ios';
} else {
return Base::retError('设备类型错误');
}
//
$user = User::auth();
$inArray = [
'userid' => $user->userid,
'alias' => $data['alias'],
'platform' => $platform,
];
if (UmengAlias::where($inArray)->exists()) {
return Base::retSuccess('别名已存在');
}
$row = UmengAlias::createInstance($inArray);
if ($row->save()) {
return Base::retSuccess('添加成功');
} else {
return Base::retError('添加错误');
}
}
}

165
app/Models/UmengAlias.php Normal file
View File

@ -0,0 +1,165 @@
<?php
namespace App\Models;
use App\Module\Base;
use Carbon\Carbon;
use Hedeqiang\UMeng\Android;
use Hedeqiang\UMeng\IOS;
/**
* App\Models\UmengAlias
*
* @property int $id
* @property int|null $userid 会员ID
* @property string|null $alias 别名
* @property string|null $platform 平台类型
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias query()
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereAlias($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias wherePlatform($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UmengAlias whereUserid($value)
* @mixin \Eloquent
*/
class UmengAlias extends AbstractModel
{
protected $table = 'umeng_alias';
/**
* 获取推送配置
* @return array|false
*/
public static function getPushConfig()
{
$setting = Base::setting('appPushSetting');
if ($setting['push'] !== 'open') {
return false;
}
$config = [];
if ($setting['ios_key']) {
$config['iOS'] = [
'appKey' => $setting['ios_key'],
'appMasterSecret' => $setting['ios_secret'],
'production_mode' => true,
];
}
if ($setting['android_key']) {
$config['Android'] = [
'appKey' => $setting['android_key'],
'appMasterSecret' => $setting['android_secret'],
'production_mode' => true,
];
}
return $config;
}
/**
* 推送消息
* @param string $alias
* @param string $platform
* @param array $array [title, subtitle, body, description, extra, seconds, badge]
* @return array|false
*/
public static function pushMsgToAlias($alias, $platform, $array)
{
$config = self::getPushConfig();
if ($config === false) {
return false;
}
//
$title = $array['title'] ?: ''; // 标题
$subtitle = $array['subtitle'] ?: ''; // 副标题iOS
$body = $array['body'] ?: ''; // 通知内容
$description = $array['description'] ?: 'no description'; // 描述
$extra = is_array($array['extra']) ? $array['extra'] : []; // 额外参数
$seconds = intval($array['seconds']) ?: 86400; // 有效时间(单位:秒)
$badge = intval($array['badge']) ?: 0; // 角标数iOS
//
switch ($platform) {
case 'ios':
if (!isset($config['iOS'])) {
return false;
}
$ios = new IOS($config);
return $ios->send([
'description' => $description,
'payload' => array_merge([
'aps' => [
'alert' => [
'title' => $title,
'subtitle' => $subtitle,
'body' => $body,
],
'sound' => 'default',
'badge' => $badge,
],
], $extra),
'type' => 'customizedcast',
'alias_type' => 'userid',
'alias' => $alias,
'policy' => [
'expire_time' => Carbon::now()->addSeconds($seconds)->toDateTimeString(),
],
]);
case 'android':
if (!isset($config['Android'])) {
return false;
}
$android = new Android($config);
return $android->send([
'description' => $description,
'payload' => array_merge([
'display_type' => 'notification',
'body' => [
'ticker' => $title,
'text' => $body,
'title' => $title,
'after_open' => 'go_app',
'play_sound' => true,
],
], $extra),
'type' => 'customizedcast',
'alias_type' => 'userid',
'alias' => $alias,
'policy' => [
'expire_time' => Carbon::now()->addSeconds($seconds)->toDateTimeString(),
],
]);
default:
return false;
}
}
/**
* 推送给指定会员
* @param array|int $userid
* @param array $array
* @return void
*/
public static function pushMsgToUserid($userid, $array)
{
$builder = self::select(['id', 'platform', 'alias']);
if (is_array($userid)) {
$builder->whereIn('userid', $userid);
} elseif (Base::isNumber($userid)) {
$builder->whereUserid($userid);
}
$builder
->orderByDesc('id')
->chunkById(100, function ($rows) use ($array) {
$lists = $rows->groupBy('platform');
foreach ($lists as $platform => $list) {
$alias = $list->pluck('alias')->implode(',');
self::pushMsgToAlias($alias, $platform, $array);
}
});
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Tasks;
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
use App\Models\UmengAlias;
use App\Module\Base;
/**
* 推送友盟消息
*/
class PushUmengMsg extends AbstractTask
{
protected $userid = 0;
protected $array = [];
/**
* @param array|int $userid
* @param array $array
*/
public function __construct($userid, $array = [])
{
$this->userid = $userid;
$this->array = is_array($array) ? $array : [];
}
public function start()
{
if (empty($this->userid) || empty($this->array)) {
return;
}
$setting = Base::setting('appPushSetting');
if ($setting['push'] !== 'open') {
return;
}
UmengAlias::pushMsgToUserid($this->userid, $this->array);
}
}

View File

@ -8,6 +8,7 @@ use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Models\WebSocketDialogMsgRead;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use Request;
@ -80,6 +81,19 @@ class WebSocketDialogMsgTask extends AbstractTask
]
]);
}
// umeng推送app
$msgTitle = User::userid2nickname($msg->userid);
if ($dialog->type == 'group') {
$msgTitle = "{$dialog->name} ($msgTitle)";
}
$umengMsg = new PushUmengMsg(array_keys($array), [
'title' => $msgTitle,
'body' => $msg->previewMsg(),
'description' => "消息推送-ID:{$msg->id}",
'seconds' => 3600,
'badge' => 1,
]);
Task::deliver($umengMsg);
// 推送目标②:正在打开这个任务会话的会员
if ($dialog->type == 'group' && $dialog->group_type == 'task') {

View File

@ -18,6 +18,7 @@
"fruitcake/laravel-cors": "^2.0.4",
"guanguans/notify": "^1.20",
"guzzlehttp/guzzle": "^7.3.0",
"hedeqiang/umeng": "^2.1",
"laravel/framework": "^v8.48.1",
"laravel/tinker": "^v2.6.1",
"maatwebsite/excel": "^3.1.31",

68
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ea9cf5820d67b47b3ade5df2398c7365",
"content-hash": "7cf09e78a30054fd005c1dac6a92ec6e",
"packages": [
{
"name": "brick/math",
@ -1207,6 +1207,72 @@
],
"time": "2021-10-06T17:43:30+00:00"
},
{
"name": "hedeqiang/umeng",
"version": "v2.1.3",
"source": {
"type": "git",
"url": "https://github.com/hedeqiang/UMeng-Push.git",
"reference": "6f48d7b8e43dbf88cf7cd22d0693e8cf5b44c118"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hedeqiang/UMeng-Push/zipball/6f48d7b8e43dbf88cf7cd22d0693e8cf5b44c118",
"reference": "6f48d7b8e43dbf88cf7cd22d0693e8cf5b44c118",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/guzzle": "^6.3|^7.0",
"php": ">=7.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.19|^3.8"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Hedeqiang\\UMeng\\PushServiceProvider"
]
},
"hyperf": {
"config": "Hedeqiang\\UMeng\\ConfigProvider"
}
},
"autoload": {
"psr-4": {
"Hedeqiang\\UMeng\\": "./src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "hedeqiang",
"email": "laravel_code@163.com"
},
{
"name": "housemecn",
"email": "houseme@outlook.com"
}
],
"description": "友盟推送",
"keywords": [
"U-Push",
"php",
"sdk",
"umeng",
"友盟"
],
"support": {
"issues": "https://github.com/hedeqiang/UMeng-Push/issues",
"source": "https://github.com/hedeqiang/UMeng-Push/tree/v2.1.3"
},
"time": "2022-04-22T12:03:37+00:00"
},
{
"name": "intervention/image",
"version": "2.7.1",

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUmengAliasTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('umeng_alias', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
$table->string('alias', 50)->nullable()->default('')->comment('别名');
$table->string('platform', 50)->nullable()->default('')->comment('平台类型');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('umeng_alias');
}
}

2
electron/build.js vendored
View File

@ -132,7 +132,7 @@ function startBuild(data, publish) {
econfig.build.directories.output = `dist/${data.id}/${data.platform}`;
econfig.build.artifactName = utils.getDomain(data.url) + "-v${version}-${os}-${arch}.${ext}";
econfig.build.nsis.artifactName = utils.getDomain(data.url) + "-v${version}-${os}-${arch}.${ext}";
if (!process.env.APPLEID || !process.env.APPLEIDPASS) {
if (!process.env.APPLEID || !process.env.APPLEIDPASS || publish !== true) {
delete econfig.build.afterSign;
}
if (process.env.RELEASE_BODY) {

View File

@ -62,7 +62,7 @@ export default {
},
computed: {
...mapState(['userId', 'cacheDrawerOverlay']),
...mapState(['userId', 'userToken', 'cacheDrawerOverlay']),
},
watch: {
@ -90,7 +90,19 @@ export default {
userId: {
handler() {
this.$store.dispatch("websocketConnection")
this.$store.dispatch("websocketConnection");
//
if (this.$isEEUiApp) {
setTimeout(_ => {
const webview = requireModuleJs("webview");
webview && webview.sendMessage({
action: 'setUmengAlias',
userid: this.userId,
token: this.userToken,
url: $A.apiUrl('users/umeng/alias')
});
}, 6000)
}
},
immediate: true
},

View File

@ -108,6 +108,7 @@ Vue.prototype.$Electron = null;
Vue.prototype.$Platform = "web";
Vue.prototype.$isMainElectron = false;
Vue.prototype.$isSubElectron = false;
Vue.prototype.$isEEUiApp = isEEUiApp;
if (isElectron) {
Vue.prototype.$Electron = electron;
Vue.prototype.$Platform = /macintosh|mac os x/i.test(navigator.userAgent) ? "mac" : "win";
@ -139,6 +140,7 @@ $A.Electron = app.$Electron;
$A.Platform = app.$Platform;
$A.isMainElectron = app.$isMainElectron;
$A.isSubElectron = app.$isSubElectron;
$A.isEEUiApp = app.$isEEUiApp;
$A.execMainDispatch = (action, data) => {
if ($A.isSubElectron) {
$A.Electron.sendMessage('sendForwardMain', {
@ -147,3 +149,4 @@ $A.execMainDispatch = (action, data) => {
});
}
};

View File

@ -0,0 +1,92 @@
<template>
<div class="setting-component-item">
<Form ref="formData" :model="formData" :rules="ruleData" label-width="auto" @submit.native.prevent>
<div class="block-setting-box">
<h3>{{ $L('友盟推送') }}</h3>
<FormItem :label="$L('开启推送')" prop="push">
<RadioGroup v-model="formData.push">
<Radio label="open">{{ $L('开启') }}</Radio>
<Radio label="close">{{ $L('关闭') }}</Radio>
</RadioGroup>
</FormItem>
<template v-if="formData.push === 'open'">
<Divider orientation="left">iOS</Divider>
<FormItem label="Appkey" prop="ios_appkey">
<Input :maxlength="255" v-model="formData.ios_key"/>
</FormItem>
<FormItem label="App Master Secret" prop="secret">
<Input :maxlength="255" v-model="formData.ios_secret" type="password"/>
</FormItem>
<Divider orientation="left">Android</Divider>
<FormItem label="Appkey" prop="android_appkey">
<Input :maxlength="255" v-model="formData.android_key"/>
</FormItem>
<FormItem label="App Master Secret" prop="secret">
<Input :maxlength="255" v-model="formData.android_secret" type="password"/>
</FormItem>
</template>
</div>
</Form>
<div class="setting-footer">
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{ $L('提交') }}</Button>
<Button :loading="loadIng > 0" @click="resetForm" style="margin-left: 8px">{{ $L('重置') }}</Button>
</div>
</div>
</template>
<script>
export default {
name: "SystemAppPush",
data() {
return {
loadIng: 0,
formData: {
push: '',
ios_key: '',
ios_secret: '',
android_key: '',
android_secret: '',
},
ruleData: {},
}
},
mounted() {
this.systemSetting();
},
methods: {
submitForm() {
this.$refs.formData.validate((valid) => {
if (valid) {
this.systemSetting(true);
}
})
},
resetForm() {
this.formData = $A.cloneJSON(this.formDatum_bak);
},
systemSetting(save) {
this.loadIng++;
this.$store.dispatch("call", {
url: 'system/setting/apppush?type=' + (save ? 'save' : 'all'),
data: this.formData,
}).then(({data}) => {
if (save) {
$A.messageSuccess('修改成功');
}
this.formData = data;
this.formDatum_bak = $A.cloneJSON(this.formData);
}).catch(({msg}) => {
if (save) {
$A.modalError(msg);
}
}).finally(_ => {
this.loadIng--;
});
}
}
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="setting-component-item">
<Form ref="formData" :model="formData" :rules="ruleData" label-width="auto" @submit.native.prevent>
<div class="email-setting-box">
<div class="block-setting-box">
<h3>{{ $L('邮箱服务器设置') }}</h3>
<FormItem :label="$L('SMTP服务器')" prop="smtp_server">
<Input v-model="formData.smtp_server"/>
@ -20,9 +20,9 @@
</FormItem>
</div>
<div class="email-setting-placeholder"></div>
<div class="block-setting-placeholder"></div>
<div class="email-setting-box">
<div class="block-setting-box">
<h3>{{ $L('邮件通知设置') }}</h3>
<FormItem :label="$L('开启注册验证')" prop="reg_verify">
<RadioGroup v-model="formData.reg_verify">

View File

@ -70,16 +70,17 @@ export default {
{path: 'personal', name: '个人设置'},
{path: 'password', name: '密码设置'},
]
if (!this.isDesktop) {
menu.push({path: 'clearCache', name: '清除缓存'})
}
if (this.userIsAdmin) {
menu.push(...[
{path: 'system', name: '系统设置', divided: true},
{path: 'clearCache', name: '清除缓存'},
{path: 'logout', name: '退出登录'},
])
} else {
menu.push(...[
{path: 'clearCache', name: '清除缓存', divided: true},
{path: 'logout', name: '退出登录'},
{path: 'logout', name: '退出登录', divided: true},
])
}
return menu;

View File

@ -13,6 +13,9 @@
<TabPane :label="$L('邮件设置')" name="emailSetting">
<SystemEmailSetting/>
</TabPane>
<TabPane v-if="appPush" :label="$L('APP推送')" name="appPush">
<SystemAppPush/>
</TabPane>
</Tabs>
</div>
</template>
@ -22,13 +25,34 @@ import SystemSetting from "./components/SystemSetting";
import SystemTaskPriority from "./components/SystemTaskPriority";
import SystemColumnTemplate from "./components/SystemColumnTemplate";
import SystemEmailSetting from "./components/SystemEmailSetting";
import SystemAppPush from "./components/SystemAppPush";
export default {
components: {SystemColumnTemplate, SystemTaskPriority, SystemSetting, SystemEmailSetting},
components: {SystemAppPush, SystemColumnTemplate, SystemTaskPriority, SystemSetting, SystemEmailSetting},
data() {
return {
tabAction: 'setting',
appPush: false,
}
},
mounted() {
if ([
'127.0.0.1:2222',
't.hitosea.com',
'dootask.com',
'www.dootask.com'
].includes(this.getDomain($A.apiUrl('../')))) {
this.appPush = true;
}
},
methods: {
getDomain(weburl) {
let urlReg = /http(s)?:\/\/([^\/]+)/i;
let domain = (weburl + "").match(urlReg);
return ((domain != null && domain.length > 0) ? domain[2] : "");
}
}
}
</script>

View File

@ -209,7 +209,7 @@
.setting-footer {
margin: 0 -32px;
}
.email-setting-box {
.block-setting-box {
position: relative;
padding: 44px 24px 4px;
margin: 24px 0 12px;
@ -247,7 +247,7 @@
}
}
.email-setting-placeholder {
.block-setting-placeholder {
height: 8px;
}
}
@ -267,6 +267,7 @@
@media (max-width: 768px) {
.page-setting {
.setting-head {
margin: 32px 32px 0;
.setting-titbox {
.setting-title {
.setting-more {
@ -299,6 +300,7 @@
padding: 12px 32px;
> li {
padding: 0 8px;
font-size: 16px;
&:hover {
background-color: transparent;
}