feat: add readonly platform identity repository

master
chenyuan 4 weeks ago
parent 10a66eca1f
commit ac78354498

@ -0,0 +1,11 @@
package com.yau.digitalrmb.platformintegration.application;
import com.yau.digitalrmb.platformintegration.domain.PlatformActor;
import java.util.Optional;
public interface PlatformIdentityRepository {
Optional<PlatformActor> findByPlatformUserId(long platformUserId);
Optional<PlatformActor> findBySchoolAccount(String schoolAccount);
}

@ -0,0 +1,11 @@
package com.yau.digitalrmb.platformintegration.domain;
import java.time.Instant;
public record PlatformActor(
long platformUserId,
String account,
String displayName,
PlatformRole role,
Instant tokenSigningTime) {
}

@ -0,0 +1,65 @@
package com.yau.digitalrmb.platformintegration.infrastructure;
import com.yau.digitalrmb.platformintegration.application.PlatformIdentityRepository;
import com.yau.digitalrmb.platformintegration.domain.PlatformActor;
import com.yau.digitalrmb.platformintegration.domain.PlatformRole;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
@Repository
public class JdbcPlatformIdentityRepository implements PlatformIdentityRepository {
private static final String TEACHER_QUERY = """
SELECT cu.ID, cu.CODE, cu.NAME, t.add_time AS signing_time
FROM core_user cu JOIN teacher t ON t.user_id = cu.ID
WHERE cu.JOB_TYPE1 = 'JT_S_02' AND cu.STATE = 'S1' AND cu.DEL_FLAG = 0
AND t.teacher_status = 1 AND t.add_time IS NOT NULL AND %s
""";
private static final String STUDENT_QUERY = """
SELECT cu.ID, cu.CODE, cu.NAME, s.add_time AS signing_time
FROM core_user cu JOIN student s ON s.user_id = cu.ID
WHERE cu.JOB_TYPE1 = 'JT_S_03' AND cu.STATE = 'S1' AND cu.DEL_FLAG = 0
AND s.student_status = 1 AND s.add_time IS NOT NULL AND %s
""";
private final JdbcClient jdbcClient;
public JdbcPlatformIdentityRepository(@Qualifier("platformJdbcClient") JdbcClient jdbcClient) {
this.jdbcClient = jdbcClient;
}
@Override
public Optional<PlatformActor> findByPlatformUserId(long platformUserId) {
return findBy("cu.ID = :value", platformUserId);
}
@Override
public Optional<PlatformActor> findBySchoolAccount(String schoolAccount) {
return findBy("cu.CODE = :value", schoolAccount);
}
private Optional<PlatformActor> findBy(String predicate, Object value) {
Optional<PlatformActor> teacher = query(TEACHER_QUERY.formatted(predicate), value, PlatformRole.TEACHER);
return teacher.isPresent() ? teacher : query(STUDENT_QUERY.formatted(predicate), value, PlatformRole.STUDENT);
}
private Optional<PlatformActor> query(String sql, Object value, PlatformRole role) {
return jdbcClient.sql(sql)
.param("value", value)
.query((resultSet, rowNumber) -> map(resultSet, role))
.optional();
}
private PlatformActor map(ResultSet resultSet, PlatformRole role) throws SQLException {
return new PlatformActor(
resultSet.getLong("ID"),
resultSet.getString("CODE"),
resultSet.getString("NAME"),
role,
resultSet.getTimestamp("signing_time").toInstant());
}
}

@ -0,0 +1,31 @@
package com.yau.digitalrmb.platformintegration.infrastructure;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.yau.digitalrmb.platformintegration.config.PlatformIntegrationProperties;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.simple.JdbcClient;
import javax.sql.DataSource;
@Configuration(proxyBeanMethods = false)
public class PlatformReadOnlyDataSourceConfig {
@Bean(name = "platformReadOnlyDataSource", destroyMethod = "close")
public HikariDataSource platformReadOnlyDataSource(PlatformIntegrationProperties properties) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(properties.getDatasource().getUrl());
config.setUsername(properties.getDatasource().getUsername());
config.setPassword(properties.getDatasource().getPassword());
config.setReadOnly(true);
config.setMaximumPoolSize(5);
config.setPoolName("platform-readonly");
return new HikariDataSource(config);
}
@Bean(name = "platformJdbcClient")
public JdbcClient platformJdbcClient(@Qualifier("platformReadOnlyDataSource") DataSource dataSource) {
return JdbcClient.create(dataSource);
}
}

