做泛微OA实施和二次开发的迟早都要面对一件事页面上的流程、表单、客户记录背后到底是哪张表在撑着。我第一次接触泛微E8时为了查一条流程的当前处理人满库翻表翻到怀疑人生。后来把常用数据表摸熟了很多需求——获取流程ID、给流程相关人员自动授客户查询权限、调整明细表下拉框类型、配置登录时长——都是同一个套路先定位表再写SQL最后回页面验证。这篇文章把我在E9、E10项目里反复用到的数据表按流程、表单、建模、组织权限几个维度拆开附可直接跑的SQL片段和踩坑记录适合刚接触泛微OA数据结构的实施新人也适合做集成的老手快速检索。1. 泛微数据表的三层结构先分清再动手1.1 系统层流程、表单、权限的基础表泛微OAEcology系列的数据库一般叫 ecology 库常见部署在 SQL Server 或 Oracle 上E10之后也有MySQL部署。不管哪个版本核心表都会保留 workflow_、formtable_、mode_custom_、hrm 这些前缀。我第一次看库的时候被几百张表惊到了其实真正高频使用的也就二三十张。系统层是这些表里最稳定的一批workflow_base 存流程定义workflow_requestbase 存流程实例hrmresource 存人员hrmdepartment 存部门sec_user 存登录账号。这些表基本不会因为某个业务模块开启或关闭而消失是所有业务数据的骨架。你在页面上做的任何操作最终都会落到这些表里或者落到它们衍生出来的业务表里。所以我建议新人在接手泛微项目时不要先去背几十张表名而是先理解这三个层次系统表、业务表、建模表。系统表回答“公司有哪些人、有哪些流程”业务表回答“某条流程的业务数据存在哪”建模表回答“客户、合同这些自定义业务对象的数据在哪”。搞懂这个后面的SQL查询才有方向。1.2 业务层E9和E10里业务数据到底存哪表单引擎是泛微使用率最高的功能。用表单设计器画一张报销单发布后系统会自动生成物理表主表叫 formtable_main_ 加一串数字明细表叫 formtable_detail_ 加一串数字。这个数字往往是表单ID或表单编码你可以在表单设计器的地址栏、流程绑定表单的信息里找到。E9和E10在这一点上保持了很高的兼容性表单物理表结构基本一致。E10的改动更多在模型和接口层面比如建模引擎能力增强、动作类接口更规范但对习惯了E9表结构的开发者来说迁移成本不大。这也是为什么网上很多E9的SQL思路在E10照样能用。做E10实施时不要一上来就想着表结构大变先按E9的思路排查通常能解决八成问题。1.3 建模层不用写代码也能建表数据去哪了建模引擎是泛微给实施人员的“低代码数据库工具”。在建模引擎里新建一个“客户管理”模块配置字段、列表、明细系统同样会在后台生成物理表表名以 mode_custom_ 开头后缀是模型ID。如果模型配置了明细一般还会生成配套的明细物理表。我在项目里常用建模表做两类事一是承接集成数据比如把外部系统的客户档案同步进来二是做流程外的辅助业务比如预算台账、项目信息。和表单表不同建模表通常没有强流程绑定你可以把任何需要持久化的数据放进去然后用系统的数据权限配置控制谁能看。这里要特别提醒不要直接在数据库物理表上改结构泛微的物理表结构与其元数据配置强关联直接改表极易造成建模界面显示异常。2. 流程链路核心表拿到流程ID之后怎么追查2.1 workflow_base 与 workflow_requestbase定义和实例必须分开看一说到流程最容易混的就是“流程定义”和“流程实例”。workflow_base 是流程定义一张流程模板一行记录哪张单走什么节点都在这里workflow_requestbase 是流程实例每发起一次请假、报销就新增一行。你可以把 workflow_base 理解为审批流程图workflow_requestbase 理解为这张图具体跑起来的那次记录。workflow_base 常用字段有 workflowid、workflowname、formid。workflow_requestbase 常用字段有 requestid流程请求ID即流程实例ID、workflowid关联定义、requestname标题、creater、createdate、currentnodeid当前所在节点、status流程状态。后端报表、待办集成、通知推送基本都是围绕 workflow_requestbase 做文章。2.2 workflow_currentoperator 与 workflow_nodebase节点和操作者怎么串拿到一个流程实例从 requestid 出发先查 workflow_base 拿到流程定义再看 workflow_currentoperator 就能知道当前轮到谁处理。workflow_currentoperator 是流程待办和已办最核心的表每条记录对应一个节点上的一位或多位处理人。字段常见的有 requestid、nodeid、userid、useridtype、isremark是否已读、receivedtype接收方式等。workflow_nodebase 是节点定义表存的是每个流程的节点名称、节点类型、是否开始节点、是否结束节点。查流程走到哪一步就是 workflow_currentoperator 里的 nodeid 关联 workflow_nodebase 里的 nodename 的过程。这个关联关系我几乎每周都要用写多了之后闭着眼都能背出来。2.3 查流程ID、当前节点和办理历史的SQL写法我在项目里最常用的两个查询一个查某个人当前待办一个查某条流程的审批足迹。查待办的SQL长这样SELECT DISTINCT wr.requestid, wr.requestname, wn.nodename, wc.userid FROM workflow_requestbase wr JOIN workflow_currentoperator wc ON wr.requestid wc.requestid JOIN workflow_nodebase wn ON wc.nodeid wn.nodeid WHERE wc.userid 1001 AND wc.isremark 0 ORDER BY wr.createtime DESC查某条流程的审批历史SELECT rq.requestid, rq.requestname, nb.nodename, hr.lastname AS 操作人, lg.operatetime, lg.remark FROM workflow_requestbase rq JOIN workflow_requestlog lg ON rq.requestid lg.requestid JOIN workflow_nodebase nb ON lg.nodeid nb.nodeid JOIN hrmresource hr ON lg.operator hr.id WHERE rq.requestid 1234 ORDER BY lg.operatetime这里 requestid 就是热词里常说的“流程ID”。很多接口和脚本要取的就是这个值。注意 workflow_requestbase 的 requestid 和 workflow_base 的 workflowid 是两回事前者是某一次实例后者是流程模板。我见过太多新人混用这两个字段查数据怎么都对不上。还有一个容易忽略的点办理历史表里还可能记录转交、退回、撤回等操作类型表里有的字段能区分做统计时最好过滤掉非审批操作。注意workflow_currentoperator 表数据量非常大尤其是上了几年的OA几千万行不稀奇。查询务必带 requestid 或 userid 条件不要裸查全表否则SQL能把数据库拖死。3. 表单结构与公式字段隐藏字段、下拉框、合计公式的关键3.1 formtable_main 主表和 formtable_detail 明细表的物理结构每张表单发布后就是一个物理表。主表字段和表单设计器里的字段一一对应字段名一般是英文字段名或控件名转换而来不一定跟页面上显示的中文标签一致。我在做数据核对时往往先在表单设计器里看字段属性再对到数据库列名否则极易找错列。明细表是独立的物理表表名一般带 detail 标识主表和明细表通过主表 requestid 或表单记录ID关联。需要注意明细表里本身也有 requestid 字段指向流程实例同时有一个自增主键用于区分明细行。写SQL汇总明细金额时务必按主表记录ID分组而不是简单SUM所有行。明细表命名还有个小规律同一个表单如果有多张明细后缀数字会递增对应表单设计器里添加明细表的顺序。3.2 筛选框隐藏字段、明细表下拉框类型调整的落地思路热词里有个“流程插入代码块根据筛选框隐藏字段”这其实是表单交互里的常见需求页面加载时根据某个下拉框筛选框的值决定其他字段显示还是隐藏。实现上有两种做法一是表单设计器里的字段联动或显示逻辑配置纯前端行为二是通过流程节点上的代码块或动作类在保存后把字段值写库并刷新页面。区别在于前者只是影响界面隐藏的字段可能不入库后者会真实落库还会触发后续的公式或权限计算。在E9里如果你想做“明细表中更改下拉框类型”常见操作是进入表单设计器选中明细表里的下拉框字段调整字段属性里的选项值或绑定数据字典。改完之后要注意存量数据物理表里存的是旧选项的显示值或编码如果你只改选项列表而没处理历史数据查询按新选项过滤就会漏掉老数据。我通常的做法是先对存量数据回刷再做选项调整顺序千万别反了。3.3 表单默认值与合计字段计算公式的触发逻辑表单默认值有两种来源一是字段属性里配置的默认值二是流程动作类在创建时写入的值。前者适合常量比如当前日期、流水号后者适合动态业务值比如根据部门带出负责人。热词里的“E9表单默认值”如果失效多半是配置了默认值但被流程事件覆盖或者字段只读导致默认值没触发排查时先分清是前端渲染问题还是后端写入问题。合计字段的触发原理也一样。明细表里如果有“数量”“单价”“金额”这类字段设置合计公式后前端是可以实时计算的但如果有人通过接口、批量导入、动作类直接改明细行合计字段不会自动变需要手动触发计算公式。我踩过的坑是集成脚本直接往明细表插数页面金额合计不变后来翻文档才发现公式字段的触发要靠系统接口不能只UPDATE物理表。4. 建模引擎表创建数据表与流程权限联动4.1 mode_custom_ 系列表建模一张表会生成什么建模引擎是泛微里特别实用的功能实施人员不用找DBA也能建业务表。我在项目里给客户建“项目台账”“客户档案”“预算科目”都是用建模引擎完成的。建模后在后台生成的表名规律很明显mode_custom_ 加数字ID主表就这一张如果开了明细还会配一张 mode_custom_ 数字ID_dt1 之类的子表。和表单表一样不要直接去改物理表否则建模界面的字段配置会和数据库不一致。建模表的字段命名也比较规律一般会以创建时填写的字段名去掉特殊字符。我在做集成开发时先在建模界面的字段列表里找到字段标识再到数据库里对应比靠猜快得多。注意建模表在生成时也会自动带上 requestid、creator、createdate 等系统字段这些字段在页面里有时看不到但SQL查询时一定存在。4.2 创建数据表并插入初始数据建表通常不直接写 CREATE TABLE而是用建模引擎界面配置。但插入初始化数据时用SQL还是方便的。比如客户档案建模表刚上线要把Excel里的几百条客户记录导进去我会先生成临时表再写INSERT语句注意主键ID不能和已有数据冲突INSERT INTO mode_custom_1001 (requestid, 客户名称, 联系人, 联系电话) SELECT NEXT VALUE FOR mode_custom_1001_id, 客户名称, 联系人, 联系电话 FROM temp_import_customer这里列名要替换成建模字段对应的实际列名。需要提醒的是建模表也有 requestid 等系统字段不要漏填。如果建表时启用了“流程相关数据”那么插入数据后还要往 workflow_relevantdata 或数据权限关联表写入关系否则在OA界面上可能查不到刚导入的数据。还有一种情况是表里自带了逻辑删除标记字段插入时要把删除标记置为有效值。4.3 E9流程选择客户后自动授权查询权限的链路热词里反复提到“E9流程选择了客户之后自动给流程相关人员赋予客户查询权限”。这个在实际项目里是这样闭环的表单里放一个“客户”字段类型通常是建模表数据选择或关联字段发起流程时选完客户系统会把客户记录ID和流程实例ID的关系写入相关数据表常见就是 workflow_relevantdata 这类“流程相关数据”表同时记录关联的表名、字段名。这样一来流程节点的相关人员就能通过数据权限配置查到自己参与流程所关联的客户。我建议在配置这一步时确认两点一是流程节点的“相关人员”范围是否正确二是数据权限策略是否勾选了“来源于流程相关数据”。这两个都对了客户查询权限才能自动带上。如果没生效优先检查 workflow_relevantdata 里是否真的有记录而不是一上来就去调权限策略。实际排查中见过很多次数据权限策略没问题纯粹是相关数据没写进去。5. 组织权限与账号配置常用表5.1 hrmresource、hrmdepartment、sec_user 的血缘关系泛微里的“人”分散在几张表。hrmresource 是人员主表存姓名、工号、直属上级、部门、分部hrmdepartment 是部门表通过 departmentid 关联人员部门上下级通过 supdepid 关联sec_user 是账号表存登录名、密码摘要、账号状态。数据库开发时查人一般查 hrmresource查登录账号才会关联 sec_user。很多报表逻辑要“按部门经理汇总”或者“查某人的下级”就是靠 hrmdepartment.supdepid 和 hrmresource.managerid 这类字段一层层递归。我在做组织架构同步时也会先理清这三张表的关联否则人员挂错部门会导致所有流程审批流都乱掉。比如查某个部门的全部下级部门人员SELECT hr.id, hr.lastname, hr.departmentid FROM hrmresource hr JOIN hrmdepartment dep ON hr.departmentid dep.id WHERE dep.supdepid 指定部门ID OR dep.id 指定部门ID5.2 登录时长设置到底改哪个表哪个配置登录时长不是单纯写SQL就能搞定的但排查方向有规律。OA后台的登录超时选项一般放在系统设置的安全类配置里不同版本菜单位置不同E8到E9、E10都有变化。数据库侧只负责记录配置项和日志比如系统配置表、登录日志表。真正生效的还要看部署的中间件比如 Tomcat 的 session 超时时间。我遇到过一次“改了后台登录时长没反应”的案例最后发现是部署层 session 超时设得更短用户还没到后台设置的时长就被中间件踢出。所以排查登录时长问题时顺序应该是先看后台安全配置再看中间件session配置最后看缓存是否刷新。三步走完基本都能定位。5.3 数据权限表与流程人员自动授权的关系泛微的权限体系里数据权限和功能权限是分开的。功能权限管“能不能进这个菜单”数据权限管“进了菜单能看到哪些行”。数据权限配置内容会落在系统权限相关表里但日常排查中我更习惯先看业务对象的关联关系比如前文说的 workflow_relevantdata。流程节点自动授权的思路在项目里很常见一种是把流程处理人写入明细权限表另一种是通过流程相关数据动态绑定。后者的好处是不用每次新流程都配置一遍权限适合客户、合同、项目这类高频业务对象。我在实施客户管理系统时默认就按动态绑定来做后续维护省了很多事。如果客户记录是历史导入、没有走流程那自动授权就不会生效这种情况要么走一次初始化流程要么单独维护数据权限需要跟客户讲清楚。6. Java动作类开发必查表budgetapproveactionforbip 这类业务动作怎么读数据6.1 BaseBean 加 Action 的动作类是什么泛微流程节点可以挂代码块后端代码块的核心就是写 Java 类。类名像 budgetapproveactionforbip extends BaseBean implements Action一眼就能看出是预算审批相关的动作类。这种类在流程节点被调用可以在节点进入前、提交后等时机执行 SQL 或调接口是解决复杂业务逻辑的主要手段。动作类的核心价值是解决纯配置搞不定的逻辑比如预算占用、成本校验、跨系统取数。写之前一定要先把涉及的表搞明白否则调试成本极高。类名里的 BIP 在泛微语境里通常指业务集成平台相关模块但使用的数据表依然是前面提到的那几张核心表所以表结构知识是通用的。6.2 动作类里经常使用的表和SQL写法我在动作类里一般通过 RecordSet 对象执行SQL最常见的是查流程主表和表单主表RecordSet rs new RecordSet(); rs.executeSql(SELECT requestid, workflowid, currentnodeid FROM workflow_requestbase WHERE requestid requestid); rs.next(); int currentNodeId rs.getInt(currentnodeid);然后根据 requestid 查表单主表拿到业务字段做预算校验或数据回写。在写这类动作类时我习惯先开数据库工具把SQL跑通再粘到Java里避免每次都要改代码编译部署才能试。等到逻辑稳定了再转成正式类这个习惯帮我省了几天的调试时间。避坑动作类里如果只做查询但不更新任何数据一定不要写不必要的 UPDATE。流程代码块的执行是有事务边界的一个失败的 UPDATE 可能把整个流程提交都回滚了这个问题排查起来很隐蔽。我吃过一次亏后来给自己立了规矩查询类动作类宁可多写几行注释也不碰 UPDATE 语句。7. 常见问题排查与实操避坑7.1 requestid 和 workflowid 到底有什么区别这个问题几乎每个新人都问过。requestid 是一条流程实例的ID从 workflow_requestbase 取workflowid 是流程模板的ID从 workflow_base 取。一个流程模板可以发起无数条实例所以 workflowid 是“1对多”里的“1”requestid 才是“多”里的每一个。取流程ID的接口、SQL、动作类里绝大多数场景要的是 requestid。如果对接文档里写的参数名是 flowId、processInstanceId也多半对应 requestid但最好先看接口说明确认。7.2 流程相关人员查询不到客户数据优先查 workflow_relevantdata 是否写入成功。如果表里有记录但页面还是看不到去查数据权限策略是否引用“流程相关数据”以及当前用户在流程节点上是否真的属于“相关人员”。不要一开始就怀疑权限策略大部分时候是关联数据没落库。还有一种常见情况是用户换过部门之后旧的关联数据没同步权限判断仍基于旧的组织关系这时候手动调整权限反而最快。7.3 明细表下拉框类型改了但不生效先看改的是不是字段属性里的“选项”而不是控件类型。如果只是调整选项值历史数据不会自动转换需要回刷存量字段值。前端不生效就清缓存看新请求后端不生效就查代码块和公式字段是否在覆盖。要注意下拉框里存的值和显示文本可能不一样修改选项时尽量保持编码不变、只改显示文本这样历史数据不用回刷。7.4 合计字段不刷新合计公式字段在直接UPDATE物理表后不会自动重算。正确的做法是通过表单或建模的接口更新明细或者调用系统的计算公式触发方法。我一般在集成脚本里先更新明细行再单独触发合计计算这两步缺一不可。如果在线调试时不方便调系统接口可以先在前端手动改一行明细字段看合计有没有重新计算用来判断到底是公式配置问题还是数据更新问题。7.5 登录时长改了没效果按后台安全配置、中间件session超时、缓存刷新的顺序排查多半能定位到。如果查不出就去部署目录里看有没有覆盖配置的定制文件有时集团统一下发的配置文件会覆盖本地配置。这类问题用排除法最快一次只改一个变量改完立刻登录验证。8. 常用数据表速查清单8.1 核心表速查表我把正文里提到的常用表做成一个速查表方便收藏。查表名按前缀记忆比挨个背强得多。表名用途常用字段workflow_base流程定义workflowid, workflowname, formidworkflow_requestbase流程实例requestid, workflowid, requestname, currentnodeid, statusworkflow_currentoperator节点待办/已办人员requestid, nodeid, userid, isremarkworkflow_nodebase节点定义nodeid, nodename, isstart, isendworkflow_requestlog流程操作日志requestid, nodeid, operator, operatetime, remarkworkflow_relevantdata流程与业务数据关联requestid, 关联表名, 关联字段值formtable_main_xxx表单主表requestid, 业务字段列formtable_detail_xxx表单明细表明细自增ID, requestid, 明细字段列mode_custom_xxx建模主表requestid, 建模字段列mode_custom_xxx_dt1建模明细表明细自增ID, requestid, 明细字段列hrmresource人员主表id, lastname, loginid, departmentid, manageridhrmdepartment部门表id, departmentname, supdepid, subcompanyidsec_user登录账号表id, loginid, 密码摘要, 账号状态system_acl 及权限相关表权限配置角色、资源、权限策略8.2 什么时候用哪张表做报表统计时先看表的数据量级和归属。查“某人提交了多少流程”用 workflow_requestbase按创建人过滤即可查“某流程当前卡在谁那里”用 workflow_currentoperator查“某张单据的业务字段”用对应 formtable_main_xxx查“客户的完整档案”用建模表 mode_custom_xxx 再加关联明细。这四个场景覆盖了我日常工作里八成以上的取数需求。如果你想做跨模块分析比如“某个客户今年发起了多少审批流程”就要把建模表、formtable_main_xxx、workflow_requestbase 三者关联起来思路是先从建模表定位客户记录ID再通过表单主表里的客户字段匹配主表 requestid最后用 requestid 去 workflow_requestbase 查流程实例信息。这个链路看起来长但每一段都有对应的表走通一次后面就顺了。最后再分享一个我的习惯无论做实施还是二次开发在动任何业务表之前先备份那几张表的原始记录或者用事务包裹再执行UPDATE。泛微的表关联非常深一次看似简单的SQL可能把流程、权限、表单、日志全带偏。数据表这东西多查一次少踩一次雷。