feat(订单中台): 后台新增「已删除」页签

- 常规页签与统计排除用户删除与后台删除(is_del=0 AND is_system_del=0)
- 新增已删除页签:只列用户已删且后台未删的订单,deleted_count 计数
  与列表体同口径(随大类/日期筛选联动)
- 修复统计缓存失效:cache delete 不支持通配,原 order_center:stat:*
  清理为静默无效,改版本号 key(statCacheKey/clearStatCache)
This commit is contained in:
吴昊天 2026-10-09 14:08:12 +08:00
parent 2a79dc7bb2
commit f2c2175443
3 changed files with 61 additions and 14 deletions

View File

@ -43,6 +43,7 @@ class OrderCenterController extends AuthController
'date_range' => $this->request->param('date_range', ''),
'uid' => (int) $this->request->param('uid', 0),
'after_sale' => $this->request->param('after_sale', ''),
'is_deleted' => (string) $this->request->param('is_deleted', ''),
];
$page = (int) $this->request->param('page', 1);
$limit = (int) $this->request->param('limit', 20);

View File

@ -220,10 +220,31 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
];
$result = $this->dao->saveOrUpdate(['order_no' => $data['order_no']], $insert);
app()->cache->delete('order_center:stat:*');
$this->clearStatCache();
return (bool) $result;
}
/**
* 订单中台统计缓存 key(带版本号:失效走版本递增,旧 key 靠自身 TTL 自然过期)
* cache 的 delete 不支持通配,此前 delete('order_center:stat:*') 为静默无效调用
* @param array $where 统计查询条件
* @return string 缓存 key
*/
protected function statCacheKey(array $where): string
{
$ver = (int) app()->cache->get('order_center:stat:ver');
return 'order_center:stat:v' . ($ver ?: 1) . ':' . md5(json_encode($where));
}
/**
* 清除订单中台统计缓存
*/
protected function clearStatCache(): void
{
app()->cache->set('order_center:stat:ver', (int) app()->cache->get('order_center:stat:ver') + 1, 86400);
}
/**
* 获取支付方式中文名称
* 从 eb_pay_channel 表动态读取,避免硬编码
@ -280,7 +301,7 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
$this->dao->update(['order_no' => $orderNo], [
'extra_info' => $this->encodeExtraInfo($extraArray),
]);
app()->cache->delete('order_center:stat:*');
$this->clearStatCache();
return true;
}
@ -294,7 +315,7 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
public function updateRecord(string $orderNo, array $data): bool
{
$result = $this->dao->update(['order_no' => $orderNo], $data) !== false;
app()->cache->delete('order_center:stat:*');
$this->clearStatCache();
return $result;
}
@ -2248,9 +2269,15 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
{
$searchWhere = [];
// 通用:是否后台删除筛选,默认不查已删除;用户删除(is_del)不影响后台可见性
$isDeleted = $where['is_deleted'] ?? false;
$searchWhere[] = ['is_system_del', '=', $isDeleted ? 1 : 0];
// 通用:is_deleted=1 时为「已删除」tab,只查用户已删且后台未删的订单;
// 常规 tab 用户删除与后台删除均不显示
if (!empty($where['is_deleted'])) {
$searchWhere[] = ['is_del', '=', 1];
$searchWhere[] = ['is_system_del', '=', 0];
} else {
$searchWhere[] = ['is_del', '=', 0];
$searchWhere[] = ['is_system_del', '=', 0];
}
// 订单大类筛选
if (!empty($where['order_category'])) {
@ -2503,11 +2530,11 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
*/
public function getStatistics(array $where): array
{
$cacheKey = 'order_center:stat:' . md5(json_encode($where));
$cacheKey = $this->statCacheKey($where);
$ttl = empty($where['date_range']) ? 300 : 60;
return app()->cache->remember($cacheKey, function () use ($where) {
$baseWhere = [['is_system_del', '=', 0]];
$baseWhere = [['is_del', '=', 0], ['is_system_del', '=', 0]];
// 按订单大类筛选
if (!empty($where['order_category'])) {
$baseWhere[] = ['order_category', '=', $where['order_category']];
@ -2563,6 +2590,18 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
->field('COUNT(*) as count, SUM(pay_price) as amount')
->find();
// 「已删除」tab 计数:用户删除(is_del=1)且后台未删(is_system_del=0),与列表体口径一致,
// 随大类/日期筛选联动
$deletedWhere = [['is_del', '=', 1], ['is_system_del', '=', 0]];
if (!empty($where['order_category'])) {
$deletedWhere[] = ['order_category', '=', $where['order_category']];
}
if (!empty($where['date_range'])) {
[$start, $end] = $this->parseDateRange($where['date_range']);
$deletedWhere[] = ['add_time', 'BETWEEN', [$start, $end]];
}
$deletedCount = $this->dao->getDb()->where($deletedWhere)->count();
// 补全所有状态码,缺值填0
$statusCounts = [];
foreach (OrderCenter::$statusMap as $code => $label) {
@ -2595,6 +2634,7 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
],
'status_counts' => $statusCounts,
'by_category' => $categoryResult,
'deleted_count' => $deletedCount,
];
}, $ttl);
}
@ -2627,7 +2667,7 @@ class OrderCenterServices extends BaseService implements OrderCenterSyncInterfac
throw new ApiException('订单未被用户删除,不能删除');
}
$res = $this->dao->update(['id' => $id], ['is_system_del' => 1]) !== false;
app()->cache->delete('order_center:stat:*');
$this->clearStatCache();
return $res;
}

