package jnpf.elnService.impl;
|
|
import cn.dev33.satoken.stp.StpUtil;
|
import cn.hutool.core.util.IdUtil;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import jnpf.audit.model.AuditEventDTO;
|
import jnpf.audit.sdk.AuditClient;
|
import jnpf.base.UserInfo;
|
import jnpf.database.util.TenantDataSourceUtil;
|
import jnpf.elnEntity.field.ElnFieldDefEntity;
|
import jnpf.elnEntity.field.ElnFieldEnums;
|
import jnpf.elnEntity.field.ElnFieldForms;
|
import jnpf.elnEntity.field.ElnFieldVersionEntity;
|
import jnpf.elnEntity.field.ElnFieldVos;
|
import jnpf.elnMapper.ElnFieldDefMapper;
|
import jnpf.elnMapper.ElnFieldVersionMapper;
|
import jnpf.elnService.ElnFieldCatalogService;
|
import jnpf.elnService.ElnFieldProviderCatalog;
|
import jnpf.elnService.field.ElnTestResultFieldCatalog;
|
import jnpf.elnService.field.ElnTestResultFields;
|
import jnpf.exception.DataException;
|
import jnpf.model.tenant.TenantVO;
|
import jnpf.util.JsonUtil;
|
import jnpf.util.UserProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.Comparator;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.function.BooleanSupplier;
|
import java.util.function.Consumer;
|
import java.util.function.Supplier;
|
import java.util.regex.Pattern;
|
|
@Service
|
@Transactional(rollbackFor = Exception.class)
|
public class ElnFieldCatalogServiceImpl implements ElnFieldCatalogService {
|
|
static final String MENU_PERMISSION = "dianziJiluGonggongZiduanGuanli";
|
private static final String ENABLED = "enabled";
|
private static final String DISABLED = "disabled";
|
private static final int CODE_RETRY_LIMIT = 3;
|
private static final Pattern CHECKBOX_GROUP_CODE = Pattern.compile("^[A-Za-z0-9_-]{1,80}$");
|
|
private final FieldCatalogStore store;
|
private final ElnFieldProviderCatalog providerCatalog;
|
private final Supplier<UserContext> userContextSupplier;
|
private final Supplier<String> idSupplier;
|
private final ElnFieldCodeGenerator codeGenerator;
|
private final Consumer<AuditEventDTO> auditRecorder;
|
private final BooleanSupplier maintainPermission;
|
private final BooleanSupplier promotePermission;
|
private final ElnTestResultFieldCatalog testResultFieldCatalog;
|
|
@Autowired
|
public ElnFieldCatalogServiceImpl(ElnFieldDefMapper definitionMapper,
|
ElnFieldVersionMapper versionMapper,
|
ElnFieldProviderCatalog providerCatalog,
|
ObjectProvider<AuditClient> auditClientProvider,
|
ObjectProvider<ElnTestResultFieldCatalog> testResultFieldCatalogProvider) {
|
this(new MyBatisFieldCatalogStore(definitionMapper, versionMapper),
|
providerCatalog,
|
ElnFieldCatalogServiceImpl::currentUserContext,
|
IdUtil::getSnowflakeNextIdStr,
|
new ElnFieldCodeGenerator(() -> IdUtil.fastSimpleUUID().substring(0, 6)),
|
event -> {
|
AuditClient client = auditClientProvider.getIfAvailable();
|
if (client != null) {
|
client.record(event);
|
}
|
},
|
ElnFieldCatalogServiceImpl::hasMaintainPermission,
|
ElnFieldCatalogServiceImpl::hasPromotePermission,
|
testResultFieldCatalogProvider.getIfAvailable());
|
}
|
|
ElnFieldCatalogServiceImpl(FieldCatalogStore store,
|
ElnFieldProviderCatalog providerCatalog,
|
Supplier<UserContext> userContextSupplier,
|
Supplier<String> idSupplier,
|
ElnFieldCodeGenerator codeGenerator,
|
Consumer<AuditEventDTO> auditRecorder,
|
BooleanSupplier maintainPermission,
|
BooleanSupplier promotePermission) {
|
this(store, providerCatalog, userContextSupplier, idSupplier, codeGenerator, auditRecorder,
|
maintainPermission, promotePermission, null);
|
}
|
|
ElnFieldCatalogServiceImpl(FieldCatalogStore store,
|
ElnFieldProviderCatalog providerCatalog,
|
Supplier<UserContext> userContextSupplier,
|
Supplier<String> idSupplier,
|
ElnFieldCodeGenerator codeGenerator,
|
Consumer<AuditEventDTO> auditRecorder,
|
BooleanSupplier maintainPermission,
|
BooleanSupplier promotePermission,
|
ElnTestResultFieldCatalog testResultFieldCatalog) {
|
this.store = store;
|
this.providerCatalog = providerCatalog;
|
this.userContextSupplier = userContextSupplier;
|
this.idSupplier = idSupplier;
|
this.codeGenerator = codeGenerator;
|
this.auditRecorder = auditRecorder;
|
this.maintainPermission = maintainPermission;
|
this.promotePermission = promotePermission;
|
this.testResultFieldCatalog = testResultFieldCatalog;
|
}
|
|
@Override
|
public void ensureTestSubmissionFields() {
|
if (testResultFieldCatalog == null) return;
|
UserContext context = userContextSupplier.get();
|
if (context == null) throw new DataException("请先登录后再维护字段目录");
|
testResultFieldCatalog.ensure(context.getTenantId(), context.getUserId());
|
}
|
|
@Override
|
public ElnFieldVos.Detail create(ElnFieldForms.Create form) {
|
UserContext context = requireCatalogAccess();
|
if (form == null) {
|
throw new DataException("字段信息不能为空");
|
}
|
if (StringUtils.hasText(form.getZuoyongFanwei())
|
&& !ElnFieldEnums.Scope.GLOBAL.name().equals(form.getZuoyongFanwei().trim())) {
|
throw new DataException("字段维护页只能创建全局字段");
|
}
|
validateDraftBasics(form);
|
return createIdentity(context.getTenantId(), context.getUserId(), false, null,
|
form.getBeizhu(), form, false);
|
}
|
|
@Override
|
public ElnFieldVos.Detail createTemplateFieldAndPreRelease(
|
ElnFieldForms.TemplateFieldCreate command) {
|
if (command == null || !StringUtils.hasText(command.getTenantId())
|
|| !StringUtils.hasText(command.getTemplateCode())
|
|| !StringUtils.hasText(command.getOperatorId())) {
|
throw new DataException("私有字段命令缺少租户、模板或操作者");
|
}
|
UserContext routed = userContextSupplier.get();
|
if (routed == null || !command.getTenantId().trim().equals(routed.getTenantId())) {
|
throw new DataException("私有字段命令租户与当前数据源租户不一致");
|
}
|
if (!StringUtils.hasText(command.getZiduanMingcheng())
|
|| !StringUtils.hasText(command.getShujuLeixing())) {
|
throw new DataException("私有字段名称和类型不能为空");
|
}
|
if (StringUtils.hasText(command.getRecommendedCheckboxGroupCode())) {
|
throw new DataException("模板私有字段的选择组应在模板标注时配置");
|
}
|
command.setShifouBitian(command.getShifouBitian() == null ? 0 : command.getShifouBitian());
|
command.setPiciMoshi(ElnFieldEnums.BatchMode.RECORD_PRIVATE.name());
|
command.setBinding(defaultManualBinding());
|
validateDraftBasics(command);
|
String tenantId = command.getTenantId().trim();
|
String templateCode = command.getTemplateCode().trim();
|
if (store.hasEnabledTemplateFieldName(tenantId, templateCode,
|
command.getZiduanMingcheng().trim())) {
|
throw new DataException("当前模板已存在同名字段,请直接选择已有字段");
|
}
|
return createIdentity(tenantId, command.getOperatorId().trim(), true,
|
templateCode, command.getBeizhu(), command, true);
|
}
|
|
@Override
|
@Transactional(readOnly = true)
|
public ElnFieldVos.Detail detail(String id) {
|
UserContext context = requireCatalogAccess();
|
ElnFieldDefEntity definition = requireDefinition(context.getTenantId(), id, false);
|
return toDetail(definition, store.listVersions(context.getTenantId(), definition.getId()));
|
}
|
|
@Override
|
@Transactional(readOnly = true)
|
public IPage<ElnFieldVos.ListItem> page(ElnFieldForms.Query query) {
|
UserContext context = requireCatalogAccess();
|
ElnFieldForms.Query actual = query == null ? new ElnFieldForms.Query() : query;
|
if (actual.getCurrentPage() < 1 || actual.getPageSize() < 1) {
|
throw new DataException("分页参数必须大于 0");
|
}
|
IPage<ElnFieldDefEntity> source = store.page(context.getTenantId(), actual);
|
List<ElnFieldVos.ListItem> records = new ArrayList<>();
|
for (ElnFieldDefEntity definition : source.getRecords()) {
|
records.add(toListItem(definition,
|
store.listVersions(context.getTenantId(), definition.getId())));
|
}
|
return new Page<ElnFieldVos.ListItem>(source.getCurrent(), source.getSize(), source.getTotal())
|
.setRecords(records);
|
}
|
|
@Override
|
public ElnFieldVos.Version createVersion(String fieldId, ElnFieldForms.VersionCreate form) {
|
UserContext context = requireCatalogAccess();
|
ElnFieldDefEntity definition = requireDefinition(context.getTenantId(), fieldId, true);
|
requireMutable(definition);
|
requireEnabled(definition);
|
if (form == null || !StringUtils.hasText(form.getSourceVersionId())) {
|
throw new DataException("复制版本必须指定来源版本");
|
}
|
ElnFieldVersionEntity source = requireVersion(context.getTenantId(), form.getSourceVersionId());
|
requireSameDefinition(definition, source);
|
if (!ElnFieldEnums.VersionStatus.PRE_RELEASE.name().equals(source.getBizStatus())
|
&& !ElnFieldEnums.VersionStatus.PUBLISHED.name().equals(source.getBizStatus())) {
|
throw new DataException("只能基于预发布或已发布版本创建新版本");
|
}
|
int nextVersion = nextVersionNumber(context.getTenantId(), definition.getId());
|
ElnFieldVersionEntity created = copyVersion(source);
|
created.setId(idSupplier.get());
|
created.setBanbenHao(nextVersion);
|
created.setBizStatus(ElnFieldEnums.VersionStatus.DRAFT.name());
|
created.setZuofeiYuanyin(null);
|
created.setFCreatorUserId(context.getUserId());
|
created.setFLastModifyUserId(context.getUserId());
|
created.setFVersion(0);
|
store.insertVersion(created);
|
audit("ELN_FIELD_VERSION_CREATE", "eln_field_version", created.getId(),
|
context, null, created, null);
|
return toVersion(created);
|
}
|
|
@Override
|
public ElnFieldVos.Version updateDraft(String versionId, ElnFieldForms.VersionUpdate form) {
|
UserContext context = requireCatalogAccess();
|
ElnFieldVersionEntity version = requireVersion(context.getTenantId(), versionId);
|
if (!ElnFieldEnums.VersionStatus.DRAFT.name().equals(version.getBizStatus())) {
|
throw new DataException("只有草稿版本可以修改");
|
}
|
ElnFieldDefEntity definition = requireDefinition(
|
context.getTenantId(), version.getZiduanDingyiId(), true);
|
requireMutable(definition);
|
requireEnabled(definition);
|
validateDraftBasics(form);
|
ElnFieldVersionEntity before = copyVersion(version);
|
applyRules(version, form);
|
version.setFLastModifyUserId(context.getUserId());
|
store.updateVersion(version);
|
audit("ELN_FIELD_VERSION_UPDATE", "eln_field_version", version.getId(),
|
context, before, version, null);
|
return toVersion(version);
|
}
|
|
@Override
|
public void preRelease(String versionId) {
|
UserContext context = requireCatalogAccess();
|
ElnFieldVersionEntity version = requireVersion(context.getTenantId(), versionId);
|
if (!ElnFieldEnums.VersionStatus.DRAFT.name().equals(version.getBizStatus())) {
|
throw new DataException("只有草稿版本可以提交预发布");
|
}
|
ElnFieldDefEntity definition = requireDefinition(
|
context.getTenantId(), version.getZiduanDingyiId(), true);
|
requireMutable(definition);
|
requireEnabled(definition);
|
validateForReference(definition, version);
|
changeVersionStatus(version, ElnFieldEnums.VersionStatus.PRE_RELEASE, null, context,
|
"ELN_FIELD_VERSION_PRE_RELEASE");
|
}
|
|
@Override
|
public void publish(String versionId) {
|
UserContext context = requireCatalogAccess();
|
ElnFieldVersionEntity version = requireVersion(context.getTenantId(), versionId);
|
if (!ElnFieldEnums.VersionStatus.PRE_RELEASE.name().equals(version.getBizStatus())) {
|
throw new DataException("只有预发布版本可以发布");
|
}
|
ElnFieldDefEntity definition = requireDefinition(
|
context.getTenantId(), version.getZiduanDingyiId(), true);
|
requireMutable(definition);
|
requireEnabled(definition);
|
validateForReference(definition, version);
|
changeVersionStatus(version, ElnFieldEnums.VersionStatus.PUBLISHED, null, context,
|
"ELN_FIELD_VERSION_PUBLISH");
|
}
|
|
@Override
|
public void cancel(String versionId, String reason) {
|
UserContext context = requireCatalogAccess();
|
String actualReason = requireReason(reason);
|
ElnFieldVersionEntity version = requireVersion(context.getTenantId(), versionId);
|
if (!ElnFieldEnums.VersionStatus.DRAFT.name().equals(version.getBizStatus())
|
&& !ElnFieldEnums.VersionStatus.PRE_RELEASE.name().equals(version.getBizStatus())) {
|
throw new DataException("只有草稿或预发布版本可以作废");
|
}
|
changeVersionStatus(version, ElnFieldEnums.VersionStatus.CANCELLED, actualReason, context,
|
"ELN_FIELD_VERSION_CANCEL");
|
}
|
|
@Override
|
public void switchCurrent(String fieldId, String versionId, String reason) {
|
UserContext context = requireCatalogAccess();
|
String actualReason = requireReason(reason);
|
ElnFieldDefEntity definition = requireDefinition(context.getTenantId(), fieldId, true);
|
requireMutable(definition);
|
requireEnabled(definition);
|
ElnFieldVersionEntity version = requireVersion(context.getTenantId(), versionId);
|
requireSameDefinition(definition, version);
|
if (!ElnFieldEnums.VersionStatus.PUBLISHED.name().equals(version.getBizStatus())) {
|
throw new DataException("当前版本只能切换到已发布版本");
|
}
|
validateForReference(definition, version);
|
ElnFieldDefEntity before = copyDefinition(definition);
|
definition.setDangqianZiduanBanbenId(version.getId());
|
definition.setFLastModifyUserId(context.getUserId());
|
store.updateDefinition(definition);
|
audit("ELN_FIELD_SWITCH_CURRENT", "eln_field_def", definition.getId(),
|
context, before, definition, actualReason);
|
}
|
|
@Override
|
public void promoteToGlobal(String fieldId, String reason) {
|
UserContext context = requireCatalogAccess();
|
if (!promotePermission.getAsBoolean()) {
|
throw new DataException("没有全局字段维护权限");
|
}
|
String actualReason = requireReason(reason);
|
ElnFieldDefEntity definition = requireDefinition(context.getTenantId(), fieldId, true);
|
if (!ElnFieldEnums.Scope.TEMPLATE.name().equals(definition.getZuoyongFanwei())) {
|
throw new DataException("只有模板私有字段可以升格为全局字段");
|
}
|
ElnFieldDefEntity before = copyDefinition(definition);
|
definition.setZuoyongFanwei(ElnFieldEnums.Scope.GLOBAL.name());
|
definition.setMubanBianhao(null);
|
definition.setFLastModifyUserId(context.getUserId());
|
store.updateDefinition(definition);
|
audit("ELN_FIELD_PROMOTE_GLOBAL", "eln_field_def", definition.getId(),
|
context, before, definition, actualReason);
|
}
|
|
@Override
|
public void enable(String fieldId, String reason) {
|
changeEnabled(fieldId, ENABLED, requireReason(reason), "ELN_FIELD_ENABLE");
|
}
|
|
@Override
|
public void disable(String fieldId, String reason) {
|
changeEnabled(fieldId, DISABLED, requireReason(reason), "ELN_FIELD_DISABLE");
|
}
|
|
private ElnFieldVos.Detail createIdentity(String tenantId, String operatorId,
|
boolean templateField, String templateCode,
|
String remark, ElnFieldForms.VersionRule rules,
|
boolean preRelease) {
|
ElnFieldDefEntity definition = null;
|
DuplicateKeyException lastCollision = null;
|
for (int attempt = 0; attempt < CODE_RETRY_LIMIT; attempt++) {
|
definition = new ElnFieldDefEntity();
|
definition.setId(idSupplier.get());
|
definition.setFTenantId(tenantId);
|
definition.setZiduanBianma(codeGenerator.generate(
|
rules.getZiduanMingcheng(), templateField));
|
definition.setZuoyongFanwei(templateField
|
? ElnFieldEnums.Scope.TEMPLATE.name() : ElnFieldEnums.Scope.GLOBAL.name());
|
definition.setMubanBianhao(templateCode);
|
definition.setBizEnabled(ENABLED);
|
definition.setBeizhu(trimToNull(remark));
|
definition.setFVersion(0);
|
definition.setFDeleteMark(0);
|
definition.setFCreatorUserId(operatorId);
|
definition.setFLastModifyUserId(operatorId);
|
Date now = new Date();
|
definition.setFCreatorTime(now);
|
definition.setFLastModifyTime(now);
|
try {
|
store.insertDefinition(definition);
|
lastCollision = null;
|
break;
|
} catch (DuplicateKeyException collision) {
|
lastCollision = collision;
|
}
|
}
|
if (lastCollision != null || definition == null) {
|
throw new DataException("字段编码生成冲突,请重试");
|
}
|
UserContext actor = new UserContext(tenantId, operatorId);
|
audit("ELN_FIELD_CREATE", "eln_field_def", definition.getId(),
|
actor, null, definition, null);
|
|
ElnFieldVersionEntity version = new ElnFieldVersionEntity();
|
version.setId(idSupplier.get());
|
version.setFTenantId(tenantId);
|
version.setZiduanDingyiId(definition.getId());
|
version.setBanbenHao(1);
|
version.setBizStatus(ElnFieldEnums.VersionStatus.DRAFT.name());
|
version.setFVersion(0);
|
version.setFDeleteMark(0);
|
version.setFCreatorUserId(operatorId);
|
version.setFLastModifyUserId(operatorId);
|
applyRules(version, rules);
|
store.insertVersion(version);
|
audit("ELN_FIELD_VERSION_CREATE", "eln_field_version", version.getId(),
|
actor, null, version, null);
|
if (preRelease) {
|
validateForReference(definition, version);
|
changeVersionStatus(version, ElnFieldEnums.VersionStatus.PRE_RELEASE, null, actor,
|
"ELN_FIELD_VERSION_PRE_RELEASE");
|
}
|
return toDetail(definition, Collections.singletonList(version));
|
}
|
|
private void changeEnabled(String fieldId, String target, String reason, String action) {
|
UserContext context = requireCatalogAccess();
|
ElnFieldDefEntity definition = requireDefinition(context.getTenantId(), fieldId, true);
|
requireMutable(definition);
|
if (target.equals(definition.getBizEnabled())) {
|
throw new DataException(ENABLED.equals(target) ? "字段已经启用" : "字段已经停用");
|
}
|
ElnFieldDefEntity before = copyDefinition(definition);
|
definition.setBizEnabled(target);
|
definition.setFLastModifyUserId(context.getUserId());
|
store.updateDefinition(definition);
|
audit(action, "eln_field_def", definition.getId(), context, before, definition, reason);
|
}
|
|
private void changeVersionStatus(ElnFieldVersionEntity version,
|
ElnFieldEnums.VersionStatus status,
|
String cancelReason,
|
UserContext context,
|
String action) {
|
ElnFieldVersionEntity before = copyVersion(version);
|
version.setBizStatus(status.name());
|
version.setZuofeiYuanyin(cancelReason);
|
version.setFLastModifyUserId(context.getUserId());
|
store.updateVersion(version);
|
audit(action, "eln_field_version", version.getId(), context,
|
before, version, cancelReason);
|
}
|
|
private void validateDraftBasics(ElnFieldForms.VersionRule rules) {
|
if (rules == null || !StringUtils.hasText(rules.getZiduanMingcheng())) {
|
throw new DataException("字段名称不能为空");
|
}
|
enumValue(ElnFieldEnums.DataType.class, rules.getShujuLeixing(), "字段类型无效");
|
if (rules.getShifouBitian() == null) {
|
rules.setShifouBitian(0);
|
}
|
if (rules.getShifouBitian() != 0 && rules.getShifouBitian() != 1) {
|
throw new DataException("必填标志只能为 0 或 1");
|
}
|
if (!StringUtils.hasText(rules.getPiciMoshi())) {
|
rules.setPiciMoshi(ElnFieldEnums.BatchMode.RECORD_PRIVATE.name());
|
}
|
enumValue(ElnFieldEnums.BatchMode.class, rules.getPiciMoshi(), "批次模式无效");
|
}
|
|
private void validateForReference(ElnFieldDefEntity definition,
|
ElnFieldVersionEntity version) {
|
enumValue(ElnFieldEnums.DataType.class, version.getShujuLeixing(), "字段类型无效");
|
enumValue(ElnFieldEnums.BatchMode.class, version.getPiciMoshi(), "批次模式无效");
|
if (version.getShifouBitian() == null
|
|| (version.getShifouBitian() != 0 && version.getShifouBitian() != 1)) {
|
throw new DataException("必填标志只能为 0 或 1");
|
}
|
validateControlConfig(version.getKongjianPeizhi());
|
ElnFieldForms.Binding binding = parseBinding(version.getBangdingPeizhi());
|
if (binding == null || binding.getRead() == null || binding.getWrite() == null) {
|
throw new DataException("字段 binding 配置不完整");
|
}
|
String source = binding.getRead().getSource();
|
enumValue(ElnFieldEnums.ReadSource.class, source, "字段读取来源无效");
|
if (!"ELN_RECORD".equals(binding.getWrite().getTarget())) {
|
throw new DataException("字段写入目标必须为 ELN_RECORD");
|
}
|
boolean external = ElnFieldEnums.ReadSource.LIMS_TASK.name().equals(source)
|
|| ElnFieldEnums.ReadSource.SYSTEM.name().equals(source);
|
if (external) {
|
if (!ElnFieldEnums.Scope.GLOBAL.name().equals(definition.getZuoyongFanwei())) {
|
throw new DataException("外部来源只允许用于全局字段");
|
}
|
ElnFieldProviderCatalog.Property property = providerCatalog.reference(
|
source, binding.getRead().getProperty(), binding.getRead().getPropertyRevision());
|
if (property == null || !property.isReadOnly() || !property.isEnabled()) {
|
throw new DataException("provider 属性不在允许目录中");
|
}
|
if (!providerTypeCompatible(property.getDataType(), version.getShujuLeixing())) {
|
throw new DataException("provider 属性类型与字段类型不兼容");
|
}
|
if (!ElnFieldEnums.BatchMode.RECORD_PRIVATE.name().equals(version.getPiciMoshi())) {
|
throw new DataException("外部来源字段不允许多批次共享");
|
}
|
} else if (StringUtils.hasText(binding.getRead().getProperty())) {
|
throw new DataException("NONE 来源不能配置 provider 属性");
|
}
|
}
|
|
private void applyRules(ElnFieldVersionEntity target, ElnFieldForms.VersionRule source) {
|
target.setZiduanMingcheng(source.getZiduanMingcheng().trim());
|
target.setShujuLeixing(source.getShujuLeixing().trim());
|
target.setDanwei(trimToNull(source.getDanwei()));
|
target.setShifouBitian(source.getShifouBitian());
|
target.setPiciMoshi(source.getPiciMoshi().trim());
|
target.setBangdingPeizhi(source.getBinding() == null
|
? null : JsonUtil.getObjectToString(source.getBinding()));
|
target.setKongjianPeizhi(source.getKongjianPeizhi() == null
|
? null : JsonUtil.getObjectToString(source.getKongjianPeizhi()));
|
applyCheckboxGroupRecommendation(target, source);
|
}
|
|
private void applyCheckboxGroupRecommendation(ElnFieldVersionEntity target,
|
ElnFieldForms.VersionRule source) {
|
String code = trimToNull(source.getRecommendedCheckboxGroupCode());
|
if (code == null) {
|
target.setRecommendedCheckboxGroupCode(null);
|
target.setRecommendedCheckboxMinSelected(null);
|
target.setRecommendedCheckboxMaxSelected(null);
|
return;
|
}
|
if (!CHECKBOX_GROUP_CODE.matcher(code).matches()) {
|
throw new DataException("推荐选择组编码只能包含字母、数字、下划线或连字符,且长度不能超过 80");
|
}
|
if (!ElnFieldEnums.DataType.CHECKBOX.name().equals(source.getShujuLeixing())) {
|
throw new DataException("只有复选框字段可以配置推荐选择组");
|
}
|
ElnFieldForms.Binding binding = source.getBinding();
|
String readSource = binding == null || binding.getRead() == null
|
? null : binding.getRead().getSource();
|
if (!ElnFieldEnums.ReadSource.NONE.name().equals(readSource)) {
|
throw new DataException("外部只读字段不能配置推荐选择组");
|
}
|
Integer min = source.getRecommendedCheckboxMinSelected();
|
Integer max = source.getRecommendedCheckboxMaxSelected();
|
min = min == null ? 0 : min;
|
if (min < 0) {
|
throw new DataException("推荐最少选择数不能小于 0");
|
}
|
if (max != null && max < 1) {
|
throw new DataException("推荐最多选择数必须大于 0,或设为不限");
|
}
|
if (max != null && min > max) {
|
throw new DataException("推荐最少选择数不能大于最多选择数");
|
}
|
target.setRecommendedCheckboxGroupCode(code);
|
target.setRecommendedCheckboxMinSelected(min);
|
target.setRecommendedCheckboxMaxSelected(max);
|
}
|
|
private int nextVersionNumber(String tenantId, String definitionId) {
|
int max = 0;
|
for (ElnFieldVersionEntity version : store.listVersions(tenantId, definitionId)) {
|
if (version.getBanbenHao() != null && version.getBanbenHao() > max) {
|
max = version.getBanbenHao();
|
}
|
}
|
return max + 1;
|
}
|
|
private ElnFieldDefEntity requireDefinition(String tenantId, String id, boolean forUpdate) {
|
if (!StringUtils.hasText(id)) {
|
throw new DataException("字段 id 不能为空");
|
}
|
ElnFieldDefEntity definition = store.findDefinition(tenantId, id.trim(), forUpdate);
|
if (definition == null) {
|
throw new DataException("字段不存在");
|
}
|
return definition;
|
}
|
|
private ElnFieldVersionEntity requireVersion(String tenantId, String id) {
|
if (!StringUtils.hasText(id)) {
|
throw new DataException("字段版本 id 不能为空");
|
}
|
ElnFieldVersionEntity version = store.findVersion(tenantId, id.trim());
|
if (version == null) {
|
throw new DataException("字段版本不存在");
|
}
|
return version;
|
}
|
|
private static void requireSameDefinition(ElnFieldDefEntity definition,
|
ElnFieldVersionEntity version) {
|
if (!definition.getId().equals(version.getZiduanDingyiId())) {
|
throw new DataException("字段版本不属于当前字段");
|
}
|
}
|
|
private static void requireEnabled(ElnFieldDefEntity definition) {
|
if (!ENABLED.equals(definition.getBizEnabled())) {
|
throw new DataException("字段已停用,不能执行该操作");
|
}
|
}
|
|
private UserContext requireLogin() {
|
UserContext context = userContextSupplier.get();
|
if (context == null || !StringUtils.hasText(context.getTenantId())
|
|| !StringUtils.hasText(context.getUserId())) {
|
throw new DataException("请先登录后再维护字段目录");
|
}
|
return context;
|
}
|
|
private UserContext requireCatalogAccess() {
|
UserContext context = requireLogin();
|
if (!maintainPermission.getAsBoolean()) {
|
throw new DataException("没有全局字段维护权限");
|
}
|
return context;
|
}
|
|
private static String requireReason(String reason) {
|
if (!StringUtils.hasText(reason)) {
|
throw new DataException("操作原因不能为空");
|
}
|
return reason.trim();
|
}
|
|
private static void requireMutable(ElnFieldDefEntity definition) {
|
if (definition != null && ElnTestResultFields.isSystemField(definition.getZiduanBianma())) {
|
throw new DataException("系统结果字段不允许通过通用字段目录修改");
|
}
|
}
|
|
private static <E extends Enum<E>> E enumValue(Class<E> type, String value, String message) {
|
if (!StringUtils.hasText(value)) {
|
throw new DataException(message);
|
}
|
try {
|
return Enum.valueOf(type, value.trim());
|
} catch (IllegalArgumentException exception) {
|
throw new DataException(message);
|
}
|
}
|
|
private static ElnFieldForms.Binding defaultManualBinding() {
|
ElnFieldForms.Read read = new ElnFieldForms.Read();
|
read.setSource(ElnFieldEnums.ReadSource.NONE.name());
|
ElnFieldForms.Write write = new ElnFieldForms.Write();
|
write.setTarget("ELN_RECORD");
|
ElnFieldForms.Binding binding = new ElnFieldForms.Binding();
|
binding.setRead(read);
|
binding.setWrite(write);
|
return binding;
|
}
|
|
private static ElnFieldForms.Binding parseBinding(String json) {
|
return StringUtils.hasText(json)
|
? JsonUtil.getJsonToBean(json, ElnFieldForms.Binding.class) : null;
|
}
|
|
private static boolean providerTypeCompatible(String providerType, String fieldType) {
|
if ("BOOLEAN".equals(providerType)) return ElnFieldEnums.DataType.CHECKBOX.name().equals(fieldType);
|
if ("TEXT".equals(providerType)) {
|
return ElnFieldEnums.DataType.TEXT.name().equals(fieldType)
|
|| ElnFieldEnums.DataType.TEXTAREA.name().equals(fieldType)
|
|| ElnFieldEnums.DataType.SELECT.name().equals(fieldType);
|
}
|
return providerType != null && providerType.equals(fieldType);
|
}
|
|
private static void validateControlConfig(String json) {
|
if (!StringUtils.hasText(json)) {
|
return;
|
}
|
try {
|
Map<?, ?> config = JsonUtil.getJsonToBean(json, Map.class);
|
if (config == null) {
|
throw new DataException("控件配置必须是 JSON 对象");
|
}
|
} catch (RuntimeException exception) {
|
throw new DataException("控件配置必须是 JSON 对象");
|
}
|
}
|
|
@SuppressWarnings("unchecked")
|
private static Map<String, Object> parseMap(String json) {
|
return StringUtils.hasText(json) ? JsonUtil.getJsonToBean(json, Map.class) : null;
|
}
|
|
private ElnFieldVos.Detail toDetail(ElnFieldDefEntity definition,
|
List<ElnFieldVersionEntity> versions) {
|
ElnFieldVos.Detail result = new ElnFieldVos.Detail();
|
result.setId(definition.getId());
|
result.setZiduanBianma(definition.getZiduanBianma());
|
result.setZuoyongFanwei(definition.getZuoyongFanwei());
|
result.setMubanBianhao(definition.getMubanBianhao());
|
result.setDangqianZiduanBanbenId(definition.getDangqianZiduanBanbenId());
|
result.setBizEnabled(definition.getBizEnabled());
|
result.setBeizhu(definition.getBeizhu());
|
List<ElnFieldVersionEntity> sorted = new ArrayList<>(versions);
|
sorted.sort(Comparator.comparing(ElnFieldVersionEntity::getBanbenHao));
|
List<ElnFieldVos.Version> versionVos = new ArrayList<>();
|
for (ElnFieldVersionEntity version : sorted) {
|
versionVos.add(toVersion(version));
|
}
|
result.setVersions(versionVos);
|
return result;
|
}
|
|
private ElnFieldVos.ListItem toListItem(ElnFieldDefEntity definition,
|
List<ElnFieldVersionEntity> versions) {
|
ElnFieldVos.ListItem result = new ElnFieldVos.ListItem();
|
result.setId(definition.getId());
|
result.setZiduanBianma(definition.getZiduanBianma());
|
result.setZuoyongFanwei(definition.getZuoyongFanwei());
|
result.setMubanBianhao(definition.getMubanBianhao());
|
result.setDangqianZiduanBanbenId(definition.getDangqianZiduanBanbenId());
|
result.setBizEnabled(definition.getBizEnabled());
|
result.setGengxinShijian(definition.getFLastModifyTime() == null
|
? definition.getFCreatorTime() : definition.getFLastModifyTime());
|
int latestPublished = 0;
|
for (ElnFieldVersionEntity version : versions) {
|
if (version.getId().equals(definition.getDangqianZiduanBanbenId())) {
|
result.setZiduanMingcheng(version.getZiduanMingcheng());
|
result.setDangqianBanbenHao(version.getBanbenHao());
|
}
|
if (ElnFieldEnums.VersionStatus.PUBLISHED.name().equals(version.getBizStatus())
|
&& version.getBanbenHao() != null && version.getBanbenHao() > latestPublished) {
|
latestPublished = version.getBanbenHao();
|
}
|
}
|
if (result.getZiduanMingcheng() == null && !versions.isEmpty()) {
|
ElnFieldVersionEntity latest = Collections.max(versions,
|
Comparator.comparing(ElnFieldVersionEntity::getBanbenHao));
|
result.setZiduanMingcheng(latest.getZiduanMingcheng());
|
}
|
result.setZuixinFabuBanbenHao(latestPublished == 0 ? null : latestPublished);
|
result.setMubanYinyongShu(0L);
|
return result;
|
}
|
|
private ElnFieldVos.Version toVersion(ElnFieldVersionEntity source) {
|
ElnFieldVos.Version result = new ElnFieldVos.Version();
|
result.setId(source.getId());
|
result.setZiduanDingyiId(source.getZiduanDingyiId());
|
result.setBanbenHao(source.getBanbenHao());
|
result.setZiduanMingcheng(source.getZiduanMingcheng());
|
result.setShujuLeixing(source.getShujuLeixing());
|
result.setDanwei(source.getDanwei());
|
result.setShifouBitian(source.getShifouBitian());
|
result.setPiciMoshi(source.getPiciMoshi());
|
result.setBinding(parseBinding(source.getBangdingPeizhi()));
|
result.setKongjianPeizhi(parseMap(source.getKongjianPeizhi()));
|
result.setRecommendedCheckboxGroupCode(source.getRecommendedCheckboxGroupCode());
|
result.setRecommendedCheckboxMinSelected(source.getRecommendedCheckboxMinSelected());
|
result.setRecommendedCheckboxMaxSelected(source.getRecommendedCheckboxMaxSelected());
|
result.setBizStatus(source.getBizStatus());
|
result.setZuofeiYuanyin(source.getZuofeiYuanyin());
|
result.setChuangjianShijian(source.getFCreatorTime());
|
result.setGengxinShijian(source.getFLastModifyTime());
|
return result;
|
}
|
|
private static ElnFieldDefEntity copyDefinition(ElnFieldDefEntity source) {
|
return JsonUtil.getJsonToBean(source, ElnFieldDefEntity.class);
|
}
|
|
private static ElnFieldVersionEntity copyVersion(ElnFieldVersionEntity source) {
|
return JsonUtil.getJsonToBean(source, ElnFieldVersionEntity.class);
|
}
|
|
private void audit(String actionCode, String table, String targetId, UserContext context,
|
Object before, Object after, String reason) {
|
Map<String, Object> snapshot = new LinkedHashMap<>();
|
snapshot.put("before", before);
|
snapshot.put("after", after);
|
auditRecorder.accept(AuditEventDTO.builder()
|
.tenantId(context.getTenantId())
|
.operatorId(context.getUserId())
|
.eventTime(new Date())
|
.eventType("DATA_CHANGE")
|
.actionCode(actionCode)
|
.actionLabel(actionCode)
|
.bizModule("eln")
|
.bizType("eln_field_def".equals(table) ? "eln_field" : "eln_field_version")
|
.targetTable(table)
|
.targetId(targetId)
|
.reason(reason)
|
.dataSnapshot(JsonUtil.getObjectToString(snapshot))
|
.build());
|
}
|
|
private static String trimToNull(String value) {
|
return StringUtils.hasText(value) ? value.trim() : null;
|
}
|
|
private static UserContext currentUserContext() {
|
UserInfo user = UserProvider.getUser();
|
String tenantId = null;
|
TenantVO routed = TenantDataSourceUtil.getTenantInfo();
|
if (routed != null && StringUtils.hasText(routed.getEnCode())) {
|
tenantId = routed.getEnCode().trim();
|
} else if (user != null && StringUtils.hasText(user.getTenantId())) {
|
tenantId = user.getTenantId().trim();
|
}
|
if (!StringUtils.hasText(tenantId)) {
|
tenantId = "0";
|
}
|
return new UserContext(tenantId, user == null ? null : trimToNull(user.getUserId()));
|
}
|
|
private static boolean hasPromotePermission() {
|
return hasMenuPermission();
|
}
|
|
private static boolean hasMaintainPermission() {
|
return hasMenuPermission();
|
}
|
|
private static boolean hasMenuPermission() {
|
UserInfo user = UserProvider.getUser();
|
return user != null && (Boolean.TRUE.equals(user.getIsAdministrator())
|
|| StpUtil.hasPermission(MENU_PERMISSION));
|
}
|
|
static final class UserContext {
|
private final String tenantId;
|
private final String userId;
|
|
UserContext(String tenantId, String userId) {
|
this.tenantId = tenantId;
|
this.userId = userId;
|
}
|
|
String getTenantId() {
|
return tenantId;
|
}
|
|
String getUserId() {
|
return userId;
|
}
|
}
|
|
interface FieldCatalogStore {
|
void insertDefinition(ElnFieldDefEntity entity);
|
|
void insertVersion(ElnFieldVersionEntity entity);
|
|
ElnFieldDefEntity findDefinition(String tenantId, String id, boolean forUpdate);
|
|
ElnFieldVersionEntity findVersion(String tenantId, String id);
|
|
List<ElnFieldVersionEntity> listVersions(String tenantId, String definitionId);
|
|
boolean hasEnabledTemplateFieldName(String tenantId, String templateCode,
|
String fieldName);
|
|
IPage<ElnFieldDefEntity> page(String tenantId, ElnFieldForms.Query query);
|
|
void updateDefinition(ElnFieldDefEntity entity);
|
|
void updateVersion(ElnFieldVersionEntity entity);
|
}
|
|
private static final class MyBatisFieldCatalogStore implements FieldCatalogStore {
|
private final ElnFieldDefMapper definitionMapper;
|
private final ElnFieldVersionMapper versionMapper;
|
|
private MyBatisFieldCatalogStore(ElnFieldDefMapper definitionMapper,
|
ElnFieldVersionMapper versionMapper) {
|
this.definitionMapper = definitionMapper;
|
this.versionMapper = versionMapper;
|
}
|
|
@Override
|
public void insertDefinition(ElnFieldDefEntity entity) {
|
if (definitionMapper.insertIgnoreCode(entity) != 1) {
|
throw new DuplicateKeyException("字段编码已存在");
|
}
|
}
|
|
@Override
|
public void insertVersion(ElnFieldVersionEntity entity) {
|
versionMapper.insert(entity);
|
}
|
|
@Override
|
public ElnFieldDefEntity findDefinition(String tenantId, String id, boolean forUpdate) {
|
LambdaQueryWrapper<ElnFieldDefEntity> query = Wrappers.<ElnFieldDefEntity>lambdaQuery()
|
.eq(ElnFieldDefEntity::getFTenantId, tenantId)
|
.eq(ElnFieldDefEntity::getId, id)
|
.and(item -> item.isNull(ElnFieldDefEntity::getFDeleteMark)
|
.or().eq(ElnFieldDefEntity::getFDeleteMark, 0));
|
if (forUpdate) {
|
query.last("FOR UPDATE");
|
}
|
return definitionMapper.selectOne(query);
|
}
|
|
@Override
|
public ElnFieldVersionEntity findVersion(String tenantId, String id) {
|
return versionMapper.selectOne(Wrappers.<ElnFieldVersionEntity>lambdaQuery()
|
.eq(ElnFieldVersionEntity::getFTenantId, tenantId)
|
.eq(ElnFieldVersionEntity::getId, id)
|
.and(item -> item.isNull(ElnFieldVersionEntity::getFDeleteMark)
|
.or().eq(ElnFieldVersionEntity::getFDeleteMark, 0)));
|
}
|
|
@Override
|
public List<ElnFieldVersionEntity> listVersions(String tenantId, String definitionId) {
|
return versionMapper.selectList(Wrappers.<ElnFieldVersionEntity>lambdaQuery()
|
.eq(ElnFieldVersionEntity::getFTenantId, tenantId)
|
.eq(ElnFieldVersionEntity::getZiduanDingyiId, definitionId)
|
.and(item -> item.isNull(ElnFieldVersionEntity::getFDeleteMark)
|
.or().eq(ElnFieldVersionEntity::getFDeleteMark, 0))
|
.orderByAsc(ElnFieldVersionEntity::getBanbenHao));
|
}
|
|
@Override
|
public boolean hasEnabledTemplateFieldName(String tenantId, String templateCode,
|
String fieldName) {
|
List<ElnFieldDefEntity> definitions = definitionMapper.selectList(
|
Wrappers.<ElnFieldDefEntity>lambdaQuery()
|
.eq(ElnFieldDefEntity::getFTenantId, tenantId)
|
.eq(ElnFieldDefEntity::getZuoyongFanwei,
|
ElnFieldEnums.Scope.TEMPLATE.name())
|
.eq(ElnFieldDefEntity::getMubanBianhao, templateCode)
|
.eq(ElnFieldDefEntity::getBizEnabled, ENABLED)
|
.and(item -> item.isNull(ElnFieldDefEntity::getFDeleteMark)
|
.or().eq(ElnFieldDefEntity::getFDeleteMark, 0)));
|
List<String> definitionIds = new ArrayList<>();
|
for (ElnFieldDefEntity definition : definitions) {
|
definitionIds.add(definition.getId());
|
}
|
return !definitionIds.isEmpty() && versionMapper.selectCount(
|
Wrappers.<ElnFieldVersionEntity>lambdaQuery()
|
.eq(ElnFieldVersionEntity::getFTenantId, tenantId)
|
.in(ElnFieldVersionEntity::getZiduanDingyiId, definitionIds)
|
.eq(ElnFieldVersionEntity::getZiduanMingcheng, fieldName)
|
.and(item -> item.isNull(ElnFieldVersionEntity::getFDeleteMark)
|
.or().eq(ElnFieldVersionEntity::getFDeleteMark, 0))) > 0;
|
}
|
|
@Override
|
public IPage<ElnFieldDefEntity> page(String tenantId, ElnFieldForms.Query form) {
|
LambdaQueryWrapper<ElnFieldDefEntity> query = Wrappers.<ElnFieldDefEntity>lambdaQuery()
|
.eq(ElnFieldDefEntity::getFTenantId, tenantId)
|
.and(item -> item.isNull(ElnFieldDefEntity::getFDeleteMark)
|
.or().eq(ElnFieldDefEntity::getFDeleteMark, 0));
|
if (StringUtils.hasText(form.getZuoyongFanwei())) {
|
query.eq(ElnFieldDefEntity::getZuoyongFanwei, form.getZuoyongFanwei().trim());
|
}
|
if (StringUtils.hasText(form.getBizEnabled())) {
|
query.eq(ElnFieldDefEntity::getBizEnabled, form.getBizEnabled().trim());
|
}
|
if (StringUtils.hasText(form.getMubanBianhao())) {
|
query.eq(ElnFieldDefEntity::getMubanBianhao, form.getMubanBianhao().trim());
|
}
|
if (StringUtils.hasText(form.getKeyword())) {
|
String keyword = form.getKeyword().trim();
|
List<String> definitionIds = versionMapper.selectObjs(
|
Wrappers.<ElnFieldVersionEntity>lambdaQuery()
|
.select(ElnFieldVersionEntity::getZiduanDingyiId)
|
.eq(ElnFieldVersionEntity::getFTenantId, tenantId)
|
.like(ElnFieldVersionEntity::getZiduanMingcheng, keyword));
|
query.and(item -> {
|
item.like(ElnFieldDefEntity::getZiduanBianma, keyword);
|
if (!definitionIds.isEmpty()) {
|
item.or().in(ElnFieldDefEntity::getId, definitionIds);
|
}
|
});
|
}
|
query.orderByDesc(ElnFieldDefEntity::getFLastModifyTime)
|
.orderByDesc(ElnFieldDefEntity::getFCreatorTime);
|
return definitionMapper.selectPage(
|
new Page<>(form.getCurrentPage(), form.getPageSize()), query);
|
}
|
|
@Override
|
public void updateDefinition(ElnFieldDefEntity entity) {
|
if (definitionMapper.updateById(entity) != 1) {
|
throw new DataException("字段状态已变化,请刷新后重试");
|
}
|
}
|
|
@Override
|
public void updateVersion(ElnFieldVersionEntity entity) {
|
if (versionMapper.updateById(entity) != 1) {
|
throw new DataException("字段版本状态已变化,请刷新后重试");
|
}
|
}
|
}
|
}
|