package com.seeyon.apps.assetsmap.service; import cn.hutool.log.Log; import com.seeyon.apps.assetsmap.config.AssetsMapConfigProvider; import com.seeyon.apps.assetsmap.constants.AssetsMapConstans; import com.seeyon.apps.assetsmap.vo.AssetsCatagory; import com.seeyon.apps.assetsmap.vo.AssetsItem; import com.seeyon.apps.assetsmap.vo.BoundsQueryVo; import com.seeyon.apps.assetsmap.vo.RegionVo; import com.seeyon.ctp.common.AppContext; import com.seeyon.ctp.common.exceptions.BusinessException; import com.seeyon.ctp.common.po.ctpenumnew.CtpEnumItem; import com.seeyon.ctp.organization.bo.V3xOrgAccount; import com.seeyon.ctp.organization.manager.OrgManager; import com.seeyon.ctp.util.JDBCAgent; import com.seeyon.utils.form.*; import org.apache.commons.lang.StringUtils; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; public class AssetsQueryService { private AssetsMapConfigProvider configProvider = (AssetsMapConfigProvider) AppContext.getBean("assetsMapConfigProvider"); private OrgManager orgManager = (OrgManager) AppContext.getBean("orgManager"); private static final Log log = Log.get(AssetsQueryService.class); // 部门名称缓存,避免 N+1 查询 private Map deptCache = new HashMap<>(); private String getFormNo(){ return configProvider.getBizConfigByKey(AssetsMapConstans.ASSETSFORMNO); } private TableContext getMasterTableContext() throws Exception { return FormTableExecutor.master(getFormNo()); } public List queryAllZoneAssets() throws Exception { TableContext tableContext = FormTableExecutor.master(getFormNo()); List conditions = buildQueryCondition(); List formColumns = FormTableExecutor.query(tableContext, null, conditions, true); System.out.println("查询出的数据条数:" + formColumns.size()); // 用 Map 做 O(1) 查找,替代 List 遍历 Map regionMap = new HashMap<>(); for (FormColumn formColumn : formColumns) { Map fieldsMap = formColumn.getFieldsMap(); if (fieldsMap == null) { continue; } upsertRegionVos(regionMap, fieldsMap); } return new ArrayList<>(regionMap.values()); } private List buildQueryCondition() { long currentAccountId = AppContext.currentAccountId(); List conditions = new ArrayList<>(); conditions.add(FormWhereCondition.build().display("所属单位-").value(currentAccountId).concatFactor(ClauseFactor.OR)); conditions.add(FormWhereCondition.build().display("管理单位-").value(currentAccountId).concatFactor(ClauseFactor.OR)); return conditions; } /** * 视口懒加载:通过数据库层经纬度范围过滤,只查询视口内的资产 * 生成 SQL: WHERE 纬度 >= south AND 纬度 <= north AND 经度 >= west AND 经度 <= east */ public List queryAssetsByBounds(BoundsQueryVo vo) throws Exception { TableContext tableContext = FormTableExecutor.master(getFormNo()); List conditions = new ArrayList<>(); // 如果传了 assetId,按 资产编号 精确查询,忽略经纬度范围 if (StringUtils.isNotBlank(vo.getAssetId())) { conditions.add(FormWhereCondition.build().display("资产编号").value(vo.getAssetId())); } else { conditions.add(FormWhereCondition.build().display("经度-").between(vo.getWest(), vo.getEast())); conditions.add(FormWhereCondition.build().display("纬度-").between(vo.getSouth(), vo.getNorth())); } conditions.add(FormWhereCondition.build().display("经度-").clauseFactor(ClauseFactor.NOT_NULL)); conditions.add(FormWhereCondition.build().display("纬度-").clauseFactor(ClauseFactor.NOT_NULL)); // 代码层过滤权限,避免 LIKE 导致索引失效 boolean showAll = showAll(); log.info("是否允许查看所有数据: " + showAll); if (StringUtils.isNotBlank(vo.getOwner())) { conditions.add(FormWhereCondition.build().display("所属单位").value(vo.getOwner())); } if (StringUtils.isNotBlank(vo.getLevel2())) { conditions.add(FormWhereCondition.build().display("资产类型二级").value(vo.getLevel2())); } if (StringUtils.isNotBlank(vo.getLevel1())) { conditions.add(FormWhereCondition.build().display("资产类型一级").value(vo.getLevel1())); } if (StringUtils.isNotBlank(vo.getLevel3())) { conditions.add(FormWhereCondition.build().display("资产分类-").value(vo.getLevel3())); } System.out.println("开始查询资产数据"); List formColumns = FormTableExecutor.query(tableContext, null, conditions, true); // 代码层过滤权限(当前单位及下级单位) if (!showAll && formColumns != null) { List unitIds = getCurrentAndChildUnitIds(); formColumns = formColumns.stream() .filter(col -> { Map fields = col.getFieldsMap(); if (fields == null) return false; Object permObj = fields.get("查看权限-选单位"); String perm = permObj == null ? null : permObj.toString(); return hasPermission(perm, unitIds); }) .collect(Collectors.toList()); } log.info("权限过滤后的数据条数: " + formColumns.size()); Map regionMap = new HashMap<>(); for (FormColumn formColumn : formColumns) { Map fieldsMap = formColumn.getFieldsMap(); if (fieldsMap == null) continue; upsertRegionVos(regionMap, fieldsMap); } log.info("返回资产数据结果"); return new ArrayList<>(regionMap.values()); } private boolean showAll() throws Exception { V3xOrgAccount currentAccount = orgManager.getAccountById(AppContext.currentAccountId()); if(currentAccount == null) { return false; }else { return currentAccount.getName().contains("国清办") || currentAccount.getName().contains("国资局") || currentAccount.getName().contains("软件开发"); } } /** * 获取当前登录者单位及其所有下级单位的ID列表 */ private List getCurrentAndChildUnitIds() { List ids = new ArrayList<>(); long currentAccountId = AppContext.currentAccountId(); JDBCAgent jdbcAgent = new JDBCAgent(); try { // 查当前单位的 path jdbcAgent.execute("select path from org_unit where id = ?", currentAccountId); List list = jdbcAgent.resultSetToList(); if (list == null || list.isEmpty()) { ids.add(String.valueOf(currentAccountId)); return ids; } Map unitMap = (Map) list.get(0); String rootPath = unitMap.get("path") + ""; // 查所有子单位 jdbcAgent.execute("select id from org_unit where path like ?", rootPath + "%"); list = jdbcAgent.resultSetToList(); if (list != null) { for (Object o : list) { Map map = (Map) o; ids.add(map.get("id") + ""); } } } catch (Exception e) { e.printStackTrace(); ids.add(String.valueOf(currentAccountId)); } finally { jdbcAgent.close(); } return ids; } /** * 检查权限字段是否包含当前单位或下级单位 * permField: 逗号分隔的单位ID字符串,如 "100,200,300" * unitIds: 当前单位及下级单位ID列表 */ private boolean hasPermission(String permField, List unitIds) { if (StringUtils.isBlank(permField) || unitIds == null || unitIds.isEmpty()) { return false; } // 用逗号分隔后逐一匹配 String[] permParts = permField.split(","); for (String part : permParts) { String trimmed = part.trim(); if (unitIds.contains(trimmed)) { return true; } } return false; } /** * 保存资产边界数据到表单字段"资产边界数据" * polygonData: JSON格式,包含polygon坐标和color颜色 */ public void saveAssetPolygon(String assetId, String polygonData) throws Exception { if (StringUtils.isBlank(assetId)) { throw new IllegalArgumentException("assetId不能为空"); } List updateFields = new ArrayList<>(); updateFields.add(FormUpdateField.build().display("资产边界数据").value(polygonData)); List conditions = new ArrayList<>(); conditions.add(FormWhereCondition.build().display("id").value(assetId)); FormTableExecutor.update(getMasterTableContext(),updateFields,conditions); } public String getDetailBaseUrlByAssetsType(String assetsType) throws Exception { TableContext master = FormTableExecutor.master(configProvider.getBizConfigByKey(AssetsMapConstans.ASSETSDETAILURLCONFIGFORMNO)); List conditions = new ArrayList<>(); if(StringUtils.isBlank(assetsType)) { conditions.add(FormWhereCondition.build().display("资产类型").value("不动产")); }else { conditions.add(FormWhereCondition.build().display("资产类型").value(assetsType)); } FormColumn formColumn = FormTableExecutor.queryOne(master, conditions, true); if(formColumn == null) { return null; } Object urlObj = formColumn.getFieldsMap().get("前置地址"); return urlObj == null ? null : urlObj.toString(); } /** * 查询单个资产的边界数据 */ public Map getAssetBound(String assetId) throws Exception { if (StringUtils.isBlank(assetId)) { return null; } TableContext tableContext = FormTableExecutor.master(getFormNo()); List conditions = new ArrayList<>(); conditions.add(FormWhereCondition.build().display("id").value(assetId)); FormColumn formColumn = FormTableExecutor.queryOne(tableContext, conditions, true); if (formColumn == null) { log.info("getAssetBound: 未查到记录, assetId=" + assetId); return null; } Map fieldsMap = formColumn.getFieldsMap(); if (fieldsMap == null) { return null; } log.info("getAssetBound: 查到记录, assetId=" + assetId + ", 资产编号=" + fieldsMap.get("资产编号") + ", 资产名称=" + fieldsMap.get("资产名称")); log.info("getAssetBound: 资产边界数据字段值=" + fieldsMap.get("资产边界数据") + ", 类型=" + (fieldsMap.get("资产边界数据") == null ? "null" : fieldsMap.get("资产边界数据").getClass().getName())); Object boundObj = fieldsMap.get("资产边界数据"); String boundData = boundObj == null ? null : boundObj.toString(); Map result = new HashMap<>(); result.put("boundData", boundData); return result; } /** * 返回当前登录者单位的树形结构 * path 每4字符代表一个层级,如 0000 → 00000001 → 000000010001 */ public List> getDeptSelectOptions() throws BusinessException { long currentAccountId = AppContext.currentAccountId(); JDBCAgent jdbcAgent = new JDBCAgent(); try { // 查当前单位的 path jdbcAgent.execute("select path from org_unit where id = ?", currentAccountId); List list = jdbcAgent.resultSetToList(); if (list == null || list.isEmpty()) { return new ArrayList<>(); } Map unitMap = (Map) list.get(0); String rootPath = unitMap.get("path") + ""; // 查所有子单位 jdbcAgent.execute("select id, name, path from org_unit where path like ?", rootPath + "%"); list = jdbcAgent.resultSetToList(); // 用 LinkedHashMap 保持插入顺序,path → node Map> nodeMap = new LinkedHashMap<>(); for (Object o : list) { Map map = (Map) o; String path = map.get("path") + ""; Map node = new LinkedHashMap<>(); node.put("id", map.get("id") + ""); node.put("name", map.get("name") + ""); node.put("path", path); node.put("children", new ArrayList>()); nodeMap.put(path, node); } // 按 path 长度排序(浅层先处理),逐个挂到父节点 List> roots = new ArrayList<>(); List sortedPaths = new ArrayList<>(nodeMap.keySet()); Collections.sort(sortedPaths, Comparator.comparingInt(String::length)); for (String path : sortedPaths) { Map node = nodeMap.get(path); if (path.equals(rootPath) || path.length() <= 4) { roots.add(node); } else { // 父节点 path = 去掉最后4字符 String parentPath = path.substring(0, path.length() - 4); Map parent = nodeMap.get(parentPath); if (parent != null) { ((List>) parent.get("children")).add(node); } else { // 找不到父节点,作为根节点 roots.add(node); } } } return roots; } catch (Exception e) { e.printStackTrace(); return new ArrayList<>(); } finally { jdbcAgent.close(); } } public Map getAssetsCatagorySelectOptions() throws Exception { TableContext tableContext = FormTableExecutor.master(getFormNo()); Map enumItemMap = EnumMapUtils.getEnumItemMap(tableContext.getTableBean(), "资产分类"); if(enumItemMap == null) { return new HashMap<>(); } Map resultMap = new HashMap<>(); for (String key : enumItemMap.keySet()) { CtpEnumItem tempItem = enumItemMap.get(key); AssetsCatagory assetsCatagory = new AssetsCatagory(); assetsCatagory.setId(tempItem.getId() + ""); assetsCatagory.setShowValue(key); resultMap.put(key,assetsCatagory); } return resultMap; } /** * 生成 region 的唯一 key,用于 Map 查找 */ private String regionKey(double lat, double lng, String address) { return lat + "_" + lng + "_" + address; } /** * 优化:接收 Map 而非 List,O(1) 查找替代 O(n) 遍历 */ public void upsertRegionVos(Map regionMap, Map fieldsMap) { BigDecimal latObj = (BigDecimal) fieldsMap.get("纬度-"); BigDecimal lngObj = (BigDecimal) fieldsMap.get("经度-"); if (latObj == null || lngObj == null) { return; } double lat = Double.parseDouble(latObj.toString()); double lng = Double.parseDouble(lngObj.toString()); Object addrObj = fieldsMap.get("所在位置"); String address = addrObj == null ? null : addrObj.toString(); if (StringUtils.isBlank(address)) { return; } String assetId = getStringValue(fieldsMap, "id"); AssetsItem newAsset = buildAssetsItem(fieldsMap); String key = regionKey(lat, lng, address); RegionVo regionVo = regionMap.get(key); if (regionVo != null) { // 判断资产是否已存在 boolean exists = regionVo.getAssets().stream() .anyMatch(a -> a.getId().equals(assetId)); if (!exists) { regionVo.getAssets().add(newAsset); } } else { // 新建区域 RegionVo newRegion = new RegionVo(); newRegion.setLat(lat); newRegion.setLng(lng); newRegion.setAddress(address); newRegion.setManager(getStringValue(fieldsMap,"管理单位")); List assets = new ArrayList<>(); assets.add(newAsset); newRegion.setAssets(assets); regionMap.put(key, newRegion); } } /** * 优化:带缓存的单位名称查询 */ private String getUnitName(Long unitId) { if (unitId == null) { return null; } if (deptCache.containsKey(unitId)) { return deptCache.get(unitId); } try { V3xOrgAccount account = orgManager.getAccountById(unitId); String name = account != null ? account.getName() : null; deptCache.put(unitId, name); return name; } catch (Exception e) { deptCache.put(unitId, null); return null; } } private AssetsItem buildAssetsItem(Map fieldsMap) { AssetsItem assetsItem = new AssetsItem(); assetsItem.setId(getStringValue(fieldsMap, "id")); assetsItem.setArea(fieldsMap.get("数量面积") == null ? null : Double.parseDouble(fieldsMap.get("数量面积") + "")); assetsItem.setAssetCode(getStringValue(fieldsMap, "资产编号")); assetsItem.setRemark(getStringValue(fieldsMap, "备注")); assetsItem.setAssetName(getStringValue(fieldsMap, "资产名称")); assetsItem.setAddress(getStringValue(fieldsMap, "所在位置")); assetsItem.setLat(Optional.ofNullable(fieldsMap.get("纬度-")).map(Object::toString).orElse(null)); assetsItem.setLng(Optional.ofNullable(fieldsMap.get("经度-")).map(Object::toString).orElse(null)); assetsItem.setManager(getStringValue(fieldsMap,"管理单位")); assetsItem.setAssetType(getStringValue(fieldsMap, "资产类型一级")); return assetsItem; } private String getStringValue(Map fieldsMap, String key) { Object val = fieldsMap.get(key); return val == null ? null : val.toString(); } /** * 优化:一次查询全量数据,内存中分类计数,替代3次独立查询 */ public Map countAllStats() throws Exception { TableContext tableContext = FormTableExecutor.master(getFormNo()); List formColumns = FormTableExecutor.query(tableContext, null, null, true); int total = 0; int rented = 0; int vacant = 0; String rentedValue = EnumMapUtils.getEnumItemValueByDisplayValue(tableContext.getTableBean(), "资产状态", "已租"); String vacantValue = EnumMapUtils.getEnumItemValueByDisplayValue(tableContext.getTableBean(), "资产状态", "待租"); for (FormColumn col : formColumns) { Map fields = col.getFieldsMap(); if (fields == null) continue; total++; String status = fields.get("资产状态") == null ? null : fields.get("资产状态") + ""; if (rentedValue != null && rentedValue.equals(status)) { rented++; } else if (vacantValue != null && vacantValue.equals(status)) { vacant++; } } Map result = new HashMap<>(); result.put("total", total); result.put("rented", rented); result.put("vacant", vacant); return result; } public Integer countAssets(String type) throws Exception { TableContext tableContext = FormTableExecutor.master(getFormNo()); List conditions = new ArrayList(); if(type == null || "0".equals(type)) { conditions = null; } if("1".equals(type)) { conditions.add(FormWhereCondition.build().display("资产状态").value(EnumMapUtils.getEnumItemValueByDisplayValue(tableContext.getTableBean(),"资产状态","已租"))); } if("2".equals(type)) { conditions.add(FormWhereCondition.build().display("资产状态").value(EnumMapUtils.getEnumItemValueByDisplayValue(tableContext.getTableBean(),"资产状态","待租"))); } List formColumns = FormTableExecutor.query(tableContext,null, conditions, true); return formColumns.size(); } /** * 按筛选条件分页查询资产列表(不走视口),用于地图右侧列表展示 * 返回格式: {records: [...], total: n} */ public Map queryAssetList(BoundsQueryVo vo) throws Exception { TableContext tableContext = FormTableExecutor.master(getFormNo()); List conditions = new ArrayList<>(); if (StringUtils.isNotBlank(vo.getOwner())) { conditions.add(FormWhereCondition.build().display("所属单位").value(vo.getOwner())); } if (StringUtils.isNotBlank(vo.getLevel1())) { conditions.add(FormWhereCondition.build().display("资产类型一级").value(vo.getLevel1())); } if (StringUtils.isNotBlank(vo.getLevel2())) { conditions.add(FormWhereCondition.build().display("资产类型二级").value(vo.getLevel2())); } if (StringUtils.isNotBlank(vo.getLevel3())) { conditions.add(FormWhereCondition.build().display("资产分类-").value(vo.getLevel3())); } if (StringUtils.isNotBlank(vo.getKeyword())) { conditions.add(FormWhereCondition.build().display("资产名称").value(vo.getKeyword()).clauseFactor(ClauseFactor.LIKE) .concatFactor(ClauseFactor.OR)); conditions.add(FormWhereCondition.build().display("资产编号").value(vo.getKeyword()).clauseFactor(ClauseFactor.LIKE)); } conditions.add(FormWhereCondition.build().display("经度-").clauseFactor(ClauseFactor.NOT_NULL)); conditions.add(FormWhereCondition.build().display("纬度-").clauseFactor(ClauseFactor.NOT_NULL)); // 先查全量用于权限过滤 List formColumns = FormTableExecutor.query(tableContext, null, conditions, true); // 权限过滤(当前单位及下级单位) boolean showAll = showAll(); if (!showAll) { List unitIds = getCurrentAndChildUnitIds(); formColumns = formColumns.stream() .filter(col -> { Map fields = col.getFieldsMap(); if (fields == null) return false; String perm = (String) fields.get("查看权限-选单位"); return hasPermission(perm, unitIds); }) .collect(Collectors.toList()); } int total = formColumns.size(); // 分页 int pageNum = vo.getPageNum() != null && vo.getPageNum() > 0 ? vo.getPageNum() : 1; int pageSize = vo.getPageSize() != null && vo.getPageSize() > 0 ? vo.getPageSize() : 10; int fromIndex = (pageNum - 1) * pageSize; int toIndex = Math.min(fromIndex + pageSize, total); List records = new ArrayList<>(); if (fromIndex < total) { List pageColumns = formColumns.subList(fromIndex, toIndex); for (FormColumn col : pageColumns) { Map fieldsMap = col.getFieldsMap(); if (fieldsMap == null) continue; records.add(buildAssetsItem(fieldsMap)); } } Map result = new HashMap<>(); result.put("records", records); result.put("total", total); return result; } public List queryAssetsByKeyword(String keyword) throws Exception { List conditions = new ArrayList(); if(StringUtils.isNotBlank(keyword)){ conditions.add(FormWhereCondition.build().display("所在位置").value(keyword).concatFactor(ClauseFactor.OR).clauseFactor(ClauseFactor.LIKE)); } TableContext tableContext = FormTableExecutor.master(getFormNo()); List formColumns = FormTableExecutor.pageQuery(tableContext,null, conditions, 1,10,true); List assetsItems = new ArrayList(); for (FormColumn formColumn : formColumns) { AssetsItem assetsItem = buildAssetsItem(formColumn.getFieldsMap()); assetsItems.add(assetsItem); } return assetsItems; } }