修复订单列表排序与活动时间刷新

This commit is contained in:
andy
2026-07-13 13:48:52 +08:00
parent 4cb0119e32
commit 3e94a146b9
7 changed files with 79 additions and 7 deletions

View File

@@ -22,7 +22,7 @@ public interface ReservationOrderMapper extends BaseMapper<ReservationOrderEntit
SELECT o.* SELECT o.*
FROM workflow_reservation_order o FROM workflow_reservation_order o
WHERE o.hotel_id = #{hotelId} WHERE o.hotel_id = #{hotelId}
AND o.order_visibility != #{hiddenSystemVisibility} AND o.order_visibility = #{visibleVisibility}
<if test="orderStatus != null and orderStatus != ''"> <if test="orderStatus != null and orderStatus != ''">
AND o.order_status = #{orderStatus} AND o.order_status = #{orderStatus}
</if> </if>
@@ -56,7 +56,7 @@ public interface ReservationOrderMapper extends BaseMapper<ReservationOrderEntit
) )
</if> </if>
ORDER BY ORDER BY
COALESCE(o.latest_activity_at, o.updated_at) DESC, o.latest_activity_at DESC,
o.updated_at DESC, o.updated_at DESC,
o.id DESC o.id DESC
</script> </script>
@@ -68,6 +68,6 @@ public interface ReservationOrderMapper extends BaseMapper<ReservationOrderEntit
@Param("groupCode") String groupCode, @Param("groupCode") String groupCode,
@Param("confirmationNumber") String confirmationNumber, @Param("confirmationNumber") String confirmationNumber,
@Param("keyword") String keyword, @Param("keyword") String keyword,
@Param("hiddenSystemVisibility") String hiddenSystemVisibility, @Param("visibleVisibility") String visibleVisibility,
@Param("sourceMessageIds") List<Long> sourceMessageIds); @Param("sourceMessageIds") List<Long> sourceMessageIds);
} }

View File

