Order.php 136 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723
  1. <?php
  2. namespace common\models\order;
  3. use addons\CardCoupons\common\models\CardCouponsScoreGive;
  4. use addons\Chanjet\common\global_func\Request;
  5. use addons\Chanjet\common\models\AddonsChanjetRequest;
  6. use addons\Community\common\service\HeadService;
  7. use addons\Coupon\common\models\AddonsCouponUserRelate;
  8. use addons\LaoDaFu\common\models\LaoDaFuStoreOrderService;
  9. use addons\Mch\common\models\Mch;
  10. use addons\PaySuccessJump\common\service\JumpUrlService;
  11. use addons\RandFee\common\service\RandFeeService;
  12. use addons\ShoppingLottery\common\helper\LotteryHelper;
  13. use addons\Store\common\models\StoreOrder;
  14. use addons\Store\common\models\StoreOrderDetail;
  15. use addons\Store\common\models\StoreOrderMarketing;
  16. use common\components\export\CsvTool;
  17. use common\enums\AddonsChanjetRequestEnum;
  18. use common\enums\AddonsEnum;
  19. use common\enums\DownloadEnum;
  20. use common\enums\GoodsTypeEnum;
  21. use common\enums\OrderSourceEnum;
  22. use common\enums\OrderStatusEnum;
  23. use common\enums\PayTypeEnum;
  24. use common\enums\RedisKeyEnum;
  25. use common\enums\RefundStatusEnum;
  26. use common\enums\RefundTypeEnum;
  27. use common\enums\ShippingStatusEnum;
  28. use common\enums\ShippingTypeEnum;
  29. use common\enums\StatusEnum;
  30. use common\enums\UserWalletEnum;
  31. use common\events\data\api\OrderToPayEvent;
  32. use common\events\order\OrderChangeAddressEvent;
  33. use common\events\order\OrderCloseEvent;
  34. use common\events\order\OrderCommentEvent;
  35. use common\events\order\OrderCreateEvent;
  36. use common\events\order\OrderDelEvent;
  37. use common\events\order\OrderFinishEvent;
  38. use common\events\order\OrderPaidEvent;
  39. use common\events\order\OrderReminderShipmentEvent;
  40. use common\events\order\OrderTakeDeliveryEvent;
  41. use common\helpers\ArrayHelper;
  42. use common\helpers\LockHelper;
  43. use common\models\cart\Cart;
  44. use common\models\common\Addons;
  45. use common\models\common\Download;
  46. use common\models\common\MallSetting;
  47. use common\models\common\PaymentTrade;
  48. use common\models\common\PaymentTradeGroupOrder;
  49. use common\models\common\PaymentTradeOrder;
  50. use common\models\goods\Goods;
  51. use common\models\goods\GoodsAttr;
  52. use common\models\goods\GoodsCate;
  53. use common\models\goods\GoodsCateRelate;
  54. use common\models\goods\GoodsExtra;
  55. use common\models\mall\MallAddons;
  56. use common\models\user\User;
  57. use common\models\user\UserPartitionRelationship;
  58. use common\models\user\UserSignAgreement;
  59. use Exception;
  60. use services\common\UserWalletService;
  61. use Yii;
  62. use yii\helpers\Json;
  63. /**
  64. * This is the model class for table "{{%order}}".
  65. *
  66. *
  67. * @property int $id 订单id
  68. * @property int $mall_id 商城id
  69. * @property int $mch_id 多商户id,0表示商城订单
  70. * @property int $store_id 门店id,
  71. * @property int $user_id 用户id
  72. * @property string $order_no 订单编号
  73. * @property string|null $out_trade_no 外部交易号
  74. * @property string|null $mobile 收货人的手机号码
  75. * @property int|null $province 收货人所在省
  76. * @property int|null $city 收货人所在城市
  77. * @property int|null $area 收货人所在街道
  78. * @property int|null $town 收货人所在镇
  79. * @property int|null $community 收货人所在镇
  80. * @property string|null $address 收货人详细地址
  81. * @property string|null $region_name 收货人详细地址
  82. * @property string|null $zip 收货人邮编
  83. * @property string|null $name 收货人姓名
  84. * @property string|null $receiver_name 收货人姓名
  85. * @property string $remark 用户订单备注
  86. * @property string $seller_remark 商家订单备注
  87. * @property int|null $shipping_type 订单配送方式
  88. * @property float|null $shipping_money 订单运费
  89. * @property float|null $reduce_shipping_money 满邮减免
  90. * @property float|null $refund_money 订单退款金额
  91. * @property float|null $pay_money 订单实付金额
  92. * @property float|null $goods_price 商品优惠后总价
  93. * @property float|null $original_goods_price 商品原本总价
  94. * @property string|null $ip 买家ip
  95. * @property int|null $coupon_id 优惠券券id
  96. * @property float|null $coupon_money 优惠券券支付金额
  97. * @property int|null $score 订单消耗积分
  98. * @property float|null $score_money 订单消耗积分抵多少钱
  99. * @property int|null $order_status 订单状态
  100. * @property int|null $pay_status 订单付款状态
  101. * @property int|null $shipping_status 订单配送状态
  102. * @property int|null $review_status 订单评价状态
  103. * @property int|null $is_feedback 订单售后状态
  104. * @property int|null $payment_type 支付类型
  105. * @property int $marketing_id 营销活动id
  106. * @property string $marketing_type 营销活动类型
  107. * @property int|null $invoice_id 发票id
  108. * @property int|null $status 状态[-1:删除;0:禁用;1启用]
  109. * @property int $is_comment 是否评价 0为未评价 1为已评价 2为已追评
  110. * @property int $is_recycle 是否加入回收站【1是 0否】
  111. * @property int|null $is_virtual 是否包含虚拟商品【1是 0否】
  112. * @property int|null $is_new_user 是否新顾客
  113. * @property int|null $pay_time 订单付款时间
  114. * @property int|null $shipping_time 买家要求配送时间
  115. * @property int|null $sign_time 买家签收时间
  116. * @property int|null $consign_time 卖家发货时间
  117. * @property int|null $finish_time 订单完成时间
  118. * @property int|null $close_time 关闭的时间
  119. * @property string|null $extra_info 额外信息
  120. * @property string|null $order_source 订单来源:JuheShop=>聚合供应链
  121. * @property int|null $created_at 创建时间
  122. * @property int|null $updated_at 更新时间
  123. * @property string|null $clerk_code 核销码
  124. * @property array|null $extra_data 额外信息
  125. * @property int $goods_subtype 订单商品子类型 (0:普通商品 其他:根据商品的goods_subtype而定)
  126. */
  127. class Order extends \common\models\base\BaseActiveRecord
  128. {
  129. const DEFAULT = 'order';
  130. public static $getRefundId;
  131. /**
  132. * {@inheritdoc}
  133. */
  134. public static function tableName()
  135. {
  136. return '{{%order}}';
  137. }
  138. /**
  139. * {@inheritdoc}
  140. */
  141. public function rules()
  142. {
  143. return [
  144. [['mch_id','store_id', 'mall_id','user_id', 'province', 'city', 'area','town', 'shipping_type', 'coupon_id', 'order_status', 'pay_status', 'shipping_status', 'review_status', 'is_feedback', 'payment_type', 'marketing_id', 'invoice_id', 'status', 'is_comment', 'is_recycle', 'is_virtual', 'is_new_user', 'pay_time', 'shipping_time', 'sign_time', 'consign_time', 'finish_time', 'close_time', 'created_at', 'updated_at', 'community','is_bill','bill_time', 'goods_subtype'], 'integer'],
  145. [['shipping_money', 'reduce_shipping_money', 'refund_money', 'pay_money', 'score', 'goods_price', 'original_goods_price', 'coupon_money', 'score_money'], 'number'],
  146. [['order_no', 'out_trade_no'], 'string', 'max' => 100],
  147. [['mobile', 'extra_data'], 'safe'],
  148. [['address', 'region_name'], 'string', 'max' => 200],
  149. [['zip', 'ip'], 'string', 'max' => 20],
  150. [['receiver_name', 'marketing_type', 'order_source'], 'string', 'max' => 50],
  151. [['remark', 'seller_remark'], 'string', 'max' => 255],
  152. [['extra_info'], 'string'],
  153. [['clerk_code'], 'string', 'max' => 12],
  154. ];
  155. }
  156. /**
  157. * {@inheritdoc}
  158. */
  159. public function attributeLabels()
  160. {
  161. return [
  162. 'id' => '订单id',
  163. 'mall_id' => '商城ID',
  164. 'mch_id' => '多商户id,0表示商城订单',
  165. 'store_id' => '门店i',
  166. 'user_id' => '用户id',
  167. 'order_no' => '订单编号',
  168. 'out_trade_no' => '外部交易号',
  169. 'mobile' => '收货人的手机号码',
  170. 'province' => '收货人所在省',
  171. 'city' => '收货人所在城市',
  172. 'area' => '收货人所在街道',
  173. 'town' => '收货人所在镇',
  174. 'address' => '收货详细地址',
  175. 'region_name' => '收货地区名称(省市区[乡/镇])',
  176. 'zip' => '收货人邮编',
  177. 'receiver_name' => '收货人姓名',
  178. 'remark' => '用户订单备注',
  179. 'seller_remark' => '商家订单备注',
  180. 'shipping_type' => '订单配送方式',
  181. 'shipping_money' => '订单运费',
  182. 'reduce_shipping_money' => '满邮减免',
  183. 'refund_money' => '订单退款金额',
  184. 'pay_money' => '订单实付金额',
  185. 'goods_price' => '商品优惠后总价',
  186. 'original_goods_price' => '商品原本总价',
  187. 'ip' => '买家ip',
  188. 'coupon_id' => '优惠券券id',
  189. 'coupon_money' => '优惠券券支付金额',
  190. 'score' => '订单消耗积分',
  191. 'score_money' => '订单消耗积分抵多少钱',
  192. 'order_status' => '订单状态',
  193. 'pay_status' => '订单付款状态',
  194. 'shipping_status' => '订单配送状态',
  195. 'review_status' => '订单评价状态',
  196. 'is_feedback' => '订单售后状态:0:未售后;1:售后中;2:售后完成',
  197. 'payment_type' => '支付类型',
  198. 'marketing_id' => '营销活动id',
  199. 'marketing_type' => '营销活动类型',
  200. 'invoice_id' => '发票id',
  201. 'status' => '状态[-1:删除;0:禁用;1启用]',
  202. 'is_comment' => '是否评价 0为未评价 1为已评价 2为已追评',
  203. 'is_recycle' => '是否加入回收站【1是 0否】',
  204. 'is_virtual' => '是否包含虚拟商品【1是 0否】',
  205. 'is_new_user' => '是否新顾客',
  206. 'pay_time' => '订单付款时间',
  207. 'shipping_time' => '买家要求配送时间',
  208. 'sign_time' => '买家签收时间',
  209. 'consign_time' => '卖家发货时间',
  210. 'finish_time' => '订单完成时间',
  211. 'close_time' => '关闭的时间',
  212. 'extra_info' => '额外信息',
  213. 'created_at' => '创建时间',
  214. 'updated_at' => '更新时间',
  215. 'clerk_code' => '核销码',
  216. 'goods_subtype' => '订单商品子类型 (0:普通商品 其他:根据商品的goods_subtype而定)',
  217. ];
  218. }
  219. // 关联模型
  220. public function getUser()
  221. {
  222. return $this->hasOne(User::class, ['id' => 'user_id']);
  223. }
  224. public function getBase_user()
  225. {
  226. return $this->hasOne(User::class, ['id' => 'user_id'])->select(['id', 'username','nickname', 'avatar_url','mobile']);
  227. }
  228. public function getDetail()
  229. {
  230. return $this->hasMany(OrderDetail::class, ['order_id' => 'id']);
  231. }
  232. public function getExpress()
  233. {
  234. return $this->hasMany(OrderExpress::class, ['order_id' => 'id'])->where(['status' => StatusEnum::ENABLED]);
  235. }
  236. public function getExpress_log()
  237. {
  238. return $this->hasMany(OrderExpressLog::class, ['order_id' => 'id'])->where(['status' => StatusEnum::ENABLED]);
  239. }
  240. public function getReserve()
  241. {
  242. return $this->hasOne(OrderReserve::class, ['order_id' => 'id']);
  243. }
  244. public function getMch()
  245. {
  246. return $this->hasOne(\addons\Mch\common\models\Mch::class, ['id' => 'mch_id'])->select('id,store_name,shop_mobile,logo_img,user_id');
  247. }
  248. /**
  249. * @return ActiveQueryInterface
  250. */
  251. public function getTrade()
  252. {
  253. return $this->hasOne(PaymentTrade::class, ['out_trade_no' => 'out_trade_no']);
  254. }
  255. public function getExtra()
  256. {
  257. return $this->hasOne(OrderExtra::class,['order_id' => 'id']);
  258. }
  259. public function getOrderPrescriptionDetails()
  260. {
  261. return $this->hasOne(OrderPrescriptionDetails::class,['order_id' => 'id']);
  262. }
  263. /**
  264. * 畅捷通请求
  265. *
  266. * @return \yii\db\ActiveQuery
  267. */
  268. public function getAddonsChanjetRequest()
  269. {
  270. return $this
  271. ->hasOne(AddonsChanjetRequest::class, ['table_id' => 'id'])
  272. ->orderBy('updated_at desc')
  273. ->select('id, table_id, request_status, updated_at')
  274. ->where([
  275. 'table' => self::tableName(),
  276. 'path' => [Request::PATHS['SALE_DELIVERY_CREATE_BATCH']]
  277. ])
  278. ->andWhere(['>', 'request_status', 0]);
  279. }
  280. /**
  281. * @Author bing
  282. * @DateTime 2021-09-14 11:24:22
  283. * @copyright: Copyright (c) 2020 广东七件事集团
  284. * @param [type] $insert
  285. * @return void
  286. */
  287. public function beforeSave($insert)
  288. {
  289. // 未支付/未付款
  290. if ($this->isNewRecord || $this->order_status == OrderStatusEnum::NOT_PAY) {
  291. // 支付金额为产品金额
  292. if (isset($this->goods_price)) {
  293. $this->pay_money = $this->goods_price;
  294. }
  295. // 实付增加运费
  296. if (isset($this->pay_money) && isset($this->shipping_money)) {
  297. $this->pay_money += $this->shipping_money - $this->reduce_shipping_money;
  298. }
  299. // 随机减金额计算,因为未支付前无法更改实付金额,所以只能在此处进行修改
  300. if (MallAddons::isInstall($this->mall_id, 'RandFee')) {
  301. $this->pay_money && $this->pay_money = RandFeeService::getRandFee($this->pay_money, $this->order_no);
  302. if(MallAddons::isInstall($this->mall_id,'Store')){
  303. $this->pay_money && StoreOrder::updateAll(['pay_money'=>$this->pay_money,'extra_info'=>$this->extra_info],['order_no'=> $this->order_no]);
  304. }
  305. }
  306. }
  307. // var_dump($this->id, $this->isNewRecord, $this->order_status, $this->oldAttributes['order_status']);
  308. // 修改订单商品状态
  309. if (!$this->isNewRecord && $this->order_status != $this->oldAttributes['order_status']) {
  310. OrderDetail::updateAll(
  311. ['order_status' => $this->order_status],
  312. [ 'order_status' => $this->oldAttributes['order_status'],'order_id' => $this->id]
  313. );
  314. }
  315. // self::orderStastics($orderStatis);
  316. return parent::beforeSave($insert);
  317. }
  318. public static function generateOrderNo($prefix = ''){
  319. // 优化唯一性保证:使用random_int()代替rand(),并结合微调以生成更唯一的ID
  320. $randInt = random_int(100000, 999999);
  321. // 日期时间戳处理
  322. $dateTimeStr = date('YmdHis');
  323. // 保证前缀不为空字符串且不超过一定长度(例如20个字符)
  324. $prefix = trim($prefix, '0');
  325. if (strlen($prefix) > 20) {
  326. $prefix = substr($prefix, 0, 20);
  327. }
  328. // 生成订单号,保证总长度为17(前缀+日期时间戳+随机数)
  329. $orderNo = $prefix . $dateTimeStr . str_pad($randInt, 6, '0', STR_PAD_LEFT);
  330. return $orderNo;
  331. }
  332. public static function createOrder( $form){
  333. $stock_locks = [];
  334. try{
  335. if (self::isStopOrder($form['mall_id'])) throw new Exception('站点维护中,暂时无法下单');
  336. $group_order_goods = $form['group_order_goods'];
  337. $trans = Yii::$app->db->beginTransaction();
  338. $attr = [];
  339. $order_trade_info = [];
  340. $order_ids = [];
  341. $tmp_goods_ids = [];
  342. foreach($group_order_goods as $mch_order){
  343. $mch_order_info = $mch_order['mch_order_info'];
  344. $mch_order_info['mch_id'] = intval($mch_order_info['mch_id']);
  345. $order = new Order();
  346. $order->loadDefaultValues();
  347. $order->load($mch_order_info,'');
  348. $order->order_status = OrderStatusEnum::NOT_PAY;
  349. empty($order->order_no) && $order->order_no = self::generateOrderNo('s');
  350. $res = $order->save();
  351. if($res === false) throw new Exception($order->getErrorMessage());
  352. $order_id = $order->id;
  353. $order_ids[] = $order->id;
  354. //$region_statistics[] = ['mall_id' => $order->mall_id, 'province_id' => $order->province, 'city_id' => $order->city, 'order' => $order->pay_money];
  355. // 总商品库存维护
  356. $goods_stock_arr = [];
  357. // 积分扣除数据
  358. $wallet_deduct_score = [
  359. 'mall_id' => $mch_order_info['mall_id'],
  360. 'user_id' => $mch_order_info['user_id'],
  361. 'is_frozen' => false,
  362. 'wallet_type' => 'score',
  363. 'source_table' => 'order',
  364. 'money' => 0,
  365. 'source_table_id' => $order_id,
  366. 'from_type' => UserWalletEnum::CONSUME,
  367. 'capital_type' => UserWalletEnum::CONSUMPTION_ORDER,
  368. ];
  369. // 遍历商户商品
  370. $goods_ids = array_keys($mch_order['goods']);
  371. array_push($tmp_goods_ids, ...$goods_ids);
  372. foreach($mch_order['goods'] as $goods_id =>$goods){
  373. !isset($goods_name) && $goods_name = $goods['goods_name'];
  374. foreach($goods['goods_details'] as $goods_detail){
  375. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_GOODS_ATTR , $goods_detail['goods_attr_id']);
  376. $identification = uniqid();
  377. if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new Exception('当前访问人数过多');
  378. $stock_locks[$lock_key] = $identification;
  379. //操作商品库存-减
  380. GoodsAttr::handleStock($goods_detail['goods_attr_id'],$goods_detail['num']*-1);
  381. if(!isset($goods_stock_arr[$goods_id])){
  382. $goods_stock_arr[$goods_id] = $goods_detail['num'];
  383. }else{
  384. $goods_stock_arr[$goods_id] += $goods_detail['num'];
  385. }
  386. // 订单详情
  387. $order_detail = new OrderDetail();
  388. $order_detail->loadDefaultValues();
  389. $goods_detail['mch_id'] = intval($goods_detail['mch_id']);
  390. $order_detail->load($goods_detail,'');
  391. $order_detail->order_id = $order_id;
  392. // $goods_statistics[] = ['goods_id' => $goods_detail['goods_id'], 'order_num' => 1, 'sales_num' => $goods_detail['num']];
  393. $res = $order_detail->save();
  394. if($res === false) throw new Exception($order_detail->getErrorMessage());
  395. $attr[$goods_detail['goods_attr_id']] = $goods_detail['num'];
  396. $goods_detail['marketing'] = $goods_detail['marketing'] ?? [];
  397. $goods_detail['marketing'] = OrderMarketing::handleGiveScore($order_detail->mall_id, $goods_detail['marketing'], $order_detail->goods_price - $order_detail->cost_price + $order_detail->adjust_money, $order_detail->num, $order_detail->mch_id);
  398. // 订单营销
  399. if (!empty($goods_detail['marketing'])) {
  400. $order_marketing = new OrderMarketing();
  401. $order_marketing->loadDefaultValues();
  402. $order_marketing->load($goods_detail['marketing'],'');
  403. $order_marketing->order_detail_id = $order_detail->id;
  404. $res = $order_marketing->save();
  405. if($res === false) throw new Exception($order_marketing->getErrorMessage());
  406. }
  407. // 积分扣除
  408. if (isset($goods_detail['marketing']['deduct_score']) && ($deduct_score = $goods_detail['marketing']['deduct_score'])) {
  409. $wallet_deduct_score['money'] = bcmul(bcadd(abs($wallet_deduct_score['money']), $deduct_score, 2), -1, 2);
  410. $wallet_deduct_score['desc'] = sprintf(
  411. '用户(%s),下单(%s)抵扣积分,数量:%s',
  412. $wallet_deduct_score['user_id'],
  413. $wallet_deduct_score['source_table_id'],
  414. abs($wallet_deduct_score['money'])
  415. );
  416. }
  417. }
  418. }
  419. // 存在扣除积分
  420. if ($wallet_deduct_score['money']) UserWalletService::handle(0, $wallet_deduct_score, false);
  421. $form_data = self::findFormDataByKey($form, 'data');
  422. $data = is_array($form_data)?$form_data:Json::decode($form_data,true);
  423. if(!empty($data['user_coupon_ids'])){
  424. $user_coupon_id = $data['user_coupon_ids'];
  425. $res = AddonsCouponUserRelate::updateAll(['is_use'=>1, 'updated_at'=>time()], ['id' => $user_coupon_id]);
  426. if($res === false) throw new Exception($order->getErrorMessage());
  427. }
  428. $order_trade_info[] = [
  429. 'order_id' => $order->id,
  430. 'order_type' => Order::DEFAULT,
  431. 'order_no' => $order->order_no,
  432. 'amount' => $order->goods_price + ($order->shipping_money - $order->reduce_shipping_money),
  433. 'title' => Order::getOrderTitle($order),
  434. 'notify_class' => 'common\logic\notify\Notify',
  435. ];
  436. // 处方商品,记录处方药登记信息
  437. if ($order['goods_subtype'] == GoodsTypeEnum::GOODS_SUBTYPE_PRESCRIPTION) {
  438. OrderPrescriptionDetails::setData([
  439. 'user_id' => $form->user_id,
  440. 'mall_id' => $form->mall_id,
  441. 'order_id' => $order_id,
  442. 'symptom' => $form['data']['symptom'] ?? [],
  443. 'medication_recipient' => $form->process_extra_data['medication_recipient'] ?? [],
  444. ]);
  445. }
  446. }
  447. $form_data = self::findFormDataByKey($form, 'data');
  448. $data = is_array($form_data)?$form_data:Json::decode($form_data,true);
  449. if(isset($data['is_agree']) && !empty($data['is_agree'])){
  450. UserSignAgreement::setData([
  451. 'mall_id' => self::findFormDataByKey($form, 'mall_id'),
  452. 'user_id' => self::findFormDataByKey($form, 'user_id'),
  453. 'source' => UserSignAgreement::SOURCE_GOODS,
  454. 'source_id' => json_encode($tmp_goods_ids),
  455. ]);
  456. }
  457. if ($order->order_source === OrderSourceEnum::SOURCE_RESERVE) {
  458. // 预约类型的订单,单独保存信息
  459. $res = OrderReserve::create($order, $form);
  460. if ($res == false) throw new Exception(OrderReserve::getStaticError());
  461. }
  462. //
  463. $trans->commit();
  464. LockHelper::batchUlock($stock_locks);
  465. //生成支付流水
  466. $res = \Yii::$app->services->pay->createTrade($form['mall_id'],$form['user_id'],$order_trade_info);
  467. if($res==false) throw new Exception('生成支付流水失败');
  468. Order::updateAll(['out_trade_no'=>$res['trade_no']],['id'=>$order_ids]);
  469. // 触发订单添加事件,事务提交完成后触发
  470. $event = new OrderCreateEvent($form['mall_id']);
  471. $form['data']['share_user_id'] = $form['data']['share_user_id']??0;
  472. Yii::$app->services->events->dispatch($event, ['order_ids' => $order_ids,'share_user_id'=>$form['data']['share_user_id']], $event->listeners);
  473. //删除购物车
  474. if (self::findFormDataByKey($form, 'type') == 'cart' && empty($form->process_extra_data['is_not_del_cart'])) Cart::decreaseNumb(['user_id' => self::findFormDataByKey($form, 'user_id'), 'list' => $attr]);
  475. $res['order_ids'] = implode(',', $order_ids);
  476. $arr['transaction'] = $res;
  477. $arr['cart_list'] = $attr;//社安消防插件--该方法的删除购物车无法删除数据,返回购物车数据回插件方法,进行删除
  478. $trade=PaymentTrade::getOne([['out_trade_no'=>$res['trade_no']]]);
  479. $trade->limit_payment='';
  480. //限制支付方式
  481. foreach ($form['sku'] as $key => $oldGoods) {
  482. $ogood=$oldGoods['goods'];
  483. if($ogood['is_pay_limit'])
  484. {
  485. $trade->limit_payment=$ogood['pay_limit'];
  486. continue;
  487. }
  488. }
  489. $trade->save();
  490. // 有可能需要跳转到指定页面
  491. if (count($tmp_goods_ids) == 1 && MallAddons::isInstall($order->mall_id, AddonsEnum::ADDONS_NAME_PAY_SUCCESS_JUMP)) {
  492. // 判断是否有开启
  493. $jump_url_config = \Yii::$app->services->setting->addons->get($order->mall_id,
  494. AddonsEnum::ADDONS_NAME_PAY_SUCCESS_JUMP, 'basic');
  495. // 两个都开启的情况下
  496. if (intval($jump_url_config['is_enable_jump']) && intval($jump_url_config['is_enable'])) {
  497. $jump_url = (new JumpUrlService())->getJumpUrl($order->mall_id, $tmp_goods_ids[0], '');
  498. if (!empty($jump_url)) {
  499. $arr['pay_success_page'] = Json::encode(['route' => $jump_url, 'open_type' => 'page', 'params' => []]);
  500. }
  501. }
  502. }
  503. // 判断是否有安装抽奖插件
  504. if (MallAddons::isInstall($order->mall_id, AddonsEnum::ADDONS_NAME_SHOPPING_LOTTERY)) {
  505. $pay_success_page = LotteryHelper::getJumpUrl($order->mall_id, $res['trade_no']);
  506. if (!empty($pay_success_page)) {
  507. $arr['pay_success_page'] = $pay_success_page;
  508. $arr['is_jump'] = StatusEnum::ENABLED;
  509. }
  510. }
  511. if (empty($arr['pay_success_page'])) {
  512. $arr['pay_success_page'] = Yii::$app->services->setting->mall->get($form['mall_id'], 'order.pay_after_link');// 支付成功后跳转的地址
  513. }
  514. $payment_expire_time = Yii::$app->services->setting->mall->get($form['mall_id'], 'order.payment_expire_time');
  515. $arr['surplus_second'] = 30*60;//剩余秒数
  516. if($payment_expire_time){
  517. $arr['surplus_second'] = intval($payment_expire_time) * 60;//剩余秒数
  518. }
  519. return $arr;
  520. }catch(Exception $e){
  521. LockHelper::batchUlock($stock_locks);
  522. if(isset($trans)) $trans->rollback();
  523. self::$static_error = $e->getMessage();
  524. return false;
  525. }
  526. }
  527. private static function findFormDataByKey($form, $key)
  528. {
  529. if (is_array($form)) return $form[$key] ?? null;
  530. return $form->{$key} ?? null;
  531. }
  532. /**
  533. * 订单改价
  534. * @Author bing
  535. * @DateTime 2021-09-10 19:10:45
  536. * @copyright: Copyright (c) 2020 广东七件事集团
  537. * @param [type] $params
  538. * @param [type] $peyment_type
  539. * @return void
  540. */
  541. public static function changePrice(array $params){
  542. try{
  543. $model = self::findOne(['mall_id' => $params['mall_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED]]);
  544. if(empty($model)) throw new Exception('订单不存在或已删除');
  545. $model->shipping_money = $params['shipping_money'];
  546. $trans = Yii::$app->db->beginTransaction();
  547. // 修改订单详情调价
  548. $all_goods_price = OrderDetail::priceAdjustment($params['detail']);
  549. if($all_goods_price === false) throw new Exception('调整订单商品价格失败:'. $model->getErrorMessage());
  550. $model->goods_price = $all_goods_price;
  551. $res = $model->save();
  552. if($res === false) throw new Exception('修改订单运费失败:'. $model->getErrorMessage());
  553. // 订单金额修复后
  554. self::changeOrderMarketingScore($model->id);
  555. $trans->commit();
  556. return $model;
  557. }catch(Exception $e){
  558. if(isset($trans)) $trans->rollback();
  559. self::$static_error = $e->getMessage();
  560. return false;
  561. }
  562. }
  563. /**
  564. * 更新订单营销积分
  565. * @return void
  566. * @throws Exception
  567. */
  568. public static function changeOrderMarketingScore(int $order_id)
  569. {
  570. try {
  571. $orderInfo = self::find()
  572. ->select('id, order_no, user_id')
  573. ->where(['status' => StatusEnum::ENABLED, 'id' => $order_id])
  574. ->with(['detail' => function($query){
  575. $query->select('id, goods_id, order_id, original_goods_price, goods_price, adjust_money');
  576. }
  577. ])->asArray()->one();
  578. foreach ($orderInfo['detail'] as $item) {
  579. // 查询当前订单是否生成营销积分
  580. $marketing_score = OrderMarketing::find()
  581. ->where(['order_detail_id' => $item['id'], 'status' => StatusEnum::ENABLED])
  582. ->asArray()
  583. ->one();
  584. if (empty($marketing_score)) continue; // 没有生成营销积分
  585. $scoreNum = self::getGoodsGiveScore($item['goods_id'], $item);
  586. if (!$scoreNum) continue; // 没有设置积分赠送规则
  587. $res = OrderMarketing::updateAll(['give_score' => $scoreNum], ['id' => $marketing_score['id']]);
  588. if (!$res) {
  589. throw new Exception('修改订单营销积分失败');
  590. }
  591. }
  592. } catch (Exception $e) {
  593. Yii::$app->custom->logs("修改订单营销积分失败:" . $e->getMessage(), $order_id);
  594. }
  595. }
  596. public static function getGoodsGiveScore($goods_id, $orderDetail)
  597. {
  598. $goodsExtra = GoodsExtra::find()->where(['status' => StatusEnum::ENABLED, 'goods_id' => $goods_id])
  599. ->select('id, goods_id, score_give_setting')
  600. ->one();
  601. if (!$goodsExtra) return false;
  602. $give_setting = Json::decode($goodsExtra['score_give_setting'], true);
  603. if (empty($give_setting['enable_give_score'])) return false;
  604. if (!empty($give_setting['give_type']) && $give_setting['give_type'] == 2) {
  605. if (!empty($give_setting['calculation_value']) && $give_setting['calculation_value'] == 1) {
  606. // 订单支付金额
  607. $goods_price = $orderDetail['goods_price'] + $orderDetail['adjust_money']; // 订单金额等于原来总金额加上调整金额
  608. $money = bcmul($goods_price, $give_setting['give_num'] / 100, 2);
  609. } else {
  610. // 订单商品总价
  611. $money = bcmul($orderDetail['original_goods_price'], $give_setting['give_num'] / 100, 2);
  612. }
  613. } else { // 固定赠送值
  614. $money = $give_setting['give_num'] ?? 0;
  615. }
  616. return $money ?? 0;
  617. }
  618. /**
  619. * 管理员订单支付
  620. * @Author bing
  621. * @DateTime 2021-09-10 10:43:57
  622. * @copyright: Copyright (c) 2020 广东七件事集团
  623. * @param Order $params 参数
  624. * @param [type] $payment_type 支付方式
  625. * @param string 支付流水号
  626. * @return boolean|object
  627. */
  628. public static function pay($params, $peyment_type,$out_trade_no = '')
  629. {
  630. $stock_locks = [];
  631. try{
  632. // $model = self::findOne(['id' => $params['id'],'status'=>[StatusEnum::ENABLED]]);
  633. $model = self::findOne(['id' => $params['id']]);
  634. if(empty($model)) throw new Exception('订单不存在或已删除');
  635. // if ($model->order_status != OrderStatusEnum::NOT_PAY) throw new Exception('订单已经被处理,无法进行支付');
  636. if (in_array($model->order_status ,[OrderStatusEnum::PAY,OrderStatusEnum::SHIPMENTS,OrderStatusEnum::ACCOMPLISH])) throw new Exception('订单已经被处理,无法进行支付');
  637. if ($model->pay_status == StatusEnum::ENABLED) throw new Exception('订单已支付,请不要重复支付');
  638. $order_status = $model->order_status;
  639. $model->order_status = OrderStatusEnum::PAY;
  640. if ($model->order_source == 'Aiyunuo' && $model->shipping_type == ShippingTypeEnum::PICKUP) {
  641. $model->order_status = OrderStatusEnum::TAKE;
  642. }
  643. if ($model->shipping_type == ShippingTypeEnum::PICKUP) {
  644. if (MallAddons::isInstall($model->mall_id, AddonsEnum::ADDONS_NAME_YUNMENG)) {
  645. //查找订单是否在云盟店订单
  646. $yunmeng_order = \addons\Yunmeng\common\models\YunmengOrder::getOne([
  647. ['mall_id' => $model->mall_id],
  648. ['order_id' => $model->id],
  649. ]);
  650. if (!empty($yunmeng_order)) $model->order_status = OrderStatusEnum::TAKE;
  651. }
  652. }
  653. $model->payment_type = $peyment_type;
  654. $model->pay_status = StatusEnum::ENABLED;
  655. $model->pay_time = time();
  656. $model->out_trade_no = $out_trade_no ?? '';
  657. $trans = Yii::$app->db->beginTransaction();
  658. if ($model->order_source === OrderSourceEnum::SOURCE_RESERVE) {
  659. // 平台订单
  660. $reserveOrder = OrderReserve::findOne(['order_id' => $model->id]);
  661. if ($reserveOrder->reserve_type == StatusEnum::DISABLED) { // 家政才需要修改为发货状态
  662. // 预约商品支付后,改成发货状态
  663. $model->shipping_status = StatusEnum::ENABLED; // 全部发货状态改为已发货
  664. // 如果是技师,那么就为已支付
  665. $model->order_status = OrderStatusEnum::SHIPMENTS;
  666. $model->consign_time = time();
  667. }
  668. OrderReserve::syncOrderStatus($model);
  669. }
  670. $res = $model->save();
  671. if($res === false) throw new Exception('修改订单失败:'. $model->getErrorMessage());
  672. // 增加销量
  673. $order_detail = $model->detail;
  674. $sale_num_goods = [];
  675. foreach($order_detail as $product){
  676. if($order_status==OrderStatusEnum::REPEAL) {
  677. if ($model->order_source !== OrderSourceEnum::SOURCE_RESERVE) {
  678. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_GOODS_ATTR, $product['goods_attr_id']);
  679. $identification = uniqid();
  680. if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new Exception('当前访问人数过多');
  681. $stock_locks[$lock_key] = $identification;
  682. //操作商品库存-减
  683. GoodsAttr::handleStock($product['goods_attr_id'], $product['num'] * -1);
  684. $stock = GoodsAttr::find()->where(['goods_id' => $product['goods_id'], 'status' => StatusEnum::ENABLED])->sum('stock');
  685. $stock = $stock ?? 0;
  686. Goods::updateAll(['stock' => $stock], ['id' => $product['goods_id']]);
  687. }
  688. }
  689. if(isset($sale_num_goods[$product['goods_id']])){
  690. $sale_num_goods[$product['goods_id']] += $product['num'];
  691. }else{
  692. $sale_num_goods[$product['goods_id']] = $product['num'];
  693. }
  694. }
  695. foreach($sale_num_goods as $goods_id => $sale_num){
  696. $goods = Goods::findOne($goods_id);
  697. $goods->sales_num += $sale_num;
  698. $goods->save();
  699. }
  700. $trans->commit();
  701. LockHelper::batchUlock($stock_locks);
  702. // 触发订单支付事件,事务提交完成后触发
  703. if(!empty($model['mch_id'])){
  704. $number = 0;
  705. $addons_arr = [AddonsEnum::ADDONS_NAME_AGENT,AddonsEnum::ADDONS_NAME_DISTRIBUTION,AddonsEnum::ADDONS_NAME_AREA, AddonsEnum::ADDONS_NAME_BOSS];
  706. foreach ($addons_arr as $addons){
  707. if(MallAddons::isInstall($model['mall_id'],$addons)){
  708. $number++;
  709. }
  710. }
  711. //多商户事件触发
  712. \Yii::$app->services->taskCounter->set('mch:order_id:' . $model['id'])->incr($number);
  713. if (MallAddons::isInstall($model['mall_id'], AddonsEnum::ADDONS_NAME_MCH)){
  714. $data = [];
  715. $event = new \addons\Mch\common\events\order\OrderPaidEvent($model['mall_id']);
  716. $data['id'] = $model['id'];
  717. $data['operator_id'] = $params['operator_id'] ?? 0;
  718. $data['operator_name'] = $params['operator_name'] ?? '';
  719. \Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  720. }
  721. }else{
  722. $number = 0;
  723. $addons_arr = [AddonsEnum::ADDONS_NAME_AGENT,AddonsEnum::ADDONS_NAME_DISTRIBUTION,AddonsEnum::ADDONS_NAME_AREA,AddonsEnum::ADDONS_NAME_DOUBLE_COPY];
  724. foreach ($addons_arr as $addons){
  725. if(MallAddons::isInstall($model['mall_id'],$addons)){
  726. $number++;
  727. }
  728. }
  729. //云库存商品-先等经销、分销、区域分佣完
  730. \Yii::$app->services->taskCounter->set($model['id'])->incr($number);
  731. $event = new OrderPaidEvent($model['mall_id']);
  732. $order['id'] = $model['id'];
  733. $order['operator_id'] = $params['operator_id'] ?? 0;
  734. $order['operator_name'] = $params['operator_name'] ?? '';
  735. $order['is_admin'] = (int)!empty($params['is_admin']);
  736. Yii::$app->services->events->dispatch($event, $order, $event->listeners);
  737. }
  738. return $model;
  739. }catch(Exception $e){
  740. LockHelper::batchUlock($stock_locks);
  741. if(isset($trans)) $trans->rollback();
  742. self::$static_error = $e->getMessage();
  743. return false;
  744. }
  745. }
  746. /**
  747. * 取消订单
  748. * @Author bing
  749. * @DateTime 2021-09-14 15:43:18
  750. * @copyright: Copyright (c) 2020 广东七件事集团
  751. * @param [type] $params
  752. * @return order
  753. */
  754. public static function close($params,$force = false){
  755. $trans = Yii::$app->db->beginTransaction();
  756. $stock_locks = [];
  757. $close_lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_GOODS , $params['id']);
  758. try{
  759. // 加锁
  760. $close_identification = uniqid();
  761. $stock_locks[$close_lock_key] = $close_identification;
  762. if (!LockHelper::lock($close_lock_key, $close_identification, 30, 60)) throw new \Exception('正在关闭订单');
  763. $cond = ['mall_id' => $params['mall_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED]];
  764. isset($params['user_id']) && $cond['user_id'] = $params['user_id'];
  765. $model = self::findOne($cond);
  766. if(empty($model)) throw new Exception('订单不存在或已删除');
  767. if($model['order_source']=='Aiyunuo') {
  768. $extra_info = $model['extra_info'];
  769. if(!empty($extra_info)){
  770. $extra_info = json_decode($extra_info,true);
  771. if(!empty($extra_info['aiyunuo_rp_order_rp_id'])){
  772. throw new Exception('爱优诺处方订单不允许取消');
  773. }
  774. }
  775. }
  776. // 判断订单是否已经被关闭
  777. if ($model->order_status == OrderStatusEnum::REPEAL) throw new Exception('订单已经关闭');
  778. // 判断是否是未支付的订单
  779. if ($force == false && $model->order_status != OrderStatusEnum::NOT_PAY) throw new Exception('订单已经被处理');
  780. $model->close_time = time();
  781. $model->order_status = OrderStatusEnum::REPEAL;
  782. if (!$model->save()) throw new Exception($model->getErrorMessage());
  783. $goods_attr_arr = array_column($model->detail,'num','goods_attr_id');
  784. //操作规格库存-回退
  785. if ($model->order_source !== OrderSourceEnum::SOURCE_RESERVE) {
  786. // 非预约商品处理库存
  787. foreach ($goods_attr_arr as $goods_attr_id => $num) {
  788. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_GOODS_ATTR, $goods_attr_id);
  789. $identification = uniqid();
  790. if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new Exception('当前访问人数过多');
  791. $stock_locks[$lock_key] = $identification;
  792. //操作商品库存-回退
  793. GoodsAttr::handleStock($goods_attr_id, $num);
  794. }
  795. }
  796. // 订单营销扣减返还
  797. $order_detail_id_arr = array_column($model->detail, 'id');
  798. if (empty($model['store_id'])) {
  799. $res = OrderMarketing::deductBack($model->id, $order_detail_id_arr, $order_detail_id_arr, $model->mall_id, $model->user_id, '交易关闭返还');
  800. if ($res == false) throw new Exception(OrderMarketing::getStaticError());
  801. }
  802. // liuzhantao,这里注释的原因是:订单支付用积分抵扣,那么取消订单时,就会返回两次积分,这里的代码返回一次积分,下面监听的代码也返回一次积分,所以把这里的注释了。
  803. // else {
  804. // if (MallAddons::isInstall($model->mall_id, AddonsEnum::ADDONS_NAME_STORE)) {
  805. // $store_order_detail_list = StoreOrderDetail::lists(['where' => [['main_order_detail_id' => $order_detail_id_arr]]]);
  806. // $store_order_detail_id_arr = array_column($store_order_detail_list, 'id');
  807. // $store_order = StoreOrder::findOne(['order_no' => $model['order_no']]);
  808. // $res = StoreOrderMarketing::deductBack($store_order, $store_order_detail_id_arr, $store_order_detail_id_arr, $model['mall_id'], $model['user_id'], '交易关闭返还');
  809. // if ($res == false) throw new Exception(StoreOrderMarketing::getStaticError());
  810. // }
  811. // }
  812. if ($model->order_source === OrderSourceEnum::SOURCE_RESERVE) {
  813. // 同步订单状态
  814. OrderReserve::syncOrderStatus($model);
  815. }
  816. $trans->commit();
  817. // 触发订单取消事件
  818. if (!empty($model['store_id'])) {
  819. if (($model->order_source == AddonsEnum::ADDONS_NAME_STORE || $model->order_source == AddonsEnum::ADDONS_NAME_RESERVERE) && MallAddons::isInstall($model->mall_id, AddonsEnum::ADDONS_NAME_STORE)) {
  820. $event = new \addons\Store\common\events\order\OrderCloseEvent($model->mall_id);
  821. $order_data['id'] = $model->id;
  822. $order_data['operator_id'] = $params['operator_id'] ?? 0;
  823. $order_data['operator_name'] = $params['operator_name'] ?? '系统';
  824. $order_data['is_store'] = 1;
  825. Yii::$app->services->events->dispatch($event, $order_data, $event->listeners);
  826. } elseif ($model->marketing_type == AddonsEnum::ADDONS_NAME_LAO_DA_FU) {
  827. $event = new \addons\LaoDaFu\common\events\order\OrderCloseEvent($model->mall_id);
  828. $order_data['id'] = $model->id;
  829. $order_data['operator_id'] = $params['operator_id'] ?? 0;
  830. $order_data['operator_name'] = $params['operator_name'] ?? '系统';
  831. $order_data['is_store'] = 1;
  832. Yii::$app->services->events->dispatch($event, $order_data, $event->listeners);
  833. }
  834. } else {
  835. $event = new OrderCloseEvent($model->mall_id);
  836. $order_data['id'] = $model->id;
  837. $order_data['operator_id'] = $params['operator_id'] ?? 0;
  838. $order_data['operator_name'] = $params['operator_name'] ?? '系统';
  839. $order_data['is_admin'] = (int)!empty($params['is_admin']);
  840. Yii::$app->services->events->dispatch($event, $order_data, $event->listeners);
  841. }
  842. // 解锁
  843. LockHelper::batchUlock($stock_locks);
  844. return $model;
  845. } catch (Exception $e) {
  846. $trans->rollback();
  847. // 解锁
  848. LockHelper::batchUlock($stock_locks);
  849. self::$static_error = $e->getMessage();
  850. return false;
  851. }
  852. }
  853. /**
  854. * 订单备注
  855. * @Author bing
  856. * @DateTime 2021-09-14 15:43:18
  857. * @copyright: Copyright (c) 2020 广东七件事集团
  858. * @param [type] $params
  859. * @return void
  860. */
  861. public static function remark($params){
  862. try{
  863. $where = ['mall_id' => $params['mall_id'], 'id' => $params['id']];
  864. if(!empty($params['mch_id']))$where['mch_id'] = $params['mch_id'];
  865. $model = self::findOne($where);
  866. if(empty($model)) throw new Exception('订单不存在或已删除');
  867. $model->remark = $params['remark'] ?? $model->remark;
  868. $model->seller_remark = $params['seller_remark'] ?? $model->seller_remark;
  869. if (!$model->save()) throw new Exception($model->getErrorMessage());
  870. return $model;
  871. }catch(Exception $e){
  872. if(isset($trans)) $trans->rollback();
  873. self::$static_error = $e->getMessage();
  874. return false;
  875. }
  876. }
  877. /**
  878. * @desc:删除订单
  879. * @Author: hua
  880. * @Date: 2021/12/3
  881. * @Time: 19:05
  882. * @Copyright: copyright (c) 2021 广东七件事集团
  883. * @param $params
  884. * @return Order|false
  885. */
  886. public static function del($params){
  887. try{
  888. $where = ['mall_id' => $params['mall_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED, StatusEnum::DISABLED]];
  889. if(!empty($params['mch_id']))$where['mch_id'] = $params['mch_id'];
  890. $model = self::findOne($where);
  891. if(empty($model)) throw new Exception('订单不存在或已删除');
  892. if(!empty($model->pay_time))throw new Exception('只允许删除未支付订单');
  893. if($model->order_status!=OrderStatusEnum::REPEAL)throw new Exception('只允许删除关闭订单');
  894. $model->status = StatusEnum::DELETE;
  895. if (!$model->save()) throw new Exception($model->getErrorMessage());
  896. OrderDetail::updateAll(['status'=>StatusEnum::DELETE],['order_id'=>$params['id']]);
  897. $event = new OrderDelEvent($model->mall_id);
  898. $order_data['id'] = $params['id'];
  899. $order_data['order_no'] = $model->order_no;
  900. $order_data['operator_id'] = $params['operator_id'] ?? 0;
  901. $order_data['operator_name'] = $params['operator_name'] ?? '系统';
  902. $order_data['is_admin'] = $params['is_admin'] ?? 0;
  903. $order_data['is_store'] = $params['is_store'] ?? 0;
  904. $order_data['is_mch'] = $params['is_mch'] ?? 0;
  905. $order_data['is_aiyunuo'] = $params['is_aiyunuo'] ?? 0;
  906. Yii::$app->services->events->dispatch($event,$order_data,$event->listeners);
  907. return $model;
  908. }catch(Exception $e){
  909. if(isset($trans)) $trans->rollback();
  910. self::$static_error = $e->getMessage();
  911. return false;
  912. }
  913. }
  914. public static function delByh5($params){
  915. try{
  916. $where = ['mall_id' => $params['mall_id'],'user_id'=>$params['user_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED]];
  917. if(!empty($params['mch_id']))$where['mch_id'] = $params['mch_id'];
  918. $model = self::findOne($where);
  919. if(empty($model)) throw new Exception('订单不存在或已删除');
  920. if(!in_array($model->order_status,[OrderStatusEnum::REPEAL,OrderStatusEnum::ACCOMPLISH]))throw new Exception('只允许删除关闭订单或者已完成订单');
  921. $model->status = StatusEnum::DISABLED;
  922. if (!$model->save()) throw new Exception($model->getErrorMessage());
  923. OrderDetail::updateAll(['status'=>StatusEnum::DISABLED],['order_id'=>$params['id']]);
  924. // 用户删除订单
  925. OrderAction::create("用户删除订单", $model->id, $model->order_status, $params['user_id'], $model->user->nickname);
  926. return $model;
  927. }catch(Exception $e){
  928. if(isset($trans)) $trans->rollback();
  929. self::$static_error = $e->getMessage();
  930. return false;
  931. }
  932. }
  933. /**
  934. * 修改收货地址
  935. * @Author bing
  936. * @DateTime 2021-09-14 15:43:18
  937. * @copyright: Copyright (c) 2020 广东七件事集团
  938. * @param [type] $params
  939. * @return void
  940. */
  941. public static function changeAddress($params){
  942. try{
  943. $where = ['mall_id' => $params['mall_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED]];
  944. if(!empty($params['mch_id']))$where['mch_id'] = $params['mch_id'];
  945. $model = self::findOne($where);
  946. if(empty($model)) throw new Exception('订单不存在或已删除');
  947. // 记录原地址
  948. $params['old_receiver_name'] = $model['receiver_name'];
  949. $params['old_mobile'] = $model['mobile'];
  950. $params['old_region_name'] = $model['region_name'];
  951. $params['old_address'] = $model['address'];
  952. $params['old_province'] = $model['province'];
  953. $params['old_city'] = $model['city'];
  954. $params['old_area'] = $model['area'];
  955. $params['old_town'] = $model['town'];
  956. $params['old_community'] = $model['community'];
  957. self::isAllowChangeAddress($params['mall_id'], $model->order_source);
  958. if(!$model->load($params,'') || !$model->save()) throw new \Exception($model->getErrorMessage());
  959. // 事件
  960. $event = new OrderChangeAddressEvent($params['mall_id']);
  961. Yii::$app->services->events->dispatch($event,$params,$event->listeners);
  962. return $model;
  963. }catch(Exception $e){
  964. self::$static_error = $e->getMessage();
  965. return false;
  966. }
  967. }
  968. /**
  969. * 只有主商城订单和聚水潭订单允许修改订单地址
  970. *
  971. * @param int $mallId
  972. * @param string $orderSource
  973. *
  974. * @throws Exception
  975. */
  976. public static function isAllowChangeAddress(int $mallId, string $orderSource)
  977. {
  978. $needCheckIsAllowModifyAddressAddons = [
  979. AddonsEnum::ADDONS_NAME_JSTAN_ERP,
  980. AddonsEnum::ADDONS_NAME_QI_JUHE,
  981. AddonsEnum::ADDONS_NAME_JUHE_SHOP,
  982. 'StarChain'
  983. ];
  984. if (!in_array($orderSource, $needCheckIsAllowModifyAddressAddons)) {
  985. return;
  986. }
  987. // 获取插件的配置
  988. $event = new \common\events\mall\GetSpecifyAdsCfgEvent($mallId);
  989. Yii::$app->services->events->dispatch($event, ['mall_id' => $mallId, 'addons_name' => $orderSource], $event->listeners);
  990. if (!empty($event->getConfig()['is_allow_modify_address'])) {
  991. return;
  992. }
  993. throw new \RuntimeException('当前订单不允许修改地址');
  994. }
  995. /**
  996. * 发货
  997. * @Author bing
  998. * @DateTime 2021-09-16 17:35:05
  999. * @copyright: Copyright (c) 2020 广东七件事集团
  1000. * @param [type] $params
  1001. * @return void
  1002. */
  1003. public static function delivery($params){
  1004. try{
  1005. $model = self::findOne(['mall_id' => $params['mall_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED]]);
  1006. if(empty($model)) throw new Exception('订单不存在或已删除');
  1007. if ($model->order_status == OrderStatusEnum::SHIPMENTS) throw new Exception('订单已经发货');
  1008. if ($model->order_status != OrderStatusEnum::PAY) throw new Exception('订单已经被处理');
  1009. $model->shipping_status = StatusEnum::ENABLED;// 全部发货状态改为已发货
  1010. $model->order_status = OrderStatusEnum::SHIPMENTS;
  1011. $model->consign_time = time();
  1012. if (!$model->save()) throw new Exception($model->getErrorMessage());
  1013. return $model;
  1014. }catch(Exception $e){
  1015. self::$static_error = $e->getMessage();
  1016. return false;
  1017. }
  1018. }
  1019. /**
  1020. * 收货
  1021. * @Author bing
  1022. * @DateTime 2021-09-16 17:35:05
  1023. * @copyright: Copyright (c) 2020 广东七件事集团
  1024. * @param [type] $params
  1025. * @return void
  1026. */
  1027. public static function takeDelivery($params){
  1028. try{
  1029. $cond = ['mall_id' => $params['mall_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED]];
  1030. isset($params['user_id']) && $cond['user_id'] = $params['user_id'];
  1031. isset($params['mch_id']) && $cond['mch_id'] = $params['mch_id'];
  1032. $model = self::findOne($cond);
  1033. if (empty($model)) throw new Exception('订单不存在或已删除');
  1034. $actionText = ($params['from'] ?? '') == OrderSourceEnum::SOURCE_RESERVE ? '核销' : '签收';
  1035. if ($model->order_status == OrderStatusEnum::SING) throw new Exception('订单已经' . $actionText);
  1036. if ($model->order_status != OrderStatusEnum::SHIPMENTS) throw new Exception('订单已经被处理');
  1037. $orderDetailRefund = OrderDetail::find()->alias('or')
  1038. ->join('inner join', OrderRefund::tableName() . 'as refund', 'refund.order_detail_id=or.id')
  1039. ->where(['or.order_id' => $params['id'], 'or.is_feedback' => OrderStatusEnum::FEEDBACKING,
  1040. 'or.status' => StatusEnum::ENABLED, 'refund.status' => StatusEnum::ENABLED,
  1041. ])
  1042. ->andWhere(['!=', 'refund.refund_status', RefundStatusEnum::FINISH])
  1043. ->select('or.id, refund.id as refund_id, or.is_feedback, refund.type')
  1044. ->orderBy('refund.type desc')
  1045. ->asArray()->all();
  1046. if (!empty($orderDetailRefund)) {
  1047. $refundTypes = array_column($orderDetailRefund, 'type');
  1048. if (in_array(RefundTypeEnum::EXCHANGE_GOODS, $refundTypes)) {
  1049. $refund_id = array_filter($orderDetailRefund, function ($item) {
  1050. return $item['type'] == RefundTypeEnum::EXCHANGE_GOODS;
  1051. });
  1052. self::$getRefundId = reset($refund_id)['refund_id']; // 获取第一个换货商品id
  1053. throw new Exception('该订单包含换货中商品,请先前往售后详情确认收货。');
  1054. } else if (in_array(RefundTypeEnum::MONEY_AND_PRODUCT, $refundTypes) && !in_array(RefundTypeEnum::MONEY, $refundTypes)) {
  1055. throw new Exception('该订单包含退货退款商品,确认收货后其售后流程将继续进行。');
  1056. } else if (in_array(RefundTypeEnum::MONEY, $refundTypes) && !in_array(RefundTypeEnum::MONEY_AND_PRODUCT, $refundTypes)) {
  1057. throw new Exception('该订单包含退款中商品,确认收货将关闭退款。');
  1058. } else if (in_array(RefundTypeEnum::MONEY_AND_PRODUCT, $refundTypes) && in_array(RefundTypeEnum::MONEY, $refundTypes)) {
  1059. throw new Exception('该订单包含退款中及退货退款中商品,确认收货将关闭退款;退货退款商品售后继续,不受影响。');
  1060. }
  1061. }
  1062. $model->order_status = OrderStatusEnum::SING;
  1063. $model->shipping_status = ShippingStatusEnum::RECEIVED;
  1064. $model->sign_time = time();
  1065. if (!$model->save()) throw new Exception($model->getErrorMessage());
  1066. OrderDetail::updateAll(['shipping_status' => ShippingStatusEnum::RECEIVED], ['order_id' => $model['id'],'is_feedback'=>0]);
  1067. if ($model->order_source === OrderSourceEnum::SOURCE_RESERVE) {
  1068. // 同步订单状态
  1069. OrderReserve::syncOrderStatus($model, $params['tech_id'] ?? null);
  1070. }
  1071. if (!empty($model['store_id'])) {
  1072. if ($model->order_source === AddonsEnum::ADDONS_NAME_STORE) {
  1073. $is_install = MallAddons::isInstall($params['mall_id'], AddonsEnum::ADDONS_NAME_STORE);
  1074. if ($is_install) {
  1075. $store_order = StoreOrder::findOne(['order_no' => $model['order_no']]);
  1076. if ($store_order) {
  1077. $store_order->order_status = OrderStatusEnum::SING;
  1078. $store_order->sign_time = time();
  1079. if (!$store_order->save()) throw new Exception($store_order->getErrorMessage());
  1080. }
  1081. }
  1082. } elseif ($model->marketing_type == AddonsEnum::ADDONS_NAME_LAO_DA_FU) {
  1083. $store_order = \addons\LaoDaFu\common\models\StoreOrder::findOne(['order_no' => $model['order_no']]);
  1084. if ($store_order) {
  1085. $store_order->order_status = OrderStatusEnum::SING;
  1086. $store_order->sign_time = time();
  1087. if (!$store_order->save()) throw new Exception($store_order->getErrorMessage());
  1088. }
  1089. }
  1090. }
  1091. // 触发订单收货事件,事务提交完成后触发
  1092. $event = new OrderTakeDeliveryEvent($params['mall_id']);
  1093. $data['id'] = $model->id;
  1094. $data['operator_id'] = $params['operator_id'];
  1095. $data['operator_name'] = $params['operator_name'];
  1096. $data['is_admin'] = (int)!empty($params['is_admin']);
  1097. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1098. return $model;
  1099. }catch(Exception $e){
  1100. self::$static_error = $e->getMessage();
  1101. return false;
  1102. }
  1103. }
  1104. /**
  1105. * @notes:取消发货
  1106. * @param $params
  1107. * @return bool
  1108. * @author: lun
  1109. * @Time: 2022/10/27 10:21
  1110. */
  1111. public static function cancelShipment($params)
  1112. {
  1113. try {
  1114. $order_details = OrderDetail::find()->where(['id' => $params['order_detail_ids']])->all();
  1115. foreach ($order_details as $detail) {
  1116. if (!$detail->shipped_num) throw new Exception('订单处于非发货状态');
  1117. if (OrderDetail::getAfterSaleCountByOrderId($detail->order_id) > 0) throw new Exception('请先处理或关闭订单售后');
  1118. $detail->order_status = OrderStatusEnum::PAY;
  1119. $detail->shipping_status = StatusEnum::DISABLED;
  1120. if (!$detail->save()) throw new Exception($detail->getErrorMessage());
  1121. OrderDetail::updateAllCounters(['shipped_num' => -min($params['nums'][$detail->id] ?? $detail->shipped_num, $detail->shipped_num)], ['id' => $detail->id]);
  1122. }
  1123. // 统计订单详情数量
  1124. if ($order_details) {
  1125. /*$order = Order::findOne(['id' => $params['order_id']]);
  1126. $order->order_status = OrderStatusEnum::PAY; // 已发货改为待发货状态
  1127. $order->consign_time = 0; // 发货时间改为0
  1128. if (!$order->save()) throw new Exception($order->getErrorMessage());*/
  1129. Yii::$app->db->createCommand()
  1130. ->update(Order::tableName(), ['order_status' => OrderStatusEnum::PAY,'consign_time' => 0,'updated_at' => time()], ['id' => $params['order_id']])
  1131. ->execute();
  1132. }
  1133. return true;
  1134. } catch (Exception $e) {
  1135. self::$static_error = $e->getMessage();
  1136. return false;
  1137. }
  1138. }
  1139. /**
  1140. * 自动维护订单状态
  1141. * @Author bing
  1142. * @DateTime 2021-09-17 11:43:41
  1143. * @copyright: Copyright (c) 2020 广东七件事集团
  1144. * @param [type] $order_id
  1145. * @return void
  1146. */
  1147. public static function autoUpdateStatus($order)
  1148. {
  1149. $order_details = OrderDetail::getOrderDetails([['order_id' => $order->id]]);
  1150. // 正常订单详情数
  1151. $normal_count = count($order_details);
  1152. // 退款状态数
  1153. $refund_count = 0;
  1154. $not_refunding_count = 0;
  1155. $refunding_count = 0;
  1156. $feedback_completion = 0;
  1157. // 已发货状态数
  1158. $shipping_count = 0;
  1159. // 未发货状态数
  1160. $no_shipping_count = 0;
  1161. // 换货状态数
  1162. $exchange_count = 0;
  1163. // 订单完成需要关闭的订单详情数
  1164. $order_detail_close_ids = [];
  1165. foreach ($order_details as $order_detail) {
  1166. $shipping_status = $order_detail['shipping_status'];
  1167. $is_feedback = $order_detail['is_feedback'];
  1168. $shipping_status == StatusEnum::ENABLED && $shipping_count++;
  1169. $is_feedback == OrderStatusEnum::FEEDBACKED && $refund_count++;
  1170. $is_feedback == OrderStatusEnum::NOT_FEEDBACK && $not_refunding_count++;
  1171. $is_feedback == OrderStatusEnum::FEEDBACKING && $refunding_count++;
  1172. $is_feedback == OrderStatusEnum::FEEDBACKED && $feedback_completion++;
  1173. /****判断订单是否售后完成 --start-- ****/
  1174. if ($is_feedback == OrderStatusEnum::FEEDBACKED) {
  1175. // 订单售后完成(判断完成订单中是否存在换货数据)
  1176. $refund_exists = OrderRefund::find()
  1177. ->where(['mall_id' => $order_detail['mall_id'], 'status' => StatusEnum::ENABLED,
  1178. 'order_detail_id' => $order_detail['id'], 'type' => RefundTypeEnum::EXCHANGE_GOODS,
  1179. 'store_id' => 0, 'mch_id' => 0
  1180. ])->exists();
  1181. if ($refund_exists) {
  1182. $exchange_count++;
  1183. } else {
  1184. // 订单完成后订单状态为已关闭(仅为退款、退货退款)
  1185. $order_detail_close_ids[] = $order_detail['id'];
  1186. }
  1187. }
  1188. /****判断订单是否售后完成--end--****/
  1189. // 未发货的正常产品
  1190. if ($shipping_status == StatusEnum::DISABLED && in_array($is_feedback, OrderStatusEnum::getFeedbackMap())) {
  1191. $no_shipping_count++;
  1192. }
  1193. }
  1194. if (!empty($order_detail_close_ids)) {
  1195. // 关闭订单详情
  1196. OrderDetail::updateAll(['order_status' => OrderStatusEnum::REPEAL], ['id' => $order_detail_close_ids]);
  1197. }
  1198. // 全部已退款直接关闭
  1199. if ($normal_count === $refund_count) {
  1200. $order->close_time = time();
  1201. // 只有当退货订单中没有换货订单时,才对主订单进行关闭
  1202. if ($exchange_count == 0) {
  1203. $order->order_status = OrderStatusEnum::REPEAL;
  1204. }
  1205. if (!$order->save()) throw new Exception($order->getErrorMessage());
  1206. // return self::close(['mall_id'=>$order->mall_id,'id'=>$order->id], true);
  1207. }
  1208. //未售后(当售后完成中有拒绝时也更改主订单状态)
  1209. if (!empty($not_refunding_count)) {
  1210. $order->is_feedback = OrderStatusEnum::NOT_FEEDBACK;
  1211. $order->save();
  1212. }
  1213. // 全部售后中
  1214. if ($normal_count === $refunding_count) {
  1215. $order->is_feedback = OrderStatusEnum::FEEDBACKING;
  1216. $order->save();
  1217. }
  1218. // 全部售后完成
  1219. if ($normal_count === $feedback_completion) {
  1220. $order->is_feedback = OrderStatusEnum::FEEDBACKED;
  1221. $order->save();
  1222. }
  1223. if($order->order_source === OrderSourceEnum::SOURCE_RESERVE){
  1224. // 预约订单处理
  1225. try{
  1226. OrderReserve::syncOrderStatus($order);
  1227. }catch(Exception $e){
  1228. throw $e;
  1229. }
  1230. }
  1231. // 全部已发货
  1232. if ($normal_count === $shipping_count) {
  1233. return self::delivery(['mall_id' => $order->mall_id, 'id' => $order->id]);
  1234. }
  1235. // 校验发货状态如果其他货物已发就改变订单状态
  1236. $refund_shipping_count = $refund_count + $shipping_count;
  1237. if ($normal_count == $refund_shipping_count && $order->order_status == OrderStatusEnum::PAY && $no_shipping_count == 0) {
  1238. return self::delivery(['mall_id' => $order->mall_id, 'id' => $order->id]);
  1239. }
  1240. }
  1241. public function export(array $data)
  1242. {
  1243. foreach ($data as &$val) {
  1244. $val['created_at'] = date('Y-m-d H:i:s', $val['created_at']);
  1245. }
  1246. return $data;
  1247. }
  1248. /**
  1249. * 导出字段数组
  1250. * @Author: hua
  1251. * @DateTime: 2021/10/11 18:06
  1252. * @Copyright: copyright (c) 2021 广东七件事集团
  1253. * @return \string[][]
  1254. */
  1255. public static function fieldsLists($field = null)
  1256. {
  1257. $fields = [
  1258. 'o.order_id' => '订单ID',
  1259. 'o.user_id' => '用户ID',
  1260. 'o.order_no' => '订单编号',
  1261. 'o.out_trade_no' => '外部交易号',
  1262. 'o.mobile' => '收货人的手机号码',
  1263. 'o.receiver_name' => '收货人姓名',
  1264. 'o.region_name' => '地区名称(省市区[乡/镇]',
  1265. 'o.address' => '详细地址',
  1266. 'd.goods_name' => '商品名', //detail
  1267. 'd.goods_attr_name' => '规格', //detail
  1268. 'd.num' => '购买数量', //detail
  1269. 'd.cost_price' => '商品成本价', //detail
  1270. 'd.original_goods_price' => '商品原本总价', //detail
  1271. 'd.goods_price' => '商品优惠后总价', //detail
  1272. 'o.shipping_money' => '订单运费',
  1273. 'o.created_at' => '下单时间',
  1274. 'o.payment_type' => '支付方式', //-
  1275. 'o.order_status' => '订单状态', //-
  1276. 'o.pay_status' => '订单付款状态', //-
  1277. 'o.pay_time' => '订单付款时间',
  1278. 'd.shipping_status' => '订单配送状态', //- detail
  1279. 'o.consign_time' => '卖家发货时间', //
  1280. 'o.sign_time' => '买家签收时间',
  1281. 'd.remark' => '用户订单备注', //detail
  1282. 'o.seller_remark' => '商家订单备注',
  1283. 'o.pay_money' => '订单实付金额'
  1284. ];
  1285. if ($field === null) return $fields;
  1286. $temp = [];
  1287. foreach ($field as $val) {
  1288. if (isset($fields[$val])) $temp[$val] = $fields[$val];
  1289. }
  1290. return $temp;
  1291. }
  1292. /**
  1293. * 获取订单标题
  1294. * @param Order $order
  1295. * @return string
  1296. */
  1297. public static function getOrderTitle($order)
  1298. {
  1299. $details = $order->getDetail()->all();
  1300. if (empty($details) || !is_array($details) || count($details) < 1) {
  1301. return '';
  1302. }
  1303. $titles = [];
  1304. foreach ($details as $detail) {
  1305. $titles[] = $detail->goods_name;
  1306. }
  1307. $title = implode(';', $titles);
  1308. return mb_strlen($title) > 32 ? mb_substr($title, 0, 32) : $title;
  1309. }
  1310. /**
  1311. * @desc:去支付
  1312. * @Author: hua
  1313. * @Date: 2021/10/19
  1314. * @Time: 14:13
  1315. * @Copyright: copyright (c) 2021 广东七件事集团
  1316. * @param $params
  1317. * @return array|false|int
  1318. */
  1319. public static function toPay($params)
  1320. {
  1321. try {
  1322. $id = json_decode($params['id'], true);
  1323. $orderUserId = $params['order_user_id'] ?? $params['user_id'];
  1324. $where = ['id' => $id, 'user_id' => $orderUserId, 'mall_id' => $params['mall_id'], 'pay_status' => 0];
  1325. if (!empty($params['mch_id'])) $where['mch_id'] = $params['mch_id'];
  1326. $list = Order::findAll($where);
  1327. $orders = [];
  1328. $payment_expire_time = Yii::$app->services->setting->mall->get($params['mall_id'], 'order.payment_expire_time');
  1329. if (empty($payment_expire_time)) $payment_expire_time = 30;
  1330. $order_nos = [];
  1331. $goods_price = 0; // 最终支付金额
  1332. $store_order_nos = [];
  1333. foreach ($list as $k => $order) {
  1334. self::toPayBeforeCheck($order, $k);
  1335. // 获取插件内容
  1336. $event = new OrderToPayEvent($order['mall_id'], $order);
  1337. Yii::$app->services->events->dispatch($event);
  1338. $orders[] = [
  1339. 'order_id' => $order->id,
  1340. 'order_no' => $order->order_no,
  1341. 'amount' => $order->goods_price + ($order->shipping_money - $order->reduce_shipping_money),
  1342. 'title' => self::getOrderTitle($order),
  1343. 'notify_class' => empty($order['store_id']) ? 'common\logic\notify\Notify' : (!empty($params['notify_class']) ? $params['notify_class'] : 'addons\Store\common\logic\notify\Notify'),
  1344. 'order_type' => Order::DEFAULT,
  1345. ];
  1346. //如果是o2o门店订单,记录订单号
  1347. if (!empty($order['store_id'])) {
  1348. $store_order_nos[] = $order->order_no;
  1349. }
  1350. $order_time = $order['created_at'] + $payment_expire_time * 60;
  1351. $order_ids[] = $order->id;
  1352. $order_nos[] = $order->order_no;
  1353. $goods_price += $order->goods_price + ($order->shipping_money - $order->reduce_shipping_money);
  1354. }
  1355. if(empty($list)) throw new Exception('查无订单');
  1356. // 判断是否已有组合待支付订单
  1357. $group_trade_ids = PaymentTradeOrder::find()->where(['order_no'=>$order_nos])->select('trade_id')->column();
  1358. if(!empty($group_trade_ids)) {
  1359. // 组合支付查询
  1360. /**
  1361. * @var PaymentTrade|null
  1362. */
  1363. $mixed_trade = PaymentTrade::getMixedTradeByIds($group_trade_ids);
  1364. !empty($mixed_trade) && $res = [
  1365. 'trade_id' => $mixed_trade->id,
  1366. 'trade_no' => $mixed_trade->out_trade_no,
  1367. 'pay_fee' => $mixed_trade->getOnlinePayFee(),
  1368. 'is_pay' => $mixed_trade->is_pay,
  1369. 'pay_type' => PayTypeEnum::MIXED,
  1370. ];
  1371. // 选择重新支付
  1372. if (isset($params['re_pay']) && $params['re_pay'] && !empty($mixed_trade)) {
  1373. $mixed_trade->rePay(reset($list), 2);
  1374. $res = null;
  1375. }
  1376. }
  1377. if (empty($res)) {
  1378. //查询上一个订单是否有限制支付方式
  1379. $orderNo=$list[0]->order_no;
  1380. $tradeOrder=PaymentTradeOrder::find()->where(['order_no'=>$orderNo])->orderBy('id DESC')->one();
  1381. $trade=PaymentTrade::find()->where(['id'=>$tradeOrder->trade_id])->one();
  1382. $res = \Yii::$app->services->pay->createTrade($params['mall_id'], $params['user_id'], $orders);
  1383. if ($res == false) throw new Exception('生成支付流水失败');
  1384. Order::updateAll(['out_trade_no'=>$res['trade_no']],['id'=>$order_ids]);
  1385. //如果门店订单号不为空,修改门店订单的外部单号
  1386. if (!empty($store_order_nos)) {
  1387. StoreOrder::updateAll(['out_trade_no'=>$res['trade_no']],['order_no'=>$store_order_nos]);
  1388. }
  1389. //限制支付方式
  1390. if(!empty($trade) && !empty($trade->limit_payment)){
  1391. $newTrade=PaymentTrade::findOne($res['trade_id']);
  1392. $newTrade->limit_payment = $trade->limit_payment;
  1393. $newTrade->save();
  1394. }
  1395. }
  1396. $res['surplus_second'] = 30*60;//剩余秒数
  1397. $res['main_order_id'] = reset($id);
  1398. $res['goods_price'] = $goods_price;//剩余秒数
  1399. if(!empty($order_time)) $res['surplus_second'] = ($order_time - time())>0?$order_time - time():0;
  1400. return $res;
  1401. }catch (Exception $e){
  1402. self::$static_error = $e->getMessage();
  1403. return false;
  1404. }
  1405. }
  1406. /**
  1407. * 检查是否允许支付
  1408. *
  1409. * @param $order
  1410. * @param $k
  1411. *
  1412. * @return void
  1413. * @throws Exception
  1414. */
  1415. protected static function toPayBeforeCheck($order, $k)
  1416. {
  1417. // 查询是否是处方药订单,如果是则需要开方后才能支付
  1418. if ($order['goods_subtype'] == GoodsTypeEnum::GOODS_SUBTYPE_PRESCRIPTION) {
  1419. // 验证是否启用插件
  1420. if (!$k) {
  1421. if (
  1422. !MallAddons::isInstall($order['mall_id'], AddonsEnum::ADDONS_NAME_CLOUD_PRESCRIPTION) ||
  1423. !((new \addons\CloudPrescription\common\services\SettingService(['mall_id' => $order['mall_id']]))->isEnable())
  1424. ) {
  1425. throw new Exception('暂不支持购买处方药');
  1426. }
  1427. }
  1428. if (
  1429. !OrderPrescriptionDetails::find()
  1430. ->where([
  1431. 'mall_id' => $order['mall_id'],
  1432. 'user_id' => $order['user_id'],
  1433. 'status' => StatusEnum::ENABLED,
  1434. 'order_id' => $order['id'],
  1435. 'prescription_status' => OrderPrescriptionDetails::PRESCRIPTION_STATUS_OPENED
  1436. ])
  1437. ->exists()
  1438. ) {
  1439. throw new Exception('开方失败订单不允许支付');
  1440. }
  1441. }
  1442. }
  1443. /**
  1444. * @desc:提醒发货
  1445. * @Author: hua
  1446. * @Date: 2021/10/20
  1447. * @Time: 9:06
  1448. * @Copyright: copyright (c) 2021 广东七件事集团
  1449. * @param $params
  1450. * @return array|false|int
  1451. */
  1452. public static function remindSend($params){
  1453. try {
  1454. $where = ['id'=>$params['order_id'],'user_id'=>$params['user_id'],'mall_id'=>$params['mall_id']];
  1455. if(!empty($params['mch_id'])) $where['mch_id'] = $params['mch_id'];
  1456. $order = self::findOne($where);
  1457. if(empty($order)) throw new Exception('查无订单');
  1458. if($order['pay_status']!=1) throw new Exception('该订单未支付');
  1459. if($order['order_status']!=OrderStatusEnum::PAY) throw new Exception('该订单已发货');
  1460. $key = RedisKeyEnum::suffix(RedisKeyEnum::REMIND_SEND,$params['order_id']);
  1461. $res = \Yii::$app->redis->get($key);
  1462. if($res)throw new Exception('您已提醒过发货,请24小时后再来');
  1463. \Yii::$app->redis->set($key,1,'NX','EX',24*3600);
  1464. //todo 调用发送短信接口
  1465. OrderAction::create('发货提醒',$order['id'], $order['order_status'], $params['user_id'], $params['user']['nickname']??'');
  1466. // 提醒发货事件
  1467. $event = new OrderReminderShipmentEvent($params['mall_id']);
  1468. $data['order_id'] = $params['order_id'];
  1469. $data['operator_id'] = $params['user_id'];
  1470. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1471. return true;
  1472. }catch (Exception $e){
  1473. self::$static_error = $e->getMessage();
  1474. return false;
  1475. }
  1476. }
  1477. /**
  1478. * @desc:提交订单评价
  1479. * @Author: hua
  1480. * @Date: 2021/10/19
  1481. * @Time: 18:28
  1482. * @Copyright: copyright (c) 2021 广东七件事集团
  1483. * @param $params
  1484. * @return bool
  1485. */
  1486. public static function postComments($params){
  1487. $t = Yii::$app->db->beginTransaction();
  1488. try{
  1489. $order = Order::findOne($params['order_id']);
  1490. if (empty($order)) throw new Exception('订单不存在');
  1491. if ($order->order_status == OrderStatusEnum::ACCOMPLISH && !in_array($order['order_source'], [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE])) { // 如果订单已被后台管理员设置为完成。则用户不能再评论
  1492. throw new Exception('订单已完成,不能再评价');
  1493. }
  1494. $goods_ids = array_column($params['comment_data'], 'goods_id');
  1495. if(!empty($order['store_id'])){
  1496. $class = \addons\Store\common\models\StoreGoods::class;
  1497. }else{
  1498. $class = Goods::class;
  1499. }
  1500. $goods_list = $class::lists([
  1501. 'where'=>[
  1502. ['id'=>$goods_ids]
  1503. ],
  1504. 'index'=>'id',
  1505. 'select'=>'id,goods_type'
  1506. ]);
  1507. foreach ($params['comment_data'] as $item) {
  1508. $item['mch_id'] = $order->mch_id;
  1509. $item['store_id'] = $order->store_id;
  1510. $item['mall_id'] = $order->mall_id;
  1511. $item['user_id'] = $order->user_id;
  1512. $item['order_id'] = $params['order_id'];
  1513. if (in_array($order->marketing_type, [AddonsEnum::ADDONS_NAME_LAO_DA_FU])) {
  1514. $item['source'] = $order->marketing_type;
  1515. } else {
  1516. $item['source'] = '';
  1517. }
  1518. $item['goods_type'] = $goods_list[$item['goods_id']]['goods_type']??1;
  1519. $res = OrderComments::setData($item);
  1520. if ($res === false) throw new Exception(OrderComments::getStaticError());
  1521. //注释下面的代码,原因是:不是首评,也能记录数据
  1522. // $is_first_comment = OrderComments::find()->where(['mall_id' => $item['mall_id'], 'store_id' => $item['store_id'], 'mch_id' => $item['mch_id'], 'user_id' => $item['user_id'], 'goods_id' => $item['goods_id']])->count();
  1523. // if ($is_first_comment == 1) { //如果为首评,记录数据
  1524. $order_comments_extra_data = OrderCommentsExtra::find()->where(['mall_id' => $item['mall_id'], 'store_id' => $item['store_id'], 'mch_id' => $item['mch_id'], 'goods_id' => $item['goods_id']])->asArray()->one();
  1525. if (!empty($order_comments_extra_data)) {
  1526. // $order_comments_extra_data['num'] += 1;
  1527. OrderCommentsExtra::updateAllCounters(['num' => 1], ['mall_id' => $item['mall_id'], 'store_id' => $item['store_id'], 'mch_id' => $item['mch_id'], 'goods_id' => $item['goods_id']]);
  1528. } else {
  1529. $order_comments_extra_data = [
  1530. 'mall_id' => $item['mall_id'],
  1531. 'store_id' => $item['store_id'],
  1532. 'mch_id' => $item['mch_id'],
  1533. 'goods_id' => $item['goods_id'],
  1534. 'num' => 1,
  1535. ];
  1536. OrderCommentsExtra::setData($order_comments_extra_data);
  1537. }
  1538. // }
  1539. if (in_array($order->order_source, [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE])) {
  1540. if (empty($item['reserve']) && !is_array($item['reserve'])) throw new Exception('对技师/师傅的评价不能为空');
  1541. $res = OrderReserveComments::setData([
  1542. 'store_id' => $order->store_id,
  1543. 'mch_id' => $order->mch_id,
  1544. 'id' => $res->id,
  1545. 'aging_score' => $item['reserve']['aging_score'],
  1546. 'attitude_score'=> $item['reserve']['attitude_score'],
  1547. 'major_score' => $item['reserve']['major_score'],
  1548. 'staff_score' => !empty($item['reserve']['staff_score']) ? $item['reserve']['staff_score'] : 0,
  1549. 'environment_score' => !empty($item['reserve']['environment_score']) ? $item['reserve']['environment_score'] : 0,
  1550. 'service_score' => !empty($item['reserve']['service_score']) ? $item['reserve']['service_score'] : 0,
  1551. 'tags' => $item['reserve']['tags'],
  1552. 'goods_id' => $item['goods_id'],
  1553. 'order_id' => $order->id,
  1554. 'order_detail_id' => $item['order_detail_id'],
  1555. 'user_id' => $order->user_id,
  1556. 'score' => $item['score'],
  1557. 'show_tech' => isset($item['show_tech']) ? $item['show_tech'] : 0, // 确保有默认值
  1558. ]);
  1559. if ($res === false) throw new Exception(OrderComments::getStaticError());
  1560. }
  1561. // 记录评分
  1562. $scores = OrderComments::getOne([
  1563. ['goods_id' => $item['goods_id']],
  1564. ['status' => StatusEnum::ENABLED],
  1565. ], true, [],
  1566. '',
  1567. "SUM(score) as score, COUNT(*) AS total"
  1568. );
  1569. if (!empty($scores['score'])) {
  1570. $scores['score'] = $item['score'];
  1571. } else {
  1572. $scores['score'] += $item['score'];
  1573. }
  1574. Goods::updateAll(['comment_score' => bcdiv($scores['score'], $scores['total'] + 1, 2)], ['id' => $item['goods_id']]);
  1575. }
  1576. $order->order_status = OrderStatusEnum::ACCOMPLISH;
  1577. $order->finish_time = time();
  1578. $order->is_comment = 1;
  1579. $order->save();
  1580. if(!empty($order['store_id'])){
  1581. $is_install = MallAddons::isInstall($order['mall_id'], AddonsEnum::ADDONS_NAME_STORE);
  1582. if($is_install && $order->order_source == AddonsEnum::ADDONS_NAME_STORE){
  1583. $store_order = StoreOrder::findOne(['order_no'=>$order['order_no']]);
  1584. if($store_order){
  1585. $store_order->order_status = OrderStatusEnum::ACCOMPLISH;
  1586. $store_order->finish_time = time();
  1587. $store_order->is_comment = 1;
  1588. if (!$store_order->save()) throw new Exception($store_order->getErrorMessage());
  1589. }
  1590. }
  1591. if ($order->marketing_type == AddonsEnum::ADDONS_NAME_LAO_DA_FU) {
  1592. $store_order = \addons\LaoDaFu\common\models\StoreOrder::findOne(['order_no' => $order['order_no']]);
  1593. if($store_order){
  1594. $store_order->order_status = OrderStatusEnum::ACCOMPLISH;
  1595. $store_order->finish_time = time();
  1596. $store_order->is_comment = 1;
  1597. if (!$store_order->save()) throw new Exception($store_order->getErrorMessage());
  1598. if ($store_order->order_source == OrderSourceEnum::SOURCE_LAODAFU_RESERVE) {
  1599. LaoDaFuStoreOrderService::updateAll(['order_id' => $store_order->id], ['order_status' => $store_order->order_status]);
  1600. }
  1601. }
  1602. }
  1603. }
  1604. $t->commit();
  1605. // 订单完成触发事件
  1606. $user = User::getOne([['id' => $order->user_id]], true, [], false, 'nickname');
  1607. if(empty($order['mch_id'])&&empty($order['store_id'])){
  1608. $user = User::getOne([['id' => $order->user_id]], true, [], false, 'nickname');
  1609. $event = new OrderFinishEvent($order->mall_id);
  1610. $data['id'] = $order->id;
  1611. $data['order_no'] = $order->order_no;
  1612. $data['operator_id'] = $order->user_id;
  1613. $data['operator_name'] = $user['nickname'];
  1614. $data['is_comment'] = 1;
  1615. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1616. }else {
  1617. if (!empty($order['mch_id'])) {
  1618. if (MallAddons::isInstall($order['mall_id'], AddonsEnum::ADDONS_NAME_MCH)) {
  1619. $user = User::getOne([['id' => $order->user_id]], true, [], false, 'nickname');
  1620. $event = new \addons\Mch\common\events\order\OrderFinishEvent($order->mall_id);
  1621. $data['id'] = $order->id;
  1622. $data['order_no'] = $order->order_no;
  1623. $data['operator_id'] = $order->user_id;
  1624. $data['operator_name'] = $user['nickname'];
  1625. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1626. }
  1627. } elseif ($order['store_id']) {
  1628. // 之前门店订单来源为预约的订单,不会触发到门店订单完成事件。这里添加到门店订单完成事件中
  1629. if (($order->order_source == AddonsEnum::ADDONS_NAME_STORE || in_array($order->order_source, [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE]))
  1630. && MallAddons::isInstall($order['mall_id'], AddonsEnum::ADDONS_NAME_STORE)) {
  1631. $user = User::getOne([['id' => $order->user_id]], true, [], false, 'nickname');
  1632. $event = new \addons\Store\common\events\order\OrderFinishEvent($order->mall_id);
  1633. $data['id'] = $order->id;
  1634. $data['order_no'] = $order->order_no;
  1635. $data['operator_id'] = $order->user_id;
  1636. $data['operator_name'] = $user['nickname'];
  1637. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1638. } elseif ($order->order_source == AddonsEnum::ADDONS_NAME_LAO_DA_FU) {
  1639. $user = User::getOne([['id' => $order->user_id]], true, [], false, 'nickname');
  1640. $event = new \addons\LaoDaFu\common\events\order\OrderFinishEvent($order->mall_id);
  1641. $data['id'] = $order->id;
  1642. $data['order_no'] = $order->order_no;
  1643. $data['operator_id'] = $order->user_id;
  1644. $data['operator_name'] = $user['nickname'];
  1645. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1646. }
  1647. }
  1648. }
  1649. return true;
  1650. }catch(Exception $e){
  1651. $t->rollBack();
  1652. self::$static_error = $e->getMessage();
  1653. return false;
  1654. }
  1655. }
  1656. /**
  1657. * @name:
  1658. * @msg: 处理订单完成(过售后)
  1659. * @param {array} $params
  1660. * @return {*}
  1661. */
  1662. public static function complete(array $params)
  1663. {
  1664. try {
  1665. $cond = ['mall_id' => $params['mall_id'], 'id' => $params['id'], 'status' => [StatusEnum::ENABLED]];
  1666. isset($params['user_id']) && $cond['user_id'] = $params['user_id'];
  1667. if(!empty($params['mch_id'])) $cond['mch_id'] = $params['mch_id'];
  1668. $model = self::findOne($cond);
  1669. if (empty($model)) throw new Exception('订单不存在或已删除');
  1670. if ($model->order_status == OrderStatusEnum::ACCOMPLISH) throw new Exception('订单已经完成,无需重复处理');
  1671. if ($model->order_status != OrderStatusEnum::SING && $model->order_status != OrderStatusEnum::TAKE_FINISH) throw new Exception('订单已经被处理');
  1672. if (OrderDetail::getAfterSaleCountByOrderId($params['id']) > 0) throw new Exception('请先处理或关闭订单售后');
  1673. $model->order_status = OrderStatusEnum::ACCOMPLISH;
  1674. $model->finish_time = time();
  1675. if (!$model->save()) throw new Exception($model->getErrorMessage());
  1676. if (in_array($model->order_source, [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE])) {
  1677. // 同步订单状态
  1678. OrderReserve::syncOrderStatus($model);
  1679. }
  1680. if (!empty($model['store_id'])) {
  1681. $is_install = MallAddons::isInstall($model['mall_id'], AddonsEnum::ADDONS_NAME_STORE);
  1682. if ($is_install) {
  1683. $store_order = StoreOrder::findOne(['order_no' => $model['order_no']]);
  1684. if ($store_order) {
  1685. $store_order->order_status = OrderStatusEnum::ACCOMPLISH;
  1686. $store_order->finish_time = time();
  1687. if (!$store_order->save()) throw new Exception($store_order->getErrorMessage());
  1688. }
  1689. }
  1690. }
  1691. if (empty($model['mch_id']) && empty($model['store_id'])) {
  1692. $user = User::getOne([['id' => $model->user_id]], true, [], false, 'nickname');
  1693. $event = new OrderFinishEvent($model->mall_id);
  1694. $data['id'] = $model->id;
  1695. $data['operator_id'] = !empty($params['operator_id']) ? $params['operator_id'] : $model->user_id;
  1696. $data['operator_name'] = !empty($params['operator_name']) ? $params['operator_name'] : $user['nickname'];
  1697. $data['is_admin'] = (int)!empty($params['is_admin']);
  1698. $data['is_system_auto'] = (int)!empty($params['is_system_auto']);
  1699. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1700. } else {
  1701. if (!empty($model['mch_id'])) {
  1702. if (MallAddons::isInstall($model['mall_id'], AddonsEnum::ADDONS_NAME_MCH)) {
  1703. $user = User::getOne([['id' => $model->user_id]], true, [], false, 'nickname');
  1704. $event = new \addons\Mch\common\events\order\OrderFinishEvent($model->mall_id);
  1705. $data['id'] = $model->id;
  1706. $data['operator_id'] = $model->user_id;
  1707. $data['operator_name'] = $user['nickname'];
  1708. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1709. }
  1710. } elseif ($model['store_id']) {
  1711. if ($model->order_source == AddonsEnum::ADDONS_NAME_STORE && MallAddons::isInstall($model['mall_id'], AddonsEnum::ADDONS_NAME_STORE)) {
  1712. $user = User::getOne([['id' => $model->user_id]], true, [], false, 'nickname');
  1713. $event = new \addons\Store\common\events\order\OrderFinishEvent($model->mall_id);
  1714. $data['id'] = $model->id;
  1715. $data['order_no'] = $model->order_no;
  1716. $data['operator_id'] = $model->user_id;
  1717. $data['operator_name'] = $user['nickname'];
  1718. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1719. } elseif ($model->marketing_type == AddonsEnum::ADDONS_NAME_LAO_DA_FU) {
  1720. $user = User::getOne([['id' => $model->user_id]], true, [], false, 'nickname');
  1721. $event = new \addons\LaoDaFu\common\events\order\OrderFinishEvent($model->mall_id);
  1722. $data['id'] = $model->id;
  1723. $data['order_no'] = $model->order_no;
  1724. $data['operator_id'] = $model->user_id;
  1725. $data['operator_name'] = $user['nickname'];
  1726. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1727. }
  1728. }
  1729. }
  1730. return $model;
  1731. } catch (Exception $e) {
  1732. self::$static_error = $e->getMessage();
  1733. return false;
  1734. }
  1735. }
  1736. public static function completes(array $params)
  1737. {
  1738. try {
  1739. $cond = ['mall_id' => $params['mall_id'], 'id' => $params['id'], 'status' => [StatusEnum::ENABLED]];
  1740. isset($params['user_id']) && $cond['user_id'] = $params['user_id'];
  1741. if (!empty($params['mch_id'])) $cond['mch_id'] = $params['mch_id'];
  1742. $model = self::findOne($cond);
  1743. if (empty($model)) throw new Exception('订单不存在或已删除');
  1744. if ($model->order_status == OrderStatusEnum::ACCOMPLISH) throw new Exception('订单已经完成,无需重复处理');
  1745. if ($model->order_status != OrderStatusEnum::SING && $model->order_status != OrderStatusEnum::TAKE_FINISH) throw new Exception('订单已经被处理');
  1746. if (OrderDetail::getAfterSaleCountByOrderId($params['id']) > 0) throw new Exception('请先处理或关闭订单售后');
  1747. $model->order_status = OrderStatusEnum::ACCOMPLISH;
  1748. $model->finish_time = time();
  1749. if (!$model->save()) throw new Exception($model->getErrorMessage());
  1750. if (in_array($model->order_source, [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE])) {
  1751. // 同步订单状态
  1752. OrderReserve::syncOrderStatus($model);
  1753. }
  1754. if (!empty($model['store_id'])) {
  1755. $is_install = MallAddons::isInstall($model['mall_id'], AddonsEnum::ADDONS_NAME_STORE);
  1756. if ($is_install) {
  1757. $store_order = StoreOrder::findOne(['order_no' => $model['order_no']]);
  1758. if ($store_order) {
  1759. $store_order->order_status = OrderStatusEnum::ACCOMPLISH;
  1760. $store_order->finish_time = time();
  1761. if (!$store_order->save()) throw new Exception($store_order->getErrorMessage());
  1762. if (MallAddons::isInstall($model['mall_id'], AddonsEnum::ADDONS_NAME_STORE)) {
  1763. $user = User::getOne([['id' => $model->user_id]], true, [], false, 'nickname');
  1764. $event = new \addons\Store\common\events\order\OrderFinishEvent($model->mall_id);
  1765. $data['id'] = $model->id;
  1766. $data['order_no'] = $model->order_no;
  1767. $data['operator_id'] = $model->user_id;
  1768. $data['operator_name'] = $user['nickname'];
  1769. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1770. }
  1771. }
  1772. }
  1773. }
  1774. return $model;
  1775. } catch (Exception $e) {
  1776. self::$static_error = $e->getMessage();
  1777. return false;
  1778. }
  1779. }
  1780. public static function exportHandle(array $data)
  1781. {
  1782. set_time_limit(3600); // 设置一个小时
  1783. ini_set('memory_limit', '1024M'); //设置内存
  1784. date_default_timezone_set('Asia/Shanghai');
  1785. $download_id = $data['download_id'] ?? 0;
  1786. $download = Download::findOne(['id' => $download_id]);
  1787. if ($download) {
  1788. // 因为当数据过大时会创建多个文件所以开启事务
  1789. $transaction = \Yii::$app->db->beginTransaction();
  1790. try {
  1791. $params = json_decode($download->params, true);
  1792. $isChanjetExport = !empty($params['is_chanjet_addons']);
  1793. $filename = $download->name;
  1794. $type = $download->type;
  1795. $order_detail_model = OrderDetail::find()->alias('od');
  1796. $order_detail_model->leftJoin(self::tableName() . 'as o', 'o.id=od.order_id');
  1797. $order_detail_model->where(['od.mall_id' => $params['mall_id'], 'o.status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]);
  1798. if (isset($params['store_id']) && $params['store_id'] !==''){
  1799. $order_detail_model->andWhere(['=', 'od.store_id', $params['store_id']])->andWhere(['=', 'o.store_id', $params['store_id']]);
  1800. }else{
  1801. $order_detail_model->andWhere(['=', 'od.store_id', 0])->andWhere(['=', 'o.store_id', 0]);
  1802. }
  1803. $is_show_mch_name = 0;
  1804. if (isset($params['mch_id'])) {
  1805. if ($params['mch_id'] == -1) {
  1806. $is_show_mch_name = 1;
  1807. $order_detail_model->andWhere(['<>', 'od.mch_id', 0]);
  1808. } else {
  1809. $order_detail_model->andWhere(['=', 'od.mch_id', $params['mch_id']]);
  1810. }
  1811. }else{
  1812. $order_detail_model->andWhere(['=', 'od.mch_id', 0]);
  1813. }
  1814. $order_detail_model->andWhere(['>=', 'od.status', StatusEnum::DISABLED]);
  1815. if (!empty($params['ids'])) {
  1816. $order_detail_model->andWhere(['od.order_id' => $params['ids']]);
  1817. }
  1818. if (!empty($params['start_date'])) {
  1819. $order_detail_model->andWhere(['>=','od.created_at',strtotime($params['start_date'])]);
  1820. }
  1821. if (!empty($params['end_date'])) {
  1822. $order_detail_model->andWhere(['<=','od.created_at',strtotime($params['start_date'])]);
  1823. }
  1824. if(!empty($params['keyword'])){
  1825. $order_detail_model->andWhere(['like','od.goods_name',$params['keyword']]);
  1826. }
  1827. if(!empty($params['user_id'])){
  1828. if (!empty($params['child_orders'])) {
  1829. $u_ids = UserPartitionRelationship::getChildIdArr($params['user_id']);
  1830. $u_ids[] = $params['user_id'];
  1831. $order_detail_model->andWhere(['in','od.user_id',$u_ids]);
  1832. } else {
  1833. $order_detail_model->andWhere(['=','od.user_id',$params['user_id']]);
  1834. }
  1835. }
  1836. if (!empty($params['order_no'])) {
  1837. $params['order_no'] = explode(',', $params['order_no']);
  1838. $orderIds = self::find()->where(['order_no' => $params['order_no']])->select('id')->column();
  1839. $order_detail_model->andWhere(['in', 'od.order_id', $orderIds]);
  1840. }
  1841. if (!empty($params['mobile'])) {
  1842. $user = User::find()->where(['like', 'mobile', $params['mobile'] . '%', false])->all();
  1843. $order_detail_model->andWhere(['in', 'od.user_id', array_column($user, 'id')]);
  1844. }
  1845. if (!empty($params['start'])) {
  1846. $order_detail_model->andWhere(['>=', 'od.created_at', strtotime($params['start'])]);
  1847. }
  1848. if (!empty($params['end'])) {
  1849. $order_detail_model->andWhere(['<=', 'od.created_at', strtotime($params['end'])]);
  1850. }
  1851. if (isset($params['order_status']) && $params['order_status'] !== '' && $params['order_status'] != 999) {
  1852. $order_detail_model->andWhere(['=', 'od.order_status', $params['order_status']]);
  1853. }
  1854. if (!empty($params['type']) && !empty($params['type_value'])) {
  1855. switch ($params['type']) {
  1856. case 'mobile':
  1857. $uids = User::find()->select('id')
  1858. ->where(['mall_id' => $params['mall_id']])
  1859. ->andWhere(['like', 'mobile', $params['type_value'] . '%', false])->column();
  1860. !empty($uids) && $order_detail_model->andWhere(['od.user_id' => $uids]);
  1861. break;
  1862. case 'username':
  1863. $uids = User::find()->select('id')
  1864. ->where(['mall_id' => $params['mall_id']])
  1865. ->andWhere(['username' => $params['type_value']])->column();
  1866. !empty($uids) && $order_detail_model->andWhere(['od.user_id' => $uids]);
  1867. break;
  1868. case 'user_id':
  1869. $order_detail_model->andWhere(['od.user_id' => $params['type_value']]);
  1870. break;
  1871. case 'order_id':
  1872. $order_detail_model->andWhere(['od.order_id' => $params['type_value']]);
  1873. break;
  1874. case 'goods_name':
  1875. $order_detail_model->andWhere(['like', 'od.goods_name', '%' . $params['type_value'] . '%', false]);
  1876. break;
  1877. case 'consignee':
  1878. case 'consignee_mobile':
  1879. if ($params['type'] == 'consignee') {
  1880. $order_detail_model->andWhere(['like', 'o.receiver_name', $params['type_value'] . '%', false]);
  1881. } else {
  1882. $order_detail_model->andWhere(['like', 'o.mobile', $params['type_value'] . '%', false]);
  1883. }
  1884. break;
  1885. default:
  1886. }
  1887. }
  1888. if (!empty($params['type'] == 'user_id') && !empty($params['type_value'])) {
  1889. $order_detail_model->andWhere(['=', 'od.user_id', $params['type_value']]);
  1890. }
  1891. if (!empty($params['payment_type'])) {
  1892. $order_detail_model
  1893. ->andWhere(['=', 'o.payment_type', $params['payment_type']]);
  1894. }
  1895. if (!empty($params['shipping_type'])) {
  1896. $order_detail_model->andWhere(['=', 'o.shipping_type', $params['shipping_type']]);
  1897. }
  1898. if(!empty($params['marketing_type'])){
  1899. if($params['marketing_type'] != 'order_user_handle_finish'){
  1900. $order_detail_model->andWhere(['=', 'o.marketing_type', $params['marketing_type']]);
  1901. }else{
  1902. $order_user_handle = OrderUserHandle::lists(['where' => [['mall_id' => $params['mall_id']], ['status' => StatusEnum::ENABLED]], 'select' => 'order_id']);
  1903. $order_detail_model->andWhere(['in', 'o.id', array_column($order_user_handle, 'order_id')]);
  1904. }
  1905. }
  1906. //标识排除
  1907. if(!empty($params['not_marketing_type'])){
  1908. if($params['not_marketing_type'] != 'order_user_handle_finish'){
  1909. $order_detail_model->andWhere(['!=', 'o.marketing_type', $params['not_marketing_type']]);
  1910. }else{
  1911. $order_user_handle = OrderUserHandle::lists(['where' => [['mall_id' => $params['mall_id']], ['status' => StatusEnum::ENABLED]], 'select' => 'order_id']);
  1912. $order_detail_model->andWhere(['not in', 'o.id', array_column($order_user_handle, 'order_id')]);
  1913. }
  1914. }
  1915. if (!empty($params['province'])) {
  1916. $order_detail_model->andWhere(['IN', 'o.id', (array)self::find()->where(['mall_id' => $params['mall_id']])
  1917. ->andWhere(['OR',['province' => $params['province']], ['city' => $params['province']], ['area' => $params['province']]])
  1918. ->select('id')
  1919. ->asArray()
  1920. ->column()]);
  1921. }
  1922. if(!empty($params['cate_id'])){
  1923. $goods_cate_ids = GoodsCate::getAllCate($params['mall_id'], [$params['cate_id']]);
  1924. $order_detail_model->andWhere(['IN', 'goods_id', (array)GoodsCateRelate::find()
  1925. ->select('goods_id')->andWhere(['IN', 'cate_id' , $goods_cate_ids])
  1926. ->column()]);
  1927. }
  1928. $order_detail_model->with(['order' => function ($query) use ($isChanjetExport) {
  1929. if ($isChanjetExport) {
  1930. $query->with('addonsChanjetRequest');
  1931. }
  1932. }, 'user' => function ($query) {
  1933. $query->select('id,nickname,username,mobile');
  1934. }]);
  1935. $fields = $params['fields'];
  1936. $fields_arr = self::fieldsList(null, $isChanjetExport);
  1937. $have_select_field_arr = [];
  1938. $is_attr = 0;
  1939. foreach ($fields as $value) {
  1940. if ($value == 'goods_attr_name') {
  1941. $is_attr = 1;
  1942. }
  1943. if (isset($fields_arr[$value])) $have_select_field_arr[] = $fields_arr[$value];
  1944. if ($value == 'mch_id') {
  1945. $have_select_field_arr[] = '商户名称';
  1946. }
  1947. }
  1948. $order_status_list = OrderStatusEnum::getMap();
  1949. $payment_type_list = PayTypeEnum::getMap();
  1950. $shipping_status_list = ShippingStatusEnum::getMap();
  1951. $batchSize = 10000; // 设置订单批量导出数量
  1952. $i = 0; //循环次数
  1953. $orderCount = $order_detail_model->count(); // 查询订单总条数
  1954. $is_order_page = $orderCount > $batchSize; // 判断是否需要分页
  1955. foreach ($order_detail_model->batch($batchSize) as $order_details) {
  1956. $goods_attr_arr = [];
  1957. if ($is_attr) {
  1958. $goods_attr_ids = array_column($order_details, 'goods_attr_id');
  1959. $goods_attr_arr = GoodsAttr::lists(['where' => [['id' => $goods_attr_ids]], 'select' => 'id,goods_no', 'index' => 'id']);
  1960. }
  1961. $mch_list = [];
  1962. if (!empty($is_show_mch_name)) {
  1963. $mch_ids = array_unique(array_column($order_details, 'mch_id'));
  1964. $mch_list = Mch::lists(['where' => [['id' => $mch_ids]], 'select' => 'id,store_name', 'index' => 'id']);
  1965. }
  1966. $order_ids_arr = [];
  1967. $exportList = array_map(function ($order_detail) use ($fields, $payment_type_list, $order_status_list, $shipping_status_list, $goods_attr_arr, $mch_list,&$order_ids_arr) {
  1968. $row = [];
  1969. foreach ($fields as $field) {
  1970. // 收货人地址切割
  1971. if (!empty($order_detail['order']['region_name'])) {
  1972. $region_name = explode(' ', $order_detail['order']['region_name']);
  1973. }
  1974. // 判断是否安装社区团购
  1975. $is_install = MallAddons::isInstall($order_detail['mall_id'], AddonsEnum::ADDONS_NAME_COMMUNITY);
  1976. if ($is_install && $order_detail['order']['shipping_type'] == 2 && !empty($order_detail['order']['extra_info'])) {
  1977. $extra_info = json_decode($order_detail['order']['extra_info'], true);
  1978. if (!empty($extra_info['head_id'])) {
  1979. $headInfo = (new HeadService())->getHeadInfo($order_detail['mall_id'], $extra_info['head_id']);
  1980. if (!empty($headInfo)) {
  1981. $region_name = [
  1982. $headInfo['province'] ?? '',
  1983. $headInfo['city'] ?? '',
  1984. $headInfo['district'] ?? ''
  1985. ];
  1986. $order_detail['order']['address'] = $headInfo['address'];
  1987. }
  1988. }
  1989. }
  1990. switch (true) {
  1991. case (in_array($field, ['created_at'])):
  1992. $row[] = !empty($order_detail[$field]) ? date('Y-m-d H:i:s', $order_detail[$field]) : '';
  1993. break;
  1994. case (in_array($field, ['pay_time', 'consign_time', 'sign_time'])):
  1995. $row[] = !empty($order_detail['order'][$field]) ? date('Y-m-d H:i:s', $order_detail['order'][$field]) : '';
  1996. break;
  1997. case (in_array($field, ['order_no','out_trade_no', 'mobile', 'receiver_name', 'region_name', 'address', 'shipping_money', 'seller_remark', 'remark'])):
  1998. $row[] = !empty($order_detail['order'][$field]) ? $order_detail['order'][$field] : '';
  1999. break;
  2000. case ($field == 'pay_money'):
  2001. if (!isset($order_ids_arr[$field]) || !in_array($order_detail['order_id'],$order_ids_arr[$field])) {
  2002. $row[] = !empty($order_detail['order'][$field]) ? $order_detail['order'][$field] : '';
  2003. $order_ids_arr[$field][]=$order_detail['order_id'];
  2004. } else {
  2005. $row[] = '';
  2006. }
  2007. break;
  2008. case (in_array($field, ['order_id'])):
  2009. $row[] = !empty($order_detail['order']['id']) ? $order_detail['order']['id'] : '';
  2010. break;
  2011. case ($field == 'payment_type'):
  2012. if (!empty($order_detail['order'][$field])) {
  2013. $row[] = !empty($payment_type_list[$order_detail['order'][$field]]) ? $payment_type_list[$order_detail['order'][$field]] : '';
  2014. } else {
  2015. $row[] = '';
  2016. }
  2017. break;
  2018. case ($field == 'order_status'):
  2019. $row[] = !empty($order_status_list[$order_detail[$field]]) ? $order_status_list[$order_detail[$field]] : '';
  2020. break;
  2021. case ($field == 'order_type'):
  2022. if ($order_detail['order']['is_feedback'] != StatusEnum::DISABLED) {
  2023. if ($order_detail['order']['is_feedback'] == StatusEnum::ENABLED) {
  2024. $row[] = '售后订单维权中';
  2025. } else {
  2026. $row[] = '售后订单维权完成';
  2027. }
  2028. } else {
  2029. $row[] = '';
  2030. }
  2031. //var_dump($order_detail['order']['is_feedback'],$row);
  2032. break;
  2033. case ($field == 'shipping_status'):
  2034. $row[] = !empty($shipping_status_list[$order_detail[$field]]) ? $shipping_status_list[$order_detail[$field]] : '';
  2035. break;
  2036. case ($field == 'pay_status'):
  2037. $row[] = !empty($order_detail['order'][$field]) ? '已付款' : '未付款';
  2038. break;
  2039. case ($field == 'user_mobile'):
  2040. $row[] = $order_detail['user']['mobile'] ?? '';
  2041. break;
  2042. case ($field == 'username'):
  2043. $row[] = $order_detail['user']['username'] ?? '';
  2044. break;
  2045. case ($field == 'nickname'):
  2046. $row[] = $order_detail['user']['nickname'] ?? '';
  2047. break;
  2048. case ($field == 'detail_address'):
  2049. $str = '';
  2050. if (!empty($region_name[0])) $str .= $region_name[0];
  2051. if (!empty($region_name[1])) $str .= $region_name[1];
  2052. if (!empty($region_name[2])) $str .= $region_name[2];
  2053. if (!empty($region_name[3])) $str .= $region_name[3];
  2054. $str .= $order_detail['order']['address'];
  2055. $row[] = $str;
  2056. break;
  2057. case ($field == 'province'):
  2058. $row[] = $region_name[0] ?? '';
  2059. break;
  2060. case ($field == 'city'):
  2061. $row[] = $region_name[1] ?? '';
  2062. break;
  2063. case ($field == 'area'):
  2064. $row[] = $region_name[2] ?? '';
  2065. break;
  2066. case ($field == 'town'):
  2067. $row[] = $region_name[3] ?? '';
  2068. break;
  2069. case ($field == 'goods_attr_name'):
  2070. $str = !empty($order_detail[$field]) ? $order_detail[$field] : '';
  2071. if (!empty($goods_attr_arr[$order_detail['goods_attr_id']]['goods_no'])) {
  2072. if (!empty($str)) {
  2073. $str .= ';';
  2074. }
  2075. $str .= '货号:' . $goods_attr_arr[$order_detail['goods_attr_id']]['goods_no'];
  2076. }
  2077. $row[] = $str;
  2078. break;
  2079. case ($field == 'mch_id'):
  2080. if (isset($mch_list) && isset($mch_list[$order_detail[$field]])) {
  2081. $row[] = $mch_list[$order_detail[$field]]['store_name'];
  2082. }
  2083. break;
  2084. case ($field == 'user_id'):
  2085. $row[] = !empty($order_detail[$field]) ? $order_detail[$field] : '';
  2086. break;
  2087. case ($field == 'chanjet_updated_at'):
  2088. if (isset($order_detail['order']['addonsChanjetRequest']['updated_at'])) {
  2089. $row[] = date('Y-m-d H:i:s', $order_detail['order']['addonsChanjetRequest']['updated_at']);
  2090. }else {
  2091. $row[] = '';
  2092. }
  2093. break;
  2094. case ($field == 'chanjet_request_status'):
  2095. if (isset($order_detail['order']['addonsChanjetRequest']['request_status'])) {
  2096. $row[] = AddonsChanjetRequestEnum::getMap()[$order_detail['order']['addonsChanjetRequest']['request_status']] ?? '';
  2097. }else {
  2098. $row[] = '';
  2099. }
  2100. break;
  2101. case ($field == 'cost_total_price'):
  2102. if (isset($order_detail['cost_price']) && isset($order_detail['num']))
  2103. {
  2104. $row[] = bcmul($order_detail['num'], $order_detail['cost_price'], 2);
  2105. }else {
  2106. $row[] = '';
  2107. }
  2108. break;
  2109. case ($field == 'parent_id'):
  2110. // 获取用户上级名称
  2111. $parentId = User::find()->where(['id' => $order_detail['user_id']])->select('parent_id')->scalar();
  2112. $parentName = User::find()->where(['id' => $parentId])->select('nickname')->scalar();
  2113. $row[] = $parentName ?? '';
  2114. break;
  2115. default:
  2116. $row[] = !empty($order_detail[$field]) ? $order_detail[$field] : '';
  2117. }
  2118. }
  2119. return $row;
  2120. }, $order_details);
  2121. if (!empty($exportList)) {
  2122. $csv = new CsvTool();
  2123. $csv->filename = $is_order_page ? $filename . "_1" : $filename;
  2124. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  2125. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  2126. $csv->header = $have_select_field_arr;
  2127. $csv->data = $exportList;
  2128. if ($is_order_page && $i == 0) { // 因为订单页需要分页,所以需要设置文件名
  2129. $download->name = $filename . '_1';
  2130. $download->save();
  2131. }
  2132. if ($i >= 1) {
  2133. $newFileName = $filename . '_' . ($i + 1);
  2134. $csv->filename = $newFileName;
  2135. $downloadData = array(
  2136. 'mall_id' => $download->mall_id,
  2137. 'mch_id' => $download->mch_id,
  2138. 'store_id' => $download->store_id,
  2139. 'name' => $newFileName,
  2140. 'type' => $download->type,
  2141. 'handler_class' => $download->handler_class,
  2142. 'method' => $download->method,
  2143. 'params' => $download->params,
  2144. 'data' => $exportList
  2145. );
  2146. $downloadModel = Download::setData($downloadData);
  2147. if ($downloadModel === false) {
  2148. Yii::$app->custom->logs('创建下载任务失败:' . $downloadData['name'], Download::getStaticError());
  2149. throw new Exception('创建新下载任务失败');
  2150. };
  2151. $download = $downloadModel;
  2152. }
  2153. $csv->export();
  2154. $csv->updateDown($download, $params['mall_id']);
  2155. $i++;
  2156. unset($exportList);
  2157. }
  2158. }
  2159. $transaction->commit();
  2160. return true;
  2161. } catch (\Exception $e) {
  2162. $transaction->rollBack();
  2163. $download->status = 3;
  2164. $res = $download->save();
  2165. var_dump($e->getMessage() . $e->getFile() . $e->getLine());
  2166. self::setStaticError($e->getMessage());
  2167. return $e->getMessage();
  2168. }
  2169. }
  2170. }
  2171. public static function fieldsList($field = null, $isChanjet = false)
  2172. {
  2173. $fields = [
  2174. 'order_id' => '订单ID',
  2175. 'user_id' => '用户ID',
  2176. 'username' => '真实姓名',
  2177. 'nickname' => '用户昵称',
  2178. 'user_mobile' => '用户手机号',
  2179. 'order_no' => '订单编号',
  2180. 'out_trade_no' => '外部交易号',
  2181. 'mobile' => '收货人的手机号码',
  2182. 'detail_address' => '收货人地址全称',
  2183. 'receiver_name' => '收货人姓名',
  2184. 'province' => '收货人省份',
  2185. 'city' => '收货人城市',
  2186. 'area' => '收货人地区/街道',
  2187. 'town' => '收货人乡/镇',
  2188. // 'region_name' => '地区名称(省市区[乡/镇]',
  2189. 'address' => '详细地址',
  2190. 'goods_name' => '商品名', //detail
  2191. 'goods_attr_name' => '规格', //detail
  2192. 'num' => '购买数量', //detail
  2193. 'cost_price' => '商品成本单价', //detail
  2194. 'cost_total_price' => '商品成本总价', //detail
  2195. 'original_goods_price' => '商品原本总价', //detail
  2196. 'goods_price' => '商品优惠后总价', //detail
  2197. 'shipping_money' => '订单运费',
  2198. 'created_at' => '下单时间',
  2199. 'payment_type' => '支付方式', //-
  2200. 'order_type' => '订单情况', //-
  2201. 'order_status' => '订单状态', //-
  2202. 'pay_status' => '订单付款状态', //-
  2203. 'pay_time' => '订单付款时间',
  2204. 'shipping_status' => '订单配送状态', //- detail
  2205. 'consign_time' => '卖家发货时间', //
  2206. 'sign_time' => '买家签收时间',
  2207. 'remark' => '用户订单备注',
  2208. 'seller_remark' => '商家订单备注',
  2209. 'pay_money' => '订单实付金额',
  2210. 'parent_id' => '直推上级',
  2211. ];
  2212. if ($isChanjet) {
  2213. $fields['chanjet_updated_at'] = '最后推送时间';
  2214. $fields['chanjet_request_status'] = '推送状态';
  2215. }
  2216. if ($field === null) return $fields;
  2217. $temp = [];
  2218. foreach ($field as $val) {
  2219. if (isset($fields[$val])) $temp[$val] = $fields[$val];
  2220. }
  2221. return $temp;
  2222. }
  2223. /**
  2224. * 拼装搜索参数
  2225. * @Author bing
  2226. * @DateTime 2021-11-29 14:52:23
  2227. * @copyright: Copyright (c) 2020 广东七件事集团
  2228. * @param [type] $mall_id
  2229. * @param [type] $search
  2230. * @return array
  2231. */
  2232. public static function searchParams($mall_id, $search)
  2233. {
  2234. date_default_timezone_set('PRC');
  2235. // 查询参数
  2236. $isReserve = $search['is_reserve'];
  2237. $where[] = ['=', 'mall_id', $mall_id];
  2238. $where[] = ['>=', 'status', StatusEnum::DISABLED];
  2239. if ($isReserve) {
  2240. $where[] = ['not in', 'order_source', [OrderSourceEnum::SOURCE_SELF_MALL]];
  2241. $where[] = ['=', 'order_source', OrderSourceEnum::SOURCE_RESERVE];
  2242. } else {
  2243. $where[] = ['not in', 'order_source', [OrderSourceEnum::SOURCE_SELF_MALL, OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_WUZHOU_CHAIN]];
  2244. }
  2245. $where[] = ['store_id' => 0];
  2246. if (!empty($search['mch_id'])) {
  2247. $where[] = ['mch_id' => $search['mch_id']];
  2248. } else {
  2249. $where[] = ['mch_id' => 0];
  2250. }
  2251. //配送类型搜索
  2252. if (!empty($search['shipping_type'])) $where[] = ['shipping_type'=>$search['shipping_type']];
  2253. if (isset($search['order_id'])) $where[] = ['in', 'id', explode(',', $search['order_id'])];
  2254. ($search['payment_type'] ?? false) && $where[] = ['=', 'payment_type', $search['payment_type']];
  2255. ($search['shipping_type'] ?? false) && $where[] = ['=', 'shipping_type', $search['shipping_type']];
  2256. isset($search['receiver_name']) && $search['receiver_name'] !== '' && $where[] = ['=', 'receiver_name', $search['receiver_name']];
  2257. if($search['marketing_type'] ?? false){
  2258. if($search['marketing_type'] == 'order_user_handle_finish'){
  2259. $order_user_handle = OrderUserHandle::lists(['where' => [['mall_id' => $mall_id], ['status' => StatusEnum::ENABLED]], 'select' => 'order_id']);
  2260. $where[] = ['id' => array_column($order_user_handle, 'order_id')];
  2261. } elseif ($search['marketing_type'] == OrderSourceEnum::SOURCE_CLOUD_PRESCRIPTION) {
  2262. $where[] = ['=', 'goods_subtype', GoodsTypeEnum::GOODS_SUBTYPE_PRESCRIPTION];
  2263. } else{
  2264. $where[] = ['=', 'marketing_type', $search['marketing_type']];
  2265. }
  2266. }
  2267. //标识排除
  2268. if($search['not_marketing_type'] ?? false){
  2269. if($search['not_marketing_type'] == 'order_user_handle_finish'){
  2270. $order_user_handle = OrderUserHandle::lists(['where' => [['mall_id' => $mall_id], ['status' => StatusEnum::ENABLED]], 'select' => 'order_id']);
  2271. $where[] = ['not in', 'id', array_column($order_user_handle, 'order_id')];
  2272. } elseif ($search['marketing_type'] == OrderSourceEnum::SOURCE_CLOUD_PRESCRIPTION) {
  2273. $where[] = ['=', 'goods_subtype', GoodsTypeEnum::GOODS_SUBTYPE_PRESCRIPTION];
  2274. } else{
  2275. $where[] = ['!=', 'marketing_type', $search['not_marketing_type']];
  2276. }
  2277. }
  2278. if (isset($search['order_status']) && $search['order_status'] != 999) {
  2279. if ($isReserve && $search['order_status'] == OrderStatusEnum::SING) { // 以前端栏目配置服务中即待验收其状态包括已提货
  2280. $where[] = ['in', 'order_status', [OrderStatusEnum::SING, OrderStatusEnum::TAKE_FINISH]];
  2281. } else {
  2282. $where[] = ['=', 'order_status', $search['order_status']];
  2283. }
  2284. }
  2285. if (isset($search['is_use_coupon'])) {
  2286. if ($search['is_use_coupon'] == 1) {
  2287. $where[] = ['>', 'coupon_money', 0];
  2288. } else {
  2289. $where[] = ['=', 'coupon_money', 0];
  2290. }
  2291. }
  2292. // ($search['order_status'] ?? false) && $search['order_status'] != 999 && $where[] = ['=', 'order_status', $search['order_status']];
  2293. ($search['order_no'] ?? false) && $where[] = ['in', 'order_no', explode(',',$search['order_no'])];
  2294. ($search['start'] ?? false) && $where[] = ['>=', 'created_at', strtotime($search['start'])];
  2295. ($search['end'] ?? false) && $where[] = ['<=', 'created_at', strtotime($search['end'])];
  2296. if($search['province'] ?? false) {
  2297. $where[] =['IN', 'id', (array)self::find()->where(['mall_id' => $mall_id])
  2298. ->andWhere(['OR',['province' => $search['province']],['city' => $search['province']], ['area' => $search['province']]])
  2299. ->select('id')
  2300. ->asArray()
  2301. ->column()];
  2302. }
  2303. if($search['cate_id'] ?? false){
  2304. $goods_cate_ids = GoodsCate::getAllCate($mall_id, [$search['cate_id']]);
  2305. $order_ids = (array)OrderDetail::find()->where(['mall_id' => $mall_id])->andWhere(['IN', 'goods_id', (array)GoodsCateRelate::find()
  2306. ->select('goods_id')->andWhere(['IN', 'cate_id' ,$goods_cate_ids])
  2307. ->column()])->select('order_id')->column();
  2308. $where[] = ['id'=> $order_ids];
  2309. }
  2310. if ($search['type_value'] ?? false) {
  2311. switch ($search['type']) {
  2312. case 'mobile':
  2313. $uids = User::find()->select('id')->where(['like', 'mobile', $search['type_value'] . '%', false])->column();
  2314. !empty($uids) ? $where[] = ['in', 'user_id', $uids] : $where[] = ['=', 'user_id', 0];
  2315. break;
  2316. case 'username':
  2317. $uids = User::find()->select('id')->where(['username' => $search['type_value']])->column();
  2318. !empty($uids) && $where[] = ['in', 'user_id', $uids];
  2319. break;
  2320. case 'user_id':
  2321. $where[] = ['in', 'user_id', $search['type_value']];
  2322. break;
  2323. case 'order_id':
  2324. $where[] = ['=', 'id', $search['type_value']];
  2325. break;
  2326. case 'goods_name':
  2327. $order_detail_list = OrderDetail::lists([
  2328. 'where' => [
  2329. ['mall_id' => $mall_id],
  2330. ['store_id' => 0],
  2331. ['mch_id' => 0],
  2332. ['like', 'goods_name', '%' . $search['type_value'] . '%', false]
  2333. ],
  2334. 'select' => 'order_id'
  2335. ]);
  2336. $orderIds = array_column($order_detail_list, 'order_id');
  2337. $where[] = ['id' => $orderIds];
  2338. break;
  2339. // 查询交易单号
  2340. case 'trade_no':
  2341. $ids = PaymentTrade::find()->where(['mall_id' => $mall_id, 'out_trade_no' => $search['type_value']])->select('id')->column();
  2342. $order_nos = PaymentTradeOrder::find()->where(['trade_id' => $ids])->select('order_no')->column();
  2343. $where[] = ['order_no' => $order_nos];
  2344. break;
  2345. case 'consignee':
  2346. case 'consignee_mobile':
  2347. if ($search['type'] == 'consignee') {
  2348. $where[] = ['like', 'receiver_name', $search['type_value'] . '%', false];
  2349. } else {
  2350. $where[] = ['like', 'mobile', $search['type_value'] . '%', false];
  2351. }
  2352. break;
  2353. default:
  2354. }
  2355. }
  2356. return $where;
  2357. }
  2358. public static function updateOrderPayMoney($rand_fee_arr)
  2359. {
  2360. foreach ($rand_fee_arr as $order_no => $rand_fee) {
  2361. $order_model = Order::getOne([['order_no' => $order_no]], false, ['detail']);
  2362. if ($order_model) {
  2363. $extra_arr = json_decode($order_model->extra_info, true);
  2364. if (!is_array($extra_arr)) {
  2365. $extra_arr[] = $order_model->extra_info;
  2366. }
  2367. // $order_model->pay_money -= $rand_fee;
  2368. // $order_model->goods_price -= $rand_fee;// 随机减的金额不能减去商品实际售价金额
  2369. $order_detail_list = $order_model['detail'];
  2370. $total_goods_price = array_sum(array_column($order_detail_list, 'goods_price'));
  2371. $temp_rand_fee = 0;
  2372. $extra_arr['rand_fee']['order'] = ['desc' => '随机减', 'order_no' => $order_no, 'money' => $rand_fee];
  2373. $order_detail_extra_info = [];
  2374. foreach ($order_detail_list as $k => $order_detail) {
  2375. if (!isset($order_detail_list[$k + 1])) {
  2376. $order_detail_rand_fee = bcsub($rand_fee, $temp_rand_fee, 2);
  2377. } else {
  2378. $rate = bcdiv($order_detail['goods_price'], $total_goods_price, 2);
  2379. $order_detail_rand_fee = bcmul($rate, $rand_fee, 2);
  2380. $temp_rand_fee = bcadd($order_detail_rand_fee, $temp_rand_fee, 2);
  2381. }
  2382. // 商品优惠后的金额不包括随机减金额
  2383. // $order_detail->goods_price-=$order_detail_rand_fee;
  2384. // $order_detail->save();
  2385. $order_detail_extra_info = ['desc' => '随机减', 'order_detail_id' => $order_detail['id'], 'money' => $order_detail_rand_fee];
  2386. }
  2387. $extra_arr['rand_fee']['order_detail'] = $order_detail_extra_info;
  2388. $order_model->extra_info = json_encode($extra_arr);
  2389. $order_model->save();
  2390. }
  2391. }
  2392. }
  2393. public static function checkScoreMallIsCommission($marketing_type)
  2394. {
  2395. $join_commission = isset(\Yii::$app->params['is_score_mall_join_commission']) ? \Yii::$app->params['is_score_mall_join_commission'] : true;
  2396. return $marketing_type == AddonsEnum::ADDONS_NAME_SCORE_MALL && !$join_commission;
  2397. }
  2398. public static function userInitiativeComplete($params)
  2399. {
  2400. try{
  2401. $order = self::findOne(['id' => $params['id'], 'mall_id' => $params['mall_id'], 'status' => StatusEnum::ENABLED]);
  2402. if(empty($order)) throw new \Exception('订单参数错误');
  2403. if(!in_array($order->order_status ,[OrderStatusEnum::PAY,OrderStatusEnum::SHIPMENTS,OrderStatusEnum::SING])){
  2404. throw new \Exception('该订单不支持此操作');
  2405. }
  2406. if($order->order_status == OrderStatusEnum::PAY){
  2407. $new_params = $params;
  2408. $new_params['order_detail_ids'] = [];
  2409. $new_params['shipping_type'] = 0;
  2410. $new_params['express_id'] = $new_params['express_name'] = $new_params['customer_name']= $new_params['express_no'] = $new_params['memo']= '';
  2411. $details = ArrayHelper::toArray($order->detail);
  2412. foreach($details as $detail){
  2413. $new_params['order_detail_ids'][] = $detail['id'];
  2414. }
  2415. if(OrderExpress::delivery($new_params) == false){
  2416. throw new \Exception(OrderExpress::getStaticError());
  2417. }
  2418. $order->order_status = OrderStatusEnum::SHIPMENTS;
  2419. if(self::takeDelivery($params) == false){
  2420. throw new \Exception(self::getStaticError());
  2421. }
  2422. }
  2423. if(self::complete($params) == false){
  2424. throw new \Exception(self::getStaticError());
  2425. }
  2426. OrderUserHandle::setData([
  2427. 'mall_id' => $params['mall_id'],
  2428. 'user_id' => $params['user_id'],
  2429. 'order_id' => $params['id'],
  2430. 'order_status' => OrderStatusEnum::ACCOMPLISH
  2431. ]);
  2432. return true;
  2433. }catch(\Exception $e){
  2434. self::setStaticError($e->getMessage());
  2435. return false;
  2436. }
  2437. }
  2438. public static function handleVerifyOrder($params)
  2439. {
  2440. try{
  2441. $order = self::findOne(['id' => $params['id'], 'mall_id' => $params['mall_id'], 'status' => StatusEnum::ENABLED]);
  2442. if(empty($order)) throw new \Exception('订单参数错误');
  2443. if(!in_array($order->order_status ,[OrderStatusEnum::PAY,OrderStatusEnum::SHIPMENTS,OrderStatusEnum::SING])){
  2444. throw new \Exception('该订单不支持此操作');
  2445. }
  2446. if($order->order_status == OrderStatusEnum::PAY){
  2447. $new_params = $params;
  2448. $new_params['order_detail_ids'] = [];
  2449. $new_params['shipping_type'] = 0;
  2450. $new_params['express_id'] = $new_params['express_name'] = $new_params['customer_name']= $new_params['express_no'] = $new_params['memo']= '';
  2451. $details = ArrayHelper::toArray($order->detail);
  2452. foreach($details as $detail){
  2453. $new_params['order_detail_ids'][] = $detail['id'];
  2454. }
  2455. if(OrderExpress::delivery($new_params) == false){
  2456. throw new \Exception(OrderExpress::getStaticError());
  2457. }
  2458. $order->order_status = OrderStatusEnum::SHIPMENTS;
  2459. }
  2460. if($order->order_status == OrderStatusEnum::SHIPMENTS){
  2461. if(self::takeDelivery($params) == false){
  2462. throw new \Exception(self::getStaticError());
  2463. }
  2464. }
  2465. if(self::complete($params) == false){
  2466. throw new \Exception(self::getStaticError());
  2467. }
  2468. return true;
  2469. }catch(\Exception $e){
  2470. self::setStaticError($e->getMessage());
  2471. return false;
  2472. }
  2473. }
  2474. /**
  2475. * 获取会员支付总金额
  2476. *
  2477. * @param integer $user_id
  2478. * @param integer $pay_status
  2479. * @return float
  2480. */
  2481. public static function getUserPayMoney(int $user_id, int $pay_status = StatusEnum::ENABLED)
  2482. {
  2483. return static::find()
  2484. ->where(['user_id' => $user_id, 'pay_status' => $pay_status, 'status' => StatusEnum::DISABLED])
  2485. ->sum('pay_money');
  2486. }
  2487. /**
  2488. * 批量添加备注
  2489. * @param $params
  2490. * @return bool
  2491. */
  2492. public static function batchRemark(array $params): bool
  2493. {
  2494. try {
  2495. $orderIds = explode(',', $params['order_ids']);
  2496. $res = self::updateAll(['seller_remark' => $params['content']], ['id' => $orderIds]);
  2497. if (!$res) {
  2498. throw new Exception('批量备注失败!');
  2499. }
  2500. return true;
  2501. } catch (Exception $e) {
  2502. if(isset($trans)) $trans->rollback();
  2503. self::$static_error = $e->getMessage();
  2504. return false;
  2505. }
  2506. }
  2507. /**
  2508. * 国内手机号验证
  2509. * @param $number
  2510. * @return bool
  2511. */
  2512. public static function isMobileNumber($number): bool
  2513. {
  2514. // 手机号正则表达式
  2515. $pattern = '/^1[3-9]\d{9}$/';
  2516. if (preg_match($pattern, $number)) {
  2517. return true;
  2518. } else {
  2519. return false;
  2520. }
  2521. }
  2522. public static function getOrderByNo($params)
  2523. {
  2524. try {
  2525. $info = self::getOne([
  2526. ['mall_id' => $params['mall_id']],
  2527. ['order_no' => $params['order_no']]
  2528. ], true, ['detail', 'user']);
  2529. $data = [
  2530. 'user' => [
  2531. 'id' => $info['user']['id'],
  2532. 'nickname' => $info['user']['nickname'],
  2533. 'avatar_url' => $info['user']['avatar_url'],
  2534. 'mobile' => $info['user']['mobile'],
  2535. 'parent_id' => $info['user']['parent_id'],
  2536. ],
  2537. 'pay_money' => $info['pay_money'],
  2538. 'order_no' => $info['order_no'],
  2539. 'id' => $info['id'],
  2540. ];
  2541. foreach ($info['detail'] as $key => $value) {
  2542. $data['product'][] = [
  2543. 'pic_url' => $value['pic_url'],
  2544. 'goods_name' => $value['goods_name'],
  2545. 'goods_attr_name' => $value['goods_attr_name'],
  2546. 'price' => $value['price'],
  2547. 'num' => $value['num'],
  2548. ];
  2549. }
  2550. return $data;
  2551. } catch (\Exception $e) {
  2552. throw new Exception($e->getMessage());
  2553. }
  2554. }
  2555. /**
  2556. * 判断会员是否支付
  2557. *
  2558. * @param $user_id
  2559. * @return bool
  2560. */
  2561. public static function userIsPay($user_id)
  2562. {
  2563. return Order::find()->andWhere(['user_id'=>$user_id,'pay_status'=>1,'store_id'=>0,'mch_id'=>0])->exists();
  2564. }
  2565. /**
  2566. * 判断会员是否创建订单
  2567. *
  2568. * @param $user_id
  2569. * @return bool
  2570. */
  2571. public static function userIsCreated($user_id)
  2572. {
  2573. return Order::find()->andWhere(['user_id'=>$user_id,'store_id'=>0,'mch_id'=>0])->exists();
  2574. }
  2575. public static function isStopOrder($mall_id)
  2576. {
  2577. $is_stop_order = MallSetting::conf($mall_id,'order.is_stop_order');
  2578. if ($is_stop_order) return true;
  2579. return false;
  2580. }
  2581. }