CustomClass.php 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396
  1. <?php
  2. namespace bizHd\custom\classes;
  3. use biz\wx\classes\WxMessageClass;
  4. use bizHd\balance\classes\BalanceChangeClass;
  5. use bizHd\balance\classes\BalanceGiveChangeClass;
  6. use bizHd\balance\classes\BalancePayChangeClass;
  7. use bizHd\birthday\classes\BirthdayGiftClass;
  8. use bizHd\distribution\classes\DistributionUserClass;
  9. use bizHd\hb\classes\HbAutoRuleClass;
  10. use bizHd\hb\models\HbAutoRule;
  11. use bizHd\member\classes\MemberChangeClass;
  12. use bizHd\member\classes\MemberLevelClass;
  13. use bizHd\order\classes\OrderClass;
  14. use bizHd\order\classes\SettleClass;
  15. use bizHd\shop\classes\ShopClass;
  16. use bizHd\user\classes\UserClass;
  17. use bizHd\user\classes\UserGrowthClass;
  18. use common\components\dict;
  19. use common\components\imgUtil;
  20. use common\components\noticeUtil;
  21. use common\components\stringUtil;
  22. use common\components\util;
  23. use Overtrue\ChineseCalendar\Calendar;
  24. use bizHd\base\classes\BaseClass;
  25. use Yii;
  26. class CustomClass extends BaseClass
  27. {
  28. public static $baseFile = '\bizHd\custom\models\Custom';
  29. public static function getBalanceStat($where)
  30. {
  31. $cacheKey = self::getBalanceStatCacheKey($where);
  32. $cached = \Yii::$app->redis->executeCommand('GET', [$cacheKey]);
  33. if ($cached !== false && $cached !== null && $cached !== '') {
  34. $data = json_decode($cached, true);
  35. if (is_array($data)) {
  36. return $data;
  37. }
  38. }
  39. $countWhere = array_merge($where, ['balance>' => 0]);
  40. $count = self::getCount($countWhere);
  41. $totalBalance = self::sum($where, 'balance');
  42. $data = [
  43. 'count' => intval($count),
  44. 'totalBalance' => bcadd($totalBalance ?: '0', '0', 2),
  45. ];
  46. \Yii::$app->redis->executeCommand('SETEX', [$cacheKey, 60, json_encode($data)]);
  47. return $data;
  48. }
  49. public static function getBalanceStatCacheKey($where)
  50. {
  51. return 'hd_custom_balance_stat:' . md5(json_encode($where, JSON_UNESCAPED_UNICODE));
  52. }
  53. /**
  54. * 修改客户生日
  55. * @param $customId
  56. * @param $shopId
  57. * @param $params
  58. * @return array [custom, bool] custom表示客户数据,bool表示是否变动了生日
  59. * @throws \Exception
  60. */
  61. public static function modifyBirthday($customId, $shopId, $params)
  62. {
  63. date_default_timezone_set('PRC');
  64. $custom = self::getById($customId, true);
  65. if (empty($custom)) {
  66. util::fail('没有找到客户');
  67. }
  68. if ($custom->shopId != $shopId) {
  69. util::fail('不是你的客户');
  70. }
  71. $lunar = $params['lunar'] ?? 0;
  72. $birthdayMonth = $params['birthdayMonth'] ?? 1;
  73. if ($birthdayMonth > 12 || $birthdayMonth < 1) {
  74. util::fail('月份必须1到12月');
  75. }
  76. $birthdayDate = $params['birthdayDate'] ?? 1;
  77. if ($birthdayDate > 31 || $birthdayDate < 1) {
  78. util::fail('日期必须1到31号');
  79. }
  80. $userId = $custom->userId;
  81. $user = UserClass::getById($userId, true);
  82. if (empty($user)) {
  83. util::fail('客户信息缺失');
  84. }
  85. if ($lunar == 1 && $birthdayDate == 31) {
  86. util::fail('农历没有31号');
  87. }
  88. $isUpdateBirthday = $user->birthdayTime == 0;
  89. // 如果 xhUser 中已经设置了生日,则直接取用
  90. if ($user->birthdayTime != 0) {
  91. $custom->lunar = $user->lunar;
  92. $custom->birthdayDate = $user->birthdayDate;
  93. $custom->birthdayMonth = $user->birthdayMonth;
  94. $custom->birthday = $user->birthday;
  95. $custom->birthdayTime = $user->birthdayTime;
  96. $custom->save();
  97. } else {
  98. $calendar = new Calendar();
  99. if ($lunar == 1) {
  100. try {
  101. $result = $calendar->lunar(date("Y"), $birthdayMonth, $birthdayDate);
  102. } catch (\InvalidArgumentException $e) {
  103. $msg = $e->getMessage();
  104. util::fail($msg);
  105. }
  106. //农历转阳历,有多处使用,需要同步修改,关键词 change_my_birthday
  107. $month = $result['gregorian_month'] ?? 1;
  108. $date = $result['gregorian_day'] ?? 1;
  109. $birthday = date("Y") . '-' . $month . '-' . $date;
  110. } else {
  111. $birthday = date("Y") . '-' . $birthdayMonth . '-' . $birthdayDate;
  112. }
  113. $birthdayTime = strtotime($birthday);
  114. if (getenv('YII_ENV') != 'dev' && $user->birthdaySet == 1) {
  115. util::fail('客户已填写,不能修改');
  116. }
  117. $custom->lunar = $lunar;
  118. $custom->birthdayDate = $birthdayDate;
  119. $custom->birthdayMonth = $birthdayMonth;
  120. $custom->birthday = $birthday;
  121. $custom->birthdayTime = $birthdayTime;
  122. $custom->save();
  123. $user->lunar = $lunar;
  124. $user->birthdayDate = $birthdayDate;
  125. $user->birthdayMonth = $birthdayMonth;
  126. $user->birthday = $birthday;
  127. $user->birthdayTime = $birthdayTime;
  128. $user->save();
  129. }
  130. // 同步变更生日赠礼记录表(xhBirthdayGift)中的数据(只更新当年年份的,往年的不去修改)
  131. $birthdayGift = BirthdayGiftClass::getByCondition(['userId'=>$user->id, 'shopId'=>$shopId, 'year'=>date("Y")], true);
  132. if (!empty($birthdayGift)) {
  133. $lunar = intval($custom->lunar);
  134. $birthdayMonth = intval($custom->birthdayMonth);
  135. $birthdayDate = intval($custom->birthdayDate);
  136. list($giftBirthdayMonth, $giftBirthdayDate) = BirthdayGiftClass::convertBirthdayMonthDay(
  137. $lunar,
  138. $birthdayMonth,
  139. $birthdayDate
  140. );
  141. $birthdayGift->lunar = $lunar;
  142. $birthdayGift->birthdayMonth = $giftBirthdayMonth;
  143. $birthdayGift->birthdayDate = $giftBirthdayDate;
  144. $birthdayGift->birthdayDisplay = BirthdayGiftClass::formatBirthdayDisplay($custom, $giftBirthdayMonth, $giftBirthdayDate);
  145. $birthdayGift->status = 1;
  146. $birthdayGift->save();
  147. }
  148. return [$custom, $isUpdateBirthday];
  149. }
  150. //用余额支付,客户余额减少 ssh 20250410
  151. public static function payToChangeBalance($order)
  152. {
  153. $actPrice = $order->actPrice ?? 0;
  154. $hdId = $order->hdId ?? 0;
  155. $hd = HdClass::getLockById($hdId);
  156. $balance = $hd->balance ?? 0;
  157. if ($actPrice > $balance) {
  158. util::fail('余额不足');
  159. }
  160. $remain = bcsub($balance, $actPrice, 2);
  161. $hd->balance = $remain;
  162. $hd->save();
  163. $hdName = $hd->name;
  164. $customId = $order->customId ?? 0;
  165. $custom = self::getLockById($customId);
  166. $balance = $custom->balance ?? 0;
  167. if ($actPrice > $balance) {
  168. util::fail('余额不足哦');
  169. }
  170. $remain = bcsub($balance, $actPrice, 2);
  171. $custom->balance = $remain;
  172. $custom->save();
  173. $customName = $custom->name;
  174. if ($custom->balance != $hd->balance) {
  175. util::fail('余额有问题哦');
  176. }
  177. $relateId = $order->id ?? 0;
  178. //0 花店操作 1 客户操作
  179. $payWay = $order->payWay;
  180. $side = 1;
  181. $shopId = $order->shopId;
  182. $mainId = $order->mainId;
  183. $staffId = $order->shopAdminId;
  184. $staffName = $order->shopAdminName;
  185. $orderSn = $order->orderSn;
  186. $event = '买花,单号:' . $orderSn;
  187. $capitalType = dict::getDict('capitalType', 'xhOrder', 'id');
  188. $change = [
  189. 'hdId' => $hdId,
  190. 'hdName' => $hdName,
  191. 'customId' => $customId,
  192. 'customName' => $customName,
  193. 'relateId' => $relateId,
  194. 'onlinePay' => 1,
  195. 'capitalType' => $capitalType,
  196. 'amount' => $actPrice,
  197. 'balance' => $hd->balance,
  198. 'io' => 0,
  199. 'side' => $side,
  200. 'payWay' => $payWay,
  201. 'event' => $event,
  202. 'staffId' => $staffId,
  203. 'staffName' => $staffName,
  204. 'shopId' => $shopId,
  205. 'mainId' => $mainId,
  206. 'remark' => '',
  207. ];
  208. BalanceChangeClass::add($change, true);
  209. $order->balance = $actPrice;
  210. $order->save();
  211. // 多处需要同步修改 balance_pay_give
  212. if ($hd->balancePay >= $actPrice) {
  213. $payAmount = $actPrice;
  214. $mustUseGiveAmount = 0;
  215. } else {
  216. $payAmount = $hd->balancePay;
  217. $mustUseGiveAmount = bcsub($actPrice, $payAmount, 2);
  218. if ($mustUseGiveAmount > $hd->balanceGive) {
  219. util::fail('赠送余额异常,不够扣');
  220. }
  221. }
  222. /************************* 使用充值余额 *****************************/
  223. $customBalancePay = bcsub($custom->balancePay, $payAmount, 2);
  224. $custom->balancePay = $customBalancePay;
  225. $custom->save();
  226. $hdBalancePay = bcsub($hd->balancePay, $payAmount, 2);
  227. $hd->balancePay = $hdBalancePay;
  228. $hd->save();
  229. $payChange = [
  230. 'hdId' => $hdId,
  231. 'hdName' => $hdName,
  232. 'customId' => $customId,
  233. 'customName' => $customName,
  234. 'relateId' => $relateId,
  235. 'onlinePay' => 1,
  236. 'capitalType' => $capitalType,
  237. 'amount' => $payAmount,
  238. 'balance' => $customBalancePay,
  239. 'io' => 0,
  240. 'side' => $side,
  241. 'payWay' => $payWay,
  242. 'event' => $event,
  243. 'staffId' => $staffId,
  244. 'staffName' => $staffName,
  245. 'shopId' => $shopId,
  246. 'mainId' => $mainId,
  247. 'remark' => '',
  248. ];
  249. BalancePayChangeClass::add($payChange, true);
  250. $order->payBalance = $payAmount;
  251. $order->save();
  252. /***************************** 使用赠送余额 ******************************/
  253. //应用掉的赠送金额
  254. if ($mustUseGiveAmount > 0) {
  255. $customBalanceGive = bcsub($custom->balanceGive, $mustUseGiveAmount, 2);
  256. if ($customBalanceGive < 0) {
  257. util::fail('余额异常哈!请联系管理员,编号' . $hdId . ' ' . $custom->balanceGive);
  258. }
  259. $custom->balanceGive = $customBalanceGive;
  260. $custom->save();
  261. $hdBalanceGive = bcsub($hd->balanceGive, $mustUseGiveAmount, 2);
  262. $hd->balanceGive = $hdBalanceGive;
  263. $hd->save();
  264. if (floatval($customBalanceGive) != floatval($hdBalanceGive)) {
  265. util::fail('余额有问题呢!请联系管理员,编号' . $hdId);
  266. }
  267. $giveChange = [
  268. 'hdId' => $hdId,
  269. 'hdName' => $hdName,
  270. 'customId' => $customId,
  271. 'customName' => $customName,
  272. 'relateId' => $relateId,
  273. 'onlinePay' => 1,
  274. 'capitalType' => $capitalType,
  275. 'amount' => $mustUseGiveAmount,
  276. 'balance' => $customBalanceGive,
  277. 'io' => 0,
  278. 'side' => $side,
  279. 'payWay' => $payWay,
  280. 'event' => $event,
  281. 'staffId' => $staffId,
  282. 'staffName' => $staffName,
  283. 'shopId' => $shopId,
  284. 'mainId' => $mainId,
  285. 'remark' => '',
  286. ];
  287. BalanceGiveChangeClass::add($giveChange, true);
  288. $order->giveBalance = $mustUseGiveAmount;
  289. $order->save();
  290. }
  291. $addBalance = bcadd($hd->balancePay, $hd->balanceGive, 2);
  292. if (floatval($addBalance) != floatval($custom->balance)) {
  293. util::fail('账户余额有问题!请检查,编号' . $customId);
  294. }
  295. }
  296. //会员等级变化,包括手动修改、自动降会员和充值升会员等 ssh 20250324
  297. public static function levelChange($oldLevel, $newLevel, $shop, $custom, $hd, $params)
  298. {
  299. $adminId = $params['adminId'];
  300. $staffId = $params['staffId'] ?? 0;
  301. $staffName = $params['staffName'] ?? '';
  302. $style = $params['style'] ?? 0;
  303. $target = $params['target'] ?? [];
  304. $rechargeAmount = $params['rechargeAmount'] ?? 0;
  305. $targetId = $target->id ?? 0;
  306. $mainId = $shop->mainId;
  307. $shopId = $shop->id;
  308. $levelData = MemberLevelClass::getAllByCondition(['mainId' => $mainId], null, '*', 'level');
  309. $current = $levelData[$newLevel] ?? [];
  310. $before = $levelData[$oldLevel] ?? [];
  311. $beforeName = $before['name'] ?? '普通客户';
  312. $discount = $current['discount'] ? $current['discount'] / 10 : 1;
  313. $memberName = $current['name'] ?? '';
  314. $oldGrowth = $custom->growth;
  315. $hd->member = $newLevel;
  316. $hd->discount = $discount;
  317. $hd->memberName = $memberName;
  318. $hd->save();
  319. $event = '手动修改';
  320. if ($style == 0) {
  321. util::fail('充值不支持升级会员');
  322. }
  323. $change = [
  324. 'customId' => $custom->id,
  325. 'customName' => $custom->name,
  326. 'hdId' => $hd->id,
  327. 'hdName' => $hd->name,
  328. 'mainId' => $mainId,
  329. 'shopId' => $shopId,
  330. 'targetId' => $targetId,
  331. 'rechargeAmount' => $rechargeAmount,
  332. 'days' => 0,
  333. 'daysBefore' => '0000-00-00 00:00:00',
  334. 'daysAfter' => '0000-00-00 00:00:00',
  335. 'beforeLevel' => $oldLevel,
  336. 'afterLevel' => $newLevel,
  337. 'beforeName' => $beforeName,
  338. 'afterName' => $memberName,
  339. 'style' => $style,
  340. 'event' => $event,
  341. 'staffId' => $staffId,
  342. 'staffName' => $staffName,
  343. ];
  344. MemberChangeClass::add($change);
  345. $custom->member = $newLevel;
  346. $custom->discount = $discount;
  347. $custom->memberName = $memberName;
  348. //成长值变更
  349. $growth = $current['amount'] ?? 0;
  350. $custom->growth = $growth;
  351. $custom->save();
  352. //成长值变动记录
  353. $growthData = [
  354. 'shopId' => $shopId,
  355. 'userId' => $custom->userId,
  356. 'userName' => $custom->name,
  357. 'event' => "管理员变更{$memberName}级,成长值从{$oldGrowth}变更为{$growth}",
  358. 'relateId' => 0,
  359. 'relateType' => 0,
  360. 'num' => $custom->growth - $oldGrowth,
  361. 'growth' => $growth,
  362. //'operatorId' => $adminId,
  363. 'staffId' => $staffId, //操作人(员工id)
  364. 'staffName' => $staffName, //员工名称
  365. 'createTime' => date('Y-m-d H:i:s')
  366. ];
  367. UserGrowthClass::add($growthData);
  368. }
  369. //客户采购欠款金额增加,如果有余额时,要进行欠款销账 ssh 20250715
  370. //这边开欠款单时,如果还有余额,会产生结账动作,结账动作也可以用赠送余额来结账,所以要分出赠送余额的占比
  371. //结账消余额,不是下单时消余额,不涉及售后时余额回滚问题,所以不需要设置 xhOrder 的 giveBalance payBalance balance等值
  372. public static function skCgDebtAmountAdd($custom, $order)
  373. {
  374. $clearAmount = 0;
  375. $amount = $order->remainDebtPrice ?? 0;
  376. $staffId = $order->shopAdminId ?? 0;
  377. $staffName = $order->shopAdminName ?? '';
  378. $orderId = $order->id;
  379. $orderSn = $order->orderSn ?? '';
  380. $shopId = $order->shopId;
  381. $settleNo = '';
  382. $settleId = 0;
  383. $settleAmount = 0;
  384. $hdId = $custom->hdId ?? 0;
  385. $hd = HdClass::getLockById($hdId);
  386. if (empty($hd)) {
  387. util::fail('花店信息缺失,编号6325');
  388. }
  389. $hdName = $hd->name ?? '';
  390. //结账时用了多少赠送的余额
  391. $settleUseGiveAmount = 0;
  392. if ($custom->balance > 0) {
  393. if ($amount > $custom->balance) {
  394. $clearAmount = $custom->balance;
  395. $settleUseGiveAmount = $custom->balanceGive;
  396. } else {
  397. util::fail('余额充足,请用余额支付');
  398. }
  399. }
  400. if ($clearAmount > 0) {
  401. $shop = ShopClass::getById($shopId, true);
  402. $needClearIdMap[] = ['orderId' => $orderId, 'clearAmount' => $clearAmount, 'orderSn' => $orderSn];
  403. $setParams = ['staffId' => $staffId, 'staffName' => $staffName];
  404. $set = SettleClass::addSettle($needClearIdMap, $shop, $custom, $hd, $setParams);
  405. $ret = SettleClass::clearSettle($set);
  406. $settleAmount = $ret['totalClearAmount'] ?? 0;
  407. $settleNo = $set->orderSn ?? '';
  408. $settleId = $set->id;
  409. $newSettle = $ret['settle'];
  410. //结账时用了多少赠送的余额
  411. $newSettle->giveBalance = bcadd($newSettle->giveBalance, $settleUseGiveAmount, 2);
  412. $newSettle->save();
  413. }
  414. /************************ 总余额 *********************************/
  415. $customBalance = bcsub($custom->balance, $amount, 2);
  416. $custom->balance = $customBalance;
  417. if ($custom->balance < 0) {
  418. $custom->isDebt = 1;
  419. }
  420. $custom->save();
  421. $hdBalance = bcsub($hd->balance, $amount, 2);
  422. $hd->balance = $hdBalance;
  423. if ($hd->balance < 0) {
  424. $hd->debt = 2;
  425. }
  426. $hd->save();
  427. if (floatval($customBalance) != floatval($hdBalance)) {
  428. util::fail('余额有错,请联系管理员,编号' . $hdId);
  429. }
  430. $relateId = $order->id ?? 0;
  431. $customId = $custom->id ?? 0;
  432. $orderSn = $order->orderSn ?? '';
  433. $capitalType = dict::getDict('capitalType', 'xhOrder', 'id');
  434. $event = '购买花材,单号:' . $orderSn;
  435. $customName = $custom->name ?? '';
  436. $onlinePay = 1;
  437. $side = 0;
  438. $payWay = 0;
  439. $change = [
  440. 'hdId' => $hdId,
  441. 'hdName' => $hdName,
  442. 'customId' => $customId,
  443. 'customName' => $customName,
  444. 'relateId' => $relateId,
  445. 'onlinePay' => $onlinePay,
  446. 'capitalType' => $capitalType,
  447. 'amount' => $amount,
  448. 'balance' => $customBalance,
  449. 'io' => 0,
  450. 'side' => $side,
  451. 'payWay' => $payWay,
  452. 'event' => $event,
  453. 'staffId' => $staffId,
  454. 'staffName' => $staffName,
  455. 'shopId' => $shopId,
  456. 'mainId' => $order->mainId,
  457. 'settleId' => $settleId,
  458. 'settleNo' => $settleNo,
  459. 'settleAmount' => $settleAmount,
  460. 'remark' => '',
  461. ];
  462. BalanceChangeClass::add($change, true);
  463. $mustUseGiveAmount = 0;
  464. // 多处需要同步修改 balance_pay_give
  465. if ($hd->balancePay >= $amount) {
  466. util::fail('请用余额支付哈');
  467. } else {
  468. // 如果【充值金额】小于订单欠款金额,则用全部【充值金额】消单,同时必须用上【赠送金额】,够抵,则【充值金额】还是正数,不够抵,则是负数
  469. if ($hd->balanceGive <= 0) {
  470. //没有【赠送金额】,则用【充值金额】来消全部的欠款金额,不够则变成负数
  471. $payAmount = $amount;
  472. } else {
  473. //这个是【总余额】,包括【充值金额】和【赠送金额】
  474. $currentAdd = bcadd($hd->balancePay, $hd->balanceGive, 2);
  475. if ($currentAdd >= $amount) {
  476. util::fail('请使用余额支付哦');
  477. } else {
  478. //【总余额】不够抵欠款金额,先用掉全部的【赠送金额】,其他的全部算在【充值金额】名下,因为充值金额也不够扣,会出现负数
  479. $mustUseGiveAmount = $hd->balanceGive;
  480. $payAmount = bcsub($amount, $mustUseGiveAmount, 2);
  481. }
  482. }
  483. }
  484. /************************* 充值余额 *****************************/
  485. $customBalancePay = bcsub($custom->balancePay, $payAmount, 2);
  486. $custom->balancePay = $customBalancePay;
  487. $custom->save();
  488. $hdBalancePay = bcsub($hd->balancePay, $payAmount, 2);
  489. $hd->balancePay = $hdBalancePay;
  490. $hd->save();
  491. if (floatval($customBalancePay) != floatval($hdBalancePay)) {
  492. util::fail('余额有错呢,请联系管理员,编号' . $hdId);
  493. }
  494. $relateId = $order->id ?? 0;
  495. $customId = $custom->id ?? 0;
  496. $orderSn = $order->orderSn ?? '';
  497. $capitalType = dict::getDict('capitalType', 'xhOrder', 'id');
  498. $event = '购买花材,单号:' . $orderSn;
  499. $customName = $custom->name ?? '';
  500. $onlinePay = 1;
  501. $side = 0;
  502. $payWay = 0;
  503. $payChange = [
  504. 'hdId' => $hdId,
  505. 'hdName' => $hdName,
  506. 'customId' => $customId,
  507. 'customName' => $customName,
  508. 'relateId' => $relateId,
  509. 'onlinePay' => $onlinePay,
  510. 'capitalType' => $capitalType,
  511. 'amount' => $payAmount,
  512. 'balance' => $customBalancePay,
  513. 'io' => 0,
  514. 'side' => $side,
  515. 'payWay' => $payWay,
  516. 'event' => $event,
  517. 'staffId' => $staffId,
  518. 'staffName' => $staffName,
  519. 'shopId' => $shopId,
  520. 'mainId' => $order->mainId,
  521. 'settleId' => $settleId,
  522. 'settleNo' => $settleNo,
  523. 'settleAmount' => $settleAmount,
  524. 'remark' => '',
  525. ];
  526. BalancePayChangeClass::add($payChange, true);
  527. /***************************** 赠送余额 ******************************/
  528. //应用掉的赠送金额
  529. if ($mustUseGiveAmount > 0) {
  530. $customBalanceGive = bcsub($custom->balanceGive, $mustUseGiveAmount, 2);
  531. if ($customBalanceGive < 0) {
  532. util::fail('余额异常哈,请联系管理员,编号' . $hdId . ' ' . $custom->balanceGive);
  533. }
  534. $custom->balanceGive = $customBalanceGive;
  535. $custom->save();
  536. $hdBalanceGive = bcsub($hd->balanceGive, $mustUseGiveAmount, 2);
  537. $hd->balanceGive = $hdBalanceGive;
  538. $hd->save();
  539. if (floatval($customBalanceGive) != floatval($hdBalanceGive)) {
  540. util::fail('余额有问题呢,请联系管理员,编号' . $hdId);
  541. }
  542. $relateId = $order->id ?? 0;
  543. $customId = $custom->id ?? 0;
  544. $orderSn = $order->orderSn ?? '';
  545. $capitalType = dict::getDict('capitalType', 'xhOrder', 'id');
  546. $event = '购买花材,单号:' . $orderSn;
  547. $customName = $custom->name ?? '';
  548. $onlinePay = 1;
  549. $side = 0;
  550. $payWay = 0;
  551. $giveChange = [
  552. 'hdId' => $hdId,
  553. 'hdName' => $hdName,
  554. 'customId' => $customId,
  555. 'customName' => $customName,
  556. 'relateId' => $relateId,
  557. 'onlinePay' => $onlinePay,
  558. 'capitalType' => $capitalType,
  559. 'amount' => $mustUseGiveAmount,
  560. 'balance' => $customBalanceGive,
  561. 'io' => 0,
  562. 'side' => $side,
  563. 'payWay' => $payWay,
  564. 'event' => $event,
  565. 'staffId' => $staffId,
  566. 'staffName' => $staffName,
  567. 'shopId' => $shopId,
  568. 'mainId' => $order->mainId,
  569. 'settleId' => $settleId,
  570. 'settleNo' => $settleNo,
  571. 'settleAmount' => $settleAmount,
  572. 'remark' => '',
  573. ];
  574. BalanceGiveChangeClass::add($giveChange, true);
  575. }
  576. $addBalance = bcadd($hd->balancePay, $hd->balanceGive, 2);
  577. if (floatval($addBalance) != floatval($custom->balance)) {
  578. util::fail('账户余额有问题,请检查,编号' . $customId);
  579. }
  580. }
  581. /**
  582. * 隔天/无原单售后返充余额(不依赖销售单 balanceTk 拆分)
  583. * 用途:成功页「返充到客户余额」;无原单时 $order 可为空。
  584. * 入账规则:实付退回算本金,同步 +balance 与 +balancePay,并写总流水 + 充值余额流水;
  585. * 不进赠送余额(赠送仅来自充值活动),以保持 balance ≈ balancePay + balanceGive。
  586. */
  587. public static function nextDayRefundReturnBalance($custom, $amount, $refund, $order = null)
  588. {
  589. $amount = bcadd((string)$amount, '0', 2);
  590. if (bccomp($amount, '0', 2) <= 0) {
  591. util::fail('返充金额必须大于0');
  592. }
  593. $hdId = $custom->hdId ?? 0;
  594. $hd = HdClass::getLockById($hdId);
  595. if (empty($hd)) {
  596. util::fail('花店信息缺失,编号5363');
  597. }
  598. // —— 总余额 ——
  599. $customBalance = bcadd((string)($custom->balance ?? '0'), $amount, 2);
  600. $custom->balance = $customBalance;
  601. $custom->isDebt = bccomp($customBalance, '0', 2) < 0 ? 1 : 0;
  602. $custom->save();
  603. $hdBalance = bcadd((string)($hd->balance ?? '0'), $amount, 2);
  604. $hd->balance = $hdBalance;
  605. if (bccomp($hdBalance, '0', 2) < 0) {
  606. $hd->debt = 2;
  607. }
  608. $hd->save();
  609. if (floatval($customBalance) !== floatval($hdBalance)) {
  610. util::fail('余额错误,请联系管理员,编号' . $hdId);
  611. }
  612. // —— 充值本金:线下/无原单返充视为实付退回,进 balancePay(不进赠送)——
  613. $customBalancePay = bcadd((string)($custom->balancePay ?? '0'), $amount, 2);
  614. $custom->balancePay = $customBalancePay;
  615. $custom->save(false, ['balancePay']);
  616. $hdBalancePay = bcadd((string)($hd->balancePay ?? '0'), $amount, 2);
  617. $hd->balancePay = $hdBalancePay;
  618. $hd->save(false, ['balancePay']);
  619. if (floatval($customBalancePay) !== floatval($hdBalancePay)) {
  620. util::fail('充值余额错误,请联系管理员,编号' . $hdId);
  621. }
  622. $refundSn = $refund->refundSn ?? '';
  623. $orderId = intval($refund->orderId ?? 0);
  624. $saleOrderSn = !empty($order) ? ($order->orderSn ?? '') : '';
  625. if ($saleOrderSn === '' && $orderId > 0) {
  626. $saleOrderSn = $refund->orderSn ?? '';
  627. }
  628. // 无原单流水只挂售后单号,避免「原单:」空串误导
  629. $event = ($orderId > 0 && $saleOrderSn !== '')
  630. ? "跨天售后返充余额,售后单:{$refundSn},原单:{$saleOrderSn}"
  631. : "无原单退款返充余额,售后单:{$refundSn}";
  632. $capitalType = dict::getDict('capitalType', 'hdRefund', 'id');
  633. $customId = intval($custom->id ?? 0);
  634. $customName = $custom->name ?? '';
  635. $staffId = intval($refund->shopAdminId ?? 0);
  636. $staffName = $refund->shopAdminName ?? '';
  637. $shopId = intval($refund->shopId ?? 0);
  638. $mainId = intval($refund->mainId ?? 0);
  639. $changeBase = [
  640. 'customId' => $customId,
  641. 'customName' => $customName,
  642. 'relateId' => $refund->id ?? 0,
  643. 'relateSn' => $refundSn,
  644. 'onlinePay' => 1,
  645. 'capitalType' => $capitalType,
  646. 'io' => 1,
  647. 'side' => 0,
  648. 'payWay' => 2,
  649. 'event' => $event,
  650. 'staffId' => $staffId,
  651. 'staffName' => $staffName,
  652. 'shopId' => $shopId,
  653. 'mainId' => $mainId,
  654. 'refundOrderId' => $orderId,
  655. 'refundOrderSn' => $saleOrderSn,
  656. 'remark' => '',
  657. ];
  658. // 总余额流水(balanceChange 页)
  659. BalanceChangeClass::add(array_merge($changeBase, [
  660. 'amount' => $amount,
  661. 'balance' => $customBalance,
  662. ]), true);
  663. // 充值本金流水(balancePayChange / 商城余额变动页)
  664. BalancePayChangeClass::add(array_merge($changeBase, [
  665. 'amount' => $amount,
  666. 'balance' => $hdBalancePay,
  667. ]), true);
  668. // 返充入账后:优先结当前售后原单挂账,剩余再 FIFO(不再二次扣余额)
  669. $balanceBefore = bcsub($customBalance, $amount, 2);
  670. self::fifoClearAfterRefundCredit($custom, $hd, $refund, $amount, $balanceBefore, $order);
  671. $custom = CustomClass::getById($customId, true);
  672. return bcadd((string)($custom->balance ?? $customBalance), '0', 2);
  673. }
  674. //欠款和余额支付的售后 ssh 20250803
  675. public static function skRefundDebtAmountReduce($custom, $amount, $refund, $payWay)
  676. {
  677. $hdId = $custom->hdId ?? 0;
  678. $hd = HdClass::getLockById($hdId);
  679. if (empty($hd)) {
  680. util::fail('花店信息缺失,编号5362');
  681. }
  682. // 入账前净余额,供结账单 payWay 判定
  683. $balanceBefore = bcadd((string)($custom->balance ?? '0'), '0', 2);
  684. // 原始支付方式:后面流水会改写成余额付,FIFO 判定要用原值
  685. $originPayWay = intval($payWay);
  686. $customBalance = bcadd($custom->balance, $amount, 2);
  687. $custom->balance = $customBalance;
  688. if ($custom->balance < 0) {
  689. $custom->isDebt = 1;
  690. }
  691. $custom->save();
  692. $hdBalance = bcadd($hd->balance, $amount, 2);
  693. $hd->balance = $hdBalance;
  694. if ($hd->balance < 0) {
  695. $hd->debt = 2;
  696. }
  697. $hd->save();
  698. if (floatval($customBalance) !== floatval($hdBalance)) {
  699. util::fail('余额错误,请联系管理员,编号' . $hdId);
  700. }
  701. $relateId = $refund->id ?? 0;
  702. $customId = $custom->id ?? 0;
  703. $refundSn = $refund->refundSn ?? '';
  704. $orderSn = $refund->orderSn ?? '';
  705. $orderId = $refund->orderId ?? 0;
  706. $order = OrderClass::getLockById($orderId);
  707. if (empty($order)) {
  708. util::fail('没有找到订单哈!');
  709. }
  710. // 多处需要同步修改 balance_pay_give
  711. $mustUseGiveAmount = 0;
  712. $buyAmount = 0;
  713. if ($payWay == 3) {
  714. //如果是欠款单售后,没有退款到赠送余额的情况
  715. $buyAmount = $amount;
  716. } elseif ($payWay == 2) {
  717. //这个是总消费余额中充值余额部分
  718. $payBalance = $order->payBalance ?? 0;
  719. //这个是累计已经退还的余额
  720. $balanceTk = $order->balanceTk ?? 0;
  721. $currentBalanceTk = bcadd($balanceTk, $amount, 2);
  722. //当前累计退还的余额如果超过了充值余额的话,说明要退还赠送的余额了
  723. if ($currentBalanceTk > $payBalance) {
  724. $pastAmount = bcsub($currentBalanceTk, $payBalance, 2);
  725. if ($pastAmount > $amount) {
  726. $mustUseGiveAmount = $amount;
  727. } else {
  728. $buyAmount = bcsub($amount, $pastAmount, 2);
  729. $mustUseGiveAmount = bcsub($amount, $buyAmount, 2);
  730. }
  731. } else {
  732. //累计退还的余额,还没超过充值余额,还是退充值余额部分
  733. $buyAmount = $amount;
  734. }
  735. } else {
  736. util::fail('无效的支付方式!编号69880');
  737. }
  738. $staffId = $refund->shopAdminId ?? 0;
  739. $staffName = $refund->shopAdminName ?? '';
  740. $capitalType = dict::getDict('capitalType', 'hdRefund', 'id');
  741. $event = "订单售后 " . $refundSn;
  742. $customName = $custom->name ?? '';
  743. $onlinePay = 1;
  744. $side = 0;
  745. $payWay = 2;//余额支付
  746. $change = [
  747. 'customId' => $customId,
  748. 'customName' => $customName,
  749. 'relateId' => $relateId,
  750. 'relateSn' => $refundSn,
  751. 'onlinePay' => $onlinePay,
  752. 'capitalType' => $capitalType,
  753. 'amount' => $amount,
  754. 'balance' => $customBalance,
  755. 'io' => 1,
  756. 'side' => $side,
  757. 'payWay' => $payWay,
  758. 'event' => $event,
  759. 'staffId' => $staffId,
  760. 'staffName' => $staffName,
  761. 'shopId' => $refund->shopId,
  762. 'mainId' => $refund->mainId,
  763. 'refundOrderId' => $orderId,
  764. 'refundOrderSn' => $orderSn,
  765. 'remark' => '',
  766. ];
  767. BalanceChangeClass::add($change, true);
  768. $order->balanceTk = bcadd($order->balanceTk, $amount, 2);
  769. if ($order->balanceTk < 0) {
  770. util::fail('不能小于0,编号88672');
  771. }
  772. $order->save();
  773. if ($buyAmount > 0) {
  774. $customBalancePay = bcadd($custom->balancePay, $buyAmount, 2);
  775. $custom->balancePay = $customBalancePay;
  776. $custom->save();
  777. $hdBalancePay = bcadd($hd->balancePay, $buyAmount, 2);
  778. $hd->balancePay = $hdBalancePay;
  779. $hd->save();
  780. $payChange = [
  781. 'customId' => $customId,
  782. 'customName' => $customName,
  783. 'relateId' => $relateId,
  784. 'relateSn' => $refundSn,
  785. 'onlinePay' => $onlinePay,
  786. 'capitalType' => $capitalType,
  787. 'amount' => $buyAmount,
  788. 'balance' => $hdBalancePay,
  789. 'io' => 1,
  790. 'side' => $side,
  791. 'payWay' => $payWay,
  792. 'event' => $event,
  793. 'staffId' => $staffId,
  794. 'staffName' => $staffName,
  795. 'shopId' => $refund->shopId,
  796. 'mainId' => $refund->mainId,
  797. 'refundOrderId' => $orderId,
  798. 'refundOrderSn' => $orderSn,
  799. 'remark' => '',
  800. ];
  801. BalancePayChangeClass::add($payChange, true);
  802. $order->payBalanceTk = bcadd($order->payBalanceTk, $buyAmount, 2);
  803. if ($order->payBalanceTk < 0) {
  804. util::fail('不能小于0,编号88673');
  805. }
  806. $order->save();
  807. }
  808. if ($mustUseGiveAmount > 0) {
  809. $customBalanceGive = bcadd($custom->balanceGive, $mustUseGiveAmount, 2);
  810. $custom->balanceGive = $customBalanceGive;
  811. $custom->save();
  812. $hdBalanceGive = bcadd($hd->balanceGive, $mustUseGiveAmount, 2);
  813. $hd->balanceGive = $hdBalanceGive;
  814. $hd->save();
  815. $giveChange = [
  816. 'customId' => $customId,
  817. 'customName' => $customName,
  818. 'relateId' => $relateId,
  819. 'relateSn' => $refundSn,
  820. 'onlinePay' => $onlinePay,
  821. 'capitalType' => $capitalType,
  822. 'amount' => $mustUseGiveAmount,
  823. 'balance' => $hdBalanceGive,
  824. 'io' => 1,
  825. 'side' => $side,
  826. 'payWay' => $payWay,
  827. 'event' => $event,
  828. 'staffId' => $staffId,
  829. 'staffName' => $staffName,
  830. 'shopId' => $refund->shopId,
  831. 'mainId' => $refund->mainId,
  832. 'refundOrderId' => $orderId,
  833. 'refundOrderSn' => $orderSn,
  834. 'remark' => '',
  835. ];
  836. BalanceGiveChangeClass::add($giveChange, true);
  837. $order->giveBalanceTk = bcadd($order->giveBalanceTk, $mustUseGiveAmount, 2);
  838. if ($order->giveBalanceTk < 0) {
  839. util::fail('不能小于0,编号88675');
  840. }
  841. $order->save();
  842. }
  843. // 当天挂账售后已先减 remainDebtPrice,再 FIFO 优先本单会重复销账;仅隔天/返充类或余额付原路才消欠
  844. $sameDay = intval($refund->sameDay ?? 1);
  845. $debtPay = intval(dict::getDict('payWay', 'debtPay'));
  846. $skipFifo = ($originPayWay === $debtPay && $sameDay === 1);
  847. if (!$skipFifo) {
  848. self::fifoClearAfterRefundCredit($custom, $hd, $refund, $amount, $balanceBefore, $order);
  849. }
  850. }
  851. /**
  852. * 售后返充/原路退余额后销挂账:优先当前售后原单 remainDebtPrice(可部分结),再按 id 升序 FIFO。
  853. * 调用时机:余额已入账;本方法只用本次金额作资金池,不再扣 balance。
  854. *
  855. * @param object $custom 客户(已加锁)
  856. * @param object $hd 花店账户
  857. * @param object $refund 售后单
  858. * @param string|float $amount 本次入账金额
  859. * @param string|float $balanceBefore 入账前净余额
  860. * @param object|null $order 售后关联销售单
  861. */
  862. protected static function fifoClearAfterRefundCredit($custom, $hd, $refund, $amount, $balanceBefore, $order = null)
  863. {
  864. $pool = bcadd((string)$amount, '0', 2);
  865. if (bccomp($pool, '0', 2) <= 0) {
  866. return;
  867. }
  868. $customId = intval($custom->id ?? 0);
  869. if ($customId <= 0) {
  870. return;
  871. }
  872. $preferOrderId = !empty($order) ? intval($order->id ?? 0) : intval($refund->orderId ?? 0);
  873. $plan = self::planFifoClearByPool($customId, $pool, $preferOrderId);
  874. if (empty($plan)) {
  875. return;
  876. }
  877. $shopId = intval($refund->shopId ?? ($custom->shopId ?? 0));
  878. $shop = $shopId > 0 ? ShopClass::getById($shopId, true) : null;
  879. if (empty($shop)) {
  880. // 无门店无法建结账单,跳过销账(余额已入账,不阻断售后)
  881. return;
  882. }
  883. $settleParams = [
  884. 'staffId' => intval($refund->shopAdminId ?? 0),
  885. 'staffName' => (string)($refund->shopAdminName ?? ''),
  886. ];
  887. $set = SettleClass::addSettle($plan, $shop, $custom, $hd, $settleParams);
  888. if (!empty($set)) {
  889. SettleClass::clearSettle($set);
  890. }
  891. // 刷新客户欠款标记(有未结挂账单则为欠款户)
  892. $leftDebt = OrderClass::getAllByCondition(['customId' => $customId, 'debt' => 1], null, 'id', null, true);
  893. $customFresh = self::getLockById($customId);
  894. if (!empty($customFresh)) {
  895. $customFresh->isDebt = !empty($leftDebt) ? 1 : 0;
  896. $customFresh->save(false, ['isDebt']);
  897. }
  898. }
  899. /**
  900. * 销挂账分配:可选优先本单,剩余按订单 id 升序 FIFO。
  901. * prefer 路径只用 remainDebtPrice,不用 actPrice 兜底,避免脏数据多销。
  902. *
  903. * @param int $customId
  904. * @param string|float $poolAmount
  905. * @param int $preferOrderId
  906. * @return array<int, array{orderId:int, clearAmount:string, orderSn:string}>
  907. */
  908. protected static function planFifoClearByPool($customId, $poolAmount, $preferOrderId = 0)
  909. {
  910. $pool = bcadd((string)$poolAmount, '0', 2);
  911. if (bccomp($pool, '0', 2) <= 0) {
  912. return [];
  913. }
  914. $orderList = OrderClass::getAllByCondition(['customId' => $customId, 'debt' => 1], 'id asc', '*', null, true);
  915. if (empty($orderList)) {
  916. return [];
  917. }
  918. $plan = [];
  919. $used = '0.00';
  920. $preferOrderId = intval($preferOrderId);
  921. // 1)售后原单仍挂账:先结本单(不够则部分结)
  922. if ($preferOrderId > 0) {
  923. foreach ($orderList as $ord) {
  924. if (intval($ord->id ?? 0) !== $preferOrderId) {
  925. continue;
  926. }
  927. $remain = bcadd((string)($ord->remainDebtPrice ?? '0'), '0', 2);
  928. if (bccomp($remain, '0', 2) <= 0) {
  929. break;
  930. }
  931. $clearAmount = bccomp($remain, $pool, 2) <= 0 ? $remain : $pool;
  932. $plan[] = [
  933. 'orderId' => $preferOrderId,
  934. 'clearAmount' => $clearAmount,
  935. 'orderSn' => $ord->orderSn ?? '',
  936. ];
  937. $used = bcadd($used, $clearAmount, 2);
  938. break;
  939. }
  940. }
  941. // 2)剩余金额 FIFO(跳过已优先处理的本单)
  942. foreach ($orderList as $ord) {
  943. $left = bcsub($pool, $used, 2);
  944. if (bccomp($left, '0', 2) <= 0) {
  945. break;
  946. }
  947. $oid = intval($ord->id ?? 0);
  948. if ($preferOrderId > 0 && $oid === $preferOrderId) {
  949. continue;
  950. }
  951. $remain = bcadd((string)($ord->remainDebtPrice ?? '0'), '0', 2);
  952. if (bccomp($remain, '0', 2) <= 0) {
  953. continue;
  954. }
  955. $clearAmount = bccomp($remain, $left, 2) <= 0 ? $remain : $left;
  956. $plan[] = [
  957. 'orderId' => $oid,
  958. 'clearAmount' => $clearAmount,
  959. 'orderSn' => $ord->orderSn ?? '',
  960. ];
  961. $used = bcadd($used, $clearAmount, 2);
  962. }
  963. return $plan;
  964. }
  965. //添加客户 ssh 2021.2.28
  966. public static function addCustom($data)
  967. {
  968. return self::add($data, true);
  969. }
  970. //创建或获取客户信息 ssh 2021.3.19
  971. /**
  972. * 建立用户与花店的 xhCustom / hd 关系(花店增加客户)
  973. * @param object $shop 门店
  974. * @param object $user 商城用户
  975. * @param int $inviterCustomId 分享邀请人 xhCustom.id,仅新建客户时尝试拉新绑定
  976. * @return array|false custom、hd、isNewCustom、inviteBound
  977. */
  978. public static function buildRelation($shop, $user, $inviterCustomId = 0)
  979. {
  980. if (empty($user)) {
  981. return false;
  982. }
  983. if (empty($shop)) {
  984. return false;
  985. }
  986. if ($shop->ptStyle == 2) {
  987. util::fail('有问题,批发店状态');
  988. }
  989. $salt = stringUtil::charsShuffleLowerCase(8);
  990. $isNewCustom = 0;
  991. /** 仅 xhCustom 新建时为 1,供分享拉新绑定(与 hd 新建触发的 $isNewCustom 区分) */
  992. $customCreated = 0;
  993. $userId = $user->id;
  994. $name = $user->name ?? '';
  995. $mobile = $user->mobile ?? '';
  996. $shopId = $shop->id ?? 0;
  997. $mainId = $shop->mainId ?? 0;
  998. $hdAvatar = $shop->avatar ?? '';
  999. $sjId = $shop->sjId ?? 0;
  1000. $sjName = $shop->merchantName ?? '';
  1001. $shopName = $shop->shopName ?? '';
  1002. $hdMobile = $shop->mobile ?? '';
  1003. $hdName = $shopName == '首店' ? $sjName : $sjName . ' ' . $shopName;
  1004. $hdPy = stringUtil::py($hdName);
  1005. $custom = self::getByCondition(['shopId' => $shopId, 'userId' => $userId], true);
  1006. if (empty($custom)) {
  1007. $py = stringUtil::py($name);
  1008. $visitTime = date("Y-m-d H:i:s");
  1009. $data = [
  1010. 'userId' => $userId,
  1011. 'sjId' => $sjId,
  1012. 'shopId' => $shopId,
  1013. 'mainId'=>$mainId,
  1014. 'name' => $name,
  1015. 'originName' => $name,
  1016. 'py' => $py,
  1017. 'mobile' => $mobile,
  1018. 'visitTime' => $visitTime,
  1019. 'salt' => $salt,
  1020. ];
  1021. $birthdaySet = $user->birthdaySet ?? 0;
  1022. if ($birthdaySet == 1) {
  1023. //如果用户自己设置过了生日,则生日给传过去
  1024. $lunar = $user->lunar ?? 0;
  1025. $birthday = $user->birthday ?? '';
  1026. $birthdayTime = $user->birthdayTime ?? 0;
  1027. $birthdayMonth = $user->birthdayMonth ?? 0;
  1028. $birthdayDate = $user->birthdayDate ?? 0;
  1029. //如果当年的生日时间没有更新,则需要给更新
  1030. $year = date("Y", strtotime($birthday));
  1031. $thisYear = date("Y");
  1032. if ($year != $thisYear) {
  1033. if ($lunar == 1) {
  1034. try {
  1035. //农历转阳历,有多处使用,需要同步修改,关键词change_my_birthday
  1036. $calendar = new Calendar();
  1037. $result = $calendar->lunar(date("Y"), $birthdayMonth, $birthdayDate);
  1038. $month = $result['gregorian_month'] ?? 1;
  1039. $date = $result['gregorian_day'] ?? 1;
  1040. $birthday = date("Y") . '-' . $month . '-' . $date;
  1041. $user->birthday = $birthday;
  1042. $user->save();
  1043. CustomClass::updateByCondition(['userId' => $userId], ['birthday' => $birthday]);
  1044. } catch (\InvalidArgumentException $e) {
  1045. $msg = $e->getMessage();
  1046. noticeUtil::push("同步生日出错了,需要检查确认,userId:" . $userId . ' 月份:' . $birthdayMonth . ' 日期:' . $birthdayDate . ' ' . $msg, '15280215347');
  1047. $birthday = '0000-00-00 00:00:00';
  1048. }
  1049. } else {
  1050. $birthday = date("Y") . '-' . $birthdayMonth . '-' . $birthdayDate;
  1051. $user->birthday = $birthday;
  1052. $user->save();
  1053. CustomClass::updateByCondition(['userId' => $userId], ['birthday' => $birthday]);
  1054. }
  1055. }
  1056. $data['birthday'] = $birthday;
  1057. $data['birthdayTime'] = $birthdayTime;
  1058. $data['birthdayMonth'] = $birthdayMonth;
  1059. $data['birthdayDate'] = $birthdayDate;
  1060. $data['lunar'] = $lunar;
  1061. }
  1062. // 处理并发插入:尝试插入,如果遇到唯一键冲突则重新查询
  1063. try {
  1064. $custom = self::addCustom($data);
  1065. $isNewCustom = 1;
  1066. $customCreated = 1;
  1067. } catch (\yii\db\IntegrityException $e) {
  1068. // 唯一键冲突,说明其他并发请求已经插入,重新查询
  1069. if (strpos($e->getMessage(), 'Duplicate entry') !== false) {
  1070. $custom = self::getByCondition(['shopId' => $shopId, 'userId' => $userId], true);
  1071. if (empty($custom)) {
  1072. // 如果仍然查不到,则抛出异常
  1073. throw $e;
  1074. }
  1075. $isNewCustom = 0;
  1076. $customCreated = 0;
  1077. } else {
  1078. throw $e;
  1079. }
  1080. }
  1081. }
  1082. $customId = $custom->id ?? 0;
  1083. $hd = HdClass::getByCondition(['shopId' => $shopId, 'userId' => $userId], true);
  1084. if (empty($hd)) {
  1085. $hdData = [
  1086. 'shopId' => $shopId,
  1087. 'userId' => $userId,
  1088. 'name' => $hdName,
  1089. 'sjId' => $sjId,
  1090. 'mainId' => $mainId,
  1091. 'customId' => $customId,
  1092. 'avatar' => $hdAvatar,
  1093. 'py' => $hdPy,
  1094. 'mobile' => $hdMobile,
  1095. 'province' => $shop->province ?? '',
  1096. 'city' => $shop->city ?? '',
  1097. 'dist' => $shop->dist ?? '',
  1098. 'address' => $shop->address ?? '',
  1099. 'floor' => $shop->floor ?? '',
  1100. 'fullAddress' => $shop->fullAddress ?? '',
  1101. 'showAddress' => $shop->showAddress ?? '',
  1102. 'lat' => $shop->lat ?? '',
  1103. 'long' => $shop->long ?? '',
  1104. 'salt' => $salt,
  1105. ];
  1106. // 处理并发插入:尝试插入,如果遇到唯一键冲突则重新查询
  1107. try {
  1108. $hd = HdClass::addData($hdData);
  1109. } catch (\yii\db\IntegrityException $e) {
  1110. // 唯一键冲突,说明其他并发请求已经插入,重新查询
  1111. if (strpos($e->getMessage(), 'Duplicate entry') !== false) {
  1112. $hd = HdClass::getByCondition(['shopId' => $shopId, 'userId' => $userId], true);
  1113. if (empty($hd)) {
  1114. // 如果仍然查不到,则抛出异常
  1115. throw $e;
  1116. }
  1117. } else {
  1118. throw $e;
  1119. }
  1120. }
  1121. $isNewCustom = 1;
  1122. }
  1123. $hdId = $hd->id ?? 0;
  1124. $custom->hdId = $hdId;
  1125. $custom->save();
  1126. // 新建 xhCustom 时:customId 无分销记录则创建,有邀请人则绑定
  1127. $inviteBound = 0;
  1128. $inviterCustomId = (int)$inviterCustomId;
  1129. try {
  1130. $inviteBound = DistributionUserClass::bindInviteOnRegister(
  1131. (int)$shopId,
  1132. (int)$customId,
  1133. $inviterCustomId
  1134. );
  1135. } catch (\Throwable $e) {
  1136. // 分销初始化失败不影响建客主流程
  1137. }
  1138. if ($isNewCustom == 1) {
  1139. WxMessageClass::newHdCustomInform($shop, $custom);
  1140. }
  1141. // 新客/受邀新客红包:仅新建 xhCustom 时触发;受邀优先且不叠加发新客红包
  1142. if ($customCreated == 1) {
  1143. try {
  1144. $mainId = (int)($shop->mainId ?? 0);
  1145. $customArr = is_object($custom) ? $custom->attributes : $custom;
  1146. if ($inviteBound == 1) {
  1147. HbAutoRuleClass::tryIssueOnRegister($shopId, $mainId, $customArr, HbAutoRule::TYPE_INVITED);
  1148. } else {
  1149. HbAutoRuleClass::tryIssueOnRegister($shopId, $mainId, $customArr, HbAutoRule::TYPE_NEW);
  1150. }
  1151. } catch (\Throwable $e) {
  1152. Yii::error('注册自动发红包失败 customId=' . $customId . ' ' . $e->getMessage(), __METHOD__);
  1153. }
  1154. }
  1155. return [
  1156. 'custom' => $custom,
  1157. 'hd' => $hd,
  1158. 'isNewCustom' => $customCreated,
  1159. 'inviteBound' => $inviteBound,
  1160. ];
  1161. }
  1162. //获取客户信息
  1163. public static function getCustom($data)
  1164. {
  1165. $custom = self::getByCondition($data);
  1166. if (empty($custom)) {
  1167. $custom = self::add($data);
  1168. }
  1169. return $custom;
  1170. }
  1171. //获取客户信息 ssh 2021.2.1
  1172. public static function getCustomInfo($id)
  1173. {
  1174. $custom = self::getById($id);
  1175. if (empty($custom)) {
  1176. return $custom;
  1177. }
  1178. $list = self::groupBaseInfo([$custom]);
  1179. return current($list);
  1180. }
  1181. //获取客户信息 ssh 2021.3.19
  1182. public static function getCustomByIds($ids)
  1183. {
  1184. $list = self::getByIds($ids);
  1185. $list = self::groupBaseInfo($list);
  1186. return $list;
  1187. }
  1188. public static function valid($info, $shopId)
  1189. {
  1190. if (isset($info['shopId']) && $info['shopId'] == $shopId) {
  1191. return true;
  1192. }
  1193. util::fail('客户信息无效');
  1194. }
  1195. //整合基本信息 ssh 2021.1.31
  1196. public static function groupBaseInfo($list)
  1197. {
  1198. if (empty($list)) {
  1199. return $list;
  1200. }
  1201. foreach ($list as $key => $val) {
  1202. $shortAvatar = $val['avatar'] ?? '';
  1203. $avatar = imgUtil::groupImg($shortAvatar) . "?x-oss-process=image/resize,m_fill,h_800,w_800";
  1204. $smallAvatar = imgUtil::groupImg($shortAvatar) . "?x-oss-process=image/resize,m_fill,h_130,w_130";
  1205. $bigAvatar = imgUtil::groupImg($shortAvatar) . "?x-oss-process=image/resize,m_fill,h_700,w_700";
  1206. $list[$key]['shortAvatar'] = $shortAvatar;
  1207. $list[$key]['avatar'] = $avatar;
  1208. $list[$key]['smallAvatar'] = $smallAvatar;
  1209. $list[$key]['bigAvatar'] = $bigAvatar;
  1210. //超时未下单
  1211. $recentExpend = $val['recentExpend'] ?? '';
  1212. $list[$key]['overTimeUnExpend'] = 0;
  1213. if ($recentExpend == '0000-00-00 00:00:00') {
  1214. $list[$key]['overTimeUnExpend'] = 10000;
  1215. } else {
  1216. $recentExpendTime = strtotime($recentExpend);
  1217. $seven = bcmul(86400, 7);
  1218. $sevenTime = bcadd($recentExpendTime, $seven);
  1219. if ($sevenTime < time()) {
  1220. $list[$key]['overTimeUnExpend'] = 7;
  1221. }
  1222. $month = bcmul(86400, 30);
  1223. $monthTime = bcadd($recentExpendTime, $month);
  1224. if ($monthTime < time()) {
  1225. $list[$key]['overTimeUnExpend'] = 30;
  1226. }
  1227. $halfYear = bcmul(86400, 180);
  1228. $halfYearTime = bcadd($recentExpendTime, $halfYear);
  1229. if ($halfYearTime < time()) {
  1230. $list[$key]['overTimeUnExpend'] = 180;
  1231. }
  1232. $year = bcmul(86400, 365);
  1233. $yearTime = bcmul($recentExpendTime, $year);
  1234. if ($yearTime < time()) {
  1235. $list[$key]['overTimeUnExpend'] = 365;
  1236. }
  1237. }
  1238. }
  1239. return $list;
  1240. }
  1241. // 客户消费等级批量变更
  1242. public static function updateCustomExpenseLevel($data)
  1243. {
  1244. $shopId = $data['shopId'];
  1245. $mainId = $data['mainId'];
  1246. $customs = self::getAllByCondition(['shopId' => $shopId], null, 'id, name, hdId, userId, buyAmount, growth, member, memberName, discount', null, true);
  1247. $levelDatas = MemberLevelClass::getAllByCondition(['mainId' => $mainId], 'amount DESC', 'id, name, level, amount, discount');
  1248. if (!empty($customs) && !empty($levelDatas)) {
  1249. foreach ($customs as $c) {
  1250. // $buyAmount = 0;
  1251. // // 获取所有零售订单,计算消费金额
  1252. // $orderList = \bizHd\order\classes\OrderClass::getAllByCondition(['customId' => $c->id], null, 'id, actPrice');
  1253. // if (!empty($orderList)) {
  1254. // foreach ($orderList as $order) {
  1255. // $buyAmount = bcadd($buyAmount, $order['actPrice'], 2);
  1256. // }
  1257. // }
  1258. // $c->buyAmount = $buyAmount;
  1259. // 根据 growth 设置等级
  1260. $memberData = self::getCustomExpenseLevel($c->growth, $mainId, $levelDatas); // 设置等级多处需要同步修改的,请搜索:getCustomExpenseLevel
  1261. if ($c->member != $memberData['level'] || $c->memberName != $memberData['name']) {
  1262. $hdId = $c->hdId;
  1263. $hd = HdClass::getById($hdId, true);
  1264. $change = [
  1265. 'customId' => $c->id,
  1266. 'customName' => $c->name,
  1267. 'hdId' => $hd->id,
  1268. 'hdName' => $hd->name,
  1269. 'mainId' => $mainId,
  1270. 'shopId' => $shopId,
  1271. 'targetId' => 0,
  1272. 'rechargeAmount' => 0,
  1273. 'days' => 0,
  1274. 'daysBefore' => '0000-00-00 00:00:00',
  1275. 'daysAfter' => '0000-00-00 00:00:00',
  1276. 'beforeLevel' => $c->member,
  1277. 'afterLevel' => $memberData['level'],
  1278. 'beforeName' => $c->memberName,
  1279. 'afterName' => $memberData['name'],
  1280. 'style' => 1, //手动修改会员,脚本执行
  1281. 'event' => '系统脚本修改',
  1282. 'staffId' => 0,
  1283. 'staffName' => '',
  1284. ];
  1285. MemberChangeClass::add($change);
  1286. }
  1287. $c->member = $memberData['level'];
  1288. $c->memberName = $memberData['name'];
  1289. $c->discount = bcdiv(floatval($memberData['discount']), 10, 2);
  1290. $c->save();
  1291. if (!empty($c->hdId)) {
  1292. $hd = HdClass::getById($c->hdId, true);
  1293. if (!empty($hd)) {
  1294. $hd->member = $memberData['level'];
  1295. $hd->memberName = $memberData['name'];
  1296. $hd->discount = $c->discount;
  1297. $hd->save();
  1298. }
  1299. }
  1300. // 更新客户所对应花店的消费金额
  1301. // HdClass::updateByCondition(['shopId'=>$shopId, 'userId'=>$c->userId], ['expendAmount'=>$buyAmount]);
  1302. }
  1303. }
  1304. return true;
  1305. }
  1306. // 获取客户消费等级
  1307. public static function getCustomExpenseLevel($growth, $mainId, $levelDatas=[])
  1308. {
  1309. if (empty($levelDatas)) {
  1310. $levelDatas = MemberLevelClass::getAllByCondition(['mainId' => $mainId], 'amount DESC', 'id, name, level, amount, discount');
  1311. }
  1312. foreach ($levelDatas as $data) {
  1313. if ($growth >= $data['amount']) {
  1314. return $data;
  1315. }
  1316. }
  1317. return ['level'=>0, 'name'=>'', 'discount'=>10];
  1318. }
  1319. }