96 Commits b85eace537 ... fc9067f33b

Autor SHA1 Mensaje Fecha
  gorden fc9067f33b 核销不验证 hace 1 mes
  gorden 4303625288 清福利账户 hace 1 mes
  Gorden fe8c30c725 充值异常 hace 1 mes
  Gorden 5ef34a2dc8 充值异常 hace 1 mes
  gorden 32e054a151 明细支出 hace 1 mes
  gorden f7e20dbee9 明细 hace 1 mes
  gorden 4ec4be72e9 明细 hace 1 mes
  gorden da87b3e82d 明细组号 hace 1 mes
  gorden f669686a80 充值 hace 1 mes
  gorden 0d58a22411 优惠券使用去掉sku验证 hace 1 mes
  gorden 467cd1a164 会员推荐人姓名 hace 1 mes
  gorden 54ef4eecd0 明细满额 hace 1 mes
  gorden bf1d61be39 明细满额 hace 1 mes
  gorden 25fa44e7c0 明细满额 hace 1 mes
  gorden ac36a14b99 优惠券使用 hace 1 mes
  gorden 8c280bd2c2 收支记录 hace 1 mes
  gorden b8ee6efe41 取消订单日志 hace 1 mes
  gorden 7fca4ec6f4 收支明细表 hace 1 mes
  gorden 353b8e5774 收支明细表 hace 1 mes
  gorden 01323a382d 充值提成 hace 1 mes
  gorden 579250f677 补字段 hace 1 mes
  gorden 0590e79192 补字段 hace 1 mes
  gorden 44b5f7d9b6 补字段 hace 1 mes
  gorden df09a82f16 退款 hace 1 mes
  gorden d2ca8d6369 合伙人过期时间 hace 1 mes
  gorden 2b6fa77dd8 Merge branch 'dev' hace 1 mes
  gorden 28ac094368 合伙人过期时间 hace 1 mes
  gorden 6fe3b34ebd 合伙人、推荐官自动过期 hace 1 mes
  gorden 87b8cd42b9 优惠券获得方式 hace 1 mes
  gorden aa7902570f 全部订单,餐饮搜索 hace 1 mes
  gorden 6c364f3a49 补字段 hace 1 mes
  gorden ec22b38e8e 补字段 hace 1 mes
  gorden da74a14d79 补字段 hace 1 mes
  gorden ca0ddb2b05 Merge branch 'master' into dev hace 1 mes
  gorden 3f89577ea7 充值记录,新客金额 hace 1 mes
  gorden 51d6137896 补字段 hace 1 mes
  gorden 25354f5525 读取合伙人赠送 hace 1 mes
  gorden 75e251c1f3 读取合伙人赠送 hace 1 mes
  gorden 2df55887bd 补字段 hace 1 mes
  gorden 02e121091d 提现打款成功,扣余额 hace 1 mes
  gorden b7ab112dbf 会员订单调整 hace 1 mes
  gorden 1b86374724 优惠券有效期限 hace 1 mes
  gorden 57c34bdb11 周期券发过去的券 hace 1 mes
  gorden e40092ca6e 完善提现 hace 1 mes
  gorden 2b9c1df42b 会员合伙人加一条支付记录 hace 1 mes
  Gorden 864bdb25e6 Merge branch 'master' into dev hace 1 mes
  Gorden fb5d925a9b 康旅改造 hace 1 mes
  Gorden 50f0164316 Merge branch 'master' into dev hace 1 mes
  Gorden 6a61fd4cfe 支付宝退款 hace 1 mes
  Gorden 6017c9db50 Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  Gorden 32cc012a1d 退款 hace 1 mes
  yxjapp 4e9b29ebec Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  Gorden e34d35fd5f 退款 hace 1 mes
  yxjapp a45e12bbeb Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  Gorden 2c78abe5f0 手动发券日期 hace 1 mes
  gorden e5bd7a306d 模拟请求 hace 1 mes
  gorden 2c11085282 模拟请求 hace 1 mes
  yxjapp 997876d3cb Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  gorden daad2884aa 发券日志 hace 1 mes
  gorden 116f7ab490 我的所有卡券 hace 1 mes
  gorden 63ec4a1b7d Merge branch 'master' into dev hace 1 mes
  yxjapp 8b0bc680cf Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  gorden e54a89c357 新客 hace 1 mes
  gorden 7857a1c701 我的佣金 hace 1 mes
  gorden 1a2777f20a 我的佣金 hace 1 mes
  gorden 778928b4a4 评价图域名 hace 1 mes
  yxjapp 110094a36f Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  gorden 6ea71e9488 优惠券时间 hace 1 mes
  yxjapp 308ab23036 Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  gorden 94f2af4251 会员调整 hace 1 mes
  gorden 48c653df77 测试推荐官 hace 1 mes
  yxjapp 9fc4147325 Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  gorden fa9138037e Merge branch 'master' of http://39.98.194.76:3000/txct/wanyue_app into dev hace 1 mes
  gorden 24735a658d 测试推荐官 hace 1 mes
  gorden 7a083f08c8 测试推荐官 hace 1 mes
  gorden cdc045185b 测试合伙人 hace 1 mes
  gorden ee08de5472 测试合伙人 hace 1 mes
  yxjapp 849fabc054 Merge branch 'dev' of http://39.98.194.76:3000/txct/wanyue_app hace 1 mes
  gorden d33df7d904 测试新客 hace 1 mes
  Gorden d6d104520b 提现 hace 1 mes
  gorden 433ca3c169 推荐官 hace 1 mes
  gorden 0671f7c1e3 余额明细 hace 1 mes
  gorden 6b25fe9cc0 余额明细 hace 1 mes
  gorden 3eb58cea67 余额明细 hace 1 mes
  gorden dce72b38dc 会员合伙人 hace 1 mes
  gorden 92bd736160 推荐官 hace 1 mes
  yb 3d396fa870 fixbug:修复客户导出时未携带初次拜访时间 hace 1 mes
  gorden 48ad22fe49 会员合伙人 hace 1 mes
  gorden 8778405201 会员合伙人 hace 1 mes
  gorden b8d473166d 新客专享 hace 1 mes
  gorden 98346a7f3c 新客专享 hace 1 mes
  gorden b8a6ade0f2 新客专享 hace 1 mes
  gorden 268a4ce394 新客专享 hace 1 mes
  gorden dfca228361 新客专享 hace 1 mes
  gorden 34b65c8c11 Merge branch 'master' into dev hace 1 mes
  gorden ada2c9ff3b 1000新客 hace 1 mes
Se han modificado 58 ficheros con 13495 adiciones y 472 borrados
  1. 4 2
      app/admin/controller/client/EvaluateController.php
  2. 28 2
      app/admin/controller/client/PointsController.php
  3. 15 31
      app/admin/controller/coupon/CouponController.php
  4. 16 7
      app/admin/controller/coupon/CouponDetailController.php
  5. 178 0
      app/admin/controller/finance/CommissionListController.php
  6. 202 0
      app/admin/controller/finance/WithdrawalListController.php
  7. 57 0
      app/admin/controller/goods/NewCustomerController.php
  8. 45 0
      app/admin/controller/goods/PartnerController.php
  9. 45 0
      app/admin/controller/goods/ReferrerController.php
  10. 106 0
      app/admin/controller/goods/TravelGoodsController.php
  11. 2 0
      app/admin/controller/member/BenefitController.php
  12. 83 1
      app/admin/controller/member/MemberController.php
  13. 8 6
      app/admin/controller/member/QuotaController.php
  14. 71 13
      app/admin/controller/member/WriteOffController.php
  15. 21 0
      app/admin/controller/notify/RechargeController.php
  16. 8 1
      app/admin/controller/order/DishesController.php
  17. 33 8
      app/admin/controller/order/GoodsController.php
  18. 25 4
      app/admin/controller/order/KangyangCityController.php
  19. 1215 0
      app/admin/controller/order/NewCustomerController.php
  20. 30 7
      app/admin/controller/order/PackagesController.php
  21. 177 16
      app/admin/controller/order/PartnerController.php
  22. 133 18
      app/admin/controller/order/PayDetailController.php
  23. 1246 0
      app/admin/controller/order/ReferrerController.php
  24. 49 13
      app/admin/controller/order/RefundController.php
  25. 33 7
      app/admin/controller/order/ServicesController.php
  26. 220 131
      app/admin/controller/order/WholeController.php
  27. 59 0
      app/admin/controller/sys_manage/ConfigController.php
  28. 2 1
      app/admin/service/added/AddedService.php
  29. 2 1
      app/admin/service/consultant/CustomService.php
  30. 236 5
      app/admin/service/coupon/CouponDetailService.php
  31. 83 3
      app/admin/service/coupon/CouponService.php
  32. 189 5
      app/admin/service/goods/GoodsService.php
  33. 24 0
      app/admin/service/goods/GoodsSkuService.php
  34. 2119 0
      app/admin/service/goods/NewCustomerService.php
  35. 2119 0
      app/admin/service/goods/PartnerService.php
  36. 2119 0
      app/admin/service/goods/ReferrerService.php
  37. 1005 0
      app/admin/service/goods/TravelService.php
  38. 309 22
      app/admin/service/member/MemberService.php
  39. 155 0
      app/admin/service/order/CommissionService.php
  40. 56 12
      app/admin/service/order/OrderService.php
  41. 1 1
      app/admin/validate/goods/GoodsValidate.php
  42. 69 0
      app/command/NewCustomCommand.php
  43. 69 0
      app/command/PartnerCommand.php
  44. 6 3
      app/command/WelfareAccountCommand.php
  45. 82 63
      app/event/order/CommissionEvent.php
  46. 131 0
      app/event/order/NewCustomerEvent.php
  47. 69 63
      app/event/order/PartnerEvent.php
  48. 51 0
      app/event/order/ReferrerEvent.php
  49. 306 0
      app/event/statistics/InOutEvent.php
  50. 33 19
      app/functions.php
  51. 11 0
      app/model/ClientPoints.php
  52. 21 0
      app/model/DataInout.php
  53. 4 0
      app/model/Goods.php
  54. 15 0
      app/model/MemberAccountList.php
  55. 12 0
      config/event.php
  56. 2 2
      config/log.php
  57. 9 4
      process/Task.php
  58. 77 1
      route/admin.php

+ 4 - 2
app/admin/controller/client/EvaluateController.php

@@ -2,10 +2,12 @@
 
 namespace app\admin\controller\client;
 
+use app\admin\service\member\MemberService;
 use app\controller\Curd;
 use app\model\ClientFavorite;
 use app\model\Goods;
 use app\model\GoodsEvaluate;
+use app\model\Member;
 use support\exception\BusinessException;
 use support\Request;
 use support\Response;
@@ -142,7 +144,7 @@ class EvaluateController extends Curd
             if (isset($goodsEvaluateJson['photo'])){
                 foreach($goodsEvaluateJson['photo'] as $file){
                     if (isset($file['file'])){
-                        $imagesStr .= getenv('APP_IMAGE_DOMAIN').$file['file']['file_savepath'].$file['file']['file_savename'].',';
+                        $imagesStr .= getenv('APP_IMAGE_DOMAIN_LOCAL').$file['file']['file_savepath'].$file['file']['file_savename'].',';
                     }
                 }
                 $evaluate->photos = rtrim($imagesStr,',');
@@ -153,7 +155,7 @@ class EvaluateController extends Curd
         }
 
         if (!empty($evaluate->info)){
-            $evaluate->info->member_info_headimg = $evaluate->info->member_info_headimg ?? getenv('STORAGE_DOMAIN').'/images/avatar_default.png';
+            $evaluate->info->member_info_headimg = MemberService::getAvatarUrl($evaluate->info->member_info_headimg);
             $evaluate->info->member_info_nickname ?? substr($evaluate->member->member_mobile,-4).'会员';
         }
         if (!empty($evaluate->goods)){

+ 28 - 2
app/admin/controller/client/PointsController.php

@@ -2,9 +2,11 @@
 
 namespace app\admin\controller\client;
 
+use app\admin\service\member\MemberService;
 use app\controller\Curd;
 use app\model\ClientFavorite;
 use app\model\ClientPoints;
+use app\model\MemberRole;
 use support\Request;
 use support\Response;
 
@@ -36,7 +38,7 @@ class PointsController extends Curd
             ->leftJoin('member_info', 'member_info.join_info_member_id', '=', 'client_points.join_client_points_member_id')
             ->leftJoin('member_cert', 'member_cert.join_cert_member_id', '=', 'client_points.join_client_points_member_id')
             ->leftJoin('order', 'order.order_id', '=', 'client_points.join_client_points_order_id')
-            ->select('member.member_id', 'member.member_mobile','member_info.member_info_nickname','member_cert.member_cert_name', 'order.order_id', 'order.order_name', 'client_points.*');
+            ->select('member.member_id', 'member.member_mobile', 'member_info.member_info_nickname', 'member_cert.member_cert_name', 'order.order_id', 'order.order_name', 'client_points.*');
 
         foreach ($where as $column => $value) {
             if (is_array($value)) {
@@ -84,8 +86,32 @@ class PointsController extends Curd
     public function info(Request $request): Response
     {
         $pointsId = $request->get('points_id');
-        if (!$pointsId){
+        if (!$pointsId) {
             return json_fail('参数异常');
         }
+
+        $points = ClientPoints::with([
+            'orders',
+            'goods',
+            'member' => function ($query) {
+                $query->select('member_id','join_member_role_id', 'member_mobile', 'member_is_owner', 'member_is_vip', 'member_is_partner', 'member_is_referrer');
+            },
+            'memberInfo' => function ($query) {
+                $query->select('join_info_member_id', 'member_info_nickname','member_info_headimg');
+            },
+            'memberCert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            },
+        ])->where('client_points_id', $pointsId)
+            ->first();
+
+        $mobile = !empty($points->member) && !empty($points->member->member_mobile) ? $points->member->member_mobile : '';
+        $nickname = !empty($points->memberInfo) && !empty($points->memberInfo->member_info_nickname) ? $points->memberInfo->member_info_nickname : '';
+        $certname = !empty($points->memberCert) && !empty($points->memberCert->member_cert_name) ? $points->memberCert->member_cert_name : '';
+        $points->member_name = MemberService::getMemberName($mobile, $certname, $nickname);
+        $points->member_info_headimg = MemberService::getAvatarUrl(!empty($points->memberInfo) ? $points->memberInfo->member_info_headimg : '');
+        $points->role_name = !empty($points->member) && !empty($points->member_name->join_member_role_id) ? MemberRole::where('member_role_id',$points->member_name->join_member_role_id)->value('member_role_name') : '';
+
+        return json_success("success", $points);
     }
 }

+ 15 - 31
app/admin/controller/coupon/CouponController.php

@@ -394,40 +394,23 @@ class CouponController extends Curd
                         foreach ($memberList as $item) {
                             $params['member_id'] = $item;
                             if ($coupon['coupon_is_period'] == 'Y') {
-                                if (CouponDetail::where('join_coupon_detail_member_id', $item)->where('join_detail_coupon_id', $coupon['coupon_id'])->exists()) {
-                                    throw new BusinessException("请勿重复发放周期券");
-                                }
+//                                if (CouponDetail::where('join_coupon_detail_member_id', $item)->where('join_detail_coupon_id', $coupon['coupon_id'])->exists()) {
+//                                    throw new BusinessException("请勿重复发放周期券");
+//                                }
                                 $params['coupon_detail_period_num'] = 1;
                                 $periodJson = json_decode($coupon['coupon_period_json'], true);
-                                if ($periodJson['unit'] == 'day') {
-                                    $val = $periodJson['val'] - 1;
-                                    if ($val < 1) {
-                                        $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59');
-                                    } else {
-                                        $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59', strtotime("+" . $val . " day"));
-                                    }
-                                } elseif ($periodJson['unit'] == 'week') {
-                                    $val = $periodJson['val'] - 1;
-                                    if ($val < 1) {
-                                        $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime('this week Sunday'));
-                                    } else {
-                                        $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59', strtotime("+" . $val . ' week', date('Y-m-d', strtotime("+" . $val . " month"))));
-                                    }
-                                } elseif ($periodJson['unit'] == 'month') {
-                                    $val = $periodJson['val'] - 1;
-                                    if ($val < 1) {
-                                        $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59');
-                                    } else {
-                                        $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59', strtotime("+" . $val . " month"));
-                                    }
-                                }
+//                                for ($i=0;$i<$periodJson['nbr'];$i++){
+                                CouponDetailService::sendPeriodCoupon($params);
+//                                }
+
+                                continue;
                             }
                             if ($chooseCoupon['nbr'] > 0) {
                                 // 有发行数量
                                 if ($coupon['coupon_number'] != 0) {
                                     // 查询还有多少张没领的
                                     $count = CouponDetail::where('join_detail_coupon_id', $coupon['coupon_id'])
-                                        ->whereIn('coupon_detail_status',['INIT','PENDING'])
+                                        ->whereIn('coupon_detail_status', ['INIT', 'PENDING'])
                                         ->count();
                                     if (!isset($couponNbr[$coupon['coupon_id']])) {
                                         $couponNbr[$coupon['coupon_id']] = $count;
@@ -439,7 +422,7 @@ class CouponController extends Curd
                                     $params['chooseCouponNbr'] = $chooseCoupon['nbr'];
                                     // 匹配已发行的优惠券
                                     CouponDetailService::customSendCouponHave($params);
-                                }else{
+                                } else {
                                     for ($i = 0; $i < $chooseCoupon['nbr']; $i++) {
                                         CouponDetailService::customSendCoupon($params);
                                     }
@@ -456,6 +439,7 @@ class CouponController extends Curd
             Db::rollBack();
             return json_fail($e->getMessage());
         } catch (\Exception $e) {
+            dump($e->getTrace());
             Db::rollBack();
             return json_fail('优惠券发放失败');
 
@@ -860,16 +844,16 @@ class CouponController extends Curd
     public function disableCoupon(Request $request)
     {
         $couponId = $request->post('coupon_id');
-        if (!$couponId){
+        if (!$couponId) {
             return json_fail("参数异常");
         }
         Db::beginTransaction();
         try {
-            Coupon::where('coupon_id',$couponId)->update(['coupon_status'=>'DISABLED']);
-            CouponDetail::where('join_detail_coupon_id',$couponId)->whereIn('coupon_detail_status',['INIT','PENDING'])->update(['coupon_detail_status'=>'DISABLED']);
+            Coupon::where('coupon_id', $couponId)->update(['coupon_status' => 'DISABLED']);
+            CouponDetail::where('join_detail_coupon_id', $couponId)->whereIn('coupon_detail_status', ['INIT', 'PENDING'])->update(['coupon_detail_status' => 'DISABLED']);
             Db::commit();
             return json_success('success');
-        }catch (\Exception $e){
+        } catch (\Exception $e) {
             Db::rollBack();
             return json_fail("禁用失败");
         }

+ 16 - 7
app/admin/controller/coupon/CouponDetailController.php

@@ -126,6 +126,12 @@ class CouponDetailController extends Curd
                     $item['gettype'] = $couponDetailExtendJson['gettype'];
                 }
             }
+            if (!empty($item['coupon_detail_deadline_datetime'])) {
+                $item['coupon_detail_deadline_datetime'] = date('Y/m/d H:i:s', strtotime($item['coupon_detail_deadline_datetime']));
+            }
+            if (!empty($item['coupon_detail_gain_datetime'])) {
+                $item['coupon_detail_gain_datetime'] = date('Y/m/d H:i:s', strtotime($item['coupon_detail_gain_datetime']));
+            }
         }
 
         return $items;
@@ -215,13 +221,16 @@ class CouponDetailController extends Curd
                 $query->select('coupon_id', 'coupon_name', 'coupon_classify', 'coupon_value', 'coupon_category', 'coupon_minimum_limit');
             }
         ])->whereIn('join_detail_coupon_id', $couponIds)
-            ->where('join_coupon_detail_member_id', $memberId);
-            if ($settlementNow == 'Y') {
-                $details = $details->whereIn('coupon_detail.coupon_detail_status', ['ACTIVED', 'WAITING']);
-            } else {
-                $details = $details->where('coupon_detail.coupon_detail_status', 'ACTIVED');
-            }
-            $details = $details->selectRaw('join_detail_coupon_id,join_coupon_detail_member_id,COUNT(*) as count')
+            ->where('join_coupon_detail_member_id', $memberId)
+            ->where('coupon_detail_gain_datetime','<',date('Y-m-d H:i:s'))
+            ->where('coupon_detail_deadline_datetime','>',date('Y-m-d H:i:s'));
+
+        if ($settlementNow == 'Y') {
+            $details = $details->whereIn('coupon_detail.coupon_detail_status', ['ACTIVED', 'WAITING']);
+        } else {
+            $details = $details->where('coupon_detail.coupon_detail_status', 'ACTIVED');
+        }
+        $details = $details->selectRaw('join_detail_coupon_id,join_coupon_detail_member_id,COUNT(*) as count')
             ->groupBy('join_detail_coupon_id', 'join_coupon_detail_member_id')
             ->get()
             ->toArray();

+ 178 - 0
app/admin/controller/finance/CommissionListController.php

@@ -0,0 +1,178 @@
+<?php
+
+namespace app\admin\controller\finance;
+
+use app\admin\service\goods\GoodsSkuService;
+use app\admin\service\member\MemberService;
+use app\controller\Curd;
+use app\model\Member;
+use app\model\MemberAccountList;
+use app\model\Order;
+use app\model\OrderSheet;
+use support\Request;
+use support\Response;
+
+class CommissionListController extends Curd
+{
+    public function __construct()
+    {
+        $this->model = new MemberAccountList();
+    }
+
+    public function select(Request $request): Response
+    {
+        [$where, $format, $limit, $field, $order] = $this->selectInput($request);
+        $order = $request->get('order', 'desc');
+        $field = $field ?? 'member_account_list_addtimes';
+        $query = $this->doSelect($where, $field, $order);
+        return $this->doFormat($query, $format, $limit);
+    }
+
+    protected function doSelect(array $where, string $field = null, string $order = 'desc')
+    {
+        $model = $this->model->with([
+            'member' => function ($query) {
+                $query->select('member_id', 'member_mobile', 'member_is_owner', 'member_is_vip', 'member_is_partner', 'member_is_referrer');
+            },
+            'memberInfo' => function ($query) {
+                $query->select('join_info_member_id', 'member_info_nickname', 'member_info_headimg');
+            },
+            'memberCert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            }
+        ]);
+        foreach ($where as $column => $value) {
+            if (is_array($value)) {
+                if ($value[0] === 'like' || $value[0] === 'not like') {
+                    $model = $model->where($column, $value[0], "%$value[1]%");
+                } elseif (in_array($value[0], ['>', '=', '<', '<>'])) {
+                    $model = $model->where($column, $value[0], $value[1]);
+                } elseif ($value[0] == 'in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereIn($column, $valArr);
+                } elseif ($value[0] == 'not in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereNotIn($column, $valArr);
+                } elseif ($value[0] == 'null') {
+                    $model = $model->whereNull($column);
+                } elseif ($value[0] == 'not null') {
+                    $model = $model->whereNotNull($column);
+                } elseif ($value[0] !== '' || $value[1] !== '') {
+                    $model = $model->whereBetween($column, $value);
+                }
+            } else {
+                $model = $model->where($column, $value);
+            }
+        }
+        if ($field) {
+            $model = $model->orderBy($field, $order);
+        }
+        return $model;
+    }
+
+    public function afterQuery($items)
+    {
+        foreach ($items as &$item) {
+            $item['member_mobile'] = isset($item['member']) ? $item['member']['member_mobile'] : '';
+            $item['member_info_nickname'] = isset($item['memberInfo']) && !empty($item['memberInfo']['member_info_nickname']) ? $item['memberInfo']['member_info_nickname'] : '';
+            $item['member_cert_name'] = isset($item['memberCert']) && !empty($item['memberCert']['member_cert_name']) ? $item['memberCert']['member_cert_name'] : '';
+            $item['member_name'] = MemberService::getMemberName($item['member_mobile'], $item['member_cert_name'], $item['member_info_nickname']);
+            $item['member_info_headimg'] = isset($item['memberInfo']) && !empty($item['memberInfo']['member_info_headimg']) ? $item['memberInfo']['member_info_headimg'] : "";
+            $item['member_is_owner'] = isset($item['member']) ? $item['member']['member_is_owner'] : '';
+            $item['member_is_vip'] = isset($item['member']) ? $item['member']['member_is_vip'] : '';
+            $item['member_is_partner'] = isset($item['member']) ? $item['member']['member_is_partner'] : '';
+            $item['member_is_referrer'] = isset($item['member']) ? $item['member']['member_is_referrer'] : '';
+
+            unset($item['member'], $item['memberInfo'], $item['memberCert']);
+        }
+
+        return $items;
+    }
+
+    /**
+     * @Desc 记录详情
+     * @Author Gorden
+     * @Date 2024/9/28 17:15
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function info(Request $request): Response
+    {
+        $id = $request->get('member_account_list_id');
+        if (!$id) {
+            return json_fail("参数异常");
+        }
+
+        $info = MemberAccountList::where('member_account_list_id', $id)->first();
+        $infoMember = Member::with([
+            'role' => function ($query) {
+                $query->select('member_role_id', 'member_role_name');
+            }
+        ])->where('member_id', $info->join_member_account_list_member_id)
+            ->first();
+        $info->member_role_name = !empty($infoMember->role) ? $infoMember->role->member_role_name : '';
+        if (!empty($info->member_account_list_json)) {
+            $accountListJson = json_decode($info->member_account_list_json, true);
+            // 下单人
+            if (isset($accountListJson['master_member_id'])) {
+                $member = Member::with([
+                    'info' => function ($query) {
+                        $query->select('join_info_member_id', 'member_info_nickname');
+                    },
+                    'cert' => function ($query) {
+                        $query->select('join_cert_member_id', 'member_cert_name');
+                    },
+                    'role' => function ($query) {
+                        $query->select('member_role_id', 'member_role_name');
+                    }
+                ])->where('member_id', $accountListJson['master_member_id'])
+                    ->first();
+                $info->master_nickname = isset($member->info) && !empty($member->info->member_info_nickname) ? $member->info->member_info_nickname : MemberService::getMemberNickname($member->member_mobile ?? '');
+                $info->master_certname = isset($member->cert) && !empty($member->cert->member_cert_name) ? $member->cert->member_cert_name : '';
+                $info->master_mobile = isset($member) && !empty($member->member_mobile) ? $member->member_mobile : '';
+                $info->master_member_name = MemberService::getMemberName($info->master_mobile, $info->master_certname, $info->master_nickname);
+                $info->master_role_name = !empty($member->role) ? $member->role->member_role_name : '';
+                $info->join_member_role_id = $member->join_member_role_id ?? '';
+                $info->member_is_owner = $member->member_is_owner ?? '';
+                $info->member_is_vip = $member->member_is_vip ?? '';
+                $info->member_is_partner = $member->member_is_partner ?? '';
+                $info->member_is_referrer = $member->member_is_referrer ?? '';
+            }
+            // 订单
+            if (isset($accountListJson['order_id'])) {
+                $info->order = Order::where('order_id', $accountListJson['order_id'])
+                    ->select('order_id','order_groupby','order_amount_total','order_amount_pay','order_addtimes')
+                    ->first();
+                $sheets = OrderSheet::with([
+                    'goods'=>function($query){
+                        $query->select('goods_id','goods_name','goods_sales_price','goods_cover');
+                    },
+                    'sku' => function($query){
+                        $query->select('goods_sku_id','goods_sku_specs_json');
+                    }
+                ])->where('join_sheet_order_id',$accountListJson['order_id'])
+                    ->select('join_sheet_order_id','join_sheet_goods_id','join_sheet_goods_sku_id','order_sheet_num','order_sheet_price','order_sheet_amount','order_sheet_addtimes')
+                    ->get()
+                    ->toArray();
+                foreach ($sheets as &$sheet){
+                    if (!empty($sheet['goods']) && !empty($sheet['goods']['goods_cover'])){
+                        $sheet['goods']['goods_cover'] = getenv('STORAGE_DOMAIN').$sheet['goods']['goods_cover'];
+                    }
+                    if (!empty($sheet['sku'])){
+                        $sheet['sku']['sku_title'] = GoodsSkuService::getSkuTitle($sheet['sku']['goods_sku_specs_json']);
+                    }
+                }
+                $info->sheet = $sheets;
+            }
+        }
+
+        return json_success('success', $info);
+    }
+}

+ 202 - 0
app/admin/controller/finance/WithdrawalListController.php

@@ -0,0 +1,202 @@
+<?php
+
+namespace app\admin\controller\finance;
+
+use app\admin\service\goods\GoodsSkuService;
+use app\admin\service\member\MemberService;
+use app\controller\Curd;
+use app\model\ClientConfig;
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\MemberAccountList;
+use app\model\Order;
+use app\model\OrderSheet;
+use support\Db;
+use support\exception\BusinessException;
+use support\Request;
+use support\Response;
+use Webman\Event\Event;
+
+class WithdrawalListController extends Curd
+{
+    public function __construct()
+    {
+        $this->model = new MemberAccountList();
+    }
+
+    public function select(Request $request): Response
+    {
+        [$where, $format, $limit, $field, $order] = $this->selectInput($request);
+        $order = $request->get('order', 'desc');
+        $field = $field ?? 'member_account_list_addtimes';
+        $query = $this->doSelect($where, $field, $order);
+        return $this->doFormat($query, $format, $limit);
+    }
+
+    protected function doSelect(array $where, string $field = null, string $order = 'desc')
+    {
+        $model = $this->model->with([
+            'member' => function ($query) {
+                $query->select('member_id', 'member_mobile', 'member_is_owner', 'member_is_vip', 'member_is_partner', 'member_is_referrer');
+            },
+            'memberInfo' => function ($query) {
+                $query->select('join_info_member_id', 'member_info_nickname', 'member_info_headimg');
+            },
+            'memberCert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            }
+        ]);
+        foreach ($where as $column => $value) {
+            if (is_array($value)) {
+                if ($value[0] === 'like' || $value[0] === 'not like') {
+                    $model = $model->where($column, $value[0], "%$value[1]%");
+                } elseif (in_array($value[0], ['>', '=', '<', '<>'])) {
+                    $model = $model->where($column, $value[0], $value[1]);
+                } elseif ($value[0] == 'in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereIn($column, $valArr);
+                } elseif ($value[0] == 'not in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereNotIn($column, $valArr);
+                } elseif ($value[0] == 'null') {
+                    $model = $model->whereNull($column);
+                } elseif ($value[0] == 'not null') {
+                    $model = $model->whereNotNull($column);
+                } elseif ($value[0] !== '' || $value[1] !== '') {
+                    $model = $model->whereBetween($column, $value);
+                }
+            } else {
+                $model = $model->where($column, $value);
+            }
+        }
+        if ($field) {
+            $model = $model->orderBy($field, $order);
+        }
+        return $model;
+    }
+
+    public function afterQuery($items)
+    {
+        foreach ($items as &$item) {
+            $item['member_mobile'] = isset($item['member']) ? $item['member']['member_mobile'] : '';
+            $item['member_info_nickname'] = isset($item['memberInfo']) && !empty($item['memberInfo']['member_info_nickname']) ? $item['memberInfo']['member_info_nickname'] : '';
+            $item['member_cert_name'] = isset($item['memberCert']) && !empty($item['memberCert']['member_cert_name']) ? $item['memberCert']['member_cert_name'] : '';
+            $item['member_name'] = MemberService::getMemberName($item['member_mobile'], $item['member_cert_name'], $item['member_info_nickname']);
+            $item['member_info_headimg'] = isset($item['memberInfo']) && !empty($item['memberInfo']['member_info_headimg']) ? $item['memberInfo']['member_info_headimg'] : "";
+            $item['member_is_owner'] = isset($item['member']) ? $item['member']['member_is_owner'] : '';
+            $item['member_is_vip'] = isset($item['member']) ? $item['member']['member_is_vip'] : '';
+            $item['member_is_partner'] = isset($item['member']) ? $item['member']['member_is_partner'] : '';
+            $item['member_is_referrer'] = isset($item['member']) ? $item['member']['member_is_referrer'] : '';
+
+            unset($item['member'], $item['memberInfo'], $item['memberCert']);
+        }
+
+        return $items;
+    }
+
+    /**
+     * @Desc 记录详情
+     * @Author Gorden
+     * @Date 2024/9/28 17:15
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function info(Request $request): Response
+    {
+        $id = $request->get('member_account_list_id');
+        if (!$id) {
+            return json_fail("参数异常");
+        }
+
+        $info = MemberAccountList::where('member_account_list_id', $id)->first();
+        $infoMember = Member::with([
+            'role' => function ($query) {
+                $query->select('member_role_id', 'member_role_name');
+            }
+        ])->where('member_id', $info->join_member_account_list_member_id)
+            ->first();
+        $info->member_role_name = !empty($infoMember->role) ? $infoMember->role->member_role_name : '';
+        // 获取银行卡信息
+        if (!empty($info->member_account_list_json)) {
+            $accountListJson = json_decode($info->member_account_list_json, true);
+            $info->banks = [
+                [
+                    'bank_name' => $accountListJson['bank_name'] ?? '',
+                    'username' => $accountListJson['bank_acount'] ?? '',
+                    'cardno' => $accountListJson['bank_cardno'] ?? ''
+                ]
+            ];
+        }
+//        $info->banks = ClientConfig::where('join_client_config_member_id',$info->join_member_account_list_member_id)->where('client_config_key','client-bank')->get()->toArray();
+
+        return json_success('success', $info);
+    }
+
+    public function changeStatus(Request $request)
+    {
+        $memberAccountListId = $request->post('member_account_list_id');
+        $status = $request->post('status');
+        $password = $request->post('pay_pwd');
+        if ($status == 'ACTIVED' && $password != '888666') {
+            return json_fail('密码错误');
+        }
+
+        Db::beginTransaction();
+        try {
+            $withdraw = MemberAccountList::where('member_account_list_id', $memberAccountListId)->first();
+            $withdraw->member_account_list_status = $status;
+            $memberAccountListJson = [];
+            if (!empty($withdraw->member_account_list_json)) {
+                $memberAccountListJson = json_decode($withdraw->member_account_list_json, true);
+            }
+            if ($status == 'ACTIVED') {
+                $memberAccountListJson['resulttime'] = date('Y-m-d H:i:s');
+                // 扣账户余额
+                $account = MemberAccount::where('join_account_member_id', $withdraw->join_member_account_list_member_id)
+                    ->where('member_account_classify', 'CASH')
+                    ->where('member_account_status', 'ACTIVED')
+                    ->first();
+                if (empty($account)) {
+                    throw new BusinessException("账户异常");
+                }
+                if ($account->member_account_surplus < $withdraw->member_account_list_amount) {
+                    throw new BusinessException("账户余额不足");
+                }
+                $account->member_account_surplus = $account->member_account_surplus - $withdraw->member_account_list_amount;
+                $account->member_account_expend = $account->member_account_expend + $withdraw->member_account_list_amount;
+                $account->save();
+            }
+            $withdraw->save();
+
+            _syslog('提现', '修改状态成功');
+
+            Db::commit();
+
+            if ($status == 'ACTIVED') {
+                // 入收支记录
+                $params['account_list_id'] = $memberAccountListId;
+                $params['type'] = 'withdraw';
+                Event::dispatch('statistics.inout.out',$params);
+            }
+
+            return json_success('success');
+        } catch (BusinessException $e) {
+
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+
+            Db::rollBack();
+            return json_fail("修改状态失败");
+        }
+
+
+    }
+}

+ 57 - 0
app/admin/controller/goods/NewCustomerController.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace app\admin\controller\goods;
+
+use app\admin\service\goods\GoodsService;
+use app\admin\service\goods\NewCustomerService;
+use app\admin\validate\goods\GoodsValidate;
+use app\model\Coupon;
+use app\model\Goods;
+use app\model\GoodsDetail;
+use app\model\GoodsLabel;
+use app\model\GoodsRunning;
+use app\model\GoodsSku;
+use app\model\SysCategory;
+use support\Db;
+use support\exception\BusinessException;
+use support\Redis;
+use support\Request;
+use support\Response;
+
+class NewCustomerController
+{
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function info(Request $request)
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('info')->check($request->get())) {
+            return json_fail($validate->getError());
+        }
+
+        return NewCustomerService::info($request->get('goods_id'));
+    }
+    /**
+     * @Desc 修改商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:22
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function update(Request $request): Response
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('update')->check($request->post())) {
+            return json_fail($validate->getError());
+        }
+
+        return NewCustomerService::update($request->post());
+    }
+}

+ 45 - 0
app/admin/controller/goods/PartnerController.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace app\admin\controller\goods;
+
+use app\admin\service\goods\PartnerService;
+use app\admin\validate\goods\GoodsValidate;
+use support\Request;
+use support\Response;
+
+class PartnerController
+{
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function info(Request $request)
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('info')->check($request->get())) {
+            return json_fail($validate->getError());
+        }
+
+        return PartnerService::info($request->get('goods_id'));
+    }
+    /**
+     * @Desc 修改商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:22
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function update(Request $request): Response
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('update')->check($request->post())) {
+            return json_fail($validate->getError());
+        }
+        return PartnerService::update($request->post());
+    }
+}

+ 45 - 0
app/admin/controller/goods/ReferrerController.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace app\admin\controller\goods;
+
+use app\admin\service\goods\ReferrerService;
+use app\admin\validate\goods\GoodsValidate;
+use support\Request;
+use support\Response;
+
+class ReferrerController
+{
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function info(Request $request)
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('info')->check($request->get())) {
+            return json_fail($validate->getError());
+        }
+
+        return ReferrerService::info($request->get('goods_id'));
+    }
+    /**
+     * @Desc 修改商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:22
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function update(Request $request): Response
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('update')->check($request->post())) {
+            return json_fail($validate->getError());
+        }
+        return ReferrerService::update($request->post());
+    }
+}

+ 106 - 0
app/admin/controller/goods/TravelGoodsController.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace app\admin\controller\goods;
+
+use app\admin\service\goods\GoodsService;
+use app\admin\service\goods\TravelService;
+use app\admin\validate\goods\GoodsValidate;
+use app\model\Goods;
+use support\Request;
+use support\Response;
+
+class TravelGoodsController
+{
+    /**
+     * @Desc 列表
+     * @Author Gorden
+     * @Date 2024/3/28 10:08
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function select(Request $request)
+    {
+        return GoodsService::select($request, 'SERVICE');
+    }
+
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function info(Request $request)
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('info')->check($request->get())) {
+            return json_fail($validate->getError());
+        }
+
+        return GoodsService::info($request->get('goods_id'));
+    }
+
+    /**
+     * @Desc 添加商品
+     * @Author Gorden
+     * @Date 2024/3/11 10:21
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function insert(Request $request): Response
+    {
+        $params = $request->post();
+        $validate = new GoodsValidate();
+        if (!$validate->scene('add')->check($params)) {
+            return json_fail($validate->getError());
+        }
+
+        return TravelService::insert($params);
+    }
+
+    /**
+     * @Desc 修改商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:22
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function update(Request $request): Response
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('update')->check($request->post())) {
+            return json_fail($validate->getError());
+        }
+
+        return TravelService::update($request->post());
+    }
+
+    public function changeStatus(Request $request): Response
+    {
+        $validate = new GoodsValidate();
+        if (!$validate->scene('changeStatus')->check($request->post())) {
+            return json_fail($validate->getError());
+        }
+
+        return GoodsService::changeStatus($request->post());
+    }
+
+    /**
+     * @Desc 删除商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:22
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function delete(Request $request)
+    {
+        $ids = $request->post('goods_id');
+
+        return GoodsService::delete($ids);
+    }
+}

+ 2 - 0
app/admin/controller/member/BenefitController.php

@@ -330,6 +330,8 @@ class BenefitController extends Curd
             if (!empty($quota['appointment_done_json'])) {
                 $doneJson = json_decode($quota['appointment_done_json'], true);
                 $quota['premises'] = $doneJson['charge']['charge_premises'] ?? '';
+                $quota['remark'] = $doneJson['charge']['charge_content'] ?? '';
+                $quota['charge_waiter'] = $doneJson['charge']['charge_waiter'] ?? '';
                 if (isset($doneJson['charge']['charge_user_id'])) {
                     $user = SysUser::where('user_id', $doneJson['charge']['charge_user_id'])
                         ->select('user_id', 'user_name')

+ 83 - 1
app/admin/controller/member/MemberController.php

@@ -378,6 +378,16 @@ class MemberController
         return MemberService::welfareInfo($request);
     }
 
+    public function commissionList(Request $request)
+    {
+        $memberId = $request->get('member_id');
+        if (!$memberId) {
+            return json_fail("参数错误");
+        }
+
+        return MemberService::commissionList($request);
+    }
+
     /**
      * @Desc 我的粉丝列表
      * @Author Gorden
@@ -499,7 +509,7 @@ class MemberController
         $memberId = $request->get('member_id', '');
         $coupons = CouponDetail::leftJoin('coupon', 'coupon.coupon_id', '=', 'coupon_detail.join_detail_coupon_id')
             ->where('join_coupon_detail_member_id', $memberId)
-            ->select('coupon.coupon_name', 'coupon.coupon_classify')
+            ->select('coupon.coupon_name', 'coupon.coupon_classify','coupon.coupon_value')
             ->get()
             ->toArray();
         $data = ['manjian' => 0, 'diyong' => 0, 'zhekou' => 0, 'lijian' => 0, 'zengpin' => 0, 'fuli' => 0, 'nianka' => 0, 'jika' => 0, 'yueka' => 0];
@@ -603,6 +613,78 @@ class MemberController
         return json_success('', compact('rows', 'page', 'pageSize', 'total'));
     }
 
+    /**
+     * @Desc 我的卡券
+     * @Author Gorden
+     * @Date 2024/8/22 18:45
+     *
+     * @param Request $request
+     * @return \support\Response
+     */
+    public function allCoupon(Request $request)
+    {
+        $memberId = $request->get('member_id', '');
+        $page = $request->get('page', 1);
+        $pageSize = $request->get('pageSize', 20);
+
+        $coupons = CouponDetail::with([
+            'member' => function ($query) {
+                $query->select('member_id', 'member_mobile');
+            },
+            'cert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            },
+            'info' => function ($query) {
+                $query->select('join_info_member_id', 'member_info_nickname');
+            }
+        ])
+            ->leftJoin('coupon', 'coupon.coupon_id', '=', 'coupon_detail.join_detail_coupon_id')
+            ->where('join_coupon_detail_member_id', $memberId)
+            ->groupBy('join_coupon_detail_member_id', 'join_detail_coupon_id');
+//        $total = $coupons->count();
+        $totalModel = Db::select("select count(a.join_detail_coupon_id) as total from (select join_detail_coupon_id from app_coupon_detail as d left join app_coupon as c ON d.join_detail_coupon_id = c.coupon_id where d.join_coupon_detail_member_id='" . $memberId . "' group by d.join_coupon_detail_member_id,d.join_detail_coupon_id) as a");
+        $total = 0;
+        if (!empty($totalModel) && !empty($totalModel[0])) {
+            $total = $totalModel[0]->total;
+        }
+        $rows = $coupons->select('coupon_detail.join_coupon_detail_member_id', 'coupon_detail.join_detail_coupon_id'
+            , 'coupon.coupon_name', 'coupon.coupon_classify', 'coupon.coupon_value')
+            ->selectRaw('COUNT(1) as total, 
+                COUNT(IF(app_coupon_detail.coupon_detail_status="USED",1,NULL)) as used_total, 
+                COUNT(IF(app_coupon_detail.coupon_detail_status="ACTIVED" or app_coupon_detail.coupon_detail_status="WAITING",1,NULL)) as unused_total,
+                MAX(app_coupon_detail.coupon_detail_deadline_datetime) as deadline_datetime')
+            ->orderBy('deadline_datetime', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+
+        foreach ($rows as &$item) {
+            $mobile = $certName = $nickname = '';
+            if (!empty($item['member']) && !empty($item['member']['member_mobile'])) {
+                $mobile = $item['member']['member_mobile'];
+            }
+            if (!empty($item['cert']) && !empty($item['cert']['member_cert_name'])) {
+                $certName = $item['cert']['member_cert_name'];
+            }
+            if (!empty($item['info']) && !empty($item['info']['member_info_nickname'])) {
+                $nickname = $item['info']['member_info_nickname'];
+            }
+            $item['member_name'] = MemberService::getMemberName($mobile, $certName, $nickname);
+            unset($item['member'], $item['cert'], $item['info']);
+
+            $item['invalid_total'] = $item['total'] - $item['used_total'] - $item['unused_total'];
+            // 优惠券分类
+            $coupon = Coupon::with('category')->where('coupon_id', $item['join_detail_coupon_id'])
+                ->select('coupon_id', 'join_coupon_category_id')
+                ->first();
+            if (!empty($coupon) && !empty($coupon->category)) {
+                $item['category_name'] = $coupon->category->category_name;
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
     /**
      * @Desc 我的卡券 领取记录
      * @Author Gorden

+ 8 - 6
app/admin/controller/member/QuotaController.php

@@ -49,7 +49,11 @@ class QuotaController extends Curd
         }
 
         if (!empty($memberId)) {
-            $memberIds = $memberId;
+            if (is_array($memberId)) {
+                $memberIds = $memberId;
+            }else{
+                $memberIds = [$memberId];
+            }
         }
 
         foreach ($memberIds as &$id) {
@@ -57,10 +61,6 @@ class QuotaController extends Curd
         }
 
         $rows = MemberQuota::select('join_quota_member_id', 'join_member_rule_added_component_id', Db::raw('MAX(`member_quota_addtimes`) as `new_addtimes`'))
-
-//            ->when(!empty($memberIds), function ($query) use ($placeholders,$pars) {
-//                $query->whereRaw("join_quota_member_id in ($placeholders))", $pars);
-//            })
             ->when(!empty($memberIds), function ($query) use ($memberIds) {
                 $query->whereIn('join_quota_member_id', $memberIds);
             })
@@ -231,6 +231,7 @@ class QuotaController extends Curd
                 $usedJson = json_decode($quota['member_quota_used_json'], true);
                 $quota['premises'] = $usedJson['charge']['charge_premises'] ?? '';
                 $quota['remark'] = $usedJson['charge']['charge_content'] ?? '';
+                $quota['charge_waiter'] = $usedJson['charge']['charge_waiter'] ?? '';
                 if (isset($usedJson['charge']['charge_user_id'])) {
                     $user = SysUser::where('user_id', $usedJson['charge']['charge_user_id'])
                         ->select('user_id', 'user_name')
@@ -288,7 +289,6 @@ class QuotaController extends Curd
         if ($redisCode != $code) {
             return json_fail("验证码错误,请重新输入");
         }
-        Redis::del($key);
 
         if (!$times) {
             $params['times'] = date('Y-m-d H:i:s');
@@ -319,6 +319,8 @@ class QuotaController extends Curd
                     MemberQuota::insert($quota);
                 }
             }
+            // 清除验证码
+            Redis::del($key);
 
             _syslog("核销", "核销成功");
 

+ 71 - 13
app/admin/controller/member/WriteOffController.php

@@ -2,14 +2,17 @@
 
 namespace app\admin\controller\member;
 
+use app\admin\service\member\MemberService;
 use app\controller\Curd;
 use app\model\Appointment;
+use app\model\Member;
 use app\model\MemberBenefit;
 use app\model\MemberQuota;
 use app\model\RuleAddedComponent;
 use app\model\SysUser;
 use support\Db;
 use support\Request;
+use support\Response;
 
 class WriteOffController extends Curd
 {
@@ -26,13 +29,13 @@ class WriteOffController extends Curd
         $userId = $request->get('user_id', '');
         $premises = $request->get('premises', '');
         $date = $request->get('date', []);
-        $memberId = $request->get('member_id','');
+        $memberId = $request->get('member_id', '');
 
         $appointmentList = Db::table('appointment')
             ->leftJoin('member', 'member.member_id', '=', 'appointment.join_appointment_member_id')
             ->leftJoin('member_cert', 'member_cert.join_cert_member_id', '=', 'appointment.join_appointment_member_id')
             ->select('appointment.appointment_id as quota_id', 'appointment.join_appointment_member_id as member_id', 'appointment.appointment_classify as classify', 'appointment.appointment_done_json as used_json', 'appointment.appointment_done_datetime as used_time', 'appointment.join_appointment_member_benefit_id as benefit_id',
-                'member.member_mobile', 'member_cert.member_cert_name','appointment_extend_json as extend_json'
+                'member.member_mobile', 'member_cert.member_cert_name', 'appointment_extend_json as extend_json'
             )
             ->when($keywords != '', function ($query) use ($keywords) {
                 $query->where('member.member_mobile', 'like', '%' . $keywords . '%');
@@ -44,15 +47,15 @@ class WriteOffController extends Curd
                 $query->whereJsonContains('appointment.appointment_done_json->charge->charge_user_id', $userId);
             })->when($premises != '', function ($query) use ($premises) {
                 $query->whereJsonContains('appointment.appointment_done_json->charge->charge_premises', $premises);
-            })->when(!empty($memberId),function ($query) use ($memberId){
-                $query->where('join_appointment_member_id',$memberId);
+            })->when(!empty($memberId), function ($query) use ($memberId) {
+                $query->where('join_appointment_member_id', $memberId);
             })
             ->where('appointment_status', 'DONE');
         $quotaList = Db::table('member_quota')
             ->leftJoin('member', 'member.member_id', '=', 'member_quota.join_quota_member_id')
             ->leftJoin('member_cert', 'member_cert.join_cert_member_id', '=', 'member_quota.join_quota_member_id')
             ->select('member_quota.member_quota_id as quota_id', 'member_quota.join_quota_member_id as member_id', 'member_quota.member_quota_category as classify', 'member_quota.member_quota_used_json as used_json', "member_quota.member_quota_extend_json->writeOffTime as used_time", "member_quota.join_member_rule_added_component_id as benefit_id",
-                'member.member_mobile', 'member_cert.member_cert_name','member_quota_extend_json as extend_json')
+                'member.member_mobile', 'member_cert.member_cert_name', 'member_quota_extend_json as extend_json')
             ->when($keywords != '', function ($query) use ($keywords) {
                 $query->where('member.member_mobile', 'like', '%' . $keywords . '%');
             })->when(!empty($date), function ($query) use ($date) {
@@ -94,9 +97,9 @@ class WriteOffController extends Curd
                 }
             }
             $remark = [];
-            if (!empty($item->extend_json)){
-                $extendJson = json_decode($item->extend_json,true);
-                if (isset($extendJson['remark'])){
+            if (!empty($item->extend_json)) {
+                $extendJson = json_decode($item->extend_json, true);
+                if (isset($extendJson['remark'])) {
                     $remark = $extendJson['remark'];
                 }
             }
@@ -116,7 +119,7 @@ class WriteOffController extends Curd
                 'member_mobile' => $item->member_mobile ? $item->member_mobile : '',
                 'classify' => $classify,
                 'nbr' => 1,
-                'remark'=>$remark
+                'remark' => $remark
             ];
 
         }
@@ -124,6 +127,61 @@ class WriteOffController extends Curd
         return json_success('', compact('rows', 'page', 'pageSize', 'total'));
     }
 
+    /**
+     * @Desc 核销详情
+     * @Author Gorden
+     * @Date 2024/10/10 14:00
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function info(Request $request): Response
+    {
+        $quotaId = $request->get('quota_id');
+        if (!$quotaId) {
+            return json_fail("参数异常");
+        }
+
+        if (substr($quotaId, 0, 2) == 'AP') {
+            $quota = Appointment::where('appointment_id', $quotaId)
+                ->select('appointment_id', 'join_appointment_member_id as member_id')
+                ->first();
+        } elseif (substr($quotaId, 0, 2) == 'MQ') {
+            $quota = MemberQuota::where('member_quota_id', $quotaId)
+                ->select('member_quota_id', 'join_quota_member_id as member_id')
+                ->first();
+        } else {
+            return json_fail("参数异常");
+        }
+
+        $member = Member::with([
+            'cert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            },
+            'info' => function ($query) {
+                $query->select('join_info_member_id', 'member_info_nickname', 'member_info_headimg');
+            }
+        ])->where('member_id', $quota['member_id'])
+            ->select('member_id', 'member_mobile', 'join_member_role_id', 'member_is_owner', 'member_is_vip', 'member_is_partner', 'member_is_referrer')
+            ->first();
+        if (!$member){
+            return json_fail('数据异常');
+        }
+        $member = $member->toArray();
+        $certName = $member['cert']['member_cert_name'] ?? '';
+        $nickname = $member['info']['member_info_nickname'] ?? MemberService::getMemberNickname($member['member_mobile'] ?? '');
+        $mobile = $member['member_mobile'] ?? '';
+        $member['member_name'] = MemberService::getMemberCertName($mobile, $certName, $nickname);
+        $member['info']['member_info_headimg'] = MemberService::getAvatarUrl($member['info']['member_info_headimg'] ?? '');
+        $member['level'] = MemberService::getRoleName($member['join_member_role_id']);
+
+        $data = [
+            'member'=>$member
+        ];
+
+        return json_success('success',$data);
+    }
+
     /**
      * @Desc 核销备注
      * @Author Gorden
@@ -160,11 +218,11 @@ class WriteOffController extends Curd
                 'business' => $request->post('business', ''),
                 'service' => $request->post('service', '')
             ];
-            if (!empty($appointment)){
-                $appointment->appointment_extend_json = json_encode($extendJson,JSON_UNESCAPED_UNICODE);
+            if (!empty($appointment)) {
+                $appointment->appointment_extend_json = json_encode($extendJson, JSON_UNESCAPED_UNICODE);
                 $appointment->save();
-            }elseif (!empty($quota)){
-                $quota->member_quota_extend_json = json_encode($extendJson,JSON_UNESCAPED_UNICODE);
+            } elseif (!empty($quota)) {
+                $quota->member_quota_extend_json = json_encode($extendJson, JSON_UNESCAPED_UNICODE);
                 $quota->save();
             }
 

+ 21 - 0
app/admin/controller/notify/RechargeController.php

@@ -5,11 +5,13 @@ namespace app\admin\controller\notify;
 use app\admin\service\notify\RechargeService;
 use app\model\Member;
 use app\model\MemberAccount;
+use app\model\MemberAccountList;
 use app\model\Order;
 use app\model\PayDetail;
 use support\Db;
 use support\exception\BusinessException;
 use support\Request;
+use Webman\Event\Event;
 
 class RechargeController
 {
@@ -145,6 +147,25 @@ class RechargeController
 //                    RechargeService::disposeAdded($roleId, $payDetail->join_pay_member_id);
 //                }
 //            }
+
+            // 计算充值提成
+            if (!empty($payDetail->join_pay_object_json)) {
+                $payObjectJson = json_decode($payDetail->join_pay_object_json, true);
+                if (isset($payObjectJson['order_id'])) {
+                    // 没有提成过
+                    if (!MemberAccountList::whereJsonContains('member_account_list_json->order_id', $payObjectJson['order_id'])->exists()) {
+                        // 上级提成
+                        Event::dispatch('commission.order', ['orderId' => $payObjectJson['order_id'], 'member_account_list_category' => '充值']);
+                    }
+
+                    // 入收支明细表
+                    $params['orderId'] = $payObjectJson['order_id'];
+                    $params['inout_category'] = '会员充值订单收入';
+                    Event::dispatch('statistics.inout.in', $params);
+                }
+            }
+
+
             Db::commit();
 
             return json_success('success');

+ 8 - 1
app/admin/controller/order/DishesController.php

@@ -129,7 +129,7 @@ class DishesController extends Curd
         if ($field) {
             $model = $model->orderBy($field, $order);
         }
-        $model = $model->select('order.*', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_remark');
+        $model = $model->select('order.*', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_refund_json', 'order_return.order_return_remark');
         return $model;
     }
 
@@ -148,6 +148,7 @@ class DishesController extends Curd
                 $item['sheet']['order_sheet_num'] = intval($item['sheet']['order_sheet_num']);
             }
             unset($item['sheets']);
+            $item['order_return_amount'] = 0;
             if (isset($item['orders_return_id'])) {
                 $item['return'] = [
                     'orders_return_id' => $item['orders_return_id'],
@@ -156,6 +157,12 @@ class DishesController extends Curd
                     'order_return_apply_json' => $item['order_return_apply_json'],
                     'order_return_remark' => $item['order_return_remark']
                 ];
+                if (!empty($item['order_return_refund_json'])) {
+                    $refundJson = json_decode($item['order_return_refund_json'], true);
+                    if (isset($refundJson['amount'])) {
+                        $item['order_return_amount'] = $refundJson['amount'];
+                    }
+                }
             }
         }
 

+ 33 - 8
app/admin/controller/order/GoodsController.php

@@ -48,7 +48,6 @@ class GoodsController extends Curd
     public function select(Request $request): Response
     {
         [$where, $format, $limit, $field, $order] = $this->selectInput($request);
-
         $where['order_classify'] = 'GOODS';
         if (!empty($where['order_addtimes'])) {
             $where['order_addtimes'][0] = strtotime($where['order_addtimes'][0]);
@@ -157,7 +156,7 @@ class GoodsController extends Curd
         if ($field) {
             $model = $model->orderBy($field, $order);
         }
-        $model = $model->select('order.*', 'order_express.join_express_order_id', 'order_express.order_express_type', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_remark');
+        $model = $model->select('order.*', 'order_express.join_express_order_id', 'order_express.order_express_type', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_refund_json', 'order_return.order_return_remark');
         return $model;
     }
 
@@ -176,6 +175,7 @@ class GoodsController extends Curd
                 $item['sheet']['order_sheet_num'] = intval($item['sheet']['order_sheet_num']);
             }
             unset($item['sheets']);
+            $item['order_return_amount'] = 0;
             if (isset($item['orders_return_id'])) {
                 $item['return'] = [
                     'orders_return_id' => $item['orders_return_id'],
@@ -184,6 +184,12 @@ class GoodsController extends Curd
                     'order_return_apply_json' => $item['order_return_apply_json'],
                     'order_return_remark' => $item['order_return_remark']
                 ];
+                if (!empty($item['order_return_refund_json'])) {
+                    $refundJson = json_decode($item['order_return_refund_json'], true);
+                    if (isset($refundJson['amount'])) {
+                        $item['order_return_amount'] = $refundJson['amount'];
+                    }
+                }
             }
             if (isset($item['join_express_order_id'])) {
                 $item['express'] = [
@@ -550,10 +556,16 @@ class GoodsController extends Curd
 
             Db::commit();
 
-            // 会员升级
+            // 触发事件
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 Event::dispatch('order.complete', $params);
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
+            }
+            // 触发事件
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 上级提成
+                OrderService::splitOrderCommission($params);
+                // 入收支明细表
+                OrderService::splitOrderStatisticsInOut($params);
             }
 
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
@@ -878,10 +890,16 @@ class GoodsController extends Curd
 
             Db::commit();
 
-            // 会员升级
+            // 触发事件
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 Event::dispatch('order.complete', $params);
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
+            }
+            // 触发事件
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 上级提成
+                OrderService::splitOrderCommission($params);
+                // 入收支明细表
+                OrderService::splitOrderStatisticsInOut($params);
             }
 
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
@@ -936,6 +954,7 @@ class GoodsController extends Curd
         }
 
         $order = Order::where('order_id', $params['order_id'])->first();
+        $oldOrderStatus = $order->order_status_system;
         if (!$order) {
             return json_fail('订单异常');
         }
@@ -1221,7 +1240,7 @@ class GoodsController extends Curd
                 if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
                     $params['pay_category'] = 'WXPAY';
                     if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1233,7 +1252,7 @@ class GoodsController extends Curd
                 } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
                     $params['pay_category'] = 'ALIPAY';
                     if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1322,6 +1341,9 @@ class GoodsController extends Curd
             if ($order->order_status_payment == 'SUCCESS') {
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = '标准订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票
@@ -1682,6 +1704,9 @@ class GoodsController extends Curd
             if ($order->order_status_payment == 'SUCCESS') {
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = '标准订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票

+ 25 - 4
app/admin/controller/order/KangyangCityController.php

@@ -362,10 +362,18 @@ class KangyangCityController extends Curd
 //            $params['order_express_goods'] = json_encode(['sheet' => [$sheetId]]);
 
             // 2.4W 康养城
+            $full = '';
             if ($params['goods_classify'] == 'VIP' && $params['order_status_payment'] == 'SUCCESS' && (!empty($paidOrder) && floatval($paidOrder->order_amount_paid) >= $paidOrder->order_amount_total || floatval($params['order_amount_pay']) >= $params['order_amount_total'])) {
+                $full = '-满额';
                 $params['member_id'] = $params['join_order_member_id'];
                 Event::dispatch('order.kangyangCityVIP.grant', $params);
             }
+            // 触发事件
+            if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买康养城VIP套餐包收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+            }
 
             Db::commit();
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
@@ -564,8 +572,9 @@ class KangyangCityController extends Curd
 //            if (!PayDetail::where('join_pay_order_id', $order->order_groupby)->where('pay_category', '<>', 'WXPAY')->where('pay_category', '<>', 'ALIPAY')->exists()) {
             $payData['join_pay_member_id'] = $params['join_order_member_id'];
             $payData['join_pay_order_id'] = $order->order_groupby;
-            $payData['pay_status'] = $payData['pay_status'] == 'SUCCESS' ? $payData['pay_status'] : 'WAITING';
+            $payData['pay_status'] = !empty($payData['pay_status']) && $payData['pay_status'] == 'SUCCESS' ? $payData['pay_status'] : 'WAITING';
             $payData['pay_category'] = $params['goods_classify'] ?? '';
+            $payData['pay_paytimes'] = date('Y-m-d H:i:s');
             $payData['pay_json_request'] = json_encode($params);   // {"pay-result": "支付成功", "result-datetime": "2024-07-29 18:38:21"}
             $payData['pay_json_response'] = $payData['pay_status'] == 'SUCCESS' ? json_encode([
                 'pay-result' => '支付成功', 'result-datetime' => date('Y-m-d H:i:s')
@@ -575,8 +584,10 @@ class KangyangCityController extends Curd
 
             PayDetail::insert($payData);
 
+            $full = '';
             // 2.4W 康养城
             if ($params['goods_classify'] == 'VIP' && $order->order_status_payment == 'SUCCESS' && floatval($order->order_amount_paid) >= $order->order_amount_total) {
+                $full = '-满额';
                 $params['member_id'] = $params['join_order_member_id'];
                 Event::dispatch('order.kangyangCityVIP.grant', $params);
             }
@@ -585,9 +596,19 @@ class KangyangCityController extends Curd
 
             if ($order->order_is_complete == 'Y' && $order->order_status_payment == 'SUCCESS') {
                 Event::dispatch('order.complete', $params);
+            }// 触发事件
+            if ($paymentStatus == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买康养城VIP套餐包收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             if ($order->order_status_payment != 'SUCCESS' && $paymentStatus != 'SUCCESS') {
+                // 已支付去掉这次的
+                $order->order_amount_paid = $order->order_amount_paid - $params['order_amount_pay'];
+                $order->order_amount_pay = $order->order_amount_paid;
+                $order->save();
+
                 _syslog("订单", "支付异常,检查是否有轮询");
                 return json_throw(2001, '支付异常', ['order_id' => $params['orderId']]);
             }
@@ -813,7 +834,7 @@ class KangyangCityController extends Curd
             $orderId = $request->get('order_id');
             $orderSheet = OrderSheet::with([
                 'member' => function ($query) {
-                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'join_member_role_id');
+                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'member_is_vip', 'member_is_partner', 'member_is_referrer', 'join_member_role_id');
                 },
                 'goods' => function ($query) {
                     $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_market_price', 'goods_sales_price', 'goods_classify', 'goods_if_express');
@@ -959,8 +980,8 @@ class KangyangCityController extends Curd
                     $item['member']['level'] = MemberRole::where('member_role_id', $item['member']['join_member_role_id'])->value('member_role_name');
                 }
 
-                if (!empty($item['member_info'])){
-                    if (empty($item['member_info']['member_info_headimg']) || substr($item['member_info']['member_info_headimg'],0,1) == '.'){
+                if (!empty($item['member_info'])) {
+                    if (empty($item['member_info']['member_info_headimg']) || substr($item['member_info']['member_info_headimg'], 0, 1) == '.') {
                         $item['member_info']['member_info_headimg'] = "https://img.wanyuewellness.com.cn/images/avatar_default.png";
                     }
                 }

+ 1215 - 0
app/admin/controller/order/NewCustomerController.php

@@ -0,0 +1,1215 @@
+<?php
+
+namespace app\admin\controller\order;
+
+use app\admin\service\coupon\CouponService;
+use app\admin\service\member\MemberService;
+use app\admin\service\order\OrderService;
+use app\admin\validate\order\OrderValidate;
+use app\controller\Curd;
+use app\model\Appointment;
+use app\model\ClientConfig;
+use app\model\Coupon;
+use app\model\Goods;
+use app\model\GoodsComponent;
+use app\model\GoodsRunning;
+use app\model\GoodsSku;
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\MemberBenefit;
+use app\model\MemberRole;
+use app\model\Order;
+use app\model\OrderExpress;
+use app\model\OrderReturn;
+use app\model\OrderSheet;
+use app\model\PayDetail;
+use app\model\Supplier;
+use app\model\SysDept;
+use app\model\SysUser;
+use support\Db;
+use support\exception\BusinessException;
+use support\Redis;
+use support\Request;
+use support\Response;
+use Webman\Event\Event;
+use Yansongda\Pay\Pay;
+
+class NewCustomerController extends Curd
+{
+    public function __construct()
+    {
+        $this->model = new Order();
+        $this->validate = true;
+        $this->validateClass = new OrderValidate();
+    }
+
+    /**
+     * @Desc 列表
+     * @Author Gorden
+     * @Date 2024/3/28 15:01
+     *
+     * @param Request $request
+     * @return Response
+     * @throws \support\exception\BusinessException
+     */
+    public function select(Request $request): Response
+    {
+        [$where, $format, $limit, $field, $order] = $this->selectInput($request);
+        $where['order_classify'] = 'COMBINE';
+        if (!empty($where['order_addtimes'])) {
+            $where['order_addtimes'][0] = strtotime($where['order_addtimes'][0]);
+            $where['order_addtimes'][1] = strtotime($where['order_addtimes'][1]);
+        }
+
+        $order = $request->get('order', 'desc');
+        $field = $field ?? 'order_addtimes';
+        $orderIds = [];
+        if (!empty($orderId)) {
+            $orderIds = Order::where('order_id', 'like', '%' . $orderId . '%')
+                ->where('order_classify', 'COMBINE')
+                ->pluck('order_id')
+                ->toArray();
+        }
+        $goodsName = trim($request->get('goods_name', ''));
+        if (!empty($goodsName)) {
+            $goodsIds = Goods::where('goods_classify', 'COMBINE')
+                ->where('goods_name', 'like', '%' . $goodsName . '%')
+                ->pluck('goods_id')
+                ->toArray();
+            $goodsOrderIds = OrderSheet::whereIn('join_sheet_goods_id', $goodsIds)->pluck('join_sheet_order_id')->toArray();
+            if (!empty($where['order_id'])) {
+                $orderIds = array_intersect($orderIds, $goodsOrderIds);
+            } else {
+                $orderIds = $goodsOrderIds;
+            }
+        }
+        if (!empty($orderId) || !empty($goodsName)) {
+            $where['order_id'] = ['in', implode(',', $orderIds)];
+        }
+
+        $query = $this->doSelect($where, $field, $order);
+        return $this->doFormat($query, $format, $limit);
+    }
+
+    protected function doSelect(array $where, string $field = null, string $order = 'desc')
+    {
+        $model = $this->model->with([
+            'sheets' => function ($query) {
+                $query->select('join_sheet_order_id', 'order_sheet_id', 'join_sheet_goods_id', 'order_sheet_num', 'order_sheet_price');
+            },
+            'member' => function ($query) {
+                $query->select('member_id', 'member_mobile');
+            },
+            'cert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            },
+//            'return' => function ($query) use ($where){
+//                if (isset($where['return'])){
+//                    dump($where['return']);
+//                    $query = $query->where('order_return_status',$where['return']);
+//                }
+//                $query->select('orders_return_id', 'join_return_order_id', 'order_return_status');
+//            },
+            // 'express' => function ($query) {
+            //     $query->select('join_express_order_id', 'order_express_type');
+            // }
+        ])->leftJoin('order_return', 'order_return.join_return_order_id', '=', 'order.order_id')
+            ->leftJoin('order_express', 'order_express.join_express_order_id', '=', 'order.order_id');
+        // ->leftJoin('order_sheet','join_sheet_order_id','=','order.order_id');
+        foreach ($where as $column => $value) {
+            if (is_array($value)) {
+                if ($value[0] === 'like' || $value[0] === 'not like') {
+                    $model = $model->where($column, $value[0], "%$value[1]%");
+                } elseif (in_array($value[0], ['>', '=', '<', '<>'])) {
+                    $model = $model->where($column, $value[0], $value[1]);
+                } elseif ($value[0] == 'in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereIn($column, $valArr);
+                } elseif ($value[0] == 'not in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereNotIn($column, $valArr);
+                } elseif ($value[0] == 'null') {
+                    $model = $model->whereNull($column);
+                } elseif ($value[0] == 'not null') {
+                    $model = $model->whereNotNull($column);
+                } elseif ($value[0] !== '' || $value[1] !== '') {
+                    $model = $model->whereBetween($column, $value);
+                }
+            } else {
+                $model = $model->where($column, $value);
+            }
+        }
+        if ($field) {
+            $model = $model->orderBy($field, $order);
+        }
+        $model = $model->select('order.*', 'order_express.join_express_order_id', 'order_express.order_express_type', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_remark');
+        return $model;
+    }
+
+    public function afterQuery($items)
+    {
+        foreach ($items as &$item) {
+            $sheetDeng = '';
+            $item['sheet'] = $item['sheets'][0] ?? [];
+            if (!empty($item['sheet'])) {
+                $goods = Goods::where('goods_id', $item['sheet']['join_sheet_goods_id'])->first();
+                if (count($item['sheets']) > 1 && $goods->goods_classify == 'MEALS') {
+                    $sheetDeng = ' 等餐品';
+                }
+                $item['sheet']['goods_name'] = ($goods && $goods->goods_name) ? $goods->goods_name . $sheetDeng : '';
+                $item['sheet']['goods_classify'] = $goods->goods_classify ?? '';
+                $item['sheet']['order_sheet_num'] = intval($item['sheet']['order_sheet_num']);
+            }
+            unset($item['sheets']);
+            if (isset($item['orders_return_id'])) {
+                $item['return'] = [
+                    'orders_return_id' => $item['orders_return_id'],
+                    'join_return_order_id' => $item['join_return_order_id'],
+                    'order_return_status' => $item['order_return_status'],
+                    'order_return_apply_json' => $item['order_return_apply_json'],
+                    'order_return_remark' => $item['order_return_remark']
+                ];
+            }
+            if (isset($item['join_express_order_id'])) {
+                $item['express'] = [
+                    'join_express_order_id' => $item['join_express_order_id'],
+                    'order_express_type' => $item['order_express_type']
+                ];
+                unset($item['join_express_order_id'], $item['order_express_type']);
+            }
+            // if (!empty($item['order_extend_json'])){
+            //     $orderExtendJson = json_decode($item['order_extend_json'],true);
+            //     $item['referee'] = $orderExtendJson['referee'] ?? '';
+            // }
+        }
+
+        return $items;
+    }
+
+    /**
+     * @Desc 下单+支付
+     * @Author Gorden
+     * @Date 2024/9/5 13:13
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function insert(Request $request): Response
+    {
+        $params = $request->post();
+        $goodsClassifys = array_unique(array_column($params['goodsContentList'], 'goods_classify'));
+        $params['goods_classify'] = $goodsClassifys[0];
+        $params['submit_goods_classify'] = $goodsClassifys[0];
+        Db::beginTransaction();
+        try {
+            // 验证线下付款密码
+            if ($params['settlement_now'] == 'Y' && $params['pay_constitute'] == 'N' && in_array($params['pay_category'], ['OFFLINE', 'MONEY'])) {
+                $password = $params['offline_password'];
+                if ($password != '666888') {
+                    throw new BusinessException('密码错误,请重新输入');
+                }
+            }
+            // 下单账户
+            if (empty($params['join_order_member_id']) && !empty($params['mobile'])) {
+                if (Member::where('member_mobile', $params['mobile'])->exists()) {
+                    throw new BusinessException('会员已存在');
+                }
+                $params['join_order_member_id'] = $params['member_id'] = 'MR' . date('ymdHi') . random_string(4, 'up');
+                // 创建会员
+                MemberService::createMember($params);
+            } else if (empty($params['join_order_member_id']) && empty($params['mobile'])) {
+                $params['join_order_member_id'] = Member::where('member_mobile', '0000')->value('member_id');
+            }
+            if (empty($params['join_order_member_id'])) {
+                throw new BusinessException('检查下单账户');
+            }
+            // 检查库存            
+            OrderService::checkGoodsStorage($params);
+
+            $params['orderId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+            $params['orderGroupId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+            // 查询是否有未完全支付订单
+            $paidOrder = Order::where('join_order_member_id', $params['join_order_member_id'])
+                ->where('order_category', 'COMBINE')
+                ->where('order_status_system', 'PAYING')
+                ->first();
+            if (!empty($paidOrder)) {
+                $paidOrder->order_groupby = $params['orderGroupId'];
+                $params['orderId'] = $paidOrder->order_id;
+            } else {
+                // 检查产品配置
+                $this->checkGoodsConfig($params);
+            }
+            $systemStatus = 'PAYING';
+            // 立即结算
+            if ($params['settlement_now'] == 'Y') {
+                $params['order_is_complete'] = 'Y';
+                $systemStatus = 'DONE';
+            }
+
+            if ($params['settlement_now'] == 'Y' && ($params['pay_category'] == 'OFFLINE' || $params['pay_category'] == 'MONEY')) {
+                if ($params['pay_category'] == 'OFFLINE' && !empty($params['pay_category_sub'])) {
+                    $params['pay_category'] = $params['pay_category_sub'];
+                }
+                $params['order_status_system'] = $systemStatus;
+                $params['order_status_payment'] = 'SUCCESS';
+            }
+            if ($params['pay_category'] == 'QRCODE' && $params['settlement_now'] == 'Y' && !empty($params['qrcode_nbr'])) {     // 付款码
+                $result = OrderService::qrcodePay($params);
+                $result = json_encode($result);
+                $params['pay_json_response'] = $result;
+                $result = json_decode($result, true);
+
+                $prefix = substr($params['qrcode_nbr'], 0, 2);
+                if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
+                    $params['pay_category'] = 'WXPAY';
+                    if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
+                        $params['order_status_system'] = 'PAYING';
+                        $params['order_status_payment'] = 'PENDING';
+                        $params['order_is_complete'] = 'N';
+                    } else {
+                        $params['order_status_system'] = $systemStatus;
+                        $params['order_status_payment'] = 'SUCCESS';
+                    }
+                } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
+                    $params['pay_category'] = 'ALIPAY';
+                    if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
+                        $params['order_status_system'] = 'PAYING';
+                        $params['order_status_payment'] = 'PENDING';
+                        $params['order_is_complete'] = 'N';
+                    } else {
+                        $params['order_status_system'] = $systemStatus;
+                        $params['order_status_payment'] = 'SUCCESS';
+                    }
+                } else {
+                    throw new BusinessException('付款码无效');
+                }
+            }
+
+            if (empty($paidOrder)) {
+                // 写入主订单
+                $this->insertMain($params);
+                // 订单详情
+                $this->insertSheet($params);
+            } else {
+                $params['orderGroupId'] = $paidOrder->order_groupby;
+                if ($params['order_status_payment'] == 'SUCCESS') {
+                    $params['order_amount_paid'] = $paidOrder->order_amount_paid;
+                    $paidOrder->order_amount_paid = $paidOrder->order_amount_paid + $params['order_amount_pay'];
+                    $paidOrder->order_amount_pay = $paidOrder->order_amount_paid;
+                    if (floatval($paidOrder->order_amount_paid) >= $paidOrder->order_amount_total) {
+                        $paidOrder->order_is_complete = 'Y';
+                        $paidOrder->order_status_system = 'DONE';
+                        $paidOrder->order_status_payment = 'SUCCESS';
+                        // 更新sheet
+                        OrderSheet::where('join_sheet_order_id', $paidOrder->order_id)->update(['order_sheet_status' => 'DONE']);
+                    }
+                }
+                $paidOrder->save();
+                // 清除此订单的未支付记录
+                PayDetail::whereJsonContains('join_pay_object_json->order_id', $paidOrder->order_id)->where('pay_status', 'WAITING')->delete();
+            }
+            // 支付记录
+            $this->insertPayDetail($params);
+
+            Db::commit();
+
+            // 分期付完
+            $full = '';
+            if ($params['goods_classify'] == 'COMBINE' && $params['order_status_payment'] == 'SUCCESS' && (!empty($paidOrder) && floatval($paidOrder->order_amount_paid) >= $paidOrder->order_amount_total || floatval($params['order_amount_pay']) >= $params['order_amount_total'])) {
+                $full = '-满额';
+                $params['member_id'] = $params['join_order_member_id'];
+                // 完成订单
+                Event::dispatch('order.complete', $params);
+
+                Event::dispatch('order.new_custom.grant', $params);
+            }
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买组合包收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+            }
+            if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
+                _syslog("订单", "支付异常,检查是否有轮询");
+                return json_throw(2001, '支付异常', ['order_id' => $params['orderId'], 'group_id' => $params['orderGroupId']]);
+            }
+            _syslog("订单", "创建订单成功");
+            return json_success('创建订单成功');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            dump($e->getTrace());
+            _syslog("订单", $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            dump($e->getTrace());
+            _syslog("订单", "创建订单失败");
+            return json_fail('创建订单失败');
+        }
+    }
+
+    /**
+     * @Desc 支付
+     * @Author Gorden
+     * @Date 2024/9/5 13:12
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function pay(Request $request)
+    {
+        $params = $request->post();
+        // 验证线下付款密码
+        if ($params['pay_constitute'] == 'N' && in_array($params['pay_category'], ['OFFLINE', 'MONEY'])) {
+            $password = $params['offline_password'];
+            if ($password != '666888') {
+                return json_fail("密码错误,请重新输入");
+            }
+        }
+
+        $order = Order::where('order_id', $params['order_id'])->first();
+        if (!$order) {
+            return json_fail('订单异常');
+        }
+        if ($order->order_status_system != 'PAYING') {
+            return json_fail('订单不是可支付状态');
+        }
+
+        $params['orderId'] = $params['order_id'];
+
+        $params['orderGroupId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+        $order->order_groupby = $params['orderGroupId'];
+
+        $goods = Goods::where('goods_id', $params['join_sheet_goods_id'])
+            ->select('goods_id', 'goods_name', 'goods_classify')
+            ->first();
+        if (!$goods) {
+            return json_fail('产品数据异常');
+        }
+        $goods = $goods->toArray();
+        $params['goods_classify'] = $goods['goods_classify'] ?? '';
+
+        // 立即结算
+        if ($params['goods_classify'] == 'COMBINE') {
+            $order->order_is_complete = 'Y';
+            $systemStatus = 'DONE';
+        }
+        $paymentStatus = 'PENDING';
+        Db::beginTransaction();
+        try {
+            // 组合支付时,付款码应收金额
+            $qrcodePayAmount = 0;
+            if ($params['pay_category'] == 'OFFLINE' || $params['pay_category'] == 'MONEY') {
+                $order->order_status_system = $systemStatus;
+                $order->order_status_payment = 'SUCCESS';
+                $paymentStatus = 'SUCCESS';
+                if ($params['pay_category'] == 'OFFLINE' && !empty($params['pay_category_sub'])) {
+                    $params['pay_category'] = $params['pay_category_sub'];
+                }
+            }
+            if (($params['pay_constitute'] == 'Y' || $params['pay_category'] == 'QRCODE') && !empty($params['qrcode_nbr'])) {     // 付款码
+                // 提交过来的支付分类
+                $submitPayCategory = $params['pay_category'];
+                // 账户支付的金额
+                $accountAmount = $params['order_amount_pay'];
+                if ($params['pay_constitute'] == 'Y' && $qrcodePayAmount > 0) {
+                    // 组合支付,改成需要付款码需要支付的金额
+                    $params['order_amount_pay'] = $qrcodePayAmount;
+                }
+                // 去支付
+                $result = OrderService::qrcodePay($params);
+                $result = json_encode($result);
+                $params['pay_json_response'] = $result;
+                $result = json_decode($result, true);
+
+                $prefix = substr($params['qrcode_nbr'], 0, 2);
+                if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
+                    $params['pay_category'] = 'WXPAY';
+                    if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
+                        $order->order_status_system = 'PAYING';
+                        $order->order_status_payment = 'PENDING';
+                        $order->order_is_complete = 'N';
+//                        Db::rollBack();
+//                        return json_fail('支付失败');
+                    } else {
+                        $order->order_status_system = $systemStatus;
+                        $order->order_status_payment = 'SUCCESS';
+                        $paymentStatus = 'SUCCESS';
+                    }
+                } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
+                    $params['pay_category'] = 'ALIPAY';
+                    if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
+                        $order->order_status_system = 'PAYING';
+                        $order->order_status_payment = 'PENDING';
+                        $order->order_is_complete = 'N';
+//                        Db::rollBack();
+//                        return json_fail('支付失败');
+                    } else {
+                        $order->order_status_system = $systemStatus;
+                        $order->order_status_payment = 'SUCCESS';
+                        $paymentStatus = 'SUCCESS';
+                    }
+                } else {
+                    throw new BusinessException('付款码无效');
+                }
+
+                // 账户支付的金额
+                $params['order_amount_pay'] = $accountAmount;
+            }
+            if ($order->order_status_payment == 'SUCCESS') {
+                $order->order_amount_paid = $order->order_amount_paid + $params['order_amount_pay'];
+                $order->order_amount_pay = $order->order_amount_paid;
+            }
+
+            // 分期,支付完就结束了
+            if ($order->order_status_payment == 'SUCCESS' && $params['goods_classify'] == 'COMBINE' && floatval($order->order_amount_paid) >= $order->order_amount_total) {
+                $order->order_is_complete = 'Y';
+            }
+            if (floatval($order->order_amount_paid) < $order->order_amount_total) {
+                $order->order_status_system = 'PAYING';
+                $order->order_status_payment = 'PENDING';
+                $order->order_is_complete = 'N';
+            }
+            // 主订单
+            $order->save();
+
+            // sheet
+            if ($order->order_status_payment == 'SUCCESS') {
+                $data = [
+                    'order_sheet_status' => $systemStatus
+                ];
+                if (floatval($order->order_amount_paid) < $order->order_amount_total) {
+                    $data['order_sheet_status'] = 'BEING';
+                }
+                OrderSheet::where('join_sheet_order_id', $params['order_id'])->update($data);
+            }
+            // payDetail
+            $payData = [
+                'pay_amount' => $params['order_amount_pay']
+            ];
+            if ($paymentStatus == 'SUCCESS') {
+                $payData['pay_paytimes'] = date('Y-m-d H:i:s');
+                $payData['pay_status'] = 'SUCCESS';
+            }
+            if ($params['pay_constitute'] == 'N' && in_array($params['pay_category'], ['WXPAY', 'ALIPAY'])) {
+                $payData['pay_prepayid'] = $params['pay_category'];
+                $payData['pay_json_response'] = $params['pay_json_response'];
+            } else if ($params['pay_category'] == 'CASH') {
+                $payData['pay_prepayid'] = $params['join_order_member_id'] . '-CASH';
+            } else if ($params['pay_category'] == 'WELFARE') {
+                $payData['pay_prepayid'] = $params['join_order_member_id'] . '-WELFARE';
+            } else if ($params['pay_category'] == 'CARD') {
+                $payData['pay_prepayid'] = $params['card_nbr'];
+            } else if ($params['pay_category'] == 'OFFLINE') {
+                $payData['pay_prepayid'] = 'OFFLINE';
+            } else if ($params['pay_category'] == 'OFFLINE_ALIPAY') {
+                $payData['pay_prepayid'] = 'OFFLINE_ALIPAY';
+            } else if ($params['pay_category'] == 'OFFLINE_WXPAY') {
+                $payData['pay_prepayid'] = 'OFFLINE_WXPAY';
+            } else if ($params['pay_category'] == 'MONEY') {
+                $payData['pay_prepayid'] = 'MONEY';
+            }
+            // 清除此订单的未支付记录
+            PayDetail::whereJsonContains('join_pay_object_json->order_id', $order->order_id)->where('pay_status', 'WAITING')->delete();
+
+            $payData['join_pay_member_id'] = $params['join_order_member_id'];
+            $payData['join_pay_order_id'] = $order->order_groupby;
+            $payData['pay_status'] = !empty($payData['pay_status']) && $payData['pay_status'] == 'SUCCESS' ? $payData['pay_status'] : 'WAITING';
+            $payData['pay_category'] = $params['goods_classify'] ?? '';
+            $payData['pay_paytimes'] = date('Y-m-d H:i:s');
+            $payData['pay_json_request'] = json_encode($params);   // {"pay-result": "支付成功", "result-datetime": "2024-07-29 18:38:21"}
+            $payData['pay_json_response'] = !empty($payData['pay_status']) && $payData['pay_status'] == 'SUCCESS' ? json_encode([
+                'pay-result' => '支付成功', 'result-datetime' => date('Y-m-d H:i:s')
+            ]) : '[]';
+            $payData['join_pay_object_json'] = !empty($params['orderId']) ? json_encode(['order_id' => $params['orderId']]) : '[]';
+            $payData['pay_addtimes'] = time();
+
+            PayDetail::insert($payData);
+
+            Db::commit();
+
+            // 分期付款完成
+            $full = '';
+            if ($params['goods_classify'] == 'COMBINE' && $order->order_status_payment == 'SUCCESS' && floatval($order->order_amount_paid) >= $order->order_amount_total) {
+                $full = '-满额';
+                $params['member_id'] = $params['join_order_member_id'];
+                // 完成订单
+                Event::dispatch('order.complete', $params);
+                // 处理分期完成后的操作
+                Event::dispatch('order.new_custom.grant', $params);
+            }
+            if ($order->order_status_payment == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买组合包收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+            }
+            if ($order->order_status_payment != 'SUCCESS' && $paymentStatus != 'SUCCESS') {
+                _syslog("订单", "支付异常,检查是否有轮询");
+                return json_throw(2001, '支付异常', ['order_id' => $params['orderId'], 'group_id' => $params['orderGroupId']]);
+            }
+            _syslog("订单", "订单支付成功");
+            return json_success('支付成功');
+        } catch (BusinessException $e) {
+            dump($e->getMessage());
+            Db::rollBack();
+            _syslog("订单", "订单支付失败:" . $e->getMessage());
+            return json_fail("支付失败:" . $e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            Db::rollBack();
+            _syslog("订单", "订单支付失败");
+            return json_fail('支付失败');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/6/7 10:30
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public function insertMain($params)
+    {
+        try {
+            $orderCategory = 'NORMAL';
+            if (!empty($params['order_category'])) {
+                $orderCategory = $params['order_category'];
+            } else if (isset($params['submit_goods_classify']) && $params['submit_goods_classify'] == 'MEALS') {
+                $orderCategory = 'DISHES';
+            } else if (isset($params['goods_classify'])) {
+                $orderCategory = $params['goods_classify'];
+            }
+            if (empty($params['order_extend_json'])) {
+                $params['order_extend_json'] = [];
+            } else {
+                if (is_json($params['order_extend_json'])) {
+                    $params['order_extend_json'] = json_decode($params['order_extend_json'], true);
+                }
+            }
+            // 推荐人
+            $params['order_extend_json']['referee'] = $params['referee'] ?? '';
+
+            $data = [
+                'order_id' => $params['orderId'],
+                'order_groupby' => $params['orderGroupId'],
+                'join_order_member_id' => $params['join_order_member_id'],
+                'order_name' => date('Y-m-d H:i:s') . '-订单',
+                'order_amount_total' => $params['order_amount_total'],
+                'order_amount_pay' => $params['order_status_payment'] == 'SUCCESS' ? $params['order_amount_pay'] : 0,
+                'order_amount_paid' => $params['order_status_payment'] == 'SUCCESS' ? $params['order_amount_pay'] : 0,
+                'order_category' => $orderCategory,
+                'order_classify' => $orderCategory,
+                'order_is_complete' => floatval($params['order_amount_pay']) >= floatval($params['order_amount_total']) && $params['order_status_payment'] == 'SUCCESS' ? 'Y' : 'N',
+                'order_status_system' => floatval($params['order_amount_pay']) >= floatval($params['order_amount_total']) && $params['order_status_payment'] == 'SUCCESS' ? 'DONE' : 'PAYING',
+                'order_status_payment' => floatval($params['order_amount_pay']) >= floatval($params['order_amount_total']) && $params['order_status_payment'] == 'SUCCESS' ? 'SUCCESS' : 'PENDING',
+                'order_status_storage' => $params['order_status_storage'],
+                'order_platform' => $params['order_platform'],
+                'order_remark' => $params['order_remark'] ?? '',
+                'order_discount_json' => $params['order_discount_json'] ?? '[]',
+                'order_config_json' => $params['order_config_json'] ?? '[]',
+                'order_express_json' => $params['order_express_json'] ?? '[]',
+                'order_extend_json' => $params['order_extend_json'] ? json_encode($params['order_extend_json']) : '[]',
+                'order_addtimes' => time()
+            ];
+
+            Order::insert($data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('订单创建信息失败');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/6/7 10:25
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public function insertSheet($params)
+    {
+        try {
+            $orderSheetIds = [];
+            foreach ($params['goodsContentList'] as $goods) {
+                //{"unit": "份", "table": null, "premises": "15"}
+                $price = floatval($goods['goods_sales_price']);
+                $extendJson['unit'] = $goods['sku_name'];
+                if (isset($params['submit_premises_id'])) {
+                    $extendJson['premises'] = $params['submit_premises_id'];
+                }
+                $orderSheetStatus = 'PAYING';
+                if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
+                    if (floatval($params['order_amount_pay']) >= floatval($params['order_amount_total'])) {
+                        $orderSheetStatus = 'DONE';
+                    } else {
+                        $orderSheetStatus = 'BEING';
+                    }
+                }
+                // 减库存
+                $goodsRunning = GoodsRunning::where('join_running_goods_id', $goods['goods_id'])->first();
+                $goodsRunning->goods_running_storage = $goodsRunning->goods_running_storage - $goods['nbr'];
+                if ($goodsRunning->goods_running_storage < 0) {
+                    throw new BusinessException('库存不足');
+                }
+                $goodsRunning->goods_running_sale = $goodsRunning->goods_running_sale + $goods['nbr'];
+                $goodsRunning->save();
+                $data = [
+                    'join_sheet_member_id' => $params['join_order_member_id'],
+                    'join_sheet_order_id' => $params['orderId'],
+                    'join_sheet_goods_id' => $goods['goods_id'],
+                    'join_sheet_goods_sku_id' => $goods['sku_id'],
+                    'order_sheet_status' => $orderSheetStatus,
+                    'order_sheet_category' => 'COMBINE',
+                    'order_sheet_num' => $goods['nbr'],
+                    'order_sheet_price' => $goods['goods_sales_price'],
+                    'order_sheet_amount' => $price * $goods['nbr'],
+                    'order_sheet_pay' => $price * $goods['nbr'],
+                    'order_sheet_task_status' => 'NONE',
+                    'order_sheet_remark' => $params['order_remark'] ?? '',
+                    'order_sheet_addtimes' => time(),
+                    'order_sheet_extend_json' => json_encode($extendJson)
+                ];
+
+                $orderSheetId = OrderSheet::insertGetId($data);
+                $orderSheetIds[] = $orderSheetId;
+            }
+            return $orderSheetIds;
+        } catch (\support\exception\BusinessException $e) {
+            dump($e->getMessage() . '||' . $e->getLine());
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage() . '||' . $e->getLine());
+            throw new BusinessException('订单创建失败');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/6/7 10:35
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public function insertPayDetail($params)
+    {
+        try {
+            if (in_array($params['pay_category'], ['WXPAY', 'ALIPAY'])) {
+                $payPrepayid = $params['pay_category'];
+            } else if ($params['pay_category'] == 'OFFLINE') {
+                $payPrepayid = 'OFFLINE';
+            } else if ($params['pay_category'] == 'OFFLINE_ALIPAY') {
+                $payPrepayid = 'OFFLINE_ALIPAY';
+            } else if ($params['pay_category'] == 'OFFLINE_WXPAY') {
+                $payPrepayid = 'OFFLINE_WXPAY';
+            } else if ($params['pay_category'] == 'MONEY') {
+                $payPrepayid = 'MONEY';
+            } else {
+                $payPrepayid = $params['join_order_member_id'] . '-' . $params['pay_category'];
+            }
+            $data = [
+                'join_pay_member_id' => $params['join_order_member_id'],
+                'join_pay_order_id' => $params['orderGroupId'],
+                'pay_status' => $params['settlement_now'] == 'Y' && $params['order_status_payment'] == 'SUCCESS' ? 'SUCCESS' : 'WAITING',
+                'pay_category' => $params['goods_classify'],
+                'pay_amount' => $params['order_amount_pay'],
+                'pay_prepayid' => $payPrepayid,
+                'pay_paytimes' => date('Y-m-d H:i:s'),
+                'join_pay_object_json' => !empty($params['orderId']) ? json_encode(['order_id' => $params['orderId']]) : '[]',
+                'pay_json_request' => json_encode($params),
+                'pay_json_response' => $params['pay_json_response'] ?? '[]',
+                'pay_remark' => $params['order_remark'] ?? '',
+                'pay_addtimes' => time(),
+            ];
+
+            PayDetail::insert($data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('创建支付记录失败');
+        }
+    }
+
+
+    /**
+     * @Desc 订单商品详情
+     * @Author Gorden
+     * @Date 2024/3/29 8:50
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function sheet(Request $request)
+    {
+        try {
+            $orderId = $request->get('order_id');
+            $orderSheet = OrderSheet::with([
+                'member' => function ($query) {
+                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'join_member_role_id', 'member_is_vip', 'member_is_partner', 'member_is_referrer');
+                },
+                'goods' => function ($query) {
+                    $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_market_price', 'goods_sales_price', 'goods_classify', 'goods_if_express', 'goods_attribute_json');
+                },
+                'memberInfo',
+                'cert',
+                'sku' => function ($query) {
+                    $query->where('goods_sku_status', 'ON')
+                        ->select('goods_sku_id', 'join_sku_goods_id', 'goods_sku_specs_json', 'goods_sku_sales_price');
+                },
+                'skus',
+                'refund' => function ($query) {
+                    $query->select('join_return_order_id', 'order_return_status');
+                }
+            ])->where('join_sheet_order_id', $orderId)
+                ->orderBy('order_sheet_id', 'DESC')
+                ->get()
+                ->toArray();
+
+            $order = Order::where('order_id', $orderId)->first();
+            // $express = OrderExpress::where('join_express_order_id', $orderId)->first();
+            // $premises = '';
+            // if ($express && $express->order_express_type == '自提') {
+            //     $premises = $express->order_express_company;
+            // }
+            $sheetAmount = 0;
+            foreach ($orderSheet as &$item) {
+                $sheetAmount += $item['order_sheet_amount'];
+                $item['goods']['goods_cover'] = getenv('STORAGE_DOMAIN') . $item['goods']['goods_cover'];
+                if (!empty($item['goods']) && $item['goods']['goods_classify'] == 'PACKAGE') {
+                    $components = GoodsComponent::with('goods')
+                        ->where('join_component_master_goods_id', $item['goods']['goods_id'])
+                        ->select('join_component_master_goods_id', 'join_component_goods_id', 'goods_component_price',
+                            'goods_component_price', 'goods_component_json')
+                        ->get()
+                        ->toArray();
+                    $goodsArr = [];
+                    foreach ($components as $component) {
+                        $configJson = !empty($component['goods_component_json']) ? json_decode($component['goods_component_json'], true) : [];
+                        if (!empty($component['goods'])) {
+                            $supplierName = Supplier::where('supplier_id', $component['goods']['join_goods_supplier_id'])->value('supplier_name');
+                            $benefit = MemberBenefit::where('join_benefit_member_id', $item['join_sheet_member_id'])
+                                ->where('join_benefit_goods_id', $component['goods']['goods_id'])
+                                ->where('join_benefit_order_id', $orderId)
+                                ->first();
+                            $goodsArr[] = [
+                                'goods_name' => $component['goods']['goods_name'],
+                                'goods_cover' => getenv('STORAGE_DOMAIN') . $component['goods']['goods_cover'],
+                                'supplier_name' => $supplierName,
+                                'nbr' => $configJson['nbr'] ?? 0,
+                                'used' => !empty($benefit->member_benefit_used_count) ? intval($benefit->member_benefit_used_count) : ''
+                            ];
+                        }
+                    }
+
+                    $item['goods']['components'] = $goodsArr;
+                }
+                if (!empty($item['sku'])) {
+                    if (!empty($item['sku']['goods_sku_specs_json'])) {
+                        $specsJson = json_decode($item['sku']['goods_sku_specs_json'], true);
+                        $skuName = '';
+                        foreach ($specsJson as $specsKey => $skuSpecs) {
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . ' ' . implode(' ', $skuSpecs) . ';';
+                            } else {
+                                $skuName = $skuName . ' ' . $skuSpecs . ';';
+                            }
+                        }
+                        $item['sku']['goods_sku_title'] = rtrim($skuName, ';');
+                    }
+                }
+                if (!empty($item['skus'])) {
+                    foreach ($item['skus'] as $key => $skus) {
+                        if (!empty($skus['goods_sku_specs_json'])) {
+                            $item['skus'][$key]['goods_sku_specs_json'] = json_decode($skus['goods_sku_specs_json']);
+
+                            $skuName = '';
+                            foreach ($item['skus'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                                $keyStr = ($specsKey == '规格') ? '' : ($specsKey . ':');
+                                if (is_array($skuSpecs)) {
+                                    $skuName = $skuName . $keyStr . ' ' . implode(' ', $skuSpecs) . '; ';
+                                } else {
+                                    $skuName = $skuName . $keyStr . ' ' . $skuSpecs . '; ';
+                                }
+                            }
+                            $item['skus'][$key]['sku_name'] = rtrim($skuName, '; ');
+                        }
+                    }
+                } else {
+                    $item['skus'] = [];
+                }
+                $coupons = [];
+                // 产品携带的优惠券
+                if (!empty($item['goods']['goods_attribute_json'])) {
+                    $goodsAttributeJson = json_decode($item['goods']['goods_attribute_json'], true);
+                    if (isset($goodsAttributeJson['coupon'])) {
+                        $couponIds = array_keys($goodsAttributeJson['coupon']);
+                        $coupons = Coupon::whereIn('coupon_id', $couponIds)
+                            ->select('coupon_id', 'coupon_name', 'coupon_classify', 'coupon_is_period', 'coupon_period_json', 'coupon_addtimes')
+                            ->get()
+                            ->toArray();
+                        foreach ($coupons as &$coupon) {
+                            if (isset($goodsAttributeJson['coupon'][$coupon['coupon_id']])) {
+                                $coupon['num'] = $goodsAttributeJson['coupon'][$coupon['coupon_id']]['num'];
+                            }
+                        }
+                    }
+                }
+
+                $item['member']['level'] = '普通会员';
+                if (!empty($item['member']['join_member_role_id'])) {
+                    $item['member']['level'] = MemberRole::where('member_role_id', $item['member']['join_member_role_id'])->value('member_role_name');
+                }
+
+                if (!empty($item['member_info'])) {
+                    if (empty($item['member_info']['member_info_headimg']) || substr($item['member_info']['member_info_headimg'], 0, 1) == '.') {
+                        $item['member_info']['member_info_headimg'] = "https://img.wanyuewellness.com.cn/images/avatar_default.png";
+                    }
+                }
+            }
+            $order->sheet_amount = number_format($sheetAmount, 2);
+
+            $payDetails = PayDetail::whereJsonContains('join_pay_object_json->order_id', $order->order_id)
+                ->where('pay_category', 'COMBINE')
+                ->where('pay_status', 'SUCCESS')
+                ->select('pay_id', 'pay_category', 'pay_prepayid', 'pay_paytimes', 'pay_status', 'pay_amount', 'pay_extend_json')
+                ->orderBy('pay_addtimes', 'DESC')
+                ->get();
+            $order->pay_amount_total = 0;
+            foreach ($payDetails as &$payDetail) {
+                $categoryArray = explode('-', $payDetail->pay_prepayid);
+                if (isset($categoryArray[1])) {
+                    $payDetail->pay_category = $categoryArray[1];
+                } else if (in_array($categoryArray[0], ['WXPAY', 'ALIPAY', 'OFFLINE', 'OFFLINE_ALIPAY', 'OFFLINE_WXPAY', 'MONEY'])) {
+                    $payDetail->pay_category = $categoryArray[0];
+                }
+                if (!empty($payDetail->pay_extend_json)) {
+                    $payExtendJson = json_decode($payDetail->pay_extend_json, true);
+                    $order->cancel_times = $payExtendJson['cancel_times'] ?? '';
+                    if (isset($payExtendJson['ticket'])) {
+                        $payDetail->ticket = str_replace('/thumb', '', getenv("STORAGE_DOMAIN") . $payExtendJson['ticket']);
+                    }
+                }
+                $order->pay_amount_total += $payDetail->pay_amount;
+            }
+            $refund = OrderReturn::where('join_return_order_id', $orderId)
+                ->select('order_return_status', 'order_return_apply_datetime', 'order_return_apply_json', 'order_return_accept_datetime', 'order_return_refund_json', 'order_return_extend_json')
+                ->first();
+            if (!empty($refund->order_return_refund_json)) {
+                $returnRefundJson = json_decode($refund->order_return_refund_json, true);
+                if (isset($returnRefundJson['user_id'])) {
+                    $userName = SysUser::where('user_id', $returnRefundJson['user_id'])->value('user_name');
+                    $returnRefundJson['person'] = $userName;
+                    unset($returnRefundJson['user_id']);
+                }
+
+                $refund->order_return_refund_json = json_encode($returnRefundJson);
+            }
+            if (!empty($order->order_config_json)) {
+                $orderConfigJson = json_decode($order->order_config_json, true);
+                if (isset($orderConfigJson['reach'])) {
+                    $order->reach = $orderConfigJson['reach'];
+                }
+                if (isset($orderConfigJson['table'])) {
+                    $order->table = $orderConfigJson['table'];
+                }
+                if (isset($orderConfigJson['eat'])) {
+                    $order->eat = $orderConfigJson['eat'];
+                }
+                if (isset($orderConfigJson['express'])) {
+                    $order->express = $orderConfigJson['express'];
+                }
+                if (isset($orderConfigJson['premises'])) {
+                    $order->premises = $orderConfigJson['premises'];
+                }
+                if (isset($orderConfigJson['tableid'])) {
+                    $order->dept_table_id = $orderConfigJson['tableid'];
+                }
+            }
+            if (!empty($order->order_extend_json)) {
+                $orderExtendJson = json_decode($order->order_extend_json, true);
+                $order->referee = $orderExtendJson['referee'] ?? '';
+                if (isset($orderExtendJson['cancel_times'])) {
+                    $order->cancel_times = $orderExtendJson['cancel_times'];
+                }
+                if (isset($orderExtendJson['free_remark'])) {
+                    $order->free_remark = $orderExtendJson['free_remark'];
+                }
+            }
+            $discount = ['coupon_name' => '', 'classify' => '', 'value' => 0];
+            if (!empty($order->order_discount_json)) {
+                $orderDiscountJson = json_decode($order->order_discount_json, true);
+                foreach ($orderDiscountJson as $discountItem) {
+                    if (!empty($discountItem['coupon_id'])) {
+                        $coupon = Coupon::where('coupon_id', $discountItem['coupon_id'])
+                            ->select('coupon_name', 'coupon_classify', 'coupon_category', 'coupon_value', 'coupon_minimum_limit')
+                            ->first();
+                        if (!$coupon) {
+                            continue;
+                        }
+                        $classify = CouponService::couponClassifyInfo($coupon->coupon_classify, $coupon->coupon_category, $coupon->coupon_value, $coupon->coupon_minimum_limit);
+                        $discount['coupon_name'] .= $coupon->coupon_classify . ':' . $coupon->coupon_name . '(优惠¥' . sprintf("%.2f", $discountItem['coupon_value']) . '), ';
+                    }
+                    if ($discountItem['coupon_classify'] == '退款') {
+                        continue;
+                    }
+                    if (empty($discountItem['coupon_id']) && !empty($discountItem['coupon_classify'])) {
+                        if (!empty($discountItem['coupon_detail_id'])) {
+
+                            $discount['classify'] .= $discountItem['coupon_detail_id'][0] . '(优惠¥' . sprintf("%.2f", $discountItem['coupon_value']) . '), ';
+                        } else {
+                            $discount['classify'] .= $discountItem['coupon_classify'] . '(优惠¥' . sprintf("%.2f", $discountItem['coupon_value']) . '), ';
+                        }
+                    }
+                    if (!empty($discountItem['coupon_value'])) {
+                        $discount['value'] += $discountItem['coupon_value'];
+                    }
+                }
+                if (!empty($discount['coupon_name'])) {
+                    $discount['coupon_name'] = rtrim($discount['coupon_name'], ', ');
+                }
+                if (!empty($discount['classify'])) {
+                    $discount['classify'] = rtrim($discount['classify'], ', ');
+                }
+            }
+            $discount['value'] = sprintf("%.2f", $discount['value']);
+            $order->discount = $discount;
+
+            // $order->premises = $order->premises ?? $premises;
+            $data = [
+                'order' => $order,
+                'refund' => json_decode(json_encode($refund)),
+                'sheet' => json_decode(json_encode($orderSheet)),
+                'coupons' => $coupons,
+                // 'express' => json_decode(json_encode($express)),
+                'payDetails' => json_decode(json_encode($payDetails))
+            ];
+
+            return json_success('', $data);
+
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+        }
+    }
+
+    /**
+     * @Desc 获取分期的订单
+     * @Author Gorden
+     * @Date 2024/9/10 15:14
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function getPaidOrder(Request $request)
+    {
+        $memberId = $request->get('member_id');
+        if (!$memberId) {
+            return json_fail('参数异常');
+        }
+
+        $order = Order::where('join_order_member_id', $memberId)
+            ->where('order_category', 'COMBINE')
+            ->where('order_status_system', 'PAYING')
+            ->select('order_id', 'order_amount_total', 'order_amount_paid', 'order_amount_pay')
+            ->first();
+
+        return json_success('', $order);
+    }
+
+    /**
+     * @Desc 上传支付凭证
+     * @Author Gorden
+     * @Date 2024/9/12 10:14
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function uploadTicket(Request $request)
+    {
+        $payId = $request->post('pay_id');
+        $url = $request->post('url');
+        if (!$payId || !$url) {
+            return json_fail("参数异常");
+        }
+        $url = str_replace(getenv("STORAGE_DOMAIN"), '', $url);
+        try {
+            $payDetail = PayDetail::where('pay_id', $payId)->first();
+            if (!$payDetail) {
+                return json_fail('数据异常');
+            }
+            $extendJson = [];
+            if (!empty($payDetail->pay_extend_json)) {
+                $extendJson = json_decode($payDetail->pay_extend_json, true);
+            }
+            $extendJson['ticket'] = $url;
+
+            $payDetail->pay_extend_json = json_encode($extendJson);
+            dump($payDetail->save());
+
+            return json_success('success');
+        } catch (\Exception $e) {
+            return json_fail("上传附件失败");
+        }
+    }
+
+
+    /**
+     * @Desc 查询订单状态
+     * @Author Gorden
+     * @Date 2024/8/19 11:55
+     *
+     * @param Request $request
+     * @return Response|void
+     */
+    public function getOrderPayStatus(Request $request)
+    {
+        $groupId = $request->get('group_id');
+        if (!$groupId) {
+            return json_fail('参数异常');
+        }
+
+        $order = Order::where('order_groupby', $groupId)->first();
+        if (empty($order)) {
+            return json_fail('订单不存在');
+        }
+        $order = $order->toArray();
+        $sheet = OrderSheet::where('join_sheet_order_id', $order['order_id'])
+            ->select('order_sheet_id', 'join_sheet_goods_id', 'join_sheet_goods_sku_id')
+            ->first();
+
+        $payDetailType = PayDetail::where('join_pay_order_id', $groupId)->pluck('pay_prepayid')->toArray();
+        try {
+            Db::beginTransaction();
+            $payStatus = 'N';
+            $payAmount = 0;
+            if (in_array('WXPAY', $payDetailType)) {
+                $result = Pay::wechat(config('payment.wxpay'))->find($groupId, 'pos');
+                $result = json_decode(json_encode($result), true);
+//                $result = '{"return_code":"SUCCESS","return_msg":"OK","result_code":"SUCCESS","mch_id":"1680393367","appid":"wxc6274da7198e3eb4","openid":"o3JAn6Ii_bAlxS-jbNEC4WnPhdwM","is_subscribe":"N","trade_type":"MICROPAY","trade_state":"SUCCESS","bank_type":"OTHERS","total_fee":"2","fee_type":"CNY","cash_fee":"1000","cash_fee_type":"CNY","transaction_id":"4200067718202409250802875650","out_trade_no":"OD24092518408RV7","attach":[],"time_end":"20240925184009","trade_state_desc":"支付成功","nonce_str":"OeGOkjch4eaV5qIt","sign":"6DCB3BFC594EBC018A2BEE2C3DFEA4E3"}';
+//                $result = json_decode($result, true);
+                if (!empty($result['return_code']) && $result['return_code'] == 'SUCCESS' && !empty($result['result_code']) && $result['result_code'] == 'SUCCESS' && !empty($result['trade_state']) && $result['trade_state'] == 'SUCCESS') {
+                    $payStatus = 'Y';
+                    $payAmount = ($result['total_fee'] / 100);
+                    $orderUpdateData['order_status_payment'] = 'PENDING';
+                    $orderUpdateData['order_amount_paid'] = $order['order_amount_paid'] + $payAmount;
+                    $orderUpdateData['order_amount_pay'] = $order['order_amount_pay'] + $payAmount;
+                    if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                        $orderUpdateData['order_status_payment'] = 'SUCCESS';
+                        $orderUpdateData['order_status_system'] = 'DONE';
+                        $orderUpdateData['order_is_complete'] = 'Y';
+                    }
+                    // 主订单
+                    Order::where('order_groupby', $groupId)->update($orderUpdateData);
+                    // Sheet
+                    OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
+
+                    // 支付记录
+                    PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'WXPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
+                }
+            } else if (in_array('ALIPAY', $payDetailType)) {
+                $result = Pay::alipay(config('payment.alipay'))->find($groupId);
+                $result = json_decode(json_encode($result), true);
+//                $result = '{"code":"10000","msg":"Success","buyer_logon_id":"138******93","buyer_pay_amount":"5.87","fund_bill_list":[{"amount":"5.69","fund_channel":"ALIPAYACCOUNT"},{"amount":"0.18","fund_channel":"COUPON"},{"amount":"0.13","fund_channel":"DISCOUNT"}],"invoice_amount":"5.69","out_trade_no":"OD2409251550Q07A","point_amount":"0.00","receipt_amount":"0.01","send_pay_date":"2024-09-25 15:50:22","total_amount":"0.01","trade_no":"2024092523001439431419750998","trade_status":"TRADE_SUCCESS","buyer_open_id":"04309N2aVhSZz4cKwN_DN2DQa7ekM3z5n8kscQHsmIZOJsf"}';
+//                $result = json_decode($result, true);
+                if (!empty($result['code']) && $result['code'] == '10000' && !empty($result['trade_status']) && $result['trade_status'] == 'TRADE_SUCCESS') {
+                    $payStatus = 'Y';
+                    $payAmount = $result['total_amount'];
+                    $orderUpdateData['order_status_payment'] = 'PENDING';
+                    $orderUpdateData['order_amount_paid'] = $order['order_amount_paid'] + $payAmount;
+                    $orderUpdateData['order_amount_pay'] = $order['order_amount_pay'] + $payAmount;
+                    if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                        $orderUpdateData['order_status_payment'] = 'SUCCESS';
+                        $orderUpdateData['order_status_system'] = 'DONE';
+                        $orderUpdateData['order_is_complete'] = 'Y';
+                    }
+                    // 主订单
+                    Order::where('order_groupby', $groupId)->update($orderUpdateData);
+                    // Sheet
+                    OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
+                    // 支付记录
+                    PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'ALIPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
+                }
+            }
+            $full = '';
+            if ($payStatus == 'Y') {
+                Db::commit();
+                if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                    $full = '-满额';
+                    $params['member_id'] = $order['join_order_member_id'];
+                    $params['join_sheet_goods_id'] = $sheet->join_sheet_goods_id;
+                    Event::dispatch('order.new_custom.grant', $params);
+                }
+
+                // 入收支明细表
+                $params['orderId'] = $order['order_id'];
+                $params['inout_category'] = '购买组合包收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+
+                return json_success('success');
+            }
+
+
+            Db::rollBack();
+            return json_fail('没有查询到记录');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            _syslog("支付轮询", '订单支付异常:' . $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            Db::rollBack();
+            return json_fail('查询失败');
+        }
+    }
+
+    /**
+     * @Desc 检查产品配置
+     * @Author Gorden
+     * @Date 2024/9/28 15:42
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public function checkGoodsConfig($params)
+    {
+        foreach ($params['goodsContentList'] as $goods) {
+            $goods = Goods::where('goods_id', $goods['goods_id'])->select('goods_attribute_json')->first();
+            if (!empty($goods)) {
+                $goodsAttributeJson = json_decode($goods->goods_attribute_json, true);
+                // 不允许分期
+                if (isset($goodsAttributeJson['control']) && $goodsAttributeJson['control']['if_installment'] == 'N') {
+                    if ($params['order_amount_total'] > $params['order_amount_pay']) {
+                        throw new BusinessException('不支持分期支付');
+                    }
+                }
+                // 不允许重复购买
+                if (isset($goodsAttributeJson['control']) && $goodsAttributeJson['control']['if_repeat_buy'] == 'N') {
+                    if (Order::where('join_order_member_id', $params['join_order_member_id'])->where('order_classify', 'COMBINE')->exists()) {
+                        throw new BusinessException('仅可购买一次');
+                    }
+                }
+            }
+        }
+    }
+}

+ 30 - 7
app/admin/controller/order/PackagesController.php

@@ -175,7 +175,7 @@ class PackagesController extends Curd
         if ($field) {
             $model = $model->orderBy($field, $order);
         }
-        $model = $model->select('order.*', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_remark');
+        $model = $model->select('order.*', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_refund_json', 'order_return.order_return_remark');
 
         return $model;
     }
@@ -195,6 +195,7 @@ class PackagesController extends Curd
                 $item['sheet']['order_sheet_num'] = intval($item['sheet']['order_sheet_num']);
             }
             unset($item['sheets']);
+            $item['order_return_amount'] = 0;
             if (isset($item['orders_return_id'])) {
                 $item['return'] = [
                     'orders_return_id' => $item['orders_return_id'],
@@ -203,6 +204,12 @@ class PackagesController extends Curd
                     'order_return_apply_json' => $item['order_return_apply_json'],
                     'order_return_remark' => $item['order_return_remark']
                 ];
+                if (!empty($item['order_return_refund_json'])) {
+                    $refundJson = json_decode($item['order_return_refund_json'], true);
+                    if (isset($refundJson['amount'])) {
+                        $item['order_return_amount'] = $refundJson['amount'];
+                    }
+                }
             }
             $item['have_success_paydetail'] = 'N';
             if (PayDetail::where('join_pay_order_id', $item['order_groupby'])
@@ -689,10 +696,16 @@ class PackagesController extends Curd
 
             Db::commit();
 
-            // 会员升级
+            // 触发事件
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 Event::dispatch('order.complete', $params);
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
+            }
+            // 触发事件
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 上级提成
+                OrderService::splitOrderCommission($params);
+                // 入收支明细表
+                OrderService::splitOrderStatisticsInOut($params);
             }
 
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
@@ -1015,10 +1028,16 @@ class PackagesController extends Curd
 
             Db::commit();
 
-            // 会员升级
+            // 触发事件
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 Event::dispatch('order.complete', $params);
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
+            }
+            // 触发事件
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 上级提成
+                OrderService::splitOrderCommission($params);
+                // 入收支明细表
+                OrderService::splitOrderStatisticsInOut($params);
             }
 
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
@@ -1073,6 +1092,7 @@ class PackagesController extends Curd
         }
 
         $order = Order::where('order_id', $params['order_id'])->first();
+        $oldOrderStatus = $order->order_status_system;
         if (!$order) {
             return json_fail('订单异常');
         }
@@ -1358,7 +1378,7 @@ class PackagesController extends Curd
                 if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
                     $params['pay_category'] = 'WXPAY';
                     if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1370,7 +1390,7 @@ class PackagesController extends Curd
                 } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
                     $params['pay_category'] = 'ALIPAY';
                     if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1915,6 +1935,9 @@ class PackagesController extends Curd
             if ($order->order_status_payment == 'SUCCESS') {
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = '标准订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票

+ 177 - 16
app/admin/controller/order/PartnerController.php

@@ -32,6 +32,7 @@ use support\Redis;
 use support\Request;
 use support\Response;
 use Webman\Event\Event;
+use Yansongda\Pay\Pay;
 
 class PartnerController extends Curd
 {
@@ -205,6 +206,7 @@ class PartnerController extends Curd
         // 判断餐品是否连带着服务或实体
         $goodsClassifys = array_unique(array_column($params['goodsContentList'], 'goods_classify'));
         $params['goods_classify'] = $goodsClassifys[0];
+        $orderExtendJson = [];
         Db::beginTransaction();
         try {
             // 验证线下付款密码
@@ -214,6 +216,14 @@ class PartnerController extends Curd
                     throw new BusinessException('密码错误,请重新输入');
                 }
             }
+            if (!empty($params['dept_id'])) {
+                $dept = SysDept::where('dept_id', $params['dept_id'])->first();
+                if (!$dept) {
+                    throw new BusinessException("入伙场所不存在");
+                }
+                $params['dept_name'] = $dept->dept_name;
+                $orderExtendJson = ['dept_id' => $params['dept_id'], 'dept_name' => $params['dept_name']];
+            }
             // 下单账户
             if (empty($params['join_order_member_id']) && !empty($params['mobile'])) {
                 if (Member::where('member_mobile', $params['mobile'])->exists()) {
@@ -229,7 +239,6 @@ class PartnerController extends Curd
                 throw new BusinessException('检查下单账户');
             }
 
-            $qrcodePayAmount = 0;
             $params['orderId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
             $params['orderGroupId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
             // 查询是否有未完全支付订单
@@ -290,6 +299,7 @@ class PartnerController extends Curd
             }
 
             if (empty($paidOrder)) {
+                $params['order_extend_json'] = $orderExtendJson;
                 // 写入主订单
                 $this->insertMain($params);
                 // 订单详情
@@ -314,17 +324,22 @@ class PartnerController extends Curd
             }
             // 支付记录
             $this->insertPayDetail($params);
-
+            Db::commit();
             // 分期付完
+            $full = '';
             if ($params['goods_classify'] == 'PARTNER' && $params['order_status_payment'] == 'SUCCESS' && (!empty($paidOrder) && floatval($paidOrder->order_amount_paid) >= $paidOrder->order_amount_total || floatval($params['order_amount_pay']) >= $params['order_amount_total'])) {
+                $full = '-满额';
                 $params['member_id'] = $params['join_order_member_id'];
                 Event::dispatch('order.partner.grant', $params);
             }
-
-            Db::commit();
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买会员合伙人收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+            }
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
                 _syslog("订单", "支付异常,检查是否有轮询");
-                return json_throw(2001, '支付异常', ['order_id' => $params['orderId']]);
+                return json_throw(2001, '支付异常', ['order_id' => $params['orderId'], 'group_id' => $params['orderGroupId']]);
             }
             _syslog("订单", "创建订单成功");
             return json_success('创建订单成功');
@@ -383,7 +398,21 @@ class PartnerController extends Curd
         }
         $goods = $goods->toArray();
         $params['goods_classify'] = $goods['goods_classify'] ?? '';
-
+        // 入伙场所
+        if (!empty($params['dept_id'])) {
+            $orderExtendJson = [];
+            if (!empty($order->order_extend_json)) {
+                $orderExtendJson = json_decode($order->order_extend_json, true);
+            }
+            $dept = SysDept::where('dept_id', $params['dept_id'])->first();
+            if (!$dept) {
+                throw new BusinessException("入伙场所不存在");
+            }
+            $params['dept_name'] = $dept->dept_name;
+            $orderExtendJson['dept_id'] = $params['dept_id'];
+            $orderExtendJson['dept_name'] = $params['dept_name'];
+            $order->order_extend_json = json_encode($orderExtendJson);
+        }
         // 立即结算
         if ($params['goods_classify'] == 'PARTNER') {
             $order->order_is_complete = 'Y';
@@ -451,7 +480,7 @@ class PartnerController extends Curd
                 // 账户支付的金额
                 $params['order_amount_pay'] = $accountAmount;
             }
-            if ($order->order_status_payment == 'SUCCESS'){
+            if ($order->order_status_payment == 'SUCCESS') {
                 $order->order_amount_paid = $order->order_amount_paid + $params['order_amount_pay'];
                 $order->order_amount_pay = $order->order_amount_paid;
             }
@@ -521,21 +550,24 @@ class PartnerController extends Curd
 
             PayDetail::insert($payData);
 
+            Db::commit();
+
             // 分期付款完成
+            $full = '';
             if ($params['goods_classify'] == 'PARTNER' && $order->order_status_payment == 'SUCCESS' && floatval($order->order_amount_paid) >= $order->order_amount_total) {
+                $full = '-满额';
                 $params['member_id'] = $params['join_order_member_id'];
+                Event::dispatch('order.complete', $params);
                 Event::dispatch('order.partner.grant', $params);
             }
-
-            Db::commit();
-
-            if ($order->order_is_complete == 'Y' && $order->order_status_payment == 'SUCCESS') {
-                Event::dispatch('order.complete', $params);
+            if ($order->order_status_payment == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买会员合伙人收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
             }
-
             if ($order->order_status_payment != 'SUCCESS' && $paymentStatus != 'SUCCESS') {
                 _syslog("订单", "支付异常,检查是否有轮询");
-                return json_throw(2001, '支付异常', ['order_id' => $params['orderId']]);
+                return json_throw(2001, '支付异常', ['order_id' => $params['orderId'], 'group_id' => $params['orderGroupId']]);
             }
             _syslog("订单", "订单支付成功");
             return json_success('支付成功');
@@ -759,7 +791,7 @@ class PartnerController extends Curd
             $orderId = $request->get('order_id');
             $orderSheet = OrderSheet::with([
                 'member' => function ($query) {
-                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'join_member_role_id');
+                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'join_member_role_id', 'member_is_vip', 'member_is_partner', 'member_is_referrer');
                 },
                 'goods' => function ($query) {
                     $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_market_price', 'goods_sales_price', 'goods_classify', 'goods_if_express');
@@ -979,6 +1011,9 @@ class PartnerController extends Curd
                 if (isset($orderExtendJson['free_remark'])) {
                     $order->free_remark = $orderExtendJson['free_remark'];
                 }
+                if (isset($orderExtendJson['dept_id'])) {
+                    $order->dept_id = $orderExtendJson['dept_id'];
+                }
             }
             $discount = ['coupon_name' => '', 'classify' => '', 'value' => 0];
             if (!empty($order->order_discount_json)) {
@@ -1053,7 +1088,7 @@ class PartnerController extends Curd
         $order = Order::where('join_order_member_id', $memberId)
             ->where('order_category', 'PARTNER')
             ->where('order_status_system', 'PAYING')
-            ->select('order_id', 'order_amount_total', 'order_amount_paid', 'order_amount_pay')
+            ->select('order_id', 'order_amount_total', 'order_amount_paid', 'order_amount_pay', 'order_extend_json')
             ->first();
 
         return json_success('', $order);
@@ -1094,4 +1129,130 @@ class PartnerController extends Curd
             return json_fail("上传附件失败");
         }
     }
+
+
+    /**
+     * @Desc 查询订单状态
+     * @Author Gorden
+     * @Date 2024/8/19 11:55
+     *
+     * @param Request $request
+     * @return Response|void
+     */
+    public function getOrderPayStatus(Request $request)
+    {
+        $groupId = $request->get('group_id');
+        if (!$groupId) {
+            return json_fail('参数异常');
+        }
+
+        $order = Order::where('order_groupby', $groupId)->first();
+        if (empty($order)) {
+            return json_fail('订单不存在');
+        }
+        $order = $order->toArray();
+        $sheet = OrderSheet::where('join_sheet_order_id', $order['order_id'])
+            ->select('order_sheet_id', 'join_sheet_goods_id', 'join_sheet_goods_sku_id')
+            ->first();
+        $payDetailType = PayDetail::where('join_pay_order_id', $groupId)->pluck('pay_prepayid')->toArray();
+        try {
+            Db::beginTransaction();
+            $payStatus = 'N';
+            $payAmount = 0;
+            if (in_array('WXPAY', $payDetailType)) {
+                $result = Pay::wechat(config('payment.wxpay'))->find($groupId, 'pos');
+                $result = json_decode(json_encode($result), true);
+//                $result = '{"return_code":"SUCCESS","return_msg":"OK","result_code":"SUCCESS","mch_id":"1680393367","appid":"wxc6274da7198e3eb4","openid":"o3JAn6Ii_bAlxS-jbNEC4WnPhdwM","is_subscribe":"N","trade_type":"MICROPAY","trade_state":"SUCCESS","bank_type":"OTHERS","total_fee":"50","fee_type":"CNY","cash_fee":"1000","cash_fee_type":"CNY","transaction_id":"4200067718202409250802875650","out_trade_no":"OD24092518408RV7","attach":[],"time_end":"20240925184009","trade_state_desc":"支付成功","nonce_str":"OeGOkjch4eaV5qIt","sign":"6DCB3BFC594EBC018A2BEE2C3DFEA4E3"}';
+//                $result = json_decode($result, true);
+                if (!empty($result['return_code']) && $result['return_code'] == 'SUCCESS' && !empty($result['result_code']) && $result['result_code'] == 'SUCCESS' && !empty($result['trade_state']) && $result['trade_state'] == 'SUCCESS') {
+                    $payStatus = 'Y';
+                    $payAmount = ($result['total_fee'] / 100);
+                    $orderUpdateData['order_status_payment'] = 'PENDING';
+                    $orderUpdateData['order_amount_paid'] = $order['order_amount_paid'] + $payAmount;
+                    $orderUpdateData['order_amount_pay'] = $order['order_amount_pay'] + $payAmount;
+                    if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                        $orderUpdateData['order_status_payment'] = 'SUCCESS';
+                        $orderUpdateData['order_status_system'] = 'DONE';
+                        $orderUpdateData['order_is_complete'] = 'Y';
+                    }
+                    // 主订单
+                    Order::where('order_groupby', $groupId)->update($orderUpdateData);
+                    // Sheet
+                    OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
+
+                    // 支付记录
+                    PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'WXPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
+                }
+            } else if (in_array('ALIPAY', $payDetailType)) {
+                $result = Pay::alipay(config('payment.alipay'))->find($groupId);
+                $result = json_decode(json_encode($result), true);
+//                $result = '{"code":"10000","msg":"Success","buyer_logon_id":"138******93","buyer_pay_amount":"5.87","fund_bill_list":[{"amount":"5.69","fund_channel":"ALIPAYACCOUNT"},{"amount":"0.18","fund_channel":"COUPON"},{"amount":"0.13","fund_channel":"DISCOUNT"}],"invoice_amount":"5.69","out_trade_no":"OD2409251550Q07A","point_amount":"0.00","receipt_amount":"0.01","send_pay_date":"2024-09-25 15:50:22","total_amount":"0.01","trade_no":"2024092523001439431419750998","trade_status":"TRADE_SUCCESS","buyer_open_id":"04309N2aVhSZz4cKwN_DN2DQa7ekM3z5n8kscQHsmIZOJsf"}';
+//                $result = json_decode($result, true);
+                if (!empty($result['code']) && $result['code'] == '10000' && !empty($result['trade_status']) && $result['trade_status'] == 'TRADE_SUCCESS') {
+                    $payStatus = 'Y';
+                    $payAmount = $result['total_amount'];
+                    $orderUpdateData['order_status_payment'] = 'PENDING';
+                    $orderUpdateData['order_amount_paid'] = $order['order_amount_paid'] + $payAmount;
+                    $orderUpdateData['order_amount_pay'] = $order['order_amount_pay'] + $payAmount;
+                    if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                        $orderUpdateData['order_status_payment'] = 'SUCCESS';
+                        $orderUpdateData['order_status_system'] = 'DONE';
+                        $orderUpdateData['order_is_complete'] = 'Y';
+                    }
+                    // 主订单
+                    Order::where('order_groupby', $groupId)->update($orderUpdateData);
+                    // Sheet
+                    OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
+                    // 支付记录
+                    PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'ALIPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
+                }
+            }
+            $full = '';
+            if ($payStatus == 'Y') {
+                if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                    $full = '-满额';
+                    $params['member_id'] = $order['join_order_member_id'];
+                    $params['orderId'] = $order['order_id'];
+                    $params['order_amount_pay'] = $payAmount;
+                    $params['order_amount_total'] = $order['order_amount_total'];
+                    $params['join_sheet_goods_id'] = $sheet->join_sheet_goods_id;
+                    if (!empty($order['order_extend_json'])) {
+                        $orderExtendJson = json_decode($order['order_extend_json'], true);
+                        $params['dept_id'] = $orderExtendJson['dept_id'] ?? '';
+                        $params['dept_name'] = $orderExtendJson['dept_name'] ?? '';
+                    }
+
+                    Event::dispatch('order.partner.grant', $params);
+                }
+
+                Db::commit();
+
+                // 入收支明细表
+                $params['orderId'] = $order['order_id'];
+                $params['inout_category'] = '购买会员合伙人收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+
+                return json_success('success');
+            }
+
+            Db::rollBack();
+            return json_fail('没有查询到记录');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            _syslog("支付轮询", '订单支付异常:' . $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            Db::rollBack();
+            return json_fail('查询失败');
+        }
+    }
 }

+ 133 - 18
app/admin/controller/order/PayDetailController.php

@@ -3,9 +3,11 @@
 namespace app\admin\controller\order;
 
 use app\admin\controller\notify\RechargeController;
+use app\admin\service\member\MemberService;
 use app\controller\Curd;
 use app\model\Goods;
 use app\model\GoodsSku;
+use app\model\Member;
 use app\model\MemberAccount;
 use app\model\Order;
 use app\model\OrderSheet;
@@ -15,6 +17,7 @@ use support\exception\BusinessException;
 use support\Request;
 use support\Response;
 
+use Webman\Event\Event;
 use function PHPSTORM_META\type;
 
 class PayDetailController extends Curd
@@ -33,6 +36,8 @@ class PayDetailController extends Curd
         if ($where['pay_category'] == 'OTHER') {
             $where['pay_status'] = 'SUCCESS';
             $where['pay_category'] = ['<>', 'RECHARGE'];
+        } elseif ($where['pay_category'] == 'RECHARGE') {
+            $where['pay_category'] = ['in', ['RECHARGE', 'VIP']];
         }
         if (!empty($where['pay_addtimes'])) {
             $where['pay_addtimes'][0] = !empty($where['pay_addtimes'][0]) ? strtotime($where['pay_addtimes'][0]) : '';
@@ -53,11 +58,11 @@ class PayDetailController extends Curd
         //     $where['pay_category'] = 'RECHARGE';
         //     $where['pay_prepayid'] = ['in','WXPAY,ALIPAY'];
         // }
-        $query = $this->doSelect($where, $field, $order,$request->get());
+        $query = $this->doSelect($where, $field, $order, $request->get());
         return $this->doFormat($query, $format, $limit);
     }
 
-    protected function doSelect(array $where, string $field = null, string $order = 'desc',$params=[])
+    protected function doSelect(array $where, string $field = null, string $order = 'desc', $params = [])
     {
         $model = $this->model->with(['member', 'cert', 'memberAccount']);
         foreach ($where as $column => $value) {
@@ -89,25 +94,43 @@ class PayDetailController extends Curd
                 $model = $model->where($column, $value);
             }
         }
-        if (empty($params['type'])){
+        if (empty($params['type'])) {
             $model = $model->where(function ($query) {
-                $query->whereIn('pay_prepayid', ['ALIPAY', 'WXPAY','OFFLINE_WXPAY','OFFLINE_ALIPAY','MONEY'])
+                $query->whereIn('pay_prepayid', ['ALIPAY', 'WXPAY', 'OFFLINE', 'OFFLINE_WXPAY', 'OFFLINE_ALIPAY', 'MONEY'])
                     ->orWhere('pay_prepayid', 'like', '%CASH%')
                     ->orWhere('pay_prepayid', 'like', '%WELFARE%');
             });
-        }else{
-            if ($params['type'] == 'NORMAL'){
+        } else {
+            if ($params['type'] == 'NORMAL') {
                 $model = $model->where(function ($query) {
-                    $query->whereIn('pay_prepayid', ['ALIPAY', 'WXPAY','OFFLINE_WXPAY','OFFLINE_ALIPAY','MONEY'])
+                    $query->whereIn('pay_prepayid', ['ALIPAY', 'WXPAY', 'OFFLINE_WXPAY', 'OFFLINE_ALIPAY', 'MONEY'])
                         ->orWhere('pay_prepayid', 'like', '%CASH%');
                 });
-            }else{
+            } else if ($params['type'] == 'VIP') {
+                $model = $model->where(function ($query) {
+                    $query->orWhere('pay_category', 'VIP');
+                });
+            } else {
                 $model = $model->where(function ($query) {
                     $query->orWhere('pay_prepayid', 'like', '%WELFARE%');
                 });
             }
         }
 
+        if (!empty($params['order_type'])) {
+            if ($params['order_type'] == 'COMBINE') {
+                $model = $model->where('join_pay_order_id', 'COMBINE');
+            } elseif ($params['order_type'] == 'PARTNER') {
+                $model = $model->where('join_pay_order_id', 'PARTNER');
+            } elseif ($params['order_type'] == 'VIP') {
+                $model = $model->where('pay_category', 'VIP');
+            } elseif ($params['order_type'] == 'RECHARGE') {
+                $model = $model->where('join_pay_order_id', '<>', 'PARTNER')
+                    ->where('join_pay_order_id', '<>', 'COMBINE')
+                    ->where('pay_category', '<>', 'VIP');
+            }
+        }
+
 
         if ($field) {
             $model = $model->orderBy($field, $order);
@@ -180,12 +203,15 @@ class PayDetailController extends Curd
     {
         foreach ($items as &$item) {
             $prepayId = explode('-', $item->pay_prepayid);
+            $item->order_classify = $item->pay_category;
             if (count($prepayId) > 1) {
                 $item->pay_prepayid = $prepayId[1];
             }
             $item->type = 'NORMAL';
-            if (isset($prepayId[1]) && $prepayId[1] == 'WELFARE'){
+            if (isset($prepayId[1]) && $prepayId[1] == 'WELFARE') {
                 $item->type = 'WELFARE';
+            } else if ($item->pay_category == 'VIP') {
+                $item->type = 'VIP';
             }
             $memberAccount = [];
             if (!empty($item->memberAccount)) {
@@ -204,6 +230,54 @@ class PayDetailController extends Curd
                     $item->premises_name = $payExtendJson['remark']['premises_name'] ?? '';
                 }
             }
+            $item->to_account_amount = $item->pay_amount;
+            if (in_array($item['join_pay_order_id'], ['COMBINE', 'PARTNER']) && !empty($item->join_pay_object_json)) {
+                $item->order_classify = $item['join_pay_order_id'];
+                $payObjectJson = json_decode($item->join_pay_object_json, true);
+                if (isset($payObjectJson['recharge_order_id'])) {
+                    $payDetails = PayDetail::whereJsonContains('join_pay_object_json->order_id', $payObjectJson['recharge_order_id'])
+                        ->where('pay_status', 'SUCCESS')
+                        ->get()
+                        ->toArray();
+                    if (count($payDetails) > 1) {
+                        $item->pay_prepayid = 'BY_STAGES';
+                    } else if (count($payDetails) == 1) {
+                        $item->pay_prepayid = $payDetails[0]['pay_prepayid'];
+                    }
+
+                    $item->pay_amount = PayDetail::whereJsonContains('join_pay_object_json->order_id', $payObjectJson['recharge_order_id'])
+                        ->where('pay_status', 'SUCCESS')
+                        ->sum('pay_amount');
+                    $orderStatus = OrderSheet::where('join_sheet_order_id', $payObjectJson['recharge_order_id'])->value('order_sheet_status');
+                    if ($orderStatus == 'BEING') {
+                        $item->pay_status = 'BEING';
+                    }
+                }
+
+//                $payDetail = PayDetail::where('join_pay_member_id', $item->join_pay_member_id)
+//                    ->where('pay_category', $item['join_pay_order_id'])
+//                    ->select('pay_amount', 'join_pay_object_json')
+//                    ->first();
+//                $item->pay_amount = $payDetail->pay_amount ?? 0;
+//                if (!empty($payDetail->join_pay_object_json)) {
+//                    $payObjectJson = json_decode($payDetail->join_pay_object_json, true);
+//                    if (isset($payObjectJson['order_id'])) {
+//                        $orderStatus = OrderSheet::where('join_sheet_order_id', $payObjectJson['order_id'])->value('order_sheet_status');
+//                        if ($orderStatus == 'BEING') {
+//                            $item->pay_status = 'BEING';
+//                        }
+//                    }
+//                }
+            }
+            if ($item->pay_category == 'VIP') {
+                $payObjectJson = json_decode($item->join_pay_object_json, true);
+                if (isset($payObjectJson['order_id'])) {
+                    $orderStatus = OrderSheet::where('join_sheet_order_id', $payObjectJson['order_id'])->value('order_sheet_status');
+                    if ($orderStatus == 'BEING') {
+                        $item->pay_status = 'BEING';
+                    }
+                }
+            }
         }
 
         return $items;
@@ -220,7 +294,8 @@ class PayDetailController extends Curd
     public function goodsOrder(Request $request)
     {
         $orderId = $request->get('order_id', '');
-        if (!$orderId) {
+        $memberId = $request->get('member_id', '');
+        if (!$orderId || !$memberId) {
             return json_fail('参数异常');
         }
         $orderSheet = OrderSheet::with([
@@ -237,7 +312,39 @@ class PayDetailController extends Curd
             ->get()
             ->toArray();
 
-        return json_success('', $orderSheet);
+        $member = Member::with([
+            'cert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            },
+            'info' => function ($query) {
+                $query->select('join_info_member_id', 'member_info_nickname', 'member_info_headimg');
+            }
+        ])->where('member_id', $memberId)
+            ->select('member_id', 'member_mobile', 'join_member_role_id', 'member_is_owner', 'member_is_vip', 'member_is_partner', 'member_is_referrer')
+            ->first();
+        if (!$member){
+            return json_fail('数据异常');
+        }
+        $member = $member->toArray();
+        $certName = $member['cert']['member_cert_name'] ?? '';
+        $nickname = $member['info']['member_info_nickname'] ?? '';
+        $mobile = $member['member_mobile'] ?? '';
+        $member['member_name'] = MemberService::getMemberCertName($mobile, $certName, $nickname);
+        $member['info']['member_info_headimg'] = MemberService::getAvatarUrl($member['info']['member_info_headimg'] ?? '');
+        $member['level'] = MemberService::getRoleName($member['join_member_role_id']);
+
+        $payDetails = PayDetail::whereJsonContains('join_pay_object_json->order_id', $orderId)
+            ->where('pay_status', 'SUCCESS')
+            ->select('pay_status', 'pay_amount', 'pay_paytimes', 'pay_prepayid')
+            ->get();
+
+        $data = [
+            'payDetails' => $payDetails,
+            'goodsOrder' => $orderSheet,
+            'member' => $member
+        ];
+
+        return json_success('', $data);
     }
 
     /**
@@ -258,7 +365,7 @@ class PayDetailController extends Curd
         try {
             $data = $this->insertRechargeInput($request);
             $data['pay_prepayid'] = $request->post('pay_prepayid', '');
-
+            unset($data['orderId']);
             $payDetailId = $this->doInsert($data);
             (new RechargeController)->disposePaySuccess($payDetailId);
 
@@ -332,22 +439,30 @@ class PayDetailController extends Curd
         Db::beginTransaction();
         try {
             $data = $this->insertRechargeInput($request, 'WELFARE');
+            $orderId = $data['orderId'];
+            unset($data['orderId']);
             $data['pay_prepayid'] = $data['join_pay_member_id'] . '-WELFARE';
             $memberAccount = MemberAccount::where('join_account_member_id', $data['join_pay_member_id'])
                 ->where('member_account_classify', 'WELFARE')
                 ->first();
-            if ($memberAccount->member_account_status != 'ACTIVED'){
+            if ($memberAccount->member_account_status != 'ACTIVED') {
                 throw new BusinessException('福利账户未开启');
             }
 
             $this->doInsert($data);
 
             // 金额累加到福利账户
-           $memberAccount->member_account_income = $memberAccount->member_account_income + $data['pay_amount'];
+            $memberAccount->member_account_income = $memberAccount->member_account_income + $data['pay_amount'];
             $memberAccount->member_account_surplus = $memberAccount->member_account_surplus + $data['pay_amount'];
             $memberAccount->save();
 
             Db::commit();
+
+            // 入收支明细表
+            $params['orderId'] = $orderId;
+            $params['inout_category'] = '会员充值订单收入';
+            Event::dispatch('statistics.inout.in', $params);
+
         } catch (BusinessException $customException) {
             Db::rollBack();
             return json_fail($customException->getMessage());
@@ -375,7 +490,7 @@ class PayDetailController extends Curd
         $goods = Goods::find($params['goods_id']);
         $extendJson = [];
         // if (!$params['is_custom']) {
-        Db::beginTransaction();
+//        Db::beginTransaction();
         try {
             if (!$params['is_custom']) {
                 $data['pay_amount'] = $goods->goods_sales_price;
@@ -422,13 +537,13 @@ class PayDetailController extends Curd
 
             OrderSheet::insert($orderSheetData);
 
-            Db::commit();
+//            Db::commit();
         } catch (\Exception $e) {
-            dump($e->getMessage());
-            Db::rollBack();
+//            Db::rollBack();
 
             throw new BusinessException("数据错误");
         }
+        $data['orderId'] = $orderId;
         $data['join_pay_order_id'] = $orderGroupId;
         $data['join_pay_object_json'] = json_encode(['order_id' => $orderId]);
 

+ 1246 - 0
app/admin/controller/order/ReferrerController.php

@@ -0,0 +1,1246 @@
+<?php
+
+namespace app\admin\controller\order;
+
+use app\admin\service\coupon\CouponService;
+use app\admin\service\member\MemberService;
+use app\admin\service\order\OrderService;
+use app\admin\validate\order\OrderValidate;
+use app\controller\Curd;
+use app\model\Appointment;
+use app\model\ClientConfig;
+use app\model\Coupon;
+use app\model\Goods;
+use app\model\GoodsComponent;
+use app\model\GoodsRunning;
+use app\model\GoodsSku;
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\MemberBenefit;
+use app\model\MemberRole;
+use app\model\Order;
+use app\model\OrderExpress;
+use app\model\OrderReturn;
+use app\model\OrderSheet;
+use app\model\PayDetail;
+use app\model\Supplier;
+use app\model\SysDept;
+use app\model\SysUser;
+use support\Db;
+use support\exception\BusinessException;
+use support\Redis;
+use support\Request;
+use support\Response;
+use Webman\Event\Event;
+use Yansongda\Pay\Pay;
+
+class ReferrerController extends Curd
+{
+    public function __construct()
+    {
+        $this->model = new Order();
+        $this->validate = true;
+        $this->validateClass = new OrderValidate();
+    }
+
+    /**
+     * @Desc 列表
+     * @Author Gorden
+     * @Date 2024/3/28 15:01
+     *
+     * @param Request $request
+     * @return Response
+     * @throws \support\exception\BusinessException
+     */
+    public function select(Request $request): Response
+    {
+        [$where, $format, $limit, $field, $order] = $this->selectInput($request);
+        $where['order_classify'] = 'REFERRER';
+        if (!empty($where['order_addtimes'])) {
+            $where['order_addtimes'][0] = strtotime($where['order_addtimes'][0]);
+            $where['order_addtimes'][1] = strtotime($where['order_addtimes'][1]);
+        }
+
+        $order = $request->get('order', 'desc');
+        $field = $field ?? 'order_addtimes';
+        $orderIds = [];
+        if (!empty($orderId)) {
+            $orderIds = Order::where('order_id', 'like', '%' . $orderId . '%')
+                ->where('order_classify', 'REFERRER')
+                ->pluck('order_id')
+                ->toArray();
+        }
+        $goodsName = trim($request->get('goods_name', ''));
+        if (!empty($goodsName)) {
+            $goodsIds = Goods::where('goods_classify', 'REFERRER')
+                ->where('goods_name', 'like', '%' . $goodsName . '%')
+                ->pluck('goods_id')
+                ->toArray();
+            $goodsOrderIds = OrderSheet::whereIn('join_sheet_goods_id', $goodsIds)->pluck('join_sheet_order_id')->toArray();
+            if (!empty($where['order_id'])) {
+                $orderIds = array_intersect($orderIds, $goodsOrderIds);
+            } else {
+                $orderIds = $goodsOrderIds;
+            }
+        }
+        if (!empty($orderId) || !empty($goodsName)) {
+            $where['order_id'] = ['in', implode(',', $orderIds)];
+        }
+
+        $query = $this->doSelect($where, $field, $order);
+        return $this->doFormat($query, $format, $limit);
+    }
+
+    protected function doSelect(array $where, string $field = null, string $order = 'desc')
+    {
+        $model = $this->model->with([
+            'sheets' => function ($query) {
+                $query->select('join_sheet_order_id', 'order_sheet_id', 'join_sheet_goods_id', 'order_sheet_num', 'order_sheet_price');
+            },
+            'member' => function ($query) {
+                $query->select('member_id', 'member_mobile');
+            },
+            'cert' => function ($query) {
+                $query->select('join_cert_member_id', 'member_cert_name');
+            },
+//            'return' => function ($query) use ($where){
+//                if (isset($where['return'])){
+//                    dump($where['return']);
+//                    $query = $query->where('order_return_status',$where['return']);
+//                }
+//                $query->select('orders_return_id', 'join_return_order_id', 'order_return_status');
+//            },
+            // 'express' => function ($query) {
+            //     $query->select('join_express_order_id', 'order_express_type');
+            // }
+        ])->leftJoin('order_return', 'order_return.join_return_order_id', '=', 'order.order_id')
+            ->leftJoin('order_express', 'order_express.join_express_order_id', '=', 'order.order_id');
+        // ->leftJoin('order_sheet','join_sheet_order_id','=','order.order_id');
+        foreach ($where as $column => $value) {
+            if (is_array($value)) {
+                if ($value[0] === 'like' || $value[0] === 'not like') {
+                    $model = $model->where($column, $value[0], "%$value[1]%");
+                } elseif (in_array($value[0], ['>', '=', '<', '<>'])) {
+                    $model = $model->where($column, $value[0], $value[1]);
+                } elseif ($value[0] == 'in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereIn($column, $valArr);
+                } elseif ($value[0] == 'not in' && !empty($value[1])) {
+                    $valArr = $value[1];
+                    if (is_string($value[1])) {
+                        $valArr = explode(",", trim($value[1]));
+                    }
+                    $model = $model->whereNotIn($column, $valArr);
+                } elseif ($value[0] == 'null') {
+                    $model = $model->whereNull($column);
+                } elseif ($value[0] == 'not null') {
+                    $model = $model->whereNotNull($column);
+                } elseif ($value[0] !== '' || $value[1] !== '') {
+                    $model = $model->whereBetween($column, $value);
+                }
+            } else {
+                $model = $model->where($column, $value);
+            }
+        }
+        if ($field) {
+            $model = $model->orderBy($field, $order);
+        }
+        $model = $model->select('order.*', 'order_express.join_express_order_id', 'order_express.order_express_type', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_remark');
+        return $model;
+    }
+
+    public function afterQuery($items)
+    {
+        foreach ($items as &$item) {
+            $sheetDeng = '';
+            $item['sheet'] = $item['sheets'][0] ?? [];
+            if (!empty($item['sheet'])) {
+                $goods = Goods::where('goods_id', $item['sheet']['join_sheet_goods_id'])->first();
+                if (count($item['sheets']) > 1 && $goods->goods_classify == 'MEALS') {
+                    $sheetDeng = ' 等餐品';
+                }
+                $item['sheet']['goods_name'] = ($goods && $goods->goods_name) ? $goods->goods_name . $sheetDeng : '';
+                $item['sheet']['goods_classify'] = $goods->goods_classify ?? '';
+                $item['sheet']['order_sheet_num'] = intval($item['sheet']['order_sheet_num']);
+            }
+            unset($item['sheets']);
+            if (isset($item['orders_return_id'])) {
+                $item['return'] = [
+                    'orders_return_id' => $item['orders_return_id'],
+                    'join_return_order_id' => $item['join_return_order_id'],
+                    'order_return_status' => $item['order_return_status'],
+                    'order_return_apply_json' => $item['order_return_apply_json'],
+                    'order_return_remark' => $item['order_return_remark']
+                ];
+            }
+            if (isset($item['join_express_order_id'])) {
+                $item['express'] = [
+                    'join_express_order_id' => $item['join_express_order_id'],
+                    'order_express_type' => $item['order_express_type']
+                ];
+                unset($item['join_express_order_id'], $item['order_express_type']);
+            }
+            // if (!empty($item['order_extend_json'])){
+            //     $orderExtendJson = json_decode($item['order_extend_json'],true);
+            //     $item['referee'] = $orderExtendJson['referee'] ?? '';
+            // }
+        }
+
+        return $items;
+    }
+
+    /**
+     * @Desc 下单+支付
+     * @Author Gorden
+     * @Date 2024/9/5 13:13
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function insert(Request $request): Response
+    {
+        $params = $request->post();
+        // 判断餐品是否连带着服务或实体
+        $goodsClassifys = array_unique(array_column($params['goodsContentList'], 'goods_classify'));
+        $params['goods_classify'] = $goodsClassifys[0];
+        $orderExtendJson = [];
+        Db::beginTransaction();
+        try {
+            // 验证线下付款密码
+            if ($params['settlement_now'] == 'Y' && $params['pay_constitute'] == 'N' && in_array($params['pay_category'], ['OFFLINE', 'MONEY'])) {
+                $password = $params['offline_password'];
+                if ($password != '666888') {
+                    throw new BusinessException('密码错误,请重新输入');
+                }
+            }
+            if (!empty($params['dept_id'])) {
+                $dept = SysDept::where('dept_id', $params['dept_id'])->first();
+                if (!$dept) {
+                    throw new BusinessException("入伙场所不存在");
+                }
+                $params['dept_name'] = $dept->dept_name;
+                $orderExtendJson = ['dept_id' => $params['dept_id'], 'dept_name' => $params['dept_name']];
+            }
+            // 下单账户
+            if (empty($params['join_order_member_id']) && !empty($params['mobile'])) {
+                if (Member::where('member_mobile', $params['mobile'])->exists()) {
+                    throw new BusinessException('会员已存在');
+                }
+                $params['join_order_member_id'] = $params['member_id'] = 'MR' . date('ymdHi') . random_string(4, 'up');
+                // 创建会员
+                MemberService::createMember($params);
+            } else if (empty($params['join_order_member_id']) && empty($params['mobile'])) {
+                $params['join_order_member_id'] = Member::where('member_mobile', '0000')->value('member_id');
+            }
+            if (empty($params['join_order_member_id'])) {
+                throw new BusinessException('检查下单账户');
+            }
+
+            $params['orderId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+            $params['orderGroupId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+            // 查询是否有未完全支付订单
+            $paidOrder = Order::where('join_order_member_id', $params['join_order_member_id'])
+                ->where('order_category', 'REFERRER')
+                ->where('order_status_system', 'PAYING')
+                ->first();
+            if (!empty($paidOrder)) {
+                $paidOrder->order_groupby = $params['orderGroupId'];
+                $params['orderId'] = $paidOrder->order_id;
+            }
+            $systemStatus = 'PAYING';
+            // 立即结算
+            if ($params['settlement_now'] == 'Y') {
+                if ($params['goods_classify'] == 'REFERRER') {
+                    $params['order_is_complete'] = 'Y';
+                    $systemStatus = 'DONE';
+                }
+            }
+
+            if ($params['settlement_now'] == 'Y' && ($params['pay_category'] == 'OFFLINE' || $params['pay_category'] == 'MONEY')) {
+                if ($params['pay_category'] == 'OFFLINE' && !empty($params['pay_category_sub'])) {
+                    $params['pay_category'] = $params['pay_category_sub'];
+                }
+                $params['order_status_system'] = $systemStatus;
+                $params['order_status_payment'] = 'SUCCESS';
+            }
+            if ($params['pay_category'] == 'QRCODE' && $params['settlement_now'] == 'Y' && !empty($params['qrcode_nbr'])) {     // 付款码
+                $result = OrderService::qrcodePay($params);
+                $result = json_encode($result);
+                $params['pay_json_response'] = $result;
+                $result = json_decode($result, true);
+
+                $prefix = substr($params['qrcode_nbr'], 0, 2);
+                if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
+                    $params['pay_category'] = 'WXPAY';
+                    if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
+                        $params['order_status_system'] = 'PAYING';
+                        $params['order_status_payment'] = 'PENDING';
+                        $params['order_is_complete'] = 'N';
+                    } else {
+                        $params['order_status_system'] = $systemStatus;
+                        $params['order_status_payment'] = 'SUCCESS';
+                    }
+                } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
+                    $params['pay_category'] = 'ALIPAY';
+                    if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
+                        $params['order_status_system'] = 'PAYING';
+                        $params['order_status_payment'] = 'PENDING';
+                        $params['order_is_complete'] = 'N';
+                    } else {
+                        $params['order_status_system'] = $systemStatus;
+                        $params['order_status_payment'] = 'SUCCESS';
+                    }
+                } else {
+                    throw new BusinessException('付款码无效');
+                }
+            }
+
+            if (empty($paidOrder)) {
+                $params['order_extend_json'] = $orderExtendJson;
+                // 写入主订单
+                $this->insertMain($params);
+                // 订单详情
+                $this->insertSheet($params);
+            } else {
+                $params['orderGroupId'] = $paidOrder->order_groupby;
+                if ($params['order_status_payment'] == 'SUCCESS') {
+                    $params['order_amount_paid'] = $paidOrder->order_amount_paid;
+                    $paidOrder->order_amount_paid = $paidOrder->order_amount_paid + $params['order_amount_pay'];
+                    $paidOrder->order_amount_pay = $paidOrder->order_amount_paid;
+                    if (floatval($paidOrder->order_amount_paid) >= $paidOrder->order_amount_total) {
+                        $paidOrder->order_is_complete = 'Y';
+                        $paidOrder->order_status_system = 'DONE';
+                        $paidOrder->order_status_payment = 'SUCCESS';
+                        // 更新sheet
+                        OrderSheet::where('join_sheet_order_id', $paidOrder->order_id)->update(['order_sheet_status' => 'DONE']);
+                    }
+                }
+                $paidOrder->save();
+                // 清除此订单的未支付记录
+                PayDetail::whereJsonContains('join_pay_object_json->order_id', $paidOrder->order_id)->where('pay_status', 'WAITING')->delete();
+            }
+            // 支付记录
+            $this->insertPayDetail($params);
+            Db::commit();
+            // 分期付完
+            $full = '';
+            if ($params['goods_classify'] == 'REFERRER' && $params['order_status_payment'] == 'SUCCESS' && (!empty($paidOrder) && floatval($paidOrder->order_amount_paid) >= $paidOrder->order_amount_total || floatval($params['order_amount_pay']) >= $params['order_amount_total'])) {
+                $full = '-满额';
+                $params['member_id'] = $params['join_order_member_id'];
+                Event::dispatch('order.referrer.grant', $params);
+            }
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买康养推荐官收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+            }
+
+            if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
+                _syslog("订单", "支付异常,检查是否有轮询");
+                return json_throw(2001, '支付异常', ['order_id' => $params['orderId'], 'group_id' => $params['orderGroupId']]);
+            }
+            _syslog("订单", "创建订单成功");
+            return json_success('创建订单成功');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            dump($e->getTrace());
+            _syslog("订单", $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            dump($e->getTrace());
+            _syslog("订单", "创建订单失败");
+            return json_fail('创建订单失败');
+        }
+    }
+
+    /**
+     * @Desc 支付
+     * @Author Gorden
+     * @Date 2024/9/5 13:12
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function pay(Request $request)
+    {
+        $params = $request->post();
+        // 验证线下付款密码
+        if ($params['pay_constitute'] == 'N' && in_array($params['pay_category'], ['OFFLINE', 'MONEY'])) {
+            $password = $params['offline_password'];
+            if ($password != '666888') {
+                return json_fail("密码错误,请重新输入");
+            }
+        }
+
+        $order = Order::where('order_id', $params['order_id'])->first();
+        if (!$order) {
+            return json_fail('订单异常');
+        }
+        if ($order->order_status_system != 'PAYING') {
+            return json_fail('订单不是可支付状态');
+        }
+
+        $params['orderId'] = $params['order_id'];
+
+        $params['orderGroupId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+        $order->order_groupby = $params['orderGroupId'];
+
+        $goods = Goods::where('goods_id', $params['join_sheet_goods_id'])
+            ->select('goods_id', 'goods_name', 'goods_classify')
+            ->first();
+        if (!$goods) {
+            return json_fail('产品数据异常');
+        }
+        $goods = $goods->toArray();
+        $params['goods_classify'] = $goods['goods_classify'] ?? '';
+        // 入伙场所
+        if (!empty($params['dept_id'])) {
+            $orderExtendJson = [];
+            if (!empty($order->order_extend_json)) {
+                $orderExtendJson = json_decode($order->order_extend_json, true);
+            }
+            $dept = SysDept::where('dept_id', $params['dept_id'])->first();
+            if (!$dept) {
+                throw new BusinessException("入伙场所不存在");
+            }
+            $params['dept_name'] = $dept->dept_name;
+            $orderExtendJson['dept_id'] = $params['dept_id'];
+            $orderExtendJson['dept_name'] = $params['dept_name'];
+            $order->order_extend_json = json_encode($orderExtendJson);
+        }
+        // 立即结算
+        if ($params['goods_classify'] == 'REFERRER') {
+            $order->order_is_complete = 'Y';
+            $systemStatus = 'DONE';
+        }
+        $paymentStatus = 'PENDING';
+        Db::beginTransaction();
+        try {
+            // 组合支付时,付款码应收金额
+            $qrcodePayAmount = 0;
+            if ($params['pay_category'] == 'OFFLINE' || $params['pay_category'] == 'MONEY') {
+                $order->order_status_system = $systemStatus;
+                $order->order_status_payment = 'SUCCESS';
+                $paymentStatus = 'SUCCESS';
+                if ($params['pay_category'] == 'OFFLINE' && !empty($params['pay_category_sub'])) {
+                    $params['pay_category'] = $params['pay_category_sub'];
+                }
+            }
+            if (($params['pay_constitute'] == 'Y' || $params['pay_category'] == 'QRCODE') && !empty($params['qrcode_nbr'])) {     // 付款码
+                // 提交过来的支付分类
+                $submitPayCategory = $params['pay_category'];
+                // 账户支付的金额
+                $accountAmount = $params['order_amount_pay'];
+                if ($params['pay_constitute'] == 'Y' && $qrcodePayAmount > 0) {
+                    // 组合支付,改成需要付款码需要支付的金额
+                    $params['order_amount_pay'] = $qrcodePayAmount;
+                }
+                // 去支付
+                $result = OrderService::qrcodePay($params);
+                $result = json_encode($result);
+                $params['pay_json_response'] = $result;
+                $result = json_decode($result, true);
+
+                $prefix = substr($params['qrcode_nbr'], 0, 2);
+                if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
+                    $params['pay_category'] = 'WXPAY';
+                    if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
+                        $order->order_status_system = 'PAYING';
+                        $order->order_status_payment = 'PENDING';
+                        $order->order_is_complete = 'N';
+//                        Db::rollBack();
+//                        return json_fail('支付失败');
+                    } else {
+                        $order->order_status_system = $systemStatus;
+                        $order->order_status_payment = 'SUCCESS';
+                        $paymentStatus = 'SUCCESS';
+                    }
+                } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
+                    $params['pay_category'] = 'ALIPAY';
+                    if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
+                        $order->order_status_system = 'PAYING';
+                        $order->order_status_payment = 'PENDING';
+                        $order->order_is_complete = 'N';
+//                        Db::rollBack();
+//                        return json_fail('支付失败');
+                    } else {
+                        $order->order_status_system = $systemStatus;
+                        $order->order_status_payment = 'SUCCESS';
+                        $paymentStatus = 'SUCCESS';
+                    }
+                } else {
+                    throw new BusinessException('付款码无效');
+                }
+
+                // 账户支付的金额
+                $params['order_amount_pay'] = $accountAmount;
+            }
+            if ($order->order_status_payment == 'SUCCESS') {
+                $order->order_amount_paid = $order->order_amount_paid + $params['order_amount_pay'];
+                $order->order_amount_pay = $order->order_amount_paid;
+            }
+
+            // 分期,支付完就结束了
+            if ($order->order_status_payment == 'SUCCESS' && $params['goods_classify'] == 'REFERRER' && floatval($order->order_amount_paid) >= $order->order_amount_total) {
+                $order->order_is_complete = 'Y';
+            }
+            if (floatval($order->order_amount_paid) < $order->order_amount_total) {
+                $order->order_status_system = 'PAYING';
+                $order->order_status_payment = 'PENDING';
+                $order->order_is_complete = 'N';
+            }
+            // 主订单
+            $order->save();
+
+            // sheet
+            if ($order->order_status_payment == 'SUCCESS') {
+                $data = [
+                    'order_sheet_status' => $systemStatus
+                ];
+                if (floatval($order->order_amount_paid) < $order->order_amount_total) {
+                    $data['order_sheet_status'] = 'BEING';
+                }
+                OrderSheet::where('join_sheet_order_id', $params['order_id'])->update($data);
+            }
+            // payDetail
+            $payData = [
+                'pay_amount' => $params['order_amount_pay']
+            ];
+            if ($paymentStatus == 'SUCCESS') {
+                $payData['pay_paytimes'] = date('Y-m-d H:i:s');
+                $payData['pay_status'] = 'SUCCESS';
+            }
+            if ($params['pay_constitute'] == 'N' && in_array($params['pay_category'], ['WXPAY', 'ALIPAY'])) {
+                $payData['pay_prepayid'] = $params['pay_category'];
+                $payData['pay_json_response'] = $params['pay_json_response'];
+            } else if ($params['pay_category'] == 'CASH') {
+                $payData['pay_prepayid'] = $params['join_order_member_id'] . '-CASH';
+            } else if ($params['pay_category'] == 'WELFARE') {
+                $payData['pay_prepayid'] = $params['join_order_member_id'] . '-WELFARE';
+            } else if ($params['pay_category'] == 'CARD') {
+                $payData['pay_prepayid'] = $params['card_nbr'];
+            } else if ($params['pay_category'] == 'OFFLINE') {
+                $payData['pay_prepayid'] = 'OFFLINE';
+            } else if ($params['pay_category'] == 'OFFLINE_ALIPAY') {
+                $payData['pay_prepayid'] = 'OFFLINE_ALIPAY';
+            } else if ($params['pay_category'] == 'OFFLINE_WXPAY') {
+                $payData['pay_prepayid'] = 'OFFLINE_WXPAY';
+            } else if ($params['pay_category'] == 'MONEY') {
+                $payData['pay_prepayid'] = 'MONEY';
+            }
+            // 清除此订单的未支付记录
+            PayDetail::whereJsonContains('join_pay_object_json->order_id', $order->order_id)->where('pay_status', 'WAITING')->delete();
+
+            $payData['join_pay_member_id'] = $params['join_order_member_id'];
+            $payData['join_pay_order_id'] = $order->order_groupby;
+            $payData['pay_status'] = !empty($payData['pay_status']) && $payData['pay_status'] == 'SUCCESS' ? $payData['pay_status'] : 'WAITING';
+            $payData['pay_category'] = $params['goods_classify'] ?? '';
+            $payData['pay_paytimes'] = date('Y-m-d H:i:s');
+            $payData['pay_json_request'] = json_encode($params);   // {"pay-result": "支付成功", "result-datetime": "2024-07-29 18:38:21"}
+            $payData['pay_json_response'] = !empty($payData['pay_status']) && $payData['pay_status'] == 'SUCCESS' ? json_encode([
+                'pay-result' => '支付成功', 'result-datetime' => date('Y-m-d H:i:s')
+            ]) : '[]';
+            $payData['join_pay_object_json'] = !empty($params['orderId']) ? json_encode(['order_id' => $params['orderId']]) : '[]';
+            $payData['pay_addtimes'] = time();
+
+            PayDetail::insert($payData);
+
+            Db::commit();
+            $full = '';
+            // 分期付款完成
+            if ($params['goods_classify'] == 'REFERRER' && $order->order_status_payment == 'SUCCESS' && floatval($order->order_amount_paid) >= $order->order_amount_total) {
+                $full = '-满额';
+                $params['member_id'] = $params['join_order_member_id'];
+                Event::dispatch('order.complete', $params);
+                Event::dispatch('order.referrer.grant', $params);
+            }
+            if ($paymentStatus == 'SUCCESS') {
+                // 入收支明细表
+                $params['inout_category'] = '购买康养推荐官收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+            }
+
+            if ($order->order_status_payment != 'SUCCESS' && $paymentStatus != 'SUCCESS') {
+                _syslog("订单", "支付异常,检查是否有轮询");
+                return json_throw(2001, '支付异常', ['order_id' => $params['orderId'], 'group_id' => $params['orderGroupId']]);
+            }
+            _syslog("订单", "订单支付成功");
+            return json_success('支付成功');
+        } catch (BusinessException $e) {
+            dump($e->getMessage());
+            Db::rollBack();
+            _syslog("订单", "订单支付失败:" . $e->getMessage());
+            return json_fail("支付失败:" . $e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            Db::rollBack();
+            _syslog("订单", "订单支付失败");
+            return json_fail('支付失败');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/6/7 10:30
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public function insertMain($params)
+    {
+        try {
+            $orderCategory = 'NORMAL';
+            if (!empty($params['order_category'])) {
+                $orderCategory = $params['order_category'];
+            } else if (isset($params['submit_goods_classify']) && $params['submit_goods_classify'] == 'MEALS') {
+                $orderCategory = 'DISHES';
+            } else if (isset($params['goods_classify'])) {
+                $orderCategory = $params['goods_classify'];
+            }
+            if (empty($params['order_extend_json'])) {
+                $params['order_extend_json'] = [];
+            } else {
+                if (is_json($params['order_extend_json'])) {
+                    $params['order_extend_json'] = json_decode($params['order_extend_json'], true);
+                }
+            }
+            // 推荐人
+            $params['order_extend_json']['referee'] = $params['referee'] ?? '';
+
+            $data = [
+                'order_id' => $params['orderId'],
+                'order_groupby' => $params['orderGroupId'],
+                'join_order_member_id' => $params['join_order_member_id'],
+                'order_name' => date('Y-m-d H:i:s') . '-订单',
+                'order_amount_total' => $params['order_amount_total'],
+                'order_amount_pay' => $params['order_status_payment'] == 'SUCCESS' ? $params['order_amount_pay'] : 0,
+                'order_amount_paid' => $params['order_status_payment'] == 'SUCCESS' ? $params['order_amount_pay'] : 0,
+                'order_category' => $orderCategory,
+                'order_classify' => $orderCategory,
+                'order_is_complete' => floatval($params['order_amount_pay']) >= floatval($params['order_amount_total']) && $params['order_status_payment'] == 'SUCCESS' ? 'Y' : 'N',
+                'order_status_system' => floatval($params['order_amount_pay']) >= floatval($params['order_amount_total']) && $params['order_status_payment'] == 'SUCCESS' ? 'DONE' : 'PAYING',
+                'order_status_payment' => floatval($params['order_amount_pay']) >= floatval($params['order_amount_total']) && $params['order_status_payment'] == 'SUCCESS' ? 'SUCCESS' : 'PENDING',
+                'order_status_storage' => $params['order_status_storage'],
+                'order_platform' => $params['order_platform'],
+                'order_remark' => $params['order_remark'] ?? '',
+                'order_discount_json' => $params['order_discount_json'] ?? '[]',
+                'order_config_json' => $params['order_config_json'] ?? '[]',
+                'order_express_json' => $params['order_express_json'] ?? '[]',
+                'order_extend_json' => $params['order_extend_json'] ? json_encode($params['order_extend_json']) : '[]',
+                'order_addtimes' => time()
+            ];
+
+            Order::insert($data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('订单创建信息失败');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/6/7 10:25
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public function insertSheet($params)
+    {
+        try {
+            $orderSheetIds = [];
+            foreach ($params['goodsContentList'] as $goods) {
+                //{"unit": "份", "table": null, "premises": "15"}
+                $price = floatval($goods['goods_sales_price']);
+                $extendJson['unit'] = $goods['sku_name'];
+                if (isset($params['submit_premises_id'])) {
+                    $extendJson['premises'] = $params['submit_premises_id'];
+                }
+                if (isset($params['submit_goods_classify']) && $params['submit_goods_classify'] == 'MEALS') {
+                    $extendJson['table'] = null;
+                }
+                $orderSheetStatus = 'PAYING';
+                if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
+                    if (floatval($params['order_amount_pay']) >= floatval($params['order_amount_total'])) {
+                        $orderSheetStatus = 'DONE';
+                    } else {
+                        $orderSheetStatus = 'BEING';
+                    }
+                }
+                $data = [
+                    'join_sheet_member_id' => $params['join_order_member_id'],
+                    'join_sheet_order_id' => $params['orderId'],
+                    'join_sheet_goods_id' => $goods['goods_id'],
+                    'join_sheet_goods_sku_id' => $goods['sku_id'],
+                    'order_sheet_status' => $orderSheetStatus,
+                    'order_sheet_category' => 'REFERRER',
+                    'order_sheet_num' => $goods['nbr'],
+                    'order_sheet_price' => $goods['goods_sales_price'],
+                    'order_sheet_amount' => $price * $goods['nbr'],
+                    'order_sheet_pay' => $price * $goods['nbr'],
+                    'order_sheet_task_status' => 'NONE',
+                    'order_sheet_remark' => $params['order_remark'] ?? '',
+                    'order_sheet_addtimes' => time(),
+                    'order_sheet_extend_json' => json_encode($extendJson)
+                ];
+
+                $orderSheetId = OrderSheet::insertGetId($data);
+                $orderSheetIds[] = $orderSheetId;
+
+                // 减库存,规格和总库存
+                if (!isset($params['submit_goods_classify']) || !in_array($params['submit_goods_classify'], ['MEALS', 'PACKAGE'])) {
+                    $goodsSku = GoodsSku::where('goods_sku_id', $goods['sku_id'])->first();
+                    $skuStorageJson = json_decode($goodsSku->goods_sku_storage_json, true);
+                    if (isset($skuStorageJson['storage']) && !empty($skuStorageJson['storage'])) {
+                        $skuStorageJson['storage'] = $skuStorageJson['storage'] - $goods['nbr'];
+                    }
+
+                    if (!isset($skuStorageJson['storage']) || (!empty($skuStorageJson['storage']) && $skuStorageJson['storage'] < 0)) {
+                        throw new BusinessException('库存不足');
+                    }
+                    $goodsSku->goods_sku_storage_json = json_encode($skuStorageJson);
+                    $goodsSku->save();
+                }
+
+
+                $goodsRunning = GoodsRunning::where('join_running_goods_id', $goods['goods_id'])->first();
+                $goodsRunning->goods_running_storage = $goodsRunning->goods_running_storage - $goods['nbr'];
+                if ($goodsRunning->goods_running_storage < 0) {
+                    throw new BusinessException('库存不足');
+                }
+                $goodsRunning->goods_running_sale = $goodsRunning->goods_running_sale + $goods['nbr'];
+                $goodsRunning->save();
+            }
+            return $orderSheetIds;
+        } catch (\support\exception\BusinessException $e) {
+            dump($e->getMessage() . '||' . $e->getLine());
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage() . '||' . $e->getLine());
+            throw new BusinessException('订单创建失败');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/6/7 10:35
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public function insertPayDetail($params)
+    {
+        try {
+            if (in_array($params['pay_category'], ['WXPAY', 'ALIPAY'])) {
+                $payPrepayid = $params['pay_category'];
+            } else if ($params['pay_category'] == 'OFFLINE') {
+                $payPrepayid = 'OFFLINE';
+            } else if ($params['pay_category'] == 'OFFLINE_ALIPAY') {
+                $payPrepayid = 'OFFLINE_ALIPAY';
+            } else if ($params['pay_category'] == 'OFFLINE_WXPAY') {
+                $payPrepayid = 'OFFLINE_WXPAY';
+            } else if ($params['pay_category'] == 'MONEY') {
+                $payPrepayid = 'MONEY';
+            } else {
+                $payPrepayid = $params['join_order_member_id'] . '-' . $params['pay_category'];
+            }
+            $data = [
+                'join_pay_member_id' => $params['join_order_member_id'],
+                'join_pay_order_id' => $params['orderGroupId'],
+                'pay_status' => $params['settlement_now'] == 'Y' && $params['order_status_payment'] == 'SUCCESS' ? 'SUCCESS' : 'WAITING',
+                'pay_category' => $params['goods_classify'],
+                'pay_amount' => $params['order_amount_pay'],
+                'pay_prepayid' => $payPrepayid,
+                'pay_paytimes' => date('Y-m-d H:i:s'),
+                'join_pay_object_json' => !empty($params['orderId']) ? json_encode(['order_id' => $params['orderId']]) : '[]',
+                'pay_json_request' => json_encode($params),
+                'pay_json_response' => $params['pay_json_response'] ?? '[]',
+                'pay_remark' => $params['order_remark'] ?? '',
+                'pay_addtimes' => time(),
+            ];
+
+            PayDetail::insert($data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('创建支付记录失败');
+        }
+    }
+
+
+    /**
+     * @Desc 订单商品详情
+     * @Author Gorden
+     * @Date 2024/3/29 8:50
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function sheet(Request $request)
+    {
+        try {
+            $orderId = $request->get('order_id');
+            $orderSheet = OrderSheet::with([
+                'member' => function ($query) {
+                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'join_member_role_id', 'member_is_vip', 'member_is_partner', 'member_is_referrer');
+                },
+                'goods' => function ($query) {
+                    $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_market_price', 'goods_sales_price', 'goods_classify', 'goods_if_express');
+                },
+                'memberInfo',
+                'cert',
+                'sku' => function ($query) {
+                    $query->where('goods_sku_status', 'ON')
+                        ->select('goods_sku_id', 'join_sku_goods_id', 'goods_sku_specs_json', 'goods_sku_sales_price');
+                },
+                'skus',
+                'refund' => function ($query) {
+                    $query->select('join_return_order_id', 'order_return_status');
+                }
+            ])->where('join_sheet_order_id', $orderId)
+                ->orderBy('order_sheet_id', 'DESC')
+                ->get()
+                ->toArray();
+
+            $order = Order::where('order_id', $orderId)->first();
+            $express = OrderExpress::where('join_express_order_id', $orderId)->first();
+            $premises = '';
+            if ($express && $express->order_express_type == '自提') {
+                $premises = $express->order_express_company;
+            }
+            $sheetAmount = 0;
+            foreach ($orderSheet as &$item) {
+                $sheetAmount += $item['order_sheet_amount'];
+                $item['goods']['goods_cover'] = getenv('STORAGE_DOMAIN') . $item['goods']['goods_cover'];
+                if (!empty($item['goods']) && $item['goods']['goods_classify'] == 'PACKAGE') {
+                    $components = GoodsComponent::with('goods')
+                        ->where('join_component_master_goods_id', $item['goods']['goods_id'])
+                        ->select('join_component_master_goods_id', 'join_component_goods_id', 'goods_component_price',
+                            'goods_component_price', 'goods_component_json')
+                        ->get()
+                        ->toArray();
+                    $goodsArr = [];
+                    foreach ($components as $component) {
+                        $configJson = !empty($component['goods_component_json']) ? json_decode($component['goods_component_json'], true) : [];
+                        if (!empty($component['goods'])) {
+                            $supplierName = Supplier::where('supplier_id', $component['goods']['join_goods_supplier_id'])->value('supplier_name');
+                            $benefit = MemberBenefit::where('join_benefit_member_id', $item['join_sheet_member_id'])
+                                ->where('join_benefit_goods_id', $component['goods']['goods_id'])
+                                ->where('join_benefit_order_id', $orderId)
+                                ->first();
+                            $goodsArr[] = [
+                                'goods_name' => $component['goods']['goods_name'],
+                                'goods_cover' => getenv('STORAGE_DOMAIN') . $component['goods']['goods_cover'],
+                                'supplier_name' => $supplierName,
+                                'nbr' => $configJson['nbr'] ?? 0,
+                                'used' => !empty($benefit->member_benefit_used_count) ? intval($benefit->member_benefit_used_count) : ''
+                            ];
+                        }
+                    }
+
+                    $item['goods']['components'] = $goodsArr;
+                }
+                if (!empty($item['sku'])) {
+                    if (!empty($item['sku']['goods_sku_specs_json'])) {
+                        $specsJson = json_decode($item['sku']['goods_sku_specs_json'], true);
+                        $skuName = '';
+                        foreach ($specsJson as $specsKey => $skuSpecs) {
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . ' ' . implode(' ', $skuSpecs) . ';';
+                            } else {
+                                $skuName = $skuName . ' ' . $skuSpecs . ';';
+                            }
+                        }
+                        $item['sku']['goods_sku_title'] = rtrim($skuName, ';');
+                    }
+                }
+                if (!empty($item['skus'])) {
+                    foreach ($item['skus'] as $key => $skus) {
+                        if (!empty($skus['goods_sku_specs_json'])) {
+                            $item['skus'][$key]['goods_sku_specs_json'] = json_decode($skus['goods_sku_specs_json']);
+
+                            $skuName = '';
+                            foreach ($item['skus'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                                $keyStr = ($specsKey == '规格') ? '' : ($specsKey . ':');
+                                if (is_array($skuSpecs)) {
+                                    $skuName = $skuName . $keyStr . ' ' . implode(' ', $skuSpecs) . '; ';
+                                } else {
+                                    $skuName = $skuName . $keyStr . ' ' . $skuSpecs . '; ';
+                                }
+                            }
+                            $item['skus'][$key]['sku_name'] = rtrim($skuName, '; ');
+                        }
+                    }
+                } else {
+                    $item['skus'] = [];
+                }
+                if (in_array($item['goods']['goods_classify'], ['SERVICE', 'CHNMED', 'CHNNCD', 'PACKAGE']) && $order->order_status_system == 'DONE') {
+                    $item['appontment'] = [];
+                    $appontments = Appointment::where('join_appointment_order_id', $orderId)
+                        ->where('appointment_status', 'DONE')
+                        ->select('appointment_status', 'appointment_id', 'appointment_done_datetime', 'appointment_done_json', 'appointment_apply_json')
+                        ->get()
+                        ->toArray();
+                    foreach ($appontments as $appontment) {
+                        $doneJson = [];
+                        $username = '';
+                        if (!empty($appontment['appointment_done_json'])) {
+                            $doneJson = json_decode($appontment['appointment_done_json'], true);
+                            if (isset($doneJson['charge'])) {
+                                $username = SysUser::where('user_id', $doneJson['charge']['charge_user_id'])->value('user_name');
+                            }
+                        }
+                        $person = 1;
+                        if (!empty($appontment->appointment_apply_json)) {
+                            $applyJson = json_decode($appontment->appointment_apply_json, true);
+                            if (isset($applyJson['person'])) {
+                                $person = $applyJson['person'];
+                            }
+                        }
+                        $item['appontment'][] = [
+                            'member' => ($item['cert'] ? $item['cert']['member_cert_name'] . '-' : '') . ($item['member'] ? $item['member']['member_mobile'] : ''),
+                            'goods_name' => $item['goods']['goods_name'],
+                            'premisses' => isset($doneJson['charge']) ? $doneJson['charge']['charge_premises'] : '',
+                            'username' => $username,
+                            'nbr' => $person,
+                            'done_time' => $appontment['appointment_done_datetime'],
+                            'appointment_status' => $appontment['appointment_status'],
+                        ];
+                    }
+                }
+                if (!empty($item['order_sheet_extend_json']) && !$express) {
+                    $extendJson = json_decode($item['order_sheet_extend_json'], true);
+                    if (isset($extendJson['address_id'])) {
+                        $address = ClientConfig::where('client_config_id', $extendJson['address_id'])->first();
+                        if (!empty($address)) {
+                            $valJson = json_decode($address->client_config_val_json, true);
+                            $express = [
+                                'order_express_address' => $valJson['address'] . ($valJson['numbers'] ?? ''),
+                                'order_express_city' => $valJson['city'],
+                                'order_express_mobile' => $valJson['mobile'],
+                                'order_express_person' => $valJson['person']
+                            ];
+                        }
+                    }
+                }
+                $item['member']['level'] = '普通会员';
+                if (!empty($item['member']['join_member_role_id'])) {
+                    $item['member']['level'] = MemberRole::where('member_role_id', $item['member']['join_member_role_id'])->value('member_role_name');
+                }
+
+                if (!empty($item['member_info'])) {
+                    if (empty($item['member_info']['member_info_headimg']) || substr($item['member_info']['member_info_headimg'], 0, 1) == '.') {
+                        $item['member_info']['member_info_headimg'] = "https://img.wanyuewellness.com.cn/images/avatar_default.png";
+                    }
+                }
+            }
+            $order->sheet_amount = number_format($sheetAmount, 2);
+
+            $payDetails = PayDetail::whereJsonContains('join_pay_object_json->order_id', $order->order_id)
+                ->where('pay_category', 'REFERRER')
+                ->where('pay_status', 'SUCCESS')
+                ->select('pay_id', 'pay_category', 'pay_prepayid', 'pay_paytimes', 'pay_status', 'pay_amount', 'pay_extend_json')
+                ->orderBy('pay_addtimes', 'DESC')
+                ->get();
+
+            $order->pay_amount_total = 0;
+            foreach ($payDetails as &$payDetail) {
+                $categoryArray = explode('-', $payDetail->pay_prepayid);
+                if (isset($categoryArray[1])) {
+                    $payDetail->pay_category = $categoryArray[1];
+                } else if (in_array($categoryArray[0], ['WXPAY', 'ALIPAY', 'OFFLINE', 'OFFLINE_ALIPAY', 'OFFLINE_WXPAY', 'MONEY'])) {
+                    $payDetail->pay_category = $categoryArray[0];
+                }
+                if (!empty($payDetail->pay_extend_json)) {
+                    $payExtendJson = json_decode($payDetail->pay_extend_json, true);
+                    $order->cancel_times = $payExtendJson['cancel_times'] ?? '';
+                    if (isset($payExtendJson['ticket'])) {
+                        $payDetail->ticket = str_replace('/thumb', '', getenv("STORAGE_DOMAIN") . $payExtendJson['ticket']);
+                    }
+                }
+                $order->pay_amount_total += $payDetail->pay_amount;
+            }
+            if (!empty($order->order_config_json)) {
+                $orderConfigJson = json_decode($order->order_config_json, true);
+                if (isset($orderConfigJson['reach'])) {
+                    $order->reach = $orderConfigJson['reach'];
+                }
+                if (isset($orderConfigJson['table'])) {
+                    $order->table = $orderConfigJson['table'];
+                }
+                if (isset($orderConfigJson['eat'])) {
+                    $order->eat = $orderConfigJson['eat'];
+                }
+                if (isset($orderConfigJson['express'])) {
+                    $order->express = $orderConfigJson['express'];
+                }
+                if (isset($orderConfigJson['premises'])) {
+                    $order->premises = $orderConfigJson['premises'];
+                }
+                if (isset($orderConfigJson['tableid'])) {
+                    $order->dept_table_id = $orderConfigJson['tableid'];
+                }
+            }
+            if (!empty($order->order_extend_json)) {
+                $orderExtendJson = json_decode($order->order_extend_json, true);
+                $order->referee = $orderExtendJson['referee'] ?? '';
+                if (isset($orderExtendJson['cancel_times'])) {
+                    $order->cancel_times = $orderExtendJson['cancel_times'];
+                }
+                if (isset($orderExtendJson['free_remark'])) {
+                    $order->free_remark = $orderExtendJson['free_remark'];
+                }
+                if (isset($orderExtendJson['dept_id'])) {
+                    $order->dept_id = $orderExtendJson['dept_id'];
+                }
+            }
+            $discount = ['coupon_name' => '', 'classify' => '', 'value' => 0];
+            if (!empty($order->order_discount_json)) {
+                $orderDiscountJson = json_decode($order->order_discount_json, true);
+                foreach ($orderDiscountJson as $discountItem) {
+                    if (!empty($discountItem['coupon_id'])) {
+                        $coupon = Coupon::where('coupon_id', $discountItem['coupon_id'])
+                            ->select('coupon_name', 'coupon_classify', 'coupon_category', 'coupon_value', 'coupon_minimum_limit')
+                            ->first();
+                        if (!$coupon) {
+                            continue;
+                        }
+                        $classify = CouponService::couponClassifyInfo($coupon->coupon_classify, $coupon->coupon_category, $coupon->coupon_value, $coupon->coupon_minimum_limit);
+                        $discount['coupon_name'] .= $coupon->coupon_classify . ':' . $coupon->coupon_name . '(优惠¥' . sprintf("%.2f", $discountItem['coupon_value']) . '), ';
+                    }
+                    if ($discountItem['coupon_classify'] == '退款') {
+                        continue;
+                    }
+                    if (empty($discountItem['coupon_id']) && !empty($discountItem['coupon_classify'])) {
+                        if (!empty($discountItem['coupon_detail_id'])) {
+
+                            $discount['classify'] .= $discountItem['coupon_detail_id'][0] . '(优惠¥' . sprintf("%.2f", $discountItem['coupon_value']) . '), ';
+                        } else {
+                            $discount['classify'] .= $discountItem['coupon_classify'] . '(优惠¥' . sprintf("%.2f", $discountItem['coupon_value']) . '), ';
+                        }
+                    }
+                    if (!empty($discountItem['coupon_value'])) {
+                        $discount['value'] += $discountItem['coupon_value'];
+                    }
+                }
+                if (!empty($discount['coupon_name'])) {
+                    $discount['coupon_name'] = rtrim($discount['coupon_name'], ', ');
+                }
+                if (!empty($discount['classify'])) {
+                    $discount['classify'] = rtrim($discount['classify'], ', ');
+                }
+            }
+            $discount['value'] = sprintf("%.2f", $discount['value']);
+            $order->discount = $discount;
+
+            $order->premises = $order->premises ?? $premises;
+            $data = [
+                'order' => $order,
+                'sheet' => json_decode(json_encode($orderSheet)),
+                'express' => json_decode(json_encode($express)),
+                'payDetails' => json_decode(json_encode($payDetails))
+            ];
+
+            return json_success('', $data);
+
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+        }
+    }
+
+    /**
+     * @Desc 获取分期的订单
+     * @Author Gorden
+     * @Date 2024/9/10 15:14
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function getPaidOrder(Request $request)
+    {
+        $memberId = $request->get('member_id');
+        if (!$memberId) {
+            return json_fail('参数异常');
+        }
+
+        $order = Order::where('join_order_member_id', $memberId)
+            ->where('order_category', 'REFERRER')
+            ->where('order_status_system', 'PAYING')
+            ->select('order_id', 'order_amount_total', 'order_amount_paid', 'order_amount_pay', 'order_extend_json')
+            ->first();
+
+        return json_success('', $order);
+    }
+
+    /**
+     * @Desc 上传支付凭证
+     * @Author Gorden
+     * @Date 2024/9/12 10:14
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public function uploadTicket(Request $request)
+    {
+        $payId = $request->post('pay_id');
+        $url = $request->post('url');
+        if (!$payId || !$url) {
+            return json_fail("参数异常");
+        }
+        $url = str_replace(getenv("STORAGE_DOMAIN"), '', $url);
+        try {
+            $payDetail = PayDetail::where('pay_id', $payId)->first();
+            if (!$payDetail) {
+                return json_fail('数据异常');
+            }
+            $extendJson = [];
+            if (!empty($payDetail->pay_extend_json)) {
+                $extendJson = json_decode($payDetail->pay_extend_json, true);
+            }
+            $extendJson['ticket'] = $url;
+
+            $payDetail->pay_extend_json = json_encode($extendJson);
+            dump($payDetail->save());
+
+            return json_success('success');
+        } catch (\Exception $e) {
+            return json_fail("上传附件失败");
+        }
+    }
+
+
+    /**
+     * @Desc 查询订单状态
+     * @Author Gorden
+     * @Date 2024/8/19 11:55
+     *
+     * @param Request $request
+     * @return Response|void
+     */
+    public function getOrderPayStatus(Request $request)
+    {
+        $groupId = $request->get('group_id');
+        if (!$groupId) {
+            return json_fail('参数异常');
+        }
+
+        $order = Order::where('order_groupby', $groupId)->first();
+        if (empty($order)) {
+            return json_fail('订单不存在');
+        }
+        $order = $order->toArray();
+        $sheet = OrderSheet::where('join_sheet_order_id', $order['order_id'])
+            ->select('order_sheet_id', 'join_sheet_goods_id', 'join_sheet_goods_sku_id')
+            ->first();
+        $payDetailType = PayDetail::where('join_pay_order_id', $groupId)->pluck('pay_prepayid')->toArray();
+        try {
+            Db::beginTransaction();
+            $payStatus = 'N';
+            $payAmount = 0;
+            if (in_array('WXPAY', $payDetailType)) {
+                $result = Pay::wechat(config('payment.wxpay'))->find($groupId, 'pos');
+                $result = json_decode(json_encode($result), true);
+//                $result = '{"return_code":"SUCCESS","return_msg":"OK","result_code":"SUCCESS","mch_id":"1680393367","appid":"wxc6274da7198e3eb4","openid":"o3JAn6Ii_bAlxS-jbNEC4WnPhdwM","is_subscribe":"N","trade_type":"MICROPAY","trade_state":"SUCCESS","bank_type":"OTHERS","total_fee":"3","fee_type":"CNY","cash_fee":"1000","cash_fee_type":"CNY","transaction_id":"4200067718202409250802875650","out_trade_no":"OD24092518408RV7","attach":[],"time_end":"20240925184009","trade_state_desc":"支付成功","nonce_str":"OeGOkjch4eaV5qIt","sign":"6DCB3BFC594EBC018A2BEE2C3DFEA4E3"}';
+//                $result = json_decode($result, true);
+                if (!empty($result['return_code']) && $result['return_code'] == 'SUCCESS' && !empty($result['result_code']) && $result['result_code'] == 'SUCCESS' && !empty($result['trade_state']) && $result['trade_state'] == 'SUCCESS') {
+                    $payStatus = 'Y';
+                    $payAmount = ($result['total_fee'] / 100);
+                    $orderUpdateData['order_status_payment'] = 'PENDING';
+                    $orderUpdateData['order_amount_paid'] = $order['order_amount_paid'] + $payAmount;
+                    $orderUpdateData['order_amount_pay'] = $order['order_amount_pay'] + $payAmount;
+                    if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                        $orderUpdateData['order_status_payment'] = 'SUCCESS';
+                        $orderUpdateData['order_status_system'] = 'DONE';
+                        $orderUpdateData['order_is_complete'] = 'Y';
+                    }
+                    // 主订单
+                    Order::where('order_groupby', $groupId)->update($orderUpdateData);
+                    // Sheet
+                    OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
+
+                    // 支付记录
+                    PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'WXPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
+                }
+            } else if (in_array('ALIPAY', $payDetailType)) {
+                $result = Pay::alipay(config('payment.alipay'))->find($groupId);
+                $result = json_decode(json_encode($result), true);
+//                $result = '{"code":"10000","msg":"Success","buyer_logon_id":"138******93","buyer_pay_amount":"5.87","fund_bill_list":[{"amount":"5.69","fund_channel":"ALIPAYACCOUNT"},{"amount":"0.18","fund_channel":"COUPON"},{"amount":"0.13","fund_channel":"DISCOUNT"}],"invoice_amount":"5.69","out_trade_no":"OD2409251550Q07A","point_amount":"0.00","receipt_amount":"0.01","send_pay_date":"2024-09-25 15:50:22","total_amount":"0.01","trade_no":"2024092523001439431419750998","trade_status":"TRADE_SUCCESS","buyer_open_id":"04309N2aVhSZz4cKwN_DN2DQa7ekM3z5n8kscQHsmIZOJsf"}';
+//                $result = json_decode($result, true);
+                if (!empty($result['code']) && $result['code'] == '10000' && !empty($result['trade_status']) && $result['trade_status'] == 'TRADE_SUCCESS') {
+                    $payStatus = 'Y';
+                    $payAmount = $result['total_amount'];
+                    $orderUpdateData['order_status_payment'] = 'PENDING';
+                    $orderUpdateData['order_amount_paid'] = $order['order_amount_paid'] + $payAmount;
+                    $orderUpdateData['order_amount_pay'] = $order['order_amount_pay'] + $payAmount;
+                    if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                        $orderUpdateData['order_status_payment'] = 'SUCCESS';
+                        $orderUpdateData['order_status_system'] = 'DONE';
+                        $orderUpdateData['order_is_complete'] = 'Y';
+                    }
+                    // 主订单
+                    Order::where('order_groupby', $groupId)->update($orderUpdateData);
+                    // Sheet
+                    OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
+                    // 支付记录
+                    PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'ALIPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
+                }
+            }
+            $full = '';
+            if ($payStatus == 'Y') {
+                if ($order['order_amount_paid'] + $payAmount >= $order['order_amount_total']) {
+                    $full = '-满额';
+                    $params['member_id'] = $order['join_order_member_id'];
+                    $params['orderId'] = $order['order_id'];
+                    $params['order_amount_pay'] = $payAmount;
+                    $params['order_amount_total'] = $order['order_amount_total'];
+                    $params['join_sheet_goods_id'] = $sheet->join_sheet_goods_id;
+                    if (!empty($order['order_extend_json'])) {
+                        $orderExtendJson = json_decode($order['order_extend_json'], true);
+                        $params['dept_id'] = $orderExtendJson['dept_id'] ?? '';
+                        $params['dept_name'] = $orderExtendJson['dept_name'] ?? '';
+                    }
+
+                    Event::dispatch('order.referrer.grant', $params);
+                }
+
+                Db::commit();
+
+                // 入收支明细表
+                $params['orderId'] = $order['order_id'];
+                $params['inout_category'] = '购买康养推荐官收入' . $full;
+                Event::dispatch('statistics.inout.in', $params);
+
+                return json_success('success');
+            }
+
+            Db::rollBack();
+            return json_fail('没有查询到记录');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            _syslog("支付轮询", '订单支付异常:' . $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            Db::rollBack();
+            return json_fail('查询失败');
+        }
+    }
+}

+ 49 - 13
app/admin/controller/order/RefundController.php

@@ -2,6 +2,7 @@
 
 namespace app\admin\controller\order;
 
+use app\admin\service\member\MemberService;
 use app\admin\validate\order\ReturnValidate;
 use app\controller\Curd;
 use app\model\CouponDetail;
@@ -20,6 +21,7 @@ use support\Log;
 use support\Request;
 use support\Response;
 use Tinywan\Jwt\JwtToken;
+use Webman\Event\Event;
 use Yansongda\Pay\Pay;
 
 class RefundController extends Curd
@@ -173,7 +175,7 @@ class RefundController extends Curd
         $orderId = $request->get('order_id');
         $orderSheet = OrderSheet::with([
             'member' => function ($query) {
-                $query->select('member_id', 'member_mobile');
+                $query->select('member_id', 'member_mobile','join_member_role_id','member_is_owner','member_is_vip','member_is_partner','member_is_referrer');
             },
             'goods' => function ($query) {
                 $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_market_price', 'goods_sales_price', 'goods_classify');
@@ -193,12 +195,15 @@ class RefundController extends Curd
         foreach ($orderSheet as &$item) {
             if (isset($item['member']) && empty($member)) {
                 $member = $item['member'];
+                $member['level'] = MemberService::getRoleName($member['join_member_role_id'] ?? '');
             }
             if (isset($item['cert']) && empty($cert)) {
                 $cert = $item['cert'];
             }
             if (isset($item['member_info']) && empty($memberInfo)) {
                 $memberInfo = $item['member_info'];
+                $memberInfo['member_info_nickname'] = $memberInfo['member_info_nickname'] ?? MemberService::getMemberNickname($member['member_mobile'] ?? '');
+                $memberInfo['member_info_headimg'] = MemberService::getAvatarUrl($memberInfo['member_info_headimg'] ?? '');
             }
             $item['goods']['goods_cover'] = getenv('STORAGE_DOMAIN') . $item['goods']['goods_cover'];
             if (!empty($item['goods']) && $item['goods']['goods_classify'] == 'PACKAGE') {
@@ -258,6 +263,25 @@ class RefundController extends Curd
             }
         }
         $express = OrderExpress::where('join_express_order_id', $orderId)->first();
+        $payDetails = PayDetail::where('join_pay_order_id',$order->order_groupby)
+            ->whereJsonContains('join_pay_object_json->order_id',$orderId)
+            ->where('pay_status','SUCCESS')
+            ->where('pay_category', '<>', 'REFUND')
+            ->get()
+            ->toArray();
+        if (count($payDetails) > 1){
+            $order->pay_category = 'CONSTITUTE';
+        }
+        $payDetail = !empty($payDetails) && count($payDetails) > 0 ? $payDetails[0] : [];
+        if (!empty($payDetail) && !empty($payDetail['pay_prepayid'])) {
+            $categoryArray = explode('-', $payDetail['pay_prepayid']);
+            if (isset($categoryArray[1])) {
+                $payDetail['pay_category'] = $categoryArray[1];
+            } else if (in_array($categoryArray[0], ['WXPAY', 'ALIPAY', 'OFFLINE', 'OFFLINE_ALIPAY', 'OFFLINE_WXPAY', 'MONEY'])) {
+                $payDetail['pay_category'] = $categoryArray[0];
+            }
+        }
+
         $data = [
             'member' => $member,
             'cert' => $cert,
@@ -265,7 +289,9 @@ class RefundController extends Curd
             'order' => $order,
             'refund' => $return,
             'sheet' => $orderSheet,
-            'express' => $express
+            'express' => $express,
+            'payDetail' => json_decode(json_encode($payDetail)),
+            'payDetails' => json_decode(json_encode($payDetails))
         ];
 
         return json_success('', $data);
@@ -306,16 +332,16 @@ class RefundController extends Curd
             if (sprintf("%.2f", $amount) == sprintf("%.2f", $order->order_amount_pay)) {
                 if (!empty($order->order_discount_json)) {
                     $discountJson = json_decode($order->order_discount_json, true);
-                    foreach ($discountJson as $item){
-                        if (empty($item['coupon_id'])){
+                    foreach ($discountJson as $item) {
+                        if (empty($item['coupon_id'])) {
                             continue;
                         }
                         // 是否有其他订单一起使用优惠券
-                        if(!Order::where('order_groupby',$order->order_groupby)->where('order_id','<>',$order->order_id)->where('order_is_complete','<>','R')->exists()){
-                            foreach ($item['coupon_detail_id'] as $detailId){
-                                CouponDetail::where('coupon_detail_id',$detailId)->update([
-                                    'coupon_detail_status'=>'ACTIVED',
-                                    'coupon_detail_used_datetime'=>''
+                        if (!Order::where('order_groupby', $order->order_groupby)->where('order_id', '<>', $order->order_id)->where('order_is_complete', '<>', 'R')->exists()) {
+                            foreach ($item['coupon_detail_id'] as $detailId) {
+                                CouponDetail::where('coupon_detail_id', $detailId)->update([
+                                    'coupon_detail_status' => 'ACTIVED',
+                                    'coupon_detail_used_datetime' => ''
                                 ]);
                             }
                         }
@@ -356,6 +382,7 @@ class RefundController extends Curd
                     $response = $this->refundToWx($payDetail0, $returnId, $amount);
                 } elseif ($payWay[0] == 'ALIPAY') {
                     $prepayid = 'ALIPAY';
+                    $payDetail0['order_id'] = $order->order_id;
                     $response = $this->refundToAlipay($payDetail0, $amount);
                 } elseif ($payWay[0] == 'MONEY') {
                     $prepayid = 'MONEY';
@@ -392,14 +419,19 @@ class RefundController extends Curd
 
             Db::commit();
 
+            // 退款入收支
+            $params['orderId'] = $orderId;
+            $params['type'] = 'refund';
+            Event::dispatch('statistics.inout.out',$params);
+
             return json_success('success');
         } catch (BusinessException $e) {
             Db::rollBack();
-            dump($e->getMessage());
+            Log::info("退款失败", ['msg' => $e->getMessage()]);
             return json_fail($e->getMessage());
         } catch (\Exception $e) {
             Db::rollBack();
-            dump($e->getMessage());
+            Log::info("退款失败", ['msg' => $e->getMessage()]);
             return json_fail("退款失败");
         }
     }
@@ -538,7 +570,10 @@ class RefundController extends Curd
         $data = [
             'out_trade_no' => $params['join_pay_order_id'],
             'refund_amount' => $amount,
+            'out_request_no' => $params['order_id']
         ];
+
+        Log::error("支付宝退款参数", $data);
         try {
             $res = Pay::alipay(config('payment.alipay'))->refund($data);
             $resArray = json_decode($res, true);
@@ -549,7 +584,7 @@ class RefundController extends Curd
 
             return $resArray;
         } catch (\Exception $e) {
-
+            Log::error("支付宝退款失败", ['msg' => $e->getMessage()]);
             throw new BusinessException("退款失败");
         }
 
@@ -573,7 +608,7 @@ class RefundController extends Curd
             $orderDiscountJson = json_decode($order->order_discount_json, true);
         }
         try {
-            $orderDiscountJson[date('YH:i:s H:i:s')] = [
+            $orderDiscountJson[date('Y-m-d H:i:s')] = [
                 'coupon_id' => null,
                 'coupon_value' => $amount,
                 'coupon_classify' => '退款',
@@ -582,6 +617,7 @@ class RefundController extends Curd
 
             $order->order_discount_json = json_encode($orderDiscountJson, JSON_UNESCAPED_UNICODE);
             $order->order_is_complete = 'R';
+            $order->order_category = 'RETURN';
 //            $order->order_amount_pay = $order->order_amount_pay - $amount;
             $order->save();
         } catch (\Exception $e) {

+ 33 - 7
app/admin/controller/order/ServicesController.php

@@ -146,7 +146,7 @@ class ServicesController extends Curd
         if ($field) {
             $model = $model->orderBy($field, $order);
         }
-        $model = $model->select('order.*', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_remark');
+        $model = $model->select('order.*', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_refund_json', 'order_return.order_return_remark');
         return $model;
     }
 
@@ -165,6 +165,7 @@ class ServicesController extends Curd
                 $item['sheet']['order_sheet_num'] = intval($item['sheet']['order_sheet_num']);
             }
             unset($item['sheets']);
+            $item['order_return_amount'] = 0;
             if (isset($item['orders_return_id'])) {
                 $item['return'] = [
                     'orders_return_id' => $item['orders_return_id'],
@@ -173,6 +174,12 @@ class ServicesController extends Curd
                     'order_return_apply_json' => $item['order_return_apply_json'],
                     'order_return_remark' => $item['order_return_remark']
                 ];
+                if (!empty($item['order_return_refund_json'])) {
+                    $refundJson = json_decode($item['order_return_refund_json'], true);
+                    if (isset($refundJson['amount'])) {
+                        $item['order_return_amount'] = $refundJson['amount'];
+                    }
+                }
             }
             $item['have_success_paydetail'] = 'N';
             if (PayDetail::where('join_pay_order_id', $item['order_groupby'])
@@ -532,10 +539,16 @@ class ServicesController extends Curd
 
             Db::commit();
 
-            // 会员升级
+            // 触发事件
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 Event::dispatch('order.complete', $params);
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
+            }
+            // 触发事件
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 上级提成
+                OrderService::splitOrderCommission($params);
+                // 入收支明细表
+                OrderService::splitOrderStatisticsInOut($params);
             }
 
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
@@ -859,10 +872,16 @@ class ServicesController extends Curd
 
             Db::commit();
 
-            // 会员升级
+            // 触发事件
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 Event::dispatch('order.complete', $params);
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
+            }
+            // 触发事件
+            if ($params['order_status_payment'] == 'SUCCESS') {
+                // 上级提成
+                OrderService::splitOrderCommission($params);
+                // 入收支明细表
+                OrderService::splitOrderStatisticsInOut($params);
             }
 
             if ($params['settlement_now'] == 'Y' && $params['order_status_payment'] != 'SUCCESS') {
@@ -917,6 +936,7 @@ class ServicesController extends Curd
         }
 
         $order = Order::where('order_id', $params['order_id'])->first();
+        $oldOrderStatus = $order->order_status_system;
         if (!$order) {
             return json_fail('订单异常');
         }
@@ -1202,7 +1222,7 @@ class ServicesController extends Curd
                 if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
                     $params['pay_category'] = 'WXPAY';
                     if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1214,7 +1234,7 @@ class ServicesController extends Curd
                 } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
                     $params['pay_category'] = 'ALIPAY';
                     if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1372,6 +1392,9 @@ class ServicesController extends Curd
             if ($order->order_status_payment == 'SUCCESS') {
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = '标准订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票
@@ -1802,6 +1825,9 @@ class ServicesController extends Curd
             if ($order->order_status_payment == 'SUCCESS') {
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = '标准订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票

+ 220 - 131
app/admin/controller/order/WholeController.php

@@ -119,6 +119,9 @@ class WholeController extends Curd
         if ($request->get('goods_classify') || $request->get('goods_name')) {
 //            $where['order_id'] = '';
             $goodsClassify = $request->get('goods_classify', '');
+            if ($goodsClassify == 'DISHES') {
+                $goodsClassify = 'MEALS';
+            }
             $goodsName = $request->get('goods_name', '');
             if (!empty($goodsName) && empty($goodsClassify)) {
                 $goodsIds = Goods::where('goods_name', 'like', '%' . $request->get('goods_name') . '%')->pluck('goods_id')->toArray();
@@ -220,7 +223,7 @@ class WholeController extends Curd
         if ($field) {
             $model = $model->orderBy($field, $order);
         }
-        $model = $model->select('order.*', 'order_express.join_express_order_id', 'order_express.order_express_type', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_remark');
+        $model = $model->select('order.*', 'order_express.join_express_order_id', 'order_express.order_express_type', 'order_return.orders_return_id', 'order_return.join_return_order_id', 'order_return.order_return_status', 'order_return.order_return_apply_json', 'order_return.order_return_refund_json', 'order_return.order_return_remark');
         return $model;
     }
 
@@ -239,14 +242,21 @@ class WholeController extends Curd
                 $item['sheet']['order_sheet_num'] = intval($item['sheet']['order_sheet_num']);
             }
             unset($item['sheets']);
+            $item['order_return_amount'] = 0;
             if (isset($item['orders_return_id'])) {
                 $item['return'] = [
                     'orders_return_id' => $item['orders_return_id'],
                     'join_return_order_id' => $item['join_return_order_id'],
                     'order_return_status' => $item['order_return_status'],
                     'order_return_apply_json' => $item['order_return_apply_json'],
-                    'order_return_remark' => $item['order_return_remark']
+                    'order_return_remark' => $item['order_return_remark'],
                 ];
+                if (!empty($item['order_return_refund_json'])) {
+                    $refundJson = json_decode($item['order_return_refund_json'], true);
+                    if (isset($refundJson['amount'])) {
+                        $item['order_return_amount'] = $refundJson['amount'];
+                    }
+                }
             }
             if (isset($item['join_express_order_id'])) {
                 $item['express'] = [
@@ -256,6 +266,7 @@ class WholeController extends Curd
                 unset($item['join_express_order_id'], $item['order_express_type']);
             }
             $payDetails = PayDetail::where('join_pay_order_id', $item['order_groupby'])
+                ->whereJsonContains('join_pay_object_json->order_id', $item['order_id'])
                 ->where('pay_category', '<>', 'REFUND')
                 ->where('pay_status', 'SUCCESS')
                 ->select('pay_id', 'pay_category', 'pay_prepayid', 'pay_paytimes', 'pay_status', 'pay_amount', 'pay_extend_json')
@@ -283,6 +294,8 @@ class WholeController extends Curd
                     $item['payWay'] = 'VIP账户';
                 } else if (!empty($payWay[1]) && $payWay[1] == 'QRCODE') {
                     $item['payWay'] = '付款码';
+                } else if (!empty($payWay[1]) && $payWay[1] == 'NONE') {
+                    $item['payWay'] = '付零';
                 } else if (!empty($payWay[0]) && $payWay[0] == 'OFFLINE') {
                     $item['payWay'] = '线下支付';
                 } else if (!empty($payWay[0]) && $payWay[0] == 'OFFLINE_ALIPAY') {
@@ -320,7 +333,7 @@ class WholeController extends Curd
             $orderId = $request->get('order_id');
             $orderSheet = OrderSheet::with([
                 'member' => function ($query) {
-                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'join_member_role_id');
+                    $query->select('member_id', 'member_mobile', 'member_is_owner', 'join_member_role_id', 'member_is_vip', 'member_is_partner', 'member_is_referrer');
                 },
                 'goods' => function ($query) {
                     $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_market_price', 'goods_sales_price', 'goods_classify', 'goods_if_express');
@@ -800,6 +813,7 @@ class WholeController extends Curd
         }
 
         $order = Order::where('order_id', $params['order_id'])->first();
+        $oldOrderPaymentStatus = $order->order_status_payment;
         if (!$order) {
             return json_fail('订单异常');
         }
@@ -817,7 +831,9 @@ class WholeController extends Curd
             }
         }
         $params['orderId'] = $params['order_id'];
-        $params['orderGroupId'] = $order->order_groupby;
+        $params['orderGroupId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+        $order->order_groupby = $params['orderGroupId'];
+//        $params['orderGroupId'] = $order->order_groupby;
 
         $goods = Goods::where('goods_id', $params['join_sheet_goods_id'])
             ->select('goods_id', 'goods_name', 'goods_classify')
@@ -1079,7 +1095,7 @@ class WholeController extends Curd
                 if (in_array($prefix, [10, 11, 12, 13, 14, 15])) {
                     $params['pay_category'] = 'WXPAY';
                     if ((!isset($result['return_code']) || $result['return_code'] != 'SUCCESS') || (!isset($result['result_code']) || $result['result_code'] != 'SUCCESS') || (empty($result['trade_state']) || $result['trade_state'] != 'SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderPaymentStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1091,7 +1107,7 @@ class WholeController extends Curd
                 } else if (in_array($prefix, [25, 26, 27, 28, 29, 30])) {
                     $params['pay_category'] = 'ALIPAY';
                     if ((!isset($result['code']) || $result['code'] != '10000') || (empty($result['trade_status']) || $result['trade_status'] != 'TRADE_SUCCESS')) {
-                        $order->order_status_system = 'PAYING';
+                        $order->order_status_system = $oldOrderPaymentStatus;
                         $order->order_status_payment = 'PENDING';
                         $order->order_is_complete = 'N';
 //                        Db::rollBack();
@@ -1184,8 +1200,9 @@ class WholeController extends Curd
             if (!PayDetail::where('join_pay_order_id', $order->order_groupby)->where('pay_category', '<>', 'WXPAY')->where('pay_category', '<>', 'ALIPAY')->exists()) {
                 $payData['join_pay_member_id'] = $params['join_order_member_id'];
                 $payData['join_pay_order_id'] = $order->order_groupby;
-                $payData['pay_status'] = $payData['pay_status'] == 'SUCCESS' ? $payData['pay_status'] : 'WAITING';
+                $payData['pay_status'] = !empty($payData['pay_status']) && $payData['pay_status'] == 'SUCCESS' ? $payData['pay_status'] : 'WAITING';
                 $payData['pay_category'] = $params['goods_classify'] ?? '';
+                $payData['pay_paytimes'] = date('Y-m-d H:i:s');
                 $payData['pay_json_request'] = json_encode($params);   // {"pay-result": "支付成功", "result-datetime": "2024-07-29 18:38:21"}
                 $payData['pay_json_response'] = $payData['pay_status'] == 'SUCCESS' ? json_encode([
                     'pay-result' => '支付成功', 'result-datetime' => date('Y-m-d H:i:s')
@@ -1287,10 +1304,11 @@ class WholeController extends Curd
             if ($order->order_is_complete == 'Y' && $order->order_status_payment == 'SUCCESS') {
                 // 完成订单
                 Event::dispatch('order.complete', $params);
-                // 会员升级
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = $oldOrderPaymentStatus == 'AWAITING' ? '挂账订单结算收入' : '餐饮订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票
@@ -1336,14 +1354,14 @@ class WholeController extends Curd
             _syslog("订单", "订单支付成功");
             return json_success('支付成功');
         } catch (BusinessException $e) {
-            dump($e->getMessage());
             Db::rollBack();
             _syslog("订单", "订单支付失败:" . $e->getMessage());
+            Log::error("订单支付失败:".$e->getMessage());
             return json_fail("支付失败:" . $e->getMessage());
         } catch (\Exception $e) {
-            dump($e->getMessage());
             Db::rollBack();
             _syslog("订单", "订单支付失败");
+            Log::error("订单支付失败:".$e->getMessage());
             return json_fail('支付失败');
         }
     }
@@ -1423,6 +1441,7 @@ class WholeController extends Curd
         }
 
         $order = Order::where('order_id', $params['order_id'])->first();
+        $oldOrderPaymentStatus = $order->order_status_payment;
         if (!$order) {
             return json_fail('订单异常');
         }
@@ -1439,7 +1458,9 @@ class WholeController extends Curd
             }
         }
         $params['orderId'] = $params['order_id'];
-        $params['orderGroupId'] = $order->order_groupby;
+        $params['orderGroupId'] = 'OD' . date('ymdHi') . random_string(4, 'up');
+        $order->order_groupby = $params['orderGroupId'];
+//        $params['orderGroupId'] = $order->order_groupby;
 
         $goods = Goods::where('goods_id', $params['join_sheet_goods_id'])
             ->select('goods_id', 'goods_name', 'goods_classify')
@@ -1745,10 +1766,11 @@ class WholeController extends Curd
             if ($order->order_is_complete == 'Y' && $order->order_status_payment == 'SUCCESS') {
                 // 完成订单
                 Event::dispatch('order.complete', $params);
-                // 会员升级
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = $oldOrderPaymentStatus == 'AWAITING' ? '挂账订单结算收入' : '餐饮订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票
@@ -2467,10 +2489,11 @@ class WholeController extends Curd
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 // 订单完成
                 Event::dispatch('order.complete', $params);
-                // 会员升级
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = '餐饮订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票
@@ -3024,10 +3047,11 @@ class WholeController extends Curd
             if (!empty($params['order_is_complete']) && $params['order_is_complete'] == 'Y' && $params['order_status_payment'] == 'SUCCESS') {
                 // 完成订单
                 Event::dispatch('order.complete', $params);
-                // 会员升级
-//                Event::dispatch('order_pay.member_level.up', $params['join_order_member_id']);
                 // 上级提成
                 Event::dispatch('commission.order', $params);
+                // 入收支明细表
+                $params['inout_category'] = '餐饮订单收入';
+                Event::dispatch('statistics.inout.in', $params);
             }
 
             // 打小票
@@ -3886,12 +3910,12 @@ class WholeController extends Curd
         }
 
         $member = Member::find($memberId);
-        $mobile = $member->member_mobile;
-        $key = "SMS:CODE:WRITE_OFF:" . $mobile;
-        $redisCode = Redis::get($key);
-        if ($redisCode != $code) {
-            return json_fail("验证码错误,请重新输入");
-        }
+//        $mobile = $member->member_mobile;
+//        $key = "SMS:CODE:WRITE_OFF:" . $mobile;
+//        $redisCode = Redis::get($key);
+//        if ($redisCode != $code) {
+//            return json_fail("验证码错误,请重新输入");
+//        }
 
         $benefits = MemberBenefit::whereIn('member_benefit_id', $benefitIds)
             ->where('join_benefit_member_id', $memberId)
@@ -4008,7 +4032,7 @@ class WholeController extends Curd
                 }
             }
 
-            Redis::del($key);
+//            Redis::del($key);
 
             Db::commit();
 
@@ -4040,44 +4064,53 @@ class WholeController extends Curd
         if (!$memberId) {
             return json_fail('查询错误');
         }
-        $goodsIds = [];
+        $sheetOrderIds = [];
         if (!empty($goodsName)) {
             $goodsIds = Goods::where('goods_name', 'like', '%' . $goodsName . '%')->pluck('goods_id')->toArray();
+            $sheetOrderIds = OrderSheet::whereIn('join_sheet_goods_id', $goodsIds)
+                ->where('join_sheet_member_id', $memberId)
+                ->pluck('join_sheet_order_id')
+                ->toArray();
         }
 
-        $sheet = OrderSheet::with([
-            'goods' => function ($query) {
-                $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_classify');
-            },
-            'order' => function ($query) {
-                $query->select('order_id', 'order_status_system', 'order_status_payment', 'order_status_storage');
-            },
-            'refund' => function ($query) {
+        $order = Order::with([
+            'return' => function ($query) {
                 $query->select('join_return_order_id', 'orders_return_id', 'order_return_status');
             },
+            'sheets',
+            'express' => function ($query) {
+                $query->select('join_express_order_id', 'order_express_type');
+            }
         ])
-            ->leftJoin('order', 'order.order_id', 'order_sheet.join_sheet_order_id')
-            ->where('join_sheet_member_id', $memberId)
-            ->where('order.order_category', '<>', 'RECHARGE')
+            ->where('join_order_member_id', $memberId)
+            ->where('order_classify', '<>', 'RECHARGE')
             ->when(!empty($datetime), function ($query) use ($datetime) {
                 $datetime[0] = strtotime($datetime[0]);
                 $datetime[1] = strtotime($datetime[1]);
-                $query->whereBetween('order_sheet_addtimes', $datetime);
+                $query->whereBetween('order_addtimes', $datetime);
             })
-            ->when(!empty($goodsIds), function ($query) use ($goodsIds) {
-                $query->whereIn('join_sheet_goods_id', $goodsIds);
+            ->when(!empty($sheetOrderIds), function ($query) use ($sheetOrderIds) {
+                $query->whereIn('order_id', $sheetOrderIds);
             });
 
-        $total = $sheet->count();
-        $rows = $sheet
-            ->orderBy('order_sheet_addtimes', 'desc')
+        $total = $order->count();
+        $rows = $order
+            ->orderBy('order_addtimes', 'desc')
             ->forPage($page, $pageSize)
             ->get()
             ->toArray();
         foreach ($rows as &$item) {
-            if (!empty($item->goods->goods_cover)) {
-                $item->goods->goods_cover = getenv('STORAGE_DOMAIN') . $item->goods->goods_cover;
+            $sheetGoodsIds = array_column($item['sheets'], 'join_sheet_goods_id');
+            $goods = Goods::whereIn('goods_id', $sheetGoodsIds)->select('goods_id', 'goods_name')->get()->toArray();
+            if (count($goods) > 1) {
+                $item['goods_name'] = $goods[0]['goods_name'] . ' 等';
+            } else {
+                $item['goods_name'] = $goods[0]['goods_name'];
             }
+//            $item['goods_name'] = !empty($goods->goods_name) ? $goods->goods_name : '';
+//            if (!empty($item->goods->goods_cover)) {
+//                $item->goods->goods_cover = getenv('STORAGE_DOMAIN') . $item->goods->goods_cover;
+//            }
         }
 
         return json_success('', compact('rows', 'page', 'pageSize', 'total'));
@@ -4411,6 +4444,89 @@ class WholeController extends Curd
         return call_user_func([$this, $format_function], $items, $total);
     }
 
+    protected function exportAfterQuery($items)
+    {
+        $data = [];
+        foreach ($items as $key => $item) {
+            $goodsClassify = '';
+            $goodsName = '--';
+            if (!empty($item['sheet'])) {
+                $goods = Goods::where('goods_id', $item['sheet']['join_sheet_goods_id'])->first();
+                $goodsClassify = $goods->goods_classify ?? '';
+                $goodsName = $goods->goods_name ?? '--';
+            }
+            $systemStatus = '';
+            if ($item['order_is_complete'] == 'Y') {
+                $systemStatus = '已完成';
+            } elseif ($item['order_is_complete'] == 'R') {
+                $systemStatus = '售后已完成';
+            }
+
+            if (!empty($item['order_return_status']) && $item['order_return_status'] == 'PENDING') {
+                $systemStatus = '售后待处理';
+            } elseif (!empty($item['order_return_status']) && $item['order_return_status'] == 'DOING') {
+                $systemStatus = '退款中';
+            } elseif (!empty($item['order_return_status']) && $item['order_return_status'] == 'DONE') {
+                $systemStatus = '退款完成';
+                if (!empty($item['order_return_refund_json'])) {
+                    $refundJson = json_decode($item['order_return_refund_json'], true);
+                    if (isset($refundJson['amount']) && $refundJson['amount'] >= $item['order_amount_pay']) {
+                        $systemStatus .= "(全额)";
+                    } else {
+                        $systemStatus .= "(部分)";
+                    }
+                }
+            } elseif (!empty($item['order_return_status']) && $item['order_return_status'] == 'DISAGREE') {
+                $systemStatus = '不同意退款';
+            }
+
+            $payDetails = PayDetail::where('join_pay_order_id', $item['order_groupby'])
+                ->whereJsonContains('join_pay_object_json->order_id', $item['order_id'])
+                ->where('pay_status', 'SUCCESS')
+                ->where('pay_category', '<>', 'REFUND')
+                ->get()
+                ->toArray();
+            if (count($payDetails) > 1) {
+                $payWay = '组合支付';
+            } elseif (count($payDetails) == 1) {
+                $payWayEn = OrderService::getPayWayByPrepayId($payDetails[0]['pay_prepayid']);
+                $payWay = '';
+                if (!empty($payWayEn)) {
+                    $payWay = OrderService::$payWay[$payWayEn];
+                }
+            }
+            if (count($payDetails) > 0) {
+                $payDetail0 = $payDetails[0];
+                $payTimes = $payDetail0['pay_paytimes'];
+            }
+            if (!empty($item['member']) && $item['member']['member_mobile'] == '0000') {
+                $memberName = '散客';
+            } else {
+                $memberName = !empty($item['cert']) ? $item['cert']['member_cert_name'] : (!empty($item['member']) ? (substr($item['member']['member_mobile'], -4) . '会员' . '-') : '');
+                if (!empty($item['member'])) {
+                    $memberName .= $item['member']['member_mobile'];
+                }
+            }
+            $data[$key] = [
+                'goods_classify' => !empty($goodsClassify) && isset(self::$goodsClassify[$goodsClassify]) ? self::$goodsClassify[$goodsClassify] : '其他订单',
+                'order_id' => $item['order_id'],
+                'member' => $memberName,
+                'goods_name' => $goodsName,
+                'nbr' => !empty($item['sheet']) ? intval($item['sheet']['order_sheet_num']) : '--',
+                'order_amount_total' => $item['order_amount_total'],
+                'order_amount_pay' => $item['order_amount_pay'],
+                'order_status_system' => !empty($systemStatus) ? $systemStatus : self::$systemStatus[$item['order_status_system']],
+                'order_status_payment' => self::$paymentStatus[$item['order_status_payment']],
+                'pay_way' => $item['order_status_payment'] == 'SUCCESS' ? ($payWay ?? '') : '',
+                'pay_times' => $item['order_status_payment'] == 'SUCCESS' || $item['order_status_payment'] == 'FREE' ? ($payTimes ?? '') : '',
+                'order_addtimes' => date('Y-m-d H:i:s', strtotime($item['order_addtimes'])),
+            ];
+        }
+
+        return $data;
+    }
+
+
     public function benefitList(Request $request)
     {
         $orderIds = $request->get('order_ids', []);
@@ -4614,79 +4730,6 @@ class WholeController extends Curd
         }
     }
 
-    protected function exportAfterQuery($items)
-    {
-        $data = [];
-        foreach ($items as $key => $item) {
-            $goodsClassify = '';
-            $goodsName = '--';
-            if (!empty($item['sheet'])) {
-                $goods = Goods::where('goods_id', $item['sheet']['join_sheet_goods_id'])->first();
-                $goodsClassify = $goods->goods_classify ?? '';
-                $goodsName = $goods->goods_name ?? '--';
-            }
-            $systemStatus = '';
-            if ($item['order_is_complete'] == 'Y') {
-                $systemStatus = '已完成';
-            } elseif ($item['order_is_complete'] == 'R') {
-                $systemStatus = '售后已完成';
-            }
-
-            if ($item['order_is_complete'] != 'R' && !empty($item['order_return_status']) && $item['order_return_status'] == 'PENDING') {
-                $systemStatus = '待处理';
-            } elseif ($item['order_is_complete'] != 'R' && !empty($item['order_return_status']) && $item['order_return_status'] == 'DOING') {
-                $systemStatus = '退款中';
-            } elseif ($item['order_is_complete'] != 'R' && !empty($item['order_return_status']) && $item['order_return_status'] == 'DONE') {
-                $systemStatus = '退款完成';
-            } elseif ($item['order_is_complete'] != 'R' && !empty($item['order_return_status']) && $item['order_return_status'] == 'DISAGREE') {
-                $systemStatus = '不同意退款';
-            }
-
-            $payDetails = PayDetail::where('join_pay_order_id', $item['order_groupby'])
-                ->where('pay_status', 'SUCCESS')
-                ->where('pay_category', '<>', 'REFUND')
-                ->get()
-                ->toArray();
-            if (count($payDetails) > 1) {
-                $payWay = '组合支付';
-            } elseif (count($payDetails) == 1) {
-                $payWayEn = OrderService::getPayWayByPrepayId($payDetails[0]['pay_prepayid']);
-                $payWay = '';
-                if (!empty($payWayEn)) {
-                    $payWay = OrderService::$payWay[$payWayEn];
-                }
-            }
-            if (count($payDetails) > 0) {
-                $payDetail0 = $payDetails[0];
-                $payTimes = $payDetail0['pay_paytimes'];
-            }
-            if (!empty($item['member']) && $item['member']['member_mobile'] == '0000') {
-                $memberName = '散客';
-            } else {
-                $memberName = !empty($item['cert']) ? $item['cert']['member_cert_name'] : (!empty($item['member']) ? (substr($item['member']['member_mobile'], -4) . '会员' . '-') : '');
-                if (!empty($item['member'])) {
-                    $memberName .= $item['member']['member_mobile'];
-                }
-            }
-            $data[$key] = [
-                'goods_classify' => !empty($goodsClassify) && isset(self::$goodsClassify[$goodsClassify]) ? self::$goodsClassify[$goodsClassify] : '其他订单',
-                'order_id' => $item['order_id'],
-                'member' => $memberName,
-                'goods_name' => $goodsName,
-                'nbr' => !empty($item['sheet']) ? intval($item['sheet']['order_sheet_num']) : '--',
-                'order_amount_total' => $item['order_amount_total'],
-                'order_amount_pay' => $item['order_amount_pay'],
-                'order_status_system' => !empty($systemStatus) ? $systemStatus : self::$systemStatus[$item['order_status_system']],
-                'order_status_payment' => self::$paymentStatus[$item['order_status_payment']],
-                'pay_way' => $item['order_status_payment'] == 'SUCCESS' ? ($payWay ?? '') : '',
-                'pay_times' => $item['order_status_payment'] == 'SUCCESS' || $item['order_status_payment'] == 'FREE' ? ($payTimes ?? '') : '',
-                'order_addtimes' => date('Y-m-d H:i:s', strtotime($item['order_addtimes'])),
-            ];
-        }
-
-        return $data;
-    }
-
     /**
      * @Desc 查询订单状态
      * @Author Gorden
@@ -4703,6 +4746,8 @@ class WholeController extends Curd
         }
 
         $order = Order::where('order_id', $orderId)->first();
+        $oldOrderPaymentStatus = $order->order_status_payment;
+        //order_status_payment
         if (!$order) {
             return json_fail('订单不存在');
         }
@@ -4715,14 +4760,16 @@ class WholeController extends Curd
         if ($order->order_status_payment == 'SUCCESS') {
             return json_success('success');
         }
+        $params['orderId'] = $orderId;
 
         $payDetailType = PayDetail::where('join_pay_order_id', $order->order_groupby)->pluck('pay_prepayid')->toArray();
         try {
             Db::beginTransaction();
+            $full = '';
             if (in_array('WXPAY', $payDetailType)) {
                 $result = Pay::wechat(config('payment.wxpay'))->find($order->order_groupby, 'pos');
                 $result = json_decode(json_encode($result), true);
-//                $result = '{"return_code":"SUCCESS","return_msg":"OK","result_code":"SUCCESS","mch_id":"1680393367","appid":"wxc6274da7198e3eb4","openid":"o3JAn6Ii_bAlxS-jbNEC4WnPhdwM","is_subscribe":"N","trade_type":"MICROPAY","trade_state":"SUCCESS","bank_type":"OTHERS","total_fee":"1000","fee_type":"CNY","cash_fee":"1000","cash_fee_type":"CNY","transaction_id":"4200067718202409250802875650","out_trade_no":"OD24092518408RV7","attach":[],"time_end":"20240925184009","trade_state_desc":"支付成功","nonce_str":"OeGOkjch4eaV5qIt","sign":"6DCB3BFC594EBC018A2BEE2C3DFEA4E3"}';
+//                $result = '{"return_code":"SUCCESS","return_msg":"OK","result_code":"SUCCESS","mch_id":"1680393367","appid":"wxc6274da7198e3eb4","openid":"o3JAn6Ii_bAlxS-jbNEC4WnPhdwM","is_subscribe":"N","trade_type":"MICROPAY","trade_state":"SUCCESS","bank_type":"OTHERS","total_fee":"3","fee_type":"CNY","cash_fee":"1000","cash_fee_type":"CNY","transaction_id":"4200067718202409250802875650","out_trade_no":"OD24092518408RV7","attach":[],"time_end":"20240925184009","trade_state_desc":"支付成功","nonce_str":"OeGOkjch4eaV5qIt","sign":"6DCB3BFC594EBC018A2BEE2C3DFEA4E3"}';
 //                $result = json_decode($result, true);
                 if (!empty($result['return_code']) && $result['return_code'] == 'SUCCESS' && !empty($result['result_code']) && $result['result_code'] == 'SUCCESS' && !empty($result['trade_state']) && $result['trade_state'] == 'SUCCESS') {
                     $order->order_status_payment = 'SUCCESS';
@@ -4734,8 +4781,10 @@ class WholeController extends Curd
                     } else if (in_array($order->order_category, ['VIP', 'PARTNER'])) {
                         $order->order_status_payment = 'PENDING';
                         $totalFee = $result['total_fee'] / 100;
-                        if ($order->order_amount_paid + $totalFee >= $order->order_amount_total) {
-                            $order->order_amount_paid = $order->order_amount_paid + $totalFee;
+                        $order->order_amount_paid = $order->order_amount_paid + $totalFee;
+                        $order->order_amount_pay = $order->order_amount_paid;
+                        if ($order->order_amount_paid >= $order->order_amount_total) {
+                            $full = '-满额';
                             $order->order_status_payment = 'SUCCESS';
                             $order->order_status_system = 'DONE';
                             $order->order_is_complete = 'Y';
@@ -4749,12 +4798,24 @@ class WholeController extends Curd
                         OrderSheet::where('join_sheet_order_id', $orderId)->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
                     }
                     // 支付记录
-                    PayDetail::where('join_pay_order_id', $order->order_groupby)->where('pay_prepayid', 'WXPAY')->update(['pay_status' => 'SUCCESS']);
-
+                    PayDetail::where('join_pay_order_id', $order->order_groupby)->where('pay_prepayid', 'WXPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
                     // 优惠券标记使用
                     $this->changeOrderCouponStatus($discountJson, 'USED');
 
                     Db::commit();
+
+                    if (in_array($order->order_category, ['SERVICE', 'CHNMED', 'CHNNCD', 'PACKAGE', 'GOODS', 'DISHES', 'MEALS'])) {
+                        // 给上级提成
+                        Event::dispatch('commission.order', $params);
+                    }
+                    // 收支明细
+                    $params['inout_category'] = $oldOrderPaymentStatus == 'AWAITING' ? '挂账订单结算收入' : ($order->order_classify == 'VIP' ? ('购买康养城VIP套餐包收入' . $full) : '餐饮订单收入');
+                    Event::dispatch('statistics.inout.in', $params);
+
                     return json_success('success');
                 }
             } else if (in_array('ALIPAY', $payDetailType)) {
@@ -4771,8 +4832,10 @@ class WholeController extends Curd
                         $order->order_status_system = 'DONE';
                     } else if (in_array($order->order_category, ['VIP', 'PARTNER'])) {
                         $order->order_status_payment = 'PENDING';
-                        if ($order->order_amount_paid + $result['total_amount'] >= $order->order_amount_total) {
-                            $order->order_amount_paid = $order->order_amount_paid + $result['total_amount'];
+                        $order->order_amount_paid = $order->order_amount_paid + $result['total_amount'];
+                        $order->order_amount_pay = $order->order_amount_paid;
+                        if ($order->order_amount_paid >= $order->order_amount_total) {
+                            $full = '-满额';
                             $order->order_status_system = 'DONE';
                             $order->order_status_payment = 'SUCCESS';
                             $order->order_is_complete = 'Y';
@@ -4786,12 +4849,25 @@ class WholeController extends Curd
                         OrderSheet::where('join_sheet_order_id', $orderId)->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
                     }
                     // 支付记录
-                    PayDetail::where('join_pay_order_id', $order->order_groupby)->where('pay_prepayid', 'ALIPAY')->update(['pay_status' => 'SUCCESS']);
-                    // 其他支付方式 扣
-//                    $this->deductAccount($order->order_groupby, 'ALIPAY');
+                    PayDetail::where('join_pay_order_id', $order->order_groupby)->where('pay_prepayid', 'ALIPAY')->update([
+                        'pay_status' => 'SUCCESS',
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_json_response' => json_encode($result)
+                    ]);
+
                     // 优惠券标记使用
                     $this->changeOrderCouponStatus($discountJson, 'USED');
                     Db::commit();
+
+                    if (in_array($order->order_category, ['SERVICE', 'CHNMED', 'CHNNCD', 'PACKAGE', 'GOODS', 'DISHES', 'MEALS'])) {
+                        // 给上级提成
+                        Event::dispatch('commission.order', $params);
+                    }
+
+                    // 收支明细
+                    $params['inout_category'] = $oldOrderPaymentStatus == 'AWAITING' ? '挂账订单结算收入' : ($order->order_classify == 'VIP' ? ('购买康养城VIP套餐包收入' . $full) : '餐饮订单收入');
+                    Event::dispatch('statistics.inout.in', $params);
+
                     return json_success('success');
                 }
             }
@@ -4823,7 +4899,7 @@ class WholeController extends Curd
         if (empty($orders)) {
             return json_fail('订单不存在');
         }
-
+        $params['orderGroupId'] = $groupId;
         $payDetailType = PayDetail::where('join_pay_order_id', $groupId)->pluck('pay_prepayid')->toArray();
         try {
             Db::beginTransaction();
@@ -4848,7 +4924,11 @@ class WholeController extends Curd
                         OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
 
                         // 支付记录
-                        PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'WXPAY')->update(['pay_status' => 'SUCCESS']);
+                        PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'WXPAY')->update([
+                            'pay_status' => 'SUCCESS',
+                            'pay_paytimes' => date('Y-m-d H:i:s'),
+                            'pay_json_response' => json_encode($result)
+                        ]);
 
                         $discountJson = [];
                         if (!empty($orders[0]['order_discount_json'])) {
@@ -4877,7 +4957,11 @@ class WholeController extends Curd
                         // Sheet
                         OrderSheet::where('join_sheet_order_id', $order['order_id'])->where('order_sheet_status', 'PAYING')->update(['order_sheet_status' => 'DONE']);
                         // 支付记录
-                        PayDetail::where('join_pay_order_id', $order->order_groupby)->where('pay_prepayid', 'ALIPAY')->update(['pay_status' => 'SUCCESS']);
+                        PayDetail::where('join_pay_order_id', $order['order_groupby'])->where('pay_prepayid', 'ALIPAY')->update([
+                            'pay_status' => 'SUCCESS',
+                            'pay_paytimes' => date('Y-m-d H:i:s'),
+                            'pay_json_response' => json_encode($result)
+                        ]);
                         // 优惠券标记使用
                         $discountJson = [];
                         if (!empty($orders[0]['order_discount_json'])) {
@@ -4919,6 +5003,11 @@ class WholeController extends Curd
                     $this->insertBenefitPackage($groupId);
                 }
 
+                // 给上级提成-拆单
+                OrderService::splitOrderCommission($params);
+                // 入收支明细表
+                OrderService::splitOrderStatisticsInOut($params);
+
                 return json_success('success');
             }
 

+ 59 - 0
app/admin/controller/sys_manage/ConfigController.php

@@ -196,6 +196,65 @@ class ConfigController
         }
     }
 
+    /**
+     * @Desc 获取推荐官参数
+     * @Author Gorden
+     * @Date 2024/9/29 15:05
+     *
+     * @return \support\Response
+     */
+    public function referrerParamsInfo()
+    {
+        $config = SysConfig::where('config_key', 'app-identity-referrer-config')->first()->toArray();
+        $config['config_value_json'] = json_decode($config['config_value_json'], true);
+        if (!empty($config['config_value_json']['account']['added'])) {
+            $config['config_value_json']['account']['added'] = round($config['config_value_json']['account']['added'] * 100, 2);
+        }
+        if (!empty($config['config_value_json']['commission'])) {
+            $config['config_value_json']['commission']['direct'] = round($config['config_value_json']['commission']['direct'] * 100, 2);
+            $config['config_value_json']['commission']['indirect'] = round($config['config_value_json']['commission']['indirect'] * 100, 2);
+            $config['config_value_json']['commission']['relate_consum'] = round($config['config_value_json']['commission']['relate-consum'] * 100, 2);
+            $config['config_value_json']['commission']['invite_non_identity'] = round($config['config_value_json']['commission']['invite-non-identity'] * 100, 2);
+            $config['config_value_json']['commission']['agent_senior_join'] = round($config['config_value_json']['commission']['agent-senior-join'] * 100, 2);
+            $config['config_value_json']['commission']['agent_senior_strategy'] = round($config['config_value_json']['commission']['agent-senior-strategy'] * 100, 2);
+        }
+
+        return json_success('success', $config);
+    }
+
+    /**
+     * @Desc 设置推荐官参数
+     * @Author Gorden
+     * @Date 2024/9/29 15:06
+     *
+     * @param Request $request
+     * @return \support\Response
+     */
+    public function setReferrerParams(Request $request)
+    {
+        $params = $request->post();
+        if (!empty($params['account']['added'])) {
+            $params['account']['added'] = $params['account']['added'] / 100;
+        }
+        if (!empty($params['commission'])) {
+            $params['commission']['direct'] = $params['commission']['direct'] / 100;
+            $params['commission']['indirect'] = $params['commission']['indirect'] / 100;
+            $params['commission']['relate-consum'] = $params['commission']['relate_consum'] / 100;
+            $params['commission']['invite-non-identity'] = $params['commission']['invite_non_identity'] / 100;
+            $params['commission']['agent-senior-join'] = $params['commission']['agent_senior_join'] / 100;
+            $params['commission']['agent-senior-strategy'] = $params['commission']['agent_senior_strategy'] / 100;
+
+            unset($params['commission']['relate_consum'], $params['commission']['invite_non_identity'], $params['commission']['agent_senior_join'], $params['commission']['agent_senior_strategy']);
+        }
+        try {
+            SysConfig::where('config_key', 'app-identity-referrer-config')->update(['config_value_json' => json_encode($params)]);
+
+            return json_success('success');
+        } catch (\Exception $e) {
+            return json_fail("保存失败");
+        }
+    }
+
     private function getType($type)
     {
         switch ($type) {

+ 2 - 1
app/admin/service/added/AddedService.php

@@ -39,7 +39,8 @@ class AddedService{
                 'charge_content'=>$params['remark'] ?? '',
                 'charge_user_id'=>$params['write_off_member_id'],
                 'charge_premises'=>$params['dept_premises_id'],
-                'charge_write_off_time'=>$params['times']
+                'charge_write_off_time'=>$params['times'],
+                'charge_waiter' => $params['charge_waiter'] ?? ''
             ],
             'member_id'=>$params['member_id'],
             'quota_code'=>random_string(10,'number'),

+ 2 - 1
app/admin/service/consultant/CustomService.php

@@ -1101,7 +1101,8 @@ class CustomService
                     'consultant_name' => $consultantKeys[$item->consultant_id]['name'] ?? '', //健康顾问
                     'team_name' => TeamService::getTeamName($teamKeys, $item->dept_id), //所属团队
                     'current_status' => $currentStatusText, //当前状态
-                    'visit_time' => $item->visit_time, //到访时间
+                    'visit_time' => $item->visit_time, //最后一次跟进时间
+                    'check_time' => $item->current_status > 1 ? $item->check_time : '', //首次到访时间
                     "age_range" => $ageRangeText, //年龄区间
                     "level" => $levelText, //客户等级
                     "region" => $regionText, //所在区域

+ 236 - 5
app/admin/service/coupon/CouponDetailService.php

@@ -2,9 +2,12 @@
 
 namespace app\admin\service\coupon;
 
+use app\model\Coupon;
 use app\model\CouponDetail;
 use app\model\SysSerial;
+use support\Db;
 use support\exception\BusinessException;
+use support\Log;
 
 class CouponDetailService
 {
@@ -19,6 +22,10 @@ class CouponDetailService
      */
     public static function customSendCoupon($params)
     {
+        $gettype = 'SEND';
+        if (!empty($params['gettype'])) {
+            $gettype = $params['gettype'];
+        }
         try {
             CouponDetail::insert([
                 'coupon_detail_id' => 'CUDT' . date("ymdHi") . random_string(4, 'up'),
@@ -28,7 +35,7 @@ class CouponDetailService
                 'coupon_detail_gain_datetime' => $params['coupon_detail_gain_datetime'],
                 'coupon_detail_deadline_datetime' => $params['coupon_detail_deadline_datetime'],
                 'coupon_detail_period_num' => $params['coupon_detail_period_num'] ?? 0,
-                'coupon_detail_extend_json'=>json_encode(['gettype'=>'SEND']),
+                'coupon_detail_extend_json' => json_encode(['gettype' => $gettype]),
                 'coupon_detail_addtimes' => time(),
             ]);
         } catch (\Exception $e) {
@@ -38,21 +45,245 @@ class CouponDetailService
 
     public static function customSendCouponHave($params)
     {
+        $gettype = 'SEND';
+        if (!empty($params['gettype'])) {
+            $gettype = $params['gettype'];
+        }
+        try {
+            CouponDetail::where('join_detail_coupon_id', $params['coupon_id'])
+                ->whereIn('coupon_detail_status', ['INIT', 'PENDING'])
+                ->limit($params['chooseCouponNbr'])
+                ->update([
+                    'join_coupon_detail_member_id' => $params['member_id'],
+                    'coupon_detail_gain_datetime' => $params['coupon_detail_gain_datetime'],
+                    'coupon_detail_deadline_datetime' => $params['coupon_detail_deadline_datetime'],
+                    'coupon_detail_extend_json' => json_encode(['gettype' => $gettype]),
+                    'coupon_detail_period_num' => $params['coupon_detail_period_num'] ?? 0,
+                    'coupon_detail_status' => 'ACTIVED',
+                    'coupon_detail_addtimes' => time()
+                ]);
+        } catch (\Exception $e) {
+            throw new BusinessException('写入优惠券失败');
+        }
+    }
+
+    /**
+     * @Desc 发周期优惠券 - 没有发行数量
+     * @Author Gorden
+     * @Date 2024/9/27 13:44
+     *
+     * @param $params
+     * @return void
+     */
+    public static function sendPeriodCoupon($params)
+    {
+        Db::beginTransaction();
+        try {
+            $coupon = Coupon::where('coupon_id', $params['coupon_id'])->first();
+            if ($coupon->coupon_is_period != 'Y') {
+                return;
+            }
+            $detailCount = -1;
+            if ($coupon->coupon_number > 0) {
+                $detailCount = CouponDetail::where('join_detail_coupon_id', $params['coupon_id'])->whereIn('coupon_detail_status', ['INIT', 'PENDING'])->count();
+            }
+            $periodJson = json_decode($coupon->coupon_period_json, true);
+            if ($detailCount != -1 && $detailCount - $periodJson['nbr'] < 0) {
+                throw new BusinessException("优惠券余量不足");
+            }
+            $periodJson['now_nbr'] = 1;
+            // 手动发券会传来时间
+            $periodJson['gain_datetime'] = $params['coupon_detail_gain_datetime'] ?? date('Y-m-d H:i:s');
+            if (!empty($periodJson['nbr'])) {
+                for ($i = 0; $i < $periodJson['nbr']; $i++) {
+                    $periodParams = self::generatePeriod($periodJson);
+                    $periodParams['gettype'] = $params['gettype'] ?? '';
+                    $periodParams['coupon_id'] = $params['coupon_id'];
+                    $periodParams['member_id'] = $params['member_id'];
+                    $periodParams['coupon_detail_period_num'] = $periodJson['now_nbr'];
+                    if ($detailCount > 0) {
+                        $periodParams['chooseCouponNbr'] = 1;
+                        self::sendPeriodCouponHave($periodParams);
+                    } else {
+                        self::sendPeriodCouponNoLimit($periodParams);
+                    }
+
+                    $periodJson['now_nbr'] += 1;
+                }
+            }
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            Log::error("发券失败:" . $e->getMessage());
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            Log::error("发券失败:" . $e->getMessage());
+            throw new BusinessException("优惠券发放失败");
+        }
+    }
+
+    /**
+     * @Desc 有发行数量
+     * @Author Gorden
+     * @Date 2024/9/27 13:45
+     *
+     * @param $params
+     * @return void
+     */
+    public static function sendPeriodCouponHave($params)
+    {
+        $gettype = 'SEND';
+        if (!empty($params['gettype'])) {
+            $gettype = $params['gettype'];
+        }
         try {
-            CouponDetail::where('join_detail_coupon_id',$params['coupon_id'])
-                ->whereIn('coupon_detail_status',['INIT','PENDING'])
+            CouponDetail::where('join_detail_coupon_id', $params['coupon_id'])
+                ->whereIn('coupon_detail_status', ['INIT', 'PENDING'])
                 ->limit($params['chooseCouponNbr'])
                 ->update([
                     'join_coupon_detail_member_id' => $params['member_id'],
                     'coupon_detail_gain_datetime' => $params['coupon_detail_gain_datetime'],
                     'coupon_detail_deadline_datetime' => $params['coupon_detail_deadline_datetime'],
-                    'coupon_detail_extend_json'=>json_encode(['gettype'=>'SEND']),
-                    'coupon_detail_status'=>'ACTIVED',
+                    'coupon_detail_extend_json' => json_encode(['gettype' => $gettype]),
+                    'coupon_detail_period_num' => $params['coupon_detail_period_num'] ?? 0,
+                    'coupon_detail_status' => 'ACTIVED',
                     'coupon_detail_addtimes' => time()
                 ]);
         } catch (\Exception $e) {
+            dump($e->getMessage());
             throw new BusinessException('写入优惠券失败');
         }
     }
 
+    /**
+     * @Desc 发行量不限
+     * @Author Gorden
+     * @Date 2024/9/27 16:19
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function sendPeriodCouponNoLimit($params)
+    {
+        $gettype = 'SEND';
+        if (!empty($params['gettype'])) {
+            $gettype = $params['gettype'];
+        }
+        try {
+            CouponDetail::insert([
+                'coupon_detail_id' => 'CUDT' . date("ymdHi") . random_string(4, 'up'),
+                'join_detail_coupon_id' => $params['coupon_id'],
+                'join_coupon_detail_member_id' => $params['member_id'],
+                'coupon_detail_status' => 'ACTIVED',
+                'coupon_detail_gain_datetime' => $params['coupon_detail_gain_datetime'],
+                'coupon_detail_deadline_datetime' => $params['coupon_detail_deadline_datetime'],
+                'coupon_detail_period_num' => $params['coupon_detail_period_num'] ?? 0,
+                'coupon_detail_extend_json' => json_encode(['gettype' => $gettype]),
+                'coupon_detail_addtimes' => time(),
+            ]);
+        } catch (\Exception $e) {
+            throw new BusinessException('写入优惠券失败');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/9/27 13:54
+     *
+     * @param $periodJson
+     * @return array
+     */
+    public static function generatePeriod($periodJson)
+    {
+        // 当前第几期
+        $now_nbr = $periodJson['now_nbr'];
+        $params = [];
+        if ($periodJson['unit'] == 'day') {
+            $val = $periodJson['val'] - 1;
+            if ($val < 1) {
+                if ($now_nbr == 1) {
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-d 00:00:00', strtotime($periodJson['gain_datetime']));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime($periodJson['gain_datetime']));
+                } else {
+                    $now_nbr -= 1;
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-d 00:00:00', strtotime($periodJson['gain_datetime'] . "+" . $now_nbr . ' day'));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime($periodJson['gain_datetime'] . "+" . $now_nbr . ' day'));
+                }
+            } else {
+                if ($now_nbr == 1) {
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-d 00:00:00', strtotime($periodJson['gain_datetime']));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime($periodJson['gain_datetime'] . "+" . $val . ' day'));
+                } else {
+                    $now_nbr -= 1;
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-d 00:00:00', strtotime($periodJson['gain_datetime'] . "+" . ((($val + 1) * $now_nbr)) . " day"));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime($periodJson['gain_datetime'] . "+" . ((($val + 1) * $now_nbr) + $val) . " day"));
+                }
+            }
+        } elseif ($periodJson['unit'] == 'week') {
+            $val = $periodJson['val'] - 1;
+            // 选的日期的周一距离今周周一是几个周
+            $timestamp = strtotime($periodJson['gain_datetime']);
+            $gainWeekDay = date("w", $timestamp);
+            $gainWeekAdd = date("w", $gainWeekDay) == 1 ? 0 : 1 - $gainWeekDay;
+            $gainMonday = date("Y-m-d 00:00:00", strtotime("$gainWeekAdd days", $timestamp));
+            $nowMonday = date('Y-m-d 00:00:00', strtotime('this week Monday'));
+
+            $interval = (new \DateTime(date($gainMonday)))->diff(new \DateTime($nowMonday));
+            $weekCut = $interval->days / 7;
+            if (strtotime($periodJson['gain_datetime']) < time() && $weekCut > 0) {
+                $weekCut = -$weekCut;
+            }
+            if ($val < 1) {
+                if ($now_nbr == 1) {
+                    $params['coupon_detail_gain_datetime'] = $gainMonday;
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime($gainMonday . "+6 days"));
+                } else {
+                    $now_nbr -= 1;
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-d 00:00:00', strtotime(date('Y-m-d 00:00:00', strtotime('this week Monday')) . "+" . ($now_nbr + $weekCut) . ' week'));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime(date('Y-m-d 23:59:59', strtotime('this week Sunday')) . "+" . ($now_nbr + $weekCut) . ' week'));
+                }
+            } else {
+                if ($now_nbr == 1) {
+                    $params['coupon_detail_gain_datetime'] = $gainMonday;
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime(date('Y-m-d 23:59:59', strtotime('this week Sunday')) . "+" . ($val + $weekCut) . ' week'));
+                } else {
+                    $now_nbr -= 1;
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-d 00:00:00', strtotime(date('Y-m-d 00:00:00', strtotime('this week Monday')) . "+" . ((($val + 1) * $now_nbr) + $weekCut) . ' week'));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-d 23:59:59', strtotime(date('Y-m-d 23:59:59', strtotime('this week Sunday')) . "+" . ((($val + 1) * $now_nbr) + $val + $weekCut) . ' week'));
+                }
+            }
+        } elseif ($periodJson['unit'] == 'month') {
+            $val = $periodJson['val'] - 1;
+            // 选的日期的1号距离今月1号是几个月
+            $interval = (new \DateTime(date('Y-m-02 00:00:00', strtotime($periodJson['gain_datetime']))))->diff(new \DateTime(date('Y-m-02 00:00:00')));
+            $monthCut = $interval->m + ($interval->y * 12);
+            if (strtotime($periodJson['gain_datetime']) < time() && $monthCut > 0) {
+                $monthCut = -$monthCut;
+            }
+            if ($val < 1) {
+                if ($now_nbr == 1) {
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-01 00:00:00', strtotime($periodJson['gain_datetime']));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59', strtotime($periodJson['gain_datetime']));
+                } else {
+                    $now_nbr -= 1;
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-01 00:00:00', strtotime("+" . ($now_nbr + $monthCut) . ' month'));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59', strtotime(date('Y-m-01 00:00:00') . "+" . ($now_nbr + $monthCut) . ' month'));
+                }
+            } else {
+                if ($now_nbr == 1) {
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-01 00:00:00', strtotime($periodJson['gain_datetime']));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59', strtotime(date('Y-m-01 00:00:00') . "+" . ($val + $monthCut) . ' month'));
+                } else {
+                    $now_nbr -= 1;
+                    $params['coupon_detail_gain_datetime'] = date('Y-m-01 00:00:00', strtotime("+" . ((($val + 1) * $now_nbr) + $monthCut) . ' month'));
+                    $params['coupon_detail_deadline_datetime'] = date('Y-m-t 23:59:59', strtotime(date('Y-m-01 00:00:00') . "+" . ((($val + 1) * $now_nbr) + $val + $monthCut) . ' month'));
+                }
+            }
+        }
+
+        return $params;
+    }
 }

+ 83 - 3
app/admin/service/coupon/CouponService.php

@@ -4,7 +4,10 @@ namespace app\admin\service\coupon;
 
 use app\model\Coupon;
 use app\model\CouponDetail;
+use app\model\Goods;
 use support\Db;
+use support\exception\BusinessException;
+use support\Log;
 
 class CouponService
 {
@@ -45,7 +48,7 @@ class CouponService
                     continue;
                 }
                 $details = CouponDetail::where('join_detail_coupon_id', $coupon['coupon_id'])
-                    ->where('join_coupon_detail_member_id','<>','')
+                    ->where('join_coupon_detail_member_id', '<>', '')
                     ->selectRaw('join_coupon_detail_member_id,COUNT(*) as count')
                     ->groupBy('join_coupon_detail_member_id')
                     ->get();
@@ -101,11 +104,11 @@ class CouponService
                             }
                         }
                     }
-                    if (CouponDetail::whereIn('coupon_detail_status',['INIT','PENDING'])->exists()){
+                    if (CouponDetail::whereIn('coupon_detail_status', ['INIT', 'PENDING'])->exists()) {
                         // 匹配已发行的优惠券
                         $params['chooseCouponNbr'] = 1;
                         CouponDetailService::customSendCouponHave($params);
-                    }else{
+                    } else {
                         // 写入优惠券
                         CouponDetailService::customSendCoupon($params);
                     }
@@ -167,4 +170,81 @@ class CouponService
         }
 
     }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/9/28 14:00
+     *
+     * @param $params
+     *          join_sheet_goods_id  关联测产品
+     *          member_id   接收人
+     * @return void
+     * @throws BusinessException
+     */
+    public static function autoSendCouponByGoods($params)
+    {
+        try {
+            $gettype = $params['gettype'] ?? '';
+            $goods = Goods::where('goods_id', $params['join_sheet_goods_id'])->select('goods_attribute_json')->first();
+            if (!empty($goods) && !empty($goods->goods_attribute_json)) {
+                $goodsAttributeJson = json_decode($goods->goods_attribute_json, true);
+                if (!empty($goodsAttributeJson['coupon'])) {
+                    foreach ($goodsAttributeJson['coupon'] as $key => $coupon) {
+                        $couponModel = Coupon::where('coupon_id', $key)->select('coupon_id', 'coupon_validdate_day', 'coupon_validdate_end', 'coupon_is_period', 'coupon_number')->first();
+                        if (empty($couponModel)) {
+                            continue;
+                        }
+                        // 券是否过期
+                        if (!empty($couponModel->coupon_validdate_end) && strtotime($couponModel->coupon_validdate_end) < time()) {
+                            continue;
+                        }
+                        // 发周期券
+                        if ($couponModel->coupon_is_period == 'Y') {
+                            // 发券参数
+                            $couponSendParams = ['gettype' => $gettype, 'coupon_id' => $key, 'member_id' => $params['member_id']];
+                            Log::info("发周期券参数", $couponSendParams);
+                            CouponDetailService::sendPeriodCoupon($couponSendParams);
+                            continue;
+                        }
+                        $couponResidue = 'all';
+                        if ($couponModel->coupon_number != 0) {
+                            $couponResidue = CouponDetail::where('join_detail_coupon_id', $key)->whereIn('coupon_detail_status', ['INIT', 'PENDING'])->count();
+                            // 超出优惠券设置的数量
+                            if ($couponResidue < $coupon['num']) {
+                                continue;
+                            }
+                        }
+                        $endDate = '';
+                        if (!empty($couponModel->coupon_validdate_end)) {
+                            $endDate = $couponModel->coupon_validdate_end;
+                        } elseif ($couponModel->coupon_validdate_day > 0) {
+                            $endDate = date('Y-m-d H:i:s', time() + ($couponModel->coupon_validdate_day * 24 * 3600) - 1);
+                        }
+                        // 发券参数
+                        $couponSendParams = [
+                            'gettype' => $gettype,
+                            'coupon_id' => $key,
+                            'chooseCouponNbr' => $coupon['num'],
+                            'member_id' => $params['member_id'],
+                            'coupon_detail_gain_datetime' => date('Y-m-d H:i:s'),
+                            'coupon_detail_deadline_datetime' => $endDate,
+                        ];
+
+                        Log::info("发普通券参数", $couponSendParams);
+                        if ($couponResidue != 'all' && $couponResidue - $coupon['num'] >= 0) {
+                            CouponDetailService::customSendCouponHave($couponSendParams);
+                        } else {
+                            for ($i = 0; $i < $coupon['num']; $i++) {
+                                CouponDetailService::customSendCoupon($couponSendParams);
+                            }
+                        }
+                    }
+                }
+            }
+        }catch (\Exception $e){
+            Log::info("优惠券发放失败:".$e->getMessage());
+            throw new BusinessException("优惠券发放失败");
+        }
+    }
 }

+ 189 - 5
app/admin/service/goods/GoodsService.php

@@ -182,9 +182,12 @@ class GoodsService
             },
             'user' => function ($query) {
                 $query->select('user_id', 'user_name');
-            }
+            },
+            'updateUser' => function ($query) {
+                $query->select('user_id', 'user_name');
+            },
         ])->leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
-            ->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_attribute_json', 'goods_addtimes', 'goods_updatetimes')
+            ->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id','updator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_attribute_json', 'goods_addtimes', 'goods_updatetimes')
             ->when($goodsName != '', function ($query) use ($goodsName) {
                 $query->where(function ($q) use ($goodsName) {
                     $q->where('goods_name', 'like', '%' . $goodsName . '%');
@@ -283,8 +286,11 @@ class GoodsService
             },
             'user' => function ($query) {
                 $query->select('user_id', 'user_name');
+            },
+            'updateUser' => function ($query) {
+                $query->select('user_id', 'user_name');
             }
-        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id','updator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
             ->when($goodsName != '', function ($query) use ($goodsName) {
                 $query->where(function ($q) use ($goodsName) {
                     $q->where('goods_name', 'like', '%' . $goodsName . '%');
@@ -347,8 +353,11 @@ class GoodsService
             },
             'user' => function ($query) {
                 $query->select('user_id', 'user_name');
+            },
+            'updateUser' => function ($query) {
+                $query->select('user_id', 'user_name');
             }
-        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id','updator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
             ->when($goodsName != '', function ($query) use ($goodsName) {
                 $query->where(function ($q) use ($goodsName) {
                     $q->where('goods_name', 'like', '%' . $goodsName . '%')
@@ -397,8 +406,11 @@ class GoodsService
             },
             'user' => function ($query) {
                 $query->select('user_id', 'user_name');
+            },
+            'updateUser' => function ($query) {
+                $query->select('user_id', 'user_name');
             }
-        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id','updator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
             ->when($goodsName != '', function ($query) use ($goodsName) {
                 $query->where(function ($q) use ($goodsName) {
                     $q->where('goods_name', 'like', '%' . $goodsName . '%')
@@ -820,6 +832,10 @@ class GoodsService
             if (!empty($data['creator_user_id'])) {
                 $data['creator_username'] = SysUser::where('user_id', $data['creator_user_id'])->value('user_name');
             }
+            $data['updator_username'] = '';
+            if (!empty($data['updator_user_id'])) {
+                $data['updator_username'] = SysUser::where('user_id', $data['updator_user_id'])->value('user_name');
+            }
 
             if (!empty($data['goods_detail_slider_json'])) {
                 $data['goods_detail_slider_json'] = json_decode($data['goods_detail_slider_json'], true);
@@ -990,6 +1006,171 @@ class GoodsService
         }
     }
 
+
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param $goodsId
+     * @return Response
+     */
+    public static function newCustomerInfo($goodsId)
+    {
+        try {
+            // 商品主表
+            $main = Goods::with('category')->where('goods_id', $goodsId)->first();
+            if (!empty($main)) {
+                $main = $main->toArray();
+                $main['goods_sku_json'] = json_decode($main['goods_sku_json'], true);
+                $main['specList'] = [];
+                foreach ($main['goods_sku_json'] as $key => $sku) {
+                    $main['specList'][] = [
+                        'label' => $key,
+                        'tags' => $sku
+                    ];
+                }
+
+            } else {
+                $main = [];
+            }
+            // 详情表
+            $detail = GoodsDetail::where('join_detail_goods_id', $goodsId)->first();
+            if (!empty($detail)) {
+                $detail = $detail->toArray();
+            } else {
+                $detail = [];
+            }
+            // 标签表
+            $label = GoodsLabel::where('join_label_goods_id', $goodsId)->first();
+            if (!empty($label)) {
+                $label = $label->toArray();
+            } else {
+                $label = [];
+            }
+            // Running表
+            $running = GoodsRunning::where('join_running_goods_id', $goodsId)->first();
+            if (!empty($running)) {
+                $running = $running->toArray();
+                if (!empty($running['goods_running_off_json']) && is_json($running['goods_running_off_json'])) {
+                    $goodsRunningOffJson = json_decode($running['goods_running_off_json'], true);
+                    $running['goods_off_addtimes'] = isset($goodsRunningOffJson['time']) ? date("Y-m-d H:i", $goodsRunningOffJson['time']) : '';
+                }
+                $running['goods_running_storage'] = !empty($running['goods_running_storage']) ? intval($running['goods_running_storage']) : '';
+                $running['goods_running_sale'] = !empty($running['goods_running_sale']) ? intval($running['goods_running_sale']) : '';
+            } else {
+                $running = [];
+            }
+            // Sku表
+            $skus = GoodsSku::where('join_sku_goods_id', $goodsId)->where('goods_sku_status','ON')->get();
+            if (!empty($skus)) {
+                $skus = $skus->toArray();
+                $submitList = [];
+                foreach ($skus as $key => $sku) {
+                    $skuSpecsJson = json_decode($sku['goods_sku_specs_json'], true);
+                    $skuSpecs = '';
+                    $skuNameValue = [];
+                    $i = 1;
+                    foreach ($skuSpecsJson as $k => $item) {
+                        if (is_array($item)) {
+                            $item = implode(',', $item);
+                        }
+                        $skuSpecs = $skuSpecs . $item . ',';
+                        $skuNameKey = 'skuName' . $i;
+                        $skuValueKey = 'skuValue' . $i;
+                        $skuNameValue[$skuNameKey] = $k;
+                        $skuNameValue[$skuValueKey] = $item;
+                        $skuNameValue[$k] = $item;
+                        $i++;
+                    }
+                    $storage = json_decode($sku['goods_sku_storage_json'], true);
+                    $priceStorage = [
+                        'sku_id' => $sku['goods_sku_id'],
+                        'sku' => rtrim($skuSpecs, ',') ?? '',
+                        'stock' => $storage['storage'] ?? '',
+                        'price' => $sku['goods_sku_sales_price'] ?? '',
+                    ];
+
+                    $submitList[] = array_merge($skuNameValue, $priceStorage);
+                }
+                $skus['submitList'] = $submitList;
+            } else {
+                $skus = [];
+            }
+
+            // 合并数据
+            $data = array_merge($main, $detail, $label, $running, ['sku' => $skus]);
+            $data['goods_sku_json_label'] = [];
+
+            $data['goods_label'] = !empty($data['goods_label']) ? explode(',', $data['goods_label']) : [];
+            $data['goods_json'] = $data['goods_json'] ? json_decode($data['goods_json'], true) : [];
+            $data['goods_cover'] = getenv('STORAGE_DOMAIN') . $data['goods_cover'];
+            if (!empty($data['goods_category']) && $data['goods_category'] == 'INDEX') {
+                $data['goods_recommend_index'] = 'INDEX';
+            }
+
+            // 创建者
+            $data['creator_username'] = '';
+            if (!empty($data['creator_user_id'])) {
+                $data['creator_username'] = SysUser::where('user_id', $data['creator_user_id'])->value('user_name');
+            }
+
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = json_decode($data['goods_detail_slider_json'], true);
+                // ……
+                if (isset($data['goods_detail_slider_json']['slider'])) {
+                    $data['goods_detail_slider_json'] = explode(',', $data['goods_detail_slider_json']['slider']);
+                }
+
+                $slider = '';
+                foreach ($data['goods_detail_slider_json'] as $item) {
+                    $slider .= getenv('STORAGE_DOMAIN') . $item . ',';
+                }
+                $data['goods_detail_slider_json'] = rtrim($slider, ',');
+            }
+            $extendJson = [];
+            if (!empty($data['goods_attribute_json'])) {
+                $extendJson = json_decode($data['goods_attribute_json'], true);
+                $data['goods_attribute_json'] = $extendJson;
+            }
+            if (!empty($extendJson['coupon'])) {
+                $data['coupon_list'] = $extendJson['coupon'];
+            }
+
+            $data['express_json'] = [];
+            if (!empty($data['goods_express_json'])) {
+                $goodsExpressJson = json_decode($data['goods_express_json'], true);
+                if (isset($goodsExpressJson['express']) && $goodsExpressJson['express'] == 'Y') {
+                    $data['express_json'][] = 'express';
+                }
+                if (isset($goodsExpressJson['self']) && $goodsExpressJson['self'] == 'Y') {
+                    $data['express_json'][] = 'self';
+                }
+                if (isset($goodsExpressJson['arrival']) && $goodsExpressJson['arrival'] == 'Y') {
+                    $data['express_json'][] = 'arrival';
+                }
+            }
+            // 详情表数据
+            if (!empty($data['goods_detail_specs_json'])) {
+                $specsJson = json_decode($data['goods_detail_specs_json'], true);
+                foreach ($specsJson as $itemSpecsJson) {
+                    if (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课时') {
+                        $data['curriculum']['period'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '群体') {
+                        $data['curriculum']['group'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课程') {
+                        $data['curriculum']['course'] = $itemSpecsJson['val'];
+                    }
+                }
+            }
+
+            return json_success('', $data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            return json_fail("查询错误~");
+        }
+    }
+
     public static function infoPackage($goodsId)
     {
         try {
@@ -1223,6 +1404,8 @@ class GoodsService
             $goods->goods_status = $params['goods_status'];
             $goods->goods_sku_json = json_encode(['规格' => [$params['goods_sales_price'] . '元']]);
             $goods->goods_sort = $params['goods_sort'];
+            $goods->goods_updatetimes = time();
+            $goods->updator_user_id = JwtToken::getCurrentId();
             if (!empty($goods->goods_attribute_json)) {
                 $attributeJson = json_decode($goods->goods_attribute_json, true);
                 $attributeJson['added']['nbr'] = $params['goods_rate'] / 100;
@@ -2261,6 +2444,7 @@ class GoodsService
                 $row->{$key} = $val;
             }
             $row->goods_updatetimes = time();
+            $row->updator_user_id = JwtToken::getCurrentId();
             $row->save();
         } catch (BusinessException $e) {
             throw new BusinessException($e->getMessage());

+ 24 - 0
app/admin/service/goods/GoodsSkuService.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace app\admin\service\goods;
+
+class GoodsSkuService
+{
+    public static function getSkuTitle($json)
+    {
+        if (is_json($json)) {
+            $json = json_decode($json, true);
+        }
+
+        $skuName = '';
+        foreach ($json as $specsKey => $skuSpecs) {
+            if (is_array($skuSpecs)) {
+                $skuName = $skuName . ' ' . implode(' ', $skuSpecs) . ';';
+            } else {
+                $skuName = $skuName . ' ' . $skuSpecs . ';';
+            }
+        }
+        return rtrim($skuName, ';');
+    }
+
+}

+ 2119 - 0
app/admin/service/goods/NewCustomerService.php

@@ -0,0 +1,2119 @@
+<?php
+
+namespace app\admin\service\goods;
+
+use app\common\Tree;
+use app\model\Appointment;
+use app\model\Coupon;
+use app\model\Goods;
+use app\model\GoodsComponent;
+use app\model\GoodsDetail;
+use app\model\GoodsLabel;
+use app\model\GoodsRunning;
+use app\model\GoodsSku;
+use app\model\OrderSheet;
+use app\model\Supplier;
+use app\model\SysCategory;
+use app\model\SysDept;
+use app\model\SysSerial;
+use app\model\SysUser;
+use support\Db;
+use support\exception\BusinessException;
+use support\Redis;
+use support\Request;
+use support\Response;
+use Tinywan\Jwt\JwtToken;
+
+class NewCustomerService
+{
+    public static function selectAll($goodsIds)
+    {
+        $goods = Goods::where('goods_status', 'ON')
+            ->when($goodsIds != '', function ($query) use ($goodsIds) {
+                $query->whereIn('goods_id', $goodsIds);
+            })
+            ->select('goods_id', 'goods_name')
+            ->get();
+
+        return json_success('', $goods);
+    }
+
+    public static function selectAllByGoodsName($goodsName)
+    {
+        $goods = Goods::with('sku')
+            ->where('goods_status', 'ON')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where('goods_name', 'like', "%" . $goodsName . '%');
+            })
+            ->select('goods_id', 'goods_name', 'goods_classify')
+            ->get()
+            ->toArray();
+        foreach ($goods as &$item) {
+            if (!empty($item['sku'])) {
+                foreach ($item['sku'] as $key => $sku) {
+                    $specsJson = json_decode($sku['goods_sku_specs_json'], true);
+                    $skuTitle = '';
+                    foreach ($specsJson as $item2) {
+                        if (is_array($item2)) {
+                            $item2 = implode(',', $item2);
+                        }
+                        $skuTitle .= $item2 . '-';
+                    }
+                    $item['sku'][$key]['goods_sku_id'] = strval($item['sku'][$key]['goods_sku_id']);
+                    $item['sku'][$key]['goods_sku_title'] = rtrim($skuTitle, '-');
+                }
+            }
+        }
+
+        return json_success('', $goods);
+    }
+
+    public static function selectAllByCategoryForRuleAddComponent($category = "GOODS")
+    {
+        $categoryIds = [];
+        $categorySuperIds = [];
+        if ($category == 'GOODS') {
+            $categorySuperIds = [5];
+        } elseif ($category == 'SERVICE') {
+            $categorySuperIds = [31, 154, 42, 65, 30, 66, 72, 70];
+        }
+        $categorys = SysCategory::whereIn('category_id', $categorySuperIds)->get()->toArray();
+        foreach ($categorys as $item) {
+            if (empty($item['category_super_path'])) {
+                $item['category_super_path'] = '#' . $item['category_id'] . '#';
+            } else {
+                $item['category_super_path'] = $item['category_super_path'] . '#' . $item['category_id'] . '#';
+            }
+
+            $categoryIds = SysCategory::where('category_super_path', 'like', '%' . $item['category_super_path'] . '%')->pluck('category_id');
+            $categoryIds = $categoryIds ? $categoryIds->toArray() : [];
+            $categorySuperIds = array_merge($categorySuperIds, $categoryIds);
+        }
+
+        $categoryIds = array_unique($categorySuperIds);
+
+        $goods = Goods::with('sku')
+//            ->where('goods_classify', $category)
+            ->when(!empty($categoryIds), function ($query) use ($categoryIds) {
+                $query->whereIn('join_goods_category_id', $categoryIds);
+            })->when(empty($categoryIds), function ($query) {
+                $query->where('goods_classify', '<>', 'RECHARGE');
+            })->where('goods_status', 'ON')
+            ->select('goods_id', 'goods_name', 'join_goods_category_id')
+            ->get()
+            ->toArray();
+
+        foreach ($goods as &$good) {
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $specsJson = json_decode($sku['goods_sku_specs_json'], true);
+                        $skuTitle = '';
+                        foreach ($specsJson as $item) {
+                            if (is_array($item)) {
+                                $item = implode(',', $item);
+                            }
+                            $skuTitle .= $item . '-';
+                        }
+                        $good['sku'][$key]['goods_sku_id'] = strval($good['sku'][$key]['goods_sku_id']);
+                        $good['sku'][$key]['goods_sku_title'] = rtrim($skuTitle, '-');
+                    }
+                    unset($good['sku'][$key]['goods_sku_specs_json'], $good['sku'][$key]['join_sku_goods_id']);
+                }
+            } else {
+                $good['sku'] = [];
+            }
+
+        }
+
+        return json_success('', $goods);
+    }
+
+    public static function selectPremisesByGoodsId($goodsId)
+    {
+        $goods = Goods::where('goods_id', $goodsId)
+            ->select('goods_id', 'goods_attribute_json')
+            ->first();
+        $premisses = [];
+        if (!empty($goods->goods_attribute_json)) {
+            $attributeJson = json_decode($goods->goods_attribute_json, true);
+            if (isset($attributeJson['premisses'])) {
+                $premisses = SysDept::whereIn('dept_id', $attributeJson['premisses'])
+                    ->select('dept_id', 'dept_name')
+                    ->get()
+                    ->toArray();
+            }
+        }
+
+        return json_success('', $premisses);
+    }
+
+    public static function select(Request $request, $classify = "GOODS")
+    {
+        $page = $request->get('page', 1);
+        $pageSize = $request->get('pageSize', 20);
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+        $goodsCategory = $request->get('goods_category', null);
+        $goodsSupplierId = $request->get('join_goods_supplier_id', null);
+        $goodsStatus = $request->get('goods_status', null);
+        $type = $request->get('type', '');
+        if ($categoryId != null) {
+            $categoryPath = SysCategory::where('category_id', $categoryId)->value('category_super_path');
+            $categoryPath .= '#' . $categoryId . '#';
+            $categoryIds = SysCategory::where('category_super_path', 'like', $categoryPath . '%')->pluck('category_id')->toArray();
+            $categoryIds[] = intval($categoryId);
+            if (!empty($categoryIds)) {
+                $categoryId = $categoryIds;
+            } else {
+                $categoryId = [intval($categoryId)];
+            }
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_attribute_json', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->whereIn('join_goods_category_id', $categoryId);
+            })->when($goodsCategory != null, function ($query) use ($goodsCategory) {
+                $query->where('goods_category', $goodsCategory);
+            })
+            ->when($classify != '', function ($query) use ($classify, $categoryId) {
+                if ($classify == 'GOODS' && empty($categoryId)) {
+                    $query->whereIn('join_goods_category_id', ['6', '7', '8', '9', '10', '11', '12', '30']);
+                } else if ($classify != 'GOODS' && empty($categoryId)) {
+                    $query->where('goods_classify', $classify);
+                }
+            })->when(!empty($type), function ($query) use ($type) {
+                if ($type == 'storageWarning') {
+                    $query->where('goods_running.goods_running_storage', '<=', 2);
+                }
+            })->when(!empty($goodsSupplierId), function ($query) use ($goodsSupplierId) {
+                $query->where('join_goods_supplier_id', $goodsSupplierId);
+            })->when(!empty($goodsStatus), function ($query) use ($goodsStatus) {
+                $query->where('goods_status', $goodsStatus);
+            })
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+
+        $total = Goods::leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->whereIn('join_goods_category_id', $categoryId);
+            })->when($goodsCategory != null, function ($query) use ($goodsCategory) {
+                $query->where('goods_category', $goodsCategory);
+            })->when($classify != '', function ($query) use ($classify, $categoryId) {
+                if ($classify == 'GOODS' && empty($categoryId)) {
+                    $query->whereIn('join_goods_category_id', ['6', '7', '8', '9', '10', '11', '12', '30']);
+                } else if ($classify != 'GOODS' && empty($categoryId)) {
+                    $query->where('goods_classify', $classify);
+                }
+            })->when(!empty($type), function ($query) use ($type) {
+                if ($type == 'storageWarning') {
+                    $query->where('goods_running.goods_running_storage', '<=', 2);
+                }
+            })->when(!empty($goodsSupplierId), function ($query) use ($goodsSupplierId) {
+                $query->where('join_goods_supplier_id', $goodsSupplierId);
+            })->when(!empty($goodsStatus), function ($query) use ($goodsStatus) {
+                $query->where('goods_status', $goodsStatus);
+            })
+            ->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+            if (!empty($row['goods_attribute_json'])) {
+                $row['goods_attribute_json'] = json_decode($row['goods_attribute_json']);
+            }
+            if (!empty($row['goods_category']) && $row['goods_category'] == 'INDEX') {
+                $row['goods_recommend_index'] = 'INDEX';
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectSpecial(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+        if ($categoryId == null) {
+            $categoryId = [65, 43];
+        } elseif (is_string($categoryId)) {
+            $categoryId = [$categoryId];
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->whereIn('join_goods_category_id', $categoryId)
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->whereIn('join_goods_category_id', $categoryId)->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectPicking(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+
+        $categorySuperId = $request->get('category_super_id', '');
+        $categoryIds = $request->get('join_goods_category_id', []);
+        if (!empty($categorySuperId) && is_array($categoryIds)) {
+            $category = SysCategory::where('category_id', $categorySuperId)->first();
+            if (empty($category->category_super_path)) {
+                $category->category_super_path = '#' . $categorySuperId . '#';
+            } else {
+                $category->category_super_path = $category->category_super_path . '#' . $categorySuperId . '#';
+            }
+            $categoryIds = SysCategory::where('category_super_path', 'like', '%' . $category->category_super_path)->pluck('category_id');
+            $categoryIds = $categoryIds ? $categoryIds->toArray() : [];
+            $categoryIds = array_merge($categoryIds, [$categorySuperId]);
+        } elseif (!is_array($categoryIds)) {
+            $categoryIds = [$categoryIds];
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->whereIn('join_goods_category_id', $categoryIds)
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->whereIn('join_goods_category_id', $categoryIds)
+            ->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectPackage(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->where('join_goods_category_id', $categoryId);
+            })
+            ->where('goods_classify', 'PACKAGE')
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->when($categoryId != null, function ($query) use ($categoryId) {
+            $query->where('join_goods_category_id', $categoryId);
+        })->where('goods_classify', 'PACKAGE')->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+//            if (!empty($row['component'])) {
+//                $ids = [];
+//                $contentList = [];
+//                foreach ($row['component'] as $component) {
+//                    $ids[] = $component['join_component_goods_id'];
+//                    $configJson = json_decode($component['goods_component_config_json'], true);
+//                    $contentList[] = [
+//                        'goods_name' => $configJson['goods_name'] ?? '',
+//                        'goods_sales_price' => $component['goods_component_price'],
+//                        'nbr' => $configJson['nbr'] ?? 0
+//                    ];
+//                }
+//
+//                $row['join_component_goods_id'] = $ids;
+//                $row['goodsContentList'] = $contentList;
+//            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    /**
+     * @Desc 下拉选择服务商品
+     * @Author Gorden
+     * @Date 2024/4/24 13:32
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public static function selectList(Request $request, $goodsClassify = "SERVICE")
+    {
+        $keywords = $request->get('keywords', '');
+        $isSupportAppointment = $request->get('is_support_appointment', '');
+//        if (!$keywords){
+//            return json_success('暂无数据');
+//        }
+
+//        $categoryIds = SysCategory::whereIn('category_super_id', [5, 31, 32, 42, 66, 70, 72])->pluck('category_id');
+
+        $goods = Goods::with('sku')
+//            ->whereIn('join_goods_category_id', $categoryIds)
+            ->when($keywords != '', function ($query) use ($keywords) {
+                $query->where('goods_name', 'like', "%" . $keywords . "%");
+            })
+            ->when($goodsClassify != '', function ($query) use ($goodsClassify) {
+                if ($goodsClassify == 'NOPACKAGE') {
+                    $query->where('goods_classify', '<>', 'PACKAGE');
+                } else if ($goodsClassify == 'SERVICE') {
+                    $query->whereIn('goods_classify', ['SERVICE', 'CHNMED', 'CHNNCD']);
+                } else {
+                    $query->where('goods_classify', $goodsClassify);
+                }
+
+            })
+            ->when($isSupportAppointment != '', function ($query) use ($isSupportAppointment) {
+                $query->where('is_support_appointment', $isSupportAppointment);
+            })
+            ->select('goods_id', 'goods_name', 'goods_sales_price', 'join_goods_category_id', 'goods_attribute_json')
+            ->get()
+            ->toArray();
+
+        foreach ($goods as &$good) {
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $good['sku'][$key]['goods_sku_specs_json'] = json_decode($sku['goods_sku_specs_json']);
+
+                        $skuName = '';
+                        foreach ($good['sku'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . ' ' . $specsKey . ':' . implode(' ', $skuSpecs) . ';';
+                            } else {
+                                $skuName = $skuName . ' ' . $specsKey . ':' . $skuSpecs . ';';
+                            }
+                        }
+                        $good['sku'][$key]['sku_name'] = $skuName;
+                    }
+                }
+            } else {
+                $good['sku'] = [];
+            }
+            $good['premisses'] = [];
+            if (!empty($good['goods_attribute_json'])) {
+                $attributeJson = json_decode($good['goods_attribute_json'], true);
+                if (isset($attributeJson['premisses'])) {
+                    $premisses = SysDept::when(!empty($attributeJson['premisses']), function ($query) use ($attributeJson) {
+                        $query->whereIn('dept_id', $attributeJson['premisses']);
+                    })->where('dept_category', '营业场所')
+                        ->select('dept_id', 'dept_name')
+                        ->get();
+                    $good['premisses'] = $premisses;
+                }
+            }
+        }
+
+        return json_success('', $goods);
+
+    }
+
+    public static function selectCascaderList(Request $request)
+    {
+        $categoryIds = $request->get('category_id', '');
+        $categorySuperId = $request->get('category_super_id', '');
+        $joinGoodsCategoryId = $request->get('join_goods_category_id', '');
+        $type = $request->get('type', '');
+        if (!$categoryIds && !$categorySuperId && !$joinGoodsCategoryId) {
+            return json_fail('参数异常');
+        }
+        $data = [];
+        $categorys = [];
+        if (!empty($categoryIds)) {
+            $categorys = SysCategory::whereIn('category_id', $categoryIds)
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $categorys);
+        } else if (!empty($categorySuperId)) {
+            $categorys = SysCategory::where('category_super_id', $categorySuperId)
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $categorys);
+        }
+
+        foreach ($categorys as $category) {
+            // if(empty($category['category_super_path'])){
+            $category['category_super_path'] = '#' . $category['id'] . '#';
+            // }
+            $subCategory = SysCategory::where('category_super_path', 'like', '%' . $category['category_super_path'] . '%')
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $subCategory);
+        }
+        if (empty($data) && !empty($joinGoodsCategoryId)) {
+            $goodsCategoryIds = $joinGoodsCategoryId;
+        } else {
+            $goodsCategoryIds = array_column($data, 'id');
+        }
+        $goods = Goods::with([
+            'sku' => function($query){
+                $query->where('goods_sku_status','ON');
+            }
+        ])
+            ->leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->where('goods_running.goods_running_storage', '>', 0)
+            ->whereIn('join_goods_category_id', $goodsCategoryIds)
+            ->where('goods_status', 'ON');
+
+        if ($type == 'dishes') {
+            $uid = JwtToken::getCurrentId();
+            $user = SysUser::where('user_id', $uid)->first();
+            $restaurant = SysDept::where('dept_category', '餐厅')->where(function ($query) use ($user) {
+                $query->where('dept_id', $user->join_user_dept_id)->orWhere('dept_super_id', $user->join_user_dept_id);
+            })->first();
+            $supplier = Supplier::where('join_supplier_dept_id', $restaurant->dept_id)->first();
+            if ($supplier) {
+                $goods = $goods->where('join_goods_supplier_id', $supplier->supplier_id);
+            }
+        }
+
+
+        $goods = $goods->select('goods_id', 'goods_id as id', 'goods_name as name', 'join_goods_category_id as pid', 'goods_attribute_json', 'goods_classify', 'goods_sales_price', 'goods_cover', 'goods_running.goods_running_storage')
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->get()
+            ->toArray();
+        foreach ($goods as &$good) {
+            $good['goods_cover'] = getenv('STORAGE_DOMAIN') . $good['goods_cover'];
+            $good['goods_running_storage'] = intval($good['goods_running_storage']);
+            $good['nbr'] = 0;
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $good['sku'][$key]['goods_sku_specs_json'] = json_decode($sku['goods_sku_specs_json']);
+
+                        $skuName = '';
+                        foreach ($good['sku'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                            $keyStr = ($specsKey == '规格') ? '' : ($specsKey . ':');
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . $keyStr . ' ' . implode(' ', $skuSpecs) . '; ';
+                            } else {
+                                $skuName = $skuName . $keyStr . ' ' . $skuSpecs . '; ';
+                            }
+                        }
+                        $good['sku'][$key]['sku_name'] = rtrim($skuName, '; ');
+                    }
+                }
+            } else {
+                $good['sku'] = [];
+            }
+            $good['premisses'] = [];
+            if (!empty($good['goods_attribute_json'])) {
+                $attributeJson = json_decode($good['goods_attribute_json'], true);
+                if (isset($attributeJson['premisses'])) {
+                    $premisses = SysDept::when(!empty($attributeJson['premisses']), function ($query) use ($attributeJson) {
+                        $query->whereIn('dept_id', $attributeJson['premisses']);
+                    })->where('dept_category', '营业场所')
+                        ->select('dept_id', 'dept_name')
+                        ->get();
+                    $good['premisses'] = $premisses;
+                }
+            }
+        }
+
+        $data = array_merge($data, $goods);
+        $tree = new Tree($data);
+        $cascaderData = $tree->getTree();
+
+        foreach ($cascaderData as $key1 => $cascader1) {
+            if (isset($cascader1['children'])) {
+                foreach ($cascader1['children'] as $key2 => $cascader2) {
+                    if (isset($cascader2['children'])) {
+                        foreach ($cascader2['children'] as $key3 => $cascader3) {
+                            if (isset($cascader3['children'])) {
+                                foreach ($cascader3['children'] as $key4 => $cascader4) {
+                                    if (!isset($cascader4['goods_id'])) {
+                                        unset($cascaderData[$key1]['children'][$key2]['children'][$key3]['children'][$key4]);
+                                    }
+                                    if (isset($cascader4['goods_id']) && empty($cascader4['sku'])){
+                                        unset($cascaderData[$key1]['children'][$key2]['children'][$key3]['children'][$key4]);
+                                    }
+                                }
+                            } else if (!isset($cascader3['goods_id'])) {
+                                unset($cascaderData[$key1]['children'][$key2]['children'][$key3]);
+                            }
+                            if (isset($cascader3['goods_id']) && empty($cascader3['sku'])){
+                                unset($cascaderData[$key1]['children'][$key2]['children'][$key3]);
+                                continue;
+                            }
+                            if (isset($cascader3['children']) && count($cascaderData[$key1]['children'][$key2]['children']) == 0) {
+                                unset($cascaderData[$key1]['children'][$key2]);
+                            }
+                            if (isset($cascader3['children']) && count($cascaderData[$key1]['children'][$key2]['children'][$key3]['children']) > 0) {
+                                $cascaderData[$key1]['children'][$key2]['children'][$key3]['children'] = array_values($cascaderData[$key1]['children'][$key2]['children'][$key3]['children']);
+                            }
+                        }
+                    } else if (!isset($cascader2['goods_id'])) {
+                        unset($cascaderData[$key1]['children'][$key2]);
+                    }
+                    if (isset($cascader2['goods_id']) && empty($cascader2['sku'])){
+                        unset($cascaderData[$key1]['children'][$key2]);
+                        continue;
+                    }
+                    if (isset($cascader2['children']) && count($cascaderData[$key1]['children'][$key2]['children']) == 0) {
+                        unset($cascaderData[$key1]['children'][$key2]);
+                    }
+                    if (isset($cascader2['children']) && isset($cascaderData[$key1]['children'][$key2]) && count($cascaderData[$key1]['children'][$key2]['children']) > 0) {
+                        $cascaderData[$key1]['children'][$key2]['children'] = array_values($cascaderData[$key1]['children'][$key2]['children']);
+                    }
+                }
+            } else if (!isset($cascader1['goods_id'])) {
+                unset($cascaderData[$key1]);
+            }
+            if (isset($cascader1['goods_id']) && empty($cascader1['sku'])){
+                unset($cascaderData[$key1]);
+                continue;
+            }
+            if (isset($cascader1['children']) && count($cascaderData[$key1]['children']) == 0) {
+
+                unset($cascaderData[$key1]);
+            }
+            if (isset($cascader1['children']) && isset($cascaderData[$key1]) && count($cascaderData[$key1]['children']) > 0) {
+                $cascaderData[$key1]['children'] = array_values($cascaderData[$key1]['children']);
+            }
+        }
+        $cascaderData = array_values($cascaderData);
+
+        return json_success('success', $cascaderData);
+    }
+
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param $goodsId
+     * @return Response
+     */
+    public static function info($goodsId)
+    {
+        try {
+            // 商品主表
+            $main = Goods::with('category')->where('goods_id', $goodsId)->first();
+            if (!empty($main)) {
+                $main = $main->toArray();
+                $main['goods_sku_json'] = json_decode($main['goods_sku_json'], true);
+                $main['specList'] = [];
+                foreach ($main['goods_sku_json'] as $key => $sku) {
+                    $main['specList'][] = [
+                        'label' => $key,
+                        'tags' => $sku
+                    ];
+                }
+
+            } else {
+                $main = [];
+            }
+            // 详情表
+            $detail = GoodsDetail::where('join_detail_goods_id', $goodsId)->first();
+            if (!empty($detail)) {
+                $detail = $detail->toArray();
+            } else {
+                $detail = [];
+            }
+            // 标签表
+            $label = GoodsLabel::where('join_label_goods_id', $goodsId)->first();
+            if (!empty($label)) {
+                $label = $label->toArray();
+            } else {
+                $label = [];
+            }
+            // Running表
+            $running = GoodsRunning::where('join_running_goods_id', $goodsId)->first();
+            if (!empty($running)) {
+                $running = $running->toArray();
+                if (!empty($running['goods_running_off_json']) && is_json($running['goods_running_off_json'])) {
+                    $goodsRunningOffJson = json_decode($running['goods_running_off_json'], true);
+                    $running['goods_off_addtimes'] = isset($goodsRunningOffJson['time']) ? date("Y-m-d H:i", $goodsRunningOffJson['time']) : '';
+                }
+                $running['goods_running_storage'] = !empty($running['goods_running_storage']) ? intval($running['goods_running_storage']) : '';
+                $running['goods_running_sale'] = !empty($running['goods_running_sale']) ? intval($running['goods_running_sale']) : '';
+            } else {
+                $running = [];
+            }
+            // Sku表
+            $skus = GoodsSku::where('join_sku_goods_id', $goodsId)->where('goods_sku_status','ON')->get();
+            if (!empty($skus)) {
+                $skus = $skus->toArray();
+                $submitList = [];
+                foreach ($skus as $key => $sku) {
+                    $skuSpecsJson = json_decode($sku['goods_sku_specs_json'], true);
+                    $skuSpecs = '';
+                    $skuNameValue = [];
+                    $i = 1;
+                    foreach ($skuSpecsJson as $k => $item) {
+                        if (is_array($item)) {
+                            $item = implode(',', $item);
+                        }
+                        $skuSpecs = $skuSpecs . $item . ',';
+                        $skuNameKey = 'skuName' . $i;
+                        $skuValueKey = 'skuValue' . $i;
+                        $skuNameValue[$skuNameKey] = $k;
+                        $skuNameValue[$skuValueKey] = $item;
+                        $skuNameValue[$k] = $item;
+                        $i++;
+                    }
+                    $storage = json_decode($sku['goods_sku_storage_json'], true);
+                    $priceStorage = [
+                        'sku_id' => $sku['goods_sku_id'],
+                        'sku' => rtrim($skuSpecs, ',') ?? '',
+                        'stock' => $storage['storage'] ?? '',
+                        'price' => $sku['goods_sku_sales_price'] ?? '',
+                    ];
+
+                    $submitList[] = array_merge($skuNameValue, $priceStorage);
+                }
+                $skus['submitList'] = $submitList;
+            } else {
+                $skus = [];
+            }
+
+            // 合并数据
+            $data = array_merge($main, $detail, $label, $running, ['sku' => $skus]);
+            $data['goods_sku_json_label'] = [];
+
+            $data['goods_label'] = !empty($data['goods_label']) ? explode(',', $data['goods_label']) : [];
+            $data['goods_json'] = $data['goods_json'] ? json_decode($data['goods_json'], true) : [];
+            $data['goods_cover'] = getenv('STORAGE_DOMAIN') . $data['goods_cover'];
+            if (!empty($data['goods_category']) && $data['goods_category'] == 'INDEX') {
+                $data['goods_recommend_index'] = 'INDEX';
+            }
+
+            // 创建者
+            $data['creator_username'] = '';
+            if (!empty($data['creator_user_id'])) {
+                $data['creator_username'] = SysUser::where('user_id', $data['creator_user_id'])->value('user_name');
+            }
+
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = json_decode($data['goods_detail_slider_json'], true);
+                // ……
+                if (isset($data['goods_detail_slider_json']['slider'])) {
+                    $data['goods_detail_slider_json'] = explode(',', $data['goods_detail_slider_json']['slider']);
+                }
+
+                $slider = '';
+                foreach ($data['goods_detail_slider_json'] as $item) {
+                    $slider .= getenv('STORAGE_DOMAIN') . $item . ',';
+                }
+                $data['goods_detail_slider_json'] = rtrim($slider, ',');
+            }
+            $extendJson = [];
+            if (!empty($data['goods_attribute_json'])) {
+                $extendJson = json_decode($data['goods_attribute_json'], true);
+                $data['goods_attribute_json'] = $extendJson;
+            }
+            if (!empty($extendJson['coupon'])) {
+                $data['coupon_list'] = $extendJson['coupon'];
+            }
+
+            $data['express_json'] = [];
+            if (!empty($data['goods_express_json'])) {
+                $goodsExpressJson = json_decode($data['goods_express_json'], true);
+                if (isset($goodsExpressJson['express']) && $goodsExpressJson['express'] == 'Y') {
+                    $data['express_json'][] = 'express';
+                }
+                if (isset($goodsExpressJson['self']) && $goodsExpressJson['self'] == 'Y') {
+                    $data['express_json'][] = 'self';
+                }
+                if (isset($goodsExpressJson['arrival']) && $goodsExpressJson['arrival'] == 'Y') {
+                    $data['express_json'][] = 'arrival';
+                }
+            }
+            // 详情表数据
+            if (!empty($data['goods_detail_specs_json'])) {
+                $specsJson = json_decode($data['goods_detail_specs_json'], true);
+                foreach ($specsJson as $itemSpecsJson) {
+                    if (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课时') {
+                        $data['curriculum']['period'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '群体') {
+                        $data['curriculum']['group'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课程') {
+                        $data['curriculum']['course'] = $itemSpecsJson['val'];
+                    }
+                }
+            }
+
+            return json_success('', $data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            return json_fail("查询错误~");
+        }
+    }
+
+    /**
+     * @Desc 添加商品
+     * @Author Gorden
+     * @Date 2024/3/11 10:20
+     *
+     * @param $params
+     * @return Response
+     */
+    public static function insert($params): Response
+    {
+        Db::beginTransaction();
+        try {
+            $params['goods_id'] = "GD" . date('ymdHi') . random_string(4, 'up');
+            // 主表
+            self::mainInsert($params);
+            // 商品详情表
+            self::detailInsert($params);
+            // 商品标签表
+            self::labelInsert($params);
+            // 产品运行控制信息表
+            self::goodsRunningInsert($params);
+            // sku表
+            self::goodsSkuSet($params, 'insert');
+            // 待上架状态,上架时间大于当前时间
+            if ($params['goods_status'] == 'PENDING' && strtotime($params['goods_on_addtimes']) > time()) {
+                $redis = Redis::connection();
+                $key = date('YmdHi', strtotime($params['goods_on_addtimes']));
+                $redis->sAdd(Goods::LISTING_KEY_PREFIX . $key, $params['goods_id']);
+            }
+            // 自动下架
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes'])) {
+                $redis = Redis::connection();
+                $key = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($key, $params['goods_id']);
+            }
+            Db::commit();
+        } catch (\PDOException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail('数据写入失败~');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据写入失败~');
+        }
+
+        _syslog("添加商品", "商品名【" . $params['goods_name'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function insertRecharge($params)
+    {
+        try {
+            Db::beginTransaction();
+            $goods = new Goods();
+            $goods->goods_id = "GD" . date('ymdHi') . random_string(4, 'up');
+            $goods->join_goods_category_id = 59;
+            $goods->goods_classify = 'RECHARGE';
+            $goods->goods_status = $params['goods_status'];
+            $goods->goods_sort = $params['goods_sort'];
+            $goods->goods_category = $params['goods_category'];
+            $goods->goods_name = $params['goods_name'];
+            $goods->goods_market_price = $params['goods_sales_price'];
+            $goods->goods_sales_price = $params['goods_sales_price'];
+            $goods->goods_sku_json = json_encode(['规格' => [$params['goods_sales_price'] . '元']]);
+            $goods->goods_attribute_json = json_encode(['added' => ['nbr' => $params['goods_rate'] / 100, 'mode' => 'rate'], 'min-count' => 1]);
+            $goods->goods_cover = '/images/app/common/null-service.png';
+            $goods->goods_process_json = json_encode(['mode' => 'do_shopping']);
+            $goods->goods_addtimes = time();
+            $goods->save();
+
+            $sku = new GoodsSku();
+            $sku->join_sku_goods_id = $goods->goods_id;
+            $sku->goods_sku_status = $params['goods_status'];
+            $sku->goods_sku_specs_json = json_encode(['规格' => $params['goods_sales_price'] . '元']);
+            $sku->goods_sku_market_price = $params['goods_sales_price'];
+            $sku->goods_sku_sales_price = $params['goods_sales_price'];
+            $sku->goods_sku_storage_json = json_encode(['storage' => 9999]);
+            $sku->save();
+
+            Db::commit();
+
+            return json_success('success');
+        } catch (\Exception $e) {
+
+            Db::rollBack();
+
+            return json_fail('添加充值产品失败');
+        }
+    }
+
+    public static function updateRecharge($params)
+    {
+        try {
+            Db::beginTransaction();
+            $goods = Goods::where('goods_id', $params['goods_id'])->first();
+            if (!$goods) {
+                return json_fail("数据异常");
+            }
+            $goods->goods_market_price = $params['goods_sales_price'];
+            $goods->goods_sales_price = $params['goods_sales_price'];
+            $goods->goods_status = $params['goods_status'];
+            $goods->goods_sku_json = json_encode(['规格' => [$params['goods_sales_price'] . '元']]);
+            $goods->goods_sort = $params['goods_sort'];
+            if (!empty($goods->goods_attribute_json)) {
+                $attributeJson = json_decode($goods->goods_attribute_json, true);
+                $attributeJson['added']['nbr'] = $params['goods_rate'] / 100;
+                $goods->goods_attribute_json = json_encode($attributeJson);
+            }
+            $goods->save();
+            $sku = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->where('goods_sku_status','ON')->first();
+            $sku->goods_sku_status = $params['goods_status'];
+            $sku->goods_sku_specs_json = json_encode(['规格' => $params['goods_sales_price'] . '元']);
+            $sku->goods_sku_market_price = $params['goods_sales_price'];
+            $sku->goods_sku_sales_price = $params['goods_sales_price'];
+            $sku->save();
+
+            Db::commit();
+
+            return json_success("success");
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail('编辑充值产品失败');
+        }
+    }
+
+    public static function insertPackage($params): Response
+    {
+        Db::beginTransaction();
+        try {
+            $params['goods_id'] = "GD" . date('ymdHi') . random_string(4, 'up');
+            // 主表
+            self::mainInsert($params);
+            // 商品详情表
+            self::detailInsert($params);
+            // 套包组件表
+            self::componentUpdate($params, 'insert');
+            // 商品标签表
+            self::labelInsert($params);
+            // 产品运行控制信息表
+            self::goodsRunningInsert($params);
+            // sku表
+//            self::goodsSkuSet($params, 'insert');
+            // 待上架状态,上架时间大于当前时间
+            if ($params['goods_status'] == 'PENDING' && strtotime($params['goods_on_addtimes']) > time()) {
+                $redis = Redis::connection();
+                $key = date('YmdHi', strtotime($params['goods_on_addtimes']));
+                $redis->sAdd(Goods::LISTING_KEY_PREFIX . $key, $params['goods_id']);
+            }
+            Db::commit();
+        } catch (\PDOException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail('数据写入失败~');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据写入失败~');
+        }
+
+        _syslog("添加套餐", "商品名【" . $params['goods_name'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function update($params)
+    {
+        Db::beginTransaction();
+        try {
+            // 主表
+            self::mainUpdate($params);
+            // 商品详情表
+            self::detailUpdate($params);
+            // 商品标签表
+            self::labelUpdate($params);
+            // 产品运行控制信息表
+            self::goodsRunningUpdate($params);
+            // sku表
+            self::goodsSkuSet($params, 'update');
+
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            return json_fail('数据更新失败~');
+        }
+
+        _syslog("编辑商品", "商品名【" . $params['goods_name'] . "】" ?? "商品ID:【" . $params['goods_id'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function changeStatus($params)
+    {
+        try {
+            Goods::where('goods_id', $params['goods_id'])->update(['goods_status' => $params['goods_status']]);
+
+            return json_success('修改成功');
+        } catch (\Exception $e) {
+            return json_fail('修改状态失败');
+        }
+    }
+
+
+    public static function updatePackage($params)
+    {
+        Db::beginTransaction();
+        try {
+            // 主表
+            self::mainUpdate($params);
+            // 商品详情表
+            self::detailUpdate($params);
+            // 套包组件表
+            self::componentUpdate($params, 'update');
+            // 商品标签表
+            self::labelUpdate($params);
+            // 产品运行控制信息表
+            self::goodsRunningUpdate($params);
+            // sku表
+            self::goodsSkuSet($params, 'update');
+
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据更新失败~');
+        }
+        _syslog("编辑套餐", "商品名【" . $params['goods_name'] . "】" ?? "商品ID:【" . $params['goods_id'] . "】");
+
+        return json_success('success');
+    }
+
+    /**
+     * @Desc 删除商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:20
+     *
+     * @param $ids
+     * @return Response
+     */
+    public static function delete($ids)
+    {
+        if (!$ids) {
+            return json_fail("数据错误~");
+        }
+        if (!is_array($ids)) {
+            $ids = [$ids];
+        }
+
+        $goods = Goods::whereIn('goods_id', $ids)->get()->toArray();
+        if (!$goods) {
+            return json_fail("数据错误~");
+        }
+
+        // 是否在套包里
+        if (GoodsComponent::whereIn('join_component_goods_id', $ids)->exists()) {
+            return json_fail("当前商品存在于套包中,请先在套包中删除");
+        }
+        // 是否已被购买过
+        if (OrderSheet::whereIn('join_sheet_goods_id', $ids)->exists()) {
+            return json_fail("当前商品已有购买历史,如不在APP显示,请选择【下架】操作");
+        }
+        // 是否预约过
+        if (Appointment::whereIn('join_appointment_goods_id', $ids)->exists()) {
+
+            return json_fail("当前商品已有预约历史,如不在APP显示,请选择【下架】操作");
+        }
+
+        Db::beginTransaction();
+        try {
+            Goods::whereIn('goods_id', $ids)->delete();
+            GoodsDetail::whereIn('join_detail_goods_id', $ids)->delete();
+            GoodsLabel::whereIn('join_label_goods_id', $ids)->delete();
+            GoodsRunning::whereIn('join_running_goods_id', $ids)->delete();
+            GoodsSku::whereIn('join_sku_goods_id', $ids)->delete();
+
+            Db::commit();
+
+            _syslog("删除商品 / 套餐", "ID:【" . implode(',', $ids) . "】", $goods);
+
+            return json_success("商品删除成功");
+        } catch (\Exception $e) {
+            Db::rollBack();
+
+            return json_fail("商品删除失败~");
+        }
+    }
+
+    /**
+     * @Desc 商品主表
+     * @Author Gorden
+     * @Date 2024/3/11 11:20
+     *
+     * @param $params
+     * @return mixed|string
+     * @throws BusinessException
+     */
+    public static function mainInsert($params)
+    {
+        if (!empty($params['goods_cover'])) {
+            $params['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_cover']);
+        }
+        // 如果产品是待处理状态
+        if ($params['goods_status'] == 'PENDING') {
+            if (strtotime($params['goods_on_addtimes']) <= time()) {
+                $params['goods_status'] = 'ON';
+            }
+        }
+        $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+        if (!$category) {
+            throw new BusinessException("产品分类不存在~");
+        }
+        if (empty($params['goods_category'])) {
+            $params['goods_category'] = $category->category_classify;
+        }
+
+        try {
+            $model = new Goods();
+            $model->goods_id = $params['goods_id'];
+            $model->join_goods_category_id = $params['join_goods_category_id'] ?? 0;
+            $model->join_goods_supplier_id = $params['join_goods_supplier_id'] ?? 0;
+            $model->goods_classify = $params['goods_classify'] ?? '';
+            $model->goods_status = $params['goods_status'] ?? '';
+            $model->goods_category = $params['goods_category'] ?? '';
+//            $model->goods_prefix = $params['goods_prefix'] ?? 】($category->category_name ? '【' . $category->category_name . '' : '');
+            $model->goods_prefix = $params['goods_prefix'] ?? '';
+            $model->goods_name = $params['goods_name'];
+            $model->goods_market_price = $params['goods_market_price'] ?? 0;
+            $model->goods_sales_price = $params['goods_sales_price'] ?? 0;
+            $model->goods_sku_json = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+            $model->goods_attribute_json = !empty($params['goods_attribute_json']) ? $params['goods_attribute_json'] : '[]';
+            $model->goods_title = $params['goods_title'] ?? '';
+            $model->goods_cover = $params['goods_cover'] ?? '';
+            $model->goods_on_addtimes = isset($params['goods_on_addtimes']) ? strtotime($params['goods_on_addtimes']) : null;
+            $model->goods_sort = $params['goods_sort'] ?? null;
+            $model->goods_groupby = $params['goods_groupby'] ?? '';
+            $model->goods_remark = $params['goods_remark'] ?? '';
+            $model->goods_extend_json = $params['goods_extend_json'] ?? '{}';
+            $model->is_support_appointment = $params['is_support_appointment'] ?? 'N';
+            $model->creator_user_id = JwtToken::getCurrentId();
+            $model->goods_addtimes = time();
+            $model->goods_updatetimes = time();
+            // {"express":"Y","self":"Y","arrival":"Y"}
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $model->goods_express_json = json_encode($expressJson);
+            }
+
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times']) && $params['goods_category'] != 'TRAVEL') {
+                $times = [];
+                $attributeJsonTimeArr = [];
+                $personTotal = 0;
+                foreach ($params['appointment_times'] as $time) {
+                    $attributeJsonTimeArr[] = strtotime(date('Y-m-d ') . $time['appointmentTimeStart']);
+                    $attributeJsonTimeArr[] = strtotime(date('Y-m-d ') . $time['appointmentTimeEnd']);
+                    $personTotal += $time['person'];
+                    $times[$time['appointmentTimeStart']] = [
+                        'person' => $time['person'],
+                        'duration' => $time['appointmentTimeStart'] . '-' . $time['appointmentTimeEnd']
+                    ];
+                }
+                $attributeJsonTime = date('H:i', min($attributeJsonTimeArr)) . '至' . date('H:i', max($attributeJsonTimeArr));
+
+                $newDates = [];
+                foreach ($params['dates'] as $date) {
+                    $key = self::$week[$date];
+                    $newDates[$key] = $date;
+                }
+                ksort($newDates);
+
+
+                $currentDate = current($newDates);
+                $lastDate = end($newDates);
+
+                $attributeJsonDate = '';
+                if (self::$week[$lastDate] - self::$week[$currentDate] + 1 > count($newDates)) {
+                    $attributeJsonDate = implode(',', $newDates);
+                } else if (self::$week[$lastDate] - self::$week[$currentDate] + 1 == count($newDates)) {
+                    $attributeJsonDate = $currentDate . '至' . $lastDate;
+                }
+
+                // if (isset($params['work_time'])){
+                //     $workTimeStart = date('H:i',strtotime($params['work_time'][0]));
+                //     $workTimeEnd = date('H:i',strtotime($params['work_time'][1]));
+                //     $attributeJsonTime = $workTimeStart.'至'.$workTimeEnd;
+                // }
+
+                $attributeJson = [
+                    'icon' => $model->goods_cover,
+                    'date' => $attributeJsonDate,
+                    // 'time' => $attributeJsonTime,
+                    'dates' => $newDates ? array_values($newDates) : [],
+                    'times' => $times,
+                    'person' => $personTotal
+                ];
+                // if(isset($params['address'])){
+                //     $attributeJson['address'] = $params['address'];
+                // }
+                // if (isset($params['min_count'])){
+                //     $attributeJson['min-count'] = $params['min_count'];
+                // }else{
+                //     $attributeJson['min-count'] = 1;
+                // }
+                // if (isset($params['teachers'])){
+                //     $attributeJson['teachers'] = $params['teachers'];
+                // }
+                if (!empty($params['appointment_label'])) {
+                    $attributeJson['label'] = $params['appointment_label'];
+                }
+                // if (!empty($params['address'])) {
+                //     $attributeJson['address'] = $params['address'];
+                // }
+                // if (!empty($params['position'])){
+                //     $attributeJson['position'] = $params['position'];
+                // }
+                // if (isset($params['goods_service_premises'])){
+                //     $attributeJson['service_premises_id'] = $params['goods_service_premises'];
+                // }
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+
+//                $times = [];
+//                foreach ($params['appointment_times'] as $time) {
+//                    $times[$time['appointmentTimeStart']] = [
+//                        'person' => $time['person'],
+//                        'duration' => $time['appointmentTimeStart'] . '-' . $time['appointmentTimeEnd']
+//                    ];
+//                }
+//                $model->goods_attribute_json = json_encode([
+//                    'icon' => '',
+//                    'dates' => $params['dates'] ?? [],
+//                    'times' => $times
+//                ]);
+            }
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times']) && $params['goods_category'] == 'TRAVEL') {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['travel-day'] = $params['travel_day'];
+                $attributeJson['travel-begin'] = $params['travel_begin'];
+                $attributeJson['travel-night'] = $params['travel_night'];
+                $attributeJson['travel-trans'] = $params['travel_trans'];
+
+                $unixs = [];
+                foreach ($params['appointment_times'] as $times) {
+                    $unix = strtotime($times['days']);
+                    $unixs[$unix] = $times['person'];
+                }
+                ksort($unixs);
+                $months = [];
+                foreach ($unixs as $key => $unix) {
+                    $month = date('Ym', $key);
+                    $day = date('d', $key);
+                    $months[$month][$day] = $unix;
+                }
+                $attributeJson['month'] = $months;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if (!empty($params['goods_json']) && $params['join_goods_category_id'] == 65) {
+                $goodsJson = json_decode($params['goods_json'], true);
+//                foreach ($goodsJson as $key => $item) {
+//                    $goodsJson[$key]['color'] = hexToRgb($item['color']);
+//                }
+//
+                $model->goods_json = json_encode($goodsJson);
+            } elseif (!empty($params['goods_json']) && $params['join_goods_category_id'] == 43) {
+                $goodsJson = json_decode($params['goods_json'], true);
+                $newGoodsJson = [];
+                foreach ($goodsJson as $item1) {
+                    if (empty($item1['title'])) {
+                        continue;
+                    }
+                    $newItem1 = [];
+                    foreach ($item1['items'] as $item2) {
+                        $newParams = [];
+                        foreach ($item2['params'] as $param) {
+                            if (!empty($param[0]) || !empty($param[1])) {
+                                $newParams[] = $param;
+                            }
+                        }
+                        $newItem1['items'][$item2['key']] = $newParams;
+                    }
+                    $newItem1['service'] = $item1['service'] ?? '';
+                    $newGoodsJson[$item1['title']] = $newItem1;
+                }
+                $model->goods_json = json_encode($newGoodsJson);
+            } else {
+                $model->goods_json = '[]';
+            }
+
+            if (!empty($params['goods_premisses'])) {
+                $attributeJson = [];
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['premisses'] = $params['goods_premisses'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+
+            if (!empty($params['goods_theme_color']) && !empty($params['goods_theme_icon'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['bg'] = $params['goods_theme_color'];
+                $attributeJson['icon'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_theme_icon']);
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['address'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['address'] = $params['address'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['position'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['position'] = $params['position'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['goods_service_premises'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['service_premises_id'] = $params['goods_service_premises'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['min_count'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['min-count'] = $params['min_count'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['teachers'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['teachers'] = $params['teachers'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['work_time'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+
+                $workTimeStart = date('H:i', strtotime($params['work_time'][0]));
+                $workTimeEnd = date('H:i', strtotime($params['work_time'][1]));
+                $attributeJsonTime = $workTimeStart . '至' . $workTimeEnd;
+
+                $attributeJson['time'] = $attributeJsonTime;
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if ($model->save()) {
+                return $model->goods_id;
+            }
+            // 异常
+            throw new BusinessException("数据写入失败~");
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 详情表
+     * @Author Gorden
+     * @Date 2024/3/11 11:19
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailInsert($params)
+    {
+        if (!empty($params['goods_detail_slider_json'])) {
+            $params['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_detail_slider_json']);
+            $params['goods_detail_slider_json'] = json_encode(['slider' => $params['goods_detail_slider_json']]);
+        }
+
+        if (isset($params['curriculum'])) {
+            $params['goods_detail_specs_json'] = json_encode([
+                [
+                    'key' => '课时',
+                    'val' => $params['curriculum']['period'] ?? '',
+                ],
+                [
+                    'key' => '群体',
+                    'val' => $params['curriculum']['group'] ?? '',
+                ],
+                [
+                    'key' => '课程',
+                    'val' => $params['curriculum']['course'] ?? '',
+                ],
+            ]);
+        }
+        try {
+            $model = new GoodsDetail();
+            $model->join_detail_goods_id = $params['goods_id'];
+            $model->goods_detail_code_json = $params['goods_detail_code_json'] ?? '{}';
+            $model->goods_detail_slider_json = $params['goods_detail_slider_json'] ?? '{}';
+            $model->goods_detail_specs_json = $params['goods_detail_specs_json'] ?? '{}';
+            $model->goods_detail_content = $params['goods_detail_content'] ?? '';
+            if (!$model->save()) {
+                // 异常
+                throw new BusinessException("轮播图/详情数据写入失败~");
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("轮播图/详情数据写入失败~");
+        }
+    }
+
+    public static function componentUpdate($params, $type = 'insert')
+    {
+        Db::beginTransaction();
+        try {
+            $goodsSku = '';
+            // 有先删除
+            if ($type == 'update') {
+                GoodsComponent::where('join_component_master_goods_id', $params['goods_id'])->delete();
+                $goodsSku = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->first();
+                if ($goodsSku) {
+                    $goodsSku->goods_sku_market_price = $params['goods_market_price'] ?? 0;
+                    $goodsSku->goods_sku_sales_price = $params['goods_sales_price'] ?? 0;
+                    $goodsSku->save();
+                }
+            }
+            if ($type == 'insert' || !$goodsSku) {
+                Goods::where('goods_id', $params['goods_id'])->update(['goods_sku_json' => '{"规格": ["标准"]}']);
+                $skuData = [
+                    'join_sku_goods_id' => $params['goods_id'],
+                    'goods_sku_status' => 'ON',
+                    'goods_sku_specs_json' => '{"规格": "标准"}',
+                    'goods_sku_title' => "标准" . $params['goods_name'],
+                    'goods_sku_market_price' => $params['goods_market_price'] ?? 0,
+                    'goods_sku_sales_price' => $params['goods_sales_price'] ?? 0,
+                ];
+                GoodsSku::insert($skuData);
+            }
+            $data = [];
+            foreach ($params['goods_content_list'] as $item) {
+                if (!in_array($item['goods_id'], $params['join_component_goods_id'])) {
+                    continue;
+                }
+                $goods = Goods::where('goods_id', $params['goods_id'])->first();
+                if (!$goods) {
+                    continue;
+                }
+
+                $data[] = [
+                    'join_component_master_goods_id' => $params['goods_id'],
+                    'join_component_goods_id' => $item['goods_id'] ?? '',
+                    'join_component_goods_sku_id' => $item['sku_id'] ?? '',
+                    'goods_component_price' => $item['goods_sales_price'] ?? '',
+                    'goods_component_cover' => $goods->goods_cover,
+                    'goods_component_json' => json_encode(['goods_name' => $item['goods_name'], 'nbr' => $item['nbr'], 'sku_id' => $item['sku_id'] ?? '']),
+                    'goods_component_addtimes' => time()
+                ];
+            }
+
+            if ($data) {
+                GoodsComponent::insert($data);
+            }
+            Db::commit();
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            throw new BusinessException("数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 标签表
+     * @Author Gorden
+     * @Date 2024/3/11 11:32
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelInsert($params)
+    {
+        $model = new GoodsLabel();
+        $model->join_label_goods_id = $params['goods_id'];
+        $model->goods_label = $params['goods_label'] ? implode(',', $params['goods_label']) : '';
+        $model->goods_label_extend_json = !empty($params['goods_label_extend_json']) ? $params['goods_label_extend_json'] : '{}';
+        if (!$model->save()) {
+            // 异常
+            throw new BusinessException('数据写入失败~');
+        }
+    }
+
+    /**
+     * @Desc 产品运行控制信息表
+     * @Author Gorden
+     * @Date 2024/3/11 11:38
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningInsert($params)
+    {
+        try {
+            $model = new GoodsRunning();
+            $model->join_running_goods_id = $params['goods_id'];
+            $model->goods_running_storage = $params['goods_running_storage'] ?? 0;
+            $model->goods_running_sale = $params['goods_running_sale'] ?? 0;
+            $model->goods_running_off_type = !empty($params['goods_running_off_type']) ? $params['goods_running_off_type'] : '';
+            $model->goods_running_off_json = !empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes']) ? json_encode(['time' => strtotime($params['goods_off_addtimes'])]) : '[]';
+            if (!$model->save()) {
+                throw new BusinessException('数据写入失败');
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据写入失败');
+        }
+    }
+
+    /**
+     * @Desc SKU
+     * @Author Gorden
+     * @Date 2024/3/11 12:01
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function skuInsert($params)
+    {
+        $model = new GoodsSku();
+        $model->join_sku_goods_id = $params['goods_id'];
+        $model->goods_sku_status = $params['goods_sku_status'];
+        $model->goods_sku_specs_json = $params['goods_sku_specs_json'];
+        $model->goods_sku_title = $params['goods_sku_title'];
+        $model->goods_sku_images_json = $params['goods_sku_images_json'];
+        $model->goods_sku_content = $params['goods_sku_content'];
+        $model->goods_sku_market_price = $params['goods_sku_market_price'];
+        $model->goods_sku_sales_price = $params['goods_sku_sales_price'];
+        $model->goods_sku_storage_json = $params['goods_sku_storage_json'];
+        $model->goods_sku_service_json = $params['goods_sku_service_json'];
+        $model->goods_sku_extend_json = $params['goods_sku_extend_json'];
+        if (!$model->save()) {
+            throw new BusinessException('规格数据写入失败~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:44
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function mainUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new Goods());
+            if (!empty($data['goods_cover'])) {
+                $data['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_cover']);
+            }
+            $data['goods_on_addtimes'] = isset($data['goods_on_addtimes']) ? strtotime($data['goods_on_addtimes']) : 0;
+            $data['goods_sku_json'] = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+
+            $row = Goods::find($data['goods_id']);
+            if ($row->join_goods_category_id != $data['join_goods_category_id']) {
+                $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+                if (!$category) {
+                    throw new BusinessException("产品分类不存在~");
+                }
+                $data['goods_category'] = $category->category_classify ?? '';
+                $data['goods_prefix'] = $data['goods_prefix'] ?? ($category->category_name ? '【' . $category->category_name . '】' : '');
+            }
+
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $data['goods_express_json'] = json_encode($expressJson);
+            }
+            $attributeJson = [];
+            if (!empty($row->goods_attribute_json)) {
+                $attributeJson = json_decode($row->goods_attribute_json, true);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+            }
+            if (!empty($params['attribute_account'])){
+                $attributeJson['account'] = $params['attribute_account'];
+            }
+            if (!empty($params['attribute_control'])){
+                $attributeJson['control'] = $params['attribute_control'];
+            }
+
+            $data['goods_attribute_json'] = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+
+            foreach ($data as $key => $val) {
+                $row->{$key} = $val;
+            }
+            $row->goods_updatetimes = time();
+            $row->updator_user_id = JwtToken::getCurrentId();
+            $row->save();
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getTrace());
+            throw new BusinessException('数据更新异常~1');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:57
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsDetail());
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_detail_slider_json']);
+                $data['goods_detail_slider_json'] = json_encode(['slider' => $data['goods_detail_slider_json']]);
+            }
+            if (isset($params['curriculum'])) {
+                $data['goods_detail_specs_json'] = json_encode([
+                    [
+                        'key' => '课时',
+                        'val' => $params['curriculum']['period'] ?? '',
+                    ],
+                    [
+                        'key' => '群体',
+                        'val' => $params['curriculum']['group'] ?? '',
+                    ],
+                    [
+                        'key' => '课程',
+                        'val' => $params['curriculum']['course'] ?? '',
+                    ],
+                ]);
+            }
+            // 根据goods_id 查详情ID
+            $detail = GoodsDetail::where('join_detail_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->join_detail_goods_id, $data, new GoodsDetail());
+            } else {
+                $data['join_detail_goods_id'] = $params['goods_id'];
+                GoodsDetail::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('轮播图/详情数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:58
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsLabel());
+            // 根据goods_id 查详情ID
+            $detail = GoodsLabel::where('join_label_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->goods_label_id, $data, new GoodsLabel());
+            } else {
+                $data['join_label_goods_id'] = $params['goods_id'];
+                GoodsLabel::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~3');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:59
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsRunning());
+            // 根据goods_id 查详情ID
+            $detail = GoodsRunning::where('join_running_goods_id', $params['goods_id'])->first();
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T') {
+                $redis = Redis::connection();
+                if (!empty($detail->goods_running_off_json)) {
+                    $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                    if (isset($goodsRunningOffJson['time'])) {
+                        $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                        // 有老的下架时间,删除老的
+                        $redis->srem($oldKey, $params['goods_id']);
+                    } else {
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                    }
+                } else {
+                    $data['goods_running_off_json'] = json_encode(['time' => strtotime($params['goods_off_addtimes'])]);
+                }
+                // 加入自动下架
+                $newKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($newKey, $params['goods_id']);
+
+            } else if (!empty($params['goods_running_off_type']) && !empty($detail->goods_running_off_type) && $params['goods_running_off_type'] == 'H' && $detail->goods_running_off_type == 'T') {
+                $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                if (isset($goodsRunningOffJson['time'])) {
+                    $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                    $redis = Redis::connection();
+                    $redis->srem($oldKey, $params['goods_id']);
+                }
+            }
+            if ($detail) {
+                self::doUpdate($detail->join_running_goods_id, $data, new GoodsRunning());
+            } else {
+                // 兼容老数据……
+                $data['join_running_goods_id'] = $params['goods_id'];
+                GoodsRunning::insert($data);
+            }
+
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc sku 设置
+     * @Author Gorden
+     * @Date 2024/4/10 10:43
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsSkuSet($params, $operation = 'insert')
+    {
+        try {
+            Db::beginTransaction();
+            $skusOldIds = [];
+            if ($operation == 'update') {
+                // 查出所有的
+                $skusOldIds = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->pluck('goods_sku_id', 'goods_sku_id');
+
+                // 删掉原有的
+//                GoodsSku::where('join_sku_goods_id', $params['goods_id'])->delete();
+            }
+            if (empty($skusOldIds) && empty($params['goods_sku_json_value'])) {
+                $skuData = [
+                    'join_sku_goods_id' => $params['goods_id'],
+                    'goods_sku_status' => 'ON',
+                    'goods_sku_specs_json' => '{"规格": "标准"}',
+                    'goods_sku_title' => "标准" . $params['goods_name'],
+                    'goods_sku_market_price' => $params['goods_market_price'] ?? 0,
+                    'goods_sku_sales_price' => $params['goods_sales_price'] ?? 0,
+                ];
+                GoodsSku::insert($skuData);
+            }
+            // 入新的
+            if (!empty($params['goods_sku_json_value'])) {
+                foreach ($params['goods_sku_json_value'] as $item) {
+                    $skus = explode(',', $item['sku']);
+                    $skuArr = [];
+                    for ($i = 1; $i <= count($skus); $i++) {
+                        $skuName = "skuName" . $i;
+                        $key = $item[$skuName];
+                        $skuArr[$key] = $skus[$i - 1];
+                    }
+                    $specsJson = json_encode($skuArr);
+                    $skuTitle = str_replace('-', ',', $item['sku']) . $params['goods_name'];
+                    if ($operation == 'update' && !empty($item['sku_id'])) {
+                        $model = GoodsSku::where('goods_sku_id', $item['sku_id'])->where('goods_sku_status','ON')->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+                    } else {
+                        $model = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->where('goods_sku_status','ON')->where('goods_sku_title', $skuTitle)->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+
+                    }
+
+                    $model->join_sku_goods_id = $params['goods_id'];
+                    $model->goods_sku_status = $params['goods_status'];
+                    $model->goods_sku_specs_json = $specsJson;
+                    $model->goods_sku_title = $skuTitle;
+                    $model->goods_sku_market_price = $params['goods_market_price'] ?? 0;
+                    $model->goods_sku_sales_price = $item['price'];
+                    $model->goods_sku_storage_json = json_encode(['storage' => $item['stock']]);
+                    $model->save();
+
+                }
+            }
+            if ($operation == 'update' && !empty($skusOldIds)) {
+                // GoodsSku::whereIn('goods_sku_id', $skusOldIds)->delete();
+                GoodsSku::whereIn('goods_sku_id', $skusOldIds)->update(['goods_sku_status' => 'DISABLED']);
+            }
+
+            Db::commit();
+        } catch (\Exception $e) {
+            dump($e->getTrace());
+            Db::rollBack();
+
+            throw new BusinessException('规格数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:45
+     *
+     * @param array $data
+     * @param $model
+     * @return array
+     * @throws BusinessException
+     */
+    private static function inputFilter(array $data, $model): array
+    {
+        $table = config('database.connections.mysql.prefix') . $model->getTable();
+        $allow_column = $model->getConnection()->select("desc `$table`");
+        if (!$allow_column) {
+            throw new BusinessException('表不存在', 2);
+        }
+        $columns = array_column($allow_column, 'Type', 'Field');
+        foreach ($data as $col => $item) {
+            if (!isset($columns[$col])) {
+                unset($data[$col]);
+                continue;
+            }
+            // 非字符串类型传空则为null
+            if ($item === '' && strpos(strtolower($columns[$col]), 'varchar') === false && strpos(strtolower($columns[$col]), 'text') === false) {
+                $data[$col] = null;
+            }
+            if (is_array($item)) {
+                $data[$col] = implode(',', $item);
+            }
+            if ($item != '' && (strpos(strtolower($columns[$col]), 'varchar') || strpos(strtolower($columns[$col]), 'text'))) {
+//                $data[$col] = htmlspecialchars($item);
+            }
+        }
+        if (empty($data['created_at'])) {
+            unset($data['created_at']);
+        }
+        if (empty($data['updated_at'])) {
+            unset($data['updated_at']);
+        }
+        return $data;
+    }
+
+    /**
+     * @Desc 执行更新
+     * @Author Gorden
+     * @Date 2024/3/12 8:43
+     *
+     * @param $id
+     * @param $data
+     * @param $model
+     * @return void
+     */
+    private static function doUpdate($id, $data, $model)
+    {
+        $row = $model->find($id);
+        foreach ($data as $key => $val) {
+            $row->{$key} = $val;
+        }
+        $row->save();
+    }
+
+    /**
+     * @Desc 上架定时
+     * @Author Gorden
+     * @Date 2024/4/11 15:13
+     *
+     * @return void
+     */
+    public static function checkListing()
+    {
+        $key = Goods::LISTING_KEY_PREFIX . date('YmdHi');
+        $redis = Redis::connection();
+        if (!$redis->exists($key)) {
+            return;
+        }
+
+        $goodsIds = $redis->sMembers($key);
+        if (Goods::whereIn('goods_id', $goodsIds)->update(['goods_status' => 'ON'])) {
+            $redis->del($key);
+        }
+    }
+
+    /**
+     * @Desc 自动下架
+     * @Author Gorden
+     * @Date 2024/4/26 15:26
+     *
+     * @return void
+     */
+    public static function checkOffListing()
+    {
+        $key = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi');
+        $redis = Redis::connection();
+        if (!$redis->exists($key)) {
+            return;
+        }
+
+        $goodsIds = $redis->sMembers($key);
+        if (Goods::whereIn('goods_id', $goodsIds)->update(['goods_status' => 'OFF'])) {
+            $redis->del($key);
+        }
+    }
+
+    public static $week = [
+        '周一' => 1,
+        '周二' => 2,
+        '周三' => 3,
+        '周四' => 4,
+        '周五' => 5,
+        '周六' => 6,
+        '周日' => 7,
+    ];
+}

+ 2119 - 0
app/admin/service/goods/PartnerService.php

@@ -0,0 +1,2119 @@
+<?php
+
+namespace app\admin\service\goods;
+
+use app\common\Tree;
+use app\model\Appointment;
+use app\model\Coupon;
+use app\model\Goods;
+use app\model\GoodsComponent;
+use app\model\GoodsDetail;
+use app\model\GoodsLabel;
+use app\model\GoodsRunning;
+use app\model\GoodsSku;
+use app\model\OrderSheet;
+use app\model\Supplier;
+use app\model\SysCategory;
+use app\model\SysDept;
+use app\model\SysSerial;
+use app\model\SysUser;
+use support\Db;
+use support\exception\BusinessException;
+use support\Redis;
+use support\Request;
+use support\Response;
+use Tinywan\Jwt\JwtToken;
+
+class PartnerService
+{
+    public static function selectAll($goodsIds)
+    {
+        $goods = Goods::where('goods_status', 'ON')
+            ->when($goodsIds != '', function ($query) use ($goodsIds) {
+                $query->whereIn('goods_id', $goodsIds);
+            })
+            ->select('goods_id', 'goods_name')
+            ->get();
+
+        return json_success('', $goods);
+    }
+
+    public static function selectAllByGoodsName($goodsName)
+    {
+        $goods = Goods::with('sku')
+            ->where('goods_status', 'ON')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where('goods_name', 'like', "%" . $goodsName . '%');
+            })
+            ->select('goods_id', 'goods_name', 'goods_classify')
+            ->get()
+            ->toArray();
+        foreach ($goods as &$item) {
+            if (!empty($item['sku'])) {
+                foreach ($item['sku'] as $key => $sku) {
+                    $specsJson = json_decode($sku['goods_sku_specs_json'], true);
+                    $skuTitle = '';
+                    foreach ($specsJson as $item2) {
+                        if (is_array($item2)) {
+                            $item2 = implode(',', $item2);
+                        }
+                        $skuTitle .= $item2 . '-';
+                    }
+                    $item['sku'][$key]['goods_sku_id'] = strval($item['sku'][$key]['goods_sku_id']);
+                    $item['sku'][$key]['goods_sku_title'] = rtrim($skuTitle, '-');
+                }
+            }
+        }
+
+        return json_success('', $goods);
+    }
+
+    public static function selectAllByCategoryForRuleAddComponent($category = "GOODS")
+    {
+        $categoryIds = [];
+        $categorySuperIds = [];
+        if ($category == 'GOODS') {
+            $categorySuperIds = [5];
+        } elseif ($category == 'SERVICE') {
+            $categorySuperIds = [31, 154, 42, 65, 30, 66, 72, 70];
+        }
+        $categorys = SysCategory::whereIn('category_id', $categorySuperIds)->get()->toArray();
+        foreach ($categorys as $item) {
+            if (empty($item['category_super_path'])) {
+                $item['category_super_path'] = '#' . $item['category_id'] . '#';
+            } else {
+                $item['category_super_path'] = $item['category_super_path'] . '#' . $item['category_id'] . '#';
+            }
+
+            $categoryIds = SysCategory::where('category_super_path', 'like', '%' . $item['category_super_path'] . '%')->pluck('category_id');
+            $categoryIds = $categoryIds ? $categoryIds->toArray() : [];
+            $categorySuperIds = array_merge($categorySuperIds, $categoryIds);
+        }
+
+        $categoryIds = array_unique($categorySuperIds);
+
+        $goods = Goods::with('sku')
+//            ->where('goods_classify', $category)
+            ->when(!empty($categoryIds), function ($query) use ($categoryIds) {
+                $query->whereIn('join_goods_category_id', $categoryIds);
+            })->when(empty($categoryIds), function ($query) {
+                $query->where('goods_classify', '<>', 'RECHARGE');
+            })->where('goods_status', 'ON')
+            ->select('goods_id', 'goods_name', 'join_goods_category_id')
+            ->get()
+            ->toArray();
+
+        foreach ($goods as &$good) {
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $specsJson = json_decode($sku['goods_sku_specs_json'], true);
+                        $skuTitle = '';
+                        foreach ($specsJson as $item) {
+                            if (is_array($item)) {
+                                $item = implode(',', $item);
+                            }
+                            $skuTitle .= $item . '-';
+                        }
+                        $good['sku'][$key]['goods_sku_id'] = strval($good['sku'][$key]['goods_sku_id']);
+                        $good['sku'][$key]['goods_sku_title'] = rtrim($skuTitle, '-');
+                    }
+                    unset($good['sku'][$key]['goods_sku_specs_json'], $good['sku'][$key]['join_sku_goods_id']);
+                }
+            } else {
+                $good['sku'] = [];
+            }
+
+        }
+
+        return json_success('', $goods);
+    }
+
+    public static function selectPremisesByGoodsId($goodsId)
+    {
+        $goods = Goods::where('goods_id', $goodsId)
+            ->select('goods_id', 'goods_attribute_json')
+            ->first();
+        $premisses = [];
+        if (!empty($goods->goods_attribute_json)) {
+            $attributeJson = json_decode($goods->goods_attribute_json, true);
+            if (isset($attributeJson['premisses'])) {
+                $premisses = SysDept::whereIn('dept_id', $attributeJson['premisses'])
+                    ->select('dept_id', 'dept_name')
+                    ->get()
+                    ->toArray();
+            }
+        }
+
+        return json_success('', $premisses);
+    }
+
+    public static function select(Request $request, $classify = "GOODS")
+    {
+        $page = $request->get('page', 1);
+        $pageSize = $request->get('pageSize', 20);
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+        $goodsCategory = $request->get('goods_category', null);
+        $goodsSupplierId = $request->get('join_goods_supplier_id', null);
+        $goodsStatus = $request->get('goods_status', null);
+        $type = $request->get('type', '');
+        if ($categoryId != null) {
+            $categoryPath = SysCategory::where('category_id', $categoryId)->value('category_super_path');
+            $categoryPath .= '#' . $categoryId . '#';
+            $categoryIds = SysCategory::where('category_super_path', 'like', $categoryPath . '%')->pluck('category_id')->toArray();
+            $categoryIds[] = intval($categoryId);
+            if (!empty($categoryIds)) {
+                $categoryId = $categoryIds;
+            } else {
+                $categoryId = [intval($categoryId)];
+            }
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_attribute_json', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->whereIn('join_goods_category_id', $categoryId);
+            })->when($goodsCategory != null, function ($query) use ($goodsCategory) {
+                $query->where('goods_category', $goodsCategory);
+            })
+            ->when($classify != '', function ($query) use ($classify, $categoryId) {
+                if ($classify == 'GOODS' && empty($categoryId)) {
+                    $query->whereIn('join_goods_category_id', ['6', '7', '8', '9', '10', '11', '12', '30']);
+                } else if ($classify != 'GOODS' && empty($categoryId)) {
+                    $query->where('goods_classify', $classify);
+                }
+            })->when(!empty($type), function ($query) use ($type) {
+                if ($type == 'storageWarning') {
+                    $query->where('goods_running.goods_running_storage', '<=', 2);
+                }
+            })->when(!empty($goodsSupplierId), function ($query) use ($goodsSupplierId) {
+                $query->where('join_goods_supplier_id', $goodsSupplierId);
+            })->when(!empty($goodsStatus), function ($query) use ($goodsStatus) {
+                $query->where('goods_status', $goodsStatus);
+            })
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+
+        $total = Goods::leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->whereIn('join_goods_category_id', $categoryId);
+            })->when($goodsCategory != null, function ($query) use ($goodsCategory) {
+                $query->where('goods_category', $goodsCategory);
+            })->when($classify != '', function ($query) use ($classify, $categoryId) {
+                if ($classify == 'GOODS' && empty($categoryId)) {
+                    $query->whereIn('join_goods_category_id', ['6', '7', '8', '9', '10', '11', '12', '30']);
+                } else if ($classify != 'GOODS' && empty($categoryId)) {
+                    $query->where('goods_classify', $classify);
+                }
+            })->when(!empty($type), function ($query) use ($type) {
+                if ($type == 'storageWarning') {
+                    $query->where('goods_running.goods_running_storage', '<=', 2);
+                }
+            })->when(!empty($goodsSupplierId), function ($query) use ($goodsSupplierId) {
+                $query->where('join_goods_supplier_id', $goodsSupplierId);
+            })->when(!empty($goodsStatus), function ($query) use ($goodsStatus) {
+                $query->where('goods_status', $goodsStatus);
+            })
+            ->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+            if (!empty($row['goods_attribute_json'])) {
+                $row['goods_attribute_json'] = json_decode($row['goods_attribute_json']);
+            }
+            if (!empty($row['goods_category']) && $row['goods_category'] == 'INDEX') {
+                $row['goods_recommend_index'] = 'INDEX';
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectSpecial(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+        if ($categoryId == null) {
+            $categoryId = [65, 43];
+        } elseif (is_string($categoryId)) {
+            $categoryId = [$categoryId];
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->whereIn('join_goods_category_id', $categoryId)
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->whereIn('join_goods_category_id', $categoryId)->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectPicking(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+
+        $categorySuperId = $request->get('category_super_id', '');
+        $categoryIds = $request->get('join_goods_category_id', []);
+        if (!empty($categorySuperId) && is_array($categoryIds)) {
+            $category = SysCategory::where('category_id', $categorySuperId)->first();
+            if (empty($category->category_super_path)) {
+                $category->category_super_path = '#' . $categorySuperId . '#';
+            } else {
+                $category->category_super_path = $category->category_super_path . '#' . $categorySuperId . '#';
+            }
+            $categoryIds = SysCategory::where('category_super_path', 'like', '%' . $category->category_super_path)->pluck('category_id');
+            $categoryIds = $categoryIds ? $categoryIds->toArray() : [];
+            $categoryIds = array_merge($categoryIds, [$categorySuperId]);
+        } elseif (!is_array($categoryIds)) {
+            $categoryIds = [$categoryIds];
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->whereIn('join_goods_category_id', $categoryIds)
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->whereIn('join_goods_category_id', $categoryIds)
+            ->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectPackage(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->where('join_goods_category_id', $categoryId);
+            })
+            ->where('goods_classify', 'PACKAGE')
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->when($categoryId != null, function ($query) use ($categoryId) {
+            $query->where('join_goods_category_id', $categoryId);
+        })->where('goods_classify', 'PACKAGE')->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+//            if (!empty($row['component'])) {
+//                $ids = [];
+//                $contentList = [];
+//                foreach ($row['component'] as $component) {
+//                    $ids[] = $component['join_component_goods_id'];
+//                    $configJson = json_decode($component['goods_component_config_json'], true);
+//                    $contentList[] = [
+//                        'goods_name' => $configJson['goods_name'] ?? '',
+//                        'goods_sales_price' => $component['goods_component_price'],
+//                        'nbr' => $configJson['nbr'] ?? 0
+//                    ];
+//                }
+//
+//                $row['join_component_goods_id'] = $ids;
+//                $row['goodsContentList'] = $contentList;
+//            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    /**
+     * @Desc 下拉选择服务商品
+     * @Author Gorden
+     * @Date 2024/4/24 13:32
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public static function selectList(Request $request, $goodsClassify = "SERVICE")
+    {
+        $keywords = $request->get('keywords', '');
+        $isSupportAppointment = $request->get('is_support_appointment', '');
+//        if (!$keywords){
+//            return json_success('暂无数据');
+//        }
+
+//        $categoryIds = SysCategory::whereIn('category_super_id', [5, 31, 32, 42, 66, 70, 72])->pluck('category_id');
+
+        $goods = Goods::with('sku')
+//            ->whereIn('join_goods_category_id', $categoryIds)
+            ->when($keywords != '', function ($query) use ($keywords) {
+                $query->where('goods_name', 'like', "%" . $keywords . "%");
+            })
+            ->when($goodsClassify != '', function ($query) use ($goodsClassify) {
+                if ($goodsClassify == 'NOPACKAGE') {
+                    $query->where('goods_classify', '<>', 'PACKAGE');
+                } else if ($goodsClassify == 'SERVICE') {
+                    $query->whereIn('goods_classify', ['SERVICE', 'CHNMED', 'CHNNCD']);
+                } else {
+                    $query->where('goods_classify', $goodsClassify);
+                }
+
+            })
+            ->when($isSupportAppointment != '', function ($query) use ($isSupportAppointment) {
+                $query->where('is_support_appointment', $isSupportAppointment);
+            })
+            ->select('goods_id', 'goods_name', 'goods_sales_price', 'join_goods_category_id', 'goods_attribute_json')
+            ->get()
+            ->toArray();
+
+        foreach ($goods as &$good) {
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $good['sku'][$key]['goods_sku_specs_json'] = json_decode($sku['goods_sku_specs_json']);
+
+                        $skuName = '';
+                        foreach ($good['sku'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . ' ' . $specsKey . ':' . implode(' ', $skuSpecs) . ';';
+                            } else {
+                                $skuName = $skuName . ' ' . $specsKey . ':' . $skuSpecs . ';';
+                            }
+                        }
+                        $good['sku'][$key]['sku_name'] = $skuName;
+                    }
+                }
+            } else {
+                $good['sku'] = [];
+            }
+            $good['premisses'] = [];
+            if (!empty($good['goods_attribute_json'])) {
+                $attributeJson = json_decode($good['goods_attribute_json'], true);
+                if (isset($attributeJson['premisses'])) {
+                    $premisses = SysDept::when(!empty($attributeJson['premisses']), function ($query) use ($attributeJson) {
+                        $query->whereIn('dept_id', $attributeJson['premisses']);
+                    })->where('dept_category', '营业场所')
+                        ->select('dept_id', 'dept_name')
+                        ->get();
+                    $good['premisses'] = $premisses;
+                }
+            }
+        }
+
+        return json_success('', $goods);
+
+    }
+
+    public static function selectCascaderList(Request $request)
+    {
+        $categoryIds = $request->get('category_id', '');
+        $categorySuperId = $request->get('category_super_id', '');
+        $joinGoodsCategoryId = $request->get('join_goods_category_id', '');
+        $type = $request->get('type', '');
+        if (!$categoryIds && !$categorySuperId && !$joinGoodsCategoryId) {
+            return json_fail('参数异常');
+        }
+        $data = [];
+        $categorys = [];
+        if (!empty($categoryIds)) {
+            $categorys = SysCategory::whereIn('category_id', $categoryIds)
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $categorys);
+        } else if (!empty($categorySuperId)) {
+            $categorys = SysCategory::where('category_super_id', $categorySuperId)
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $categorys);
+        }
+
+        foreach ($categorys as $category) {
+            // if(empty($category['category_super_path'])){
+            $category['category_super_path'] = '#' . $category['id'] . '#';
+            // }
+            $subCategory = SysCategory::where('category_super_path', 'like', '%' . $category['category_super_path'] . '%')
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $subCategory);
+        }
+        if (empty($data) && !empty($joinGoodsCategoryId)) {
+            $goodsCategoryIds = $joinGoodsCategoryId;
+        } else {
+            $goodsCategoryIds = array_column($data, 'id');
+        }
+        $goods = Goods::with([
+            'sku' => function($query){
+                $query->where('goods_sku_status','ON');
+            }
+        ])
+            ->leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->where('goods_running.goods_running_storage', '>', 0)
+            ->whereIn('join_goods_category_id', $goodsCategoryIds)
+            ->where('goods_status', 'ON');
+
+        if ($type == 'dishes') {
+            $uid = JwtToken::getCurrentId();
+            $user = SysUser::where('user_id', $uid)->first();
+            $restaurant = SysDept::where('dept_category', '餐厅')->where(function ($query) use ($user) {
+                $query->where('dept_id', $user->join_user_dept_id)->orWhere('dept_super_id', $user->join_user_dept_id);
+            })->first();
+            $supplier = Supplier::where('join_supplier_dept_id', $restaurant->dept_id)->first();
+            if ($supplier) {
+                $goods = $goods->where('join_goods_supplier_id', $supplier->supplier_id);
+            }
+        }
+
+
+        $goods = $goods->select('goods_id', 'goods_id as id', 'goods_name as name', 'join_goods_category_id as pid', 'goods_attribute_json', 'goods_classify', 'goods_sales_price', 'goods_cover', 'goods_running.goods_running_storage')
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->get()
+            ->toArray();
+        foreach ($goods as &$good) {
+            $good['goods_cover'] = getenv('STORAGE_DOMAIN') . $good['goods_cover'];
+            $good['goods_running_storage'] = intval($good['goods_running_storage']);
+            $good['nbr'] = 0;
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $good['sku'][$key]['goods_sku_specs_json'] = json_decode($sku['goods_sku_specs_json']);
+
+                        $skuName = '';
+                        foreach ($good['sku'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                            $keyStr = ($specsKey == '规格') ? '' : ($specsKey . ':');
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . $keyStr . ' ' . implode(' ', $skuSpecs) . '; ';
+                            } else {
+                                $skuName = $skuName . $keyStr . ' ' . $skuSpecs . '; ';
+                            }
+                        }
+                        $good['sku'][$key]['sku_name'] = rtrim($skuName, '; ');
+                    }
+                }
+            } else {
+                $good['sku'] = [];
+            }
+            $good['premisses'] = [];
+            if (!empty($good['goods_attribute_json'])) {
+                $attributeJson = json_decode($good['goods_attribute_json'], true);
+                if (isset($attributeJson['premisses'])) {
+                    $premisses = SysDept::when(!empty($attributeJson['premisses']), function ($query) use ($attributeJson) {
+                        $query->whereIn('dept_id', $attributeJson['premisses']);
+                    })->where('dept_category', '营业场所')
+                        ->select('dept_id', 'dept_name')
+                        ->get();
+                    $good['premisses'] = $premisses;
+                }
+            }
+        }
+
+        $data = array_merge($data, $goods);
+        $tree = new Tree($data);
+        $cascaderData = $tree->getTree();
+
+        foreach ($cascaderData as $key1 => $cascader1) {
+            if (isset($cascader1['children'])) {
+                foreach ($cascader1['children'] as $key2 => $cascader2) {
+                    if (isset($cascader2['children'])) {
+                        foreach ($cascader2['children'] as $key3 => $cascader3) {
+                            if (isset($cascader3['children'])) {
+                                foreach ($cascader3['children'] as $key4 => $cascader4) {
+                                    if (!isset($cascader4['goods_id'])) {
+                                        unset($cascaderData[$key1]['children'][$key2]['children'][$key3]['children'][$key4]);
+                                    }
+                                    if (isset($cascader4['goods_id']) && empty($cascader4['sku'])){
+                                        unset($cascaderData[$key1]['children'][$key2]['children'][$key3]['children'][$key4]);
+                                    }
+                                }
+                            } else if (!isset($cascader3['goods_id'])) {
+                                unset($cascaderData[$key1]['children'][$key2]['children'][$key3]);
+                            }
+                            if (isset($cascader3['goods_id']) && empty($cascader3['sku'])){
+                                unset($cascaderData[$key1]['children'][$key2]['children'][$key3]);
+                                continue;
+                            }
+                            if (isset($cascader3['children']) && count($cascaderData[$key1]['children'][$key2]['children']) == 0) {
+                                unset($cascaderData[$key1]['children'][$key2]);
+                            }
+                            if (isset($cascader3['children']) && count($cascaderData[$key1]['children'][$key2]['children'][$key3]['children']) > 0) {
+                                $cascaderData[$key1]['children'][$key2]['children'][$key3]['children'] = array_values($cascaderData[$key1]['children'][$key2]['children'][$key3]['children']);
+                            }
+                        }
+                    } else if (!isset($cascader2['goods_id'])) {
+                        unset($cascaderData[$key1]['children'][$key2]);
+                    }
+                    if (isset($cascader2['goods_id']) && empty($cascader2['sku'])){
+                        unset($cascaderData[$key1]['children'][$key2]);
+                        continue;
+                    }
+                    if (isset($cascader2['children']) && count($cascaderData[$key1]['children'][$key2]['children']) == 0) {
+                        unset($cascaderData[$key1]['children'][$key2]);
+                    }
+                    if (isset($cascader2['children']) && isset($cascaderData[$key1]['children'][$key2]) && count($cascaderData[$key1]['children'][$key2]['children']) > 0) {
+                        $cascaderData[$key1]['children'][$key2]['children'] = array_values($cascaderData[$key1]['children'][$key2]['children']);
+                    }
+                }
+            } else if (!isset($cascader1['goods_id'])) {
+                unset($cascaderData[$key1]);
+            }
+            if (isset($cascader1['goods_id']) && empty($cascader1['sku'])){
+                unset($cascaderData[$key1]);
+                continue;
+            }
+            if (isset($cascader1['children']) && count($cascaderData[$key1]['children']) == 0) {
+
+                unset($cascaderData[$key1]);
+            }
+            if (isset($cascader1['children']) && isset($cascaderData[$key1]) && count($cascaderData[$key1]['children']) > 0) {
+                $cascaderData[$key1]['children'] = array_values($cascaderData[$key1]['children']);
+            }
+        }
+        $cascaderData = array_values($cascaderData);
+
+        return json_success('success', $cascaderData);
+    }
+
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param $goodsId
+     * @return Response
+     */
+    public static function info($goodsId)
+    {
+        try {
+            // 商品主表
+            $main = Goods::with('category')->where('goods_id', $goodsId)->first();
+            if (!empty($main)) {
+                $main = $main->toArray();
+                $main['goods_sku_json'] = json_decode($main['goods_sku_json'], true);
+                $main['specList'] = [];
+                foreach ($main['goods_sku_json'] as $key => $sku) {
+                    $main['specList'][] = [
+                        'label' => $key,
+                        'tags' => $sku
+                    ];
+                }
+
+            } else {
+                $main = [];
+            }
+            // 详情表
+            $detail = GoodsDetail::where('join_detail_goods_id', $goodsId)->first();
+            if (!empty($detail)) {
+                $detail = $detail->toArray();
+            } else {
+                $detail = [];
+            }
+            // 标签表
+            $label = GoodsLabel::where('join_label_goods_id', $goodsId)->first();
+            if (!empty($label)) {
+                $label = $label->toArray();
+            } else {
+                $label = [];
+            }
+            // Running表
+            $running = GoodsRunning::where('join_running_goods_id', $goodsId)->first();
+            if (!empty($running)) {
+                $running = $running->toArray();
+                if (!empty($running['goods_running_off_json']) && is_json($running['goods_running_off_json'])) {
+                    $goodsRunningOffJson = json_decode($running['goods_running_off_json'], true);
+                    $running['goods_off_addtimes'] = isset($goodsRunningOffJson['time']) ? date("Y-m-d H:i", $goodsRunningOffJson['time']) : '';
+                }
+                $running['goods_running_storage'] = !empty($running['goods_running_storage']) ? intval($running['goods_running_storage']) : '';
+                $running['goods_running_sale'] = !empty($running['goods_running_sale']) ? intval($running['goods_running_sale']) : '';
+            } else {
+                $running = [];
+            }
+            // Sku表
+            $skus = GoodsSku::where('join_sku_goods_id', $goodsId)->where('goods_sku_status','ON')->get();
+            if (!empty($skus)) {
+                $skus = $skus->toArray();
+                $submitList = [];
+                foreach ($skus as $key => $sku) {
+                    $skuSpecsJson = json_decode($sku['goods_sku_specs_json'], true);
+                    $skuSpecs = '';
+                    $skuNameValue = [];
+                    $i = 1;
+                    foreach ($skuSpecsJson as $k => $item) {
+                        if (is_array($item)) {
+                            $item = implode(',', $item);
+                        }
+                        $skuSpecs = $skuSpecs . $item . ',';
+                        $skuNameKey = 'skuName' . $i;
+                        $skuValueKey = 'skuValue' . $i;
+                        $skuNameValue[$skuNameKey] = $k;
+                        $skuNameValue[$skuValueKey] = $item;
+                        $skuNameValue[$k] = $item;
+                        $i++;
+                    }
+                    $storage = json_decode($sku['goods_sku_storage_json'], true);
+                    $priceStorage = [
+                        'sku_id' => $sku['goods_sku_id'],
+                        'sku' => rtrim($skuSpecs, ',') ?? '',
+                        'stock' => $storage['storage'] ?? '',
+                        'price' => $sku['goods_sku_sales_price'] ?? '',
+                    ];
+
+                    $submitList[] = array_merge($skuNameValue, $priceStorage);
+                }
+                $skus['submitList'] = $submitList;
+            } else {
+                $skus = [];
+            }
+
+            // 合并数据
+            $data = array_merge($main, $detail, $label, $running, ['sku' => $skus]);
+            $data['goods_sku_json_label'] = [];
+
+            $data['goods_label'] = !empty($data['goods_label']) ? explode(',', $data['goods_label']) : [];
+            $data['goods_json'] = $data['goods_json'] ? json_decode($data['goods_json'], true) : [];
+            $data['goods_cover'] = getenv('STORAGE_DOMAIN') . $data['goods_cover'];
+            if (!empty($data['goods_category']) && $data['goods_category'] == 'INDEX') {
+                $data['goods_recommend_index'] = 'INDEX';
+            }
+
+            // 创建者
+            $data['creator_username'] = '';
+            if (!empty($data['creator_user_id'])) {
+                $data['creator_username'] = SysUser::where('user_id', $data['creator_user_id'])->value('user_name');
+            }
+
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = json_decode($data['goods_detail_slider_json'], true);
+                // ……
+                if (isset($data['goods_detail_slider_json']['slider'])) {
+                    $data['goods_detail_slider_json'] = explode(',', $data['goods_detail_slider_json']['slider']);
+                }
+
+                $slider = '';
+                foreach ($data['goods_detail_slider_json'] as $item) {
+                    $slider .= getenv('STORAGE_DOMAIN') . $item . ',';
+                }
+                $data['goods_detail_slider_json'] = rtrim($slider, ',');
+            }
+            $extendJson = [];
+            if (!empty($data['goods_attribute_json'])) {
+                $extendJson = json_decode($data['goods_attribute_json'], true);
+                $data['goods_attribute_json'] = $extendJson;
+            }
+            if (!empty($extendJson['coupon'])) {
+                $data['coupon_list'] = $extendJson['coupon'];
+            }
+
+            $data['express_json'] = [];
+            if (!empty($data['goods_express_json'])) {
+                $goodsExpressJson = json_decode($data['goods_express_json'], true);
+                if (isset($goodsExpressJson['express']) && $goodsExpressJson['express'] == 'Y') {
+                    $data['express_json'][] = 'express';
+                }
+                if (isset($goodsExpressJson['self']) && $goodsExpressJson['self'] == 'Y') {
+                    $data['express_json'][] = 'self';
+                }
+                if (isset($goodsExpressJson['arrival']) && $goodsExpressJson['arrival'] == 'Y') {
+                    $data['express_json'][] = 'arrival';
+                }
+            }
+            // 详情表数据
+            if (!empty($data['goods_detail_specs_json'])) {
+                $specsJson = json_decode($data['goods_detail_specs_json'], true);
+                foreach ($specsJson as $itemSpecsJson) {
+                    if (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课时') {
+                        $data['curriculum']['period'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '群体') {
+                        $data['curriculum']['group'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课程') {
+                        $data['curriculum']['course'] = $itemSpecsJson['val'];
+                    }
+                }
+            }
+
+            return json_success('', $data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            return json_fail("查询错误~");
+        }
+    }
+
+    /**
+     * @Desc 添加商品
+     * @Author Gorden
+     * @Date 2024/3/11 10:20
+     *
+     * @param $params
+     * @return Response
+     */
+    public static function insert($params): Response
+    {
+        Db::beginTransaction();
+        try {
+            $params['goods_id'] = "GD" . date('ymdHi') . random_string(4, 'up');
+            // 主表
+            self::mainInsert($params);
+            // 商品详情表
+            self::detailInsert($params);
+            // 商品标签表
+            self::labelInsert($params);
+            // 产品运行控制信息表
+            self::goodsRunningInsert($params);
+            // sku表
+            self::goodsSkuSet($params, 'insert');
+            // 待上架状态,上架时间大于当前时间
+            if ($params['goods_status'] == 'PENDING' && strtotime($params['goods_on_addtimes']) > time()) {
+                $redis = Redis::connection();
+                $key = date('YmdHi', strtotime($params['goods_on_addtimes']));
+                $redis->sAdd(Goods::LISTING_KEY_PREFIX . $key, $params['goods_id']);
+            }
+            // 自动下架
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes'])) {
+                $redis = Redis::connection();
+                $key = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($key, $params['goods_id']);
+            }
+            Db::commit();
+        } catch (\PDOException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail('数据写入失败~');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据写入失败~');
+        }
+
+        _syslog("添加商品", "商品名【" . $params['goods_name'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function insertRecharge($params)
+    {
+        try {
+            Db::beginTransaction();
+            $goods = new Goods();
+            $goods->goods_id = "GD" . date('ymdHi') . random_string(4, 'up');
+            $goods->join_goods_category_id = 59;
+            $goods->goods_classify = 'RECHARGE';
+            $goods->goods_status = $params['goods_status'];
+            $goods->goods_sort = $params['goods_sort'];
+            $goods->goods_category = $params['goods_category'];
+            $goods->goods_name = $params['goods_name'];
+            $goods->goods_market_price = $params['goods_sales_price'];
+            $goods->goods_sales_price = $params['goods_sales_price'];
+            $goods->goods_sku_json = json_encode(['规格' => [$params['goods_sales_price'] . '元']]);
+            $goods->goods_attribute_json = json_encode(['added' => ['nbr' => $params['goods_rate'] / 100, 'mode' => 'rate'], 'min-count' => 1]);
+            $goods->goods_cover = '/images/app/common/null-service.png';
+            $goods->goods_process_json = json_encode(['mode' => 'do_shopping']);
+            $goods->goods_addtimes = time();
+            $goods->save();
+
+            $sku = new GoodsSku();
+            $sku->join_sku_goods_id = $goods->goods_id;
+            $sku->goods_sku_status = $params['goods_status'];
+            $sku->goods_sku_specs_json = json_encode(['规格' => $params['goods_sales_price'] . '元']);
+            $sku->goods_sku_market_price = $params['goods_sales_price'];
+            $sku->goods_sku_sales_price = $params['goods_sales_price'];
+            $sku->goods_sku_storage_json = json_encode(['storage' => 9999]);
+            $sku->save();
+
+            Db::commit();
+
+            return json_success('success');
+        } catch (\Exception $e) {
+
+            Db::rollBack();
+
+            return json_fail('添加充值产品失败');
+        }
+    }
+
+    public static function updateRecharge($params)
+    {
+        try {
+            Db::beginTransaction();
+            $goods = Goods::where('goods_id', $params['goods_id'])->first();
+            if (!$goods) {
+                return json_fail("数据异常");
+            }
+            $goods->goods_market_price = $params['goods_sales_price'];
+            $goods->goods_sales_price = $params['goods_sales_price'];
+            $goods->goods_status = $params['goods_status'];
+            $goods->goods_sku_json = json_encode(['规格' => [$params['goods_sales_price'] . '元']]);
+            $goods->goods_sort = $params['goods_sort'];
+            if (!empty($goods->goods_attribute_json)) {
+                $attributeJson = json_decode($goods->goods_attribute_json, true);
+                $attributeJson['added']['nbr'] = $params['goods_rate'] / 100;
+                $goods->goods_attribute_json = json_encode($attributeJson);
+            }
+            $goods->save();
+            $sku = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->where('goods_sku_status','ON')->first();
+            $sku->goods_sku_status = $params['goods_status'];
+            $sku->goods_sku_specs_json = json_encode(['规格' => $params['goods_sales_price'] . '元']);
+            $sku->goods_sku_market_price = $params['goods_sales_price'];
+            $sku->goods_sku_sales_price = $params['goods_sales_price'];
+            $sku->save();
+
+            Db::commit();
+
+            return json_success("success");
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail('编辑充值产品失败');
+        }
+    }
+
+    public static function insertPackage($params): Response
+    {
+        Db::beginTransaction();
+        try {
+            $params['goods_id'] = "GD" . date('ymdHi') . random_string(4, 'up');
+            // 主表
+            self::mainInsert($params);
+            // 商品详情表
+            self::detailInsert($params);
+            // 套包组件表
+            self::componentUpdate($params, 'insert');
+            // 商品标签表
+            self::labelInsert($params);
+            // 产品运行控制信息表
+            self::goodsRunningInsert($params);
+            // sku表
+//            self::goodsSkuSet($params, 'insert');
+            // 待上架状态,上架时间大于当前时间
+            if ($params['goods_status'] == 'PENDING' && strtotime($params['goods_on_addtimes']) > time()) {
+                $redis = Redis::connection();
+                $key = date('YmdHi', strtotime($params['goods_on_addtimes']));
+                $redis->sAdd(Goods::LISTING_KEY_PREFIX . $key, $params['goods_id']);
+            }
+            Db::commit();
+        } catch (\PDOException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail('数据写入失败~');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据写入失败~');
+        }
+
+        _syslog("添加套餐", "商品名【" . $params['goods_name'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function update($params)
+    {
+        Db::beginTransaction();
+        try {
+            // 主表
+            self::mainUpdate($params);
+            // 商品详情表
+            self::detailUpdate($params);
+            // 商品标签表
+            self::labelUpdate($params);
+            // 产品运行控制信息表
+            self::goodsRunningUpdate($params);
+            // sku表
+            self::goodsSkuSet($params, 'update');
+
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            return json_fail('数据更新失败~');
+        }
+
+        _syslog("编辑商品", "商品名【" . $params['goods_name'] . "】" ?? "商品ID:【" . $params['goods_id'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function changeStatus($params)
+    {
+        try {
+            Goods::where('goods_id', $params['goods_id'])->update(['goods_status' => $params['goods_status']]);
+
+            return json_success('修改成功');
+        } catch (\Exception $e) {
+            return json_fail('修改状态失败');
+        }
+    }
+
+
+    public static function updatePackage($params)
+    {
+        Db::beginTransaction();
+        try {
+            // 主表
+            self::mainUpdate($params);
+            // 商品详情表
+            self::detailUpdate($params);
+            // 套包组件表
+            self::componentUpdate($params, 'update');
+            // 商品标签表
+            self::labelUpdate($params);
+            // 产品运行控制信息表
+            self::goodsRunningUpdate($params);
+            // sku表
+            self::goodsSkuSet($params, 'update');
+
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据更新失败~');
+        }
+        _syslog("编辑套餐", "商品名【" . $params['goods_name'] . "】" ?? "商品ID:【" . $params['goods_id'] . "】");
+
+        return json_success('success');
+    }
+
+    /**
+     * @Desc 删除商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:20
+     *
+     * @param $ids
+     * @return Response
+     */
+    public static function delete($ids)
+    {
+        if (!$ids) {
+            return json_fail("数据错误~");
+        }
+        if (!is_array($ids)) {
+            $ids = [$ids];
+        }
+
+        $goods = Goods::whereIn('goods_id', $ids)->get()->toArray();
+        if (!$goods) {
+            return json_fail("数据错误~");
+        }
+
+        // 是否在套包里
+        if (GoodsComponent::whereIn('join_component_goods_id', $ids)->exists()) {
+            return json_fail("当前商品存在于套包中,请先在套包中删除");
+        }
+        // 是否已被购买过
+        if (OrderSheet::whereIn('join_sheet_goods_id', $ids)->exists()) {
+            return json_fail("当前商品已有购买历史,如不在APP显示,请选择【下架】操作");
+        }
+        // 是否预约过
+        if (Appointment::whereIn('join_appointment_goods_id', $ids)->exists()) {
+
+            return json_fail("当前商品已有预约历史,如不在APP显示,请选择【下架】操作");
+        }
+
+        Db::beginTransaction();
+        try {
+            Goods::whereIn('goods_id', $ids)->delete();
+            GoodsDetail::whereIn('join_detail_goods_id', $ids)->delete();
+            GoodsLabel::whereIn('join_label_goods_id', $ids)->delete();
+            GoodsRunning::whereIn('join_running_goods_id', $ids)->delete();
+            GoodsSku::whereIn('join_sku_goods_id', $ids)->delete();
+
+            Db::commit();
+
+            _syslog("删除商品 / 套餐", "ID:【" . implode(',', $ids) . "】", $goods);
+
+            return json_success("商品删除成功");
+        } catch (\Exception $e) {
+            Db::rollBack();
+
+            return json_fail("商品删除失败~");
+        }
+    }
+
+    /**
+     * @Desc 商品主表
+     * @Author Gorden
+     * @Date 2024/3/11 11:20
+     *
+     * @param $params
+     * @return mixed|string
+     * @throws BusinessException
+     */
+    public static function mainInsert($params)
+    {
+        if (!empty($params['goods_cover'])) {
+            $params['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_cover']);
+        }
+        // 如果产品是待处理状态
+        if ($params['goods_status'] == 'PENDING') {
+            if (strtotime($params['goods_on_addtimes']) <= time()) {
+                $params['goods_status'] = 'ON';
+            }
+        }
+        $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+        if (!$category) {
+            throw new BusinessException("产品分类不存在~");
+        }
+        if (empty($params['goods_category'])) {
+            $params['goods_category'] = $category->category_classify;
+        }
+
+        try {
+            $model = new Goods();
+            $model->goods_id = $params['goods_id'];
+            $model->join_goods_category_id = $params['join_goods_category_id'] ?? 0;
+            $model->join_goods_supplier_id = $params['join_goods_supplier_id'] ?? 0;
+            $model->goods_classify = $params['goods_classify'] ?? '';
+            $model->goods_status = $params['goods_status'] ?? '';
+            $model->goods_category = $params['goods_category'] ?? '';
+//            $model->goods_prefix = $params['goods_prefix'] ?? 】($category->category_name ? '【' . $category->category_name . '' : '');
+            $model->goods_prefix = $params['goods_prefix'] ?? '';
+            $model->goods_name = $params['goods_name'];
+            $model->goods_market_price = $params['goods_market_price'] ?? 0;
+            $model->goods_sales_price = $params['goods_sales_price'] ?? 0;
+            $model->goods_sku_json = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+            $model->goods_attribute_json = !empty($params['goods_attribute_json']) ? $params['goods_attribute_json'] : '[]';
+            $model->goods_title = $params['goods_title'] ?? '';
+            $model->goods_cover = $params['goods_cover'] ?? '';
+            $model->goods_on_addtimes = isset($params['goods_on_addtimes']) ? strtotime($params['goods_on_addtimes']) : null;
+            $model->goods_sort = $params['goods_sort'] ?? null;
+            $model->goods_groupby = $params['goods_groupby'] ?? '';
+            $model->goods_remark = $params['goods_remark'] ?? '';
+            $model->goods_extend_json = $params['goods_extend_json'] ?? '{}';
+            $model->is_support_appointment = $params['is_support_appointment'] ?? 'N';
+            $model->creator_user_id = JwtToken::getCurrentId();
+            $model->goods_addtimes = time();
+            $model->goods_updatetimes = time();
+            // {"express":"Y","self":"Y","arrival":"Y"}
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $model->goods_express_json = json_encode($expressJson);
+            }
+
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times']) && $params['goods_category'] != 'TRAVEL') {
+                $times = [];
+                $attributeJsonTimeArr = [];
+                $personTotal = 0;
+                foreach ($params['appointment_times'] as $time) {
+                    $attributeJsonTimeArr[] = strtotime(date('Y-m-d ') . $time['appointmentTimeStart']);
+                    $attributeJsonTimeArr[] = strtotime(date('Y-m-d ') . $time['appointmentTimeEnd']);
+                    $personTotal += $time['person'];
+                    $times[$time['appointmentTimeStart']] = [
+                        'person' => $time['person'],
+                        'duration' => $time['appointmentTimeStart'] . '-' . $time['appointmentTimeEnd']
+                    ];
+                }
+                $attributeJsonTime = date('H:i', min($attributeJsonTimeArr)) . '至' . date('H:i', max($attributeJsonTimeArr));
+
+                $newDates = [];
+                foreach ($params['dates'] as $date) {
+                    $key = self::$week[$date];
+                    $newDates[$key] = $date;
+                }
+                ksort($newDates);
+
+
+                $currentDate = current($newDates);
+                $lastDate = end($newDates);
+
+                $attributeJsonDate = '';
+                if (self::$week[$lastDate] - self::$week[$currentDate] + 1 > count($newDates)) {
+                    $attributeJsonDate = implode(',', $newDates);
+                } else if (self::$week[$lastDate] - self::$week[$currentDate] + 1 == count($newDates)) {
+                    $attributeJsonDate = $currentDate . '至' . $lastDate;
+                }
+
+                // if (isset($params['work_time'])){
+                //     $workTimeStart = date('H:i',strtotime($params['work_time'][0]));
+                //     $workTimeEnd = date('H:i',strtotime($params['work_time'][1]));
+                //     $attributeJsonTime = $workTimeStart.'至'.$workTimeEnd;
+                // }
+
+                $attributeJson = [
+                    'icon' => $model->goods_cover,
+                    'date' => $attributeJsonDate,
+                    // 'time' => $attributeJsonTime,
+                    'dates' => $newDates ? array_values($newDates) : [],
+                    'times' => $times,
+                    'person' => $personTotal
+                ];
+                // if(isset($params['address'])){
+                //     $attributeJson['address'] = $params['address'];
+                // }
+                // if (isset($params['min_count'])){
+                //     $attributeJson['min-count'] = $params['min_count'];
+                // }else{
+                //     $attributeJson['min-count'] = 1;
+                // }
+                // if (isset($params['teachers'])){
+                //     $attributeJson['teachers'] = $params['teachers'];
+                // }
+                if (!empty($params['appointment_label'])) {
+                    $attributeJson['label'] = $params['appointment_label'];
+                }
+                // if (!empty($params['address'])) {
+                //     $attributeJson['address'] = $params['address'];
+                // }
+                // if (!empty($params['position'])){
+                //     $attributeJson['position'] = $params['position'];
+                // }
+                // if (isset($params['goods_service_premises'])){
+                //     $attributeJson['service_premises_id'] = $params['goods_service_premises'];
+                // }
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+
+//                $times = [];
+//                foreach ($params['appointment_times'] as $time) {
+//                    $times[$time['appointmentTimeStart']] = [
+//                        'person' => $time['person'],
+//                        'duration' => $time['appointmentTimeStart'] . '-' . $time['appointmentTimeEnd']
+//                    ];
+//                }
+//                $model->goods_attribute_json = json_encode([
+//                    'icon' => '',
+//                    'dates' => $params['dates'] ?? [],
+//                    'times' => $times
+//                ]);
+            }
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times']) && $params['goods_category'] == 'TRAVEL') {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['travel-day'] = $params['travel_day'];
+                $attributeJson['travel-begin'] = $params['travel_begin'];
+                $attributeJson['travel-night'] = $params['travel_night'];
+                $attributeJson['travel-trans'] = $params['travel_trans'];
+
+                $unixs = [];
+                foreach ($params['appointment_times'] as $times) {
+                    $unix = strtotime($times['days']);
+                    $unixs[$unix] = $times['person'];
+                }
+                ksort($unixs);
+                $months = [];
+                foreach ($unixs as $key => $unix) {
+                    $month = date('Ym', $key);
+                    $day = date('d', $key);
+                    $months[$month][$day] = $unix;
+                }
+                $attributeJson['month'] = $months;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if (!empty($params['goods_json']) && $params['join_goods_category_id'] == 65) {
+                $goodsJson = json_decode($params['goods_json'], true);
+//                foreach ($goodsJson as $key => $item) {
+//                    $goodsJson[$key]['color'] = hexToRgb($item['color']);
+//                }
+//
+                $model->goods_json = json_encode($goodsJson);
+            } elseif (!empty($params['goods_json']) && $params['join_goods_category_id'] == 43) {
+                $goodsJson = json_decode($params['goods_json'], true);
+                $newGoodsJson = [];
+                foreach ($goodsJson as $item1) {
+                    if (empty($item1['title'])) {
+                        continue;
+                    }
+                    $newItem1 = [];
+                    foreach ($item1['items'] as $item2) {
+                        $newParams = [];
+                        foreach ($item2['params'] as $param) {
+                            if (!empty($param[0]) || !empty($param[1])) {
+                                $newParams[] = $param;
+                            }
+                        }
+                        $newItem1['items'][$item2['key']] = $newParams;
+                    }
+                    $newItem1['service'] = $item1['service'] ?? '';
+                    $newGoodsJson[$item1['title']] = $newItem1;
+                }
+                $model->goods_json = json_encode($newGoodsJson);
+            } else {
+                $model->goods_json = '[]';
+            }
+
+            if (!empty($params['goods_premisses'])) {
+                $attributeJson = [];
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['premisses'] = $params['goods_premisses'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+
+            if (!empty($params['goods_theme_color']) && !empty($params['goods_theme_icon'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['bg'] = $params['goods_theme_color'];
+                $attributeJson['icon'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_theme_icon']);
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['address'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['address'] = $params['address'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['position'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['position'] = $params['position'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['goods_service_premises'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['service_premises_id'] = $params['goods_service_premises'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['min_count'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['min-count'] = $params['min_count'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['teachers'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['teachers'] = $params['teachers'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['work_time'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+
+                $workTimeStart = date('H:i', strtotime($params['work_time'][0]));
+                $workTimeEnd = date('H:i', strtotime($params['work_time'][1]));
+                $attributeJsonTime = $workTimeStart . '至' . $workTimeEnd;
+
+                $attributeJson['time'] = $attributeJsonTime;
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if ($model->save()) {
+                return $model->goods_id;
+            }
+            // 异常
+            throw new BusinessException("数据写入失败~");
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 详情表
+     * @Author Gorden
+     * @Date 2024/3/11 11:19
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailInsert($params)
+    {
+        if (!empty($params['goods_detail_slider_json'])) {
+            $params['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_detail_slider_json']);
+            $params['goods_detail_slider_json'] = json_encode(['slider' => $params['goods_detail_slider_json']]);
+        }
+
+        if (isset($params['curriculum'])) {
+            $params['goods_detail_specs_json'] = json_encode([
+                [
+                    'key' => '课时',
+                    'val' => $params['curriculum']['period'] ?? '',
+                ],
+                [
+                    'key' => '群体',
+                    'val' => $params['curriculum']['group'] ?? '',
+                ],
+                [
+                    'key' => '课程',
+                    'val' => $params['curriculum']['course'] ?? '',
+                ],
+            ]);
+        }
+        try {
+            $model = new GoodsDetail();
+            $model->join_detail_goods_id = $params['goods_id'];
+            $model->goods_detail_code_json = $params['goods_detail_code_json'] ?? '{}';
+            $model->goods_detail_slider_json = $params['goods_detail_slider_json'] ?? '{}';
+            $model->goods_detail_specs_json = $params['goods_detail_specs_json'] ?? '{}';
+            $model->goods_detail_content = $params['goods_detail_content'] ?? '';
+            if (!$model->save()) {
+                // 异常
+                throw new BusinessException("轮播图/详情数据写入失败~");
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("轮播图/详情数据写入失败~");
+        }
+    }
+
+    public static function componentUpdate($params, $type = 'insert')
+    {
+        Db::beginTransaction();
+        try {
+            $goodsSku = '';
+            // 有先删除
+            if ($type == 'update') {
+                GoodsComponent::where('join_component_master_goods_id', $params['goods_id'])->delete();
+                $goodsSku = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->first();
+                if ($goodsSku) {
+                    $goodsSku->goods_sku_market_price = $params['goods_market_price'] ?? 0;
+                    $goodsSku->goods_sku_sales_price = $params['goods_sales_price'] ?? 0;
+                    $goodsSku->save();
+                }
+            }
+            if ($type == 'insert' || !$goodsSku) {
+                Goods::where('goods_id', $params['goods_id'])->update(['goods_sku_json' => '{"规格": ["标准"]}']);
+                $skuData = [
+                    'join_sku_goods_id' => $params['goods_id'],
+                    'goods_sku_status' => 'ON',
+                    'goods_sku_specs_json' => '{"规格": "标准"}',
+                    'goods_sku_title' => "标准" . $params['goods_name'],
+                    'goods_sku_market_price' => $params['goods_market_price'] ?? 0,
+                    'goods_sku_sales_price' => $params['goods_sales_price'] ?? 0,
+                ];
+                GoodsSku::insert($skuData);
+            }
+            $data = [];
+            foreach ($params['goods_content_list'] as $item) {
+                if (!in_array($item['goods_id'], $params['join_component_goods_id'])) {
+                    continue;
+                }
+                $goods = Goods::where('goods_id', $params['goods_id'])->first();
+                if (!$goods) {
+                    continue;
+                }
+
+                $data[] = [
+                    'join_component_master_goods_id' => $params['goods_id'],
+                    'join_component_goods_id' => $item['goods_id'] ?? '',
+                    'join_component_goods_sku_id' => $item['sku_id'] ?? '',
+                    'goods_component_price' => $item['goods_sales_price'] ?? '',
+                    'goods_component_cover' => $goods->goods_cover,
+                    'goods_component_json' => json_encode(['goods_name' => $item['goods_name'], 'nbr' => $item['nbr'], 'sku_id' => $item['sku_id'] ?? '']),
+                    'goods_component_addtimes' => time()
+                ];
+            }
+
+            if ($data) {
+                GoodsComponent::insert($data);
+            }
+            Db::commit();
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            throw new BusinessException("数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 标签表
+     * @Author Gorden
+     * @Date 2024/3/11 11:32
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelInsert($params)
+    {
+        $model = new GoodsLabel();
+        $model->join_label_goods_id = $params['goods_id'];
+        $model->goods_label = $params['goods_label'] ? implode(',', $params['goods_label']) : '';
+        $model->goods_label_extend_json = !empty($params['goods_label_extend_json']) ? $params['goods_label_extend_json'] : '{}';
+        if (!$model->save()) {
+            // 异常
+            throw new BusinessException('数据写入失败~');
+        }
+    }
+
+    /**
+     * @Desc 产品运行控制信息表
+     * @Author Gorden
+     * @Date 2024/3/11 11:38
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningInsert($params)
+    {
+        try {
+            $model = new GoodsRunning();
+            $model->join_running_goods_id = $params['goods_id'];
+            $model->goods_running_storage = $params['goods_running_storage'] ?? 0;
+            $model->goods_running_sale = $params['goods_running_sale'] ?? 0;
+            $model->goods_running_off_type = !empty($params['goods_running_off_type']) ? $params['goods_running_off_type'] : '';
+            $model->goods_running_off_json = !empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes']) ? json_encode(['time' => strtotime($params['goods_off_addtimes'])]) : '[]';
+            if (!$model->save()) {
+                throw new BusinessException('数据写入失败');
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据写入失败');
+        }
+    }
+
+    /**
+     * @Desc SKU
+     * @Author Gorden
+     * @Date 2024/3/11 12:01
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function skuInsert($params)
+    {
+        $model = new GoodsSku();
+        $model->join_sku_goods_id = $params['goods_id'];
+        $model->goods_sku_status = $params['goods_sku_status'];
+        $model->goods_sku_specs_json = $params['goods_sku_specs_json'];
+        $model->goods_sku_title = $params['goods_sku_title'];
+        $model->goods_sku_images_json = $params['goods_sku_images_json'];
+        $model->goods_sku_content = $params['goods_sku_content'];
+        $model->goods_sku_market_price = $params['goods_sku_market_price'];
+        $model->goods_sku_sales_price = $params['goods_sku_sales_price'];
+        $model->goods_sku_storage_json = $params['goods_sku_storage_json'];
+        $model->goods_sku_service_json = $params['goods_sku_service_json'];
+        $model->goods_sku_extend_json = $params['goods_sku_extend_json'];
+        if (!$model->save()) {
+            throw new BusinessException('规格数据写入失败~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:44
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function mainUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new Goods());
+            if (!empty($data['goods_cover'])) {
+                $data['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_cover']);
+            }
+            $data['goods_on_addtimes'] = isset($data['goods_on_addtimes']) ? strtotime($data['goods_on_addtimes']) : 0;
+            $data['goods_sku_json'] = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+
+            $row = Goods::find($data['goods_id']);
+            if ($row->join_goods_category_id != $data['join_goods_category_id']) {
+                $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+                if (!$category) {
+                    throw new BusinessException("产品分类不存在~");
+                }
+                $data['goods_category'] = $category->category_classify ?? '';
+                $data['goods_prefix'] = $data['goods_prefix'] ?? ($category->category_name ? '【' . $category->category_name . '】' : '');
+            }
+
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $data['goods_express_json'] = json_encode($expressJson);
+            }
+            $attributeJson = [];
+            if (!empty($row->goods_attribute_json)) {
+                $attributeJson = json_decode($row->goods_attribute_json, true);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+            }
+            if (!empty($params['attribute_account'])){
+                $attributeJson['account'] = $params['attribute_account'];
+            }
+            if (!empty($params['attribute_control'])){
+                $attributeJson['control'] = $params['attribute_control'];
+            }
+
+            $data['goods_attribute_json'] = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+
+            foreach ($data as $key => $val) {
+                $row->{$key} = $val;
+            }
+            $row->goods_updatetimes = time();
+            $row->updator_user_id = JwtToken::getCurrentId();
+            $row->save();
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getTrace());
+            throw new BusinessException('数据更新异常~1');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:57
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsDetail());
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_detail_slider_json']);
+                $data['goods_detail_slider_json'] = json_encode(['slider' => $data['goods_detail_slider_json']]);
+            }
+            if (isset($params['curriculum'])) {
+                $data['goods_detail_specs_json'] = json_encode([
+                    [
+                        'key' => '课时',
+                        'val' => $params['curriculum']['period'] ?? '',
+                    ],
+                    [
+                        'key' => '群体',
+                        'val' => $params['curriculum']['group'] ?? '',
+                    ],
+                    [
+                        'key' => '课程',
+                        'val' => $params['curriculum']['course'] ?? '',
+                    ],
+                ]);
+            }
+            // 根据goods_id 查详情ID
+            $detail = GoodsDetail::where('join_detail_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->join_detail_goods_id, $data, new GoodsDetail());
+            } else {
+                $data['join_detail_goods_id'] = $params['goods_id'];
+                GoodsDetail::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('轮播图/详情数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:58
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsLabel());
+            // 根据goods_id 查详情ID
+            $detail = GoodsLabel::where('join_label_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->goods_label_id, $data, new GoodsLabel());
+            } else {
+                $data['join_label_goods_id'] = $params['goods_id'];
+                GoodsLabel::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~3');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:59
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsRunning());
+            // 根据goods_id 查详情ID
+            $detail = GoodsRunning::where('join_running_goods_id', $params['goods_id'])->first();
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T') {
+                $redis = Redis::connection();
+                if (!empty($detail->goods_running_off_json)) {
+                    $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                    if (isset($goodsRunningOffJson['time'])) {
+                        $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                        // 有老的下架时间,删除老的
+                        $redis->srem($oldKey, $params['goods_id']);
+                    } else {
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                    }
+                } else {
+                    $data['goods_running_off_json'] = json_encode(['time' => strtotime($params['goods_off_addtimes'])]);
+                }
+                // 加入自动下架
+                $newKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($newKey, $params['goods_id']);
+
+            } else if (!empty($params['goods_running_off_type']) && !empty($detail->goods_running_off_type) && $params['goods_running_off_type'] == 'H' && $detail->goods_running_off_type == 'T') {
+                $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                if (isset($goodsRunningOffJson['time'])) {
+                    $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                    $redis = Redis::connection();
+                    $redis->srem($oldKey, $params['goods_id']);
+                }
+            }
+            if ($detail) {
+                self::doUpdate($detail->join_running_goods_id, $data, new GoodsRunning());
+            } else {
+                // 兼容老数据……
+                $data['join_running_goods_id'] = $params['goods_id'];
+                GoodsRunning::insert($data);
+            }
+
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc sku 设置
+     * @Author Gorden
+     * @Date 2024/4/10 10:43
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsSkuSet($params, $operation = 'insert')
+    {
+        try {
+            Db::beginTransaction();
+            $skusOldIds = [];
+            if ($operation == 'update') {
+                // 查出所有的
+                $skusOldIds = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->pluck('goods_sku_id', 'goods_sku_id');
+
+                // 删掉原有的
+//                GoodsSku::where('join_sku_goods_id', $params['goods_id'])->delete();
+            }
+            if (empty($skusOldIds) && empty($params['goods_sku_json_value'])) {
+                $skuData = [
+                    'join_sku_goods_id' => $params['goods_id'],
+                    'goods_sku_status' => 'ON',
+                    'goods_sku_specs_json' => '{"规格": "标准"}',
+                    'goods_sku_title' => "标准" . $params['goods_name'],
+                    'goods_sku_market_price' => $params['goods_market_price'] ?? 0,
+                    'goods_sku_sales_price' => $params['goods_sales_price'] ?? 0,
+                ];
+                GoodsSku::insert($skuData);
+            }
+            // 入新的
+            if (!empty($params['goods_sku_json_value'])) {
+                foreach ($params['goods_sku_json_value'] as $item) {
+                    $skus = explode(',', $item['sku']);
+                    $skuArr = [];
+                    for ($i = 1; $i <= count($skus); $i++) {
+                        $skuName = "skuName" . $i;
+                        $key = $item[$skuName];
+                        $skuArr[$key] = $skus[$i - 1];
+                    }
+                    $specsJson = json_encode($skuArr);
+                    $skuTitle = str_replace('-', ',', $item['sku']) . $params['goods_name'];
+                    if ($operation == 'update' && !empty($item['sku_id'])) {
+                        $model = GoodsSku::where('goods_sku_id', $item['sku_id'])->where('goods_sku_status','ON')->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+                    } else {
+                        $model = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->where('goods_sku_status','ON')->where('goods_sku_title', $skuTitle)->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+
+                    }
+
+                    $model->join_sku_goods_id = $params['goods_id'];
+                    $model->goods_sku_status = $params['goods_status'];
+                    $model->goods_sku_specs_json = $specsJson;
+                    $model->goods_sku_title = $skuTitle;
+                    $model->goods_sku_market_price = $params['goods_market_price'] ?? 0;
+                    $model->goods_sku_sales_price = $item['price'];
+                    $model->goods_sku_storage_json = json_encode(['storage' => $item['stock']]);
+                    $model->save();
+
+                }
+            }
+            if ($operation == 'update' && !empty($skusOldIds)) {
+                // GoodsSku::whereIn('goods_sku_id', $skusOldIds)->delete();
+                GoodsSku::whereIn('goods_sku_id', $skusOldIds)->update(['goods_sku_status' => 'DISABLED']);
+            }
+
+            Db::commit();
+        } catch (\Exception $e) {
+            dump($e->getTrace());
+            Db::rollBack();
+
+            throw new BusinessException('规格数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:45
+     *
+     * @param array $data
+     * @param $model
+     * @return array
+     * @throws BusinessException
+     */
+    private static function inputFilter(array $data, $model): array
+    {
+        $table = config('database.connections.mysql.prefix') . $model->getTable();
+        $allow_column = $model->getConnection()->select("desc `$table`");
+        if (!$allow_column) {
+            throw new BusinessException('表不存在', 2);
+        }
+        $columns = array_column($allow_column, 'Type', 'Field');
+        foreach ($data as $col => $item) {
+            if (!isset($columns[$col])) {
+                unset($data[$col]);
+                continue;
+            }
+            // 非字符串类型传空则为null
+            if ($item === '' && strpos(strtolower($columns[$col]), 'varchar') === false && strpos(strtolower($columns[$col]), 'text') === false) {
+                $data[$col] = null;
+            }
+            if (is_array($item)) {
+                $data[$col] = implode(',', $item);
+            }
+            if ($item != '' && (strpos(strtolower($columns[$col]), 'varchar') || strpos(strtolower($columns[$col]), 'text'))) {
+//                $data[$col] = htmlspecialchars($item);
+            }
+        }
+        if (empty($data['created_at'])) {
+            unset($data['created_at']);
+        }
+        if (empty($data['updated_at'])) {
+            unset($data['updated_at']);
+        }
+        return $data;
+    }
+
+    /**
+     * @Desc 执行更新
+     * @Author Gorden
+     * @Date 2024/3/12 8:43
+     *
+     * @param $id
+     * @param $data
+     * @param $model
+     * @return void
+     */
+    private static function doUpdate($id, $data, $model)
+    {
+        $row = $model->find($id);
+        foreach ($data as $key => $val) {
+            $row->{$key} = $val;
+        }
+        $row->save();
+    }
+
+    /**
+     * @Desc 上架定时
+     * @Author Gorden
+     * @Date 2024/4/11 15:13
+     *
+     * @return void
+     */
+    public static function checkListing()
+    {
+        $key = Goods::LISTING_KEY_PREFIX . date('YmdHi');
+        $redis = Redis::connection();
+        if (!$redis->exists($key)) {
+            return;
+        }
+
+        $goodsIds = $redis->sMembers($key);
+        if (Goods::whereIn('goods_id', $goodsIds)->update(['goods_status' => 'ON'])) {
+            $redis->del($key);
+        }
+    }
+
+    /**
+     * @Desc 自动下架
+     * @Author Gorden
+     * @Date 2024/4/26 15:26
+     *
+     * @return void
+     */
+    public static function checkOffListing()
+    {
+        $key = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi');
+        $redis = Redis::connection();
+        if (!$redis->exists($key)) {
+            return;
+        }
+
+        $goodsIds = $redis->sMembers($key);
+        if (Goods::whereIn('goods_id', $goodsIds)->update(['goods_status' => 'OFF'])) {
+            $redis->del($key);
+        }
+    }
+
+    public static $week = [
+        '周一' => 1,
+        '周二' => 2,
+        '周三' => 3,
+        '周四' => 4,
+        '周五' => 5,
+        '周六' => 6,
+        '周日' => 7,
+    ];
+}

+ 2119 - 0
app/admin/service/goods/ReferrerService.php

@@ -0,0 +1,2119 @@
+<?php
+
+namespace app\admin\service\goods;
+
+use app\common\Tree;
+use app\model\Appointment;
+use app\model\Coupon;
+use app\model\Goods;
+use app\model\GoodsComponent;
+use app\model\GoodsDetail;
+use app\model\GoodsLabel;
+use app\model\GoodsRunning;
+use app\model\GoodsSku;
+use app\model\OrderSheet;
+use app\model\Supplier;
+use app\model\SysCategory;
+use app\model\SysDept;
+use app\model\SysSerial;
+use app\model\SysUser;
+use support\Db;
+use support\exception\BusinessException;
+use support\Redis;
+use support\Request;
+use support\Response;
+use Tinywan\Jwt\JwtToken;
+
+class ReferrerService
+{
+    public static function selectAll($goodsIds)
+    {
+        $goods = Goods::where('goods_status', 'ON')
+            ->when($goodsIds != '', function ($query) use ($goodsIds) {
+                $query->whereIn('goods_id', $goodsIds);
+            })
+            ->select('goods_id', 'goods_name')
+            ->get();
+
+        return json_success('', $goods);
+    }
+
+    public static function selectAllByGoodsName($goodsName)
+    {
+        $goods = Goods::with('sku')
+            ->where('goods_status', 'ON')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where('goods_name', 'like', "%" . $goodsName . '%');
+            })
+            ->select('goods_id', 'goods_name', 'goods_classify')
+            ->get()
+            ->toArray();
+        foreach ($goods as &$item) {
+            if (!empty($item['sku'])) {
+                foreach ($item['sku'] as $key => $sku) {
+                    $specsJson = json_decode($sku['goods_sku_specs_json'], true);
+                    $skuTitle = '';
+                    foreach ($specsJson as $item2) {
+                        if (is_array($item2)) {
+                            $item2 = implode(',', $item2);
+                        }
+                        $skuTitle .= $item2 . '-';
+                    }
+                    $item['sku'][$key]['goods_sku_id'] = strval($item['sku'][$key]['goods_sku_id']);
+                    $item['sku'][$key]['goods_sku_title'] = rtrim($skuTitle, '-');
+                }
+            }
+        }
+
+        return json_success('', $goods);
+    }
+
+    public static function selectAllByCategoryForRuleAddComponent($category = "GOODS")
+    {
+        $categoryIds = [];
+        $categorySuperIds = [];
+        if ($category == 'GOODS') {
+            $categorySuperIds = [5];
+        } elseif ($category == 'SERVICE') {
+            $categorySuperIds = [31, 154, 42, 65, 30, 66, 72, 70];
+        }
+        $categorys = SysCategory::whereIn('category_id', $categorySuperIds)->get()->toArray();
+        foreach ($categorys as $item) {
+            if (empty($item['category_super_path'])) {
+                $item['category_super_path'] = '#' . $item['category_id'] . '#';
+            } else {
+                $item['category_super_path'] = $item['category_super_path'] . '#' . $item['category_id'] . '#';
+            }
+
+            $categoryIds = SysCategory::where('category_super_path', 'like', '%' . $item['category_super_path'] . '%')->pluck('category_id');
+            $categoryIds = $categoryIds ? $categoryIds->toArray() : [];
+            $categorySuperIds = array_merge($categorySuperIds, $categoryIds);
+        }
+
+        $categoryIds = array_unique($categorySuperIds);
+
+        $goods = Goods::with('sku')
+//            ->where('goods_classify', $category)
+            ->when(!empty($categoryIds), function ($query) use ($categoryIds) {
+                $query->whereIn('join_goods_category_id', $categoryIds);
+            })->when(empty($categoryIds), function ($query) {
+                $query->where('goods_classify', '<>', 'RECHARGE');
+            })->where('goods_status', 'ON')
+            ->select('goods_id', 'goods_name', 'join_goods_category_id')
+            ->get()
+            ->toArray();
+
+        foreach ($goods as &$good) {
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $specsJson = json_decode($sku['goods_sku_specs_json'], true);
+                        $skuTitle = '';
+                        foreach ($specsJson as $item) {
+                            if (is_array($item)) {
+                                $item = implode(',', $item);
+                            }
+                            $skuTitle .= $item . '-';
+                        }
+                        $good['sku'][$key]['goods_sku_id'] = strval($good['sku'][$key]['goods_sku_id']);
+                        $good['sku'][$key]['goods_sku_title'] = rtrim($skuTitle, '-');
+                    }
+                    unset($good['sku'][$key]['goods_sku_specs_json'], $good['sku'][$key]['join_sku_goods_id']);
+                }
+            } else {
+                $good['sku'] = [];
+            }
+
+        }
+
+        return json_success('', $goods);
+    }
+
+    public static function selectPremisesByGoodsId($goodsId)
+    {
+        $goods = Goods::where('goods_id', $goodsId)
+            ->select('goods_id', 'goods_attribute_json')
+            ->first();
+        $premisses = [];
+        if (!empty($goods->goods_attribute_json)) {
+            $attributeJson = json_decode($goods->goods_attribute_json, true);
+            if (isset($attributeJson['premisses'])) {
+                $premisses = SysDept::whereIn('dept_id', $attributeJson['premisses'])
+                    ->select('dept_id', 'dept_name')
+                    ->get()
+                    ->toArray();
+            }
+        }
+
+        return json_success('', $premisses);
+    }
+
+    public static function select(Request $request, $classify = "GOODS")
+    {
+        $page = $request->get('page', 1);
+        $pageSize = $request->get('pageSize', 20);
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+        $goodsCategory = $request->get('goods_category', null);
+        $goodsSupplierId = $request->get('join_goods_supplier_id', null);
+        $goodsStatus = $request->get('goods_status', null);
+        $type = $request->get('type', '');
+        if ($categoryId != null) {
+            $categoryPath = SysCategory::where('category_id', $categoryId)->value('category_super_path');
+            $categoryPath .= '#' . $categoryId . '#';
+            $categoryIds = SysCategory::where('category_super_path', 'like', $categoryPath . '%')->pluck('category_id')->toArray();
+            $categoryIds[] = intval($categoryId);
+            if (!empty($categoryIds)) {
+                $categoryId = $categoryIds;
+            } else {
+                $categoryId = [intval($categoryId)];
+            }
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_attribute_json', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->whereIn('join_goods_category_id', $categoryId);
+            })->when($goodsCategory != null, function ($query) use ($goodsCategory) {
+                $query->where('goods_category', $goodsCategory);
+            })
+            ->when($classify != '', function ($query) use ($classify, $categoryId) {
+                if ($classify == 'GOODS' && empty($categoryId)) {
+                    $query->whereIn('join_goods_category_id', ['6', '7', '8', '9', '10', '11', '12', '30']);
+                } else if ($classify != 'GOODS' && empty($categoryId)) {
+                    $query->where('goods_classify', $classify);
+                }
+            })->when(!empty($type), function ($query) use ($type) {
+                if ($type == 'storageWarning') {
+                    $query->where('goods_running.goods_running_storage', '<=', 2);
+                }
+            })->when(!empty($goodsSupplierId), function ($query) use ($goodsSupplierId) {
+                $query->where('join_goods_supplier_id', $goodsSupplierId);
+            })->when(!empty($goodsStatus), function ($query) use ($goodsStatus) {
+                $query->where('goods_status', $goodsStatus);
+            })
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+
+        $total = Goods::leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->whereIn('join_goods_category_id', $categoryId);
+            })->when($goodsCategory != null, function ($query) use ($goodsCategory) {
+                $query->where('goods_category', $goodsCategory);
+            })->when($classify != '', function ($query) use ($classify, $categoryId) {
+                if ($classify == 'GOODS' && empty($categoryId)) {
+                    $query->whereIn('join_goods_category_id', ['6', '7', '8', '9', '10', '11', '12', '30']);
+                } else if ($classify != 'GOODS' && empty($categoryId)) {
+                    $query->where('goods_classify', $classify);
+                }
+            })->when(!empty($type), function ($query) use ($type) {
+                if ($type == 'storageWarning') {
+                    $query->where('goods_running.goods_running_storage', '<=', 2);
+                }
+            })->when(!empty($goodsSupplierId), function ($query) use ($goodsSupplierId) {
+                $query->where('join_goods_supplier_id', $goodsSupplierId);
+            })->when(!empty($goodsStatus), function ($query) use ($goodsStatus) {
+                $query->where('goods_status', $goodsStatus);
+            })
+            ->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+            if (!empty($row['goods_attribute_json'])) {
+                $row['goods_attribute_json'] = json_decode($row['goods_attribute_json']);
+            }
+            if (!empty($row['goods_category']) && $row['goods_category'] == 'INDEX') {
+                $row['goods_recommend_index'] = 'INDEX';
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectSpecial(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+        if ($categoryId == null) {
+            $categoryId = [65, 43];
+        } elseif (is_string($categoryId)) {
+            $categoryId = [$categoryId];
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->whereIn('join_goods_category_id', $categoryId)
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%');
+//                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->whereIn('join_goods_category_id', $categoryId)->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectPicking(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+
+        $categorySuperId = $request->get('category_super_id', '');
+        $categoryIds = $request->get('join_goods_category_id', []);
+        if (!empty($categorySuperId) && is_array($categoryIds)) {
+            $category = SysCategory::where('category_id', $categorySuperId)->first();
+            if (empty($category->category_super_path)) {
+                $category->category_super_path = '#' . $categorySuperId . '#';
+            } else {
+                $category->category_super_path = $category->category_super_path . '#' . $categorySuperId . '#';
+            }
+            $categoryIds = SysCategory::where('category_super_path', 'like', '%' . $category->category_super_path)->pluck('category_id');
+            $categoryIds = $categoryIds ? $categoryIds->toArray() : [];
+            $categoryIds = array_merge($categoryIds, [$categorySuperId]);
+        } elseif (!is_array($categoryIds)) {
+            $categoryIds = [$categoryIds];
+        }
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->whereIn('join_goods_category_id', $categoryIds)
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->whereIn('join_goods_category_id', $categoryIds)
+            ->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    public static function selectPackage(Request $request)
+    {
+        $page = $request->get('page');
+        $pageSize = $request->get('pageSize');
+        $goodsName = $request->get('goods_name', '');
+        $categoryId = $request->get('join_goods_category_id', null);
+
+        $rows = Goods::with([
+            'category' => function ($query) {
+                $query->select('category_id', 'category_name');
+            },
+            'running' => function ($query) {
+                $query->select('join_running_goods_id', 'goods_running_storage');
+            },
+            'supplier' => function ($query) {
+                $query->select('supplier_id', 'supplier_name');
+            },
+            'user' => function ($query) {
+                $query->select('user_id', 'user_name');
+            }
+        ])->select('goods_id', 'join_goods_category_id', 'join_goods_supplier_id', 'creator_user_id', 'goods_status', 'goods_sales_price', 'goods_category', 'goods_name', 'goods_title', 'goods_cover', 'goods_sort', 'goods_addtimes', 'goods_updatetimes')
+            ->when($goodsName != '', function ($query) use ($goodsName) {
+                $query->where(function ($q) use ($goodsName) {
+                    $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                        ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+                });
+            })->when($categoryId != null, function ($query) use ($categoryId) {
+                $query->where('join_goods_category_id', $categoryId);
+            })
+            ->where('goods_classify', 'PACKAGE')
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        $total = Goods::when($goodsName != '', function ($query) use ($goodsName) {
+            $query->where(function ($q) use ($goodsName) {
+                $q->where('goods_name', 'like', '%' . $goodsName . '%')
+                    ->OrWhere('goods_title', 'like', '%' . $goodsName . '%');
+            });
+        })->when($categoryId != null, function ($query) use ($categoryId) {
+            $query->where('join_goods_category_id', $categoryId);
+        })->where('goods_classify', 'PACKAGE')->count();
+
+        foreach ($rows as &$row) {
+            $row['goods_cover'] = getenv('STORAGE_DOMAIN') . $row['goods_cover'];
+            if (isset($row['running'])) {
+                $row['running']['goods_running_storage'] = intval($row['running']['goods_running_storage']);
+            }
+//            if (!empty($row['component'])) {
+//                $ids = [];
+//                $contentList = [];
+//                foreach ($row['component'] as $component) {
+//                    $ids[] = $component['join_component_goods_id'];
+//                    $configJson = json_decode($component['goods_component_config_json'], true);
+//                    $contentList[] = [
+//                        'goods_name' => $configJson['goods_name'] ?? '',
+//                        'goods_sales_price' => $component['goods_component_price'],
+//                        'nbr' => $configJson['nbr'] ?? 0
+//                    ];
+//                }
+//
+//                $row['join_component_goods_id'] = $ids;
+//                $row['goodsContentList'] = $contentList;
+//            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
+    /**
+     * @Desc 下拉选择服务商品
+     * @Author Gorden
+     * @Date 2024/4/24 13:32
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public static function selectList(Request $request, $goodsClassify = "SERVICE")
+    {
+        $keywords = $request->get('keywords', '');
+        $isSupportAppointment = $request->get('is_support_appointment', '');
+//        if (!$keywords){
+//            return json_success('暂无数据');
+//        }
+
+//        $categoryIds = SysCategory::whereIn('category_super_id', [5, 31, 32, 42, 66, 70, 72])->pluck('category_id');
+
+        $goods = Goods::with('sku')
+//            ->whereIn('join_goods_category_id', $categoryIds)
+            ->when($keywords != '', function ($query) use ($keywords) {
+                $query->where('goods_name', 'like', "%" . $keywords . "%");
+            })
+            ->when($goodsClassify != '', function ($query) use ($goodsClassify) {
+                if ($goodsClassify == 'NOPACKAGE') {
+                    $query->where('goods_classify', '<>', 'PACKAGE');
+                } else if ($goodsClassify == 'SERVICE') {
+                    $query->whereIn('goods_classify', ['SERVICE', 'CHNMED', 'CHNNCD']);
+                } else {
+                    $query->where('goods_classify', $goodsClassify);
+                }
+
+            })
+            ->when($isSupportAppointment != '', function ($query) use ($isSupportAppointment) {
+                $query->where('is_support_appointment', $isSupportAppointment);
+            })
+            ->select('goods_id', 'goods_name', 'goods_sales_price', 'join_goods_category_id', 'goods_attribute_json')
+            ->get()
+            ->toArray();
+
+        foreach ($goods as &$good) {
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $good['sku'][$key]['goods_sku_specs_json'] = json_decode($sku['goods_sku_specs_json']);
+
+                        $skuName = '';
+                        foreach ($good['sku'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . ' ' . $specsKey . ':' . implode(' ', $skuSpecs) . ';';
+                            } else {
+                                $skuName = $skuName . ' ' . $specsKey . ':' . $skuSpecs . ';';
+                            }
+                        }
+                        $good['sku'][$key]['sku_name'] = $skuName;
+                    }
+                }
+            } else {
+                $good['sku'] = [];
+            }
+            $good['premisses'] = [];
+            if (!empty($good['goods_attribute_json'])) {
+                $attributeJson = json_decode($good['goods_attribute_json'], true);
+                if (isset($attributeJson['premisses'])) {
+                    $premisses = SysDept::when(!empty($attributeJson['premisses']), function ($query) use ($attributeJson) {
+                        $query->whereIn('dept_id', $attributeJson['premisses']);
+                    })->where('dept_category', '营业场所')
+                        ->select('dept_id', 'dept_name')
+                        ->get();
+                    $good['premisses'] = $premisses;
+                }
+            }
+        }
+
+        return json_success('', $goods);
+
+    }
+
+    public static function selectCascaderList(Request $request)
+    {
+        $categoryIds = $request->get('category_id', '');
+        $categorySuperId = $request->get('category_super_id', '');
+        $joinGoodsCategoryId = $request->get('join_goods_category_id', '');
+        $type = $request->get('type', '');
+        if (!$categoryIds && !$categorySuperId && !$joinGoodsCategoryId) {
+            return json_fail('参数异常');
+        }
+        $data = [];
+        $categorys = [];
+        if (!empty($categoryIds)) {
+            $categorys = SysCategory::whereIn('category_id', $categoryIds)
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $categorys);
+        } else if (!empty($categorySuperId)) {
+            $categorys = SysCategory::where('category_super_id', $categorySuperId)
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $categorys);
+        }
+
+        foreach ($categorys as $category) {
+            // if(empty($category['category_super_path'])){
+            $category['category_super_path'] = '#' . $category['id'] . '#';
+            // }
+            $subCategory = SysCategory::where('category_super_path', 'like', '%' . $category['category_super_path'] . '%')
+                ->where('category_status', 'ACTIVED')
+                ->select('category_id as id', 'category_name as name', 'category_super_id as pid', 'category_super_path')
+                ->orderBy('category_sort', 'DESC')
+                ->get()
+                ->toArray();
+            $data = array_merge($data, $subCategory);
+        }
+        if (empty($data) && !empty($joinGoodsCategoryId)) {
+            $goodsCategoryIds = $joinGoodsCategoryId;
+        } else {
+            $goodsCategoryIds = array_column($data, 'id');
+        }
+        $goods = Goods::with([
+            'sku' => function($query){
+                $query->where('goods_sku_status','ON');
+            }
+        ])
+            ->leftJoin('goods_running', 'goods_running.join_running_goods_id', '=', 'goods.goods_id')
+            ->where('goods_running.goods_running_storage', '>', 0)
+            ->whereIn('join_goods_category_id', $goodsCategoryIds)
+            ->where('goods_status', 'ON');
+
+        if ($type == 'dishes') {
+            $uid = JwtToken::getCurrentId();
+            $user = SysUser::where('user_id', $uid)->first();
+            $restaurant = SysDept::where('dept_category', '餐厅')->where(function ($query) use ($user) {
+                $query->where('dept_id', $user->join_user_dept_id)->orWhere('dept_super_id', $user->join_user_dept_id);
+            })->first();
+            $supplier = Supplier::where('join_supplier_dept_id', $restaurant->dept_id)->first();
+            if ($supplier) {
+                $goods = $goods->where('join_goods_supplier_id', $supplier->supplier_id);
+            }
+        }
+
+
+        $goods = $goods->select('goods_id', 'goods_id as id', 'goods_name as name', 'join_goods_category_id as pid', 'goods_attribute_json', 'goods_classify', 'goods_sales_price', 'goods_cover', 'goods_running.goods_running_storage')
+            ->orderBy('goods_sort', 'DESC')
+            ->orderBy('goods_addtimes', 'DESC')
+            ->get()
+            ->toArray();
+        foreach ($goods as &$good) {
+            $good['goods_cover'] = getenv('STORAGE_DOMAIN') . $good['goods_cover'];
+            $good['goods_running_storage'] = intval($good['goods_running_storage']);
+            $good['nbr'] = 0;
+            if (!empty($good['sku'])) {
+                foreach ($good['sku'] as $key => $sku) {
+                    if (!empty($sku['goods_sku_specs_json'])) {
+                        $good['sku'][$key]['goods_sku_specs_json'] = json_decode($sku['goods_sku_specs_json']);
+
+                        $skuName = '';
+                        foreach ($good['sku'][$key]['goods_sku_specs_json'] as $specsKey => $skuSpecs) {
+                            $keyStr = ($specsKey == '规格') ? '' : ($specsKey . ':');
+                            if (is_array($skuSpecs)) {
+                                $skuName = $skuName . $keyStr . ' ' . implode(' ', $skuSpecs) . '; ';
+                            } else {
+                                $skuName = $skuName . $keyStr . ' ' . $skuSpecs . '; ';
+                            }
+                        }
+                        $good['sku'][$key]['sku_name'] = rtrim($skuName, '; ');
+                    }
+                }
+            } else {
+                $good['sku'] = [];
+            }
+            $good['premisses'] = [];
+            if (!empty($good['goods_attribute_json'])) {
+                $attributeJson = json_decode($good['goods_attribute_json'], true);
+                if (isset($attributeJson['premisses'])) {
+                    $premisses = SysDept::when(!empty($attributeJson['premisses']), function ($query) use ($attributeJson) {
+                        $query->whereIn('dept_id', $attributeJson['premisses']);
+                    })->where('dept_category', '营业场所')
+                        ->select('dept_id', 'dept_name')
+                        ->get();
+                    $good['premisses'] = $premisses;
+                }
+            }
+        }
+
+        $data = array_merge($data, $goods);
+        $tree = new Tree($data);
+        $cascaderData = $tree->getTree();
+
+        foreach ($cascaderData as $key1 => $cascader1) {
+            if (isset($cascader1['children'])) {
+                foreach ($cascader1['children'] as $key2 => $cascader2) {
+                    if (isset($cascader2['children'])) {
+                        foreach ($cascader2['children'] as $key3 => $cascader3) {
+                            if (isset($cascader3['children'])) {
+                                foreach ($cascader3['children'] as $key4 => $cascader4) {
+                                    if (!isset($cascader4['goods_id'])) {
+                                        unset($cascaderData[$key1]['children'][$key2]['children'][$key3]['children'][$key4]);
+                                    }
+                                    if (isset($cascader4['goods_id']) && empty($cascader4['sku'])){
+                                        unset($cascaderData[$key1]['children'][$key2]['children'][$key3]['children'][$key4]);
+                                    }
+                                }
+                            } else if (!isset($cascader3['goods_id'])) {
+                                unset($cascaderData[$key1]['children'][$key2]['children'][$key3]);
+                            }
+                            if (isset($cascader3['goods_id']) && empty($cascader3['sku'])){
+                                unset($cascaderData[$key1]['children'][$key2]['children'][$key3]);
+                                continue;
+                            }
+                            if (isset($cascader3['children']) && count($cascaderData[$key1]['children'][$key2]['children']) == 0) {
+                                unset($cascaderData[$key1]['children'][$key2]);
+                            }
+                            if (isset($cascader3['children']) && count($cascaderData[$key1]['children'][$key2]['children'][$key3]['children']) > 0) {
+                                $cascaderData[$key1]['children'][$key2]['children'][$key3]['children'] = array_values($cascaderData[$key1]['children'][$key2]['children'][$key3]['children']);
+                            }
+                        }
+                    } else if (!isset($cascader2['goods_id'])) {
+                        unset($cascaderData[$key1]['children'][$key2]);
+                    }
+                    if (isset($cascader2['goods_id']) && empty($cascader2['sku'])){
+                        unset($cascaderData[$key1]['children'][$key2]);
+                        continue;
+                    }
+                    if (isset($cascader2['children']) && count($cascaderData[$key1]['children'][$key2]['children']) == 0) {
+                        unset($cascaderData[$key1]['children'][$key2]);
+                    }
+                    if (isset($cascader2['children']) && isset($cascaderData[$key1]['children'][$key2]) && count($cascaderData[$key1]['children'][$key2]['children']) > 0) {
+                        $cascaderData[$key1]['children'][$key2]['children'] = array_values($cascaderData[$key1]['children'][$key2]['children']);
+                    }
+                }
+            } else if (!isset($cascader1['goods_id'])) {
+                unset($cascaderData[$key1]);
+            }
+            if (isset($cascader1['goods_id']) && empty($cascader1['sku'])){
+                unset($cascaderData[$key1]);
+                continue;
+            }
+            if (isset($cascader1['children']) && count($cascaderData[$key1]['children']) == 0) {
+
+                unset($cascaderData[$key1]);
+            }
+            if (isset($cascader1['children']) && isset($cascaderData[$key1]) && count($cascaderData[$key1]['children']) > 0) {
+                $cascaderData[$key1]['children'] = array_values($cascaderData[$key1]['children']);
+            }
+        }
+        $cascaderData = array_values($cascaderData);
+
+        return json_success('success', $cascaderData);
+    }
+
+    /**
+     * @Desc 商品详情
+     * @Author Gorden
+     * @Date 2024/3/28 10:25
+     *
+     * @param $goodsId
+     * @return Response
+     */
+    public static function info($goodsId)
+    {
+        try {
+            // 商品主表
+            $main = Goods::with('category')->where('goods_id', $goodsId)->first();
+            if (!empty($main)) {
+                $main = $main->toArray();
+                $main['goods_sku_json'] = json_decode($main['goods_sku_json'], true);
+                $main['specList'] = [];
+                foreach ($main['goods_sku_json'] as $key => $sku) {
+                    $main['specList'][] = [
+                        'label' => $key,
+                        'tags' => $sku
+                    ];
+                }
+
+            } else {
+                $main = [];
+            }
+            // 详情表
+            $detail = GoodsDetail::where('join_detail_goods_id', $goodsId)->first();
+            if (!empty($detail)) {
+                $detail = $detail->toArray();
+            } else {
+                $detail = [];
+            }
+            // 标签表
+            $label = GoodsLabel::where('join_label_goods_id', $goodsId)->first();
+            if (!empty($label)) {
+                $label = $label->toArray();
+            } else {
+                $label = [];
+            }
+            // Running表
+            $running = GoodsRunning::where('join_running_goods_id', $goodsId)->first();
+            if (!empty($running)) {
+                $running = $running->toArray();
+                if (!empty($running['goods_running_off_json']) && is_json($running['goods_running_off_json'])) {
+                    $goodsRunningOffJson = json_decode($running['goods_running_off_json'], true);
+                    $running['goods_off_addtimes'] = isset($goodsRunningOffJson['time']) ? date("Y-m-d H:i", $goodsRunningOffJson['time']) : '';
+                }
+                $running['goods_running_storage'] = !empty($running['goods_running_storage']) ? intval($running['goods_running_storage']) : '';
+                $running['goods_running_sale'] = !empty($running['goods_running_sale']) ? intval($running['goods_running_sale']) : '';
+            } else {
+                $running = [];
+            }
+            // Sku表
+            $skus = GoodsSku::where('join_sku_goods_id', $goodsId)->where('goods_sku_status','ON')->get();
+            if (!empty($skus)) {
+                $skus = $skus->toArray();
+                $submitList = [];
+                foreach ($skus as $key => $sku) {
+                    $skuSpecsJson = json_decode($sku['goods_sku_specs_json'], true);
+                    $skuSpecs = '';
+                    $skuNameValue = [];
+                    $i = 1;
+                    foreach ($skuSpecsJson as $k => $item) {
+                        if (is_array($item)) {
+                            $item = implode(',', $item);
+                        }
+                        $skuSpecs = $skuSpecs . $item . ',';
+                        $skuNameKey = 'skuName' . $i;
+                        $skuValueKey = 'skuValue' . $i;
+                        $skuNameValue[$skuNameKey] = $k;
+                        $skuNameValue[$skuValueKey] = $item;
+                        $skuNameValue[$k] = $item;
+                        $i++;
+                    }
+                    $storage = json_decode($sku['goods_sku_storage_json'], true);
+                    $priceStorage = [
+                        'sku_id' => $sku['goods_sku_id'],
+                        'sku' => rtrim($skuSpecs, ',') ?? '',
+                        'stock' => $storage['storage'] ?? '',
+                        'price' => $sku['goods_sku_sales_price'] ?? '',
+                    ];
+
+                    $submitList[] = array_merge($skuNameValue, $priceStorage);
+                }
+                $skus['submitList'] = $submitList;
+            } else {
+                $skus = [];
+            }
+
+            // 合并数据
+            $data = array_merge($main, $detail, $label, $running, ['sku' => $skus]);
+            $data['goods_sku_json_label'] = [];
+
+            $data['goods_label'] = !empty($data['goods_label']) ? explode(',', $data['goods_label']) : [];
+            $data['goods_json'] = $data['goods_json'] ? json_decode($data['goods_json'], true) : [];
+            $data['goods_cover'] = getenv('STORAGE_DOMAIN') . $data['goods_cover'];
+            if (!empty($data['goods_category']) && $data['goods_category'] == 'INDEX') {
+                $data['goods_recommend_index'] = 'INDEX';
+            }
+
+            // 创建者
+            $data['creator_username'] = '';
+            if (!empty($data['creator_user_id'])) {
+                $data['creator_username'] = SysUser::where('user_id', $data['creator_user_id'])->value('user_name');
+            }
+
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = json_decode($data['goods_detail_slider_json'], true);
+                // ……
+                if (isset($data['goods_detail_slider_json']['slider'])) {
+                    $data['goods_detail_slider_json'] = explode(',', $data['goods_detail_slider_json']['slider']);
+                }
+
+                $slider = '';
+                foreach ($data['goods_detail_slider_json'] as $item) {
+                    $slider .= getenv('STORAGE_DOMAIN') . $item . ',';
+                }
+                $data['goods_detail_slider_json'] = rtrim($slider, ',');
+            }
+            $extendJson = [];
+            if (!empty($data['goods_attribute_json'])) {
+                $extendJson = json_decode($data['goods_attribute_json'], true);
+                $data['goods_attribute_json'] = $extendJson;
+            }
+            if (!empty($extendJson['coupon'])) {
+                $data['coupon_list'] = $extendJson['coupon'];
+            }
+
+            $data['express_json'] = [];
+            if (!empty($data['goods_express_json'])) {
+                $goodsExpressJson = json_decode($data['goods_express_json'], true);
+                if (isset($goodsExpressJson['express']) && $goodsExpressJson['express'] == 'Y') {
+                    $data['express_json'][] = 'express';
+                }
+                if (isset($goodsExpressJson['self']) && $goodsExpressJson['self'] == 'Y') {
+                    $data['express_json'][] = 'self';
+                }
+                if (isset($goodsExpressJson['arrival']) && $goodsExpressJson['arrival'] == 'Y') {
+                    $data['express_json'][] = 'arrival';
+                }
+            }
+            // 详情表数据
+            if (!empty($data['goods_detail_specs_json'])) {
+                $specsJson = json_decode($data['goods_detail_specs_json'], true);
+                foreach ($specsJson as $itemSpecsJson) {
+                    if (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课时') {
+                        $data['curriculum']['period'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '群体') {
+                        $data['curriculum']['group'] = $itemSpecsJson['val'];
+                    } elseif (isset($itemSpecsJson['key']) && $itemSpecsJson['key'] == '课程') {
+                        $data['curriculum']['course'] = $itemSpecsJson['val'];
+                    }
+                }
+            }
+
+            return json_success('', $data);
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            return json_fail("查询错误~");
+        }
+    }
+
+    /**
+     * @Desc 添加商品
+     * @Author Gorden
+     * @Date 2024/3/11 10:20
+     *
+     * @param $params
+     * @return Response
+     */
+    public static function insert($params): Response
+    {
+        Db::beginTransaction();
+        try {
+            $params['goods_id'] = "GD" . date('ymdHi') . random_string(4, 'up');
+            // 主表
+            self::mainInsert($params);
+            // 商品详情表
+            self::detailInsert($params);
+            // 商品标签表
+            self::labelInsert($params);
+            // 产品运行控制信息表
+            self::goodsRunningInsert($params);
+            // sku表
+            self::goodsSkuSet($params, 'insert');
+            // 待上架状态,上架时间大于当前时间
+            if ($params['goods_status'] == 'PENDING' && strtotime($params['goods_on_addtimes']) > time()) {
+                $redis = Redis::connection();
+                $key = date('YmdHi', strtotime($params['goods_on_addtimes']));
+                $redis->sAdd(Goods::LISTING_KEY_PREFIX . $key, $params['goods_id']);
+            }
+            // 自动下架
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes'])) {
+                $redis = Redis::connection();
+                $key = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($key, $params['goods_id']);
+            }
+            Db::commit();
+        } catch (\PDOException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail('数据写入失败~');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据写入失败~');
+        }
+
+        _syslog("添加商品", "商品名【" . $params['goods_name'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function insertRecharge($params)
+    {
+        try {
+            Db::beginTransaction();
+            $goods = new Goods();
+            $goods->goods_id = "GD" . date('ymdHi') . random_string(4, 'up');
+            $goods->join_goods_category_id = 59;
+            $goods->goods_classify = 'RECHARGE';
+            $goods->goods_status = $params['goods_status'];
+            $goods->goods_sort = $params['goods_sort'];
+            $goods->goods_category = $params['goods_category'];
+            $goods->goods_name = $params['goods_name'];
+            $goods->goods_market_price = $params['goods_sales_price'];
+            $goods->goods_sales_price = $params['goods_sales_price'];
+            $goods->goods_sku_json = json_encode(['规格' => [$params['goods_sales_price'] . '元']]);
+            $goods->goods_attribute_json = json_encode(['added' => ['nbr' => $params['goods_rate'] / 100, 'mode' => 'rate'], 'min-count' => 1]);
+            $goods->goods_cover = '/images/app/common/null-service.png';
+            $goods->goods_process_json = json_encode(['mode' => 'do_shopping']);
+            $goods->goods_addtimes = time();
+            $goods->save();
+
+            $sku = new GoodsSku();
+            $sku->join_sku_goods_id = $goods->goods_id;
+            $sku->goods_sku_status = $params['goods_status'];
+            $sku->goods_sku_specs_json = json_encode(['规格' => $params['goods_sales_price'] . '元']);
+            $sku->goods_sku_market_price = $params['goods_sales_price'];
+            $sku->goods_sku_sales_price = $params['goods_sales_price'];
+            $sku->goods_sku_storage_json = json_encode(['storage' => 9999]);
+            $sku->save();
+
+            Db::commit();
+
+            return json_success('success');
+        } catch (\Exception $e) {
+
+            Db::rollBack();
+
+            return json_fail('添加充值产品失败');
+        }
+    }
+
+    public static function updateRecharge($params)
+    {
+        try {
+            Db::beginTransaction();
+            $goods = Goods::where('goods_id', $params['goods_id'])->first();
+            if (!$goods) {
+                return json_fail("数据异常");
+            }
+            $goods->goods_market_price = $params['goods_sales_price'];
+            $goods->goods_sales_price = $params['goods_sales_price'];
+            $goods->goods_status = $params['goods_status'];
+            $goods->goods_sku_json = json_encode(['规格' => [$params['goods_sales_price'] . '元']]);
+            $goods->goods_sort = $params['goods_sort'];
+            if (!empty($goods->goods_attribute_json)) {
+                $attributeJson = json_decode($goods->goods_attribute_json, true);
+                $attributeJson['added']['nbr'] = $params['goods_rate'] / 100;
+                $goods->goods_attribute_json = json_encode($attributeJson);
+            }
+            $goods->save();
+            $sku = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->where('goods_sku_status','ON')->first();
+            $sku->goods_sku_status = $params['goods_status'];
+            $sku->goods_sku_specs_json = json_encode(['规格' => $params['goods_sales_price'] . '元']);
+            $sku->goods_sku_market_price = $params['goods_sales_price'];
+            $sku->goods_sku_sales_price = $params['goods_sales_price'];
+            $sku->save();
+
+            Db::commit();
+
+            return json_success("success");
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail('编辑充值产品失败');
+        }
+    }
+
+    public static function insertPackage($params): Response
+    {
+        Db::beginTransaction();
+        try {
+            $params['goods_id'] = "GD" . date('ymdHi') . random_string(4, 'up');
+            // 主表
+            self::mainInsert($params);
+            // 商品详情表
+            self::detailInsert($params);
+            // 套包组件表
+            self::componentUpdate($params, 'insert');
+            // 商品标签表
+            self::labelInsert($params);
+            // 产品运行控制信息表
+            self::goodsRunningInsert($params);
+            // sku表
+//            self::goodsSkuSet($params, 'insert');
+            // 待上架状态,上架时间大于当前时间
+            if ($params['goods_status'] == 'PENDING' && strtotime($params['goods_on_addtimes']) > time()) {
+                $redis = Redis::connection();
+                $key = date('YmdHi', strtotime($params['goods_on_addtimes']));
+                $redis->sAdd(Goods::LISTING_KEY_PREFIX . $key, $params['goods_id']);
+            }
+            Db::commit();
+        } catch (\PDOException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail('数据写入失败~');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据写入失败~');
+        }
+
+        _syslog("添加套餐", "商品名【" . $params['goods_name'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function update($params)
+    {
+        Db::beginTransaction();
+        try {
+            // 主表
+            self::mainUpdate($params);
+            // 商品详情表
+            self::detailUpdate($params);
+            // 商品标签表
+            self::labelUpdate($params);
+            // 产品运行控制信息表
+            self::goodsRunningUpdate($params);
+            // sku表
+            self::goodsSkuSet($params, 'update');
+
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            return json_fail('数据更新失败~');
+        }
+
+        _syslog("编辑商品", "商品名【" . $params['goods_name'] . "】" ?? "商品ID:【" . $params['goods_id'] . "】");
+
+        return json_success('success');
+    }
+
+    public static function changeStatus($params)
+    {
+        try {
+            Goods::where('goods_id', $params['goods_id'])->update(['goods_status' => $params['goods_status']]);
+
+            return json_success('修改成功');
+        } catch (\Exception $e) {
+            return json_fail('修改状态失败');
+        }
+    }
+
+
+    public static function updatePackage($params)
+    {
+        Db::beginTransaction();
+        try {
+            // 主表
+            self::mainUpdate($params);
+            // 商品详情表
+            self::detailUpdate($params);
+            // 套包组件表
+            self::componentUpdate($params, 'update');
+            // 商品标签表
+            self::labelUpdate($params);
+            // 产品运行控制信息表
+            self::goodsRunningUpdate($params);
+            // sku表
+            self::goodsSkuSet($params, 'update');
+
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据更新失败~');
+        }
+        _syslog("编辑套餐", "商品名【" . $params['goods_name'] . "】" ?? "商品ID:【" . $params['goods_id'] . "】");
+
+        return json_success('success');
+    }
+
+    /**
+     * @Desc 删除商品
+     * @Author Gorden
+     * @Date 2024/3/28 13:20
+     *
+     * @param $ids
+     * @return Response
+     */
+    public static function delete($ids)
+    {
+        if (!$ids) {
+            return json_fail("数据错误~");
+        }
+        if (!is_array($ids)) {
+            $ids = [$ids];
+        }
+
+        $goods = Goods::whereIn('goods_id', $ids)->get()->toArray();
+        if (!$goods) {
+            return json_fail("数据错误~");
+        }
+
+        // 是否在套包里
+        if (GoodsComponent::whereIn('join_component_goods_id', $ids)->exists()) {
+            return json_fail("当前商品存在于套包中,请先在套包中删除");
+        }
+        // 是否已被购买过
+        if (OrderSheet::whereIn('join_sheet_goods_id', $ids)->exists()) {
+            return json_fail("当前商品已有购买历史,如不在APP显示,请选择【下架】操作");
+        }
+        // 是否预约过
+        if (Appointment::whereIn('join_appointment_goods_id', $ids)->exists()) {
+
+            return json_fail("当前商品已有预约历史,如不在APP显示,请选择【下架】操作");
+        }
+
+        Db::beginTransaction();
+        try {
+            Goods::whereIn('goods_id', $ids)->delete();
+            GoodsDetail::whereIn('join_detail_goods_id', $ids)->delete();
+            GoodsLabel::whereIn('join_label_goods_id', $ids)->delete();
+            GoodsRunning::whereIn('join_running_goods_id', $ids)->delete();
+            GoodsSku::whereIn('join_sku_goods_id', $ids)->delete();
+
+            Db::commit();
+
+            _syslog("删除商品 / 套餐", "ID:【" . implode(',', $ids) . "】", $goods);
+
+            return json_success("商品删除成功");
+        } catch (\Exception $e) {
+            Db::rollBack();
+
+            return json_fail("商品删除失败~");
+        }
+    }
+
+    /**
+     * @Desc 商品主表
+     * @Author Gorden
+     * @Date 2024/3/11 11:20
+     *
+     * @param $params
+     * @return mixed|string
+     * @throws BusinessException
+     */
+    public static function mainInsert($params)
+    {
+        if (!empty($params['goods_cover'])) {
+            $params['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_cover']);
+        }
+        // 如果产品是待处理状态
+        if ($params['goods_status'] == 'PENDING') {
+            if (strtotime($params['goods_on_addtimes']) <= time()) {
+                $params['goods_status'] = 'ON';
+            }
+        }
+        $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+        if (!$category) {
+            throw new BusinessException("产品分类不存在~");
+        }
+        if (empty($params['goods_category'])) {
+            $params['goods_category'] = $category->category_classify;
+        }
+
+        try {
+            $model = new Goods();
+            $model->goods_id = $params['goods_id'];
+            $model->join_goods_category_id = $params['join_goods_category_id'] ?? 0;
+            $model->join_goods_supplier_id = $params['join_goods_supplier_id'] ?? 0;
+            $model->goods_classify = $params['goods_classify'] ?? '';
+            $model->goods_status = $params['goods_status'] ?? '';
+            $model->goods_category = $params['goods_category'] ?? '';
+//            $model->goods_prefix = $params['goods_prefix'] ?? 】($category->category_name ? '【' . $category->category_name . '' : '');
+            $model->goods_prefix = $params['goods_prefix'] ?? '';
+            $model->goods_name = $params['goods_name'];
+            $model->goods_market_price = $params['goods_market_price'] ?? 0;
+            $model->goods_sales_price = $params['goods_sales_price'] ?? 0;
+            $model->goods_sku_json = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+            $model->goods_attribute_json = !empty($params['goods_attribute_json']) ? $params['goods_attribute_json'] : '[]';
+            $model->goods_title = $params['goods_title'] ?? '';
+            $model->goods_cover = $params['goods_cover'] ?? '';
+            $model->goods_on_addtimes = isset($params['goods_on_addtimes']) ? strtotime($params['goods_on_addtimes']) : null;
+            $model->goods_sort = $params['goods_sort'] ?? null;
+            $model->goods_groupby = $params['goods_groupby'] ?? '';
+            $model->goods_remark = $params['goods_remark'] ?? '';
+            $model->goods_extend_json = $params['goods_extend_json'] ?? '{}';
+            $model->is_support_appointment = $params['is_support_appointment'] ?? 'N';
+            $model->creator_user_id = JwtToken::getCurrentId();
+            $model->goods_addtimes = time();
+            $model->goods_updatetimes = time();
+            // {"express":"Y","self":"Y","arrival":"Y"}
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $model->goods_express_json = json_encode($expressJson);
+            }
+
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times']) && $params['goods_category'] != 'TRAVEL') {
+                $times = [];
+                $attributeJsonTimeArr = [];
+                $personTotal = 0;
+                foreach ($params['appointment_times'] as $time) {
+                    $attributeJsonTimeArr[] = strtotime(date('Y-m-d ') . $time['appointmentTimeStart']);
+                    $attributeJsonTimeArr[] = strtotime(date('Y-m-d ') . $time['appointmentTimeEnd']);
+                    $personTotal += $time['person'];
+                    $times[$time['appointmentTimeStart']] = [
+                        'person' => $time['person'],
+                        'duration' => $time['appointmentTimeStart'] . '-' . $time['appointmentTimeEnd']
+                    ];
+                }
+                $attributeJsonTime = date('H:i', min($attributeJsonTimeArr)) . '至' . date('H:i', max($attributeJsonTimeArr));
+
+                $newDates = [];
+                foreach ($params['dates'] as $date) {
+                    $key = self::$week[$date];
+                    $newDates[$key] = $date;
+                }
+                ksort($newDates);
+
+
+                $currentDate = current($newDates);
+                $lastDate = end($newDates);
+
+                $attributeJsonDate = '';
+                if (self::$week[$lastDate] - self::$week[$currentDate] + 1 > count($newDates)) {
+                    $attributeJsonDate = implode(',', $newDates);
+                } else if (self::$week[$lastDate] - self::$week[$currentDate] + 1 == count($newDates)) {
+                    $attributeJsonDate = $currentDate . '至' . $lastDate;
+                }
+
+                // if (isset($params['work_time'])){
+                //     $workTimeStart = date('H:i',strtotime($params['work_time'][0]));
+                //     $workTimeEnd = date('H:i',strtotime($params['work_time'][1]));
+                //     $attributeJsonTime = $workTimeStart.'至'.$workTimeEnd;
+                // }
+
+                $attributeJson = [
+                    'icon' => $model->goods_cover,
+                    'date' => $attributeJsonDate,
+                    // 'time' => $attributeJsonTime,
+                    'dates' => $newDates ? array_values($newDates) : [],
+                    'times' => $times,
+                    'person' => $personTotal
+                ];
+                // if(isset($params['address'])){
+                //     $attributeJson['address'] = $params['address'];
+                // }
+                // if (isset($params['min_count'])){
+                //     $attributeJson['min-count'] = $params['min_count'];
+                // }else{
+                //     $attributeJson['min-count'] = 1;
+                // }
+                // if (isset($params['teachers'])){
+                //     $attributeJson['teachers'] = $params['teachers'];
+                // }
+                if (!empty($params['appointment_label'])) {
+                    $attributeJson['label'] = $params['appointment_label'];
+                }
+                // if (!empty($params['address'])) {
+                //     $attributeJson['address'] = $params['address'];
+                // }
+                // if (!empty($params['position'])){
+                //     $attributeJson['position'] = $params['position'];
+                // }
+                // if (isset($params['goods_service_premises'])){
+                //     $attributeJson['service_premises_id'] = $params['goods_service_premises'];
+                // }
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+
+//                $times = [];
+//                foreach ($params['appointment_times'] as $time) {
+//                    $times[$time['appointmentTimeStart']] = [
+//                        'person' => $time['person'],
+//                        'duration' => $time['appointmentTimeStart'] . '-' . $time['appointmentTimeEnd']
+//                    ];
+//                }
+//                $model->goods_attribute_json = json_encode([
+//                    'icon' => '',
+//                    'dates' => $params['dates'] ?? [],
+//                    'times' => $times
+//                ]);
+            }
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times']) && $params['goods_category'] == 'TRAVEL') {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['travel-day'] = $params['travel_day'];
+                $attributeJson['travel-begin'] = $params['travel_begin'];
+                $attributeJson['travel-night'] = $params['travel_night'];
+                $attributeJson['travel-trans'] = $params['travel_trans'];
+
+                $unixs = [];
+                foreach ($params['appointment_times'] as $times) {
+                    $unix = strtotime($times['days']);
+                    $unixs[$unix] = $times['person'];
+                }
+                ksort($unixs);
+                $months = [];
+                foreach ($unixs as $key => $unix) {
+                    $month = date('Ym', $key);
+                    $day = date('d', $key);
+                    $months[$month][$day] = $unix;
+                }
+                $attributeJson['month'] = $months;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if (!empty($params['goods_json']) && $params['join_goods_category_id'] == 65) {
+                $goodsJson = json_decode($params['goods_json'], true);
+//                foreach ($goodsJson as $key => $item) {
+//                    $goodsJson[$key]['color'] = hexToRgb($item['color']);
+//                }
+//
+                $model->goods_json = json_encode($goodsJson);
+            } elseif (!empty($params['goods_json']) && $params['join_goods_category_id'] == 43) {
+                $goodsJson = json_decode($params['goods_json'], true);
+                $newGoodsJson = [];
+                foreach ($goodsJson as $item1) {
+                    if (empty($item1['title'])) {
+                        continue;
+                    }
+                    $newItem1 = [];
+                    foreach ($item1['items'] as $item2) {
+                        $newParams = [];
+                        foreach ($item2['params'] as $param) {
+                            if (!empty($param[0]) || !empty($param[1])) {
+                                $newParams[] = $param;
+                            }
+                        }
+                        $newItem1['items'][$item2['key']] = $newParams;
+                    }
+                    $newItem1['service'] = $item1['service'] ?? '';
+                    $newGoodsJson[$item1['title']] = $newItem1;
+                }
+                $model->goods_json = json_encode($newGoodsJson);
+            } else {
+                $model->goods_json = '[]';
+            }
+
+            if (!empty($params['goods_premisses'])) {
+                $attributeJson = [];
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['premisses'] = $params['goods_premisses'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+
+            if (!empty($params['goods_theme_color']) && !empty($params['goods_theme_icon'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['bg'] = $params['goods_theme_color'];
+                $attributeJson['icon'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_theme_icon']);
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['address'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['address'] = $params['address'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['position'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['position'] = $params['position'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['goods_service_premises'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['service_premises_id'] = $params['goods_service_premises'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['min_count'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['min-count'] = $params['min_count'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['teachers'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['teachers'] = $params['teachers'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['work_time'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+
+                $workTimeStart = date('H:i', strtotime($params['work_time'][0]));
+                $workTimeEnd = date('H:i', strtotime($params['work_time'][1]));
+                $attributeJsonTime = $workTimeStart . '至' . $workTimeEnd;
+
+                $attributeJson['time'] = $attributeJsonTime;
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if ($model->save()) {
+                return $model->goods_id;
+            }
+            // 异常
+            throw new BusinessException("数据写入失败~");
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 详情表
+     * @Author Gorden
+     * @Date 2024/3/11 11:19
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailInsert($params)
+    {
+        if (!empty($params['goods_detail_slider_json'])) {
+            $params['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_detail_slider_json']);
+            $params['goods_detail_slider_json'] = json_encode(['slider' => $params['goods_detail_slider_json']]);
+        }
+
+        if (isset($params['curriculum'])) {
+            $params['goods_detail_specs_json'] = json_encode([
+                [
+                    'key' => '课时',
+                    'val' => $params['curriculum']['period'] ?? '',
+                ],
+                [
+                    'key' => '群体',
+                    'val' => $params['curriculum']['group'] ?? '',
+                ],
+                [
+                    'key' => '课程',
+                    'val' => $params['curriculum']['course'] ?? '',
+                ],
+            ]);
+        }
+        try {
+            $model = new GoodsDetail();
+            $model->join_detail_goods_id = $params['goods_id'];
+            $model->goods_detail_code_json = $params['goods_detail_code_json'] ?? '{}';
+            $model->goods_detail_slider_json = $params['goods_detail_slider_json'] ?? '{}';
+            $model->goods_detail_specs_json = $params['goods_detail_specs_json'] ?? '{}';
+            $model->goods_detail_content = $params['goods_detail_content'] ?? '';
+            if (!$model->save()) {
+                // 异常
+                throw new BusinessException("轮播图/详情数据写入失败~");
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("轮播图/详情数据写入失败~");
+        }
+    }
+
+    public static function componentUpdate($params, $type = 'insert')
+    {
+        Db::beginTransaction();
+        try {
+            $goodsSku = '';
+            // 有先删除
+            if ($type == 'update') {
+                GoodsComponent::where('join_component_master_goods_id', $params['goods_id'])->delete();
+                $goodsSku = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->first();
+                if ($goodsSku) {
+                    $goodsSku->goods_sku_market_price = $params['goods_market_price'] ?? 0;
+                    $goodsSku->goods_sku_sales_price = $params['goods_sales_price'] ?? 0;
+                    $goodsSku->save();
+                }
+            }
+            if ($type == 'insert' || !$goodsSku) {
+                Goods::where('goods_id', $params['goods_id'])->update(['goods_sku_json' => '{"规格": ["标准"]}']);
+                $skuData = [
+                    'join_sku_goods_id' => $params['goods_id'],
+                    'goods_sku_status' => 'ON',
+                    'goods_sku_specs_json' => '{"规格": "标准"}',
+                    'goods_sku_title' => "标准" . $params['goods_name'],
+                    'goods_sku_market_price' => $params['goods_market_price'] ?? 0,
+                    'goods_sku_sales_price' => $params['goods_sales_price'] ?? 0,
+                ];
+                GoodsSku::insert($skuData);
+            }
+            $data = [];
+            foreach ($params['goods_content_list'] as $item) {
+                if (!in_array($item['goods_id'], $params['join_component_goods_id'])) {
+                    continue;
+                }
+                $goods = Goods::where('goods_id', $params['goods_id'])->first();
+                if (!$goods) {
+                    continue;
+                }
+
+                $data[] = [
+                    'join_component_master_goods_id' => $params['goods_id'],
+                    'join_component_goods_id' => $item['goods_id'] ?? '',
+                    'join_component_goods_sku_id' => $item['sku_id'] ?? '',
+                    'goods_component_price' => $item['goods_sales_price'] ?? '',
+                    'goods_component_cover' => $goods->goods_cover,
+                    'goods_component_json' => json_encode(['goods_name' => $item['goods_name'], 'nbr' => $item['nbr'], 'sku_id' => $item['sku_id'] ?? '']),
+                    'goods_component_addtimes' => time()
+                ];
+            }
+
+            if ($data) {
+                GoodsComponent::insert($data);
+            }
+            Db::commit();
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getMessage());
+            throw new BusinessException("数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 标签表
+     * @Author Gorden
+     * @Date 2024/3/11 11:32
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelInsert($params)
+    {
+        $model = new GoodsLabel();
+        $model->join_label_goods_id = $params['goods_id'];
+        $model->goods_label = $params['goods_label'] ? implode(',', $params['goods_label']) : '';
+        $model->goods_label_extend_json = !empty($params['goods_label_extend_json']) ? $params['goods_label_extend_json'] : '{}';
+        if (!$model->save()) {
+            // 异常
+            throw new BusinessException('数据写入失败~');
+        }
+    }
+
+    /**
+     * @Desc 产品运行控制信息表
+     * @Author Gorden
+     * @Date 2024/3/11 11:38
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningInsert($params)
+    {
+        try {
+            $model = new GoodsRunning();
+            $model->join_running_goods_id = $params['goods_id'];
+            $model->goods_running_storage = $params['goods_running_storage'] ?? 0;
+            $model->goods_running_sale = $params['goods_running_sale'] ?? 0;
+            $model->goods_running_off_type = !empty($params['goods_running_off_type']) ? $params['goods_running_off_type'] : '';
+            $model->goods_running_off_json = !empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes']) ? json_encode(['time' => strtotime($params['goods_off_addtimes'])]) : '[]';
+            if (!$model->save()) {
+                throw new BusinessException('数据写入失败');
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据写入失败');
+        }
+    }
+
+    /**
+     * @Desc SKU
+     * @Author Gorden
+     * @Date 2024/3/11 12:01
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function skuInsert($params)
+    {
+        $model = new GoodsSku();
+        $model->join_sku_goods_id = $params['goods_id'];
+        $model->goods_sku_status = $params['goods_sku_status'];
+        $model->goods_sku_specs_json = $params['goods_sku_specs_json'];
+        $model->goods_sku_title = $params['goods_sku_title'];
+        $model->goods_sku_images_json = $params['goods_sku_images_json'];
+        $model->goods_sku_content = $params['goods_sku_content'];
+        $model->goods_sku_market_price = $params['goods_sku_market_price'];
+        $model->goods_sku_sales_price = $params['goods_sku_sales_price'];
+        $model->goods_sku_storage_json = $params['goods_sku_storage_json'];
+        $model->goods_sku_service_json = $params['goods_sku_service_json'];
+        $model->goods_sku_extend_json = $params['goods_sku_extend_json'];
+        if (!$model->save()) {
+            throw new BusinessException('规格数据写入失败~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:44
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function mainUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new Goods());
+            if (!empty($data['goods_cover'])) {
+                $data['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_cover']);
+            }
+            $data['goods_on_addtimes'] = isset($data['goods_on_addtimes']) ? strtotime($data['goods_on_addtimes']) : 0;
+            $data['goods_sku_json'] = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+
+            $row = Goods::find($data['goods_id']);
+            if ($row->join_goods_category_id != $data['join_goods_category_id']) {
+                $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+                if (!$category) {
+                    throw new BusinessException("产品分类不存在~");
+                }
+                $data['goods_category'] = $category->category_classify ?? '';
+                $data['goods_prefix'] = $data['goods_prefix'] ?? ($category->category_name ? '【' . $category->category_name . '】' : '');
+            }
+
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $data['goods_express_json'] = json_encode($expressJson);
+            }
+            $attributeJson = [];
+            if (!empty($row->goods_attribute_json)) {
+                $attributeJson = json_decode($row->goods_attribute_json, true);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+            }
+            if (!empty($params['attribute_account'])){
+                $attributeJson['account'] = $params['attribute_account'];
+            }
+            if (!empty($params['attribute_control'])){
+                $attributeJson['control'] = $params['attribute_control'];
+            }
+
+            $data['goods_attribute_json'] = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+
+            foreach ($data as $key => $val) {
+                $row->{$key} = $val;
+            }
+            $row->goods_updatetimes = time();
+            $row->updator_user_id = JwtToken::getCurrentId();
+            $row->save();
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getTrace());
+            throw new BusinessException('数据更新异常~1');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:57
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsDetail());
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_detail_slider_json']);
+                $data['goods_detail_slider_json'] = json_encode(['slider' => $data['goods_detail_slider_json']]);
+            }
+            if (isset($params['curriculum'])) {
+                $data['goods_detail_specs_json'] = json_encode([
+                    [
+                        'key' => '课时',
+                        'val' => $params['curriculum']['period'] ?? '',
+                    ],
+                    [
+                        'key' => '群体',
+                        'val' => $params['curriculum']['group'] ?? '',
+                    ],
+                    [
+                        'key' => '课程',
+                        'val' => $params['curriculum']['course'] ?? '',
+                    ],
+                ]);
+            }
+            // 根据goods_id 查详情ID
+            $detail = GoodsDetail::where('join_detail_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->join_detail_goods_id, $data, new GoodsDetail());
+            } else {
+                $data['join_detail_goods_id'] = $params['goods_id'];
+                GoodsDetail::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('轮播图/详情数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:58
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsLabel());
+            // 根据goods_id 查详情ID
+            $detail = GoodsLabel::where('join_label_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->goods_label_id, $data, new GoodsLabel());
+            } else {
+                $data['join_label_goods_id'] = $params['goods_id'];
+                GoodsLabel::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~3');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:59
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsRunning());
+            // 根据goods_id 查详情ID
+            $detail = GoodsRunning::where('join_running_goods_id', $params['goods_id'])->first();
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T') {
+                $redis = Redis::connection();
+                if (!empty($detail->goods_running_off_json)) {
+                    $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                    if (isset($goodsRunningOffJson['time'])) {
+                        $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                        // 有老的下架时间,删除老的
+                        $redis->srem($oldKey, $params['goods_id']);
+                    } else {
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                    }
+                } else {
+                    $data['goods_running_off_json'] = json_encode(['time' => strtotime($params['goods_off_addtimes'])]);
+                }
+                // 加入自动下架
+                $newKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($newKey, $params['goods_id']);
+
+            } else if (!empty($params['goods_running_off_type']) && !empty($detail->goods_running_off_type) && $params['goods_running_off_type'] == 'H' && $detail->goods_running_off_type == 'T') {
+                $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                if (isset($goodsRunningOffJson['time'])) {
+                    $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                    $redis = Redis::connection();
+                    $redis->srem($oldKey, $params['goods_id']);
+                }
+            }
+            if ($detail) {
+                self::doUpdate($detail->join_running_goods_id, $data, new GoodsRunning());
+            } else {
+                // 兼容老数据……
+                $data['join_running_goods_id'] = $params['goods_id'];
+                GoodsRunning::insert($data);
+            }
+
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc sku 设置
+     * @Author Gorden
+     * @Date 2024/4/10 10:43
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsSkuSet($params, $operation = 'insert')
+    {
+        try {
+            Db::beginTransaction();
+            $skusOldIds = [];
+            if ($operation == 'update') {
+                // 查出所有的
+                $skusOldIds = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->pluck('goods_sku_id', 'goods_sku_id');
+
+                // 删掉原有的
+//                GoodsSku::where('join_sku_goods_id', $params['goods_id'])->delete();
+            }
+            if (empty($skusOldIds) && empty($params['goods_sku_json_value'])) {
+                $skuData = [
+                    'join_sku_goods_id' => $params['goods_id'],
+                    'goods_sku_status' => 'ON',
+                    'goods_sku_specs_json' => '{"规格": "标准"}',
+                    'goods_sku_title' => "标准" . $params['goods_name'],
+                    'goods_sku_market_price' => $params['goods_market_price'] ?? 0,
+                    'goods_sku_sales_price' => $params['goods_sales_price'] ?? 0,
+                ];
+                GoodsSku::insert($skuData);
+            }
+            // 入新的
+            if (!empty($params['goods_sku_json_value'])) {
+                foreach ($params['goods_sku_json_value'] as $item) {
+                    $skus = explode(',', $item['sku']);
+                    $skuArr = [];
+                    for ($i = 1; $i <= count($skus); $i++) {
+                        $skuName = "skuName" . $i;
+                        $key = $item[$skuName];
+                        $skuArr[$key] = $skus[$i - 1];
+                    }
+                    $specsJson = json_encode($skuArr);
+                    $skuTitle = str_replace('-', ',', $item['sku']) . $params['goods_name'];
+                    if ($operation == 'update' && !empty($item['sku_id'])) {
+                        $model = GoodsSku::where('goods_sku_id', $item['sku_id'])->where('goods_sku_status','ON')->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+                    } else {
+                        $model = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->where('goods_sku_status','ON')->where('goods_sku_title', $skuTitle)->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+
+                    }
+
+                    $model->join_sku_goods_id = $params['goods_id'];
+                    $model->goods_sku_status = $params['goods_status'];
+                    $model->goods_sku_specs_json = $specsJson;
+                    $model->goods_sku_title = $skuTitle;
+                    $model->goods_sku_market_price = $params['goods_market_price'] ?? 0;
+                    $model->goods_sku_sales_price = $item['price'];
+                    $model->goods_sku_storage_json = json_encode(['storage' => $item['stock']]);
+                    $model->save();
+
+                }
+            }
+            if ($operation == 'update' && !empty($skusOldIds)) {
+                // GoodsSku::whereIn('goods_sku_id', $skusOldIds)->delete();
+                GoodsSku::whereIn('goods_sku_id', $skusOldIds)->update(['goods_sku_status' => 'DISABLED']);
+            }
+
+            Db::commit();
+        } catch (\Exception $e) {
+            dump($e->getTrace());
+            Db::rollBack();
+
+            throw new BusinessException('规格数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:45
+     *
+     * @param array $data
+     * @param $model
+     * @return array
+     * @throws BusinessException
+     */
+    private static function inputFilter(array $data, $model): array
+    {
+        $table = config('database.connections.mysql.prefix') . $model->getTable();
+        $allow_column = $model->getConnection()->select("desc `$table`");
+        if (!$allow_column) {
+            throw new BusinessException('表不存在', 2);
+        }
+        $columns = array_column($allow_column, 'Type', 'Field');
+        foreach ($data as $col => $item) {
+            if (!isset($columns[$col])) {
+                unset($data[$col]);
+                continue;
+            }
+            // 非字符串类型传空则为null
+            if ($item === '' && strpos(strtolower($columns[$col]), 'varchar') === false && strpos(strtolower($columns[$col]), 'text') === false) {
+                $data[$col] = null;
+            }
+            if (is_array($item)) {
+                $data[$col] = implode(',', $item);
+            }
+            if ($item != '' && (strpos(strtolower($columns[$col]), 'varchar') || strpos(strtolower($columns[$col]), 'text'))) {
+//                $data[$col] = htmlspecialchars($item);
+            }
+        }
+        if (empty($data['created_at'])) {
+            unset($data['created_at']);
+        }
+        if (empty($data['updated_at'])) {
+            unset($data['updated_at']);
+        }
+        return $data;
+    }
+
+    /**
+     * @Desc 执行更新
+     * @Author Gorden
+     * @Date 2024/3/12 8:43
+     *
+     * @param $id
+     * @param $data
+     * @param $model
+     * @return void
+     */
+    private static function doUpdate($id, $data, $model)
+    {
+        $row = $model->find($id);
+        foreach ($data as $key => $val) {
+            $row->{$key} = $val;
+        }
+        $row->save();
+    }
+
+    /**
+     * @Desc 上架定时
+     * @Author Gorden
+     * @Date 2024/4/11 15:13
+     *
+     * @return void
+     */
+    public static function checkListing()
+    {
+        $key = Goods::LISTING_KEY_PREFIX . date('YmdHi');
+        $redis = Redis::connection();
+        if (!$redis->exists($key)) {
+            return;
+        }
+
+        $goodsIds = $redis->sMembers($key);
+        if (Goods::whereIn('goods_id', $goodsIds)->update(['goods_status' => 'ON'])) {
+            $redis->del($key);
+        }
+    }
+
+    /**
+     * @Desc 自动下架
+     * @Author Gorden
+     * @Date 2024/4/26 15:26
+     *
+     * @return void
+     */
+    public static function checkOffListing()
+    {
+        $key = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi');
+        $redis = Redis::connection();
+        if (!$redis->exists($key)) {
+            return;
+        }
+
+        $goodsIds = $redis->sMembers($key);
+        if (Goods::whereIn('goods_id', $goodsIds)->update(['goods_status' => 'OFF'])) {
+            $redis->del($key);
+        }
+    }
+
+    public static $week = [
+        '周一' => 1,
+        '周二' => 2,
+        '周三' => 3,
+        '周四' => 4,
+        '周五' => 5,
+        '周六' => 6,
+        '周日' => 7,
+    ];
+}

+ 1005 - 0
app/admin/service/goods/TravelService.php

@@ -0,0 +1,1005 @@
+<?php
+
+namespace app\admin\service\goods;
+
+use app\model\Coupon;
+use app\model\Goods;
+use app\model\GoodsDetail;
+use app\model\GoodsLabel;
+use app\model\GoodsRunning;
+use app\model\GoodsSku;
+use app\model\SysCategory;
+use support\Db;
+use support\exception\BusinessException;
+use support\Redis;
+use support\Response;
+use Tinywan\Jwt\JwtToken;
+
+class TravelService
+{
+    /**
+     * @Desc 添加商品
+     * @Author Gorden
+     * @Date 2024/3/11 10:20
+     *
+     * @param $params
+     * @return Response
+     */
+    public static function insert($params): Response
+    {
+        Db::beginTransaction();
+        try {
+            $params['goods_id'] = "GD" . date('ymdHi') . random_string(4, 'up');
+            // 主表
+            self::mainInsert($params);
+            // 商品详情表
+            self::detailInsert($params);
+            // 商品标签表
+            self::labelInsert($params);
+            // 产品运行控制信息表
+            self::goodsRunningInsert($params);
+            // sku表
+            self::goodsSkuSet($params, 'insert');
+            // 待上架状态,上架时间大于当前时间
+            if ($params['goods_status'] == 'PENDING' && strtotime($params['goods_on_addtimes']) > time()) {
+                $redis = Redis::connection();
+                $key = date('YmdHi', strtotime($params['goods_on_addtimes']));
+                $redis->sAdd(Goods::LISTING_KEY_PREFIX . $key, $params['goods_id']);
+            }
+            // 自动下架
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes'])) {
+                $redis = Redis::connection();
+                $key = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($key, $params['goods_id']);
+            }
+            Db::commit();
+        } catch (\PDOException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail('数据写入失败~');
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            dump($e->getFile() . '(' . $e->getLine() . '):' . $e->getMessage());
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            dump($e->getTrace());
+            return json_fail('数据写入失败~');
+        }
+
+        _syslog("添加商品", "商品名【" . $params['goods_name'] . "】");
+
+        return json_success('success');
+    }
+
+    /**
+     * @Desc 商品主表
+     * @Author Gorden
+     * @Date 2024/3/11 11:20
+     *
+     * @param $params
+     * @return mixed|string
+     * @throws BusinessException
+     */
+    public static function mainInsert($params)
+    {
+        if (!empty($params['goods_cover'])) {
+            $params['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_cover']);
+        }
+        // 如果产品是待处理状态
+        if ($params['goods_status'] == 'PENDING') {
+            if (strtotime($params['goods_on_addtimes']) <= time()) {
+                $params['goods_status'] = 'ON';
+            }
+        }
+        $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+        if (!$category) {
+            throw new BusinessException("产品分类不存在~");
+        }
+        if (empty($params['goods_category'])) {
+            $params['goods_category'] = $category->category_classify;
+        }
+
+        try {
+            $model = new Goods();
+            $model->goods_id = $params['goods_id'];
+            $model->join_goods_category_id = $params['join_goods_category_id'] ?? 0;
+            $model->join_goods_supplier_id = $params['join_goods_supplier_id'] ?? 0;
+            $model->goods_classify = $params['goods_classify'] ?? '';
+            $model->goods_status = $params['goods_status'] ?? '';
+            $model->goods_category = $params['goods_category'] ?? '';
+//            $model->goods_prefix = $params['goods_prefix'] ?? 】($category->category_name ? '【' . $category->category_name . '' : '');
+            $model->goods_prefix = $params['goods_prefix'] ?? '';
+            $model->goods_name = $params['goods_name'];
+            $model->goods_market_price = $params['goods_market_price'] ?? 0;
+            $model->goods_sales_price = $params['goods_sales_price'] ?? 0;
+            $model->goods_sku_json = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+            $model->goods_attribute_json = !empty($params['goods_attribute_json']) ? $params['goods_attribute_json'] : '[]';
+            $model->goods_title = $params['goods_title'] ?? '';
+            $model->goods_cover = $params['goods_cover'] ?? '';
+            $model->goods_on_addtimes = isset($params['goods_on_addtimes']) ? strtotime($params['goods_on_addtimes']) : null;
+            $model->goods_sort = $params['goods_sort'] ?? null;
+            $model->goods_groupby = $params['goods_groupby'] ?? '';
+            $model->goods_remark = $params['goods_remark'] ?? '';
+            $model->goods_extend_json = $params['goods_extend_json'] ?? '{}';
+            $model->is_support_appointment = $params['is_support_appointment'] ?? 'N';
+            $model->creator_user_id = JwtToken::getCurrentId();
+            $model->goods_addtimes = time();
+            $model->goods_updatetimes = time();
+            // {"express":"Y","self":"Y","arrival":"Y"}
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $model->goods_express_json = json_encode($expressJson);
+            }
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['travel-day'] = $params['travel_day'];
+                $attributeJson['travel-begin'] = $params['travel_begin'];
+                $attributeJson['travel-night'] = $params['travel_night'];
+                $attributeJson['travel-trans'] = $params['travel_trans'];
+
+                $unixs = [];
+                foreach ($params['appointment_times'] as $times) {
+                    $unix = strtotime($times['days']);
+                    $unixs[$unix] = $times['person'];
+                }
+                ksort($unixs);
+                $months = [];
+                foreach ($unixs as $key => $unix) {
+                    $month = date('Ym', $key);
+                    $day = date('d', $key);
+                    $months[$month][$day] = $unix;
+                }
+                $attributeJson['month'] = $months;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if (!empty($params['goods_json']) && $params['join_goods_category_id'] == 65) {
+                $goodsJson = json_decode($params['goods_json'], true);
+//                foreach ($goodsJson as $key => $item) {
+//                    $goodsJson[$key]['color'] = hexToRgb($item['color']);
+//                }
+//
+                $model->goods_json = json_encode($goodsJson);
+            } elseif (!empty($params['goods_json']) && $params['join_goods_category_id'] == 43) {
+                $goodsJson = json_decode($params['goods_json'], true);
+                $newGoodsJson = [];
+                foreach ($goodsJson as $item1) {
+                    if (empty($item1['title'])) {
+                        continue;
+                    }
+                    $newItem1 = [];
+                    foreach ($item1['items'] as $item2) {
+                        $newParams = [];
+                        foreach ($item2['params'] as $param) {
+                            if (!empty($param[0]) || !empty($param[1])) {
+                                $newParams[] = $param;
+                            }
+                        }
+                        $newItem1['items'][$item2['key']] = $newParams;
+                    }
+                    $newItem1['service'] = $item1['service'] ?? '';
+                    $newGoodsJson[$item1['title']] = $newItem1;
+                }
+                $model->goods_json = json_encode($newGoodsJson);
+            } else {
+                $model->goods_json = '[]';
+            }
+
+            if (!empty($params['goods_premisses'])) {
+                $attributeJson = [];
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['premisses'] = $params['goods_premisses'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+
+            if (!empty($params['goods_theme_color']) && !empty($params['goods_theme_icon'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['bg'] = $params['goods_theme_color'];
+                $attributeJson['icon'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_theme_icon']);
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['address'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['address'] = $params['address'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['position'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['position'] = $params['position'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['goods_service_premises'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['service_premises_id'] = $params['goods_service_premises'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['min_count'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['min-count'] = $params['min_count'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['teachers'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $attributeJson['teachers'] = $params['teachers'];
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (isset($params['work_time'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+
+                $workTimeStart = date('H:i', strtotime($params['work_time'][0]));
+                $workTimeEnd = date('H:i', strtotime($params['work_time'][1]));
+                $attributeJsonTime = $workTimeStart . '至' . $workTimeEnd;
+
+                $attributeJson['time'] = $attributeJsonTime;
+                $model->goods_attribute_json = json_encode($attributeJson);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                if (!empty($model->goods_attribute_json) && !is_array($model->goods_attribute_json)) {
+                    $attributeJson = json_decode($model->goods_attribute_json, true);
+                } elseif (empty($model->goods_attribute_json)) {
+                    $attributeJson = [];
+                }
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+                $model->goods_attribute_json = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if ($model->save()) {
+                return $model->goods_id;
+            }
+            // 异常
+            throw new BusinessException("数据写入失败~");
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 详情表
+     * @Author Gorden
+     * @Date 2024/3/11 11:19
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailInsert($params)
+    {
+        if (!empty($params['goods_detail_slider_json'])) {
+            $params['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_detail_slider_json']);
+            $params['goods_detail_slider_json'] = json_encode(['slider' => $params['goods_detail_slider_json']]);
+        }
+
+        if (isset($params['curriculum'])) {
+            $params['goods_detail_specs_json'] = json_encode([
+                [
+                    'key' => '课时',
+                    'val' => $params['curriculum']['period'] ?? '',
+                ],
+                [
+                    'key' => '群体',
+                    'val' => $params['curriculum']['group'] ?? '',
+                ],
+                [
+                    'key' => '课程',
+                    'val' => $params['curriculum']['course'] ?? '',
+                ],
+            ]);
+        }
+        try {
+            $model = new GoodsDetail();
+            $model->join_detail_goods_id = $params['goods_id'];
+            $model->goods_detail_code_json = $params['goods_detail_code_json'] ?? '{}';
+            $model->goods_detail_slider_json = $params['goods_detail_slider_json'] ?? '{}';
+            $model->goods_detail_specs_json = $params['goods_detail_specs_json'] ?? '{}';
+            $model->goods_detail_content = $params['goods_detail_content'] ?? '';
+            if (!$model->save()) {
+                // 异常
+                throw new BusinessException("轮播图/详情数据写入失败~");
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException("轮播图/详情数据写入失败~");
+        }
+    }
+
+    /**
+     * @Desc 标签表
+     * @Author Gorden
+     * @Date 2024/3/11 11:32
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelInsert($params)
+    {
+        $model = new GoodsLabel();
+        $model->join_label_goods_id = $params['goods_id'];
+        $model->goods_label = $params['goods_label'] ? implode(',', $params['goods_label']) : '';
+        $model->goods_label_extend_json = !empty($params['goods_label_extend_json']) ? $params['goods_label_extend_json'] : '{}';
+        if (!$model->save()) {
+            // 异常
+            throw new BusinessException('数据写入失败~');
+        }
+    }
+
+    /**
+     * @Desc 产品运行控制信息表
+     * @Author Gorden
+     * @Date 2024/3/11 11:38
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningInsert($params)
+    {
+        try {
+            $model = new GoodsRunning();
+            $model->join_running_goods_id = $params['goods_id'];
+            $model->goods_running_storage = $params['goods_running_storage'] ?? 0;
+            $model->goods_running_sale = $params['goods_running_sale'] ?? 0;
+            $model->goods_running_off_type = !empty($params['goods_running_off_type']) ? $params['goods_running_off_type'] : '';
+            $model->goods_running_off_json = !empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T' && !empty($params['goods_off_addtimes']) ? json_encode(['time' => strtotime($params['goods_off_addtimes'])]) : '[]';
+            if (!$model->save()) {
+                throw new BusinessException('数据写入失败');
+            }
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据写入失败');
+        }
+    }
+
+    /**
+     * 更新商品
+     * @param $params
+     * @return \support\Response
+     */
+    public static function update($params)
+    {
+        Db::beginTransaction();
+        try {
+            // 主表
+            self::mainUpdate($params);
+            // 商品详情表
+            self::detailUpdate($params);
+            // 商品标签表
+            self::labelUpdate($params);
+            // 产品运行控制信息表
+            self::goodsRunningUpdate($params);
+            // sku表
+            self::goodsSkuSet($params, 'update');
+
+            Db::commit();
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            return json_fail($e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            return json_fail('数据更新失败~');
+        }
+
+        _syslog("编辑商品", "商品名【" . $params['goods_name'] . "】" ?? "商品ID:【" . $params['goods_id'] . "】");
+
+        return json_success('success');
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:44
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function mainUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new Goods());
+            if (!empty($data['goods_cover'])) {
+                $data['goods_cover'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_cover']);
+            }
+            $data['goods_on_addtimes'] = isset($data['goods_on_addtimes']) ? strtotime($data['goods_on_addtimes']) : 0;
+            $data['goods_sku_json'] = !empty($params['goods_sku_json_label']) ? json_encode($params['goods_sku_json_label']) : json_encode(['规格' => ['标准']]);
+
+            $row = Goods::find($data['goods_id']);
+            if ($row->join_goods_category_id != $data['join_goods_category_id']) {
+                $category = SysCategory::where('category_id', $params['join_goods_category_id'])->first();
+                if (!$category) {
+                    throw new BusinessException("产品分类不存在~");
+                }
+                $data['goods_category'] = $category->category_classify ?? '';
+                $data['goods_prefix'] = $data['goods_prefix'] ?? ($category->category_name ? '【' . $category->category_name . '】' : '');
+            }
+            if (!empty($params['goods_recommend_index'])) {
+                $data['goods_category'] = $params['goods_recommend_index'];
+            }
+            // 上架时间有变动
+            if ($data['goods_status'] == 'PENDING' && $row->goods_on_addtimes != $data['goods_on_addtimes']) {
+                $redis = Redis::connection();
+                // 删掉原来的
+                $oldKey = Goods::LISTING_KEY_PREFIX . date('YmdHi', $row->goods_on_addtimes);
+                $redis->srem($oldKey, $data['goods_id']);
+
+                // 加入新的
+                $newKey = Goods::LISTING_KEY_PREFIX . date('YmdHi', $data['goods_on_addtimes']);
+                $redis->sadd($newKey, $data['goods_id']);
+            }
+
+            $expressJson = [];
+            if (!empty($params['express_json'])) {
+                if (in_array('express', $params['express_json'])) {
+                    $expressJson['express'] = 'Y';
+                } else {
+                    $expressJson['express'] = 'N';
+                }
+                if (in_array('self', $params['express_json'])) {
+                    $expressJson['self'] = 'Y';
+                } else {
+                    $expressJson['self'] = 'N';
+                }
+                if (in_array('arrival', $params['express_json'])) {
+                    $expressJson['arrival'] = 'Y';
+                } else {
+                    $expressJson['arrival'] = 'N';
+                }
+
+                $data['goods_express_json'] = json_encode($expressJson);
+            }
+            if (!empty($params['is_support_appointment']) && $params['is_support_appointment'] == 'Y' && !empty($params['appointment_times'])) {
+                $attributeJson = [];
+                if (!empty($row->goods_attribute_json)) {
+                    $attributeJson = json_decode($row->goods_attribute_json, true);
+                }
+                $attributeJson['travel-day'] = $params['travel_day'];
+                $attributeJson['travel-begin'] = $params['travel_begin'];
+                $attributeJson['travel-night'] = $params['travel_night'];
+                $attributeJson['travel-trans'] = $params['travel_trans'];
+
+                $unixs = [];
+                foreach ($params['appointment_times'] as $times) {
+                    $unix = strtotime($times['days']);
+                    $unixs[$unix] = $times['person'];
+                }
+                ksort($unixs);
+                $months = [];
+                foreach ($unixs as $key => $unix) {
+                    $month = date('Ym', $key);
+                    $day = date('d', $key);
+                    $months[$month][$day] = $unix;
+                }
+                $attributeJson['month'] = $months;
+                $data['goods_attribute_json'] = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if (!empty($params['coupon_id']) && !empty($params['coupon_nbr'])) {
+                $attributeJson = [];
+                if (!empty($row->goods_attribute_json)) {
+                    $attributeJson = json_decode($row->goods_attribute_json, true);
+                }
+                $coupons = Coupon::whereIn('coupon_id', $params['coupon_id'])
+                    ->select('coupon_id', 'coupon_name')
+                    ->get()
+                    ->toArray();
+                $couponList = [];
+                foreach ($coupons as $coupon) {
+                    if (isset($params['coupon_nbr'][$coupon['coupon_id']])) {
+                        $couponList[$coupon['coupon_id']] = [
+                            'num' => $params['coupon_nbr'][$coupon['coupon_id']],
+                            'name' => $coupon['coupon_name']
+                        ];
+                    }
+                }
+                $attributeJson['coupon'] = $couponList;
+                $data['goods_attribute_json'] = json_encode($attributeJson, JSON_UNESCAPED_UNICODE);
+            }
+            if (!empty($params['goods_premisses'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+                $data['goods_attribute_json']['premisses'] = $params['goods_premisses'];
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+            }
+            if (!empty($data['goods_json']) && $data['join_goods_category_id'] == 65) {
+//                $goodsJson = json_decode($data['goods_json'], true);
+//                foreach ($goodsJson as $key => $item) {
+//                    $goodsJson[$key]['color'] = hexToRgb($item['color']);
+//                }
+//
+//                $data['goods_json'] = json_encode($goodsJson);
+            } elseif (!empty($data['goods_json']) && $data['join_goods_category_id'] == 43) {
+                $goodsJson = json_decode($data['goods_json'], true);
+                $newGoodsJson = [];
+                foreach ($goodsJson as $item1) {
+                    if (empty($item1['title'])) {
+                        continue;
+                    }
+                    $newItem1 = [];
+                    foreach ($item1['items'] as $item2) {
+                        $newParams = [];
+                        foreach ($item2['params'] as $param) {
+                            if (!empty($param[0]) || !empty($param[1])) {
+                                $newParams[] = $param;
+                            }
+                        }
+                        $newItem1['items'][$item2['key']] = $newParams;
+                    }
+                    $newItem1['service'] = $item1['service'];
+                    $newGoodsJson[$item1['title']] = $newItem1;
+                }
+                $data['goods_json'] = json_encode($newGoodsJson);
+            } else {
+                $data['goods_json'] = '[]';
+            }
+            if (!empty($params['goods_theme_color']) && !empty($params['goods_theme_icon'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+                $data['goods_attribute_json']['bg'] = $params['goods_theme_color'];
+                $data['goods_attribute_json']['icon'] = str_replace(getenv('STORAGE_DOMAIN'), '', $params['goods_theme_icon']);
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+            }
+
+            if (!empty($params['address'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+                $data['goods_attribute_json']['address'] = $params['address'];
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+            }
+            if (!empty($params['position'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+                $data['goods_attribute_json']['position'] = $params['position'];
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+            }
+            if (isset($params['goods_service_premises'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+                $data['goods_attribute_json']['service_premises_id'] = $params['goods_service_premises'];
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+            }
+            if (isset($params['work_time'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+
+                $workTimeStart = date('H:i', strtotime($params['work_time'][0]));
+                $workTimeEnd = date('H:i', strtotime($params['work_time'][1]));
+
+                $data['goods_attribute_json']['time'] = $workTimeStart . '至' . $workTimeEnd;
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+
+            }
+            if (isset($params['min_count'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+                $data['goods_attribute_json']['min-count'] = $params['min_count'];
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+            }
+            if (isset($params['teachers'])) {
+                if (!empty($data['goods_attribute_json']) && !is_array($data['goods_attribute_json'])) {
+                    if (is_json($data['goods_attribute_json'])) {
+                        $data['goods_attribute_json'] = json_decode($data['goods_attribute_json'], true);
+                    } else {
+                        $data['goods_attribute_json'] = [];
+                    }
+                } elseif (empty($data['goods_attribute_json'])) {
+                    $data['goods_attribute_json'] = [];
+                }
+                $data['goods_attribute_json']['teachers'] = $params['teachers'];
+                $data['goods_attribute_json'] = json_encode($data['goods_attribute_json']);
+            }
+
+            $data['goods_json'] = '[]';
+
+            foreach ($data as $key => $val) {
+                $row->{$key} = $val;
+            }
+            $row->goods_updatetimes = time();
+            $row->updator_user_id = JwtToken::getCurrentId();
+            $row->save();
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~1');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:57
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function detailUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsDetail());
+            if (!empty($data['goods_detail_slider_json'])) {
+                $data['goods_detail_slider_json'] = str_replace(getenv('STORAGE_DOMAIN'), '', $data['goods_detail_slider_json']);
+                $data['goods_detail_slider_json'] = json_encode(['slider' => $data['goods_detail_slider_json']]);
+            }
+            if (isset($params['curriculum'])) {
+                $data['goods_detail_specs_json'] = json_encode([
+                    [
+                        'key' => '课时',
+                        'val' => $params['curriculum']['period'] ?? '',
+                    ],
+                    [
+                        'key' => '群体',
+                        'val' => $params['curriculum']['group'] ?? '',
+                    ],
+                    [
+                        'key' => '课程',
+                        'val' => $params['curriculum']['course'] ?? '',
+                    ],
+                ]);
+            }
+            // 根据goods_id 查详情ID
+            $detail = GoodsDetail::where('join_detail_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->join_detail_goods_id, $data, new GoodsDetail());
+            } else {
+                $data['join_detail_goods_id'] = $params['goods_id'];
+                GoodsDetail::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('轮播图/详情数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:58
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function labelUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsLabel());
+            // 根据goods_id 查详情ID
+            $detail = GoodsLabel::where('join_label_goods_id', $params['goods_id'])->first();
+            if ($detail) {
+                self::doUpdate($detail->goods_label_id, $data, new GoodsLabel());
+            } else {
+                $data['join_label_goods_id'] = $params['goods_id'];
+                GoodsLabel::insert($data);
+            }
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~3');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 9:59
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsRunningUpdate($params)
+    {
+        try {
+            $data = self::inputFilter($params, new GoodsRunning());
+            // 根据goods_id 查详情ID
+            $detail = GoodsRunning::where('join_running_goods_id', $params['goods_id'])->first();
+            if (!empty($params['goods_running_off_type']) && $params['goods_running_off_type'] == 'T') {
+                $redis = Redis::connection();
+                if (!empty($detail->goods_running_off_json)) {
+                    $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                    if (isset($goodsRunningOffJson['time'])) {
+                        $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                        // 有老的下架时间,删除老的
+                        $redis->srem($oldKey, $params['goods_id']);
+                    } else {
+                        $goodsRunningOffJson['time'] = strtotime($params['goods_off_addtimes']);
+                        $data['goods_running_off_json'] = json_encode($goodsRunningOffJson);
+                    }
+                } else {
+                    $data['goods_running_off_json'] = json_encode(['time' => strtotime($params['goods_off_addtimes'])]);
+                }
+                // 加入自动下架
+                $newKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', strtotime($params['goods_off_addtimes']));
+                $redis->sAdd($newKey, $params['goods_id']);
+
+            } else if (!empty($params['goods_running_off_type']) && !empty($detail->goods_running_off_type) && $params['goods_running_off_type'] == 'H' && $detail->goods_running_off_type == 'T') {
+                $goodsRunningOffJson = json_decode($detail->goods_running_off_json, true);
+                if (isset($goodsRunningOffJson['time'])) {
+                    $oldKey = Goods::LISTING_OFF_KEY_PREFIX . date('YmdHi', $goodsRunningOffJson['time']);
+                    $redis = Redis::connection();
+                    $redis->srem($oldKey, $params['goods_id']);
+                }
+            }
+            if ($detail) {
+                self::doUpdate($detail->join_running_goods_id, $data, new GoodsRunning());
+            } else {
+                // 兼容老数据……
+                $data['join_running_goods_id'] = $params['goods_id'];
+                GoodsRunning::insert($data);
+            }
+
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+            throw new BusinessException('数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc sku 设置
+     * @Author Gorden
+     * @Date 2024/4/10 10:43
+     *
+     * @param $params
+     * @return void
+     * @throws BusinessException
+     */
+    public static function goodsSkuSet($params, $operation = 'insert')
+    {
+        try {
+            Db::beginTransaction();
+            $skusOldIds = [];
+            if ($operation == 'update') {
+                // 查出所有的
+                $skusOldIds = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->pluck('goods_sku_id', 'goods_sku_id');
+
+                // 删掉原有的
+//                GoodsSku::where('join_sku_goods_id', $params['goods_id'])->delete();
+            }
+            if (empty($skusOldIds) && empty($params['goods_sku_json_value'])) {
+                $skuData = [
+                    'join_sku_goods_id' => $params['goods_id'],
+                    'goods_sku_status' => 'ON',
+                    'goods_sku_specs_json' => '{"规格": "标准"}',
+                    'goods_sku_title' => "标准" . $params['goods_name'],
+                    'goods_sku_market_price' => $params['goods_market_price'] ?? 0,
+                    'goods_sku_sales_price' => $params['goods_sales_price'] ?? 0,
+                ];
+                GoodsSku::insert($skuData);
+            }
+            // 入新的
+            if (!empty($params['goods_sku_json_value'])) {
+                foreach ($params['goods_sku_json_value'] as $item) {
+                    $skus = explode(',', $item['sku']);
+                    $skuArr = [];
+                    for ($i = 1; $i <= count($skus); $i++) {
+                        $skuName = "skuName" . $i;
+                        $key = $item[$skuName];
+                        $skuArr[$key] = $skus[$i - 1];
+                    }
+                    $specsJson = json_encode($skuArr);
+                    $skuTitle = str_replace('-', ',', $item['sku']) . $params['goods_name'];
+                    if ($operation == 'update' && !empty($item['sku_id'])) {
+                        $model = GoodsSku::where('goods_sku_id', $item['sku_id'])->where('goods_sku_status','ON')->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+                    } else {
+                        $model = GoodsSku::where('join_sku_goods_id', $params['goods_id'])->where('goods_sku_status','ON')->where('goods_sku_title', $skuTitle)->first();
+                        if (!$model) {
+                            $model = new GoodsSku();
+                        } else {
+                            unset($skusOldIds[$model->goods_sku_id]);
+                        }
+
+                    }
+
+                    $model->join_sku_goods_id = $params['goods_id'];
+                    $model->goods_sku_status = $params['goods_status'];
+                    $model->goods_sku_specs_json = $specsJson;
+                    $model->goods_sku_title = $skuTitle;
+                    $model->goods_sku_market_price = $params['goods_market_price'] ?? 0;
+                    $model->goods_sku_sales_price = $item['price'];
+                    $model->goods_sku_storage_json = json_encode(['storage' => $item['stock']]);
+                    $model->save();
+
+                }
+            }
+            if ($operation == 'update' && !empty($skusOldIds)) {
+                // GoodsSku::whereIn('goods_sku_id', $skusOldIds)->delete();
+                GoodsSku::whereIn('goods_sku_id', $skusOldIds)->update(['goods_sku_status' => 'DISABLED']);
+            }
+
+            Db::commit();
+        } catch (\Exception $e) {
+            dump($e->getTrace());
+            Db::rollBack();
+
+            throw new BusinessException('规格数据更新异常~');
+        }
+    }
+
+    /**
+     * @Desc
+     * @Author Gorden
+     * @Date 2024/3/12 8:45
+     *
+     * @param array $data
+     * @param $model
+     * @return array
+     * @throws BusinessException
+     */
+    private static function inputFilter(array $data, $model): array
+    {
+        $table = config('database.connections.mysql.prefix') . $model->getTable();
+        $allow_column = $model->getConnection()->select("desc `$table`");
+        if (!$allow_column) {
+            throw new BusinessException('表不存在', 2);
+        }
+        $columns = array_column($allow_column, 'Type', 'Field');
+        foreach ($data as $col => $item) {
+            if (!isset($columns[$col])) {
+                unset($data[$col]);
+                continue;
+            }
+            // 非字符串类型传空则为null
+            if ($item === '' && strpos(strtolower($columns[$col]), 'varchar') === false && strpos(strtolower($columns[$col]), 'text') === false) {
+                $data[$col] = null;
+            }
+            if (is_array($item)) {
+                $data[$col] = implode(',', $item);
+            }
+            if ($item != '' && (strpos(strtolower($columns[$col]), 'varchar') || strpos(strtolower($columns[$col]), 'text'))) {
+//                $data[$col] = htmlspecialchars($item);
+            }
+        }
+        if (empty($data['created_at'])) {
+            unset($data['created_at']);
+        }
+        if (empty($data['updated_at'])) {
+            unset($data['updated_at']);
+        }
+        return $data;
+    }
+
+    /**
+     * @Desc 执行更新
+     * @Author Gorden
+     * @Date 2024/3/12 8:43
+     *
+     * @param $id
+     * @param $data
+     * @param $model
+     * @return void
+     */
+    private static function doUpdate($id, $data, $model)
+    {
+        $row = $model->find($id);
+        foreach ($data as $key => $val) {
+            $row->{$key} = $val;
+        }
+        $row->save();
+    }
+}

+ 309 - 22
app/admin/service/member/MemberService.php

@@ -11,9 +11,11 @@ use app\model\FamilyMember;
 use app\model\Goods;
 use app\model\Member;
 use app\model\MemberAccount;
+use app\model\MemberAccountList;
 use app\model\MemberCert;
 use app\model\MemberDevice;
 use app\model\MemberInfo;
+use app\model\MemberRole;
 use app\model\Order;
 use app\model\OrderSheet;
 use app\model\PayDetail;
@@ -23,6 +25,7 @@ use app\model\SysSerial;
 use app\model\SysUser;
 use support\Db;
 use support\exception\BusinessException;
+use support\Log;
 use support\Request;
 use support\Response;
 
@@ -101,7 +104,7 @@ class MemberService
                     $query->where('member_is_referrer', 'Y');
                 }
             })
-            ->select('member_id', 'member_is_vip', 'member_is_owner', 'member_is_partner', 'member_is_referrer', 'member_classify', 'member_status', 'member_mobile', 'member_from', 'member_addtimes', 'member_extend_json',
+            ->select('member_id', 'member_is_vip', 'member_is_owner', 'join_invite_member_id', 'member_is_partner', 'member_is_referrer', 'member_classify', 'member_status', 'member_mobile', 'member_from', 'member_addtimes', 'member_extend_json',
                 'member_info.member_info_nickname', 'member_info.member_info_headimg', 'member_info.member_info_referee', 'member_info.member_info_business', 'member_info.member_info_service', 'member_info.member_info_city', 'member_info.member_info_address', 'member_info.member_info_gender', 'member_info.member_info_birthday',
                 'member_role.member_role_id', 'member_role.member_role_name',
                 'member_cert.member_cert_birth', 'member_cert.member_cert_gender', 'member_cert.member_cert_name', 'member_cert.member_cert_nbr', 'member_cert.member_cert_province', 'member_cert.member_cert_addr', 'member_cert.member_cert_face', 'member_cert.member_cert_photo', 'member_cert.member_cert_nation'
@@ -117,6 +120,10 @@ class MemberService
         foreach ($rows as &$row) {
             $row['coupon_count'] = CouponDetail::where('join_coupon_detail_member_id', $row['member_id'])->count();
             $row['member_mobile'] = substr($row['member_mobile'], 0, 3) . '****' . substr($row['member_mobile'], 7);
+            // 推荐人
+            if (!empty($row['join_invite_member_id'])) {
+                $row['referrer_name'] = MemberService::getMemberNameByMemberId($row['join_invite_member_id']);
+            }
             $row['info'] = [
                 'member_info_nickname' => !empty($row['member_info_nickname']) ? $row['member_info_nickname'] : substr($row['member_mobile'], -4, 4) . '会员',
                 'member_info_headimg' => !empty($row['member_info_headimg']) ? $row['member_info_headimg'] : '',
@@ -218,6 +225,17 @@ class MemberService
                 }
                 if (isset($extendJson['referee'])) {
                     foreach ($extendJson['referee'] as $key => $referee) {
+                        if (preg_match('/^1[3-9]\d{9}$/', $referee)) {
+                            $member = Member::with([
+                                'cert' => function ($query) {
+                                    $query->select('join_cert_member_id', 'member_cert_name');
+                                }
+                            ])->where('member_mobile', $referee)->where('member_status', 'ACTIVED')
+                                ->first();
+                            if ($member->cert && $member->cert->member_cert_name) {
+                                $referee = $member->cert->member_cert_name . '-' . $referee;
+                            }
+                        }
                         $row['referee'][] = [
                             'name' => $referee,
                             'time' => $key
@@ -249,6 +267,8 @@ class MemberService
         $level = $request->get('level', '');
         $isAuth = $request->get('is_auth', '');
         $addtime = $request->get('member_addtimes', []);
+        $isPartner = $request->get('member_is_partner', '');
+        $isReferrer = $request->get('member_is_referrer', '');
 
         $rows = Member::with('account')
             ->where('member_is_owner', 'N')
@@ -264,6 +284,10 @@ class MemberService
                 $query->where('member_cert.member_cert_name', 'like', '%' . $name . '%');
             })->when($mobile != '', function ($query) use ($mobile) {
                 $query->where('member.member_mobile', 'like', '%' . $mobile . '%');
+            })->when($isPartner != '', function ($query) use ($isPartner) {
+                $query->where('member.member_is_partner', $isPartner);
+            })->when($isReferrer != '', function ($query) use ($isReferrer) {
+                $query->where('member.member_is_referrer', $isReferrer);
             })->when($level != '', function ($query) use ($level) {
                 if ($level == 'other') {
                     $query->where('member.join_member_role_id', NULL)->orWhere('member.join_member_role_id', '');
@@ -280,7 +304,7 @@ class MemberService
                 $addtime[0] = strtotime($addtime[0]);
                 $addtime[1] = strtotime($addtime[1]);
                 $query->whereBetween('member.member_addtimes', $addtime);
-            })->select('member_id', 'member_is_owner', 'member_classify', 'member_status', 'member_mobile', 'member_from', 'member_addtimes', 'member_extend_json',
+            })->select('member_id', 'join_invite_member_id', 'member_is_owner', 'member_classify', 'member_status', 'member_mobile', 'member_from', 'member_addtimes', 'member_extend_json',
                 'member_info.member_info_nickname', 'member_info.member_info_headimg', 'member_info.member_info_referee', 'member_info.member_info_business', 'member_info.member_info_service',
                 'member_role.member_role_id', 'member_role.member_role_name',
                 'member_cert.member_cert_birth', 'member_cert.member_cert_gender', 'member_cert.member_cert_name', 'member_cert.member_cert_nbr', 'member_cert.member_cert_province', 'member_cert.member_cert_addr', 'member_cert.member_cert_face', 'member_cert.member_cert_photo', 'member_cert.member_cert_nation',
@@ -290,7 +314,10 @@ class MemberService
             ->toArray();
 
         $exportData = [];
-        foreach ($rows as $row) {
+        foreach ($rows as $row) {// 推荐人
+            if (!empty($row['join_invite_member_id'])) {
+                $row['referrer_name'] = MemberService::getMemberNameByMemberId($row['join_invite_member_id']);
+            }
             $row['info'] = [
                 'member_info_nickname' => !empty($row['member_info_nickname']) ? $row['member_info_nickname'] : substr($row['member_mobile'], -4, 4) . '会员',
                 'member_info_headimg' => !empty($row['member_info_headimg']) ? $row['member_info_headimg'] : '',
@@ -349,7 +376,7 @@ class MemberService
                 'is_auth' => $row['is_auth'],
                 'level' => $row['member_role_name'] ?? '普通会员',
                 'member_addtimes' => $row['member_addtimes'],
-                'member_info_referee' => $row['info']['member_info_referee'] ?? '',
+                'referrer_name' => $row['referrer_name'] ?? '',
                 'member_info_business' => $row['info']['member_info_business'] ?? '',
                 'member_info_service' => $row['info']['member_info_service'] ?? '',
             ];
@@ -373,6 +400,7 @@ class MemberService
             return json_fail('查询错误');
         }
         $member = $member->toArray();
+        unset($member['member_password']);
         $member['info'] = [
             'member_info_nickname' => !empty($member['info']) && !empty($member['info']['member_info_nickname']) ? $member['info']['member_info_nickname'] : substr($member['member_mobile'], -4, 4) . '会员',
             'member_info_headimg' => !empty($member['info']) && !empty($member['info']['member_info_headimg']) ? $member['info']['member_info_headimg'] : '',
@@ -447,6 +475,17 @@ class MemberService
             }
             if (isset($extendJson['referee'])) {
                 foreach ($extendJson['referee'] as $key => $referee) {
+                    if (preg_match('/^1[3-9]\d{9}$/', $referee)) {
+                        $refereeMember = Member::with([
+                            'cert' => function ($query) {
+                                $query->select('join_cert_member_id', 'member_cert_name');
+                            }
+                        ])->where('member_mobile', $referee)->where('member_status', 'ACTIVED')
+                            ->first();
+                        if ($refereeMember->cert && $refereeMember->cert->member_cert_name) {
+                            $referee = $refereeMember->cert->member_cert_name . '-' . $referee;
+                        }
+                    }
                     $member['referee'][] = [
                         'name' => $referee,
                         'time' => $key
@@ -470,14 +509,33 @@ class MemberService
                     $nickname = $inviteMember['info']['member_info_nickname'];
                 }
                 $certname = '';
-                if (!empty($inviteMember['cert']) && !empty($inviteMember['cert']['member_info_nickname'])) {
-                    $nickname = $inviteMember['cert']['member_cert_name'];
+                if (!empty($inviteMember['cert']) && !empty($inviteMember['cert']['member_cert_name'])) {
+                    $certname = $inviteMember['cert']['member_cert_name'];
                 }
                 $member['invite_name'] = MemberService::getMemberName($mobile, $certname, $nickname);
                 $member['invite_code'] = $inviteMember['member_invite_code'] ?? '';
             }
         }
 
+        if (!empty($member['member_partner_json'])) {
+            $partnerJson = json_decode($member['member_partner_json'], true);
+            if (isset($partnerJson['duedate'])){
+                $member['partner_info'] = [
+                    'surplus' => ceil((strtotime($partnerJson['duedate'].' 23:59:59') - time())/(3600*24)),
+                    'dates' => date('Y/m/d 00:00:00',strtotime($partnerJson['datetime'])).'-'.date('Y/m/d 23:59:59',strtotime($partnerJson['duedate']))
+                ];
+            }
+        }
+        if (!empty($member['member_referrer_json'])) {
+            $referrerJson = json_decode($member['member_referrer_json'], true);
+            if (isset($referrerJson['duedate'])){
+                $member['referrer_info'] = [
+                    'surplus' => ceil((strtotime($referrerJson['duedate'].' 23:59:59') - time())/(3600*24)),
+                    'dates' => date('Y/m/d 00:00:00',strtotime($referrerJson['datetime'])).'-'.date('Y/m/d 23:59:59',strtotime($referrerJson['duedate']))
+                ];
+            }
+        }
+
         return json_success('', $member);
     }
 
@@ -713,6 +771,38 @@ class MemberService
         return json_success('', compact('rows', 'page', 'pageSize', 'total'));
     }
 
+    /**
+     * @Desc 佣金记录
+     * @Author Gorden
+     * @Date 2024/9/30 14:52
+     *
+     * @param Request $request
+     * @return Response
+     */
+    public static function commissionList(Request $request)
+    {
+        $memberId = $request->get('member_id');
+        $page = $request->get('page', 1);
+        $pageSize = $request->get('pageSize', 20);
+
+        $rows = MemberAccountList::where('join_member_account_list_member_id', $memberId)
+            ->where('member_account_list_attr', 'IN');
+        $total = $rows->count();
+        $rows = $rows->forPage($page, $pageSize)
+            ->get()
+            ->toArray();
+        foreach ($rows as &$row) {
+            if (!empty($row['member_account_list_json'])) {
+                $memberAccountListJson = json_decode($row['member_account_list_json'], true);
+                if ($memberAccountListJson['master_member_id']) {
+                    $row['master_member_name'] = MemberService::getMemberNameByMemberId($memberAccountListJson['master_member_id']);
+                }
+            }
+        }
+
+        return json_success('', compact('rows', 'page', 'pageSize', 'total'));
+    }
+
     /**
      * @Desc 我的粉丝列表
      * @Author Gorden
@@ -898,29 +988,62 @@ class MemberService
 
         $info = PayDetail::where(function ($query) use ($memberId) {
             $query->where('join_pay_member_id', $memberId)
-                // ->whereIn('pay_prepayid', ['W06', 'A01'])
-                ->whereIn('pay_category', ['RECHARGE','PARTNER'])
+                ->whereIn('pay_category', ['RECHARGE'])
                 ->where('pay_prepayid', '<>', $memberId . '-WELFARE')
                 ->where('pay_status', 'SUCCESS');
         })->orWhere(function ($query) use ($memberId) {
             $query->where('join_pay_member_id', $memberId)
                 ->where('pay_prepayid', 'like', '%CASH%')
                 ->where('pay_status', 'SUCCESS');
-        })->orderByDesc('pay_addtimes')
+        })->select('join_pay_object_json', 'join_pay_member_id', 'pay_category', 'join_pay_order_id', 'pay_extend_json', 'pay_addtimes', 'pay_amount');
+//            ->orderByDesc('pay_addtimes')
+//            ->get()
+//            ->toArray();
+
+        $memberAccountList = MemberAccountList::where('join_member_account_list_member_id', $memberId)
+            ->where('member_account_list_status', 'ACTIVED')
+            ->selectRaw('
+                member_account_list_extend_json as join_pay_object_json,
+                join_member_account_list_member_id as join_pay_member_id,
+                member_account_list_attr as pay_category,
+                join_list_member_account_nbr as pay_prepayid,
+                member_account_list_extend_json as pay_extend_json,
+                member_account_list_addtimes as pay_addtimes,
+                member_account_list_amount as pay_amount');
+
+        $info = $info->union($memberAccountList)
+            ->orderByDesc('pay_addtimes')
             ->get()
             ->toArray();
         $data = [];
         foreach ($info as $key => $item) {
-            if ($item['pay_category'] == 'PARTNER'){
-                $payObjectJson = json_decode($item['join_pay_object_json'],true);
-                if (!Order::where('order_id',$payObjectJson['order_id'])->where('order_is_complete','Y')->exists()){
-                    continue;
-                }
+            $payObjectJson = [];
+            if (!empty($item['join_pay_object_json'])) {
+                $payObjectJson = json_decode($item['join_pay_object_json'], true);
             }
+//            if ($item['pay_category'] == 'PARTNER') {
+//                $payObjectJson = json_decode($item['join_pay_object_json'], true);
+//                if (!Order::where('order_id', $payObjectJson['order_id'])->where('order_is_complete', 'Y')->exists()) {
+//                    continue;
+//                }
+//            }
+//            if ($item['pay_category'] == 'REFERRER') {
+//                $payObjectJson = json_decode($item['join_pay_object_json'], true);
+//                if (!Order::where('order_id', $payObjectJson['order_id'])->where('order_is_complete', 'Y')->exists()) {
+//                    continue;
+//                }
+//            }
             $categoryIds = [];
             if (!empty($item['join_pay_order_id'])) {
-                $orderIds = Order::where('order_groupby', $item['join_pay_order_id'])->pluck('order_id');
-                $goodsIds = OrderSheet::whereIn('join_sheet_order_id', $orderIds)->pluck('join_sheet_goods_id');
+                $goodsIds = [];
+                if (in_array($item['join_pay_order_id'], ['PARTNER', 'COMBINE', 'REFERRER'])) {
+                    if (isset($payObjectJson['recharge_goods_id'])) {
+                        $goodsIds = [$payObjectJson['recharge_goods_id']];
+                    }
+                } else {
+                    $orderIds = Order::where('order_groupby', $item['join_pay_order_id'])->pluck('order_id');
+                    $goodsIds = OrderSheet::whereIn('join_sheet_order_id', $orderIds)->pluck('join_sheet_goods_id');
+                }
                 $categoryIds = Goods::whereIn('goods_id', $goodsIds)->pluck('join_goods_category_id');
             }
 
@@ -938,14 +1061,16 @@ class MemberService
             $item['goods_category'] = implode(',', array_unique($categoryName));
             if (mb_substr($item['goods_category'], 0, 7) == '自定义金额充值') {
                 $item['goods_category'] = '自定义金额充值';
+            } else if (mb_substr($item['goods_category'], 0, 7) == '会员合伙人产品') {
+                $item['goods_category'] = '会员合伙人充值';
             }
-            if (in_array($item['pay_category'], ['RECHARGE', 'REFUND', 'GIVE', 'PARTNER'])) {
+            if (in_array($item['pay_category'], ['RECHARGE', 'REFUND', 'GIVE', 'PARTNER', 'IN'])) {
                 $item['balance_type'] = 'add';
             } else {
                 $item['balance_type'] = 'cut';
             }
-            if ($key == 0 && in_array($item['pay_category'], ['RECHARGE', 'REFUND', 'GIVE', 'PARTNER'])) {
-                if (!empty($item['pay_extend_json'])) {
+            if ($key == 0 && in_array($item['pay_category'], ['RECHARGE', 'REFUND', 'GIVE'])) {
+                if (!empty($item['pay_extend_json']) && !in_array($item['join_pay_order_id'], ['PARTNER', 'COMBINE'])) {
                     $extendJson = json_decode($item['pay_extend_json'], true);
                     if (!isset($extendJson['added_amount'])) {
                         $extendJson['added_amount'] = 0;
@@ -966,6 +1091,24 @@ class MemberService
                     }
                     $balance = $balance - $extendJson['added_amount'];
                 }
+                if (in_array($item['join_pay_order_id'], ['PARTNER', 'COMBINE'])) {
+                    $addedAmount = 0;
+                    if (!isset($payObjectJson['recharge_config'])) {
+                        $addedAmount = 0;
+                    }
+                    if (isset($payObjectJson['recharge_config']) && !empty($payObjectJson['recharge_config']['nbr']) && floatval($payObjectJson['recharge_config']['nbr'] > 0)) {
+                        $addedAmount = $payObjectJson['recharge_config']['nbr'];
+                        $data[] = [
+                            'join_pay_member_id' => $item['join_pay_member_id'],
+                            'pay_addtimes' => $item['pay_addtimes'],
+                            'pay_amount' => $addedAmount,
+                            'pay_category' => 'ADDED',
+                            'balance' => number_format($balance, 2, '.', ''),
+                            'balance_type' => 'add',
+                        ];
+                    }
+                    $balance = $balance - $addedAmount;
+                }
 
                 $item['balance'] = number_format($balance, 2, '.', '');
                 $balance = $balance - $item['pay_amount'];
@@ -976,8 +1119,8 @@ class MemberService
                 continue;
             }
 
-            if (in_array($item['pay_category'], ['RECHARGE', 'REFUND', 'GIVE', 'PARTNER'])) {
-                if (!empty($item['pay_extend_json'])) {
+            if (in_array($item['pay_category'], ['RECHARGE', 'REFUND', 'GIVE', 'PARTNER', 'COMBINE', 'IN'])) {
+                if (!empty($item['pay_extend_json']) && !in_array($item['join_pay_order_id'], ['PARTNER', 'COMBINE'])) {
                     $extendJson = json_decode($item['pay_extend_json'], true);
                     if (isset($extendJson['added_amount']) && floatval($extendJson['added_amount']) > 0) {
                         $data[] = [
@@ -991,6 +1134,24 @@ class MemberService
                         $balance = $balance - $extendJson['added_amount'];
                     }
                 }
+                if (in_array($item['join_pay_order_id'], ['PARTNER', 'COMBINE'])) {
+                    $addedAmount = 0;
+                    if (!isset($payObjectJson['recharge_config'])) {
+                        $addedAmount = 0;
+                    }
+                    if (isset($payObjectJson['recharge_config']) && !empty($payObjectJson['recharge_config']['nbr']) && floatval($payObjectJson['recharge_config']['nbr'] > 0)) {
+                        $addedAmount = $payObjectJson['recharge_config']['nbr'];
+                        $data[] = [
+                            'join_pay_member_id' => $item['join_pay_member_id'],
+                            'pay_addtimes' => $item['pay_addtimes'],
+                            'pay_amount' => $addedAmount,
+                            'pay_category' => 'ADDED',
+                            'balance' => number_format($balance, 2, '.', ''),
+                            'balance_type' => 'add',
+                        ];
+                    }
+                    $balance = $balance - $addedAmount;
+                }
                 $item['balance'] = number_format($balance, 2, '.', '');
                 $balance = $balance - $item['pay_amount'];
             } else {
@@ -1019,7 +1180,7 @@ class MemberService
             return json_fail('暂无数据');
         }
         $balance = $account->member_account_surplus + $account->member_account_added;
-        $points = ClientPoints::where('join_client_points_member_id', $memberId)->get()->toArray();
+        $points = ClientPoints::where('join_client_points_member_id', $memberId)->orderBy('client_points_addtimes', 'DESC')->get()->toArray();
         $nowPoint = 0;
         foreach ($points as $key => &$point) {
             $point['goods_category'] = '其他';
@@ -1078,6 +1239,10 @@ class MemberService
     {
         Db::beginTransaction();
         try {
+            // 排重
+            if (Member::where('member_mobile', $params['mobile'])->where('member_status', 'ACTIVED')->exists()) {
+                throw new BusinessException("会员已存在");
+            }
             $memberId = self::generateMemberId();
             // 先写主表,要ID
             $memberData = [
@@ -1512,6 +1677,30 @@ class MemberService
         return $name;
     }
 
+    public static function getMemberCertName($mobile, $certName, $nickname)
+    {
+        $name = '';
+        if (!empty($certName)) {
+            $name = $certName;
+        }
+        if (!empty($name) && !empty($mobile)) {
+            $name .= '-' . $mobile;
+        } elseif (!empty($mobile)) {
+            $name = $mobile;
+        }
+
+        return $name;
+    }
+
+    public static function getMemberNickname($mobile)
+    {
+        if (empty($mobile)) {
+            return '';
+        }
+
+        return substr($mobile, -4, 4) . '会员';
+    }
+
     public static function getMemberNameByMemberId($memberId)
     {
         $member = Member::with([
@@ -1558,4 +1747,102 @@ class MemberService
             return $code;
         }
     }
+
+    public static function getAvatarUrl($url)
+    {
+        if (substr($url, 0, 1) == '.' || substr($url, 0, 1) == '/' || empty($url)) {
+            $url = "https://img.wanyuewellness.com.cn/images/avatar_default.png";
+        }
+
+        return $url;
+    }
+
+    public static function getRoleName($roleId)
+    {
+        if (empty($roleId)) {
+            return '';
+        }
+
+        return MemberRole::where('member_role_id', $roleId)->value('member_role_name');
+    }
+
+    /**
+     * @Desc 获取身份-合伙人,推荐官
+     * @Author Gorden
+     * @Date 2024/9/28 13:41
+     *
+     * @param $isPartner
+     * @param $isReferrer
+     * @return string
+     */
+    public static function getIdentity($isPartner, $isReferrer)
+    {
+        if ($isPartner == 'Y') {
+            return "PARTNER";
+        } elseif ($isReferrer == 'Y') {
+            return "REFERRER";
+        } else {
+            return 'NON';
+        }
+    }
+
+    /**
+     * @Desc 会员合伙人过期
+     * @Author Gorden
+     * @Date 2024/10/10 9:50
+     *
+     * @return void
+     */
+    public static function partnerExpired()
+    {
+        $members = Member::where('member_is_partner', 'Y')->get()->toArray();
+        foreach ($members as $member) {
+            $partnerJson = ['member_id' => $member['member_id']];
+            if (!empty($member['member_partner_json'])) {
+                $partnerJson = json_decode($member['member_partner_json'], true);
+            }
+            if (isset($partnerJson['duedate'])) {
+                $duedateUnix = strtotime($partnerJson['duedate'] . ' 23:59:59');
+                if ($duedateUnix > time()) {
+                    continue;
+                }
+            }
+
+            Member::where('member_id', $member['member_id'])->update(['member_is_partner' => 'N', 'member_partner_json' => '[]']);
+
+            _syslog("会员合伙人过期", "取消身份", false, $partnerJson, 1001);
+
+            Log::info("会员【" . $member['member_id'] . '】会员合伙人身份已过期', $partnerJson);
+        }
+    }
+
+    /**
+     * @Desc 康养推荐官过期
+     * @Author Gorden
+     * @Date 2024/10/10 10:14
+     *
+     * @return void
+     */
+    public static function referrerExpired()
+    {
+        $members = Member::where('member_is_referrer', 'Y')->get()->toArray();
+        foreach ($members as $member) {
+            $referrerJson = ['member_id' => $member['member_id']];
+            if (!empty($member['member_referrer_json'])) {
+                $referrerJson = json_decode($member['member_referrer_json'], true);
+            }
+            if (isset($referrerJson['duedate'])) {
+                $duedateUnix = strtotime($referrerJson['duedate'] . ' 23:59:59');
+                if ($duedateUnix > time()) {
+                    continue;
+                }
+            }
+
+            Member::where('member_id', $member['member_id'])->update(['member_is_referrer' => 'N', 'member_referrer_json' => '[]']);
+
+            _syslog("康养推荐官过期", "取消身份", false, $referrerJson, 1001);
+
+            Log::info("会员【" . $member['member_id'] . '】康养推荐官身份已过期', $referrerJson);
+        }
+    }
 }

+ 155 - 0
app/admin/service/order/CommissionService.php

@@ -0,0 +1,155 @@
+<?php
+
+namespace app\admin\service\order;
+
+use app\admin\service\member\MemberService;
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\MemberAccountList;
+use app\model\SysConfig;
+use support\Log;
+
+class CommissionService
+{
+    /*
+    {
+        "config": {
+            "name": "名称",
+            "valid": 365, //有效天
+            "protocol": 229, //服务协议分类ID
+            "notice":["", ""] //注意事项数组
+        },
+        "goods": {
+            "goods_id": "", //产品ID
+            "goods_sku_id": 0, //产品SKUID
+            "amount": "goods_sales_price" //核算金额字段
+        },
+        "commission": {
+            "direct": 0.2, //直属提成
+            "indirect": 0.1, //间属提成
+            "relate-consum": 0.1, //消费关联提成 带客
+            "invite-non-identity": 0.05, //邀请人非身份会员达成提成
+            "agent-senior-join": 0.05, //中介加盟合伙人提成
+            "agent-senior-strategy": 0.05, //中介战略合伙人提成
+            "account":"CASH" //提成写入账户
+        },
+        "account": {
+            "account":"CASH", //达成金额进入账户 不则为空
+            "added":0.12 //达成金额增值 不则为0
+        },
+        "control": {
+            "notify": "procMemberPartnerExec", //回调函数
+            "if_installment": "N", //是否支持分期
+            "if_commission_out": "Y" //提成是否可提现
+        }
+    }
+     */
+
+    /**
+     * @Desc 处理提成
+     * @Author Gorden
+     * @Date 2024/9/28 16:09
+     *
+     * @param $config
+     * @param $memberId
+     * @param $orderId
+     * @param $amount
+     * @param $accountListCategory
+     * @return void
+     */
+    public static function commission($config, $memberId, $orderId, $amount, $accountListCategory)
+    {
+        Log::info("开始处理提成", ['member_id' => $memberId, 'amount' => $amount]);
+        try {
+            $member = Member::where('member_id', $memberId)->first();
+            $configParams1 = [];
+            $configParams2 = [];
+            if (!empty($member->join_invite_member_id)) {
+                $member1 = Member::where('member_id', $member->join_invite_member_id)->first();
+                if ($member1->member_is_partner == 'Y') {
+                    $config1 = SysConfig::where('config_key', 'app-identity-partner-config')->first();
+                } else if ($member1->member_is_referrer == 'Y') {
+                    $config1 = SysConfig::where('config_key', 'app-identity-referrer-config')->first();
+                }
+                if (!empty($config1)){
+                    $configParams1 = json_decode($config1->config_value_json, true);
+                    Log::info("一级提成参数", ['config' => $configParams1]);
+                }
+                if (!empty($member1->join_invite_member_id)) {
+                    $member2 = Member::where('member_id', $member1->join_invite_member_id)->first();
+                    if ($member2->member_is_partner == 'Y') {
+                        $config2 = SysConfig::where('config_key', 'app-identity-partner-config')->first();
+                    } else if ($member1->member_is_referrer == 'Y') {
+                        $config2 = SysConfig::where('config_key', 'app-identity-referrer-config')->first();
+                    }
+                    if (!empty($config2)){
+                        $configParams2 = json_decode($config2->config_value_json, true);
+                        Log::info("二级提成参数", ['config' => $configParams2]);
+                    }
+                }
+            }
+            if (empty($configParams1) && empty($configParams2)) {
+                Log::info("提成失败,提成参数异常", ['member_id' => $memberId, 'amount' => $amount]);
+            }
+            // 提成记录JSON
+            $accountListJson = [
+                'line' => 'relate-consum',
+                'amount' => $amount,
+                'order_id' => $orderId,
+                'master_member_id' => $memberId
+            ];
+            // 直属提成
+            if (!empty($member1) && ($member1->member_is_partner == 'Y' || $member1->member_is_referrer == 'Y') && !empty($configParams1)) {
+                $accountListJson['identity'] = MemberService::getIdentity($member1->member_is_partner, $member1->member_is_referrer);
+                $accountListJson['line'] = 'direct';
+                $member1CommissionAmount = $configParams1['commission']['direct'] * $amount;
+                self::commissionToMember($member1->member_id, $accountListJson, $member1CommissionAmount, $accountListCategory);
+                Log::info("直属提成参数", ['member_id' => $member1->member_id, 'account_list_json' => $accountListJson, 'amount' => $member1CommissionAmount, 'category' => $accountListCategory]);
+
+            }
+            // 间属提成
+            if (!empty($member2) && ($member2->member_is_partner == 'Y' || $member2->member_is_referrer == 'Y') && !empty($configParams2)) {
+                $accountListJson['identity'] = MemberService::getIdentity($member2->member_is_partner, $member2->member_is_referrer);
+                $accountListJson['line'] = 'indirect';
+                $member2CommissionAmount = $configParams2['commission']['indirect'] * $amount;
+                self::commissionToMember($member2->member_id, $accountListJson, $member2CommissionAmount, $accountListCategory);
+                Log::info("间属提成参数", ['member_id' => $member2->member_id, 'account_list_json' => $accountListJson, 'amount' => $member2CommissionAmount, 'category' => $accountListCategory]);
+
+            }
+        } catch (\Exception $e) {
+            dump('提成处理失败:'.$e->getMessage());
+            _syslog("提成处理失败", '提成处理失败');
+        }
+    }
+
+    /**
+     * @Desc 提成到账户
+     * @Author Gorden
+     * @Date 2024/9/25 15:14
+     *
+     * @param $memberId
+     * @param $accountListJson
+     * @param $money
+     * @return void
+     */
+    private static function commissionToMember($memberId, $accountListJson, $money, $accountListCategory)
+    {
+        $account = MemberAccount::where('join_account_member_id', $memberId)->where('member_account_classify', 'CASH')->first();
+        $account->member_account_income = $account->member_account_income + $money;
+        $account->member_account_surplus = $account->member_account_surplus + $money;
+        $account->save();
+
+        MemberAccountList::insert([
+            'join_list_member_account_nbr' => $memberId . '-CASH',
+            'join_member_account_list_member_id' => $memberId,
+            'member_account_list_status' => 'ACTIVED',
+            'member_account_list_attr' => 'IN',
+            'member_account_list_classify' => $accountListJson['identity'],
+            'member_account_list_category' => $accountListCategory,
+            'member_account_list_datetime' => date('Y-m-d H:i:s'),
+            'member_account_list_amount' => $money,
+            'member_account_list_json' => json_encode($accountListJson),
+            'member_account_list_addtimes' => time()
+        ]);
+    }
+}

+ 56 - 12
app/admin/service/order/OrderService.php

@@ -111,12 +111,16 @@ class OrderService
             $orders = Order::where('order_status_system', 'PAYING')
                 ->where('order_category', '<>', 'DISHES')     // 点餐不自动取消
                 ->where('order_category', '<>', 'VIP')      // 康养城订单不自动取消,有分次付款
+                ->where('order_category', '<>', 'COMBINE')      // 新客专享
+                ->where('order_category', '<>', 'PARTNER')      // 会员合伙人
+                ->where('order_category', '<>', 'REFERRER')      // 推荐官
                 ->where(function ($query) {
                     $query->where('order_platform', '<>', 'SYSTEM')->orWhereNull('order_platform');
                 })
                 ->where('order_addtimes', '<', $timeUnix)
                 ->get();
             foreach ($orders as $order) {
+                \support\Log::info("取消的订单", ['order_id' => $order->order_id]);
                 // 订单主表
                 Order::where('order_id', $order->order_id)->update([
                     'order_is_complete' => 'Y',
@@ -178,6 +182,7 @@ class OrderService
 
             Db::commit();
         } catch (\Exception $e) {
+            \support\Log::error("取消订单失败:" . $e->getMessage());
             Db::rollBack();
         }
     }
@@ -318,7 +323,8 @@ class OrderService
                 $result = self::findAlipay($params['orderGroupId'], 0);
             } catch (\Exception $e) {
                 $log->error("ALIPAY", ['msg' => $e->getMessage()]);
-                throw new BusinessException("支付失败");
+                throw new BusinessException("支付失败:" . $e->getMessage());
+//                $result['key'] = 'value';
             }
 
             try {
@@ -347,6 +353,7 @@ class OrderService
         try {
             $result = Pay::wechat(config('payment.wxpay'))->find($orderId, 'pos');
             $result = json_decode(json_encode($result), true);
+            SupportLog::channel('pay')->info("FIND_WXPAY_RESULT", $result);
         } catch (\Exception $e) {
             SupportLog::channel('pay')->error("FIND_WXPAY", ['msg' => $e->getMessage()]);
         }
@@ -355,7 +362,7 @@ class OrderService
             SupportLog::channel('pay')->info("FIND_WXPAY_SUCCESS", ['nbr' => $nbr, 'order_id' => $orderId]);
             return $result;
         } else {
-            if ($nbr > 1) {
+            if ($nbr > 0) {
                 SupportLog::channel('pay')->error("FIND_WXPAY", ['msg' => '订单查询失败', 'order_id' => $orderId]);
                 return ['msg' => '订单查询失败'];
             }
@@ -380,6 +387,7 @@ class OrderService
         try {
             $result = Pay::alipay(config('payment.alipay'))->find($orderId);
             $result = json_decode(json_encode($result), true);
+            SupportLog::channel('pay')->info("FIND_ALIPAY_RESULT", $result);
         } catch (\Exception $e) {
             SupportLog::channel('pay')->error("FIND_ALIPAY", ['msg' => $e->getMessage()]);
         }
@@ -388,7 +396,7 @@ class OrderService
             SupportLog::channel('pay')->info("FIND_ALIPAY_SUCCESS", ['nbr' => $nbr, 'order_id' => $orderId]);
             return $result;
         } else {
-            if ($nbr > 1) {
+            if ($nbr > 0) {
                 SupportLog::channel('pay')->error("FIND_ALIPAY", ['msg' => '订单查询失败', 'order_id' => $orderId]);
                 return ['msg' => '订单查询失败'];
             }
@@ -405,7 +413,7 @@ class OrderService
     {
         foreach ($params['goodsContentList'] as $goods) {
             // 减库存,规格和总库存
-            if (!isset($params['submit_goods_classify']) || !in_array($params['submit_goods_classify'], ['MEALS', 'PACKAGE'])) {
+            if (!isset($params['submit_goods_classify']) || !in_array($params['submit_goods_classify'], ['MEALS', 'PACKAGE', 'COMBINE'])) {
                 $goodsSku = GoodsSku::where('goods_sku_id', $goods['sku_id'])->first();
                 $skuStorageJson = json_decode($goodsSku->goods_sku_storage_json, true);
                 if (isset($skuStorageJson['storage']) && !empty($skuStorageJson['storage'])) {
@@ -482,7 +490,6 @@ class OrderService
      */
     public static function createProductPayConstituteDetail($params)
     {
-        dump($params);
         $payDetail = new PayDetail();
         $payDetail->join_pay_member_id = $params['join_order_member_id'];
         $payDetail->join_pay_order_id = $params['orderGroupId'];
@@ -583,9 +590,11 @@ class OrderService
                     ->leftJoin('coupon', 'coupon.coupon_id', '=', 'coupon_detail.join_detail_coupon_id')
                     ->select('coupon_detail.coupon_detail_id', 'coupon_goods.coupon_goods_id', 'coupon_id', 'coupon_classify', 'coupon_value', 'coupon_minimum_limit', 'coupon_category')
                     ->where('coupon_goods.join_coupon_goods_id', $goodsId)
-                    ->where('coupon_goods.join_coupon_goods_sku_id', $good['sku_id'])
+//                    ->where('coupon_goods.join_coupon_goods_sku_id', $good['sku_id'])
                     ->where('join_goods_coupon_id', $couponId)
-                    ->where('coupon_detail.join_coupon_detail_member_id', $memberId);
+                    ->where('coupon_detail.join_coupon_detail_member_id', $memberId)
+                    ->where('coupon_detail.coupon_detail_gain_datetime', '<', date('Y-m-d H:i:s'))
+                    ->where('coupon_detail.coupon_detail_deadline_datetime', '>', date('Y-m-d H:i:s'));
                 if ($settlementNow == 'Y' && $type == 'pay') {
                     $couponDetail = $couponDetail->whereIn('coupon_detail.coupon_detail_status', ['ACTIVED']);
                 } else {
@@ -599,7 +608,7 @@ class OrderService
                 if ($settlementNow == 'Y') {
                     $updateData = [
                         'coupon_detail_status' => 'USED',
-                        'coupon_detail_used_datetime' => date('Y-m-d H:i:s')
+                        'coupon_detail_used_datetime' => date('Y-m-d H:i:s'),
                     ];
                 } else {
                     $updateData = [
@@ -766,9 +775,11 @@ class OrderService
                     ->leftJoin('coupon', 'coupon.coupon_id', '=', 'coupon_detail.join_detail_coupon_id')
                     ->select('coupon_detail.coupon_detail_id', 'coupon_goods.coupon_goods_id', 'coupon_id', 'coupon_classify', 'coupon_value', 'coupon_minimum_limit', 'coupon_category')
                     ->where('coupon_goods.join_coupon_goods_id', $goodsId)
-                    ->where('coupon_goods.join_coupon_goods_sku_id', $good['sku_id'])
+//                    ->where('coupon_goods.join_coupon_goods_sku_id', $good['sku_id'])
                     ->where('join_goods_coupon_id', $couponId)
-                    ->where('coupon_detail.join_coupon_detail_member_id', $memberId);
+                    ->where('coupon_detail.join_coupon_detail_member_id', $memberId)
+                    ->where('coupon_detail.coupon_detail_gain_datetime', '<', date('Y-m-d H:i:s'))
+                    ->where('coupon_detail.coupon_detail_deadline_datetime', '>', date('Y-m-d H:i:s'));
                 if ($settlementNow == 'Y') {
                     $couponDetail = $couponDetail->whereIn('coupon_detail.coupon_detail_status', ['ACTIVED']);
                 } else {
@@ -866,14 +877,15 @@ class OrderService
             $goodsIds = array_column($goods, 'goods_id');
             $couponGoods = CouponGoods::whereIn('join_coupon_goods_id', $goodsIds)
                 ->where('join_goods_coupon_id', $couponId)
-                ->select('join_coupon_goods_id', 'join_coupon_goods_sku_id')
+//                ->select('join_coupon_goods_id', 'join_coupon_goods_sku_id')
                 ->get()
                 ->toArray();
             $count = 0;
             $amount = 0;
             foreach ($couponGoods as $couponGood) {
                 foreach ($goods as $good) {
-                    if ($good['goods_id'] == $couponGood['join_coupon_goods_id'] && $good['sku_id'] == $couponGood['join_coupon_goods_sku_id']) {
+//                    if ($good['goods_id'] == $couponGood['join_coupon_goods_id'] && $good['sku_id'] == $couponGood['join_coupon_goods_sku_id']) {
+                    if ($good['goods_id'] == $couponGood['join_coupon_goods_id']) {
                         $count += $good['nbr'];
                         $amount += $good['goods_sales_price'] * $good['nbr'];
                     }
@@ -919,6 +931,38 @@ class OrderService
         return $result;
     }
 
+    /**
+     * @Desc 拆单提成
+     * @Author Gorden
+     * @Date 2024/9/29 16:27
+     *
+     * @return void
+     */
+    public static function splitOrderCommission($params)
+    {
+        $orders = Order::where('order_groupby', $params['orderGroupId'])->get()->toArray();
+        if (!empty($orders)) {
+            foreach ($orders as $orderItem) {
+                $params['member_id'] = $orderItem['join_order_member_id'];
+                $params['orderId'] = $orderItem['order_id'];
+                Event::dispatch('commission.order', $params);
+            }
+        }
+    }
+
+    public static function splitOrderStatisticsInOut($params)
+    {
+        $orders = Order::where('order_groupby', $params['orderGroupId'])->get()->toArray();
+        if (!empty($orders)) {
+            foreach ($orders as $orderItem) {
+                $params['member_id'] = $orderItem['join_order_member_id'];
+                $params['orderId'] = $orderItem['order_id'];
+                $params['inout_category'] = '标准订单收入';
+                Event::dispatch('statistics.inout.in', $params);
+            }
+        }
+    }
+
 
     public static $couponClassify = [
         'wipe' => '抹零',

+ 1 - 1
app/admin/validate/goods/GoodsValidate.php

@@ -22,7 +22,7 @@ class GoodsValidate extends Validate
         'goods_service_json|服务' => 'isJson',
         'goods_title|简介' => 'max:300',
         'goods_cover|封面' => 'url',
-        'goods_on_addtimes|上架时间' => 'date',
+//        'goods_on_addtimes|上架时间' => 'date',
         'goods_sort|排序' => 'integer',
         'goods_groupby|分组' => 'max:32;',
         'goods_extend_json' => 'isJson',

+ 69 - 0
app/command/NewCustomCommand.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace app\command;
+
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\Order;
+use app\model\PayDetail;
+use support\Db;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class NewCustomCommand extends Command
+{
+
+    protected static $defaultName = 'NewCustomCommand';
+    protected static $defaultDescription = 'NewCustomCommand';
+
+    /**
+     * @return void
+     */
+    protected function configure()
+    {
+        // $this->addArgument('name', InputArgument::OPTIONAL, '余额账户转福利账户');
+        $this->addArgument('name', InputArgument::OPTIONAL, '新客专享回填订单号');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $payDetails = PayDetail::where('pay_category', 'COMBINE')->get()->toArray();
+        try {
+
+            foreach ($payDetails as $payDetail) {
+                if (!empty($payDetail['join_pay_object_json'])) {
+                    $payObjectJson = json_decode($payDetail['join_pay_object_json'], true);
+                }
+                // 查订单号
+                $payDetailOrder = PayDetail::where('join_pay_order_id', 'COMBINE')
+                    ->where('join_pay_member_id', $payDetail['join_pay_member_id'])
+                    ->first();
+                if (!empty($payDetailOrder->join_pay_object_json)) {
+                    $payObjectJson2 = json_decode($payDetailOrder->join_pay_object_json, true);
+                    if (isset($payObjectJson2['recharge_order_id'])) {
+                        continue;
+                    }
+                    if (isset($payObjectJson['order_id'])) {
+                        $payObjectJson2['recharge_order_id'] = $payObjectJson['order_id'];
+                        PayDetail::where('pay_id', $payDetailOrder->pay_id)->update([
+                            'join_pay_object_json' => json_encode($payObjectJson2)
+                        ]);
+                    }
+                }
+
+                echo "支付记录【" . $payDetail['pay_id'] . "】已处理完成\n";
+            }
+            Db::commit();
+
+            return self::SUCCESS;
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+
+            Db::rollBack();
+            return self::SUCCESS;
+        }
+
+    }
+}

+ 69 - 0
app/command/PartnerCommand.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace app\command;
+
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\Order;
+use app\model\PayDetail;
+use support\Db;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class PartnerCommand extends Command
+{
+
+    protected static $defaultName = 'PartnerCommand';
+    protected static $defaultDescription = 'PartnerCommand';
+
+    /**
+     * @return void
+     */
+    protected function configure()
+    {
+        // $this->addArgument('name', InputArgument::OPTIONAL, '余额账户转福利账户');
+        $this->addArgument('name', InputArgument::OPTIONAL, '会员合伙人回填订单号');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $payDetails = PayDetail::where('pay_category', 'PARTNER')->get()->toArray();
+        try {
+
+            foreach ($payDetails as $payDetail) {
+                if (!empty($payDetail['join_pay_object_json'])) {
+                    $payObjectJson = json_decode($payDetail['join_pay_object_json'], true);
+                }
+                // 查订单号
+                $payDetailOrder = PayDetail::where('join_pay_order_id', 'PARTNER')
+                    ->where('join_pay_member_id', $payDetail['join_pay_member_id'])
+                    ->first();
+                if (!empty($payDetailOrder->join_pay_object_json)) {
+                    $payObjectJson2 = json_decode($payDetailOrder->join_pay_object_json, true);
+                    if (isset($payObjectJson2['recharge_order_id'])) {
+                        continue;
+                    }
+                    if (isset($payObjectJson['order_id'])) {
+                        $payObjectJson2['recharge_order_id'] = $payObjectJson['order_id'];
+                        PayDetail::where('pay_id', $payDetailOrder->pay_id)->update([
+                            'join_pay_object_json' => json_encode($payObjectJson2)
+                        ]);
+                    }
+                }
+
+                echo "支付记录【" . $payDetail['pay_id'] . "】已处理完成\n";
+            }
+            Db::commit();
+
+            return self::SUCCESS;
+        } catch (\Exception $e) {
+            dump($e->getMessage());
+
+            Db::rollBack();
+            return self::SUCCESS;
+        }
+
+    }
+}

+ 6 - 3
app/command/WelfareAccountCommand.php

@@ -7,6 +7,7 @@ use app\model\MemberAccount;
 use app\model\Order;
 use app\model\PayDetail;
 use support\Db;
+use support\Log;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
@@ -32,9 +33,8 @@ class WelfareAccountCommand extends Command
         $accounts = MemberAccount::where('member_account_classify','WELFARE')->get()->toArray();
         Db::beginTransaction();
         try{
-            
             foreach($accounts as $account){
-                if ($account['member_account_income'] == '0.00'){
+                if ($account['member_account_income'] == '0.00' || $account['member_account_surplus'] == '0.00'){
                     echo "会员【" . $account['join_account_member_id'] . "】跳过\n";
                     continue;
                 }
@@ -46,6 +46,9 @@ class WelfareAccountCommand extends Command
 
                 // 插入清零记录
                 $this->generatePayDetail($account);
+                // 记录日志
+                Log::info("会员【" . $account['join_account_member_id'] . "】清除福利已完成",$account);
+
                 echo "会员【" . $account['join_account_member_id'] . "】已处理完成\n";
             }
             Db::commit();
@@ -55,8 +58,8 @@ class WelfareAccountCommand extends Command
             dump($e->getMessage());
 
             Db::rollBack();
+            return self::SUCCESS;
         }
-
     }
 
     /**

+ 82 - 63
app/event/order/CommissionEvent.php

@@ -6,9 +6,11 @@ use app\model\Member;
 use app\model\MemberAccount;
 use app\model\MemberAccountList;
 use app\model\Order;
+use app\model\PayDetail;
 use app\model\SysConfig;
 use support\Db;
 use support\exception\BusinessException;
+use support\Log;
 
 class CommissionEvent
 {
@@ -45,53 +47,66 @@ class CommissionEvent
 //}
     public function order($params)
     {
-        // 提成设置
-//        $config = SysConfig::where('config_key', 'app-identity-partner-config')->first();
-//        $configParams = json_decode($config->config_value_json, true);
-//        Db::beginTransaction();
-//        try {
-//            $order = Order::where('order_id', $params['orderId'])->where('order_status_payment', 'SUCCESS')->first();
-//            // 有传过来的
-//            if (!empty($params['join_invite_member_id'])) {
-//                $inviteMemberId = $params['join_invite_member_id'];
-//            } else {
-//                // 下单人
-//                $member = Member::where('member_id', $order->join_order_member_id)->first();
-//                if (empty($member->join_invite_member_id)) {
-//                    throw new BusinessException("没有直属上级");
-//                }
-//                $inviteMemberId = $member->join_invite_member_id;
-//            }
-//            // 上级
-//            $memberUp = Member::where('member_id', $inviteMemberId)->first();
-//            if ($memberUp->member_is_partner == 'Y') {
-//                $identity = 'PARTNER';
-//            } elseif ($memberUp->member_is_referrer == 'Y') {
-//                $identity = 'REFERRER';
-//            }else{
-//                throw new BusinessException("直属上级非合伙人或者推荐官");
-//            }
-//            // 记录的JSON
-//            $accountListJson = [
-//                'line' => 'relate-consum',
-//                'amount' => $order->order_amount_pay,
-//                'identity' => $identity,
-//                'order_id' => $order->order_id,
-//                'master_member_id' => $order->join_order_member_id
-//            ];
-//            // 提成
-//            $this->commissionToMember($inviteMemberId, $accountListJson, round($configParams['commission']['relate-consum'] * $order->order_amount_pay, 2), '消费');
-//
-//            Db::commit();
-//            _syslog("提成", "计算提成成功");
-//        } catch (BusinessException $e) {
-//            Db::rollBack();
-//            _syslog("提成", "计算提成失败:" . $e->getMessage());
-//        } catch (\Exception $e) {
-//            dump($e->getMessage());
-//            Db::rollBack();
-//            _syslog("提成", "计算提成失败");
-//        }
+        Log::info("计算消费提成", $params);
+        $accountListCategory = $params['member_account_list_category'] ?? '消费';
+        Db::beginTransaction();
+        try {
+            $order = Order::where('order_id', $params['orderId'])->where('order_status_payment', 'SUCCESS')->first();
+            $actualPaymentAmount = PayDetail::whereJsonContains('join_pay_object_json->order_id', $params['orderId'])
+                ->where('pay_status', 'SUCCESS')
+                ->whereIn('pay_prepayid', ['WXPAY', 'ALIPAY', 'OFFLINE_WXPAY', 'OFFLINE_ALIPAY', 'MONEY'])
+                ->sum('pay_amount');
+            if ($actualPaymentAmount == 0) {
+                throw new BusinessException("微信、支付宝、现金支付金额为0");
+            }
+            // 有传过来的
+            if (!empty($params['join_invite_member_id'])) {
+                $inviteMemberId = $params['join_invite_member_id'];
+            } else {
+                // 下单人
+                $member = Member::where('member_id', $order->join_order_member_id)->first();
+                if (empty($member->join_invite_member_id)) {
+                    throw new BusinessException("没有直属上级");
+                }
+                $inviteMemberId = $member->join_invite_member_id;
+            }
+            // 上级
+            $memberUp = Member::where('member_id', $inviteMemberId)->first();
+            if ($memberUp->member_is_partner == 'Y') {
+                $identity = 'PARTNER';
+                // 提成设置
+                $config = SysConfig::where('config_key', 'app-identity-partner-config')->first();
+            } elseif ($memberUp->member_is_referrer == 'Y') {
+                $identity = 'REFERRER';
+                // 提成设置
+                $config = SysConfig::where('config_key', 'app-identity-referrer-config')->first();
+            } else {
+                throw new BusinessException("直属上级非合伙人或者推荐官");
+            }
+            $configParams = json_decode($config->config_value_json, true);
+            // 记录的JSON
+            $accountListJson = [
+                'line' => 'relate-consum',
+                'amount' => $order->order_amount_pay,
+                'identity' => $identity,
+                'order_id' => $order->order_id,
+                'master_member_id' => $order->join_order_member_id
+            ];
+            Log::info("提成参数", $accountListJson);
+            // 提成
+            $this->commissionToMember($inviteMemberId, $accountListJson, round($configParams['commission']['relate-consum'] * $actualPaymentAmount, 2), $accountListCategory);
+
+            Db::commit();
+            _syslog("提成", "计算提成成功");
+        } catch (BusinessException $e) {
+            Db::rollBack();
+            Log::error("计算消费提成异常", ['msg' => $e->getMessage()]);
+            _syslog("提成", "计算提成失败:" . $e->getMessage());
+        } catch (\Exception $e) {
+            Db::rollBack();
+            Log::error("计算消费提成异常", ['msg' => $e->getMessage()]);
+            _syslog("提成", "计算提成失败");
+        }
     }
 
     /**
@@ -106,22 +121,26 @@ class CommissionEvent
      */
     private function commissionToMember($memberId, $accountListJson, $money, $accountListCategory)
     {
-        $account = MemberAccount::where('join_account_member_id', $memberId)->where('member_account_classify', 'CASH')->first();
-        $account->member_account_income = $account->member_account_income + $money;
-        $account->member_account_surplus = $account->member_account_surplus + $money;
-        $account->save();
+        try {
+            $account = MemberAccount::where('join_account_member_id', $memberId)->where('member_account_classify', 'CASH')->first();
+            $account->member_account_income = $account->member_account_income + $money;
+            $account->member_account_surplus = $account->member_account_surplus + $money;
+            $account->save();
 
-        MemberAccountList::insert([
-            'join_list_member_account_nbr' => $memberId . '-CASH',
-            'join_member_account_list_member_id' => $memberId,
-            'member_account_list_status' => 'ACTIVED',
-            'member_account_list_attr' => 'IN',
-            'member_account_list_classify' => $accountListJson['identity'],
-            'member_account_list_category' => $accountListCategory,
-            'member_account_list_datetime' => date('Y-m-d H:i:s'),
-            'member_account_list_amount' => $money,
-            'member_account_list_json' => json_encode($accountListJson),
-            'member_account_list_addtimes' => time()
-        ]);
+            MemberAccountList::insert([
+                'join_list_member_account_nbr' => $memberId . '-CASH',
+                'join_member_account_list_member_id' => $memberId,
+                'member_account_list_status' => 'ACTIVED',
+                'member_account_list_attr' => 'IN',
+                'member_account_list_classify' => $accountListJson['identity'],
+                'member_account_list_category' => $accountListCategory,
+                'member_account_list_datetime' => date('Y-m-d H:i:s'),
+                'member_account_list_amount' => $money,
+                'member_account_list_json' => json_encode($accountListJson),
+                'member_account_list_addtimes' => time()
+            ]);
+        } catch (\Exception $e) {
+            Log::error("提成入账失败", ['msg' => $e->getMessage()]);
+        }
     }
 }

+ 131 - 0
app/event/order/NewCustomerEvent.php

@@ -0,0 +1,131 @@
+<?php
+
+namespace app\event\order;
+
+use app\admin\service\coupon\CouponDetailService;
+use app\model\Coupon;
+use app\model\CouponDetail;
+use app\model\Goods;
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\PayDetail;
+use app\model\SysSerial;
+use support\Db;
+use support\exception\BusinessException;
+use support\Log;
+
+class NewCustomerEvent
+{
+    public function grant($params)
+    {
+        Db::beginTransaction();
+        try {
+            Log::info("进入新客专享事件", $params);
+            // 余额账户
+            $memberAccount = MemberAccount::where('join_account_member_id', $params['member_id'])->where('member_account_classify', 'CASH')->first();
+            // 分期付款
+            if (isset($params['order_amount_paid']) && $params['order_amount_paid'] > 0) {
+                $params['order_amount_pay'] = $params['order_amount_paid'] + $params['order_amount_pay'];
+            }
+
+            //发放优惠券
+            $goods = Goods::where('goods_id', $params['join_sheet_goods_id'])->select('goods_id','goods_name','goods_attribute_json')->first();
+            if (!empty($goods) && !empty($goods->goods_attribute_json)) {
+                $goodsAttributeJson = json_decode($goods->goods_attribute_json, true);
+                if (isset($goodsAttributeJson['account'])) {
+                    $memberAccount->member_account_income = $memberAccount->member_account_income + $goodsAttributeJson['account']['amount'];
+                    $memberAccount->member_account_surplus = $memberAccount->member_account_surplus + $goodsAttributeJson['account']['amount'];
+                    $memberAccount->save();
+
+                    Log::info("余额账户入账:" . $goodsAttributeJson['account']['amount']);
+
+                    // PayDetail 里写条充值记录
+                    PayDetail::insert([
+                        'join_pay_member_id' => $params['member_id'],
+                        'join_pay_order_id' => 'COMBINE',
+                        'join_pay_object_json' => json_encode([
+                            'recharge_order_id' => $params['orderId'],
+                            'recharge_config' => ['nbr' => 0],
+                            'recharge_goods_id' => $goods->goods_id,
+                            'recharge_goods_name' => $goods->goods_name
+                        ]),
+                        'pay_status' => 'SUCCESS',
+                        'pay_category' => 'RECHARGE',
+                        'pay_amount' => $goodsAttributeJson['account']['amount'],
+                        'pay_paytimes' => date('Y-m-d H:i:s'),
+                        'pay_prepayid' => $params['member_id'] . '-CASH',
+                        'pay_json_request' => '[]',
+                        'pay_json_response' => '[]',
+                        'pay_extend_json' => '[]',
+                        'pay_addtimes' => time()
+                    ]);
+                }
+                if (!empty($goodsAttributeJson['coupon'])) {
+                    foreach ($goodsAttributeJson['coupon'] as $key => $coupon) {
+                        $couponModel = Coupon::where('coupon_id', $key)->select('coupon_id', 'coupon_validdate_day', 'coupon_validdate_end', 'coupon_is_period', 'coupon_number')->first();
+                        if (empty($couponModel)) {
+                            continue;
+                        }
+                        // 券是否过期
+                        if (!empty($couponModel->coupon_validdate_end) && strtotime($couponModel->coupon_validdate_end) < time()) {
+                            continue;
+                        }
+                        // 发周期券
+                        if ($couponModel->coupon_is_period == 'Y') {
+                            // 发券参数
+                            $couponSendParams = [
+                                'gettype' => 'COMBINE',
+                                'coupon_id' => $key,
+                                'member_id' => $params['member_id']
+                            ];
+
+                            Log::info("发周期券参数", $couponSendParams);
+
+                            CouponDetailService::sendPeriodCoupon($couponSendParams);
+                            continue;
+                        }
+
+                        $couponResidue = 'all';
+                        if ($couponModel->coupon_number != 0) {
+                            $couponResidue = CouponDetail::where('join_detail_coupon_id', $key)->whereIn('coupon_detail_status', ['INIT', 'PENDING'])->count();
+                            // 超出优惠券设置的数量
+                            if ($couponResidue < $coupon['num']) {
+                                continue;
+                            }
+                        }
+                        $endDate = '';
+                        if (!empty($couponModel->coupon_validdate_end)) {
+                            $endDate = $couponModel->coupon_validdate_end;
+                        } elseif ($couponModel->coupon_validdate_day > 0) {
+                            $endDate = date('Y-m-d H:i:s', time() + ($couponModel->coupon_validdate_day * 24 * 3600) - 1);
+                        }
+                        // 发券参数
+                        $couponSendParams = [
+                            'gettype' => 'COMBINE',
+                            'coupon_id' => $key,
+                            'chooseCouponNbr' => $coupon['num'],
+                            'member_id' => $params['member_id'],
+                            'coupon_detail_gain_datetime' => date('Y-m-d H:i:s'),
+                            'coupon_detail_deadline_datetime' => $endDate,
+                        ];
+
+                        Log::info("发普通券参数", $couponSendParams);
+                        if ($couponResidue != 'all' && $couponResidue - $coupon['num'] >= 0) {
+                            CouponDetailService::customSendCouponHave($couponSendParams);
+                        } else {
+                            for ($i = 0; $i < $coupon['num']; $i++) {
+                                CouponDetailService::customSendCoupon($couponSendParams);
+                            }
+                        }
+                    }
+                }
+            }
+
+            Db::commit();
+        } catch (\Exception $e) {
+            Db::rollBack();
+            _syslog("新客专享", "处理失败");
+            Log::error("新客专享处理失败", ['msg' => $e->getMessage()]);
+        }
+    }
+}

+ 69 - 63
app/event/order/PartnerEvent.php

@@ -2,81 +2,87 @@
 
 namespace app\event\order;
 
-use app\model\Coupon;
-use app\model\CouponDetail;
+use app\admin\service\coupon\CouponService;
+use app\admin\service\order\CommissionService;
 use app\model\Goods;
 use app\model\Member;
 use app\model\MemberAccount;
 use app\model\PayDetail;
 use app\model\SysConfig;
-use app\model\SysSerial;
+use support\Db;
+use support\exception\BusinessException;
 use support\Log;
 
 class PartnerEvent
 {
     public function grant($params)
     {
-        $config = SysConfig::where('config_key', 'app-identity-partner-config')->first();
-        $configParams = json_decode($config->config_value_json, true);
-        // 更新身份
-        Member::where('member_id', $params['member_id'])->update(['member_is_partner' => 'Y']);
-        // 余额账户
-        $memberAccount = MemberAccount::where('join_account_member_id', $params['member_id'])->where('member_account_classify', 'CASH')->first();
-        // 分期付款
-        if (isset($params['order_amount_paid']) && $params['order_amount_paid'] > 0) {
-            $params['order_amount_pay'] = $params['order_amount_paid'] + $params['order_amount_pay'];
-        }
-        // 增值到最后一条支付记录
-        $added = 0;
-        $payDetail = PayDetail::whereJsonContains('join_pay_object_json->order_id', $params['orderId'])->orderBy('pay_addtimes', 'DESC')->first();
-        if (!empty($configParams['account']['added'])) {
-            $params['order_amount_pay'] = 10000;
-            $added = round($params['order_amount_pay'] * $configParams['account']['added'], 2);
-            $payDetail->pay_extend_json = json_encode(['added_rate' => $configParams['account']['added'], 'added_amount' => $added]);
-            $payDetail->save();
-        }
-        // 入账
-        $memberAccount->member_account_income = $memberAccount->member_account_income + $params['order_amount_pay'];
-        $memberAccount->member_account_surplus = $memberAccount->member_account_surplus + $params['order_amount_pay'];
-        $memberAccount->member_account_added = $memberAccount->member_account_added + $added;
-        $memberAccount->save();
-        //发放优惠券
-        $goods = Goods::where('goods_id', $params['join_sheet_goods_id'])->select('goods_attribute_json')->first();
-        if (!empty($goods) && !empty($goods->goods_attribute_json)) {
-            $goodsAttributeJson = json_decode($goods->goods_attribute_json, true);
-            if (!empty($goodsAttributeJson['coupon'])) {
-                foreach ($goodsAttributeJson['coupon'] as $key => $coupon) {
-                    $couponModel = Coupon::where('coupon_id', $key)->select('coupon_id', 'coupon_validdate_day', 'coupon_validdate_end')->first();
-                    if (empty($couponModel)) {
-                        continue;
-                    }
-                    // 券是否过期
-                    if (!empty($couponModel->coupon_validdate_end) && strtotime($couponModel->coupon_validdate_end) < time()) {
-                        continue;
-                    }
-                    $endDate = '';
-                    if (!empty($couponModel->coupon_validdate_end)) {
-                        $endDate = $couponModel->coupon_validdate_end;
-                    } elseif ($couponModel->coupon_validdate_day > 0) {
-                        $endDate = date('Y-m-d H:i:s', time() + ($couponModel->coupon_validdate_day * 24 * 3600) - 1);
-                    }
-
-                    $num = $coupon['num'];
-                    for ($i = 0; $i < $num; $i++) {
-                        CouponDetail::insert([
-                            'coupon_detail_id' => 'CUDT' . date("ymdHi") . random_string(4, 'up'),
-                            'join_detail_coupon_id' => $key,
-                            'join_coupon_detail_member_id' => $params['member_id'],
-                            'coupon_detail_status' => 'ACTIVED',
-                            'coupon_detail_gain_datetime' => date('Y-m-d H:i:s'),
-                            'coupon_detail_deadline_datetime' => $endDate,
-                            'coupon_detail_json' => '[]',
-                            'coupon_detail_extend_json' => json_encode(['gettype' => 'PARTNER']),
-                            'coupon_detail_addtimes' => time()
-                        ]);
-                    }
-                }
+        try {
+            Log::info("进入会员合伙人事件", $params);
+            $config = SysConfig::where('config_key', 'app-identity-partner-config')->first();
+            $configParams = json_decode($config->config_value_json, true);
+            // 更新身份{"duedate": "2025-09-26", "datetime": "2024-09-27 14:40:51", "order_id": "OD2409271440L951"}
+            $memberUpdateData = [
+                'member_partner_json' => json_encode([
+                    'datetime' => date('Y-m-d H:i:s'),
+                    'duedate' => date('Y-m-d', strtotime("+" . ($configParams['config']['valid']-1) . ' day')),
+                    'order_id' => $params['orderId'],
+                    'dept_id' => $params['dept_id'] ?? '',
+                    'dept_name' => $params['dept_name'] ?? ''
+                ]),
+                'member_is_partner' => 'Y',
+            ];
+            Member::where('member_id', $params['member_id'])->update($memberUpdateData);
+            // 余额账户
+            $memberAccount = MemberAccount::where('join_account_member_id', $params['member_id'])->where('member_account_classify', 'CASH')->first();
+            // 增值到最后一条支付记录
+            $added = 0;
+//            $payDetail = PayDetail::whereJsonContains('join_pay_object_json->order_id', $params['orderId'])->orderBy('pay_addtimes', 'DESC')->first();
+            if (!empty($configParams['account']['added']) && $configParams['account']['added'] > 0) {
+                $added = round($params['order_amount_total'] * $configParams['account']['added'], 2);
+//                $payDetail->pay_extend_json = json_encode(['added_rate' => $configParams['account']['added'], 'added_amount' => $added]);
+//                $payDetail->save();
+                $goods = Goods::where('goods_id',$params['join_sheet_goods_id'])->select('goods_id','goods_name')->first();
+                // 增加一条支付记录
+                PayDetail::insert([
+                    'join_pay_member_id' => $params['member_id'],
+                    'join_pay_order_id' => 'PARTNER',
+                    'join_pay_object_json' => json_encode([
+                        'recharge_order_id' => $params['orderId'],
+                        'recharge_config' => [
+                            'nbr' => round($params['order_amount_total'] * $configParams['account']['added'], 2)
+                        ],
+                        'recharge_goods_id' => $params['join_sheet_goods_id'] ?? '',
+                        'recharge_goods_name' => !empty($goods->goods_name) ? $goods->goods_name : ''
+                    ]),
+                    'pay_status' => 'SUCCESS',
+                    'pay_category' => 'RECHARGE',
+                    'pay_amount'=>$params['order_amount_total'],
+                    'pay_paytimes'=>date('Y-m-d H:i:s'),
+                    'pay_prepayid' => $params['member_id'].'-CASH',
+                    'pay_addtimes'=>time()
+                ]);
             }
+            // 入账
+            $memberAccount->member_account_income = $memberAccount->member_account_income + $params['order_amount_total'];
+            $memberAccount->member_account_surplus = $memberAccount->member_account_surplus + $params['order_amount_total'];
+            $memberAccount->member_account_added = $memberAccount->member_account_added + $added;
+            $memberAccount->save();
+
+            // 给上级提成
+            CommissionService::commission($configParams, $params['member_id'], $params['orderId'], $params['order_amount_pay'], '推荐会员合伙人');
+
+            //发放优惠券
+            $params['gettype'] = 'PARTNER';
+            CouponService::autoSendCouponByGoods($params);
+
+        } catch (BusinessException $e) {
+            _syslog('会员合伙人', '会员合伙人处理失败:' . $e->getMessage());
+            throw new BusinessException("会员合伙人处理失败");
+        } catch (\Exception $e) {
+            Log::info('PARTNER_DISPOSE_ERROR:' . $e->getMessage(), $params);
+            _syslog('会员合伙人', '会员合伙人处理失败');
+            throw new BusinessException("会员合伙人处理失败");
         }
     }
 }

+ 51 - 0
app/event/order/ReferrerEvent.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace app\event\order;
+
+use app\admin\service\coupon\CouponService;
+use app\admin\service\order\CommissionService;
+use app\model\Goods;
+use app\model\Member;
+use app\model\MemberAccount;
+use app\model\PayDetail;
+use app\model\SysConfig;
+use support\Db;
+use support\exception\BusinessException;
+use support\Log;
+
+class ReferrerEvent
+{
+    public function grant($params)
+    {
+        try {
+            Log::info("进入推荐官事件", $params);
+            $config = SysConfig::where('config_key', 'app-identity-referrer-config')->first();
+            $configParams = json_decode($config->config_value_json, true);
+            // 更新身份{"duedate": "2025-09-26", "datetime": "2024-09-27 14:40:51", "order_id": "OD2409271440L951"}
+            $memberUpdateData = [
+                'member_referrer_json' => json_encode([
+                    'datetime' => date('Y-m-d H:i:s'),
+                    'duedate' => date('Y-m-d', strtotime("+" . ($configParams['config']['valid']-1) . ' day')),
+                    'order_id' => $params['orderId'],
+                ]),
+                'member_is_referrer' => 'Y',
+            ];
+            Member::where('member_id', $params['member_id'])->update($memberUpdateData);
+
+            // 给上级提成
+            CommissionService::commission($configParams, $params['member_id'], $params['orderId'], $params['order_amount_pay'], '推荐康养推荐官');
+
+            //发放优惠券
+            $params['gettype'] = 'REFERRER';
+            CouponService::autoSendCouponByGoods($params);
+
+        } catch (BusinessException $e) {
+            _syslog('会员合伙人', '推荐官处理失败:' . $e->getMessage());
+            throw new BusinessException("推荐官处理失败");
+        } catch (\Exception $e) {
+            Log::info('PARTNER_DISPOSE_ERROR:' . $e->getMessage(), $params);
+            _syslog('会员合伙人', '推荐官处理失败');
+            throw new BusinessException("推荐官处理失败");
+        }
+    }
+}

+ 306 - 0
app/event/statistics/InOutEvent.php

@@ -0,0 +1,306 @@
+<?php
+
+namespace app\event\statistics;
+
+use app\model\DataInout;
+use app\model\MemberAccountList;
+use app\model\Order;
+use app\model\OrderSheet;
+use app\model\PayDetail;
+use app\model\SysDept;
+use support\exception\BusinessException;
+use support\Log;
+
+class InOutEvent
+{
+    /*
+     * 	{"label": "标准订单收入", "value": "标准订单收入"},
+        {"label": "餐饮订单收入", "value": "餐饮订单收入"},
+        {"label": "购买康养城VIP套餐包收入", "value": "购买康养城VIP套餐包收入"},
+        {"label": "购买会员合伙人收入", "value": "购买会员合伙人收入"},
+        {"label": "购买康养推荐官收入", "value": "购买康养推荐官收入"},
+        {"label": "购买组合包收入", "value": "购买组合包收入"},
+        {"label": "会员充值订单收入", "value": "会员充值订单收入"},
+        {"label": "挂账订单结算收入", "value": "挂账订单结算收入"},
+        {"label": "购买储值卡订单收入", "value": "购买储值卡订单收入"},
+        {"label": "佣金提现", "value": "佣金提现"},
+        {"label": "退款", "value": "退款"}
+     */
+
+    /**
+     * @Desc 收入
+     * @Author Gorden
+     * @Date 2024/10/12 15:23
+     *
+     * @param $params
+     * @return void
+     */
+    public function dataIn($params)
+    {
+        try {
+            $order = Order::where('order_id', $params['orderId'])->first();
+            $sheets = OrderSheet::with([
+                'goods' => function ($query) {
+                    $query->select('goods_id', 'goods_name', 'goods_classify');
+                }
+            ])->where('join_sheet_order_id', $params['orderId'])
+                ->get()
+                ->toArray();
+            $payDetails = PayDetail::where('join_pay_order_id', $order->order_groupby)
+                ->whereJsonContains('join_pay_object_json->order_id', $params['orderId'])
+                ->where('pay_status', 'SUCCESS')
+                ->get()
+                ->toArray();
+
+            $data = [
+                'dept_id' => 0,
+                'member_id' => $order->join_order_member_id,
+                'inout_classify' => 'IN',
+                'inout_category' => $params['inout_category'] ?? '标准订单收入',
+
+            ];
+            // 部门id
+            if (!empty($order->order_config_json)) {
+                $orderConfigJson = json_decode($order->order_config_json, true);
+                if (!empty($orderConfigJson['dept'])) {
+                    $data['dept_id'] = $orderConfigJson['dept'];
+                } elseif (!empty($orderConfigJson['premises'])) {
+                    $data['dept_id'] = SysDept::where('dept_name', $orderConfigJson['premises'])->value('dept_id');
+                }
+            }
+            $inoutObjectJson = [];
+            // 产品
+            foreach ($sheets as $sheet) {
+                $inoutObjectJson['goods'][] = [
+                    'goods_id' => $sheet['goods']['goods_id'] ?? '',
+                    'goods_name' => $sheet['goods']['goods_name'] ?? ''
+                ];
+                $data['inout_name'][] = $sheet['goods']['goods_name'] ?? '';
+                if (!isset($inoutObjectJson['order'])) {
+                    $inoutObjectJson['order'][$params['orderId']] = [
+                        'pay' => $order->order_amount_pay,
+                        'classify' => $order->order_classify,
+                        'discount' => $order->order_discount_json ?? json_decode($order->order_discount_json)
+                    ];
+                }
+                $inoutObjectJson['order'][$params['orderId']]['goods'][] = [
+                    'goods_id' => $sheet['goods']['goods_id'] ?? '',
+                    'goods_name' => $sheet['goods']['goods_name'] ?? '',
+                    'order_sheet_num' => $sheet['order_sheet_amount'],
+                    'order_sheet_pay' => $sheet['order_sheet_pay']
+                ];
+                $inoutObjectJson['classify'][] = $sheet['goods']['goods_classify'] ?? '';
+            }
+            $inoutObjectJson['ordergroup'] = $order->order_groupby;
+            $data['inout_object_json'] = json_encode($inoutObjectJson);
+            $data['inout_name'] = implode(',', $data['inout_name']);
+
+            $payJson = [
+                'trade' => '',
+                'if_union' => count($payDetails) > 1 ? 'Y' : 'N',
+                'union_order_id' => count($payDetails) > 1 ? $params['orderId'] : ''
+            ];
+            foreach ($payDetails as $payDetail) {
+                if (in_array($payDetail['pay_prepayid'], ['WXPAY', 'ALIPAY', 'OFFLINE_WXPAY', 'OFFLINE_ALIPAY', 'MONEY'])) {
+                    $data['inout_attr'] = 'MONEY';
+                } else {
+                    $data['inout_attr'] = 'ACCOUNT';
+                }
+                $data['inout_amount'] = $payDetail['pay_amount'];
+                $prepayId = explode('-', $payDetail['pay_prepayid']);
+                if (count($prepayId) == 1) {
+                    $data['pay_type'] = $payDetail['pay_prepayid'];
+                } elseif (count($prepayId) > 1) {
+                    $data['pay_type'] = $prepayId[1];
+                }
+                if (!empty($payDetail['pay_json_response'])) {
+                    $payJsonResponse = json_decode($payDetail['pay_json_response'], true);
+                    if (isset($payJsonResponse['transaction_id'])) {
+                        $payJson['trade'] = $payJsonResponse['transaction_id'];
+                    } elseif (isset($payJsonResponse['trade_no'])) {
+                        $payJson['trade'] = $payJsonResponse['trade_no'];
+                    }
+                }
+                $data['pay_json'] = json_encode($payJson);
+
+                $this->dataSave($data);
+            }
+
+        } catch (\Exception $e) {
+            _syslog("统计", '收支明细统计失败', ['msg' => $e->getMessage()]);
+            Log::error("收支明细统计失败", ['msg' => $e->getMessage()]);
+        }
+    }
+
+    public function dataOut($params)
+    {
+        try {
+            if ($params['type'] == 'refund') {
+                $data = $this->orderRefund($params);
+            } elseif ($params['type'] == 'withdraw') {
+                $data = $this->commissionWithdraw($params);
+            } else {
+                throw new BusinessException("不支持的支出类型");
+            }
+
+            $this->dataSave($data);
+        } catch (BusinessException $e) {
+            Log::error("统计支出失败:" . $e->getMessage(), $params);
+        } catch (\Exception $e) {
+            Log::error("统计支出失败:" . $e->getMessage(), $params);
+        }
+    }
+
+    public function orderRefund($params)
+    {
+        try {
+            $order = Order::where('order_id', $params['orderId'])->first();
+            $sheets = OrderSheet::with([
+                'goods' => function ($query) {
+                    $query->select('goods_id', 'goods_name', 'goods_classify');
+                }
+            ])->where('join_sheet_order_id', $params['orderId'])
+                ->get()
+                ->toArray();
+            $payDetail = PayDetail::where('join_pay_order_id', $order->order_groupby)
+                ->whereJsonContains('join_pay_object_json->order_id', $params['orderId'])
+                ->where('pay_category', 'REFUND')
+                ->where('pay_status', 'SUCCESS')
+                ->first();
+            if (!$payDetail) {
+                throw new BusinessException("支付数据异常");
+            }
+            $payDetail = $payDetail->toArray();
+
+            $data = [
+                'dept_id' => 0,
+                'member_id' => $order->join_order_member_id,
+                'inout_classify' => 'OUT',
+                'inout_category' => '退款',
+
+            ];
+            // 部门id
+            if (!empty($order->order_config_json)) {
+                $orderConfigJson = json_decode($order->order_config_json, true);
+                if (!empty($orderConfigJson['dept'])) {
+                    $data['dept_id'] = $orderConfigJson['dept'];
+                } elseif (!empty($orderConfigJson['premises'])) {
+                    $data['dept_id'] = SysDept::where('dept_name', $orderConfigJson['premises'])->value('dept_id');
+                }
+            }
+            $inoutObjectJson = [];
+            // 产品
+            foreach ($sheets as $sheet) {
+                $inoutObjectJson['goods'][] = [
+                    'goods_id' => $sheet['goods']['goods_id'] ?? '',
+                    'goods_name' => $sheet['goods']['goods_name'] ?? ''
+                ];
+                $data['inout_name'][] = $sheet['goods']['goods_name'] ?? '';
+                if (!isset($inoutObjectJson['order'])) {
+                    $inoutObjectJson['order'][$params['orderId']] = [
+                        'pay' => $payDetail['pay_amount'],
+                        'classify' => $order->order_classify,
+                        'discount' => $order->order_discount_json ?? json_decode($order->order_discount_json)
+                    ];
+                }
+                $inoutObjectJson['order'][$params['orderId']]['goods'][] = [
+                    'goods_id' => $sheet['goods']['goods_id'] ?? '',
+                    'goods_name' => $sheet['goods']['goods_name'] ?? '',
+                    'order_sheet_num' => $sheet['order_sheet_amount'],
+                    'order_sheet_pay' => $sheet['order_sheet_pay']
+                ];
+                $inoutObjectJson['classify'][] = $sheet['goods']['goods_classify'] ?? '';
+            }
+            $inoutObjectJson['ordergroup'] = $order->order_groupby;
+            $data['inout_object_json'] = json_encode($inoutObjectJson);
+            $data['inout_name'] = implode(',', $data['inout_name']);
+
+            $payJson = [
+                'trade' => '',
+                'if_union' => 'N',
+                'union_order_id' => ''
+            ];
+            if (in_array($payDetail['pay_prepayid'], ['WXPAY', 'ALIPAY', 'OFFLINE_WXPAY', 'OFFLINE_ALIPAY', 'MONEY'])) {
+                $data['inout_attr'] = 'MONEY';
+            } else {
+                $data['inout_attr'] = 'ACCOUNT';
+            }
+            $data['inout_amount'] = $payDetail['pay_amount'];
+            $prepayId = explode('-', $payDetail['pay_prepayid']);
+            if (count($prepayId) == 1) {
+                $data['pay_type'] = $payDetail['pay_prepayid'];
+            } elseif (count($prepayId) > 1) {
+                $data['pay_type'] = $prepayId[1];
+            }
+            if (!empty($payDetail['pay_json_response'])) {
+                $payJsonResponse = json_decode($payDetail['pay_json_response'], true);
+                if (isset($payJsonResponse['transaction_id'])) {
+                    $payJson['trade'] = $payJsonResponse['transaction_id'];
+                } elseif (isset($payJsonResponse['trade_no'])) {
+                    $payJson['trade'] = $payJsonResponse['trade_no'];
+                }
+            }
+            $data['pay_json'] = json_encode($payJson);
+
+            return $data;
+        } catch (BusinessException $e) {
+            throw new BusinessException($e->getMessage());
+        } catch (\Exception $e) {
+            throw new BusinessException("退款支出处理失败");
+        }
+    }
+
+    public function commissionWithdraw($params)
+    {
+        try {
+            $memberAccountList = MemberAccountList::where('member_account_list_id', $params['account_list_id'])->first();
+            $data = [
+                'dept_id' => 0,
+                'member_id' => $memberAccountList->join_member_account_list_member_id,
+                'inout_object_json' => $memberAccountList->member_account_list_json,
+                'inout_attr' => 'MONEY',
+                'inout_classify' => 'OUT',
+                'inout_category' => '提现',
+                'inout_name' => '提现',
+                'inout_amount' => $memberAccountList->member_account_list_amount,
+                'pay_type' => 'UNIONPAY',
+                'pay_json' => json_encode([
+                    'trade' => '',
+                    'if_union' => 'N',
+                    'union_order_id' => ''
+                ]),
+            ];
+
+            return $data;
+        } catch (\Exception $e) {
+            throw new BusinessException("提现支出处理失败");
+        }
+    }
+
+    public function dataSave($data)
+    {
+        DataInout::insert([
+            'join_data_inout_dept_id' => $data['dept_id'],
+            'join_data_inout_member_id' => $data['member_id'],
+            'join_data_inout_object_json' => $data['inout_object_json'],
+            'data_inout_is_valid' => 'Y',
+            'data_inout_valid_date' => date('Y-m-d'),
+            'data_inout_valid_time' => date('Y-m-d H:i:s'),
+            'data_inout_status' => 'VALID',
+            'data_inout_attr' => $data['inout_attr'],
+            'data_inout_classify' => $data['inout_classify'],
+            'data_inout_category' => $data['inout_category'],
+            'data_inout_name' => $data['inout_name'],
+            'data_inout_amount' => $data['inout_amount'],
+            'data_inout_count' => 0,
+            'data_inout_json' => '[]',
+            'data_inout_pay_type' => $data['pay_type'] ?? '',
+            'data_inout_pay_json' => $data['pay_json'],
+            'data_inout_process_json' => '[]',
+            'data_inout_occurtime' => date('Y-m-d H:i:s'),
+            'data_inout_remark' => '',
+            'data_inout_extend_json' => '[]',
+            'data_inout_addtimes' => time()
+        ]);
+    }
+}

+ 33 - 19
app/functions.php

@@ -28,25 +28,34 @@ if (!function_exists('http_post')) {
 if (!function_exists('http_post_json')) {
     function http_post_json($url, $params)
     {
-        $ch = curl_init();
-        $this_header = array('Content-Type: application/json;charset=UTF-8');
-        curl_setopt($ch, CURLOPT_HTTPHEADER, $this_header);
-        curl_setopt($ch, CURLOPT_URL, $url);
-        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
-        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
+        try {
+            $ch = curl_init();
+            $this_header = array('Content-Type: application/json;charset=UTF-8');
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $this_header);
+            curl_setopt($ch, CURLOPT_URL, $url);
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
+            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
 
-        curl_setopt($ch, CURLOPT_POST, 1);
-        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
-        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//如果不加验证,就设false,商户自行处理
-        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
+            curl_setopt($ch, CURLOPT_POST, 1);
+            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
+            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//如果不加验证,就设false,商户自行处理
+            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
 
-        $output = curl_exec($ch);
-        curl_close($ch);
-        // 写日志
-        \support\Log::info('模拟请求:',json_decode($output,true));
+            $output = curl_exec($ch);
+            curl_close($ch);
+            // 写日志
+            if (is_json($output)) {
+                \support\Log::info('模拟请求:', json_decode($output, true));
+            } else {
+                \support\Log::info('模拟请求,返回异常:', ['msg' => (string)$output]);
+            }
 
-        return $output;
+            return $output;
+
+        } catch (\Exception $e) {
+            \support\Log::info('模拟请求失败:', ['msg' => $e->getMessage()]);
+        }
     }
 }
 
@@ -68,7 +77,7 @@ if (!function_exists('month_12')) {
  * @Author Gorden
  * @Date 2024/3/29 17:07
  *
- * adminId = 1001 充值
+ *  adminId = 1001 计划任务
  *
  *
  * @param $name
@@ -79,14 +88,19 @@ if (!function_exists('month_12')) {
 if (!function_exists('_syslog')) {
     function _syslog($name, $operation, $operationData = false, $requestParams = false, $adminId = false)
     {
-        $logAdminId = $adminId ? $adminId : \Tinywan\Jwt\JwtToken::getCurrentId();
+        try {
+            $adminId = $adminId ? $adminId : \Tinywan\Jwt\JwtToken::getCurrentId();
+        } catch (\Exception $e) {
+            $adminId = 1001;
+        }
+        $logAdminId = $adminId;
         $model = new \app\model\SysLog();
         $model->log_admin_id = $logAdminId;
         $model->log_name = $name;
         $model->log_route = \request() && \request()->uri() ? \request()->uri() : (\request() && \request()->route && \request()->route->getPath() ? \request()->route->getPath() : '');
         $model->log_operation = $operation;
         $model->log_ip = \request() && \request()->getRealIp() ? \request()->getRealIp() : '0.0.0.0';
-        $model->log_request_params = $requestParams ? json_encode($requestParams) : json_encode(\request()->all());
+        $model->log_request_params = $requestParams ? json_encode($requestParams) : ($adminId == 1001 ? '[]' : json_encode(\request()->all()));
         $model->log_operation_data = $operationData ? json_encode($operationData) : null;
         $model->save();
     }

+ 11 - 0
app/model/ClientPoints.php

@@ -33,6 +33,17 @@ class ClientPoints extends Model
         return $this->hasOne(Member::class, 'member_id', 'join_client_points_member_id');
     }
 
+
+    public function memberInfo()
+    {
+        return $this->hasOne(MemberInfo::class, 'join_info_member_id', 'join_client_points_member_id');
+    }
+
+    public function memberCert()
+    {
+        return $this->hasOne(MemberCert::class,'join_cert_member_id','join_client_points_member_id');
+    }
+
     /**
      * @Desc 关联商品
      * @Author Gorden

+ 21 - 0
app/model/DataInout.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace app\model;
+
+use support\Model;
+
+class DataInout extends Model
+{
+    protected $table = 'data_inout';
+
+    protected $primaryKey = 'data_inout_id';
+
+    protected $dateFormat = 'U';
+
+    const CREATED_AT = 'data_inout_addtimes';
+
+    public function serializeDate(\DateTimeInterface $date)
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+}

+ 4 - 0
app/model/Goods.php

@@ -77,4 +77,8 @@ class Goods extends Model
     public function user(){
         return $this->hasOne(SysUser::class,'user_id','creator_user_id');
     }
+
+    public function updateUser(){
+        return $this->hasOne(SysUser::class,'user_id','updator_user_id');
+    }
 }

+ 15 - 0
app/model/MemberAccountList.php

@@ -16,4 +16,19 @@ class MemberAccountList extends Model
     const CREATED_AT = 'member_account_list_addtimes';
 
     const UPDATED_AT = null;
+
+    public function member()
+    {
+        return $this->hasOne(Member::class, 'member_id', 'join_member_account_list_member_id');
+    }
+
+    public function memberInfo()
+    {
+        return $this->hasOne(MemberInfo::class, 'join_info_member_id', 'join_member_account_list_member_id');
+    }
+
+    public function memberCert()
+    {
+        return $this->hasOne(MemberCert::class,'join_cert_member_id','join_member_account_list_member_id');
+    }
 }

+ 12 - 0
config/event.php

@@ -16,6 +16,12 @@ return [
     'order.partner.grant' => [
         [\app\event\order\PartnerEvent::class, 'grant']
     ],
+    'order.referrer.grant' => [
+        [\app\event\order\ReferrerEvent::class, 'grant']
+    ],
+    'order.new_custom.grant' => [
+        [\app\event\order\NewCustomerEvent::class, 'grant']
+    ],
     'order_pay.member_level.up' => [
         [\app\event\MemberLevelEvent::class, 'payOrderLevelUp']
     ],
@@ -27,5 +33,11 @@ return [
     ],
     'commission.order' => [
         [\app\event\order\CommissionEvent::class, 'order']
+    ],
+    'statistics.inout.in' => [
+        [\app\event\statistics\InOutEvent::class, 'dataIn']
+    ],
+    'statistics.inout.out' => [
+        [\app\event\statistics\InOutEvent::class, 'dataOut']
     ]
 ];

+ 2 - 2
config/log.php

@@ -19,7 +19,7 @@ return [
                 'class' => Monolog\Handler\RotatingFileHandler::class,
                 'constructor' => [
                     runtime_path() . '/logs/webman.log',
-                    7, //$maxFiles
+                    100, //$maxFiles
                     Monolog\Logger::DEBUG,
                 ],
                 'formatter' => [
@@ -35,7 +35,7 @@ return [
                 'class' => Monolog\Handler\RotatingFileHandler::class,
                 'constructor' => [
                     runtime_path() . '/logs/pay.log',
-                    7, //$maxFiles
+                    100, //$maxFiles
                     Monolog\Logger::DEBUG,
                 ],
                 'formatter' => [

+ 9 - 4
process/Task.php

@@ -6,6 +6,7 @@ use app\admin\controller\notify\RechargeController;
 use app\admin\service\client\MessageService;
 use app\admin\service\coupon\CouponService;
 use app\admin\service\goods\GoodsService;
+use app\admin\service\member\MemberService;
 use app\admin\service\order\OrderService;
 use app\admin\service\order\PayDetailService;
 use Workerman\Crontab\Crontab;
@@ -32,8 +33,14 @@ class Task
 
             // 优惠券自动过期
             CouponService::checkCouponExpired();
-            // 发放周期券
-//            CouponService::sendPeriodCoupon();
+        });
+
+        // 每天的0点1分执行,注意这里省略了秒位
+        new Crontab('1 0 * * *', function () {
+            // 会员合伙人自动过期
+            MemberService::partnerExpired();
+            // 康养推荐官自动过期
+            MemberService::referrerExpired();
         });
 
         // 每天的2点执行,注意这里省略了秒位
@@ -42,8 +49,6 @@ class Task
             OrderService::AutomaticReceipt();
             // 发货后15天自动完成
             OrderService::AutomaticComplete();
-            // 发放周期券
-            CouponService::sendPeriodCoupon();
         });
     }
 }

+ 77 - 1
route/admin.php

@@ -106,6 +106,35 @@ Route::group('/admin', function () {
         })->middleware([
             \app\middleware\AdminAuthCheck::class
         ]);
+        /*康旅商品管理 */
+        Route::group('/travel', function () {
+            Route::get('/list', [\app\admin\controller\goods\TravelGoodsController::class, 'select']);
+            Route::post('/add', [\app\admin\controller\goods\TravelGoodsController::class, 'insert']);
+            Route::post('/update', [\app\admin\controller\goods\TravelGoodsController::class, 'update']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
+        /* 新客专享 */
+        Route::group('/newCustomer', function () {
+            Route::get('/info', [\app\admin\controller\goods\NewCustomerController::class, 'info']);
+            Route::post('/update', [\app\admin\controller\goods\NewCustomerController::class, 'update']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
+        /* 合伙人 */
+        Route::group('/partner', function () {
+            Route::get('/info', [\app\admin\controller\goods\PartnerController::class, 'info']);
+            Route::post('/update', [\app\admin\controller\goods\PartnerController::class, 'update']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
+        /* 推荐官 */
+        Route::group('/referrer', function () {
+            Route::get('/info', [\app\admin\controller\goods\ReferrerController::class, 'info']);
+            Route::post('/update', [\app\admin\controller\goods\ReferrerController::class, 'update']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
     });
     /* 财务管理 */
     Route::group('/finance', function () {
@@ -126,6 +155,22 @@ Route::group('/admin', function () {
         Route::group('/incomeAndExpend', function () {
             Route::get('/list', [\app\admin\controller\finance\IncomeAndExpendController::class, 'list']);
         });
+
+        /* 提成管理 */
+        Route::group('/commissionList', function () {
+            Route::get('/list', [\app\admin\controller\finance\CommissionListController::class, 'select']);
+            Route::get('/info', [\app\admin\controller\finance\CommissionListController::class, 'info']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
+        /* 提现管理 */
+        Route::group('/withdrawalList', function () {
+            Route::get('/list', [\app\admin\controller\finance\WithdrawalListController::class, 'select']);
+            Route::get('/info', [\app\admin\controller\finance\WithdrawalListController::class, 'info']);
+            Route::post('/changeStatus', [\app\admin\controller\finance\WithdrawalListController::class,'changeStatus']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
     });
     /* 系统管理中心 */
     Route::group('/sys', function () {
@@ -260,6 +305,8 @@ Route::group('/admin', function () {
             Route::get('/partnerParams',[\app\admin\controller\sys_manage\ConfigController::class,'partnerParamsInfo']);
             Route::post('/setPartnerParams',[\app\admin\controller\sys_manage\ConfigController::class,'setPartnerParams']);
 
+            Route::get('/referrerParams',[\app\admin\controller\sys_manage\ConfigController::class,'referrerParamsInfo']);
+            Route::post('/setReferrerParams',[\app\admin\controller\sys_manage\ConfigController::class,'setReferrerParams']);
         });
         /* 运费模板管理 */
         Route::group('/postageTemplate', function () {
@@ -671,6 +718,7 @@ Route::group('/admin', function () {
         ]);
         Route::group('/writeOff', function () {
             Route::get('/list', [\app\admin\controller\member\WriteOffController::class, 'list']);
+            Route::get('/info', [\app\admin\controller\member\WriteOffController::class, 'info']);
             Route::post('/remark', [\app\admin\controller\member\WriteOffController::class, 'remark']);
         })->middleware([
             \app\middleware\AdminAuthCheck::class
@@ -686,6 +734,7 @@ Route::group('/admin', function () {
             Route::post('/clearWelfare', [\app\admin\controller\member\MemberController::class, 'clearWelfare']);
             Route::get('/vipInfo', [\app\admin\controller\member\MemberController::class, 'vipInfo']);
             Route::get('/coupon', [\app\admin\controller\member\MemberController::class, 'coupon']);
+            Route::get('/allCoupon', [\app\admin\controller\member\MemberController::class, 'allCoupon']);
             Route::get('/couponCount', [\app\admin\controller\member\MemberController::class, 'couponCount']);
             Route::get('/couponDetail', [\app\admin\controller\member\MemberController::class, 'couponDetail']);
             Route::get('/balanceInfo', [\app\admin\controller\member\MemberController::class, 'balanceInfo']);
@@ -696,6 +745,7 @@ Route::group('/admin', function () {
             Route::post('/update', [\app\admin\controller\member\MemberController::class, 'update']);
             Route::get('/exportMember', [\app\admin\controller\member\MemberController::class, 'exportMember']);
             Route::get('/fansList', [\app\admin\controller\member\MemberController::class, 'fansList']);
+            Route::get('/commissionList', [\app\admin\controller\member\MemberController::class, 'commissionList']);
         })->middleware([
             \app\middleware\AdminAuthCheck::class
         ]);
@@ -990,6 +1040,31 @@ Route::group('/admin', function () {
             Route::get('/sheet', [\app\admin\controller\order\PartnerController::class, 'sheet']);
             Route::get('/getPaidOrder', [\app\admin\controller\order\PartnerController::class, 'getPaidOrder']);
             Route::post('/uploadTicket', [\app\admin\controller\order\PartnerController::class, 'uploadTicket']);
+            Route::get('/getOrderPayStatus', [\app\admin\controller\order\PartnerController::class, 'getOrderPayStatus']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
+        // 推荐人订单
+        Route::group('/referrer', function () {
+            Route::get('/list', [\app\admin\controller\order\ReferrerController::class, 'select']);
+            Route::post('/add', [\app\admin\controller\order\ReferrerController::class, 'insert']);
+            Route::post('/pay', [\app\admin\controller\order\ReferrerController::class, 'pay']);
+            Route::get('/sheet', [\app\admin\controller\order\ReferrerController::class, 'sheet']);
+            Route::get('/getPaidOrder', [\app\admin\controller\order\ReferrerController::class, 'getPaidOrder']);
+            Route::post('/uploadTicket', [\app\admin\controller\order\ReferrerController::class, 'uploadTicket']);
+            Route::get('/getOrderPayStatus', [\app\admin\controller\order\ReferrerController::class, 'getOrderPayStatus']);
+        })->middleware([
+            \app\middleware\AdminAuthCheck::class
+        ]);
+        // 新客专享订单
+        Route::group('/newCustomer', function () {
+            Route::get('/list', [\app\admin\controller\order\NewCustomerController::class, 'select']);
+            Route::post('/add', [\app\admin\controller\order\NewCustomerController::class, 'insert']);
+            Route::post('/pay', [\app\admin\controller\order\NewCustomerController::class, 'pay']);
+            Route::get('/sheet', [\app\admin\controller\order\NewCustomerController::class, 'sheet']);
+            Route::get('/getPaidOrder', [\app\admin\controller\order\NewCustomerController::class, 'getPaidOrder']);
+            Route::post('/uploadTicket', [\app\admin\controller\order\NewCustomerController::class, 'uploadTicket']);
+            Route::get('/getOrderPayStatus', [\app\admin\controller\order\NewCustomerController::class, 'getOrderPayStatus']);
         })->middleware([
             \app\middleware\AdminAuthCheck::class
         ]);
@@ -1000,6 +1075,7 @@ Route::group('/admin', function () {
             Route::post('/addConstitute', [\app\admin\controller\order\ServicesController::class, 'insertConstitute']);
             Route::post('/pay', [\app\admin\controller\order\ServicesController::class, 'pay']);
             Route::post('/payConstitute', [\app\admin\controller\order\ServicesController::class, 'payConstitute']);
+            Route::get('/getOrderPayStatus', [\app\admin\controller\order\WholeController::class, 'getOrderPayStatus']);
         })->middleware([
             \app\middleware\AdminAuthCheck::class
         ]);
@@ -1097,7 +1173,7 @@ Route::group('/admin', function () {
             Route::delete('/delete', [\app\admin\controller\client\EvaluateController::class, 'delete']);
         })->middleware([
             \app\middleware\AdminAuthCheck::class
-        ]);;
+        ]);
         /* 消息管理 */
         Route::group('/message', function () {
             Route::group('', function () {