From 7b551b4920acfa612e151275b9988019821e3cf8 Mon Sep 17 00:00:00 2001 From: yuxueliang0813 <407010292@qq.com> Date: Fri, 28 Feb 2025 18:03:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86=E9=A1=B9=E7=9B=AE=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repc/task/DobeContractAttachmentTask.java | 132 ++++++++++++++ .../shkd/repc/task/DobeDWprojectTask.java | 92 +++++++++- main/java/shkd/utils/AttachmentFileUtil.java | 166 ++++++++++++++++++ 3 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 main/java/shkd/repc/task/DobeContractAttachmentTask.java create mode 100644 main/java/shkd/utils/AttachmentFileUtil.java diff --git a/main/java/shkd/repc/task/DobeContractAttachmentTask.java b/main/java/shkd/repc/task/DobeContractAttachmentTask.java new file mode 100644 index 0000000..cdc62f8 --- /dev/null +++ b/main/java/shkd/repc/task/DobeContractAttachmentTask.java @@ -0,0 +1,132 @@ +package shkd.repc.task; + +import com.alibaba.fastjson.JSONObject; +import kd.bos.context.RequestContext; +import kd.bos.dataentity.entity.DynamicObject; +import kd.bos.db.DB; +import kd.bos.db.DBRoute; +import kd.bos.entity.MainEntityType; +import kd.bos.exception.KDException; +import kd.bos.logging.Log; +import kd.bos.logging.LogFactory; +import kd.bos.orm.query.QFilter; +import kd.bos.schedule.executor.AbstractTask; +import kd.bos.servicehelper.AttachmentServiceHelper; +import kd.bos.servicehelper.BusinessDataServiceHelper; +import kd.bos.servicehelper.MetadataServiceHelper; +import kd.bos.util.HttpClientUtils; +import kd.sdk.plugin.Plugin; +import shkd.utils.AttachmentFileUtil; +import shkd.utils.DobeDWUtils; +import shkd.utils.OAUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * 后台任务插件 + */ +public class DobeContractAttachmentTask extends AbstractTask implements Plugin { + + private static final String entityName = "recon_contractbi";//表名 t_recon_contractbill + private static final String attachKey = "attachmentcontactdoc";//合同普通附件面板标识 + private static final String zwattKey = "attachmentpanel";//合同正文(盖章件)附件面板标识 + private static final Log logger = LogFactory.getLog(DobeContractAttachmentTask.class); + private static final String oa_menthod = "/seeyon/rest/ats/dlNas/";//OA接口方法 + public static final String oaUrl = System.getProperty("oaurl");//(测试环境外网地址)http://140.206.96.138:9091 + + public static final String updatesql = "UPDATE t_recon_contractbill SET fk_qeug_nofj=0 WHERE fid=?;"; + + @Override + public void execute(RequestContext requestContext, Map map) throws KDException { + String contractno; + String fj_oaid; + String fj_zwid; + Long contractid; + DynamicObject contractinfo; + //先查出合同有附件oa id但是在金蝶中没有附件的记录 + QFilter oaidFtr = QFilter.isNotNull("qeug_fjoaid");//合同的附件OAID不为空 + QFilter nofjFtr = new QFilter("qeug_nofj","=","1");//合同未上传附件的标记为true + String selectStr = "id,billno,qeug_fjoaid,qeug_fjzwid,qeug_nofj"; + DynamicObject[] cons = BusinessDataServiceHelper.load(entityName,selectStr,new QFilter[]{oaidFtr.and(nofjFtr)}); + if(cons.length == 0){ + return; + } + //获取appId + String appId = MetadataServiceHelper.getDataEntityType(entityName).getAppId(); + //TODO 获取oa token 此token时效是多少 + String oaToken = OAUtils.getOaToken("oa-contract-attachment"); + if(DobeDWUtils.isEmpty(oaToken)){ + return; + } + //定义请求头 + Map customerHeader = new HashMap<>(); + customerHeader.put("Content-Type", "application/json"); + customerHeader.put("token", oaToken); + String resultjson; + String requrl; + String nasPath = null; + JSONObject resultjsonObject; + for (int i = 0; i < cons.length; i++) { + contractinfo = cons[i]; + //根据合同中的编号、附件oa id、正文oa id三个字段调用OA附件接口,记录日志 + contractid = contractinfo.getLong("id"); + contractno = contractinfo.getString("billno"); + fj_oaid = contractinfo.getString("qeug_fjoaid"); + fj_zwid = contractinfo.getString("qeug_fjzwid"); + + //拿到OA附件接口返回的NAS文件存放路径 + requrl = oaUrl+oa_menthod+contractno+"?summaryId="+fj_oaid+"&subReference="+fj_zwid; + try { + // 发送get请求oa附件接口并获取响应 + resultjson = HttpClientUtils.get(requrl, customerHeader,null); + resultjsonObject = JSONObject.parseObject(resultjson); + logger.info(contractno+" OA附件接口返回结果:\n{}", resultjson); + // 检查JSON对象是否为空 + if (resultjsonObject != null && !resultjsonObject.isEmpty()) { + if(resultjsonObject.getBoolean("success")){ + // /isc/prod/ZLHT20250105001/ + //其中isc为供应链系统标志,prod为生产/uat为测试,ZLHT20250105001为接口传入的code字段 + nasPath = resultjsonObject.getString("nasPath"); + } + //记录成功日志 + DobeDWUtils.saveLog(contractno,"致远附件",requrl,resultjson,true,"调用OA附件接口"); + } + } catch (Exception e) { + //记录OA接口异常信息 + logger.info(String.format(contractno+" 调用OA附件接口异常:%s", e.getMessage())); + DobeDWUtils.saveLog(contractno,"致远附件",requrl,e.getMessage(), false,"调用OA附件接口"); + throw new RuntimeException(e); + } + + //访问NAS文件指定存放路径,拿到当前附件和正文目录下的所有文件对象,记录日志 + //在nas路径里,需要供应链系统在/isc/prod/{code}/下继续组装相对地址: + // /isc/prod/{code}/{summaryId}/下文件夹内为表单附件,/isc/prod/{code}/{subReference}/为合同正文附件 + if(!DobeDWUtils.isEmpty(nasPath)){ + //处理表单附件目录 + try { + //TODO nasPath+fj_oaid; + boolean upresult = AttachmentFileUtil.saveDirFile2Attchment(appId,entityName,contractid,nasPath+fj_oaid,attachKey); + //处理合同正文附件目录 + if(!DobeDWUtils.isEmpty(fj_zwid)){ + //TODO nasPath+fj_zwid; + AttachmentFileUtil.saveDirFile2Attchment(appId,entityName,contractid,nasPath+fj_zwid,zwattKey); + } + //上传成功后更新合同未上传附件的标记为false + if(upresult){ + DB.update(DBRoute.of("scm"), updatesql, new Object[]{contractid}); + //记录成功日志 + DobeDWUtils.saveLog(contractno,"NAS附件",nasPath,null,true,"NAS上传文件至星瀚"); + } + } catch (Exception e) { + //记录附件上传异常信息 + logger.info(String.format(contractno+" 从NAS上传文件至星瀚异常:%s", e.getMessage())); + DobeDWUtils.saveLog(contractno,"NAS附件",nasPath,e.getMessage(),false,"NAS上传文件至星瀚"); + } + }else{ + logger.info(contractno+" OA附件接口返回路径为空,fj_oaid:", fj_oaid); + } + } + } + +} \ No newline at end of file diff --git a/main/java/shkd/repc/task/DobeDWprojectTask.java b/main/java/shkd/repc/task/DobeDWprojectTask.java index 2389095..ab1e37b 100644 --- a/main/java/shkd/repc/task/DobeDWprojectTask.java +++ b/main/java/shkd/repc/task/DobeDWprojectTask.java @@ -24,6 +24,8 @@ import okhttp3.Response; import shkd.utils.DobeDWUtils; import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Date; import java.util.Map; @@ -33,8 +35,10 @@ import java.util.Map; public class DobeDWprojectTask extends AbstractTask implements Plugin { //项目f7的基础资料是单独的表 t_repmd_project private static final String entityName = "repmd_projectbill";//表名 t_repmd_projectbill + private static final String orgName = "bos_org";//表名 t_org_org private static Log log = LogFactory.getLog(DobeDWprojectTask.class); private static final String dw_menthod = "mdm_projectinfo"; + private static final Long accttableid = 1318154893474663424l; @Override public void execute(RequestContext requestContext, Map map) throws KDException { @@ -152,9 +156,31 @@ public class DobeDWprojectTask extends AbstractTask implements Plugin { }else if("成熟园区".equals(project_stage)){ projectinfo.set("qeug_combofield", "CSYQ"); } -// projectinfo.set("billno", fbillno); -// projectinfo.set("org", ); -// projectinfo.set("isleaf", fisleaf); + //如果项目原所属组织为空,则更新,不能将原组织有值的直接更新,防止数据和权限发生变化 + if(projectinfo.getDynamicObject("org") == null){ + orginfo = BusinessDataServiceHelper.loadSingle(orgName,new QFilter[]{new QFilter("number","=",forgid)}); + if(orginfo != null){ + projectinfo.set("org", orginfo);//项目所属组织 + projectinfo.set("purchaseorg", orginfo);//项目采购组织同所属组织 + //核算组织默认和所属组织保持一致 + projectinfo.set("fiorg", orginfo); + }else{ + log.info(String.format("数仓传入的项目所属组织在金蝶中找不到:%s", forgid)); + DobeDWUtils.saveLog(fbillno,"数仓项目同步",json_body.toString(),"数仓传入的项目所属组织在金蝶中找不到:"+forgid,false,"定时任务"); + } + } + if(projectinfo.getDynamicObject("account") == null){ + projectinfo.set("account", accttableid);//会计科目表默认新准则会计科目表 + } + //TODO 数仓新增两个面积字段 20250221 +// projectinfo.set("qeug_decimalfield1", json_body.getBigDecimal("addr"));//原始可出租面积 +// projectinfo.set("qeug_decimalfield3", json_body.getBigDecimal("addr"));//原始建筑面积 +// projectinfo.set("qeug_textfield2", calcDFL(json_body.getBigDecimal("addr"), +// json_body.getBigDecimal("addr")).toString());//原始得房率=可出租/建筑面积*100% + projectinfo.set("qeug_textfield1", json_body.getString("project_oriname"));//项目原名称 + projectinfo.set("manageway", getManageway(json_body.getString("investment_model")));//投资模式 + projectinfo.set("landusage", getLandusage(json_body.getString("land_usage")));//用地性质 + projectinfo.set("acquiredate", json_body.getDate("project_gettime"));//项目获取时间 //原保存逻辑直接存入数据库,现在改成调用保存操作 // SaveServiceHelper.update(projectinfo); OperationServiceHelper.executeOperate("save",entityName,new DynamicObject[]{projectinfo}, OperateOption.create()); @@ -177,14 +203,17 @@ public class DobeDWprojectTask extends AbstractTask implements Plugin { // } projectinfo.set("address", faddress); projectinfo.set("acquiredate", project_getdate); - orginfo = QueryServiceHelper.queryOne("bos_org","id,number,name",new QFilter[]{new QFilter("number","=",forgid)}); + orginfo = BusinessDataServiceHelper.loadSingle(orgName,new QFilter[]{new QFilter("number","=",forgid)}); if(orginfo != null){ - projectinfo.set("org", orginfo.getLong("id"));//项目所属组织 - projectinfo.set("purchaseorg", orginfo.getLong("id"));//项目采购组织同所属组织 + projectinfo.set("org", orginfo);//项目所属组织 + projectinfo.set("purchaseorg", orginfo);//项目采购组织同所属组织 + //核算组织默认和所属组织保持一致 + projectinfo.set("fiorg", orginfo); }else{ log.info(String.format("数仓传入的项目所属组织在金蝶中找不到:%s", forgid)); DobeDWUtils.saveLog(fbillno,"数仓项目同步",json_body.toString(),"数仓传入的项目所属组织在金蝶中找不到:"+forgid,false,"定时任务"); } + projectinfo.set("account", accttableid);//会计科目表默认新准则会计科目表 projectinfo.set("isleaf", true); projectinfo.set("enable", 1);//是否启用 // projectinfo.set("islatestversion", true);//是否最新版-审核之后系统控制的,不需要在此处设置 @@ -197,6 +226,16 @@ public class DobeDWprojectTask extends AbstractTask implements Plugin { projectinfo.set("qeug_combofield", "CSYQ"); } projectinfo.set("qeug_ywlx", ywlx); + //TODO 数仓新增两个面积字段 20250221 +// projectinfo.set("qeug_decimalfield1", json_body.getBigDecimal("addr"));//原始可出租面积 +// projectinfo.set("qeug_decimalfield3", json_body.getBigDecimal("addr"));//原始建筑面积 +// projectinfo.set("qeug_textfield2", calcDFL(json_body.getBigDecimal("addr"), +// json_body.getBigDecimal("addr")).toString());//原始得房率=可出租/建筑面积*100% + projectinfo.set("qeug_textfield1", json_body.getString("project_oriname"));//项目原名称 + projectinfo.set("manageway", getManageway(json_body.getString("investment_model")));//投资模式 + projectinfo.set("landusage", getLandusage(json_body.getString("land_usage")));//用地性质 + projectinfo.set("acquiredate", json_body.getDate("project_gettime"));//项目获取时间 + //保存数据:直接保存入库,不走操作校验 // SaveServiceHelper.save(new DynamicObject[]{projectinfo}); OperationServiceHelper.executeOperate("save",entityName,new DynamicObject[]{projectinfo}, OperateOption.create()); @@ -212,4 +251,45 @@ public class DobeDWprojectTask extends AbstractTask implements Plugin { } } } + + private BigDecimal calcDFL(BigDecimal b1, BigDecimal b2){ + //原始得房率=可出租 b1/建筑面积 b2*100% + if(b1 == null || b2 == null){ + return BigDecimal.ZERO; + }else{ + return b1.divide(b2,4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)); + } + } + + private String getManageway(String manageway){ + //根据数仓入参返回金蝶的投资模式 + String typevalue; + switch (manageway){ + case "收入要素": + typevalue = "1"; + case "成本要素": + typevalue = "2"; + case "管理费用": + typevalue = "3"; + default: + typevalue = "0"; + } + return typevalue; + } + + private String getLandusage(String landusage){ + //根据数仓入参返回金蝶的用地性质 + String typevalue; + switch (landusage){ + case "收入要素": + typevalue = "1"; + case "成本要素": + typevalue = "2"; + case "管理费用": + typevalue = "3"; + default: + typevalue = "0"; + } + return typevalue; + } } \ No newline at end of file diff --git a/main/java/shkd/utils/AttachmentFileUtil.java b/main/java/shkd/utils/AttachmentFileUtil.java new file mode 100644 index 0000000..1b9a91f --- /dev/null +++ b/main/java/shkd/utils/AttachmentFileUtil.java @@ -0,0 +1,166 @@ +package shkd.utils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.file.Files; +import java.util.*; + + +import kd.bos.cache.CacheFactory; +import kd.bos.cache.TempFileCache; +import kd.bos.context.RequestContext; +import kd.bos.entity.MainEntityType; +import kd.bos.fileservice.FileItem; +import kd.bos.fileservice.FileService; +import kd.bos.fileservice.FileServiceFactory; +import kd.bos.servicehelper.AttachmentServiceHelper; +import kd.bos.servicehelper.MetadataServiceHelper; +import kd.bos.util.FileNameUtils; + +public class AttachmentFileUtil { + + /**将第三方提供的某个在线文件上传至星瀚对应单据附件上 + * @param entity 目标单据实体标识 + * @param pk 目标数据id + * @param fileUrl 待存入文件的url + * @param fileName 待存入文件名称 + * @param fileSize 待存入文件大小 + * @param attachKey 附件面板标识 + * @throws IOException + */ + public static boolean saveUrlFile2Attchment(String entity,Object pk,String fileUrl,String fileName,String fileSize,String attachKey){ + List> attachments = new ArrayList<>(); + Map attachItem = new HashMap<>(); + //文件名称 + attachItem.put("name", fileName); + //将url文件转换成输入流 + InputStream inputStream = null; + HttpURLConnection conn = null; + String tempUrl = null; + try { + URL url = new URL(fileUrl); + conn = (HttpURLConnection)url.openConnection(); + //设置超时间为30秒 + conn.setConnectTimeout(30000); + //防止屏蔽程序抓取而返回403错误 + conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); + //得到输入流 + inputStream = conn.getInputStream(); + //文件大小 + attachItem.put("size", fileSize); + //获取临时文件缓存 + TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache(); + //将文件流存入临时文件缓存(拷贝完成)(最后一个参数为缓存有效期,600秒) + tempUrl = cache.saveAsFullUrl(fileName, inputStream, 600); + + } catch (Exception e) { + e.printStackTrace(); + return false; + } finally { + //关闭流 + try { + inputStream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + //关闭连接 + conn.disconnect(); + } + //获取文件的缓存路径 +// tempUrl = EncreptSessionUtils.encryptSession(tempUrl); +// //获取域名前缀 +// String address = RequestContext.get().getClientFullContextPath(); +// if (!address.endsWith("/")) { +// address = address + "/"; +// } +// //拼接url路径 +// String tempUrl3 = address + tempUrl; + //修改时间 + attachItem.put("lastModified",System.currentTimeMillis()); + //获取appId + MainEntityType dataEntityType = MetadataServiceHelper.getDataEntityType(entity); + String appId = dataEntityType.getAppId(); + //将文件缓存中的附件文件上传到正式文件服务器 + String actUrl = AttachmentServiceHelper.saveTempToFileService(tempUrl,appId,entity, pk, (String)attachItem.get("name")); + + TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache(); + InputStream in = cache.getInputStream(tempUrl); + FileService service = FileServiceFactory.getAttachmentFileService(); + RequestContext requestContext = RequestContext.get(); + String uuid = UUID.randomUUID().toString().replace("-", ""); + // 生成文件路径-上传附件时远程服务器需要存储文件的位置 + String pathParam = FileNameUtils.getAttachmentFileName(requestContext.getTenantId(), + requestContext.getAccountId(), uuid, fileName); + FileItem fileItem = new FileItem(fileName, pathParam, in); + // cache.remove(url); + // 上传附件到文件服务器 + String downUrl =service.upload(fileItem); + + //将新文件的物理路径存入map + attachItem.put("url", actUrl); + attachments.add(attachItem); + //维护单据和附件的关系(非文件上传) + AttachmentServiceHelper.upload(entity, pk, attachKey, attachments); + return true; + } + + /**将文件服务器上某个目录下的所有文件上传至星瀚对应单据附件上 + * @param appId 目标单据所属应用标识 + * @param entity 目标单据实体标识 + * @param pk 目标数据id + * @param attachKey 附件面板标识 + * @param dirpath 文件目录路径 + * @throws IOException + */ + public static boolean saveDirFile2Attchment(String appId,String entity,Object pk,String dirpath,String attachKey) throws IOException { + File[] files = new File(dirpath).listFiles(); + if(files.length == 0){ + //目录下没有文件,不需要处理 + return false; + } + List> attachments = new ArrayList<>(); + Map attachItem; + String fileName; + File destFile; + String tempUrl;//星瀚临时文件url + String actUrl;//星瀚正式文件url + //获取临时文件缓存 + TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache(); + //遍历目标目录,得到所有文件 + for (int i = 0; i < files.length; i++) { + destFile = files[i]; + fileName = destFile.getName(); + attachItem = new HashMap<>(); + //文件名称 + attachItem.put("name", fileName); + //文件大小 + attachItem.put("size", destFile.length()); + //将文件流存入临时文件缓存(拷贝完成)(最后一个参数为缓存有效期,600秒) + tempUrl = cache.saveAsFullUrl(fileName, Files.readAllBytes(destFile.toPath()), 600); + //修改时间 + attachItem.put("lastModified",System.currentTimeMillis()); + //将文件缓存中的附件文件上传到正式文件服务器 + actUrl = AttachmentServiceHelper.saveTempToFileService(tempUrl,appId,entity,pk,fileName); + //如下代码功能与saveTempToFileService实际功能重复,先注释掉,待调试确认 +// FileService service = FileServiceFactory.getAttachmentFileService(); +// RequestContext requestContext = RequestContext.get(); +// String uuid = UUID.randomUUID().toString().replace("-", ""); +// // 生成文件路径-上传附件时远程服务器需要存储文件的位置 +// String pathParam = FileNameUtils.getAttachmentFileName(requestContext.getTenantId(), +// requestContext.getAccountId(), uuid, fileName); +// FileItem fileItem = new FileItem(fileName, pathParam, cache.getInputStream(tempUrl)); +// // 上传附件到文件服务器 +// service.upload(fileItem); + + //将新文件的物理路径存入map + attachItem.put("url", actUrl); + attachments.add(attachItem); + //维护单据和附件的关系(非文件上传) + AttachmentServiceHelper.upload(entity, pk, attachKey, attachments); + } + return true; + } +}