20260821230656

This commit is contained in:
oneao committed 2026-08-21 23:06:56 +08:00
1 parent 2e5d532f23
commit c43b4c8364
144 files changed
+7665 -242

No files matched your search

@@ -10,13 +10,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 登录日志服务:在登录成功或失败时采集客户端信息,并通过通用 saveobjt 机制写入 s_login_log 表。
* 登录日志服务:在登录成功或失败时采集客户端信息,并通过通用 saveobjt 机制写入 s_log_login 表。
* 写日志失败不影响登录主流程;机构上下文通过 OrgContext 临时设置并在 finally 中清理。
*/
@Service
@@ -24,6 +23,9 @@ public class LoginLogService {
private static final Logger log = LoggerFactory.getLogger(LoginLogService.class);
/** 登录类型:密码登录(与 s_log_login.b_login_type 约定一致) */
private static final String LOGIN_TYPE_PASSWORD = "password";
private final DataSaveService dataSaveService;
private final ClientInfoExtractor clientInfoExtractor;
@@ -49,10 +51,10 @@ public class LoginLogService {
Map<String, Object> row = new LinkedHashMap<>();
row.put("b_id", IdGenerator.nextId());
row.put("b_org_id", orgId);
row.put("b_user_id", userId);
row.put("b_account", account);
row.put("b_login_time", Timestamp.valueOf(LocalDateTime.now()));
row.put("b_login_type", LOGIN_TYPE_PASSWORD);
row.put("b_occurdatetime", Timestamp.from(java.time.Instant.now()));
row.put("b_ip", info.ip());
row.put("b_ip_location", info.ipLocation());
row.put("b_browser", info.browser());
@@ -66,7 +68,7 @@ public class LoginLogService {
row.put("b_session_id", sessionId);
Map<String, Object> tableOp = new LinkedHashMap<>();
tableOp.put("table", "s_login_log");
tableOp.put("table", "s_log_login");
tableOp.put("key_field", "b_id");
tableOp.put("inserts", List.of(row));
@@ -166,33 +166,12 @@ public class DbUtils {
String account
) throws SQLException {
try (Connection connection = dataSourceManager.getConnection(orgId)) {
SQLException failure = null;
boolean privileged = false;
try {
setSystemUserVisibility(connection, 1);
privileged = true;
String sql = "SELECT [b_id], [b_account], [b_name], [b_password] "
+ "FROM [b_user] WHERE [b_account] = ? AND [b_canuse] = 1";
try (PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, account);
try (ResultSet resultSet = statement.executeQuery()) {
return readRows(resultSet);
}
}
} catch (SQLException exception) {
failure = exception;
throw exception;
} finally {
if (privileged) {
try {
setSystemUserVisibility(connection, null);
} catch (SQLException clearException) {
if (failure != null) {
failure.addSuppressed(clearException);
} else {
throw clearException;
}
}
String sql = "SELECT [b_id], [b_account], [b_name], [b_password] "
+ "FROM [b_user] WHERE [b_account] = ? AND [b_canuse] = 1";
try (PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, account);
try (ResultSet resultSet = statement.executeQuery()) {
return readRows(resultSet);
}
}
}
@@ -506,22 +485,6 @@ public class DbUtils {
return result.toString();
}
private void setSystemUserVisibility(
Connection connection,
Integer value
) throws SQLException {
String sql = "EXEC sys.sp_set_session_context @key = ?, @value = ?";
try (PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, "allow_g3soft");
if (value == null) {
statement.setNull(2, java.sql.Types.INTEGER);
} else {
statement.setInt(2, value);
}
statement.execute();
}
}
private String buildColumns(List<String> searchColumns) {
if (searchColumns == null || searchColumns.isEmpty()) {
return "*";
@@ -0,0 +1,130 @@
package cn.g3soft.fmsapi.utils.snowflake;
import cn.g3soft.fmsapi.utils.snowflake.contract.IdGeneratorException;
import cn.g3soft.fmsapi.utils.snowflake.contract.IdGeneratorOptions;
import cn.g3soft.fmsapi.utils.snowflake.idgen.DefaultIdGenerator;
import cn.g3soft.fmsapi.utils.snowflake.idgen.IdGenerator;
import org.junit.jupiter.api.Test;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class DefaultIdGeneratorTests {
private static IdGeneratorOptions options(short workerId) {
IdGeneratorOptions o = new IdGeneratorOptions(workerId);
o.Method = 1; // 漂移算法(生产默认,自带时钟回拨保护)
return o;
}
@Test
void generatedIds_areUniqueInBulk() throws Exception {
DefaultIdGenerator gen = new DefaultIdGenerator(options((short) 1));
int n = 100_000;
Set<Long> ids = ConcurrentHashMap.newKeySet(n);
for (int i = 0; i < n; i++) {
ids.add(gen.newLong());
}
assertThat(ids).hasSize(n);
}
@Test
void generatedIds_areStrictlyIncreasingInSingleThread() throws Exception {
DefaultIdGenerator gen = new DefaultIdGenerator(options((short) 1));
long prev = gen.newLong();
for (int i = 0; i < 10_000; i++) {
long cur = gen.newLong();
assertThat(cur).isGreaterThan(prev);
prev = cur;
}
}
@Test
void generatedIds_concurrentNoDuplicates() throws Exception {
DefaultIdGenerator gen = new DefaultIdGenerator(options((short) 1));
int threads = 8;
int perThread = 10_000;
Set<Long> ids = ConcurrentHashMap.newKeySet();
ExecutorService pool = Executors.newFixedThreadPool(threads);
CountDownLatch start = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(threads);
for (int t = 0; t < threads; t++) {
pool.submit(() -> {
try {
start.await();
for (int i = 0; i < perThread; i++) {
ids.add(gen.newLong());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.countDown();
}
});
}
start.countDown();
assertThat(done.await(30, TimeUnit.SECONDS)).isTrue();
pool.shutdownNow();
assertThat(ids).hasSize(threads * perThread);
}
@Test
void generatedId_encodesWorkerIdAndApproximateTimestamp() throws Exception {
IdGeneratorOptions o = options((short) 7);
DefaultIdGenerator gen = new DefaultIdGenerator(o);
long id = gen.newLong();
int timestampShift = o.WorkerIdBitLength + o.SeqBitLength; // 12
long timestamp = id >>> timestampShift;
long workerId = (id >>> o.SeqBitLength) & ((1L << o.WorkerIdBitLength) - 1);
assertThat(workerId).isEqualTo(7L);
long expectedTimestamp = System.currentTimeMillis() - o.BaseTime;
assertThat(timestamp).isBetween(expectedTimestamp - 5_000, expectedTimestamp + 5_000);
}
@Test
void invalidOptions_areRejected() {
IdGeneratorOptions badWorkerId = options((short) -1);
assertThatThrownBy(() -> new DefaultIdGenerator(badWorkerId))
.isInstanceOf(IdGeneratorException.class);
IdGeneratorOptions badBaseTime = options((short) 1);
badBaseTime.BaseTime = 1000L; // 早于校验下限 315504000000
assertThatThrownBy(() -> new DefaultIdGenerator(badBaseTime))
.isInstanceOf(IdGeneratorException.class);
IdGeneratorOptions tooManyBits = options((short) 1);
tooManyBits.WorkerIdBitLength = 22; // 22 + 6 > 22
assertThatThrownBy(() -> new DefaultIdGenerator(tooManyBits))
.isInstanceOf(IdGeneratorException.class);
IdGeneratorOptions tooSmallSeq = options((short) 1);
tooSmallSeq.SeqBitLength = 1; // 下限 2
assertThatThrownBy(() -> new DefaultIdGenerator(tooSmallSeq))
.isInstanceOf(IdGeneratorException.class);
}
@Test
void staticSingleton_worksAfterInitializationLikeApplicationStartup() throws Exception {
IdGenerator.setIdGenerator(options((short) 1));
long id = IdGenerator.nextId();
assertThat(id).isPositive();
}
}