完成初版

This commit is contained in:
2026-07-22 17:23:49 +08:00
parent cf40ccbdb0
commit bf390e8f7e
37 changed files with 4387 additions and 102 deletions

View File

@@ -4,6 +4,11 @@ public enum AssetsMapConstans {
plugin("assetsmap",""),
ASSETSFORMNO("assetsformno","资产表单"),
ASSETSDETAILURLCONFIGFORMNO("","资产详情URL配置表单"),
FILESYSTEMHOST("http://localhost:8080","SeeyonFileSystem网盘服务地址"),
FILESYSAPPID("","SeeyonFileSystemAppId"),
FILESYSAUSER("","文档系统用户"),
BOUNDCONFIGUPDATELOGFORMNO("","边界数据变更日志表编码"),
FORMLOGINNAME("","登录名"),
;
private String defaultValue;

View File

@@ -0,0 +1,65 @@
package com.seeyon.apps.assetsmap.fieldCtrl;
import com.seeyon.cap4.form.bean.ParamDefinition;
import com.seeyon.cap4.form.bean.fieldCtrl.FormFieldCustomCtrl;
import com.seeyon.cap4.form.util.Enums;
/**
* 关联附件控件
* 弹框查询网盘文件列表,勾选后获取预览链接回填到表单
*/
public class FileRefFieldCtrl extends FormFieldCustomCtrl {
@Override
public String getPCInjectionInfo() {
return "{path:'apps_res/cap/customCtrlResources/fileUploadResources',jsUri:'/js/fileRef.js',initMethod:'init',nameSpace:'" + getNameSpace() + "'}";
}
@Override
public String getMBInjectionInfo() {
return "{path:'apps_res/cap/customCtrlResources/fileUploadResources',jsUri:'/js/fileRef.js',initMethod:'init',nameSpace:'" + getNameSpace() + "'}";
}
@Override
public String getKey() {
return "88661234567890123457";
}
@Override
public boolean canUse(Enums.FormType formType) {
return true;
}
@Override
public String[] getDefaultVal(String s) {
return new String[0];
}
public String getNameSpace() {
return "field_" + this.getKey();
}
public String getFieldLength() {
return "5000";
}
@Override
public String getText() {
return "关联附件";
}
@Override
public Enums.FieldType[] getFieldType() {
return new Enums.FieldType[]{Enums.FieldType.LONGTEXT};
}
@Override
public void init() {
setPluginId("assetsmap");
setIcon("cap-icon-custom-button");
ParamDefinition param = new ParamDefinition();
param.setParamType(Enums.ParamType.text);
addDefinition(param);
}
}

View File

@@ -0,0 +1,66 @@
package com.seeyon.apps.assetsmap.fieldCtrl;
import com.seeyon.cap4.form.bean.ParamDefinition;
import com.seeyon.cap4.form.bean.fieldCtrl.FormFieldCustomCtrl;
import com.seeyon.cap4.form.util.Enums;
/**
* 从明细表获取数据重新生成边界数据控件
*/
public class GBCFromSubTableFieldCtrl extends FormFieldCustomCtrl {
@Override
public String getPCInjectionInfo() {
return "{path:'apps_res/cap/customCtrlResources/GBCFromSubTableResources',jsUri:'/js/fileRef.js',initMethod:'init',nameSpace:'" + getNameSpace() + "'}";
}
@Override
public String getMBInjectionInfo() {
return "{path:'http://bgcSubTableBtn.v5.cmp/v1.0.0/',weixinpath:'bgcSubTableBtn',jsUri:'js/bgcSubTableBtn.js',initMethod:'init',nameSpace:'" + getNameSpace() + "'}";
}
@Override
public String getKey() {
return "88661234567890123656";
}
@Override
public boolean canUse(Enums.FormType formType) {
return true;
}
@Override
public String[] getDefaultVal(String s) {
return new String[0];
}
public String getNameSpace() {
return "field_" + this.getKey();
}
public String getFieldLength() {
return "5000";
}
@Override
public String getText() {
return "边界配置重新生成";
}
@Override
public Enums.FieldType[] getFieldType() {
return new Enums.FieldType[]{Enums.FieldType.LONGTEXT};
}
@Override
public void init() {
setPluginId("assetsmap");
setIcon("cap-icon-custom-button");
ParamDefinition param = new ParamDefinition();
param.setParamType(Enums.ParamType.text);
addDefinition(param);
}
}

View File

@@ -0,0 +1,58 @@
package com.seeyon.apps.assetsmap.node;
import cn.hutool.log.Log;
import com.seeyon.apps.assetsmap.constants.AssetsMapConstans;
import com.seeyon.apps.assetsmap.service.FileUploadService;
import com.seeyon.apps.common.workflow.constants.WorkFlowType;
import com.seeyon.apps.common.workflow.node.ACommonSuperNode;
import com.seeyon.apps.ext.workflow.vo.FieldDataVo;
import com.seeyon.apps.ext.workflow.vo.FormDataVo;
import com.seeyon.apps.ext.workflow.vo.SuperNodeContext;
import com.seeyon.cap4.form.bean.FormDataMasterBean;
import com.seeyon.ctp.common.AppContext;
public class AssetsCreateFolderNode extends ACommonSuperNode {
private FileUploadService fileUploadService = (FileUploadService) AppContext.getBean("fileUploadService");
private static final Log log = Log.get(AssetsCreateFolderNode.class);
@Override
public String getPluginId() {
return AssetsMapConstans.getPluginId();
}
@Override
public String getFormParse() {
return "json";
}
@Override
public SuperNodeContext proceed(String s, FormDataVo formDataVo, FormDataMasterBean formDataMasterBean) throws Exception {
SuperNodeContext context = new SuperNodeContext();
context.setNeedSave(true);
log.info("开始新增资产影像资料文件夹");
try {
FieldDataVo fieldData = formDataVo.getFieldData("资产编号");
String assetsNo = fieldData.getStringValue();
fileUploadService.createFolder(assetsNo,assetsNo);
}catch (Exception e){
log.warn(e.getMessage());
}
return context.success("新增资产影像资料文件夹超级节点业务执行结束");
}
@Override
public WorkFlowType[] getTypes() {
return new WorkFlowType[] {WorkFlowType.superNode};
}
@Override
public String getNodeId() {
return "ndesgkzcyxzl20260715";
}
@Override
public String getNodeName() {
return "新增资产影像资料文件夹";
}
}

View File

@@ -0,0 +1,48 @@
package com.seeyon.apps.assetsmap.service;
import com.seeyon.apps.assetsmap.config.AssetsMapConfigProvider;
import com.seeyon.apps.assetsmap.constants.AssetsMapConstans;
import com.seeyon.apps.assetsmap.vo.AssetsItem;
import com.seeyon.ctp.common.AppContext;
import com.seeyon.ctp.common.authenticate.domain.User;
import com.seeyon.utils.form.FormSaveUtil;
import com.seeyon.v3x.services.form.FormFactory;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class AssetsBoundUpdateLogService {
private AssetsMapConfigProvider configProvider = (AssetsMapConfigProvider) AppContext.getBean("assetsMapConfigProvider");
private FormFactory factory = (FormFactory) AppContext.getBean("formFactory");
private AssetsQueryService assetsQueryService = (AssetsQueryService) AppContext.getBean("assetsQueryService");
private DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private String getFormNo(){
return configProvider.getBizConfigByKey(AssetsMapConstans.BOUNDCONFIGUPDATELOGFORMNO);
}
public void recordLog(String configValue,String formId,String isSubForm) {
try {
User user = AppContext.getCurrentUser();
Long userId = user.getId();
String loginName = configProvider.getBizConfigByKey(AssetsMapConstans.FORMLOGINNAME);
AssetsItem assetsItem = assetsQueryService.getDetailById(formId);
Map<String,Object> mainData = new HashMap<>();
if(assetsItem != null) {
mainData.put("资产编码",assetsItem.getAssetCode());
mainData.put("资产名称",assetsItem.getAssetName());
}
mainData.put("数据来源",isSubForm.equals("1") ? "明细表生成" : "主表生成");
mainData.put("更新时间",df.format(new Date()));
mainData.put("更新结果",configValue);
mainData.put("操作人员",userId);
FormSaveUtil.formSave(loginName,getFormNo(),factory,mainData,null);
}catch (Exception e) {
}
}
}

View File

@@ -1,12 +1,11 @@
package com.seeyon.apps.assetsmap.service;
import cn.hutool.log.Log;
import com.fasterxml.jackson.core.type.TypeReference;
import com.seeyon.aicloud.common.JsonUtils;
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.apps.assetsmap.vo.*;
import com.seeyon.ctp.common.AppContext;
import com.seeyon.ctp.common.exceptions.BusinessException;
import com.seeyon.ctp.common.po.ctpenumnew.CtpEnumItem;
@@ -24,6 +23,7 @@ public class AssetsQueryService {
private AssetsMapConfigProvider configProvider = (AssetsMapConfigProvider) AppContext.getBean("assetsMapConfigProvider");
private OrgManager orgManager = (OrgManager) AppContext.getBean("orgManager");
private FileUploadService fileUploadService = (FileUploadService) AppContext.getBean("fileUploadService");
private static final Log log = Log.get(AssetsQueryService.class);
// 部门名称缓存,避免 N+1 查询
private Map<Long, String> deptCache = new HashMap<>();
@@ -133,6 +133,21 @@ public class AssetsQueryService {
}
}
public AssetsItem getDetailById(String id) throws BusinessException {
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormWhereCondition> conditions = new ArrayList<>();
conditions.add(FormWhereCondition.build().display("ID").value(id));
FormColumn formColumn = FormTableExecutor.queryOne(tableContext, conditions, true);
if(formColumn == null || formColumn.getId() == null) {
return new AssetsItem();
}
AssetsItem assetsItem = new AssetsItem();
Map<String, Object> fieldsMap = formColumn.getFieldsMap();
assetsItem.setAssetName((String)fieldsMap.get("资产编号"));
assetsItem.setAssetName((String)fieldsMap.get("名称编号"));
return assetsItem;
}
/**
* 获取当前登录者单位及其所有下级单位的ID列表
*/
@@ -209,7 +224,7 @@ public class AssetsQueryService {
TableContext master = FormTableExecutor.master(configProvider.getBizConfigByKey(AssetsMapConstans.ASSETSDETAILURLCONFIGFORMNO));
List<FormWhereCondition> conditions = new ArrayList<>();
if(StringUtils.isBlank(assetsType)) {
conditions.add(FormWhereCondition.build().display("资产类型").value("不动产"));
conditions.add(FormWhereCondition.build().display("资产类型").value("默认"));
}else {
conditions.add(FormWhereCondition.build().display("资产类型").value(assetsType));
}
@@ -221,6 +236,42 @@ public class AssetsQueryService {
return urlObj == null ? null : urlObj.toString();
}
public Map<String,Object> reComputedBoundConfig(){
HashMap<String, Object> map = new HashMap<>();
map.put("fieldName","");
map.put("value","");
return map;
}
/**
* 根据资产编号查询资产详情(名称、经纬度、边界数据)
*/
public Map<String, Object> getAssetDetail(String assetCode) throws Exception {
if (StringUtils.isBlank(assetCode)) {
return null;
}
TableContext tableContext = FormTableExecutor.master(getFormNo());
List<FormWhereCondition> conditions = new ArrayList<>();
conditions.add(FormWhereCondition.build().display("资产编号").value(assetCode));
FormColumn formColumn = FormTableExecutor.queryOne(tableContext, conditions, true);
if (formColumn == null) {
return null;
}
Map<String, Object> fieldsMap = formColumn.getFieldsMap();
if (fieldsMap == null) {
return null;
}
Map<String, Object> result = new HashMap<>();
result.put("id", getStringValue(fieldsMap, "id"));
result.put("assetCode", getStringValue(fieldsMap, "资产编号"));
result.put("assetName", getStringValue(fieldsMap, "资产名称"));
result.put("lat", Optional.ofNullable(fieldsMap.get("纬度-")).map(Object::toString).orElse(null));
result.put("lng", Optional.ofNullable(fieldsMap.get("经度-")).map(Object::toString).orElse(null));
Object boundObj = fieldsMap.get("资产边界数据");
result.put("boundData", boundObj == null ? null : boundObj.toString());
return result;
}
/**
* 查询单个资产的边界数据
*/
@@ -315,7 +366,7 @@ public class AssetsQueryService {
public Map<String,AssetsCatagory> getAssetsCatagorySelectOptions() throws Exception {
TableContext tableContext = FormTableExecutor.master(getFormNo());
Map<String, CtpEnumItem> enumItemMap = EnumMapUtils.getEnumItemMap(tableContext.getTableBean(), "资产分类");
Map<String, CtpEnumItem> enumItemMap = EnumMapUtils.getEnumItemMap(tableContext.getTableBean(), "资产分类-");
if(enumItemMap == null) {
return new HashMap<>();
}
@@ -413,7 +464,8 @@ public class AssetsQueryService {
AssetsItem assetsItem = new AssetsItem();
assetsItem.setId(getStringValue(fieldsMap, "id"));
assetsItem.setArea(fieldsMap.get("数量面积") == null ? null : Double.parseDouble(fieldsMap.get("数量面积") + ""));
assetsItem.setAssetCode(getStringValue(fieldsMap, "资产编号"));
String assetsCode = getStringValue(fieldsMap, "资产编号");
assetsItem.setAssetCode(assetsCode);
assetsItem.setRemark(getStringValue(fieldsMap, "备注"));
assetsItem.setAssetName(getStringValue(fieldsMap, "资产名称"));
assetsItem.setAddress(getStringValue(fieldsMap, "所在位置"));
@@ -421,6 +473,33 @@ public class AssetsQueryService {
assetsItem.setLng(Optional.ofNullable(fieldsMap.get("经度-")).map(Object::toString).orElse(null));
assetsItem.setManager(getStringValue(fieldsMap,"管理单位"));
assetsItem.setAssetType(getStringValue(fieldsMap, "资产类型一级"));
assetsItem.setTroubledAssets(getStringValue(fieldsMap,"是否问题资产-主表"));
// 媒体字段JSON数组字符串解析为FileItemVO列表
try {
String folderId = fileUploadService.getFolderIdByBizId(assetsCode);
if(folderId != null) {
FileListVO fileListVO = fileUploadService.listFolderChildren(folderId);
if(fileListVO != null) {
List<FileItemVO> imgs = new ArrayList<>();
List<FileItemVO> videos = new ArrayList<>();
for (FileItemVO file : fileListVO.getFiles()) {
String mimeType = file.getMimeType();
if(mimeType.startsWith("image")){
imgs.add(file);
}else if(mimeType.startsWith("video")){
videos.add(file);
}
}
assetsItem.setImages(imgs);
assetsItem.setVideos(videos);
}
}
// assetsItem.setPanoramas();
// assetsItem.setAttachments();
}catch (Exception e){
}
return assetsItem;
}
@@ -429,6 +508,23 @@ public class AssetsQueryService {
return val == null ? null : val.toString();
}
/**
* 解析JSON数组字符串为FileItemVO列表
* 字段值格式: [{"id":null,"name":"xxx.png","previewUrl":"http://..."}]
*/
private List<FileItemVO> parseFileItemList(Map<String, Object> fieldsMap, String key) {
String json = getStringValue(fieldsMap, key);
if (json == null || json.trim().isEmpty() || "[]".equals(json.trim())) {
return new ArrayList<>();
}
try {
return JsonUtils.parseObject(json, new TypeReference<List<FileItemVO>>() {});
} catch (Exception e) {
log.warn("解析媒体字段失败, key=" + key + ", value=" + json, e);
return new ArrayList<>();
}
}
/**
* 优化一次查询全量数据内存中分类计数替代3次独立查询
*/
@@ -498,9 +594,10 @@ public class AssetsQueryService {
conditions.add(FormWhereCondition.build().display("资产分类-").value(vo.getLevel3()));
}
if (StringUtils.isNotBlank(vo.getKeyword())) {
// keyword 与 owner 用 AND 连接,缩小范围而非扩大
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));
.concatFactor(ClauseFactor.OR).startWithBracket(1));
conditions.add(FormWhereCondition.build().display("资产编号").value(vo.getKeyword()).clauseFactor(ClauseFactor.LIKE).endWithBracket(1));
}
conditions.add(FormWhereCondition.build().display("经度-").clauseFactor(ClauseFactor.NOT_NULL));
conditions.add(FormWhereCondition.build().display("纬度-").clauseFactor(ClauseFactor.NOT_NULL));
@@ -515,7 +612,8 @@ public class AssetsQueryService {
.filter(col -> {
Map<String, Object> fields = col.getFieldsMap();
if (fields == null) return false;
String perm = (String) fields.get("查看权限-选单位");
Object permObj = fields.get("查看权限-选单位");
String perm = permObj == null ? null : permObj.toString();
return hasPermission(perm, unitIds);
})
.collect(Collectors.toList());

View File

@@ -0,0 +1,518 @@
package com.seeyon.apps.assetsmap.service;
import cn.hutool.log.Log;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.seeyon.aicloud.common.JsonUtils;
import com.seeyon.apps.assetsmap.config.AssetsMapConfigProvider;
import com.seeyon.apps.assetsmap.constants.AssetsMapConstans;
import com.seeyon.apps.assetsmap.vo.ApiResultVO;
import com.seeyon.apps.assetsmap.vo.FileItemVO;
import com.seeyon.apps.assetsmap.vo.FileListVO;
import com.seeyon.apps.assetsmap.vo.FolderItemVO;
import com.seeyon.ctp.common.AppContext;
import org.apache.commons.lang.StringUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文件上传服务
* 封装与 SeeyonFileSystem 网盘服务的所有交互
*/
public class FileUploadService {
private static final Log log = Log.get(FileUploadService.class);
private AssetsMapConfigProvider configProvider = (AssetsMapConfigProvider) AppContext.getBean("assetsMapConfigProvider");
/**
* 获取网盘服务地址
*/
public String getFsHost() {
return configProvider.getBizConfigByKey(AssetsMapConstans.FILESYSTEMHOST);
}
/**
* 检查网盘服务是否已配置
*/
public boolean isConfigured() {
return StringUtils.isNotBlank(getFsHost());
}
/**
* 获取文件夹子项列表
*/
public FileListVO listFolderChildren(String folderId) throws Exception {
if (StringUtils.isBlank(folderId)) {
folderId = "0";
}
String url = getFsHost() + "/api/folder/" + folderId + "/children";
String resp = httpGet(url, getToken());
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
log.warn("listFolderChildren 接口调用失败");
return null;
}
JSONObject data = apiResult.getDataJsonObject();
if (data == null) {
return null;
}
FileListVO result = new FileListVO();
result.setTotalFiles(data.getString("total_files"));
result.setTotalFolders(data.getString("total_folders"));
result.setPage(data.getString("page"));
result.setPageSize(data.getString("page_size"));
// 文件夹列表
List<FolderItemVO> folders = new ArrayList<>();
JSONArray folderArray = data.getJSONArray("folders");
if (folderArray != null && !folderArray.isEmpty()) {
for (int i = 0; i < folderArray.size(); i++) {
JSONObject fObj = folderArray.getJSONObject(i);
FolderItemVO vo = new FolderItemVO();
vo.setId(fObj.getString("id"));
vo.setBizId(fObj.getString("biz_id"));
vo.setName(fObj.getString("name"));
vo.setParentId(fObj.getString("parent_id"));
vo.setPath(fObj.getString("path"));
vo.setVersion(fObj.getInteger("version"));
folders.add(vo);
}
}
result.setFolders(folders);
// 文件列表
List<FileItemVO> files = new ArrayList<>();
JSONArray fileArray = data.getJSONArray("files");
if (fileArray != null && !fileArray.isEmpty()) {
for (int i = 0; i < fileArray.size(); i++) {
JSONObject fObj = fileArray.getJSONObject(i);
files.add(parseFileItem(fObj));
}
}
result.setFiles(files);
return result;
}
/**
* 转换文件项
*/
private FileItemVO parseFileItem(JSONObject fObj) {
FileItemVO vo = new FileItemVO();
vo.setId(fObj.getString("id"));
vo.setName(fObj.getString("name"));
vo.setExtension(fObj.getString("extension"));
vo.setFolderId(fObj.getString("folder_id"));
vo.setSize(fObj.getLong("size"));
vo.setMimeType(fObj.getString("mime_type"));
vo.setPreviewUrl(fObj.getString("preview_url"));
return vo;
}
/**
* 通过业务ID获取文件夹ID
*/
public void createFolder(String bizId,String folderName) throws Exception {
if (StringUtils.isBlank(bizId)) {
return;
}
String url = getFsHost() + "/api/folder";
Map<String,String> params = new HashMap<>();
params.put("name",folderName);
params.put("biz_id",bizId);
String resp = httpPost(url, JsonUtils.toJSONString(params), getToken());
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
log.error("创建文件夹失败 失败 bizId={}", bizId);
}
}
/**
* 通过业务ID获取文件夹ID
*/
public String getFolderIdByBizId(String bizId) throws Exception {
if (StringUtils.isBlank(bizId)) {
return null;
}
String url = getFsHost() + "/api/folder/by-bizid/" + URLEncoder.encode(bizId, "UTF-8");
String resp = httpGet(url, getToken());
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
log.warn("getFolderIdByBizId 失败 bizId={}", bizId);
return null;
}
JSONObject data = apiResult.getDataJsonObject();
if (data == null) {
return null;
}
String folderId = data.getString("id");
if (StringUtils.isBlank(folderId)) {
folderId = data.getString("folder_id");
}
return folderId;
}
/**
* 上传文件到网盘默认100MB分片阈值
*/
public String uploadFile(byte[] fileBytes, String fileName, String folderId) throws Exception {
return uploadFile(fileBytes, fileName, folderId, 100 * 1024 * 1024);
}
/**
* 上传文件到网盘(自动判断直接上传或分片上传)
* @param fileBytes 文件内容
* @param fileName 文件名
* @param folderId 目标文件夹ID
* @param chunkSize 分片阈值(字节),超过此大小走分片上传
* @return 上传结果JSON字符串
*/
public String uploadFile(byte[] fileBytes, String fileName, String folderId, long chunkSize) throws Exception {
if (StringUtils.isBlank(folderId)) folderId = "0";
if (chunkSize <= 0) chunkSize = 5 * 1024 * 1024; // 默认5MB
if (fileBytes.length <= chunkSize) {
return uploadDirect(fileBytes, fileName, folderId);
} else {
return uploadChunked(fileBytes, fileName, folderId, chunkSize);
}
}
/**
* 直接上传(小文件)
*/
private String uploadDirect(byte[] fileBytes, String fileName, String folderId) throws Exception {
String url = getFsHost() + "/api/file/upload?folder_id=" + folderId;
String resp = httpMultipartPost(url, fileBytes, fileName, getToken());
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
log.error("直接上传失败: " + resp);
return null;
}
return apiResult.getDataJsonObject().toString();
}
/**
* 分片上传(大文件)
* 流程init → 逐片上传 → merge
*/
private String uploadChunked(byte[] fileBytes, String fileName, String folderId, long chunkSize) throws Exception {
String token = getToken();
long totalSize = fileBytes.length;
int totalChunks = (int) Math.ceil((double) totalSize / chunkSize);
// 1. 初始化分片上传
JSONObject initBody = new JSONObject();
initBody.put("file_name", fileName);
initBody.put("file_size", totalSize);
initBody.put("folder_id", Long.parseLong(folderId));
String initResp = httpPost(getFsHost() + "/api/file/upload/init", initBody.toString(), token);
ApiResultVO initResult = parseResponse(initResp);
if (initResult == null || !initResult.isSuccess()) {
log.error("分片上传初始化失败: " + initResp);
return null;
}
JSONObject initData = initResult.getDataJsonObject();
String uploadId = initData.getString("upload_id");
int chunkSizeResp = initData.getIntValue("chunk_size");
if (chunkSizeResp > 0) chunkSize = chunkSizeResp;
totalChunks = (int) Math.ceil((double) totalSize / chunkSize);
log.info("分片上传开始: uploadId=" + uploadId + ", fileName=" + fileName + ", chunks=" + totalChunks);
// 2. 逐片上传
for (int i = 0; i < totalChunks; i++) {
int offset = i * (int) chunkSize;
int length = (int) Math.min(chunkSize, totalSize - offset);
byte[] chunk = new byte[length];
System.arraycopy(fileBytes, offset, chunk, 0, length);
String chunkUrl = getFsHost() + "/api/file/upload/chunk/" + uploadId + "/" + i;
String chunkResp = httpMultipartPost(chunkUrl, chunk, "chunk", token);
ApiResultVO chunkResult = parseResponse(chunkResp);
if (chunkResult == null || !chunkResult.isSuccess()) {
log.error("分片上传失败: chunk=" + i + ", resp=" + chunkResp);
return null;
}
log.info("分片上传进度: " + (i + 1) + "/" + totalChunks);
}
// 3. 合并分片
String mergeUrl = getFsHost() + "/api/file/upload/merge/" + uploadId;
String mergeResp = httpPost(mergeUrl, "{}", token);
ApiResultVO mergeResult = parseResponse(mergeResp);
if (mergeResult == null || !mergeResult.isSuccess()) {
log.error("分片合并失败: " + mergeResp);
return null;
}
log.info("分片上传完成: uploadId=" + uploadId);
return mergeResult.getDataJsonObject().toString();
}
/**
* 获取文件下载链接
*/
public String getFileDownloadUrl(String fileId) throws Exception {
String url = getFsHost() + "/api/file/" + fileId + "/download-url";
String resp = httpGet(url, getToken());
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
return null;
}
Map<String,Object> data = (Map<String, Object>) apiResult.getData();
return data == null ? null : (String)data.get("download_url");
}
/**
* 获取文件预览链接
*/
public String getFilePreviewUrl(String fileId) throws Exception {
String url = getFsHost() + "/api/file/" + fileId + "/preview-url";
String resp = httpGet(url, getToken());
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
return null;
}
Map<String,Object> data = (Map<String, Object>) apiResult.getData();
return data == null ? null : (String)data.get("preview_url");
}
/**
* 获取文件详情
*/
public String getFileInfo(String fileId) throws Exception {
String url = getFsHost() + "/api/file/" + fileId;
String resp = httpGet(url, getToken());
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
return null;
}
Object data = apiResult.getData();
return data == null ? null : data.toString();
}
/**
* 搜索文件
*/
public List<FileItemVO> searchFiles(String keyword) throws Exception {
String url = getFsHost() + "/api/file/search?keyword=" + URLEncoder.encode(keyword, "UTF-8");
String resp = httpGet(url, getToken());
ApiResultVO apiResult = parseResponse(resp);
List<FileItemVO> result = new ArrayList<>();
if (apiResult == null || !apiResult.isSuccess()) {
return result;
}
JSONArray dataArray = apiResult.getDataJsonObject().getJSONArray("data");
if (dataArray == null || dataArray.isEmpty()) {
return result;
}
for (int i = 0; i < dataArray.size(); i++) {
JSONObject fObj = dataArray.getJSONObject(i);
result.add(parseFileItem(fObj));
}
return result;
}
// ==================== 统一响应解析 ====================
private ApiResultVO parseResponse(String resp) {
if (StringUtils.isBlank(resp)) {
log.warn("响应为空");
return null;
}
try {
return JSONObject.parseObject(resp, ApiResultVO.class);
} catch (Exception e) {
log.error("解析响应JSON失败: {}", resp, e);
return null;
}
}
// ==================== HTTP 工具 ====================
private String httpGet(String urlStr, String token) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
if (StringUtils.isNotBlank(token)) {
conn.setRequestProperty("token", token);
}
conn.setConnectTimeout(10000);
conn.setReadTimeout(30000);
int code = conn.getResponseCode();
InputStream is = code >= 400 ? conn.getErrorStream() : conn.getInputStream();
String result = readStream(is);
conn.disconnect();
if (code >= 400) {
log.warn("GET 请求异常 url={} code={}", urlStr, code);
}
return result;
}
private String httpPost(String urlStr, String jsonBody, String token) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
if (StringUtils.isNotBlank(token)) {
conn.setRequestProperty("token", token);
}
conn.setConnectTimeout(10000);
conn.setReadTimeout(30000);
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
os.write(jsonBody.getBytes(StandardCharsets.UTF_8));
os.flush();
}
int code = conn.getResponseCode();
InputStream is = code >= 400 ? conn.getErrorStream() : conn.getInputStream();
String result = readStream(is);
conn.disconnect();
if (code >= 400) {
log.warn("POST 请求异常 url={} code={}", urlStr, code);
}
return result;
}
private String httpMultipartPost(String urlStr, byte[] fileBytes, String fileName, String token) throws Exception {
if (fileBytes == null || fileBytes.length == 0) {
throw new IllegalArgumentException("文件内容不能为空");
}
if (StringUtils.isBlank(fileName)) {
fileName = "upload_file";
}
String boundary = "----SeeyonBoundary" + System.currentTimeMillis();
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
if (StringUtils.isNotBlank(token)) {
conn.setRequestProperty("Authorization", "Bearer " + token);
}
conn.setConnectTimeout(10000);
conn.setReadTimeout(60000);
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8), true)) {
writer.append("--").append(boundary).append("\r\n");
writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"").append(fileName).append("\"\r\n");
writer.append("Content-Type: application/octet-stream\r\n\r\n");
writer.flush();
os.write(fileBytes);
os.flush();
writer.append("\r\n");
writer.append("--").append(boundary).append("--\r\n");
writer.flush();
}
int code = conn.getResponseCode();
InputStream is = code >= 400 ? conn.getErrorStream() : conn.getInputStream();
String result = readStream(is);
conn.disconnect();
if (code >= 400) {
log.warn("上传文件异常 url={} code={}", urlStr, code);
}
return result;
}
private String readStream(InputStream is) throws Exception {
if (is == null) {
return "{}";
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
}
// ==================== Token 缓存 ====================
private String cachedToken = null;
private long tokenExpireTime = 0;
public String getToken() {
if (cachedToken != null && System.currentTimeMillis() < tokenExpireTime - 300000) {
return cachedToken;
}
String host = getFsHost();
if (StringUtils.isBlank(host)) {
log.warn("网盘服务地址未配置");
return null;
}
String username = configProvider.getBizConfigByKey(AssetsMapConstans.FILESYSAUSER);
String appId = configProvider.getBizConfigByKey(AssetsMapConstans.FILESYSAPPID);
if (StringUtils.isBlank(username)) {
log.warn("网盘登录用户未配置");
return null;
}
try {
String loginUrl = host + "/api/auth/app-login";
JSONObject body = new JSONObject();
body.put("username", username);
body.put("app_id", appId);
String resp = httpPost(loginUrl, body.toString(), null);
ApiResultVO apiResult = parseResponse(resp);
if (apiResult == null || !apiResult.isSuccess()) {
log.error("网盘登录失败");
return null;
}
JSONObject data = apiResult.getDataJsonObject();
if (data == null || !data.containsKey("token")) {
log.error("登录返回无token");
return null;
}
cachedToken = data.getString("token");
tokenExpireTime = System.currentTimeMillis() + 23 * 3600 * 1000L;
log.info("网盘Token获取成功");
return cachedToken;
} catch (Exception e) {
log.error("获取Token异常", e);
return null;
}
}
}

