This commit is contained in:
parent
7a907ad314
commit
dc1d3c1cc1
|
|
@ -156,7 +156,7 @@ class Login extends Base
|
|||
#[Auth(needAuth: false)]
|
||||
public function saveInfo()
|
||||
{
|
||||
$param = Param::only(['nickname', 'birthday', 'sex', 'bio', 'avatar']);
|
||||
$param = Param::only(['nickname', 'birthday', 'sex', 'bio', 'avatar', 'phone', 'email']);
|
||||
$res = Account::where("account_id", $this->accountId())->update($param);
|
||||
return $res ? $this->success("保存成功") : $this->error("保存失败");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use App\Model\Crontab as cModel;
|
|||
use App\Model\CrontabLog as clModel;
|
||||
use App\Model\Dept as dModel;
|
||||
use App\Model\Menu as mModel;
|
||||
use App\Model\AccountMessage as amModel;
|
||||
use App\Model\Online as oModel;
|
||||
use App\Model\Post as pModel;
|
||||
use App\Model\Role as rModel;
|
||||
|
|
@ -275,7 +276,7 @@ class System extends Base
|
|||
$request = $this->container->get(aRequest::class);
|
||||
$request->scene('add')->validateResolved();
|
||||
$account = $this->request->getAttribute("account");
|
||||
$data = Param::only(['roles' => [], 'username', 'status' => 1, 'dept_id' => 0, 'posts' => [], 'password' => '123456']);
|
||||
$data = Param::only(['roles' => [], 'username', 'email', 'phone', 'status' => 1, 'dept_id' => 0, 'posts' => [], 'password' => '123456']);
|
||||
// 判断数据库是否存在相同的用户名
|
||||
$admin = aModel::getByUsername($data['username'], ['account_id']);
|
||||
if (!empty($admin)) {
|
||||
|
|
@ -294,7 +295,7 @@ class System extends Base
|
|||
{
|
||||
$request = $this->container->get(aRequest::class);
|
||||
$request->scene('edit')->validateResolved();
|
||||
$data = Param::only(['roles' => [], 'username', 'status' => 1, 'dept_id' => 0, 'posts' => [], 'password' => '', 'account_id']);
|
||||
$data = Param::only(['roles' => [], 'username', 'email', 'phone', 'status' => 1, 'dept_id' => 0, 'posts' => [], 'password' => '', 'account_id']);
|
||||
$res = aModel::edit($data);
|
||||
return $res ? $this->success("操作成功") : $this->error("操作失败");
|
||||
}
|
||||
|
|
@ -630,7 +631,7 @@ class System extends Base
|
|||
$request->scene('edit')->validateResolved();
|
||||
$data = Param::only(["config_id", "config_name", "config_key", "config_value", "remark"]);
|
||||
// 删除原来的缓存
|
||||
$info = scModel::getById($data['config_id'],['config_key']);
|
||||
$info = scModel::getById($data['config_id'], ['config_key']);
|
||||
(new ConfigCacheService())->removeItem($info['config_key']);
|
||||
// 变更数据
|
||||
$res = scModel::edit($data);
|
||||
|
|
@ -655,7 +656,7 @@ class System extends Base
|
|||
#[Auth(auth: "dict:list")]
|
||||
public function dictList()
|
||||
{
|
||||
$param = Param::only(["dict_name", "dict_type", "limit"=>10]);
|
||||
$param = Param::only(["dict_name", "dict_type", "limit" => 10]);
|
||||
return $this->success("列表接口", dtModel::list($param));
|
||||
}
|
||||
|
||||
|
|
@ -706,7 +707,7 @@ class System extends Base
|
|||
#[Auth(auth: "dict_data:list")]
|
||||
public function dictDataList()
|
||||
{
|
||||
$param = Param::only(["dict_id", "dict_label", "status", "limit"=>10]);
|
||||
$param = Param::only(["dict_id", "dict_label", "status", "limit" => 10]);
|
||||
return $this->success("列表接口", ddModel::list($param));
|
||||
}
|
||||
|
||||
|
|
@ -714,7 +715,7 @@ class System extends Base
|
|||
#[Auth(needAuth: false)]
|
||||
public function dictDataOption()
|
||||
{
|
||||
$param = Param::only(["key"=>""]);
|
||||
$param = Param::only(["key" => ""]);
|
||||
return $this->success(ddModel::options($param));
|
||||
}
|
||||
|
||||
|
|
@ -758,7 +759,7 @@ class System extends Base
|
|||
#[Auth(auth: "translation:list")]
|
||||
public function translationList()
|
||||
{
|
||||
$param = Param::only(["group", "translation_key", "limit"=>10]);
|
||||
$param = Param::only(["group", "translation_key", "limit" => 10]);
|
||||
return $this->success("列表接口", tsModel::list($param));
|
||||
}
|
||||
|
||||
|
|
@ -789,4 +790,50 @@ class System extends Base
|
|||
$ids = $this->request->input("ids");
|
||||
return $this->toAjax(tsModel::del($ids));
|
||||
}
|
||||
|
||||
#[GetMapping(path: "message/mine")]
|
||||
#[Auth(needAuth: false)]
|
||||
public function messageList()
|
||||
{
|
||||
$param = Param::only(["limit" => 10, 'type' => 'system']);
|
||||
$param['account_id'] = $this->accountId();
|
||||
return $this->success("列表接口", amModel::list($param));
|
||||
}
|
||||
|
||||
#[PostMapping(path: "message/read")]
|
||||
#[Auth(needAuth: false)]
|
||||
public function messageRead()
|
||||
{
|
||||
$param = Param::only(["ids" => [], 'type' => '']);
|
||||
$param['account_id'] = $this->accountId();
|
||||
amModel::read($param);
|
||||
// 全部已读
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
#[GetMapping(path: "message/detail")]
|
||||
#[Auth(needAuth: false)]
|
||||
public function messageDetail()
|
||||
{
|
||||
$param = Param::only(["id"]);
|
||||
if (!$param['id']) return $this->error();
|
||||
$param['account_id'] = $this->accountId();
|
||||
$info = amModel::detail($param);
|
||||
if (!empty($info)) {
|
||||
$info = $info->toArray();
|
||||
// 设为已读
|
||||
amModel::read(['ids' => [$param['id']], 'type' => $info['type'], 'account_id' => $this->accountId()]);
|
||||
}
|
||||
return $this->success("详情", $info);
|
||||
}
|
||||
|
||||
#[DeleteMapping(path: "message/remove")]
|
||||
#[Auth(needAuth: false)]
|
||||
public function messageRemove()
|
||||
{
|
||||
$param = Param::only(["id"]);
|
||||
if (!$param['id']) return $this->error();
|
||||
$res = amModel::where(['mess_id' => $param['id'], 'account_id' => $this->accountId()])->delete();
|
||||
return $this->toAjax($res);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,10 @@ namespace App\Controller\Admin;
|
|||
use App\Annotation\Auth;
|
||||
use App\Model\GenTable as gtModel;
|
||||
use App\Request\GenTable as gtRequest;
|
||||
use App\Model\Message as mModel;
|
||||
use App\Request\Message as mRequest;
|
||||
use App\Utils\Param;
|
||||
use App\Utils\QueueClient;
|
||||
use Hyperf\DbConnection\Db;
|
||||
use Hyperf\HttpServer\Annotation\Controller;
|
||||
use Hyperf\HttpServer\Annotation\DeleteMapping;
|
||||
|
|
@ -216,4 +219,54 @@ class Tools extends Base
|
|||
}
|
||||
return $this->success("表单信息", $data);
|
||||
}
|
||||
|
||||
#[GetMapping(path: "message/list")]
|
||||
#[Auth(auth: "message:list")]
|
||||
public function messageList()
|
||||
{
|
||||
$param = Param::only(["title", "limit" => 10]);
|
||||
return $this->success("列表接口", mModel::list($param));
|
||||
}
|
||||
|
||||
#[PostMapping(path: "message/add")]
|
||||
#[Auth(auth: "message:add")]
|
||||
public function messageAdd()
|
||||
{
|
||||
$request = $this->container->get(mRequest::class);
|
||||
$request->scene('add')->validateResolved();
|
||||
$data = Param::only(["title", "content"]);
|
||||
$data['create_id'] = $this->accountId();
|
||||
$data['type'] = 'system';
|
||||
$res = mModel::add($data);
|
||||
if (!$res) return $this->error();
|
||||
QueueClient::push("App\Job\NotifyJob", ['messageId' => $res, 'channels' => ['websocket', 'email']]);
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
#[PutMapping(path: "message/edit")]
|
||||
#[Auth(auth: "message:edit")]
|
||||
public function messageEdit()
|
||||
{
|
||||
$request = $this->container->get(mRequest::class);
|
||||
$request->scene('edit')->validateResolved();
|
||||
$data = Param::only(["message_id", "title", "content"]);
|
||||
return $this->toAjax(mModel::edit($data));
|
||||
}
|
||||
|
||||
#[DeleteMapping(path: "message/del")]
|
||||
#[Auth(auth: "message:del")]
|
||||
public function messageDel()
|
||||
{
|
||||
$ids = $this->request->input("ids");
|
||||
return $this->toAjax(mModel::del($ids));
|
||||
}
|
||||
|
||||
#[PutMapping(path: "message/recalled")]
|
||||
#[Auth(auth: "message:recalled")]
|
||||
public function messageRecalled()
|
||||
{
|
||||
$data = Param::only(["id"]);
|
||||
if (!$data['id']) return $this->error();
|
||||
return $this->toAjax(mModel::edit(['message_id' => $data['id'], 'recalled_flag' => 1]));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Job;
|
||||
|
||||
use App\Utils\QueueClient;
|
||||
use Hyperf\AsyncQueue\Job;
|
||||
use App\Model\Message as mModel;
|
||||
use App\Model\Account as aModel;
|
||||
use App\Model\AccountMessage as amModel;
|
||||
use App\Model\MessageChannel as mcModel;
|
||||
|
||||
class NotifyJob extends Job
|
||||
{
|
||||
// 重试次数
|
||||
protected int $maxAttempts = 1;
|
||||
|
||||
protected int|null $messageId;
|
||||
protected array|null $userIds;
|
||||
protected array|null $channels;
|
||||
|
||||
|
||||
public function __construct(array $params)
|
||||
{
|
||||
$this->messageId = $params["messageId"] ?? null;
|
||||
$this->userIds = $params["userIds"] ?? null;
|
||||
$this->channels = $params["channels"] ?? null;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
// 查询消息信息
|
||||
$message = mModel::getById($this->messageId);
|
||||
if (empty($message)) return true;
|
||||
if (empty($this->userIds) && $message['type'] == "system") {
|
||||
// 系统消息推送给所有用户
|
||||
$this->userIds = aModel::pluck("account_id")->toArray();
|
||||
}
|
||||
if (empty($this->userIds)) return true;
|
||||
// 批量插入数据
|
||||
$mess = [];
|
||||
foreach ($this->userIds as $id) {
|
||||
$mess[] = [
|
||||
'account_id' => $id,
|
||||
'message_id' => $this->messageId,
|
||||
'create_time' => date("Y-m-d H:i:s")
|
||||
];
|
||||
}
|
||||
amModel::insert($mess);
|
||||
// 多渠道推送
|
||||
if (empty($this->channels)) return true;
|
||||
foreach ($this->userIds as $id) {
|
||||
foreach ($this->channels as $channel) {
|
||||
mcModel::add([
|
||||
'account_id' => $id,
|
||||
'message_id' => $this->messageId,
|
||||
'channel' => $channel
|
||||
]);
|
||||
// 队列多渠道推送
|
||||
QueueClient::push("App\Job\SendChannelJob", ["userId" => $id, "messageId" => $this->messageId, "channel" => $channel]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Job;
|
||||
|
||||
use App\Service\Message\ChannelFactory;
|
||||
use App\Utils\Log;
|
||||
use Hyperf\AsyncQueue\Job;
|
||||
use App\Model\Message as mModel;
|
||||
use App\Model\Account as aModel;
|
||||
use App\Model\MessageChannel as mchModel;
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
class SendChannelJob extends Job
|
||||
{
|
||||
protected int $maxAttempts = 2;
|
||||
|
||||
protected ChannelFactory $factory;
|
||||
|
||||
protected int $userId;
|
||||
protected int $messageId;
|
||||
protected string $channel;
|
||||
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->userId = $params['userId'];
|
||||
$this->messageId = $params['messageId'];
|
||||
$this->channel = $params['channel'];
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$channel = make(ChannelFactory::class)->get($this->channel);
|
||||
$account = aModel::find($this->userId);
|
||||
$message = mModel::find($this->messageId);
|
||||
$channel->send($account, $message);
|
||||
mchModel::query()
|
||||
->where('message_id', $this->messageId)
|
||||
->where('account_id', $this->userId)
|
||||
->where('channel', $this->channel)
|
||||
->update(['status' => 1]);
|
||||
} catch (\Exception $e) {
|
||||
Log::record("消息投递失败:" . $e->getMessage(), "SendChannelJob");
|
||||
mchModel::query()
|
||||
->where('message_id', $this->messageId)
|
||||
->where('account_id', $this->userId)
|
||||
->where('channel', $this->channel)
|
||||
->update(['status' => 2, 'fail_reason' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,8 @@ use Hyperf\DbConnection\Db;
|
|||
* @property string $bio
|
||||
* @property string $tags
|
||||
* @property int $sex
|
||||
* @property string $email
|
||||
* @property string $phone
|
||||
* @property string $birthday
|
||||
* @property string $deleted_at
|
||||
* @property string $create_time
|
||||
|
|
@ -60,14 +62,14 @@ class Account extends Model
|
|||
{
|
||||
$info = self::with('roles')
|
||||
->with('posts')
|
||||
->select(['account_id', 'username', 'nickname', 'avatar', 'bio', 'tags', 'sex', 'birthday', 'create_time', 'dept_id'])
|
||||
->select(['account_id', 'username', 'nickname', 'avatar', 'bio', 'tags', 'sex', 'birthday', 'create_time', 'dept_id', 'email', 'phone'])
|
||||
->find($account_id);
|
||||
if ($info) {
|
||||
$info = $info->toArray();
|
||||
// 获取所有上级
|
||||
$dept = [];
|
||||
Dept::getTop($info['dept_id'], $dept);
|
||||
$info['dept'] = array_reverse(array_column($dept,"dept_name"));
|
||||
$info['dept'] = array_reverse(array_column($dept, "dept_name"));
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
|
@ -108,7 +110,7 @@ class Account extends Model
|
|||
$model = $model->where('dept_id', $where['dept_id']);
|
||||
}
|
||||
$paginate = $model->orderByDesc("account_id")
|
||||
->paginate((int)$where['limit'], ['account_id', 'username', 'avatar', 'create_time', 'dept_id']);
|
||||
->paginate((int)$where['limit'], ['account_id', 'username', 'avatar', 'create_time', 'dept_id', 'email', 'phone', 'nickname']);
|
||||
$count = $paginate->total();
|
||||
$data = $paginate->items();
|
||||
foreach ($data as &$item) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
/**
|
||||
* @property int $mess_id
|
||||
* @property int $account_id
|
||||
* @property int $message_id
|
||||
* @property int $read_flag
|
||||
* @property string $read_time
|
||||
* @property string $deleted_at
|
||||
* @property string $create_time
|
||||
* @property string $update_time
|
||||
*/
|
||||
class AccountMessage extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'account_message';
|
||||
|
||||
protected string $primaryKey = 'mess_id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = ['mess_id' => 'integer', 'account_id' => 'integer', 'message_id' => 'integer', 'read_flag' => 'integer'];
|
||||
|
||||
public static function list(array $param)
|
||||
{
|
||||
return (new self())->setTable("am")
|
||||
->from("account_message as am")
|
||||
->leftJoin("message as m", "m.message_id", "=", "am.message_id")
|
||||
->where("m.recalled_flag", 0)
|
||||
->where("m.type", $param['type'])
|
||||
->where("am.account_id", $param['account_id'])
|
||||
->whereNull("m.deleted_at")
|
||||
->orderBy("am.read_flag")
|
||||
->orderByDesc("am.mess_id")
|
||||
->select(['am.mess_id', 'am.read_flag', 'am.create_time', 'm.title', 'm.extra'])
|
||||
->paginate($param['limit']);
|
||||
}
|
||||
|
||||
public static function read(array $param)
|
||||
{
|
||||
return (new self())->setTable("am")
|
||||
->from("account_message as am")
|
||||
->leftJoin("message as m", "m.message_id", "=", "am.message_id")
|
||||
->where("m.recalled_flag", 0)
|
||||
->whereNull("m.deleted_at")
|
||||
->where("am.account_id", $param['account_id'])
|
||||
->where("m.type", $param['type'])
|
||||
->where("am.read_flag", 0)
|
||||
->when(!empty($param['ids']), function ($query) use ($param) {
|
||||
$query->whereIn("am.mess_id", $param['ids']);
|
||||
})
|
||||
->update([
|
||||
'read_flag' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
public static function detail(array $param)
|
||||
{
|
||||
return (new self())->setTable("am")
|
||||
->from("account_message as am")
|
||||
->leftJoin("message as m", "m.message_id", "=", "am.message_id")
|
||||
->leftJoin("account as a", "a.account_id", "=", "m.create_id")
|
||||
->where("m.recalled_flag", 0)
|
||||
->whereNull("m.deleted_at")
|
||||
->where("am.account_id", $param['account_id'])
|
||||
->where("am.mess_id", $param['id'])
|
||||
->select(['am.mess_id', 'am.create_time', 'm.title', 'm.content', 'm.extra', 'm.type', 'a.nickname'])
|
||||
->first();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
/**
|
||||
* @property int $message_id
|
||||
* @property string $title
|
||||
* @property string $content
|
||||
* @property string $type
|
||||
* @property string $extra
|
||||
* @property int $recalled_flag
|
||||
* @property int $create_id
|
||||
* @property string $deleted_at
|
||||
* @property string $create_time
|
||||
* @property string $update_time
|
||||
*/
|
||||
class Message extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'message';
|
||||
|
||||
protected string $primaryKey = 'message_id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = ['message_id' => 'integer', 'recalled_flag' => 'integer', 'create_id' => 'integer'];
|
||||
|
||||
/**
|
||||
* 仅管理系统消息
|
||||
* @param array $param
|
||||
* @return \Hyperf\Contract\LengthAwarePaginatorInterface
|
||||
*/
|
||||
public static function list(array $param)
|
||||
{
|
||||
$model = self::query()->with(["account" => function ($query) {
|
||||
$query->select(['account_id', 'username']);
|
||||
}]);
|
||||
if (isset($param['title']) && $param['title'] != '') {
|
||||
$model = $model->where('title', "like", "%{$param['title']}%");
|
||||
}
|
||||
return $model->where("type", "system")
|
||||
->orderByDesc("message_id")
|
||||
->select(["message_id", "title", "content", "recalled_flag", "create_id", "create_time", "update_time"])
|
||||
->paginate((int)$param['limit']);
|
||||
}
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->hasOne(Account::class, 'account_id', 'create_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
/**
|
||||
* @property int $channel_id
|
||||
* @property int $account_id
|
||||
* @property int $message_id
|
||||
* @property string $channel
|
||||
* @property int $status
|
||||
* @property string $fail_reason
|
||||
* @property string $deleted_at
|
||||
* @property string $create_time
|
||||
* @property string $update_time
|
||||
*/
|
||||
class MessageChannel extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'message_channel';
|
||||
|
||||
protected string $primaryKey = 'channel_id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = ['channel_id' => 'integer', 'account_id' => 'integer', 'message_id' => 'integer', 'status' => 'integer'];
|
||||
}
|
||||
|
|
@ -4,9 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Model;
|
||||
|
||||
use App\Utils\Ip;
|
||||
use Swoole\Http\Request;
|
||||
|
||||
/**
|
||||
* @property int $online_id
|
||||
* @property string $session_id
|
||||
|
|
@ -92,4 +89,12 @@ class Online extends Model
|
|||
{
|
||||
return $this->hasOne(Account::class, 'account_id', 'account_id');
|
||||
}
|
||||
|
||||
public static function getFds($accountId): array
|
||||
{
|
||||
return self::where("status", 1)
|
||||
->where("account_id", $accountId)
|
||||
->pluck("fd")
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Request;
|
||||
|
||||
use Hyperf\Validation\Request\FormRequest;
|
||||
|
||||
class Message extends FormRequest
|
||||
{
|
||||
protected array $scenes = [
|
||||
'add' => ["title", "content"],
|
||||
'edit' => ["message_id", "title", "content"],
|
||||
];
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'message_id' => 'required',
|
||||
'title' => 'required',
|
||||
'content' => 'required',
|
||||
'type' => 'required',
|
||||
'extra' => 'required',
|
||||
'recalled_flag' => 'required',
|
||||
'create_id' => 'required',
|
||||
'deleted_at' => 'required',
|
||||
'create_time' => 'required',
|
||||
'update_time' => 'required'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'message_id.required' => 'ID必传!',
|
||||
'title.required' => '标题必传!',
|
||||
'content.required' => '内容必传!',
|
||||
'recalled_flag.required' => '是否撤回必传!'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service\Message\Channel;
|
||||
|
||||
use App\Model\Account;
|
||||
use App\Model\Message;
|
||||
use App\Service\Message\Exception\ChannelFailException;
|
||||
use App\Service\Message\Interface\ChannelInterface;
|
||||
use Symfony\Component\Mailer\Mailer;
|
||||
use Symfony\Component\Mailer\Transport;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Email;
|
||||
use function Hyperf\Config\config;
|
||||
|
||||
class EmailChannel implements ChannelInterface
|
||||
{
|
||||
protected Mailer $mailer;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = config('mailer.default');
|
||||
// 创建传输器
|
||||
$transport = Transport::fromDsn(sprintf(
|
||||
'smtp://%s:%s@%s:%d?encryption=%s',
|
||||
$config['username'],
|
||||
$config['password'],
|
||||
$config['host'],
|
||||
$config['port'],
|
||||
$config['encryption']
|
||||
));
|
||||
$this->mailer = new Mailer($transport);
|
||||
}
|
||||
|
||||
public function send(Account $account, Message $message): bool
|
||||
{
|
||||
try {
|
||||
if (!$account->email) {
|
||||
throw new ChannelFailException("用户邮箱地址不存在");
|
||||
}
|
||||
$email = (new Email())
|
||||
->from(new Address(config("mailer.default.username"), config("mailer.default.name")))
|
||||
->to($account->email)
|
||||
->subject($message->title)
|
||||
->html($message->content);
|
||||
$this->mailer->send($email);
|
||||
return true;
|
||||
} catch (\Exception $exception) {
|
||||
throw new ChannelFailException($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return "email";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service\Message\Channel;
|
||||
|
||||
use App\Model\Account;
|
||||
use App\Model\Message;
|
||||
use App\Model\Online as oModel;
|
||||
use App\Service\Message\Exception\ChannelFailException;
|
||||
use App\Service\Message\Interface\ChannelInterface;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\WebSocketServer\Sender;
|
||||
use function Hyperf\Coroutine\go;
|
||||
|
||||
class WebsocketChannel implements ChannelInterface
|
||||
{
|
||||
#[Inject]
|
||||
protected Sender $sender;
|
||||
|
||||
public function send(Account $account, Message $message): bool
|
||||
{
|
||||
try {
|
||||
$lines = oModel::getFds($account->account_id);
|
||||
foreach ($lines as $fd) {
|
||||
$this->sendOk($fd, $message->title, $message->type, $message->extra);
|
||||
}
|
||||
return true;
|
||||
} catch (\Exception $exception) {
|
||||
throw new ChannelFailException($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return "websocket";
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送信息
|
||||
* Author: cfn <cfn@leapy.cn>
|
||||
* @param int $fd
|
||||
* @param string $msg
|
||||
* @param string $type
|
||||
* @param string|null $extra
|
||||
* @return void
|
||||
*/
|
||||
public function sendOk(int $fd, string $msg, string $type = "msg", string $extra = null): void
|
||||
{
|
||||
$resp = [
|
||||
'code' => 200,
|
||||
'msg' => $msg,
|
||||
'type' => $type,
|
||||
'extra' => $extra
|
||||
];
|
||||
$this->sender->push($fd, json_encode($resp, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送信息
|
||||
* Author: cfn <cfn@leapy.cn>
|
||||
* @param int $fd
|
||||
* @param string $msg
|
||||
* @param string $type
|
||||
* @param string|null $extra
|
||||
* @return void
|
||||
*/
|
||||
public function sendErr(int $fd, string $msg, string $type = "msg", string $extra = null): void
|
||||
{
|
||||
$resp = [
|
||||
'code' => 400,
|
||||
'msg' => $msg,
|
||||
'type' => $type,
|
||||
'extra' => $extra
|
||||
];
|
||||
$this->sender->push($fd, json_encode($resp, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
* Author: cfn <cfn@leapy.cn>
|
||||
* @param int $fd
|
||||
* @return void
|
||||
*/
|
||||
public function close(int $fd): void
|
||||
{
|
||||
go(function () use ($fd) {
|
||||
sleep(1);
|
||||
$this->sender->disconnect($fd);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service\Message;
|
||||
|
||||
use App\Service\Message\Channel\EmailChannel;
|
||||
use App\Service\Message\Channel\WebsocketChannel;
|
||||
use App\Service\Message\Exception\ChannelNotFoundException;
|
||||
use App\Service\Message\Interface\ChannelInterface;
|
||||
use Hyperf\Di\Container;
|
||||
|
||||
class ChannelFactory
|
||||
{
|
||||
protected Container $container;
|
||||
|
||||
/**
|
||||
* 渠道映射表(可配置)
|
||||
*/
|
||||
protected array $channels = [
|
||||
'email' => EmailChannel::class,
|
||||
'websocket' => WebsocketChannel::class,
|
||||
];
|
||||
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取渠道实例
|
||||
*/
|
||||
public function get(string $name): ChannelInterface
|
||||
{
|
||||
if (!isset($this->channels[$name])) {
|
||||
throw new ChannelNotFoundException("Channel [$name] not found");
|
||||
}
|
||||
return $this->container->get($this->channels[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有可用渠道
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->channels as $class) {
|
||||
$list[] = $this->container->get($class);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service\Message\Exception;
|
||||
|
||||
class ChannelFailException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service\Message\Exception;
|
||||
|
||||
class ChannelNotFoundException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service\Message\Interface;
|
||||
|
||||
use App\Model\Account;
|
||||
use App\Model\Message;
|
||||
|
||||
interface ChannelInterface
|
||||
{
|
||||
public function send(Account $account, Message $message): bool;
|
||||
|
||||
public function getName(): string;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Hyperf\AsyncQueue\Driver\DriverFactory;
|
||||
use Hyperf\AsyncQueue\Driver\DriverInterface;
|
||||
|
||||
/**
|
||||
* 消息投递
|
||||
*/
|
||||
class QueueService
|
||||
{
|
||||
/**
|
||||
* @var DriverInterface
|
||||
*/
|
||||
protected DriverInterface $driver;
|
||||
|
||||
public function __construct(DriverFactory $driverFactory)
|
||||
{
|
||||
$this->driver = $driverFactory->get('default');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产消息.
|
||||
* @param array $params 数据
|
||||
* @param int $delay 延时时间 单位秒
|
||||
* @param string $queue_name
|
||||
* @return bool
|
||||
*/
|
||||
public function push(string $queue_name, array $params, int $delay = 0): bool
|
||||
{
|
||||
return $this->driver->push(new $queue_name($params), $delay);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
/**
|
||||
* Author: cfn <cfn@leapy.cn>
|
||||
*/
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use App\Service\QueueService;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
|
||||
/**
|
||||
* Author: cfn <cfn@leapy.cn>
|
||||
*/
|
||||
class QueueClient
|
||||
{
|
||||
#[Inject]
|
||||
protected QueueService $queueService;
|
||||
|
||||
/**
|
||||
* 静态调用
|
||||
* Author: cfn <cfn@leapy.cn>
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @return void
|
||||
*/
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
(new self())->$name(...$arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步队列推送
|
||||
* Author: cfn <cfn@leapy.cn>
|
||||
* @param string $queue_name
|
||||
* @param array $params
|
||||
* @param int $delay
|
||||
* @return void
|
||||
*/
|
||||
protected function push(string $queue_name, array $params, int $delay = 0): void
|
||||
{
|
||||
$this->queueService->push($queue_name, $params, $delay);
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
"hyperf/view": "^3.1",
|
||||
"hyperf/websocket-server": "^3.1",
|
||||
"phpoffice/phpspreadsheet": "^4.5",
|
||||
"symfony/mailer": "^7.4",
|
||||
"twig/twig": "^3.22",
|
||||
"zoujingli/ip2region": "^3.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'transport' => 'smtp',
|
||||
'host' => 'smtp.ym.163.com', // SMTP 服务器地址
|
||||
'port' => 465, // SMTP 端口
|
||||
'encryption' => 'ssl', // 加密方式,支持 ssl 或 tls
|
||||
'username' => 'mail@leapy.cn', // 邮箱地址
|
||||
'password' => 'e!@3j9wjri2', // SMTP 授权码
|
||||
'name' => '里派科技', // 发件人名称
|
||||
],
|
||||
];
|
||||
|
|
@ -11,4 +11,5 @@ declare(strict_types=1);
|
|||
*/
|
||||
return [
|
||||
App\Process\CrontabDispatcherProcess::class,
|
||||
Hyperf\AsyncQueue\Process\ConsumerProcess::class,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
use Hyperf\HttpMessage\Server\Response;
|
||||
use Hyperf\HttpServer\Router\Router;
|
||||
|
||||
Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController@index');
|
||||
Router::addRoute(['POST', 'HEAD'], '/', 'App\Controller\IndexController@index');
|
||||
|
||||
Router::get('/', function () {
|
||||
return (new Response())
|
||||
->withStatus(302)
|
||||
->withHeader('Location', '/super/');
|
||||
});
|
||||
|
||||
Router::get('/favicon.ico', function () {
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
|
||||
<pi-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
|
||||
<el-form :model="form" :rules="rules" :disabled="mode==='show'" ref="formRef" label-width="100px">
|
||||
{% for field in insert_fields %}
|
||||
{% if field.html_type == 'select' %}
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
<el-button @click="visible=false" >取 消</el-button>
|
||||
<el-button v-if="mode!=='show'" type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</pi-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
|
|
|||
Loading…
Reference in New Issue