@@ -453,7 +453,7 @@ public class MybatisReservationAiWorkflowRepository implements ReservationAiWork
trim(request.groupCode()), trim(request.groupCode()),
trim(request.confirmationNumber()), trim(request.confirmationNumber()),
trim(request.keyword()), trim(request.keyword()),
ReservationOrderVisibility.HIDDEN_SYSTEM.name(), ReservationOrderVisibility.VISIBLE.name(),
sourceMessageIds); sourceMessageIds);
return new ReservationPageSnapshot<>( return new ReservationPageSnapshot<>(
page.getRecords().stream().map(this::toAiQueryOrderSnapshot).toList(), page.getRecords().stream().map(this::toAiQueryOrderSnapshot).toList(),
@@ -788,11 +788,12 @@ public class MybatisReservationAiWorkflowRepository implements ReservationAiWork
public boolean updateOperaOperationAfterAttempt( public boolean updateOperaOperationAfterAttempt(
String hotelId, String hotelId,
Long operationId, Long operationId,
Long orderId,
String operationStatus, String operationStatus,
Long lastAttemptId, Long lastAttemptId,
String lastErrorMessage, String lastErrorMessage,
LocalDateTime now) { LocalDateTime now) {
return operaOperationMapper.update(null, Wrappers.<ReservationOperaOperationEntity>lambdaUpdate() boolean updated = operaOperationMapper.update(null, Wrappers.<ReservationOperaOperationEntity>lambdaUpdate()
.set(ReservationOperaOperationEntity::getOperationStatus, operationStatus) .set(ReservationOperaOperationEntity::getOperationStatus, operationStatus)
.set(ReservationOperaOperationEntity::getLastAttemptId, lastAttemptId) .set(ReservationOperaOperationEntity::getLastAttemptId, lastAttemptId)
.set(ReservationOperaOperationEntity::getLastErrorMessage, lastErrorMessage) .set(ReservationOperaOperationEntity::getLastErrorMessage, lastErrorMessage)
@@ -800,6 +801,10 @@ public class MybatisReservationAiWorkflowRepository implements ReservationAiWork
.setSql("attempt_count = attempt_count + 1") .setSql("attempt_count = attempt_count + 1")
.eq(ReservationOperaOperationEntity::getHotelId, hotelId) .eq(ReservationOperaOperationEntity::getHotelId, hotelId)
.eq(ReservationOperaOperationEntity::getId, operationId)) > 0; .eq(ReservationOperaOperationEntity::getId, operationId)) > 0;
if (updated) {
touchOrderLatestActivity(hotelId, orderId, now);
}
return updated;
} }
/** /**

View File

@@ -248,6 +248,7 @@ public interface ReservationAiWorkflowRepository {
boolean updateOperaOperationAfterAttempt( boolean updateOperaOperationAfterAttempt(
String hotelId, String hotelId,
Long operationId, Long operationId,
Long orderId,
String operationStatus, String operationStatus,
Long lastAttemptId, Long lastAttemptId,
String lastErrorMessage, String lastErrorMessage,

View File

@@ -500,6 +500,7 @@ public class ReservationTaskWorkflowServiceImpl implements ReservationTaskWorkfl
workflowRepository.updateOperaOperationAfterAttempt( workflowRepository.updateOperaOperationAfterAttempt(
task.hotelId(), task.hotelId(),
operation.id(), operation.id(),
task.orderId(),
operationStatus, operationStatus,
attemptId, attemptId,
errorMessage, errorMessage,

View File

@@ -1,6 +1,6 @@
-- M002 前端订单列表排序优化:订单自身维护最近业务活动时间,避免列表查询每次聚合全量任务。 -- M002 前端订单列表排序优化:订单自身维护最近业务活动时间,避免列表查询每次聚合全量任务。
ALTER TABLE workflow_reservation_order ALTER TABLE workflow_reservation_order
ADD COLUMN latest_activity_at DATETIME(6) NULL COMMENT '订单最近业务活动 UTC 时间,用于订单列表最新活动排序'; ADD COLUMN latest_activity_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订单最近业务活动 UTC 时间,用于订单列表最新活动排序';
-- 历史数据回填:优先取订单更新时间和所属任务最新来源/创建时间中的较大值。 -- 历史数据回填:优先取订单更新时间和所属任务最新来源/创建时间中的较大值。
UPDATE workflow_reservation_order UPDATE workflow_reservation_order
@@ -19,4 +19,4 @@ SET latest_activity_at = GREATEST(
-- 订单列表按可见性和最新活动时间倒序分页。 -- 订单列表按可见性和最新活动时间倒序分页。
CREATE INDEX idx_reservation_order_visibility_activity CREATE INDEX idx_reservation_order_visibility_activity
ON workflow_reservation_order (hotel_id, order_visibility, latest_activity_at, id); ON workflow_reservation_order (hotel_id, order_visibility, latest_activity_at DESC, updated_at DESC, id DESC);

View File

@@ -248,6 +248,44 @@ class ReservationFrontendQueryControllerTest {
.andExpect(jsonPath("$.page.total").value(2)); .andExpect(jsonPath("$.page.total").value(2));
} }
@Test
void shouldKeepOrderLatestActivityAtRequiredForIndexedSorting() {
String isNullable = jdbcTemplate.queryForObject("""
SELECT IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'WORKFLOW_RESERVATION_ORDER'
AND COLUMN_NAME = 'LATEST_ACTIVITY_AT'
""", String.class);
assertThat(isNullable).isEqualTo("NO");
}
@Test
void shouldOnlyReturnVisibleOrdersInFrontendOrderList() throws Exception {
SourceMessageCaptureResult visibleSource = captureSourceMessage(
"mail-frontend-order-visible-filter-001",
"Frontend Query Visible Filter");
SourceMessageCaptureResult archivedSource = captureSourceMessage(
"mail-frontend-order-visible-filter-archived-001",
"Frontend Query Visible Filter Archived");
Long visibleOrderId = 930000000000002201L;
Long archivedOrderId = 930000000000002202L;
insertGroupOrder(visibleOrderId, visibleSource.inboxId(),
"GRP-FRONTEND-VISIBLE-FILTER-001", "ACTIVE");
insertGroupOrder(archivedOrderId, archivedSource.inboxId(),
"GRP-FRONTEND-VISIBLE-FILTER-ARCHIVED", "ACTIVE");
updateOrderVisibility(archivedOrderId, "ARCHIVED_SYSTEM");
mockMvc.perform(get("/api/reservation/orders")
.param("hotel_id", HOTEL_ID)
.param("keyword", "GRP-FRONTEND-VISIBLE-FILTER-")
.param("page_num", "1")
.param("page_size", "20"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.items[0].order_id").value(visibleOrderId.toString()))
.andExpect(jsonPath("$.page.total").value(1));
}
@Test @Test
void shouldTouchOrderLatestActivityWhenRepositoryInsertsTask() { void shouldTouchOrderLatestActivityWhenRepositoryInsertsTask() {
SourceMessageCaptureResult source = captureSourceMessage( SourceMessageCaptureResult source = captureSourceMessage(
@@ -457,6 +495,14 @@ class ReservationFrontendQueryControllerTest {
""", Timestamp.from(latestActivityAt), orderId); """, Timestamp.from(latestActivityAt), orderId);
} }
private void updateOrderVisibility(Long orderId, String orderVisibility) {
jdbcTemplate.update("""
UPDATE workflow_reservation_order
SET order_visibility = ?
WHERE id = ?
""", orderVisibility, orderId);
}
private void insertActiveGroupOrder(Long orderId, Long sourceMessageId, String groupCode) { private void insertActiveGroupOrder(Long orderId, Long sourceMessageId, String groupCode) {
insertGroupOrder(orderId, sourceMessageId, groupCode, "ACTIVE"); insertGroupOrder(orderId, sourceMessageId, groupCode, "ACTIVE");
} }

View File

@@ -19,6 +19,7 @@ import cn.nianxx.thhotel.platform.message.common.result.SourceMessageCaptureResu
import cn.nianxx.thhotel.platform.message.service.SourceMessageCaptureService; import cn.nianxx.thhotel.platform.message.service.SourceMessageCaptureService;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.sql.Timestamp;
import java.time.Instant; import java.time.Instant;
import java.util.HexFormat; import java.util.HexFormat;
import java.util.List; import java.util.List;
@@ -1968,6 +1969,18 @@ class SuperAgentTaskResultControllerTest {
"CNF-CP5-OPERA-RETRY-001"); "CNF-CP5-OPERA-RETRY-001");
String taskId = taskAndOperationIds[0]; String taskId = taskAndOperationIds[0];
String firstOperationId = taskAndOperationIds[1]; String firstOperationId = taskAndOperationIds[1];
Long orderId = jdbcTemplate.queryForObject("""
SELECT order_id
FROM workflow_reservation_task
WHERE id = ?
""", Long.class, Long.valueOf(taskId));
Timestamp staleLatestActivityAt = Timestamp.from(Instant.parse("2026-07-01T00:00:00Z"));
jdbcTemplate.update("""
UPDATE workflow_reservation_order
SET latest_activity_at = ?,
updated_at = ?
WHERE id = ?
""", staleLatestActivityAt, staleLatestActivityAt, orderId);
mockMvc.perform(post( mockMvc.perform(post(
"/api/reservation/tasks/{taskId}/opera-operations/{operationId}/execute", "/api/reservation/tasks/{taskId}/opera-operations/{operationId}/execute",
@@ -1984,6 +1997,12 @@ class SuperAgentTaskResultControllerTest {
.andExpect(jsonPath("$.operation_status").value("FAILED")) .andExpect(jsonPath("$.operation_status").value("FAILED"))
.andExpect(jsonPath("$.attempt_count").value(1)) .andExpect(jsonPath("$.attempt_count").value(1))
.andExpect(jsonPath("$.attempts[0].attempt_status").value("FAILED")); .andExpect(jsonPath("$.attempts[0].attempt_status").value("FAILED"));
Timestamp failedAttemptLatestActivityAt = jdbcTemplate.queryForObject("""
SELECT latest_activity_at
FROM workflow_reservation_order
WHERE id = ?
""", Timestamp.class, orderId);
assertThat(failedAttemptLatestActivityAt.toInstant()).isAfter(staleLatestActivityAt.toInstant());
mockMvc.perform(post( mockMvc.perform(post(
"/api/reservation/tasks/{taskId}/opera-operations/{operationId}/retry", "/api/reservation/tasks/{taskId}/opera-operations/{operationId}/retry",