作者 郭文星

123

正在显示 53 个修改的文件 包含 4764 行增加0 行删除

要显示太多修改。

为保证性能只显示 53 of 53+ 个文件。

{"files":["application\\admin\\controller\\groupon\\activity\\Activity.php","application\\admin\\controller\\groupon\\Admin.php","application\\admin\\controller\\groupon\\Area.php","application\\admin\\controller\\groupon\\Base.php","application\\admin\\controller\\groupon\\Category.php","application\\admin\\controller\\groupon\\Config.php","application\\admin\\controller\\groupon\\Coupons.php","application\\admin\\controller\\groupon\\Decorate.php","application\\admin\\controller\\groupon\\Express.php","application\\admin\\controller\\groupon\\Faq.php","application\\admin\\controller\\groupon\\Feedback.php","application\\admin\\controller\\groupon\\goods\\Comment.php","application\\admin\\controller\\groupon\\goods\\Goods.php","application\\admin\\controller\\groupon\\goods\\Service.php","application\\admin\\controller\\groupon\\goods\\Sku.php","application\\admin\\controller\\groupon\\goods\\SkuPrice.php","application\\admin\\controller\\groupon\\goods\\StockWarning.php","application\\admin\\controller\\groupon\\Link.php","application\\admin\\controller\\groupon\\Notification.php","application\\admin\\controller\\groupon\\order\\Order.php","application\\admin\\controller\\groupon\\order\\OrderItem.php","application\\admin\\controller\\groupon\\order\\StoreExpress.php","application\\admin\\controller\\groupon\\Richtext.php","application\\admin\\controller\\groupon\\store\\Apply.php","application\\admin\\controller\\groupon\\store\\Dashboard.php","application\\admin\\controller\\groupon\\store\\Store.php","application\\admin\\controller\\groupon\\store\\TakeLog.php","application\\admin\\controller\\groupon\\Upload.php","application\\admin\\controller\\groupon\\user\\Group.php","application\\admin\\controller\\groupon\\user\\Rule.php","application\\admin\\controller\\groupon\\user\\User.php","application\\admin\\controller\\groupon\\UserWalletApply.php","application\\admin\\lang\\zh-cn\\groupon\\area.php","application\\admin\\lang\\zh-cn\\groupon\\category.php","application\\admin\\lang\\zh-cn\\groupon\\config.php","application\\admin\\lang\\zh-cn\\groupon\\coupons.php","application\\admin\\lang\\zh-cn\\groupon\\decorate.php","application\\admin\\lang\\zh-cn\\groupon\\faq.php","application\\admin\\lang\\zh-cn\\groupon\\feedback.php","application\\admin\\lang\\zh-cn\\groupon\\goods\\comment.php","application\\admin\\lang\\zh-cn\\groupon\\goods\\goods.php","application\\admin\\lang\\zh-cn\\groupon\\goods\\service.php","application\\admin\\lang\\zh-cn\\groupon\\goods\\sku.php","application\\admin\\lang\\zh-cn\\groupon\\goods\\sku_price.php","application\\admin\\lang\\zh-cn\\groupon\\link.php","application\\admin\\lang\\zh-cn\\groupon\\notification\\config.php","application\\admin\\lang\\zh-cn\\groupon\\notification.php","application\\admin\\lang\\zh-cn\\groupon\\order\\order.php","application\\admin\\lang\\zh-cn\\groupon\\order\\order_item.php","application\\admin\\lang\\zh-cn\\groupon\\order\\refund_log.php","application\\admin\\lang\\zh-cn\\groupon\\richtext.php","application\\admin\\lang\\zh-cn\\groupon\\share.php","application\\admin\\lang\\zh-cn\\groupon\\store\\apply.php","application\\admin\\lang\\zh-cn\\groupon\\upload.php","application\\admin\\lang\\zh-cn\\groupon\\user\\group.php","application\\admin\\lang\\zh-cn\\groupon\\user\\rule.php","application\\admin\\lang\\zh-cn\\groupon\\user\\user.php","application\\admin\\lang\\zh-cn\\groupon\\user_wallet_apply.php","application\\admin\\model\\groupon\\activity\\Activity.php","application\\admin\\model\\groupon\\Area.php","application\\admin\\model\\groupon\\Category.php","application\\admin\\model\\groupon\\Config.php","application\\admin\\model\\groupon\\Coupons.php","application\\admin\\model\\groupon\\Decorate.php","application\\admin\\model\\groupon\\DecorateContent.php","application\\admin\\model\\groupon\\DecoratePage.php","application\\admin\\model\\groupon\\dispatch\\ExpressCompany.php","application\\admin\\model\\groupon\\Express.php","application\\admin\\model\\groupon\\Faq.php","application\\admin\\model\\groupon\\Feedback.php","application\\admin\\model\\groupon\\goods\\Comment.php","application\\admin\\model\\groupon\\goods\\Goods.php","application\\admin\\model\\groupon\\goods\\Service.php","application\\admin\\model\\groupon\\goods\\Sku.php","application\\admin\\model\\groupon\\goods\\SkuPrice.php","application\\admin\\model\\groupon\\goods\\StockWarning.php","application\\admin\\model\\groupon\\Link.php","application\\admin\\model\\groupon\\notification\\Config.php","application\\admin\\model\\groupon\\Notification.php","application\\admin\\model\\groupon\\order\\Order.php","application\\admin\\model\\groupon\\order\\OrderAction.php","application\\admin\\model\\groupon\\order\\OrderExpress.php","application\\admin\\model\\groupon\\order\\OrderExpressLog.php","application\\admin\\model\\groupon\\order\\OrderItem.php","application\\admin\\model\\groupon\\order\\OrderStoreExpress.php","application\\admin\\model\\groupon\\order\\RefundLog.php","application\\admin\\model\\groupon\\Richtext.php","application\\admin\\model\\groupon\\Share.php","application\\admin\\model\\groupon\\store\\Apply.php","application\\admin\\model\\groupon\\store\\Recruit.php","application\\admin\\model\\groupon\\store\\Store.php","application\\admin\\model\\groupon\\store\\TakeLog.php","application\\admin\\model\\groupon\\Upload.php","application\\admin\\model\\groupon\\user\\Coupon.php","application\\admin\\model\\groupon\\user\\Favorite.php","application\\admin\\model\\groupon\\user\\Group.php","application\\admin\\model\\groupon\\user\\MoneyLog.php","application\\admin\\model\\groupon\\user\\Oauth.php","application\\admin\\model\\groupon\\user\\ScoreLog.php","application\\admin\\model\\groupon\\user\\User.php","application\\admin\\model\\groupon\\user\\View.php","application\\admin\\model\\groupon\\UserFake.php","application\\admin\\model\\groupon\\UserWalletApply.php","application\\admin\\model\\groupon\\wechat\\Fans.php","application\\admin\\model\\groupon\\wechat\\Wechat.php","application\\admin\\validate\\groupon\\activity\\Activity.php","application\\admin\\validate\\groupon\\Area.php","application\\admin\\validate\\groupon\\Category.php","application\\admin\\validate\\groupon\\Config.php","application\\admin\\validate\\groupon\\Coupons.php","application\\admin\\validate\\groupon\\Decorate.php","application\\admin\\validate\\groupon\\Faq.php","application\\admin\\validate\\groupon\\Feedback.php","application\\admin\\validate\\groupon\\Goods.php","application\\admin\\validate\\groupon\\GoodsComment.php","application\\admin\\validate\\groupon\\GoodsService.php","application\\admin\\validate\\groupon\\GoodsSku.php","application\\admin\\validate\\groupon\\GoodsSkuPrice.php","application\\admin\\validate\\groupon\\Link.php","application\\admin\\validate\\groupon\\notification\\Config.php","application\\admin\\validate\\groupon\\Notification.php","application\\admin\\validate\\groupon\\Notification_config.php","application\\admin\\validate\\groupon\\order\\Order.php","application\\admin\\validate\\groupon\\order\\OrderItem.php","application\\admin\\validate\\groupon\\Richtext.php","application\\admin\\validate\\groupon\\store\\Apply.php","application\\admin\\validate\\groupon\\Upload.php","application\\admin\\validate\\groupon\\UserWalletApply.php","application\\admin\\view\\groupon\\activity\\activity\\edit.html","application\\admin\\view\\groupon\\activity\\activity\\index.html","application\\admin\\view\\groupon\\activity\\activity\\recyclebin.html","application\\admin\\view\\groupon\\activity\\activity\\select.html","application\\admin\\view\\groupon\\area\\add.html","application\\admin\\view\\groupon\\area\\edit.html","application\\admin\\view\\groupon\\area\\index.html","application\\admin\\view\\groupon\\area\\select.html","application\\admin\\view\\groupon\\category\\index.html","application\\admin\\view\\groupon\\category\\select.html","application\\admin\\view\\groupon\\config\\add.html","application\\admin\\view\\groupon\\config\\edit.html","application\\admin\\view\\groupon\\config\\index.html","application\\admin\\view\\groupon\\config\\platform.html","application\\admin\\view\\groupon\\coupons\\add.html","application\\admin\\view\\groupon\\coupons\\edit.html","application\\admin\\view\\groupon\\coupons\\index.html","application\\admin\\view\\groupon\\coupons\\recyclebin.html","application\\admin\\view\\groupon\\coupons\\select.html","application\\admin\\view\\groupon\\decorate\\custom.html","application\\admin\\view\\groupon\\decorate\\dodecorate.html","application\\admin\\view\\groupon\\decorate\\lists.html","application\\admin\\view\\groupon\\decorate\\recyclebin.html","application\\admin\\view\\groupon\\decorate\\select.html","application\\admin\\view\\groupon\\express\\add.html","application\\admin\\view\\groupon\\express\\edit.html","application\\admin\\view\\groupon\\express\\index.html","application\\admin\\view\\groupon\\faq\\add.html","application\\admin\\view\\groupon\\faq\\edit.html","application\\admin\\view\\groupon\\faq\\index.html","application\\admin\\view\\groupon\\faq\\recyclebin.html","application\\admin\\view\\groupon\\feedback\\edit.html","application\\admin\\view\\groupon\\feedback\\index.html","application\\admin\\view\\groupon\\feedback\\recyclebin.html","application\\admin\\view\\groupon\\goods\\comment\\add.html","application\\admin\\view\\groupon\\goods\\comment\\edit.html","application\\admin\\view\\groupon\\goods\\comment\\index.html","application\\admin\\view\\groupon\\goods\\comment\\recyclebin.html","application\\admin\\view\\groupon\\goods\\goods\\add.html","application\\admin\\view\\groupon\\goods\\goods\\edit.html","application\\admin\\view\\groupon\\goods\\goods\\index.html","application\\admin\\view\\groupon\\goods\\goods\\recyclebin.html","application\\admin\\view\\groupon\\goods\\goods\\select.html","application\\admin\\view\\groupon\\goods\\goods\\sku.html","application\\admin\\view\\groupon\\goods\\goods\\skuedit.html","application\\admin\\view\\groupon\\goods\\service\\add.html","application\\admin\\view\\groupon\\goods\\service\\edit.html","application\\admin\\view\\groupon\\goods\\service\\index.html","application\\admin\\view\\groupon\\goods\\service\\recyclebin.html","application\\admin\\view\\groupon\\goods\\sku\\add.html","application\\admin\\view\\groupon\\goods\\sku\\edit.html","application\\admin\\view\\groupon\\goods\\sku\\index.html","application\\admin\\view\\groupon\\goods\\sku_price\\add.html","application\\admin\\view\\groupon\\goods\\sku_price\\edit.html","application\\admin\\view\\groupon\\goods\\sku_price\\index.html","application\\admin\\view\\groupon\\goods\\sku_price\\recyclebin.html","application\\admin\\view\\groupon\\goods\\stock_warning\\index.html","application\\admin\\view\\groupon\\goods\\stock_warning\\recyclebin.html","application\\admin\\view\\groupon\\link\\add.html","application\\admin\\view\\groupon\\link\\edit.html","application\\admin\\view\\groupon\\link\\index.html","application\\admin\\view\\groupon\\link\\recyclebin.html","application\\admin\\view\\groupon\\link\\select.html","application\\admin\\view\\groupon\\notification\\config.html","application\\admin\\view\\groupon\\order\\order\\detail.html","application\\admin\\view\\groupon\\order\\order\\index.html","application\\admin\\view\\groupon\\order\\order\\recyclebin.html","application\\admin\\view\\groupon\\order\\order_item\\add.html","application\\admin\\view\\groupon\\order\\order_item\\edit.html","application\\admin\\view\\groupon\\order\\order_item\\index.html","application\\admin\\view\\groupon\\order\\order_item\\recyclebin.html","application\\admin\\view\\groupon\\order\\store_express\\add.html","application\\admin\\view\\groupon\\order\\store_express\\detail.html","application\\admin\\view\\groupon\\order\\store_express\\index.html","application\\admin\\view\\groupon\\richtext\\add.html","application\\admin\\view\\groupon\\richtext\\edit.html","application\\admin\\view\\groupon\\richtext\\index.html","application\\admin\\view\\groupon\\richtext\\recyclebin.html","application\\admin\\view\\groupon\\richtext\\select.html","application\\admin\\view\\groupon\\store\\apply\\add.html","application\\admin\\view\\groupon\\store\\apply\\detail.html","application\\admin\\view\\groupon\\store\\apply\\edit.html","application\\admin\\view\\groupon\\store\\apply\\index.html","application\\admin\\view\\groupon\\store\\dashboard\\index.html","application\\admin\\view\\groupon\\store\\store\\add.html","application\\admin\\view\\groupon\\store\\store\\edit.html","application\\admin\\view\\groupon\\store\\store\\index.html","application\\admin\\view\\groupon\\store\\store\\recyclebin.html","application\\admin\\view\\groupon\\store\\take_log\\index.html","application\\admin\\view\\groupon\\user\\group\\add.html","application\\admin\\view\\groupon\\user\\group\\edit.html","application\\admin\\view\\groupon\\user\\group\\index.html","application\\admin\\view\\groupon\\user\\rule\\add.html","application\\admin\\view\\groupon\\user\\rule\\edit.html","application\\admin\\view\\groupon\\user\\rule\\index.html","application\\admin\\view\\groupon\\user\\user\\index.html","application\\admin\\view\\groupon\\user\\user\\money_recharge.html","application\\admin\\view\\groupon\\user\\user\\profile.html","application\\admin\\view\\groupon\\user\\user\\score_recharge.html","application\\admin\\view\\groupon\\user_wallet_apply\\index.html","public\\assets\\addons\\groupon\\img\\activity\\full_discount.png","public\\assets\\addons\\groupon\\img\\activity\\full_gift.png","public\\assets\\addons\\groupon\\img\\activity\\full_reduce.png","public\\assets\\addons\\groupon\\img\\category\\edit.png","public\\assets\\addons\\groupon\\img\\category\\img-1.png","public\\assets\\addons\\groupon\\img\\category\\img-2.png","public\\assets\\addons\\groupon\\img\\category\\img-3.png","public\\assets\\addons\\groupon\\img\\category\\img-4.png","public\\assets\\addons\\groupon\\img\\category\\selected.png","public\\assets\\addons\\groupon\\img\\config\\alipay-icon.png","public\\assets\\addons\\groupon\\img\\config\\App-icon.png","public\\assets\\addons\\groupon\\img\\config\\apple-icon.png","public\\assets\\addons\\groupon\\img\\config\\chat-icon.png","public\\assets\\addons\\groupon\\img\\config\\groupon-icon.png","public\\assets\\addons\\groupon\\img\\config\\h5-icon.png","public\\assets\\addons\\groupon\\img\\config\\order-icon.png","public\\assets\\addons\\groupon\\img\\config\\score-icon.png","public\\assets\\addons\\groupon\\img\\config\\services-icon.png","public\\assets\\addons\\groupon\\img\\config\\share-icon.png","public\\assets\\addons\\groupon\\img\\config\\store-icon.png","public\\assets\\addons\\groupon\\img\\config\\user-icon.png","public\\assets\\addons\\groupon\\img\\config\\wallet-icon.png","public\\assets\\addons\\groupon\\img\\config\\wechat-icon.png","public\\assets\\addons\\groupon\\img\\config\\withdraw-icon.png","public\\assets\\addons\\groupon\\img\\config\\wxMiniProgram-icon.png","public\\assets\\addons\\groupon\\img\\config\\wxOfficialAccount-icon.png","public\\assets\\addons\\groupon\\img\\decorate\\activity-fire.png","public\\assets\\addons\\groupon\\img\\decorate\\adv-default-2.png","public\\assets\\addons\\groupon\\img\\decorate\\adv.png","public\\assets\\addons\\groupon\\img\\decorate\\adv_01.png","public\\assets\\addons\\groupon\\img\\decorate\\adv_02.png","public\\assets\\addons\\groupon\\img\\decorate\\adv_03.png","public\\assets\\addons\\groupon\\img\\decorate\\adv_04.png","public\\assets\\addons\\groupon\\img\\decorate\\adv_05.png","public\\assets\\addons\\groupon\\img\\decorate\\adv_06.png","public\\assets\\addons\\groupon\\img\\decorate\\adv_07.png","public\\assets\\addons\\groupon\\img\\decorate\\banner.png","public\\assets\\addons\\groupon\\img\\decorate\\banner_default.png","public\\assets\\addons\\groupon\\img\\decorate\\cat.png","public\\assets\\addons\\groupon\\img\\decorate\\category_tabs.png","public\\assets\\addons\\groupon\\img\\decorate\\coupons.png","public\\assets\\addons\\groupon\\img\\decorate\\coupons_bg.png","public\\assets\\addons\\groupon\\img\\decorate\\float-button.png","public\\assets\\addons\\groupon\\img\\decorate\\goods-group.png","public\\assets\\addons\\groupon\\img\\decorate\\goods-list.png","public\\assets\\addons\\groupon\\img\\decorate\\header.png","public\\assets\\addons\\groupon\\img\\decorate\\hot-label.png","public\\assets\\addons\\groupon\\img\\decorate\\image-default.png","public\\assets\\addons\\groupon\\img\\decorate\\image-default2.png","public\\assets\\addons\\groupon\\img\\decorate\\image-default3.png","public\\assets\\addons\\groupon\\img\\decorate\\limit-time.png","public\\assets\\addons\\groupon\\img\\decorate\\menu.png","public\\assets\\addons\\groupon\\img\\decorate\\menu_default.gif","public\\assets\\addons\\groupon\\img\\decorate\\popup.png","public\\assets\\addons\\groupon\\img\\decorate\\special-image.png","public\\assets\\addons\\groupon\\img\\decorate\\store-image.jpg","public\\assets\\addons\\groupon\\img\\decorate\\store.png","public\\assets\\addons\\groupon\\img\\decorate\\tabs-active.png","public\\assets\\addons\\groupon\\img\\decorate\\template_name.png","public\\assets\\addons\\groupon\\img\\decorate\\title-block.png","public\\assets\\addons\\groupon\\img\\decorate\\title1.png","public\\assets\\addons\\groupon\\img\\decorate\\title2.png","public\\assets\\addons\\groupon\\img\\decorate\\title3.png","public\\assets\\addons\\groupon\\img\\decorate\\title4.png","public\\assets\\addons\\groupon\\img\\decorate\\title5.png","public\\assets\\addons\\groupon\\img\\decorate\\wxMiniProgram.png","public\\assets\\addons\\groupon\\img\\decorate\\zujian.png","public\\assets\\addons\\groupon\\img\\default-avatar.png","public\\assets\\addons\\groupon\\img\\derocate_down.png","public\\assets\\addons\\groupon\\img\\derocate_up.png","public\\assets\\addons\\groupon\\img\\goods\\active.png","public\\assets\\addons\\groupon\\img\\goods\\batch.png","public\\assets\\addons\\groupon\\img\\goods\\close.png","public\\assets\\addons\\groupon\\img\\goods\\hover.png","public\\assets\\addons\\groupon\\img\\goods\\move.png","public\\assets\\addons\\groupon\\img\\goods\\selected.png","public\\assets\\addons\\groupon\\img\\notification\\wxMiniProgram.png","public\\assets\\addons\\groupon\\img\\sku_close.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\aftersale.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\allordernum.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\noget.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\nosend.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\orderpersonnum.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\payed.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\paymoney.png","public\\assets\\addons\\groupon\\img\\store\\dashboard\\refund.png","public\\assets\\addons\\groupon\\img\\store\\position-icon.png","public\\assets\\addons\\groupon\\img\\user_wallet_apply\\alipay.png","public\\assets\\addons\\groupon\\img\\user_wallet_apply\\bank.png","public\\assets\\addons\\groupon\\img\\user_wallet_apply\\wechat.png","public\\assets\\addons\\groupon\\libs\\antv.js","public\\assets\\addons\\groupon\\libs\\clipboard.min.js","public\\assets\\addons\\groupon\\libs\\color-thief.js","public\\assets\\addons\\groupon\\libs\\common.css","public\\assets\\addons\\groupon\\libs\\element\\element.css","public\\assets\\addons\\groupon\\libs\\element\\element.js","public\\assets\\addons\\groupon\\libs\\element\\fonts\\element-icons.ttf","public\\assets\\addons\\groupon\\libs\\element\\fonts\\element-icons.woff","public\\assets\\addons\\groupon\\libs\\groupon.js","public\\assets\\addons\\groupon\\libs\\moment.js","public\\assets\\addons\\groupon\\libs\\require-text.js","public\\assets\\addons\\groupon\\libs\\Sortable.min.js","public\\assets\\addons\\groupon\\libs\\vue-dev.js","public\\assets\\addons\\groupon\\libs\\vue.js","public\\assets\\addons\\groupon\\libs\\vuedraggable.js","public\\assets\\js\\backend\\groupon\\activity\\activity.js","public\\assets\\js\\backend\\groupon\\area.js","public\\assets\\js\\backend\\groupon\\category.js","public\\assets\\js\\backend\\groupon\\config.js","public\\assets\\js\\backend\\groupon\\coupons.js","public\\assets\\js\\backend\\groupon\\decorate.js","public\\assets\\js\\backend\\groupon\\express.js","public\\assets\\js\\backend\\groupon\\faq.js","public\\assets\\js\\backend\\groupon\\feedback.js","public\\assets\\js\\backend\\groupon\\goods\\comment.js","public\\assets\\js\\backend\\groupon\\goods\\goods.js","public\\assets\\js\\backend\\groupon\\goods\\service.js","public\\assets\\js\\backend\\groupon\\goods\\sku.js","public\\assets\\js\\backend\\groupon\\goods\\sku_price.js","public\\assets\\js\\backend\\groupon\\goods\\stock_warning.js","public\\assets\\js\\backend\\groupon\\link.js","public\\assets\\js\\backend\\groupon\\notification.js","public\\assets\\js\\backend\\groupon\\order\\order.js","public\\assets\\js\\backend\\groupon\\order\\order_item.js","public\\assets\\js\\backend\\groupon\\order\\store_express.js","public\\assets\\js\\backend\\groupon\\richtext.js","public\\assets\\js\\backend\\groupon\\store\\apply.js","public\\assets\\js\\backend\\groupon\\store\\dashboard.js","public\\assets\\js\\backend\\groupon\\store\\store.js","public\\assets\\js\\backend\\groupon\\store\\take_log.js","public\\assets\\js\\backend\\groupon\\upload.js","public\\assets\\js\\backend\\groupon\\user\\group.js","public\\assets\\js\\backend\\groupon\\user\\user.js","public\\assets\\js\\backend\\groupon\\user_wallet_apply.js"],"license":"regular","licenseto":"44234","licensekey":"oO7jRk8tbSxzT2hJ QYIUbhnu2J0xrMQyJb0sfQ==","domains":[],"licensecodes":[],"validations":[],"menus":["groupon","groupon\/config","groupon\/config\/index","groupon\/config\/add","groupon\/config\/edit","groupon\/config\/del","groupon\/config\/multi","groupon\/config\/platform","groupon\/decorate","groupon\/decorate\/lists","groupon\/decorate\/add","groupon\/decorate\/edit","groupon\/decorate\/del","groupon\/decorate\/multi","groupon\/decorate\/recyclebin","groupon\/decorate\/destroy","groupon\/decorate\/restore","groupon\/decorate\/dodecorate","groupon\/decorate\/preview","groupon\/decorate\/custom","groupon\/decorate\/publish","groupon\/decorate\/dodecorate_save","groupon\/decorate\/copy","groupon\/decorate\/index","groupon\/decorate\/down","groupon\/coupons","groupon\/coupons\/index","groupon\/coupons\/recyclebin","groupon\/coupons\/add","groupon\/coupons\/edit","groupon\/coupons\/del","groupon\/coupons\/destroy","groupon\/coupons\/restore","groupon\/coupons\/multi","groupon\/coupons\/select","groupon\/product","groupon\/category","groupon\/category\/index","groupon\/category\/add","groupon\/category\/edit","groupon\/category\/del","groupon\/category\/multi","groupon\/category\/select","groupon\/category\/update","groupon\/goods\/goods","groupon\/goods\/goods\/index","groupon\/goods\/goods\/recyclebin","groupon\/goods\/goods\/add","groupon\/goods\/goods\/edit","groupon\/goods\/goods\/del","groupon\/goods\/goods\/destroy","groupon\/goods\/goods\/restore","groupon\/goods\/goods\/multi","groupon\/goods\/goods\/select","groupon\/goods\/goods\/detail","groupon\/goods\/goods\/setstatus","groupon\/goods\/service","groupon\/goods\/service\/index","groupon\/goods\/service\/recyclebin","groupon\/goods\/service\/add","groupon\/goods\/service\/edit","groupon\/goods\/service\/del","groupon\/goods\/service\/destroy","groupon\/goods\/service\/restore","groupon\/goods\/service\/multi","groupon\/goods\/comment","groupon\/goods\/comment\/index","groupon\/goods\/comment\/recyclebin","groupon\/goods\/comment\/add","groupon\/goods\/comment\/edit","groupon\/goods\/comment\/del","groupon\/goods\/comment\/destroy","groupon\/goods\/comment\/restore","groupon\/goods\/comment\/multi","groupon\/goods\/stock_warning","groupon\/goods\/stock_warning\/index","groupon\/goods\/stock_warning\/recyclebin","groupon\/goods\/stock_warning\/addstock","groupon\/goods\/stock_warning\/destroy","groupon\/material","groupon\/express","groupon\/express\/index","groupon\/express\/add","groupon\/express\/edit","groupon\/express\/del","groupon\/express\/multi","groupon\/express\/select","groupon\/richtext","groupon\/richtext\/index","groupon\/richtext\/recyclebin","groupon\/richtext\/add","groupon\/richtext\/edit","groupon\/richtext\/del","groupon\/richtext\/destroy","groupon\/richtext\/restore","groupon\/richtext\/multi","groupon\/richtext\/select","groupon\/link","groupon\/link\/index","groupon\/link\/recyclebin","groupon\/link\/add","groupon\/link\/edit","groupon\/link\/del","groupon\/link\/destroy","groupon\/link\/restore","groupon\/link\/multi","groupon\/link\/select","groupon\/area","groupon\/area\/index","groupon\/area\/add","groupon\/area\/edit","groupon\/area\/del","groupon\/area\/multi","groupon\/area\/select","groupon\/faq","groupon\/faq\/index","groupon\/faq\/recyclebin","groupon\/faq\/add","groupon\/faq\/edit","groupon\/faq\/del","groupon\/faq\/destroy","groupon\/faq\/restore","groupon\/faq\/multi","groupon\/user_wallet_apply","groupon\/user_wallet_apply\/index","groupon\/user_wallet_apply\/gettype","groupon\/user_wallet_apply\/handle","groupon\/user_wallet_apply\/log","groupon\/order","groupon\/order\/order","groupon\/order\/order\/index","groupon\/order\/order\/recyclebin","groupon\/order\/order\/add","groupon\/order\/order\/edit","groupon\/order\/order\/del","groupon\/order\/order\/destroy","groupon\/order\/order\/restore","groupon\/order\/order\/multi","groupon\/order\/order\/detail","groupon\/order\/order\/send","groupon\/order\/order\/aftersalefinish","groupon\/order\/order\/refund","groupon\/order\/order\/editmemo","groupon\/order\/order\/gettype","groupon\/order\/order\/refundrefuse","groupon\/order\/order\/export","groupon\/order\/order\/actions","groupon\/order\/order\/editconsignee","groupon\/order\/order\/sendstore","groupon\/order\/order_item","groupon\/order\/order_item\/index","groupon\/order\/order_item\/recyclebin","groupon\/order\/order_item\/add","groupon\/order\/order_item\/edit","groupon\/order\/order_item\/del","groupon\/order\/order_item\/destroy","groupon\/order\/order_item\/restore","groupon\/order\/order_item\/multi","groupon\/store\/dashboard","groupon\/store\/dashboard\/index","groupon\/order\/store_express","groupon\/order\/store_express\/getexpress","groupon\/order\/store_express\/index","groupon\/order\/store_express\/add","groupon\/order\/store_express\/send","groupon\/order\/store_express\/detail","groupon\/order\/store_express\/setexpress","groupon\/order\/store_express\/export","groupon\/feedback","groupon\/feedback\/index","groupon\/feedback\/recyclebin","groupon\/feedback\/add","groupon\/feedback\/edit","groupon\/feedback\/del","groupon\/feedback\/destroy","groupon\/feedback\/restore","groupon\/feedback\/multi","groupon\/feedback\/detail","groupon\/notification","groupon\/notification\/index","groupon\/notification\/recyclebin","groupon\/notification\/add","groupon\/notification\/edit","groupon\/notification\/del","groupon\/notification\/destroy","groupon\/notification\/restore","groupon\/notification\/config","groupon\/notification\/set_template","groupon\/notification\/set_status","groupon\/store","groupon\/store\/store","groupon\/store\/store\/add","groupon\/store\/store\/edit","groupon\/store\/store\/del","groupon\/store\/store\/recyclebin","groupon\/store\/store\/setstatus","groupon\/store\/store\/select","groupon\/store\/store\/index","groupon\/store\/store\/all","groupon\/store\/store\/restore","groupon\/store\/store\/destroy","groupon\/store\/apply","groupon\/store\/apply\/applyoper","groupon\/store\/apply\/index","groupon\/store\/apply\/detail","groupon\/store\/take_log","groupon\/store\/take_log\/index","groupon\/store\/take_log\/run_money","groupon\/store\/take_log\/back_money","groupon\/user\/user","groupon\/user\/user\/index","groupon\/user\/user\/select","groupon\/user\/user\/profile","groupon\/user\/user\/del","groupon\/user\/user\/order_log","groupon\/user\/user\/login_log","groupon\/user\/user\/money_log","groupon\/user\/user\/score_log","groupon\/user\/user\/share_log","groupon\/user\/user\/goods_favorite","groupon\/user\/user\/coupon_log","groupon\/user\/user\/goods_view","groupon\/user\/user\/recyclebin","groupon\/user\/user\/update","groupon\/user\/user\/money_recharge","groupon\/user\/user\/score_recharge","groupon\/activity","groupon\/activity\/activity","groupon\/activity\/activity\/index","groupon\/activity\/activity\/add","groupon\/activity\/activity\/edit","groupon\/activity\/activity\/del","groupon\/activity\/activity\/gettype","groupon\/activity\/activity\/recyclebin"]}
\ No newline at end of file
... ...
<?php
namespace addons\groupon;
use think\Addons;
use app\common\library\Menu;
use app\admin\model\AuthRule;
use addons\groupon\library\Hook;
use think\Exception;
use think\exception\PDOException;
use think\Loader;
/**
* Groupon插件
*/
class Groupon extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
$menu = self::getMenu();
Menu::create($menu['new']);
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
Menu::delete('groupon');
return true;
}
/**
* 插件启用方法
*/
public function enable()
{
Menu::enable('groupon');
return true;
}
/**
* 插件更新方法
*/
public function upgrade()
{
$menu = self::getMenu();
if (method_exists(Menu::class, 'upgrade')) {
Menu::upgrade('groupon', $menu['new']);
} else {
//使用Groupon自带的更新操作
self::menuCreateOrUpdate($menu['new'], $menu['old']);
}
return true;
}
/**
* 插件禁用方法
*/
public function disable()
{
Menu::disable('groupon');
return true;
}
/**
* 应用初始化
*/
public function appInit()
{
// 公共方法
require_once __DIR__ . '/helper.php';
// 补丁
if (!class_exists('\GuzzleHttp\UriTemplate')) {
require_once __DIR__ . '/patch/storage.php';
Loader::addNamespace('GuzzleHttp', $this->addons_path . str_replace('/', DS, 'patch/storage'));
}
// 全局注册行为事件
Hook::register();
}
private static function getMenu()
{
$newMenu = [];
$config_file = ADDON_PATH . "groupon" . DS . 'config' . DS . "menu.php";
if (is_file($config_file)) {
$newMenu = include $config_file;
}
$oldMenu = AuthRule::where('name', 'like', "groupon%")->select();
$oldMenu = array_column($oldMenu, null, 'name');
return ['new' => $newMenu, 'old' => $oldMenu];
}
private static function menuCreateOrUpdate($newMenu, $oldMenu, $parent = 0)
{
if (!is_numeric($parent)) {
$parentRule = AuthRule::getByName($parent);
$pid = $parentRule ? $parentRule['id'] : 0;
} else {
$pid = $parent;
}
$allow = array_flip(['file', 'name', 'title', 'icon', 'condition', 'remark', 'ismenu', 'weigh']);
foreach ($newMenu as $k => $v) {
$hasChild = isset($v['sublist']) && $v['sublist'] ? true : false;
$data = array_intersect_key($v, $allow);
$data['ismenu'] = isset($data['ismenu']) ? $data['ismenu'] : ($hasChild ? 1 : 0);
$data['icon'] = isset($data['icon']) ? $data['icon'] : ($hasChild ? 'fa fa-list' : 'fa fa-circle-o');
$data['pid'] = $pid;
$data['status'] = 'normal';
try {
if (!isset($oldMenu[$data['name']])) {
$menu = AuthRule::create($data);
} else {
$menu = $oldMenu[$data['name']];
}
if ($hasChild) {
self::menuCreateOrUpdate($v['sublist'], $oldMenu, $menu['id']);
}
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
}
}
}
... ...
<?php
return array (
'table_name' => '',
'self_path' => '
application/admin/lang/zh-cn/groupon',
'update_data' => '',
);
\ No newline at end of file
... ...
此 diff 太大无法显示。
<?php
namespace addons\groupon\controller;
use addons\groupon\exception\Exception;
use addons\groupon\model\UserAddress;
use addons\groupon\model\Area;
class Address extends Base
{
protected $noNeedLogin = ['area'];
protected $noNeedRight = ['*'];
public function index()
{
$this->success('收货地址', UserAddress::getUserAddress());
}
public function defaults()
{
$this->success('默认收货地址', UserAddress::getUserDefaultAddress());
}
public function area()
{
$data['provinceData'] = Area::where('level', 1)->order('id asc')->field('id as value, name as label, pid, level')->select();
foreach ($data['provinceData'] as $k => $p) {
$data['cityData'][$k] = Area::where(['level' => 2, 'pid' => $p->value])->order('id asc')->field('id as value, name as label, pid, level')->select();
foreach ($data['cityData'][$k] as $i => $c) {
$data['areaData'][$k][$i] = Area::where(['level' => 3, 'pid' => $c->value])->order('id asc')->field('id as value, name as label, pid, level')->select();
}
}
$this->success('省市区', $data);
}
public function edit()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'edit');
$this->success('编辑地址', UserAddress::edit($params));
}
public function info()
{
$params = $this->request->get();
$this->success('地址详情', UserAddress::info($params));
}
public function del()
{
$params = $this->request->post();
$this->success('地址详情', UserAddress::del($params));
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\exception\Exception;
use app\common\controller\Api;
use think\Lang;
class Base extends Api
{
public function _initialize()
{
parent::_initialize();
$controllername = strtolower($this->request->controller());
$this->loadlang($controllername);
}
protected function loadlang($name)
{
Lang::load(ADDON_PATH . 'groupon/lang/' . $this->request->langset() . '/' . str_replace('.', '/', $name) . '.php');
}
protected function grouponValidate($params, $class, $scene, $rules = []) {
$validate = validate(str_replace('controller', 'validate', $class));
if (!$validate->check($params, $rules, $scene)) {
$this->error($validate->getError());
}
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\model\Cart as CartModel;
class Cart extends Base
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$data = CartModel::info();
$this->success('我的购物车', $data);
}
public function add()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'add');
$goodsList = $params['goods_list'];
$this->success('已添加', CartModel::add($goodsList));
}
public function edit()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'edit');
$this->success('', CartModel::edit($params));
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\model\Category as CategoryModel;
use addons\groupon\model\Goods;
/**
* 分类管理
*
* @icon fa fa-list
* @remark 用于统一管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加
*/
class Category extends Base
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
public function detail () {
$id = $this->request->get('id');
$data = CategoryModel::getCategoryDetail($id);
$this->success('商城分类', $data);
}
public function index()
{
$id = $this->request->get('id');
$data = CategoryModel::getCategoryList($id);
$this->success('商城分类', $data);
}
// 获取指定 ids 分类列表
public function list()
{
$ids = $this->request->get('ids');
$data = CategoryModel::getCategoryListByIds($ids);
$this->success('商城分类', $data);
}
public function goods() {
$params = $this->request->get();
$category_id = $params['category_id'];
$categories = CategoryModel::where('pid', $category_id)->where('status', 'normal')->select();
// 获取这个分类下面的所有商品
$goodsList = Goods::getGoodsList($params, false);
foreach($categories as $key => $category) {
$categoryIds = ',' . $category['id'] . ',';
$currentCategoryGoods = [];
foreach ($goodsList as $k => $goods) {
$goodsCategoryIds = ',' . $goods['category_ids'] . ',';
if (strpos($goodsCategoryIds, $categoryIds) !== false) {
$currentCategoryGoods[] = $goods;
}
}
$categories[$key]['goods'] = $currentCategoryGoods;
}
$this->success('商城分类商品', $categories);
}
}
... ...
<?php
namespace addons\groupon\controller;
class Coupons extends Base
{
protected $noNeedLogin = ['lists', 'detail', 'goods'];
protected $noNeedRight = ['*'];
// 领券中心,自己的优惠券
public function index()
{
$type = $this->request->get('type');
$this->success('优惠券中心', \addons\groupon\model\Coupons::getCouponsList($type));
}
public function lists()
{
$ids = $this->request->get('ids');
$this->success('优惠券列表', \addons\groupon\model\Coupons::getCouponsListByIds($ids));
}
public function get()
{
$id = $this->request->get('id');
$this->success('领取成功', \addons\groupon\model\Coupons::getCoupon($id));
}
public function detail()
{
$id = $this->request->get('id');
$user_coupons_id = $this->request->get('user_coupons_id', 0);
$detail = \addons\groupon\model\Coupons::getCouponsDetail($id, $user_coupons_id);
$this->success('优惠券详情', $detail);
}
public function goods()
{
$id = $this->request->get('id');
$detail = \addons\groupon\model\Coupons::getGoodsByCoupons($id);
$this->success('优惠券详情', $detail);
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\exception\Exception;
use addons\groupon\model\Order;
use addons\groupon\model\User;
use think\Db;
use think\Log;
class Express extends Base
{
protected $noNeedLogin = ['callback'];
protected $noNeedRight = ['*'];
/**
* 物流信息订阅回调接口
*/
public function callback()
{
$requestData = $this->request->post();
$expressLib = new \addons\groupon\library\Express();
// 信息记录日志
// \think\Log::write('expresscallback:'. json_encode($requestData));
$data = json_decode(html_entity_decode($requestData['RequestData']), true);
$expressData = $data['Data'];
foreach ($expressData as $key => $express) {
$order_code = $express['OrderCode'] ?? '';
$orderExpress = null;
if (substr($order_code, 0, 1) == 'S') {
// 门店包裹
$orderExpress = \addons\groupon\model\OrderStoreExpress::where('code', $order_code)->find();
}
if (!$orderExpress) {
// 包裹不存在,记录日志信息,然后继续下一个
\think\Log::write('orderExpressNotFound:' . json_encode($express));
continue;
}
if (!$express['Success']) {
// 失败了
if (isset($express['Reason']) && ($express['Reason'] == '三天无轨迹' || $express['Reason'] == '七天无轨迹')) {
// 需要重新订阅
$expressLib->subscribe([
'express_code' => $express['ShipperCode'],
'express_no' => $express['LogisticCode'],
'order_code' => $orderExpress->code ? $orderExpress->code : ''
], $orderExpress, $orderExpress->order ?? null);
}
\think\Log::write('orderExpressReason:' . json_encode($express));
continue;
}
$expressLib->checkAndAddTraces($orderExpress, $express);
}
$result = $expressLib->setPushResult(true);
return json($result);
}
}
... ...
<?php
namespace addons\groupon\controller;
class Faq extends Base
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
// faq 列表
public function index()
{
$this->success('获取成功', \addons\groupon\model\Faq::order('id', 'DESC')->paginate(10));
}
public function detail () {
$id = $this->request->get('id');
$this->success('签到成功', \addons\groupon\model\Faq::where('id', $id)->find());
}
}
... ...
<?php
namespace addons\groupon\controller;
class Feedback extends Base
{
protected $noNeedLogin = ['type'];
protected $noNeedRight = ['*'];
public function type()
{
$this->success('反馈类型', array_values(\addons\groupon\model\Feedback::$typeAll));
}
public function add() {
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'add');
$this->success('反馈成功', \addons\groupon\model\Feedback::add($params));
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\exception\Exception;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Border;
class Goods extends Base
{
protected $noNeedLogin = ['index', 'detail', 'lists', 'activity', 'seckillList', 'grouponList', 'store'];
protected $noNeedRight = ['*'];
public function index()
{
}
public function detail()
{
$id = $this->request->get('id');
$detail = \addons\groupon\model\Goods::getGoodsDetail($id);
// 记录足记
\addons\groupon\model\UserView::addView($detail);
$sku_price = $detail['sku_price']; // 处理过的规格
// tp bug json_encode 或者 toArray 的时候 sku_price 会重新查询数据库,导致被处理过的规格又还原回去了
$detail = json_decode(json_encode($detail), true);
$detail['sku_price'] = $sku_price;
$this->success('商品详情', $detail);
}
public function lists()
{
$params = $this->request->get();
$data = \addons\groupon\model\Goods::getGoodsList($params);
$this->success('商品列表', $data);
}
public function favorite()
{
$params = $this->request->post();
$result = \addons\groupon\model\UserFavorite::edit($params);
$this->success($result ? '收藏成功' : '取消收藏', $result);
}
public function favoriteList()
{
$data = \addons\groupon\model\UserFavorite::getGoodsList();
$this->success('商品收藏列表', $data);
}
public function viewDelete()
{
$params = $this->request->post();
$result = \addons\groupon\model\UserView::del($params);
$this->success('删除成功', $result);
}
public function viewList()
{
$data = \addons\groupon\model\UserView::getGoodsList();
$this->success('商品浏览列表', $data);
}
}
... ...
<?php
namespace addons\groupon\controller;
class GoodsComment extends Base
{
protected $noNeedLogin = ['index', 'type'];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->get();
$goodsComments = \addons\groupon\model\GoodsComment::getList($params);
$this->success('评价详情', $goodsComments);
}
public function type() {
$goods_id = $this->request->get('goods_id', 0);
$type = array_values(\addons\groupon\model\GoodsComment::$typeAll);
foreach ($type as $key => $val) {
// 只查询 count 比查出来所有评论,在判断状态要快
$comment = \addons\groupon\model\GoodsComment::where('goods_id', $goods_id);
if ($val['code'] != 'all') {
$comment = $comment->{$val['code']}();
}
$comment = $comment->count();
$type[$key]['num'] = $comment;
}
$this->success('筛选类型', $type);
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\model\Config;
use think\Db;
use think\Config as FaConfig;
use fast\Random;
use think\exception\HttpResponseException;
class Index extends Base
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
public function index()
{
}
//初始化商城数据 服务器压力大可以把最后的data数据存入cache缓存中来调用,防止多次查sql
public function init()
{
$params = $this->request->get();
$platform = $this->request->header('platform');
$config = new \addons\groupon\model\Config;
$platformConfig = json_decode($config->where(['name' => $platform])->value('value'), true);
$paymentConfig = $config->where('group', 'payment')->select();
$payment = [];
foreach ($paymentConfig as $key => $config) {
$val = json_decode($config->value, true);
if ($val && in_array($platform, $val['platform'])) {
$payment[] = $config->name;
}
}
// 处理微信支付 v3
$pay_version = pay_version();
if ($pay_version == 'v3' && in_array('wechatv3', $payment) && !in_array('wechat', $payment)) {
$payment[] = 'wechat';
};
$data['payment'] = $payment; // 平台支持的支付方式
$groupon = json_decode(Config::where(['name' => 'groupon'])->value('value'), true);
$data['info'] = $groupon;
$data['info']['logo'] = cdnurl($data['info']['logo'], true);
$autologin = false;
if (isset($platformConfig['auto_login']) && $platformConfig['auto_login'] == 1) {
$autologin = true;
}
$data['wechat'] = [
'appid' => isset($platformConfig['app_id']) ? $platformConfig['app_id'] : '',
'autologin' => $autologin,
];
$share = json_decode(Config::where(['name' => 'share'])->value('value'), true);
$data['share'] = [
'title' => $share['title'],
'image' => isset($share['image']) ? cdnurl($share['image'], true) : '',
'goods_poster_bg' => isset($share['goods_poster_bg']) ? cdnurl($share['goods_poster_bg'], true) : '',
'user_poster_bg' => isset($share['user_poster_bg']) ? cdnurl($share['user_poster_bg'], true) : '',
'groupon_poster_bg' => isset($share['groupon_poster_bg']) ? cdnurl($share['groupon_poster_bg'], true) : '',
'store_poster_bg' => isset($share['store_poster_bg']) ? cdnurl($share['store_poster_bg'], true) : '',
];
$data['addons'] = array_keys(get_addon_list());
$data['chat'] = json_decode(Config::where(['name' => 'chat'])->value('value'), true);
$this->success('初始化数据', $data);
}
//商城模板数据
public function template()
{
$get = $this->request->get();
$platform = $this->request->header('platform');
if (isset($get['shop_id'])) {
$template = \addons\groupon\model\Decorate::getCurrentPlatformDecorate('preview', $get['shop_id']);
} else {
$template = \addons\groupon\model\Decorate::getCurrentPlatformDecorate($platform);
}
$this->success('模板数据', $template);
}
//自定义页面
public function custom()
{
$get = $this->request->get();
$decorate = \addons\groupon\model\Decorate::get($get['id']);
if (!$decorate) {
$this->error('未找到自定义页面');
}
$decorate->template = \addons\groupon\model\Decorate::getCustomDecorate($get['id']);
$this->success('自定义模板数据', $decorate);
}
//富文本详情
public function richtext()
{
$id = $this->request->get('id');
$data = \addons\groupon\model\Richtext::get(['id' => $id]);
$this->success($data->title, $data);
}
//同步前端所有页面链接
public function asyncLink()
{
$post = $this->request->post();
$newLink = $post['data'];
$existLink = Db::name('groupon_link')->select();
$newLinkPath = array_column($newLink, 'path');
$existLinkPath = array_flip(array_column($existLink, 'path'));
$insertData = [];
$count = 1;
foreach ($newLinkPath as $key => $item) {
if (!isset($existLinkPath[$item]) && isset($newLink[$key]['meta']['async']) && $newLink[$key]['meta']['async']) {
$insertData[] = [
'name' => isset($newLink[$key]['meta']['title']) ? $newLink[$key]['meta']['title'] : '新链接' . $count,
'path' => $item,
'group' => isset($newLink[$key]['meta']['group']) ? $newLink[$key]['meta']['group'] : '其它',
'createtime' => time(),
'updatetime' => time()
];
$count++;
}
}
if ($insertData !== []) {
Db::name('groupon_link')->insertAll($insertData);
}
}
//店铺装修 截图商城首页
public function asyncDecorateScreenShot()
{
$params = $this->request->post();
\addons\groupon\model\Decorate::asyncDecorateScreenShot($params);
}
/**
* 上传文件
* @ApiMethod (POST)
* @param File $file 文件流
*/
public function upload()
{
$file = $this->request->file('file');
if (empty($file)) {
$this->error(__('No file upload or server upload limit exceeded'));
}
//判断是否已经存在附件
$sha1 = $file->hash();
$upload = FaConfig::get('upload');
preg_match('/(\d+)(\w+)/', $upload['maxsize'], $matches);
$type = strtolower($matches[2]);
$typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
$size = (int)$upload['maxsize'] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0);
$fileInfo = $file->getInfo();
$suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION));
$suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
$mimetypeArr = explode(',', strtolower($upload['mimetype']));
$typeArr = explode('/', $fileInfo['type']);
//禁止上传PHP和HTML文件
if (in_array($fileInfo['type'], ['text/x-php', 'text/html']) || in_array($suffix, ['php', 'html', 'htm'])) {
$this->error(__('Uploaded file format is limited'));
}
//验证文件后缀
if ($upload['mimetype'] !== '*' &&
(
!in_array($suffix, $mimetypeArr)
|| (stripos($typeArr[0] . '/', $upload['mimetype']) !== false && (!in_array($fileInfo['type'], $mimetypeArr) && !in_array($typeArr[0] . '/*', $mimetypeArr)))
)
) {
$this->error(__('Uploaded file format is limited'));
}
//验证是否为图片文件
$imagewidth = $imageheight = 0;
if (in_array($fileInfo['type'], ['image/gif', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp']) || in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'])) {
$imgInfo = getimagesize($fileInfo['tmp_name']);
if (!$imgInfo || !isset($imgInfo[0]) || !isset($imgInfo[1])) {
$this->error(__('Uploaded file is not a valid image'));
}
$imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : $imagewidth;
$imageheight = isset($imgInfo[1]) ? $imgInfo[1] : $imageheight;
}
// 文件 md5
$fileMd5 = md5_file($fileInfo['tmp_name']);
$replaceArr = [
'{year}' => date("Y"),
'{mon}' => date("m"),
'{day}' => date("d"),
'{hour}' => date("H"),
'{min}' => date("i"),
'{sec}' => date("s"),
'{random}' => Random::alnum(16),
'{random32}' => Random::alnum(32),
'{filename}' => $suffix ? substr($fileInfo['name'], 0, strripos($fileInfo['name'], '.')) : $fileInfo['name'],
'{suffix}' => $suffix,
'{.suffix}' => $suffix ? '.' . $suffix : '',
'{filemd5}' => $fileMd5,
];
$savekey = $upload['savekey'];
$savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey);
$uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1);
$fileName = substr($savekey, strripos($savekey, '/') + 1);
//
if (in_array($upload['storage'], ['cos', 'alioss', 'qiniu'])) { // upyun:又拍云 ,bos:百度BOS,ucloud: Ucloud, 如果要使用这三种,请自行安装插件配置,并将标示填入前面数组,进行测试
$token_name = $upload['storage'] . 'token'; // costoken, aliosstoken, qiniutoken
$controller_name = '\\addons\\' . $upload['storage'] . '\\controller\\Index';
$storageToken[$token_name] = $upload['multipart'] && $upload['multipart'][$token_name] ? $upload['multipart'][$token_name] : '';
$domain = request()->domain();
try {
$uploadCreate = \think\Request::create('foo', 'POST', array_merge([
'key' => $savekey,
'name' => $fileInfo['name'],
'md5' => $fileMd5,
'chunk' => 0,
], $storageToken));
// 重新设置跨域允许域名
$cors = config('fastadmin.cors_request_domain');
config('fastadmin.cors_request_domain', $cors . ',' . $domain);
$uploadController = new $controller_name($uploadCreate);
$uploadController->upload();
} catch (HttpResponseException $e) {
$result = $e->getResponse()->getData();
if (isset($result['code']) && $result['code'] == 0) {
$this->error($result['msg']);
}
$resultData = $result['data'];
}
} else {
$splInfo = $file->validate(['size' => $size])->move(ROOT_PATH . '/public' . $uploadDir, $fileName);
if ($splInfo) {
$resultData = [
'url' => $uploadDir . $splInfo->getSaveName(),
'fullurl' => request()->domain() . $uploadDir . $splInfo->getSaveName()
];
} else {
// 上传失败获取错误信息
$this->error($file->getError());
}
}
$params = array(
'admin_id' => 0,
'user_id' => (int)$this->auth->id,
'filename' => substr(htmlspecialchars(strip_tags($fileInfo['name'])), 0, 100),
'filesize' => $fileInfo['size'],
'imagewidth' => $imagewidth,
'imageheight' => $imageheight,
'imagetype' => $suffix,
'imageframes' => 0,
'mimetype' => $fileInfo['type'] == 'application/octet-stream' && in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp']) ? 'image/' . $suffix : $fileInfo['type'],
'url' => $resultData['url'],
'uploadtime' => time(),
'storage' => $upload['storage'],
'sha1' => $sha1,
);
$attachment = new \app\common\model\Attachment;
$attachment->data(array_filter($params));
$attachment->save();
\think\Hook::listen("upload_after", $attachment);
$this->success(__('Upload successful'), $resultData);
}
/**
* 上传 base64 图片
* @ApiMethod (POST)
* @param base64 内容
*/
public function uploadBase64()
{
$content = $this->request->post('content', '');
if (empty($content) || !preg_match('/^(data:\s*image\/(\w+);base64,)/', $content, $matches)) {
$this->error(__('No file upload or server upload limit exceeded'));
}
$upload = FaConfig::get('upload');
$suffix = $matches[2]; //图片后缀
$mimetypeArr = explode(',', strtolower($upload['mimetype']));
// 判断是否允许的后缀
if ($upload['mimetype'] !== '*' && !in_array($suffix, $mimetypeArr)) {
$this->error(__('Uploaded file format is limited'));
}
$replaceArr = [
'{year}' => date("Y"),
'{mon}' => date("m"),
'{day}' => date("d"),
'{hour}' => date("H"),
'{min}' => date("i"),
'{sec}' => date("s"),
'{random}' => Random::alnum(16),
'{random32}' => Random::alnum(32),
'{filename}' => "店铺装修截图",
'{suffix}' => $suffix,
'{.suffix}' => $suffix ? '.' . $suffix : '',
'{filemd5}' => md5($content),
];
$savekey = $upload['savekey'];
$savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey);
$uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1);
$fileName = substr($savekey, strripos($savekey, '/') + 1);
$fullDir = ROOT_PATH . '/public' . $uploadDir;
$uploadPath = $uploadDir . $fileName;
$fullPath = $fullDir . $fileName;
if (!is_dir($fullDir)) {
mkdir($fullDir, 0777, true);
}
$imageContent = base64_decode(str_replace($matches[1], '', $content));
if (file_put_contents($fullPath, $imageContent)) {
$imgInfo = getimagesize($fullPath);
$imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : 0;
$imageheight = isset($imgInfo[1]) ? $imgInfo[1] : 0;
$params = array(
'admin_id' => 0,
'user_id' => (int) $this->auth->id,
'filesize' => strlen($content),
'imagewidth' => $imagewidth,
'imageheight' => $imageheight,
'imagetype' => $suffix,
'imageframes' => 0,
'mimetype' => isset($imgInfo['mime']) ? $imgInfo['mime'] : 'image/' . $suffix,
'url' => $uploadPath,
'uploadtime' => time(),
'storage' => 'local',
'sha1' => hash_file('sha1', $fullPath),
);
$attachment = new \app\common\model\Attachment;
$attachment->data(array_filter($params));
$attachment->save();
\think\Hook::listen("upload_after", $attachment);
$this->success(__('Upload successful'), [
'url' => $uploadPath,
'full_url' => request()->domain() . $uploadPath
]);
} else {
$this->error("文件上传失败");
}
}
public function debugLog()
{
$params = $this->request->post();
\think\Log::write($params, 'notice');
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\library\Wechat;
use addons\groupon\exception\Exception;
use think\Cache;
class Live extends Base
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->get();
$type = $params['type'] ?? 'all';
$ids = array_filter(isset($params['ids']) ? explode(',', $params['ids']) : []);
if (!in_array($type, ['all', 'notice', 'living', 'lived'])) {
$this->error('参数错误');
}
// 同步直播
\addons\groupon\model\Live::autoSyncLive();
if ($type != 'all') {
$lives = \addons\groupon\model\Live::{$type}()
->with('goods')
->order('id', 'desc')
->paginate(10);
} else {
$lives = \addons\groupon\model\Live::order('live_status', 'asc')
->with('goods')
->order('id', 'desc');
if (isset($params['ids'])) {
// 首页根据 id 获取
$lives = $lives->where('id', 'in', $ids)->select();
} else {
// 直播列表
$lives = $lives->paginate(10);
}
}
$this->success('获取成功', $lives);
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\exception\Exception;
use addons\groupon\model\NotificationConfig;
use think\Cache;
class Notification extends Base
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
public function template()
{
// 获取小程序模板消息所有配置
$notificationConfig = NotificationConfig::cache(300)->where('platform', 'wxMiniProgram')->select();
$templates = [];
foreach ($notificationConfig as $k => $config) {
if ($config['status'] && $config['content_arr'] && $config['content_arr']['template_id']) {
$templates[$config['event']] = $config['content_arr']['template_id'];
}
}
$this->success('获取成功', $templates);
}
}
... ...
<?php
namespace addons\groupon\controller;
class Order extends Base
{
protected $noNeedLogin = ['remind'];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->get();
$this->success('订单列表', \addons\groupon\model\Order::getList($params));
}
public function detail()
{
$params = $this->request->get();
$this->success('订单详情', \addons\groupon\model\Order::detail($params));
}
public function itemDetail()
{
$params = $this->request->get();
$this->success('订单商品', \addons\groupon\model\Order::itemDetail($params));
}
//
public function statusNum () {
$this->success('订单数量', \addons\groupon\model\Order::statusNum());
}
// 取消订单
public function cancel()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'cancel');
$this->success('取消成功', \addons\groupon\model\Order::operCancel($params));
}
// 删除订单
public function delete()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'delete');
$this->success('删除成功', \addons\groupon\model\Order::operDelete($params));
}
// 确认收货订单
public function confirm()
{
repeat_filter(); // 防抖
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'confirm');
$this->success('收货成功', \addons\groupon\model\Order::operConfirm($params));
}
// 确认收货订单商品
public function confirmItem()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'confirm_item');
$this->success('收货成功', \addons\groupon\model\Order::operConfirmItem($params));
}
public function comment ()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'comment');
$this->success('评价成功', \addons\groupon\model\Order::operComment($params));
}
public function pre()
{
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'pre');
$result = \addons\groupon\model\Order::pre($params);
$this->success('计算成功', $result);
}
public function createOrder()
{
repeat_filter(); // 防抖
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'createOrder');
$order = \addons\groupon\model\Order::createOrder($params);
$this->success('订单添加成功', $order);
}
// 获取可用优惠券列表
public function coupons () {
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'coupons');
$coupons = \addons\groupon\model\Order::coupons($params);
$this->success('获取成功', $coupons);
}
public function remind() {
// 查询最近三天的 20 个订单,的用户头像昵称
$reminds = \addons\groupon\model\Order::getRemind();
$this->success('获取成功', $reminds);
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\epay\library\Service;
use fast\Random;
use think\addons\Controller;
use addons\groupon\exception\Exception;
use addons\groupon\model\Order;
use addons\groupon\model\User;
use think\Db;
use think\Log;
use Yansongda\Pay\Pay as YansongdaPay;
class Pay extends Base
{
protected $noNeedLogin = ['prepay', 'notifyx', 'notifyr', 'alipay'];
protected $noNeedRight = ['*'];
/**
* 支付宝网页支付
*/
public function alipay()
{
$pay_version = pay_version(); // 获取微信支付版本
$order_sn = $this->request->get('order_sn');
$platform = $this->request->get('platform');
$order = Order::where('order_sn', $order_sn)->find();
try {
if (!$order) {
throw new \Exception("订单不存在");
}
if ($order->status > 0) {
throw new \Exception("订单已支付");
}
if ($order->status < 0) {
throw new \Exception("订单已失效");
}
$order_data = [
'order_id' => $order->id,
'out_trade_no' => $order->order_sn,
'total_fee' => $order->total_fee,
'subject' => '商城订单支付',
];
if ($pay_version == 'v3') {
$payService = new \addons\groupon\library\payv3\PayServiceV3('alipay', $platform);
$result = $payService->pay($order_data, [], 'url');
$result = $result->getBody()->getContents();
} else {
$notify_url = $this->request->root(true) . '/addons/groupon/pay/notifyx/payment/alipay/platform/H5';
$pay = new \addons\groupon\library\PayService('alipay', 'url', $notify_url);
$result = $pay->create($order_data);
$result = $result->getContent();
}
$result = $pay->create($order_data);
$result = $result->getContent();
echo $result;
} catch (\Exception $e) {
echo $e->getMessage();
}
// $this->assign('result', $result);
// return $this->view->fetch();
}
/**
* 拉起支付
*/
public function prepay()
{
$user = User::info();
$pay_version = pay_version(); // 获取微信支付版本
$order_sn = $this->request->post('order_sn');
$payment = $this->request->post('payment');
$openid = $this->request->post('openid', '');
$platform = request()->header('platform');
$order = Order::nopay()->where('user_id', $user->id)->where('order_sn', $order_sn)->find();
if (!$order) {
$this->error("订单不存在");
}
if (in_array($order->status, [$order::STATUS_INVALID, $order::STATUS_CANCEL])) {
$this->error("订单已失效");
}
if (!$payment || !in_array($payment, ['wechat', 'alipay', 'wallet'])) {
$this->error("支付类型不能为空");
}
if ($payment == 'wallet') {
// 余额支付
$this->walletPay($order, $payment, $platform);
}
$order_data = [
'out_trade_no' => $order->order_sn,
'total_fee' => $order->total_fee,
];
// 微信公众号,小程序支付,必须有 openid
if ($payment == 'wechat') {
if (in_array($platform, ['wxOfficialAccount', 'wxMiniProgram'])) {
if (isset($openid) && $openid) {
// 如果传的有 openid
$order_data['openid'] = $openid;
} else {
// 没有 openid 默认拿下单人的 openid
$oauth = \addons\groupon\model\UserOauth::where([
'user_id' => $order->user_id,
'provider' => 'Wechat',
'platform' => $platform
])->find();
$order_data['openid'] = $oauth ? $oauth->openid : '';
}
if (empty($order_data['openid'])) {
// 缺少 openid
return $this->success('缺少 openid', 'no_openid');
}
}
$order_data['body'] = '商城订单支付';
} else {
$order_data['subject'] = '商城订单支付';
}
try {
if ($pay_version == 'v3') {
$payService = new \addons\groupon\library\payv3\PayServiceV3($payment); // 回调地址再 payServiceV3 中拼接
$result = $payService->pay($order_data);
} else {
$notify_url = $this->request->root(true) . '/addons/groupon/pay/notifyx/payment/' . $payment . '/platform/' . $platform;
$pay = new \addons\groupon\library\PayService($payment, $platform, $notify_url);
$result = $pay->create($order_data);
}
} catch (\Exception $e) {
$this->error("支付配置错误:" . $e->getMessage());
}
if ($platform == 'App') {
if ($pay_version == 'v3') {
if ($payment == 'wechat') {
// Yansongda\Supports\Collection,可当数组,可当字符串, 因为前端会 JSON.parse 所以这里提前转字符串
$result = json_encode($result);
} else {
$result = $result->getBody()->getContents();
}
} else {
$result = $result->getContent();
}
}
if ($platform == 'H5' && $payment == 'wechat') {
if ($pay_version == 'v3') {
$result = [
'h5_url' => $result->h5_url
];
} else {
$result = $result->getContent();
}
}
return $this->success('获取预付款成功', [
'pay_data' => $result,
]);
}
// 余额支付
public function walletPay($order, $type, $method)
{
$order = Db::transaction(function () use ($order, $type, $method) {
// 重新加锁读,防止连点问题
$order = Order::nopay()->where('order_sn', $order->order_sn)->lock(true)->find();
if (!$order) {
$this->error("订单已支付");
}
$total_fee = $order->total_fee;
// 扣除余额
$user = User::info();
if (is_null($user)) {
// 没有登录,请登录
$this->error(__('Please login first'), null, 401);
}
User::money(-$total_fee, $user->id, 'wallet_pay', $order->id, '', [
'order_id' => $order->id,
'order_sn' => $order->order_sn,
]);
// 支付后流程
$notify = [
'order_sn' => $order['order_sn'],
'transaction_id' => '',
'notify_time' => date('Y-m-d H:i:s'),
'buyer_email' => $user->id,
'pay_fee' => $order->total_fee,
'pay_type' => 'wallet' // 支付方式
];
$notify['payment_json'] = json_encode($notify);
$order->paymentProcess($order, $notify);
return $order;
});
$this->success('支付成功', $order);
}
/**
* 支付成功回调
*/
public function notifyx()
{
Log::write('notifyx-comein:');
$pay_version = pay_version(); // 获取微信支付版本
$payment = $this->request->param('payment');
$platform = $this->request->param('platform');
if ($pay_version == 'v3') {
$pay = new \addons\groupon\library\payv3\PayServiceV3($payment, $platform);
} else {
$pay = new \addons\groupon\library\PayService($payment, $platform);
}
$result = $pay->notify(function ($data, $pay = null) use ($payment, $pay_version) {
Log::write('notifyx-result:' . json_encode($data));
// { // 微信回调参数
// "appid":"wx39cd0799d4567dd0",
// "bank_type":"OTHERS",
// "cash_fee":"1",
// "fee_type":"CNY",
// "is_subscribe":"N",
// "mch_id":"1481069012",
// "nonce_str":"dPpcZ6AzCDU8daNC",
// "openid":"oD9ko4x7QMDQPZEuN8V5jtZjie3g",
// "out_trade_no":"202010240834057843002700",
// "result_code":"SUCCESS",
// "return_code":"SUCCESS",
// "sign":"3103B6D06F13D2B3959C5B3F7F1FD61C",
// "time_end":"20200407102424",
// "total_fee":"1",
// "trade_type":"JSAPI",
// "transaction_id":"4200000479202004070804485027"
// }
// { // 支付宝支付成功回调参数
// "gmt_create":"2020-04-10 19:15:00",
// "charset":"utf-8",
// "seller_email":"xptech@qq.com",
// "subject":"\u5546\u57ce\u8ba2\u5355\u652f\u4ed8",
// "sign":"AOiYZ1a2mEMOuIbHFCi6V6A0LJ97UMiHsCWgNdSU9dlzKFl15Ts8b0mL\/tN+Hhskl+94S3OUiNTBD3dD0Kv923SqaTWxNdj533PCdo2GDKsZIZgKbavnOvaccSKUdmQRE9KtmePPq9V9lFzEQvdUkKq1M8KAWO5K9LTy2iT2y5CUynpiu\/04GVzsTL9PqY+LDwqj6K+w7MgceWm1BWaFWg27AXIRw7wvsFckr3k9GGajgE2fufhoCYGYtGFbhGOp6ExtqS5RXBuPODOyRhBLpD8mwpOX38Oy0X+R4YQIrOi02dhqwPpvw79YjnvgXY3qZEQ66EdUsrT7EBdcPHK0Gw==",
// "buyer_id":"2088902485164146",
// "invoice_amount":"0.01",
// "notify_id":"2020041000222191501064141414240102",
// "fund_bill_list":"[{\"amount\":\"0.01\",\"fundChannel\":\"PCREDIT\"}]",
// "notify_type":"trade_status_sync",
// "trade_status":"TRADE_SUCCESS",
// "receipt_amount":"0.01",
// "buyer_pay_amount":"0.01",
// "app_id":"2021001114666742",
// "sign_type":"RSA2",
// "seller_id":"2088721922277739",
// "gmt_payment":"2020-04-10 19:15:00",
// "notify_time":"2020-04-10 19:15:01",
// "version":"1.0",
// "out_trade_no":"202007144778322770017000",
// "total_amount":"0.01",
// "trade_no":"2020041022001464141443020240",
// "auth_app_id":"2021001114666742",
// "buyer_logon_id":"157***@163.com",
// "point_amount":"0.00"
// }
// { // 支付宝退款成功(交易关闭)回调参数
// "gmt_create": "2020-08-15 14:48:32",
// "charset": "utf-8",
// "seller_email": "xptech@qq.com",
// "gmt_payment": "2020-08-15 14:48:32",
// "notify_time": "2020-08-15 16:11:45",
// "subject": "商城订单支付",
// "gmt_refund": "2020-08-15 16:11:45.140",
// "sign": "b6ArkgzLIRteRL9FMGC6i\/jf6VwFYQbaGDGRx002W+pdmN5q9+O4edZ3ALF74fYaijSl9ksNr0dKdvanu3uYxBTcd\/GIS4N1CWzmCOv6pzgx5rO\/YvGoHLM3Yop0GKKuMxmnNsZ6jhYKEY7SYD3Y0L6PU9ZMdHV7yIiVj+zZmbKzUgK9MPDCEXs+nzpNAiSM8GTqYRSUvKobAK68hswG2k1QIcqr5z+ZmVYa\/nHHkoC9qXt5zwyGi4P+2eOsr6V2PjA3x8qqe7TN5aI1DeoZD5KqHPYYaYF17J2q6YPlgl3WUl1RhE7H86bivB1fIuYEv\/3+JR74WN\/o7krGw1RPHg==",
// "out_biz_no": "R202004114414846255015300",
// "buyer_id": "2088902485164146",
// "version": "1.0",
// "notify_id": "2020081500222161145064141453349793",
// "notify_type": "trade_status_sync",
// "out_trade_no": "202002460317545607015300",
// "total_amount": "0.01",
// "trade_status": "TRADE_CLOSED",
// "refund_fee": "0.01",
// "trade_no": "2020081522001464141438570535",
// "auth_app_id": "2021001114666742",
// "buyer_logon_id": "157***@163.com",
// "gmt_close": "2020-08-15 16:11:45",
// "app_id": "2021001114666742",
// "sign_type": "RSA2",
// "seller_id": "2088721922277739"
// }
try {
$out_trade_no = $data['out_trade_no'];
$out_refund_no = $data['out_biz_no'] ?? '';
// 判断是否是支付宝退款(支付宝退款成功会通知该接口)
if ($payment == 'alipay' // 支付宝支付
&& $data['notify_type'] == 'trade_status_sync' // 同步交易状态
&& $data['trade_status'] == 'TRADE_CLOSED' // 交易关闭
&& $out_refund_no // 退款单号
) {
// 退款回调
$this->refundFinish($out_trade_no, $out_refund_no);
return $this->payResponse($pay, $payment);
}
// 判断支付宝微信是否是支付成功状态,如果不是,直接返回响应
if ($payment == 'alipay' && $data['trade_status'] != 'TRADE_SUCCESS') {
// 不是交易成功的通知,直接返回成功
return $this->payResponse($pay, $payment);
}
if ($pay_version == 'v3') {
// 支付成功流程
$pay_fee = $data['pay_fee'];
} else {
if ($payment == 'wechat' && ($data['result_code'] != 'SUCCESS' || $data['return_code'] != 'SUCCESS')) {
// 微信交易未成功,返回 false,让微信再次通知
return false;
}
// 支付成功流程
$pay_fee = $payment == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
}
//你可以在此编写订单逻辑
$order = Order::where('order_sn', $out_trade_no)->find();
if (!$order || $order->status > 0) {
// 订单不存在,或者订单已支付
return $this->payResponse($pay, $payment);
}
Db::transaction(function () use ($order, $data, $payment, $pay_fee) {
$notify = [
'order_sn' => $data['out_trade_no'],
'transaction_id' => $payment == 'alipay' ? $data['trade_no'] : $data['transaction_id'],
'notify_time' => date('Y-m-d H:i:s', (strtotime($data['time_end'] ?? $data['notify_time']))),
'buyer_email' => $payment == 'alipay' ? $data['buyer_logon_id'] : $data['openid'],
'payment_json' => json_encode($data),
'pay_fee' => $pay_fee,
'pay_type' => $payment // 支付方式
];
$order->paymentProcess($order, $notify);
});
return $this->payResponse($pay, $payment);
} catch (\Exception $e) {
Log::write('notifyx-error:' . json_encode($e->getMessage()));
}
});
return $result;
}
/**
* 退款成功回调
*/
public function notifyr()
{
Log::write('notifyreturn-comein:');
$pay_version = pay_version(); // 获取微信支付版本
$payment = $this->request->param('payment');
$platform = $this->request->param('platform');
if ($pay_version == 'v3') {
$pay = new \addons\groupon\library\payv3\PayServiceV3($payment, $platform);
} else {
$pay = new \addons\groupon\library\PayService($payment, $platform);
}
$result = $pay->notifyRefund(function ($data, $pay = null) use ($payment, $platform) {
try {
$out_refund_no = $data['out_refund_no'];
$out_trade_no = $data['out_trade_no'];
// 退款
$this->refundFinish($out_trade_no, $out_refund_no);
return $this->payResponse($pay, $payment);
} catch (\Exception $e) {
Log::write('notifyreturn-error:' . json_encode($e->getMessage()));
return false;
}
});
return $result;
}
private function refundFinish($out_trade_no, $out_refund_no)
{
$order = Order::where('order_sn', $out_trade_no)->find();
$refundLog = \app\admin\model\groupon\order\RefundLog::where('refund_sn', $out_refund_no)->find();
if (!$order || !$refundLog || $refundLog->status != 0) {
// 订单不存在,或者订单已退款
return true;
}
$item = \app\admin\model\groupon\order\OrderItem::where('id', $refundLog->order_item_id)->find();
Db::transaction(function () use ($order, $item, $refundLog) {
\app\admin\model\groupon\order\Order::refundFinish($order, $item, $refundLog);
});
return true;
}
/**
* v2, v3 响应转换
*/
private function payResponse($pay = null, $payment = null)
{
$pay_version = pay_version(); // 获取微信支付版本
if ($pay_version == 'v3') {
$result = YansongdaPay::$payment()->success();
$content = $result->getBody()->getContents();
$content = $payment == 'wechat' ? json_decode($content, true) : $content;
return response($content, 200, [], ($payment == 'wechat' ? 'json' : ''));
} else {
return $pay->success()->send();
}
return $pay_version == 'v3' ? YansongdaPay::$payment()->success() : $pay->success()->send();
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\model\Share as ShareModel;
class Share extends Base
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
/**
* 获取分享记录
*
* @return void
*/
public function index()
{
$params = $this->request->get();
$shares = ShareModel::getList($params);
return $this->success('获取成功', $shares);
}
public function add()
{
$params = $this->request->post();
$params['platform'] = $this->request->header('platform');
$shareLog = ShareModel::add($params);
if ($shareLog) {
\think\Hook::listen('share_after', $shareLog);
$this->success('添加分享记录', $shareLog);
}
$this->success('添加分享记录'); // 使用 success 前端不提示
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\controller;
use app\common\library\Sms as Smslib;
use addons\groupon\model\User;
use think\Hook;
/**
* 手机短信接口
*/
class Sms extends Base
{
protected $noNeedLogin = '*';
protected $noNeedRight = '*';
/**
* 发送验证码
*
* @param string $mobile 手机号
* @param string $event 事件名称
*/
public function send()
{
$mobile = $this->request->post("mobile");
$event = $this->request->post("event");
$event = $event ? $event : 'register';
if (!$mobile || !\think\Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('手机号不正确'));
}
$last = Smslib::get($mobile, $event);
if ($last && time() - $last['createtime'] < 60) {
$this->error(__('发送频繁'));
}
$ipSendTotal = \app\common\model\Sms::where(['ip' => $this->request->ip()])->whereTime('createtime', '-1 hours')->count();
if ($ipSendTotal >= 5) {
$this->error(__('发送频繁'));
}
if ($event) {
$userinfo = User::getByMobile($mobile);
if ($event == 'register' && $userinfo) {
//已被注册
$this->error(__('已被注册'));
} elseif (in_array($event, ['changemobile']) && $userinfo) {
//被占用
$this->error(__('已被占用'));
} elseif (in_array($event, ['changepwd', 'resetpwd', 'mobilelogin']) && !$userinfo) {
//未注册
$this->error(__('未注册'));
}
}
if (!Hook::get('sms_send')) {
$this->error(__('请在后台插件管理安装短信验证插件'));
}
$ret = Smslib::send($mobile, null, $event);
if ($ret) {
$this->success(__('发送成功'));
} else {
$this->error(__('发送失败,请检查短信配置是否正确'));
}
}
/**
* 检测验证码
*
* @param string $mobile 手机号
* @param string $event 事件名称
* @param string $captcha 验证码
*/
public function check()
{
$mobile = $this->request->post("mobile");
$event = $this->request->post("event");
$event = $event ? $event : 'register';
$captcha = $this->request->post("captcha");
if (!$mobile || !\think\Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('手机号不正确'));
}
if ($event) {
$userinfo = User::getByMobile($mobile);
if ($event == 'register' && $userinfo) {
//已被注册
$this->error(__('已被注册'));
} elseif (in_array($event, ['changemobile']) && $userinfo) {
//被占用
$this->error(__('已被占用'));
} elseif (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo) {
//未注册
$this->error(__('未注册'));
}
}
$ret = Smslib::check($mobile, $captcha, $event);
if ($ret) {
$this->success(__('成功'));
} else {
$this->error(__('验证码不正确'));
}
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\exception\Exception;
use addons\groupon\model\Store as ModelStore;
use addons\groupon\model\User;
use addons\groupon\model\UserStore;
class Store extends Base
{
protected $noNeedLogin = ['nearby', 'detail'];
protected $noNeedRight = ['*'];
public function index()
{
$user = User::info();
$userStore = UserStore::where('user_id', $user->id)->select();
$store_id_arr = array_column($userStore, 'store_id');
$stores = [];
if ($store_id_arr) {
$stores = ModelStore::show()->where('id', 'in', $store_id_arr)->select();
}
$this->success('获取自提点列表', $stores);
}
public function nearby()
{
$params = $this->request->get();
$stores = ModelStore::getStoreList($params);
$this->success('获取自提点列表', $stores);
}
public function detail() {
$params = $this->request->get();
$store = ModelStore::getStoreDetail($params);
$this->success('获取自提点', $store);
}
}
... ...
<?php
namespace addons\groupon\controller;
use app\common\library\Ems;
use app\common\library\Sms;
use fast\Random;
use think\Validate;
use addons\groupon\library\Wechat;
use think\Db;
use addons\groupon\model\UserOauth;
use addons\groupon\model\User as UserModel;
use addons\groupon\model\UserStore;
/**
* 会员接口
*/
class User extends Base
{
protected $noNeedLogin = ['login', 'mobilelogin', 'accountLogin', 'register', 'resetpwd', 'changeemail', 'changemobile', 'third', 'wxMiniProgramOauth', 'getWxMiniProgramSessionKey', 'getUserDefaultFields'];
protected $noNeedRight = '*';
public function _initialize()
{
return parent::_initialize();
}
/**
* 会员中心
*/
public function index()
{
$auth = \app\common\library\Auth::instance();
$auth->setAllowFields(['id', 'username', 'nickname', 'mobile', 'avatar', 'score', 'birthday', 'money', 'group', 'group_id', 'child_user_count', 'child_user_count_1', 'child_user_count_2', 'total_consume']);
$data = $auth->getUserinfo();
$data['avatar'] = $data['avatar'] ? cdnurl($data['avatar'], true) : '';
if (!isset($data['group'])) {
$data['group'] = \addons\groupon\model\UserGroup::get($data['group_id']);
}
// 查询用户优惠券数量
$userCoupons = \addons\groupon\model\Coupons::getCouponsList(1);
$data['coupons_num'] = count($userCoupons);
// 查询用户是否是门店管理员
$userStores = UserStore::where('user_id', $data['id'])->select();
$data['is_store'] = $userStores ? 1 : 0;
$data['store_id'] = 0;
if (count($userStores) == 1) {
// 只有一个店铺 直接进入店铺
$data['store_id'] = $userStores[0]['store_id'];
}
$this->success('用户信息', $data);
}
/**
* 会员登录
*
* @param string $account 账号
* @param string $password 密码
*/
public function accountLogin()
{
$account = $this->request->post('account');
$password = $this->request->post('password');
if (!$account || !$password) {
$this->error(__('Invalid parameters'));
}
$ret = $this->auth->login($account, $password);
if ($ret) {
$data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Logged in successful'), $data);
} else {
$this->error($this->auth->getError());
}
}
/**
* 获取微信小程序session_key
*
* @param string $code 加密code
* @param boolean $autoLogin 是否自动登录(需已注册会员)
*/
public function getWxMiniProgramSessionKey()
{
$post = $this->request->post();
$autoLogin = $post['autoLogin'];
$wechat = new Wechat('wxMiniProgram');
$decryptSession = $wechat->code($post['code']);
if (!isset($decryptSession['session_key'])) {
$this->error('未获取session_key,请重启应用');
}
$uuid = Random::uuid();
\think\Cache::set($uuid, $decryptSession, 24 * 3600 * 30); // 强制30天过期
$userOauth = UserOauth::get([
'provider' => 'Wechat',
'platform' => 'wxMiniProgram',
'openid' => $decryptSession['openid'],
'user_id' => ['neq', 0]
]);
if ($userOauth) {
$userOauth->save(['session_key' => $decryptSession['session_key']]);
}
if ($autoLogin && $userOauth) {
$ret = $this->auth->direct($userOauth->user_id);
if ($ret) {
$token = $this->auth->getToken();
$this->success(__('Logged in successful'), ['token' => $token, 'session_id' => $uuid, 'openid' => $decryptSession['openid']]);
} else {
$this->error($this->auth->getError());
}
}
$this->success('', ['session_id' => $uuid]);
}
/**
* 微信小程序登录
*
* @param string $session_key session_key
* @param string $signature 校验签名
* @param string $iv 解密向量
* @param string $encryptedData 需解密完整用户信息
* @param boolean $refresh 重新获取或刷新最新的用户信息 (用户头像失效或微信客户端修改昵称等情况)
*/
public function wxMiniProgramOauth()
{
$post = $this->request->post();
$post['encryptedData'] = $this->request->post('encryptedData', null, null);
$token = Db::transaction(function () use ($post) {
try {
$wechat = new Wechat('wxMiniProgram');
$decryptSession = \think\Cache::get($post['session_id']);
if (!$decryptSession || !isset($decryptSession['openid'])) {
throw new \Exception('未获取到登录态,请重试');
}
$decryptUserInfo = $wechat->decryptData($decryptSession['session_key'], $post['iv'], $post['encryptedData']); // 客户端传值数据都不可信,需服务端解密用户信息
$decryptUserInfo = array_merge($decryptUserInfo, $decryptSession);
//组装decryptData
$decryptData = array_change_key_case($decryptUserInfo, CASE_LOWER);
if (empty($decryptData['openid'])) {
throw new \Exception('code错误,请重试');
}
$decryptData['headimgurl'] = $decryptData['avatarurl'];
$decryptData['sex'] = $decryptData['gender'];
$decryptData['session_key'] = $decryptSession['session_key'];
return $this->oauthLoginOrRegisterOrBindOrRefresh($post['event'], $decryptData, 'wxMiniProgram', 'Wechat');
} catch (\Exception $e) {
$this->error($e->getMessage());
}
});
if ($token) {
$this->success(__('Logged in successful'), ['token' => $token]);
} else {
$this->error($this->auth->getError());
}
}
/**
* 微信小程序登录
*
* @param string $code 加密code
*/
public function wxMiniProgramPhone()
{
$user = $this->auth->getUser();
$post = $this->request->post();
$decryptSession = \think\Cache::get($post['session_id']);
$wechat = new Wechat('wxMiniProgram');
$decryptUserInfo = $wechat->decryptData($decryptSession['session_key'], $post['iv'], $post['encryptedData']);
$phone = $decryptUserInfo['purePhoneNumber'];
if (empty($phone)) {
$this->error('获取手机号授权失败');
}
if ($post['type'] == 'bind') {
// 绑定手机号
if (\app\common\model\User::where('mobile', $phone)->where('id', '<>', $user->id)->find()) {
$this->error('手机号已存在');
}
$verification = $user->verification;
$verification->mobile = 1;
$user->verification = $verification;
$user->mobile = $phone;
$user->save();
}
$this->success("绑定成功", [
'phone' => $phone
]);
}
/**
* 第三方登录或自动注册
*
* @param array $decryptData 解密参数
* @param string $platform 平台名称
* @param string $provider 厂商名称
* @param int $keeptime 有效时长
* @param boolean $refresh 刷新头像和昵称
*/
private function oauthLoginOrRegisterOrBindOrRefresh($event, $decryptData, $platform, $provider, $keeptime = 0)
{
$oauthData = $decryptData;
$oauthData = array_merge($oauthData, [
'provider' => $provider,
'platform' => $platform,
]);
if ($platform === 'wxMiniProgram' || $platform === 'App') {
$oauthData['expire_in'] = 7200;
$oauthData['expiretime'] = time() + 7200;
}
$userOauth = UserOauth::where(['openid' => $decryptData['openid'], 'user_id' => ['neq', 0]])->where('platform', $platform)->where('provider', $provider)->lock(true)->find();
switch ($event) {
case 'login': // 登录(自动注册)
if (!$userOauth) { // 没有找到第三方登录信息 创建新用户
//默认创建新用户
$user_id = 0;
$createNewUser = true;
$oauthData['logintime'] = time();
$oauthData['logincount'] = 1;
// 判断是否有unionid 并且已存在oauth数据中
if (isset($oauthData['unionid'])) {
//存在同厂商信息,添加oauthData数据,合并用户
$userUnionOauth = UserOauth::get(['unionid' => $oauthData['unionid'], 'provider' => $provider, 'user_id' => ['neq', 0]]);
if ($userUnionOauth) {
$existUser = $this->auth->direct($userUnionOauth->user_id);
if ($existUser) {
$createNewUser = false;
}
}
}
// 创建空用户
if ($createNewUser) {
$username = Random::alnum(20);
$password = '';
$domain = request()->host();
$extend = $this->getUserDefaultFields();
$extend['nickname'] = $oauthData['nickname'] ? $oauthData['nickname'] : $extend['nickname'];
$extend['avatar'] = $oauthData['headimgurl'] ? $oauthData['headimgurl'] : $extend['avatar'];
$this->auth->register($username, $password, $username . '@' . $domain, '', $extend, $keeptime);
if (empty($oauthData['nickname'])) {
$this->auth->getUser()->save(['nickname' => $extend['nickname'] . $this->auth->getUser()->id]);
}
}
$oauthData['user_id'] = $this->auth->getUser()->id;
$oauthData['createtime'] = time();
UserOauth::strict(false)->insert($oauthData);
} else {
// 找到第三方登录信息,直接登录
$user_id = $userOauth->user_id;
if ($this->auth->direct($user_id) && $this->auth->getUser()) { // 获取到用户
$oauthData['logincount'] = $userOauth->logincount + 1;
$oauthData['logintime'] = time();
$userOauth->allowField(true)->save($oauthData);
} else { // 用户已被删除 重新执行登录
// throw new \Exception('此用户已删除');
$userOauth->delete();
$this->oauthLoginOrRegisterOrBindOrRefresh($event, $decryptData, $platform, $provider);
}
}
break;
case 'refresh':
if (!$userOauth) {
throw new \Exception('未找到第三方授权账户');
}
if (!empty($oauthData['nickname'])) {
$refreshFields['nickname'] = $oauthData['nickname'];
}
if (!empty($oauthData['headimgurl'])) {
$refreshFields['avatar'] = $oauthData['headimgurl'];
}
$this->auth->getUser()->save($refreshFields);
$userOauth->allowField(true)->save($oauthData);
break;
case 'bind':
if (!$this->auth->getUser()) {
throw new \Exception('请先登录');
}
$oauthData['user_id'] = $this->auth->getUser()->id;
if ($userOauth) {
if ($userOauth['user_id'] != 0 && $userOauth['user_id'] != $this->auth->getUser()->id && UserModel::get($userOauth['user_id'])) {
throw new \Exception('该账号已被其他用户绑定');
}
$oauthData['id'] = $userOauth->id;
$userOauth->strict(false)->update($oauthData);
} else {
$oauthData['logincount'] = 1;
$oauthData['logintime'] = time();
$oauthData['createtime'] = time();
UserOauth::strict(false)->insert($oauthData);
}
break;
}
if ($this->auth->getUser()) {
return $this->auth->getToken();
}
return false;
}
/**
* 手机验证码登录
*
* @param string $mobile 手机号
* @param string $code 验证码
*/
public function mobileLogin()
{
$mobile = $this->request->post('mobile');
$code = $this->request->post('code');
if (!$mobile || !$code) {
$this->error(__('Invalid parameters'));
}
if (!Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('Mobile is incorrect'));
}
if (!Sms::check($mobile, $code, 'mobilelogin')) {
$this->error(__('Captcha is incorrect'));
}
$user = \app\common\model\User::getByMobile($mobile);
if ($user) {
if ($user->status != 'normal') {
$this->error(__('Account is locked'));
}
//如果已经有账号则直接登录
$ret = $this->auth->direct($user->id);
}
if ($ret) {
Sms::flush($mobile, 'mobilelogin');
$data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Logged in successful'), $data);
} else {
$this->error($this->auth->getError());
}
}
/**
* 注册会员
*
* @param string $username 用户名
* @param string $password 密码
* @param string $email 邮箱
* @param string $mobile 手机号
* @param string $code 验证码
*/
public function register()
{
$username = $this->request->post('mobile');
$password = $this->request->post('password');
$email = $this->request->post('mobile') . '@' . request()->host();
$mobile = $this->request->post('mobile');
$code = $this->request->post('code');
if (!$password) {
$this->error(__('请填写密码')); //TODO:密码规则校验
}
if (strlen($password) < 6 || strlen($password) > 16) {
$this->error(__('密码长度 6-16 位')); //TODO:密码规则校验
}
if ($email && !Validate::is($email, "email")) {
$this->error(__('邮箱填写错误'));
}
if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('手机号填写错误'));
}
$ret = Sms::check($mobile, $code, 'register');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
$extend = $this->getUserDefaultFields();
$ret = $this->auth->register($username, $password, $email, $mobile, $extend);
if ($ret) {
$user = $this->auth->getUser();
$user->nickname = $user->nickname . $user->id;
$user->save();
$data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('注册成功'), $data);
} else {
$this->error($this->auth->getError());
}
}
/**
* 注销登录
*/
public function logout()
{
$this->auth->logout();
$this->success(__('Logout successful'));
}
/**
* 修改会员个人信息
*
* @param string $avatar 头像地址
* @param string $username 用户名
* @param string $nickname 昵称
* @param string $birthday 生日
* @param string $bio 个人简介
*/
public function profile()
{
$user = $this->auth->getUser();
$username = $this->request->post('username');
$nickname = $this->request->post('nickname');
$bio = $this->request->post('bio', '');
$birthday = $this->request->post('birthday');
$avatar = $this->request->post('avatar', '', 'trim,strip_tags,htmlspecialchars');
if ($username) {
$exists = \app\common\model\User::where('username', $username)->where('id', '<>', $this->auth->id)->find();
if ($exists) {
$this->error(__('Username already exists'));
}
$user->username = $username;
}
$user->nickname = $nickname;
$user->bio = $bio;
$user->birthday = $birthday;
$user->avatar = $avatar;
$user->save();
$this->success();
}
/**
* 修改邮箱
*
* @param string $email 邮箱
* @param string $captcha 验证码
*/
public function changeemail()
{
$user = $this->auth->getUser();
$email = $this->request->post('email');
$captcha = $this->request->request('captcha');
if (!$email || !$captcha) {
$this->error(__('Invalid parameters'));
}
if (!Validate::is($email, "email")) {
$this->error(__('Email is incorrect'));
}
if (\app\common\model\User::where('email', $email)->where('id', '<>', $user->id)->find()) {
$this->error(__('Email already exists'));
}
$result = Ems::check($email, $captcha, 'changeemail');
if (!$result) {
$this->error(__('Captcha is incorrect'));
}
$verification = $user->verification;
$verification->email = 1;
$user->verification = $verification;
$user->email = $email;
$user->save();
Ems::flush($email, 'changeemail');
$this->success();
}
/**
* 修改手机号
*
* @param string $mobile 手机号
* @param string $captcha 验证码
*/
public function changemobile()
{
$user = $this->auth->getUser();
$mobile = $this->request->post('mobile');
$captcha = $this->request->post('captcha');
if (!$mobile || !$captcha) {
$this->error(__('Invalid parameters'));
}
if (!Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('Mobile is incorrect'));
}
if (\app\common\model\User::where('mobile', $mobile)->where('id', '<>', $user->id)->find()) {
$this->error(__('Mobile already exists'));
}
$result = Sms::check($mobile, $captcha, 'changemobile');
if (!$result) {
$this->error(__('Captcha is incorrect'));
}
$verification = $user->verification;
$verification->mobile = 1;
$user->verification = $verification;
$user->mobile = $mobile;
$user->save();
Sms::flush($mobile, 'changemobile');
$this->success();
}
/**
* 重置密码
*
* @param string $mobile 手机号
* @param string $newpassword 新密码
* @param string $captcha 验证码
*/
public function resetpwd()
{
$type = $this->request->post("type", 'mobile');
$mobile = $this->request->post("mobile");
$email = $this->request->post("email");
$newpassword = $this->request->post("newpassword");
$captcha = $this->request->post("captcha");
if (!$newpassword || !$captcha) {
$this->error(__('Invalid parameters'));
}
if (strlen($newpassword) < 6 || strlen($newpassword) > 16) {
$this->error(__('密码长度 6-16 位')); //TODO:密码规则校验
}
if ($type == 'mobile') {
if (!Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('Mobile is incorrect'));
}
$user = \app\common\model\User::getByMobile($mobile);
if (!$user) {
$this->error(__('User not found'));
}
$ret = Sms::check($mobile, $captcha, 'resetpwd');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
Sms::flush($mobile, 'resetpwd');
} else {
if (!Validate::is($email, "email")) {
$this->error(__('Email is incorrect'));
}
$user = \app\common\model\User::getByEmail($email);
if (!$user) {
$this->error(__('User not found'));
}
$ret = Ems::check($email, $captcha, 'resetpwd');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
Ems::flush($email, 'resetpwd');
}
//模拟一次登录
$this->auth->direct($user->id);
$ret = $this->auth->changepwd($newpassword, '', true);
if ($ret) {
$this->success(__('Reset password successful'));
} else {
$this->error($this->auth->getError());
}
}
public function changepwd()
{
$user = $this->auth->getUser();
$oldpassword = $this->request->post("oldpassword");
$newpassword = $this->request->post("newpassword");
if (!$newpassword || !$oldpassword) {
$this->error(__('Invalid parameters'));
}
if (strlen($newpassword) < 6 || strlen($newpassword) > 16) {
$this->error(__('密码长度 6-16 位')); //TODO:密码规则校验
}
$ret = $this->auth->changepwd($newpassword, $oldpassword);
if ($ret) {
$this->auth->direct($user->id);
$data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Change password successful'), $data);
} else {
$this->error($this->auth->getError());
}
}
private function getUserDefaultFields()
{
$userConfig = json_decode(\addons\groupon\model\Config::get(['name' => 'user'])->value, true);
return $userConfig;
}
}
... ...
<?php
namespace addons\groupon\controller;
class UserBank extends Base
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function info()
{
$type = $this->request->get('type');
$bankInfo = \addons\groupon\model\UserBank::info($type);
$this->success('提现账户', $bankInfo);
}
public function edit () {
$params = $this->request->post();
if ($params['type'] === 'alipay') {
$params['bank_name'] = '支付宝账户';
}
// 表单验证
$this->grouponValidate($params, get_class(), 'edit');
$this->success('编辑成功', \addons\groupon\model\UserBank::edit($params));
}
}
... ...
<?php
namespace addons\groupon\controller;
class UserWalletApply extends Base
{
protected $noNeedLogin = ['rule'];
protected $noNeedRight = ['*'];
public function index()
{
$this->success('提现记录', \addons\groupon\model\UserWalletApply::getList());
}
public function apply () {
repeat_filter(); // 防抖
$type = $this->request->post('type');
$money = $this->request->post('money');
$apply = \addons\groupon\model\UserWalletApply::apply($type, $money);
if ($apply) {
$this->success('申请成功');
}
$this->error('申请失败');
}
public function rule () {
// 提现规则
$config = \addons\groupon\model\UserWalletApply::getWithdrawConfig();
$min = round(floatval($config['min']), 2);
$max = round(floatval($config['max']), 2);
$service_fee = floatval($config['service_fee']) * 100;
$service_fee = round($service_fee, 1); // 1 位小数
$rule = [
'min' => $min,
'max' => $max,
'service_fee' => $service_fee,
'methods' => $config['methods'] ?? []
];
$this->success('提现规则', $rule);
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\exception\Exception;
class UserWalletLog extends Base
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->get();
$wallet_type = $params['wallet_type'] ?? 'money';
if (!in_array($wallet_type, ['money', 'score'])) {
$this->error('参数错误');
}
$this->success(($wallet_type == 'money' ? '钱包记录' : '积分记录'), \addons\groupon\model\UserWalletLog::getList($params));
}
}
... ...
<?php
namespace addons\groupon\controller;
use addons\groupon\library\Wechat as WechatLibrary;
use addons\groupon\model\Wechat as WechatModel;
use addons\groupon\model\Config;
/**
* 微信接口
*/
class Wechat extends Base
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
protected $app = null;
protected $userOpenId = '';
/**
* 微信公众号服务端API对接、处理消息回复
*/
public function index()
{
$wechat = new WechatLibrary('wxOfficialAccount');
$this->app = $wechat->getApp();
$this->app->server->push(function ($message) {
//初始化信息
$this->userOpenId = $message['FromUserName'];
// return json_encode($message, JSON_UNESCAPED_UNICODE); //调试使用
switch ($message['MsgType']) {
case 'event': //收到事件消息
switch ($message['Event']) {
case 'subscribe': //订阅(关注)事件
//获取粉丝信息并保存
$subscribe = WechatModel::get(['type' => 'subscribe']);
if ($subscribe) {
return $this->response($subscribe);
}
break;
case 'unsubscribe': //取消订阅(关注)事件
//获取粉丝信息并保存
break;
case 'CLICK': //自定义菜单事件
return $this->response($message, 'CLICK');
break;
case 'SCAN': //扫码事件
return '';
break;
}
break;
case 'text': //收到文本消息
//检测关键字回复
$content = $message['Content'];
$auto_reply = WechatModel::where('type', 'auto_reply')->where('find_in_set(:keywords,rules)', ['keywords' => $content])->find();
if ($auto_reply) {
return $this->response($auto_reply);
}
case 'image': //收到图片消息
case 'voice': //收到语音消息
case 'video': //收到视频消息
case 'location': //收到坐标消息
case 'link': //收到链接消息
case 'file': //收到文件消息
default: // ... 默认回复消息
$default_reply = WechatModel::where('type', 'default_reply')->find();
if ($default_reply) {
return $this->response($default_reply);
}
}
});
$response = $this->app->server->serve();
// 将响应输出
$response->send();
}
public function jssdk()
{
$params = $this->request->post();
$apis = [
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'getLocation', //获取位置
'openLocation', //打开位置
'scanQRCode', //扫一扫接口
'chooseWXPay', //微信支付
'chooseImage', //拍照或从手机相册中选图接口
'previewImage', //预览图片接口
'uploadImage', //上传图片
'openAddress', // 获取微信地址
];
$uri = urldecode($params['uri']);
$wechat = new WechatLibrary('wxOfficialAccount');
$res = $wechat->getApp()->jssdk->setUrl($uri)->buildConfig($apis, $debug = false, $beta = false, $json = false);
$this->success('sdk', $res);
}
/**
* 微信公众号服务端API对接
*/
public function wxacode()
{
$scene = $this->request->get('scene', '');
$path = $this->request->get('path', '');
if (empty($path)) {
$path = 'pages/index/index';
}
$wechat = new WechatLibrary('wxMiniProgram');
$content = $wechat->getApp()->app_code->getUnlimit($scene, [
'page' => $path,
'is_hyaline' => true,
]);
if ($content instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
return response($content->getBody(), 200, ['Content-Length' => strlen($content)])->contentType('image/png');
} else {
// 小程序码获取失败
$msg = isset($content['errcode']) ? $content['errcode'] : '-';
$msg .= isset($content['errmsg']) ? $content['errmsg'] : '';
\think\Log::write('wxacode-error' . $msg);
$this->error('获取失败', $msg);
}
}
/**
* 回复消息
*/
private function response($replyInfo, $event = 'text')
{
switch ($event) {
case 'SCAN': //解析扫码事件EventKey
break;
case 'CLICK': //解析菜单点击事件EventKey
$key = explode('|', $replyInfo['EventKey']);
if ($key) {
$message['type'] = $key[0];
if ($key[0] === 'text') {
$message['content'] = json_decode(WechatModel::get($key[1])->content, true);
} elseif($key[0] === 'link') {
$link = WechatModel::get($key[1]);
$message = array_merge($message, json_decode($link->content, true));
$message['title'] = $link->name;
// return json_encode($message);
}else {
$message['media_id'] = $key[1];
}
}
break;
default:
$message = json_decode($replyInfo['content'], true);
break;
}
switch ($message['type']) {
case 'text': //回复文本
$content = new \EasyWeChat\Kernel\Messages\Text($message['content']);
break;
case 'image': //回复图片
$content = new \EasyWeChat\Kernel\Messages\Image($message['media_id']);
break;
case 'news': //回复图文
$message = new \EasyWeChat\Kernel\Messages\Media($message['media_id'], 'mpnews');
$this->app->customer_service->message($message)->to($this->userOpenId)->send(); //素材消息使用客服接口回复
break;
case 'voice': //回复语音
$content = new \EasyWeChat\Kernel\Messages\Voice($message['media_id']);
break;
case 'video': //回复视频
$content = new \EasyWeChat\Kernel\Messages\Video($message['media_id']);
break;
case 'link': //回复链接
$items = new \EasyWeChat\Kernel\Messages\NewsItem([
'title' => $message['title'],
'description' => $message['description'],
'url' => $message['url'],
'image' => cdnurl($message['image'], true),
// ...
]);
$content = new \EasyWeChat\Kernel\Messages\News([$items]);
break;
}
return $content;
}
}
... ...
<?php
namespace addons\groupon\controller\store;
use addons\groupon\exception\Exception;
use addons\groupon\controller\Base as GrouponBase;
/**
* 不继承门店的 base
*/
class Apply extends GrouponBase
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function info() {
$config = new \addons\groupon\model\Config;
$storeConfig = json_decode($config->where(['name' => 'store'])->value('value'), true);
$storeConfig['intro_image'] = (isset($storeConfig['intro_image']) && $storeConfig['intro_image']) ? cdnurl($storeConfig['intro_image'], true) : '';
$this->success('自提点申请', [
'apply' => \addons\groupon\model\store\Apply::info(),
'config' => $storeConfig
]);
}
public function apply() {
$params = $this->request->post();
// 表单验证
$this->grouponValidate($params, get_class(), 'apply');
$order = \addons\groupon\model\store\Apply::apply($params);
$this->success('自提点申请提交成功', $order);
}
}
... ...
<?php
namespace addons\groupon\controller\store;
use addons\groupon\exception\Exception;
use addons\groupon\controller\Base as AddonsBase;
use addons\groupon\model\Store;
use addons\groupon\model\User;
use addons\groupon\model\UserStore;
class Base extends AddonsBase
{
public function _initialize()
{
parent::_initialize();
// 验证登录用户是否可以访问门店接口
$this->checkUserStore();
}
/**
* 检测用户管理的是否有门店
*/
private function checkUserStore() {
// 获取当前用户的门店
$user = User::info();
$store_id = $this->request->param('store_id');
if (!$store_id) {
$this->error('请选择自提点');
}
$userStore = UserStore::with('store')->where('user_id', $user->id)->where('store_id', $store_id)->find();
if (!$userStore || !$userStore->store) {
$this->error('权限不足');
}
$store = $userStore->store->toArray();
if (!$store['status']) {
$this->error('自提点已被禁用');
}
// 存 session 本次请求有效
session('current_oper_store', $store);
}
}
... ...
<?php
namespace addons\groupon\controller\store;
class Order extends Base
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->get();
$this->success('订单列表', \addons\groupon\model\store\Order::getList($params));
}
public function detail()
{
$params = $this->request->get();
$this->success('订单详情', \addons\groupon\model\store\Order::detail($params));
}
public function confirm()
{
repeat_filter(); // 防抖
$params = $this->request->post();
$this->success('核销成功', \addons\groupon\model\store\Order::operConfirm($params));
}
}
... ...
<?php
namespace addons\groupon\controller\store;
use addons\groupon\exception\Exception;
use addons\groupon\model\Store as ModelStore;
class Store extends Base
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->get();
$store = ModelStore::info();
if (!$store) {
$this->error('门店不存在');
}
$this->success('获取成功', $store);
}
}
... ...
<?php
namespace addons\groupon\controller\store;
use addons\groupon\exception\Exception;
use addons\groupon\library\traits\export\ExportOrder;
/**
* 门店包裹
*/
class StoreExpress extends Base
{
use ExportOrder;
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index() {
$params = $this->request->get();
$this->success('包裹列表', \addons\groupon\model\store\StoreExpress::getList($params));
}
public function detail() {
$params = $this->request->get();
$this->success('包裹详情', \addons\groupon\model\store\StoreExpress::getDetail($params));
}
/**
* 包裹到货
*
* @return void
*/
public function arrive()
{
$params = $this->request->post();
$this->success('到货成功', \addons\groupon\model\store\StoreExpress::operArrive($params));
}
public function export() {
$params = $this->request->get();
$id = $params['id'] ?? 0;
$orderStoreExpress = \app\admin\model\groupon\order\OrderStoreExpress::with(['store'])->where('id', $id)->find();
if (!$orderStoreExpress) {
$this->error('包裹未找到');
}
$this->exportOutput($orderStoreExpress);
}
}
... ...
<?php
namespace addons\groupon\exception;
use think\Response;
class Exception
{
protected $msg = '错误消息';
protected $code = 0; //TOAST自动弹出消息
const NOT_LOGIN = 401; //未登录自动弹框提醒
const NOT_AUTHORIZE = 403;//
const IGNORE = -1;
public function __construct($msg, $code = 0, $status_code = 200)
{
$this->msg = $msg;
$this->code = $code;
$this->send($status_code);
}
protected function send($code = 200)
{
$data = [
'code' => $this->code,
'msg' => $this->msg,
'data' => null,
'time' => time()
];
$response = Response::create($data, 'json', $code);
throw new \think\exception\HttpResponseException($response);
}
}
... ...
<?php
if (!function_exists('matchLatLng')) {
function matchLatLng($latlng) {
$match = "/^\d{1,3}\.\d{1,30}$/";
return preg_match($match, $latlng) ? $latlng : 0;
}
}
if (!function_exists('getDistanceBuilder')) {
function getDistanceBuilder($lat, $lng) {
return "ROUND(6378.138 * 2 * ASIN(SQRT(POW(SIN((". matchLatLng($lat) . " * PI() / 180 - latitude * PI() / 180) / 2), 2) + COS(". matchLatLng($lat). " * PI() / 180) * COS(latitude * PI() / 180) * POW(SIN((". matchLatLng($lng). " * PI() / 180 - longitude * PI() / 180) / 2), 2))) * 1000) AS distance";
}
}
/**
* 下划线转驼峰
* step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
* step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
*/
if (!function_exists('camelize')) {
function camelize($uncamelized_words, $separator = '_') {
$uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator);
}
}
/**
* 驼峰命名转下划线命名
* 思路:
* 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
*/
if (!function_exists('uncamelize')) {
function uncamelize($camelCaps, $separator='_')
{
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}
}
/**
* 检测系统必要环境
*/
if (!function_exists('checkEnv')) {
function checkEnv($need = [], $is_throw = true)
{
$need = is_string($need) ? [$need] : $need;
// 检测是否安装浮点数运算扩展
if (in_array('bcmath', $need)) {
if (!extension_loaded('bcmath')) {
if ($is_throw) {
new \addons\groupon\exception\Exception('请安装浮点数扩展 bcmath');
} else {
return false;
}
}
}
// 检测是否安装了队列
if (in_array('queue', $need)) {
if (!class_exists(\think\Queue::class)) {
if ($is_throw) {
new \addons\groupon\exception\Exception('请安装 topthink/think-queue:v1.1.6 队列扩展');
} else {
return false;
}
}
}
if (in_array('yansongda', $need)) {
if (!class_exists(\Yansongda\Pay\Pay::class)) {
if ($is_throw) {
new \addons\groupon\exception\Exception('请查看文档,配置支付');
} else {
return false;
}
}
}
return true;
}
}
/**
* 后端防抖
*/
if (!function_exists('repeat_filter')) {
function repeat_filter($key = null, $expire = 5)
{
if (!$key) {
$url = request()->baseUrl();
$ip = request()->ip();
$key = md5($url . ':' . $ip);
}
$hasRedis = config('redis');
if ($hasRedis) {
$redis = (new \addons\groupon\library\Redis())->getRedis();
if ($redis->exists($key)) {
throw new \addons\groupon\exception\Exception('请不要重复提交');
}
$redis->setex($key, $expire, time()); // 缓存 五秒
} else {
if (cache('?' . $key)) {
throw new \addons\groupon\exception\Exception('请不要重复提交');
}
cache($key, time(), $expire);
}
}
}
/**
* 返回配置的微信支付版本
*/
if (!function_exists('pay_version')) {
function pay_version()
{
$version = config('pay_version');
$version = $version ? strtolower($version) : 'v2';
return $version;
}
}
/**
* 过滤掉字符串中的 sql 关键字
*/
if (!function_exists('filter_sql')) {
function filter_sql($str)
{
$str = strtolower($str); // 转小写
$str = str_replace("and", "", $str);
$str = str_replace("execute", "", $str);
$str = str_replace("update", "", $str);
$str = str_replace("count", "", $str);
$str = str_replace("chr", "", $str);
$str = str_replace("mid", "", $str);
$str = str_replace("master", "", $str);
$str = str_replace("truncate", "", $str);
$str = str_replace("char", "", $str);
$str = str_replace("declare", "", $str);
$str = str_replace("select", "", $str);
$str = str_replace("create", "", $str);
$str = str_replace("delete", "", $str);
$str = str_replace("insert", "", $str);
$str = str_replace("union", "", $str);
$str = str_replace("alter", "", $str);
$str = str_replace("into", "", $str);
$str = str_replace("'", "", $str);
$str = str_replace("or", "", $str);
$str = str_replace("=", "", $str);
return $str;
}
}
... ...
<?php
$defaultHooks = [
// 订单创建
'groupon_order_create_before' => [ // 订单创建前
'addons\\groupon\\listener\\order\\Create'
],
'groupon_order_create_after' => [ // 订单创建后
'addons\\groupon\\listener\\order\\Create'
],
'groupon_order_payed_after' => [ // 订单支付成功
'addons\\groupon\\listener\\order\\Payed',
'addons\\groupon\\listener\\store\\Take',
'addons\\groupon\\listener\\order\\Coupon',
],
// 订单关闭
'groupon_order_close_before' => [ // 订单关闭前
],
'groupon_order_close_after' => [ // 订单关闭后
'addons\\groupon\\listener\\order\\Invalid'
],
// 订单取消
'groupon_order_cancel_before' => [ // 订单取消前
],
'groupon_order_cancel_after' => [ // 订单取消后
'addons\\groupon\\listener\\order\\Invalid'
],
// 订单备货
'groupon_order_send_before' => [
],
// 订单备货
'groupon_order_send_after' => [
'addons\\groupon\\listener\\order\\Send'
],
// 订单到货
'groupon_order_arrive_before' => [],
// 订单备货
'groupon_order_arrive_after' => [
'addons\\groupon\\listener\\order\\Send'
],
// 订单确认收货
'groupon_order_confirm_before' => [ // 订单确认收货前
],
'groupon_order_confirm_after' => [ // 订单确认收货后
'addons\\groupon\\listener\\order\\Confirm',
'addons\\groupon\\listener\\store\\Take'
],
'groupon_order_confirm_finish' => [ // 订单确认收货完成
'addons\\groupon\\listener\\order\\Coupon',
],
// 订单完成事件
'groupon_order_finish' => [
'addons\\groupon\\listener\\store\\Take',
'addons\\groupon\\listener\\order\\Coupon',
],
// 订单评价
'groupon_order_comment_before' => [ // 订单评价前
],
'groupon_order_comment_after' => [ // 订单评价后
'addons\\groupon\\listener\\order\\Comment'
],
// 订单退款
'groupon_order_refund_before' => [ // 订单退款前
'addons\\groupon\\listener\\order\\Refund'
],
'groupon_order_refund_after' => [ // 订单退款后
'addons\\groupon\\listener\\order\\Refund',
'addons\\groupon\\listener\\store\\Take'
],
// 活动更新
'groupon_activity_update_after' => [ // 活动更新后
'addons\\groupon\\listener\\activity\\Update'
],
'groupon_activity_delete_after' => [ // 活动删除之后
'addons\\groupon\\listener\\activity\\Update'
],
];
return $defaultHooks;
... ...
name = groupon
title = 社区团购
intro = 自提点管理、数据统计、店铺装修、自定义限时活动、Canvas分享海报、消息通知等多种功能
author = 星品科技
website = https://groupon.7wpp.com
version = 1.1.1
state = 1
url = /addons/groupon
first_menu = groupon
license = regular
licenseto = 44234
... ...
此 diff 太大无法显示。
<?php
namespace addons\groupon\job;
use addons\groupon\library\traits\ActivityCache;
use addons\groupon\model\Activity;
use think\queue\Job;
/**
* 订单自动操作
*/
class ActivityAutoOper extends BaseJob
{
use ActivityCache;
/**
* 活动自动删除
*/
public function autoClose(Job $job, $data){
try {
$activity = $data['activity'];
$activity = Activity::get($activity['id']);
// 一个活动会存在多个队列,要排重
if ($activity) {
// 如果活动还没被删除
// 规则
$rules = $activity['rules'];
// 当前配置应该自动结束的时间
$laterTime = $activity['endtime'];
if (isset($rules['activity_auto_close']) && $rules['activity_auto_close'] > 0) {
$laterTime += ($rules['activity_auto_close'] * 60);
}
// 如果当前时间大于 laterTime,可以执行删除
if (time() >= $laterTime) {
// 删除活动 【软删】
$activity->delete();
// 删除活动缓存
$this->delActivity($activity);
}
}
// 删除 job
$job->delete();
} catch (\Exception $e) {
// 队列执行失败
\think\Log::write('queue-' . get_class() . '-autoClose' . ':执行失败,错误信息:' . $e->getMessage());
}
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\job;
use addons\groupon\model\Order;
use addons\groupon\model\OrderAction;
use think\queue\Job;
/**
* BaseJob 基类
*/
class BaseJob
{
public function failed($data){
// 记录日志
\think\Db::name('groupon_failed_job')->insert([
'data' => json_encode($data),
'createtime' => time(),
'updatetime' => time()
]);
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\job;
use think\queue\Job;
/**
* 队列消息通知
*/
class Notification extends BaseJob
{
/**
* 发送通知
*/
public function send(Job $job, $data){
try {
// 这里获取到的 $notifiables 和 notification 两个都是数组,不是类,尴尬, 更可恨的 notification 只是 {"delay":0,"event":"changemobile"}
$notifiables = $data['notifiables'];
$notification = $data['notification'];
// 因为 notification 只有参数,需要把对应的类传过来,在这里重新初始化
$notification_name = $data['notification_name'];
// 重新实例化 notification 实例
$notification = new $notification_name($notification['data']);
// 发送消息
\addons\groupon\library\notify\Notify::sendNow($notifiables, $notification);
// 删除 job
$job->delete();
} catch (\Exception $e) {
// 队列执行失败
\think\Log::write('queue-' . get_class()
. (isset($notification->event) ? ('-' . $notification->event) : '')
. ':执行失败,错误信息:' . $e->getMessage());
}
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\job;
use addons\groupon\model\GoodsComment;
use addons\groupon\model\Order;
use addons\groupon\model\OrderAction;
use addons\groupon\model\OrderItem;
use addons\groupon\model\Config;
use addons\groupon\model\Verify;
use think\queue\Job;
/**
* 订单自动操作
*/
class OrderAutoOper extends BaseJob
{
/**
* 订单自动关闭
*/
public function autoClose(Job $job, $data){
try {
$order = $data['order'];
$order = Order::get($order['id']);
if ($order && $order['status'] == 0) {
\think\Db::transaction(function () use ($order, $data) {
// 订单关闭前
\think\Hook::listen('groupon_order_close_before', $data);
// 执行关闭
$order->status = Order::STATUS_INVALID;
$order->ext = json_encode($order->setExt($order, ['invalid_time' => time()])); // 取消时间
$order->save();
OrderAction::operAdd($order, null, null, 'system', '系统自动失效订单');
// 订单自动关闭之后 行为 返还用户优惠券,积分
$data = ['order' => $order];
\think\Hook::listen('groupon_order_close_after', $data);
});
}
// 删除 job
$job->delete();
} catch (\Exception $e) {
// 队列执行失败
\think\Log::write('queue-' . get_class() . '-autoClose' . ':执行失败,错误信息:' . $e->getMessage());
}
}
/**
* 订单自动确认
*/
public function autoConfirm(Job $job, $data) {
try {
$order = $data['order'];
$item = $data['item'];
// 只要没有退款成功,所有队列正常执行
$item = OrderItem::where('id', $item['id'])
->where('dispatch_status', 'in', [OrderItem::DISPATCH_STATUS_READY, OrderItem::DISPATCH_STATUS_ARRIVE])
->where('refund_status', 'not in', [OrderItem::REFUND_STATUS_OK, OrderItem::REFUND_STATUS_FINISH])
->find();
if ($item) {
\think\Db::transaction(function () use ($order, $item, $data) {
// 查询核销券,并且将核销券设置为已使用
Verify::canUse()->where('order_item_id', $item['id'])->where('type', 'verify')->update([
'usetime' => time(),
'oper_type' => 'system',
'oper_id' => 0,
]);
(new Order())->getedItem($order, $item, ['oper_type' => 'system']);
});
}
// 删除 job
$job->delete();
} catch (\Exception $e) {
// 队列执行失败
\think\Log::write('queue-' . get_class() . '-autoConfirm' . ':执行失败,错误信息:' . $e->getMessage());
}
}
public function autoComment(Job $job, $data) {
try {
$order = $data['order'];
$item = $data['item'];
// 只要没有退款成功,所有队列正常执行
$item = OrderItem::where('id', $item['id'])
->where('dispatch_status', OrderItem::DISPATCH_STATUS_GETED)
->where('comment_status', OrderItem::COMMENT_STATUS_NO)
->where('refund_status', 'not in', [OrderItem::REFUND_STATUS_OK, OrderItem::REFUND_STATUS_FINISH])
->find();
if ($item) {
\think\Db::transaction(function () use ($order, $item, $data) {
// 订单评价前
\think\Hook::listen('groupon_order_comment_before', $data);
// 获取自动好评内容
$config = Config::where('name', 'order')->cache(300)->find(); // 读取配置自动缓存 5 分钟
$config = isset($config) ? json_decode($config['value'], true) : [];
$comment_content = (isset($config['order_comment_content']) && $config['order_comment_content'])
? $config['order_comment_content'] : '用户默认好评'; // 单位天
GoodsComment::create([
'goods_id' => $item['goods_id'],
'order_id' => $order['id'],
'user_id' => $order['user_id'],
'level' => 5, // 自动好评
'content' => $comment_content,
'images' => '',
'status' => 'show'
]);
$item->comment_status = OrderItem::COMMENT_STATUS_OK; // 评价成功
$item->save();
OrderAction::operAdd($order, $item, null, 'system', '系统自动评价成功');
// 订单评价后
$data = ['order' => $order, 'item' => $item];
\think\Hook::listen('groupon_order_comment_after', $data);
});
}
// 删除 job
$job->delete();
} catch (\Exception $e) {
// 队列执行失败
\think\Log::write('queue-' . get_class() . '-autoComment' . $e->getMessage());
}
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\job;
use addons\groupon\library\traits\Groupon;
use addons\groupon\library\traits\StockSale;
use addons\groupon\model\GoodsComment;
use addons\groupon\model\Order;
use addons\groupon\model\OrderAction;
use addons\groupon\model\OrderItem;
use think\queue\Job;
/**
* 订单支付完成
*/
class OrderPayed extends BaseJob
{
use StockSale;
/**
* 订单支付完成
*/
public function payed(Job $job, $data){
try {
$order = $data['order'];
$user = $data['user'];
$order = Order::with('item')->where('id', $order['id'])->find();
// 数据库删订单的问题常见,这里被删的订单直接把队列移除
if ($order) {
\think\Db::transaction(function () use ($order, $user, $data) {
// 触发订单支付完成事件
$data = ['order' => $order];
\think\Hook::listen('groupon_order_payed_after', $data);
});
}
// 删除 job
$job->delete();
} catch (\Exception $e) {
// 队列执行失败
$error = json_encode([
'a' => $e->getLine(),
'b' => $e->getFile(),
'c' =>$e->getTrace(),
'd' => $e->getMessage()
], JSON_UNESCAPED_UNICODE);
\think\Log::error('queue-' . get_class() . '-payed' . ':执行失败,错误信息:' . $error);
}
}
}
\ No newline at end of file
... ...
<?php
return [
'Keep login' => '保持会话',
'Username' => '用户名',
'User id' => '会员ID',
'Nickname' => '昵称',
'Password' => '密码',
'Sign up' => '注 册',
'Sign in' => '登 录',
'Sign out' => '注 销',
'Guest' => '游客',
'Welcome' => '%s,你好!',
'Add' => '添加',
'Edit' => '编辑',
'Delete' => '删除',
'Move' => '移动',
'Name' => '名称',
'Status' => '状态',
'Weigh' => '权重',
'Operate' => '操作',
'Warning' => '温馨提示',
'Default' => '默认',
'Article' => '文章',
'Page' => '单页',
'OK' => '确定',
'Cancel' => '取消',
'Loading' => '加载中',
'More' => '更多',
'Normal' => '正常',
'Hidden' => '隐藏',
'Submit' => '提交',
'Reset' => '重置',
'Execute' => '执行',
'Close' => '关闭',
'Search' => '搜索',
'Refresh' => '刷新',
'First' => '首页',
'Previous' => '上一页',
'Next' => '下一页',
'Last' => '末页',
'None' => '无',
'Home' => '主页',
'Online' => '在线',
'Logout' => '注销',
'Profile' => '个人资料',
'Index' => '首页',
'Hot' => '热门',
'Recommend' => '推荐',
'Dashboard' => '控制台',
'Code' => '编号',
'Message' => '内容',
'Line' => '行号',
'File' => '文件',
'Menu' => '菜单',
'Type' => '类型',
'Title' => '标题',
'Content' => '内容',
'Append' => '追加',
'Memo' => '备注',
'Parent' => '父级',
'Params' => '参数',
'Permission' => '权限',
'Advance search' => '高级搜索',
'Check all' => '选中全部',
'Expand all' => '展开全部',
'Begin time' => '开始时间',
'End time' => '结束时间',
'Create time' => '创建时间',
'Flag' => '标志',
'Please login first' => '请登录后操作',
'Redirect now' => '立即跳转',
'Operation completed' => '操作成功!',
'Operation failed' => '操作失败!',
'Unknown data format' => '未知的数据格式!',
'Network error' => '网络错误!',
'Advanced search' => '高级搜索',
'Invalid parameters' => '未知参数',
'No results were found' => '记录未找到',
'Parameter %s can not be empty' => '参数%s不能为空',
'You have no permission' => '你没有权限访问',
'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中',
'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转',
];
... ...
<?php
return [
'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制',
'Uploaded file format is limited' => '上传文件格式受限制',
'Uploaded file is not a valid image' => '上传文件不是有效的图片文件',
'Upload successful' => '上传成功',
];
... ...
<?php
return [
'User center' => '会员中心',
'Register' => '注册',
'Login' => '登录',
'Sign up successful' => '注册成功',
'Username can not be empty' => '用户名不能为空',
'Username must be 6 to 30 characters' => '用户名必须6-30个字符',
'Password can not be empty' => '密码不能为空',
'Password must be 6 to 30 characters' => '密码必须6-30个字符',
'Mobile is incorrect' => '手机格式不正确',
'Username already exist' => '用户名已经存在',
'Email already exist' => '邮箱已经存在',
'Mobile already exist' => '手机号已经存在',
'Username is incorrect' => '用户名不正确',
'Email is incorrect' => '邮箱不正确',
'Account is locked' => '账户已经被锁定',
'Password is incorrect' => '密码不正确',
'Account is incorrect' => '账户不正确',
'Account not exist' => '账户不存在',
'Account can not be empty' => '账户不能为空',
'Username or password is incorrect' => '用户名或密码不正确',
'You are not logged in' => '你当前还未登录',
'You\'ve logged in, do not login again' => '你已经存在,请不要重复登录',
'Profile' => '个人资料',
'Verify email' => '邮箱验证',
'Change password' => '修改密码',
'Captcha is incorrect' => '验证码不正确',
'Logged in successful' => '登录成功',
'Logout successful' => '注销成功',
'Operation failed' => '操作失败',
'Invalid parameters' => '参数不正确',
'Change password failure' => '修改密码失败',
'Change password successful' => '修改密码成功',
'Reset password successful' => '重置密码成功',
];
... ...
<?php
namespace addons\groupon\library;
use EasyWeChat\Factory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use Cache\Adapter\Filesystem\FilesystemCachePool;
use addons\groupon\library\Redis;
/**
*
*/
class Export
{
public function __construct()
{
}
public function exportExcel($expTitle, $expCellName, $expTableData, &$spreadsheet = null, &$sheet = null, $pages = [])
{
$page = $pages['page'] ?? 1;
$page_size = $pages['page_size'] ?? 1000;
$is_last_page = $pages['is_last_page'] ?? 1;
$current_total = $pages['current_total'] ?? 0;
if ($current_total) {
// 每次传来的 expTableData 数据条数不等,比如订单导出
$base_cell = $current_total - count($expTableData) + 2;
} else {
$base_cell = ($page - 1) * $page_size + 2;
}
$fileName = $expTitle;
$cellNum = count($expCellName);
$dataNum = count($expTableData);
$cellName = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM', 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ');
if ($page == 1) {
// 不限时
set_time_limit(0);
// 根据需要调大内存限制
ini_set('memory_limit', '512M');
$cache_type = 'redis';
// 设置缓存
if ($cache_type == 'redis' && class_exists(\Cache\Adapter\Redis\RedisCachePool::class)) {
// 将表格数据暂存 redis,可以降低 php 进程内存占用,需要安装扩展包 composer require cache/simple-cache-bridge cache/redis-adapter
$options = [
// 'select' => 0 // 注释解开,并且换成一个空的 select 库,redis 库默认是 0-15 共 16 个库
];
$redis = (new Redis($options))->getRedis();
$pool = new \Cache\Adapter\Redis\RedisCachePool($redis);
$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool);
\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache);
} else if ($cache_type == 'file' && class_exists(FilesystemCachePool::class)) {
// 将数据暂存磁盘,可以降低内存,但是导出速度会大幅下降 需要安装扩展包 composer require cache/filesystem-adapter
$path = ROOT_PATH . 'runtime' . DS . 'export/';
@mkdir($path);
$filesystemAdapter = new Local($path);
$filesystem = new Filesystem($filesystemAdapter);
$pool = new FilesystemCachePool($filesystem);
\PhpOffice\PhpSpreadsheet\Settings::setCache($pool);
}
// 实例化excel
$spreadsheet = new Spreadsheet();
// 初始化工作簿
$sheet = $spreadsheet->getActiveSheet(0);
// 给表头设置边框
$sheet->getStyle('A1:' . $cellName[$cellNum - 1] . '1')->getFont()->setBold(true);
// 表头
$i = 0;
foreach ($expCellName as $key => $cell) {
$sheet->setCellValue($cellName[$i] . '1', $cell);
$i++;
}
}
// for ($i = 0; $i < $cellNum; $i++) {
// $sheet->getColumnDimension($cellName[$i])->setWidth(30);
// }
// 写入数据
for ($i = 0; $i < $dataNum; $i++) {
if ($is_last_page && $i == ($dataNum - 1)) {
$sheet->mergeCells('A' . ($i + $base_cell) . ':' . $cellName[$cellNum - 1] . ($i + $base_cell));
$sheet->setCellValue('A' . ($i + $base_cell), $expTableData[$i][key($expCellName)]);
} else {
$j = 0;
foreach ($expCellName as $key => $cell) {
$sheet->setCellValue($cellName[$j] . ($i + $base_cell), $expTableData[$i][$key]);
$j++;
}
}
}
if ($is_last_page) {
// ini_set('memory_limit', '256M');
ob_end_clean();
header('pragma:public');
header('Content-type:application/vnd.ms-excel;charset=utf-8;name="' . $fileName . '.xlsx"');
header("Content-Disposition:attachment;filename=$fileName.xlsx"); //attachment新窗口打印inline本窗口打印
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
}
}
}
... ...
<?php
namespace addons\groupon\library;
use fast\Http;
class Express
{
// 查询接口
const REQURL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";
// 订阅接口
const SUBURL = "https://api.kdniao.com/api/dist";
protected $config = [];
/**
* 构造函数
*/
public function __construct()
{
$config = \addons\groupon\model\Config::get(['name' => 'services']);
$config = ($config && $config->value) ? json_decode($config->value, true) : [];
$expressConfig = $config['express'] ?? [];
if (!$expressConfig || !$expressConfig['ebusiness_id'] || !$expressConfig['appkey']) {
throw new \Exception('请配置快递接口');
}
$this->config = $expressConfig;
}
/**
* Json方式 物流信息订阅
*/
public function subscribe($data = [], $orderExpress = null, $order = null)
{
$requestData = $this->getRequestData($data, $orderExpress, $order);
$datas = [
'EBusinessID' => $this->config['ebusiness_id'],
'RequestType' => $this->config['type'] == 'free' ? '1008' : '8008',
'RequestData' => urlencode($requestData),
'DataType' => '2',
];
$datas['DataSign'] = $this->encrypt($requestData, $this->config['appkey']);
$result = Http::sendRequest(self::SUBURL, $datas, 'POST', []);
if ($result['ret'] == 1) {
$exResult = json_decode($result['msg'], true);
if (!$exResult['Success']) {
throw new \Exception($exResult['Reason']);
}
return $exResult;
} else {
throw new \Exception($result['msg']);
}
}
// 查询快递信息
public function search($data = [], $orderExpress = null, $order = null)
{
$requestData = $this->getRequestData($data, $orderExpress, $order);
$datas = [
'EBusinessID' => $this->config['ebusiness_id'],
'RequestType' => $this->config['type'] == 'free' ? '1002' : '8001',
'RequestData' => urlencode($requestData),
'DataType' => '2',
];
$datas['DataSign'] = $this->encrypt($requestData, $this->config['appkey']);
$result = Http::sendRequest(self::REQURL, $datas, 'POST', []);
if ($result['ret'] == 1) {
$exResult = json_decode($result['msg'], true);
if (!$exResult['Success']) {
throw new \Exception($exResult['Reason']);
}
return $exResult;
} else {
throw new \Exception($result['msg']);
}
}
// 组装请求数据
private function getRequestData($data = [], $orderExpress = null, $order = null) {
$requestData = [
'OrderCode' => $order ? $order->order_sn : ($data['order_code'] ?? ''),
'ShipperCode' => $data['express_code'],
'LogisticCode' => $data['express_no'],
];
if ($data['express_code'] == 'JD') {
// 京东青龙配送单号
$requestData['CustomerName'] = $this->config['jd_code'];
} else if ($data['express_code'] == 'SF') {
// 收件人手机号后四位
$requestData['CustomerName'] = substr($order->phone, 7);
}
$requestData = json_encode($requestData);
return $requestData;
}
// 差异更新物流信息
public function checkAndAddTraces ($orderExpress, $express) {
$traces = $express['Traces'];
// 查询现有轨迹记录
$orderExpressLog = \addons\groupon\model\OrderExpressLog::where('order_express_id', $orderExpress->id)->where('store_id', $orderExpress['store_id'])->select();
$log_count = count($orderExpressLog);
if ($log_count > 0) {
// 移除已经存在的记录
array_splice($traces, 0, $log_count);
}
// 增加包裹记录
foreach ($traces as $k => $trace) {
$orderExpressLog = new \addons\groupon\model\OrderExpressLog();
$orderExpressLog->user_id = $orderExpress['user_id'] ?? 0;
$orderExpressLog->order_id = $orderExpress['order_id'] ?? 0;
$orderExpressLog->store_id = $orderExpress['store_id'] ?? 0;
$orderExpressLog->order_express_id = $orderExpress['id'];
$orderExpressLog->status = $trace['Action'] ?? $express['State'];
$orderExpressLog->content = $trace['AcceptStation'];
$orderExpressLog->changedate = substr($trace['AcceptTime'], 0, 19); // 快递鸟测试数据 返回的是个 2020-08-03 16:58:272 格式
$orderExpressLog->location = $trace['Location'] ?? ($express['Location'] ?? null);
$orderExpressLog->save();
}
}
// 组装返回结果
public function setPushResult($success = false, $reason = '') {
$result = [
"EBusinessID" => $this->config['ebusiness_id'],
"UpdateTime" => date('Y-m-d H:i:s'),
"Success" => $success,
"Reason" => $reason
];
return $result;
}
// 加签
function encrypt($data, $appkey)
{
return urlencode(base64_encode(md5($data . $appkey)));
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\library;
use EasyWeChat\Factory;
use addons\groupon\model\Config;
use think\Model;
/**
*
*/
class Hook
{
public function __construct()
{
}
public static function register ($behaviors = []) {
$default = require ROOT_PATH . 'addons/groupon/hooks.php';
$behaviors = array_merge($default, $behaviors);
foreach ($behaviors as $tag => $behavior) {
// 数组反转 保证最上面的行为优先级最高
$behavior = array_reverse($behavior);
foreach ($behavior as $be) {
\think\Hook::add($tag, $be, true); // 所有行为都插入最前面
}
}
}
}
... ...
<?php
namespace addons\groupon\library;
use app\admin\library\Auth as AdminAuth;
use addons\groupon\model\Store;
use addons\groupon\model\User;
class Oper
{
public static function set($operType = '', $operId = 0)
{
if ($operType === '') {
// 自动获取操作人
$admin = AdminAuth::instance(); // 没有登录返回的还是这个类实例
if ($admin->isLogin()) {
// 后台管理员
$operType = 'admin';
$operId = $admin->id;
} else if (strpos(request()->url(), 'store.store') !== false) {
// 门店
$store = Store::info();
if ($store) {
$operType = 'store';
$operId = $store['id'];
}
} else if (strpos(request()->url(), 'addons/groupon') !== false) {
// 用户
$user = User::info();
if ($user) {
$operType = 'user';
$operId = $user->id;
}
}
}
if ($operType === '') {
$operType = 'system';
}
return [
'oper_type' => $operType,
'oper_id' => $operId
];
}
public static function get($operType, $operId)
{
$operator = null;
if ($operType === 'admin') {
$operator = \app\admin\model\Admin::where('id', $operId)->field('nickname as name, avatar')->find();
$operator['type'] = '管理员';
} elseif ($operType === 'user') {
$operator = \addons\groupon\model\User::where('id', $operId)->field('nickname as name, avatar')->find();
$operator['type'] = '用户';
} elseif ($operType === 'store') {
$operator = \addons\groupon\model\Store::where('id', $operId)->field('name')->find();
$operator['type'] = '用户';
$operator['avatar'] = '';
} else {
$operator = [
'name' => '系统',
'avatar' => '',
'type' => '系统'
];
}
if(!isset($operator['name'])) {
$operator['name'] = '已删除';
$operator['avatar'] = '';
}
return $operator;
}
}
... ...
<?php
namespace addons\groupon\library;
use Yansongda\Pay\Pay;
use Yansongda\Pay\Log;
use addons\groupon\exception\Exception;
class PayService
{
protected $config;
protected $platform;
protected $payment;
protected $notify_url;
public $method;
public function __construct($payment, $platform = '', $notify_url = '', $type = 'pay')
{
$this->platform = $platform;
$this->payment = $payment;
$this->notify_url = $notify_url;
$this->type = $type;
$this->setPaymentConfig();
}
private function setPaymentConfig()
{
$paymentConfig = json_decode(\addons\groupon\model\Config::get(['name' => $this->payment])->value, true);
// 如果是支付,并且不是 复制地址的支付宝支付
if ($this->type == 'pay' && $this->platform != 'url' && !in_array($this->platform, $paymentConfig['platform'])) {
throw new Exception('暂不支持该方式付款');
}
$this->config = $paymentConfig;
$this->config['notify_url'] = $this->notify_url;
$this->config['http'] = [
'timeout' => 10,
'connect_timeout' => 10,
];
if ($this->payment === 'wechat') {
// 根据不同平台设置相应的 appid
$this->setWechatAppId();
}
// 设置支付证书路径
$this->setCert();
}
private function setWechatAppId()
{
switch ($this->platform) {
case 'wxOfficialAccount':
$platformConfig = json_decode(\addons\groupon\model\Config::get(['name' => $this->platform])->value, true);
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
$this->config['sub_app_id'] = $platformConfig['app_id'];
$this->config['app_id'] = $this->config['app_id']; // 主商户号,关联的 app_id
} else {
$this->config['app_id'] = $platformConfig['app_id'];
}
break;
case 'wxMiniProgram':
$platformConfig = json_decode(\addons\groupon\model\Config::get(['name' => $this->platform])->value, true);
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
$this->config['sub_miniapp_id'] = $platformConfig['app_id'];
// $this->config['sub_app_id'] = $platformConfig['app_id'];
$this->config['miniapp_id'] = $this->config['app_id']; // 主商户号,关联的 app_id
} else {
$this->config['miniapp_id'] = $platformConfig['app_id'];
$this->config['app_id'] = $platformConfig['app_id']; // 小程序微信企业付款
}
break;
case 'H5':
$platformConfig = json_decode(\addons\groupon\model\Config::get(['name' => $this->platform])->value, true);
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
$this->config['sub_app_id'] = $platformConfig['app_id'];
$this->config['appid'] = $this->config['app_id']; // 主商户号,关联的 app_id
} else {
$this->config['app_id'] = $platformConfig['app_id'];
}
break;
case 'App':
$platformConfig = json_decode(\addons\groupon\model\Config::get(['name' => 'App'])->value, true);
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
$this->config['sub_appid'] = $platformConfig['app_id'];
$this->config['sub_app_id'] = $platformConfig['app_id'];
$this->config['appid'] = $this->config['app_id']; // 主商户号,关联的 app_id
} else {
$this->config['appid'] = $platformConfig['app_id']; // 微信 App 支付使用这个
$this->config['app_id'] = $platformConfig['app_id']; // 微信 App 支付退款使用的是这个
}
break;
}
}
// 处理证书路径
private function setCert()
{
// 处理证书路径
if ($this->payment == 'wechat') {
// 微信支付证书
$this->config['cert_client'] = ROOT_PATH . 'public' . $this->config['cert_client'];
$this->config['cert_key'] = ROOT_PATH . 'public' . $this->config['cert_key'];
} else {
// 支付宝证书路径
$end = substr($this->config['ali_public_key'], -4);
if ($end == '.crt') {
$this->config['ali_public_key'] = ROOT_PATH . 'public' . $this->config['ali_public_key'];
}
$this->config['app_cert_public_key'] = ROOT_PATH . 'public' . $this->config['app_cert_public_key'];
$this->config['alipay_root_cert'] = ROOT_PATH . 'public' . $this->config['alipay_root_cert'];
}
}
private function setPaymentMethod()
{
$method = [
'wechat' => [
'wxOfficialAccount' => 'mp', //公众号支付 Collection
'wxMiniProgram' => 'miniapp', //小程序支付
'H5' => 'wap', //手机网站支付 Response
'App' => 'app' //APP 支付 JsonResponse
],
'alipay' => [
'wxOfficialAccount' => 'wap', //手机网站支付 Response
'wxMiniProgram' => 'wap', //小程序支付
'H5' => 'wap', //手机网站支付 Response
'url' => 'wap', //手机网站支付 Response
'App' => 'app' //APP 支付 JsonResponse
],
];
$this->method = $method[$this->payment][$this->platform];
}
public function create($order)
{
// $order = [
// 'out_trade_no' => time(),
// 'total_fee' => '1', // **单位:分**
// 'body' => 'test body - 测试',
// 'openid' => 'onkVf1FjWS5SBIixxxxxxx', //微信需要带openid过来
// ];
// 设置支付方式
$this->setPaymentMethod();
$method = $this->method;
switch ($this->payment) {
case 'wechat':
if (isset($this->config['mode']) && $this->config['mode'] === 'service') {
$order['sub_openid'] = $order['openid'] ?? '';
unset($order['openid']);
}
$order['total_fee'] = $order['total_fee'] * 100;
$pay = Pay::wechat($this->config)->$method($order);
break;
case 'alipay':
if (in_array($this->platform, ['wxOfficialAccount', 'wxMiniProgram', 'H5'])) {
// 返回支付宝支付链接
$pay = request()->domain() . '/addons/groupon/pay/alipay?order_sn=' . $order['out_trade_no'];
} else {
if ($this->method == 'wap') {
// 支付宝 wap 支付,增加 return_url
// 获取 h5 域名
$platformConfig = json_decode(\addons\groupon\model\Config::get(['name' => 'groupon'])->value, true);
// 如果域名存在,增加 return_url
if ($platformConfig && isset($platformConfig['domain'])) {
$start = substr($platformConfig['domain'], -1) == '/' ? "" : "/";
$this->config['return_url'] = $platformConfig['domain'] . $start . "pages/order/payment/result?orderSn=" . $order['out_trade_no'] . "&type=alipay&pay=1";
}
}
$pay = Pay::alipay($this->config)->$method($order);
}
break;
}
return $pay;
}
// 企业付款
public function transfer($payload)
{
// 服务商模式企业付款使用子商户证书
if ($this->payment == 'wechat' && isset($this->config['mode']) && $this->config['mode'] === 'service') {
$this->config['cert_client'] = ROOT_PATH . 'public' . $this->config['sub_cert_client'];
$this->config['cert_key'] = ROOT_PATH . 'public' . $this->config['sub_cert_key'];
$this->config['key'] = $this->config['sub_key'];
$this->config['mch_id'] = $this->config['sub_mch_id'];
$this->config['mode'] = 'normal'; // 临时改为普通商户
}
$code = 0;
$response = [];
// 支付宝返回结果
// [code] => 10000
// [msg] => Success
// [order_id] => 20210309110070000006730051669626
// [out_biz_no] => W202107231625700647008000
// [pay_fund_order_id] => 20210309110070001506730065959451
// [status] => SUCCESS
// [trans_date] => 2021-03-09 10:42:23
// 微信返回结果
// [return_code] => SUCCESS
// [return_msg] => Array
// (
// )
// [mch_appid] => wx38939920ace0d244
// [mchid] => 1481069012
// [nonce_str] => O9fwzb6HDGO2zU6H
// [result_code] => SUCCESS
// [partner_trade_no] => W202106361960501154008000
// [payment_no] => 10100294905592102257932685920233
// [payment_time] => 2021-02-25 18:36:30
switch ($this->payment) {
case 'wechat':
$payload['amount'] = $payload['amount'] * 100;
$response = Pay::wechat($this->config)->transfer($payload);
if ($response['return_code'] === 'SUCCESS' && $response['result_code'] === 'SUCCESS') {
$code = 1;
}
break;
case 'alipay':
$response = Pay::alipay($this->config)->transfer($payload);
if ($response['code'] === '10000' && $response['status'] === 'SUCCESS') {
$code = 1;
}
break;
}
return [$code, $response];
}
public function notify($callback)
{
$pay = $this->getPay();
try {
$data = $pay->verify(); // 是的,验签就这么简单!
$result = $callback($data, $pay);
// Log::debug('Wechat notify', $data->all());
} catch (\Exception $e) {
\think\Log::error('notify-error:' . $e->getMessage());
// $e->getMessage();
}
return $result;
}
public function refund($order_data)
{
$pay = $this->getPay();
$order_data['type'] = $this->platform == 'wxMiniProgram' ? 'miniapp' : '';
$result = $pay->refund($order_data);
\think\Log::write('refund-result' . json_encode($result));
if ($this->payment == 'wechat') {
// 微信通知回调 pay->notifyr
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
return $result;
} else {
throw new \Exception($result['return_msg']);
}
} else {
// 支付宝通知回调 pay->notifyx
if ($result['code'] == "10000") {
return $result;
} else {
throw new \Exception($result['msg']);
}
}
}
public function notifyRefund($callback)
{
$pay = $this->getPay();
try {
$data = $pay->verify(null, true); // 是的,验签就这么简单!
\think\Log::write('notifyr-result:' . json_encode($data));
$result = $callback($data, $pay);
// Log::debug('Wechat notify', $data->all());
} catch (\Exception $e) {
\think\Log::write('notifyr-verify-error:' . $e->getMessage()); // $e->getMessage();
return false;
}
return $result;
}
private function getPay()
{
switch ($this->payment) {
case 'wechat':
$pay = Pay::wechat($this->config);
break;
case 'alipay':
$pay = Pay::alipay($this->config);
break;
default:
throw new Exception('支付方式不支持');
}
return $pay;
}
}
... ...
<?php
namespace addons\groupon\library;
use addons\groupon\exception\Exception;
class Redis
{
protected $handler = null;
protected $options = [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'expire' => 0,
'persistent' => false,
'prefix' => '',
];
/**
* 构造函数
* @param array $options 缓存参数
* @access public
*/
public function __construct($options = [])
{
if (!extension_loaded('redis')) {
throw new \BadFunctionCallException('not support: redis');
}
// 获取 redis 配置
$config = \think\Config::get('redis');
if (empty($config) && empty($options)) {
throw new \Exception('redis connection fail: no redis config');
}
if (!empty($config)) {
$this->options = array_merge($this->options, $config);
}
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
$this->handler = new \Redis();
if ($this->options['persistent']) {
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
} else {
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
}
if ('' != $this->options['password']) {
$this->handler->auth($this->options['password']);
}
if (0 != $this->options['select']) {
$this->handler->select($this->options['select']);
}
// 赋值全局,避免多次实例化
$GLOBALS['SPREDIS'] = $this->handler;
}
public function getRedis() {
return $this->handler;
}
}
\ No newline at end of file
... ...
<?php
namespace addons\groupon\library;
use EasyWeChat\Factory;
use addons\groupon\model\Config;
use think\Model;
use fast\Http;
/**
*
*/
class Wechat extends Model
{
protected $config;
protected $app;
public function __construct($platform)
{
$this->config = Config::getEasyWechatConfig($platform);
switch ($platform) {
case 'wxOfficialAccount':
$this->app = Factory::officialAccount($this->config);
break;
case 'wxMiniProgram':
$this->app = Factory::miniProgram($this->config);
break;
}
}
// 返回实例
public function getApp() {
return $this->app;
}
//小程序:获取openid&session_key
public function code($code)
{
return $this->app->auth->session($code);
}
public function oauth()
{
$oauth = $this->app->oauth;
return $oauth;
}
//解密信息
public function decryptData($session, $iv, $encryptData)
{
$data = $this->app->encryptor->decryptData($session, $iv, $encryptData);
return $data;
}
public function unify($orderBody)
{
$result = $this->app->order->unify($orderBody);
return $result;
}
public function bridgeConfig($prepayId)
{
$jssdk = $this->app->jssdk;
$config = $jssdk->bridgeConfig($prepayId, false);
return $config;
}
public function notify()
{
$result = $this->app;
return $result;
}
//获取accessToken
public function getAccessToken()
{
$accessToken = $this->app->access_token;
$token = $accessToken->getToken(); // token 数组 token['access_token'] 字符串
//$token = $accessToken->getToken(true); // 强制重新从微信服务器获取 token.
return $token;
}
public function sendTemplateMessage($attributes)
{
extract($attributes);
$this->app->template_message->send([
'touser' => $openId,
'template_id' => $templateId,
'page' => $page,
'form_id' => $formId,
'data' => $data,
'emphasis_keyword' => $emphasis_keyword
]);
}
// 同步小程序直播
public function live(Array $params = [])
{
$default = [
'start' => 0,
'limit' => 10
];
$params = array_merge($default, $params);
$default = json_encode($params);
$access_token = $this->app->access_token->getToken();
$getRoomsListUrl = "https://api.weixin.qq.com/wxa/business/getliveinfo?access_token={$access_token['access_token']}";
$headers = ['Content-type: application/json'];
$options = [
CURLOPT_HTTPHEADER => $headers
];
$result = Http::sendRequest($getRoomsListUrl, $default, 'POST', $options);
if (isset($result['ret']) && $result['ret']) {
$msg = json_decode($result['msg'], true);
$result = $msg;
}
// $result = $this->app->live->getRooms(...array_values($params));
$rooms = [];
if ($result && $result['errcode'] == 0 && $result['errmsg'] === 'ok') {
$rooms = $result['room_info'];
}
return $rooms;
}
// 小程序直播回放
public function liveReplay(array $params = [])
{
$default = [
'room_id' => 0,
'start' => 0,
'limit' => 20
];
$params = array_merge($default, $params);
$default = json_encode($params);
$access_token = $this->app->access_token->getToken();
$getPlayBackListUrl = "https://api.weixin.qq.com/wxa/business/getliveinfo?access_token={$access_token['access_token']}";
$headers = ['Content-type: application/json'];
$options = [
CURLOPT_HTTPHEADER => $headers
];
$result = Http::sendRequest($getPlayBackListUrl, $default, 'POST', $options);
if (isset($result['ret']) && $result['ret']) {
$msg = json_decode($result['msg'], true);
$result = $msg;
}
// $result = $this->app->live->getPlaybacks(...array_values($params));
$liveReplay = [];
if ($result && $result['errcode'] == 0 && $result['errmsg'] === 'ok') {
$liveReplay = $result['live_replay'];
}
return $liveReplay;
}
public function menu($act = 'create', $buttons = '')
{
$result = $this->app->menu->$act($buttons);
return $result;
}
// 公众号 获取所有粉丝
public function asyncFans($nextOpenId = null, $currentPage = 1, $totalPage = 1)
{
$fans = $this->app->user->list($nextOpenId);
$openIdsArray = $fans['data']['openid'];
//放入最大10000条openid队列去执行
\think\Queue::push('\addons\groupon\job\Wechat@createQueueByOpenIdsArray', $openIdsArray, 'groupon');
//第一次计算总页数
if ($currentPage === 1) {
$totalPage = intval($fans['total'] % $fans['count'] === 0 ? $fans['total'] / $fans['count'] : ceil($fans['total'] / $fans['count']));
}
//有分页 递归下一页
if ($currentPage < $totalPage) {
$openIdsArray = array_merge($openIdsArray, $this->asyncFans($fans['next_openid'], $currentPage++, $totalPage));
}
if ($currentPage == $totalPage) {
if ($totalPage == 1) {
$code = 1;
$msg = '同步成功';
}else{
$code = 1;
$msg = '数据较大,请稍后再查看...';
}
return [
'code' => $code,
'msg' => $msg
];
}
return $openIdsArray;
}
//通过openid获取已经关注的用户信息
public function getSubscribeUserInfoByOpenId(array $openIdsArray)
{
$result = $this->app->user->select($openIdsArray);
return $result;
}
}
... ...
<?php
namespace addons\groupon\library\notify;
use addons\groupon\exception\Exception;
use think\queue\ShouldQueue;
/**
* 消息通知 trait
*/
trait Notifiable
{
public function notify ($notification) {
return \addons\groupon\library\notify\Notify::send([$this], $notification);
}
}
... ...