/*!
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
See LICENSE and COMMERCIAL in the project root for license information.
*/
package com.rebuild.core.service.general;
import cn.devezhao.bizz.privileges.Permission;
import cn.devezhao.bizz.privileges.impl.BizzPermission;
import cn.devezhao.commons.ReflectUtils;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.Filter;
import cn.devezhao.persist4j.PersistManagerFactory;
import cn.devezhao.persist4j.Query;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.RebuildException;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.metadata.DeleteRecord;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.MetadataSorter;
import com.rebuild.core.metadata.easymeta.DisplayType;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
import com.rebuild.core.privileges.UserService;
import com.rebuild.core.privileges.bizz.InternalPermission;
import com.rebuild.core.privileges.bizz.User;
import com.rebuild.core.service.BaseService;
import com.rebuild.core.service.DataSpecificationException;
import com.rebuild.core.service.NoRecordFoundException;
import com.rebuild.core.service.approval.ApprovalHelper;
import com.rebuild.core.service.approval.ApprovalState;
import com.rebuild.core.service.general.recyclebin.RecycleStore;
import com.rebuild.core.service.general.series.SeriesGeneratorFactory;
import com.rebuild.core.service.notification.NotificationObserver;
import com.rebuild.core.service.query.QueryHelper;
import com.rebuild.core.service.trigger.ActionType;
import com.rebuild.core.service.trigger.RobotTriggerManager;
import com.rebuild.core.service.trigger.RobotTriggerManual;
import com.rebuild.core.service.trigger.RobotTriggerObserver;
import com.rebuild.core.service.trigger.TriggerAction;
import com.rebuild.core.service.trigger.TriggerWhen;
import com.rebuild.core.service.trigger.impl.AutoApproval;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.core.support.task.TaskExecutors;
import com.rebuild.utils.CommonsUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
@Slf4j
@Service("rbGeneralEntityService")
public class GeneralEntityService extends ObservableService implements EntityService {
public static final String HAS_DETAILS = "$DETAILS$";
protected GeneralEntityService(PersistManagerFactory aPMFactory) {
super(aPMFactory);
addObserver(new NotificationObserver());
addObserver(new RobotTriggerObserver());
}
@Override
public int getEntityCode() {
return 0;
}
@Override
public Record createOrUpdate(Record record) {
@SuppressWarnings("unchecked")
final List<Record> details = (List<Record>) record.removeValue(HAS_DETAILS);
final int rcm = GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
if (rcm == GeneralEntityServiceContextHolder.RCM_CHECK_MAIN
|| rcm == GeneralEntityServiceContextHolder.RCM_CHECK_ALL) {
List<Record> repeated = getAndCheckRepeated(record, 20);
if (!repeated.isEmpty()) {
throw new RepeatedRecordsException(repeated);
}
}
final boolean hasDetails = details != null && !details.isEmpty();
boolean lazyAutoApproval4Details = false;
boolean lazyAutoTransform4Details = false;
boolean lazyHookUrl4Details = false;
if (hasDetails) {
TriggerAction[] hasAutoApprovalTriggers = getSpecTriggers(
record.getEntity(), ActionType.AUTOAPPROVAL, TriggerWhen.CREATE, TriggerWhen.UPDATE);
lazyAutoApproval4Details = hasAutoApprovalTriggers.length > 0;
if (!lazyAutoApproval4Details) {
TriggerAction[] hasOnApprovedTriggers = getSpecTriggers(
record.getEntity().getDetailEntity(), null, TriggerWhen.APPROVED);
lazyAutoApproval4Details = hasOnApprovedTriggers.length > 0;
}
if (lazyAutoApproval4Details) AutoApproval.setLazy();
TriggerAction[] hasAutoTransformTriggers = getSpecTriggers(
record.getEntity(), ActionType.AUTOTRANSFORM, TriggerWhen.CREATE, TriggerWhen.UPDATE);
lazyAutoTransform4Details = hasAutoTransformTriggers.length > 0;
if (lazyAutoTransform4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#setLazy");
TriggerAction[] hasHookUrlTriggers = getSpecTriggers(
record.getEntity(), ActionType.HOOKURL, TriggerWhen.CREATE, TriggerWhen.UPDATE, TriggerWhen.DELETE);
lazyHookUrl4Details = hasHookUrlTriggers.length > 0;
if (lazyHookUrl4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.HookUrl#setLazy");
}
Map<Integer, ID> detaileds = new TreeMap<>();
try {
record = record.getPrimary() == null ? create(record) : update(record);
if (!hasDetails) return record;
final Entity detailEntity = record.getEntity().getDetailEntity();
final String dtfField = MetadataHelper.getDetailToMainField(detailEntity).getName();
final ID mainid = record.getPrimary();
final boolean checkDetailsRepeated = rcm == GeneralEntityServiceContextHolder.RCM_CHECK_DETAILS
|| rcm == GeneralEntityServiceContextHolder.RCM_CHECK_ALL;
EntityService des = Application.getEntityService(detailEntity.getEntityCode());
if (des.getEntityCode() == 0) des = this;
for (int i = 0; i < details.size(); i++) {
Record d = details.get(i);
if (d instanceof DeleteRecord) {
des.delete(d.getPrimary());
detaileds.put(i, d.getPrimary());
}
}
for (int i = 0; i < details.size(); i++) {
Record d = details.get(i);
if (d instanceof DeleteRecord) continue;
if (checkDetailsRepeated) {
d.setID(dtfField, mainid);
List<Record> repeated = des.getAndCheckRepeated(d, 20);
if (!repeated.isEmpty()) {
throw new RepeatedRecordsException(repeated);
}
}
if (d.getPrimary() == null) {
d.setID(dtfField, mainid);
des.create(d);
} else {
des.update(d);
}
detaileds.put(i, d.getPrimary());
}
record.setObjectValue(HAS_DETAILS, detaileds.values());
return record;
} finally {
if (lazyAutoApproval4Details) AutoApproval.executeLazy();
if (lazyAutoTransform4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#executeLazy");
if (lazyHookUrl4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.HookUrl#executeLazy");
}
}
@Override