|
|
@@ -0,0 +1,151 @@
|
|
|
+package com.etotem.cfc.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.etotem.cfc.entity.FamilyRelationType;
|
|
|
+import com.etotem.cfc.entity.FamilyRelationship;
|
|
|
+import com.etotem.cfc.mapper.FamilyRelationTypeMapper;
|
|
|
+import com.etotem.cfc.mapper.FamilyRelationshipMapper;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 家庭成员关系类型字典服务(平台运营 SC-A-04 / admin.familyRelation.manage)。
|
|
|
+ *
|
|
|
+ * <p>承载关系类型字典的增删改查:新增重名/重编码拒绝(TC-A-10-E),
|
|
|
+ * 停用前检查 {@code family_relationships} 引用(SC-A-04 边界「被引用时删需确认」),
|
|
|
+ * 名称/编码含注入字符拒绝(TC-A-10-E 注入防护)。</p>
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class FamilyRelationTypeService extends ServiceImpl<FamilyRelationTypeMapper, FamilyRelationType> {
|
|
|
+
|
|
|
+ /** 注入/非法字符:单引号、双引号、分号、反引号、尖括号、双减号 */
|
|
|
+ private static final Pattern UNSAFE_PATTERN = Pattern.compile("['\"`;<>]|--|\\\\");
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FamilyRelationshipMapper familyRelationshipMapper;
|
|
|
+
|
|
|
+ /** 字典列表(含停用,管理页展示全量)。 */
|
|
|
+ public List<FamilyRelationType> listTypes() {
|
|
|
+ LambdaQueryWrapper<FamilyRelationType> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.orderByAsc(FamilyRelationType::getSortOrder)
|
|
|
+ .orderByAsc(FamilyRelationType::getId);
|
|
|
+ return this.list(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 新增关系类型(名称唯一 + 编码唯一 + 注入防护)。 */
|
|
|
+ public FamilyRelationType createType(FamilyRelationType type) {
|
|
|
+ if (type == null) {
|
|
|
+ throw new IllegalArgumentException("请求体不能为空");
|
|
|
+ }
|
|
|
+ if (type.getName() == null || type.getName().trim().isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("关系类型名称不能为空");
|
|
|
+ }
|
|
|
+ if (type.getCode() == null || type.getCode().trim().isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("关系类型编码不能为空");
|
|
|
+ }
|
|
|
+ type.setName(type.getName().trim());
|
|
|
+ type.setCode(type.getCode().trim());
|
|
|
+ if (containsUnsafe(type.getName()) || containsUnsafe(type.getCode())) {
|
|
|
+ throw new IllegalArgumentException("名称/编码包含非法字符");
|
|
|
+ }
|
|
|
+ if (nameExists(type.getName(), null)) {
|
|
|
+ throw new IllegalArgumentException("关系类型名称已存在:" + type.getName());
|
|
|
+ }
|
|
|
+ if (codeExists(type.getCode(), null)) {
|
|
|
+ throw new IllegalArgumentException("关系类型编码已存在:" + type.getCode());
|
|
|
+ }
|
|
|
+ if (type.getStatus() == null || type.getStatus().trim().isEmpty()) {
|
|
|
+ type.setStatus("ACTIVE");
|
|
|
+ }
|
|
|
+ if (type.getSortOrder() == null) {
|
|
|
+ type.setSortOrder(0);
|
|
|
+ }
|
|
|
+ type.setId(null);
|
|
|
+ type.setCreatedAt(new Date());
|
|
|
+ type.setUpdatedAt(new Date());
|
|
|
+ this.save(type);
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 编辑关系类型(重名/重编码校验排除自身)。 */
|
|
|
+ public boolean updateType(FamilyRelationType type) {
|
|
|
+ if (type == null || type.getId() == null) {
|
|
|
+ throw new IllegalArgumentException("关系类型ID不能为空");
|
|
|
+ }
|
|
|
+ FamilyRelationType exist = this.getById(type.getId());
|
|
|
+ if (exist == null) {
|
|
|
+ throw new IllegalArgumentException("关系类型不存在");
|
|
|
+ }
|
|
|
+ if (type.getName() != null && !type.getName().trim().isEmpty()) {
|
|
|
+ type.setName(type.getName().trim());
|
|
|
+ if (containsUnsafe(type.getName())) {
|
|
|
+ throw new IllegalArgumentException("名称包含非法字符");
|
|
|
+ }
|
|
|
+ if (nameExists(type.getName(), type.getId())) {
|
|
|
+ throw new IllegalArgumentException("关系类型名称已存在:" + type.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type.getCode() != null && !type.getCode().trim().isEmpty()) {
|
|
|
+ type.setCode(type.getCode().trim());
|
|
|
+ if (containsUnsafe(type.getCode())) {
|
|
|
+ throw new IllegalArgumentException("编码包含非法字符");
|
|
|
+ }
|
|
|
+ if (codeExists(type.getCode(), type.getId())) {
|
|
|
+ throw new IllegalArgumentException("关系类型编码已存在:" + type.getCode());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ type.setUpdatedAt(new Date());
|
|
|
+ return this.updateById(type);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 停用关系类型(SC-A-04 边界 + TC-A-10-E 步骤2):
|
|
|
+ * 被 {@code family_relationships.relation_type} 引用时提示先解绑,禁止停用。
|
|
|
+ */
|
|
|
+ public boolean disableType(Long id) {
|
|
|
+ if (id == null) {
|
|
|
+ throw new IllegalArgumentException("关系类型ID不能为空");
|
|
|
+ }
|
|
|
+ FamilyRelationType exist = this.getById(id);
|
|
|
+ if (exist == null) {
|
|
|
+ throw new IllegalArgumentException("关系类型不存在");
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<FamilyRelationship> refWrapper = new LambdaQueryWrapper<>();
|
|
|
+ refWrapper.eq(FamilyRelationship::getRelationType, exist.getCode());
|
|
|
+ Long refCount = familyRelationshipMapper.selectCount(refWrapper);
|
|
|
+ if (refCount != null && refCount > 0) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "该关系类型已被 " + refCount + " 条家庭成员关系引用,请先解绑再停用");
|
|
|
+ }
|
|
|
+ exist.setStatus("DISABLED");
|
|
|
+ exist.setUpdatedAt(new Date());
|
|
|
+ return this.updateById(exist);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean nameExists(String name, Long excludeId) {
|
|
|
+ LambdaQueryWrapper<FamilyRelationType> w = new LambdaQueryWrapper<>();
|
|
|
+ w.eq(FamilyRelationType::getName, name);
|
|
|
+ if (excludeId != null) {
|
|
|
+ w.ne(FamilyRelationType::getId, excludeId);
|
|
|
+ }
|
|
|
+ return this.count(w) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean codeExists(String code, Long excludeId) {
|
|
|
+ LambdaQueryWrapper<FamilyRelationType> w = new LambdaQueryWrapper<>();
|
|
|
+ w.eq(FamilyRelationType::getCode, code);
|
|
|
+ if (excludeId != null) {
|
|
|
+ w.ne(FamilyRelationType::getId, excludeId);
|
|
|
+ }
|
|
|
+ return this.count(w) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean containsUnsafe(String s) {
|
|
|
+ return s != null && UNSAFE_PATTERN.matcher(s).find();
|
|
|
+ }
|
|
|
+}
|