diff --git a/addons/RfMerchants/backend/controllers/AuthItemController.php b/addons/RfMerchants/backend/controllers/AuthItemController.php index 34557f781..c87e11d7c 100644 --- a/addons/RfMerchants/backend/controllers/AuthItemController.php +++ b/addons/RfMerchants/backend/controllers/AuthItemController.php @@ -77,7 +77,7 @@ public function actionAjaxEdit() return $this->renderAjax($this->viewPrefix . 'auth-item/ajax-edit', [ 'model' => $model, - 'dropDownList' => Yii::$app->services->authItem->getDropDownForEdit($id), + 'dropDownList' => Yii::$app->services->authItem->getDropDownForEdit(AppEnum::MERCHANT, $id), ]); } } \ No newline at end of file diff --git a/api/config/main.php b/api/config/main.php index 2b2609415..2a5de0e48 100644 --- a/api/config/main.php +++ b/api/config/main.php @@ -80,6 +80,7 @@ 'v1/common/provinces', 'v1/member/member', 'v1/member/address', + 'v1/member/invoice', 'v1/member/auth', // 版本2 'v2/default', // 默认测试入口 diff --git a/api/controllers/ActiveController.php b/api/controllers/ActiveController.php index 059c2d112..0709ce2cd 100644 --- a/api/controllers/ActiveController.php +++ b/api/controllers/ActiveController.php @@ -87,7 +87,7 @@ public function behaviors() * yii\filters\auth\HttpHeaderAuth::class, */ // HttpBasicAuth::class, - // HttpBearerAuth::class, + HttpBearerAuth::class, HttpHeaderAuth::class, [ 'class' => QueryParamAuth::class, @@ -121,6 +121,20 @@ public function behaviors() return $behaviors; } + /** + * {@inheritdoc} + */ + protected function verbs() + { + return [ + 'index' => ['GET', 'HEAD', 'OPTIONS'], + 'view' => ['GET', 'HEAD', 'OPTIONS'], + 'create' => ['POST', 'OPTIONS'], + 'update' => ['PUT', 'PATCH', 'OPTIONS'], + 'delete' => ['DELETE', 'OPTIONS'], + ]; + } + /** * 前置操作验证token有效期和记录日志和检查curd权限 * diff --git a/api/modules/v1/controllers/SiteController.php b/api/modules/v1/controllers/SiteController.php index 8940a378b..ab872a2ff 100644 --- a/api/modules/v1/controllers/SiteController.php +++ b/api/modules/v1/controllers/SiteController.php @@ -66,7 +66,7 @@ public function actionLogout() return ResultHelper::api(200, '退出成功'); } - return ResultHelper::api(200, '退出失败'); + return ResultHelper::api(422, '退出失败'); } /** diff --git a/api/modules/v1/controllers/member/InvoiceController.php b/api/modules/v1/controllers/member/InvoiceController.php new file mode 100644 index 000000000..2c37f3cff --- /dev/null +++ b/api/modules/v1/controllers/member/InvoiceController.php @@ -0,0 +1,21 @@ + + */ +class InvoiceController extends UserAuthController +{ + /** + * @var Invoice + */ + public $modelClass = Invoice::class; +} \ No newline at end of file diff --git a/backend/modules/base/views/member/_form.php b/backend/modules/base/views/member/_form.php index 40d226941..93ad9855d 100644 --- a/backend/modules/base/views/member/_form.php +++ b/backend/modules/base/views/member/_form.php @@ -3,6 +3,7 @@ use yii\widgets\ActiveForm; use common\helpers\Url; use common\enums\GenderEnum; +use common\helpers\Html; $actionLog = Yii::$app->services->actionLog->findByAppId(Yii::$app->id, $model['id'], 10); diff --git a/backend/modules/common/views/action-log/index.php b/backend/modules/common/views/action-log/index.php index b63b0bd55..5a5a5f2a0 100644 --- a/backend/modules/common/views/action-log/index.php +++ b/backend/modules/common/views/action-log/index.php @@ -40,6 +40,7 @@ return Yii::$app->services->backend->getUserName($model); }, 'filter' => false, //不显示搜索框 + 'format' => 'raw', ], 'behavior', 'url', diff --git a/backend/modules/common/views/addons/template/controllers/BaseController.php b/backend/modules/common/views/addons/template/controllers/BaseController.php index f83899f91..43cab2388 100644 --- a/backend/modules/common/views/addons/template/controllers/BaseController.php +++ b/backend/modules/common/views/addons/template/controllers/BaseController.php @@ -1,5 +1,7 @@ @@ -19,7 +21,7 @@ class BaseController extends AddonsController /** * @var string */ - + // public $layout = "@addons/name;?>//views/layouts/main"; public $layout = "@addons/name;?>//views/layouts/main"; diff --git a/backend/modules/common/views/log/index.php b/backend/modules/common/views/log/index.php index a68772528..4f9e2b62c 100644 --- a/backend/modules/common/views/log/index.php +++ b/backend/modules/common/views/log/index.php @@ -50,6 +50,7 @@ return Yii::$app->services->backend->getUserName($model); }, 'filter' => false, //不显示搜索框 + 'format' => 'raw', ], 'url', [ diff --git a/backend/modules/common/views/menu-cate/index.php b/backend/modules/common/views/menu-cate/index.php index 09bae838a..1e26c5f8d 100644 --- a/backend/modules/common/views/menu-cate/index.php +++ b/backend/modules/common/views/menu-cate/index.php @@ -31,10 +31,7 @@ //重新定义分页样式 'tableOptions' => ['class' => 'table table-hover'], 'columns' => [ - [ - 'class' => 'yii\grid\SerialColumn', - 'visible' => true, // 不显示# - ], + 'id', [ 'attribute' => 'title', 'value' => function ($model) { diff --git a/backend/modules/common/views/system/info.php b/backend/modules/common/views/system/info.php index 8be76879f..5eaff9386 100644 --- a/backend/modules/common/views/system/info.php +++ b/backend/modules/common/views/system/info.php @@ -56,7 +56,7 @@
- + @@ -68,7 +68,7 @@ - + diff --git a/backend/widgets/notify/views/_nav.php b/backend/widgets/notify/views/_nav.php index 332027cb8..22aedb025 100644 --- a/backend/widgets/notify/views/_nav.php +++ b/backend/widgets/notify/views/_nav.php @@ -7,9 +7,9 @@ diff --git a/backend/widgets/notify/views/notify.php b/backend/widgets/notify/views/notify.php index 00922d5a5..525490bdd 100644 --- a/backend/widgets/notify/views/notify.php +++ b/backend/widgets/notify/views/notify.php @@ -34,6 +34,6 @@ - + \ No newline at end of file diff --git a/common/components/BaseWebSocket.php b/common/components/BaseWebSocket.php new file mode 100644 index 000000000..03e2ef4d7 --- /dev/null +++ b/common/components/BaseWebSocket.php @@ -0,0 +1,28 @@ + + */ +class BaseWebSocket +{ + /** + * @var Server + */ + public $server; + public $frame; + public $content; + + /** + * 测试 + */ + public function actionIndex() + { + $this->server->push($this->frame->fd, '测试'); + } +} \ No newline at end of file diff --git a/common/components/Init.php b/common/components/Init.php index 41d6dfdc0..85c8c66cd 100644 --- a/common/components/Init.php +++ b/common/components/Init.php @@ -49,7 +49,12 @@ public function bootstrap($application) $identity = Yii::$app->user->identity; $this->afreshLoad($identity->merchant_id ?? $this->default_merchant_id); } else { - $this->afreshLoad(Yii::$app->request->get('merchant_id', $this->default_merchant_id)); + $merchant_id = Yii::$app->request->headers->get('merchant-id', ''); + if (empty($merchant_id)) { + $merchant_id = Yii::$app->request->get('merchant_id', $this->default_merchant_id); + } + + $this->afreshLoad($merchant_id); } } diff --git a/common/components/WebSocketServer.php b/common/components/WebSocketServer.php new file mode 100644 index 000000000..31999006e --- /dev/null +++ b/common/components/WebSocketServer.php @@ -0,0 +1,175 @@ + + */ +class WebSocketServer +{ + protected $host; + protected $port; + protected $mode; + protected $socket_type; + protected $type; + protected $config; + + /** + * 子服务 + * + * @var + */ + public $childService = [ + 'test' => BaseWebSocket::class, + ]; + + protected $_childService; + + /** + * 服务 + * + * @var Server + */ + protected $server; + + /** + * WebSocket constructor. + * @param $host + * @param $port + * @param $config + */ + public function __construct($host, $port, $mode, $socket_type, $type, $config) + { + $this->host = $host; + $this->port = $port; + $this->mode = $mode; + $this->socket_type = $socket_type; + $this->type = $type; + $this->config = $config; + } + + /** + * 启动进程 + */ + public function run() + { + if ($this->type == 'wss') { + $this->server = new Server($this->host, $this->port, $this->mode, $this->socket_type | SWOOLE_SSL); + } else { + $this->server = new Server($this->host, $this->port, $this->mode); + } + + $this->server->set($this->config); + $this->server->on('open', [$this, 'onOpen']); + $this->server->on('message', [$this, 'onMessage']); + $this->server->on('task', [$this, 'onTask']); + $this->server->on('finish', [$this, 'onFinish']); + $this->server->on('close', [$this, 'onClose']); + $this->server->start(); + } + + /** + * 开启连接 + * + * @param $server + * @param $frame + */ + public function onOpen(Server $server, $request) + { + echo "server: handshake success with fd{$request->fd}\n"; + } + + /** + * 消息 + * @param $server + * @param $frame + * @throws \Exception + */ + public function onMessage(Server $server, $frame) + { + // 消息 + $data = Json::decode($frame->data); + $runAction = explode('/', $data['action']); + $content = $data['content']; + + try { + // 对应方法 + $action = 'action' . ucfirst(strtolower($runAction[1])); + $this->childService($runAction[0], $server, $frame, $content)->$action(); + } catch (\Exception $e) { + $server->push($frame->fd, "出现了报错:" . $e->getMessage()); + } + + echo "receive from {}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + } + + /** + * 关闭连接 + * + * @param $server + * @param $fd + */ + public function onClose(Server $server, $fd) + { + echo "client {$fd} closed" . PHP_EOL; + } + + /** + * 处理异步任务 + * + * @param $server + * @param $task_id + * @param $from_id + * @param $data + */ + public function onTask(Server $server, $task_id, $from_id, $data) + { + echo "新 AsyncTask[id=$task_id]" . PHP_EOL; + $server->finish($data); + } + + /** + * 处理异步任务的结果 + * + * @param $server + * @param $task_id + * @param $data + */ + public function onFinish(Server $server, $task_id, $data) + { + echo "AsyncTask[$task_id] 完成: $data" . PHP_EOL; + } + + /** + * 获取 services 里面配置的子服务 childService 的实例 + * + * @param $childServiceName + * @return mixed + * @throws InvalidConfigException + */ + protected function childService($childServiceName, $server, $frame, $content) + { + if (!isset($this->_childService[$childServiceName])) { + $childService = $this->childService; + + if (isset($childService[$childServiceName])) { + $service = $childService[$childServiceName]; + $this->_childService[$childServiceName] = Yii::createObject($service); + } else { + throw new InvalidConfigException('Child Service [' . $childServiceName . '] is not find in ' . get_called_class() . ', you must config it! '); + } + } + + $this->_childService[$childServiceName]->server = $server; + $this->_childService[$childServiceName]->frame = $frame; + $this->_childService[$childServiceName]->content = $content; + return $this->_childService[$childServiceName]; + } +} \ No newline at end of file diff --git a/common/config/main.php b/common/config/main.php index 01b90b7f0..37cfadd01 100644 --- a/common/config/main.php +++ b/common/config/main.php @@ -1,7 +1,7 @@ 'RageFrame', - 'version' => '2.4.21', + 'version' => '2.4.27', 'aliases' => [ '@bower' => '@vendor/bower-asset', '@npm' => '@vendor/npm-asset', diff --git a/common/enums/AuthMenuEnum.php b/common/enums/AuthMenuEnum.php new file mode 100644 index 000000000..1578aaed7 --- /dev/null +++ b/common/enums/AuthMenuEnum.php @@ -0,0 +1,27 @@ + + */ +class AuthMenuEnum extends BaseEnum +{ + const DEDAULT = 0; + const LEFT = 1; + const TOP = 2; + + /** + * @return array + */ + public static function getMap(): array + { + return [ + self::DEDAULT => '默认', + self::LEFT => '左侧菜单', + self::TOP => '顶部菜单', + ]; + } +} \ No newline at end of file diff --git a/common/enums/InvoiceTypeEnum.php b/common/enums/InvoiceTypeEnum.php new file mode 100644 index 000000000..01f63e3e3 --- /dev/null +++ b/common/enums/InvoiceTypeEnum.php @@ -0,0 +1,25 @@ + + */ +class InvoiceTypeEnum extends BaseEnum +{ + const COMPANY = 1; + const PERSONAGE = 2; + + /** + * @return array + */ + public static function getMap(): array + { + return [ + self::COMPANY => '公司', + self::PERSONAGE => '个人', + ]; + } +} \ No newline at end of file diff --git a/common/enums/SortEnum.php b/common/enums/SortEnum.php new file mode 100644 index 000000000..d9ccea8e0 --- /dev/null +++ b/common/enums/SortEnum.php @@ -0,0 +1,25 @@ + + */ +class SortEnum extends BaseEnum +{ + const DESC = 'desc'; + const ASC = 'asc'; + + /** + * @return array + */ + public static function getMap(): array + { + return [ + self::DESC => '降序', + self::ASC => '升序', + ]; + } +} \ No newline at end of file diff --git a/common/helpers/Html.php b/common/helpers/Html.php index 5441fe61d..443e7a882 100644 --- a/common/helpers/Html.php +++ b/common/helpers/Html.php @@ -248,11 +248,15 @@ public static function modelBaseCss() */ protected static function beforVerify($route) { + // 未登录直接放行 + if (Yii::$app->user->isGuest) { + return true; + } + is_array($route) && $route = $route[0]; - $prefix = ''; - substr("$route", 0, 1) != '/' && $prefix = '/'; - $route = $prefix . Url::getAuthUrl($route); + $route = Url::getAuthUrl($route); + substr("$route", 0, 1) != '/' && $route = '/' . $route; // 判断是否在模块内容 if (true === Yii::$app->params['inAddon']) { diff --git a/common/helpers/ImageHelper.php b/common/helpers/ImageHelper.php index a0631e880..ba48dd275 100644 --- a/common/helpers/ImageHelper.php +++ b/common/helpers/ImageHelper.php @@ -12,6 +12,18 @@ */ class ImageHelper { + /** + * 默认图片 + * + * @param $imgSrc + * @param string $defaultImgSre + * @return string + */ + public static function default($imgSrc, $defaultImgSre = '/resources/img/error.png') + { + return !empty($imgSrc) ? $imgSrc : Yii::getAlias('@web') . $defaultImgSre; + } + /** * 默认头像 * diff --git a/common/models/member/Invoice.php b/common/models/member/Invoice.php new file mode 100644 index 000000000..9131c54f3 --- /dev/null +++ b/common/models/member/Invoice.php @@ -0,0 +1,101 @@ + InvoiceTypeEnum::getKeys()], + [['type'], 'verifyType'], + [['merchant_id', 'member_id', 'is_default', 'type', 'status', 'created_at', 'updated_at'], 'integer'], + [['title', 'duty_paragraph'], 'string', 'max' => 200], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'merchant_id' => '商户id', + 'member_id' => '用户id', + 'title' => '发票抬头', + 'duty_paragraph' => '税号', + 'is_default' => '默认', + 'type' => '类型', // 1企业 2个人 + 'status' => '状态', + 'created_at' => '创建时间', + 'updated_at' => '修改时间', + ]; + } + + /** + * @param $attribute + */ + public function verifyType($attribute) + { + if ($this->type == InvoiceTypeEnum::COMPANY && !$this->duty_paragraph) { + $this->addError($attribute, '请填写税号'); + } else { + $this->duty_paragraph = ''; + } + } + + /** + * 关联用户 + * + * @return \yii\db\ActiveQuery + */ + public function getMember() + { + return $this->hasOne(Member::class, ['id' => 'member_id']); + } + + /** + * @param bool $insert + * @return bool + */ + public function beforeSave($insert) + { + if ($this->is_default == StatusEnum::ENABLED) { + self::updateAll(['is_default' => StatusEnum::DISABLED], ['member_id' => $this->member_id, 'is_default' => StatusEnum::ENABLED]); + } + + return parent::beforeSave($insert); + } +} diff --git a/common/models/merchant/Member.php b/common/models/merchant/Member.php index eafc2919f..0627f45a8 100644 --- a/common/models/merchant/Member.php +++ b/common/models/merchant/Member.php @@ -13,7 +13,7 @@ * This is the model class for table "{{%merchant_member}}". * * @property int $id - * @property string $merchant_id 商户id + * @property int $merchant_id 商户id * @property string $username 帐号 * @property string $password_hash 密码 * @property string $auth_key 授权令牌 diff --git a/console/config/main.php b/console/config/main.php index 3000f4fd0..639eb9246 100644 --- a/console/config/main.php +++ b/console/config/main.php @@ -24,6 +24,23 @@ 'migrate' => [ 'class' => 'jianyan\migration\ConsoleController', ], + /** ------ websocket ------ **/ + 'websocket' => [ + 'class' => 'console\controllers\WebSocketController', + 'server' => 'common\components\WebSocketServer', + 'host' => '0.0.0.0',// 监听地址 + 'port' => 9501, // 监听端口 + 'type' => 'ws', // 默认为ws连接,可修改为wss + 'config' => [ // 标准的swoole配置项都可以再此加入 + 'daemonize' => false, // 守护进程执行 + 'task_worker_num' => 4, //task进程的数量 + // 'ssl_cert_file' => '', + // 'ssl_key_file' => '', + 'pid_file' => __DIR__ . '/../../backend/runtime/logs/server.pid', + 'log_file' => __DIR__ . '/../../backend/runtime/logs/swoole.log', + 'log_level' => 0, + ], + ], ], 'components' => [ 'log' => [ diff --git a/console/controllers/WebSocketController.php b/console/controllers/WebSocketController.php new file mode 100644 index 000000000..632641c57 --- /dev/null +++ b/console/controllers/WebSocketController.php @@ -0,0 +1,192 @@ + + */ +class WebSocketController extends Controller +{ + /** + * 实例化服务 + * + * @var WebSocketServer + */ + public $server; + + /** + * 监听地址 + * + * @var string + */ + public $host = '0.0.0.0'; + + /** + * 监听端口 + * + * @var string + */ + public $port = 9501; + + /** + * 运行模式 + * + * @var int + */ + public $mode = SWOOLE_BASE; + + /** + * 传输协议 + * + * @var int + */ + public $socket_type = SWOOLE_SOCK_TCP; + + /** + * 长连接方式 + * + * @var string + */ + public $type = 'ws'; + + /** + * swoole 配置 + * + * @var array + */ + public $config = [ + 'daemonize' => false, // 守护进程执行 + 'task_worker_num' => 4,//task进程的数量 + 'ssl_cert_file' => '', + 'ssl_key_file' => '', + 'pid_file' => '', + 'buffer_output_size' => 2 * 1024 * 1024, //配置发送输出缓存区内存尺寸 + 'heartbeat_check_interval' => 60,// 心跳检测秒数 + 'heartbeat_idle_time' => 600,// 检查最近一次发送数据的时间和当前时间的差,大于则强行关闭 + ]; + + /** + * 启动 + * + * @throws \yii\base\Exception + */ + public function actionStart() + { + if ($this->getPid() !== false) { + $this->stderr("服务已经启动..."); + exit(1); + } + // 写入进程 + $this->setPid(); + /** @var WebSocketServer $websocket 运行 */ + $websocket = new $this->server( + $this->host, + $this->port, + $this->mode, + $this->socket_type, + $this->type, + $this->config + ); + $websocket->run(); + + $this->stdout("服务正在运行,监听 {$this->host}:{$this->port}" . PHP_EOL); + } + + /** + * 关闭进程 + */ + public function actionStop() + { + $this->sendSignal(SIGTERM); + $this->stdout("服务已经停止, 停止监听 {$this->host}:{$this->port}" . PHP_EOL); + } + + /** + * 重启进程 + * + * @throws \yii\base\Exception + */ + public function actionRestart() + { + $this->sendSignal(SIGTERM); + $time = 0; + while (posix_getpgid($this->getPid()) && $time <= 10) { + usleep(100000); + $time++; + } + if ($time > 100) { + $this->stderr("服务停止超时..." . PHP_EOL); + exit(1); + } + if ($this->getPid() === false) { + $this->stdout("服务重启成功..." . PHP_EOL); + } else { + $this->stderr("服务停止错误, 请手动处理杀死进程..." . PHP_EOL); + } + + $this->actionStart(); + } + + /** + * 发送信号 + * + * @param $sig + */ + private function sendSignal($sig) + { + if ($pid = $this->getPid()) { + posix_kill($pid, $sig); + } else { + $this->stdout("服务未运行..." . PHP_EOL); + exit(1); + } + } + + /** + * 获取pid进程 + * + * @return bool|string + */ + private function getPid() + { + $pid_file = $this->config['pid_file']; + if (file_exists($pid_file)) { + $pid = file_get_contents($pid_file); + if (posix_getpgid($pid)) { + return $pid; + } else { + unlink($pid_file); + } + } + + return false; + } + + /** + * 写入pid进程 + * + * @throws \yii\base\Exception + */ + private function setPid() + { + $parentPid = getmypid(); + $pidDir = dirname($this->config['pid_file']); + if (!file_exists($pidDir)) { + FileHelper::createDirectory($pidDir); + } + + file_put_contents($this->config['pid_file'], $parentPid + 1); + } +} \ No newline at end of file diff --git a/docs/guide-zh-CN/api-explain.md b/docs/guide-zh-CN/api-explain.md index fb33e1e43..3641969d6 100644 --- a/docs/guide-zh-CN/api-explain.md +++ b/docs/guide-zh-CN/api-explain.md @@ -22,23 +22,24 @@ v1 #### 北京时间格式 -YYYYmmddHHiiss +YYYY-mm-dd HH:ii:ss #### 公共入参说明 > 注意是通过Url传递 -> 例如 `http://www.example.com/api/v1/member/info?access-token=[access-token]` +> 例如 `http://www.example.com/api/v1/member/info Query 入参说明 参数名 | 参数类型| 必填 | 默认 | 说明 | 备注 ---|---|---|---|---|--- -access-token | string | 否 | 无 | 授权秘钥 | 需登录验证(出现401错误)必传 +access-token | string | 否 | 无 | 授权秘钥 | 需登录验证(出现401错误)必传,与下面的x-api-key 2选1即可 Header 入参说明 参数名 | 参数类型| 必填 | 默认 | 说明 | 备注 ---|---|---|---|---|--- +x-api-key | string | 否 | | 授权秘钥 | 与上面的access-token 2选1即可 device | string | 否 | | 设备类型 | ios/android device-uuid | string | 否 | | 设备唯一码 | ios: IDFA码;android:IMEI码; device-version | string | 否 | | 设备系统版本号 | diff --git a/docs/guide-zh-CN/api-pay.md b/docs/guide-zh-CN/api-pay.md index f266782f1..f8e800c6f 100644 --- a/docs/guide-zh-CN/api-pay.md +++ b/docs/guide-zh-CN/api-pay.md @@ -16,7 +16,7 @@ 参数名 | 参数类型 | 必填 | 默认 | 说明 | 备注 ---|---|---|---|---|--- -payType | int | 是 | 无 | 支付类型 | 0:余额支付;1:微信;2:支付宝;3:银联 | +payType | int | 是 | 无 | 支付类型 | 0:余额支付;1:微信;2:支付宝;3:银联;4:小程序 | tradeType | string | 是 | 无 | 交易类型 | 具体查看下文具体参数说明 | orderGroup | string | 是 | 无 | 订单类型 | default:默认;goods:订单商品 | data | string | 是 | 无 | json格式数组具体看下文 | @@ -29,6 +29,8 @@ tradeType 支付宝:'native', 'app', 'js', 'pos', 'mweb' 微信:'pc', 'app', 'f2f', 'wap' 银联:'app', 'html' +小程序:'default' +余额:'default' ``` 支付宝返回 diff --git a/docs/guide-zh-CN/helper-auth.md b/docs/guide-zh-CN/helper-auth.md index 48f334d84..15d206bee 100644 --- a/docs/guide-zh-CN/helper-auth.md +++ b/docs/guide-zh-CN/helper-auth.md @@ -5,6 +5,8 @@ - 校验权限是否拥有 - 批量校验权限是否拥有 +> 未登录默认不校验权限,注意需要判断权限的地方先引入登录认证 + 引入 ``` diff --git a/docs/guide-zh-CN/images/RageFrame2.png b/docs/guide-zh-CN/images/RageFrame2.png index 0cedbadbb..50f087330 100644 Binary files a/docs/guide-zh-CN/images/RageFrame2.png and b/docs/guide-zh-CN/images/RageFrame2.png differ diff --git a/docs/guide-zh-CN/start-update-log.md b/docs/guide-zh-CN/start-update-log.md index 94471d92f..85cdc05c5 100644 --- a/docs/guide-zh-CN/start-update-log.md +++ b/docs/guide-zh-CN/start-update-log.md @@ -9,6 +9,16 @@ > 各个小版本升级例如 2.1.x 升级到 2.2.x 以上不能完美升级最好重新安装 +### v2.4.27 +updated 2019.11.25 + +- 修复: 商户禁用未让用户登录失效 +- 修复: 设置 Redis 缓存时候设置省市区过期时间过长导致错误 +- 修复: 商家权限添加无法添加二级以下的权限 +- 修复: 按钮在个别情况下会导致权限验证不能通过 +- 修复: 微信图片资源路径错误 +- 升级: Yii2版本到2.0.30 + ### v2.4.21 updated 2019.11.15 diff --git a/docs/guide-zh-CN/sys-console.md b/docs/guide-zh-CN/sys-console.md index ea7349846..e31268d5c 100644 --- a/docs/guide-zh-CN/sys-console.md +++ b/docs/guide-zh-CN/sys-console.md @@ -19,7 +19,7 @@ yii pull-remind/sys yii addons/rf-wechat/msg-history/index // 定时群发微信消息(需安装微信插件) -yii addons/rf-wechat/send-message +yii addons/rf-wechat/send-message/index ``` ### 数据迁移 diff --git a/docs/guide-zh-CN/sys-exploit.md b/docs/guide-zh-CN/sys-exploit.md index 828b65306..d5389e151 100644 --- a/docs/guide-zh-CN/sys-exploit.md +++ b/docs/guide-zh-CN/sys-exploit.md @@ -72,7 +72,6 @@ - 尽可能避免使用外键约束 - 设置数据表架构应考虑后期扩展型 - 遵循范式与冗余平衡原则 -- 控制每张表的字段在20以内,否则业务分表 字段设计规范 diff --git a/html5/controllers/SiteController.php b/html5/controllers/SiteController.php index df026b0bc..d78796aa3 100644 --- a/html5/controllers/SiteController.php +++ b/html5/controllers/SiteController.php @@ -65,11 +65,18 @@ public function actionLogout() } /** - * 生成微信JSAPI支付的Demo方法 默认禁止外部访问 测试请修改方法类型 + * 生成微信JSAPI支付的Demo方法 + * + * 默认禁止外部访问 + * 测试请修改方法类型 + * + * 注意:请开启微信的安全支付路径 + * 域名/html5/site * * @return string + * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException - * @throws \yii\base\InvalidConfigException + * @throws \GuzzleHttp\Exception\GuzzleException */ private function actionWechatPay() { diff --git a/merchant/config/params.php b/merchant/config/params.php index 2f5469482..1baec18f7 100644 --- a/merchant/config/params.php +++ b/merchant/config/params.php @@ -8,6 +8,8 @@ // 多商户开启 'merchantOpen' => true, + // 登陆后的当前商户信息 + 'merchant' => '', /** ------ 总管理员配置 ------ **/ 'adminAccount' => '0',// 系统管理员账号id diff --git a/merchant/controllers/BaseController.php b/merchant/controllers/BaseController.php index b442b2fd7..7f350b19a 100644 --- a/merchant/controllers/BaseController.php +++ b/merchant/controllers/BaseController.php @@ -2,6 +2,7 @@ namespace merchant\controllers; +use common\enums\StatusEnum; use Yii; use yii\web\Controller; use yii\filters\AccessControl; @@ -52,6 +53,15 @@ public function beforeAction($action) return false; } + // 判断商户的有效性 + if (!($merchant = Yii::$app->services->merchant->findByLogin()) || $merchant->status != StatusEnum::ENABLED) { + Yii::$app->user->logout(); + + throw new UnauthorizedHttpException('对不起,您还无法登陆请联系管理员'); + } + + Yii::$app->params['merchant'] = $merchant; + // 每页数量 $this->pageSize = Yii::$app->request->get('per-page', 10); $this->pageSize > 50 && $this->pageSize = 50; diff --git a/merchant/forms/LoginForm.php b/merchant/forms/LoginForm.php index 238fba5b8..6cdba5f15 100644 --- a/merchant/forms/LoginForm.php +++ b/merchant/forms/LoginForm.php @@ -2,9 +2,11 @@ namespace merchant\forms; +use common\enums\StatusEnum; use Yii; use common\helpers\StringHelper; use common\models\merchant\Member; +use yii\web\UnauthorizedHttpException; /** * Class LoginForm @@ -37,6 +39,7 @@ public function rules() ['rememberMe', 'boolean'], ['password', 'validatePassword'], ['password', 'validateIp'], + ['password', 'validateMerchant'], ['verifyCode', 'captcha', 'on' => 'captchaRequired'], ]; } @@ -73,6 +76,19 @@ public function validateIp($attribute) } } + /** + * @param $attribute + */ + public function validateMerchant($attribute) + { + /** @var Member $user */ + if ($user = $this->getUser()) { + if (!($merchant = Yii::$app->services->merchant->findById($user->merchant_id)) || $merchant->status != StatusEnum::ENABLED) { + $this->addError($attribute, '无法登陆请联系管理员'); + } + } + } + /** * @return mixed|null|static */ diff --git a/services/Application.php b/services/Application.php index e7cde3147..05435c71a 100644 --- a/services/Application.php +++ b/services/Application.php @@ -20,6 +20,7 @@ * @property \services\member\AuthService $memberAuth 会员第三方授权 * @property \services\member\AccountService $memberAccount 会员账号 * @property \services\member\AddressService $memberAddress 会员收货地址 + * @property \services\member\InvoiceService $memberInvoice 会员发票 * @property \services\member\CreditsLogService $memberCreditsLog 会员积分/余额变动日志 * @property \services\common\ActionLogService $actionLog 行为日志 * @property \services\common\ActionBehaviorService $actionBehavior 可被记录的行为 @@ -66,6 +67,7 @@ class Application extends Service 'memberAuth' => 'services\member\AuthService', 'memberAccount' => 'services\member\AccountService', 'memberAddress' => 'services\member\AddressService', + 'memberInvoice' => 'services\member\InvoiceService', 'memberCreditsLog' => 'services\member\CreditsLogService', /** ------ 商户 ------ **/ 'merchant' => 'services\merchant\MerchantService', diff --git a/services/api/AccessTokenService.php b/services/api/AccessTokenService.php index 86698d6e3..776f7b059 100644 --- a/services/api/AccessTokenService.php +++ b/services/api/AccessTokenService.php @@ -113,8 +113,10 @@ public function disableByAccessToken($access_token) if ($model = $this->findByAccessToken($access_token)) { $model->status = StatusEnum::DISABLED; - $model->save(); + return $model->save(); } + + return false; } /** diff --git a/services/backend/BackendService.php b/services/backend/BackendService.php index 7ad914c5a..d2048cf12 100644 --- a/services/backend/BackendService.php +++ b/services/backend/BackendService.php @@ -39,16 +39,57 @@ public function getUserName($model) { switch ($model->app_id) { case AppEnum::BACKEND : - return $model->backendMember->username ?? '游客'; + if (!empty($model->backendMember)) { + $str = []; + $str[] = 'ID:' . $model->backendMember->id; + $str[] = '账号:' . $model->backendMember->username; + $str[] = '姓名:' . $model->backendMember->realname; + + return implode($str, '
'); + } + + return '游客'; break; case AppEnum::MERCHANT : - return $model->merchantMember->username ?? '游客'; + if (!empty($model->merchantMember)) { + $str = []; + $str[] = 'ID:' . $model->merchantMember->id; + $str[] = '账号:' . $model->merchantMember->username; + $str[] = '姓名:' . $model->merchantMember->realname; + + return implode($str, '
'); + } + + return '游客'; + break; case AppEnum::OAUTH2 : - return $model->oauth2Member->username ?? '游客'; + if (!empty($model->oauth2Member)) { + $str = []; + $str[] = 'ID:' . $model->oauth2Member->id; + $str[] = '账号:' . $model->oauth2Member->username; + $str[] = '昵称:' . $model->oauth2Member->nickname; + $str[] = '姓名:' . $model->oauth2Member->realname; + + return implode($str, '
'); + } + + return '游客'; + break; default : - return $model->member->username ?? '游客'; + if (!empty($model->member)) { + $str = []; + $str[] = 'ID:' . $model->member->id; + $str[] = '账号:' . $model->member->username; + $str[] = '昵称:' . $model->member->nickname; + $str[] = '姓名:' . $model->member->realname; + + return implode($str, '
'); + } + + return '游客'; + break; } } diff --git a/services/common/AuthItemService.php b/services/common/AuthItemService.php index b9cd18405..3c18f82c0 100644 --- a/services/common/AuthItemService.php +++ b/services/common/AuthItemService.php @@ -2,6 +2,7 @@ namespace services\common; +use common\enums\AuthMenuEnum; use Yii; use yii\web\UnprocessableEntityHttpException; use common\components\Service; @@ -97,12 +98,8 @@ public function createByAddons($allAuthItem, $allMenu, $removeAppIds, $name) $menu = ArrayHelper::getColumn(ArrayHelper::getRowsByItemsMerge($menu, 'child'), 'route'); } - $is_menu = 1; - // 是否菜单被移除 - if (in_array($key, $removeAppIds)) { - $is_menu = 2; - } - + // 菜单类型 + $is_menu = in_array($key, $removeAppIds) ? AuthMenuEnum::TOP : AuthMenuEnum::LEFT; $allAuth = ArrayHelper::merge($allAuth, $this->regroupByAddonsData($item, $menu, $is_menu, $name, $key)); } diff --git a/services/common/PayService.php b/services/common/PayService.php index 443e3cae8..448c769a9 100644 --- a/services/common/PayService.php +++ b/services/common/PayService.php @@ -44,6 +44,7 @@ public function wechat(PayForm $payForm, $baseOrder) // 交易类型 $tradeType = $payForm->tradeType; + return Yii::$app->pay->wechat->$tradeType($order); } @@ -58,7 +59,7 @@ public function alipay(PayForm $payForm, $baseOrder) $config = [ 'notify_url' => $payForm->notifyUrl, // 支付通知回调地址 'return_url' => $payForm->returnUrl, // 买家付款成功跳转地址 - 'sandbox' => false + 'sandbox' => false, ]; // 生成订单 @@ -70,8 +71,9 @@ public function alipay(PayForm $payForm, $baseOrder) // 交易类型 $tradeType = $payForm->tradeType; + return [ - 'config' => Yii::$app->pay->alipay($config)->$tradeType($order) + 'config' => Yii::$app->pay->alipay($config)->$tradeType($order), ]; } @@ -98,20 +100,23 @@ public function union(PayForm $payForm, $baseOrder) // 交易类型 $tradeType = $payForm->tradeType; + return Yii::$app->pay->union($config)->$tradeType($order); } /** * @param PayForm $payForm + * @param $baseOrder * @return array + * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException - * @throws \yii\base\InvalidConfigException + * @throws \GuzzleHttp\Exception\GuzzleException */ public function miniProgram(PayForm $payForm, $baseOrder) { // 设置appid Yii::$app->params['wechatPaymentConfig'] = ArrayHelper::merge(Yii::$app->params['wechatPaymentConfig'], [ - 'app_id' => Yii::$app->debris->config('miniprogram_appid') + 'app_id' => Yii::$app->debris->config('miniprogram_appid'), ]); $orderData = [ @@ -126,6 +131,7 @@ public function miniProgram(PayForm $payForm, $baseOrder) $payment = Yii::$app->wechat->payment; $result = $payment->order->unify($orderData); + return $payment->jssdk->sdkConfig($result['prepay_id']); } diff --git a/services/common/ProvincesService.php b/services/common/ProvincesService.php index eb84e5bfc..86036beb7 100644 --- a/services/common/ProvincesService.php +++ b/services/common/ProvincesService.php @@ -192,7 +192,7 @@ public function findAllInCache() ->asArray() ->all(); - Yii::$app->cache->set($cacheKey, $data, 60 * 60 * 24 * 30); + Yii::$app->cache->set($cacheKey, $data, 60 * 60 * 24 * 24); } return $data; diff --git a/services/common/SmsService.php b/services/common/SmsService.php index 1e09bf72c..cbea55a66 100644 --- a/services/common/SmsService.php +++ b/services/common/SmsService.php @@ -109,7 +109,7 @@ public function send($mobile, $code, $usage, $member_id = 0) public function realSend($mobile, $code, $usage, $member_id = 0) { $template = Yii::$app->debris->config('sms_aliyun_template'); - !empty($template) && $template = ArrayHelper::map(unserialize($template), 'group', 'template'); + !empty($template) && $template = ArrayHelper::map(Json::decode($template), 'group', 'template'); $templateID = $template[$usage] ?? ''; try { diff --git a/services/member/CreditsLogService.php b/services/member/CreditsLogService.php index 6b5bcf51e..b3e15b43e 100644 --- a/services/member/CreditsLogService.php +++ b/services/member/CreditsLogService.php @@ -17,7 +17,7 @@ class CreditsLogService extends Service { /** - * 类型 + * 字段类型 * * @var */ @@ -33,6 +33,13 @@ class CreditsLogService extends Service */ protected $newNum; + /** + * 累计字段 + * + * @var string + */ + protected $accumulate; + /** * 增加积分 * @@ -41,24 +48,31 @@ class CreditsLogService extends Service */ public function incrInt(CreditsLogForm $creditsLogForm) { - if ($creditsLogForm->num <= 0) { + if ($creditsLogForm->num < 0) { return; } - $creditsLogForm->num = abs($creditsLogForm->num); - /** @var Account $account */ $account = $creditsLogForm->member->account; + if ($creditsLogForm->num == 0) { + $this->creditType = CreditsLog::CREDIT_TYPE_USER_INTEGRAL; + $this->oldNum = $account->user_integral; + $this->newNum = $account->user_integral; + $this->create($creditsLogForm); + + return; + } + + $creditsLogForm->num = abs($creditsLogForm->num); $this->creditType = CreditsLog::CREDIT_TYPE_USER_INTEGRAL; $this->oldNum = $account->user_integral; $account->user_integral += $creditsLogForm->num; - $account->accumulate_integral += $creditsLogForm->num; + $this->accumulate = 'accumulate_integral'; $this->newNum = $account->user_integral; - if (!$account->save()) { - throw new NotFoundHttpException($this->getError($account)); - } - + // 更新账户 + $this->updateAccount($account); + // 记录日志 $this->create($creditsLogForm); } @@ -71,13 +85,22 @@ public function incrInt(CreditsLogForm $creditsLogForm) */ public function decrInt(CreditsLogForm $creditsLogForm) { - if ($creditsLogForm->num <= 0) { + if ($creditsLogForm->num < 0) { return; } - $creditsLogForm->num = - abs($creditsLogForm->num); /** @var Account $account */ $account = $creditsLogForm->member->account; + if ($creditsLogForm->num == 0) { + $this->creditType = CreditsLog::CREDIT_TYPE_USER_INTEGRAL; + $this->oldNum = $account->user_integral; + $this->newNum = $account->user_integral; + $this->create($creditsLogForm); + + return; + } + + $creditsLogForm->num = -abs($creditsLogForm->num); $this->creditType = CreditsLog::CREDIT_TYPE_USER_INTEGRAL; $this->oldNum = $account->user_integral; $account->user_integral += $creditsLogForm->num; @@ -87,10 +110,9 @@ public function decrInt(CreditsLogForm $creditsLogForm) throw new NotFoundHttpException('积分不足'); } - if (!$account->save()) { - throw new NotFoundHttpException($this->getError($account)); - } - + // 更新账户 + $this->updateAccount($account); + // 记录日志 $this->create($creditsLogForm); } @@ -102,23 +124,32 @@ public function decrInt(CreditsLogForm $creditsLogForm) */ public function incrMoney(CreditsLogForm $creditsLogForm) { - if ($creditsLogForm->num <= 0) { + if ($creditsLogForm->num < 0) { return; } - $creditsLogForm->num = abs($creditsLogForm->num); /** @var Account $account */ $account = $creditsLogForm->member->account; + // 增加金额为0不变更 + if ($creditsLogForm->num == 0) { + $this->creditType = CreditsLog::CREDIT_TYPE_USER_MONEY; + $this->oldNum = $account->user_money; + $this->newNum = $account->user_money; + $this->create($creditsLogForm); + + return; + } + + $creditsLogForm->num = abs($creditsLogForm->num); $this->creditType = CreditsLog::CREDIT_TYPE_USER_MONEY; $this->oldNum = $account->user_money; $account->user_money += $creditsLogForm->num; - $account->accumulate_money += $creditsLogForm->num; + $this->accumulate = 'accumulate_money'; $this->newNum = $account->user_money; - if (!$account->save()) { - throw new NotFoundHttpException($this->getError($account)); - } - + // 更新账户 + $this->updateAccount($account); + // 记录日志 $model = $this->create($creditsLogForm); $creditsLogForm->map_id = $model->id; } @@ -131,13 +162,23 @@ public function incrMoney(CreditsLogForm $creditsLogForm) */ public function decrMoney(CreditsLogForm $creditsLogForm) { - if ($creditsLogForm->num <= 0) { + if ($creditsLogForm->num < 0) { return; } - $creditsLogForm->num = - abs($creditsLogForm->num); /** @var Account $account */ $account = $creditsLogForm->member->account; + // 消费金额为0不变更 + if ($creditsLogForm->num == 0) { + $this->creditType = CreditsLog::CREDIT_TYPE_USER_MONEY; + $this->oldNum = $account->user_money; + $this->newNum = $account->user_money; + $this->create($creditsLogForm); + + return; + } + + $creditsLogForm->num = -abs($creditsLogForm->num); $this->creditType = CreditsLog::CREDIT_TYPE_USER_MONEY; $this->oldNum = $account->user_money; $account->user_money += $creditsLogForm->num; @@ -147,14 +188,34 @@ public function decrMoney(CreditsLogForm $creditsLogForm) throw new NotFoundHttpException('余额不足'); } - if (!$account->save()) { - throw new NotFoundHttpException($this->getError($account)); - } - + // 更新账户 + $this->updateAccount($account); + // 记录日志 $model = $this->create($creditsLogForm); $creditsLogForm->map_id = $model->id; } + /** + * 更新账户,尽量不拒绝,宁可记录不正确 + * + * @param Account $account + * @throws NotFoundHttpException + */ + protected function updateAccount(Account $account) + { + $amount = $this->newNum - $this->oldNum; + + if ($amount > 0) { + if (!$account->updateAllCounters([$this->creditType => $amount, $this->accumulate => $amount], ['id' => $account->id])) { + throw new NotFoundHttpException('系统繁忙'); + } + } else { + if (!$account->updateAllCounters([$this->creditType => $amount], ['and', ['id' => $account->id], ['>=', $this->creditType, $amount]])) { + throw new NotFoundHttpException('余额不足'); + } + } + } + /** * 创建 * diff --git a/services/member/InvoiceService.php b/services/member/InvoiceService.php new file mode 100644 index 000000000..798a6e06a --- /dev/null +++ b/services/member/InvoiceService.php @@ -0,0 +1,56 @@ + + */ +class InvoiceService extends Service +{ + /** + * 获取默认地址 + * + * @param $member_id + * @return array|null|\yii\db\ActiveRecord|Invoice + */ + public function findDefaultByMemberId($member_id) + { + return Invoice::find() + ->where(['member_id' => $member_id, 'status' => StatusEnum::ENABLED, 'is_default' => StatusEnum::ENABLED]) + ->andFilterWhere(['merchant_id' => $this->getMerchantId()]) + ->one(); + } + + /** + * @param $id + * @param $member_id + * @return array|null|\yii\db\ActiveRecord + */ + public function findById($id, $member_id) + { + return Invoice::find() + ->where(['id' => $id, 'member_id' => $member_id, 'status' => StatusEnum::ENABLED]) + ->andFilterWhere(['merchant_id' => $this->getMerchantId()]) + ->one(); + } + + /** + * @param $member_id + * @return array|\yii\db\ActiveRecord[] + */ + public function findByMemberId($member_id) + { + return Invoice::find() + ->where(['member_id' => $member_id, 'status' => StatusEnum::ENABLED]) + ->andFilterWhere(['merchant_id' => $this->getMerchantId()]) + ->orderBy(['is_default desc']) + ->asArray() + ->all(); + } +} \ No newline at end of file diff --git a/services/merchant/MerchantService.php b/services/merchant/MerchantService.php index 2115657b0..04c7abf2b 100644 --- a/services/merchant/MerchantService.php +++ b/services/merchant/MerchantService.php @@ -3,6 +3,8 @@ namespace services\merchant; use common\components\Service; +use common\enums\StatusEnum; +use common\models\merchant\Merchant; /** * 商户 @@ -33,4 +35,23 @@ public function setId($merchant_id) { $this->merchant_id = $merchant_id; } + + /** + * @return array|\yii\db\ActiveRecord|null + */ + public function findByLogin() + { + return $this->findById($this->getId()); + } + + /** + * @return array|\yii\db\ActiveRecord|null + */ + public function findById($id) + { + return Merchant::find() + ->where(['status' => StatusEnum::ENABLED]) + ->andWhere(['id' => $id]) + ->one(); + } } \ No newline at end of file
系统全称系统全称 params['exploitFullName']; ?>
version; ?>
Yii2版本Yii2版本