@ -0,0 +1,65 @@
package com.yau.digitalrmb.platformintegration.infrastructure;
import com.yau.digitalrmb.platformintegration.application.PlatformIdentityRepository;
import com.yau.digitalrmb.platformintegration.domain.PlatformActor;
import com.yau.digitalrmb.platformintegration.domain.PlatformRole;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.simple.JdbcClient;
import javax.sql.DataSource;
import java.sql.Timestamp;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat;
class JdbcPlatformIdentityRepositoryTest {
private DataSource dataSource;
private PlatformIdentityRepository repository;
@BeforeEach
void setUp() throws Exception {
JdbcDataSource source = new JdbcDataSource();
source.setURL("jdbc:h2:mem:platform_identity;MODE=MySQL;DB_CLOSE_DELAY=-1");
source.setUser("sa");
dataSource = source;
execute("DROP ALL OBJECTS");
createSchema();
repository = new JdbcPlatformIdentityRepository(JdbcClient.create(dataSource));
}
@Test
void resolvesEnabledTeacherFromCoreUserAndTeacherProfile() throws Exception {
execute("INSERT INTO core_user(ID, CODE, NAME, STATE, JOB_TYPE1, DEL_FLAG) VALUES (101, 't001', '教师甲', 'S1', 'JT_S_02', 0)");
execute("INSERT INTO teacher(teacher_id, user_id, teacher_status, add_time) VALUES (1, 101, 1, '2026-01-01 00:00:00')");
PlatformActor actor = repository.findByPlatformUserId(101L).orElseThrow();
assertThat(actor.account()).isEqualTo("t001");
assertThat(actor.role()).isEqualTo(PlatformRole.TEACHER);
assertThat(actor.tokenSigningTime()).isEqualTo(Instant.parse("2025-12-31T16:00:00Z"));
}
@Test
void excludesDisabledOrUnsupportedUsers() throws Exception {
execute("INSERT INTO core_user(ID, CODE, NAME, STATE, JOB_TYPE1, DEL_FLAG) VALUES (201, 's001', '学生甲', 'S1', 'JT_S_03', 0)");
execute("INSERT INTO student(student_id, user_id, student_status, add_time) VALUES (1, 201, 2, '2026-01-01 00:00:00')");
execute("INSERT INTO core_user(ID, CODE, NAME, STATE, JOB_TYPE1, DEL_FLAG) VALUES (202, 'admin', '管理员', 'S1', 'JT_S_01', 0)");
assertThat(repository.findByPlatformUserId(201L)).isEmpty();
assertThat(repository.findByPlatformUserId(202L)).isEmpty();
}
private void createSchema() throws Exception {
execute("CREATE TABLE core_user(ID BIGINT PRIMARY KEY, CODE VARCHAR(64), NAME VARCHAR(64), STATE VARCHAR(16), JOB_TYPE1 VARCHAR(16), DEL_FLAG INT)");
execute("CREATE TABLE teacher(teacher_id BIGINT PRIMARY KEY, user_id BIGINT, teacher_status INT, add_time TIMESTAMP)");
execute("CREATE TABLE student(student_id BIGINT PRIMARY KEY, user_id BIGINT, student_status INT, add_time TIMESTAMP)");
}
private void execute(String sql) throws Exception {
try (var connection = dataSource.getConnection(); var statement = connection.createStatement()) {
statement.execute(sql);
}
}
}

@ -0,0 +1,43 @@
package com.yau.digitalrmb.platformintegration.infrastructure;
import com.zaxxer.hikari.HikariDataSource;
import com.yau.digitalrmb.platformintegration.config.PlatformIntegrationProperties;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.simple.JdbcClient;
import static org.assertj.core.api.Assertions.assertThat;
class PlatformReadOnlyDataSourceConfigTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PropertiesConfiguration.class, PlatformReadOnlyDataSourceConfig.class))
.withPropertyValues(
"platform-integration.datasource.url=jdbc:h2:mem:platform_config",
"platform-integration.datasource.username=sa",
"platform-integration.datasource.password=test-password",
"platform-integration.token.max-age=PT2M",
"platform-integration.token.teacher-claim-value=teacher",
"platform-integration.token.student-claim-value=student",
"platform-integration.cas.login-url=https://sso.example.edu/login",
"platform-integration.cas.validate-url=https://sso.example.edu/p3/serviceValidate",
"platform-integration.cas.callback-url=https://rmb.example.edu/api/v1/auth/cas/callback",
"platform-integration.frontend.callback-url=https://rmb.example.edu/sso-callback");
@Test
void createsNamedReadOnlyDatasourceAndJdbcClient() {
contextRunner.run(context -> {
HikariDataSource dataSource = context.getBean("platformReadOnlyDataSource", HikariDataSource.class);
assertThat(dataSource.isReadOnly()).isTrue();
assertThat(context.getBean("platformJdbcClient", JdbcClient.class)).isNotNull();
});
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PlatformIntegrationProperties.class)
static class PropertiesConfiguration {
}
}
Loading…
Cancel
Save