修复数据映射:对齐住宿有效状态关系并修正课次考勤名册参数契约

This commit is contained in:
wangzhibiao committed 2026-10-09 16:20:24 +08:00
1 parent 1149c2b141
commit b1d5a999ca
3 files changed
+143 -13

No files matched your search

@@ -85,16 +85,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.accommodation_status as accommodationStatus
from train_enrollment e
left join train_student s on e.student_id = s.s_id
left join (
select a1.enrollment_id, a1.dorm_id, a1.bed_no, a1.accommodation_status
from train_accommodation a1
inner join (
select enrollment_id, max(accommodation_id) as max_id
from train_accommodation
group by enrollment_id
) a2 on a1.accommodation_id = a2.max_id
) a on e.enrollment_id = a.enrollment_id
left join train_dormitory_info d on a.dorm_id = d.dorm_id
left join train_accommodation a on e.enrollment_id = a.enrollment_id and a.accommodation_status in (0, 1)
left join train_dormitory_info d on a.dorm_id = d.dorm_id and d.del_flag = '0'
<where>
e.plan_id = #{planId}
<if test="studentName != null and studentName != ''">
@@ -0,0 +1,139 @@
package com.ruoyi.train.mapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.junit.Test;
import com.ruoyi.train.domain.TrainClassPlan;
import com.ruoyi.train.domain.TrainEnrollment;
import com.ruoyi.train.domain.query.EnrollmentQuery;
import com.ruoyi.train.domain.vo.EnrollmentItemVO;
/**
* 参训登记 Mapper 数据映射契约与合规性保护测试 (TrainEnrollmentMapperContractTest)
* 验证住宿关联只使用真实系统表 train_accommodation 与 train_dormitory_info,
* 严禁引用历史不存在的假表,且 MyBatis 映射可成功解析。
*/
public class TrainEnrollmentMapperContractTest
{
@Test
public void enrollmentMapperMustOnlyReferenceRealAccommodationTables() throws Exception
{
String xml = loadMapperXml("TrainEnrollmentMapper.xml");
// 1. 验证必须引用的真实系统表
assertContains(xml,
"train_enrollment",
"train_student",
"train_accommodation",
"train_dormitory_info"
);
// 2. 验证有效状态与逻辑删除过滤约束
assertContains(xml,
"a.accommodation_status in (0, 1)",
"d.del_flag = '0'"
);
// 3. 严禁引用不存在的临时假表
assertOmits(xml,
"train_dorm_assignment",
"train_dorm_room",
"train_dorm_bed",
"train_attendance"
);
// 4. 严禁使用非通用 PG 专有方言 DISTINCT ON
assertOmits(xml, "distinct on");
}
@Test
public void enrollmentMapperXmlParsesValidMyBatisMappedStatements() throws Exception
{
Configuration configuration = new Configuration();
// 注册必要类型别名以便 XMLMapperBuilder 解析
configuration.getTypeAliasRegistry().registerAlias("TrainEnrollment", TrainEnrollment.class);
configuration.getTypeAliasRegistry().registerAlias("TrainClassPlan", TrainClassPlan.class);
configuration.getTypeAliasRegistry().registerAlias("EnrollmentQuery", EnrollmentQuery.class);
configuration.getTypeAliasRegistry().registerAlias("EnrollmentItemVO", EnrollmentItemVO.class);
try (InputStream inputStream = getClass().getResourceAsStream("/mapper/train/TrainEnrollmentMapper.xml"))
{
assertNotNull("TrainEnrollmentMapper.xml must exist in classpath", inputStream);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(
inputStream,
configuration,
"/mapper/train/TrainEnrollmentMapper.xml",
configuration.getSqlFragments()
);
mapperParser.parse();
}
// 验证 selectEnrollmentItemList 语句成功生成
String statementId = "com.ruoyi.train.mapper.TrainEnrollmentMapper.selectEnrollmentItemList";
assertTrue("Statement must be registered in configuration", configuration.hasStatement(statementId));
MappedStatement ms = configuration.getMappedStatement(statementId);
assertNotNull(ms);
assertEquals(EnrollmentItemVO.class, ms.getResultMaps().get(0).getType());
// 验证 EnrollmentItemVO 具备映射所需住宿属性
assertHasProperty(EnrollmentItemVO.class, "getDormBuilding");
assertHasProperty(EnrollmentItemVO.class, "getDormRoom");
assertHasProperty(EnrollmentItemVO.class, "getBedNo");
assertHasProperty(EnrollmentItemVO.class, "getAccommodationStatus");
}
private String loadMapperXml(String name) throws Exception
{
try (InputStream input = getClass().getResourceAsStream("/mapper/train/" + name))
{
assertNotNull("missing mapper " + name, input);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) != -1)
{
output.write(buffer, 0, length);
}
return new String(output.toByteArray(), StandardCharsets.UTF_8).toLowerCase();
}
}
private void assertContains(String content, String... tokens)
{
for (String token : tokens)
{
assertTrue("Expected content to contain: " + token, content.contains(token.toLowerCase()));
}
}
private void assertOmits(String content, String... tokens)
{
for (String token : tokens)
{
assertFalse("Expected content to omit: " + token, content.contains(token.toLowerCase()));
}
}
private void assertHasProperty(Class<?> clazz, String getterName)
{
try
{
Method method = clazz.getMethod(getterName);
assertNotNull("Getter must exist: " + getterName, method);
}
catch (NoSuchMethodException e)
{
throw new AssertionError("Class " + clazz.getSimpleName() + " missing method: " + getterName, e);
}
}
}
@@ -1130,7 +1130,7 @@ export default {
handleRosterSignIn(row) {
signInAttendance({
lessonId: this.attendanceDialog.lesson.lessonId,
studentId: row.studentId,
enrollmentId: row.enrollmentId,
checkinWay: 4
}).then(() => {
this.$modal.msgSuccess(`学员【${row.studentName}】人工签到成功`)
@@ -1140,8 +1140,7 @@ export default {
handleRosterSignOut(row) {
signOutAttendance({
lessonId: this.attendanceDialog.lesson.lessonId,
studentId: row.studentId,
checkinWay: 4
enrollmentId: row.enrollmentId
}).then(() => {
this.$modal.msgSuccess(`学员【${row.studentName}】签退成功`)
this.refreshAttendanceRoster()