View File

@ -149,6 +149,7 @@
<el-tab-pane :label="getTabLabel('已完成', 'completed')" name="completed"></el-tab-pane>
<el-tab-pane :label="getTabLabel('已取消', 'canceled')" name="canceled"></el-tab-pane>
<el-tab-pane :label="getTabLabel('售后中', 'after_sale')" name="after_sale"></el-tab-pane>
<el-tab-pane :label="getTabLabel('已删除', 'deleted')" name="deleted"></el-tab-pane>
</el-tabs>
<el-table :data="orderList" ref="table" v-loading="loading" empty-text="暂无数据" class="orderData">
<el-table-column type="selection" width="55"> </el-table-column>
@ -240,8 +241,10 @@
<a v-db-click @click="viewDetail(scope.row)">详情</a>
<el-divider direction="vertical" />
<a v-db-click @click="editRemark(scope.row)" v-auth="['admin-order-orderCenter-remark']">备注</a>
<el-divider direction="vertical" />
<a v-db-click @click="deleteOrder(scope.row)" v-auth="['admin-order-orderCenter-delete']">删除</a>
<template v-if="statusTab === 'deleted'">
<el-divider direction="vertical" />
<a v-db-click @click="deleteOrder(scope.row)" v-auth="['admin-order-orderCenter-delete']">删除</a>
</template>
</template>
</el-table-column>
</el-table>
@ -331,7 +334,9 @@ function getList(res) {
loading.value = true;
orderCenterList({
...searchForm.value,
status_code: statusTab.value,
// 「已删除」tab 只查用户删除(is_del=1)的订单,不按状态过滤
status_code: statusTab.value === 'deleted' ? '' : statusTab.value,
is_deleted: statusTab.value === 'deleted' ? 1 : '',
page: page.value.page,
limit: page.value.limit,
})
@ -359,6 +364,7 @@ function getStat() {
pending_count: d.status_counts?.pending_payment?.count || 0,
completed_count: d.status_counts?.completed?.count || 0,
status_counts: d.status_counts || {},
deleted_count: d.deleted_count || 0,
};
})
.catch((res) => {
@ -446,7 +452,7 @@ function saveRemark() {
function deleteOrder(row) {
ElMessageBox({
title: '提示',
message: '确定要删除该订单吗?',
message: '确定删除该订单吗?删除后后台列表将不再显示。',
showCancelButton: true,
cancelButtonText: '取消',
confirmButtonText: '确定',
@ -511,7 +517,7 @@ function getPayStatusClass(status) {
}
function getTabLabel(text, statusCode) {
const count = statData.value.status_counts?.[statusCode]?.count;
const count = statusCode === 'deleted' ? statData.value.deleted_count : statData.value.status_counts?.[statusCode]?.count;
return count ? `${text}(${count})` : text;
}