View File

@@ -0,0 +1,563 @@
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 ManageAssetsQueryService {
private AssetsMapConfigProvider configProvider = (AssetsMapConfigProvider) AppContext.getBean("assetsMapConfigProvider");
private OrgManager orgManager = (OrgManager) AppContext.getBean("orgManager");
private static final Log log = Log.get(ManageAssetsQueryService.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;
}
}

View File

@@ -0,0 +1,28 @@
package com.seeyon.apps.assetsmap.vo;
import com.alibaba.fastjson.JSONObject;
public class ApiResultVO {
private Integer code;
private String message;
private Object data;
public boolean isSuccess() {
return code != null && code == 0;
}
// 快速获取 data 转为 JSONObject
public JSONObject getDataJsonObject() {
if (data == null) return null;
return (JSONObject) JSONObject.toJSON(data);
}
// getter / setter
public Integer getCode() { return code; }
public void setCode(Integer code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Object getData() { return data; }
public void setData(Object data) { this.data = data; }
}

View File

@@ -0,0 +1,22 @@
package com.seeyon.apps.assetsmap.vo;
public class AssetsFileVo {
private String name;
private String url;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@@ -1,5 +1,7 @@
package com.seeyon.apps.assetsmap.vo;
import java.util.List;
public class AssetsItem {
private String id;
private String assetCode;
@@ -19,6 +21,11 @@ public class AssetsItem {
private String assetType;
private String assetCategory;
private String boundData;
private String troubledAssets;
private List<FileItemVO> images;
private List<FileItemVO> videos;
private List<FileItemVO> panoramas;
private List<FileItemVO> attachments;
public String getId() {
return id;
@@ -163,4 +170,44 @@ public class AssetsItem {
public void setBoundData(String boundData) {
this.boundData = boundData;
}
public String getTroubledAssets() {
return troubledAssets;
}
public void setTroubledAssets(String troubledAssets) {
this.troubledAssets = troubledAssets;
}
public List<FileItemVO> getImages() {
return images;
}
public void setImages(List<FileItemVO> images) {
this.images = images;
}
public List<FileItemVO> getVideos() {
return videos;
}
public void setVideos(List<FileItemVO> videos) {
this.videos = videos;
}
public List<FileItemVO> getPanoramas() {
return panoramas;
}
public void setPanoramas(List<FileItemVO> panoramas) {
this.panoramas = panoramas;
}
public List<FileItemVO> getAttachments() {
return attachments;
}
public void setAttachments(List<FileItemVO> attachments) {
this.attachments = attachments;
}
}

View File

@@ -0,0 +1,59 @@
package com.seeyon.apps.assetsmap.vo;
/**
* 网盘文件视图对象
*/
public class FileItemVO {
private String id;
private String name;
private String extension;
private String folderId;
private Long size;
private String mimeType;
private String previewUrl;
private String downloadUrl;
private Integer version;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getExtension() { return extension; }
public void setExtension(String extension) { this.extension = extension; }
public String getMimeType() { return mimeType; }
public void setMimeType(String mimeType) { this.mimeType = mimeType; }
public String getPreviewUrl() { return previewUrl; }
public void setPreviewUrl(String previewUrl) { this.previewUrl = previewUrl; }
public String getDownloadUrl() { return downloadUrl; }
public void setDownloadUrl(String downloadUrl) { this.downloadUrl = downloadUrl; }
public Integer getVersion() { return version; }
public void setVersion(Integer version) { this.version = version; }
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFolderId() {
return folderId;
}
public void setFolderId(String folderId) {
this.folderId = folderId;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
}

View File

@@ -0,0 +1,54 @@
package com.seeyon.apps.assetsmap.vo;
import java.util.List;
/**
* 网盘文件夹子项列表视图对象
* 对应 GET /api/folder/:id/children 的响应 data 部分
*/
public class FileListVO {
private List<FolderItemVO> folders;
private List<FileItemVO> files;
private String totalFiles;
private String totalFolders;
private String page;
private String pageSize;
public List<FolderItemVO> getFolders() { return folders; }
public void setFolders(List<FolderItemVO> folders) { this.folders = folders; }
public List<FileItemVO> getFiles() { return files; }
public void setFiles(List<FileItemVO> files) { this.files = files; }
public String getTotalFiles() {
return totalFiles;
}
public void setTotalFiles(String totalFiles) {
this.totalFiles = totalFiles;
}
public String getTotalFolders() {
return totalFolders;
}
public void setTotalFolders(String totalFolders) {
this.totalFolders = totalFolders;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
public String getPageSize() {
return pageSize;
}
public void setPageSize(String pageSize) {
this.pageSize = pageSize;
}
}

View File

@@ -0,0 +1,42 @@
package com.seeyon.apps.assetsmap.vo;
/**
* 网盘文件夹视图对象
*/
public class FolderItemVO {
private String id;
private String bizId;
private String name;
private String parentId;
private String path;
private Integer version;
public String getBizId() { return bizId; }
public void setBizId(String bizId) { this.bizId = bizId; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
public Integer getVersion() { return version; }
public void setVersion(Integer version) { this.version = version; }
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
}

View File

@@ -0,0 +1,40 @@
package com.seeyon.apps.assetsmap.vo;
public class SubBoundConfig {
private Integer blockNum;
private Integer pathOrder;
private String lat;
private String lng;
public String getLat() {
return lat;
}
public void setLat(String lat) {
this.lat = lat;
}
public Integer getBlockNum() {
return blockNum;
}
public void setBlockNum(Integer blockNum) {
this.blockNum = blockNum;
}
public Integer getPathOrder() {
return pathOrder;
}
public void setPathOrder(Integer pathOrder) {
this.pathOrder = pathOrder;
}
public String getLng() {
return lng;
}
public void setLng(String lng) {
this.lng = lng;
}
}

View File

@@ -4,21 +4,30 @@ import cn.hutool.log.Log;
import com.alibaba.fastjson.JSONObject;
import com.seeyon.aicloud.common.JsonUtils;
import com.seeyon.apps.assetsmap.kit.CAP4FormKit;
import com.seeyon.apps.assetsmap.service.AssetsBoundUpdateLogService;
import com.seeyon.apps.assetsmap.service.AssetsQueryService;
import com.seeyon.apps.assetsmap.service.FileUploadService;
import com.seeyon.apps.assetsmap.vo.AssetsCatagory;
import com.seeyon.apps.assetsmap.vo.BoundsQueryVo;
import com.seeyon.apps.assetsmap.vo.FileItemVO;
import com.seeyon.apps.assetsmap.vo.SubBoundConfig;
import com.seeyon.cap4.form.bean.FormDataMasterBean;
import com.seeyon.cap4.form.bean.FormFieldBean;
import com.seeyon.cap4.form.bean.FormTableBean;
import com.seeyon.cap4.form.service.CAP4FormManager;
import com.seeyon.cap4.template.util.CAPFormUtil;
import com.seeyon.ctp.common.AppContext;
import com.seeyon.ctp.rest.resources.BaseResource;
import com.seeyon.utils.form.FormTableExecutor;
import com.seeyon.utils.form.TableContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Path("/assetsRegion")
@@ -28,7 +37,8 @@ public class AssetsRegionResources extends BaseResource {
private static final Log log = Log.get(AssetsRegionResources.class);
private CAP4FormManager cap4FormManager = (CAP4FormManager) AppContext.getBean("cap4FormManager");
private AssetsQueryService assetsQueryService = (AssetsQueryService) AppContext.getBean("assetsQueryService");
private FileUploadService fileUploadService = (FileUploadService) AppContext.getBean("fileUploadService");
private AssetsBoundUpdateLogService assetsBoundUpdateLogService = (AssetsBoundUpdateLogService) AppContext.getBean("assetsBoundUpdateLogService");
@POST
@Path("/getall")
@@ -115,6 +125,93 @@ public class AssetsRegionResources extends BaseResource {
}
}
@GET
@Path("/getAssetDetail")
@Produces({"application/json"})
public Response getAssetDetail(@QueryParam("assetCode") String assetCode){
try {
return success(assetsQueryService.getAssetDetail(assetCode));
} catch (Exception e) {
log.error(e.getMessage(), e);
e.printStackTrace();
return fail("查询失败:" + e.getMessage());
}
}
@POST
@Path("/reComputedBoundConfig")
@Produces({"application/json"})
@Consumes({"application/json"})
public Response reComputedBoundConfig(Map<String, Object> params){
try {
String formId = (String) params.get("formId");
String fieldDisplay = (String) params.get("fieldDisplay");
String color = "#27ae60";
// 解析 subBoundConfigs
List<?> rawList = (List<?>) params.get("subBoundConfigs");
List<SubBoundConfig> configs = new ArrayList<>();
if (rawList != null) {
for (Object item : rawList) {
if (item instanceof Map) {
Map<String, Object> map = (Map<String, Object>) item;
SubBoundConfig cfg = new SubBoundConfig();
cfg.setBlockNum(map.get("blockNum") != null ? Integer.parseInt((String)map.get("blockNum")) : 0);
cfg.setPathOrder(map.get("pathOrder") != null ? Integer.parseInt((String)map.get("pathOrder")) : 0);
cfg.setLat(map.get("lat") != null ? map.get("lat").toString() : null);
cfg.setLng(map.get("lng") != null ? map.get("lng").toString() : null);
configs.add(cfg);
}
}
}
// 按 blockNum 分组,按 pathOrder 排序
Map<Integer, List<SubBoundConfig>> blockMap = new java.util.LinkedHashMap<>();
for (SubBoundConfig cfg : configs) {
int block = cfg.getBlockNum() != null ? cfg.getBlockNum() : 0;
blockMap.computeIfAbsent(block, k -> new ArrayList<>()).add(cfg);
}
// 生成 regions
List<Map<String, Object>> regions = new ArrayList<>();
for (Map.Entry<Integer, List<SubBoundConfig>> entry : blockMap.entrySet()) {
List<SubBoundConfig> points = entry.getValue();
points.sort(java.util.Comparator.comparingInt(p -> p.getPathOrder() != null ? p.getPathOrder() : 0));
List<List<Double>> outer = new ArrayList<>();
for (SubBoundConfig p : points) {
if (p.getLat() != null && p.getLng() != null) {
List<Double> coord = new ArrayList<>();
coord.add(Double.parseDouble(p.getLat()));
coord.add(Double.parseDouble(p.getLng()));
outer.add(coord);
}
}
if (outer.size() < 3) continue;
Map<String, Object> region = new HashMap<>();
region.put("id", System.currentTimeMillis() + entry.getKey());
region.put("outer", outer);
region.put("holes", new ArrayList<>());
regions.add(region);
}
// 构建边界JSON字符串
Map<String, Object> boundData = new HashMap<>();
boundData.put("regions", regions);
boundData.put("color", color);
String value = JsonUtils.toJSONString(boundData);
// 回填到表单
TableContext master = FormTableExecutor.master("esassetsdoc");
FormTableBean tableBean = master.getTableBean();
FormFieldBean fieldBean = tableBean.getFieldBeanByDisplay("资产边界数据");
Map<String,Object> resMap = new HashMap<>();
resMap.put("updateData",value);
resMap.put("targetField",fieldBean.getColumnName());
//日志记录
assetsBoundUpdateLogService.recordLog(value,formId,"1");
return success(resMap);
} catch (Exception e) {
log.error(e.getMessage(), e);
e.printStackTrace();
return fail("转换失败:" + e.getMessage());
}
}
@GET
@Path("/search")
@Produces({"application/json"})
@@ -132,21 +229,45 @@ public class AssetsRegionResources extends BaseResource {
@Path("/formBackFill")
@Produces({"application/json"})
@Consumes({"application/json"})
public Response boundRefCallBack(Map<String, Object> params) {
public Response formRefCallBack(Map<String, Object> params) {
try {
String masterId = (String) params.get("masterId");
String fieldDisplay = (String) params.get("fieldDisplay");
String isFile = (String)params.get("isFile");
Object value = params.get("value");
FormDataMasterBean cacheFormData = cap4FormManager.getSessioMasterDataBean(Long.parseLong(masterId));
FormFieldBean fieldBean = CAP4FormKit.getFieldBean(cacheFormData, fieldDisplay);
Map<String, Object> displayValueMap = CAPFormUtil.getDisplayValueMap(value, fieldBean, null);
cacheFormData.addFieldValue(fieldBean.getName(), value);
Map<String, Object> mainData = new HashMap<>();
mainData.put(fieldBean.getName(), displayValueMap);
return success(mainData);
if("true".equals(isFile)) {
// 需要回填的主表字段
List<FileItemVO> fileRefs = new ArrayList<>();
//把旧值的json字符串转换成List<FileItemVO>然后追加到fileRefs中
List<Object> files = (ArrayList) value;
for (Object file : files) {
Map<String,String> fileMap = (Map<String,String>) file;
FileItemVO fileItemVO = new FileItemVO();
fileItemVO.setId(fileMap.get("fileKey"));
fileItemVO.setName(fileMap.get("fileName"));
String url = fileUploadService.getFilePreviewUrl(fileMap.get("fileKey"));
fileItemVO.setPreviewUrl(url);
fileRefs.add(fileItemVO);
}
// CAPFormUtil可以用于生成前端需要的数据结构
Map<String, Object> fileUrlRes = CAPFormUtil.getDisplayValueMap(JsonUtils.toJSONString(fileRefs), fieldBean, null);
// 数据加入缓存
cacheFormData.addFieldValue(fieldBean.getName(), fileUrlRes);
// 前端需要的数据
mainData.put(fieldBean.getName(), fileUrlRes);
return success(mainData);
}else {
Map<String, Object> displayValueMap = CAPFormUtil.getDisplayValueMap(value, fieldBean, null);
cacheFormData.addFieldValue(fieldBean.getName(), value);
mainData.put(fieldBean.getName(), displayValueMap);
if(fieldDisplay.equals("资产边界数据")) {
assetsBoundUpdateLogService.recordLog((String)value,masterId,"0");
}
return success(mainData);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return fail("保存失败:" + e.getMessage());

View File

@@ -0,0 +1,204 @@
package com.seeyon.ctp.rest.resources.assetsmap;
import cn.hutool.log.Log;
import com.alibaba.fastjson.JSONObject;
import com.seeyon.apps.assetsmap.service.FileUploadService;
import com.seeyon.apps.assetsmap.vo.FileListVO;
import com.seeyon.ctp.common.AppContext;
import com.seeyon.ctp.rest.resources.BaseResource;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* 文件上传控件 REST 接口
* 薄层:参数解析 + 调用 FileUploadService + 返回响应
*/
@Path("/fileUpload")
@Produces({"application/json"})
public class FileUploadResources extends BaseResource {
private static final Log log = Log.get(FileUploadResources.class);
private FileUploadService fileUploadService = (FileUploadService) AppContext.getBean("fileUploadService");
/**
* 获取文件夹子项列表
* GET /seeyon/rest/fileUpload/folder/children?folderId=0
*/
@GET
@Path("/folder/children")
public Response listFolderChildren(@QueryParam("folderId") String folderId) {
try {
if (!fileUploadService.isConfigured()) {
return fail("网盘服务地址未配置");
}
FileListVO result = fileUploadService.listFolderChildren(folderId);
if (result == null) {
return fail("获取文件夹列表失败");
}
return success(result);
} catch (Exception e) {
log.error("获取文件夹列表失败", e);
return fail("获取文件夹列表失败:" + e.getMessage());
}
}
/**
* 通过业务ID获取文件夹ID
* GET /seeyon/rest/fileUpload/folder/by-bizid?bizId=xxx
*/
@GET
@Path("/folder/by-bizid")
public Response getFolderIdByBizId(@QueryParam("bizId") String bizId) {
try {
if (!fileUploadService.isConfigured()) {
return fail("网盘服务地址未配置");
}
String folderId = fileUploadService.getFolderIdByBizId(bizId);
if (folderId != null) {
Map<String, Object> data = new HashMap<>();
data.put("folderId", folderId);
return success(data);
}
return fail("未找到对应文件夹");
} catch (Exception e) {
log.error("获取文件夹ID失败", e);
return fail("获取文件夹ID失败" + e.getMessage());
}
}
/**
* 上传文件
* POST /seeyon/rest/fileUpload/upload?folderId=0
* Body: multipart/form-data
*/
@POST
@Path("/upload")
@Consumes({"multipart/form-data"})
@Produces({"text/html", "application/json"})
public Response uploadFile(@Context HttpServletRequest request) {
try {
if (!fileUploadService.isConfigured()) {
return fail("网盘服务地址未配置");
}
CommonsMultipartResolver resolver = (CommonsMultipartResolver)AppContext.getBean("multipartResolver");
MultipartHttpServletRequest multipartReq = resolver.resolveMultipart(request);
Map<String, MultipartFile> fileMap = multipartReq.getFileMap();
// 从 multipart 中提取文件
MultipartFile file = fileMap.get("file");
if (file == null || file.isEmpty()) {
return fail("未接收到文件数据");
}
byte[] fileBytes = file.getBytes();
String fileName = multipartReq.getParameter("fileName");
if (fileName == null || fileName.isEmpty()) {
fileName = file.getOriginalFilename();
}
String folderId = multipartReq.getParameter("folderId");
log.info("上传文件: fileName=" + fileName + ", folderId=" + folderId + ", size=" + fileBytes.length);
// 超过100MB走分片上传
long chunkSize = 100 * 1024 * 1024;
String result = fileUploadService.uploadFile(fileBytes, fileName, folderId, chunkSize);
if (result == null || result.isEmpty()) {
return fail("上传失败:网盘服务无响应");
}
return success(JSONObject.parseObject(result));
} catch (Exception e) {
log.error("上传文件失败", e);
return fail("上传文件失败:" + e.getMessage());
}
}
/**
* 从 multipart header 中提取 part name
*/
private String extractPartName(String header) {
int nameIdx = header.indexOf("name=\"");
if (nameIdx < 0) return null;
int start = nameIdx + 6;
int end = header.indexOf("\"", start);
return end > start ? header.substring(start, end) : null;
}
/**
* 从 multipart header 中提取文件名
*/
private String extractFileName(String header) {
int fnIdx = header.indexOf("filename=\"");
if (fnIdx < 0) return null;
int start = fnIdx + 10;
int end = header.indexOf("\"", start);
return end > start ? header.substring(start, end) : null;
}
/**
* 获取文件下载链接
* GET /seeyon/rest/fileUpload/file/download?fileId=xxx
*/
@GET
@Path("/file/download")
public Response getFileDownloadUrl(@QueryParam("fileId") String fileId) {
try {
if (!fileUploadService.isConfigured()) {
return fail("网盘服务地址未配置");
}
String result = fileUploadService.getFileDownloadUrl(fileId);
if (result == null || result.isEmpty()) {
return fail("未找到文件");
}
return success(JSONObject.parseObject(result));
} catch (Exception e) {
log.error("获取下载链接失败", e);
return fail("获取下载链接失败:" + e.getMessage());
}
}
@GET
@Path("/file/info")
public Response getFileInfo(@QueryParam("fileId") String fileId) {
try {
if (!fileUploadService.isConfigured()) {
return fail("网盘服务地址未配置");
}
String result = fileUploadService.getFileInfo(fileId);
if (result == null || result.isEmpty()) {
return fail("未找到文件");
}
return success(JSONObject.parseObject(result));
} catch (Exception e) {
log.error("获取文件信息失败", e);
return fail("获取文件信息失败:" + e.getMessage());
}
}
/**
* 搜索文件
* GET /seeyon/rest/fileUpload/file/search?keyword=xxx
*/
@GET
@Path("/file/search")
public Response searchFiles(@QueryParam("keyword") String keyword) {
try {
if (!fileUploadService.isConfigured()) {
return fail("网盘服务地址未配置");
}
java.util.List<com.seeyon.apps.assetsmap.vo.FileItemVO> result = fileUploadService.searchFiles(keyword);
return success(result);
} catch (Exception e) {
log.error("搜索文件失败", e);
return fail("搜索文件失败:" + e.getMessage());
}
}
}