Browse Source

fix:大小写

super-yimizi 2 months ago
parent
commit
d0fb3160ab

+ 43 - 0
application/common/Service/Activity.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace app\common\Service;
+
+use app\common\Enum\StatusEnum;
+use app\common\Enum\ActivityEnum;
+use app\common\model\marketing\Activity as ActivityModel;
+use app\common\model\marketing\ActivitySkuPrize as ActivitySkuPrizeModel;
+class Activity
+{
+    // 获取当前时间 有活动的商品
+    public function getActivityGoods()
+    {
+        $now = time();
+        $activityIds = ActivityModel::where('start_time', '<=', $now)
+        ->where('end_time', '>=', $now)
+        ->where('status',StatusEnum::ENABLED)
+        ->where('activity_status',ActivityEnum::ACTIVITY_STATUS_ONGOING)
+        ->select();
+        //获取对应的商品 
+        
+
+        
+
+        // 查询出活动中的商品数据
+        $activityGoods = ActivitySkuPrizeModel::whereIn('activity_id',$activityIds)
+        ->where('status',StatusEnum::ENABLED)
+
+        ->select();
+
+        
+        return $activityGoods;
+    }
+}
+
+
+
+
+
+
+
+
+

+ 111 - 0
application/common/Service/CartService.php

@@ -0,0 +1,111 @@
+<?php
+
+namespace app\common\Service;
+
+use app\common\model\Carts;
+use think\Exception;
+use app\common\Enum\GoodsEnum;
+class CartService
+{
+        /**
+     * 将购物车数据转换为标准的商品列表格式
+     * @param array $cart_ids 购物车ID列表
+     * @param int $user_id 用户ID
+     * @return array
+     * @throws Exception
+     */
+    public static function convertCartToGoodsList($cart_ids, $user_id)
+    {
+        // 查询购物车数据
+        $cartItems = Carts::where('id', 'in', $cart_ids)
+            ->where('user_id', $user_id)
+            ->select();
+            
+        if (empty($cartItems)) {
+            throw new Exception("购物车数据不存在");
+        }
+        
+        $goods_list = [];
+        foreach ($cartItems as $cart) {
+            $goods_list[] = [
+                'goods_id' => $cart->goods_id,
+                'goods_sku_id' => $cart->goods_sku_id,
+                'nums' => $cart->nums
+            ];
+        }
+        
+        return $goods_list;
+    }
+
+       /**
+     * @ DateTime 2021-05-31
+     * @ 获取购物车商品列表
+     * @param string  $ids
+     * @param integer $user_id
+     * @param integer $sceneval
+     * @return array
+     */
+    public static function getGoodsList($ids, $user_id)
+    {
+        return Carts::field("c.*,GROUP_CONCAT(sp.name,':',sv.value order by sp.id asc) sku_attr")
+            ->with([
+                'Goods' => function ($query) {
+                    $query->where('status', GoodsEnum::STATUS_ON_SALE);
+                },
+                'Sku'
+            ])
+            ->alias('c')
+            ->join('shop_goods_sku sku', 'c.goods_sku_id=sku.id', 'LEFT')
+            ->join('shop_goods_sku_spec p', "FIND_IN_SET(p.id,sku.spec_value_ids)", 'LEFT')
+            ->join('shop_spec sp', 'sp.id=p.spec_id', 'LEFT')
+            ->join('shop_spec_value sv', 'sv.id=p.spec_value_id', 'LEFT')
+            ->where(function ($query) use ($ids) {
+                if ($ids) {
+                    $query->where('c.id', 'in', $ids);
+                }
+            })
+            ->where('c.user_id', $user_id)
+            ->order('c.createtime desc')
+            ->group('c.id')
+            ->select();
+    }
+
+    /**
+     * 添加商品到购物车
+     *
+     * @param string $goods_id     商品ID
+     * @param string $goods_sku_id 商品SKUID
+     * @param int    $nums         数量
+     * @param int    $user_id      会员ID
+     * @return mixed
+     */
+    public static function push($goods_id, $goods_sku_id, $nums = 1, $user_id = 0)
+    {
+        $row = Carts::where('goods_id', $goods_id)
+            ->where('goods_sku_id', $goods_sku_id)
+            ->where('user_id', $user_id)
+            ->find();
+        //已存在,数量加
+        if ($row) {
+            $row->setInc('nums', $nums);
+        } else {
+            $row = new Carts();
+            $row->save([
+                'goods_id'     => $goods_id,
+                'goods_sku_id' => $goods_sku_id,
+                'user_id'      => $user_id,
+                'nums'         => $nums
+            ]);
+        }
+        return $row->id;
+    }
+
+    /**
+     * 清空购物车
+     */
+    public static function clear($cart_ids)
+    {
+        Carts::where('id', 'IN', $cart_ids)->delete();
+    }
+
+}

+ 521 - 0
application/common/Service/OrderService.php

