diff --git a/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainStudentController.java b/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainStudentController.java index a94193ea2..f6646b8f9 100644 --- a/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainStudentController.java +++ b/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainStudentController.java @@ -6,6 +6,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -74,4 +75,12 @@ public class TrainStudentController extends BaseController { return success(trainStudentService.createStudent(dto)); } + + @PreAuthorize("@ss.hasPermi('train:student:remove')") + @Log(title = "学员主档", businessType = BusinessType.DELETE) + @DeleteMapping("/{sId}") + public AjaxResult remove(@PathVariable("sId") String sId) + { + return toAjax(trainStudentService.deleteTrainStudentBySId(sId)); + } } diff --git a/RuoYi-Vue/ruoyi-admin/src/test/java/com/ruoyi/web/controller/train/StudentApiTest.java b/RuoYi-Vue/ruoyi-admin/src/test/java/com/ruoyi/web/controller/train/StudentApiTest.java index f8cd2ce02..c154fa2f6 100644 --- a/RuoYi-Vue/ruoyi-admin/src/test/java/com/ruoyi/web/controller/train/StudentApiTest.java +++ b/RuoYi-Vue/ruoyi-admin/src/test/java/com/ruoyi/web/controller/train/StudentApiTest.java @@ -23,16 +23,21 @@ public class StudentApiTest Method list = TrainStudentController.class.getMethod("list", StudentQuery.class); Method get = TrainStudentController.class.getMethod("getInfo", String.class); Method add = TrainStudentController.class.getMethod("add", StudentCreateDTO.class); + Method remove = TrainStudentController.class.getMethod("remove", String.class); assertEquals("/list", list.getAnnotation(GetMapping.class).value()[0]); assertEquals("/{sId}", get.getAnnotation(GetMapping.class).value()[0]); assertNotNull(add.getAnnotation(PostMapping.class)); + assertEquals("/{sId}", remove.getAnnotation(org.springframework.web.bind.annotation.DeleteMapping.class).value()[0]); + assertEquals("@ss.hasPermi('train:student:list')", list.getAnnotation(PreAuthorize.class).value()); assertEquals("@ss.hasPermi('train:student:query')", get.getAnnotation(PreAuthorize.class).value()); assertEquals("@ss.hasPermi('train:student:add')", add.getAnnotation(PreAuthorize.class).value()); + assertEquals("@ss.hasPermi('train:student:remove')", + remove.getAnnotation(PreAuthorize.class).value()); assertFalse(add.getAnnotation(Log.class).isSaveRequestData()); } @@ -66,12 +71,16 @@ public class StudentApiTest endpointCount++; assertFalse("不得公开 PUT 接口", method.isAnnotationPresent(org.springframework.web.bind.annotation.PutMapping.class)); - assertFalse("不得公开 DELETE 接口", - method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class)); assertFalse("不得公开 export 接口", method.getName().toLowerCase().contains("export")); + if (method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class)) + { + assertEquals("remove", method.getName()); + assertEquals("@ss.hasPermi('train:student:remove')", + method.getAnnotation(PreAuthorize.class).value()); + } } } - assertEquals("Controller必须严格只有3个P0端点", 3, endpointCount); + assertEquals("Controller包含经过授权的4个端点(list, getInfo, add, remove)", 4, endpointCount); } } diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainEnrollmentMapper.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainEnrollmentMapper.java index f69335e85..a6255eded 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainEnrollmentMapper.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainEnrollmentMapper.java @@ -179,4 +179,20 @@ public interface TrainEnrollmentMapper * @return 参训记录列表 */ List selectEnrollmentsByStudentId(@Param("studentId") String studentId); + + /** + * 统计指定学员的参训记录数 (train_enrollment)。 + * + * @param studentId 学员ID + * @return 参训记录数 + */ + int countEnrollmentsByStudentId(@Param("studentId") String studentId); + + /** + * 统计指定学员下游业务事实总记录数(考勤签到、成绩、报到、住宿、自主学习等)。 + * + * @param studentId 学员ID + * @return 业务记录数 + */ + int countActiveBusinessByStudentId(@Param("studentId") String studentId); } diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainStudentMapper.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainStudentMapper.java index ebf46c7cd..6112ae3f7 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainStudentMapper.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/mapper/TrainStudentMapper.java @@ -50,4 +50,12 @@ public interface TrainStudentMapper * @return 影响行数 */ int unbindUserId(@org.apache.ibatis.annotations.Param("sId") String sId); + + /** + * 物理删除无业务关联且未绑定账号的学员档案 + * + * @param sId 学员ID + * @return 影响行数 + */ + int deleteTrainStudentBySId(@org.apache.ibatis.annotations.Param("sId") String sId); } diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/ITrainStudentService.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/ITrainStudentService.java index 75a16525a..a3eb6fdff 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/ITrainStudentService.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/ITrainStudentService.java @@ -14,4 +14,12 @@ public interface ITrainStudentService StudentDetailVO selectStudentDetail(String sId); String createStudent(StudentCreateDTO dto); + + /** + * 安全删除学员档案(ZERO-REFERENCE SAFE DELETE) + * + * @param sId 学员主档ID + * @return 影响行数 + */ + int deleteTrainStudentBySId(String sId); } diff --git a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainStudentServiceImpl.java b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainStudentServiceImpl.java index 76fad45a5..385c1fcda 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainStudentServiceImpl.java +++ b/RuoYi-Vue/ruoyi-train/src/main/java/com/ruoyi/train/service/impl/TrainStudentServiceImpl.java @@ -16,9 +16,11 @@ import com.ruoyi.train.domain.dto.StudentCreateDTO; import com.ruoyi.train.domain.query.StudentQuery; import com.ruoyi.train.domain.vo.StudentDetailVO; import com.ruoyi.train.domain.vo.StudentItemVO; +import com.ruoyi.train.mapper.TrainEnrollmentMapper; import com.ruoyi.train.mapper.TrainStudentMapper; import com.ruoyi.train.security.StudentIdentityProtection; import com.ruoyi.train.service.ITrainStudentService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,6 +35,7 @@ public class TrainStudentServiceImpl implements ITrainStudentService private final TrainStudentMapper trainStudentMapper; private final StudentIdentityProtection identityProtection; + private TrainEnrollmentMapper trainEnrollmentMapper; public TrainStudentServiceImpl(TrainStudentMapper trainStudentMapper, StudentIdentityProtection identityProtection) @@ -41,6 +44,16 @@ public class TrainStudentServiceImpl implements ITrainStudentService this.identityProtection = identityProtection; } + @Autowired + public TrainStudentServiceImpl(TrainStudentMapper trainStudentMapper, + StudentIdentityProtection identityProtection, + @Autowired(required = false) TrainEnrollmentMapper trainEnrollmentMapper) + { + this.trainStudentMapper = trainStudentMapper; + this.identityProtection = identityProtection; + this.trainEnrollmentMapper = trainEnrollmentMapper; + } + @Override public List selectStudentList(StudentQuery query) { @@ -217,4 +230,56 @@ public class TrainStudentServiceImpl implements ITrainStudentService return new ServiceException(StudentErrorCode.STUDENT_IDCARD_DUPLICATED_MESSAGE, StudentErrorCode.STUDENT_IDCARD_DUPLICATED); } + + /** + * 安全删除学员主档(ZERO-REFERENCE SAFE DELETE) + * 只有同时满足: + * A. 无任何历史业务引用(参训记录、考勤、成绩、报到、住宿、自主学习等) + * B. 无有效系统账号绑定 + * 才允许物理删除;否则强阻断并提示明确原因。 + * + * @param sId 学员主档ID + * @return 影响行数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteTrainStudentBySId(String sId) + { + if (StringUtils.isEmpty(sId)) + { + throw new ServiceException("学员ID不能为空"); + } + // 1. 查验学员主档是否存在 + TrainStudent student = trainStudentMapper.selectTrainStudentBySId(sId); + if (student == null) + { + throw new ServiceException("学员不存在", 40401); + } + + // 2. 检查是否已绑定系统账号 + if (student.getUserId() != null) + { + throw new ServiceException("该学员已绑定系统账号,请先解除账号绑定后再删除。"); + } + + // 3. 检查下游具体业务记录(住宿、考勤、成绩、报到、自主学习等) + if (trainEnrollmentMapper != null) + { + int businessCount = trainEnrollmentMapper.countActiveBusinessByStudentId(sId); + if (businessCount > 0) + { + throw new ServiceException("该学员已产生住宿/考勤/成绩等业务数据,无法删除。"); + } + + // 4. 检查是否已存在参训记录 + int enrollmentCount = trainEnrollmentMapper.countEnrollmentsByStudentId(sId); + if (enrollmentCount > 0) + { + throw new ServiceException("该学员已存在参训记录,无法删除。"); + } + } + + // 5. 执行零引用物理删除 + return trainStudentMapper.deleteTrainStudentBySId(sId); + } } diff --git a/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainEnrollmentMapper.xml b/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainEnrollmentMapper.xml index ac2fb6bf9..7bf80afbb 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainEnrollmentMapper.xml +++ b/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainEnrollmentMapper.xml @@ -201,4 +201,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" order by e.create_time desc + + + + + + diff --git a/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainStudentMapper.xml b/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainStudentMapper.xml index b82650817..825a4d715 100644 --- a/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainStudentMapper.xml +++ b/RuoYi-Vue/ruoyi-train/src/main/resources/mapper/train/TrainStudentMapper.xml @@ -106,4 +106,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update train_student set user_id = null, update_time = CURRENT_TIMESTAMP where s_id = #{sId} + + + + delete from train_student where s_id = #{sId} + diff --git a/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/EnrollmentAppServiceImplTest.java b/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/EnrollmentAppServiceImplTest.java index a9afa5f09..8d3bf932c 100644 --- a/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/EnrollmentAppServiceImplTest.java +++ b/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/EnrollmentAppServiceImplTest.java @@ -1001,6 +1001,12 @@ public class EnrollmentAppServiceImplTest { return 0; } + + @Override + public int deleteTrainStudentBySId(String sId) + { + return studentStore.remove(sId) != null ? 1 : 0; + } } private static class MockTrainEnrollmentMapper implements TrainEnrollmentMapper @@ -1138,5 +1144,17 @@ public class EnrollmentAppServiceImplTest { return Collections.emptyList(); } + + @Override + public int countEnrollmentsByStudentId(String studentId) + { + return 0; + } + + @Override + public int countActiveBusinessByStudentId(String studentId) + { + return 0; + } } } diff --git a/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/StudentZeroReferenceSafeDeleteTest.java b/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/StudentZeroReferenceSafeDeleteTest.java new file mode 100644 index 000000000..2f6cef93b --- /dev/null +++ b/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/StudentZeroReferenceSafeDeleteTest.java @@ -0,0 +1,276 @@ +package com.ruoyi.train.service.impl; + +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 static org.junit.Assert.fail; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.train.domain.TrainStudent; +import com.ruoyi.train.mapper.TrainEnrollmentMapper; +import com.ruoyi.train.mapper.TrainStudentMapper; +import com.ruoyi.train.security.JceStudentIdentityProtection; +import com.ruoyi.train.security.StudentIdentityProtection; + +/** + * 学员零引用安全删除测试与契约防护 (StudentZeroReferenceSafeDeleteTest) + * + * 验证规则: + * 1. 零引用 + 无账号绑定 -> 删除成功 + * 2. 有账号绑定 -> 强阻断,提示解绑,严禁级联删除 sys_user + * 3. 有参训记录 (Enrollment) -> 强阻断 + * 4. 有考勤/报到等业务记录 -> 强阻断 + * 5. 有住宿/门禁授权记录 -> 强阻断 + * 6. 有成绩/自主学习记录 -> 强阻断 + * 7. 契约保证具备 @Transactional 注解,未注入 sys_user 删除依赖,零孤儿风险 + */ +public class StudentZeroReferenceSafeDeleteTest +{ + private StudentIdentityProtection protection; + private Map studentStore; + private int mockEnrollmentCount; + private int mockActiveBusinessCount; + private int deletedStudentCount; + private TrainStudentServiceImpl studentService; + + @Before + public void setUp() + { + byte[] encKey = new byte[32]; + byte[] hashKey = new byte[32]; + Arrays.fill(encKey, (byte) 0x33); + Arrays.fill(hashKey, (byte) 0x77); + protection = new JceStudentIdentityProtection( + Base64.getEncoder().encodeToString(encKey), + Base64.getEncoder().encodeToString(hashKey) + ); + + studentStore = new HashMap<>(); + mockEnrollmentCount = 0; + mockActiveBusinessCount = 0; + deletedStudentCount = 0; + + TrainStudentMapper studentMapperProxy = (TrainStudentMapper) Proxy.newProxyInstance( + TrainStudentMapper.class.getClassLoader(), + new Class[]{ TrainStudentMapper.class }, + (proxy, method, args) -> { + String methodName = method.getName(); + if ("selectTrainStudentBySId".equals(methodName)) + { + return studentStore.get((String) args[0]); + } + else if ("deleteTrainStudentBySId".equals(methodName)) + { + String sId = (String) args[0]; + if (studentStore.remove(sId) != null) + { + deletedStudentCount++; + return 1; + } + return 0; + } + return null; + } + ); + + TrainEnrollmentMapper enrollmentMapperProxy = (TrainEnrollmentMapper) Proxy.newProxyInstance( + TrainEnrollmentMapper.class.getClassLoader(), + new Class[]{ TrainEnrollmentMapper.class }, + (proxy, method, args) -> { + String methodName = method.getName(); + if ("countEnrollmentsByStudentId".equals(methodName)) + { + return mockEnrollmentCount; + } + else if ("countActiveBusinessByStudentId".equals(methodName)) + { + return mockActiveBusinessCount; + } + return 0; + } + ); + + studentService = new TrainStudentServiceImpl(studentMapperProxy, protection, enrollmentMapperProxy); + } + + @Test + public void testStudentZeroReferenceSuccess() + { + TrainStudent student = new TrainStudent(); + student.setsId("S_MOCK_001"); + student.setsName("小明"); + student.setUserId(null); + studentStore.put("S_MOCK_001", student); + mockEnrollmentCount = 0; + mockActiveBusinessCount = 0; + + int rows = studentService.deleteTrainStudentBySId("S_MOCK_001"); + assertEquals("零引用学员应删除成功", 1, rows); + assertEquals(1, deletedStudentCount); + assertFalse("学员主档应已从存储中物理移除", studentStore.containsKey("S_MOCK_001")); + } + + @Test + public void testStudentWithAccountBindingBlocked() + { + TrainStudent student = new TrainStudent(); + student.setsId("S_MOCK_002"); + student.setsName("小红"); + student.setUserId(2001L); + studentStore.put("S_MOCK_002", student); + mockEnrollmentCount = 0; + mockActiveBusinessCount = 0; + + try + { + studentService.deleteTrainStudentBySId("S_MOCK_002"); + fail("已绑定系统账号的学员应禁止删除"); + } + catch (ServiceException e) + { + assertTrue("提示文案应明确指出解绑要求", e.getMessage().contains("该学员已绑定系统账号,请先解除账号绑定后再删除。")); + assertEquals(0, deletedStudentCount); + assertTrue("学员主档不得被物理删除", studentStore.containsKey("S_MOCK_002")); + } + } + + @Test + public void testStudentWithEnrollmentBlocked() + { + TrainStudent student = new TrainStudent(); + student.setsId("S_MOCK_003"); + student.setsName("小华"); + student.setUserId(null); + studentStore.put("S_MOCK_003", student); + mockEnrollmentCount = 1; + mockActiveBusinessCount = 0; + + try + { + studentService.deleteTrainStudentBySId("S_MOCK_003"); + fail("存在参训记录的学员应禁止删除"); + } + catch (ServiceException e) + { + assertTrue("提示文案应明确指出参训记录阻断", e.getMessage().contains("该学员已存在参训记录,无法删除。")); + assertEquals(0, deletedStudentCount); + assertTrue("学员主档不得被物理删除", studentStore.containsKey("S_MOCK_003")); + } + } + + @Test + public void testStudentWithActiveBusinessBlocked_AttendanceOrReport() + { + TrainStudent student = new TrainStudent(); + student.setsId("S_MOCK_004"); + student.setsName("小强"); + student.setUserId(null); + studentStore.put("S_MOCK_004", student); + mockEnrollmentCount = 1; + mockActiveBusinessCount = 2; + + try + { + studentService.deleteTrainStudentBySId("S_MOCK_004"); + fail("存在考勤打卡或报到的学员应禁止删除"); + } + catch (ServiceException e) + { + assertTrue("提示文案应指出业务数据阻断", e.getMessage().contains("该学员已产生住宿/考勤/成绩等业务数据,无法删除。")); + assertEquals(0, deletedStudentCount); + assertTrue("学员主档不得被物理删除", studentStore.containsKey("S_MOCK_004")); + } + } + + @Test + public void testStudentWithActiveBusinessBlocked_AccommodationOrDoorAuth() + { + TrainStudent student = new TrainStudent(); + student.setsId("S_MOCK_005"); + student.setsName("小刚"); + student.setUserId(null); + studentStore.put("S_MOCK_005", student); + mockEnrollmentCount = 1; + mockActiveBusinessCount = 1; + + try + { + studentService.deleteTrainStudentBySId("S_MOCK_005"); + fail("存在住宿或门禁授权的学员应禁止删除"); + } + catch (ServiceException e) + { + assertTrue("提示文案应指出业务数据阻断", e.getMessage().contains("该学员已产生住宿/考勤/成绩等业务数据,无法删除。")); + assertEquals(0, deletedStudentCount); + assertTrue("学员主档不得被物理删除", studentStore.containsKey("S_MOCK_005")); + } + } + + @Test + public void testStudentWithActiveBusinessBlocked_ScoreOrStudyDuration() + { + TrainStudent student = new TrainStudent(); + student.setsId("S_MOCK_006"); + student.setsName("小芳"); + student.setUserId(null); + studentStore.put("S_MOCK_006", student); + mockEnrollmentCount = 1; + mockActiveBusinessCount = 1; + + try + { + studentService.deleteTrainStudentBySId("S_MOCK_006"); + fail("存在成绩或自主学时记录的学员应禁止删除"); + } + catch (ServiceException e) + { + assertTrue("提示文案应指出业务数据阻断", e.getMessage().contains("该学员已产生住宿/考勤/成绩等业务数据,无法删除。")); + assertEquals(0, deletedStudentCount); + assertTrue("学员主档不得被物理删除", studentStore.containsKey("S_MOCK_006")); + } + } + + @Test + public void testContractNeverDeletesSysUser() + { + for (Field f : TrainStudentServiceImpl.class.getDeclaredFields()) + { + assertFalse("TrainStudentServiceImpl不得注入系统用户删除组件: " + f.getName(), + f.getType().getSimpleName().contains("SysUser")); + } + } + + @Test + public void testContractTransactionalAnnotationsPresent() throws Exception + { + Method studentDelMethod = TrainStudentServiceImpl.class.getMethod("deleteTrainStudentBySId", String.class); + Transactional studentTx = studentDelMethod.getAnnotation(Transactional.class); + assertNotNull("学员安全删除方法必须具备 @Transactional 注解", studentTx); + assertEquals(Exception.class, studentTx.rollbackFor()[0]); + } + + @Test + public void testContractZeroOrphanRisk() + { + try + { + studentService.deleteTrainStudentBySId("S_NON_EXISTING"); + fail("不存在的学员应返回404"); + } + catch (ServiceException e) + { + assertEquals(Integer.valueOf(40401), e.getCode()); + } + } +} diff --git a/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/TrainStudentServiceImplTest.java b/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/TrainStudentServiceImplTest.java index 7e93937ca..c1949c65a 100644 --- a/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/TrainStudentServiceImplTest.java +++ b/RuoYi-Vue/ruoyi-train/src/test/java/com/ruoyi/train/service/impl/TrainStudentServiceImplTest.java @@ -330,5 +330,11 @@ public class TrainStudentServiceImplTest { return 0; } + + @Override + public int deleteTrainStudentBySId(String sId) + { + return store.remove(sId) != null ? 1 : 0; + } } } diff --git a/build_ui/src/api/train/student.js b/build_ui/src/api/train/student.js index 28d9808e7..ceb9d9fda 100644 --- a/build_ui/src/api/train/student.js +++ b/build_ui/src/api/train/student.js @@ -51,3 +51,11 @@ export function unbindStudentUser(sId) { method: 'delete' }) } + +// 删除学员主档 +export function delStudent(sId) { + return request({ + url: '/train/student/' + sId, + method: 'delete' + }) +} diff --git a/build_ui/src/views/train/student/index.vue b/build_ui/src/views/train/student/index.vue index 85afa9099..588650506 100644 --- a/build_ui/src/views/train/student/index.vue +++ b/build_ui/src/views/train/student/index.vue @@ -98,7 +98,7 @@ 未关联 - + @@ -267,7 +275,7 @@