Merge branch 'feature/nongxinweixinnotice' into 'master'
Feature/nongxinweixinnotice See merge request !7
Showing
7 changed files
with
840 additions
and
1 deletions
| 1 | +package com.tianting.infoloop.mapper; | ||
| 2 | + | ||
| 3 | +import com.tianting.infoloop.inject.MyBaseMapper; | ||
| 4 | +import com.tianting.infoloop.model.db.OrderSubscriptionMessageDb; | ||
| 5 | +import org.apache.ibatis.annotations.Delete; | ||
| 6 | +import org.apache.ibatis.annotations.Param; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +public interface OrderSubscriptionMessageMapper extends MyBaseMapper<OrderSubscriptionMessageDb> { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * 批量物理删除(绕过逻辑删除) | ||
| 14 | + */ | ||
| 15 | + @Delete("<script>" + | ||
| 16 | + "DELETE FROM order_subscription_message " + | ||
| 17 | + "WHERE enterpriseId = #{enterpriseId} " + | ||
| 18 | + "AND id IN " + | ||
| 19 | + "<foreach collection='ids' item='id' open='(' separator=',' close=')'>" + | ||
| 20 | + "#{id}" + | ||
| 21 | + "</foreach>" + | ||
| 22 | + "</script>") | ||
| 23 | + int physicalDeleteByIds(@Param("ids") List<Long> ids, @Param("enterpriseId") int enterpriseId); | ||
| 24 | +} | ||
| 25 | + |
| 1 | +package com.tianting.infoloop.model.db; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.FieldFill; | ||
| 4 | +import com.baomidou.mybatisplus.annotation.IdType; | ||
| 5 | +import com.baomidou.mybatisplus.annotation.TableField; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.TableLogic; | ||
| 8 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 9 | +import com.baomidou.mybatisplus.extension.activerecord.Model; | ||
| 10 | +import lombok.Data; | ||
| 11 | +import lombok.EqualsAndHashCode; | ||
| 12 | +import lombok.experimental.Accessors; | ||
| 13 | + | ||
| 14 | +import java.time.LocalDateTime; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * order subscription message DB | ||
| 18 | + * @TableName order_subscription_message | ||
| 19 | + */ | ||
| 20 | +@Data | ||
| 21 | +@Accessors(chain = true) | ||
| 22 | +@EqualsAndHashCode(callSuper = true) | ||
| 23 | +@TableName(value = "meal_order_subscription_messages") | ||
| 24 | +public class OrderSubscriptionMessageDb extends Model<OrderSubscriptionMessageDb> { | ||
| 25 | + /** | ||
| 26 | + * 主键ID | ||
| 27 | + */ | ||
| 28 | + @TableId(type = IdType.AUTO) | ||
| 29 | + private Long id; | ||
| 30 | + /** | ||
| 31 | + * 企业ID | ||
| 32 | + */ | ||
| 33 | + private Integer enterpriseId; | ||
| 34 | + /** | ||
| 35 | + * 订餐用户ID(dinerId) | ||
| 36 | + */ | ||
| 37 | + private Integer dinerId; | ||
| 38 | + /** | ||
| 39 | + * 微信用户OpenID | ||
| 40 | + */ | ||
| 41 | + private String openId; | ||
| 42 | + /** | ||
| 43 | + * 订阅消息模板ID | ||
| 44 | + */ | ||
| 45 | + private String templateId; | ||
| 46 | + /** | ||
| 47 | + * 跳转路径(小程序 path) | ||
| 48 | + */ | ||
| 49 | + private String jumpPath; | ||
| 50 | + /** | ||
| 51 | + * 关联的订餐周期开始时间(精确到秒) | ||
| 52 | + */ | ||
| 53 | + private LocalDateTime orderPeriodStartDate; | ||
| 54 | + /** | ||
| 55 | + * 关联的订餐周期结束时间(精确到秒) | ||
| 56 | + */ | ||
| 57 | + private LocalDateTime orderPeriodEndDate; | ||
| 58 | + /** | ||
| 59 | + * 授权时间(创建时间) | ||
| 60 | + */ | ||
| 61 | + @TableField(fill = FieldFill.INSERT) | ||
| 62 | + private LocalDateTime createdAt; | ||
| 63 | + /** | ||
| 64 | + * 更新时间 | ||
| 65 | + */ | ||
| 66 | + @TableField(fill = FieldFill.INSERT_UPDATE) | ||
| 67 | + private LocalDateTime updatedAt; | ||
| 68 | + /** | ||
| 69 | + * 授权状态,0-待使用(已授权但未发送),1-已使用(已发送,逻辑删除) | ||
| 70 | + */ | ||
| 71 | + @TableLogic | ||
| 72 | + private Boolean isDeleted; | ||
| 73 | +} | ||
| 74 | + |
| 1 | +package com.tianting.infoloop.service; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.extension.service.IService; | ||
| 4 | +import com.infoloop.tianting.mealorderservice.CreateOrderSubscriptionMessageRpcRequest; | ||
| 5 | +import com.infoloop.tianting.mealorderservice.GetUserOrderSubscriptionMessageHistoryRpcRequest; | ||
| 6 | +import com.tianting.infoloop.model.db.OrderSubscriptionMessageDb; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +public interface OrderSubscriptionMessageDbService extends IService<OrderSubscriptionMessageDb> { | ||
| 11 | + | ||
| 12 | + OrderSubscriptionMessageDb createOrderSubscriptionMessage(CreateOrderSubscriptionMessageRpcRequest request); | ||
| 13 | + | ||
| 14 | + List<OrderSubscriptionMessageDb> getUserOrderSubscriptionMessageHistory(GetUserOrderSubscriptionMessageHistoryRpcRequest request); | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 批量逻辑删除授权记录(表示已使用) | ||
| 18 | + * @param ids 要删除的记录ID列表 | ||
| 19 | + * @param enterpriseId 企业ID | ||
| 20 | + * @return 受影响的行数 | ||
| 21 | + */ | ||
| 22 | + int batchDeleteByIds(java.util.List<Long> ids, int enterpriseId); | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 批量物理删除授权记录 | ||
| 26 | + * @param ids 要删除的记录ID列表 | ||
| 27 | + * @param enterpriseId 企业ID | ||
| 28 | + * @return 受影响的行数 | ||
| 29 | + */ | ||
| 30 | + int batchPhysicalDeleteByIds(java.util.List<Long> ids, int enterpriseId); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 查询待发送的订阅消息(未删除的记录) | ||
| 34 | + * @param enterpriseId 企业ID | ||
| 35 | + * @return 待发送的订阅消息列表 | ||
| 36 | + */ | ||
| 37 | + List<OrderSubscriptionMessageDb> queryPendingMessages(int enterpriseId); | ||
| 38 | +} | ||
| 39 | + |
| ... | @@ -7,6 +7,18 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ... | @@ -7,6 +7,18 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| 7 | import com.infoloop.tianting.mealorderservice.BatchCreateDinerMealSuspensionRecordsRpcRequest; | 7 | import com.infoloop.tianting.mealorderservice.BatchCreateDinerMealSuspensionRecordsRpcRequest; |
| 8 | import com.infoloop.tianting.mealorderservice.BatchCreateDinerMealSuspensionRecordsRpcResponse; | 8 | import com.infoloop.tianting.mealorderservice.BatchCreateDinerMealSuspensionRecordsRpcResponse; |
| 9 | import com.infoloop.tianting.mealorderservice.BatchCreateGradeClassesRpcRequest; | 9 | import com.infoloop.tianting.mealorderservice.BatchCreateGradeClassesRpcRequest; |
| 10 | +import com.infoloop.tianting.mealorderservice.BatchDeleteOrderSubscriptionMessagesByIdsRpcRequest; | ||
| 11 | +import com.infoloop.tianting.mealorderservice.BatchDeleteOrderSubscriptionMessagesByIdsRpcResponse; | ||
| 12 | +import com.infoloop.tianting.mealorderservice.BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcRequest; | ||
| 13 | +import com.infoloop.tianting.mealorderservice.BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcResponse; | ||
| 14 | +import com.infoloop.tianting.mealorderservice.CreateOrderSubscriptionMessageRpcRequest; | ||
| 15 | +import com.infoloop.tianting.mealorderservice.CreateOrderSubscriptionMessageRpcResponse; | ||
| 16 | +import com.infoloop.tianting.mealorderservice.GetUserOrderSubscriptionMessageHistoryRpcRequest; | ||
| 17 | +import com.infoloop.tianting.mealorderservice.GetUserOrderSubscriptionMessageHistoryRpcResponse; | ||
| 18 | +import com.infoloop.tianting.mealorderservice.OrderSubscriptionMessageRpcResponse; | ||
| 19 | +import com.infoloop.tianting.mealorderservice.PendingOrderSubscriptionMessageRpcResponse; | ||
| 20 | +import com.infoloop.tianting.mealorderservice.QueryPendingOrderSubscriptionMessagesRpcRequest; | ||
| 21 | +import com.infoloop.tianting.mealorderservice.QueryPendingOrderSubscriptionMessagesRpcResponse; | ||
| 10 | import com.infoloop.tianting.mealorderservice.BatchCreateGradeClassesRpcResponse; | 22 | import com.infoloop.tianting.mealorderservice.BatchCreateGradeClassesRpcResponse; |
| 11 | import com.infoloop.tianting.mealorderservice.BatchCreateMealOrdersRpcRequest; | 23 | import com.infoloop.tianting.mealorderservice.BatchCreateMealOrdersRpcRequest; |
| 12 | import com.infoloop.tianting.mealorderservice.BatchCreateMealOrdersRpcResponse; | 24 | import com.infoloop.tianting.mealorderservice.BatchCreateMealOrdersRpcResponse; |
| ... | @@ -124,6 +136,8 @@ import com.tianting.infoloop.service.MealOrderService; | ... | @@ -124,6 +136,8 @@ import com.tianting.infoloop.service.MealOrderService; |
| 124 | import com.tianting.infoloop.service.MpAccountDinerRefService; | 136 | import com.tianting.infoloop.service.MpAccountDinerRefService; |
| 125 | import com.tianting.infoloop.service.MpAccountService; | 137 | import com.tianting.infoloop.service.MpAccountService; |
| 126 | import com.tianting.infoloop.service.DinerMealSuspensionRecordService; | 138 | import com.tianting.infoloop.service.DinerMealSuspensionRecordService; |
| 139 | +import com.tianting.infoloop.service.OrderSubscriptionMessageDbService; | ||
| 140 | +import com.tianting.infoloop.model.db.OrderSubscriptionMessageDb; | ||
| 127 | import com.tianting.infoloop.utils.ProtoBeanUtil; | 141 | import com.tianting.infoloop.utils.ProtoBeanUtil; |
| 128 | import io.grpc.Status; | 142 | import io.grpc.Status; |
| 129 | import io.grpc.stub.StreamObserver; | 143 | import io.grpc.stub.StreamObserver; |
| ... | @@ -134,10 +148,10 @@ import org.springframework.stereotype.Service; | ... | @@ -134,10 +148,10 @@ import org.springframework.stereotype.Service; |
| 134 | import org.springframework.transaction.annotation.Transactional; | 148 | import org.springframework.transaction.annotation.Transactional; |
| 135 | 149 | ||
| 136 | import java.time.DayOfWeek; | 150 | import java.time.DayOfWeek; |
| 137 | -import java.time.Instant; | ||
| 138 | import java.time.LocalDate; | 151 | import java.time.LocalDate; |
| 139 | import java.time.LocalDateTime; | 152 | import java.time.LocalDateTime; |
| 140 | import java.time.LocalTime; | 153 | import java.time.LocalTime; |
| 154 | +import java.time.ZoneId; | ||
| 141 | import java.time.temporal.TemporalAdjusters; | 155 | import java.time.temporal.TemporalAdjusters; |
| 142 | import java.util.ArrayList; | 156 | import java.util.ArrayList; |
| 143 | import java.util.Collections; | 157 | import java.util.Collections; |
| ... | @@ -167,6 +181,8 @@ public class MealOrderGrpcService extends MealOrderServiceRpcGrpc.MealOrderServi | ... | @@ -167,6 +181,8 @@ public class MealOrderGrpcService extends MealOrderServiceRpcGrpc.MealOrderServi |
| 167 | 181 | ||
| 168 | private final DinerMealSuspensionRecordService dinerMealSuspensionRecordService; | 182 | private final DinerMealSuspensionRecordService dinerMealSuspensionRecordService; |
| 169 | 183 | ||
| 184 | + private final OrderSubscriptionMessageDbService orderSubscriptionMessageDbService; | ||
| 185 | + | ||
| 170 | @Override | 186 | @Override |
| 171 | @DS(DATA_SOURCE_SLAVE) | 187 | @DS(DATA_SOURCE_SLAVE) |
| 172 | public void getMealOrderById(final GetMealOrderByIdRpcRequest request, | 188 | public void getMealOrderById(final GetMealOrderByIdRpcRequest request, |
| ... | @@ -1417,4 +1433,149 @@ public class MealOrderGrpcService extends MealOrderServiceRpcGrpc.MealOrderServi | ... | @@ -1417,4 +1433,149 @@ public class MealOrderGrpcService extends MealOrderServiceRpcGrpc.MealOrderServi |
| 1417 | responseObserver.onNext(builder.build()); | 1433 | responseObserver.onNext(builder.build()); |
| 1418 | responseObserver.onCompleted(); | 1434 | responseObserver.onCompleted(); |
| 1419 | } | 1435 | } |
| 1436 | + | ||
| 1437 | + // 订餐订阅消息相关服务实现 | ||
| 1438 | + @Override | ||
| 1439 | + @DS(DATA_SOURCE_MASTER) | ||
| 1440 | + public void createOrderSubscriptionMessage(final CreateOrderSubscriptionMessageRpcRequest request, | ||
| 1441 | + final StreamObserver<CreateOrderSubscriptionMessageRpcResponse> responseObserver) { | ||
| 1442 | + final var builder = CreateOrderSubscriptionMessageRpcResponse.newBuilder(); | ||
| 1443 | + try { | ||
| 1444 | + final var data = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(request); | ||
| 1445 | + builder.setId(data.getId()); | ||
| 1446 | + } catch (final Exception e) { | ||
| 1447 | + log.error("createOrderSubscriptionMessage error;", e); | ||
| 1448 | + responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asException()); | ||
| 1449 | + return; | ||
| 1450 | + } | ||
| 1451 | + responseObserver.onNext(builder.build()); | ||
| 1452 | + responseObserver.onCompleted(); | ||
| 1453 | + } | ||
| 1454 | + | ||
| 1455 | + @Override | ||
| 1456 | + @DS(DATA_SOURCE_MASTER) | ||
| 1457 | + public void getUserOrderSubscriptionMessageHistory(final GetUserOrderSubscriptionMessageHistoryRpcRequest request, | ||
| 1458 | + final StreamObserver<GetUserOrderSubscriptionMessageHistoryRpcResponse> responseObserver) { | ||
| 1459 | + final var builder = GetUserOrderSubscriptionMessageHistoryRpcResponse.newBuilder(); | ||
| 1460 | + try { | ||
| 1461 | + final var list = orderSubscriptionMessageDbService.getUserOrderSubscriptionMessageHistory(request); | ||
| 1462 | + builder.addAllResponse(list.stream() | ||
| 1463 | + .map(this::convertToRpcResponse) | ||
| 1464 | + .collect(Collectors.toList())); | ||
| 1465 | + } catch (final Exception e) { | ||
| 1466 | + log.error("getUserOrderSubscriptionMessageHistory error;", e); | ||
| 1467 | + responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asException()); | ||
| 1468 | + return; | ||
| 1469 | + } | ||
| 1470 | + responseObserver.onNext(builder.build()); | ||
| 1471 | + responseObserver.onCompleted(); | ||
| 1472 | + } | ||
| 1473 | + | ||
| 1474 | + private OrderSubscriptionMessageRpcResponse convertToRpcResponse(OrderSubscriptionMessageDb db) { | ||
| 1475 | + OrderSubscriptionMessageRpcResponse.Builder builder = OrderSubscriptionMessageRpcResponse.newBuilder(); | ||
| 1476 | + builder.setId(db.getId()); | ||
| 1477 | + | ||
| 1478 | + if (db.getJumpPath() != null) { | ||
| 1479 | + builder.setJumpPath(db.getJumpPath()); | ||
| 1480 | + } | ||
| 1481 | + | ||
| 1482 | + // 转换LocalDateTime为时间戳(毫秒),与项目中其他时间字段保持一致 | ||
| 1483 | + if (db.getOrderPeriodStartDate() != null) { | ||
| 1484 | + long timestamp = db.getOrderPeriodStartDate().atZone(ZoneId.systemDefault()) | ||
| 1485 | + .toInstant() | ||
| 1486 | + .toEpochMilli(); | ||
| 1487 | + builder.setOrderPeriodStartDate(timestamp); | ||
| 1488 | + } | ||
| 1489 | + | ||
| 1490 | + if (db.getOrderPeriodEndDate() != null) { | ||
| 1491 | + long timestamp = db.getOrderPeriodEndDate().atZone(ZoneId.systemDefault()) | ||
| 1492 | + .toInstant() | ||
| 1493 | + .toEpochMilli(); | ||
| 1494 | + builder.setOrderPeriodEndDate(timestamp); | ||
| 1495 | + } | ||
| 1496 | + | ||
| 1497 | + // 转换LocalDateTime为时间戳(毫秒) | ||
| 1498 | + if (db.getCreatedAt() != null) { | ||
| 1499 | + long timestamp = db.getCreatedAt().atZone(ZoneId.systemDefault()) | ||
| 1500 | + .toInstant() | ||
| 1501 | + .toEpochMilli(); | ||
| 1502 | + builder.setCreatedAt(timestamp); | ||
| 1503 | + } | ||
| 1504 | + | ||
| 1505 | + builder.setIsDeleted(db.getIsDeleted() != null && db.getIsDeleted()); | ||
| 1506 | + return builder.build(); | ||
| 1507 | + } | ||
| 1508 | + | ||
| 1509 | + @Override | ||
| 1510 | + @DS(DATA_SOURCE_MASTER) | ||
| 1511 | + public void batchDeleteOrderSubscriptionMessagesByIds(final BatchDeleteOrderSubscriptionMessagesByIdsRpcRequest request, | ||
| 1512 | + final StreamObserver<BatchDeleteOrderSubscriptionMessagesByIdsRpcResponse> responseObserver) { | ||
| 1513 | + final var builder = BatchDeleteOrderSubscriptionMessagesByIdsRpcResponse.newBuilder(); | ||
| 1514 | + try { | ||
| 1515 | + final int affectedRows = orderSubscriptionMessageDbService.batchDeleteByIds( | ||
| 1516 | + request.getIdsList(), | ||
| 1517 | + request.getEnterpriseId()); | ||
| 1518 | + builder.setAffectedRows(affectedRows); | ||
| 1519 | + } catch (final Exception e) { | ||
| 1520 | + log.error("batchDeleteOrderSubscriptionMessagesByIds error;", e); | ||
| 1521 | + responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asException()); | ||
| 1522 | + return; | ||
| 1523 | + } | ||
| 1524 | + responseObserver.onNext(builder.build()); | ||
| 1525 | + responseObserver.onCompleted(); | ||
| 1526 | + } | ||
| 1527 | + | ||
| 1528 | + @Override | ||
| 1529 | + @DS(DATA_SOURCE_MASTER) | ||
| 1530 | + public void batchPhysicalDeleteOrderSubscriptionMessagesByIds( | ||
| 1531 | + final BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcRequest request, | ||
| 1532 | + final StreamObserver<BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcResponse> responseObserver) { | ||
| 1533 | + final var builder = BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcResponse.newBuilder(); | ||
| 1534 | + try { | ||
| 1535 | + final int affectedRows = orderSubscriptionMessageDbService.batchPhysicalDeleteByIds( | ||
| 1536 | + request.getIdsList(), | ||
| 1537 | + request.getEnterpriseId()); | ||
| 1538 | + builder.setAffectedRows(affectedRows); | ||
| 1539 | + } catch (final Exception e) { | ||
| 1540 | + log.error("batchPhysicalDeleteOrderSubscriptionMessagesByIds error;", e); | ||
| 1541 | + responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asException()); | ||
| 1542 | + return; | ||
| 1543 | + } | ||
| 1544 | + responseObserver.onNext(builder.build()); | ||
| 1545 | + responseObserver.onCompleted(); | ||
| 1546 | + } | ||
| 1547 | + | ||
| 1548 | + @Override | ||
| 1549 | + @DS(DATA_SOURCE_SLAVE) | ||
| 1550 | + public void queryPendingOrderSubscriptionMessages( | ||
| 1551 | + final QueryPendingOrderSubscriptionMessagesRpcRequest request, | ||
| 1552 | + final StreamObserver<QueryPendingOrderSubscriptionMessagesRpcResponse> responseObserver) { | ||
| 1553 | + final var builder = QueryPendingOrderSubscriptionMessagesRpcResponse.newBuilder(); | ||
| 1554 | + try { | ||
| 1555 | + final var messages = orderSubscriptionMessageDbService.queryPendingMessages( | ||
| 1556 | + request.getEnterpriseId()); | ||
| 1557 | + for (final var message : messages) { | ||
| 1558 | + builder.addResponses(PendingOrderSubscriptionMessageRpcResponse.newBuilder() | ||
| 1559 | + .setId(message.getId()) | ||
| 1560 | + .setEnterpriseId(message.getEnterpriseId()) | ||
| 1561 | + .setDinerId(message.getDinerId() != null ? message.getDinerId() : 0) | ||
| 1562 | + .setOpenId(message.getOpenId()) | ||
| 1563 | + .setTemplateId(message.getTemplateId() != null ? message.getTemplateId() : "") | ||
| 1564 | + .setOrderPeriodStartDate(message.getOrderPeriodStartDate() != null ? | ||
| 1565 | + message.getOrderPeriodStartDate().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : 0) | ||
| 1566 | + .setJumpPath(message.getJumpPath() != null ? message.getJumpPath() : "") | ||
| 1567 | + .setOrderPeriodEndDate(message.getOrderPeriodEndDate() != null ? | ||
| 1568 | + message.getOrderPeriodEndDate().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : 0) | ||
| 1569 | + .setCreatedAt(message.getCreatedAt() != null ? | ||
| 1570 | + message.getCreatedAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : 0) | ||
| 1571 | + .build()); | ||
| 1572 | + } | ||
| 1573 | + } catch (final Exception e) { | ||
| 1574 | + log.error("queryPendingOrderSubscriptionMessages error;", e); | ||
| 1575 | + responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asException()); | ||
| 1576 | + return; | ||
| 1577 | + } | ||
| 1578 | + responseObserver.onNext(builder.build()); | ||
| 1579 | + responseObserver.onCompleted(); | ||
| 1580 | + } | ||
| 1420 | } | 1581 | } | ... | ... |
src/main/java/com/tianting/infoloop/service/impl/OrderSubscriptionMessageDbServiceImpl.java
0 → 100644
| 1 | +package com.tianting.infoloop.service.impl; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 4 | +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||
| 5 | +import com.infoloop.tianting.mealorderservice.CreateOrderSubscriptionMessageRpcRequest; | ||
| 6 | +import com.infoloop.tianting.mealorderservice.GetUserOrderSubscriptionMessageHistoryRpcRequest; | ||
| 7 | +import com.tianting.infoloop.mapper.OrderSubscriptionMessageMapper; | ||
| 8 | +import com.tianting.infoloop.model.db.OrderSubscriptionMessageDb; | ||
| 9 | +import com.tianting.infoloop.service.OrderSubscriptionMessageDbService; | ||
| 10 | +import com.tianting.infoloop.utils.exception.ErrorCodeConstants; | ||
| 11 | +import com.tianting.infoloop.utils.exception.ServiceExceptionUtil; | ||
| 12 | +import lombok.RequiredArgsConstructor; | ||
| 13 | +import lombok.extern.slf4j.Slf4j; | ||
| 14 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 15 | +import org.springframework.stereotype.Service; | ||
| 16 | + | ||
| 17 | +import java.time.Instant; | ||
| 18 | +import java.time.LocalDateTime; | ||
| 19 | +import java.time.ZoneId; | ||
| 20 | +import java.util.List; | ||
| 21 | + | ||
| 22 | +@Slf4j | ||
| 23 | +@Service | ||
| 24 | +@RequiredArgsConstructor(onConstructor_ = @Autowired) | ||
| 25 | +public class OrderSubscriptionMessageDbServiceImpl extends ServiceImpl<OrderSubscriptionMessageMapper, OrderSubscriptionMessageDb> implements OrderSubscriptionMessageDbService { | ||
| 26 | + | ||
| 27 | + @Override | ||
| 28 | + public OrderSubscriptionMessageDb createOrderSubscriptionMessage(CreateOrderSubscriptionMessageRpcRequest request) { | ||
| 29 | + OrderSubscriptionMessageDb orderSubscriptionMessageDb = new OrderSubscriptionMessageDb(); | ||
| 30 | + orderSubscriptionMessageDb.setEnterpriseId(request.getEnterpriseId()); | ||
| 31 | + if (request.getDinerId() != 0) { | ||
| 32 | + orderSubscriptionMessageDb.setDinerId(request.getDinerId()); | ||
| 33 | + } | ||
| 34 | + orderSubscriptionMessageDb.setOpenId(request.getOpenId()); | ||
| 35 | + orderSubscriptionMessageDb.setTemplateId(request.getTemplateId()); | ||
| 36 | + if (!request.getJumpPath().isEmpty()) { | ||
| 37 | + orderSubscriptionMessageDb.setJumpPath(request.getJumpPath()); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + // 转换时间戳为LocalDateTime(精确到秒),与项目中其他时间字段保持一致 | ||
| 41 | + LocalDateTime orderPeriodStartDate = Instant.ofEpochMilli(request.getOrderPeriodStartDate()) | ||
| 42 | + .atZone(ZoneId.systemDefault()) | ||
| 43 | + .toLocalDateTime(); | ||
| 44 | + orderSubscriptionMessageDb.setOrderPeriodStartDate(orderPeriodStartDate); | ||
| 45 | + | ||
| 46 | + if (request.getOrderPeriodEndDate() > 0) { | ||
| 47 | + LocalDateTime orderPeriodEndDate = Instant.ofEpochMilli(request.getOrderPeriodEndDate()) | ||
| 48 | + .atZone(ZoneId.systemDefault()) | ||
| 49 | + .toLocalDateTime(); | ||
| 50 | + orderSubscriptionMessageDb.setOrderPeriodEndDate(orderPeriodEndDate); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + boolean res = this.save(orderSubscriptionMessageDb); | ||
| 54 | + if (!res) { | ||
| 55 | + log.error("创建订阅消息授权记录失败, request: {}", request); | ||
| 56 | + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_GEN_FAIL); | ||
| 57 | + } | ||
| 58 | + return orderSubscriptionMessageDb; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + @Override | ||
| 62 | + public List<OrderSubscriptionMessageDb> getUserOrderSubscriptionMessageHistory(GetUserOrderSubscriptionMessageHistoryRpcRequest request) { | ||
| 63 | + LambdaQueryWrapper<OrderSubscriptionMessageDb> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 64 | + queryWrapper.eq(OrderSubscriptionMessageDb::getEnterpriseId, request.getEnterpriseId()); | ||
| 65 | + queryWrapper.eq(OrderSubscriptionMessageDb::getOpenId, request.getOpenId()); | ||
| 66 | + | ||
| 67 | + // 如果传入了 orderPeriodStartDate,查询大于等于该值的记录 | ||
| 68 | + if (request.getOrderPeriodStartDate() > 0) { | ||
| 69 | + LocalDateTime orderPeriodStartDate = Instant.ofEpochMilli(request.getOrderPeriodStartDate()) | ||
| 70 | + .atZone(ZoneId.systemDefault()) | ||
| 71 | + .toLocalDateTime(); | ||
| 72 | + queryWrapper.ge(OrderSubscriptionMessageDb::getOrderPeriodStartDate, orderPeriodStartDate); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + queryWrapper.orderByDesc(OrderSubscriptionMessageDb::getCreatedAt); | ||
| 76 | + return this.list(queryWrapper); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Override | ||
| 80 | + public int batchDeleteByIds(java.util.List<Long> ids, int enterpriseId) { | ||
| 81 | + if (ids == null || ids.isEmpty()) { | ||
| 82 | + return 0; | ||
| 83 | + } | ||
| 84 | + LambdaQueryWrapper<OrderSubscriptionMessageDb> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 85 | + queryWrapper.eq(OrderSubscriptionMessageDb::getEnterpriseId, enterpriseId); | ||
| 86 | + queryWrapper.in(OrderSubscriptionMessageDb::getId, ids); | ||
| 87 | + // 逻辑删除(使用 MyBatis-Plus 的 @TableLogic) | ||
| 88 | + return this.remove(queryWrapper) ? ids.size() : 0; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + @Override | ||
| 92 | + public int batchPhysicalDeleteByIds(java.util.List<Long> ids, int enterpriseId) { | ||
| 93 | + if (ids == null || ids.isEmpty()) { | ||
| 94 | + return 0; | ||
| 95 | + } | ||
| 96 | + // 物理删除(使用 Mapper 中的 SQL,绕过逻辑删除) | ||
| 97 | + return this.baseMapper.physicalDeleteByIds(ids, enterpriseId); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @Override | ||
| 101 | + public List<OrderSubscriptionMessageDb> queryPendingMessages(int enterpriseId) { | ||
| 102 | + LambdaQueryWrapper<OrderSubscriptionMessageDb> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 103 | + queryWrapper.eq(OrderSubscriptionMessageDb::getEnterpriseId, enterpriseId) | ||
| 104 | + .eq(OrderSubscriptionMessageDb::getIsDeleted, false) | ||
| 105 | + .orderByAsc(OrderSubscriptionMessageDb::getCreatedAt); | ||
| 106 | + return this.list(queryWrapper); | ||
| 107 | + } | ||
| 108 | +} | ||
| 109 | + |
| ... | @@ -63,6 +63,13 @@ service MealOrderServiceRpc { | ... | @@ -63,6 +63,13 @@ service MealOrderServiceRpc { |
| 63 | rpc batchCreateDinerMealSuspensionRecords(BatchCreateDinerMealSuspensionRecordsRpcRequest) returns (BatchCreateDinerMealSuspensionRecordsRpcResponse); | 63 | rpc batchCreateDinerMealSuspensionRecords(BatchCreateDinerMealSuspensionRecordsRpcRequest) returns (BatchCreateDinerMealSuspensionRecordsRpcResponse); |
| 64 | rpc updateDinerMealSuspensionRecord(UpdateDinerMealSuspensionRecordRpcRequest) returns (UpdateDinerMealSuspensionRecordRpcResponse); | 64 | rpc updateDinerMealSuspensionRecord(UpdateDinerMealSuspensionRecordRpcRequest) returns (UpdateDinerMealSuspensionRecordRpcResponse); |
| 65 | rpc deleteDinerMealSuspensionRecordsByIds(DeleteDinerMealSuspensionRecordsByIdsRpcRequest) returns (DeleteDinerMealSuspensionRecordsRpcResponse); | 65 | rpc deleteDinerMealSuspensionRecordsByIds(DeleteDinerMealSuspensionRecordsByIdsRpcRequest) returns (DeleteDinerMealSuspensionRecordsRpcResponse); |
| 66 | + | ||
| 67 | + // 订餐订阅消息相关服务 | ||
| 68 | + rpc CreateOrderSubscriptionMessage (CreateOrderSubscriptionMessageRpcRequest) returns (CreateOrderSubscriptionMessageRpcResponse) {} | ||
| 69 | + rpc GetUserOrderSubscriptionMessageHistory (GetUserOrderSubscriptionMessageHistoryRpcRequest) returns (GetUserOrderSubscriptionMessageHistoryRpcResponse) {} | ||
| 70 | + rpc BatchDeleteOrderSubscriptionMessagesByIds (BatchDeleteOrderSubscriptionMessagesByIdsRpcRequest) returns (BatchDeleteOrderSubscriptionMessagesByIdsRpcResponse) {} | ||
| 71 | + rpc BatchPhysicalDeleteOrderSubscriptionMessagesByIds (BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcRequest) returns (BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcResponse) {} | ||
| 72 | + rpc QueryPendingOrderSubscriptionMessages (QueryPendingOrderSubscriptionMessagesRpcRequest) returns (QueryPendingOrderSubscriptionMessagesRpcResponse) {} | ||
| 66 | } | 73 | } |
| 67 | 74 | ||
| 68 | enum MealOrderOrderMethodEnum { | 75 | enum MealOrderOrderMethodEnum { |
| ... | @@ -955,3 +962,76 @@ message BatchCreateDinerMealSuspensionRecordsRpcResponse { | ... | @@ -955,3 +962,76 @@ message BatchCreateDinerMealSuspensionRecordsRpcResponse { |
| 955 | bool isCreated = 1; | 962 | bool isCreated = 1; |
| 956 | repeated int32 ids = 2; | 963 | repeated int32 ids = 2; |
| 957 | } | 964 | } |
| 965 | + | ||
| 966 | +// 订餐订阅消息相关消息定义 | ||
| 967 | +message CreateOrderSubscriptionMessageRpcRequest { | ||
| 968 | + int32 enterpriseId = 1; | ||
| 969 | + int32 dinerId = 2; | ||
| 970 | + string openId = 3; | ||
| 971 | + string templateId = 4; | ||
| 972 | + int64 orderPeriodStartDate = 5; // Unix 时间戳(毫秒),精确到秒 | ||
| 973 | + string jumpPath = 6; // 跳转路径(小程序 path),可选 | ||
| 974 | + int64 orderPeriodEndDate = 7; // Unix 时间戳(毫秒),精确到秒,可选 | ||
| 975 | +} | ||
| 976 | + | ||
| 977 | +message CreateOrderSubscriptionMessageRpcResponse { | ||
| 978 | + int64 id = 1; | ||
| 979 | +} | ||
| 980 | + | ||
| 981 | +message GetUserOrderSubscriptionMessageHistoryRpcRequest { | ||
| 982 | + int32 enterpriseId = 1; | ||
| 983 | + string openId = 2; | ||
| 984 | + int64 orderPeriodStartDate = 3; // Unix 时间戳(毫秒),精确到秒,可选,如果传入则查询大于等于该值的记录 | ||
| 985 | +} | ||
| 986 | + | ||
| 987 | +message OrderSubscriptionMessageRpcResponse { | ||
| 988 | + int64 id = 1; | ||
| 989 | + int64 orderPeriodStartDate = 2; // Unix 时间戳(毫秒),精确到秒 | ||
| 990 | + int64 createdAt = 3; // Unix 时间戳(毫秒) | ||
| 991 | + bool isDeleted = 4; | ||
| 992 | + string jumpPath = 5; // 跳转路径(小程序 path),可选 | ||
| 993 | + int64 orderPeriodEndDate = 6; // Unix 时间戳(毫秒),精确到秒,可选 | ||
| 994 | +} | ||
| 995 | + | ||
| 996 | +message GetUserOrderSubscriptionMessageHistoryRpcResponse { | ||
| 997 | + repeated OrderSubscriptionMessageRpcResponse response = 1; | ||
| 998 | +} | ||
| 999 | + | ||
| 1000 | +message BatchDeleteOrderSubscriptionMessagesByIdsRpcRequest { | ||
| 1001 | + int32 enterpriseId = 1; | ||
| 1002 | + repeated int64 ids = 2; // 要删除的记录ID列表 | ||
| 1003 | +} | ||
| 1004 | + | ||
| 1005 | +message BatchDeleteOrderSubscriptionMessagesByIdsRpcResponse { | ||
| 1006 | + int32 affectedRows = 1; // 受影响的行数 | ||
| 1007 | +} | ||
| 1008 | + | ||
| 1009 | +message BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcRequest { | ||
| 1010 | + int32 enterpriseId = 1; | ||
| 1011 | + repeated int64 ids = 2; // 要删除的记录ID列表 | ||
| 1012 | +} | ||
| 1013 | + | ||
| 1014 | +message BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcResponse { | ||
| 1015 | + int32 affectedRows = 1; // 受影响的行数 | ||
| 1016 | +} | ||
| 1017 | + | ||
| 1018 | +// 查询待发送的订阅消息(未删除的记录) | ||
| 1019 | +message QueryPendingOrderSubscriptionMessagesRpcRequest { | ||
| 1020 | + int32 enterpriseId = 1; | ||
| 1021 | +} | ||
| 1022 | + | ||
| 1023 | +message PendingOrderSubscriptionMessageRpcResponse { | ||
| 1024 | + int64 id = 1; | ||
| 1025 | + int32 enterpriseId = 2; | ||
| 1026 | + int32 dinerId = 3; | ||
| 1027 | + string openId = 4; | ||
| 1028 | + string templateId = 5; | ||
| 1029 | + int64 orderPeriodStartDate = 6; | ||
| 1030 | + string jumpPath = 7; | ||
| 1031 | + int64 orderPeriodEndDate = 8; | ||
| 1032 | + int64 createdAt = 9; | ||
| 1033 | +} | ||
| 1034 | + | ||
| 1035 | +message QueryPendingOrderSubscriptionMessagesRpcResponse { | ||
| 1036 | + repeated PendingOrderSubscriptionMessageRpcResponse responses = 1; | ||
| 1037 | +} | ... | ... |
| 1 | +package com.tianting.infoloop.service; | ||
| 2 | + | ||
| 3 | +import com.infoloop.tianting.ordersubscriptionmessageservice.CreateOrderSubscriptionMessageRpcRequest; | ||
| 4 | +import com.infoloop.tianting.ordersubscriptionmessageservice.GetUserOrderSubscriptionMessageHistoryRpcRequest; | ||
| 5 | + | ||
| 6 | +import java.util.Arrays; | ||
| 7 | +import java.util.List; | ||
| 8 | +import com.tianting.infoloop.App; | ||
| 9 | +import com.tianting.infoloop.model.db.OrderSubscriptionMessageDb; | ||
| 10 | +import org.junit.jupiter.api.BeforeAll; | ||
| 11 | +import org.junit.jupiter.api.Test; | ||
| 12 | +import org.junit.jupiter.api.TestInstance; | ||
| 13 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 14 | +import org.springframework.boot.test.context.SpringBootTest; | ||
| 15 | +import org.springframework.jdbc.core.JdbcTemplate; | ||
| 16 | +import org.springframework.test.context.ActiveProfiles; | ||
| 17 | + | ||
| 18 | +import java.util.List; | ||
| 19 | + | ||
| 20 | +import static org.assertj.core.api.Assertions.assertThat; | ||
| 21 | + | ||
| 22 | +@ActiveProfiles("dev") | ||
| 23 | +@SpringBootTest(classes = App.class) | ||
| 24 | +@TestInstance(TestInstance.Lifecycle.PER_CLASS) | ||
| 25 | +class OrderSubscriptionMessageDbServiceTest { | ||
| 26 | + | ||
| 27 | + @Autowired | ||
| 28 | + private JdbcTemplate jdbcTemplate; | ||
| 29 | + | ||
| 30 | + @Autowired | ||
| 31 | + private OrderSubscriptionMessageDbService orderSubscriptionMessageDbService; | ||
| 32 | + | ||
| 33 | + @BeforeAll | ||
| 34 | + void ensureTable() { | ||
| 35 | + jdbcTemplate.execute( | ||
| 36 | + "CREATE TABLE IF NOT EXISTS order_subscription_message (" | ||
| 37 | + + "id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID'," | ||
| 38 | + + "enterpriseId INT NOT NULL COMMENT '企业ID'," | ||
| 39 | + + "dinerId INT DEFAULT NULL COMMENT '订餐用户ID(dinerId)'," | ||
| 40 | + + "openId VARCHAR(255) NOT NULL COMMENT '微信用户OpenID'," | ||
| 41 | + + "templateId VARCHAR(255) NOT NULL COMMENT '订阅消息模板ID'," | ||
| 42 | + + "jumpPath VARCHAR(255) DEFAULT NULL COMMENT '跳转路径(小程序 path)'," | ||
| 43 | + + "orderPeriodStartDate DATETIME DEFAULT NULL COMMENT '关联的订餐周期开始时间(精确到秒)'," | ||
| 44 | + + "orderPeriodEndDate DATETIME DEFAULT NULL COMMENT '关联的订餐周期结束时间(精确到秒)'," | ||
| 45 | + + "createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'," | ||
| 46 | + + "updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'," | ||
| 47 | + + "isDeleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除;0:未删除,1:已删除'," | ||
| 48 | + + "PRIMARY KEY (id)," | ||
| 49 | + + "KEY idx_enterprise_id (enterpriseId)" | ||
| 50 | + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订餐订阅消息授权表';"); | ||
| 51 | + | ||
| 52 | + // 兼容本地已存在的旧表结构(修改字段类型为 DATETIME,精确到秒) | ||
| 53 | + Integer startDateType = jdbcTemplate.queryForObject( | ||
| 54 | + "SELECT COUNT(1) FROM information_schema.COLUMNS " | ||
| 55 | + + "WHERE table_schema = DATABASE() " | ||
| 56 | + + "AND table_name = 'order_subscription_message' " | ||
| 57 | + + "AND column_name = 'orderPeriodStartDate' " | ||
| 58 | + + "AND data_type IN ('date', 'bigint')", | ||
| 59 | + Integer.class); | ||
| 60 | + if (startDateType != null && startDateType > 0) { | ||
| 61 | + // 如果字段是 bigint,需要先清空数据或转换数据,这里先清空(测试环境) | ||
| 62 | + jdbcTemplate.update("UPDATE order_subscription_message SET orderPeriodStartDate = NULL WHERE orderPeriodStartDate IS NOT NULL"); | ||
| 63 | + jdbcTemplate.execute( | ||
| 64 | + "ALTER TABLE order_subscription_message " | ||
| 65 | + + "MODIFY COLUMN orderPeriodStartDate DATETIME DEFAULT NULL COMMENT '关联的订餐周期开始时间(精确到秒)'"); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + Integer endDateExists = jdbcTemplate.queryForObject( | ||
| 69 | + "SELECT COUNT(1) FROM information_schema.COLUMNS " | ||
| 70 | + + "WHERE table_schema = DATABASE() " | ||
| 71 | + + "AND table_name = 'order_subscription_message' " | ||
| 72 | + + "AND column_name = 'orderPeriodEndDate'", | ||
| 73 | + Integer.class); | ||
| 74 | + if (endDateExists == null || endDateExists == 0) { | ||
| 75 | + jdbcTemplate.execute( | ||
| 76 | + "ALTER TABLE order_subscription_message " | ||
| 77 | + + "ADD COLUMN orderPeriodEndDate DATETIME DEFAULT NULL COMMENT '关联的订餐周期结束时间(精确到秒)' " | ||
| 78 | + + "AFTER orderPeriodStartDate"); | ||
| 79 | + } else { | ||
| 80 | + Integer endDateType = jdbcTemplate.queryForObject( | ||
| 81 | + "SELECT COUNT(1) FROM information_schema.COLUMNS " | ||
| 82 | + + "WHERE table_schema = DATABASE() " | ||
| 83 | + + "AND table_name = 'order_subscription_message' " | ||
| 84 | + + "AND column_name = 'orderPeriodEndDate' " | ||
| 85 | + + "AND data_type IN ('date', 'bigint')", | ||
| 86 | + Integer.class); | ||
| 87 | + if (endDateType != null && endDateType > 0) { | ||
| 88 | + // 如果字段是 bigint,需要先清空数据或转换数据,这里先清空(测试环境) | ||
| 89 | + jdbcTemplate.update("UPDATE order_subscription_message SET orderPeriodEndDate = NULL WHERE orderPeriodEndDate IS NOT NULL"); | ||
| 90 | + jdbcTemplate.execute( | ||
| 91 | + "ALTER TABLE order_subscription_message " | ||
| 92 | + + "MODIFY COLUMN orderPeriodEndDate DATETIME DEFAULT NULL COMMENT '关联的订餐周期结束时间(精确到秒)'"); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + @Test | ||
| 98 | + void shouldCreateAndQueryHistory() { | ||
| 99 | + final String openId = "openId_test_junit_001"; | ||
| 100 | + | ||
| 101 | + // clean | ||
| 102 | + jdbcTemplate.update("DELETE FROM order_subscription_message WHERE enterpriseId=? AND openId=?", 1, openId); | ||
| 103 | + | ||
| 104 | + // create | ||
| 105 | + CreateOrderSubscriptionMessageRpcRequest createReq = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 106 | + .setEnterpriseId(1) | ||
| 107 | + .setDinerId(100) | ||
| 108 | + .setOpenId(openId) | ||
| 109 | + .setTemplateId("template_test_junit_001") | ||
| 110 | + .setJumpPath("/pages/order/index") | ||
| 111 | + .setOrderPeriodStartDate(1738512000000L) // 2025-02-03 00:00:00 (毫秒时间戳,转换为 LocalDateTime) | ||
| 112 | + .setOrderPeriodEndDate(1739030400000L) // 2025-02-09 00:00:00 (毫秒时间戳,转换为 LocalDateTime) | ||
| 113 | + .build(); | ||
| 114 | + | ||
| 115 | + OrderSubscriptionMessageDb created = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq); | ||
| 116 | + assertThat(created.getId()).isNotNull(); | ||
| 117 | + assertThat(created.getEnterpriseId()).isEqualTo(1); | ||
| 118 | + assertThat(created.getDinerId()).isEqualTo(100); | ||
| 119 | + assertThat(created.getJumpPath()).isEqualTo("/pages/order/index"); | ||
| 120 | + assertThat(created.getOrderPeriodEndDate()).isNotNull(); | ||
| 121 | + | ||
| 122 | + // query history | ||
| 123 | + GetUserOrderSubscriptionMessageHistoryRpcRequest historyReq = GetUserOrderSubscriptionMessageHistoryRpcRequest.newBuilder() | ||
| 124 | + .setEnterpriseId(1) | ||
| 125 | + .setOpenId(openId) | ||
| 126 | + .setOrderPeriodStartDate(0) | ||
| 127 | + .build(); | ||
| 128 | + | ||
| 129 | + List<OrderSubscriptionMessageDb> list = orderSubscriptionMessageDbService.getUserOrderSubscriptionMessageHistory(historyReq); | ||
| 130 | + assertThat(list).isNotEmpty(); | ||
| 131 | + assertThat(list.get(0).getOpenId()).isEqualTo(openId); | ||
| 132 | + assertThat(list.get(0).getJumpPath()).isEqualTo("/pages/order/index"); | ||
| 133 | + assertThat(list.get(0).getOrderPeriodEndDate()).isNotNull(); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + @Test | ||
| 137 | + void shouldQueryByOrderPeriodStartDateWithGreaterEqual() { | ||
| 138 | + final String openId = "openId_test_junit_date_query"; | ||
| 139 | + | ||
| 140 | + // clean | ||
| 141 | + jdbcTemplate.update("DELETE FROM order_subscription_message WHERE enterpriseId=? AND openId=?", 1, openId); | ||
| 142 | + | ||
| 143 | + // 创建多条不同开始时间的记录 | ||
| 144 | + // 记录1: 2025-02-03 00:00:00 | ||
| 145 | + CreateOrderSubscriptionMessageRpcRequest createReq1 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 146 | + .setEnterpriseId(1) | ||
| 147 | + .setDinerId(100) | ||
| 148 | + .setOpenId(openId) | ||
| 149 | + .setTemplateId("template_test_date_1") | ||
| 150 | + .setJumpPath("/pages/order/index") | ||
| 151 | + .setOrderPeriodStartDate(1738512000000L) // 2025-02-03 00:00:00 | ||
| 152 | + .setOrderPeriodEndDate(1739030400000L) // 2025-02-09 00:00:00 | ||
| 153 | + .build(); | ||
| 154 | + OrderSubscriptionMessageDb created1 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq1); | ||
| 155 | + | ||
| 156 | + // 记录2: 2025-02-10 00:00:00 (晚于记录1) | ||
| 157 | + CreateOrderSubscriptionMessageRpcRequest createReq2 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 158 | + .setEnterpriseId(1) | ||
| 159 | + .setDinerId(100) | ||
| 160 | + .setOpenId(openId) | ||
| 161 | + .setTemplateId("template_test_date_2") | ||
| 162 | + .setJumpPath("/pages/order/index") | ||
| 163 | + .setOrderPeriodStartDate(1739116800000L) // 2025-02-10 00:00:00 | ||
| 164 | + .setOrderPeriodEndDate(1739635200000L) // 2025-02-16 00:00:00 | ||
| 165 | + .build(); | ||
| 166 | + OrderSubscriptionMessageDb created2 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq2); | ||
| 167 | + | ||
| 168 | + // 记录3: 2025-01-27 00:00:00 (早于记录1) | ||
| 169 | + CreateOrderSubscriptionMessageRpcRequest createReq3 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 170 | + .setEnterpriseId(1) | ||
| 171 | + .setDinerId(100) | ||
| 172 | + .setOpenId(openId) | ||
| 173 | + .setTemplateId("template_test_date_3") | ||
| 174 | + .setJumpPath("/pages/order/index") | ||
| 175 | + .setOrderPeriodStartDate(1737907200000L) // 2025-01-27 00:00:00 | ||
| 176 | + .setOrderPeriodEndDate(1738425600000L) // 2025-02-02 00:00:00 | ||
| 177 | + .build(); | ||
| 178 | + OrderSubscriptionMessageDb created3 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq3); | ||
| 179 | + | ||
| 180 | + // 记录4: 2025-02-03 12:00:00 (等于记录1的日期,但时间更晚) | ||
| 181 | + CreateOrderSubscriptionMessageRpcRequest createReq4 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 182 | + .setEnterpriseId(1) | ||
| 183 | + .setDinerId(100) | ||
| 184 | + .setOpenId(openId) | ||
| 185 | + .setTemplateId("template_test_date_4") | ||
| 186 | + .setJumpPath("/pages/order/index") | ||
| 187 | + .setOrderPeriodStartDate(1738555200000L) // 2025-02-03 12:00:00 | ||
| 188 | + .setOrderPeriodEndDate(1739073600000L) // 2025-02-09 12:00:00 | ||
| 189 | + .build(); | ||
| 190 | + OrderSubscriptionMessageDb created4 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq4); | ||
| 191 | + | ||
| 192 | + // 测试1: 查询 orderPeriodStartDate >= 2025-02-03 00:00:00 | ||
| 193 | + // 应该返回: 记录1(等于), 记录2(大于), 记录4(大于) | ||
| 194 | + // 不应该返回: 记录3(小于) | ||
| 195 | + GetUserOrderSubscriptionMessageHistoryRpcRequest historyReq = GetUserOrderSubscriptionMessageHistoryRpcRequest.newBuilder() | ||
| 196 | + .setEnterpriseId(1) | ||
| 197 | + .setOpenId(openId) | ||
| 198 | + .setOrderPeriodStartDate(1738512000000L) // 2025-02-03 00:00:00 | ||
| 199 | + .build(); | ||
| 200 | + | ||
| 201 | + List<OrderSubscriptionMessageDb> list = orderSubscriptionMessageDbService.getUserOrderSubscriptionMessageHistory(historyReq); | ||
| 202 | + | ||
| 203 | + // 验证返回的记录数量:应该包含记录1、记录2、记录4,共3条 | ||
| 204 | + assertThat(list.size()).isEqualTo(3); | ||
| 205 | + | ||
| 206 | + // 验证所有返回的记录的开始时间都 >= 2025-02-03 00:00:00 | ||
| 207 | + java.time.LocalDateTime threshold = java.time.Instant.ofEpochMilli(1738512000000L) | ||
| 208 | + .atZone(java.time.ZoneId.systemDefault()) | ||
| 209 | + .toLocalDateTime(); | ||
| 210 | + | ||
| 211 | + for (OrderSubscriptionMessageDb db : list) { | ||
| 212 | + assertThat(db.getOrderPeriodStartDate()).isNotNull(); | ||
| 213 | + assertThat(db.getOrderPeriodStartDate().isAfter(threshold) || db.getOrderPeriodStartDate().isEqual(threshold)) | ||
| 214 | + .as("开始时间应该 >= 2025-02-03 00:00:00,实际: %s", db.getOrderPeriodStartDate()) | ||
| 215 | + .isTrue(); | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + // 验证包含记录1(等于) | ||
| 219 | + assertThat(list.stream().anyMatch(db -> db.getId().equals(created1.getId()))).isTrue(); | ||
| 220 | + | ||
| 221 | + // 验证包含记录2(大于) | ||
| 222 | + assertThat(list.stream().anyMatch(db -> db.getId().equals(created2.getId()))).isTrue(); | ||
| 223 | + | ||
| 224 | + // 验证包含记录4(大于,同一天但时间更晚) | ||
| 225 | + assertThat(list.stream().anyMatch(db -> db.getId().equals(created4.getId()))).isTrue(); | ||
| 226 | + | ||
| 227 | + // 验证不包含记录3(小于) | ||
| 228 | + assertThat(list.stream().anyMatch(db -> db.getId().equals(created3.getId()))).isFalse(); | ||
| 229 | + | ||
| 230 | + // 测试2: 查询 orderPeriodStartDate >= 2025-02-10 00:00:00 | ||
| 231 | + // 应该只返回: 记录2(等于) | ||
| 232 | + GetUserOrderSubscriptionMessageHistoryRpcRequest historyReq2 = GetUserOrderSubscriptionMessageHistoryRpcRequest.newBuilder() | ||
| 233 | + .setEnterpriseId(1) | ||
| 234 | + .setOpenId(openId) | ||
| 235 | + .setOrderPeriodStartDate(1739116800000L) // 2025-02-10 00:00:00 | ||
| 236 | + .build(); | ||
| 237 | + | ||
| 238 | + List<OrderSubscriptionMessageDb> list2 = orderSubscriptionMessageDbService.getUserOrderSubscriptionMessageHistory(historyReq2); | ||
| 239 | + | ||
| 240 | + // 验证只返回记录2 | ||
| 241 | + assertThat(list2.size()).isEqualTo(1); | ||
| 242 | + assertThat(list2.get(0).getId()).isEqualTo(created2.getId()); | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + @Test | ||
| 246 | + void shouldBatchDeleteByIds() { | ||
| 247 | + final String openId = "openId_test_junit_delete"; | ||
| 248 | + | ||
| 249 | + // clean | ||
| 250 | + jdbcTemplate.update("DELETE FROM order_subscription_message WHERE enterpriseId=? AND openId=?", 1, openId); | ||
| 251 | + | ||
| 252 | + // create multiple records | ||
| 253 | + CreateOrderSubscriptionMessageRpcRequest createReq1 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 254 | + .setEnterpriseId(1) | ||
| 255 | + .setDinerId(100) | ||
| 256 | + .setOpenId(openId) | ||
| 257 | + .setTemplateId("template_test_junit_delete_1") | ||
| 258 | + .setJumpPath("/pages/order/index") | ||
| 259 | + .setOrderPeriodStartDate(1738512000000L) | ||
| 260 | + .setOrderPeriodEndDate(1739030400000L) | ||
| 261 | + .build(); | ||
| 262 | + | ||
| 263 | + CreateOrderSubscriptionMessageRpcRequest createReq2 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 264 | + .setEnterpriseId(1) | ||
| 265 | + .setDinerId(100) | ||
| 266 | + .setOpenId(openId) | ||
| 267 | + .setTemplateId("template_test_junit_delete_2") | ||
| 268 | + .setJumpPath("/pages/order/index") | ||
| 269 | + .setOrderPeriodStartDate(1738512000000L) | ||
| 270 | + .setOrderPeriodEndDate(1739030400000L) | ||
| 271 | + .build(); | ||
| 272 | + | ||
| 273 | + OrderSubscriptionMessageDb created1 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq1); | ||
| 274 | + OrderSubscriptionMessageDb created2 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq2); | ||
| 275 | + List<Long> ids = Arrays.asList(created1.getId(), created2.getId()); | ||
| 276 | + | ||
| 277 | + // verify records exist | ||
| 278 | + List<OrderSubscriptionMessageDb> beforeDelete = orderSubscriptionMessageDbService.getUserOrderSubscriptionMessageHistory( | ||
| 279 | + GetUserOrderSubscriptionMessageHistoryRpcRequest.newBuilder() | ||
| 280 | + .setEnterpriseId(1) | ||
| 281 | + .setOpenId(openId) | ||
| 282 | + .setOrderPeriodStartDate(0) | ||
| 283 | + .build()); | ||
| 284 | + assertThat(beforeDelete.size()).isGreaterThanOrEqualTo(2); | ||
| 285 | + | ||
| 286 | + // batch delete (逻辑删除) | ||
| 287 | + int affectedRows = orderSubscriptionMessageDbService.batchDeleteByIds(ids, 1); | ||
| 288 | + assertThat(affectedRows).isEqualTo(2); | ||
| 289 | + | ||
| 290 | + // verify deleted - 逻辑删除后查询不到 | ||
| 291 | + List<OrderSubscriptionMessageDb> afterDelete = orderSubscriptionMessageDbService.getUserOrderSubscriptionMessageHistory( | ||
| 292 | + GetUserOrderSubscriptionMessageHistoryRpcRequest.newBuilder() | ||
| 293 | + .setEnterpriseId(1) | ||
| 294 | + .setOpenId(openId) | ||
| 295 | + .setOrderPeriodStartDate(0) | ||
| 296 | + .build()); | ||
| 297 | + assertThat(afterDelete.size()).isLessThan(beforeDelete.size()); | ||
| 298 | + | ||
| 299 | + // verify deleted - 但数据库里还存在(isDeleted=1) | ||
| 300 | + Integer count = jdbcTemplate.queryForObject( | ||
| 301 | + "SELECT COUNT(1) FROM order_subscription_message WHERE id IN (?,?) AND isDeleted=1", | ||
| 302 | + Integer.class, | ||
| 303 | + created1.getId(), created2.getId()); | ||
| 304 | + assertThat(count).isEqualTo(2); | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + @Test | ||
| 308 | + void shouldBatchPhysicalDeleteByIds() { | ||
| 309 | + final String openId = "openId_test_junit_physical_delete"; | ||
| 310 | + | ||
| 311 | + // clean | ||
| 312 | + jdbcTemplate.update("DELETE FROM order_subscription_message WHERE enterpriseId=? AND openId=?", 1, openId); | ||
| 313 | + | ||
| 314 | + // create multiple records | ||
| 315 | + CreateOrderSubscriptionMessageRpcRequest createReq1 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 316 | + .setEnterpriseId(1) | ||
| 317 | + .setDinerId(100) | ||
| 318 | + .setOpenId(openId) | ||
| 319 | + .setTemplateId("template_test_junit_physical_delete_1") | ||
| 320 | + .setJumpPath("/pages/order/index") | ||
| 321 | + .setOrderPeriodStartDate(1738512000000L) | ||
| 322 | + .setOrderPeriodEndDate(1739030400000L) | ||
| 323 | + .build(); | ||
| 324 | + | ||
| 325 | + CreateOrderSubscriptionMessageRpcRequest createReq2 = CreateOrderSubscriptionMessageRpcRequest.newBuilder() | ||
| 326 | + .setEnterpriseId(1) | ||
| 327 | + .setDinerId(100) | ||
| 328 | + .setOpenId(openId) | ||
| 329 | + .setTemplateId("template_test_junit_physical_delete_2") | ||
| 330 | + .setJumpPath("/pages/order/index") | ||
| 331 | + .setOrderPeriodStartDate(1738512000000L) | ||
| 332 | + .setOrderPeriodEndDate(1739030400000L) | ||
| 333 | + .build(); | ||
| 334 | + | ||
| 335 | + OrderSubscriptionMessageDb created1 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq1); | ||
| 336 | + OrderSubscriptionMessageDb created2 = orderSubscriptionMessageDbService.createOrderSubscriptionMessage(createReq2); | ||
| 337 | + List<Long> ids = Arrays.asList(created1.getId(), created2.getId()); | ||
| 338 | + | ||
| 339 | + // batch physical delete | ||
| 340 | + int affectedRows = orderSubscriptionMessageDbService.batchPhysicalDeleteByIds(ids, 1); | ||
| 341 | + assertThat(affectedRows).isEqualTo(2); | ||
| 342 | + | ||
| 343 | + // verify deleted - 物理删除后数据库里也不存在 | ||
| 344 | + Integer count = jdbcTemplate.queryForObject( | ||
| 345 | + "SELECT COUNT(1) FROM order_subscription_message WHERE id IN (?,?)", | ||
| 346 | + Integer.class, | ||
| 347 | + created1.getId(), created2.getId()); | ||
| 348 | + assertThat(count).isEqualTo(0); | ||
| 349 | + } | ||
| 350 | +} | ||
| 351 | + |
-
Please register or login to post a comment