FastMovieAI 二开基线 v1.0 (完整源码)

This commit is contained in:
李建琦
2026-08-26 18:41:02 +08:00
parent fad7690c0c
commit d54584f70d
888 changed files with 105571 additions and 1 deletions
@@ -0,0 +1,20 @@
<?php
namespace plugin\control\app\admin\controller;
use app\Basic;
use app\expose\build\builder\ComponentBuilder;
use app\expose\build\builder\FormBuilder;
use app\expose\enum\Filesystem;
use app\expose\enum\SubmitEvent;
use app\expose\trait\Config;
use support\Request;
class SettingsController extends Basic
{
use Config;
public function state(Request $request)
{
return $this->builder();
}
}
@@ -0,0 +1,426 @@
<?php
namespace plugin\control\app\admin\controller;
use app\Basic;
use app\expose\build\builder\ComponentBuilder;
use app\expose\build\builder\FormBuilder;
use app\expose\build\builder\TableBuilder;
use app\expose\enum\Action;
use app\expose\enum\State;
use plugin\control\app\model\PluginChannelsUser;
use support\Request;
class UserController extends Basic
{
public function __construct()
{
$this->model = new PluginChannelsUser;
}
public function indexGetTable(Request $request)
{
$builder = new TableBuilder;
$builder->addAction('操作', [
'width' => '100px',
'fixed' => 'right'
]);
$builder->addTableAction('编辑', [
'model' => Action::DIALOG['value'],
'path' => '/app/control/admin/User/update',
'props' => [
'title' => '编辑《ID{id}》渠道用户'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'primary',
'size' => 'small'
]
]
]);
$builder->addHeader();
$builder->addHeaderAction('创建渠道用户', [
'model' => Action::DIALOG['value'],
'path' => '/app/control/admin/User/create',
'props' => [
'title' => '创建渠道用户'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'success'
]
]
]);
$formBuilder = new FormBuilder(null, null, [
'inline' => true
]);
$formBuilder->add('nickname', '昵称', 'input', '', [
'props' => [
'placeholder' => '昵称搜索',
'clearable' => true
]
]);
$formBuilder->add('username', '账号', 'input', '', [
'props' => [
'placeholder' => '账号搜索',
'clearable' => true
]
]);
$formBuilder->add('mobile', '手机号', 'input', '', [
'props' => [
'placeholder' => '手机号搜索',
'clearable' => true
]
]);
$formBuilder->add('email', '邮箱', 'input', '', [
'props' => [
'placeholder' => '邮箱搜索',
'clearable' => true
]
]);
$formBuilder->add('uid', 'UID', 'input', '', [
'props' => [
'placeholder' => 'UID搜索',
'clearable' => true
]
]);
$builder->addScreen($formBuilder);
$builder->add('id', 'ID', [
'props' => [
'width' => '100px'
]
]);
$builder->add('userinfo', '渠道用户', [
'component' => [
'name' => 'table-userinfo',
'props' => [
'nickname' => 'nickname',
'avatar' => 'headimg',
'info' => 'username',
'nicknameTags' => [
[
'field' => 'new_text',
'props' => [
'type' => 'success',
'size' => 'small'
]
],
[
'field' => 'week_text',
'props' => [
'type' => 'warning',
'size' => 'small'
]
],
[
'field' => 'month_text',
'props' => [
'type' => 'info',
'size' => 'small'
]
]
]
]
],
'props' => [
'minWidth' => '300px'
]
]);
$builder->add('contact', '联系方式', [
'props' => [
'width' => '280px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'mobile',
'label' => '手机号'
],
[
'field' => 'email',
'label' => '邮箱'
]
]
]
]
]);
$builder->add('state', '状态', [
'component' => [
'name' => 'switch',
'api' => '/app/control/admin/User/indexUpdateState',
'props' => [
'active-value' => State::YES['value'],
'inactive-value' => State::NO['value']
]
],
'props' => [
'width' => '100px'
]
]);
$builder->add('online_time', '活动', [
'props' => [
'width' => '200px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'activation_time',
'label' => '激活'
],
[
'field' => 'login_time',
'label' => '登录'
],
[
'component' => 'tag',
'field' => 'login_ip',
'label' => '登录IP',
'props' => [
'size' => 'small',
]
]
]
]
]
]);
$builder->add('create_time', '时间', [
'props' => [
'width' => '200px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'create_time',
'label' => '创建'
],
[
'field' => 'update_time',
'label' => '更新'
]
]
]
]
]);
$builder = $builder->builder();
return $this->resData($builder);
}
public function query(Request $request)
{
$query = $request->post('query');
if (empty($query)) {
return $this->resData([]);
}
$where = [];
$where[] = ['nickname|username|mobile|email', 'like', "%{$query}%"];
return $this->resData(PluginChannelsUser::options($where));
}
public function index(Request $request)
{
$limit = $request->get('limit', 10);
$where = [];
$username = $request->get('username');
if ($username) {
$where[] = ['username', 'like', "%{$username}%"];
}
$mobile = $request->get('mobile');
if ($mobile) {
$where[] = ['mobile', 'like', "%{$mobile}%"];
}
$email = $request->get('email');
if ($email) {
$where[] = ['email', 'like', "%{$email}%"];
}
$puid = $request->get('puid');
if ($puid) {
$where[] = ['puid', '=', $puid];
}
$uid = $request->get('uid');
if ($uid) {
$where[] = ['id', '=', $uid];
}
$list = PluginChannelsUser::where($where)
->order('id desc')->paginate($limit)->each(function ($item) {
// 三天以内创建的
$create_time = strtotime($item->create_time);
$create_time = time() - $create_time;
if ($create_time < 3 * 24 * 60 * 60) {
$item->new_text = '新渠道用户';
} elseif ($create_time < 7 * 24 * 60 * 60) {
$item->week_text = '一周内';
} elseif ($create_time < 30 * 24 * 60 * 60) {
$item->month_text = '30天内';
}
});
return $this->resData($list);
}
public function create(Request $request)
{
if ($request->method() === 'POST') {
$data = $request->post();
try {
$insterData = [];
if (empty($data['username']) && empty($data['mobile'])) {
throw new \Exception('渠道用户名、手机号至少填写一项');
}
if (!empty($data['username'])) {
$Find = PluginChannelsUser::where(['username' => $data['username']])->find();
if ($Find) {
throw new \Exception('渠道用户名已存在');
}
$insterData['username'] = $data['username'];
}
if (!empty($data['mobile'])) {
$Find = PluginChannelsUser::where(['mobile' => $data['mobile']])->find();
if ($Find) {
throw new \Exception('手机号已存在');
}
$insterData['mobile'] = $data['mobile'];
}
if (!empty($data['email'])) {
$insterData['email'] = $data['email'];
}
if (!empty($data['password'])) {
$insterData['password'] = $data['password'];
}
if (!empty($data['activation_time'])) {
$insterData['activation_time'] = $data['activation_time'];
}
if (!empty($data['nickname'])) {
$insterData['nickname'] = $data['nickname'];
}
if (!empty($data['headimg'])) {
$insterData['headimg'] = $data['headimg'];
}
$model = new PluginChannelsUser();
$model->save($insterData);
} catch (\Throwable $th) {
return $this->exception($th);
}
return $this->success('创建成功');
}
$builder = $this->getFormBuilder();
$Component = new ComponentBuilder;
$builder->add('activation_time', '激活时间', 'date-picker', null, [
'prompt' => [
$Component->add('text', ['default' => '不选择时间则不激活,由渠道用户前台登录后自动激活'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => '选择激活时间',
'type' => 'datetime',
'format' => 'YYYY-MM-DD HH:mm:ss',
'value-format' => 'YYYY-MM-DD HH:mm:ss'
]
]);
return $this->resData($builder);
}
public function update(Request $request)
{
if ($request->method() === 'POST') {
$data = $request->post();
try {
$user = PluginChannelsUser::where(['id' => $data['id']])->find();
if (!$user) {
throw new \Exception('渠道用户不存在');
}
if (!empty($data['password'])) {
$user->password = $data['password'];
}
if (!empty($data['username'])) {
$Find = PluginChannelsUser::where(['username' => $data['username']])->find();
if ($Find && $Find->id != $data['id']) {
throw new \Exception('渠道用户名已存在');
}
$user->username = $data['username'];
}
if (!empty($data['mobile'])) {
$Find = PluginChannelsUser::where(['mobile' => $data['mobile']])->find();
if ($Find && $Find->id != $data['id']) {
throw new \Exception('手机号已存在');
}
$user->mobile = $data['mobile'];
}
if (!empty($data['email'])) {
$user->email = $data['email'];
}
if (!empty($data['nickname'])) {
$user->nickname = $data['nickname'];
}
if (!empty($data['headimg'])) {
$user->headimg = $data['headimg'];
}
$user->save();
} catch (\Throwable $th) {
return $this->exception($th);
}
return $this->success('更新成功');
}
$id = $request->get('id');
$User = PluginChannelsUser::where(['id' => $id])->withoutField('password')->find();
$builder = $this->getFormBuilder();
$builder->setData($User->toArray());
return $this->resData($builder);
}
private function getFormBuilder()
{
$builder = new FormBuilder(null, null, [
'labelPosition' => 'right',
'label-width' => "200px",
'class' => 'w-80 mx-auto',
'size' => 'large',
]);
$Component = new ComponentBuilder;
$builder->add('nickname', '昵称', 'input', '', [
'required' => true,
'maxlength' => 30,
'show-word-limit' => true
]);
$builder->add('username', '账号', 'input', '', [
'prompt' => [
$Component->add('text', ['default' => '支持字母、数字、下划线,长度不超过30个字符'], ['type' => 'info', 'size' => 'small'])->builder(),
$Component->add('text', ['default' => '可用于账号登录'], ['type' => 'success', 'size' => 'small'])->builder()
],
'props' => [
'maxlength' => 30,
'show-word-limit' => true
]
]);
$builder->add('mobile', '手机号', 'input', '', [
'prompt' => [
$Component->add('text', ['default' => '中国大陆11位手机号'], ['type' => 'info', 'size' => 'small'])->builder(),
$Component->add('text', ['default' => '可用于账号,短信验证码登录'], ['type' => 'success', 'size' => 'small'])->builder()
],
'props' => [
'maxlength' => 11,
'show-word-limit' => true
]
]);
$builder->add('email', '邮箱', 'input', '', [
'prompt' => [
$Component->add('text', ['default' => '可用于账号,邮箱验证码登录'], ['type' => 'success', 'size' => 'small'])->builder()
],
'props' => [
'maxlength' => 100,
'show-word-limit' => true
]
]);
$builder->add('password', '密码', 'input', '', [
'prompt' => [
$Component->add('text', ['default' => '不修改密码请留空'], ['type' => 'info', 'size' => 'small'])->builder()
],
'props' => [
'placeholder' => '不修改密码请留空',
'maxlength' => 30,
'show-word-limit' => true
]
]);
return $builder;
}
}
@@ -0,0 +1,113 @@
<?php
namespace plugin\control\app\api\controller;
use app\Basic;
use app\expose\helper\Captcha;
use app\expose\helper\Config;
use plugin\control\expose\helper\Vcode;
use plugin\shortplay\utils\enum\ActorAge;
use plugin\shortplay\utils\enum\ActorGender;
use plugin\shortplay\utils\enum\ActorSpeciesType;
use plugin\shortplay\utils\enum\ActorStatus;
use plugin\shortplay\utils\enum\StyleClassify;
use plugin\shortplay\utils\enum\VoiceEmotion;
use plugin\shortplay\utils\enum\VoiceLanguage;
use plugin\user\app\model\PluginUser;
use plugin\user\utils\enum\VcodeScene;
use support\Request;
class PublicController extends Basic
{
protected $notNeedLoginAll = true;
public function config(Request $request)
{
$config = new Config('basic', 'control');
$register = new Config('register', 'user', $request->channels_uid);
$config->enum = [
'actor_species_type' => ActorSpeciesType::getOptions(),
'actor_gender' => ActorGender::getOptions(),
'actor_age' => ActorAge::getOptions(),
'actor_status' => ActorStatus::getOptions(),
'style_classify' => StyleClassify::getOptions(),
'voice_emotion' => VoiceEmotion::getOptions(),
'voice_language' => VoiceLanguage::getOptions(),
];
$pushConfig = new Config('push', 'notification', 0);
if ($pushConfig->state) {
$config->push = [
'url' => $pushConfig->wss_url,
'app_key' => config('plugin.webman.push.app.app_key'),
'auth' => config('plugin.webman.push.app.auth'),
];
}
$config->register = $register->toArray();
return $this->resData($config);
}
public function getSmsVcode(Request $request)
{
$scene = $request->post('scene');
if (empty($scene)) {
return $this->fail('参数错误');
}
$token = $request->post('token');
if ($token) {
$request->sessionId($token);
}
try {
$captcha_state = 1;
if ($captcha_state) {
$captcha = $request->post('captcha');
if (empty($captcha)) {
return $this->fail('请输入验证码');
}
Captcha::check($captcha, $token);
}
$username = $request->post('username');
switch ($scene) {
case VcodeScene::LOGIN['value']:
$UserModel = PluginUser::where(['mobile' => $username])->find();
if (!$UserModel) {
throw new \Exception('手机号未注册');
}
Vcode::send($username, VcodeScene::LOGIN['value'], $token);
break;
case VcodeScene::SIGNUP['value']:
Vcode::send($username, VcodeScene::SIGNUP['value'], $token);
break;
case VcodeScene::BIND_MOBILE['value']:
if (!$request->uid) {
throw new \Exception('请先登录');
}
// $UserModel = PluginUser::where(['mobile' => $username])->find();
// if ($UserModel && $UserModel->id != $request->uid) {
// throw new \Exception('手机号已存在');
// }
Vcode::send($username, VcodeScene::BIND_MOBILE['value'], $token);
break;
case VcodeScene::BIND_EMAIL['value']:
if (!$request->uid) {
throw new \Exception('请先登录');
}
$UserModel = PluginUser::where(['email' => $username])->find();
if ($UserModel && $UserModel->id != $request->uid) {
throw new \Exception('邮箱已存在');
}
Vcode::send($username, VcodeScene::BIND_EMAIL['value'], $token);
break;
case VcodeScene::SET_PASSWORD['value']:
if (!$request->uid) {
throw new \Exception('请先登录');
}
$mobile = PluginUser::where(['id' => $request->uid])->value('mobile');
Vcode::send($mobile, VcodeScene::SET_PASSWORD['value'], $token);
break;
default:
throw new \Exception('场景错误');
}
} catch (\Throwable $th) {
return $this->fail($th->getMessage());
}
return $this->success();
}
}
@@ -0,0 +1,39 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\helper\Captcha;
use support\Request;
use support\Response;
/**
* 图像验证码
* Class CaptchaController
* @package app\api\controller
*/
class CaptchaController extends Basic
{
/**
* 不需要登录的方法
* @var string[]
*/
protected $notNeedLogin = [
'captcha', 'captchaCode'
];
/**
* 获取图像验证码
* @param Request $request
* @return Response
*/
public function captcha(Request $request)
{
return Captcha::captcha();
}
public function captchaCode()
{
return $this->resData( Captcha::captchaCode());
}
}
@@ -0,0 +1,289 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\builder\ComponentBuilder;
use app\expose\build\builder\FormBuilder;
use app\expose\build\builder\TableBuilder;
use app\expose\enum\Action;
use app\expose\enum\State;
use app\expose\utils\Rsa;
use app\expose\utils\Str;
use GuzzleHttp\Client;
use plugin\control\app\model\PluginChannelsDomain;
use support\Redis;
use support\Request;
class DomainController extends Basic
{
protected $notNeedLogin = ['downloadFile'];
public function __construct()
{
$this->model = new PluginChannelsDomain();
}
public function indexGetTable(Request $request)
{
$builder = new TableBuilder;
$builder->addAction('操作', [
'width' => '100px',
'fixed' => 'right'
]);
$builder->addTableAction('删除', [
'model' => Action::COMFIRM['value'],
'path' => '/app/control/control/Domain/delete',
'props' => [
'message' => '确定要删除《{domain}》域名吗?'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'danger',
'size' => 'small'
]
]
]);
$builder->addHeader();
$builder->addHeaderAction('绑定域名', [
'model' => Action::DIALOG['value'],
'path' => '/app/control/control/Domain/create',
'props' => [
'title' => '绑定域名'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'success'
]
]
]);
$formBuilder = new FormBuilder(null, null, [
'inline' => true
]);
$formBuilder->add('domain', '域名', 'input', '', [
'props' => [
'placeholder' => '域名搜索',
'clearable' => true
]
]);
$builder->addScreen($formBuilder);
$builder->add('id', 'ID', [
'props' => [
'width' => '100px'
]
]);
$builder->add('domain', '域名', [
'props' => [
'minWidth' => '200px'
]
]);
$builder->add('state', '状态', [
'component' => [
'name' => 'switch',
'api' => '/app/control/control/Domain/indexUpdateState',
'props' => [
'active-value' => State::YES['value'],
'inactive-value' => State::NO['value']
]
],
'props' => [
'width' => '100px'
]
]);
$builder->add('remarks', '备注', [
'props' => [
'width' => '300px'
]
]);
$builder->add('create_time', '时间', [
'props' => [
'width' => '200px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'create_time',
'label' => '创建'
],
[
'field' => 'update_time',
'label' => '更新'
]
]
]
]
]);
$builder = $builder->builder();
return $this->resData($builder);
}
public function index(Request $request)
{
$limit = $request->get('limit', 10);
$where = [];
$domain = $request->get('domain');
if ($domain) {
$where[] = ['domain', 'like', "%{$domain}%"];
}
$where[] = ['channels_uid', '=', $request->channels_uid];
$list = PluginChannelsDomain::where($where)
->order('id desc')->paginate($limit)->each(function ($item) {});
return $this->resData($list);
}
/**
* 更新状态字段
* @method POST
*/
public function indexUpdateState(Request $request)
{
$id = $request->post('id');
$field = $request->post('field');
$value = $request->post('value');
$model = $this->model->where(['id' => $id])->find();
if (!$model) {
return $this->fail('数据不存在');
}
$model->{$field} = $value;
if ($model->save()) {
if ($field == 'state' && $value == State::YES['value']) {
Redis::hSet('domain_map', $model->domain, $model->channels_uid);
} else if ($field == 'state' && $value == State::NO['value']) {
Redis::hDel('domain_map', $model->domain);
}
return $this->success();
}
return $this->fail('操作失败');
}
public function create(Request $request)
{
$file_name = md5('domain-verify-' . $request->channels_uid) . '.txt';
$file = runtime_path('temp/') . $file_name;
if ($request->method() === 'POST') {
$data = $request->post();
try {
$content = file_get_contents($file);
if ($data['verify_type'] === 'file') {
$remote_content = file_get_contents('http://' . $data['domain'] . '/' . $file_name);
if ($content !== $remote_content) {
throw new \Exception('验证失败');
}
} else {
// 获取域名解析记录
$this->verifyDomainRecord($file_name . '.' . $data['domain'], $content);
}
} catch (\Throwable $th) {
return $this->fail('验证失败');
}
try {
$insterData = [];
if (empty($data['domain'])) {
throw new \Exception('请填写域名');
}
if (!empty($data['domain'])) {
$Find = PluginChannelsDomain::where(['domain' => $data['domain']])->find();
if ($Find) {
throw new \Exception('域名已存在');
}
$insterData['domain'] = $data['domain'];
}
if (!empty($data['remarks'])) {
$insterData['remarks'] = $data['remarks'];
}
$insterData['channels_uid'] = $request->channels_uid;
$insterData['state'] = State::YES['value'];
$model = new PluginChannelsDomain();
$model->save($insterData);
Redis::hSet('domain_map', $model->domain, $model->channels_uid);
} catch (\Throwable $th) {
return $this->exception($th);
}
return $this->success('创建成功');
}
$builder = new FormBuilder(null, null, [
'labelPosition' => 'right',
'label-width' => "200px",
'class' => 'w-80 mx-auto',
'size' => 'large',
]);
$Component = new ComponentBuilder;
$builder->add('verify_type', '验证方式', 'radio', 'file', [
'required' => true,
'options' => [
[
'label' => '文件',
'value' => 'file'
],
[
'label' => 'DNS',
'value' => 'dns'
]
],
'subProps' => [
'border' => true
]
]);
if (!is_dir(runtime_path('temp'))) {
mkdir(runtime_path('temp'), 0755, true);
}
if (!file_exists($file)) {
$content = Str::random(32);
file_put_contents($file, $content);
} else {
$content = file_get_contents($file);
}
$builder->add('domain', '域名(HOST)', 'input', '', [
'required' => true,
'prompt' => [
$Component->add('link', ['default' => '点击下载验证文件'], ['href' => '/app/control/control/Domain/downloadFile?file_name=' . $file_name, 'type' => 'primary', 'size' => 'small', 'target' => '_blank', 'underline' => 'never'])
->add('text', ['default' => "文件验证:将文件 {$file_name} 放置域名根目录下,确保域名+文件名能访问到文件内容:{$content}"], ['type' => 'info', 'size' => 'small'])
->add('text', ['default' => "DNS验证:添加一条域名解析记录,记录类型为TXT,记录值为:{$content}"], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => '请输入不带协议的域名(HOST),如:www.example.com',
'clearable' => true
]
]);
$builder->add('remarks', '备注', 'input', '', [
'props' => [
'clearable' => true
]
]);
return $this->resData($builder);
}
public function downloadFile(Request $request)
{
$file_name = $request->get('file_name');
$file = runtime_path('temp/') . $file_name;
if (file_exists($file)) {
return response()->download($file, $file_name);
}
return $this->fail('文件不存在');
}
public function verifyDomainRecord($domain, $content)
{
$res = dns_get_record($domain, DNS_ALL);
foreach ($res as $key => $value) {
if ($value['type'] === 'TXT') {
if ($value['txt'] === $content) {
return true;
}
}
}
throw new \Exception('验证失败');
}
public function delete(Request $request)
{
$id = $request->post('id');
$model = $this->model->where(['id' => $id, 'channels_uid' => $request->channels_uid])->find();
if (!$model) {
return $this->fail('数据不存在');
}
if (!$model->delete()) {
return $this->fail('删除失败');
}
Redis::hDel('domain_map', $model->domain);
return $this->success('删除成功');
}
}
@@ -0,0 +1,216 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\builder\DataboardBuilder;
use app\expose\build\builder\databoardBuilder\component\Echarts;
use app\expose\build\builder\databoardBuilder\component\Statistic;
use app\expose\helper\Config;
use plugin\finance\app\model\PluginFinanceOrders;
use plugin\finance\utils\enum\OrdersState;
use plugin\shortplay\app\model\PluginShortplayDrama;
use plugin\user\app\model\PluginUser;
use support\Cache;
use support\Request;
class IndexController extends Basic
{
/**
* 不需要登录的方法
* @var string[]
*/
protected $notNeedLogin = ['index'];
protected $notNeedAuth = ['index'];
public function index(Request $request)
{
$path = $request->path();
# 如果不是以/结尾的,就重定向到以/结尾的URL
if (substr($path, -1) != '/') {
return redirect($path . '/');
}
return view(public_path('index.html'));
}
public function control(Request $request)
{
$builder = new DataboardBuilder([
'gutter' => 6
]);
$today = PluginUser::where(['channels_uid' => $request->channels_uid])->whereDay('create_time')->count();
$yesterday = PluginUser::where(['channels_uid' => $request->channels_uid])->whereDay('create_time', 'yesterday')->count();
$statistic = new Statistic;
$statistic->setLabel('今日新注册用户')
->setUnit('人')
->setData([
'today' => $today,
'yesterday' => $yesterday,
'growth_rate' => $yesterday ? round(($today - $yesterday) / $yesterday * 100, 2) : 0
])
->setClass('p-6');
$builder->add($statistic, [
'xs' => 24,
'sm' => 12,
'md' => 6,
'lg' => 4,
'class' => 'bg-white p-4 rounded-4 shadow-lighter'
]);
$today = PluginFinanceOrders::where(['channels_uid' => $request->channels_uid])->whereIn('state', [OrdersState::PAID['value'], OrdersState::FINISH['value']])->whereDay('create_time')->count();
$yesterday = PluginFinanceOrders::where(['channels_uid' => $request->channels_uid])->whereIn('state', [OrdersState::PAID['value'], OrdersState::FINISH['value']])->whereDay('create_time', 'yesterday')->count();
$statistic = new Statistic;
$statistic->setLabel('今日支付订单数')
->setUnit('笔')
->setData([
'today' => $today,
'yesterday' => $yesterday,
'growth_rate' => $yesterday ? round(($today - $yesterday) / $yesterday * 100, 2) : 0
])
->setClass('p-6');
$builder->add($statistic, [
'xs' => 24,
'sm' => 12,
'md' => 6,
'lg' => 4,
'class' => 'bg-white p-4 rounded-4 shadow-lighter'
]);
$today = PluginFinanceOrders::where(['channels_uid' => $request->channels_uid])->whereIn('state', [OrdersState::PAID['value'], OrdersState::FINISH['value']])->whereDay('create_time')->sum('money');
$yesterday = PluginFinanceOrders::where(['channels_uid' => $request->channels_uid])->whereIn('state', [OrdersState::PAID['value'], OrdersState::FINISH['value']])->whereDay('create_time', 'yesterday')->sum('money');
$statistic = new Statistic;
$statistic->setLabel('今日支付金额')
->setUnit('元')
->setData([
'today' => $today,
'yesterday' => $yesterday,
'growth_rate' => $yesterday ? round(($today - $yesterday) / $yesterday * 100, 2) : 0
])
->setClass('p-6');
$builder->add($statistic, [
'xs' => 24,
'sm' => 12,
'md' => 6,
'lg' => 4,
'class' => 'bg-white p-4 rounded-4 shadow-lighter'
]);
$today = PluginShortplayDrama::where(['channels_uid' => $request->channels_uid])->whereDay('create_time')->count();
$yesterday = PluginShortplayDrama::where(['channels_uid' => $request->channels_uid])->whereDay('create_time', 'yesterday')->count();
$statistic = new Statistic;
$statistic->setLabel('今日短剧数')
->setUnit('部')
->setData([
'today' => $today,
'yesterday' => $yesterday,
'growth_rate' => $yesterday ? round(($today - $yesterday) / $yesterday * 100, 2) : 0
])
->setClass('p-6');
$builder->add($statistic, [
'xs' => 24,
'sm' => 12,
'md' => 6,
'lg' => 4,
'class' => 'bg-white p-4 rounded-4 shadow-lighter'
]);
$this->echarts($builder);
return $this->resData($builder);
}
private function echarts(DataboardBuilder $builder)
{
$endTime = strtotime('23:59:59', time()) - time();
$request = request();
$echarts = new Echarts;
$echarts->setClass('bg-white p-4 rounded-4 shadow-lighter vh-65');
$EchartsData = Cache::get('control_echarts_data_' . $request->channels_uid);
if (!$EchartsData) {
$EchartsData = $this->getEchartsData($request);
Cache::set('control_echarts_data_' . $request->channels_uid, $EchartsData, $endTime);
}
$echarts->setData($EchartsData);
$builder->add($echarts);
}
private function getEchartsData(Request $request)
{
$data = [
'color' => ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'],
'title' => [
'text' => '数据可视化'
],
'tooltip' => [
'trigger' => 'axis',
'axisPointer' => [
'type' => 'cross',
'label' => [
'backgroundColor' => '#6a7985'
]
]
],
'legend' => [
'data' => ["用户", "订单数", "支付金额"]
],
'toolbox' => [
'show' => false
],
'grid' => [
'left' => '3%',
'right' => '4%',
'bottom' => '3%',
'containLabel' => true
],
'xAxis' => [
[
'type' => 'category',
'boundaryGap' => false,
'data' => []
]
],
'yAxis' => [
[
'type' => 'value'
]
],
'series' => [
[
'name' => "用户",
'type' => 'line',
'smooth' => true,
'data' => []
],
[
'name' => "订单数",
'type' => 'line',
'smooth' => true,
'data' => []
],
[
'name' => "支付金额",
'type' => 'line',
'smooth' => true,
'data' => []
]
]
];
for ($i = 7; $i > 0; $i--) {
$date = date('Y-m-d', strtotime("-$i day"));
$data['xAxis'][0]['data'][] = $date;
$Statistic = PluginUser::where(['channels_uid' => $request->channels_uid])->whereDay('create_time', $date)->count();
if ($Statistic) {
$data['series'][0]['data'][] = $Statistic;
} else {
$data['series'][0]['data'][] = 0;
}
$Order = PluginFinanceOrders::where(['channels_uid' => $request->channels_uid])->whereIn('state', [OrdersState::PAID['value'], OrdersState::FINISH['value']])->whereDay('create_time', $date)->count();
if ($Order) {
$data['series'][1]['data'][] = $Order;
} else {
$data['series'][1]['data'][] = 0;
}
$Payment = PluginFinanceOrders::where(['channels_uid' => $request->channels_uid])->whereIn('state', [OrdersState::PAID['value'], OrdersState::FINISH['value']])->whereDay('create_time', $date)->sum('money');
if ($Payment) {
$data['series'][2]['data'][] = $Payment;
} else {
$data['series'][2]['data'][] = 0;
}
}
return $data;
}
}
@@ -0,0 +1,157 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\enum\EventName;
use app\expose\utils\Password;
use app\expose\enum\ResponseCode;
use app\expose\enum\State;
use app\expose\helper\Captcha;
use app\expose\helper\Config;
use app\expose\helper\Vcode;
use app\expose\helper\Wechat;
use plugin\control\app\model\PluginChannelsUser;
use Exception;
use plugin\user\expose\helper\User as HelperUser;
use support\Redis;
use support\Request;
use Webman\Event\Event;
class LoginController extends Basic
{
/**
* 不需要登录的方法
* @var string[]
*/
protected $notNeedLogin = ['login', 'vcode', 'qrcode', 'register', 'checkQrcode'];
protected $notNeedAuth = ['login', 'vcode', 'qrcode', 'register', 'checkQrcode'];
public function login(Request $request)
{
try {
$D = $request->post();
$captcha_state = Config::get('captcha', 'state');
if ($captcha_state) {
if (!Captcha::check($D['captcha'], $D['token'])) {
throw new Exception("验证码不正确", ResponseCode::CAPTCHA);
}
}
$where = [];
$where[] = ['mobile|username', '=', $D['username']];
$User = PluginChannelsUser::where($where)->find();
if (!$User) {
throw new Exception('用户不存在');
}
if (!$User->password) {
throw new Exception('当前用户不支持密码登录,请使用验证码登录');
}
if (!password_verify($D['password'], $User->password)) {
throw new Exception('密码错误');
}
if (!$User->state) {
throw new Exception('用户已被禁用');
}
if (!$User->activation_time) {
$User->activation_time = date('Y-m-d H:i:s');
}
$User->login_ip = $request->getRealIp(true);
$User->login_time = date('Y-m-d H:i:s');
if ($User->save()) {
Event::emit(EventName::USER_LOGIN['value'], $User);
return $this->success('登录成功', PluginChannelsUser::getTokenInfo($User));
} else {
throw new Exception("登录失败");
}
} catch (\Throwable $th) {
return $this->exception($th);
}
}
public function vcode(Request $request)
{
$vcode = $request->post('vcode');
$username = $request->post('username');
$token = $request->post('token');
if (!Vcode::check($username, $vcode, 'login', $token)) {
return $this->fail('验证码不正确');
}
$User = PluginChannelsUser::where(['mobile' => $username])->find();
if (empty($User)) {
try {
HelperUser::register([
'mobile' => $username
]);
$User = PluginChannelsUser::where(['mobile' => $username])->find();
} catch (\Throwable $th) {
return $this->exception($th);
}
}
if ($User->state != State::YES['value']) {
return $this->fail('用户已被禁用');
}
if (!$User->activation_time) {
$User->activation_time = date('Y-m-d H:i:s');
}
$User->login_ip = $request->getRealIp(true);
$User->login_time = date('Y-m-d H:i:s');
if ($User->save()) {
Event::emit(EventName::USER_LOGIN['value'], $User);
return $this->success('登录成功', PluginChannelsUser::getTokenInfo($User));
} else {
throw new Exception("登录失败");
}
}
public function register(Request $request)
{
$vcode = $request->post('vcode');
$username = $request->post('username');
$token = $request->post('token');
if (!Vcode::check($username, $vcode, 'register', $token)) {
return $this->fail('验证码不正确');
}
$password = $request->post('password');
try {
HelperUser::register([
'mobile' => $username,
'password' => $password
]);
return $this->success('注册成功');
} catch (\Throwable $th) {
return $this->exception($th);
}
}
public function qrcode(Request $request)
{
try {
$expire = 5 * 60;
$params = [];
if ($request->icode) {
$params['puid'] = HelperUser::getUidByIcode($request->icode);
}
$res = Wechat::createQrCode(Wechat::QR_STR_SCENE, $expire, [\plugin\control\event\WechatOfficialAccount::class, 'login', $params]);
} catch (\Throwable $th) {
return $this->exception($th);
}
$data = [
'id' => $res['id'],
'qrcode' => $res['url'],
'expire' => $res['expire_seconds']
];
return $this->resData($data);
}
public function checkQrcode(Request $request)
{
$id = $request->post('id');
$uid = Redis::get($id . '_callback');
if ($uid) {
$User = PluginChannelsUser::where('id', $uid)->find();
if ($User) {
return $this->success('登录成功', PluginChannelsUser::getTokenInfo($User));
} else {
return $this->fail('登录失败');
}
}
return $this->code(ResponseCode::WAIT);
}
}
@@ -0,0 +1,190 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\builder\TableBuilder;
use app\expose\enum\PaymentChannels;
use app\expose\enum\Platform;
use app\expose\enum\State;
use app\model\PaymentConfig;
use app\model\PaymentTemplate;
use support\Request;
class PaymentController extends Basic
{
public function __construct()
{
$this->model = new PaymentConfig;
}
public function index(Request $request)
{
$tabs = [];
$PaymentConfig = PaymentConfig::where(['channels_uid' => $request->channels_uid])->select();
$Platform = Platform::getOptions(function($value){
return $value['value'] === Platform::PC['value'];
});
foreach ($Platform as $key => $item) {
$channels = [];
switch ($item['value']) {
case Platform::PC['value']:
$channels = [
PaymentChannels::WXPAY,
// PaymentChannels::ALIPAY,
// PaymentChannels::BALANCE,
// PaymentChannels::INTEGRAL
];
break;
case Platform::H5['value']:
$channels = [
PaymentChannels::WXPAY,
PaymentChannels::ALIPAY,
PaymentChannels::BALANCE,
PaymentChannels::INTEGRAL
];
break;
case Platform::WECHAT_MINIAPP['value']:
$channels = [
PaymentChannels::WXPAY,
PaymentChannels::BALANCE,
PaymentChannels::INTEGRAL
];
break;
case Platform::WECHAT_OFFICIAL_ACCOUNT['value']:
$channels = [
PaymentChannels::WXPAY,
PaymentChannels::BALANCE,
PaymentChannels::INTEGRAL
];
break;
case Platform::APP['value']:
$channels = [
PaymentChannels::WXPAY,
PaymentChannels::ALIPAY,
PaymentChannels::BALANCE,
PaymentChannels::INTEGRAL
];
break;
}
$tabs[] = $this->getFormBuilder($PaymentConfig, $item, $channels, $request);
}
return $this->resData($tabs);
}
private function getFormBuilder($PaymentConfig, $Platform, $channels, $request)
{
$builder = new TableBuilder([
'border' => false,
'stripe' => false
]);
$builder->add('channels', '支付方式', [
'component' => [
'name' => 'table-userinfo',
'props' => [
'nickname' => 'channels_text',
'avatar' => [
'field' => 'channels_icon',
'size' => 40
]
]
],
'props' => []
]);
$builder->add('template_id', '支付模板', [
'component' => [
'name' => 'select',
'api' => '/app/control/control/Payment/indexUpdateField',
'options' => PaymentTemplate::options(['channels_uid' => $request->channels_uid]),
'props' => [
'clearable' => true
]
],
'props' => [
'width' => '300px'
]
]);
$builder->add('state', '是否启用', [
'component' => [
'name' => 'switch',
'api' => '/app/control/control/Payment/indexUpdateState',
'props' => [
'active-value' => State::YES['value'],
'inactive-value' => State::NO['value']
]
],
'props' => [
'width' => '160px'
]
]);
$builder->add('is_default', '是否为默认支付', [
'component' => [
'name' => 'switch',
'api' => '/app/control/control/Payment/indexUpdateState',
'props' => [
'active-value' => State::YES['value'],
'inactive-value' => State::NO['value']
]
],
'props' => [
'width' => '160px'
]
]);
$tableData = [];
$exist = [];
foreach ($PaymentConfig as $item) {
if ($item->platform != $Platform['value']) continue;
$temp = $item->toArray();
$PaymentChannels = PaymentChannels::get($item->channels);
if ($PaymentChannels) {
$temp['channels_text'] = $PaymentChannels['label'];
$temp['channels_icon'] = $PaymentChannels['icon'];
}
$exist[] = $item->channels;
$tableData[] = $temp;
}
foreach ($channels as $key => $channel) {
if (!in_array($channel['value'], $exist)) {
$model = new PaymentConfig;
$model->channels_uid = $request->channels_uid;
$model->platform = $Platform['value'];
$model->channels = $channel['value'];
$model->state = State::NO['value'];
$model->save();
$tableData[] = [
'id' => $model->id,
'channels' => $channel['value'],
'channels_text' => $channel['label'],
'channels_icon' => $channel['icon'],
'template_id' => null,
'state' => State::NO['value'],
'is_default' => State::NO['value']
];
}
}
$data = [
'title' => $Platform['label'],
'name' => $Platform['value'],
'tips' => '在' . $Platform['label'] . '端付款时使用',
'builder' => $builder,
'data' => $tableData
];
return $data;
}
public function indexUpdateState(Request $request)
{
$id = $request->post('id');
$field = $request->post('field');
$value = $request->post('value');
$PaymentConfig = PaymentConfig::where(['id' => $id, 'channels_uid' => $request->channels_uid])->find();
if (!$PaymentConfig) {
return $this->fail('数据不存在');
}
if ($field == 'is_default' && $value == State::YES['value']) {
$PaymentConfig->where(['platform' => $PaymentConfig->platform])->update(['is_default' => State::NO['value']]);
}
$PaymentConfig->{$field} = $value;
if ($PaymentConfig->save()) {
return $this->success();
}
return $this->fail('操作失败');
}
}
@@ -0,0 +1,645 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\builder\ComponentBuilder;
use app\expose\build\builder\FormBuilder;
use app\expose\build\builder\TableBuilder;
use app\expose\enum\Action;
use app\expose\enum\AlipayRsaModel;
use app\expose\enum\PaymentChannels;
use app\expose\enum\State;
use app\expose\enum\WxpayMchType;
use app\expose\enum\WxpayVersion;
use app\model\PaymentTemplate;
use support\Request;
class PaymentTemplateController extends Basic
{
public function indexGetTable(Request $request)
{
$builder = new TableBuilder;
$builder->addAction('操作', [
'width' => '200px',
'fixed' => 'right'
]);
$builder->addTableAction('编辑', [
'path' => '/app/control/control/PaymentTemplate/update',
'props' => [
'type' => 'primary',
'title' => '编辑《{title}》支付模板'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'primary',
'size' => 'small'
]
]
]);
$builder->addTableAction('删除', [
'model' => Action::COMFIRM['value'],
'path' => '/app/control/control/PaymentTemplate/delete',
'props' => [
'type' => 'error',
'message' => '确定要删除《{title}》支付模板吗?',
'confirmButtonClass' => 'el-button--danger'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'danger',
'size' => 'small'
]
]
]);
$builder->addHeader();
$builder->addHeaderAction('新增模板', [
'path' => '/app/control/control/PaymentTemplate/create',
'props' => [
'type' => 'success',
'title' => '新增支付模板'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'success',
'icon' => 'Plus'
]
]
]);
$builder->add('id', 'ID', [
'props' => [
'width' => '80px'
]
]);
$builder->add('title', '模板名称');
$builder->add('channels', '支付方式', [
'component' => [
'name' => 'tag',
'options' => PaymentChannels::getOptions()
],
'props' => [
'width' => '160px'
]
]);
$builder->add('state', '状态', [
'component' => [
'name' => 'switch',
'api' => '/app/control/control/PaymentTemplate/indexUpdateState',
'props' => [
'active-value' => State::YES['value'],
'inactive-value' => State::NO['value']
]
],
'props' => [
'width' => '100px'
]
]);
$builder->add('create_time', '时间', [
'props' => [
'width' => '200px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'create_time',
'label' => '创建'
],
[
'field' => 'update_time',
'label' => '更新'
]
]
]
]
]);
return $this->resData($builder);
}
public function index(Request $request)
{
$limit = $request->get('limit', 10);
$where = [];
$where[] = ['channels_uid', '=', $request->channels_uid];
$list = PaymentTemplate::where($where)->withoutField('value')
->order('id desc')->paginate($limit)->each(function ($item) {});
return $this->resData($list);
}
public function create(Request $request)
{
if ($request->method() === 'POST') {
$D = $request->post();
$model = new PaymentTemplate;
$model->channels_uid = $request->channels_uid;
$model->title = $D['title'];
unset($D['title']);
$model->channels = $D['channels'];
unset($D['channels']);
$D['notify_url'] = 'https://' . $request->host();
$model->state = State::YES['value'];
$model->value = $this->getValue($model->channels, $D);
if ($model->save()) {
return $this->success('新增成功');
}
return $this->fail('新增失败');
}
$builder = $this->getFormBuilder();
return $this->resData($builder);
}
public function update(Request $request)
{
if ($request->method() === 'POST') {
$D = $request->post();
$model = PaymentTemplate::where(['id' => $D['id'], 'channels_uid' => $request->channels_uid])->find();
if (!$model) {
return $this->fail('数据不存在');
}
unset($D['id']);
$model->title = $D['title'];
unset($D['title']);
$model->channels = $D['channels'];
unset($D['channels']);
$D['notify_url'] = 'https://' . $request->host();
$model->state = State::YES['value'];
$model->value = $this->getValue($model->channels, $D);
if ($model->save()) {
return $this->success('更新成功');
}
return $this->fail('更新失败');
}
$id = $request->get('id');
$model = PaymentTemplate::where(['id' => $id, 'channels_uid' => $request->channels_uid])->find();
if (!$model) {
return $this->fail('数据不存在');
}
$builder = $this->getFormBuilder(true);
$data = [
'id' => $model->id,
'title' => $model->title,
'channels' => $model->channels
];
foreach ($model->value as $key => $value) {
$data[$key] = $value;
}
$builder->setData($data);
return $this->resData($builder);
}
private function getValue($channels, $D)
{
$value = [];
switch ($channels) {
case PaymentChannels::WXPAY['value']:
$value = [
'mch_type' => $D['mch_type'],
'appid' => $D['appid'],
'mch_id' => $D['mch_id'],
'mch_key' => $D['mch_key'],
'ssl_cert' => $D['ssl_cert'],
'ssl_key' => $D['ssl_key'],
'notify_url' => $D['notify_url'],
];
break;
// case PaymentChannels::ALIPAY['value']:
// $value = [
// 'appid' => $D['appid'],
// 'ras_model' => $D['ras_model'],
// 'app_public_cert' => $D['app_public_cert'],
// 'alipay_public_cert' => $D['alipay_public_cert'],
// 'alipay_root_cert' => $D['alipay_root_cert'],
// 'alipay_public_key' => $D['alipay_public_key'],
// 'private_key' => $D['private_key'],
// 'notify_url' => $D['notify_url'],
// ];
// break;
// case PaymentChannels::INTEGRAL['value']:
// $value = [
// 'display_name' => $D['display_name'],
// 'proportion' => $D['proportion'],
// 'is_integer' => $D['is_integer'],
// 'integer' => $D['integer']
// ];
// break;
}
return $value;
}
private function getFormBuilder($update = false)
{
$builder = new FormBuilder(null, '', [
'labelWidth' => '300px',
'labelPosition' => 'right'
]);
$Component = new ComponentBuilder;
$builder->add('title', '模板名称', 'input', null, [
'required' => true,
'prompt' => [
$Component->add('text', ['default' => '仅用于后台管理使用,对前台用户不可见;例如:H5-支付宝支付;微信小程序-微信支付'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入模板名称'
]
]);
$builder->add('channels', '支付方式', 'radio', PaymentChannels::WXPAY['value'], [
'required' => true,
'prompt' => [
$Component->add('text', ['default' => '保存以后支付方式将不可修改,请谨慎操作,'], ['type' => 'info', 'size' => 'small'])
->add('link', ['default' => '微信商户平台'], ['type' => 'success', 'href' => 'https://pay.weixin.qq.com', 'target' => '_blank', 'underline' => 'never', 'size' => 'small'])
->add('text', ['default' => ''], ['type' => 'info', 'size' => 'small'])
->add('link', ['default' => '支付宝开放平台'], ['type' => 'primary', 'href' => 'https://alipay.com', 'target' => '_blank', 'underline' => 'never', 'size' => 'small'])
->builder()
],
'options' => PaymentChannels::getOptions(function ($item) {
return $item['value'] === PaymentChannels::WXPAY['value'];
}),
'props' => [
'disabled' => $update
],
'subProps' => [
'border' => true
]
]);
# 微信支付开始
/* $builder->add('api_version', '微信支付接口版本', 'radio', WxpayVersion::V3['value'], [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']]
],
'options' => WxpayVersion::getOptions(),
'subProps' => [
'border' => true
]
]); */
$builder->add('mch_type', '微信商户号类型', 'radio', WxpayMchType::NORMAL['value'], [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']]
],
'options' => WxpayMchType::getOptions(function ($item) {
return $item['value'] === WxpayMchType::NORMAL['value'];
}),
'subProps' => [
'border' => true
]
]);
$builder->add('appid', '应用ID (AppID)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::NORMAL['value']]
],
'prompt' => [
$Component->add('text', ['default' => '微信小程序或者微信公众号的APPID,需要在哪个客户端支付就填写哪个,APP支付需要填写开放平台的应用APPID'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入应用ID (AppID)'
]
]);
$builder->add('mch_id', '微信商户号 (MchId)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::NORMAL['value']]
],
'prompt' => [
$Component->add('text', ['default' => '微信支付的商户号,纯数字格式;例如:1600000109'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入微信商户号 (MchId)'
]
]);
$builder->add('mch_key', '支付密钥 (APIKEY)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::NORMAL['value']]
],
'prompt' => [
$Component->add('text', ['default' => '微信支付商户平台 - 账户中心 - API安全 - 设置APIv3密钥'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入支付密钥 (APIKEY)'
]
]);
$builder->add('ssl_cert', '证书文件 (CERT)', 'bundle', '', [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::NORMAL['value']]
],
'props' => [
'view' => 'file',
'multiple' => 1
]
]);
$builder->add('ssl_key', '证书文件 (KEY)', 'bundle', '', [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::NORMAL['value']]
],
'props' => [
'view' => 'file',
'multiple' => 1
]
]);
# 微信支付结束
# 微信支付服务商模式开始
$builder->add('service_appid', '服务商应用ID (AppID)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::SERVICE['value']]
],
'prompt' => [
$Component->add('text', ['default' => '请填写微信支付服务商的AppID'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入服务商应用ID (AppID)'
]
]);
$builder->add('service_mch_id', '服务商户号 (MchId)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::SERVICE['value']]
],
'prompt' => [
$Component->add('text', ['default' => '微信支付服务商的商户号,纯数字格式;例如:1600000109'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入服务商户号 (MchId)'
]
]);
$builder->add('service_mch_key', '服务商密钥 (APIKEY)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::SERVICE['value']]
],
'prompt' => [
$Component->add('text', ['default' => '微信支付商户平台 - 账户中心 - API安全 - 设置API密钥'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入服务商密钥 (APIKEY)'
]
]);
$builder->add('appid', '子商户应用ID (AppID)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::SERVICE['value']]
],
'prompt' => [
$Component->add('text', ['default' => '微信小程序或者微信公众号的APPID,需要在哪个客户端支付就填写哪个,APP支付需要填写开放平台的应用APPID'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入子商户应用ID (AppID)'
]
]);
$builder->add('mch_id', '子商户号 (MchId)', 'input', null, [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::SERVICE['value']]
],
'prompt' => [
$Component->add('text', ['default' => '微信支付的商户号,纯数字格式;例如:1600000109'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '请输入子商户号 (MchId)'
]
]);
$builder->add('service_ssl_cert', '服务商证书文件 (CERT)', 'bundle', '', [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::SERVICE['value']]
],
'props' => [
'view' => 'file',
'multiple' => 1
]
]);
$builder->add('service_ssl_key', '服务商证书文件 (KEY)', 'bundle', '', [
'required' => true,
'where' => [
['channels', '=', PaymentChannels::WXPAY['value']],
['mch_type', '=', WxpayMchType::SERVICE['value']]
],
'props' => [
'accept' => 'application/octet-stream',
'view' => 'file',
'multiple' => 1
]
]);
# 微信支付服务商模式结束
# 支付宝支付开始
// $builder->add('appid', '支付宝应用 (AppID)', 'input', null, [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::ALIPAY['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '支付宝分配给开发者的应用ID;例如:2021072300007148'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'type' => 'text',
// 'placeholder' => '请输入支付宝应用 (AppID)'
// ]
// ]);
// $builder->add('ras_model', '加签模式', 'radio', AlipayRsaModel::PUBLIC_CERT['value'], [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::ALIPAY['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '如需使用资金支出类的接口,则必须使用公钥证书模式'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'options' => AlipayRsaModel::getOptions(),
// 'subProps' => [
// 'border' => true
// ]
// ]);
// $builder->add('app_public_cert', '应用公钥证书', 'bundle', '', [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::ALIPAY['value']],
// ['ras_model', '=', AlipayRsaModel::PUBLIC_CERT['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '请上传 "appCertPublicKey_xxxxxxxx.crt" 文件'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'view' => 'file',
// 'multiple' => 1
// ]
// ]);
// $builder->add('alipay_public_cert', '支付宝公钥证书', 'bundle', '', [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::ALIPAY['value']],
// ['ras_model', '=', AlipayRsaModel::PUBLIC_CERT['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '请上传 "alipayCertPublicKey_RSA2.crt" 文件'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'view' => 'file',
// 'multiple' => 1
// ]
// ]);
// $builder->add('alipay_root_cert', '支付宝根证书', 'bundle', '', [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::ALIPAY['value']],
// ['ras_model', '=', AlipayRsaModel::PUBLIC_CERT['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '请上传 "alipayRootCert.crt" 文件'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'view' => 'file',
// 'multiple' => 1
// ]
// ]);
// $builder->add('alipay_public_key', '支付宝公钥 (alipayPublicKey)', 'input', '', [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::ALIPAY['value']],
// ['ras_model', '=', AlipayRsaModel::PUBLIC_KEY['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '可在 "支付宝开放平台" - "应用信息" - "接口加签方式" - "支付宝公钥" 中复制'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'type' => 'textarea',
// 'autosize' => [
// 'minRows' => 4,
// 'maxRows' => 6
// ],
// 'placeholder' => '请输入支付宝公钥 (alipayPublicKey)'
// ]
// ]);
// $builder->add('private_key', '应用私钥 (privateKey)', 'input', '', [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::ALIPAY['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '查看 "应用私钥RSA2048-敏感数据,请妥善保管.txt" 文件,将全部内容复制到此处'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'type' => 'textarea',
// 'autosize' => [
// 'minRows' => 4,
// 'maxRows' => 6
// ],
// 'placeholder' => '请输入应用私钥 (privateKey)'
// ]
// ]);
// $builder->add('display_name', '显示名称', 'input', null, [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::INTEGRAL['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '在客户端显示的名称'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'type' => 'text',
// 'placeholder' => '请输入显示名称'
// ]
// ]);
// $builder->add('proportion', '充值比例', 'input-number', null, [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::INTEGRAL['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '1元=多少积分'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'controls' => false,
// 'min' => 1,
// 'max' => 1000000
// ]
// ]);
// $builder->add('is_integer', '整数使用', 'switch', State::NO['value'], [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::INTEGRAL['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '开启后只能使用整数的倍数积分'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'active-value' => State::YES['value'],
// 'inactive-value' => State::NO['value']
// ]
// ]);
// $builder->add('integer', '整数', 'input-number', null, [
// 'required' => true,
// 'where' => [
// ['channels', '=', PaymentChannels::INTEGRAL['value']],
// ['is_integer', '=', State::YES['value']]
// ],
// 'prompt' => [
// $Component->add('text', ['default' => '整数的倍数使用'], ['type' => 'info', 'size' => 'small'])
// ->builder()
// ],
// 'props' => [
// 'controls' => false,
// 'min' => 1,
// 'max' => 1000000
// ]
// ]);
/* $builder->add('notify_url', '支付通知', 'input', null, [
'required' => true,
'where' => [
['channels', 'in', [PaymentChannels::WXPAY['value'], PaymentChannels::ALIPAY['value']]]
],
'prompt' => [
$Component->add('text', ['default' => '只需要填写域名,不需要填写路径,如:https://www.baidu.com'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'type' => 'text',
'placeholder' => '支付通知接口域名'
]
]); */
return $builder;
}
}
@@ -0,0 +1,22 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\trait\Config;
use support\Request;
class PlatformController extends Basic
{
use Config;
public function wechat_miniproject(Request $request)
{
$this->channels_uid = $request->channels_uid;
return $this->builder();
}
public function wechat_official_account(Request $request)
{
$this->channels_uid = $request->channels_uid;
return $this->builder();
}
}
@@ -0,0 +1,195 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\config\Action;
use app\expose\build\config\Web;
use app\expose\enum\Action as EnumAction;
use app\expose\enum\Filesystem;
use app\expose\enum\State;
use app\expose\helper\Captcha;
use app\expose\helper\Config;
use plugin\control\expose\helper\Control;
use app\expose\helper\Vcode;
use loong\oauth\facade\Auth;
use plugin\control\app\model\PluginChannelsRole;
use support\Request;
class PublicController extends Basic
{
/**
* 不需要登录的方法
* @var string[]
*/
protected $notNeedLogin = ['config', 'menus', 'vcode', 'outLogin', 'unlock'];
protected $notNeedAuth = ['config', 'menus', 'vcode', 'outLogin'];
public function config(Request $request)
{
$lang = $request->lang;
$domain = 'control';
$config = new Config('basic', '');
$config = new Web($config->toArray());
$captcha_config = new Config('captcha', '');
$config->useLogin([
'url' => 'Login/login',
'title' => trans('Login Title', [], $domain, $lang),
'captcha' => $captcha_config->state,
'link_text' => trans('Login Link Text', [], $domain, $lang),
'user_agreement_config' => [
'title' => trans('Login User Agreement Title', [], $domain, $lang),
'label' => trans('Login User Agreement Label', [], $domain, $lang)
]
]);
$config->useVcode([
'url' => 'Login/vcode',
'title' => trans('Vcode Login', [], $domain, $lang)
]);
$config->useRegister([
'url' => 'Login/register',
'title' => trans('Register Title', ['%web_name%' => $config['web_name']], $domain, $lang),
'link_text' => trans('Register Link Text', [], $domain, $lang),
'user_agreement_config' => [
'title' => trans('Register User Agreement Title', [], $domain, $lang),
'label' => trans('Register User Agreement Label', [], $domain, $lang)
]
]);
$wechat_official_account_config = new Config('wechat_official_account', '');
if ($wechat_official_account_config->state && $wechat_official_account_config->message_state) {
$config->useQrcodeLogin([
'url' => 'Login/qrcode',
'check' => 'Login/checkQrcode',
'title' => trans('Qrcode Login', [], $domain, $lang)
]);
}
$config->useApis([
'userinfo' => '/app/control/control/User/getInfo',
'lock' => '/app/control/control/User/lock',
'unlock' => '/app/control/control/Public/unlock',
'menus' => '/app/control/control/Public/menus',
'vcode' => '/app/control/control/Public/vcode',
'outLogin' => '/app/control/control/Public/outLogin',
]);
$toolbar = new Action();
$toolbar->add(EnumAction::LOCK['value'], [
'icon' => 'Lock',
'tips' => trans('toolbar Lock', [], $domain, $lang)
]);
$toolbar->add(EnumAction::SEARCH['value'], [
'icon' => 'Search',
'tips' => trans('toolbar Search', [], $domain, $lang)
]);
$toolbar->add(EnumAction::NOTIFICATION['value'], [
'icon' => 'Notification',
'tips' => trans('toolbar Notification', [], $domain, $lang)
]);
$toolbar->add(EnumAction::FULL_SCREEN['value'], [
'icon' => 'FullScreen',
'tips' => trans('toolbar FullScreen', [], $domain, $lang)
]);
$config->useToolbar($toolbar->toArray());
$userDropdownMenu = new Action();
$userDropdownMenu->add(EnumAction::DIALOG['value'], [
'path' => '/app/control/control/User/update',
'label' => trans('userDropdownMenu updateSelf', [], $domain, $lang),
'icon' => 'User',
'props' => [
'title' => trans('userDropdownMenu updateSelf', [], $domain, $lang)
]
]);
$config->useUserDropdownMenu($userDropdownMenu->toArray());
$config->storage = Filesystem::getOptions(function ($item) {
return !in_array($item['value'], [Filesystem::PUBLIC['value'], Filesystem::LOCAL['value']]);
});
$pluginConfig = glob(base_path("plugin/*/api/{$request->app}/PublicController.php"));
foreach ($pluginConfig as $path) {
$plugin_name = basename(dirname(dirname(dirname($path))));
if ($plugin_name == 'control') {
continue;
}
$class = 'plugin\\' . $plugin_name . "\\api\\{$request->app}\\PublicController";
if (!class_exists($class)) {
continue;
}
$plugin = new $class;
if (method_exists($plugin, 'config')) {
$plugin->config($config);
}
}
$updateVersionContent = file_get_contents(base_path('update/VERSION'));
$updateVersionArr = explode("\n", $updateVersionContent);
$config->version_name=$updateVersionArr[1];
$config->version=$updateVersionArr[0];
return $this->resData($config);
}
public function menus(Request $request)
{
$Control = new \plugin\control\api\Control;
$menus = new Control($Control);
return $this->resData($menus);
}
// private function filterMenu(array $menus, array $allowPaths): array
// {
// $result = [];
// foreach ($menus as $menu) {
// // 递归处理 children
// if (!empty($menu['children'])) {
// $menu['children'] = $this->filterMenu($menu['children'], $allowPaths);
// }
// // 是否保留当前节点
// $keep = in_array($menu['path'], $allowPaths, true)
// || !empty($menu['children']);
// if ($keep) {
// $result[] = $menu;
// }
// }
// return $result;
// }
public function outLogin(Request $request)
{
$token = $request->header('Authorization');
if ($token) {
try {
Auth::setPrefix('CONTROL')->delete($token);
} catch (\Throwable $th) {
}
}
return $this->success(trans('Logout Success', [], $request->app, $request->lang));
}
public function vcode(Request $request)
{
$username = $request->post('username');
$token = $request->post('token');
$captcha = $request->post('captcha');
if (!Captcha::check($captcha, $token)) {
return $this->fail(trans('Captcha Incorrect', [], $request->app, $request->lang));
}
$scene = $request->post('scene');
if (!$scene) {
return $this->fail(trans('Scene Cannot Be Empty', [], $request->app, $request->lang));
}
try {
Vcode::send($username, $scene, $token);
return $this->success(trans('Vcode Send Success', [], $request->app, $request->lang));
} catch (\Throwable $th) {
return $this->exception($th);
}
}
public function unlock(Request $request)
{
$password = $request->post('password');
try {
$token = $request->header('Authorization');
Auth::setPrefix('CONTROL')->unlock($token, $password);
return $this->success(trans('Unlock Success', [], $request->app, $request->lang));
} catch (\Throwable $th) {
return $this->exception($th);
}
}
}
@@ -0,0 +1,260 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\builder\ComponentBuilder;
use app\expose\build\builder\FormBuilder;
use app\expose\build\builder\TableBuilder;
use app\expose\enum\Action;
use app\expose\enum\ResponseEvent;
use app\expose\enum\State;
use app\expose\helper\Menus;
use app\model\Admin;
use plugin\control\app\model\PluginChannelsRole;
use support\Request;
use plugin\control\expose\helper\Control;
class RoleController extends Basic
{
public function indexGetTable(Request $request)
{
$builder = new TableBuilder([
'rowKey' => 'id',
'api' => '/app/control/Role/index',
'lazy' => true,
'treeProps' => [
'children' => 'children',
'hasChildren' => 'hasChildren'
]
]);
$builder->addAction('操作', [
'width' => '200px',
'fixed' => 'right'
]);
$builder->addTableAction('编辑', [
'path' => '/app/control/control/Role/update',
'where' => [
['is_system', '!=', 1]
],
'props' => [
'type' => 'primary',
'title' => '编辑《{name}》角色'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'primary',
'size' => 'small'
]
]
]);
$builder->addTableAction('删除', [
'model' => Action::COMFIRM['value'],
'path' => 'AdminRole/delete',
'where' => [
['is_system', '!=', 1]
],
'props' => [
'type' => 'error',
'message' => '确定要删除《{name}》角色吗?',
'confirmButtonClass' => 'el-button--danger'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'danger',
'size' => 'small'
]
]
]);
$builder->addHeader();
$builder->addHeaderAction('创建角色', [
'path' => '/app/control/control/Role/create',
'props' => [
'type' => 'success',
'title' => '创建角色'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'success'
]
]
]);
$builder->add('id', 'ID', [
'props' => [
'width' => '80px'
]
]);
$builder->add('name', '角色名称');
$builder->add('admin_num', '人数', [
'props' => [
'width' => '120px'
]
]);
$builder->add('rule_num', '拥有权限数', [
'props' => [
'width' => '120px'
]
]);
$builder->add('create_time', '时间', [
'props' => [
'width' => '200px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'create_time',
'label' => '创建'
],
[
'field' => 'update_time',
'label' => '更新'
]
]
]
]
]);
$builder = $builder->builder();
return $this->resData($builder);
}
public function index(Request $request)
{
$limit = $request->get('limit', 10);
$where = [];
$where[]=['channels_uid','=',$request->channels_uid];
$list = PluginChannelsRole::where($where)
->order('id asc')->paginate($limit)->each(function ($item) {
$item->admin_num = Admin::where(['role_id' => $item->id])->count();
$item->rule_num = 0;
if ($item->is_system) {
$item->rule_num = '所有';
} elseif ($item->rule) {
$item->rule_num = count($item->rule);
}
$item->hasChildren = PluginChannelsRole::where(['pid' => $item->id])->count() > 0;
});
return $this->resData($list);
}
public function indexUpdateState(Request $request)
{
$id = $request->post('id');
$field = $request->post('field');
$value = $request->post('value');
$AdminRole = PluginChannelsRole::where(['id' => $id])->find();
if (!$AdminRole) {
return $this->fail('角色不存在');
}
if ($AdminRole->is_system) {
return $this->fail('系统角色不允许操作');
}
$AdminRole->{$field} = $value;
if ($AdminRole->save()) {
return $this->success();
}
return $this->fail('操作失败');
}
public function delete(Request $request)
{
$id = $request->post('id');
$AdminRole = PluginChannelsRole::where(['id' => $id])->find();
if (!$AdminRole) {
return $this->fail('角色不存在');
}
if ($AdminRole->is_system) {
return $this->fail('系统角色不允许操作');
}
if (PluginChannelsRole::where(['pid' => $id])->count() > 0) {
return $this->fail('请先删除子角色');
}
if (Admin::where(['role_id' => $id])->count() > 0) {
return $this->fail('请先将拥有该角色的用户移除');
}
if ($AdminRole->delete()) {
return $this->success();
}
return $this->fail('操作失败');
}
public function create(Request $request)
{
if ($request->method() === 'POST') {
$D = $request->post();
$AdminRole = new PluginChannelsRole;
$AdminRole->name = $D['name'];
$AdminRole->rule = $D['rule'];
$AdminRole->channels_uid=$request->channels_uid;
if ($AdminRole->save()) {
return $this->event(ResponseEvent::UPDATE_USERINFO, '保存成功');
}
return $this->fail('保存失败');
}
$builder = $this->getFormBuilder();
return $this->resData($builder);
}
public function update(Request $request)
{
if ($request->method() === 'POST') {
$D = $request->post();
$AdminRole = PluginChannelsRole::where(['id' => $D['id']])->find();
if (!$AdminRole) {
return $this->fail('角色不存在');
}
$AdminRole->name = $D['name'];
$AdminRole->rule = $D['rule'];
if ($AdminRole->save()) {
return $this->event(ResponseEvent::UPDATE_USERINFO, '保存成功');
}
return $this->fail('保存失败');
}
$id = $request->get('id');
$AdminRole = PluginChannelsRole::where(['id' => $id])->find();
if (!$AdminRole) {
return $this->fail('角色不存在');
}
$builder = $this->getFormBuilder(true);
if (!$AdminRole->rule) {
$AdminRole->rule = [];
}
$builder->setData($AdminRole->toArray());
return $this->resData($builder);
}
private function getFormBuilder($update = false)
{
$builder = new FormBuilder;
$Component = new ComponentBuilder;
$builder->add('name', '角色名称', 'input', '', [
'required' => true,
'prompt' => [
$Component->add('text', ['default' => '建议使用公司组织架构为角色名称'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'maxlength' => 30,
'show-word-limit' => true
]
]);
$Control = new \plugin\control\api\Control;
$menus = new Control($Control);
$builder->add('rule', '规则', 'admin-rule', [], [
'required' => true,
'options' => $menus,
'props' => [
'rightDefaultChecked' => [
'Index',
'Index/index',
'Index/control',
'Admin/updateSelf',
'Admin/getSelfInfo',
'Public',
'Public/outLogin'
]
]
]);
return $builder;
}
}
@@ -0,0 +1,326 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\builder\ComponentBuilder;
use app\expose\build\builder\FormBuilder;
use app\expose\enum\Filesystem;
use app\expose\enum\SubmitEvent;
use app\expose\trait\Config;
use app\model\Config as ModelConfig;
use support\Request;
class SystemController extends Basic
{
use Config;
public function basic(Request $request)
{
$this->channels_uid = $request->channels_uid;
return $this->builder();
}
public function yidevs(Request $request)
{
$this->channels_uid = $request->channels_uid;
return $this->builder();
}
public function payment(Request $request)
{
$this->channels_uid = $request->channels_uid;
return $this->builder();
}
public function sms(Request $request)
{
$this->channels_uid = $request->channels_uid;
return $this->tabsBuilder();
}
public function upload(Request $request)
{
$request = request();
if ($request->method() === 'POST') {
$D = $request->post();
$config = [];
$config['default'] = $D['default'];
$config['max_size'] = (int)$D['max_size'];
foreach ($D['ftp'] as $key => $value) {
$config['storage']['ftp'][$key] = $value;
}
foreach ($D['s3'] as $key => $value) {
if (in_array($key, ['key', 'secret'])) {
$config['storage']['s3']['credentials'][$key] = $value;
} else {
$config['storage']['s3'][$key] = $value;
}
}
foreach ($D['minio'] as $key => $value) {
if (in_array($key, ['key', 'secret'])) {
$config['storage']['minio']['credentials'][$key] = $value;
} else {
$config['storage']['minio'][$key] = $value;
}
}
foreach ($D['oss'] as $key => $value) {
$config['storage']['oss'][$key] = $value;
}
foreach ($D['qiniu'] as $key => $value) {
$config['storage']['qiniu'][$key] = $value;
}
foreach ($D['cos'] as $key => $value) {
$config['storage']['cos'][$key] = $value;
}
$ModelConfig = ModelConfig::where(['group' => 'filesystem', 'channels_uid' => $request->channels_uid])->find();
if (!$ModelConfig) {
$ModelConfig = new ModelConfig;
$ModelConfig->group = 'filesystem';
$ModelConfig->channels_uid = $request->channels_uid;
}
$ModelConfig->value = $config;
$ModelConfig->save();
return $this->success('保存成功');
}
$config = [
'enable' => true,
'default' => Filesystem::OSS['value'],
'max_size' => 1024 * 1024 * 10, //单个文件大小10M
'ext_yes' => [], //允许上传文件类型 为空则为允许所有
'ext_no' => [], // 不允许上传文件类型 为空则不限制
'storage' => [
'ftp' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\FtpAdapterFactory::class,
'host' => 'ftp.example.com',
'username' => 'username',
'password' => 'password',
'url' => '', // 静态文件访问域名
'port' => 21,
'root' => '/path/to/root',
'passive' => true,
'ssl' => true,
'timeout' => 30,
'ignorePassiveAddress' => false,
'timestampsOnUnixListingsEnabled' => true,
],
'memory' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\MemoryAdapterFactory::class,
],
's3' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\S3AdapterFactory::class,
'credentials' => [
'key' => 'S3_KEY',
'secret' => 'S3_SECRET',
],
'region' => 'S3_REGION',
'version' => 'latest',
'bucket_endpoint' => false,
'use_path_style_endpoint' => false,
'endpoint' => 'S3_ENDPOINT',
'bucket_name' => 'S3_BUCKET',
'url' => '' // 静态文件访问域名
],
'minio' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\S3AdapterFactory::class,
'credentials' => [
'key' => 'S3_KEY',
'secret' => 'S3_SECRET',
],
'region' => 'S3_REGION',
'version' => 'latest',
'bucket_endpoint' => false,
'use_path_style_endpoint' => true,
'endpoint' => 'S3_ENDPOINT',
'bucket_name' => 'S3_BUCKET',
'url' => '' // 静态文件访问域名
],
'oss' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\AliyunOssAdapterFactory::class,
'accessId' => 'OSS_ACCESS_ID',
'accessSecret' => 'OSS_ACCESS_SECRET',
'bucket' => 'OSS_BUCKET',
'endpoint' => 'OSS_ENDPOINT',
'url' => '', // 静态文件访问域名
'timeout' => 3600,
'connectTimeout' => 10,
'isCName' => false,
'token' => null,
'proxy' => null,
],
'qiniu' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\QiniuAdapterFactory::class,
'accessKey' => 'QINIU_ACCESS_KEY',
'secretKey' => 'QINIU_SECRET_KEY',
'bucket' => 'QINIU_BUCKET',
'domain' => 'QINBIU_DOMAIN',
'url' => '' // 静态文件访问域名
],
'cos' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\CosAdapterFactory::class,
'region' => 'COS_REGION',
'app_id' => 'COS_APPID',
'secret_id' => 'COS_SECRET_ID',
'secret_key' => 'COS_SECRET_KEY',
// 可选,如果 bucket 为私有访问请打开此项
'signed_url' => false,
'bucket' => 'COS_BUCKET',
'read_from_cdn' => false,
'url' => '' // 静态文件访问域名
// 'timeout' => 60,
// 'connect_timeout' => 60,
// 'cdn' => '',
// 'scheme' => 'https',
],
],
];
$ModelConfig = ModelConfig::where(['group' => 'filesystem', 'channels_uid' => $request->channels_uid])->find();
if ($ModelConfig) {
$user = $ModelConfig->value;
foreach ($user as $key => $value) {
if ($key !== 'storage') {
$config[$key] = $value;
}
}
foreach ($config['storage'] as $key => $value) {
if (isset($user['storage'][$key])) {
$config['storage'][$key] = array_merge($config['storage'][$key], $user['storage'][$key]);
}
}
}
$builder = new FormBuilder(null, null, [
'translations' => true,
'submitEvent' => SubmitEvent::SILENT
]);
$defaultOptions = Filesystem::getOptions(function ($item) {
return !in_array($item['value'], [Filesystem::PUBLIC['value'], Filesystem::LOCAL['value']]);
});
$builder->add('default', '默认存储渠道商', 'select', $config['default'], [
'options' => $defaultOptions
]);
$builder->add('max_size', '单个文件大小(字节)', 'input-number', $config['max_size'], [
'props' => [
'min' => 0,
'controls' => false,
'style' => [
'width' => '200px'
]
]
]);
$Component = new ComponentBuilder;
$subBuilder = new FormBuilder('oss', '阿里云存储');
$subBuilder->addValue('driver', \Shopwwi\WebmanFilesystem\Adapter\AliyunOssAdapterFactory::class);
$subBuilder->add('accessId', 'AccessId', 'input', $config['storage']['oss']['accessId']);
$subBuilder->add('accessSecret', 'AccessSecret', 'input', $config['storage']['oss']['accessSecret']);
$subBuilder->add('bucket', 'Bucket', 'input', $config['storage']['oss']['bucket']);
$subBuilder->add('endpoint', 'Bucket域名', 'input', $config['storage']['oss']['endpoint']);
$subBuilder->add('isCName', '私有空间', 'switch', $config['storage']['oss']['isCName']);
$subBuilder->add('url', '静态文件访问域名', 'input', $config['storage']['oss']['url'], [
'prompt' => [
$Component->add('text', ['default' => '对外访问域名,不以斜杠结尾'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => 'http://static.example.com'
]
]);
$builder->addGroupForm($subBuilder);
$subBuilder = new FormBuilder('cos', '腾讯云存储');
$subBuilder->addValue('driver', \Shopwwi\WebmanFilesystem\Adapter\CosAdapterFactory::class);
$subBuilder->add('app_id', 'Appid', 'input', $config['storage']['cos']['app_id']);
$subBuilder->add('secret_id', 'SecretId', 'input', $config['storage']['cos']['secret_id']);
$subBuilder->add('secret_key', 'SecretKey', 'input', $config['storage']['cos']['secret_key']);
$subBuilder->add('region', 'Region', 'input', $config['storage']['cos']['region']);
$subBuilder->add('bucket', 'Bucket', 'input', $config['storage']['cos']['bucket']);
$subBuilder->add('read_from_cdn', '从CDN读取', 'switch', $config['storage']['cos']['read_from_cdn']);
$subBuilder->add('signed_url', '私有空间', 'switch', $config['storage']['cos']['signed_url']);
$subBuilder->add('url', '静态文件访问域名', 'input', $config['storage']['cos']['url'], [
'prompt' => [
$Component->add('text', ['default' => '对外访问域名,不以斜杠结尾'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => 'http://static.example.com'
]
]);
$builder->addGroupForm($subBuilder);
$subBuilder = new FormBuilder('qiniu', '七牛存储');
$subBuilder->addValue('driver', \Shopwwi\WebmanFilesystem\Adapter\QiniuAdapterFactory::class);
$subBuilder->add('accessKey', 'AccessKey', 'input', $config['storage']['qiniu']['accessKey']);
$subBuilder->add('secretKey', 'SecretKey', 'input', $config['storage']['qiniu']['secretKey']);
$subBuilder->add('bucket', 'Bucket', 'input', $config['storage']['qiniu']['bucket']);
$subBuilder->add('domain', 'Domain', 'input', $config['storage']['qiniu']['domain']);
$subBuilder->add('url', '静态文件访问域名', 'input', $config['storage']['qiniu']['url'], [
'prompt' => [
$Component->add('text', ['default' => '对外访问域名,不以斜杠结尾'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => 'http://static.example.com'
]
]);
$builder->addGroupForm($subBuilder);
$subBuilder = new FormBuilder('ftp', 'FTP存储');
$subBuilder->addValue('driver', \Shopwwi\WebmanFilesystem\Adapter\FtpAdapterFactory::class);
$subBuilder->add('host', 'HOST', 'input', $config['storage']['ftp']['host']);
$subBuilder->add('username', 'UserName', 'input', $config['storage']['ftp']['username']);
$subBuilder->add('password', 'Password', 'input', $config['storage']['ftp']['password']);
$subBuilder->add('port', 'Port', 'input', $config['storage']['ftp']['port']);
$subBuilder->add('root', '目录', 'input', $config['storage']['ftp']['root']);
$subBuilder->add('passive', '被动模式', 'switch', $config['storage']['ftp']['passive']);
$subBuilder->add('ssl', 'SSL', 'switch', $config['storage']['ftp']['ssl']);
$subBuilder->add('timeout', '超时', 'input-number', $config['storage']['ftp']['timeout']);
$subBuilder->add('url', '静态文件访问域名', 'input', $config['storage']['ftp']['url'], [
'prompt' => [
$Component->add('text', ['default' => '对外访问域名,不以斜杠结尾'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => 'http://static.example.com'
]
]);
$builder->addGroupForm($subBuilder);
$subBuilder = new FormBuilder('s3', 'S3存储');
$subBuilder->addValue('driver', \Shopwwi\WebmanFilesystem\Adapter\S3AdapterFactory::class);
$subBuilder->add('key', 'KEY', 'input', $config['storage']['s3']['credentials']['key']);
$subBuilder->add('secret', 'Secret', 'input', $config['storage']['s3']['credentials']['secret']);
$subBuilder->add('region', 'Region', 'input', $config['storage']['s3']['region']);
$subBuilder->add('version', 'Version', 'input', $config['storage']['s3']['version']);
$subBuilder->add('bucket_endpoint', 'Bucket Endpoint', 'switch', $config['storage']['s3']['bucket_endpoint']);
$subBuilder->add('use_path_style_endpoint', 'Use path style endpoint', 'switch', $config['storage']['s3']['use_path_style_endpoint']);
$subBuilder->add('endpoint', 'Endpoint', 'input', $config['storage']['s3']['endpoint']);
$subBuilder->add('bucket_name', 'Bucket Name', 'input', $config['storage']['s3']['bucket_name']);
$subBuilder->add('url', '静态文件访问域名', 'input', $config['storage']['s3']['url'], [
'prompt' => [
$Component->add('text', ['default' => '对外访问域名,不以斜杠结尾'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => 'http://static.example.com'
]
]);
$builder->addGroupForm($subBuilder);
$subBuilder = new FormBuilder('minio', 'Minio存储');
$subBuilder->addValue('driver', \Shopwwi\WebmanFilesystem\Adapter\S3AdapterFactory::class);
$subBuilder->add('key', 'KEY', 'input', $config['storage']['minio']['credentials']['key']);
$subBuilder->add('secret', 'Secret', 'input', $config['storage']['minio']['credentials']['secret']);
$subBuilder->add('region', 'Region', 'input', $config['storage']['minio']['region']);
$subBuilder->add('version', 'Version', 'input', $config['storage']['minio']['version']);
$subBuilder->add('bucket_endpoint', 'Bucket Endpoint', 'switch', $config['storage']['minio']['bucket_endpoint']);
$subBuilder->add('use_path_style_endpoint', 'Use path style endpoint', 'switch', $config['storage']['minio']['use_path_style_endpoint']);
$subBuilder->add('endpoint', 'Endpoint', 'input', $config['storage']['minio']['endpoint']);
$subBuilder->add('bucket_name', 'Bucket Name', 'input', $config['storage']['minio']['bucket_name']);
$subBuilder->add('url', '静态文件访问域名', 'input', $config['storage']['minio']['url'], [
'prompt' => [
$Component->add('text', ['default' => '对外访问域名,不以斜杠结尾'], ['type' => 'info', 'size' => 'small'])
->builder()
],
'props' => [
'placeholder' => 'http://static.example.com'
]
]);
$builder->addGroupForm($subBuilder);
return $this->resData($builder);
}
}
@@ -0,0 +1,22 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\helper\Config;
use plugin\control\expose\trait\Uploads;
class UploadsController extends Basic
{
use Uploads;
public function __construct()
{
$request = request();
$this->channels_uid = $request->channels_uid;
$this->admin_uid = $request->uid;
$Config = new Config('filesystem', '', $this->channels_uid);
if (empty($Config->toArray())) {
throw new \Exception('请先完善上传配置');
}
}
}
@@ -0,0 +1,385 @@
<?php
namespace plugin\control\app\control\controller;
use app\Basic;
use app\expose\build\builder\FormBuilder;
use app\expose\build\builder\TableBuilder;
use app\expose\enum\Action;
use app\expose\enum\ResponseEvent;
use app\expose\enum\State;
use app\expose\utils\Password;
use app\validate\User as ValidateUser;
use loong\oauth\facade\Auth;
use plugin\control\app\model\PluginChannelsRole;
use plugin\control\app\model\PluginChannelsUser;
use support\Request;
class UserController extends Basic
{
public function indexGetTable(Request $request)
{
$builder = new TableBuilder();
$builder->addAction('操作', [
'width' => '200px',
'fixed' => 'right'
]);
$builder->addTableAction('编辑', [
'model' => Action::DIALOG['value'],
'path' => '/app/control/control/User/updateUser',
'props' => [
'title' => '编辑《{id}》账号'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'primary',
'size' => 'small'
]
]
]);
$builder->addHeader();
$builder->addHeaderAction('创建账号', [
'model' => Action::DIALOG['value'],
'path' => '/app/control/control/User/create',
'props' => [
'title' => '创建账号'
],
'component' => [
'name' => 'button',
'props' => [
'type' => 'success'
]
]
]);
$builder->add('id', 'ID', [
'props' => [
'width' => '80px'
]
]);
$builder->add('userinfo', '用户', [
'component' => [
'name' => 'table-userinfo',
'props' => [
'nickname' => 'nickname',
'avatar' => 'headimg',
'info' => 'username',
'nicknameTags' => [
[
'field' => 'new_text',
'props' => [
'type' => 'success',
'size' => 'small'
]
],
[
'field' => 'week_text',
'props' => [
'type' => 'warning',
'size' => 'small'
]
],
[
'field' => 'month_text',
'props' => [
'type' => 'info',
'size' => 'small'
]
]
]
]
],
'props' => [
'minWidth' => '300px'
]
]);
$builder->add('contact', '联系方式', [
'props' => [
'width' => '280px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'mobile',
'label' => '手机号'
],
[
'field' => 'email',
'label' => '邮箱'
]
]
]
]
]);
$builder->add('login_ip', '登录IP', [
'props' => [
'width' => '130px'
]
]);
$builder->add('role.name', '角色', [
'props' => [
'width' => '100px'
]
]);
$builder->add('role.state', '状态', [
'props' => [
'width' => '100px'
]
]);
$builder->add('sort', '排序(小到大)', [
'props' => [
'width' => '130px'
]
]);
$builder->add('create_time', '时间', [
'props' => [
'width' => '200px'
],
'component' => [
'name' => 'table-times',
'props' => [
'group' => [
[
'field' => 'create_time',
'label' => '创建'
],
[
'field' => 'update_time',
'label' => '更新'
]
]
]
]
]);
$builder = $builder->builder();
return $this->resData($builder);
}
public function index(Request $request)
{
$limit = $request->get('limit', 10);
$where = [];
$where[] = ['channels_uid', '=', $request->channels_uid];
$list = PluginChannelsUser::with('role')->where($where)->paginate($limit);
return $this->resData($list);
}
public function create(Request $request)
{
if ($request->method() === 'POST') {
$D = $request->post();
$D['channels_uid'] = $request->channels_uid;
if (empty($D['password'])) {
return $this->fail('密码不能为空');
}
try {
PluginChannelsUser::create($D);
} catch (\Throwable $th) {
return $this->exception($th);
}
return $this->success('创建成功');
}
$builder = $this->getForm($request->channels_uid);
return $this->resData($builder);
}
public function updateUser(Request $request)
{
if ($request->method() === 'POST') {
$D = $request->post();
$User = PluginChannelsUser::where(['id' => $D['id']])->find();
if (!$User) {
return $this->fail('用户不存在');
}
if (empty($D['password'])) {
unset($D['password']);
} else {
$password = $D['password'];
}
$User->role_id = $D['role_id'];
$User->username = $D['username'];
$User->nickname = $D['nickname'];
$User->headimg = $D['headimg'];
$User->mobile = $D['mobile'];
$User->email = $D['email'];
$User->password = $password;
if ($User->save()) {
return $this->success('更新成功');
}
return $this->fail('更新失败');
}
$id = $request->get('id');
$User = PluginChannelsUser::where(['id' => $id])->find();
if (!$User) {
return $this->fail('用户不存在');
}
$builder = $this->getForm($request->channels_uid);
unset($User->password);
$builder->setData($User->toArray());
return $this->resData($builder);
}
private function getForm($uid)
{
$list = PluginChannelsRole::field('id as value,name as label')->where('channels_uid', $uid)->where('state', State::YES['value'])->select();
$builder = new FormBuilder;
$builder->add('role_id', '所属角色', 'select', null, [
'options' => $list
]);
$builder->add('username', '账号', 'input', '', [
'props' => [
'maxlength' => 30,
'show-word-limit' => true
]
]);
$builder->add('password', '密码', 'input', '', [
'props' => [
'placeholder' => '不修改密码请留空',
'maxlength' => 30,
'show-word-limit' => true
]
]);
$builder->add('nickname', '昵称', 'input', '', [
'required' => true,
'maxlength' => 30,
'show-word-limit' => true
]);
$builder->add('headimg', '头像', 'bundle', '', [
'props' => [
'accept' => 'image/*',
'multiple' => 1
]
]);
$builder->add('mobile', '手机号', 'input', '', [
'props' => [
'maxlength' => 11,
'show-word-limit' => true
]
]);
$builder->add('email', '邮箱', 'input', '', [
'props' => [
'maxlength' => 50,
'show-word-limit' => true
]
]);
return $builder;
}
public function update(Request $request)
{
$id = $request->channels_uid;
if ($request->method() === 'POST') {
$D = $request->post();
$D['id'] = $id;
try {
$validate = new ValidateUser;
$validate->scene('self')->check($D);
} catch (\Throwable $th) {
return $this->exception($th);
}
$User = PluginChannelsUser::where(['id' => $D['id']])->find();
if (!$User) {
return $this->fail('用户不存在');
}
if (!$User->username) {
$User->username = $D['username'];
}
$User->nickname = $D['nickname'];
$User->headimg = $D['headimg'];
$User->mobile = $D['mobile'];
$User->email = $D['email'];
if ($D['password']) {
$User->password = $D['password'];
}
if ($User->save()) {
return $this->event(ResponseEvent::UPDATE_USERINFO, '保存成功');
}
return $this->fail('保存失败');
}
$User = PluginChannelsUser::where(['id' => $id])->withoutField('password')->find();
if (!$User) {
return $this->fail('用户不存在');
}
$builder = new FormBuilder();
$builder->add('username', '账号', 'input', '', [
'props' => [
'maxlength' => 30,
'show-word-limit' => true,
'disabled' => $User->username ? true : false
]
]);
$builder->add('password', '密码', 'input', '', [
'props' => [
'placeholder' => '不修改密码请留空',
'maxlength' => 30,
'show-word-limit' => true
]
]);
$builder->add('nickname', '昵称', 'input', '', [
'required' => true,
'maxlength' => 30,
'show-word-limit' => true
]);
$builder->add('headimg', '头像', 'bundle', '', [
'props' => [
'accept' => 'image/*',
'multiple' => 1
]
]);
$builder->add('mobile', '手机号', 'input', '', [
'props' => [
'maxlength' => 11,
'show-word-limit' => true
]
]);
$builder->add('email', '邮箱', 'input', '', [
'props' => [
'maxlength' => 50,
'show-word-limit' => true
]
]);
$builder->setData($User->toArray());
return $this->resData($builder);
}
public function getInfo(Request $request)
{
$User = PluginChannelsUser::where(['id' => $request->uid])->withoutField('password')->find();
return $this->resData(PluginChannelsUser::getTokenInfo($User));
}
public function refresh()
{
return $this->event(ResponseEvent::UPDATE_USERINFO, '刷新成功');
}
public function lock(Request $request)
{
try {
$password = $request->post('password');
if (!$password) {
return $this->fail('PIN码不能为空');
}
if (mb_strlen($password) != 6) {
return $this->fail('请输入6位PIN码');
}
$token = $request->header('Authorization');
Auth::setPrefix('CONTROL')->lock($token, $password);
return $this->event(ResponseEvent::UPDATE_USERINFO, '锁定成功');
} catch (\Throwable $th) {
return $this->exception($th);
}
}
}
@@ -0,0 +1,32 @@
<?php
namespace plugin\control\app\controller;
use app\expose\helper\Config;
use app\expose\utils\wechat\OfficialAccount;
use support\Log;
use support\Request;
class WechatOfficialAccountController
{
public function message(Request $request, $channels_uid)
{
$OfficialAccount = new OfficialAccount();
$config = new Config('wechat_official_account', 'control', $channels_uid);
if (!$config->state) {
return 'wechat official account is not enabled';
}
try {
$OfficialAccount->checkSignature($request, $config);
if ($request->method() === 'GET') {
return $request->get('echostr');
}
} catch (\Throwable $th) {
if ($request->method() === 'GET') {
return $th->getMessage();
} else {
}
}
return $OfficialAccount->handle($request, $config);
}
}
@@ -0,0 +1,9 @@
<?php
namespace plugin\control\app\middleware;
use plugin\control\expose\middleware\ControlAuth;
class Auth extends ControlAuth
{
}
@@ -0,0 +1,7 @@
<?php
namespace plugin\control\app\model;
use app\model\Basic;
class PluginChannelsDomain extends Basic {}
@@ -0,0 +1,23 @@
<?php
namespace plugin\control\app\model;
use app\model\Basic;
class PluginChannelsRole extends Basic
{
public function getRuleAttr($value)
{
if (empty($value)) {
return '';
}
return json_decode($value, true);
}
public function setRuleAttr($value)
{
if (empty($value)) {
return '';
}
return json_encode($value, JSON_UNESCAPED_UNICODE);
}
}
@@ -0,0 +1,155 @@
<?php
namespace plugin\control\app\model;
use app\expose\utils\Password;
use app\expose\utils\Rsa;
use app\expose\utils\Str;
use app\model\Basic;
use Exception;
use loong\oauth\facade\Auth;
use plugin\control\expose\helper\Uploads;
use plugin\user\utils\enum\UserPermission;
class PluginChannelsUser extends Basic
{
public function getHeadimgAttr($value, $data)
{
$id = $data['channels_uid'] ?? 0;
return Uploads::url($data['id'], $value);
}
public function setHeadimgAttr($value, $data)
{
$id = $data['channels_uid'] ?? 0;
return Uploads::path($id, $value);
}
public function setPasswordAttr($value)
{
return $value ? Password::encrypt($value) : $value;
}
public static function options($where = [])
{
$models = self::where($where)->field('id,nickname,mobile')->select();
$data = [];
foreach ($models as $item) {
$data[] = [
'label' => $item->nickname,
'value' => $item->id,
'tips' => "UID{$item->id}M{$item->mobile}"
];
}
return $data;
}
public static function genUser($id)
{
$id = (int)$id;
return str_replace(['+', '/'], ['-', '_'], Rsa::encryptNumber($id));
}
public static function getUidByUser($user)
{
return Rsa::decryptNumber(str_replace(['-', '_'], ['+', '/'], $user));
}
public static function getTokenInfo($model, $twofa = false)
{
$request = request();
/* 重组用户信息 */
$User = new \stdClass;
$User->user = self::genUser($model->id);
$User->nickname = $model->nickname;
$User->headimg = $model->headimg;
$User->username = $model->username;
$User->username_time = $model->username_time ? 30 - ceil((time() - strtotime($model->username_time)) / 86400) : 0;
$User->mobile = Str::mask($model->mobile);
$User->email = Str::mask($model->email);
$User->password = $model->password ? 1 : 0;
$User->create_time = $model->create_time;
$User->last_login_time = $model->login_time;
$User->last_login_ip = $model->login_ip;
$User->twofa_state = $model->twofa_state;
$PluginChannelsWechat = PluginChannelsWechat::where(['uid' => $model->id])->field('openid,unionid,mp_openid,nickname,headimg,subscribe')->find();
if ($PluginChannelsWechat) {
$User->wechat = $PluginChannelsWechat;
}
if ($model->channels_uid === null) {
$User->is_system = 1;
$User->permissions = null;
$User->channels_uid = $model->id;
} else {
$role = PluginChannelsRole::where(['id' => $model->role_id, 'channels_uid' => $model->channels_uid])->find();
if (!$role) {
throw new Exception("无权限访问");
}
# 是否为主账号
$User->is_system = 0;
# 不为主账号则所拥有的权限列表
$User->permissions = $role->rule;
}
$pluginConfig = glob(base_path("plugin/*/api/{$request->app}/PublicController.php"));
foreach ($pluginConfig as $path) {
$plugin_name = basename(dirname(dirname(dirname($path))));
if ($plugin_name == 'user') {
continue;
}
$class = 'plugin\\' . $plugin_name . "\\api\\{$request->app}\\PublicController";
if (!class_exists($class)) {
continue;
}
$plugin = new $class;
if (method_exists($plugin, 'appendUserInfo')) {
$plugin->appendUserInfo($User, $model);
}
}
/* 生成token */
$data = new \stdClass;
$data->uid = $model->id;
$data->username = $model->username;
$data->mobile = $model->mobile;
$data->email = $model->email;
$data->channels_uid = $User->channels_uid;
$data->is_system = $User->is_system;
$data->permissions = $User->permissions;
$data->twofa_state = $model->twofa_state;
if ($twofa) {
if ($twofa === true) {
$data->twofa = [
'expire' => time() + (config('oauth.expire') / 2),
'time' => time(),
];
} else {
$data->twofa = $twofa;
}
}
$User->token = Auth::setPrefix('CONTROL')->encrypt($data);
if ($request->token) {
Auth::setPrefix('CONTROL')->refresh($request->token, 60);
}
return $User;
}
public static function onAfterRead($model)
{
if (empty($model->nickname)) {
if (!empty($model->mobile)) {
$model->nickname = 'FM-' . substr($model->mobile, -4);
} else {
$model->nickname = '未命名的用户';
}
}
}
public static function onBeforeWrite($model)
{
if (empty($model->nickname)) {
if (!empty($model->mobile)) {
$model->nickname = 'FM-' . substr($model->mobile, -4);
} else {
$model->nickname = 'FM-' . Str::random();
}
} else {
$model->nickname = iconv('UTF-8', 'UTF-8//IGNORE', $model->nickname);
}
}
public function role(){
return $this->hasOne(PluginChannelsRole::class, 'id', 'role_id');
}
}
@@ -0,0 +1,15 @@
<?php
namespace plugin\control\app\model;
use app\model\Basic;
class PluginChannelsWechat extends Basic
{
public static function onBeforeWrite($model)
{
if(!empty($model->nickname)){
$model->nickname= iconv('UTF-8', 'UTF-8//IGNORE', $model->nickname);
}
}
}