Files
AssetsMap/src/main/java/com/seeyon/apps/assetsmap/service/AssetsQueryService.java
2026-06-18 17:56:31 +08:00

564 lines
25 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<Long, String> deptCache = new HashMap<>();
private String getFormNo(){
return configProvider.getBizConfigByKey(AssetsMapConstans.ASSETSFORMNO);
}
private TableContext getMasterTableContext() throws Exception {
return FormTableExecutor.master(getFormNo());
}
public List<RegionVo> queryAllZoneAssets() throws Exception {
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormWhereCondition> conditions = buildQueryCondition();
List<FormColumn> formColumns = FormTableExecutor.query(tableContext, null, conditions, true);
System.out.println("查询出的数据条数:" + formColumns.size());
// 用 Map 做 O(1) 查找,替代 List 遍历
Map<String, RegionVo> regionMap = new HashMap<>();
for (FormColumn formColumn : formColumns) {
Map<String, Object> fieldsMap = formColumn.getFieldsMap();
if (fieldsMap == null) {
continue;
}
upsertRegionVos(regionMap, fieldsMap);
}
return new ArrayList<>(regionMap.values());
}
private List<FormWhereCondition> buildQueryCondition() {
long currentAccountId = AppContext.currentAccountId();
List<FormWhereCondition> 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<RegionVo> queryAssetsByBounds(BoundsQueryVo vo) throws Exception {
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormWhereCondition> 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<FormColumn> formColumns = FormTableExecutor.query(tableContext, null, conditions, true);
// 代码层过滤权限(当前单位及下级单位)
if (!showAll && formColumns != null) {
List<String> unitIds = getCurrentAndChildUnitIds();
formColumns = formColumns.stream()
.filter(col -> {
Map<String, Object> 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<String, RegionVo> regionMap = new HashMap<>();
for (FormColumn formColumn : formColumns) {
Map<String, Object> 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<String> getCurrentAndChildUnitIds() {
List<String> 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<String> 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<FormUpdateField> updateFields = new ArrayList<>();
updateFields.add(FormUpdateField.build().display("资产边界数据").value(polygonData));
List<FormWhereCondition> 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<FormWhereCondition> 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<String, String> getAssetBound(String assetId) throws Exception {
if (StringUtils.isBlank(assetId)) {
return null;
}
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormWhereCondition> 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<String, Object> 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<String, String> result = new HashMap<>();
result.put("boundData", boundData);
return result;
}
/**
* 返回当前登录者单位的树形结构
* path 每4字符代表一个层级如 0000 → 00000001 → 000000010001
*/
public List<Map<String, Object>> 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<String, Map<String, Object>> nodeMap = new LinkedHashMap<>();
for (Object o : list) {
Map map = (Map) o;
String path = map.get("path") + "";
Map<String, Object> node = new LinkedHashMap<>();
node.put("id", map.get("id") + "");
node.put("name", map.get("name") + "");
node.put("path", path);
node.put("children", new ArrayList<Map<String, Object>>());
nodeMap.put(path, node);
}
// 按 path 长度排序(浅层先处理),逐个挂到父节点
List<Map<String, Object>> roots = new ArrayList<>();
List<String> sortedPaths = new ArrayList<>(nodeMap.keySet());
Collections.sort(sortedPaths, Comparator.comparingInt(String::length));
for (String path : sortedPaths) {
Map<String, Object> 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<String, Object> parent = nodeMap.get(parentPath);
if (parent != null) {
((List<Map<String, Object>>) parent.get("children")).add(node);
} else {
// 找不到父节点,作为根节点
roots.add(node);
}
}
}
return roots;
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
} finally {
jdbcAgent.close();
}
}
public Map<String,AssetsCatagory> getAssetsCatagorySelectOptions() throws Exception {
TableContext tableContext = FormTableExecutor.master(getFormNo());
Map<String, CtpEnumItem> enumItemMap = EnumMapUtils.getEnumItemMap(tableContext.getTableBean(), "资产分类");
if(enumItemMap == null) {
return new HashMap<>();
}
Map<String,AssetsCatagory> 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 而非 ListO(1) 查找替代 O(n) 遍历
*/
public void upsertRegionVos(Map<String, RegionVo> regionMap, Map<String, Object> 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<AssetsItem> 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<String, Object> 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<String, Object> fieldsMap, String key) {
Object val = fieldsMap.get(key);
return val == null ? null : val.toString();
}
/**
* 优化一次查询全量数据内存中分类计数替代3次独立查询
*/
public Map<String, Integer> countAllStats() throws Exception {
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormColumn> 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<String, Object> 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<String, Integer> 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<FormWhereCondition> conditions = new ArrayList<FormWhereCondition>();
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<FormColumn> formColumns = FormTableExecutor.query(tableContext,null, conditions, true);
return formColumns.size();
}
/**
* 按筛选条件分页查询资产列表(不走视口),用于地图右侧列表展示
* 返回格式: {records: [...], total: n}
*/
public Map<String, Object> queryAssetList(BoundsQueryVo vo) throws Exception {
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormWhereCondition> 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<FormColumn> formColumns = FormTableExecutor.query(tableContext, null, conditions, true);
// 权限过滤(当前单位及下级单位)
boolean showAll = showAll();
if (!showAll) {
List<String> unitIds = getCurrentAndChildUnitIds();
formColumns = formColumns.stream()
.filter(col -> {
Map<String, Object> 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<AssetsItem> records = new ArrayList<>();
if (fromIndex < total) {
List<FormColumn> pageColumns = formColumns.subList(fromIndex, toIndex);
for (FormColumn col : pageColumns) {
Map<String, Object> fieldsMap = col.getFieldsMap();
if (fieldsMap == null) continue;
records.add(buildAssetsItem(fieldsMap));
}
}
Map<String, Object> result = new HashMap<>();
result.put("records", records);
result.put("total", total);
return result;
}
public List<AssetsItem> queryAssetsByKeyword(String keyword) throws Exception {
List<FormWhereCondition> conditions = new ArrayList<FormWhereCondition>();
if(StringUtils.isNotBlank(keyword)){
conditions.add(FormWhereCondition.build().display("所在位置").value(keyword).concatFactor(ClauseFactor.OR).clauseFactor(ClauseFactor.LIKE));
}
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormColumn> formColumns = FormTableExecutor.pageQuery(tableContext,null, conditions, 1,10,true);
List<AssetsItem> assetsItems = new ArrayList<AssetsItem>();
for (FormColumn formColumn : formColumns) {
AssetsItem assetsItem = buildAssetsItem(formColumn.getFieldsMap());
assetsItems.add(assetsItem);
}
return assetsItems;
}
}