zory il y a 2 heures
Parent
commit
efdf5f0a37
56 fichiers modifiés avec 3439 ajouts et 294 suppressions
  1. 31 0
      app/command/ClearCart.php
  2. 106 0
      app/command/DayMoney.php
  3. 51 0
      app/command/DayPay.php
  4. 59 0
      app/command/DayTotal.php
  5. 58 27
      app/controller/api/Card.php
  6. 130 3
      app/controller/api/Cart.php
  7. 101 3
      app/controller/api/Home.php
  8. 92 66
      app/controller/api/Notify.php
  9. 43 13
      app/controller/api/Order.php
  10. 263 0
      app/controller/api/Test.php
  11. 8 3
      app/controller/api/User.php
  12. 0 1
      app/controller/common/Login.php
  13. 1 0
      app/controller/exe/Login.php
  14. 154 113
      app/controller/exe/Prints.php
  15. 122 0
      app/controller/wap/Card.php
  16. 200 0
      app/controller/wap/Detail.php
  17. 104 0
      app/controller/wap/Discount.php
  18. 175 47
      app/controller/wap/Login.php
  19. 109 0
      app/controller/wap/Member.php
  20. 140 0
      app/controller/wap/Order.php
  21. 108 0
      app/controller/wap/Price.php
  22. 102 0
      app/controller/wap/Prints.php
  23. 70 0
      app/controller/wap/Turnover.php
  24. 22 9
      app/event/CreateOrder.php
  25. 8 2
      app/event/MergePdf.php
  26. 108 0
      app/event/PushPrint.php
  27. 19 0
      app/event/SyncUser.php
  28. 158 0
      app/extra/jhfPay/Pay.php
  29. 134 0
      app/extra/jhfPay/Utils.php
  30. 31 0
      app/extra/service/saas/MemberService.php
  31. 42 0
      app/extra/service/saas/OrderService.php
  32. 36 0
      app/extra/service/saas/ShopLogService.php
  33. 35 0
      app/extra/service/system/MoneyLogService.php
  34. 1 0
      app/extra/wechat/WechatService.php
  35. 19 0
      app/functions.php
  36. 7 2
      app/middleware/WxMiddleware.php
  37. 45 0
      app/model/saas/SaasPrintLog.php
  38. 7 0
      app/model/saas/SaasShop.php
  39. 46 0
      app/model/saas/SaasWordChange.php
  40. 61 0
      app/model/system/SystemUserBankcard.php
  41. 57 0
      app/model/system/SystemUserMoney.php
  42. 113 0
      app/queue/redis/PushPrint.php
  43. 46 0
      app/queue/redis/WordChange.php
  44. 42 0
      app/validate/saas/CardValidate.php
  45. 7 2
      app/wss/PrintWss.php
  46. 2 1
      composer.json
  47. 6 0
      config/event.php
  48. 6 0
      config/plugin/shopwwi/auth/app.php
  49. 10 0
      config/plugin/webman/push/app.php
  50. 21 0
      config/plugin/webman/push/process.php
  51. 88 0
      config/plugin/webman/push/route.php
  52. 2 2
      config/plugin/webman/redis-queue/redis.php
  53. 33 0
      config/think-orm.php
  54. BIN
      print-api.zip
  55. 0 0
      public/city.json
  56. BIN
      public/uploads/card/10888738344576-pay.jpg

+ 31 - 0
app/command/ClearCart.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace app\command;
+
+use app\model\saas\SaasCart;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ClearCart extends Command
+{
+
+
+    protected static string $defaultName = 'clear:cart';
+    protected static string $defaultDescription = '清理购物车前一天无效数据';
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $output->writeln("清理开始====>" . getDateFull());
+        $cartNum = (new SaasCart)->where("create_at","<",getDateFull())->count();
+        $output->writeln("共计需清理{$cartNum}条数据====>" . getDateFull());
+        $state = (new SaasCart)->where("create_at","<",getDateFull())->delete();
+        if (!$state) {
+            $output->writeln("清理失败====>" . getDateFull());
+        } else {
+            $output->writeln("清理完成====>" . getDateFull());
+        }
+        return self::SUCCESS;
+    }
+
+}

+ 106 - 0
app/command/DayMoney.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace app\command;
+
+use app\extra\jhfPay\Pay;
+use app\model\saas\SaasOrder;
+use app\model\saas\SaasShop;
+use app\model\saas\SaasUserBuy;
+use app\model\system\SystemUserMoney;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DayMoney extends Command
+{
+
+    protected static string $defaultName = 'day:money';
+    protected static string $defaultDescription = '每日结算金额统计';
+
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $nextPage = 1;$done = 0;
+        $mode = (new SaasShop);
+        $orderMode = (new SaasOrder);
+        $moneyMode = (new SystemUserMoney);
+        $cardMode = (new SaasUserBuy);
+        $day = date("Y-m-d",strtotime("-1 day"));
+//        $respJhf = (new Pay)->config([
+//            "appid"  => sConf("wechat.jhf_appid"),
+//            "mch_id" => sConf("wechat.jhf_mch_id"),
+//            "aeskey" => sConf("wechat.jhf_aeskey"),
+//            "pubkey" => sConf("wechat.jhf_pubkey"),
+//            "prikey" => sConf("wechat.jhf_prikey"),
+//        ]);
+        while (is_numeric($nextPage)) {
+            $total = $mode->count();
+            $result = $mode->limit(($nextPage-1)*10,10)->with(['card'])->select();
+            if ($result->isEmpty()) {
+                $output->writeln('没有更多数据啦~==>'.getDateFull());
+                $nextPage = null;
+                return self::SUCCESS;
+            }
+            if ($done >= $total){
+                $output->writeln('没有更多数据啦!~==>'.getDateFull());
+                $nextPage = null;
+                return self::SUCCESS;
+            }
+            $moneyData = [];
+            foreach ($result as $key => $val)
+            {
+                ++$done;
+                if (empty($val['cash_rate'])) {
+                    $rate = 0.006;
+                } else {
+                    $rate = $val['cash_rate'] / 100;
+                }
+                $dayData = $moneyMode->where("agent_id",$val['shop_id'])->where("day",$day)->findOrEmpty();
+                if ($dayData->isEmpty()) {
+                    $dayMoney = $orderMode->where("shop_id",$val['shop_id'])->whereIn("status",[1,2,3])->whereDay("create_at","yesterday")->where("pay_type",1)->sum("money");
+                    $cardMoney = $cardMode->where("shop_id",$val['shop_id'])->where("status",1)->whereDay("create_at","yesterday")->sum("money");
+                    $status = 0;
+                    $memberId = $val['card']['member_id']??'';
+                    if ($dayMoney == 0) {
+                        $status = 1;
+                    }
+                    $totalMoney = $dayMoney + $cardMoney;
+                    $rateMoney = ($totalMoney==0)?0:round($totalMoney*$rate);
+                    $endMoney = round($totalMoney - $rateMoney,0);
+                    $moneyData[$key] = [
+                        "order_sn"      => strtoupper(md5($day.$val['shop_id'])),
+                        "agent_id"      => $val['shop_id'],
+                        "money"         => $endMoney,
+                        "re_money"      => $totalMoney, // 当日金额
+                        "order_money"   => $dayMoney,
+                        "user_money"    => $cardMoney, // 会员卡充值金额
+                        "rate"          => $rateMoney,
+                        "day"           => $day,
+                        "status"        => $status,
+                        "member_id"     => $memberId
+                    ];
+//                    if (!empty($memberId) && $totalMoney > 0) { // 发起余额分账
+//                        $shopName = $val['shop_name']??'';
+//                        $respJhf->createBalancePay([
+//                            "app_id" => sConf("wechat.jhf_appid"),
+//                            "order_no"  => strtoupper(md5($day.$val['shop_id'])),
+//                            "member_id" => $memberId,
+//                            "pay_amt"   => format_money($endMoney/100,2),
+//                            "description"   => "{$day}-{$shopName}-结算"
+//                        ]);
+//                    }
+                    echo getDateFull()."==={$val['shop_id']}=={$val['shop_name']}==当天营业额统计===¥{$totalMoney}====结算卡:{$memberId}==状态:{$status}===\n";
+                } else {
+                    echo getDateFull()."==={$val['shop_id']}=={$val['shop_name']}=={$day}==已完成统计===\n";
+                }
+            }
+            if (!empty($moneyData)) {
+                $moneyMode->insertAll(array_values($moneyData));
+            }
+            $nextPage = $nextPage+1;
+            $output->writeln("当前页码{$nextPage}==>".getDateFull());
+        }
+        return self::SUCCESS;
+    }
+
+}

+ 51 - 0
app/command/DayPay.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace app\command;
+
+use app\extra\jhfPay\Pay;
+use app\extra\tools\CodeExtend;
+use app\extra\tools\DataExtend;
+use app\model\system\SystemUserBankcard;
+use app\model\system\SystemUserMoney;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DayPay extends Command
+{
+
+    protected static string $defaultName = 'day:pay';
+    protected static string $defaultDescription = '每日结算金额发放';
+
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $card = (new SystemUserBankcard)->select();
+        $day = date("Y-m-d",strtotime("-1 day"));
+        $cardData = unique_arr($card->toArray(),"member_id");
+        $respJhf = (new Pay)->config([
+            "appid"  => sConf("wechat.jhf_appid"),
+            "mch_id" => sConf("wechat.jhf_mch_id"),
+            "aeskey" => sConf("wechat.jhf_aeskey"),
+            "pubkey" => sConf("wechat.jhf_pubkey"),
+            "prikey" => sConf("wechat.jhf_prikey"),
+        ]);
+        if (!empty($cardData)) {
+            foreach ($cardData as $val)
+            {
+                $jsResp = $respJhf->createBalanceWithdraw([
+                    "app_id" => sConf("wechat.jhf_appid"),
+                    "order_no"  => $val['member_id'].CodeExtend::uniqidDate(14),
+                    "member_id" => $val['member_id'],
+                    "remark"    => "{$day}-结算",
+                    "notify_url"    => "https://panel.huiyinduo.cn/notify/withdraw",
+                ]);
+                echo getDateFull()."=={$val['user_name']}==开始结算\n";
+                echo json_encode($jsResp)."\n";
+            }
+        }
+        return self::SUCCESS;
+    }
+
+
+}

+ 59 - 0
app/command/DayTotal.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace app\command;
+
+use app\extra\jhfPay\Pay;
+use app\extra\tools\CodeExtend;
+use app\model\system\SystemUserMoney;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DayTotal extends Command
+{
+
+    protected static string $defaultName = 'day:total';
+    protected static string $defaultDescription = '每日结算金额余额分账';
+
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $day = date("Y-m-d",strtotime("-1 day"));
+        $totalMoney = (new SystemUserMoney)->where("day",$day)->select();
+        $moneyData = [];
+        if ($totalMoney->isEmpty()) return self::SUCCESS;
+        $respJhf = (new Pay)->config([
+            "appid"  => sConf("wechat.jhf_appid"),
+            "mch_id" => sConf("wechat.jhf_mch_id"),
+            "aeskey" => sConf("wechat.jhf_aeskey"),
+            "pubkey" => sConf("wechat.jhf_pubkey"),
+            "prikey" => sConf("wechat.jhf_prikey"),
+        ]);
+        foreach ($totalMoney as $key=>$value) {
+            if (!empty($value['member_id'])) {
+//                echo "{$value['member_id']}==={$value['money']}\n";
+                if (isset($moneyData[$value['member_id']])) {
+                    $moneyData[$value['member_id']] += $value['money'];
+                } else {
+                    $moneyData[$value['member_id']] = $value['money'];
+                }
+            }
+        }
+        if (!empty($moneyData)) {
+            foreach ($moneyData as  $memberId=>$val){
+                $resp = $respJhf->createBalancePay([
+                    "app_id" => sConf("wechat.jhf_appid"),
+                    "order_no"  => CodeExtend::uniqidDate(18),
+                    "member_id" => $memberId,
+                    "pay_amt"   => format_money($val/100,2),
+                    "description"   => "{$day}-结算"
+                ]);
+                echo getDateFull()."===余额分账===\n";
+                echo json_encode($resp)."\n";
+                sleep(1);
+            }
+        }
+        return self::SUCCESS;
+    }
+
+}

+ 58 - 27
app/controller/api/Card.php

@@ -3,6 +3,7 @@
 namespace app\controller\api;
 
 use app\extra\basic\Base;
+use app\extra\jhfPay\Pay;
 use app\extra\tools\CodeExtend;
 use app\middleware\WxMiddleware;
 use app\model\saas\SaasCombo;
@@ -35,22 +36,26 @@ class Card extends Base
             $memberCard['balance'] = format_money($memberCard['balance']/100,2);
             $memberCard['end_at'] = date("Y-m-d",strtotime($memberCard['create_at']));
             $isRecharge = (new SaasUserBuy)->where("shop_id",$param['shop'])->where("openid",$request->user['openid'])->where("status",1)->sum("money");
-            if ($memberCard['shop']['user_card'] == 2) { // 自定义套餐
-                $cardPrice = array_values($memberCard['shop']['user_card_price']);
-            } else {
-                $cardPrice = (new SaasCombo)->where("type",2)->select();
-            }
-            $cardPrice = array_filter($cardPrice, function($item) use ($isRecharge) {
-                if ($isRecharge > 0) {
-                    return $item['is_first'] != '1'; // 注意:这里使用松散比较,因为数据中有字符串'1'
+            $cardPrice = [];
+            if ($memberCard['shop']['user_card'] < 3) {
+                if ($memberCard['shop']['user_card'] == 2) { // 自定义套餐
+                    $cardPrice = array_values($memberCard['shop']['user_card_price']);
                 } else {
-                    return $item;
+                    $cardPrice = (new SaasCombo)->where("type",2)->select();
+                }
+                $cardPrice = array_filter($cardPrice, function($item) use ($isRecharge) {
+                    if ($isRecharge > 0) {
+                        return $item['is_first'] != '1'; // 注意:这里使用松散比较,因为数据中有字符串'1'
+                    } else {
+                        return $item;
+                    }
+                });
+                foreach ($cardPrice as $key=>$val) {
+                    $cardPrice[$key] = $val;
+                    $cardPrice[$key]['money'] = $val['money'];
+                    $cardPrice[$key]['old_money'] = $val['old_money'];
                 }
-            });
-            foreach ($cardPrice as $key=>$val) {
-                $cardPrice[$key] = $val;
-                $cardPrice[$key]['money'] = $val['money'];
-                $cardPrice[$key]['old_money'] = $val['old_money'];
+                $cardPrice = array_values($cardPrice);
             }
             $memberCard['card'] = array_values($cardPrice);
             return success("ok",$memberCard->toArray());
@@ -109,6 +114,7 @@ class Card extends Base
                 $query->field("shop_id,shop_name");
             }])->findOrEmpty();
             if ($card->isEmpty()) return error("尚未开通会员卡");
