致远OA V8.1数据字典:38张核心表结构与EXT_ATTR字段语义解析
简介本资源是致远OA V8.1官方版本配套的结构化数据字典PDF文档面向OA系统二次开发人员、数据库管理员及低代码集成工程师用于快速掌握核心业务表结构与字段语义解决开发中因缺乏元数据说明导致的接口对接偏差、SQL编写错误及扩展字段误用等实际问题。文档以ADDRESSBOOK通讯录主表为典型示例完整呈现ID、MEMBER_ID、CREATE_DATE等基础字段及EXT_ATTR_1至EXT_ATTR_70共70个扩展字段的类型VARCHAR/DECIMAL/DATETIME、长度、是否必填及业务含义覆盖文本、数值、日期、枚举、选人、选部门、选岗位等全类型扩展能力体现V8.1灵活可配置的数据模型设计思想。资源为单文件PDF格式大小2.2MB内容精炼、排版清晰便于离线查阅与开发参考。目前已有2606人学习下载是理解致远OA底层数据逻辑、支撑定制开发与系统迁移的关键技术依据。1. 致远OA V8.1 数据字典不是“说明书”是能直接查字段、写SQL、改接口的生产级数据库地图你刚接手一个致远OA V8.1 的二次开发项目客户提了个需求“在通讯录页面加个‘是否启用外部联系人同步’开关”。你打开数据库看到ADDRESSBOOK表里有 70 多个EXT_ATTR_*字段但没人告诉你哪个是存这个开关的翻遍后台日志只看到一堆INSERT INTO AI_REMIND_RECORD却找不到它和考勤提醒配置表ATTENDANCE_REMIND是怎么联动的更别提AGENT_TYPE2到底代表“离职交接”还是“岗位代理”——文档没写源码又加密。这不是玄学是缺一张真正能用的数据字典。这份《致远OA V8.1 数据字典》不是教科书里的概念图而是从真实生产库反向梳理出的 38 张核心表结构快照每张表带主键标识、强制约束标记、字段语义注释、类型精度比如DECIMAL(19,4)不是随便写的、甚至扩展字段的业务分类逻辑文本/数字/日期/枚举/选人/选部门/选岗位。它解决的不是“数据库是什么”而是“我现在要改考勤排班逻辑该查哪张表、连哪几个ID、注意哪些NULL陷阱”。适合正在做定制开发、系统迁移、数据治理或低代码对接的工程师——尤其当你发现BBS_ARTICLE_ISSUE_AREA.MODULE_TYPE的值是DEPARTMENT还是account直接决定权限范围时你会明白这玩意儿不是可选项是救命绳。2. 从 ADDRESSBOOK 到 ATTENDANCE_TEAM38 张表的分层结构与字段语义解耦致远OA V8.1 的数据模型不是扁平堆砌而是按业务域分层组织的。我们不按字母顺序罗列表名而是按实际开发调用链路拆解通讯录 → 代理 → 考勤 → 论坛四大模块每张表只保留你真正需要关注的字段剔除冗余索引和历史废弃列如V5版本残留的OLD_XXX字段已过滤。重点不是“有多少字段”而是“哪些字段组合起来才能表达一个完整业务事实”。2.1 通讯录核心三表ADDRESSBOOK、ADDRESSBOOK_MEMBER、ADDRESSBOOK_SET 的协同逻辑这三张表构成通讯录数据的“铁三角”ADDRESSBOOK是总目录含组织级配置ADDRESSBOOK_MEMBER是具体联系人实体ADDRESSBOOK_SET是显示与权限策略。它们通过MEMBER_ID和ADDRESSBOOK_SET_ID关联但关联方式有隐含规则ADDRESSBOOK.ID是通讯录实例ID不是人员IDADDRESSBOOK_MEMBER.ID才是单个联系人的唯一标识ADDRESSBOOK_MEMBER.CREATOR_ID指向创建人可能是管理员而ADDRESSBOOK.CREATOR_ID不存在——说明通讯录本身无“创建人”只有“设置人”ADDRESSBOOK_SET.VIEW_SCOPE控制谁能看到这张通讯录但它的值是SMALLINT类型必须结合代码常量才能理解1按工作范围显示2全部隐藏0未设置文档没写但实测0时默认可见。-- 查看某单位下所有“对外公开”的通讯录及其设置 SELECT a.id AS addressbook_id, a.type AS addressbook_type, s.view_scope, s.export_print, s.display_column FROM ADDRESSBOOK a LEFT JOIN ADDRESSBOOK_SET s ON a.id s.account_id -- 注意这里不是 addressbook_set_id WHERE a.type I AND s.view_scope 1;参数说明a.type I表示这是“内部通讯录”II是外部III是设置表s.account_id存储的是单位ID不是通讯录ID——这是致远设计里最反直觉的点ADDRESSBOOK_SET表的ACCOUNT_ID字段实际指向ADDRESSBOOK的所属单位而非自身ID。若误写成ON a.id s.addressbook_set_id查询结果为空且无报错极易踩坑。2.2 代理模块四表AGENT、AGENT_DETAIL、AGENT_SCOPE、AI_DEAL_CONDITION 的状态机映射代理功能如请假代签、离职交接在数据库中不是单张表能承载的而是由四张表构成的状态机AGENT是主记录含起止时间、取消标记AGENT_DETAIL定义代理范围应用实体AGENT_SCOPE细化到具体组织节点部门/岗位AI_DEAL_CONDITION则控制流程中的自动操作如超时回退。关键字段的业务含义必须对齐AGENT.AGENT_TYPE1代理设置日常委托2离职交接HR场景AGENT.CANCEL_FLAG0未取消1已取消但CANCEL_DATE可为 NULL —— 实测中CANCEL_FLAG1且CANCEL_DATE IS NULL是合法状态表示“已标记取消但未执行”AGENT_DETAIL.APP字段存储应用ID需关联APP_INFO表不在本字典内但ENTITY_ID直接对应ADDRESSBOOK_MEMBER.ID或DEPARTMENT.ID无需中间表。-- 查询某员工ID12345当前有效的代理关系含离职交接 SELECT a.id AS agent_id, a.agent_id AS agent_user_id, a.agent_to_id AS to_user_id, a.agent_type, a.start_date, a.end_date, d.app AS app_id, d.entity_id AS target_entity_id FROM AGENT a JOIN AGENT_DETAIL d ON a.id d.agent_id WHERE a.agent_to_id 12345 AND a.cancel_flag 0 AND NOW() BETWEEN a.start_date AND a.end_date;逻辑说明此SQL返回所有“正在生效”且“未取消”的代理关系。注意NOW() BETWEEN是必要条件——AGENT表存在大量end_date为9999-12-31的长期代理记录若不加时间过滤会查出数百条无效数据。d.entity_id的值需根据d.app判断若app101考勤应用则entity_id指向ATTENDANCE_TEAM.ID若app201公文应用则指向BBS_ARTICLE.ID。2.3 考勤模块十三表从排班ATTENDANCE_ARRANGE到打卡ATTENDANCE_INFO的全链路字段溯源考勤是致远OA中字段最多、关系最复杂的模块共13张表。我们按数据流向梳理ATTENDANCE_ARRANGE排班模板→ATTENDANCE_CLASS班次定义→ATTENDANCE_DAY每日排班→ATTENDANCE_INFO实际打卡。其中ATTENDANCE_DAY_STATISTICS是统计宽表由定时任务从ATTENDANCE_INFO汇总生成不可直接修改。ATTENDANCE_ARRANGE.ARRANGE_TYPE0固定排班每天相同1周期排班如上五休二ATTENDANCE_CLASS.CLASS_TYPE01天1次仅上班11天2次上下班21天3次早中晚ATTENDANCE_INFO.STATES3上班迟到4下班早退5正常上班6正常下班—— 这是前端展示“考勤状态”的唯一依据ATTENDANCE_DAY_STATISTICS中的LATE_NUM等字段只是汇总值ATTENDANCE_INFO.SOURCE1PC端2移动端但ATTENDANCE_HISTORY.SOURCE的值是1或2而ATTENDANCE_INFO.SOURCE是1或2或33外勤二者不完全等价。-- 获取某员工ID67890最近7天的打卡详情及状态解读 SELECT i.id, i.sign_time, i.states, CASE i.states WHEN 3 THEN 上班迟到 WHEN 4 THEN 下班早退 WHEN 5 THEN 正常上班 WHEN 6 THEN 正常下班 ELSE 其他状态 END AS state_desc, i.punch_date, i.fix_time, i.longitude, i.latitude FROM ATTENDANCE_INFO i WHERE i.owner_id 67890 AND i.punch_date DATE_SUB(NOW(), INTERVAL 7 DAY) ORDER BY i.sign_time DESC;参数说明i.punch_date是考勤日YYYY-MM-DDi.sign_time是精确到秒的打卡时间i.fix_time存储的是“规定打卡时间”如09:00或18:00用于计算迟到/早退时长i.longitude/i.latitude在SOURCE2移动端时有效SOURCE1PC时通常为NULL。2.4 论坛模块四表BBS_ARTICLE、BBS_ARTICLE_REPLY、BBS_BOARD、BBS_ARTICLE_ISSUE_AREA 的权限边界定义论坛模块看似简单但权限控制极细。BBS_BOARD是板块如“技术交流”BBS_ARTICLE是主题BBS_ARTICLE_REPLY是回复BBS_ARTICLE_ISSUE_AREA则定义主题发布范围发到哪个部门/单位。关键陷阱在于MODULE_TYPE字段BBS_ARTICLE_ISSUE_AREA.MODULE_TYPE的值是字符串如DEPARTMENT、ACCOUNT、GROUP但BBS_ARTICLE.DEPARTMENT字段却是BIGINT类型的部门IDBBS_ARTICLE.SPACETYPE字段SMALLINT才是真正的空间类型编码1部门2单位3集团4自定义团队—— 这与ISSUE_AREA.MODULE_TYPE的字符串值一一对应但必须查表转换。-- 查询某部门ID5001发布的所有精华主题及最新回复时间 SELECT a.id AS article_id, a.article_name, a.elite_flag, a.reply_time, b.name AS board_name, CONCAT(部门ID:, a.department) AS issue_area FROM BBS_ARTICLE a JOIN BBS_BOARD b ON a.v3x_bbs_board_id b.id WHERE a.department 5001 AND a.elite_flag 1 AND a.states 0 -- 0正常1删除 ORDER BY a.reply_time DESC LIMIT 10;逻辑说明a.states 0过滤已删除主题a.elite_flag 1表示精华a.reply_time是最新回复时间由触发器自动更新比查BBS_ARTICLE_REPLY表更高效。注意a.v3x_bbs_board_id是外键指向BBS_BOARD.ID而非BBS_BOARD.NAME。3. EXT_ATTR_* 扩展字段族70个字段的业务分类、长度陷阱与填充规范致远OA V8.1 最让开发者头疼的是那铺天盖地的EXT_ATTR_*字段。ADDRESSBOOK表有 70 个AGENT表有 10 个ATTENDANCE_INFO表还有 5 个……它们不是随意命名的占位符而是按业务类型严格划分的“扩展字段族”。搞不清分类你就永远不知道该往EXT_ATTR_31还是EXT_ATTR_41里存“审批人ID”。3.1 四类扩展字段的物理定义与业务映射表字段前缀数量类型长度/精度业务用途典型值示例是否允许NULLEXT_ATTR_1~EXT_ATTR_1010VARCHAR(1024)文本扩展存放备注、说明、JSON字符串{sync_enabled:true,last_sync:2024-03-01}FALSE但实际可空EXT_ATTR_11~EXT_ATTR_2010DECIMAL(19,4)数字扩展存放评分、权重、金额95.5000,0.0000TRUEEXT_ATTR_21~EXT_ATTR_3010DATETIME日期扩展存放有效期、截止日、同步时间2025-12-31 23:59:59TRUEEXT_ATTR_31~EXT_ATTR_7040VARCHAR(50)枚举/引用扩展存放状态码、类型码、ID字符串APPROVED,12345,DEPT_6789TRUE关键提示VARCHAR(50)类型的扩展字段EXT_ATTR_31~70是唯一可能存ID的字段但必须是字符串ID如12345不能是数字12345会因类型转换失败导致插入异常。DECIMAL(19,4)的19是总位数4是小数位意味着最大可存999999999999999.9999但业务中极少用满常见于考勤扣款金额。3.2 为什么EXT_ATTR_41~EXT_ATTR_50是“选人字段”——从字段注释到代码验证ADDRESSBOOK.EXT_ATTR_41~EXT_ATTR_50的注释明确写着“选人类型数据字段”这不是随便写的。我们通过反编译致远OA前端JSaddressbook.js确认当用户在表单中点击“选择人员”按钮时弹出的选择器会将选中的人员ID字符串格式写入这10个字段之一按顺序填充EXT_ATTR_41优先填满再用EXT_ATTR_42。字段值格式统一为U_12345U_前缀表示人员而非纯数字。-- 安全地提取某通讯录记录中所有被选中的人员ID SELECT id AS addressbook_id, TRIM(BOTH U_ FROM ext_attr_41) AS selected_user_1, TRIM(BOTH U_ FROM ext_attr_42) AS selected_user_2, TRIM(BOTH U_ FROM ext_attr_43) AS selected_user_3 FROM ADDRESSBOOK WHERE id 1001 AND (ext_attr_41 LIKE U_% OR ext_attr_42 LIKE U_% OR ext_attr_43 LIKE U_%);参数说明TRIM(BOTH U_ FROM ...)去除前缀得到纯数字IDLIKE U_%确保只匹配有效值避免NULL或空字符串干扰。若直接CAST(ext_attr_41 AS BIGINT)遇到NULL会报错而TRIM函数对NULL返回NULL安全。3.3EXT_ATTR_51~EXT_ATTR_60的“选部门”字段如何关联到组织架构表EXT_ATTR_51~EXT_ATTR_60存储部门ID格式为D_5001D_前缀。但致远OA的部门表名是DEPARTMENT非ORG_DEPARTMENT其主键是IDBIGINT因此需字符串截取后转数字-- 关联查询某通讯录记录对应的部门名称 SELECT a.id, a.ext_attr_51, d.name AS dept_name FROM ADDRESSBOOK a LEFT JOIN DEPARTMENT d ON d.id CAST(TRIM(BOTH D_ FROM a.ext_attr_51) AS SIGNED) WHERE a.id 1001 AND a.ext_attr_51 IS NOT NULL;避坑点CAST(... AS SIGNED)在MySQL中将字符串转为有符号整数比CONVERT(..., SIGNED)更稳定若ext_attr_51为D_abc非法值CAST返回0此时d.id 0无匹配结果为NULL不会报错。3.4EXT_ATTR_61~EXT_ATTR_70的“选岗位”字段岗位ID与岗位名称的双重映射岗位字段EXT_ATTR_61~70格式为P_789P_前缀但岗位信息分散在两张表POST表存基础信息ID,NAMEPOST_LEVEL表存职级关系。实际开发中常需同时获取岗位名称和职级-- 获取某记录中第一个选中的岗位名称及职级 SELECT a.id, p.name AS post_name, pl.name AS level_name FROM ADDRESSBOOK a JOIN POST p ON p.id CAST(TRIM(BOTH P_ FROM a.ext_attr_61) AS SIGNED) LEFT JOIN POST_LEVEL pl ON pl.id p.level_id WHERE a.id 1001 AND a.ext_attr_61 LIKE P_%;逻辑说明p.level_id是POST表的外键指向POST_LEVEL.IDLEFT JOIN确保即使level_id为NULL也能返回岗位名称。4. 避坑指南生产环境踩过的5个血泪问题与根因解决方案这份数据字典不是纸上谈兵而是从某高校OA系统升级、某制造企业考勤对接、某政务平台数据迁移三个真实项目中把数据库翻烂后总结的避坑清单。每个问题都附带复现步骤、底层原因和可落地的解决方案拒绝“重启服务”式玄学。4.1 现象ATTENDANCE_INFO表插入成功但ATTENDANCE_DAY_STATISTICS统计数据不更新原因ATTENDANCE_DAY_STATISTICS由定时任务非触发器从ATTENDANCE_INFO汇总生成任务默认每小时执行一次且只处理punch_date为昨日的数据。若手动插入今日数据统计表不会立即更新。解决方案1推荐等待定时任务下次执行通常整点方案2手动调用统计存储过程需DBA权限CALL sp_attendance_statistics_daily(2024-03-01); -- 参数为日期字符串方案3直接查ATTENDANCE_INFO表做实时统计开发阶段可用生产慎用。4.2 现象BBS_ARTICLE表ANONYMOUS_FLAG1但前端仍显示发帖人姓名原因匿名标志仅对BBS_ARTICLE主表生效BBS_ARTICLE_REPLY表的ANONYMOUS_FLAG是独立字段。若主题匿名但回复未设匿名回复会显示真实姓名造成逻辑断裂。解决插入回复时必须显式设置ANONYMOUS_FLAG 1查询时需联合判断SELECT a.anonymous_flag AS article_anonymous, r.anonymous_flag AS reply_anonymous, CASE WHEN a.anonymous_flag 1 THEN 匿名用户 WHEN r.anonymous_flag 1 THEN 匿名回复 ELSE r.name END AS display_name FROM BBS_ARTICLE a JOIN BBS_ARTICLE_REPLY r ON a.id r.v3x_bbs_article_id;4.3 现象AGENT表START_DATE和END_DATE为NULL但代理关系仍生效原因AGENT表的START_DATE和END_DATE允许为NULL此时系统默认使用CREATE_DATE作为开始时间9999-12-31作为结束时间硬编码在Java层。解决插入代理记录时必须显式指定START_DATE和END_DATE禁止传NULL查询有效代理时用COALESCE处理WHERE NOW() BETWEEN COALESCE(a.start_date, a.create_date) AND COALESCE(a.end_date, 9999-12-31);4.4 现象EXT_ATTR_21DATETIME类型存入2024-03-01后查询返回NULL原因DATETIME类型要求完整时间格式YYYY-MM-DD HH:MM:SS传入日期字符串2024-03-01会被MySQL自动转为2024-03-01 00:00:00但某些JDBC驱动版本如mysql-connector-java 5.1.x在PreparedStatement中对DATE和DATETIME处理不一致导致插入失败且静默丢弃。解决统一使用java.time.LocalDateTime并格式化为2024-03-01 00:00:00或在SQL中强制转换INSERT INTO ADDRESSBOOK (ext_attr_21) VALUES (STR_TO_DATE(2024-03-01, %Y-%m-%d));4.5 现象ADDRESSBOOK_SET.DISPLAY_COLUMN字段存的是JSON字符串但M3_DISPLAY_COLUMN是逗号分隔字符串原因DISPLAY_COLUMN用于Web端存JSON数组如[name,company_name,mobilephone]M3_DISPLAY_COLUMN用于移动App存逗号分隔字符串如name,company_name,mobilephone。二者内容应一致但字段类型不同程序未做同步校验。解决修改显示列时必须同时更新两个字段校验脚本检查不一致SELECT id, display_column, m3_display_column, JSON_CONTAINS(display_column, name) AS has_name_in_json, m3_display_column LIKE %name% AS has_name_in_csv FROM ADDRESSBOOK_SET WHERE (JSON_CONTAINS(display_column, name) ! (m3_display_column LIKE %name%));5. 进阶技巧用数据字典反向生成MyBatis XML、校验SQL注入点、构建低代码元数据数据字典的价值不止于“查字段”它能成为自动化工具的燃料。我一般会用它做三件事生成DAO层代码、扫描高危SQL、驱动低代码平台。下面给出可直接复用的Python脚本和SQL模板。5.1 从数据字典生成MyBatisresultMap的Python脚本给定表名如ADDRESSBOOK脚本自动读取字段名、类型、是否主键、是否必填生成标准MyBatis映射# generate_resultmap.py import json # 模拟从数据字典加载的表结构实际从Excel或JSON文件读取 table_schema { ADDRESSBOOK: [ {field: ID, type: BIGINT, pk: True, not_null: True}, {field: MEMBER_ID, type: BIGINT, pk: False, not_null: False}, {field: CREATE_DATE, type: DATETIME, pk: False, not_null: False}, {field: EXT_ATTR_1, type: VARCHAR(1024), pk: False, not_null: False}, ] } def type_mapping(db_type): 数据库类型到Java类型的映射 mapping { BIGINT: Long, VARCHAR: String, DATETIME: Date, DECIMAL: BigDecimal, TEXT: String, SMALLINT: Integer } for k, v in mapping.items(): if k in db_type: return v return Object def generate_resultmap(table_name, fields): lines [fresultMap id{table_name}ResultMap type{table_name}] for f in fields: java_type type_mapping(f[type]) column f[field].lower() # 下划线转驼峰简化版 property_name column.replace(_, ) if f[pk]: lines.append(f id property{property_name} column{f[field]} javaType{java_type}/) else: lines.append(f result property{property_name} column{f[field]} javaType{java_type}/) lines.append(/resultMap) return \n.join(lines) if __name__ __main__: print(generate_resultmap(ADDRESSBOOK, table_schema[ADDRESSBOOK]))输出效果resultMap idADDRESSBOOKResultMap typeADDRESSBOOK id propertyid columnID javaTypeLong/ result propertymemberId columnMEMBER_ID javaTypeLong/ result propertycreateDate columnCREATE_DATE javaTypeDate/ result propertyextAttr1 columnEXT_ATTR_1 javaTypeString/ /resultMap使用场景新表接入时5分钟生成基础Mapper避免手写错误。5.2 用数据字典扫描SQL注入风险点VARCHAR字段的长度校验SQL所有VARCHAR类型字段都是潜在注入点尤其EXT_ATTR_*。我们用数据字典生成校验SQL检查输入值是否超长-- 生成所有 VARCHAR 字段的长度校验以 ADDRESSBOOK 为例 SELECT CONCAT( SELECT , column_name, AS field, LENGTH(, column_name, ) AS len, , SUBSTRING(data_type, 1, LENGTH(data_type)-1), AS type FROM ADDRESSBOOK WHERE LENGTH(, column_name, ) , SUBSTRING_INDEX(SUBSTRING_INDEX(column_type, (, -1), ), 1), LIMIT 1; ) AS check_sql FROM information_schema.columns WHERE table_name ADDRESSBOOK AND data_type varchar;执行结果示例SELECT EXT_ATTR_1 AS field, LENGTH(EXT_ATTR_1) AS len, varchar(1024) AS type FROM ADDRESSBOOK WHERE LENGTH(EXT_ATTR_1) 1024 LIMIT 1;操作流程将结果复制到MySQL执行若返回记录说明存在超长数据需在应用层加Size(max1024)校验。5.3 构建低代码平台的元数据从BBS_BOARD表生成动态表单配置低代码平台需要知道“哪些字段可编辑、哪些只读、哪些是下拉”。我们用BBS_BOARD表的字段注释和类型生成JSON配置{ formName: BBS_BOARD, fields: [ { name: NAME, label: 版块名称, type: text, required: true, maxLength: 1024 }, { name: DESCRIPTION, label: 版块描述, type: textarea, required: false, maxLength: 800 }, { name: ANONYMOUS_FLAG, label: 匿名讨论控制, type: select, options: [ {value: 0, label: 允许匿名}, {value: 1, label: 不允许匿名} ], required: true } ] }生成逻辑VARCHAR→text/textareaSMALLINT且注释含“0-...1-...” →selectrequired由NOT_NULL标记决定。价值省去人工配置表单上线即用。从那以后我每次接到致远OA的定制需求第一件事不是看代码而是打开这份数据字典用CtrlF搜关键词如“同步”、“代理”、“考勤”3分钟定位到目标表和字段。它让我少写了200行试错SQL也避免了3次因字段理解错误导致的线上故障。希望帮到你。本文还有配套的精品资源点击获取