feat: add readonly platform identity projection

master
chenyuan 4 weeks ago
parent 977d7eb6ce
commit e84d424c42

@ -0,0 +1,85 @@
package com.yau.digitalrmb.identity.application;
import com.yau.digitalrmb.identity.infrastructure.persistence.entity.PlatformUserSnapshotEntity;
import com.yau.digitalrmb.identity.infrastructure.persistence.entity.UserEntity;
import com.yau.digitalrmb.identity.infrastructure.persistence.mapper.PlatformUserSnapshotMapper;
import com.yau.digitalrmb.identity.infrastructure.persistence.mapper.UserMapper;
import com.yau.digitalrmb.platformintegration.domain.PlatformActor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
@Service
public class PlatformIdentityProjectionService {
private static final String EXTERNAL_SSO_ONLY_PASSWORD = "{noop}EXTERNAL_SSO_ONLY";
private final UserMapper userMapper;
private final PlatformUserSnapshotMapper snapshotMapper;
private final JdbcTemplate jdbcTemplate;
public PlatformIdentityProjectionService(UserMapper userMapper,
PlatformUserSnapshotMapper snapshotMapper,
JdbcTemplate jdbcTemplate) {
this.userMapper = userMapper;
this.snapshotMapper = snapshotMapper;
this.jdbcTemplate = jdbcTemplate;
}
@Transactional
public void project(PlatformActor actor) {
projectUser(actor);
projectSnapshot(actor);
projectRole(actor);
}
private void projectUser(PlatformActor actor) {
UserEntity user = userMapper.selectById(actor.platformUserId());
if (user == null) {
user = new UserEntity();
user.setId(actor.platformUserId());
user.setUsername(actor.account());
user.setPasswordHash(EXTERNAL_SSO_ONLY_PASSWORD);
user.setEnabled(true);
userMapper.insert(user);
return;
}
user.setUsername(actor.account());
user.setEnabled(true);
userMapper.updateById(user);
}
private void projectSnapshot(PlatformActor actor) {
PlatformUserSnapshotEntity snapshot = snapshotMapper.selectById(actor.platformUserId());
boolean newSnapshot = snapshot == null;
if (newSnapshot) {
snapshot = new PlatformUserSnapshotEntity();
snapshot.setPlatformUserId(actor.platformUserId());
}
snapshot.setAccount(actor.account());
snapshot.setDisplayName(actor.displayName());
snapshot.setRoleKey(actor.role().name());
snapshot.setSourceUpdatedAt(LocalDateTime.ofInstant(actor.tokenSigningTime(), ZoneOffset.UTC));
snapshot.setSyncedAt(LocalDateTime.now(ZoneOffset.UTC));
if (newSnapshot) {
snapshotMapper.insert(snapshot);
} else {
snapshotMapper.updateById(snapshot);
}
}
private void projectRole(PlatformActor actor) {
jdbcTemplate.update("DELETE FROM sys_user_role WHERE user_id = ?", actor.platformUserId());
jdbcTemplate.update("INSERT INTO sys_user_role (user_id, role_id) VALUES (?, ?)",
actor.platformUserId(), roleId(actor));
}
private long roleId(PlatformActor actor) {
return switch (actor.role()) {
case TEACHER -> 1001L;
case STUDENT -> 1002L;
};
}
}

@ -0,0 +1,29 @@
package com.yau.digitalrmb.identity.infrastructure.persistence.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
@TableName("platform_user_snapshot")
public class PlatformUserSnapshotEntity {
@TableId(value = "platform_user_id", type = IdType.INPUT)
private Long platformUserId;
private String account;
private String displayName;
private String roleKey;
private LocalDateTime sourceUpdatedAt;
@TableField("synced_at")
private LocalDateTime syncedAt;
}

@ -0,0 +1,9 @@
package com.yau.digitalrmb.identity.infrastructure.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yau.digitalrmb.identity.infrastructure.persistence.entity.PlatformUserSnapshotEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PlatformUserSnapshotMapper extends BaseMapper<PlatformUserSnapshotEntity> {
}

@ -0,0 +1,28 @@
CREATE TABLE platform_user_snapshot (
platform_user_id BIGINT PRIMARY KEY,
account VARCHAR(64) NOT NULL,
display_name VARCHAR(64) NOT NULL,
role_key VARCHAR(16) NOT NULL,
source_updated_at TIMESTAMP NOT NULL,
synced_at TIMESTAMP NOT NULL
);
CREATE TABLE auth_login_exchange_code (
code_hash CHAR(64) PRIMARY KEY,
platform_user_id BIGINT NOT NULL,
expires_at TIMESTAMP NOT NULL,
consumed_at TIMESTAMP NULL
);
CREATE TABLE auth_refresh_token (
token_hash CHAR(64) PRIMARY KEY,
platform_user_id BIGINT NOT NULL,
expires_at TIMESTAMP NOT NULL,
revoked_at TIMESTAMP NULL
);
INSERT INTO sys_role (id, name, created_at, updated_at, created_by, updated_by, deleted)
VALUES (1001, 'TEACHER', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'SYSTEM', 'SYSTEM', FALSE);
INSERT INTO sys_role (id, name, created_at, updated_at, created_by, updated_by, deleted)
VALUES (1002, 'STUDENT', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'SYSTEM', 'SYSTEM', FALSE);

@ -0,0 +1,43 @@
package com.yau.digitalrmb.identity;
import com.yau.digitalrmb.identity.application.PlatformIdentityProjectionService;
import com.yau.digitalrmb.platformintegration.domain.PlatformActor;
import com.yau.digitalrmb.platformintegration.domain.PlatformRole;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;
import javax.sql.DataSource;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@ActiveProfiles("test")
class PlatformIdentityProjectionServiceTest {
@Autowired
private PlatformIdentityProjectionService projectionService;
@Autowired
private DataSource dataSource;
@Test
void projectionIsIdempotentAndOwnsExactlyOneRole() {
PlatformActor teacher = new PlatformActor(101L, 1L, "t001", "教师甲", PlatformRole.TEACHER,
Instant.parse("2026-01-01T00:00:00Z"));
projectionService.project(teacher);
projectionService.project(teacher);
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
Integer associationCount = jdbc.queryForObject("SELECT COUNT(*) FROM sys_user_role WHERE user_id = 101", Integer.class);
String role = jdbc.queryForObject("SELECT role_key FROM platform_user_snapshot WHERE platform_user_id = 101", String.class);
String userName = jdbc.queryForObject("SELECT username FROM sys_user WHERE id = 101", String.class);
assertThat(associationCount).isEqualTo(1);
assertThat(role).isEqualTo("TEACHER");
assertThat(userName).isEqualTo("t001");
}
}
Loading…
Cancel
Save