+            return error("充值通道暂时关闭");
             $orderSn = strtoupper(CodeExtend::random(12,3));
             $buyCard = json_decode($param['card'],true);
             $state = (new SaasUserBuy)->insertGetId([
@@ -121,21 +127,46 @@ class Card extends Base
                 "remark"        => "赠送金额".$buyCard['old_money']
             ]);
             if ($state) {
-                $wechat = new \WeChat\Pay($this->getWxConfig());
-                $options = [
-                    'body'             => $card['shop']['shop_name']."-会员卡充值",
-                    'out_trade_no'     => $orderSn."-".CodeExtend::random(8),
-                    "attach"            => $orderSn,
-                    'total_fee'        => $buyCard['money'] * 100,
-                    'openid'           => $request->user['openid'],
-                    'trade_type'       => 'JSAPI',
-                    'notify_url'       => 'https://panel.huiyinduo.cn/notify/recharge',
-                    'spbill_create_ip' => $request->getRealIp(),
+                $param_data = [];
+                $param_data["order_no"] = $orderSn;
+                $param_data["app_id"] = sConf("wechat.jhf_appid");
+                $param_data["pay_channel"] = "wx_lite";
+                $param_data["pay_amt"] = format_money($buyCard['money'],2);
+                $param_data["goods_title"] = $card['shop']['shop_name']."-会员卡充值";
+                $param_data["device_info"] = array("device_ip" => $request->getRealIp());
+                $param_data['notify_url'] = "https://panel.huiyinduo.cn/notify/recharge";
+                $param_data["expend"] = [
+                    "wx_app_id" => sConf("wechat.mini_appid"),
+                    "open_id" => $request->user['openid']
                 ];
-                // 生成预支付码
-                $result = $wechat->createOrder($options);
+                $respJhf = (new Pay)->config([
+                    "appid"  => sConf("wechat.jhf_appid"),
+                    "mch_id" => sConf("wechat.jhf_mch_id"),
+                    "aeskey" => sConf("wechat.jhf_aeskey"),
+                    "pubkey" => sConf("wechat.jhf_pubkey"),
+                    "prikey" => sConf("wechat.jhf_prikey"),
+                ])->createPay($param_data);
+                if (isset($respJhf['code'])) {
+                    return error("发起支付失败");
+                }
                 // 创建JSAPI参数签名
-                $resp = $wechat->createParamsForJsApi($result['prepay_id']);
+                $resp = json_decode($respJhf['expend']['pay_info'],true);
+                $resp['timestamp'] = $resp['timeStamp'];
+//                $wechat = new \WeChat\Pay($this->getWxConfig());
+//                $options = [
+//                    'body'             => $card['shop']['shop_name']."-会员卡充值",
+//                    'out_trade_no'     => $orderSn."-".CodeExtend::random(8),
+//                    "attach"            => $orderSn,
+//                    'total_fee'        => $buyCard['money'] * 100,
+//                    'openid'           => $request->user['openid'],
+//                    'trade_type'       => 'JSAPI',
+//                    'notify_url'       => 'https://panel.huiyinduo.cn/notify/recharge',
+//                    'spbill_create_ip' => $request->getRealIp(),
+//                ];
+//                // 生成预支付码
+//                $result = $wechat->createOrder($options);
+//                // 创建JSAPI参数签名
+//                $resp = $wechat->createParamsForJsApi($result['prepay_id']);
                 return success("ok",$resp);
             }
             return error("发起充值失败");

+ 130 - 3
app/controller/api/Cart.php

@@ -3,6 +3,7 @@
 namespace app\controller\api;
 
 use app\extra\basic\Base;
+use app\extra\tools\CodeExtend;
 use app\middleware\WxMiddleware;
 use app\model\saas\SaasCart;
 use app\model\saas\SaasCombo;
@@ -12,12 +13,14 @@ use app\model\saas\SaasPrintClient;
 use app\model\saas\SaasShop;
 use app\model\saas\SaasUser;
 use app\model\saas\SaasUserBuy;
+use app\model\saas\SaasWordChange;
 use LinFly\Annotation\Route\Controller;
 use LinFly\Annotation\Route\Middleware;
 use LinFly\Annotation\Route\Route;
 use Qcloud\Cos\Client;
 use support\Request;
 use support\Response;
+use Webman\RedisQueue\Redis;
 use yzh52521\EasyHttp\Http;
 
 
@@ -39,7 +42,8 @@ class Cart extends Base
      * 打印方向
      * @var array|string[]
      */
-    protected array $direction = ["1" => "自适应","2" => "横向", "3" => "竖向"];
+    protected array $direction = ["1" => "长边翻转","2" => "短边翻转",'3' => ""];
+//    protected array $direction = ["1" => "自适应","2" => "横向", "3" => "竖向"];
     /**
      * 配送方式
      * @var array|string[]
@@ -69,7 +73,7 @@ class Cart extends Base
                 "type.default"  => 1
             ]);
             if (!is_array($param)) return error($param);
-            $cart = (new SaasCart)->where("shop_id",$param['shop'])->where("openid",$request->user['openid'])->order("create_at desc")->select();
+            $cart = (new SaasCart)->where("shop_id",$param['shop'])->where("print_id",$param['print'])->where("openid",$request->user['openid'])->order("create_at desc")->select();
             if ($cart->isEmpty()) return success('ok',['cart' => []]);
             $totalAmount = $totalDiscount = 0;
             foreach ($cart as $k=>$v){
@@ -81,6 +85,7 @@ class Cart extends Base
                 $cart[$k] = $v;
                 $cart[$k]['money'] = format_money($v['money'] / 100,2);
                 $cart[$k]['name'] = msubstr($v['name'],0,12);
+                $cart[$k]['jobStatus'] = 'success';
             }
             $printData = (new SaasPrintClient)->where("shop_id",$param['shop'])->where("code",$param['print'])->select();
             if ($printData->isEmpty()) return error('无可用打印机');
@@ -284,14 +289,18 @@ class Cart extends Base
                 "word.require"      => trans("empty.require"),
                 "print.require"     => trans("empty.require"),
                 "type.default"      => 1, // 1打印 2复印
+                "size.default"      => "", // 纸张类型
             ],$request->method());
             if (!is_array($param)) return error($param);
             $wordData = json_decode($param["word"], true);
             $printData = (new SaasPrintClient)->where(['shop_id' => $param['shop'],'code' => $param['print']])->findOrEmpty();
             if ($printData->isEmpty()) return error('无可用打印机');
+            if ($printData['status'] <> 1) return error('当前打印机不可用');
             $paperRule = is_string($printData['rule'])?json_decode($printData['rule'],true):$printData['rule'];
             $paperSize = count($paperRule['paper_size'])==1?$paperRule['paper_size'][0]:'A4';
             $colorSize = count($paperRule['color'])==1?$paperRule['color'][0]:2;
+
+            $paperSize = empty($param['size'])?$paperSize:$param['size'];
             $moneyMode = (new SaasPrice)->where(['shop_id' => $param['shop'],'paper_size' => $paperSize,'color' => $colorSize,'type' => $param['type']])->findOrEmpty();
             if ($moneyMode->isEmpty()) return error("店铺未设置收费规则");
             $extraMoney = 0;
@@ -348,10 +357,13 @@ class Cart extends Base
         try {
             $param = $this->_valid([
                 "cosKey.require"  => trans("empty.require"),
+                "type.default"  => "",
+                "ext.default"  => ""
             ],$request->method());
             if (!is_array($param)) return error($param);
             $suffix = pathinfo($param['cosKey'], PATHINFO_EXTENSION);
             if (empty($suffix)) return error("empty.data.suffix");
+            return error("请重启小程序");
             $cosClient = new Client([
                 'region' => sConf("storage.cos_region"),
                 'schema' => 'https', // 协议头部,默认为 http
@@ -373,7 +385,122 @@ class Cart extends Base
             $resp = Http::get($path)->headers();
             if (!isset($resp['X-Total-Page'])) return error("文档可能需要密码,请先删除后再确认");
             $page = $resp['X-Total-Page']?$resp['X-Total-Page'][0]:1;
-            return success("ok",compact('page'));
+            $path = $param['cosKey'];
+            return success("ok",compact('page','path'));
+        } catch (\Throwable $th) {
+            return error($th->getMessage());
+        }
+    }
+
+
+    #[Route(path: "change_total",methods: ['post','get'])]
+    public function checkChangeStep(Request $request)
+    {
+        try {
+            $param = $this->_valid([
+                "cosKey.require"  => trans("empty.require"),
+                "type.default"  => "",
+                "ext.default"  => ""
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            $suffix = pathinfo($param['cosKey'], PATHINFO_EXTENSION);
+            if (empty($suffix)) return error("empty.data.suffix");
+            $cosClient = new Client([
+                'region' => sConf("storage.cos_region"),
+                'schema' => 'https', // 协议头部,默认为 http
+                'credentials' => array(
+                    'secretId' => sConf("storage.cos_access_key"),
+                    'secretKey' => sConf("storage.cos_secret_key"),
+                ),
+                "verify"    => false
+            ]);
+            $url = $cosClient->getObjectUrl(sConf("storage.cos_bucket"), $param['cosKey']);
+            $params = array(
+                'ci-process' => 'doc-preview',
+                'page' => 1,
+                'dstType' => 'jpg',
+                'imageDpi' => '120',
+            );
+            $query = http_build_query($params);
+            $pathUri = $url.$query;
+            $resp = Http::get($pathUri)->headers();
+            if (!isset($resp['X-Total-Page'])) return error("文档可能需要密码,请先删除后再确认");
+            $page = $resp['X-Total-Page']?$resp['X-Total-Page'][0]:1;
+            if ($param['ext'] !== 'pdf') {
+                $change = (new SaasWordChange)->where("key",md5($param['cosKey']))->findOrEmpty();
+                if ($change->isEmpty())
+                {
+                    $result = $cosClient->CreateDocProcessJobs([
+                        'Bucket' => sConf("storage.cos_bucket"), // 存储桶名称,由 BucketName-Appid 组成,可以在 COS 控制台查看 https://console.cloud.tencent.com/cos5/bucket
+                        'Tag' => 'DocProcess', //任务的 Tag:DocProcess 固定值
+                        'Input' => array(
+                            'Object' => $param['cosKey'] //待操作的文件对象
+                        ),
+                        'Operation' => array(
+                            'DocProcess' => array(
+                                'SheetId' => 0, //表格文件参数,转换第 X 个表,默认为1
+                                'StartPage' => 1, //从第 X 页开始转换,默认为1
+                                'Quality' => 100, //生成预览图的图片质量,取值范围 [1-100],默认值100
+                                'Zoom' => 100, //预览图片的缩放参数,取值范围[10-200], 默认值100
+                                "ImageDpi" => 300
+                            ),
+                            'Output' => array(
+                                'Region' => sConf("storage.cos_region"), //存储桶的地域
+                                'Bucket' => sConf("storage.cos_bucket"), // 存储结果的存储桶
+                                'Object' => 'out/'.date('Ymd').'/'.time().md5($param['cosKey']).'-${Number}.pdf', //输出文件路径
+                            ),
+                        ),
+                    ])->toArray();
+                    $change->insertGetId([
+                        "key"       => md5($param['cosKey']),
+                        "task_id"   => $result['JobsDetail']['JobId']??'',
+                        "total"     => $page
+                    ]);
+                    return error("error");
+                }
+                $result = $cosClient->describeDocProcessJob(array(
+                    'Bucket' => sConf("storage.cos_bucket"), // 存储桶名称,由 BucketName-Appid 组成,可以在 COS 控制台查看 https://console.cloud.tencent.com/cos5/bucket
+                    'Key' => $change['task_id'], // JobId
+                ))->toArray();
+                if ($result['JobsDetail']['State'] == 'Success') {
+                    $change->status = 1;
+                    $change->path = $result['JobsDetail']['Operation']['DocProcessResult']['PageInfo'][0]['TgtUri']??'';
+                    $change->end_at = getDateFull();
+                    $change->save();
+                    $page = $change['total'];
+                    $path = $change['path'];
+                    return success("ok",compact("path","page"));
+                }
+                return error("Error");
+            } else {
+                $path = $param['cosKey'];
+                return success("ok",compact("path","page"));
+            }
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+
+    /**
+     * 获取打印机支持的纸张
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "size",methods: ['post','get'])]
+    public function getPageSize(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "shop.require"      => trans("empty.require"),
+                "print.require"     => trans("empty.require")
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            $printData = (new SaasPrintClient)->where(['shop_id' => $param['shop'],'code' => $param['print']])->findOrEmpty();
+            if ($printData->isEmpty()) return error('无可用打印机');
+            $paperRule = is_string($printData['rule'])?json_decode($printData['rule'],true):$printData['rule'];
+            return success("ok",['size' => $paperRule['paper_size']]);
         } catch (\Throwable $th) {
             return error($th->getMessage());
         }

+ 101 - 3
app/controller/api/Home.php

@@ -15,17 +15,105 @@ use support\Request;
 use support\Response;
 use LinFly\Annotation\Route\Middleware;
 use Shopwwi\WebmanAuth\Facade\Auth as AuthMode;
+use support\think\Db;
 
 
 #[Controller(prefix: "/wx_api/default"),Middleware(WxMiddleware::class)]
 class Home extends Base
 {
 
+
+    protected array $whiteShop = [
+        "10888572278827"    => "10888572278827", // 惠印多马村店
+        "10888566286841"    => "327545130062964", // 中站小学店
+        "10888590458813"    => "326945519114632", // 幸小印美店
+        "10888645729727"    => "326844775971796", // 焦作和平街店
+    ];
+
     /**
      * 跳出授权
      * @var array|string[]
      */
-    protected array $noNeedLogin = ['loginHome'];
+    protected array $noNeedLogin = ['loginHome','changeData','changeDataSubmit'];
+
+    /**
+     * 不需要迁移店铺
+     * @var array|string[]
+     */
+    protected array $shopNoUser = [
+        "10888742460468",
+        "10888697645625",
+        "10888684635673",
+        "10888684633177",
+    ];
+
+    #[Route(path: "change_save",methods: "post")]
+    public function changeDataSubmit(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "shop.require"  => "",
+                "old.require"  => "",
+                "openid.default"  => ""
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            if (in_array($param['shop'],$this->shopNoUser)) return error("该店铺不支持迁移");
+            $member = (new SaasUser)->where("openid",$param['openid'])->findOrEmpty();
+            if (!$member->isEmpty()) return error("无需多次操作");
+            $old = Db::connect("old")->table("inmei_member_card")->where("openid",$param['old'])->findOrEmpty();
+            if (isset($this->whiteShop[$param['shop']])) {
+                $shopId = $this->whiteShop[$param['shop']];
+            } else {
+                $shopId = $param['shop'];
+            }
+            $state = $member->insertGetId([
+                "openid"    => $param['openid'],
+                "shop_id"   => $shopId,
+                "card_no"   => strtoupper(md5($param['openid'].$shopId)),
+                "balance"   => $old['balance'],
+                "total_balance" => $old['total_balance']
+            ]);
+            if (!$state) return error("操作失败");
+            return success("转入成功");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    #[Route(path: "change",methods: "post")]
+    public function changeData(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "shop.require"  => "",
+                "code.require"  => "",
+                "openid.default"  => ""
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            echo getDateFull()."迁移信息\n";
+            echo json_encode($param)."\n";
+            $userinfo = (new Crypt([
+                "appid"     => sConf("wechat.mini_appid"),
+                "appsecret" => sConf("wechat.mini_secret")
+            ]))->session($param['code']);
+            if (!isset($userinfo['openid'])) return error("获取数据失败");
+            $openId = $userinfo['openid'];
+            if (isset($this->whiteShop[$param['shop']])) {
+                $shopId = $this->whiteShop[$param['shop']];
+            } else {
+                $shopId = $param['shop'];
+            }
+            $old = Db::connect("old")->table("inmei_member_card")->where("openid",$param['openid'])->where("shop_id",$shopId)->find();
+            print_r($old);
+            if (empty($old)) $old = null;
+            $shop = (new SaasShop)->where("shop_id",$shopId)->findOrEmpty();
+            if ($shop->isEmpty()) $shop = null;
+            return success("ok",compact("shop",'old','openId'));
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
 
     /**
      * 首页信息
@@ -39,7 +127,9 @@ class Home extends Base
                 "shop.require"  => "",
                 "print.require" => "",
                 "code.require"  => "",
-                "card.default"  => ""
+                "card.default"  => "",
+                "oshop.default"  => "",
+                "oid.default"  => ""
             ],$request->method());
             if (!is_array($param)) return error($param);
             $userinfo = (new Crypt([
@@ -50,8 +140,16 @@ class Home extends Base
             $map = ['is_deleted' => 0, 'openid' => $userinfo['openid']];
             $user = (new SaasUserOpen)->where($map)->findOrEmpty();
             if ($user->isEmpty()) {
-                $user->insertGetId(['openid' => $userinfo['openid'],"create_ip" => $request->getRealIp(),"nickname" => "微信用户","headimg" => "https://inmei-print.oss-cn-guangzhou.aliyuncs.com/logo.png"]);
+                $user->insertGetId(['openid' => $userinfo['openid'],'oid' => ($param['oid'] == 'undefined'?'':$param['oid']),'update_at' => getDateFull(),"create_ip" => $request->getRealIp(),"nickname" => "微信用户","headimg" => "https://inmei-print.oss-cn-guangzhou.aliyuncs.com/logo.png"]);
                 $user = (new SaasUserOpen)->where($map)->findOrEmpty();
+            } else {
+                if (!empty($param['oid']) && empty($user['oid'])) {
+                    if ($param['oid'] !== 'undefined') {
+                        $user->oid = ($param['oid'] == 'undefined'?'':$param['oid']);
+                        $user->update_at = getDateFull();
+                        $user->save();
+                    }
+                }
             }
             $userAuth = get_object_vars(AuthMode::guard("member")->login($user->toArray()));
             $shopData = [];

+ 92 - 66
app/controller/api/Notify.php

@@ -3,6 +3,7 @@
 namespace app\controller\api;
 
 use app\extra\basic\Base;
+use app\extra\jhfPay\Utils;
 use app\model\saas\SaasCart;
 use app\model\saas\SaasOrder;
 use app\model\saas\SaasOrderDetail;
@@ -11,6 +12,7 @@ use app\model\saas\SaasShopLog;
 use app\model\saas\SaasUser;
 use app\model\saas\SaasUserBuy;
 use app\model\saas\SaasUserLog;
+use app\model\system\SystemUserMoney;
 use LinFly\Annotation\Route\Controller;
 use LinFly\Annotation\Route\Route;
 use support\Request;
@@ -23,6 +25,31 @@ use WeChat\Contracts\Tools;
 class Notify extends Base
 {
 
+
+    /**
+     * 提现银行卡回调
+     */
+    #[Route(path: "withdraw",methods: "post")]
+    public function notifyWithdraw(Request $request): Response
+    {
+        try {
+            echo getDateFull() . "===>提现银行卡异步返回\n";
+            $data = $this->jhfReturn($request->all(),"withdraw.succeeded");
+            if (empty($data)) return error("提现失败");
+            if ($data['status'] == "succeeded") // 成功了
+            {
+                $day = date("Y-m-d",strtotime("-1 day"));
+                $todayMoney = (new SystemUserMoney)->where("member_id",$data['member_id'])->where("day",$day)->select();
+                if ($todayMoney->count() > 0) {
+                    $state = (new SystemUserMoney)->where("member_id",$data['member_id'])->where("day",$day)->update(['status' => 1,"withdraw_id" => $data['withdraw_id']]);
+                }
+            }
+            return success("ok");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
     /**
      * 退款回调
      * @param Request $request
@@ -31,32 +58,35 @@ class Notify extends Base
     public function notifyRefund(Request $request)
     {
         try {
-            echo getDateFull() . "===>退款申请返回\n";
-            $payResp = $request->rawBody();
-            $wechat = new \WePay\Refund($this->getWxConfig());
-            if (empty($payResp)) return $wechat->getNotifySuccessReply();
-            $data = $wechat->getNotify($payResp);
-            if (empty($data)) return $wechat->getNotifySuccessReply();
-            $orderSn = explode("-",$data['result']['out_refund_no']);
-            $order = (new SaasOrder)->where("order_sn",$orderSn[0])->with(['shop' => function($query){
+            $data = $this->jhfReturn($request->all(),"refund.succeeded");
+            if (empty($data)) return error("支付失败");
+//            $payResp = $request->rawBody();
+//            $wechat = new \WePay\Refund($this->getWxConfig());
+//            if (empty($payResp)) return $wechat->getNotifySuccessReply();
+//            $data = $wechat->getNotify($payResp);
+//            if (empty($data)) return $wechat->getNotifySuccessReply();
+//            $orderSn = explode("-",$data['attach']);
+            $order = (new SaasOrder)->where("order_sn",$data['attach'])->with(['shop' => function($query){
                 $query->field('shop_id,shop_name,shop_address');
             }])->findOrEmpty();
-            if ($order->isEmpty()) return $wechat->getNotifySuccessReply();
-            if ($order['status'] <> 4) return $wechat->getNotifySuccessReply();
+            if ($order->isEmpty()) return $this->getNotifySuccessReply();
+            if ($order['status'] <> 4) return $this->getNotifySuccessReply();
             $order->status = 6;
+            $order->refund_at = getDateFull();
             $order->save();
             (new SaasOrderDetail)->where("order_sn",$order['order_sn'])->update(['status' => 6]);
+            $orderMoney = (($order['money'] > $order['discount']) ? $order['discount'] : $order['money']);
             // 商家账户额度退款
             $shop = (new SaasShop)->where("shop_id",$order['shop_id'])->with(['wx' => function($query){
                 $query->field("shop_id,openid,is_msg");
             }])->findOrEmpty();
             if ($shop->isEmpty()) return $this->getNotifySuccessReply();
-            $shop->balance = Db::raw("balance-".$order['discount']);
-            $shop->total_balance = Db::raw("total_balance-".$order['discount']);
+            $shop->balance = $shop['balance'] - $orderMoney;
+            $shop->total_balance = $shop['total_balance'] - $orderMoney;
             $shop->save();
             (new SaasShopLog)->insertGetId([
                 "shop_id"   => $order['shop_id'],
-                "money"     => $order['discount'],
+                "money"     => $orderMoney,
                 "balance"   => $shop->balance,
                 "remark"    => "订单退款【{$order['order_sn']}】",
                 "type"      => 2
@@ -96,24 +126,26 @@ class Notify extends Base
     public function notifyWx(Request $request)
     {
         try {
-            echo getDateFull()."===>支付返回\n";
-            $payResp = $request->rawBody();
-            $data = $this->payReturn($payResp);
+            $data = $this->jhfReturn($request->all());
+            if (empty($data)) return error("支付失败");
+//            $payResp = $request->rawBody();
+//            $data = $this->payReturn($payResp);
             $order = (new SaasOrder)->where("order_sn",$data['attach'])->with(['shop' => function($query){
                 $query->field('shop_id,shop_name,shop_address');
             }])->findOrEmpty();
-            if ($order->isEmpty()) return $this->getNotifySuccessReply();
-            if ($order['status'] <> 0) return $this->getNotifySuccessReply(); // 已支付或者是其他状态
+            if ($order->isEmpty()) return "success";
+            if ($order['status'] <> 0) return "success"; // 已支付或者是其他状态
             $order->status = 1;
             $order->pay_at = getDateFull();
             $order->transaction_id = $data['transaction_id']??'';
+            $order->payment_id = $data['payment_id']??'';
             $order->notify_status = 1;
             $order->pay_type = 1;
             $order->save();
             $shop = (new SaasShop)->where("shop_id",$order['shop_id'])->with(['wx' => function($query){
                 $query->field("shop_id,openid,is_msg");
             }])->findOrEmpty();
-            if ($shop->isEmpty()) return $this->getNotifySuccessReply();
+            if ($shop->isEmpty()) return "success";
             $shop->balance = Db::raw("balance+".$order['money']);
             $shop->total_balance = Db::raw("total_balance+".$order['money']);
             $shop->save();
@@ -123,7 +155,7 @@ class Notify extends Base
                 "balance"   => $shop->balance,
                 "remark"    => "新订单【{$order['order_sn']}】"
             ]);
-            events("create-order",['shop' => $order['shop_id'],'openid' => $order['openid'],"order" => $order['order_sn']]);
+            events("create-order",['shop' => $order['shop_id'],'print' => $order['print_id'],'openid' => $order['openid'],"order" => $order['order_sn']]);
             // 推送消息-公众号
             if (!empty($shop['wx'])) {
                 $obj = \We::WeChatTemplate([
@@ -149,9 +181,9 @@ class Notify extends Base
                 }
             }
 
-            return $this->getNotifySuccessReply();
+            return "success";
         } catch (\Throwable $throwable) {
-            return error($throwable->getMessage());
+            return "success";
         }
     }
 
@@ -164,11 +196,11 @@ class Notify extends Base
     {
         try {
             echo getDateFull()."===>充值并支付支付返回\n";
-            $payResp = $request->rawBody();
-            $data = $this->payReturn($payResp);
+            $data = $this->jhfReturn($request->all());
+            if (empty($data)) return error("支付失败");
             $orderBuy = (new SaasUserBuy)->where("order_sn",$data['attach'])->with(['orders'])->findOrEmpty();
-            if ($orderBuy->isEmpty()) return $this->getNotifySuccessReply();
-            if ($orderBuy['status'] <> 0) return $this->getNotifySuccessReply(); // 已支付或者是其他状态
+            if ($orderBuy->isEmpty()) return "success";
+            if ($orderBuy['status'] <> 0) return "success"; // 已支付或者是其他状态
             $orderMoney = 0;
             $logData[0] = [
                 "openid"    => $orderBuy['openid'],
@@ -196,12 +228,13 @@ class Notify extends Base
             };
             $orderBuy->status = 1;
             $orderBuy->pay_at = getDateFull();
+            $orderBuy->payment_id = $data['payment_id']??'';
             $orderBuy->transaction_id = $data['transaction_id']??'';
             $orderBuy->save();
             $shop = (new SaasShop)->where("shop_id",$orderBuy['shop_id'])->with(['wx' => function($query){
                 $query->field("shop_id,openid,is_msg");
             }])->findOrEmpty();
-            if ($shop->isEmpty()) return $this->getNotifySuccessReply();
+            if ($shop->isEmpty()) return "success";
             // 开通vip账户
             $card = (new SaasUser)->where("card_no",$orderBuy['card_no'])->findOrEmpty();
             $balanceMoney = $orderBuy['total_money'] - $orderMoney;
@@ -223,15 +256,15 @@ class Notify extends Base
             if (!empty($orderBuy['orders']))
             {
                 $order = (new SaasOrder)->where("order_sn",$orderBuy['order_sn'])->findOrEmpty();
-                if ($order->isEmpty()) return $this->getNotifySuccessReply();
-                if ($order['status'] <> 0) return $this->getNotifySuccessReply(); // 已支付或者是其他状态
+                if ($order->isEmpty()) return "success";
+                if ($order['status'] <> 0) return "success"; // 已支付或者是其他状态
                 $order->status = 1;
                 $order->pay_at = getDateFull();
                 $order->transaction_id = $data['transaction_id']??'';
                 $order->notify_status = 1;
                 $order->pay_type = 2;
                 $order->save();
-                events("create-order",['shop' => $order['shop_id'],'openid' => $order['openid'],"order" => $order['order_sn']]);
+                events("create-order",['shop' => $order['shop_id'],'print' => $order['print_id'],'openid' => $order['openid'],"order" => $order['order_sn']]);
             }
             $shop->balance = Db::raw("balance+".$orderBuy['money']);
             $shop->total_balance = Db::raw("total_balance+".$orderBuy['money']);
@@ -269,7 +302,7 @@ class Notify extends Base
                 }
             }
             (new SaasUserLog)->insertAll($logData);
-            return $this->getNotifySuccessReply();
+            return "success";
         } catch (\Throwable $throwable) {
             return error($throwable->getMessage());
         }
@@ -283,13 +316,37 @@ class Notify extends Base
     {
         try {
             echo getDateFull()."===>会员卡支付返回\n";
-            $payResp = $request->rawBody();
-            $data = $this->payReturn($payResp);
+            $data = $this->jhfReturn($request->all());
+            if (empty($data)) return error("支付失败");
+
         } catch (\Throwable $throwable) {
             return error($throwable->getMessage());
         }
     }
 
+
+    /**
+     * 第三方支付返回
+     * @param array $respData
+     * @return array
+     */
+    protected function jhfReturn(array $respData = [],string $return = "payment.succeeded"): array
+    {
+        if ($respData['type'] <> $return) return [];
+        $resCipher = Utils::aes_decrypt($respData['resCipher'], sConf("wechat.jhf_aeskey"));
+        $data = json_decode($resCipher,true);
+        if ($return == "withdraw.succeeded") {
+            print_r($data);
+        }
+        $data['attach'] = $data['order_no'];
+        $data['transaction_id'] = $data['out_trans_id']??'';
+        $data['payment_id'] = $data['payment_id']??'';
+        $data['withdraw_id'] = $data['withdraw_id']??'';
+        $data['member_id'] = $data['member_id']??'';
+        $data['status'] = $data['status']??'';
+        return $data;
+    }
+
     protected function payReturn($payResp)
     {
         $wechat = new \WeChat\Pay($this->getWxConfig());
@@ -306,7 +363,8 @@ class Notify extends Base
      */
     protected function getNotifySuccessReply(): string
     {
-        return Tools::arr2xml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']);
+//        return Tools::arr2xml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']);
+        return json(['return_code' => 'SUCCESS', 'return_msg' => 'OK']);
     }
 
 
@@ -328,36 +386,4 @@ class Notify extends Base
     }
 
 
-
-    #[Route(path: "test",methods: "get")]
-    public function testData()
-    {
-        try {
-            $data = $this->_valid([
-                "openid.default" => "otdgy3fYZ0JBHW7mbnxmdKTQJRUU",
-                "shop.default"  => "326945519114632",
-                "order.default"  => "202604022640934408406"
-            ]);
-            $cart = (new SaasCart)->where("shop_id",$data['shop'])->where("openid",$data['openid'])->order("create_at desc")->select();
-            if ($cart->isEmpty()) return success('ok',['cart' => []]);
-            $orderDetail = [];
-            foreach ($cart->toArray() as $key=>$val)
-            {
-                unset($val['id']);
-                $orderDetail[$key] = $val;
-                $orderDetail[$key]['order_sn'] = $data['order'];
-                if ($val['extension'] == "pdf") {
-                    $orderDetail[$key]['status'] = 1;
-                } else {
-                    $orderDetail[$key]['status'] = 0;
-                }
-            }
-            print_r($orderDetail);
-            $state = (new SaasOrderDetail)->strict(false)->insertAll($orderDetail);
-            return success("ok");
-        } catch (\Throwable $throwable) {
-            return error($throwable->getMessage());
-        }
-    }
-
 }

+ 43 - 13
app/controller/api/Order.php

@@ -3,6 +3,7 @@
 namespace app\controller\api;
 
 use app\extra\basic\Base;
+use app\extra\jhfPay\Pay;
 use app\extra\tools\CodeExtend;
 use app\middleware\WxMiddleware;
 use app\model\saas\SaasCart;
@@ -29,9 +30,12 @@ class Order extends Base
 
     protected array $types = [
         '1_1_1' => ['name' => '彩色-单面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
+        '1_1_2' => ['name' => '彩色-单面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
         '1_2_1' => ['name' => '彩色-双面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
         '2_1_1' => ['name' => '黑白-单面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
         '2_2_1' => ['name' => '黑白-双面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
+        '2_1_2' => ['name' => '黑白-双面', 'amount' => 0, 'quantity' => 0,'discount' => 0],
+
     ];
 
     protected array $color = ["1" => "彩色", "2" => "黑白"];
@@ -121,7 +125,7 @@ class Order extends Base
                 "gift.default"      => 0
             ],$request->method());
             if (!is_array($param)) return error($param);
-            $cart = (new SaasCart)->where("shop_id",$param['shop'])->where("openid",$request->user['openid'])->order("create_at desc")->select();
+            $cart = (new SaasCart)->where("shop_id",$param['shop'])->where("print_id",$param['print'])->where("openid",$request->user['openid'])->order("create_at desc")->select();
             if ($cart->isEmpty()) return error('请重新下单进行支付');
             $totalAmount = $totalDiscount = 0;
             foreach ($cart as $k=>$v){
@@ -156,6 +160,7 @@ class Order extends Base
                 "money"         => $totalAmount,
                 "discount"      => $totalDiscount==0?$totalAmount:$totalDiscount, // 跟原价相等无折扣
                 "print_name"    => $param['printName'],
+                "print_id"      => $param['print'],
                 "package"       => $param['express'],
                 "package_sn"    => date('md')."-".sprintf("%02d",($totalDay+1)),
                 "extra_money"   => 0,
@@ -163,14 +168,14 @@ class Order extends Base
             ];
             $shop = (new SaasShop)->where("shop_id",$param['shop'])->findOrEmpty();
             if ($param['pay'] == 2) { // 会员卡支付
-                $card = (new SaasUser)->where("openid",$request->user['openid'])->where("shop_id",$param['shop'])->field("balance")->findOrEmpty();
+                $card = (new SaasUser)->where("openid",$request->user['openid'])->where("shop_id",$param['shop'])->findOrEmpty();
                 $payMoney = $totalDiscount > 0 ? $totalDiscount : $totalAmount;
-                if ($payMoney > $card['balance']) {
+                if ($payMoney >= $card['balance']) {
                     return error("卡内余额不足~");
                 }
                 // 直接支付
-                $card->balance = Db::raw("balance - {$payMoney}");
-                $card->total_consume = Db::raw("total_consume + {$payMoney}");
+                $card->balance = ($card['balance'] - $payMoney);
+                $card->total_consume = ($card['total_consume'] + $payMoney);
                 $card->save();
                 $orderData['pay_type'] = 2;
                 $orderData['status'] = 1;
@@ -183,13 +188,13 @@ class Order extends Base
                     "money"     => $payMoney,
                     "card_no"   => strtoupper(md5($request->user['openid'].$param['shop'])),
                     "type"      => 1,
-                    "balance"   => $card->balance,
+                    "balance"   => $card['balance'] - $payMoney,
                 ]);
-                events("create-order",['shop' => $param['shop'],'openid' => $request->user['openid'],"order" => $orderSn]);
+                events("create-order",['shop' => $param['shop'],'print' => $param['print'],'openid' => $request->user['openid'],"order" => $orderSn]);
                 return success("支付成功",['type' => 2,'data' => []]);
             }
             $options = [
-                'body'              => $shop['shop_name']."-打印",
+                'body'              => "{$shop['shop_name']}-{$param['printName']}",
                 'out_trade_no'      => $orderSn."-".$orderData['package_sn'],
                 "attach"            => $orderSn,
                 'total_fee'         => $orderData['money'],
@@ -198,6 +203,9 @@ class Order extends Base
                 'spbill_create_ip'  => $request->getRealIp(),
                 "notify_url"        => "https://panel.huiyinduo.cn/notify/wx"
             ];
+            if ($orderData['money'] <= 0) {
+                return error("数据变动请重新上传再下单");
+            }
             if ($param['pay'] == 3 && !empty($param['card'])) { // 开通会员卡并充值
                 $buyCard = json_decode($param['card'],true);
                 (new SaasUserBuy)->insertGetId([
@@ -213,14 +221,36 @@ class Order extends Base
                 $options['notify_url'] = "https://panel.huiyinduo.cn/notify/recharge";
             }
             (new SaasOrder)->insertGetId($orderData);
-            $wechat = new \WeChat\Pay($this->getWxConfig());
-            // 生成预支付码
-            $result = $wechat->createOrder($options);
-            if (!isset($result['return_code']) || $result['return_code'] !== "SUCCESS") {
+            $param_data["order_no"] = $orderSn;
+            $param_data["app_id"] = sConf("wechat.jhf_appid");
+            $param_data["pay_channel"] = "wx_lite";
+            $param_data["pay_amt"] = format_money($options['total_fee'] / 100 , 2);
+            $param_data["goods_title"] = $options['body'];
+            $param_data["device_info"] = array("device_ip" => $request->getRealIp());
+            $param_data['notify_url'] = $options['notify_url'];
+            $param_data["expend"] = [
+                "wx_app_id" => sConf("wechat.mini_appid"),
+                "open_id" => $request->user['openid']
+            ];
+            $respJhf = (new Pay)->config([
+                "appid"  => sConf("wechat.jhf_appid"),
+                "mch_id" => sConf("wechat.jhf_mch_id"),
+                "aeskey" => sConf("wechat.jhf_aeskey"),
+                "pubkey" => sConf("wechat.jhf_pubkey"),
+                "prikey" => sConf("wechat.jhf_prikey"),
+            ])->createPay($param_data);
+
+//            $wechat = new \WeChat\Pay($this->getWxConfig());
+//            // 生成预支付码
+//            echo getDateFull()."生成支付二维码\n";
+//            print_r($options);
+//            $result = $wechat->createOrder($options);
+            if (isset($respJhf['code'])) {
                 return error("发起支付失败");
             }
             // 创建JSAPI参数签名
-            $resp = $wechat->createParamsForJsApi($result['prepay_id']);
+            $resp = json_decode($respJhf['expend']['pay_info'],true);
+            $resp['timestamp'] = $resp['timeStamp'];
             return success("ok",['type' => 1,"data" => $resp]);
         } catch (\Throwable $throwable) {
             echo $throwable->getLine()."\n";

+ 263 - 0
app/controller/api/Test.php

@@ -0,0 +1,263 @@
+<?php
+
+namespace app\controller\api;
+
+
+use app\extra\basic\Base;
+use app\extra\jhfPay\Pay;
+use app\extra\tools\CodeExtend;
+use app\model\saas\SaasCart;
+use app\model\saas\SaasOrderDetail;
+use app\model\saas\SaasUser;
+use app\model\saas\SaasUserOpen;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\think\Db;
+use Webman\Push\Api;
+use Webman\RedisQueue\Redis;
+
+#[Controller(prefix: "/api/test")]
+class Test extends Base
+{
+
+
+    protected array $whiteShop = [
+        "10888572278827"    => "10888572278827", // 惠印多马村店
+        "10888566286841"    => "327545130062964", // 中站小学店
+        "10888590458813"    => "326945519114632", // 幸小印美店
+        "10888645729727"    => "326844775971796", // 焦作和平街店
+    ];
+
+
+    #[Route(path: "pay",methods: "get")]
+    public function testPay(Request $request)
+    {
+        try {
+            $respJhf = (new Pay)->config([
+                "appid"  => sConf("wechat.jhf_appid"),
+                "mch_id" => sConf("wechat.jhf_mch_id"),
+                "aeskey" => sConf("wechat.jhf_aeskey"),
+                "pubkey" => sConf("wechat.jhf_pubkey"),
+                "prikey" => sConf("wechat.jhf_prikey"),
+            ]);
+            $jsResp = $respJhf->createBalancePay([
+                "app_id" => sConf("wechat.jhf_appid"),
+                "order_no"  => CodeExtend::uniqidDate(18),
+                "member_id" => "300919564646719394",
+                "pay_amt"   => "77.73",
+                "description"   => "补发"
+            ]);
+//            $jsResp = $respJhf->createBalanceWithdraw([
+//                "app_id" => sConf("wechat.jhf_appid"),
+//                "order_no"  => "9D18B0EEB5E70548308CB77274755601",
+//                "member_id" => "974707386910290082",
+//                "remark"    => "2026-05-01-结算",
+//                "notify_url"    => "https://panel.huiyinduo.cn/notify/withdraw",
+//            ]);
+            print_r($jsResp);
+
+//            $param_data = array();
+//            $param_data["order_no"] = date("YmdHis").CodeExtend::random(8);
+//            $param_data["app_id"] = "app_6666000195741955";
+//            $param_data["pay_channel"] = "wx_lite";
+//            $param_data["pay_amt"] = "0.10";
+//            $param_data["goods_title"] = "智惠印打印";
+//            $param_data["device_info"] = array("device_ip" => '192.168.0.1');
+//            $param_data["expend"] = [
+//                "wx_app_id" => "wxbeeb0dcd7336612f",
+//                "open_id" => "omf322AlD9wFjm5Ucix9uKmRXd4I"
+//            ]; //expend参数根据支付渠道变化
+//            $param_data["notify_url"] = "https://panel.huiyinduo.cn/notify/wx"; //接收支付结果异步通知地址
+//            $resp = (new Pay)->config([
+//                "appid" => "app_6666000195741955",
+//                "mch_id" => "1120260422467651",
+//                "aeskey" => "54784acef7d89b50edff9d42832f124c",
+//                "pubkey" => "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOKBBFXirWIJth+SNJCY4mhbATbL60sKV66bRixHMVz8vpBqONio9X6A+Pm9LNutBe+hLpI1BMmFJk3Mb1/QEcklWptRGgHqIrBxR4b19qc/2/pSxyqlpaifYJFZhOg2+OcQ/fqpAmhNXN5uc1pcYvbvWTam0j+6+nBNQeAAku5QIDAQAB",
+//                "prikey" => "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOK1bos5glGB/tq/iyDaCLwAgVUwTLRFltG7j5QjS+CEBu/0t6wW3z2UfacMxWGRgkPQBWCDNSOlYV9gVOoxtfj3TS0zWK1t9OCO+7PaSI9BqkYumlylpRq09gplul6C7HXgrWx3WxuzbLXSaJ6wAeFr0ZY1KqPdhZ5OB7m0zNeXAgMBAAECgYAr1COg6udU1qrso2dEXKKfpgFa9NF/cIyt03L4krJSn3Ov5EG2FV3nS9PW/dMS/8yNS6Qeen9Feu2OQNNpy16AfDiy5cFn5MvLm/PHb7syBMaakuKogEDYKnoo/CPbI8kTYymZA0tDnad1BkeY3lb0Bx6ou4oRZ+TYc0QOJCqwUQJBAPE7QRhpR4VygkIcPORyIR7PhBKkxmMz+ZHt+E/ep7o5KpdDfBP95gUs7591BZDnyh1EBrkBh9G8WQVBCeV9Qk0CQQDwlpEsLyw9DdtgKTneoRfw+bnLhIfvkshoxfhWf3i4iCcIqAvZZbfInY2W37vBJrtpfirHOhpuLjV7R2fqSctzAkBCuuJx70WSm69+vDL3+r5AuKTPR3d9n7YM8Sg8Z9o8AG5Qs6FSIm0Lx3dtw8BLamMVn2jAqrS4hwKVGn2zVugNAkEAyjFgFEgY377TjX9YOTgdzNGzSc06CSfM8fDfAqLirAMQ+v9v5ebMi/eNVSz2uB97Be+YuBKmv85p+A9Mz+Pw7QJAb4fn1d5Tw3B7gku3XANH3RTfvNWBeXBpxpAlnaxJU39pVh4lh9UGoaWGoEKEdufHSJhJMUtaSpI2morVAfo0Ow==",
+//            ])->createPay($param_data);
+            return success("ok");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    #[Route(path: "s2",methods: "get")]
+    public function sendWs2()
+    {
+        try {
+            Redis::send("push-print",['type' => 'order' , 'order' => '202604205907640644072']);
+            return success("ok");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "s",methods: "get")]
+    public function sendWs()
+    {
+        try {
+            // return error('err');
+            $api = new Api('http://127.0.0.1:3232',
+                config('plugin.webman.push.app.app_key'),
+                config('plugin.webman.push.app.app_secret'));
+            $param = $this->_valid([
+                "color.default" => "color",
+                "size.default" => "A4",
+                "key.default" => "004356708646",
+                "shop.default" => "10888543912042",
+                "name.default" => "qiantai",
+                "id.default" => "29131",
+                "number.default" => 1,
+                "range.default" => "1",
+                "duplex.default"    => "simplex"
+            ]);
+            $printJobData = (new SaasOrderDetail)->where("paper_size",'A4')->where("extension",'docx')->limit(1)->order("create_at",'desc')->select();
+//            $printJobData = (new SaasOrderDetail)->where("id",$param['id'])->select();
+//            $printJobData = (new SaasOrderDetail)->whereDay("create_at","yesterday")->limit(1)->select();
+            if ($printJobData->isEmpty()) return error('err');
+            $printData = [];
+            foreach ($printJobData as $key=>$printJob) {
+                $range = "1";
+                // if ($printJob['end_page'] > $printJob['start_page']) {
+                //     $range = $printJob['start_page']."-".$printJob['end_page'];
+                // }
+                // if ($printJob['end_page'] == $printJob['start_page']) {
+                //     $range = (string) $printJob['end_page'];
+                // }
+                $color_mode = ($printJob['color']==1?'color':'monochrome');
+                $duplex = "simplex";
+                if ($printJob['paper_size'] == 'A4' && $printJob['duplex'] == 2) {
+                    $duplex = 'duplexlong'; // duplexlong
+                }
+                if ($printJob['paper_size'] == 'A3' && $printJob['duplex'] == 2) {
+                    $duplex = 'duplexlong'; // duplexshort
+                }
+                $printData[$key] = [
+                    "printerName"   => $param['name'],
+                    "copies"        => $param['number'],
+                    "landscape"     => false,
+                    "paperSize"     => $param['size'],
+                    "pageRange"     => $param['range'],
+                    "duplex"        => $param['duplex'],
+                    "colorMode"     => $param['color'],
+                    "jobId"         => $printJob['id'],
+                    "dpi"           => 300,
+                    "action"        => 'create',
+                    "scaleMode"     => 'fit',
+                    "remoteUrl"     => "https://zhy-1355132020.cos.ap-guangzhou.myqcloud.com/".$printJob['path'],
+//                    "remoteUrl"     => "https://zhy-1355132020.cos.ap-guangzhou.myqcloud.com/pdfs/20260501/12456-001_ac02a21c-3ad6-436f-9c13-ec415d26b21d.pdf", //竖版
+//                    "remoteUrl"     => "https://zhy-1355132020.cos.ap-guangzhou.myqcloud.com/ossmini/20260502/1777694521564_sopo6f.pdf", // 横版
+//                    "remoteUrl"     => "https://yunenv.oss-cn-shenzhen.aliyuncs.com/a3shu.pdf", // 横版
+//                    "remoteUrl"     => "https://inmei-print.oss-cn-guangzhou.aliyuncs.com/wxjiaoyi.pdf",
+//                    "remoteUrl"  => "https://zhy-1355132020.cos.ap-guangzhou.myqcloud.com/ossmini/20260504/1777898266812_rcyph1.docx",
+//                    "customFileName" => $printJob['order_sn']."_".$printJob['id'],
+                    "customFileName" => time()."_".$printJob['id'],
+                ];
+            }
+            $api->trigger("client-{$param['key']}-{$param['shop']}",'message',[
+                "type"  => "print",
+                "data"  => $printData
+            ]);
+//             if (!$printJob->isEmpty()) {
+//                 $range = "1";
+//                 // if ($printJob['end_page'] > $printJob['start_page']) {
+//                 //     $range = $printJob['start_page']."-".$printJob['end_page'];
+//                 // }
+//                 // if ($printJob['end_page'] == $printJob['start_page']) {
+//                 //     $range = (string) $printJob['end_page'];
+//                 // }
+//                 $color_mode = ($printJob['color']==1?'color':'monochrome');
+//                 $duplex = "simplex";
+//                 if ($printJob['paper_size'] == 'A4' && $printJob['duplex'] == 2) {
+//                     $duplex = 'duplexlong'; // duplexlong
+//                 }
+//                 if ($printJob['paper_size'] == 'A3' && $printJob['duplex'] == 2) {
+//                     $duplex = 'duplexshort'; // duplexshort
+//                 }
+//                 $api->trigger("client-005571234125-10888543912042",'message',[
+//                     "type"  => "print",
+//                     "data"  => [
+//                         [
+//                             "printerName"   => "750",
+//                             "copies"        => "1",
+//                             // "copies"        => (string)$printJob['number'],
+//                             "landscape"     => false,
+//                             "paperSize"     => "A4",
+//                             // "paperSize"     => $printJob['paper_size'],
+//                             "pageRange"     => $range,
+//                             "duplex"        => "duplexlong",
+//                             // "duplex"        => $duplex,
+//                             "monochrome"    => $color_mode,
+//                             "colorMode"     => "monochrome",// monochrome color
+//                             "jobId"         => $printJob['id']."-".time().rand(1,99),
+//                             "action"        => 'create',
+//                             "scaleMode"     => 'fit',
+//                             "remoteUrl"     => "https://cdn-zhy.huiyinduo.cn/".$printJob['path'],
+//                             "customFileName" => $printJob['order_sn']."_".$printJob['id'],    
+//                         ]
+// //                    "exe"       => ['-print-to',$printJob['print_name'],'-print-settings',"{$range},{$printJob['number']}x,{$color_mode},$duplex,fit,paper={$printJob['paper_size']}",'-silent'],
+//                     ]
+//                 ]);
+//             }
+            return success("ok",compact('printData'));
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    #[Route(path: "t",methods: "get")]
+    public function testData(Request $request): \support\Response
+    {
+        try {
+//            $data = (new SaasUserOpen)->whereNotNull("oid")->whereDay("update_at",'yesterday')->select();
+            $data = (new SaasUserOpen)->whereNotNull("oid")->whereDay("update_at")->select();
+            if ($data->isEmpty()) return error("ok");
+            $userData = [];
+            foreach ($data as $key=>$val) {
+                if (!empty($val['oid'])) {
+                    $old = Db::connect("old")->table("inmei_member_card")->where("openid",$val['oid'])->find();
+                    if (!empty($old)) {
+                        $shopId = $old['shop_id'];
+                        if (isset($this->whiteShop[$old['shop_id']])) {
+                            $shopId = $this->whiteShop[$old['shop_id']];
+                        }
+                        $card = (new SaasUser)->where("openid",$val['openid'])->findOrEmpty();
+                        if ($card->isEmpty()) {
+                            $userData[$key] = [
+                                "openid"        => $val['openid'],
+                                "shop_id"       => $shopId,
+                                "card_no"       => strtoupper(md5($val['openid'].$shopId)),
+                                "balance"       => $old['balance'],
+                                "total_balance" => $old['total_balance'],
+                                "remark"        => "迁移,原ID:{$old['openid']}",
+                            ];
+                        }
+                    }
+                }
+            }
+            if (!empty($userData)) {
+                (new SaasUser)->insertAll($userData);
+            }
+            return success("ok",array_values($userData));
+        } catch (\Throwable $throwable) {
+            echo $throwable->getLine()."\n";
+            return error($throwable->getMessage());
+        }
+    }
+    
+    #[Route(path: "tid",methods: "get")]
+    public function testDataId(Request $request): \support\Response
+    {
+        try {
+            $id = $request->get("id",16992);
+            Redis::send("push-print",['type' => 'id' , 'order' => $id]);
+            return success("ok");
+        } catch (\Throwable $throwable) {
+            echo $throwable->getLine()."\n";
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 8 - 3
app/controller/api/User.php

@@ -5,6 +5,7 @@ namespace app\controller\api;
 use app\extra\basic\Base;
 use app\middleware\WxMiddleware;
 use app\model\saas\SaasPrice;
+use app\model\saas\SaasUser;
 use app\model\saas\SaasUserOpen;
 use LinFly\Annotation\Route\Controller;
 use LinFly\Annotation\Route\Route;
@@ -33,11 +34,15 @@ class User extends Base
             if (!is_array($param)) return error($param);
             $user = $request->user;
             if (empty($user)) return errorTrans("empty.data");
-            $member = (new SaasUserOpen)->where("openid",$user['openid'])->with(['vip' => function($query) use($param){
-                $query->where("shop_id",$param['shop'])->field("openid,shop_id,ROUND(balance/100,2) as f_balance,ROUND(total_balance/100,2) as f_total_balance,ROUND(total_consume/100,2) as f_total_consume,card_no");
-            }])->field("openid,headimg,nickname")->append(["coupon"])->withAttr(['coupon' => function(){
+
+            $member = (new SaasUserOpen)->where("openid",$user['openid'])->field("openid,headimg,nickname")->append(["coupon"])->withAttr(['coupon' => function(){
                 return 0;
             }])->findOrEmpty();
+            $memberUser = (new SaasUser)->where(['shop_id' => $param['shop'],'openid' => $user['openid']])->field("openid,ROUND(balance/100,2) as f_balance,ROUND(total_balance/100,2) as f_total_balance,ROUND(total_consume/100,2) as f_total_consume,card_no")->findOrEmpty();
+            $member['vip'] = [
+                "f_balance"         => $memberUser['f_balance']??'0.00',
+                "f_total_consume"   => $memberUser['f_total_balance']??'0.00',
+            ];
             return success("ok",$member->toArray());
         } catch (\Throwable $th) {
             return error($th->getMessage());

+ 0 - 1
app/controller/common/Login.php

@@ -3,7 +3,6 @@
 namespace app\controller\common;
 
 use app\extra\basic\Base;
-use app\extra\service\basic\SmsService;
 use app\middleware\AuthMiddleware;
 use app\model\saas\SaasShop;
 use app\model\system\SystemUser;

+ 1 - 0
app/controller/exe/Login.php

@@ -36,6 +36,7 @@ class Login extends Base
             if (!is_array($param)) return error($param);
             $map = ["is_deleted" => 0,"username" => $param['username']];
             [$state,$msg,$user] = $this->checkLogin($map,2,$param);
+            print_r($msg);
             if (!$state) return error($msg);
             return successTrans("success.login",$user);
         } catch (\Throwable $throwable) {

+ 154 - 113
app/controller/exe/Prints.php

@@ -1,114 +1,155 @@
-<?php
-
-namespace app\controller\exe;
-
-use app\extra\basic\Base;
-use app\middleware\ExeMiddleware;
-use app\model\saas\SaasOrder;
-use app\model\saas\SaasOrderDetail;
-use app\model\saas\SaasPrintClient;
-use app\model\saas\SaasShop;
-use LinFly\Annotation\Route\Controller;
-use LinFly\Annotation\Route\Middleware;
-use LinFly\Annotation\Route\Route;
-use support\Request;
-use support\Response;
-
-
-#[Controller(prefix: "/exe/prints"),Middleware(ExeMiddleware::class)]
-class Prints extends Base
-{
-
-    protected array $noNeedLogin = ["getPrintData"];
-
-    /**
-     * 回传打印机信息
-     * @param Request $request
-     * @return Response
-     */
-    #[Route(path: "set",methods: "post")]
-    public function getPrintList(Request $request): Response
-    {
-        try {
-            $param = $request->post();
-            if (empty($param)) return errorTrans("empty.data");
-            $shopId = $request->uuid;
-            $mode = (new SaasPrintClient);
-            $printIn = [];
-            foreach ($param as $key=>$val)
-            {
-                $param[$key] = $val;
-                $printId = strToUniqueNumberV4(trim($val['name']));
-                $param[$key]['key'] = "{$printId}-{$shopId}";
-                $param[$key]['userKey'] = $printId;
-                $printData = $mode->where("code",$printId)->where("shop_id",$shopId)->findOrEmpty();
-                if ($printData->isEmpty()) {
-                    $printIn[$key] = [
-                        "shop_id"       => $shopId,
-                        "code"          => $printId,
-                        "name"          => $val['name'],
-                        "ipaddress"     => $val['ipAddress'],
-                        "print_status"  => $val['status'],
-                    ];
-                }
-            }
-            if (!empty($printIn)) {
-                $mode->insertAll(array_values($printIn));
-            }
-            (new SaasShop)->where("shop_id",$shopId)->update(['line_time' => getDateFull()]);
-            return success("ok",$param);
-        } catch (\Throwable $throwable) {
-            return error($throwable->getMessage());
-        }
-    }
-
-    /**
-     * 打印回传
-     * @param Request $request
-     * @return Response
-     */
-    #[Route(path: "status",methods: "post")]
-    public function getPrintData(Request $request): Response
-    {
-        try {
-            $param = $this->_valid([
-                "data.require"      => trans("empty.data"),
-                "param.require"     => trans("empty.data"),
-            ],$request->method());
-            if (!is_array($param)) return error($param);
-            $orderId = $param['param']['jobId'];
-            $order = (new SaasOrderDetail)->where("id",$orderId)->with(['orders'])->findOrEmpty();
-            $total = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 'ta' => 0];
-            foreach ((new SaasOrderDetail)->where("id",$orderId)->field('create_at,status,count(1) total')->group('status,create_at')->cursor() as $vo)
-            {
-                [$total["t{$vo['status']}"] += $vo['total'], $total['ta'] += $vo['total']];
-            }
-            $outPut = $param['data']['outputArray'];
-            $logData = [];
-            foreach ($outPut as $key=>$val) {
-                if ($val['type'] == 'stdout') {
-                    $logData[$key] = [
-                        "order_sn"  => $param['param']['customFileName'],
-                        "msg"       => delNt($val['data'])
-                    ];
-                } else if ($val['type'] == "exit") {
-                    if ($val['code'] == 0) {
-                        $order->status = 3;
-                        $order->save();
-                        if (($total['t3']+1) == $total['ta']) {
-                            (new SaasOrder)->where("order_sn",$order['order_sn'])->update(['status' => 3,'print_at' => getDateFull()]);
-                        }
-                    }
-                }
-            }
-            $logData = array_values($logData);
-            if (!empty($logData)) {
-
-            }
-            return success("ok");
-        } catch (\Throwable $throwable) {
-            return error($throwable->getMessage());
-        }
-    }
-
+<?php
+
+namespace app\controller\exe;
+
+use app\extra\basic\Base;
+use app\middleware\ExeMiddleware;
+use app\model\saas\SaasOrder;
+use app\model\saas\SaasOrderDetail;
+use app\model\saas\SaasPrintClient;
+use app\model\saas\SaasShop;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+
+#[Controller(prefix: "/exe/prints"),Middleware(ExeMiddleware::class)]
+class Prints extends Base
+{
+
+    protected array $noNeedLogin = ["getPrintData"];
+
+    /**
+     * 回传打印机信息
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "set",methods: "post")]
+    public function getPrintList(Request $request): Response
+    {
+        try {
+            $param = $request->post();
+            if (empty($param)) return errorTrans("empty.data");
+            $shopId = $request->uuid;
+            $mode = (new SaasPrintClient);
+            $printIn = [];
+            foreach ($param as $key=>$val)
+            {
+                $param[$key] = $val;
+                $printId = strToUniqueNumberV4(trim($val['name']));
+                $param[$key]['key'] = "{$printId}-{$shopId}";
+                $param[$key]['userKey'] = $printId;
+                $printData = $mode->where("code",$printId)->where("shop_id",$shopId)->findOrEmpty();
+                if ($printData->isEmpty()) {
+                    $printIn[$key] = [
+                        "shop_id"       => $shopId,
+                        "code"          => $printId,
+                        "name"          => $val['name'],
+                        "ipaddress"     => $val['ipAddress'],
+                        "print_status"  => $val['status'],
+                    ];
+                } else {
+                    $printData->ipaddress = $val['ipAddress'];
+                    $printData->print_status = $val['status'];
+                    $printData->save();
+                }
+            }
+            if (!empty($printIn)) {
+                $mode->insertAll(array_values($printIn));
+            }
+            (new SaasShop)->where("shop_id",$shopId)->update(['line_time' => getDateFull()]);
+            return success("ok",$param);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * 打印识别
+     * @return Response
+     */
+    #[Route(path: "error",methods: "post")]
+    public function getPrintError(Request $request): Response
+    {
+        try {
+            echo getDateFull()."===打印出错\n";
+            print_r($request->all());
+            $param = $this->_valid([
+                "data.require"      => trans("empty.data"),
+                "param.require"     => trans("empty.data"),
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            echo getDateFull()."===打印出错\n";
+            print_r($param);
+            $orderId = $param['param']['jobId'];
+            
+            return success("ok");
+        }  catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * 打印回传
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "status",methods: "post")]
+    public function getPrintData(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "data.require"      => trans("empty.data"),
+                "param.require"     => trans("empty.data"),
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            $orderId = $param['param']['jobId'];
+            $order = (new SaasOrderDetail)->where("id",$orderId)->with(['orders'])->findOrEmpty();
+            $total = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 'ta' => 0];
+            foreach ((new SaasOrderDetail)->where("id",$orderId)->field('create_at,status,count(1) total')->group('status,create_at')->cursor() as $vo)
+            {
+                [$total["t{$vo['status']}"] += $vo['total'], $total['ta'] += $vo['total']];
+            }
+            if (isset($param['data']['success'])) {
+                if ($param['data']['success']) { // 打印成功
+                    $order->status = 3;
+                    $order->save();
+                    if (($total['t3']+1) == $total['ta']) {
+                        (new SaasOrder)->where("order_sn",$order['order_sn'])->update(['status' => 3,'print_at' => getDateFull()]);
+                    }
+                } else { // 打印失败
+                    
+                }
+            }
+            
+            // $outPut = $param['data']['outputArray'];
+            // $logData = [];
+            // foreach ($outPut as $key=>$val) {
+            //     if ($val['type'] == 'stdout') {
+            //         $logData[$key] = [
+            //             "order_sn"  => $param['param']['customFileName'],
+            //             "msg"       => delNt($val['data'])
+            //         ];
+            //     } else if ($val['type'] == "exit") {
+            //         if ($val['code'] == 0) {
+            //             $order->status = 3;
+            //             $order->save();
+            //             if (($total['t3']+1) == $total['ta']) {
+            //                 (new SaasOrder)->where("order_sn",$order['order_sn'])->update(['status' => 3,'print_at' => getDateFull()]);
+            //             }
+            //         }
+            //     }
+            // }
+            // $logData = array_values($logData);
+            // if (!empty($logData)) {
+
+            // }
+            return success("ok");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
 }

+ 122 - 0
app/controller/wap/Card.php

@@ -0,0 +1,122 @@
+<?php
+
+namespace app\controller\wap;
+
+use app\extra\basic\Base;
+use app\extra\jhfPay\Pay;
+use app\extra\service\system\MoneyLogService;
+use app\extra\tools\CodeExtend;
+use app\middleware\WxMiddleware;
+use app\model\system\SystemUserBankcard;
+use app\validate\saas\CardValidate;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+
+#[Controller(prefix: "/wap/card"),Middleware(WxMiddleware::class)]
+class Card extends Base
+{
+
+    #[Inject]
+    protected SystemUserBankcard $model;
+
+    #[Inject]
+    protected MoneyLogService $service;
+
+    #[Inject]
+    protected CardValidate $validate;
+
+    #[Route(path: "list",methods: "get")]
+    public function getCardList(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            $data = $this->model->where("agent_id",$request->user['shop_id'])->findOrEmpty();
+            if ($data->isEmpty())
+            {
+                $data = null;
+            } else {
+                $data['logo'] = "https://apimg.alipay.com/combo.png?d=cashier&t={$data['bank']}";
+                $data['user_name_hide'] = hide_str($data['user_name'],1);
+                $data['card_id_hide'] = hide_str($data['card_id'],3,10);
+            }
+            $city = json_decode(file_get_contents(public_path()."/city.json"),true);
+            if (empty($param['shop'])) {
+                $param['shop'] = $request->user['shop_id'];
+            }
+            $list = $this->service->getList($param);
+            $list = pageFormat($list);
+            return success("ok",compact("data",'city','list'));
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    /**
+     * 新增银行卡
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "save",methods: "post")]
+    public function setUserCard(Request $request): Response
+    {
+        try {
+            $param = $request->post();
+            $card = $this->model->where("agent_id",$request->user['shop_id'])->findOrEmpty();
+            if (!$card->isEmpty()) return error("请勿重复添加");
+            if ($param['type'] == 2) {
+                $memberId = $this->model->where("member_id",$param['data_id'])->field("member_id,user_name,cert_id,tel_no,bank,card_id,valid_from,valid_until,card_prov_code,card_area_code,card_city_name")->findOrEmpty();
+                if ($memberId->isEmpty()) return errorTrans("empty.data");
+                $memberId['agent_id'] = $request->user['shop_id'];
+                $state = $card->strict(false)->insertGetId($memberId->toArray());
+            } else {
+                if (empty($param['card_city'])) return error("请选择开户地区");
+                $param['card_prov_code'] = $param['card_city'][0];
+                $param['card_area_code'] = $param['card_city'][1];
+                $param['card_city'] = json_encode($param['card_city']);
+                $resp = $this->validate->scene("add")->check($param);
+                if (!$resp) return error($this->validate->getError());
+                $bankAli = json_decode(file_get_contents("https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardNo={$param['card_id']}&cardBinCheck=true"),true);
+                if (!$bankAli['validated']) return error("银行卡号有误");
+                if ($param['valid_from'] >= $param['valid_until']) return error("身份证有效期有误");
+                $param['bank'] = $bankAli['bank'];
+                $param['agent_id'] = $request->user['shop_id'];
+                $param['member_id'] = CodeExtend::random(18);
+                $respJhf = (new Pay)->config([
+                    "appid"  => sConf("wechat.jhf_appid"),
+                    "mch_id" => sConf("wechat.jhf_mch_id"),
+                    "aeskey" => sConf("wechat.jhf_aeskey"),
+                    "pubkey" => sConf("wechat.jhf_pubkey"),
+                    "prikey" => sConf("wechat.jhf_prikey"),
+                ])->createMember([
+                    "app_id"    => sConf("wechat.jhf_appid"),
+                    "member_id" => $param['member_id'],
+                    "member_type"   => "01",
+                    "member_p"  => [
+                        "user_name"     => $param['user_name'],
+                        "cert_id"       => $param['cert_id'],
+                        "tel_no"        => $param['tel_no'],
+                        "card_id"       => $param['card_id'],
+                        "valid_from"    => $param['valid_from'],
+                        "valid_until"   => ($param['valid_until']=='20990101')?"99991231":$param['valid_until'],
+                        "card_prov_code"    => $param['card_prov_code'],
+                        "card_area_code"    => $param['card_area_code']
+                    ]
+                ]);
+                if (isset($respJhf['code'])) return error($respJhf['error_msg']);
+                // 推送绑定
+                $state = $card->strict(false)->insertGetId($param);
+            }
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 200 - 0
app/controller/wap/Detail.php

@@ -0,0 +1,200 @@
+<?php
+
+namespace app\controller\wap;
+
+
+use app\extra\basic\Base;
+use app\extra\service\saas\ShopLogService;
+use app\extra\weMini\Link;
+use app\middleware\WxMiddleware;
+use app\model\saas\SaasShop;
+use app\model\system\SystemUser;
+use app\model\system\SystemUserOpen;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use Shopwwi\WebmanAuth\Auth;
+use support\Request;
+use support\Response;
+
+#[Controller(prefix: "/wap/shop"),Middleware(WxMiddleware::class)]
+class Detail extends Base
+{
+
+
+    #[Inject]
+    protected SaasShop $model;
+
+    #[Inject]
+    protected ShopLogService $service;
+
+
+    #[Route(path: "detail",methods: "get")]
+    public function getDetail(Request $request): Response
+    {
+        try {
+            $shop = $this->model->where("shop_id",$request->user['shop_id'])->field("shop_name,shop_mobile,start_at,end_at,vip_end,shop_status,shop_notice,line_time,status,shop_address")->withAttr(['vip_end'  => function($query,$data) {
+                return date("Y-m-d",strtotime($data['vip_end']));
+            }])->findOrEmpty();
+            if ($shop->isEmpty()) return errorTrans("empty.data");
+            return success("ok",$shop->toArray());
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    #[Route(path: "save",methods: "post")]
+    public function setData(Request $request): Response
+    {
+        try {
+            $param = $request->post();
+            $shop = $this->model->where("shop_id",$request->user['shop_id'])->findOrEmpty();
+            if ($shop->isEmpty()) return errorTrans("empty.data");
+            $state = $shop->save($param);
+            if (!$state) return errorTrans("error.data");
+            // 更新店铺名称到账号信息
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "msg",methods: ['post','get'])]
+    public function getMsgType(Request $request): Response
+    {
+        try {
+            if ($request->method() == "GET") {
+                $msg = (new SystemUserOpen)->where("id",$request->user['id'])->value("is_msg");
+                return success("ok",compact('msg'));
+            }
+            $param = $this->_valid([
+                "msg.require"   => trans("empty.require")
+            ],"post");
+            if (!is_array($param)) return error($param);
+            $msg = (new SystemUserOpen)->where("id",$request->user['id'])->findOrEmpty();
+            if ($msg->isEmpty()) return errorTrans("error.data");
+            $msg->is_msg = $param['msg'];
+            $state = $msg->save();
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    /**
+     * 绑定新门店
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "bind",methods: "post")]
+    public function bindUser(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "username.require"  => trans("empty.user"),
+                "password.require"  => trans("empty.passwd"),
+            ],"post");
+            if (!is_array($param)) return error($param);
+            $map = ["is_deleted" => 0,"username" => $param['username']];
+            $user = (new SystemUser)->where($map)->with(['account'])->findOrEmpty();
+            if ($user->isEmpty()) return errorTrans("error.user-empty");
+            if ($user['status'] <> 1) return errorTrans("error.user-status");
+            if (md5($param['password'].$user['salt']) <> $user['password']) return errorTrans("error.passwd");
+            $userOpen = (new SystemUserOpen)->where(['openid' => $request->user['openid'],'shop_id' => $user['agent_id']])->findOrEmpty();
+            if (!$userOpen->isEmpty()) return error("请勿重复绑定");
+            $state = $userOpen->insertGetId([
+                "uid"       => $user['id'],
+                "openid"    => $request->user['openid'],
+                "shop_id"   => $user['agent_id'],
+                "shop_name" => $user['account']['shop_name']??'',
+                'is_msg'    => 1
+            ]);
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    /**
+     * 解绑
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "unbind",methods: "post")]
+    public function unbindUser(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.require"  => trans("empty.data")
+            ],"post");
+            if (!is_array($param)) return error($param);
+            $user = (new SystemUserOpen)->where("id",$param['id'])->findOrEmpty();
+            if ($user->isEmpty()) return errorTrans("empty.data");
+            if ($user['openid'] <> $request->user['openid']) return errorTrans("error.request");
+            $state = $user->delete();
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "change",methods: ['post','get'])]
+    public function changeAuth(Request $request): Response
+    {
+        try {
+            if ($request->method() == "GET") {
+                $data = (new SystemUserOpen)->where("openid",$request->user['openid'])->select();
+                $shop = $request->user['shop_id'];
+                return success("ok",compact("data","shop"));
+            }
+            $param = $this->_valid([
+                "id.require"    => trans("empty.data")
+            ],'post');
+            if (!is_array($param)) return error($param);
+            $user = (new SystemUserOpen)->where("id",$param['id'])->findOrEmpty();
+            if ($user->isEmpty()) return errorTrans("empty.data");
+            if ($user['openid'] <> $request->user['openid']) return errorTrans("error.request");
+            $loginUser = (new SystemUser)->where(['id' => $user['uid']])->with(['account' => function($query){
+                $query->field("shop_id,shop_name");
+            }])->findOrEmpty();
+            return success("ok",['shop_name' => $loginUser['account']['shop_name'],'token' => get_object_vars((new Auth)->guard("mp")->login($user->toArray()))]); // 正常登陆进入
+
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    /**
+     * 钱包记录
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "walletLog",methods: "get")]
+    public function walletLog(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            $param['shop'] = $request->user['shop_id'];
+            $list = $this->service->getList($param);
+            return successTrans("success.data",pageFormat($list),200);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 104 - 0
app/controller/wap/Discount.php

@@ -0,0 +1,104 @@
+<?php
+
+namespace app\controller\wap;
+
+
+use app\extra\basic\Base;
+use app\extra\service\saas\DisCountService;
+use app\middleware\WxMiddleware;
+use app\model\saas\SaasDiscount;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+#[Controller(prefix: "/wap/discount"),Middleware(WxMiddleware::class)]
+class Discount extends Base
+{
+
+
+
+    #[Inject]
+    protected SaasDiscount $model;
+
+    #[Inject]
+    protected DisCountService $service;
+
+    #[Route(path: "list",methods: "get")]
+    public function getPriceList(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            $param['shop'] = $request->user['shop_id'];
+            $list = $this->service->getList($param);
+            return successTrans("success.data",pageFormat($list),200);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "save",methods: "post")]
+    public function savePrice(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.default"        => 0,
+                "color.require"     => trans("empty.require"),
+                "duplex.require"    => trans("empty.require"),
+                "number.require"    => trans("empty.require"),
+                "rate.require"      => trans("empty.require"),
+                "shop_id.default"   => $request->user['agent_id'],
+            ],"post");
+            if (!is_array($param)) return error($param);
+            $param['keys'] = $param['color']."_".$param['duplex']."_1";
+            $price = $this->model->where("id",$param["id"])->findOrEmpty();
+            if ($price->isEmpty())
+            {
+                $priceType = $this->model->where([
+                    "shop_id"   => $param["shop_id"],
+                    "keys"      => $param["keys"]
+                ])->findOrEmpty();
+                if (!$priceType->isEmpty()) return errorTrans("error.exist");
+                $state = $price->insertGetId($param);
+            } else {
+                $state = $price->save($param);
+            }
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "del",methods: "post")]
+    public function delPrice(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.require"    => trans("empty.require"),
+                "type.default"  => ""
+            ],"post");
+            if (!is_array($param)) return error($param);
+            if ($param["type"] == "batch") {
+                $state = $this->model->whereIn("id",$param["id"])->delete();
+            } else {
+                $price = $this->model->where("id",$param["id"])->findOrEmpty();
+                if ($price->isEmpty()) return errorTrans("empty.data");
+                if ($price['shop_id'] <> $request->user["agent_id"]) return errorTrans("empty.data");
+                $state = $price->delete();
+            }
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 175 - 47
app/controller/wap/Login.php

@@ -5,6 +5,7 @@ namespace app\controller\wap;
 use app\extra\basic\Base;
 use app\extra\wechat\WechatService;
 use app\middleware\WxMiddleware;
+use app\model\system\SystemConfig;
 use app\model\system\SystemUser;
 use app\model\system\SystemUserOpen;
 use LinFly\Annotation\Route\Controller;
@@ -19,32 +20,80 @@ use support\Response;
 class Login extends Base
 {
 
-    protected array $noNeedLogin = ["checkLogin"];
+    protected array $noNeedLogin = ["checkLogin","bindLogin"];
 
 
     #[Route(path: "check",methods: "post")]
     public function checkLogin(Request $request): Response
     {
         try {
-            $source = $request->header("referer");
-//            $userInfo = WechatService::getWebOauthInfo($source,1,false);
-            $userInfo['openid'] = 123;
+            $postData = $request->all();
+            if (empty($postData)) {
+                $source = $request->header("referer")."login";
+            } else {
+                $source = $request->header("referer")."login?".http_build_query($postData);
+            }
+            $userInfo = WechatService::getWebOauthInfo($source,1,false);
+//            $userInfo['openid'] = "otdgy3S5ZzPUD5DYOsHacX5KhZ9I";
+//            $userInfo['openid'] = "omf322AlD9wFjm5Ucix9uKmRXd4I";
             if (empty($userInfo['openid'])) {
                 return success("ok",['url' => $userInfo['url'],'type' => 1]); // 跳转
             } else {
+                $service = (new SystemConfig)->where("type","service")->column("value","name");
                 $user = (new SystemUserOpen)->where(['openid' => $userInfo['openid']])->findOrEmpty();
                 if ($user->isEmpty())
                 {
-                    return success("ok",['url' => '','type' => 2,'token' => ['access_token' => $userInfo['openid']]]); // 跳到绑定用户
+                    return success("ok",['url' => '','shop_name' => "",'type' => 2,'service' => $service,'token' => ['access_token' => $userInfo['openid']]]); // 跳到绑定用户
                 }
-                $loginUser = (new SystemUser)->where(['id' => $user['uid']])->findOrEmpty();
-                return success("ok",['url' => '','type' => 3,'menu' => $this->getMenu(),'token' => get_object_vars((new Auth)->guard("admin")->login($loginUser))]); // 正常登陆进入
+                $loginUser = (new SystemUser)->where(['id' => $user['uid']])->with(['account' => function($query){
+                    $query->field("shop_id,shop_name");
+                }])->findOrEmpty();
+                return success("ok",['url' => '','shop_name' => $loginUser['account']['shop_name'],'type' => 3,'service' => $service,'menu' => $this->getMenu(),'token' => get_object_vars((new Auth)->guard("mp")->login($user->toArray()))]); // 正常登陆进入
             }
         } catch (\Throwable $th) {
             return error($th->getMessage());
         }
     }
 
+    /**
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "bind",methods: "post")]
+    public function bindLogin(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "username.require"  => trans("empty.user"),
+                "password.require"  => trans("empty.passwd"),
+                "open.require"      => trans("empty.passwd"),
+            ],"post");
+            if (!is_array($param)) return error($param);
+            $map = ["is_deleted" => 0,"username" => $param['username']];
+            $user = (new SystemUser)->where($map)->with(['account' => function($query){
+                $query->field("shop_id,shop_name");
+            }])->findOrEmpty();
+            if ($user->isEmpty()) return errorTrans("error.user-empty");
+            if ($user['status'] <> 1) return errorTrans("error.user-status");
+            if (md5($param['password'].$user['salt']) <> $user['password']) return errorTrans("error.passwd");
+            $userOpen = (new SystemUserOpen)->where(['openid' => $param['open'],'shop_id' => $user['agent_id']])->findOrEmpty();
+            if (!$userOpen->isEmpty()) return error("请勿重复授权");
+            $openData = [
+                "uid"       => $user['id'],
+                "openid"    => $param['open'],
+                "shop_id"   => $user['agent_id'],
+                "shop_name" => $user['account']['shop_name']??'',
+                'is_msg'    => 1
+            ];
+            $state = $userOpen->insertGetId($openData);
+            $openData['id'] = $state;
+            if (!$state) return errorTrans("error.data");
+            return success("ok",['shop_name' => $user['account']['shop_name'],'menu' => $this->getMenu(),'token' => get_object_vars((new Auth)->guard("mp")->login($openData))]); // 正常登陆进入
+        } catch (\Throwable $th) {
+            return error($th->getMessage());
+        }
+    }
+
     /**
      * @return array[]
      */
@@ -58,8 +107,10 @@ class Login extends Base
                 "meta"  => [
                     "icon"  => "",
                     "title" => "总揽",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 1
             ],[
                 "path"  => "/shop/detail",
                 "name"  => "shop/detail",
@@ -67,98 +118,175 @@ class Login extends Base
                 "meta"  => [
                     "icon"  => "",
                     "title" => "店铺详情",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 1
+            ],[
+                "path"  => "/shop/change",
+                "name"  => "shop/change",
+                "component" => "shop/change",
+                "meta"  => [
+                    "icon"  => "",
+                    "title" => "切换店铺",
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 1
             ],[
                 "path"  => "/print",
                 "name"  => "print/index",
                 "component" => "print/index",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-printer",
                     "title" => "打印机",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
             ],[
                 "path"  => "/print/price",
                 "name"  => "print/price",
                 "component" => "print/price",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-shopping-bag",
                     "title" => "价格设置",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
             ],[
                 "path"  => "/print/discount",
                 "name"  => "print/discount",
                 "component" => "print/discount",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-price-tag",
                     "title" => "折扣设置",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
             ],[
                 "path"  => "/order",
                 "name"  => "order/index",
                 "component" => "order/index",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-tickets",
                     "title" => "实时订单",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
+            ],[
+                "path"  => "/order/detail",
+                "name"  => "order/detail",
+                "component" => "order/detail",
+                "meta"  => [
+                    "icon"  => "el-icon-tickets",
+                    "title" => "订单详情",
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 1
             ],[
                 "path"  => "/turnover/index",
                 "name"  => "turnover/index",
                 "component" => "turnover/index",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-money",
                     "title" => "营业额",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
             ],[
                 "path"  => "/member/index",
                 "name"  => "member/index",
                 "component" => "member/index",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-credit-card",
                     "title" => "会员卡",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
+            ],[
+                "path"  => "/member/list",
+                "name"  => "member/list",
+                "component" => "member/list",
+                "meta"  => [
+                    "icon"  => "el-icon-credit-card",
+                    "title" => "会员列表",
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 1
+            ],[
+                "path"  => "/member/recharge",
+                "name"  => "member/recharge",
+                "component" => "member/recharge",
+                "meta"  => [
+                    "icon"  => "el-icon-credit-card",
+                    "title" => "充值套餐",
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 1
             ],[
                 "path"  => "/wallet/index",
                 "name"  => "wallet/index",
                 "component" => "wallet/index",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-wallet",
                     "title" => "我的钱包",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
+            ],[
+                "path"  => "/wallet/log",
+                "name"  => "wallet/log",
+                "component" => "wallet/log",
+                "meta"  => [
+                    "icon"  => "el-icon-wallet",
+                    "title" => "钱包记录",
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 1
+            ],[
+                "path"  => "/wallet/card",
+                "name"  => "wallet/card",
+                "component" => "wallet/card",
+                "meta"  => [
+                    "icon"  => "el-icon-postcard",
+                    "title" => "结算卡",
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
             ],[
                 "path"  => "/shop/bind",
                 "name"  => "shop/bind",
                 "component" => "shop/bind",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-connection",
                     "title" => "绑定门店",
-                    "type"  => "menu"
-                ]
-            ],[
-                "path"  => "/shop/change",
-                "name"  => "shop/change",
-                "component" => "shop/change",
-                "meta"  => [
-                    "icon"  => "",
-                    "title" => "切换门店",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
             ],[
                 "path"  => "/shop/msg",
                 "name"  => "shop/msg",
                 "component" => "shop/msg",
                 "meta"  => [
-                    "icon"  => "",
+                    "icon"  => "el-icon-notification",
                     "title" => "消息开关",
-                    "type"  => "menu"
-                ]
+                    "type"  => "menu",
+                    "color" => "#333"
+                ],
+                "hide"  => 0
             ]
         ];
     }

+ 109 - 0
app/controller/wap/Member.php

@@ -0,0 +1,109 @@
+<?php
+
+namespace app\controller\wap;
+
+use app\extra\basic\Base;
+use app\extra\service\saas\MemberService;
+use app\middleware\WxMiddleware;
+use app\model\saas\SaasCombo;
+use app\model\saas\SaasShop;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+
+#[Controller(prefix: "/wap/member"),Middleware(WxMiddleware::class)]
+class Member extends Base
+{
+
+    #[Inject]
+    protected MemberService $service;
+
+    #[Route(path: "total",methods: "get")]
+    public function getMemberTotal(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            $param['shop'] = $request->user['shop_id'];
+            $data = $this->service->getMpTotal($param);
+            return success("ok",$data);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "list",methods: "get")]
+    public function getMemberList(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            if (empty($param['shop'])) {
+                $param['shop'] = $request->user['shop_id'];
+            }
+            $list = $this->service->getList($param);
+            return successTrans("success.data",pageFormat($list),200);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "combo",methods: "get")]
+    public function getComboDetail(Request $request): Response
+    {
+        try {
+            $shop = (new SaasShop)->where("shop_id",$request->user['shop_id'])->findOrEmpty();
+            $combo = (new SaasCombo)->where("type",2)->where("status",1)->field("id,name,ROUND(money/100,2) as money,ROUND(old_money/100,2) as old_money,is_first")->select();
+            $param = [
+                "state"     => $shop['user_card'], // 1 默认 2 自定义 3关闭
+                "system"    => $combo,
+                "shop"      => empty($shop['user_card_price']) ? $combo :$shop['user_card_price']
+            ];
+            return success("ok",$param);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "combo",methods: "post")]
+    public function setCombo(Request $request): Response
+    {
+        try {
+            $param = $request->post();
+            $shop = (new SaasShop)->where("shop_id",$request->user['shop_id'])->findOrEmpty();
+            $shop->user_card = $param['state'];
+            if (isset($param['shop'])) {
+                $shop->user_card_price = json_encode($param['shop']);
+            }
+            $state = $shop->save();
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    /**
+     * 钱包余额
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "wallet",methods: "get")]
+    public function getWalletData(Request $request): Response
+    {
+        try {
+            $detail = (new SaasShop)->where("shop_id",$request->user['shop_id'])->column("balance,total_balance");
+            if (!isset($detail[0])) return errorTrans("empty.data");
+            return success("ok",$detail[0]);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 140 - 0
app/controller/wap/Order.php

@@ -0,0 +1,140 @@
+<?php
+
+namespace app\controller\wap;
+
+use app\extra\basic\Base;
+use app\extra\jhfPay\Pay;
+use app\extra\service\saas\OrderService;
+use app\middleware\WxMiddleware;
+use app\model\saas\SaasOrder;
+use app\model\saas\SaasOrderDetail;
+use app\model\saas\SaasUser;
+use app\model\saas\SaasUserLog;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+#[Controller(prefix: "/wap/order"),Middleware(WxMiddleware::class)]
+class Order extends Base
+{
+
+    #[Inject]
+    protected OrderService $service;
+
+    #[Inject]
+    protected SaasOrder $model;
+
+    #[Route(path: "list",methods: "get")]
+    public function getPriceList(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            if (empty($param['shop'])) {
+                $param['shop'] = $request->user['shop_id'];
+            }
+            if (!empty($param['order'])) {
+                $orderType = explode("-",$param['order']);
+                if (count($orderType)>1) { // 搜索取件码
+                    $param['sn'] = $param['order'];
+                } else {
+                    $param['orderid'] = $param['order'];
+                }
+            }
+            if (empty($param['status'])) $param['statusGt'] = 1;
+            $list = $this->service->getList($param);
+            return successTrans("success.data",pageFormat($list),200);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "detail",methods: "get")]
+    public function getOrderDetail(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.require"    => trans("empty.require")
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            $detail = $this->model->where("id",$param['id'])->with(['detail','shop' => function($query){
+                $query->field("shop_id,shop_name");
+            }])->findOrEmpty();
+            if ($detail->isEmpty()) return errorTrans("error.data");
+            if ($detail['shop_id'] <> $request->user['shop_id']) return errorTrans("error.data");
+            $startTime = strtotime(date('Y-m-d 00:00:00'));
+            $endTime = strtotime(date('Y-m-d 23:59:58'));
+            $orderTime = strtotime($detail['create_at']);
+            $detail['today'] = (($orderTime > $startTime && $orderTime < $endTime) ? 1 : 0);
+            return success("ok",$detail->toArray());
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "refund",methods: "post")]
+    public function orderRefund(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.require"    => trans("empty.require")
+            ],$request->method());
+            if (!is_array($param)) return error($param);
+            $detail = $this->model->where("id",$param['id'])->findOrEmpty();
+            if ($detail->isEmpty()) return errorTrans("error.data");
+            if ($detail['shop_id'] <> $request->user['shop_id']) return errorTrans("error.data");
+            if (!in_array($detail['status'],[1,2,3])) return error("该订单不支持退款");
+            if ($detail['pay_type'] == 2) {
+                $card = (new SaasUser)->where("openid",$detail['openid'])->where("shop_id",$detail['shop_id'])->findOrEmpty();
+                if ($card->isEmpty()) return error("会员卡失效");
+                $card->balance = ($card['balance'] + $detail['money']);
+                $card->total_consume = ($card['total_consume'] - $detail['money']);
+                $card->save();
+                (new SaasUserLog)->insertGetId([
+                    "openid"    => $detail['openid'],
+                    "shop_id"   => $detail['shop_id'],
+                    "order_sn"  => $detail['order_sn'],
+                    "money"     => $detail['money'],
+                    "card_no"   => strtoupper(md5($detail['openid'].$detail['shop_id'])),
+                    "type"      => 2,
+                    "remark"    => "订单退款",
+                    "balance"   => $card['balance'] - $detail['money'],
+                ]);
+                $detail->status = 6;
+                $detail->refund_at = getDateFull();
+                $detail->save();
+                (new SaasOrderDetail)->where("order_sn",$detail['order_sn'])->update(['status' => 6]);
+                return success("退款成功");
+            }
+            $respJhf = (new Pay)->config([
+                "appid"  => sConf("wechat.jhf_appid"),
+                "mch_id" => sConf("wechat.jhf_mch_id"),
+                "aeskey" => sConf("wechat.jhf_aeskey"),
+                "pubkey" => sConf("wechat.jhf_pubkey"),
+                "prikey" => sConf("wechat.jhf_prikey"),
+            ])->createRefund([
+                "payment_id"    => $detail['payment_id'],
+                "order_no"      => $detail['order_sn'],
+                "notify_url"    => "https://panel.huiyinduo.cn/notify/refund",
+                "refund_amt"    => format_money($detail['money']/100,2)
+            ]);
+            if (isset($respJhf['code'])) {
+                return error("发起退款失败");
+            }
+            $detail->status = 4;
+            $state = $detail->save();
+            if (!$state) return errorTrans("error.data");
+            (new SaasOrderDetail)->where("order_sn",$detail['order_sn'])->update(['status' => 4]);
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+
+}

+ 108 - 0
app/controller/wap/Price.php

@@ -0,0 +1,108 @@
+<?php
+
+namespace app\controller\wap;
+
+use app\extra\basic\Base;
+use app\extra\service\saas\PriceService;
+use app\middleware\WxMiddleware;
+use app\model\saas\SaasPrice;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+
+#[Controller(prefix: "/wap/price"),Middleware(WxMiddleware::class)]
+class Price extends Base
+{
+
+
+
+    #[Inject]
+    protected SaasPrice $model;
+
+    #[Inject]
+    protected PriceService $service;
+
+    #[Route(path: "list",methods: "get")]
+    public function getPriceList(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            $param['shop'] = $request->user['shop_id'];
+            $list = $this->service->getList($param);
+            return successTrans("success.data",pageFormat($list),200);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "save",methods: "post")]
+    public function savePrice(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.default"        => 0,
+                "type.require"      => trans("empty.require"),
+                "color.require"     => trans("empty.require"),
+                "paper_size.require"=> trans("empty.require"),
+                "duplex.require"    => trans("empty.require"),
+                "price.require"     => trans("empty.require"),
+                "shop_id.default"   => $request->user['agent_id'],
+            ],"post");
+            if (!is_array($param)) return error($param);
+            $param['price'] = $param['price'] * 100;
+            $price = $this->model->where("id",$param["id"])->findOrEmpty();
+            if ($price->isEmpty())
+            {
+                $priceType = $this->model->where([
+                    "type"      => $param["type"],
+                    "shop_id"   => $param["shop_id"],
+                    "color"     => $param["color"],
+                    "duplex"    => $param["duplex"],
+                    "paper_size"=> $param["paper_size"],
+                ])->findOrEmpty();
+                if (!$priceType->isEmpty()) return errorTrans("error.exist");
+                $state = $price->insertGetId($param);
+            } else {
+                $state = $price->save($param);
+            }
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[Route(path: "del",methods: "post")]
+    public function delPrice(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.require"    => trans("empty.require"),
+                "type.default"  => ""
+            ],"post");
+            if (!is_array($param)) return error($param);
+            if ($param["type"] == "batch") {
+                $state = $this->model->whereIn("id",$param["id"])->delete();
+            } else {
+                $price = $this->model->where("id",$param["id"])->findOrEmpty();
+                if ($price->isEmpty()) return errorTrans("empty.data");
+                if ($price['shop_id'] <> $request->user["agent_id"]) return errorTrans("empty.data");
+                $state = $price->delete();
+            }
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 102 - 0
app/controller/wap/Prints.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace app\controller\wap;
+
+use app\extra\basic\Base;
+use app\extra\service\saas\PrintService;
+use app\middleware\WxMiddleware;
+use app\model\saas\SaasPrintClient;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+
+#[Controller(prefix: "/wap/prints"),Middleware(WxMiddleware::class)]
+class Prints extends Base
+{
+
+
+    #[Inject]
+    protected PrintService $service;
+
+    #[Inject]
+    protected SaasPrintClient $model;
+
+
+    #[Route(path: "list",methods: "get")]
+    public function getPrintList(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            $param['shop'] = $request->user['shop_id'];
+            $list = $this->service->getList($param);
+            return successTrans("success.data",pageFormat($list),200);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * 保存配置
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "save",methods: "post")]
+    public function savePrint(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "id.require"            => trans(20010),
+                "is_price.require"      => trans(20010),
+                "paper_size.require"    => trans(20010),
+                "color.require"         => trans(20010),
+                "direction.require"     => trans(20010),
+                "duplex.require"        => trans(20010),
+                "package.require"       => trans(20010),
+                "type.require"          => trans(20010)
+            ],"post");
+            if (!is_array($param)) return error($param);
+            $printId = $param['id'];
+            $is_price = $param['is_price'];
+            unset($param['id'],$param['is_price']);
+            $print = (new SaasPrintClient)->where("id",$printId)->findOrEmpty();
+            if ($print->isEmpty()) return errorTrans("empty.data");
+            $print->is_price = $is_price;
+            $print->rule = json_encode($param);
+            $state = $print->save();
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    /**
+     * 保存配置
+     * @param Request $request
+     * @return Response
+     */
+    #[Route(path: "single",methods: "post")]
+    public function savePrintSingle(Request $request): Response
+    {
+        try {
+            $param = $request->post();
+            if (empty($param['id'])) return errorTrans("empty.require");
+            $print = (new SaasPrintClient)->where("id",$param['id'])->findOrEmpty();
+            if ($print->isEmpty()) return errorTrans("empty.data");
+            if (isset($param['status'])) {
+                $param['status'] = $print['status'] == 1 ? 2 : 1;
+            }
+            $state = $print->save($param);
+            if (!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 70 - 0
app/controller/wap/Turnover.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace app\controller\wap;
+
+use app\extra\basic\Base;
+use app\extra\service\saas\OrderService;
+use app\middleware\WxMiddleware;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Route\Controller;
+use LinFly\Annotation\Route\Middleware;
+use LinFly\Annotation\Route\Route;
+use support\Request;
+use support\Response;
+
+
+#[Controller(prefix: "/wap/turnover"),Middleware(WxMiddleware::class)]
+class Turnover extends Base
+{
+
+    #[Inject]
+    protected OrderService $service;
+
+    #[Route(path: "total",methods: "get")]
+    public function getTotal(Request $request): Response
+    {
+        try {
+            $param = $request->get();
+            if (empty($param['shop']))
+            {
+                $param['shop'] = $request->user['shop_id'];
+            }
+            $data = $this->service->getTotalDate($param);
+            $total = [
+                [
+                    "name"  => "小程序订单",
+                    "type"  => "线上支付",
+                    "order" => $data['total']['p1'],
+                    "money" => $data['total']['p1m']
+                ],
+                [
+                    "name"  => "收款码",
+                    "type"  => "线上支付",
+                    "order" => $data['qrcode']['total'],
+                    "money" => $data['qrcode']['money']
+                ],
+                [
+                    "name"  => "会员卡充值",
+                    "type"  => "线上支付",
+                    "order" => $data['card']['total'],
+                    "money" => $data['card']['money']
+                ],
+                [
+                    "name"  => "会员卡支付",
+                    "type"  => "线上支付",
+                    "order" => $data['total']['p2'],
+                    "money" => $data['total']['p2m']
+                ]
+            ];
+            $today = [
+                "money"     => $data['total']['p1m'],
+                "user"      => $data['total']['p2m'],
+                "qrcode"    => $data['qrcode']['money'],
+            ];
+            return success("ok",compact("today","total"));
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 22 - 9
app/event/CreateOrder.php

@@ -3,7 +3,9 @@
 namespace app\event;
 
 use app\model\saas\SaasCart;
+use app\model\saas\SaasOrder;
 use app\model\saas\SaasOrderDetail;
+use Webman\RedisQueue\Redis;
 
 class CreateOrder
 {
@@ -11,24 +13,35 @@ class CreateOrder
     public function createOrder(array $data = [])
     {
         try {
-            $cart = (new SaasCart)->where("shop_id",$data['shop'])->where("openid",$data['openid'])->order("create_at desc")->select();
+            $cart = (new SaasCart)->where("shop_id",$data['shop'])->where("openid",$data['openid'])->where("print_id",$data['print'])->order("create_at desc")->select();
             if ($cart->isEmpty()) return success('ok',['cart' => []]);
+            $order = (new SaasOrder)->where("order_sn",$data['order'])->findOrEmpty();
             $orderDetail = [];
+            $print_name = !empty($order['print_name']) ? explode("-",$order['print_name']) : [];
+            $printNameOut = $print_name[1] ?? '';
+//            echo "订单号:{$data['order']}====打印机名:{$printNameOut}====打印机id:{$order['print_id']}\n";
             foreach ($cart->toArray() as $key=>$val)
             {
                 unset($val['id']);
                 $orderDetail[$key] = $val;
                 $orderDetail[$key]['order_sn'] = $data['order'];
-                if ($val['extension'] == "pdf") {
-                    $orderDetail[$key]['status'] = 1;
-                } else {
-                    $orderDetail[$key]['status'] = 0;
-                }
+                $orderDetail[$key]['print_id'] = $order['print_id']?$order['print_id']:$val['print_id'];
+                $orderDetail[$key]['print_name'] = $print_name[1] ?? $val['print_name'];
+                $orderDetail[$key]['status'] = 1;
+//                if ($val['extension'] == "pdf") {
+//                    $orderDetail[$key]['status'] = 1;
+//                } else {
+//                    $orderDetail[$key]['status'] = 0;
+//                }
             }
             $state = (new SaasOrderDetail)->strict(false)->insertAll($orderDetail);
-            echo "写入订单详情{$data['order']}\n";
-            var_dump($state);
-            events("merge-pdf",$data);
+            events("push-print",['type' => 'order' , 'order' => $data['order']]);
+//            Redis::send("push-print",['type' => 'order' , 'order' => $data['order']]);
+//            events("merge-pdf",$data);
+            if (empty($printNameOut)) {
+                $order->print_name = $cart[0]['print_name']??'';
+                $order->save();
+            }
             (new SaasCart)->where("shop_id",$data['shop'])->where("openid",$data['openid'])->delete();
         } catch (\Throwable $throwable) {
             echo "写入订单详情错误\n";

+ 8 - 2
app/event/MergePdf.php

@@ -3,6 +3,7 @@
 namespace app\event;
 
 use app\model\saas\SaasOrderDetail;
+use Webman\RedisQueue\Redis;
 use yzh52521\EasyHttp\Http;
 
 class MergePdf
@@ -16,8 +17,9 @@ class MergePdf
         foreach ($detail as $key=>$val) {
             if ($val['extension'] !== 'pdf') {
                 $pathUri = [];
-                for ($i = 1; $i <= $val['total_page']; $i ++) {
-                    $pathUri[$i] = "https://".sConf("storage.cos_http_domain")."/".$val['path']."?ci-process=doc-preview&dstType=jpg&imageDpi=300&page={$i}";
+                for ($i = $val['start_page']; $i <= $val['end_page']; $i ++) {
+//                for ($i = 1; $i <= $val['total_page']; $i ++) {
+                    $pathUri[$i] = "https://".sConf("storage.cos_http_domain")."/".$val['path']."?ci-process=doc-preview&dstType=png&imageDpi=300&page={$i}";
                 }
                 $resp = Http::asJson()->withHeaders(['x-api-key' => "your-api-key-here"])->post("http://127.0.0.1:3000/api/images-to-pdf",[
                     "imageUrls" => array_values($pathUri),
@@ -27,8 +29,12 @@ class MergePdf
                         "quality"       => 100
                     ]
                 ])->array();
+                echo getDateFull()."==={$data['order']}===word转码\n";
+                print_r($resp);
                 if ($resp['success']) {
                     (new SaasOrderDetail)->where("id",$val['id'])->update(['path' => $resp['data']['pdfUrl'],'old_path' => $val['path'], 'status' => 1]);
+                    // 推送打印
+                    Redis::send("push-print",['type' => 'id' , 'order' => $val['id']]);
                 }
             }
         }

+ 108 - 0
app/event/PushPrint.php

@@ -0,0 +1,108 @@
+<?php
+
+namespace app\event;
+
+use app\model\saas\SaasOrderDetail;
+use Webman\Push\Api;
+
+class PushPrint
+{
+
+    public function createPrint(array $data = []): bool
+    {
+        try {
+            if (empty($data['order'])) return true;
+            if (empty($data['type'])) return true;
+            $api = new Api('http://127.0.0.1:3232', config('plugin.webman.push.app.app_key'),config('plugin.webman.push.app.app_secret'));
+            $printData = [];
+            if ($data['type'] == 'order') { // 按订单
+                $detail = (new SaasOrderDetail)->where(["order_sn" => $data['order'],'status' => 1])->select();
+                if ($detail->isEmpty()) return true;
+                foreach ($detail as $key=>$printJob) {
+                    $range = Null;
+                    if ($printJob['end_page'] > $printJob['start_page']) {
+                        $range = $printJob['start_page']."-".$printJob['end_page'];
+                    }
+                    if ($printJob['end_page'] == $printJob['start_page']) {
+//                        $range = (string) $printJob['end_page'];
+                        $range = $printJob['start_page']."-".$printJob['end_page'];
+                    }
+                    $color_mode = ($printJob['color']==1?'color':'monochrome');
+                    $duplex = "simplex";
+                    if ($printJob['paper_size'] == 'A4' && $printJob['duplex'] == 2) {
+                        $duplex = 'shortedge'; // duplexlong  longedge
+                    }
+                    if ($printJob['paper_size'] == 'A3' && $printJob['duplex'] == 2) {
+                        $duplex = 'longedge'; // duplexshort  shortedge
+                    }
+                    $printData[$key] = [
+                        "printerName"   => $printJob['print_name'],
+                        "copies"        => $printJob['number'],
+                        "landscape"     => false,
+                        "paperSize"     => $printJob['paper_size'],
+                        "pageRange"     => $range,
+                        "duplex"        => $duplex,
+                        "monochrome"    => $color_mode,
+                        "colorMode"     => $color_mode,
+                        "jobId"         => $printJob['id'],
+                        "dpi"           => 300,
+                        "action"        => 'create',
+                        "scaleMode"     => 'fit',
+                        "remoteUrl"     => "https://cdn-zhy.huiyinduo.cn/".$printJob['path'],
+                        "customFileName" => $printJob['order_sn']."_".$printJob['id'],
+                    ];
+                    (new SaasOrderDetail)->where("id",$printJob['id'])->update(['status' => 2]);
+                }
+                $printKey = "{$detail[0]['print_id']}-{$detail[0]['shop_id']}";
+            } else { // 单笔打印
+                $printJob = (new SaasOrderDetail)->where(["id" => $data['order'],'status' => 1])->findOrEmpty();
+                if ($printJob->isEmpty()) return true;
+                $range = Null;
+                if ($printJob['end_page'] > $printJob['start_page']) {
+                    $range = $printJob['start_page']."-".$printJob['end_page'];
+                }
+                if ($printJob['end_page'] == $printJob['start_page']) {
+                    $range = (string) $printJob['end_page'];
+                }
+                $color_mode = ($printJob['color']==1?'color':'monochrome');
+                $duplex = "simplex";
+                if ($printJob['paper_size'] == 'A4' && $printJob['duplex'] == 2) {
+                    $duplex = 'shortedge'; // duplexlong
+                }
+                if ($printJob['paper_size'] == 'A3' && $printJob['duplex'] == 2) {
+                    $duplex = 'longedge'; // duplexshort
+                }
+                $printData = [
+                    [
+                        "printerName"   => $printJob['print_name'],
+                        "copies"        => $printJob['number'],
+                        "landscape"     => false,
+                        "paperSize"     => $printJob['paper_size'],
+                        "pageRange"     => $range,
+                        "duplex"        => $duplex,
+                        "monochrome"    => $color_mode,
+                        "colorMode"     => $color_mode,
+                        "jobId"         => $printJob['id'],
+                        "dpi"           => 300,
+                        "action"        => 'create',
+                        "scaleMode"     => 'fit',
+                        "remoteUrl"     => "https://cdn-zhy.huiyinduo.cn/".$printJob['path'],
+                        "customFileName" => $printJob['order_sn']."_".$printJob['id'],
+                    ]
+                ];
+                $printKey = "{$printJob['print_id']}-{$printJob['shop_id']}";
+                (new SaasOrderDetail)->where("id",$printJob['id'])->update(['status' => 2]);
+            }
+            echo getDateFull()."==={$printKey}==打印任务\n";
+            echo json_encode($printData)."\n";
+            $api->trigger("client-{$printKey}",'message',[
+                "type"  => "print",
+                "data"  => $printData
+            ]);
+            return true;
+        } catch (\Throwable $throwable) {
+            return true;
+        }
+    }
+
+}

+ 19 - 0
app/event/SyncUser.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace app\event;
+
+use support\think\Db;
+
+class SyncUser
+{
+
+    public function syncUserData(array $data = [])
+    {
+        if (empty($data['openid'])) return true;
+        if (empty($data['oid'])) return true;
+        $old = Db::connect("old")->table("inmei_member_card")->where("openid",$data['oid'])->findOrEmpty();
+        if (empty($old)) return true;
+
+    }
+
+}

+ 158 - 0
app/extra/jhfPay/Pay.php

@@ -0,0 +1,158 @@
+<?php
+
+namespace app\extra\jhfPay;
+
+use yzh52521\EasyHttp\Http;
+
+class Pay
+{
+
+    protected string $gateway = "https://payapi.juhefu.com/";
+
+    protected array $config = [];
+
+    public function config(array $config = [])
+    {
+        $this->config = $config;
+        return $this;
+    }
+
+    /**
+     * 分账完之后立马提现
+     * @param array $param
+     * @return array
+     */
+    public function createBalanceWithdraw(array $param = []): array
+    {
+        return $this->paramData($param,"api/member/balance_withdraw_a");
+    }
+
+    /**
+     * 基于余额分账
+     * @param array $param
+     * @return array
+     */
+    public function createBalancePay(array $param = []): array
+    {
+        return $this->paramData($param,"api/account/balance_pay");
+    }
+
+    /**
+     * 绑定用户结算卡
+     * @param array $param
+     * @return array
+     */
+    public function createMember(array $param = []): array
+    {
+        return $this->paramData($param,"api/member/create_user_a");
+    }
+
+    /**
+     * 更新结算卡信息
+     * @param array $param
+     * @return array
+     */
+    public function updateMember(array $param = []): array
+    {
+        return $this->paramData($param,"api/member/update_account_a");
+    }
+
+    /**
+     * 账户可用余额查询
+     * @param array $param
+     * @return array [balance,freeze_balance,divide_balance,total]
+     */
+    public function getBalance(array $param = []): array
+    {
+        return $this->paramData($param,"api/member/balance_query_a");
+    }
+
+    /**
+     * 创建支付
+     * @param array $param
+     * @return array
+     */
+    public function createPay(array $param = []): array
+    {
+        return $this->paramData($param,"api/payment/create_payment");
+    }
+
+
+    /**
+     * 创建退款
+     * @param array $param
+     * @return array
+     */
+    public function createRefund(array $param = []): array
+    {
+        return $this->paramData($param,"api/payment/payment_refund");
+    }
+
+    protected function paramData(array $param = [],string $url = ""): array
+    {
+        //转为json格式业务报文
+        $encryptData = json_encode($param, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
+        //对业务报文进行aes加密
+        $reqCipher = Utils::aes_encrypt($encryptData, $this->config['aeskey']);
+        //报文拼接签名字符串并进行SHA256withRSA签名
+        $param = $this->nestedSort($param);
+        $signData = Json_encode($param,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
+        $signData = Utils::rsa_sign($signData, $this->config['prikey']);
+        $time = microtime(true);
+        $timestamp = (int)$time; // 整数部分为时间戳
+        $microseconds = ($time - $timestamp) * 1000; // 小数部分转换为毫秒
+        $formattedTime = date("YmdHis", $timestamp) . sprintf('%03d', $microseconds);
+        $post_data = [
+            "merId"     => $this->config['mch_id'],
+            "sign"      => $signData,
+            "reqCipher" => $reqCipher,
+            "reqTime"   => $formattedTime
+        ];
+        $resp = Http::asJson()->post($this->gateway.$url,$post_data)->array();
+        if (array_key_exists("error_msg", $resp)) //失败
+        {
+            return ['code' => 0,"error_code"=>$resp["error_code"], "error_msg"=>$resp["error_msg"]];
+        }
+        $resCipher = Utils::aes_decrypt($resp['resCipher'], $this->config['aeskey']);
+        // 返回 [expend][pay_info]
+        return json_decode($resCipher,true);
+    }
+
+    /**
+     * @param $array
+     * @return array
+     */
+    protected function ksort_recursive(&$array): array
+    {
+        // 先对当前层数组按键名排序
+        ksort($array);
+        // 遍历数组中的每个元素
+        foreach ($array as &$value) {
+            // 如果元素是数组,递归调用排序函数
+            if (is_array($value)) {
+                $this->ksort_recursive($value);
+            }
+        }
+        unset($value); // 解除引用
+    }
+
+    /**
+     * 对多维数组进行递归字母顺序排序(每层按键名排序)
+     *
+     * @param array $array 待排序的数组
+     * @return array 排序后的数组
+     */
+    protected function nestedSort(array $array): array
+    {
+        // 对当前层的键进行排序
+        ksort($array);
+        // 递归处理子数组
+        foreach ($array as $key => &$value) {
+            if (is_array($value)) {
+                $value = $this->nestedSort($value);
+            }
+        }
+        return $array;
+    }
+
+}

+ 134 - 0
app/extra/jhfPay/Utils.php

@@ -0,0 +1,134 @@
+<?php
+
+namespace app\extra\jhfPay;
+
+use InvalidArgumentException;
+use RuntimeException;
+
+class Utils
+{
+    /**
+     * AES加密函数(ECB模式,32位密钥)
+     *
+     * @param string $plaintext 待加密的明文
+     * @param string $key 32字节(256位)密钥
+     * @return string 加密后的Base64编码字符串
+     */
+    public static function aes_encrypt($plaintext, $key) {
+        // 验证密钥长度(32字节 = 256位)
+        if (strlen($key) !== 32) {
+            throw new InvalidArgumentException('密钥长度必须为32字节');
+        }
+
+        // 执行加密(ECB模式,PKCS#7填充)
+        $ciphertext = openssl_encrypt(
+            $plaintext,
+            'AES-256-ECB',
+            $key,
+            OPENSSL_RAW_DATA
+        );
+
+        // 返回Base64编码结果
+        return base64_encode($ciphertext);
+    }
+
+    /**
+     * AES解密函数(ECB模式,32位密钥)
+     *
+     * @param string $ciphertext 待解密的Base64编码密文
+     * @param string $key 32字节(256位)密钥
+     * @return string 解密后的明文
+     */
+    public static function aes_decrypt($ciphertext, $key) {
+        // 验证密钥长度(32字节 = 256位)
+        if (strlen($key) !== 32) {
+            throw new InvalidArgumentException('密钥长度必须为32字节');
+        }
+
+        // 解码Base64密文
+        $ciphertext = base64_decode($ciphertext);
+
+        // 执行解密(ECB模式,PKCS#7填充)
+        return openssl_decrypt(
+            $ciphertext,
+            'AES-256-ECB',
+            $key,
+            OPENSSL_RAW_DATA
+        );
+    }
+
+    /**
+     * RSA签名函数(SHA256withRSA,1024位密钥)
+     *
+     * @param string $data 待签名的数据
+     * @param string $privateKeyText 私钥纯文本内容(不带PEM头和尾)
+     * @return string 签名后的Base64编码字符串
+     */
+    public static function rsa_sign(string $data, string $privateKeyText): string
+    {
+
+        // 将纯文本私钥转换为PEM格式
+        $privateKeyPem = "-----BEGIN PRIVATE KEY-----\n" .
+            chunk_split($privateKeyText, 64, "\n") .
+            "-----END PRIVATE KEY-----";
+        // 创建私钥资源
+        $privateKeyResource = openssl_pkey_get_private($privateKeyPem);
+        if (!$privateKeyResource) {
+            throw new InvalidArgumentException('私钥格式错误: ' . openssl_error_string());
+        }
+        // 生成签名(SHA256withRSA)
+        $success = openssl_sign($data, $signature, $privateKeyResource, OPENSSL_ALGO_SHA256);
+        // 释放资源
+        unset($privateKey);
+//        openssl_free_key($privateKeyResource);
+
+        if (!$success) {
+            throw new RuntimeException('签名失败: ' . openssl_error_string());
+        }
+
+        // 返回Base64编码的签名
+        return base64_encode($signature);
+    }
+
+    /**
+     * RSA验签函数(SHA256withRSA,1024位密钥)
+     *
+     * @param string $data 原始数据
+     * @param string $signature 签名的Base64编码字符串
+     * @param string $publicKeyText 公钥纯文本内容(不带PEM头和尾)
+     * @return bool 验签结果
+     */
+    public static function rsa_verify(string $data, string $signature, string $publicKeyText): bool
+    {
+        // 将纯文本公钥转换为PEM格式
+        $publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" .
+            chunk_split($publicKeyText, 64, "\n") .
+            "-----END PUBLIC KEY-----";
+
+        // 创建公钥资源
+        $publicKeyResource = openssl_pkey_get_public($publicKeyPem);
+
+        if (!$publicKeyResource) {
+            throw new InvalidArgumentException('公钥格式错误: ' . openssl_error_string());
+        }
+
+        // 解码Base64签名
+        $signature = base64_decode($signature);
+
+        // 验证签名(SHA256withRSA)
+        $result = openssl_verify($data, $signature, $publicKeyResource, OPENSSL_ALGO_SHA256);
+
+        // 释放资源
+        openssl_free_key($publicKeyResource);
+
+        // 返回验证结果(1=成功,0=失败,-1=错误)
+        if ($result === 1) {
+            return true;
+        } elseif ($result === 0) {
+            return false;
+        } else {
+            throw new RuntimeException('验签过程发生错误: ' . openssl_error_string());
+        }
+    }
+
+}

+ 31 - 0
app/extra/service/saas/MemberService.php

@@ -4,6 +4,7 @@ namespace app\extra\service\saas;
 
 use app\extra\basic\Service;
 use app\model\saas\SaasUser;
+use app\model\saas\SaasUserBuy;
 
 class MemberService extends Service
 {
@@ -23,6 +24,36 @@ class MemberService extends Service
     }
 
 
+    /**
+     * @param array $param
+     * @return array
+     */
+    public function getMpTotal(array $param = []): array
+    {
+        $this->mode = new SaasUser();
+        $commonFilter = [];
+        $filter = $this->searchFilter($param);
+        // 起止时间
+        if (!empty($param['create'])) {
+            $times = between_time($param['create']);
+            $start = date('Y-m-d',$times['start_time']);
+            $end = date('Y-m-d',($times['end_time'] + 86400));
+            $commonFilter[] = ['create_at', '>=', $start ];
+            $commonFilter[] = ['create_at', '<', $end ];
+        }
+        $filter = array_merge($filter,$commonFilter);
+        $userTotal = $this->mode->field("count(1) as number,sum(balance) as total")->where($filter)->findOrEmpty();
+        $filter[] = ['status','=',1];
+        $buyTotal = (new SaasUserBuy)->field("count(1) as number,sum(money) as total")->where($filter)->findOrEmpty();
+        return [
+            "user"          => $userTotal['number'],
+            "userMoney"     => $userTotal['total'],
+            "recharge"      => $buyTotal['number'],
+            "rechargeMoney" => $buyTotal['total'],
+        ];
+    }
+
+
     protected function searchFilter(array $param = []): array
     {
         $filter = [];

+ 42 - 0
app/extra/service/saas/OrderService.php

@@ -4,6 +4,8 @@ namespace app\extra\service\saas;
 
 use app\extra\basic\Service;
 use app\model\saas\SaasOrder;
+use app\model\saas\SaasOrderQrcode;
+use app\model\saas\SaasUserBuy;
 
 class OrderService extends Service
 {
@@ -40,6 +42,46 @@ class OrderService extends Service
         return $total;
     }
 
+    /**
+     * 手机端
+     * @param array $param
+     * @return int[]
+     */
+    public function getTotalDate(array $param = []): array
+    {
+        $this->mode = new SaasOrder();
+        $commonFilter = [];
+        $filter = $this->searchFilter($param);
+        // 起止时间
+        if (!empty($param['create'])) {
+            $times = between_time($param['create']);
+            $start = date('Y-m-d',$times['start_time']);
+            $end = date('Y-m-d',($times['end_time'] + 86400));
+            $commonFilter[] = ['create_at', '>=', $start ];
+            $commonFilter[] = ['create_at', '<', $end ];
+        }
+        $filter = array_merge($filter,$commonFilter);
+        $total = ['ta' => 0,'t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 'tm' => 0, 'p1' => 0, 'p2' => 0, 'p1m' => 0, 'p2m' => 0];
+        foreach ($this->mode->where($filter)->whereIn("status",[1,2,3])->field('create_at,pay_type,status,sum(discount) as money,count(1) as total')->group('status,create_at,pay_type')->cursor() as $vo)
+        {
+            $total["t{$vo['status']}"] += $vo['total'];
+            $total['ta'] += $vo['total'];
+            $total['tm'] += $vo['money'];
+            if ($vo['pay_type'] == 1) {
+                $total["p1"] += $vo['total'];
+                $total["p1m"] += $vo['money'];
+            }
+            if ($vo['pay_type'] == 2) {
+                $total["p2"] += $vo['total'];
+                $total["p2m"] += $vo['money'];
+            }
+        }
+        $filter[] = ['status','=',1];
+        $qrcode = (new SaasOrderQrcode)->where($filter)->field("sum(money) as money,count(1) as total")->find();
+        $card = (new SaasUserBuy)->where($filter)->field("sum(money) as money,count(1) as total")->find();
+        return compact("total",'qrcode','card');
+    }
+
 
     public function getTotalToday(array $param = []): array
     {

+ 36 - 0
app/extra/service/saas/ShopLogService.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace app\extra\service\saas;
+
+use app\extra\basic\Service;
+use app\model\saas\SaasShopLog;
+
+class ShopLogService extends Service
+{
+
+
+    /**
+     * 列表
+     * @param array $param
+     */
+    public function getList(array $param = [])
+    {
+        $this->mode = new SaasShopLog();
+        return $this->searchVal($param,$this->searchFilter($param))->paginate([
+            "list_rows" => $param['pageSize'],
+            "page"      => $param['page']
+        ]);
+    }
+
+
+
+    protected function searchFilter(array $param = []): array
+    {
+        $filter = [];
+        !empty($param['status']) && $filter[] = ["status", '=', ($param['status']-1)];
+        !empty($param['shop']) && $filter[] = ["shop_id", '=', $param['shop']];
+        !empty($param['type']) && $filter[] = ["type", '=', $param['type']];
+        return $filter;
+    }
+
+}

+ 35 - 0
app/extra/service/system/MoneyLogService.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace app\extra\service\system;
+
+use app\extra\basic\Service;
+use app\model\system\SystemUserMoney;
+
+class MoneyLogService extends Service
+{
+
+    /**
+     * 列表
+     * @param array $param
+     */
+    public function getList(array $param = [])
+    {
+        $this->mode = new SystemUserMoney();
+        return $this->searchVal($param,$this->searchFilter($param))->paginate([
+            "list_rows" => $param['pageSize'],
+            "page"      => $param['page']
+        ]);
+    }
+
+
+
+    protected function searchFilter(array $param = []): array
+    {
+        $filter = [];
+        !empty($param['status']) && $filter[] = ["status", '=', ($param['status']-1)];
+        !empty($param['shop']) && $filter[] = ["agent_id", '=', $param['shop']];
+        !empty($param['name']) && $filter[] = ["name", 'like', "%{$param['name']}%"];
+        return $filter;
+    }
+
+}

+ 1 - 0
app/extra/wechat/WechatService.php

@@ -124,6 +124,7 @@ class WechatService
                     $userinfo = $wechat->getUserInfo($token['access_token'], $openid);
                     request()->session()->set("{$appid}_fansinfo", $userinfo);
                 }
+                return ['openid' => $openid, 'url' => '', 'fansinfo' => ""];
             }
             if ($getVars['rcode']) {
                 $location = debase64url($getVars['rcode']);

+ 19 - 0
app/functions.php

@@ -579,4 +579,23 @@ if (!function_exists('debase64url')) {
     {
         return CodeExtend::deSafe64($string);
     }
+}
+if (!function_exists('unique_arr')) {
+
+    /**
+     * 多维数组根据指定字段去重
+     * @param array $array 原始数组
+     * @param string $field 去重字段名
+     * @return array 去重后的数组
+     */
+    function unique_arr(array $array, string $field): array
+    {
+        $tmp = [];
+        foreach ($array as $key => $val) {
+            // 用去重字段的值作为临时键,重复会自动覆盖
+            $tmp[$val[$field]] = $val;
+        }
+        // 重置索引返回
+        return array_values($tmp);
+    }
 }

+ 7 - 2
app/middleware/WxMiddleware.php

@@ -20,8 +20,13 @@ class WxMiddleware implements MiddlewareInterface
                 if (empty($type)) return json(['code'=> 0,'msg'=> trans("error.param")]);
                 $token =  $request->header("Authorization","");
                 if (empty($token)) return json(['code'=> 0,'msg'=> trans("error.request")]);
-                (new JWT)->guard("member")->verify();
-                $user = (new Auth)->guard("member")->user();
+                if ($type == "mini") {
+                    $typeName = "member";
+                } else {
+                    $typeName = "mp";
+                }
+                (new JWT)->guard($typeName)->verify();
+                $user = (new Auth)->guard($typeName)->user();
                 if (empty($user)) return json(['code'=>401,'msg'=> trans("error.login")]);
                 $request->user = $user->toArray();
             }

+ 45 - 0
app/model/saas/SaasPrintLog.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace app\model\saas;
+
+use app\extra\basic\Model;
+
+
+/**
+ * @property integer $id (主键)
+ * @property mixed $order_sn 
+ * @property string $msg 
+ * @property mixed $create_at
+ */
+class SaasPrintLog extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "saas_print_log";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+}

+ 7 - 0
app/model/saas/SaasShop.php

@@ -4,6 +4,7 @@ namespace app\model\saas;
 
 use app\extra\basic\Model;
 use think\model\relation\HasMany;
+use think\model\relation\HasOne;
 
 
 /**
@@ -68,4 +69,10 @@ class SaasShop extends Model
         return $this->hasMany("app\model\system\SystemUserOpen","shop_id","shop_id");
     }
 
+
+    public function card(): HasOne
+    {
+        return $this->hasOne("app\model\system\SystemUserBankcard","agent_id","shop_id");
+    }
+
 }

+ 46 - 0
app/model/saas/SaasWordChange.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace app\model\saas;
+
+use app\extra\basic\Model;
+
+
+/**
+ * @property integer $id (主键)
+ * @property mixed $key 
+ * @property string $path 
+ * @property integer $status 
+ * @property mixed $create_at
+ */
+class SaasWordChange extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "saas_word_change";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+}

+ 61 - 0
app/model/system/SystemUserBankcard.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace app\model\system;
+
+use app\extra\basic\Model;
+use app\model\saas\SaasShop;
+use think\model\relation\HasOne;
+
+
+/**
+ * @property integer $id (主键)
+ * @property integer $agent_id 代理ID
+ * @property mixed $member_id 
+ * @property string $user_name 姓名
+ * @property mixed $cert_id 身份证
+ * @property mixed $tel_no 电话
+ * @property mixed $bank 银行代码
+ * @property mixed $card_id 银行卡号
+ * @property string $valid_from 身份证有效期
+ * @property string $valid_until 身份证有效期结束
+ * @property string $card_prov_code 开户银行省
+ * @property string $card_area_code 开户银行市
+ * @property string $card_city_name·
+ */
+class SystemUserBankcard extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "system_user_bankcard";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+    public function shop(): HasOne
+    {
+        return $this->hasOne(SaasShop::class,"agent_id","shop_id");
+    }
+
+
+}

+ 57 - 0
app/model/system/SystemUserMoney.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace app\model\system;
+
+use app\extra\basic\Model;
+use app\model\saas\SaasShop;
+use think\model\relation\HasOne;
+
+
+/**
+ * @property integer $id (主键)
+ * @property mixed $order_sn 结算编号
+ * @property mixed $agent_id 
+ * @property integer $money 
+ * @property mixed $day 
+ * @property integer $status 1已结算0待结算
+ * @property mixed $member_id 银行卡id
+ * @property mixed $create_at
+ */
+class SystemUserMoney extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "system_user_money";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+
+    public function shop(): HasOne
+    {
+        return $this->hasOne(SaasShop::class,"shop_id","agent_id");
+    }
+
+}

+ 113 - 0
app/queue/redis/PushPrint.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace app\queue\redis;
+
+use app\model\saas\SaasOrderDetail;
+use Webman\Push\Api;
+use Webman\RedisQueue\Consumer;
+
+class PushPrint  implements Consumer
+{
+
+    public $queue = "push-print";
+
+    public $connection = "default";
+
+
+    public function consume($data): bool
+    {
+        try {
+            if (empty($data['order'])) return true;
+            if (empty($data['type'])) return true;
+            $api = new Api('http://127.0.0.1:3232', config('plugin.webman.push.app.app_key'),config('plugin.webman.push.app.app_secret'));
+            $printData = [];
+            if ($data['type'] == 'order') { // 按订单
+                $detail = (new SaasOrderDetail)->where(["order_sn" => $data['order'],'status' => 1])->select();
+                if ($detail->isEmpty()) return true;
+                foreach ($detail as $key=>$printJob) {
+                    $range = Null;
+                    if ($printJob['end_page'] > $printJob['start_page']) {
+                        $range = $printJob['start_page']."-".$printJob['end_page'];
+                    }
+                    if ($printJob['end_page'] == $printJob['start_page']) {
+//                        $range = (string) $printJob['end_page'];
+                        $range = $printJob['start_page']."-".$printJob['end_page'];
+                    }
+                    $color_mode = ($printJob['color']==1?'color':'monochrome');
+                    $duplex = "simplex";
+                    if ($printJob['paper_size'] == 'A4' && $printJob['duplex'] == 2) {
+                        $duplex = 'shortedge'; // duplexlong  longedge
+                    }
+                    if ($printJob['paper_size'] == 'A3' && $printJob['duplex'] == 2) {
+                        $duplex = 'longedge'; // duplexshort  shortedge
+                    }
+                    $printData[$key] = [
+                        "printerName"   => $printJob['print_name'],
+                        "copies"        => $printJob['number'],
+                        "landscape"     => false,
+                        "paperSize"     => $printJob['paper_size'],
+                        "pageRange"     => $range,
+                        "duplex"        => $duplex,
+                        "monochrome"    => $color_mode,
+                        "colorMode"     => $color_mode,
+                        "jobId"         => $printJob['id'],
+                        "dpi"           => 300,
+                        "action"        => 'create',
+                        "scaleMode"     => 'fit',
+                        "remoteUrl"     => "https://cdn-zhy.huiyinduo.cn/".$printJob['path'],
+                        "customFileName" => $printJob['order_sn']."_".$printJob['id'],
+                    ];
+                    (new SaasOrderDetail)->where("id",$printJob['id'])->update(['status' => 2]);
+                }
+                $printKey = "{$detail[0]['print_id']}-{$detail[0]['shop_id']}";
+            } else { // 单笔打印
+                $printJob = (new SaasOrderDetail)->where(["id" => $data['order'],'status' => 1])->findOrEmpty();
+                if ($printJob->isEmpty()) return true;
+                $range = Null;
+                if ($printJob['end_page'] > $printJob['start_page']) {
+                    $range = $printJob['start_page']."-".$printJob['end_page'];
+                }
+                if ($printJob['end_page'] == $printJob['start_page']) {
+                    $range = (string) $printJob['end_page'];
+                }
+                $color_mode = ($printJob['color']==1?'color':'monochrome');
+                $duplex = "simplex";
+                if ($printJob['paper_size'] == 'A4' && $printJob['duplex'] == 2) {
+                    $duplex = 'duplexlong'; // duplexlong
+                }
+                if ($printJob['paper_size'] == 'A3' && $printJob['duplex'] == 2) {
+                    $duplex = 'duplexshort'; // duplexshort
+                }
+                $printData = [
+                    [
+                        "printerName"   => $printJob['print_name'],
+                        "copies"        => $printJob['number'],
+                        "landscape"     => false,
+                        "paperSize"     => $printJob['paper_size'],
+                        "pageRange"     => $range,
+                        "duplex"        => $duplex,
+                        "monochrome"    => $color_mode,
+                        "colorMode"     => $color_mode,
+                        "jobId"         => $printJob['id'],
+                        "dpi"           => 300,
+                        "action"        => 'create',
+                        "scaleMode"     => 'fit',
+                        "remoteUrl"     => "https://cdn-zhy.huiyinduo.cn/".$printJob['path'],
+                        "customFileName" => $printJob['order_sn']."_".$printJob['id'],    
+                    ]
+                ];
+                $printKey = "{$printJob['print_id']}-{$printJob['shop_id']}";
+                (new SaasOrderDetail)->where("id",$printJob['id'])->update(['status' => 2]);
+            }
+//            echo getDateFull()."==={$printKey}==打印任务\n";
+//            echo json_encode($printData)."\n";
+            $api->trigger("client-{$printKey}",'message',[
+                "type"  => "print",
+                "data"  => $printData
+            ]);
+            return true;
+        } catch (\Throwable $throwable) {
+            return true;
+        }
+    }
+}

+ 46 - 0
app/queue/redis/WordChange.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace app\queue\redis;
+
+use app\extra\tools\CodeExtend;
+use app\model\saas\SaasWordChange;
+use Webman\RedisQueue\Consumer;
+use yzh52521\EasyHttp\Http;
+
+class WordChange  implements Consumer
+{
+
+    public $queue = "word-change";
+
+    public $connection = "default";
+
+    public function consume($data): bool
+    {
+        print_r($data);
+        $change = (new SaasWordChange)->where("key",md5($data['key']))->findOrEmpty();
+        if ($change->isEmpty()) return true;
+        if ($change['status'] == 1) return true;
+        if ($data['ext'] !== 'pdf') {
+            $pathUri = [];
+            for ($i = 1; $i <= $data['total']; $i ++) {
+                $pathUri[$i] = "https://".sConf("storage.cos_http_domain")."/".$data['key']."?ci-process=doc-preview&dstType=png&imageDpi=300&page={$i}";
+            }
+            $resp = Http::asJson()->withHeaders(['x-api-key' => "your-api-key-here"])->post("http://127.0.0.1:3000/api/images-to-pdf",[
+                "imageUrls" => array_values($pathUri),
+                "pdfName"   => "upload_".CodeExtend::uniqidDate(18),
+                "options"   => [
+                    "compression"   => false,
+                    "quality"       => 100
+                ]
+            ])->array();
+            echo getDateFull()."==={$data['key']}===word转码\n";
+            print_r($resp);
+            $path = $resp['data']['pdfUrl'];
+            $change->status = 1;
+            $change->path = $path;
+            $change->save();
+        }
+        return true;
+    }
+
+}

+ 42 - 0
app/validate/saas/CardValidate.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace app\validate\saas;
+
+use think\Validate;
+
+class CardValidate extends Validate
+{
+
+
+
+    protected $rule = [
+        "user_name"     => "require",
+        "cert_id"       => "require|idCard",
+        "tel_no"        => "require|mobile",
+        "card_id"       => "require",
+        "valid_from"    => "require",
+        "valid_until"   => "require",
+        "card_prov_code"    => "require",
+        "card_area_code"    => "require",
+        "card_city_name"    => "require",
+    ];
+
+
+    protected $message = [
+        "user_name.require"     => "请输入姓名",
+        "cert_id.require"       => "请输入身份证号",
+        "tel_no.require"        => "请输入手机号码",
+        "cert_id.idCard"        => "身份证号有误",
+        "tel_no.mobile"         => "手机号有误",
+        "card_id.require"       => "请输入银行卡号",
+        "valid_from.require"    => "请选择身份证日期",
+        "valid_until.require"   => "请选择身份证日期",
+        "card_prov_code.require" => "请选择开户地区",
+        "card_area_code.require" => "请选择开户地区",
+        "card_city_name.require" => "请选择开户地区"
+    ];
+    protected $scene = [
+        'add'  =>  ['user_name','cert_id','tel_no','card_id','valid_from','valid_until','card_prov_code','card_area_code','card_city_name'],
+    ];
+
+}

+ 7 - 2
app/wss/PrintWss.php

@@ -24,11 +24,14 @@ class PrintWss
             $version = sConf("service.version");
             $data = [
                 "type"      => "update",
-                "version"   => $version?:'1.0.1'
+                "version"   => $version?:'1.0.1',
+                "del"       => 0
             ];
         }
         if ($param['type'] == 'get_data') {
             $printer = explode("-",$param['printer']);
+            echo getDateFull()."===wss参数\n";
+            print_r($printer);
             $printJob = (new SaasOrderDetail)->where("shop_id",$printer[1])->where("print_id",$printer[0])->where("status",1)->findOrEmpty();
             if (!$printJob->isEmpty()) {
                 $range = Null;
@@ -51,8 +54,10 @@ class PrintWss
                     "data"  => [
                         "jobId"     => $printJob['id'],
                         "action"    => 'create',
-                        "remoteUrl" => "https://".sConf("storage.cos_http_domain")."/".$printJob['path'],
+                        "remoteUrl" => "https://cdn-zhy.huiyinduo.cn/".$printJob['path'],
+//                        "remoteUrl" => "https://".sConf("storage.cos_http_domain")."/".$printJob['path'],
                         "customFileName" => $printJob['order_sn']."_".$printJob['id'],
+//                        "exe"       => ['-print-to',$printJob['print_name'],'-print-settings',"{$range},{$printJob['number']}x,{$color_mode},$duplex,paper={$printJob['paper_size']}",'-silent'],
                         "exe"       => ['-print-to',$printJob['print_name'],'-print-settings',"{$range},{$printJob['number']}x,{$color_mode},$duplex,fit,paper={$printJob['paper_size']}",'-silent'],
                     ]
                 ];

+ 2 - 1
composer.json

@@ -54,7 +54,8 @@
     "topthink/think-validate": "^3.0",
     "hzdad/codecheck": "^1.0",
     "php-di/php-di": "7.0",
-    "zoujingli/wechat-developer": "^1.2"
+    "zoujingli/wechat-developer": "^1.2",
+    "webman/push": "^1.1"
   },
   "suggest": {
     "ext-event": "For better performance. "

+ 6 - 0
config/event.php

@@ -9,5 +9,11 @@ return [
     ],
     "merge-pdf" => [
         [\app\event\MergePdf::class,"imgMergePdf"]
+    ],
+    "sync-user" => [
+        [\app\event\SyncUser::class,"syncUserData"]
+    ],
+    "push-print" => [
+        [\app\event\PushPrint::class,"createPrint"]
     ]
 ];

+ 6 - 0
config/plugin/shopwwi/auth/app.php

@@ -15,6 +15,12 @@
              'field' => ['id','openid'], //设置允许写入扩展中的字段
              'num' => 0, //-1为不限制终端数量 0为只支持一个终端在线 大于0为同一账号同终端支持数量 建议设置为1 则同一账号同终端在线1个
              'model'=> [\app\model\saas\SaasUserOpen::class,'thinkphp'] // 当为数组时 [app\model\Test::class,'thinkphp'] 来说明模型归属
+         ],
+         'mp' => [
+             'key' => 'id',
+             'field' => ['id','openid'], //设置允许写入扩展中的字段
+             'num' => 0, //-1为不限制终端数量 0为只支持一个终端在线 大于0为同一账号同终端支持数量 建议设置为1 则同一账号同终端在线1个
+             'model'=> [\app\model\system\SystemUserOpen::class,'thinkphp'] // 当为数组时 [app\model\Test::class,'thinkphp'] 来说明模型归属
          ]
      ],
      'jwt' => [

+ 10 - 0
config/plugin/webman/push/app.php

@@ -0,0 +1,10 @@
+<?php
+return [
+    'enable'       => true,
+    'websocket'    => 'websocket://0.0.0.0:3131',
+    'api'          => 'http://0.0.0.0:3232',
+    'app_key'      => '9b497db73e0145ff4aea6af871c8b381',
+    'app_secret'   => 'ccdc58876eaad13a1a793d12211c5016',
+    'channel_hook' => 'http://127.0.0.1:8787/plugin/webman/push/hook',
+    'auth'         => '/plugin/webman/push/auth'
+];

+ 21 - 0
config/plugin/webman/push/process.php

@@ -0,0 +1,21 @@
+<?php
+
+use Webman\Push\Server;
+
+return [
+    'server' => [
+        'handler'     => Server::class,
+        'listen'      => config('plugin.webman.push.app.websocket'),
+        'count'       => 1, // 必须是1
+        'reloadable'  => false, // 执行reload不重启
+        'constructor' => [
+            'api_listen' => config('plugin.webman.push.app.api'),
+            'app_info'   => [
+                config('plugin.webman.push.app.app_key') => [
+                    'channel_hook' => config('plugin.webman.push.app.channel_hook'),
+                    'app_secret'   => config('plugin.webman.push.app.app_secret'),
+                ],
+            ]
+        ]
+    ]
+];

+ 88 - 0
config/plugin/webman/push/route.php

@@ -0,0 +1,88 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use support\Request;
+use Webman\Route;
+use Webman\Push\Api;
+
+/**
+ * 推送js客户端文件
+ */
+Route::get('/plugin/webman/push/push.js', function (Request $request) {
+    return response()->file(base_path().'/vendor/webman/push/src/push.js');
+});
+
+/**
+ * 私有频道鉴权,这里应该使用session辨别当前用户身份,然后确定该用户是否有权限监听channel_name
+ */
+Route::post(config('plugin.webman.push.app.auth'), function (Request $request) {
+    print_r($request->post());
+    $pusher = new Api(str_replace('0.0.0.0', '127.0.0.1', config('plugin.webman.push.app.api')), config('plugin.webman.push.app.app_key'), config('plugin.webman.push.app.app_secret'));
+    $channel_name = $request->post('channel_name');
+    $session = $request->session();
+    // 这里应该通过session和channel_name判断当前用户是否有权限监听channel_name
+    $has_authority = true;
+    if ($has_authority) {
+        return response($pusher->socketAuth($channel_name, $request->post('socket_id')));
+    } else {
+        return response('Forbidden', 403);
+    }
+});
+
+/**
+ * 当频道上线以及下线时触发的回调
+ * 频道上线:是指某个频道从没有连接在线到有连接在线的事件
+ * 频道下线:是指某个频道的所有连接都断开触发的事件
+ */
+Route::post(parse_url(config('plugin.webman.push.app.channel_hook'), PHP_URL_PATH), function (Request $request) {
+
+    // 没有x-pusher-signature头视为伪造请求
+    if (!$webhook_signature = $request->header('x-pusher-signature')) {
+        return response('401 Not authenticated', 401);
+    }
+
+    $body = $request->rawBody();
+
+    // 计算签名,$app_secret 是双方使用的密钥,是保密的,外部无从得知
+    $expected_signature = hash_hmac('sha256', $body, config('plugin.webman.push.app.app_secret'), false);
+
+    // 安全校验,如果签名不一致可能是伪造的请求,返回401状态码
+    if ($webhook_signature !== $expected_signature) {
+        return response('401 Not authenticated', 401);
+    }
+
+    // 这里存储这上线 下线的channel数据
+    $payload = json_decode($body, true);
+
+    $channels_online = $channels_offline = [];
+
+    foreach ($payload['events'] as $event) {
+        if ($event['name'] === 'channel_added') {
+            $channels_online[] = $event['channel'];
+        } else if ($event['name'] === 'channel_removed') {
+            $channels_offline[] = $event['channel'];
+        }
+    }
+
+    // 业务根据需要处理上下线的channel,例如将在线状态写入数据库,通知其它channel等
+    // 上线的所有channel
+    echo 'online channels: ' . implode(',', $channels_online) . "\n";
+    // 下线的所有channel
+    echo 'offline channels: ' . implode(',', $channels_offline) . "\n";
+
+    return 'OK';
+});
+
+
+

+ 2 - 2
config/plugin/webman/redis-queue/redis.php

@@ -6,8 +6,8 @@ return [
             'auth' => null,
             'db' => 0,
             'prefix' => '',
-            'max_attempts'  => 5,
-            'retry_seconds' => 5,
+            'max_attempts'  => 1,
+            'retry_seconds' => 1,
         ],
         // Connection pool, supports only Swoole or Swow drivers.
         'pool' => [

+ 33 - 0
config/think-orm.php

@@ -36,6 +36,39 @@ return [
                 'heartbeat_interval' => 50, // 心跳检测间隔,需要小于60秒
             ],
         ],
+        'old' => [
+            // 数据库类型
+            'type' => 'mysql',
+            // 服务器地址
+            'hostname' => getenv('DB_HOST'),
+            // 数据库名
+            'database' => "print",
+            // 数据库用户名
+            'username' => getenv('DB_USER'),
+            // 数据库密码
+            'password' => getenv('DB_PASSWORD'),
+            // 数据库连接端口
+            'hostport' => getenv('DB_PORT'),
+            // 数据库连接参数
+            'params' => [
+                // 连接超时3秒
+                \PDO::ATTR_TIMEOUT => 3,
+            ],
+            // 数据库编码默认采用utf8
+            'charset' => 'utf8',
+            // 数据库表前缀
+            'prefix' => '',
+            // 断线重连
+            'break_reconnect' => true,
+            // 连接池配置
+            'pool' => [
+                'max_connections' => 5, // 最大连接数
+                'min_connections' => 1, // 最小连接数
+                'wait_timeout' => 3,    // 从连接池获取连接等待超时时间
+                'idle_timeout' => 60,   // 连接最大空闲时间,超过该时间会被回收
+                'heartbeat_interval' => 50, // 心跳检测间隔,需要小于60秒
+            ],
+        ],
     ],
     // 自定义分页类
     'paginator' =>  '',

BIN
print-api.zip


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
public/city.json


BIN
public/uploads/card/10888738344576-pay.jpg


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff