致远互联 · 运维知识
身份证号一填,其余全自动
5 个 Groovy 函数,把年龄、性别、出生日期、籍贯一次性读出来
?? Groovy 自定义函数实战系列 · 第 2 期
这一系列只攻一件事,CAP 自定义函数。表单里那些翻来覆去手工干的活,配一次函数,往后就不用再管了。
这期聊 身份证号一键解析年龄/性别/生日/籍贯
下期聊 多个人员姓名(文本)中是否包含表单选人控件人员判断
?? 适用提示
适用产品:致远互联 CAP 表单(自定义函数)
适用场景:表单设计 · 身份证号自动取值
关键字符:Groovy / 身份证 18 位 / 公共枚举
难度指数:★★☆☆☆(会复制粘贴就能落地)
一张员工信息登记表,身份证号、出生日期、年龄、性别、籍贯——看着是五个字段,其实是一个字段。员工已经老老实实敲完 18 位身份证号,再让他把生日、年龄、性别、籍贯重复录四遍,既是体力活,也是错字温床。
身份证号从来不是一串随机数字。前 2 位指到省份,第 7 ~ 14 位是出生年月日,第 17 位是性别码。
下面给出 5 段可以直接复制的 Groovy 函数——出生日期、年龄、性别(文本)、性别(枚举)、籍贯(省级),以及把函数装进表单、并让它真正生效的操作路径。全程不需要装插件,也不需要动数据库。
PART01
适用场景:输一次,四个字段自己长出来
在表单中输入身份证号码后,年龄(数字控件)、性别(文本或枚举)、出生日期(文本控件)、籍贯(文本控件)内容自动生成。
?? 一句话理解
身份证号是唯一的输入,其余四个字段都是它的“派生值”。
派生逻辑写在「计算公式」里,用 Groovy 自定义函数实现。
函数只做计算,不写库、不联网——纯字符串运算。
PART02
效果展示:空着的时候 vs 填完之后
当身份证号为空,或者输入身份证号后,其他字段显示如下:
?? 截图 · 身份证号为空时的表单
空值不会让表单报错:性别-枚举退回到「不详」,年龄显示 0,出生日期与籍贯留空——这正是函数里那句 return "" / return 0 在兜底。
?? 截图1 · 填入 18 位身份证号后的表单
?? 截图2 · 填入 18 位身份证号后的表单
两张图的身份证完全不同:截图1那位算出来 性别男、38 岁、1987-11-03、四川省;截图2那位算出来性别女、 41 岁、1985-02-01、内蒙古自治区。性别文本和枚举两条路都走通了,值也一致。
PART03
先看清身份证号:三个下标定乾坤
动手写代码之前,先把“从哪一位读什么”对齐。这一节不谈语法,只谈位置。
配图:18 位身份证的三段读法
| 读什么 | 18 位怎么取 | 15 位怎么取 |
| 出生年月日 | substring(6,10) / (10,12) / (12,14) | 年份补 "19" 前缀,substring(6,8) / (8,10) / (10,12) |
| 性别码 | 第 17 位(下标 16) | 第 15 位(下标 14) |
| 籍贯(省级) | 前 2 位查表 | 前 2 位查表 |
?? 一条经验法则
性别码 奇数为男,偶数为女——“男单女双”,记这一句就够。
15 位身份证是上世纪的产物,年份只有两位,必须补 "19" 前缀。
两种情况都要先挡住长度:不是 15 位、也不是 18 位,直接返回空。
PART04
5 个 Groovy 函数,逐个抄
五个函数彼此独立,谁需要谁复制。函数名可以按自己的习惯改,但参数只绑一个:身份证号。
配图:5 个函数各管一个字段
函数一:从身份证提取出生日期
def p1 = param[0];if(p1 == null || p1.toString().trim() == ""){ return "";}String idCard = p1.toString().trim();if(idCard.length() != 15 && idCard.length() != 18){ return "";}String year, month, day;if(idCard.length() == 18){ year = idCard.substring(6, 10); month = idCard.substring(10, 12); day = idCard.substring(12, 14);} else { // 15 位身份证,年份补"19"前缀 year = "19" + idCard.substring(6, 8); month = idCard.substring(8, 10); day = idCard.substring(10, 12);}return year + "-" + month + "-" + day;
?? 抄的时候注意
18 位走 if 分支,15 位走 else 分支,两套下标不能混。
15 位那支的 年份补 "19" 前缀是关键,漏了就会算成 20 世纪前的年份。
返回值直接是 year + "-" + month + "-" + day,形如 1987-11-03。
函数二:从身份证计算年龄
def p1 = param[0];if(p1 == null || p1.toString().trim() == ""){ return 0;}String idCard = p1.toString().trim();if(idCard.length() != 15 && idCard.length() != 18){ return 0;}String year, month, day;if(idCard.length() == 18){ year = idCard.substring(6, 10); month = idCard.substring(10, 12); day = idCard.substring(12, 14);} else { year = "19" + idCard.substring(6, 8); month = idCard.substring(8, 10); day = idCard.substring(10, 12);}java.util.Calendar birth = java.util.Calendar.getInstance();birth.set(Integer.parseInt(year), Integer.parseInt(month) - 1, Integer.parseInt(day));java.util.Calendar now = java.util.Calendar.getInstance();int age = now.get(java.util.Calendar.YEAR) - birth.get(java.util.Calendar.YEAR);if(now.get(java.util.Calendar.MONTH) < birth.get(java.util.Calendar.MONTH)){ age--;} else if(now.get(java.util.Calendar.MONTH) == birth.get(java.util.Calendar.MONTH) && now.get(java.util.Calendar.DAY_OF_MONTH) < birth.get(java.util.Calendar.DAY_OF_MONTH)){ age--;}return age;
?? 这段为什么值得照抄
年龄不是简单的“今年减出生年”。只减年份,会把还没过生日的人算大一岁。
代码先减出年份差,再判两种情况各减 1:月份没到;月份到了但日子没到。
Calendar.MONTH 是从 0 开始数的,所以 month 要 -1——这一处最容易写错。
函数三:从身份证判断性别(文本)
def p1 = param[0];if(p1 == null || p1.toString().trim() == ""){ return "";}String idCard = p1.toString().trim();if(idCard.length() != 15 && idCard.length() != 18){ return "";}int genderCode;if(idCard.length() == 18){ // 第17位(下标16)为性别码 genderCode = Integer.parseInt(idCard.substring(16, 17));} else { // 15位身份证第15位(下标14)为性别码 genderCode = Integer.parseInt(idCard.substring(14, 15));}return (genderCode % 2 != 0) ? "男" : "女";
最后一行是全部逻辑的核心:取模 2 不等于 0 就是男,否则是女。三目运算符把判断压成了一行,读起来也顺。
函数四:从身份证判断性别(枚举)
def p1 = param[0];if(p1 == null || p1.toString().trim().length() == 0){ return 0;}String id = p1.toString().trim();int len = id.length();char genderChar;if(len == 18){ genderChar = id.charAt(16);} else if(len == 15){ genderChar = id.charAt(14);} else { return 0;}int num = Integer.parseInt(String.valueOf(genderChar));if(num % 2 == 0){ return 2;}return 1;
?? 说明
本案例中性别枚举绑定的是:公共枚举-性别,与代码一致,不需要单独调整。
注意这个函数返回的是 数字,不是“男/女”两个字。枚举控件认的是枚举值,代码里偶数给 2、奇数给 1、长度不对给 0,三个值必须和枚举项一一对上,否则控件会显示空白。
函数五:从身份证提取籍贯(省级)
?? 注意
Groovy 函数代码长度最大 4000 字符,若精确到市级(6 位行政区划码,约 3000+ 条)会超出上限,因此仅保留省级映射。
def p1 = param[0];if(p1 == null || p1.toString().trim() == ""){ return "";}String idCard = p1.toString().trim();if(idCard.length() != 15 && idCard.length() != 18){ return "";}String code = idCard.substring(0, 2);Map<String, String> provinceMap = new HashMap<String, String>();provinceMap.put("11", "北京市");provinceMap.put("12", "天津市");provinceMap.put("13", "河北省");provinceMap.put("14", "山西省");provinceMap.put("15", "内蒙古自治区");provinceMap.put("21", "辽宁省");provinceMap.put("22", "吉林省");provinceMap.put("23", "黑龙江省");provinceMap.put("31", "上海市");provinceMap.put("32", "江苏省");provinceMap.put("33", "浙江省");provinceMap.put("34", "安徽省");provinceMap.put("35", "福建省");provinceMap.put("36", "江西省");provinceMap.put("37", "山东省");provinceMap.put("41", "河南省");provinceMap.put("42", "湖北省");provinceMap.put("43", "湖南省");provinceMap.put("44", "广东省");provinceMap.put("45", "广西壮族自治区");provinceMap.put("46", "海南省");provinceMap.put("50", "重庆市");provinceMap.put("51", "四川省");provinceMap.put("52", "贵州省");provinceMap.put("53", "云南省");provinceMap.put("54", "西藏自治区");provinceMap.put("61", "陕西省");provinceMap.put("62", "甘肃省");provinceMap.put("63", "青海省");provinceMap.put("64", "宁夏回族自治区");provinceMap.put("65", "新疆维吾尔自治区");provinceMap.put("71", "中国台湾");provinceMap.put("81", "中国香港");provinceMap.put("82", "中国澳门");String province = provinceMap.get(code);return (province != null) ? province : "未知";
??? 这张表怎么看
键是身份证前 2 位,值是省级名称,31 个省市区 + 71 / 81 / 82 三个特别码。
查不到时返回 "未知",不会抛异常——表单不会因为一个陌生号码整页报错。
想做到市级,得先解决 4000 字符上限,否则代码存不进去。
PART05
装进表单:四步,最后一步最容易被漏
代码写对只是半程。函数不审核,表单跑起来照样报错。完整路径是这样:
配图:从建函数到生效的四步
第 1 步 在表单设计界面选控件、进「计算公式」
在表单设计界面,选中需要自动计算的控件,在「计算公式」中逐一创建 Groovy 函数:
?? 截图 · 计算公式设置:动态组合(普通)+ 自定义函数
图示里能看清三件事:控件属性面板右侧的「计算公式」入口、编辑区里的 selfFunction 调用、以及下方「函数自定义」按钮。四个字段各来一遍,一共建四到五个函数。
第 2 步 函数自定义:参数、代码类型、返回类型
?? 截图 · 函数自定义:参数设置 / 代码类型 / 返回类型
?? 照图填这几项
函数名称:Birthday、Sex、Nativeplace(截图里就是这么命名的,可自行调整)。
参数设置:身份证号;参数值取表单字段 field0002。
代码类型:Groovy;返回类型:文本类型。
把 Groovy 代码粘贴进函数体,点「确定」。
同类项可以在此界面逐一创建完成,不用反复开关窗口。
第 3 步 系统管理员后台审核,逐个放行
系统管理员后台页面→安全管理→安全审计→CAP 自定义函数审核,找到新建的函数,逐一审核通过。
?? 截图 · CAP 自定义函数审核:勾选「审核通过」→ 确定
?? 提示
新建的函数需要审核通过后才能生效,否则表单运行时会报错。
PART06
5 个容易踩的坑,提前避开
??? 坑位清单
坑 1 · 只建不审。函数建完不进审核列表,表单运行时直接报错,排查半天才发现代码根本没过审。
坑 2 · 想做到市级。4000 字符上限卡在那里,3000+ 条区划码塞不进函数体,先按省级落地更稳。
坑 3 · 枚举值对不上。函数四返回 1 / 2 / 0,枚举项必须同序,否则控件白屏。
坑 4 · 忘了 15 位。老档案里还有 15 位身份证,年份不补 "19" 会算出负数年龄。
坑 5 · 空值没挡。不判空、不验长度,用户随手敲两个字符,函数就抛异常。
写在最后
身份证号本身就是一个自带索引的小数据库,前 6 位管籍贯、中间 8 位管生日、第 17 位管性别。你缺的从来不是数据,只是一个会读它的函数。
能把重复录入省掉的表单,
才是好表单。
五个函数抄完,登记表就少四次手工输入,也少四次手滑的机会。
?? 下期预告 · 第 3 期
表单里一边是手工填的一串姓名,一边是选人控件里选出来的人,两边到底对不对得上?一个自定义函数就能给出结论。
多人员姓名包含判断
主题 · Groovy 函数实现多个人员姓名(文本)中是否包含表单选人控件人员判断
看点 · 姓名串存在文本控件里,人员存在选人控件里,两边的格式天生不一样。下期用一个自定义函数把它们都拆成集合再逐个比对,直接给出「在」或「不在」的结论。
?? 想第一时间看到更新,记得关注公众号「致远互联微服务」。
往期精彩
? 致远OA升级后启动不了?CAP自检没过 3 个救场方法
? V5 环境巡检工具实战
? 取日志不用再登服务器-致远OA日志打包实战
? 致远CAP实战:多个明细表去重拼接?
?? 关于致远互联
? 北京致远互联软件股份有限公司 · 证券代码 688369
? 成立于 2002 年 · 2019 年科创板上市
? AI-COP 数智化协同运营平台
? 服务超 5 万家中大型政企客户
? ?? 服务热线:400-700-8822
? ?? 知识库:support.seeyon.com
关注公众号「致远互联微服务」
定期更新运维知识干货 ?
股票
板块
用户
资讯
评论 (0)