AbstractAction.java

/*
 *        @ Copyright 2001 FA Software;
 *        All right reserved. No part of this program may be reproduced or
 *        transmitted in any form or by any means, electronic or
 *        mechanical, including photocopying, recording, or by any
 *        information storage or retrieval system without written
 *        permission from FA Software, except for inclusion of brief
 *        quotations in a review.
 */
package com.mycim.webapp.actions;

import com.fa.sesa.exception.Assert;
import com.fa.sesa.exception.Errors;
import com.fa.sesa.i18n.I18nUtils;
import com.fa.sesa.threadlocal.LocalContext;
import com.mycim.framework.context.spring.SpringContext;
import com.mycim.framework.logging.Logger;
import com.mycim.framework.logging.LoggerFactory;
import com.mycim.framework.utils.beans.PropertyUtils;
import com.mycim.framework.utils.lang.ArrayUtils;
import com.mycim.framework.utils.lang.StringUtils;
import com.mycim.framework.utils.msg.JsonUtils;
import com.mycim.framework.utils.msg.model.Request;
import com.mycim.framework.utils.msg.model.RequestHeader;
import com.mycim.server.base.service.BaseService;
import com.mycim.server.security.service.SecurityService;
import com.mycim.server.system.service.SysService;
import com.mycim.valueobject.*;
import com.mycim.valueobject.bas.NamedObject;
import com.mycim.valueobject.sys.Facility;
import com.mycim.valueobject.sys.ReferenceFileDetail;
import com.mycim.webapp.Constants;
import com.mycim.webapp.ResponseMsgBuilder;
import com.mycim.webapp.WebUtils;
import com.mycim.webapp.forms.RootForm;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;

/**
 * @author Johnson.Wang
 * @version 6.0.0
 * @date 2019/8/24
 **/
public abstract class AbstractAction extends Action {

    protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    protected BaseService baseService = SpringContext.getBean(BaseService.class);

    protected SysService sysService = SpringContext.getBean(SysService.class);

