Compare commits
64 Commits
c6a4e1f5f6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c77ef29846 | |||
| 349841a8a1 | |||
| e30de5cc79 | |||
| a00dd72640 | |||
| db29a0f7c0 | |||
| 335c6a6c68 | |||
| 5227396484 | |||
|
|
9ba854cc76 | ||
| 3ef8b70823 | |||
| 601dd0abd6 | |||
| 055521dc8e | |||
| 0432965b88 | |||
| 70baaa2fd5 | |||
| c9a8b9d5e8 | |||
| d788227ff1 | |||
| 9223c1b127 | |||
| 582c52e3a1 | |||
| 19bfe5e992 | |||
| 3feb90619c | |||
| 8fe0a460cd | |||
| 1b63962bff | |||
| 35725ffd2d | |||
| 2a1cec7909 | |||
| 681af2a1ca | |||
| a44f364914 | |||
| be7ee40690 | |||
| 6e655c6121 | |||
| 94a540ac96 | |||
| b7023e7ab3 | |||
| d48daea477 | |||
| 185578aa6a | |||
| ac0777e34b | |||
| 8481d8ef0c | |||
| c3b0a47e6d | |||
| 55af3cb570 | |||
| 6c4fc6bff6 | |||
| e420668a44 | |||
| 751c742726 | |||
| 8a68fb84af | |||
| c2478c1aea | |||
| 1dc3d9c516 | |||
| 1bd66c30ba | |||
| 6a5d81badc | |||
| 7934afeb04 | |||
| 4ff29bed97 | |||
| b7002e3e2a | |||
| 12adc51eeb | |||
| e0e42e3ecc | |||
| 1c53b73614 | |||
| c76019416a | |||
| 98eda4e5ff | |||
| 207e6b8a5d | |||
| 7101db7e5c | |||
| 6b3c4782a1 | |||
| 20a6248fb6 | |||
| 0304dc7494 | |||
| 9622062509 | |||
| 5765cb21d5 | |||
| 3213a01287 | |||
| 2886c4162b | |||
| 08a6dfdb6e | |||
| abfbb8d1ee | |||
| 0bd7421371 | |||
| 32612f3103 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
/runtime/*
|
/runtime/*
|
||||||
/public/uploads/*
|
/public/uploads/*
|
||||||
|
/public/storage/*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
composer.lock
|
composer.lock
|
||||||
@@ -11,3 +12,8 @@ composer.lock
|
|||||||
.vscode
|
.vscode
|
||||||
node_modules
|
node_modules
|
||||||
.user.ini
|
.user.ini
|
||||||
|
addons/alisms/config.php
|
||||||
|
addons/address/config.php
|
||||||
|
addons/epay/config.php
|
||||||
|
addons/hwobs/config.php
|
||||||
|
addons/*/config.php
|
||||||
|
|||||||
94
README.md
94
README.md
@@ -1,93 +1,5 @@
|
|||||||
FastAdmin是一款基于ThinkPHP+Bootstrap的极速后台开发框架。
|
FastAdmin是一款基于ThinkPHP+Bootstrap的极速后台开发框架。
|
||||||
|
|
||||||
|
### 队列启动命令
|
||||||
## 主要特性
|
- php think queue:work --daemon --queue shopro
|
||||||
|
- php think queue:work --daemon --queue shopro-high
|
||||||
* 基于`Auth`验证的权限管理系统
|
|
||||||
* 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置
|
|
||||||
* 支持单管理员多角色
|
|
||||||
* 支持管理子级数据或个人数据
|
|
||||||
* 强大的一键生成功能
|
|
||||||
* 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等
|
|
||||||
* 一键压缩打包JS和CSS文件,一键CDN静态资源部署
|
|
||||||
* 一键生成控制器菜单和规则
|
|
||||||
* 一键生成API接口文档
|
|
||||||
* 完善的前端功能组件开发
|
|
||||||
* 基于`AdminLTE`二次开发
|
|
||||||
* 基于`Bootstrap`开发,自适应手机、平板、PC
|
|
||||||
* 基于`RequireJS`进行JS模块管理,按需加载
|
|
||||||
* 基于`Less`进行样式开发
|
|
||||||
* 强大的插件扩展功能,在线安装卸载升级插件
|
|
||||||
* 通用的会员模块和API模块
|
|
||||||
* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证
|
|
||||||
* 二级域名部署支持,同时域名支持绑定到应用插件
|
|
||||||
* 多语言支持,服务端及客户端支持
|
|
||||||
* 支持大文件分片上传、剪切板粘贴上传、拖拽上传,进度条显示,图片上传前压缩
|
|
||||||
* 支持表格固定列、固定表头、跨页选择、Excel导出、模板渲染等功能
|
|
||||||
* 强大的第三方应用模块支持([CMS](https://www.fastadmin.net/store/cms.html)、[CRM](https://www.fastadmin.net/store/facrm.html)、[企业网站管理系统](https://www.fastadmin.net/store/ldcms.html)、[知识库文档系统](https://www.fastadmin.net/store/knowbase.html)、[在线投票系统](https://www.fastadmin.net/store/vote.html)、[B2C商城](https://www.fastadmin.net/store/shopro.html)、[B2B2C商城](https://www.fastadmin.net/store/wanlshop.html))
|
|
||||||
* 整合第三方短信接口(阿里云、腾讯云短信)
|
|
||||||
* 无缝整合第三方云存储(七牛云、阿里云OSS、腾讯云存储、又拍云)功能,支持云储存分片上传
|
|
||||||
* 第三方富文本编辑器支持(Summernote、百度编辑器)
|
|
||||||
* 第三方登录(QQ、微信、微博)整合
|
|
||||||
* 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付
|
|
||||||
* 丰富的插件应用市场
|
|
||||||
|
|
||||||
## 安装使用
|
|
||||||
|
|
||||||
https://doc.fastadmin.net
|
|
||||||
|
|
||||||
## 在线演示
|
|
||||||
|
|
||||||
https://demo.fastadmin.net
|
|
||||||
|
|
||||||
用户名:admin
|
|
||||||
|
|
||||||
密 码:123456
|
|
||||||
|
|
||||||
提 示:演示站数据无法进行修改,请下载源码安装体验全部功能
|
|
||||||
|
|
||||||
## 界面截图
|
|
||||||

|
|
||||||
|
|
||||||
## 问题反馈
|
|
||||||
|
|
||||||
在使用中有任何问题,请使用以下联系方式联系我们
|
|
||||||
|
|
||||||
问答社区: https://ask.fastadmin.net
|
|
||||||
|
|
||||||
Github: https://github.com/fastadminnet/fastadmin
|
|
||||||
|
|
||||||
Gitee: https://gitee.com/fastadminnet/fastadmin
|
|
||||||
|
|
||||||
## 特别鸣谢
|
|
||||||
|
|
||||||
感谢以下的项目,排名不分先后
|
|
||||||
|
|
||||||
ThinkPHP:http://www.thinkphp.cn
|
|
||||||
|
|
||||||
AdminLTE:https://adminlte.io
|
|
||||||
|
|
||||||
Bootstrap:http://getbootstrap.com
|
|
||||||
|
|
||||||
jQuery:http://jquery.com
|
|
||||||
|
|
||||||
Bootstrap-table:https://github.com/wenzhixin/bootstrap-table
|
|
||||||
|
|
||||||
Nice-validator: https://validator.niceue.com
|
|
||||||
|
|
||||||
SelectPage: https://github.com/TerryZ/SelectPage
|
|
||||||
|
|
||||||
Layer: https://layuion.com/layer/
|
|
||||||
|
|
||||||
DropzoneJS: https://www.dropzonejs.com
|
|
||||||
|
|
||||||
|
|
||||||
## 版权信息
|
|
||||||
|
|
||||||
FastAdmin遵循Apache2开源协议发布,并提供免费使用。
|
|
||||||
|
|
||||||
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
|
||||||
|
|
||||||
版权所有Copyright © 2017-2024 by FastAdmin (https://www.fastadmin.net)
|
|
||||||
|
|
||||||
All rights reserved。
|
|
||||||
67
add.sql
Normal file
67
add.sql
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
ALTER TABLE `zy_club` ADD COLUMN `intro` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '介绍' AFTER `blurb`;
|
||||||
|
ALTER TABLE `zy_club` ADD COLUMN `contect` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系方式' AFTER `img`;
|
||||||
|
ALTER TABLE `zy_club` ADD COLUMN `attention` int(11) NOT NULL DEFAULT 0 COMMENT '关注人数' AFTER `contect`;
|
||||||
|
-- 已执行 2025-05-18
|
||||||
|
|
||||||
|
ALTER TABLE `zy_circle` ADD COLUMN `top` int NOT NULL DEFAULT 0 COMMENT '置顶' AFTER `status`;
|
||||||
|
-- 已执行 2025-05-31
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE `zy_sign_record` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '用户id',
|
||||||
|
`date` date NOT NULL DEFAULT current_timestamp() COMMENT '签到日期',
|
||||||
|
`last` int(11) NOT NULL DEFAULT 1 COMMENT '已持续天数',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT current_timestamp() COMMENT '创建时间',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `user_id` (`user_id`,`last`) USING BTREE,
|
||||||
|
KEY `date` (`date`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='签到记录';
|
||||||
|
|
||||||
|
CREATE TABLE `zy_sign_set` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`last` tinyint(4) NOT NULL DEFAULT 1 COMMENT '连续天数',
|
||||||
|
`chance1` tinyint(4) NOT NULL DEFAULT 0 COMMENT '概率1',
|
||||||
|
`coupon1_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '券1',
|
||||||
|
`chance2` tinyint(4) NOT NULL DEFAULT 0 COMMENT '概率2',
|
||||||
|
`coupon2_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '券2',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT current_timestamp() COMMENT '创建时间',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `type` (`last`) USING BTREE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='签到设置';
|
||||||
|
|
||||||
|
ALTER TABLE `zy_sign_set`
|
||||||
|
ADD COLUMN `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态(0禁用,1启用)' AFTER `coupon2_id`;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `zy_sign`;
|
||||||
|
-- 已执行 2025-06-16
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE `zy_sign_record`
|
||||||
|
ADD COLUMN `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '获得礼物备注' AFTER `last`;
|
||||||
|
|
||||||
|
ALTER TABLE `zy_sign_set`
|
||||||
|
ADD COLUMN `name` varchar(255) NOT NULL DEFAULT '' COMMENT '名称' AFTER `status`,
|
||||||
|
ADD COLUMN `begin_date` date NULL DEFAULT NULL COMMENT '有效期开始' AFTER `name`,
|
||||||
|
ADD COLUMN `end_date` date NULL DEFAULT NULL COMMENT '有效期结束' AFTER `begin_date`,
|
||||||
|
ADD COLUMN `intro` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '说明' AFTER `end_date`;
|
||||||
|
-- 已执行 2025-06-17
|
||||||
|
|
||||||
|
CREATE TABLE `zy_visitor` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '类型(0俱乐部/1球馆/2用户)',
|
||||||
|
`obj_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '对象id',
|
||||||
|
`user_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '用户id',
|
||||||
|
`nickname` varchar(255) NOT NULL DEFAULT '' COMMENT '昵称',
|
||||||
|
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
|
||||||
|
`gender` tinyint(4) NOT NULL DEFAULT 0 COMMENT '性别(0女,1男)',
|
||||||
|
`times` int(11) NOT NULL DEFAULT 0 COMMENT '次数',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT current_timestamp() COMMENT '创建时间',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
KEY `type` (`type`) USING BTREE,
|
||||||
|
KEY `obj_id` (`obj_id`) USING BTREE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='标签';
|
||||||
|
-- 已执行 2025-06-28
|
||||||
1
addons/address/.addonrc
Normal file
1
addons/address/.addonrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":["public\\assets\\addons\\address\\js\\gcoord.min.js","public\\assets\\addons\\address\\js\\jquery.autocomplete.js"],"license":"regular","licenseto":"34485","licensekey":"Fr1gIoX2PBbGmMnp GFErXGAvLGllzXV\/ASjNRQ==","domains":["localhost"],"licensecodes":[],"validations":["757319b447175b6ca1882635b132a594"]}
|
||||||
32
addons/address/Address.php
Normal file
32
addons/address/Address.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\address;
|
||||||
|
|
||||||
|
use think\Addons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址选择
|
||||||
|
* @author [MiniLing] <[laozheyouxiang@163.com]>
|
||||||
|
*/
|
||||||
|
class Address extends Addons
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件安装方法
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function install()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件卸载方法
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function uninstall()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
34
addons/address/bootstrap.js
vendored
Normal file
34
addons/address/bootstrap.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
require([], function () {
|
||||||
|
//绑定data-toggle=addresspicker属性点击事件
|
||||||
|
|
||||||
|
$(document).on('click', "[data-toggle='addresspicker']", function () {
|
||||||
|
var that = this;
|
||||||
|
var callback = $(that).data('callback');
|
||||||
|
var input_id = $(that).data("input-id") ? $(that).data("input-id") : "";
|
||||||
|
var lat_id = $(that).data("lat-id") ? $(that).data("lat-id") : "";
|
||||||
|
var lng_id = $(that).data("lng-id") ? $(that).data("lng-id") : "";
|
||||||
|
var zoom_id = $(that).data("zoom-id") ? $(that).data("zoom-id") : "";
|
||||||
|
var lat = lat_id ? $("#" + lat_id).val() : '';
|
||||||
|
var lng = lng_id ? $("#" + lng_id).val() : '';
|
||||||
|
var zoom = zoom_id ? $("#" + zoom_id).val() : '';
|
||||||
|
var url = "/addons/address/index/select";
|
||||||
|
url += (lat && lng) ? '?lat=' + lat + '&lng=' + lng + (input_id ? "&address=" + $("#" + input_id).val() : "") + (zoom ? "&zoom=" + zoom : "") : '';
|
||||||
|
Fast.api.open(url, '位置选择', {
|
||||||
|
callback: function (res) {
|
||||||
|
input_id && $("#" + input_id).val(res.address).trigger("change");
|
||||||
|
lat_id && $("#" + lat_id).val(res.lat).trigger("change");
|
||||||
|
lng_id && $("#" + lng_id).val(res.lng).trigger("change");
|
||||||
|
zoom_id && $("#" + zoom_id).val(res.zoom).trigger("change");
|
||||||
|
|
||||||
|
try {
|
||||||
|
//执行回调函数
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback.call(that, res);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
132
addons/address/config.bak.php
Normal file
132
addons/address/config.bak.php
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name' => 'maptype',
|
||||||
|
'title' => '默认地图类型',
|
||||||
|
'type' => 'radio',
|
||||||
|
'content' => [
|
||||||
|
'baidu' => '百度地图',
|
||||||
|
'amap' => '高德地图',
|
||||||
|
'tencent' => '腾讯地图',
|
||||||
|
],
|
||||||
|
'value' => 'amap',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'zoom',
|
||||||
|
'title' => '默认缩放级别',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '11',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'lat',
|
||||||
|
'title' => '默认Lat',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '23.002569',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'lng',
|
||||||
|
'title' => '默认Lng',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '113.752215',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'baidukey',
|
||||||
|
'title' => '百度地图KEY',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'amapkey',
|
||||||
|
'title' => '高德地图KEY',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '78c9a922ba2d29f005eaa89e3f1b00bb',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'amapsecurityjscode',
|
||||||
|
'title' => '高德地图安全密钥',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'tencentkey',
|
||||||
|
'title' => '腾讯地图KEY',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'coordtype',
|
||||||
|
'title' => '坐标系类型',
|
||||||
|
'type' => 'select',
|
||||||
|
'content' => [
|
||||||
|
'DEFAULT' => '默认(使用所选地图默认坐标系)',
|
||||||
|
'GCJ02' => 'GCJ-02',
|
||||||
|
'BD09' => 'BD-09',
|
||||||
|
],
|
||||||
|
'value' => 'DEFAULT',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => '__tips__',
|
||||||
|
'title' => '温馨提示',
|
||||||
|
'type' => '',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '请先申请对应地图的Key,配置后再使用',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => 'alert-danger-light',
|
||||||
|
],
|
||||||
|
];
|
||||||
64
addons/address/controller/Index.php
Normal file
64
addons/address/controller/Index.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\address\controller;
|
||||||
|
|
||||||
|
use think\addons\Controller;
|
||||||
|
use think\Config;
|
||||||
|
use think\Hook;
|
||||||
|
|
||||||
|
class Index extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
// 首页
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
// 语言检测
|
||||||
|
$lang = $this->request->langset();
|
||||||
|
$lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn';
|
||||||
|
|
||||||
|
$site = Config::get("site");
|
||||||
|
|
||||||
|
// 配置信息
|
||||||
|
$config = [
|
||||||
|
'site' => array_intersect_key($site, array_flip(['name', 'cdnurl', 'version', 'timezone', 'languages'])),
|
||||||
|
'upload' => null,
|
||||||
|
'modulename' => 'addons',
|
||||||
|
'controllername' => 'index',
|
||||||
|
'actionname' => 'index',
|
||||||
|
'jsname' => 'addons/address',
|
||||||
|
'moduleurl' => '',
|
||||||
|
'language' => $lang
|
||||||
|
];
|
||||||
|
$config = array_merge($config, Config::get("view_replace_str"));
|
||||||
|
|
||||||
|
// 配置信息后
|
||||||
|
Hook::listen("config_init", $config);
|
||||||
|
// 加载当前控制器语言包
|
||||||
|
$this->view->assign('site', $site);
|
||||||
|
$this->view->assign('config', $config);
|
||||||
|
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择地址
|
||||||
|
public function select()
|
||||||
|
{
|
||||||
|
$config = get_addon_config('address');
|
||||||
|
$zoom = (int)$this->request->get('zoom', $config['zoom']);
|
||||||
|
$lng = (float)$this->request->get('lng');
|
||||||
|
$lat = (float)$this->request->get('lat');
|
||||||
|
$address = $this->request->get('address');
|
||||||
|
$lng = $lng ?: $config['lng'];
|
||||||
|
$lat = $lat ?: $config['lat'];
|
||||||
|
$this->view->assign('zoom', $zoom);
|
||||||
|
$this->view->assign('lng', $lng);
|
||||||
|
$this->view->assign('lat', $lat);
|
||||||
|
$this->view->assign('address', $address);
|
||||||
|
$maptype = $config['maptype'];
|
||||||
|
if (!isset($config[$maptype . 'key']) || !$config[$maptype . 'key']) {
|
||||||
|
$this->error("请在配置中配置对应类型地图的密钥");
|
||||||
|
}
|
||||||
|
return $this->view->fetch('index/' . $maptype);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
addons/address/info.ini
Normal file
10
addons/address/info.ini
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
name = address
|
||||||
|
title = 地址位置选择插件
|
||||||
|
intro = 地图位置选择插件,可返回地址和经纬度
|
||||||
|
author = FastAdmin
|
||||||
|
website = https://www.fastadmin.net
|
||||||
|
version = 1.1.8
|
||||||
|
state = 1
|
||||||
|
url = /addons/address
|
||||||
|
license = regular
|
||||||
|
licenseto = 34485
|
||||||
258
addons/address/view/index/amap.html
Normal file
258
addons/address/view/index/amap.html
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
<title>地址选择器</title>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/css/frontend.min.css"/>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/libs/font-awesome/css/font-awesome.min.css"/>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 4%;
|
||||||
|
z-index: 99;
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 15px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: white;
|
||||||
|
background: #1ABC9C;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
position: absolute;
|
||||||
|
width: 400px;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
padding: 5px;
|
||||||
|
margin-left: -200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amap-marker-label {
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding: .75rem 1.25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: .25rem;
|
||||||
|
position: fixed;
|
||||||
|
top: 2rem;
|
||||||
|
background-color: white;
|
||||||
|
width: auto;
|
||||||
|
min-width: 22rem;
|
||||||
|
border-width: 0;
|
||||||
|
left: 1.8rem;
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="place" name="q" class="form-control" placeholder="输入地点"/>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="submit" name="search" id="search-btn" class="btn btn-success">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="confirm">确定</div>
|
||||||
|
<div id="container"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window._AMapSecurityConfig = {
|
||||||
|
securityJsCode: "{$config.amapsecurityjscode|default=''}",
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="//webapi.amap.com/maps?v=1.4.11&key={$config.amapkey|default=''}&plugin=AMap.ToolBar,AMap.Autocomplete,AMap.PlaceSearch,AMap.Geocoder"></script>
|
||||||
|
<!-- UI组件库 1.0 -->
|
||||||
|
<script src="//webapi.amap.com/ui/1.0/main.js?v=1.0.11"></script>
|
||||||
|
<script src="__CDN__/assets/libs/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script src="__CDN__/assets/addons/address/js/gcoord.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
var as, map, geocoder, address, fromtype, totype;
|
||||||
|
address = "{$address|htmlentities}";
|
||||||
|
var lng = Number("{$lng}");
|
||||||
|
var lat = Number("{$lat}");
|
||||||
|
fromtype = "GCJ02";
|
||||||
|
totype = "{$config.coordtype|default='DEFAULT'}"
|
||||||
|
totype = totype === 'DEFAULT' ? "GCJ02" : totype;
|
||||||
|
|
||||||
|
if (lng && lat && fromtype !== totype) {
|
||||||
|
var result = gcoord.transform([lng, lat], gcoord[totype], gcoord[fromtype]);
|
||||||
|
lng = result[0] || lng;
|
||||||
|
lat = result[1] || lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
var init = function () {
|
||||||
|
AMapUI.loadUI(['misc/PositionPicker', 'misc/PoiPicker'], function (PositionPicker, PoiPicker) {
|
||||||
|
//加载PositionPicker,loadUI的路径参数为模块名中 'ui/' 之后的部分
|
||||||
|
map = new AMap.Map('container', {
|
||||||
|
zoom: parseInt('{$zoom}'),
|
||||||
|
center: [lng, lat]
|
||||||
|
});
|
||||||
|
geocoder = new AMap.Geocoder({
|
||||||
|
radius: 1000 //范围,默认:500
|
||||||
|
});
|
||||||
|
var positionPicker = new PositionPicker({
|
||||||
|
mode: 'dragMarker',//设定为拖拽地图模式,可选'dragMap'、'dragMarker',默认为'dragMap'
|
||||||
|
map: map//依赖地图对象
|
||||||
|
});
|
||||||
|
//输入提示
|
||||||
|
var autoOptions = {
|
||||||
|
input: "place"
|
||||||
|
};
|
||||||
|
|
||||||
|
var relocation = function (lnglat, addr) {
|
||||||
|
lng = lnglat.lng;
|
||||||
|
lat = lnglat.lat;
|
||||||
|
map.panTo([lng, lat]);
|
||||||
|
positionPicker.start(lnglat);
|
||||||
|
if (addr) {
|
||||||
|
// var label = '<div class="info">地址:' + addr + '<br>经度:' + lng + '<br>纬度:' + lat + '</div>';
|
||||||
|
var label = '<div class="info">地址:' + addr + '</div>';
|
||||||
|
positionPicker.marker.setLabel({
|
||||||
|
content: label //显示内容
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
geocoder.getAddress(lng + ',' + lat, function (status, result) {
|
||||||
|
if (status === 'complete' && result.regeocode) {
|
||||||
|
var address = result.regeocode.formattedAddress;
|
||||||
|
// var label = '<div class="info">地址:' + address + '<br>经度:' + lng + '<br>纬度:' + lat + '</div>';
|
||||||
|
var label = '<div class="info">地址:' + address + '</div>';
|
||||||
|
positionPicker.marker.setLabel({
|
||||||
|
content: label //显示内容
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(JSON.stringify(result));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var auto = new AMap.Autocomplete(autoOptions);
|
||||||
|
|
||||||
|
//构造地点查询类
|
||||||
|
var placeSearch = new AMap.PlaceSearch({
|
||||||
|
map: map
|
||||||
|
});
|
||||||
|
//注册监听,当选中某条记录时会触发
|
||||||
|
AMap.event.addListener(auto, "select", function (e) {
|
||||||
|
placeSearch.setCity(e.poi.adcode);
|
||||||
|
placeSearch.search(e.poi.name, function (status, result) {
|
||||||
|
$(map.getAllOverlays("marker")).each(function (i, j) {
|
||||||
|
j.on("click", function () {
|
||||||
|
relocation(j.De.position);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}); //关键字查询查询
|
||||||
|
});
|
||||||
|
AMap.event.addListener(map, 'click', function (e) {
|
||||||
|
relocation(e.lnglat);
|
||||||
|
});
|
||||||
|
|
||||||
|
//加载工具条
|
||||||
|
var tool = new AMap.ToolBar();
|
||||||
|
map.addControl(tool);
|
||||||
|
|
||||||
|
var poiPicker = new PoiPicker({
|
||||||
|
input: 'place',
|
||||||
|
placeSearchOptions: {
|
||||||
|
map: map,
|
||||||
|
pageSize: 6 //关联搜索分页
|
||||||
|
}
|
||||||
|
});
|
||||||
|
poiPicker.on('poiPicked', function (poiResult) {
|
||||||
|
poiPicker.hideSearchResults();
|
||||||
|
$('.poi .nearpoi').text(poiResult.item.name);
|
||||||
|
$('.address .info').text(poiResult.item.address);
|
||||||
|
$('#address').val(poiResult.item.address);
|
||||||
|
$("#place").val(poiResult.item.name);
|
||||||
|
|
||||||
|
relocation(poiResult.item.location);
|
||||||
|
});
|
||||||
|
|
||||||
|
positionPicker.on('success', function (positionResult) {
|
||||||
|
console.log(positionResult);
|
||||||
|
as = positionResult.position;
|
||||||
|
address = positionResult.address;
|
||||||
|
lat = as.lat;
|
||||||
|
lng = as.lng;
|
||||||
|
});
|
||||||
|
positionPicker.on('fail', function (positionResult) {
|
||||||
|
address = '';
|
||||||
|
});
|
||||||
|
positionPicker.start();
|
||||||
|
|
||||||
|
if (address) {
|
||||||
|
// 添加label
|
||||||
|
var label = '<div class="info">地址:' + address + '</div>';
|
||||||
|
positionPicker.marker.setLabel({
|
||||||
|
content: label //显示内容
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//点击搜索按钮
|
||||||
|
$(document).on('click', '#search-btn', function () {
|
||||||
|
if ($("#place").val() == '')
|
||||||
|
return;
|
||||||
|
placeSearch.search($("#place").val(), function (status, result) {
|
||||||
|
$(map.getAllOverlays("marker")).each(function (i, j) {
|
||||||
|
j.on("click", function () {
|
||||||
|
relocation(j.De.position);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//点击确定后执行回调赋值
|
||||||
|
var close = function (data) {
|
||||||
|
var index = parent.Layer.getFrameIndex(window.name);
|
||||||
|
var callback = parent.$("#layui-layer" + index).data("callback");
|
||||||
|
//再执行关闭
|
||||||
|
parent.Layer.close(index);
|
||||||
|
//再调用回传函数
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback.call(undefined, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//点击搜索按钮
|
||||||
|
$(document).on('click', '.confirm', function () {
|
||||||
|
var zoom = map.getZoom();
|
||||||
|
var data = {lat: lat, lng: lng, zoom: zoom, address: address};
|
||||||
|
if (fromtype !== totype) {
|
||||||
|
var result = gcoord.transform([data.lng, data.lat], gcoord[fromtype], gcoord[totype]);
|
||||||
|
data.lng = (result[0] || data.lng).toFixed(5);
|
||||||
|
data.lat = (result[1] || data.lat).toFixed(5);
|
||||||
|
console.log(data, result, fromtype, totype);
|
||||||
|
}
|
||||||
|
close(data);
|
||||||
|
});
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
243
addons/address/view/index/baidu.html
Normal file
243
addons/address/view/index/baidu.html
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
<title>地址选择器</title>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/css/frontend.min.css"/>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/libs/font-awesome/css/font-awesome.min.css"/>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 4%;
|
||||||
|
z-index: 99;
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 15px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: white;
|
||||||
|
background: #1ABC9C;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
position: absolute;
|
||||||
|
width: 400px;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
padding: 5px;
|
||||||
|
margin-left: -200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.BMapLabel {
|
||||||
|
max-width: inherit;
|
||||||
|
padding: .75rem 1.25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background-color: white;
|
||||||
|
width: auto;
|
||||||
|
min-width: 22rem;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="place" name="q" class="form-control" placeholder="输入地点"/>
|
||||||
|
<div id="searchResultPanel" style="border:1px solid #C0C0C0;width:150px;height:auto; display:none;"></div>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="button" name="search" id="search-btn" class="btn btn-success">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="confirm">确定</div>
|
||||||
|
<div id="container"></div>
|
||||||
|
<script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak={$config.baidukey|default=''}"></script>
|
||||||
|
<script src="__CDN__/assets/libs/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script src="__CDN__/assets/addons/address/js/gcoord.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
var map, marker, point, fromtype, totype;
|
||||||
|
var zoom = parseInt("{$zoom}");
|
||||||
|
var address = "{$address|htmlentities}";
|
||||||
|
var lng = Number("{$lng}");
|
||||||
|
var lat = Number("{$lat}");
|
||||||
|
fromtype = "BD09";
|
||||||
|
totype = "{$config.coordtype|default='DEFAULT'}"
|
||||||
|
totype = totype === 'DEFAULT' ? "BD09" : totype;
|
||||||
|
|
||||||
|
if (lng && lat && fromtype !== totype) {
|
||||||
|
var result = gcoord.transform([lng, lat], gcoord[totype], gcoord[fromtype]);
|
||||||
|
lng = result[0] || lng;
|
||||||
|
lat = result[1] || lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
var geocoder = new BMap.Geocoder();
|
||||||
|
|
||||||
|
var addPointMarker = function (point, addr) {
|
||||||
|
deletePoint();
|
||||||
|
addPoint(point);
|
||||||
|
|
||||||
|
if (addr) {
|
||||||
|
addMarker(point, addr);
|
||||||
|
} else {
|
||||||
|
geocoder.getLocation(point, function (rs) {
|
||||||
|
addMarker(point, rs.address);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var addPoint = function (point) {
|
||||||
|
lng = point.lng;
|
||||||
|
lat = point.lat;
|
||||||
|
marker = new BMap.Marker(point);
|
||||||
|
map.addOverlay(marker);
|
||||||
|
map.panTo(point);
|
||||||
|
};
|
||||||
|
|
||||||
|
var addMarker = function (point, addr) {
|
||||||
|
address = addr;
|
||||||
|
// var labelhtml = '<div class="info">地址:' + address + '<br>经度:' + point.lng + '<br>纬度:' + point.lat + '</div>';
|
||||||
|
var labelhtml = '<div class="info">地址:' + address + '</div>';
|
||||||
|
var label = new BMap.Label(labelhtml, {offset: new BMap.Size(16, 20)});
|
||||||
|
label.setStyle({
|
||||||
|
border: 'none',
|
||||||
|
padding: '.75rem 1.25rem'
|
||||||
|
});
|
||||||
|
marker.setLabel(label);
|
||||||
|
};
|
||||||
|
|
||||||
|
var deletePoint = function () {
|
||||||
|
var allOverlay = map.getOverlays();
|
||||||
|
for (var i = 0; i < allOverlay.length; i++) {
|
||||||
|
map.removeOverlay(allOverlay[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var init = function () {
|
||||||
|
map = new BMap.Map("container"); // 创建地图实例
|
||||||
|
var point = new BMap.Point(lng, lat); // 创建点坐标
|
||||||
|
map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放
|
||||||
|
map.centerAndZoom(point, zoom); // 初始化地图,设置中心点坐标和地图级别
|
||||||
|
|
||||||
|
var size = new BMap.Size(10, 20);
|
||||||
|
map.addControl(new BMap.CityListControl({
|
||||||
|
anchor: BMAP_ANCHOR_TOP_LEFT,
|
||||||
|
offset: size,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if ("{$lng}" != '' && "{$lat}" != '') {
|
||||||
|
addPointMarker(point, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
ac = new BMap.Autocomplete({"input": "place", "location": map}); //建立一个自动完成的对象
|
||||||
|
ac.addEventListener("onhighlight", function (e) { //鼠标放在下拉列表上的事件
|
||||||
|
var str = "";
|
||||||
|
var _value = e.fromitem.value;
|
||||||
|
var value = "";
|
||||||
|
if (e.fromitem.index > -1) {
|
||||||
|
value = _value.province + _value.city + _value.district + _value.street + _value.business;
|
||||||
|
}
|
||||||
|
str = "FromItem<br />index = " + e.fromitem.index + "<br />value = " + value;
|
||||||
|
|
||||||
|
value = "";
|
||||||
|
if (e.toitem.index > -1) {
|
||||||
|
_value = e.toitem.value;
|
||||||
|
value = _value.province + _value.city + _value.district + _value.street + _value.business;
|
||||||
|
}
|
||||||
|
str += "<br />ToItem<br />index = " + e.toitem.index + "<br />value = " + value;
|
||||||
|
$("#searchResultPanel").html(str);
|
||||||
|
});
|
||||||
|
ac.addEventListener("onconfirm", function (e) { //鼠标点击下拉列表后的事件
|
||||||
|
var _value = e.item.value;
|
||||||
|
myValue = _value.province + _value.city + _value.district + _value.street + _value.business;
|
||||||
|
$("#searchResultPanel").html("onconfirm<br />index = " + e.item.index + "<br />myValue = " + myValue);
|
||||||
|
setPlace();
|
||||||
|
});
|
||||||
|
|
||||||
|
function setPlace(text) {
|
||||||
|
map.clearOverlays(); //清除地图上所有覆盖物
|
||||||
|
function myFun() {
|
||||||
|
var results = local.getResults();
|
||||||
|
var result = local.getResults().getPoi(0);
|
||||||
|
var point = result.point; //获取第一个智能搜索的结果
|
||||||
|
map.centerAndZoom(point, 18);
|
||||||
|
// map.addOverlay(new BMap.Marker(point)); //添加标注
|
||||||
|
if (result.type != 0) {
|
||||||
|
address = results.province + results.city + result.address;
|
||||||
|
} else {
|
||||||
|
address = result.address;
|
||||||
|
}
|
||||||
|
addPointMarker(point, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
var local = new BMap.LocalSearch(map, { //智能搜索
|
||||||
|
onSearchComplete: myFun
|
||||||
|
});
|
||||||
|
local.search(text || myValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
map.addEventListener("click", function (e) {
|
||||||
|
//通过点击百度地图,可以获取到对应的point, 由point的lng、lat属性就可以获取对应的经度纬度
|
||||||
|
addPointMarker(e.point);
|
||||||
|
});
|
||||||
|
|
||||||
|
//点击搜索按钮
|
||||||
|
$(document).on('click', '#search-btn', function () {
|
||||||
|
if ($("#place").val() == '')
|
||||||
|
return;
|
||||||
|
setPlace($("#place").val());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var close = function (data) {
|
||||||
|
var index = parent.Layer.getFrameIndex(window.name);
|
||||||
|
var callback = parent.$("#layui-layer" + index).data("callback");
|
||||||
|
//再执行关闭
|
||||||
|
parent.Layer.close(index);
|
||||||
|
//再调用回传函数
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback.call(undefined, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//点击确定后执行回调赋值
|
||||||
|
$(document).on('click', '.confirm', function () {
|
||||||
|
var zoom = map.getZoom();
|
||||||
|
var data = {lat: lat, lng: lng, zoom: zoom, address: address};
|
||||||
|
if (fromtype !== totype) {
|
||||||
|
var result = gcoord.transform([data.lng, data.lat], gcoord[fromtype], gcoord[totype]);
|
||||||
|
data.lng = (result[0] || data.lng).toFixed(5);
|
||||||
|
data.lat = (result[1] || data.lat).toFixed(5);
|
||||||
|
console.log(data, result, fromtype, totype);
|
||||||
|
}
|
||||||
|
close(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
132
addons/address/view/index/index.html
Normal file
132
addons/address/view/index/index.html
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
|
||||||
|
<title>地图位置(经纬度)选择插件</title>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/css/frontend.min.css"/>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/libs/font-awesome/css/font-awesome.min.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div class="bs-docs-section clearfix">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>地图位置(经纬度)选择示例</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bs-component">
|
||||||
|
<form action="" method="post" role="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="address" placeholder="地址">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="lng" placeholder="经度">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="lat" placeholder="纬度">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="zoom" placeholder="缩放">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary" data-toggle='addresspicker' data-input-id="address" data-lng-id="lng" data-lat-id="lat" data-zoom-id="zoom">点击选择</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h2 id="code">调用代码</h2>
|
||||||
|
</div>
|
||||||
|
<div class="bs-component">
|
||||||
|
<textarea class="form-control" rows="17">
|
||||||
|
<form action="" method="post" role="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="address" placeholder="地址">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="lng" placeholder="经度">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="lat" placeholder="纬度">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for=""></label>
|
||||||
|
<input type="text" class="form-control" name="" id="zoom" placeholder="缩放">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary" data-toggle='addresspicker' data-input-id="address" data-lng-id="lng" data-lat-id="lat" data-zoom-id="zoom">点击选择</button>
|
||||||
|
</form>
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>参数说明</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bs-component" style="background:#fff;">
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>参数</th>
|
||||||
|
<th>释义</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>data-input-id</td>
|
||||||
|
<td>填充地址的文本框ID</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>data-lng-id</td>
|
||||||
|
<td>填充经度的文本框ID</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>data-lat-id</td>
|
||||||
|
<td>填充纬度的文本框ID</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>data-zoom-id</td>
|
||||||
|
<td>填充缩放的文本框ID</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!--@formatter:off-->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var require = {
|
||||||
|
config: {$config|json_encode}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<!--@formatter:on-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
require.callback = function () {
|
||||||
|
define('addons/address', ['jquery', 'bootstrap', 'frontend', 'template'], function ($, undefined, Frontend, Template) {
|
||||||
|
var Controller = {
|
||||||
|
index: function () {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Controller;
|
||||||
|
});
|
||||||
|
define('lang', function () {
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="__CDN__/assets/js/require.min.js" data-main="__CDN__/assets/js/require-frontend.min.js?v={$site.version}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
291
addons/address/view/index/tencent.html
Normal file
291
addons/address/view/index/tencent.html
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
<title>地址选择器</title>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/css/frontend.min.css"/>
|
||||||
|
<link rel="stylesheet" href="__CDN__/assets/libs/font-awesome/css/font-awesome.min.css"/>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 4%;
|
||||||
|
z-index: 99;
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 15px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: white;
|
||||||
|
background: #1ABC9C;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
position: absolute;
|
||||||
|
width: 400px;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
padding: 5px;
|
||||||
|
margin-left: -200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autocomplete-search {
|
||||||
|
text-align: left;
|
||||||
|
cursor: default;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||||
|
-moz-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||||
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||||
|
background-clip: padding-box;
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
z-index: 1036;
|
||||||
|
max-height: 254px;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autocomplete-search .autocomplete-suggestion {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autocomplete-search .autocomplete-suggestion:hover {
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="search">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="place" name="q" class="form-control" placeholder="输入地点"/>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="button" name="search" id="search-btn" class="btn btn-success">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="confirm">确定</div>
|
||||||
|
<div id="container"></div>
|
||||||
|
|
||||||
|
<script charset="utf-8" src="//map.qq.com/api/js?v=2.exp&libraries=place&key={$config.tencentkey|default=''}"></script>
|
||||||
|
<script src="__CDN__/assets/libs/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script src="__CDN__/assets/addons/address/js/gcoord.min.js"></script>
|
||||||
|
<script src="__CDN__/assets/addons/address/js/jquery.autocomplete.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
var map, marker, geocoder, infoWin, searchService, keyword, address, fromtype, totype;
|
||||||
|
address = "{$address|htmlentities}";
|
||||||
|
var lng = Number("{$lng}");
|
||||||
|
var lat = Number("{$lat}");
|
||||||
|
fromtype = "GCJ02";
|
||||||
|
totype = "{$config.coordtype|default='DEFAULT'}"
|
||||||
|
totype = totype === 'DEFAULT' ? "GCJ02" : totype;
|
||||||
|
|
||||||
|
if (lng && lat && fromtype !== totype) {
|
||||||
|
var result = gcoord.transform([lng, lat], gcoord[totype], gcoord[fromtype]);
|
||||||
|
lng = result[0] || lng;
|
||||||
|
lat = result[1] || lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
var init = function () {
|
||||||
|
var center = new qq.maps.LatLng(lat, lng);
|
||||||
|
map = new qq.maps.Map(document.getElementById('container'), {
|
||||||
|
center: center,
|
||||||
|
zoom: parseInt("{$config.zoom}")
|
||||||
|
});
|
||||||
|
|
||||||
|
//实例化信息窗口
|
||||||
|
infoWin = new qq.maps.InfoWindow({
|
||||||
|
map: map
|
||||||
|
});
|
||||||
|
|
||||||
|
geocoder = {
|
||||||
|
getAddress: function (latLng) {
|
||||||
|
$.ajax({
|
||||||
|
url: "https://apis.map.qq.com/ws/geocoder/v1/?location=" + latLng.lat + "," + latLng.lng + "&key={$config.tencentkey|default=''}&output=jsonp",
|
||||||
|
dataType: "jsonp",
|
||||||
|
type: 'GET',
|
||||||
|
cache: true,
|
||||||
|
crossDomain: true,
|
||||||
|
success: function (ret) {
|
||||||
|
console.log("getAddress:", ret)
|
||||||
|
if (ret.status === 0) {
|
||||||
|
var component = ret.result.address_component;
|
||||||
|
if (ret.result.formatted_addresses && ret.result.formatted_addresses.recommend) {
|
||||||
|
var recommend = ret.result.formatted_addresses.recommend;
|
||||||
|
var standard_address = ret.result.formatted_addresses.standard_address;
|
||||||
|
var address = component.province !== component.city ? component.province + component.city : component.province;
|
||||||
|
|
||||||
|
address = address + (recommend.indexOf(component.district) === 0 ? '' : component.district) + recommend;
|
||||||
|
} else {
|
||||||
|
address = ret.result.address;
|
||||||
|
}
|
||||||
|
showMarker(ret.result.location, address);
|
||||||
|
showInfoWin(ret.result.location, address);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e, 'error')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//初始化marker
|
||||||
|
showMarker(center);
|
||||||
|
if (address) {
|
||||||
|
showInfoWin(center, address);
|
||||||
|
} else {
|
||||||
|
geocoder.getAddress(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
var place = $("#place");
|
||||||
|
place.autoComplete({
|
||||||
|
minChars: 1,
|
||||||
|
cache: 0,
|
||||||
|
menuClass: 'autocomplete-search',
|
||||||
|
source: function (term, response) {
|
||||||
|
try {
|
||||||
|
xhr.abort();
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
xhr = $.ajax({
|
||||||
|
url: "https://apis.map.qq.com/ws/place/v1/suggestion?keyword=" + term + "&key={$config.tencentkey|default=''}&output=jsonp",
|
||||||
|
dataType: "jsonp",
|
||||||
|
type: 'GET',
|
||||||
|
cache: true,
|
||||||
|
success: function (ret) {
|
||||||
|
if (ret.status === 0) {
|
||||||
|
if(ret.data.length === 0){
|
||||||
|
$(".autocomplete-suggestions.autocomplete-search").html('');
|
||||||
|
}
|
||||||
|
response(ret.data);
|
||||||
|
} else {
|
||||||
|
console.log(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderItem: function (item, search) {
|
||||||
|
search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||||
|
var regexp = new RegExp("(" + search.replace(/[\,|\u3000|\uff0c]/, ' ').split(' ').join('|') + ")", "gi");
|
||||||
|
return "<div class='autocomplete-suggestion' data-item='" + JSON.stringify(item) + "' data-title='" + item.title + "' data-val='" + item.title + "'>" + item.title.replace(regexp, "<b>$1</b>") + "</div>";
|
||||||
|
},
|
||||||
|
onSelect: function (e, term, sel) {
|
||||||
|
e.preventDefault();
|
||||||
|
var item = $(sel).data("item");
|
||||||
|
//调用获取位置方法
|
||||||
|
geocoder.getAddress(item.location);
|
||||||
|
|
||||||
|
var position = new qq.maps.LatLng(item.location.lat, item.location.lng);
|
||||||
|
map.setCenter(position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//地图点击
|
||||||
|
qq.maps.event.addListener(map, 'click', function (event) {
|
||||||
|
try {
|
||||||
|
//调用获取位置方法
|
||||||
|
geocoder.getAddress(event.latLng);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//显示info窗口
|
||||||
|
var showInfoWin = function (latLng, title) {
|
||||||
|
var position = new qq.maps.LatLng(latLng.lat, latLng.lng);
|
||||||
|
infoWin.open();
|
||||||
|
infoWin.setContent(title);
|
||||||
|
infoWin.setPosition(position);
|
||||||
|
};
|
||||||
|
|
||||||
|
//实例化marker和监听拖拽结束事件
|
||||||
|
var showMarker = function (latLng, title) {
|
||||||
|
console.log("showMarker", latLng, title)
|
||||||
|
var position = new qq.maps.LatLng(latLng.lat, latLng.lng);
|
||||||
|
marker && marker.setMap(null);
|
||||||
|
marker = new qq.maps.Marker({
|
||||||
|
map: map,
|
||||||
|
position: position,
|
||||||
|
draggable: true,
|
||||||
|
title: title || '拖动图标选择位置'
|
||||||
|
});
|
||||||
|
|
||||||
|
//监听拖拽结束
|
||||||
|
qq.maps.event.addListener(marker, 'dragend', function (event) {
|
||||||
|
//调用获取位置方法
|
||||||
|
geocoder.getAddress(event.latLng);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var close = function (data) {
|
||||||
|
var index = parent.Layer.getFrameIndex(window.name);
|
||||||
|
var callback = parent.$("#layui-layer" + index).data("callback");
|
||||||
|
//再执行关闭
|
||||||
|
parent.Layer.close(index);
|
||||||
|
//再调用回传函数
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback.call(undefined, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//点击确定后执行回调赋值
|
||||||
|
$(document).on('click', '.confirm', function () {
|
||||||
|
var zoom = map.getZoom();
|
||||||
|
var data = {lat: infoWin.position.lat.toFixed(5), lng: infoWin.position.lng.toFixed(5), zoom: zoom, address: infoWin.content};
|
||||||
|
if (fromtype !== totype) {
|
||||||
|
var result = gcoord.transform([data.lng, data.lat], gcoord[fromtype], gcoord[totype]);
|
||||||
|
data.lng = (result[0] || data.lng).toFixed(5);
|
||||||
|
data.lat = (result[1] || data.lat).toFixed(5);
|
||||||
|
console.log(data, result, fromtype, totype);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
//点击搜索按钮
|
||||||
|
$(document).on('click', '#search-btn', function () {
|
||||||
|
if ($("#place").val() === '')
|
||||||
|
return;
|
||||||
|
var first = $(".autocomplete-search > .autocomplete-suggestion:first");
|
||||||
|
if (!first.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var item = first.data("item");
|
||||||
|
|
||||||
|
//调用获取位置方法
|
||||||
|
geocoder.getAddress(item.location);
|
||||||
|
|
||||||
|
var position = new qq.maps.LatLng(item.location.lat, item.location.lng);
|
||||||
|
map.setCenter(position);
|
||||||
|
});
|
||||||
|
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
4
addons/command/config.bak.php
Normal file
4
addons/command/config.bak.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
];
|
||||||
@@ -68,3 +68,5 @@ return [
|
|||||||
'extend' => '',
|
'extend' => '',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
1
addons/hwobs/.addonrc
Normal file
1
addons/hwobs/.addonrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"files":["public\\assets\\addons\\hwobs\\js\\spark.js"],"license":"regular","licenseto":"34485","licensekey":"fE1oqlUNvPsVy07I LHfdNbd7Yb6AD1LjzKfdIA==","domains":[],"licensecodes":[],"validations":[]}
|
||||||
152
addons/hwobs/Hwobs.php
Normal file
152
addons/hwobs/Hwobs.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\hwobs;
|
||||||
|
|
||||||
|
use addons\hwobs\library\Auth;
|
||||||
|
use Obs\ObsClient;
|
||||||
|
use think\Addons;
|
||||||
|
use think\App;
|
||||||
|
use think\Loader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 华为云存储插件
|
||||||
|
*/
|
||||||
|
class Hwobs extends Addons
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件安装方法
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function install()
|
||||||
|
{
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件卸载方法
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function uninstall()
|
||||||
|
{
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件启用方法
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function enable()
|
||||||
|
{
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件禁用方法
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function disable()
|
||||||
|
{
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否来源于API上传
|
||||||
|
*/
|
||||||
|
public function moduleInit($request)
|
||||||
|
{
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$module = strtolower($request->module());
|
||||||
|
if ($module == 'api' && ($config['apiupload'] ?? 0) &&
|
||||||
|
strtolower($request->controller()) == 'common' &&
|
||||||
|
strtolower($request->action()) == 'upload') {
|
||||||
|
request()->param('isApi', true);
|
||||||
|
App::invokeMethod(["\\addons\\hwobs\\controller\\Index", "upload"], ['isApi' => true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传配置初始化
|
||||||
|
*/
|
||||||
|
public function uploadConfigInit(&$upload)
|
||||||
|
{
|
||||||
|
$config = $this->getConfig();
|
||||||
|
$module = request()->module();
|
||||||
|
$module = $module ? strtolower($module) : 'index';
|
||||||
|
|
||||||
|
$data = ['deadline' => time() + $config['expire']];
|
||||||
|
$signature = hash_hmac('sha1', json_encode($data), $config['secretKey'], true);
|
||||||
|
|
||||||
|
$token = '';
|
||||||
|
if (Auth::isModuleAllow()) {
|
||||||
|
$token = $config['accessKey'] . ':' . base64_encode($signature) . ':' . base64_encode(json_encode($data));
|
||||||
|
}
|
||||||
|
$multipart = [
|
||||||
|
'hwobstoken' => $token
|
||||||
|
];
|
||||||
|
|
||||||
|
$upload = array_merge($upload, [
|
||||||
|
'cdnurl' => $config['cdnurl'],
|
||||||
|
'uploadurl' => $config['uploadmode'] == 'client' ? $config['uploadurl'] : addon_url('hwobs/index/upload', [], false, true),
|
||||||
|
'uploadmode' => $config['uploadmode'],
|
||||||
|
'bucket' => $config['bucket'],
|
||||||
|
'maxsize' => $config['maxsize'],
|
||||||
|
'mimetype' => $config['mimetype'],
|
||||||
|
'savekey' => $config['savekey'],
|
||||||
|
'chunking' => (bool)($config['chunking'] ?? $upload['chunking']),
|
||||||
|
'chunksize' => (int)($config['chunksize'] ?? $upload['chunksize']),
|
||||||
|
'multipart' => $multipart,
|
||||||
|
'storage' => $this->getName(),
|
||||||
|
'multiple' => (bool)$config['multiple'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件删除后
|
||||||
|
*/
|
||||||
|
public function uploadDelete($attachment)
|
||||||
|
{
|
||||||
|
$config = $this->getConfig();
|
||||||
|
if ($attachment['storage'] == 'hwobs' && isset($config['syncdelete']) && $config['syncdelete']) {
|
||||||
|
try {
|
||||||
|
//删除云储存文件
|
||||||
|
$config = get_addon_config('hwobs');
|
||||||
|
$obs = new ObsClient([
|
||||||
|
'key' => $config['accessKey'],
|
||||||
|
'secret' => $config['secretKey'],
|
||||||
|
'endpoint' => $config['endpoint']
|
||||||
|
]);
|
||||||
|
$result = $obs->deleteObject([
|
||||||
|
'Bucket' => $config['bucket'],
|
||||||
|
'Key' => ltrim($attachment->url, '/')
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果是服务端中转,还需要删除本地文件
|
||||||
|
//if ($config['uploadmode'] == 'server') {
|
||||||
|
// $filePath = ROOT_PATH . 'public' . str_replace('/', DS, $attachment->url);
|
||||||
|
// if ($filePath) {
|
||||||
|
// @unlink($filePath);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染命名空间配置信息
|
||||||
|
*/
|
||||||
|
public function appInit()
|
||||||
|
{
|
||||||
|
if (!class_exists('\Obs\ObsClient')) {
|
||||||
|
Loader::addNamespace('Obs', $this->addons_path . str_replace('/', DS, 'library/Obs/'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
279
addons/hwobs/bootstrap.js
vendored
Normal file
279
addons/hwobs/bootstrap.js
vendored
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
if (typeof Config.upload.storage !== 'undefined' && Config.upload.storage === 'hwobs') {
|
||||||
|
require(['upload'], function (Upload) {
|
||||||
|
//获取文件MD5值
|
||||||
|
var getFileMd5 = function (file, cb) {
|
||||||
|
//如果savekey中未检测到md5,则无需获取文件md5,直接返回upload的uuid
|
||||||
|
if (!Config.upload.savekey.match(/\{(file)?md5\}/)) {
|
||||||
|
cb && cb(file.upload.uuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
require(['../addons/hwobs/js/spark'], function (SparkMD5) {
|
||||||
|
var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
|
||||||
|
chunkSize = 10 * 1024 * 1024,
|
||||||
|
chunks = Math.ceil(file.size / chunkSize),
|
||||||
|
currentChunk = 0,
|
||||||
|
spark = new SparkMD5.ArrayBuffer(),
|
||||||
|
fileReader = new FileReader();
|
||||||
|
|
||||||
|
fileReader.onload = function (e) {
|
||||||
|
spark.append(e.target.result);
|
||||||
|
currentChunk++;
|
||||||
|
if (currentChunk < chunks) {
|
||||||
|
loadNext();
|
||||||
|
} else {
|
||||||
|
cb && cb(spark.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.onerror = function () {
|
||||||
|
console.warn('文件读取错误');
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadNext() {
|
||||||
|
var start = currentChunk * chunkSize,
|
||||||
|
end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
|
||||||
|
|
||||||
|
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadNext();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var _onInit = Upload.events.onInit;
|
||||||
|
//初始化中完成判断
|
||||||
|
Upload.events.onInit = function () {
|
||||||
|
_onInit.apply(this, Array.prototype.slice.apply(arguments));
|
||||||
|
//如果上传接口不是hwobs,则不处理
|
||||||
|
if (this.options.url !== Config.upload.uploadurl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.extend(this.options, {
|
||||||
|
//关闭自动处理队列功能
|
||||||
|
autoQueue: false,
|
||||||
|
params: function (files, xhr, chunk) {
|
||||||
|
var params = $.extend({}, Config.upload.multipart);
|
||||||
|
if (chunk) {
|
||||||
|
return $.extend({}, params, {
|
||||||
|
filesize: chunk.file.size,
|
||||||
|
filename: chunk.file.name,
|
||||||
|
chunkid: chunk.file.upload.uuid,
|
||||||
|
chunkindex: chunk.index,
|
||||||
|
chunkcount: chunk.file.upload.totalChunkCount,
|
||||||
|
chunkfilesize: chunk.dataBlock.data.size,
|
||||||
|
chunksize: this.options.chunkSize,
|
||||||
|
width: chunk.file.width || 0,
|
||||||
|
height: chunk.file.height || 0,
|
||||||
|
type: chunk.file.type,
|
||||||
|
uploadId: chunk.file.uploadId,
|
||||||
|
key: chunk.file.key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
},
|
||||||
|
chunkSuccess: function (chunk, file, response) {
|
||||||
|
var etag = chunk.xhr.getResponseHeader("ETag").replace(/(^")|("$)/g, '');
|
||||||
|
file.etags = file.etags ? file.etags : [];
|
||||||
|
file.etags[chunk.index] = etag;
|
||||||
|
},
|
||||||
|
chunksUploaded: function (file, done) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
Fast.api.ajax({
|
||||||
|
url: "/addons/hwobs/index/upload",
|
||||||
|
data: {
|
||||||
|
action: 'merge',
|
||||||
|
filesize: file.size,
|
||||||
|
filename: file.name,
|
||||||
|
chunkid: file.upload.uuid,
|
||||||
|
chunkcount: file.upload.totalChunkCount,
|
||||||
|
md5: file.md5,
|
||||||
|
key: file.key,
|
||||||
|
uploadId: file.uploadId,
|
||||||
|
etags: file.etags,
|
||||||
|
category: file.category || '',
|
||||||
|
hwobstoken: Config.upload.multipart.hwobstoken,
|
||||||
|
},
|
||||||
|
}, function (data, ret) {
|
||||||
|
done(JSON.stringify(ret));
|
||||||
|
return false;
|
||||||
|
}, function (data, ret) {
|
||||||
|
file.accepted = false;
|
||||||
|
that._errorProcessing([file], ret.msg);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var _success = this.options.success;
|
||||||
|
//先移除已有的事件
|
||||||
|
this.off("success", _success).on("success", function (file, response) {
|
||||||
|
var ret = {code: 0, msg: response};
|
||||||
|
try {
|
||||||
|
if (response) {
|
||||||
|
ret = typeof response === 'string' ? JSON.parse(response) : response;
|
||||||
|
}
|
||||||
|
if (file.xhr.status === 200 || file.xhr.status === 204) {
|
||||||
|
|
||||||
|
if (Config.upload.uploadmode === 'client') {
|
||||||
|
ret = {code: 1, data: {url: '/' + file.key}};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret.code == 1) {
|
||||||
|
var url = ret.data.url || '';
|
||||||
|
Fast.api.ajax({
|
||||||
|
url: "/addons/hwobs/index/notify",
|
||||||
|
data: {name: file.name, url: url, md5: file.md5, size: file.size, width: file.width || 0, height: file.height || 0, type: file.type, category: file.category || '', hwobstoken: Config.upload.multipart.hwobstoken}
|
||||||
|
}, function () {
|
||||||
|
return false;
|
||||||
|
}, function () {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(ret);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(file.xhr);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
_success.call(this, file, ret);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on("addedfile", function (file) {
|
||||||
|
var that = this;
|
||||||
|
setTimeout(function () {
|
||||||
|
if (file.status === 'error') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getFileMd5(file, function (md5) {
|
||||||
|
var chunk = that.options.chunking && file.size > that.options.chunkSize ? 1 : 0;
|
||||||
|
var params = $(that.element).data("params") || {};
|
||||||
|
var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
|
||||||
|
category = typeof category === 'function' ? category.call(that, file) : category;
|
||||||
|
Fast.api.ajax({
|
||||||
|
url: "/addons/hwobs/index/params",
|
||||||
|
data: {method: 'POST', category: category, md5: md5, name: file.name, type: file.type, size: file.size, chunk: chunk, chunksize: that.options.chunkSize, hwobstoken: Config.upload.multipart.hwobstoken},
|
||||||
|
}, function (data) {
|
||||||
|
file.md5 = md5;
|
||||||
|
file.id = data.id;
|
||||||
|
file.key = data.key;
|
||||||
|
file.date = data.date;
|
||||||
|
file.uploadId = data.uploadId;
|
||||||
|
file.policy = data.policy;
|
||||||
|
file.signature = data.signature;
|
||||||
|
file.partsAuthorization = data.partsAuthorization;
|
||||||
|
file.headers = data.headers;
|
||||||
|
delete data.headers;
|
||||||
|
file.params = data;
|
||||||
|
file.category = category;
|
||||||
|
|
||||||
|
if (file.status != 'error') {
|
||||||
|
//开始上传
|
||||||
|
that.enqueueFile(file);
|
||||||
|
} else {
|
||||||
|
that.removeFile(file);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, function () {
|
||||||
|
that.removeFile(file);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Config.upload.uploadmode === 'client') {
|
||||||
|
var _method = this.options.method;
|
||||||
|
var _url = this.options.url;
|
||||||
|
this.options.method = function (files) {
|
||||||
|
if (files[0].upload.chunked) {
|
||||||
|
var chunk = null;
|
||||||
|
files[0].upload.chunks.forEach(function (item) {
|
||||||
|
if (item.status === 'uploading') {
|
||||||
|
chunk = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!chunk) {
|
||||||
|
return "POST";
|
||||||
|
} else {
|
||||||
|
return "PUT";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "POST";
|
||||||
|
}
|
||||||
|
return _method;
|
||||||
|
};
|
||||||
|
this.options.url = function (files) {
|
||||||
|
if (files[0].upload.chunked) {
|
||||||
|
var chunk = null;
|
||||||
|
files[0].upload.chunks.forEach(function (item) {
|
||||||
|
if (item.status === 'uploading') {
|
||||||
|
chunk = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var index = chunk.dataBlock.chunkIndex;
|
||||||
|
this.options.headers = {"Authorization": files[0]['partsAuthorization'][index], "x-amz-date": files[0]['date']};
|
||||||
|
if (!chunk) {
|
||||||
|
return Config.upload.uploadurl + "/" + files[0].key + "?uploadId=" + files[0].uploadId;
|
||||||
|
} else {
|
||||||
|
return Config.upload.uploadurl + "/" + files[0].key + "?partNumber=" + (index + 1) + "&uploadId=" + files[0].uploadId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _url;
|
||||||
|
};
|
||||||
|
this.options.params = function (files, xhr, chunk) {
|
||||||
|
var params = Config.upload.multipart;
|
||||||
|
delete params.category;
|
||||||
|
if (chunk) {
|
||||||
|
return $.extend({}, params, {
|
||||||
|
filesize: chunk.file.size,
|
||||||
|
filename: chunk.file.name,
|
||||||
|
chunkid: chunk.file.upload.uuid,
|
||||||
|
chunkindex: chunk.index,
|
||||||
|
chunkcount: chunk.file.upload.totalChunkCount,
|
||||||
|
chunkfilesize: chunk.dataBlock.data.size,
|
||||||
|
width: chunk.file.width || 0,
|
||||||
|
height: chunk.file.height || 0,
|
||||||
|
type: chunk.file.type,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var retParams = $.extend({}, params, files[0].params || {});
|
||||||
|
delete retParams.hwobstoken;
|
||||||
|
delete retParams.date;
|
||||||
|
delete retParams.md5;
|
||||||
|
if (Config.upload.uploadmode !== 'client') {
|
||||||
|
params.category = files[0].category || '';
|
||||||
|
}
|
||||||
|
return retParams;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.on("sending", function (file, xhr, formData) {
|
||||||
|
var that = this;
|
||||||
|
var _send = xhr.send;
|
||||||
|
//仅允许部分字段
|
||||||
|
var allowFields = ['partNumber', 'uploadId', 'key', 'AccessKeyId', 'policy', 'signature', 'file'];
|
||||||
|
formData.forEach(function (value, key) {
|
||||||
|
if (allowFields.indexOf(key) < 0) {
|
||||||
|
formData.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (file.upload.chunked) {
|
||||||
|
xhr.send = function () {
|
||||||
|
if (file.upload.chunked) {
|
||||||
|
var chunk = null;
|
||||||
|
file.upload.chunks.forEach(function (item) {
|
||||||
|
if (item.status == 'uploading') {
|
||||||
|
chunk = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_send.call(xhr, chunk.dataBlock.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
245
addons/hwobs/config.bak.php
Normal file
245
addons/hwobs/config.bak.php
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name' => 'accessKey',
|
||||||
|
'title' => 'Access Key',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => 'HPUA3H63YULC0YUCFE5J',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '请前往华为云控制台->我的凭证->访问密钥中生成',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'secretKey',
|
||||||
|
'title' => 'Secret Key',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => 'B2Obu8P3Er23EwzcoH8pPhbWBk0GOxpFrlwwRgSh',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '请前往华为云控制台->我的凭证->访问密钥中生成',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'bucket',
|
||||||
|
'title' => '存储桶名称',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => 'doxixi',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '存储桶名称',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'endpoint',
|
||||||
|
'title' => 'Endpoint',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => 'obs.cn-south-1.myhuaweicloud.com',
|
||||||
|
'rule' => 'required;endpoint',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '请输入你的Endpoint',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => 'data-rule-endpoint="[/^(?!http(s)?:\\/\\/).*$/, \'不能以http(s)://开头\']"',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'uploadurl',
|
||||||
|
'title' => '上传接口地址',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => 'https://doxixi.obs.cn-south-1.myhuaweicloud.com',
|
||||||
|
'rule' => 'required;uploadurl',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '请使用存储桶->基本信息->访问域名的值,并在前面加上http://或https://',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => 'data-rule-uploadurl="[/^http(s)?:\\/\\/.*$/, \'必需以http(s)://开头\']"',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'cdnurl',
|
||||||
|
'title' => 'CDN地址',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => 'https://doxixi.obs.cn-south-1.myhuaweicloud.com',
|
||||||
|
'rule' => 'required;cdnurl',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '如果你的云存储有绑定自定义域名,请输入自定义域名',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => 'data-rule-cdnurl="[/^http(s)?:\\/\\/.*$/, \'必需以http(s)://开头\']"',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'uploadmode',
|
||||||
|
'title' => '上传模式',
|
||||||
|
'type' => 'select',
|
||||||
|
'content' => [
|
||||||
|
'client' => '客户端直传(速度快,无备份)',
|
||||||
|
'server' => '服务器中转(占用服务器带宽,有备份)',
|
||||||
|
],
|
||||||
|
'value' => 'client',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'serverbackup',
|
||||||
|
'title' => '服务器中转模式备份',
|
||||||
|
'type' => 'radio',
|
||||||
|
'content' => [
|
||||||
|
1 => '备份(附件管理将产生2条记录)',
|
||||||
|
0 => '不备份',
|
||||||
|
],
|
||||||
|
'value' => '0',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '服务器中转模式下是否备份文件',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'savekey',
|
||||||
|
'title' => '保存文件名',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '/uploads/{year}{mon}{day}/{filemd5}{.suffix}',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'expire',
|
||||||
|
'title' => '上传有效时长',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '600',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '用户停留页面上传有效时长,单位秒',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'maxsize',
|
||||||
|
'title' => '最大可上传',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '10M',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'mimetype',
|
||||||
|
'title' => '可上传后缀格式',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => 'jpg,png,bmp,jpeg,gif,zip,rar,xls,xlsx',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'multiple',
|
||||||
|
'title' => '多文件上传',
|
||||||
|
'type' => 'bool',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '1',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'thumbstyle',
|
||||||
|
'title' => '缩略图样式',
|
||||||
|
'type' => 'string',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '用于后台列表缩略图样式,可使用:?x-image-process=image/resize,m_fixed,h_90,w_120或?x-image-process=style/样式名称',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'chunking',
|
||||||
|
'title' => '分片上传',
|
||||||
|
'type' => 'radio',
|
||||||
|
'content' => [
|
||||||
|
1 => '开启',
|
||||||
|
0 => '关闭',
|
||||||
|
],
|
||||||
|
'value' => '1',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'chunksize',
|
||||||
|
'title' => '分片大小',
|
||||||
|
'type' => 'number',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '4194304',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'syncdelete',
|
||||||
|
'title' => '附件删除时是否同步删除云存储文件',
|
||||||
|
'type' => 'bool',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '0',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'apiupload',
|
||||||
|
'title' => 'API接口使用云存储',
|
||||||
|
'type' => 'bool',
|
||||||
|
'content' => [],
|
||||||
|
'value' => '0',
|
||||||
|
'rule' => 'required',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'noneedlogin',
|
||||||
|
'title' => '免登录上传',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'content' => [
|
||||||
|
'api' => 'API',
|
||||||
|
'index' => '前台',
|
||||||
|
'admin' => '后台',
|
||||||
|
],
|
||||||
|
'value' => 'api,index',
|
||||||
|
'rule' => '',
|
||||||
|
'msg' => '',
|
||||||
|
'tip' => '',
|
||||||
|
'ok' => '',
|
||||||
|
'extend' => '',
|
||||||
|
],
|
||||||
|
];
|
||||||
338
addons/hwobs/controller/Index.php
Normal file
338
addons/hwobs/controller/Index.php
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\hwobs\controller;
|
||||||
|
|
||||||
|
use think\Config;
|
||||||
|
use Obs\ObsClient;
|
||||||
|
use addons\hwobs\Hwobs;
|
||||||
|
use think\addons\Controller;
|
||||||
|
use addons\hwobs\library\Auth;
|
||||||
|
use app\common\library\Upload;
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
use addons\hwobs\library\Signer;
|
||||||
|
use app\common\model\Attachment;
|
||||||
|
use app\common\exception\UploadException;
|
||||||
|
use Obs\Internal\Signature\DefaultSignature;
|
||||||
|
|
||||||
|
class Index extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
//跨域检测
|
||||||
|
check_cors_request();
|
||||||
|
|
||||||
|
parent::_initialize();
|
||||||
|
Config::set('default_return_type', 'json');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
Config::set('default_return_type', 'html');
|
||||||
|
$this->error("当前插件暂无前台页面");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function config()
|
||||||
|
{
|
||||||
|
$upload = [];
|
||||||
|
(new Hwobs)->uploadConfigInit($upload);
|
||||||
|
$this->success('', null, $upload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function params()
|
||||||
|
{
|
||||||
|
$this->check();
|
||||||
|
|
||||||
|
$config = get_addon_config('hwobs');
|
||||||
|
$name = $this->request->post('name');
|
||||||
|
$md5 = $this->request->post('md5');
|
||||||
|
$chunk = $this->request->post('chunk');
|
||||||
|
$name = xss_clean($name);
|
||||||
|
|
||||||
|
// 检查文件后缀
|
||||||
|
$extension = strtolower(pathinfo($name, PATHINFO_EXTENSION));
|
||||||
|
$allowedExtensions = explode(',', strtolower($config['mimetype']));
|
||||||
|
if (!in_array($extension, $allowedExtensions) || in_array($extension, ['php', 'html', 'htm', 'phar', 'phtml']) || preg_match("/^php(.*)/i", $extension)) {
|
||||||
|
$this->error('不允许的文件类型');
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = (new Upload())->getSavekey($config['savekey'], $name, $md5);
|
||||||
|
$key = ltrim($key, "/");
|
||||||
|
$params = [
|
||||||
|
'key' => $key,
|
||||||
|
'md5' => $md5
|
||||||
|
];
|
||||||
|
$fileSize = $this->request->post('size');
|
||||||
|
$type = $this->request->post('type');
|
||||||
|
$date = gmdate('D, d M Y H:i:s \G\M\T', time());
|
||||||
|
$headers = [];
|
||||||
|
$obs = new ObsClient([
|
||||||
|
'key' => $config['accessKey'],
|
||||||
|
'secret' => $config['secretKey'],
|
||||||
|
'endpoint' => $config['endpoint']
|
||||||
|
]);
|
||||||
|
if ($chunk) {
|
||||||
|
$partSize = $this->request->post("chunksize");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$ret = $obs->initiateMultipartUpload([
|
||||||
|
'Bucket' => $config['bucket'],
|
||||||
|
'Key' => $key,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error("上传失败");
|
||||||
|
}
|
||||||
|
$uploadId = $ret['UploadId'];
|
||||||
|
|
||||||
|
$params['key'] = $key;
|
||||||
|
$params['uploadId'] = $uploadId;
|
||||||
|
$params['partsAuthorization'] = Signer::getPartsAuthorization($key, $uploadId, $fileSize, $partSize, $date);
|
||||||
|
} else {
|
||||||
|
$signature = $obs->createPostSignature([
|
||||||
|
'Bucket' => $config['bucket'],
|
||||||
|
'Key' => $key,
|
||||||
|
'FormParams' => []
|
||||||
|
]);
|
||||||
|
$params['key'] = $key;
|
||||||
|
$params['AccessKeyId'] = $config['accessKey'];
|
||||||
|
$params['policy'] = $signature['Policy'];
|
||||||
|
$params['signature'] = $signature['Signature'];
|
||||||
|
}
|
||||||
|
$params['headers'] = $headers;
|
||||||
|
$params['date'] = $date;
|
||||||
|
$this->success('', null, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器中转上传文件
|
||||||
|
* 上传分片
|
||||||
|
* 合并分片
|
||||||
|
* @param bool $isApi
|
||||||
|
*/
|
||||||
|
public function upload($isApi = false)
|
||||||
|
{
|
||||||
|
if ($isApi === true) {
|
||||||
|
if (!Auth::isModuleAllow()) {
|
||||||
|
$this->error("请登录后再进行操作");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->check();
|
||||||
|
}
|
||||||
|
$config = get_addon_config('hwobs');
|
||||||
|
$obs = new ObsClient([
|
||||||
|
'key' => $config['accessKey'],
|
||||||
|
'secret' => $config['secretKey'],
|
||||||
|
'endpoint' => $config['endpoint']
|
||||||
|
]);
|
||||||
|
|
||||||
|
//检测删除文件或附件
|
||||||
|
$checkDeleteFile = function ($attachment, $upload, $force = false) use ($config) {
|
||||||
|
//如果设定为不备份则删除文件和记录 或 强制删除
|
||||||
|
if ((isset($config['serverbackup']) && !$config['serverbackup']) || $force) {
|
||||||
|
if ($attachment && !empty($attachment['id'])) {
|
||||||
|
$attachment->delete();
|
||||||
|
}
|
||||||
|
if ($upload) {
|
||||||
|
//文件绝对路径
|
||||||
|
$filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
|
||||||
|
@unlink($filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$chunkid = $this->request->post("chunkid");
|
||||||
|
if ($chunkid) {
|
||||||
|
$action = $this->request->post("action");
|
||||||
|
$chunkindex = $this->request->post("chunkindex/d");
|
||||||
|
$chunkcount = $this->request->post("chunkcount/d");
|
||||||
|
$filesize = $this->request->post("filesize");
|
||||||
|
$filename = $this->request->post("filename");
|
||||||
|
$method = $this->request->method(true);
|
||||||
|
$key = $this->request->post("key");
|
||||||
|
$uploadId = $this->request->post("uploadId");
|
||||||
|
|
||||||
|
if ($action == 'merge') {
|
||||||
|
$attachment = null;
|
||||||
|
$upload = null;
|
||||||
|
//合并分片
|
||||||
|
if ($config['uploadmode'] == 'server') {
|
||||||
|
//合并分片文件
|
||||||
|
try {
|
||||||
|
$upload = new Upload();
|
||||||
|
$attachment = $upload->merge($chunkid, $chunkcount, $filename);
|
||||||
|
} catch (UploadException $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$etags = $this->request->post("etags/a", []);
|
||||||
|
if (count($etags) != $chunkcount) {
|
||||||
|
$checkDeleteFile($attachment, $upload, true);
|
||||||
|
$this->error("分片数据错误");
|
||||||
|
}
|
||||||
|
$listParts = [];
|
||||||
|
for ($i = 0; $i < $chunkcount; $i++) {
|
||||||
|
$listParts[] = array("PartNumber" => $i + 1, "ETag" => $etags[$i]);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$result = $obs->completeMultipartUpload([
|
||||||
|
'Bucket' => $config['bucket'],
|
||||||
|
'Key' => $key,
|
||||||
|
// 设置Upload ID
|
||||||
|
'UploadId' => $uploadId,
|
||||||
|
'Parts' => $listParts
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$checkDeleteFile($attachment, $upload, true);
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if (!isset($result['Key'])) {
|
||||||
|
$checkDeleteFile($attachment, $upload, true);
|
||||||
|
$this->error("上传失败");
|
||||||
|
} else {
|
||||||
|
$checkDeleteFile($attachment, $upload);
|
||||||
|
$this->success("上传成功", '', ['url' => "/" . $key, 'fullurl' => cdnurl("/" . $key, true)]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//默认普通上传文件
|
||||||
|
$file = $this->request->file('file');
|
||||||
|
try {
|
||||||
|
$upload = new Upload($file);
|
||||||
|
$file = $upload->chunk($chunkid, $chunkindex, $chunkcount);
|
||||||
|
} catch (UploadException $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$ret = $obs->uploadPart([
|
||||||
|
'Bucket' => $config['bucket'],
|
||||||
|
'Key' => $key,
|
||||||
|
// 设置分段号,范围是1~10000
|
||||||
|
'PartNumber' => $chunkindex + 1,
|
||||||
|
// 设置Upload ID
|
||||||
|
'UploadId' => $uploadId,
|
||||||
|
// 设置将要上传的大文件,localfile为上传的本地文件路径,需要指定到具体的文件名
|
||||||
|
'SourceFile' => $file->getRealPath(),
|
||||||
|
// 设置分段大小
|
||||||
|
'PartSize' => $file->getSize(),
|
||||||
|
// 设置分段的起始偏移大小
|
||||||
|
'Offset' => 0
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$etag = isset($ret['ETag']) ? $ret['ETag'] : '';
|
||||||
|
$this->success("上传成功", "", [], 3, ['ETag' => $etag]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$attachment = null;
|
||||||
|
//默认普通上传文件
|
||||||
|
$file = $this->request->file('file');
|
||||||
|
try {
|
||||||
|
$upload = new Upload($file);
|
||||||
|
$attachment = $upload->upload();
|
||||||
|
} catch (UploadException $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
//文件绝对路径
|
||||||
|
$filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
|
||||||
|
|
||||||
|
$url = $attachment->url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$ret = $obs->putObject([
|
||||||
|
'Bucket' => $config['bucket'],
|
||||||
|
'Key' => ltrim($attachment->url, '/'),
|
||||||
|
'SourceFile' => $filePath
|
||||||
|
]);
|
||||||
|
//成功不做任何操作
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$checkDeleteFile($attachment, $upload, true);
|
||||||
|
$this->error("上传失败");
|
||||||
|
}
|
||||||
|
$checkDeleteFile($attachment, $upload);
|
||||||
|
|
||||||
|
// 记录云存储记录
|
||||||
|
$data = $attachment->toArray();
|
||||||
|
unset($data['id']);
|
||||||
|
$data['storage'] = 'hwobs';
|
||||||
|
Attachment::create($data, true);
|
||||||
|
|
||||||
|
$this->success("上传成功", '', ['url' => $url, 'fullurl' => cdnurl($url, true)]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调
|
||||||
|
*/
|
||||||
|
public function notify()
|
||||||
|
{
|
||||||
|
$this->check();
|
||||||
|
$config = get_addon_config('hwobs');
|
||||||
|
if ($config['uploadmode'] != 'client') {
|
||||||
|
$this->error("无需执行该操作");
|
||||||
|
}
|
||||||
|
$this->request->filter('trim,strip_tags,htmlspecialchars,xss_clean');
|
||||||
|
|
||||||
|
$size = $this->request->post('size/d');
|
||||||
|
$name = $this->request->post('name', '');
|
||||||
|
$md5 = $this->request->post('md5', '');
|
||||||
|
$type = $this->request->post('type', '');
|
||||||
|
$url = $this->request->post('url', '');
|
||||||
|
$width = $this->request->post('width/d');
|
||||||
|
$height = $this->request->post('height/d');
|
||||||
|
$category = $this->request->post('category', '');
|
||||||
|
$suffix = strtolower(pathinfo($name, PATHINFO_EXTENSION));
|
||||||
|
$suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
|
||||||
|
$attachment = Attachment::where('url', $url)->where('storage', 'hwobs')->find();
|
||||||
|
if (!$attachment) {
|
||||||
|
$params = array(
|
||||||
|
'category' => $category,
|
||||||
|
'admin_id' => (int)session('admin.id'),
|
||||||
|
'user_id' => (int)$this->auth->id,
|
||||||
|
'filesize' => $size,
|
||||||
|
'filename' => $name,
|
||||||
|
'imagewidth' => $width,
|
||||||
|
'imageheight' => $height,
|
||||||
|
'imagetype' => $suffix,
|
||||||
|
'imageframes' => 0,
|
||||||
|
'mimetype' => $type,
|
||||||
|
'url' => $url,
|
||||||
|
'uploadtime' => time(),
|
||||||
|
'storage' => 'hwobs',
|
||||||
|
'sha1' => $md5,
|
||||||
|
);
|
||||||
|
Attachment::create($params, true);
|
||||||
|
}
|
||||||
|
$this->success();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查签名是否正确或过期
|
||||||
|
*/
|
||||||
|
protected function check()
|
||||||
|
{
|
||||||
|
$hwobstoken = $this->request->post('hwobstoken', '', 'trim');
|
||||||
|
if (!$hwobstoken) {
|
||||||
|
$this->error("参数不正确(code:1)");
|
||||||
|
}
|
||||||
|
$config = get_addon_config('hwobs');
|
||||||
|
list($accessKey, $sign, $data) = explode(':', $hwobstoken);
|
||||||
|
if (!$accessKey || !$sign || !$data) {
|
||||||
|
$this->error("参数不正确(code:2)");
|
||||||
|
}
|
||||||
|
if ($accessKey !== $config['accessKey']) {
|
||||||
|
$this->error("参数不正确(code:3)");
|
||||||
|
}
|
||||||
|
if ($sign !== base64_encode(hash_hmac('sha1', base64_decode($data), $config['secretKey'], true))) {
|
||||||
|
$this->error("签名不正确");
|
||||||
|
}
|
||||||
|
$json = json_decode(base64_decode($data), true);
|
||||||
|
if ($json['deadline'] < time()) {
|
||||||
|
$this->error("请求已经超时");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
addons/hwobs/info.ini
Normal file
10
addons/hwobs/info.ini
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
name = hwobs
|
||||||
|
title = 华为OBS云储存
|
||||||
|
intro = 使用华为OBS作为默认云储存
|
||||||
|
author = FastAdmin
|
||||||
|
website = https://www.fastadmin.net
|
||||||
|
version = 1.2.10
|
||||||
|
state = 1
|
||||||
|
url = /addons/hwobs
|
||||||
|
license = regular
|
||||||
|
licenseto = 34485
|
||||||
37
addons/hwobs/library/Auth.php
Normal file
37
addons/hwobs/library/Auth.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\hwobs\library;
|
||||||
|
|
||||||
|
class Auth
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isModuleAllow()
|
||||||
|
{
|
||||||
|
$config = get_addon_config('hwobs');
|
||||||
|
$module = request()->module();
|
||||||
|
$module = $module ? strtolower($module) : 'index';
|
||||||
|
$noNeedLogin = array_filter(explode(',', $config['noneedlogin'] ?? ''));
|
||||||
|
$isModuleLogin = false;
|
||||||
|
$tagName = 'upload_config_checklogin';
|
||||||
|
foreach (\think\Hook::get($tagName) as $index => $name) {
|
||||||
|
if (\think\Hook::exec($name, $tagName)) {
|
||||||
|
$isModuleLogin = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (in_array($module, $noNeedLogin)
|
||||||
|
|| ($module == 'admin' && \app\admin\library\Auth::instance()->id)
|
||||||
|
|| ($module != 'admin' && \app\common\library\Auth::instance()->id)
|
||||||
|
|| $isModuleLogin) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
addons/hwobs/library/Obs/Internal/Common/CheckoutStream.php
Normal file
63
addons/hwobs/library/Obs/Internal/Common/CheckoutStream.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use GuzzleHttp\Psr7\StreamDecoratorTrait;
|
||||||
|
use Obs\ObsException;
|
||||||
|
|
||||||
|
class CheckoutStream implements StreamInterface {
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
private $expectedLength;
|
||||||
|
private $readedCount = 0;
|
||||||
|
|
||||||
|
public function __construct(StreamInterface $stream, $expectedLength) {
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->expectedLength = $expectedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents() {
|
||||||
|
$contents = $this->stream->getContents();
|
||||||
|
$length = strlen($contents);
|
||||||
|
if ($this->expectedLength !== null && floatval($length) !== $this->expectedLength) {
|
||||||
|
$this -> throwObsException($this->expectedLength, $length);
|
||||||
|
}
|
||||||
|
return $contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($length) {
|
||||||
|
$string = $this->stream->read($length);
|
||||||
|
if ($this->expectedLength !== null) {
|
||||||
|
$this->readedCount += strlen($string);
|
||||||
|
if ($this->stream->eof()) {
|
||||||
|
if (floatval($this->readedCount) !== $this->expectedLength) {
|
||||||
|
$this -> throwObsException($this->expectedLength, $this->readedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function throwObsException($expectedLength, $reaLength) {
|
||||||
|
$obsException = new ObsException('premature end of Content-Length delimiter message body (expected:' . $expectedLength . '; received:' . $reaLength . ')');
|
||||||
|
$obsException->setExceptionType('server');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
23
addons/hwobs/library/Obs/Internal/Common/ITransform.php
Normal file
23
addons/hwobs/library/Obs/Internal/Common/ITransform.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
interface ITransform {
|
||||||
|
public function transform($sign, $para);
|
||||||
|
}
|
||||||
|
|
||||||
257
addons/hwobs/library/Obs/Internal/Common/Model.php
Normal file
257
addons/hwobs/library/Obs/Internal/Common/Model.php
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
||||||
|
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
class Model implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
|
||||||
|
{
|
||||||
|
protected $data;
|
||||||
|
|
||||||
|
public function __construct(?array $data = [])
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return count($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIterator(): \Traversable
|
||||||
|
{
|
||||||
|
return new \ArrayIterator($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clear()
|
||||||
|
{
|
||||||
|
$this->data = [];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAll(?array $keys = null)
|
||||||
|
{
|
||||||
|
return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($key)
|
||||||
|
{
|
||||||
|
return isset($this->data[$key]) ? $this->data[$key] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($key, $value)
|
||||||
|
{
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($key, $value)
|
||||||
|
{
|
||||||
|
if (!array_key_exists($key, $this->data)) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
} elseif (is_array($this->data[$key])) {
|
||||||
|
$this->data[$key][] = $value;
|
||||||
|
} else {
|
||||||
|
$this->data[$key] = [$this->data[$key], $value];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove($key)
|
||||||
|
{
|
||||||
|
unset($this->data[$key]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeys()
|
||||||
|
{
|
||||||
|
return array_keys($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasKey($key)
|
||||||
|
{
|
||||||
|
return array_key_exists($key, $this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function keySearch($key)
|
||||||
|
{
|
||||||
|
foreach (array_keys($this->data) as $k) {
|
||||||
|
if (!strcasecmp($k, $key)) {
|
||||||
|
return $k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function hasValue($value)
|
||||||
|
{
|
||||||
|
return array_search($value, $this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function replace(?array $data)
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function merge($data)
|
||||||
|
{
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$this->add($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function overwriteWith($data)
|
||||||
|
{
|
||||||
|
if (is_array($data)) {
|
||||||
|
$this->data = $data + $this->data;
|
||||||
|
} else {
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map(\Closure $closure, ?array $context = [], $static = true)
|
||||||
|
{
|
||||||
|
$collection = $static ? new static() : new self();
|
||||||
|
foreach ($this as $key => $value) {
|
||||||
|
$collection->add($key, $closure($key, $value, $context));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(\Closure $closure, $static = true)
|
||||||
|
{
|
||||||
|
$collection = ($static) ? new static() : new self();
|
||||||
|
foreach ($this->data as $key => $value) {
|
||||||
|
if ($closure($key, $value)) {
|
||||||
|
$collection->add($key, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetExists($offset): bool
|
||||||
|
{
|
||||||
|
return isset($this->data[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet($offset): mixed
|
||||||
|
{
|
||||||
|
return isset($this->data[$offset]) ? $this->data[$offset] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet($offset, $value): void
|
||||||
|
{
|
||||||
|
$this->data[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset($offset): void
|
||||||
|
{
|
||||||
|
unset($this->data[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPath($path, $value)
|
||||||
|
{
|
||||||
|
$current = &$this->data;
|
||||||
|
$queue = explode('/', $path);
|
||||||
|
while (null !== ($key = array_shift($queue))) {
|
||||||
|
if (!is_array($current)) {
|
||||||
|
throw new \RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
|
||||||
|
} elseif (!$queue) {
|
||||||
|
$current[$key] = $value;
|
||||||
|
} elseif (isset($current[$key])) {
|
||||||
|
$current = &$current[$key];
|
||||||
|
} else {
|
||||||
|
$current[$key] = [];
|
||||||
|
$current = &$current[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPath($path, $separator = '/', $data = null)
|
||||||
|
{
|
||||||
|
if ($data === null) {
|
||||||
|
$data = &$this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = is_array($path) ? $path : explode($separator, $path);
|
||||||
|
while (null !== ($part = array_shift($path))) {
|
||||||
|
if (!is_array($data)) {
|
||||||
|
return null;
|
||||||
|
} elseif (isset($data[$part])) {
|
||||||
|
$data = &$data[$part];
|
||||||
|
} elseif ($part != '*') {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
// Perform a wildcard search by diverging and merging paths
|
||||||
|
$result = [];
|
||||||
|
foreach ($data as $value) {
|
||||||
|
if (!$path) {
|
||||||
|
$result = array_merge_recursive($result, (array) $value);
|
||||||
|
} elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
|
||||||
|
$result = array_merge_recursive($result, (array) $test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$output = 'Debug output of ';
|
||||||
|
$output .= 'model';
|
||||||
|
$output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n";
|
||||||
|
$output .= "Model data\n-----------\n\n";
|
||||||
|
$output .= "This data can be retrieved from the model object using the get() method of the model "
|
||||||
|
. "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n";
|
||||||
|
$lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1);
|
||||||
|
$output .= implode("\n", $lines);
|
||||||
|
|
||||||
|
return $output . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
78
addons/hwobs/library/Obs/Internal/Common/ObsTransform.php
Normal file
78
addons/hwobs/library/Obs/Internal/Common/ObsTransform.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
use Obs\ObsClient;
|
||||||
|
|
||||||
|
class ObsTransform implements ITransform {
|
||||||
|
private static $instance;
|
||||||
|
|
||||||
|
private function __construct(){}
|
||||||
|
|
||||||
|
public static function getInstance() {
|
||||||
|
if (!(self::$instance instanceof ObsTransform)) {
|
||||||
|
self::$instance = new ObsTransform();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function transform($sign, $para) {
|
||||||
|
if ($sign === 'aclHeader') {
|
||||||
|
$para = $this->transAclHeader($para);
|
||||||
|
} else if ($sign === 'aclUri') {
|
||||||
|
$para = $this->transAclGroupUri($para);
|
||||||
|
} else if ($sign == 'event') {
|
||||||
|
$para = $this->transNotificationEvent($para);
|
||||||
|
} else if ($sign == 'storageClass') {
|
||||||
|
$para = $this->transStorageClass($para);
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transAclHeader($para) {
|
||||||
|
if ($para === ObsClient::AclAuthenticatedRead || $para === ObsClient::AclBucketOwnerRead ||
|
||||||
|
$para === ObsClient::AclBucketOwnerFullControl || $para === ObsClient::AclLogDeliveryWrite) {
|
||||||
|
$para = null;
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transAclGroupUri($para) {
|
||||||
|
if ($para === ObsClient::GroupAllUsers) {
|
||||||
|
$para = ObsClient::AllUsers;
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transNotificationEvent($para) {
|
||||||
|
$pos = strpos($para, 's3:');
|
||||||
|
if ($pos !== false && $pos === 0) {
|
||||||
|
$para = substr($para, 3);
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transStorageClass($para) {
|
||||||
|
$search = array('STANDARD', 'STANDARD_IA', 'GLACIER');
|
||||||
|
$repalce = array(ObsClient::StorageClassStandard, ObsClient::StorageClassWarm, ObsClient::StorageClassCold);
|
||||||
|
$para = str_replace($search, $repalce, $para);
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
116
addons/hwobs/library/Obs/Internal/Common/SchemaFormatter.php
Normal file
116
addons/hwobs/library/Obs/Internal/Common/SchemaFormatter.php
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
|
||||||
|
class SchemaFormatter
|
||||||
|
{
|
||||||
|
protected static $utcTimeZone;
|
||||||
|
|
||||||
|
public static function format($fmt, $value)
|
||||||
|
{
|
||||||
|
if($fmt === 'date-time'){
|
||||||
|
return self::formatDateTime($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($fmt === 'data-time-http'){
|
||||||
|
return self::formatDateTimeHttp($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($fmt === 'data-time-middle'){
|
||||||
|
return self::formatDateTimeMiddle($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($fmt === 'date'){
|
||||||
|
return self::formatDate($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($fmt === 'timestamp'){
|
||||||
|
return self::formatTimestamp($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($fmt === 'boolean-string'){
|
||||||
|
return self::formatBooleanAsString($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatDateTimeMiddle($dateTime)
|
||||||
|
{
|
||||||
|
if (is_string($dateTime)) {
|
||||||
|
$dateTime = new \DateTime($dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dateTime instanceof \DateTime) {
|
||||||
|
return $dateTime -> format('Y-m-d\T00:00:00\Z');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatDateTime($value)
|
||||||
|
{
|
||||||
|
return self::dateFormatter($value, 'Y-m-d\TH:i:s\Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatDateTimeHttp($value)
|
||||||
|
{
|
||||||
|
return self::dateFormatter($value, 'D, d M Y H:i:s \G\M\T');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatDate($value)
|
||||||
|
{
|
||||||
|
return self::dateFormatter($value, 'Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatTime($value)
|
||||||
|
{
|
||||||
|
return self::dateFormatter($value, 'H:i:s');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatBooleanAsString($value)
|
||||||
|
{
|
||||||
|
return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatTimestamp($value)
|
||||||
|
{
|
||||||
|
return (int) self::dateFormatter($value, 'U');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dateFormatter($dt, $fmt)
|
||||||
|
{
|
||||||
|
if (is_numeric($dt)) {
|
||||||
|
return gmdate($fmt, (int) $dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($dt)) {
|
||||||
|
$dt = new \DateTime($dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dt instanceof \DateTime) {
|
||||||
|
if (!self::$utcTimeZone) {
|
||||||
|
self::$utcTimeZone = new \DateTimeZone('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dt->setTimezone(self::$utcTimeZone)->format($fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
422
addons/hwobs/library/Obs/Internal/Common/SdkCurlFactory.php
Normal file
422
addons/hwobs/library/Obs/Internal/Common/SdkCurlFactory.php
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
||||||
|
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
use GuzzleHttp\Psr7\LazyOpenStream;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use GuzzleHttp\Handler\CurlFactoryInterface;
|
||||||
|
use GuzzleHttp\Handler\EasyHandle;
|
||||||
|
|
||||||
|
class SdkCurlFactory implements CurlFactoryInterface
|
||||||
|
{
|
||||||
|
private $handles = [];
|
||||||
|
|
||||||
|
private $maxHandles;
|
||||||
|
|
||||||
|
public function __construct($maxHandles)
|
||||||
|
{
|
||||||
|
$this->maxHandles = $maxHandles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(?RequestInterface $request, ?array $options): EasyHandle
|
||||||
|
{
|
||||||
|
if (isset($options['curl']['body_as_string'])) {
|
||||||
|
$options['_body_as_string'] = $options['curl']['body_as_string'];
|
||||||
|
unset($options['curl']['body_as_string']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$easy = new EasyHandle;
|
||||||
|
$easy->request = $request;
|
||||||
|
$easy->options = $options;
|
||||||
|
$conf = $this->getDefaultConf($easy);
|
||||||
|
$this->applyMethod($easy, $conf);
|
||||||
|
$this->applyHandlerOptions($easy, $conf);
|
||||||
|
$this->applyHeaders($easy, $conf);
|
||||||
|
|
||||||
|
|
||||||
|
unset($conf['_headers']);
|
||||||
|
|
||||||
|
if (isset($options['curl'])) {
|
||||||
|
$conf = array_replace($conf, $options['curl']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
|
||||||
|
if($this->handles){
|
||||||
|
$easy->handle = array_pop($this->handles);
|
||||||
|
}else{
|
||||||
|
$easy->handle = curl_init();
|
||||||
|
}
|
||||||
|
curl_setopt_array($easy->handle, $conf);
|
||||||
|
|
||||||
|
return $easy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
if($this->handles){
|
||||||
|
foreach ($this->handles as $handle){
|
||||||
|
curl_close($handle);
|
||||||
|
}
|
||||||
|
unset($this->handles);
|
||||||
|
$this->handles = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function release(?EasyHandle $easy): void
|
||||||
|
{
|
||||||
|
$resource = $easy->handle;
|
||||||
|
unset($easy->handle);
|
||||||
|
|
||||||
|
if (count($this->handles) >= $this->maxHandles) {
|
||||||
|
curl_close($resource);
|
||||||
|
} else {
|
||||||
|
curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
|
||||||
|
curl_setopt($resource, CURLOPT_READFUNCTION, null);
|
||||||
|
curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
|
||||||
|
curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
|
||||||
|
curl_reset($resource);
|
||||||
|
$this->handles[] = $resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDefaultConf(?EasyHandle $easy)
|
||||||
|
{
|
||||||
|
$conf = [
|
||||||
|
'_headers' => $easy->request->getHeaders(),
|
||||||
|
CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
|
||||||
|
CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
|
||||||
|
CURLOPT_RETURNTRANSFER => false,
|
||||||
|
CURLOPT_HEADER => false,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 150,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (defined('CURLOPT_PROTOCOLS')) {
|
||||||
|
$conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $easy->request->getProtocolVersion();
|
||||||
|
if ($version == 1.1) {
|
||||||
|
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
|
||||||
|
} elseif ($version == 2.0) {
|
||||||
|
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
|
||||||
|
} else {
|
||||||
|
$conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyMethod(?EasyHandle $easy, ?array &$conf)
|
||||||
|
{
|
||||||
|
$body = $easy->request->getBody();
|
||||||
|
$size = $body->getSize();
|
||||||
|
|
||||||
|
if ($size === null || $size > 0) {
|
||||||
|
$this->applyBody($easy->request, $easy->options, $conf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$method = $easy->request->getMethod();
|
||||||
|
if ($method === 'PUT' || $method === 'POST') {
|
||||||
|
if (!$easy->request->hasHeader('Content-Length')) {
|
||||||
|
$conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
|
||||||
|
}
|
||||||
|
} elseif ($method === 'HEAD') {
|
||||||
|
$conf[CURLOPT_NOBODY] = true;
|
||||||
|
unset(
|
||||||
|
$conf[CURLOPT_WRITEFUNCTION],
|
||||||
|
$conf[CURLOPT_READFUNCTION],
|
||||||
|
$conf[CURLOPT_FILE],
|
||||||
|
$conf[CURLOPT_INFILE]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyBody(?RequestInterface $request, ?array $options, ?array &$conf)
|
||||||
|
{
|
||||||
|
$size = $request->hasHeader('Content-Length')
|
||||||
|
? (int) $request->getHeaderLine('Content-Length')
|
||||||
|
: $request->getBody()->getSize();
|
||||||
|
|
||||||
|
if($request->getBody()->getSize() === $size && $request -> getBody() ->tell() <= 0){
|
||||||
|
if (($size !== null && $size < 1000000) ||
|
||||||
|
!empty($options['_body_as_string'])
|
||||||
|
) {
|
||||||
|
$conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
|
||||||
|
$this->removeHeader('Content-Length', $conf);
|
||||||
|
$this->removeHeader('Transfer-Encoding', $conf);
|
||||||
|
} else {
|
||||||
|
$conf[CURLOPT_UPLOAD] = true;
|
||||||
|
if ($size !== null) {
|
||||||
|
$conf[CURLOPT_INFILESIZE] = $size;
|
||||||
|
$this->removeHeader('Content-Length', $conf);
|
||||||
|
}
|
||||||
|
$body = $request->getBody();
|
||||||
|
if ($body->isSeekable()) {
|
||||||
|
$body->rewind();
|
||||||
|
}
|
||||||
|
$conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
|
||||||
|
return $body->read($length);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$body = $request->getBody();
|
||||||
|
$conf[CURLOPT_UPLOAD] = true;
|
||||||
|
$conf[CURLOPT_INFILESIZE] = $size;
|
||||||
|
$readCount = 0;
|
||||||
|
$conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, $readCount, $size) {
|
||||||
|
if($readCount >= $size){
|
||||||
|
$body -> close();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$readCountOnce = $length <= $size ? $length : $size;
|
||||||
|
$readCount += $readCountOnce;
|
||||||
|
return $body->read($readCountOnce);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!$request->hasHeader('Expect')) {
|
||||||
|
$conf[CURLOPT_HTTPHEADER][] = 'Expect:';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$request->hasHeader('Content-Type')) {
|
||||||
|
$conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyHeaders(?EasyHandle $easy, ?array &$conf)
|
||||||
|
{
|
||||||
|
foreach ($conf['_headers'] as $name => $values) {
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$conf[CURLOPT_HTTPHEADER][] = "$name: $value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the Accept header if one was not set
|
||||||
|
if (!$easy->request->hasHeader('Accept')) {
|
||||||
|
$conf[CURLOPT_HTTPHEADER][] = 'Accept:';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function removeHeader($name, ?array &$options)
|
||||||
|
{
|
||||||
|
foreach (array_keys($options['_headers']) as $key) {
|
||||||
|
if (!strcasecmp($key, $name)) {
|
||||||
|
unset($options['_headers'][$key]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyHandlerOptions(?EasyHandle $easy, ?array &$conf)
|
||||||
|
{
|
||||||
|
$options = $easy->options;
|
||||||
|
if (isset($options['verify'])) {
|
||||||
|
$conf[CURLOPT_SSL_VERIFYHOST] = 0;
|
||||||
|
if ($options['verify'] === false) {
|
||||||
|
unset($conf[CURLOPT_CAINFO]);
|
||||||
|
$conf[CURLOPT_SSL_VERIFYPEER] = false;
|
||||||
|
} else {
|
||||||
|
$conf[CURLOPT_SSL_VERIFYPEER] = true;
|
||||||
|
if (is_string($options['verify'])) {
|
||||||
|
if (!file_exists($options['verify'])) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"SSL CA bundle not found: {$options['verify']}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (is_dir($options['verify']) ||
|
||||||
|
(is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
|
||||||
|
$conf[CURLOPT_CAPATH] = $options['verify'];
|
||||||
|
} else {
|
||||||
|
$conf[CURLOPT_CAINFO] = $options['verify'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($options['decode_content'])) {
|
||||||
|
$accept = $easy->request->getHeaderLine('Accept-Encoding');
|
||||||
|
if ($accept) {
|
||||||
|
$conf[CURLOPT_ENCODING] = $accept;
|
||||||
|
} else {
|
||||||
|
$conf[CURLOPT_ENCODING] = '';
|
||||||
|
$conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['sink'])) {
|
||||||
|
$sink = $options['sink'];
|
||||||
|
if (!is_string($sink)) {
|
||||||
|
$sink = \GuzzleHttp\Psr7\Utils::streamFor($sink);
|
||||||
|
} elseif (!is_dir(dirname($sink))) {
|
||||||
|
throw new \RuntimeException(sprintf(
|
||||||
|
'Directory %s does not exist for sink value of %s',
|
||||||
|
dirname($sink),
|
||||||
|
$sink
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$sink = new LazyOpenStream($sink, 'w+');
|
||||||
|
}
|
||||||
|
$easy->sink = $sink;
|
||||||
|
$conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
|
||||||
|
return $sink->write($write);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
$conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
|
||||||
|
$easy->sink = \GuzzleHttp\Psr7\Utils::streamFor($conf[CURLOPT_FILE]);
|
||||||
|
}
|
||||||
|
$timeoutRequiresNoSignal = false;
|
||||||
|
if (isset($options['timeout'])) {
|
||||||
|
$timeoutRequiresNoSignal |= $options['timeout'] < 1;
|
||||||
|
$conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['force_ip_resolve'])) {
|
||||||
|
if ('v4' === $options['force_ip_resolve']) {
|
||||||
|
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
|
||||||
|
} else if ('v6' === $options['force_ip_resolve']) {
|
||||||
|
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['connect_timeout'])) {
|
||||||
|
$timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
|
||||||
|
$conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
||||||
|
$conf[CURLOPT_NOSIGNAL] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['proxy'])) {
|
||||||
|
if (!is_array($options['proxy'])) {
|
||||||
|
$conf[CURLOPT_PROXY] = $options['proxy'];
|
||||||
|
} else {
|
||||||
|
$scheme = $easy->request->getUri()->getScheme();
|
||||||
|
if (isset($options['proxy'][$scheme])) {
|
||||||
|
$host = $easy->request->getUri()->getHost();
|
||||||
|
if (!isset($options['proxy']['no']) ||
|
||||||
|
!\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
|
||||||
|
) {
|
||||||
|
$conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['cert'])) {
|
||||||
|
$cert = $options['cert'];
|
||||||
|
if (is_array($cert)) {
|
||||||
|
$conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
|
||||||
|
$cert = $cert[0];
|
||||||
|
}
|
||||||
|
if (!file_exists($cert)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"SSL certificate not found: {$cert}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$conf[CURLOPT_SSLCERT] = $cert;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['ssl_key'])) {
|
||||||
|
$sslKey = $options['ssl_key'];
|
||||||
|
if (is_array($sslKey)) {
|
||||||
|
$conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1];
|
||||||
|
$sslKey = $sslKey[0];
|
||||||
|
}
|
||||||
|
if (!file_exists($sslKey)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
"SSL private key not found: {$sslKey}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$conf[CURLOPT_SSLKEY] = $sslKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['progress'])) {
|
||||||
|
$progress = $options['progress'];
|
||||||
|
if (!is_callable($progress)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'progress client option must be callable'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$conf[CURLOPT_NOPROGRESS] = false;
|
||||||
|
$conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
|
||||||
|
$args = func_get_args();
|
||||||
|
if (is_resource($args[0])) {
|
||||||
|
array_shift($args);
|
||||||
|
}
|
||||||
|
call_user_func_array($progress, $args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($options['debug'])) {
|
||||||
|
$conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']);
|
||||||
|
$conf[CURLOPT_VERBOSE] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function createHeaderFn(?EasyHandle $easy)
|
||||||
|
{
|
||||||
|
if (isset($easy->options['on_headers'])) {
|
||||||
|
$onHeaders = $easy->options['on_headers'];
|
||||||
|
|
||||||
|
if (!is_callable($onHeaders)) {
|
||||||
|
throw new \InvalidArgumentException('on_headers must be callable');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$onHeaders = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return function ($ch, $h) use (
|
||||||
|
$onHeaders,
|
||||||
|
$easy,
|
||||||
|
&$startingResponse
|
||||||
|
) {
|
||||||
|
$value = trim($h);
|
||||||
|
if ($value === '') {
|
||||||
|
$startingResponse = true;
|
||||||
|
$easy->createResponse();
|
||||||
|
if ($onHeaders !== null) {
|
||||||
|
try {
|
||||||
|
$onHeaders($easy->response);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$easy->onHeadersException = $e;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($startingResponse) {
|
||||||
|
$startingResponse = false;
|
||||||
|
$easy->headers = [$value];
|
||||||
|
} else {
|
||||||
|
$easy->headers[] = $value;
|
||||||
|
}
|
||||||
|
return strlen($h);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
502
addons/hwobs/library/Obs/Internal/Common/SdkStreamHandler.php
Normal file
502
addons/hwobs/library/Obs/Internal/Common/SdkStreamHandler.php
Normal file
@@ -0,0 +1,502 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
||||||
|
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use GuzzleHttp\Promise\FulfilledPromise;
|
||||||
|
use GuzzleHttp\Promise\PromiseInterface;
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
use GuzzleHttp\TransferStats;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
class SdkStreamHandler
|
||||||
|
{
|
||||||
|
private $lastHeaders = [];
|
||||||
|
|
||||||
|
public function __invoke(?RequestInterface $request, ?array $options)
|
||||||
|
{
|
||||||
|
if (isset($options['delay'])) {
|
||||||
|
usleep($options['delay'] * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
$startTime = isset($options['on_stats']) ? microtime(true) : null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$request = $request->withoutHeader('Expect');
|
||||||
|
|
||||||
|
if (0 === $request->getBody()->getSize()) {
|
||||||
|
$request = $request->withHeader('Content-Length', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->createResponse(
|
||||||
|
$request,
|
||||||
|
$options,
|
||||||
|
$this->createStream($request, $options),
|
||||||
|
$startTime
|
||||||
|
);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
throw $e;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$message = $e->getMessage();
|
||||||
|
if (strpos($message, 'getaddrinfo')
|
||||||
|
|| strpos($message, 'Connection refused')
|
||||||
|
|| strpos($message, "couldn't connect to host")
|
||||||
|
) {
|
||||||
|
$e = new ConnectException($e->getMessage(), $request, $e);
|
||||||
|
}
|
||||||
|
$e = RequestException::wrapException($request, $e);
|
||||||
|
$this->invokeStats($options, $request, $startTime, null, $e);
|
||||||
|
|
||||||
|
return \GuzzleHttp\Promise\rejection_for($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function invokeStats(
|
||||||
|
?array $options,
|
||||||
|
?RequestInterface $request,
|
||||||
|
$startTime,
|
||||||
|
?ResponseInterface $response = null,
|
||||||
|
$error = null
|
||||||
|
) {
|
||||||
|
if (isset($options['on_stats'])) {
|
||||||
|
$stats = new TransferStats(
|
||||||
|
$request,
|
||||||
|
$response,
|
||||||
|
microtime(true) - $startTime,
|
||||||
|
$error,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
call_user_func($options['on_stats'], $stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createResponse(
|
||||||
|
?RequestInterface $request,
|
||||||
|
?array $options,
|
||||||
|
$stream,
|
||||||
|
$startTime
|
||||||
|
) {
|
||||||
|
$hdrs = $this->lastHeaders;
|
||||||
|
$this->lastHeaders = [];
|
||||||
|
$parts = explode(' ', array_shift($hdrs), 3);
|
||||||
|
$ver = explode('/', $parts[0])[1];
|
||||||
|
$status = $parts[1];
|
||||||
|
$reason = isset($parts[2]) ? $parts[2] : null;
|
||||||
|
$headers = \GuzzleHttp\headers_from_lines($hdrs);
|
||||||
|
list ($stream, $headers) = $this->checkDecode($options, $headers, $stream);
|
||||||
|
$stream = \GuzzleHttp\Psr7\Utils::streamFor($stream);
|
||||||
|
$sink = $stream;
|
||||||
|
|
||||||
|
if (strcasecmp('HEAD', $request->getMethod())) {
|
||||||
|
$sink = $this->createSink($stream, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
|
||||||
|
|
||||||
|
if (isset($options['on_headers'])) {
|
||||||
|
try {
|
||||||
|
$options['on_headers']($response);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$msg = 'An error was encountered during the on_headers event';
|
||||||
|
$ex = new RequestException($msg, $request, $response, $e);
|
||||||
|
return \GuzzleHttp\Promise\rejection_for($ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sink !== $stream) {
|
||||||
|
$this->drain(
|
||||||
|
$stream,
|
||||||
|
$sink,
|
||||||
|
$response->getHeaderLine('Content-Length')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->invokeStats($options, $request, $startTime, $response, null);
|
||||||
|
|
||||||
|
return new FulfilledPromise($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createSink(StreamInterface $stream, ?array $options)
|
||||||
|
{
|
||||||
|
if (!empty($options['stream'])) {
|
||||||
|
return $stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sink = isset($options['sink'])
|
||||||
|
? $options['sink']
|
||||||
|
: fopen('php://temp', 'r+');
|
||||||
|
|
||||||
|
return is_string($sink)
|
||||||
|
? new Psr7\LazyOpenStream($sink, 'w+')
|
||||||
|
: \GuzzleHttp\Psr7\Utils::streamFor($sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkDecode(?array $options, ?array $headers, $stream)
|
||||||
|
{
|
||||||
|
if (!empty($options['decode_content'])) {
|
||||||
|
$normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
|
||||||
|
if (isset($normalizedKeys['content-encoding'])) {
|
||||||
|
$encoding = $headers[$normalizedKeys['content-encoding']];
|
||||||
|
if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
|
||||||
|
$stream = new Psr7\InflateStream(
|
||||||
|
\GuzzleHttp\Psr7\Utils::streamFor($stream)
|
||||||
|
);
|
||||||
|
$headers['x-encoded-content-encoding']
|
||||||
|
= $headers[$normalizedKeys['content-encoding']];
|
||||||
|
unset($headers[$normalizedKeys['content-encoding']]);
|
||||||
|
if (isset($normalizedKeys['content-length'])) {
|
||||||
|
$headers['x-encoded-content-length']
|
||||||
|
= $headers[$normalizedKeys['content-length']];
|
||||||
|
|
||||||
|
$length = (int) $stream->getSize();
|
||||||
|
if ($length === 0) {
|
||||||
|
unset($headers[$normalizedKeys['content-length']]);
|
||||||
|
} else {
|
||||||
|
$headers[$normalizedKeys['content-length']] = [$length];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$stream, $headers];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function drain(
|
||||||
|
StreamInterface $source,
|
||||||
|
StreamInterface $sink,
|
||||||
|
$contentLength
|
||||||
|
) {
|
||||||
|
\GuzzleHttp\Psr7\Utils::copyToStream(
|
||||||
|
$source,
|
||||||
|
$sink,
|
||||||
|
(strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
|
||||||
|
);
|
||||||
|
|
||||||
|
$sink->seek(0);
|
||||||
|
$source->close();
|
||||||
|
|
||||||
|
return $sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createResource(callable $callback)
|
||||||
|
{
|
||||||
|
$errors = null;
|
||||||
|
set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
|
||||||
|
$errors[] = [
|
||||||
|
'message' => $msg,
|
||||||
|
'file' => $file,
|
||||||
|
'line' => $line
|
||||||
|
];
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
$resource = $callback();
|
||||||
|
restore_error_handler();
|
||||||
|
|
||||||
|
if (!$resource) {
|
||||||
|
$message = 'Error creating resource: ';
|
||||||
|
foreach ($errors as $err) {
|
||||||
|
foreach ($err as $key => $value) {
|
||||||
|
$message .= "[$key] $value" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new \RuntimeException(trim($message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createStream(?RequestInterface $request, ?array $options)
|
||||||
|
{
|
||||||
|
static $methods;
|
||||||
|
if (!$methods) {
|
||||||
|
$methods = array_flip(get_class_methods(__CLASS__));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->getProtocolVersion() == '1.1'
|
||||||
|
&& !$request->hasHeader('Connection')
|
||||||
|
) {
|
||||||
|
$request = $request->withHeader('Connection', 'close');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($options['verify'])) {
|
||||||
|
$options['verify'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
$context = $this->getDefaultContext($request, $options);
|
||||||
|
|
||||||
|
if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
|
||||||
|
throw new \InvalidArgumentException('on_headers must be callable');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($options)) {
|
||||||
|
foreach ($options as $key => $value) {
|
||||||
|
$method = "add_{$key}";
|
||||||
|
if (isset($methods[$method])) {
|
||||||
|
$this->{$method}($request, $context, $value, $params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['stream_context'])) {
|
||||||
|
if (!is_array($options['stream_context'])) {
|
||||||
|
throw new \InvalidArgumentException('stream_context must be an array');
|
||||||
|
}
|
||||||
|
$context = array_replace_recursive(
|
||||||
|
$context,
|
||||||
|
$options['stream_context']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['auth'])
|
||||||
|
&& is_array($options['auth'])
|
||||||
|
&& isset($options['auth'][2])
|
||||||
|
&& 'ntlm' == $options['auth'][2]
|
||||||
|
) {
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = $this->resolveHost($request, $options);
|
||||||
|
|
||||||
|
$context = $this->createResource(
|
||||||
|
function () use ($context, $params) {
|
||||||
|
return stream_context_create($context, $params);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->createResource(
|
||||||
|
function () use ($uri, &$http_response_header, $context, $options) {
|
||||||
|
$resource = fopen((string) $uri, 'r', null, $context);
|
||||||
|
$this->lastHeaders = $http_response_header;
|
||||||
|
|
||||||
|
if (isset($options['read_timeout'])) {
|
||||||
|
$readTimeout = $options['read_timeout'];
|
||||||
|
$sec = (int) $readTimeout;
|
||||||
|
$usec = ($readTimeout - $sec) * 100000;
|
||||||
|
stream_set_timeout($resource, $sec, $usec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $resource;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveHost(?RequestInterface $request, ?array $options)
|
||||||
|
{
|
||||||
|
$uri = $request->getUri();
|
||||||
|
|
||||||
|
if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
|
||||||
|
if ('v4' === $options['force_ip_resolve']) {
|
||||||
|
$records = dns_get_record($uri->getHost(), DNS_A);
|
||||||
|
if (!isset($records[0]['ip'])) {
|
||||||
|
throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
|
||||||
|
}
|
||||||
|
$uri = $uri->withHost($records[0]['ip']);
|
||||||
|
} elseif ('v6' === $options['force_ip_resolve']) {
|
||||||
|
$records = dns_get_record($uri->getHost(), DNS_AAAA);
|
||||||
|
if (!isset($records[0]['ipv6'])) {
|
||||||
|
throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
|
||||||
|
}
|
||||||
|
$uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDefaultContext(?RequestInterface $request)
|
||||||
|
{
|
||||||
|
$headers = '';
|
||||||
|
foreach ($request->getHeaders() as $name => $value) {
|
||||||
|
foreach ($value as $val) {
|
||||||
|
$headers .= "$name: $val\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = [
|
||||||
|
'http' => [
|
||||||
|
'method' => $request->getMethod(),
|
||||||
|
'header' => $headers,
|
||||||
|
'protocol_version' => $request->getProtocolVersion(),
|
||||||
|
'ignore_errors' => true,
|
||||||
|
'follow_location' => 0,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$body = (string) $request->getBody();
|
||||||
|
|
||||||
|
if (!empty($body)) {
|
||||||
|
$context['http']['content'] = $body;
|
||||||
|
if (!$request->hasHeader('Content-Type')) {
|
||||||
|
$context['http']['header'] .= "Content-Type:\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$context['http']['header'] = rtrim($context['http']['header']);
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_proxy(?RequestInterface $request, &$options, $value, &$params)
|
||||||
|
{
|
||||||
|
if (!is_array($value)) {
|
||||||
|
$options['http']['proxy'] = $value;
|
||||||
|
} else {
|
||||||
|
$scheme = $request->getUri()->getScheme();
|
||||||
|
if (isset($value[$scheme])) {
|
||||||
|
if (!isset($value['no'])
|
||||||
|
|| !\GuzzleHttp\is_host_in_noproxy(
|
||||||
|
$request->getUri()->getHost(),
|
||||||
|
$value['no']
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$options['http']['proxy'] = $value[$scheme];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_timeout(?RequestInterface $request, &$options, $value, &$params)
|
||||||
|
{
|
||||||
|
if ($value > 0) {
|
||||||
|
$options['http']['timeout'] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_verify(?RequestInterface $request, &$options, $value, &$params)
|
||||||
|
{
|
||||||
|
if ($value === true) {
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
$options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
|
||||||
|
}
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
$options['ssl']['cafile'] = $value;
|
||||||
|
if (!file_exists($value)) {
|
||||||
|
throw new \RuntimeException("SSL CA bundle not found: $value");
|
||||||
|
}
|
||||||
|
} elseif ($value === false) {
|
||||||
|
$options['ssl']['verify_peer'] = false;
|
||||||
|
$options['ssl']['verify_peer_name'] = false;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw new \InvalidArgumentException('Invalid verify request option');
|
||||||
|
}
|
||||||
|
|
||||||
|
$options['ssl']['verify_peer'] = true;
|
||||||
|
$options['ssl']['verify_peer_name'] = true;
|
||||||
|
$options['ssl']['allow_self_signed'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_cert(?RequestInterface $request, &$options, $value, &$params)
|
||||||
|
{
|
||||||
|
if (is_array($value)) {
|
||||||
|
$options['ssl']['passphrase'] = $value[1];
|
||||||
|
$value = $value[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($value)) {
|
||||||
|
throw new \RuntimeException("SSL certificate not found: {$value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$options['ssl']['local_cert'] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_progress(?RequestInterface $request, &$options, $value, &$params)
|
||||||
|
{
|
||||||
|
$this->addNotification(
|
||||||
|
$params,
|
||||||
|
function ($code, $a, $b, $c, $transferred, $total) use ($value) {
|
||||||
|
if ($code == STREAM_NOTIFY_PROGRESS) {
|
||||||
|
$value($total, $transferred, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_debug(?RequestInterface $request, &$options, $value, &$params)
|
||||||
|
{
|
||||||
|
if ($value === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static $map = [
|
||||||
|
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
||||||
|
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
||||||
|
STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
|
||||||
|
STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
|
||||||
|
STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
|
||||||
|
STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
|
||||||
|
STREAM_NOTIFY_PROGRESS => 'PROGRESS',
|
||||||
|
STREAM_NOTIFY_FAILURE => 'FAILURE',
|
||||||
|
STREAM_NOTIFY_COMPLETED => 'COMPLETED',
|
||||||
|
STREAM_NOTIFY_RESOLVE => 'RESOLVE',
|
||||||
|
];
|
||||||
|
static $args = ['severity', 'message', 'message_code',
|
||||||
|
'bytes_transferred', 'bytes_max'];
|
||||||
|
|
||||||
|
$value = \GuzzleHttp\debug_resource($value);
|
||||||
|
$ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
|
||||||
|
$this->addNotification(
|
||||||
|
$params,
|
||||||
|
function () use ($ident, $value, $map, $args) {
|
||||||
|
$passed = func_get_args();
|
||||||
|
$code = array_shift($passed);
|
||||||
|
fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
|
||||||
|
foreach (array_filter($passed) as $i => $v) {
|
||||||
|
fwrite($value, $args[$i] . ': "' . $v . '" ');
|
||||||
|
}
|
||||||
|
fwrite($value, "\n");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addNotification(?array &$params, callable $notify)
|
||||||
|
{
|
||||||
|
if (!isset($params['notification'])) {
|
||||||
|
$params['notification'] = $notify;
|
||||||
|
} else {
|
||||||
|
$params['notification'] = $this->callArray([
|
||||||
|
$params['notification'],
|
||||||
|
$notify
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function callArray(?array $functions)
|
||||||
|
{
|
||||||
|
return function () use ($functions) {
|
||||||
|
$args = func_get_args();
|
||||||
|
foreach ($functions as $fn) {
|
||||||
|
call_user_func_array($fn, $args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
interface ToArrayInterface
|
||||||
|
{
|
||||||
|
public function toArray();
|
||||||
|
}
|
||||||
83
addons/hwobs/library/Obs/Internal/Common/V2Transform.php
Normal file
83
addons/hwobs/library/Obs/Internal/Common/V2Transform.php
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Common;
|
||||||
|
|
||||||
|
use Obs\ObsClient;
|
||||||
|
use Obs\Internal\Resource\V2Constants;
|
||||||
|
|
||||||
|
class V2Transform implements ITransform{
|
||||||
|
private static $instance;
|
||||||
|
|
||||||
|
private function __construct(){}
|
||||||
|
|
||||||
|
public static function getInstance() {
|
||||||
|
if (!(self::$instance instanceof V2Transform)) {
|
||||||
|
self::$instance = new V2Transform();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transform($sign, $para) {
|
||||||
|
if ($sign === 'storageClass') {
|
||||||
|
$para = $this->transStorageClass($para);
|
||||||
|
} else if ($sign === 'aclHeader') {
|
||||||
|
$para = $this->transAclHeader($para);
|
||||||
|
} else if ($sign === 'aclUri') {
|
||||||
|
$para = $this->transAclGroupUri($para);
|
||||||
|
} else if ($sign == 'event') {
|
||||||
|
$para = $this->transNotificationEvent($para);
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transStorageClass($para) {
|
||||||
|
$search = array(ObsClient::StorageClassStandard, ObsClient::StorageClassWarm, ObsClient::StorageClassCold);
|
||||||
|
$repalce = array('STANDARD', 'STANDARD_IA', 'GLACIER');
|
||||||
|
$para = str_replace($search, $repalce, $para);
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transAclHeader($para) {
|
||||||
|
if ($para === ObsClient::AclPublicReadDelivered || $para === ObsClient::AclPublicReadWriteDelivered) {
|
||||||
|
$para = null;
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transAclGroupUri($para) {
|
||||||
|
if ($para === ObsClient::GroupAllUsers) {
|
||||||
|
$para = V2Constants::GROUP_ALL_USERS_PREFIX . $para;
|
||||||
|
} else if ($para === ObsClient::GroupAuthenticatedUsers) {
|
||||||
|
$para = V2Constants::GROUP_AUTHENTICATED_USERS_PREFIX . $para;
|
||||||
|
} else if ($para === ObsClient::GroupLogDelivery) {
|
||||||
|
$para = V2Constants::GROUP_LOG_DELIVERY_PREFIX . $para;
|
||||||
|
} else if ($para === ObsClient::AllUsers) {
|
||||||
|
$para = V2Constants::GROUP_ALL_USERS_PREFIX . ObsClient::GroupAllUsers;
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function transNotificationEvent($para) {
|
||||||
|
$pos = strpos($para, 's3:');
|
||||||
|
if ($pos === false || $pos !== 0) {
|
||||||
|
$para = 's3:' . $para;
|
||||||
|
}
|
||||||
|
return $para;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
380
addons/hwobs/library/Obs/Internal/GetResponseTrait.php
Normal file
380
addons/hwobs/library/Obs/Internal/GetResponseTrait.php
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use Obs\ObsException;
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
use Obs\Internal\Resource\Constants;
|
||||||
|
use Obs\Log\ObsLog;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Obs\Internal\Common\CheckoutStream;
|
||||||
|
|
||||||
|
trait GetResponseTrait
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $exceptionResponseMode = true;
|
||||||
|
|
||||||
|
protected $chunkSize = 65536;
|
||||||
|
|
||||||
|
protected function isClientError(Response $response)
|
||||||
|
{
|
||||||
|
return $response -> getStatusCode() >= 400 && $response -> getStatusCode() < 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseXmlByType($searchPath, $key, &$value, $xml, $prefix)
|
||||||
|
{
|
||||||
|
$type = 'string';
|
||||||
|
|
||||||
|
if(isset($value['sentAs'])){
|
||||||
|
$key = $value['sentAs'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($searchPath === null){
|
||||||
|
$searchPath = '//'.$prefix.$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($value['type'])){
|
||||||
|
$type = $value['type'];
|
||||||
|
if($type === 'array'){
|
||||||
|
$items = $value['items'];
|
||||||
|
if(isset($value['wrapper'])){
|
||||||
|
$paths = explode('/', $searchPath);
|
||||||
|
$size = count($paths);
|
||||||
|
if ($size > 1) {
|
||||||
|
$end = $paths[$size - 1];
|
||||||
|
$paths[$size - 1] = $value['wrapper'];
|
||||||
|
$paths[] = $end;
|
||||||
|
$searchPath = implode('/', $paths) .'/' . $prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$array = [];
|
||||||
|
if(!isset($value['data']['xmlFlattened'])){
|
||||||
|
$pkey = isset($items['sentAs']) ? $items['sentAs'] : $items['name'];
|
||||||
|
$_searchPath = $searchPath .'/' . $prefix .$pkey;
|
||||||
|
}else{
|
||||||
|
$pkey = $key;
|
||||||
|
$_searchPath = $searchPath;
|
||||||
|
}
|
||||||
|
if($result = $xml -> xpath($_searchPath)){
|
||||||
|
if(is_array($result)){
|
||||||
|
foreach ($result as $subXml){
|
||||||
|
$subXml = simplexml_load_string($subXml -> asXML());
|
||||||
|
$subPrefix = $this->getXpathPrefix($subXml);
|
||||||
|
$array[] = $this->parseXmlByType('//'.$subPrefix. $pkey, $pkey, $items, $subXml, $subPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $array;
|
||||||
|
}else if($type === 'object'){
|
||||||
|
$properties = $value['properties'];
|
||||||
|
$array = [];
|
||||||
|
foreach ($properties as $pkey => $pvalue){
|
||||||
|
$name = isset($pvalue['sentAs']) ? $pvalue['sentAs'] : $pkey;
|
||||||
|
$array[$pkey] = $this->parseXmlByType($searchPath.'/' . $prefix .$name, $name, $pvalue, $xml, $prefix);
|
||||||
|
}
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($result = $xml -> xpath($searchPath)){
|
||||||
|
if($type === 'boolean'){
|
||||||
|
return strval($result[0]) !== 'false';
|
||||||
|
}else if($type === 'numeric' || $type === 'float'){
|
||||||
|
return floatval($result[0]);
|
||||||
|
}else if($type === 'int' || $type === 'integer'){
|
||||||
|
return intval($result[0]);
|
||||||
|
}else{
|
||||||
|
return strval($result[0]);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if($type === 'boolean'){
|
||||||
|
return false;
|
||||||
|
}else if($type === 'numeric' || $type === 'float' || $type === 'int' || $type === 'integer'){
|
||||||
|
return null;
|
||||||
|
}else{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseCommonHeaders($model, $response){
|
||||||
|
$constants = Constants::selectConstants($this -> signature);
|
||||||
|
foreach($constants::COMMON_HEADERS as $key => $value){
|
||||||
|
$model[$value] = $response -> getHeaderLine($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseItems($responseParameters, $model, $response, $body)
|
||||||
|
{
|
||||||
|
$prefix = '';
|
||||||
|
|
||||||
|
$this->parseCommonHeaders($model, $response);
|
||||||
|
|
||||||
|
$closeBody = false;
|
||||||
|
try{
|
||||||
|
foreach ($responseParameters as $key => $value){
|
||||||
|
if(isset($value['location'])){
|
||||||
|
$location = $value['location'];
|
||||||
|
if($location === 'header'){
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||||
|
$isSet = false;
|
||||||
|
if(isset($value['type'])){
|
||||||
|
$type = $value['type'];
|
||||||
|
if($type === 'object'){
|
||||||
|
$headers = $response -> getHeaders();
|
||||||
|
$temp = [];
|
||||||
|
foreach ($headers as $headerName => $headerValue){
|
||||||
|
if(stripos($headerName, $name) === 0){
|
||||||
|
$metaKey = rawurldecode(substr($headerName, strlen($name)));
|
||||||
|
$temp[$metaKey] = rawurldecode($response -> getHeaderLine($headerName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$model[$key] = $temp;
|
||||||
|
$isSet = true;
|
||||||
|
}else{
|
||||||
|
if($response -> hasHeader($name)){
|
||||||
|
if($type === 'boolean'){
|
||||||
|
$model[$key] = ($response -> getHeaderLine($name)) !== 'false';
|
||||||
|
$isSet = true;
|
||||||
|
}else if($type === 'numeric' || $type === 'float'){
|
||||||
|
$model[$key] = floatval($response -> getHeaderLine($name));
|
||||||
|
$isSet = true;
|
||||||
|
}else if($type === 'int' || $type === 'integer'){
|
||||||
|
$model[$key] = intval($response -> getHeaderLine($name));
|
||||||
|
$isSet = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!$isSet){
|
||||||
|
$model[$key] = rawurldecode($response -> getHeaderLine($name));
|
||||||
|
}
|
||||||
|
}else if($location === 'xml' && $body !== null){
|
||||||
|
if(!isset($xml) && ($xml = simplexml_load_string($body -> getContents()))){
|
||||||
|
$prefix = $this ->getXpathPrefix($xml);
|
||||||
|
}
|
||||||
|
$closeBody = true;
|
||||||
|
$model[$key] = $this -> parseXmlByType(null, $key,$value, $xml, $prefix);
|
||||||
|
}else if($location === 'body' && $body !== null){
|
||||||
|
if(isset($value['type']) && $value['type'] === 'stream'){
|
||||||
|
$model[$key] = $body;
|
||||||
|
}else{
|
||||||
|
$model[$key] = $body -> getContents();
|
||||||
|
$closeBody = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}finally {
|
||||||
|
if($closeBody && $body !== null){
|
||||||
|
$body -> close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function writeFile($filePath, StreamInterface &$body, $contentLength)
|
||||||
|
{
|
||||||
|
$filePath = iconv('UTF-8', 'GBK', $filePath);
|
||||||
|
if(is_string($filePath) && $filePath !== '')
|
||||||
|
{
|
||||||
|
$fp = null;
|
||||||
|
$dir = dirname($filePath);
|
||||||
|
try{
|
||||||
|
if(!is_dir($dir))
|
||||||
|
{
|
||||||
|
mkdir($dir,0755,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(($fp = fopen($filePath, 'w')))
|
||||||
|
{
|
||||||
|
while(!$body->eof())
|
||||||
|
{
|
||||||
|
$str = $body->read($this->chunkSize);
|
||||||
|
fwrite($fp, $str);
|
||||||
|
}
|
||||||
|
fflush($fp);
|
||||||
|
ObsLog::commonLog(DEBUG, "write file %s ok",$filePath);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ObsLog::commonLog(ERROR, "open file error,file path:%s",$filePath);
|
||||||
|
}
|
||||||
|
}finally{
|
||||||
|
if($fp){
|
||||||
|
fclose($fp);
|
||||||
|
}
|
||||||
|
$body->close();
|
||||||
|
$body = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseXmlToException($body, $obsException){
|
||||||
|
try{
|
||||||
|
$xmlErrorBody = trim($body -> getContents());
|
||||||
|
if($xmlErrorBody && ($xml = simplexml_load_string($xmlErrorBody))){
|
||||||
|
$prefix = $this->getXpathPrefix($xml);
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'Code')) {
|
||||||
|
$obsException-> setExceptionCode(strval($tempXml[0]));
|
||||||
|
}
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'RequestId')) {
|
||||||
|
$obsException-> setRequestId(strval($tempXml[0]));
|
||||||
|
}
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'Message')) {
|
||||||
|
$obsException-> setExceptionMessage(strval($tempXml[0]));
|
||||||
|
}
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'HostId')) {
|
||||||
|
$obsException -> setHostId(strval($tempXml[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}finally{
|
||||||
|
$body -> close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseXmlToModel($body, $model){
|
||||||
|
try{
|
||||||
|
$xmlErrorBody = trim($body -> getContents());
|
||||||
|
if($xmlErrorBody && ($xml = simplexml_load_string($xmlErrorBody))){
|
||||||
|
$prefix = $this->getXpathPrefix($xml);
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'Code')) {
|
||||||
|
$model['Code'] = strval($tempXml[0]);
|
||||||
|
}
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'RequestId')) {
|
||||||
|
$model['RequestId'] = strval($tempXml[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'HostId')) {
|
||||||
|
$model['HostId'] = strval($tempXml[0]);
|
||||||
|
}
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'Resource')) {
|
||||||
|
$model['Resource'] = strval($tempXml[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tempXml = $xml->xpath('//'.$prefix . 'Message')) {
|
||||||
|
$model['Message'] = strval($tempXml[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}finally {
|
||||||
|
$body -> close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseResponse(Model $model, Request $request, Response $response, array $requestConfig)
|
||||||
|
{
|
||||||
|
$statusCode = $response -> getStatusCode();
|
||||||
|
$expectedLength = $response -> getHeaderLine('content-length');
|
||||||
|
$expectedLength = is_numeric($expectedLength) ? floatval($expectedLength) : null;
|
||||||
|
|
||||||
|
$body = new CheckoutStream($response->getBody(), $expectedLength);
|
||||||
|
|
||||||
|
if($statusCode >= 300){
|
||||||
|
if($this-> exceptionResponseMode){
|
||||||
|
$obsException= new ObsException();
|
||||||
|
$obsException-> setRequest($request);
|
||||||
|
$obsException-> setResponse($response);
|
||||||
|
$obsException-> setExceptionType($this->isClientError($response) ? 'client' : 'server');
|
||||||
|
$this->parseXmlToException($body, $obsException);
|
||||||
|
throw $obsException;
|
||||||
|
}else{
|
||||||
|
$this->parseCommonHeaders($model, $response);
|
||||||
|
$this->parseXmlToModel($body, $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
if(!empty($model)){
|
||||||
|
foreach ($model as $key => $value){
|
||||||
|
if($key === 'method'){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(isset($value['type']) && $value['type'] === 'file'){
|
||||||
|
$this->writeFile($value['value'], $body, $expectedLength);
|
||||||
|
}
|
||||||
|
$model[$key] = $value['value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($requestConfig['responseParameters'])){
|
||||||
|
$responseParameters = $requestConfig['responseParameters'];
|
||||||
|
if(isset($responseParameters['type']) && $responseParameters['type'] === 'object'){
|
||||||
|
$responseParameters = $responseParameters['properties'];
|
||||||
|
}
|
||||||
|
$this->parseItems($responseParameters, $model, $response, $body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$model['HttpStatusCode'] = $statusCode;
|
||||||
|
$model['Reason'] = $response -> getReasonPhrase();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getXpathPrefix($xml)
|
||||||
|
{
|
||||||
|
$namespaces = $xml -> getDocNamespaces();
|
||||||
|
if (isset($namespaces[''])) {
|
||||||
|
$xml->registerXPathNamespace('ns', $namespaces['']);
|
||||||
|
$prefix = 'ns:';
|
||||||
|
} else {
|
||||||
|
$prefix = '';
|
||||||
|
}
|
||||||
|
return $prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildException(Request $request, RequestException $exception, $message)
|
||||||
|
{
|
||||||
|
$response = $exception-> hasResponse() ? $exception-> getResponse() : null;
|
||||||
|
$obsException= new ObsException($message ? $message : $exception-> getMessage());
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
$obsException-> setRequest($request);
|
||||||
|
if($response){
|
||||||
|
$obsException-> setResponse($response);
|
||||||
|
$obsException-> setExceptionType($this->isClientError($response) ? 'client' : 'server');
|
||||||
|
$this->parseXmlToException($response -> getBody(), $obsException);
|
||||||
|
if ($obsException->getRequestId() === null) {
|
||||||
|
$prefix = strcasecmp($this->signature, 'obs' ) === 0 ? 'x-obs-' : 'x-amz-';
|
||||||
|
$requestId = $response->getHeaderLine($prefix . 'request-id');
|
||||||
|
$obsException->setRequestId($requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $obsException;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseExceptionAsync(Request $request, RequestException $exception, $message=null)
|
||||||
|
{
|
||||||
|
return $this->buildException($request, $exception, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseException(Model $model, Request $request, RequestException $exception, $message=null)
|
||||||
|
{
|
||||||
|
$response = $exception-> hasResponse() ? $exception-> getResponse() : null;
|
||||||
|
|
||||||
|
if($this-> exceptionResponseMode){
|
||||||
|
throw $this->buildException($request, $exception, $message);
|
||||||
|
}else{
|
||||||
|
if($response){
|
||||||
|
$model['HttpStatusCode'] = $response -> getStatusCode();
|
||||||
|
$model['Reason'] = $response -> getReasonPhrase();
|
||||||
|
$this->parseXmlToModel($response -> getBody(), $model);
|
||||||
|
}else{
|
||||||
|
$model['HttpStatusCode'] = -1;
|
||||||
|
$model['Message'] = $exception -> getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
addons/hwobs/library/Obs/Internal/Resource/Constants.php
Normal file
123
addons/hwobs/library/Obs/Internal/Resource/Constants.php
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Resource;
|
||||||
|
|
||||||
|
class Constants {
|
||||||
|
const ALLOWED_RESOURCE_PARAMTER_NAMES = [
|
||||||
|
'acl',
|
||||||
|
'policy',
|
||||||
|
'torrent',
|
||||||
|
'logging',
|
||||||
|
'location',
|
||||||
|
'storageinfo',
|
||||||
|
'quota',
|
||||||
|
'storagepolicy',
|
||||||
|
'requestpayment',
|
||||||
|
'versions',
|
||||||
|
'versioning',
|
||||||
|
'versionid',
|
||||||
|
'uploads',
|
||||||
|
'uploadid',
|
||||||
|
'partnumber',
|
||||||
|
'website',
|
||||||
|
'notification',
|
||||||
|
'lifecycle',
|
||||||
|
'deletebucket',
|
||||||
|
'delete',
|
||||||
|
'cors',
|
||||||
|
'restore',
|
||||||
|
'tagging',
|
||||||
|
'response-content-type',
|
||||||
|
'response-content-language',
|
||||||
|
'response-expires',
|
||||||
|
'response-cache-control',
|
||||||
|
'response-content-disposition',
|
||||||
|
'response-content-encoding',
|
||||||
|
'x-image-process',
|
||||||
|
|
||||||
|
'backtosource',
|
||||||
|
'storageclass',
|
||||||
|
'replication',
|
||||||
|
'append',
|
||||||
|
'position',
|
||||||
|
'x-oss-process'
|
||||||
|
];
|
||||||
|
const ALLOWED_REQUEST_HTTP_HEADER_METADATA_NAMES = [
|
||||||
|
'content-type',
|
||||||
|
'content-md5',
|
||||||
|
'content-length',
|
||||||
|
'content-language',
|
||||||
|
'expires',
|
||||||
|
'origin',
|
||||||
|
'cache-control',
|
||||||
|
'content-disposition',
|
||||||
|
'content-encoding',
|
||||||
|
'access-control-request-method',
|
||||||
|
'access-control-request-headers',
|
||||||
|
'x-default-storage-class',
|
||||||
|
'location',
|
||||||
|
'date',
|
||||||
|
'etag',
|
||||||
|
'range',
|
||||||
|
'host',
|
||||||
|
'if-modified-since',
|
||||||
|
'if-unmodified-since',
|
||||||
|
'if-match',
|
||||||
|
'if-none-match',
|
||||||
|
'last-modified',
|
||||||
|
'content-range',
|
||||||
|
|
||||||
|
'success-action-redirect'
|
||||||
|
];
|
||||||
|
const ALLOWED_RESPONSE_HTTP_HEADER_METADATA_NAMES = [
|
||||||
|
'content-type',
|
||||||
|
'content-md5',
|
||||||
|
'content-length',
|
||||||
|
'content-language',
|
||||||
|
'expires',
|
||||||
|
'origin',
|
||||||
|
'cache-control',
|
||||||
|
'content-disposition',
|
||||||
|
'content-encoding',
|
||||||
|
'x-default-storage-class',
|
||||||
|
'location',
|
||||||
|
'date',
|
||||||
|
'etag',
|
||||||
|
'host',
|
||||||
|
'last-modified',
|
||||||
|
'content-range',
|
||||||
|
'x-reserved',
|
||||||
|
'access-control-allow-origin',
|
||||||
|
'access-control-allow-headers',
|
||||||
|
'access-control-max-age',
|
||||||
|
'access-control-allow-methods',
|
||||||
|
'access-control-expose-headers',
|
||||||
|
'connection'
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function selectConstants($signature) {
|
||||||
|
$signature = (strcasecmp ( $signature, 'obs' ) === 0) ? 'OBS' : 'V2';
|
||||||
|
return __NAMESPACE__ . '\\' . $signature . 'Constants';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function selectRequestResource($signature) {
|
||||||
|
$signature = (strcasecmp ( $signature, 'obs' ) === 0) ? 'OBS' : 'V2';
|
||||||
|
return (__NAMESPACE__ . '\\' . $signature . 'RequestResource');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
addons/hwobs/library/Obs/Internal/Resource/OBSConstants.php
Normal file
35
addons/hwobs/library/Obs/Internal/Resource/OBSConstants.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Resource;
|
||||||
|
|
||||||
|
class OBSConstants extends Constants {
|
||||||
|
const FLAG = 'OBS';
|
||||||
|
const METADATA_PREFIX = 'x-obs-meta-';
|
||||||
|
const HEADER_PREFIX = 'x-obs-';
|
||||||
|
const ALTERNATIVE_DATE_HEADER = 'x-obs-date';
|
||||||
|
const SECURITY_TOKEN_HEAD = 'x-obs-security-token';
|
||||||
|
const TEMPURL_AK_HEAD = 'AccessKeyId';
|
||||||
|
|
||||||
|
const COMMON_HEADERS = [
|
||||||
|
'content-length' => 'ContentLength',
|
||||||
|
'date' => 'Date',
|
||||||
|
'x-obs-request-id' => 'RequestId',
|
||||||
|
'x-obs-id-2' => 'Id2',
|
||||||
|
'x-reserved' => 'Reserved'
|
||||||
|
];
|
||||||
|
}
|
||||||
4071
addons/hwobs/library/Obs/Internal/Resource/OBSRequestResource.php
Normal file
4071
addons/hwobs/library/Obs/Internal/Resource/OBSRequestResource.php
Normal file
File diff suppressed because it is too large
Load Diff
38
addons/hwobs/library/Obs/Internal/Resource/V2Constants.php
Normal file
38
addons/hwobs/library/Obs/Internal/Resource/V2Constants.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Resource;
|
||||||
|
|
||||||
|
class V2Constants extends Constants {
|
||||||
|
const FLAG = 'AWS';
|
||||||
|
const METADATA_PREFIX = 'x-amz-meta-';
|
||||||
|
const HEADER_PREFIX = 'x-amz-';
|
||||||
|
const ALTERNATIVE_DATE_HEADER = 'x-amz-date';
|
||||||
|
const SECURITY_TOKEN_HEAD = 'x-amz-security-token';
|
||||||
|
const TEMPURL_AK_HEAD = 'AWSAccessKeyId';
|
||||||
|
|
||||||
|
const GROUP_ALL_USERS_PREFIX = 'http://acs.amazonaws.com/groups/global/';
|
||||||
|
const GROUP_AUTHENTICATED_USERS_PREFIX = 'http://acs.amazonaws.com/groups/global/';
|
||||||
|
const GROUP_LOG_DELIVERY_PREFIX = 'http://acs.amazonaws.com/groups/s3/';
|
||||||
|
|
||||||
|
const COMMON_HEADERS = [
|
||||||
|
'content-length' => 'ContentLength',
|
||||||
|
'date' => 'Date',
|
||||||
|
'x-amz-request-id' => 'RequestId',
|
||||||
|
'x-amz-id-2' => 'Id2',
|
||||||
|
'x-reserved' => 'Reserved'
|
||||||
|
];
|
||||||
|
}
|
||||||
3890
addons/hwobs/library/Obs/Internal/Resource/V2RequestResource.php
Normal file
3890
addons/hwobs/library/Obs/Internal/Resource/V2RequestResource.php
Normal file
File diff suppressed because it is too large
Load Diff
718
addons/hwobs/library/Obs/Internal/SendRequestTrait.php
Normal file
718
addons/hwobs/library/Obs/Internal/SendRequestTrait.php
Normal file
@@ -0,0 +1,718 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
use Obs\Log\ObsLog;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
use Obs\Internal\Resource\V2Constants;
|
||||||
|
use Obs\ObsException;
|
||||||
|
use Obs\Internal\Signature\V4Signature;
|
||||||
|
use Obs\Internal\Signature\DefaultSignature;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Obs\Internal\Resource\Constants;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Obs\Internal\Resource\V2RequestResource;
|
||||||
|
|
||||||
|
trait SendRequestTrait
|
||||||
|
{
|
||||||
|
protected $ak;
|
||||||
|
|
||||||
|
protected $sk;
|
||||||
|
|
||||||
|
protected $securityToken = false;
|
||||||
|
|
||||||
|
protected $endpoint = '';
|
||||||
|
|
||||||
|
protected $pathStyle = false;
|
||||||
|
|
||||||
|
protected $region = 'region';
|
||||||
|
|
||||||
|
protected $signature = 'v2';
|
||||||
|
|
||||||
|
protected $sslVerify = false;
|
||||||
|
|
||||||
|
protected $maxRetryCount = 3;
|
||||||
|
|
||||||
|
protected $timeout = 0;
|
||||||
|
|
||||||
|
protected $socketTimeout = 60;
|
||||||
|
|
||||||
|
protected $connectTimeout = 60;
|
||||||
|
|
||||||
|
protected $isCname = false;
|
||||||
|
|
||||||
|
/** @var Client */
|
||||||
|
protected $httpClient;
|
||||||
|
|
||||||
|
public function createSignedUrl(array $args=[]){
|
||||||
|
if (strcasecmp($this -> signature, 'v4') === 0) {
|
||||||
|
return $this -> createV4SignedUrl($args);
|
||||||
|
}
|
||||||
|
return $this->createCommonSignedUrl($args, $this->signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createV2SignedUrl(array $args=[]) {
|
||||||
|
return $this->createCommonSignedUrl($args, 'v2');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createCommonSignedUrl(array $args=[], $signature = '') {
|
||||||
|
if(!isset($args['Method'])){
|
||||||
|
$obsException = new ObsException('Method param must be specified, allowed values: GET | PUT | HEAD | POST | DELETE | OPTIONS');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
$method = strval($args['Method']);
|
||||||
|
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||||
|
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||||
|
$specialParam = isset($args['SpecialParam'])? strval($args['SpecialParam']): null;
|
||||||
|
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||||
|
|
||||||
|
$headers = [];
|
||||||
|
if(isset($args['Headers']) && is_array($args['Headers']) ){
|
||||||
|
foreach ($args['Headers'] as $key => $val){
|
||||||
|
if(is_string($key) && $key !== ''){
|
||||||
|
$headers[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$queryParams = [];
|
||||||
|
if(isset($args['QueryParams']) && is_array($args['QueryParams']) ){
|
||||||
|
foreach ($args['QueryParams'] as $key => $val){
|
||||||
|
if(is_string($key) && $key !== ''){
|
||||||
|
$queryParams[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$constants = Constants::selectConstants($signature);
|
||||||
|
if($this->securityToken && !isset($queryParams[$constants::SECURITY_TOKEN_HEAD])){
|
||||||
|
$queryParams[$constants::SECURITY_TOKEN_HEAD] = $this->securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sign = new DefaultSignature($this->ak, $this->sk, $this->pathStyle, $this->endpoint, $method, $this->signature, $this->securityToken, $this->isCname);
|
||||||
|
|
||||||
|
$url = parse_url($this->endpoint);
|
||||||
|
$host = $url['host'];
|
||||||
|
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
if($bucketName){
|
||||||
|
if($this-> pathStyle){
|
||||||
|
$result = '/' . $bucketName;
|
||||||
|
}else{
|
||||||
|
$host = $this->isCname ? $host : $bucketName . '.' . $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers['Host'] = $host;
|
||||||
|
|
||||||
|
if($objectKey){
|
||||||
|
$objectKey = $sign ->urlencodeWithSafe($objectKey);
|
||||||
|
$result .= '/' . $objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .= '?';
|
||||||
|
|
||||||
|
if($specialParam){
|
||||||
|
$queryParams[$specialParam] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryParams[$constants::TEMPURL_AK_HEAD] = $this->ak;
|
||||||
|
|
||||||
|
|
||||||
|
if(!is_numeric($expires) || $expires < 0){
|
||||||
|
$expires = 300;
|
||||||
|
}
|
||||||
|
$expires = intval($expires) + intval(microtime(true));
|
||||||
|
|
||||||
|
$queryParams['Expires'] = strval($expires);
|
||||||
|
|
||||||
|
$_queryParams = [];
|
||||||
|
|
||||||
|
foreach ($queryParams as $key => $val){
|
||||||
|
$key = $sign -> urlencodeWithSafe($key);
|
||||||
|
$val = $sign -> urlencodeWithSafe($val);
|
||||||
|
$_queryParams[$key] = $val;
|
||||||
|
$result .= $key;
|
||||||
|
if($val){
|
||||||
|
$result .= '=' . $val;
|
||||||
|
}
|
||||||
|
$result .= '&';
|
||||||
|
}
|
||||||
|
|
||||||
|
$canonicalstring = $sign ->makeCanonicalstring($method, $headers, $_queryParams, $bucketName, $objectKey, $expires);
|
||||||
|
$signatureContent = base64_encode(hash_hmac('sha1', $canonicalstring, $this->sk, true));
|
||||||
|
|
||||||
|
$result .= 'Signature=' . $sign->urlencodeWithSafe($signatureContent);
|
||||||
|
|
||||||
|
$model = new Model();
|
||||||
|
$model['ActualSignedRequestHeaders'] = $headers;
|
||||||
|
$model['SignedUrl'] = $url['scheme'] . '://' . $host . ':' . (isset($url['port']) ? $url['port'] : (strtolower($url['scheme']) === 'https' ? '443' : '80')) . $result;
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createV4SignedUrl(array $args=[]){
|
||||||
|
if(!isset($args['Method'])){
|
||||||
|
$obsException= new ObsException('Method param must be specified, allowed values: GET | PUT | HEAD | POST | DELETE | OPTIONS');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
$method = strval($args['Method']);
|
||||||
|
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||||
|
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||||
|
$specialParam = isset($args['SpecialParam'])? strval($args['SpecialParam']): null;
|
||||||
|
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||||
|
$headers = [];
|
||||||
|
if(isset($args['Headers']) && is_array($args['Headers']) ){
|
||||||
|
foreach ($args['Headers'] as $key => $val){
|
||||||
|
if(is_string($key) && $key !== ''){
|
||||||
|
$headers[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryParams = [];
|
||||||
|
if(isset($args['QueryParams']) && is_array($args['QueryParams']) ){
|
||||||
|
foreach ($args['QueryParams'] as $key => $val){
|
||||||
|
if(is_string($key) && $key !== ''){
|
||||||
|
$queryParams[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->securityToken && !isset($queryParams['x-amz-security-token'])){
|
||||||
|
$queryParams['x-amz-security-token'] = $this->securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
$v4 = new V4Signature($this->ak, $this->sk, $this->pathStyle, $this->endpoint, $this->region, $method, $this->signature, $this->securityToken, $this->isCname);
|
||||||
|
|
||||||
|
$url = parse_url($this->endpoint);
|
||||||
|
$host = $url['host'];
|
||||||
|
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
if($bucketName){
|
||||||
|
if($this-> pathStyle){
|
||||||
|
$result = '/' . $bucketName;
|
||||||
|
}else{
|
||||||
|
$host = $this->isCname ? $host : $bucketName . '.' . $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers['Host'] = $host;
|
||||||
|
|
||||||
|
if($objectKey){
|
||||||
|
$objectKey = $v4 -> urlencodeWithSafe($objectKey);
|
||||||
|
$result .= '/' . $objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .= '?';
|
||||||
|
|
||||||
|
if($specialParam){
|
||||||
|
$queryParams[$specialParam] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_numeric($expires) || $expires < 0){
|
||||||
|
$expires = 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
$expires = strval($expires);
|
||||||
|
|
||||||
|
$date = isset($headers['date']) ? $headers['date'] : (isset($headers['Date']) ? $headers['Date'] : null);
|
||||||
|
|
||||||
|
$timestamp = $date ? date_create_from_format('D, d M Y H:i:s \G\M\T', $date, new \DateTimeZone ('UTC')) -> getTimestamp()
|
||||||
|
:time();
|
||||||
|
|
||||||
|
$longDate = gmdate('Ymd\THis\Z', $timestamp);
|
||||||
|
$shortDate = substr($longDate, 0, 8);
|
||||||
|
|
||||||
|
$headers['host'] = $host;
|
||||||
|
if(isset($url['port'])){
|
||||||
|
$port = $url['port'];
|
||||||
|
if($port !== 443 && $port !== 80){
|
||||||
|
$headers['host'] = $headers['host'] . ':' . $port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$signedHeaders = $v4 -> getSignedHeaders($headers);
|
||||||
|
|
||||||
|
$queryParams['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
|
||||||
|
$queryParams['X-Amz-Credential'] = $v4 -> getCredential($shortDate);
|
||||||
|
$queryParams['X-Amz-Date'] = $longDate;
|
||||||
|
$queryParams['X-Amz-Expires'] = $expires;
|
||||||
|
$queryParams['X-Amz-SignedHeaders'] = $signedHeaders;
|
||||||
|
|
||||||
|
$_queryParams = [];
|
||||||
|
|
||||||
|
foreach ($queryParams as $key => $val){
|
||||||
|
$key = rawurlencode($key);
|
||||||
|
$val = rawurlencode($val);
|
||||||
|
$_queryParams[$key] = $val;
|
||||||
|
$result .= $key;
|
||||||
|
if($val){
|
||||||
|
$result .= '=' . $val;
|
||||||
|
}
|
||||||
|
$result .= '&';
|
||||||
|
}
|
||||||
|
|
||||||
|
$canonicalstring = $v4 -> makeCanonicalstring($method, $headers, $_queryParams, $bucketName, $objectKey, $signedHeaders, 'UNSIGNED-PAYLOAD');
|
||||||
|
|
||||||
|
$signatureContent = $v4 -> getSignature($canonicalstring, $longDate, $shortDate);
|
||||||
|
|
||||||
|
$result .= 'X-Amz-Signature=' . $v4 -> urlencodeWithSafe($signatureContent);
|
||||||
|
|
||||||
|
$model = new Model();
|
||||||
|
$model['ActualSignedRequestHeaders'] = $headers;
|
||||||
|
$model['SignedUrl'] = $url['scheme'] . '://' . $host . ':' . (isset($url['port']) ? $url['port'] : (strtolower($url['scheme']) === 'https' ? '443' : '80')) . $result;
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createPostSignature(array $args=[]) {
|
||||||
|
if (strcasecmp($this -> signature, 'v4') === 0) {
|
||||||
|
return $this -> createV4PostSignature($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||||
|
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||||
|
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||||
|
|
||||||
|
$formParams = [];
|
||||||
|
|
||||||
|
if(isset($args['FormParams']) && is_array($args['FormParams'])){
|
||||||
|
foreach ($args['FormParams'] as $key => $val){
|
||||||
|
$formParams[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$constants = Constants::selectConstants($this -> signature);
|
||||||
|
if($this->securityToken && !isset($formParams[$constants::SECURITY_TOKEN_HEAD])){
|
||||||
|
$formParams[$constants::SECURITY_TOKEN_HEAD] = $this->securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = time();
|
||||||
|
$expires = gmdate('Y-m-d\TH:i:s\Z', $timestamp + $expires);
|
||||||
|
|
||||||
|
if($bucketName){
|
||||||
|
$formParams['bucket'] = $bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($objectKey){
|
||||||
|
$formParams['key'] = $objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
$policy = [];
|
||||||
|
|
||||||
|
$policy[] = '{"expiration":"';
|
||||||
|
$policy[] = $expires;
|
||||||
|
$policy[] = '", "conditions":[';
|
||||||
|
|
||||||
|
$matchAnyBucket = true;
|
||||||
|
$matchAnyKey = true;
|
||||||
|
|
||||||
|
$conditionAllowKeys = ['acl', 'bucket', 'key', 'success_action_redirect', 'redirect', 'success_action_status'];
|
||||||
|
|
||||||
|
foreach($formParams as $key => $val){
|
||||||
|
if($key){
|
||||||
|
$key = strtolower(strval($key));
|
||||||
|
|
||||||
|
if($key === 'bucket'){
|
||||||
|
$matchAnyBucket = false;
|
||||||
|
}else if($key === 'key'){
|
||||||
|
$matchAnyKey = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!in_array($key, Constants::ALLOWED_REQUEST_HTTP_HEADER_METADATA_NAMES) && strpos($key, $constants::HEADER_PREFIX) !== 0 && !in_array($key, $conditionAllowKeys)){
|
||||||
|
$key = $constants::METADATA_PREFIX . $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$policy[] = '{"';
|
||||||
|
$policy[] = $key;
|
||||||
|
$policy[] = '":"';
|
||||||
|
$policy[] = $val !== null ? strval($val) : '';
|
||||||
|
$policy[] = '"},';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($matchAnyBucket){
|
||||||
|
$policy[] = '["starts-with", "$bucket", ""],';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($matchAnyKey){
|
||||||
|
$policy[] = '["starts-with", "$key", ""],';
|
||||||
|
}
|
||||||
|
|
||||||
|
$policy[] = ']}';
|
||||||
|
|
||||||
|
$originPolicy = implode('', $policy);
|
||||||
|
|
||||||
|
$policy = base64_encode($originPolicy);
|
||||||
|
|
||||||
|
$signatureContent = base64_encode(hash_hmac('sha1', $policy, $this->sk, true));
|
||||||
|
|
||||||
|
$model = new Model();
|
||||||
|
$model['OriginPolicy'] = $originPolicy;
|
||||||
|
$model['Policy'] = $policy;
|
||||||
|
$model['Signature'] = $signatureContent;
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createV4PostSignature(array $args=[]){
|
||||||
|
$bucketName = isset($args['Bucket'])? strval($args['Bucket']): null;
|
||||||
|
$objectKey = isset($args['Key'])? strval($args['Key']): null;
|
||||||
|
$expires = isset($args['Expires']) && is_numeric($args['Expires']) ? intval($args['Expires']): 300;
|
||||||
|
|
||||||
|
$formParams = [];
|
||||||
|
|
||||||
|
if(isset($args['FormParams']) && is_array($args['FormParams'])){
|
||||||
|
foreach ($args['FormParams'] as $key => $val){
|
||||||
|
$formParams[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->securityToken && !isset($formParams['x-amz-security-token'])){
|
||||||
|
$formParams['x-amz-security-token'] = $this->securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = time();
|
||||||
|
$longDate = gmdate('Ymd\THis\Z', $timestamp);
|
||||||
|
$shortDate = substr($longDate, 0, 8);
|
||||||
|
|
||||||
|
$credential = sprintf('%s/%s/%s/s3/aws4_request', $this->ak, $shortDate, $this->region);
|
||||||
|
|
||||||
|
$expires = gmdate('Y-m-d\TH:i:s\Z', $timestamp + $expires);
|
||||||
|
|
||||||
|
$formParams['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
|
||||||
|
$formParams['X-Amz-Date'] = $longDate;
|
||||||
|
$formParams['X-Amz-Credential'] = $credential;
|
||||||
|
|
||||||
|
if($bucketName){
|
||||||
|
$formParams['bucket'] = $bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($objectKey){
|
||||||
|
$formParams['key'] = $objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
$policy = [];
|
||||||
|
|
||||||
|
$policy[] = '{"expiration":"';
|
||||||
|
$policy[] = $expires;
|
||||||
|
$policy[] = '", "conditions":[';
|
||||||
|
|
||||||
|
$matchAnyBucket = true;
|
||||||
|
$matchAnyKey = true;
|
||||||
|
|
||||||
|
$conditionAllowKeys = ['acl', 'bucket', 'key', 'success_action_redirect', 'redirect', 'success_action_status'];
|
||||||
|
|
||||||
|
foreach($formParams as $key => $val){
|
||||||
|
if($key){
|
||||||
|
$key = strtolower(strval($key));
|
||||||
|
|
||||||
|
if($key === 'bucket'){
|
||||||
|
$matchAnyBucket = false;
|
||||||
|
}else if($key === 'key'){
|
||||||
|
$matchAnyKey = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!in_array($key, Constants::ALLOWED_REQUEST_HTTP_HEADER_METADATA_NAMES) && strpos($key, V2Constants::HEADER_PREFIX) !== 0 && !in_array($key, $conditionAllowKeys)){
|
||||||
|
$key = V2Constants::METADATA_PREFIX . $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$policy[] = '{"';
|
||||||
|
$policy[] = $key;
|
||||||
|
$policy[] = '":"';
|
||||||
|
$policy[] = $val !== null ? strval($val) : '';
|
||||||
|
$policy[] = '"},';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($matchAnyBucket){
|
||||||
|
$policy[] = '["starts-with", "$bucket", ""],';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($matchAnyKey){
|
||||||
|
$policy[] = '["starts-with", "$key", ""],';
|
||||||
|
}
|
||||||
|
|
||||||
|
$policy[] = ']}';
|
||||||
|
|
||||||
|
$originPolicy = implode('', $policy);
|
||||||
|
|
||||||
|
$policy = base64_encode($originPolicy);
|
||||||
|
|
||||||
|
$dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $this -> sk, true);
|
||||||
|
$regionKey = hash_hmac('sha256', $this->region, $dateKey, true);
|
||||||
|
$serviceKey = hash_hmac('sha256', 's3', $regionKey, true);
|
||||||
|
$signingKey = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
|
||||||
|
$signatureContent = hash_hmac('sha256', $policy, $signingKey);
|
||||||
|
|
||||||
|
$model = new Model();
|
||||||
|
$model['OriginPolicy'] = $originPolicy;
|
||||||
|
$model['Policy'] = $policy;
|
||||||
|
$model['Algorithm'] = $formParams['X-Amz-Algorithm'];
|
||||||
|
$model['Credential'] = $formParams['X-Amz-Credential'];
|
||||||
|
$model['Date'] = $formParams['X-Amz-Date'];
|
||||||
|
$model['Signature'] = $signatureContent;
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($originMethod, $args)
|
||||||
|
{
|
||||||
|
$method = $originMethod;
|
||||||
|
|
||||||
|
$contents = Constants::selectRequestResource($this->signature);
|
||||||
|
$resource = &$contents::$RESOURCE_ARRAY;
|
||||||
|
$async = false;
|
||||||
|
if(strpos($method, 'Async') === (strlen($method) - 5)){
|
||||||
|
$method = substr($method, 0, strlen($method) - 5);
|
||||||
|
$async = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($resource['aliases'][$method])){
|
||||||
|
$method = $resource['aliases'][$method];
|
||||||
|
}
|
||||||
|
|
||||||
|
$method = lcfirst($method);
|
||||||
|
|
||||||
|
|
||||||
|
$operation = isset($resource['operations'][$method]) ?
|
||||||
|
$resource['operations'][$method] : null;
|
||||||
|
|
||||||
|
if(!$operation){
|
||||||
|
ObsLog::commonLog(WARNING, 'unknow method ' . $originMethod);
|
||||||
|
$obsException= new ObsException('unknow method '. $originMethod);
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
|
if(!$async){
|
||||||
|
ObsLog::commonLog(INFO, 'enter method '. $originMethod. '...');
|
||||||
|
$model = new Model();
|
||||||
|
$model['method'] = $method;
|
||||||
|
$params = empty($args) ? [] : $args[0];
|
||||||
|
$this->checkMimeType($method, $params);
|
||||||
|
$this->doRequest($model, $operation, $params);
|
||||||
|
ObsLog::commonLog(INFO, 'obsclient cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms to execute '. $originMethod);
|
||||||
|
unset($model['method']);
|
||||||
|
return $model;
|
||||||
|
}else{
|
||||||
|
if(empty($args) || !(is_callable($callback = $args[count($args) -1]))){
|
||||||
|
ObsLog::commonLog(WARNING, 'async method ' . $originMethod . ' must pass a CallbackInterface as param');
|
||||||
|
$obsException= new ObsException('async method ' . $originMethod . ' must pass a CallbackInterface as param');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
ObsLog::commonLog(INFO, 'enter method '. $originMethod. '...');
|
||||||
|
$params = count($args) === 1 ? [] : $args[0];
|
||||||
|
$this->checkMimeType($method, $params);
|
||||||
|
$model = new Model();
|
||||||
|
$model['method'] = $method;
|
||||||
|
return $this->doRequestAsync($model, $operation, $params, $callback, $start, $originMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkMimeType($method, &$params){
|
||||||
|
// fix bug that guzzlehttp lib will add the content-type if not set
|
||||||
|
if(($method === 'putObject' || $method === 'initiateMultipartUpload' || $method === 'uploadPart') && (!isset($params['ContentType']) || $params['ContentType'] === null)){
|
||||||
|
if(isset($params['Key'])){
|
||||||
|
$params['ContentType'] = \GuzzleHttp\Psr7\MimeType::fromFilename($params['Key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((!isset($params['ContentType']) || $params['ContentType'] === null) && isset($params['SourceFile'])){
|
||||||
|
$params['ContentType'] = \GuzzleHttp\Psr7\MimeType::fromFilename($params['SourceFile']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($params['ContentType']) || $params['ContentType'] === null){
|
||||||
|
$params['ContentType'] = 'binary/octet-stream';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function makeRequest($model, &$operation, $params, $endpoint = null)
|
||||||
|
{
|
||||||
|
if($endpoint === null){
|
||||||
|
$endpoint = $this->endpoint;
|
||||||
|
}
|
||||||
|
$signatureInterface = strcasecmp($this-> signature, 'v4') === 0 ?
|
||||||
|
new V4Signature($this->ak, $this->sk, $this->pathStyle, $endpoint, $this->region, $model['method'], $this->signature, $this->securityToken, $this->isCname) :
|
||||||
|
new DefaultSignature($this->ak, $this->sk, $this->pathStyle, $endpoint, $model['method'], $this->signature, $this->securityToken, $this->isCname);
|
||||||
|
$authResult = $signatureInterface -> doAuth($operation, $params, $model);
|
||||||
|
$httpMethod = $authResult['method'];
|
||||||
|
ObsLog::commonLog(DEBUG, 'perform '. strtolower($httpMethod) . ' request with url ' . $authResult['requestUrl']);
|
||||||
|
ObsLog::commonLog(DEBUG, 'cannonicalRequest:' . $authResult['cannonicalRequest']);
|
||||||
|
ObsLog::commonLog(DEBUG, 'request headers ' . var_export($authResult['headers'],true));
|
||||||
|
$authResult['headers']['User-Agent'] = self::default_user_agent();
|
||||||
|
if($model['method'] === 'putObject'){
|
||||||
|
$model['ObjectURL'] = ['value' => $authResult['requestUrl']];
|
||||||
|
}
|
||||||
|
return new Request($httpMethod, $authResult['requestUrl'], $authResult['headers'], $authResult['body']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function doRequest($model, &$operation, $params, $endpoint = null)
|
||||||
|
{
|
||||||
|
$request = $this -> makeRequest($model, $operation, $params, $endpoint);
|
||||||
|
$this->sendRequest($model, $operation, $params, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sendRequest($model, &$operation, $params, $request, $requestCount = 1)
|
||||||
|
{
|
||||||
|
$start = microtime(true);
|
||||||
|
$saveAsStream = false;
|
||||||
|
if(isset($operation['stream']) && $operation['stream']){
|
||||||
|
$saveAsStream = isset($params['SaveAsStream']) ? $params['SaveAsStream'] : false;
|
||||||
|
|
||||||
|
if(isset($params['SaveAsFile'])){
|
||||||
|
if($saveAsStream){
|
||||||
|
$obsException = new ObsException('SaveAsStream cannot be used with SaveAsFile together');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
$saveAsStream = true;
|
||||||
|
}
|
||||||
|
if(isset($params['FilePath'])){
|
||||||
|
if($saveAsStream){
|
||||||
|
$obsException = new ObsException('SaveAsStream cannot be used with FilePath together');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
$saveAsStream = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($params['SaveAsFile']) && isset($params['FilePath'])){
|
||||||
|
$obsException = new ObsException('SaveAsFile cannot be used with FilePath together');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$promise = $this->httpClient->sendAsync($request, ['stream' => $saveAsStream])->then(
|
||||||
|
function(Response $response) use ($model, $operation, $params, $request, $requestCount, $start){
|
||||||
|
|
||||||
|
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||||
|
$statusCode = $response -> getStatusCode();
|
||||||
|
$readable = isset($params['Body']) && ($params['Body'] instanceof StreamInterface || is_resource($params['Body']));
|
||||||
|
if($statusCode >= 300 && $statusCode <400 && $statusCode !== 304 && !$readable && $requestCount <= $this->maxRetryCount){
|
||||||
|
if($location = $response -> getHeaderLine('location')){
|
||||||
|
$url = parse_url($this->endpoint);
|
||||||
|
$newUrl = parse_url($location);
|
||||||
|
$scheme = (isset($newUrl['scheme']) ? $newUrl['scheme'] : $url['scheme']);
|
||||||
|
$defaultPort = strtolower($scheme) === 'https' ? '443' : '80';
|
||||||
|
$this->doRequest($model, $operation, $params, $scheme. '://' . $newUrl['host'] .
|
||||||
|
':' . (isset($newUrl['port']) ? $newUrl['port'] : $defaultPort));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this -> parseResponse($model, $request, $response, $operation);
|
||||||
|
},
|
||||||
|
function ($exception) use ($model, $operation, $params, $request, $requestCount, $start) {
|
||||||
|
|
||||||
|
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||||
|
$message = null;
|
||||||
|
if($exception instanceof ConnectException){
|
||||||
|
if($requestCount <= $this->maxRetryCount){
|
||||||
|
$this -> sendRequest($model, $operation, $params, $request, $requestCount + 1);
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
$message = 'Exceeded retry limitation, max retry count:'. $this->maxRetryCount . ', error message:' . $exception -> getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this -> parseException($model, $request, $exception, $message);
|
||||||
|
});
|
||||||
|
$promise -> wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function doRequestAsync($model, &$operation, $params, $callback, $startAsync, $originMethod, $endpoint = null){
|
||||||
|
$request = $this -> makeRequest($model, $operation, $params, $endpoint);
|
||||||
|
return $this->sendRequestAsync($model, $operation, $params, $callback, $startAsync, $originMethod, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sendRequestAsync($model, &$operation, $params, $callback, $startAsync, $originMethod, $request, $requestCount = 1)
|
||||||
|
{
|
||||||
|
$start = microtime(true);
|
||||||
|
|
||||||
|
$saveAsStream = false;
|
||||||
|
if(isset($operation['stream']) && $operation['stream']){
|
||||||
|
$saveAsStream = isset($params['SaveAsStream']) ? $params['SaveAsStream'] : false;
|
||||||
|
|
||||||
|
if($saveAsStream){
|
||||||
|
if(isset($params['SaveAsFile'])){
|
||||||
|
$obsException = new ObsException('SaveAsStream cannot be used with SaveAsFile together');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
if(isset($params['FilePath'])){
|
||||||
|
$obsException = new ObsException('SaveAsStream cannot be used with FilePath together');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($params['SaveAsFile']) && isset($params['FilePath'])){
|
||||||
|
$obsException = new ObsException('SaveAsFile cannot be used with FilePath together');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->httpClient->sendAsync($request, ['stream' => $saveAsStream])->then(
|
||||||
|
function(Response $response) use ($model, $operation, $params, $callback, $startAsync, $originMethod, $request, $start){
|
||||||
|
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||||
|
$statusCode = $response -> getStatusCode();
|
||||||
|
$readable = isset($params['Body']) && ($params['Body'] instanceof StreamInterface || is_resource($params['Body']));
|
||||||
|
if($statusCode === 307 && !$readable){
|
||||||
|
if($location = $response -> getHeaderLine('location')){
|
||||||
|
$url = parse_url($this->endpoint);
|
||||||
|
$newUrl = parse_url($location);
|
||||||
|
$scheme = (isset($newUrl['scheme']) ? $newUrl['scheme'] : $url['scheme']);
|
||||||
|
$defaultPort = strtolower($scheme) === 'https' ? '443' : '80';
|
||||||
|
return $this->doRequestAsync($model, $operation, $params, $callback, $startAsync, $originMethod, $scheme. '://' . $newUrl['host'] .
|
||||||
|
':' . (isset($newUrl['port']) ? $newUrl['port'] : $defaultPort));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this -> parseResponse($model, $request, $response, $operation);
|
||||||
|
ObsLog::commonLog(INFO, 'obsclient cost ' . round(microtime(true) - $startAsync, 3) * 1000 . ' ms to execute '. $originMethod);
|
||||||
|
unset($model['method']);
|
||||||
|
$callback(null, $model);
|
||||||
|
},
|
||||||
|
function ($exception) use ($model, $operation, $params, $callback, $startAsync, $originMethod, $request, $start, $requestCount){
|
||||||
|
ObsLog::commonLog(INFO, 'http request cost ' . round(microtime(true) - $start, 3) * 1000 . ' ms');
|
||||||
|
$message = null;
|
||||||
|
if($exception instanceof ConnectException){
|
||||||
|
if($requestCount <= $this->maxRetryCount){
|
||||||
|
return $this -> sendRequestAsync($model, $operation, $params, $callback, $startAsync, $originMethod, $request, $requestCount + 1);
|
||||||
|
}else{
|
||||||
|
$message = 'Exceeded retry limitation, max retry count:'. $this->maxRetryCount . ', error message:' . $exception -> getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$obsException = $this -> parseExceptionAsync($request, $exception, $message);
|
||||||
|
ObsLog::commonLog(INFO, 'obsclient cost ' . round(microtime(true) - $startAsync, 3) * 1000 . ' ms to execute '. $originMethod);
|
||||||
|
$callback($obsException, null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,462 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Signature;
|
||||||
|
|
||||||
|
use Obs\Log\ObsLog;
|
||||||
|
use Obs\Internal\Resource\Constants;
|
||||||
|
use Obs\ObsException;
|
||||||
|
use Obs\Internal\Common\SchemaFormatter;
|
||||||
|
use GuzzleHttp\Psr7\Stream;
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Obs\Internal\Common\ObsTransform;
|
||||||
|
use Obs\Internal\Common\V2Transform;
|
||||||
|
|
||||||
|
abstract class AbstractSignature implements SignatureInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $ak;
|
||||||
|
|
||||||
|
protected $sk;
|
||||||
|
|
||||||
|
protected $pathStyle;
|
||||||
|
|
||||||
|
protected $endpoint;
|
||||||
|
|
||||||
|
protected $methodName;
|
||||||
|
|
||||||
|
protected $securityToken;
|
||||||
|
|
||||||
|
protected $signature;
|
||||||
|
|
||||||
|
protected $isCname;
|
||||||
|
|
||||||
|
public static function urlencodeWithSafe($val, $safe='/'){
|
||||||
|
if(($len = strlen($val)) === 0){
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$buffer = [];
|
||||||
|
for ($index=0;$index<$len;$index++){
|
||||||
|
$str = $val[$index];
|
||||||
|
$buffer[] = !($pos = strpos($safe, $str)) && $pos !== 0 ? rawurlencode($str) : $str;
|
||||||
|
}
|
||||||
|
return implode('', $buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function __construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken=false, $isCname=false)
|
||||||
|
{
|
||||||
|
$this -> ak = $ak;
|
||||||
|
$this -> sk = $sk;
|
||||||
|
$this -> pathStyle = $pathStyle;
|
||||||
|
$this -> endpoint = $endpoint;
|
||||||
|
$this -> methodName = $methodName;
|
||||||
|
$this -> signature = $signature;
|
||||||
|
$this -> securityToken = $securityToken;
|
||||||
|
$this -> isCname = $isCname;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function transXmlByType($key, &$value, &$subParams, $transHolder)
|
||||||
|
{
|
||||||
|
$xml = [];
|
||||||
|
$treatAsString = false;
|
||||||
|
if(isset($value['type'])){
|
||||||
|
$type = $value['type'];
|
||||||
|
if($type === 'array'){
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||||
|
$subXml = [];
|
||||||
|
foreach($subParams as $item){
|
||||||
|
$temp = $this->transXmlByType($key, $value['items'], $item, $transHolder);
|
||||||
|
if($temp !== ''){
|
||||||
|
$subXml[] = $temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!empty($subXml)){
|
||||||
|
if(!isset($value['data']['xmlFlattened'])){
|
||||||
|
$xml[] = '<' . $name . '>';
|
||||||
|
$xml[] = implode('', $subXml);
|
||||||
|
$xml[] = '</' . $name . '>';
|
||||||
|
}else{
|
||||||
|
$xml[] = implode('', $subXml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if($type === 'object'){
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : (isset($value['name']) ? $value['name'] : $key);
|
||||||
|
$properties = $value['properties'];
|
||||||
|
$subXml = [];
|
||||||
|
$attr = [];
|
||||||
|
foreach ($properties as $pkey => $pvalue){
|
||||||
|
if(isset($pvalue['required']) && $pvalue['required'] && !isset($subParams[$pkey])){
|
||||||
|
$obsException= new ObsException('param:' .$pkey. ' is required');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
if(isset($subParams[$pkey])){
|
||||||
|
if(isset($pvalue['data']) && isset($pvalue['data']['xmlAttribute']) && $pvalue['data']['xmlAttribute']){
|
||||||
|
$attrValue = $this->xml_tansfer(trim(strval($subParams[$pkey])));
|
||||||
|
$attr[$pvalue['sentAs']] = '"' . $attrValue . '"';
|
||||||
|
if(isset($pvalue['data']['xmlNamespace'])){
|
||||||
|
$ns = substr($pvalue['sentAs'], 0, strpos($pvalue['sentAs'], ':'));
|
||||||
|
$attr['xmlns:' . $ns] = '"' . $pvalue['data']['xmlNamespace'] . '"';
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$subXml[] = $this -> transXmlByType($pkey, $pvalue, $subParams[$pkey], $transHolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$val = implode('', $subXml);
|
||||||
|
if($val !== ''){
|
||||||
|
$_name = $name;
|
||||||
|
if(!empty($attr)){
|
||||||
|
foreach ($attr as $akey => $avalue){
|
||||||
|
$_name .= ' ' . $akey . '=' . $avalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!isset($value['data']['xmlFlattened'])){
|
||||||
|
$xml[] = '<' . $_name . '>';
|
||||||
|
$xml[] = $val;
|
||||||
|
$xml[] = '</' . $name . '>';
|
||||||
|
} else {
|
||||||
|
$xml[] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$treatAsString = true;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$treatAsString = true;
|
||||||
|
$type = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($treatAsString){
|
||||||
|
if($type === 'boolean'){
|
||||||
|
if(!is_bool($subParams) && strval($subParams) !== 'false' && strval($subParams) !== 'true'){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a boolean value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}else if($type === 'numeric'){
|
||||||
|
if(!is_numeric($subParams)){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a numeric value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}else if($type === 'float'){
|
||||||
|
if(!is_float($subParams)){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a float value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}else if($type === 'int' || $type === 'integer'){
|
||||||
|
if(!is_int($subParams)){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a int value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||||
|
if(is_bool($subParams)){
|
||||||
|
$val = $subParams ? 'true' : 'false';
|
||||||
|
}else{
|
||||||
|
$val = strval($subParams);
|
||||||
|
}
|
||||||
|
if(isset($value['format'])){
|
||||||
|
$val = SchemaFormatter::format($value['format'], $val);
|
||||||
|
}
|
||||||
|
if (isset($value['transform'])) {
|
||||||
|
$val = $transHolder->transform($value['transform'], $val);
|
||||||
|
}
|
||||||
|
if(isset($val) && $val !== ''){
|
||||||
|
$val = $this->xml_tansfer($val);
|
||||||
|
if(!isset($value['data']['xmlFlattened'])){
|
||||||
|
$xml[] = '<' . $name . '>';
|
||||||
|
$xml[] = $val;
|
||||||
|
$xml[] = '</' . $name . '>';
|
||||||
|
} else {
|
||||||
|
$xml[] = $val;
|
||||||
|
}
|
||||||
|
}else if(isset($value['canEmpty']) && $value['canEmpty']){
|
||||||
|
$xml[] = '<' . $name . '>';
|
||||||
|
$xml[] = $val;
|
||||||
|
$xml[] = '</' . $name . '>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$ret = implode('', $xml);
|
||||||
|
|
||||||
|
if(isset($value['wrapper'])){
|
||||||
|
$ret = '<'. $value['wrapper'] . '>' . $ret . '</'. $value['wrapper'] . '>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function xml_tansfer($tag) {
|
||||||
|
$search = array('&', '<', '>', '\'', '"');
|
||||||
|
$repalce = array('&', '<', '>', ''', '"');
|
||||||
|
$transferXml = str_replace($search, $repalce, $tag);
|
||||||
|
return $transferXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepareAuth(array &$requestConfig, array &$params, Model $model)
|
||||||
|
{
|
||||||
|
$transHolder = strcasecmp($this-> signature, 'obs') === 0 ? ObsTransform::getInstance() : V2Transform::getInstance();
|
||||||
|
$method = $requestConfig['httpMethod'];
|
||||||
|
$requestUrl = $this->endpoint;
|
||||||
|
$headers = [];
|
||||||
|
$pathArgs = [];
|
||||||
|
$dnsParam = null;
|
||||||
|
$uriParam = null;
|
||||||
|
$body = [];
|
||||||
|
$xml = [];
|
||||||
|
|
||||||
|
if(isset($requestConfig['specialParam'])){
|
||||||
|
$pathArgs[$requestConfig['specialParam']] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = ['body' => null];
|
||||||
|
$url = parse_url($requestUrl);
|
||||||
|
$host = $url['host'];
|
||||||
|
|
||||||
|
$fileFlag = false;
|
||||||
|
|
||||||
|
if(isset($requestConfig['requestParameters'])){
|
||||||
|
$paramsMetadata = $requestConfig['requestParameters'];
|
||||||
|
foreach ($paramsMetadata as $key => $value){
|
||||||
|
if(isset($value['required']) && $value['required'] && !isset($params[$key])){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is required');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
if(isset($params[$key]) && isset($value['location'])){
|
||||||
|
$location = $value['location'];
|
||||||
|
$val = $params[$key];
|
||||||
|
$type = 'string';
|
||||||
|
if($val !== '' && isset($value['type'])){
|
||||||
|
$type = $value['type'];
|
||||||
|
if($type === 'boolean'){
|
||||||
|
if(!is_bool($val) && strval($val) !== 'false' && strval($val) !== 'true'){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a boolean value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}else if($type === 'numeric'){
|
||||||
|
if(!is_numeric($val)){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a numeric value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}else if($type === 'float'){
|
||||||
|
if(!is_float($val)){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a float value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}else if($type === 'int' || $type === 'integer'){
|
||||||
|
if(!is_int($val)){
|
||||||
|
$obsException= new ObsException('param:' .$key. ' is not a int value');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($location === 'header'){
|
||||||
|
if($type === 'object'){
|
||||||
|
if(is_array($val)){
|
||||||
|
$sentAs = strtolower($value['sentAs']);
|
||||||
|
foreach ($val as $k => $v){
|
||||||
|
$k = self::urlencodeWithSafe(strtolower($k), ' ;/?:@&=+$,');
|
||||||
|
$name = strpos($k, $sentAs) === 0 ? $k : $sentAs . $k;
|
||||||
|
$headers[$name] = self::urlencodeWithSafe($v, ' ;/?:@&=+$,\'*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if($type === 'array'){
|
||||||
|
if(is_array($val)){
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : (isset($value['items']['sentAs']) ? $value['items']['sentAs'] : $key);
|
||||||
|
$temp = [];
|
||||||
|
foreach ($val as $v){
|
||||||
|
if(($v = strval($v)) !== ''){
|
||||||
|
$temp[] = self::urlencodeWithSafe($val, ' ;/?:@&=+$,\'*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers[$name] = $temp;
|
||||||
|
}
|
||||||
|
}else if($type === 'password'){
|
||||||
|
if(($val = strval($val)) !== ''){
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||||
|
$pwdName = isset($value['pwdSentAs']) ? $value['pwdSentAs'] : $name . '-MD5';
|
||||||
|
$val1 = base64_encode($val);
|
||||||
|
$val2 = base64_encode(md5($val, true));
|
||||||
|
$headers[$name] = $val1;
|
||||||
|
$headers[$pwdName] = $val2;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (isset($value['transform'])) {
|
||||||
|
$val = $transHolder->transform($value['transform'], strval($val));
|
||||||
|
}
|
||||||
|
if(isset($val)){
|
||||||
|
if(is_bool($val)){
|
||||||
|
$val = $val ? 'true' : 'false';
|
||||||
|
}else{
|
||||||
|
$val = strval($val);
|
||||||
|
}
|
||||||
|
if($val !== ''){
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||||
|
if(isset($value['format'])){
|
||||||
|
$val = SchemaFormatter::format($value['format'], $val);
|
||||||
|
}
|
||||||
|
$headers[$name] = self::urlencodeWithSafe($val, ' ;/?:@&=+$,\'*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if($location === 'uri' && $uriParam === null){
|
||||||
|
$uriParam = self::urlencodeWithSafe($val);
|
||||||
|
}else if($location === 'dns' && $dnsParam === null){
|
||||||
|
$dnsParam = $val;
|
||||||
|
}else if($location === 'query'){
|
||||||
|
$name = isset($value['sentAs']) ? $value['sentAs'] : $key;
|
||||||
|
if(strval($val) !== ''){
|
||||||
|
if (strcasecmp ( $this->signature, 'v4' ) === 0) {
|
||||||
|
$pathArgs[rawurlencode($name)] = rawurlencode(strval($val));
|
||||||
|
} else {
|
||||||
|
$pathArgs[self::urlencodeWithSafe($name)] = self::urlencodeWithSafe(strval($val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if($location === 'xml'){
|
||||||
|
$val = $this->transXmlByType($key, $value, $val, $transHolder);
|
||||||
|
if($val !== ''){
|
||||||
|
$xml[] = $val;
|
||||||
|
}
|
||||||
|
}else if($location === 'body'){
|
||||||
|
|
||||||
|
if(isset($result['body'])){
|
||||||
|
$obsException= new ObsException('duplicated body provided');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($type === 'file'){
|
||||||
|
if(!file_exists($val)){
|
||||||
|
$obsException= new ObsException('file[' .$val. '] does not exist');
|
||||||
|
$obsException-> setExceptionType('client');
|
||||||
|
throw $obsException;
|
||||||
|
}
|
||||||
|
$result['body'] = new Stream(fopen($val, 'r'));
|
||||||
|
$fileFlag = true;
|
||||||
|
}else if($type === 'stream'){
|
||||||
|
$result['body'] = $val;
|
||||||
|
}else{
|
||||||
|
$result['body'] = strval($val);
|
||||||
|
}
|
||||||
|
}else if($location === 'response'){
|
||||||
|
$model[$key] = ['value' => $val, 'type' => $type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if($dnsParam){
|
||||||
|
if($this -> pathStyle){
|
||||||
|
$requestUrl = $requestUrl . '/' . $dnsParam;
|
||||||
|
}else{
|
||||||
|
$defaultPort = strtolower($url['scheme']) === 'https' ? '443' : '80';
|
||||||
|
$host = $this -> isCname ? $host : $dnsParam. '.' . $host;
|
||||||
|
$requestUrl = $url['scheme'] . '://' . $host . ':' . (isset($url['port']) ? $url['port'] : $defaultPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($uriParam){
|
||||||
|
$requestUrl = $requestUrl . '/' . $uriParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($pathArgs)){
|
||||||
|
$requestUrl .= '?';
|
||||||
|
$_pathArgs = [];
|
||||||
|
foreach ($pathArgs as $key => $value){
|
||||||
|
$_pathArgs[] = $value === null || $value === '' ? $key : $key . '=' . $value;
|
||||||
|
}
|
||||||
|
$requestUrl .= implode('&', $_pathArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($xml || (isset($requestConfig['data']['xmlAllowEmpty']) && $requestConfig['data']['xmlAllowEmpty'])){
|
||||||
|
$body[] = '<';
|
||||||
|
$xmlRoot = $requestConfig['data']['xmlRoot']['name'];
|
||||||
|
|
||||||
|
$body[] = $xmlRoot;
|
||||||
|
$body[] = '>';
|
||||||
|
$body[] = implode('', $xml);
|
||||||
|
$body[] = '</';
|
||||||
|
$body[] = $xmlRoot;
|
||||||
|
$body[] = '>';
|
||||||
|
$headers['Content-Type'] = 'application/xml';
|
||||||
|
$result['body'] = implode('', $body);
|
||||||
|
|
||||||
|
ObsLog::commonLog(DEBUG, 'request content ' . $result['body']);
|
||||||
|
|
||||||
|
if(isset($requestConfig['data']['contentMd5']) && $requestConfig['data']['contentMd5']){
|
||||||
|
$headers['Content-MD5'] = base64_encode(md5($result['body'],true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($fileFlag && ($result['body'] instanceof StreamInterface)){
|
||||||
|
if($this->methodName === 'uploadPart' && (isset($model['Offset']) || isset($model['PartSize']))){
|
||||||
|
$bodySize = $result['body'] ->getSize();
|
||||||
|
if(isset($model['Offset'])){
|
||||||
|
$offset = intval($model['Offset']['value']);
|
||||||
|
$offset = $offset >= 0 && $offset < $bodySize ? $offset : 0;
|
||||||
|
}else{
|
||||||
|
$offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($model['PartSize'])){
|
||||||
|
$partSize = intval($model['PartSize']['value']);
|
||||||
|
$partSize = $partSize > 0 && $partSize <= ($bodySize - $offset) ? $partSize : $bodySize - $offset;
|
||||||
|
}else{
|
||||||
|
$partSize = $bodySize - $offset;
|
||||||
|
}
|
||||||
|
$result['body'] -> rewind();
|
||||||
|
$result['body'] -> seek($offset);
|
||||||
|
$headers['Content-Length'] = $partSize;
|
||||||
|
}else if(isset($headers['Content-Length'])){
|
||||||
|
$bodySize = $result['body'] -> getSize();
|
||||||
|
if(intval($headers['Content-Length']) > $bodySize){
|
||||||
|
$headers['Content-Length'] = $bodySize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$constants = Constants::selectConstants($this -> signature);
|
||||||
|
|
||||||
|
if($this->securityToken){
|
||||||
|
$headers[$constants::SECURITY_TOKEN_HEAD] = $this->securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers['Host'] = $host;
|
||||||
|
|
||||||
|
$result['host'] = $host;
|
||||||
|
$result['method'] = $method;
|
||||||
|
$result['headers'] = $headers;
|
||||||
|
$result['pathArgs'] = $pathArgs;
|
||||||
|
$result['dnsParam'] = $dnsParam;
|
||||||
|
$result['uriParam'] = $uriParam;
|
||||||
|
$result['requestUrl'] = $requestUrl;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
138
addons/hwobs/library/Obs/Internal/Signature/DefaultSignature.php
Normal file
138
addons/hwobs/library/Obs/Internal/Signature/DefaultSignature.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Signature;
|
||||||
|
|
||||||
|
|
||||||
|
use Obs\Internal\Resource\Constants;
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
use Obs\Internal\Resource\V2Constants;
|
||||||
|
|
||||||
|
class DefaultSignature extends AbstractSignature
|
||||||
|
{
|
||||||
|
const INTEREST_HEADER_KEY_LIST = array('content-type', 'content-md5', 'date');
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken=false, $isCname=false)
|
||||||
|
{
|
||||||
|
|
||||||
|
parent::__construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken, $isCname);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doAuth(array &$requestConfig, array &$params, Model $model)
|
||||||
|
{
|
||||||
|
$result = $this -> prepareAuth($requestConfig, $params, $model);
|
||||||
|
|
||||||
|
$result['headers']['Date'] = gmdate('D, d M Y H:i:s \G\M\T');
|
||||||
|
$canonicalstring = $this-> makeCanonicalstring($result['method'], $result['headers'], $result['pathArgs'], $result['dnsParam'], $result['uriParam']);
|
||||||
|
|
||||||
|
$result['cannonicalRequest'] = $canonicalstring;
|
||||||
|
//print_r($result);
|
||||||
|
$signature = base64_encode(hash_hmac('sha1', $canonicalstring, $this->sk, true));
|
||||||
|
|
||||||
|
$constants = Constants::selectConstants($this -> signature);
|
||||||
|
$signatureFlag = $constants::FLAG;
|
||||||
|
|
||||||
|
$authorization = $signatureFlag . ' ' . $this->ak . ':' . $signature;
|
||||||
|
|
||||||
|
$result['headers']['Authorization'] = $authorization;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeCanonicalstring($method, $headers, $pathArgs, $bucketName, $objectKey, $expires = null)
|
||||||
|
{
|
||||||
|
$buffer = [];
|
||||||
|
$buffer[] = $method;
|
||||||
|
$buffer[] = "\n";
|
||||||
|
$interestHeaders = [];
|
||||||
|
$constants = Constants::selectConstants($this -> signature);
|
||||||
|
|
||||||
|
foreach ($headers as $key => $value){
|
||||||
|
$key = strtolower($key);
|
||||||
|
if(in_array($key, self::INTEREST_HEADER_KEY_LIST) || strpos($key, $constants::HEADER_PREFIX) === 0){
|
||||||
|
$interestHeaders[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(array_key_exists($constants::ALTERNATIVE_DATE_HEADER, $interestHeaders)){
|
||||||
|
$interestHeaders['date'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($expires !== null){
|
||||||
|
$interestHeaders['date'] = strval($expires);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!array_key_exists('content-type', $interestHeaders)){
|
||||||
|
$interestHeaders['content-type'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!array_key_exists('content-md5', $interestHeaders)){
|
||||||
|
$interestHeaders['content-md5'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($interestHeaders);
|
||||||
|
|
||||||
|
foreach ($interestHeaders as $key => $value){
|
||||||
|
if(strpos($key, $constants::HEADER_PREFIX) === 0){
|
||||||
|
$buffer[] = $key . ':' . $value;
|
||||||
|
}else{
|
||||||
|
$buffer[] = $value;
|
||||||
|
}
|
||||||
|
$buffer[] = "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = '';
|
||||||
|
|
||||||
|
$bucketName = $this->isCname ? $headers['Host'] : $bucketName;
|
||||||
|
|
||||||
|
if($bucketName){
|
||||||
|
$uri .= '/';
|
||||||
|
$uri .= $bucketName;
|
||||||
|
if(!$this->pathStyle){
|
||||||
|
$uri .= '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($objectKey){
|
||||||
|
if(!($pos=strripos($uri, '/')) || strlen($uri)-1 !== $pos){
|
||||||
|
$uri .= '/';
|
||||||
|
}
|
||||||
|
$uri .= $objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
$buffer[] = $uri === ''? '/' : $uri;
|
||||||
|
|
||||||
|
|
||||||
|
if(!empty($pathArgs)){
|
||||||
|
ksort($pathArgs);
|
||||||
|
$_pathArgs = [];
|
||||||
|
foreach ($pathArgs as $key => $value){
|
||||||
|
if(in_array(strtolower($key), $constants::ALLOWED_RESOURCE_PARAMTER_NAMES) || strpos($key, $constants::HEADER_PREFIX) === 0){
|
||||||
|
$_pathArgs[] = $value === null || $value === '' ? $key : $key . '=' . urldecode($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!empty($_pathArgs)){
|
||||||
|
$buffer[] = '?';
|
||||||
|
$buffer[] = implode('&', $_pathArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('', $buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Signature;
|
||||||
|
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
|
||||||
|
interface SignatureInterface
|
||||||
|
{
|
||||||
|
function doAuth(array &$requestConfig, array &$params, Model $model);
|
||||||
|
}
|
||||||
199
addons/hwobs/library/Obs/Internal/Signature/V4Signature.php
Normal file
199
addons/hwobs/library/Obs/Internal/Signature/V4Signature.php
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Internal\Signature;
|
||||||
|
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
|
||||||
|
class V4Signature extends AbstractSignature
|
||||||
|
{
|
||||||
|
const CONTENT_SHA256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
|
||||||
|
|
||||||
|
protected $region;
|
||||||
|
|
||||||
|
protected $utcTimeZone;
|
||||||
|
|
||||||
|
public function __construct($ak, $sk, $pathStyle, $endpoint, $region, $methodName, $signature, $securityToken=false, $isCname=false)
|
||||||
|
{
|
||||||
|
parent::__construct($ak, $sk, $pathStyle, $endpoint, $methodName, $signature, $securityToken, $isCname);
|
||||||
|
$this->region = $region;
|
||||||
|
$this->utcTimeZone = new \DateTimeZone ('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doAuth(array &$requestConfig, array &$params, Model $model)
|
||||||
|
{
|
||||||
|
$result = $this -> prepareAuth($requestConfig, $params, $model);
|
||||||
|
|
||||||
|
$result['headers']['x-amz-content-sha256'] = self::CONTENT_SHA256;
|
||||||
|
|
||||||
|
$bucketName = $result['dnsParam'];
|
||||||
|
|
||||||
|
$result['headers']['Host'] = $result['host'];
|
||||||
|
|
||||||
|
$time = null;
|
||||||
|
if(array_key_exists('x-amz-date', $result['headers'])){
|
||||||
|
$time = $result['headers']['x-amz-date'];
|
||||||
|
}else if(array_key_exists('X-Amz-Date', $result['headers'])){
|
||||||
|
$time = $result['headers']['X-Amz-Date'];
|
||||||
|
}
|
||||||
|
$timestamp = $time ? date_create_from_format('Ymd\THis\Z', $time, $this->utcTimeZone) -> getTimestamp()
|
||||||
|
:time();
|
||||||
|
|
||||||
|
$result['headers']['Date'] = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
|
||||||
|
|
||||||
|
$longDate = gmdate('Ymd\THis\Z', $timestamp);
|
||||||
|
$shortDate = substr($longDate, 0, 8);
|
||||||
|
|
||||||
|
$credential = $this-> getCredential($shortDate);
|
||||||
|
|
||||||
|
$signedHeaders = $this->getSignedHeaders($result['headers']);
|
||||||
|
|
||||||
|
$canonicalstring = $this-> makeCanonicalstring($result['method'], $result['headers'], $result['pathArgs'], $bucketName, $result['uriParam'], $signedHeaders);
|
||||||
|
|
||||||
|
$result['cannonicalRequest'] = $canonicalstring;
|
||||||
|
|
||||||
|
$signature = $this -> getSignature($canonicalstring, $longDate, $shortDate);
|
||||||
|
|
||||||
|
$authorization = 'AWS4-HMAC-SHA256 ' . 'Credential=' . $credential. ',' . 'SignedHeaders=' . $signedHeaders . ',' . 'Signature=' . $signature;
|
||||||
|
|
||||||
|
$result['headers']['Authorization'] = $authorization;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSignature($canonicalstring, $longDate, $shortDate)
|
||||||
|
{
|
||||||
|
$stringToSign = [];
|
||||||
|
$stringToSign[] = 'AWS4-HMAC-SHA256';
|
||||||
|
|
||||||
|
$stringToSign[] = "\n";
|
||||||
|
|
||||||
|
$stringToSign[] = $longDate;
|
||||||
|
|
||||||
|
$stringToSign[] = "\n";
|
||||||
|
$stringToSign[] = $this -> getScope($shortDate);
|
||||||
|
$stringToSign[] = "\n";
|
||||||
|
|
||||||
|
$stringToSign[] = hash('sha256', $canonicalstring);
|
||||||
|
|
||||||
|
$dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $this -> sk, true);
|
||||||
|
$regionKey = hash_hmac('sha256', $this->region, $dateKey, true);
|
||||||
|
$serviceKey = hash_hmac('sha256', 's3', $regionKey, true);
|
||||||
|
$signingKey = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
|
||||||
|
$signature = hash_hmac('sha256', implode('', $stringToSign), $signingKey);
|
||||||
|
return $signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCanonicalQueryString($pathArgs)
|
||||||
|
{
|
||||||
|
$queryStr = '';
|
||||||
|
|
||||||
|
ksort($pathArgs);
|
||||||
|
$index = 0;
|
||||||
|
foreach ($pathArgs as $key => $value){
|
||||||
|
$queryStr .= $key . '=' . $value;
|
||||||
|
if($index++ !== count($pathArgs) - 1){
|
||||||
|
$queryStr .= '&';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $queryStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCanonicalHeaders($headers)
|
||||||
|
{
|
||||||
|
$_headers = [];
|
||||||
|
foreach ($headers as $key => $value) {
|
||||||
|
$_headers[strtolower($key)] = $value;
|
||||||
|
}
|
||||||
|
ksort($_headers);
|
||||||
|
|
||||||
|
$canonicalHeaderStr = '';
|
||||||
|
|
||||||
|
foreach ($_headers as $key => $value){
|
||||||
|
$value = is_array($value) ? implode(',', $value) : $value;
|
||||||
|
$canonicalHeaderStr .= $key . ':' . $value;
|
||||||
|
$canonicalHeaderStr .= "\n";
|
||||||
|
}
|
||||||
|
return $canonicalHeaderStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCanonicalURI($bucketName, $objectKey)
|
||||||
|
{
|
||||||
|
$uri = '';
|
||||||
|
if($this -> pathStyle && $bucketName){
|
||||||
|
$uri .= '/' . $bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($objectKey){
|
||||||
|
$uri .= '/' . $objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($uri === ''){
|
||||||
|
$uri = '/';
|
||||||
|
}
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeCanonicalstring($method, $headers, $pathArgs, $bucketName, $objectKey, $signedHeaders=null, $payload=null)
|
||||||
|
{
|
||||||
|
$buffer = [];
|
||||||
|
$buffer[] = $method;
|
||||||
|
$buffer[] = "\n";
|
||||||
|
$buffer[] = $this->getCanonicalURI($bucketName, $objectKey);
|
||||||
|
$buffer[] = "\n";
|
||||||
|
$buffer[] = $this->getCanonicalQueryString($pathArgs);
|
||||||
|
$buffer[] = "\n";
|
||||||
|
$buffer[] = $this->getCanonicalHeaders($headers);
|
||||||
|
$buffer[] = "\n";
|
||||||
|
$buffer[] = $signedHeaders ? $signedHeaders : $this->getSignedHeaders($headers);
|
||||||
|
$buffer[] = "\n";
|
||||||
|
$buffer[] = $payload ? strval($payload) : self::CONTENT_SHA256;
|
||||||
|
|
||||||
|
return implode('', $buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSignedHeaders($headers)
|
||||||
|
{
|
||||||
|
$_headers = [];
|
||||||
|
|
||||||
|
foreach ($headers as $key => $value) {
|
||||||
|
$_headers[] = strtolower($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
sort($_headers);
|
||||||
|
|
||||||
|
$signedHeaders = '';
|
||||||
|
|
||||||
|
foreach ($_headers as $key => $value){
|
||||||
|
$signedHeaders .= $value;
|
||||||
|
if($key !== count($_headers) - 1){
|
||||||
|
$signedHeaders .= ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $signedHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScope($shortDate)
|
||||||
|
{
|
||||||
|
return $shortDate . '/' . $this->region . '/s3/aws4_request';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCredential($shortDate)
|
||||||
|
{
|
||||||
|
return $this->ak . '/' . $this->getScope($shortDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
addons/hwobs/library/Obs/Log/ObsConfig.php
Normal file
28
addons/hwobs/library/Obs/Log/ObsConfig.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Log;
|
||||||
|
|
||||||
|
class ObsConfig
|
||||||
|
{
|
||||||
|
const LOG_FILE_CONFIG = [
|
||||||
|
'FilePath'=>'./logs',
|
||||||
|
'FileName'=>'eSDK-OBS-PHP.log',
|
||||||
|
'MaxFiles'=>10,
|
||||||
|
'Level'=>INFO
|
||||||
|
];
|
||||||
|
}
|
||||||
126
addons/hwobs/library/Obs/Log/ObsLog.php
Normal file
126
addons/hwobs/library/Obs/Log/ObsLog.php
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs\Log;
|
||||||
|
|
||||||
|
use Monolog\Formatter\LineFormatter;
|
||||||
|
use Monolog\Handler\RotatingFileHandler;
|
||||||
|
use Monolog\Logger;
|
||||||
|
|
||||||
|
class ObsLog extends Logger
|
||||||
|
{
|
||||||
|
public static $log = null;
|
||||||
|
|
||||||
|
protected $log_path = './';
|
||||||
|
protected $log_name = null;
|
||||||
|
protected $log_level = Logger::DEBUG;
|
||||||
|
protected $log_maxFiles = 0;
|
||||||
|
|
||||||
|
private $formatter = null;
|
||||||
|
private $handler = null;
|
||||||
|
private $filepath = '';
|
||||||
|
|
||||||
|
public static function initLog($logConfig= [])
|
||||||
|
{
|
||||||
|
$s3log = new ObsLog('');
|
||||||
|
$s3log->setConfig($logConfig);
|
||||||
|
$s3log->cheakDir();
|
||||||
|
$s3log->setFilePath();
|
||||||
|
$s3log->setFormat();
|
||||||
|
$s3log->setHande();
|
||||||
|
}
|
||||||
|
private function setFormat()
|
||||||
|
{
|
||||||
|
$output = '[%datetime%][%level_name%]'.'%message%' . "\n";
|
||||||
|
$this->formatter = new LineFormatter($output);
|
||||||
|
|
||||||
|
}
|
||||||
|
private function setHande()
|
||||||
|
{
|
||||||
|
self::$log = new Logger('obs_logger');
|
||||||
|
$rotating = new RotatingFileHandler($this->filepath, $this->log_maxFiles, $this->log_level);
|
||||||
|
$rotating->setFormatter($this->formatter);
|
||||||
|
self::$log->pushHandler($rotating);
|
||||||
|
}
|
||||||
|
private function setConfig($logConfig= [])
|
||||||
|
{
|
||||||
|
$arr = empty($logConfig) ? ObsConfig::LOG_FILE_CONFIG : $logConfig;
|
||||||
|
$this->log_path = iconv('UTF-8', 'GBK',$arr['FilePath']);
|
||||||
|
$this->log_name = iconv('UTF-8', 'GBK',$arr['FileName']);
|
||||||
|
$this->log_maxFiles = is_numeric($arr['MaxFiles']) ? 0 : intval($arr['MaxFiles']);
|
||||||
|
$this->log_level = $arr['Level'];
|
||||||
|
}
|
||||||
|
private function cheakDir()
|
||||||
|
{
|
||||||
|
if (!is_dir($this->log_path)){
|
||||||
|
mkdir($this->log_path, 0755, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function setFilePath()
|
||||||
|
{
|
||||||
|
$this->filepath = $this->log_path.'/'.$this->log_name;
|
||||||
|
}
|
||||||
|
private static function writeLog($level, $msg)
|
||||||
|
{
|
||||||
|
switch ($level) {
|
||||||
|
case DEBUG:
|
||||||
|
self::$log->debug($msg);
|
||||||
|
break;
|
||||||
|
case INFO:
|
||||||
|
self::$log->info($msg);
|
||||||
|
break;
|
||||||
|
case NOTICE:
|
||||||
|
self::$log->notice($msg);
|
||||||
|
break;
|
||||||
|
case WARNING:
|
||||||
|
self::$log->warning($msg);
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
self::$log->error($msg);
|
||||||
|
break;
|
||||||
|
case CRITICAL:
|
||||||
|
self::$log->critical($msg);
|
||||||
|
break;
|
||||||
|
case ALERT:
|
||||||
|
self::$log->alert($msg);
|
||||||
|
break;
|
||||||
|
case EMERGENCY:
|
||||||
|
self::$log->emergency($msg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function commonLog($level, $format, $args1 = null, $arg2 = null)
|
||||||
|
{
|
||||||
|
if(ObsLog::$log){
|
||||||
|
if ($args1 === null && $arg2 === null) {
|
||||||
|
$msg = urldecode($format);
|
||||||
|
} else {
|
||||||
|
$msg = sprintf($format, $args1, $arg2);
|
||||||
|
}
|
||||||
|
$back = debug_backtrace();
|
||||||
|
$line = $back[0]['line'];
|
||||||
|
$funcname = $back[1]['function'];
|
||||||
|
$filename = basename($back[0]['file']);
|
||||||
|
$message = '['.$filename.':'.$line.']: '.$msg;
|
||||||
|
ObsLog::writeLog($level, $message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
405
addons/hwobs/library/Obs/ObsClient.php
Normal file
405
addons/hwobs/library/Obs/ObsClient.php
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs;
|
||||||
|
|
||||||
|
use Obs\Log\ObsLog;
|
||||||
|
use Obs\Internal\Common\SdkCurlFactory;
|
||||||
|
use Obs\Internal\Common\SdkStreamHandler;
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
use Monolog\Logger;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\HandlerStack;
|
||||||
|
use GuzzleHttp\Handler\CurlHandler;
|
||||||
|
use GuzzleHttp\Handler\CurlMultiHandler;
|
||||||
|
use GuzzleHttp\Handler\Proxy;
|
||||||
|
use GuzzleHttp\Promise\Promise;
|
||||||
|
use Obs\Internal\Resource\Constants;
|
||||||
|
|
||||||
|
|
||||||
|
define('DEBUG', Logger::DEBUG);
|
||||||
|
define('INFO', Logger::INFO);
|
||||||
|
define('NOTICE', Logger::NOTICE);
|
||||||
|
define('WARNING', Logger::WARNING);
|
||||||
|
define('WARN', Logger::WARNING);
|
||||||
|
define('ERROR', Logger::ERROR);
|
||||||
|
define('CRITICAL', Logger::CRITICAL);
|
||||||
|
define('ALERT', Logger::ALERT);
|
||||||
|
define('EMERGENCY', Logger::EMERGENCY);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method Model createPostSignature(array $args=[]);
|
||||||
|
* @method Model createSignedUrl(array $args=[]);
|
||||||
|
* @method Model createBucket(array $args = []);
|
||||||
|
* @method Model listBuckets();
|
||||||
|
* @method Model deleteBucket(array $args = []);
|
||||||
|
* @method Model listObjects(array $args = []);
|
||||||
|
* @method Model listVersions(array $args = []);
|
||||||
|
* @method Model headBucket(array $args = []);
|
||||||
|
* @method Model getBucketMetadata(array $args = []);
|
||||||
|
* @method Model getBucketLocation(array $args = []);
|
||||||
|
* @method Model getBucketStorageInfo(array $args = []);
|
||||||
|
* @method Model setBucketQuota(array $args = []);
|
||||||
|
* @method Model getBucketQuota(array $args = []);
|
||||||
|
* @method Model setBucketStoragePolicy(array $args = []);
|
||||||
|
* @method Model getBucketStoragePolicy(array $args = []);
|
||||||
|
* @method Model setBucketAcl(array $args = []);
|
||||||
|
* @method Model getBucketAcl(array $args = []);
|
||||||
|
* @method Model setBucketLogging(array $args = []);
|
||||||
|
* @method Model getBucketLogging(array $args = []);
|
||||||
|
* @method Model setBucketPolicy(array $args = []);
|
||||||
|
* @method Model getBucketPolicy(array $args = []);
|
||||||
|
* @method Model deleteBucketPolicy(array $args = []);
|
||||||
|
* @method Model setBucketLifecycle(array $args = []);
|
||||||
|
* @method Model getBucketLifecycle(array $args = []);
|
||||||
|
* @method Model deleteBucketLifecycle(array $args = []);
|
||||||
|
* @method Model setBucketWebsite(array $args = []);
|
||||||
|
* @method Model getBucketWebsite(array $args = []);
|
||||||
|
* @method Model deleteBucketWebsite(array $args = []);
|
||||||
|
* @method Model setBucketVersioning(array $args = []);
|
||||||
|
* @method Model getBucketVersioning(array $args = []);
|
||||||
|
* @method Model setBucketCors(array $args = []);
|
||||||
|
* @method Model getBucketCors(array $args = []);
|
||||||
|
* @method Model deleteBucketCors(array $args = []);
|
||||||
|
* @method Model setBucketNotification(array $args = []);
|
||||||
|
* @method Model getBucketNotification(array $args = []);
|
||||||
|
* @method Model setBucketTagging(array $args = []);
|
||||||
|
* @method Model getBucketTagging(array $args = []);
|
||||||
|
* @method Model deleteBucketTagging(array $args = []);
|
||||||
|
* @method Model optionsBucket(array $args = []);
|
||||||
|
*
|
||||||
|
* @method Model putObject(array $args = []);
|
||||||
|
* @method Model getObject(array $args = []);
|
||||||
|
* @method Model copyObject(array $args = []);
|
||||||
|
* @method Model deleteObject(array $args = []);
|
||||||
|
* @method Model deleteObjects(array $args = []);
|
||||||
|
* @method Model getObjectMetadata(array $args = []);
|
||||||
|
* @method Model setObjectAcl(array $args = []);
|
||||||
|
* @method Model getObjectAcl(array $args = []);
|
||||||
|
* @method Model initiateMultipartUpload(array $args = []);
|
||||||
|
* @method Model uploadPart(array $args = []);
|
||||||
|
* @method Model copyPart(array $args = []);
|
||||||
|
* @method Model listParts(array $args = []);
|
||||||
|
* @method Model completeMultipartUpload(array $args = []);
|
||||||
|
* @method Model abortMultipartUpload(array $args = []);
|
||||||
|
* @method Model listMultipartUploads(array $args = []);
|
||||||
|
* @method Model optionsObject(array $args = []);
|
||||||
|
* @method Model restoreObject(array $args = []);
|
||||||
|
*
|
||||||
|
* @method Promise createBucketAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise listBucketsAsync(callable $callback);
|
||||||
|
* @method Promise deleteBucketAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise listObjectsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise listVersionsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise headBucketAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketMetadataAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketLocationAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketStorageInfoAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketQuotaAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketQuotaAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketStoragePolicyAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketStoragePolicyAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketAclAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketAclAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketLoggingAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketLoggingAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketPolicyAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketPolicyAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise deleteBucketPolicyAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketLifecycleAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketLifecycleAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise deleteBucketLifecycleAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketWebsiteAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketWebsiteAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise deleteBucketWebsiteAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketVersioningAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketVersioningAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketCorsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketCorsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise deleteBucketCorsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketNotificationAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketNotificationAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setBucketTaggingAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getBucketTaggingAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise deleteBucketTaggingAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise optionsBucketAsync(array $args = [], callable $callback);
|
||||||
|
*
|
||||||
|
* @method Promise putObjectAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getObjectAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise copyObjectAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise deleteObjectAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise deleteObjectsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getObjectMetadataAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise setObjectAclAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise getObjectAclAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise initiateMultipartUploadAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise uploadPartAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise copyPartAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise listPartsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise completeMultipartUploadAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise abortMultipartUploadAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise listMultipartUploadsAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise optionsObjectAsync(array $args = [], callable $callback);
|
||||||
|
* @method Promise restoreObjectAsync(array $args = [], callable $callback);
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ObsClient
|
||||||
|
{
|
||||||
|
|
||||||
|
const SDK_VERSION = '3.20.5';
|
||||||
|
|
||||||
|
const AclPrivate = 'private';
|
||||||
|
const AclPublicRead = 'public-read';
|
||||||
|
const AclPublicReadWrite = 'public-read-write';
|
||||||
|
const AclPublicReadDelivered = 'public-read-delivered';
|
||||||
|
const AclPublicReadWriteDelivered = 'public-read-write-delivered';
|
||||||
|
|
||||||
|
const AclAuthenticatedRead = 'authenticated-read';
|
||||||
|
const AclBucketOwnerRead = 'bucket-owner-read';
|
||||||
|
const AclBucketOwnerFullControl = 'bucket-owner-full-control';
|
||||||
|
const AclLogDeliveryWrite = 'log-delivery-write';
|
||||||
|
|
||||||
|
const StorageClassStandard = 'STANDARD';
|
||||||
|
const StorageClassWarm = 'WARM';
|
||||||
|
const StorageClassCold = 'COLD';
|
||||||
|
|
||||||
|
const PermissionRead = 'READ';
|
||||||
|
const PermissionWrite = 'WRITE';
|
||||||
|
const PermissionReadAcp = 'READ_ACP';
|
||||||
|
const PermissionWriteAcp = 'WRITE_ACP';
|
||||||
|
const PermissionFullControl = 'FULL_CONTROL';
|
||||||
|
|
||||||
|
const AllUsers = 'Everyone';
|
||||||
|
|
||||||
|
const GroupAllUsers = 'AllUsers';
|
||||||
|
const GroupAuthenticatedUsers = 'AuthenticatedUsers';
|
||||||
|
const GroupLogDelivery = 'LogDelivery';
|
||||||
|
|
||||||
|
const RestoreTierExpedited = 'Expedited';
|
||||||
|
const RestoreTierStandard = 'Standard';
|
||||||
|
const RestoreTierBulk = 'Bulk';
|
||||||
|
|
||||||
|
const GranteeGroup = 'Group';
|
||||||
|
const GranteeUser = 'CanonicalUser';
|
||||||
|
|
||||||
|
const CopyMetadata = 'COPY';
|
||||||
|
const ReplaceMetadata = 'REPLACE';
|
||||||
|
|
||||||
|
const SignatureV2 = 'v2';
|
||||||
|
const SignatureV4 = 'v4';
|
||||||
|
const SigantureObs = 'obs';
|
||||||
|
|
||||||
|
const ObjectCreatedAll = 'ObjectCreated:*';
|
||||||
|
const ObjectCreatedPut = 'ObjectCreated:Put';
|
||||||
|
const ObjectCreatedPost = 'ObjectCreated:Post';
|
||||||
|
const ObjectCreatedCopy = 'ObjectCreated:Copy';
|
||||||
|
const ObjectCreatedCompleteMultipartUpload = 'ObjectCreated:CompleteMultipartUpload';
|
||||||
|
const ObjectRemovedAll = 'ObjectRemoved:*';
|
||||||
|
const ObjectRemovedDelete = 'ObjectRemoved:Delete';
|
||||||
|
const ObjectRemovedDeleteMarkerCreated = 'ObjectRemoved:DeleteMarkerCreated';
|
||||||
|
|
||||||
|
use Internal\SendRequestTrait;
|
||||||
|
use Internal\GetResponseTrait;
|
||||||
|
|
||||||
|
private $factorys;
|
||||||
|
|
||||||
|
public function __construct(array $config = []){
|
||||||
|
$this ->factorys = [];
|
||||||
|
|
||||||
|
$this -> ak = strval($config['key']);
|
||||||
|
$this -> sk = strval($config['secret']);
|
||||||
|
|
||||||
|
if(isset($config['security_token'])){
|
||||||
|
$this -> securityToken = strval($config['security_token']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['endpoint'])){
|
||||||
|
$this -> endpoint = trim(strval($config['endpoint']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this -> endpoint === ''){
|
||||||
|
throw new \RuntimeException('endpoint is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
while($this -> endpoint[strlen($this -> endpoint)-1] === '/'){
|
||||||
|
$this -> endpoint = substr($this -> endpoint, 0, strlen($this -> endpoint)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strpos($this-> endpoint, 'http') !== 0){
|
||||||
|
$this -> endpoint = 'https://' . $this -> endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['signature'])){
|
||||||
|
$this -> signature = strval($config['signature']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['path_style'])){
|
||||||
|
$this -> pathStyle = $config['path_style'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['region'])){
|
||||||
|
$this -> region = strval($config['region']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['ssl_verify'])){
|
||||||
|
$this -> sslVerify = $config['ssl_verify'];
|
||||||
|
}else if(isset($config['ssl.certificate_authority'])){
|
||||||
|
$this -> sslVerify = $config['ssl.certificate_authority'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['max_retry_count'])){
|
||||||
|
$this -> maxRetryCount = intval($config['max_retry_count']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['timeout'])){
|
||||||
|
$this -> timeout = intval($config['timeout']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['socket_timeout'])){
|
||||||
|
$this -> socketTimeout = intval($config['socket_timeout']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['connect_timeout'])){
|
||||||
|
$this -> connectTimeout = intval($config['connect_timeout']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['chunk_size'])){
|
||||||
|
$this -> chunkSize = intval($config['chunk_size']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($config['exception_response_mode'])){
|
||||||
|
$this -> exceptionResponseMode = $config['exception_response_mode'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($config['is_cname'])) {
|
||||||
|
$this -> isCname = $config['is_cname'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = parse_url($this -> endpoint)['host'];
|
||||||
|
if(filter_var($host, FILTER_VALIDATE_IP) !== false) {
|
||||||
|
$this -> pathStyle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$handler = self::choose_handler($this);
|
||||||
|
|
||||||
|
$this -> httpClient = new Client(
|
||||||
|
[
|
||||||
|
'timeout' => 0,
|
||||||
|
'read_timeout' => $this -> socketTimeout,
|
||||||
|
'connect_timeout' => $this -> connectTimeout,
|
||||||
|
'allow_redirects' => false,
|
||||||
|
'verify' => $this -> sslVerify,
|
||||||
|
'expect' => false,
|
||||||
|
'handler' => HandlerStack::create($handler),
|
||||||
|
'curl' => [
|
||||||
|
CURLOPT_BUFFERSIZE => $this -> chunkSize
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct(){
|
||||||
|
$this-> close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refresh($key, $secret, $security_token=false){
|
||||||
|
$this -> ak = strval($key);
|
||||||
|
$this -> sk = strval($secret);
|
||||||
|
if($security_token){
|
||||||
|
$this -> securityToken = strval($security_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default User-Agent string to use with Guzzle
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function default_user_agent()
|
||||||
|
{
|
||||||
|
static $defaultAgent = '';
|
||||||
|
if (!$defaultAgent) {
|
||||||
|
$defaultAgent = 'obs-sdk-php/' . self::SDK_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $defaultAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method to create a new Obs client using an array of configuration options.
|
||||||
|
*
|
||||||
|
* @param array $config Client configuration data
|
||||||
|
*
|
||||||
|
* @return ObsClient
|
||||||
|
*/
|
||||||
|
public static function factory(array $config = [])
|
||||||
|
{
|
||||||
|
return new ObsClient($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close(){
|
||||||
|
if($this->factorys){
|
||||||
|
foreach ($this->factorys as $factory){
|
||||||
|
$factory->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initLog(array $logConfig= [])
|
||||||
|
{
|
||||||
|
ObsLog::initLog($logConfig);
|
||||||
|
|
||||||
|
$msg = [];
|
||||||
|
$msg[] = '[OBS SDK Version=' . self::SDK_VERSION;
|
||||||
|
$msg[] = 'Endpoint=' . $this->endpoint;
|
||||||
|
$msg[] = 'Access Mode=' . ($this->pathStyle ? 'Path' : 'Virtual Hosting').']';
|
||||||
|
|
||||||
|
ObsLog::commonLog(WARNING, implode("];[", $msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function choose_handler($obsclient)
|
||||||
|
{
|
||||||
|
$handler = null;
|
||||||
|
if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
|
||||||
|
$f1 = new SdkCurlFactory(50);
|
||||||
|
$f2 = new SdkCurlFactory(3);
|
||||||
|
$obsclient->factorys[] = $f1;
|
||||||
|
$obsclient->factorys[] = $f2;
|
||||||
|
$handler = Proxy::wrapSync(new CurlMultiHandler(['handle_factory' => $f1]), new CurlHandler(['handle_factory' => $f2]));
|
||||||
|
} elseif (function_exists('curl_exec')) {
|
||||||
|
$f = new SdkCurlFactory(3);
|
||||||
|
$obsclient->factorys[] = $f;
|
||||||
|
$handler = new CurlHandler(['handle_factory' => $f]);
|
||||||
|
} elseif (function_exists('curl_multi_exec')) {
|
||||||
|
$f = new SdkCurlFactory(50);
|
||||||
|
$obsclient->factorys[] = $f;
|
||||||
|
$handler = new CurlMultiHandler(['handle_factory' => $f]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ini_get('allow_url_fopen')) {
|
||||||
|
$handler = $handler
|
||||||
|
? Proxy::wrapStreaming($handler, new SdkStreamHandler())
|
||||||
|
: new SdkStreamHandler();
|
||||||
|
} elseif (!$handler) {
|
||||||
|
throw new \RuntimeException('GuzzleHttp requires cURL, the '
|
||||||
|
. 'allow_url_fopen ini setting, or a custom HTTP handler.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
140
addons/hwobs/library/Obs/ObsException.php
Normal file
140
addons/hwobs/library/Obs/ObsException.php
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2019 Huawei Technologies Co.,Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Obs;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use Obs\Log\ObsLog;
|
||||||
|
|
||||||
|
class ObsException extends \RuntimeException
|
||||||
|
{
|
||||||
|
const CLIENT = 'client';
|
||||||
|
|
||||||
|
const SERVER = 'server';
|
||||||
|
|
||||||
|
private $response;
|
||||||
|
|
||||||
|
private $request;
|
||||||
|
|
||||||
|
private $requestId;
|
||||||
|
|
||||||
|
private $exceptionType;
|
||||||
|
|
||||||
|
private $exceptionCode;
|
||||||
|
|
||||||
|
private $exceptionMessage;
|
||||||
|
|
||||||
|
private $hostId;
|
||||||
|
|
||||||
|
public function __construct ($message = null, $code = null, $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExceptionCode($exceptionCode)
|
||||||
|
{
|
||||||
|
$this->exceptionCode = $exceptionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExceptionCode()
|
||||||
|
{
|
||||||
|
return $this->exceptionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExceptionMessage($exceptionMessage)
|
||||||
|
{
|
||||||
|
$this->exceptionMessage = $exceptionMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExceptionMessage()
|
||||||
|
{
|
||||||
|
return $this->exceptionMessage ? $this->exceptionMessage : $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExceptionType($exceptionType)
|
||||||
|
{
|
||||||
|
$this->exceptionType = $exceptionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExceptionType()
|
||||||
|
{
|
||||||
|
return $this->exceptionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRequestId($requestId)
|
||||||
|
{
|
||||||
|
$this->requestId = $requestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestId()
|
||||||
|
{
|
||||||
|
return $this->requestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setResponse(Response $response)
|
||||||
|
{
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResponse()
|
||||||
|
{
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRequest(Request $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequest()
|
||||||
|
{
|
||||||
|
return $this->request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusCode()
|
||||||
|
{
|
||||||
|
return $this->response ? $this->response->getStatusCode() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHostId($hostId){
|
||||||
|
$this->hostId = $hostId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHostId(){
|
||||||
|
return $this->hostId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$message = get_class($this) . ': '
|
||||||
|
. 'OBS Error Code: ' . $this->getExceptionCode() . ', '
|
||||||
|
. 'Status Code: ' . $this->getStatusCode() . ', '
|
||||||
|
. 'OBS Error Type: ' . $this->getExceptionType() . ', '
|
||||||
|
. 'OBS Error Message: ' . ($this->getExceptionMessage() ? $this->getExceptionMessage():$this->getMessage());
|
||||||
|
|
||||||
|
// Add the User-Agent if available
|
||||||
|
if ($this->request) {
|
||||||
|
$message .= ', ' . 'User-Agent: ' . $this->request->getHeaderLine('User-Agent');
|
||||||
|
}
|
||||||
|
$message .= "\n";
|
||||||
|
|
||||||
|
ObsLog::commonLog(INFO, "http request:status:%d, %s",$this->getStatusCode(),"code:".$this->getExceptionCode().", message:".$this->getMessage());
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
68
addons/hwobs/library/Signer.php
Normal file
68
addons/hwobs/library/Signer.php
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\hwobs\library;
|
||||||
|
|
||||||
|
use Obs\Internal\Common\Model;
|
||||||
|
use Obs\Internal\Signature\DefaultSignature;
|
||||||
|
|
||||||
|
class Signer
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分片文件的签名
|
||||||
|
* @param string $url
|
||||||
|
* @param string $uploadId
|
||||||
|
* @param int $fileSize
|
||||||
|
* @param int $partSize
|
||||||
|
* @param string $date
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getPartsAuthorization($url, $uploadId, $fileSize, $partSize, $date)
|
||||||
|
{
|
||||||
|
$config = get_addon_config('hwobs');
|
||||||
|
$i = 0;
|
||||||
|
$size_count = $fileSize;
|
||||||
|
$values = array();
|
||||||
|
while ($size_count > 0) {
|
||||||
|
$size_count -= $partSize;
|
||||||
|
$values[] = array(
|
||||||
|
$partSize * $i,
|
||||||
|
($size_count > 0) ? $partSize : ($size_count + $partSize),
|
||||||
|
);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$httpMethod = "PUT";
|
||||||
|
$headers = [
|
||||||
|
"Host" => str_replace(['http://', 'https://'], '', $config['uploadurl']),
|
||||||
|
"Content-Length" => 0,
|
||||||
|
"x-amz-date" => $date,
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($values as $index => $value) {
|
||||||
|
$headers['Content-Length'] = $value[1];
|
||||||
|
$params = ['partNumber' => $index + 1, 'uploadId' => $uploadId, 'uriParam' => $url, 'dnsParam' => $config['bucket'], 'x-amz-date' => $date];
|
||||||
|
$model = new Model($params);
|
||||||
|
$sign = new DefaultSignature($config['accessKey'], $config['secretKey'], false, $config['uploadurl'], $httpMethod, 'v2', false, false);
|
||||||
|
$requestConfig = [
|
||||||
|
'httpMethod' => $httpMethod,
|
||||||
|
'requestParameters' => [
|
||||||
|
'x-amz-date' => ['location' => 'header'],
|
||||||
|
'partNumber' => ['location' => 'query'],
|
||||||
|
'uploadId' => ['location' => 'query'],
|
||||||
|
'uriParam' => ['location' => 'uri'],
|
||||||
|
'dnsParam' => ['location' => 'dns'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$sig = $sign->doAuth($requestConfig, $params, $model);
|
||||||
|
$result[] = $sig['headers']['Authorization'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ class Command extends BaseCommand
|
|||||||
/**
|
/**
|
||||||
* 执行帮助命令
|
* 执行帮助命令
|
||||||
*/
|
*/
|
||||||
protected function execute(Input $input, Output $output)
|
protected function execute(?Input $input, ?Output $output)
|
||||||
{
|
{
|
||||||
$this->input = $input;
|
$this->input = $input;
|
||||||
$this->output = $output;
|
$this->output = $output;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class ShoproChat extends Command
|
|||||||
/**
|
/**
|
||||||
* 执行帮助命令
|
* 执行帮助命令
|
||||||
*/
|
*/
|
||||||
protected function execute(Input $input, Output $output)
|
protected function execute(?Input $input, ?Output $output)
|
||||||
{
|
{
|
||||||
$this->input = $input;
|
$this->input = $input;
|
||||||
$this->output = $output;
|
$this->output = $output;
|
||||||
|
|||||||
@@ -2,14 +2,24 @@
|
|||||||
|
|
||||||
namespace addons\shopro\controller\user;
|
namespace addons\shopro\controller\user;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
use app\common\library\Sms;
|
use app\common\library\Sms;
|
||||||
|
use app\admin\model\zy\Club;
|
||||||
|
use app\admin\model\zy\Menber;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use app\admin\model\zy\link\Apply;
|
||||||
use addons\shopro\controller\Common;
|
use addons\shopro\controller\Common;
|
||||||
|
use app\admin\model\zy\link\Message;
|
||||||
|
use app\admin\model\zy\link\Visitor;
|
||||||
|
use app\admin\model\zy\link\Relation;
|
||||||
|
use app\admin\model\shopro\ThirdOauth;
|
||||||
|
use think\exception\ValidateException;
|
||||||
use addons\shopro\service\user\UserAuth;
|
use addons\shopro\service\user\UserAuth;
|
||||||
use app\admin\model\shopro\user\User as UserModel;
|
use app\admin\model\shopro\user\User as UserModel;
|
||||||
use app\admin\model\shopro\user\Coupon as UserCouponModel;
|
|
||||||
use app\admin\model\shopro\order\Order as OrderModel;
|
use app\admin\model\shopro\order\Order as OrderModel;
|
||||||
|
use app\admin\model\shopro\user\Coupon as UserCouponModel;
|
||||||
use app\admin\model\shopro\order\Aftersale as AftersaleModel;
|
use app\admin\model\shopro\order\Aftersale as AftersaleModel;
|
||||||
use app\admin\model\shopro\ThirdOauth;
|
|
||||||
|
|
||||||
class User extends Common
|
class User extends Common
|
||||||
{
|
{
|
||||||
@@ -76,11 +86,41 @@ class User extends Common
|
|||||||
public function profile()
|
public function profile()
|
||||||
{
|
{
|
||||||
//TODO @ldh: 1.账号被禁用 2.连表查group
|
//TODO @ldh: 1.账号被禁用 2.连表查group
|
||||||
$user = auth_user(true);
|
$self = auth_user(true); //自己
|
||||||
|
$user_id = $self->id;
|
||||||
$user = UserModel::with(['parent_user', 'third_oauth'])->where('id', $user->id)->find();
|
$params = $this->request->param();
|
||||||
|
if (!empty($params['user_id'])) {
|
||||||
|
$user_id = $params['user_id'];
|
||||||
|
$relation = Relation::where('user_id', $self->id)->where('target_id', $user_id)->find();
|
||||||
|
$content = json_decode($relation->content ?? '', true);
|
||||||
|
$visitor = Visitor::where('type', 2)->where('obj_id', $user_id)->where('user_id', $self->id)->find();
|
||||||
|
if (empty($visitor)) {
|
||||||
|
$visitor = new Visitor;
|
||||||
|
}
|
||||||
|
$visitor->allowField(true)->save([
|
||||||
|
'type' => 2,
|
||||||
|
'obj_id' => $user_id,
|
||||||
|
'user_id' => $self->id,
|
||||||
|
'nickname' => $self['nickname'],
|
||||||
|
'avatar' => $self['avatar'],
|
||||||
|
'gender' => $self['gender'],
|
||||||
|
'times' => empty($visitor->times) ? 1 : $visitor->times + 1
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$user = UserModel::with(['parent_user', 'third_oauth'])->where('id', $user_id)->find();
|
||||||
|
|
||||||
$user->hidden(['password', 'salt', 'createtime', 'updatetime', 'deletetime', 'remember_token', 'login_fail', 'login_ip', 'login_time']);
|
$user->hidden(['password', 'salt', 'createtime', 'updatetime', 'deletetime', 'remember_token', 'login_fail', 'login_ip', 'login_time']);
|
||||||
|
$user = $user->toArray();
|
||||||
|
$user['msg_num'] = Message::where('user_id', $user['id'])->where('status', 0)->count();
|
||||||
|
$user['club_list'] = Menber::alias('m')->join([Club::$tableName => 'c'], 'c.id=m.club_id')->field('m.*,c.name')->where('user_id', $user['id'])->where('role', '>', 0)->select();
|
||||||
|
$user['club_num'] = count($user['club_list']);
|
||||||
|
$user['card_num'] = 0;
|
||||||
|
if (isset($content)) {
|
||||||
|
if (empty($content['phone'])) $user['mobile'] = ''; //手机查看权限
|
||||||
|
if (empty($content['wechat'])) $user['wechat'] = ''; //微信查看权限
|
||||||
|
if (empty($content['qq'])) $user['qq'] = ''; //qq查看权限
|
||||||
|
$user['content'] = $content; //权限
|
||||||
|
}
|
||||||
|
|
||||||
$this->success('个人详情', $user);
|
$this->success('个人详情', $user);
|
||||||
}
|
}
|
||||||
@@ -92,7 +132,7 @@ class User extends Common
|
|||||||
{
|
{
|
||||||
$user = auth_user();
|
$user = auth_user();
|
||||||
|
|
||||||
$params = $this->request->only(['avatar', 'nickname', 'gender']);
|
$params = $this->request->only(['avatar', 'nickname', 'mobile', 'gender', 'qq', 'wechat', 'years', 'bio']);
|
||||||
$this->svalidate($params);
|
$this->svalidate($params);
|
||||||
|
|
||||||
$user->save($params);
|
$user->save($params);
|
||||||
@@ -147,7 +187,7 @@ class User extends Common
|
|||||||
}
|
}
|
||||||
//如果已经有账号则直接登录
|
//如果已经有账号则直接登录
|
||||||
$ret = $this->auth->direct($user->id);
|
$ret = $this->auth->direct($user->id);
|
||||||
}else {
|
} else {
|
||||||
$this->error('该手机号暂未注册');
|
$this->error('该手机号暂未注册');
|
||||||
}
|
}
|
||||||
if (isset($ret) && $ret) {
|
if (isset($ret) && $ret) {
|
||||||
@@ -302,4 +342,279 @@ class User extends Common
|
|||||||
|
|
||||||
$this->success('注销成功');
|
$this->success('注销成功');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发送用户消息
|
||||||
|
public function sendMsg()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
if ($params['user_id'] == $this->auth->id) {
|
||||||
|
$this->error('不能发送给自己');
|
||||||
|
}
|
||||||
|
$user = auth_user();
|
||||||
|
$target = UserModel::get($params['user_id']);
|
||||||
|
if (empty($target)) {
|
||||||
|
$this->error('用户不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$result = (new Message())->allowField(true)->save([
|
||||||
|
'type' => 2,
|
||||||
|
'name' => $user['nickname'],
|
||||||
|
'avatar' => $user['avatar'],
|
||||||
|
'from_id' => $user['id'],
|
||||||
|
'user_id' => $params['user_id'],
|
||||||
|
'content' => json_encode([
|
||||||
|
'topic' => '好友消息',
|
||||||
|
'time' => date('Y-m-d H:i:s'),
|
||||||
|
'content' => $params['content']
|
||||||
|
]),
|
||||||
|
'status' => 0
|
||||||
|
]);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分类消息
|
||||||
|
public function msgGroup()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$member = Menber::where('user_id', $this->auth->id)->where('role', '>', 1)->column('club_id');
|
||||||
|
if (!empty($member)) {
|
||||||
|
$query1 = Message::field('*,count(*) as num')->where(function ($q1) use ($member) {
|
||||||
|
$q1->where('user_id', $this->auth->id)
|
||||||
|
->whereOr(function ($q2) use ($member) {
|
||||||
|
$q2->where('type', 3)->whereIn('from_id', $member);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$query2 = Message::field('*,count(*) as num')->where(function ($q1) use ($member) {
|
||||||
|
$q1->where('user_id', $this->auth->id)
|
||||||
|
->whereOr(function ($q2) use ($member) {
|
||||||
|
$q2->where('type', 3)->whereIn('from_id', $member);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$query1 = Message::field('*,count(*) as num')->where('user_id', $this->auth->id);
|
||||||
|
$query2 = Message::field('*,count(*) as num')->where('user_id', $this->auth->id);
|
||||||
|
}
|
||||||
|
if (isset($params['type'])) {
|
||||||
|
$query1->where('type', $params['type']);
|
||||||
|
$query2->where('type', $params['type']);
|
||||||
|
}
|
||||||
|
$query1->group('from_id')->order('update_time', 'desc');
|
||||||
|
$query2->group('from_id')->order('update_time', 'desc');
|
||||||
|
$num = $query2->where('status', 0)->column('count(*) as num', 'from_id');
|
||||||
|
$res = $query1->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$r) {
|
||||||
|
$r['content'] = json_decode($r['content'], true);
|
||||||
|
$r['num'] = $num[$r['from_id']] ?? 0;
|
||||||
|
}
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户消息
|
||||||
|
public function msg()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$member = Menber::where('user_id', $this->auth->id)->where('role', '>', 1)->column('club_id');
|
||||||
|
if (!empty($member)) {
|
||||||
|
$query = Message::where(function ($q1) use ($member) {
|
||||||
|
$q1->where('user_id', $this->auth->id) //用户消息
|
||||||
|
->whereOr(function ($q2) use ($member) { //俱乐部消息
|
||||||
|
$q2->where('type', 3)->whereIn('from_id', $member);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$query = Message::where('user_id', $this->auth->id);
|
||||||
|
}
|
||||||
|
if (isset($params['type'])) {
|
||||||
|
$query->where('type', $params['type']);
|
||||||
|
}
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$r) {
|
||||||
|
$r['content'] = json_decode($r['content'], true);
|
||||||
|
}
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消息读取
|
||||||
|
public function msgRead()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$model = Message::get($params['msg_id'] ?? NULL);
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
$model->save(['status' => 1]); //已读
|
||||||
|
$model['content'] = json_decode($model['content'], true);
|
||||||
|
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请联系信息
|
||||||
|
public function apply()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
if (empty($params['content'])) {
|
||||||
|
return $this->error('申请内容不能为空');
|
||||||
|
}
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$fromUser = auth_user();
|
||||||
|
$user = UserModel::get($params['user_id'] ?? NULL);
|
||||||
|
if (empty($user)) {
|
||||||
|
return $this->error('用户不存在');
|
||||||
|
}
|
||||||
|
$apply = (new Apply);
|
||||||
|
if ($apply::get(['type' => 2, 'user_id' => $fromUser->id, 'target_id' => $user->id, 'status' => 1])) {
|
||||||
|
return $this->error('申请处理中');
|
||||||
|
}
|
||||||
|
$apply->allowField(true)->save([ // 记录申请
|
||||||
|
'type' => 2,
|
||||||
|
'user_id' => $fromUser->id,
|
||||||
|
'target_id' => $user->id,
|
||||||
|
'content' => $params['content'],
|
||||||
|
'reason' => $params['reason'] ?? '',
|
||||||
|
'status' => 1
|
||||||
|
]);
|
||||||
|
// (new Message())->allowField(true)->save([ // 消息通知
|
||||||
|
// 'type' => 2,
|
||||||
|
// 'name' => $fromUser->nickname,
|
||||||
|
// 'avatar' => $fromUser->avatar,
|
||||||
|
// 'from_id' => $fromUser->id,
|
||||||
|
// 'user_id' => $user->id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '申请联系信息',
|
||||||
|
// '申请人' => $fromUser->nickname,
|
||||||
|
// '申请时间' => date('Y-m-d H:i:s'),
|
||||||
|
// 'reason' => $params['reason'] ?? '',
|
||||||
|
// 'apply_id' => $apply->id
|
||||||
|
// ])
|
||||||
|
// ]);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('已邀请,请等候审核');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取申请列表
|
||||||
|
public function applyList()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = Apply::where('type', 2)->where('target_id', $this->auth->id);
|
||||||
|
if (isset($params['status'])) {
|
||||||
|
$query->where('status', $params['status']);
|
||||||
|
}
|
||||||
|
$applyList = $query->select();
|
||||||
|
$this->success('Success', $applyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理申请
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$apply = Apply::get(['id' => $params['apply_id'], 'user_id' => $this->auth->id, 'status' => 1]);
|
||||||
|
if (empty($apply)) {
|
||||||
|
return $this->error('申请记录不存在');
|
||||||
|
}
|
||||||
|
if ($params['status'] == 2) { //同意
|
||||||
|
$relation = Relation::get(['user_id' => $apply['user_id'], 'target_id' => $apply['user_id']]);
|
||||||
|
if (empty($relation)) {
|
||||||
|
$relation = new Relation;
|
||||||
|
}
|
||||||
|
$relation->allowField(true)->save([
|
||||||
|
'target_id' => $apply['user_id'],
|
||||||
|
'user_id' => $apply['target_id'],
|
||||||
|
'status' => 1,
|
||||||
|
'content' => $params['content'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$apply->save([
|
||||||
|
'status' => $params['status'],
|
||||||
|
'reply' => $params['reply'] ?? ''
|
||||||
|
]);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 好友关系
|
||||||
|
public function relation()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
if (!isset($params['status'])) {
|
||||||
|
$this->error('缺少参数:status');
|
||||||
|
}
|
||||||
|
$ids = explode(',', $params['user_id']);
|
||||||
|
if (empty($ids)) {
|
||||||
|
$this->error('缺少参数:user_id');
|
||||||
|
}
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if ($id == $this->auth->id) {
|
||||||
|
$this->error('不能设置与自己的好友关系');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$dbUserId = UserModel::where('id', 'IN', $ids)->column('id');
|
||||||
|
if (!empty($diffId = array_diff($ids, $dbUserId))) {
|
||||||
|
return $this->error('用户不存在:' . implode(',', $diffId));
|
||||||
|
}
|
||||||
|
$res = Relation::where('user_id', $this->auth->id)->where('target_id', 'IN', $ids)->update(['status' => $params['status']]);
|
||||||
|
if ($res < count($ids)) {
|
||||||
|
$target = Relation::where('user_id', $this->auth->id)->where('target_id', 'IN', $ids)->column("target_id");
|
||||||
|
$_relationModel = new Relation;
|
||||||
|
foreach ((array_diff($ids, $target)) as $id) {
|
||||||
|
(clone $_relationModel)->allowField(true)->save([
|
||||||
|
'user_id' => $this->auth->id,
|
||||||
|
'target_id' => $id,
|
||||||
|
'status' => $params['status'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取关系列表
|
||||||
|
public function list()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = Relation::alias('r')
|
||||||
|
->join([UserModel::$tableName => 'u'], 'u.id=r.target_id')
|
||||||
|
->field('r.*,u.avatar,u.gender,u.nickname')
|
||||||
|
->where('user_id', $this->auth->id);
|
||||||
|
if (isset($params['status'])) {
|
||||||
|
$query->where('r.status', $params['status']);
|
||||||
|
} else {
|
||||||
|
$query->where('r.status', '<>', 0);
|
||||||
|
}
|
||||||
|
$list = $query->select();
|
||||||
|
foreach ($list as &$l) {
|
||||||
|
$l['content'] = json_decode($l['content'] ?? '[]', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', $list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
282
addons/shopro/controller/zy/Activity.php
Normal file
282
addons/shopro/controller/zy/Activity.php
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\controller\zy;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
|
use app\admin\model\zy\Club;
|
||||||
|
use app\admin\model\zy\Stadium;
|
||||||
|
use app\admin\model\zy\game\Game;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use app\admin\model\shopro\Category;
|
||||||
|
use app\admin\model\zy\link\Message;
|
||||||
|
use app\admin\model\zy\game\GameJoin;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use app\admin\model\shopro\goods\Goods;
|
||||||
|
use app\admin\model\zy\game\Participant;
|
||||||
|
use addons\shopro\service\order\OrderCreate;
|
||||||
|
use app\admin\controller\shopro\traits\SkuPrice;
|
||||||
|
|
||||||
|
class Activity extends Base
|
||||||
|
{
|
||||||
|
use SkuPrice;
|
||||||
|
|
||||||
|
protected $noNeedLogin = ['index', 'view', 'test'];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->model = new \app\admin\model\zy\game\Activity;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = $this->model->alias('a')
|
||||||
|
->join([Stadium::$tableName => 's'], 's.id = a.gym_id', 'LEFT')
|
||||||
|
->join([Club::$tableName => 'c'], 'c.id = a.club_id', 'LEFT')
|
||||||
|
->field('a.*, s.name as gym_name, c.name as club_name');
|
||||||
|
if (isset($params['name'])) {
|
||||||
|
$query->where('a.name', 'like', '%' . $params['name'] . '%');
|
||||||
|
}
|
||||||
|
if (isset($params['gym_id'])) {
|
||||||
|
$query->where('a.gym_id', $params['gym_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['club_id'])) {
|
||||||
|
$query->where('a.club_id', $params['club_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['week'])) {
|
||||||
|
$query->where('week', $params['week']);
|
||||||
|
}
|
||||||
|
if (isset($params['pid'])) {
|
||||||
|
$query->where('pid', $params['pid']);
|
||||||
|
} else {
|
||||||
|
$query->where('pid', 0);
|
||||||
|
}
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$v) {
|
||||||
|
$v['public_time'] = json_decode($v['public_time'] ?? '[]', true);
|
||||||
|
$v['join_start_time'] = json_decode($v['join_start_time'] ?? '[]', true);
|
||||||
|
$v['join_end_time'] = json_decode($v['join_end_time'] ?? '[]', true);
|
||||||
|
$v['quit_time'] = json_decode($v['quit_time'] ?? '[]', true);
|
||||||
|
$v['cost'] = json_decode($v['cost'] ?? '[]', true);
|
||||||
|
$v['referee'] = explode(',', $v['referee'] ?? '');
|
||||||
|
}
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view()
|
||||||
|
{
|
||||||
|
$model = $this->model->get($this->request->param('id'));
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$model['public_time'] = json_decode($model['public_time'] ?? '[]', true);
|
||||||
|
$model['join_start_time'] = json_decode($model['join_start_time'] ?? '[]', true);
|
||||||
|
$model['join_end_time'] = json_decode($model['join_end_time'] ?? '[]', true);
|
||||||
|
$model['quit_time'] = json_decode($model['quit_time'] ?? '[]', true);
|
||||||
|
$model['cost'] = json_decode($model['cost'] ?? '[]', true);
|
||||||
|
$model['referee'] = explode(',', $model['referee'] ?? '');
|
||||||
|
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$category = new Category;
|
||||||
|
$category->name = $params['name'];
|
||||||
|
$category->parent_id = 157;
|
||||||
|
$category->description = '报名费商品';
|
||||||
|
$category->save();
|
||||||
|
foreach ($this->model->costKey as $key => $val) {
|
||||||
|
if (isset($params['cost'][$key])) {
|
||||||
|
$goods = new Goods;
|
||||||
|
$goods->category_ids = $category->id;
|
||||||
|
$goods->subtitle = $key;
|
||||||
|
$goods->title = $val;
|
||||||
|
$goods->type = 'virtual';
|
||||||
|
$goods->limit_type = 'none';
|
||||||
|
$goods->dispatch_type = 'autosend';
|
||||||
|
$goods->dispatch_id = 2;
|
||||||
|
$goods->is_sku = 0;
|
||||||
|
$goods->original_price = $params['cost'][$key];
|
||||||
|
$goods->price = $params['cost'][$key];
|
||||||
|
$goods->save();
|
||||||
|
$this->zySku($goods, 'add');
|
||||||
|
} else {
|
||||||
|
throw new Exception('请填写' . $val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$params['public_time'] = json_encode($params['public_time'] ?? []);
|
||||||
|
$params['join_start_time'] = json_encode($params['join_start_time'] ?? []);
|
||||||
|
$params['join_end_time'] = json_encode($params['join_end_time'] ?? []);
|
||||||
|
$params['quit_time'] = json_encode($params['quit_time'] ?? []);
|
||||||
|
$params['cost']['goods_category_id'] = $category->id;
|
||||||
|
$params['cost'] = json_encode($params['cost'] ?? []);
|
||||||
|
$result = $this->model->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if (empty($result)) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$model = $this->model->get($params['id']);
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
$cost = json_decode($model->cost, true);
|
||||||
|
$goods = Goods::where('category_ids', $cost['goods_category_id'])->select();
|
||||||
|
foreach ($this->model->costKey as $key => $val) {
|
||||||
|
if (isset($params['cost'][$key])) {
|
||||||
|
foreach ($goods as $g) { //更新报名费价格
|
||||||
|
if ($g['title'] == $val && $g['price'] != $params['cost'][$key]) {
|
||||||
|
$g->save(['price' => $params['cost'][$key]]);
|
||||||
|
$this->zySku($g, 'edit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception('请填写' . $val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$params['public_time'] = json_encode($params['public_time'] ?? []);
|
||||||
|
$params['join_start_time'] = json_encode($params['join_start_time'] ?? []);
|
||||||
|
$params['join_end_time'] = json_encode($params['join_end_time'] ?? []);
|
||||||
|
$params['quit_time'] = json_encode($params['quit_time'] ?? []);
|
||||||
|
$params['cost']['goods_category_id'] = $cost['goods_category_id'];
|
||||||
|
$params['cost'] = json_encode($params['cost'] ?? []);
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$result = $model->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if (empty($result)) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动周期性活动创建比赛任务
|
||||||
|
public function test()
|
||||||
|
{
|
||||||
|
if (!\think\Cache::has('addons\shopro\job\Test@zy')) {
|
||||||
|
$res = \think\Queue::push('\addons\shopro\job\Test@zy', time(), 'shopro');
|
||||||
|
\think\Cache::set('addons\shopro\job\Test@zy', $res);
|
||||||
|
$this->success('Success', $res);
|
||||||
|
}
|
||||||
|
$this->success('Success', '任务已存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比赛报名
|
||||||
|
public function gameJoin()
|
||||||
|
{
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$params = $this->request->param();
|
||||||
|
if (isset($params['game_id'])) {
|
||||||
|
$game = Game::get($params['game_id']);
|
||||||
|
} else {
|
||||||
|
$game = Game::where('id', $params['act_id'] ?? NULL)
|
||||||
|
->where('week', $params['week'] ?? NULL)
|
||||||
|
->where('date', $params['date'] ?? NULL)
|
||||||
|
->find();
|
||||||
|
}
|
||||||
|
if (empty($game)) {
|
||||||
|
$this->error('活动不存在');
|
||||||
|
}
|
||||||
|
$currentTime = date('Y-m-d H:i:s');
|
||||||
|
if ($game['join_start_time'] > $currentTime || $game['join_end_time'] < $currentTime) {
|
||||||
|
$this->error('不在报名时间');
|
||||||
|
}
|
||||||
|
if (empty($params['users']) || !is_array($params['users']) || empty($params['goods_list']) || !is_array($params['goods_list'])) {
|
||||||
|
$this->error('请核对报名人员');
|
||||||
|
}
|
||||||
|
$join = GameJoin::where(['game_id' => $game['id'], 'user_id' => $this->auth->id])->where('status', '>', -1)->find();
|
||||||
|
if (!empty($join)) {
|
||||||
|
$this->error('您已报名此活动');
|
||||||
|
}
|
||||||
|
$order = GameJoin::where('game_id', $game['id'])->count();
|
||||||
|
$gender = ['man' => 0, 'woman' => 0];
|
||||||
|
$user = auth_user();
|
||||||
|
$participant = $msgs = [];
|
||||||
|
foreach ($params['users'] as $u) {
|
||||||
|
$order++;
|
||||||
|
$participant[] = [
|
||||||
|
'user_id' => $u['user_id'],
|
||||||
|
'gender' => $u['gender'],
|
||||||
|
'avatar' => $u['avatar'],
|
||||||
|
'name' => $u['nickname'],
|
||||||
|
'game_id' => $game['id'],
|
||||||
|
'signin' => 0,
|
||||||
|
'order' => $order,
|
||||||
|
'status' => ($order > $game['limit_num']) ? 0 : 1, //超过报名人数为候补
|
||||||
|
];
|
||||||
|
if ($u['gender'] == 0) {
|
||||||
|
$gender['woman'] += 1;
|
||||||
|
} else {
|
||||||
|
$gender['man'] += 1;
|
||||||
|
}
|
||||||
|
$msgs[] = [
|
||||||
|
'type' => 1,
|
||||||
|
'name' => '系统消息',
|
||||||
|
'avatar' => '',
|
||||||
|
'from_id' => 0,
|
||||||
|
'user_id' => $u['user_id'],
|
||||||
|
'content' => json_encode([
|
||||||
|
'msgType' => 1,
|
||||||
|
'content' => $user['nickname'] . ' 帮你报名了组球',
|
||||||
|
'game_id' => $game['id'],
|
||||||
|
'act_id' => $game['act_id'],
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
foreach ($gender as $k => $v) {
|
||||||
|
if ($params['goods_list'][$k]['goods_num'] != $v) {
|
||||||
|
$this->error('报名人数错误');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cost = json_decode($game['cost'], true);
|
||||||
|
if (!empty($cost['type']) && ($cost['type'] == 10 || $cost['type'] == 30)) {
|
||||||
|
$orderCreate = new OrderCreate($params);
|
||||||
|
$result = $orderCreate->calc('create');
|
||||||
|
$order = $orderCreate->create($result);
|
||||||
|
$join = new GameJoin;
|
||||||
|
$join->allowField(true)->save([
|
||||||
|
'act_id' => $game['act_id'],
|
||||||
|
'game_id' => $game['id'],
|
||||||
|
'user_id' => $this->auth->id,
|
||||||
|
'order_id' => $order['id'],
|
||||||
|
'status' => 0, //待支付
|
||||||
|
'users' => json_encode($params['users'] ?? [])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
foreach ($participant as &$p) {
|
||||||
|
$p['game_join_id'] = $join->id;
|
||||||
|
}
|
||||||
|
(new Participant)->insertAll($participant);
|
||||||
|
(new Message())->insertAll($msgs);
|
||||||
|
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', $order ?? []);
|
||||||
|
}
|
||||||
|
}
|
||||||
73
addons/shopro/controller/zy/Base.php
Normal file
73
addons/shopro/controller/zy/Base.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\controller\zy;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use addons\shopro\controller\Common;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
|
class Base extends Common
|
||||||
|
{
|
||||||
|
protected $noNeedLogin = ['index'];
|
||||||
|
protected $noNeedRight = ['*'];
|
||||||
|
|
||||||
|
protected $model;
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->user = auth_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$result = false;
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$result = $this->model->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
$result = false;
|
||||||
|
$params = $this->request->param();
|
||||||
|
$model = $this->model->get($params['id']);
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$result = $model->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view()
|
||||||
|
{
|
||||||
|
$model = $this->model->get($this->request->param('id'));
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
}
|
||||||
289
addons/shopro/controller/zy/Circle.php
Normal file
289
addons/shopro/controller/zy/Circle.php
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\controller\zy;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
|
use app\admin\model\zy\Club;
|
||||||
|
use app\admin\model\zy\Menber;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use app\admin\model\zy\circle\Likes;
|
||||||
|
use app\admin\model\zy\link\Message;
|
||||||
|
use app\admin\model\zy\link\Relation;
|
||||||
|
use app\admin\model\zy\circle\Comment;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use app\admin\model\zy\circle\Circle as CircleModel;
|
||||||
|
|
||||||
|
class Circle extends Base
|
||||||
|
{
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
|
||||||
|
$sub = CircleModel::alias('c')
|
||||||
|
->join([Likes::$tableName => 'l'], 'c.id = l.circle_id', 'LEFT')
|
||||||
|
->field("c.*,JSON_ARRAYAGG(JSON_OBJECT(
|
||||||
|
'id', l.id,
|
||||||
|
'user_id', l.user_id,
|
||||||
|
'nickname', l.nickname,
|
||||||
|
'avatar', l.avatar,
|
||||||
|
'gender', l.gender
|
||||||
|
)) AS likes")->group('c.id')->buildSql();
|
||||||
|
$query = Comment::alias('m')
|
||||||
|
->join([$sub => 'c'], 'c.id = m.circle_id', 'RIGHT')
|
||||||
|
->field("c.*,
|
||||||
|
JSON_ARRAYAGG(JSON_OBJECT(
|
||||||
|
'id', m.id,
|
||||||
|
'pid', m.pid,
|
||||||
|
'puser_id', m.puser_id,
|
||||||
|
'pnickname', m.pnickname,
|
||||||
|
'user_id', m.user_id,
|
||||||
|
'nickname', m.nickname,
|
||||||
|
'avatar', m.avatar,
|
||||||
|
'gender', m.gender,
|
||||||
|
'content', m.content,
|
||||||
|
'create_time', m.create_time
|
||||||
|
)) AS comment")->group('c.id');
|
||||||
|
if (isset($params['status'])) {
|
||||||
|
$query->where('c.status', $params['status']);
|
||||||
|
} else {
|
||||||
|
$query->where('c.status', 1);
|
||||||
|
}
|
||||||
|
if (isset($params['club_id'])) {
|
||||||
|
$query->where('c.club_id', $params['club_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['user_id'])) {
|
||||||
|
$query->where('c.user_id', $params['user_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['friend'])) {
|
||||||
|
$friend = Relation::where('user_id', $this->auth->id)->where('status', 'IN', explode(',', $params['friend']))->column('target_id');
|
||||||
|
$query->where('c.user_id', 'IN', $friend);
|
||||||
|
}
|
||||||
|
$query->order(['c.top' => 'desc', 'c.create_time' => 'desc']);
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$r) {
|
||||||
|
$r['likes'] = json_decode($r['likes'], true);
|
||||||
|
if (count($r['likes']) == 1 && empty($r['likes'][0]['id'])) $r['likes'] = [];
|
||||||
|
$r['comment'] = buildTree(json_decode($r['comment'], true));
|
||||||
|
}
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view()
|
||||||
|
{
|
||||||
|
$model = CircleModel::get($this->request->param('id'));
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
if (empty($params['content'])) {
|
||||||
|
$this->error('内容不能为空');
|
||||||
|
}
|
||||||
|
$user = auth_user();
|
||||||
|
$club = Menber::alias('m')->join([Club::$tableName => 'c'], 'm.club_id=c.id')
|
||||||
|
->field('c.name')
|
||||||
|
->where(['m.club_id' => $params['club_id'], 'm.user_id' => $user['id']])
|
||||||
|
->where('m.role', '>', 0)->find();
|
||||||
|
if (empty($club)) {
|
||||||
|
$this->error('你无权在此俱乐部发布');
|
||||||
|
}
|
||||||
|
$params['user_id'] = $user['id'];
|
||||||
|
$params['nickname'] = $user['nickname'];
|
||||||
|
$params['avatar'] = $user['avatar'];
|
||||||
|
$params['gender'] = $user['gender'];
|
||||||
|
$params['club_name'] = $club['name'];
|
||||||
|
$params['status'] = 0;
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$result = (new CircleModel)->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点赞
|
||||||
|
public function like()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$circle = CircleModel::get($params['circle_id']);
|
||||||
|
if (empty($circle)) {
|
||||||
|
$this->error('数据不存在');
|
||||||
|
}
|
||||||
|
$user = auth_user();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$like = Likes::get(['circle_id' => $params['circle_id'], 'user_id' => $user['id']]);
|
||||||
|
if (empty($like)) { // 点赞
|
||||||
|
(new Likes)->allowField(true)->save([
|
||||||
|
'circle_id' => $params['circle_id'],
|
||||||
|
'user_id' => $user['id'],
|
||||||
|
'nickname' => $user['nickname'],
|
||||||
|
'avatar' => $user['avatar'],
|
||||||
|
'gender' => $user['gender'],
|
||||||
|
]);
|
||||||
|
// (new Message())->allowField(true)->save([ // 消息通知
|
||||||
|
// 'type' => 1,
|
||||||
|
// 'name' => '互动消息',
|
||||||
|
// 'avatar' => '',
|
||||||
|
// 'from_id' => 0,
|
||||||
|
// 'user_id' => $circle->user_id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '点赞',
|
||||||
|
// 'content' => $user['nickname'] . '点赞了你的帖子',
|
||||||
|
// 'circle_id' => $circle->id
|
||||||
|
// ])
|
||||||
|
// ]);
|
||||||
|
} else { // 取消点赞
|
||||||
|
$like->delete();
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评论
|
||||||
|
public function comment()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$circle = CircleModel::get($params['circle_id']);
|
||||||
|
if (empty($circle)) {
|
||||||
|
$this->error('数据不存在');
|
||||||
|
}
|
||||||
|
if (empty($params['content'])) {
|
||||||
|
$this->error('内容不能为空');
|
||||||
|
}
|
||||||
|
$pcomment = Comment::get($params['pid']);
|
||||||
|
if (!empty($params['pid']) && empty($pcomment)) {
|
||||||
|
$this->error('回复的评论不存在');
|
||||||
|
}
|
||||||
|
$user = auth_user();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
(new Comment)->allowField(true)->save([
|
||||||
|
'circle_id' => $params['circle_id'],
|
||||||
|
'pid' => $params['pid'],
|
||||||
|
'puser_id' => $pcomment['user_id'] ?? 0,
|
||||||
|
'pnickname' => $pcomment['nickname'] ?? '',
|
||||||
|
'user_id' => $user['id'],
|
||||||
|
'nickname' => $user['nickname'],
|
||||||
|
'avatar' => $user['avatar'],
|
||||||
|
'gender' => $user['gender'],
|
||||||
|
'content' => $params['content'],
|
||||||
|
]);
|
||||||
|
// (new Message())->allowField(true)->save([ // 消息通知
|
||||||
|
// 'type' => 1,
|
||||||
|
// 'name' => '互动消息',
|
||||||
|
// 'avatar' => '',
|
||||||
|
// 'from_id' => 0,
|
||||||
|
// 'target_id' => $circle->user_id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '评论',
|
||||||
|
// 'content' => $user['nickname'] . '评论了你的帖子',
|
||||||
|
// 'circle_id' => $circle->id
|
||||||
|
// ])
|
||||||
|
// ]);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function approve()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$ids = explode(',', $params['ids'] ?? '');
|
||||||
|
if (empty($ids) || empty($params['club_id'])) {
|
||||||
|
$this->error('参数错误');
|
||||||
|
}
|
||||||
|
if (empty($params['status']) || ($params['status'] != -1 && $params['status'] != 1)) {
|
||||||
|
$this->error('status:参数错误');
|
||||||
|
}
|
||||||
|
$member = Menber::get(['club_id' => $params['club_id'], 'user_id' => $this->auth->id]);
|
||||||
|
if (empty($member) || $member->role < 2) {
|
||||||
|
$this->error('无权审核');
|
||||||
|
}
|
||||||
|
$models = CircleModel::where('id', 'IN', $ids)->where('club_id', $params['club_id'])->select();
|
||||||
|
if (empty($models)) {
|
||||||
|
$this->error('数据不存在');
|
||||||
|
}
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
foreach ($models as $model) {
|
||||||
|
if ($model->status != 0) {
|
||||||
|
throw new Exception($model->id . ' 此记录不在待审核状态');
|
||||||
|
}
|
||||||
|
$model->save(['status' => $params['status']]);
|
||||||
|
// (new Message())->allowField(true)->save([ // 消息通知
|
||||||
|
// 'type' => 3,
|
||||||
|
// 'name' => '通知消息',
|
||||||
|
// 'avatar' => '',
|
||||||
|
// 'from_id' => 1,
|
||||||
|
// 'user_id' => $model->user_id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '影圈审核',
|
||||||
|
// 'result' => ($params['status'] == 1) ? '通过' : '不通过',
|
||||||
|
// 'circle_id' => $model->id
|
||||||
|
// ])
|
||||||
|
// ]);
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', count($models));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$ids = explode(',', $params['ids'] ?? '');
|
||||||
|
if (empty($ids) || empty($params['club_id'])) {
|
||||||
|
$this->error('参数错误');
|
||||||
|
}
|
||||||
|
$member = Menber::get(['club_id' => $params['club_id'], 'user_id' => $this->auth->id]);
|
||||||
|
if (empty($member) || $member->role < 2) {
|
||||||
|
$this->error('无权删除');
|
||||||
|
}
|
||||||
|
$result = CircleModel::where('id', 'IN', $ids)->where('club_id', $params['club_id'])->delete();
|
||||||
|
|
||||||
|
$this->success('Success', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function top()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$model = CircleModel::get($params['id'] ?? null);
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error('数据不存在');
|
||||||
|
}
|
||||||
|
$member = Menber::get(['club_id' => $model['club_id'], 'user_id' => $this->auth->id]);
|
||||||
|
if (empty($member) || $member->role < 2) {
|
||||||
|
$this->error('无权操作');
|
||||||
|
}
|
||||||
|
$result = CircleModel::where('club_id', $model['club_id'])->column('max(top) as top');
|
||||||
|
$model->save(['top' => $result[0] + 1]);
|
||||||
|
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
}
|
||||||
429
addons/shopro/controller/zy/Club.php
Normal file
429
addons/shopro/controller/zy/Club.php
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\controller\zy;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
|
use app\admin\model\zy\Menber;
|
||||||
|
use app\admin\model\zy\Stadium;
|
||||||
|
use app\admin\model\zy\game\Game;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use app\admin\model\zy\link\Apply;
|
||||||
|
use app\admin\model\zy\link\Message;
|
||||||
|
use app\admin\model\zy\link\Visitor;
|
||||||
|
use app\admin\model\shopro\user\User;
|
||||||
|
use app\admin\model\zy\game\Activity;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use app\admin\model\zy\game\Participant;
|
||||||
|
use app\admin\model\zy\link\Relation;
|
||||||
|
|
||||||
|
class Club extends Base
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->model = new \app\admin\model\zy\Club;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$sub = Game::alias('g')
|
||||||
|
->join([Participant::$tableName => 'p'], 'p.game_id=g.id', 'LEFT')
|
||||||
|
->field('g.id,g.pid,g.club_id,g.act_id,g.date,g.name,g.start_time,g.end_time,g.cost,JSON_ARRAYAGG(p.avatar) as avatar,count(p.id) as join_num')
|
||||||
|
->where('g.date', '>=', date('Y-m-d'))
|
||||||
|
->order('g.date', 'asc')->group('g.id')->buildSql();
|
||||||
|
$query = $this->model->alias('c')
|
||||||
|
->join([Stadium::$tableName => 's'], 's.id = c.gym_id', 'LEFT')
|
||||||
|
->join([$sub => 'g'], 'g.club_id = c.id', 'LEFT')
|
||||||
|
->field("c.*, s.position,s.name as gym_name,JSON_ARRAYAGG(JSON_OBJECT(
|
||||||
|
'id', g.id,
|
||||||
|
'pid', g.pid,
|
||||||
|
'act_id', g.act_id,
|
||||||
|
'date', g.date,
|
||||||
|
'name', g.name,
|
||||||
|
'start_time', g.start_time,
|
||||||
|
'end_time', g.end_time,
|
||||||
|
'join_num', g.join_num,
|
||||||
|
'avatar', g.avatar,
|
||||||
|
'cost', g.cost
|
||||||
|
)) AS games")->group('c.id');
|
||||||
|
if (isset($params['name'])) {
|
||||||
|
$query->where('c.name', 'like', '%' . $params['name'] . '%');
|
||||||
|
}
|
||||||
|
if (isset($params['tag'])) {
|
||||||
|
$query->where('c.tags', 'like', '%' . $params['tag'] . '%');
|
||||||
|
}
|
||||||
|
if (isset($params['gym_id'])) {
|
||||||
|
$query->where('c.gym_id', $params['gym_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['order'])) {
|
||||||
|
$query->order($params['order'], $params['sort'] ?? NULL);
|
||||||
|
}
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$l) {
|
||||||
|
$games = json_decode($l['games'], true);
|
||||||
|
if (count($games) == 1 && empty($games[0]['id'])) $games = [];
|
||||||
|
foreach ($games as &$g) {
|
||||||
|
$g['cost'] = json_decode($g['cost'], true);
|
||||||
|
}
|
||||||
|
$l['games'] = $games;
|
||||||
|
}
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view()
|
||||||
|
{
|
||||||
|
$model = $this->model->get($this->request->param('id'))->toarray();
|
||||||
|
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
$menber = Menber::alias('m')
|
||||||
|
->join([User::$tableName => 'u'], 'u.id = m.user_id')
|
||||||
|
->where('club_id', $model['id'])->field('gender,count(*) as num')->group('u.gender')
|
||||||
|
->column('count(*) as num', 'gender');
|
||||||
|
$model['gender0'] = $menber[0] ?? 0;
|
||||||
|
$model['gender1'] = $menber[1] ?? 0;
|
||||||
|
$this->model->where('id', $model['id'])->setInc('attention');
|
||||||
|
$user = auth_user();
|
||||||
|
$visitor = Visitor::where('type', 0)->where('obj_id', $model['id'])->where('user_id', $user['id'])->find();
|
||||||
|
if (empty($visitor)) {
|
||||||
|
$visitor = new Visitor;
|
||||||
|
}
|
||||||
|
$visitor->allowField(true)->save([
|
||||||
|
'type' => 0,
|
||||||
|
'obj_id' => $model['id'],
|
||||||
|
'user_id' => $user['id'],
|
||||||
|
'nickname' => $user['nickname'],
|
||||||
|
'avatar' => $user['avatar'],
|
||||||
|
'gender' => $user['gender'],
|
||||||
|
'times' => empty($visitor->times) ? 1 : $visitor->times + 1
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$result = false;
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$params['president'] = $this->auth->id;
|
||||||
|
$result = $this->model->allowField(true)->save($params);
|
||||||
|
$menber = new Menber;
|
||||||
|
$menber->allowField(true)->save([
|
||||||
|
'club_id' => $this->model->id,
|
||||||
|
'user_id' => $this->auth->id,
|
||||||
|
'role' => 3
|
||||||
|
]);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取俱乐部成员
|
||||||
|
public function menber()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$sub = Relation::field('user_id,target_id,content')->where('user_id', $this->auth->id)->buildSql();
|
||||||
|
$query = Db::table(Menber::$tableName)->alias('m')
|
||||||
|
->join([User::$tableName => 'u'], 'u.id = m.user_id', 'LEFT')
|
||||||
|
->join([$sub => 'r'], 'r.target_id = m.user_id', 'LEFT')
|
||||||
|
->field('m.*,u.avatar,u.gender,u.nickname,u.wechat,u.qq,u.mobile,r.content');
|
||||||
|
if (empty($params['id'])) {
|
||||||
|
return $this->error('参数错误');
|
||||||
|
}
|
||||||
|
$query->where('club_id', $params['id']);
|
||||||
|
if (isset($params['role'])) {
|
||||||
|
$query->where('role', 'IN', explode(',', $params['role']));
|
||||||
|
} else {
|
||||||
|
$query->where('role', '>', 0);
|
||||||
|
}
|
||||||
|
if (isset($params['nickname'])) {
|
||||||
|
$query->where('nickname', 'LIKE', '%' . $params['nickname'] . '%');
|
||||||
|
}
|
||||||
|
if (isset($params['order'])) {
|
||||||
|
$query->order($params['order'], $params['sort'] ?? NULL);
|
||||||
|
}
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$l) {
|
||||||
|
$l['content'] = json_decode($l['content'] ?? '[]', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取俱乐部活动
|
||||||
|
public function activity()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = Db::table(Activity::$tableName)
|
||||||
|
->where('pid', 0) //主活动
|
||||||
|
->field('');
|
||||||
|
if (isset($params['club_id'])) {
|
||||||
|
$query->where('club_id', $params['club_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['week'])) {
|
||||||
|
$query->where('week', $params['week']);
|
||||||
|
}
|
||||||
|
$res = $query->select();
|
||||||
|
|
||||||
|
$this->success('Success', $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请加入俱乐部
|
||||||
|
public function apply()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$club = $this->model->get($params['club_id']);
|
||||||
|
if (empty($club)) {
|
||||||
|
return $this->error('俱乐部不存在');
|
||||||
|
}
|
||||||
|
if ($club['join_type'] > 1) {
|
||||||
|
$this->error('该俱乐部不允许申请');
|
||||||
|
}
|
||||||
|
$user = Menber::get(['club_id' => $club->id, 'user_id' => $this->auth->id]);
|
||||||
|
if (empty($user)) {
|
||||||
|
$user = new Menber;
|
||||||
|
} else {
|
||||||
|
if ($user['role'] > 0) {
|
||||||
|
$this->error('您已经是俱乐部成员,无需重复申请');
|
||||||
|
} else if ($user['role'] == -1) {
|
||||||
|
$this->error('您已被管理员拉黑');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($club['join_type'] == 0) {
|
||||||
|
$user->allowField(true)->save([
|
||||||
|
'club_id' => $club->id,
|
||||||
|
'user_id' => $this->auth->id,
|
||||||
|
'role' => 1
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
if (Apply::get(['type' => 1, 'user_id' => $this->auth->id, 'target_id' => $club->id, 'status' => 1])) {
|
||||||
|
return $this->error('申请审核中');
|
||||||
|
}
|
||||||
|
$apply = new Apply;
|
||||||
|
$apply->allowField(true)->save([ // 记录申请
|
||||||
|
'type' => 1,
|
||||||
|
'user_id' => $this->auth->id,
|
||||||
|
'target_id' => $club->id,
|
||||||
|
'content' => json_encode(['reason' => $params['reason'] ?? '']),
|
||||||
|
'reason' => $params['reason'],
|
||||||
|
'status' => 1
|
||||||
|
]);
|
||||||
|
// (new Message())->allowField(true)->save([ // 消息通知
|
||||||
|
// 'type' => 3,
|
||||||
|
// 'name' => $club->name,
|
||||||
|
// 'avatar' => $club->logo,
|
||||||
|
// 'from_id' => $club->id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '俱乐部加入申请',
|
||||||
|
// '俱乐部名称' => $club->name,
|
||||||
|
// '申请人' => $this->user->nickname,
|
||||||
|
// '申请时间' => date('Y-m-d H:i:s'),
|
||||||
|
// 'reason' => $params['reason'] ?? '',
|
||||||
|
// 'apply_id' => $apply->id
|
||||||
|
// ])
|
||||||
|
// ]);
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
//join_type 0:'开放加入,无需审核',1:'开放申请,审核加入',2:'会员邀请,无需审核',3:'会员邀请,审核加入',4:'仅管理员邀请,无需审核'
|
||||||
|
|
||||||
|
// 邀请加入俱乐部
|
||||||
|
public function invite()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$club = $this->model->get($params['club_id']);
|
||||||
|
if (empty($club)) {
|
||||||
|
return $this->error('俱乐部不存在');
|
||||||
|
}
|
||||||
|
$user = User::get($params['user_id']);
|
||||||
|
if (empty($user)) {
|
||||||
|
return $this->error('用户不存在');
|
||||||
|
}
|
||||||
|
$menber = Menber::where('club_id', $params['club_id'])
|
||||||
|
->where('user_id', $this->auth->id)
|
||||||
|
->where('role', '>', 0)->find();
|
||||||
|
if (empty($menber)) {
|
||||||
|
$this->error('您无权邀请加入俱乐部');
|
||||||
|
}
|
||||||
|
$invate = Menber::get(['user_id' => $params['user_id'], 'club_id' => $params['club_id']]);
|
||||||
|
if (empty($invate)) {
|
||||||
|
$invate = new Menber;
|
||||||
|
} else {
|
||||||
|
if ($invate['role'] > 0) {
|
||||||
|
$this->error('用户已在俱乐部');
|
||||||
|
} else if ($invate['role'] == -1) {
|
||||||
|
$this->error('用户已被管理员拉黑');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($menber['role'] > 1 || $club['join_type'] == 0 || $club['join_type'] == 2) { // 管理员或者开放加入
|
||||||
|
$invate->allowField(true)->save([
|
||||||
|
'club_id' => $club->id,
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'role' => 1
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
if (Apply::get(['type' => 1, 'user_id' => $this->auth->id, 'target_id' => $user->id, 'status' => 1])) {
|
||||||
|
return $this->error('申请审核中');
|
||||||
|
}
|
||||||
|
$apply = new Apply;
|
||||||
|
$apply->allowField(true)->save([ // 记录申请
|
||||||
|
'type' => 1,
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'target_id' => $club->id,
|
||||||
|
'content' => json_encode(['reason' => $params['reason'] ?? '']),
|
||||||
|
'reason' => $params['reason'],
|
||||||
|
'status' => 1
|
||||||
|
]);
|
||||||
|
// (new Message())->allowField(true)->save([ // 消息通知
|
||||||
|
// 'type' => 3,
|
||||||
|
// 'name' => $club->name,
|
||||||
|
// 'avatar' => $club->logo,
|
||||||
|
// 'from_id' => $club->id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '俱乐部加入申请',
|
||||||
|
// '俱乐部名称' => $club->name,
|
||||||
|
// '申请人' => $user->nickname,
|
||||||
|
// '申请时间' => date('Y-m-d H:i:s'),
|
||||||
|
// 'reason' => $params['reason'] ?? '',
|
||||||
|
// 'apply_id' => $apply->id,
|
||||||
|
// ])
|
||||||
|
// ]);
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取申请列表
|
||||||
|
public function applyList()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$club = $this->model->get($params['club_id']);
|
||||||
|
if (empty($club)) {
|
||||||
|
return $this->error('俱乐部不存在');
|
||||||
|
}
|
||||||
|
$menber = Menber::where('club_id', $params['club_id'])
|
||||||
|
->where('user_id', $this->auth->id)
|
||||||
|
->where('role', '>', 1)->find();
|
||||||
|
if (empty($menber)) {
|
||||||
|
$this->error('您无权处理申请');
|
||||||
|
}
|
||||||
|
$query = Apply::where('type', 1)->where('target_id', $params['club_id']);
|
||||||
|
if (isset($params['status'])) {
|
||||||
|
$query->where('status', $params['status']);
|
||||||
|
}
|
||||||
|
$applyList = $query->select();
|
||||||
|
$this->success('Success', $applyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理申请
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$apply = Apply::get(['id' => $params['apply_id'], 'status' => 1]);
|
||||||
|
if (empty($apply)) {
|
||||||
|
return $this->error('申请记录不存在');
|
||||||
|
}
|
||||||
|
$menber = Menber::where('club_id', $apply['target_id'])
|
||||||
|
->where('user_id', $this->auth->id)
|
||||||
|
->where('role', '>', 1)->find();
|
||||||
|
if (empty($menber)) {
|
||||||
|
$this->error('您无权处理申请');
|
||||||
|
}
|
||||||
|
if ($apply['status'] != 1) {
|
||||||
|
return $this->error('该申请已处理');
|
||||||
|
}
|
||||||
|
if ($params['status'] == 2) {
|
||||||
|
(new Menber)->allowField(true)->save([
|
||||||
|
'club_id' => $apply['target_id'],
|
||||||
|
'user_id' => $apply['user_id'],
|
||||||
|
'role' => 1
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$apply->allowField(true)->save([
|
||||||
|
'status' => $params['status'],
|
||||||
|
'reply' => $params['reply'] ?? ''
|
||||||
|
]);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置成员角色
|
||||||
|
public function setmember()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$user = Menber::where('club_id', $params['club_id'])
|
||||||
|
->where('user_id', $this->auth->id)
|
||||||
|
->where('role', '>', 1)->find();
|
||||||
|
if (empty($user)) {
|
||||||
|
$this->error('无权限');
|
||||||
|
}
|
||||||
|
if (isset($params['role']) && ($params['role'] < -1 || $params['role'] > 2)) {
|
||||||
|
$this->error('非法角色');
|
||||||
|
}
|
||||||
|
$user_ids = explode(',', $params['user_id'] ?? '');
|
||||||
|
$menber = Menber::where('club_id', $params['club_id'])->where('user_id', 'IN', $user_ids)->select();
|
||||||
|
if (empty($menber)) {
|
||||||
|
$this->error('成员不存在');
|
||||||
|
}
|
||||||
|
if ($user['role'] != 3) {
|
||||||
|
if (isset($params['role']) && $params['role'] > 1) {
|
||||||
|
$this->error('只有会长才能设置管理员');
|
||||||
|
}
|
||||||
|
foreach ($menber as $m) {
|
||||||
|
if ($m['role'] > 1) $this->error('只有会长才能修改管理员');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($menber as $m) {
|
||||||
|
if ($m['role'] == 3 && isset($params['role'])) {
|
||||||
|
$this->error('不可修改会长角色');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$update = [];
|
||||||
|
if (isset($params['role'])) $update['role'] = $params['role'];
|
||||||
|
if (isset($params['remark'])) $update['remark'] = $params['remark'];
|
||||||
|
if (isset($params['tags'])) $update['tags'] = $params['tags'];
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
Menber::where('club_id', $params['club_id'])->where('user_id', 'IN', $user_ids)->update($update);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
}
|
||||||
495
addons/shopro/controller/zy/Game.php
Normal file
495
addons/shopro/controller/zy/Game.php
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\controller\zy;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
|
use app\admin\model\zy\Club;
|
||||||
|
use app\admin\model\zy\Menber;
|
||||||
|
use app\admin\model\zy\Stadium;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use app\admin\model\zy\link\Message;
|
||||||
|
use app\admin\model\zy\game\Activity;
|
||||||
|
use app\admin\model\zy\game\GameJoin;
|
||||||
|
use app\admin\model\zy\game\GameMatch;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use app\admin\model\zy\game\Participant;
|
||||||
|
use addons\shopro\service\order\OrderRefund;
|
||||||
|
|
||||||
|
|
||||||
|
class Game extends Base
|
||||||
|
{
|
||||||
|
protected $noNeedLogin = ['view'];
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->model = new \app\admin\model\zy\game\Game;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = $this->model->alias('g')
|
||||||
|
->join([Participant::$tableName => 'p'], 'p.game_id=g.id', 'LEFT')
|
||||||
|
->join([Activity::$tableName => 'a'], 'a.id=g.act_id', 'LEFT')
|
||||||
|
->join([Stadium::$tableName => 's'], 's.id = g.gym_id', 'LEFT')
|
||||||
|
->join([Club::$tableName => 'c'], 'c.id = g.club_id', 'LEFT')
|
||||||
|
->field('g.*,a.type,s.name as gym_name,c.name as club_name,JSON_ARRAYAGG(p.avatar) as avatar,count(p.id) as join_num');
|
||||||
|
if (isset($params['name'])) {
|
||||||
|
$query->where('g.name', 'like', '%' . $params['name'] . '%');
|
||||||
|
}
|
||||||
|
if (isset($params['club_id'])) {
|
||||||
|
$query->where('g.club_id', $params['club_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['week'])) {
|
||||||
|
$query->where('g.week', $params['week']);
|
||||||
|
}
|
||||||
|
if (isset($params['pid'])) {
|
||||||
|
$query->where('g.pid', $params['pid']);
|
||||||
|
} else {
|
||||||
|
$query->where('g.pid', 0);
|
||||||
|
}
|
||||||
|
if (isset($params['public_time'])) {
|
||||||
|
$query->where('g.public_time', $params['public_time']);
|
||||||
|
} else {
|
||||||
|
$query->where('g.public_time', '<=', date('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
if (isset($params['page'])) {
|
||||||
|
$pageSize = intval($params['pageSize'] ?? 10);
|
||||||
|
$offeset = (intval($params['page']) - 1) * $pageSize;
|
||||||
|
$query->limit($offeset, $pageSize);
|
||||||
|
}
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$v) {
|
||||||
|
$v['cost'] = json_decode($v['cost'] ?? '[]', true);
|
||||||
|
$v['avatar'] = json_decode($v['avatar'] ?? '[]', true);
|
||||||
|
}
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view()
|
||||||
|
{
|
||||||
|
$model = $this->model->get($this->request->param('id'));
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->model->where('id', $model['id'])->setInc('attention');
|
||||||
|
$model['cost'] = json_decode($model['cost'] ?? '[]', true);
|
||||||
|
$model['referee'] = explode(',', $model['referee'] ?? '');
|
||||||
|
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
// 退坑
|
||||||
|
public function quit()
|
||||||
|
{
|
||||||
|
$model = $this->model->get($this->request->param('id'));
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
if ($model['status'] > 1) {
|
||||||
|
$this->error('活动已开始或结束,不能退出');
|
||||||
|
}
|
||||||
|
if ($model['status'] == -1) {
|
||||||
|
$this->error('活动已取消');
|
||||||
|
}
|
||||||
|
$join = GameJoin::where('user_id', $this->auth->id)->find();
|
||||||
|
if (empty($join) || $join['status'] == -1) {
|
||||||
|
$this->error('未报名或已取消');
|
||||||
|
}
|
||||||
|
if (date('Y-m-d H:i:s') >= $model['quit_time']) {
|
||||||
|
$this->error('已超过免费退出时间');
|
||||||
|
}
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$join->save(['status' => -1]);
|
||||||
|
if ($join['status'] == 1) {
|
||||||
|
$order = $this->model->paid()->where('id', $join->order_id)->lock(true)->find();
|
||||||
|
if (!$order) {
|
||||||
|
$this->error('订单不存在或不可退款');
|
||||||
|
}
|
||||||
|
$orderRefund = new OrderRefund($order);
|
||||||
|
$orderRefund->fullRefund(NULL, [
|
||||||
|
'refund_type' => 'back',
|
||||||
|
'remark' => '用户自行退出活动'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// (new Message())->save([
|
||||||
|
// 'type' => 1,
|
||||||
|
// 'name' => '系统消息',
|
||||||
|
// 'avatar' => '',
|
||||||
|
// 'from_id' => 0,
|
||||||
|
// 'user_id' => $join->user_id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '退出',
|
||||||
|
// 'content' => '已退出 ' . $model['name'] . ' 活动',
|
||||||
|
// 'game_id' => $model->id
|
||||||
|
// ])
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消活动
|
||||||
|
public function cancle()
|
||||||
|
{
|
||||||
|
$model = $this->model->get($this->request->param('id'));
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
$member = Menber::get(['club_id' => $model->club_id, 'user_id' => $this->auth->id]);
|
||||||
|
if (empty($member) || $member->role < 2) {
|
||||||
|
$this->error('无权取消活动');
|
||||||
|
}
|
||||||
|
if ($model['pid'] != 0) {
|
||||||
|
$this->error('不能取消子活动');
|
||||||
|
}
|
||||||
|
if ($model['status'] > 1) {
|
||||||
|
$this->error('活动已开始或结束,不能取消');
|
||||||
|
}
|
||||||
|
if ($model['status'] == -1) {
|
||||||
|
$this->error('活动已取消');
|
||||||
|
}
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$model->save(['status' => -1]);
|
||||||
|
(new \app\admin\model\zy\game\Game)->where('pid', $model->id)->update(['status' => -1]);
|
||||||
|
//取消成功,进入退款流程并通知用户
|
||||||
|
$join = GameJoin::where('game_id', $model['id'])->select();
|
||||||
|
$msgs = [];
|
||||||
|
foreach ($join as $j) {
|
||||||
|
$j->save(['status' => -1]);
|
||||||
|
if ($j['status'] == 1) {
|
||||||
|
$order = $this->model->paid()->where('id', $j->order_id)->lock(true)->find();
|
||||||
|
if (!$order) {
|
||||||
|
$this->error('订单不存在或不可退款');
|
||||||
|
}
|
||||||
|
$orderRefund = new OrderRefund($order);
|
||||||
|
$orderRefund->fullRefund(NULL, [
|
||||||
|
'refund_type' => 'back',
|
||||||
|
'remark' => '活动取消,全额退款'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// $msgs[] = [
|
||||||
|
// 'type' => 1,
|
||||||
|
// 'name' => '系统消息',
|
||||||
|
// 'avatar' => '',
|
||||||
|
// 'from_id' => 0,
|
||||||
|
// 'user_id' => $j->user_id,
|
||||||
|
// 'content' => json_encode([
|
||||||
|
// 'topic' => '评论',
|
||||||
|
// 'content' => $model['name'] . ' 活动已取消',
|
||||||
|
// 'game_id' => $model->id
|
||||||
|
// ])
|
||||||
|
// ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// (new Message())->insertAll($msgs);;
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赛制说明
|
||||||
|
public function describe()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$game = $this->model->get($params['id'] ?? NULL);
|
||||||
|
if (empty($game)) {
|
||||||
|
$this->error('比赛不存在');
|
||||||
|
}
|
||||||
|
$gameClass = 'format\\Game' . $game['team_type'] . $game['rule_type'];
|
||||||
|
if (!class_exists($gameClass)) {
|
||||||
|
throw new Exception("赛制文件不存在,请联系管理人员: {$gameClass}");
|
||||||
|
}
|
||||||
|
$format = new $gameClass;
|
||||||
|
if (!$format instanceof \format\GameInterface) {
|
||||||
|
throw new Exception("赛制配置错误,请联系管理人员: {$gameClass}");
|
||||||
|
}
|
||||||
|
$describe = $format->describe();
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', $describe);
|
||||||
|
}
|
||||||
|
|
||||||
|
//参与人员列表
|
||||||
|
public function participant()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = Participant::where('game_id', $params['game_id']);
|
||||||
|
if (isset($params['game_join_id'])) {
|
||||||
|
$query->where('game_join_id', $params['game_join_id']);
|
||||||
|
}
|
||||||
|
if (isset($params['status'])) {
|
||||||
|
$query->where('status', $params['status']);
|
||||||
|
} else {
|
||||||
|
$query->where('status', 1);
|
||||||
|
}
|
||||||
|
if (isset($params['gender'])) {
|
||||||
|
$query->where('gender', $params['gender']);
|
||||||
|
}
|
||||||
|
if (isset($params['order'])) {
|
||||||
|
$query->order($params['order'], $params['sort'] ?? NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', $query->select());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户
|
||||||
|
public function setUser()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$user = Participant::get($params['id'] ?? NULL);
|
||||||
|
if (empty($user)) {
|
||||||
|
$this->error('用户不存在');
|
||||||
|
}
|
||||||
|
$game = $this->model->get($user['game_id']);
|
||||||
|
$referee = explode(',', $game['referee'] ?? ''); //裁判
|
||||||
|
$menber = Menber::where(['club_id' => $game['club_id'], 'user_id' => $this->auth->id])->where('role', '>', 1)->find();
|
||||||
|
if (empty($menber) && !in_array($this->auth->id, $referee)) {
|
||||||
|
$this->error('您没有权限');
|
||||||
|
}
|
||||||
|
$update['status'] = intval($params['status']);
|
||||||
|
if ($update['status'] > 1 || $update['status'] < -1) {
|
||||||
|
$this->error('status错误');
|
||||||
|
}
|
||||||
|
if (!empty($params['mark'])) {
|
||||||
|
$update['mark'] = $params['mark'];
|
||||||
|
}
|
||||||
|
$user->save($update);
|
||||||
|
$this->success('修改成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取比赛匹配列表
|
||||||
|
public function macthList()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$game = $this->model->get($params['game_id'] ?? NULL);
|
||||||
|
if (empty($game)) {
|
||||||
|
$this->error('比赛不存在');
|
||||||
|
}
|
||||||
|
$dataTime = date('Y-m-d H:i:s');
|
||||||
|
$startTime = $game['date'] . ' ' . $game['start_time'];
|
||||||
|
if ($dataTime < $startTime) {
|
||||||
|
$this->error('比赛时间未开始');
|
||||||
|
}
|
||||||
|
$endTime = $game['date'] . ' ' . $game['end_time'];
|
||||||
|
if ($dataTime > $endTime) {
|
||||||
|
$this->error('比赛时间已结束');
|
||||||
|
}
|
||||||
|
$matchs = GameMatch::where('game_id', $game['id'])->select();
|
||||||
|
if (empty($matchs)) {
|
||||||
|
$participant = Participant::where('game_id', $game['id'])->where('status', 1)->select();
|
||||||
|
$gameClass = 'format\\Game' . $game['team_type'] . $game['rule_type'];
|
||||||
|
if (!class_exists($gameClass)) {
|
||||||
|
throw new Exception("赛制文件不存在,请联系管理人员: {$gameClass}");
|
||||||
|
}
|
||||||
|
$format = new $gameClass;
|
||||||
|
if (!$format instanceof \format\GameInterface) {
|
||||||
|
throw new Exception("赛制配置错误,请联系管理人员: {$gameClass}");
|
||||||
|
}
|
||||||
|
$matchs = $format->match($game, $participant);
|
||||||
|
$res = (new GameMatch)->insertAll($matchs);
|
||||||
|
$matchs = GameMatch::where('game_id', $game['id'])->select();
|
||||||
|
}
|
||||||
|
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage(), $e);
|
||||||
|
}
|
||||||
|
foreach ($matchs as $k => &$m) {
|
||||||
|
if (!empty($params['level']) && $params['level'] != $m['level']) {
|
||||||
|
unset($matchs[$k]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$m['teamA'] = json_decode($m['teamA'], true);
|
||||||
|
$m['teamB'] = json_decode($m['teamB'], true);
|
||||||
|
}
|
||||||
|
$this->success('Success', $matchs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMacth()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$m = GameMatch::get($params['id'] ?? NULL);
|
||||||
|
if (empty($m)) {
|
||||||
|
$this->error('对阵记录不存在');
|
||||||
|
}
|
||||||
|
$m['teamA'] = json_decode($m['teamA'] ?? '[]', true);
|
||||||
|
$m['teamB'] = json_decode($m['teamB'] ?? '[]', true);
|
||||||
|
$m['round1'] = json_decode($m['round1'] ?? '[]', true);
|
||||||
|
$m['round2'] = json_decode($m['round2'] ?? '[]', true);
|
||||||
|
$m['round3'] = json_decode($m['round3'] ?? '[]', true);
|
||||||
|
$m['winner'] = json_decode($m['winner'] ?? '[]', true);
|
||||||
|
return $this->success('Success', $m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scoring()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$match = GameMatch::get($params['id'] ?? NULL);
|
||||||
|
if (empty($match)) {
|
||||||
|
$this->error('对阵记录不存在');
|
||||||
|
}
|
||||||
|
$game = $this->model->get($match['game_id']);
|
||||||
|
$referee = explode(',', $game['referee']?? '');
|
||||||
|
if (empty($referee)) {
|
||||||
|
$this->error('请先设置裁判');
|
||||||
|
}
|
||||||
|
if (!in_array($this->auth->id, $referee)) {
|
||||||
|
$this->error('不是裁判无权计分');
|
||||||
|
}
|
||||||
|
if (empty($params['round']) || ($params['round'] != 1 && $params['round'] != 2 && $params['round'] != 3)) {
|
||||||
|
$this->error('回合(局)数错误');
|
||||||
|
}
|
||||||
|
$round = 'round' . $params['round'];
|
||||||
|
if (!empty($match->$round)) {
|
||||||
|
$this->error('请勿重复提交');
|
||||||
|
}
|
||||||
|
$match->$round = json_encode([
|
||||||
|
'addedA' => $params['addedA'], //队伍B让分
|
||||||
|
'addedB' => $params['addedB'], //队伍A让分
|
||||||
|
'scoreA' => $params['scoreA'], //队伍A得分
|
||||||
|
'scoreB' => $params['scoreB'], //队伍B得分
|
||||||
|
]);
|
||||||
|
$totalA = $params['scoreA'] + $params['addedA'];
|
||||||
|
$totalB = $params['scoreB'] + $params['addedB'];
|
||||||
|
if ($totalA > $totalB) {
|
||||||
|
$match->scoreA = $match->scoreA + 1; //队伍A回合得分加1
|
||||||
|
} else if ($totalA < $totalB) {
|
||||||
|
$match->scoreB = $match->scoreB + 1; //队伍B回合得分加1
|
||||||
|
} else {
|
||||||
|
$this->error('回合(局)内分数不能相等');
|
||||||
|
}
|
||||||
|
if (!empty($match->round1) && !empty($match->round2) && !empty($match->round3)) { //3局结束
|
||||||
|
$match->winner = $match->scoreA > $match->scoreB ? $match->teamA : $match->teamB;
|
||||||
|
}
|
||||||
|
$match->save();
|
||||||
|
// 更新用户得分
|
||||||
|
$teamA = json_decode($match->teamA, true);
|
||||||
|
|
||||||
|
$teamAuser = Participant::where('game_id', $game['id'])
|
||||||
|
->where('user_id', 'IN', array_column($teamA['user'], 'user_id'))
|
||||||
|
->where('status', '>', -1)->select();
|
||||||
|
// dd($teamAuser);
|
||||||
|
foreach ($teamAuser as $u) {
|
||||||
|
$u->team = $teamA['name']; //队伍名
|
||||||
|
$u->score = $totalA; //得分
|
||||||
|
$u->net_score = $params['scoreA']; //净得分
|
||||||
|
$u->save();
|
||||||
|
}
|
||||||
|
$teamB = json_decode($match->teamB, true);
|
||||||
|
$teamBuser = Participant::where('game_id', $game['id'])->where('user_id', 'IN', array_column($teamB['user'], 'user_id'))->select();
|
||||||
|
foreach ($teamBuser as $u) {
|
||||||
|
$u->team = $teamB['name']; //队伍名
|
||||||
|
$u->score = $totalA; //得分
|
||||||
|
$u->net_score = $params['scoreA']; //净得分
|
||||||
|
$u->save();
|
||||||
|
}
|
||||||
|
$undone = GameMatch::where('level', $match['level'])->where('winner', null)->count();
|
||||||
|
if ($undone == 0) { //所有比赛完成,开启下一轮比赛
|
||||||
|
$gameClass = 'format\\Game' . $game['team_type'] . $game['rule_type'];
|
||||||
|
if (!class_exists($gameClass)) {
|
||||||
|
throw new Exception("赛制文件不存在,请联系管理人员: {$gameClass}");
|
||||||
|
}
|
||||||
|
$format = new $gameClass;
|
||||||
|
if (!$format instanceof \format\GameInterface) {
|
||||||
|
throw new Exception("赛制配置错误,请联系管理人员: {$gameClass}");
|
||||||
|
}
|
||||||
|
$done = GameMatch::where('level', $match['level'])->where('winner', '!=', '')->select();
|
||||||
|
$matchs = $format->nextLevel($game, $done, $match['level'] + 1);
|
||||||
|
if (!empty($matchs)) {
|
||||||
|
(new GameMatch)->insertAll($matchs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 团队排名
|
||||||
|
public function teamRank()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$game = $this->model->get($params['game_id']) ?? null;
|
||||||
|
if (empty($game)) {
|
||||||
|
$this->error('比赛不存在');
|
||||||
|
}
|
||||||
|
$match = GameMatch::field('*,GREATEST(scoreA,scoreB) as score')->where('game_id', $game['id'])->order(['level' => 'desc', 'score' => 'desc'])->select();
|
||||||
|
$teams = [];
|
||||||
|
$rank = 1;
|
||||||
|
foreach ($match as $m) {
|
||||||
|
$win = json_decode($m['winner'] ?? '', true);
|
||||||
|
if (empty($win)) { // 未决出胜负的按暂时得分 高的队伍排名靠前
|
||||||
|
$teamA = json_decode($m['teamA'] ?? '', true);
|
||||||
|
$teamB = json_decode($m['teamB'] ?? '', true);
|
||||||
|
if (($m['scoreA'] >= $m['scoreB'])) {
|
||||||
|
$teamA['rank'] = $rank;
|
||||||
|
$teamB['rank'] = $rank + 1;
|
||||||
|
$teams[] = $teamA;
|
||||||
|
$teams[] = $teamB;
|
||||||
|
$rank += 2;
|
||||||
|
} else {
|
||||||
|
$teamB['rank'] = $rank;
|
||||||
|
$teamA['rank'] = $rank + 1;
|
||||||
|
$teams[] = $teamB;
|
||||||
|
$teams[] = $teamA;
|
||||||
|
$rank += 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$win['rank'] = $rank;
|
||||||
|
$rank++;
|
||||||
|
$teams[] = $win;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', $teams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 个人排名
|
||||||
|
public function rank()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$game = $this->model->get($params['game_id']) ?? null;
|
||||||
|
if (empty($game)) {
|
||||||
|
$this->error('比赛不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = Participant::where('game_id', $game['id'])->where('status', '>', -1)->order('score', 'desc')->select();
|
||||||
|
$rank = 1;
|
||||||
|
foreach ($list as &$l) {
|
||||||
|
$l['rank'] = $rank;
|
||||||
|
$rank += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success('Success', $list);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
addons/shopro/controller/zy/Gym.php
Normal file
53
addons/shopro/controller/zy/Gym.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\controller\zy;
|
||||||
|
|
||||||
|
use app\admin\model\zy\link\Visitor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Gym extends Base
|
||||||
|
{
|
||||||
|
protected $noNeedRight = ['*'];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->model = new \app\admin\model\zy\Stadium;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = $this->model->where('status', 1);
|
||||||
|
if (isset($params['name'])) {
|
||||||
|
$query->where('name', 'like', '%' . $params['name'] . '%');
|
||||||
|
}
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$this->success('Success', ['list' => $res->items(), 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view()
|
||||||
|
{
|
||||||
|
$model = $this->model->get($this->request->param('id'));
|
||||||
|
if (empty($model)) {
|
||||||
|
$this->error(__('No rows were found'));
|
||||||
|
}
|
||||||
|
$user = auth_user();
|
||||||
|
$visitor = Visitor::where('type', 1)->where('obj_id', $model['id'])->where('user_id', $user['id'])->find();
|
||||||
|
if (empty($visitor)) {
|
||||||
|
$visitor = new Visitor;
|
||||||
|
}
|
||||||
|
$visitor->allowField(true)->save([
|
||||||
|
'type' => 1,
|
||||||
|
'obj_id' => $model['id'],
|
||||||
|
'user_id' => $user['id'],
|
||||||
|
'nickname' => $user['nickname'],
|
||||||
|
'avatar' => $user['avatar'],
|
||||||
|
'gender' => $user['gender'],
|
||||||
|
'times' => empty($visitor->times) ? 1 : $visitor->times + 1
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->success('Success', $model);
|
||||||
|
}
|
||||||
|
}
|
||||||
157
addons/shopro/controller/zy/Sys.php
Normal file
157
addons/shopro/controller/zy/Sys.php
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addons\shopro\controller\zy;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
|
use app\admin\model\zy\Tags;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use app\admin\model\zy\sign\Record;
|
||||||
|
use app\admin\model\zy\link\Visitor;
|
||||||
|
use app\admin\model\zy\sign\SignSet;
|
||||||
|
use app\admin\model\shopro\user\User;
|
||||||
|
use app\admin\model\zy\circle\Circle;
|
||||||
|
use app\admin\model\zy\link\Complaint;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use addons\shopro\library\activity\traits\GiveGift;
|
||||||
|
|
||||||
|
class Sys extends Base
|
||||||
|
{
|
||||||
|
use GiveGift;
|
||||||
|
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$model = new Tags();
|
||||||
|
if (isset($params['type'])) {
|
||||||
|
$model->where('type', $params['type']);
|
||||||
|
}
|
||||||
|
if (isset($params['group'])) {
|
||||||
|
$model->where('group', $params['group']);
|
||||||
|
}
|
||||||
|
$res = $model->select();
|
||||||
|
|
||||||
|
$this->success('Success', $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 举报投诉
|
||||||
|
public function complain()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
if ($params['type'] == 3) { //用户举报
|
||||||
|
$target = User::get($params['target_id']);
|
||||||
|
} elseif ($params['type'] == 2) { //影圈举报
|
||||||
|
$target = Circle::get($params['target_id']);
|
||||||
|
} elseif ($params['type'] == 1) {
|
||||||
|
$target = ['id' => 0];
|
||||||
|
$params['target_id'] = 0;
|
||||||
|
} else {
|
||||||
|
$this->error('type 类型错误');
|
||||||
|
}
|
||||||
|
if (empty($target)) {
|
||||||
|
$this->error('举报对象不存在');
|
||||||
|
}
|
||||||
|
if (empty($params['content'])) {
|
||||||
|
$this->error('举报内容不能为空');
|
||||||
|
}
|
||||||
|
$user = auth_user();
|
||||||
|
$res = Complaint::get(['user_id' => $user['id'], 'target_id' => $params['target_id'], 'type' => $params['type'], 'status' => 0]);
|
||||||
|
if ($res) {
|
||||||
|
$this->error('您已举报过该对象');
|
||||||
|
}
|
||||||
|
$params['status'] = 0;
|
||||||
|
$params['user_id'] = $user['id'];
|
||||||
|
$params['username'] = $params['username'] ?? $user['username'];
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$result = (new Complaint)->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error('操作失败');
|
||||||
|
}
|
||||||
|
$this->success('Success');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function visitor()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$res = Visitor::where('type', $params['type'])->where('obj_id', $params['obj_id'])->paginate($params['pageSize'] ?? 10);
|
||||||
|
$this->success('Success', ['list' => $res->items(), 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signin()
|
||||||
|
{
|
||||||
|
$signSet = SignSet::where('status', 1)->select();
|
||||||
|
// if (empty($signSet)) {
|
||||||
|
// $this->error('没有签到活动');
|
||||||
|
// }
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$user = auth_user();
|
||||||
|
$newRecord = new Record;
|
||||||
|
$newRecord->user_id = $user->id;
|
||||||
|
$newRecord->date = date('Y-m-d');
|
||||||
|
$newRecord->last = 1;
|
||||||
|
$record = Record::where('user_id', $user->id)->order('id', 'desc')->find();
|
||||||
|
if (!empty($record)) {
|
||||||
|
if ($newRecord->date == $record->date) {
|
||||||
|
$this->error('您今天已签到,请勿重复签到');
|
||||||
|
}
|
||||||
|
if ($record['date'] == date('Y-m-d', strtotime('-1 day'))) {
|
||||||
|
$newRecord->last = $record->last + 1; //连续签到
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$newRecord->remark = '';
|
||||||
|
foreach ($signSet as $set) {
|
||||||
|
if ($newRecord->last >= $set['last']) { // 达成连续签到天数
|
||||||
|
if ($set['chance1'] > 0 && rand(0, 100) <= $set['chance1']) {
|
||||||
|
$res1 = $this->giveCoupons($user, $set['coupon1_id']);
|
||||||
|
if ($res1['errors']) $this->error('优惠券赠送失败,请联系管理员', $res1);
|
||||||
|
$newRecord->remark .= '获得【' . implode(',', $res1['success']) . '】;';
|
||||||
|
}
|
||||||
|
if ($set['chance2'] > 0 && mt_rand(0, 100) <= $set['chance2']) {
|
||||||
|
$res2 = $this->giveCoupons($user, $set['coupon2_id']);
|
||||||
|
if ($res2['errors']) $this->error('优惠券赠送失败,请联系管理员', $res2);
|
||||||
|
$newRecord->remark .= '获得【' . implode(',', $res2['success']) . '】;';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$newRecord->save();
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
$this->success('Success', $newRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signList()
|
||||||
|
{
|
||||||
|
$params = $this->request->param();
|
||||||
|
$query = Record::where('user_id', $this->auth->id)->order('id', 'desc');
|
||||||
|
if (!empty($params['date_begin'])) {
|
||||||
|
$query->where('date', '>=', $params['date_begin']);
|
||||||
|
}
|
||||||
|
if (!empty($params['date_end'])) {
|
||||||
|
$query->where('date', '<=', $params['date_end']);
|
||||||
|
}
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$this->success('Success', ['list' => $res->items(), 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function task()
|
||||||
|
{
|
||||||
|
$record = Record::where('user_id', $this->auth->id)->where('date', date('Y-m-d'))->find();
|
||||||
|
$query = SignSet::field('name,begin_date,end_date,intro')->where('status', 1);
|
||||||
|
$res = $query->paginate($params['pageSize'] ?? 10);
|
||||||
|
$list = $res->items();
|
||||||
|
foreach ($list as &$val) {
|
||||||
|
$val['progress'] = empty($record) ? '今日未完成' : '今日已完成';
|
||||||
|
}
|
||||||
|
$this->success('Success', ['list' => $list, 'count' => $res->total()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
namespace addons\shopro\job;
|
namespace addons\shopro\job;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
use think\queue\Job;
|
use think\queue\Job;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
class Test extends BaseJob
|
class Test extends BaseJob
|
||||||
{
|
{
|
||||||
@@ -48,4 +52,88 @@ class Test extends BaseJob
|
|||||||
@mkdir($dir, 0755, true);
|
@mkdir($dir, 0755, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期性活动创建比赛
|
||||||
|
*/
|
||||||
|
public function zy(Job $job, $data)
|
||||||
|
{
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$_actModel = new \app\admin\model\zy\game\Activity();
|
||||||
|
$_gameModel = new \app\admin\model\zy\game\Game();
|
||||||
|
$dateTime = date('Y-m-d H:i:s'); //当前星期
|
||||||
|
$week = date('w'); //当前星期
|
||||||
|
$current = time(); //当前时间
|
||||||
|
$activity = (clone $_actModel)::where('pid', 0)->select(); //周期性主活动
|
||||||
|
foreach ($activity as $act) {
|
||||||
|
$act = $act->toArray();
|
||||||
|
$publicTime = json_decode($act['public_time'] ?? '[]', true);
|
||||||
|
$publicBefore = intval($publicTime['before'] ?? 0); //提前几天
|
||||||
|
$PublicWeek = $act['week'] - $publicBefore; //设定应该发布的星期
|
||||||
|
if ($PublicWeek != $week && ($PublicWeek + 7) != $week) {
|
||||||
|
print_r('星期不对');
|
||||||
|
continue; // 星期不对
|
||||||
|
}
|
||||||
|
$public_time = strtotime($publicTime['time']); //设定的时间
|
||||||
|
if ($current < $public_time) {
|
||||||
|
print_r('时间未到');
|
||||||
|
continue; //时间未到
|
||||||
|
}
|
||||||
|
$act['public_time'] = date('Y-m-d H:i:s', $public_time - $publicBefore * 86400); //设定的时间
|
||||||
|
$act['date'] = date('Y-m-d', $public_time - $publicBefore * 86400); //设定的日期
|
||||||
|
$startTime = explode(' ', $act['startTime']);
|
||||||
|
if (count($startTime) == 2) {
|
||||||
|
$act['date'] = $startTime[0];
|
||||||
|
$act['startTime'] = $startTime[1];
|
||||||
|
}
|
||||||
|
$endTime = explode(' ', $act['endTime']);
|
||||||
|
if (count($endTime) == 2) $act['endTime'] = $endTime[1];
|
||||||
|
$query = (clone $_gameModel)::where('act_id', $act['id'])->where('public_time', $act['public_time']);
|
||||||
|
if ($act['type'] == 0) { //一次性活动
|
||||||
|
$query->where('date', $act['date']);
|
||||||
|
}
|
||||||
|
$games = $query->select();
|
||||||
|
if (!empty($games)) {
|
||||||
|
print_r('已存在');
|
||||||
|
continue; // 已存在
|
||||||
|
}
|
||||||
|
$joinStartTime = json_decode($act['join_start_time'] ?? '[]', true);
|
||||||
|
$act['join_start_time'] = date('Y-m-d H:i:s', strtotime($joinStartTime['time']) - intval($joinStartTime['before']) * 86400);
|
||||||
|
$joinEndTime = json_decode($act['join_end_time'] ?? '[]', true);
|
||||||
|
$act['join_end_time'] = date('Y-m-d H:i:s', strtotime($joinEndTime['time']) - intval($joinEndTime['before']) * 86400);
|
||||||
|
$quitTime = json_decode($act['quit_time'] ?? '[]', true);
|
||||||
|
$act['quit_time'] = date('Y-m-d H:i:s', strtotime($quitTime['time']) - intval($quitTime['before']) * 86400);
|
||||||
|
$act['act_id'] = $act['id'];
|
||||||
|
unset($act['id']);
|
||||||
|
$res = (clone $_gameModel)->allowField(true)->save($act);
|
||||||
|
$subs = (clone $_actModel)::where('pid', $act['act_id'])->select();
|
||||||
|
foreach ($subs as $sub) { //存在子活动
|
||||||
|
$sub = $sub->toArray();
|
||||||
|
$sub['public_time'] = $act['public_time'];
|
||||||
|
$sub['join_start_time'] = $act['join_start_time'];
|
||||||
|
$sub['join_end_time'] = $act['join_end_time'];
|
||||||
|
$sub['quit_time'] = $act['quit_time'];
|
||||||
|
$sub['act_id'] = $act['act_id'];
|
||||||
|
$sub['date'] = $act['date'];
|
||||||
|
unset($sub['id']);
|
||||||
|
$res = (clone $_gameModel)->allowField(true)->save($sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//处理已生成的比赛
|
||||||
|
$games = (clone $_gameModel)::where('join_start_time', '>=', $dateTime)->where('status', '<', 2)->select();
|
||||||
|
foreach ($games as $g) {
|
||||||
|
if ($g['join_start_time'] <= $dateTime && $g['join_end_time'] > $dateTime) {
|
||||||
|
$g->save(['status' => 1]); //开始报名
|
||||||
|
} elseif ($g['join_end_time'] <= $dateTime) {
|
||||||
|
$g->save(['status' => 2]); //报名截止,活动进行中
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
format_log_error($e, 'zy.activity create game');
|
||||||
|
}
|
||||||
|
$job->release(600); //$delay为延迟时间
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Tree
|
|||||||
* @param \Closure $resultCb 用来处理每一次查询的结果 比如要取出树中的所有 name
|
* @param \Closure $resultCb 用来处理每一次查询的结果 比如要取出树中的所有 name
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getTree($items = 0, \Closure $resultCb = null)
|
public function getTree($items = 0, ?\Closure $resultCb = null)
|
||||||
{
|
{
|
||||||
if (!is_array($items) && !$items instanceof Collection) {
|
if (!is_array($items) && !$items instanceof Collection) {
|
||||||
$items = $this->getQuery()->where('parent_id', $items)->select();
|
$items = $this->getQuery()->where('parent_id', $items)->select();
|
||||||
@@ -48,7 +48,7 @@ class Tree
|
|||||||
* @param \Closure $resultCb 用来处理每一次查询的结果 比如要取出树中的所有 name
|
* @param \Closure $resultCb 用来处理每一次查询的结果 比如要取出树中的所有 name
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getChildren($id, \Closure $resultCb = null)
|
public function getChildren($id, ?\Closure $resultCb = null)
|
||||||
{
|
{
|
||||||
$self = $this->getQuery()->where('id', $id)->select();
|
$self = $this->getQuery()->where('id', $id)->select();
|
||||||
if(!$self) {
|
if(!$self) {
|
||||||
|
|||||||
@@ -2,30 +2,32 @@
|
|||||||
|
|
||||||
namespace addons\shopro\listener;
|
namespace addons\shopro\listener;
|
||||||
|
|
||||||
use addons\shopro\library\notify\Notify;
|
use addons\shopro\facade\Wechat;
|
||||||
use addons\shopro\library\Pipeline;
|
|
||||||
use addons\shopro\service\order\OrderThrough;
|
|
||||||
use app\admin\model\shopro\user\User;
|
|
||||||
use app\admin\model\shopro\Admin;
|
|
||||||
use app\admin\model\shopro\Cart;
|
use app\admin\model\shopro\Cart;
|
||||||
use app\admin\model\shopro\user\Coupon;
|
use app\admin\model\shopro\Admin;
|
||||||
use app\admin\model\shopro\order\Order as OrderModel;
|
|
||||||
use app\admin\model\shopro\order\OrderItem;
|
|
||||||
use app\admin\model\shopro\order\Invoice as OrderInvoiceModel;
|
|
||||||
use app\admin\model\shopro\order\Express as OrderExpressModel;
|
|
||||||
use app\admin\model\shopro\order\Action;
|
|
||||||
use app\admin\model\shopro\order\Invoice;
|
|
||||||
use app\admin\model\shopro\Config;
|
use app\admin\model\shopro\Config;
|
||||||
|
use addons\shopro\library\Pipeline;
|
||||||
use addons\shopro\service\StockSale;
|
use addons\shopro\service\StockSale;
|
||||||
use addons\shopro\traits\CouponSend;
|
use addons\shopro\traits\CouponSend;
|
||||||
use addons\shopro\service\order\OrderRefund;
|
use app\admin\model\shopro\user\User;
|
||||||
|
use app\admin\model\zy\game\GameJoin;
|
||||||
use addons\shopro\service\pay\PayOper;
|
use addons\shopro\service\pay\PayOper;
|
||||||
|
use app\admin\model\shopro\user\Coupon;
|
||||||
|
use addons\shopro\library\notify\Notify;
|
||||||
|
use app\admin\model\shopro\order\Action;
|
||||||
|
use app\admin\model\zy\game\Participant;
|
||||||
|
use app\admin\model\shopro\order\Invoice;
|
||||||
use addons\shopro\service\order\OrderOper;
|
use addons\shopro\service\order\OrderOper;
|
||||||
use addons\shopro\service\user\User as UserService;
|
use app\admin\model\shopro\order\OrderItem;
|
||||||
|
use addons\shopro\service\order\OrderRefund;
|
||||||
|
use addons\shopro\service\order\OrderThrough;
|
||||||
use addons\shopro\service\order\OrderDispatch;
|
use addons\shopro\service\order\OrderDispatch;
|
||||||
use addons\shopro\library\activity\traits\GiveGift;
|
use addons\shopro\library\activity\traits\GiveGift;
|
||||||
|
use addons\shopro\service\user\User as UserService;
|
||||||
use addons\shopro\facade\Activity as ActivityFacade;
|
use addons\shopro\facade\Activity as ActivityFacade;
|
||||||
use addons\shopro\facade\Wechat;
|
use app\admin\model\shopro\order\Order as OrderModel;
|
||||||
|
use app\admin\model\shopro\order\Express as OrderExpressModel;
|
||||||
|
use app\admin\model\shopro\order\Invoice as OrderInvoiceModel;
|
||||||
use addons\shopro\library\easywechatPlus\WechatMiniProgramShop;
|
use addons\shopro\library\easywechatPlus\WechatMiniProgramShop;
|
||||||
|
|
||||||
class Order
|
class Order
|
||||||
@@ -252,6 +254,9 @@ class Order
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// zy体育报名处理
|
||||||
|
GameJoin::where('order_id', $order->id)->update(['status' => 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -348,6 +353,14 @@ class Order
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ZY比赛开始时自动确认收货。
|
||||||
|
$join = GameJoin::get('order_id', $order->id);
|
||||||
|
if (!empty($join)) {
|
||||||
|
$later = strtotime($join->quit_time) - time();
|
||||||
|
if ($later > 0) {
|
||||||
|
\think\Queue::later(($later), '\addons\shopro\job\OrderAutoOper@autoConfirm', $params, 'shopro');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// 添加自动确认收货队列,这个队列只自动确认 本次发货的 items
|
// 添加自动确认收货队列,这个队列只自动确认 本次发货的 items
|
||||||
$confirm_days = Config::getConfigField('shop.order.auto_confirm');
|
$confirm_days = Config::getConfigField('shop.order.auto_confirm');
|
||||||
$confirm_days = $confirm_days > 0 ? $confirm_days : 0;
|
$confirm_days = $confirm_days > 0 ? $confirm_days : 0;
|
||||||
@@ -356,6 +369,7 @@ class Order
|
|||||||
\think\Queue::later(($confirm_days * 86400), '\addons\shopro\job\OrderAutoOper@autoConfirm', $params, 'shopro');
|
\think\Queue::later(($confirm_days * 86400), '\addons\shopro\job\OrderAutoOper@autoConfirm', $params, 'shopro');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$user = User::where('id', $order['user_id'])->find();
|
$user = User::where('id', $order['user_id'])->find();
|
||||||
$user && $user->notify(
|
$user && $user->notify(
|
||||||
@@ -599,6 +613,13 @@ class Order
|
|||||||
// 将开票信息取消
|
// 将开票信息取消
|
||||||
$this->cancelInvoice($order, 'invalid');
|
$this->cancelInvoice($order, 'invalid');
|
||||||
|
|
||||||
|
// zy体育报名处理(未支付,自动退坑)
|
||||||
|
$join = GameJoin::where('order_id', $order->id)->find();
|
||||||
|
if (!empty($join)) {
|
||||||
|
$join->save(['status' => -1]);
|
||||||
|
Participant::where('game_join_id', $join['id'])->update(['status' => -1]);
|
||||||
|
}
|
||||||
|
|
||||||
return $order;
|
return $order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class GoodsService
|
|||||||
protected $is_activity = false; // 是否处理活动
|
protected $is_activity = false; // 是否处理活动
|
||||||
protected $show_score_shop = false; // 查询积分商城商品
|
protected $show_score_shop = false; // 查询积分商城商品
|
||||||
|
|
||||||
public function __construct(\Closure $format = null, \think\db\Query $query = null)
|
public function __construct(?\Closure $format = null, ?\think\db\Query $query = null)
|
||||||
{
|
{
|
||||||
$this->query = $query ?: new Goods();
|
$this->query = $query ?: new Goods();
|
||||||
$this->format = $format;
|
$this->format = $format;
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ trait CouponSend
|
|||||||
foreach ($coupons as $coupon) {
|
foreach ($coupons as $coupon) {
|
||||||
try {
|
try {
|
||||||
$userCoupon = $this->send($user, $coupon);
|
$userCoupon = $this->send($user, $coupon);
|
||||||
$success[] = $coupon->id;
|
$success[] = $coupon->name;
|
||||||
} catch (HttpResponseException $e) {
|
} catch (HttpResponseException $e) {
|
||||||
$data = $e->getResponse()->getData();
|
$data = $e->getResponse()->getData();
|
||||||
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
|
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ trait StockWarning
|
|||||||
$stockWarning = new StockLogModel();
|
$stockWarning = new StockLogModel();
|
||||||
|
|
||||||
$stockWarning->goods_id = $goodsSkuPrice['goods_id'];
|
$stockWarning->goods_id = $goodsSkuPrice['goods_id'];
|
||||||
$stockWarning->admin_id = $admin['id'];
|
$stockWarning->admin_id = $admin['id'] ?? 0;
|
||||||
$stockWarning->goods_sku_price_id = $goodsSkuPrice['id'];
|
$stockWarning->goods_sku_price_id = $goodsSkuPrice['id'];
|
||||||
$stockWarning->goods_sku_text = is_array($goodsSkuPrice['goods_sku_text']) ? join(',', $goodsSkuPrice['goods_sku_text']) : $goodsSkuPrice['goods_sku_text'];
|
$stockWarning->goods_sku_text = is_array($goodsSkuPrice['goods_sku_text']) ? join(',', $goodsSkuPrice['goods_sku_text']) : $goodsSkuPrice['goods_sku_text'];
|
||||||
$stockWarning->before = $before;
|
$stockWarning->before = $before;
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ class Coupon extends Common
|
|||||||
if (!$this->request->isAjax()) {
|
if (!$this->request->isAjax()) {
|
||||||
return $this->view->fetch();
|
return $this->view->fetch();
|
||||||
}
|
}
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
$coupons = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10))->each(function ($coupon) {
|
$coupons = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10))->each(function ($coupon) {
|
||||||
// 优惠券领取和使用数量
|
// 优惠券领取和使用数量
|
||||||
$coupon->get_num = $coupon->get_num;
|
$coupon->get_num = $coupon->get_num;
|
||||||
|
|||||||
@@ -40,7 +40,13 @@ trait SkuPrice
|
|||||||
protected function editSimSku($goods, $type = 'add')
|
protected function editSimSku($goods, $type = 'add')
|
||||||
{
|
{
|
||||||
$params = $this->request->only([
|
$params = $this->request->only([
|
||||||
'stock', 'stock_warning', 'sn', 'weight', 'cost_price', 'original_price', 'price'
|
'stock',
|
||||||
|
'stock_warning',
|
||||||
|
'sn',
|
||||||
|
'weight',
|
||||||
|
'cost_price',
|
||||||
|
'original_price',
|
||||||
|
'price'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
@@ -83,7 +89,36 @@ trait SkuPrice
|
|||||||
// 检测库存预警
|
// 检测库存预警
|
||||||
$this->checkStockWarning($skuPrice, $type);
|
$this->checkStockWarning($skuPrice, $type);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中羿体育项目报名费sku处理
|
||||||
|
private function zySku($goods, $type)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'goods_id' => $goods->id,
|
||||||
|
'stock' => 999,
|
||||||
|
'cost_price' => 0,
|
||||||
|
'original_price' => 0,
|
||||||
|
'price' => $goods['price'][0] ?? 0,
|
||||||
|
'original_price' => $goods['price'][0] ?? 0,
|
||||||
|
'stock_warning' => null
|
||||||
|
];
|
||||||
|
if ($type == 'edit') {
|
||||||
|
$skuPrice = SkuPriceModel::where('goods_id', $goods->id)->order('id', 'asc')->find();
|
||||||
|
if ($skuPrice) {
|
||||||
|
SkuPriceModel::where('goods_id', $goods->id)->where('id', '<>', $skuPrice->id)->delete();
|
||||||
|
SkuModel::where('goods_id', $goods->id)->delete();
|
||||||
|
}
|
||||||
|
unset($data['stock']);
|
||||||
|
}
|
||||||
|
if (!isset($skuPrice) || !$skuPrice) {
|
||||||
|
$skuPrice = new SkuPriceModel();
|
||||||
|
}
|
||||||
|
$skuPrice->save($data);
|
||||||
|
if ($type == 'add') {
|
||||||
|
$this->addStockLog($skuPrice, 0, $data['stock'], $type);
|
||||||
|
$this->checkStockWarning($skuPrice, $type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -97,7 +132,8 @@ trait SkuPrice
|
|||||||
protected function editMultSku($goods, $type = 'add')
|
protected function editMultSku($goods, $type = 'add')
|
||||||
{
|
{
|
||||||
$params = $this->request->only([
|
$params = $this->request->only([
|
||||||
'skus', 'sku_prices'
|
'skus',
|
||||||
|
'sku_prices'
|
||||||
]);
|
]);
|
||||||
$skus = $params['skus'] ?? [];
|
$skus = $params['skus'] ?? [];
|
||||||
$skuPrices = $params['sku_prices'] ?? [];
|
$skuPrices = $params['sku_prices'] ?? [];
|
||||||
|
|||||||
71
application/admin/controller/zy/Club.php
Normal file
71
application/admin/controller/zy/Club.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 俱乐部
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Club extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Club模型对象
|
||||||
|
* @var \app\admin\model\zy\Club
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\Club;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
application/admin/controller/zy/Menber.php
Normal file
72
application/admin/controller/zy/Menber.php
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 俱乐部成员
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Menber extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menber模型对象
|
||||||
|
* @var \app\admin\model\zy\Menber
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\Menber;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['club','user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('club')->visible(['name']);
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
168
application/admin/controller/zy/Stadium.php
Normal file
168
application/admin/controller/zy/Stadium.php
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
use Exception;
|
||||||
|
use think\Db;
|
||||||
|
use think\exception\DbException;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 球馆
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Stadium extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stadium模型对象
|
||||||
|
* @var \app\admin\model\zy\Stadium
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\Stadium;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['id', 'username', 'nickname', 'avatar', 'gender']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \think\Exception
|
||||||
|
*/
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
if (false === $this->request->isPost()) {
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
$params = $this->request->post('row/a');
|
||||||
|
if (empty($params)) {
|
||||||
|
$this->error(__('Parameter %s can not be empty', ''));
|
||||||
|
}
|
||||||
|
$params = $this->preExcludeFields($params);
|
||||||
|
$params['position'] = sprintf("POINT(%s %s)", $params['lng'], $params['lat']);
|
||||||
|
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
|
||||||
|
$params[$this->dataLimitField] = $this->auth->id;
|
||||||
|
}
|
||||||
|
$result = false;
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
//是否采用模型验证
|
||||||
|
if ($this->modelValidate) {
|
||||||
|
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
||||||
|
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
|
||||||
|
$this->model->validateFailException()->validate($validate);
|
||||||
|
}
|
||||||
|
$result = $this->model->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error(__('No rows were inserted'));
|
||||||
|
}
|
||||||
|
$this->success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑
|
||||||
|
*
|
||||||
|
* @param $ids
|
||||||
|
* @return string
|
||||||
|
* @throws DbException
|
||||||
|
* @throws \think\Exception
|
||||||
|
*/
|
||||||
|
public function edit($ids = null)
|
||||||
|
{
|
||||||
|
$row = $this->model->get($ids);
|
||||||
|
if (!$row) {
|
||||||
|
$this->error(__('No Results were found'));
|
||||||
|
}
|
||||||
|
$adminIds = $this->getDataLimitAdminIds();
|
||||||
|
if (is_array($adminIds) && !in_array($row[$this->dataLimitField], $adminIds)) {
|
||||||
|
$this->error(__('You have no permission'));
|
||||||
|
}
|
||||||
|
if (false === $this->request->isPost()) {
|
||||||
|
$point = empty($row['position']) ? [0, 0] : explode(' ', substr($row['position'], 6, -1));
|
||||||
|
$row['lng'] = $point[0];
|
||||||
|
$row['lat'] = $point[1];
|
||||||
|
$this->view->assign('row', $row);
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
$params = $this->request->post('row/a');
|
||||||
|
if (empty($params)) {
|
||||||
|
$this->error(__('Parameter %s can not be empty', ''));
|
||||||
|
}
|
||||||
|
$params = $this->preExcludeFields($params);
|
||||||
|
$params['position'] = sprintf("POINT(%s %s)", $params['lng'], $params['lat']);
|
||||||
|
$result = false;
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
//是否采用模型验证
|
||||||
|
if ($this->modelValidate) {
|
||||||
|
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
||||||
|
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
|
||||||
|
$row->validateFailException()->validate($validate);
|
||||||
|
}
|
||||||
|
$result = $row->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException | PDOException | Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if (false === $result) {
|
||||||
|
$this->error(__('No rows were updated'));
|
||||||
|
}
|
||||||
|
$this->success();
|
||||||
|
}
|
||||||
|
}
|
||||||
37
application/admin/controller/zy/Tags.php
Normal file
37
application/admin/controller/zy/Tags.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Tags extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tags模型对象
|
||||||
|
* @var \app\admin\model\zy\Tags
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\Tags;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
72
application/admin/controller/zy/circle/Circle.php
Normal file
72
application/admin/controller/zy/circle/Circle.php
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\circle;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 影圈
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Circle extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circle模型对象
|
||||||
|
* @var \app\admin\model\zy\circle\Circle
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\circle\Circle;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['club','user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('club')->visible(['name']);
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
application/admin/controller/zy/circle/Comment.php
Normal file
71
application/admin/controller/zy/circle/Comment.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\circle;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 影圈评论/回复
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Comment extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comment模型对象
|
||||||
|
* @var \app\admin\model\zy\circle\Comment
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\circle\Comment;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['circle'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('circle')->visible(['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
application/admin/controller/zy/circle/Likes.php
Normal file
72
application/admin/controller/zy/circle/Likes.php
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\circle;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 影圈点赞
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Likes extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Likes模型对象
|
||||||
|
* @var \app\admin\model\zy\circle\Likes
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\circle\Likes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['circle','user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('circle')->visible(['id']);
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
73
application/admin/controller/zy/game/Activity.php
Normal file
73
application/admin/controller/zy/game/Activity.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\game;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 球局
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Activity extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity模型对象
|
||||||
|
* @var \app\admin\model\zy\Activity
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\game\Activity;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['stadium','club','user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('stadium')->visible(['name']);
|
||||||
|
$row->getRelation('club')->visible(['name']);
|
||||||
|
$row->getRelation('user')->visible(['username']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
74
application/admin/controller/zy/game/Game.php
Normal file
74
application/admin/controller/zy/game/Game.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\game;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赛事
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Game extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game模型对象
|
||||||
|
* @var \app\admin\model\zy\Game
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\game\Game;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['activity','stadium','club','user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('activity')->visible(['name']);
|
||||||
|
$row->getRelation('stadium')->visible(['name']);
|
||||||
|
$row->getRelation('club')->visible(['name']);
|
||||||
|
$row->getRelation('user')->visible(['username']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
74
application/admin/controller/zy/game/GameJoin.php
Normal file
74
application/admin/controller/zy/game/GameJoin.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\game;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比赛报名
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class GameJoin extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GameJoin模型对象
|
||||||
|
* @var \app\admin\model\zy\GameJoin
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\game\GameJoin;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['activity','game','user','order'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('activity')->visible(['name']);
|
||||||
|
$row->getRelation('game')->visible(['name']);
|
||||||
|
$row->getRelation('user')->visible(['username']);
|
||||||
|
$row->getRelation('order')->visible(['order_sn']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
74
application/admin/controller/zy/game/GameMatch.php
Normal file
74
application/admin/controller/zy/game/GameMatch.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\game;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配对阵
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class GameMatch extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GameMatch模型对象
|
||||||
|
* @var \app\admin\model\zy\GameMatch
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\game\GameMatch;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['stadium','club','activity','game'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('stadium')->visible(['name']);
|
||||||
|
$row->getRelation('club')->visible(['name']);
|
||||||
|
$row->getRelation('activity')->visible(['name']);
|
||||||
|
$row->getRelation('game')->visible(['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
73
application/admin/controller/zy/game/Participant.php
Normal file
73
application/admin/controller/zy/game/Participant.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\game;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参加人员
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Participant extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Participant模型对象
|
||||||
|
* @var \app\admin\model\zy\Participant
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\game\Participant;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user','join','game'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['username']);
|
||||||
|
$row->getRelation('join')->visible(['id']);
|
||||||
|
$row->getRelation('game')->visible(['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
application/admin/controller/zy/link/Apply.php
Normal file
72
application/admin/controller/zy/link/Apply.php
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\link;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Apply extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply模型对象
|
||||||
|
* @var \app\admin\model\zy\link\Apply
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\link\Apply;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user','club'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
$row->getRelation('club')->visible(['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
application/admin/controller/zy/link/Complaint.php
Normal file
72
application/admin/controller/zy/link/Complaint.php
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\link;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 投诉举报
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Complaint extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complaint模型对象
|
||||||
|
* @var \app\admin\model\zy\link\Complaint
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\link\Complaint;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user','circle'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
$row->getRelation('circle')->visible(['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
application/admin/controller/zy/link/Message.php
Normal file
71
application/admin/controller/zy/link/Message.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\link;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息管理
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Message extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message模型对象
|
||||||
|
* @var \app\admin\model\zy\link\Message
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\link\Message;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
application/admin/controller/zy/link/Relation.php
Normal file
71
application/admin/controller/zy/link/Relation.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\link;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关系
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Relation extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relation模型对象
|
||||||
|
* @var \app\admin\model\zy\link\Relation
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\link\Relation;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
application/admin/controller/zy/link/Visitor.php
Normal file
71
application/admin/controller/zy/link/Visitor.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\link;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Visitor extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visitor模型对象
|
||||||
|
* @var \app\admin\model\zy\link\Visitor
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\link\Visitor;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['nickname']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
application/admin/controller/zy/sign/Record.php
Normal file
71
application/admin/controller/zy/sign/Record.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\sign;
|
||||||
|
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到记录
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class Record extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record模型对象
|
||||||
|
* @var \app\admin\model\zy\sign\Record
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\sign\Record;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['user'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('user')->visible(['username']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
154
application/admin/controller/zy/sign/SignSet.php
Normal file
154
application/admin/controller/zy/sign/SignSet.php
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\controller\zy\sign;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Exception;
|
||||||
|
use think\exception\PDOException;
|
||||||
|
use app\common\controller\Backend;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到设置
|
||||||
|
*
|
||||||
|
* @icon fa fa-circle-o
|
||||||
|
*/
|
||||||
|
class SignSet extends Backend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SignSet模型对象
|
||||||
|
* @var \app\admin\model\zy\sign\SignSet
|
||||||
|
*/
|
||||||
|
protected $model = null;
|
||||||
|
|
||||||
|
public function _initialize()
|
||||||
|
{
|
||||||
|
parent::_initialize();
|
||||||
|
$this->model = new \app\admin\model\zy\sign\SignSet;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||||
|
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||||
|
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//当前是否为关联查询
|
||||||
|
$this->relationSearch = true;
|
||||||
|
//设置过滤方法
|
||||||
|
$this->request->filter(['strip_tags', 'trim']);
|
||||||
|
if ($this->request->isAjax()) {
|
||||||
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||||
|
if ($this->request->request('keyField')) {
|
||||||
|
return $this->selectpage();
|
||||||
|
}
|
||||||
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||||
|
|
||||||
|
$list = $this->model
|
||||||
|
->with(['coupon1','coupon2'])
|
||||||
|
->where($where)
|
||||||
|
->order($sort, $order)
|
||||||
|
->paginate($limit);
|
||||||
|
|
||||||
|
foreach ($list as $row) {
|
||||||
|
|
||||||
|
$row->getRelation('coupon1')->visible(['name']);
|
||||||
|
$row->getRelation('coupon2')->visible(['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||||
|
|
||||||
|
return json($result);
|
||||||
|
}
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
if (false === $this->request->isPost()) {
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
$params = $this->request->post('row/a');
|
||||||
|
if (empty($params)) {
|
||||||
|
$this->error(__('Parameter %s can not be empty', ''));
|
||||||
|
}
|
||||||
|
$params = $this->preExcludeFields($params);
|
||||||
|
if (empty($params['begin_date'])) $params['begin_date'] = NULL;
|
||||||
|
if (empty($params['end_date'])) $params['end_date'] = NULL;
|
||||||
|
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
|
||||||
|
$params[$this->dataLimitField] = $this->auth->id;
|
||||||
|
}
|
||||||
|
$result = false;
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
//是否采用模型验证
|
||||||
|
if ($this->modelValidate) {
|
||||||
|
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
||||||
|
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
|
||||||
|
$this->model->validateFailException()->validate($validate);
|
||||||
|
}
|
||||||
|
$result = $this->model->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException|PDOException|Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
$this->error(__('No rows were inserted'));
|
||||||
|
}
|
||||||
|
$this->success();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit($ids = null)
|
||||||
|
{
|
||||||
|
$row = $this->model->get($ids);
|
||||||
|
if (!$row) {
|
||||||
|
$this->error(__('No Results were found'));
|
||||||
|
}
|
||||||
|
$adminIds = $this->getDataLimitAdminIds();
|
||||||
|
if (is_array($adminIds) && !in_array($row[$this->dataLimitField], $adminIds)) {
|
||||||
|
$this->error(__('You have no permission'));
|
||||||
|
}
|
||||||
|
if (false === $this->request->isPost()) {
|
||||||
|
$this->view->assign('row', $row);
|
||||||
|
return $this->view->fetch();
|
||||||
|
}
|
||||||
|
$params = $this->request->post('row/a');
|
||||||
|
if (empty($params)) {
|
||||||
|
$this->error(__('Parameter %s can not be empty', ''));
|
||||||
|
}
|
||||||
|
$params = $this->preExcludeFields($params);
|
||||||
|
if (empty($params['begin_date'])) $params['begin_date'] = NULL;
|
||||||
|
if (empty($params['end_date'])) $params['end_date'] = NULL;
|
||||||
|
$result = false;
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
//是否采用模型验证
|
||||||
|
if ($this->modelValidate) {
|
||||||
|
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
||||||
|
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
|
||||||
|
$row->validateFailException()->validate($validate);
|
||||||
|
}
|
||||||
|
$result = $row->allowField(true)->save($params);
|
||||||
|
Db::commit();
|
||||||
|
} catch (ValidateException|PDOException|Exception $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
if (false === $result) {
|
||||||
|
$this->error(__('No rows were updated'));
|
||||||
|
}
|
||||||
|
$this->success();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
26
application/admin/lang/zh-cn/zy/circle/circle.php
Normal file
26
application/admin/lang/zh-cn/zy/circle/circle.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Club_id' => '俱乐部',
|
||||||
|
'User_id' => '用户',
|
||||||
|
'Nickname' => '昵称',
|
||||||
|
'Avatar' => '头像',
|
||||||
|
'Gender' => '性别',
|
||||||
|
'Club_name' => '俱乐部名称',
|
||||||
|
'Content' => '内容',
|
||||||
|
'Imgs' => '图片',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'Club.name' => '俱乐部名称',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
|
||||||
|
|
||||||
|
'Gender1' => '男',
|
||||||
|
'Gender0' => '女',
|
||||||
|
|
||||||
|
'Status-1' => '审核不通过',
|
||||||
|
'Status0' => '待审核',
|
||||||
|
'Status1' => '审核通过',
|
||||||
|
|
||||||
|
];
|
||||||
17
application/admin/lang/zh-cn/zy/circle/comment.php
Normal file
17
application/admin/lang/zh-cn/zy/circle/comment.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Circle_id' => '影圈id',
|
||||||
|
'Pid' => '评论id',
|
||||||
|
'User_id' => '用户',
|
||||||
|
'Nickname' => '昵称',
|
||||||
|
'Avatar' => '用户头像',
|
||||||
|
'Gender' => '性别',
|
||||||
|
'Content' => '评论或回复内容',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
|
||||||
|
'Male' => '男',
|
||||||
|
'Female' => '女',
|
||||||
|
];
|
||||||
15
application/admin/lang/zh-cn/zy/circle/likes.php
Normal file
15
application/admin/lang/zh-cn/zy/circle/likes.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Circle_id' => '影圈id',
|
||||||
|
'User_id' => '用户',
|
||||||
|
'Nickname' => '昵称',
|
||||||
|
'Avatar' => '用户头像',
|
||||||
|
'Gender' => '性别',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
|
||||||
|
'Male' => '男',
|
||||||
|
'Female' => '女',
|
||||||
|
];
|
||||||
29
application/admin/lang/zh-cn/zy/club.php
Normal file
29
application/admin/lang/zh-cn/zy/club.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Gym_id' => '球馆',
|
||||||
|
'Name' => '俱乐部名称',
|
||||||
|
'Sub_name' => '简称',
|
||||||
|
'Logo' => 'logo',
|
||||||
|
'Contect' => '联系方式',
|
||||||
|
'City' => '常住地',
|
||||||
|
'Blurb' => '简介',
|
||||||
|
'Intro' => '详细介绍',
|
||||||
|
'Join_type' => '入会方式',
|
||||||
|
'Img' => '图片',
|
||||||
|
'President' => '会长',
|
||||||
|
'Admin_ids' => '管理员',
|
||||||
|
'Tags' => '标签',
|
||||||
|
'attention' => '关注数',
|
||||||
|
'Is_public' => '是否公开',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
|
||||||
|
'Join_type0' => '开放加入,无需审核',
|
||||||
|
'Join_type1' => '开放申请,审核加入',
|
||||||
|
'Join_type2' => '会员邀请,无需审核',
|
||||||
|
'Join_type3' => '会员邀请,审核加入',
|
||||||
|
'Join_type4' => '仅管理员邀请,无需审核',
|
||||||
|
'Is_public0' => '否',
|
||||||
|
'Is_public1' => '是',
|
||||||
|
];
|
||||||
82
application/admin/lang/zh-cn/zy/game/activity.php
Normal file
82
application/admin/lang/zh-cn/zy/game/activity.php
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Pid' => '所属活动',
|
||||||
|
'Gym_id' => '球馆',
|
||||||
|
'Club_id' => '俱乐部',
|
||||||
|
'Name' => '活动名称',
|
||||||
|
'Address' => '地点',
|
||||||
|
'Field' => '场地号',
|
||||||
|
'Is_public' => '是否公开',
|
||||||
|
'Team_type' => '队伍类型',
|
||||||
|
'Rule_type' => '规则类型',
|
||||||
|
'Type' => '周期类型',
|
||||||
|
'Week' => '每周几',
|
||||||
|
'Start_time' => '开始时间',
|
||||||
|
'End_time' => '结束时间',
|
||||||
|
'Public_time' => '公布时间',
|
||||||
|
'Join_start_time' => '报名开始时间',
|
||||||
|
'Join_end_time' => '报名截止时间',
|
||||||
|
'Quit_time' => '免费退坑时间',
|
||||||
|
'Game_time' => '比赛时长(时)',
|
||||||
|
'Position' => '经纬度',
|
||||||
|
'Cost' => '报名费用',
|
||||||
|
'Limit_num' => '报名限制人数',
|
||||||
|
'Describe' => '活动介绍',
|
||||||
|
'Img' => '图片',
|
||||||
|
'Game_rule' => '比赛规则',
|
||||||
|
'Is_bring' => '可否带人',
|
||||||
|
'Bring_num' => '可带人数',
|
||||||
|
'Referee' => '裁判',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'Stadium.name' => '球馆名称',
|
||||||
|
'Club.name' => '俱乐部名称',
|
||||||
|
'User.username' => '用户名',
|
||||||
|
|
||||||
|
'Is_public0' => '否',
|
||||||
|
'Is_public1' => '是',
|
||||||
|
|
||||||
|
'Is_bring0' => '否',
|
||||||
|
'Is_bring1' => '是',
|
||||||
|
|
||||||
|
'Team_type1' => '双打',
|
||||||
|
'Team_type2' => '单打',
|
||||||
|
'Team_type3' => '团队',
|
||||||
|
|
||||||
|
'Rule_type1' => '八人转',
|
||||||
|
'Rule_type2' => '超八转',
|
||||||
|
'Rule_type3' => '混双转',
|
||||||
|
'Rule_type4' => '固搭转',
|
||||||
|
'Rule_type5' => '固定擂',
|
||||||
|
'Rule_type6' => '活动擂',
|
||||||
|
'Rule_type7' => '转转',
|
||||||
|
'Rule_type8' => '分区转',
|
||||||
|
'Rule_type9' => '擂台赛',
|
||||||
|
'Rule_type10' => '守擂赛',
|
||||||
|
'Rule_type11' => '追分赛',
|
||||||
|
'Rule_type12' => '固搭追分赛',
|
||||||
|
'Rule_type13' => '大循环群内赛',
|
||||||
|
'Rule_type14' => '两队PK赛',
|
||||||
|
'Rule_type15' => '战队淘汰赛',
|
||||||
|
'Rule_type16' => '单项淘汰赛',
|
||||||
|
'Rule_type17' => '分区循环淘汰赛',
|
||||||
|
|
||||||
|
'Type0' => '一次性',
|
||||||
|
'Type1' => '周期性',
|
||||||
|
|
||||||
|
'Status0' => '发布',
|
||||||
|
'Status1' => '报名中',
|
||||||
|
'Status2' => '比赛中',
|
||||||
|
'Status3' => '结束',
|
||||||
|
|
||||||
|
'Week0' => '周日',
|
||||||
|
'Week1' => '周一',
|
||||||
|
'Week2' => '周二',
|
||||||
|
'Week3' => '周三',
|
||||||
|
'Week4' => '周四',
|
||||||
|
'Week5' => '周五',
|
||||||
|
'Week6' => '周六',
|
||||||
|
|
||||||
|
];
|
||||||
90
application/admin/lang/zh-cn/zy/game/game.php
Normal file
90
application/admin/lang/zh-cn/zy/game/game.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Pid' => '主活动比赛',
|
||||||
|
'Act_id' => '球局活动',
|
||||||
|
'Gym_id' => '球馆',
|
||||||
|
'Club_id' => '俱乐部',
|
||||||
|
'Name' => '赛事名称',
|
||||||
|
'Team_type' => '队伍类型',
|
||||||
|
'Rule_type' => '规则类型',
|
||||||
|
'Date' => '比赛日期',
|
||||||
|
'Week' => '星期几',
|
||||||
|
'Start_time' => '开始时间',
|
||||||
|
'End_time' => '结束时间',
|
||||||
|
'Public_time' => '发布时间',
|
||||||
|
'Join_start_time' => '报名开始时间',
|
||||||
|
'Join_end_time' => '报名截止时间',
|
||||||
|
'Quit_time' => '免费退坑时间',
|
||||||
|
'Game_time' => '比赛时长(时)',
|
||||||
|
'Address' => '地点',
|
||||||
|
'Field' => '场地号',
|
||||||
|
'Position' => '经纬度',
|
||||||
|
'Cost' => '报名费用',
|
||||||
|
'Limit_num' => '报名限制人数',
|
||||||
|
'Join_num' => '报名人数',
|
||||||
|
'Attention' => '关注人数',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Describe' => '比赛介绍',
|
||||||
|
'Img' => '图片',
|
||||||
|
'Game_rule' => '比赛规则',
|
||||||
|
'Is_public' => '是否公开',
|
||||||
|
'Bring_num' => '可带人数',
|
||||||
|
'Is_bring' => '可否带人',
|
||||||
|
'Referee' => '裁判',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'Activity.name' => '名称',
|
||||||
|
'Stadium.name' => '球馆名称',
|
||||||
|
'Club.name' => '俱乐部名称',
|
||||||
|
'User.username' => '用户名',
|
||||||
|
|
||||||
|
|
||||||
|
'Is_public0' => '否',
|
||||||
|
'Is_public1' => '是',
|
||||||
|
|
||||||
|
'Is_bring0' => '否',
|
||||||
|
'Is_bring1' => '是',
|
||||||
|
|
||||||
|
'Team_type1' => '双打',
|
||||||
|
'Team_type2' => '单打',
|
||||||
|
'Team_type3' => '团队',
|
||||||
|
|
||||||
|
'Rule_type1' => '八人转',
|
||||||
|
'Rule_type2' => '超八转',
|
||||||
|
'Rule_type3' => '混双转',
|
||||||
|
'Rule_type4' => '固搭转',
|
||||||
|
'Rule_type5' => '固定擂',
|
||||||
|
'Rule_type6' => '活动擂',
|
||||||
|
'Rule_type7' => '转转',
|
||||||
|
'Rule_type8' => '分区转',
|
||||||
|
'Rule_type9' => '擂台赛',
|
||||||
|
'Rule_type10' => '守擂赛',
|
||||||
|
'Rule_type11' => '追分赛',
|
||||||
|
'Rule_type12' => '固搭追分赛',
|
||||||
|
'Rule_type13' => '大循环群内赛',
|
||||||
|
'Rule_type14' => '两队PK赛',
|
||||||
|
'Rule_type15' => '战队淘汰赛',
|
||||||
|
'Rule_type16' => '单项淘汰赛',
|
||||||
|
'Rule_type17' => '分区循环淘汰赛',
|
||||||
|
|
||||||
|
|
||||||
|
'Type0' => '一次性',
|
||||||
|
'Type1' => '周期性',
|
||||||
|
|
||||||
|
'Status-1' => '取消',
|
||||||
|
'Status0' => '未开始',
|
||||||
|
'Status1' => '报名中',
|
||||||
|
'Status2' => '进行中',
|
||||||
|
'Status3' => '已结束',
|
||||||
|
|
||||||
|
'Week0' => '周日',
|
||||||
|
'Week1' => '周一',
|
||||||
|
'Week2' => '周二',
|
||||||
|
'Week3' => '周三',
|
||||||
|
'Week4' => '周四',
|
||||||
|
'Week5' => '周五',
|
||||||
|
'Week6' => '周六',
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
28
application/admin/lang/zh-cn/zy/game/game_join.php
Normal file
28
application/admin/lang/zh-cn/zy/game/game_join.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Act_id' => '活动id',
|
||||||
|
'Game_id' => '赛事id',
|
||||||
|
'User_id' => '用户id',
|
||||||
|
'Order_id' => '订单id',
|
||||||
|
'Users' => '参与用户',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'Activity.name' => '活动名称',
|
||||||
|
'Game.name' => '赛事名称',
|
||||||
|
'User.username' => '用户名',
|
||||||
|
'Order.order_sn' => '订单号',
|
||||||
|
|
||||||
|
'Status-1' => '取消',
|
||||||
|
'Status0' => '待支付',
|
||||||
|
'Status1' => '已支付',
|
||||||
|
|
||||||
|
'Cost10' => '报名时收取',
|
||||||
|
'Cost21' => '活动后收取:固定',
|
||||||
|
'Cost22' => '活动后收取:AA',
|
||||||
|
'Cost23' => '活动后收取:女固定男AA',
|
||||||
|
'Cost24' => '活动后收取:AA,男比女高X元',
|
||||||
|
'Cost25' => '活动后收取:AA,男比女高X%',
|
||||||
|
'Cost30' => '多退少补',
|
||||||
|
];
|
||||||
25
application/admin/lang/zh-cn/zy/game/game_match.php
Normal file
25
application/admin/lang/zh-cn/zy/game/game_match.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Gym_id' => '球馆',
|
||||||
|
'Club_id' => '俱乐部',
|
||||||
|
'Act_id' => '活动',
|
||||||
|
'Game_id' => '比赛',
|
||||||
|
'Level' => '轮次',
|
||||||
|
'Turn' => '场次',
|
||||||
|
'TeamA' => '甲方队员',
|
||||||
|
'TeamB' => '乙方队员',
|
||||||
|
'Round1' => '第一局比分
|
||||||
|
',
|
||||||
|
'Round2' => '第二局比分',
|
||||||
|
'Round3' => '第三局比分',
|
||||||
|
'ScoreA' => '甲方总得分',
|
||||||
|
'ScoreB' => '乙方总得分',
|
||||||
|
'Winner' => '胜队',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'Stadium.name' => '球馆名称',
|
||||||
|
'Club.name' => '俱乐部名称',
|
||||||
|
'Activity.name' => '活动名称',
|
||||||
|
'Game.name' => '赛事名称'
|
||||||
|
];
|
||||||
30
application/admin/lang/zh-cn/zy/game/participant.php
Normal file
30
application/admin/lang/zh-cn/zy/game/participant.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'User_id' => '用户id',
|
||||||
|
'Name' => '用户名',
|
||||||
|
'Avatar' => '头像',
|
||||||
|
'Gender' => '性别',
|
||||||
|
'Game_join_id' => '报名id',
|
||||||
|
'Game_id' => '赛事id',
|
||||||
|
'Team' => '战队',
|
||||||
|
'Order' => '排序',
|
||||||
|
'Rank' => '排名',
|
||||||
|
'Mark' => '说明/备注',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Signin' => '签到状态',
|
||||||
|
'Score' => '得分',
|
||||||
|
'Net_score' => '净得分',
|
||||||
|
'Win' => '胜局',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'User.username' => '用户名',
|
||||||
|
'Game.name' => '赛事名称',
|
||||||
|
|
||||||
|
'Status-1' => '退坑',
|
||||||
|
'Status0' => '候补',
|
||||||
|
'Status1' => '正常',
|
||||||
|
|
||||||
|
'Signin0' => '未签到',
|
||||||
|
'Signin1' => '已签到',
|
||||||
|
];
|
||||||
23
application/admin/lang/zh-cn/zy/link/apply.php
Normal file
23
application/admin/lang/zh-cn/zy/link/apply.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Type' => '申请类型',
|
||||||
|
'User_id' => '用户id',
|
||||||
|
'Target_id' => '目标id',
|
||||||
|
'Content' => '内容',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Reason' => '申请理由',
|
||||||
|
'Reply' => '审核理由',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
'Club.name' => '俱乐部名称',
|
||||||
|
|
||||||
|
'Type1' => '入会申请',
|
||||||
|
'Type2' => '好友申请',
|
||||||
|
'Type3' => '报名邀请',
|
||||||
|
|
||||||
|
'Status-1' => '拒绝',
|
||||||
|
'Status1' => '发起',
|
||||||
|
'Status2' => '通过'
|
||||||
|
];
|
||||||
23
application/admin/lang/zh-cn/zy/link/complaint.php
Normal file
23
application/admin/lang/zh-cn/zy/link/complaint.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Type' => '投诉类型',
|
||||||
|
'Target_id' => '投诉对象',
|
||||||
|
'Name' => '投诉名称',
|
||||||
|
'User_id' => '用户',
|
||||||
|
'Username' => '称呼',
|
||||||
|
'Phone' => '手机号',
|
||||||
|
'Imgs' => '图片',
|
||||||
|
'Content' => '投诉内容',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
|
||||||
|
'Type1' => '系统投诉',
|
||||||
|
'Type2' => '影圈举报',
|
||||||
|
'Type3' => '用户举报',
|
||||||
|
|
||||||
|
'Status0' => '待处理',
|
||||||
|
'Status1' => '已处理'
|
||||||
|
];
|
||||||
20
application/admin/lang/zh-cn/zy/link/message.php
Normal file
20
application/admin/lang/zh-cn/zy/link/message.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Type' => '消息类型',
|
||||||
|
'From_id' => '来源id',
|
||||||
|
'User_id' => '目标用户',
|
||||||
|
'Content' => '消息内容',
|
||||||
|
'Status' => '状态',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
|
||||||
|
'Type0' => '系统消息',
|
||||||
|
'Type1' => '通知消息',
|
||||||
|
'Type2' => '用户消息',
|
||||||
|
'Type3' => '俱乐部消息',
|
||||||
|
|
||||||
|
'Status0' => '未读',
|
||||||
|
'Status1' => '已读'
|
||||||
|
];
|
||||||
21
application/admin/lang/zh-cn/zy/link/relation.php
Normal file
21
application/admin/lang/zh-cn/zy/link/relation.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'User_id' => '用户id',
|
||||||
|
'Target_id' => '目标id',
|
||||||
|
'Status' => '关系',
|
||||||
|
'Content' => '开放权限',
|
||||||
|
'Remark' => '备注名称',
|
||||||
|
'Look' => '让看',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
|
||||||
|
'Status-1' => '拉黑',
|
||||||
|
'Status0' => '取关',
|
||||||
|
'Status1' => '关注',
|
||||||
|
'Status2' => '特别关注',
|
||||||
|
|
||||||
|
'Look0' => '不让看',
|
||||||
|
'Look1' => '让看'
|
||||||
|
];
|
||||||
21
application/admin/lang/zh-cn/zy/link/visitor.php
Normal file
21
application/admin/lang/zh-cn/zy/link/visitor.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Type' => '访问类型',
|
||||||
|
'Obj_id' => '访问对象id',
|
||||||
|
'User_id' => '用户id',
|
||||||
|
'Nickname' => '昵称',
|
||||||
|
'Avatar' => '头像',
|
||||||
|
'Gender' => '性别',
|
||||||
|
'Times' => '次数',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
|
||||||
|
'Type0' => '俱乐部',
|
||||||
|
'Type1' => '球馆',
|
||||||
|
'Type2' => '用户',
|
||||||
|
|
||||||
|
'Gender0' => '女',
|
||||||
|
'Gender1' => '男',
|
||||||
|
];
|
||||||
23
application/admin/lang/zh-cn/zy/menber.php
Normal file
23
application/admin/lang/zh-cn/zy/menber.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Club_id' => '俱乐部',
|
||||||
|
'User_id' => '用户',
|
||||||
|
'Role' => '角色',
|
||||||
|
'Remark' => '备注',
|
||||||
|
'Activity' => '组球次数',
|
||||||
|
'Game' => '比赛次数',
|
||||||
|
'Plane' => '飞机次数',
|
||||||
|
'Tags' => '标签',
|
||||||
|
'Create_time' => '创建时间',
|
||||||
|
'Update_time' => '修改时间',
|
||||||
|
'Club.name' => '俱乐部名称',
|
||||||
|
'User.nickname' => '昵称',
|
||||||
|
|
||||||
|
'Role0' => '退出',
|
||||||
|
'Role1' => '成员',
|
||||||
|
'Role2' => '管理员',
|
||||||
|
'Role3' => '会长',
|
||||||
|
'Role-1' => '黑名单'
|
||||||
|
|
||||||
|
];
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user