@@ -0,0 +1,521 @@
+<?php
+
+namespace app\common\Service;
+
+use app\common\Enum\GoodsEnum;
+use app\common\model\Order;
+use app\common\model\OrderGoods;
+use app\common\model\OrderAction;
+use app\common\model\Address;
+use app\common\model\UserCoupon;
+use app\common\model\Goods;
+use app\common\model\Sku;
+use app\common\model\Freight;
+use app\common\model\Coupon;
+use app\common\model\Carts;
+use think\Db;
+use think\Exception;
+use app\common\model\OrderAddress;
+/**
+ * 订单服务类
+ * 封装订单创建相关逻辑
+ */
+class OrderService
+{
+
+        /**
+     * 根据商品列表计算订单明细
+     * @param array $orderInfo 订单基础信息
+     * @param array $goods_list 商品列表
+     * @param int $user_id 用户ID
+     * @param int $area_id 地区ID
+     * @param int $user_coupon_id 优惠券ID
+     * @return array
+     * @throws Exception
+     */
+    protected static function computeGoods(&$orderInfo, $goodsList, $userId, $areaId, $userCouponId = 0)
+    {
+        $config = get_addon_config('shop');
+        $orderInfo['amount'] = 0;
+        $orderInfo['goods_price'] = 0;
+        $orderInfo['goods_num'] = 0;
+        $orderInfo['express_fee'] = 0;
+        $orderInfo['discount_fee'] = 0;
+        $orderInfo['coupon_discount_fee'] = 0;
+        $orderInfo['order_amount'] = 0;
+        $orderItem = [];
+        $shippingTemp = [];
+        $userCoupon = null;
+        $processedGoodsList = []; // 处理后的商品列表,避免与参数$goodsList冲突
+        // 校验优惠券
+        if ($userCouponId) {
+            $userCouponModel = new UserCoupon();
+            $userCoupon = $userCouponModel->checkUserOrUse($userCouponId, $userId);
+            $orderInfo['user_coupon_id'] = $userCouponId;
+        }
+
+        // 提取所有商品ID和SKU ID,进行批量查询
+        $goodsIds = array_column($goodsList, 'goods_id');
+        $skuIds = [];
+        foreach ($goodsList as $index => $item) {
+            if (isset($item['goods_sku_id']) && $item['goods_sku_id'] > 0) {
+                $skuIds[] = $item['goods_sku_id'];
+            }
+        }
+
+        // 批量查询商品信息
+        $goodsData = [];
+        if (!empty($goodsIds)) {
+            $goodsCollection = Goods::with(['brand'])
+                ->where('id', 'in', $goodsIds)
+                ->where('status', GoodsEnum::STATUS_ON_SALE)
+                ->select();
+            foreach ($goodsCollection as $goods) {
+                $goodsData[$goods->id] = $goods;
+            }
+        }
+
+        // 批量查询SKU信息
+        $skuData = [];
+        $multiSpecSkuIds = []; // 用于存储多规格商品的SKU ID
+        if (!empty($skuIds)) {
+            $skuCollection = Sku::where('id', 'in', $skuIds)->select();
+            foreach ($skuCollection as $sku) {               
+                $skuData[$sku->id] = $sku;
+                // 过滤出有规格值的SKU ID(spec_value_ids不为空)
+                if (!empty($sku->spec_value_ids)) {
+                    $multiSpecSkuIds[] = $sku->id;
+                }
+            }
+        }
+        // 批量查询规格属性字符串(只查询多规格商品的SKU)
+        $skuAttrData = [];
+        if (!empty($multiSpecSkuIds)) {
+            $skuAttrData = \app\common\Service\SkuSpec::getSkuAttrs($multiSpecSkuIds);
+        }
+
+        // 验证并构建商品数据
+        foreach ($goodsList as $item) {
+            $goods_id = $item['goods_id'];
+            $goods_sku_id = $item['goods_sku_id']; // 现在所有商品都应该有SKU ID
+            $nums = $item['nums'];
+
+            if ($nums <= 0) {
+                throw new Exception("商品数量必须大于0");
+            }
+
+            // 检查商品是否存在
+            if (!isset($goodsData[$goods_id])) {
+                throw new Exception("商品已下架");
+            }
+            $goods = $goodsData[$goods_id];
+
+            // 所有商品都必须有SKU(包括单规格商品的默认SKU)
+            if (empty($skuData) || !isset($skuData[$goods_sku_id])) {
+                throw new Exception("商品规格不存在");
+            }
+            $sku = $skuData[$goods_sku_id];
+            
+            // 验证SKU是否属于该商品
+            if ($sku->goods_id != $goods_id) {
+                throw new Exception("商品规格不匹配");
+            }
+            
+            // 获取规格属性字符串(单规格商品的sku_attr为空)
+            $sku_attr = $skuAttrData[$goods_sku_id] ?? '';
+
+            // 构建商品对象,模拟购物车数据结构
+            $goodsItem = (object)[
+                'goods_id' => $goods_id,
+                'goods_sku_id' => $goods_sku_id,
+                'nums' => $nums,
+                'goods' => $goods,
+                'sku' => $sku,
+                'sku_attr' => $sku_attr
+            ];
+
+            $processedGoodsList[] = $goodsItem;
+        }
+    
+        // 计算商品价格和运费(统一使用SKU进行计算)
+        foreach ($processedGoodsList as $item) {
+            $goodsItemData = [];
+            
+            if (empty($item->goods) || empty($item->sku)) {
+                throw new Exception("商品已下架");
+            }
+            
+            // 库存验证(统一使用SKU库存)
+            if ($item->sku->stocks < $item->nums) {
+                throw new Exception("商品库存不足,请重新修改数量");
+            }
+            
+            // 统一使用SKU数据进行计算
+            $goodsItemData['image'] = !empty($item->sku->image) ? $item->sku->image : $item->goods->image;
+            $goodsItemData['price'] = $item->sku->price;
+            // $goodsItemData['lineation_price'] = $item->sku->lineation_price;
+            $goodsItemData['sku_sn'] = $item->sku->sku_sn;
+            $amount = bcmul($item->sku->price, $item->nums, 2);
+            
+            $goodsItemData['amount'] = $amount;
+            // 订单应付金额
+            $orderInfo['amount'] = bcadd($orderInfo['amount'], $amount, 2);
+            // 商品总价
+            $orderInfo['goods_price'] = bcadd($orderInfo['goods_price'], $amount, 2);
+            // 商品数量累计
+            $orderInfo['goods_num'] += $item->nums;
+
+            $freight_id = $item->goods->express_template_id;
+            // 计算邮费【合并运费模板】
+            if (!isset($shippingTemp[$freight_id])) {
+                $shippingTemp[$freight_id] = [
+                    'nums'   => $item->nums,
+                    'weight' => $item->sku->weight,
+                    'amount' => $amount
+                ];
+            } else {
+                $shippingTemp[$freight_id] = [
+                    'nums'   => bcadd($shippingTemp[$freight_id]['nums'], $item->nums, 2),
+                    'weight' => bcadd($shippingTemp[$freight_id]['weight'], $item->sku->weight, 2),
+                    'amount' => bcadd($shippingTemp[$freight_id]['amount'], $amount, 2)
+                ];
+            }
+            
+            // 创建订单商品数据 (基于正确的表结构)
+            $orderItemData = [
+                'user_id'              => $userId,
+                'order_sn'             => '', // 将在订单创建后补充
+                'goods_sn'             => $item->goods->goods_sn ?: '', // 商品货号
+                'sku_sn'               => $item->sku->sku_sn ?: '', // SKU编号
+                'goods_type'           => $item->goods->type ?: 0, // 商品类型
+                'goods_id'             => $item->goods->id,
+                'goods_sku_id'         => $item->sku->id,
+                'goods_title'          => $item->goods->title,
+                'goods_market_price'   => $item->sku->market_price ?: 0, // 市场价
+                'goods_original_price' => $item->sku->price, // 商城售价
+                'goods_price'          => $amount, // 实付金额
+                'goods_image'          => $goodsItemData['image'],
+                'goods_weight'         => $item->sku->weight ?: 0,
+                'nums'                 => $item->nums,
+                'sale_status'          => 0, // 销售状态:0=待申请
+                'comment_status'       => 0, // 评论状态:0=未评论
+                'status'               => 1, // 状态
+                // 添加分类和品牌信息用于优惠券计算 (临时字段,不会保存到数据库)
+                // 'category_id'          => $item->goods->category_ids,
+                'brand_id'             => $item->goods->brand_id,
+            ];
+            
+            $orderItem[] = $orderItemData;
+        }
+    
+        // 按运费模板计算
+        foreach ($shippingTemp as $key => $item) {
+            $shippingfee = Freight::calculate($key, $areaId, $item['nums'], $item['weight'], $item['amount']);
+            $orderInfo['express_fee'] = bcadd($orderInfo['express_fee'], $shippingfee, 2);
+        }
+
+        // 订单金额(商品价格+运费)
+        $orderInfo['order_amount'] = bcadd($orderInfo['goods_price'], $orderInfo['express_fee'], 2);
+        // 订单应付金额(暂时等于订单金额,后续会减去优惠)
+        $orderInfo['amount'] = $orderInfo['order_amount'];
+
+        // if (!empty($userCoupon)) {
+        //     // 校验优惠券
+        //     $goods_ids = array_column($orderItem, 'goods_id');
+        //     $category_ids = array_column($orderItem, 'category_id');
+        //     $brand_ids = array_column($orderItem, 'brand_id');
+        //     $couponModel = new Coupon();
+        //     $coupon = $couponModel->getCoupon($userCoupon['coupon_id'])
+        //         ->checkCoupon()
+        //         ->checkOpen()
+        //         ->checkUseTime($userCoupon['createtime'])
+        //         ->checkConditionGoods($goods_ids, $userId, $category_ids, $brand_ids);
+
+        //     // 计算折扣金额,判断是使用不含运费,还是含运费的金额
+        //     $amount = !isset($config['shippingfeecoupon']) || $config['shippingfeecoupon'] == 0 ? $orderInfo['goods_price'] : $orderInfo['order_amount'];
+        //     list($new_money, $coupon_money) = $coupon->doBuy($amount);
+
+        //     // 判断优惠金额是否超出总价,超出则直接设定优惠金额为总价
+        //     $orderInfo['coupon_discount_fee'] = $coupon_money > $amount ? $amount : $coupon_money;
+        //     $orderInfo['discount_fee'] = $orderInfo['coupon_discount_fee'];
+        // }
+
+        // 计算最终应付金额【订单金额减去折扣】
+        $orderInfo['amount'] = max(0, bcsub($orderInfo['order_amount'], $orderInfo['discount_fee'], 2));
+        $orderInfo['pay_amount'] = $orderInfo['amount']; // 实际付款金额等于应付金额
+        $orderInfo['discount_fee'] = bcadd($orderInfo['discount_fee'], 0, 2);
+
+        return [
+            $orderItem,
+            $processedGoodsList,
+            $userCoupon
+        ];
+    }
+
+    
+    /**
+     * 统一的创建订单方法
+     * @param int $address_id 地址ID
+     * @param int $user_id 用户ID
+     * @param array $goods_list 标准化的商品列表
+     * @param int $user_coupon_id 优惠券ID
+     * @param string $memo 备注
+     * @param array $cart_ids 购物车ID数组(如果是购物车模式需要清空)
+     * @return Order
+     * @throws Exception
+     */
+    public static function createOrder($addressId, $userId, $goodsList, $userCouponId = 0, $remark = '')
+    {
+        $address = Address::get($addressId);
+        if (!$address || $address['user_id'] != $userId) {
+            throw new Exception("地址未找到");
+        }
+
+        if (empty($goodsList)) {
+            throw new Exception("商品列表不能为空");
+        }
+
+        $config = get_addon_config('shop');
+        $orderSn = date("Ymdhis") . sprintf("%08d", $userId) . mt_rand(1000, 9999);
+
+        // 订单主表信息 (基于新表结构)
+        $orderInfo = [
+            'type'                 => 1, // 1:普通订单
+            'source'               => 'H5', // 订单来源 (暂定H5,可根据实际情况调整)
+            'order_sn'             => $orderSn,
+            'user_id'              => $userId,
+            'amount'               => 0, // 订单应付金额
+            'goods_price'          => 0, // 商品总费用
+            'goods_num'            => 0, // 商品数量
+            'discount_fee'         => 0, // 优惠金额
+            'coupon_discount_fee'  => 0, // 优惠券金额
+            'promo_discount_fee'   => 0, // 营销金额
+            'order_amount'         => 0, // 订单金额
+            'pay_amount'           => 0, // 实际付款金额
+            'pay_type'             => '', // 支付方式
+            'pay_time'             => null, // 支付时间
+            'delivery_type'        => 1, // 发货方式
+            'express_name'         => '', // 快递名称
+            'express_no'           => '', // 快递单号
+            'express_fee'          => 0, // 配送费用
+            'expire_time'          => time() + $config['order_timeout'], // 过期时间
+            'order_status'         => \app\common\Enum\OrderEnum::STATUS_CREATE, // 待付款
+            'invoice_status'       => 0, // 发票开具状态
+            'remark'               => $remark, // 用户备注
+            'user_coupon_id'       => $userCouponId ?: null,
+            'ip'                   => request()->ip(), // IP地址
+            'status'               => 'normal'
+        ];
+      
+
+        // 通过商品列表计算订单明细
+        list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods($orderInfo, $goodsList, $userId, $address->area_id, $userCouponId);
+        // 创建订单
+        $order = self::createOrderWithTransaction($orderInfo, $orderItem, $calculatedGoodsList, $userCoupon, $address);
+        
+        return $order;
+    }
+
+
+    /**
+     * 在事务中创建订单
+     * @param array $orderInfo 订单信息
+     * @param array $orderItem 订单商品列表
+     * @param array $goodsList 商品列表
+     * @param object $userCoupon 优惠券
+     * @param object $address 地址信息
+     * @return Order
+     * @throws Exception
+     */
+    protected static function createOrderWithTransaction($orderInfo, $orderItem, $goodsList, $userCoupon, $address)
+    {
+        $order = null;
+        Db::startTrans();
+        try {
+            // 创建订单
+            $order = Order::create($orderInfo, true);
+            
+            // 为每个订单商品添加订单ID和订单号
+            foreach ($orderItem as &$item) {
+                $item['order_id'] = $order->id;
+                $item['order_sn'] = $order->order_sn;
+                // 移除临时字段
+                unset($item['category_id'], $item['brand_id']);
+            }
+            unset($item);
+            // 创建订单地址信息
+            $orderAddressData = [
+                'order_id'      => $order->id,
+                'user_id'       => $orderInfo['user_id'],
+                'consignee'     => $address->receiver,
+                'mobile'        => $address->mobile,
+                'province_name' => $address->province->name ?? '',
+                'city_name'     => $address->city->name ?? '',
+                'district_name' => $address->area->name ?? '',
+                'address'       => $address->address,
+                'province_id'   => $address->province_id,
+                'city_id'       => $address->city_id,
+                'district_id'   => $address->area_id,
+            ];
+            OrderAddress::create($orderAddressData);
+            
+            // 减库存
+            foreach ($goodsList as $index => $item) {
+                if ($item->sku) {
+                    $item->sku->setDec('stocks', $item->nums);
+                }
+                $item->goods->setDec("stocks", $item->nums);
+            }
+            
+            // 计算单个商品折扣后的价格 (基于新字段名)
+            $saleamount = bcsub($order['amount'], $order['express_fee'], 2);
+            $saleratio = $order['goods_price'] > 0 ? bcdiv($saleamount, $order['goods_price'], 10) : 1;
+            $saleremains = $saleamount;
+            
+            foreach ($orderItem as $index => &$item) {
+                if (!isset($orderItem[$index + 1])) {
+                    $saleprice = $saleremains;
+                } else {
+                    $saleprice = $order['discount_fee'] == 0 ? bcmul($item['goods_original_price'], $item['nums'], 2) : bcmul(bcmul($item['goods_original_price'], $item['nums'], 2), $saleratio, 2);
+                }
+                $saleremains = bcsub($saleremains, $saleprice, 2);
+                $item['goods_price'] = $saleprice;
+            }
+            unset($item);
+            
+            // 批量创建订单商品数据
+            if (!empty($orderItem)) {
+                (new OrderGoods())->saveAll($orderItem);
+            }
+            
+            // 修改地址使用次数
+            if ($address) {
+                $address->setInc('usednums');
+            }
+            
+            // 优惠券已使用
+            if (!empty($userCoupon)) {
+                $userCoupon->save(['is_used' => 2]);
+            }
+            
+            // 提交事务
+            Db::commit();
+        } catch (Exception $e) {
+            Db::rollback();
+            throw new Exception($e->getMessage());
+        }
+        
+        // 记录操作
+        OrderAction::push($orderInfo['order_sn'], '系统', '订单创建成功');
+        
+        // 订单应付金额为0时直接结算
+        if ($order['amount'] == 0) {
+            Order::settle($order->order_sn, 0);
+            $order = Order::get($order->id);
+        }
+        
+        return $order;
+    }
+
+    /**
+     * 验证商品规格参数
+     * @param array $goods_list 商品列表
+     * @throws Exception
+     */
+    public static function validateGoodsList($goods_list)
+    {
+        if (empty($goods_list) || !is_array($goods_list)) {
+            throw new Exception("商品列表不能为空");
+        }
+
+        foreach ($goods_list as $item) {
+            if (!isset($item['goods_id']) || !is_numeric($item['goods_id']) || $item['goods_id'] <= 0) {
+                throw new Exception("商品ID无效");
+            }
+            
+            if (!isset($item['nums']) || !is_numeric($item['nums']) || $item['nums'] <= 0) {
+                throw new Exception("商品数量必须大于0");
+            }
+            
+            if (isset($item['goods_sku_id']) && !is_numeric($item['goods_sku_id'])) {
+                throw new Exception("商品规格ID无效");
+            }
+        }
+    }
+
+    /**
+     * 统一的订单计算方法(用于预览订单)
+     * @param array $goods_list 标准化的商品列表
+     * @param int $user_id 用户ID
+     * @param int $area_id 地区ID
+     * @param int $user_coupon_id 优惠券ID
+     * @return array
+     * @throws Exception
+     */
+    public static function calculateOrder($goodsList, $userId, $areaId = 0, $userCouponId = 0)
+    {
+        if (empty($goodsList)) {
+            throw new Exception("商品列表不能为空");
+        }
+      
+        // 验证商品列表格式
+        self::validateGoodsList($goodsList);
+
+        // 订单基础信息(用于计算,不包含订单号)
+        $orderInfo = [
+            'amount'               => 0, // 应付金额
+            'goods_price'          => 0, // 商品金额 (不含运费)
+            'goods_num'            => 0, // 商品数量
+            'discount_fee'         => 0, // 总优惠金额
+            'coupon_discount_fee'  => 0, // 优惠券金额
+            'promo_discount_fee'   => 0, // 营销金额
+            'order_amount'         => 0, // 总金额 (含运费)
+            'express_fee'          => 0, // 运费
+        ];       
+
+        // 计算商品明细
+        list($orderItem, $calculatedGoodsList, $userCoupon) = self::computeGoods($orderInfo, $goodsList, $userId, $areaId, $userCouponId);
+
+        return [
+            'orderItem' => $orderItem,
+            'goodsList' => $calculatedGoodsList,
+            'orderInfo' => $orderInfo,
+            'userCoupon' => $userCoupon
+        ];
+    }
+
+        /**
+     * 订单列表
+     *
+     * @param $param
+     * @return \think\Paginator
+     */
+    public static function getOrderList($userId = 0, $param,$status = [])
+    {
+        $pageSize = 15;
+        if (!empty($param['pageSize'])) {
+            $pageSize = $param['pageSize'];
+        }
+        return Order::with(['orderGoods'])
+            ->where(function ($query) use ($param) {
+
+                if (!empty($userId)) {
+                    $query->where('user_id', $userId);
+                }
+               
+                if (!empty($status)) {
+                    $query->whereIn('order_status', $status );
+                }
+              
+                if (isset($param['keywords']) && $param['keywords'] != '') {
+                    $query->where('order_sn', 'in', function ($query) use ($param) {
+                        return $query->name('shop_order_goods')->where('order_sn|goods_title', 'like', '%' . $param['q'] . '%')->field('order_sn');
+                    });
+                }
+            })
+            ->order('createtime desc')
+            ->paginate($pageSize, false, ['query' => request()->get()]);
+    }
+
+
+} 

