fix: 收口融合 QA 构建与验收安全基线

This commit is contained in:
wangzhibiao committed 2026-10-14 11:32:31 +08:00
1 parent 47bc6604e5
commit 5560a3ac04
28 files changed
+22947 -352

No files matched your search

+5 -7
View File
@@ -16,7 +16,7 @@
<ruoyi.version>3.9.2</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<java.version>21</java.version>
<spring-boot.version>4.1.0</spring-boot.version>
<mybatis-spring-boot.version>4.1.0</mybatis-spring-boot.version>
<druid.version>1.2.28</druid.version>
@@ -137,9 +137,8 @@
<version>${kaptcha.version}</version>
</dependency>
<!-- KingbaseES(人大金仓)驱动:官方未提供公网 Maven 仓库,驱动包直接放置在
ruoyi-admin/lib 目录,由 ruoyi-admin 模块的 kingbase profile 以 system scope 引入。
若 jar 文件名或版本变更,请同步修改 kingbase.version 属性及 ruoyi-admin 中的 systemPath。 -->
<!-- Approved vendor JDBC artifact: privately provision cn.com.kingbase:kingbase8.
See scripts/bootstrap/install-kingbase-jdbc.sh; no vendor binary in Git. -->
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
@@ -224,8 +223,7 @@
<version>3.13.0</version>
<configuration>
<parameters>true</parameters>
<source>${java.version}</source>
<target>${java.version}</target>
<release>${java.version}</release>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
@@ -262,4 +260,4 @@
</pluginRepository>
</pluginRepositories>
</project>
</project>
+2 -27
View File
@@ -94,7 +94,7 @@
</dependencies>
</profile>
<!-- KingbaseES(人大金仓):驱动包置于 ruoyi-admin/lib 目录,直接按文件路径引入 -->
<!-- KingbaseES: provision approved cn.com.kingbase:kingbase8 privately before build. -->
<profile>
<id>kingbase</id>
<activation>
@@ -104,31 +104,8 @@
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
<scope>system</scope>
<systemPath>${project.basedir}/lib/kingbase8-${kingbase.version}.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<!-- 打 war 包时把外部驱动一起放进 WEB-INF/lib -->
<webResources>
<resource>
<directory>${project.basedir}/lib</directory>
<targetPath>WEB-INF/lib</targetPath>
<filtering>false</filtering>
<includes>
<include>kingbase8-${kingbase.version}.jar</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
@@ -139,8 +116,6 @@
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
<!-- KingbaseES 等 system scope 驱动需要打进可执行 jar -->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
@@ -163,4 +138,4 @@
<finalName>${project.artifactId}</finalName>
</build>
</project>
</project>
+1 -13
View File
@@ -7,18 +7,6 @@
<groupId>com.ruoyi</groupId>
<version>3.9.2</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common</artifactId>
@@ -132,4 +120,4 @@
</dependencies>
</project>
</project>
-12
View File
@@ -7,18 +7,6 @@
<groupId>com.ruoyi</groupId>
<version>3.9.2</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
</plugins>
</build>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-iot</artifactId>
@@ -224,14 +224,8 @@ public class TrainAccommodationServiceImpl implements ITrainAccommodationService
boolean isTransferInsert = accommodation.getRemark() != null && accommodation.getRemark().contains("调房迁入");
if (!isTransferInsert && TrainAccommodation.STATUS_CHECKED_IN.equals(accommodation.getAccommodationStatus()))
{
try
{
iotLockAuthBridgeService.reconcileCheckin(accommodation.getAccommodationId(), accommodation.getCreateBy());
}
catch (Exception ex)
{
log.warn("IoT lock auth reconcile on checkin failed for accommodation {}: {}", accommodation.getAccommodationId(), ex.getMessage());
}
// Intent persistence failure must abort the surrounding accommodation transaction.
iotLockAuthBridgeService.reconcileCheckin(accommodation.getAccommodationId(), accommodation.getCreateBy());
}
}
else if (accessAuthorizationOrchestrator != null)
@@ -302,6 +296,7 @@ public class TrainAccommodationServiceImpl implements ITrainAccommodationService
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int checkIn(Long accommodationId, String operator)
{
TrainAccommodation exist = trainAccommodationMapper.selectTrainAccommodationByAccommodationId(accommodationId);
@@ -320,14 +315,8 @@ public class TrainAccommodationServiceImpl implements ITrainAccommodationService
boolean useIot = "iot".equalsIgnoreCase(authEngine) && iotLockAuthBridgeService != null;
if (useIot)
{
try
{
iotLockAuthBridgeService.reconcileCheckin(accommodationId, operator);
}
catch (Exception ex)
{
log.warn("IoT lock auth reconcile on checkIn failed for accommodation {}: {}", accommodationId, ex.getMessage());
}
// Intent persistence failure must abort the surrounding accommodation transaction.
iotLockAuthBridgeService.reconcileCheckin(accommodationId, operator);
}
else if (accessAuthorizationOrchestrator != null)
{
@@ -457,14 +446,8 @@ public class TrainAccommodationServiceImpl implements ITrainAccommodationService
if ("iot".equalsIgnoreCase(authEngine) && iotLockAuthBridgeService != null)
{
try
{
iotLockAuthBridgeService.reconcileTransfer(oldAccommodationId, newAcc.getAccommodationId(), operator);
}
catch (Exception ex)
{
log.error("IoT lock auth transfer reconcile failed for old {} -> new {}: {}", oldAccommodationId, newAcc.getAccommodationId(), ex.getMessage());
}
// Intent persistence failure must abort the surrounding accommodation transaction.
iotLockAuthBridgeService.reconcileTransfer(oldAccommodationId, newAcc.getAccommodationId(), operator);
}
return newAcc;
@@ -0,0 +1,59 @@
package com.ruoyi.train.service.impl;
import static org.junit.Assert.*;
import java.lang.reflect.*;
import java.util.*;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.train.domain.TrainAccommodation;
import com.ruoyi.train.integration.lock.service.ITrainLockAuthBridgeService;
import com.ruoyi.train.mapper.TrainAccommodationMapper;
import com.ruoyi.train.mapper.TrainDormitoryInfoMapper;
/** Actual business entry-point exception/transaction declarations, not a database rollback proof. */
public class Round5vAccommodationAtomicityTest {
private final RuntimeException failure = new IllegalStateException("synthetic-intent-persistence-failure");
@SuppressWarnings("unchecked")
private <T> T proxy(Class<T> type, InvocationHandler handler) {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, handler);
}
private void inject(Object object, String name, Object value) throws Exception {
Field field = TrainAccommodationServiceImpl.class.getDeclaredField(name);
field.setAccessible(true); field.set(object,value);
}
private TrainAccommodationServiceImpl service(boolean transfer) throws Exception {
TrainAccommodationServiceImpl service = transfer ? new TrainAccommodationServiceImpl() {
@Override public int insertTrainAccommodation(TrainAccommodation value) {
value.setAccommodationId(2L); return 1; // isolate transfer exception boundary only
}
} : new TrainAccommodationServiceImpl();
TrainAccommodation old = new TrainAccommodation(); old.setAccommodationId(1L);
old.setAccommodationStatus(TrainAccommodation.STATUS_CHECKED_IN); old.setDormId("QA_A"); old.setEnrollmentId(3L);
inject(service,"trainAccommodationMapper",proxy(TrainAccommodationMapper.class,(p,m,a)-> {
if (m.getName().equals("selectTrainAccommodationByAccommodationId")) return old;
if (m.getReturnType()==int.class) return 1;
return null;
}));
inject(service,"trainDormitoryInfoMapper",proxy(TrainDormitoryInfoMapper.class,(p,m,a)->null));
service.setAuthEngine("iot");
service.setIotLockAuthBridgeService(proxy(ITrainLockAuthBridgeService.class,(p,m,a)-> { throw failure; }));
return service;
}
@Test public void checkinDoesNotSwallowIntentPersistenceFailure() throws Exception {
try { service(false).checkIn(1L,"qa-test"); fail("Failure must propagate"); }
catch (RuntimeException expected) { assertSame(failure,expected); }
}
@Test public void transferDoesNotSwallowIntentPersistenceFailure() throws Exception {
try { service(true).transfer(1L,"QA_B","1","qa-test"); fail("Failure must propagate"); }
catch (RuntimeException expected) { assertSame(failure,expected); }
}
@Test public void checkoutStillPropagatesIntentPersistenceFailure() throws Exception {
try { service(false).checkOut(1L,"qa-test"); fail("Failure must propagate"); }
catch (RuntimeException expected) { assertSame(failure,expected); }
}
@Test public void checkinDeclaresRollbackBoundary() throws Exception {
Transactional annotation = TrainAccommodationServiceImpl.class.getMethod("checkIn",Long.class,String.class).getAnnotation(Transactional.class);
assertNotNull(annotation); assertTrue(Arrays.asList(annotation.rollbackFor()).contains(Exception.class));
}
}