feat(android): add device activation flow and update dependencies

重构项目架构,引入启动页(SplashActivity)检查激活状态,未激活设备引导至激活页(ActivationActivity)完成provision+token流程。集成Retrofit+RxJava3网络层、Room数据库、Lifecycle组件、MMKV等依赖,升级OkHttp/Gson版本,并将构建配置从旧项目全面迁移至新项目结构。
This commit is contained in:
TongTongStudio
2026-08-03 00:25:51 +08:00
parent 61398ff4ff
commit ded6fe5ab7
105 changed files with 5363 additions and 1503 deletions

View File

@@ -0,0 +1,28 @@
package com.ttstd.signaling.store;
import com.ttstd.signaling.model.AdminAccount;
import java.util.List;
import java.util.Optional;
/**
* 后台管理员存储抽象。与 {@code UserStore} 互不耦合JPA 与内存两套实现可切换。
*/
public interface AdminStore {
AdminAccount save(AdminAccount admin);
Optional<AdminAccount> findById(String adminId);
Optional<AdminAccount> findByUsername(String username);
boolean existsByUsername(String username);
boolean existsById(String adminId);
List<AdminAccount> findAll();
long count();
void delete(String adminId);
}

View File

@@ -16,7 +16,13 @@ public interface DeviceStore {
DeviceAccount save(DeviceAccount account);
List<DeviceAccount> listAll();
/** 删除设备。设备不存在时静默返回。 */
void delete(String deviceUid);
boolean isSnAllowed(String sn);
void addAllowedSn(DeviceAllowlist entry);
List<DeviceAllowlist> listAllowed();
/** 从 SN 白名单移除条目。 */
void removeAllowedSn(String sn);
}

View File

@@ -0,0 +1,74 @@
package com.ttstd.signaling.store;
import com.ttstd.signaling.model.AdminAccount;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
/** 内存实现不依赖数据库用于测试与本地演示storage.mode=memory。 */
@Component
@Profile("memory")
public class InMemoryAdminStore implements AdminStore {
private final ConcurrentMap<String, AdminAccount> byId = new ConcurrentHashMap<>();
private final ConcurrentMap<String, String> usernameToId = new ConcurrentHashMap<>();
@Override
public synchronized AdminAccount save(AdminAccount admin) {
byId.put(admin.getAdminId(), admin);
if (admin.getUsername() != null) {
usernameToId.put(admin.getUsername().toLowerCase(java.util.Locale.ROOT), admin.getAdminId());
}
return admin;
}
@Override
public Optional<AdminAccount> findById(String adminId) {
return Optional.ofNullable(byId.get(adminId));
}
@Override
public Optional<AdminAccount> findByUsername(String username) {
if (username == null) {
return Optional.empty();
}
String id = usernameToId.get(username.toLowerCase(java.util.Locale.ROOT));
return Optional.ofNullable(id == null ? null : byId.get(id));
}
@Override
public boolean existsByUsername(String username) {
if (username == null) {
return false;
}
return usernameToId.containsKey(username.toLowerCase(java.util.Locale.ROOT));
}
@Override
public boolean existsById(String adminId) {
return byId.containsKey(adminId);
}
@Override
public List<AdminAccount> findAll() {
return List.copyOf(byId.values());
}
@Override
public long count() {
return byId.size();
}
@Override
public synchronized void delete(String adminId) {
AdminAccount removed = byId.remove(adminId);
if (removed != null && removed.getUsername() != null) {
usernameToId.remove(removed.getUsername().toLowerCase(java.util.Locale.ROOT));
}
}
}

View File

@@ -48,6 +48,14 @@ public class InMemoryDeviceStore implements DeviceStore {
return List.copyOf(byUid.values());
}
@Override
public synchronized void delete(String deviceUid) {
DeviceAccount removed = byUid.remove(deviceUid);
if (removed != null) {
snToUid.remove(removed.getSn());
}
}
@Override
public synchronized boolean isSnAllowed(String sn) {
return allowlist.containsKey(sn);
@@ -62,4 +70,9 @@ public class InMemoryDeviceStore implements DeviceStore {
public synchronized List<DeviceAllowlist> listAllowed() {
return List.copyOf(allowlist.values());
}
@Override
public synchronized void removeAllowedSn(String sn) {
allowlist.remove(sn);
}
}

View File

@@ -45,4 +45,12 @@ public class InMemoryUserStore implements UserStore {
public synchronized List<UserAccount> listAll() {
return List.copyOf(byId.values());
}
@Override
public synchronized void delete(String userId) {
UserAccount removed = byId.remove(userId);
if (removed != null) {
usernameToId.remove(removed.getUsername());
}
}
}

View File

@@ -0,0 +1,61 @@
package com.ttstd.signaling.store;
import com.ttstd.signaling.model.AdminAccount;
import com.ttstd.signaling.repository.AdminAccountRepository;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
/** JPA 实现:管理员账号持久化到 MySQL。仅在 storage.mode=jpa默认时生效。 */
@Component
@Profile("!memory")
public class JpaAdminStore implements AdminStore {
private final AdminAccountRepository repository;
public JpaAdminStore(AdminAccountRepository repository) {
this.repository = repository;
}
@Override
public AdminAccount save(AdminAccount admin) {
return repository.save(admin);
}
@Override
public Optional<AdminAccount> findById(String adminId) {
return repository.findById(adminId);
}
@Override
public Optional<AdminAccount> findByUsername(String username) {
return repository.findByUsername(username);
}
@Override
public boolean existsByUsername(String username) {
return repository.existsByUsername(username);
}
@Override
public boolean existsById(String adminId) {
return repository.existsById(adminId);
}
@Override
public List<AdminAccount> findAll() {
return repository.findAll();
}
@Override
public long count() {
return repository.count();
}
@Override
public void delete(String adminId) {
repository.deleteById(adminId);
}
}

View File

@@ -49,6 +49,11 @@ public class JpaDeviceStore implements DeviceStore {
return deviceRepository.findAll();
}
@Override
public void delete(String deviceUid) {
deviceRepository.deleteById(deviceUid);
}
@Override
public boolean isSnAllowed(String sn) {
return allowlistRepository.existsBySn(sn);
@@ -63,4 +68,9 @@ public class JpaDeviceStore implements DeviceStore {
public List<DeviceAllowlist> listAllowed() {
return allowlistRepository.findAll();
}
@Override
public void removeAllowedSn(String sn) {
allowlistRepository.deleteById(sn);
}
}

View File

@@ -43,4 +43,9 @@ public class JpaUserStore implements UserStore {
public List<UserAccount> listAll() {
return repository.findAll();
}
@Override
public void delete(String userId) {
repository.deleteById(userId);
}
}

View File

@@ -14,4 +14,7 @@ public interface UserStore {
boolean existsByUsername(String username);
UserAccount save(UserAccount account);
List<UserAccount> listAll();
/** 删除账号。账号不存在时静默返回。 */
void delete(String userId);
}