adminOperateLogger = new AdminOperate([ 'fetchClass' => BaApproachOrder::class, ]); $this->payType = ShopGoods::SALE_TYPE[7]['label']; } /** * @inheritdoc */ public function rules(): array { return [ [['sn', 'expressCompany', 'orderTrackNo', 'status', 'remark','type','addressId','payType','goodsId','goodsNum', 'payPassword','userName','consignee','acceptMobile','province','lgaName','cityName','detailaddress','email'], 'trim'], [['sn', 'expressCompany', 'orderTrackNo', 'status', 'remark','type','addressId','payType','goodsId','goodsNum', 'payPassword','userName','consignee','acceptMobile','province','detailaddress'], 'required'], [['status'], 'isStatus'], [['addressId'], 'isAddress'], [['payPassword'], 'validatePassword'], ]; } public function attributeLabels(): array { return [ 'sn' => 'Order code',//订单号 'expressCompany' => 'Express Company',//快递公司 'orderTrackNo' => 'Track Code',//快递单号 'status' => 'Status',//状态 'remark' => 'Remark',//备注 'type' => 'Order Type',//订单类型 'addressId' => 'AddressId',//收货地址 'payType' => 'PayType',//支付方式 'goodsId' => 'Product ID',// 商品编号 'goodsNum' => 'Quantity',// 'userName' => 'UserName',//复消会员编号 'consignee' => 'Consignee',//收货人 'acceptMobile' => 'Accept Mobile',//收货电话 'lgaName' => 'LgaName', 'cityName' => 'CityName', 'detailaddress' => 'Detail Address',// 收货详细地址 'email' => 'Email', ]; } /** * 指定校验场景 * @return array */ public function scenarios(): array { $parentScenarios = parent::scenarios(); $customScenarios = [ // 管理员修改订单状态 'adminStatus' => ['sn', 'status'], // 校验订单支付 'verifyPayStack' => ['sn', 'note', 'status'], // 会员下单 'userOrder' => ['type', 'addressId', 'goodsId', 'goodsNum', 'note', 'payPassword'], ]; return array_merge($parentScenarios, $customScenarios); } /** * 校验之前 * @return bool */ public function beforeValidate() { $parentValidate = parent::beforeValidate(); if ($this->sn) { $this->_model = BaApproachOrder::findOne(['SN' => $this->sn]); if (!$this->_model){ $this->addError('sn', 'Order does not exist');// 订单不存在 return false; } } if ($this->scenario == 'verifyPayStack'){ if ($this->_model->STATUS != \Yii::$app->params['orderStatus']['notPaid']['value']) { $this->addError('sn', '订单支付状态错误'); return false; } } return $parentValidate; } /** * 判断收货地址是否存在 * @param $attribute * @param $params */ public function isAddress($attribute, $params) { if (!$receiveAddress = BaReceiveAddress::find()->where(' ID=:ID', [':ID' => $this->addressId])->asArray()->one()) { $this->addError($attribute, 'Receive address does not exist'); // 收货地址不存在 } else { $this->_address = $receiveAddress; } } /** * 校验支付密码 * @param $attribute * @param $params */ public function validatePassword($attribute, $params) { if (!BaUser::validatePayPassword(\Yii::$app->user->id, $this->payPassword)) { $this->addError($attribute, 'The payment password is incorrect');//支付密码不正确 } } /** * 判断支付方式 * @param $attribute */ public function isPayType($attribute) { if ($this->payType != 'pay_stack'){ $this->addError($attribute, '只允许PayStack方式支付'); return; } // 一个订单只能包含一类商品 $goods = ShopGoods::find()->select('ID,CATEGORY_TYPE')->where(['in', 'ID', $this->goodsId])->andWhere(['STATUS' => 1])->asArray()->all(); $goodsCategoryType = array_unique(array_column($goods, 'CATEGORY_TYPE')); if (count($goodsCategoryType) != 1) { $this->addError($attribute, 'Order cannot contain multiple product categories');//订单不能包含多种商品分类 } } /** * 校验类型 * @param $attribute */ public function isStatus($attribute) { if($this->type && !in_array($this->type, \Yii::$app->params['orderStatus'])){ $this->addError($attribute, '订单状态类型错误'); return ; } if ($this->scenario == 'adminStatus'){ if ($this->status == $this->_model['STATUS']) { $this->addError($attribute, '订单状态没有改变'); return ; } if($this->status == \Yii::$app->params['orderStatus']['notPaid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['delivery']) { $this->addError($attribute, '订单已经进入物流状态不能改为未支付'); return ; } elseif($this->status == \Yii::$app->params['orderStatus']['paid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['cancel']) { $this->addError($attribute, '订单已失效不能处理'); return ; } elseif($this->status == \Yii::$app->params['orderStatus']['delivery']) { $this->addError($attribute, '订单不能单独处理为物流状态'); return ; } elseif($this->status == \Yii::$app->params['orderStatus']['complete'] && $this->_model['STATUS'] > \Yii::$app->params['orderStatus']['cancel']) { $this->addError($attribute, '订单已失效不能处理'); return ; } elseif($this->status == \Yii::$app->params['orderStatus']['cancel']) { if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) { $this->addError($attribute, '订单已完成不能取消'); return ; } if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['del']) { $this->addError($attribute, '订单已删除不能取消'); return ; } } elseif($this->status == \Yii::$app->params['orderStatus']['del']) { if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) { $this->addError($attribute, '订单已完成不能删除'); return ; } } } } /** * 校验PayStack支付,更新订单状态.同步到正式订单. * @throws Exception */ public function verifyPayStack() { if (!$this->validate()) { return null; } // 调用PayStack支付校验 LoggerTool::info([$this->note['reference'], $this->note]); $payload = PayStack::transactionVerify($this->note['reference']); LoggerTool::info($payload); if ($payload['status'] !== true) { throw new Exception(Form::formatErrorsForApi($payload['message'])); } if ($payload['data']['amount'] != $this->_model->PAY_AMOUNT * 100) { throw new Exception(Form::formatErrorsForApi('支付金额与订单金额不符')); } // 订单类型:baOrder(BA订单)、baDec(BA报单) $orderType = $this->note['metadata']['custom_fields'][1]['value'] ?? false; $db = \Yii::$app->db; $transaction = $db->beginTransaction(); try { // 更新准订单状态为已支付 $this->_model->STATUS = $this->status; $this->_model->NOTE = json_encode($this->note); $this->_model->PAY_AT = Date::utcToTime($this->note['paid_at']); $this->_model->EMAIL = $this->note['email']; if (!$this->_model->save()) { throw new Exception(Form::formatErrorsForApi($this->_model->getErrors())); } // 更新订单商品的支付Email BaApproachOrderGoods::updateAll(['EMAIL' => $this->note['email']], 'ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]); // 同步准订单到正式订单 BaOrder::insertOne($this->_model->toArray()); // 同步准订单商品到正式订单商品 $approachOrderGoods = BaApproachOrderGoods::findAllAsArray('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]); foreach ($approachOrderGoods as &$approachOrderGood) { $approachOrderGood['EMAIL'] = $this->email; } BaOrderGoods::batchInsert($approachOrderGoods); // BA报单 if ($orderType === 'baDec') { // 同步报单 $approachDecOrder = BaApproachDecOrder::findOneAsArray('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]); if ($approachDecOrder) { unset($approachDecOrder['STATUS']); // 同步报单 BaDecOrder::insertOne($approachDecOrder); // 修改会员锁定状态 if (!BaUser::updateAll(['STATUS' => 1], 'ID=:USER_ID', [':USER_ID' => $approachDecOrder['TO_USER_ID']])) { throw new Exception(Form::formatErrorsForApi('Update Brand Ambassador status error')); } } BaApproachDecOrder::deleteAll('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]); } // 删除中间表 BaApproachOrder::deleteAll('SN = :SN', [':SN' => $this->sn]); BaApproachOrderGoods::deleteAll('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]); $transaction->commit(); } catch (Exception $e) { $transaction->rollBack(); $this->addError('edit', $e->getFile() . ' ' . $e->getMessage()); return null; } return $this->_model; } /** * 复销 */ public function add() { if(!$this->validate()){ return null; } $ids = $this->goodsId; $totalAmount = 0; $totalPv = 0; $goodsType = ShopGoods::GOODS_TYPE; $exchangeRate = floatval(Cache::getSystemConfig()['exchangeRate']['VALUE'] ?? 0); // 汇率 foreach ($this->goodsNum as $k => $v) { if ($v) { $goods = ShopGoods::findOneAsArray('ID = :ID AND STATUS = 1', [':ID' => $ids[$k]]); if ($goods['STORE_NUMS'] > 0) { if ($goods['TYPE'] == 1 || $goods['TYPE'] == 2) { $discount = $goodsType[$goods['TYPE']]['discount']; $realPrice = $goods['SELL_PRICE'] * $discount/100; $realPv = $goods['PRICE_PV'] * $discount/100; $realPriceStandard = $goods['SELL_PRICE_STANDARD'] * $discount/100; } else { $discount = $goods['SELL_DISCOUNT']; $realPrice = $goods['SELL_PRICE'] * $discount; $realPv = $goods['PRICE_PV'] * $discount; $realPriceStandard = $goods['SELL_PRICE_STANDARD'] * $discount; } $totalAmount += $realPrice * intval($v); $totalPv += $realPv * intval($v); $this->_orderGoods[] = [ 'GOODS_ID' => $goods['ID'], 'PRICE' => $goods['SELL_PRICE'], 'PV' => 0, 'REAL_PRICE' => $realPrice, 'REAL_PV' => 0, 'POINT' => 0, 'BUY_NUMS' => intval($v), 'TAX_RATE' => $goods['TAX_RATE'], 'SKU_CODE' => $goods['GOODS_NO'], 'GOODS_TITLE' => $goods['GOODS_NAME'], 'CATEGORY_TYPE' => $goods['CATEGORY_TYPE'], 'PAY_TYPE' => $this->payType, 'EMAIL' => $this->email, 'STANDARD_PRICE' => $goods['SELL_PRICE_STANDARD'], 'REAL_STANDARD_PRICE' => $realPriceStandard, 'EXCHANGE_RATE' => $exchangeRate, ]; } } } // 运费.奈拉 $freight = floatval(Cache::getSystemConfig()['freight']['VALUE'] ?? 0); // 普通商品免运费阈值.奈拉 $freeShipping = floatval(Cache::getSystemConfig()['freeShipping']['VALUE'] ?? 0); $this->_decAmount = $totalAmount; $this->_decPv = $totalPv; $this->_freight = ($totalAmount >= $freeShipping) ? 0 : $freight; $this->_payAmount = $this->_decAmount + $this->_freight; $db = \Yii::$app->db; $transaction = $db->beginTransaction(); try { // 创建订单 $orderResult = $this->addOrder(); if (!$orderResult) { throw new Exception(Form::formatErrorsForApi($orderResult->getErrors())); } $transaction->commit(); return $orderResult; } catch (\Exception $e) { $transaction->rollBack(); $this->addError('add', $e->getMessage()); return null; } } /** * 复销订单 * @throws Exception */ public function addOrder() { $periodObj = Period::instance(); $nowPeriodNum = $periodObj->getNowPeriodNum(); $nowCalcMonth = $periodObj->getYearMonth($nowPeriodNum); $userId = \Yii::$app->user->id; $userName = Info::getBaUserNameByUserId($userId); $userMobile = Info::getBaUserMobileByUserId($userId); $userEmail = Info::getBaUserEmailByUserId($userId); // 加入订单信息 if ($this->_address['PROVINCE'] != 1) { $warehouse = Region::getWarehouseByCode($this->_address['PROVINCE']);//仓库 if (!$warehouse) { // throw new Exception('地区2暂时不支持配送,具体联系客服'); } }else{ $warehouse = '01'; } $_hasPV = 0; $ordNo = $this->_generateSn(); $orderModel = new BaApproachOrder(); $orderModel->SN = 'OS' . $ordNo; $orderModel->DEC_SN = 'DS' . $ordNo; $orderModel->ORDER_TYPE = $this->type; $orderModel->USER_ID = $userId; $orderModel->USER_NAME = $userName; $orderModel->ORDER_AMOUNT = $this->_decAmount; $orderModel->PV = $_hasPV; $orderModel->PAY_AMOUNT = $this->_payAmount; $orderModel->PAY_PV = 0; $orderModel->PAY_AT = 0; $orderModel->PAY_TYPE = $this->payType; $orderModel->PERIOD_NUM = $nowPeriodNum; $orderModel->P_CALC_MONTH = Date::ociToDate($nowCalcMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH); $orderModel->FREIGHT = $this->_freight; $orderModel->PAY_FREIGHT = $this->_freight; $orderModel->CONSIGNEE = $this->_address['CONSIGNEE'] ?: $userName; $orderModel->MOBILE = $this->_address['MOBILE'] ?: $userMobile; $orderModel->LGA_NAME = $this->_address['LGA_NAME']; $orderModel->CITY_NAME = $this->_address['CITY_NAME']; $orderModel->ADDRESS = $this->_address['ADDRESS']; $orderModel->FRONT_REMARK = $this->remark; $orderModel->WAREHOUSE = $warehouse; $orderModel->STATUS = \Yii::$app->params['orderStatus']['notPaid']['value']; $orderModel->CREATED_AT = Date::nowTime(); $orderModel->CREATE_USER = $userName; $orderModel->EMAIL = $userEmail ?: $userName . '@elken.net'; $orderModel->EXPRESS_TYPE = 1; $orderModel->PROVINCE = 1; $orderModel->CITY = 1; $orderModel->COUNTY = 1; if (!$orderModel->save()) { $this->addErrors($orderModel->getErrors()); return false; } // 加入商品到订单商品表 foreach($this->_orderGoods as $key => $value) { $this->_orderGoods[$key]['ORDER_SN'] = $orderModel->SN; $this->_orderGoods[$key]['P_CALC_MONTH'] = Date::ociToDate($nowCalcMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH); } BaApproachOrderGoods::batchInsert($this->_orderGoods); return $orderModel; } /** * 生成订单流水号 * @return string */ private function _generateSn(): string { return Date::today('Ymd') . $this->_random(10, 1); } /** * 生成随机数 * @param $length * @param int $numeric * @return string */ private function _random($length, int $numeric = 0): string { $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed)); $hash = ''; $max = strlen($seed) - 1; for ($i = 0; $i < $length; $i++) { $hash .= $seed[mt_rand(0, $max)]; } return $hash; } }