比较提交
2 次代码提交
0385b2010a
...
cf2013a52d
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
cf2013a52d | ||
|
|
dc1ada94ea |
10
.dockerignore
普通文件
10
.dockerignore
普通文件
@ -0,0 +1,10 @@
|
|||||||
|
.git
|
||||||
|
.idea
|
||||||
|
.gradle
|
||||||
|
**/target
|
||||||
|
**/build
|
||||||
|
**/dist
|
||||||
|
**/node_modules
|
||||||
|
**/.DS_Store
|
||||||
|
**/*.log
|
||||||
|
*.iml
|
||||||
@ -1 +1 @@
|
|||||||
17
|
21
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
ARG SERVICE_MODULE=tenant-service
|
ARG SERVICE_MODULE=tenant-service
|
||||||
|
|
||||||
FROM maven:3.9.9-eclipse-temurin-21 AS build
|
FROM maven:3.9.9-eclipse-temurin-21 AS build
|
||||||
@ -5,6 +6,7 @@ ARG SERVICE_MODULE
|
|||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
|
|
||||||
COPY pom.xml ./pom.xml
|
COPY pom.xml ./pom.xml
|
||||||
|
COPY maven-settings.xml ./maven-settings.xml
|
||||||
COPY common ./common
|
COPY common ./common
|
||||||
COPY im-sdk ./im-sdk
|
COPY im-sdk ./im-sdk
|
||||||
COPY tenant-service ./tenant-service
|
COPY tenant-service ./tenant-service
|
||||||
@ -14,7 +16,8 @@ COPY update-service ./update-service
|
|||||||
COPY demo-service ./demo-service
|
COPY demo-service ./demo-service
|
||||||
COPY file-service ./file-service
|
COPY file-service ./file-service
|
||||||
|
|
||||||
RUN mvn -pl ${SERVICE_MODULE} -am -DskipTests package
|
RUN --mount=type=cache,target=/root/.m2,sharing=locked \
|
||||||
|
mvn -s /workspace/maven-settings.xml -pl ${SERVICE_MODULE} -am -DskipTests package
|
||||||
|
|
||||||
FROM eclipse-temurin:21-jre-jammy
|
FROM eclipse-temurin:21-jre-jammy
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@ -15,6 +15,7 @@ pipeline {
|
|||||||
PROD_HOST = '106.54.23.149'
|
PROD_HOST = '106.54.23.149'
|
||||||
PROD_USER = 'ubuntu'
|
PROD_USER = 'ubuntu'
|
||||||
COMPOSE_FILE = '/opt/xuqm/deploy/compose.production.yaml'
|
COMPOSE_FILE = '/opt/xuqm/deploy/compose.production.yaml'
|
||||||
|
DOCKER_BUILDKIT = '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
@ -29,7 +30,8 @@ pipeline {
|
|||||||
def imageName = "${ACR_REGISTRY}/${ACR_NAMESPACE}/${params.SERVICE}:${params.IMAGE_TAG}"
|
def imageName = "${ACR_REGISTRY}/${ACR_NAMESPACE}/${params.SERVICE}:${params.IMAGE_TAG}"
|
||||||
bat """
|
bat """
|
||||||
docker login ${ACR_REGISTRY} -u ${ACR_USERNAME} -p %ACR_PASS%
|
docker login ${ACR_REGISTRY} -u ${ACR_USERNAME} -p %ACR_PASS%
|
||||||
docker build --build-arg SERVICE_MODULE=${params.SERVICE} -t ${imageName} .
|
docker pull ${imageName} || exit 0
|
||||||
|
docker build --build-arg SERVICE_MODULE=${params.SERVICE} --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ${imageName} -t ${imageName} .
|
||||||
docker push ${imageName}
|
docker push ${imageName}
|
||||||
docker rmi ${imageName}
|
docker rmi ${imageName}
|
||||||
"""
|
"""
|
||||||
|
|||||||
47
README.md
47
README.md
@ -138,15 +138,15 @@ cd update-service && mvn spring-boot:run &
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> 说明:SDK 和 demo 侧传入的 `appId` 实际按 `appKey` 解析。当前默认值是 `ak_demo_chat`,如果数据库里没有这条记录,tenant-service 会在启动时自动创建。
|
> 说明:SDK 和 demo 侧统一传 `appKey`。当前默认值是 `ak_demo_chat`,如果数据库里没有这条记录,tenant-service 会在启动时自动创建。
|
||||||
|
|
||||||
#### 功能服务(需 Token)
|
#### 功能服务(需 Token)
|
||||||
|
|
||||||
| 方法 | 路径 | 说明 |
|
| 方法 | 路径 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| GET | `/api/apps/{appId}/services` | 获取应用下所有功能服务 |
|
| GET | `/api/apps/{appKey}/services` | 获取应用下所有功能服务 |
|
||||||
| POST | `/api/apps/{appId}/services/toggle?platform=&serviceType=&enable=` | 开启/关闭功能服务 |
|
| POST | `/api/apps/{appKey}/services/toggle?platform=&serviceType=&enable=` | 开启/关闭功能服务 |
|
||||||
| POST | `/api/apps/{appId}/services/{id}/regenerate-key` | 重新生成 secretKey |
|
| POST | `/api/apps/{appKey}/services/{id}/regenerate-key` | 重新生成 secretKey |
|
||||||
|
|
||||||
**platform 枚举**:`ANDROID` / `IOS` / `HARMONY`
|
**platform 枚举**:`ANDROID` / `IOS` / `HARMONY`
|
||||||
**serviceType 枚举**:`IM` / `PUSH` / `UPDATE`
|
**serviceType 枚举**:`IM` / `PUSH` / `UPDATE`
|
||||||
@ -216,7 +216,7 @@ cd update-service && mvn spring-boot:run &
|
|||||||
|
|
||||||
```
|
```
|
||||||
POST /api/im/auth/login
|
POST /api/im/auth/login
|
||||||
?appId=ak_xxx
|
?appKey=ak_xxx
|
||||||
&userId=user_001
|
&userId=user_001
|
||||||
&nickname=张三 (可选,仅首次注册时存入外部系统)
|
&nickname=张三 (可选,仅首次注册时存入外部系统)
|
||||||
&avatar=https://... (可选)
|
&avatar=https://... (可选)
|
||||||
@ -230,9 +230,9 @@ POST /api/im/auth/login
|
|||||||
|
|
||||||
| 方法 | 路径 | 说明 |
|
| 方法 | 路径 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| POST | `/api/im/messages/send?appId=` | 发送消息 |
|
| POST | `/api/im/messages/send?appKey=` | 发送消息 |
|
||||||
| POST | `/api/im/messages/{id}/revoke?appId=` | 撤回消息 |
|
| POST | `/api/im/messages/{id}/revoke?appKey=` | 撤回消息 |
|
||||||
| GET | `/api/im/messages/history/{toId}?appId=&page=&size=` | 查询历史消息 |
|
| GET | `/api/im/messages/history/{toId}?appKey=&page=&size=` | 查询历史消息 |
|
||||||
|
|
||||||
**发送消息请求体**
|
**发送消息请求体**
|
||||||
```json
|
```json
|
||||||
@ -276,7 +276,7 @@ ws://localhost:8082/ws/im?token=<im_jwt>
|
|||||||
{
|
{
|
||||||
"destination": "/app/chat.send",
|
"destination": "/app/chat.send",
|
||||||
"payload": {
|
"payload": {
|
||||||
"appId": "ak_xxx",
|
"appKey": "ak_xxx",
|
||||||
"toId": "user_002",
|
"toId": "user_002",
|
||||||
"chatType": "SINGLE",
|
"chatType": "SINGLE",
|
||||||
"msgType": "TEXT",
|
"msgType": "TEXT",
|
||||||
@ -310,7 +310,7 @@ Frame 格式:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"destination": "/app/chat.revoke",
|
"destination": "/app/chat.revoke",
|
||||||
"payload": { "appId": "ak_xxx", "messageId": "msg-uuid" }
|
"payload": { "appKey": "ak_xxx", "messageId": "msg-uuid" }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -332,7 +332,7 @@ Frame 格式:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"event": "message",
|
"event": "message",
|
||||||
"appId": "ak_xxx",
|
"appKey": "ak_xxx",
|
||||||
"message": { ...消息对象... }
|
"message": { ...消息对象... }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -345,11 +345,11 @@ Frame 格式:
|
|||||||
|
|
||||||
| 表名 | 说明 |
|
| 表名 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `push_device_token` | 设备推送 token(appId + userId + vendor 唯一) |
|
| `push_device_token` | 设备推送 token(appKey + userId + vendor 唯一) |
|
||||||
|
|
||||||
### 支持厂商
|
### 支持厂商
|
||||||
|
|
||||||
`HUAWEI` / `XIAOMI` / `OPPO` / `VIVO` / `HONOR` / `APNS`(iOS)/ `FCM`
|
`HUAWEI` / `XIAOMI` / `OPPO` / `VIVO` / `HONOR` / `APNS`(iOS)
|
||||||
|
|
||||||
### 接口
|
### 接口
|
||||||
|
|
||||||
@ -366,7 +366,7 @@ Frame 格式:
|
|||||||
**注册 token**
|
**注册 token**
|
||||||
```
|
```
|
||||||
POST /api/push/register
|
POST /api/push/register
|
||||||
?appId=ak_xxx
|
?appKey=ak_xxx
|
||||||
&userId=user_001
|
&userId=user_001
|
||||||
&vendor=HUAWEI
|
&vendor=HUAWEI
|
||||||
&token=device_push_token
|
&token=device_push_token
|
||||||
@ -375,7 +375,7 @@ POST /api/push/register
|
|||||||
**发送推送**
|
**发送推送**
|
||||||
```
|
```
|
||||||
POST /api/push/send
|
POST /api/push/send
|
||||||
?appId=ak_xxx
|
?appKey=ak_xxx
|
||||||
&userId=user_001
|
&userId=user_001
|
||||||
&title=新消息
|
&title=新消息
|
||||||
&body=张三: Hello!
|
&body=张三: Hello!
|
||||||
@ -385,7 +385,7 @@ POST /api/push/send
|
|||||||
**开关接收推送**
|
**开关接收推送**
|
||||||
```
|
```
|
||||||
POST /api/push/receive-push
|
POST /api/push/receive-push
|
||||||
?appId=ak_xxx
|
?appKey=ak_xxx
|
||||||
&userId=user_001
|
&userId=user_001
|
||||||
&enabled=false
|
&enabled=false
|
||||||
```
|
```
|
||||||
@ -393,7 +393,7 @@ POST /api/push/receive-push
|
|||||||
**内部通知**
|
**内部通知**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"appId": "ak_xxx",
|
"appKey": "ak_xxx",
|
||||||
"userIds": ["user_001", "user_002"],
|
"userIds": ["user_001", "user_002"],
|
||||||
"title": "群聊消息",
|
"title": "群聊消息",
|
||||||
"body": "张三: Hello!",
|
"body": "张三: Hello!",
|
||||||
@ -437,11 +437,12 @@ push:
|
|||||||
| POST | `/api/v1/updates/app/upload` | 上传版本(先传 file-service,再把 `apkUrl` 交给 update-service) |
|
| POST | `/api/v1/updates/app/upload` | 上传版本(先传 file-service,再把 `apkUrl` 交给 update-service) |
|
||||||
| POST | `/api/v1/updates/app/{id}/publish` | 发布版本 |
|
| POST | `/api/v1/updates/app/{id}/publish` | 发布版本 |
|
||||||
| GET | `/api/v1/updates/app/list` | 版本列表 |
|
| GET | `/api/v1/updates/app/list` | 版本列表 |
|
||||||
|
| POST | `/api/v1/updates/store/app/{id}/execute-submit` | 批量提交应用市场,过程会写入批次日志与逐市场状态 |
|
||||||
|
|
||||||
**检查更新**
|
**检查更新**
|
||||||
```
|
```
|
||||||
GET /api/v1/updates/app/check
|
GET /api/v1/updates/app/check
|
||||||
?appId=ak_xxx
|
?appKey=ak_xxx
|
||||||
&platform=ANDROID
|
&platform=ANDROID
|
||||||
¤tVersionCode=10
|
¤tVersionCode=10
|
||||||
```
|
```
|
||||||
@ -464,6 +465,8 @@ GET /api/v1/updates/app/check
|
|||||||
|
|
||||||
**platform 枚举**:`ANDROID` / `IOS` / `HARMONY`
|
**platform 枚举**:`ANDROID` / `IOS` / `HARMONY`
|
||||||
|
|
||||||
|
**市场提审状态**:`PENDING` / `SUBMITTING` / `UNDER_REVIEW` / `APPROVED` / `REJECTED`
|
||||||
|
|
||||||
**上传 APK(两段式)**
|
**上传 APK(两段式)**
|
||||||
```
|
```
|
||||||
POST /api/file/upload
|
POST /api/file/upload
|
||||||
@ -474,7 +477,7 @@ POST /api/file/upload
|
|||||||
|
|
||||||
```
|
```
|
||||||
POST /api/v1/updates/app/upload
|
POST /api/v1/updates/app/upload
|
||||||
appId=ak_xxx
|
appKey=ak_xxx
|
||||||
platform=ANDROID
|
platform=ANDROID
|
||||||
versionName=1.1.0
|
versionName=1.1.0
|
||||||
versionCode=11
|
versionCode=11
|
||||||
@ -494,7 +497,7 @@ POST /api/v1/updates/app/upload
|
|||||||
**检查 RN 更新**
|
**检查 RN 更新**
|
||||||
```
|
```
|
||||||
GET /api/v1/rn/update/check
|
GET /api/v1/rn/update/check
|
||||||
?appId=ak_xxx
|
?appKey=ak_xxx
|
||||||
&moduleId=main
|
&moduleId=main
|
||||||
&platform=android
|
&platform=android
|
||||||
¤tVersion=1.0.0
|
¤tVersion=1.0.0
|
||||||
@ -517,7 +520,7 @@ GET /api/v1/rn/update/check
|
|||||||
**上传 Bundle(multipart/form-data)**
|
**上传 Bundle(multipart/form-data)**
|
||||||
```
|
```
|
||||||
POST /api/v1/rn/upload
|
POST /api/v1/rn/upload
|
||||||
appId=ak_xxx
|
appKey=ak_xxx
|
||||||
moduleId=main
|
moduleId=main
|
||||||
platform=ANDROID
|
platform=ANDROID
|
||||||
version=1.1.0
|
version=1.1.0
|
||||||
@ -526,7 +529,7 @@ POST /api/v1/rn/upload
|
|||||||
bundle=<binary .bundle 文件>
|
bundle=<binary .bundle 文件>
|
||||||
```
|
```
|
||||||
|
|
||||||
服务端自动计算 MD5,存储至 `{upload-dir}/rn/{appId}/{platform}/{moduleId}/`。
|
服务端自动计算 MD5,存储至 `{upload-dir}/rn/{appKey}/{platform}/{moduleId}/`。
|
||||||
|
|
||||||
### 环境变量配置
|
### 环境变量配置
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import java.time.Instant;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "demo_user",
|
name = "demo_user",
|
||||||
uniqueConstraints = @UniqueConstraint(name = "uq_demo_user_appid_userid", columnNames = {"app_id", "user_id"})
|
uniqueConstraints = @UniqueConstraint(name = "uq_demo_user_appkey_userid", columnNames = {"app_key", "user_id"})
|
||||||
)
|
)
|
||||||
public class DemoUserEntity {
|
public class DemoUserEntity {
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ public class DemoUserEntity {
|
|||||||
@Column(name = "id", length = 36, nullable = false, updatable = false)
|
@Column(name = "id", length = 36, nullable = false, updatable = false)
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(name = "app_id", length = 64, nullable = false)
|
@Column(name = "app_key", length = 64, nullable = false)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(name = "user_id", length = 128, nullable = false)
|
@Column(name = "user_id", length = 128, nullable = false)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -43,8 +43,8 @@ public class DemoUserEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -10,14 +10,14 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface DemoUserRepository extends JpaRepository<DemoUserEntity, String> {
|
public interface DemoUserRepository extends JpaRepository<DemoUserEntity, String> {
|
||||||
|
|
||||||
Optional<DemoUserEntity> findByAppIdAndUserId(String appId, String userId);
|
Optional<DemoUserEntity> findByAppKeyAndUserId(String appKey, String userId);
|
||||||
|
|
||||||
boolean existsByAppIdAndUserId(String appId, String userId);
|
boolean existsByAppKeyAndUserId(String appKey, String userId);
|
||||||
|
|
||||||
@Query("SELECT u FROM DemoUserEntity u WHERE u.appId = :appId AND " +
|
@Query("SELECT u FROM DemoUserEntity u WHERE u.appKey = :appKey AND " +
|
||||||
"(LOWER(u.userId) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " +
|
"(LOWER(u.userId) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " +
|
||||||
"LOWER(u.nickname) LIKE LOWER(CONCAT('%', :keyword, '%')))")
|
"LOWER(u.nickname) LIKE LOWER(CONCAT('%', :keyword, '%')))")
|
||||||
List<DemoUserEntity> searchByKeyword(@Param("appId") String appId, @Param("keyword") String keyword);
|
List<DemoUserEntity> searchByKeyword(@Param("appKey") String appKey, @Param("keyword") String keyword);
|
||||||
|
|
||||||
List<DemoUserEntity> findAllByAppIdOrderByCreatedAtAsc(String appId);
|
List<DemoUserEntity> findAllByAppKeyOrderByCreatedAtAsc(String appKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,13 +58,13 @@ public class DemoAuthService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public AuthResult register(String appKey, String userId, String password, String nickname) {
|
public AuthResult register(String appKey, String userId, String password, String nickname) {
|
||||||
if (userRepository.existsByAppIdAndUserId(appKey, userId)) {
|
if (userRepository.existsByAppKeyAndUserId(appKey, userId)) {
|
||||||
throw new BusinessException(409, "User already exists: " + userId);
|
throw new BusinessException(409, "User already exists: " + userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
DemoUserEntity user = new DemoUserEntity();
|
DemoUserEntity user = new DemoUserEntity();
|
||||||
user.setId(UUID.randomUUID().toString());
|
user.setId(UUID.randomUUID().toString());
|
||||||
user.setAppId(appKey);
|
user.setAppKey(appKey);
|
||||||
user.setUserId(userId);
|
user.setUserId(userId);
|
||||||
user.setPasswordHash(passwordEncoder.encode(password));
|
user.setPasswordHash(passwordEncoder.encode(password));
|
||||||
user.setNickname(nickname != null ? nickname : userId);
|
user.setNickname(nickname != null ? nickname : userId);
|
||||||
@ -84,7 +84,7 @@ public class DemoAuthService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public AuthResult login(String appKey, String userId, String password) {
|
public AuthResult login(String appKey, String userId, String password) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
DemoUserEntity user = userRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(401, "Invalid credentials"));
|
.orElseThrow(() -> new BusinessException(401, "Invalid credentials"));
|
||||||
|
|
||||||
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
|
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
|
||||||
@ -103,7 +103,7 @@ public class DemoAuthService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void resetPassword(String appKey, String userId, String newPassword) {
|
public void resetPassword(String appKey, String userId, String newPassword) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
DemoUserEntity user = userRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found: " + userId));
|
.orElseThrow(() -> new BusinessException(404, "User not found: " + userId));
|
||||||
user.setPasswordHash(passwordEncoder.encode(newPassword));
|
user.setPasswordHash(passwordEncoder.encode(newPassword));
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
@ -161,7 +161,7 @@ public class DemoAuthService {
|
|||||||
|
|
||||||
private UserProfile toProfile(DemoUserEntity user) {
|
private UserProfile toProfile(DemoUserEntity user) {
|
||||||
return new UserProfile(
|
return new UserProfile(
|
||||||
user.getAppId(),
|
user.getAppKey(),
|
||||||
user.getUserId(),
|
user.getUserId(),
|
||||||
user.getNickname(),
|
user.getNickname(),
|
||||||
user.getAvatar(),
|
user.getAvatar(),
|
||||||
|
|||||||
@ -24,14 +24,14 @@ public class DemoUserService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public UserProfile getProfile(String appKey, String userId) {
|
public UserProfile getProfile(String appKey, String userId) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
DemoUserEntity user = userRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
||||||
return toProfile(user);
|
return toProfile(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public UserProfile updateProfile(String appKey, String userId, String nickname, String avatar, String gender) {
|
public UserProfile updateProfile(String appKey, String userId, String nickname, String avatar, String gender) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
DemoUserEntity user = userRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
||||||
|
|
||||||
if (nickname != null && !nickname.isBlank()) {
|
if (nickname != null && !nickname.isBlank()) {
|
||||||
@ -54,7 +54,7 @@ public class DemoUserService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void resetPassword(String appKey, String userId, String oldPassword, String newPassword) {
|
public void resetPassword(String appKey, String userId, String oldPassword, String newPassword) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
DemoUserEntity user = userRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
||||||
|
|
||||||
if (!passwordEncoder.matches(oldPassword, user.getPasswordHash())) {
|
if (!passwordEncoder.matches(oldPassword, user.getPasswordHash())) {
|
||||||
@ -81,7 +81,7 @@ public class DemoUserService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<UserProfile> listMembers(String appKey) {
|
public List<UserProfile> listMembers(String appKey) {
|
||||||
return userRepository.findAllByAppIdOrderByCreatedAtAsc(appKey)
|
return userRepository.findAllByAppKeyOrderByCreatedAtAsc(appKey)
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::toProfile)
|
.map(this::toProfile)
|
||||||
.toList();
|
.toList();
|
||||||
@ -89,7 +89,7 @@ public class DemoUserService {
|
|||||||
|
|
||||||
private UserProfile toProfile(DemoUserEntity user) {
|
private UserProfile toProfile(DemoUserEntity user) {
|
||||||
return new UserProfile(
|
return new UserProfile(
|
||||||
user.getAppId(),
|
user.getAppKey(),
|
||||||
user.getUserId(),
|
user.getUserId(),
|
||||||
user.getNickname(),
|
user.getNickname(),
|
||||||
user.getAvatar(),
|
user.getAvatar(),
|
||||||
|
|||||||
@ -20,14 +20,14 @@
|
|||||||
| 名称 | 含义 | 常见位置 |
|
| 名称 | 含义 | 常见位置 |
|
||||||
|------|------|----------|
|
|------|------|----------|
|
||||||
| `tenant appKey` | 租户平台应用唯一标识,`tenant-service` 的 `/api/apps/{appKey}`、`/api/apps/{appKey}/services` 使用它 | 租户平台、服务配置页 |
|
| `tenant appKey` | 租户平台应用唯一标识,`tenant-service` 的 `/api/apps/{appKey}`、`/api/apps/{appKey}/services` 使用它 | 租户平台、服务配置页 |
|
||||||
| `IM appKey` | IM 业务域作用域标识,`im-service` 的管理接口和消息接口虽然历史参数名仍叫 `appId`,但实际传的是这个值 | IM 管理页、IM HTTP 接口 |
|
| `IM appKey` | IM 业务域作用域标识,`im-service` 的管理接口和消息接口统一使用它 | IM 管理页、IM HTTP 接口 |
|
||||||
|
|
||||||
结论:
|
结论:
|
||||||
|
|
||||||
- `tenant-platform` 的“服务配置”只认租户 `appKey`
|
- `tenant-platform` 的“服务配置”只认租户 `appKey`
|
||||||
- `tenant-platform` 的“IM 管理”必须带 `appKey`
|
- `tenant-platform` 的“IM 管理”必须带 `appKey`
|
||||||
- `tenant-platform` 的“离线推送 / 版本管理”只需要一次开通,平台差异配置在各自的管理页里维护,不再按 Android / iOS / 鸿蒙分别申请开通
|
- `tenant-platform` 的“离线推送 / 版本管理”只需要一次开通,平台差异配置在各自的管理页里维护,不再按 Android / iOS / 鸿蒙分别申请开通
|
||||||
- `im-service` 代码里沿用旧参数名 `appId`,但这是历史命名,调用方传的是 `appKey`
|
- `im-service` 的对外协议统一使用 `appKey`
|
||||||
|
|
||||||
## 初始化管理员账号
|
## 初始化管理员账号
|
||||||
|
|
||||||
@ -184,6 +184,8 @@
|
|||||||
- RN bundle 建议打成 zip 后再上传,zip 内至少包含 `rn-manifest.json`、bundle 文件和资源文件;update-service 会优先从 manifest 自动读取 `moduleId`、`version` / `bundleVersion`、`minCommonVersion` 和 `packageName`。
|
- RN bundle 建议打成 zip 后再上传,zip 内至少包含 `rn-manifest.json`、bundle 文件和资源文件;update-service 会优先从 manifest 自动读取 `moduleId`、`version` / `bundleVersion`、`minCommonVersion` 和 `packageName`。
|
||||||
- 租户平台里的“发布配置”标签页保存灰度默认模式、成员目录同步回调和成员选择回调;当默认模式切到成员灰度时,至少要配置一个回调才允许保存,保存前也会做连通性校验。
|
- 租户平台里的“发布配置”标签页保存灰度默认模式、成员目录同步回调和成员选择回调;当默认模式切到成员灰度时,至少要配置一个回调才允许保存,保存前也会做连通性校验。
|
||||||
- 上下架、上传、发布、灰度、市场提交、商店配置变更都会写入 `update_operation_log`,可通过 `GET /api/v1/updates/ops/logs?appKey=...` 查询。
|
- 上下架、上传、发布、灰度、市场提交、商店配置变更都会写入 `update_operation_log`,可通过 `GET /api/v1/updates/ops/logs?appKey=...` 查询。
|
||||||
|
- 市场提交增加了批次级日志和逐市场阶段日志,常见 action 包括 `STORE_SUBMIT_REQUEST`、`STORE_SUBMIT_BATCH_START`、`STORE_SUBMIT_STORE_START`、`STORE_SUBMIT_STORE_STAGE`、`STORE_SUBMIT_STORE_SUCCESS`、`STORE_SUBMIT_STORE_FAILED`、`STORE_SUBMIT_BATCH_END`。
|
||||||
|
- `storeReviewStatus` 的单市场状态除了 `PENDING / UNDER_REVIEW / APPROVED / REJECTED` 之外,还会出现 `SUBMITTING`,表示当前已经进入提审流程但尚未返回最终结果;详情页会额外显示阶段、提交时间和批次 ID。
|
||||||
- 提交应用市场会真实调用已实现的厂商接口。小米、OPPO、vivo 和华为/荣耀当前支持服务端提交;App Store、Google Play、鸿蒙仍以跳转页和人工流程为主。
|
- 提交应用市场会真实调用已实现的厂商接口。小米、OPPO、vivo 和华为/荣耀当前支持服务端提交;App Store、Google Play、鸿蒙仍以跳转页和人工流程为主。
|
||||||
- 租户平台控制台新增 `GET /api/dashboard/stats`,返回当前租户的应用数、已开通服务数和子账号数,同时会写一条 `CONSOLE / DASHBOARD / VIEW_DASHBOARD` 操作日志。
|
- 租户平台控制台新增 `GET /api/dashboard/stats`,返回当前租户的应用数、已开通服务数和子账号数,同时会写一条 `CONSOLE / DASHBOARD / VIEW_DASHBOARD` 操作日志。
|
||||||
- 租户平台“操作日志”菜单现在集中查看租户平台与版本管理两类日志;版本管理日志继续按 `appKey` 查询,控制台访问日志则落在 `t_operation_log`。
|
- 租户平台“操作日志”菜单现在集中查看租户平台与版本管理两类日志;版本管理日志继续按 `appKey` 查询,控制台访问日志则落在 `t_operation_log`。
|
||||||
@ -343,7 +345,7 @@ IM 服务会在消息、好友和已读等事件发生后,以 `POST` 方式向
|
|||||||
请求头:
|
请求头:
|
||||||
|
|
||||||
- `Content-Type: application/json`
|
- `Content-Type: application/json`
|
||||||
- `X-App-Id`: 应用 `appId`
|
- `X-App-Key`: 应用 `appKey`
|
||||||
- `X-App-Timestamp`: 请求时间戳
|
- `X-App-Timestamp`: 请求时间戳
|
||||||
- `X-App-Nonce`: 随机串
|
- `X-App-Nonce`: 随机串
|
||||||
- `X-App-Signature`: 签名结果
|
- `X-App-Signature`: 签名结果
|
||||||
@ -351,7 +353,7 @@ IM 服务会在消息、好友和已读等事件发生后,以 `POST` 方式向
|
|||||||
签名规则:
|
签名规则:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(body))
|
HMAC-SHA256(appSecret, appKey + "\n" + timestamp + "\n" + nonce + "\n" + sha256(body))
|
||||||
```
|
```
|
||||||
|
|
||||||
统一请求体结构:
|
统一请求体结构:
|
||||||
@ -364,7 +366,7 @@ HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(b
|
|||||||
"requestTime": 1714360000000,
|
"requestTime": 1714360000000,
|
||||||
"payload": {},
|
"payload": {},
|
||||||
"signature": null,
|
"signature": null,
|
||||||
"appId": "ak_demo_chat"
|
"appKey": "ak_demo_chat"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -378,7 +380,7 @@ HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(b
|
|||||||
| `requestTime` | 毫秒时间戳 |
|
| `requestTime` | 毫秒时间戳 |
|
||||||
| `payload` | 事件数据,结构随事件变化 |
|
| `payload` | 事件数据,结构随事件变化 |
|
||||||
| `signature` | 预留字段,当前由请求头承载 |
|
| `signature` | 预留字段,当前由请求头承载 |
|
||||||
| `appId` | 回调所属应用 ID |
|
| `appKey` | 回调所属应用唯一标识 |
|
||||||
|
|
||||||
`payload` 会根据事件类型变化:
|
`payload` 会根据事件类型变化:
|
||||||
|
|
||||||
@ -403,7 +405,7 @@ HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(b
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"appId": "ak_demo_chat",
|
"appKey": "ak_demo_chat",
|
||||||
"readerId": "user_001",
|
"readerId": "user_001",
|
||||||
"peerId": "user_002",
|
"peerId": "user_002",
|
||||||
"groupId": null,
|
"groupId": null,
|
||||||
@ -417,7 +419,7 @@ HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(b
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"appId": "ak_demo_chat",
|
"appKey": "ak_demo_chat",
|
||||||
"requestId": "req_001",
|
"requestId": "req_001",
|
||||||
"fromUserId": "user_001",
|
"fromUserId": "user_001",
|
||||||
"toUserId": "user_002",
|
"toUserId": "user_002",
|
||||||
@ -431,7 +433,7 @@ HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(b
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"appId": "ak_demo_chat",
|
"appKey": "ak_demo_chat",
|
||||||
"requestId": "req_001",
|
"requestId": "req_001",
|
||||||
"groupId": "group_001",
|
"groupId": "group_001",
|
||||||
"groupName": "技术群",
|
"groupName": "技术群",
|
||||||
@ -446,7 +448,7 @@ HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(b
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"appId": "ak_demo_chat",
|
"appKey": "ak_demo_chat",
|
||||||
"id": "blk_001",
|
"id": "blk_001",
|
||||||
"userId": "user_001",
|
"userId": "user_001",
|
||||||
"blockedUserId": "user_002",
|
"blockedUserId": "user_002",
|
||||||
@ -469,14 +471,14 @@ HMAC-SHA256(appSecret, appId + "\n" + timestamp + "\n" + nonce + "\n" + sha256(b
|
|||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
|
||||||
export function verifyWebhook({
|
export function verifyWebhook({
|
||||||
appId,
|
appKey,
|
||||||
timestamp,
|
timestamp,
|
||||||
nonce,
|
nonce,
|
||||||
body,
|
body,
|
||||||
signature,
|
signature,
|
||||||
appSecret,
|
appSecret,
|
||||||
}: {
|
}: {
|
||||||
appId: string
|
appKey: string
|
||||||
timestamp: string
|
timestamp: string
|
||||||
nonce: string
|
nonce: string
|
||||||
body: string
|
body: string
|
||||||
@ -484,7 +486,7 @@ export function verifyWebhook({
|
|||||||
appSecret: string
|
appSecret: string
|
||||||
}) {
|
}) {
|
||||||
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex')
|
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex')
|
||||||
const payload = `${appId}\n${timestamp}\n${nonce}\n${bodyHash}`
|
const payload = `${appKey}\n${timestamp}\n${nonce}\n${bodyHash}`
|
||||||
const expected = crypto.createHmac('sha256', appSecret).update(payload, 'utf8').digest('hex')
|
const expected = crypto.createHmac('sha256', appSecret).update(payload, 'utf8').digest('hex')
|
||||||
return expected === signature
|
return expected === signature
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public class FriendController {
|
|||||||
public ResponseEntity<ApiResponse<List<String>>> listFriends(
|
public ResponseEntity<ApiResponse<List<String>>> listFriends(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appKey) {
|
@RequestParam String appKey) {
|
||||||
List<String> friendIds = friendRepository.findByAppIdAndUserId(appKey, userId)
|
List<String> friendIds = friendRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(ImFriendEntity::getFriendId)
|
.map(ImFriendEntity::getFriendId)
|
||||||
.toList();
|
.toList();
|
||||||
@ -68,8 +68,8 @@ public class FriendController {
|
|||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@PathVariable String friendId,
|
@PathVariable String friendId,
|
||||||
@RequestParam String appKey) {
|
@RequestParam String appKey) {
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, userId, friendId);
|
friendRepository.deleteByAppKeyAndUserIdAndFriendId(appKey, userId, friendId);
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
friendRepository.deleteByAppKeyAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(null));
|
return ResponseEntity.ok(ApiResponse.success(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +77,8 @@ public class FriendController {
|
|||||||
public ResponseEntity<ApiResponse<Void>> removeAllFriends(
|
public ResponseEntity<ApiResponse<Void>> removeAllFriends(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appKey) {
|
@RequestParam String appKey) {
|
||||||
friendRepository.deleteByAppIdAndUserId(appKey, userId);
|
friendRepository.deleteByAppKeyAndUserId(appKey, userId);
|
||||||
friendRepository.deleteByAppIdAndFriendId(appKey, userId);
|
friendRepository.deleteByAppKeyAndFriendId(appKey, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +91,8 @@ public class FriendController {
|
|||||||
if (friendId == null || friendId.isBlank() || userId.equals(friendId)) {
|
if (friendId == null || friendId.isBlank() || userId.equals(friendId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, userId, friendId);
|
friendRepository.deleteByAppKeyAndUserIdAndFriendId(appKey, userId, friendId);
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
friendRepository.deleteByAppKeyAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(null));
|
return ResponseEntity.ok(ApiResponse.success(null));
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ public class FriendController {
|
|||||||
@PathVariable String friendId,
|
@PathVariable String friendId,
|
||||||
@RequestParam String appKey,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String groupName) {
|
@RequestParam(required = false) String groupName) {
|
||||||
ImFriendEntity link = friendRepository.findByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
ImFriendEntity link = friendRepository.findByAppKeyAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
.orElseGet(() -> addFriendLink(appKey, userId, friendId));
|
.orElseGet(() -> addFriendLink(appKey, userId, friendId));
|
||||||
link.setFriendGroup(normalizeGroup(groupName));
|
link.setFriendGroup(normalizeGroup(groupName));
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRepository.save(link)));
|
return ResponseEntity.ok(ApiResponse.success(friendRepository.save(link)));
|
||||||
@ -113,7 +113,7 @@ public class FriendController {
|
|||||||
public ResponseEntity<ApiResponse<List<String>>> listFriendGroups(
|
public ResponseEntity<ApiResponse<List<String>>> listFriendGroups(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appKey) {
|
@RequestParam String appKey) {
|
||||||
List<String> groups = friendRepository.findByAppIdAndUserId(appKey, userId).stream()
|
List<String> groups = friendRepository.findByAppKeyAndUserId(appKey, userId).stream()
|
||||||
.map(ImFriendEntity::getFriendGroup)
|
.map(ImFriendEntity::getFriendGroup)
|
||||||
.filter(group -> group != null && !group.isBlank())
|
.filter(group -> group != null && !group.isBlank())
|
||||||
.distinct()
|
.distinct()
|
||||||
@ -128,7 +128,7 @@ public class FriendController {
|
|||||||
@RequestParam String appKey,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupName) {
|
@PathVariable String groupName) {
|
||||||
List<String> friendIds = friendRepository
|
List<String> friendIds = friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendGroup(appKey, userId, normalizeGroup(groupName))
|
.findByAppKeyAndUserIdAndFriendGroup(appKey, userId, normalizeGroup(groupName))
|
||||||
.stream()
|
.stream()
|
||||||
.map(ImFriendEntity::getFriendId)
|
.map(ImFriendEntity::getFriendId)
|
||||||
.toList();
|
.toList();
|
||||||
@ -137,19 +137,19 @@ public class FriendController {
|
|||||||
|
|
||||||
private ImFriendEntity addFriendLink(String appKey, String userId, String friendId) {
|
private ImFriendEntity addFriendLink(String appKey, String userId, String friendId) {
|
||||||
ImFriendEntity forward = friendRepository
|
ImFriendEntity forward = friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
.findByAppKeyAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImFriendEntity e = new ImFriendEntity();
|
ImFriendEntity e = new ImFriendEntity();
|
||||||
e.setAppId(appKey);
|
e.setAppKey(appKey);
|
||||||
e.setUserId(userId);
|
e.setUserId(userId);
|
||||||
e.setFriendId(friendId);
|
e.setFriendId(friendId);
|
||||||
return friendRepository.save(e);
|
return friendRepository.save(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
friendRepository.findByAppIdAndUserIdAndFriendId(appKey, friendId, userId)
|
friendRepository.findByAppKeyAndUserIdAndFriendId(appKey, friendId, userId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImFriendEntity e = new ImFriendEntity();
|
ImFriendEntity e = new ImFriendEntity();
|
||||||
e.setAppId(appKey);
|
e.setAppKey(appKey);
|
||||||
e.setUserId(friendId);
|
e.setUserId(friendId);
|
||||||
e.setFriendId(userId);
|
e.setFriendId(userId);
|
||||||
return friendRepository.save(e);
|
return friendRepository.save(e);
|
||||||
@ -172,8 +172,8 @@ public class FriendController {
|
|||||||
@RequestBody FriendCheckRequest req) {
|
@RequestBody FriendCheckRequest req) {
|
||||||
List<FriendCheckResult> results = new ArrayList<>();
|
List<FriendCheckResult> results = new ArrayList<>();
|
||||||
for (String friendId : req.friendIds() == null ? List.<String>of() : req.friendIds()) {
|
for (String friendId : req.friendIds() == null ? List.<String>of() : req.friendIds()) {
|
||||||
boolean isFriend = friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
boolean isFriend = friendRepository.existsByAppKeyAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
|| friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
|| friendRepository.existsByAppKeyAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
results.add(new FriendCheckResult(friendId, isFriend));
|
results.add(new FriendCheckResult(friendId, isFriend));
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(results));
|
return ResponseEntity.ok(ApiResponse.success(results));
|
||||||
|
|||||||
@ -109,7 +109,7 @@ public class ImAdminController {
|
|||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
accountRepository.findByAppId(appKey, PageRequest.of(page, size))));
|
accountRepository.findByAppKey(appKey, PageRequest.of(page, size))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ban or unban a user. */
|
/** Ban or unban a user. */
|
||||||
@ -119,7 +119,7 @@ public class ImAdminController {
|
|||||||
@PathVariable String userId,
|
@PathVariable String userId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody Map<String, String> body) {
|
@RequestBody Map<String, String> body) {
|
||||||
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appKey, userId)
|
ImAccountEntity account = accountRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
||||||
String status = body.get("status");
|
String status = body.get("status");
|
||||||
if (status == null || status.isBlank()) {
|
if (status == null || status.isBlank()) {
|
||||||
@ -152,7 +152,7 @@ public class ImAdminController {
|
|||||||
/** List all groups for the given appKey. */
|
/** List all groups for the given appKey. */
|
||||||
@GetMapping("/groups")
|
@GetMapping("/groups")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> listGroups(@RequestParam String appKey) {
|
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> listGroups(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupRepository.findByAppId(appKey)));
|
return ResponseEntity.ok(ApiResponse.success(groupRepository.findByAppKey(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Admin registers a new IM user (or returns existing). */
|
/** Admin registers a new IM user (or returns existing). */
|
||||||
@ -253,10 +253,10 @@ public class ImAdminController {
|
|||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> stats(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> stats(
|
||||||
@RequestParam String appKey,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
long totalMessages = messageRepository.countByAppId(appKey);
|
long totalMessages = messageRepository.countByAppKey(appKey);
|
||||||
long totalUsers = accountRepository.countByAppId(appKey);
|
long totalUsers = accountRepository.countByAppKey(appKey);
|
||||||
long totalGroups = groupRepository.countByAppId(appKey);
|
long totalGroups = groupRepository.countByAppKey(appKey);
|
||||||
long todayMessages = messageRepository.countTodayByAppId(appKey);
|
long todayMessages = messageRepository.countTodayByAppKey(appKey);
|
||||||
operationLogService.record(appKey, operatorId, "VIEW_STATS", "STATS", null, "summary");
|
operationLogService.record(appKey, operatorId, "VIEW_STATS", "STATS", null, "summary");
|
||||||
|
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
||||||
@ -551,11 +551,11 @@ public class ImAdminController {
|
|||||||
Page<WebhookDeliveryEntity> result;
|
Page<WebhookDeliveryEntity> result;
|
||||||
PageRequest pageable = PageRequest.of(page, size);
|
PageRequest pageable = PageRequest.of(page, size);
|
||||||
if (callbackEvent != null && !callbackEvent.isBlank()) {
|
if (callbackEvent != null && !callbackEvent.isBlank()) {
|
||||||
result = webhookDeliveryRepository.findByAppIdAndCallbackEvent(appKey, callbackEvent, pageable);
|
result = webhookDeliveryRepository.findByAppKeyAndCallbackEvent(appKey, callbackEvent, pageable);
|
||||||
} else if (success != null) {
|
} else if (success != null) {
|
||||||
result = webhookDeliveryRepository.findByAppIdAndSuccess(appKey, success, pageable);
|
result = webhookDeliveryRepository.findByAppKeyAndSuccess(appKey, success, pageable);
|
||||||
} else {
|
} else {
|
||||||
result = webhookDeliveryRepository.findByAppId(appKey, pageable);
|
result = webhookDeliveryRepository.findByAppKey(appKey, pageable);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
@ -569,9 +569,9 @@ public class ImAdminController {
|
|||||||
Page<WebhookAlertEntity> result;
|
Page<WebhookAlertEntity> result;
|
||||||
PageRequest pageable = PageRequest.of(page, size);
|
PageRequest pageable = PageRequest.of(page, size);
|
||||||
if (acknowledged != null) {
|
if (acknowledged != null) {
|
||||||
result = webhookAlertRepository.findByAppIdAndAcknowledged(appKey, acknowledged, pageable);
|
result = webhookAlertRepository.findByAppKeyAndAcknowledged(appKey, acknowledged, pageable);
|
||||||
} else {
|
} else {
|
||||||
result = webhookAlertRepository.findByAppId(appKey, pageable);
|
result = webhookAlertRepository.findByAppKey(appKey, pageable);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
@ -583,7 +583,7 @@ public class ImAdminController {
|
|||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
WebhookAlertEntity alert = webhookAlertRepository.findById(id)
|
WebhookAlertEntity alert = webhookAlertRepository.findById(id)
|
||||||
.orElseThrow(() -> new BusinessException(404, "告警不存在"));
|
.orElseThrow(() -> new BusinessException(404, "告警不存在"));
|
||||||
if (!alert.getAppId().equals(appKey)) {
|
if (!alert.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
alert.setAcknowledged(true);
|
alert.setAcknowledged(true);
|
||||||
@ -604,7 +604,7 @@ public class ImAdminController {
|
|||||||
health.put("enabled", webhook.isEnabled());
|
health.put("enabled", webhook.isEnabled());
|
||||||
health.put("consecutiveFailures", webhook.getConsecutiveFailures());
|
health.put("consecutiveFailures", webhook.getConsecutiveFailures());
|
||||||
health.put("lastFailureAt", webhook.getLastFailureAt());
|
health.put("lastFailureAt", webhook.getLastFailureAt());
|
||||||
long unacknowledgedAlerts = webhookAlertRepository.countUnacknowledgedByAppId(appKey);
|
long unacknowledgedAlerts = webhookAlertRepository.countUnacknowledgedByAppKey(appKey);
|
||||||
health.put("unacknowledgedAlerts", unacknowledgedAlerts);
|
health.put("unacknowledgedAlerts", unacknowledgedAlerts);
|
||||||
return ResponseEntity.ok(ApiResponse.success(health));
|
return ResponseEntity.ok(ApiResponse.success(health));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,10 +27,10 @@ public class StatisticsController {
|
|||||||
@RequestParam(required = false) Integer days) {
|
@RequestParam(required = false) Integer days) {
|
||||||
int d = days == null ? 7 : days;
|
int d = days == null ? 7 : days;
|
||||||
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
||||||
long total = messageRepository.countByAppIdAndCreatedAtAfter(appKey, since);
|
long total = messageRepository.countByAppKeyAndCreatedAtAfter(appKey, since);
|
||||||
long single = messageRepository.countByAppIdAndChatTypeAndCreatedAtAfter(
|
long single = messageRepository.countByAppKeyAndChatTypeAndCreatedAtAfter(
|
||||||
appKey, com.xuqm.im.entity.ImMessageEntity.ChatType.SINGLE, since);
|
appKey, com.xuqm.im.entity.ImMessageEntity.ChatType.SINGLE, since);
|
||||||
long group = messageRepository.countByAppIdAndChatTypeAndCreatedAtAfter(
|
long group = messageRepository.countByAppKeyAndChatTypeAndCreatedAtAfter(
|
||||||
appKey, com.xuqm.im.entity.ImMessageEntity.ChatType.GROUP, since);
|
appKey, com.xuqm.im.entity.ImMessageEntity.ChatType.GROUP, since);
|
||||||
return ApiResponse.success(Map.of(
|
return ApiResponse.success(Map.of(
|
||||||
"totalMessages", total,
|
"totalMessages", total,
|
||||||
@ -46,9 +46,9 @@ public class StatisticsController {
|
|||||||
@RequestParam(required = false) Integer days) {
|
@RequestParam(required = false) Integer days) {
|
||||||
int d = days == null ? 7 : days;
|
int d = days == null ? 7 : days;
|
||||||
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
||||||
long success = webhookDeliveryRepository.countSuccessfulByAppIdSince(appKey, since);
|
long success = webhookDeliveryRepository.countSuccessfulByAppKeySince(appKey, since);
|
||||||
long failed = webhookDeliveryRepository.countFailedByAppIdSince(appKey, since);
|
long failed = webhookDeliveryRepository.countFailedByAppKeySince(appKey, since);
|
||||||
List<Object[]> raw = webhookDeliveryRepository.statsByAppIdSince(appKey, since);
|
List<Object[]> raw = webhookDeliveryRepository.statsByAppKeySince(appKey, since);
|
||||||
List<Map<String, Object>> eventStats = new ArrayList<>();
|
List<Map<String, Object>> eventStats = new ArrayList<>();
|
||||||
for (Object[] row : raw) {
|
for (Object[] row : raw) {
|
||||||
eventStats.add(Map.of(
|
eventStats.add(Map.of(
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "im_account",
|
@Table(name = "im_account",
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"appId", "userId"}))
|
uniqueConstraints = @UniqueConstraint(columnNames = {"appKey", "userId"}))
|
||||||
public class ImAccountEntity {
|
public class ImAccountEntity {
|
||||||
|
|
||||||
public enum Gender { UNKNOWN, MALE, FEMALE }
|
public enum Gender { UNKNOWN, MALE, FEMALE }
|
||||||
@ -23,7 +23,7 @@ public class ImAccountEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -49,8 +49,8 @@ public class ImAccountEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -11,13 +11,13 @@ import java.time.LocalDateTime;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "im_blacklist",
|
name = "im_blacklist",
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"appId", "userId", "blockedUserId"}),
|
uniqueConstraints = @UniqueConstraint(columnNames = {"appKey", "userId", "blockedUserId"}),
|
||||||
indexes = @Index(name = "idx_blacklist_app_user", columnList = "appId,userId")
|
indexes = @Index(name = "idx_blacklist_app_user", columnList = "appKey,userId")
|
||||||
)
|
)
|
||||||
public class ImBlacklistEntity extends BaseIdEntity {
|
public class ImBlacklistEntity extends BaseIdEntity {
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -28,8 +28,8 @@ public class ImBlacklistEntity extends BaseIdEntity {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -10,16 +10,16 @@ import java.time.LocalDateTime;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "im_conversation_state",
|
name = "im_conversation_state",
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"appId", "userId", "targetId", "chatType"}),
|
uniqueConstraints = @UniqueConstraint(columnNames = {"appKey", "userId", "targetId", "chatType"}),
|
||||||
indexes = {
|
indexes = {
|
||||||
@Index(name = "idx_conv_state_app_user", columnList = "appId,userId"),
|
@Index(name = "idx_conv_state_app_user", columnList = "appKey,userId"),
|
||||||
@Index(name = "idx_conv_state_app_target", columnList = "appId,targetId")
|
@Index(name = "idx_conv_state_app_target", columnList = "appKey,targetId")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
public class ImConversationStateEntity extends BaseIdEntity {
|
public class ImConversationStateEntity extends BaseIdEntity {
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -53,8 +53,8 @@ public class ImConversationStateEntity extends BaseIdEntity {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import java.time.Instant;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "im_friends",
|
@Table(name = "im_friends",
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"appId", "userId", "friendId"}))
|
uniqueConstraints = @UniqueConstraint(columnNames = {"appKey", "userId", "friendId"}))
|
||||||
public class ImFriendEntity {
|
public class ImFriendEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -21,7 +21,7 @@ public class ImFriendEntity {
|
|||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -39,8 +39,8 @@ public class ImFriendEntity {
|
|||||||
public Long getId() { return id; }
|
public Long getId() { return id; }
|
||||||
public void setId(Long id) { this.id = id; }
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -13,15 +13,15 @@ import java.time.LocalDateTime;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "im_friend_request",
|
name = "im_friend_request",
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"appId", "fromUserId", "toUserId"}),
|
uniqueConstraints = @UniqueConstraint(columnNames = {"appKey", "fromUserId", "toUserId"}),
|
||||||
indexes = @Index(name = "idx_friend_request_app_to", columnList = "appId,toUserId")
|
indexes = @Index(name = "idx_friend_request_app_to", columnList = "appKey,toUserId")
|
||||||
)
|
)
|
||||||
public class ImFriendRequestEntity extends BaseIdEntity {
|
public class ImFriendRequestEntity extends BaseIdEntity {
|
||||||
|
|
||||||
public enum Status { PENDING, ACCEPTED, REJECTED }
|
public enum Status { PENDING, ACCEPTED, REJECTED }
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String fromUserId;
|
private String fromUserId;
|
||||||
@ -40,8 +40,8 @@ public class ImFriendRequestEntity extends BaseIdEntity {
|
|||||||
|
|
||||||
private LocalDateTime reviewedAt;
|
private LocalDateTime reviewedAt;
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getFromUserId() { return fromUserId; }
|
public String getFromUserId() { return fromUserId; }
|
||||||
public void setFromUserId(String fromUserId) { this.fromUserId = fromUserId; }
|
public void setFromUserId(String fromUserId) { this.fromUserId = fromUserId; }
|
||||||
|
|||||||
@ -17,7 +17,7 @@ public class ImGlobalMuteEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64, unique = true)
|
@Column(nullable = false, length = 64, unique = true)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
@ -33,8 +33,8 @@ public class ImGlobalMuteEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public boolean isEnabled() { return enabled; }
|
public boolean isEnabled() { return enabled; }
|
||||||
public void setEnabled(boolean enabled) { this.enabled = enabled; }
|
public void setEnabled(boolean enabled) { this.enabled = enabled; }
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public class ImGroupEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String name;
|
private String name;
|
||||||
@ -49,8 +49,8 @@ public class ImGroupEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
public void setName(String name) { this.name = name; }
|
public void setName(String name) { this.name = name; }
|
||||||
|
|||||||
@ -12,14 +12,14 @@ import java.time.LocalDateTime;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "im_group_join_request",
|
name = "im_group_join_request",
|
||||||
indexes = @Index(name = "idx_group_join_request_app_group", columnList = "appId,groupId")
|
indexes = @Index(name = "idx_group_join_request_app_group", columnList = "appKey,groupId")
|
||||||
)
|
)
|
||||||
public class ImGroupJoinRequestEntity extends BaseIdEntity {
|
public class ImGroupJoinRequestEntity extends BaseIdEntity {
|
||||||
|
|
||||||
public enum Status { PENDING, ACCEPTED, REJECTED }
|
public enum Status { PENDING, ACCEPTED, REJECTED }
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String groupId;
|
private String groupId;
|
||||||
@ -38,8 +38,8 @@ public class ImGroupJoinRequestEntity extends BaseIdEntity {
|
|||||||
|
|
||||||
private LocalDateTime reviewedAt;
|
private LocalDateTime reviewedAt;
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getGroupId() { return groupId; }
|
public String getGroupId() { return groupId; }
|
||||||
public void setGroupId(String groupId) { this.groupId = groupId; }
|
public void setGroupId(String groupId) { this.groupId = groupId; }
|
||||||
|
|||||||
@ -16,8 +16,8 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "im_message", indexes = {
|
@Table(name = "im_message", indexes = {
|
||||||
@Index(name = "idx_app_from", columnList = "appId,fromUserId"),
|
@Index(name = "idx_app_from", columnList = "appKey,fromUserId"),
|
||||||
@Index(name = "idx_app_to", columnList = "appId,toId")
|
@Index(name = "idx_app_to", columnList = "appKey,toId")
|
||||||
})
|
})
|
||||||
public class ImMessageEntity {
|
public class ImMessageEntity {
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ public class ImMessageEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String fromUserId;
|
private String fromUserId;
|
||||||
@ -74,8 +74,8 @@ public class ImMessageEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getFromUserId() { return fromUserId; }
|
public String getFromUserId() { return fromUserId; }
|
||||||
public void setFromUserId(String fromUserId) { this.fromUserId = fromUserId; }
|
public void setFromUserId(String fromUserId) { this.fromUserId = fromUserId; }
|
||||||
|
|||||||
@ -14,7 +14,7 @@ public class ImOfflineMessageEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -31,8 +31,8 @@ public class ImOfflineMessageEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -11,13 +11,13 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "im_operation_log", indexes = {
|
@Table(name = "im_operation_log", indexes = {
|
||||||
@Index(name = "idx_op_log_app_time", columnList = "appId,createdAt"),
|
@Index(name = "idx_op_log_app_time", columnList = "appKey,createdAt"),
|
||||||
@Index(name = "idx_op_log_app_operator", columnList = "appId,operatorId")
|
@Index(name = "idx_op_log_app_operator", columnList = "appKey,operatorId")
|
||||||
})
|
})
|
||||||
public class ImOperationLogEntity extends BaseIdEntity {
|
public class ImOperationLogEntity extends BaseIdEntity {
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String operatorId;
|
private String operatorId;
|
||||||
@ -38,8 +38,8 @@ public class ImOperationLogEntity extends BaseIdEntity {
|
|||||||
@JsonSerialize(using = EpochMillisLocalDateTimeSerializer.class)
|
@JsonSerialize(using = EpochMillisLocalDateTimeSerializer.class)
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getOperatorId() { return operatorId; }
|
public String getOperatorId() { return operatorId; }
|
||||||
public void setOperatorId(String operatorId) { this.operatorId = operatorId; }
|
public void setOperatorId(String operatorId) { this.operatorId = operatorId; }
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public class KeywordFilterEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 512)
|
@Column(nullable = false, length = 512)
|
||||||
private String pattern;
|
private String pattern;
|
||||||
@ -39,8 +39,8 @@ public class KeywordFilterEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getPattern() { return pattern; }
|
public String getPattern() { return pattern; }
|
||||||
public void setPattern(String pattern) { this.pattern = pattern; }
|
public void setPattern(String pattern) { this.pattern = pattern; }
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public class WebhookAlertEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String webhookId;
|
private String webhookId;
|
||||||
@ -43,8 +43,8 @@ public class WebhookAlertEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getWebhookId() { return webhookId; }
|
public String getWebhookId() { return webhookId; }
|
||||||
public void setWebhookId(String webhookId) { this.webhookId = webhookId; }
|
public void setWebhookId(String webhookId) { this.webhookId = webhookId; }
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public class WebhookConfigEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 512)
|
@Column(nullable = false, length = 512)
|
||||||
private String url;
|
private String url;
|
||||||
@ -40,8 +40,8 @@ public class WebhookConfigEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUrl() { return url; }
|
public String getUrl() { return url; }
|
||||||
public void setUrl(String url) { this.url = url; }
|
public void setUrl(String url) { this.url = url; }
|
||||||
|
|||||||
@ -14,7 +14,7 @@ public class WebhookDeliveryEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String callbackId;
|
private String callbackId;
|
||||||
@ -46,8 +46,8 @@ public class WebhookDeliveryEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getCallbackId() { return callbackId; }
|
public String getCallbackId() { return callbackId; }
|
||||||
public void setCallbackId(String callbackId) { this.callbackId = callbackId; }
|
public void setCallbackId(String callbackId) { this.callbackId = callbackId; }
|
||||||
|
|||||||
@ -10,12 +10,12 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ImAccountRepository extends JpaRepository<ImAccountEntity, String> {
|
public interface ImAccountRepository extends JpaRepository<ImAccountEntity, String> {
|
||||||
Optional<ImAccountEntity> findByAppIdAndUserId(String appId, String userId);
|
Optional<ImAccountEntity> findByAppKeyAndUserId(String appKey, String userId);
|
||||||
boolean existsByAppIdAndUserId(String appId, String userId);
|
boolean existsByAppKeyAndUserId(String appKey, String userId);
|
||||||
Page<ImAccountEntity> findByAppId(String appId, Pageable pageable);
|
Page<ImAccountEntity> findByAppKey(String appKey, Pageable pageable);
|
||||||
long countByAppId(String appId);
|
long countByAppKey(String appKey);
|
||||||
|
|
||||||
@Query("SELECT a FROM ImAccountEntity a WHERE a.appId = :appId AND a.status = 'ACTIVE' AND " +
|
@Query("SELECT a FROM ImAccountEntity a WHERE a.appKey = :appKey AND a.status = 'ACTIVE' AND " +
|
||||||
"(LOWER(a.userId) LIKE LOWER(CONCAT('%',:kw,'%')) OR LOWER(a.nickname) LIKE LOWER(CONCAT('%',:kw,'%')))")
|
"(LOWER(a.userId) LIKE LOWER(CONCAT('%',:kw,'%')) OR LOWER(a.nickname) LIKE LOWER(CONCAT('%',:kw,'%')))")
|
||||||
List<ImAccountEntity> searchByKeyword(@Param("appId") String appId, @Param("kw") String keyword, Pageable pageable);
|
List<ImAccountEntity> searchByKeyword(@Param("appKey") String appKey, @Param("kw") String keyword, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ImBlacklistRepository extends JpaRepository<ImBlacklistEntity, String> {
|
public interface ImBlacklistRepository extends JpaRepository<ImBlacklistEntity, String> {
|
||||||
List<ImBlacklistEntity> findByAppId(String appId);
|
List<ImBlacklistEntity> findByAppKey(String appKey);
|
||||||
List<ImBlacklistEntity> findByAppIdAndUserId(String appId, String userId);
|
List<ImBlacklistEntity> findByAppKeyAndUserId(String appKey, String userId);
|
||||||
|
|
||||||
Optional<ImBlacklistEntity> findByAppIdAndUserIdAndBlockedUserId(
|
Optional<ImBlacklistEntity> findByAppKeyAndUserIdAndBlockedUserId(
|
||||||
String appId, String userId, String blockedUserId);
|
String appKey, String userId, String blockedUserId);
|
||||||
|
|
||||||
boolean existsByAppIdAndUserIdAndBlockedUserId(String appId, String userId, String blockedUserId);
|
boolean existsByAppKeyAndUserIdAndBlockedUserId(String appKey, String userId, String blockedUserId);
|
||||||
|
|
||||||
void deleteByAppIdAndUserIdAndBlockedUserId(String appId, String userId, String blockedUserId);
|
void deleteByAppKeyAndUserIdAndBlockedUserId(String appKey, String userId, String blockedUserId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,16 +7,16 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ImConversationStateRepository extends JpaRepository<ImConversationStateEntity, String> {
|
public interface ImConversationStateRepository extends JpaRepository<ImConversationStateEntity, String> {
|
||||||
Optional<ImConversationStateEntity> findByAppIdAndUserIdAndTargetIdAndChatType(
|
Optional<ImConversationStateEntity> findByAppKeyAndUserIdAndTargetIdAndChatType(
|
||||||
String appId, String userId, String targetId, String chatType);
|
String appKey, String userId, String targetId, String chatType);
|
||||||
|
|
||||||
List<ImConversationStateEntity> findByAppIdAndUserId(String appId, String userId);
|
List<ImConversationStateEntity> findByAppKeyAndUserId(String appKey, String userId);
|
||||||
|
|
||||||
List<ImConversationStateEntity> findByAppIdAndUserIdAndHiddenFalse(String appId, String userId);
|
List<ImConversationStateEntity> findByAppKeyAndUserIdAndHiddenFalse(String appKey, String userId);
|
||||||
|
|
||||||
List<ImConversationStateEntity> findByAppIdAndUserIdAndConversationGroup(
|
List<ImConversationStateEntity> findByAppKeyAndUserIdAndConversationGroup(
|
||||||
String appId, String userId, String conversationGroup);
|
String appKey, String userId, String conversationGroup);
|
||||||
|
|
||||||
void deleteByAppIdAndUserIdAndTargetIdAndChatType(
|
void deleteByAppKeyAndUserIdAndTargetIdAndChatType(
|
||||||
String appId, String userId, String targetId, String chatType);
|
String appKey, String userId, String targetId, String chatType);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,20 +9,20 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface ImFriendRepository extends JpaRepository<ImFriendEntity, Long> {
|
public interface ImFriendRepository extends JpaRepository<ImFriendEntity, Long> {
|
||||||
|
|
||||||
List<ImFriendEntity> findByAppIdAndUserId(String appId, String userId);
|
List<ImFriendEntity> findByAppKeyAndUserId(String appKey, String userId);
|
||||||
|
|
||||||
List<ImFriendEntity> findByAppIdAndUserIdAndFriendGroup(String appId, String userId, String friendGroup);
|
List<ImFriendEntity> findByAppKeyAndUserIdAndFriendGroup(String appKey, String userId, String friendGroup);
|
||||||
|
|
||||||
Optional<ImFriendEntity> findByAppIdAndUserIdAndFriendId(String appId, String userId, String friendId);
|
Optional<ImFriendEntity> findByAppKeyAndUserIdAndFriendId(String appKey, String userId, String friendId);
|
||||||
|
|
||||||
boolean existsByAppIdAndUserIdAndFriendId(String appId, String userId, String friendId);
|
boolean existsByAppKeyAndUserIdAndFriendId(String appKey, String userId, String friendId);
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
void deleteByAppIdAndUserIdAndFriendId(String appId, String userId, String friendId);
|
void deleteByAppKeyAndUserIdAndFriendId(String appKey, String userId, String friendId);
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
void deleteByAppIdAndUserId(String appId, String userId);
|
void deleteByAppKeyAndUserId(String appKey, String userId);
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
void deleteByAppIdAndFriendId(String appId, String friendId);
|
void deleteByAppKeyAndFriendId(String appKey, String friendId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ImFriendRequestRepository extends JpaRepository<ImFriendRequestEntity, String> {
|
public interface ImFriendRequestRepository extends JpaRepository<ImFriendRequestEntity, String> {
|
||||||
List<ImFriendRequestEntity> findByAppId(String appId);
|
List<ImFriendRequestEntity> findByAppKey(String appKey);
|
||||||
Optional<ImFriendRequestEntity> findByAppIdAndFromUserIdAndToUserId(
|
Optional<ImFriendRequestEntity> findByAppKeyAndFromUserIdAndToUserId(
|
||||||
String appId, String fromUserId, String toUserId);
|
String appKey, String fromUserId, String toUserId);
|
||||||
|
|
||||||
List<ImFriendRequestEntity> findByAppIdAndToUserId(String appId, String toUserId);
|
List<ImFriendRequestEntity> findByAppKeyAndToUserId(String appKey, String toUserId);
|
||||||
List<ImFriendRequestEntity> findByAppIdAndFromUserId(String appId, String fromUserId);
|
List<ImFriendRequestEntity> findByAppKeyAndFromUserId(String appKey, String fromUserId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,5 +6,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ImGlobalMuteRepository extends JpaRepository<ImGlobalMuteEntity, String> {
|
public interface ImGlobalMuteRepository extends JpaRepository<ImGlobalMuteEntity, String> {
|
||||||
Optional<ImGlobalMuteEntity> findByAppId(String appId);
|
Optional<ImGlobalMuteEntity> findByAppKey(String appKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ImGroupJoinRequestRepository extends JpaRepository<ImGroupJoinRequestEntity, String> {
|
public interface ImGroupJoinRequestRepository extends JpaRepository<ImGroupJoinRequestEntity, String> {
|
||||||
List<ImGroupJoinRequestEntity> findByAppId(String appId);
|
List<ImGroupJoinRequestEntity> findByAppKey(String appKey);
|
||||||
Optional<ImGroupJoinRequestEntity> findByAppIdAndGroupIdAndRequesterId(
|
Optional<ImGroupJoinRequestEntity> findByAppKeyAndGroupIdAndRequesterId(
|
||||||
String appId, String groupId, String requesterId);
|
String appKey, String groupId, String requesterId);
|
||||||
|
|
||||||
List<ImGroupJoinRequestEntity> findByAppIdAndGroupId(String appId, String groupId);
|
List<ImGroupJoinRequestEntity> findByAppKeyAndGroupId(String appKey, String groupId);
|
||||||
List<ImGroupJoinRequestEntity> findByAppIdAndRequesterId(String appId, String requesterId);
|
List<ImGroupJoinRequestEntity> findByAppKeyAndRequesterId(String appKey, String requesterId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,22 +8,22 @@ import org.springframework.data.repository.query.Param;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ImGroupRepository extends JpaRepository<ImGroupEntity, String> {
|
public interface ImGroupRepository extends JpaRepository<ImGroupEntity, String> {
|
||||||
List<ImGroupEntity> findByAppId(String appId);
|
List<ImGroupEntity> findByAppKey(String appKey);
|
||||||
long countByAppId(String appId);
|
long countByAppKey(String appKey);
|
||||||
|
|
||||||
@Query(value = """
|
@Query(value = """
|
||||||
SELECT * FROM im_group
|
SELECT * FROM im_group
|
||||||
WHERE app_id = :appId
|
WHERE app_key = :appKey
|
||||||
AND JSON_CONTAINS(member_ids, JSON_QUOTE(:userId))
|
AND JSON_CONTAINS(member_ids, JSON_QUOTE(:userId))
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
""", nativeQuery = true)
|
""", nativeQuery = true)
|
||||||
List<ImGroupEntity> findUserGroups(
|
List<ImGroupEntity> findUserGroups(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId);
|
@Param("userId") String userId);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select g from ImGroupEntity g
|
select g from ImGroupEntity g
|
||||||
where g.appId = :appId
|
where g.appKey = :appKey
|
||||||
and (:keyword is null or :keyword = '' or
|
and (:keyword is null or :keyword = '' or
|
||||||
lower(g.id) like lower(concat('%', :keyword, '%')) or
|
lower(g.id) like lower(concat('%', :keyword, '%')) or
|
||||||
lower(g.name) like lower(concat('%', :keyword, '%')) or
|
lower(g.name) like lower(concat('%', :keyword, '%')) or
|
||||||
@ -32,7 +32,7 @@ public interface ImGroupRepository extends JpaRepository<ImGroupEntity, String>
|
|||||||
order by g.createdAt desc
|
order by g.createdAt desc
|
||||||
""")
|
""")
|
||||||
List<ImGroupEntity> searchByKeyword(
|
List<ImGroupEntity> searchByKeyword(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("keyword") String keyword,
|
@Param("keyword") String keyword,
|
||||||
Pageable pageable);
|
Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,16 +25,16 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
chat_type,
|
chat_type,
|
||||||
MAX(created_at) AS last_time
|
MAX(created_at) AS last_time
|
||||||
FROM im_message
|
FROM im_message
|
||||||
WHERE app_id = :appId AND chat_type = 'SINGLE'
|
WHERE app_key = :appKey AND chat_type = 'SINGLE'
|
||||||
AND (from_user_id = :userId OR to_id = :userId)
|
AND (from_user_id = :userId OR to_id = :userId)
|
||||||
GROUP BY target_id, chat_type
|
GROUP BY target_id, chat_type
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT to_id AS target_id, chat_type, MAX(created_at) AS last_time
|
SELECT to_id AS target_id, chat_type, MAX(created_at) AS last_time
|
||||||
FROM im_message
|
FROM im_message
|
||||||
WHERE app_id = :appId AND chat_type = 'GROUP'
|
WHERE app_key = :appKey AND chat_type = 'GROUP'
|
||||||
AND to_id IN (
|
AND to_id IN (
|
||||||
SELECT id FROM im_group
|
SELECT id FROM im_group
|
||||||
WHERE app_id = :appId AND JSON_CONTAINS(member_ids, JSON_QUOTE(:userId))
|
WHERE app_key = :appKey AND JSON_CONTAINS(member_ids, JSON_QUOTE(:userId))
|
||||||
)
|
)
|
||||||
GROUP BY to_id, chat_type
|
GROUP BY to_id, chat_type
|
||||||
) combined
|
) combined
|
||||||
@ -42,31 +42,31 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
LIMIT :size
|
LIMIT :size
|
||||||
""", nativeQuery = true)
|
""", nativeQuery = true)
|
||||||
List<ConversationSummary> findConversations(
|
List<ConversationSummary> findConversations(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId,
|
@Param("userId") String userId,
|
||||||
@Param("size") int size);
|
@Param("size") int size);
|
||||||
Page<ImMessageEntity> findByAppIdAndToIdOrderByCreatedAtDesc(
|
Page<ImMessageEntity> findByAppKeyAndToIdOrderByCreatedAtDesc(
|
||||||
String appId, String toId, Pageable pageable);
|
String appKey, String toId, Pageable pageable);
|
||||||
Page<ImMessageEntity> findByAppIdAndFromUserIdAndToIdOrderByCreatedAtDesc(
|
Page<ImMessageEntity> findByAppKeyAndFromUserIdAndToIdOrderByCreatedAtDesc(
|
||||||
String appId, String fromUserId, String toId, Pageable pageable);
|
String appKey, String fromUserId, String toId, Pageable pageable);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select m from ImMessageEntity m
|
select m from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
||||||
and ((m.fromUserId = :userId and m.toId = :peerId)
|
and ((m.fromUserId = :userId and m.toId = :peerId)
|
||||||
or (m.fromUserId = :peerId and m.toId = :userId))
|
or (m.fromUserId = :peerId and m.toId = :userId))
|
||||||
order by m.createdAt desc
|
order by m.createdAt desc
|
||||||
""")
|
""")
|
||||||
Page<ImMessageEntity> findSingleConversation(
|
Page<ImMessageEntity> findSingleConversation(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId,
|
@Param("userId") String userId,
|
||||||
@Param("peerId") String peerId,
|
@Param("peerId") String peerId,
|
||||||
Pageable pageable);
|
Pageable pageable);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select m from ImMessageEntity m
|
select m from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
||||||
and ((m.fromUserId = :userId and m.toId = :peerId)
|
and ((m.fromUserId = :userId and m.toId = :peerId)
|
||||||
or (m.fromUserId = :peerId and m.toId = :userId))
|
or (m.fromUserId = :peerId and m.toId = :userId))
|
||||||
@ -81,7 +81,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
order by m.createdAt desc
|
order by m.createdAt desc
|
||||||
""")
|
""")
|
||||||
Page<ImMessageEntity> findSingleConversationFiltered(
|
Page<ImMessageEntity> findSingleConversationFiltered(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId,
|
@Param("userId") String userId,
|
||||||
@Param("peerId") String peerId,
|
@Param("peerId") String peerId,
|
||||||
@Param("msgType") ImMessageEntity.MsgType msgType,
|
@Param("msgType") ImMessageEntity.MsgType msgType,
|
||||||
@ -90,33 +90,33 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
@Param("endTime") LocalDateTime endTime,
|
@Param("endTime") LocalDateTime endTime,
|
||||||
Pageable pageable);
|
Pageable pageable);
|
||||||
|
|
||||||
List<ImMessageEntity> findByAppIdAndFromUserIdAndToIdAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
List<ImMessageEntity> findByAppKeyAndFromUserIdAndToIdAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
||||||
String appId,
|
String appKey,
|
||||||
String fromUserId,
|
String fromUserId,
|
||||||
String toId,
|
String toId,
|
||||||
LocalDateTime createdAt);
|
LocalDateTime createdAt);
|
||||||
|
|
||||||
List<ImMessageEntity> findByAppIdAndToIdAndChatTypeAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
List<ImMessageEntity> findByAppKeyAndToIdAndChatTypeAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
||||||
String appId,
|
String appKey,
|
||||||
String toId,
|
String toId,
|
||||||
ImMessageEntity.ChatType chatType,
|
ImMessageEntity.ChatType chatType,
|
||||||
LocalDateTime createdAt);
|
LocalDateTime createdAt);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select m from ImMessageEntity m
|
select m from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
||||||
and m.toId = :groupId
|
and m.toId = :groupId
|
||||||
order by m.createdAt desc
|
order by m.createdAt desc
|
||||||
""")
|
""")
|
||||||
Page<ImMessageEntity> findGroupHistory(
|
Page<ImMessageEntity> findGroupHistory(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("groupId") String groupId,
|
@Param("groupId") String groupId,
|
||||||
Pageable pageable);
|
Pageable pageable);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select m from ImMessageEntity m
|
select m from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
||||||
and m.toId = :groupId
|
and m.toId = :groupId
|
||||||
and (:msgType is null or m.msgType = :msgType)
|
and (:msgType is null or m.msgType = :msgType)
|
||||||
@ -129,7 +129,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
order by m.createdAt desc
|
order by m.createdAt desc
|
||||||
""")
|
""")
|
||||||
Page<ImMessageEntity> findGroupHistoryFiltered(
|
Page<ImMessageEntity> findGroupHistoryFiltered(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("groupId") String groupId,
|
@Param("groupId") String groupId,
|
||||||
@Param("msgType") ImMessageEntity.MsgType msgType,
|
@Param("msgType") ImMessageEntity.MsgType msgType,
|
||||||
@Param("keyword") String keyword,
|
@Param("keyword") String keyword,
|
||||||
@ -139,7 +139,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select m from ImMessageEntity m
|
select m from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and (:chatType is null or m.chatType = :chatType)
|
and (:chatType is null or m.chatType = :chatType)
|
||||||
and (:msgType is null or m.msgType = :msgType)
|
and (:msgType is null or m.msgType = :msgType)
|
||||||
and (:keyword is null or :keyword = '' or
|
and (:keyword is null or :keyword = '' or
|
||||||
@ -152,7 +152,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
order by m.createdAt desc
|
order by m.createdAt desc
|
||||||
""")
|
""")
|
||||||
Page<ImMessageEntity> searchByKeyword(
|
Page<ImMessageEntity> searchByKeyword(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("chatType") ImMessageEntity.ChatType chatType,
|
@Param("chatType") ImMessageEntity.ChatType chatType,
|
||||||
@Param("msgType") ImMessageEntity.MsgType msgType,
|
@Param("msgType") ImMessageEntity.MsgType msgType,
|
||||||
@Param("keyword") String keyword,
|
@Param("keyword") String keyword,
|
||||||
@ -162,7 +162,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select m from ImMessageEntity m
|
select m from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and (:chatType is null or m.chatType = :chatType)
|
and (:chatType is null or m.chatType = :chatType)
|
||||||
and (:msgType is null or m.msgType = :msgType)
|
and (:msgType is null or m.msgType = :msgType)
|
||||||
and (:keyword is null or :keyword = '' or
|
and (:keyword is null or :keyword = '' or
|
||||||
@ -178,7 +178,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
or (m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
or (m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
||||||
and m.toId in (
|
and m.toId in (
|
||||||
select g.id from ImGroupEntity g
|
select g.id from ImGroupEntity g
|
||||||
where g.appId = :appId
|
where g.appKey = :appKey
|
||||||
and function('JSON_CONTAINS', g.memberIds, function('JSON_QUOTE', :userId)) = 1
|
and function('JSON_CONTAINS', g.memberIds, function('JSON_QUOTE', :userId)) = 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -186,7 +186,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
order by m.createdAt desc
|
order by m.createdAt desc
|
||||||
""")
|
""")
|
||||||
Page<ImMessageEntity> searchByKeywordForUser(
|
Page<ImMessageEntity> searchByKeywordForUser(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId,
|
@Param("userId") String userId,
|
||||||
@Param("chatType") ImMessageEntity.ChatType chatType,
|
@Param("chatType") ImMessageEntity.ChatType chatType,
|
||||||
@Param("msgType") ImMessageEntity.MsgType msgType,
|
@Param("msgType") ImMessageEntity.MsgType msgType,
|
||||||
@ -197,7 +197,7 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select count(m) from ImMessageEntity m
|
select count(m) from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
||||||
and ((m.fromUserId = :userId and m.toId = :peerId)
|
and ((m.fromUserId = :userId and m.toId = :peerId)
|
||||||
or (m.fromUserId = :peerId and m.toId = :userId))
|
or (m.fromUserId = :peerId and m.toId = :userId))
|
||||||
@ -205,48 +205,48 @@ public interface ImMessageRepository extends JpaRepository<ImMessageEntity, Stri
|
|||||||
and (:since is null or m.createdAt > :since)
|
and (:since is null or m.createdAt > :since)
|
||||||
""")
|
""")
|
||||||
long countUnreadSingleConversation(
|
long countUnreadSingleConversation(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId,
|
@Param("userId") String userId,
|
||||||
@Param("peerId") String peerId,
|
@Param("peerId") String peerId,
|
||||||
@Param("since") LocalDateTime since);
|
@Param("since") LocalDateTime since);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select count(m) from ImMessageEntity m
|
select count(m) from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.GROUP
|
||||||
and m.toId = :groupId
|
and m.toId = :groupId
|
||||||
and m.fromUserId <> :userId
|
and m.fromUserId <> :userId
|
||||||
and (:since is null or m.createdAt > :since)
|
and (:since is null or m.createdAt > :since)
|
||||||
""")
|
""")
|
||||||
long countUnreadGroupConversation(
|
long countUnreadGroupConversation(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId,
|
@Param("userId") String userId,
|
||||||
@Param("groupId") String groupId,
|
@Param("groupId") String groupId,
|
||||||
@Param("since") LocalDateTime since);
|
@Param("since") LocalDateTime since);
|
||||||
|
|
||||||
long countByAppId(String appId);
|
long countByAppKey(String appKey);
|
||||||
|
|
||||||
@Query("select count(m) from ImMessageEntity m where m.appId = :appId and m.createdAt >= :since")
|
@Query("select count(m) from ImMessageEntity m where m.appKey = :appKey and m.createdAt >= :since")
|
||||||
long countByAppIdAndCreatedAtAfter(@Param("appId") String appId, @Param("since") LocalDateTime since);
|
long countByAppKeyAndCreatedAtAfter(@Param("appKey") String appKey, @Param("since") LocalDateTime since);
|
||||||
|
|
||||||
@Query("select count(m) from ImMessageEntity m where m.appId = :appId and m.chatType = :chatType and m.createdAt >= :since")
|
@Query("select count(m) from ImMessageEntity m where m.appKey = :appKey and m.chatType = :chatType and m.createdAt >= :since")
|
||||||
long countByAppIdAndChatTypeAndCreatedAtAfter(
|
long countByAppKeyAndChatTypeAndCreatedAtAfter(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("chatType") ImMessageEntity.ChatType chatType,
|
@Param("chatType") ImMessageEntity.ChatType chatType,
|
||||||
@Param("since") LocalDateTime since);
|
@Param("since") LocalDateTime since);
|
||||||
|
|
||||||
default long countTodayByAppId(String appId) {
|
default long countTodayByAppKey(String appKey) {
|
||||||
return countByAppIdAndCreatedAtAfter(appId, LocalDateTime.now().toLocalDate().atStartOfDay());
|
return countByAppKeyAndCreatedAtAfter(appKey, LocalDateTime.now().toLocalDate().atStartOfDay());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
select m from ImMessageEntity m
|
select m from ImMessageEntity m
|
||||||
where m.appId = :appId
|
where m.appKey = :appKey
|
||||||
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
and m.chatType = com.xuqm.im.entity.ImMessageEntity$ChatType.SINGLE
|
||||||
and m.toId = :userId
|
and m.toId = :userId
|
||||||
and m.status <> com.xuqm.im.entity.ImMessageEntity$MsgStatus.READ
|
and m.status <> com.xuqm.im.entity.ImMessageEntity$MsgStatus.READ
|
||||||
""")
|
""")
|
||||||
List<ImMessageEntity> findUnreadByAppIdAndToId(
|
List<ImMessageEntity> findUnreadByAppKeyAndToId(
|
||||||
@Param("appId") String appId,
|
@Param("appKey") String appKey,
|
||||||
@Param("userId") String userId);
|
@Param("userId") String userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,14 +11,14 @@ import java.util.List;
|
|||||||
@Repository
|
@Repository
|
||||||
public interface ImOfflineMessageRepository extends JpaRepository<ImOfflineMessageEntity, String> {
|
public interface ImOfflineMessageRepository extends JpaRepository<ImOfflineMessageEntity, String> {
|
||||||
|
|
||||||
List<ImOfflineMessageEntity> findByAppIdAndUserIdAndDeliveredFalse(String appId, String userId);
|
List<ImOfflineMessageEntity> findByAppKeyAndUserIdAndDeliveredFalse(String appKey, String userId);
|
||||||
|
|
||||||
@Modifying
|
@Modifying
|
||||||
@Query("UPDATE ImOfflineMessageEntity o SET o.delivered = true WHERE o.id IN ?1")
|
@Query("UPDATE ImOfflineMessageEntity o SET o.delivered = true WHERE o.id IN ?1")
|
||||||
void markDeliveredByIds(List<String> ids);
|
void markDeliveredByIds(List<String> ids);
|
||||||
|
|
||||||
@Query("SELECT COUNT(o) FROM ImOfflineMessageEntity o WHERE o.appId = ?1 AND o.userId = ?2 AND o.delivered = false")
|
@Query("SELECT COUNT(o) FROM ImOfflineMessageEntity o WHERE o.appKey = ?1 AND o.userId = ?2 AND o.delivered = false")
|
||||||
long countUndeliveredByAppIdAndUserId(String appId, String userId);
|
long countUndeliveredByAppKeyAndUserId(String appKey, String userId);
|
||||||
|
|
||||||
void deleteByAppIdAndUserIdAndDeliveredTrue(String appId, String userId);
|
void deleteByAppKeyAndUserIdAndDeliveredTrue(String appKey, String userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,5 +6,5 @@ import org.springframework.data.domain.Pageable;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
public interface ImOperationLogRepository extends JpaRepository<ImOperationLogEntity, String> {
|
public interface ImOperationLogRepository extends JpaRepository<ImOperationLogEntity, String> {
|
||||||
Page<ImOperationLogEntity> findByAppIdOrderByCreatedAtDesc(String appId, Pageable pageable);
|
Page<ImOperationLogEntity> findByAppKeyOrderByCreatedAtDesc(String appKey, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface KeywordFilterRepository extends JpaRepository<KeywordFilterEntity, String> {
|
public interface KeywordFilterRepository extends JpaRepository<KeywordFilterEntity, String> {
|
||||||
List<KeywordFilterEntity> findByAppIdAndEnabledTrue(String appId);
|
List<KeywordFilterEntity> findByAppKeyAndEnabledTrue(String appKey);
|
||||||
List<KeywordFilterEntity> findByAppId(String appId);
|
List<KeywordFilterEntity> findByAppKey(String appKey);
|
||||||
java.util.Optional<KeywordFilterEntity> findByIdAndAppId(String id, String appId);
|
java.util.Optional<KeywordFilterEntity> findByIdAndAppId(String id, String appKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,10 @@ import org.springframework.data.jpa.repository.Query;
|
|||||||
|
|
||||||
public interface WebhookAlertRepository extends JpaRepository<WebhookAlertEntity, String> {
|
public interface WebhookAlertRepository extends JpaRepository<WebhookAlertEntity, String> {
|
||||||
|
|
||||||
Page<WebhookAlertEntity> findByAppId(String appId, Pageable pageable);
|
Page<WebhookAlertEntity> findByAppKey(String appKey, Pageable pageable);
|
||||||
|
|
||||||
Page<WebhookAlertEntity> findByAppIdAndAcknowledged(String appId, boolean acknowledged, Pageable pageable);
|
Page<WebhookAlertEntity> findByAppKeyAndAcknowledged(String appKey, boolean acknowledged, Pageable pageable);
|
||||||
|
|
||||||
@Query("SELECT COUNT(a) FROM WebhookAlertEntity a WHERE a.appId = ?1 AND a.acknowledged = false")
|
@Query("SELECT COUNT(a) FROM WebhookAlertEntity a WHERE a.appKey = ?1 AND a.acknowledged = false")
|
||||||
long countUnacknowledgedByAppId(String appId);
|
long countUnacknowledgedByAppKey(String appKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface WebhookConfigRepository extends JpaRepository<WebhookConfigEntity, String> {
|
public interface WebhookConfigRepository extends JpaRepository<WebhookConfigEntity, String> {
|
||||||
List<WebhookConfigEntity> findByAppIdAndEnabledTrue(String appId);
|
List<WebhookConfigEntity> findByAppKeyAndEnabledTrue(String appKey);
|
||||||
List<WebhookConfigEntity> findByAppId(String appId);
|
List<WebhookConfigEntity> findByAppKey(String appKey);
|
||||||
java.util.Optional<WebhookConfigEntity> findByIdAndAppId(String id, String appId);
|
java.util.Optional<WebhookConfigEntity> findByIdAndAppId(String id, String appKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,21 +10,21 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface WebhookDeliveryRepository extends JpaRepository<WebhookDeliveryEntity, String> {
|
public interface WebhookDeliveryRepository extends JpaRepository<WebhookDeliveryEntity, String> {
|
||||||
|
|
||||||
Page<WebhookDeliveryEntity> findByAppIdAndCallbackEvent(String appId, String callbackEvent, Pageable pageable);
|
Page<WebhookDeliveryEntity> findByAppKeyAndCallbackEvent(String appKey, String callbackEvent, Pageable pageable);
|
||||||
|
|
||||||
Page<WebhookDeliveryEntity> findByAppId(String appId, Pageable pageable);
|
Page<WebhookDeliveryEntity> findByAppKey(String appKey, Pageable pageable);
|
||||||
|
|
||||||
Page<WebhookDeliveryEntity> findByAppIdAndSuccess(String appId, boolean success, Pageable pageable);
|
Page<WebhookDeliveryEntity> findByAppKeyAndSuccess(String appKey, boolean success, Pageable pageable);
|
||||||
|
|
||||||
List<WebhookDeliveryEntity> findByCallbackId(String callbackId);
|
List<WebhookDeliveryEntity> findByCallbackId(String callbackId);
|
||||||
|
|
||||||
@Query("SELECT COUNT(d) FROM WebhookDeliveryEntity d WHERE d.appId = ?1 AND d.success = true AND d.createdAt >= ?2")
|
@Query("SELECT COUNT(d) FROM WebhookDeliveryEntity d WHERE d.appKey = ?1 AND d.success = true AND d.createdAt >= ?2")
|
||||||
long countSuccessfulByAppIdSince(String appId, LocalDateTime since);
|
long countSuccessfulByAppKeySince(String appKey, LocalDateTime since);
|
||||||
|
|
||||||
@Query("SELECT COUNT(d) FROM WebhookDeliveryEntity d WHERE d.appId = ?1 AND d.success = false AND d.createdAt >= ?2")
|
@Query("SELECT COUNT(d) FROM WebhookDeliveryEntity d WHERE d.appKey = ?1 AND d.success = false AND d.createdAt >= ?2")
|
||||||
long countFailedByAppIdSince(String appId, LocalDateTime since);
|
long countFailedByAppKeySince(String appKey, LocalDateTime since);
|
||||||
|
|
||||||
@Query("SELECT d.callbackEvent, COUNT(d), SUM(CASE WHEN d.success = true THEN 1 ELSE 0 END) " +
|
@Query("SELECT d.callbackEvent, COUNT(d), SUM(CASE WHEN d.success = true THEN 1 ELSE 0 END) " +
|
||||||
"FROM WebhookDeliveryEntity d WHERE d.appId = ?1 AND d.createdAt >= ?2 GROUP BY d.callbackEvent")
|
"FROM WebhookDeliveryEntity d WHERE d.appKey = ?1 AND d.createdAt >= ?2 GROUP BY d.callbackEvent")
|
||||||
List<Object[]> statsByAppIdSince(String appId, LocalDateTime since);
|
List<Object[]> statsByAppKeySince(String appKey, LocalDateTime since);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,11 +23,11 @@ public class BlacklistService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImBlacklistEntity add(String appKey, String userId, String blockedUserId) {
|
public ImBlacklistEntity add(String appKey, String userId, String blockedUserId) {
|
||||||
return repository.findByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId)
|
return repository.findByAppKeyAndUserIdAndBlockedUserId(appKey, userId, blockedUserId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImBlacklistEntity entity = new ImBlacklistEntity();
|
ImBlacklistEntity entity = new ImBlacklistEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setBlockedUserId(blockedUserId);
|
entity.setBlockedUserId(blockedUserId);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
@ -39,24 +39,24 @@ public class BlacklistService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void remove(String appKey, String userId, String blockedUserId) {
|
public void remove(String appKey, String userId, String blockedUserId) {
|
||||||
ImBlacklistEntity entity = repository.findByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId)
|
ImBlacklistEntity entity = repository.findByAppKeyAndUserIdAndBlockedUserId(appKey, userId, blockedUserId)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
repository.deleteByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId);
|
repository.deleteByAppKeyAndUserIdAndBlockedUserId(appKey, userId, blockedUserId);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
dispatchWebhook(entity, "blacklist.removed");
|
dispatchWebhook(entity, "blacklist.removed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImBlacklistEntity> list(String appKey, String userId) {
|
public List<ImBlacklistEntity> list(String appKey, String userId) {
|
||||||
return repository.findByAppIdAndUserId(appKey, userId);
|
return repository.findByAppKeyAndUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImBlacklistEntity> listByApp(String appKey) {
|
public List<ImBlacklistEntity> listByApp(String appKey) {
|
||||||
return repository.findByAppId(appKey);
|
return repository.findByAppKey(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBlocked(String appKey, String userId, String blockedUserId) {
|
public boolean isBlocked(String appKey, String userId, String blockedUserId) {
|
||||||
return repository.existsByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId);
|
return repository.existsByAppKeyAndUserIdAndBlockedUserId(appKey, userId, blockedUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEitherBlocked(String appKey, String userId, String targetUserId) {
|
public boolean isEitherBlocked(String appKey, String userId, String targetUserId) {
|
||||||
@ -65,11 +65,11 @@ public class BlacklistService {
|
|||||||
|
|
||||||
private void dispatchWebhook(ImBlacklistEntity entity, String callbackEvent) {
|
private void dispatchWebhook(ImBlacklistEntity entity, String callbackEvent) {
|
||||||
webhookDispatchService.dispatch(
|
webhookDispatchService.dispatch(
|
||||||
entity.getAppId(),
|
entity.getAppKey(),
|
||||||
"blacklist",
|
"blacklist",
|
||||||
callbackEvent,
|
callbackEvent,
|
||||||
new BlacklistCallbackPayload(
|
new BlacklistCallbackPayload(
|
||||||
entity.getAppId(),
|
entity.getAppKey(),
|
||||||
entity.getId(),
|
entity.getId(),
|
||||||
entity.getUserId(),
|
entity.getUserId(),
|
||||||
entity.getBlockedUserId(),
|
entity.getBlockedUserId(),
|
||||||
|
|||||||
@ -87,7 +87,7 @@ public class ConversationStateService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteConversationState(String appKey, String userId, String targetId, String chatType) {
|
public void deleteConversationState(String appKey, String userId, String targetId, String chatType) {
|
||||||
repository.deleteByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType);
|
repository.deleteByAppKeyAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -107,7 +107,7 @@ public class ConversationStateService {
|
|||||||
public void clearHiddenForUsers(String appKey, String targetId, String chatType, Collection<String> userIds) {
|
public void clearHiddenForUsers(String appKey, String targetId, String chatType, Collection<String> userIds) {
|
||||||
for (String userId : userIds) {
|
for (String userId : userIds) {
|
||||||
ImConversationStateEntity state = repository
|
ImConversationStateEntity state = repository
|
||||||
.findByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
.findByAppKeyAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (state != null && state.isHidden()) {
|
if (state != null && state.isHidden()) {
|
||||||
state.setHidden(false);
|
state.setHidden(false);
|
||||||
@ -118,11 +118,11 @@ public class ConversationStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ImConversationStateEntity getOrCreate(String appKey, String userId, String targetId, String chatType) {
|
public ImConversationStateEntity getOrCreate(String appKey, String userId, String targetId, String chatType) {
|
||||||
return repository.findByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
return repository.findByAppKeyAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImConversationStateEntity entity = new ImConversationStateEntity();
|
ImConversationStateEntity entity = new ImConversationStateEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setTargetId(targetId);
|
entity.setTargetId(targetId);
|
||||||
entity.setChatType(chatType);
|
entity.setChatType(chatType);
|
||||||
@ -139,16 +139,16 @@ public class ConversationStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ImConversationStateEntity find(String appKey, String userId, String targetId, String chatType) {
|
public ImConversationStateEntity find(String appKey, String userId, String targetId, String chatType) {
|
||||||
return repository.findByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
return repository.findByAppKeyAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImConversationStateEntity> listVisible(String appKey, String userId) {
|
public List<ImConversationStateEntity> listVisible(String appKey, String userId) {
|
||||||
return repository.findByAppIdAndUserIdAndHiddenFalse(appKey, userId);
|
return repository.findByAppKeyAndUserIdAndHiddenFalse(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> listConversationGroups(String appKey, String userId) {
|
public List<String> listConversationGroups(String appKey, String userId) {
|
||||||
return repository.findByAppIdAndUserId(appKey, userId).stream()
|
return repository.findByAppKeyAndUserId(appKey, userId).stream()
|
||||||
.map(ImConversationStateEntity::getConversationGroup)
|
.map(ImConversationStateEntity::getConversationGroup)
|
||||||
.filter(group -> group != null && !group.isBlank())
|
.filter(group -> group != null && !group.isBlank())
|
||||||
.distinct()
|
.distinct()
|
||||||
@ -157,7 +157,7 @@ public class ConversationStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ImConversationStateEntity> listByConversationGroup(String appKey, String userId, String conversationGroup) {
|
public List<ImConversationStateEntity> listByConversationGroup(String appKey, String userId, String conversationGroup) {
|
||||||
return repository.findByAppIdAndUserIdAndConversationGroup(appKey, userId, normalizeGroup(conversationGroup));
|
return repository.findByAppKeyAndUserIdAndConversationGroup(appKey, userId, normalizeGroup(conversationGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void touch(ImConversationStateEntity entity) {
|
private void touch(ImConversationStateEntity entity) {
|
||||||
|
|||||||
@ -51,12 +51,12 @@ public class FriendRequestService {
|
|||||||
throw new BusinessException(403, "当前应用未开放好友申请");
|
throw new BusinessException(403, "当前应用未开放好友申请");
|
||||||
}
|
}
|
||||||
final boolean[] created = {false};
|
final boolean[] created = {false};
|
||||||
ImFriendRequestEntity saved = requestRepository.findByAppIdAndFromUserIdAndToUserId(appKey, fromUserId, toUserId)
|
ImFriendRequestEntity saved = requestRepository.findByAppKeyAndFromUserIdAndToUserId(appKey, fromUserId, toUserId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
created[0] = true;
|
created[0] = true;
|
||||||
ImFriendRequestEntity entity = new ImFriendRequestEntity();
|
ImFriendRequestEntity entity = new ImFriendRequestEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setFromUserId(fromUserId);
|
entity.setFromUserId(fromUserId);
|
||||||
entity.setToUserId(toUserId);
|
entity.setToUserId(toUserId);
|
||||||
entity.setRemark(remark);
|
entity.setRemark(remark);
|
||||||
@ -94,10 +94,10 @@ public class FriendRequestService {
|
|||||||
request.setReviewedAt(LocalDateTime.now());
|
request.setReviewedAt(LocalDateTime.now());
|
||||||
requestRepository.save(request);
|
requestRepository.save(request);
|
||||||
friendRepository
|
friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(appKey, request.getFromUserId(), request.getToUserId())
|
.findByAppKeyAndUserIdAndFriendId(appKey, request.getFromUserId(), request.getToUserId())
|
||||||
.orElseGet(() -> friendEntity(appKey, request.getFromUserId(), request.getToUserId()));
|
.orElseGet(() -> friendEntity(appKey, request.getFromUserId(), request.getToUserId()));
|
||||||
friendRepository
|
friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(appKey, request.getToUserId(), request.getFromUserId())
|
.findByAppKeyAndUserIdAndFriendId(appKey, request.getToUserId(), request.getFromUserId())
|
||||||
.orElseGet(() -> friendEntity(appKey, request.getToUserId(), request.getFromUserId()));
|
.orElseGet(() -> friendEntity(appKey, request.getToUserId(), request.getFromUserId()));
|
||||||
dispatchWebhook(request, "friend.request.accepted");
|
dispatchWebhook(request, "friend.request.accepted");
|
||||||
publishNotification(
|
publishNotification(
|
||||||
@ -148,17 +148,17 @@ public class FriendRequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ImFriendRequestEntity> incoming(String appKey, String userId) {
|
public List<ImFriendRequestEntity> incoming(String appKey, String userId) {
|
||||||
return requestRepository.findByAppIdAndToUserId(appKey, userId).stream()
|
return requestRepository.findByAppKeyAndToUserId(appKey, userId).stream()
|
||||||
.filter(request -> ImFriendRequestEntity.Status.PENDING.name().equals(request.getStatus()))
|
.filter(request -> ImFriendRequestEntity.Status.PENDING.name().equals(request.getStatus()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImFriendRequestEntity> outgoing(String appKey, String userId) {
|
public List<ImFriendRequestEntity> outgoing(String appKey, String userId) {
|
||||||
return requestRepository.findByAppIdAndFromUserId(appKey, userId);
|
return requestRepository.findByAppKeyAndFromUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImFriendRequestEntity> listByApp(String appKey) {
|
public List<ImFriendRequestEntity> listByApp(String appKey) {
|
||||||
return requestRepository.findByAppId(appKey);
|
return requestRepository.findByAppKey(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -176,7 +176,7 @@ public class FriendRequestService {
|
|||||||
private ImFriendRequestEntity getRequest(String appKey, String requestId, String operatorId) {
|
private ImFriendRequestEntity getRequest(String appKey, String requestId, String operatorId) {
|
||||||
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
||||||
if (!request.getAppId().equals(appKey) || !request.getToUserId().equals(operatorId)) {
|
if (!request.getAppKey().equals(appKey) || !request.getToUserId().equals(operatorId)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
@ -185,7 +185,7 @@ public class FriendRequestService {
|
|||||||
private ImFriendRequestEntity getRequest(String appKey, String requestId) {
|
private ImFriendRequestEntity getRequest(String appKey, String requestId) {
|
||||||
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
||||||
if (!request.getAppId().equals(appKey)) {
|
if (!request.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
@ -201,11 +201,11 @@ public class FriendRequestService {
|
|||||||
request.setReviewedAt(LocalDateTime.now());
|
request.setReviewedAt(LocalDateTime.now());
|
||||||
ImFriendRequestEntity saved = requestRepository.save(request);
|
ImFriendRequestEntity saved = requestRepository.save(request);
|
||||||
friendRepository
|
friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(request.getAppId(), request.getFromUserId(), request.getToUserId())
|
.findByAppKeyAndUserIdAndFriendId(request.getAppKey(), request.getFromUserId(), request.getToUserId())
|
||||||
.orElseGet(() -> friendEntity(request.getAppId(), request.getFromUserId(), request.getToUserId()));
|
.orElseGet(() -> friendEntity(request.getAppKey(), request.getFromUserId(), request.getToUserId()));
|
||||||
friendRepository
|
friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(request.getAppId(), request.getToUserId(), request.getFromUserId())
|
.findByAppKeyAndUserIdAndFriendId(request.getAppKey(), request.getToUserId(), request.getFromUserId())
|
||||||
.orElseGet(() -> friendEntity(request.getAppId(), request.getToUserId(), request.getFromUserId()));
|
.orElseGet(() -> friendEntity(request.getAppKey(), request.getToUserId(), request.getFromUserId()));
|
||||||
dispatchWebhook(saved, "friend.request.accepted");
|
dispatchWebhook(saved, "friend.request.accepted");
|
||||||
publishNotification(
|
publishNotification(
|
||||||
request,
|
request,
|
||||||
@ -225,7 +225,7 @@ public class FriendRequestService {
|
|||||||
|
|
||||||
private com.xuqm.im.entity.ImFriendEntity friendEntity(String appKey, String userId, String friendId) {
|
private com.xuqm.im.entity.ImFriendEntity friendEntity(String appKey, String userId, String friendId) {
|
||||||
com.xuqm.im.entity.ImFriendEntity entity = new com.xuqm.im.entity.ImFriendEntity();
|
com.xuqm.im.entity.ImFriendEntity entity = new com.xuqm.im.entity.ImFriendEntity();
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setFriendId(friendId);
|
entity.setFriendId(friendId);
|
||||||
return friendRepository.save(entity);
|
return friendRepository.save(entity);
|
||||||
@ -245,7 +245,7 @@ public class FriendRequestService {
|
|||||||
) {
|
) {
|
||||||
ImMessageEntity message = new ImMessageEntity();
|
ImMessageEntity message = new ImMessageEntity();
|
||||||
message.setId(UUID.randomUUID().toString());
|
message.setId(UUID.randomUUID().toString());
|
||||||
message.setAppId(request.getAppId());
|
message.setAppKey(request.getAppKey());
|
||||||
message.setFromUserId(fromUserId);
|
message.setFromUserId(fromUserId);
|
||||||
message.setToId(toUserId);
|
message.setToId(toUserId);
|
||||||
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
||||||
@ -302,11 +302,11 @@ public class FriendRequestService {
|
|||||||
|
|
||||||
private void dispatchWebhook(ImFriendRequestEntity request, String callbackEvent) {
|
private void dispatchWebhook(ImFriendRequestEntity request, String callbackEvent) {
|
||||||
webhookDispatchService.dispatch(
|
webhookDispatchService.dispatch(
|
||||||
request.getAppId(),
|
request.getAppKey(),
|
||||||
"friend_request",
|
"friend_request",
|
||||||
callbackEvent,
|
callbackEvent,
|
||||||
new FriendRequestCallbackPayload(
|
new FriendRequestCallbackPayload(
|
||||||
request.getAppId(),
|
request.getAppKey(),
|
||||||
request.getId(),
|
request.getId(),
|
||||||
request.getFromUserId(),
|
request.getFromUserId(),
|
||||||
request.getToUserId(),
|
request.getToUserId(),
|
||||||
|
|||||||
@ -17,14 +17,14 @@ public class GlobalMuteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled(String appKey) {
|
public boolean isEnabled(String appKey) {
|
||||||
return repository.findByAppId(appKey).map(ImGlobalMuteEntity::isEnabled).orElse(false);
|
return repository.findByAppKey(appKey).map(ImGlobalMuteEntity::isEnabled).orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImGlobalMuteEntity get(String appKey) {
|
public ImGlobalMuteEntity get(String appKey) {
|
||||||
return repository.findByAppId(appKey).orElseGet(() -> {
|
return repository.findByAppKey(appKey).orElseGet(() -> {
|
||||||
ImGlobalMuteEntity entity = new ImGlobalMuteEntity();
|
ImGlobalMuteEntity entity = new ImGlobalMuteEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setEnabled(false);
|
entity.setEnabled(false);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
entity.setUpdatedAt(LocalDateTime.now());
|
entity.setUpdatedAt(LocalDateTime.now());
|
||||||
@ -33,10 +33,10 @@ public class GlobalMuteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ImGlobalMuteEntity setEnabled(String appKey, boolean enabled) {
|
public ImGlobalMuteEntity setEnabled(String appKey, boolean enabled) {
|
||||||
ImGlobalMuteEntity entity = repository.findByAppId(appKey).orElseGet(() -> {
|
ImGlobalMuteEntity entity = repository.findByAppKey(appKey).orElseGet(() -> {
|
||||||
ImGlobalMuteEntity created = new ImGlobalMuteEntity();
|
ImGlobalMuteEntity created = new ImGlobalMuteEntity();
|
||||||
created.setId(UUID.randomUUID().toString());
|
created.setId(UUID.randomUUID().toString());
|
||||||
created.setAppId(appKey);
|
created.setAppKey(appKey);
|
||||||
created.setCreatedAt(LocalDateTime.now());
|
created.setCreatedAt(LocalDateTime.now());
|
||||||
return created;
|
return created;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -47,11 +47,11 @@ public class ImAccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LoginResult loginOrRegister(String appKey, String userId) {
|
public LoginResult loginOrRegister(String appKey, String userId) {
|
||||||
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appKey, userId)
|
ImAccountEntity account = accountRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImAccountEntity e = new ImAccountEntity();
|
ImAccountEntity e = new ImAccountEntity();
|
||||||
e.setId(UUID.randomUUID().toString());
|
e.setId(UUID.randomUUID().toString());
|
||||||
e.setAppId(appKey);
|
e.setAppKey(appKey);
|
||||||
e.setUserId(userId);
|
e.setUserId(userId);
|
||||||
e.setGender(ImAccountEntity.Gender.UNKNOWN);
|
e.setGender(ImAccountEntity.Gender.UNKNOWN);
|
||||||
e.setStatus(ImAccountEntity.Status.ACTIVE);
|
e.setStatus(ImAccountEntity.Status.ACTIVE);
|
||||||
@ -67,7 +67,7 @@ public class ImAccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ImAccountEntity getAccount(String appKey, String userId) {
|
public ImAccountEntity getAccount(String appKey, String userId) {
|
||||||
return accountRepository.findByAppIdAndUserId(appKey, userId)
|
return accountRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,11 +98,11 @@ public class ImAccountService {
|
|||||||
public ImAccountEntity importAccount(String appKey, String userId, String nickname,
|
public ImAccountEntity importAccount(String appKey, String userId, String nickname,
|
||||||
String avatar, ImAccountEntity.Gender gender,
|
String avatar, ImAccountEntity.Gender gender,
|
||||||
ImAccountEntity.Status status) {
|
ImAccountEntity.Status status) {
|
||||||
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appKey, userId)
|
ImAccountEntity account = accountRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImAccountEntity entity = new ImAccountEntity();
|
ImAccountEntity entity = new ImAccountEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
return entity;
|
return entity;
|
||||||
@ -122,12 +122,12 @@ public class ImAccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteAccount(String appKey, String userId) {
|
public void deleteAccount(String appKey, String userId) {
|
||||||
accountRepository.findByAppIdAndUserId(appKey, userId)
|
accountRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
.ifPresent(accountRepository::delete);
|
.ifPresent(accountRepository::delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exists(String appKey, String userId) {
|
public boolean exists(String appKey, String userId) {
|
||||||
return accountRepository.existsByAppIdAndUserId(appKey, userId);
|
return accountRepository.existsByAppKeyAndUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImAccountEntity> searchAccounts(String appKey, String keyword, int size) {
|
public List<ImAccountEntity> searchAccounts(String appKey, String keyword, int size) {
|
||||||
|
|||||||
@ -75,7 +75,7 @@ public class ImGroupService {
|
|||||||
|
|
||||||
ImGroupEntity group = new ImGroupEntity();
|
ImGroupEntity group = new ImGroupEntity();
|
||||||
group.setId(UUID.randomUUID().toString());
|
group.setId(UUID.randomUUID().toString());
|
||||||
group.setAppId(appKey);
|
group.setAppKey(appKey);
|
||||||
group.setName(name);
|
group.setName(name);
|
||||||
group.setGroupType(normalizeGroupType(groupType));
|
group.setGroupType(normalizeGroupType(groupType));
|
||||||
group.setCreatorId(creatorId);
|
group.setCreatorId(creatorId);
|
||||||
@ -135,7 +135,7 @@ public class ImGroupService {
|
|||||||
if (changed) {
|
if (changed) {
|
||||||
group.setMemberIds(toJson(members));
|
group.setMemberIds(toJson(members));
|
||||||
ImGroupEntity saved = groupRepository.save(group);
|
ImGroupEntity saved = groupRepository.save(group);
|
||||||
webhookDispatchService.dispatch(saved.getAppId(), "group", "group.member_added",
|
webhookDispatchService.dispatch(saved.getAppKey(), "group", "group.member_added",
|
||||||
java.util.Map.of("groupId", saved.getId(), "addedUserIds", userIds, "operatorId", operatorId));
|
java.util.Map.of("groupId", saved.getId(), "addedUserIds", userIds, "operatorId", operatorId));
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
@ -153,7 +153,7 @@ public class ImGroupService {
|
|||||||
members.remove(userId);
|
members.remove(userId);
|
||||||
group.setMemberIds(toJson(members));
|
group.setMemberIds(toJson(members));
|
||||||
ImGroupEntity saved = groupRepository.save(group);
|
ImGroupEntity saved = groupRepository.save(group);
|
||||||
webhookDispatchService.dispatch(saved.getAppId(), "group", "group.member_removed",
|
webhookDispatchService.dispatch(saved.getAppKey(), "group", "group.member_removed",
|
||||||
java.util.Map.of("groupId", saved.getId(), "removedUserId", userId, "operatorId", operatorId));
|
java.util.Map.of("groupId", saved.getId(), "removedUserId", userId, "operatorId", operatorId));
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ public class ImGroupService {
|
|||||||
if (changed) {
|
if (changed) {
|
||||||
group.setMemberIds(toJson(members));
|
group.setMemberIds(toJson(members));
|
||||||
ImGroupEntity saved = groupRepository.save(group);
|
ImGroupEntity saved = groupRepository.save(group);
|
||||||
webhookDispatchService.dispatch(saved.getAppId(), "group", "group.member_removed",
|
webhookDispatchService.dispatch(saved.getAppKey(), "group", "group.member_removed",
|
||||||
java.util.Map.of("groupId", saved.getId(), "removedUserIds", userIds, "operatorId", operatorId));
|
java.util.Map.of("groupId", saved.getId(), "removedUserIds", userIds, "operatorId", operatorId));
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
@ -197,7 +197,7 @@ public class ImGroupService {
|
|||||||
group.setAnnouncement(announcement);
|
group.setAnnouncement(announcement);
|
||||||
}
|
}
|
||||||
ImGroupEntity saved = groupRepository.save(group);
|
ImGroupEntity saved = groupRepository.save(group);
|
||||||
webhookDispatchService.dispatch(saved.getAppId(), "group", "group.updated",
|
webhookDispatchService.dispatch(saved.getAppKey(), "group", "group.updated",
|
||||||
java.util.Map.of("groupId", saved.getId(), "name", saved.getName(), "operatorId", operatorId));
|
java.util.Map.of("groupId", saved.getId(), "name", saved.getName(), "operatorId", operatorId));
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupEntity> listByApp(String appKey) {
|
public List<ImGroupEntity> listByApp(String appKey) {
|
||||||
return groupRepository.findByAppId(appKey);
|
return groupRepository.findByAppKey(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupEntity> listUserGroups(String appKey, String userId) {
|
public List<ImGroupEntity> listUserGroups(String appKey, String userId) {
|
||||||
@ -277,7 +277,7 @@ public class ImGroupService {
|
|||||||
|
|
||||||
public List<ImGroupEntity> listPublicGroups(String appKey, String keyword) {
|
public List<ImGroupEntity> listPublicGroups(String appKey, String keyword) {
|
||||||
String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase();
|
String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase();
|
||||||
return groupRepository.findByAppId(appKey).stream()
|
return groupRepository.findByAppKey(appKey).stream()
|
||||||
.filter(group -> "PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType())))
|
.filter(group -> "PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType())))
|
||||||
.filter(group -> normalizedKeyword.isBlank()
|
.filter(group -> normalizedKeyword.isBlank()
|
||||||
|| group.getName().toLowerCase().contains(normalizedKeyword)
|
|| group.getName().toLowerCase().contains(normalizedKeyword)
|
||||||
@ -313,7 +313,7 @@ public class ImGroupService {
|
|||||||
throw new BusinessException(403, "当前应用未开放群加入申请");
|
throw new BusinessException(403, "当前应用未开放群加入申请");
|
||||||
}
|
}
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
if (!group.getAppId().equals(appKey)) {
|
if (!group.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
if (!"PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType()))) {
|
if (!"PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType()))) {
|
||||||
@ -322,11 +322,11 @@ public class ImGroupService {
|
|||||||
if (memberIds(group).contains(requesterId)) {
|
if (memberIds(group).contains(requesterId)) {
|
||||||
throw new BusinessException(400, "已经在群内");
|
throw new BusinessException(400, "已经在群内");
|
||||||
}
|
}
|
||||||
return joinRequestRepository.findByAppIdAndGroupIdAndRequesterId(appKey, groupId, requesterId)
|
return joinRequestRepository.findByAppKeyAndGroupIdAndRequesterId(appKey, groupId, requesterId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImGroupJoinRequestEntity entity = new ImGroupJoinRequestEntity();
|
ImGroupJoinRequestEntity entity = new ImGroupJoinRequestEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setGroupId(groupId);
|
entity.setGroupId(groupId);
|
||||||
entity.setRequesterId(requesterId);
|
entity.setRequesterId(requesterId);
|
||||||
entity.setRemark(remark);
|
entity.setRemark(remark);
|
||||||
@ -350,7 +350,7 @@ public class ImGroupService {
|
|||||||
public List<ImGroupJoinRequestEntity> listJoinRequests(String appKey, String groupId, String operatorId) {
|
public List<ImGroupJoinRequestEntity> listJoinRequests(String appKey, String groupId, String operatorId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureCanManage(group, operatorId);
|
ensureCanManage(group, operatorId);
|
||||||
return joinRequestRepository.findByAppIdAndGroupId(appKey, groupId);
|
return joinRequestRepository.findByAppKeyAndGroupId(appKey, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -442,7 +442,7 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ensureAppMatches(ImGroupEntity group, String appKey) {
|
private void ensureAppMatches(ImGroupEntity group, String appKey) {
|
||||||
if (!group.getAppId().equals(appKey)) {
|
if (!group.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -614,7 +614,7 @@ public class ImGroupService {
|
|||||||
public List<ImGroupJoinRequestEntity> adminListJoinRequests(String appKey, String groupId) {
|
public List<ImGroupJoinRequestEntity> adminListJoinRequests(String appKey, String groupId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appKey);
|
ensureAppMatches(group, appKey);
|
||||||
return joinRequestRepository.findByAppIdAndGroupId(appKey, groupId);
|
return joinRequestRepository.findByAppKeyAndGroupId(appKey, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -667,7 +667,7 @@ public class ImGroupService {
|
|||||||
group.setCreatorId(newOwnerId);
|
group.setCreatorId(newOwnerId);
|
||||||
group.setAdminIds(toJson(admins));
|
group.setAdminIds(toJson(admins));
|
||||||
ImGroupEntity saved = groupRepository.save(group);
|
ImGroupEntity saved = groupRepository.save(group);
|
||||||
webhookDispatchService.dispatch(saved.getAppId(), "group", "group.owner_transferred",
|
webhookDispatchService.dispatch(saved.getAppKey(), "group", "group.owner_transferred",
|
||||||
java.util.Map.of("groupId", saved.getId(), "newOwnerId", newOwnerId));
|
java.util.Map.of("groupId", saved.getId(), "newOwnerId", newOwnerId));
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
@ -683,7 +683,7 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
group.setExtAttributes(toJson(current));
|
group.setExtAttributes(toJson(current));
|
||||||
ImGroupEntity saved = groupRepository.save(group);
|
ImGroupEntity saved = groupRepository.save(group);
|
||||||
webhookDispatchService.dispatch(saved.getAppId(), "group", "group.attributes_updated",
|
webhookDispatchService.dispatch(saved.getAppKey(), "group", "group.attributes_updated",
|
||||||
java.util.Map.of("groupId", saved.getId(), "attributes", current));
|
java.util.Map.of("groupId", saved.getId(), "attributes", current));
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
@ -760,7 +760,7 @@ public class ImGroupService {
|
|||||||
if (recipient == null || recipient.isBlank() || recipient.equals(fromUserId)) continue;
|
if (recipient == null || recipient.isBlank() || recipient.equals(fromUserId)) continue;
|
||||||
ImMessageEntity message = new ImMessageEntity();
|
ImMessageEntity message = new ImMessageEntity();
|
||||||
message.setId(UUID.randomUUID().toString());
|
message.setId(UUID.randomUUID().toString());
|
||||||
message.setAppId(group.getAppId());
|
message.setAppKey(group.getAppKey());
|
||||||
message.setFromUserId(fromUserId);
|
message.setFromUserId(fromUserId);
|
||||||
message.setToId(recipient);
|
message.setToId(recipient);
|
||||||
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
||||||
@ -775,11 +775,11 @@ public class ImGroupService {
|
|||||||
|
|
||||||
private void dispatchJoinRequestWebhook(ImGroupEntity group, ImGroupJoinRequestEntity request, String callbackEvent) {
|
private void dispatchJoinRequestWebhook(ImGroupEntity group, ImGroupJoinRequestEntity request, String callbackEvent) {
|
||||||
webhookDispatchService.dispatch(
|
webhookDispatchService.dispatch(
|
||||||
group.getAppId(),
|
group.getAppKey(),
|
||||||
"group_join_request",
|
"group_join_request",
|
||||||
callbackEvent,
|
callbackEvent,
|
||||||
new GroupJoinRequestCallbackPayload(
|
new GroupJoinRequestCallbackPayload(
|
||||||
group.getAppId(),
|
group.getAppKey(),
|
||||||
request.getId(),
|
request.getId(),
|
||||||
request.getGroupId(),
|
request.getGroupId(),
|
||||||
group.getName(),
|
group.getName(),
|
||||||
@ -832,7 +832,7 @@ public class ImGroupService {
|
|||||||
private ImGroupJoinRequestEntity getJoinRequest(String appKey, String requestId) {
|
private ImGroupJoinRequestEntity getJoinRequest(String appKey, String requestId) {
|
||||||
ImGroupJoinRequestEntity request = joinRequestRepository.findById(requestId)
|
ImGroupJoinRequestEntity request = joinRequestRepository.findById(requestId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "加群申请不存在"));
|
.orElseThrow(() -> new BusinessException(404, "加群申请不存在"));
|
||||||
if (!request.getAppId().equals(appKey)) {
|
if (!request.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
@ -894,7 +894,7 @@ public class ImGroupService {
|
|||||||
private List<ImAccountEntity> resolveMembers(String appKey, List<String> ids) {
|
private List<ImAccountEntity> resolveMembers(String appKey, List<String> ids) {
|
||||||
List<ImAccountEntity> members = new ArrayList<>();
|
List<ImAccountEntity> members = new ArrayList<>();
|
||||||
for (String userId : ids == null ? List.<String>of() : ids) {
|
for (String userId : ids == null ? List.<String>of() : ids) {
|
||||||
accountRepository.findByAppIdAndUserId(appKey, userId).ifPresent(members::add);
|
accountRepository.findByAppKeyAndUserId(appKey, userId).ifPresent(members::add);
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ public class KeywordFilterService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String filter(String appKey, String content) {
|
public String filter(String appKey, String content) {
|
||||||
List<KeywordFilterEntity> filters = repository.findByAppIdAndEnabledTrue(appKey);
|
List<KeywordFilterEntity> filters = repository.findByAppKeyAndEnabledTrue(appKey);
|
||||||
String result = content;
|
String result = content;
|
||||||
for (KeywordFilterEntity f : filters) {
|
for (KeywordFilterEntity f : filters) {
|
||||||
try {
|
try {
|
||||||
@ -42,7 +42,7 @@ public class KeywordFilterService {
|
|||||||
public KeywordFilterEntity add(String appKey, String pattern, String replacement, String action) {
|
public KeywordFilterEntity add(String appKey, String pattern, String replacement, String action) {
|
||||||
KeywordFilterEntity entity = new KeywordFilterEntity();
|
KeywordFilterEntity entity = new KeywordFilterEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setPattern(pattern);
|
entity.setPattern(pattern);
|
||||||
entity.setReplacement(replacement);
|
entity.setReplacement(replacement);
|
||||||
entity.setAction(action);
|
entity.setAction(action);
|
||||||
@ -52,7 +52,7 @@ public class KeywordFilterService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<KeywordFilterEntity> list(String appKey) {
|
public List<KeywordFilterEntity> list(String appKey) {
|
||||||
return repository.findByAppId(appKey);
|
return repository.findByAppKey(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeywordFilterEntity update(String appKey, String id, String pattern, String replacement, String action, Boolean enabled) {
|
public KeywordFilterEntity update(String appKey, String id, String pattern, String replacement, String action, Boolean enabled) {
|
||||||
|
|||||||
@ -116,7 +116,7 @@ public class MessageService {
|
|||||||
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
||||||
? req.messageId()
|
? req.messageId()
|
||||||
: UUID.randomUUID().toString());
|
: UUID.randomUUID().toString());
|
||||||
message.setAppId(appKey);
|
message.setAppKey(appKey);
|
||||||
message.setFromUserId(fromUserId);
|
message.setFromUserId(fromUserId);
|
||||||
message.setToId(req.toId());
|
message.setToId(req.toId());
|
||||||
message.setChatType(req.chatType());
|
message.setChatType(req.chatType());
|
||||||
@ -184,14 +184,14 @@ public class MessageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFriend(String appKey, String userId, String friendId) {
|
private boolean isFriend(String appKey, String userId, String friendId) {
|
||||||
return friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
return friendRepository.existsByAppKeyAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
|| friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
|| friendRepository.existsByAppKeyAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImMessageEntity revoke(String appKey, String messageId, String requestUserId) {
|
public ImMessageEntity revoke(String appKey, String messageId, String requestUserId) {
|
||||||
ImMessageEntity message = messageRepository.findById(messageId)
|
ImMessageEntity message = messageRepository.findById(messageId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
||||||
if (!message.getAppId().equals(appKey)) {
|
if (!message.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
if (!message.getFromUserId().equals(requestUserId)) {
|
if (!message.getFromUserId().equals(requestUserId)) {
|
||||||
@ -223,7 +223,7 @@ public class MessageService {
|
|||||||
public ImMessageEntity edit(String appKey, String messageId, String requestUserId, EditMessageRequest req) {
|
public ImMessageEntity edit(String appKey, String messageId, String requestUserId, EditMessageRequest req) {
|
||||||
ImMessageEntity message = messageRepository.findById(messageId)
|
ImMessageEntity message = messageRepository.findById(messageId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
||||||
if (!message.getAppId().equals(appKey)) {
|
if (!message.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
if (!message.getFromUserId().equals(requestUserId)) {
|
if (!message.getFromUserId().equals(requestUserId)) {
|
||||||
@ -278,7 +278,7 @@ public class MessageService {
|
|||||||
public ImMessageEntity adminRevoke(String appKey, String messageId) {
|
public ImMessageEntity adminRevoke(String appKey, String messageId) {
|
||||||
ImMessageEntity message = messageRepository.findById(messageId)
|
ImMessageEntity message = messageRepository.findById(messageId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
||||||
if (!message.getAppId().equals(appKey)) {
|
if (!message.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
message.setStatus(ImMessageEntity.MsgStatus.REVOKED);
|
message.setStatus(ImMessageEntity.MsgStatus.REVOKED);
|
||||||
@ -341,7 +341,7 @@ public class MessageService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<ImMessageEntity> messages = messageRepository
|
List<ImMessageEntity> messages = messageRepository
|
||||||
.findByAppIdAndFromUserIdAndToIdAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
.findByAppKeyAndFromUserIdAndToIdAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
||||||
appKey, peerId, readerId, readAt);
|
appKey, peerId, readerId, readAt);
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@ -374,7 +374,7 @@ public class MessageService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<ImMessageEntity> messages = messageRepository
|
List<ImMessageEntity> messages = messageRepository
|
||||||
.findByAppIdAndToIdAndChatTypeAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
.findByAppKeyAndToIdAndChatTypeAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
||||||
appKey, groupId, ImMessageEntity.ChatType.GROUP, readAt);
|
appKey, groupId, ImMessageEntity.ChatType.GROUP, readAt);
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@ -491,12 +491,12 @@ public class MessageService {
|
|||||||
|
|
||||||
public List<GroupReadReceiptSummary> groupReadReceipts(String appKey, String groupId, List<String> messageIds) {
|
public List<GroupReadReceiptSummary> groupReadReceipts(String appKey, String groupId, List<String> messageIds) {
|
||||||
ImGroupEntity group = groupService.get(groupId);
|
ImGroupEntity group = groupService.get(groupId);
|
||||||
if (!group.getAppId().equals(appKey)) {
|
if (!group.getAppKey().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
List<String> members = groupService.memberIds(group);
|
List<String> members = groupService.memberIds(group);
|
||||||
return messageRepository.findAllById(messageIds == null ? List.of() : messageIds).stream()
|
return messageRepository.findAllById(messageIds == null ? List.of() : messageIds).stream()
|
||||||
.filter(message -> appKey.equals(message.getAppId()))
|
.filter(message -> appKey.equals(message.getAppKey()))
|
||||||
.filter(message -> groupId.equals(message.getToId()))
|
.filter(message -> groupId.equals(message.getToId()))
|
||||||
.filter(message -> message.getChatType() == ImMessageEntity.ChatType.GROUP)
|
.filter(message -> message.getChatType() == ImMessageEntity.ChatType.GROUP)
|
||||||
.map(message -> {
|
.map(message -> {
|
||||||
@ -520,7 +520,7 @@ public class MessageService {
|
|||||||
try {
|
try {
|
||||||
Map<String, Object> payload = new java.util.LinkedHashMap<>();
|
Map<String, Object> payload = new java.util.LinkedHashMap<>();
|
||||||
payload.put("messageId", message.getId());
|
payload.put("messageId", message.getId());
|
||||||
payload.put("appKey", message.getAppId());
|
payload.put("appKey", message.getAppKey());
|
||||||
payload.put("fromUserId", message.getFromUserId());
|
payload.put("fromUserId", message.getFromUserId());
|
||||||
payload.put("toId", message.getToId());
|
payload.put("toId", message.getToId());
|
||||||
payload.put("chatType", message.getChatType().name());
|
payload.put("chatType", message.getChatType().name());
|
||||||
@ -598,7 +598,7 @@ public class MessageService {
|
|||||||
public ImMessageEntity adminSend(String appKey, String fromUserId, String toId, ImMessageEntity.MsgType msgType, String content) {
|
public ImMessageEntity adminSend(String appKey, String fromUserId, String toId, ImMessageEntity.MsgType msgType, String content) {
|
||||||
ImMessageEntity message = new ImMessageEntity();
|
ImMessageEntity message = new ImMessageEntity();
|
||||||
message.setId(UUID.randomUUID().toString());
|
message.setId(UUID.randomUUID().toString());
|
||||||
message.setAppId(appKey);
|
message.setAppKey(appKey);
|
||||||
message.setFromUserId(fromUserId);
|
message.setFromUserId(fromUserId);
|
||||||
message.setToId(toId);
|
message.setToId(toId);
|
||||||
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
||||||
@ -624,7 +624,7 @@ public class MessageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void adminSetMsgRead(String appKey, String userId) {
|
public void adminSetMsgRead(String appKey, String userId) {
|
||||||
List<ImMessageEntity> messages = messageRepository.findUnreadByAppIdAndToId(appKey, userId);
|
List<ImMessageEntity> messages = messageRepository.findUnreadByAppKeyAndToId(appKey, userId);
|
||||||
for (ImMessageEntity message : messages) {
|
for (ImMessageEntity message : messages) {
|
||||||
message.setStatus(ImMessageEntity.MsgStatus.READ);
|
message.setStatus(ImMessageEntity.MsgStatus.READ);
|
||||||
messageRepository.save(message);
|
messageRepository.save(message);
|
||||||
@ -642,7 +642,7 @@ public class MessageService {
|
|||||||
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
||||||
? req.messageId()
|
? req.messageId()
|
||||||
: UUID.randomUUID().toString());
|
: UUID.randomUUID().toString());
|
||||||
message.setAppId(appKey);
|
message.setAppKey(appKey);
|
||||||
message.setFromUserId(req.fromUserId());
|
message.setFromUserId(req.fromUserId());
|
||||||
message.setToId(req.toId());
|
message.setToId(req.toId());
|
||||||
message.setChatType(req.chatType() != null ? req.chatType() : ImMessageEntity.ChatType.SINGLE);
|
message.setChatType(req.chatType() != null ? req.chatType() : ImMessageEntity.ChatType.SINGLE);
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class OfflineMessageSyncService {
|
|||||||
public void storeOfflineMessage(String appKey, String userId, String messageId) {
|
public void storeOfflineMessage(String appKey, String userId, String messageId) {
|
||||||
ImOfflineMessageEntity entity = new ImOfflineMessageEntity();
|
ImOfflineMessageEntity entity = new ImOfflineMessageEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setMessageId(messageId);
|
entity.setMessageId(messageId);
|
||||||
entity.setDelivered(false);
|
entity.setDelivered(false);
|
||||||
@ -48,7 +48,7 @@ public class OfflineMessageSyncService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void syncAndDeliver(String appKey, String userId) {
|
public void syncAndDeliver(String appKey, String userId) {
|
||||||
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
||||||
.findByAppIdAndUserIdAndDeliveredFalse(appKey, userId);
|
.findByAppKeyAndUserIdAndDeliveredFalse(appKey, userId);
|
||||||
if (offlineMessages.isEmpty()) {
|
if (offlineMessages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -68,17 +68,17 @@ public class OfflineMessageSyncService {
|
|||||||
log.info("Synced {} offline messages for appKey={} userId={}", deliveredIds.size(), appKey, userId);
|
log.info("Synced {} offline messages for appKey={} userId={}", deliveredIds.size(), appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
offlineMessageRepository.deleteByAppIdAndUserIdAndDeliveredTrue(appKey, userId);
|
offlineMessageRepository.deleteByAppKeyAndUserIdAndDeliveredTrue(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long countUndelivered(String appKey, String userId) {
|
public long countUndelivered(String appKey, String userId) {
|
||||||
return offlineMessageRepository.countUndeliveredByAppIdAndUserId(appKey, userId);
|
return offlineMessageRepository.countUndeliveredByAppKeyAndUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ImMessageEntity> syncAndReturn(String appKey, String userId) {
|
public List<ImMessageEntity> syncAndReturn(String appKey, String userId) {
|
||||||
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
||||||
.findByAppIdAndUserIdAndDeliveredFalse(appKey, userId);
|
.findByAppKeyAndUserIdAndDeliveredFalse(appKey, userId);
|
||||||
List<ImMessageEntity> result = new ArrayList<>();
|
List<ImMessageEntity> result = new ArrayList<>();
|
||||||
List<String> deliveredIds = new ArrayList<>();
|
List<String> deliveredIds = new ArrayList<>();
|
||||||
for (ImOfflineMessageEntity offline : offlineMessages) {
|
for (ImOfflineMessageEntity offline : offlineMessages) {
|
||||||
@ -91,7 +91,7 @@ public class OfflineMessageSyncService {
|
|||||||
if (!deliveredIds.isEmpty()) {
|
if (!deliveredIds.isEmpty()) {
|
||||||
offlineMessageRepository.markDeliveredByIds(deliveredIds);
|
offlineMessageRepository.markDeliveredByIds(deliveredIds);
|
||||||
}
|
}
|
||||||
offlineMessageRepository.deleteByAppIdAndUserIdAndDeliveredTrue(appKey, userId);
|
offlineMessageRepository.deleteByAppKeyAndUserIdAndDeliveredTrue(appKey, userId);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ public class OperationLogService {
|
|||||||
String detail) {
|
String detail) {
|
||||||
ImOperationLogEntity entity = new ImOperationLogEntity();
|
ImOperationLogEntity entity = new ImOperationLogEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setOperatorId(operatorId == null || operatorId.isBlank() ? "system" : operatorId);
|
entity.setOperatorId(operatorId == null || operatorId.isBlank() ? "system" : operatorId);
|
||||||
entity.setAction(action);
|
entity.setAction(action);
|
||||||
entity.setResourceType(resourceType);
|
entity.setResourceType(resourceType);
|
||||||
@ -38,6 +38,6 @@ public class OperationLogService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Page<ImOperationLogEntity> list(String appKey, Pageable pageable) {
|
public Page<ImOperationLogEntity> list(String appKey, Pageable pageable) {
|
||||||
return repository.findByAppIdOrderByCreatedAtDesc(appKey, pageable);
|
return repository.findByAppKeyOrderByCreatedAtDesc(appKey, pageable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public class WebhookConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<WebhookConfigEntity> list(String appKey) {
|
public List<WebhookConfigEntity> list(String appKey) {
|
||||||
return repository.findByAppId(appKey);
|
return repository.findByAppKey(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebhookConfigEntity get(String appKey, String id) {
|
public WebhookConfigEntity get(String appKey, String id) {
|
||||||
@ -30,7 +30,7 @@ public class WebhookConfigService {
|
|||||||
public WebhookConfigEntity create(String appKey, String url, String secret, Boolean enabled) {
|
public WebhookConfigEntity create(String appKey, String url, String secret, Boolean enabled) {
|
||||||
WebhookConfigEntity entity = new WebhookConfigEntity();
|
WebhookConfigEntity entity = new WebhookConfigEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setUrl(url);
|
entity.setUrl(url);
|
||||||
entity.setSecret(secret);
|
entity.setSecret(secret);
|
||||||
entity.setEnabled(enabled == null || enabled);
|
entity.setEnabled(enabled == null || enabled);
|
||||||
|
|||||||
@ -61,7 +61,7 @@ public class WebhookDispatchService {
|
|||||||
|
|
||||||
@Async
|
@Async
|
||||||
public void dispatch(String appKey, String callbackType, String callbackEvent, Object payload) {
|
public void dispatch(String appKey, String callbackType, String callbackEvent, Object payload) {
|
||||||
List<WebhookConfigEntity> webhooks = webhookRepository.findByAppIdAndEnabledTrue(appKey);
|
List<WebhookConfigEntity> webhooks = webhookRepository.findByAppKeyAndEnabledTrue(appKey);
|
||||||
if (webhooks.isEmpty()) {
|
if (webhooks.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ public class WebhookDispatchService {
|
|||||||
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||||
WebhookDeliveryEntity delivery = new WebhookDeliveryEntity();
|
WebhookDeliveryEntity delivery = new WebhookDeliveryEntity();
|
||||||
delivery.setId(UUID.randomUUID().toString());
|
delivery.setId(UUID.randomUUID().toString());
|
||||||
delivery.setAppId(appKey);
|
delivery.setAppKey(appKey);
|
||||||
delivery.setCallbackId(callbackId);
|
delivery.setCallbackId(callbackId);
|
||||||
delivery.setCallbackEvent(callbackEvent);
|
delivery.setCallbackEvent(callbackEvent);
|
||||||
delivery.setUrl(webhook.getUrl());
|
delivery.setUrl(webhook.getUrl());
|
||||||
@ -112,7 +112,7 @@ public class WebhookDispatchService {
|
|||||||
.uri(URI.create(webhook.getUrl()))
|
.uri(URI.create(webhook.getUrl()))
|
||||||
.timeout(Duration.ofMillis(webhookTimeoutMs))
|
.timeout(Duration.ofMillis(webhookTimeoutMs))
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.header("X-App-Id", appKey)
|
.header("X-App-Key", appKey)
|
||||||
.header("X-App-Timestamp", String.valueOf(requestTime))
|
.header("X-App-Timestamp", String.valueOf(requestTime))
|
||||||
.header("X-App-Nonce", nonce)
|
.header("X-App-Nonce", nonce)
|
||||||
.header("X-App-Signature", signature)
|
.header("X-App-Signature", signature)
|
||||||
@ -182,7 +182,7 @@ public class WebhookDispatchService {
|
|||||||
|
|
||||||
WebhookAlertEntity alert = new WebhookAlertEntity();
|
WebhookAlertEntity alert = new WebhookAlertEntity();
|
||||||
alert.setId(UUID.randomUUID().toString());
|
alert.setId(UUID.randomUUID().toString());
|
||||||
alert.setAppId(appKey);
|
alert.setAppKey(appKey);
|
||||||
alert.setWebhookId(webhook.getId());
|
alert.setWebhookId(webhook.getId());
|
||||||
alert.setWebhookUrl(webhook.getUrl());
|
alert.setWebhookUrl(webhook.getUrl());
|
||||||
alert.setAlertType("AUTO_DISABLED");
|
alert.setAlertType("AUTO_DISABLED");
|
||||||
|
|||||||
48
maven-settings.xml
普通文件
48
maven-settings.xml
普通文件
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0
|
||||||
|
https://maven.apache.org/xsd/settings-1.2.0.xsd">
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>xuqm-nexus</id>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>xuqm-maven-public</id>
|
||||||
|
<url>https://nexus.xuqinmin.com/repository/maven-public/</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>xuqm-maven-snapshots</id>
|
||||||
|
<url>https://nexus.xuqinmin.com/repository/maven-snapshots/</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>xuqm-maven-public</id>
|
||||||
|
<url>https://nexus.xuqinmin.com/repository/maven-public/</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
<activeProfiles>
|
||||||
|
<activeProfile>xuqm-nexus</activeProfile>
|
||||||
|
</activeProfiles>
|
||||||
|
</settings>
|
||||||
1
pom.xml
1
pom.xml
@ -15,7 +15,6 @@
|
|||||||
<module>common</module>
|
<module>common</module>
|
||||||
<module>tenant-service</module>
|
<module>tenant-service</module>
|
||||||
<module>im-service</module>
|
<module>im-service</module>
|
||||||
<module>im-sdk</module>
|
|
||||||
<module>push-service</module>
|
<module>push-service</module>
|
||||||
<module>update-service</module>
|
<module>update-service</module>
|
||||||
<module>demo-service</module>
|
<module>demo-service</module>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "push_device_login_log", indexes = {
|
@Table(name = "push_device_login_log", indexes = {
|
||||||
@Index(name = "idx_push_device_log_user_time", columnList = "appId,userId,createdAt"),
|
@Index(name = "idx_push_device_log_user_time", columnList = "appKey,userId,createdAt"),
|
||||||
@Index(name = "idx_push_device_log_token", columnList = "tokenHash")
|
@Index(name = "idx_push_device_log_token", columnList = "tokenHash")
|
||||||
})
|
})
|
||||||
public class DeviceLoginLogEntity {
|
public class DeviceLoginLogEntity {
|
||||||
@ -24,7 +24,7 @@ public class DeviceLoginLogEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -70,8 +70,8 @@ public class DeviceLoginLogEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -11,18 +11,18 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "push_device_token",
|
@Table(name = "push_device_token",
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"appId", "userId", "deviceId"}))
|
uniqueConstraints = @UniqueConstraint(columnNames = {"appKey", "userId", "deviceId"}))
|
||||||
public class DeviceTokenEntity {
|
public class DeviceTokenEntity {
|
||||||
|
|
||||||
public enum Vendor {
|
public enum Vendor {
|
||||||
HUAWEI, XIAOMI, OPPO, VIVO, HONOR, HARMONY, FCM, APNS
|
HUAWEI, XIAOMI, OPPO, VIVO, HONOR, HARMONY, APNS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String userId;
|
private String userId;
|
||||||
@ -67,8 +67,8 @@ public class DeviceTokenEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getUserId() { return userId; }
|
public String getUserId() { return userId; }
|
||||||
public void setUserId(String userId) { this.userId = userId; }
|
public void setUserId(String userId) { this.userId = userId; }
|
||||||
|
|||||||
@ -6,5 +6,5 @@ import org.springframework.data.domain.Pageable;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
public interface DeviceLoginLogRepository extends JpaRepository<DeviceLoginLogEntity, String> {
|
public interface DeviceLoginLogRepository extends JpaRepository<DeviceLoginLogEntity, String> {
|
||||||
Page<DeviceLoginLogEntity> findByAppIdAndUserIdOrderByCreatedAtDesc(String appId, String userId, Pageable pageable);
|
Page<DeviceLoginLogEntity> findByAppKeyAndUserIdOrderByCreatedAtDesc(String appKey, String userId, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,16 +7,16 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface DeviceTokenRepository extends JpaRepository<DeviceTokenEntity, String> {
|
public interface DeviceTokenRepository extends JpaRepository<DeviceTokenEntity, String> {
|
||||||
List<DeviceTokenEntity> findByAppIdAndUserIdAndReceivePushTrue(String appId, String userId);
|
List<DeviceTokenEntity> findByAppKeyAndUserIdAndReceivePushTrue(String appKey, String userId);
|
||||||
Optional<DeviceTokenEntity> findFirstByAppIdAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(
|
Optional<DeviceTokenEntity> findFirstByAppKeyAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(
|
||||||
String appId, String userId);
|
String appKey, String userId);
|
||||||
List<DeviceTokenEntity> findByAppIdAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(String appId, String userId);
|
List<DeviceTokenEntity> findByAppKeyAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(String appKey, String userId);
|
||||||
Optional<DeviceTokenEntity> findByAppIdAndUserIdAndVendor(
|
Optional<DeviceTokenEntity> findByAppKeyAndUserIdAndVendor(
|
||||||
String appId, String userId, DeviceTokenEntity.Vendor vendor);
|
String appKey, String userId, DeviceTokenEntity.Vendor vendor);
|
||||||
Optional<DeviceTokenEntity> findByAppIdAndUserIdAndDeviceId(String appId, String userId, String deviceId);
|
Optional<DeviceTokenEntity> findByAppKeyAndUserIdAndDeviceId(String appKey, String userId, String deviceId);
|
||||||
Optional<DeviceTokenEntity> findFirstByToken(String token);
|
Optional<DeviceTokenEntity> findFirstByToken(String token);
|
||||||
List<DeviceTokenEntity> findByAppIdAndUserId(String appId, String userId);
|
List<DeviceTokenEntity> findByAppKeyAndUserId(String appKey, String userId);
|
||||||
List<DeviceTokenEntity> findByAppIdAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(String appId, String userId);
|
List<DeviceTokenEntity> findByAppKeyAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(String appKey, String userId);
|
||||||
void deleteByAppIdAndUserIdAndVendor(String appId, String userId, DeviceTokenEntity.Vendor vendor);
|
void deleteByAppKeyAndUserIdAndVendor(String appKey, String userId, DeviceTokenEntity.Vendor vendor);
|
||||||
void deleteByAppIdAndUserIdAndDeviceId(String appId, String userId, String deviceId);
|
void deleteByAppKeyAndUserIdAndDeviceId(String appKey, String userId, String deviceId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,15 +35,15 @@ public class PushDiagnosticsService {
|
|||||||
this.jwtUtil = jwtUtil;
|
this.jwtUtil = jwtUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PushTokenDiagnostics searchByToken(String token, String appIdHint) {
|
public PushTokenDiagnostics searchByToken(String token, String appKeyHint) {
|
||||||
Optional<DeviceTokenEntity> tokenMatch = tokenRepository.findFirstByToken(token);
|
Optional<DeviceTokenEntity> tokenMatch = tokenRepository.findFirstByToken(token);
|
||||||
String appKey = appIdHint;
|
String appKey = appKeyHint;
|
||||||
String userId = null;
|
String userId = null;
|
||||||
String tokenType = "UNKNOWN";
|
String tokenType = "UNKNOWN";
|
||||||
|
|
||||||
if (tokenMatch.isPresent()) {
|
if (tokenMatch.isPresent()) {
|
||||||
DeviceTokenEntity device = tokenMatch.get();
|
DeviceTokenEntity device = tokenMatch.get();
|
||||||
appKey = device.getAppId();
|
appKey = device.getAppKey();
|
||||||
userId = device.getUserId();
|
userId = device.getUserId();
|
||||||
tokenType = "PUSH";
|
tokenType = "PUSH";
|
||||||
} else {
|
} else {
|
||||||
@ -56,8 +56,8 @@ public class PushDiagnosticsService {
|
|||||||
try {
|
try {
|
||||||
Claims claims = jwtUtil.parse(token);
|
Claims claims = jwtUtil.parse(token);
|
||||||
userId = claims.getSubject();
|
userId = claims.getSubject();
|
||||||
String claimAppId = claims.get("appKey", String.class);
|
String claimAppKey = claims.get("appKey", String.class);
|
||||||
appKey = claimAppId == null || claimAppId.isBlank() ? appIdHint : claimAppId;
|
appKey = claimAppKey == null || claimAppKey.isBlank() ? appKeyHint : claimAppKey;
|
||||||
tokenType = "IM";
|
tokenType = "IM";
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
// Keep UNKNOWN and return an empty diagnostic.
|
// Keep UNKNOWN and return an empty diagnostic.
|
||||||
@ -71,7 +71,7 @@ public class PushDiagnosticsService {
|
|||||||
|
|
||||||
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
|
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
|
||||||
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
|
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
|
||||||
List<DeviceTokenEntity> devices = tokenRepository.findByAppIdAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
List<DeviceTokenEntity> devices = tokenRepository.findByAppKeyAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
||||||
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
|
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(DeviceInfo::from)
|
.map(DeviceInfo::from)
|
||||||
@ -93,7 +93,7 @@ public class PushDiagnosticsService {
|
|||||||
public PushTokenDiagnostics searchByUserId(String appKey, String userId) {
|
public PushTokenDiagnostics searchByUserId(String appKey, String userId) {
|
||||||
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
|
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
|
||||||
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
|
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
|
||||||
List<DeviceTokenEntity> devices = tokenRepository.findByAppIdAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
List<DeviceTokenEntity> devices = tokenRepository.findByAppKeyAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
||||||
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
|
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(DeviceInfo::from)
|
.map(DeviceInfo::from)
|
||||||
@ -126,7 +126,7 @@ public class PushDiagnosticsService {
|
|||||||
public Page<DeviceLoginLogEntity> deviceLogs(String appKey, String userId, int page, int size) {
|
public Page<DeviceLoginLogEntity> deviceLogs(String appKey, String userId, int page, int size) {
|
||||||
int safePage = Math.max(page, 0);
|
int safePage = Math.max(page, 0);
|
||||||
int safeSize = Math.min(Math.max(size, 1), 200);
|
int safeSize = Math.min(Math.max(size, 1), 200);
|
||||||
return logRepository.findByAppIdAndUserIdOrderByCreatedAtDesc(appKey, userId, PageRequest.of(safePage, safeSize));
|
return logRepository.findByAppKeyAndUserIdOrderByCreatedAtDesc(appKey, userId, PageRequest.of(safePage, safeSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PushTokenDiagnostics(
|
public record PushTokenDiagnostics(
|
||||||
|
|||||||
@ -68,17 +68,22 @@ public class PushDispatcher {
|
|||||||
String routeType = routeType(payload);
|
String routeType = routeType(payload);
|
||||||
String platform = platformForVendor(vendor, payload);
|
String platform = platformForVendor(vendor, payload);
|
||||||
return pushConfigClient.loadServiceConfig(appKey, platform, "PUSH")
|
return pushConfigClient.loadServiceConfig(appKey, platform, "PUSH")
|
||||||
.map(config -> {
|
.map(config -> profileFor(config, vendor.name(), routeType)
|
||||||
JsonNode route = config.path("routing").path(routeType);
|
.map(profile -> new PushSendOptions(
|
||||||
String channelKey = route.path("channel").asText("");
|
profile.path("key").asText(""),
|
||||||
String channelId = effectiveChannelId(config.path("channels"), channelKey);
|
routeType,
|
||||||
return new PushSendOptions(
|
profile.path("channelId").asText(""),
|
||||||
routeType,
|
profile.path("category").asText(""),
|
||||||
channelId,
|
profile.path("threadIdentifier").asText(""),
|
||||||
route.path("category").asText(""),
|
profile.path("interruptionLevel").asText(""),
|
||||||
route.path("priority").asText(""));
|
profile.path("importance").asText(""),
|
||||||
})
|
readBoolean(profile, "badge"),
|
||||||
.orElseGet(() -> new PushSendOptions(routeType, "", "", ""));
|
readBoolean(profile, "sound"),
|
||||||
|
readBoolean(profile, "vibration"),
|
||||||
|
readInteger(profile, "notifyType"),
|
||||||
|
mapPriority(profile.path("importance").asText(""))))
|
||||||
|
.orElseGet(() -> new PushSendOptions("", routeType, "", "", "", "", "", null, null, null, null, "")))
|
||||||
|
.orElseGet(() -> new PushSendOptions("", routeType, "", "", "", "", "", null, null, null, null, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String platformForVendor(DeviceTokenEntity.Vendor vendor, String payload) {
|
private String platformForVendor(DeviceTokenEntity.Vendor vendor, String payload) {
|
||||||
@ -121,18 +126,73 @@ public class PushDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String effectiveChannelId(JsonNode channels, String channelKey) {
|
private java.util.Optional<JsonNode> profileFor(JsonNode config, String vendor, String routeType) {
|
||||||
if (channels == null || !channels.isArray() || channelKey == null || channelKey.isBlank()) {
|
JsonNode profiles = config.path("profiles");
|
||||||
return "";
|
if (profiles == null || !profiles.isArray()) {
|
||||||
|
return java.util.Optional.empty();
|
||||||
}
|
}
|
||||||
for (JsonNode channel : channels) {
|
JsonNode fallback = null;
|
||||||
if (channelKey.equals(channel.path("key").asText(""))) {
|
for (JsonNode profile : profiles) {
|
||||||
String base = channel.path("channelId").asText(channelKey);
|
if (!profile.path("enabled").asBoolean(true)) {
|
||||||
int version = Math.max(channel.path("version").asInt(1), 1);
|
continue;
|
||||||
return base + "_v" + version;
|
|
||||||
}
|
}
|
||||||
|
if (!vendor.equalsIgnoreCase(profile.path("vendor").asText(""))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String profileRouteType = profile.path("routeType").asText("");
|
||||||
|
if (profileRouteType.isBlank() || "DEFAULT".equalsIgnoreCase(profileRouteType)) {
|
||||||
|
if (fallback == null) {
|
||||||
|
fallback = profile;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!routeType.equalsIgnoreCase(profileRouteType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return java.util.Optional.of(profile);
|
||||||
}
|
}
|
||||||
return "";
|
return java.util.Optional.ofNullable(fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean readBoolean(JsonNode node, String key) {
|
||||||
|
if (node == null || node.isNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JsonNode value = node.get(key);
|
||||||
|
if (value == null || value.isNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value.asBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer readInteger(JsonNode node, String key) {
|
||||||
|
if (node == null || node.isNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JsonNode value = node.get(key);
|
||||||
|
if (value == null || value.isNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value.isInt() || value.isLong() || value.isNumber()) {
|
||||||
|
return value.asInt();
|
||||||
|
}
|
||||||
|
String text = value.asText("");
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(text.trim());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mapPriority(String importance) {
|
||||||
|
return switch (importance == null ? "" : importance.trim().toUpperCase()) {
|
||||||
|
case "HIGH", "MAX" -> "HIGH";
|
||||||
|
case "LOW", "MIN" -> "LOW";
|
||||||
|
default -> "DEFAULT";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DeviceTokenEntity> selectedPushTargets(String appKey, String userId) {
|
public List<DeviceTokenEntity> selectedPushTargets(String appKey, String userId) {
|
||||||
@ -150,7 +210,7 @@ public class PushDispatcher {
|
|||||||
|
|
||||||
private List<DeviceTokenEntity> selectTargets(String appKey, String userId) {
|
private List<DeviceTokenEntity> selectTargets(String appKey, String userId) {
|
||||||
List<DeviceTokenEntity> devices = tokenRepository
|
List<DeviceTokenEntity> devices = tokenRepository
|
||||||
.findByAppIdAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
.findByAppKeyAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
||||||
if (devices.isEmpty()) {
|
if (devices.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
@ -185,15 +245,15 @@ public class PushDispatcher {
|
|||||||
String osVersion,
|
String osVersion,
|
||||||
String appVersion) {
|
String appVersion) {
|
||||||
String resolvedDeviceId = normalizeDeviceId(deviceId, vendor, token);
|
String resolvedDeviceId = normalizeDeviceId(deviceId, vendor, token);
|
||||||
Optional<DeviceTokenEntity> existing = tokenRepository.findByAppIdAndUserIdAndDeviceId(appKey, userId, resolvedDeviceId);
|
Optional<DeviceTokenEntity> existing = tokenRepository.findByAppKeyAndUserIdAndDeviceId(appKey, userId, resolvedDeviceId);
|
||||||
if (existing.isEmpty() && (deviceId == null || deviceId.isBlank())) {
|
if (existing.isEmpty() && (deviceId == null || deviceId.isBlank())) {
|
||||||
existing = tokenRepository.findByAppIdAndUserIdAndVendor(appKey, userId, vendor);
|
existing = tokenRepository.findByAppKeyAndUserIdAndVendor(appKey, userId, vendor);
|
||||||
}
|
}
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
DeviceTokenEntity entity = existing.orElseGet(() -> {
|
DeviceTokenEntity entity = existing.orElseGet(() -> {
|
||||||
DeviceTokenEntity e = new DeviceTokenEntity();
|
DeviceTokenEntity e = new DeviceTokenEntity();
|
||||||
e.setId(UUID.randomUUID().toString());
|
e.setId(UUID.randomUUID().toString());
|
||||||
e.setAppId(appKey);
|
e.setAppKey(appKey);
|
||||||
e.setUserId(userId);
|
e.setUserId(userId);
|
||||||
e.setVendor(vendor);
|
e.setVendor(vendor);
|
||||||
e.setCreatedAt(now);
|
e.setCreatedAt(now);
|
||||||
@ -216,8 +276,8 @@ public class PushDispatcher {
|
|||||||
|
|
||||||
public void setReceivePush(String appKey, String userId, String deviceId, boolean enabled) {
|
public void setReceivePush(String appKey, String userId, String deviceId, boolean enabled) {
|
||||||
List<DeviceTokenEntity> tokens = deviceId == null || deviceId.isBlank()
|
List<DeviceTokenEntity> tokens = deviceId == null || deviceId.isBlank()
|
||||||
? tokenRepository.findByAppIdAndUserId(appKey, userId)
|
? tokenRepository.findByAppKeyAndUserId(appKey, userId)
|
||||||
: tokenRepository.findByAppIdAndUserIdAndDeviceId(appKey, userId, deviceId).stream().toList();
|
: tokenRepository.findByAppKeyAndUserIdAndDeviceId(appKey, userId, deviceId).stream().toList();
|
||||||
for (DeviceTokenEntity token : tokens) {
|
for (DeviceTokenEntity token : tokens) {
|
||||||
token.setReceivePush(enabled);
|
token.setReceivePush(enabled);
|
||||||
token.setUpdatedAt(LocalDateTime.now());
|
token.setUpdatedAt(LocalDateTime.now());
|
||||||
@ -228,20 +288,20 @@ public class PushDispatcher {
|
|||||||
|
|
||||||
public void unregisterToken(String appKey, String userId, DeviceTokenEntity.Vendor vendor, String deviceId) {
|
public void unregisterToken(String appKey, String userId, DeviceTokenEntity.Vendor vendor, String deviceId) {
|
||||||
if (deviceId != null && !deviceId.isBlank()) {
|
if (deviceId != null && !deviceId.isBlank()) {
|
||||||
tokenRepository.findByAppIdAndUserIdAndDeviceId(appKey, userId, deviceId)
|
tokenRepository.findByAppKeyAndUserIdAndDeviceId(appKey, userId, deviceId)
|
||||||
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
||||||
tokenRepository.deleteByAppIdAndUserIdAndDeviceId(appKey, userId, deviceId);
|
tokenRepository.deleteByAppKeyAndUserIdAndDeviceId(appKey, userId, deviceId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tokenRepository.findByAppIdAndUserIdAndVendor(appKey, userId, vendor)
|
tokenRepository.findByAppKeyAndUserIdAndVendor(appKey, userId, vendor)
|
||||||
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
||||||
tokenRepository.deleteByAppIdAndUserIdAndVendor(appKey, userId, vendor);
|
tokenRepository.deleteByAppKeyAndUserIdAndVendor(appKey, userId, vendor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordLog(DeviceTokenEntity token, DeviceLoginLogEntity.EventType eventType) {
|
private void recordLog(DeviceTokenEntity token, DeviceLoginLogEntity.EventType eventType) {
|
||||||
DeviceLoginLogEntity entity = new DeviceLoginLogEntity();
|
DeviceLoginLogEntity entity = new DeviceLoginLogEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(token.getAppId());
|
entity.setAppKey(token.getAppKey());
|
||||||
entity.setUserId(token.getUserId());
|
entity.setUserId(token.getUserId());
|
||||||
entity.setVendor(token.getVendor());
|
entity.setVendor(token.getVendor());
|
||||||
entity.setTokenHash(hashToken(token.getToken()));
|
entity.setTokenHash(hashToken(token.getToken()));
|
||||||
|
|||||||
@ -82,16 +82,31 @@ public class ApnsPushProvider implements PushProvider {
|
|||||||
String url = (production ? productionPushUrl : sandboxPushUrl).replace("{token}", token);
|
String url = (production ? productionPushUrl : sandboxPushUrl).replace("{token}", token);
|
||||||
Map<String, Object> aps = new java.util.LinkedHashMap<>();
|
Map<String, Object> aps = new java.util.LinkedHashMap<>();
|
||||||
aps.put("alert", Map.of("title", title, "body", body));
|
aps.put("alert", Map.of("title", title, "body", body));
|
||||||
aps.put("sound", "default");
|
if (options == null || options.sound() == null || options.sound()) {
|
||||||
|
aps.put("sound", "default");
|
||||||
|
}
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
if (options.category() != null && !options.category().isBlank()) {
|
if (options.category() != null && !options.category().isBlank()) {
|
||||||
aps.put("category", options.category());
|
aps.put("category", options.category());
|
||||||
}
|
}
|
||||||
if (options.routeType() != null && !options.routeType().isBlank()) {
|
if (options.threadIdentifier() != null && !options.threadIdentifier().isBlank()) {
|
||||||
|
aps.put("thread-id", options.threadIdentifier());
|
||||||
|
} else if (options.routeType() != null && !options.routeType().isBlank()) {
|
||||||
aps.put("thread-id", options.routeType());
|
aps.put("thread-id", options.routeType());
|
||||||
}
|
}
|
||||||
if ("HIGH".equalsIgnoreCase(options.priority())) {
|
if ("HIGH".equalsIgnoreCase(options.priority()) || "HIGH".equalsIgnoreCase(options.interruptionLevel())) {
|
||||||
aps.put("interruption-level", "time-sensitive");
|
aps.put("interruption-level", "time-sensitive");
|
||||||
|
} else if ("CRITICAL".equalsIgnoreCase(options.interruptionLevel())) {
|
||||||
|
aps.put("interruption-level", "critical");
|
||||||
|
} else if ("PASSIVE".equalsIgnoreCase(options.interruptionLevel())) {
|
||||||
|
aps.put("interruption-level", "passive");
|
||||||
|
} else if ("ACTIVE".equalsIgnoreCase(options.interruptionLevel())) {
|
||||||
|
aps.put("interruption-level", "active");
|
||||||
|
}
|
||||||
|
if (options.badge() != null) {
|
||||||
|
if (Boolean.TRUE.equals(options.badge())) {
|
||||||
|
aps.put("badge", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<String, Object> message = new java.util.LinkedHashMap<>();
|
Map<String, Object> message = new java.util.LinkedHashMap<>();
|
||||||
@ -153,7 +168,7 @@ public class ApnsPushProvider implements PushProvider {
|
|||||||
|
|
||||||
private String resolveConfig(String appKey, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "IOS", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "IOS", "PUSH")
|
||||||
.map(node -> node.path("apns"))
|
.map(node -> node.path("vendors").path("apns"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return fallback == null ? "" : fallback;
|
return fallback == null ? "" : fallback;
|
||||||
|
|||||||
@ -1,159 +0,0 @@
|
|||||||
package com.xuqm.push.service.provider;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.xuqm.push.service.TenantPushConfigClient;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class FcmPushProvider implements PushProvider {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(FcmPushProvider.class);
|
|
||||||
|
|
||||||
@Value("${push.fcm.project-id:}")
|
|
||||||
private String envProjectId;
|
|
||||||
|
|
||||||
@Value("${push.fcm.service-account-json:}")
|
|
||||||
private String envServiceAccountJson;
|
|
||||||
|
|
||||||
@Value("${push.fcm.token-url:https://oauth2.googleapis.com/token}")
|
|
||||||
private String tokenUrl;
|
|
||||||
|
|
||||||
@Value("${push.fcm.push-url:https://fcm.googleapis.com/v1/projects/{projectId}/messages:send}")
|
|
||||||
private String pushUrl;
|
|
||||||
|
|
||||||
private final TenantPushConfigClient configClient;
|
|
||||||
private final HttpClient httpClient = HttpClient.newHttpClient();
|
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
private final Map<String, TokenCache> tokenCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public FcmPushProvider(TenantPushConfigClient configClient) {
|
|
||||||
this.configClient = configClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String vendorName() {
|
|
||||||
return "FCM";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean send(String appKey, String token, String title, String body, String payload) {
|
|
||||||
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
|
||||||
String projectId = resolveConfig(appKey, "projectId", envProjectId);
|
|
||||||
String serviceAccountJson = resolveConfig(appKey, "serviceAccountJson", envServiceAccountJson);
|
|
||||||
if (projectId.isBlank() || serviceAccountJson.isBlank()) {
|
|
||||||
log.warn("FCM push not configured");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String accessToken = getAccessToken(projectId, serviceAccountJson);
|
|
||||||
String url = pushUrl.replace("{projectId}", projectId);
|
|
||||||
Map<String, Object> bodyMap = new LinkedHashMap<>();
|
|
||||||
bodyMap.put("token", token);
|
|
||||||
bodyMap.put("notification", Map.of("title", title, "body", body));
|
|
||||||
bodyMap.put("data", payload != null ? Map.of("payload", payload) : Map.of());
|
|
||||||
if (options != null && options.channelId() != null && !options.channelId().isBlank()) {
|
|
||||||
bodyMap.put("android", Map.of("notification", Map.of("channel_id", options.channelId())));
|
|
||||||
}
|
|
||||||
Map<String, Object> message = Map.of("message", bodyMap);
|
|
||||||
String requestBody = objectMapper.writeValueAsString(message);
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create(url))
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.header("Authorization", "Bearer " + accessToken)
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
|
||||||
.build();
|
|
||||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
||||||
return response.statusCode() == 200;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("FCM push failed: {}", e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getAccessToken(String projectId, String serviceAccountJson) throws Exception {
|
|
||||||
TokenCache cache = tokenCache.get(projectId);
|
|
||||||
if (cache != null && cache.expiresAt > System.currentTimeMillis() + 60_000) {
|
|
||||||
return cache.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode sa = objectMapper.readTree(serviceAccountJson);
|
|
||||||
String clientEmail = sa.path("client_email").asText();
|
|
||||||
String privateKeyPem = sa.path("private_key").asText();
|
|
||||||
|
|
||||||
PrivateKey privateKey = parseRsaPrivateKey(privateKeyPem);
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
String jwt = Jwts.builder()
|
|
||||||
.subject(clientEmail)
|
|
||||||
.issuer(clientEmail)
|
|
||||||
.claim("aud", tokenUrl)
|
|
||||||
.claim("scope", "https://www.googleapis.com/auth/firebase.messaging")
|
|
||||||
.issuedAt(new Date(now))
|
|
||||||
.expiration(new Date(now + 3600_000))
|
|
||||||
.signWith(privateKey)
|
|
||||||
.compact();
|
|
||||||
|
|
||||||
String form = "grant_type=" + URLEncoder.encode("urn:ietf:params:oauth:grant-type:jwt-bearer", StandardCharsets.UTF_8)
|
|
||||||
+ "&assertion=" + jwt;
|
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create(tokenUrl))
|
|
||||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(form))
|
|
||||||
.build();
|
|
||||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
||||||
Map<?, ?> json = objectMapper.readValue(response.body(), Map.class);
|
|
||||||
String accessToken = (String) json.get("access_token");
|
|
||||||
Integer expiresIn = (Integer) json.get("expires_in");
|
|
||||||
long expiresAt = System.currentTimeMillis() + (expiresIn != null ? expiresIn : 3600) * 1000L;
|
|
||||||
tokenCache.put(projectId, new TokenCache(accessToken, expiresAt));
|
|
||||||
return accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PrivateKey parseRsaPrivateKey(String pem) throws Exception {
|
|
||||||
String clean = pem.replace("-----BEGIN PRIVATE KEY-----", "")
|
|
||||||
.replace("-----END PRIVATE KEY-----", "")
|
|
||||||
.replaceAll("\\s", "");
|
|
||||||
byte[] decoded = Base64.getDecoder().decode(clean);
|
|
||||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
|
|
||||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
|
||||||
return kf.generatePrivate(spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveConfig(String appKey, String key, String fallback) {
|
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
|
||||||
.map(node -> node.path("fcm"))
|
|
||||||
.orElse(null);
|
|
||||||
if (config == null) {
|
|
||||||
return fallback == null ? "" : fallback;
|
|
||||||
}
|
|
||||||
String value = config.path(key).asText("");
|
|
||||||
return value.isBlank() ? (fallback == null ? "" : fallback) : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private record TokenCache(String token, long expiresAt) {}
|
|
||||||
}
|
|
||||||
@ -78,6 +78,9 @@ public class HarmonyPushProvider implements PushProvider {
|
|||||||
if (options.channelId() != null && !options.channelId().isBlank()) {
|
if (options.channelId() != null && !options.channelId().isBlank()) {
|
||||||
notification.put("channel_id", options.channelId());
|
notification.put("channel_id", options.channelId());
|
||||||
}
|
}
|
||||||
|
if (options.badge() != null && options.badge()) {
|
||||||
|
notification.put("badge", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Map<String, Object> message = Map.of(
|
Map<String, Object> message = Map.of(
|
||||||
"message", Map.of(
|
"message", Map.of(
|
||||||
@ -115,7 +118,7 @@ public class HarmonyPushProvider implements PushProvider {
|
|||||||
|
|
||||||
private String resolveConfig(String appKey, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "HARMONY", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "HARMONY", "PUSH")
|
||||||
.map(node -> node.path("harmony"))
|
.map(node -> node.path("vendors").path("harmony"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return fallback == null ? "" : fallback;
|
return fallback == null ? "" : fallback;
|
||||||
|
|||||||
@ -46,6 +46,11 @@ public class HonorPushProvider implements PushProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appKey, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String resolvedAppId = resolveConfig(appKey, "appId", envAppId);
|
String resolvedAppId = resolveConfig(appKey, "appId", envAppId);
|
||||||
String resolvedAppSecret = resolveConfig(appKey, "clientSecret", envAppSecret);
|
String resolvedAppSecret = resolveConfig(appKey, "clientSecret", envAppSecret);
|
||||||
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
||||||
@ -58,10 +63,21 @@ public class HonorPushProvider implements PushProvider {
|
|||||||
try {
|
try {
|
||||||
String accessToken = getAccessToken(resolvedAppId, resolvedAppSecret);
|
String accessToken = getAccessToken(resolvedAppId, resolvedAppSecret);
|
||||||
String url = pushUrl.replace("{appId}", resolvedAppId);
|
String url = pushUrl.replace("{appId}", resolvedAppId);
|
||||||
|
java.util.Map<String, Object> notification = new java.util.LinkedHashMap<>();
|
||||||
|
notification.put("title", title);
|
||||||
|
notification.put("body", body);
|
||||||
|
if (options != null) {
|
||||||
|
if (options.channelId() != null && !options.channelId().isBlank()) {
|
||||||
|
notification.put("channel_id", options.channelId());
|
||||||
|
}
|
||||||
|
if (options.category() != null && !options.category().isBlank()) {
|
||||||
|
notification.put("category", options.category());
|
||||||
|
}
|
||||||
|
}
|
||||||
Map<String, Object> message = Map.of(
|
Map<String, Object> message = Map.of(
|
||||||
"message", Map.of(
|
"message", Map.of(
|
||||||
"token", new String[]{token},
|
"token", new String[]{token},
|
||||||
"notification", Map.of("title", title, "body", body),
|
"notification", notification,
|
||||||
"data", payload != null ? payload : "{}"
|
"data", payload != null ? payload : "{}"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -94,7 +110,7 @@ public class HonorPushProvider implements PushProvider {
|
|||||||
|
|
||||||
private String resolveConfig(String appKey, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("honor"))
|
.map(node -> node.path("vendors").path("honor"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return fallback == null ? "" : fallback;
|
return fallback == null ? "" : fallback;
|
||||||
|
|||||||
@ -68,7 +68,9 @@ public class HuaweiPushProvider implements PushProvider {
|
|||||||
Map<String, Object> androidNotification = new LinkedHashMap<>();
|
Map<String, Object> androidNotification = new LinkedHashMap<>();
|
||||||
String channelId = options != null && options.channelId() != null && !options.channelId().isBlank()
|
String channelId = options != null && options.channelId() != null && !options.channelId().isBlank()
|
||||||
? options.channelId() : "";
|
? options.channelId() : "";
|
||||||
String category = resolveConfig(appKey, "category", "");
|
String category = options != null && options.category() != null && !options.category().isBlank()
|
||||||
|
? options.category()
|
||||||
|
: resolveConfig(appKey, "category", "");
|
||||||
if (!channelId.isBlank()) androidNotification.put("channel_id", channelId);
|
if (!channelId.isBlank()) androidNotification.put("channel_id", channelId);
|
||||||
if (!category.isBlank()) androidNotification.put("category", category);
|
if (!category.isBlank()) androidNotification.put("category", category);
|
||||||
if (!androidNotification.isEmpty()) {
|
if (!androidNotification.isEmpty()) {
|
||||||
@ -104,7 +106,7 @@ public class HuaweiPushProvider implements PushProvider {
|
|||||||
|
|
||||||
private String resolveConfig(String appKey, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("huawei"))
|
.map(node -> node.path("vendors").path("huawei"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return fallback == null ? "" : fallback;
|
return fallback == null ? "" : fallback;
|
||||||
|
|||||||
@ -40,6 +40,11 @@ public class OppoPushProvider implements PushProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appKey, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String vendorAppKey = resolveConfig(appKey, "appKey");
|
String vendorAppKey = resolveConfig(appKey, "appKey");
|
||||||
String masterSecret = resolveConfig(appKey, "masterSecret");
|
String masterSecret = resolveConfig(appKey, "masterSecret");
|
||||||
if (vendorAppKey.isBlank() || masterSecret.isBlank()) {
|
if (vendorAppKey.isBlank() || masterSecret.isBlank()) {
|
||||||
@ -55,7 +60,9 @@ public class OppoPushProvider implements PushProvider {
|
|||||||
inner.put("content", body);
|
inner.put("content", body);
|
||||||
inner.put("target_type", 2);
|
inner.put("target_type", 2);
|
||||||
inner.put("target_value", token);
|
inner.put("target_value", token);
|
||||||
String channelId = resolveConfig(appKey, "channelId");
|
String channelId = options != null && options.channelId() != null && !options.channelId().isBlank()
|
||||||
|
? options.channelId()
|
||||||
|
: resolveConfig(appKey, "channelId");
|
||||||
if (!channelId.isBlank()) inner.put("channel_id", channelId);
|
if (!channelId.isBlank()) inner.put("channel_id", channelId);
|
||||||
Map<String, Object> message = Map.of("message", inner);
|
Map<String, Object> message = Map.of("message", inner);
|
||||||
String requestBody = objectMapper.writeValueAsString(message);
|
String requestBody = objectMapper.writeValueAsString(message);
|
||||||
@ -98,7 +105,7 @@ public class OppoPushProvider implements PushProvider {
|
|||||||
|
|
||||||
private String resolveConfig(String appKey, String key) {
|
private String resolveConfig(String appKey, String key) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("oppo"))
|
.map(node -> node.path("vendors").path("oppo"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@ -1,12 +1,20 @@
|
|||||||
package com.xuqm.push.service.provider;
|
package com.xuqm.push.service.provider;
|
||||||
|
|
||||||
public record PushSendOptions(
|
public record PushSendOptions(
|
||||||
|
String profileKey,
|
||||||
String routeType,
|
String routeType,
|
||||||
String channelId,
|
String channelId,
|
||||||
String category,
|
String category,
|
||||||
|
String threadIdentifier,
|
||||||
|
String interruptionLevel,
|
||||||
|
String importance,
|
||||||
|
Boolean badge,
|
||||||
|
Boolean sound,
|
||||||
|
Boolean vibration,
|
||||||
|
Integer notifyType,
|
||||||
String priority
|
String priority
|
||||||
) {
|
) {
|
||||||
public static PushSendOptions empty() {
|
public static PushSendOptions empty() {
|
||||||
return new PushSendOptions("", "", "", "");
|
return new PushSendOptions("", "", "", "", "", "", "", null, null, null, null, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,11 @@ public class VivoPushProvider implements PushProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appKey, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String vendorAppKey = resolveConfig(appKey, "appKey");
|
String vendorAppKey = resolveConfig(appKey, "appKey");
|
||||||
String appIdConfig = resolveConfig(appKey, "appId");
|
String appIdConfig = resolveConfig(appKey, "appId");
|
||||||
if (vendorAppKey.isBlank() || appIdConfig.isBlank()) {
|
if (vendorAppKey.isBlank() || appIdConfig.isBlank()) {
|
||||||
@ -53,11 +58,17 @@ public class VivoPushProvider implements PushProvider {
|
|||||||
message.put("title", title);
|
message.put("title", title);
|
||||||
message.put("content", body);
|
message.put("content", body);
|
||||||
message.put("notifyType", 1);
|
message.put("notifyType", 1);
|
||||||
String category = resolveConfig(appKey, "category");
|
String category = options != null && options.category() != null && !options.category().isBlank()
|
||||||
|
? options.category()
|
||||||
|
: resolveConfig(appKey, "category");
|
||||||
String receiptId = resolveConfig(appKey, "receiptId");
|
String receiptId = resolveConfig(appKey, "receiptId");
|
||||||
if (!category.isBlank()) {
|
if (!category.isBlank()) {
|
||||||
// vivo classification: 0=operation, 1=IM/system
|
message.put("classification", switch (category.toUpperCase()) {
|
||||||
message.put("classification", "IM".equalsIgnoreCase(category) ? 1 : 0);
|
case "MESSAGE", "IM" -> 1;
|
||||||
|
case "SOCIAL" -> 2;
|
||||||
|
case "SYSTEM" -> 3;
|
||||||
|
default -> 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!receiptId.isBlank()) {
|
if (!receiptId.isBlank()) {
|
||||||
message.put("requestId", receiptId + "_" + System.currentTimeMillis());
|
message.put("requestId", receiptId + "_" + System.currentTimeMillis());
|
||||||
@ -102,7 +113,7 @@ public class VivoPushProvider implements PushProvider {
|
|||||||
|
|
||||||
private String resolveConfig(String appKey, String key) {
|
private String resolveConfig(String appKey, String key) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("vivo"))
|
.map(node -> node.path("vendors").path("vivo"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@ -70,8 +70,9 @@ public class XiaomiPushProvider implements PushProvider {
|
|||||||
.append("&title=").append(URLEncoder.encode(title, StandardCharsets.UTF_8))
|
.append("&title=").append(URLEncoder.encode(title, StandardCharsets.UTF_8))
|
||||||
.append("&description=").append(URLEncoder.encode(body, StandardCharsets.UTF_8))
|
.append("&description=").append(URLEncoder.encode(body, StandardCharsets.UTF_8))
|
||||||
.append("&restricted_package_name=").append(URLEncoder.encode(packageName, StandardCharsets.UTF_8))
|
.append("&restricted_package_name=").append(URLEncoder.encode(packageName, StandardCharsets.UTF_8))
|
||||||
.append("¬ify_type=1")
|
|
||||||
.append("&extra.notify_foreground=1");
|
.append("&extra.notify_foreground=1");
|
||||||
|
Integer notifyType = options != null ? options.notifyType() : null;
|
||||||
|
form.append("¬ify_type=").append(notifyType == null ? 1 : Math.max(notifyType, 0));
|
||||||
if (!channelId.isBlank()) {
|
if (!channelId.isBlank()) {
|
||||||
form.append("&channel_id=").append(URLEncoder.encode(channelId, StandardCharsets.UTF_8));
|
form.append("&channel_id=").append(URLEncoder.encode(channelId, StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
@ -110,7 +111,7 @@ public class XiaomiPushProvider implements PushProvider {
|
|||||||
|
|
||||||
private String resolveVendorConfig(String appKey, String key, String fallback) {
|
private String resolveVendorConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("xiaomi"))
|
.map(node -> node.path("vendors").path("xiaomi"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return fallback == null ? "" : fallback;
|
return fallback == null ? "" : fallback;
|
||||||
|
|||||||
@ -34,11 +34,6 @@ push:
|
|||||||
xiaomi:
|
xiaomi:
|
||||||
app-secret: ${XIAOMI_APP_SECRET:}
|
app-secret: ${XIAOMI_APP_SECRET:}
|
||||||
push-url: https://api.xmpush.xiaomi.com/v3/message/regid
|
push-url: https://api.xmpush.xiaomi.com/v3/message/regid
|
||||||
fcm:
|
|
||||||
project-id: ${FCM_PROJECT_ID:}
|
|
||||||
service-account-json: ${FCM_SERVICE_ACCOUNT_JSON:}
|
|
||||||
token-url: https://oauth2.googleapis.com/token
|
|
||||||
push-url: https://fcm.googleapis.com/v1/projects/{projectId}/messages:send
|
|
||||||
apns:
|
apns:
|
||||||
team-id: ${APNS_TEAM_ID:}
|
team-id: ${APNS_TEAM_ID:}
|
||||||
key-id: ${APNS_KEY_ID:}
|
key-id: ${APNS_KEY_ID:}
|
||||||
|
|||||||
@ -20,6 +20,11 @@
|
|||||||
<groupId>com.xuqm</groupId>
|
<groupId>com.xuqm</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xuqm</groupId>
|
||||||
|
<artifactId>im-sdk</artifactId>
|
||||||
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
|||||||
@ -112,35 +112,7 @@ public class FeatureServiceController {
|
|||||||
case PUSH -> featureServiceManager.buildPushConfig(
|
case PUSH -> featureServiceManager.buildPushConfig(
|
||||||
appKey,
|
appKey,
|
||||||
platform,
|
platform,
|
||||||
req == null ? null : req.huaweiAppIdValue(),
|
req == null ? null : req.pushConfig());
|
||||||
req == null ? null : req.huaweiAppSecretValue(),
|
|
||||||
req == null ? null : req.huaweiCategoryValue(),
|
|
||||||
req == null ? null : req.xiaomiAppIdValue(),
|
|
||||||
req == null ? null : req.xiaomiAppKeyValue(),
|
|
||||||
req == null ? null : req.xiaomiAppSecretValue(),
|
|
||||||
req == null ? null : req.xiaomiChannelIdValue(),
|
|
||||||
req == null ? null : req.oppoAppIdValue(),
|
|
||||||
req == null ? null : req.oppoAppKeyValue(),
|
|
||||||
req == null ? null : req.oppoMasterSecretValue(),
|
|
||||||
req == null ? null : req.oppoChannelIdValue(),
|
|
||||||
req == null ? null : req.vivoAppIdValue(),
|
|
||||||
req == null ? null : req.vivoAppKeyValue(),
|
|
||||||
req == null ? null : req.vivoAppSecretValue(),
|
|
||||||
req == null ? null : req.vivoCategoryValue(),
|
|
||||||
req == null ? null : req.vivoReceiptIdValue(),
|
|
||||||
req == null ? null : req.honorAppIdValue(),
|
|
||||||
req == null ? null : req.honorClientIdValue(),
|
|
||||||
req == null ? null : req.honorClientSecretValue(),
|
|
||||||
req == null ? null : req.harmonyAppIdValue(),
|
|
||||||
req == null ? null : req.harmonyAppSecretValue(),
|
|
||||||
req == null ? null : req.apnsTeamIdValue(),
|
|
||||||
req == null ? null : req.apnsKeyIdValue(),
|
|
||||||
req == null ? null : req.apnsBundleIdValue(),
|
|
||||||
req == null ? null : req.apnsKeyPathValue(),
|
|
||||||
req != null && req.apnsSandboxValue(),
|
|
||||||
req == null ? null : req.fcmServiceAccountJsonValue(),
|
|
||||||
req == null ? null : req.channels(),
|
|
||||||
req == null ? null : req.routing());
|
|
||||||
};
|
};
|
||||||
FeatureServiceEntity saved = featureServiceManager.updateConfig(
|
FeatureServiceEntity saved = featureServiceManager.updateConfig(
|
||||||
appKey, platform, serviceType, config);
|
appKey, platform, serviceType, config);
|
||||||
@ -215,102 +187,7 @@ public class FeatureServiceController {
|
|||||||
String defaultPackageName,
|
String defaultPackageName,
|
||||||
String defaultAppStoreUrl,
|
String defaultAppStoreUrl,
|
||||||
String defaultMarketUrl,
|
String defaultMarketUrl,
|
||||||
String huaweiAppId,
|
JsonNode pushConfig
|
||||||
String huaweiAppSecret,
|
|
||||||
String huaweiCategory,
|
|
||||||
String xiaomiAppId,
|
|
||||||
String xiaomiAppKey,
|
|
||||||
String xiaomiAppSecret,
|
|
||||||
String xiaomiChannelId,
|
|
||||||
String oppoAppId,
|
|
||||||
String oppoAppKey,
|
|
||||||
String oppoMasterSecret,
|
|
||||||
String oppoChannelId,
|
|
||||||
String vivoAppId,
|
|
||||||
String vivoAppKey,
|
|
||||||
String vivoAppSecret,
|
|
||||||
String vivoCategory,
|
|
||||||
String vivoReceiptId,
|
|
||||||
String honorAppId,
|
|
||||||
String honorClientId,
|
|
||||||
String honorClientSecret,
|
|
||||||
String harmonyAppId,
|
|
||||||
String harmonyAppSecret,
|
|
||||||
String apnsTeamId,
|
|
||||||
String apnsKeyId,
|
|
||||||
String apnsBundleId,
|
|
||||||
String apnsKeyPath,
|
|
||||||
Boolean apnsSandbox,
|
|
||||||
String fcmServiceAccountJson,
|
|
||||||
PushVendorConfig huawei,
|
|
||||||
PushVendorConfig xiaomi,
|
|
||||||
PushVendorConfig oppo,
|
|
||||||
PushVendorConfig vivo,
|
|
||||||
PushVendorConfig honor,
|
|
||||||
PushVendorConfig harmony,
|
|
||||||
PushVendorConfig apns,
|
|
||||||
PushVendorConfig fcm,
|
|
||||||
JsonNode channels,
|
|
||||||
JsonNode routing
|
|
||||||
) {
|
) {
|
||||||
public String huaweiAppIdValue() { return firstText(huaweiAppId, huawei == null ? null : huawei.appId()); }
|
|
||||||
public String huaweiAppSecretValue() { return firstText(huaweiAppSecret, huawei == null ? null : huawei.appSecret()); }
|
|
||||||
public String huaweiCategoryValue() { return firstText(huaweiCategory, huawei == null ? null : huawei.category()); }
|
|
||||||
public String xiaomiAppIdValue() { return firstText(xiaomiAppId, xiaomi == null ? null : xiaomi.appId()); }
|
|
||||||
public String xiaomiAppKeyValue() { return firstText(xiaomiAppKey, xiaomi == null ? null : xiaomi.appKey()); }
|
|
||||||
public String xiaomiAppSecretValue() { return firstText(xiaomiAppSecret, xiaomi == null ? null : xiaomi.appSecret()); }
|
|
||||||
public String xiaomiChannelIdValue() { return firstText(xiaomiChannelId, xiaomi == null ? null : xiaomi.channelId()); }
|
|
||||||
public String oppoAppIdValue() { return firstText(oppoAppId, oppo == null ? null : oppo.appId()); }
|
|
||||||
public String oppoAppKeyValue() { return firstText(oppoAppKey, oppo == null ? null : oppo.appKey()); }
|
|
||||||
public String oppoMasterSecretValue() { return firstText(oppoMasterSecret, oppo == null ? null : oppo.masterSecret()); }
|
|
||||||
public String oppoChannelIdValue() { return firstText(oppoChannelId, oppo == null ? null : oppo.channelId()); }
|
|
||||||
public String vivoAppIdValue() { return firstText(vivoAppId, vivo == null ? null : vivo.appId()); }
|
|
||||||
public String vivoAppKeyValue() { return firstText(vivoAppKey, vivo == null ? null : vivo.appKey()); }
|
|
||||||
public String vivoAppSecretValue() { return firstText(vivoAppSecret, vivo == null ? null : vivo.appSecret()); }
|
|
||||||
public String vivoCategoryValue() { return firstText(vivoCategory, vivo == null ? null : vivo.category()); }
|
|
||||||
public String vivoReceiptIdValue() { return firstText(vivoReceiptId, vivo == null ? null : vivo.receiptId()); }
|
|
||||||
public String honorAppIdValue() { return firstText(honorAppId, honor == null ? null : honor.appId()); }
|
|
||||||
public String honorClientIdValue() { return firstText(honorClientId, honor == null ? null : honor.clientId()); }
|
|
||||||
public String honorClientSecretValue() { return firstText(honorClientSecret, honor == null ? null : honor.clientSecret()); }
|
|
||||||
public String harmonyAppIdValue() { return firstText(harmonyAppId, harmony == null ? null : harmony.appId()); }
|
|
||||||
public String harmonyAppSecretValue() { return firstText(harmonyAppSecret, harmony == null ? null : harmony.appSecret()); }
|
|
||||||
public String apnsTeamIdValue() { return firstText(apnsTeamId, apns == null ? null : apns.teamId()); }
|
|
||||||
public String apnsKeyIdValue() { return firstText(apnsKeyId, apns == null ? null : apns.keyId()); }
|
|
||||||
public String apnsBundleIdValue() { return firstText(apnsBundleId, apns == null ? null : apns.bundleId()); }
|
|
||||||
public String apnsKeyPathValue() { return firstText(apnsKeyPath, apns == null ? null : apns.keyPath()); }
|
|
||||||
public boolean apnsSandboxValue() {
|
|
||||||
if (apnsSandbox != null) {
|
|
||||||
return apnsSandbox;
|
|
||||||
}
|
|
||||||
return apns != null && Boolean.TRUE.equals(apns.sandbox());
|
|
||||||
}
|
|
||||||
public String fcmServiceAccountJsonValue() {
|
|
||||||
return firstText(fcmServiceAccountJson, fcm == null ? null : fcm.serviceAccountJson());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String firstText(String first, String second) {
|
|
||||||
if (first != null) {
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
return second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PushVendorConfig(
|
|
||||||
String appId,
|
|
||||||
String appKey,
|
|
||||||
String appSecret,
|
|
||||||
String masterSecret,
|
|
||||||
String clientId,
|
|
||||||
String clientSecret,
|
|
||||||
String teamId,
|
|
||||||
String keyId,
|
|
||||||
String bundleId,
|
|
||||||
String keyPath,
|
|
||||||
Boolean sandbox,
|
|
||||||
String serviceAccountJson,
|
|
||||||
String channelId,
|
|
||||||
String category,
|
|
||||||
String receiptId
|
|
||||||
) {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
package com.xuqm.tenant.controller;
|
||||||
|
|
||||||
|
import com.xuqm.common.model.ApiResponse;
|
||||||
|
import com.xuqm.tenant.entity.AppEntity;
|
||||||
|
import com.xuqm.tenant.service.ImPlatformEventService;
|
||||||
|
import com.xuqm.tenant.service.SdkAppProvisioningService;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/im/platform-events")
|
||||||
|
public class ImPlatformEventController {
|
||||||
|
|
||||||
|
private final SdkAppProvisioningService provisioningService;
|
||||||
|
private final ImPlatformEventService platformEventService;
|
||||||
|
|
||||||
|
public ImPlatformEventController(SdkAppProvisioningService provisioningService,
|
||||||
|
ImPlatformEventService platformEventService) {
|
||||||
|
this.provisioningService = provisioningService;
|
||||||
|
this.platformEventService = platformEventService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/token")
|
||||||
|
public ResponseEntity<ApiResponse<Map<String, String>>> token(
|
||||||
|
@AuthenticationPrincipal String tenantId,
|
||||||
|
@RequestParam String appKey) {
|
||||||
|
try {
|
||||||
|
AppEntity app = provisioningService.resolveApp(appKey);
|
||||||
|
if (tenantId == null || tenantId.isBlank() || !tenantId.equals(app.getTenantId())) {
|
||||||
|
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(platformEventService.issueToken(appKey)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResponseEntity.status(500)
|
||||||
|
.body(ApiResponse.error(500, e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.xuqm.tenant.controller;
|
||||||
|
|
||||||
|
import com.xuqm.common.model.ApiResponse;
|
||||||
|
import com.xuqm.tenant.service.ImPlatformEventService;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/internal/im/platform-events")
|
||||||
|
public class InternalImPlatformEventController {
|
||||||
|
|
||||||
|
private final ImPlatformEventService platformEventService;
|
||||||
|
|
||||||
|
@Value("${sdk.internal-token:xuqm-internal-token}")
|
||||||
|
private String internalToken;
|
||||||
|
|
||||||
|
public InternalImPlatformEventController(ImPlatformEventService platformEventService) {
|
||||||
|
this.platformEventService = platformEventService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/notify")
|
||||||
|
public ResponseEntity<ApiResponse<Map<String, String>>> notify(
|
||||||
|
@RequestHeader(value = "X-Internal-Token", required = false) String token,
|
||||||
|
@RequestBody ImPlatformEventService.StoreReviewEventRequest request) {
|
||||||
|
if (token == null || !internalToken.equals(token)) {
|
||||||
|
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(platformEventService.notifyStoreReviewChange(request)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResponseEntity.status(500)
|
||||||
|
.body(ApiResponse.error(500, e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -56,22 +56,22 @@ public class SdkConfigController {
|
|||||||
@RequestParam(required = false, defaultValue = "ANDROID") FeatureServiceEntity.Platform platform) {
|
@RequestParam(required = false, defaultValue = "ANDROID") FeatureServiceEntity.Platform platform) {
|
||||||
|
|
||||||
AppEntity app = sdkAppProvisioningService.resolveApp(appKey);
|
AppEntity app = sdkAppProvisioningService.resolveApp(appKey);
|
||||||
List<FeatureServiceEntity> features = featureServiceRepository.findByAppId(app.getAppKey());
|
List<FeatureServiceEntity> features = featureServiceRepository.findByAppKey(app.getAppKey());
|
||||||
|
|
||||||
boolean imEnabled = features.stream()
|
boolean imEnabled = features.stream()
|
||||||
.anyMatch(f -> f.getServiceType() == FeatureServiceEntity.ServiceType.IM && f.isEnabled());
|
.anyMatch(f -> f.getServiceType() == FeatureServiceEntity.ServiceType.IM && f.isEnabled());
|
||||||
boolean pushEnabled = features.stream()
|
boolean pushEnabled = features.stream()
|
||||||
.anyMatch(f -> f.getServiceType() == FeatureServiceEntity.ServiceType.PUSH && f.isEnabled());
|
.anyMatch(f -> f.getServiceType() == FeatureServiceEntity.ServiceType.PUSH && f.isEnabled());
|
||||||
JsonNode updateConfig = featureServiceRepository
|
JsonNode updateConfig = featureServiceRepository
|
||||||
.findByAppIdAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
|
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
|
||||||
.map(feature -> parseConfig(feature.getConfig()))
|
.map(feature -> parseConfig(feature.getConfig()))
|
||||||
.orElseGet(objectMapper::createObjectNode);
|
.orElseGet(objectMapper::createObjectNode);
|
||||||
JsonNode pushConfig = featureServiceRepository
|
JsonNode pushConfig = featureServiceRepository
|
||||||
.findByAppIdAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.PUSH)
|
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.PUSH)
|
||||||
.map(feature -> parseConfig(feature.getConfig()))
|
.map(feature -> parseConfig(feature.getConfig()))
|
||||||
.orElseGet(objectMapper::createObjectNode);
|
.orElseGet(objectMapper::createObjectNode);
|
||||||
boolean updateEnabled = featureServiceRepository
|
boolean updateEnabled = featureServiceRepository
|
||||||
.findByAppIdAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
|
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
|
||||||
.map(FeatureServiceEntity::isEnabled)
|
.map(FeatureServiceEntity::isEnabled)
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public class FeatureServiceEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(nullable = false, length = 16)
|
@Column(nullable = false, length = 16)
|
||||||
@ -52,8 +52,8 @@ public class FeatureServiceEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public Platform getPlatform() { return platform; }
|
public Platform getPlatform() { return platform; }
|
||||||
public void setPlatform(Platform platform) { this.platform = platform; }
|
public void setPlatform(Platform platform) { this.platform = platform; }
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public class ServiceActivationRequestEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(nullable = false, length = 16)
|
@Column(nullable = false, length = 16)
|
||||||
@ -47,14 +47,11 @@ public class ServiceActivationRequestEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
@JsonProperty("appKey")
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public String getAppKey() { return appKey; }
|
||||||
|
|
||||||
@JsonProperty("appKey")
|
@JsonProperty("appKey")
|
||||||
public String getAppKey() { return appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
@JsonProperty("appKey")
|
|
||||||
public void setAppKey(String appKey) { this.appId = appKey; }
|
|
||||||
|
|
||||||
public FeatureServiceEntity.Platform getPlatform() { return platform; }
|
public FeatureServiceEntity.Platform getPlatform() { return platform; }
|
||||||
public void setPlatform(FeatureServiceEntity.Platform platform) { this.platform = platform; }
|
public void setPlatform(FeatureServiceEntity.Platform platform) { this.platform = platform; }
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface FeatureServiceRepository extends JpaRepository<FeatureServiceEntity, String> {
|
public interface FeatureServiceRepository extends JpaRepository<FeatureServiceEntity, String> {
|
||||||
List<FeatureServiceEntity> findByAppId(String appId);
|
List<FeatureServiceEntity> findByAppKey(String appKey);
|
||||||
List<FeatureServiceEntity> findByAppIdAndServiceType(String appId, FeatureServiceEntity.ServiceType serviceType);
|
List<FeatureServiceEntity> findByAppKeyAndServiceType(String appKey, FeatureServiceEntity.ServiceType serviceType);
|
||||||
Optional<FeatureServiceEntity> findByAppIdAndPlatformAndServiceType(
|
Optional<FeatureServiceEntity> findByAppKeyAndPlatformAndServiceType(
|
||||||
String appId,
|
String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType);
|
FeatureServiceEntity.ServiceType serviceType);
|
||||||
|
|
||||||
|
|||||||
@ -12,13 +12,13 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface ServiceActivationRequestRepository extends JpaRepository<ServiceActivationRequestEntity, String> {
|
public interface ServiceActivationRequestRepository extends JpaRepository<ServiceActivationRequestEntity, String> {
|
||||||
|
|
||||||
Optional<ServiceActivationRequestEntity> findFirstByAppIdAndPlatformAndServiceTypeOrderByCreatedAtDesc(
|
Optional<ServiceActivationRequestEntity> findFirstByAppKeyAndPlatformAndServiceTypeOrderByCreatedAtDesc(
|
||||||
String appId, FeatureServiceEntity.Platform platform, FeatureServiceEntity.ServiceType serviceType);
|
String appKey, FeatureServiceEntity.Platform platform, FeatureServiceEntity.ServiceType serviceType);
|
||||||
|
|
||||||
Optional<ServiceActivationRequestEntity> findFirstByAppIdAndServiceTypeOrderByCreatedAtDesc(
|
Optional<ServiceActivationRequestEntity> findFirstByAppKeyAndServiceTypeOrderByCreatedAtDesc(
|
||||||
String appId, FeatureServiceEntity.ServiceType serviceType);
|
String appKey, FeatureServiceEntity.ServiceType serviceType);
|
||||||
|
|
||||||
List<ServiceActivationRequestEntity> findByAppIdOrderByCreatedAtDesc(String appId);
|
List<ServiceActivationRequestEntity> findByAppKeyOrderByCreatedAtDesc(String appKey);
|
||||||
|
|
||||||
Page<ServiceActivationRequestEntity> findByStatusOrderByCreatedAtDesc(Status status, Pageable pageable);
|
Page<ServiceActivationRequestEntity> findByStatusOrderByCreatedAtDesc(Status status, Pageable pageable);
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public class DashboardService {
|
|||||||
List<AppEntity> apps = appRepository.findByTenantId(tenantId);
|
List<AppEntity> apps = appRepository.findByTenantId(tenantId);
|
||||||
long serviceCount = 0;
|
long serviceCount = 0;
|
||||||
for (AppEntity app : apps) {
|
for (AppEntity app : apps) {
|
||||||
serviceCount += featureServiceRepository.findByAppId(app.getId()).stream()
|
serviceCount += featureServiceRepository.findByAppKey(app.getId()).stream()
|
||||||
.filter(FeatureServiceEntity::isEnabled)
|
.filter(FeatureServiceEntity::isEnabled)
|
||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public class FeatureServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<FeatureServiceEntity> listByApp(String appKey) {
|
public List<FeatureServiceEntity> listByApp(String appKey) {
|
||||||
List<FeatureServiceEntity> services = repository.findByAppId(appKey);
|
List<FeatureServiceEntity> services = repository.findByAppKey(appKey);
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ public class FeatureServiceManager {
|
|||||||
String applyReason) {
|
String applyReason) {
|
||||||
|
|
||||||
if (isAppWideService(serviceType)) {
|
if (isAppWideService(serviceType)) {
|
||||||
requestRepository.findFirstByAppIdAndServiceTypeOrderByCreatedAtDesc(appKey, serviceType)
|
requestRepository.findFirstByAppKeyAndServiceTypeOrderByCreatedAtDesc(appKey, serviceType)
|
||||||
.ifPresent(req -> {
|
.ifPresent(req -> {
|
||||||
if (req.getStatus() == Status.PENDING) {
|
if (req.getStatus() == Status.PENDING) {
|
||||||
throw new BusinessException(400, "已有待审核的开通申请,请等待运营人员处理");
|
throw new BusinessException(400, "已有待审核的开通申请,请等待运营人员处理");
|
||||||
@ -78,7 +78,7 @@ public class FeatureServiceManager {
|
|||||||
|
|
||||||
ServiceActivationRequestEntity req = new ServiceActivationRequestEntity();
|
ServiceActivationRequestEntity req = new ServiceActivationRequestEntity();
|
||||||
req.setId(UUID.randomUUID().toString());
|
req.setId(UUID.randomUUID().toString());
|
||||||
req.setAppId(appKey);
|
req.setAppKey(appKey);
|
||||||
req.setPlatform(platform);
|
req.setPlatform(platform);
|
||||||
req.setServiceType(serviceType);
|
req.setServiceType(serviceType);
|
||||||
req.setStatus(Status.PENDING);
|
req.setStatus(Status.PENDING);
|
||||||
@ -94,7 +94,7 @@ public class FeatureServiceManager {
|
|||||||
public FeatureServiceEntity disable(String appKey, FeatureServiceEntity.Platform platform,
|
public FeatureServiceEntity disable(String appKey, FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
if (isAppWideService(serviceType)) {
|
if (isAppWideService(serviceType)) {
|
||||||
List<FeatureServiceEntity> services = repository.findByAppIdAndServiceType(appKey, serviceType);
|
List<FeatureServiceEntity> services = repository.findByAppKeyAndServiceType(appKey, serviceType);
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
throw new BusinessException(404, "服务未开通");
|
throw new BusinessException(404, "服务未开通");
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ public class FeatureServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FeatureServiceEntity entity = repository
|
FeatureServiceEntity entity = repository
|
||||||
.findByAppIdAndPlatformAndServiceType(appKey, platform, serviceType)
|
.findByAppKeyAndPlatformAndServiceType(appKey, platform, serviceType)
|
||||||
.orElseThrow(() -> new BusinessException(404, "服务未开通"));
|
.orElseThrow(() -> new BusinessException(404, "服务未开通"));
|
||||||
entity.setEnabled(false);
|
entity.setEnabled(false);
|
||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
@ -126,17 +126,17 @@ public class FeatureServiceManager {
|
|||||||
requestRepository.save(req);
|
requestRepository.save(req);
|
||||||
|
|
||||||
// Normalize to appKey so SdkConfigController queries are consistent
|
// Normalize to appKey so SdkConfigController queries are consistent
|
||||||
String normalizedAppId = appRepository.findById(req.getAppId())
|
String normalizedAppId = appRepository.findById(req.getAppKey())
|
||||||
.map(app -> app.getAppKey())
|
.map(app -> app.getAppKey())
|
||||||
.orElse(req.getAppId());
|
.orElse(req.getAppKey());
|
||||||
|
|
||||||
if (isAppWideService(req.getServiceType())) {
|
if (isAppWideService(req.getServiceType())) {
|
||||||
List<FeatureServiceEntity> services = repository.findByAppIdAndServiceType(normalizedAppId, req.getServiceType());
|
List<FeatureServiceEntity> services = repository.findByAppKeyAndServiceType(normalizedAppId, req.getServiceType());
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
for (FeatureServiceEntity.Platform platform : FeatureServiceEntity.Platform.values()) {
|
for (FeatureServiceEntity.Platform platform : FeatureServiceEntity.Platform.values()) {
|
||||||
FeatureServiceEntity created = new FeatureServiceEntity();
|
FeatureServiceEntity created = new FeatureServiceEntity();
|
||||||
created.setId(UUID.randomUUID().toString());
|
created.setId(UUID.randomUUID().toString());
|
||||||
created.setAppId(normalizedAppId);
|
created.setAppKey(normalizedAppId);
|
||||||
created.setPlatform(platform);
|
created.setPlatform(platform);
|
||||||
created.setServiceType(req.getServiceType());
|
created.setServiceType(req.getServiceType());
|
||||||
created.setEnabled(true);
|
created.setEnabled(true);
|
||||||
@ -151,11 +151,11 @@ public class FeatureServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FeatureServiceEntity entity = repository
|
FeatureServiceEntity entity = repository
|
||||||
.findByAppIdAndPlatformAndServiceType(normalizedAppId, req.getPlatform(), req.getServiceType())
|
.findByAppKeyAndPlatformAndServiceType(normalizedAppId, req.getPlatform(), req.getServiceType())
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
FeatureServiceEntity e = new FeatureServiceEntity();
|
FeatureServiceEntity e = new FeatureServiceEntity();
|
||||||
e.setId(UUID.randomUUID().toString());
|
e.setId(UUID.randomUUID().toString());
|
||||||
e.setAppId(normalizedAppId);
|
e.setAppKey(normalizedAppId);
|
||||||
e.setPlatform(req.getPlatform());
|
e.setPlatform(req.getPlatform());
|
||||||
e.setServiceType(req.getServiceType());
|
e.setServiceType(req.getServiceType());
|
||||||
e.setCreatedAt(LocalDateTime.now());
|
e.setCreatedAt(LocalDateTime.now());
|
||||||
@ -183,18 +183,18 @@ public class FeatureServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ServiceActivationRequestEntity> listRequestsByApp(String appKey) {
|
public List<ServiceActivationRequestEntity> listRequestsByApp(String appKey) {
|
||||||
return requestRepository.findByAppIdOrderByCreatedAtDesc(appKey);
|
return requestRepository.findByAppKeyOrderByCreatedAtDesc(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeatureServiceEntity getOrFail(String appKey, FeatureServiceEntity.Platform platform,
|
public FeatureServiceEntity getOrFail(String appKey, FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
||||||
return repository.findByAppIdAndServiceType(appKey, serviceType)
|
return repository.findByAppKeyAndServiceType(appKey, serviceType)
|
||||||
.stream()
|
.stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
||||||
}
|
}
|
||||||
return repository.findByAppIdAndPlatformAndServiceType(appKey, platform, serviceType)
|
return repository.findByAppKeyAndPlatformAndServiceType(appKey, platform, serviceType)
|
||||||
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ public class FeatureServiceManager {
|
|||||||
FeatureServiceEntity.ServiceType serviceType,
|
FeatureServiceEntity.ServiceType serviceType,
|
||||||
String config) {
|
String config) {
|
||||||
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
||||||
List<FeatureServiceEntity> services = repository.findByAppIdAndServiceType(appKey, serviceType);
|
List<FeatureServiceEntity> services = repository.findByAppKeyAndServiceType(appKey, serviceType);
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
throw new BusinessException(404, "服务未配置");
|
throw new BusinessException(404, "服务未配置");
|
||||||
}
|
}
|
||||||
@ -477,127 +477,25 @@ public class FeatureServiceManager {
|
|||||||
|
|
||||||
public String buildPushConfig(String appKey,
|
public String buildPushConfig(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
String huaweiAppId,
|
JsonNode pushConfig) {
|
||||||
String huaweiAppSecret,
|
ObjectNode root = objectMapper.createObjectNode();
|
||||||
String huaweiCategory,
|
root.put("schemaVersion", 2);
|
||||||
String xiaomiAppId,
|
root.put("updatedAt", java.time.LocalDateTime.now().toString());
|
||||||
String xiaomiAppKey,
|
ObjectNode vendors = objectMapper.createObjectNode();
|
||||||
String xiaomiAppSecret,
|
ArrayNode profiles = objectMapper.createArrayNode();
|
||||||
String xiaomiChannelId,
|
if (pushConfig != null && pushConfig.isObject()) {
|
||||||
String oppoAppId,
|
JsonNode inputVendors = pushConfig.path("vendors");
|
||||||
String oppoAppKey,
|
if (inputVendors != null && inputVendors.isObject()) {
|
||||||
String oppoMasterSecret,
|
vendors.setAll((ObjectNode) inputVendors);
|
||||||
String oppoChannelId,
|
}
|
||||||
String vivoAppId,
|
JsonNode inputProfiles = pushConfig.path("profiles");
|
||||||
String vivoAppKey,
|
if (inputProfiles != null && inputProfiles.isArray()) {
|
||||||
String vivoAppSecret,
|
inputProfiles.forEach(profile -> profiles.add(profile.deepCopy()));
|
||||||
String vivoCategory,
|
}
|
||||||
String vivoReceiptId,
|
|
||||||
String honorAppId,
|
|
||||||
String honorClientId,
|
|
||||||
String honorClientSecret,
|
|
||||||
String harmonyAppId,
|
|
||||||
String harmonyAppSecret,
|
|
||||||
String apnsTeamId,
|
|
||||||
String apnsKeyId,
|
|
||||||
String apnsBundleId,
|
|
||||||
String apnsKeyPath,
|
|
||||||
boolean apnsSandbox,
|
|
||||||
String fcmServiceAccountJson,
|
|
||||||
JsonNode channels,
|
|
||||||
JsonNode routing) {
|
|
||||||
ObjectNode node = readConfigNode(appKey, platform, FeatureServiceEntity.ServiceType.PUSH).deepCopy();
|
|
||||||
ensureObjectNode(node, "huawei");
|
|
||||||
ensureObjectNode(node, "xiaomi");
|
|
||||||
ensureObjectNode(node, "oppo");
|
|
||||||
ensureObjectNode(node, "vivo");
|
|
||||||
ensureObjectNode(node, "honor");
|
|
||||||
ensureObjectNode(node, "harmony");
|
|
||||||
ensureObjectNode(node, "apns");
|
|
||||||
ensureObjectNode(node, "fcm");
|
|
||||||
|
|
||||||
putText(node.with("huawei"), "appId", huaweiAppId);
|
|
||||||
putText(node.with("huawei"), "appSecret", huaweiAppSecret);
|
|
||||||
putText(node.with("huawei"), "category", huaweiCategory);
|
|
||||||
|
|
||||||
putText(node.with("xiaomi"), "appId", xiaomiAppId);
|
|
||||||
putText(node.with("xiaomi"), "appKey", xiaomiAppKey);
|
|
||||||
putText(node.with("xiaomi"), "appSecret", xiaomiAppSecret);
|
|
||||||
putText(node.with("xiaomi"), "channelId", xiaomiChannelId);
|
|
||||||
|
|
||||||
putText(node.with("oppo"), "appId", oppoAppId);
|
|
||||||
putText(node.with("oppo"), "appKey", oppoAppKey);
|
|
||||||
putText(node.with("oppo"), "masterSecret", oppoMasterSecret);
|
|
||||||
putText(node.with("oppo"), "channelId", oppoChannelId);
|
|
||||||
|
|
||||||
putText(node.with("vivo"), "appId", vivoAppId);
|
|
||||||
putText(node.with("vivo"), "appKey", vivoAppKey);
|
|
||||||
putText(node.with("vivo"), "appSecret", vivoAppSecret);
|
|
||||||
putText(node.with("vivo"), "category", vivoCategory);
|
|
||||||
putText(node.with("vivo"), "receiptId", vivoReceiptId);
|
|
||||||
|
|
||||||
putText(node.with("honor"), "appId", honorAppId);
|
|
||||||
putText(node.with("honor"), "clientId", honorClientId);
|
|
||||||
putText(node.with("honor"), "clientSecret", honorClientSecret);
|
|
||||||
|
|
||||||
putText(node.with("harmony"), "appId", harmonyAppId);
|
|
||||||
putText(node.with("harmony"), "appSecret", harmonyAppSecret);
|
|
||||||
|
|
||||||
putText(node.with("apns"), "teamId", apnsTeamId);
|
|
||||||
putText(node.with("apns"), "keyId", apnsKeyId);
|
|
||||||
putText(node.with("apns"), "bundleId", apnsBundleId);
|
|
||||||
putText(node.with("apns"), "keyPath", apnsKeyPath);
|
|
||||||
node.with("apns").put("sandbox", apnsSandbox);
|
|
||||||
|
|
||||||
putText(node.with("fcm"), "serviceAccountJson", fcmServiceAccountJson);
|
|
||||||
if (channels != null && channels.isArray()) {
|
|
||||||
node.set("channels", channels);
|
|
||||||
} else if (!node.has("channels")) {
|
|
||||||
node.set("channels", defaultPushChannels());
|
|
||||||
}
|
}
|
||||||
if (routing != null && routing.isObject()) {
|
root.set("vendors", vendors);
|
||||||
node.set("routing", routing);
|
root.set("profiles", profiles);
|
||||||
} else if (!node.has("routing")) {
|
return root.toString();
|
||||||
node.set("routing", defaultPushRouting());
|
|
||||||
}
|
|
||||||
return node.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayNode defaultPushChannels() {
|
|
||||||
ArrayNode channels = objectMapper.createArrayNode();
|
|
||||||
channels.add(defaultPushChannel("im_message", "xuqm_im_message", "聊天消息", "单聊、群聊和好友消息", "HIGH"));
|
|
||||||
channels.add(defaultPushChannel("system_notice", "xuqm_system_notice", "系统通知", "系统通知和业务提醒", "DEFAULT"));
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectNode defaultPushChannel(String key, String channelId, String name, String description, String importance) {
|
|
||||||
ObjectNode channel = objectMapper.createObjectNode();
|
|
||||||
channel.put("key", key);
|
|
||||||
channel.put("channelId", channelId);
|
|
||||||
channel.put("version", 1);
|
|
||||||
channel.put("name", name);
|
|
||||||
channel.put("description", description);
|
|
||||||
channel.put("importance", importance);
|
|
||||||
channel.put("sound", true);
|
|
||||||
channel.put("vibration", true);
|
|
||||||
channel.put("badge", true);
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectNode defaultPushRouting() {
|
|
||||||
ObjectNode routing = objectMapper.createObjectNode();
|
|
||||||
routing.set("IM_MESSAGE", defaultPushRoute("im_message", "MESSAGE", "HIGH"));
|
|
||||||
routing.set("FRIEND_REQUEST", defaultPushRoute("im_message", "SOCIAL", "HIGH"));
|
|
||||||
routing.set("SYSTEM_NOTICE", defaultPushRoute("system_notice", "SYSTEM", "DEFAULT"));
|
|
||||||
return routing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectNode defaultPushRoute(String channel, String category, String priority) {
|
|
||||||
ObjectNode route = objectMapper.createObjectNode();
|
|
||||||
route.put("channel", channel);
|
|
||||||
route.put("category", category);
|
|
||||||
route.put("priority", priority);
|
|
||||||
return route;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -649,12 +547,12 @@ public class FeatureServiceManager {
|
|||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
FeatureServiceEntity entity;
|
FeatureServiceEntity entity;
|
||||||
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
||||||
entity = repository.findByAppIdAndServiceType(appKey, serviceType)
|
entity = repository.findByAppKeyAndServiceType(appKey, serviceType)
|
||||||
.stream()
|
.stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
} else {
|
} else {
|
||||||
entity = repository.findByAppIdAndPlatformAndServiceType(appKey, platform, serviceType)
|
entity = repository.findByAppKeyAndPlatformAndServiceType(appKey, platform, serviceType)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
if (entity == null || entity.getConfig() == null || entity.getConfig().isBlank()) {
|
if (entity == null || entity.getConfig() == null || entity.getConfig().isBlank()) {
|
||||||
|
|||||||
@ -0,0 +1,165 @@
|
|||||||
|
package com.xuqm.tenant.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.xuqm.common.security.AppRequestSignatureUtil;
|
||||||
|
import com.xuqm.im.sdk.XuqmImServerSdk;
|
||||||
|
import com.xuqm.tenant.entity.AppEntity;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ImPlatformEventService {
|
||||||
|
|
||||||
|
private final HttpClient httpClient = HttpClient.newHttpClient();
|
||||||
|
private final SdkAppProvisioningService provisioningService;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Value("${sdk.im-api-url:https://im.dev.xuqinmin.com}")
|
||||||
|
private String imApiUrl;
|
||||||
|
|
||||||
|
@Value("${sdk.im-platform-events-user-prefix:platform-events:}")
|
||||||
|
private String platformEventsUserPrefix;
|
||||||
|
|
||||||
|
@Value("${sdk.im-platform-events-system-user:platform-events-system}")
|
||||||
|
private String platformEventsSystemUser;
|
||||||
|
|
||||||
|
public ImPlatformEventService(SdkAppProvisioningService provisioningService,
|
||||||
|
ObjectMapper objectMapper) {
|
||||||
|
this.provisioningService = provisioningService;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> issueToken(String appKey) throws Exception {
|
||||||
|
AppEntity app = provisioningService.resolveApp(appKey);
|
||||||
|
XuqmImServerSdk sdk = sdk(app);
|
||||||
|
String userId = platformEventsUserId(appKey);
|
||||||
|
ensureAccount(sdk, app, userId, "平台通知");
|
||||||
|
String token = requestImToken(app, userId);
|
||||||
|
Map<String, String> result = new LinkedHashMap<>();
|
||||||
|
result.put("appKey", app.getAppKey());
|
||||||
|
result.put("userId", userId);
|
||||||
|
result.put("token", token);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> notifyStoreReviewChange(StoreReviewEventRequest request) throws Exception {
|
||||||
|
AppEntity app = provisioningService.resolveApp(request.appKey());
|
||||||
|
XuqmImServerSdk sdk = sdk(app);
|
||||||
|
String recipientUserId = platformEventsUserId(request.appKey());
|
||||||
|
String senderUserId = platformEventsSystemUserId();
|
||||||
|
|
||||||
|
ensureAccount(sdk, app, recipientUserId, "平台通知");
|
||||||
|
ensureAccount(sdk, app, senderUserId, "系统通知");
|
||||||
|
|
||||||
|
Map<String, Object> contentPayload = new LinkedHashMap<>();
|
||||||
|
contentPayload.put("event", request.event() == null || request.event().isBlank() ? "store_review_update" : request.event());
|
||||||
|
contentPayload.put("appKey", request.appKey());
|
||||||
|
contentPayload.put("versionId", request.versionId() == null ? "" : request.versionId());
|
||||||
|
contentPayload.put("storeType", request.storeType() == null ? "" : request.storeType());
|
||||||
|
contentPayload.put("reviewState", request.reviewState() == null ? "" : request.reviewState());
|
||||||
|
contentPayload.put("reviewReason", request.reviewReason() == null ? "" : request.reviewReason());
|
||||||
|
contentPayload.put("stage", request.stage() == null ? "" : request.stage());
|
||||||
|
contentPayload.put("batchId", request.batchId() == null ? "" : request.batchId());
|
||||||
|
contentPayload.put("publishStatus", request.publishStatus() == null ? "" : request.publishStatus());
|
||||||
|
contentPayload.put("source", request.source() == null ? "update-service" : request.source());
|
||||||
|
contentPayload.put("timestamp", System.currentTimeMillis());
|
||||||
|
String content = objectMapper.writeValueAsString(contentPayload);
|
||||||
|
|
||||||
|
var message = sdk.sendMessage(new XuqmImServerSdk.SendMessageRequest(
|
||||||
|
UUID.randomUUID().toString(),
|
||||||
|
recipientUserId,
|
||||||
|
"SINGLE",
|
||||||
|
"NOTIFY",
|
||||||
|
content,
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
|
Map<String, String> result = new LinkedHashMap<>();
|
||||||
|
result.put("appKey", app.getAppKey());
|
||||||
|
result.put("userId", recipientUserId);
|
||||||
|
result.put("messageId", message.id());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureAccount(XuqmImServerSdk sdk, AppEntity app, String userId, String suffix) {
|
||||||
|
sdk.importAccount(
|
||||||
|
userId,
|
||||||
|
app.getName() + " " + suffix,
|
||||||
|
app.getIconUrl(),
|
||||||
|
"UNKNOWN",
|
||||||
|
"ACTIVE"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XuqmImServerSdk sdk(AppEntity app) {
|
||||||
|
return XuqmImServerSdk.builder()
|
||||||
|
.baseUrl(imApiUrl)
|
||||||
|
.appKey(app.getAppKey())
|
||||||
|
.appSecret(app.getAppSecret())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String requestImToken(AppEntity app, String userId) throws Exception {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
String nonce = UUID.randomUUID().toString();
|
||||||
|
String payload = AppRequestSignatureUtil.payload(app.getAppKey(), userId, timestamp, nonce);
|
||||||
|
String signature = AppRequestSignatureUtil.sign(app.getAppSecret(), payload);
|
||||||
|
URI uri = URI.create(imApiUrl + "/api/im/auth/login?appKey="
|
||||||
|
+ encodeQuery(app.getAppKey())
|
||||||
|
+ "&userId="
|
||||||
|
+ encodeQuery(userId));
|
||||||
|
HttpRequest request = HttpRequest.newBuilder(uri)
|
||||||
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
.header("X-App-Timestamp", String.valueOf(timestamp))
|
||||||
|
.header("X-App-Nonce", nonce)
|
||||||
|
.header("X-App-Signature", signature)
|
||||||
|
.POST(HttpRequest.BodyPublishers.noBody())
|
||||||
|
.build();
|
||||||
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||||
|
throw new IllegalStateException("Failed to issue IM token: HTTP " + response.statusCode());
|
||||||
|
}
|
||||||
|
var root = objectMapper.readTree(response.body());
|
||||||
|
if (root.path("code").asInt() != 200) {
|
||||||
|
throw new IllegalStateException("Failed to issue IM token: " + root.path("message").asText("unknown error"));
|
||||||
|
}
|
||||||
|
String token = root.path("data").path("token").asText(null);
|
||||||
|
if (token == null || token.isBlank()) {
|
||||||
|
throw new IllegalStateException("Failed to issue IM token: empty token");
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encodeQuery(String value) {
|
||||||
|
return java.net.URLEncoder.encode(value == null ? "" : value, java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String platformEventsUserId(String appKey) {
|
||||||
|
return platformEventsUserPrefix + appKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String platformEventsSystemUserId() {
|
||||||
|
return platformEventsUserPrefix + platformEventsSystemUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record StoreReviewEventRequest(
|
||||||
|
String appKey,
|
||||||
|
String versionId,
|
||||||
|
String storeType,
|
||||||
|
String reviewState,
|
||||||
|
String reviewReason,
|
||||||
|
String stage,
|
||||||
|
String batchId,
|
||||||
|
String publishStatus,
|
||||||
|
String event,
|
||||||
|
String source
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@ -80,7 +80,7 @@ public class OpsService {
|
|||||||
List<AppEntity> apps = appRepository.findByTenantId(tenantId);
|
List<AppEntity> apps = appRepository.findByTenantId(tenantId);
|
||||||
long subAccountCount = tenantRepository.countByParentId(tenantId);
|
long subAccountCount = tenantRepository.countByParentId(tenantId);
|
||||||
long activeServiceCount = apps.stream()
|
long activeServiceCount = apps.stream()
|
||||||
.flatMap(app -> featureServiceRepository.findByAppId(app.getAppKey()).stream())
|
.flatMap(app -> featureServiceRepository.findByAppKey(app.getAppKey()).stream())
|
||||||
.filter(FeatureServiceEntity::isEnabled)
|
.filter(FeatureServiceEntity::isEnabled)
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ public class OpsService {
|
|||||||
public Map<String, Object> getAppDetail(String appKey) {
|
public Map<String, Object> getAppDetail(String appKey) {
|
||||||
AppEntity app = appRepository.findByAppKey(appKey)
|
AppEntity app = appRepository.findByAppKey(appKey)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
||||||
List<FeatureServiceEntity> services = featureServiceRepository.findByAppId(app.getAppKey());
|
List<FeatureServiceEntity> services = featureServiceRepository.findByAppKey(app.getAppKey());
|
||||||
long enabledCount = services.stream().filter(FeatureServiceEntity::isEnabled).count();
|
long enabledCount = services.stream().filter(FeatureServiceEntity::isEnabled).count();
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
result.put("app", app);
|
result.put("app", app);
|
||||||
@ -221,7 +221,7 @@ public class OpsService {
|
|||||||
public List<FeatureServiceEntity> listAppServices(String appKey) {
|
public List<FeatureServiceEntity> listAppServices(String appKey) {
|
||||||
appRepository.findByAppKey(appKey)
|
appRepository.findByAppKey(appKey)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
||||||
return featureServiceRepository.findByAppId(appKey);
|
return featureServiceRepository.findByAppKey(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<OperationLogEntity> listOperationLogs(int page, int size) {
|
public Page<OperationLogEntity> listOperationLogs(int page, int size) {
|
||||||
|
|||||||
@ -117,13 +117,13 @@ public class SdkAppProvisioningService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ensureFeatureDefaults(AppEntity app) {
|
private void ensureFeatureDefaults(AppEntity app) {
|
||||||
featureServiceRepository.findByAppIdAndServiceType(app.getAppKey(), FeatureServiceEntity.ServiceType.IM)
|
featureServiceRepository.findByAppKeyAndServiceType(app.getAppKey(), FeatureServiceEntity.ServiceType.IM)
|
||||||
.stream()
|
.stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
FeatureServiceEntity feature = new FeatureServiceEntity();
|
FeatureServiceEntity feature = new FeatureServiceEntity();
|
||||||
feature.setId(UUID.randomUUID().toString());
|
feature.setId(UUID.randomUUID().toString());
|
||||||
feature.setAppId(app.getAppKey());
|
feature.setAppKey(app.getAppKey());
|
||||||
feature.setPlatform(FeatureServiceEntity.Platform.ANDROID);
|
feature.setPlatform(FeatureServiceEntity.Platform.ANDROID);
|
||||||
feature.setServiceType(FeatureServiceEntity.ServiceType.IM);
|
feature.setServiceType(FeatureServiceEntity.ServiceType.IM);
|
||||||
feature.setEnabled(true);
|
feature.setEnabled(true);
|
||||||
@ -141,11 +141,11 @@ public class SdkAppProvisioningService {
|
|||||||
for (FeatureServiceEntity.ServiceType serviceType : List.of(
|
for (FeatureServiceEntity.ServiceType serviceType : List.of(
|
||||||
FeatureServiceEntity.ServiceType.PUSH,
|
FeatureServiceEntity.ServiceType.PUSH,
|
||||||
FeatureServiceEntity.ServiceType.UPDATE)) {
|
FeatureServiceEntity.ServiceType.UPDATE)) {
|
||||||
featureServiceRepository.findByAppIdAndPlatformAndServiceType(app.getAppKey(), platform, serviceType)
|
featureServiceRepository.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, serviceType)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
FeatureServiceEntity feature = new FeatureServiceEntity();
|
FeatureServiceEntity feature = new FeatureServiceEntity();
|
||||||
feature.setId(UUID.randomUUID().toString());
|
feature.setId(UUID.randomUUID().toString());
|
||||||
feature.setAppId(app.getAppKey());
|
feature.setAppKey(app.getAppKey());
|
||||||
feature.setPlatform(platform);
|
feature.setPlatform(platform);
|
||||||
feature.setServiceType(serviceType);
|
feature.setServiceType(serviceType);
|
||||||
feature.setEnabled(true);
|
feature.setEnabled(true);
|
||||||
|
|||||||
@ -88,3 +88,4 @@ sdk:
|
|||||||
im-ws-url: ${SDK_IM_WS_URL:wss://im.dev.xuqinmin.com/ws/im}
|
im-ws-url: ${SDK_IM_WS_URL:wss://im.dev.xuqinmin.com/ws/im}
|
||||||
file-service-url: ${SDK_FILE_SERVICE_URL:https://file.dev.xuqinmin.com}
|
file-service-url: ${SDK_FILE_SERVICE_URL:https://file.dev.xuqinmin.com}
|
||||||
im-api-url: ${SDK_IM_API_URL:https://im.dev.xuqinmin.com}
|
im-api-url: ${SDK_IM_API_URL:https://im.dev.xuqinmin.com}
|
||||||
|
im-platform-events-user-prefix: ${SDK_IM_PLATFORM_EVENTS_USER_PREFIX:platform-events:}
|
||||||
|
|||||||
@ -58,10 +58,10 @@ public class AppVersionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Optional<AppVersionEntity> latest = versionRepository
|
Optional<AppVersionEntity> latest = versionRepository
|
||||||
.findTopByAppIdAndPlatformAndPublishStatusAndVersionCodeGreaterThanOrderByVersionCodeDesc(
|
.findTopByAppKeyAndPlatformAndPublishStatusAndVersionCodeGreaterThanOrderByVersionCodeDesc(
|
||||||
appKey, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
appKey, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
||||||
Optional<AppVersionEntity> forcedHigher = versionRepository
|
Optional<AppVersionEntity> forcedHigher = versionRepository
|
||||||
.findTopByAppIdAndPlatformAndPublishStatusAndVersionCodeGreaterThanAndForceUpdateTrueOrderByVersionCodeDesc(
|
.findTopByAppKeyAndPlatformAndPublishStatusAndVersionCodeGreaterThanAndForceUpdateTrueOrderByVersionCodeDesc(
|
||||||
appKey, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
appKey, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
||||||
|
|
||||||
if (latest.isEmpty()) {
|
if (latest.isEmpty()) {
|
||||||
@ -146,7 +146,7 @@ public class AppVersionController {
|
|||||||
}
|
}
|
||||||
AppVersionEntity entity = new AppVersionEntity();
|
AppVersionEntity entity = new AppVersionEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setPlatform(platform);
|
entity.setPlatform(platform);
|
||||||
entity.setVersionName(resolvedVersionName);
|
entity.setVersionName(resolvedVersionName);
|
||||||
entity.setVersionCode(resolvedVersionCode);
|
entity.setVersionCode(resolvedVersionCode);
|
||||||
@ -189,7 +189,7 @@ public class AppVersionController {
|
|||||||
}
|
}
|
||||||
AppVersionEntity saved = versionRepository.save(entity);
|
AppVersionEntity saved = versionRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"APP_VERSION",
|
"APP_VERSION",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"UPLOAD",
|
"UPLOAD",
|
||||||
@ -246,7 +246,7 @@ public class AppVersionController {
|
|||||||
entity.setGrayMemberIds(null);
|
entity.setGrayMemberIds(null);
|
||||||
AppVersionEntity saved = versionRepository.save(entity);
|
AppVersionEntity saved = versionRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"APP_VERSION",
|
"APP_VERSION",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
publishAction(previousStatus, saved.getPublishStatus(), publishImmediately),
|
publishAction(previousStatus, saved.getPublishStatus(), publishImmediately),
|
||||||
@ -273,7 +273,7 @@ public class AppVersionController {
|
|||||||
entity.setPublishStatus(AppVersionEntity.PublishStatus.DEPRECATED);
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.DEPRECATED);
|
||||||
AppVersionEntity saved = versionRepository.save(entity);
|
AppVersionEntity saved = versionRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"APP_VERSION",
|
"APP_VERSION",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"UNPUBLISH",
|
"UNPUBLISH",
|
||||||
@ -290,7 +290,7 @@ public class AppVersionController {
|
|||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@RequestBody Map<String, Object> body) throws Exception {
|
@RequestBody Map<String, Object> body) throws Exception {
|
||||||
AppVersionEntity entity = versionRepository.findById(id).orElseThrow();
|
AppVersionEntity entity = versionRepository.findById(id).orElseThrow();
|
||||||
if (publishConfigService.allowAnonymousUpdateCheck(entity.getAppId())) {
|
if (publishConfigService.allowAnonymousUpdateCheck(entity.getAppKey())) {
|
||||||
throw new com.xuqm.common.exception.BusinessException(400, "允许免登录检查更新的应用不支持灰度发布");
|
throw new com.xuqm.common.exception.BusinessException(400, "允许免登录检查更新的应用不支持灰度发布");
|
||||||
}
|
}
|
||||||
boolean enabled = Boolean.TRUE.equals(body.get("enabled"));
|
boolean enabled = Boolean.TRUE.equals(body.get("enabled"));
|
||||||
@ -305,7 +305,7 @@ public class AppVersionController {
|
|||||||
String selectionSource = body.get("selectionSource") == null ? "LOCAL"
|
String selectionSource = body.get("selectionSource") == null ? "LOCAL"
|
||||||
: body.get("selectionSource").toString().trim().toUpperCase();
|
: body.get("selectionSource").toString().trim().toUpperCase();
|
||||||
if (memberIds.isEmpty() && "CALLBACK".equals(selectionSource)) {
|
if (memberIds.isEmpty() && "CALLBACK".equals(selectionSource)) {
|
||||||
memberIds = publishConfigService.resolveGrayMembers(entity.getAppId(), body);
|
memberIds = publishConfigService.resolveGrayMembers(entity.getAppKey(), body);
|
||||||
}
|
}
|
||||||
entity.setGrayMode("MEMBERS");
|
entity.setGrayMode("MEMBERS");
|
||||||
entity.setGrayMemberIds(toJson(memberIds));
|
entity.setGrayMemberIds(toJson(memberIds));
|
||||||
@ -318,7 +318,7 @@ public class AppVersionController {
|
|||||||
entity.setPublishStatus(AppVersionEntity.PublishStatus.PUBLISHED);
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.PUBLISHED);
|
||||||
AppVersionEntity saved = versionRepository.save(entity);
|
AppVersionEntity saved = versionRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"APP_VERSION",
|
"APP_VERSION",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"GRAY_UPDATE",
|
"GRAY_UPDATE",
|
||||||
@ -336,7 +336,7 @@ public class AppVersionController {
|
|||||||
public ResponseEntity<ApiResponse<List<AppVersionEntity>>> list(
|
public ResponseEntity<ApiResponse<List<AppVersionEntity>>> list(
|
||||||
@RequestParam String appKey, @RequestParam AppVersionEntity.Platform platform) {
|
@RequestParam String appKey, @RequestParam AppVersionEntity.Platform platform) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
versionRepository.findByAppIdAndPlatformOrderByVersionCodeDesc(appKey, platform)));
|
versionRepository.findByAppKeyAndPlatformOrderByVersionCodeDesc(appKey, platform)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String publishAction(AppVersionEntity.PublishStatus previousStatus,
|
private String publishAction(AppVersionEntity.PublishStatus previousStatus,
|
||||||
|
|||||||
@ -56,7 +56,7 @@ public class RnBundleController {
|
|||||||
|
|
||||||
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
||||||
Optional<RnBundleEntity> latest = bundleRepository
|
Optional<RnBundleEntity> latest = bundleRepository
|
||||||
.findTopByAppIdAndModuleIdAndPlatformAndPublishStatusOrderByCreatedAtDesc(
|
.findTopByAppKeyAndModuleIdAndPlatformAndPublishStatusOrderByCreatedAtDesc(
|
||||||
appKey, moduleId, p, RnBundleEntity.PublishStatus.PUBLISHED);
|
appKey, moduleId, p, RnBundleEntity.PublishStatus.PUBLISHED);
|
||||||
|
|
||||||
if (latest.isEmpty()) {
|
if (latest.isEmpty()) {
|
||||||
@ -115,7 +115,7 @@ public class RnBundleController {
|
|||||||
|
|
||||||
RnBundleEntity entity = new RnBundleEntity();
|
RnBundleEntity entity = new RnBundleEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setModuleId(resolvedModuleId);
|
entity.setModuleId(resolvedModuleId);
|
||||||
entity.setPlatform(resolvedPlatform);
|
entity.setPlatform(resolvedPlatform);
|
||||||
entity.setVersion(resolvedVersion);
|
entity.setVersion(resolvedVersion);
|
||||||
@ -132,7 +132,7 @@ public class RnBundleController {
|
|||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
RnBundleEntity saved = bundleRepository.save(entity);
|
RnBundleEntity saved = bundleRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"RN_BUNDLE",
|
"RN_BUNDLE",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"UPLOAD",
|
"UPLOAD",
|
||||||
@ -160,11 +160,11 @@ public class RnBundleController {
|
|||||||
List<RnBundleEntity> result;
|
List<RnBundleEntity> result;
|
||||||
if (moduleId != null && platform != null) {
|
if (moduleId != null && platform != null) {
|
||||||
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
||||||
result = bundleRepository.findByAppIdAndModuleIdAndPlatformOrderByCreatedAtDesc(appKey, moduleId, p);
|
result = bundleRepository.findByAppKeyAndModuleIdAndPlatformOrderByCreatedAtDesc(appKey, moduleId, p);
|
||||||
} else if (moduleId != null) {
|
} else if (moduleId != null) {
|
||||||
result = bundleRepository.findByAppIdAndModuleIdOrderByCreatedAtDesc(appKey, moduleId);
|
result = bundleRepository.findByAppKeyAndModuleIdOrderByCreatedAtDesc(appKey, moduleId);
|
||||||
} else {
|
} else {
|
||||||
result = bundleRepository.findByAppIdOrderByCreatedAtDesc(appKey);
|
result = bundleRepository.findByAppKeyOrderByCreatedAtDesc(appKey);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ public class RnBundleController {
|
|||||||
entity.setGrayMemberIds(null);
|
entity.setGrayMemberIds(null);
|
||||||
RnBundleEntity saved = bundleRepository.save(entity);
|
RnBundleEntity saved = bundleRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"RN_BUNDLE",
|
"RN_BUNDLE",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
publishImmediately && (scheduledPublishAt == null || scheduledPublishAt.isBlank()) ? "PUBLISH" : "SCHEDULE_PUBLISH",
|
publishImmediately && (scheduledPublishAt == null || scheduledPublishAt.isBlank()) ? "PUBLISH" : "SCHEDULE_PUBLISH",
|
||||||
@ -220,7 +220,7 @@ public class RnBundleController {
|
|||||||
entity.setPublishStatus(RnBundleEntity.PublishStatus.DEPRECATED);
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.DEPRECATED);
|
||||||
RnBundleEntity saved = bundleRepository.save(entity);
|
RnBundleEntity saved = bundleRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"RN_BUNDLE",
|
"RN_BUNDLE",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"UNPUBLISH",
|
"UNPUBLISH",
|
||||||
@ -237,7 +237,7 @@ public class RnBundleController {
|
|||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@RequestBody Map<String, Object> body) throws Exception {
|
@RequestBody Map<String, Object> body) throws Exception {
|
||||||
RnBundleEntity entity = bundleRepository.findById(id).orElseThrow();
|
RnBundleEntity entity = bundleRepository.findById(id).orElseThrow();
|
||||||
if (publishConfigService.allowAnonymousUpdateCheck(entity.getAppId())) {
|
if (publishConfigService.allowAnonymousUpdateCheck(entity.getAppKey())) {
|
||||||
throw new com.xuqm.common.exception.BusinessException(400, "允许免登录检查更新的应用不支持灰度发布");
|
throw new com.xuqm.common.exception.BusinessException(400, "允许免登录检查更新的应用不支持灰度发布");
|
||||||
}
|
}
|
||||||
boolean enabled = Boolean.TRUE.equals(body.get("enabled"));
|
boolean enabled = Boolean.TRUE.equals(body.get("enabled"));
|
||||||
@ -252,7 +252,7 @@ public class RnBundleController {
|
|||||||
String selectionSource = body.get("selectionSource") == null ? "LOCAL"
|
String selectionSource = body.get("selectionSource") == null ? "LOCAL"
|
||||||
: body.get("selectionSource").toString().trim().toUpperCase();
|
: body.get("selectionSource").toString().trim().toUpperCase();
|
||||||
if (memberIds.isEmpty() && "CALLBACK".equals(selectionSource)) {
|
if (memberIds.isEmpty() && "CALLBACK".equals(selectionSource)) {
|
||||||
memberIds = publishConfigService.resolveGrayMembers(entity.getAppId(), body);
|
memberIds = publishConfigService.resolveGrayMembers(entity.getAppKey(), body);
|
||||||
}
|
}
|
||||||
entity.setGrayMode("MEMBERS");
|
entity.setGrayMode("MEMBERS");
|
||||||
entity.setGrayMemberIds(toJson(memberIds));
|
entity.setGrayMemberIds(toJson(memberIds));
|
||||||
@ -265,7 +265,7 @@ public class RnBundleController {
|
|||||||
entity.setPublishStatus(RnBundleEntity.PublishStatus.PUBLISHED);
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.PUBLISHED);
|
||||||
RnBundleEntity saved = bundleRepository.save(entity);
|
RnBundleEntity saved = bundleRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"RN_BUNDLE",
|
"RN_BUNDLE",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"GRAY_UPDATE",
|
"GRAY_UPDATE",
|
||||||
|
|||||||
@ -69,7 +69,7 @@ public class UnifiedReleaseController {
|
|||||||
}
|
}
|
||||||
AppVersionEntity entity = new AppVersionEntity();
|
AppVersionEntity entity = new AppVersionEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setPlatform(item.platform());
|
entity.setPlatform(item.platform());
|
||||||
entity.setVersionName(item.versionName());
|
entity.setVersionName(item.versionName());
|
||||||
entity.setVersionCode(item.versionCode());
|
entity.setVersionCode(item.versionCode());
|
||||||
@ -90,7 +90,7 @@ public class UnifiedReleaseController {
|
|||||||
}
|
}
|
||||||
AppVersionEntity saved = appVersionRepository.save(entity);
|
AppVersionEntity saved = appVersionRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"APP_VERSION",
|
"APP_VERSION",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"UPLOAD",
|
"UPLOAD",
|
||||||
@ -117,7 +117,7 @@ public class UnifiedReleaseController {
|
|||||||
|
|
||||||
RnBundleEntity entity = new RnBundleEntity();
|
RnBundleEntity entity = new RnBundleEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appKey);
|
entity.setAppKey(appKey);
|
||||||
entity.setModuleId(item.moduleId());
|
entity.setModuleId(item.moduleId());
|
||||||
entity.setPlatform(item.platform());
|
entity.setPlatform(item.platform());
|
||||||
entity.setVersion(item.version());
|
entity.setVersion(item.version());
|
||||||
@ -130,7 +130,7 @@ public class UnifiedReleaseController {
|
|||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
RnBundleEntity saved = rnBundleRepository.save(entity);
|
RnBundleEntity saved = rnBundleRepository.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
saved.getAppId(),
|
saved.getAppKey(),
|
||||||
"RN_BUNDLE",
|
"RN_BUNDLE",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
"UPLOAD",
|
"UPLOAD",
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "update_gray_member", uniqueConstraints = {
|
@Table(name = "update_gray_member", uniqueConstraints = {
|
||||||
@jakarta.persistence.UniqueConstraint(columnNames = {"appId", "groupName", "userId"})
|
@jakarta.persistence.UniqueConstraint(columnNames = {"appKey", "groupName", "userId"})
|
||||||
})
|
})
|
||||||
public class AppGrayMemberEntity {
|
public class AppGrayMemberEntity {
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ public class AppGrayMemberEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(length = 64)
|
@Column(length = 64)
|
||||||
private String groupName;
|
private String groupName;
|
||||||
@ -37,8 +37,8 @@ public class AppGrayMemberEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getGroupName() { return groupName; }
|
public String getGroupName() { return groupName; }
|
||||||
public void setGroupName(String groupName) { this.groupName = groupName; }
|
public void setGroupName(String groupName) { this.groupName = groupName; }
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "update_publish_config", uniqueConstraints = {
|
@Table(name = "update_publish_config", uniqueConstraints = {
|
||||||
@jakarta.persistence.UniqueConstraint(columnNames = {"appId"})
|
@jakarta.persistence.UniqueConstraint(columnNames = {"appKey"})
|
||||||
})
|
})
|
||||||
public class AppPublishConfigEntity {
|
public class AppPublishConfigEntity {
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ public class AppPublishConfigEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "TEXT")
|
||||||
private String configJson;
|
private String configJson;
|
||||||
@ -28,8 +28,8 @@ public class AppPublishConfigEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getConfigJson() { return configJson; }
|
public String getConfigJson() { return configJson; }
|
||||||
public void setConfigJson(String configJson) { this.configJson = configJson; }
|
public void setConfigJson(String configJson) { this.configJson = configJson; }
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "update_store_config", uniqueConstraints = {
|
@Table(name = "update_store_config", uniqueConstraints = {
|
||||||
@UniqueConstraint(columnNames = {"appId", "storeType"})
|
@UniqueConstraint(columnNames = {"appKey", "storeType"})
|
||||||
})
|
})
|
||||||
public class AppStoreConfigEntity {
|
public class AppStoreConfigEntity {
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ public class AppStoreConfigEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(nullable = false, length = 16)
|
@Column(nullable = false, length = 16)
|
||||||
@ -58,8 +58,8 @@ public class AppStoreConfigEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public StoreType getStoreType() { return storeType; }
|
public StoreType getStoreType() { return storeType; }
|
||||||
public void setStoreType(StoreType storeType) { this.storeType = storeType; }
|
public void setStoreType(StoreType storeType) { this.storeType = storeType; }
|
||||||
|
|||||||
@ -15,13 +15,13 @@ public class AppVersionEntity {
|
|||||||
public enum Platform { ANDROID, IOS, HARMONY }
|
public enum Platform { ANDROID, IOS, HARMONY }
|
||||||
public enum PublishStatus { DRAFT, PUBLISHED, DEPRECATED }
|
public enum PublishStatus { DRAFT, PUBLISHED, DEPRECATED }
|
||||||
/** Per-store review state used in storeReviewStatus JSON values. */
|
/** Per-store review state used in storeReviewStatus JSON values. */
|
||||||
public enum StoreReviewState { PENDING, UNDER_REVIEW, APPROVED, REJECTED }
|
public enum StoreReviewState { PENDING, SUBMITTING, UNDER_REVIEW, APPROVED, REJECTED }
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(nullable = false, length = 16)
|
@Column(nullable = false, length = 16)
|
||||||
@ -107,8 +107,8 @@ public class AppVersionEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public Platform getPlatform() { return platform; }
|
public Platform getPlatform() { return platform; }
|
||||||
public void setPlatform(Platform platform) { this.platform = platform; }
|
public void setPlatform(Platform platform) { this.platform = platform; }
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public class RnBundleEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String moduleId;
|
private String moduleId;
|
||||||
@ -73,8 +73,8 @@ public class RnBundleEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getModuleId() { return moduleId; }
|
public String getModuleId() { return moduleId; }
|
||||||
public void setModuleId(String moduleId) { this.moduleId = moduleId; }
|
public void setModuleId(String moduleId) { this.moduleId = moduleId; }
|
||||||
|
|||||||
@ -15,7 +15,7 @@ public class UpdateOperationLogEntity {
|
|||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String appId;
|
private String appKey;
|
||||||
|
|
||||||
@Column(nullable = false, length = 32)
|
@Column(nullable = false, length = 32)
|
||||||
private String resourceType;
|
private String resourceType;
|
||||||
@ -41,8 +41,8 @@ public class UpdateOperationLogEntity {
|
|||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
public void setId(String id) { this.id = id; }
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
public String getAppId() { return appId; }
|
public String getAppKey() { return appKey; }
|
||||||
public void setAppId(String appId) { this.appId = appId; }
|
public void setAppKey(String appKey) { this.appKey = appKey; }
|
||||||
|
|
||||||
public String getResourceType() { return resourceType; }
|
public String getResourceType() { return resourceType; }
|
||||||
public void setResourceType(String resourceType) { this.resourceType = resourceType; }
|
public void setResourceType(String resourceType) { this.resourceType = resourceType; }
|
||||||
|
|||||||
@ -6,5 +6,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface AppGrayMemberRepository extends JpaRepository<AppGrayMemberEntity, String> {
|
public interface AppGrayMemberRepository extends JpaRepository<AppGrayMemberEntity, String> {
|
||||||
List<AppGrayMemberEntity> findByAppIdOrderByGroupNameAscNameAscUserIdAsc(String appId);
|
List<AppGrayMemberEntity> findByAppKeyOrderByGroupNameAscNameAscUserIdAsc(String appKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,5 +6,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface AppPublishConfigRepository extends JpaRepository<AppPublishConfigEntity, String> {
|
public interface AppPublishConfigRepository extends JpaRepository<AppPublishConfigEntity, String> {
|
||||||
Optional<AppPublishConfigEntity> findByAppId(String appId);
|
Optional<AppPublishConfigEntity> findByAppKey(String appKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,9 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface AppStoreConfigRepository extends JpaRepository<AppStoreConfigEntity, String> {
|
public interface AppStoreConfigRepository extends JpaRepository<AppStoreConfigEntity, String> {
|
||||||
|
|
||||||
List<AppStoreConfigEntity> findByAppId(String appId);
|
List<AppStoreConfigEntity> findByAppKey(String appKey);
|
||||||
|
|
||||||
List<AppStoreConfigEntity> findByAppIdAndEnabled(String appId, boolean enabled);
|
List<AppStoreConfigEntity> findByAppKeyAndEnabled(String appKey, boolean enabled);
|
||||||
|
|
||||||
Optional<AppStoreConfigEntity> findByAppIdAndStoreType(String appId, AppStoreConfigEntity.StoreType storeType);
|
Optional<AppStoreConfigEntity> findByAppKeyAndStoreType(String appKey, AppStoreConfigEntity.StoreType storeType);
|
||||||
}
|
}
|
||||||
|
|||||||
某些文件未显示,因为此 diff 中更改的文件太多 显示更多
正在加载...
在新工单中引用
屏蔽一个用户