创建 / 维护用户(写系统库)¶
目标:Agent 直接写数据库 创建或维护前台用户(登录账号 + 用户档案 + 部门角色绑定)。知识以本文为准。不走管理端 UI、不调 REST。
REQUIRED SUB-SKILL: 写库成功后必须用 rebuild-index 的 clear_cache.py 刷新缓存。禁止只 INSERT 就声称完成。
术语:
| 说法 | 落点 |
|---|---|
| 用户名 / 账号 / loginno | T_ACCOUNT.LOGINNO(登录名) |
| 显示名 / 姓名 | T_USER.NAME(未给则 = 用户名) |
| 所属角色 | workspace role/{名}.role 的 id → T_USER_DEPARTMENT_ROLE_SET.ROLEID |
| 所在部门 | 系统库 T_DEPARTMENT 的 id → 绑定表 DEPARTMENTID + T_USER.DEFAULTDEPARTMENT |
| 系统库 | Java Spring Boot 服务的 spring.datasource(usercenter / lite / runtime 连的那套库) |
| 应用库 | 软件 .datasource 指向的业务库(TLK_ 等)。**禁止**当作用户库 |
角色本体在 workspace 文件,不在 t_role。历史 INSERT INTO t_role 已失效。
硬性规则:默认授予「工作台」员工角色¶
创建的用户需要默认授予「工作台」应用的员工角色。 无例外。
| 项 | 值 |
|---|---|
| 软件 | desk.application(<name>desk</name>,<description>工作台</description>) |
| 角色文件 | desk.application/role/员工.role(<name>员工</name>,defaultRole=true) |
| 绑定 | 再写一行 T_USER_DEPARTMENT_ROLE_SET,ROLEID = 该文件根属性 id(须读文件,禁止把「员工」当 ROLEID) |
| 部门 | 与本次用户的 DEPARTMENTID 相同 |
与 --app-dir 业务角色**并存**(两行;若业务角色已经是工作台员工则只保留一行,勿重复插)。
禁止:
- 只绑业务软件角色、漏绑工作台员工(前台无「发起新建 / 我的待办 / 经办跟踪」等 desk 入口)
- 维护用户时
DELETE掉工作台员工行,或把全部绑定「替换」成仅业务角色 - 找不到
desk.application/role/员工.role仍声称创建完成(停手问用户)
定位 desk.application:优先 --app-dir 的**同级目录** desk.application;再沿父目录查找 storage/workspace/desk.application。用 <description>工作台</description> 或 <name>desk</name> 确认,不要凭目录名猜测其它软件。
写库成功后 clear_cache.py 须对 业务 --app-dir 与 desk.application 都执行(未改 XML 不必 rebuild index)。
产出物¶
| 部分 | 落库 | 说明 |
|---|---|---|
| 登录凭证 | T_ACCOUNT |
LOGINNO 全局唯一;密码为密文 LOGINPWD |
| 用户档案 | T_USER |
属企业域;ACCOUNTID → 账户;DEFAULTDEPARTMENT 必填 |
| 部门-角色 | T_USER_DEPARTMENT_ROLE_SET |
三元组 (USERID, DEPARTMENTID, ROLEID);无此行则前台看不到非系统软件。创建用户必须额外授予「工作台」应用的员工角色(可与业务角色并存,多行) |
同 LOGINNO 已有账户时**复用** T_ACCOUNT(一账户可对应多域多个 T_USER)。
推荐脚本(本目录):
python "<skill-dir>/scripts/create_user.py" --loginno zhangsan --name 张三 --role 员工 --department 研发部 --app-dir "<某个.application绝对路径>"
# 未传 --password 时 LOGINPWD 使用 admin1 的 123456 密文(见下)
连接参数未给齐时脚本会按下文「定位系统库」尝试读取;仍不确定则**停止并问用户**。
何时用 / 不用¶
用:新建测试账号;给已有用户改部门/角色/密码/姓名;用户说「按用户名、角色、部门创建用户」。
不用:生成 .role 文件(用 role 技能);改业务表 TLK_*(那是应用库);走管理端/REST 创建(本技能只写库)。
门禁(写库前必须齐)¶
缺一则询问,禁止猜测后直接 INSERT:
- 用户名(loginno)
- 所属角色(名称或 id)+ 角色所在 软件
.application路径(用来读.role的 id,并给clear_cache用) - 所在部门(名称或 id)
- 密码:未给则默认明文
123456(写入下文固定密文)。其它明文须加密或从已有账号复制。禁止把明文写入LOGINPWD - 系统库连接:按「定位系统库」;无法唯一确定时必须问用户(JDBC URL 或 host/port/库名/账号/密码)
可选:显示名(默认=用户名)、企业域(默认可从部门 DOMAIN_ID 推导)、邮箱/手机。
定位系统库(禁止用应用 .datasource)¶
用户中心表由 平台全局库 管理,对应正在跑的 Java Spring Boot 服务(常见 obpm-lite 8888,或 obpm-usercenter / obpm-runtime)。**不要**打开 {软件}.application/datasource/*.datasource 当作用户库——那是创建应用时的业务库,即使碰巧同一 MySQL 实例,连接来源也必须是服务配置。
按序尝试:
- 用户已给出 JDBC / host+port+库名+账号
- 正在使用的服务配置:
obpm-lite→obpm-lite/src/main/resources/application.properties的spring.datasource.url;微服务则看 usercenter(或 runtime)对应application.properties - 环境变量:
DB_HOST、DB_PORT、DB_USERNAME、DB_PASSWORD(缺省常见localhost/3307/root/Teemlink2010,仅作候选,须能连上并验表)
Lite 样例(仓库默认,部署可能不同):
jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3307}/obpm_lite?...
spring.datasource.username=${DB_USERNAME:root}
spring.datasource.password=${DB_PASSWORD:Teemlink2010}
验库(必须先跑,失败即连错库):
SHOW TABLES LIKE 'T_USER';
SHOW TABLES LIKE 'T_ACCOUNT';
SHOW TABLES LIKE 'T_USER_DEPARTMENT_ROLE_SET';
SHOW TABLES LIKE 'T_DEPARTMENT';
SHOW TABLES LIKE 'T_DOMAIN';
五张都在 → 系统库。只有 TLK_* / 没有 T_ACCOUNT → 应用库,停手。候选连接 ≥2 条且验库都过、或一条都不过 → 问用户。
解析角色 / 部门 / 域¶
角色 id(workspace,不是库表)¶
读 {软件}.application/role/{角色名}.role 根属性 id。可按 <name> 匹配。多个 .role 重名或找不到 → 问用户。**禁止**把角色名写入 ROLEID。
部门 id(系统库)¶
SELECT ID, NAME, DOMAIN_ID, VALID
FROM T_DEPARTMENT
WHERE NAME = '{部门名}' AND (VALID = 1 OR VALID IS NULL);
0 行 / 多行(跨域重名)→ 问用户。不要自动建部门。
企业域¶
优先部门的 DOMAIN_ID。否则 SELECT ID, NAME FROM T_DOMAIN:仅 1 行可用;多行 → 问用户。
创建 vs 维护¶
SELECT u.ID, u.NAME, u.DOMAINID, u.ACCOUNTID, u.DEFAULTDEPARTMENT, a.LOGINNO
FROM T_USER u
JOIN T_ACCOUNT a ON u.ACCOUNTID = a.ID
WHERE a.LOGINNO = '{loginno}' AND u.DOMAINID = '{domainId}';
| 结果 | 动作 |
|---|---|
| 0 行 | 创建:账户(按 loginno 复用或新建)+ 用户 + 绑定 |
| 1 行 | 维护:改姓名/部门/角色/密码(只改用户明确要改的字段) |
| 多行 | 停手问用户 |
另:SELECT ID, LOGINNO FROM T_ACCOUNT WHERE LOGINNO = '{loginno}' —— 已有账户则复用 ID,只补本域 T_USER。
写库约定¶
主键:Sequence.getSequence() 风格 19 位字母数字,或 __ + 短 UUID;库内不冲突即可。T_USER_DEPARTMENT_ROLE_SET.ID 同样生成(hbm 虽标 uuid,批量插入用 Sequence)。
表名以 Hibernate 为准:T_ACCOUNT / T_USER / T_USER_DEPARTMENT_ROLE_SET(MySQL 大小写不敏感时 t_user 亦可)。
布尔用 0/1。STATUS=1(在职有效)、LOCKFLAG=1(未锁)、ORDERBYNO=10000、PERMISSION_TYPE='public'、ISFIRSTLOGIN=1(新建)。
NAME_LETTER:中文用拼音首字母(如张三→zs);英文用小写字母。可空,但列表排序会受影响。
密码(LOGINPWD)¶
必须写密文,禁止明文。默认加密模式 0:Security.encryptPassword(Blowfish-CBC)。
优先:复制已知账号密文。 安装包内置前台账号 admin1 明文为 123456,其 T_ACCOUNT.LOGINPWD 可直接复用(Blowfish 带随机 IV,同明文每次加密结果不同,但同一密文可反复登录):
| 来源 loginno | 明文 | LOGINPWD(原样写入) |
|---|---|---|
admin1 |
123456 |
77e558a1dd70259b8b10219707b8d54c0cec5d7b3fb3a200 |
新建用户未指定密码、或密码就是 123456 时:直接写入上表密文,不必再加密、不必连 admin1 现查。
其它明文:--copy-password-from {已有loginno}(明文须与源账号相同),或脚本按模式 0 加密(需 pycryptodome)。模式 1 为 SHA1 小写 hex。
创建 SQL(事务内按序)¶
1. T_ACCOUNT(loginno 尚不存在时):
INSERT INTO T_ACCOUNT (ID, LOGINNO, LOGINPWD, EMAIL, TELEPHONE, TELEPHONE2, ISFIRSTLOGIN)
VALUES ('{accountId}', '{loginno}', '77e558a1dd70259b8b10219707b8d54c0cec5d7b3fb3a200', NULL, NULL, NULL, 1);
-- 上式为明文 123456(admin1 密文);其它密码替换 LOGINPWD
2. T_USER:
INSERT INTO T_USER (
ID, NAME, NAME_LETTER, STATUS, DOMAINID, DEFAULTDEPARTMENT, ACCOUNTID,
LEVELS, DEPARTMENTUSER, USEIM, ORDERBYNO, LOCKFLAG, PERMISSION_TYPE,
LIAISON_OFFICER, TELEPHONEPUBLIC, TELEPHONEPUBLIC2, EMAILPUBLIC, USERINFOPUBLIC,
LASTMODIFYTIME
) VALUES (
'{userId}', '{name}', '{nameLetter}', 1, '{domainId}', '{deptId}', '{accountId}',
0, 0, 0, 10000, 1, 'public',
0, 0, 0, 0, 1,
NOW()
);
3. T_USER_DEPARTMENT_ROLE_SET(可多角色则多行;**至少**业务角色 + 工作台员工,已重合则一行):
INSERT INTO T_USER_DEPARTMENT_ROLE_SET (ID, USERID, DEPARTMENTID, ROLEID)
VALUES ('{setId}', '{userId}', '{deptId}', '{roleId}');
-- 默认再插工作台员工(ROLEID 读 desk.application/role/员工.role 根属性 id)
INSERT INTO T_USER_DEPARTMENT_ROLE_SET (ID, USERID, DEPARTMENTID, ROLEID)
VALUES ('{setIdDesk}', '{userId}', '{deptId}', '{deskEmployeeRoleId}');
三步同一事务。失败回滚。不要只插用户不插绑定。创建必须含工作台员工绑定。
维护 SQL¶
- 改部门:
UPDATE T_USER SET DEFAULTDEPARTMENT='{deptId}', LASTMODIFYTIME=NOW() WHERE ID='{userId}';该用户全部绑定行的DEPARTMENTID一并改到新部门(UPDATE T_USER_DEPARTMENT_ROLE_SET SET DEPARTMENTID=... WHERE USERID=...) - 改角色:为新
ROLEID补一行或改对应行;**不得删除**工作台员工绑定 - 改姓名:
UPDATE T_USER SET NAME=..., NAME_LETTER=... - 改密码:
UPDATE T_ACCOUNT SET LOGINPWD='{encrypted}' WHERE ID='{accountId}' - 不要无故新建第二个同域同 loginno 用户
- 维护结束时仍须保证存在工作台员工绑定(缺则补插)
Agent 步骤(按序)¶
1. 收齐门禁五项;缺则问
2. 定位并验系统库;不确定则问(禁止用 .datasource)
3. 解析业务 roleId、deptId、domainId;再解析 desk.application/role/员工.role → deskEmployeeRoleId
4. 按 loginno+domain 判断创建或维护
5. 事务写 T_ACCOUNT → T_USER → T_USER_DEPARTMENT_ROLE_SET(业务角色 + 工作台员工)
6. 向用户摘要:loginno、userId、accountId、部门、业务角色 id、工作台员工角色 id、创建还是更新
7. REQUIRED:对业务软件与 desk.application 都执行
python "<rebuild-index>/scripts/clear_cache.py" "<软件.application绝对路径>"
只清缓存即可(未改模板 XML,不必 rebuild pid.index)。按 .done/.failed 判定
8. 失败(.failed / 超时)必须警告;成功才可声称完成
clear_cache 会按 applicationId 清设计时对象缓存,并做附属全局清理(含 MyCache / 权限等)。监视进程须为 Runtime / Manager / Job;Designer 不处理 .sync/。
校验与常见错误¶
| 问题 | 处理 |
|---|---|
| 连的是应用库 | 验表失败;改用 Spring spring.datasource,或问用户 |
| 明文密码 | 无法登录;123456 用 admin1 密文;其它用加密或 copy-from |
| 未绑部门角色 | 前台看不到非系统软件;补 T_USER_DEPARTMENT_ROLE_SET + DEFAULTDEPARTMENT |
| 未绑工作台员工 | 无「发起新建 / 我的待办」等 desk 入口;补 desk.application/role/员工.role 的 id |
| 维护时删光其它绑定 | 会丢掉工作台员工;只 upsert,不整表替换 |
| ROLEID 写成角色名 | 绑定无效;用 .role 根属性 id |
| 跳过 clear_cache | 运行时可能仍用旧用户/权限缓存 |
INSERT t_role |
无效;角色在 role/*.role |
| 同域 loginno 重复再 INSERT | 应走维护 |
| 部门不存在就手建 | 先问用户 |
与其它技能边界¶
| 内容 | 本文 | 其它 |
|---|---|---|
| 系统库用户/账户/部门角色绑定 | ✓ | |
企业域绑定软件(t_domain.bind_applications) |
bind-domain-application | |
.role 文件、permissions |
generate-role-file | |
应用 .datasource / TLK_ |
禁止当作用户库 | generate-datasource-file |
| 写库后清缓存 | 必做,调用 clear_cache | rebuild-index |
测试账号文档 USER.md |
可提示补 | run-testcase |