ProductClass.php 130 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250
  1. <?php
  2. namespace bizGhs\product\classes;
  3. use biz\item\classes\PtItemClass;
  4. use biz\item\classes\PtItemClassClass;
  5. use biz\item\classes\UnitClass;
  6. use biz\wx\classes\WxMessageClass;
  7. use bizGhs\base\classes\BaseClass;
  8. use bizGhs\custom\classes\CustomClass;
  9. use bizGhs\item\classes\CostChangeClass;
  10. use bizGhs\item\classes\ItemClassClass;
  11. use bizGhs\item\classes\PriceChangeClass;
  12. use bizGhs\merchant\classes\ShopClass;
  13. use bizGhs\order\classes\CheckOrderClass;
  14. use bizGhs\order\classes\CheckOrderItemClass;
  15. use bizGhs\order\traits\OrderTrait;
  16. use bizGhs\shop\classes\ShopAdminClass;
  17. use bizGhs\stock\classes\StockRecordClass;
  18. use common\components\dict;
  19. use common\components\imgUtil;
  20. use common\components\noticeUtil;
  21. use common\components\orderSn;
  22. use common\components\stringUtil;
  23. use common\components\util;
  24. use common\services\xhItemService;
  25. use PhpAmqpLib\Wire\AMQPTable;
  26. use Yii;
  27. class ProductClass extends BaseClass
  28. {
  29. use OrderTrait;
  30. public static $baseFile = '\bizGhs\product\models\Product';
  31. const PRICE_LABEL_AUTO = 1; //自动调价
  32. const PRICE_LABEL_CHANGE = 2; //改价
  33. const LOCK_STOCK = 'lock_stock';//修改库存锁
  34. const LOCK_ON_STOCK = 'lock_on_stock';//修改库存锁
  35. const LIMIT_BUY_KEY = 'limit_buy_';
  36. const CLEAR_MARK_KEY = 'limit_buy_clear_at:';
  37. const CLEAR_LOOP_KEY = 'limit_buy_clear_loop:';
  38. const LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY = 'limitBuyRollbackSnapshot'; //记录限购旧值快照缓存键
  39. const RECOVER_LIMIT_BUY_KEY = 'recover_limit_buy:'; //记录限购旧值快照缓存键
  40. public static function getNotShowFrontHideMainIds()
  41. {
  42. if (getenv('YII_ENV', 'local') == 'production') {
  43. //惠雅(52, 1459, 2126, 2128)、花样年华(1553)、泉城(60,1082)不显示隐藏按钮
  44. $notShowFrontHide = [52, 1459, 2126, 2128, 1553, 60, 1082];
  45. } else {
  46. $notShowFrontHide = [];
  47. }
  48. return $notShowFrontHide;
  49. }
  50. //添加花材的根方法 ssh 20230224
  51. public static function addBaseData($data)
  52. {
  53. $respond = self::add($data, true);
  54. $sjId = $data['sjId'] ?? 0;
  55. $mainId = $data['mainId'] ?? 0;
  56. $productId = $respond->id ?? 0;
  57. $ptItemId = $respond->itemId ?? 0;
  58. $cost = $respond->cost ?? 0;
  59. $name = $respond->name ?? '';
  60. $cover = $respond->cover ?? '';
  61. $staffId = $data['staffId'] ?? 0;
  62. $staffName = $data['staffName'] ?? '';
  63. if (isset($respond->hasCpId) == false || $respond->hasCpId == 0) {
  64. //noticeUtil::push("mainId:" . $mainId . "建的花材【{$name}】没有关联产品", '15280215347');
  65. }
  66. $ptItem = PtItemClass::getById($ptItemId, true);
  67. if (!empty($ptItem)) {
  68. $ptItem->useNum = 1;
  69. $ptItem->save();
  70. }
  71. if ($cost > 0) {
  72. $changeData = [
  73. 'ptStyle' => 2,
  74. 'sjId' => $sjId,
  75. 'mainId' => $mainId,
  76. 'itemId' => $productId,
  77. 'ptItemId' => $ptItemId,
  78. 'cost' => $cost,
  79. 'staffId' => $staffId,
  80. 'staffName' => $staffName,
  81. 'name' => $name ?? '',
  82. 'cover' => $cover,
  83. 'type' => 3,
  84. 'targetId' => $productId ?? 0,
  85. 'event' => '创建花材录入',
  86. ];
  87. CostChangeClass::addData($changeData);
  88. }
  89. $price = $data['price'] ?? 0;
  90. $skPrice = $data['skPrice'] ?? 0;
  91. if ($price > 0) {
  92. $changeData = [
  93. 'ptStyle' => 2,
  94. 'sjId' => $sjId,
  95. 'mainId' => $mainId,
  96. 'itemId' => $productId,
  97. 'ptItemId' => $ptItemId,
  98. 'price' => $price,
  99. 'skPrice' => $skPrice,
  100. 'staffId' => $staffId,
  101. 'staffName' => $staffName,
  102. 'name' => $name,
  103. 'cover' => $cover,
  104. 'event' => '创建花材录入',
  105. 'type' => 3,
  106. 'targetId' => $productId,
  107. ];
  108. PriceChangeClass::addData($changeData);
  109. }
  110. return $respond;
  111. }
  112. //根据itemIds获取相应的花材信息
  113. public static function getProductInfoByItemIds(array $itemIds, $hdMainId)
  114. {
  115. $where = ['itemId' => ['in', $itemIds], 'delStatus' => 0, 'mainId' => $hdMainId];
  116. $data = self::getAllList('*', $where);
  117. $data = self::groupItemBaseInfo($data);
  118. return $data;
  119. }
  120. //根据花材ID,获取花材名称,图片
  121. public static function groupItemBaseInfo($list)
  122. {
  123. //取得itemIds
  124. $itemIds = array_column($list, 'itemId');
  125. //获取相应的图片,名称,py
  126. $where = ['id' => ['in', $itemIds]];
  127. $data = xhItemService::getAllByCondition($where, null, '*');
  128. if (!empty($data)) {
  129. $data = array_column($data, NULL, 'id');
  130. foreach ($list as $k => $v) {
  131. if (isset($data[$v['itemId']]['cover']) && !empty($data[$v['itemId']]['cover'])) {
  132. $cover = self::groupImg($data[$v['itemId']]['cover']);
  133. } else {
  134. //没有图片时取默认图片 ssh 2021.2.18
  135. $cover = imgUtil::getPrefix() . '/hhb_small.png';
  136. }
  137. $unitNum = $data[$v['itemId']]['ratio'] ?? 0;
  138. $stockInfo = self::formatStock($v['stock'], $unitNum);
  139. $list[$k]['cover'] = $cover;
  140. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  141. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  142. $list[$k]['itemName'] = $data[$v['itemId']]['name'] ?? '';
  143. $list[$k]['py'] = $data[$v['itemId']]['py'] ?? '';
  144. $list[$k]['ratio'] = $unitNum ?? 0;
  145. //换算大小单位对应价格
  146. $list[$k]['bigPrice'] = $v['price']; //大单位价格就是product表存的价格
  147. $smallPrice = 0;
  148. if ($unitNum) {
  149. $smallPrice = bcdiv($v['price'], $unitNum, 2);
  150. }
  151. $list[$k]['smallPrice'] = $smallPrice;
  152. $list[$k]['bigUnit'] = $data[$v['itemId']]['bigUnit'];
  153. $list[$k]['smallUnit'] = $data[$v['itemId']]['smallUnit'];
  154. }
  155. }
  156. return $list;
  157. }
  158. //获取详情 ruidian 2021.5.3
  159. public static function getItemInfo($id, $ghs = [])
  160. {
  161. $info = self::getById($id);
  162. if (empty($info)) {
  163. return $info;
  164. }
  165. $level = $ghs->giveLevel ?? 1;
  166. $list = self::groupProductInfo([$info], $level);
  167. return current($list);
  168. }
  169. //库存预警列表用到的数据组合 ssh 20221225
  170. public static function warningGroup($list, $level = 0)
  171. {
  172. foreach ($list as $k => $v) {
  173. $shortCover = $v['cover'] ?? '';
  174. $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  175. }
  176. return $list;
  177. }
  178. /**
  179. * 库存预警分页列表(与 get-item-list 共用 where 条件):stock + onStock < stockWarning
  180. */
  181. public static function warningList($where, $order = ['inTurn' => SORT_DESC, 'actualSold' => SORT_DESC])
  182. {
  183. $get = Yii::$app->request->get();
  184. $page = isset($get['page']) ? intval($get['page']) : 1;
  185. $pageSize = isset($get['pageSize']) && !empty($get['pageSize']) ? intval($get['pageSize']) : Yii::$app->params['pageSize'];
  186. $offset = ($page - 1) * $pageSize;
  187. $model = self::getModel();
  188. $query = $model->conditionQuery($where)->andWhere('(`stock` + `onStock`) < `stockWarning`');
  189. $count = (clone $query)->count();
  190. $totalPage = $pageSize > 0 ? (int)ceil($count / $pageSize) : 0;
  191. if (!empty($order)) {
  192. $query->orderBy($order);
  193. }
  194. $list = $query->offset($offset)->limit($pageSize)->asArray()->all();
  195. return [
  196. 'totalNum' => $count,
  197. 'totalPage' => $totalPage,
  198. 'moreData' => $totalPage > $page ? 1 : 0,
  199. 'list' => $list,
  200. ];
  201. }
  202. //盘点用到的数据组合
  203. public static function checkItemGroup($list, $level = 0)
  204. {
  205. foreach ($list as $k => $v) {
  206. $shortCover = $v['cover'] ?? '';
  207. $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  208. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
  209. $stockInfo = self::formatStock($v['stock'], $ratio);
  210. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  211. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  212. }
  213. return $list;
  214. }
  215. public static function stockOutGroup($list, $level = 0)
  216. {
  217. //各个等级对应的price addPrice字段
  218. $priceMap = CustomClass::$levelPriceKeyMap;
  219. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  220. foreach ($list as $k => $v) {
  221. $shortCover = $v['cover'] ?? '';
  222. $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  223. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  224. $list[$k]['price'] = $price;
  225. $list[$k]['bigPrice'] = $price;
  226. $smallRatio = $v['smallRatio'] ?? 0;
  227. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 20;
  228. $smallPrice = bcdiv($price, $ratio, 2);
  229. $smallPrice = bcmul($smallPrice, $smallRatio, 2);
  230. $list[$k]['smallPrice'] = $smallPrice;
  231. }
  232. return $list;
  233. }
  234. //拆散花材 ssh 20230329
  235. public static function partItemGroup($list, $level = 0)
  236. {
  237. foreach ($list as $k => $v) {
  238. $shortCover = $v['cover'] ?? '';
  239. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 20;
  240. $stockInfo = self::formatStock($v['stock'], $ratio);
  241. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  242. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  243. $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  244. }
  245. return $list;
  246. }
  247. //报损花材列表 ssh 20221226
  248. public static function breakItemGroup($list, $level = 0)
  249. {
  250. foreach ($list as $k => $v) {
  251. $shortCover = $v['cover'] ?? '';
  252. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 20;
  253. $stockInfo = self::formatStock($v['stock'], $ratio);
  254. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  255. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  256. $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  257. }
  258. return $list;
  259. }
  260. //采购花材列表用到的数据组合 ssh 20221225
  261. public static function cgItemGroup($list, $level = 0)
  262. {
  263. foreach ($list as $k => $v) {
  264. $shortCover = $v['cover'] ?? '';
  265. $list[$k]['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  266. }
  267. return $list;
  268. }
  269. //预订 ssh 20231121
  270. public static function bookItemGroup($list, $level = 0)
  271. {
  272. //各个等级对应的price addPrice字段
  273. $priceMap = CustomClass::$levelPriceKeyMap;
  274. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  275. foreach ($list as $k => $v) {
  276. $shortCover = $v['cover'] ?? '';
  277. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  278. if (!empty($shortCover)) {
  279. $parse = parse_url(self::groupImg($shortCover));
  280. if (empty($parse['query'])) {
  281. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  282. } else {
  283. $cover = self::groupImg($shortCover);
  284. }
  285. }
  286. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
  287. $list[$k]['cover'] = $cover;
  288. $stockInfo = self::formatStock($v['stock'], $ratio);
  289. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  290. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  291. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  292. $list[$k]['price'] = $price;
  293. $list[$k]['bigPrice'] = $price;
  294. $smallRatio = $v['smallRatio'] ?? 0;
  295. $smallPrice = bcdiv($price, $ratio, 2);
  296. $smallPrice = bcmul($smallPrice, $smallRatio, 2);
  297. $list[$k]['smallPrice'] = $smallPrice;
  298. }
  299. return $list;
  300. }
  301. //花材列表用到的数据组合 ssh 20221225
  302. public static function kdItemGroup($list, $level = 0, $custom = [])
  303. {
  304. //各个等级对应的price addPrice字段
  305. $priceMap = CustomClass::$levelPriceKeyMap;
  306. //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116
  307. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  308. //但这边加的参数是有用的,给指定客户涨价的
  309. $addPriceMap['risePercent'] = $custom['risePercent'] ?? 0;
  310. foreach ($list as $k => $v) {
  311. $shortCover = $v['cover'] ?? '';
  312. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  313. if (!empty($shortCover)) {
  314. $parse = parse_url(self::groupImg($shortCover));
  315. if (empty($parse['query'])) {
  316. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  317. } else {
  318. $cover = self::groupImg($shortCover);
  319. }
  320. }
  321. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
  322. $list[$k]['cover'] = $cover;
  323. $stockInfo = self::formatStock($v['stock'], $ratio);
  324. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  325. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  326. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  327. $list[$k]['originPrice'] = $list[$k]['price']; //原始价
  328. $list[$k]['price'] = $price;
  329. $list[$k]['bigPrice'] = $price;
  330. $smallRatio = $v['smallRatio'] ?? 0;
  331. $smallPrice = bcdiv($price, $ratio, 2);
  332. $smallPrice = bcmul($smallPrice, $smallRatio, 2);
  333. $list[$k]['smallPrice'] = $smallPrice;
  334. }
  335. return $list;
  336. }
  337. //花材列表用到的数据组合 ssh 20221225
  338. public static function itemListGroup($list, $level = 0)
  339. {
  340. //各个等级对应的price addPrice字段
  341. $priceMap = CustomClass::$levelPriceKeyMap;
  342. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  343. foreach ($list as $k => $v) {
  344. $shortCover = $v['cover'] ?? '';
  345. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  346. if (!empty($shortCover)) {
  347. $parse = parse_url(self::groupImg($shortCover));
  348. if (empty($parse['query'])) {
  349. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  350. } else {
  351. $cover = self::groupImg($shortCover);
  352. }
  353. }
  354. $list[$k]['cover'] = $cover;
  355. //今日特价、会员价
  356. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  357. if ($level == 0) {
  358. //零售做特价时显示的原价,不能删除
  359. if (isset($v['skPrice'])) {
  360. $list[$k]['prePrice'] = $v['skPrice'];
  361. }
  362. }
  363. if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
  364. //做特价花材时,花材列表和改价列表显示的会员价和零售价,不能删除
  365. $list[$k]['hjPrice'] = floatval($v['hjDiscountPrice']);
  366. $list[$k]['skPrice'] = floatval($v['skDiscountPrice']);
  367. }
  368. $list[$k]['price'] = $price;
  369. $cost = $v['cost'] ?? 0;
  370. if ($price > 0) {
  371. $profit = bcsub($price, $cost, 2);
  372. $mll = bcdiv($profit, $price, 3);
  373. $mll = bcmul($mll, 100);
  374. $mll = round($mll, 1);
  375. } else {
  376. $mll = 0;
  377. }
  378. $list[$k]['mll'] = $mll;
  379. }
  380. return $list;
  381. }
  382. public static function cgStaffListGroup($list, $level = 0)
  383. {
  384. //各个等级对应的price addPrice字段
  385. $priceMap = CustomClass::$levelPriceKeyMap;
  386. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  387. foreach ($list as $k => $v) {
  388. $shortCover = $v['cover'] ?? '';
  389. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  390. if (!empty($shortCover)) {
  391. $parse = parse_url(self::groupImg($shortCover));
  392. if (empty($parse['query'])) {
  393. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  394. } else {
  395. $cover = self::groupImg($shortCover);
  396. }
  397. }
  398. $list[$k]['cover'] = $cover;
  399. //今日特价、会员价
  400. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  401. if ($level == 0) {
  402. //零售做特价时显示的原价,不能删除
  403. if (isset($v['skPrice'])) {
  404. $list[$k]['prePrice'] = $v['skPrice'];
  405. }
  406. }
  407. if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
  408. //做特价花材时,花材列表和改价列表显示的会员价和零售价,不能删除
  409. $list[$k]['hjPrice'] = floatval($v['hjDiscountPrice']);
  410. $list[$k]['skPrice'] = floatval($v['skDiscountPrice']);
  411. }
  412. $list[$k]['price'] = $price;
  413. $cost = $v['cost'] ?? 0;
  414. if ($price > 0) {
  415. $profit = bcsub($price, $cost, 2);
  416. $mll = bcdiv($profit, $price, 3);
  417. $mll = bcmul($mll, 100);
  418. $mll = round($mll, 1);
  419. } else {
  420. $mll = 0;
  421. }
  422. $list[$k]['mll'] = $mll;
  423. }
  424. return $list;
  425. }
  426. //通用花材列表数组组合
  427. public static function changeStockGroup($list, $level = 0)
  428. {
  429. //各个等级对应的price addPrice字段
  430. $priceMap = CustomClass::$levelPriceKeyMap;
  431. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  432. foreach ($list as $k => $v) {
  433. $shortCover = $v['cover'] ?? '';
  434. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  435. if (!empty($shortCover)) {
  436. $parse = parse_url(self::groupImg($shortCover));
  437. if (empty($parse['query'])) {
  438. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  439. } else {
  440. $cover = self::groupImg($shortCover);
  441. }
  442. }
  443. $list[$k]['cover'] = $cover;
  444. $list[$k]['prePrice'] = $v['price'] ?? 0;
  445. //今日特价、会员价
  446. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  447. //零售做特价时的原价
  448. if ($level == 0) {
  449. $currentAddPrice = $v['skMore'] ?? 0;
  450. $list[$k]['prePrice'] = bcadd($v['price'], $currentAddPrice, 2);
  451. }
  452. if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
  453. $currentAddPrice = $v['skMore'] ?? 0;
  454. $list[$k]['skPrice'] = bcadd($v['discountPrice'], $currentAddPrice, 2);
  455. }
  456. $list[$k]['price'] = $price;
  457. $cost = $v['cost'] ?? 0;
  458. $ml = 0;
  459. if ($price > 0) {
  460. $profit = bcsub($price, $cost, 2);
  461. $ml = $profit;
  462. $mll = bcdiv($profit, $price, 3);
  463. $mll = bcmul($mll, 100);
  464. $mll = round($mll, 1);
  465. } else {
  466. $mll = 0;
  467. }
  468. $list[$k]['mll'] = $mll;
  469. $list[$k]['ml'] = $ml;
  470. }
  471. return $list;
  472. }
  473. //通用花材列表数组组合
  474. public static function changePriceGroup($list, $level = 0)
  475. {
  476. //各个等级对应的price addPrice字段
  477. $priceMap = CustomClass::$levelPriceKeyMap;
  478. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  479. foreach ($list as $k => $v) {
  480. $shortCover = $v['cover'] ?? '';
  481. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  482. if (!empty($shortCover)) {
  483. $parse = parse_url(self::groupImg($shortCover));
  484. if (empty($parse['query'])) {
  485. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  486. } else {
  487. $cover = self::groupImg($shortCover);
  488. }
  489. }
  490. $list[$k]['cover'] = $cover;
  491. $list[$k]['prePrice'] = $v['price'] ?? 0;
  492. //今日特价、会员价
  493. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  494. if ($level == 0) {
  495. //零售做特价时显示的原价,不能删除
  496. if (isset($v['skPrice'])) {
  497. $list[$k]['prePrice'] = $v['skPrice'];
  498. }
  499. }
  500. if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
  501. //做特价花材时,花材列表和改价列表显示的会员价和零售价,不能删除
  502. $list[$k]['hjPrice'] = floatval($v['hjDiscountPrice']);
  503. $list[$k]['skPrice'] = floatval($v['skDiscountPrice']);
  504. }
  505. $list[$k]['price'] = $price;
  506. $cost = $v['avCost'] ?? 0;
  507. $ml = 0;
  508. if ($price > 0) {
  509. $profit = bcsub($price, $cost, 2);
  510. $ml = $profit;
  511. $mll = bcdiv($profit, $price, 3);
  512. $mll = bcmul($mll, 100);
  513. $mll = round($mll, 1);
  514. } else {
  515. $mll = 0;
  516. }
  517. $list[$k]['mll'] = $mll;
  518. $list[$k]['ml'] = $ml;
  519. }
  520. return $list;
  521. }
  522. //简单组合
  523. public static function simpleGroup($list, $level = 0)
  524. {
  525. //各个等级对应的price addPrice字段
  526. $priceMap = CustomClass::$levelPriceKeyMap;
  527. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  528. foreach ($list as $k => $v) {
  529. $shortCover = $v['cover'] ?? '';
  530. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  531. if (!empty($shortCover)) {
  532. $parse = parse_url(self::groupImg($shortCover));
  533. if (empty($parse['query'])) {
  534. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  535. } else {
  536. $cover = self::groupImg($shortCover);
  537. }
  538. }
  539. $list[$k]['cover'] = $cover;
  540. $list[$k]['prePrice'] = $v['price'] ?? 0;
  541. //今日特价、会员价
  542. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  543. //零售做特价时的原价
  544. if ($level == 0) {
  545. $currentAddPrice = $v['skMore'] ?? 0;
  546. $list[$k]['prePrice'] = bcadd($v['price'], $currentAddPrice, 2);
  547. }
  548. if (isset($v['discountPrice']) && $v['discountPrice'] > 0) {
  549. $currentAddPrice = $v['skMore'] ?? 0;
  550. $list[$k]['skPrice'] = bcadd($v['discountPrice'], $currentAddPrice, 2);
  551. }
  552. $list[$k]['price'] = $price;
  553. }
  554. return $list;
  555. }
  556. //通用花材列表数组组合
  557. public static function groupProductInfo($list, $level = 0, $custom = [])
  558. {
  559. //各个等级对应的price addPrice字段
  560. $priceMap = CustomClass::$levelPriceKeyMap;
  561. //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116
  562. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  563. //但这边加的参数是有用的,给指定客户涨价的
  564. $addPriceMap['risePercent'] = $custom['risePercent'] ?? 0;
  565. foreach ($list as $k => $v) {
  566. $shortCover = $v['cover'] ?? '';
  567. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  568. $bigCover = imgUtil::getPrefix() . 'hhb_small.png';
  569. $largeCover = imgUtil::getPrefix() . 'hhb_small.png';
  570. if (!empty($shortCover)) {
  571. $parse = parse_url(self::groupImg($shortCover));
  572. if (empty($parse['query'])) {
  573. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  574. } else {
  575. $cover = self::groupImg($shortCover);
  576. }
  577. $bigCover = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_700,w_700";
  578. $largeCover = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_990,w_990";
  579. }
  580. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
  581. $stockInfo = self::formatStock($v['stock'], $ratio);
  582. $list[$k]['cover'] = $cover;
  583. $list[$k]['bigCover'] = $bigCover;
  584. $list[$k]['largeCover'] = $largeCover;
  585. $list[$k]['shortCover'] = $shortCover;
  586. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  587. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  588. $list[$k]['itemName'] = $v['name'] ?? '';
  589. $list[$k]['prePrice'] = $v['price'] ?? 0;
  590. //今日特价、会员价
  591. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  592. $list[$k]['price'] = $price;
  593. $list[$k]['bigPrice'] = $price;
  594. $smallRatio = $v['smallRatio'] ?? 0;
  595. $smallPrice = bcdiv($price, $ratio, 2);
  596. $smallPrice = bcmul($smallPrice, $smallRatio, 2);
  597. $list[$k]['smallPrice'] = $smallPrice;
  598. $preSellDate = $v['presellDate'] ?? '';
  599. $preSellDateList = [];
  600. $preSellDateShow = [];
  601. if (!empty($preSellDate)) {
  602. $dateArr = explode(',', trim($preSellDate));
  603. if (!empty($dateArr)) {
  604. foreach ($dateArr as $sellTime) {
  605. $sellDate = date("Y-m-d", $sellTime);
  606. $showDate = date("n.j", $sellTime);
  607. $preSellDateList[] = $sellDate;
  608. $preSellDateShow[] = $showDate;
  609. }
  610. }
  611. }
  612. $list[$k]['presellDateList'] = $preSellDateList;
  613. $list[$k]['presellDateShow'] = $preSellDateShow;
  614. //这个很重要不能随意乱改
  615. $autoPrice = bcadd($v['cost'], $v['addPrice'], 2);
  616. $skMore = $v['skMore'] ?? 0;
  617. $skAutoPrice = bcadd($autoPrice, $skMore, 2);
  618. $list[$k]['autoPrice'] = $autoPrice;
  619. $list[$k]['autoSkPrice'] = $skAutoPrice;
  620. $list[$k]['userPrice'] = 0;
  621. // 限购缓存
  622. if ($v['limitBuy'] > 0) {
  623. $limitKey = self::LIMIT_BUY_KEY . $v['id'];
  624. $has = Yii::$app->redis->executeCommand('HEXISTS', [$limitKey, 0]);
  625. if (!empty($has)) {
  626. // 获取缓存超时时间
  627. $expire = Yii::$app->redis->executeCommand('TTL', [$limitKey]);
  628. if ($expire > 0) {
  629. $list[$k]['limitBuyClearTime'] = date('Y-m-d H:i:s', time() + $expire);
  630. } else {
  631. $list[$k]['limitBuyClearTime'] = '';
  632. }
  633. } else {
  634. $list[$k]['limitBuyClearTime'] = '';
  635. }
  636. }
  637. }
  638. return $list;
  639. }
  640. //零售端花店采购
  641. public static function hdCgGroup($list, $level = 0, $ghs = [])
  642. {
  643. //各个等级对应的price addPrice字段
  644. $priceMap = CustomClass::$levelPriceKeyMap;
  645. //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116
  646. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  647. //这里加的参数是有用的 ssh 20260116
  648. $addPriceMap['risePercent'] = $ghs['risePercent'] ?? 0;
  649. foreach ($list as $k => $v) {
  650. $shortCover = $v['cover'] ?? '';
  651. $cover = imgUtil::getPrefix() . 'hhb_small.png';
  652. if (!empty($shortCover)) {
  653. $parse = parse_url(self::groupImg($shortCover));
  654. if (empty($parse['query'])) {
  655. $cover = self::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_100,w_100";
  656. } else {
  657. $cover = self::groupImg($shortCover);
  658. }
  659. }
  660. $ratio = isset($v['ratio']) && $v['ratio'] > 0 ? $v['ratio'] : 1;
  661. $stockInfo = self::formatStock($v['stock'], $ratio);
  662. $list[$k]['cover'] = $cover;
  663. $list[$k]['prePrice'] = $v['price'] ?? 0;
  664. $list[$k]['bigNum'] = $stockInfo['bigNum'];
  665. $list[$k]['smallNum'] = $stockInfo['smallNum'];
  666. $list[$k]['itemName'] = $v['name'] ?? '';
  667. //今日特价、会员价
  668. $price = self::getFinalPrice($v, $level, $priceMap, $addPriceMap);
  669. $defaultGradePrice = 0;
  670. if ($level == 2) {
  671. //如果是大客户,取出更低一级的客户价格,用于前端对比展示:显示原价和会员价
  672. $littleLevel = 1;
  673. $defaultGradePrice = self::getFinalPrice($v, $littleLevel, $priceMap, $addPriceMap);
  674. }
  675. $list[$k]['defaultGradePrice'] = $defaultGradePrice;
  676. $list[$k]['myLevel'] = $level;
  677. $reachPrice = 0;
  678. //满10扎多少钱
  679. if (isset($v['reachNum']) && $v['reachNum'] > 0) {
  680. $reachNumDiscount = $v['reachNumDiscount'] ?? 0;
  681. $reachPrice = bcsub($price, $reachNumDiscount, 2);
  682. $reachPrice = $reachPrice <= 0 ? 0.01 : floatval($reachPrice);
  683. }
  684. $list[$k]['reachPrice'] = $reachPrice;
  685. $list[$k]['price'] = $price;
  686. $list[$k]['bigPrice'] = $price;
  687. $smallRatio = $v['smallRatio'] ?? 0;
  688. $smallPrice = bcdiv($price, $ratio, 2);
  689. $smallPrice = bcmul($smallPrice, $smallRatio, 2);
  690. $list[$k]['smallPrice'] = $smallPrice;
  691. $preSellDate = $v['presellDate'] ?? '';
  692. $preSellDateShow = [];
  693. if (!empty($preSellDate)) {
  694. $dateArr = explode(',', trim($preSellDate));
  695. if (!empty($dateArr)) {
  696. foreach ($dateArr as $sellTime) {
  697. $showDate = date("n.j", $sellTime);
  698. $preSellDateShow[] = $showDate;
  699. }
  700. }
  701. }
  702. $list[$k]['presellDateShow'] = $preSellDateShow;
  703. unset($list[$k]['presellDate']);
  704. unset($list[$k]['addPrice']);
  705. unset($list[$k]['cost']);
  706. unset($list[$k]['skMore']);
  707. }
  708. return $list;
  709. }
  710. /**
  711. * 输出价格 ssh 20211009
  712. * @param $product 花材信息
  713. * @param $level 等级
  714. * @param $priceMap 各个等级对应的price addPrice字段
  715. * @param $params 原来是 $addPriceMap,现在 $addPriceMap 值没用了,把它改成 $params ,用作传别的参数
  716. * @param int $changePrice 开单改价
  717. * @param int $buyNum 当前购买数量
  718. * @param int $totalBuyNum 已购买总数量
  719. * @return int|string|null
  720. * @throws \Exception
  721. */
  722. public static function getFinalPrice($product, $level, $priceMap, $params, $changePrice = 0, $buyNum = 0, $totalBuyNum = 0)
  723. {
  724. //开单改价优先级最高
  725. if ($changePrice > 0) {
  726. return $changePrice;
  727. }
  728. if (empty($product) || isset($product['price']) == false) {
  729. return 0;
  730. }
  731. if ($level == 9) {
  732. $avCost = $product['avCost'] ?? 0;
  733. $price = $avCost;
  734. if ($price <= 0) {
  735. $cost = $product['cost'] ?? 0;
  736. $price = $cost;
  737. }
  738. } else {
  739. $currentPriceField = $priceMap[$level] ?? 'price';
  740. $price = $product[$currentPriceField] ?? 0;
  741. }
  742. //特价与限购的综合处理
  743. $discountPrice = $product['discountPrice'] ?? 0;
  744. $hjDiscountPrice = $product['hjDiscountPrice'] ?? 0;
  745. $skDiscountPrice = $product['skDiscountPrice'] ?? 0;
  746. $limitBuy = $product['limitBuy'];
  747. if ($discountPrice > 0) {
  748. $oldPrice = $price;
  749. if ($level == 0) {
  750. $price = $skDiscountPrice;
  751. }
  752. if ($level == 1) {
  753. $price = $discountPrice;
  754. }
  755. if ($level == 2) {
  756. $price = $hjDiscountPrice;
  757. }
  758. //超出限购情况的价格计算
  759. if ($limitBuy > 0 && $totalBuyNum > $limitBuy) {
  760. $hasBuyNum = $totalBuyNum - $buyNum;
  761. if ($hasBuyNum >= $limitBuy) {
  762. $price = $oldPrice;
  763. } else {
  764. $overBuyNum = $totalBuyNum - $limitBuy;
  765. $overBuyPrice = bcmul($overBuyNum, $oldPrice, 2); // 超出限购部分的价格
  766. $leftBuyNum = $buyNum - $overBuyNum;
  767. $leftBuyPrice = bcmul($leftBuyNum, $price, 2); // 未超出限购部分的价格
  768. $price = bcdiv($overBuyPrice + $leftBuyPrice, $buyNum, 2); // 最终价格
  769. }
  770. }
  771. }
  772. //满多少数量,再优惠多少钱
  773. $reachNum = $product['reachNum'] ?? 0;
  774. $reachNumDiscount = $product['reachNumDiscount'] ?? 0;
  775. if ($reachNum > 0) {
  776. if ($buyNum >= $reachNum) {
  777. if ($reachNumDiscount >= $price) {
  778. util::fail('优惠价比售价还大');
  779. }
  780. $price = bcsub($price, $reachNumDiscount, 2);
  781. }
  782. }
  783. //这个 $risePercent 可能是正整数,也可能是负整数,如果是10表示$price涨价10%,如果是-10表示降价10%,同时$price降价后,不能小于0.01
  784. $risePercent = $params['risePercent'] ?? 0;
  785. if ($risePercent != 0) {
  786. // risePercent为正数表示涨价百分比,负数表示降价百分比
  787. $priceAdjustment = bcmul($price, bcdiv($risePercent, 100, 4), 2);
  788. $price = bcadd($price, $priceAdjustment, 2);
  789. // 降价后不能小于0.01
  790. if ($price < 0.01) {
  791. $price = 0.01;
  792. }
  793. }
  794. return $price;
  795. }
  796. //根据花材ID,库存=》 计算出多少扎,多少支
  797. public static function formatStockByItemId($itemId, $stock)
  798. {
  799. $unitNum = xhItemService::getItemUnitNum($itemId);
  800. return self::formatStock($stock, $unitNum);
  801. }
  802. //花材的库存转换: 库存是小数,根据各个花材的unitNum, 计算出多少扎,多少支
  803. public static function formatStock($stock, $unitNum)
  804. {
  805. $pn = true; //正数
  806. if ($stock < 0) {
  807. $pn = false;
  808. $stock = abs($stock);
  809. }
  810. $bigNum = floor($stock); // 扎
  811. $stockArr = explode('.', $stock);
  812. $smallNum = 0; //支
  813. if (count($stockArr) === 2) {
  814. $smallNum = bcmul(($stock - $bigNum), $unitNum);
  815. }
  816. if (!$pn) {
  817. $bigNum *= -1;
  818. $smallNum *= -1;
  819. }
  820. return [
  821. 'bigNum' => $bigNum,
  822. 'smallNum' => $smallNum,
  823. ];
  824. }
  825. //根据 花材ID,将花材大小数量 换算成小数
  826. public static function mergeItemNumByItemId($bigNum, $smallNum, $itemId)
  827. {
  828. $unitNum = xhItemService::getItemUnitNum($itemId);
  829. return self::mergeItemNum($bigNum, $smallNum, $unitNum);
  830. }
  831. //花材扎、支 合并转换 小数
  832. /**
  833. * @param $bigNum 大单位 数量
  834. * @param $smallNum 小单位 数量
  835. * @param $unitNum 花材比例
  836. * @return string
  837. */
  838. public static function mergeItemNum($bigNum, $smallNum, $unitNum)
  839. {
  840. $stockDecimal = 0;
  841. if ($unitNum > 0) {
  842. $stockDecimal = bcdiv($smallNum, $unitNum, 2);
  843. }
  844. $itemNum = bcadd($bigNum, $stockDecimal, 2);
  845. return $itemNum;
  846. }
  847. //获取常用的itemIds
  848. public static function getOftenItemIds($shopId)
  849. {
  850. $data = self::getLimitList("itemId", ['shopId' => $shopId], 10, ['actualSold' => SORT_DESC, 'inTurn' => SORT_DESC]);
  851. if ($data) {
  852. $data = array_column($data, 'itemId');
  853. }
  854. return $data;
  855. }
  856. /**
  857. * 库存变动统一入口:用数据库行锁串行化同一花材的读-算-写,避免并发覆盖库存。
  858. * 出现并发时,会自动重试3次。
  859. * @param $productId
  860. * @param callable $callback
  861. * @return mixed
  862. * @throws \Exception
  863. */
  864. private static function runStockMutation($productId, callable $callback)
  865. {
  866. $db = Yii::$app->db;
  867. $currentTransaction = $db->getTransaction();
  868. $ownTransaction = empty($currentTransaction) || !$currentTransaction->isActive;
  869. $mutation = function () use ($db, $currentTransaction, $ownTransaction, $productId, $callback) {
  870. $transaction = $ownTransaction ? $db->beginTransaction() : $currentTransaction;
  871. try {
  872. $productData = self::getLockById($productId);
  873. if (!$productData) {
  874. util::fail("花材不存在");
  875. }
  876. $respond = call_user_func($callback, $productData);
  877. if ($ownTransaction) {
  878. $transaction->commit();
  879. }
  880. return $respond;
  881. } catch (\Exception $exception) {
  882. if ($ownTransaction && !empty($transaction) && $transaction->isActive) {
  883. $transaction->rollBack();
  884. }
  885. throw $exception;
  886. }
  887. };
  888. if (!$ownTransaction) {
  889. return call_user_func($mutation);
  890. }
  891. return util::runWithDbConcurrencyRetry($mutation);
  892. }
  893. //加库存,允许负库存,负数数量(盘点时)
  894. public static function addStock($productId, $itemNumBundle, $itemNumPiece)
  895. {
  896. //小单位库存不再考虑 ssh 20230303
  897. //$itemNumPiece = 0;
  898. $callback = function ($productData) use ($itemNumBundle, $itemNumPiece) {
  899. $oldStock = $productData->stock;
  900. $ratio = $productData->ratio;
  901. //转成小数,加库存
  902. $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio);
  903. $newStock = bcadd($oldStock, $itemNum, 2);
  904. $productData->stock = $newStock;
  905. //上架
  906. if ($newStock > 0 && $productData->status == 2) {
  907. $productData->status = 1;
  908. }
  909. $productData->save();
  910. return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum];
  911. };
  912. return self::runStockMutation($productId, $callback);
  913. }
  914. //加路上库存
  915. public static function addOnStockByItemNum($productId, $itemNum)
  916. {
  917. $callback = function ($productInfo) use ($itemNum) {
  918. $oldOnStock = $productInfo->onStock;
  919. $newOnStock = bcadd($oldOnStock, $itemNum);
  920. $productInfo->onStock = $newOnStock;
  921. $productInfo->save();
  922. return ['oldOnStock' => $oldOnStock, 'newOnStock' => $newOnStock, 'itemNum' => $itemNum];
  923. };
  924. return self::runStockMutation($productId, $callback);
  925. }
  926. //加库存
  927. public static function addStockByItemNum($productId, $itemNum, $params = [])
  928. {
  929. $callback = function ($productData) use ($itemNum, $params) {
  930. $oldStock = $productData->stock;
  931. $newItemCost = 0;
  932. $newItemStock = 0;
  933. $avCost = 0;
  934. $changeCost = 0;
  935. $unitCost = 0;
  936. //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost
  937. if (!empty($params['cost']) && $params['cost'] > 0) {
  938. //原库存和原成本,也叫剩余库存和剩余成本
  939. $oldItemStock = $productData->stock ?? 0;
  940. $oldAvCost = $productData->avCost ?? 0;
  941. $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2);
  942. //变动库存和成本
  943. $unitCost = $params['cost'];
  944. $changeCost = bcmul($params['cost'], $itemNum, 2);
  945. //新库存和新成本
  946. $newItemCost = bcadd($changeCost, $oldItemCost, 2);
  947. $newItemStock = bcadd($itemNum, $oldItemStock, 2);
  948. if ($newItemStock > 0) {
  949. $avCost = bcdiv($newItemCost, $newItemStock, 2);
  950. } else {
  951. $newItemStock = 0;
  952. $newItemCost = 0;
  953. }
  954. $productData->avCost = $avCost;
  955. $productData->totalCost = $newItemCost;
  956. $productData->totalStock = $newItemStock;
  957. }
  958. $newStock = bcadd($oldStock, $itemNum, 2);
  959. $productData->stock = $newStock;
  960. //上架
  961. if ($newStock > 0 && $productData->status == 2) {
  962. $productData->status = 1;
  963. }
  964. //如果花材删除了则要恢复,用于调拨出库时,入库店的花材被删除
  965. if ($newStock > 0 && $productData->delStatus == 1) {
  966. $productData->delStatus = 0;
  967. }
  968. $productData->save();
  969. return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum,
  970. 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost];
  971. };
  972. return self::runStockMutation($productId, $callback);
  973. }
  974. //减库存,允许负库存,负数数量(盘点时)
  975. //checkStock boolean 是否需要判断库存
  976. //addSold boolean 是否增加销量,报损和拆散不要增加销量
  977. public static function decreaseStock($productId, $itemNumBundle, $itemNumPiece, $checkStock = false, $addSold = true, $params = [])
  978. {
  979. //小单位库存不再考虑 ssh 20230303
  980. //$itemNumPiece = 0;
  981. $callback = function ($productData) use ($productId, $itemNumBundle, $itemNumPiece, $checkStock, $addSold, $params) {
  982. $oldStock = $productData->stock;
  983. $ratio = $productData->ratio;
  984. //转成小数,更新库存
  985. $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio);
  986. $newStock = bcsub($oldStock, $itemNum, 2);
  987. $avCost = 0;
  988. $newItemStock = 0;
  989. $newItemCost = 0;
  990. $changeCost = 0;
  991. $unitCost = 0;
  992. //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost
  993. if (!empty($params['cost']) && $params['cost'] > 0) {
  994. //变成库存和变成成本
  995. $unitCost = $params['cost'];
  996. $changeCost = bcmul($params['cost'], $itemNum, 2);
  997. //原库存和原成本,也叫剩余库存和剩余成本
  998. $oldItemStock = $productData->stock;
  999. $oldAvCost = $productData->avCost ?? 0;
  1000. $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2);
  1001. //新库存和新成本
  1002. $newItemCost = bcsub($oldItemCost, $changeCost, 2);
  1003. $newItemStock = bcsub($oldItemStock, $itemNum, 2);
  1004. if ($newItemStock > 0) {
  1005. $avCost = bcdiv($newItemCost, $newItemStock, 2);
  1006. } else {
  1007. $newItemStock = 0;
  1008. $newItemCost = 0;
  1009. }
  1010. //如果减库存之后,还有剩余库存,则变更新的平均成本。如果没有库存不再变更平均成本。如没有库存了还变更,会造成全部调拨出库时,保存的成本是0,重要!!!!!!!!!!
  1011. //确实是,如果没有库存了,平均成本也不能再变化了。如果再变化,平均成本也是变成0。变成0后,如果再盘点几个库存出来,会变成没有成本的库存。 ssh 20251130
  1012. if ($newStock > 0) {
  1013. $productData->avCost = $avCost;
  1014. }
  1015. $productData->totalCost = $newItemCost;
  1016. $productData->totalStock = $newItemStock;
  1017. }
  1018. if ($checkStock && $newStock < 0) {
  1019. //需要判断库存是否充足
  1020. $currentName = $productData->name ?? '';
  1021. $currentRemainStock = floatval($oldStock);
  1022. util::fail("{$currentName} 只剩{$currentRemainStock}{$productData->bigUnit}");
  1023. }
  1024. $productData->stock = $newStock;
  1025. if ($addSold) {
  1026. //减库存,加销量 2021.6.21 lqh
  1027. $actualSold = $productData->actualSold;
  1028. $productData->actualSold = bcadd($actualSold, $itemNum, 2);
  1029. }
  1030. //如果花材是自动补充库存类型的花材,则库存低于900时,自动补充到98520,多处要同步修改,关键词 auto_add_stock
  1031. if ($productData->virtualStock == 1 && $newStock < 9900) {
  1032. $productData->stock = 98520;
  1033. }
  1034. //库存=0 下架商品,特价去掉,限购去掉 lqh 2021.7.9 ssh 20251106 花材满减去掉 20260318
  1035. if ($newStock <= 0) {
  1036. $productData->status = 2;
  1037. //限购数量重置为 0,但会把限购不为 0 的数据记录到缓存
  1038. self::createRecoverLimitBuyCache($productId, $productData->limitBuy);
  1039. }
  1040. $productData->save();
  1041. return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum,
  1042. 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost];
  1043. };
  1044. return self::runStockMutation($productId, $callback);
  1045. }
  1046. //根据数量减库存
  1047. public static function decreaseStockByItemNum($productId, $itemNum, $checkStock = false)
  1048. {
  1049. $callback = function ($productData) use ($itemNum, $checkStock) {
  1050. $oldStock = $productData->stock;
  1051. $newStock = bcsub($oldStock, $itemNum, 2);
  1052. if ($checkStock && $newStock < 0) {
  1053. //需要判断库存是否充足
  1054. util::fail("库存不足");
  1055. }
  1056. $productData->stock = $newStock;
  1057. //减库存,加销量 2021.6.21 lqh
  1058. $actualSold = $productData->actualSold;
  1059. $productData->actualSold = bcadd($actualSold, $itemNum, 2);
  1060. $productData->save();
  1061. return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum];
  1062. };
  1063. return self::runStockMutation($productId, $callback);
  1064. }
  1065. //减少在路上的库存
  1066. public static function decreaseOnStockByItemNum($productId, $itemNum, $checkStock = false)
  1067. {
  1068. $callback = function ($productInfo) use ($itemNum, $checkStock) {
  1069. $oldOnStock = $productInfo->onStock;
  1070. $newOnStock = bcsub($oldOnStock, $itemNum);
  1071. if ($checkStock && $newOnStock < 0) {
  1072. //需要判断库存是否充足
  1073. util::fail("库存不足");
  1074. }
  1075. $productInfo->onStock = $newOnStock;
  1076. $productInfo->save();
  1077. return ['oldOnStock' => $oldOnStock, 'newOnStock' => $newOnStock, 'itemNum' => $itemNum];
  1078. };
  1079. return self::runStockMutation($productId, $callback);
  1080. }
  1081. //计算价格
  1082. public static function computedPrice($productId, $bigNum, $smallNum)
  1083. {
  1084. //获取itemId的价格
  1085. $productData = self::getById($productId);
  1086. if (!$productData) {
  1087. util::fail("花材不存在");
  1088. }
  1089. $price = $productData['price'];
  1090. $itemId = $productData['itemId'];
  1091. $unitNum = xhItemService::getItemUnitNum($itemId);
  1092. //换算出一支多少钱
  1093. $piecePrice = 0;
  1094. if ($unitNum > 0) {
  1095. //有支
  1096. $unitPrice = bcdiv($price, $unitNum, 2);
  1097. $piecePrice = bcmul($unitPrice, $smallNum, 2);
  1098. }
  1099. $bundlePrice = bcmul($price, $bigNum, 2);
  1100. $total = bcadd($bundlePrice, $piecePrice, 2);
  1101. return $total;
  1102. }
  1103. //根据门店ID, 平台花材ID, 获取门店的花材ID
  1104. public static function getGhsProductId($shopId, $itemId)
  1105. {
  1106. $data = self::getGhsProductDataByItemId($shopId, $itemId);
  1107. if ($data) {
  1108. return $data['id'];
  1109. }
  1110. return 0;
  1111. }
  1112. public static function getGhsProductDataByItemId($shopId, $itemId)
  1113. {
  1114. $where = ['shopId' => $shopId, 'itemId' => $itemId];
  1115. $data = self::getByCondition($where);
  1116. return $data;
  1117. }
  1118. //根据多个itemIds shopId 获取对应的 productId lqh 2021.1.24
  1119. public static function getGhsProductDataByItemIds($mainId, $itemIds)
  1120. {
  1121. $where = ['itemId' => ['in', $itemIds], 'mainId' => $mainId];
  1122. $data = self::getAllByCondition($where, null, "*");
  1123. return $data;
  1124. }
  1125. //补全itemId (针对入库时 当前门店下无对应的花材 itemId) lqh 2021.1.24
  1126. public static function complementProduct($sjId, $mainId, $shopId, $itemInfo)
  1127. {
  1128. $itemIds = array_column($itemInfo, "itemId");
  1129. $oldProductIds = array_column($itemInfo, "productId");
  1130. $productData = self::getGhsProductDataByItemIds($mainId, $itemIds);
  1131. $defaultClass = ItemClassClass::getByCondition(['mainId' => $mainId, 'isDefault' => 1], true);
  1132. $defaultClassId = $defaultClass->id ?? 0;
  1133. if (empty($defaultClassId)) {
  1134. util::fail('没有找到默认分类');
  1135. }
  1136. $shop = ShopClass::getById($shopId, true);
  1137. if (empty($shop)) {
  1138. util::fail('没有找到门店24');
  1139. }
  1140. if (count($productData) !== count($itemIds)) {
  1141. $productDataItemIds = array_column($productData, 'itemId');
  1142. //取差集
  1143. $diffItemIds = array_diff($itemIds, $productDataItemIds);
  1144. if ($diffItemIds) {
  1145. $oldProductInfo = self::getAllByCondition(['id' => ['in', $oldProductIds]], null, '*', 'itemId');
  1146. //新增到product表,price = itemPrice (出库时的售价), stock=0
  1147. foreach ($diffItemIds as $v) {
  1148. $addData = [];
  1149. $addData['sjId'] = $sjId;
  1150. $addData['shopId'] = $shopId;
  1151. $addData['mainId'] = $mainId;
  1152. $addData['itemId'] = $v;
  1153. $addData['name'] = $oldProductInfo[$v]['name'] ?? '';
  1154. $addData['price'] = $oldProductInfo[$v]['price'] ?? 0;
  1155. $addData['classId'] = $defaultClassId;
  1156. $addData['smallRatio'] = $oldProductInfo[$v]['smallRatio'] ?? 2;
  1157. $addData['skPrice'] = $oldProductInfo[$v]['skPrice'] ?? 0;
  1158. $addData['hjPrice'] = $oldProductInfo[$v]['hjPrice'] ?? 0;
  1159. $addData['cost'] = $oldProductInfo[$v]['cost'] ?? 0;
  1160. $addData['addPrice'] = $oldProductInfo[$v]['addPrice'] ?? 0;
  1161. $addData['skAddPrice'] = $oldProductInfo[$v]['skAddPrice'] ?? 0;
  1162. $addData['hjAddPrice'] = $oldProductInfo[$v]['hjAddPrice'] ?? 0;
  1163. $addData['stockWarning'] = $oldProductInfo[$v]['stockWarning'] ?? 5;
  1164. $addData['weight'] = $oldProductInfo[$v]['weight'] ?? 0;
  1165. $addData['alias'] = $oldProductInfo[$v]['alias'] ?? '';
  1166. $addData['itemRemark'] = $oldProductInfo[$v]['itemRemark'] ?? '';
  1167. $addData['py'] = $oldProductInfo[$v]['py'] ?? '';
  1168. $addData['ratio'] = $oldProductInfo[$v]['ratio'] ?? 20;
  1169. $addData['bigUnit'] = $oldProductInfo[$v]['bigUnit'] ?? '';
  1170. $addData['smallUnit'] = $oldProductInfo[$v]['smallUnit'] ?? '';
  1171. $addData['bigUnitId'] = $oldProductInfo[$v]['bigUnitId'] ?? 0;
  1172. $addData['smallUnitId'] = $oldProductInfo[$v]['smallUnitId'] ?? 0;
  1173. $addData['status'] = 1;
  1174. $addData['delStatus'] = 0;
  1175. $addData['inTurn'] = 100;
  1176. self::addProduct($addData, $shop);
  1177. }
  1178. }
  1179. }
  1180. }
  1181. public static function check($info, $mainId)
  1182. {
  1183. if (empty($info)) {
  1184. util::fail('没有花材信息');
  1185. }
  1186. if (isset($info->mainId) == false || $info->mainId != $mainId) {
  1187. util::fail('无效花材');
  1188. }
  1189. return true;
  1190. }
  1191. //有效花材判断,包括id有效、同个门店等 ssh 2021.1.19
  1192. public static function valid($list, $mainId)
  1193. {
  1194. $ids = array_unique(array_filter(array_column($list, 'productId')));
  1195. $productList = self::getByIds($ids, null, 'id');
  1196. if (!empty($productList)) {
  1197. foreach ($productList as $product) {
  1198. if (isset($product['mainId']) == false || $product['mainId'] != $mainId) {
  1199. $name = $product['name'] ?? '';
  1200. $pMainId = $product['mainId'] ?? 0;
  1201. //noticeUtil::push("只能选择同一家的商品哦!{$name} {$pMainId} {$mainId}", '15280215347');
  1202. util::fail($name . ' 不是一家的商品,请清空重选');
  1203. }
  1204. }
  1205. if (count($productList) != count($ids)) {
  1206. util::fail('存在无效商品');
  1207. }
  1208. } else {
  1209. util::fail('商品不存在');
  1210. }
  1211. }
  1212. //通过ids 批量获取花材商品的信息
  1213. public static function getProductByIds($ids)
  1214. {
  1215. return self::getByIds($ids);
  1216. }
  1217. //修改花材商品的库存: 统一方法
  1218. public static function updateStockById($id, $stock, $adminId = 0)
  1219. {
  1220. $callback = function ($productData) use ($stock, $adminId) {
  1221. $productData->stock = $stock;
  1222. //如果有库存需要保持上架状态
  1223. if ($stock > 0) {
  1224. $productData->status = 1;
  1225. }
  1226. if ($adminId) {
  1227. $productData->adminId = $adminId;
  1228. }
  1229. return $productData->save();
  1230. };
  1231. return self::runStockMutation($id, $callback);
  1232. }
  1233. //订单退回库存
  1234. public static function backStockByOrderItemInfo($orderItemInfo)
  1235. {
  1236. foreach ($orderItemInfo as $v) {
  1237. self::addStockByItemNum($v['productId'], $v['itemNum']);
  1238. }
  1239. }
  1240. //获取当前门店下商品信息 lqh 2021.1.28
  1241. public static function getProductData($id, $mainId, $obj = false)
  1242. {
  1243. return self::getByCondition(['id' => $id, 'mainId' => $mainId], $obj);
  1244. }
  1245. //单个修改价格 lqh 2021.1.28
  1246. public static function changeSinglePrice($shop, $ptItemId, $price, $skPrice = null, $hjPrice = null, $params = [])
  1247. {
  1248. $mainId = $shop->mainId ?? 0;
  1249. $productInfo = self::getByCondition(['itemId' => $ptItemId, 'mainId' => $mainId, 'delStatus' => 0], true);
  1250. if (empty($productInfo)) {
  1251. return false;
  1252. }
  1253. if (empty($price)) {
  1254. util::fail('没有批发价');
  1255. }
  1256. $discountPrice = $productInfo->discountPrice ?? 0;
  1257. $discountPrice = floatval($discountPrice);
  1258. $name = $productInfo->name ?? '';
  1259. if ($discountPrice > 0) {
  1260. if ($discountPrice >= $price) {
  1261. //util::fail($name . " 价格应该大于特价");
  1262. }
  1263. }
  1264. $productInfo->price = $price;
  1265. if (empty($skPrice)) {
  1266. util::fail('没有零售价');
  1267. }
  1268. if (empty($hjPrice)) {
  1269. util::fail('没有会员价');
  1270. }
  1271. $productInfo->skPrice = $skPrice;
  1272. $productInfo->hjPrice = $hjPrice;
  1273. $productInfo->save();
  1274. $sjId = $shop->sjId ?? 0;
  1275. $itemId = $productInfo->id ?? 0;
  1276. $ptItemId = $productInfo->itemId ?? 0;
  1277. $name = $productInfo->name ?? '';
  1278. $cover = $productInfo->cover ?? '';
  1279. $staffId = $params['staffId'] ?? 0;
  1280. $staffName = $params['staffName'] ?? '';
  1281. $changeType = $params['changeType'] ?? 'normal';
  1282. $targetId = $params['targetId'] ?? 0;
  1283. $event = $params['event'] ?? '修改';
  1284. $type = 0;
  1285. if ($changeType == 'cg') {
  1286. $event = '采购后修改';
  1287. $type = 1;
  1288. }
  1289. $changeData = [
  1290. 'ptStyle' => 2,
  1291. 'sjId' => $sjId,
  1292. 'mainId' => $mainId,
  1293. 'itemId' => $itemId,
  1294. 'ptItemId' => $ptItemId,
  1295. 'price' => $price,
  1296. 'skPrice' => $skPrice,
  1297. 'hjPrice' => $hjPrice,
  1298. 'staffId' => $staffId,
  1299. 'staffName' => $staffName,
  1300. 'name' => $name,
  1301. 'cover' => $cover,
  1302. 'event' => $event,
  1303. 'type' => $type,
  1304. 'targetId' => $targetId,
  1305. ];
  1306. PriceChangeClass::addData($changeData);
  1307. return true;
  1308. }
  1309. //下单时计算商品的价格 lqh 2021.1.28
  1310. public static function formatProductInfo($itemInfo, $level = 0, $live = 1, $moreParams = [])
  1311. {
  1312. $data = [];
  1313. if (empty($itemInfo)) {
  1314. util::fail('请选择花材,编号 179612');
  1315. }
  1316. $itemInfo = self::mergeItemInfo($itemInfo);
  1317. $productIds = array_column($itemInfo, 'productId');
  1318. $ghsProductData = self::getProductByIds($productIds);
  1319. $ghsProductData = array_column($ghsProductData, NULL, 'id');
  1320. // if ($live == 1) {
  1321. // $hdProductData = [];
  1322. // } else {
  1323. // $hdIds = array_column($itemInfo, 'hdProductId');
  1324. // $hdProductData = self::getByIds($hdIds, null, 'id');
  1325. // }
  1326. $totalPrice = 0;
  1327. $totalBigNum = 0;
  1328. $totalSmallNum = 0;
  1329. $book = $moreParams['book'] ?? 0;
  1330. $customShopAdminId = $moreParams['customShopAdminId'] ?? 0;
  1331. $custom = $moreParams['custom'] ?? [];
  1332. $skipLimitBuy = $moreParams['skipLimitBuy'] ?? false;
  1333. //不同等级对应 price addPrice
  1334. $priceMap = CustomClass::$levelPriceKeyMap;
  1335. //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116
  1336. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  1337. //但这边加的参数是有用的,给指定客户涨价的
  1338. $addPriceMap['risePercent'] = $custom['risePercent'] ?? 0;
  1339. $totalItemNum = 0;
  1340. //满减累计
  1341. $totalReachDiscount = 0;
  1342. $totalCost = 0;
  1343. foreach ($itemInfo as $v) {
  1344. $ghsProductId = $v['productId'];
  1345. $currentGhsProduct = $ghsProductData[$ghsProductId] ?? [];
  1346. if (empty($currentGhsProduct)) {
  1347. noticeUtil::push("ghsProductId={$ghsProductId} 在 ghsProductData 中为空");
  1348. continue;
  1349. }
  1350. $itemId = $currentGhsProduct['itemId'];
  1351. $ratio = $currentGhsProduct['ratio'];
  1352. $cost = $currentGhsProduct['avCost'];
  1353. $classId = $currentGhsProduct['classId'];
  1354. $bigNum = $v['bigNum'] ?? 0;
  1355. $smallNum = $v['smallNum'] ?? 0;
  1356. //大小数量合并多少扎
  1357. $itemNum = self::mergeItemNum($bigNum, $smallNum, $ratio);
  1358. $totalItemNum = bcadd($totalItemNum, $itemNum, 2);
  1359. if ($live == 1) {
  1360. //供货商开单可以传单价过来
  1361. $changePrice = isset($v['price']) && $v['price'] > 0 ? $v['price'] : 0;
  1362. //如果零售店自己下预订单,预付0.1元,有二个地方要同步修改,搜索关键词 pre0.1
  1363. if ($book == 1 && $customShopAdminId > 0) {
  1364. $itemPrice = 0.1;
  1365. } else {
  1366. $priceBuyNum = $skipLimitBuy ? 0 : $itemNum;
  1367. $totalBuyNum = 0;
  1368. if ($skipLimitBuy == false) {
  1369. $limitKey = self::LIMIT_BUY_KEY . $ghsProductId;
  1370. $hasBuyNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $custom['id']]);
  1371. $totalBuyNum = $itemNum + ($hasBuyNum == null ? 0 : $hasBuyNum); // 已购买总数量
  1372. }
  1373. //今日特价、会员价
  1374. $itemPrice = self::getFinalPrice($currentGhsProduct, $level, $priceMap, $addPriceMap, $changePrice, $priceBuyNum, $totalBuyNum);
  1375. }
  1376. $thisPrice = $itemPrice;
  1377. $reachNum = $currentGhsProduct['reachNum'] ?? 0;
  1378. if ($reachNum > 0) {
  1379. if ($itemNum >= $reachNum) {
  1380. $reachNumDiscount = $currentGhsProduct['reachNumDiscount'] ?? 0;
  1381. $currentReachDiscount = bcmul($reachNumDiscount, $itemNum, 2);
  1382. $totalReachDiscount = bcadd($totalReachDiscount, $currentReachDiscount, 2);
  1383. }
  1384. }
  1385. } else {
  1386. $itemPrice = $v['unitPrice'] ?? 0;
  1387. $thisPrice = $itemPrice;
  1388. }
  1389. if ($smallNum > 0) {
  1390. $price = bcmul($smallNum, $itemPrice, 2);
  1391. } else {
  1392. $price = bcmul($bigNum, $itemPrice, 2);
  1393. }
  1394. $tmp = [];
  1395. $tmp['productId'] = $ghsProductId;
  1396. $tmp['price'] = $price;
  1397. $tmp['name'] = $currentGhsProduct['name'] ?? '';
  1398. $tmp['smallUnit'] = $currentGhsProduct['smallUnit'] ?? '支';
  1399. $tmp['bigUnit'] = $currentGhsProduct['bigUnit'] ?? '扎';
  1400. $tmp['cover'] = $currentGhsProduct['cover'] ?? '';
  1401. $tmp['itemId'] = $itemId;
  1402. $tmp['rank'] = $currentGhsProduct['rank'] ?? '';
  1403. $tmp['ratio'] = $currentGhsProduct['ratio'] ?? 1;
  1404. $tmp['ratioType'] = $currentGhsProduct['ratioType'] ?? 0;
  1405. $tmp['variety'] = $currentGhsProduct['variety'] ?? 0;
  1406. $tmp['unitPrice'] = $thisPrice;
  1407. $tmp['unitWeight'] = $currentGhsProduct['weight'] ?? 0;;
  1408. $tmp['num'] = $itemNum;
  1409. $tmp['bigNum'] = $v['bigNum'];
  1410. $tmp['smallNum'] = $v['smallNum'];
  1411. $tmp['preNum'] = $itemNum;
  1412. $tmp['preBigNum'] = $v['bigNum'];
  1413. $tmp['preSmallNum'] = $v['smallNum'];
  1414. $tmp['remark'] = $v['remark'] ?? '';
  1415. $tmp['userPrice'] = $price;
  1416. $tmp['weight'] = $currentGhsProduct['weight'] ?? 0;
  1417. $tmp['sjId'] = $currentGhsProduct['sjId'] ?? 0;
  1418. $tmp['shopId'] = $currentGhsProduct['shopId'] ?? 0;
  1419. $tmp['mainId'] = $currentGhsProduct['mainId'] ?? 0;
  1420. $tmp['limitBuy'] = $currentGhsProduct['limitBuy'];
  1421. $tmp['smallUnitPrice'] = $itemPrice;
  1422. $tmp['classId'] = $classId;
  1423. $tmp['kind'] = $currentGhsProduct['kind'] ?? 0;
  1424. $tmp['belongCost'] = $currentGhsProduct['belongCost'] ?? 0;
  1425. $tmp['hjDiscountPrice'] = $currentGhsProduct['hjDiscountPrice'];
  1426. $tmp['discountPrice'] = $currentGhsProduct['discountPrice'];
  1427. $tmp['skDiscountPrice'] = $currentGhsProduct['skDiscountPrice'];
  1428. $totalPrice = bcadd($totalPrice, $price, 2);
  1429. $totalBigNum += $bigNum;
  1430. $totalSmallNum += $smallNum;
  1431. if ($bigNum > 0) {
  1432. $xhUnitType = dict::getDict('unitType', 'big');
  1433. $xhNum = $bigNum;
  1434. $xhUnitName = $tmp['bigUnit'] ?? '';
  1435. } else {
  1436. $xhUnitType = dict::getDict('unitType', 'small');
  1437. $xhNum = $smallNum;
  1438. $xhUnitName = $tmp['smallUnit'] ?? '';
  1439. $smallCost = bcdiv($cost, $ratio, 2);
  1440. $cost = $smallCost;
  1441. }
  1442. $xhPrice = bcmul($xhNum, $itemPrice, 2);
  1443. $tmp['cost'] = $cost;
  1444. $tmp['xhNum'] = $xhNum;
  1445. $tmp['xhUnitName'] = $xhUnitName;
  1446. $tmp['xhUnitType'] = $xhUnitType;
  1447. $tmp['xhUnitPrice'] = $itemPrice;
  1448. $tmp['xhPrice'] = $xhPrice;
  1449. $tmp['xhPreNum'] = $xhNum;
  1450. $tmp['xhPreUnitName'] = $xhUnitName;
  1451. $tmp['xhPreUnitType'] = $xhUnitType;
  1452. $tmp['xhPreUnitPrice'] = $itemPrice;
  1453. $tmp['xhPrePrice'] = $xhPrice;
  1454. $data[] = $tmp;
  1455. $currentCost = bcmul($xhNum, $cost, 2);
  1456. $totalCost = bcadd($totalCost, $currentCost, 2); // TODO 根据限购与特价进行价格计算的关键参数
  1457. }
  1458. return ['product' => $data, 'totalReachDiscount' => $totalReachDiscount, 'smallNum' => $totalSmallNum,
  1459. 'bigNum' => $totalBigNum, 'price' => $totalPrice, 'totalItemNum' => $totalItemNum, 'totalCost' => $totalCost];
  1460. }
  1461. // //修改某个花材的成本价(采购之后要用到) lqh 2021.04.06
  1462. public static function changeCost($productId, $cost)
  1463. {
  1464. return self::updateById($productId, ['cost' => $cost]);
  1465. }
  1466. //更新某个花材的加价 2021.04.06
  1467. public static function changeAddPrice($productId, $addPrice)
  1468. {
  1469. return self::updateById($productId, ['addPrice' => $addPrice]);
  1470. }
  1471. //根据productId 计算花材的重量
  1472. public static function getWeight($productId, $bigNum, $smallNum)
  1473. {
  1474. $productInfo = self::getById($productId);
  1475. if (empty($productInfo)) {
  1476. return 0;
  1477. }
  1478. $ratio = $productInfo['ratio'];
  1479. $weight = $productInfo['weight'];
  1480. $mergeNum = self::mergeItemNum($bigNum, $smallNum, $ratio);
  1481. return bcmul($mergeNum, $weight, 2);
  1482. }
  1483. /**
  1484. * 修改product
  1485. * @param $data
  1486. * @param $shop
  1487. * @param $type 所属平台:ghs 或 hd
  1488. * @throws \Exception
  1489. */
  1490. public static function updateProduct($data, $shop, $type)
  1491. {
  1492. $id = $data['id'] ?? 0;
  1493. $adminId = $data['adminId'] ?? 0;
  1494. $sjId = $shop->sjId ?? 0;
  1495. $shopId = $shop->id ?? 0;
  1496. $mainId = $shop->mainId ?? 0;
  1497. $product = self::getLockById($id);
  1498. if (empty($product)) {
  1499. util::fail("该花材不存在");
  1500. }
  1501. if (isset($data['limitBuy']) && intval($data['limitBuy']) > 0) {
  1502. if ($product->limitBuy == 0) {
  1503. $type == 'ghs' ? self::clearLimitBuyCache($id) : \bizHd\product\classes\ProductClass::clearLimitBuyCache($id);
  1504. }
  1505. } else {
  1506. $type == 'ghs' ? self::clearLimitBuyCache($id) : \bizHd\product\classes\ProductClass::clearLimitBuyCache($id);
  1507. }
  1508. //扫码收款、直接收款,不包括特价商品,不可修改,多处要同步修改 system_item
  1509. if (getenv('YII_ENV') == 'production') {
  1510. $systemItem = [78076, 80960];
  1511. $adminList = [4, 1960];
  1512. } else {
  1513. $systemItem = [11157, 11156];
  1514. $adminList = [919, 932];
  1515. }
  1516. if (in_array($product->itemId, $systemItem)) {
  1517. if (!in_array($adminId, $adminList)) {
  1518. util::fail('系统花材,不可修改');
  1519. }
  1520. }
  1521. $name = $product->name ?? '';
  1522. $cover = $product->cover ?? '';
  1523. $upData = [];
  1524. //库存可以修改
  1525. if (isset($data['stock']) && is_numeric($data['stock'])) {
  1526. $preStock = $product->stock ?? 0;
  1527. if ($data['stock'] != $preStock) {
  1528. if ($data['stock'] < 0) {
  1529. util::fail("库存不能小于0");
  1530. }
  1531. if ($data['stock'] > 9999) {
  1532. util::fail('库存数不能超过4个9');
  1533. }
  1534. //增加盘点记录 2021.7.27
  1535. $ratio = $product->ratio ?? 0;
  1536. $formNum = self::formatStock($data['stock'], $ratio);
  1537. $ghsItemInfo = [['productId' => $product->id, 'bigNum' => $formNum['bigNum'], 'smallNum' => $formNum['smallNum'],]];
  1538. $pdData = [];
  1539. $pdData['itemInfo'] = $ghsItemInfo;
  1540. $pdData['sjId'] = $sjId;
  1541. $pdData['shopId'] = $shopId;
  1542. $pdData['adminId'] = $data['adminId'];
  1543. $pdData['mainId'] = $mainId;
  1544. $pdData['staffName'] = $data['staffName'] ?? '';
  1545. CheckOrderClass::opOrder($pdData, false, false);
  1546. }
  1547. unset($data['stock']);
  1548. }
  1549. unset($data['adminId']);
  1550. if (isset($data['name'])) {
  1551. if (empty($data['name'])) {
  1552. util::fail("请输入花材名称");
  1553. }
  1554. $upData['name'] = $data['name'];
  1555. }
  1556. if (!empty($data['py'])) {
  1557. $upData['py'] = $data['py'];
  1558. }
  1559. if (!empty($data['cover'])) {
  1560. $upData['cover'] = $data['cover'];
  1561. }
  1562. if (!empty($data['images'])) {
  1563. $upData['images'] = $data['images'];
  1564. }
  1565. if (isset($data['discountPrice']) && is_numeric($data['discountPrice'])) {
  1566. $discountPrice = $data['discountPrice'] ?? 0;
  1567. $currentPrice = $data['price'] ?? 0;
  1568. if ($discountPrice >= $currentPrice) {
  1569. util::fail('特价要小于售价');
  1570. }
  1571. $upData['discountPrice'] = $discountPrice;
  1572. if ($discountPrice > 0) {
  1573. if ($product->presell == 1) {
  1574. util::fail('预售花材不能做特价');
  1575. }
  1576. }
  1577. $hjDiscountPrice = isset($data['hjDiscountPrice']) && is_numeric($data['hjDiscountPrice']) ? $data['hjDiscountPrice'] : 0;
  1578. $skDiscountPrice = isset($data['skDiscountPrice']) && is_numeric($data['skDiscountPrice']) ? $data['skDiscountPrice'] : 0;
  1579. if ($hjDiscountPrice > $discountPrice) {
  1580. util::fail('批发特价要大于会员特价');
  1581. }
  1582. if ($discountPrice > $skDiscountPrice) {
  1583. util::fail('零售特价要大于批发特价');
  1584. }
  1585. $upData['hjDiscountPrice'] = $hjDiscountPrice;
  1586. $upData['skDiscountPrice'] = $skDiscountPrice;
  1587. }
  1588. $cost = $data['cost'] ?? 0;
  1589. $price = $data['price'] ?? 0;
  1590. $price = floatval($price);
  1591. $skPrice = $data['skPrice'] ?? 0;
  1592. $skPrice = floatval($skPrice);
  1593. $hjPrice = $data['hjPrice'] ?? 0;
  1594. $hjPrice = floatval($hjPrice);
  1595. if (!is_numeric($price) || $price <= 0) {
  1596. util::fail('请填写价格');
  1597. }
  1598. if ($hjPrice > $price) {
  1599. util::fail('批发价要大于会员价');
  1600. }
  1601. if ($price > $skPrice) {
  1602. util::fail('零售价要大于批发价');
  1603. }
  1604. if (isset($data['addPrice']) && is_numeric($data['addPrice'])) {
  1605. $upData['addPrice'] = $data['addPrice'];
  1606. }
  1607. if (isset($data['skMore']) && is_numeric($data['skMore'])) {
  1608. $upData['skMore'] = $data['skMore'];
  1609. }
  1610. if (isset($data['hjAddPrice']) && is_numeric($data['hjAddPrice'])) {
  1611. $upData['hjAddPrice'] = $data['hjAddPrice'];
  1612. }
  1613. if (floatval($price) != floatval($product->price) || floatval($skPrice) != floatval($product->skPrice) || floatval($hjPrice) != floatval($product->hjPrice)) {
  1614. if ($shop->default == 0 && $shop->join == 0) {
  1615. //除了小蔡鲜花的蔡总、郑总、苏南3007,其他人直营分店不能改价格,多处要同步修改,关键词 direct_price_change
  1616. if (!in_array($adminId, [35725, 23659, 29264])) {
  1617. util::fail('直营店不能改价哦');
  1618. }
  1619. }
  1620. }
  1621. if (isset($data['limitBuy']) && is_numeric($data['limitBuy'])) {
  1622. $upData['limitBuy'] = $data['limitBuy'];
  1623. //取消限购
  1624. if ($data['limitBuy'] == 0) {
  1625. if ($type == 'ghs') {
  1626. self::clearLimitBuyCache($id);
  1627. } else {
  1628. \bizHd\product\classes\ProductClass::clearLimitBuyCache($id);
  1629. }
  1630. }
  1631. }
  1632. if (isset($data['stockWarning'])) {
  1633. $upData['stockWarning'] = $data['stockWarning'];
  1634. }
  1635. //重量不可修改
  1636. unset($data['weight']);
  1637. if (isset($data['inTurn'])) {
  1638. $upData['inTurn'] = $data['inTurn'];
  1639. }
  1640. if (array_key_exists('itemRemark', $data)) {
  1641. $itemRemark = trim($data['itemRemark'] ?? '');
  1642. if (mb_strlen($itemRemark) > 200) {
  1643. util::fail('备注不能超过200字');
  1644. }
  1645. $upData['itemRemark'] = $itemRemark;
  1646. }
  1647. if (isset($data['delStatus'])) {
  1648. if (!in_array($data['delStatus'], [0, 1])) {
  1649. util::fail("删除错误");
  1650. }
  1651. $upData['delStatus'] = $data['delStatus'];
  1652. if ($data['delStatus'] == 1) {
  1653. $upData['discountPrice'] = 0;
  1654. }
  1655. }
  1656. if (isset($data['status']) && is_numeric($data['status'])) {
  1657. $upData['status'] = $data['status'];
  1658. if ($data['status'] == 2) {
  1659. $upData['discountPrice'] = 0;
  1660. }
  1661. }
  1662. if (isset($data['frontHide']) && is_numeric($data['frontHide'])) {
  1663. $upData['frontHide'] = $data['frontHide'];
  1664. }
  1665. if (isset($data['frontUse']) && is_numeric($data['frontUse'])) {
  1666. $upData['frontUse'] = $data['frontUse'];
  1667. }
  1668. if (isset($data['variety']) && is_numeric($data['variety'])) {
  1669. $upData['variety'] = $data['variety'];
  1670. }
  1671. //分类ID
  1672. if (isset($data["classId"])) {
  1673. $classId = $data['classId'];
  1674. $upData['classId'] = $classId;
  1675. if (isset($product->classId) && $product->classId != $classId) {
  1676. //如果有修改分类,则在多个订单表中同步修改,如需修改,多个地方要同步修改,请搜索关键词item_class_modify_sync
  1677. $itemClassKey = 'item_class_modify_sync';
  1678. Yii::$app->redis->executeCommand('LPUSH', [$itemClassKey, $id]);
  1679. }
  1680. }
  1681. if (isset($data["scanNum"]) && is_numeric($data["scanNum"])) {
  1682. $upData['scanNum'] = $data['scanNum'];
  1683. }
  1684. if (isset($data["reachNum"]) && $data["reachNum"] > 0) {
  1685. $upData['reachNum'] = $data['reachNum'];
  1686. if (!isset($data['reachNumDiscount']) || $data['reachNumDiscount'] <= 0) {
  1687. util::fail('请填写满减的优惠金额');
  1688. }
  1689. $reachNumDiscount = $data['reachNumDiscount'];
  1690. $upData['reachNumDiscount'] = $reachNumDiscount;
  1691. if ($reachNumDiscount >= $hjPrice) {
  1692. util::fail('优惠金额不能大于售价');
  1693. }
  1694. if (isset($upData['hjDiscountPrice']) && $upData['hjDiscountPrice'] > 0) {
  1695. if ($reachNumDiscount >= $upData['hjDiscountPrice']) {
  1696. util::fail('优惠金额不能大于特价');
  1697. }
  1698. }
  1699. if ($product->variety == 1) {
  1700. util::fail('多颜色暂不支持满减');
  1701. }
  1702. } else {
  1703. $upData['reachNum'] = 0;
  1704. $upData['reachNumDiscount'] = 0;
  1705. }
  1706. if (isset($data["smallRatio"]) && is_numeric($data["smallRatio"])) {
  1707. $upData['smallRatio'] = $data['smallRatio'];
  1708. }
  1709. if (isset($data['ratioType'])) {
  1710. $upData['ratioType'] = $data['ratioType'];
  1711. }
  1712. if (isset($data["smallShow"]) && is_numeric($data["smallShow"])) {
  1713. $upData['smallShow'] = $data['smallShow'];
  1714. }
  1715. if (isset($data["cost"]) && is_numeric($data["cost"])) {
  1716. $upData['cost'] = $data['cost'];
  1717. }
  1718. if (isset($data["virtualStock"]) && is_numeric($data["virtualStock"])) {
  1719. $upData['virtualStock'] = $data['virtualStock'];
  1720. }
  1721. $paramsBack = $upData;
  1722. $ptItemId = $product->itemId ?? 0;
  1723. $staffId = $data['staffId'] ?? 0;
  1724. $staffName = $data['staffName'] ?? '';
  1725. $changeParams = ['staffId' => $staffId, 'staffName' => $staffName];
  1726. if ($price != floatval($product->price) || $skPrice != floatval($product->skPrice) || $hjPrice != floatval($product->hjPrice)) {
  1727. self::changeSinglePrice($shop, $ptItemId, $price, $skPrice, $hjPrice, $changeParams);
  1728. unset($upData['price']);
  1729. unset($upData['skPrice']);
  1730. unset($upData['hjPrice']);
  1731. if (!empty($mainId)) {
  1732. //提醒零售收银台界面要刷新,多处要修改,关键词 modify_price_remind_cashier
  1733. $staffList = ShopAdminClass::getAllByCondition(['mainId' => $mainId], null, '*', null, true);
  1734. if (!empty($staffList)) {
  1735. foreach ($staffList as $staff) {
  1736. $staffId = $staff->id ?? 0;
  1737. Yii::$app->redis->executeCommand('SET', ['cashier_' . $mainId . '_' . $staffId . '_may_refresh', 'yes']);
  1738. Yii::$app->redis->executeCommand('SET', ['ghs_cashier_' . $mainId . '_' . $staffId . '_may_refresh', 'yes']);
  1739. }
  1740. }
  1741. }
  1742. }
  1743. if ($cost != $product->cost) {
  1744. $changeData = [
  1745. 'ptStyle' => 2,
  1746. 'sjId' => $sjId,
  1747. 'mainId' => $mainId,
  1748. 'itemId' => $id,
  1749. 'ptItemId' => $ptItemId,
  1750. 'cost' => $cost,
  1751. 'staffId' => $staffId,
  1752. 'staffName' => $staffName,
  1753. 'name' => $name ?? '',
  1754. 'cover' => $cover,
  1755. 'type' => 2,
  1756. 'targetId' => $id ?? 0,
  1757. 'event' => '修改',
  1758. ];
  1759. CostChangeClass::addData($changeData);
  1760. }
  1761. self::updateById($id, $upData);
  1762. //创建限购缓存
  1763. $limitBuy = $data['limitBuy'] ?? 0;
  1764. if ($limitBuy > 0) {
  1765. $limitBuyClearType = $data['limitBuyClearType'];
  1766. $limitBuyClearLoopConfig = self::getLimitBuyClearLoopConfig($data);
  1767. if ($limitBuyClearType == 1 && !empty($limitBuyClearLoopConfig)) {
  1768. if ($type == 'ghs') {
  1769. self::createRecurringLimitBuyCache($id, $limitBuyClearLoopConfig['intervalDays'], $limitBuyClearLoopConfig['clearHour']);
  1770. } else {
  1771. \bizHd\product\classes\ProductClass::createRecurringLimitBuyCache($id, $limitBuyClearLoopConfig['intervalDays'], $limitBuyClearLoopConfig['clearHour']);
  1772. }
  1773. } elseif ($limitBuyClearType == 2 && isset($data['limitBuyClearTime']) && $data['limitBuyClearTime'] != '') {
  1774. $limitBuyClearTime = strtotime($data['limitBuyClearTime']);
  1775. if ($limitBuyClearTime > time()) {
  1776. if ($type == 'ghs') {
  1777. self::createLimitBuyCache($id, $limitBuyClearTime - time());
  1778. } else {
  1779. \bizHd\product\classes\ProductClass::createLimitBuyCache($id, $limitBuyClearTime - time());
  1780. }
  1781. } else {
  1782. util::fail('清空时间必须大于当前时间');
  1783. }
  1784. } else { // 没有设置清空时间,则删除缓存
  1785. if ($type == 'ghs') {
  1786. self::createLimitBuyCache($id, 0);
  1787. } else {
  1788. \bizHd\product\classes\ProductClass::createLimitBuyCache($id, 0);
  1789. }
  1790. }
  1791. }
  1792. //不影响其它直营店花材的上下架
  1793. unset($upData['status']);
  1794. if (isset($shop->default) && $shop->default == 1 && isset($shop->dataSync) && $shop->dataSync == 1) {
  1795. //总店修改同步到所有门店
  1796. $chainShopList = ShopClass::getAllByCondition(['sjId' => $sjId], null, '*', null, true);
  1797. foreach ($chainShopList as $chainShop) {
  1798. $chainShopId = $chainShop->id ?? 0;
  1799. if ($chainShopId == $shop->id) {
  1800. //前面已经添加过了
  1801. continue;
  1802. }
  1803. $chainMainId = $chainShop->mainId ?? 0;
  1804. $params = $paramsBack;
  1805. //所有门店上下架、启用停用等不同步
  1806. unset($params['status']);
  1807. unset($params['delStatus']);
  1808. unset($params['limitBuy']);
  1809. $staff = ShopAdminClass::getByCondition(['mainId' => $chainMainId, 'adminId' => $adminId], true);
  1810. $staffId = $staff->id ?? 0;
  1811. $staffName = $staff->name ?? '';
  1812. $changeParams2 = ['staffId' => $staffId, 'staffName' => $staffName];
  1813. $chainProduct = self::getByCondition(['mainId' => $chainMainId, 'itemId' => $ptItemId], true);
  1814. if (empty($chainProduct)) {
  1815. continue;
  1816. }
  1817. //如果有同步分类则移动,没有则不移动
  1818. if (isset($params['classId'])) {
  1819. $masterClassId = $params['classId'];
  1820. unset($params['classId']);
  1821. $masterClass = ItemClassClass::getById($masterClassId, true);
  1822. $masterClassName = $masterClass->name ?? '';
  1823. $classList = ItemClassClass::getAllByCondition(['mainId' => $chainMainId], null, '*', null, true);
  1824. if (!empty($classList)) {
  1825. foreach ($classList as $currentClass) {
  1826. if ($currentClass->name == $masterClassName) {
  1827. $params['classId'] = $currentClass->id ?? 0;
  1828. if (isset($chainProduct->classId) && $chainProduct->classId != $params['classId']) {
  1829. //如果有修改分类,则在多个订单表中同步修改,如需修改,多个地方要同步修改,请搜索关键词item_class_modify_sync
  1830. $itemClassKey = 'item_class_modify_sync';
  1831. Yii::$app->redis->executeCommand('LPUSH', [$itemClassKey, $chainProduct->id]);
  1832. }
  1833. }
  1834. }
  1835. }
  1836. }
  1837. //加盟店仅同步名称等非重要参数
  1838. if ($chainShop->join == 1) {
  1839. unset($params['skPrice']);
  1840. unset($params['price']);
  1841. unset($params['hjPrice']);
  1842. unset($params['addPrice']);
  1843. unset($params['skMore']);
  1844. unset($params['hjAddPrice']);
  1845. unset($params['cost']);
  1846. //特价的金额也不能同步,淘花里
  1847. unset($params['hjDiscountPrice']);
  1848. unset($params['discountPrice']);
  1849. unset($params['skDiscountPrice']);
  1850. //满减也不能同步,惠雅鲜花批发
  1851. unset($params['reachNum']);
  1852. unset($params['reachNumDiscount']);
  1853. self::updateById($chainProduct->id, $params);
  1854. continue;
  1855. }
  1856. if ($price != $chainProduct->price) {
  1857. //批发价有变化
  1858. self::changeSinglePrice($chainShop, $ptItemId, $price, $skPrice, $hjPrice, $changeParams2);
  1859. unset($params['price']);
  1860. }
  1861. if ($cost != $chainProduct->cost) {
  1862. $changeData = [
  1863. 'ptStyle' => 2,
  1864. 'sjId' => $chainShop->sjId ?? 0,
  1865. 'mainId' => $chainShop->mainId ?? 0,
  1866. 'itemId' => $chainProduct->id,
  1867. 'ptItemId' => $ptItemId,
  1868. 'cost' => $cost,
  1869. 'staffId' => $staffId,
  1870. 'staffName' => $staffName,
  1871. 'name' => $chainProduct->name ?? '',
  1872. 'cover' => $chainProduct->cover,
  1873. 'type' => 2,
  1874. 'targetId' => $chainProduct->id ?? 0,
  1875. 'event' => '修改',
  1876. ];
  1877. CostChangeClass::addData($changeData);
  1878. }
  1879. //直营店同步价格等重要因素
  1880. self::updateById($chainProduct->id, $params);
  1881. }
  1882. }
  1883. }
  1884. //变成自动改价 2021.4.14
  1885. public static function autoPriceById($productId)
  1886. {
  1887. $product = self::getById($productId, true);
  1888. if (empty($product)) {
  1889. return false;
  1890. }
  1891. $price = bcadd($product->cost, $product->addPrice, 2);
  1892. self::changePrice($product, $price, self::PRICE_LABEL_AUTO);
  1893. }
  1894. //获取当前门店下所有的product
  1895. public static function getAllProduct($sjId, $shopId, $field = "*")
  1896. {
  1897. $where = [
  1898. 'sjId' => $sjId,
  1899. 'shopId' => $shopId,
  1900. ];
  1901. return self::getAllByCondition($where, null, $field);
  1902. }
  1903. //获取当前门店下所有花材的库存数量,及成本价,库存预警的花材数量 lqh 2021-04-20
  1904. public static function getProductStockByShopId($sjId, $shopId)
  1905. {
  1906. $productInfo = self::getAllProduct($sjId, $shopId, "*");
  1907. $data = [
  1908. 'stockBigNum' => 0,
  1909. 'stockSmallNum' => 0,
  1910. 'stockCost' => 0,
  1911. 'stockWarning' => 0,
  1912. ];
  1913. if ($productInfo) {
  1914. foreach ($productInfo as $v) {
  1915. $stock = self::formatStock($v['stock'], $v['ratio']);
  1916. $data['stockBigNum'] = bcadd($data['stockBigNum'], $stock['bigNum']);
  1917. $data['stockSmallNum'] = bcadd($data['stockSmallNum'], $stock['smallNum']);
  1918. $data['stockCost'] = bcadd($data['stockCost'], bcmul($v['stock'], $v['cost'], 2), 2);
  1919. if ($v['stock'] <= $v['stockWarning']) {
  1920. $data['stockWarning']++;
  1921. }
  1922. }
  1923. }
  1924. return $data;
  1925. }
  1926. //补className
  1927. public static function groupClassName($mainId, $data)
  1928. {
  1929. $classNameMap = ItemClassClass::getAllByCondition(['mainId' => $mainId], null, 'id,name', 'id');
  1930. foreach ($data as $k => $v) {
  1931. $classId = $v['classId'];
  1932. $className = isset($classNameMap[$classId]) && isset($classNameMap[$classId]['name']) ? $classNameMap[$classId]['name'] : '';
  1933. $v['className'] = $className;
  1934. $data[$k] = $v;
  1935. }
  1936. return $data;
  1937. }
  1938. //供货商的productId => 零售端的productId ,不存在的补productId(放在默认分类)
  1939. //productList [{"productId":"113","bigNum":1,"smallNum":0,"classId":"-2","itemId":1}]
  1940. public static function toggleProductList($productList, $hdShopId, $hdSjId, $hdMainId, $ghs)
  1941. {
  1942. $itemIds = array_column($productList, "itemId");
  1943. $hdProductData = self::getAllByCondition(['mainId' => $hdMainId, 'itemId' => ['in', $itemIds]], null, '*', 'itemId', true);
  1944. $defaultClassId = ItemClassClass::getDefaultClassId($hdMainId);
  1945. if (empty($defaultClassId)) {
  1946. util::fail('没有找到花材的默认分类');
  1947. }
  1948. foreach ($productList as $k => $v) {
  1949. $itemId = $v['itemId'] ?? 0;
  1950. $ghsProductId = $v['productId'] ?? 0;
  1951. if (isset($hdProductData[$itemId]) == false) {
  1952. //不存在,则从平台新增
  1953. $ptItem = PtItemClass::getById($itemId);
  1954. unset($ptItem['addTime']);
  1955. unset($ptItem['updateTime']);
  1956. $productData = $ptItem;
  1957. $productData['sjId'] = $hdSjId;
  1958. $productData['shopId'] = $hdShopId;
  1959. $productData['mainId'] = $hdMainId;
  1960. $productData['itemId'] = $itemId;
  1961. $productData['classId'] = $defaultClassId;
  1962. unset($productData['id']);
  1963. $productData['rank'] = 'A';
  1964. //计算价格
  1965. $cost = $v['cost'] ?? 0;
  1966. $addPrice = $v['addPrice'] ?? 0;
  1967. $price = bcadd($cost, $addPrice, 2);
  1968. $userPrice = $v['userPrice'] ?? 0;
  1969. if ($cost <= 0 && $price <= 0) {
  1970. if ($userPrice > 0) {
  1971. $price = $userPrice;
  1972. $productData['cost'] = $price;
  1973. } else {
  1974. $price = 28;
  1975. $productData['cost'] = 28;
  1976. }
  1977. $productData['skMore'] = 5;
  1978. $productData['addPrice'] = 4;
  1979. $productData['hjAddPrice'] = 3;
  1980. }
  1981. $productData['price'] = $price;
  1982. $productData['skPrice'] = $price;
  1983. $productData['hjPrice'] = $price;
  1984. $productData['status'] = 2;
  1985. //被归到默认分类,排序值要小,这样在花材列表搜索时,默认分类才不会被排在前面
  1986. $productData['inTurn'] = -1;
  1987. $addProduct = self::addBaseData($productData);
  1988. $hdProductId = $addProduct->id ?? 0;
  1989. //通知新花材需要分类
  1990. //NotifyClass::cgGetNewProduct($addProduct, $hdMainId, $hdShopId, $ghs);
  1991. } else {
  1992. $current = $hdProductData[$itemId];
  1993. $hdProductId = $current->id ?? 0;
  1994. $current->delStatus = 0;
  1995. $current->save();
  1996. }
  1997. $productList[$k]['productId'] = $hdProductId;
  1998. $productList[$k]['ghsProductId'] = $ghsProductId;
  1999. }
  2000. return $productList;
  2001. }
  2002. //供货商、零售端新开门店时,初始化花材
  2003. public static function newShopInitProduct($sjId, $shopId, $newShopId, $adminId)
  2004. {
  2005. $product = self::getAllProduct($sjId, $shopId, "name,cover,classId,itemId,rank,price,priceLabel,cost,addPrice,stockWarning,weight,alias,py,ratio,bigUnit,smallUnit,bigUnitId,smallUnitId,inTurn");
  2006. $productData = [];
  2007. foreach ($product as $v) {
  2008. $tmp = $v;
  2009. $tmp['sjId'] = $sjId;
  2010. $tmp['shopId'] = $newShopId;
  2011. $tmp['adminId'] = $adminId;
  2012. $productData[] = $tmp;
  2013. }
  2014. self::batchAdd($productData);
  2015. }
  2016. //花材销量增加
  2017. public static function updateActualSoldById($id, $itemNum)
  2018. {
  2019. $data['actualSold'] = $itemNum;
  2020. $res = self::updateById($id, $data);
  2021. return $res;
  2022. }
  2023. //新增花材
  2024. public static function addProduct($data, $currentShop)
  2025. {
  2026. $currentMainId = $currentShop->mainId ?? 0;
  2027. $classId = $data['classId'] ?? 0;
  2028. $mainId = $data['mainId'] ?? 0;
  2029. $stockWarning = $data['stockWarning'] ?? 5;
  2030. $data['py'] = stringUtil::py($data['name']);
  2031. $name = $data['name'] ?? '';
  2032. $data['alias'] = $data['alias'] ?? '';
  2033. $data['cover'] = !empty($data['cover']) ? $data['cover'] : '';
  2034. $ratioType = $data['ratioType'] ?? 0;
  2035. if ($ratioType == 0) {
  2036. if (!isset($data['ratio']) || $data['ratio'] <= 0) {
  2037. util::fail('请填写几支装');
  2038. }
  2039. $ratio = $data['ratio'];
  2040. } else {
  2041. $ratio = 10;
  2042. $data['ratio'] = 10;
  2043. }
  2044. $weight = $data['weight'] ?? 0;
  2045. $bigUnitId = $data['bigUnitId'] ?? 0;
  2046. $smallUnitId = $data['smallUnitId'] ?? 0;
  2047. if (empty($bigUnitId) || empty($smallUnitId)) {
  2048. util::fail('请选择单位');
  2049. }
  2050. $unitList = UnitClass::getByIds([$bigUnitId, $smallUnitId], null, 'id');
  2051. $bigUnit = $unitList[$bigUnitId]['name'] ?? '扎';
  2052. $smallUnit = $unitList[$smallUnitId]['name'] ?? '支';
  2053. $data['bigUnit'] = $bigUnit;
  2054. $data['smallUnit'] = $smallUnit;
  2055. $data['shopId'] = $data['shopId'] ?? 0;
  2056. $currentShopId = $data['shopId'];
  2057. $data['sjId'] = $data['sjId'] ?? 0;
  2058. if (!$data['sjId']) {
  2059. util::fail("参数错误");
  2060. }
  2061. $stock = 0;
  2062. if (isset($data['stock']) && is_numeric($data['stock'])) {
  2063. if ($data['stock'] < 0) {
  2064. util::fail("库存数不能小于0");
  2065. }
  2066. if ($data['stock'] > 999999) {
  2067. util::fail('库存数不能超过6个9');
  2068. }
  2069. $stock = $data['stock'];
  2070. }
  2071. //没有库存则下架
  2072. $data['status'] = $stock == 0 ? 2 : 1;
  2073. if (empty($classId)) {
  2074. util::fail("请选择分类");
  2075. }
  2076. if (!isset($data['price']) || !is_numeric($data['price']) || $data['price'] < 0) {
  2077. util::fail("请输入价格");
  2078. }
  2079. if (!isset($data['cost']) || !is_numeric($data['cost']) || $data['cost'] < 0) {
  2080. util::fail("请输入成本价");
  2081. }
  2082. if (!isset($data['addPrice']) || !is_numeric($data['addPrice'])) {
  2083. util::fail("请输入加价");
  2084. }
  2085. if ($stockWarning === false) {
  2086. util::fail("请输入库存预警值");
  2087. }
  2088. if ($name === false) {
  2089. util::fail("请输入花材名称");
  2090. }
  2091. if ($weight === false) {
  2092. util::fail("请输入花材重量");
  2093. }
  2094. if (isset($data['itemRemark'])) {
  2095. $itemRemark = trim($data['itemRemark'] ?? '');
  2096. if (mb_strlen($itemRemark) > 200) {
  2097. util::fail('备注不能超过200字');
  2098. }
  2099. $data['itemRemark'] = $itemRemark;
  2100. }
  2101. $cat = ItemClassClass::getById($classId, true);
  2102. if (empty($cat)) {
  2103. util::fail("花材分类不存在");
  2104. }
  2105. if ($cat->mainId != $mainId) {
  2106. util::fail('不是您的分类');
  2107. }
  2108. $catName = $cat->name ?? '';
  2109. if (isset($data['discountPrice']) && is_numeric($data['discountPrice'])) {
  2110. $discountPrice = $data['discountPrice'];
  2111. $currentPrice = $data['price'] ?? 0;
  2112. if ($discountPrice >= $currentPrice) {
  2113. util::fail('特价要小于售价');
  2114. }
  2115. $hjDiscountPrice = isset($data['hjDiscountPrice']) && is_numeric($data['hjDiscountPrice']) ? $data['hjDiscountPrice'] : 0;
  2116. $skDiscountPrice = isset($data['skDiscountPrice']) && is_numeric($data['skDiscountPrice']) ? $data['skDiscountPrice'] : 0;
  2117. if ($hjDiscountPrice > $discountPrice) {
  2118. util::fail('批发特价要大于会员特价');
  2119. }
  2120. if ($discountPrice > $skDiscountPrice) {
  2121. util::fail('零售特价要大于批发特价');
  2122. }
  2123. }
  2124. $connection = Yii::$app->db;
  2125. $transaction = $connection->beginTransaction();
  2126. try {
  2127. $ptItemId = $data['itemId'] ?? 0;
  2128. $ptItemInfo = PtItemClass::getById($ptItemId, true);
  2129. $bigUnitId = $data['bigUnitId'] ?? 0;
  2130. $smallUnitId = $data['smallUnitId'] ?? 0;
  2131. $changeUnit = 0;
  2132. if ($ptItemInfo !== null && ($ptItemInfo->bigUnitId != $bigUnitId || $ptItemInfo->smallUnitId != $smallUnitId || $ptItemInfo->ratio != $ratio)) {
  2133. //如果被修改了单位,则重新创建花材
  2134. $changeUnit = 1;
  2135. }
  2136. if (empty($ptItemId) || $changeUnit == 1) {
  2137. //添加新品种先在平台新增花材
  2138. $defaultInfo = PtItemClassClass::getByCondition(['isDefault' => 1]);
  2139. $defaultClassId = $defaultInfo['id'] ?? 0;
  2140. if (empty($defaultClassId)) {
  2141. util::fail('没有找到平台默认分类');
  2142. }
  2143. $itemData = $data;
  2144. $itemData['classId'] = $defaultClassId;
  2145. $itemData['skAddPrice'] = $itemData['skAddPrice'] ?? 3;
  2146. $itemData['hjAddPrice'] = $itemData['hjAddPrice'] ?? 2;
  2147. $itemData['zsAddPrice'] = $itemData['zsAddPrice'] ?? 1;
  2148. $ptItemInfo = PtItemClass::addItem($itemData);
  2149. $data['itemId'] = $ptItemInfo['id'];
  2150. } else {
  2151. //从平台导入花材
  2152. $has = self::getByCondition(['mainId' => $mainId, 'itemId' => $data['itemId']], true);
  2153. if (!empty($has)) {
  2154. $hasName = $has->name ?? '';
  2155. util::fail($hasName . '已经存在了');
  2156. }
  2157. }
  2158. //小菊的结构
  2159. $data['variety'] = $ptItemInfo->variety ?? 0;
  2160. //批发价比进货价贵多少 2021-12-07 lqh 如果前端有传取前端的,没有则取pt
  2161. $addPrice = $data['addPrice'] ?? $ptItemInfo->addPrice;
  2162. $data['addPrice'] = $addPrice;
  2163. //批发价
  2164. $price = $data['price'] ?? 0;
  2165. if (empty($price) || $price <= 0) {
  2166. util::fail('请填写价格');
  2167. }
  2168. $skPrice = $data['skPrice'] ?? 0;
  2169. if (empty($skPrice) || $skPrice <= 0) {
  2170. util::fail('请填写零售价');
  2171. }
  2172. $hjPrice = $data['hjPrice'] ?? 0;
  2173. if (empty($hjPrice) || $hjPrice <= 0) {
  2174. util::fail('请填写会员价');
  2175. }
  2176. if ($hjPrice > $price) {
  2177. util::fail('批发价要大于会员价');
  2178. }
  2179. if ($price > $skPrice) {
  2180. util::fail('零售价要大于批发价');
  2181. }
  2182. $data['property'] = 1;
  2183. $respond = self::addBaseData($data);
  2184. if ($stock > 0) {
  2185. //有库存数量,增加盘盈记录 2021.7.9 lqh
  2186. //增加盘点订单
  2187. $pdOrderSn = orderSn::getGhsCheckSn();
  2188. $pdOrderData = [];
  2189. $pdOrderData['orderSn'] = $pdOrderSn;
  2190. $pdOrderData['sjId'] = $data['sjId'];
  2191. $pdOrderData['shopId'] = $data['shopId'];
  2192. $pdOrderData['status'] = CheckOrderClass::STATUS_COMPLETE;
  2193. $pdOrderData['bigNum'] = $stock;
  2194. $pdOrderData['adminId'] = $data['adminId'];
  2195. $pdOrderData['smallNum'] = 0;
  2196. $pdOrderData['price'] = $data['price'];
  2197. $pdOrderData['remark'] = '初始库存';
  2198. CheckOrderClass::add($pdOrderData);
  2199. $pdOrderItemData = [];
  2200. $pdOrderItemData['orderSn'] = $pdOrderSn;
  2201. $pdOrderItemData['productId'] = $respond->id ?? 0;
  2202. $pdOrderItemData['itemId'] = $data['itemId'];
  2203. $pdOrderItemData['itemNum'] = $stock;
  2204. $pdOrderItemData['itemStock'] = 0;
  2205. $pdOrderItemData['profitLostNum'] = $stock;
  2206. $itemInfo = [
  2207. 'itemName' => $name,
  2208. 'itemCover' => $data['cover'],
  2209. 'bigNum' => $stock,
  2210. 'smallNum' => 0,
  2211. 'bigNumStock' => 0,
  2212. 'smallNumStock' => 0,
  2213. 'bigNumProfitLoss' => $stock,
  2214. 'smallNumProfitLoss' => 0,
  2215. 'itemUnit' => 0,
  2216. 'ratio' => $ratio,
  2217. 'rank' => 'A',
  2218. 'bigUnit' => $data['bigUnit'],
  2219. 'smallUnit' => $data['smallUnit'],
  2220. ];
  2221. $pdOrderItemData['itemInfo'] = json_encode($itemInfo);
  2222. $pdOrderItemData['itemPrice'] = $data['price'];
  2223. $pdOrderItemData['itemCost'] = $data['cost'];
  2224. CheckOrderItemClass::add($pdOrderItemData);
  2225. $record = [];
  2226. $record['sjId'] = $data['sjId'];
  2227. $record['shopId'] = $data['shopId'];
  2228. //增加中央ID
  2229. $record['mainId'] = $currentMainId;
  2230. $record['orderSn'] = $pdOrderSn;
  2231. $record['itemId'] = $data['itemId'];
  2232. $record['productId'] = $respond->id ?? 0;
  2233. $record['itemNum'] = $stock;//盈亏数
  2234. $record['oldStock'] = 0;
  2235. $record['newStock'] = $stock; //新库存直接等于盘点的数量
  2236. $record['relateName'] = $data['staffName'] ?? '';
  2237. StockRecordClass::addCheckOrderRecord($record);
  2238. }
  2239. //在首店添加,则找到父级sj下的所有直营和加盟店,都进行同步
  2240. if (isset($currentShop->default) && $currentShop->default == 1) {
  2241. $sjId = $currentShop->sjId ?? 0;
  2242. $shopList = ShopClass::getAllByCondition(['sjId' => $sjId], null, '*', null, true);
  2243. foreach ($shopList as $shop) {
  2244. $shopId = $shop->id ?? 0;
  2245. if ($shopId == $currentShop->id) {
  2246. //前面已经添加过了
  2247. continue;
  2248. }
  2249. $thisMainId = $shop->mainId ?? 0;
  2250. $data['shopId'] = $shopId;
  2251. $data['mainId'] = $thisMainId;
  2252. //不是当前门店,库存默认为0并且是下架状态。花材分类放在同名分类下,如果没有则放默认分类
  2253. if ($currentShopId != $shopId) {
  2254. $data['stock'] = 0;
  2255. $data['status'] = 2;
  2256. $currentClassId = 0;
  2257. $classList = ItemClassClass::getAllByCondition(['mainId' => $thisMainId], null, '*', null, true);
  2258. if (!empty($classList)) {
  2259. foreach ($classList as $currentClass) {
  2260. $currentName = $currentClass->name ?? '';
  2261. if ($currentName == $catName) {
  2262. $currentClassId = $currentClass->id ?? 0;
  2263. }
  2264. }
  2265. }
  2266. if (empty($currentClassId)) {
  2267. $defaultCat = ItemClassClass::getByCondition(['mainId' => $thisMainId, 'isDefault' => 1], true);
  2268. $currentClassId = $defaultCat->id ?? 0;
  2269. }
  2270. if (empty($currentClassId)) {
  2271. util::fail('直营店没有分类,也没有默认分类');
  2272. }
  2273. $data['classId'] = $currentClassId;
  2274. }
  2275. //已经有了不再添加
  2276. $chainHas = self::getByCondition(['mainId' => $thisMainId, 'itemId' => $ptItemId], true);
  2277. if (!empty($chainHas)) {
  2278. continue;
  2279. }
  2280. self::addBaseData($data);
  2281. }
  2282. }
  2283. $transaction->commit();
  2284. } catch (\Exception $exception) {
  2285. $transaction->rollBack();
  2286. Yii::info('失败原因:' . $exception->getMessage());
  2287. util::fail('添加失败');
  2288. }
  2289. }
  2290. /**
  2291. * 改变花材状态并重置特价、满减、限购
  2292. * @param $product
  2293. * @param $status 1上架 2下架
  2294. * @param bool $stockEmpty 库存为空(指新设置的库存)
  2295. * @throws \Exception
  2296. */
  2297. public static function changeStatusAndReset($product, $status, $stockEmpty = false)
  2298. {
  2299. $product->status = $status;
  2300. // 当前库存为0
  2301. if (intval($product->stock) == 0) {
  2302. //库存=0时下架,特价一起去掉
  2303. $product->discountPrice = 0;
  2304. $product->hjDiscountPrice = 0;
  2305. $product->skDiscountPrice = 0;
  2306. //花材满减去掉 ssh 20260318
  2307. $product->reachNum = 0;
  2308. $product->reachNumDiscount = 0;
  2309. //花材去除限购
  2310. $product->limitBuy = 0;
  2311. self::clearLimitBuyCache($product->id); // 清空限购记录缓存
  2312. }
  2313. // 新设置的库存为0
  2314. if ($stockEmpty) {
  2315. //库存=0时下架,特价一起去掉
  2316. $product->discountPrice = 0;
  2317. $product->hjDiscountPrice = 0;
  2318. $product->skDiscountPrice = 0;
  2319. //花材满减去掉 ssh 20260318
  2320. $product->reachNum = 0;
  2321. $product->reachNumDiscount = 0;
  2322. //花材去除限购
  2323. $product->limitBuy = 0;
  2324. // self::clearLimitBuyCache($productId); // 此处不清空限购记录缓存,留在更新库存的地方来做清空缓存
  2325. }
  2326. }
  2327. //获取当前供货商下的所有item
  2328. public static function getGhsItemAll($sjId, $select = "*")
  2329. {
  2330. $where = [
  2331. 'sjId' => $sjId
  2332. ];
  2333. $data = self::getAllList($select, $where);
  2334. return $data;
  2335. }
  2336. //库存预警通知 ssh 20210909
  2337. public static function stockWarningAddQueue($product, $stock)
  2338. {
  2339. $warningNum = $product['stockWarning'] ?? 0;
  2340. if ($warningNum <= 0) {
  2341. return false;
  2342. }
  2343. $date = date("Y-m-d");
  2344. $productId = $product['id'] ?? 0;
  2345. $key = 'stockWarning_' . $productId . '_' . $date;
  2346. //当天已经放入队列通知的不再通知
  2347. $hasWarning = Yii::$app->redis->executeCommand('GET', [$key]);
  2348. if (isset($hasWarning) && !empty($hasWarning)) {
  2349. return false;
  2350. }
  2351. if ($stock <= $warningNum) {
  2352. Yii::$app->redis->executeCommand('SETEX', [$key, 86400, 'hasWarning']);
  2353. $shopId = $product['shopId'] ?? 0;
  2354. if (empty($shopId)) {
  2355. return false;
  2356. }
  2357. //队列方式去处理
  2358. $stockWarningListKey = 'stockWarningList_' . $shopId;
  2359. Yii::$app->redis->executeCommand('LPUSH', [$stockWarningListKey, $productId]);
  2360. }
  2361. }
  2362. //所有门店进行库存通知 ssh 20210909
  2363. public static function allShopStockWarningFromQueue()
  2364. {
  2365. //先取消库存预警
  2366. return true;
  2367. $ghs = dict::getDict('ptStyle', 'ghs');
  2368. $shopList = ShopClass::getAllByCondition(['ptStyle' => $ghs], null, '*', null, true);
  2369. if (!empty($shopList)) {
  2370. foreach ($shopList as $key => $shop) {
  2371. $ids = [];
  2372. $shopId = $shop->id ?? 0;
  2373. $stockWarningListKey = 'stockWarningList_' . $shopId;
  2374. while ($count = Yii::$app->redis->executeCommand('LLEN', [$stockWarningListKey])) {
  2375. $productId = Yii::$app->redis->executeCommand('RPOP', [$stockWarningListKey]);
  2376. if (is_numeric($productId)) {
  2377. $ids[] = $productId;
  2378. }
  2379. }
  2380. if (empty($ids)) {
  2381. continue;
  2382. }
  2383. $ids = array_unique(array_filter($ids));
  2384. $productList = self::getAllByCondition(['id' => ['in', $ids]], null, '*', null, true);
  2385. if (!empty($productList)) {
  2386. WxMessageClass::stockWarningInform($shop, $productList);
  2387. }
  2388. }
  2389. }
  2390. }
  2391. //通过花材ID 获取mainId lqh 2022.03.20
  2392. public static function getMainIdByProductId($productId)
  2393. {
  2394. $product = self::getById($productId, false);
  2395. return $product['mainId'] ?? 0;
  2396. }
  2397. /**
  2398. * 创建限购缓存(有过期时间)
  2399. * @param $productId 花材ID
  2400. * @param int $seconds 过期时间
  2401. * @param array $options ['clearAt' => 0, 'recurring' => 0, 'intervalDays' => 0, 'clearHour' => 0] 循环清空配置
  2402. * @return bool
  2403. */
  2404. public static function createLimitBuyCache($productId, $seconds = 0, $options = [])
  2405. {
  2406. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2407. $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
  2408. $clearLoopKey = self::CLEAR_LOOP_KEY . $productId;
  2409. $recurring = !empty($options['recurring']);
  2410. $has = Yii::$app->redis->executeCommand('HEXISTS', [$limitKey, 0]);
  2411. if (!empty($has)) {
  2412. // 获取缓存超时时间
  2413. $expire = Yii::$app->redis->executeCommand('TTL', [$limitKey]);
  2414. if ($expire > 0) {
  2415. //return;
  2416. }
  2417. //删除缓存
  2418. Yii::$app->redis->executeCommand('HDEL', [$limitKey, 0]);
  2419. }
  2420. Yii::$app->redis->executeCommand('HSET', [$limitKey, 0, 0]);
  2421. if ($seconds > 0) {
  2422. $clearAt = intval($options['clearAt'] ?? 0);
  2423. if ($clearAt <= 0) {
  2424. $clearAt = time() + intval($seconds);
  2425. }
  2426. Yii::$app->redis->executeCommand('EXPIRE', [$limitKey, $seconds]);
  2427. Yii::$app->redis->executeCommand('SET', [$clearMarkKey, $clearAt]);
  2428. if ($recurring) {
  2429. $intervalDays = intval($options['intervalDays'] ?? 0);
  2430. $clearHour = intval($options['clearHour'] ?? -1);
  2431. Yii::$app->redis->executeCommand('SET', [$clearLoopKey, json_encode([
  2432. 'intervalDays' => $intervalDays,
  2433. 'clearHour' => $clearHour,
  2434. ], JSON_UNESCAPED_UNICODE)]);
  2435. } else {
  2436. Yii::$app->redis->executeCommand('DEL', [$clearLoopKey]);
  2437. }
  2438. // 用 RabbitMQ 延迟消息在到期时清空订单项限购字段
  2439. $message = [
  2440. 'type' => 'limit_buy_clear',
  2441. 'ptType' => 'ghs',
  2442. 'productId' => $productId,
  2443. 'clearAt' => $clearAt,
  2444. ];
  2445. if ($recurring) {
  2446. $message['recurring'] = 1;
  2447. $message['intervalDays'] = intval($options['intervalDays'] ?? 0);
  2448. $message['clearHour'] = intval($options['clearHour'] ?? -1);
  2449. }
  2450. $message = serialize($message);
  2451. $producer = Yii::$app->rabbitmq->getProducer('stockProducer');
  2452. $producer->publish($message, 'limitBuyDelayExchange', 'limitBuyDelayRoute', [
  2453. 'delivery_mode' => 2,
  2454. 'content_type' => 'application/octet-stream',
  2455. 'application_headers' => new AMQPTable([
  2456. 'x-delay' => intval($seconds * 1000),
  2457. ]),
  2458. ]);
  2459. Yii::info('限购延迟消息已发送: ' . json_encode([
  2460. 'type' => 'limit_buy_clear',
  2461. 'ptType' => 'ghs',
  2462. 'productId' => intval($productId),
  2463. 'clearAt' => intval($clearAt),
  2464. 'delayMs' => intval($seconds * 1000),
  2465. 'recurring' => $recurring ? 1 : 0,
  2466. ], JSON_UNESCAPED_UNICODE), __METHOD__);
  2467. } else {
  2468. Yii::$app->redis->executeCommand('DEL', [$clearMarkKey]);
  2469. Yii::$app->redis->executeCommand('DEL', [$clearLoopKey]);
  2470. }
  2471. }
  2472. // 解析循环清空限购配置
  2473. public static function getLimitBuyClearLoopConfig($data)
  2474. {
  2475. if ($data['limitBuyClearType'] == 0) {
  2476. return [];
  2477. }
  2478. $hasIntervalDays = array_key_exists('limitBuyClearIntervalDays', $data) && $data['limitBuyClearIntervalDays'] !== '';
  2479. $hasClearHour = (array_key_exists('cleartAt', $data) && $data['cleartAt'] !== '')
  2480. || (array_key_exists('clearAt', $data) && $data['clearAt'] !== '');
  2481. if (!$hasIntervalDays && !$hasClearHour) {
  2482. return [];
  2483. }
  2484. if (!$hasIntervalDays || !$hasClearHour) {
  2485. util::fail('请填写循环清空限购的间隔天数和执行时间');
  2486. }
  2487. if (!is_numeric($data['limitBuyClearIntervalDays'])) {
  2488. util::fail('循环清空限购的间隔天数错误');
  2489. }
  2490. $clearHourValue = array_key_exists('cleartAt', $data) ? $data['cleartAt'] : $data['clearAt'];
  2491. if (!is_numeric($clearHourValue)) {
  2492. util::fail('循环清空限购的执行时间错误');
  2493. }
  2494. $intervalDays = intval($data['limitBuyClearIntervalDays']);
  2495. $clearHour = intval($clearHourValue);
  2496. if ($intervalDays <= 0) {
  2497. util::fail('循环清空限购的间隔天数必须大于0');
  2498. }
  2499. if ($clearHour < 0 || $clearHour > 23) {
  2500. util::fail('循环清空限购的执行时间必须在0点到23点之间');
  2501. }
  2502. return ['intervalDays' => $intervalDays, 'clearHour' => $clearHour];
  2503. }
  2504. /**
  2505. * 创建循环清空限购缓存
  2506. * @param $productId 花材ID
  2507. * @param int $intervalDays 循环清空限购的间隔天数
  2508. * @param int $clearHour 循环清空限购的执行时间点
  2509. * @param int $clearAt 循环清空限购的下次清空时间
  2510. * @return bool
  2511. */
  2512. public static function createRecurringLimitBuyCache($productId, $intervalDays, $clearHour, $clearAt = 0)
  2513. {
  2514. $productId = intval($productId);
  2515. $intervalDays = intval($intervalDays);
  2516. $clearHour = intval($clearHour);
  2517. if ($productId <= 0 || $intervalDays <= 0 || $clearHour < 0 || $clearHour > 23) {
  2518. return false;
  2519. }
  2520. if ($clearAt <= 0) {
  2521. $clearAt = self::getNextLimitBuyClearAt($intervalDays, $clearHour);
  2522. }
  2523. $seconds = max(1, $clearAt - time());
  2524. self::createLimitBuyCache($productId, $seconds, [
  2525. 'recurring' => 1,
  2526. 'intervalDays' => $intervalDays,
  2527. 'clearHour' => $clearHour,
  2528. 'clearAt' => $clearAt,
  2529. ]);
  2530. return true;
  2531. }
  2532. // 计算下一次循环清空时间
  2533. public static function getNextLimitBuyClearAt($intervalDays, $clearHour, $baseTime = 0)
  2534. {
  2535. $baseTime = $baseTime > 0 ? intval($baseTime) : time();
  2536. $targetDay = $baseTime + intval($intervalDays) * 86400;
  2537. $clearAt = strtotime(date('Y-m-d', $targetDay) . ' ' . sprintf('%02d:00:00', intval($clearHour)));
  2538. while ($clearAt <= $baseTime) {
  2539. $clearAt = strtotime('+' . intval($intervalDays) . ' days', $clearAt);
  2540. }
  2541. return $clearAt;
  2542. }
  2543. // 获取循环清空限购配置
  2544. public static function getLimitBuyClearLoopConfigByProductId($productId)
  2545. {
  2546. $productId = intval($productId);
  2547. if ($productId <= 0) {
  2548. return [];
  2549. }
  2550. $clearLoopKey = self::CLEAR_LOOP_KEY . $productId;
  2551. $config = Yii::$app->redis->executeCommand('GET', [$clearLoopKey]);
  2552. if (empty($config)) {
  2553. return [];
  2554. }
  2555. $config = json_decode($config, true);
  2556. return is_array($config) ? $config : [];
  2557. }
  2558. // 获取限购清空配置,用于商品详情回显
  2559. public static function getLimitBuyClearInfo($productId)
  2560. {
  2561. $productId = intval($productId);
  2562. $info = [
  2563. 'limitBuyClearType' => 0, // 0不清空 1定时清空 2循环清空
  2564. 'limitBuyClearTypeName' => '不清空',
  2565. 'limitBuyClearAt' => 0,
  2566. 'limitBuyClearTime' => '',
  2567. 'limitBuyNextClearAt' => 0,
  2568. 'limitBuyNextClearTime' => '',
  2569. 'limitBuyClearIntervalDays' => 0,
  2570. 'cleartAt' => '',
  2571. ];
  2572. if ($productId <= 0) {
  2573. return $info;
  2574. }
  2575. $loopConfig = self::getLimitBuyClearLoopConfigByProductId($productId);
  2576. if (!empty($loopConfig)) {
  2577. $info['limitBuyClearType'] = 1;
  2578. $info['limitBuyClearTypeName'] = '循环清空';
  2579. $info['limitBuyClearIntervalDays'] = intval($loopConfig['intervalDays'] ?? 0);
  2580. $info['cleartAt'] = intval($loopConfig['clearHour'] ?? 0);
  2581. return $info;
  2582. }
  2583. $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
  2584. $clearAt = intval(Yii::$app->redis->executeCommand('GET', [$clearMarkKey]));
  2585. if ($clearAt <= 0) {
  2586. return $info;
  2587. }
  2588. $info['limitBuyClearType'] = 2;
  2589. $info['limitBuyClearTypeName'] = '定时清空';
  2590. $info['limitBuyClearAt'] = $clearAt;
  2591. $info['limitBuyClearTime'] = date('Y-m-d H:i:s', $clearAt);
  2592. $info['limitBuyNextClearAt'] = $clearAt;
  2593. $info['limitBuyNextClearTime'] = $info['limitBuyClearTime'];
  2594. return $info;
  2595. }
  2596. // 清理循环清空限购配置
  2597. public static function clearLimitBuyClearLoopConfig($productId)
  2598. {
  2599. $productId = intval($productId);
  2600. if ($productId <= 0) {
  2601. return false;
  2602. }
  2603. $clearLoopKey = self::CLEAR_LOOP_KEY . $productId;
  2604. Yii::$app->redis->executeCommand('DEL', [$clearLoopKey]);
  2605. return true;
  2606. }
  2607. /**
  2608. * 记录限购缓存写入前快照(同一次请求内同商品同客户只记录一次)
  2609. *
  2610. * @param int $productId
  2611. * @param int $customId
  2612. * @param bool $hasOldValue
  2613. * @param string|int|float $oldValue
  2614. * @return void
  2615. */
  2616. public static function recordLimitBuyRollbackSnapshot($productId, $customId, $hasOldValue, $oldValue = 0)
  2617. {
  2618. $productId = intval($productId);
  2619. $customId = intval($customId);
  2620. if ($productId <= 0 || $customId <= 0) {
  2621. return;
  2622. }
  2623. $snapshotKey = $productId . '_' . $customId;
  2624. if (!isset(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY]) || !is_array(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY])) {
  2625. Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY] = [];
  2626. }
  2627. if (isset(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY][$snapshotKey])) {
  2628. return;
  2629. }
  2630. Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY][$snapshotKey] = [
  2631. 'productId' => $productId,
  2632. 'customId' => $customId,
  2633. 'hasOldValue' => $hasOldValue ? 1 : 0,
  2634. 'oldValue' => strval($oldValue),
  2635. ];
  2636. }
  2637. /**
  2638. * 清理当前请求中记录的限购回滚快照
  2639. *
  2640. * @return void
  2641. */
  2642. public static function clearLimitBuyRollbackSnapshot()
  2643. {
  2644. unset(Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY]);
  2645. }
  2646. /**
  2647. * 回滚当前请求中已记录的限购缓存变更
  2648. *
  2649. * @return bool
  2650. */
  2651. public static function rollbackLimitBuySnapshot()
  2652. {
  2653. $snapshotList = Yii::$app->params[self::LIMIT_BUY_ROLLBACK_SNAPSHOT_KEY] ?? [];
  2654. if (empty($snapshotList) || !is_array($snapshotList)) {
  2655. return true;
  2656. }
  2657. foreach ($snapshotList as $snapshot) {
  2658. $productId = intval($snapshot['productId'] ?? 0);
  2659. $customId = intval($snapshot['customId'] ?? 0);
  2660. if ($productId <= 0 || $customId <= 0) {
  2661. continue;
  2662. }
  2663. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2664. $hasOldValue = intval($snapshot['hasOldValue'] ?? 0);
  2665. if ($hasOldValue == 1) {
  2666. $oldValue = $snapshot['oldValue'] ?? '0';
  2667. Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $oldValue]);
  2668. } else {
  2669. Yii::$app->redis->executeCommand('HDEL', [$limitKey, $customId]);
  2670. }
  2671. }
  2672. self::clearLimitBuyRollbackSnapshot();
  2673. return true;
  2674. }
  2675. // 获取客户当前采购清单中各花材的限购情况
  2676. public static function getLimitBuyInfoByList($list, $customId)
  2677. {
  2678. if (empty($list) || !is_array($list)) {
  2679. return [];
  2680. }
  2681. $buyNumMap = [];
  2682. $ids = [];
  2683. foreach ($list as $item) {
  2684. $productId = $item['id'] ?? 0;
  2685. if (empty($productId)) {
  2686. continue;
  2687. }
  2688. $productId = intval($productId);
  2689. $bigNum = $item['bigCount'] ?? ($item['bigNum'] ?? 0);
  2690. $smallNum = $item['smallCount'] ?? ($item['smallNum'] ?? 0);
  2691. if (isset($buyNumMap[$productId]) == false) {
  2692. $buyNumMap[$productId] = [
  2693. 'bigNum' => 0,
  2694. 'smallNum' => 0,
  2695. ];
  2696. }
  2697. $buyNumMap[$productId]['bigNum'] = bcadd($buyNumMap[$productId]['bigNum'], $bigNum, 2);
  2698. $buyNumMap[$productId]['smallNum'] = bcadd($buyNumMap[$productId]['smallNum'], $smallNum, 2);
  2699. $ids[] = $productId;
  2700. }
  2701. $ids = array_values(array_unique($ids));
  2702. if (empty($ids)) {
  2703. return [];
  2704. }
  2705. $productData = self::getProductByIds($ids);
  2706. $productData = array_column($productData, null, 'id');
  2707. $respond = [];
  2708. foreach ($ids as $productId) {
  2709. $product = $productData[$productId] ?? [];
  2710. if (empty($product)) {
  2711. continue;
  2712. }
  2713. $limitBuy = $product['limitBuy'] ?? 0;
  2714. $isLimit = $limitBuy > 0 ? true : false;
  2715. $hasBuyNum = 0;
  2716. if ($isLimit) {
  2717. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2718. $hasBuyNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
  2719. $hasBuyNum = $hasBuyNum == null ? 0 : $hasBuyNum;
  2720. }
  2721. $buyNum = $buyNumMap[$productId] ?? [];
  2722. $ratio = $product['ratio'] ?? 1;
  2723. $currentBuyNum = self::mergeItemNum($buyNum['bigNum'] ?? 0, $buyNum['smallNum'] ?? 0, $ratio);
  2724. $totalBuyNum = bcadd($hasBuyNum, $currentBuyNum, 2);
  2725. $specialPrice = (
  2726. ($product['hjDiscountPrice'] ?? 0) > 0
  2727. && ($product['discountPrice'] ?? 0) > 0
  2728. && ($product['skDiscountPrice'] ?? 0) > 0
  2729. ) ? true : false;
  2730. $respond[] = [
  2731. 'id' => $productId,
  2732. 'name' => $product['name'] ?? '',
  2733. 'isLimit' => $isLimit, //是否限购
  2734. 'limitBuy' => (float)$limitBuy, //限购数量
  2735. 'specialPrice' => $specialPrice, //是否特价
  2736. 'hasBuyNum' => $isLimit ? (float)$hasBuyNum : 0, //已购买数量
  2737. 'currentBuyNum' => (float)$currentBuyNum, //此次购买的数量
  2738. 'reachLimitBuyNum' => ($isLimit && $totalBuyNum > $limitBuy) ? true : false, //是否达到限购数量
  2739. 'exceedNum' => $isLimit ? (float)bcsub($hasBuyNum, $limitBuy, 2) : 0, //超出数量, 小于等于0表示未超出
  2740. ];
  2741. }
  2742. return $respond;
  2743. }
  2744. /**
  2745. * 限购处理
  2746. * @param $product
  2747. * @param $customId
  2748. * @param $num
  2749. */
  2750. public static function handleLimitBuy($product, $customId, $num)
  2751. {
  2752. $limitBuy = $product['limitBuy'];
  2753. if ($limitBuy <= 0) {
  2754. return;
  2755. }
  2756. $productId = $product['productId'];
  2757. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2758. $has = Yii::$app->redis->executeCommand('HEXISTS', [$limitKey, $customId]);
  2759. if (!empty($has)) {
  2760. $hasNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
  2761. self::recordLimitBuyRollbackSnapshot($productId, $customId, true, $hasNum);
  2762. $lastNum = bcadd($hasNum, $num);
  2763. if ($lastNum > $limitBuy) {
  2764. // 如果有特价,则超过的数量按原价卖;否则中断
  2765. if ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0) {
  2766. Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $lastNum]);
  2767. return;
  2768. }
  2769. util::error(-1, '累计已超出限购数', ['productId' => $productId, 'limitBuy' => $limitBuy, 'exceed' => $lastNum - $limitBuy]);
  2770. }
  2771. Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $lastNum]);
  2772. } else {
  2773. self::recordLimitBuyRollbackSnapshot($productId, $customId, false, 0);
  2774. if ($num > $limitBuy) {
  2775. // 如果有特价,则超过的数量按原价卖;否则中断
  2776. if ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0) {
  2777. Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $num]);
  2778. return;
  2779. }
  2780. util::error(-1, '超出限购数', ['productId' => $productId, 'limitBuy' => $limitBuy, 'exceed' => $num - $limitBuy]);
  2781. }
  2782. Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $num]);
  2783. }
  2784. }
  2785. /**
  2786. * 清空限购的缓存
  2787. * @param int $productId
  2788. * @param bool $clearAll
  2789. */
  2790. public static function clearLimitBuyCache($productId, $clearAll = true)
  2791. {
  2792. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2793. $arr = Yii::$app->redis->executeCommand('HKEYS', [$limitKey]);
  2794. if (!empty($arr)) {
  2795. foreach ($arr as $field) {
  2796. self::baseClearLimitBuy($productId, $field);
  2797. }
  2798. }
  2799. if ($clearAll === true) {
  2800. self::clearLimitBuyClearMark($productId);
  2801. self::clearLimitBuyClearLoopConfig($productId);
  2802. }
  2803. }
  2804. // 校验清空限购消息是否是当前有效版本
  2805. public static function checkLimitBuyClearMessage($productId, $clearAt)
  2806. {
  2807. $productId = intval($productId);
  2808. $clearAt = intval($clearAt);
  2809. if ($productId <= 0 || $clearAt <= 0) {
  2810. return false;
  2811. }
  2812. $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
  2813. $currentClearAt = intval(Yii::$app->redis->executeCommand('GET', [$clearMarkKey]));
  2814. return $currentClearAt > 0 && $currentClearAt == $clearAt;
  2815. }
  2816. // 清理限购清空标记
  2817. public static function clearLimitBuyClearMark($productId)
  2818. {
  2819. $productId = intval($productId);
  2820. if ($productId <= 0) {
  2821. return false;
  2822. }
  2823. $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
  2824. Yii::$app->redis->executeCommand('DEL', [$clearMarkKey]);
  2825. return true;
  2826. }
  2827. //$num -1 表示减全部,大于-1表示减值
  2828. public static function baseClearLimitBuy($productId, $customId, $num = -1)
  2829. {
  2830. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2831. if ($num <= -1) {
  2832. //清掉全部
  2833. Yii::$app->redis->executeCommand('HDEL', [$limitKey, $customId]);
  2834. } else {
  2835. $hasNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
  2836. $remainNum = $hasNum > $num ? bcsub($hasNum, $num) : 0;
  2837. $remainNum = floor($remainNum);
  2838. if ($remainNum > 0) {
  2839. Yii::$app->redis->executeCommand('HSET', [$limitKey, $customId, $remainNum]);
  2840. } else {
  2841. Yii::$app->redis->executeCommand('HDEL', [$limitKey, $customId]);
  2842. }
  2843. }
  2844. }
  2845. // 只清空已购买记录,不关闭商品限购配置
  2846. public static function clearLimitBuyRecordByProductId($productId)
  2847. {
  2848. $productId = intval($productId);
  2849. if ($productId <= 0) {
  2850. return false;
  2851. }
  2852. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2853. Yii::$app->redis->executeCommand('DEL', [$limitKey]);
  2854. return true;
  2855. }
  2856. public static function getHasLimitBuyList($product)
  2857. {
  2858. $productId = $product->id;
  2859. $limitBuy = $product->limitBuy;
  2860. $limitKey = self::LIMIT_BUY_KEY . $productId;
  2861. $arr = Yii::$app->redis->executeCommand('HKEYS', [$limitKey]);
  2862. $customList = [];
  2863. if (!empty($arr)) {
  2864. $numMap = [];
  2865. $ids = [];
  2866. foreach ($arr as $customId) {
  2867. $num = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
  2868. $numMap[$customId] = $num;
  2869. $ids[] = $customId;
  2870. }
  2871. $specialPrice = ($product['hjDiscountPrice'] > 0 && $product['discountPrice'] > 0 && $product['skDiscountPrice'] > 0); // 销售花材是否开启特价
  2872. $customList = CustomClass::getAllByCondition(['id' => ['in', $ids]], null, '*');
  2873. foreach ($customList as $key => $val) {
  2874. $customId = $val['id'];
  2875. $num = $numMap[$customId] ?? 0;
  2876. $customList[$key]['hasLimitBuyNum'] = $num;
  2877. $customList[$key]['reachLimitBuyNum'] = $num >= $limitBuy ? 1 : 0;
  2878. $avatar = $val['avatar'] ?? '';
  2879. $smallAvatar = imgUtil::groupImg($avatar);
  2880. $customList[$key]['smallAvatar'] = $smallAvatar . "?x-oss-process=image/resize,m_fill,h_80,w_80";
  2881. $customList[$key]['specialPrice'] = $specialPrice;
  2882. }
  2883. }
  2884. return $customList;
  2885. }
  2886. /**
  2887. * 根据花材清空订单项限购值
  2888. *
  2889. * @param int $productId
  2890. * @return bool
  2891. */
  2892. public static function clearLimitBuyByProductId($productId, $oldLimitBuy = 0)
  2893. {
  2894. $productId = intval($productId);
  2895. if ($productId <= 0) {
  2896. return false;
  2897. }
  2898. self::updateById($productId, ['limitBuy' => 0]);
  2899. if ($oldLimitBuy > 0) {
  2900. self::createRecoverLimitBuyCache($productId, $oldLimitBuy);
  2901. }
  2902. return true;
  2903. }
  2904. /**
  2905. * 创建限购旧值快照缓存
  2906. * @param $productId
  2907. * @param $oldLimitBuy
  2908. * @return bool
  2909. */
  2910. public static function createRecoverLimitBuyCache($productId, $oldLimitBuy)
  2911. {
  2912. $productId = intval($productId);
  2913. if ($productId <= 0) {
  2914. return false;
  2915. }
  2916. $limitKey = self::RECOVER_LIMIT_BUY_KEY . $productId;
  2917. Yii::$app->redis->executeCommand('SET', [$limitKey, $oldLimitBuy]);
  2918. //Yii::$app->redis->executeCommand('EXPIRE', [$limitKey, 1200]);// 要考虑售后退款,所以不能主动清空,而是只能被动清空
  2919. return true;
  2920. }
  2921. /**
  2922. * 删除限购旧值快照缓存
  2923. * @param $productId
  2924. * @return bool
  2925. */
  2926. public static function deleteRecoverLimitBuyCache($productId)
  2927. {
  2928. $productId = intval($productId);
  2929. if ($productId <= 0) {
  2930. return false;
  2931. }
  2932. $limitKey = self::RECOVER_LIMIT_BUY_KEY . $productId;
  2933. Yii::$app->redis->executeCommand('DEL', [$limitKey]);
  2934. return true;
  2935. }
  2936. /**
  2937. * 获取限购旧值快照缓存
  2938. * @param $productId
  2939. * @return bool
  2940. */
  2941. public static function getRecoverLimitBuyCache($productId)
  2942. {
  2943. $productId = intval($productId);
  2944. if ($productId <= 0) {
  2945. return false;
  2946. }
  2947. $limitKey = self::RECOVER_LIMIT_BUY_KEY . $productId;
  2948. return Yii::$app->redis->executeCommand('GET', [$limitKey]);
  2949. }
  2950. /**
  2951. * 库存从 0 回滚时,尝试恢复限购数量
  2952. * @param int $productId
  2953. * @param mixed $oldStock
  2954. * @return bool
  2955. */
  2956. public static function tryRecoverLimitBuyWhenStockRollbackFromZero($productId, $oldStock)
  2957. {
  2958. if (floatval($oldStock) > 0) {
  2959. return false;
  2960. }
  2961. $recoverLimitBuy = self::getRecoverLimitBuyCache($productId);
  2962. if ($recoverLimitBuy === false || $recoverLimitBuy === null || $recoverLimitBuy === '') {
  2963. return false;
  2964. }
  2965. $recoverLimitBuy = intval($recoverLimitBuy);
  2966. if ($recoverLimitBuy <= 0) {
  2967. self::deleteRecoverLimitBuyCache($productId);
  2968. return false;
  2969. }
  2970. self::updateById($productId, ['limitBuy' => $recoverLimitBuy]);
  2971. self::deleteRecoverLimitBuyCache($productId);
  2972. return true;
  2973. }
  2974. }