diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileDownloadController.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileDownloadController.java index c8401d6..13a3bb3 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileDownloadController.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileDownloadController.java @@ -41,7 +41,7 @@ public class FileDownloadController { @RequestParam(value = "absolutePath", required = false) Boolean absolutePath, HttpServletRequest request ) throws FileNotFoundException { - String filePath = request.getRequestURI().substring(10); + String filePath = request.getServletPath().substring(10); return download(filePath, name, noCache,absolutePath, request); } @@ -91,7 +91,7 @@ public class FileDownloadController { @RequestParam(value = "noCache", required = false) Boolean noCache, @RequestHeader(value = "If-Modified-Since", required = false) String ifModifiedSince, HttpServletRequest request) throws FileNotFoundException { - String filePath = request.getRequestURI().substring(11); + String filePath = request.getServletPath().substring(11); HttpHeaders headers = new HttpHeaders(); diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmConst.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmConst.java index 40217f5..bb80bc4 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmConst.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmConst.java @@ -12,5 +12,9 @@ public interface BpmConst { interface DataRight { public final static Long CUR_VALUE = 999L;//本机构,本部门 public final static Long ALL_DEPT = 998L;//全部门权限 + public final static int V_SELF = 0;//本级 + public final static int V_INCLUDE_CHILDREN = 1;//含下级 + public final static int V_INCLUDE = 0;//包含 + public final static int V_EXCEPT = 1;//排除 } } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmEnum.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmEnum.java index b19f7c9..2489030 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmEnum.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/common/BpmEnum.java @@ -29,6 +29,8 @@ public interface BpmEnum { public static MenuFuncRight instance = new MenuFuncRight(); public static StrEnumBean READ = instance.addEnum("read", "查看"); public static StrEnumBean WRITE = instance.addEnum("write", "修改"); + public static StrEnumBean EXPORT = instance.addEnum("export", "导出"); + public static StrEnumBean AUDIT = instance.addEnum("audit", "审核"); } class UserStatu extends IntEnum { diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/dynPage/DynPageHelper.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/dynPage/DynPageHelper.java index 5bb741b..b11d4f1 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/dynPage/DynPageHelper.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/dynPage/DynPageHelper.java @@ -12,11 +12,17 @@ import cc.smtweb.framework.core.db.vo.ModelTable; import cc.smtweb.framework.core.exception.BizException; import cc.smtweb.framework.core.exception.SwException; import cc.smtweb.framework.core.mvc.service.SqlNamedPara; +import cc.smtweb.framework.core.mvc.service.TreeHelper; import cc.smtweb.framework.core.util.MapUtil; import cc.smtweb.framework.core.util.NumberUtil; import cc.smtweb.framework.core.util.SqlUtil; import cc.smtweb.framework.core.util.StringUtil; import cc.smtweb.system.bpm.web.design.form.define.*; +import cc.smtweb.system.bpm.web.sys.user.dataRight.DataRightDefine; +import cc.smtweb.system.bpm.web.sys.user.dataRight.DataRightDefineCache; +import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.IDataRightHandler; +import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.IDataRightTreeFactory; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; @@ -31,6 +37,7 @@ import static cc.smtweb.framework.core.common.SwConsts.TOTAL_KEY; * Created by Akmm at 2022/4/23 10:01 * 动态页面辅助类 */ +@Slf4j public class DynPageHelper { /** * 新建bean @@ -137,29 +144,50 @@ public class DynPageHelper { buildSelectSql(dataSet, params, sqlNamedPara, null); return sqlNamedPara; } + private static Map mapBuilder; + private static IBuilderExpr baseBuilder; + static { + baseBuilder = (opt, field, name, value, args, filter) -> { + args.put(name, value); + return field + " " + opt + " :" + name; + }; + mapBuilder = new HashMap<>(); + mapBuilder.put(SwEnum.OptType.LIKE.value, (opt, field, name, value, args, filter) -> { + args.put(name, "%" + value + "%"); + return field + " like :" + name; + }); - /** - * 构建合计栏sql - * - * @param dataSet - * @param params - * @return - */ - public static SqlNamedPara buildSumSql(PageDataset dataSet, @NonNull SqlNamedPara sqlNamedPara) { - StringBuilder sql = new StringBuilder(256); - sql.append("select count(1) " + TOTAL_KEY); - for (PageDatasetField field : dataSet.fields) { - if (StringUtils.isEmpty(field.summary)) continue; - if (field.fieldIsCalc()) continue; - sql.append(","); - if (!SwEnum.SummaryType.instance.isText(field.summary)) { - sql.append(field.summary).append("(").append(field.name).append(") ").append(field.name); - } else { - sql.append("'").append(field.summary).append("' ").append(field.name); + mapBuilder.put(SwEnum.OptType.PLIKE.value, (opt, field, name, value, args, filter) -> { + args.put(name, value + "%"); + return field + " like :" + name; + }); + + mapBuilder.put(SwEnum.OptType.BT.value, (opt, field, name, value, args, filter) -> { + String[] ss = value.toString().split(","); + if (ss.length != 2) throw new BizException("介于条件,参数值个数错误!"); + args.put(name + "_1", ss[0]); + args.put(name + "_2", ss[1]); + return "(" + field + ">=:" + name + "_1 and " + field + "<=:" + name + "_2)"; + }); + + mapBuilder.put(SwEnum.OptType.IN.value, (opt, field, name, value, args, filter) -> { + if (StringUtil.isEmpty(value.toString())) return ""; + args.put(name, value); + return field + " in (:" + name + ") "; + }); + + mapBuilder.put(SwEnum.OptType.NOT_IN.value, (opt, field, name, value, args, filter) -> { + if (StringUtil.isEmpty(value.toString())) return ""; + args.put(name, value); + return field + " not in (:" + name + ") "; + }); + mapBuilder.put(SwEnum.OptType.IN_CHILD.value, (opt, field, name, value, args, filter) -> { + ModelTable table = ModelTableCache.getInstance().get(filter.link); + if(table==null){ + return ""; } - } - sqlNamedPara.sql = sql.toString() + " from (" + sqlNamedPara.sql + ") xxxxa"; - return sqlNamedPara; + return TreeHelper.getTreeHelper(table.getName()).buildLikeFrSql(Long.parseLong(value.toString()),field); + }); } private static String buildSelFieldsSql(PageDataset dataSet, SqlNamedPara sqlNamedPara, IBuildSqlListener listener) { @@ -189,6 +217,30 @@ public class DynPageHelper { } /** + * 构建合计栏sql + * + * @param dataSet + * @param sqlNamedPara + * @return + */ + public static SqlNamedPara buildSumSql(PageDataset dataSet, @NonNull SqlNamedPara sqlNamedPara) { + StringBuilder sql = new StringBuilder(256); + sql.append("select count(1) " + TOTAL_KEY); + for (PageDatasetField field : dataSet.fields) { + if (StringUtils.isEmpty(field.summary)) continue; + if (field.fieldIsCalc()) continue; + sql.append(","); + if (!SwEnum.SummaryType.instance.isText(field.summary)) { + sql.append(field.summary).append("(").append(field.name).append(") ").append(field.name); + } else { + sql.append("'").append(field.summary).append("' ").append(field.name); + } + } + sqlNamedPara.sql = sql.toString() + " from (" + sqlNamedPara.sql + ") xxxxa"; + return sqlNamedPara; + } + + /** * 构建where条件:组合固定和动态条件 * * @param dataSet @@ -218,6 +270,12 @@ public class DynPageHelper { args.put(s, MapUtil.readString(params, s, "")); } if (listener != null) listener.buildWhere(dataSet, sql, args); + // 设置固定条件数据权限 + for (PageDatasetFilter filter : dataSet.filters) { + if(!setFixedFilter.contains(filter.name))break; + if(filter.dataRightType<=0L)break; + buildDataRight(filter.dataRightType,filter.sqlName,MapUtil.readString(params, filter.name, ""),sql,args); + } return new SqlNamedPara(sql.toString(), args); } @@ -265,6 +323,11 @@ public class DynPageHelper { value = params.get(filter.name); } if (value == null || StringUtils.isEmpty(value.toString())) { + if(filter.dataRightType>0L){ + StringBuilder frSql = new StringBuilder(); + buildDataRight(filter.dataRightType,filter.sqlName,value.toString(),frSql,args); + return frSql.toString(); + } if (filter.required) { throw new BizException("过滤条件不能为空(" + filter.name + ")!"); } @@ -272,9 +335,21 @@ public class DynPageHelper { } IBuilderExpr builder = getBuilder(dynCond.opt); String ns = isNameSelf ? filter.name: filter.name + "_" + dynCond.hashCode(); - return builder.build(dynCond.opt, filter.sqlName, ns, value, args); + String optWhere = builder.build(dynCond.opt, filter.sqlName, ns, value, args,filter); + // 添加数据权限 + if(filter.dataRightType>0L){ + StringBuilder frSql = new StringBuilder(); + buildDataRight(filter.dataRightType,filter.sqlName,value.toString(),frSql,args); + if(frSql.length()>0){ + if(StringUtil.isEmpty(optWhere)){ + optWhere = frSql.toString(); + }else { + optWhere+= " and "+ frSql.toString(); + } + } + } + return optWhere; } - /** * 处理计算字段 * @@ -289,44 +364,24 @@ public class DynPageHelper { } } - private static Map mapBuilder; - private static IBuilderExpr baseBuilder; - - static { - baseBuilder = (opt, field, name, value, args) -> { - args.put(name, value); - return field + " " + opt + " :" + name; - }; - mapBuilder = new HashMap<>(); - mapBuilder.put(SwEnum.OptType.LIKE.value, (opt, field, name, value, args) -> { - args.put(name, "%" + value + "%"); - return field + " like :" + name; - }); - - mapBuilder.put(SwEnum.OptType.PLIKE.value, (opt, field, name, value, args) -> { - args.put(name, value + "%"); - return field + " like :" + name; - }); - - mapBuilder.put(SwEnum.OptType.BT.value, (opt, field, name, value, args) -> { - String[] ss = value.toString().split(","); - if (ss.length != 2) throw new BizException("介于条件,参数值个数错误!"); - args.put(name + "_1", ss[0]); - args.put(name + "_2", ss[1]); - return "(" + field + ">=:" + name + "_1 and " + field + "<=:" + name + "_2)"; - }); - - mapBuilder.put(SwEnum.OptType.IN.value, (opt, field, name, value, args) -> { - if (StringUtil.isEmpty(value.toString())) return ""; - args.put(name, value); - return field + " in (:" + name + ") "; - }); - - mapBuilder.put(SwEnum.OptType.NOT_IN.value, (opt, field, name, value, args) -> { - if (StringUtil.isEmpty(value.toString())) return ""; - args.put(name, value); - return field + " not in (:" + name + ") "; - }); + /** + * 构建数据权限 + * @param dsType 数据权限定义ID + * @param sqlField sql字段名 + * @param value 值 + * @param sql sql语句 + * @param args 参数 + */ + private static void buildDataRight(long dsType,String sqlField,String value,StringBuilder sql,SwMap args){ + DataRightDefine drd = DataRightDefineCache.getInstance().get(dsType); + if(drd==null){return;} + try{ + IDataRightHandler dataRightHandler = IDataRightTreeFactory.getInstance().getHandler(drd.getCode()); + dataRightHandler.buildSqlWhere(true,sqlField,value,sql,args); + }catch (Exception e){ + log.error("加载数据权限失败:",e); + throw new SwException(e); + } } private static IBuilderExpr getBuilder(String opt) { @@ -335,7 +390,7 @@ public class DynPageHelper { } interface IBuilderExpr { - String build(String opt, String field, String name, Object value, Map args); + String build(String opt, String field, String name, Object value, Map args,PageDatasetFilter filter); } } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java index b9760c8..abc4c3f 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java @@ -28,6 +28,8 @@ public class FlowModelService extends LCSingleService { return new FlowModelSaveHandler<>(); case TYPE_MODEL_DEL: return new FlowModelDelHandler(); + case TYPE_FLOW: + return new FlowTransHandler(); } return super.createHandler(type); } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java index b8c3c16..10e5982 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java @@ -25,6 +25,7 @@ public class FlowLcLc1Service extends FlowModelService { case TYPE_MODEL_LOAD: case TYPE_MODEL_SAVE: case TYPE_MODEL_DEL: + case TYPE_FLOW: return super.createHandler(type); } return worker.createHandler(type); diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java index 5b495f5..3c7f460 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java @@ -22,6 +22,7 @@ public class FlowLcMsService extends FlowModelService { switch (type) { case TYPE_MODEL_LIST: case TYPE_MODEL_LOAD: + case TYPE_FLOW: return super.createHandler(type); case TYPE_MODEL_SAVE: return new FlowLcMsSaveHandler(); diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/login/AuthService.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/login/AuthService.java index 1759cdf..291e7d1 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/login/AuthService.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/login/AuthService.java @@ -61,6 +61,7 @@ public class AuthService { User user = UserCache.getInstance().get(mappingUserRet.getUserId()); if(user==null){ data.put("isOk", false); + data.put("extra", mappingUserRet.getExtra()); data.put("msg", "映射登录失败!"); return R.success(data); } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/AbsTreeDataRightHandler.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/AbsTreeDataRightHandler.java index a546398..57bbfa7 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/AbsTreeDataRightHandler.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/AbsTreeDataRightHandler.java @@ -1,5 +1,6 @@ package cc.smtweb.system.bpm.web.sys.user.dataRightGroup; +import cc.smtweb.framework.core.common.SwConsts; import cc.smtweb.framework.core.common.SwMap; import cc.smtweb.framework.core.session.UserSession; import cc.smtweb.framework.core.util.StringUtil; @@ -12,7 +13,7 @@ import java.util.Map; * 数据权限计算处理抽象类 */ public abstract class AbsTreeDataRightHandler implements IDataRightHandler{ - protected final static String menuIdKey = "_menuId"; + protected final static String KEY_HEADER_MENU = "hmk"; //权限查找结果常量定义 protected final static int RIGHT_RET_NONE = 0; //未找到 protected final static int RIGHT_RET_IN_CHILD = 1;//找到,包含,且含下级 @@ -35,11 +36,16 @@ public abstract class AbsTreeDataRightHandler implements IDataRightHandler{ } @Override public void init(UserSession us,SwMap params) { - this.isByRight = true; this.us = us; this.params = params; - String menuId = params.readString("_menuId"); + // 获取header中的菜单ID + SwMap header = params.readMap(SwConsts.PARAMS_HEADER_KEY); + long menuId = 0L; + if(header!=null){ + menuId = header.readLong(KEY_HEADER_MENU); + } if (us == null) return; + this.isByRight = true; mapRight = DataRightHelper.getDataRightItemMap(getDataRightType(), menuId, us); if (mapRight == null) return; //将本单位处理下 diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroup.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroup.java index 1624e97..3fc3112 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroup.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroup.java @@ -2,6 +2,12 @@ package cc.smtweb.system.bpm.web.sys.user.dataRightGroup; import cc.smtweb.framework.core.annotation.SwTable; import cc.smtweb.framework.core.db.impl.DefaultEntity; +import cc.smtweb.framework.core.util.CommUtil; +import cc.smtweb.framework.core.util.JsonUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** * Created by 1 at 2022-08-04 17:45:35 @@ -98,4 +104,18 @@ public class DataRightGroup extends DefaultEntity { public void setSdrdId(long sdrg_sdrd_id) { put("sdrg_sdrd_id", sdrg_sdrd_id); } + // 获取权限明细集合 + public List getDataRightItemList(){ + List rtList = new ArrayList<>(); + List> listItem = data.readListMap("sdrg_content"); + if(!CommUtil.isEmpty(listItem)){ + listItem.forEach(map-> { + DataRightGroupItem item = JsonUtil.parse(map,DataRightGroupItem.class); + if(item!=null){ + rtList.add(item); + } + }); + } + return rtList; + } } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroupItem.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroupItem.java index 6b0cb3e..21bfbb7 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroupItem.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightGroupItem.java @@ -1,6 +1,7 @@ package cc.smtweb.system.bpm.web.sys.user.dataRightGroup; import cc.smtweb.framework.core.db.impl.BaseBean; +import cc.smtweb.system.bpm.web.common.BpmConst; /** * @Author: tanghp @@ -31,4 +32,8 @@ public class DataRightGroupItem extends BaseBean { public void setType(int type) { put("type",type); } + // 包含 + public boolean isInclude(){ + return getKind() == BpmConst.DataRight.V_INCLUDE; + } } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightHelper.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightHelper.java index 9fb97df..defb44b 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightHelper.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/DataRightHelper.java @@ -1,8 +1,13 @@ package cc.smtweb.system.bpm.web.sys.user.dataRightGroup; import cc.smtweb.framework.core.session.UserSession; +import cc.smtweb.system.bpm.web.common.BpmConst; +import cc.smtweb.system.bpm.web.sys.user.role.RoleHelper; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Set; /** * @Author: tanghp @@ -18,7 +23,30 @@ public class DataRightHelper { * @param us 用户session * @return */ - public static Map getDataRightItemMap(String dType, String menuId, UserSession us ){ - return null; + public static Map getDataRightItemMap(String dType, long menuId, UserSession us ){ + Set setGroup = RoleHelper.getDataRightGroup(us.getUserId(), us.getPartyId(), menuId,dType); + //以value作为key + Map map = new HashMap<>(); + for (DataRightGroup group : setGroup) { + List items = group.getDataRightItemList(); + for (DataRightGroupItem item : items) { + if (!map.containsKey(item.getValue())) { + map.put(item.getValue(), item); + } + DataRightGroupItem i = map.get(item.getValue()); + if ((!i.isInclude() && item.isInclude()) || (i.isInclude() == item.isInclude() && item.getType() == BpmConst.DataRight.V_INCLUDE_CHILDREN)) { + map.put(item.getValue(), item); + } + } + } + // 如果没有配置数据权限,默认设置个本级及下级 + if(map.size()==0){ + DataRightGroupItem drgi = new DataRightGroupItem(); + drgi.setKind(BpmConst.DataRight.V_INCLUDE); + drgi.setType(BpmConst.DataRight.V_INCLUDE_CHILDREN); + drgi.setValue(BpmConst.DataRight.CUR_VALUE.toString()); + map.put(drgi.getValue(),drgi); + } + return map; } } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightHandler.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightHandler.java index fbfbab3..897478e 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightHandler.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightHandler.java @@ -12,5 +12,5 @@ import java.util.List; public interface IDataRightHandler { void init(UserSession us,SwMap params); //构建sql - boolean buildSqlWhere(boolean where, String sqlField, String value, StringBuilder sql, List args); + boolean buildSqlWhere(boolean where, String sqlField, String value, StringBuilder sql, SwMap args); } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightTreeFactory.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightTreeFactory.java index 1ed9369..3e65305 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightTreeFactory.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/IDataRightTreeFactory.java @@ -3,6 +3,7 @@ package cc.smtweb.system.bpm.web.sys.user.dataRightGroup; import cc.smtweb.framework.core.exception.SwException; import cc.smtweb.system.bpm.web.common.BpmEnum; import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.impl.DeptDataRightTreeWork; +import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.impl.PartyDataRightHandler; import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.impl.PartyDataRightTreeWork; import java.util.HashMap; @@ -27,11 +28,13 @@ public class IDataRightTreeFactory { } private Map> mapWorks; - + private Map> mapHandlers; private IDataRightTreeFactory() { mapWorks = new HashMap<>(); + mapHandlers = new HashMap<>(); mapWorks.put(BpmEnum.DataRightType.PARTY.value, PartyDataRightTreeWork.class); + mapHandlers.put(BpmEnum.DataRightType.PARTY.value, PartyDataRightHandler.class); mapWorks.put(BpmEnum.DataRightType.DEPT.value, DeptDataRightTreeWork.class); } @@ -40,4 +43,9 @@ public class IDataRightTreeFactory { if (cls == null) throw new SwException("暂不支持[" + key + "]类型权限!"); return cls.newInstance(); } + public IDataRightHandler getHandler(String key) throws Exception { + Class cls = mapHandlers.get(key); + if (cls == null) throw new SwException("暂不支持[" + key + "]类型权限!"); + return cls.newInstance(); + } } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/impl/PartyDataRightHandler.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/impl/PartyDataRightHandler.java index 70147cc..3d8d495 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/impl/PartyDataRightHandler.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/dataRightGroup/impl/PartyDataRightHandler.java @@ -1,13 +1,24 @@ package cc.smtweb.system.bpm.web.sys.user.dataRightGroup.impl; +import cc.smtweb.framework.core.common.SwConsts; +import cc.smtweb.framework.core.common.SwMap; +import cc.smtweb.framework.core.db.EntityHelper; +import cc.smtweb.framework.core.util.CommUtil; +import cc.smtweb.system.bpm.web.common.BpmConst; +import cc.smtweb.system.bpm.web.common.BpmEnum; import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.AbsTreeDataRightHandler; +import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.DataRightGroupItem; +import cc.smtweb.system.bpm.web.sys.user.party.Party; +import cc.smtweb.system.bpm.web.sys.user.party.PartyCache; -import java.util.*; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; /** * @Author: tanghp * @Date: 2022-09-22 15:17 - * @Desc: + * @Desc: 单位数据权限 */ public class PartyDataRightHandler extends AbsTreeDataRightHandler { @Override @@ -18,11 +29,225 @@ public class PartyDataRightHandler extends AbsTreeDataRightHandler { @Override protected String getDataRightType() { - return "partyId"; + return BpmEnum.DataRightType.PARTY.value; } @Override - public boolean buildSqlWhere(boolean where, String sqlField, String value, StringBuilder sql, List args) { + public boolean buildSqlWhere(boolean where, String sqlField, String value, StringBuilder sql, SwMap args) { return false; } + /** + * 根据菜单数据权限,找有权限的顶级机构列表 + * + * @return + */ + public Set getTopList() { + if (!isByRight || mapRight == null) { //不控制权限,直接取吧 + return PartyCache.getInstance().getChildren(SwConsts.DEF_ROOT_ID_LONG); + } + + Set list = new HashSet<>(); + if (mapRight.isEmpty()) return list; + Set setExists = new HashSet<>(); + for (DataRightGroupItem item : mapRight.values()) { + if (!item.isInclude()) continue; //不包含的对象,这里是计算根节点,不管就是了 + //本单位之类的已处理 + Party party = PartyCache.getInstance().get(item.getValue()); + if (party == null) continue; + boolean isParentExists = false; + //添加节点,注意:如果是已经添加了的单位的下级,则不要添加 + String[] pids = party.getLevelCode().split(SwConsts.SPLIT_CHAR); + for (String pid : pids) { + if (setExists.contains(pid)) { + isParentExists = true; + break; + } + } + if (!isParentExists) { + list.add(party); + setExists.add(String.valueOf(party.getEntityId())); + } + } + return list; + } + + /** + * 获取下级节点 + * + * @param parentId 上级机构id + * @return + */ + public Set getChildren(long parentId) { + Set children = PartyCache.getInstance().getChildren(parentId); + if (!isByRight || mapRight == null || CommUtil.isEmpty(children)) { //不控制权限,直接取吧 + return children; + } + + if (mapRight.isEmpty()) return null; + + children.removeIf(party -> !hasRight(party)); + return children; + } + + /** + * 是否拥有此单位权限 + * + * @param party + * @return + */ + private boolean hasRight(Party party) { + int nret = hasRight(party.getEntityId()); + if (nret != RIGHT_RET_NONE) return nret != RIGHT_RET_EX; + String[] pids = party.getLevelCode().split(SwConsts.SPLIT_CHAR); + for (String pid : pids) { + nret = hasRight(Long.parseLong(pid)); + if (nret == RIGHT_RET_IN_CHILD) return true; + if (nret == RIGHT_RET_IN_NOCHILD || nret == RIGHT_RET_EX) return false; + } + return false; + } + + private int hasRight(long id) { + if (mapRight == null || !mapRight.containsKey(String.valueOf(id))) return RIGHT_RET_NONE; + DataRightGroupItem item = mapRight.get(String.valueOf(id)); + if (item.isInclude()) { + if (item.getType()== BpmConst.DataRight.V_INCLUDE_CHILDREN) return RIGHT_RET_IN_CHILD; + return RIGHT_RET_IN_NOCHILD; + } + if (!item.isInclude()) return RIGHT_RET_EX; + return 0; + } + + + /** + * 判断childId是否是parentId的子孙 + * + * @param childId + * @param parentId + * @return + */ + public boolean isChild(long childId, long parentId) { + if (parentId<=0L) return true; + Party party = PartyCache.getInstance().get(childId); + if (party == null) return false; + String parentIdStr = SwConsts.SPLIT_CHAR+parentId+SwConsts.SPLIT_CHAR; + return (party.getLevelCode() + SwConsts.SPLIT_CHAR).contains(parentIdStr); + } + /** + * 获取查询语句,使用子查询的方式,数据权限越复杂,构建的语句就越复杂 + * + * @param where 是否带的有where了,确定是where还是and + * @param sqlField 查询的表的单位字段 + * @param partyId 查询条件机构的值,可以为空 + * @param qnext 是否带下级 注意:此方法仅单位管理使用。 + * @param includeSelf 是否包含自己 + * @param sql sql + * @param args 参数 + */ + public boolean buildSqlWhere(boolean where, String sqlField, long partyId, boolean qnext, + boolean includeSelf, StringBuilder sql, SwMap args) { + if (partyId>0L) {//前端页面传入了单位id + Party party = PartyCache.getInstance().get(partyId); + if (isByRight&&party==null) { + if (where) sql.append(" and "); + else sql.append(" where "); + sql.append(" 1 <> 1 "); + return true; + } + if (party != null) {//单位不存在,则默认查所有,押后 + int nret = hasRight(partyId); + if (nret == RIGHT_RET_IN_NOCHILD) {//不包含下级,则直接等于即可 + if (where) sql.append(" and "); + else sql.append(" where "); + sql.append(" ").append(sqlField).append(" =:_dr_partyId "); + args.put("_dr_partyId",partyId); + return true; + } + if (nret == RIGHT_RET_EX) {//不包含,直接用顶级单位的id来筛选,应该是无数据返回 + if (where) sql.append(" and "); + else sql.append(" where "); + sql.append(" 1 <> 1 "); + return true; + } + } else { + partyId = SwConsts.DEF_ROOT_ID_LONG; + } + } + + StringBuilder sqlAnd = new StringBuilder(128); + StringBuilder sqlOr = new StringBuilder(128); + + addPartySql(partyId, true, true, qnext, includeSelf, sqlAnd, sqlOr, args); + + if (mapRight != null) { + for (DataRightGroupItem item : mapRight.values()) { + long itemPartyId = Long.parseLong(item.getValue()); + if (item.getValue().equalsIgnoreCase(String.valueOf(partyId))) continue; + if (!isChild(itemPartyId, partyId)) continue; //非指定partyId的下级,不用搭理 + addPartySql(itemPartyId, item.isInclude(), item.getType() == BpmConst.DataRight.V_INCLUDE_CHILDREN, true, true, sqlAnd, sqlOr, args); + } + } + + if (sqlAnd.length() == 0 && sqlOr.length() == 0) { + return where; + } + + if (where) sql.append(" and "); + else sql.append(" where "); + + if (sqlOr.length() > 0) { + sql.append("\nEXISTS(\n" + + " SELECT pt_id FROM " + EntityHelper.getSchemaTableName(Party.ENTITY_NAME) + " r2 WHERE r2.party_id=" + sqlField); + sql.append(" and (").append(sqlOr.substring(4)).append("))"); + return true; + } + if (sqlAnd.length() > 0) { + sql.append("\nand not EXISTS(\n" + + " SELECT pt_id FROM " + EntityHelper.getSchemaTableName(Party.ENTITY_NAME)+ " r2 WHERE r2.party_id=" + sqlField); + sql.append(" and (").append(sqlAnd.substring(4)).append("))"); + return true; + } + return false; + + + } + private void addPartySql(long partyId, boolean isInclude, boolean hasChild, boolean qnext, boolean includeSelf, StringBuilder sqlAnd, StringBuilder sqlOr, SwMap args) { + if (partyId<=0L) return; + if (!hasChild && isInclude) {//不包含下级 + String p = String.format("_dr_pt_id_%s", new Random().nextInt(10)); + sqlOr.append(String.format(" or r2.pt_id=:%s",p)); + args.put(p,partyId); + return; + } + Party party = PartyCache.getInstance().get(partyId); + if (party == null) return; + if (isInclude) { + localBuildSqlPara(qnext, includeSelf, sqlOr, args, party); + } else { + localBuildSqlPara(qnext, includeSelf, sqlAnd, args, party); + } + } + + //构建子查询语句 + private void localBuildSqlPara(boolean qnext, boolean includeSelf, StringBuilder sqlOr, SwMap args, Party party) { + sqlOr.append(" or ( "); + if (includeSelf) { + String p = String.format("_dr_pt_id_%s", new Random().nextInt(10)); + sqlOr.append(String.format("(r2.pt_id=:%s or ",p)); + args.put(p,party.getId()); + } + if (qnext) { + String p = String.format("_dr_pt_level_code_%s", new Random().nextInt(10)); + sqlOr.append(String.format(" r2.pt_level_code like :%s",p)); + args.put(p,party.getLevelCode() + SwConsts.SPLIT_CHAR + party.getId() + "%"); + } else { + String p = String.format("_dr_pt_parent_id_%s", new Random().nextInt(10)); + sqlOr.append(String.format(" r2.pt_parent_id = :%s",p)); + args.put(p,party.getId()); + } + + if (includeSelf) sqlOr.append("))"); + else sqlOr.append(")"); + } + } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanCache.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanCache.java index 7677d93..a1ede39 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanCache.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanCache.java @@ -14,7 +14,6 @@ import java.util.Set; public class MenuPlanCache extends AbstractEntityCache { //缓存key:按项目缓存 public final static String mk_p = "p"; - public static MenuPlanCache getInstance() { return CacheManager.getIntance().getCache(MenuPlanCache.class); } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanHelper.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanHelper.java new file mode 100644 index 0000000..f070351 --- /dev/null +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanHelper.java @@ -0,0 +1,21 @@ +package cc.smtweb.system.bpm.web.sys.user.menuPlan; + +/** + * @Author: tanghp + * @Date: 2022-09-26 10:19 + * @Desc: 菜单方案辅助类 + */ +public class MenuPlanHelper { + /** + * 获取命中菜单方案明细ID + * @param menuId 菜单方案明细ID + * @param pageId 页面ID + * @param fullPath 带参数的路径 + * @return + */ + public long findMenuId(long menuId,long pageId,String fullPath){ + // + + return 0L; + } +} diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanItemProvider.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanItemProvider.java new file mode 100644 index 0000000..2cd16f8 --- /dev/null +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/menuPlan/MenuPlanItemProvider.java @@ -0,0 +1,45 @@ +package cc.smtweb.system.bpm.web.sys.user.menuPlan; + +import cc.smtweb.framework.core.cache.SessionCacheFactory; +import cc.smtweb.framework.core.util.CommUtil; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * @Author: tanghp + * @Date: 2022-09-26 11:12 + * @Desc: 菜单明细加载类 + */ +public class MenuPlanItemProvider { + private volatile MenuPlanItemProvider provider; + private Map map = new HashMap<>(); + private MenuPlanItemProvider() { + } + public MenuPlanItemProvider getInstance(){ + if (provider == null) { + synchronized (SessionCacheFactory.class) { + if (provider == null) { + provider = new MenuPlanItemProvider(); + } + } + } + return provider; + } + // 获取菜单方案明细ID + public MenuPlanItem getMenuPlanItem(long menuId){ + MenuPlanItem menuPlanItem = map.get(menuId); + if(menuPlanItem!=null)return menuPlanItem; + Collection plans = MenuPlanCache.getInstance().getAll(); + if(CommUtil.isEmpty(plans))return null; + for(MenuPlan menuPlan: plans){ + menuPlanItem = MenuPlanCache.getInstance().getById(menuPlan.getEntityId(),menuId); + if(menuPlanItem!=null){ + map.put(menuId,menuPlanItem); + return menuPlanItem; + } + } + return null; + } +} diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleHelper.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleHelper.java index a468715..91289d2 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleHelper.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleHelper.java @@ -1,5 +1,9 @@ package cc.smtweb.system.bpm.web.sys.user.role; +import cc.smtweb.system.bpm.web.sys.user.dataRight.DataRightDefine; +import cc.smtweb.system.bpm.web.sys.user.dataRight.DataRightDefineCache; +import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.DataRightGroup; +import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.DataRightGroupCache; import cc.smtweb.system.bpm.web.sys.user.menuPlan.MenuPlan; import cc.smtweb.system.bpm.web.sys.user.menuPlan.MenuPlanCache; import cc.smtweb.system.bpm.web.sys.user.menuPlan.MenuPlanContent; @@ -8,9 +12,7 @@ import cc.smtweb.system.bpm.web.sys.user.user.User; import cc.smtweb.system.bpm.web.sys.user.user.UserCache; import cc.smtweb.system.bpm.web.sys.user.user.UserRoleCache; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @Author: tanghp @@ -127,4 +129,54 @@ public final class RoleHelper { }); return mpList; } + + /** + * 获取用户指定类型的数据权限组 + * @param userId 用户ID + * @param partyId 登录的单位 + * @param menuId 菜单ID + * @param dsType 数据权限类型编码 + * @return 数据权限组集合 + */ + public static Set getDataRightGroup(long userId,long partyId,long menuId,String dsType){ + Set mpIds = getMenuPlanIds(userId,partyId); + if(mpIds.size()==0){ + return new HashSet<>(); + } + // 没传菜单方案ID,随机取一个菜单方案 + return getDataRightGroup(userId,partyId,menuId,dsType,mpIds.iterator().next()); + } + + /** + * 获取用户指定类型的数据权限组 + * @param userId 用户ID + * @param partyId 登录单位 + * @param dsType 数据权限类型编码 参见 BpmEnum.DataRightType + * @param menuPlanId 菜单方案id + * @return 据权限组集合 + */ + public static Set getDataRightGroup(long userId,long partyId,long menuId,String dsType,long menuPlanId){ + Map mapGroup = new HashMap<>(); + //先看数据权限类型存在不 + DataRightDefine drd = DataRightDefineCache.getInstance().getByCode(dsType); + if(drd==null)return new HashSet<>(); + + Set roleIds = UserRoleCache.getInstance().getRoleIdByUP(userId,partyId); + RoleCache roleCache = RoleCache.getInstance(); + roleIds.forEach((roleId)-> { + Role role = roleCache.get(roleId); + if(role==null || role.getSmpId()!=menuPlanId){ + return; + } + RoleRightContent roleRightContent = new RoleRightContent(role.getPrivilege()); + long groupId =roleRightContent.getDataRightGroupId(menuId,drd.getEntityId()); + if(groupId>0L){ + DataRightGroup dataRightGroup = DataRightGroupCache.getInstance().get(groupId); + if(dataRightGroup!=null){ + mapGroup.put(groupId,dataRightGroup); + } + } + }); + return new HashSet<>(mapGroup.values()); + } } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleRightContent.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleRightContent.java index b3b4cf3..cf10613 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleRightContent.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/role/RoleRightContent.java @@ -112,4 +112,25 @@ public class RoleRightContent { }); return cache; } + + /** + * 获取菜单的数据权限组ID + * @param menuId 菜单id + * @param dataRightId 数据权限定义id + * @return 数据权限组ID + */ + public long getDataRightGroupId(long menuId, long dataRightId){ + RoleRight right = getRoleRight(menuId); + List> list = right.getRightData(); + if(CommUtil.isEmpty(list))return 0L; + for(Map map :list){ + SwMap swMap = new SwMap(); + swMap.putAll(map); + long key = swMap.readLong("key"); + if(key ==dataRightId){ + return swMap.readLong("value"); + } + } + return 0L; + } } diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl index 9529067..546828d 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl @@ -34,7 +34,21 @@ <#if fields.lookup??> "lookup": { <#list fields.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_dialog.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_dialog.ftl index b48748e..1985f26 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_dialog.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_dialog.ftl @@ -25,9 +25,23 @@ "required": "${dfield.required}", <#if dfield.lookup??> "lookup": { - <#list dfield.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, - + <#list dfield.lookup as k, v> + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, + }, <#if dfield.maxlength gt 0> diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_query.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_query.ftl index a2053a2..1275371 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_query.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_list_query.ftl @@ -33,7 +33,21 @@ <#if filter.lookup??> "lookup": { <#list filter.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_query_sfilters.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_query_sfilters.ftl index 2af9c8e..4d2316e 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_query_sfilters.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_query_sfilters.ftl @@ -59,9 +59,23 @@ "field": "${filter.id}", <#if filter.lookup??> "lookup": { - <#list filter.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, - + <#list filter.lookup as k, v> + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, + }, "name": "${filter.name}" diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl index b0097a1..f1b3eef 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl @@ -184,7 +184,21 @@ <#if fields.lookup??> "lookup": { <#list fields.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, @@ -293,7 +307,21 @@ <#if fields.lookup??> "lookup": { <#list fields.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl index b4de32d..e3b246d 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl @@ -80,7 +80,21 @@ <#if field.lookup??> "lookup": { <#list field.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl index e4cc8b2..c322324 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl @@ -165,7 +165,21 @@ <#if field.lookup??> "lookup": { <#list field.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl index 6547a5d..682c99c 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl @@ -168,7 +168,21 @@ <#if dfield.lookup??> "lookup": { <#list dfield.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/core/pom.xml b/smtweb-framework/core/pom.xml index 4e524ba..1ff619f 100644 --- a/smtweb-framework/core/pom.xml +++ b/smtweb-framework/core/pom.xml @@ -168,6 +168,16 @@ spring-boot-starter-test test + + org.apache.poi + poi + 5.2.2 + + + org.apache.poi + poi-ooxml + 5.2.2 + diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwConsts.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwConsts.java index 8a191ff..b3f06be 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwConsts.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwConsts.java @@ -62,4 +62,6 @@ public interface SwConsts { //定时任务key String REDIS_KEY_BASE_JOB = "jobexe"; + // params 参数中的header key + String PARAMS_HEADER_KEY ="_header"; } diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwEnum.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwEnum.java index 795d691..375eb87 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwEnum.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwEnum.java @@ -314,6 +314,7 @@ public interface SwEnum { public static StrEnumBean LIKE = instance.addEnum("like", "包含"); public static StrEnumBean IN = instance.addEnum("in", "在列表"); public static StrEnumBean NOT_IN = instance.addEnum("not in", "不在列表"); + public static StrEnumBean IN_CHILD = instance.addEnum("inChild", "本级及下级"); } //合计栏类型 "summary": "COUNT/SUM/AVG/MAX/MIN/其他为文本" diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/SwMapBodyEditor.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/SwMapBodyEditor.java index ae64f13..82f5b1c 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/SwMapBodyEditor.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/SwMapBodyEditor.java @@ -8,6 +8,7 @@ import cc.smtweb.framework.core.util.JsonUtil; import org.apache.commons.lang3.StringUtils; import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; import java.util.Map; /** @@ -32,6 +33,10 @@ public class SwMapBodyEditor implements IEditor { private Object getGetValue(String paramName, Class paramType, Map params, HttpServletRequest request) { SwMap result = JsonUtil.parseSimple(params, SwMap.class); + if(result!=null){ + SwMap header = getHeaderMap(request); + result.put("_header",header); + } request.setAttribute(BODY_MAP, result); return getFieldValue(result, paramName); } @@ -42,10 +47,25 @@ public class SwMapBodyEditor implements IEditor { } SwMap result = JsonUtil.parse(body, SwMap.class); + if(result!=null){ + SwMap header = getHeaderMap(request); + result.put("_header",header); + } request.setAttribute(BODY_MAP, result); return getFieldValue(result, paramName); } - + private SwMap getHeaderMap( HttpServletRequest request){ + SwMap header = new SwMap(); + Enumeration headerNames = request.getHeaderNames(); + if (null != headerNames) { + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + String headerValue = request.getHeader(headerName); + header.put(headerName,headerValue); + } + } + return header; + } private Object getFieldValue(SwMap result, String paramName) { if (paramName != null) { Object value = result.get(paramName); diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java index c64bf1b..3813a8f 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java @@ -54,6 +54,14 @@ public abstract class AbstractListHandler extends AbstractHandler { return R.success(buildListData()); } + /** + * 导出excel + * @return R:{ file: fileName@@filePath } + */ + public R exportExcel() { + return R.success(buildListData()); + } + public SwListData buildListData() { List listData; SqlPara sqlPara = buildDataSql(); diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/TreeHelper.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/TreeHelper.java index 029b469..2885fcb 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/TreeHelper.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/TreeHelper.java @@ -6,6 +6,7 @@ import cc.smtweb.framework.core.common.SwConsts; import cc.smtweb.framework.core.common.SwEnum; import cc.smtweb.framework.core.db.DbEngine; import cc.smtweb.framework.core.db.EntityDao; +import cc.smtweb.framework.core.db.EntityHelper; import cc.smtweb.framework.core.db.cache.ModelTableCache; import cc.smtweb.framework.core.db.impl.DefaultEntity; import cc.smtweb.framework.core.db.vo.ModelTable; @@ -36,6 +37,7 @@ public class TreeHelper { private String fieldParent; private String fieldLevel; private String fieldLevelCode; + private String fieldId; public static TreeHelper getTreeHelper(String tableName) { @@ -69,6 +71,7 @@ public class TreeHelper { fieldParent = table.findFieldNameByType(SwEnum.FieldType.PARENT_ID.value); fieldLevelCode = table.findFieldNameByType(SwEnum.FieldType.LEVEL_CODE.value); fieldLevel = table.findFieldNameByType(SwEnum.FieldType.LEVEL.value); + fieldId = table.getIdField(); } public long getParentId(T bean) { @@ -125,13 +128,24 @@ public class TreeHelper { } return list; } + // 拼接查询本级及下级sql片段 + public String buildLikeFrSql(long id,String sqlField){ + T bean = cache.get(id); + if(bean==null){ + return "1<>1"; + } + String childStr = "'"+getLevelCode(bean)+ SwConsts.SPLIT_CHAR+ bean.getEntityId()+"'"; + String schemaTableName = EntityHelper.getSchemaTableName(tableName); + return String.format(" exists ( select %s from %s tt where tt.%s = %s and (tt.%s=%s or tt.%s like %s))", + fieldId,schemaTableName,fieldId,sqlField,fieldId,id,fieldLevelCode,childStr); + } private void resetParentChildren(T bean, List list) { T parent = cache.get(getParentId(bean)); if (parent != null) { bean.put(fieldLevelCode, getLevelCode(parent) + SwConsts.SPLIT_CHAR + parent.getEntityId()); bean.put(fieldLevel, getLevel(parent) + 1); - dao.updateEntity(bean, fieldLevelCode, fieldLevel); + dao.updateEntity(bean, fieldLevelCode+","+fieldLevel); list.add(bean); } Collection children = getChildren(bean); diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/session/SessionUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/session/SessionUtil.java index 5d96948..28b4c29 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/session/SessionUtil.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/session/SessionUtil.java @@ -37,7 +37,6 @@ public class SessionUtil { //url参数名 private final static String KEY_PARAM_FP_KEY = "fpk"; private final static String KEY_PARAM_FP_VAL = "fpv"; - //不需要校验登录的url public static List notLoginUrl = new ArrayList<>(); /*//不需要切换数据源的url,强制用主库 diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelExportUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelExportUtil.java new file mode 100644 index 0000000..c6b3abd --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelExportUtil.java @@ -0,0 +1,1081 @@ +package cc.smtweb.framework.core.util; + +import cc.smtweb.framework.core.common.SwMap; +import org.apache.commons.io.IOUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFDataFormat; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellUtil; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import sun.misc.BASE64Decoder; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; + +/** + * Excel导出工具类 + */ +public class ExcelExportUtil { + public static Integer MAX_EXCEL_COUNT = 60000; + private static Map> cacheMaps = null; + + public static void clearCellStyleCache(Workbook web) { + if (cacheMaps != null) { + cacheMaps.remove(web); + } + } + + public static CellStyle createCellStyle(Workbook wb, short fontSize, boolean isBold, boolean hasBorder, HorizontalAlignment align) { + CellStyle cs = getCellStyle(wb, fontSize + "-" + isBold + "-" + hasBorder + "-" + align); + Font f = wb.createFont(); + f.setFontHeightInPoints(fontSize); + f.setBold(isBold); + cs.setFont(f); + if (hasBorder) { + cs.setBorderBottom(BorderStyle.THIN); + cs.setBorderLeft(BorderStyle.THIN); + cs.setBorderRight(BorderStyle.THIN); + cs.setBorderTop(BorderStyle.THIN); + } + cs.setAlignment(align); + cs.setVerticalAlignment(VerticalAlignment.CENTER); + cs.setWrapText(true); + return cs; + } + + private static CellStyle getCellStyle(Workbook wb, String key) { + if (cacheMaps == null) { + cacheMaps = new HashMap<>(); + } + if (cacheMaps.size() > 1000) { + cacheMaps = new HashMap<>(); + } + Map map = cacheMaps.computeIfAbsent(wb, k -> new HashMap<>()); + CellStyle cellStyle = map.get(key); + if (cellStyle == null) { + cellStyle = wb.createCellStyle(); + map.put(key, cellStyle); + } + return cellStyle; + } + + public static Row createRow(Sheet sheet, int rowNum, int rowHeight) { + Row row = sheet.getRow(rowNum); + if (row != null) return row; + row = sheet.createRow(rowNum); + if (rowHeight > 0) row.setHeightInPoints(rowHeight); + return row; + } + + public static Cell createCell(Row row, int column) { + Cell cell = row.getCell(column); + if (cell != null) return cell; + return row.createCell(column); + } + + public static void writeMergeCell(Sheet sheet, int rowHeight, int fromRow, int endRow, int fromCol, int entCol, CellStyle cs, String text) { + Row row = createRow(sheet, fromRow, rowHeight); + Cell cell = createCell(row, fromCol); + cell.setCellStyle(cs); + cell.setCellValue(text); + if (endRow != fromRow || entCol != fromCol) { + sheet.addMergedRegion(new CellRangeAddress(fromRow, endRow, fromCol, entCol)); + for (int i = fromRow; i <= endRow; i++) { + row = CellUtil.getRow(i, sheet); + for (int j = fromCol; j <= entCol; j++) { + cell = CellUtil.getCell(row, (short) j); + cell.setCellStyle(cs); + } + } + } + } + + public static void writeMergeCell(Sheet sheet, int fromRow, int endRow, int fromCol, int entCol, CellStyle cs, String text) { + writeMergeCell(sheet, 0, fromRow, endRow, fromCol, entCol, cs, text); + } + + /** + * @param row 行 + * @param colIndex 列号 + * @param cs 样式 + * @param text 内容 + * @param colType 列类型 + * @param ignoreZero 是否忽略零 + */ + public static void writeCell(Row row, int colIndex, CellStyle cs, String text, ColType colType, boolean ignoreZero) { + Cell cell = createCell(row, colIndex); + if (StringUtil.isNotEmpty(text)) text = text.replace(" ", " "); + if (ColType.AMOUNT.equals(colType)) {//金额 + if (ignoreZero && MathUtil.isEqualsZero(text)) cell.setCellValue(""); + else { + cell.setCellValue(StringUtil.getDoubleIgnoreErr(text)); + } + } else if (ColType.NUMBER.equals(colType)) {//数字 + final int v = StringUtil.getIntIgnoreErr(text); + if (ignoreZero && 0 == v) cell.setCellValue(""); + else { + cell.setCellValue(v); + } + } else {//文本 + cell.setCellValue(text); + } + cell.setCellStyle(cs); + } + + /** + * 根据前端传回的导出模型导出 + * + * @param modelJson 前端传回的导出模型 + * @throws Exception + */ + public static String exportToExcel(List> data, String modelJson) throws Exception { + return exportToExcel(data, modelJson, ""); + } + + public static String exportToExcel(List> data, String modelJson, Map mapMergeField) throws Exception { + SwMap model = new SwMap(); + model.putAll(JsonUtil.parseMap(modelJson)); + return exportToExcel(data, model, mapMergeField, null, (short) 9); + } + + public static String exportToExcel(List> data, SwMap model, short defFontSize) throws Exception { + return exportToExcel(data, model, null, defFontSize); + } + + public static String exportToExcel(List> data, String modelJson, String fileName) throws Exception { + SwMap model = new SwMap(); + model.putAll(JsonUtil.parseMap(modelJson)); + return exportToExcel(data, model, fileName, (short) 9); + } + + public static String exportToExcel(List> data, SwMap model, String fileName, short defFontSize) throws Exception { + return exportToExcel(data, model, null, fileName, defFontSize); + } + + public static String exportToExcel(List> data, SwMap model, Map mapMergeField, String fileName, short defFontSize) throws Exception { +// File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + // TODO: 2022/9/27 路径作测试用 + File tempFile = File.createTempFile("xls_", ".xls", new File("D:\\jjkj\\")); + try (FileOutputStream fs = new FileOutputStream(tempFile)) { + exportToExcel(fs, data, model, mapMergeField, defFontSize); + fs.flush(); + } + if (StringUtil.isEmpty(fileName)) fileName = model.readString("title"); + fileName = fileName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return fileName + "@@" + tempFile.getAbsolutePath(); + } + + public static int getStrLength(String text) { + if (StringUtil.isEmpty(text)) return 1; + if (!text.contains("\n")) return text.getBytes().length; + String[] ss = text.split("\n"); + int len = 0; + for (String s : ss) { + len = Math.max(len, s.getBytes().length); + } + return len; + } + + public static void exportToExcel(OutputStream os, List> data, SwMap model, Map mapMergeField, short defFontSize) throws Exception { + HSSFWorkbook wb = new HSSFWorkbook(); + int detSize = data.size(); + if (detSize == 0) { + Sheet sheet = wb.createSheet(); + exportToExcelEx(sheet, wb, data, model, mapMergeField, defFontSize); + } else { + int sheetCount = detSize % MAX_EXCEL_COUNT; + sheetCount = sheetCount != 0 ? detSize / MAX_EXCEL_COUNT + 1 : detSize / MAX_EXCEL_COUNT; + List listSheetName = new ArrayList<>(); + for (int i = 0; i < sheetCount; i++) { + List> listDetailPage = new ArrayList<>(); + int endIndex = (i + 1) * MAX_EXCEL_COUNT; + if (endIndex > detSize) { + endIndex = detSize; + } + for (int index = i * MAX_EXCEL_COUNT; index < endIndex; index++) { + listDetailPage.add(data.get(index)); + } + String sheetName = sheetCount + "-" + (i + 1); + listSheetName.add(sheetName); + Sheet sheet = wb.createSheet(sheetName); + exportToExcelEx(sheet, wb, listDetailPage, model, mapMergeField, defFontSize); + } + //删除非我们生成的sheet + while (listSheetName.size() != wb.getNumberOfSheets()) { + if (!listSheetName.contains(wb.getSheetName(0))) { + wb.removeSheetAt(0); + } + } + } + wb.write(os); + clearCellStyleCache(wb); + } + + private static String getMergeFieldValue(String keyField, Map curRow, Map mapMergeField) { + if (mapMergeField == null || !mapMergeField.containsKey(keyField)) { + return StringUtil.checkNull(curRow.get(keyField)); + } + return StringUtil.checkNull(curRow.get(mapMergeField.get(keyField))); + } + + private static void exportToExcelEx(Sheet sheet, HSSFWorkbook wb, List> data, SwMap model, Map mapMergeField, short defFontSize) throws Exception { + CellStyle colTitleStyle; + if (model.containsKey("colTitleBold") && model.readString("colTitleBold").equals("1")) { + colTitleStyle = createCellStyle(wb, defFontSize, true, true, HorizontalAlignment.CENTER); + } else { + colTitleStyle = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.CENTER); + } + CellStyle dataStyle_left = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.LEFT); + CellStyle dataStyle_right = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.RIGHT); + CellStyle dataStyle_center = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.CENTER); + + CellStyle dataStyle_amount = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.RIGHT); + dataStyle_amount.setDataFormat(HSSFDataFormat.getBuiltinFormat("#,##0.00"));//两位小数 + + CellStyle dataStyle_number = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.CENTER); + dataStyle_number.setDataFormat(HSSFDataFormat.getBuiltinFormat("#")); + + Map mapIndention = new HashMap<>(); + mapIndention.put(0, dataStyle_left); + + //总列数,加上序号列 + int colCount = 0; + if (model.containsKey("needSeqCol")) { + colCount = model.readListMap("columns").size(); + } else { + colCount = model.readListMap("columns").size() + 1; + } + //导出表头 + int rowIndex = 0, colIndex = 0; + //写入标题行 + String s = model.readString("top"); + if (StringUtil.isNotEmpty(s)) { + CellStyle csh = createCellStyle(wb, (short) 16, true, false, HorizontalAlignment.CENTER); + writeMergeCell(sheet, 35, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + rowIndex++; + } + s = model.readString("title"); + if (StringUtil.isNotEmpty(s)) { + CellStyle csh = createCellStyle(wb, (short) 16, true, false, HorizontalAlignment.CENTER); + writeMergeCell(sheet, 35, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + rowIndex++; + } + //写入表头上方说明文字,一般为查询条件等 + s = model.readString("headerStr"); + if (StringUtil.isNotEmpty(s)) { + HorizontalAlignment align; + switch (s){ + case "center":{ + align = HorizontalAlignment.CENTER; + break; + } + case "right":{ + align = HorizontalAlignment.RIGHT; + break; + } + default:align = HorizontalAlignment.LEFT; + } + CellStyle csh = createCellStyle(wb, defFontSize, false, false, align); + writeMergeCell(sheet, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + rowIndex++; + } + final List> columns = model.readListMap("columns"); + final int collen = columns.size(); + int[] colMaxCharLen = new int[collen + 1]; + //写列标题 + int i, len; + List> header = model.readListMap("header"); + for (i = 0, len = header.size(); i < len; i++) { + Map hm = header.get(i); + SwMap h = new SwMap(); + h.putAll(hm); + // + int hRow = h.readInt("row"), endRow, hCol = h.readInt("col"), colspan = h.readInt("colspan"); + if (h.readBool("isLeaf")) endRow = model.readInt("headerRowCount") - 1 + rowIndex; + else endRow = hRow + h.readInt("rowspan") - 1 + rowIndex; + String text = h.readString("title"); + int fromCol = hCol + colIndex; + writeMergeCell(sheet, hRow + rowIndex, endRow, fromCol, fromCol + colspan - 1, colTitleStyle, text); + if (colspan == 1 && fromCol < colMaxCharLen.length) colMaxCharLen[fromCol] = getStrLength(text); + } + rowIndex += model.readInt("headerRowCount"); + + int curRowIndex; + Row row; + Map curRow; + AutoMergeCols amcs = new AutoMergeCols(); + AutoMergeColInfo mci; + boolean isIgnoreZero = model.readBool("ignoreZero"); + for (i = 0, len = data.size(); i < len; i++) { + curRowIndex = rowIndex + i; + curRow = data.get(i); + row = sheet.createRow(curRowIndex); + boolean isEnd = curRowIndex == data.size() + rowIndex - 1; + //写序号列 + String rowNum = ""; + int indention = curRow.containsKey("indention") ? StringUtil.getIntIgnoreErr(curRow.get("indention")) : 0; + if (StringUtil.isEmpty(curRow.get("__sys_footer"))) rowNum = String.valueOf(i + 1); + if (!model.containsKey("needSeqCol")) { + writeCell(row, 0, dataStyle_number, rowNum, ColType.TEXT, isIgnoreZero); + } + for (int j = 0; j < collen; j++) { + Map cm = columns.get(j); + SwMap col = new SwMap(); + col.putAll(cm); + // + String keyField = col.readString("field"); + String key = StringUtil.checkNull(curRow.get(keyField)); + String value = getMergeFieldValue(keyField, curRow, mapMergeField); + CellStyle cs; + ColType ct = ColType.getColType(col.readString("colType")); + if (ct == ColType.AMOUNT) { + cs = dataStyle_amount; + } else if (ct == ColType.NUMBER) { + cs = dataStyle_number; + } else if (ct == ColType.MAIN) { + cs = mapIndention.computeIfAbsent(indention, k -> { + CellStyle cst = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.LEFT); + cst.setIndention((short) (indention * 2)); + return cst; + }); + } else { + String align = col.readString("align"); + if ("CENTER".equalsIgnoreCase(align)) cs = dataStyle_center; + else if ("RIGHT".equalsIgnoreCase(align)) cs = dataStyle_right; + else cs = dataStyle_left; + } + if (!model.containsKey("needSeqCol")) { + writeCell(row, j + 1, cs, value, ct, col.readBool("ignoreZero", isIgnoreZero)); + colMaxCharLen[j + 1] = Math.max(colMaxCharLen[j + 1], getStrLength(value)); + if (col.readBool("autoMerge")) {//需要自动合并单元格 + mci = amcs.checkMerge(j + 1, curRowIndex, key, isEnd); + if (mci != null) { + writeMergeCell(sheet, mci.row, curRowIndex - 1, j + 1, j + 1, cs, mci.value); + } + amcs.addCol(j + 1, curRowIndex, key, value); + if (i == len - 1) { + //最后一行了,也要看下有合并行没,有的话要写上 + mci = amcs.checkMergeLast(j + 1); + if (mci != null) { + writeMergeCell(sheet, mci.row, curRowIndex, j + 1, j + 1, cs, mci.value); + } + } + } + } else { + writeCell(row, j, cs, value, ct, col.readBool("ignoreZero", isIgnoreZero)); + colMaxCharLen[j] = Math.max(colMaxCharLen[j], getStrLength(value)); + if (col.readBool("autoMerge")) {//需要自动合并单元格 + mci = amcs.checkMerge(j, curRowIndex, key, isEnd); + if (mci != null) writeMergeCell(sheet, mci.row, curRowIndex - 1, j, j, cs, mci.value); + amcs.addCol(j, curRowIndex, key, value); + } + } + + } + } + //去除最后一行 + if (model.readBool("removeLastRow")) { + sheet.removeRow(sheet.getRow(sheet.getLastRowNum())); + len = 1; + } else { + len = 0; + } + + s = model.readString("footer"); + if (StringUtil.isNotEmpty(s)) { + rowIndex += data.size(); + rowIndex -= len; + CellStyle csh = createCellStyle(wb, (short) (defFontSize + 2), true, false, HorizontalAlignment.LEFT); + writeMergeCell(sheet, 30, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + } + if (model.containsKey("footers")) { + List footers = model.readStringList("footers"); + for (int j = 0; j < footers.size(); j++) { + if (StringUtil.isNotEmpty(s)) { + rowIndex++; + } else { + if (j == 0) { + rowIndex += data.size(); + rowIndex -= len; + } else { + rowIndex++; + } + } + CellStyle csh = createCellStyle(wb, (short) (defFontSize + 2), true, false, HorizontalAlignment.LEFT); + writeMergeCell(sheet, 30, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, footers.get(j)); + } + } + + //自动列宽 + for (int j = 0; j <= collen; j++) { + int l = colMaxCharLen[j] + 3; + if (l < 255) { + sheet.setColumnWidth(j, l * 256); + } else { + sheet.setColumnWidth(j, 6000); + } + } + } + + public enum ColType { + AMOUNT, + NUMBER, + TEXT, + MAIN; + + public static ColType getColType(String colType) { + switch (colType.toUpperCase()) { + case "AMOUNT": //金额 + return AMOUNT; + case "NUMBER": + return NUMBER; + case "MAIN": + return MAIN; + default: + return TEXT; + } + + } + } + + public static void exportToExcelModelFile(Map header, List> listDetail, String modelFile, String savePath, int rowCount) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; + File tempFile = new File(savePath); + if (tempFile.exists()) tempFile.delete(); + tempFile.createNewFile(); + + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + if (CommUtil.isEmpty(listDetail)) return; + int detSize = listDetail.size(); + int sheetCount = detSize % rowCount; + sheetCount = sheetCount != 0 ? detSize / rowCount + 1 : detSize / rowCount; + List listSheetName = new ArrayList<>(); + for (int i = 0; i < sheetCount; i++) { + List> listDetailPage = new ArrayList<>(); + int endIndex = (i + 1) * rowCount; + if (endIndex > detSize) { + endIndex = detSize; + } + for (int index = i * rowCount; index < endIndex; index++) { + listDetailPage.add(listDetail.get(index)); + } + String sheetName = sheetCount + "-" + (i + 1); + listSheetName.add(sheetName); + fillSheet(book.cloneSheet(0), header, listDetailPage, sheetName, isExcel2003); + } + //删除非我们生成的sheet + while (listSheetName.size() != book.getNumberOfSheets()) { + if (!listSheetName.contains(book.getSheetName(0))) { + book.removeSheetAt(0); + } + } + book.write(fs); + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + } + + public static void exportToExcelModelFile(Map header, List> listDetail, String modelFile, String savePath) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; + File tempFile = new File(savePath); + if (tempFile.exists()) tempFile.delete(); + tempFile.createNewFile(); + + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + + fillSheet(book.getSheetAt(0), header, listDetail, isExcel2003); + book.write(fs); + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + } + + public static String exportToExcelModel(Map header, List> listDetail, String modelFile, String fileName) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; + File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + int detSize = listDetail.size(); + if (detSize == 0) { + Sheet sheet = book.createSheet(); + fillSheet(sheet, header, listDetail, isExcel2003); + } else { + int sheetCount = detSize % MAX_EXCEL_COUNT; + sheetCount = sheetCount != 0 ? detSize / MAX_EXCEL_COUNT + 1 : detSize / MAX_EXCEL_COUNT; + List listSheetName = new ArrayList<>(); + List list = new ArrayList<>(); + for (int i = 0; i < sheetCount; i++) { + String sheetName = sheetCount + "-" + (i + 1); + listSheetName.add(sheetName); + Sheet sheet = null; + if (i > 0) { + sheet = book.cloneSheet(0); + book.setSheetName(i, sheetName); + } else { + sheet = book.getSheetAt(0); + } + list.add(sheet); + } + for (int i = 0; i < list.size(); i++) { + List> listDetailPage = new LinkedList<>(); + int endIndex = (i + 1) * MAX_EXCEL_COUNT; + if (endIndex > detSize) { + endIndex = detSize; + } + for (int index = i * MAX_EXCEL_COUNT; index < endIndex; index++) { + listDetailPage.add(listDetail.get(index)); + } + + fillSheet(list.get(i), header, listDetailPage, isExcel2003); + } + //删除非我们生成的sheet + while (listSheetName.size() != book.getNumberOfSheets()) { + if (!listSheetName.contains(book.getSheetName(0))) { + book.removeSheetAt(0); + } + } + book.write(fs); + } + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + + fileName = fileName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return fileName + "@@" + tempFile.getAbsolutePath(); + } + + public static String exportToExcelModel(Map header, List> listSon, Map>> mapSonDetail, String modelFile, String fileName) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; +// File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + File tempFile = File.createTempFile("xls_", ".xls", new File("D:\\jjkj\\")); + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + for (int i = 0, len = book.getNumberOfSheets(); i < len; i++) { + Sheet sheet = book.getSheetAt(i); + final String sheetName = sheet.getSheetName(); + if (!sheetName.contains("{@dyn@}")) fillSheet(sheet, header, listSon, isExcel2003); + else { + int cloneCount = 0; + for (Map stringStringMap : listSon) { + String s = sheetName.replace("{@dyn@}", "").replace("{#", "").replace("#}", ""); + s = stringStringMap.get(s); + if (!mapSonDetail.containsKey(s)) continue; + cloneCount++; + Sheet newSheet = book.cloneSheet(i); + + book.setSheetName(i + cloneCount, s); + stringStringMap.putAll(header); + fillSheet(newSheet, stringStringMap, mapSonDetail.get(s), isExcel2003); + } + book.removeSheetAt(i); + break; + } + } + + book.write(fs); + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + + fileName = fileName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return fileName + "@@" + tempFile.getAbsolutePath(); + } + + //读sheet + private static void fillSheet(Sheet sheet, Map header, List> listDetail, String sheetName, boolean isExcel2003) throws Exception { + //String sheetName = sheet.getSheetName(); + int index = sheet.getWorkbook().getSheetIndex(sheet); + sheet.getWorkbook().setSheetName(index, sheetName); + fillSheetEx(sheet, header, listDetail, isExcel2003); + } + + //读sheet + private static void fillSheet(Sheet sheet, Map header, List> listDetail, boolean isExcel2003) throws Exception { + String sheetName = sheet.getSheetName(); + if (sheetName.contains("{#") && sheetName.contains("#}")) { + final int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet); + sheet.getWorkbook().setSheetName(sheetIndex, ExportUtil.myReplaceStrEx(sheetName, "{#", "#}", header)); + } + fillSheetEx(sheet, header, listDetail, isExcel2003); + } + + private static void setCellValue(Cell cell, String value, String org_txt) { + if (org_txt.startsWith("[@expri@]") || org_txt.startsWith("[@i@]")) { + CellStyle cs = cell.getCellStyle(); + if (cs == null) { + cs = cell.getSheet().getWorkbook().createCellStyle(); + cell.setCellStyle(cs); + } + cell.setCellValue(StringUtil.getIntIgnoreErr(value)); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("0")); + cell.setCellStyle(cs); + } else if (org_txt.startsWith("[@exprd") || org_txt.startsWith("[@d")) { + String s = org_txt.replace("[@exprd", "").replace("[@d", ""); + int c = StringUtil.getIntIgnoreErr(s.substring(0, s.indexOf("@]"))); + if (c == 0) c = 2; + CellStyle cs = cell.getCellStyle(); + if (cs == null) { + cs = cell.getSheet().getWorkbook().createCellStyle(); + cell.setCellStyle(cs); + } + cell.setCellValue(StringUtil.getDoubleIgnoreErr(value)); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("0." + StringUtil.repeatString("0", c))); + cell.setCellStyle(cs); + + } else if (org_txt.startsWith("[@expfml@]")) {//公式格式 + CellStyle cs = cell.getCellStyle(); + if (cs == null) { + cs = cell.getSheet().getWorkbook().createCellStyle(); + cell.setCellStyle(cs); + } + cell.setCellStyle(cs); + cell.setCellFormula("HYPERLINK(\"" + value + "\",\"" + value + "\")"); + } else { + cell.setCellValue(value); + } + } + + private static void fillSheetEx(Sheet sheet, Map header, List> listDetail, boolean isExcel2003) throws Exception { + int totalRows = sheet.getLastRowNum() + 1; + Drawing drawing = sheet.createDrawingPatriarch(); + // excel中的3行,代码中要-1. + for (int i = 0; i < totalRows; i++) { + Row row = sheet.getRow(i); + if (row == null) continue; + if (!isDynRow(row)) {//非动态行 + for (int j = row.getFirstCellNum(), endCell = row.getLastCellNum(); j <= endCell; j++) { + Cell cell = row.getCell(j); + if (null == cell) continue; + if (cell.getCellType() == CellType.STRING) { + String value = cell.getStringCellValue(); + if (StringUtil.isEmpty(value) || !value.contains("[#") || !value.contains("#]")) continue; + String rv = ExportUtil.myReplaceStr(value, header); + if (FileUtil.isImageFromBase64(rv)) { + createPic(sheet, drawing, cell, rv, value, j, i, isExcel2003); + } else { + setCellValue(cell, rv, value); + } + } + } + } else { + if (CommUtil.isEmpty(listDetail)) { + sheet.removeRow(row);//删除行内容 + continue; + } + final int detailSize = listDetail.size(); + + //移动行,插入空行 + final int lastRowNum = sheet.getLastRowNum(); + if (lastRowNum > i) sheet.shiftRows(i + 1, lastRowNum, detailSize - 1, true, false); + + List> list = new ArrayList<>(); + for (int kk = 0; kk < detailSize; kk++) { + Map m = listDetail.get(kk); + Map map = new HashMap<>(m); + map.putAll(header); + list.add(map); + map.put("序号", String.valueOf(kk + 1)); + } + + AutoMergeCols amcs = new AutoMergeCols(); + AutoMergeColInfo mci = null; + for (int j = row.getFirstCellNum(), endCell = row.getLastCellNum(); j <= endCell; j++) { + Cell cell = row.getCell(j); + if (null == cell) continue; + String value = ExcelUtil.getStr(row, j); +// if (cell.getCellType() == Cell.CELL_TYPE_STRING || cell.getCellType() == Cell.CELL_TYPE_FORMULA) { +// String value = cell.getStringCellValue(); +// if (CommUtil.isEmpty(value)) continue; + value = value.replace("[@dyn@]", ""); + boolean automerge = value.contains("[@am@]"); + boolean mergeCell = value.contains("[@mc@]"); + value = value.replace("[@am@]", "").replace("[@mc@]", ""); +// if (!value.contains("[#") || !value.contains("#]")) continue; + + String sValue = ExportUtil.myReplaceStr(value, list.get(0)); +// cell.setCellValue(sValue); + if (FileUtil.isImageFromBase64(sValue)) { + createPic(sheet, drawing, cell, sValue, value, j, i, isExcel2003); + } else { + setCellValue(cell, sValue, value); + } + if (automerge) amcs.addCol(j, i, sValue); + for (int kk = 1; kk < detailSize; kk++) { + boolean isEnd = kk == detailSize - 1; + int end_index = isEnd ? 0 : 1; + Row dr = sheet.getRow(i + kk); + if (dr == null) dr = sheet.createRow(i + kk); + dr.setHeight(row.getHeight()); + + Cell dCell = dr.createCell(j); + + // 风格一样 + dCell.setCellStyle(cell.getCellStyle()); + dCell.setCellType(cell.getCellType()); + sValue = ExportUtil.myReplaceStr(value, list.get(kk)); +// dCell.setCellValue(sValue); + setCellValue(dCell, sValue, value); + if (automerge) {//需要自动合并单元格 + mci = amcs.checkMerge(j, i + kk, sValue, isEnd); + if (mci != null && StringUtil.isNotEmpty(mci.value)) + writeMergeCell(sheet, mci.row, i + kk - end_index, j, j, cell.getCellStyle(), mci.value); + amcs.addCol(j, i + kk, sValue); + } + if (mergeCell) { + writeMergeCell(sheet, i + kk, i + kk, j, getMergeCellEndIndex(j, row), cell.getCellStyle(), sValue); + } + } +// } + } + totalRows += detailSize - 1; + i += detailSize - 1; + } + } + } + + private static int getMergeCellEndIndex(int col, Row row) { + int begin = col; + String value = ""; + while (true) { + begin++; + value = ExcelUtil.getStr(row, begin); + if (!"".equals(value)) { + return begin - 1; + } + if (begin == row.getLastCellNum() - 1) { + return begin; + } + } + } + + public static void createPic(Sheet sheet, Drawing drawing, Cell cell, String value, String org_value, int cell_index, int row_index, boolean isExcel2003) { + try { + BufferedImage bufImg = ImageIO.read(new ByteArrayInputStream(new BASE64Decoder().decodeBuffer(value))); + ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); + ImageIO.write(bufImg, "png", byteArrayOut); + byte[] data = byteArrayOut.toByteArray(); + ClientAnchor anchor = null; + if (isExcel2003) + anchor = new HSSFClientAnchor(5, 5, 1000, 250, (short) cell_index, row_index, (short) cell_index, row_index); + else + anchor = new XSSFClientAnchor(0, 0, 1000, 250, (short) cell_index, row_index, (short) cell_index, row_index); + anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); + drawing.createPicture(anchor, sheet.getWorkbook().addPicture(data, Workbook.PICTURE_TYPE_PNG)); + setCellValue(cell, "", org_value); + } catch (Exception e) { +// UtilLogger.error("插入图片到Excel失败!", e); + System.err.println("插入图片到Excel失败"); + } + } + + public static String createExcelModel(String planName, List head, List detail) throws Exception { + File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + FileOutputStream fs = new FileOutputStream(tempFile); + try { + HSSFWorkbook wb = new HSSFWorkbook(); + Sheet sheet = wb.createSheet("sheet1"); + Row row = null; + + CellStyle csh = createCellStyle(wb, (short) 16, true, false, HorizontalAlignment.CENTER); + writeMergeCell(sheet, 35, 0, 0, 0, detail.size(), csh, planName); + + int rowIndex = 0; +// if (head != null && head.size() > 0) { +// int i = 0; +// for (String key : head) { +// if (i % 2 == 0) { +// row = createRow(sheet, rowIndex + 1, 20); +// setModelCellValue(row, 0, key); +// setModelCellValue(row, 1, "[#" + key + "#]"); +// rowIndex++; +// } else { +// setModelCellValue(row, 2, key); +// setModelCellValue(row, 3, "[#" + key + "#]"); +// } +// i++; +// } +// } + CellStyle dataStyle_center = createCellStyle(wb, (short) 9, false, true, HorizontalAlignment.CENTER); +// CellStyle dataStyle_left = createCellStyle(wb, (short) 9, false, true, CellStyle.ALIGN_LEFT); + if (detail != null && detail.size() > 0) { + Row title = createRow(sheet, rowIndex + 1, 20); + Row value = createRow(sheet, rowIndex + 2, 20); + Row sum = createRow(sheet, rowIndex + 3, 20); + writeCell(title, 0, dataStyle_center, "序号", ColType.TEXT, false); + writeCell(value, 0, dataStyle_center, "[@dyn@][#序号#]", ColType.TEXT, false); + writeCell(sum, 0, dataStyle_center, "合计", ColType.TEXT, false); + int i = 1; + for (String key : detail) { + writeCell(title, i, dataStyle_center, key, ColType.TEXT, false); + writeCell(value, i, dataStyle_center, "[#" + key + "#]", ColType.TEXT, false); + writeCell(sum, i, dataStyle_center, "[#" + key + "_合计#]", ColType.TEXT, false); + i++; + } + } + + rowIndex += 10; + csh = createCellStyle(wb, (short) 9, false, false, HorizontalAlignment.LEFT); + writeMergeCell(sheet, 20, rowIndex, rowIndex, 0, detail.size(), csh, "支持的变量参数"); + rowIndex++; + writeMergeCell(sheet, 20, rowIndex, rowIndex, 0, detail.size(), csh, "头:" + toText(head)); + rowIndex++; + writeMergeCell(sheet, 20, rowIndex, rowIndex, 0, detail.size(), csh, "明细:" + toText(detail)); + + /*row = createRow(sheet, rowIndex + 4, 20); + writeCell(row, 0, dataStyle_left, "支持的变量参数", ColType.TEXT, false); + row = createRow(sheet, rowIndex + 5, 20); + writeCell(row, 0, dataStyle_left, "头:", ColType.TEXT, false); + writeCell(row, 1, dataStyle_left, toText(head), ColType.TEXT, false); + row = createRow(sheet, rowIndex + 6, 20); + writeCell(row, 0, dataStyle_left, "明细:", ColType.TEXT, false); + writeCell(row, 1, dataStyle_left, toText(detail), ColType.TEXT, false);*/ + + wb.write(fs); + clearCellStyleCache(wb); + fs.flush(); + } catch (Exception ioe) { +// UtilLogger.error("读取文件出现错误:", ioe); + System.err.println("读取文件出现错误"); + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(fs); + } + planName = planName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return planName + "@@" + tempFile.getAbsolutePath(); + } + + private static String toText(List list) { + if (CommUtil.isEmpty(list)) return ""; + StringBuilder sb = new StringBuilder(256); + for (String s : list) { + sb.append(",").append(s); + } + return sb.substring(1); + } + + private static void setModelCellValue(Row row, int cellIndex, String value) throws Exception { + Cell cell = createCell(row, cellIndex); + cell.setCellValue(value); + } + + //是否动态行 + private static boolean isDynRow(Row row) { + final short firstCellNum = row.getFirstCellNum(); + if (firstCellNum < 0) return false; + Cell cell = row.getCell(firstCellNum); + if (cell.getCellType() == CellType.STRING) { + String value = cell.getStringCellValue(); + return !(StringUtil.isEmpty(value) || !value.startsWith("[@dyn@]")); + } + return false; + } + + + //需要自动合并单元格的字段列信息 + private static class AutoMergeColInfo { + private int col = -1;//列索引 + private String key = null;// 合并的值 + private String value = null;//合并的值的format值 + private int row = -1;//开始行索引 + } + + private static class AutoMergeCols { + private Map map = new HashMap<>(); + + private void addCol(int col, int row, String value) { + addCol(col, row, value, value); + } + + private void addCol(int col, int row, String key, String value) { + AutoMergeColInfo colInfo = map.get(col); + if (colInfo == null) { + colInfo = new AutoMergeColInfo(); + map.put(col, colInfo); + } + colInfo.col = col; + if (colInfo.key == null || !colInfo.key.equals(key)) colInfo.row = row; + colInfo.key = key; + colInfo.value = value; + } + + //校验是否需要合并单元格,返回null,不需要,其他则返回合并的开始列信息 + private AutoMergeColInfo checkMerge(int col, int row, String key, boolean isEnd) { + AutoMergeColInfo colInfo = map.get(col); + if (colInfo == null) return null; + if (colInfo.key == null) return null; + if (colInfo.row == row - 1) return null;//上一行开始,表示只有一行是此数据, 不需要考虑合并 + if (isEnd && colInfo.key.equals(key)) return colInfo; + if (colInfo.key.equals(key)) return null; + return colInfo; + } + + private AutoMergeColInfo checkMergeLast(int col) { + AutoMergeColInfo colInfo = map.get(col); + if (colInfo == null) return null; + if (colInfo.value == null) return null; + return colInfo; + } + } + + private static void checkTemplateFile(String modelFile) throws Exception { + if (StringUtil.isEmpty(modelFile) || !FileUtil.isFileExist(modelFile)) throw new Exception("模板文件不存在:" + modelFile); + } + + public static void main(String[] args) throws Exception { + Map map = new HashMap<>(); + map.put("户数-永顺镇", "1"); + map.put("人数-永顺镇", "100"); + map.put("低保金-永顺镇", "1000"); + List> listSon = new ArrayList<>(); + HashMap m = new HashMap<>(); + m.put("区划", "永顺镇"); + m.put("p-人数", "100"); + m.put("p-低保金", "10000"); + listSon.add(m); + + m = new HashMap<>(); + m.put("区划", "宋庄镇"); + m.put("p-人数", "200"); + m.put("p-低保金", "20000"); + listSon.add(m); + + Map>> mapSonDetail = new HashMap<>(); + List> listSonD = new ArrayList<>(); + mapSonDetail.put("永顺镇", listSonD); + m = new HashMap<>(); + m.put("姓名", "永顺镇1"); + m.put("人数", "50"); + m.put("低保金", "5000"); + listSonD.add(m); + + m = new HashMap<>(); + m.put("姓名", "永顺镇2"); + m.put("人数", "40"); + m.put("低保金", "4000"); + listSonD.add(m); + + listSonD = new ArrayList<>(); + mapSonDetail.put("宋庄镇", listSonD); + m = new HashMap<>(); + m.put("姓名", "宋庄镇1"); + m.put("人数", "10"); + m.put("低保金", "1000"); + listSonD.add(m); + + m = new HashMap<>(); + m.put("姓名", "宋庄镇2"); + m.put("人数", "20"); + m.put("低保金", "2000"); + listSonD.add(m); + + // + List> rows = new ArrayList<>(); + HashMap row = new HashMap<>(); + row.put("F1", "宋庄镇2"); + row.put("F2", "20"); + row.put("F3", "2000"); + rows.add(row); + + row = new HashMap<>(); + row.put("F1", "宋庄镇22"); + row.put("F2", "220"); + row.put("F3", "22000"); + rows.add(row); + + row = new HashMap<>(); + row.put("F1", "宋庄镇222"); + row.put("F2", "2220"); + row.put("F3", "222000"); + rows.add(row); + + row = new HashMap<>(); + row.put("F1", "宋庄镇2222"); + row.put("F2", "22220"); + row.put("F3", "2222000"); + rows.add(row); + + String model = "{\"header\":[{\"title\":\"姓名\",\"colspan\":1,\"rowspan\":1,\"row\":0,\"col\":0,\"isLeaf\":true},{\"title\":\"人数\",\"colspan\":1,\"rowspan\":1,\"row\":0,\"col\":1,\"isLeaf\":true},{\"title\":\"低保金\",\"colspan\":1,\"rowspan\":1,\"row\":0,\"col\":2,\"isLeaf\":true}],\"columns\":[{\"field\":\"F1\",\"align\":\"center\",\"title\":\"姓名1\",\"chname\":\"姓名1\"},{\"field\":\"F2\",\"align\":\"center\",\"title\":\"人数1\",\"chname\":\"人数1\"},{\"field\":\"F3\",\"align\":\"center\",\"title\":\"低保金1\",\"chname\":\"低保金1\"}],\"headerRowCount\":1,\"title\":\"收款台列表\",\"rownum_\":0}"; + + String ss = exportToExcel(rows, model); +// String ss = exportToExcelModel(map, listSonD, mapSonDetail, "D:\\jjkj\\test.xlsx", "a"); + System.out.println(ss); + + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelUtil.java new file mode 100644 index 0000000..a64fdbf --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelUtil.java @@ -0,0 +1,131 @@ +package cc.smtweb.framework.core.util; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; + +import java.util.ArrayList; +import java.util.List; + +public class ExcelUtil { + private static int xy(int x, int y) { + if (y == 0) return 1; + if (y == 1) return x; + int f = x * x; + for (int i = 0; i < (y - 2); ++i) { + f *= x; + } + return f; + } + + // Z==> 26, AA == > 27 , AB ==> 28 + public static int toColInt(String col) { + col = col.toUpperCase(); + int colv = 0, left = 0; + for (int i = col.length(); i > 0; --i) { + char c = col.charAt(i - 1); + colv += (c - 'A' + 1) * xy(26, left); + ++left; + } + return colv - 1; // excel是从0开始的 + } + + /** + * 数字下标转列 + * @param index + * @return + */ + public static String toColExcel(int index) { + int shang = 0; + int yu = 0; + List list = new ArrayList(); //10进制转26进制 倒序 + do { + shang = index / 26; + yu = index % 26; + index = shang; + list.add(yu); + } while (shang != 0); + StringBuilder sb = new StringBuilder(); + for (int j = list.size() - 1; j >= 0; j--) { + sb.append((char) (list.get(j) + 'A' - (Math.min(j, 1)))); //倒序拼接 序号转字符 非末位 序号减去 1 + } + return sb.toString(); + } + + + // 注意col要大写字母 + public static String getStr(Row row, char col) { + int i = col - 'A'; + return getStr(row, i); + } + + public static String getStr(Row row, int col) { + if(col <0) return ""; + Cell cell = row.getCell(col); + if (null == cell) return ""; + if (cell.getCellType() == CellType.BLANK || cell.getCellType() == CellType.ERROR) { + return ""; + } + // AKzz : cell 是数字时,会报错误, + if (cell.getCellType() == CellType.NUMERIC) { + // 非日期格式 + if (!org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { + double d = cell.getNumericCellValue(); +// long x = (long) d; + return MathUtil.getDoubleStr(d); + } else { + return cc.smtweb.framework.core.util.DateUtil.toStdDateString(cell.getDateCellValue()); + } + } + + if (cell.getCellType() == CellType.STRING) { + return StringUtil.checkNull(cell.getStringCellValue()); + } + + if (cell.getCellType() == CellType.FORMULA) { + return getFormulaStr(cell); + } + return ""; + } + + // 注意col要大写字母 + public static int getInt(Row row, char col) { + int i = col - 'A'; + return getInt(row, i); + } + + public static int getInt(Row row, int col) { + if(col <0) return 0; + Cell cell = row.getCell(col); + if (null == cell) return 0; + if (cell.getCellType() == CellType.NUMERIC && !org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { + double d = cell.getNumericCellValue(); + return (int) d; + } else if (cell.getCellType() == CellType.STRING) { + return MathUtil.toint(cell.getStringCellValue(), 0); + } else if (cell.getCellType() == CellType.FORMULA) { + return (int) getFormulaDbl(cell); + } + return 0; + } + + private static String getFormulaStr(Cell cell) { + try { + return StringUtil.checkNull(cell.getStringCellValue()); + } catch (Exception e) { +// UtilLogger.warn("读取数据出错:"+cell, e); + System.err.println("读取数据出错"); + String v = MathUtil.toStdNumberString(cell.getNumericCellValue()); + if (v.endsWith(".00")) v = v.substring(0, v.length() - 3); + return v; + } + } + + private static double getFormulaDbl(Cell cell) { + try { + return cell.getNumericCellValue(); + } catch (Exception e) { + return MathUtil.todouble(cell.getStringCellValue()); + } + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExportUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExportUtil.java new file mode 100644 index 0000000..b4784f7 --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExportUtil.java @@ -0,0 +1,54 @@ +package cc.smtweb.framework.core.util; + +import java.util.HashMap; +import java.util.Map; + +public class ExportUtil { + //填充变量 + public static String myReplaceStr(String s, Map map) { + boolean isDouble = s.startsWith("[@exprd"); + boolean isInt = s.startsWith("[@expri@]"); + if (isDouble || isInt) { + s = s.substring(s.indexOf("@]") + 2); + String b = "[#", e = "#]", svar; + int n = s.indexOf(b), lb = b.length(); + Map mapvar = new HashMap<>(); + int i = 0; + while (n >= 0) { + svar = "a" + (++i); + String fn = s.substring(n + lb, s.indexOf(e)); + s = s.replace(b + fn + e, svar); + n = s.indexOf(b); + mapvar.put(svar, StringUtil.getDoubleIgnoreErr(map.get(fn))); + } + double d; + try { + d = MathUtil.calcExprMapDouble(s, mapvar); + } catch (Exception e1) { + d = 0.0; +// UtilLogger.error("计算错误:", e1); + } + if (isDouble) { + String v = MathUtil.getAmountStr(d); + v = v.replace(".00", "").replaceAll(",", ""); + if (v.contains(".") && v.endsWith("0")) v = v.substring(0, v.length() - 1); + return v; + } + return MathUtil.toStdNumberString(d, 0); + } else if (s.startsWith("[@d") || s.startsWith("[@i@]")) s = s.substring(s.indexOf("@]") + 2); + else if (s.startsWith("[@expfml@]")) s = s.substring(10); + + return myReplaceStrEx(s, "[#", "#]", map); + } + + //填充变量 + public static String myReplaceStrEx(String s, String b, String e, Map map) { + int n = s.indexOf(b), lb = b.length(); + while (n >= 0) { + String fn = s.substring(n + lb, s.indexOf(e)); + s = s.replace(b + fn + e, StringUtil.checkNull(map.get(fn))); + n = s.indexOf(b); + } + return s; + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java index 4742429..6bc240a 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java @@ -3,6 +3,7 @@ package cc.smtweb.framework.core.util; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; +import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.imageio.ImageIO; @@ -98,6 +99,24 @@ public class FileUtil { return "data:image/png;base64," + png_base64; } + /** + * 判断是否是图片 + * + * @param base64Str + * @return + */ + public static boolean isImageFromBase64(String base64Str) { + try { + BufferedImage bufImg = ImageIO.read(new ByteArrayInputStream(new BASE64Decoder().decodeBuffer(base64Str))); + if (null == bufImg) { + return false; + } + } catch (Exception e) { + return false; + } + return true; + } + public static boolean copyFile(File oldfile, String newPath, boolean isDelOld) { if (!oldfile.exists()) return true; int byteread; diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/HttpUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/HttpUtil.java index e9e7759..4b62e4c 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/HttpUtil.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/HttpUtil.java @@ -107,7 +107,12 @@ public class HttpUtil { public static SwMap post(String url, Map paras) throws Exception { String json_resp = post(url, null, paras, null); if (StringUtils.isEmpty(json_resp) || !json_resp.startsWith("{")) return null; - return (SwMap) JsonUtil.parseMap(json_resp); + SwMap swMap = new SwMap(); + Map map = JsonUtil.parseMap(json_resp); + if(map!=null){ + swMap.putAll(map); + } + return swMap; } /** @@ -147,7 +152,12 @@ public class HttpUtil { json_resp = new String(Base64.decodeBase64(json_resp), charset); } if (StringUtils.isEmpty(json_resp) || !json_resp.startsWith("{")) return null; - return (SwMap) JsonUtil.parseMap(json_resp); + SwMap swMap = new SwMap(); + Map map = JsonUtil.parseMap(json_resp); + if(map!=null){ + swMap.putAll(map); + } + return swMap; } /** @@ -169,14 +179,24 @@ public class HttpUtil { json_resp = new String(Base64.decodeBase64(json_resp), charset); } if (StringUtils.isEmpty(json_resp) || !json_resp.startsWith("{")) return null; - return (SwMap) JsonUtil.parseMap(json_resp); + SwMap swMap = new SwMap(); + Map map = JsonUtil.parseMap(json_resp); + if(map!=null){ + swMap.putAll(map); + } + return swMap; } public static SwMap post(String url, Map header, Map paras) throws Exception { String json_resp = post(url, header, paras, null); if (StringUtils.isEmpty(json_resp) || !json_resp.startsWith("{")) return null; - return (SwMap) JsonUtil.parseMap(json_resp); + SwMap swMap = new SwMap(); + Map map = JsonUtil.parseMap(json_resp); + if(map!=null){ + swMap.putAll(map); + } + return swMap; } /** diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MathUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MathUtil.java new file mode 100644 index 0000000..34f038b --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MathUtil.java @@ -0,0 +1,507 @@ +package cc.smtweb.framework.core.util; + +import org.apache.commons.jexl3.*; +import org.apache.commons.jexl3.internal.Engine; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.Map; + +public class MathUtil { + //整数 + private static DecimalFormat dfLng = new DecimalFormat("##############0"); + private static DecimalFormat dfLong = new DecimalFormat("###,###,###,###,##0"); + //一位小数 + private static DecimalFormat df1 = new DecimalFormat("##############0.0"); + private static DecimalFormat df1Format = new DecimalFormat("###,###,###,###,##0.0"); + //两位小数 + private static DecimalFormat df2 = new DecimalFormat("##############0.00"); + private static DecimalFormat df2Format = new DecimalFormat("###,###,###,###,##0.00"); + //四位小数 + private static DecimalFormat df4 = new DecimalFormat("###,###,###,###,##0.0000"); + //六位小数 + private static DecimalFormat df6Number = new DecimalFormat("#######################0.000000"); + private static DecimalFormat df6NumberF = new DecimalFormat("#,###,###,###,###,###,##0.000000"); + + public final static DecimalFormat stdAmountFormat = new DecimalFormat("###,###,###,###,##0.00"); + public final static DecimalFormat stdNumberFormat = new DecimalFormat("#0.00"); + public final static String DEF_NUM_TEN_THOUSAND = "10000";//万 + public static final double MAX_VALUE = 9999999999999.99D; + public static final double MIN_VALUE = -9999999999999.99D; + + private final static BigDecimal ONE_BIG = new BigDecimal(1.00D); + private static final String UNIT = "万仟佰拾亿仟佰拾万仟佰拾元角分"; + private static final String DIGIT = "零壹贰叁肆伍陆柒捌玖"; + + /** + * 4舍5入double,2位小数 + */ + public static double roundDouble(double src) { + return roundDouble(src, 2); + } + + /** + * 4舍5入double,N 位小数 + * + * @param src + * @param scale 小数位数 + * @return + */ + public static double roundDouble(Object src, int scale) { + if (src == null) return 0.0; + String v = src.toString(); + if (StringUtil.isEmpty(v)) return 0.0; + if (scale < 0) scale = 2; + + BigDecimal src_b = new BigDecimal(v); + BigDecimal src_v = src_b.divide(ONE_BIG, scale + 2, BigDecimal.ROUND_HALF_UP);// 4舍5入 + src_v = src_v.divide(ONE_BIG, scale, BigDecimal.ROUND_HALF_UP);// 4舍5入 + return src_v.doubleValue(); + } + + /** + * 舍位处理,原生floor有坑,部分浮点数记录为.99999999999形式,导致floor结果错误 + * + * @param d + * @return + */ + public static double floor(double d) { + return Math.floor(MathUtil.roundDouble(d, 2)); + } + + /** + * 比较两Double是否相等,将会吧他们专程BigDecimal进行比较; + * + * @param src double1 + * @param tag double2 + * @return src > tag 返回1, src < tag 返回-1, 否则返回0 + */ + public static int compare(double src, double tag) { + BigDecimal src_b = new BigDecimal(src); + BigDecimal src_v = src_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入 + + BigDecimal tag_b = new BigDecimal(tag); + BigDecimal tag_v = tag_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入 + + return src_v.compareTo(tag_v); + } + + /** + * 自动过滤金额中的逗号转换为double,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return double + */ + public static Double toDouble(String s) { + return todouble(s); + } + + /** + * 自动过滤金额中的逗号转换为double,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return double + */ + public static double todouble(String s) { + try { + return Double.parseDouble(s.replaceAll(",", "")); + } catch (Exception e) { + return 0.00; + } + } + + /** + * 获取double,主要过滤d为null的情况; + * + * @param d Double对象; + * @return double + */ + public static double todouble(Double d) { + if (d == null) return 0.0d; + return d.doubleValue(); + } + + /** + * 自动过滤金额中的逗号转换为float,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return Float + */ + public static Float toFloat(String s) { + return tofloat(s); + } + + /** + * 自动过滤金额中的逗号转换为float,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return Float + */ + public static float tofloat(String s) { + try { + return Float.parseFloat(s.replaceAll(",", "")); + } catch (Exception e) { + return 0.0f; + } + } + + public static long tolong(String src, long defaultvalue) { + try { + return Long.parseLong(src); + } catch (Exception e) { + return defaultvalue; + } + } + + public static int toint(String src, int defaultvalue) { + try { + return Integer.parseInt(src); + } catch (Exception e) { + return defaultvalue; + } + } + + /** + * 考虑使用中的精度,判断一个Value是否>0,实际是>0.00001 + * + * @param value double类型 + * @return boolean + */ + public static boolean isBigThanZero(double value) { + return (value > 0.00001); + } + + /** + * 考虑使用中的精度,判断一个Value是否>0,实际是>0.00001 + * + * @param value String类型 + * @return boolean + */ + public static boolean isBigThanZero(String value) { + return !StringUtil.isEmpty(value) && isBigThanZero(toDouble(value)); + } + + /** + * 考虑使用中的精度,判断一个Value是否=0,实际是给出一个值范围。 + * + * @param value double类型 + * @return boolean + */ + public static boolean isEqualsZero(double value) { + return (-0.00001 < value && value < 0.00001); + } + + /** + * 考虑使用中的精度,判断一个Value是否=0,实际是给出一个值范围。 + * + * @param value String类型 + * @return boolean + */ + public static boolean isEqualsZero(String value) { + return StringUtil.isEmpty(value) || isEqualsZero(toDouble(value)); + } + + /** + * 是否是负数 + * + * @param db_val 要判断的double + * @return 负数则返回true; + */ + public static boolean isNegative(double db_val) { + return (compare(db_val, 0.00D) == -1); + } + + /** + * 是否是正数 + * + * @param db_val 要判断的double + * @return 正数则返回true; + */ + public static boolean isPlus(double db_val) { + return (compare(db_val, 0.00D) == 1); + } + + /** + * 得到金额字符串,保持小数点2位 + * + * @param db 将double转换为金额字符串; + * @return 金额字符串#0.00; + */ + public static String toStdNumberString(double db) { + try { + return stdNumberFormat.format(db); + } catch (Exception e) { + return "0.00"; + } + } + + public static String toStdNumberStringEx(double db) { + try { + if (compare(db, -1d) == 0) return "-"; + return stdNumberFormat.format(db); + } catch (Exception e) { + return "0.00"; + } + } + + /** + * 将金额格式字符串,如23,333,093.01 去掉逗号 + * + * @param s 金额串 + * @return String 去掉逗号后的串,如果amount为空,则返回0.00 + */ + public static String toStdNumberString(String s) { + if (StringUtil.isEmpty(s)) + return "0.00"; + return stdNumberFormat.format(MathUtil.todouble(s)); + } + + /** + * 将数据转换为两位小数的数字格式; + * + * @param d 数据 + * @param isZeroToEmpty 如果未0,是否返回“”; + * @return 两位小数的字符串; + */ + public static String toStdNumberString(double d, boolean isZeroToEmpty) { + if (isEqualsZero(d)) { + return isZeroToEmpty ? "" : "0.00"; + } + return stdNumberFormat.format(d); + } + + public static String toStdNumberString(String s, boolean isZeroToEmpty) { + return toStdNumberString(MathUtil.todouble(s), isZeroToEmpty); + } + + public static String toStdNumberString(double d, int scale) { + DecimalFormat dfn = null; + if (scale == 1) dfn = df1Format; + if (scale == 2) dfn = df2Format; + else if (scale == 4) dfn = df4; + else if (scale == 6) dfn = df6NumberF; + else if (scale <= 0) dfn = dfLong; + else { + StringBuilder sb = new StringBuilder("###,###,###,###,##0."); + for (int i = 0; i < scale; i++) sb.append("0"); + dfn = new DecimalFormat(sb.toString()); + } + return dfn.format(d); + } + + /** + * 将数字乘100,保留小数点后两位, 然后后面添加% + * + * @param d 值 + * @param isZeroToEmpty,如果值为0,是否返回空; + * @return 字符串; + */ + public static String toStdPercentNumberStr(double d, boolean isZeroToEmpty) { + if (d > -0.00000000001 && d < 0.00000000001) { + return isZeroToEmpty ? "" : "0.00%"; + } + return toStdNumberString(d * 100) + "%"; + } + + + public static String toStdAmountString(double d) { + return toStdAmountString(d, false); + } + + /** + * 将数据转换为两位小数的金额格式,带逗号; + * + * @param d 数据 + * @param isZeroToEmpty 如果未0,是否返回“”; + * @return 金额格式的字符串; + */ + public static String toStdAmountString(double d, boolean isZeroToEmpty) { + if (isEqualsZero(d)) { + return isZeroToEmpty ? "" : "0.00"; + } + return stdAmountFormat.format(d); + } + + public static String toStdAmountString(String s) { + return toStdAmountString(MathUtil.todouble(s), false); + } + + public static String toStdAmountString(String s, boolean isZeroToEmpty) { + return toStdAmountString(MathUtil.todouble(s), isZeroToEmpty); + } + + + /** + * 将小写金额转换为人民币大写金额 + * + * @param s 金额格式的串 + * @return String 转换结果 + */ + public static String toCapsAmountString(String s) { + if (StringUtil.isEmpty(s)) return ""; + return toCapsAmountString(todouble(s)); + } + + /** + * 将小写金额转换为人民币大写金额 + * + * @param v double + * @return String 转换结果 + */ + public static String toCapsAmountString(double v) { + if (v < MIN_VALUE || v > MAX_VALUE) return "参数非法!"; + + boolean negative = isNegative(v); + + if (negative) v = Math.abs(v); + long l = Math.round(v * 100); + if (l == 0) return "零元整"; + + String strValue = String.valueOf(l); + // i用来控制数 + int i = 0; + // j用来控制单位 + int j = UNIT.length() - strValue.length(); + StringBuilder rs = new StringBuilder(32); + boolean isZero = false; + for (; i < strValue.length(); i++, j++) { + char ch = strValue.charAt(i); + if (ch == '0') { + isZero = true; + if (UNIT.charAt(j) == '亿' || UNIT.charAt(j) == '万' || UNIT.charAt(j) == '元') { + rs.append(UNIT.charAt(j)); + isZero = false; + } + } else { + if (isZero) { + rs.append('零'); + isZero = false; + } + rs.append(DIGIT.charAt(ch - '0')).append(UNIT.charAt(j)); + } + } + if (rs.charAt(rs.length() - 1) != '分') + rs.append('整'); + + i = rs.indexOf("亿万"); + if (i > 0) rs.delete(i + 1, i + 2); // i+1 ->万 + + if (negative) + return rs.insert(0, '负').toString(); + else + return rs.toString(); + } + + /** + * 返回0 到 maxvalue的随机数 + * + * @param maxvalue 随机数的最大值 + * @return int + */ + public static int rnd(int maxvalue) { + return (int) (Math.random() * (maxvalue + 1)); + } + + + public static double chkDbNull(Double v) { + return v == null ? 0 : v; + } + + public static double max(double d1, double d2) { + return compare(d1, d2) < 0 ? d2 : d1; + } + + public static double min(double d1, double d2) { + return compare(d1, d2) < 0 ? d1 : d2; + } + + /** + * double 去尾法 + * + * @param src 待处理数据 + * @param scale 保留小数位数 + * @return + */ + public static double cutDouble(double src, int scale) { + String v = toStdNumberString(src, 6);//先到6位小数,再去计算,否则容易出错,如8.3成8.29999999999,舍位就成了8.29了 + DecimalFormat formater = new DecimalFormat(); + formater.setMaximumFractionDigits(scale); + formater.setGroupingSize(0); + formater.setRoundingMode(RoundingMode.FLOOR); + return new BigDecimal(formater.format(toDouble(v))).doubleValue(); + } + + public static String getAmountStr(double amount) { + return df2Format.format(amount); + } + + //数字转字符串,如无小数,则去掉.00 + public static String getDoubleStr(double d) { + return getAmountStr(d).replaceAll(",", "").replaceAll("\\.00", ""); + } + + /** + * double 进位法 + * + * @param src 待处理数据 + * @param scale 保留小数位数 + * @return + */ + public static double upDouble(double src, int scale) { + String v = toStdNumberString(src, 6);//先到6位小数,再去计算 + DecimalFormat formater = new DecimalFormat(); + formater.setMaximumFractionDigits(scale); + formater.setGroupingSize(0); + formater.setRoundingMode(RoundingMode.UP); + return new BigDecimal(formater.format(toDouble(v))).doubleValue(); + } + + /** + * 计算公式 参数以Map方式传入 + * + * @param expr 表达式 + * @param mapVar 变量 + * @return 计算结果 + */ + public static Object calcExprMapObject(String expr, Map mapVar) { + if (StringUtil.isEmpty(expr)) return ""; + JexlContext jc = new MapContext(); + jc.set("Math", Math.class); + jc.set("UtilPub", StringUtil.class); + for (String k : mapVar.keySet()) jc.set(k, mapVar.get(k)); + JexlExpression e = new Engine().createExpression(expr); + return e.evaluate(jc); + } + + /** + * 计算公式,参数以数组方式传入,key、value交替 + * + * @param expr 表达式 + * @param vars 变量 + * @return + */ + public static Object calcExprObjectEx(String expr, Object... vars) { + if (StringUtil.isEmpty(expr)) return ""; + JexlContext jc = new MapContext(); + jc.set("Math", Math.class); + jc.set("UtilPub", StringUtil.class); + for (int i = 0, len = vars.length; i < len; ) { + jc.set((String) vars[i++], vars[i++]); + } + JexlExpression e = new Engine().createExpression(expr); + return e.evaluate(jc); + } + + public static double calcExprMapDouble(String expr, Map mapVar) { + Object o = calcExprMapObject(expr, mapVar); + if (o != null) return StringUtil.getDoubleIgnoreErr(o.toString()); + return 0.0; + } + + public static double calcExprDoubleEx(String expr, Object... vars) { + Object o = calcExprObjectEx(expr, vars); + if (o != null) return StringUtil.getDoubleIgnoreErr(o.toString()); + return 0.0; + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java index 16a108f..b24cb8f 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; import java.text.Collator; +import java.text.DecimalFormat; import java.util.*; /** @@ -15,6 +16,8 @@ import java.util.*; */ @SuppressWarnings("UnusedDeclaration") public class StringUtil { + private static DecimalFormat df2Format = new DecimalFormat("###,###,###,###,##0.00"); + private static Collator chineseCollator = Collator.getInstance(Locale.CHINA); public static boolean isEmpty(String str) { @@ -182,6 +185,52 @@ public class StringUtil { } /** + * 获取浮点数(有错误默认为0),可以识别金额中的逗号格式 + * + * @param str 带转换的字符串 + * @return 浮点数 + */ + public static double getDoubleIgnoreErr(String str) { + if (str == null) + return 0.0; + str = str.trim(); + if (str.equals("")) + return 0.0; + str = str.replaceAll(",", "").replaceAll(",", ""); + try { + return Double.valueOf(str); + } catch (Exception e) { + return 0.0; + } + } + + /** + * 得到int 获取转换的int值,有错返回0 + * + * @param str 带转换的字符串 + * @return int + */ + public static int getIntIgnoreErr(String str) { + return getIntIgnoreErr(str, 0); + } + + public static int getIntIgnoreErr(String str, int defValue) { + if (str == null) + return defValue; + str = str.trim(); + if (str.equals("")) + return defValue; + str = str.replaceAll(",", "").replaceAll(",", ""); + if (str.contains(".")) + str = str.substring(0, str.indexOf('.')); + try { + return Integer.valueOf(str); + } catch (Exception e) { + return defValue; + } + } + + /** * 取得重复字串 * * @param repeatString 重复字串