-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Feat/offline msg #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/offline msg #170
Conversation
This reverts commit 5f868b9.
…solve possible data inconsistency problems
…en redis and mysql by locking
…en redis and mysql by locking
…neMsg # Conflicts: # cim-server/src/main/resources/application.yaml
…s to use project version
…ve error logging and add offline msg test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Adds support for offline message storage and retrieval across Redis and MySQL, updates client‐side logic to fetch and deliver those messages, and integrates necessary configuration and proto changes.
- Introduces
OfflineModelconstants and conditional Spring beans for Redis/MySQL storage. - Extends client implementation with a scheduled task to pull offline messages after connection.
- Updates the protocol definition, error codes, tests, and build configuration for offline messaging.
Reviewed Changes
Copilot reviewed 65 out of 65 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| Constant.java | Added OfflineModel inner class for storage modes |
| OfflineMsgStoreConfig.java | Configures Redis/MySQL beans based on offline.store.mode |
| BeanConfig.java | Added SnowflakeIdWorker bean (plus unused imports) |
| RouteApplication.java | Excluded DataSource auto-config and adjusted component scan |
| pom.xml | Added persistence and Testcontainers dependencies |
| cim-common/src/main/proto/cim.proto | Added OFFLINE to BaseCommand enum |
| ClientImpl.java | Instantiates RingBufferWheel for offline fetch |
| RouteManager.java | Added fetchOfflineMsgs stub |
| OfflineMsgTest.java | New end-to-end offline messaging test |
| README.md | Simplified Maven build instructions |
Comments suppressed due to low confidence (4)
README.md:128
- [nitpick] The consolidated Maven command may not build all modules in multi-module setups; verify or split into explicit module build steps for clarity.
mvn clean install -DskipTests=true
cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java:32
- [nitpick] Add JavaDoc for
FETCH_OFFLINE_MSG_LIMIT(and related offline status constants) to explain their purpose, valid range, and usage context, or consider grouping them into an enum.
public static final Integer FETCH_OFFLINE_MSG_LIMIT = 100 ;
cim-forward-route/src/main/java/com/crossoverjie/cim/route/constant/Constant.java:29
- [nitpick] Consider renaming
OfflineModeltoOfflineStoreModeorOfflineStorageTypeto clarify that these constants represent storage backends.
public final static class OfflineModel {
cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java:103
- The RingBufferWheel is instantiated and tasks are added but never started; add a call to
ringBufferWheel.start()so the offline fetch job actually runs.
ringBufferWheel = new RingBufferWheel(Executors.newFixedThreadPool(1));
cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/RouteManager.java
Show resolved
Hide resolved
cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java
Outdated
Show resolved
Hide resolved
WalkthroughThis update introduces a comprehensive offline messaging feature to the system. It adds support for offline message storage using both MySQL and Redis, with new persistence modules, configuration options, and database schemas. The client and route layers are extended to fetch, store, and deliver offline messages, and integration tests are provided for both storage modes. Supporting changes include protocol updates, new constants, and enhancements to configuration and test infrastructure. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant RouteManager
participant RouteApi
participant RouteController
participant OfflineMsgService
participant OfflineMsgStore (MySQL/Redis)
participant ServerApi
participant Server
%% User goes offline and message is sent
Client->>RouteManager: Send P2P message
RouteManager->>RouteApi: p2pRoute(P2PReqVO)
RouteApi->>RouteController: p2pRoute
RouteController->>OfflineMsgService: saveOfflineMsg(P2PReqVO)
OfflineMsgService->>OfflineMsgStore: save(OfflineMsg)
OfflineMsgStore-->>OfflineMsgService: Ack
OfflineMsgService-->>RouteController: Ack
RouteController-->>RouteManager: User offline, message saved
%% User comes online and fetches offline messages
Client->>RouteManager: fetchOfflineMsgs(userId)
RouteManager->>RouteApi: fetchOfflineMsgs(OfflineMsgReqVO)
RouteApi->>RouteController: fetchOfflineMsgs
RouteController->>OfflineMsgService: fetchOfflineMsgs(serverInfo, userId)
OfflineMsgService->>OfflineMsgStore: fetch(userId)
OfflineMsgStore-->>OfflineMsgService: List<OfflineMsg>
OfflineMsgService->>ServerApi: send batch messages to user
ServerApi->>Server: Deliver messages
OfflineMsgService->>OfflineMsgStore: markDelivered(userId, messageIds)
OfflineMsgStore-->>OfflineMsgService: Ack
OfflineMsgService-->>RouteController: Ack
RouteController-->>RouteManager: Success
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 27
♻️ Duplicate comments (9)
cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/RouteManager.java (1)
83-86:⚠️ Potential issueHandle the response payload for offline messages.
This implementation ignores the response from
fetchOfflineMsgs, which means fetched offline messages are not processed or delivered to the client.Implement proper response handling:
-public void fetchOfflineMsgs(Long userId){ +public void fetchOfflineMsgs(Long userId) { OfflineMsgReqVO offlineMsgReqVO = OfflineMsgReqVO.builder().receiveUserId(userId).build(); - routeApi.fetchOfflineMsgs(offlineMsgReqVO); + try { + BaseResponse<Set<String>> response = routeApi.fetchOfflineMsgs(offlineMsgReqVO); + if (response.getCode().equals(StatusEnum.SUCCESS.getCode())) { + event.info("Fetched offline messages: {}", response.getDataBody()); + // Process and deliver messages to client listener + } else { + event.warn("Failed to fetch offline messages: {}", response.getMessage()); + } + } catch (Exception e) { + event.error("Error fetching offline messages", e); + } }cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java (1)
139-142: Address the hardcoded workerId configuration in SnowflakeIdWorker.The bean definition is correct, but the underlying
SnowflakeIdWorkerhas a hardcodedworkerId = 1Lwhich could cause ID collisions in a clustered environment where multiple instances generate IDs simultaneously.Consider externalizing the workerId configuration:
@Bean public SnowflakeIdWorker snowflakeIdWorker() { - return new SnowflakeIdWorker(); + return new SnowflakeIdWorker(appConfiguration.getWorkerId()); }And update the
SnowflakeIdWorkerconstructor to accept a configurable workerId parameter.cim-forward-route/src/main/resources/application.yaml (1)
15-19: Remove hard-coded credentials even from commented sections.Even though the datasource configuration is commented out, it still contains hard-coded credentials that could be accidentally exposed or committed to version control.
Apply this diff to remove the sensitive information:
-# password: zcyzcy159162 +# password: ${DB_PASSWORD}cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java (2)
34-34: 🛠️ Refactor suggestionAddress the wildcard import as previously suggested.
This duplicates a previous review comment about avoiding wildcard imports. Please use specific imports instead.
-import java.util.concurrent.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit;
238-238:⚠️ Potential issueAdd null check before stopping ringBufferWheel.
This addresses a previous review comment about preventing potential NullPointerException.
- ringBufferWheel.stop(true); + if (ringBufferWheel != null) { + ringBufferWheel.stop(true); + ringBufferWheel = null; + }cim-forward-route/src/main/java/com/crossoverjie/cim/route/controller/RouteController.java (1)
119-121: Review exception flow after offline message saving.The current implementation saves the offline message but then immediately throws an exception. This could potentially cause issues with transaction rollback or downstream error handling, as noted in previous reviews.
Consider whether the exception should be thrown after successfully saving the offline message, or if a different response pattern would be more appropriate for offline scenarios.
cim-common/src/main/java/com/crossoverjie/cim/common/util/SnowflakeIdWorker.java (1)
8-46: Consider architectural placement based on past feedback.Based on past review feedback, basic components in the common package should not be handed over to Spring management directly, but should be managed at the application layer (route module).
cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/OfflineMsgServiceImpl.java (1)
46-63:⚠️ Potential issueAdd safeguards to prevent infinite loop and CPU overuse.
This while(true) loop can cause busy spinning if messages continuously arrive or if there are issues with the persistence layer.
Apply this diff to add safeguards:
+ private static final int MAX_ITERATIONS = 1000; + private static final long LOOP_DELAY_MS = 100; + @Override public void fetchOfflineMsgs(CIMServerResVO cimServerResVO, Long receiveUserId) { String url = "http://" + cimServerResVO.getIp() + ":" + cimServerResVO.getHttpPort(); - while (true) { + int iterationCount = 0; + while (iterationCount < MAX_ITERATIONS) { List<OfflineMsg> offlineMsgs = offlineMsgStore.fetch(receiveUserId); if (offlineMsgs == null || offlineMsgs.isEmpty()) { break; } + iterationCount++; // ... existing logic ... offlineMsgStore.markDelivered(receiveUserId, offlineMsgs.stream().map(OfflineMsg::getMessageId).toList()); + + try { + Thread.sleep(LOOP_DELAY_MS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } } }cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgMapper.xml (1)
61-68:⚠️ Potential issueAdd missing status filter for consistency.
This query is missing the
status=0condition that's present infetchOfflineMsgsWithCursor, leading to inconsistent behavior.Apply this diff to add the missing condition:
<select id="fetchOfflineMsgIdsWithCursor" resultType="java.lang.Long" parameterType="java.lang.Long"> SELECT message_id FROM offline_msg - WHERE receive_user_id = #{receiveUserId} + WHERE receive_user_id = #{receiveUserId} and status=0 AND message_id > (select last_message_id from offline_msg_last_send_record where receive_user_id = #{receiveUserId}) ORDER BY message_id ASC </select>
🧹 Nitpick comments (42)
cim-server/src/main/java/com/crossoverjie/cim/server/config/BeanConfig.java (1)
9-9: Remove unused import.The added
import com.fasterxml.jackson.databind.ObjectMapper;is not referenced in this class, resulting in an unused import. Either remove this line or add a corresponding@Beandefinition forObjectMapperif it’s required.cim-server/src/main/resources/application.yaml (1)
35-36: Excessive blank lines
YAMLlint flags two consecutive blank lines here. Please collapse them to at most one blank line to satisfy lint rules and maintain readability.🧰 Tools
🪛 YAMLlint (1.37.1)
[warning] 36-36: too many blank lines
(2 > 0) (empty-lines)
docker/wait-for-it.sh (3)
36-40: Quote host/port expansions to avoid word-splitting.Unquoted
$WAITFORIT_HOST/$WAITFORIT_PORTcould break on values with spaces or special chars. Wrap them in quotes:- nc -z $WAITFORIT_HOST $WAITFORIT_PORT + nc -z "$WAITFORIT_HOST" "$WAITFORIT_PORT" ... - (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + (echo -n > "/dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT") >/dev/null 2>&1
75-77: Robustly splithost:portwithout word-splitting pitfalls.Bash arrays for splitting can misbehave; use
IFSandreadinstead:- WAITFORIT_hostport=(${1//:/ }) - WAITFORIT_HOST=${WAITFORIT_hostport[0]} - WAITFORIT_PORT=${WAITFORIT_hostport[1]} + IFS=: read -r WAITFORIT_HOST WAITFORIT_PORT <<< "$1"🧰 Tools
🪛 Shellcheck (0.10.0)
[warning] 75-75: Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
(SC2206)
174-174: Check for non-empty CLI array correctly.
[[ $WAITFORIT_CLI != "" ]]only inspects the first element. Use array length:- if [[ $WAITFORIT_CLI != "" ]]; then + if [[ ${#WAITFORIT_CLI[@]} -gt 0 ]]; then🧰 Tools
🪛 Shellcheck (0.10.0)
[warning] 174-174: Expanding an array without an index only gives the first element.
(SC2128)
docker/allin1-ubuntu.Dockerfile (1)
28-29: Optional: Install script to a standard bin directory.For consistency, consider copying to
/usr/local/binso it’s onPATHand not at root:-COPY wait-for-it.sh /wait-for-it.sh -RUN chmod +x /wait-for-it.sh +COPY wait-for-it.sh /usr/local/bin/wait-for-it.sh +RUN chmod +x /usr/local/bin/wait-for-it.shsql/offline_msg_last_send_record.sql (1)
1-6: Add default timestamp to updated_at Consider definingupdated_atwithNOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPso the column auto-populates and updates on record changes.sql/offline_msg.sql (1)
1-11: Define defaults and tighten constraints It’s advisable to addNOT NULLand default values, e.g.:status TINYINT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,Also evaluate if
contentorpropertiesshould useTEXTinstead ofVARCHAR(2000)for larger payloads.cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/AccountService.java (1)
6-6: Remove unused importP2PReqVOis imported but never referenced in this interface. Please remove it to avoid compiler warnings and improve clarity.sql/01schema.sql (1)
2-2: Consider adding IF NOT EXISTS for idempotency.The database creation with UTF8MB4 character set is appropriate for international support. However, consider adding
IF NOT EXISTSto make the script idempotent.-create database `cim-test` default character set utf8mb4 collate utf8mb4_general_ci; +create database if not exists `cim-test` default character set utf8mb4 collate utf8mb4_general_ci;cim-client/src/main/resources/application.yaml (1)
31-33: Review thread pool sizing.
Increasing the callback queue to 10 while reducing the pool size to 1 may create a processing bottleneck; confirm these settings meet your throughput and latency requirements.cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/res/OfflineMsgResVO.java (2)
3-7: Provide meaningful class-level JavaDoc.
The current Javadoc block is empty; consider adding a description of the VO’s purpose or responsibilities.
10-18: Reduce boilerplate and fix formatting.
Consider using Lombok annotations (e.g.,@Data) to generate getters/setters automatically and remove the extra space before the semicolon inprivate String msg ;.cim-server/src/main/java/com/crossoverjie/cim/server/server/CIMServer.java (1)
93-93: Good implementation of dynamic command support.This change successfully addresses the past review feedback by making the command dynamic based on the
SendMsgReqVO.cmdfield, enabling differentiation between regular and offline messages.Consider adding validation to ensure only valid commands are processed:
Request.Builder requestBuilder = Request.newBuilder() .setRequestId(sendMsgReqVO.getUserId()) .putAllProperties(sendMsgReqVO.getProperties()) + .setCmd(sendMsgReqVO.getCmd() != null ? sendMsgReqVO.getCmd() : BaseCommand.MESSAGE); - .setCmd(sendMsgReqVO.getCmd());cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/RouteApi.java (1)
7-7: Consider specific imports over wildcard import.While wildcard imports reduce clutter, they can hide unused imports and make dependencies less explicit.
cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/res/SaveOfflineMsgResVO.java (1)
1-15: Consider using Lombok for cleaner code.The class follows standard VO patterns but could benefit from modern Java practices for better maintainability.
Apply Lombok annotations to reduce boilerplate:
+import lombok.Data; + +@Data public class SaveOfflineMsgResVO { private String msg; - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } }cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/FetchOfflineMsgJob.java (1)
7-7: Consider externalizing the delay configuration.The hardcoded 5-second delay might not be suitable for all environments. Consider making this configurable through application properties.
-private static final int INITIAL_DELAY_SECONDS = 5; +private final int delaySeconds; -public FetchOfflineMsgJob(RouteManager routeManager, ClientConfigurationData conf) { +public FetchOfflineMsgJob(RouteManager routeManager, ClientConfigurationData conf, int delaySeconds) { this.routeManager = routeManager; this.conf = conf; - setKey(INITIAL_DELAY_SECONDS); + this.delaySeconds = delaySeconds; + setKey(delaySeconds); }cim-integration-test/src/test/resources/application-route.yaml (1)
16-20: Validate MySQL Testcontainer configuration and credentials handling.This hardcodes
spring.datasource.urltolocalhost:3306and expects${DB_PASSWORD}to be set in the environment. Ensure your CI/Dev environments or Testcontainers override these properties (host, port, credentials) correctly. Consider externalizing or templating the URL/credentials so tests can spin up MySQL containers dynamically.Would you like a helper script to inject Testcontainer‐driven properties in this YAML or examples for secure password management?
cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java (2)
31-33: Prefer primitive types for constants.Using
intinstead ofIntegeravoids unnecessary boxing/unboxing and clarifies immutability:-public static final Integer FETCH_OFFLINE_MSG_LIMIT = 100 ; +public static final int FETCH_OFFLINE_MSG_LIMIT = 100;
31-41: Add documentation and grouping for offline-message constants.The new constants lack Javadoc and are mixed in the root class. Consider extracting them into a nested
OfflineMsgorMsgTypeinner class with proper Javadoc, e.g.:public static class OfflineMsg { /** Maximum number of messages fetched per request. */ public static final int FETCH_LIMIT = 100; /** Pending offline message status. */ public static final int STATUS_PENDING = 0; // ... }cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/service/OfflineMsgStore.java (1)
13-37: Well-designed interface with room for improvement.The
OfflineMsgStoreinterface provides a clean abstraction for offline message operations. The three core methods (save, fetch, markDelivered) cover the essential functionality needed.However, consider these improvements:
- Enhanced Javadoc: Add parameter and return value descriptions:
/** * Save offline message * - * @param offlineMsg + * @param offlineMsg the offline message to save */ void save(OfflineMsg offlineMsg); /** * Fetch offline messages for a user * - * @param userId - * @return + * @param userId the user ID to fetch messages for + * @return list of offline messages for the user */ List<OfflineMsg> fetch(Long userId);
- Consider pagination: The
fetchmethod might need pagination support for users with many offline messages to avoid memory issues and improve performance.cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/pojo/OfflineMsg.java (2)
24-25: Consider using enums for better type safety.The integer constants for
messageTypeandstatuscould be replaced with enums to provide better type safety and clearer API contracts.Example implementation:
public enum MessageType { TEXT(0), IMAGE(1); private final int value; MessageType(int value) { this.value = value; } public int getValue() { return value; } } public enum MessageStatus { PENDING(0), ACKED(1); private final int value; MessageStatus(int value) { this.value = value; } public int getValue() { return value; } }
28-29: Consider using English for all comments.For consistency with the rest of the codebase, consider using English for all comments.
Apply this diff:
- /** - * 消息来源存储在这里 - */ + /** + * Message source properties are stored here + */cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/OfflineMsgService.java (2)
11-16: Enhance JavaDoc with parameter descriptions.The JavaDoc could be more helpful by describing the parameters and their purposes.
Apply this diff:
/** * fetch offline messages - * @param cimServerResVO - * @param receiveUserId + * @param cimServerResVO the CIM server information where messages should be sent + * @param receiveUserId the ID of the user to fetch offline messages for */
18-22: Enhance JavaDoc with parameter description.Add parameter description for better API documentation.
Apply this diff:
/** * save offline message - * @param p2pRequest + * @param p2pRequest the peer-to-peer message request to save as offline message */cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java (1)
65-65: Initialize the field to prevent potential issues.Consider initializing the field to avoid potential null pointer issues during the construction phase.
-private RingBufferWheel ringBufferWheel; +private RingBufferWheel ringBufferWheel = null;cim-forward-route/src/test/resources/application.yaml (1)
67-67: Add missing newline at end of file.Static analysis correctly identified a missing newline character at the end of the file.
offline: store: mode: mysql +🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 67-67: no new line character at the end of file
(new-line-at-end-of-file)
cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/OfflineMsgStoreRouteBaseTest.java (1)
34-34: Reconsider container reuse policy for test isolation.The
withReuse(true)setting may cause issues in concurrent test execution or when tests need clean state. Container reuse can lead to test interdependencies and flaky tests.Consider removing reuse for better test isolation:
- .withReuse(true); + .withReuse(false);cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/mapper/OfflineMsgMapper.java (1)
11-15: Add comprehensive Javadoc documentation.The class lacks proper documentation describing its purpose, usage, and method behaviors. This is especially important for persistence layer interfaces that define the data access contract.
/** - * @author zhongcanyu - * @date 2025/5/9 - * @description + * MyBatis mapper interface for offline message persistence operations. + * Provides CRUD operations for managing offline messages in MySQL database. + * + * @author zhongcanyu + * @date 2025/5/9 */cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/OfflineMsgStoreConfig.java (1)
12-16: Add comprehensive class documentation.The configuration class lacks proper documentation explaining the conditional bean creation and property requirements.
/** - * @author zhongcanyu - * @date 2025/5/18 - * @description + * Configuration class for offline message storage implementations. + * Conditionally creates beans based on the 'offline.store.mode' property: + * - 'mysql': Creates OfflineMsgDb for MySQL-based storage + * - 'redis': Creates OfflineMsgBuffer for Redis-based storage + * + * @author zhongcanyu + * @date 2025/5/18 */cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/mapper/OfflineMsgLastSendRecordMapper.java (1)
6-10: Add proper Javadoc documentation.The class and methods lack meaningful documentation. Add descriptions explaining the purpose and behavior of each method.
/** - * @author zhongcanyu - * @date 2025/5/10 - * @description + * Mapper interface for tracking the last sent offline message ID per user. + * This helps determine which messages are new when fetching offline messages. + * + * @author zhongcanyu + * @date 2025/5/10 */cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/config/BeanConfig.java (2)
33-44: Replace Chinese comments with English for consistency.The codebase appears to use English comments throughout. Please translate the Chinese comments to maintain consistency.
- // 设置键的序列化器为字符串 + // Set key serializer to string redisTemplate.setKeySerializer(new StringRedisSerializer()); - // 设置值的序列化器为JSON,支持对象存储 + // Set value serializer to JSON for object storage support redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); - // 设置Hash键和值的序列化器(可选) + // Set hash key and value serializers (optional) redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); - // 初始化属性 + // Initialize properties redisTemplate.afterPropertiesSet();
50-51: Add documentation for the Jackson2HashMapper configuration.The comment about
@classinformation could be more explanatory for maintainability.- // false 表示不在 key 里加 @class 信息 + // false means do not include @class type information in hash keys + // This prevents Jackson from adding type metadata to Redis hash keys return new Jackson2HashMapper(objectMapper, false);cim-persistence/cim-persistence-redis/src/main/java/com/crossoverjie/cim/persistence/redis/OfflineMsgBuffer.java (2)
73-78: Simplify lambda expression.The lambda can be simplified by removing unnecessary stream operations.
- messageIds.stream().forEach(messageId -> { + messageIds.forEach(messageId -> { String key = MSG_KEY + messageId; if (Boolean.TRUE.equals(redis.hasKey(key))) { redis.opsForHash().put(key, "status", OFFLINE_MSG_DELIVERED); } });
44-45: Address potential data consistency issues with dual storage approach.Using both hash storage for messages and list storage for user indexes can lead to consistency issues if one operation fails while the other succeeds.
Consider using Redis transactions or Lua scripts to ensure atomicity:
// Example using Redis transaction redis.multi(); redis.opsForHash().putAll(key, hashMap); redis.opsForList().rightPush(USER_IDX + msg.getReceiveUserId(), msg.getMessageId().toString()); redis.exec();cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/vo/req/SaveOfflineMsgReqVO.java (2)
15-21: Consider improving field naming consistency.The field names could be more consistent -
msguses abbreviation whilereceiveUserIdis fully spelled out. Consider using eithermessageandreceiveUserIdormsgandreceiverIdfor consistency.- @NotNull(message = "msg 不能为空") + @NotNull(message = "message 不能为空") @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "msg", example = "hello") - private String msg ; + private String message;
23-25: Remove redundant Lombok annotations.The
@Setterand@Getterannotations on thepropertiesfield are redundant since@Dataalready provides getters and setters for all fields.- @Setter - @Getter private Map<String, String> properties;cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/OfflineMsgTest.java (1)
51-54: Consider more robust state checking approach.The current approach captures the state once and then waits for it to become
Ready. However, the state is captured before the asynchronous connection process completes. Consider checking the state dynamically within the Awaitility assertion.- TimeUnit.SECONDS.sleep(3); - ClientState.State state = client1.getState(); Awaitility.await().atMost(10, TimeUnit.SECONDS) - .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, state)); + .untilAsserted(() -> Assertions.assertEquals(ClientState.State.Ready, client1.getState()));Also applies to: 70-73
cim-client-sdk/src/test/resources/init.sql (1)
2-27: Consider adding foreign key constraints and improving data types.The
offline_msgtable design is generally good but could benefit from a few improvements:
- Consider adding a foreign key constraint on
receive_user_idif there's a users table- The
propertiesfield using VARCHAR(2000) might be better as JSON type in MySQL 8.0+- Consider adding a composite index on
(receive_user_id, status)for efficient querying of pending messagesCREATE TABLE IF NOT EXISTS `offline_msg` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `message_id` BIGINT NOT NULL, `receive_user_id` BIGINT NOT NULL, `content` VARCHAR(2000), `message_type` INT, `status` TINYINT COMMENT '0: Pending, 1: Acked', `created_at` DATETIME, - `properties` VARCHAR(2000), + `properties` JSON, INDEX `idx_receive_user_id` (`receive_user_id`), + INDEX `idx_receive_user_status` (`receive_user_id`, `status`) );cim-forward-route/src/test/resources/init.sql (2)
2-27: Fix formatting inconsistencies and add missing engine specification.The
offline_msgtable definition has inconsistent formatting and lacks an explicit storage engine specification, unlike the second table.Apply this diff to improve consistency:
-CREATE TABLE IF NOT EXISTS `offline_msg` -( - `id` - BIGINT - PRIMARY - KEY - AUTO_INCREMENT, - `message_id` - BIGINT - NOT - NULL, - `receive_user_id` - BIGINT - NOT - NULL, - `content` - VARCHAR(2000), - `message_type` INT, - `status` TINYINT COMMENT '0: Pending, 1: Acked', - `created_at` DATETIME, - `properties` VARCHAR(2000), - INDEX `idx_receive_user_id` - ( - `receive_user_id` - ) -); +CREATE TABLE IF NOT EXISTS `offline_msg` +( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT, + `message_id` BIGINT NOT NULL, + `receive_user_id` BIGINT NOT NULL, + `content` VARCHAR(2000), + `message_type` INT, + `status` TINYINT COMMENT '0: Pending, 1: Acked', + `created_at` DATETIME, + `properties` VARCHAR(2000), + INDEX `idx_receive_user_id` (`receive_user_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4;
17-22: Consider adding NOT NULL constraints and default values.The schema could benefit from more explicit constraints to ensure data integrity.
Consider these improvements:
- `content` VARCHAR(2000), - `message_type` INT, - `status` TINYINT COMMENT '0: Pending, 1: Acked', - `created_at` DATETIME, - `properties` VARCHAR(2000), + `content` VARCHAR(2000) NOT NULL, + `message_type` INT NOT NULL DEFAULT 0, + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0: Pending, 1: Acked', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `properties` VARCHAR(2000),cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java (1)
22-22: Consider thread safety and configurability of ObjectMapper.The static ObjectMapper instance may need configuration for specific JSON handling requirements and thread safety considerations.
Consider making the ObjectMapper configurable:
- private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final ObjectMapper objectMapper = createObjectMapper(); + + private static ObjectMapper createObjectMapper() { + ObjectMapper mapper = new ObjectMapper(); + // Add any specific configuration here + return mapper; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (68)
README.md(1 hunks)cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/FetchOfflineMsgJob.java(1 hunks)cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/RouteManager.java(2 hunks)cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/impl/ClientImpl.java(5 hunks)cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandle.java(1 hunks)cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/ClientTest.java(7 hunks)cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/OfflineMsgTest.java(1 hunks)cim-client-sdk/src/test/resources/application-route.yaml(2 hunks)cim-client-sdk/src/test/resources/init.sql(1 hunks)cim-client/src/main/resources/application.yaml(1 hunks)cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java(1 hunks)cim-common/src/main/java/com/crossoverjie/cim/common/enums/StatusEnum.java(1 hunks)cim-common/src/main/java/com/crossoverjie/cim/common/util/RandomUtil.java(0 hunks)cim-common/src/main/java/com/crossoverjie/cim/common/util/SnowflakeIdWorker.java(1 hunks)cim-common/src/main/proto/cim.proto(1 hunks)cim-forward-route/pom.xml(2 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java(1 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java(2 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/MySqlPersistenceConfig.java(1 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/OfflineMsgStoreConfig.java(1 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/constant/Constant.java(1 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/controller/RouteController.java(4 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/factory/OfflineMsgFactory.java(1 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/AccountService.java(2 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/OfflineMsgService.java(1 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java(2 hunks)cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/OfflineMsgServiceImpl.java(1 hunks)cim-forward-route/src/main/resources/application.yaml(2 hunks)cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AbstractBaseTest.java(3 hunks)cim-forward-route/src/test/resources/application.yaml(2 hunks)cim-forward-route/src/test/resources/init.sql(1 hunks)cim-integration-test/pom.xml(1 hunks)cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java(1 hunks)cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/OfflineMsgStoreRouteBaseTest.java(1 hunks)cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/server/AbstractServerBaseTest.java(4 hunks)cim-integration-test/src/test/resources/application-route.yaml(1 hunks)cim-persistence/cim-persistence-api/pom.xml(1 hunks)cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/config/BeanConfig.java(1 hunks)cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/pojo/OfflineMsg.java(1 hunks)cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/pojo/OfflineMsgLastSendRecord.java(1 hunks)cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/service/OfflineMsgStore.java(1 hunks)cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/vo/req/SaveOfflineMsgReqVO.java(1 hunks)cim-persistence/cim-persistence-mysql/pom.xml(1 hunks)cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/config/MyBatisConfig.java(1 hunks)cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/OfflineMsgDb.java(1 hunks)cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/mapper/OfflineMsgLastSendRecordMapper.java(1 hunks)cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/mapper/OfflineMsgMapper.java(1 hunks)cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java(1 hunks)cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgLastSendRecordMapper.xml(1 hunks)cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgMapper.xml(1 hunks)cim-persistence/cim-persistence-redis/pom.xml(1 hunks)cim-persistence/cim-persistence-redis/src/main/java/com/crossoverjie/cim/persistence/redis/OfflineMsgBuffer.java(1 hunks)cim-persistence/pom.xml(1 hunks)cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/RouteApi.java(2 hunks)cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/OfflineMsgReqVO.java(1 hunks)cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/req/SendMsgReqVO.java(4 hunks)cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/res/OfflineMsgResVO.java(1 hunks)cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/res/SaveOfflineMsgResVO.java(1 hunks)cim-server/pom.xml(0 hunks)cim-server/src/main/java/com/crossoverjie/cim/server/config/BeanConfig.java(1 hunks)cim-server/src/main/java/com/crossoverjie/cim/server/server/CIMServer.java(1 hunks)cim-server/src/main/resources/application.yaml(2 hunks)docker/allin1-ubuntu.Dockerfile(1 hunks)docker/wait-for-it.sh(1 hunks)pom.xml(2 hunks)sql/01schema.sql(1 hunks)sql/offline_msg.sql(1 hunks)sql/offline_msg_last_send_record.sql(1 hunks)
💤 Files with no reviewable changes (2)
- cim-server/pom.xml
- cim-common/src/main/java/com/crossoverjie/cim/common/util/RandomUtil.java
🧰 Additional context used
🧬 Code Graph Analysis (12)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java (1)
cim-common/src/main/java/com/crossoverjie/cim/common/util/SnowflakeIdWorker.java (1)
SnowflakeIdWorker(8-46)
cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/OfflineMsgReqVO.java (1)
cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/LoginReqVO.java (1)
AllArgsConstructor(13-41)
cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/OfflineMsgStoreRouteBaseTest.java (1)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/constant/Constant.java (2)
Constant(10-42)OfflineStoreMode(29-39)
cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/pojo/OfflineMsgLastSendRecord.java (1)
cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/pojo/OfflineMsg.java (1)
Data(16-32)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/factory/OfflineMsgFactory.java (3)
cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java (2)
Constants(10-42)MetaKey(24-29)cim-common/src/main/java/com/crossoverjie/cim/common/exception/CIMException.java (1)
CIMException(13-71)cim-common/src/main/java/com/crossoverjie/cim/common/util/SnowflakeIdWorker.java (1)
SnowflakeIdWorker(8-46)
cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/ClientTest.java (1)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/constant/Constant.java (2)
Constant(10-42)OfflineStoreMode(29-39)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/MySqlPersistenceConfig.java (1)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/OfflineMsgStoreConfig.java (1)
Configuration(17-31)
cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/config/BeanConfig.java (2)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java (1)
Slf4j(13-29)cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/BeanConfig.java (1)
Configuration(39-143)
cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/OfflineMsgDb.java (1)
cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java (1)
Constants(10-42)
cim-persistence/cim-persistence-redis/src/main/java/com/crossoverjie/cim/persistence/redis/OfflineMsgBuffer.java (3)
cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java (1)
Slf4j(13-29)cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java (1)
Slf4j(43-180)cim-common/src/main/java/com/crossoverjie/cim/common/constant/Constants.java (1)
Constants(10-42)
cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/req/SendMsgReqVO.java (2)
cim-common/src/main/java/com/crossoverjie/cim/common/req/BaseRequest.java (1)
BaseRequest(12-51)cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/P2PReqVO.java (1)
Builder(20-89)
cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/OfflineMsgTest.java (3)
cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/OfflineMsgStoreRouteBaseTest.java (1)
OfflineMsgStoreRouteBaseTest(15-66)cim-forward-route/src/main/java/com/crossoverjie/cim/route/constant/Constant.java (2)
Constant(10-42)OfflineStoreMode(29-39)cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/ClientState.java (1)
ClientState(5-23)
🪛 YAMLlint (1.37.1)
cim-server/src/main/resources/application.yaml
[warning] 36-36: too many blank lines
(2 > 0) (empty-lines)
cim-forward-route/src/test/resources/application.yaml
[error] 67-67: no new line character at the end of file
(new-line-at-end-of-file)
🪛 Shellcheck (0.10.0)
docker/wait-for-it.sh
[warning] 61-61: Use single quotes, otherwise this expands now rather than when signalled.
(SC2064)
[warning] 75-75: Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
(SC2206)
[error] 153-153: This redirection overrides the output pipe. Use 'tee' to output to both.
(SC2260)
[warning] 174-174: Expanding an array without an index only gives the first element.
(SC2128)
🔇 Additional comments (62)
cim-server/src/main/resources/application.yaml (2)
18-18: No-op formatting change
Single blank line added to separate sections; this is harmless and improves readability.
34-34: Whitespace normalization
Trailing whitespace (if any) on thecim.heartbeat.timeline is removed, which is a harmless cleanup.docker/allin1-ubuntu.Dockerfile (1)
28-29: Use local wait-for-it script for deterministic builds.Replacing the remote
ADDwith a localCOPYensures the script version is controlled and the build is repeatable.cim-integration-test/pom.xml (1)
76-81: Ensure version management for Testcontainers modules The new MySQL Testcontainers dependency omits a<version>. Verify that a BOM or the parent POM managesorg.testcontainers:mysql; otherwise, explicitly specify a version to prevent resolution errors.cim-common/src/main/proto/cim.proto (1)
26-26: LGTM! Clean enum addition.The
OFFLINE = 3command addition follows protobuf best practices with sequential numbering and descriptive naming. This properly supports the offline messaging feature.cim-client-sdk/src/test/resources/application-route.yaml (1)
51-53:Details
✅ Verification successful
Configuration addition supports offline messaging feature well.
The
offline.store.mode: redisconfiguration properly enables backend selection for offline message storage. This aligns with the system's modular persistence approach.Verify configuration consistency across environments:
🏁 Script executed:
#!/bin/bash # Description: Check offline storage mode configuration across different environments # Find all application YAML files and check offline storage configuration fd "application.*\.ya?ml$" --type f -x echo "=== {} ===" \; -x grep -A 2 -B 2 "offline" {} # Check for any hardcoded storage mode references rg -i "offline.*store.*mode" --type javaLength of output: 3367
Configuration consistency verified across environments
Production defaults to
offline.store.mode: redis, and test configurations explicitly override this to exercise both Redis and MySQL backends. No further changes are needed.cim-client/src/main/resources/application.yaml (1)
26-27: Verify updated user identity.
Ensure the newidanduserNamevalues are intentional and align with your test or production environment settings, as they will affect authentication and offline message fetching.cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/AccountServiceRedisImpl.java (2)
7-7: Import ofBaseCommandadded.
The new import supports the additionalcmdparameter inSendMsgReqVOand aligns with updated request handling.
158-158: Set explicit command type inSendMsgReqVO.
UsingBaseCommand.MESSAGEas the final argument ensures the server correctly interprets the message command; this change is consistent with the updated VO signature.pom.xml (2)
42-42: Addcim-persistencemodule.
Confirm that the new module directory exists and is correctly configured in the reactor build.
201-206: Include Testcontainers MySQL dependency.
Verify that version1.19.7oforg.testcontainers:mysqlaligns with other Testcontainers modules and works with your integration tests.cim-forward-route/pom.xml (2)
26-42: Add offline persistence dependencies.
Thecim-persistence-api,cim-persistence-mysql, andcim-persistence-redisdependencies are added with the project version—this aligns the route module with the new persistence layer.
98-102: Validate Testcontainers MySQL dependency.
Ensure that including the MySQL container in this module’s test scope is necessary and consistent with the integration test strategy.cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/config/MyBatisConfig.java (1)
15-20:Details
✅ Verification successful
LGTM! Verify MapToJsonTypeHandler dependency.
The MyBatis configuration correctly registers the custom type handler using
ConfigurationCustomizer. This is the standard approach for integrating custom type handlers in MyBatis Spring Boot applications.Verify that the
MapToJsonTypeHandlerclass exists and is properly implemented:
🏁 Script executed:
#!/bin/bash # Check if MapToJsonTypeHandler exists and examine its implementation fd -t f "MapToJsonTypeHandler.java" | head -5 ast-grep --pattern 'class MapToJsonTypeHandler extends $_ { $$$ }'Length of output: 238
🏁 Script executed:
#!/bin/bash # Inspect MapToJsonTypeHandler class signature and imports rg -n "class MapToJsonTypeHandler" -A5 cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java # Show the first 50 lines to verify correct BaseTypeHandler usage sed -n '1,50p' cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.javaLength of output: 2310
LGTM – Custom Type Handler Verified
The
MapToJsonTypeHandlerclass exists at
cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java
and correctly extendsBaseTypeHandler<Map<String, String>>, serializing/deserializing via Jackson. The MyBatisConfigurationCustomizerregistration is appropriate and no further changes are needed.cim-forward-route/src/main/java/com/crossoverjie/cim/route/constant/Constant.java (1)
29-40: LGTM! Well-structured constant definitions.The nested static class provides good namespace organization for offline store mode constants. The implementation follows Java constant naming conventions and provides clear documentation.
cim-persistence/cim-persistence-redis/pom.xml (3)
5-9: Parent POM inheritance is correctly configured.
Child modules will inherit groupId/artifactId/version fromcim-persistence.
16-21: Redis persistence module correctly depends on the API.
Transitive inclusion ofspring-boot-starter-data-redisvia the API makes Redis client available here.
30-39: Spring Boot plugin configuration is appropriate.
Skipping Spring Boot packaging is correct for a library module.cim-persistence/cim-persistence-mysql/pom.xml (3)
5-9: Parent POM reference is correct.
Module will inherit version and group coordinates fromcim-persistence.
18-28: Dependencies are well-defined.
Includes MyBatis starter, MySQL Connector/J, and the API module.
38-46: Build plugin skip configuration is appropriate.
Skipping Spring Boot packaging for this library module is intentional.cim-persistence/cim-persistence-api/pom.xml (3)
5-9: Parent POM inheritance is correct.
Aligns this API module under thecim-persistenceaggregator.
18-21: Core Redis dependency is declared without version, leveraging Spring Boot’s dependency management.
This ensures compatibility with the parent’s Spring Boot BOM.
28-33: Spring Boot plugin skip is set correctly.
This is a library POM, skipping the Spring Boot repackage goal is desired.cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/ClientTest.java (3)
19-19: LGTM - Import added for offline store mode constants.The import is properly used throughout the test file to specify the Redis offline store mode.
39-39: Excellent consistency in offline store mode configuration.All test methods now consistently use
Constant.OfflineStoreMode.REDISwhen starting the route server, ensuring uniform test environment configuration across different test scenarios.Also applies to: 114-114, 245-245, 331-331, 411-411, 441-441
107-108: Good practice - Explicit client resource cleanup.The addition of explicit
client.close()calls after stopping servers ensures proper resource cleanup and prevents potential resource leaks in tests. This improves test isolation and reliability.Also applies to: 227-229, 324-325, 404-405
cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/server/AbstractServerBaseTest.java (2)
10-10: LGTM - Import added for DataSource exclusion.The import is properly used in the server startup arguments.
34-38: Appropriate DataSource auto-configuration exclusion for tests.Excluding
DataSourceAutoConfigurationduring server startup prevents unwanted database auto-configuration in integration tests, allowing for better control over test environment setup. The exclusion is consistently applied across all server startup scenarios.Also applies to: 55-56, 66-67
cim-integration-test/src/main/java/com/crossoverjie/cim/client/sdk/route/AbstractRouteBaseTest.java (2)
20-20: Appropriate visibility change for inheritance.Changing the
runfield from private to protected allows subclasses to access the ApplicationContext, which is reasonable for extensible test base classes.
21-21: Well-designed parameterization for offline storage modes.The modified
startRoute(String offlineModel)method enables flexible testing of different offline storage backends (Redis/MySQL) by accepting the mode as a parameter and properly passing it to the Spring application arguments.Also applies to: 28-28
cim-rout-api/src/main/java/com/crossoverjie/cim/route/api/vo/req/OfflineMsgReqVO.java (1)
19-25: LGTM! Clean and well-structured request VO.The implementation follows established patterns, uses appropriate validation, and has clear Swagger documentation. The single-purpose design is appropriate for fetching offline messages.
cim-forward-route/src/main/resources/application.yaml (1)
57-68: LGTM! Configuration properly supports the offline messaging feature.The MyBatis configuration enables proper ORM mapping, and the offline store mode configuration allows flexible switching between Redis and MySQL storage backends.
cim-persistence/pom.xml (1)
1-52: LGTM! Well-structured Maven module configuration.The POM follows Maven best practices with proper parent-child relationships, clear module separation (API, MySQL, Redis), and appropriate dependencies for a persistence layer.
cim-persistence/cim-persistence-api/src/main/java/com/crossoverjie/cim/persistence/api/pojo/OfflineMsg.java (1)
20-31: LGTM! Well-designed entity with good extensibility.The entity design appropriately captures all necessary offline message data with proper field types and extensible properties support.
cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/OfflineMsgService.java (1)
9-23: LGTM! Clean service interface design.The interface properly abstracts offline message operations with appropriate method signatures and clear responsibilities.
cim-forward-route/src/test/resources/application.yaml (2)
15-19: LGTM! Secure configuration practices.The datasource configuration follows security best practices by using an environment variable for the password and includes proper URL parameters for timezone and encoding.
56-63: LGTM! Standard MyBatis configuration.The MyBatis configuration is properly set up with camel case conversion and appropriate package scanning.
cim-common/src/main/java/com/crossoverjie/cim/common/enums/StatusEnum.java (1)
36-40: LGTM! Well-organized error codes for offline messaging.The new offline message error constants follow the existing patterns with logical error code grouping (92xx range) and descriptive messages.
cim-forward-route/src/main/java/com/crossoverjie/cim/route/RouteApplication.java (1)
14-18: LGTM! Proper configuration for conditional persistence.The exclusion of
DataSourceAutoConfigurationand explicit component scanning for persistence packages correctly supports the new offline message storage functionality with conditional MySQL/Redis configuration.cim-forward-route/src/main/java/com/crossoverjie/cim/route/config/MySqlPersistenceConfig.java (1)
14-20: LGTM! Configuration properly implements conditional MySQL persistence.The configuration correctly uses
@ConditionalOnPropertyto activate MySQL persistence only when needed, properly scans the mapper package, and imports the necessary auto-configuration.cim-forward-route/src/main/java/com/crossoverjie/cim/route/controller/RouteController.java (1)
63-67: LGTM! Clean dependency injection.The new service dependencies are properly injected using Spring's
@Resourceannotation, following the existing pattern in the controller.cim-forward-route/src/test/java/com/crossoverjie/cim/route/service/impl/AbstractBaseTest.java (4)
10-10: LGTM: Proper imports for MySQL test container support.The additional imports for
MySQLContainerandMountableFileare correctly added to support the new MySQL test infrastructure.Also applies to: 13-13
30-40: LGTM: Well-configured MySQL test container.The MySQL container configuration is comprehensive and follows best practices:
- Uses a specific, stable MySQL version (8.0.33)
- Properly configures database credentials
- Mounts the initialization script for schema setup
- Enables container reuse for performance
53-59: LGTM: Dynamic Spring datasource configuration.The dynamic system property setting for Spring datasource configuration is a clean approach for test integration. This allows the application to connect to the test MySQL container without hardcoded configuration.
66-66: LGTM: Proper container cleanup.The MySQL container is correctly stopped in the cleanup method, ensuring proper test resource management.
cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/OfflineMsgTest.java (1)
139-139: LGTM: Proper client cleanup verification.The assertion ensuring all clients are properly cleaned up from the client map is a good practice for preventing test interference.
cim-client-sdk/src/test/resources/init.sql (1)
28-34: LGTM: Well-designed tracking table.The
offline_msg_last_send_recordtable design is appropriate for tracking the last sent message per user. The primary key onreceive_user_idensures efficient lookups and prevents duplicate records.cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/OfflineMsgDb.java (2)
17-25: LGTM! Clean implementation following dependency injection pattern.The constructor injection approach and interface implementation are well-structured.
27-35: LGTM! Methods delegate appropriately to mappers.The save and fetch methods correctly delegate to the respective mappers with proper parameter passing.
cim-server-api/src/main/java/com/crossoverjie/cim/server/api/vo/req/SendMsgReqVO.java (5)
3-3: LGTM! Appropriate import for BaseCommand.The import correctly brings in the BaseCommand enum/class needed for the cmd field.
12-13: LGTM! Proper Lombok annotations added.The
@Builderand@AllArgsConstructorannotations provide convenient object construction patterns.
38-43: LGTM! Clean field definition with Lombok getter.The userId field now uses Lombok
@Getterannotation and the new cmd field follows the same pattern with proper schema annotation.
52-57: LGTM! Constructor updated to match new field.The constructor correctly includes the new cmd parameter and initializes the field appropriately.
74-74: LGTM! toString method includes new cmd field.The toString method is properly updated to include the cmd field in the output.
cim-forward-route/src/main/java/com/crossoverjie/cim/route/service/impl/OfflineMsgServiceImpl.java (3)
1-24: LGTM! Clean imports and modern Jakarta EE usage.The package structure and imports are well-organized, using modern Jakarta annotations.
25-39: LGTM! Proper Spring service structure with appropriate dependency injection.The class follows Spring best practices with clean dependency injection using @resource annotations.
69-86: LGTM! Well-structured method with proper null handling.The method follows good practices with Optional usage, factory pattern for entity creation, and appropriate metadata handling.
cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgMapper.xml (4)
1-4: LGTM! Standard MyBatis mapper structure.Proper XML header and namespace declaration for the MyBatis mapper.
5-43: LGTM! Efficient insert operations with proper type handling.Both single and batch insert operations are well-implemented with appropriate use of custom type handlers for JSON properties.
45-59: LGTM! Well-designed cursor-based pagination query.The query properly implements cursor-based pagination with appropriate filtering for undelivered messages (status=0) and handles null cursor values with COALESCE.
70-81: LGTM! Flexible update operation with conditional filtering.The status update operation is well-implemented with optional message ID filtering, allowing for both bulk and selective updates.
cim-client-sdk/src/main/java/com/crossoverjie/cim/client/sdk/io/CIMClientHandle.java
Show resolved
Hide resolved
...tence-api/src/main/java/com/crossoverjie/cim/persistence/api/vo/req/SaveOfflineMsgReqVO.java
Show resolved
Hide resolved
cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/OfflineMsgTest.java
Show resolved
Hide resolved
...ce-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java
Show resolved
Hide resolved
...ce-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java
Outdated
Show resolved
Hide resolved
...ence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/OfflineMsgDb.java
Show resolved
Hide resolved
…urce management in OfflineMsgTest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
cim-common/src/main/java/com/crossoverjie/cim/common/util/HttpClient.java (1)
46-48: 🛠️ Refactor suggestionApply consistent error reporting to the get method.
The
getmethod still uses the old generic error message format, which is inconsistent with the improved error reporting in thepostmethod. Apply the same enhancement for consistency.- if (!response.isSuccessful()) { - throw new IOException("Unexpected code " + response); - } + if (!response.isSuccessful()) { + throw new IOException("request url [" + url + "] failed, response code: " + response.code() + + ", message: " + response.message()); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/OfflineMsgTest.java(1 hunks)cim-common/src/main/java/com/crossoverjie/cim/common/util/HttpClient.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- cim-client-sdk/src/test/java/com/crossoverjie/cim/client/sdk/OfflineMsgTest.java
cim-common/src/main/java/com/crossoverjie/cim/common/util/HttpClient.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgLastSendRecordMapper.xml (1)
5-13: Verify unique index exists for optimal performance.The
INSERT...ON DUPLICATE KEY UPDATEstatement requires a unique index onreceive_user_idfor correct functionality and optimal performance.#!/bin/bash # Check for database schema or migration files that create the required index fd -e sql | xargs grep -l "offline_msg_last_send_record" | head -5
🧹 Nitpick comments (2)
cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/mapper/OfflineMsgLastSendRecordMapper.java (2)
9-10: Empty JavaDoc description should be completed or removed.The
@descriptiontag is empty. Either provide a meaningful description of this mapper's purpose or remove the empty tag./** * @author zhongcanyu * @date 2025/5/10 - * @description + * @description Mapper for managing offline message last send records to track message delivery status */
14-14: Add space after comma in parameter list for better readability.Missing space between parameters affects code readability.
- void saveLatestMessageId(@Param("receiveUserId") Long receiveUserId,@Param("lastMessageId") Long lastMessageId); + void saveLatestMessageId(@Param("receiveUserId") Long receiveUserId, @Param("lastMessageId") Long lastMessageId);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/mapper/OfflineMsgLastSendRecordMapper.java(1 hunks)cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java(1 hunks)cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgLastSendRecordMapper.xml(1 hunks)cim-persistence/cim-persistence-redis/src/main/java/com/crossoverjie/cim/persistence/redis/OfflineMsgBuffer.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/util/MapToJsonTypeHandler.java
- cim-persistence/cim-persistence-redis/src/main/java/com/crossoverjie/cim/persistence/redis/OfflineMsgBuffer.java
🔇 Additional comments (2)
cim-persistence/cim-persistence-mysql/src/main/java/com/crossoverjie/cim/persistence/mysql/offlinemsg/mapper/OfflineMsgLastSendRecordMapper.java (1)
12-16:Details
❌ Incorrect review comment
Consider consolidating with existing offline message mappers.
Given the previous question about whether this function can be deleted, evaluate if this separate mapper is necessary or if the functionality could be consolidated into the main
OfflineMsgMapperto reduce complexity.
🏁 Script executed:
#!/bin/bash # Check if there are other offline message mappers that could accommodate this functionality fd -e java -e xml | grep -i "offlinemsg.*mapper" | head -10Length of output: 521
🏁 Script executed:
#!/bin/bash # Show the contents of the LastSendRecord mapper XML sed -n '1,200p' cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgLastSendRecordMapper.xml echo "----" # Show the contents of the main OfflineMsg mapper XML sed -n '1,200p' cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgMapper.xmlLength of output: 3896
Separate mappers are intentional and should be kept as is
The two mappers target distinct tables—
offline_msgvs.offline_msg_last_send_record—and contain no overlapping SQL. Consolidating them would blur table-level responsibilities and reduce clarity. Keeping one mapper per table aligns with the Single Responsibility Principle.Likely an incorrect or invalid review comment.
cim-persistence/cim-persistence-mysql/src/main/resources/mapper/OfflineMsgLastSendRecordMapper.xml (1)
5-13: LGTM: Well-structured upsert operation.The INSERT...ON DUPLICATE KEY UPDATE implementation correctly handles both insert and update scenarios with proper timestamp management using NOW().
crossoverJie
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
Thank you for your contribution. |
Offline message -demo
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores