@@ -66,6 +66,7 @@ public class DbClassController {
|
||||
row.put("SchSerID", schSerId);
|
||||
row.put("TeaTel", teacher.getTel());
|
||||
row.put("TeaName", teacher.getName());
|
||||
row.put("TeaGender", teacher.getGender());
|
||||
row.put("UserTeaSerID", teacher.getTeaId());
|
||||
row.put("TeaIDCard", teacher.getIdCard());
|
||||
row.put("TeaNo", teacher.getSort());
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.yunhai.bschclassmanage.controller.oapi.sch;
|
||||
|
||||
import com.yunhai.bschclassmanage.config.Config;
|
||||
import com.yunhai.bschclassmanage.model.dto.TeacherBaseInfoDTO;
|
||||
import com.yunhai.bschclassmanage.service.impl.teacher.SchTeacherBaseInfoService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Tag(name = "OapiSchTeacher", description = "开放接口-教师基础信息")
|
||||
@RestController
|
||||
@RequestMapping(Config.OAPI + "/sch/")
|
||||
public class OapiSchTeacherController {
|
||||
@Autowired private SchTeacherBaseInfoService service;
|
||||
|
||||
@GetMapping("getTeacherBaseInfo")
|
||||
@Operation(summary = "获取教师基础信息", description = "姓名、性别、身份证、手机;teaId 为平台用户ID")
|
||||
public ResponseEntity<TeacherBaseInfoDTO> get(@RequestParam Integer schId, @RequestParam Integer teaId) {
|
||||
return ResponseEntity.ok(service.get(schId, teaId));
|
||||
}
|
||||
|
||||
@PostMapping("editTeacherBaseInfo")
|
||||
@Operation(summary = "编辑教师基础信息并同步基础平台", description = "提交四项资料;手机、身份证可清空,性别0女1男")
|
||||
public ResponseEntity<TeacherBaseInfoDTO> edit(@Valid @RequestBody TeacherBaseInfoDTO dto) {
|
||||
return ResponseEntity.ok(service.edit(dto));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.yunhai.bschclassmanage.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
/** 教师基础资料;teaId 为基础平台用户 ID,而非本地教师表主键。 */
|
||||
@Data
|
||||
public class TeacherBaseInfoDTO {
|
||||
@NotNull @Min(1)
|
||||
private Integer schId;
|
||||
@NotNull @Min(1)
|
||||
private Integer teaId;
|
||||
@NotBlank @Size(max = 255)
|
||||
private String name;
|
||||
/** 0=女,1=男;null=未登记 */
|
||||
@Min(0) @Max(1)
|
||||
private Integer gender;
|
||||
@Pattern(regexp = "^$|^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$", message = "身份证号码格式不正确")
|
||||
private String idcard;
|
||||
@Size(max = 255)
|
||||
private String phone;
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.yunhai.bschclassmanage.service.impl.teacher;
|
||||
|
||||
import com.yunhai.bschclassmanage.Repository.SchTeacherGroupRepository;
|
||||
import com.yunhai.bschclassmanage.Repository.SchTeacherRepository;
|
||||
import com.yunhai.bschclassmanage.entity.SchTeacher;
|
||||
import com.yunhai.bschclassmanage.entity.SchTeacherGroup;
|
||||
import com.yunhai.bschclassmanage.model.dto.TeacherBaseInfoDTO;
|
||||
import com.yunhai.bschclassmanage.annotation.SyncOpLog;
|
||||
import com.yunhai.bschclassmanage.enums.SyncDataType;
|
||||
import com.yunhai.platform.openapi.core.YschOpenCall;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** 开放接口只修改四项资料,通过平台签名客户端同步基础平台。 */
|
||||
@Service
|
||||
public class SchTeacherBaseInfoService {
|
||||
@Autowired private SchTeacherRepository teacherRepository;
|
||||
@Autowired private SchTeacherGroupRepository groupRepository;
|
||||
@Autowired private YschOpenCall openCall;
|
||||
@Autowired private SchTeacherCacheService cacheService;
|
||||
|
||||
public TeacherBaseInfoDTO get(Integer schId, Integer teaId) {
|
||||
requireMembership(schId, teaId);
|
||||
return toDto(schId, teachers(teaId).get(0));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@SyncOpLog(SyncDataType.SEND_EDIT_TEACHER)
|
||||
public TeacherBaseInfoDTO edit(TeacherBaseInfoDTO dto) {
|
||||
requireMembership(dto.getSchId(), dto.getTeaId());
|
||||
List<SchTeacher> teachers = teachers(dto.getTeaId());
|
||||
String name = dto.getName() == null ? "" : dto.getName().trim();
|
||||
if (name.isEmpty()) throw new DException("姓名不能为空");
|
||||
// 同名规则与管理端一致,排除教师自己的所有本地记录。
|
||||
List<Integer> ids = groupRepository.findTeaIdsBySchId(dto.getSchId());
|
||||
if (teacherRepository.findByTeaIdIn(ids).stream()
|
||||
.anyMatch(t -> !dto.getTeaId().equals(t.getTeaId()) && name.equals(t.getName()))) {
|
||||
throw new DException("该校下已存在同名教师:" + name);
|
||||
}
|
||||
for (SchTeacher teacher : teachers) {
|
||||
teacher.setName(name);
|
||||
teacher.setGender(dto.getGender());
|
||||
teacher.setIdCard(trim(dto.getIdcard()));
|
||||
teacher.setTel(trim(dto.getPhone()));
|
||||
teacher.setUpdateDate(new Date());
|
||||
teacher.setUpdateUserId(dto.getTeaId());
|
||||
}
|
||||
// 先同步;失败抛异常并回滚本地事务。teaId 可能对应多行,统一修改。
|
||||
Map<String, Object> profile = new LinkedHashMap<>();
|
||||
profile.put("realName", name);
|
||||
profile.put("gender", dto.getGender());
|
||||
profile.put("phone", trim(dto.getPhone()));
|
||||
profile.put("idcard", trim(dto.getIdcard()));
|
||||
BoaUser synced = openCall.post("userbase", "/oapi/user/editBasicInfo/" + dto.getTeaId(),
|
||||
null, profile, BoaUser.class);
|
||||
if (synced == null || !dto.getTeaId().equals(synced.getId())) {
|
||||
throw new DException("基础平台未返回有效教师信息,同步失败");
|
||||
}
|
||||
teacherRepository.saveAll(teachers);
|
||||
Set<Integer> schools = groupRepository.findByTeaIdIn(Collections.singletonList(dto.getTeaId())).stream()
|
||||
.map(SchTeacherGroup::getSchId).collect(Collectors.toSet());
|
||||
schools.add(dto.getSchId());
|
||||
cacheService.clearTeacherCaches(schools);
|
||||
return toDto(dto.getSchId(), teachers.get(0));
|
||||
}
|
||||
|
||||
private List<SchTeacherGroup> requireMembership(Integer schId, Integer teaId) {
|
||||
if (schId == null || schId <= 0 || teaId == null || teaId <= 0) {
|
||||
throw new DException("学校和教师平台用户ID不能为空");
|
||||
}
|
||||
List<SchTeacherGroup> memberships = groupRepository.findByTeaIdAndSchId(teaId, schId).stream()
|
||||
.filter(g -> Integer.valueOf(0).equals(g.getTransferStatus())).collect(Collectors.toList());
|
||||
if (memberships.isEmpty()) throw new DException("教师不属于指定学校或已转出");
|
||||
return memberships;
|
||||
}
|
||||
|
||||
private List<SchTeacher> teachers(Integer teaId) {
|
||||
List<SchTeacher> teachers = teacherRepository.findByTeaId(teaId);
|
||||
if (teachers.isEmpty()) throw new DException("教师不存在");
|
||||
return teachers;
|
||||
}
|
||||
|
||||
private TeacherBaseInfoDTO toDto(Integer schId, SchTeacher teacher) {
|
||||
TeacherBaseInfoDTO dto = new TeacherBaseInfoDTO();
|
||||
dto.setSchId(schId); dto.setTeaId(teacher.getTeaId()); dto.setName(teacher.getName());
|
||||
dto.setGender(teacher.getGender()); dto.setIdcard(teacher.getIdCard()); dto.setPhone(teacher.getTel());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private String trim(String value) { return value == null || value.trim().isEmpty() ? null : value.trim(); }
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.yunhai.bschclassmanage.service.impl.teacher;
|
||||
|
||||
import com.yunhai.bschclassmanage.Repository.*;
|
||||
import com.yunhai.bschclassmanage.entity.*;
|
||||
import com.yunhai.bschclassmanage.model.dto.TeacherBaseInfoDTO;
|
||||
import com.yunhai.platform.openapi.core.YschOpenCall;
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.*;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SchTeacherBaseInfoServiceTest {
|
||||
@InjectMocks private SchTeacherBaseInfoService service;
|
||||
@Mock private SchTeacherRepository teacherRepository;
|
||||
@Mock private SchTeacherGroupRepository groupRepository;
|
||||
@Mock private YschOpenCall openCall;
|
||||
@Mock private SchTeacherCacheService cacheService;
|
||||
|
||||
private SchTeacherGroup group(int school) {
|
||||
SchTeacherGroup g = new SchTeacherGroup();
|
||||
g.setSchId(school); g.setTeaId(101); g.setGroupId(31); g.setTransferStatus(0);
|
||||
return g;
|
||||
}
|
||||
private TeacherBaseInfoDTO request() {
|
||||
TeacherBaseInfoDTO d = new TeacherBaseInfoDTO();
|
||||
d.setSchId(3); d.setTeaId(101); d.setName("新姓名"); d.setGender(1); d.setPhone("");
|
||||
return d;
|
||||
}
|
||||
private List<SchTeacher> givenTeacher() {
|
||||
when(groupRepository.findByTeaIdAndSchId(101, 3)).thenReturn(Collections.singletonList(group(3)));
|
||||
SchTeacher a = new SchTeacher(); a.setId(11); a.setTeaId(101); a.setAccount("teacher"); a.setStatus(1);
|
||||
SchTeacher b = new SchTeacher(); b.setId(12); b.setTeaId(101); b.setAccount("teacher"); b.setStatus(1);
|
||||
List<SchTeacher> rows = Arrays.asList(a,b);
|
||||
when(teacherRepository.findByTeaId(101)).thenReturn(rows);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Test void updatesAllTeacherRowsAndInvalidatesAllSchoolsAfterSync() {
|
||||
List<SchTeacher> rows = givenTeacher();
|
||||
when(openCall.post(eq("userbase"), eq("/oapi/user/editBasicInfo/101"), isNull(), any(), eq(BoaUser.class)))
|
||||
.thenReturn(new BoaUser(101, "teacher", "新姓名"));
|
||||
when(groupRepository.findByTeaIdIn(Collections.singletonList(101))).thenReturn(Arrays.asList(group(3),group(4)));
|
||||
assertEquals("新姓名", service.edit(request()).getName());
|
||||
for (SchTeacher t : rows) {
|
||||
assertEquals("新姓名", t.getName()); assertEquals(1,t.getGender());
|
||||
assertNull(t.getTel()); assertEquals("teacher",t.getAccount()); assertEquals(1,t.getStatus());
|
||||
}
|
||||
InOrder order = inOrder(openCall,teacherRepository,cacheService);
|
||||
order.verify(openCall).post(eq("userbase"),eq("/oapi/user/editBasicInfo/101"),isNull(),any(),eq(BoaUser.class));
|
||||
order.verify(teacherRepository).saveAll(rows);
|
||||
order.verify(cacheService).clearTeacherCaches(new HashSet<>(Arrays.asList(3,4)));
|
||||
}
|
||||
|
||||
@Test void rejectsWrongSchoolAndTransferredOutTeacher() {
|
||||
SchTeacherGroup out = group(3); out.setTransferStatus(1);
|
||||
when(groupRepository.findByTeaIdAndSchId(101,3)).thenReturn(Collections.singletonList(out));
|
||||
assertThrows(DException.class, () -> service.edit(request()));
|
||||
verifyNoInteractions(openCall,teacherRepository,cacheService);
|
||||
}
|
||||
|
||||
@Test void syncFailureDoesNotSaveOrClearCache() {
|
||||
givenTeacher();
|
||||
when(openCall.post(anyString(),anyString(),isNull(),any(),eq(BoaUser.class))).thenThrow(new DException("平台失败"));
|
||||
assertThrows(DException.class, () -> service.edit(request()));
|
||||
verify(teacherRepository,never()).saveAll(any()); verifyNoInteractions(cacheService);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user