+ 62 - 0
application/common/Service/SkuSpec.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace app\common\Service;
+
+use app\common\model\SkuSpec as SkuSpecModel;
+use think\Db;
+
+class SkuSpec
+{
+    /**
+     * 获取指定商品的SKU信息
+     * @param int $goods_id 商品ID
+     * @return array
+     */
+    public static function getGoodsSkuSpec($goods_id)
+    {
+        $list = (new SkuSpecModel())
+             ->field('MIN(`id`) AS `id`, MIN(`goods_id`) AS `goods_id`, `spec_id`')
+             ->where('goods_id', $goods_id)
+             ->with([
+                'Spec',
+                'SkuValue' => function ($query) use ($goods_id) {
+                    $query->where('goods_id', $goods_id)
+                    ->field('id,goods_id,spec_id,spec_value_id')
+                    ->with(['SpecValue']);
+                }
+            ])->group('spec_id')->select();
+
+        $list = collection($list)->toArray();
+        return $list;
+    }
+
+    /**
+     * 批量获取SKU规格属性字符串
+     * @param array $sku_ids SKU ID数组
+     * @return array 返回 [sku_id => 'spec_name:spec_value,spec_name:spec_value'] 格式
+     */
+    public static function getSkuAttrs($sku_ids)
+    {
+        if (empty($sku_ids)) {
+            return [];
+        }
+
+        // 通过shop_goods_sku表的spec_value_ids字段来查询规格信息
+        $list = Db::name('shop_goods_sku')
+            ->alias('sku')
+            ->field('sku.id, GROUP_CONCAT(sp.name,":",sv.value ORDER BY sp.id asc) as sku_attr')
+            ->join('shop_goods_sku_spec gss', "FIND_IN_SET(gss.id, sku.spec_value_ids)", 'LEFT')
+            ->join('shop_spec sp', 'sp.id = gss.spec_id', 'LEFT')
+            ->join('shop_spec_value sv', 'sv.id = gss.spec_value_id', 'LEFT')
+            ->where('sku.id', 'in', $sku_ids)
+            ->group('sku.id')
+            ->select();
+
+        $result = [];
+        foreach ($list as $item) {
+            $result[$item['id']] = $item['sku_attr'] ?: '';
+        }
+
+        return $result;
+    }
+}

