fix(orders): wire detail progress timeline and two-step cancel

Order progress was stuck on "订单创建" because timeline only read sparse
timestamp columns and status transitions didn't always stamp them.
Drive timeline from current status + related records, backfill lifecycle
timestamps on update_status, and replace window.confirm cancel with a
关闭订单 → 确认取消 button step in the detail modal.
This commit is contained in:
2026-07-27 15:15:35 +08:00
parent 64d0dc66da
commit ad40d9a7ca
4 changed files with 327 additions and 61 deletions
+34 -7
View File
@@ -204,21 +204,48 @@ trait OrderRepositoryCore {
$update_data = ['status' => $new_status];
$format = ['%s'];
// Stamp lifecycle timestamps. Some statuses share a column (e.g. expired → cancelled_at;
// pending_review still counts as a completion-like milestone for the timeline).
$time_fields = [
'paid' => 'paid_at',
'delivered' => 'delivered_at',
'completed' => 'completed_at',
'cancelled' => 'cancelled_at',
'refunded' => 'refunded_at',
\AKSRT\Core\DomainModel::ORDER_STATUS_PAID => 'paid_at',
\AKSRT\Core\DomainModel::ORDER_STATUS_DELIVERED => 'delivered_at',
\AKSRT\Core\DomainModel::ORDER_STATUS_COMPLETED => 'completed_at',
\AKSRT\Core\DomainModel::ORDER_STATUS_PENDING_REVIEW => 'completed_at',
\AKSRT\Core\DomainModel::ORDER_STATUS_CANCELLED => 'cancelled_at',
\AKSRT\Core\DomainModel::ORDER_STATUS_EXPIRED => 'cancelled_at',
\AKSRT\Core\DomainModel::ORDER_STATUS_REFUNDED => 'refunded_at',
];
foreach ($time_fields as $status_key => $field) {
if ($new_status === $status_key) {
if (isset($time_fields[$new_status])) {
$field = $time_fields[$new_status];
// Do not overwrite an existing timestamp (idempotent callbacks / force updates).
$existing = is_string($order[$field] ?? null) ? trim((string) $order[$field]) : '';
if ($existing === '' || str_starts_with($existing, '0000-00-00')) {
$update_data[$field] = current_time('mysql');
$format[] = '%s';
}
}
// When jumping past paid (force admin transitions), backfill paid_at so the
// progress chain is never stuck on "订单创建" alone.
if (
in_array($new_status, [
\AKSRT\Core\DomainModel::ORDER_STATUS_DELIVERED,
\AKSRT\Core\DomainModel::ORDER_STATUS_COMPLETED,
\AKSRT\Core\DomainModel::ORDER_STATUS_PENDING_REVIEW,
\AKSRT\Core\DomainModel::ORDER_STATUS_REFUNDING,
\AKSRT\Core\DomainModel::ORDER_STATUS_REFUNDED,
], true)
) {
$existing_paid = is_string($order['paid_at'] ?? null) ? trim((string) $order['paid_at']) : '';
if ($existing_paid === '' || str_starts_with($existing_paid, '0000-00-00')) {
if (!isset($update_data['paid_at'])) {
$update_data['paid_at'] = current_time('mysql');
$format[] = '%s';
}
}
}
if (!empty($extra['payment_method'])) {
$update_data['payment_method'] = sanitize_key($extra['payment_method']);
$format[] = '%s';