Casbin权限控制相关参考
方案一
方案二
原理
规则
数据ER
管理UI
数据表实例
特别声明:其中方案二
为某大佬线上生产环境系统设计,如果侵犯到此权限请通知我删除,同时采纳者一同承担相应责任。
原理
规则
数据ER
管理UI
数据表实例
特别声明:其中方案二
为某大佬线上生产环境系统设计,如果侵犯到此权限请通知我删除,同时采纳者一同承担相应责任。
id | article_id | parent_id(string) |
---|---|---|
1 | 3 | 0 |
2 | 3 | 1 |
3 | 3 | 1_2 |
4 | 3 | 2 |
5 | 3 | 1_2_3 |
删除 id = 1 的1 级文章及下级所有文章
Article::where('id', 1)->delete();
Article::where('parent_id', 'like', '1%')->delete();
这样和 id = 1 的所有相关文章就删除了
删除这些文章下的评论就用观察者就行了
习惯将CDN文件下载到本地,保存至静态目录中,注意保留版本号。
<!-- 引入 css -->
<link href="https://cdn.jsdelivr.net/npm/@wangeditor/editor@latest/dist/css/style.css" rel="stylesheet">
<!-- 引入 js -->
<script src="https://cdn.jsdelivr.net/npm/@wangeditor/editor@latest/dist/index.min.js"></script>
直接上 HTML
代码吧:
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-form-item">
<label class="layui-form-label">正文:</label>
<div class="layui-input-block">
<textarea name="content" id="content" style="display: none"></textarea>
<div id="toolbar-container"></div>
<div id="editor-container" style="height: 300px;border:1px solid #dddddd"></div>
</div>
</div>
</div>
</div>
注意从官方拷贝js
代码时,要注意javascript与typescript的修改。
直接上 JS
代码。
// 渲染富文本编辑器
const { createEditor, createToolbar } = window.wangEditor;
// 编辑器配置
const editorConfig = {MENU_CONF: {}};
// 图片上传
editorConfig.MENU_CONF['uploadImage'] = {
// 表单字段名称
fieldName: 'file',
// 单个文件的最大体积限制,默认为 2M
maxFileSize: 5 * 1024 * 1024, // 5M
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: 5,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['image/*'],
// 小于该值就插入 base64 格式(而不上传),默认为 0
base64LimitSize: 5 * 1024, // 5kb
// 自定义参数
meta: {
from: 'editor', //标志是从编辑器上传
},
// 上传图片的配置
server: '<?=route("upload.image")?>',
};
// 默认提示文字
editorConfig.placeholder = '请输入内容'
editorConfig.onChange = (editor) => {
// 当编辑器选区、内容变化时,即触发
//console.log('content', editor.children)
//console.log('html', editor.getHtml())
document.getElementById('content').value = editor.getHtml();
}
// 工具栏配置
const toolbarConfig = {}
// 创建编辑器
const editor = createEditor({
html: document.getElementById('content').value,
selector: '#editor-container',
config: editorConfig,
mode: 'simple' // 或 'simple' 参考下文
})
// 创建工具栏
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'simple' // 或 'simple' 参考下文
})
截图如下。
<?php
namespace app\controller;
use support\Request;
use support\Response;
use Yansongda\Pay\Pay;
class Payment
{
protected $config = [
'alipay' => [
'default' => [
// 必填-支付宝分配的 app_id
'app_id' => 'wx4926de289f70b113',
// 必填-应用私钥 字符串或路径
'app_secret_cert' => 'MIIEvLoLc1s/VdBHku+JEd0YmEY+p4sjmcRnlu4AlzLxkWUTTg==',
// 必填-应用公钥证书 路径
'app_public_cert_path' => '/Users/yansongda/pay/cert/appCertPublicKey_2016082000295641.crt',
// 必填-支付宝公钥证书 路径
'alipay_public_cert_path' => '/Users/yansongda/pay/cert/alipayCertPublicKey_RSA2.crt',
// 必填-支付宝根证书 路径
'alipay_root_cert_path' => '/Users/yansongda/pay/cert/alipayRootCert.crt',
'return_url' => 'https://yansongda.cn/alipay/return',
'notify_url' => 'https://yansongda.cn/alipay/notify',
// 选填-第三方应用授权token
'app_auth_token' => '',
// 选填-服务商模式下的服务商 id,当 mode 为 Pay::MODE_SERVICE 时使用该参数
'service_provider_id' => '',
// 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SANDBOX, MODE_SERVICE
'mode' => Pay::MODE_NORMAL,
]
],
'wechat' => [
'default' => [
// 必填-商户号,服务商模式下为服务商商户号
'mch_id' => '123xxxx02',
// 必填-商户秘钥
'mch_secret_key' => 'xxxxx', //api安全中心里的V3密码32位的那个
// 必填-商户私钥 字符串或路径
'mch_secret_cert' => '/xxxxx/apiclient_key.pem', //api安全中心里下载的的密钥,绝对路径
// 必填-商户公钥证书路径
'mch_public_cert_path' => '/xxxx/apiclient_cert.pem', //api安全中心里下载的公钥,绝对路径
// 必填
'notify_url' => 'https://www.xxx.com/member/wx_native_notify', //支付回调地址必须https
// 选填-公众号 的 app_id
'mp_app_id' => 'wx4926de289f70b113',
// 选填-小程序 的 app_id
'mini_app_id' => '',
// 选填-app 的 app_id
'app_id' => '',
// 选填-合单 app_id
'combine_app_id' => '',
// 选填-合单商户号
'combine_mch_id' => '',
// 选填-服务商模式下,子公众号 的 app_id
'sub_mp_app_id' => '',
// 选填-服务商模式下,子 app 的 app_id
'sub_app_id' => '',
// 选填-服务商模式下,子小程序 的 app_id
'sub_mini_app_id' => '',
// 选填-服务商模式下,子商户id
'sub_mch_id' => '',
// 选填-微信公钥证书路径, optional,强烈建议 php-fpm 模式下配置此参数
'wechat_public_cert_path' => [
'4248DC46520F9EAC26C7FET4464ADBE5ADDA3A' => '/xxxx/wechatpay_public.pem', //通过算法生成的,具体生成方式往下面看。
],
// 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE
'mode' => Pay::MODE_NORMAL,
]
],
'logger' => [
'enable' => false,
'file' => './logs/alipay.log',
'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug
'type' => 'single', // optional, 可选 daily.
'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
],
'http' => [ // optional
'timeout' => 5.0,
'connect_timeout' => 5.0,
// 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
],
];
//二维码生成展示页
public function index(Request $request)
{
Pay::config($this->config);
$order = [
'out_trade_no' => time().'',
'description' => 'subject-测试',
'amount' => [
'total' => 1,
],
];
$result = Pay::wechat()->scan($order);
// 二维码内容: $qr = $result->code_url;
//var_export($result->code_url);
return view('payment/index', ['code_url' => $result->code_url]);
}
//支付成功回调
public function verify(Request $request)
{
Pay::config($this->config);
// 是的,你没有看错,就是这么简单!
$result = Pay::wechat()->callback($request->post());
file_put_contents(__DIR__ . '/notify_result_ysd.txt',
date('Y-m-d H:i:s') . ':' . var_export($result, true));
//这里很关键,需要注意。
return new Response(200, [], json_encode(['code' => 'SUCCESS', 'message' => '成功']));
}
}
// app\member\controller\pay.php
<?php
namespace app\controller;
use EasyWeChat\Kernel\Exceptions\Exception;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use support\Request;
use EasyWeChat\Pay\Application;
use Symfony\Component\HttpFoundation\HeaderBag;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
class Index
{
private $config = [
'mch_id' => 'xxxx', //根据自已实际的填写
// 商户证书
'private_key' => '/xxx/apiclient_key.pem',//根据自已实际的填写,绝对路径
'certificate' => '/xxx/apiclient_cert.pem', //根据自已实际的填写,绝对路径
// v3 API 秘钥
'secret_key' => 'xxxx', //根据自已实际的填写
// v2 API 秘钥
'v2_secret_key' => 'xxx', //根据自已实际的填写
'platform_certs' => [
'/xxx/wechatpay_public.pem', //根据自已实际的填写,获取办法见下面。
],
'http' => [
'throw' => true, // 状态码非 200、300 时是否抛出异常,默认为开启
'timeout' => 5.0,
// 'base_uri' => 'https://api.mch.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
],
];
//二维码生成展示页
public function index(Request $request)
{
try {
$app = new Application($this->config);
$response = $app->getClient()->post('/v3/pay/transactions/native', [
'json' => [
'mchid' => (string)$app->getMerchant()->getMerchantId(),
'out_trade_no' => 'SN_'.time(),
'appid' => 'xxxx', //根据自已实际的填写
'description' => '测试内容',
'notify_url' => 'https://www.xxxx.com/member/wx_native_notify', //注意是ssl协议
'amount' => [
'total' => 2,
'currency' => 'CNY',
]
]
]);
} catch (InvalidArgumentException $e) {
//
}
return view('index/index', ['code_url' => $response->toArray()]);
}
//回调
public function verify(Request $request)
{
try {
//下面四行很关键
$symfony_request = new SymfonyRequest($request->get(), $request->post(), [], $request->cookie(), [], [], $request->rawBody());
$symfony_request->headers = new HeaderBag($request->header());
$app = new Application($this->config);
$app->setRequestFromSymfonyRequest($symfony_request);
// $app 为你实例化的支付对象,此处省略实例化步骤
$server = $app->getServer();
// 处理支付结果事件
$server->handlePaid(function ($message) {
// $message 为微信推送的通知结果,详看微信官方文档
file_put_contents(__DIR__ . '/notify_result.txt',
date('Y-m-d H:i:s') . ':' . var_export($message, true));
// 微信支付订单号 $message['transaction_id']
// 商户订单号 $message['out_trade_no']
// 商户号 $message['mchid']
// 具体看微信官方文档...
// 进行业务处理,如存数据库等...
});
//这一步特别要注意,必须这么写。
return $server->serve()->getBody()->getContents();
} catch (Exception $e) {
echo $e->getMessage();
}
}
}
一般来说在Worker::runAll();
调用前运行的代码都是在主进程运行的,onXXX
回调运行的代码都属于子进程。注意写在Worker::runAll();
后面的代码永远不会被执行。
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// 运行在主进程
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// 赋值过程运行在主进程
$tcp_worker->onMessage = function(TcpConnection $connection, $data)
{
// 这部分运行在子进程
$connection->send('hello ' . $data);
};
Worker::runAll();
注意:
1、不要在主进程中初始化数据库、memcache
、redis
等连接资源,因为主进程初始化的连接可能会被子进程自动继承(尤其是使用单例的时候),所有进程都持有同一个连接,服务端通过这个连接返回的数据在多个进程上都可读,会导致数据错乱。同样的,如果任何一个进程关闭连接(例如daemon
模式运行时主进程会退出导致连接关闭),都导致所有子进程的连接都被一起关闭,并发生不可预知的错误,例如mysql gone away
错误。
2、推荐在onWorkerStart
里面初始化连接资源。
1、在文件support/helper.php
添加:
function env($key, $default = null)
{
static $env_config = [];
if (!$env_config) {
$env_config = include base_path() . DIRECTORY_SEPARATOR . '.env.php';
}
return $env_config[$key] ?? $default;
}
2、在项目根目录添加.env.php
:
<?php
// config/app.php
$app = [
'APP_NAME' => 'webman',
'APP_ENV' => 'dev',
'APP_DEBUG' => true,
];
// config/database.php
$mysql = [
'DB_HOST' => '127.0.0.1',
'DB_PORT' => 3306,
'DB_USER' => 'root',
'DB_PASSWORD' => 'root',
];
//合并数组
return array_merge($app, $mysql);
3、在config/database.php
中改为:
return [
// 默认数据库
'default' => 'mysql',
// 各种数据库配置
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env("DB_HOST", '127.0.0.1'),
'port' => env("DB_PORT", 3306),
'database' => 'test',
'username' => env('DB_USER', 'root'),
'password' => env('DB_PASSWORD', 'root'),
'unix_socket' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
本文转自:https://www.workerman.net/q/7564
https://www.usr.cn/Product/176.html
server
{
listen 80;
server_name xxx.webman.xxx;
index index.html;
root /server/webman/public;
location / {
proxy_http_version 1.1;
proxy_set_header Connection "keep-alive";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-Scheme $scheme;
if ($uri = /) {
proxy_pass http://127.0.0.1:8787;
break;
}
if (!-e $request_filename) {
proxy_pass http://127.0.0.1:8787;
}
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
{
expires 30d;
error_log /dev/null;
access_log /dev/null;
}
access_log /dev/null;
error_log /server/logs/webman.error.log;
}