资讯动态

泛微OA e-cology 8 WebService接口:配置部署与Java调用实操

发布时间:2026/10/6 12:00:59 来源:尧图企业网站定制
简介面向泛微OA e-cology 8 最新 webservice 接口的说明文档为需要做 OA 系统接口集成、数据同步或文档管理的开发者与实施顾问提供直接参考。该 docx 文档系统梳理了文档模块 WebService 的部署与调用细节包括在 services.xml 中添加服务名称、命名空间、服务类与实现类配置重启后通过 wsdl 地址验证是否发布成功同时逐个说明 login、createDoc、updateDoc、deleteDoc、getDoc、getDocCount、getList 等接口的参数、返回值与业务含义其中 login 还区分数据库验证、动态密码验证、LDAP 验证等登录方式。文档附有 DocInfo 对象字段说明覆盖文档 ID、类型、标题、编号、主目录/分目录/子目录、部门、语言等属性也包含创建、修改、批准、失效等操作记录字段能帮助理解请求数据结构和返回对象。压缩包内仅含 1 个 docx 文件体积 330KB内容结构清晰便于本地检索查阅。该资源已有 6785 人学习下载适合熟悉泛微后端配置、但首次接触接口调用的人员作为速查手册可快速定位部署、字段映射与调用参数问题。1. 泛微OA e-cology 8 的 WebService 接口把文档操作搬到外部系统的省力路径做泛微OA二开时最常遇到的一个需求就是让外部系统直接操作OA里的文档——比如把合同附件同步给文件服务器、把公告推送到门户首页、或者做历史数据迁移。这套 e-cology 8 的 WebService 接口文档给出的方案很直接登录拿 Session然后用 createDoc、updateDoc、deleteDoc、getDoc 这一组方法操作文档附件以 Base64 编码塞进 SOAP 报文里传输。对负责系统集成的开发者和实施运维来说这一套接口比模拟登录再点页面稳定得多也更容易对接。真正让新手翻车的往往不是接口逻辑而是 services.xml 部署、附件压缩标记这类细节下面按落地顺序把每一步拆开。2. 让 WebService 先跑起来services.xml 部署与 WSDL 验证2.1 部署前先确认 classbean 路径与 xfire 版本泛微的 WebService 基于 xfire 框架实现接口服务定义都写在services.xml里。这个文件的实际路径是 OA 安装目录下的classbean/META-INF/xfire/services.xml不是随便放到哪个 classpath 都能被扫描到的。很多新手第一次部署时直接在整个磁盘里搜services.xml搜出来好几个同名文件改错一个就白忙活半天。正确做法是先确认 OA 根目录比如/opt/ecology或者D:\Ecology然后顺着classbean/META-INF/xfire/往下找。打开这个文件之前建议先做两件事第一备份原文件改动出错可以回滚第二确认 Tomcat 用的是哪个 JDK 版本因为 xfire 对 JDK 版本比较敏感JDK 8 和 JDK 11 下同一份配置的解析行为会有差异。文件本身是 XML 格式多个service节点依次排列每个节点对应一个对外暴露的 WebService。泛微很多其他模块也往这个文件里注册服务所以改的时候只动自己要用的那几个节点别把别的模块的配置删了。service nameDocService/name namespacehttp://localhost/services/DocService/namespace serviceClassweaver.docs.webservices.DocService/serviceClass implementationClassweaver.docs.webservices.DocServiceImpl/implementationClass serviceFactoryorg.codehaus.xfire.annotations.AnnotationServiceFactory/serviceFactory /service这段配置里最容易被忽略的是namespace。文档里写的是http://localhost/services/DocService但如果按这个原样部署生成的 WSDL 地址会指向 localhost客户端在别的机器上解析时连不上服务器。我一般在部署环境里把 localhost 改成 OA 服务器的实际 IP 或域名改完再重启服务。serviceClass和implementationClass对应的是接口类和实现类这两个是泛微已经写好的类类名拼错一个字母服务就起不来。2.2 文档接口与流程接口的 services.xml 配置差异文档接口只暴露了文档操作而流程接口在 services.xml 里有两种配置方式一种是没有权限验证的WorkflowServiceImpl另一种是带权限验证的WorkflowServiceImplSec。文档接口本身没有提到需要额外加过滤器的版本但流程接口那部分明确写了上面这个接口没有权限验证以及客户端访问控制如果需要进行权限控制则需要配置下面的代码推荐都使用下面的接口。service nameWorkflowService/name namespacewebservices.services.weaver.com.cn/namespace serviceClassweaver.workflow.webservices.WorkflowService/serviceClass implementationClassweaver.workflow.webservices.WorkflowServiceImplSec/implementationClass serviceFactoryorg.codehaus.xfire.annotations.AnnotationServiceFactory/serviceFactory /service注意这里的 namespace 是webservices.services.weaver.com.cn跟文档接口的http://localhost/services/DocService完全不同客户端生成时用的包名也跟 namespace 有关。如果你同时接文档和流程接口生成客户端代码时两个服务的包名可能会不一样这个不奇怪是 WSDL 的 namespace 决定的。配置 Sec 版本后还要在/Ecology/WEB-INF/web.xml里加一个过滤器而且位置必须在XFireServlet配置之前filter filter-nameintsecurity/filter-name filter-classweaver.filter.IntefaceSecurityFilter/filter-class /filter filter-mapping filter-nameintsecurity/filter-name url-pattern/services/*/url-pattern /filter-mapping这个过滤器的作用是拦截所有/services/*的请求然后根据/workflow/UserList.jsp页面里配置的 IP 白名单决定放行还是拒绝。如果过滤器配置在 XFireServlet 后面请求会先被 xfire 处理掉过滤器根本不起作用。这个顺序问题我在实际部署时踩过后来每次改 web.xml 都会确认过滤器在前、Servlet 在后。2.3 部署成功与否的验证方法看 WSDL 而不是看页面配置改完、Tomcat 重启之后验证方式是在浏览器里访问http://OA地址/services/DocService?wsdl。如果能看到一堆 XML 标签里面有wsdl:definitions、wsdl:operation这样的结构说明部署成功了。如果访问返回 404先检查 URL 拼写特别是大小写和services之间的路径如果返回 500大概率是 services.xml 里的类名写错或者 XML 格式有问题去 Tomcat 的 catalina.out 里搜xfire或者DocService关键词能看到具体的异常栈。这里有个容易误判的地方浏览器里看到 XML 页面不一定代表服务真的可用。有些环境里服务器返回了 WSDL但实际调用时因为类加载问题抛异常。我习惯在验证时顺手用curl调一次login方法确认不是只看 WSDL才算真正部署完成。2.4 流程接口的权限控制从 UserList.jsp 到 IP 白名单Process 接口部署完成后还要登录 OA 后台访问/workflow/UserList.jsp在页面上添加允许调用 WebService 的 IP 地址。这个页面是泛微自己的权限列表管理页跟操作系统防火墙没什么关系它就是通过之前那个IntefaceSecurityFilter过滤器来生效的。配置好之后只有白名单里的 IP 能调用流程接口其他 IP 即使能访问 WSDL实际调用时也会被拦。生产环境我一般建议直接上 Sec 版本。文档里特意写了推荐都使用下面的接口言下之意就是不带权限验证的版本只适合开发调试。如果你在内网环境图省事用了不带 Sec 的版本一旦有横向移动风险攻击者拿到一个 OA 地址就能直接调流程接口创建审批流后果比想像严重。我自己维护的测试环境用无权限版生产环境一律 Sec 版加白名单。3. 吃透方法与 DocInfo 对象七个接口方法、三类对象的边界3.1 接口方法总览与登录 Session 机制文档接口一共七个方法核心是登录方法和六个文档操作。登录方法login接收四个参数登录名、密码、登录方式和客户端 IP。登录方式 0 是数据库验证1 是动态密码验证2 是 LDAP 验证。返回值是一个 Session 字符串后续所有文档操作都要把这个 session 传进去。方法名参数返回值功能loginloginid, password, logintype, ipaddressString 类型 Session 码登录验证获取会话createDocDocInfo, sessioncodeint1 成功 0 失败根据对象创建文档updateDocDocInfo, sessioncodeint1 成功 0 失败根据对象修改文档deleteDocid, sessioncodeint1 成功 0 失败根据 ID 删除文档getDocid, sessioncodeDocInfo按 ID 取完整文档对象getDocCountsessioncodeint当前用户有权限的文档数getListsessioncode, page, pagesizeDocInfo[]分页取文档对象数组注意getList的参数接口概览表格里只写了 sessioncode但客户端示例代码里实际是service.getList(session, page, pagesize)说明列表方法本身支持分页。这个参数在 WSDL 里是定义了的如果你用生成的客户端代码IDE 里自动提示会给你展示重载方法签名。Session 的含义要搞清楚它不是 OA 系统里的用户会话 Token而是本次 WebService 调用专门生成的 Session 码。每次login成功都会返回一个新的 Session 值服务端会把它跟登录用户绑定。Session 要不要缓存复用我的做法是短流程调用里每次登录就行性能影响不大如果做批量数据同步就缓存 Session定期重新登录减少认证次数。3.2 DocInfo 关键字段目录、状态、附件与常用组合DocInfo 是这个接口的核心数据对象字段非常多但实际开发中高频用到的就那么几组。第一组是文档身份id、docSubject标题、docCode文档编号、docEdition版本号。创建文档时 id 传 0让系统自增更新文档时 id 必须传原文档的 id。第二组是目录结构maincategory、subcategory、seccategory分别对应主目录、分目录、子目录的 ID对应的maincategoryStr、subcategoryStr、seccategoryStr是目录名称。创建文档时这三个 ID 必须有效否则返回失败。第三组是状态与创建人docStatus是文档状态示例代码里创建时传 1doccreaterid是创建人 ID文档里示例传的是 111。这个字段要特别留意有些泛微版本里如果创建人 ID 不对文档虽然创建成功但权限归属会出问题。还有ownerid和ownertype表示文档所有者和所有者类型创建时建议一并设置。第四组是内容与附件docType为 1 时是 HTML 文档内容存在doccontent字段里docType为 2 时是 Office 文档内容对应imagefileId和versionId。附件存在attachments数组里类型是 DocAttachment[]每个附件对象里有文件名称、Base64 内容、服务器路径、压缩标记等。3.3 getList 与 getDoc 的边界列表不携带附件getList 返回的是 DocInfo 数组但说得很清楚无文档内容及附件。也就是说你拿列表接口做展示没问题但想通过列表直接拿附件内容是拿不到的必须再调一次 getDoc 按 id 取详情。这个设计目的是控制列表接口的网络包大小——一个文档可能挂好几个大附件如果列表接口把附件的 Base64 内容全带回来分页接口瞬间就会撑爆。getDoc 返回单个 DocInfo带完整内容和附件。附件内容在DocAttachment.filecontent字段里是 Base64 编码后的字符串拿过来之后要用 Base64 解码才能得到原始字节流。至于 getDocCount主要是给分页逻辑算总页数用的返回的是当前登录用户有权限看到的文档数不是数据库里所有文档数。这里隐含了一个权限边界整个接口的可见范围都跟在 Session 关联的用户有关没有权限的文档不会出现在列表里。3.4 DocCustomField 与关联类型自定义字段的取值方式DocInfo 里还有一个doccustomfields数组类型是 DocCustomField。这个对象有六个属性fieldid自定义字段 ID、fieldhtmltype显示类型、fielddbtype存储类型、fieldtype字段类型、fieldshow显示名称、fieldvalue字段值。如果你在 OA 文档模块里配置了自定义字段通过 getDoc 拿到的 DocInfo 里就能看到这些键值对。创建和更新文档时自定义字段的处理方式是在 DocInfo 里 new 一个 DocCustomField 数组逐个 set 好 fieldid 和 fieldvalue 再塞进去。需要注意字段 ID 必须是后台自定义字段配置里的真实 ID拿不准的话先在 OA 后台文档设置里查一下。另外 DocInfo 还带了一批关联类型字段hrmresid关联人力资源、assetid关联资产、crmid关联 CRM、itemid关联项目等。这些字段在你做跨模块数据打通时很有用比如一个文档既挂在项目下又关联了客户就同时填 projectid 和 crmid。4. 用 Java 客户端调通文档接口从生成客户端代码到完整的增删改查4.1 生成客户端代码的两种方式与工程依赖泛微这套接口走的是 xfire 加 Axis 的老路线客户端代码推荐用 Axis 的 WSDL2Java 工具生成。方式一从 WSDL 文件直接生成。java -cp axis.jar:commons-discovery.jar:commons-logging.jar:wsdl4j.jar \ org.apache.axis.wsdl.WSDL2Java \ http://192.168.7.200:8080/services/DocService?wsdl生成完成后你会得到一个以 namespace 为包名的目录。如果 services.xml 里的 namespace 是http://localhost/services/DocService生成出来的包名就是localhost.services.DocService里面有DocServiceLocator和DocServicePortType两个关键类示例代码里 import 的就是这两个。如果 namespace 改成了实际 IP包名会变成com.xxx.services.DocService之类引用时留意一下。运行环境需要的依赖 jar 至少要包含 axis.jar、wsdl4j.jar、commons-discovery-0.2.jar、commons-logging.jar。泛微服务端的 xfire 版本比较老客户端 jar 版本不要用太新的否则序列化兼容性会出现诡异问题。我一般直接用 OA 服务器上 classbean 依赖的同版本 jar或者从泛微的 lib 目录里拷一份。4.2 登录鉴权与 Session 管理客户端生成之后第一步先登录。这是所有操作的前置条件。private static String getSession(String loginid, String password, int logintype, String ip) throws MalformedURLException, ServiceException, RemoteException { DocServicePortType service new DocServiceLocator() .getDocServiceHttpPort(new URL(serviceurl)); String session service.login(loginid, password, logintype, ip); return session; }这里 serviceurl 指向服务的 WSDL 地址比如http://192.168.7.200:8080/services/DocService。loginid是 OA 里真实存在的用户登录名不是显示名。logintype按实际认证方式传。ip参数服务端会记录客户端的 IP 来源如果你们 OA 的登录审计有要求这里要传真实客户端 IP不要写死 127.0.0.1。login返回的 session 字符串建议放在一个全局变量里后续所有方法调用都要带它丢失后必须重新登录。4.3 查询文档列表与按 ID 拉取文档详情列表和详情是日常用最多的两个操作。列表用getList分页参数控制往返数据量。public static void getDocList(String session, int page, int pagesize) throws RemoteException { DocInfo[] docs service.getList(session, page, pagesize); for (int i 0; i docs.length; i) { System.out.println(docs[i].getId() | docs[i].getDocSubject() | docs[i].getMaincategory() | docs[i].getMaincategoryStr() | docs[i].getDoccreaterid() | docs[i].getDoccreatername() | docs[i].getDoccreatedate() | docs[i].getDoccreatetime()); } }注意getDocCreaterType这类字段列表接口里也会带出来但如果你没调用 getter它不会自动序列化到日志里。列表拿到的 DocInfo 对象里attachments字段是空的属于正常现象。真正要拿附件内容用 getDocpublic static void getDocInfo(String session, int docid) throws RemoteException { DocInfo doc service.getDoc(docid, session); DocAttachment[] atts doc.getAttachments(); if (atts ! null atts.length 0) { DocAttachment da atts[0]; byte[] content Base64.decode(da.getFilecontent()); System.out.println(文件名 da.getFilename()); System.out.println(大小 content.length); } }这里的 Base64 是org.apache.axis.encoding.Base64不是 java.util.Base64。用错类会导致解码结果不对这是一个很隐蔽的坑后面避坑章节会展开。4.4 创建带附件的文档Base64 编码与参数设置创建文档是最复杂的场景因为它同时涉及 DocInfo 和 DocAttachment 两个对象的组装。直接看代码public static void createNewDoc(String session) throws RemoteException { // 读取本地文件转成字节数组 byte[] content new byte[102400]; try { int byteread; byte data[] new byte[1024]; InputStream input new FileInputStream(new File(d:\\service test.doc)); ByteArrayOutputStream out new ByteArrayOutputStream(); while ((byteread input.read(data)) ! -1) { out.write(data, 0, byteread); out.flush(); } content out.toByteArray(); input.close(); out.close(); } catch (Exception e) { e.printStackTrace(); } // 组装附件对象 DocAttachment da new DocAttachment(); da.setDocid(0); da.setImagefileid(0); da.setFilecontent(Base64.encode(content)); da.setFilerealpath(d:\\service test.doc); da.setIszip(1); da.setFilename(service test.doc); da.setIsextfile(1); da.setDocfiletype(3); // 组装文档对象 DocInfo doc new DocInfo(); doc.setDoccreaterid(111); doc.setAccessorycount(1); doc.setMaincategory(38); // 主目录 ID doc.setSubcategory(53); // 分目录 ID doc.setSeccategory(204); // 子目录 ID doc.setOwnerid(111); doc.setDocStatus(1); doc.setId(0); doc.setDocType(2); doc.setDocSubject(service html 文档); doc.setDoccontent(service html 文档 content 22222); doc.setAttachments(new DocAttachment[]{da}); int newId service.createDoc(doc, session); System.out.println(新文档 id newId); }几个关键点doc.setAccessorycount(1)要跟实际附件数量一致否则服务端校验可能失败。isextfile为1表示这是文档内容附件docfiletype的取值含义建议先查一下你所在版本的文档附件类型对应表示例里给的是 3。iszip为 1 表示内容做过 zip 压缩具体是不是真的压缩了要看服务端的实现客户端解码后要根据这个值决定要不要解压。创建成功返回新文档的 ID失败返回 0。4.5 更新与删除先取后改的注意事项更新文档最稳的模式是三步走先 getDoc 拿到原始对象改需要改的字段再 updateDoc 传回去。public static void updateDocInfo(String session, int docid) throws RemoteException { DocInfo doc service.getDoc(docid, session); doc.setDocSubject(新的标题); doc.setDoccontent(新的正文内容); // 如果不需要改动附件直接传回原对象即可 int result service.updateDoc(doc, session); System.out.println(更新结果 result); }注意这里有一个很容易踩的坑如果 getDoc 拿到的 DocInfo 里没有附件而你直接 set 新的 attachments 数组覆盖上去旧附件可能被替换掉如果只是想改标题和正文就别动 attachments 字段。示例代码里的更新逻辑是先取回附件对象改掉 filecontent 再塞回去他是为了让附件内容也一并更新。还有doc.setAccessorycount要跟附件数组长度对应否则更新后附件数量不一致。删除操作最简单public static void deleteDoc(String session, int docid) throws RemoteException { int result service.deleteDoc(docid, session); System.out.println(删除结果 result); }返回 1 表示成功0 表示失败。文档不存在时接口可能会抛 RemoteException建议包一层 try-catch把删除失败和文档不存在区分开。5. 避坑清单部署与调用中的五个高频问题5.1 部署后访问 WSDL 404 或白屏现象按文档配置完 services.xml重启 Tomcat浏览器访问http://OA地址/services/DocService?wsdl返回 404 或者一片空白。原因最常见的是 services.xml 没放到正确的 classbean 路径下Tomcat 根本没加载到其次是改了配置文件但没重启服务xfire 不会热加载 services.xml还有一种是 service name 和 URL 里的名称不一致比如配置里写的是 DocServiceURL 写成了 WorkflowService。解决确认文件在classbean/META-INF/xfire/services.xml全文搜索一下DocService节点是否真的存在重启整个 Tomcat 服务而不是 reload 应用检查 URL 拼写注意大小写。改完配置后我习惯先tail -fTomcat 的 catalina.out再访问一次 WSDL能直接在日志里看到 xfire 的加载记录。5.2 客户端运行报 ClassCastException 或 NoClassDefFoundError现象生成的客户端代码编译没问题运行时一调 login 或者 getDoc 就抛 ClassCastException或者提示找不到某个类。原因客户端用的 axis/xfire jar 版本跟服务端不一致导致 SOAP 序列化生成的代理类类型对不上。常见做法是直接从 OA 服务器的 lib 目录里把 xfire、axis 相关 jar 拷到客户端工程里而不是自己从 Maven 仓库拉新版。解决统一 axis 版本到 1.4 或服务端使用的版本检查DocServiceLocator生成的 stub 类是否引用了weaver.docs.webservices.DocInfo如果这个类不在客户端 classpath 里需要把泛微提供的接口 jar 也加进来。我一般会把服务端classbean目录下和webservices相关的 class 文件打成 jar 放进客户端 lib。5.3 附件下载后打不开或中文乱码现象getDoc 拿到附件Base64 解码后写成本地文件打开提示文件损坏或者文件名显示乱码。原因两处。第一Axis 的 Base64.encode 输出可能带换行符解码时没处理干净第二da.getIszip()返回 1 时内容实际是 zip 压缩过的字节流直接当成原文件写盘必然打不开。解决解码前把字符串里的\r\n去掉判断 iszip 后决定要不要走 ZipInputStream 解压一步再写文件。文件名乱码一般是 WSDL 传输时编码造成的可以用new String(filename.getBytes(ISO-8859-1), UTF-8)修正。5.4 getDoc 返回的 attachments 为 null 或为空数组现象调用doc.getAttachments()时抛 NullPointerException或者拿到的数组 length 为 0。原因文档本身确实没有附件这是正常的还有一种情况是当前登录用户对该目录只有浏览权限附件字段被服务端过滤置空。另外Office 文档的正文内容存在 imagefileId 里不在 doccontent 字段如果附件数组为空但你想取正文要先确认文档类型。解决访问 attachments 前先判空判长度确认文档在 OA 后台里确实挂了附件检查登录账号的文档权限范围最好用有该目录完整权限的管理员账号调试。5.5 直接使用无鉴权版本的 WorkflowService 导致服务暴露现象测试环境部署了 WorkflowService 无权限版本后来被扫到并反复调用创建流程。原因文档里明确写了上面这个接口没有权限验证以及客户端访问控制但很多人嫌 Sec 版本配置麻烦直接上了无权限版。解决生产环境强制改用WorkflowServiceImplSec在 web.xml 里加IntefaceSecurityFilter过滤器并且在/workflow/UserList.jsp里把允许调用服务的 IP 白名单收窄只留运维和业务系统的固定 IP。6. 进阶把附件读写封装成工具类处理 Base64 与 iszip 判断6.1 封装下载附件的方法getDoc 拿到的 DocAttachment 里有 filecontent 和 iszip 两个字段每次手动处理很啰嗦。我把它封装成一个独立方法统一处理 Base64 解码和压缩判断。public static void saveAttachmentToFile(DocAttachment da, String savePath) throws Exception { // 1. 先判空 if (da null || da.getFilecontent() null || da.getFilecontent().isEmpty()) { throw new IllegalArgumentException(附件内容为空); } // 2. 清理换行符解码 Base64 String content da.getFilecontent().replaceAll(\\r\\n, ).replaceAll(\n, ); byte[] bytes Base64.decode(content); // 3. 判断是否压缩 InputStream input null; if (da.getIszip() 1) { ZipInputStream zin new ZipInputStream(new ByteArrayInputStream(bytes)); zin.getNextEntry(); input new BufferedInputStream(zin); } else { input new ByteArrayInputStream(bytes); } // 4. 写文件 File file new File(savePath File.separator da.getFilename()); OutputStream out new FileOutputStream(file); byte[] buf new byte[1024]; int len; while ((len input.read(buf)) ! -1) { out.write(buf, 0, len); out.flush(); } input.close(); out.close(); }这里最关键的一步是第 3 步的 iszip 分支。如果不判断压缩标记哪怕是 iszip1 的附件也会被当成原始文件写盘文件自然打不开。判断了之后如果是 zip 流就走 ZipInputStream先进入第一个条目再读取这样才能拿到真正的文件内容。6.2 上传附件时的参数建议创建带附件的文档时我一般建议把iszip直接设 0客户端自己不做压缩让服务端去处理。因为你不知道服务端反序列化后会不会主动压缩设 0 是标准姿势设 1 反而要确保你传的字节流确实是 zip 格式。filerealpath这个字段在无状态调用里没什么用服务端不会真的按照这个路径去服务器上取文件文件内容还是以filecontent的 Base64 为准。isextfile和docfiletype按文档模块的实际类型填不确定的时候先建一个文档看看 OA 里默认值是什么。6.3 我的调用习惯从那以后我每次调附件接口都会在日志里把iszip的值和filecontent前 64 个字符打印出来先确认内容是不是 Base64、文件头对不对再写盘。批量同步文档之前先拿一条测试数据完整跑一遍创建、查询、更新、删除四个操作确认 Session 和附件链路都没问题再上量。这套接口本身不复杂复杂的是它跟 xfire、Axis 这些老框架绑定得比较紧部署和调用细节上稍不留神就会翻车。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