|
|
@@ -93,6 +93,100 @@ class PsMethodClass extends BaseClass
|
|
|
return $config;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 客户 homeRule=1 时,用 xhCustom/xhHd 上的送货上门字段覆盖 style=0 配置
|
|
|
+ * @param array $config xhPsMethod 送货配置
|
|
|
+ * @param array $customInfo 客户信息(含 home、homeRule、homeAmount 等)
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function applyCustomHomeRule($config, $customInfo)
|
|
|
+ {
|
|
|
+ if (empty($config) || empty($customInfo)) {
|
|
|
+ return $config;
|
|
|
+ }
|
|
|
+ if (!isset($customInfo['homeRule']) || (int)$customInfo['homeRule'] !== 1) {
|
|
|
+ return $config;
|
|
|
+ }
|
|
|
+ $config['status'] = isset($customInfo['home']) ? (int)$customInfo['home'] : ($config['status'] ?? 1);
|
|
|
+ $config['minAmount'] = $customInfo['homeAmount'] ?? ($config['minAmount'] ?? 0);
|
|
|
+ $config['minNum'] = $customInfo['homeNum'] ?? ($config['minNum'] ?? 0);
|
|
|
+ $config['unMeet'] = $customInfo['homeUnMeet'] ?? ($config['unMeet'] ?? 0);
|
|
|
+ $config['unMeetFee'] = $customInfo['homeUnFee'] ?? ($config['unMeetFee'] ?? 0);
|
|
|
+ return $config;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 一次性获取商户全部配送方式配置(含 xhPsExplain / xhPsReduceRule)
|
|
|
+ * @param int $shopId 门店ID
|
|
|
+ * @param int $mainId 商户ID
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getAllConfigs($shopId, $mainId)
|
|
|
+ {
|
|
|
+ $configsMap = self::getAllByCondition(['mainId' => $mainId], 'sort asc', '*', 'style', true);
|
|
|
+
|
|
|
+ for ($style = 0; $style <= 4; $style++) {
|
|
|
+ if (!isset($configsMap[$style])) {
|
|
|
+ $defaultConfig = dict::getDict('shMethod', $style);
|
|
|
+ $initData = array_merge([
|
|
|
+ 'shopId' => $shopId,
|
|
|
+ 'mainId' => $mainId,
|
|
|
+ 'style' => $style,
|
|
|
+ ], $defaultConfig);
|
|
|
+
|
|
|
+ $configObj = self::add($initData, true);
|
|
|
+ $configsMap[$style] = $configObj;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $configsArray = [];
|
|
|
+ $methodIds = [];
|
|
|
+ foreach ($configsMap as $style => $configObj) {
|
|
|
+ $configArr = $configObj instanceof \yii\db\ActiveRecord ? $configObj->toArray() : $configObj;
|
|
|
+ $configsArray[$style] = $configArr;
|
|
|
+ $methodIds[] = (int)$configArr['id'];
|
|
|
+ }
|
|
|
+
|
|
|
+ $allExplains = [];
|
|
|
+ if (!empty($methodIds)) {
|
|
|
+ $allExplains = PsExplainClass::getAllByCondition(['methodId' => ['in', $methodIds]], 'sort ASC, id ASC');
|
|
|
+ }
|
|
|
+ $explainsMap = [];
|
|
|
+ foreach ($allExplains as $exp) {
|
|
|
+ $explainsMap[(int)$exp['methodId']][] = $exp;
|
|
|
+ }
|
|
|
+
|
|
|
+ $allReduceRules = [];
|
|
|
+ if (!empty($methodIds)) {
|
|
|
+ $allReduceRules = PsReduceRuleClass::getAllByCondition(['methodId' => ['in', $methodIds]], 'sort ASC, id ASC');
|
|
|
+ }
|
|
|
+ $reduceRulesMap = [];
|
|
|
+ foreach ($allReduceRules as $rule) {
|
|
|
+ $reduceRulesMap[(int)$rule['methodId']][] = $rule;
|
|
|
+ }
|
|
|
+
|
|
|
+ $result = [];
|
|
|
+ for ($style = 0; $style <= 4; $style++) {
|
|
|
+ $config = $configsArray[$style];
|
|
|
+ $methodId = (int)$config['id'];
|
|
|
+
|
|
|
+ $config['explains'] = $explainsMap[$methodId] ?? [];
|
|
|
+ $config['reduceRules'] = ($style == 2) ? ($reduceRulesMap[$methodId] ?? []) : [];
|
|
|
+
|
|
|
+ $result[] = $config;
|
|
|
+ }
|
|
|
+
|
|
|
+ usort($result, function ($a, $b) {
|
|
|
+ $sortCompare = (int)($a['sort'] ?? 0) <=> (int)($b['sort'] ?? 0);
|
|
|
+ if ($sortCompare !== 0) {
|
|
|
+ return $sortCompare;
|
|
|
+ }
|
|
|
+ return (int)($a['style'] ?? 0) <=> (int)($b['style'] ?? 0);
|
|
|
+ });
|
|
|
+
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 保存指定配送方式配置及关联说明项、满减规则
|
|
|
* @param int $shopId 门店ID
|
|
|
@@ -192,4 +286,223 @@ class PsMethodClass extends BaseClass
|
|
|
throw $e;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 合并客户送货上门单独规则(style=0 且 homeRule=1)
|
|
|
+ * @param array $config xhPsMethod 配置
|
|
|
+ * @param int $style 配送方式
|
|
|
+ * @param array $customInfo 客户+花店关系信息
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected static function mergeCustomHomeRule($config, $style, $customInfo)
|
|
|
+ {
|
|
|
+ if ((int)$style === 0 && !empty($customInfo)) {
|
|
|
+ return self::applyCustomHomeRule($config, $customInfo);
|
|
|
+ }
|
|
|
+ return $config;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断当前订单是否未达最低消费
|
|
|
+ * 规则:满 minAmount 或 minNum 任一达标即视为达标;两者均未达标才返回 true
|
|
|
+ */
|
|
|
+ public static function isBelowMinimum($itemTotalAmount, $bigNum, $config)
|
|
|
+ {
|
|
|
+ $minAmount = isset($config['minAmount']) ? (float)$config['minAmount'] : 0.00;
|
|
|
+ $minNum = isset($config['minNum']) ? (int)$config['minNum'] : 0;
|
|
|
+ if ($minAmount <= 0 && $minNum <= 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $amountOk = $minAmount <= 0 || (float)$itemTotalAmount >= $minAmount;
|
|
|
+ $numOk = $minNum <= 0 || (float)$bigNum >= $minNum;
|
|
|
+ return !($amountOk || $numOk);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 拼接最低消费门槛文案,如 200元或30扎
|
|
|
+ */
|
|
|
+ protected static function buildUnMeetConditionText($config)
|
|
|
+ {
|
|
|
+ $parts = [];
|
|
|
+ $minAmount = isset($config['minAmount']) ? (float)$config['minAmount'] : 0.00;
|
|
|
+ $minNum = isset($config['minNum']) ? (int)$config['minNum'] : 0;
|
|
|
+ if ($minAmount > 0) {
|
|
|
+ $parts[] = $minAmount . '元';
|
|
|
+ }
|
|
|
+ if ($minNum > 0) {
|
|
|
+ $parts[] = $minNum . '扎';
|
|
|
+ }
|
|
|
+ return implode('或', $parts);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断当天是否已收过包装类附加费(xhPsMethod unMeet=2)
|
|
|
+ */
|
|
|
+ public static function isPackPaidToday($customId, $sendType = 0)
|
|
|
+ {
|
|
|
+ if (empty($customId)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $current = date('Y_m_d');
|
|
|
+ $key = 'hd_ps_custom_today_has_get_pack_cost_' . (int)$sendType . '_' . $current . '_' . $customId;
|
|
|
+ $has = Yii::$app->redis->executeCommand('GET', [$key]);
|
|
|
+ return (!empty($has) && $has == 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断当天是否已收过运费类附加费(xhPsMethod unMeet=0)
|
|
|
+ */
|
|
|
+ public static function isSendCostPaidToday($customId, $sendType = 0)
|
|
|
+ {
|
|
|
+ if (empty($customId)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $current = date('Y_m_d');
|
|
|
+ $key = 'hd_ps_custom_today_has_get_send_cost_' . (int)$sendType . '_' . $current . '_' . $customId;
|
|
|
+ $has = Yii::$app->redis->executeCommand('GET', [$key]);
|
|
|
+ return (!empty($has) && $has == 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 支付成功后标记当天已收附加费,保证同一客户同配送方式每日只收一次
|
|
|
+ */
|
|
|
+ public static function markUnMeetFeePaid($customId, $sendType, $packingFee = 0, $unMeetSendCost = 0)
|
|
|
+ {
|
|
|
+ if (empty($customId)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $current = date('Y_m_d');
|
|
|
+ $style = (int)$sendType;
|
|
|
+ if ((float)$packingFee > 0) {
|
|
|
+ $packKey = 'hd_ps_custom_today_has_get_pack_cost_' . $style . '_' . $current . '_' . $customId;
|
|
|
+ Yii::$app->redis->executeCommand('SETEX', [$packKey, 86400, '1']);
|
|
|
+ }
|
|
|
+ if ((float)$unMeetSendCost > 0) {
|
|
|
+ $sendKey = 'hd_ps_custom_today_has_get_send_cost_' . $style . '_' . $current . '_' . $customId;
|
|
|
+ Yii::$app->redis->executeCommand('SETEX', [$sendKey, 86400, '1']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据订单信息标记当日附加费(支付成功回调/余额支付)
|
|
|
+ */
|
|
|
+ public static function markUnMeetFeePaidFromOrder($order)
|
|
|
+ {
|
|
|
+ if (empty($order)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $orderSn = is_object($order) ? ($order->orderSn ?? '') : ($order['orderSn'] ?? '');
|
|
|
+ $customId = (int)(is_object($order) ? ($order->customId ?? 0) : ($order['customId'] ?? 0));
|
|
|
+ $sendType = (int)(is_object($order) ? ($order->sendType ?? 0) : ($order['sendType'] ?? 0));
|
|
|
+ $packingFee = (float)(is_object($order) ? ($order->packingFee ?? 0) : ($order['packingFee'] ?? 0));
|
|
|
+ $unMeetSendCost = 0;
|
|
|
+ if (!empty($orderSn)) {
|
|
|
+ $unMeetSendKey = 'hd_ps_order_unmeet_send_' . $orderSn;
|
|
|
+ $cached = Yii::$app->redis->executeCommand('GET', [$unMeetSendKey]);
|
|
|
+ if (!empty($cached)) {
|
|
|
+ $unMeetSendCost = (float)$cached;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ self::markUnMeetFeePaid($customId, $sendType, $packingFee, $unMeetSendCost);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算未达最低消费时应写入订单的附加运费/包装费
|
|
|
+ * @return array ['sendCost' => 附加运费, 'packCost' => 附加包装费]
|
|
|
+ */
|
|
|
+ public static function calcFee($shopId, $mainId, $sendType, $itemTotalAmount, $bigNum, $customId = 0, $customInfo = [])
|
|
|
+ {
|
|
|
+ $costs = ['sendCost' => 0, 'packCost' => 0];
|
|
|
+ $style = (int)$sendType;
|
|
|
+ if ($style < 0 || $style > 4) {
|
|
|
+ return $costs;
|
|
|
+ }
|
|
|
+
|
|
|
+ $config = self::getConfig($shopId, $mainId, $style);
|
|
|
+ if (empty($config)) {
|
|
|
+ return $costs;
|
|
|
+ }
|
|
|
+ $config = self::mergeCustomHomeRule($config, $style, $customInfo);
|
|
|
+
|
|
|
+ if (!self::isBelowMinimum($itemTotalAmount, $bigNum, $config)) {
|
|
|
+ return $costs;
|
|
|
+ }
|
|
|
+
|
|
|
+ $unMeet = isset($config['unMeet']) ? (int)$config['unMeet'] : 0;
|
|
|
+ $unMeetFee = isset($config['unMeetFee']) ? (float)$config['unMeetFee'] : 0;
|
|
|
+ if ($unMeetFee <= 0) {
|
|
|
+ return $costs;
|
|
|
+ }
|
|
|
+
|
|
|
+ // unMeet: 0 加收运费,2 加收包装费
|
|
|
+ if ($unMeet === 0 && !self::isSendCostPaidToday($customId, $style)) {
|
|
|
+ $costs['sendCost'] = $unMeetFee;
|
|
|
+ } elseif ($unMeet === 2 && !self::isPackPaidToday($customId, $style)) {
|
|
|
+ $costs['packCost'] = $unMeetFee;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $costs;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 下单前校验配送方式最低消费与附加费是否已正确写入
|
|
|
+ * @param float $packCost 附加包装费
|
|
|
+ * @param float $unMeetSendCost 未达门槛附加运费(不含跑腿报价)
|
|
|
+ * @return string 空字符串表示通过
|
|
|
+ */
|
|
|
+ public static function checkLimit($shopId, $mainId, $sendType, $actPrice, $bigNum, $packCost = 0, $unMeetSendCost = 0, $customId = 0, $customInfo = [])
|
|
|
+ {
|
|
|
+ $style = (int)$sendType;
|
|
|
+ if ($style < 0 || $style > 4) {
|
|
|
+ return '配送方式无效';
|
|
|
+ }
|
|
|
+
|
|
|
+ $config = self::getConfig($shopId, $mainId, $style);
|
|
|
+ if (empty($config)) {
|
|
|
+ return '配送方式配置异常';
|
|
|
+ }
|
|
|
+ $config = self::mergeCustomHomeRule($config, $style, $customInfo);
|
|
|
+
|
|
|
+ $methodName = !empty($config['name']) ? $config['name'] : '该配送方式';
|
|
|
+ if (isset($config['status']) && (int)$config['status'] === 0) {
|
|
|
+ return "暂不支持{$methodName},请选其它配送方式";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!self::isBelowMinimum($actPrice, $bigNum, $config)) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ $minAmount = isset($config['minAmount']) ? (float)$config['minAmount'] : 0.00;
|
|
|
+ $minNum = isset($config['minNum']) ? (int)$config['minNum'] : 0;
|
|
|
+ $unMeet = isset($config['unMeet']) ? (int)$config['unMeet'] : 0;
|
|
|
+ $unMeetFee = isset($config['unMeetFee']) ? (float)$config['unMeetFee'] : 0;
|
|
|
+
|
|
|
+ if ($unMeet === 1) {
|
|
|
+ $conditionText = self::buildUnMeetConditionText($config);
|
|
|
+ if ($conditionText !== '') {
|
|
|
+ return "订单不满{$conditionText},不能下单";
|
|
|
+ }
|
|
|
+ return '未达到最低消费,不能下单';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (($unMeet === 0 || $unMeet === 2) && $unMeetFee > 0) {
|
|
|
+ $feeToCheck = $unMeet === 2 ? $packCost : $unMeetSendCost;
|
|
|
+ if ($unMeet === 2 && self::isPackPaidToday($customId, $style)) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ if ($unMeet === 0 && self::isSendCostPaidToday($customId, $style)) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ if (bccomp((string)$feeToCheck, (string)$unMeetFee, 2) < 0) {
|
|
|
+ $feeLabel = $unMeet === 2 ? '包装费' : '运费';
|
|
|
+ $conditionText = self::buildUnMeetConditionText($config);
|
|
|
+ if ($conditionText !== '') {
|
|
|
+ return "订单不满{$conditionText},加{$feeLabel}{$unMeetFee}元";
|
|
|
+ }
|
|
|
+ return "未达最低消费,需加收{$feeLabel}{$unMeetFee}元";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return '';
|
|
|
+ }
|
|
|
}
|