|
|
@@ -15,6 +15,7 @@ use bizHd\order\classes\OrderGoodsClass;
|
|
|
use bizHd\order\classes\OrderItemClass;
|
|
|
use bizHd\refund\services\HdRefundService;
|
|
|
use bizHd\shop\classes\ShopClass;
|
|
|
+use biz\shop\classes\MainClass;
|
|
|
use biz\shop\classes\ShopClass as SharedShopClass;
|
|
|
use common\components\imgUtil;
|
|
|
use common\components\orderSn;
|
|
|
@@ -66,8 +67,17 @@ class HdRefundMallClass
|
|
|
util::fail('退款金额超过可退金额');
|
|
|
}
|
|
|
|
|
|
+ // 申请时写入原单支付快照,待审核详情/列表即可展示退回方式;通过时 addRefund 会再按原单刷新
|
|
|
+ $snap = HdNextDayRefundClass::buildRefundPaySnapshot($order, true);
|
|
|
+ // 预判当天/封账退款口径,便于待审核展示;最终以审核通过时 mustUseNextDay 为准
|
|
|
+ $mainId = (int)($extra['mainId'] ?? $order->mainId ?? 0);
|
|
|
+ $main = $mainId > 0 ? MainClass::getById($mainId, true) : null;
|
|
|
+ $sameDay = HdNextDayRefundClass::mustUseNextDay($order, $main)
|
|
|
+ ? HdRefundClass::SAME_DAY_NO
|
|
|
+ : HdRefundClass::SAME_DAY_YES;
|
|
|
+
|
|
|
$data = [
|
|
|
- 'mainId' => (int)($extra['mainId'] ?? $order->mainId ?? 0),
|
|
|
+ 'mainId' => $mainId,
|
|
|
'shopId' => (int)($extra['shopId'] ?? $order->shopId ?? 0),
|
|
|
'sjId' => (int)($extra['sjId'] ?? $order->sjId ?? 0),
|
|
|
'hdId' => (int)($extra['hdId'] ?? $order->hdId ?? 0),
|
|
|
@@ -79,6 +89,8 @@ class HdRefundMallClass
|
|
|
'refundSn' => orderSn::getHdRefundSn(),
|
|
|
'refundType' => $refundType,
|
|
|
'product' => !empty($productList) ? json_encode($productList, JSON_UNESCAPED_UNICODE) : '',
|
|
|
+ // 顾客上传售后图短路径 JSON,审核页展开为 smallImgList
|
|
|
+ 'imgList' => self::normalizeImgListJson($post['imgList'] ?? ''),
|
|
|
'refundPrice' => round((float)$refundPrice, 2),
|
|
|
'remark' => trim((string)($post['remark'] ?? '')),
|
|
|
'status' => HdRefundClass::STATUS_PENDING,
|
|
|
@@ -86,9 +98,12 @@ class HdRefundMallClass
|
|
|
'shopAdminName' => '',
|
|
|
'passTime' => null,
|
|
|
'rejectReason' => '',
|
|
|
- 'hasReturn' => 0,
|
|
|
- 'returnBalance' => 0,
|
|
|
- 'couldReturn' => 1,
|
|
|
+ 'sameDay' => $sameDay,
|
|
|
+ 'onlinePay' => $snap['onlinePay'],
|
|
|
+ 'payWay' => $snap['payWay'],
|
|
|
+ 'hasReturn' => $snap['hasReturn'],
|
|
|
+ 'returnBalance' => $snap['returnBalance'],
|
|
|
+ 'couldReturn' => $snap['couldReturn'],
|
|
|
];
|
|
|
|
|
|
// 撤销后重新申请:复用已取消记录,避免同一订单堆多条取消单
|
|
|
@@ -101,6 +116,85 @@ class HdRefundMallClass
|
|
|
return HdRefundClass::add($data, true);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 规范化售后图片字段:只保留短路径字符串列表再 JSON 编码
|
|
|
+ * @param string|array $imgList 前端 JSON 或数组
|
|
|
+ * @return string 空串或 JSON 数组字符串
|
|
|
+ */
|
|
|
+ public static function normalizeImgListJson($imgList)
|
|
|
+ {
|
|
|
+ $arr = [];
|
|
|
+ if (is_string($imgList)) {
|
|
|
+ $imgList = trim($imgList);
|
|
|
+ if ($imgList === '') {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ $decoded = json_decode($imgList, true);
|
|
|
+ $arr = is_array($decoded) ? $decoded : [];
|
|
|
+ } elseif (is_array($imgList)) {
|
|
|
+ $arr = $imgList;
|
|
|
+ }
|
|
|
+ $paths = [];
|
|
|
+ foreach ($arr as $item) {
|
|
|
+ if (!is_string($item)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $path = trim($item);
|
|
|
+ // 若误传完整 URL,尽量截成短路径
|
|
|
+ if (strpos($path, 'http://') === 0 || strpos($path, 'https://') === 0) {
|
|
|
+ $parts = parse_url($path);
|
|
|
+ $path = ltrim((string)($parts['path'] ?? ''), '/');
|
|
|
+ }
|
|
|
+ if ($path === '') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $paths[] = $path;
|
|
|
+ if (count($paths) >= 6) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (empty($paths)) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ return json_encode($paths, JSON_UNESCAPED_UNICODE);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 给售后行挂上可预览的缩略图/大图列表(对齐 ghs refundDetail)
|
|
|
+ * @param array $row
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function attachImgDisplayFields($row)
|
|
|
+ {
|
|
|
+ if (!is_array($row)) {
|
|
|
+ return $row;
|
|
|
+ }
|
|
|
+ $imgString = $row['imgList'] ?? '';
|
|
|
+ $imgArr = [];
|
|
|
+ if (is_string($imgString) && $imgString !== '') {
|
|
|
+ $decoded = json_decode($imgString, true);
|
|
|
+ if (is_array($decoded)) {
|
|
|
+ $imgArr = $decoded;
|
|
|
+ }
|
|
|
+ } elseif (is_array($imgString)) {
|
|
|
+ $imgArr = $imgString;
|
|
|
+ }
|
|
|
+ $smallImgList = [];
|
|
|
+ $bigImgList = [];
|
|
|
+ foreach ($imgArr as $image) {
|
|
|
+ if (!is_string($image) || trim($image) === '') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $short = trim($image);
|
|
|
+ $smallImgList[] = imgUtil::groupImg($short) . '?x-oss-process=image/resize,m_fill,h_130,w_130';
|
|
|
+ $bigImgList[] = imgUtil::groupImg($short) . '?x-oss-process=image/resize,m_fill,h_700,w_700';
|
|
|
+ }
|
|
|
+ $row['imgList'] = $imgArr;
|
|
|
+ $row['smallImgList'] = $smallImgList;
|
|
|
+ $row['bigImgList'] = $bigImgList;
|
|
|
+ return $row;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 将已取消申请改回待审核
|
|
|
* 顾客撤销后再次提交时复用同一条记录,刷新申请内容并清掉审核痕迹
|
|
|
@@ -352,16 +446,21 @@ class HdRefundMallClass
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 剩余可退现金:实付合计 orderPrice 减已退 tkPrice
|
|
|
+ * 剩余可退现金:实付合计 orderPrice 减当天已退 tkPrice 与封账退款 nextDayTkPrice
|
|
|
* 红包抵扣已计入 orderPrice,所以红包全额单这里为 0
|
|
|
* @param array|object $order
|
|
|
* @return string 两位小数字符串,不会为负
|
|
|
*/
|
|
|
public static function remainRefundCash($order)
|
|
|
{
|
|
|
+ $already = bcadd(
|
|
|
+ self::toMoney(self::orderField($order, 'tkPrice', 0)),
|
|
|
+ self::toMoney(self::orderField($order, 'nextDayTkPrice', 0)),
|
|
|
+ 2
|
|
|
+ );
|
|
|
$could = bcsub(
|
|
|
self::toMoney(self::orderField($order, 'orderPrice', 0)),
|
|
|
- self::toMoney(self::orderField($order, 'tkPrice', 0)),
|
|
|
+ $already,
|
|
|
2
|
|
|
);
|
|
|
if (bccomp($could, '0', 2) < 0) {
|
|
|
@@ -477,24 +576,28 @@ class HdRefundMallClass
|
|
|
// 复用待审核行写明细/回库/资金,避免再插一条
|
|
|
$refund = HdRefundService::addRefund($post, $order, $refund);
|
|
|
|
|
|
- $custom = CustomClass::getLockById($order->customId);
|
|
|
- if (!empty($custom)) {
|
|
|
- $custom->buyAmount = bcsub((string)$custom->buyAmount, (string)$refund->refundPrice, 2);
|
|
|
- $customName = $custom->name ?? $order->customName ?? '';
|
|
|
- $orderSnText = $order->orderSn ?? '';
|
|
|
- $eventPrefix = $customName !== '' ? $customName : '客户';
|
|
|
- $custom->eventRemark = $eventPrefix . '商城售后' . $orderSnText;
|
|
|
- $custom->relateId = $order->id;
|
|
|
- $custom->relateType = 1;
|
|
|
- $custom->staffId = (int)($shopAdmin['id'] ?? 0);
|
|
|
- $custom->staffName = $shopAdmin['name'] ?? '';
|
|
|
- $custom->mainId = (int)($shopAdmin['mainId'] ?? $refund->mainId);
|
|
|
- $custom->save();
|
|
|
+ // 当天售后才在此回退累计消费;封账/隔天已在 applyHdAmountAndFund 扣过,避免重复
|
|
|
+ $sameDay = intval($refund->sameDay ?? HdRefundClass::SAME_DAY_YES);
|
|
|
+ if ($sameDay === HdRefundClass::SAME_DAY_YES) {
|
|
|
+ $custom = CustomClass::getLockById($order->customId);
|
|
|
+ if (!empty($custom)) {
|
|
|
+ $custom->buyAmount = bcsub((string)$custom->buyAmount, (string)$refund->refundPrice, 2);
|
|
|
+ $customName = $custom->name ?? $order->customName ?? '';
|
|
|
+ $orderSnText = $order->orderSn ?? '';
|
|
|
+ $eventPrefix = $customName !== '' ? $customName : '客户';
|
|
|
+ $custom->eventRemark = $eventPrefix . '商城售后' . $orderSnText;
|
|
|
+ $custom->relateId = $order->id;
|
|
|
+ $custom->relateType = 1;
|
|
|
+ $custom->staffId = (int)($shopAdmin['id'] ?? 0);
|
|
|
+ $custom->staffName = $shopAdmin['name'] ?? '';
|
|
|
+ $custom->mainId = (int)($shopAdmin['mainId'] ?? $refund->mainId);
|
|
|
+ $custom->save();
|
|
|
|
|
|
- $hd = HdClass::getLockById($custom->hdId);
|
|
|
- if (!empty($hd)) {
|
|
|
- $hd->expendAmount = bcsub((string)$hd->expendAmount, (string)$refund->refundPrice, 2);
|
|
|
- $hd->save();
|
|
|
+ $hd = HdClass::getLockById($custom->hdId);
|
|
|
+ if (!empty($hd)) {
|
|
|
+ $hd->expendAmount = bcsub((string)$hd->expendAmount, (string)$refund->refundPrice, 2);
|
|
|
+ $hd->save();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -548,15 +651,21 @@ class HdRefundMallClass
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 顾客端售后列表(mallApp 退款售后页,跨店)
|
|
|
+ * 顾客端售后列表(mallApp 退款售后页,跨店;可按订单收窄)
|
|
|
* @param int $userId 当前登录商城用户
|
|
|
* @param string $tab all|pending|0|refunded|1|closed|2|3
|
|
|
+ * @param int $orderId 可选,>0 时只返回该订单的顾客申请(订单详情「申请记录」)
|
|
|
* @return array 分页 list + pendingCount
|
|
|
*/
|
|
|
- public static function getCustomApplyList($userId, $tab = 'all')
|
|
|
+ public static function getCustomApplyList($userId, $tab = 'all', $orderId = 0)
|
|
|
{
|
|
|
$userId = (int)$userId;
|
|
|
+ $orderId = (int)$orderId;
|
|
|
$where = ['userId' => $userId];
|
|
|
+ // 订单详情进入时只看本单,避免混入其它订单售后
|
|
|
+ if ($orderId > 0) {
|
|
|
+ $where['orderId'] = $orderId;
|
|
|
+ }
|
|
|
$tab = (string)$tab;
|
|
|
if ($tab === 'pending' || $tab === '0') {
|
|
|
$where['status'] = HdRefundClass::STATUS_PENDING;
|
|
|
@@ -569,10 +678,14 @@ class HdRefundMallClass
|
|
|
}
|
|
|
|
|
|
$data = HdRefundClass::getList('*', $where, 'addTime DESC');
|
|
|
- $data['pendingCount'] = (int)HdRefundClass::getCount([
|
|
|
+ $pendingWhere = [
|
|
|
'userId' => $userId,
|
|
|
'status' => HdRefundClass::STATUS_PENDING,
|
|
|
- ]);
|
|
|
+ ];
|
|
|
+ if ($orderId > 0) {
|
|
|
+ $pendingWhere['orderId'] = $orderId;
|
|
|
+ }
|
|
|
+ $data['pendingCount'] = (int)HdRefundClass::getCount($pendingWhere);
|
|
|
if (!empty($data['list'])) {
|
|
|
$data['list'] = self::formatCustomList($data['list']);
|
|
|
} else {
|
|
|
@@ -589,7 +702,9 @@ class HdRefundMallClass
|
|
|
public static function formatCustomDetail($row)
|
|
|
{
|
|
|
$list = self::formatCustomList([$row]);
|
|
|
- return $list[0] ?? $row;
|
|
|
+ $detail = $list[0] ?? $row;
|
|
|
+ // 详情再补图片预览字段,列表可不带大图
|
|
|
+ return self::attachImgDisplayFields($detail);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -610,11 +725,13 @@ class HdRefundMallClass
|
|
|
|
|
|
/**
|
|
|
* 列表卡片主状态文案,对齐设计稿「退款中·待商家处理 / 退款成功」
|
|
|
+ * 已返充时用「退款成功·已返充」,列表右上角也能看清
|
|
|
* @param int $status
|
|
|
* @param int $refundType 1退货退款 2仅退款
|
|
|
+ * @param array $row 可选,用于通过后按资金态细化标题
|
|
|
* @return string
|
|
|
*/
|
|
|
- public static function displayStatusTitle($status, $refundType)
|
|
|
+ public static function displayStatusTitle($status, $refundType, $row = [])
|
|
|
{
|
|
|
$status = (int)$status;
|
|
|
if ($status === HdRefundClass::STATUS_PENDING) {
|
|
|
@@ -623,6 +740,15 @@ class HdRefundMallClass
|
|
|
: '退款中·待商家处理';
|
|
|
}
|
|
|
if ($status === HdRefundClass::STATUS_PASS) {
|
|
|
+ if ((int)($row['returnBalance'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return '退款成功·已返充';
|
|
|
+ }
|
|
|
+ if ((int)($row['hasReturn'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return '退款成功';
|
|
|
+ }
|
|
|
+ if ((int)($row['couldReturn'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return '退款成功·待退还';
|
|
|
+ }
|
|
|
return '退款成功';
|
|
|
}
|
|
|
if ($status === HdRefundClass::STATUS_REJECT) {
|
|
|
@@ -635,7 +761,31 @@ class HdRefundMallClass
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 卡片底部提示:处理中给审核时效,通过后带审核时间
|
|
|
+ * 资金落地文案:返充 / 原路 / 待商家退还(供列表 tip 与详情「退回方式」)
|
|
|
+ * @param array $row
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public static function fundStatusText($row)
|
|
|
+ {
|
|
|
+ $status = (int)($row['status'] ?? 0);
|
|
|
+ if ($status !== HdRefundClass::STATUS_PASS) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ if ((int)($row['returnBalance'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return '已返充到余额';
|
|
|
+ }
|
|
|
+ if ((int)($row['hasReturn'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return '已原路退回';
|
|
|
+ }
|
|
|
+ // 审核通过但线下等尚未选手选落地
|
|
|
+ if ((int)($row['couldReturn'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return '等待商家退还';
|
|
|
+ }
|
|
|
+ return '退款处理中';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 卡片底部提示:处理中给审核时效;通过后按返充/原路/待退还区分,避免一律写「原路退回」
|
|
|
* @param array $row
|
|
|
* @return string
|
|
|
*/
|
|
|
@@ -646,11 +796,23 @@ class HdRefundMallClass
|
|
|
return '商家审核中·预计 24 小时内处理';
|
|
|
}
|
|
|
if ($status === HdRefundClass::STATUS_PASS) {
|
|
|
+ $fundText = self::fundStatusText($row);
|
|
|
$time = trim((string)($row['passTime'] ?? ''));
|
|
|
- if ($time !== '' && $time !== '0000-00-00 00:00:00') {
|
|
|
- return '退款已到账·原路退回 ' . $time;
|
|
|
+ $timeOk = $time !== '' && $time !== '0000-00-00 00:00:00';
|
|
|
+ if ((int)($row['returnBalance'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return $timeOk
|
|
|
+ ? ('退款已到账·已返充到余额 ' . $time)
|
|
|
+ : '退款已到账·已返充到余额';
|
|
|
+ }
|
|
|
+ if ((int)($row['hasReturn'] ?? 0) === HdNextDayRefundClass::FLAG_YES) {
|
|
|
+ return $timeOk
|
|
|
+ ? ('退款已到账·原路退回 ' . $time)
|
|
|
+ : '退款已到账·原路退回';
|
|
|
+ }
|
|
|
+ if ($fundText !== '') {
|
|
|
+ return $timeOk ? ($fundText . ' ' . $time) : $fundText;
|
|
|
}
|
|
|
- return '退款已到账·原路退回';
|
|
|
+ return '退款已通过';
|
|
|
}
|
|
|
if ($status === HdRefundClass::STATUS_REJECT) {
|
|
|
$reason = trim((string)($row['rejectReason'] ?? ''));
|
|
|
@@ -810,13 +972,21 @@ class HdRefundMallClass
|
|
|
$row['hdName'] = $displayName !== '' ? $displayName : '花店';
|
|
|
$row['hdAvatar'] = self::coverUrl($shop['avatar'] ?? '');
|
|
|
$row['statusText'] = self::statusText($status);
|
|
|
- $row['statusTitle'] = self::displayStatusTitle($status, $refundType);
|
|
|
+ $row['statusTitle'] = self::displayStatusTitle($status, $refundType, $row);
|
|
|
$row['statusTip'] = self::displayStatusTip($row);
|
|
|
+ $row['fundStatusText'] = self::fundStatusText($row);
|
|
|
$row['statusTone'] = self::statusTone($status);
|
|
|
$row['refundTypeText'] = self::refundTypeText($refundType);
|
|
|
$row['reasonText'] = self::reasonText($row);
|
|
|
$row['canModify'] = $status === HdRefundClass::STATUS_PENDING ? 1 : 0;
|
|
|
$row['refundPrice'] = round((float)($row['refundPrice'] ?? 0), 2);
|
|
|
+ // 详情页「审核时间」读 auditTime;库字段是 passTime
|
|
|
+ $passTime = trim((string)($row['passTime'] ?? ''));
|
|
|
+ $row['auditTime'] = ($passTime !== '' && $passTime !== '0000-00-00 00:00:00') ? $passTime : '';
|
|
|
+ // 资金三态显式转 int,前端判断返充/原路更稳
|
|
|
+ $row['returnBalance'] = (int)($row['returnBalance'] ?? 0);
|
|
|
+ $row['hasReturn'] = (int)($row['hasReturn'] ?? 0);
|
|
|
+ $row['couldReturn'] = (int)($row['couldReturn'] ?? 0);
|
|
|
return $row;
|
|
|
}
|
|
|
|