    protected SecurityService securityService = SpringContext.getBean(SecurityService.class);

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                 HttpServletResponse response) throws Exception {
        try {
            return perform(mapping, form, request, response);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            request.setAttribute(Constants.MESSAGE_ERROR, ResponseMsgBuilder.buildErrorJsonMsg(e));
            return (mapping.findForward(Constants.ERROR_KEY));
        }
    }

    public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                 HttpServletResponse response) throws Exception {
        Boolean isUpload = Boolean.valueOf(request.getParameter("isUpload"));
        if (isAjax(request)) {
            // 请求为json消息时
            return executeAjax(mapping, form, request, response);
        } else if (isUpload) {
            // 上传文件
            return executeUpload(mapping, form, request, response);
        } else {
            // 请求为struts时
            return executeStruts(mapping, form, request, response);
        }
    }

    /**
     * 在struts action中注册初始化查询数据的方法
     *
     * @return
     */
    public String inItMethod() {
        return "init";
    }

    public ActionForward init(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                              HttpServletResponse response) throws Exception {
        return WebUtils.NULLActionForward;
    }

    /**
     * 默认的返回方法
     */
    public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                HttpServletResponse response) throws Exception {
        RootForm rootForm = (RootForm) form;
        rootForm.setInstanceId("");
        return (mapping.findForward(Constants.SETUP_KEY));
    }

    private void checkUrl(HttpServletRequest request) {
        String query = request.getQueryString();
        int index = StringUtils.indexOf(query, "?");
        Assert.isFalse(index > 0,
                       Errors.create().key(MessageIdList.SYSTEM_FORBID_REPETITION_SUBMIT).content("禁止重复提交!").build());
    }

    private ActionForward executeUpload(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                        HttpServletResponse response) {
        try {
            Object result = invokeAction(getMethodName(request), null, mapping, form, request, response);
            if (result == null) {
                WebUtils.writeJson(response, ResponseMsgBuilder.buildSuccessJsonMsg());
            } else if (result instanceof ActionForward) {
                return (ActionForward) result;
            } else {
                WebUtils.writeJson(response, ResponseMsgBuilder.buildSuccessJsonMsg(result));
            }
        } catch (Exception e) {
            LOGGER.error(e.getCause());
            String msg = ResponseMsgBuilder.buildErrorJsonMsg(e);
            WebUtils.writeJson(response, msg);
        }
        return WebUtils.NULLActionForward;
    }

    private ActionForward executeStruts(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                        HttpServletResponse response) {
        if (form == null) {
            return mapping.findForward(Constants.LOGIN_KEY);
        }

        try {
            return (ActionForward) invokeAction(getMethodName(request), null, mapping, form, request, response);
        } catch (Exception e) {
            LOGGER.error(e.getCause());
            try {
                LOGGER.error(JsonUtils.toString(form));
            } catch (Exception exception) {
                LOGGER.error("Failed to serialize form:" + form.getClass().getName());
            }
            request.setAttribute(Constants.MESSAGE_ERROR, ResponseMsgBuilder.buildErrorJsonMsg(e));
            try {
                return (ActionForward) invokeAction(inItMethod(), null, mapping, form, request, response);
            } catch (Exception se) {
                LOGGER.error(e.getCause());
                return (mapping.findForward(Constants.ERROR_KEY));
            }

        }
    }

    private String getMethodName(HttpServletRequest request) {
        String methodName = request.getParameter("action");
        methodName = StringUtils.isEmpty(methodName) ? inItMethod() : methodName;
        return methodName;
    }

    /**
     * 执行Ajax请求
     *
     * @return
     */
    private ActionForward executeAjax(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                      HttpServletResponse response) {
        String msg = null;
        try {
            msg = getMsg4Request(request.getInputStream());
            Assert.isFalse(StringUtils.isEmpty(msg),
                           Errors.create().key(MessageIdList.SYSTEM_MISSING_JSON).content("请求中没有json数据!").build());
            Request msgReq = JsonUtils.toObject(msg, Request.class);

            RequestHeader header = msgReq.getHead();

            verificationParameter(header, request);
            String methodName = header.getMethod();

            try {
                Object result = invokeAction(methodName, msg, mapping, form, request, response);

                if (result == null) {
                    WebUtils.writeJson(response, ResponseMsgBuilder.buildSuccessJsonMsg(header));
                } else if (result instanceof ActionForward) {
                    return (ActionForward) result;
                } else {
                    WebUtils.writeJson(response, ResponseMsgBuilder.buildSuccessJsonMsg(result, header));
                }
            } catch (Exception e) {
                LOGGER.error(msg);
                LOGGER.error(e.getCause());
                msg = ResponseMsgBuilder.buildErrorJsonMsg(e);
                WebUtils.writeJson(response, msg);
            }

        } catch (Exception e) {
            LOGGER.error(msg);
            LOGGER.error(e);
            msg = ResponseMsgBuilder.buildErrorJsonMsg(e);
            WebUtils.writeJson(response, msg);
        }
        return WebUtils.NULLActionForward;
    }

    /**
     * 判断是否在白名单
     *
     * @param request
     * @return 存在: true ,不存在:false
     */
    private boolean isWhitelisted(HttpServletRequest request) {
        boolean result = false;
        String[] excludes = {"login.do", "i18nService.do", "/actuator/health"};
        String[] requestPath = StringUtils.split(request.getServletPath(), "/");
        result = Arrays.asList(excludes).contains(requestPath[requestPath.length - 1]);
        return result;
    }

    /**
     * 校验 msg中head的数据
     *
     * @param header requestHead
     */
    private void verificationParameter(RequestHeader header, HttpServletRequest request) {
        String userId = header.getUserId();
        long facility = header.getFacility();
        String methodName = header.getMethod();
        String transactionId = header.getTransactionId();

        if (!isWhitelisted(request)) {
            Assert.state(StringUtils.isNotEmptyTrim(userId),
                         Errors.create().key(MessageIdList.SYSTEM_MISSING_USERID).content("缺少请求用户信息!").build());
            Assert.state(facility > 0,
                         Errors.create().key(MessageIdList.SYSTEM_MISSING_FACILITY).content("缺少请求区域信息!").build());
        }

        Assert.state(StringUtils.isNotEmptyTrim(methodName),
                     Errors.create().key(MessageIdList.SYSTEM_MISSING_METHOD).content("缺少请求方法!").build());
        Assert.state(StringUtils.isNotEmptyTrim(transactionId),
                     Errors.create().key(MessageIdList.SYSTEM_MISSING_TRANSACTION).content("缺少请求transactionId!")
                           .build());

    }

    /**
     * 从request中提取json消息
     */
    private String getMsg4Request(InputStream inputRequest) throws Exception {

        StringBuilder jsonMsg = new StringBuilder();
        String readerLine = null;
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputRequest))) {
            while (StringUtils.isNotEmpty(readerLine = reader.readLine())) {
                jsonMsg.append(readerLine).append("/n");
            }
        }
        return jsonMsg.toString();

    }

    /**
     * 反射action中具体的业务方法
     *
     * @param methodName 业务方法名
     * @param jsonData   json数据
     * @return 业务处理结果
     */
    private Object invokeAction(String methodName, String jsonData, ActionMapping mapping, ActionForm form,
                                HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 检测url 合法
        checkUrl(request);

        Method method = getMethod(methodName);
        Assert.state(method != null, Errors.create().key(MessageIdList.SYSTEM_INVALID_METHOD).content("执行方法无效,{}")
                                           .args(String.valueOf(methodName)).build());

        List<Class<?>> types = Arrays.asList(method.getParameterTypes());
        Assert.state(types.size() <= (StringUtils.isNotEmpty(jsonData) ? 1 : 0) + 4,
                     Errors.create().key(MessageIdList.SYSTEM_INVALID_METHOD).content("执行方法无效,{}").build());

        List<Object> parameters = new ArrayList<>();
        for (Class<?> type : types) {
            if (type.isInstance(request)) {
                parameters.add(request);
            } else if (type.isInstance(response)) {
                parameters.add(response);
            } else if (type.isInstance(mapping)) {
                parameters.add(mapping);
            } else if (type.isInstance(form) && StringUtils.isEmpty(jsonData)) {
                filterSpecialChar(form);
                parameters.add(form);
            } else {
                Object body = JsonUtils.getValue2Bean(jsonData, type, "body");
                filterSpecialChar(body);
                parameters.add(body);
            }
        }

        return method.invoke(this, parameters.toArray());
    }


    final private Function<String,ReferenceFileDetail> queryConfigRegex = (String key1)->{
        List<ReferenceFileDetail>  referenceFileDetails= Optional.ofNullable( sysService.getRefFileValues("$$SYS_PARAMETER", LocalContext.getFacilityRrn())).orElse(Arrays.asList());
        Supplier<ReferenceFileDetail> getDefaultConfig = ()->{//系统缺省配置
            ReferenceFileDetail referenceFileDetail =  new ReferenceFileDetail();
            referenceFileDetail.setKey1Value("JAVA");
            //默认正则
            referenceFileDetail.setData1Value(SystemConstant.Str.SPECIAL_CHAR);
            //英文提示
            referenceFileDetail.setData2Value("The characters must be letters or digits or hyphens(-) or underscores(_) or at(@) or slash(/) or dollar($) or percent(%) or comma(.) or brackets(()) or question mark(?)");
            //中文提示
            referenceFileDetail.setData3Value("字符必须是字母或数字或连字符 (-) 或下划线 (_) 或 at(@) 或斜杠 (/) 或美元 ($) 或百分比 (%) 或英文小数点(.) 或括号(()) 或问号(?)");
            return referenceFileDetail;
        };
        //库中得配置如果查询不到,则使用代码中得缺省配置
        return referenceFileDetails.stream().filter(e->StringUtils.equalsIgnoreCase("JAVA",e.getKey1Value())).findFirst().orElseGet(getDefaultConfig);
    };


    protected void filterSpecialChar(Object bean){
        //for循环防止频繁查询
        ReferenceFileDetail referenceFileDetail = null;
        try {
            Class<?> clazz = bean.getClass();
            for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
                Field[] field = clazz.getDeclaredFields();
                //目前属性中字段的各种检查只有instanceId
                //找到instanceId 后检查,直接return
                //如果后面扩展为一个对象多个字段检查,则需要缓存系统中配置的正则
                for (Field f : field) {
                    PropertyDescriptor pd = null;
                    try {
                        pd = new PropertyDescriptor(f.getName(), clazz);
                    }catch (IntrospectionException e) {
                        continue;
                    }
                    Method readMethod = pd.getReadMethod();
                    if(readMethod == null){
                        continue;
                    }
                    if(bean instanceof  RootForm){
                        RootForm rootForm = (RootForm)bean;
                        if(Optional.ofNullable(rootForm.getSkipIdCheck()).orElse(false)){//跳过检查属性
                            return;
                        }
                    }
                    Object invoke = readMethod.invoke(bean);
                    SpecialCharValidater annotation = f.getAnnotation(SpecialCharValidater.class);
                    if(annotation != null && invoke instanceof String && StringUtils.isNotEmpty((String)invoke)){
                        String regexStr = annotation.value();
                        //功能模块特殊配置得正则 验证
                        if(StringUtils.isNotBlank(regexStr)){
                            Assert.state(Pattern.matches(regexStr,(String)invoke),
                                         Errors.create().key(annotation.msgId())
                                               .content("The characters must be letters or digits or hyphens(-) or underscores(_) or at(@) or slash(/) or dollar($) or percent(%) or comma(.) or brackets(()) or question mark(?)").args(invoke).build());
                            return;
                        }
                        //系统全局配置得缺省正则
                        if(referenceFileDetail==null){
                            referenceFileDetail=queryConfigRegex.apply("JAVA");//初始化类表中配置
                        }
                        String language = I18nUtils.getCurrentLanguage().name();
                        String content = StringUtils.equalsIgnoreCase(language,"EN")?referenceFileDetail.getData2Value(): referenceFileDetail.getData3Value();
                        Assert.state(Pattern.matches(referenceFileDetail.getData1Value(),(String)invoke),
                                     Errors.create().content(content).args(invoke).build());
                        return;
                    }
                }
            }
        } catch (InvocationTargetException | IllegalAccessException e) {
            LOGGER.error(e);
        }
    };

    /**
     * 拿到当前类中的方法
     *
     * @param methodName 方法名
     * @return Method
     */
    private Method getMethod(String methodName) {
        Method[] methods = this.getClass().getMethods();
        if (ArrayUtils.isNotEmpty(methods)) {
            for (Method method : methods) {
                if (method.getName().equalsIgnoreCase(methodName)) {
                    return method;
                }
            }
        }
        return null;
    }

    /**
     * 判断ajax请求
     *
     * @return
     */
    private boolean isAjax(HttpServletRequest request) {
        return (request.getHeader("X-Requested-With") != null &&
                "XMLHttpRequest".equals(request.getHeader("X-Requested-With")));
    }

    /**
     * @param optionKeys
     * @return
     */
    private List buildComboxDatasKey(Collection optionKeys) {
        List comboxData = new ArrayList();
        for (Object optionKey : optionKeys) {
            Map<String, Object> jsonObject = new HashMap<>();
            String[] _temp = (String[]) optionKey;
            String key = _temp[0];
            jsonObject.put("key", key);
            jsonObject.put("value", key);
            comboxData.add(jsonObject);
        }
        return comboxData;
    }

    /**
     * @param optionValues
     * @return
     */
    protected List buildComboxDatas(Collection optionValues) {
        List comboxData = new ArrayList();
        for (Object optionValue : optionValues) {
            Map<String, Object> jsonObject = new HashMap<>();
            String[] _temp = (String[]) optionValue;
            String key = _temp[0];
            String value = _temp[1];
            jsonObject.put("key", key);
            jsonObject.put("value", value);
            comboxData.add(jsonObject);
        }
        return comboxData;
    }

    /**
     * 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
     * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
     * 192.168.1.100 用户真实IP为: 192.168.1.110
     *
     * @return
     */
    protected String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    protected void doCopy(RootForm theForm, NamedObject instance) throws Exception {
        String origId = theForm.getInstanceId();

        instance = getInstance(instance);
        // populate the new information thru the new instance id
        PropertyUtils.copyProperties(theForm, instance);

        // and restore the original Id
        theForm.setInstanceId(origId);
    }

    protected NamedObject getInstance(NamedObject instance) {
        return instance;
    }

    protected long getInstanceRrn(String instanceId, String namedSpace, String object, String objectType) {
        if (instanceId != null) {
            return baseService.getNamedObjectRrn(instanceId.toUpperCase(), namedSpace, object, objectType);
        } else {
            return 0;
        }
    }

    protected long getInstanceRrn(String instanceId, String namedSpace, String object, String objectType,
                                  String subType) {
        if (instanceId != null) {
            return baseService.getNamedObjectRrn(instanceId.toUpperCase(), namedSpace, object, objectType, subType);
        } else {
            return 0;
        }
    }

    protected String getInstanceDesc(long instanceRrn) {
        return baseService.getInstanceDesc(instanceRrn);
    }

    protected Map<String,String> getInstanceDesc(Set<String> instanceIds,String namedSpace,String object){
        return baseService.getInstanceDesc(instanceIds,namedSpace,object);
    }

    /**
     * @param referenceFileId
     * @param key1Value
     * @param facilityRrn
     * @return
     */
    protected ReferenceFileDetail getReferenceFileDetail(String referenceFileId, String key1Value, Long facilityRrn) {
        return getReferenceFileDetail(referenceFileId, key1Value, " ", facilityRrn);
    }

    protected List<ReferenceFileDetail> getReferenceFileDetails(String referenceFileId) {
        long namedObjectRrn = baseService.getNamedObjectRrn(referenceFileId,
                                                            getNamedSpace(ObjectList.REFERENCE_FILE_KEY,
                                                                          LocalContext.getFacilityRrn()),
                                                            ObjectList.REFERENCE_FILE_KEY);
        List<ReferenceFileDetail> referenceFileDetails = sysService.getReferenceFileDetails(namedObjectRrn);
        return referenceFileDetails;
    }

    protected ReferenceFileDetail getReferenceFileDetail(String referenceFileId, String key1Value, String key2Value,
                                                         Long facilityRrn) {
        ReferenceFileDetail referenceFileDetail = new ReferenceFileDetail(referenceFileId,
                                                                          getNamedSpace(ObjectList.REFERENCE_FILE_KEY,
                                                                                        facilityRrn),
                                                                          ObjectList.REFERENCE_FILE_KEY);
        referenceFileDetail.setKey1Value(key1Value);
        referenceFileDetail.setKey2Value(key2Value);
        referenceFileDetail = sysService.getReferenceFileDetail(referenceFileDetail);
        return referenceFileDetail;
    }

    protected String getNamedSpace(String type, Long facility) {
        return baseService.getNamedSpace(facility, type);
    }

    protected String getEcnFlag() {
        Facility facility = sysService.getFacility(LocalContext.getFacilityRrn());
        return facility.getEcnFlag() != null ? facility.getEcnFlag() : "0";
    }

    protected String getInstanceId(long instanceRrn) {
        return baseService.getNamedObjectId(instanceRrn);
    }

    protected long getInstanceRrn(String instanceId, Long facilityRrn, String object) {
        String namedSpace = baseService.getNamedSpace(facilityRrn, object);
        return getInstanceRrn(instanceId, namedSpace, object);
    }

    protected long getInstanceRrn(String instanceId, String namedSpace, String object) {
        if (instanceId != null) {
            return baseService.getNamedObjectRrn(instanceId.toUpperCase(), namedSpace, object);
        } else {
            return 0;
        }
    }

    protected void processItemAction(HttpServletRequest request, RootForm theForm) {
        if (theForm.getPages() == null) {
            theForm.setPages(1);
        }

        if (request.getParameter(Constants.FIRST_KEY) != null) {
            theForm.setPages(1);
        } else if (request.getParameter(Constants.PREV_KEY) != null) {
            theForm.setPages(theForm.getPages() - 1);
        } else if (request.getParameter(Constants.NEXT_KEY) != null) {
            theForm.setPages(theForm.getPages() + 1);
        } else if (request.getParameter(Constants.LAST_KEY) != null) {
            theForm.setPages(-1);
        } else {
            theForm.setPages(1);
        }

    }

    protected void registerOptionType(Long facilityRrn, Collection types, HttpServletRequest request) {
        Map<String, Object> reponseData = new HashMap<>();
        for (Object o : types) {
            String type = (String) o;
            Collection optionValues = getOptionTypeList(facilityRrn, type);
            if (optionValues != null) {
                List comboxData;
                if (StringUtils.equals(type, "PROCESSLOCATION")) {// PROCESSLOCATION显示KEY,value由于使用判断
                    comboxData = buildComboxDatasKey(optionValues);
                } else {
                    comboxData = buildComboxDatas(optionValues);
                }

                reponseData.put(type, comboxData);
            }
        }
        put("COMBOXDATAS", JsonUtils.toString(reponseData), request);
    }

    protected Collection getOptionTypeList(Long facilityRrn, String type) {
        OptionTypeList option = new OptionTypeList(type);
        String where = option.getWhere();
        if (option.getRefFile() != null) {
            if (where != null) {
                where = " NAMED_SPACE=coalesce((SELECT NAMED_SPACE FROM NAMED_SPACE_DEFINITION WHERE " +
                        "FACILITY_RRN=" + facilityRrn + " AND OBJECT='REFERENCEFILE'),'MYCIM2') AND " + where;
            } else {
                where = " NAMED_SPACE=coalesce((SELECT NAMED_SPACE FROM NAMED_SPACE_DEFINITION WHERE " +
                        "FACILITY_RRN=" + facilityRrn + " AND OBJECT='REFERENCEFILE'),'MYCIM2')";
            }
        }

        Collection optionValues = sysService
                .getOption(option.getValue(), option.getText(), option.getTable(), option.getRefFile(), where);
        return optionValues;
    }

    protected void put(String name, Object value, HttpServletRequest request) {
        request.setAttribute(name, value);
    }

    protected String getObjectValue(long instanceRrn, String fieldId) {
        String result = sysService.getObjectAttributeValue(instanceRrn, 1, getInstanceRrn(fieldId, getNamedSpace(
                ObjectList.FIELD_KEY, LocalContext.getFacilityRrn()), ObjectList.FIELD_KEY));
        return StringUtils.trimToEmpty(result);
    }


    protected <T extends NamedObject> T setDefaultParam(NamedObject namedObject) {
        boolean isCreate = Constants.CREATE_KEY.equalsIgnoreCase(namedObject.getTransId()) ||
                Constants.ADD_KEY.equalsIgnoreCase(namedObject.getTransId());
        namedObject.setCreatedUserRrn(isCreate ? LocalContext.getUserRrn() : null);
        namedObject.setFacilityRrn(isCreate ? LocalContext.getFacilityRrn() : 0L);
        namedObject.setLastUpdateUserRrn(LocalContext.getUserRrn());
        return (T) namedObject;
    }

    protected <T extends ActionForm> T getForm(ActionForm actionForm){
        return (T) actionForm;
    }

    /**
     * 验证特殊字符
     * @param inputStr
     * @return
     */
    protected String validateSpecialChar(String inputStr) {
        if (!Pattern.matches(SystemConstant.Str.SPECIAL_CHAR, inputStr)) {
            String message = I18nUtils.getMessage(MessageIdList.SYSTEM_ALLOW_SPECIAL_CHAR_WITH_KEY,
                                                  "({}) The characters must be letters or digits or hyphens(-) or " +
                                                          "underscores(_) or at(@) or slash(/) or dollar($) or percent(%) or comma(.)",
                                                  inputStr);
            return message;
        }
        return StringUtils.EMPTY;
    }

    protected boolean isSpecialRole() {
        return Boolean.parseBoolean(LocalContext.get(SystemConstant.Str.SPECIAL_ROLE));
    }

}