+ 275 - 0
application/common/Service/lottery/LuckLotteryRecordServices.php

@@ -0,0 +1,275 @@
+<?php
+
+
+namespace app\services\lottery;
+
+
+use app\common\model\lottery\Record;
+
+/**
+ *  抽奖记录
+ * Class LuckLotteryRecordServices
+ *
+ */
+class LuckLotteryRecordServices
+{
+
+
+
+    /**
+     * 获取抽奖记录列表
+     * @param array $where
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getList(array $where)
+    {
+        [$page, $limit] = $this->getPageValue();
+        /** @var LuckLotteryServices $luckServices */
+        $luckServices = app()->make(LuckLotteryServices::class);
+        $where['lottery_id'] = $where['factor'] == '' ? '' : $luckServices->value(['factor' => $where['factor']], 'id');
+        unset($where['factor']);
+        $list = $this->dao->getList($where, '*', ['lottery', 'prize', 'user'], $page, $limit);
+        foreach ($list as &$item) {
+            $item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
+        }
+        $count = $this->dao->count($where);
+        return compact('list', 'count');
+    }
+
+    /**
+     * 获取中奖记录
+     * @param array $where
+     * @param int $limit
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getWinList(array $where, int $limit = 20)
+    {
+        $where = $where + ['not_type' => 1];
+        $list = $this->dao->getList($where, 'id,uid,prize_id,lottery_id,receive_time,add_time', ['user', 'prize'], 0, $limit);
+        foreach ($list as &$item) {
+            $item['receive_time'] = $item['receive_time'] ? date('Y-m-d H:i:s', $item['receive_time']) : '';
+            $item['add_time'] = $item['add_time'] ? date('Y-m-d H:i', $item['add_time']) : '';
+        }
+        return $list;
+    }
+
+    /**
+     * 参与抽奖数据统计
+     * @param int $lottery_id
+     * @return int[]
+     */
+    public function getLotteryRecordData(int $lottery_id)
+    {
+        $data = ['all' => 0, 'people' => 0, 'win' => 0];
+        if ($lottery_id) {
+            $where = [['lottery_id', '=', $lottery_id]];
+            $data['all'] = $this->dao->getCount($where);
+            $data['people'] = $this->dao->getCount($where, 'uid');
+            $data['win'] = $this->dao->getCount($where + [['type', '>', 1]], 'uid');
+        }
+        return $data;
+    }
+
+    /**
+     * 写入中奖纪录
+     * @param int $uid
+     * @param array $prize
+     * @param array $userInfo
+     * @return mixed
+     */
+    public function insertPrizeRecord(int $uid, array $prize, array $userInfo = [])
+    {
+        if (!$userInfo) {
+          return [1,'请上传用户信息'];
+        }
+        if (!$prize) {
+            return [1,'请上传奖品信息'];
+        }
+        $data = [];
+        $data['user_id'] = $uid;
+        $data['lottery_id'] = $prize['lottery_id'];
+        $data['prize_id'] = $prize['id'];
+        $data['type'] = $prize['type'];
+        $data['num'] = $prize['num'];
+        $data['createtime'] = time();
+        $record = new Record();
+        if (! $record->save($data)) {
+            return [1,'记录日志数据接口失败'];
+        }
+        return [0,$record];
+    }
+
+    /**
+     * 领取奖品
+     * @param int $uid
+     * @param int $lottery_record_id
+     * @param string $receive_info
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function receivePrize(int $uid, int $lottery_record_id, array $receive_info = [])
+    {
+        /** @var UserServices $userServices */
+        $userServices = app()->make(UserServices::class);
+        $userInfo = $userServices->getUserInfo($uid);
+        if (!$userInfo) {
+            throw new ApiException(410032);
+        }
+        $lotteryRecord = $this->dao->get($lottery_record_id, ['*'], ['prize']);
+        if (!$lotteryRecord || !isset($lotteryRecord['prize'])) {
+            throw new ApiException(410050);
+        }
+        if ($lotteryRecord['is_receive'] == 1) {
+            throw new ApiException(410051);
+        }
+        $data = ['is_receive' => 1, 'receive_time' => time(), 'receive_info' => $receive_info];
+        $prize = $lotteryRecord['prize'];
+        $this->transaction(function () use ($uid, $userInfo, $lottery_record_id, $data, $prize, $userServices, $receive_info) {
+            //奖品类型1:未中奖2:积分3:余额4:红包5:优惠券6:站内商品7:等级经验8:用户等级 9:svip天数
+            switch ($prize['type']) {
+                case 1:
+                    break;
+                case 2:
+                    /** @var UserBillServices $userBillServices */
+                    $userBillServices = app()->make(UserBillServices::class);
+                    $userBillServices->income('lottery_give_integral', $uid, $prize['num'], $userInfo['integral'] + $prize['num'], $prize['id']);
+                    $userServices->update($uid, ['integral' => bcadd((string)$userInfo['integral'], (string)$prize['num'], 0)], 'uid');
+                    break;
+                case 3:
+                    /** @var UserMoneyServices $userMoneyServices */
+                    $userMoneyServices = app()->make(UserMoneyServices::class);
+                    $now_money = bcadd((string)$userInfo['now_money'], (string)$prize['num'], 2);
+                    $userMoneyServices->income('lottery_give_money', $uid, $prize['num'], $now_money, $prize['id']);
+                    $userServices->update($uid, ['now_money' => $now_money], 'uid');
+                    break;
+                case 4:
+                    /** @var WechatUserServices $wechatServices */
+                    $wechatServices = app()->make(WechatUserServices::class);
+                    $openid = $wechatServices->uidToOpenid((int)$uid, 'wechat');
+                    $type = 'JSAPI';
+                    if (!$openid) {
+                        $openid = $wechatServices->uidToOpenid((int)$uid, 'routine');
+                        $type = 'mini';
+                    }
+                    if (!$openid) {
+                        $openid = $wechatServices->uidToOpenid((int)$uid, 'app');
+                        $type = 'APP';
+                    }
+                    if ($openid) {
+                        /** @var StoreOrderCreateServices $services */
+                        $services = app()->make(StoreOrderCreateServices::class);
+                        $wechat_order_id = $services->getNewOrderId();
+                        /** @var CapitalFlowServices $capitalFlowServices */
+                        $capitalFlowServices = app()->make(CapitalFlowServices::class);
+                        $capitalFlowServices->setFlow([
+                            'order_id' => $wechat_order_id,
+                            'uid' => $uid,
+                            'price' => bcmul('-1', (string)$prize['num'], 2),
+                            'pay_type' => 'weixin',
+                            'nickname' => $userInfo['nickname'],
+                            'phone' => $userInfo['phone']
+                        ], 'luck');
+
+                        if (sys_config('pay_wechat_type')) {
+                            $pay = new Pay('v3_wechat_pay');
+                            $pay->merchantPay($openid, $wechat_order_id, $prize['num'], [
+                                'type' => $type,
+                                'batch_name' => '抽奖中奖红包',
+                                'batch_remark' => '您于' . date('Y-m-d H:i:s') . '中奖.' . $prize['num'] . '元'
+                            ]);
+                        } else {
+                            WechatService::merchantPay($openid, $wechat_order_id, $prize['num'], '抽奖中奖红包');
+                        }
+                    }
+                    break;
+                case 5:
+                    /** @var StoreCouponIssueServices $couponIssueService */
+                    $couponIssueService = app()->make(StoreCouponIssueServices::class);
+                    try {
+                        $couponIssueService->issueUserCoupon($prize['coupon_id'], $userInfo);
+                    } catch (\Throwable $e) {
+                        Log::error('抽奖领取优惠券失败,原因:' . $e->getMessage());
+                    }
+                    break;
+                case 6:
+                    if (!$receive_info['name'] || !$receive_info['phone'] || !$receive_info['address']) {
+                        throw new ApiException(410052);
+                    }
+                    if (!check_phone($receive_info['phone'])) {
+                        throw new ApiException(410053);
+                    }
+                    break;
+            }
+            $this->dao->update($lottery_record_id, $data, 'id');
+        });
+        return true;
+    }
+
+    /**
+     * 发货、备注
+     * @param int $lottery_record_id
+     * @param array $data
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function setDeliver(int $lottery_record_id, array $data)
+    {
+        $lotteryRecord = $this->dao->get($lottery_record_id);
+        if (!$lotteryRecord) {
+            throw new ApiException(410054);
+        }
+        $deliver_info = $lotteryRecord['deliver_info'];
+        $edit = [];
+        //备注
+        if ($data['deliver_name'] && $data['deliver_number']) {
+            if ($lotteryRecord['type'] != 6 && ($data['deliver_name'] || $data['deliver_number'])) {
+                throw new ApiException(410055);
+            }
+            if ($lotteryRecord['type'] == 6 && (!$data['deliver_name'] || !$data['deliver_number'])) {
+                throw new ApiException(410056);
+            }
+            $deliver_info['deliver_name'] = $data['deliver_name'];
+            $deliver_info['deliver_number'] = $data['deliver_number'];
+            $edit['is_deliver'] = 1;
+            $edit['deliver_time'] = time();
+        }
+        $deliver_info['mark'] = $data['mark'];
+        $edit['deliver_info'] = $deliver_info;
+        if (!$this->dao->update($lottery_record_id, $edit, 'id')) {
+            throw new ApiException(100005);
+        }
+        return true;
+    }
+
+    /**
+     * 获取中奖记录
+     * @param int $uid
+     * @param array $where
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getRecord( $uid = 0,$page =1, $pageSize = 10)
+    {
+        $list =  Record::with(['lottery','prize','user'])
+            ->where('user_id',$uid)
+            ->field('*')->order("createtime desc")
+            ->paginate($pageSize, false, ['page' => $page]);
+        $list->each(function($item, $key){
+            $item->deliver_time =  $item['deliver_time'] ? date('Y-m-d H:i:s', $item['deliver_time']) : '';;
+            $item->receive_time =  $item['receive_time'] ? date('Y-m-d H:i:s', $item['receive_time']) : '';
+        });
+        return $list;
+    }
+}

+ 176 - 0
application/common/Service/lottery/LuckLotteryServices.php

@@ -0,0 +1,176 @@
+<?php
+
+
+namespace app\services\lottery;
+use app\admin\model\lottery\Info;
+use app\admin\model\lottery\Record;
+use app\admin\model\User;
+use think\Db;
+
+/**
+ *
+ * Class LuckLotteryServices
+ * @package app\services\activity\lottery
+ */
+class LuckLotteryServices
+{
+    /**
+     * 抽奖形式,奖品数量
+     * @var int[]
+     */
+    protected $lottery_type = [
+        '1' => 8 //九宫格
+    ];
+    /**
+     * 抽奖类型
+     * @var string[]
+     */
+    protected $lottery_factor = [
+        '1' => '积分',
+        '2' => '余额',
+        '3' => '下单支付成功',
+        '4' => '订单评价',
+        '5' => '关注公众号'
+    ];
+
+    public function getLottery($id, $field = '*',$with = ['prize'])
+    {
+        $time = time();
+        return Info::where('id',$id)
+            ->with($with)
+            ->where('status', 1)
+            ->where('start_time', '<=', $time)
+            ->where('end_time', '>=', $time)
+            ->find();
+    }
+
+    public function getLotteryInfo($id, $field = '*',$with = ['prize'])
+    {
+        return Info::where('id',$id)
+            ->where('status', 1)
+            ->find();
+    }
+
+
+
+
+    /**
+     * 抽奖
+     * @param int $uid
+     * @param int $lottery_id
+     * @return mixed
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function luckLottery( $userInfo,  $lottery_id)
+    {
+
+        $lottery = $this->getLotteryInfo($lottery_id, '*', [], true);
+        if (!$lottery) {
+           return [1,'抽奖活动不存在'];
+        }
+
+        $uid  = $userInfo['id'];
+        $lottery = $lottery->toArray();
+        if ( $lottery['lottery_num_term'] > 0 &&  $this->getPaperDateGradeCount($lottery_id,$uid) >= $lottery['lottery_num_term']) {
+            return [1,'当前抽奖次数已达今日上限,明天再来吧~'];
+        }
+
+        $lotteryPrizeServices = new LuckPrizeServices();
+        $lotteryPrize = $lotteryPrizeServices->getLotteryPrizeList($lottery_id);
+        if (!$lotteryPrize) {
+            return [1,'抽奖奖品不存在'];
+        }
+        $lotteryPrize = collection($lotteryPrize)->toArray();
+
+        return Db::transaction(function () use ($uid, $lotteryPrize, $userInfo, $lottery) {
+            $luckPrizeServices =new LuckPrizeServices();
+            //随机抽奖
+            $prize = $luckPrizeServices->getLuckPrize($lotteryPrize);
+            if (!$prize) {
+                return [1,'抽奖异常'];
+            }
+            //中奖扣除积分、余额
+            list($nError,$msg) = $this->lotteryFactor($uid, $userInfo, $lottery);
+            if ($nError ==  1) {
+                return [1,$msg];
+            }
+            //中奖减少奖品数量
+            list($nError,$msg) = $luckPrizeServices->decPrizeNum($prize['id'], $prize);
+            if ($nError ==  1) {
+                return [1,$msg];
+            }
+            $lotteryRecordServices = new LuckLotteryRecordServices();
+            //中奖写入记录
+            list($nError,$record) = $lotteryRecordServices->insertPrizeRecord($uid, $prize, $userInfo);
+            if ($nError ==  1) {
+                return [1,$record];
+            }
+            //不是站内商品直接领奖
+            /*if ($prize['type'] != 6) {
+                $lotteryRecordServices->receivePrize($uid, (int)$record->id);
+            }*/
+            $prize['lottery_record_id'] = $record->id;
+            return [0,$prize];
+        });
+    }
+
+
+    /**
+     * 次数
+     * @param int    $paper_id 试卷ID
+     * @param string $date     日期
+     * @return int|string
+     */
+    public static function getPaperDateGradeCount($lottery_id,$uid)
+    {
+        if (!$lottery_id) {
+            return 0;
+        }
+        return Record::where('lottery_id', $lottery_id)->where('user_id',$uid)->whereTime('createtime', 'today')->count();
+    }
+
+    /**
+     * 抽奖消耗扣除用户积分、余额等
+     * @param int $uid
+     * @param array $userInfo
+     * @param array $lottery
+     * @return bool
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function lotteryFactor(int $uid, array $userInfo, array $lottery)
+    {
+        if (!$userInfo || !$lottery) {
+            return true;
+        }
+        if (!User::where('id',$uid)->setDec('lottery_num')) {
+            return [1,'抽奖次数修改失败'];
+        }
+        return [0,'抽奖次数修改成功'];
+
+    }
+
+    /**
+     * 增加抽奖次数
+     * @param int $uid
+     * @param array $userInfo
+     * @param array $lottery
+     * @return bool
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function lotteryNum( $uid)
+    {
+        if (!User::where('id',$uid)->setInc('lottery_num')) {
+            return [1,'抽奖次数增加失败'];
+        }
+        return [0,'抽奖次数成功增加'];
+
+    }
+
+
+
+
+
+}

+ 158 - 0
application/common/Service/lottery/LuckPrizeServices.php

@@ -0,0 +1,158 @@
+<?php
+
+
+namespace app\services\lottery;
+
+use app\admin\model\lottery\Prize;
+
+/**
+ *
+ * Class LuckPrizeServices
+ *
+ */
+class LuckPrizeServices
+{
+    /**
+     * @var array 1:未中奖2:积分3:余额4:红包5:优惠券6:站内商品7:等级经验8:用户等级 9:svip天数
+     */
+    public $prize_type = [
+        '1' => '未中奖',
+        '2' => '积分',
+        '3' => '余额',
+        '4' => '红包',
+        '5' => '优惠券',
+        '6' => '站内商品',
+        '7' => '等级经验',
+        '8' => '用户等级',
+        '9' => 'svip天数'
+    ];
+
+    /**
+     * 奖品数据字段
+     * @var array
+     */
+    public $prize = [
+        'id' => 0,
+        'type' => 1,
+        'lottery_id' => 0,
+        'name' => '',
+        'prompt' => '',
+        'image' => '',
+        'chance' => 0,
+        'total' => 0,
+        'coupon_id' => 0,
+        'product_id' => 0,
+        'unique' => '',
+        'num' => 1,
+        'sort' => 0,
+        'status' => 1,
+        'is_del' => 0,
+        'add_time' => 0,
+    ];
+    /**
+     * 奖品数据验证
+     * @param array $data
+     */
+    public function checkPrizeData(array $data)
+    {
+        $data = array_merge($this->prize, array_intersect_key($data, $this->prize));
+        if (!isset($data['name']) || !$data['name']) {
+            return [1,'奖品不存在'];
+        }
+        if (!isset($data['image']) || !$data['image']) {
+            return [1,'奖品不存在'];
+        }
+        if (!isset($data['chance']) || !$data['chance']) {
+            return [1,'奖品不存在'];
+        }
+        if (!isset($data['type']) || !isset($this->prize_type[$data['type']])) {
+            return [1,'奖品不存在'];
+        }
+        if (in_array($data['type'], [2, 3, 4]) && (!isset($data['num']) || !$data['num'])) {
+            $msg = '';
+            switch ($data['type']) {
+                case 2:
+                    $msg = '积分';
+                    break;
+                case 3:
+                    $msg = '余额';
+                    break;
+                case 4:
+                    $msg = '红包';
+                    break;
+            }
+            return [1,'type' => $msg];
+        }
+        return [0,$data];
+    }
+
+    /**
+     * 获取某个抽奖活动的所有奖品
+     * @param int $lottery_id
+     * @param string $field
+     */
+    public function getLotteryPrizeList(int $lottery_id, string $field = '*')
+    {
+        $where = ['status' => 1,'lottery_id' => $lottery_id];
+        return Prize::where($where)->field($field)->order('sort desc,id desc')->select();
+    }
+
+
+    /**
+     * 随机奖品
+     * @param array $data
+     * @return array|mixed
+     */
+    function getLuckPrize(array $data)
+    {
+        $prize = [];
+        if (!$data) return $prize;
+        $totalChance = array_sum(array_column($data, 'chance'));
+        if (!$totalChance) return $prize;
+        $startChance = 0;
+        mt_srand();
+        $prizeChance = rand(0, $totalChance);
+        $newPrize = array_combine(array_column($data, 'type'), $data);
+        foreach ($data as $item) {
+            $newStartChance = $item['chance'] + $startChance;
+            //随机数在这个基数端内 且该商品数量大于0 中奖
+            if ($prizeChance >= $startChance && $prizeChance < $newStartChance) {
+                //随机到不是未中奖奖品-》设置了奖品数量-》数量不足时 返回未中奖奖品   || 抽到优惠券 数量不足
+                if (($item['type'] != 1 && $item['total'] != -1 && $item['total'] <= 0)
+                    ) {
+                    $prize = $newPrize[1] ?? [];
+                } else {
+                    $prize = $item;
+                }
+                break;
+            }
+            $startChance = $newStartChance;
+        }
+        return $prize;
+    }
+
+    /**
+     * 中奖后减少奖品数量
+     * @param int $id
+     * @param array $prize
+     * @return array
+     */
+    public function decPrizeNum(int $id, array $prize = [])
+    {
+        if (!$id) return [1,'请上传奖品'];
+        if (!$prize) {
+            $prize = Prize::find($id);
+        }
+        if (!$prize) {
+           return [1,'奖品不存在'];
+        }
+        //不是未中奖奖品 减少奖品数量
+        if ($prize['type'] != 1 && $prize['total'] >= 1) {
+            $total = $prize['total'] - 1;
+            if (! Prize::where('id',$id)->update(['total' => $total] )) {
+                return [1,'修改奖品数量失败'];
+            }
+        }
+        return [0,'减少奖品数量成功'];
+    }
+}