feat(sample): 集成 Sentry 异常监控功能
- 添加 Sentry Android SDK 依赖 (版本 8.39.1) - 在 sample-app 中集成 Sentry 监控插件 - 添加 Sentry 初始化配置到应用 Application 类 - 在 MainActivity 中添加异常上报测试按钮 - 添加闪退测试功能用于验证 Sentry 监控 - 更新 AndroidManifest.xml 配置应用入口点 - 添加新的 gradle wrapper 文件支持项目构建 - 创建 sdk-core、sdk-im、sdk-push、sdk-update 模块基础结构 - 配置各 SDK 模块的 build.gradle.kts 文件 - 更新 libs.versions.toml 添加 Sentry 版本定义
这个提交包含在:
父节点
3285dfe79c
当前提交
f79a27862f
@ -40,7 +40,7 @@ spring:
|
|||||||
write-dates-as-timestamps: false
|
write-dates-as-timestamps: false
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: xuqm-im-service-secret-key-must-be-at-least-256-bits-long-for-hmac-sha
|
secret: ${XUQM_JWT_SECRET:xuqm-tenant-service-secret-key-must-be-at-least-256-bits-long-for-hmac}
|
||||||
expiration: 86400000
|
expiration: 86400000
|
||||||
|
|
||||||
im:
|
im:
|
||||||
|
|||||||
@ -0,0 +1,72 @@
|
|||||||
|
package com.xuqm.tenant.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "t_service_activation_request")
|
||||||
|
public class ServiceActivationRequestEntity {
|
||||||
|
|
||||||
|
public enum Status { PENDING, APPROVED, REJECTED }
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column(nullable = false, length = 64)
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 16)
|
||||||
|
private FeatureServiceEntity.Platform platform;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 16)
|
||||||
|
private FeatureServiceEntity.ServiceType serviceType;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 16)
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
@Column(length = 512)
|
||||||
|
private String applyReason;
|
||||||
|
|
||||||
|
@Column(length = 512)
|
||||||
|
private String reviewNote;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
private LocalDateTime reviewedAt;
|
||||||
|
|
||||||
|
public String getId() { return id; }
|
||||||
|
public void setId(String id) { this.id = id; }
|
||||||
|
|
||||||
|
public String getAppId() { return appId; }
|
||||||
|
public void setAppId(String appId) { this.appId = appId; }
|
||||||
|
|
||||||
|
public FeatureServiceEntity.Platform getPlatform() { return platform; }
|
||||||
|
public void setPlatform(FeatureServiceEntity.Platform platform) { this.platform = platform; }
|
||||||
|
|
||||||
|
public FeatureServiceEntity.ServiceType getServiceType() { return serviceType; }
|
||||||
|
public void setServiceType(FeatureServiceEntity.ServiceType serviceType) { this.serviceType = serviceType; }
|
||||||
|
|
||||||
|
public Status getStatus() { return status; }
|
||||||
|
public void setStatus(Status status) { this.status = status; }
|
||||||
|
|
||||||
|
public String getApplyReason() { return applyReason; }
|
||||||
|
public void setApplyReason(String applyReason) { this.applyReason = applyReason; }
|
||||||
|
|
||||||
|
public String getReviewNote() { return reviewNote; }
|
||||||
|
public void setReviewNote(String reviewNote) { this.reviewNote = reviewNote; }
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
|
|
||||||
|
public LocalDateTime getReviewedAt() { return reviewedAt; }
|
||||||
|
public void setReviewedAt(LocalDateTime reviewedAt) { this.reviewedAt = reviewedAt; }
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package com.xuqm.tenant.repository;
|
||||||
|
|
||||||
|
import com.xuqm.tenant.entity.FeatureServiceEntity;
|
||||||
|
import com.xuqm.tenant.entity.ServiceActivationRequestEntity;
|
||||||
|
import com.xuqm.tenant.entity.ServiceActivationRequestEntity.Status;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface ServiceActivationRequestRepository extends JpaRepository<ServiceActivationRequestEntity, String> {
|
||||||
|
|
||||||
|
Optional<ServiceActivationRequestEntity> findFirstByAppIdAndPlatformAndServiceTypeOrderByCreatedAtDesc(
|
||||||
|
String appId, FeatureServiceEntity.Platform platform, FeatureServiceEntity.ServiceType serviceType);
|
||||||
|
|
||||||
|
List<ServiceActivationRequestEntity> findByAppIdOrderByCreatedAtDesc(String appId);
|
||||||
|
|
||||||
|
Page<ServiceActivationRequestEntity> findByStatusOrderByCreatedAtDesc(Status status, Pageable pageable);
|
||||||
|
|
||||||
|
Page<ServiceActivationRequestEntity> findAllByOrderByCreatedAtDesc(Pageable pageable);
|
||||||
|
}
|
||||||
@ -2,8 +2,12 @@ package com.xuqm.tenant.service;
|
|||||||
|
|
||||||
import com.xuqm.common.exception.BusinessException;
|
import com.xuqm.common.exception.BusinessException;
|
||||||
import com.xuqm.tenant.entity.FeatureServiceEntity;
|
import com.xuqm.tenant.entity.FeatureServiceEntity;
|
||||||
|
import com.xuqm.tenant.entity.ServiceActivationRequestEntity;
|
||||||
|
import com.xuqm.tenant.entity.ServiceActivationRequestEntity.Status;
|
||||||
import com.xuqm.tenant.repository.FeatureServiceRepository;
|
import com.xuqm.tenant.repository.FeatureServiceRepository;
|
||||||
|
import com.xuqm.tenant.repository.ServiceActivationRequestRepository;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -15,32 +19,112 @@ import java.util.UUID;
|
|||||||
public class FeatureServiceManager {
|
public class FeatureServiceManager {
|
||||||
|
|
||||||
private final FeatureServiceRepository repository;
|
private final FeatureServiceRepository repository;
|
||||||
|
private final ServiceActivationRequestRepository requestRepository;
|
||||||
private static final SecureRandom random = new SecureRandom();
|
private static final SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
public FeatureServiceManager(FeatureServiceRepository repository) {
|
public FeatureServiceManager(FeatureServiceRepository repository,
|
||||||
|
ServiceActivationRequestRepository requestRepository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
|
this.requestRepository = requestRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FeatureServiceEntity> listByApp(String appId) {
|
public List<FeatureServiceEntity> listByApp(String appId) {
|
||||||
return repository.findByAppId(appId);
|
return repository.findByAppId(appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeatureServiceEntity toggle(String appId, FeatureServiceEntity.Platform platform,
|
/**
|
||||||
FeatureServiceEntity.ServiceType serviceType, boolean enable) {
|
* Submit an activation request. Disabling is immediate; enabling requires ops approval.
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public ServiceActivationRequestEntity submitActivationRequest(
|
||||||
|
String appId,
|
||||||
|
FeatureServiceEntity.Platform platform,
|
||||||
|
FeatureServiceEntity.ServiceType serviceType,
|
||||||
|
String applyReason) {
|
||||||
|
|
||||||
|
// Check if there's already a pending request
|
||||||
|
requestRepository.findFirstByAppIdAndPlatformAndServiceTypeOrderByCreatedAtDesc(appId, platform, serviceType)
|
||||||
|
.ifPresent(req -> {
|
||||||
|
if (req.getStatus() == Status.PENDING) {
|
||||||
|
throw new BusinessException(400, "已有待审核的开通申请,请等待运营人员处理");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ServiceActivationRequestEntity req = new ServiceActivationRequestEntity();
|
||||||
|
req.setId(UUID.randomUUID().toString());
|
||||||
|
req.setAppId(appId);
|
||||||
|
req.setPlatform(platform);
|
||||||
|
req.setServiceType(serviceType);
|
||||||
|
req.setStatus(Status.PENDING);
|
||||||
|
req.setApplyReason(applyReason);
|
||||||
|
req.setCreatedAt(LocalDateTime.now());
|
||||||
|
return requestRepository.save(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable a service immediately (no approval needed).
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public FeatureServiceEntity disable(String appId, FeatureServiceEntity.Platform platform,
|
||||||
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
FeatureServiceEntity entity = repository
|
FeatureServiceEntity entity = repository
|
||||||
.findByAppIdAndPlatformAndServiceType(appId, platform, serviceType)
|
.findByAppIdAndPlatformAndServiceType(appId, platform, serviceType)
|
||||||
|
.orElseThrow(() -> new BusinessException(404, "服务未开通"));
|
||||||
|
entity.setEnabled(false);
|
||||||
|
return repository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by ops when approving an activation request.
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public ServiceActivationRequestEntity approveRequest(String requestId, String reviewNote) {
|
||||||
|
ServiceActivationRequestEntity req = requestRepository.findById(requestId)
|
||||||
|
.orElseThrow(() -> new BusinessException(404, "申请不存在"));
|
||||||
|
if (req.getStatus() != Status.PENDING) {
|
||||||
|
throw new BusinessException(400, "申请已处理");
|
||||||
|
}
|
||||||
|
req.setStatus(Status.APPROVED);
|
||||||
|
req.setReviewNote(reviewNote);
|
||||||
|
req.setReviewedAt(LocalDateTime.now());
|
||||||
|
requestRepository.save(req);
|
||||||
|
|
||||||
|
// Activate the service
|
||||||
|
FeatureServiceEntity entity = repository
|
||||||
|
.findByAppIdAndPlatformAndServiceType(req.getAppId(), 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(appId);
|
e.setAppId(req.getAppId());
|
||||||
e.setPlatform(platform);
|
e.setPlatform(req.getPlatform());
|
||||||
e.setServiceType(serviceType);
|
e.setServiceType(req.getServiceType());
|
||||||
e.setSecretKey(generateSecretKey());
|
e.setSecretKey(generateSecretKey());
|
||||||
e.setCreatedAt(LocalDateTime.now());
|
e.setCreatedAt(LocalDateTime.now());
|
||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
entity.setEnabled(enable);
|
entity.setEnabled(true);
|
||||||
return repository.save(entity);
|
repository.save(entity);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by ops when rejecting an activation request.
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public ServiceActivationRequestEntity rejectRequest(String requestId, String reviewNote) {
|
||||||
|
ServiceActivationRequestEntity req = requestRepository.findById(requestId)
|
||||||
|
.orElseThrow(() -> new BusinessException(404, "申请不存在"));
|
||||||
|
if (req.getStatus() != Status.PENDING) {
|
||||||
|
throw new BusinessException(400, "申请已处理");
|
||||||
|
}
|
||||||
|
req.setStatus(Status.REJECTED);
|
||||||
|
req.setReviewNote(reviewNote);
|
||||||
|
req.setReviewedAt(LocalDateTime.now());
|
||||||
|
return requestRepository.save(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ServiceActivationRequestEntity> listRequestsByApp(String appId) {
|
||||||
|
return requestRepository.findByAppIdOrderByCreatedAtDesc(appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeatureServiceEntity getOrFail(String appId, FeatureServiceEntity.Platform platform,
|
public FeatureServiceEntity getOrFail(String appId, FeatureServiceEntity.Platform platform,
|
||||||
|
|||||||
@ -52,7 +52,7 @@ spring:
|
|||||||
write-dates-as-timestamps: false
|
write-dates-as-timestamps: false
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: xuqm-tenant-service-secret-key-must-be-at-least-256-bits-long-for-hmac
|
secret: ${XUQM_JWT_SECRET:xuqm-tenant-service-secret-key-must-be-at-least-256-bits-long-for-hmac}
|
||||||
expiration: 86400000
|
expiration: 86400000
|
||||||
|
|
||||||
captcha:
|
captcha:
|
||||||
|
|||||||
@ -25,7 +25,7 @@ spring:
|
|||||||
max-request-size: 200MB
|
max-request-size: 200MB
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: xuqm-update-service-secret-key-must-be-at-least-256-bits-long-for-hmac
|
secret: ${XUQM_JWT_SECRET:xuqm-tenant-service-secret-key-must-be-at-least-256-bits-long-for-hmac}
|
||||||
expiration: 86400000
|
expiration: 86400000
|
||||||
|
|
||||||
update:
|
update:
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户