diff --git a/app/admin/controller/AppMarketController.php b/app/admin/controller/AppMarketController.php index 99f96c59..ef2bc04a 100644 --- a/app/admin/controller/AppMarketController.php +++ b/app/admin/controller/AppMarketController.php @@ -137,7 +137,7 @@ public function install(){ $dir = WEB_ROOT.$result['data']['app']['uuid'].'.zip'; - $content = curl_download($this->market_url."/console/v1/app_market/market/download?id={$id}&from=".request()->domain().'/'.DIR_ADMIN.'&token='.$token.'&time='.time(), $dir); + $content = $this->curl_download($this->market_url."/console/v1/app_market/market/download?id={$id}&from=".request()->domain().'/'.DIR_ADMIN.'&token='.$token.'&time='.time(), $dir); if($content){ @@ -163,7 +163,7 @@ public function install(){ } }else{ $dir = WEB_ROOT."plugins/".$result['data']['app']['type'].'/'.$result['data']['app']['uuid'].'.zip'; - $content = curl_download($this->market_url."/console/v1/app_market/market/download?id={$id}&from=".request()->domain().'/'.DIR_ADMIN.'&token='.$token.'&time='.time(), $dir); + $content = $this->curl_download($this->market_url."/console/v1/app_market/market/download?id={$id}&from=".request()->domain().'/'.DIR_ADMIN.'&token='.$token.'&time='.time(), $dir); if($content){ $file = WEB_ROOT."plugins/".$result['data']['app']['type']; @@ -204,4 +204,25 @@ private function unzip($filepath,$path) return ['status' => 400 , 'msg' => $res]; } } + + /* + * curl下载解压包到指定路径 + */ + private function curl_download($url, $file_name) + { + $ch = curl_init($url); + //设置抓取的url + $dir = $file_name; + $fp = fopen($dir, "wb"); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_HEADER, 0); + + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $res=curl_exec($ch); + curl_close($ch); + fclose($fp); + + return $res; + } } \ No newline at end of file diff --git a/app/admin/controller/ConfigurationController.php b/app/admin/controller/ConfigurationController.php index 6380a30a..e916ee61 100755 --- a/app/admin/controller/ConfigurationController.php +++ b/app/admin/controller/ConfigurationController.php @@ -530,5 +530,69 @@ public function certificationUpdate() return json($result); } + + /** + * 时间 2023-02-28 + * @title 获取信息配置 + * @desc 获取信息配置 + * @url /admin/v1/configuration/info + * @method GET + * @author theworld + * @version v1 + * @return string put_on_record - 备案信息 + * @return string enterprise_name - 企业名称 + * @return string enterprise_telephone - 企业电话 + * @return string enterprise_mailbox - 企业邮箱 + * @return string enterprise_qrcode - 企业二维码 + * @return string online_customer_service_link - 在线客服链接 + */ + public function infoList() + { + //实例化模型类 + $ConfigurationModel = new ConfigurationModel(); + + //获取信息配置 + $data = $ConfigurationModel->infoList(); + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data, + ]; + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 保存信息配置 + * @desc 保存信息配置 + * @url /admin/v1/configuration/info + * @method PUT + * @author theworld + * @version v1 + * @param string put_on_record - 备案信息 required + * @param string enterprise_name - 企业名称 required + * @param string enterprise_telephone - 企业电话 required + * @param string enterprise_mailbox - 企业邮箱 required + * @param string enterprise_qrcode - 企业二维码 required + * @param string online_customer_service_link - 在线客服链接 required + */ + public function infoUpdate() + { + //接收参数 + $param = $this->request->param(); + + //参数验证 + if (!$this->validate->scene('info_update')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + //实例化模型类 + $ConfigurationModel = new ConfigurationModel(); + + //保存信息配置 + $result = $ConfigurationModel->infoUpdate($param); + + return json($result); + } } diff --git a/app/admin/controller/ConsultController.php b/app/admin/controller/ConsultController.php new file mode 100644 index 00000000..f46766a0 --- /dev/null +++ b/app/admin/controller/ConsultController.php @@ -0,0 +1,55 @@ +request->param(), ['page' => $this->request->page, 'limit' => $this->request->limit, 'sort' => $this->request->sort]); + + // 实例化模型类 + $ConsultModel = new ConsultModel(); + + // 获取方案咨询列表 + $data = $ConsultModel->consultList($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } +} \ No newline at end of file diff --git a/app/admin/controller/FeedbackController.php b/app/admin/controller/FeedbackController.php new file mode 100644 index 00000000..ecac83cd --- /dev/null +++ b/app/admin/controller/FeedbackController.php @@ -0,0 +1,180 @@ +validate = new FeedbackTypeValidate(); + } + + /** + * 时间 2023-02-28 + * @title 意见反馈列表 + * @desc 意见反馈列表 + * @author theworld + * @version v1 + * @url /admin/v1/feedback + * @method GET + * @param int page - 页数 + * @param int limit - 每页条数 + * @param string orderby - 排序 id + * @param string sort - 升/降序 asc,desc + * @return array list - 意见反馈 + * @return int list[].id - 意见反馈ID + * @return string list[].title - 标题 + * @return string list[].type - 类型 + * @return string list[].description - 描述 + * @return int list[].client_id - 用户ID + * @return string list[].username - 用户名 + * @return string list[].contact - 联系方式 + * @return array list[].attachment - 附件 + * @return int list[].create_time - 反馈时间 + * @return int count - 意见反馈总数 + */ + public function feedbackList() + { + // 合并分页参数 + $param = array_merge($this->request->param(), ['page' => $this->request->page, 'limit' => $this->request->limit, 'sort' => $this->request->sort]); + + // 实例化模型类 + $FeedbackModel = new FeedbackModel(); + + // 获取意见反馈列表 + $data = $FeedbackModel->feedbackList($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 获取意见反馈类型 + * @desc 获取意见反馈类型 + * @author theworld + * @version v1 + * @url /admin/v1/feedback/type + * @method GET + * @return array list - 意见反馈类型 + * @return int list[].id - 意见反馈类型ID + * @return string list[].name - 名称 + * @return string list[].description - 描述 + */ + public function feedbackTypeList() + { + // 实例化模型类 + $FeedbackTypeModel = new FeedbackTypeModel(); + + // 获取意见反馈类型 + $data = $FeedbackTypeModel->feedbackTypeList(); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 添加意见反馈类型 + * @desc 添加意见反馈类型 + * @author theworld + * @version v1 + * @url /admin/v1/feedback/type + * @method POST + * @param string name - 名称 required + * @param string description - 描述 required + */ + public function createFeedbackType() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $FeedbackTypeModel = new FeedbackTypeModel(); + + // 新建意见反馈类型 + $result = $FeedbackTypeModel->createFeedbackType($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 编辑意见反馈类型 + * @desc 编辑意见反馈类型 + * @author theworld + * @version v1 + * @url /admin/v1/feedback/type/:id + * @method PUT + * @param int id - 意见反馈类型ID required + * @param string name - 名称 required + * @param string description - 描述 required + */ + public function updateFeedbackType() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('update')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $FeedbackTypeModel = new FeedbackTypeModel(); + + // 修改意见反馈类型 + $result = $FeedbackTypeModel->updateFeedbackType($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 删除意见反馈类型 + * @desc 删除意见反馈类型 + * @author theworld + * @version v1 + * @url /admin/v1/feedback/type/:id + * @method DELETE + * @param int id - 意见反馈类型ID required + */ + public function deleteFeedbackType() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $FeedbackTypeModel = new FeedbackTypeModel(); + + // 删除意见反馈类型 + $result = $FeedbackTypeModel->deleteFeedbackType($param['id']); + + return json($result); + + } + + +} \ No newline at end of file diff --git a/app/admin/controller/FriendlyLinkController.php b/app/admin/controller/FriendlyLinkController.php new file mode 100644 index 00000000..c8a1c191 --- /dev/null +++ b/app/admin/controller/FriendlyLinkController.php @@ -0,0 +1,136 @@ +validate = new FriendlyLinkValidate(); + } + + /** + * 时间 2023-02-28 + * @title 获取友情链接 + * @desc 获取友情链接 + * @author theworld + * @version v1 + * @url /admin/v1/friendly_link + * @method GET + * @return array list - 友情链接 + * @return int list[].id - 友情链接ID + * @return string list[].name - 名称 + * @return string list[].url - 链接地址 + */ + public function list() + { + // 实例化模型类 + $FriendlyLinkModel = new FriendlyLinkModel(); + + // 获取友情链接 + $data = $FriendlyLinkModel->friendlyLinkList(); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 添加友情链接 + * @desc 添加友情链接 + * @author theworld + * @version v1 + * @url /admin/v1/friendly_link + * @method POST + * @param string name - 名称 required + * @param string url - 链接地址 required + */ + public function create() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $FriendlyLinkModel = new FriendlyLinkModel(); + + // 新建友情链接 + $result = $FriendlyLinkModel->createFriendlyLink($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 编辑友情链接 + * @desc 编辑友情链接 + * @author theworld + * @version v1 + * @url /admin/v1/friendly_link/:id + * @method PUT + * @param int id - 友情链接ID required + * @param string name - 名称 required + * @param string url - 链接地址 required + */ + public function update() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('update')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $FriendlyLinkModel = new FriendlyLinkModel(); + + // 修改友情链接 + $result = $FriendlyLinkModel->updateFriendlyLink($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 删除友情链接 + * @desc 删除友情链接 + * @author theworld + * @version v1 + * @url /admin/v1/friendly_link/:id + * @method DELETE + * @param int id - 友情链接ID required + */ + public function delete() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $FriendlyLinkModel = new FriendlyLinkModel(); + + // 删除友情链接 + $result = $FriendlyLinkModel->deleteFriendlyLink($param['id']); + + return json($result); + + } + + +} \ No newline at end of file diff --git a/app/admin/controller/HonorController.php b/app/admin/controller/HonorController.php new file mode 100644 index 00000000..a830f56a --- /dev/null +++ b/app/admin/controller/HonorController.php @@ -0,0 +1,136 @@ +validate = new HonorValidate(); + } + + /** + * 时间 2023-02-28 + * @title 获取荣誉资质 + * @desc 获取荣誉资质 + * @author theworld + * @version v1 + * @url /admin/v1/honor + * @method GET + * @return array list - 荣誉资质 + * @return int list[].id - 荣誉资质ID + * @return string list[].name - 名称 + * @return string list[].img - 图片地址 + */ + public function list() + { + // 实例化模型类 + $HonorModel = new HonorModel(); + + // 获取荣誉资质 + $data = $HonorModel->honorList(); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 添加荣誉资质 + * @desc 添加荣誉资质 + * @author theworld + * @version v1 + * @url /admin/v1/honor + * @method POST + * @param string name - 名称 required + * @param string img - 图片地址 required + */ + public function create() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $HonorModel = new HonorModel(); + + // 新建荣誉资质 + $result = $HonorModel->createHonor($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 编辑荣誉资质 + * @desc 编辑荣誉资质 + * @author theworld + * @version v1 + * @url /admin/v1/honor/:id + * @method PUT + * @param int id - 荣誉资质ID required + * @param string name - 名称 required + * @param string img - 图片地址 required + */ + public function update() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('update')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $HonorModel = new HonorModel(); + + // 修改荣誉资质 + $result = $HonorModel->updateHonor($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 删除荣誉资质 + * @desc 删除荣誉资质 + * @author theworld + * @version v1 + * @url /admin/v1/honor/:id + * @method DELETE + * @param int id - 荣誉资质ID required + */ + public function delete() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $HonorModel = new HonorModel(); + + // 删除荣誉资质 + $result = $HonorModel->deleteHonor($param['id']); + + return json($result); + + } + + +} \ No newline at end of file diff --git a/app/admin/controller/HostController.php b/app/admin/controller/HostController.php index 3fca3d04..e38a38a1 100755 --- a/app/admin/controller/HostController.php +++ b/app/admin/controller/HostController.php @@ -96,6 +96,7 @@ public function hostList() * @return string host.suspend_reason - 暂停原因 * @return array status - 状态Unpaid未付款Pending开通中Active已开通Suspended已暂停Deleted已删除Failed开通失败 * @return string host.product_name - 商品名称 + * @return string host.agent - 代理产品0否1是 */ public function index() { diff --git a/app/admin/controller/MenuController.php b/app/admin/controller/MenuController.php index 9322692f..55d31e23 100755 --- a/app/admin/controller/MenuController.php +++ b/app/admin/controller/MenuController.php @@ -122,6 +122,9 @@ public function getAdminMenu() * @return array module - 模块 * @return string module[].name - 模块名称 * @return string module[].display_name - 模块显示名称 + * @return array res_module - 上游模块 + * @return string res_module[].name - 上游模块名称 + * @return string res_module[].display_name - 上游模块显示名称 */ public function getHomeMenu() { @@ -189,23 +192,23 @@ public function saveAdminMenu() * @url /admin/v1/menu/home * @method PUT * @param array menu - 菜单 required - * @param string menu[].type - 菜单类型system系统plugin插件custom自定义module模块 required + * @param string menu[].type - 菜单类型system系统plugin插件custom自定义module模块res_module上游模块 required * @param string menu[].name - 名称 required * @param object menu[].language - 语言 required * @param string menu[].url - 网址 菜单类型为自定义时需要传递 * @param string menu[].icon - 图标 * @param int menu[].nav_id - 导航ID 菜单类型为系统或插件时需要传递 - * @param string menu[].module - 模块类型 菜单类型为模块时需要传递 - * @param array menu[].product_id - 商品ID 菜单类型为模块时需要传递 + * @param string menu[].module - 模块类型 菜单类型为模块或上游模块时需要传递 + * @param array menu[].product_id - 商品ID 菜单类型为模块或上游模块时需要传递 * @param array menu[].child - 子菜单 required - * @param string menu[].child[].type - 菜单类型system系统plugin插件custom自定义module模块 required + * @param string menu[].child[].type - 菜单类型system系统plugin插件custom自定义module模块res_module上游模块 required * @param string menu[].child[].name - 名称 required * @param object menu[].child[].language - 语言 required * @param string menu[].child[].url - 网址 菜单类型为自定义时需要传递 * @param string menu[].child[].icon - 图标 * @param int menu[].child[].nav_id - 导航ID 菜单类型为系统或插件时需要传递 - * @param string menu[].child[].module - 模块类型 菜单类型为模块时需要传递 - * @param array menu[].child[].product_id - 商品ID 菜单类型为模块时需要传递 + * @param string menu[].child[].module - 模块类型 菜单类型为模块或上游模块时需要传递 + * @param array menu[].child[].product_id - 商品ID 菜单类型为模块或上游模块时需要传递 */ public function saveHomeMenu() { diff --git a/app/admin/controller/OrderController.php b/app/admin/controller/OrderController.php index 364c219e..f8a6269b 100755 --- a/app/admin/controller/OrderController.php +++ b/app/admin/controller/OrderController.php @@ -18,6 +18,21 @@ public function initialize() $this->validate = new OrderValidate(); } + public function test() + { + // 实例化模型类 + $OrderModel = new OrderModel(); + + // 获取订单列表 + $data = $OrderModel->test(); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + ]; + return json($result); + } + /** * 时间 2022-05-17 * @title 订单列表 @@ -50,7 +65,6 @@ public function initialize() * @return string list[].phone - 手机号 * @return string list[].company - 公司 * @return string list[].host_name - 产品标识 - * @return string list[].billing_cycle - 计费周期 * @return array list[].product_names - 订单下所有产品的商品名称 * @return int list[].host_id 产品ID * @return int list[].order_item_count - 订单子项数量 @@ -108,6 +122,8 @@ public function orderList() * @return string order.items[].billing_cycle - 计费周期 * @return string order.items[].host_status - 产品状态Unpaid未付款Pending开通中Active使用中Suspended暂停Deleted删除Failed开通失败 * @return int order.items[].edit - 是否可编辑1是0否 + * @return string order.items[].profit - 利润 + * @return int order.items[].agent - 代理订单1是0否 */ public function index() { diff --git a/app/admin/controller/PartnerController.php b/app/admin/controller/PartnerController.php new file mode 100644 index 00000000..8c7a570c --- /dev/null +++ b/app/admin/controller/PartnerController.php @@ -0,0 +1,139 @@ +validate = new PartnerValidate(); + } + + /** + * 时间 2023-02-28 + * @title 获取合作伙伴 + * @desc 获取合作伙伴 + * @author theworld + * @version v1 + * @url /admin/v1/partner + * @method GET + * @return array list - 合作伙伴 + * @return int list[].id - 合作伙伴ID + * @return string list[].name - 名称 + * @return string list[].img - 图片地址 + * @return string list[].description - 描述 + */ + public function list() + { + // 实例化模型类 + $PartnerModel = new PartnerModel(); + + // 获取合作伙伴 + $data = $PartnerModel->partnerList(); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 添加合作伙伴 + * @desc 添加合作伙伴 + * @author theworld + * @version v1 + * @url /admin/v1/partner + * @method POST + * @param string name - 名称 required + * @param string img - 图片地址 required + * @param string description - 描述 required + */ + public function create() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $PartnerModel = new PartnerModel(); + + // 新建合作伙伴 + $result = $PartnerModel->createPartner($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 编辑合作伙伴 + * @desc 编辑合作伙伴 + * @author theworld + * @version v1 + * @url /admin/v1/partner/:id + * @method PUT + * @param int id - 合作伙伴ID required + * @param string name - 名称 required + * @param string img - 图片地址 required + * @param string description - 描述 required + */ + public function update() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('update')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $PartnerModel = new PartnerModel(); + + // 修改合作伙伴 + $result = $PartnerModel->updatePartner($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 删除合作伙伴 + * @desc 删除合作伙伴 + * @author theworld + * @version v1 + * @url /admin/v1/partner/:id + * @method DELETE + * @param int id - 合作伙伴ID required + */ + public function delete() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $PartnerModel = new PartnerModel(); + + // 删除合作伙伴 + $result = $PartnerModel->deletePartner($param['id']); + + return json($result); + + } + + +} \ No newline at end of file diff --git a/app/admin/controller/ProductController.php b/app/admin/controller/ProductController.php index 9f371356..897aff92 100755 --- a/app/admin/controller/ProductController.php +++ b/app/admin/controller/ProductController.php @@ -42,6 +42,8 @@ public function initialize() * @return int list[].product_group_id_second - 二级分组ID * @return int list[].product_group_name_first - 一级分组名称 * @return int list[].product_group_id_first - 一级分组ID + * @return int list[].agentable - 是否可代理商品0否1是 + * @return int list[].agent - 代理商品0否1是 * @return int count - 商品总数 */ public function productList() @@ -455,5 +457,56 @@ public function productListSearch() return json($result); } + /** + * 时间 2023-02-20 + * @title 保存可代理商品 + * @desc 保存可代理商品 + * @url /admin/v1/product/agentable + * @method PUT + * @author theworld + * @version v1 + * @param array param.id - 商品ID require + */ + public function saveAgentableProduct() + { + $param = $this->request->param(); + + $ProductModel = new ProductModel(); + $result = $ProductModel->saveAgentableProduct($param); + + return json($result); + } + + /** + * 时间 2023-03-01 + * @title 根据上游模块获取商品列表 + * @desc 根据上游模块获取商品列表 + * @url /admin/v1/res_module/:module/product + * @method GET + * @author theworld + * @version v1 + * @param string module - 模块名称 + * @return array list - 一级分组列表 + * @return int list[].id - 一级分组ID + * @return string list[].name - 一级分组名称 + * @return array list[].child - 二级分组 + * @return int list[].child[].id - 二级分组ID + * @return string list[].child[].name - 二级分组名称 + * @return array list[].child[].child - 商品 + * @return int list[].child[].child[].id - 商品ID + * @return string list[].child[].child[].name - 商品名称 + */ + public function resModuleProductList() + { + $param = $this->request->param(); + + $result = [ + 'status'=>200, + 'msg'=>lang('success_message'), + 'data' =>(new ProductModel())->resModuleProductList($param) + ]; + return json($result); + } + } diff --git a/app/admin/controller/PublicController.php b/app/admin/controller/PublicController.php index 933a1432..36b41e06 100755 --- a/app/admin/controller/PublicController.php +++ b/app/admin/controller/PublicController.php @@ -3,7 +3,6 @@ use app\admin\model\AdminModel; use app\admin\validate\AdminValidate; -use app\common\model\ClientModel; /** * @title 后台开放类 @@ -98,101 +97,4 @@ public function captcha() return json($result); } - public function test() - { - $arr = [500,1,42,34,5,7,235,645,654,6455,62,253,25,2,2453]; - var_dump($this->bubbleSort($arr));die; - $array = array(1,1);foreach ($array as $k=>$v){ $v = 2;} - var_dump($array); - $a = new ClientModel(); - var_dump(11);die; - - $array = [ - 1 => [ - 'id' => 10, - 'name' => 'wuyuhua' - ], - 2 => [ - 'id' => 20, - 'name' => 'test' - ], - 3 => [ - 'id' => 30, - 'name' => 'teaast' - ], - ]; - - $array2 = [ - 'wuyuhua' => 'aldfjklad', - 'test' => 'aldfjkasdf', - 'teaast' => 'adsfhasjkdf' - ]; - - $result = array_walk_recursive($array,function(&$value,$key,$other){ - $value = $key . ':' . $value . '-' . ($other[$value]??''); - return $value; - },$array2); - var_dump($array,$result);die; - - var_dump(111);die; - $IdcsmartCommonProductConfigoptionModel = new \server\zjmfapp\model\IdcsmartCommonProductConfigoptionModel(); - - $IdcsmartCommonProductConfigoptionSubModel = new \server\zjmfapp\model\IdcsmartCommonProductConfigoptionSubModel(); - - $IdcsmartCommonCustomCycleModel = new \server\zjmfapp\model\IdcsmartCommonCustomCycleModel(); - - $IdcsmartCommonCustomCyclePricingModel = new \server\zjmfapp\model\IdcsmartCommonCustomCyclePricingModel(); - - $ProductModel = new \app\common\model\ProductModel(); - - $products = $ProductModel->select()->toArray(); - - $ProductModel->startTrans(); - - try{ - foreach ($products as $product){ - $productId = $product['id']; - $customCycles = $IdcsmartCommonCustomCycleModel->alias('cc') - ->field('cc.id,cc.name,cc.cycle_time,cc.cycle_unit,ccp.amount,ccp.id as pricing_id') - ->leftJoin('module_zjmfapp_custom_cycle_pricing ccp','ccp.custom_cycle_id=cc.id AND ccp.type=\'product\'') - ->where('cc.product_id',$productId) - ->where('ccp.rel_id',$productId) - ->select() - ->toArray(); - $configoptions = $IdcsmartCommonProductConfigoptionModel->where('product_id',$productId)->select()->toArray(); - - foreach ($customCycles as $customCycle){ - $totalPrice = $customCycle['amount']??0; - foreach ($configoptions as $configoption){ - $subs = $IdcsmartCommonProductConfigoptionSubModel->where('product_configoption_id',$configoption['id'])->select()->toArray(); - foreach ($subs as $sub){ - $subAmount = $IdcsmartCommonCustomCyclePricingModel->where('custom_cycle_id',$customCycle['id']) - ->where('rel_id',$sub['id']) - ->where('type','configoption') - ->value('amount'); - $totalPrice += $subAmount??0; - } - } - if ($totalPrice<=0){ - $IdcsmartCommonCustomCycleModel->where('id',$customCycle['id'])->delete(); - $IdcsmartCommonCustomCyclePricingModel->where('custom_cycle_id',$customCycle['id'])->delete(); - } - } - } - - $ProductModel->commit(); - }catch (\Exception $e){ - $ProductModel->rollback(); - return json([ - 'status' => 400, - 'msg' => $e->getMessage() - ]); - } - - return json([ - 'status' => 200, - 'msg' => lang_plugins('success_message') - ]); - } - } \ No newline at end of file diff --git a/app/admin/controller/SupplierController.php b/app/admin/controller/SupplierController.php new file mode 100644 index 00000000..03775f49 --- /dev/null +++ b/app/admin/controller/SupplierController.php @@ -0,0 +1,257 @@ +validate = new SupplierValidate(); + } + + /** + * 时间 2023-02-13 + * @title 供应商列表 + * @desc 供应商列表 + * @author theworld + * @version v1 + * @url /admin/v1/supplier + * @method GET + * @param string keywords - 关键字,搜索范围:供应商名称,链接地址 + * @param int page - 页数 + * @param int limit - 每页条数 + * @param string orderby - 排序 id + * @param string sort - 升/降序 asc,desc + * @return array list - 供应商 + * @return int list[].id - 供应商ID + * @return string list[].name - 供应商名称 + * @return string list[].url - 链接地址 + * @return int list[].host_num - 产品数量 + * @return int list[].product_num - 商品数量 + * @return int count - 供应商总数 + */ + public function list() + { + // 合并分页参数 + $param = array_merge($this->request->param(), ['page' => $this->request->page, 'limit' => $this->request->limit, 'sort' => $this->request->sort]); + + // 实例化模型类 + $SupplierModel = new SupplierModel(); + + // 获取供应商列表 + $data = $SupplierModel->supplierList($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 供应商详情 + * @desc 供应商详情 + * @author theworld + * @version v1 + * @url /admin/v1/supplier/:id + * @method GET + * @param int id - 供应商ID required + * @return object supplier - 供应商 + * @return int supplier.id - 供应商ID + * @return string supplier.name - 名称 + * @return string supplier.url - 链接地址 + * @return string supplier.username - 用户名 + * @return string supplier.token - API密钥 + * @return string supplier.secret - API私钥 + * @return string supplier.contact - 联系方式 + * @return string supplier.notes - 备注 + */ + public function index() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $SupplierModel = new SupplierModel(); + + // 获取供应商 + $supplier = $SupplierModel->indexSupplier($param['id']); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => [ + 'supplier' => $supplier + ] + ]; + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 添加供应商 + * @desc 添加供应商 + * @author theworld + * @version v1 + * @url /admin/v1/supplier + * @method POST + * @param string name - 名称 required + * @param string url - 链接地址 required + * @param string username - 用户名 required + * @param string token - API密钥 required + * @param string secret - API私钥 required + * @param string contact - 联系方式 + * @param string notes - 备注 + */ + public function create() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $SupplierModel = new SupplierModel(); + + // 新建供应商 + $result = $SupplierModel->createSupplier($param); + + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 编辑供应商 + * @desc 编辑供应商 + * @author theworld + * @version v1 + * @url /admin/v1/supplier/:id + * @method PUT + * @param int id - 供应商ID required + * @param string name - 名称 required + * @param string url - 链接地址 required + * @param string username - 用户名 required + * @param string token - API密钥 required + * @param string secret - API私钥 required + * @param string contact - 联系方式 + * @param string notes - 备注 + */ + public function update() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('update')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $SupplierModel = new SupplierModel(); + + // 修改供应商 + $result = $SupplierModel->updateSupplier($param); + + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 删除供应商 + * @desc 删除供应商 + * @author theworld + * @version v1 + * @url /admin/v1/supplier/:id + * @method DELETE + * @param int id - 供应商ID required + */ + public function delete() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $SupplierModel = new SupplierModel(); + + // 删除供应商 + $result = $SupplierModel->deleteSupplier($param['id']); + + return json($result); + + } + + /** + * 时间 2023-02-13 + * @title 检查供应商接口连接状态 + * @desc 检查供应商接口连接状态 + * @author theworld + * @version v1 + * @url /admin/v1/supplier/:id/status + * @method GET + * @param int id - 供应商ID required + */ + public function status() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $SupplierModel = new SupplierModel(); + + // 检查供应商接口连接状态 + $result = $SupplierModel->supplierStatus($param['id']); + + return json($result); + + } + + /** + * 时间 2023-02-13 + * @title 获取供应商商品列表 + * @desc 获取供应商商品列表 + * @author theworld + * @version v1 + * @url /admin/v1/supplier/:id/product + * @method GET + * @param int id - 供应商ID required + * @return array list - 商品列表 + * @return int list[].id - 商品ID + * @return string list[].name - 商品名 + * @return string list[].description - 描述 + * @return string list[].price - 商品最低价格 + * @return string list[].cycle - 商品最低周期 + */ + public function product() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $SupplierModel = new SupplierModel(); + + // 获取供应商商品列表 + $data = $SupplierModel->supplierProduct($param['id']); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + + } + +} \ No newline at end of file diff --git a/app/admin/controller/UpstreamHostController.php b/app/admin/controller/UpstreamHostController.php new file mode 100644 index 00000000..6613984b --- /dev/null +++ b/app/admin/controller/UpstreamHostController.php @@ -0,0 +1,104 @@ +request->param(), ['page' => $this->request->page, 'limit' => $this->request->limit, 'sort' => $this->request->sort]); + + // 实例化模型类 + $UpstreamHostModel = new UpstreamHostModel(); + + // 获取上游产品列表 + $data = $UpstreamHostModel->hostList($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 产品详情 + * @desc 产品详情 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/host/:id + * @method GET + * @param int id - 产品ID required + * @return object host - 产品 + * @return int host.id - 产品ID + * @return int host.upstream_host_id - 上游产品ID + * @return string host.first_payment_amount - 订购金额 + * @return string host.renew_amount - 续费金额 + * @return string host.billing_cycle - 计费周期 + * @return string host.billing_cycle_name - 模块计费周期名称 + * @return string host.billing_cycle_time - 模块计费周期时间 + * @return int host.active_time - 开通时间 + * @return int host.due_time - 到期时间 + * @return string host.status - 状态Unpaid未付款Pending开通中Active已开通Suspended已暂停Deleted已删除Failed开通失败 + */ + public function index() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $UpstreamHostModel = new UpstreamHostModel(); + + // 获取产品 + $host = $UpstreamHostModel->indexHost($param['id']); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => [ + 'host' => $host, + ] + ]; + return json($result); + } +} \ No newline at end of file diff --git a/app/admin/controller/UpstreamOrderController.php b/app/admin/controller/UpstreamOrderController.php new file mode 100644 index 00000000..0f2c97f3 --- /dev/null +++ b/app/admin/controller/UpstreamOrderController.php @@ -0,0 +1,99 @@ +request->param(), ['page' => $this->request->page, 'limit' => $this->request->limit, 'sort' => $this->request->sort]); + + // 实例化模型类 + $UpstreamOrderModel = new UpstreamOrderModel(); + + // 获取上游订单列表 + $data = $UpstreamOrderModel->orderList($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 销售信息 + * @desc 销售信息 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/sell_info + * @method GET + * @param int supplier_id - 供应商ID + * @return string total - 总销售额 + * @return string profit - 总利润 + * @return int product_count - 商品总数 + * @return int host_count - 产品总数 + */ + public function sellInfo() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $UpstreamOrderModel = new UpstreamOrderModel(); + + // 获取销售信息 + $data = $UpstreamOrderModel->sellInfo($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } +} \ No newline at end of file diff --git a/app/admin/controller/UpstreamProductController.php b/app/admin/controller/UpstreamProductController.php new file mode 100644 index 00000000..4cb9025e --- /dev/null +++ b/app/admin/controller/UpstreamProductController.php @@ -0,0 +1,293 @@ +validate = new UpstreamProductValidate(); + } + + /** + * 时间 2023-02-13 + * @title 商品列表 + * @desc 商品列表 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/product + * @method GET + * @param string keywords - 关键字,搜索范围:商品名称 + * @param int supplier_id - 供应商ID + * @param int page - 页数 + * @param int limit - 每页条数 + * @param string orderby - 排序 id + * @param string sort - 升/降序 asc,desc + * @return array list - 商品 + * @return int list[].id - 商品ID + * @return string list[].name - 商品名称 + * @return string list[].description - 商品描述 + * @return int list[].supplier_id - 供应商ID + * @return string list[].supplier_name - 供应商名称 + * @return string list[].profit_percent - 利润百分比 + * @return int list[].auto_setup - 是否自动开通:1是,0否 + * @return int list[].hidden - 0显示,1隐藏 + * @return string list[].pay_type - 付款类型,免费free,一次onetime,周期先付recurring_prepayment,周期后付recurring_postpaid + * @return string list[].price - 商品最低价格 + * @return string list[].cycle - 商品最低周期 + * @return int list[].upstream_product_id - 上游商品ID + * @return int list[].certification - 本地实名购买0关闭,1开启 + * @return string list[].product_group_name_second - 二级分组名称 + * @return int list[].product_group_id_second - 二级分组ID + * @return string list[].product_group_name_first - 一级分组名称 + * @return int list[].product_group_id_first - 一级分组ID + * @return int count - 商品总数 + */ + public function list() + { + // 合并分页参数 + $param = array_merge($this->request->param(), ['page' => $this->request->page, 'limit' => $this->request->limit, 'sort' => $this->request->sort]); + + // 实例化模型类 + $UpstreamProductModel = new UpstreamProductModel(); + + // 获取上游商品列表 + $data = $UpstreamProductModel->productList($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 商品详情 + * @desc 商品详情 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/product/:id + * @method GET + * @param int id - 商品ID required + * @return object product - 商品 + * @return int product.id - 商品ID + * @return string product.name - 商品名称 + * @return string product.description - 商品描述 + * @return int product.supplier_id - 供应商ID + * @return string product.supplier_name - 供应商名称 + * @return string product.profit_percent - 利润百分比 + * @return int product.auto_setup - 是否自动开通:1是,0否 + * @return int product.hidden - 0显示,1隐藏 + * @return string product.pay_type - 付款类型,免费free,一次onetime,周期先付recurring_prepayment,周期后付recurring_postpaid + * @return string product.price - 商品最低价格 + * @return string product.cycle - 商品最低周期 + * @return int product.upstream_product_id - 上游商品ID + * @return int product.certification - 本地实名购买0关闭,1开启 + * @return string product.product_group_name_second - 二级分组名称 + * @return int product.product_group_id_second - 二级分组ID + * @return string product.product_group_name_first - 一级分组名称 + * @return int product.product_group_id_first - 一级分组ID + */ + public function index() + { + // 接收参数 + $param = $this->request->param(); + + // 实例化模型类 + $UpstreamProductModel = new UpstreamProductModel(); + + // 获取商品 + $product = $UpstreamProductModel->indexProduct($param['id']); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => [ + 'product' => $product, + ] + ]; + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 添加商品 + * @desc 添加商品 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/product + * @method POST + * @param int supplier_id - 供应商ID required + * @param int upstream_product_id - 上游商品ID required + * @param string name - 商品名称 required + * @param string description - 商品描述 + * @param float profit_percent - 利润百分比 required + * @param int auto_setup - 是否自动开通:1是,0否 required + * @param int certification - 本地实名购买0关闭,1开启 required + * @param int product_group_id - 二级分组ID required + */ + public function create() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $UpstreamProductModel = new UpstreamProductModel(); + + // 新建商品 + $result = $UpstreamProductModel->createProduct($param); + + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 编辑商品 + * @desc 编辑商品 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/product/:id + * @method PUT + * @param int id - 商品ID required + * @param int supplier_id - 供应商ID required + * @param int upstream_product_id - 上游商品ID required + * @param string name - 商品名称 required + * @param string description - 商品描述 + * @param float profit_percent - 利润百分比 required + * @param int auto_setup - 是否自动开通:1是,0否 required + * @param int certification - 本地实名购买0关闭,1开启 required + * @param int product_group_id - 二级分组ID required + */ + public function update() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('update')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $UpstreamProductModel = new UpstreamProductModel(); + + // 修改商品 + $result = $UpstreamProductModel->updateProduct($param); + + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 推荐代理商品列表 + * @desc 推荐代理商品列表 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/recommend/product + * @method GET + * @param string keywords - 关键字,搜索范围:商品名称 + * @param int page - 页数 + * @param int limit - 每页条数 + * @param string orderby - 排序 id + * @param string sort - 升/降序 asc,desc + * @return array list - 推荐商品 + * @return int list[].id - 推荐商品ID + * @return int list[].upstream_product_id - 上游商品ID + * @return string list[].name - 商品名称 + * @return string list[].supplier_name - 供应商名称 + * @return string list[].login_url - 前台网站地址 + * @return string list[].url - 接口地址 + * @return string list[].price - 商品最低价格 + * @return string list[].cycle - 商品最低周期 + * @return int list[].cpu_min - CPU(核)最小值 + * @return int list[].cpu_max - CPU(核)最大值 + * @return int list[].memory_min - 内存(GB)最小值 + * @return int list[].memory_max - 内存(GB)最大值 + * @return int list[].disk_min - 硬盘(GB)最小值 + * @return int list[].disk_max - 硬盘(GB)最大值 + * @return int list[].bandwidth_min - 带宽(Mbps)最小值 + * @return int list[].bandwidth_max - 带宽(Mbps)最大值 + * @return int list[].flow_min - 流量(G)最小值 + * @return int list[].flow_max - 流量(G)最大值 + * @return string list[].description - 简介 + * @return int list[].agent - 是否已代理0否1是 + * @return object list[].supplier - 供应商,已添加时有数据 + * @return object list[].supplier.id - 供应商ID + * @return object list[].supplier.username - 上游账户名 + * @return object list[].supplier.token - API密钥 + * @return object list[].supplier.secret - API私钥 + * @return int count - 推荐商品总数 + */ + public function recommendProductList() + { + // 合并分页参数 + $param = array_merge($this->request->param(), ['page' => $this->request->page, 'limit' => $this->request->limit, 'sort' => $this->request->sort]); + + // 实例化模型类 + $UpstreamLogic = new UpstreamLogic(); + + // 获取推荐代理商品列表 + $data = $UpstreamLogic->recommendProductList($param); + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => $data + ]; + return json($result); + } + + /** + * 时间 2023-02-13 + * @title 代理推荐商品 + * @desc 代理推荐商品 + * @author theworld + * @version v1 + * @url /admin/v1/upstream/recommend/product + * @method POST + * @param int id - 推荐代理商品ID required + * @param string username - 用户名 required + * @param string token - API密钥 required + * @param string secret - API私钥 required + * @param string name - 商品名称 required + * @param string description - 商品描述 + * @param float profit_percent - 利润百分比 required + * @param int auto_setup - 是否自动开通:1是,0否 required + * @param int certification - 本地实名购买0关闭,1开启 required + * @param int product_group_id - 二级分组ID required + */ + public function agentRecommendProduct() + { + // 接收参数 + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('agent')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $UpstreamProductModel = new UpstreamProductModel(); + + // 代理推荐商品 + $result = $UpstreamProductModel->agentRecommendProduct($param); + + return json($result); + } +} \ No newline at end of file diff --git a/app/admin/validate/ConfigurationValidate.php b/app/admin/validate/ConfigurationValidate.php index 183b2ff1..b47557b5 100755 --- a/app/admin/validate/ConfigurationValidate.php +++ b/app/admin/validate/ConfigurationValidate.php @@ -79,6 +79,14 @@ class ConfigurationValidate extends Validate 'certification_update_client_phone' => 'require|in:0,1', 'certification_uncertified_suspended_host' => 'require|in:0,1', + # 实名设置 + 'put_on_record' => 'require|max:255', + 'enterprise_name' => 'require|max:255', + 'enterprise_telephone' => 'require|max:50', + 'enterprise_mailbox' => 'require|max:255', + 'enterprise_qrcode' => 'require', + 'online_customer_service_link' => 'require', + ]; protected $message = [ @@ -185,6 +193,18 @@ class ConfigurationValidate extends Validate 'certification_upload.require' => 'configuration_certification_upload_require', 'certification_update_client_phone.require' => 'configuration_certification_update_client_phone_require', 'certification_uncertified_suspended_host.require' => 'configuration_certification_uncertified_suspended_host_require', + + # 实名设置 + 'put_on_record.require' => 'put_on_record_require', + 'put_on_record.max' => 'put_on_record_max', + 'enterprise_name.require' => 'enterprise_name_require', + 'enterprise_name.max' => 'enterprise_name_max', + 'enterprise_telephone.require' => 'enterprise_telephone_require', + 'enterprise_telephone.max' => 'enterprise_telephone_max', + 'enterprise_mailbox.require' => 'enterprise_mailbox_require', + 'enterprise_mailbox.max' => 'enterprise_mailbox_max', + 'enterprise_qrcode.require' => 'enterprise_qrcode_require', + 'online_customer_service_link.require' => 'online_customer_service_link_require', ]; protected $scene = [ 'system_update' => ['lang_admin','lang_home_open','lang_home','maintenance_mode','website_name','website_url','terms_service_url','terms_privacy_url'], @@ -224,5 +244,13 @@ class ConfigurationValidate extends Validate 'certification_uncertified_suspended_host', 'certification_upload' ], + 'info_update' => [ + 'put_on_record', + 'enterprise_name', + 'enterprise_telephone', + 'enterprise_mailbox', + 'enterprise_qrcode', + 'online_customer_service_link', + ], ]; } \ No newline at end of file diff --git a/app/admin/validate/FeedbackTypeValidate.php b/app/admin/validate/FeedbackTypeValidate.php new file mode 100644 index 00000000..06131436 --- /dev/null +++ b/app/admin/validate/FeedbackTypeValidate.php @@ -0,0 +1,31 @@ + 'require|integer|gt:0', + 'name' => 'require|max:255', + 'description' => 'require', + ]; + + protected $message = [ + 'id.require' => 'id_error', + 'id.integer' => 'id_error', + 'id.gt' => 'id_error', + 'name.require' => 'please_enter_feedback_type_name', + 'name.max' => 'feedback_type_name_cannot_exceed_255_chars', + 'description.require' => 'please_enter_feedback_type_description', + ]; + + protected $scene = [ + 'create' => ['name', 'description'], + 'update' => ['id', 'name', 'description'], + ]; + +} \ No newline at end of file diff --git a/app/admin/validate/FriendlyLinkValidate.php b/app/admin/validate/FriendlyLinkValidate.php new file mode 100644 index 00000000..5919059b --- /dev/null +++ b/app/admin/validate/FriendlyLinkValidate.php @@ -0,0 +1,33 @@ + 'require|integer|gt:0', + 'name' => 'require|max:100', + 'url' => 'require|max:255|url' + ]; + + protected $message = [ + 'id.require' => 'id_error', + 'id.integer' => 'id_error', + 'id.gt' => 'id_error', + 'name.require' => 'please_enter_friendly_link_name', + 'name.max' => 'friendly_link_name_cannot_exceed_100_chars', + 'url.require' => 'please_enter_friendly_link_url', + 'url.max' => 'friendly_link_url_cannot_exceed_255_chars', + 'url.url' => 'friendly_link_url_error', + ]; + + protected $scene = [ + 'create' => ['name', 'url'], + 'update' => ['id', 'name', 'url'], + ]; + +} \ No newline at end of file diff --git a/app/admin/validate/HonorValidate.php b/app/admin/validate/HonorValidate.php new file mode 100644 index 00000000..25e91e5a --- /dev/null +++ b/app/admin/validate/HonorValidate.php @@ -0,0 +1,31 @@ + 'require|integer|gt:0', + 'name' => 'require|max:100', + 'img' => 'require' + ]; + + protected $message = [ + 'id.require' => 'id_error', + 'id.integer' => 'id_error', + 'id.gt' => 'id_error', + 'name.require' => 'please_enter_honor_name', + 'name.max' => 'honor_name_cannot_exceed_100_chars', + 'img.require' => 'please_select_honor_image', + ]; + + protected $scene = [ + 'create' => ['name', 'img'], + 'update' => ['id', 'name', 'img'], + ]; + +} \ No newline at end of file diff --git a/app/admin/validate/MenuValidate.php b/app/admin/validate/MenuValidate.php index b23ec6c8..7bb1a060 100755 --- a/app/admin/validate/MenuValidate.php +++ b/app/admin/validate/MenuValidate.php @@ -134,7 +134,7 @@ public function checkHomeMenu($value) if(!isset($v['type'])){ return false; } - if(!in_array($v['type'], ['system', 'plugin', 'custom', 'module'])){ + if(!in_array($v['type'], ['system', 'plugin', 'custom', 'module', 'res_module'])){ return false; } if(!isset($v['name'])){ @@ -159,7 +159,7 @@ public function checkHomeMenu($value) if(strlen($v['url'])>255){ return false; } - }else if($v['type']=='module'){ + }else if(in_array($v['type'], ['module', 'res_module'])){ if(!isset($v['module'])){ return false; } @@ -202,7 +202,7 @@ public function checkHomeMenu($value) if(!isset($cv['type'])){ return false; } - if(!in_array($cv['type'], ['system', 'plugin', 'custom', 'module'])){ + if(!in_array($cv['type'], ['system', 'plugin', 'custom', 'module', 'res_module'])){ return false; } if(!isset($cv['name'])){ @@ -227,7 +227,7 @@ public function checkHomeMenu($value) if(strlen($cv['url'])>255){ return false; } - }else if($cv['type']=='module'){ + }else if(in_array($cv['type'], ['module', 'res_module'])){ if(!isset($cv['module'])){ return false; } diff --git a/app/admin/validate/PartnerValidate.php b/app/admin/validate/PartnerValidate.php new file mode 100644 index 00000000..1ab7c0ab --- /dev/null +++ b/app/admin/validate/PartnerValidate.php @@ -0,0 +1,33 @@ + 'require|integer|gt:0', + 'name' => 'require|max:100', + 'img' => 'require', + 'description' => 'require', + ]; + + protected $message = [ + 'id.require' => 'id_error', + 'id.integer' => 'id_error', + 'id.gt' => 'id_error', + 'name.require' => 'please_enter_partner_name', + 'name.max' => 'partner_name_cannot_exceed_100_chars', + 'img.require' => 'please_select_partner_image', + 'description.require' => 'please_enter_partner_description', + ]; + + protected $scene = [ + 'create' => ['name', 'img', 'description'], + 'update' => ['id', 'name', 'img', 'description'], + ]; + +} \ No newline at end of file diff --git a/app/admin/validate/SupplierValidate.php b/app/admin/validate/SupplierValidate.php new file mode 100644 index 00000000..f5ecf0c1 --- /dev/null +++ b/app/admin/validate/SupplierValidate.php @@ -0,0 +1,44 @@ + 'require|integer|gt:0', + 'name' => 'require|max:50', + 'url' => 'require|max:255|url', + 'username' => 'require|max:100', + 'token' => 'require|max:200', + 'secret' => 'require', + 'contact' => 'max:1000', + 'notes' => 'max:1000', + ]; + + protected $message = [ + 'id.require' => 'id_error', + 'id.integer' => 'id_error', + 'id.gt' => 'id_error', + 'name.require' => 'please_enter_supplier_name', + 'name.max' => 'supplier_name_cannot_exceed_50_chars', + 'url.require' => 'please_enter_supplier_url', + 'url.max' => 'supplier_url_cannot_exceed_255_chars', + 'url.url' => 'supplier_url_error', + 'username.require' => 'please_enter_supplier_username', + 'username.max' => 'supplier_username_cannot_exceed_100_chars', + 'token.require' => 'please_enter_supplier_token', + 'token.max' => 'supplier_token_cannot_exceed_200_chars', + 'secret.require' => 'please_enter_supplier_secret', + 'contact.max' => 'supplier_contact_cannot_exceed_1000_chars', + 'notes.max' => 'supplier_notes_cannot_exceed_1000_chars', + ]; + + protected $scene = [ + 'create' => ['name', 'url', 'username', 'token', 'secret', 'contact', 'notes'], + 'update' => ['id', 'name', 'url', 'username', 'token', 'secret', 'contact', 'notes'], + ]; +} \ No newline at end of file diff --git a/app/admin/validate/UpstreamProductValidate.php b/app/admin/validate/UpstreamProductValidate.php new file mode 100644 index 00000000..7b2f22aa --- /dev/null +++ b/app/admin/validate/UpstreamProductValidate.php @@ -0,0 +1,59 @@ + 'require|integer|gt:0', + 'supplier_id' => 'require|integer|gt:0', + 'upstream_product_id' => 'require|integer|gt:0', + 'name' => 'require|max:50', + 'profit_percent' => 'require|float|gt:0', + 'auto_setup' => 'require|in:0,1', + 'certification' => 'require|in:0,1', + 'product_group_id' => 'require|integer|gt:0', + 'username' => 'require|max:100', + 'token' => 'require|max:200', + 'secret' => 'require', + ]; + + protected $message = [ + 'id.require' => 'id_error', + 'id.integer' => 'id_error', + 'id.gt' => 'id_error', + 'supplier_id.require' => 'supplier_id_error', + 'supplier_id.integer' => 'supplier_id_error', + 'supplier_id.gt' => 'supplier_id_error', + 'upstream_product_id.require' => 'upstream_product_id_error', + 'upstream_product_id.integer' => 'upstream_product_id_error', + 'upstream_product_id.gt' => 'upstream_product_id_error', + 'name.require' => 'please_enter_upstream_product_name', + 'name.max' => 'upstream_product_name_cannot_exceed_50_chars', + 'profit_percent.require' => 'please_enter_upstream_product_profit_percent', + 'profit_percent.float' => 'upstream_product_profit_percent_error', + 'profit_percent.gt' => 'upstream_product_profit_percent_error', + 'auto_setup.require' => 'param_error', + 'auto_setup.in' => 'param_error', + 'certification.require' => 'param_error', + 'certification.in' => 'param_error', + 'product_group_id.require' => 'product_group_id_error', + 'product_group_id.integer' => 'product_group_id_error', + 'product_group_id.gt' => 'product_group_id_error', + 'username.require' => 'please_enter_supplier_username', + 'username.max' => 'supplier_username_cannot_exceed_100_chars', + 'token.require' => 'please_enter_supplier_token', + 'token.max' => 'supplier_token_cannot_exceed_200_chars', + 'secret.require' => 'please_enter_supplier_secret', + ]; + + protected $scene = [ + 'create' => ['supplier_id', 'upstream_product_id', 'name', 'profit_percent', 'auto_setup', 'certification', 'product_group_id'], + 'update' => ['id', 'supplier_id', 'upstream_product_id', 'name', 'profit_percent', 'auto_setup', 'certification', 'product_group_id'], + 'agent' => ['id', 'username', 'token', 'secret', 'name', 'profit_percent', 'auto_setup', 'certification', 'product_group_id'], + ]; +} \ No newline at end of file diff --git a/app/api/controller/AuthController.php b/app/api/controller/AuthController.php new file mode 100644 index 00000000..f994ca12 --- /dev/null +++ b/app/api/controller/AuthController.php @@ -0,0 +1,46 @@ +param(); + + $validate = new \think\Validate([ + 'username' => 'require|length:4,20', + 'password' => 'require' + ]); + $validate->message([ + 'username.require' => '用户不能为空', + 'username.length' => '用户名4-20位', + 'password.require' => '密码不能为空', + ]); + if (!$validate->check($param)) { + return json(['status' => 400, 'msg' => '鉴权失败']); + } + + $ClientModel = new ClientModel(); + + $result = $ClientModel->apiAuth($param); + + return json($result); + } +} \ No newline at end of file diff --git a/app/api/controller/HostController.php b/app/api/controller/HostController.php new file mode 100644 index 00000000..310e115b --- /dev/null +++ b/app/api/controller/HostController.php @@ -0,0 +1,18 @@ +field('id,name,description,pay_type,price,cycle') + ->where('hidden',0) + ->where('agentable',1) + ->order('id','desc') + ->order('order','asc') + ->select() + ->toArray(); + idcsmart_cache('product:list',json_encode($list),30*24*3600); + } + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => [ + 'list' => $list + ] + ]; + + return json($result); + } + + public function index() + { + $param = request()->param(); + $id = intval($param['id'] ?? 0); + $ProductModel = new ProductModel(); + $product = $ProductModel->field('id,name,pay_type,price,cycle') + ->where('hidden', 0) + ->where('agentable', 1) + ->where('id', $id) + ->find(); + if(empty($product)){ + $product = (object)[]; + } + + $result = [ + 'status' => 200, + 'msg' => lang('success_message'), + 'data' => [ + 'product' => $product + ] + ]; + + return json($result); + } + + /*public function allConfigoption() + { + $param = request()->param(); + + $ProductModel = new ProductModel(); + + $result = $ProductModel->productAllConfigOption($param['id']); + + return json($result); + }*/ + + /** + * 时间 2023-02-16 + * @title 获取上游商品模块和资源 + * @desc 获取上游商品模块和资源 + * @url + * @method GET + * @author hh + * @version v1 + * @param int id - 商品ID require + * @return string module - resmodule名称 + * @return string url - zip包完整下载路径 + */ + public function downloadResource(){ + $param = request()->param(); + + $ProductModel = new ProductModel(); + $result = $ProductModel->downloadResource($param); + return json($result); + } + + +} \ No newline at end of file diff --git a/app/command/Cron.php b/app/command/Cron.php index 80047cea..cf9f179a 100755 --- a/app/command/Cron.php +++ b/app/command/Cron.php @@ -11,6 +11,9 @@ use app\common\model\ConfigurationModel; use app\common\model\SmsTemplateModel; use app\common\model\TransactionModel; +use app\common\model\SupplierModel; +use app\common\model\UpstreamProductModel; +use app\common\logic\UpstreamLogic; class Cron extends Command { @@ -76,6 +79,7 @@ public function dayCron($config,$output){ $this->hostDue($config);//主机续费提示 $this->hostOverdue($config);//主机逾期提示 $this->orderOverdue($config);//订单未付款 + $this->downstreamSyncProduct();//订单未付款 $output->writeln('续费提醒结束:'.date('Y-m-d H:i:s')); hook('daily_cron');// 每日执行一次定时任务钩子 @@ -102,6 +106,9 @@ public function fiveMinuteCron($config,$output){ $this->hostModule($config);// 主机暂停、删除 $output->writeln('自动暂停、删除结束:'.date('Y-m-d H:i:s')); + // TODO 删除,测试! + $this->downstreamSyncProduct();//订单未付款 + hook('five_minute_cron');// 每五分钟执行一次定时任务钩子 $this->configurationUpdate('cron_lock_five_minute_last_time',time()); } @@ -494,4 +501,40 @@ public function getIndexSaleInfo() return ['this_year_amount' => amount_format($thisYearAmount), 'this_year_amount_percent' => $thisYearAmountPercent, 'this_month_amount' => amount_format($thisMonthAmount), 'this_month_amount_percent' => $thisMonthAmountPercent, 'this_year_month_amount' => $thisYearMonthAmount, 'clients' => $clients]; } + + public function downstreamSyncProduct() + { + $SupplierModel = new SupplierModel(); + $supplier = $SupplierModel->select()->toArray(); + + $UpstreamProductModel = new UpstreamProductModel(); + $product = $UpstreamProductModel->select()->toArray(); + $productArr = []; + foreach ($product as $key => $value) { + $productArr[$value['supplier_id']][$value['upstream_product_id']] = ['id' => $value['product_id'], 'profit_percent' => $value['profit_percent']]; + } + foreach ($supplier as $key => $value) { + // 从上游商品拉取 + $UpstreamLogic = new UpstreamLogic(); + $res = $UpstreamLogic->upstreamProductList(['url' => $value['url']]); + foreach ($res['list'] as $k => $v) { + if(isset($productArr[$value['id']][$v['id']])){ + $id = $productArr[$value['id']][$v['id']]['id']; + $profit_percent = $productArr[$value['id']][$v['id']]['profit_percent']; + $price = $v['price'] ?? 0; + $price = bcdiv($price*(100+$profit_percent), 100, 2); + ProductModel::update([ + 'pay_type' => $v['pay_type'] ?? 'recurring_prepayment', + 'price' => $price, + 'cycle' => $v['cycle'] ?? '', + ], ['id' => $id]); + } + } + if(isset($res['list'][0]['id'])){ + $UpstreamLogic->upstreamProductDownloadResource(['url' => $value['url'], 'id' => $res['list'][0]['id']]); + } + + } + + } } diff --git a/app/command/Task.php b/app/command/Task.php index e124f0c4..bfb1f39c 100755 --- a/app/command/Task.php +++ b/app/command/Task.php @@ -45,24 +45,41 @@ protected function execute(Input $input, Output $output) } //队列 public function taskWait(){ - Db::startTrans(); $task_lock = file_exists(__DIR__.'/task.lock') ? file_get_contents(__DIR__.'/task.lock') : 0; if(empty($task_lock) || time()>($task_lock+2*60)){ file_put_contents(__DIR__.'/task.lock', time()); - $task_wait = Db::name('task_wait')->limit(10) - ->whereIn('status',['Wait','Failed']) - ->where('retry','<=',3) # 重试次数小于等于3 - ->select()->toArray();//取10条数据 - Db::name('task_wait')->where('retry','>',3) - ->whereOr('status','Finish') - ->delete(); # 删除重试次数大于3或者状态已完成的任务 + Db::startTrans(); + + try{ + $task_wait = Db::name('task_wait')->limit(10) + //->lock(true) # 加悲观锁,不允许其它进程访问(supervisor开启5个进程) + ->whereIn('status',['Wait','Failed']) + ->where('retry','<=',3) # 重试次数小于等于3 + ->select()->toArray();//取10条数据 + + Db::name('task_wait')->where('retry','>',3) + ->whereOr('status','Finish') + ->delete(); # 删除重试次数大于3或者状态已完成的任务 + + Db::commit(); + }catch(\think\db\exception\PDOException $e){ + // file_put_contents(__DIR__.'/task.lock', 0); + Db::rollback(); + return ; + }catch(\Exception $e){ + // file_put_contents(__DIR__.'/task.lock', 0); + Db::rollback(); + return ; + } - Db::commit(); - file_put_contents(__DIR__.'/task.lock', 0); if($task_wait){ foreach($task_wait as $v){ + $start = Db::name('task_wait')->where('id', $v['id'])->whereIn('status',['Wait','Failed'])->update(['status'=>'Exec']); + if(empty($start)){ + continue; + } $task_data = json_decode($v['task_data'],true); if(strpos($v['type'],'host_')===0){ $result = $this->host(str_replace('host_','',$v['type']),$task_data); @@ -86,13 +103,18 @@ public function taskWait(){ 'finish_time' => time(), 'retry' => $v['retry']+1 ])->update(); + }else{ + Db::name('task_wait')->where('id', $v['id'])->update(['status'=>$result['status'] ]); } + } + // Db::commit(); # 当前进程的任务执行完毕,释放锁 }else{ + // Db::commit(); sleep(3); } - }else{ - Db::commit(); + + file_put_contents(__DIR__.'/task.lock', 0); } diff --git a/app/common.php b/app/common.php index 9f9f86e8..9d2a221b 100755 --- a/app/common.php +++ b/app/common.php @@ -356,6 +356,16 @@ function lang_plugins($name = '', $param = [], $reload = false) $lang = array_merge($lang,$pluginLang); } } + + # 加载模块多语言 + $reserverDir = WEB_ROOT . 'plugins/reserver/'; + $servers = array_map('basename', glob($reserverDir . '*', GLOB_ONLYDIR)); + foreach ($servers as $server){ + if (is_file($reserverDir . $server . "/lang/{$defaultLang}.php")){ + $pluginLang = include $reserverDir . $server . "/lang/{$defaultLang}.php"; + $lang = array_merge($lang,$pluginLang); + } + } Cache::set($cacheName, json_encode($lang), 24*3600); } @@ -419,8 +429,14 @@ function curl($url, $data = [], $timeout = 30, $request = 'POST', $header = []) } $s = http_build_query($data); } - if($s){ - $s = '?'.$s; + if(strpos($url, '?') !== false){ + if($s){ + $s = '&'.$s; + } + }else{ + if($s){ + $s = '?'.$s; + } } curl_setopt($curl, CURLOPT_URL, $url.$s); }else{ @@ -904,6 +920,8 @@ function active_log($description, $type = '', $relId = 0, $clientId = 0) // 实例化模型类 $SystemLogModel = new SystemLogModel(); + $description = htmlspecialchars($description); + $param = [ 'description' => $description, 'type' => $type, @@ -1432,3 +1450,146 @@ function get_idcsamrt_auth() return false; } } + +/** + * @title 魔方缓存 + * @desc 魔方缓存 + * @author wyh + * @version v1 + * @param string key - 键 + * @param string value - 值:为null表示删除,’‘表示获取,其他设置 + * @param int timeout - 过期时间 + */ +function idcsmart_cache($key,$value='',$timeout=null) +{ + return \app\common\lib\IdcsmartCache::cache($key,$value,$timeout); +} + +/** + * @title API鉴权登录 + * @desc API鉴权登录 + * @author wyh + * @version v1 + * @param int api_id - 供应商ID + * @param boolean force - 是否强制登录 + * @return array + */ +function idcsmart_api_login($api_id,$force=false) +{ + $SupplierModel = new \app\common\model\SupplierModel(); + + return $SupplierModel->apiAuth($api_id,$force); +} + +/** + * @title 代理商请求供应商接口通用方法 + * @desc 代理商请求供应商接口通用方法 + * @author wyh + * @version v1 + * @param int api_id 财务APIid + * @param string path 接口路径 + * @param array data 请求数据 + * @param int timeout 超时时间 + * @param string request 请求方式(GET,POST,PUT,DELETE) + */ +function idcsmart_api_curl($api_id,$path,$data=[],$timeout=30,$request='POST') +{ + //idcsmart_cache('api_auth_login_' . AUTHCODE . '_' . $api_id,null); + $login = idcsmart_api_login($api_id); + if ($login['status']!=200){ + return $login; + } + + $header = [ + 'Authorization: Bearer '.$login['data']['jwt'] + ]; + + $apiUrl = $login['data']['url'] . '/' .$path; + + $result = curl($apiUrl,$data,$timeout,$request,$header); + if($result['http_code'] != 200){ + return ['status'=>400, 'msg'=>'网络开小差', 'content'=>$result['content']]; + } + $result = json_decode($result['content'], true); + if(empty($result)){ + $result = ['status'=>400, 'msg'=>'网络开小差', 'content'=>$result['content']]; + } + if ($result['status']==401){ + $login = idcsmart_api_login($api_id, true); + if ($login['status']!=200){ + return $login; + } + + $header = [ + 'Authorization: Bearer '.$login['data']['jwt'] + ]; + $result = curl($apiUrl,$data,$timeout,$request,$header); + if($result['http_code'] != 200){ + return ['status'=>400, 'msg'=>'网络开小差', 'content'=>$result['content']]; + } + $result = json_decode($result['content'], true); + + if ($result['status']==401){ + $result['status']=400; + $result['msg'] = 'API账号或密码错误'; + } + } + + return $result; +} + +/** + * @title 魔方生成RSA公私钥 + * @desc 魔方生成RSA公私钥 + * @author theworld + * @version v1 + * @return string public_key - 公钥 + * @return string private_key - 私钥 + */ +function idcsmart_openssl_rsa_key_create() +{ + $config = array( + "digest_alg" => "sha512", + "private_key_bits" => 4096, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + ); + + $res = openssl_pkey_new($config); + + openssl_pkey_export($res, $privateKey); + + $publicKey = openssl_pkey_get_details($res); + $publicKey = $publicKey["key"]; + + return ['public_key' => $publicKey, 'private_key' => $privateKey]; +} + +/** + * @title 上游同步产品信息到下游 + * @desc 上游同步产品信息到下游 + * @author theworld + * @version v1 + * @param int host_id 财务产品ID + * @param string action 动作 + */ +function upstream_sync_host($host_id, $action = '') +{ + $HostModel = new \app\common\model\HostModel(); + + return $HostModel->upstreamSyncHost($host_id, $action); +} + +/** + * @title 更新上游订单利润 + * @desc 更新上游订单利润 + * @author theworld + * @version v1 + * @param int order_id 财务订单ID + */ +function update_upstream_order_profit($order_id) +{ + $OrderModel = new \app\common\model\OrderModel(); + + return $OrderModel->updateUpstreamOrderProfit($order_id); +} + diff --git a/app/common/lib/IdcsmartCache.php b/app/common/lib/IdcsmartCache.php new file mode 100644 index 00000000..3203ef64 --- /dev/null +++ b/app/common/lib/IdcsmartCache.php @@ -0,0 +1,29 @@ +del($key); + }elseif($value===''){ + return $Redis->get($key); + }else{ + return $Redis->set($key,$value,isset($timeout)?(float)$timeout:0); + } + }else{ + return cache($key,$value,$timeout); + } + } +} diff --git a/app/common/lib/RedisPool.php b/app/common/lib/RedisPool.php new file mode 100644 index 00000000..6f1b3384 --- /dev/null +++ b/app/common/lib/RedisPool.php @@ -0,0 +1,40 @@ + [REDIS_HOST,REDIS_PORT,REDIS_PASSWORD], // host port password + ]; + + public static function addServer($config) + { + foreach ($config as $alias=>$data){ + self::$servers[$alias] = $data; + } + } + + public static function getRedis($alias,$select=0) + { + if (!array_key_exists($alias,self::$connections)){ + $redis = new \Redis(); + $redis->connect(self::$servers[$alias][0],self::$servers[$alias][1]); + if (isset(self::$servers[$alias][2]) && !empty(self::$servers[$alias][2])){ + $redis->auth(self::$servers[$alias][2]); + } + self::$connections[$alias]=$redis; + } + self::$connections[$alias]->select($select); + + return self::$connections[$alias]; + } +} diff --git a/app/common/logic/ModuleLogic.php b/app/common/logic/ModuleLogic.php index d842eb7b..a6784e27 100755 --- a/app/common/logic/ModuleLogic.php +++ b/app/common/logic/ModuleLogic.php @@ -792,15 +792,15 @@ public function allConfigOption(ProductModel $ProductModel){ * @return string msg - 提示信息 * @return array data - 数据 */ - public function currentConfigOptioin(HostModel $HostModel) + public function currentConfigOption(HostModel $HostModel) { $res = []; $module = $HostModel->getModule(); if($ImportModule = $this->importModule($module)){ - if(method_exists($ImportModule, 'currentConfigOptioin')){ + if(method_exists($ImportModule, 'currentConfigOption')){ // 获取模块通用参数 $moduleParams = $HostModel->getModuleParams(); - $res = call_user_func([$ImportModule, 'currentConfigOptioin'], $moduleParams); + $res = call_user_func([$ImportModule, 'currentConfigOption'], $moduleParams); // TODO 验证返回 } } @@ -845,6 +845,33 @@ public function getPriceCycle($productId) return $res; } + /** + * 时间 2023-02-14 + * @title 下载上游资源 + * @desc 下载上游资源 + * @author hh + * @version v1 + * @param ProductModel $ProductModel - 商品实例 require + */ + public function downloadResource(ProductModel $ProductModel){ + $res = []; + $module = $ProductModel->getModule(); + if($ImportModule = $this->importModule($module)){ + if(method_exists($ImportModule, 'downloadResource')){ + $res = call_user_func([$ImportModule, 'downloadResource'], ['product'=>$ProductModel]); + // TODO 验证返回 + + } + } + if(empty($res)){ + // 未实现该方法返回成功 + $res = ['status'=>200, 'msg'=>'module_file_is_not_exist', 'data'=>[] ]; + } + return $res; + } + + + diff --git a/app/common/logic/ResModuleLogic.php b/app/common/logic/ResModuleLogic.php new file mode 100644 index 00000000..554da021 --- /dev/null +++ b/app/common/logic/ResModuleLogic.php @@ -0,0 +1,1081 @@ +upstreamProduct = $upstreamProduct; + } + + /** + * 时间 2022-05-27 + * @title 获取模块列表 + * @desc 获取模块列表 + * @author hh + * @version v1 + * @return string [].name - 模块名称 + * @return string [].display_name - 模块显示名称 + */ + public function getModuleList(): array + { + $modules = []; + if(is_dir($this->path)){ + if($handle = opendir($this->path)){ + while(($file = readdir($handle)) !== false){ + if($file != '.' && $file != '..' && is_dir($this->path . $file) && preg_match('/^[a-z][a-z0-9_]{0,99}$/', $file)){ + if($ImportModule = $this->importModule($file)){ + if(method_exists($ImportModule, 'metaData')){ + $metaData = call_user_func([$ImportModule, 'metaData']); + $modules[] = [ + 'name'=>$file, + 'display_name'=>$metaData['display_name'] ?: $file, + ]; + }else{ + $modules[] = [ + 'name'=>$file, + 'display_name'=>$file, + ]; + } + } + } + } + closedir($handle); + } + } + return $modules; + } + + /** + * 时间 2022-05-27 + * @title 测试连接 + * @desc 测试连接 + * @author hh + * @version v1 + * @param ServerModel ServerModel - 接口模型 + * @return int status - 200=连接成功,400=连接失败 + * @return string msg - 信息 + */ + // public function testConnect(ServerModel $ServerModel): array + // { + // $module = $ServerModel['module']; + // if($ImportModule = $this->importModule($module)){ + // if(method_exists($ImportModule, 'testConnect')){ + // // 获取模块通用参数 + // $res = call_user_func([$ImportModule, 'testConnect'], ['server'=>$ServerModel]); + // $res = $this->formatResult($res, lang('module_test_connect_success'), lang('module_test_connect_fail')); + // }else{ + // $res['status'] = 400; + // $res['msg'] = lang('undefined_test_connect_function'); + // } + // }else{ + // $res['status'] = 400; + // $res['msg'] = lang('module_file_is_not_exist'); + // } + // return $res; + // } + + /** + * 时间 2022-05-16 + * @title 产品开通 + * @desc 产品开通 + * @author hh + * @version v1 + * @param HostModel $HostModel - 产品模型 + * @return array + * @return int status - 状态,200=成功,400=失败 + * @return string msg - 信息 + */ + public function createAccount(HostModel $HostModel): array + { + $id = $HostModel->id; + + $UpstreamHostModel = new UpstreamHostModel(); + $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + if (empty($upstreamHost)) { + return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + } + $apiId = $this->upstreamProduct['supplier_id']; + # 检查是否可以登录 + $res = idcsmart_api_login($apiId,true); + if ($res['status']==200){ + $upstreamInfo = json_decode($upstreamHost['upstream_info'],true)??[]; + if (!empty($upstreamInfo['token'])){ + $token = $upstreamInfo['token']; + $url = $upstreamInfo['url']; + }else{ + $token = md5(rand_str(16).time().$id); + $url = configuration('website_url'); //request()->domain() . request()->rootUrl(); 服务器定时任务走cli模式,获取的是本地localhost + $upstreamHost->save([ + 'upstream_info' => json_encode(['token'=>$token,'url'=>$url]) + ]); + } + + $clearCartData = [ + 'downstream_url' => $url, + 'downstream_token' => $token, + 'downstream_host_id' => $id, + 'downstream_client_id' => $HostModel['client_id'], + ]; + + $enable = PluginModel::where('name', 'IdcsmartSubAccount')->where('module', 'addon')->where('status',1)->find(); + if(!empty($enable) && class_exists('addon\idcsmart_sub_account\model\IdcsmartSubAccountHostModel')){ + // 是否是子账户 + $IdcsmartSubAccountHostModel = IdcsmartSubAccountHostModel::where('host_id', $id)->find(); + if(!empty($IdcsmartSubAccountHostModel)){ + $IdcsmartSubAccountModel = IdcsmartSubAccountModel::find($IdcsmartSubAccountHostModel['addon_idcsmart_sub_account_id']); + if(!empty($IdcsmartSubAccountModel)){ + $clearCartData['downstream_client_id'] = $IdcsmartSubAccountModel['client_id']; + } + } + } + # 清空购物车(检查是否已下单) + $res = idcsmart_api_curl($apiId,'/console/v1/cart',$clearCartData,30,'DELETE'); + if ($res['status']==200){ + if (isset($res['data']['order_id']) && $res['data']['order_id']){ # 已在上游下单,但未支付 + /*$creditData = [ + 'id' => $res['data']['order_id']??0, + 'use' => 1 + ];*/ + # 使用余额 + //$res = idcsmart_api_curl($apiId,'/console/v1/credit',$creditData,30,'POST'); + //if ($res['status']==200){ + $payData = [ + 'id' => $res['data']['order_id']??0, + 'gateway' => 'credit' + ]; + # 支付 + $res = idcsmart_api_curl($apiId,'/console/v1/pay',$payData,30,'POST'); + //} + }else{ + $cartData = [ + 'product_id' => $this->upstreamProduct['upstream_product_id'], + 'qty' => 1, + 'config_options' => json_decode($upstreamHost['upstream_configoption'],true) + ]; + # 加入购物车 + $res = idcsmart_api_curl($apiId,'/console/v1/cart',$cartData,30,'POST'); + if ($res['status']==200){ + $settleCartData = $clearCartData; + $settleCartData['positions'] = [0]; # 取第一个,只有一个 + $settleCartData['downstream_client_id'] = $clearCartData['downstream_client_id']; + # 结算 + $res = idcsmart_api_curl($apiId,'/console/v1/cart/settle',$settleCartData,30,'POST'); + if ($res['status']==200){ + $upstreamHost->save([ + 'upstream_host_id' => $res['data']['host_ids'][0]??0, + ]); + if ($res['data']['amount']>0){ // 处理需要支付的 + /*$creditData = [ + 'id' => $res['data']['order_id']??0, + 'use' => 1 + ];*/ + # 使用余额 + //$res = idcsmart_api_curl($apiId,'/console/v1/credit',$creditData,30,'POST'); + //if ($res['status']==200){ + $payData = [ + 'id' => $res['data']['order_id']??'', + 'gateway' => 'credit' + ]; + # 支付 + $res = idcsmart_api_curl($apiId,'/console/v1/pay',$payData,30,'POST'); + //} + } + } + } + } + } + } + + return $res; + } + + /** + * 时间 2022-05-16 + * @title 产品暂停 + * @desc 产品暂停 + * @author hh + * @version v1 + * @param HostModel $HostModel - 产品模型 + * @return array + * @return int status - 状态,200=成功,400=失败 + * @return string msg - 信息 + */ + public function suspendAccount(HostModel $HostModel, $param = []): array + { + $id = $HostModel->id; + + $UpstreamHostModel = new UpstreamHostModel(); + $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + if (empty($upstreamHost)) { + return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + } + + $apiId = $this->upstreamProduct['supplier_id']; + + $suspendData = [ + 'suspend_type' => $param['suspend_type']??'downstream', + 'suspend_reason' => $param['suspend_reason']??'代理商暂停' + ]; + + $res = idcsmart_api_curl($apiId,"/console/v1/host/{$upstreamHost['upstream_host_id']}/module/suspend",$suspendData,30,'POST'); + + return $res; + } + + /** + * 时间 2022-05-16 + * @title 产品解除暂停 + * @desc 产品解除暂停 + * @author hh + * @version v1 + * @param HostModel $HostModel - 产品模型 + * @return array + * @return int status - 状态,200=成功,400=失败 + * @return string msg - 信息 + */ + public function unsuspendAccount(HostModel $HostModel): array + { + $id = $HostModel->id; + + $UpstreamHostModel = new UpstreamHostModel(); + $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + if (empty($upstreamHost)) { + return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + } + + $apiId = $this->upstreamProduct['supplier_id']; + + $res = idcsmart_api_curl($apiId,"/console/v1/host/{$upstreamHost['upstream_host_id']}/module/unsuspend",[],30,'POST'); + + return $res; + } + + /** + * 时间 2022-05-16 + * @title 产品删除 + * @desc 产品删除 + * @author hh + * @version v1 + * @param HostModel $HostModel - 产品模型 + * @return array + * @return int status - 状态,200=成功,400=失败 + * @return string msg - 信息 + */ + public function terminateAccount(HostModel $HostModel): array + { + $id = $HostModel->id; + + $UpstreamHostModel = new UpstreamHostModel(); + $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + if (empty($upstreamHost)) { + return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + } + + $apiId = $this->upstreamProduct['supplier_id']; + + // TODO + $terminateData = [ + 'host_id' => $upstreamHost['upstream_host_id'], + 'suspend_reason' => '代理商删除', + 'type' => 'Immediate' + ]; + + $res = idcsmart_api_curl($apiId,"/console/v1/refund",$terminateData,30,'POST'); + + return $res; + } + + /** + * 时间 2022-05-16 + * @title 续费订单支付后调用 + * @desc 续费订单支付后调用 + * @author hh + * @version v1 + * @param HostModel $HostModel - 产品模型 + */ + public function renew(HostModel $HostModel) + { + $id = $HostModel['id']; + + $UpstreamHostModel = new UpstreamHostModel(); + $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + if (empty($upstreamHost)) { + return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + } + + $apiId = $this->upstreamProduct['supplier_id']; + + $renewData = [ + 'billing_cycle' => $HostModel['billing_cycle_name']??'' + ]; + + $res = idcsmart_api_curl($apiId,"/console/v1/host/{$upstreamHost['upstream_host_id']}/renew",$renewData,30,'POST'); + if ($res['status']==200){ + if ($res['code']=='Unpaid'){ # 未支付 + $creditData = [ + 'id' => $res['data']['id']??0, + 'use' => 1 + ]; + # 使用余额 + $res = idcsmart_api_curl($apiId,'/console/v1/credit',$creditData,30,'POST'); + if ($res['status']==200){ + $payData = [ + 'id' => $res['data']['id'], + 'gateway' => 'credit' + ]; + # 支付 + $res = idcsmart_api_curl($apiId,'/console/v1/pay',$payData,30,'POST'); + } + } + unset($res['code']); + } + + return $res; + } + + /** + * 时间 2022-05-26 + * @title 升降级配置项完成后调用 + * @desc 升降级配置项完成后调用 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @param mixed params - 自定义参数 + */ + public function changePackage(HostModel $HostModel, $params) + { + if($ImportModule = $this->importModule()){ + if(method_exists($ImportModule, 'changePackage')){ + // 获取模块通用参数 + $moduleParams = $HostModel->getModuleParams(); + $moduleParams['custom'] = $params; + $res = call_user_func([$ImportModule, 'changePackage'], $moduleParams); + } + } + // 不需要返回 + } + + /** + * 时间 2022-06-01 + * @title 升降级商品完成后调用 + * @desc 升降级商品完成后调用 + * @author hh + * @version v1 + * @param HostModel HostModel - 已经关联新商品的产品模型 + * @param mixed params - 自定义参数 + */ + // public function changeProduct(HostModel $HostModel, $params) + // { + // if($ImportModule = $this->importModule()){ + // if(method_exists($ImportModule, 'changeProduct')){ + // // 获取模块通用参数 + // $moduleParams = $HostModel->getModuleParams(); + // $moduleParams['custom'] = $params; + // $res = call_user_func([$ImportModule, 'changeProduct'], $moduleParams); + // } + // } + // // 不需要返回 + // } + + /** + * 时间 2022-05-26 + * @title 购物车价格计算 + * @desc 购物车价格计算 + * @author hh + * @version v1 + * @param ProductModel $ProductModel - 产品模型 + * @param mixed $params [] 自己定义的参数 + * @param string scene - 场景(buy=验证所有参数,cal_price=价格计算) + * @return int status - 状态(200=成功,400=失败) + * @return array data - 购物车数据 + * @return float data.price - 配置项金额 + * @return string data.billing_cycle - 周期名称 + * @return int data.duration - 周期时长 + * @return string data.description - 订单子项描述 + * @return string data.content - 购物车配置显示,支持模板 + * @return string data.preview[].name - 名称 + * @return string data.preview[].value - 值 + * @return string data.preview[].price - 价格 + */ + public function cartCalculatePrice($ProductModel, $params = [], $qty=1) + { + $apiId = $this->upstreamProduct['supplier_id']; + + $upstreamProductId = $this->upstreamProduct['upstream_product_id']; + + $res = idcsmart_api_curl($apiId,"/console/v1/product/{$upstreamProductId}/config_option", ['config_options'=>$params],30,'POST'); + if ($res['status']==200){ + $res['data']['profit'] = bcmul($res['data']['price'] ?? 0, ($this->upstreamProduct['profit_percent']/100), 2); + $res['data']['price'] = bcadd($res['data']['price'] ?? 0, $res['data']['profit'], 2); + $res['data']['renew_price'] = bcmul($res['data']['renew_price']??0,(1+$this->upstreamProduct['profit_percent']/100),2); + $res['data']['base_price'] = bcmul($res['data']['base_price']??0,(1+$this->upstreamProduct['profit_percent']/100),2); + + $description = ''; + foreach($res['data']['preview'] as $k=>$v){ + if($v['price']>0){ + $v['price'] = bcmul($v['price'],(1+$this->upstreamProduct['profit_percent']/100),2); + $res['data']['preview'][$k]['price'] = $v['price']; + } + $description .= $v['name'].': '.$v['value'].',价格:'.$v['price']."\r\n"; + } + $res['data']['description'] = $description; + $res['data']['content'] = $description; + } + return $res; + } + + /** + * 时间 2022-05-16 + * @title 后台商品接口配置输出 + * @desc 后台商品接口配置输出 + * @author hh + * @version v1 + * @param HostModel $HostModel - 产品模型 + * @return string + */ + /*public function serverConfigOption($module, ProductModel $ProductModel) + { + $res = ''; + // 模块调用 + // if($ImportModule = $this->importModule()){ + // if(method_exists($ImportModule, 'serverConfigOption')){ + // // 获取模块通用参数 + // $res = call_user_func([$ImportModule, 'serverConfigOption'], ['product'=>$ProductModel]); + // $res = $this->formatTemplate($res); + // } + // } + return $res; + }*/ + + /** + * 时间 2022-05-16 + * @title 产品列表页内容 + * @desc 产品列表页内容 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @return string + */ + public function hostList($module, $params): string + { + $res = ''; + // 模块调用 + if($ImportModule = $this->importModule($module)){ + if(method_exists($ImportModule, 'hostList')){ + // 获取模块通用参数 + $this->upstreamProduct['res_module'] = $module; + $res = call_user_func([$ImportModule, 'hostList'], $params); + $res = $this->formatTemplate($res); + } + } + return $res; + } + + /** + * 时间 2022-05-16 + * @title 产品前台内页输出 + * @desc 产品前台内页输出 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @return string + */ + public function clientArea(HostModel $HostModel): string + { + $res = ''; + // 模块调用 + // $module = $HostModel->getModule(); + if($ImportModule = $this->importModule()){ + if(method_exists($ImportModule, 'clientArea')){ + // 获取模块通用参数 + $params = $HostModel->getModuleParams(); + $res = call_user_func([$ImportModule, 'clientArea'], $params); + $res = $this->formatTemplate($res); + } + } + return $res; + // $id = $HostModel['id']; + + // $UpstreamHostModel = new UpstreamHostModel(); + // $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + // if (empty($upstreamHost)) { + // return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + // } + + // $apiId = $this->upstreamProduct['supplier_id']; + + // $res = idcsmart_api_curl($apiId,"/console/v1/host/{$upstreamHost['upstream_host_id']}/view",[],30,'GET'); + + // return $res; + } + + /** + * 时间 2022-05-16 + * @title 产品后台内页输出 + * @desc 产品后台内页输出 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @return string + */ + public function adminArea(HostModel $HostModel): string + { + // $id = $HostModel['id']; + + // $UpstreamHostModel = new UpstreamHostModel(); + // $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + // if (empty($upstreamHost)) { + // return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + // } + + // $apiId = $this->upstreamProduct['supplier_id']; + + // $res = idcsmart_api_curl($apiId,"/console/v1/host/{$upstreamHost['upstream_host_id']}/view",[],30,'GET'); + $res = ''; + return $res; + } + + /** + * 时间 2022-05-30 + * @title 前台商品配置页面 + * @desc 前台商品配置输出,购物车,单独订购,升降级商品 + * @author hh + * @version v1 + * @param ProductModel ProductModel - 产品模型 + * @return string + */ + public function clientProductConfigOption(ProductModel $ProductModel, $tag = ''): string + { + $res = ''; + if($ImportModule = $this->importModule()){ + if(method_exists($ImportModule, 'clientProductConfigOption')){ + // 获取模块通用参数 + $res = call_user_func([$ImportModule, 'clientProductConfigOption'], ['product'=>$ProductModel, 'tag'=>$tag]); + $res = $this->formatTemplate($res); + } + } + return $res; + // $apiId = $this->upstreamProduct['supplier_id']; + + // $upstreamProductId = $this->upstreamProduct['upstream_product_id']; + + // $res = idcsmart_api_curl($apiId,"/console/v1/product/{$upstreamProductId}/config_option",['tag'=>$tag],30,'GET'); + // if ($res['status']==200){ + // return $res['data']['content']??''; + // } + + // return ''; + } + + /** + * 时间 2022-05-31 + * @title 后台商品配置页面 + * @desc 后台商品配置输出,新建订单,升降级商品 + * @author hh + * @version v1 + * @param ProductModel ProductModel - 产品模型 + * @return string + */ + /*public function adminProductConfigOption(ProductModel $ProductModel, $tag = ''): string + { + $apiId = $this->upstreamProduct['supplier_id']; + + $upstreamProductId = $this->upstreamProduct['upstream_product_id']; + + $res = idcsmart_api_curl($apiId,"/console/v1/product/{$upstreamProductId}/config_option",['tag'=>$tag],30,'GET'); + + return $res; + }*/ + + /** + * 时间 2022-05-31 + * @title 前台产品升降级配置输出 + * @desc 前台产品升降级配置输出 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @return string + */ + /*public function clientChangeConfigOption(HostModel $HostModel): string + { + $res = ''; + if($ImportModule = $this->importModule()){ + if(method_exists($ImportModule, 'clientChangeConfigOption')){ + // 获取模块通用参数 + $params = $HostModel->getModuleParams(); + $res = call_user_func([$ImportModule, 'clientChangeConfigOption'], $params); + $res = $this->formatTemplate($res); + } + } + return $res; + }*/ + + /** + * 时间 2022-05-31 + * @title 后台产品升降级配置输出(暂时未用) + * @desc 后台产品升降级配置输出 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @return string + */ + // public function adminChangeConfigOption(HostModel $HostModel): string + // { + // $res = ''; + // if($ImportModule = $this->importModule()){ + // if(method_exists($ImportModule, 'adminChangeConfigOption')){ + // // 获取模块通用参数 + // $params = $HostModel->getModuleParams(); + // $res = call_user_func([$ImportModule, 'adminChangeConfigOption'], $params); + // $res = $this->formatTemplate($res); + // } + // } + // return $res; + // } + + /** + * 时间 2022-05-31 + * @title 升降级配置项计算价格(暂时未用) + * @desc 升降级配置项计算价格 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @param array params - 产品模型 + * @return array + */ + // public function changeConfigOptionCalculatePrice(HostModel $HostModel, $params): array + // { + // $result = []; + // if($ImportModule = $this->importModule()){ + // if(method_exists($ImportModule, 'changeConfigOptionCalculatePrice')){ + // // 获取模块通用参数 + // $result = call_user_func([$ImportModule, 'changeConfigOptionCalculatePrice'], ['host'=>$HostModel, 'product'=>ProductModel::find($HostModel['product_id']), 'custom'=>$params]); + // // TODO 是否判断返回/格式化 + + // } + // } + // if(empty($result)){ + // $result = [ + // 'status'=>400, + // 'msg'=>lang('module_file_is_not_exist'), + // ]; + // } + // return $result; + // } + + /** + * 时间 2022-05-30 + * @title 在结算之后调用 + * @desc 在结算之后调用,通常是验证参数,并保存参数 + * @author hh + * @version v1 + * @param ProductModel ProductModel - 商品模型 + * @param int hostId - 产品ID + * @param array params - 自定义参数 + */ + public function afterSettle($ProductModel, $hostId, $params): void + { + if($ImportModule = $this->importModule()){ + if(method_exists($ImportModule, 'afterSettle')){ + call_user_func([$ImportModule, 'afterSettle'], ['product'=>$ProductModel, 'host_id'=>$hostId, 'custom'=>$params]); + } + } + } + + /** + * 时间 2022-05-16 + * @title 自定义后台方法 + * @desc 自定义后台方法 + * @author hh + * @version v1 + * @param string module - 模块名称 + * @return mixed params - 自定义参数 + */ + // public function customAdminFunction($module, $params) + // { + // $res = []; + // // 验证模块格式是否正确 + // if(!$this->checkModule($module)){ + // $res['status'] = 400; + // $res['msg'] = '模块格式错误'; + // return json($res); + // } + // $controller = $params['controller'] ?? ''; + // $method = $params['method'] ?? ''; + // if(empty($controller) || empty($method)){ + // $res['status'] = 400; + // $res['msg'] = '模块格式错误'; + // return json($res); + // } + // $controller = parse_name($controller.'_controller', 1); + // $method = parse_name($method, 1, false); + + // $class = '\reserver\\'.$module.'\\controller\\admin\\'.$controller; + // if(class_exists($class)){ + // $class = new $class(); + + // if(method_exists($class, $method)){ + // $res = call_user_func([$class, $method], $params); + // }else{ + // $res['status'] = 400; + // $res['msg'] = '模块或方法不存在'; + // $res = json($res); + // } + // }else{ + // $res['status'] = 400; + // $res['msg'] = '模块或方法不存在'; + // $res = json($res); + // } + // // if($this->importModule($module)){ + // // // 执行模块操作 + // // $func = $module . '_CustomAdminFunction'; + // // if(function_exists($func)){ + // // $res = call_user_func($func, $params); + // // $res = $this->formatResult($res); + // // } + // // } + // // if(empty($res)){ + // // $res['status'] = 400; + // // $res['msg'] = '模块或方法不存在'; + // // } + // return $res; + // } + + /** + * 时间 2022-06-08 + * @title 自定义前台方法 + * @desc 自定义前台方法 + * @author hh + * @version v1 + * @param string $module - 模块名称 + * @param mixed $params - POST的其他参数 + * @return array + */ + // public function customClientFunction($module, $params) + // { + // $res = []; + // // 验证模块格式是否正确 + // if(!$this->checkModule($module)){ + // $res['status'] = 400; + // $res['msg'] = '模块格式错误'; + // return json($res); + // } + // $controller = $params['controller'] ?? ''; + // $method = $params['method'] ?? ''; + // if(empty($controller) || empty($method)){ + // $res['status'] = 400; + // $res['msg'] = '模块格式错误'; + // return json($res); + // } + // $controller = parse_name($controller.'_controller', 1); + // $method = parse_name($method, 1, false); + + // $class = '\reserver\\'.$module.'\\controller\\home\\'.$controller; + // if(class_exists($class)){ + // $class = new $class(); + + // if(method_exists($class, $method)){ + // $res = call_user_func([$class, $method], $params); + // }else{ + // $res['status'] = 400; + // $res['msg'] = '模块或方法不存在'; + // $res = json($res); + // } + // }else{ + // $res['status'] = 400; + // $res['msg'] = '模块或方法不存在'; + // $res = json($res); + // } + // return $res; + // } + + /** + * 时间 2022-06-02 + * @title 获取当前产品所有周期价格 + * @desc 获取当前产品所有周期价格 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @return int status - 状态码(200=成功,400=失败) + * @return string msg - 提示信息 + * @return array data - 数据 + * @return float data[].price - 金额 + * @return string data[].billing_cycle - 周期名称 + * @return int data[].duration - 周期时长(秒) + */ + public function durationPrice(HostModel $HostModel) + { + $id = $HostModel['id']; + + $UpstreamHostModel = new UpstreamHostModel(); + $upstreamHost = $UpstreamHostModel->where('host_id', $id)->find(); + if (empty($upstreamHost)) { + return ['status' => 400, 'msg' => lang('upstream_host_is_not_exist')]; + } + + $apiId = $this->upstreamProduct['supplier_id']; + + $res = idcsmart_api_curl($apiId,"/console/v1/host/{$upstreamHost['upstream_host_id']}/renew",[],30,'GET'); + + if ($res['status']==200){ + $result = [ + 'status' => 200, + 'msg' => $res['msg'], + 'data' => [ + ] + ]; + foreach ($res['data']['host'] as $item){ + $item['profit'] = bcmul($item['price'], ($this->upstreamProduct['profit_percent']/100), 2); + $item['price'] = bcadd($item['price'], $item['profit'], 2); + $result['data'][] = $item; + } + return $result; + } + + return $res; + } + + /** + * 时间 2022-06-16 + * @title 获取商品所有配置项 + * @desc 获取商品所有配置项 + * @author hh + * @version v1 + * @param ProductModel ProductModel - 商品模型 + * @return array + */ + /*public function allConfigOption(ProductModel $ProductModel) + { + $apiId = $this->upstreamProduct['supplier_id']; + + $upstreamProductId = $this->upstreamProduct['upstream_product_id']; + + $res = idcsmart_api_curl($apiId,"/api/v1/product/{$upstreamProductId}/all_config_option",[],30,'GET'); + + return $res; + }*/ + + /** + * 时间 2022-08-04 + * @title 获取当前产品配置项 + * @desc 获取当前产品配置项 + * @author hh + * @version v1 + * @param HostModel HostModel - 产品模型 + * @return int status - 状态码(200=成功,400=失败) + * @return string msg - 提示信息 + * @return array data - 数据 + */ + /*public function currentConfigOption(HostModel $HostModel) + { + $res = []; + if($ImportModule = $this->importModule()){ + if(method_exists($ImportModule, 'currentConfigOption')){ + // 获取模块通用参数 + $moduleParams = $HostModel->getModuleParams(); + $res = call_user_func([$ImportModule, 'currentConfigOption'], $moduleParams); + // TODO 验证返回 + } + } + // if(empty($res)){ + // $res = ['status'=>400, 'msg'=>'module_file_is_not_exist']; + // } + return $res; + }*/ + + /** + * 时间 2023-01-30 + * @title 获取商品最低周期价格 + * @desc 获取商品最低周期价格 + * @author hh + * @version v1 + * @param int productId - 商品ID + * @return float price - 价格 + * @return string cycle - 周期 + * @return ProductModel product - ProductModel实例 + */ + // public function getPriceCycle($productId) + // { + // $res = [ + // 'price' => null, + // 'cycle' => null + // ]; + // $ProductModel = ProductModel::findOrEmpty($productId); + + // $module = $ProductModel->getModule(); + // if($ImportModule = $this->importModule($module)){ + // if(method_exists($ImportModule, 'getPriceCycle')){ + // $moduleRes = call_user_func([$ImportModule, 'getPriceCycle'], ['product'=>$ProductModel]); + // if(isset($moduleRes['price']) && is_numeric($moduleRes['price'])){ + // $res['price'] = $moduleRes['price']; + // } + // if(isset($moduleRes['cycle'])){ + // $res['cycle'] = $moduleRes['cycle']; + // } + // } + // } + // $res['product'] = $ProductModel; + // return $res; + // } + + + public function downloadResource($ProductModel){ + $apiId = $this->upstreamProduct['supplier_id']; + + $res = idcsmart_api_curl($apiId, sprintf('api/v1/product/%d/resource', $this->upstreamProduct['upstream_product_id']), [] ,30, 'GET'); + return $res; + } + + + + + /** + * 时间 2022-06-08 + * @title 验证模块名称是否正确 + * @desc 验证模块名称是否正确 + * @author hh + * @version v1 + * @param string $module - 模块名称 + * @return bool + */ + protected function checkModule($module){ + return (bool)preg_match('/^[a-z][a-z0-9_]{0,99}$/', $module); + } + + /** + * 时间 2022-05-16 + * @title 引入商品模块文件 + * @desc 引入商品模块文件 + * @author hh + * @version v1 + * @param string module - 模块类型 + * @return bool|object - - false=没有对应类,object=成功实例化模块类 + */ + protected function importModule($module = null) + { + $module = $module ?? $this->upstreamProduct['res_module']; + if(!empty($module)){ + $className = parse_name($module, 1); + + $class = '\reserver\\'.$module.'\\'.$className; + + if(class_exists($class)){ + return new $class(); + } + } + return false; + } + + /** + * 时间 2022-05-26 + * @title 格式化文本返回 + * @desc 格式化文本返回 + * @author hh + * @version v1 + * @param string $module 模块名称 + * @param mixed $res 模块返回 + * @return string + */ + private function formatTemplate($res = null): string + { + $html = ''; + $module = $module ?? $this->upstreamProduct['res_module']; + if(is_array($res)){ + // 认为是使用模板的方式来输出内容,格式大概如下 + // [ + // 'template'=>'abc.html', + // 'vars'=>[ + // 'aaaa'=>'bbb' + // ] + // ] + $template_file = $this->path . $module . '/' . $res['template']; + if(file_exists($template_file)){ + $PluginModel=new PluginModel(); + $addons = $PluginModel->plugins('addon')['list']; + + $vars = isset($res['vars']) && !empty($res['vars']) && is_array($res['vars']) ? $res['vars'] : []; + $vars['addons'] = $addons; + + View::assign($vars); + // 调用方法变量 + $html = View::fetch($template_file); + }else{ + $html = lang('module_cannot_find_template_file'); + } + }else if(is_string($res)){ + $html = $res; + }else{ + $html = (string)$res; + } + return $html; + } + + /** + * 时间 2022-05-13 + * @title 格式化系统操作返回 + * @desc 格式化系统操作返回 + * @author hh + * @version v1 + * @param mixed res - 操作返回 required + * @param string successMsg - 成功返回没有提示信息时,会用该信息提示 + * @param string failMsg - 失败返回没有提示信息时,会用该信息提示 + * @return array + */ + private function formatResult($res, $successMsg = '', $failMsg = ''): array + { + $result = []; + // 不兼容原来的老模块写法,都必须按标准返回 + if(is_array($res)){ + $result = $res; + + if($result['status'] === 400){ + $result['msg'] = $result['msg'] ?? ($failMsg ?: lang('module_operate_fail')); + }else if($result['status'] === 200){ + $result['msg'] = $result['msg'] ?? ($successMsg ?: lang('module_operate_success')); + }else{ + $result = []; + $result['status'] = 400; + $result['msg'] = lang('module_res_format_error'); + } + }else{ + $result = []; + $result['status'] = 400; + $result['msg'] = lang('module_res_format_error'); + // 原模块返回判断(废弃) + // if($res === null || $res == 'success' || $res == 'ok'){ + // $result['status'] = 200; + // $result['msg'] = '操作成功'; + // }else{ + // $result['status'] = 400; + // $result['msg'] = (string)$res; + // } + } + return $result; + } + + +} + + + diff --git a/app/common/logic/UpstreamLogic.php b/app/common/logic/UpstreamLogic.php new file mode 100644 index 00000000..189f5230 --- /dev/null +++ b/app/common/logic/UpstreamLogic.php @@ -0,0 +1,287 @@ +upstreamRequest($this->officialUrl.'/console/v1/recommend/product', $param, 30, 'GET'); + $list = $res['data']['list'] ?? []; + $upstreamProduct = UpstreamProductModel::column('upstream_product_id'); + $supplier = SupplierModel::select()->toArray(); + $supplierArr = []; + foreach ($supplier as $key => $value) { + $supplierArr[$value['url']] = ['id' => $value['id'], 'username' => $value['username'], 'token' => aes_password_decode($value['token']), 'secret' => aes_password_decode($value['secret'])]; + } + foreach ($list as $key => $value) { + if(in_array($value['upstream_product_id'], $upstreamProduct)){ + $list[$key]['agent'] = 1; + }else{ + $list[$key]['agent'] = 0; + } + $list[$key]['supplier'] = $supplierArr[$value['url']] ?? (object)[]; + } + + return ['list' => $list, 'count' => $res['data']['count'] ?? 0]; + } + + public function recommendProductDetail($param) + { + $param['id'] = $param['id'] ?? 0; + + $res = $this->upstreamRequest($this->officialUrl.'/console/v1/recommend/product/'.$param['id'], [], 30, 'GET'); + + return ['data' => $res['data']['product'] ?? []]; + } + + public function upstreamProductList($param) + { + $res = $this->upstreamRequest(rtrim($param['url'],'/').'/api/v1/product', [], 30, 'GET'); + + return ['list' => $res['data']['list'] ?? []]; + } + + public function upstreamProductDetail($param) + { + $param['id'] = $param['id'] ?? 0; + + $res = $this->upstreamRequest(rtrim($param['url'],'/').'/api/v1/product/'.$param['id'], [], 30, 'GET'); + + return ['data' => $res['data']['product'] ?? []]; + } + + public function upstreamProductDownloadResource($param) + { + $param['id'] = $param['id'] ?? 0; + + $res = $this->upstreamRequest(rtrim($param['url'],'/').'/api/v1/product/'.$param['id'].'/resource', [], 30, 'GET'); + $res['data'] = $res['data'] ?? []; + if(!empty($res['data'])){ + if(file_exists(WEB_ROOT."plugins/reserver/".$res['data']['module'].'/'.$res['data']['module'].'_version.txt')){ + $version = file_get_contents(WEB_ROOT."plugins/reserver/".$res['data']['module'].'/'.$res['data']['module'].'_version.txt'); + if(!version_compare($res['data']['version'], $version, '>')){ + return ['status' => 200, 'msg' => lang('success_message'), 'data' => $res['data']]; + } + } + + $dir = WEB_ROOT.'plugins/reserver/'.$res['data']['module'].'.zip'; + $content = $this->curl_download($res['data']['url'], $dir); + if($content){ + $file = WEB_ROOT."plugins/reserver/".$res['data']['module']; + $uuid = $res['data']['module']; + $type = 'reserver'; + $result = $this->unzip($dir,$file); + + if ($result['status'] == 200){ + file_put_contents($file.'/'.$res['data']['module'].'_version.txt', $res['data']['version']); + unlink($dir); + return ['status' => 200, 'msg' => lang('success_message'), 'data' => $res['data']]; + }else{ + return ['status' => 400 , 'msg' => lang('file_unzip_failed', ['{code}' =>$result['msg'], '{file}' => $dir])]; + } + }else{ + return ['status' => 400, 'msg' => lang('resource_download_failed')]; + } + }else{ + return ['status' => 400, 'msg' => lang('upstream_product_resource_get_failed')]; + } + } + + public function upstreamApiAuth($param) + { + $res = $this->upstreamRequest(rtrim($param['url'],'/').'/api/v1/auth', $param, 30, 'POST'); + + return $res; + } + + # 同步产品信息 + public function syncHost($param) + { + $param['host_id'] = $param['host_id'] ?? 0; + if(empty($param['host_id'])){ + return ['status' => 400, 'msg' => lang('fail_message')]; + } + $HostModel = new HostModel(); + $host = $HostModel->find($param['host_id']); + if(empty($host)){ + return ['status' => 400, 'msg' => lang('fail_message')]; + } + + $UpstreamHostModel = new UpstreamHostModel(); + $upstreamHost = $UpstreamHostModel->where('host_id', $param['host_id'])->find(); + if(empty($upstreamHost)){ + return ['status' => 400, 'msg' => lang('fail_message')]; + } + + $UpstreamProductModel = new UpstreamProductModel(); + $product = $UpstreamProductModel->where('product_id', $host['product_id'])->find(); + if(empty($product)){ + return ['status' => 400, 'msg' => lang('fail_message')]; + } + + $SupplierModel = new SupplierModel(); + $supplier = $SupplierModel->where('id', $product['supplier_id'])->find(); + $param['data'] = $this->rsaDecrypt($param['data'], aes_password_decode($supplier['secret'])); + $param['data'] = json_decode($param['data'], true); + if(empty($param['data'])){ + return ['status' => 400, 'msg' => lang('fail_message')]; + } + if($param['data']['action']=='module_create'){ + if($param['data']['host']['status']=='Active'){ + $HostModel->update([ + 'status' => 'Active', + 'update_time' => time() + ], ['id' => $param['host_id']]); + + $UpstreamHostModel->update([ + 'upstream_host_id' => $param['data']['host']['id'], + ], ['host_id' => $param['host_id']]); + + }else if($param['data']['host']['status']=='Failed'){ + $HostModel->update([ + 'status' => 'Failed', + 'update_time' => time() + ], ['id' => $param['host_id']]); + + $UpstreamHostModel->update([ + 'upstream_host_id' => $param['data']['host']['id'], + ], ['host_id' => $param['host_id']]); + + } + upstream_sync_host($param['host_id'], 'module_create'); + }else if($param['data']['action']=='module_suspend'){ + if($param['data']['host']['status']=='Suspended'){ + $HostModel->update([ + 'status' => 'Suspended', + 'suspend_type' => 'upstream', + 'suspend_reason' => '上游暂停', + 'update_time' => time() + ], ['id' => $param['host_id']]); + } + upstream_sync_host($param['host_id'], 'module_suspend'); + }else if($param['data']['action']=='module_unsuspend'){ + if($param['data']['host']['status']=='Active'){ + $HostModel->update([ + 'status' => 'Active', + 'suspend_type' => '', + 'suspend_reason' => '', + 'update_time' => time() + ], ['id' => $param['host_id']]); + } + upstream_sync_host($param['host_id'], 'module_unsuspend'); + }else if($param['data']['action']=='module_terminate'){ + if($param['data']['host']['status']=='Deleted'){ + $HostModel->update([ + 'status' => 'Deleted', + 'update_time' => time() + ], ['id' => $param['host_id']]); + } + upstream_sync_host($param['host_id'], 'module_terminate'); + }else if($param['data']['action']=='update_host'){ + if(in_array($param['data']['host']['status'], ['Active', 'Suspended', 'Deleted'])){ + $HostModel->update([ + 'status' => $param['data']['host']['status'], + 'update_time' => time() + ], ['id' => $param['host_id']]); + } + upstream_sync_host($param['host_id'], 'update_host'); + }else if($param['data']['action']=='delete_host'){ + $HostModel->update([ + 'status' => 'Deleted', + 'update_time' => time() + ], ['id' => $param['host_id']]); + upstream_sync_host($param['host_id'], 'delete_host'); + }else if($param['data']['action']=='host_renew'){ + $HostModel->update([ + 'status' => $param['data']['host']['status'], + 'due_time' => $param['data']['host']['due_time'], + 'update_time' => time() + ], ['id' => $param['host_id']]); + upstream_sync_host($param['host_id'], 'host_renew'); + } + return ['status' => 200, 'msg' => lang('success_message')]; + } + + # 接口请求 + public function upstreamRequest($url, $data, $timeout = 30, $request = 'POST', $header = []) + { + $res = curl($url, $data, $timeout, $request, $header); + + if($res['http_code'] == 200){ + $result = json_decode($res['content'], true); + }else{ + $result['status'] = 400; + $result['msg'] = '请求失败,HTTP状态码:'.$res['http_code']; + } + return $result; + } + + private function rsaDecrypt($encryptData, $rsaPrivateKey){ + + $crypto = ''; + + foreach (str_split(base64_decode($encryptData), 512) as $chunk) { + + openssl_private_decrypt($chunk, $decryptData, $rsaPrivateKey); + + $crypto .= $decryptData; + } + + return $crypto; + } + + private function unzip($filepath,$path) + { + $zip = new \ZipArchive(); + + $res = $zip->open($filepath); + if ( $res === true) { + //解压文件到获得的路径a文件夹下 + if (!file_exists($path)){ + mkdir($path,0777,true); + } + $zip->extractTo($path); + //关闭 + $zip->close(); + return ['status' => 200 , 'msg' => lang('success_message')]; + } else { + return ['status' => 400 , 'msg' => $res]; + } + } + + /* + * curl下载解压包到指定路径 + */ + private function curl_download($url, $file_name) + { + $ch = curl_init($url); + //设置抓取的url + $dir = $file_name; + $fp = fopen($dir, "wb"); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_HEADER, 0); + + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $res=curl_exec($ch); + curl_close($ch); + fclose($fp); + + return $res; + } +} \ No newline at end of file diff --git a/app/common/model/ApiModel.php b/app/common/model/ApiModel.php index 515049c8..41adceac 100755 --- a/app/common/model/ApiModel.php +++ b/app/common/model/ApiModel.php @@ -22,6 +22,8 @@ class ApiModel extends Model 'token' => 'string', 'status' => 'int', 'ip' => 'string', + 'public_key' => 'string', + 'private_key' => 'string', 'create_time' => 'int', 'update_time' => 'int', ]; @@ -117,11 +119,16 @@ public function createApi($param) $this->startTrans(); try { $token = rand_str(32); + + $res = idcsmart_openssl_rsa_key_create(); + $api = $this->create([ 'client_id' => $clientId, 'name' => $param['name'] ?? '', 'token' => aes_password_encode($token), // token加密 'ip' => '', + 'public_key' => aes_password_encode($res['public_key']), + 'private_key' => aes_password_encode($res['private_key']), 'create_time' => time() ]); @@ -134,7 +141,7 @@ public function createApi($param) $this->rollback(); return ['status' => 400, 'msg' => lang('create_fail')]; } - return ['status' => 200, 'msg' => lang('create_success'), 'data' => ['name' => $param['name'] ?? '', 'id' => $api->id, 'token' => $token, 'create_time' => $api->create_time]]; + return ['status' => 200, 'msg' => lang('create_success'), 'data' => ['name' => $param['name'] ?? '', 'id' => $api->id, 'token' => $token, 'private_key' => $res['private_key'], 'create_time' => $api->create_time]]; } /** diff --git a/app/common/model/ClientModel.php b/app/common/model/ClientModel.php index 19c75f59..c4536b1b 100755 --- a/app/common/model/ClientModel.php +++ b/app/common/model/ClientModel.php @@ -2115,4 +2115,80 @@ public function visitClientList($param) return ['list'=>$clients, 'count'=>$count]; } + + /** + * 时间 2023-02-16 + * @title API鉴权登录 + * @desc API鉴权登录 + * @author wyh + * @version v1 + * @url /api/v1/auth + * @method POST + * @param string username - 用户名(用户注册时的邮箱或手机号) + * @param string password - 密码(api信息的token) + */ + public function apiAuth($param) + { + $this->startTrans(); + + try{ + $username = trim($param['username']); + + $password = trim($param['password']); + + if (strpos($username,'@') !== false){ + $client = $this->where('email',$username)->find(); + }else{ + $client = $this->where('phone',$username)->find(); + } + + if (empty($client)){ + throw new \Exception(lang('client_is_not_exist')); + } + + $ApiModel = new ApiModel(); + + $api = $ApiModel->where('client_id',$client['id'])->where('token',aes_password_encode($password))->find(); + + if (empty($api)){ + throw new \Exception(lang('api_auth_fail')); + } + + if ($api['status']==1 && !in_array(get_client_ip(),explode("\n",$api['ip']))){ + throw new \Exception(lang('api_auth_fail')); + } + + /*if (aes_password_encode($password)!=$api['token']){ + throw new \Exception(lang('api_auth_fail')); + }*/ + + $upData = [ + 'last_login_time' => time(), + 'last_login_ip' => get_client_ip() + ]; + + $client->save($upData); + + $info = [ + 'id' => $client['id'], + 'name' => $client['username'], + 'remember_password' => 0, + 'is_api' => true, + 'api_id' => $api['id'], + 'api_name' => $api['name'] + ]; + + active_log(lang('log_api_auth_login',['{client}'=>'client#'.$client['id'].'#'.$client['username'].'#']),'client',$client['id']); + + hook('client_api_login',['id'=>$client['id'],'username'=>$username,'password'=>$password]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + + return ['status'=>400,'msg'=>$e->getMessage()]; + } + + return ['status'=>200,'msg'=>lang('success_message'),'data'=>['jwt'=>create_jwt($info)]]; + } } diff --git a/app/common/model/ConfigurationModel.php b/app/common/model/ConfigurationModel.php index 6aa42fc2..0e769726 100755 --- a/app/common/model/ConfigurationModel.php +++ b/app/common/model/ConfigurationModel.php @@ -93,6 +93,14 @@ class ConfigurationModel extends Model 'certification_update_client_phone', 'certification_uncertified_suspended_host', ], + 'info' => [ + 'put_on_record', + 'enterprise_name', + 'enterprise_telephone', + 'enterprise_mailbox', + 'enterprise_qrcode', + 'online_customer_service_link', + ], ]; /** * 时间 2022-5-10 @@ -856,8 +864,8 @@ public function certificationList() /** * 时间 2022-08-12 - * @title 保存主题设置 - * @desc 保存主题设置 + * @title 保存实名设置 + * @desc 保存实名设置 * @author theworld * @version v1 * @param int certification_open - 实名认证是否开启:1开启默认,0关 required @@ -888,4 +896,63 @@ public function certificationUpdate($param) } return ['status' => 200, 'msg' => lang('update_success')]; } + + /** + * 时间 2023-02-28 + * @title 获取信息配置 + * @desc 获取信息配置 + * @author theworld + * @version v1 + * @return string put_on_record - 备案信息 + * @return string enterprise_name - 企业名称 + * @return string enterprise_telephone - 企业电话 + * @return string enterprise_mailbox - 企业邮箱 + * @return string enterprise_qrcode - 企业二维码 + * @return string online_customer_service_link - 在线客服链接 + */ + public function infoList() + { + $configuration = $this->index(); + $data = []; + foreach($configuration as $v){ + if(in_array($v['setting'], $this->config['info'])){ + $data[$v['setting']] = (string)$v['value']; + } + } + return $data; + } + + /** + * 时间 2023-02-28 + * @title 保存信息配置 + * @desc 保存信息配置 + * @author theworld + * @version v1 + * @param string put_on_record - 备案信息 required + * @param string enterprise_name - 企业名称 required + * @param string enterprise_telephone - 企业电话 required + * @param string enterprise_mailbox - 企业邮箱 required + * @param string enterprise_qrcode - 企业二维码 required + * @param string online_customer_service_link - 在线客服链接 required + */ + public function infoUpdate($param) + { + $this->startTrans(); + try { + foreach($this->config['info'] as $v){ + $list[] = [ + 'setting'=>$v, + 'value'=>$param[$v], + ]; + } + $this->saveAll($list); + + $this->commit(); + } catch (\Exception $e) { + // 回滚事务 + $this->rollback(); + return ['status' => 400, 'msg' => lang('update_fail')]; + } + return ['status' => 200, 'msg' => lang('update_success')]; + } } \ No newline at end of file diff --git a/app/common/model/ConsultModel.php b/app/common/model/ConsultModel.php new file mode 100644 index 00000000..b7b1a7cf --- /dev/null +++ b/app/common/model/ConsultModel.php @@ -0,0 +1,74 @@ + 'int', + 'client_id' => 'int', + 'matter' => 'string', + 'contact' => 'string', + 'company' => 'string', + 'phone' => 'string', + 'email' => 'string', + 'create_time' => 'int', + ]; + + public function consultList($param) + { + + $param['orderby'] = isset($param['orderby']) && in_array($param['orderby'], ['id']) ? 'a.'.$param['orderby'] : 'a.id'; + + $count = $this->alias('a') + ->field('a.id') + ->count(); + + $list = $this->alias('a') + ->field('a.id,a.matter,a.contact,a.company,a.phone,a.email,a.client_id,c.username,a.create_time') + ->leftjoin('client c', 'c.id=a.client_id') + ->limit($param['limit']) + ->page($param['page']) + ->order($param['orderby'], $param['sort']) + ->select() + ->toArray(); + + return ['list' => $list, 'count' => $count]; + } + + public function createConsult($param) + { + $clientId = get_client_id(); + + $this->startTrans(); + try{ + $this->create([ + 'client_id' => $clientId, + 'matter' => $param['matter'], + 'contact' => $param['contact'], + 'company' => $param['company'] ?? '', + 'phone' => $param['phone'] ?? '', + 'email' => $param['email'] ?? '', + 'create_time' => time(), + ]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400, 'msg'=>lang('create_fail')]; + } + + return ['status'=>200, 'msg'=>lang('create_success')]; + } + + +} \ No newline at end of file diff --git a/app/common/model/FeedbackModel.php b/app/common/model/FeedbackModel.php new file mode 100644 index 00000000..a40efed8 --- /dev/null +++ b/app/common/model/FeedbackModel.php @@ -0,0 +1,94 @@ + 'int', + 'client_id' => 'int', + 'feedback_type_id' => 'int', + 'title' => 'string', + 'description' => 'string', + 'contact' => 'string', + 'attachment' => 'string', + 'create_time' => 'int', + ]; + + public function feedbackList($param) + { + + $param['orderby'] = isset($param['orderby']) && in_array($param['orderby'], ['id']) ? 'a.'.$param['orderby'] : 'a.id'; + + $count = $this->alias('a') + ->field('a.id') + ->count(); + $url = request()->domain() . '/upload/common/default/'; + $list = $this->alias('a') + ->field('a.id,a.title,b.name type,a.description,a.client_id,c.username,a.contact,a.attachment,a.create_time') + ->leftjoin('feedback_type b', 'b.id=a.feedback_type_id') + ->leftjoin('client c', 'c.id=a.client_id') + ->withAttr('attachment',function ($value) use ($url){ + $attachment = !empty($value) ? explode(',',$value) : []; + foreach ($attachment as &$vv){ + $vv = $url . $vv; + } + return $attachment; + }) + ->limit($param['limit']) + ->page($param['page']) + ->order($param['orderby'], $param['sort']) + ->select() + ->toArray(); + + return ['list' => $list, 'count' => $count]; + } + + public function createFeedback($param) + { + $clientId = get_client_id(); + + $type = FeedbackTypeModel::find($param['type']); + if(empty($type)){ + return ['status' => 400, 'msg' => lang('feedback_type_is_not_exist')]; + } + $param['attachment'] = $param['attachment'] ?? []; + foreach ($param['attachment'] as $key => $value) { + if(!file_exists(UPLOAD_DEFAULT.$value)){ + return ['status' => 400, 'msg' => lang('upload_file_is_not_exist')]; + } + } + + $this->startTrans(); + try{ + $this->create([ + 'client_id' => $clientId, + 'feedback_type_id' => $param['type'], + 'title' => $param['title'], + 'description' => $param['description'], + 'contact' => $param['contact'] ?? '', + 'attachment' => implode(',', $param['attachment']), + 'create_time' => time(), + ]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400, 'msg'=>lang('create_fail')]; + } + + return ['status'=>200, 'msg'=>lang('create_success')]; + } + +} \ No newline at end of file diff --git a/app/common/model/FeedbackTypeModel.php b/app/common/model/FeedbackTypeModel.php new file mode 100644 index 00000000..12dc9961 --- /dev/null +++ b/app/common/model/FeedbackTypeModel.php @@ -0,0 +1,98 @@ + 'int', + 'name' => 'string', + 'description' => 'string', + 'create_time' => 'int', + 'update_time' => 'int', + ]; + + public function feedbackTypeList() + { + $list = $this->field('id,name,description') + ->select() + ->toArray(); + + return ['list' => $list]; + } + + public function createFeedbackType($param) + { + $this->startTrans(); + try{ + $this->create([ + 'name' => $param['name'], + 'description' => $param['description'], + 'create_time' => time(), + ]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400, 'msg'=>lang('create_fail')]; + } + + return ['status'=>200, 'msg'=>lang('create_success')]; + } + + public function updateFeedbackType($param) + { + $feedbackType = $this->find($param['id']); + if(empty($feedbackType)){ + return ['status'=>400, 'msg'=>lang('feedback_type_is_not_exist')]; + } + + $this->startTrans(); + try{ + $this->update([ + 'name' => $param['name'], + 'description' => $param['description'], + 'update_time' => time(), + ], ['id' => $param['id']]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('update_fail')]; + } + + return ['status'=>200,'msg'=>lang('update_success')]; + } + + public function deleteFeedbackType($id) + { + $feedbackType = $this->find($id); + if(empty($feedbackType)){ + return ['status'=>400, 'msg'=>lang('feedback_type_is_not_exist')]; + } + $count = FeedbackModel::where('feedback_type_id', $id)->count(); + if($count>0){ + return ['status' => 400, 'msg' => lang('cannot_delete_feedback_type')]; + } + + $this->startTrans(); + try{ + $this->destroy($id); + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('delete_fail')]; + } + + return ['status'=>200,'msg'=>lang('delete_success')]; + } +} \ No newline at end of file diff --git a/app/common/model/FriendlyLinkModel.php b/app/common/model/FriendlyLinkModel.php new file mode 100644 index 00000000..0b4fa42a --- /dev/null +++ b/app/common/model/FriendlyLinkModel.php @@ -0,0 +1,94 @@ + 'int', + 'name' => 'string', + 'url' => 'string', + 'create_time' => 'int', + 'update_time' => 'int', + ]; + + public function friendlyLinkList() + { + $list = $this->field('id,name,url') + ->select() + ->toArray(); + + return ['list' => $list]; + } + + public function createFriendlyLink($param) + { + $this->startTrans(); + try{ + $this->create([ + 'name' => $param['name'], + 'url' => $param['url'], + 'create_time' => time(), + ]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400, 'msg'=>lang('create_fail')]; + } + + return ['status'=>200, 'msg'=>lang('create_success')]; + } + + public function updateFriendlyLink($param) + { + $friendlyLink = $this->find($param['id']); + if(empty($friendlyLink)){ + return ['status'=>400, 'msg'=>lang('friendly_link_is_not_exist')]; + } + + $this->startTrans(); + try{ + $this->update([ + 'name' => $param['name'], + 'url' => $param['url'], + 'update_time' => time(), + ], ['id' => $param['id']]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('update_fail')]; + } + + return ['status'=>200,'msg'=>lang('update_success')]; + } + + public function deleteFriendlyLink($id) + { + $friendlyLink = $this->find($id); + if(empty($friendlyLink)){ + return ['status'=>400, 'msg'=>lang('friendly_link_is_not_exist')]; + } + + $this->startTrans(); + try{ + $this->destroy($id); + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('delete_fail')]; + } + + return ['status'=>200,'msg'=>lang('delete_success')]; + } +} \ No newline at end of file diff --git a/app/common/model/HonorModel.php b/app/common/model/HonorModel.php new file mode 100644 index 00000000..23b18178 --- /dev/null +++ b/app/common/model/HonorModel.php @@ -0,0 +1,94 @@ + 'int', + 'name' => 'string', + 'img' => 'string', + 'create_time' => 'int', + 'update_time' => 'int', + ]; + + public function honorList() + { + $list = $this->field('id,name,img') + ->select() + ->toArray(); + + return ['list' => $list]; + } + + public function createHonor($param) + { + $this->startTrans(); + try{ + $this->create([ + 'name' => $param['name'], + 'img' => $param['img'], + 'create_time' => time(), + ]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400, 'msg'=>lang('create_fail')]; + } + + return ['status'=>200, 'msg'=>lang('create_success')]; + } + + public function updateHonor($param) + { + $honor = $this->find($param['id']); + if(empty($honor)){ + return ['status'=>400, 'msg'=>lang('honor_is_not_exist')]; + } + + $this->startTrans(); + try{ + $this->update([ + 'name' => $param['name'], + 'img' => $param['img'], + 'update_time' => time(), + ], ['id' => $param['id']]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('update_fail')]; + } + + return ['status'=>200,'msg'=>lang('update_success')]; + } + + public function deleteHonor($id) + { + $honor = $this->find($id); + if(empty($honor)){ + return ['status'=>400, 'msg'=>lang('honor_is_not_exist')]; + } + + $this->startTrans(); + try{ + $this->destroy($id); + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('delete_fail')]; + } + + return ['status'=>200,'msg'=>lang('delete_success')]; + } +} \ No newline at end of file diff --git a/app/common/model/HostModel.php b/app/common/model/HostModel.php index fca4f36e..2974b051 100755 --- a/app/common/model/HostModel.php +++ b/app/common/model/HostModel.php @@ -1,6 +1,7 @@ 'int', 'create_time' => 'int', 'update_time' => 'int', + 'downstream_info' => 'string', + 'downstream_host_id' => 'int', ]; /** @@ -303,6 +306,7 @@ public function indexHost($id) } $product = ProductModel::find($host['product_id']); + $upstreamHost = UpstreamHostModel::where('host_id', $host['id'])->find(); // 产品的用户ID和前台用户不一致时返回空对象 if($app=='home'){ @@ -319,6 +323,7 @@ public function indexHost($id) $host['first_payment_amount'] = amount_format($host['first_payment_amount']); $host['renew_amount'] = amount_format($host['renew_amount']); $host['product_name'] = $product['name'] ?? ''; + $host['agent'] = !empty($upstreamHost) ? 1 : 0; unset($host['client_id']); return $host; @@ -474,13 +479,15 @@ public function updateHost($param) 'update_time' => time() ], ['id' => $param['id']]); + upstream_sync_host($param['id'], 'update_host'); + if(!empty($description)) active_log(lang('admin_modify_host', ['{admin}'=>request()->admin_name, '{host}'=>'host#'.$host->id.'#'.$param['name'].'#', '{description}'=>$description]), 'host', $host->id); $this->commit(); } catch (\Exception $e) { // 回滚事务 $this->rollback(); - return ['status' => 400, 'msg' => lang('update_fail')]; + return ['status' => 400, 'msg' => lang('update_fail').$e->getMessage()]; } hook('after_host_edit',['id'=>$param['id'],'customfield'=>$param['customfield']??[]]); @@ -531,6 +538,9 @@ public function deleteHost($param) OrderModel::update(['amount'=>$amount],['id'=>$host['order_id']]); } } + UpstreamHostModel::where('host_id', $id)->delete(); + + upstream_sync_host($id, 'delete_host'); $this->destroy($id); $this->commit(); @@ -576,6 +586,10 @@ public function batchDeleteHost($param) $this->startTrans(); try { foreach ($host as $key => $value) { + upstream_sync_host($value['id'], 'delete_host'); + + UpstreamHostModel::where('host_id', $value['id'])->delete(); + $this->destroy($value['id']); hook('after_host_delete',['id'=>$value['id']]); @@ -586,6 +600,7 @@ public function batchDeleteHost($param) }else{ $clientName = 'client#'.$client['id'].'#'.$client['username'].'#'; } + # 记录日志 active_log(lang('admin_batch_delete_user_host', ['{admin}'=>request()->admin_name, '{client}'=>$clientName, '{host}'=>$value['name']]), 'host', $value['id']); } @@ -667,25 +682,45 @@ public function createAccount($id) return ['status'=>400, 'msg'=>lang('host_is_suspended')]; } + hook('before_host_create',['id'=>$id]); - $ModuleLogic = new ModuleLogic(); - $res = $ModuleLogic->createAccount($host); + if($host['billing_cycle']=='onetime'){ + $due_time = 0; + }else if($host['billing_cycle']=='free' && $host['billing_cycle_time']==0){ + $due_time = 0; + }else{ + $due_time = time() + $host['billing_cycle_time']; + } + $this->update([ + 'active_time' => time(), + 'due_time' => $due_time, + 'update_time' => time(), + ], ['id'=>$id]); + + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $res = $ResModuleLogic->createAccount($host); + }else{ + $ModuleLogic = new ModuleLogic(); + $res = $ModuleLogic->createAccount($host); + } if($res['status'] == 200){ hook('after_host_create_success',['id'=>$id]); - if($host['billing_cycle']=='onetime'){ + /*if($host['billing_cycle']=='onetime'){ $due_time = 0; }else if($host['billing_cycle']=='free' && $host['billing_cycle_time']==0){ $due_time = 0; }else{ $due_time = time() + $host['billing_cycle_time']; - } + }*/ $this->update([ 'status' => 'Active', - 'active_time' => time(), - 'due_time' => $due_time, + /*'active_time' => time(), + 'due_time' => $due_time,*/ 'update_time' => time(), ], ['id'=>$id]); @@ -727,6 +762,8 @@ public function createAccount($id) '{reason}'=>$res['msg'] ?? '', ]); } + + upstream_sync_host($id, 'module_create'); active_log($description, 'host', $host->id); return $res; } @@ -738,7 +775,7 @@ public function createAccount($id) * @author hh * @version v1 * @param int id - 产品ID require - * @param string param.suspend_type overdue 暂停类型(overdue=到期暂停,overtraffic=超流暂停,certification_not_complete=实名未完成,other=其他) + * @param string param.suspend_type overdue 暂停类型(overdue=到期暂停,overtraffic=超流暂停,certification_not_complete=实名未完成,other=其他,downstream下游暂停) * @param string param.suspend_reason - 暂停原因 * @return int status - 状态码,200=成功,400=失败 * @return string msg - 提示信息 @@ -759,11 +796,18 @@ public function suspendAccount($param) if($host['status'] != 'Active'){ return ['status'=>400, 'msg'=>lang('host_is_not_active_cannot_suspend')]; } + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); hook('before_host_suspend',['id'=>$id]); - $ModuleLogic = new ModuleLogic(); - $res = $ModuleLogic->suspendAccount($host, $param); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $res = $ResModuleLogic->suspendAccount($host, $param); + }else{ + $ModuleLogic = new ModuleLogic(); + $res = $ModuleLogic->suspendAccount($host, $param); + } + if($res['status'] == 200){ hook('after_host_suspend_success',['id'=>$id]); @@ -799,6 +843,8 @@ public function suspendAccount($param) 'other'=>'其他', ]; + upstream_sync_host($id, 'module_suspend'); + $description = lang('log_module_suspend_account_success', [ '{host}'=>'host#'.$host->id.'#'.$host['name'].'#', '{type}'=>$suspendType[ $param['suspend_type'] ] ?? $suspendType['overdue'], @@ -840,11 +886,21 @@ public function unsuspendAccount($id) if($host['status'] != 'Active' && $host['status'] != 'Suspended'){ return ['status'=>400, 'msg'=>lang('host_status_not_need_unsuspend')]; } + if($host['suspend_type'] == 'upstream'){ + return ['status'=>400, 'msg'=>lang('不可解除上游发起的暂停')]; + } + + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); hook('before_host_unsuspend',['id'=>$id]); - $ModuleLogic = new ModuleLogic(); - $res = $ModuleLogic->unsuspendAccount($host); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $res = $ResModuleLogic->unsuspendAccount($host); + }else{ + $ModuleLogic = new ModuleLogic(); + $res = $ModuleLogic->unsuspendAccount($host); + } if($res['status'] == 200){ hook('after_host_unsuspend_success',['id'=>$id]); @@ -873,6 +929,7 @@ public function unsuspendAccount($id) ], ]); } + upstream_sync_host($id, 'module_unsuspend'); $description = lang('log_module_unsuspend_account_success', [ '{host}'=>'host#'.$host->id.'#'.$host['name'].'#', @@ -905,12 +962,18 @@ public function terminateAccount($id) if(empty($host)){ return ['status'=>400, 'msg'=>lang('host_is_not_exist')]; } + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); hook('before_host_terminate',['id'=>$id]); // 暂不判断状态,所有状态应该都能删除 - $ModuleLogic = new ModuleLogic(); - $res = $ModuleLogic->terminateAccount($host); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $res = $ResModuleLogic->terminateAccount($host); + }else{ + $ModuleLogic = new ModuleLogic(); + $res = $ModuleLogic->terminateAccount($host); + } if($res['status'] == 200){ hook('after_host_terminate_success',['id'=>$id]); @@ -937,6 +1000,8 @@ public function terminateAccount($id) ], ]); + upstream_sync_host($id, 'module_terminate'); + $description = lang('log_module_terminate_account_success', [ '{host}'=>'host#'.$host->id.'#'.$host['name'].'#', ]); @@ -969,9 +1034,15 @@ public function adminArea($id) if(empty($host)){ return ['status'=>400, 'msg'=>lang('host_is_not_exist')]; } + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->adminArea($host); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $content = $ResModuleLogic->adminArea($host); + }else{ + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->adminArea($host); + } $result = [ 'status' => 200, @@ -1002,8 +1073,15 @@ public function menuHostList($id) } $param['product_id'] = json_decode($menu['product_id'], true); - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->hostList($menu['module'], $param); + /*$upstreamProduct = UpstreamProductModel::where('product_id', $param['product_id'][0] ?? 0)->find();*/ + + if($menu['menu_type']=='res_module'){ + $ResModuleLogic = new ResModuleLogic(); + $content = $ResModuleLogic->hostList($menu['module'], $param); + }else{ + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->hostList($menu['module'], $param); + } $result = [ 'status' => 200, @@ -1042,9 +1120,15 @@ public function clientArea($id) if(isset($hostId) && !in_array($id, $hostId)){ return ['status'=>400, 'msg'=>lang('host_is_not_exist')]; } + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->clientArea($host); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $content = $ResModuleLogic->clientArea($host); + }else{ + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->clientArea($host); + } $result = [ 'status' => 200, @@ -1073,9 +1157,15 @@ public function adminChangeConfigOption($id) if(empty($host)){ return ['status'=>400, 'msg'=>lang('host_is_not_exist')]; } - - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->adminChangeConfigOption($host); + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $content = $ResModuleLogic->adminChangeConfigOption($host); + }else{ + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->adminChangeConfigOption($host); + } $result = [ 'status' => 200, @@ -1105,8 +1195,15 @@ public function clientChangeConfigOption($id) return ['status'=>400, 'msg'=>lang('host_is_not_exist')]; } - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->clientChangeConfigOption($host); + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $content = $ResModuleLogic->clientChangeConfigOption($host); + }else{ + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->clientChangeConfigOption($host); + } $result = [ 'status' => 200, @@ -1170,10 +1267,12 @@ public function upgradeAccount($id) return false; } + $host = $this->find($upgrade['host_id']); + $upstreamProduct = UpstreamProductModel::where('product_id', $host['product_id'])->find(); # 升降级 if($upgrade['type']=='product'){ // 获取接口 - $product = ProductModel::find($upgrade['rel_id']); + /*$product = ProductModel::find($upgrade['rel_id']); if($product['type']=='server_group'){ $server = ServerModel::where('server_group_id', $product['rel_id'])->where('status', 1)->find(); $serverId = $server['id'] ?? 0; @@ -1218,19 +1317,27 @@ public function upgradeAccount($id) 'billing_cycle_name' => $upgrade['billing_cycle_name'], 'billing_cycle_time' => $upgrade['billing_cycle_time'], 'due_time' => $hostDueTime, - ],['id' => $upgrade['host_id']]); - $ModuleLogic = new ModuleLogic(); - $host = $this->find($upgrade['host_id']); - $ModuleLogic->changeProduct($host, json_decode($upgrade['data'], true)); + ],['id' => $upgrade['host_id']]);*/ + + if($upstreamProduct){ + + }else{ + $ModuleLogic = new ModuleLogic(); + $ModuleLogic->changeProduct($host, json_decode($upgrade['data'], true)); + } }else if($upgrade['type']=='config_option'){ - $host = $this->find($upgrade['host_id']); + /*$host = $this->find($upgrade['host_id']); $this->update([ 'first_payment_amount' => $upgrade['price'], 'renew_amount' => ($host['billing_cycle']=='recurring_postpaid' || $host['billing_cycle']=='recurring_prepayment') ? $upgrade['renew_price'] : 0, - ],['id' => $upgrade['host_id']]); - $ModuleLogic = new ModuleLogic(); - $host = $this->find($upgrade['host_id']); - $ModuleLogic->changePackage($host, json_decode($upgrade['data'], true)); + ],['id' => $upgrade['host_id']]);*/ + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $ResModuleLogic->changePackage($host, json_decode($upgrade['data'], true)); + }else{ + $ModuleLogic = new ModuleLogic(); + $ModuleLogic->changePackage($host, json_decode($upgrade['data'], true)); + } } $ProductModel = new ProductModel(); @@ -1426,6 +1533,41 @@ public function moduleAdminButton($param){ return $result; } + public function upstreamSyncHost($id, $action = '') + { + $host = $this->find($id); + if(empty($host)){ + return false; + } + if(empty($host['downstream_host_id'])){ + return false; + } + $downstreamInfo = json_decode($host['downstream_info'], true) ?? []; + if(empty($downstreamInfo)){ + return false; + } + $api = ApiModel::find($downstreamInfo['api'] ?? 0); + if(empty($api)){ + return false; + } + $api['public_key'] = aes_password_decode($api['public_key']); + + $data = json_encode(['action' => $action, 'host' => $host]); + + $crypto = ''; + + foreach (str_split($data, 117) as $chunk) { + + openssl_public_encrypt($chunk, $encryptData, $api['public_key']); + + $crypto .= $encryptData; + } + + $data = base64_encode($crypto); + $res = curl(rtrim($downstreamInfo['url'],'/').'/console/v1/upstream/sync', ['host_id' => $host['downstream_host_id'], 'data' => $data], 30, 'POST'); + + return true; + } } diff --git a/app/common/model/MenuModel.php b/app/common/model/MenuModel.php index 9f58c220..c3054b7d 100755 --- a/app/common/model/MenuModel.php +++ b/app/common/model/MenuModel.php @@ -6,6 +6,7 @@ use app\admin\model\PluginModel; use app\admin\model\AuthModel; use app\common\logic\ModuleLogic; +use app\common\logic\ResModuleLogic; /** * @title 导航管理模型 @@ -182,6 +183,10 @@ public function getHomeMenu() $module = $ModuleLogic->getModuleList(); + $ResModuleLogic = new ResModuleLogic(); + + $resModule = $ResModuleLogic->getModuleList(); + $menus = $this->field('id,menu_type type,name,language,url,icon,nav_id,parent_id,module,product_id') ->where('type', 'home') ->order('order','asc') @@ -227,7 +232,7 @@ public function getHomeMenu() } } } - return ['menu' => $tree, 'language' => lang_list('home'), 'system_nav' => $navs, 'plugin_nav' => $plugins, 'module' => $module]; + return ['menu' => $tree, 'language' => lang_list('home'), 'system_nav' => $navs, 'plugin_nav' => $plugins, 'module' => $module, 'res_module' => $resModule]; } /** @@ -325,10 +330,13 @@ public function saveAdminMenu($param) public function saveHomeMenu($param) { $ModuleLogic = new ModuleLogic(); - $module = $ModuleLogic->getModuleList(); $module = array_column($module, 'name'); + $ResModuleLogic = new ResModuleLogic(); + $resModule = $ResModuleLogic->getModuleList(); + $resModule = array_column($resModule, 'name'); + foreach ($param['menu'] as $key => $value) { if(in_array($value['type'], ['system', 'plugin'])){ $nav = NavModel::find($value['nav_id']); @@ -352,6 +360,18 @@ public function saveHomeMenu($param) if(count(array_diff($value['product_id'], $products))>0){ return ['status' => 400, 'msg' => lang('product_error')]; } + }else if($value['type']=='res_module'){ + if(!in_array($value['module'], $resModule)){ + return ['status' => 400, 'msg' => lang('module_error')]; + } + $products = ProductModel::alias('p') + ->leftjoin('upstream_product up','up.product_id=p.id') + ->where('p.hidden', 0) + ->where('up.res_module', $value['module']) + ->column('p.id'); + if(count(array_diff($value['product_id'], $products))>0){ + return ['status' => 400, 'msg' => lang('product_error')]; + } } if(isset($value['child'])){ foreach ($value['child'] as $k => $v) { @@ -377,6 +397,18 @@ public function saveHomeMenu($param) if(count(array_diff($v['product_id'], $products))>0){ return ['status' => 400, 'msg' => lang('product_error')]; } + }else if($v['type']=='res_module'){ + if(!in_array($v['module'], $resModule)){ + return ['status' => 400, 'msg' => lang('module_error')]; + } + $products = ProductModel::alias('p') + ->leftjoin('upstream_product up','up.product_id=p.id') + ->where('p.hidden', 0) + ->where('up.res_module', $v['module']) + ->column('p.id'); + if(count(array_diff($v['product_id'], $products))>0){ + return ['status' => 400, 'msg' => lang('product_error')]; + } } } } @@ -611,7 +643,7 @@ public function homeMenu(){ $data['language'] = json_decode($data['language'], true); $menus[$key]['name'] = $data['language'][$language] ?? $data['name']; $menus[$key]['url'] = $data['nav_url'] ?? $data['url']; - if($data['menu_type']=='module'){ + if($data['menu_type']=='module' || $data['menu_type']=='res_module'){ $menus[$key]['url'] = 'product.html?m='.$data['id']; } diff --git a/app/common/model/OrderModel.php b/app/common/model/OrderModel.php index d3891249..837fc74f 100755 --- a/app/common/model/OrderModel.php +++ b/app/common/model/OrderModel.php @@ -6,6 +6,7 @@ use think\Model; use app\admin\model\PluginModel; use app\common\logic\ModuleLogic; +use app\common\logic\ResModuleLogic; use app\common\model\NoticeSettingModel; /** @@ -313,9 +314,10 @@ public function indexOrder($id) //unset($order['client_id']); $orderItems = OrderItemModel::alias('oi') - ->field('oi.id,oi.type,oi.description,oi.amount,h.id host_id,p.name product_name,h.name host_name,h.billing_cycle,h.billing_cycle_name,h.status host_status') + ->field('oi.id,oi.type,oi.description,oi.amount,h.id host_id,p.name product_name,h.name host_name,h.billing_cycle,h.billing_cycle_name,h.status host_status,uo.id upstream_order_id,uo.profit') ->leftjoin('host h',"h.id=oi.host_id") ->leftjoin('product p',"p.id=oi.product_id") + ->leftjoin('upstream_order uo',"uo.host_id=oi.host_id AND uo.order_id=oi.order_id") ->where('oi.order_id', $id) ->select() ->toArray(); @@ -327,6 +329,8 @@ public function indexOrder($id) $orderItems[$key]['host_name'] = $orderItem['host_name'] ?? ''; // 处理空数据 $orderItems[$key]['billing_cycle'] = $orderItem['billing_cycle_name']; // 处理空数据 $orderItems[$key]['host_status'] = $orderItem['host_status'] ?? ''; // 处理空数据 + $orderItems[$key]['profit'] = amount_format($orderItem['profit']); // 处理金额格式 + $orderItems[$key]['agent'] = !empty($orderItem['upstream_order_id']) ? 1 : 0; if(in_array($orderItem['type'], ['addon_promo_code', 'addon_idcsmart_promo_code', 'addon_idcsmart_client_level'])){ $orderItems[$key]['product_name'] = $orderItem['description']; @@ -350,8 +354,10 @@ public function indexOrder($id) if($app!='home'){ $orderItems[$key]['edit'] = $order['status']=='Unpaid' ? ($orderItem['type']=='manual' ? 1 : 0) : 0; + }else{ + unset($orderItems[$key]['profit'], $orderItems[$key]['agent']); } - unset($orderItems[$key]['billing_cycle_name'], $orderItems[$key]['type']); + unset($orderItems[$key]['billing_cycle_name'], $orderItems[$key]['type'], $orderItems[$key]['upstream_order_id']); } $order['items'] = $orderItems; @@ -447,8 +453,14 @@ private function createNewOrder($param) } $value['config_options'] = $value['config_options'] ?? []; - $result = $ModuleLogic->cartCalculatePrice($product, $value['config_options']); + $upstreamProduct = UpstreamProductModel::where('product_id', $value['product_id'])->find(); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($product, $value['config_options']); + }else{ + $result = $ModuleLogic->cartCalculatePrice($product, $value['config_options']); + } if($result['status']!=200){ return $result; } @@ -523,7 +535,14 @@ private function createNewOrder($param) // 产品和对应自定义字段 $param['customfield']['host_customfield'][] = ['id'=>$host->id, 'customfield' => $value['customfield'] ?? []]; - $ModuleLogic->afterSettle($product, $host->id, $value['config_options']); + $upstreamProduct = UpstreamProductModel::where('product_id', $value['product_id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $ResModuleLogic->afterSettle($product, $host->id, $value['config_options']); + }else{ + $ModuleLogic->afterSettle($product, $host->id, $value['config_options']); + } $orderItem[] = [ 'order_id' => $order->id, 'client_id' => $clientId, @@ -544,6 +563,8 @@ private function createNewOrder($param) hook('after_order_create',['id'=>$order->id,'customfield'=>$param['customfield']??[]]); + update_upstream_order_profit($order->id); + # 金额从数据库重新获取,hook里可能会修改金额,wyh改 20220804 $amount = $this->where('id',$order->id)->value('amount'); @@ -605,9 +626,15 @@ public function getUpgradeAmount($param) return ['status'=>400, 'msg'=>lang('product_is_not_exist')]; } - $ModuleLogic = new ModuleLogic(); - $result = $ModuleLogic->cartCalculatePrice($product, $param['product']['config_options']); + $upstreamProduct = UpstreamProductModel::where('product_id', $product['id'])->find(); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($product, $param['product']['config_options']); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->cartCalculatePrice($product, $param['product']['config_options']); + } if($result['status']!=200){ return $result; } @@ -691,8 +718,15 @@ public function createUpgradeOrder($param) return ['status'=>400, 'msg'=>lang('product_is_not_exist')]; } - $ModuleLogic = new ModuleLogic(); - $result = $ModuleLogic->cartCalculatePrice($product, $param['product']['config_options']); + $upstreamProduct = UpstreamProductModel::where('product_id', $product['id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($product, $param['product']['config_options']); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->cartCalculatePrice($product, $param['product']['config_options']); + } if($result['status']!=200){ return $result; @@ -822,6 +856,8 @@ public function createUpgradeOrder($param) hook('after_order_create',['id'=>$order->id,'customfield'=>$param['customfield']??[]]); + update_upstream_order_profit($order->id); + # 金额从数据库重新获取,hook里可能会修改金额,wyh改 20220804 $amount = $this->where('id',$order->id)->value('amount'); @@ -976,6 +1012,8 @@ public function createUpgradeConfigOrder($param) hook('after_order_create',['id'=>$order->id,'customfield'=>$param['customfield']??[]]); + update_upstream_order_profit($order->id); + # 金额从数据库重新获取,hook里可能会修改金额,wyh改 20220804 $amount = $this->where('id',$order->id)->value('amount'); @@ -1199,6 +1237,8 @@ public function updateAmount($param) 'order_id'=>$param['id'],//订单ID ], ]); + + update_upstream_order_profit($order->id); $this->commit(); } catch (\Exception $e) { @@ -1287,6 +1327,8 @@ public function updateOrderItem($param) 'order_id'=>$param['id'],//订单ID ], ]); + + update_upstream_order_profit($order->id); $this->commit(); } catch (\Exception $e) { @@ -1365,6 +1407,8 @@ public function deleteOrderItem($id) 'order_id'=>$id,//订单ID ], ]); + + update_upstream_order_profit($order->id); $this->commit(); } catch (\Exception $e) { @@ -1523,6 +1567,10 @@ public function deleteOrder($param) OrderItemModel::destroy(function($query) use($id){ $query->where('order_id', $id); }); + // 删除上游订单 + UpstreamOrderModel::destroy(function($query) use($id){ + $query->where('order_id', $id); + }); if($delete_host==1){ // 删除订单产品 HostModel::destroy(function($query) use($id){ @@ -2025,6 +2073,53 @@ public function updateNotes($param) return ['status' => 200, 'msg' => lang('update_success')]; } + # 更新上游订单利润 + public function updateUpstreamOrderProfit($id){ + $OrderItemModel = new OrderItemModel(); + $orderItems = $OrderItemModel->where('order_id', $id)->select()->toArray(); + $upstreamOrder = UpstreamOrderModel::where('order_id', $id)->select()->toArray(); + if(!empty($upstreamOrder)){ + $hostAmountArr = []; + $hostReduceArr = []; + $amount = 0; + $reduce = 0; + foreach ($orderItems as $key => $value) { + if($value['amount']>=0){ + $amount += $value['amount']; + if(!empty($value['host_id'])){ + $hostAmountArr[$value['host_id']] = ($hostAmountArr[$value['host_id']] ?? 0) + $value['amount']; + } + }else{ + if(!empty($value['host_id'])){ + $hostReduceArr[$value['host_id']] = ($hostReduceArr[$value['host_id']] ?? 0) + $value['amount']; + }else{ + $reduce += $value['amount']; + } + } + + } + + foreach ($hostAmountArr as $key => $value) { + if($amount>0){ + $value = bcadd($value + ($hostReduceArr[$key] ?? 0), ($value/$amount*$reduce), 2); + }else{ + $value = 0; + } + $hostAmountArr[$key] = $value; + } + + foreach ($upstreamOrder as $key => $value) { + $value['original_price'] = $value['amount'] - $value['profit']; + $amount = $hostAmountArr[$value['host_id']] ?? 0; + UpstreamOrderModel::update([ + 'amount' => $amount, + 'profit' => bcsub($amount, $value['original_price'], 2) + ], ['id' => $value['id']]); + } + } + return true; + } + # 处理已支付订单 public function processPaidOrder($id) { @@ -2071,7 +2166,7 @@ public function processPaidOrder($id) ], ]); } - + update_upstream_order_profit($id); foreach($orderItems as $orderItem){ $type = $orderItem['type']; @@ -2214,6 +2309,62 @@ public function upgradeOrderHandle($id) 'update_time' => time() ], ['id' => $id]); + # 升降级 + if($upgrade['type']=='product'){ + // 获取接口 + $product = ProductModel::find($upgrade['rel_id']); + if($product['type']=='server_group'){ + $server = ServerModel::where('server_group_id', $product['rel_id'])->where('status', 1)->find(); + $serverId = $server['id'] ?? 0; + }else{ + $serverId = $product['rel_id']; + } + + $host = $this->find($upgrade['host_id']); + // wyh 20210109 改 一次性/免费可升级后 + if($host['billing_cycle']=='onetime'){ + if ($product['pay_type']=='onetime'){ + $hostDueTime = 0; + }elseif ($product['pay_type']=='free' && $upgrade['billing_cycle_time']==0){ + $hostDueTime = 0; + }else{ + $hostDueTime = time()+$upgrade['billing_cycle_time']; + } + }else if($host['billing_cycle']=='free' && $host['billing_cycle_time']==0){ + if ($product['pay_type']=='onetime'){ + $hostDueTime = 0; + }elseif ($product['pay_type']=='free' && $upgrade['billing_cycle_time']==0){ + $hostDueTime = 0; + }else{ + $hostDueTime = time()+$upgrade['billing_cycle_time']; + } + }else{ + if ($product['pay_type']=='onetime'){ + $hostDueTime = 0; + }elseif ($product['pay_type']=='free' && $upgrade['billing_cycle_time']==0){ + $hostDueTime = 0; + }else{ # 周期到周期,不变更 + $hostDueTime = $host['due_time']; + } + } + + HostModel::update([ + 'product_id' => $upgrade['rel_id'], + 'server_id' => $serverId, + 'first_payment_amount' => $upgrade['price'], + 'renew_amount' => ($product['pay_type']=='recurring_postpaid' || $product['pay_type']=='recurring_prepayment') ? $upgrade['renew_price'] : 0, + 'billing_cycle' => $product['pay_type'], + 'billing_cycle_name' => $upgrade['billing_cycle_name'], + 'billing_cycle_time' => $upgrade['billing_cycle_time'], + 'due_time' => $hostDueTime, + ],['id' => $upgrade['host_id']]); + }else if($upgrade['type']=='config_option'){ + $host = HostModel::find($upgrade['host_id']); + HostModel::update([ + 'first_payment_amount' => $upgrade['price'], + 'renew_amount' => ($host['billing_cycle']=='recurring_postpaid' || $host['billing_cycle']=='recurring_prepayment') ? $upgrade['renew_price'] : 0, + ],['id' => $upgrade['host_id']]); + } # 添加到定时任务 add_task([ diff --git a/app/common/model/OrderTmpModel.php b/app/common/model/OrderTmpModel.php index 363debbb..f6c2f519 100755 --- a/app/common/model/OrderTmpModel.php +++ b/app/common/model/OrderTmpModel.php @@ -90,13 +90,13 @@ public function pay($param) $client = $ClientModel->find($order->client_id); # 客户余额足够 - if ($client->credit >= $order->credit){ + if (($client->credit >= $order->credit && $order->amount_unpaid <= 0) || ($client->credit >= $order->amount && $param['gateway']=='credit')){ $this->startTrans(); try{ $order->save([ 'gateway' => $gateway, - 'gateway_name' => $plugin['title']??'', + 'gateway_name' => $plugin['title']??'余额支付', 'status' => 'Paid', 'pay_time' => time() ]); @@ -114,7 +114,7 @@ public function pay($param) $OrderItemModel = new OrderItemModel(); $OrderItemModel->update([ 'gateway' => $gateway, - 'gateway_name' => $plugin['title']??'', + 'gateway_name' => $plugin['title']??'余额支付', ],['order_id'=>$order->id]); $this->commit(); diff --git a/app/common/model/PartnerModel.php b/app/common/model/PartnerModel.php new file mode 100644 index 00000000..c15c47bd --- /dev/null +++ b/app/common/model/PartnerModel.php @@ -0,0 +1,97 @@ + 'int', + 'name' => 'string', + 'img' => 'string', + 'description' => 'string', + 'create_time' => 'int', + 'update_time' => 'int', + ]; + + public function partnerList() + { + $list = $this->field('id,name,img,description') + ->select() + ->toArray(); + + return ['list' => $list]; + } + + public function createPartner($param) + { + $this->startTrans(); + try{ + $this->create([ + 'name' => $param['name'], + 'img' => $param['img'], + 'description' => $param['description'], + 'create_time' => time(), + ]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400, 'msg'=>lang('create_fail')]; + } + + return ['status'=>200, 'msg'=>lang('create_success')]; + } + + public function updatePartner($param) + { + $partner = $this->find($param['id']); + if(empty($partner)){ + return ['status'=>400, 'msg'=>lang('partner_is_not_exist')]; + } + + $this->startTrans(); + try{ + $this->update([ + 'name' => $param['name'], + 'img' => $param['img'], + 'description' => $param['description'], + 'update_time' => time(), + ], ['id' => $param['id']]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('update_fail')]; + } + + return ['status'=>200,'msg'=>lang('update_success')]; + } + + public function deletePartner($id) + { + $partner = $this->find($id); + if(empty($partner)){ + return ['status'=>400, 'msg'=>lang('partner_is_not_exist')]; + } + + $this->startTrans(); + try{ + $this->destroy($id); + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('delete_fail')]; + } + + return ['status'=>200,'msg'=>lang('delete_success')]; + } +} \ No newline at end of file diff --git a/app/common/model/ProductModel.php b/app/common/model/ProductModel.php index febdecd1..31eb2768 100755 --- a/app/common/model/ProductModel.php +++ b/app/common/model/ProductModel.php @@ -6,6 +6,7 @@ use think\facade\Db; use think\Model; use app\common\logic\ModuleLogic; +use app\common\logic\ResModuleLogic; /** * @title 商品模型 @@ -47,6 +48,7 @@ class ProductModel extends Model 'update_time' => 'int', 'price' => 'float', 'cycle' => 'string', + 'agentable' => 'int', ]; /** @@ -116,7 +118,7 @@ public function productList($param) } return $value; }) - ->whereIn('s.module|ss.module',['idcsmart_common','common_cloud','idcsmart_dcim','baidu_cloud','room_box','idcsmart_common_finance','idcsmart_common_dcim','idcsmart_common_cloud','idcsmart_common_business','idcsmart_cert','idcsmart_email','idcsmart_sms','zjmfapp','dcimapp']) + /*->whereIn('s.module|ss.module',['idcsmart_common','common_cloud','idcsmart_dcim','baidu_cloud','room_box','idcsmart_common_finance','idcsmart_common_dcim','idcsmart_common_cloud','idcsmart_common_business','idcsmart_cert','idcsmart_email','idcsmart_sms','zjmfapp','dcimapp','mf_cloud','mf_dcim'])*/ ->where($where) ->limit((isset($param['limit']) && !empty($param['limit']))?intval($param['limit']):1000000) ->page((isset($param['page']) && !empty($param['page']))?intval($param['page']):1) @@ -144,9 +146,9 @@ public function productList($param) ->leftjoin('server s','p.type=\'server\' AND p.rel_id=s.id') ->leftjoin('server_group sg','p.type=\'server_group\' AND p.rel_id=sg.id') ->leftjoin('server ss','ss.server_group_id=sg.id') - ->whereIn('s.module|ss.module',['idcsmart_common','common_cloud','idcsmart_dcim','baidu_cloud','room_box','idcsmart_common_finance','idcsmart_common_dcim','idcsmart_common_cloud','idcsmart_common_business','idcsmart_cert','idcsmart_email','idcsmart_sms','zjmfapp','dcimapp']) + /*->whereIn('s.module|ss.module',['idcsmart_common','common_cloud','idcsmart_dcim','baidu_cloud','room_box','idcsmart_common_finance','idcsmart_common_dcim','idcsmart_common_cloud','idcsmart_common_business','idcsmart_cert','idcsmart_email','idcsmart_sms','zjmfapp','dcimapp','mf_cloud','mf_dcim'])*/ ->where($where) - ->count(); + ->count(); return ['list'=>$products,'count'=>$count]; } @@ -315,7 +317,7 @@ public function indexProduct($id) $product = $this->field('id,name,product_group_id,description,hidden,stock_control,qty, creating_notice_sms,creating_notice_sms_api,creating_notice_sms_api_template,created_notice_sms, created_notice_sms_api,created_notice_sms_api_template,creating_notice_mail,creating_notice_mail_api,creating_notice_mail_template, - created_notice_mail,created_notice_mail_api,created_notice_mail_template,pay_type,auto_setup,type,rel_id,product_id') + created_notice_mail,created_notice_mail_api,created_notice_mail_template,pay_type,auto_setup,type,rel_id,product_id,price,cycle') ->find($id); if (!empty($product->description)){ @@ -328,7 +330,7 @@ public function indexProduct($id) if (!empty($product)){ $product['upgrade'] = $upgradeProducts; if($app=='home'){ - $product = ['id' => $product['id'], 'name' => $product['name']]; + $product = ['id' => $product['id'], 'name' => $product['name'], 'pay_type' => $product['pay_type'], 'price' => $product['price'], 'cycle' => $product['cycle']]; } } @@ -406,6 +408,9 @@ public function createProduct($param) $maxOrder = $this->max('order'); + // 双删 + idcsmart_cache('product:list',null); + $this->startTrans(); try{ @@ -427,6 +432,8 @@ public function createProduct($param) return ['status'=>400,'msg'=>lang('create_fail')]; } + idcsmart_cache('product:list',null); + hook('after_product_create',['id'=>$product->id,'customfield'=>$param['customfield']??[]]); return ['status'=>200,'msg'=>lang('create_success'),'data' => ['product_id' => $product->id]]; @@ -619,6 +626,9 @@ public function updateProduct($param) $logDescription .= ',' . lang('log_admin_update_product_upgrade_product',['{old}'=>implode(',',array_column($old,'upgrade_product_id')),'{new}'=>implode(',',$upgradeIds)]); } + // 双删 + idcsmart_cache('product:list',null); + $this->startTrans(); try{ @@ -670,9 +680,13 @@ public function updateProduct($param) return ['status'=>400,'msg'=>lang('update_fail') . ':' . $e->getMessage()]; } - $ModuleLogic = new ModuleLogic(); - $priceCycle = $ModuleLogic->getPriceCycle($product->id); - $this->setPriceCycle($priceCycle['product'], $priceCycle['price'], $priceCycle['cycle']); + $upstreamProduct = UpstreamProductModel::where('product_id', $id)->find(); + if(empty($upstreamProduct)){ + $ModuleLogic = new ModuleLogic(); + $priceCycle = $ModuleLogic->getPriceCycle($product->id); + $this->setPriceCycle($priceCycle['product'], $priceCycle['price'], $priceCycle['cycle']); + } + idcsmart_cache('product:list',null); hook('after_product_edit',['id'=>$product->id,'customfield'=>$param['customfield']??[]]); @@ -768,6 +782,8 @@ public function deleteProduct($id) return ['status'=>400,'msg'=>lang('product_has_host')]; } + idcsmart_cache('product:list',null); + $this->startTrans(); try{ # 删除商品 @@ -791,6 +807,8 @@ public function deleteProduct($id) $MenuModel = new MenuModel(); $MenuModel->deleteHomeModuleMenu($id); + UpstreamProductModel::where('product_id', $id)->delete(); + # 记录日志 active_log(lang('log_admin_delete_product',['{admin}'=>'admin#'.get_admin_id().'#'.request()->admin_name.'#','{product}'=>'product#'.$id.'#'.$product['name'].'#']),'product',$id); @@ -799,6 +817,9 @@ public function deleteProduct($id) $this->rollback(); return ['status'=>400,'msg'=>lang('delete_fail') . $e->getMessage()]; } + + idcsmart_cache('product:list',null); + hook('after_product_delete', ['id'=>$id]); return ['status'=>200,'msg'=>lang('delete_success')]; @@ -830,6 +851,8 @@ public function hiddenProduct($param) return ['status'=>400,'msg'=>lang('cannot_repeat_opreate')]; } + idcsmart_cache('product:list',null); + $this->startTrans(); try{ $this->update([ @@ -850,6 +873,8 @@ public function hiddenProduct($param) return ['status'=>400,'msg'=>lang('fail_message')]; } + idcsmart_cache('product:list',null); + return ['status'=>200,'msg'=>lang('success_message')]; } @@ -897,6 +922,9 @@ public function orderProduct($param) if (empty($productGroup)){ return ['status'=>400,'msg'=>lang('product_group_is_not_exist')]; } + + idcsmart_cache('product:list',null); + # 排序处理 $this->startTrans(); try{ @@ -956,6 +984,8 @@ public function orderProduct($param) return ['status'=>400,'msg'=>lang('move_fail') . ':' . $e->getMessage()]; } + idcsmart_cache('product:list',null); + return ['status'=>200,'msg'=>lang('move_success')]; } @@ -1024,12 +1054,17 @@ public function moduleServerConfigOption($param){ if(empty($ProductModel)){ return ['status'=>400, 'msg'=>lang('product_is_not_exist')]; } - // 商品 - // $oldModule = $ProductModel->getModule(); - $newModule = $this->getModule($param['rel_id'], $param['type']); + $content = ''; + $upstreamProduct = UpstreamProductModel::where('product_id', $ProductModel['product_id'])->find(); - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->serverConfigOption($newModule, $ProductModel); + if(empty($upstreamProduct)){ + // 商品 + // $oldModule = $ProductModel->getModule(); + $newModule = $this->getModule($param['rel_id'], $param['type']); + + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->serverConfigOption($newModule, $ProductModel); + } $result = [ 'status' => 200, @@ -1061,8 +1096,15 @@ public function moduleClientConfigOption($param){ } $param['tag'] = $param['tag'] ?? ''; - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->clientProductConfigOption($ProductModel, $param['tag']); + $upstreamProduct = UpstreamProductModel::where('product_id', $ProductModel['id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $content = $ResModuleLogic->clientProductConfigOption($ProductModel, $param['tag']); + }else{ + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->clientProductConfigOption($ProductModel, $param['tag']); + } $result = [ 'status'=> 200, @@ -1095,8 +1137,15 @@ public function moduleAdminConfigOption($param){ } $param['tag'] = $param['tag'] ?? ''; - $ModuleLogic = new ModuleLogic(); - $content = $ModuleLogic->adminProductConfigOption($ProductModel, $param['tag']); + $upstreamProduct = UpstreamProductModel::where('product_id', $ProductModel['id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $content = $ResModuleLogic->adminProductConfigOption($ProductModel, $param['tag']); + }else{ + $ModuleLogic = new ModuleLogic(); + $content = $ModuleLogic->adminProductConfigOption($ProductModel, $param['tag']); + } $result = [ 'status'=> 200, @@ -1145,6 +1194,7 @@ public function settle($param) $param['config_options'] = $param['config_options'] ?? []; $clientId = get_client_id(); + $certification = check_certification($clientId); $result = hook('before_order_create', ['client_id'=>$clientId, 'param' => $param]); @@ -1154,8 +1204,18 @@ public function settle($param) } } - $ModuleLogic = new ModuleLogic(); - $result = $ModuleLogic->cartCalculatePrice($product, $param['config_options'],$param['qty']); + $upstreamProduct = UpstreamProductModel::where('product_id', $product['id'])->find(); + + if($upstreamProduct){ + if($upstreamProduct['certification']==1 && !$certification){ + return ['status'=>400, 'msg'=>lang('certification_uncertified_cannot_buy_product')]; + } + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($product, $param['config_options'],$param['qty']); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->cartCalculatePrice($product, $param['config_options'],$param['qty']); + } if($result['status']!=200){ return $result; @@ -1170,6 +1230,9 @@ public function settle($param) $param['duration'] = $result['data']['duration']; $param['description'] = $result['data']['description']; $param['config_options'] = $param['config_options'] ?? []; + if($upstreamProduct){ + $param['profit'] = $result['data']['profit'] ?? 0; + } if(empty($param['description'])){ if($product['pay_type']=='recurring_postpaid' || $product['pay_type']=='recurring_prepayment'){ @@ -1215,6 +1278,8 @@ public function settle($param) }else{ $serverId = $product['rel_id']; } + + $upstreamProduct = UpstreamProductModel::where('product_id', $param['product_id'])->find(); for ($i=1; $i<=$param['qty']; $i++) { $host = HostModel::create([ 'client_id' => $clientId, @@ -1232,7 +1297,27 @@ public function settle($param) 'due_time' => $product['pay_type']!='onetime' ? $time : 0, 'create_time' => $time ]); - $ModuleLogic->afterSettle($product, $host->id, $param['config_options']); + + if($upstreamProduct){ + UpstreamHostModel::create([ + 'supplier_id' => $upstreamProduct['supplier_id'], + 'host_id' => $host->id, + 'upstream_configoption' => json_encode($param['config_options']), + 'create_time' => $time + ]); + UpstreamOrderModel::create([ + 'supplier_id' => $upstreamProduct['supplier_id'], + 'order_id' => $order->id, + 'host_id' => $host->id, + 'amount' => $param['price'], + 'profit' => $param['profit'], + 'create_time' => $time + ]); + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $ResModuleLogic->afterSettle($product, $host->id, $param['config_options']); + }else{ + $ModuleLogic->afterSettle($product, $host->id, $param['config_options']); + } $orderItem[] = [ 'order_id' => $order->id, 'client_id' => $clientId, @@ -1252,6 +1337,8 @@ public function settle($param) hook('after_order_create',['id'=>$order->id,'customfield'=>$param['customfield']??[]]); + update_upstream_order_profit($order->id); + $OrderModel = new OrderModel(); # 金额从数据库重新获取,hook里可能会修改金额,wyh改 20220804 $amount = $OrderModel->where('id',$order->id)->value('amount'); @@ -1300,8 +1387,18 @@ public function productCalculatePrice($param) } $param['config_options'] = $param['config_options'] ?? []; - $ModuleLogic = new ModuleLogic(); - return $ModuleLogic->cartCalculatePrice($ProductModel, $param['config_options'], 1, 'cal_price'); + $upstreamProduct = UpstreamProductModel::where('product_id', $ProductModel['id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($ProductModel, $param['config_options'], 1, 'cal_price'); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->cartCalculatePrice($ProductModel, $param['config_options'], 1, 'cal_price'); + } + if(isset($result['data']['profit'])) unset($result['data']['profit']); + + return $result; } /** @@ -1326,8 +1423,16 @@ public function productAllConfigOption($id) return ['status'=>400, 'msg'=>lang('product_is_not_exist')]; } - $ModuleLogic = new ModuleLogic(); - return $ModuleLogic->allConfigOption($ProductModel); + $upstreamProduct = UpstreamProductModel::where('product_id', $ProductModel['id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->allConfigOption($ProductModel); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->allConfigOption($ProductModel); + } + return $result; } /** @@ -1402,12 +1507,13 @@ public function productListSearch($param) if($app=='home'){ $field = 'p.id,p.name'; }else{ - $field = 'p.id,p.name,p.stock_control,p.qty,p.hidden,p.pay_type,pg.name as product_group_name_second,pg.id as product_group_id_second,pgf.name as product_group_name_first,pgf.id as product_group_id_first'; + $field = 'p.id,p.name,p.stock_control,p.qty,p.hidden,p.pay_type,pg.name as product_group_name_second,pg.id as product_group_id_second,pgf.name as product_group_name_first,pgf.id as product_group_id_first,up.id upstream_product_id,p.agentable'; } $products = $this->alias('p') ->field($field) ->leftjoin('product_group pg','p.product_group_id=pg.id') ->leftjoin('product_group pgf','pg.parent_id=pgf.id') + ->leftjoin('upstream_product up','up.product_id=p.id') ->where($where) ->limit((isset($param['limit']) && !empty($param['limit']))?intval($param['limit']):1000000) ->page((isset($param['page']) && !empty($param['page']))?intval($param['page']):1) @@ -1420,6 +1526,12 @@ public function productListSearch($param) ->alias('p') ->where($where) ->count(); + if($app!='home'){ + foreach ($products as $key => $value) { + $products[$key]['agent'] = !empty($value['upstream_product_id']) ? 1 : 0; + unset($products[$key]['upstream_product_id']); + } + } return ['list'=>$products, 'count'=>$count]; } @@ -1470,4 +1582,132 @@ public function setPriceCycle($id = null, $price = null, $cycle = null){ return $update; } + /** + * 时间 2023-02-16 + * @title 获取上游模块资源 + * @desc 获取上游模块资源 + * @author hh + * @version v1 + * @param int param.id - 商品ID require + * @return string module - resmodule名称 + * @return string url - zip包完整下载路径 + */ + public function downloadResource($param){ + $ProductModel = $this->find((int)$param['id']); + if(empty($ProductModel)){ + return ['status'=>400, 'msg'=>lang('product_is_not_exist')]; + } + $upstreamProduct = UpstreamProductModel::where('product_id', $ProductModel['id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->downloadResource($ProductModel); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->downloadResource($ProductModel); + } + return $result; + } + + /** + * 时间 2023-02-20 + * @title 保存可代理商品 + * @desc 保存可代理商品 + * @author theworld + * @version v1 + * @param array param.id - 商品ID require + * @return int status - 状态码,200成功,400失败 + * @return string msg - 提示信息 + */ + public function saveAgentableProduct($param) + { + $param['id'] = $param['id'] ?? []; + if(!is_array($param['id']) || empty($param['id'])){ + return ['status'=>400,'msg'=>lang('param_error')]; + } + $product = $this->whereIn('id', $param['id'])->select()->toArray(); + if (empty($product)){ + return ['status'=>400,'msg'=>lang('param_error')]; + } + + idcsmart_cache('product:list',null); + + $this->startTrans(); + try{ + $this->where('agentable', 1)->update(['agentable' => 0]); + $this->whereIn('id', $param['id'])->update(['agentable' => 1]); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('fail_message') . $e->getMessage()]; + } + + idcsmart_cache('product:list',null); + + return ['status'=>200,'msg'=>lang('success_message')]; + } + + + /** + * 时间 2022-10-12 + * @title 根据模块获取商品列表 + * @desc 根据模块获取商品列表 + * @author theworld + * @version v1 + * @param string param.module - 模块名称 + * @return array list - 一级分组列表 + * @return int list[].id - 一级分组ID + * @return string list[].name - 一级分组名称 + * @return array list[].child - 二级分组 + * @return int list[].child[].id - 二级分组ID + * @return string list[].child[].name - 二级分组名称 + * @return array list[].child[].child - 商品 + * @return int list[].child[].child[].id - 商品ID + * @return string list[].child[].child[].name - 商品名称 + */ + public function resModuleProductList($param) + { + $where = function (Query $query) use($param) { + $query->where('p.hidden', 0)->where('up.res_module', '<>', ''); + if(!empty($param['module'])){ + $query->where('up.res_module', $param['module']); + } + }; + + $ProductGroupModel = new ProductGroupModel(); + $firstGroup = $ProductGroupModel->productGroupFirstList(); + $firstGroup = $firstGroup['list']; + + $secondGroup = $ProductGroupModel->productGroupSecondList([]); + $secondGroup = $secondGroup['list']; + + $products = $this->alias('p') + ->field('p.id,p.name,p.product_group_id') + ->leftjoin('upstream_product up','up.product_id=p.id') + ->where($where) + ->order('p.order','desc') + ->select() + ->toArray(); + $productArr = []; + foreach ($products as $key => $value) { + $productArr[$value['product_group_id']][] = ['id' => $value['id'], 'name' => $value['name']]; + } + $secondGroupArr = []; + foreach ($secondGroup as $key => $value) { + if(isset($productArr[$value['id']])){ + $secondGroupArr[$value['parent_id']][] = ['id' => $value['id'], 'name' => $value['name'], 'child' => $productArr[$value['id']]]; + } + } + $list = []; + foreach ($firstGroup as $key => $value) { + if(isset($secondGroupArr[$value['id']])){ + $list[] = ['id' => $value['id'], 'name' => $value['name'], 'child' => $secondGroupArr[$value['id']]]; + } + } + + return ['list'=>$list]; + } + + } diff --git a/app/common/model/SupplierModel.php b/app/common/model/SupplierModel.php new file mode 100644 index 00000000..da3fc63c --- /dev/null +++ b/app/common/model/SupplierModel.php @@ -0,0 +1,265 @@ + 'int', + 'name' => 'string', + 'url' => 'string', + 'username' => 'string', + 'token' => 'string', + 'secret' => 'string', + 'contact' => 'string', + 'notes' => 'string', + 'create_time' => 'int', + 'update_time' => 'int', + ]; + + # 供应商列表 + public function supplierList($param) + { + $param['keywords'] = $param['keywords'] ?? ''; + $param['orderby'] = isset($param['orderby']) && in_array($param['orderby'], ['id']) ? 'a.'.$param['orderby'] : 'a.id'; + + $where = function (Query $query) use ($param){ + if (!empty($param['keywords'])){ + $query->where('a.name|a.url','like',"%{$param['keywords']}%"); + } + }; + $count = $this->alias('a') + ->field('a.id') + ->where($where) + ->count(); + + + + $list = $this->alias('a') + ->field('a.id,a.name,a.url') + ->where($where) + ->group('a.id') + ->limit($param['limit']) + ->page($param['page']) + ->order($param['orderby'], $param['sort']) + ->select() + ->toArray(); + $supplierId = array_column($list, 'id'); + $productNum = UpstreamProductModel::field('COUNT(id) num,supplier_id')->whereIn('supplier_id', $supplierId)->group('supplier_id')->select()->toArray();; + $productNum = array_column($productNum, 'num', 'supplier_id'); + $hostNum = UpstreamHostModel::field('COUNT(id) num,supplier_id')->whereIn('supplier_id', $supplierId)->group('supplier_id')->select()->toArray();; + $hostNum = array_column($hostNum, 'num', 'supplier_id'); + + foreach ($list as $key => $value) { + $list[$key]['host_num'] = $hostNum[$value['id']] ?? 0; + $list[$key]['product_num'] = $productNum[$value['id']] ?? 0; + } + + return ['list' => $list, 'count' => $count]; + } + + # 供应商详情 + public function indexSupplier($id) + { + $supplier = $this->field('id,name,url,username,token,secret,contact,notes')->find($id); + if(empty($supplier)){ + return (object)[]; + } + $supplier['token'] = aes_password_decode($supplier['token']); + $supplier['secret'] = aes_password_decode($supplier['secret']); + return $supplier; + } + + # 添加供应商 + public function createSupplier($param) + { + + $this->startTrans(); + try{ + $supplier = $this->create([ + 'name' => $param['name'], + 'url' => $param['url'], + 'username' => $param['username'], + 'token' => aes_password_encode($param['token']), + 'secret' => aes_password_encode(str_replace("\r\n", "\n", $param['secret'])), + 'contact' => $param['contact'] ?? '', + 'notes' => $param['notes'] ?? '', + 'create_time' => time(), + ]); + + # 记录日志 + active_log(lang('log_create_supplier',['{admin}'=>request()->admin_name,'{name}'=>$param['name']]), 'supplier', $supplier->id); + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400, 'msg'=>lang('create_fail')]; + } + + return ['status'=>200, 'msg'=>lang('create_success')]; + } + + # 编辑供应商 + public function updateSupplier($param) + { + $supplier = $this->find($param['id']); + if(empty($supplier)){ + return ['status' => 400, 'msg' => lang('supplier_is_not_exist')]; + } + $supplier = $supplier->toArray(); + $token = aes_password_decode($supplier['token']); + $secret = aes_password_decode($supplier['secret']); + unset($supplier['token'],$supplier['secret']); + # 日志描述 + $logDescription = log_description($supplier,$param,'supplier'); + if($token!=$param['token']){ + $logDescription = $logDescription.','.lang('log_supplier_token'); + } + if($secret!=$param['secret']){ + $logDescription = $logDescription.','.lang('log_supplier_secret'); + } + $logDescription = ltrim(',', $logDescription); + + $this->startTrans(); + try{ + $this->update([ + 'name' => $param['name'], + 'url' => $param['url'], + 'username' => $param['username'], + 'token' => aes_password_encode($param['token']), + 'secret' => aes_password_encode(str_replace("\r\n", "\n", $param['secret'])), + 'contact' => $param['contact'] ?? '', + 'notes' => $param['notes'] ?? '', + 'update_time' => time(), + ], ['id' => $param['id']]); + + # 记录日志 + active_log(lang('log_update_supplier',['{admin}'=>request()->admin_name,'{name}'=>$param['name'],'{description}'=>$logDescription]),'supplier',$supplier['id']); + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('update_fail')]; + } + + return ['status'=>200,'msg'=>lang('update_success')]; + } + + # 删除供应商 + public function deleteSupplier($id) + { + $supplier = $this->find($id); + if(empty($supplier)){ + return ['status' => 400, 'msg' => lang('supplier_is_not_exist')]; + } + $productCount = UpstreamProductModel::where('supplier_id', $id)->count(); + if($productCount>0){ + return ['status' => 400, 'msg' => lang('cannot_delete_supplier')]; + } + + $this->startTrans(); + try{ + $this->destroy($id); + + # 记录日志 + active_log(lang('log_delete_supplier',['{admin}'=>request()->admin_name,'{name}'=>$supplier['name']]),'supplier',$supplier->id); + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('delete_fail')]; + } + + return ['status'=>200,'msg'=>lang('delete_success')]; + } + + # 检查供应商接口连接状态 + public function supplierStatus($id) + { + $supplier = $this->find($id); + if(empty($supplier)){ + return ['status' => 400, 'msg' => lang('supplier_is_not_exist')]; + } + // 从上游商品详情拉取 + $UpstreamLogic = new UpstreamLogic(); + $res = $UpstreamLogic->upstreamApiAuth(['url' => $supplier['url'], 'username' => $supplier['username'], 'password' => aes_password_decode($supplier['token'])]); + if($res['status']==400){ + return ['status'=>400,'msg'=>$res['msg']]; + } + return ['status'=>200,'msg'=>lang('success_message')]; + } + + # 获取供应商商品列表 + public function supplierProduct($id) + { + $supplier = $this->find($id); + if(empty($supplier)){ + return ['list' => [], 'count' => 0]; + } + // 从上游商品详情拉取 + $UpstreamLogic = new UpstreamLogic(); + $res = $UpstreamLogic->upstreamProductList(['url' => $supplier['url']]); + return $res; + } + + public function apiAuth($api_id,$force) + { + $this->startTrans(); + + try{ + $path='api/v1/auth'; + + $supplier = $this->where('id',$api_id)->find(); + if (empty($supplier)){ + throw new \Exception(lang('supplier_is_not_exist')); + } + $url = rtrim($supplier['url'],'/'); + + $key = 'api_auth_login_' . AUTHCODE . '_' . $api_id; + + $jwt = idcsmart_cache($key); + + if (empty($jwt) || $force){ + + $apiUrl = $url . '/' . $path; + + $data = [ + 'username' => $supplier['username'], + 'password' => aes_password_decode($supplier['token']) + ]; + + $result = curl($apiUrl,$data); + if ($result['http_code']!=200){ + idcsmart_cache($key,null); + throw new \Exception($result['content']??"api_auth_fail"); + } + $result = json_decode($result['content'], true); + + if($result['status'] == 200){ + $jwt = $result['data']['jwt']; + + idcsmart_cache($key,$jwt,2*3600); + }else{ + throw new \Exception($result['msg']??"api_auth_fail"); + } + } + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + + return ['status'=>400,'msg'=>$e->getMessage()]; + } + + return ['status'=>200,'msg'=>lang('success_message'),'data'=>['jwt'=>$jwt,'url'=>$url]]; + } +} \ No newline at end of file diff --git a/app/common/model/UpstreamHostModel.php b/app/common/model/UpstreamHostModel.php new file mode 100644 index 00000000..ef02697a --- /dev/null +++ b/app/common/model/UpstreamHostModel.php @@ -0,0 +1,96 @@ + 'int', + 'supplier_id' => 'int', + 'host_id' => 'int', + 'upstream_host_id' => 'int', + 'upstream_info' => 'string', + 'upstream_configoption' => 'string', + 'create_time' => 'int', + 'update_time' => 'int', + ]; + + # 产品列表 + public function hostList($param) + { + if (!isset($param['orderby']) || !in_array($param['orderby'],['id'])){ + $param['orderby'] = 'h.id'; + }else{ + $param['orderby'] = 'h.'.$param['orderby']; + } + $param['keywords'] = $param['keywords'] ?? ''; + $param['supplier_id'] = intval($param['supplier_id'] ?? 0); + + $where = function (Query $query) use($param) { + $query->where('h.id', '>', 0); + if(!empty($param['keywords'])){ + $query->where('h.id|h.name|c.username|c.email|c.phone|p.name', 'like', "%{$param['keywords']}%"); + } + if(!empty($param['supplier_id'])){ + $query->where('a.supplier_id', $param['supplier_id']); + } + }; + + $count = $this->alias('a') + ->field('h.id') + ->leftJoin('host h','h.id=a.host_id') + ->leftjoin('client c','h.client_id=c.id') + ->leftjoin('product p','p.id=h.product_id') + ->where($where) + ->count(); + + $hosts = $this->alias('a') + ->field('h.id,h.name,p.name product_name,h.status,h.first_payment_amount,h.renew_amount,h.billing_cycle_name,h.billing_cycle,h.due_time,h.client_id,c.username,c.company,c.email,c.phone_code,c.phone') + ->leftJoin('host h','h.id=a.host_id') + ->leftjoin('client c','h.client_id=c.id') + ->leftjoin('product p','p.id=h.product_id') + ->where($where) + ->limit($param['limit']) + ->page($param['page']) + ->order($param['orderby'], $param['sort']) + ->select() + ->toArray(); + + foreach ($hosts as $key => $value) { + $hosts[$key]['first_payment_amount'] = amount_format($value['first_payment_amount']); + $hosts[$key]['billing_cycle'] = $value['billing_cycle']!='onetime' ? $value['billing_cycle_name'] : ''; + unset($hosts[$key]['billing_cycle_name']); + } + + return ['list'=>$hosts,'count'=>$count]; + + } + + # 产品详情 + public function indexHost($id) + { + $host = $this->where('host_id', $id)->find(); + if(empty($host)){ + return (object)[]; + } + + $res = idcsmart_api_curl($host['supplier_id'], 'console/v1/host/'.$host['upstream_host_id'], [], 30, 'GET'); + if(!isset($res['data'])){ + return (object)[]; + } + $host = $res['data']; + + return $host; + } +} \ No newline at end of file diff --git a/app/common/model/UpstreamOrderModel.php b/app/common/model/UpstreamOrderModel.php new file mode 100644 index 00000000..5ad042bc --- /dev/null +++ b/app/common/model/UpstreamOrderModel.php @@ -0,0 +1,194 @@ + 'int', + 'supplier_id' => 'int', + 'order_id' => 'int', + 'host_id' => 'int', + 'amount' => 'float', + 'profit' => 'float', + 'create_time' => 'int', + 'update_time' => 'int', + ]; + + # 订单列表 + public function orderList($param) + { + $param['keywords'] = $param['keywords'] ?? ''; + $param['supplier_id'] = intval($param['supplier_id'] ?? 0); + /*$param['type'] = $param['type'] ?? ''; + $param['status'] = $param['status'] ?? '';*/ + $param['orderby'] = isset($param['orderby']) && in_array($param['orderby'], ['id', 'type', 'create_time', 'amount', 'status']) ? 'o.'.$param['orderby'] : 'o.id'; + + $where = function (Query $query) use ($param){ + if (!empty($param['keywords'])){ + $query->where('o.id|c.username|c.email|c.phone|p.name','like',"%{$param['keywords']}%"); + } + if(!empty($param['supplier_id'])){ + $query->where('a.supplier_id', $param['supplier_id']); + } + }; + + $count = $this->alias('a') + ->field('a.id') + ->leftjoin('order o', 'o.id=a.order_id') + ->leftjoin('client c', 'c.id=o.client_id') + ->leftjoin('host h', 'h.id=a.host_id') + ->leftjoin('product p', 'p.id=h.product_id') + ->where($where) + ->group('o.id') + ->count(); + $orders = $this->alias('a') + ->field('o.id,o.type,o.create_time,o.amount,sum(a.profit) profit,o.status,o.gateway_name gateway,o.credit,o.client_id,c.username client_name,c.email,c.phone_code,c.phone,c.company,p.name product_name') + ->leftjoin('order o', 'o.id=a.order_id') + ->leftjoin('client c', 'c.id=o.client_id') + ->leftjoin('host h', 'h.id=a.host_id') + ->leftjoin('product p', 'p.id=h.product_id') + ->where($where) + ->limit($param['limit']) + ->page($param['page']) + ->order($param['orderby'], $param['sort']) + ->group('o.id') + ->select() + ->toArray(); + $orderId = array_column($orders, 'id'); + + $orderItems = OrderItemModel::alias('oi') + ->field('oi.order_id,oi.type,h.id,h.name,h.billing_cycle,h.billing_cycle_name,p.name product_name,oi.description') + ->leftjoin('host h',"h.id=oi.host_id") + ->leftjoin('product p',"p.id=oi.product_id") + ->whereIn('oi.order_id', $orderId) + ->select() + ->toArray(); + $orderItemCount = []; + $names = []; + $billingCycles = []; + $productNames = []; + $descriptions = []; + $hostIds = []; + foreach ($orderItems as $key => $orderItem) { + + // wyh 20230130 有问题就注释 + $description = explode("\n",$orderItem['description']); + if (!empty($description)){ + $newDes = ''; + foreach ($description as $item1){ + if (count(explode('=>',$item1))==4){ + $arr = explode('=>',$item1); + $itemDes = $arr[0] . ':' . $arr[1] . $arr[2] . $arr[3]; + $newDes = $newDes.$itemDes . "\n"; + }else{ + $newDes = $newDes . $item1 . "\n"; + } + } + $orderItem['description'] = trim($newDes,"\n"); + } + + $orderItemCount[$orderItem['order_id']] = $orderItemCount[$orderItem['order_id']] ?? 0; + $orderItemCount[$orderItem['order_id']]++; + // 获取产品ID + if(!empty($orderItem['id'])){ + $hostIds[$orderItem['order_id']][] = $orderItem['id']; + } + // 获取产品名称 + $names[$orderItem['order_id']][] = $orderItem['name']; + // 获取产品计费周期 + $billingCycles[$orderItem['order_id']][] = $orderItem['billing_cycle_name']; + // 获取商品名称 + if(in_array($orderItem['type'], ['addon_promo_code', 'addon_idcsmart_promo_code', 'addon_idcsmart_client_level'])){ + $productNames[$orderItem['order_id']][] = $orderItem['description']; + }else if(!empty($orderItem['product_name'])){ + $productNames[$orderItem['order_id']][] = $orderItem['product_name']; + }else{ + $productNames[$orderItem['order_id']][] = $orderItem['description']; + } + // 获取商品名称 + if(!empty($orderItem['description'])){ + $descriptions[$orderItem['order_id']][] = $orderItem['description']; + } + } + + foreach ($orders as $key => $order) { + $orders[$key]['amount'] = amount_format($order['amount']); // 处理金额格式 + $orders[$key]['profit'] = amount_format($order['profit']); // 处理金额格式 + + // 获取产品标识,产品标识不一致是返回空字符串 + if($order['type']=='artificial'){ + $orders[$key]['host_name'] = $descriptions[$order['id']] ?? []; + }else{ + $orders[$key]['host_name'] = $names[$order['id']] ?? []; + } + if(!empty($orders[$key]['host_name']) && count($orders[$key]['host_name'])==1){ + $orders[$key]['host_name'] = $orders[$key]['host_name'][0] ?? ''; + }else{ + $orders[$key]['host_name'] = ''; + } + $orders[$key]['description'] = $descriptions[$order['id']] ?? []; + if(!empty($orders[$key]['description']) && count($orders[$key]['description'])==1){ + $orders[$key]['description'] = $orders[$key]['description'][0] ?? ''; + }else{ + $orders[$key]['description'] = ''; + } + + + // 获取计费周期,计费周期不一致是返回空字符串 + /*$billingCycle = isset($billingCycles[$order['id']]) ? array_values(array_unique($billingCycles[$order['id']])) : []; + if(!empty($billingCycle) && count($billingCycle)==1){ + $orders[$key]['billing_cycle'] = $billingCycle[0] ?? ''; + }else{ + $orders[$key]['billing_cycle'] = ''; + }*/ + + // 获取商品名称 + $orders[$key]['product_names'] = $productNames[$order['id']] ?? []; + + if(count($orders[$key]['product_names'])==1){ + $orders[$key]['host_id'] = $hostIds[$order['id']][0] ?? 0; + }else{ + $orders[$key]['host_id'] = 0; + } + + $orders[$key]['order_item_count'] = $orderItemCount[$order['id']] ?? 0; + } + + + return ['list' => $orders, 'count' => $count]; + + } + + # 销售信息 + public function sellInfo($param) + { + $where = function (Query $query) use ($param){ + if (!empty($param['supplier_id'])){ + $query->where('a.supplier_id', $param['supplier_id']); + } + }; + $total = $this->alias('a')->leftjoin('order o', 'o.id=a.order_id')->where('o.status', 'Paid')->where($where)->sum('a.amount'); + $profit = $this->alias('a')->leftjoin('order o', 'o.id=a.order_id')->where('o.status', 'Paid')->where($where)->sum('a.profit'); + $productCount = UpstreamProductModel::alias('a')->leftjoin('product p', 'p.id=a.product_id')->where('p.id', '>', 0)->where($where)->count(); + $hostCount = UpstreamHostModel::alias('a')->leftjoin('host h', 'h.id=a.host_id')->where('h.id', '>', 0)->where($where)->count(); + + return [ + 'total' => amount_format($total), + 'profit' => amount_format($profit), + 'product_count' => $productCount, + 'host_count' => $hostCount, + ]; + } +} \ No newline at end of file diff --git a/app/common/model/UpstreamProductModel.php b/app/common/model/UpstreamProductModel.php new file mode 100644 index 00000000..d2ac2fd8 --- /dev/null +++ b/app/common/model/UpstreamProductModel.php @@ -0,0 +1,354 @@ + 'int', + 'supplier_id' => 'int', + 'product_id' => 'int', + 'upstream_product_id' => 'int', + 'profit_percent' => 'float', + 'certification' => 'int', + 'create_time' => 'int', + 'update_time' => 'int', + 'res_module' => 'string', + ]; + + # 商品列表 + public function productList($param) + { + if (!isset($param['orderby']) || !in_array($param['orderby'],['id'])){ + $param['orderby'] = 'p.id'; + }else{ + $param['orderby'] = 'p.'.$param['orderby']; + } + $param['keywords'] = $param['keywords'] ?? ''; + $param['supplier_id'] = intval($param['supplier_id'] ?? 0); + + $where = function (Query $query) use($param) { + $query->where('p.id', '>', 0); + if(!empty($param['keywords'])){ + $query->where('p.id|p.name|p.description', 'like', "%{$param['keywords']}%"); + } + if(!empty($param['supplier_id'])){ + $query->where('a.supplier_id', $param['supplier_id']); + } + }; + + $count = $this->alias('a') + ->field('p.id') + ->leftJoin('product p','p.id=a.product_id') + ->leftjoin('product_group pg','p.product_group_id=pg.id') + ->leftjoin('product_group pgf','pg.parent_id=pgf.id') + ->leftjoin('supplier s','a.supplier_id=s.id') + ->where($where) + ->count(); + + $products = $this->alias('a') + ->field('p.id,p.name,p.description,a.supplier_id,s.name supplier_name,a.profit_percent,p.auto_setup,p.hidden,p.pay_type,p.price,p.cycle,a.upstream_product_id,a.certification,pg.name as product_group_name_second,pg.id as product_group_id_second,pgf.name as product_group_name_first,pgf.id as product_group_id_first') + ->leftJoin('product p','p.id=a.product_id') + ->leftjoin('product_group pg','p.product_group_id=pg.id') + ->leftjoin('product_group pgf','pg.parent_id=pgf.id') + ->leftjoin('supplier s','a.supplier_id=s.id') + ->where($where) + ->limit($param['limit']) + ->page($param['page']) + ->order($param['orderby'], $param['sort']) + ->select() + ->toArray(); + + foreach ($products as $key => $value) { + $products[$key]['price'] = amount_format($value['price']); + } + + return ['list'=>$products,'count'=>$count]; + + } + + public function indexProduct($id) + { + $product = $this->alias('a') + ->field('p.id,p.name,p.description,a.supplier_id,s.name supplier_name,a.profit_percent,p.auto_setup,p.hidden,p.pay_type,p.price,p.cycle,a.upstream_product_id,a.certification,pg.name as product_group_name_second,pg.id as product_group_id_second,pgf.name as product_group_name_first,pgf.id as product_group_id_first') + ->leftJoin('product p','p.id=a.product_id') + ->leftjoin('product_group pg','p.product_group_id=pg.id') + ->leftjoin('product_group pgf','pg.parent_id=pgf.id') + ->leftjoin('supplier s','a.supplier_id=s.id') + ->where('p.id', $id) + ->find(); + if(empty($product)){ + $product = (object)[]; + } + return $product; + } + + # 添加商品 + public function createProduct($param) + { + $productGroupId = intval($param['product_group_id']); + + $ProductGroupModel = new ProductGroupModel(); + $productGroup = $ProductGroupModel->where('id',$productGroupId) + ->where('parent_id','>',0) + ->find(); + if (empty($productGroup)){ + return ['status'=>400,'msg'=>lang('please_select_product_group_second')]; + } + + $supplier = SupplierModel::find($param['supplier_id']); + if (empty($supplier)){ + return ['status'=>400,'msg'=>lang('supplier_is_not_exist')]; + } + + // 从上游商品详情拉取 + $UpstreamLogic = new UpstreamLogic(); + $res = $UpstreamLogic->upstreamProductDetail(['url' => $supplier['url'], 'id' => $param['upstream_product_id']]); + if(empty($res['data'])){ + return ['status'=>400,'msg'=>lang('upstream_product_is_not_exist')]; + } + + $exist = $this->where('upstream_product_id', $param['upstream_product_id'])->find(); + if($exist){ + return ['status'=>400,'msg'=>lang('agent_product_cannot_repeat_agent')]; + } + + $resource = $UpstreamLogic->upstreamProductDownloadResource(['url' => $supplier['url'], 'id' => $param['upstream_product_id']]); + if($resource['status']==400){ + return ['status'=>400, 'msg'=>$resource['msg']]; + } + + + $this->startTrans(); + try{ + $maxOrder = ProductModel::max('order'); + + $price = $res['data']['price'] ?? 0; + $price = bcdiv($price*(100+$param['profit_percent']), 100, 2); + + $product = ProductModel::create([ + 'name' => $param['name'], + 'order' => $maxOrder+1, + 'product_group_id' => $productGroupId, + 'description' => $param['description'] ?? '', + 'pay_type' => $res['data']['pay_type'] ?? 'recurring_prepayment', + 'auto_setup' => $param['auto_setup'], + 'price' => $price, + 'cycle' => $res['data']['cycle'] ?? '', + 'create_time' => time() + ]); + + $this->create([ + 'supplier_id' => $param['supplier_id'], + 'product_id' => $product->id, + 'upstream_product_id' => $param['upstream_product_id'], + 'profit_percent' => $param['profit_percent'], + 'certification' => $param['certification'], + 'create_time' => time(), + 'res_module' => $resource['data']['module'] ?? '' + ]); + + # 记录日志 + active_log(lang('log_admin_create_upstream_product',['{admin}'=>'admin#'.get_admin_id().'#'.request()->admin_name.'#','{product}'=>'product#'.$product->id.'#'.$param['name'].'#']),'product',$product->id); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('create_fail')]; + } + + return ['status'=>200,'msg'=>lang('create_success')]; + } + + # 编辑商品 + public function updateProduct($param) + { + $product = ProductModel::find($param['id']); + if (empty($product)){ + return ['status'=>400,'msg'=>lang('product_is_not_exist')]; + } + + $productGroupId = intval($param['product_group_id']); + + $ProductGroupModel = new ProductGroupModel(); + $productGroup = $ProductGroupModel->where('id',$productGroupId) + ->where('parent_id','>',0) + ->find(); + if (empty($productGroup)){ + return ['status'=>400,'msg'=>lang('please_select_product_group_second')]; + } + + $supplier = SupplierModel::find($param['supplier_id']); + if (empty($supplier)){ + return ['status'=>400,'msg'=>lang('supplier_is_not_exist')]; + } + + // 从上游商品详情拉取 + $UpstreamLogic = new UpstreamLogic(); + $res = $UpstreamLogic->upstreamProductDetail(['url' => $supplier['url'], 'id' => $param['upstream_product_id']]); + if(empty($res['data'])){ + return ['status'=>400,'msg'=>lang('upstream_product_is_not_exist')]; + } + + $exist = $this->where('upstream_product_id', $param['upstream_product_id'])->where('product_id', '<>', $param['id'])->find(); + if($exist){ + return ['status'=>400,'msg'=>lang('agent_product_cannot_repeat_agent')]; + } + + $resource = $UpstreamLogic->upstreamProductDownloadResource(['url' => $supplier['url'], 'id' => $param['upstream_product_id']]); + if($resource['status']==400){ + return ['status'=>400, 'msg'=>$resource['msg']]; + } + + $upstreamProduct = $this->where('product_id', $param['id'])->find(); + $upstreamProduct = $upstreamProduct->toArray(); + $upstreamProduct['name'] = $product['name']; + $upstreamProduct['auto_setup'] = $product['auto_setup']; + # 日志描述 + $logDescription = log_description($upstreamProduct,$param,'upstream_product'); + + $this->startTrans(); + try{ + + $price = $res['data']['price'] ?? 0; + $price = bcdiv($price*(100+$param['profit_percent']), 100, 2); + + ProductModel::update([ + 'name' => $param['name'], + 'product_group_id' => $productGroupId, + 'description' => $param['description'] ?? '', + 'pay_type' => $res['data']['pay_type'] ?? 'recurring_prepayment', + 'auto_setup' => $param['auto_setup'], + 'price' => $price, + 'cycle' => $res['data']['cycle'] ?? '', + 'update_time' => time() + ], ['id' => $param['id']]); + + $this->update([ + 'supplier_id' => $param['supplier_id'], + 'upstream_product_id' => $param['upstream_product_id'], + 'profit_percent' => $param['profit_percent'], + 'certification' => $param['certification'], + 'update_time' => time(), + 'res_module' => $resource['data']['module'] ?? '' + ], ['product_id' => $param['id']]); + + # 记录日志 + active_log(lang('log_admin_update_upstream_product',['{admin}'=>'admin#'.get_admin_id().'#'.request()->admin_name.'#','{product}'=>'product#'.$product->id.'#'.$param['name'].'#','{description}'=>$logDescription]),'product',$param['id']); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('update_fail')]; + } + + idcsmart_cache('product:recommend:list',null); + + return ['status'=>200,'msg'=>lang('update_success')]; + } + + # 代理推荐商品 + public function agentRecommendProduct($param) + { + $productGroupId = intval($param['product_group_id']); + + $ProductGroupModel = new ProductGroupModel(); + $productGroup = $ProductGroupModel->where('id',$productGroupId) + ->where('parent_id','>',0) + ->find(); + if (empty($productGroup)){ + return ['status'=>400,'msg'=>lang('please_select_product_group_second')]; + } + + # 从推荐代理接口获取商品详情 + $UpstreamLogic = new UpstreamLogic(); + $res = $UpstreamLogic->recommendProductDetail(['id' => $param['id']]); + if(empty($res['data'])){ + return ['status'=>400,'msg'=>lang('recommend_product_is_not_exist')]; + } + + $exist = $this->where('upstream_product_id', $res['data']['upstream_product_id'])->find(); + if($exist){ + return ['status'=>400,'msg'=>lang('agent_product_cannot_repeat_agent')]; + } + + $resource = $UpstreamLogic->upstreamProductDownloadResource(['url' => $res['data']['url'], 'id' => $res['data']['upstream_product_id']]); + if($resource['status']==400){ + return ['status'=>400, 'msg'=>$resource['msg']]; + } + + $supplier = SupplierModel::where('url', $res['data']['url'])->find(); + $this->startTrans(); + try{ + if(empty($supplier)){ + $supplier = SupplierModel::create([ + 'name' => $res['data']['supplier_name'], + 'url' => $res['data']['url'], + 'username' => $param['username'], + 'token' => aes_password_encode($param['token']), + 'secret' => aes_password_encode(str_replace("\r\n", "\n", $param['secret'])), + 'contact' => '', + 'notes' => '', + 'create_time' => time(), + ]); + } + + $maxOrder = ProductModel::max('order'); + + $price = $res['data']['price'] ?? 0; + $price = bcdiv($price*(100+$param['profit_percent']), 100, 2); + + $product = ProductModel::create([ + 'name' => $param['name'], + 'order' => $maxOrder+1, + 'product_group_id' => $productGroupId, + 'description' => $param['description'] ?? '', + 'pay_type' => $res['data']['pay_type'] ?? 'recurring_prepayment', + 'auto_setup' => $param['auto_setup'], + 'price' => $price, + 'cycle' => $res['data']['cycle'] ?? '', + 'create_time' => time() + ]); + + $this->create([ + 'supplier_id' => $supplier->id, + 'product_id' => $product->id, + 'upstream_product_id' => $res['data']['upstream_product_id'], + 'profit_percent' => $param['profit_percent'], + 'certification' => $param['certification'], + 'create_time' => time(), + 'res_module' => $resource['data']['module'] ?? '' + ]); + + # 记录日志 + active_log(lang('log_admin_agent_upstream_product',['{admin}'=>'admin#'.get_admin_id().'#'.request()->admin_name.'#','{product}'=>'product#'.$product->id.'#'.$param['name'].'#']),'product',$product->id); + + $this->commit(); + }catch (\Exception $e){ + $this->rollback(); + return ['status'=>400,'msg'=>lang('create_fail')]; + } + + return ['status'=>200,'msg'=>lang('create_success')]; + } + + # 删除商品 + public function afterProductDelete($id) + { + $this->where('product_id', $id)->delete(); + + return true; + } +} \ No newline at end of file diff --git a/app/event/AppInit.php b/app/event/AppInit.php index a55f67c0..59a2f0d5 100755 --- a/app/event/AppInit.php +++ b/app/event/AppInit.php @@ -86,6 +86,20 @@ public function handle() include_once $serverDir . $server . '/route.php'; } } + + # 加载RES模块钩子文件 + $serverDir = WEB_ROOT . 'plugins/reserver/'; + $servers = array_map('basename', glob($serverDir . '*', GLOB_ONLYDIR)); + foreach ($servers as $server){ + if (is_file($serverDir . $server . '/hooks.php')){ + include_once $serverDir . $server . '/hooks.php'; + } + # 允许模块自定义路由(不管是否与系统冲突) + if (is_file($serverDir . $server . '/route.php')){ + include_once $serverDir . $server . '/route.php'; + } + } + } // 缓存插件钩子 diff --git a/app/home/controller/CartController.php b/app/home/controller/CartController.php index 0bdaaf6d..32f53f79 100755 --- a/app/home/controller/CartController.php +++ b/app/home/controller/CartController.php @@ -209,11 +209,12 @@ public function batchDelete() */ public function clear() { + $param = $this->request->param(); // 实例化模型类 $CartModel = new CartModel(); // 清空购物车 - $result = $CartModel->clearCart(); + $result = $CartModel->clearCart($param); return json($result); } @@ -244,7 +245,7 @@ public function settle() $CartModel = new CartModel(); // 结算购物车 - $result = $CartModel->settle($param['positions'],$param['customfield']??[]); + $result = $CartModel->settle($param['positions'],$param['customfield']??[],$param); return json($result); } diff --git a/app/home/controller/CommonController.php b/app/home/controller/CommonController.php index 0aee8f3c..279c5136 100755 --- a/app/home/controller/CommonController.php +++ b/app/home/controller/CommonController.php @@ -11,6 +11,14 @@ use app\common\model\HostModel; use app\common\model\MenuModel; use app\home\model\ClientareaAuthModel; +use app\common\model\FeedbackModel; +use app\common\model\FeedbackTypeModel; +use app\common\model\ConsultModel; +use app\common\model\FriendlyLinkModel; +use app\common\model\HonorModel; +use app\common\model\PartnerModel; +use app\home\validate\FeedbackValidate; +use app\home\validate\ConsultValidate; /** * @title 公共接口(前台,无需登录) @@ -197,7 +205,30 @@ public function gateway() * @return string currency_prefix ¥ 货币符号 * @return string currency_suffix 元 货币后缀 * @return string code_client_email_register 0 邮箱注册数字验证码开关:1开启0关闭 - * @return string system_logo 系统LOGO + * @return string system_logo - 系统LOGO + * @return string put_on_record - 备案信息 + * @return string enterprise_name - 企业名称 + * @return string enterprise_telephone - 企业电话 + * @return string enterprise_mailbox - 企业邮箱 + * @return string enterprise_qrcode - 企业二维码 + * @return string online_customer_service_link - 在线客服链接 + * @return array feedback_type - 意见反馈类型 + * @return int feedback_type[].id - 意见反馈类型ID + * @return string feedback_type[].name - 名称 + * @return string feedback_type[].description - 描述 + * @return array friendly_link - 友情链接 + * @return int friendly_link[].id - 友情链接ID + * @return string friendly_link[].name - 名称 + * @return string friendly_link[].url - 链接地址 + * @return array honor - 荣誉资质 + * @return int honor[].id - 荣誉资质ID + * @return string honor[].name - 名称 + * @return string honor[].img - 图片地址 + * @return array partner - 合作伙伴 + * @return int partner[].id - 合作伙伴ID + * @return string partner[].name - 名称 + * @return string partner[].img - 图片地址 + * @return string partner[].description - 描述 */ public function common() { @@ -228,6 +259,12 @@ public function common() 'currency_suffix', 'code_client_email_register', 'system_logo', + 'put_on_record', + 'enterprise_name', + 'enterprise_telephone', + 'enterprise_mailbox', + 'enterprise_qrcode', + 'online_customer_service_link', ]; //$data = configuration($setting); @@ -235,6 +272,26 @@ public function common() $data['system_logo'] = config('idcsmart.system_logo_url') . $data['system_logo']; $account = $param['account']??''; + // 获取意见反馈类型 + $FeedbackTypeModel = new FeedbackTypeModel(); + $feedbackType = $FeedbackTypeModel->feedbackTypeList(); + $data['feedback_type'] = $feedbackType['list']; + + // 获取友情链接 + $FriendlyLinkModel = new FriendlyLinkModel(); + $friendlyLink = $FriendlyLinkModel->friendlyLinkList(); + $data['friendly_link'] = $friendlyLink['list']; + + // 获取荣誉资质 + $HonorModel = new HonorModel(); + $honor = $HonorModel->honorList(); + $data['honor'] = $honor['list']; + + // 获取意见反馈类型 + $PartnerModel = new PartnerModel(); + $partner = $PartnerModel->partnerList(); + $data['partner'] = $partner['list']; + # 登录3次失败 if ($account){ $ip = get_client_ip(); @@ -396,4 +453,72 @@ public function authList() return json($result); } + /** + * 时间 2023-02-28 + * @title 提交意见反馈 + * @desc 提交意见反馈 + * @url /console/v1/feedback + * @method POST + * @author theworld + * @version v1 + * @param int type - 类型 required + * @param string title - 标题 required + * @param string description - 描述 required + * @param array attachment - 附件 + * @param string contact - 联系方式 + */ + public function createFeedback() + { + // 接收参数 + $param = $this->request->param(); + + $FeedbackValidate = new FeedbackValidate(); + // 参数验证 + if (!$FeedbackValidate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $FeedbackModel = new FeedbackModel(); + + // 提交意见反馈 + $result = $FeedbackModel->createFeedback($param); + + return json($result); + } + + /** + * 时间 2023-02-28 + * @title 提交方案咨询 + * @desc 提交方案咨询 + * @url /console/v1/consult + * @method POST + * @author theworld + * @version v1 + * @param string contact - 联系人 required + * @param string company - 公司名称 + * @param string phone - 手机号码 手机号码和邮箱二选一必填 + * @param string email - 联系邮箱 手机号码和邮箱二选一必填 + * @param string matter - 咨询产品 required + */ + public function createConsult() + { + // 接收参数 + $param = $this->request->param(); + + $ConsultValidate = new ConsultValidate(); + // 参数验证 + if (!$ConsultValidate->scene('create')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $ConsultModel = new ConsultModel(); + + // 提交方案咨询 + $result = $ConsultModel->createConsult($param); + + return json($result); + } + } \ No newline at end of file diff --git a/app/home/controller/HostController.php b/app/home/controller/HostController.php index 5af5b291..e934f5e4 100755 --- a/app/home/controller/HostController.php +++ b/app/home/controller/HostController.php @@ -258,4 +258,53 @@ public function clientHost() return json($result); } + /** + * 时间 2023-02-20 + * @title 模块暂停 + * @desc 模块暂停 + * @url /console/v1/host/:id/module/suspend + * @method POST + * @author hh + * @version v1 + * @param int id - 产品ID required + * @param string suspend_type - 暂停类型(overdue=到期暂停,overtraffic=超流暂停,certification_not_complete=实名未完成,other=其他) required + * @param string suspend_reason - 暂停原因 + */ + public function suspendAccount() + { + $param = $this->request->param(); + + // 参数验证 + if (!$this->validate->scene('suspend')->check($param)){ + return json(['status' => 400 , 'msg' => lang($this->validate->getError())]); + } + + // 实例化模型类 + $HostModel = new HostModel(); + + $result = $HostModel->suspendAccount($param); + return json($result); + } + + /** + * 时间 2023-02-20 + * @title 模块解除暂停 + * @desc 模块解除暂停 + * @url /console/v1/host/:id/module/unsuspend + * @method POST + * @author wyh + * @version v1 + * @param int id - 产品ID required + */ + public function unsuspendAccount() + { + $param = $this->request->param(); + + // 实例化模型类 + $HostModel = new HostModel(); + + $result = $HostModel->unsuspendAccount($param['id']); + return json($result); + } + } \ No newline at end of file diff --git a/app/home/controller/ModuleController.php b/app/home/controller/ModuleController.php index 4604f1e1..5b5a7c53 100755 --- a/app/home/controller/ModuleController.php +++ b/app/home/controller/ModuleController.php @@ -60,6 +60,7 @@ public function moduleList() ]; return json($result); } -} + +} diff --git a/app/home/controller/UpstreamController.php b/app/home/controller/UpstreamController.php new file mode 100644 index 00000000..782bf995 --- /dev/null +++ b/app/home/controller/UpstreamController.php @@ -0,0 +1,36 @@ +request->param(); + + // 实例化模型类 + $UpstreamLogic = new UpstreamLogic(); + + // 上游同步数据 + $result = $UpstreamLogic->syncHost($param); + + return json($result); + } +} \ No newline at end of file diff --git a/app/home/controller/ViewController.php b/app/home/controller/ViewController.php index bc42420a..1c1ed366 100755 --- a/app/home/controller/ViewController.php +++ b/app/home/controller/ViewController.php @@ -7,17 +7,17 @@ class ViewController extends HomeBaseController { - public function index() + public function index() { - $param = $this->request->param(); - $data = [ - 'title'=>'首页-智简魔方', - ]; + $param = $this->request->param(); + $data = [ + 'title'=>'首页-智简魔方', + ]; - $data['template_catalog'] = 'clientarea'; - $tplName = empty($param['view_html'])?'index':$param['view_html']; + $data['template_catalog'] = 'clientarea'; + $tplName = empty($param['view_html'])?'index':$param['view_html']; - if (isset($param['theme']) && !empty($param['theme'])){ + if (isset($param['theme']) && !empty($param['theme'])){ cookie('clientarea_theme',$param['theme']); $data['themes'] = $param['theme']; } elseif (cookie('clientarea_theme')){ @@ -25,38 +25,49 @@ public function index() } else{ $data['themes'] = configuration('clientarea_theme'); } - $view_path = '../public/clientarea/template/'.$data['themes'].'/'; - if(!file_exists($view_path.$tplName)){ - $theme_config=$this->themeConfig($view_path); - if(!empty($theme_config['config-parent-theme'])){ - $view_path = '../public/clientarea/template/'.$theme_config['config-parent-theme'].'/'; - } - } + if($tplName=='index'){ + header('location:/theme/index.html');die; + $view_path = '../public/theme/'; + }else{ + $view_path = '../public/clientarea/template/'.$data['themes'].'/'; + } + + if(!file_exists($view_path.$tplName)){ + $theme_config=$this->themeConfig($view_path); + if(!empty($theme_config['config-parent-theme'])){ + $view_path = '../public/clientarea/template/'.$theme_config['config-parent-theme'].'/'; + } + } $PluginModel = new PluginModel(); $addons = $PluginModel->plugins('addon'); - $data['addons'] = $addons['list']; - - View::config(['view_path' => $view_path]); + $data['addons'] = $addons['list']; - return View::fetch("/".$tplName,$data); - } - - public function plugin() + $config['view_path'] = $view_path; + /*if($tplName=='index'){ + $config['view_suffix'] = 'html'; + }*/ + + View::config($config); + + return View::fetch("/".$tplName,$data); + } + + public function plugin() { - $param = $this->request->param(); - $plugin_id = $param['plugin_id']; - $tplName = empty($param['view_html'])?'index':$param['view_html']; - $addon = (new PluginModel())->plugins('addon')['list']; - $addon = array_column($addon,'name','id'); - $name=parse_name($addon[$plugin_id]??''); - if(empty($name)){ + $param = $this->request->param(); + $plugin_id = $param['plugin_id']; + $tplName = empty($param['view_html'])?'index':$param['view_html']; + $addon = (new PluginModel())->plugins('addon')['list']; + $addon = array_column($addon,'name','id'); + $name=parse_name($addon[$plugin_id]??''); + if(empty($name)){ throw new TemplateNotFoundException(lang('not_found'), $name); - #exit('not found template1'); - } - $tpl = '../public/plugins/addon/'.$name.'/template/clientarea/'; + #exit('not found template1'); + } + $tpl = '../public/plugins/addon/'.$name.'/template/clientarea/'; $data['template_catalog'] = 'clientarea'; @@ -72,43 +83,43 @@ public function plugin() $PluginModel = new PluginModel(); $addons = $PluginModel->plugins('addon'); - $data['addons'] = $addons['list']; - - if(file_exists($tpl.$tplName.".php")){ - $content=$this->view('header',$data); - $content.=$this->pluginView($tplName,$data,$name); - $content.=$this->view('footer',$data); - return $content; - }else{ + $data['addons'] = $addons['list']; + + if(file_exists($tpl.$tplName.".php")){ + $content=$this->view('header',$data); + $content.=$this->pluginView($tplName,$data,$name); + $content.=$this->view('footer',$data); + return $content; + }else{ throw new TemplateNotFoundException(lang('not_found'), $tpl); - #exit('not found template'); - } - + #exit('not found template'); + } + } - - private function view($tplName, $data){ + + private function view($tplName, $data){ View::config(['view_path' => '../public/clientarea/template/default/']); - return View::fetch('/'.$tplName,$data); + return View::fetch('/'.$tplName,$data); } - - private function pluginView($tplName, $data, $name){ + + private function pluginView($tplName, $data, $name){ View::config(['view_path' => '../public/plugins/addon/'.$name.'/template/clientarea/']); - return View::fetch('/'.$tplName,$data); + return View::fetch('/'.$tplName,$data); + } + //模板继承文件读取 + private function themeConfig($file){ + $theme=$file.'/theme.config';$themes=[]; + if(file_exists($theme)){ + $theme=file_get_contents($theme); + + $theme=explode("\r\n",$theme); + $theme=array_filter($theme); + + foreach($theme as $v){ + $theme_config=explode(":",$v); + $themes[trim($theme_config[0])]=trim(trim(trim($theme_config[1],"'"),'"')); + } + } + return $themes; } - //模板继承文件读取 - private function themeConfig($file){ - $theme=$file.'/theme.config';$themes=[]; - if(file_exists($theme)){ - $theme=file_get_contents($theme); - - $theme=explode("\r\n",$theme); - $theme=array_filter($theme); - - foreach($theme as $v){ - $theme_config=explode(":",$v); - $themes[trim($theme_config[0])]=trim(trim(trim($theme_config[1],"'"),'"')); - } - } - return $themes; - } } diff --git a/app/home/model/CartModel.php b/app/home/model/CartModel.php index a23a9e81..6597a95b 100755 --- a/app/home/model/CartModel.php +++ b/app/home/model/CartModel.php @@ -10,7 +10,11 @@ use app\common\model\OrderItemModel; use app\common\model\HostModel; use app\common\logic\ModuleLogic; +use app\common\logic\ResModuleLogic; use app\common\model\ServerModel; +use app\common\model\UpstreamProductModel; +use app\common\model\UpstreamHostModel; +use app\common\model\UpstreamOrderModel; /** * @title 购物车模型 @@ -153,8 +157,15 @@ public function createCart($param) $param['config_options'] = $param['config_options'] ?? []; - $ModuleLogic = new ModuleLogic(); - $result = $ModuleLogic->cartCalculatePrice($product, $param['config_options'],$param['qty']); + $upstreamProduct = UpstreamProductModel::where('product_id', $product['id'])->find(); + + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($product, $param['config_options'],$param['qty']); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->cartCalculatePrice($product, $param['config_options'],$param['qty']); + } if($result['status']!=200){ return $result; @@ -202,9 +213,15 @@ public function updateCart($param) $param['config_options'] = $param['config_options'] ?? []; - $ModuleLogic = new ModuleLogic(); - $result = $ModuleLogic->cartCalculatePrice($product, $param['config_options']); + $upstreamProduct = UpstreamProductModel::where('product_id', $product['id'])->find(); + if($upstreamProduct){ + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($product, $param['config_options']); + }else{ + $ModuleLogic = new ModuleLogic(); + $result = $ModuleLogic->cartCalculatePrice($product, $param['config_options']); + } if($result['status']!=200){ return $result; } @@ -312,10 +329,28 @@ public function batchDeleteCart($positions) * @return int status - 状态码,200成功,400失败 * @return string msg - 提示信息 */ - public function clearCart() + public function clearCart($param) { self::$cartData = []; self::saveCart(); + + # 20230216 wyh + if (request()->is_api){ + $HostModel = new HostModel(); + $host = $HostModel->whereLike('downstream_info', '%'.$param['downstream_token'].'%')->where('downstream_host_id',$param['downstream_host_id']??0)->find(); + if (!empty($host)){ + $OrderModel = new OrderModel(); + $order = $OrderModel->find($host['order_id']); + if (!empty($order)){ + if ($order['status']!='Paid'){ + return ['status'=>200, 'msg'=>lang('clear_cart_success'),'data'=>['order_id'=>$order['id']]]; + }else{ + return ['status'=>400,'msg'=>'订单已开通,请勿重新开通']; + } + } + } + } + return ['status'=>200, 'msg'=>lang('clear_cart_success')]; } @@ -330,13 +365,17 @@ public function clearCart() * @return int status - 状态码,200成功,400失败 * @return string msg - 提示信息 */ - public function settle($position,$customfield=[]) + public function settle($position,$customfield=[],$param=[]) { $amount = 0; $cartData = []; if(empty(self::$cartData)){ return ['status'=>400, 'msg'=>lang('there_are_no_items_in_the_cart')]; } + + $clientId = get_client_id(); + + $certification = check_certification($clientId); $ModuleLogic = new ModuleLogic(); foreach (self::$cartData as $key => $value) { if(in_array($key, $position)){ @@ -349,8 +388,17 @@ public function settle($position,$customfield=[]) } $value['config_options'] = $value['config_options'] ?? []; - $result = $ModuleLogic->cartCalculatePrice($product, $value['config_options'],$value['qty']); + $upstreamProduct = UpstreamProductModel::where('product_id', $product['id'])->find(); + if($upstreamProduct){ + if($upstreamProduct['certification']==1 && !$certification){ + return ['status'=>400, 'msg'=>lang('certification_uncertified_cannot_buy_product')]; + } + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->cartCalculatePrice($product, $value['config_options'],$value['qty']); + }else{ + $result = $ModuleLogic->cartCalculatePrice($product, $value['config_options'],$value['qty']); + } if($result['status']!=200){ return $result; } @@ -365,13 +413,16 @@ public function settle($position,$customfield=[]) $cartData[$key]['billing_cycle'] = $result['data']['billing_cycle']; $cartData[$key]['duration'] = $result['data']['duration']; $cartData[$key]['description'] = $result['data']['description']; + if($upstreamProduct){ + $cartData[$key]['profit'] = $result['data']['profit']; + } unset(self::$cartData[$key]); } } if(empty($cartData)){ return ['status'=>400, 'msg'=>lang('please_select_products_in_the_cart')]; } - $clientId = get_client_id(); + $result = hook('before_order_create', ['client_id'=>$clientId, 'cart' => $cartData]); @@ -404,6 +455,7 @@ public function settle($position,$customfield=[]) // 创建产品 $orderItem = []; $productLog = []; + $hostIds = []; foreach ($cartData as $key => $value) { $product = ProductModel::find($value['product_id']); if($product['stock_control']==1){ @@ -427,7 +479,15 @@ public function settle($position,$customfield=[]) }else{ $serverId = $product['rel_id']; } + $upstreamProduct = UpstreamProductModel::where('product_id', $value['product_id'])->find(); for ($i=1; $i<=$value['qty']; $i++) { + if (request()->is_api){ + $downstreamHostId = intval($param['downstream_host_id'] ?? 0); + if(!empty($downstreamHostId)){ + $downstreamInfo = json_encode(['url' => $param['downstream_url']??'', 'token'=>$param['downstream_token']??'', 'api'=>request()->api_id]); + } + } + $host = HostModel::create([ 'client_id' => $clientId, 'order_id' => $order->id, @@ -442,9 +502,35 @@ public function settle($position,$customfield=[]) 'billing_cycle_time' => $value['duration'], 'active_time' => $time, 'due_time' => $product['pay_type']!='onetime' ? $time : 0, - 'create_time' => $time + 'create_time' => $time, + 'downstream_info' => $downstreamInfo ?? '', + 'downstream_host_id' => $downstreamHostId ?? 0, ]); - $ModuleLogic->afterSettle($product, $host->id, $value['config_options']); + + hook('after_host_create',['id'=>$host->id, 'param'=>$param]); + + $hostIds[] = $host->id; + + if($upstreamProduct){ + UpstreamHostModel::create([ + 'supplier_id' => $upstreamProduct['supplier_id'], + 'host_id' => $host->id, + 'upstream_configoption' => json_encode($value['config_options']), + 'create_time' => $time + ]); + UpstreamOrderModel::create([ + 'supplier_id' => $upstreamProduct['supplier_id'], + 'order_id' => $order->id, + 'host_id' => $host->id, + 'amount' => $value['price'], + 'profit' => $value['profit'], + 'create_time' => $time + ]); + $ResModuleLogic = new ResModuleLogic($upstreamProduct); + $result = $ResModuleLogic->afterSettle($product, $host->id, $value['config_options']); + }else{ + $ModuleLogic->afterSettle($product, $host->id, $value['config_options']); + } // 产品和对应自定义字段 $customfield['host_customfield'][] = ['id'=>$host->id, 'customfield' => $value['customfield'] ?? []]; @@ -472,6 +558,8 @@ public function settle($position,$customfield=[]) hook('after_order_create',['id'=>$order->id,'customfield'=>$customfield]); + update_upstream_order_profit($order->id); + self::saveCart(); $OrderModel = new OrderModel(); @@ -488,7 +576,7 @@ public function settle($position,$customfield=[]) return ['status' => 400, 'msg' => $e->getMessage()]; } - return ['status' => 200, 'msg' => lang('success_message'), 'data' => ['order_id' => $order->id, 'amount' => $amount]]; + return ['status' => 200, 'msg' => lang('success_message'), 'data' => ['order_id' => $order->id, 'amount' => $amount,'host_ids'=>$hostIds]]; } # 保存购物车 diff --git a/app/home/validate/ConsultValidate.php b/app/home/validate/ConsultValidate.php new file mode 100644 index 00000000..980d050e --- /dev/null +++ b/app/home/validate/ConsultValidate.php @@ -0,0 +1,34 @@ + 'require|max:255', + 'company' => 'max:255', + 'phone' => 'requireWithout:email|max:20', + 'email' => 'requireWithout:phone|email', + 'matter' => 'require|max:1000', + ]; + + protected $message = [ + 'contact.require' => 'please_enter_consult_contact', + 'contact.max' => 'consult_contact_cannot_exceed_50_chars', + 'company.max' => 'consult_company_cannot_exceed_255_chars', + 'phone.requireWithout' => 'please_enter_consult_phone', + 'phone.max' => 'consult_phone_cannot_exceed_20_chars', + 'email.requireWithout' => 'please_enter_consult_email', + 'email.email' => 'consult_email_error', + 'matter.require' => 'please_enter_consult_matter', + 'matter.max' => 'consult_matter_cannot_exceed_1000_chars', + ]; + + protected $scene = [ + 'create' => ['contact', 'company', 'phone', 'email', 'matter'], + ]; +} \ No newline at end of file diff --git a/app/home/validate/FeedbackValidate.php b/app/home/validate/FeedbackValidate.php new file mode 100644 index 00000000..9dfb6137 --- /dev/null +++ b/app/home/validate/FeedbackValidate.php @@ -0,0 +1,33 @@ + 'require|integer|gt:0', + 'title' => 'require|max:255', + 'description' => 'require', + 'attachment' => 'array', + 'contact' => 'max:255', + ]; + + protected $message = [ + 'type.require' => 'param_error', + 'type.integer' => 'param_error', + 'type.gt' => 'param_error', + 'title.require' => 'please_enter_feedback_title', + 'title.max' => 'feedback_title_cannot_exceed_255_chars', + 'description.require' => 'please_enter_feedback_description', + 'attachment.array' => 'param_error', + 'contact.max' => 'feedback_contact_cannot_exceed_255_chars', + ]; + + protected $scene = [ + 'create' => ['type', 'title', 'description', 'attachment', 'contact'], + ]; +} \ No newline at end of file diff --git a/app/http/middleware/Check.php b/app/http/middleware/Check.php index b0697865..4f7e493d 100755 --- a/app/http/middleware/Check.php +++ b/app/http/middleware/Check.php @@ -23,6 +23,12 @@ public function handle($request,\Closure $next) $request->client_id = $jwtToken['id']; $request->client_name = $jwtToken['name']; $request->client_remember_password = $jwtToken['remember_password']; + + if (isset($jwtToken['is_api']) && $jwtToken['is_api']){ // 兼容不需要登录的接口 + $request->is_api = $jwtToken['is_api']; + $request->api_id = $jwtToken['api_id']??0; + $request->api_name = $jwtToken['api_name']??''; + } } return $next($request); @@ -80,52 +86,6 @@ public function checkToken(Request $request, $is_admin=false, $jwt = '') return ['status'=>200,'data'=>['jwt_token'=>$jwtToken]]; } - # 校验API - public function checkApi(Request $request) - { - $param = $request->param(); - - if (!isset($param['login_token'])){ - return ['status' => 401,'msg' => lang('login_unauthorized')]; # 未授权 - } - - $loginToken = explode(',', $param['login_token']); - - if(empty($loginToken) || !is_array($loginToken)){ - return ['status' => 401,'msg' => lang('login_unauthorized')]; - } - - if(count($loginToken)!=2){ - return ['status' => 401,'msg' => lang('login_unauthorized')]; - } - - if(empty($loginToken[0]) || empty($loginToken[1])){ - return ['status' => 401,'msg' => lang('login_unauthorized')]; - } - - $api = ApiModel::where('id', $loginToken[0])->find(); - - if(empty($api)){ - return ['status' => 401,'msg' => lang('login_unauthorized')]; - } - - if($loginToken[1]!=aes_password_decode($api['token'])){ - return ['status' => 401,'msg' => lang('login_unauthorized')]; - } - - if($api['status']==1){ - $ip = explode("\n", $api['ip']); - $clientIp = get_client_ip(); - if(!in_array($clientIp, $ip)){ - return ['status' => 401,'msg' => lang('login_unauthorized')]; - } - } - - return ['status'=>200, 'data' => ['api' => $api]]; - - - } - # 参考JWT文档:https://packagist.org/packages/firebase/php-jwt protected function verifyJwt($jwt,$id,$is_admin=false) { @@ -158,6 +118,9 @@ protected function verifyJwt($jwt,$id,$is_admin=false) 'nbf' => $jwtAuth['nbf'], 'ip' => $jwtAuth['ip'], 'is_admin' => isset($info['is_admin'])?$info['is_admin']:false, # 是否后台验证 + 'is_api' => isset($info['is_api'])?$info['is_api']:false, # + 'api_id' => isset($info['api_id'])?$info['api_id']:0, # + 'api_name' => isset($info['api_name'])?$info['api_name']:'', # ]; return ['status'=>200,'data'=>$data]; diff --git a/app/http/middleware/CheckHome.php b/app/http/middleware/CheckHome.php index 768fb1c8..c56fbdb3 100755 --- a/app/http/middleware/CheckHome.php +++ b/app/http/middleware/CheckHome.php @@ -16,32 +16,20 @@ public function handle($request, \Closure $next) { $result = parent::checkToken($request); if ($result['status'] != 200){ - $param = $request->param(); - if (isset($param['login_token'])){ - $result = parent::checkApi($request); - if ($result['status'] != 200){ - return json($result); - } - }else{ - return json($result); - } - + return json($result); } - if(isset($result['data']['api'])){ - $api = $result['data']['api']; + $jwtToken = $result['data']['jwt_token']; - $request->client_id = $api['client_id']; - $request->api_id = $api['id']; - $request->api_name = $api['name']; - }else{ - $jwtToken = $result['data']['jwt_token']; + $request->client_id = $jwtToken['id']; + $request->client_name = $jwtToken['name']; + $request->client_remember_password = $jwtToken['remember_password']; - $request->client_id = $jwtToken['id']; - $request->client_name = $jwtToken['name']; - $request->client_remember_password = $jwtToken['remember_password']; + if (isset($jwtToken['is_api']) && $jwtToken['is_api']){ + $request->is_api = $jwtToken['is_api']; + $request->api_id = $jwtToken['api_id']??0; + $request->api_name = $jwtToken['api_name']??''; } - $time = time(); $ClientModel = new ClientModel(); diff --git a/config/idcsmart.php b/config/idcsmart.php index f448df2a..38d85d1c 100755 --- a/config/idcsmart.php +++ b/config/idcsmart.php @@ -59,6 +59,7 @@ 'sms' => WEB_ROOT . 'plugins/sms/', 'mail' => WEB_ROOT . 'plugins/mail/', 'server' => WEB_ROOT . 'plugins/server/', + 'reserver' => WEB_ROOT . 'plugins/reserver/', ], // 订单类型 'order_type' => [ diff --git a/public/admin/language/zh-cn.php b/public/admin/language/zh-cn.php index 6e6d5d86..1b6688d1 100755 --- a/public/admin/language/zh-cn.php +++ b/public/admin/language/zh-cn.php @@ -248,6 +248,13 @@ 'nav_plugin_list' => '插件列表', 'nav_navigation' => '导航管理', 'nav_real_name_approval' => '实名认证', + 'nav_upstream_management' => '上下游管理', + 'nav_supplier' => '供应商管理', + 'nav_upstream_order' => '上游订单管理', + 'nav_upstream_product' => '上游产品管理', + 'nav_upstream_goods' => '上游商品管理', + 'nav_template' => '模板管理', + # 日志 @@ -970,4 +977,91 @@ # 退款记录 'refund_record_is_not_exist' => '退款记录不存在', + # 供应商 + 'supplier_is_not_exist' => '供应商不存在', + 'cannot_delete_supplier' => '已代理该供应商商品,不可删除该供应商', + + 'log_create_supplier' => '{admin}新增供应商{name}', + 'log_update_supplier' => '{admin}修改供应商{name}:{description}', + 'log_delete_supplier' => '{admin}删除供应商{name}', + 'field_supplier_name' => '名称', + 'field_supplier_url' => '接口地址', + 'log_supplier_token' => '密钥变更', + 'log_supplier_secret' => '私钥变更', + 'field_supplier_contact' => '联系方式', + 'field_supplier_notes' => '备注', + + 'please_enter_supplier_name' => '请输入供应商名称', + 'supplier_name_cannot_exceed_50_chars' => '供应商名称不能超过50个字符', + 'please_enter_supplier_url' => '请输入接口地址', + 'supplier_url_cannot_exceed_255_chars' => '接口地址不能超过255个字符', + 'supplier_url_error' => '接口地址格式错误', + 'please_enter_supplier_username' => '请输入用户名', + 'supplier_username_cannot_exceed_100_chars' => '用户名不能超过100个字符', + 'please_enter_supplier_token' => '请输入API密钥', + 'supplier_token_cannot_exceed_200_chars' => 'API密钥不能超过200个字符', + 'please_enter_supplier_secret' => '请输入API私钥', + 'supplier_contact_cannot_exceed_1000_chars' => '联系方式不能超过1000个字符', + 'supplier_notes_cannot_exceed_1000_chars' => '备注不能超过1000个字符', + + # 上游商品 + 'upstream_product_is_not_exist' => '上游商品不存在', + 'agent_product_cannot_repeat_agent' => '已代理商品不可重复代理', + 'file_unzip_failed' => '文件解压失败,失败code:{code},请到网站目录下解压下载的文件{file}', + 'resource_download_failed' => '资源下载失败', + 'upstream_product_resource_get_failed' => '上游商品资源获取失败', + 'recommend_product_is_not_exist' => '推荐代理商品不存在', + + + 'log_admin_create_upstream_product' => '{admin}新增上游商品{product}', + 'log_admin_update_upstream_product' => '{admin}修改上游商品{product}:{description}', + 'log_admin_agent_upstream_product' => '{admin}代理上游商品{product}', + 'field_upstream_product_name' => '名称', + 'field_upstream_product_auto_setup' => '自动开通', + 'field_upstream_product_supplier_id' => '供应商ID', + 'field_upstream_product_upstream_product_id' => '上游商品ID', + 'field_upstream_product_profit_percent' => '利润百分比', + 'field_upstream_product_certification' => '本地实名认证', + + 'supplier_id_error' => '供应商ID错误', + 'upstream_product_id_error' => '上游商品ID错误', + 'please_enter_upstream_product_name' => '请输入商品名称', + 'upstream_product_name_cannot_exceed_50_chars' => '商品名称不能超过50个字符', + 'please_enter_upstream_product_profit_percent' => '请输入利润百分比', + 'upstream_product_profit_percent_error' => '利润百分比只能为0以上的数', + 'product_group_id_error' => '二级分组ID错误', + + # 主题设置 + 'please_enter_feedback_type_name' => '请输入名称', + 'feedback_type_name_cannot_exceed_255_chars' => '名称不能超过255个字符', + 'please_enter_feedback_type_description' => '请输入描述', + 'feedback_type_is_not_exist' => '意见反馈类型不存在', + 'upload_file_is_not_exist' => '上传文件不存在', + 'cannot_delete_feedback_type' => '该类型已被使用不可删除', + 'friendly_link_is_not_exist' => '友情链接不存在', + 'honor_is_not_exist' => '荣誉资质不存在', + 'partner_is_not_exist' => '合作伙伴不存在', + + 'put_on_record_require' => '请输入备案信息', + 'put_on_record_max' => '备案信息不能超过255个字符', + 'enterprise_name_require' => '请输入企业名称', + 'enterprise_name_max' => '企业名称不能超过255个字符', + 'enterprise_telephone_require' => '请输入企业电话', + 'enterprise_telephone_max' => '企业电话不能超过50个字符', + 'enterprise_mailbox_require' => '请输入企业邮箱', + 'enterprise_mailbox_max' => '企业邮箱不能超过255个字符', + 'enterprise_qrcode_require' => '请上传企业二维码', + 'online_customer_service_link_require' => '请输入在线客服链接', + 'please_enter_friendly_link_name' => '请输入名称', + 'friendly_link_name_cannot_exceed_100_chars' => '名称不能超过100个字符', + 'please_enter_friendly_link_url' => '请输入链接地址', + 'friendly_link_url_cannot_exceed_255_chars' => '链接地址不能超过255个字符', + 'friendly_link_url_error' => '链接地址格式有误', + 'please_enter_honor_name' => '请输入名称', + 'honor_name_cannot_exceed_100_chars' => '名称不能超过100个字符', + 'please_select_honor_image' => '请选择图片', + 'please_enter_partner_name' => '请输入名称', + 'partner_name_cannot_exceed_100_chars' => '名称不能超过100个字符', + 'please_select_partner_image' => '请选择图片', + 'please_enter_partner_description' => '请输入描述', ]; diff --git a/public/admin/template/default/admin_role.php b/public/admin/template/default/admin_role.php index fcdcba53..bdb5a9f4 100755 --- a/public/admin/template/default/admin_role.php +++ b/public/admin/template/default/admin_role.php @@ -19,7 +19,7 @@ - + @@ -65,9 +65,7 @@
- +
diff --git a/public/admin/template/default/agentList.php b/public/admin/template/default/agentList.php new file mode 100644 index 00000000..10eb988d --- /dev/null +++ b/public/admin/template/default/agentList.php @@ -0,0 +1,132 @@ +{include file="header"} + + +
+ +
+
+ +
+ + + + + + + + + + +
+ + +
+
+
所属供应商:
+
{{curObj.supplier_name}}
+
+
+
商品名称:
+
{{curObj.name}}
+
+
+
商品售价:
+
{{currency_prefix}}{{curObj.price}}/{{curObj.cycle}} 起
+
+
+
推荐简介:
+
{{curObj.description}}
+
+
+ + + + + + +

{{curObj.login_url}} 前往注册

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
建议在供应商处实名后再代理商品,避免供应商开启购买实名要求后,无法购买商品。
+
+ + + + + + + + + + + + + +
+ 立即代理 + {{lang.cancel}} +
+
+
+
+ + + + + +{include file="footer"} \ No newline at end of file diff --git a/public/admin/template/default/api/client.js b/public/admin/template/default/api/client.js index 98ae4769..3af51f26 100755 --- a/public/admin/template/default/api/client.js +++ b/public/admin/template/default/api/client.js @@ -253,43 +253,48 @@ function deleteHost(params) { /* 2023-1-30新增订单详情 */ // 订单详情 -function getOrderDetails (params) { +function getOrderDetails(params) { return Axios.get(`/order/${params.id}`) } // 订单退款 -function orderRefund (params) { +function orderRefund(params) { return Axios.post(`/order/${params.id}/refund`, params) } // 订单退款记录列表 -function getOrderRefundRecord (params) { +function getOrderRefundRecord(params) { return Axios.get(`/order/${params.id}/refund_record`) } // 删除退款记录 -function delOrderRecord (params) { +function delOrderRecord(params) { return Axios.delete(`/refund_record/${params.id}`) } // 订单应用余额 -function orderApplyCredit (params) { +function orderApplyCredit(params) { return Axios.post(`/order/${params.id}/apply_credit`, params) } // 订单扣除余额 -function orderRemoveCredit (params) { +function orderRemoveCredit(params) { return Axios.post(`/order/${params.id}/remove_credit`, params) } // 修改订单支付方式 -function changePayway (params) { +function changePayway(params) { return Axios.put(`/order/${params.id}/gateway`, params) } // 修改订单备注 -function changeOrderNotes (params) { +function changeOrderNotes(params) { return Axios.put(`/order/${params.id}/notes`, params) } // 订单管理-删除人工调整的订单子项 -function delArtificialOrder (params) { +function delArtificialOrder(params) { return Axios.delete(`/order/item/${params.id}`, params) } // 1-31 模块按钮输出 function getMoudleBtns(params) { return Axios.get(`/host/${params.id}/module/button`) -} \ No newline at end of file +} + +//产品详情 +function upHostDetail(id) { + return Axios.get(`/upstream/host/${id}`) +} diff --git a/public/admin/template/default/api/navigation.js b/public/admin/template/default/api/navigation.js index 5df26f25..0776ad4f 100644 --- a/public/admin/template/default/api/navigation.js +++ b/public/admin/template/default/api/navigation.js @@ -16,10 +16,11 @@ function saveAdminMenu(params){ } // 根据模块获取商品列表 -function productBymodule(params){ - return Axios.get(`/module/${params.module}/product`) +function productBymodule(type,params){ + return Axios.get(`/${type}/${params.module}/product`) } + // 获取后台导航 function leftMenu(){ return Axios.get(`/menu`) diff --git a/public/admin/template/default/api/product.js b/public/admin/template/default/api/product.js index aec1506c..740efe67 100755 --- a/public/admin/template/default/api/product.js +++ b/public/admin/template/default/api/product.js @@ -20,6 +20,10 @@ function editProduct(params) { function editProductServer(id, params) { return Axios.put(`/product/${id}/server`, params) } +// 插件列表 +function getAddon(params) { + return Axios.get(`/active_plugin`, { params }); +} // 选择接口获取配置 function getProductConfig(id, params) { @@ -107,4 +111,9 @@ function getInterface(params) { // 接口分组 function getGroup(params) { return Axios.get('/server/group', { params }) +} + +// 保存可代理商品 +function agentable(params) { + return Axios.put(`/product/agentable`, params) } \ No newline at end of file diff --git a/public/admin/template/default/api/system.js b/public/admin/template/default/api/system.js new file mode 100644 index 00000000..839e54e6 --- /dev/null +++ b/public/admin/template/default/api/system.js @@ -0,0 +1,57 @@ +/* 意见反馈 */ +function getFeedback (params) { + return Axios.get(`/feedback`, { params }) +} +// 意见反馈类型 +function getFeedbackType (params) { + return Axios.get(`/feedback/type`, { params }) +} +function addAndUpdateFeedbackType (type, params) { + if (type === 'add') { + return Axios.post(`/feedback/type`, params) + } else if (type === 'update') { + return Axios.put(`/feedback/type/${params.id}`, params) + } +} +// 删除 +function delFeedbackType (params) { + return Axios.delete(`/feedback/type/${params.id}`) +} +//下载文件 +function downloadfile(params) { + return Axios.get("/file/" + params.id + "/download", { + params: { ...params }, + responseType: "blob", + }); +} + +/* 方案咨询 */ +function getConsult (params) { + return Axios.get(`/consult`, { params }) +} + +/* 信息配置 */ +function getConfigInfo () { + return Axios.get(`/configuration/info`) +} +function saveConfigInfo (params) { + return Axios.put(`/configuration/info`, params) +} +/* 友情链接 - friendly_link + 荣誉资质 - honor + 合作伙伴 - partner +*/ +function getComInfo (name) { + return Axios.get(`/${name}`) +} +function addAndUpdateComInfo (name, type, params) { + if (type === 'add') { + return Axios.post(`/${name}`, params) + } else if (type === 'update') { + return Axios.put(`/${name}/${params.id}`, params) + } +} +// 删除 +function delComInfo (name, id) { + return Axios.delete(`/${name}/${id}`) +} \ No newline at end of file diff --git a/public/admin/template/default/api/upstream.js b/public/admin/template/default/api/upstream.js new file mode 100644 index 00000000..1d8aca45 --- /dev/null +++ b/public/admin/template/default/api/upstream.js @@ -0,0 +1,88 @@ +// 销售信息 +function sellInfo(params) { + return Axios.get(`/upstream/sell_info`, { params }) +} + +// 订单列表 +function orderList(params) { + return Axios.get(`/upstream/order`, { params }) +} + +// 商品列表 +function upstreamList(params) { + return Axios.get(`/upstream/product`, { params }) +} + +// 获取供应商商品列表 +function supplierGoodsList(id) { + return Axios.get(`/supplier/${id}/product`) +} + +// 供应商列表 +function supplierList(params) { + return Axios.get(`/supplier`, { params }) +} + +// 添加商品 +function addUpstreamProduct(params) { + return Axios.post(`/upstream/product`, params) +} + +// 编辑商品 +function editUpstreamProduct(params) { + return Axios.put(`/upstream/product/${params.id}`, params) +} + +// 商品详情 +function upstreamProductDetail(id) { + return Axios.get(`/upstream/product/${id}`) +} + +// 添加供应商 +function addSupplier(params) { + return Axios.post(`/supplier`, params) +} + +//编辑供应商 +function editSupplier(id, params) { + return Axios.put(`/supplier/${id}`, params) +} + +//供应商详情 +function supplierDrtail(id) { + return Axios.get(`/supplier/${id}`) +} + +// 删除供应商 +function delSupplier(id) { + return Axios.delete(`/supplier/${id}`) +} + +// 检查供应商接口连接状态 +function supplierStatus(id) { + return Axios.get(`/supplier/${id}/status`) +} +// 订单管理-调整订单金额 +function updateOrder(params) { + return Axios.put(`/order/${params.id}/amount`, params); +} + +// 产品列表 +function upstreamHost(params) { + return Axios.get(`/upstream/host`, { params }) +} + +//产品详情 +function upHostDetail(params) { + return Axios.get(`/upstream/host/${params.id}`, { params }) +} + +// 推荐代理商品列表 +function recomProList(params) { + return Axios.get(`/upstream/recommend/product`, { params }) +} + +//代理推荐商品 +function recomProduct(params) { + return Axios.post(`/upstream/recommend/product`, params) +} \ No newline at end of file diff --git a/public/admin/template/default/client_order.php b/public/admin/template/default/client_order.php index 17a26f1b..469dc942 100755 --- a/public/admin/template/default/client_order.php +++ b/public/admin/template/default/client_order.php @@ -74,7 +74,7 @@
{{row.description}}
- {{row.product_names[0]}} + {{row.product_names[0]}} {{lang.wait}}{{row.product_names.length}}{{lang.products}}
@@ -89,7 +89,7 @@ - {{row.product_name ? row.product_name : row.description}} + {{row.product_name ? row.product_name : row.description}} ({{row.host_name}}) diff --git a/public/admin/template/default/consult.php b/public/admin/template/default/consult.php new file mode 100644 index 00000000..c426d8eb --- /dev/null +++ b/public/admin/template/default/consult.php @@ -0,0 +1,36 @@ +{include file="header"} + + + + + + +{include file="footer"} \ No newline at end of file diff --git a/public/admin/template/default/css/common/viewer.min.css b/public/admin/template/default/css/common/viewer.min.css new file mode 100644 index 00000000..59e27681 --- /dev/null +++ b/public/admin/template/default/css/common/viewer.min.css @@ -0,0 +1,386 @@ +/*! + * Viewer.js v0.3.1 + * https://github.com/fengyuanchen/viewerjs + * + * Copyright (c) 2015-2016 Fengyuan Chen + * Released under the MIT license + * + * Date: 2016-02-02T11:35:36.273Z + */ +.viewer-container, +.viewer-navbar { + background-color: #000; + overflow: hidden +} + +.viewer-canvas, +.viewer-container, +.viewer-footer, +.viewer-player { + right: 0; + bottom: 0; + left: 0 +} + +.viewer-button, +.viewer-canvas, +.viewer-container, +.viewer-footer, +.viewer-list, +.viewer-navbar, +.viewer-open, +.viewer-title, +.viewer-toolbar, +.viewer-toolbar>li { + overflow: hidden +} + +.viewer-close:before, +.viewer-flip-horizontal:before, +.viewer-flip-vertical:before, +.viewer-fullscreen-exit:before, +.viewer-fullscreen:before, +.viewer-next:before, +.viewer-one-to-one:before, +.viewer-play:before, +.viewer-prev:before, +.viewer-reset:before, +.viewer-rotate-left:before, +.viewer-rotate-right:before, +.viewer-zoom-in:before, +.viewer-zoom-out:before { + font-size: 0; + line-height: 0; + display: block; + width: 20px; + height: 20px; + color: transparent; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC); + background-repeat: no-repeat +} + +.viewer-zoom-in:before { + content: 'Zoom In'; + background-position: 0 0 +} + +.viewer-zoom-out:before { + content: 'Zoom Out'; + background-position: -20px 0 +} + +.viewer-one-to-one:before { + content: 'One to One'; + background-position: -40px 0 +} + +.viewer-reset:before { + content: 'Reset'; + background-position: -60px 0 +} + +.viewer-prev:before { + content: 'Previous'; + background-position: -80px 0 +} + +.viewer-play:before { + content: 'Play'; + background-position: -100px 0 +} + +.viewer-next:before { + content: 'Next'; + background-position: -120px 0 +} + +.viewer-rotate-left:before { + content: 'Rotate Left'; + background-position: -140px 0 +} + +.viewer-rotate-right:before { + content: 'Rotate Right'; + background-position: -160px 0 +} + +.viewer-flip-horizontal:before { + content: 'Flip Horizontal'; + background-position: -180px 0 +} + +.viewer-flip-vertical:before { + content: 'Flip Vertical'; + background-position: -200px 0 +} + +.viewer-fullscreen:before { + content: 'Enter Full Screen'; + background-position: -220px 0 +} + +.viewer-fullscreen-exit:before { + content: 'Exit Full Screen'; + background-position: -240px 0 +} + +.viewer-close:before { + content: 'Close'; + background-position: -260px 0 +} + +.viewer-container { + font-size: 0; + line-height: 0; + position: absolute; + top: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: rgba(0, 0, 0, .5); + direction: ltr !important; + -ms-touch-action: none; + touch-action: none; + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none +} + +.viewer-container ::-moz-selection, +.viewer-container::-moz-selection { + background-color: transparent +} + +.viewer-container ::selection, +.viewer-container::selection { + background-color: transparent +} + +.viewer-container img { + display: block; + width: 100%; + min-width: 0 !important; + max-width: none !important; + height: auto; + min-height: 0 !important; + max-height: none !important +} + +.viewer-player, +.viewer-tooltip { + display: none; + position: absolute +} + +.viewer-canvas { + position: absolute; + top: 0 +} + +.viewer-canvas>img { + width: auto; + max-width: 90% !important; + height: auto; + margin: 15px auto +} + +.viewer-footer { + position: absolute; + text-align: center +} + +.viewer-navbar { + background-color: rgba(0, 0, 0, .5) +} + +.viewer-list { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 50px; + margin: 0; + padding: 1px 0 +} + +.viewer-list>li { + font-size: 0; + line-height: 0; + float: left; + overflow: hidden; + width: 30px; + height: 50px; + cursor: pointer; + opacity: .5; + color: transparent; + filter: alpha(opacity=50) +} + +.viewer-list>li+li { + margin-left: 1px +} + +.viewer-list>.viewer-active { + opacity: 1; + filter: alpha(opacity=100) +} + +.viewer-player { + top: 0; + cursor: none; + background-color: #000 +} + +.viewer-player>img { + position: absolute; + top: 0; + left: 0 +} + +.viewer-toolbar { + width: 280px; + margin: 0 auto 5px; + padding: 3px 0 +} + +.viewer-toolbar>li { + float: left; + width: 24px; + height: 24px; + cursor: pointer; + border-radius: 50%; + background-color: #000; + background-color: rgba(0, 0, 0, .5) +} + +.viewer-toolbar>li:hover { + background-color: #000; + background-color: rgba(0, 0, 0, .8) +} + +.viewer-toolbar>li:before { + margin: 2px +} + +.viewer-toolbar>li+li { + margin-left: 1px +} + +.viewer-toolbar>.viewer-play { + width: 30px; + height: 30px; + margin-top: -3px; + margin-bottom: -3px +} + +.viewer-toolbar>.viewer-play:before { + margin: 5px +} + +.viewer-tooltip { + font-size: 12px; + line-height: 20px; + top: 50%; + left: 50%; + width: 50px; + height: 20px; + margin-top: -10px; + margin-left: -25px; + text-align: center; + color: #fff; + border-radius: 10px; + background-color: #000; + background-color: rgba(0, 0, 0, .8) +} + +.viewer-title { + font-size: 12px; + line-height: 1; + display: inline-block; + max-width: 90%; + margin: 0 5% 5px; + white-space: nowrap; + text-overflow: ellipsis; + opacity: .8; + color: #ccc; + filter: alpha(opacity=80) +} + +.viewer-title:hover { + opacity: 1; + filter: alpha(opacity=100) +} + +.viewer-button { + position: absolute; + top: -40px; + right: -40px; + width: 80px; + height: 80px; + cursor: pointer; + border-radius: 50%; + background-color: #000; + background-color: rgba(0, 0, 0, .5) +} + +.viewer-button:before { + position: absolute; + bottom: 15px; + left: 15px +} + +.viewer-fixed { + position: fixed +} + +.viewer-show { + display: block +} + +.viewer-hide { + display: none +} + +.viewer-invisible { + visibility: hidden +} + +.viewer-move { + cursor: move; + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab +} + +.viewer-fade { + opacity: 0; + filter: alpha(opacity=0) +} + +.viewer-in { + opacity: 1; + filter: alpha(opacity=100) +} + +.viewer-transition { + -webkit-transition: all .3s ease-out; + -o-transition: all .3s ease-out; + transition: all .3s ease-out +} + +@media (max-width:767px) { + .viewer-hide-xs-down { + display: none + } +} + +@media (max-width:991px) { + .viewer-hide-sm-down { + display: none + } +} + +@media (max-width:1199px) { + .viewer-hide-md-down { + display: none + } +} \ No newline at end of file diff --git a/public/admin/template/default/css/product.css b/public/admin/template/default/css/product.css index d814494e..7a102bd9 100755 --- a/public/admin/template/default/css/product.css +++ b/public/admin/template/default/css/product.css @@ -8,7 +8,7 @@ color: var(--td-brand-color); } .product .second-name { - margin-left: 10px; + margin-left: 20px; } .product .product-table p { margin: 0; @@ -20,43 +20,35 @@ color: var(--td-text-color-placeholder); cursor: pointer; } -.product .connect-group .t-dialog__header { - font-size: 12px; - color: var(--td-text-color-secondary); - font-weight: normal; -} -.product .connect-group .t-dialog__header b { - font-size: 18px; - font-weight: bold; - color: var(--td-text-color-primary); +.product .auth-dialog .t-form__controls-content { + display: flex; + flex-direction: column; + align-items: flex-start; } -.product .connect-group .t-dialog__header .t-icon { - color: var(--td-warning-color); - margin: 0 5px 0 10px; +.product .opt { + margin-top: 5px; + display: flex; + width: 100%; + justify-content: space-between; } -.product .row-bg { - background-color: #F3F5F8; +.product .opt .tip { + color: red; } -/* product-detail */ -.product-detail .t-radio { - display: flex; - align-items: center; +.product .disable { + cursor: not-allowed; } -.product-detail .small-tit { - font-size: 18px; +.product .anget-btn { + background: rgba(0, 83, 217, 0.2); + border: none; + color: #0052D9; } +/* product-detail */ .product-detail .t-form { display: flex; } .product-detail .box { padding: 13px 20px 20px; } -.product-detail textarea { - height: 70px; -} -.product-detail .t-switch { - margin-left: 10px; -} .product-detail .product-container .item { display: flex; justify-content: space-between; @@ -73,22 +65,9 @@ flex: 1; margin-left: 0 !important; } -.product-detail .connect { - margin-top: 30px; -} -.product-detail .r-box .t-col { - margin-bottom: 24px; -} -.product-detail .r-box .t-form:not(.t-form-inline) .t-form__item:last-of-type { - margin-bottom: auto; -} -.product-detail .r-box .free { - margin-top: 102px; -} .product-detail .dis-box .half { display: flex; justify-content: space-between; - margin-bottom: 0; } .product-detail .dis-box .half .r-item { width: 45%; @@ -101,25 +80,23 @@ .product-detail textarea { width: 100% !important; } +.product-detail .connect { + margin-top: 24px; +} +.product-detail .footer-btn { + margin-top: 24px; +} .product-detail .footer-btn .t-button { margin-right: 20px; } /* product-api */ +/* product-api */ .product-api { min-height: auto !important; } .product-api .box { padding: 13px 20px 20px; } -.product-api .t-form__label, -.product-api .t-form__controls { - width: 100% !important; - text-align: left; - margin-left: 0 !important; -} -.product-api .dis-box { - margin: 0 !important; -} .product-api .t-form > .item { display: flex; align-items: flex-end; @@ -128,14 +105,3 @@ margin-bottom: 0; margin-right: 40px; } -.product-api .t-button { - margin-right: 20px; -} -.product-api .t-select { - width: 200px; -} -@media screen and (max-width: 1440px) { - .product-detail .t-col:first-child { - margin-bottom: 40px; - } -} diff --git a/public/admin/template/default/css/product.less b/public/admin/template/default/css/product.less index e25b0e63..dec6f9c0 100755 --- a/public/admin/template/default/css/product.less +++ b/public/admin/template/default/css/product.less @@ -1,46 +1,92 @@ /* 商品管理 */ -.product{ - td.t-table__handle-draggable{ +.product { + td.t-table__handle-draggable { padding-left: 0 !important; padding-right: 0 !important; } - .first-name,.product-name{ + + .first-name, + .product-name { color: var(--td-brand-color); } - .second-name{ + + .second-name { margin-left: 20px; } - .product-table{ - p{ + + .product-table { + p { margin: 0; display: flex; align-items: center; - .t-icon{ + + .t-icon { margin-left: 5px; color: var(--td-text-color-placeholder); cursor: pointer; } } } + + .auth-dialog { + .t-form__controls-content { + display: flex; + flex-direction: column; + align-items: flex-start; + } + + .t-dialog__body { + + } + } + + .opt { + margin-top: 5px; + display: flex; + width: 100%; + justify-content: space-between; + + .tip { + color: red; + } + } + + .auth {} + + .disable { + cursor: not-allowed; + } + + .anget-btn { + background: rgba(0, 83, 217, 0.2); + border: none; + color: #0052D9; + } } + /* product-detail */ -.product-detail{ - .t-form{ +.product-detail { + .t-form { display: flex; } - .box{ + + .box { padding: 13px 20px 20px; } - .product-container{ - .item{ + + .product-container { + .item { display: flex; justify-content: space-between; } - .t-form__item{ + + .t-form__item { flex-direction: column; flex-wrap: wrap; width: 48%; - .t-form__label,.t-form__controls{ + + .t-form__label, + .t-form__controls { width: 100% !important; text-align: left; flex: 1; @@ -48,40 +94,75 @@ } } } - .dis-box{ - .half{ + + .dis-box { + .half { display: flex; justify-content: space-between; - .r-item{ + + .r-item { width: 45%; } } - p{ + + p { line-height: 32px; margin: 0; } } - .textarea,textarea{ + + .textarea, + textarea { width: 100% !important; } + .connect{ + margin-top: 24px; + } + .footer-btn{ + margin-top: 24px; + .t-button { + margin-right: 20px; + } + } } + +/* product-api */ +// .product-api { +// .box { +// padding: 13px 20px 20px; +// } + +// .t-form__label, +// .t-form__controls { +// width: 100% !important; +// text-align: left; +// margin-left: 0 !important; +// } + +// .dis-box { +// margin: 0 !important; +// } + +// .t-form { +// width: 155px; +// } + +// .t-button { +// margin-right: 20px; +// } +// } /* product-api */ .product-api{ + min-height: auto !important; .box{ padding: 13px 20px 20px; } - .t-form__label,.t-form__controls{ - width: 100% !important; - text-align: left; - margin-left: 0 !important; - } - .dis-box{ - margin: 0 !important; - } - .t-form{ - width: 155px; - } - .t-button{ - margin-right: 20px; + .t-form > .item{ + display: flex; + align-items: flex-end; + .t-form__item{ + margin-bottom: 0; + margin-right: 40px; + } } } \ No newline at end of file diff --git a/public/admin/template/default/css/supplier_list.css b/public/admin/template/default/css/supplier_list.css new file mode 100644 index 00000000..573457f1 --- /dev/null +++ b/public/admin/template/default/css/supplier_list.css @@ -0,0 +1,7 @@ +.supplier_list { + flex-direction: column; +} +.supplier_list .add-btn { + margin-top: 26px; + margin-bottom: 18px; +} diff --git a/public/admin/template/default/css/supplier_list.less b/public/admin/template/default/css/supplier_list.less new file mode 100644 index 00000000..6bd41daa --- /dev/null +++ b/public/admin/template/default/css/supplier_list.less @@ -0,0 +1,8 @@ +.supplier_list { + flex-direction: column; + + .add-btn { + margin-top: 26px; + margin-bottom: 18px; + } +} \ No newline at end of file diff --git a/public/admin/template/default/css/system.css b/public/admin/template/default/css/system.css new file mode 100644 index 00000000..97d9983a --- /dev/null +++ b/public/admin/template/default/css/system.css @@ -0,0 +1,138 @@ +.feedback .com-top { + display: flex; + justify-content: space-between; + align-items: center; + line-height: 1; + margin-bottom: 10px; +} +.feedback .com-top .add-btn { + cursor: pointer; + line-height: 32px; + height: 32px; + background: var(--td-brand-color-8); + font-size: 14px; + color: #fff; + padding: 0 14px; + border-radius: 4px; +} +.feedback .com-top .tit { + margin: 0; +} +.feedback .limit-table { + max-width: 900px; +} +.feedback .limit-table .com-top { + margin-top: 30px; +} +.feedback .limit-table thead tr th { + background: #f3f5f8; +} +.feedback .limit-table th, +.feedback .limit-table td { + padding: 12px 24px; +} +.feedback .com-opt .t-icon { + color: var(--td-brand-color-active); + margin-right: 10px; + font-size: 18px; + cursor: pointer; +} +.feedback .type-table { + margin-top: 30px; +} +.feedback .type-table thead tr th { + background: #f3f5f8; +} +.feedback .type-table th, +.feedback .type-table td { + padding: 12px 24px; +} +.feedback .attachment { + display: flex; +} +.feedback .attachment .down { + flex: 1; +} +.feedback .attachment .down p { + margin: 0; +} +.feedback .attachment .down span { + color: rgba(0, 0, 0, 0.4); + cursor: pointer; +} +.feedback .attachment .down span:hover { + color: var(--td-brand-color-active); +} +.feedback .info-form { + max-width: 700px; + display: flex; + flex-wrap: wrap; +} +.feedback .info-form .t-form__item { + width: 330px; + margin-right: 20px; +} +.feedback .info-form .t-form__item:nth-child(2n + 2) { + margin-right: 0; +} +.feedback .info-form .qrcode { + width: 100%; +} +.feedback .info-form .info-btn { + margin-top: 20px; +} +.feedback .t-upload .t-dialog { + width: 800px !important; + min-height: 500px !important; + display: flex; + align-items: center; + justify-content: center; + max-width: 100%; + position: relative; +} +.feedback .t-upload .t-dialog__header { + display: none; +} +.feedback .t-upload .t-dialog__close { + z-index: 10; +} +.feedback .t-upload .t-dialog__body { + height: 100%; + top: 0; + left: 0; + padding: 40px; + position: absolute; + width: 100%; + box-sizing: border-box; +} +.feedback .t-upload .t__dialog-body-img-box { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.feedback .t-upload .t__dialog-body-img-box img { + max-width: 100%; + max-height: 100%; +} +#viewer { + width: 0; + height: 0; +} +.viewer-container { + z-index: 20000 !important; +} +.viewer-navbar { + display: none !important; +} +.class-dialog .t-input__wrap { + width: 360px; +} +.class-dialog .des { + padding: 10px; + background: #f3f5f8; + border-radius: 3px; + min-height: 200px; + max-height: 500px; + overflow-y: auto; +} diff --git a/public/admin/template/default/css/system.less b/public/admin/template/default/css/system.less new file mode 100644 index 00000000..a884e7aa --- /dev/null +++ b/public/admin/template/default/css/system.less @@ -0,0 +1,150 @@ + +.feedback{ + .com-top { + display: flex; + justify-content: space-between; + align-items: center; + line-height: 1; + margin-bottom: 10px; + .add-btn { + cursor: pointer; + line-height: 32px; + height: 32px; + background: var(--td-brand-color-8); + font-size: 14px; + color: #fff; + padding: 0 14px; + border-radius: 4px; + } + + .tit { + margin: 0; + } + } + .limit-table{ + max-width: 900px; + .com-top{ + margin-top: 30px; + } + thead { + tr th { + background: #f3f5f8; + } + } + th,td{ + padding: 12px 24px; + } + } + .com-opt{ + .t-icon{ + color: var(--td-brand-color-active); + margin-right: 10px; + font-size: 18px; + cursor: pointer; + } + } + .type-table{ + margin-top: 30px; + thead { + tr th { + background: #f3f5f8; + } + } + th,td{ + padding: 12px 24px; + } + } + .attachment{ + display: flex; + .down{ + flex: 1; + p{ + margin: 0; + } + span{ + color: rgba(0, 0, 0, 0.4); + cursor: pointer; + &:hover{ + color: var(--td-brand-color-active); + } + } + } + } + .info-form{ + max-width: 700px; + display: flex; + flex-wrap: wrap; + .t-form__item{ + width: 330px; + margin-right: 20px; + &:nth-child(2n + 2) { + margin-right: 0; + } + } + .qrcode{ + width: 100%; + } + .info-btn{ + margin-top: 20px; + } + } + .t-upload{ + .t-dialog{ + width: 800px !important; + min-height: 500px !important; + display: flex; + align-items: center; + justify-content: center; + max-width: 100%; + position: relative; + } + .t-dialog__header{ + display: none; + } + .t-dialog__close{ + z-index: 10; + } + .t-dialog__body{ + height: 100%; + top: 0; + left: 0; + padding: 40px; + position: absolute; + width: 100%; + box-sizing: border-box; + } + .t__dialog-body-img-box{ + display: flex; + align-items: center; + justify-content: center; + height: 100%; + img{ + max-width: 100%; + max-height: 100%; + } + } + } +} +#viewer { + width: 0; + height: 0; +} +.viewer-container{ + z-index: 20000 !important; +} +.viewer-navbar{ + display: none !important; +} +.class-dialog { + .t-input__wrap { + width: 360px; + } + .des{ + padding: 10px; + background: #f3f5f8; + border-radius: 3px; + min-height: 200px; + max-height: 500px; + overflow-y: auto; + } +} \ No newline at end of file diff --git a/public/admin/template/default/css/upstream_order.css b/public/admin/template/default/css/upstream_order.css new file mode 100644 index 00000000..c93bbf72 --- /dev/null +++ b/public/admin/template/default/css/upstream_order.css @@ -0,0 +1,151 @@ +.supplier_order { + flex-direction: column; + display: block !important; +} +.supplier_order .b-table { + margin-top: 20px; +} +.supplier_order .top-card .t-card__body { + padding-bottom: 60px; +} +.supplier_order .common-tab { + margin-bottom: 54px; +} +.supplier_order .bottom-card .t-card__body { + padding: 26px 20px 50px; +} +.supplier_order .common-header { + margin-top: 0; +} +.supplier_order .money-box { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} +.supplier_order .money-box .item { + display: flex; + align-items: center; + font-size: 13px; + line-height: 1; + color: #9696A3; + justify-content: space-between; +} +.supplier_order .money-box .item p { + margin: 0; +} +.supplier_order .money-box .item .l-text { + margin-right: 65px; +} +.supplier_order .money-box .item .num { + font-size: 32px; + color: rgba(0, 0, 0, 0.9); + margin-bottom: 10px; +} +.supplier_order .money-box .item img { + width: 83px; + height: 50px; +} +.upstream_order { + flex-direction: column; + display: block !important; +} +.upstream_order .b-table { + margin-top: 20px; +} +.upstream_order .top-card .t-card__body { + padding-top: 60px; + padding-bottom: 60px; +} +.upstream_order .common-tab { + margin-bottom: 54px; +} +.upstream_order .bottom-card .t-card__body { + padding: 26px 20px 50px; +} +.upstream_order .common-header { + margin-top: 0; +} +.upstream_order .money-box { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} +.upstream_order .money-box .item { + display: flex; + align-items: center; + font-size: 13px; + line-height: 1; + color: #9696A3; + justify-content: space-between; +} +.upstream_order .money-box .item p { + margin: 0; +} +.upstream_order .money-box .item .l-text { + margin-right: 65px; +} +.upstream_order .money-box .item .btn { + margin-top: 8px; + height: 24px; + line-height: 24px; + padding: 0 8px; + text-align: center; + border: 1px solid #0052D9; + cursor: pointer; + font-size: 12px; + border-radius: 3px; + color: #0052D9; +} +.upstream_order .money-box .item .num { + font-size: 32px; + color: rgba(0, 0, 0, 0.9); + margin-bottom: 10px; +} +.upstream_order .money-box .item img { + width: 83px; + height: 50px; +} +.client-search { + width: 460px; +} +.upstream_goods .add-btn { + margin-left: 20px; + color: #0052D9; + background: rgba(0, 83, 217, 0.2); + border: none; +} +.agent-list .greey-color { + cursor: pointer; + color: rgba(0, 0, 0, 0.26); +} +.agent-list .goods-info { + padding: 14px 25px 14px 15px; + margin-bottom: 20px; + background: #F0F0F0; + border-radius: 3px; +} +.agent-list .goods-info > div { + font-size: 14px; + display: flex; + margin-bottom: 4px; +} +.agent-list .goods-info > div .leble { + color: rgba(0, 0, 0, 0.4); + flex-shrink: 0; +} +.agent-list .goods-info > div .value { + color: #333333; +} +.agent-list .first-item .t-form__controls-content { + flex-wrap: wrap; +} +.agent-list .first-item .t-input__wrap { + width: 94%; +} +.no-flex .t-form__controls-content { + display: block !important; +} +.tips-div { + color: rgba(0, 0, 0, 0.4); + margin-top: 4px; +} diff --git a/public/admin/template/default/css/upstream_order.less b/public/admin/template/default/css/upstream_order.less new file mode 100644 index 00000000..43d917b8 --- /dev/null +++ b/public/admin/template/default/css/upstream_order.less @@ -0,0 +1,209 @@ +.supplier_order { + flex-direction: column; + display: block !important; + + .b-table { + margin-top: 20px; + } + + .top-card { + .t-card__body { + padding-bottom: 60px; + } + } + + .common-tab { + margin-bottom: 54px; + } + + .bottom-card { + .t-card__body { + padding: 26px 20px 50px; + } + } + + .common-header { + margin-top: 0; + } + + .money-box { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + + + .item { + display: flex; + align-items: center; + font-size: 13px; + line-height: 1; + color: #9696A3; + justify-content: space-between; + + p { + margin: 0; + } + + .l-text { + margin-right: 65px; + } + + .txt {} + + .num { + font-size: 32px; + color: rgba(0, 0, 0, .9); + margin-bottom: 10px; + } + + img { + width: 83px; + height: 50px; + } + } + } +} + +.upstream_order { + flex-direction: column; + display: block !important; + + .b-table { + margin-top: 20px; + } + + .top-card { + .t-card__body { + padding-top: 60px; + padding-bottom: 60px; + } + } + + .common-tab { + margin-bottom: 54px; + } + + .bottom-card { + .t-card__body { + padding: 26px 20px 50px; + } + } + + .common-header { + margin-top: 0; + } + + .money-box { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + + + .item { + display: flex; + align-items: center; + font-size: 13px; + line-height: 1; + color: #9696A3; + justify-content: space-between; + + p { + margin: 0; + } + + .l-text { + margin-right: 65px; + } + + .txt {} + + .btn { + margin-top: 8px; + height: 24px; + line-height: 24px; + padding: 0 8px; + text-align: center; + border: 1px solid #0052D9; + cursor: pointer; + font-size: 12px; + border-radius: 3px; + color: #0052D9; + } + + .num { + font-size: 32px; + color: rgba(0, 0, 0, .9); + margin-bottom: 10px; + } + + img { + width: 83px; + height: 50px; + } + } + } +} + +.client-search { + width: 460px; +} + +.upstream_goods { + .add-btn { + margin-left: 20px; + color: #0052D9; + background: rgba(0, 83, 217, 0.2); + border: none; + } +} + +.agent-list { + .greey-color { + cursor: pointer; + color: rgba(0, 0, 0, 0.26); + } + + .goods-info { + padding: 14px 25px 14px 15px; + margin-bottom: 20px; + background: #F0F0F0; + border-radius: 3px; + + >div { + font-size: 14px; + display: flex; + margin-bottom: 4px; + + .leble { + color: rgba(0, 0, 0, 0.40); + flex-shrink: 0; + } + + .value { + color: rgba(51, 51, 51, 1); + } + } + } + + .first-item { + .t-form__controls-content { + flex-wrap: wrap; + } + + .t-input__wrap { + width: 94%; + } + } + +} + +.no-flex { + .t-form__controls-content { + display: block !important; + } +} + +.tips-div { + color: rgba(0, 0, 0, 0.4); + margin-top: 4px; +} \ No newline at end of file diff --git a/public/admin/template/default/host.php b/public/admin/template/default/host.php index ec0732d4..0b7f2997 100755 --- a/public/admin/template/default/host.php +++ b/public/admin/template/default/host.php @@ -10,8 +10,7 @@ - + @@ -29,9 +28,9 @@ -