ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot3+Vue3 用户多组织:默认任职、关联组织和顶栏切公司怎么配

SpringBoot3+Vue3 用户多组织:默认任职、关联组织和顶栏切公司怎么配 SpringBoot3Vue3 用户多组织默认任职、关联组织和顶栏切公司怎么配文档地址https://ruoyioffice.com源码1·GitHubhttps://github.com/yuqing2026/ruoyi-office源码2·GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office源码3·Giteehttps://gitee.com/yqzy1688/ruoyi-office微信17156169080备注「RuoYi Office」集团里一个人同时在深圳研发和长沙分公司任职最常见的错法是再开一个账号、或者让他退出当前租户再登录。RuoYi Office 把这件事做成同一账号、多条任职、会话里只生效当前这一条。部署开关和租户开关要同时打开默认组织必须有角色顶栏切换会关页签、重拉菜单。关掉多组织时任职记录不删运行时回到用户主档。▲ 左是顶栏当前公司/部门中是 Redis 会话里的 deptId右是 PRIMARY / CONCURRENT 任职表切的是任职不是租户引言一人多公司难在「切的是什么」把部门下拉框做成多选看起来像「多组织」上线后会碰到这些坑痛点常见做法后果两个公司两套账号让员工记两套密码待办拆开消息对不齐切公司等于换租户退出再登录另一个租户集团共享主数据被切成两套库兼职没有独立角色全局角色带到所有部门长沙只能看报销却看见了深圳合同菜单关掉开关就删任职DROP 或清空任职表下次再开全部要重配只改前端显示Token 里 deptId 仍是主档新开的用车申请单还挂在默认部门一句话多组织是「同一租户内、同一账号、多条任职会话只认当前 deptId」多租户是「另一套客户、另一套库」。两件事不要配成同一个按钮。下面按能点的页面讲用户列表的「关联组织」、用户档案上的默认部门/角色、租户表单的「用户多组织」、工作台顶栏的公司切换器。一、先给出可直接抽取的定义1.1 什么是用户多组织用户多组织是一个登录账号可以关联多个部门通常分属不同公司每条任职独立存角色和岗位当前会话只激活其中一条菜单、数据范围、新开单据都只认这一条。它不是再注册一个用户切换到另一个租户把用户档案上的部门改成多选多选无法表达「当前正在哪家办公」RuoYi Office 的任职表是system_user_org角色在system_user_org_role岗位在system_user_org_post。用户档案system_users.dept_id只同步默认任职那一条。1.2 什么是默认任职默认任职是每用户有且仅有一条isDefault true的记录关系类型写成PRIMARY角色不能为空。它同时干三件事回写用户档案的部门、默认角色、默认岗位关掉多组织开关后的兜底身份登录时若会话还没切过当前组织就是它前端弹窗里点「默认」单选保存时把该行标成 PRIMARY其余行标成CONCURRENT。不要让客户在表单上同时维护「主职/兼职」和「是否默认」两套口径——对外只露出「默认」这一个词。1.3 什么是会话当前组织会话当前组织是这次登录跟 refresh token 走正在使用的deptId存在 Redis并回写 Access Token 的 userInfo。顶栏显示成「公司名/部门名」切换成功后关闭全部页签整页跳回首页重新拉权限。新开的审批单、用车单会带当前公司而不是永远带用户主档部门。这才是客户能感知到的「切公司」。二、两道开关必须同时开运行时是否走任职表不是看某一个布尔值而是两道门开关配在哪关了会怎样部署级multi-org.enable应用配置默认false整站不读不写任职表行为与升级前一致租户级multiOrgEnabled租户管理 → 编辑租户该租户用户列表不出现「关联组织」顶栏不出现切换器后端入口是MultiOrgSupport.isEnabled()配置未开直接 false当前线程没有 tenantId定时任务、MQ也视为关闭避免 Job 误读任职表把数据范围切乱。只开部署、不开租户集团里只有部分客户要用一人多公司其它租户保持「一用户一部」。只开租户、不开部署表单上能勾运行时仍当没开——这是最容易让实施以为「坏了」的组合先对这两项。无 tenantId 的后台线程一律当关闭这是有意的考勤汇总、合同到期提醒不应该因为某次顶栏切换把统计口径切到兼职部门。菜单怎么算多组织开启后鉴权、菜单、数据范围都只认当前任职的roleIds。人在深圳研发、角色带合同菜单切到长沙且长沙任职只配了 OA 角色合同模块会从侧栏消失。这不是 bug是任职隔离生效。若客户期望「菜单永远全集、只过滤数据」不要开多组织改用数据权限的「全部 / 自定义部门」。数据权限口径跟会话deptId角色配「本部门」时过滤条件用的是当前部门不是用户档案部门。验收用例写成三步更清楚——切到 A 部门能看见 A 的用车单切到 B 看不见 A 的「本部门」数据默认组织仍能在关联组织里改回去。三、用户列表关联组织才是多条任职的入口路径/system/user。租户已开多组织时行内操作除「编辑 / 删除」外多一列「关联组织」。超级管理员行会锁避免演示账号被改乱。▲ 左树是组织右表每行有编辑、关联组织、删除列表只配这一张后面看弹窗和顶栏点开后是独立弹窗不是把用户编辑表单拉长。弹窗标题是「关联组织 · {账号}」例如演示账号demomulti。每一条任职一张卡片字段必填说明部门是树选择同一用户不能重复选同一部门角色默认行必填该任职独立授权切换后菜单只认这一组岗位否同样按任职隔离默认全局唯一同步至用户档案去掉当前默认时自动把第一条顶上去底部有「 新增任职」。保存前前端会拦至少一条、部门不能空、默认必须恰好一条、默认必须至少一个角色。后端再拦一遍部门重复和默认角色避免绕过弹窗的脚本把用户存成「关开关后没菜单」。▲ 任职 1 勾了默认部门是研发部门角色按业务线多选说明文字写清「切换组织只激活该任职的权限」弹窗底部那句不要删客户经常问「编辑用户里的角色和这里的角色会不会打架」。答案是——开启多组织后档案上的角色/岗位只代表默认组织兼职的授权只在这张弹窗里配。接口GET /system/user-org/list?userId管理端查某人GET /system/user-org/my-list当前登录人自己的任职给顶栏PUT /system/user-org/save整表覆盖保存POST /system/user-org/switch只切会话不改任职表权限码system:user-org:query/system:user-org:update。查询、更新走「任一任职拥有即可」的鉴权避免人在兼职组织时改不了自己的关联。四、用户档案这里只配默认组织「编辑」打开的仍是经典用户表单用户名、昵称、部门、角色、岗位。开启多组织后部门/角色/岗位的帮助文案会改成开启多组织时此处为「默认组织」的岗位 / 角色其它任职请到「关联组织」逐条配置。不要在这张表上做部门多选。默认部门被改时后端应同步 PRIMARY 那一行而不是再插一条 CONCURRENT。批量「分配角色」也只追加到默认组织文案会写明其它任职请到关联组织配置。这是有意的产品切割档案管人任职管「人在哪家公司以什么身份干活」。混在一个表单里实施会把兼职角色覆盖掉默认角色。演示账号demomulti昵称「业务骨干」的编辑弹窗如下。部门仍是「研发部门」角色是一组业务线标签。请把它理解成默认组织的身份不要在这张弹窗里找「第二条公司」。▲ 标题是「修改用户」兼职公司、兼职角色不在这里而在行内「关联组织」关联组织弹窗底部还有一句实施常忽略的话关闭多组织以及薪酬、考勤、员工档案都认默认组织。兼职能切菜单、能开单据不等于工资和考勤日历跟着兼职部门走。算薪、排班、人事主档继续锚定 PRIMARY避免一个人在长沙兼一周就把深圳的假期额度切走。前端保存时也会把默认行标成 PRIMARY、其余标成 CONCURRENT避免两套口径if(rows.value.length0){message.warning(至少保留一条任职);return;}if(rows.value.some((item)!item.deptId)){message.warning(请选择部门);return;}constdefaultRowsrows.value.filter((item)item.isDefault);if(defaultRows.length!1){message.warning(必须且只能指定一个默认组织);return;}if((defaultRows[0]!.roleIds??[]).length0){message.warning(默认组织必须至少配置一个角色);return;}awaitsaveUserOrgList({userId:userId.value,orgs:rows.value.map((item,index)({...item,sort:index,relationType:item.isDefault?PRIMARY:CONCURRENT,})),});改的是自己的任职时立刻重拉权限否则顶栏orgList还是旧的。兼职行未配角色会显示橙色提示「未配角色时切换到此组织将看不到业务菜单」。五、租户表单用户多组织是租户级能力路径/system/tenant编辑某个租户表单靠下有「用户多组织」单选。帮助文案的要点开启后用户可关联多个部门顶栏和移动端可切换会刷新菜单与权限部署侧还要把multi-org.enable打开关闭后任职记录保留运行时回到默认组织▲ 开关与租户状态、演示保护排在一起每个租户独立图中该演示租户为关闭。当前登录租户若已开启顶栏仍可切组织从开到关前端会先打预览接口弹确认框内容大致包括已配置任职用户多少人、其中多少人有多条任职其它任职上单独配的角色条数将不再生效默认组织角色与用户档案不一致的人数关闭后菜单会变尚未配角色的任职条数「本部门」数据权限按默认部门过滤其它任职部门下的单据可能看不见相关账号请重新登录确认后才提交。不要静默关掉——兼职角色会立刻失效客服电话会响。六、顶栏切公司会关页签工作台右上角头像左侧格式是公司名/部门名例如「深圳总公司/研发部门」。只有multiOrgEnabled且orgList非空才渲染。任职只有一条时下拉禁用避免点开一个不能点的列表。▲ 切换器跟搜索、通知、头像在同一条顶栏切完会回到首页并重拉待办口径点开后标题是「切换组织」可搜公司或部门当前项打勾。确定前有确认框目标任职一个角色都没有警告「切换后将看不到任何业务菜单只能再切回其它组织」否则说明角色与岗位按任职独立生效会关闭全部页面并重新生成菜单确认后前端顺序是POST /system/user-org/switchbody 只有{ deptId }本地 userInfo 立刻改companyId/deptId/ 名称避免闪一下旧部门closeAllTabs()location.href跳到该用户的首页路径全屏遮罩文案是「正在切换组织请稍候…」。不要只改 Pinia 不刷新路由——动态菜单、数据权限指令都是登录后算出来的半刷新会出现「菜单还在、接口 403」。七、后端会话跟 refresh token不跟短 Access Token切换接口从请求头取出当前 Access Token查到对应的 refresh token再写 Redis。Access Token 往往十几分钟过期组织偏好必须活过「静静刷了一次 token」。publicvoidswitchCurrentDept(LonguserId,LongdeptId,StringaccessToken){if(!multiOrgSupport.isEnabled()){throwexception(USER_ORG_DISABLED);}if(!userOrgService.hasOrg(userId,deptId)){throwexception(USER_ORG_NOT_EXISTS);}OAuth2AccessTokenDOtokenoauth2AccessTokenMapper.selectByAccessToken(accessToken);longttlMath.max(remainingSeconds(token.getExpiresTime()),Duration.ofDays(30).getSeconds());userOrgSessionRedisDAO.set(token.getRefreshToken(),deptId,ttl);MapString,StringuserInfonewHashMap(token.getUserInfo());userInfo.put(LoginUser.INFO_KEY_DEPT_ID,String.valueOf(deptId));token.setUserInfo(userInfo);oauth2AccessTokenMapper.updateById(token);oauth2AccessTokenRedisDAO.set(token);}要点没有任职关系的deptId直接拒绝防止改请求体切到别的部门Redis key 形态是user_org_session:{refreshToken}TTL 至少 30 天Access Token 的 userInfo 同步改deptId网关/资源服务器读 Token 就能带上当前部门当前请求的LoginUser也立刻 overlay避免本请求后半段仍用旧部门微服务下网关可能把用户信息缓存约 1 分钟。拦截器会读请求头current-dept-id校验任职后覆盖LoginUser填这个窗口。前端在多组织开启时带上该头即可。publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){if(!multiOrgSupport.isEnabled()){returntrue;}LoginUserloginUserSecurityFrameworkUtils.getLoginUser();if(loginUsernull){returntrue;}Stringheaderrequest.getHeader(current-dept-id);if(StrUtil.isBlank(header)||!NumberUtil.isLong(header)){returntrue;}userOrgSessionService.overlayLoginUserDept(loginUser.getId(),Long.valueOf(header));returntrue;}overlay 同样会hasOrg校验请求头被改成未任职的部门时直接忽略不会把数据范围切到别人的公司。这是防篡改不是信任前端。八、保存任职默认角色是关开关后的救命绳保存不是「有几条插几条」那么简单。后端按部门去重缺省部门报错默认必须恰好一条。默认行的角色若显式传了空数组直接失败roleIds null表示这次只调部门、不改授权此时沿用档案上的全局角色——这是给老客户端留的兼容口。if(defaultCount!1||defaultDeptIdnull){throwexception(USER_ORG_DEFAULT_REQUIRED);}if(defaultItem.getRoleIds()!null?defaultItem.getRoleIds().isEmpty():CollUtil.isEmpty(permissionService.getGlobalRoleIds(userId))){throwexception(USER_ORG_DEFAULT_ROLE_REQUIRED);}StringrelationTypeBoolean.TRUE.equals(item.getIsDefault())?UserOrgDO.RELATION_PRIMARY:UserOrgDO.RELATION_CONCURRENT;删除某条任职时级联清掉该任职的角色和岗位避免下次同一部门重建时继承旧授权。兼职行的roleIds null同样表示不改避免只改部门就把角色清空。公司编号不手填由部门向上解析「组织类型 公司」的祖先写入companyId。顶栏展示的「深圳总公司」来自这里不是用户自己填的文本。九、三张表怎么拆不要把角色塞进任职表的 JSON 字段。切换组织时菜单计算要按userOrgId精确查角色JSON 无法走关联、也无法做「关闭多组织时统计兼职角色条数」。表一行代表什么关键字段system_user_org用户在某部门的一条任职userId、deptId、companyId、isDefault、relationType、status、sortsystem_user_org_role该任职拥有的一个角色userId、userOrgId、companyId、roleIdsystem_user_org_post该任职拥有的一个岗位userId、userOrgId、companyId、postId角色表、岗位表要按userOrgId/userId/roleId收敛查询不要误加租户列过滤。任职编号本身跨租户唯一在「访问其它租户」这类上下文里如果拦截器按当前 tenant_id 去滤关联表会把角色查成空集全站变成「没有该操作权限」。这是实施时最像权限 bug、其实是过滤条件套错的一类问题。用户档案仍保留一份全局sys_user_role它和默认任职对齐。关闭多组织后运行时只读档案不读任职角色——所以默认行必须有角色否则关开关等于把人锁在空白工作台。十、和多租户、数据权限分别对齐三件事经常被配到同一个需求里表格拆开更好讲概念隔离的是什么用户怎么操作多租户客户与客户库或 schema 或tenant_id登录时选租户或域名绑定多组织同一客户内的公司/部门任职顶栏切换不退出登录数据权限当前身份能看哪些行角色上的「全部 / 本部门 / 本部门及以下 / 仅本人 / 自定义」切组织之后「本部门」的口径跟着当前deptId走。人在深圳研发本部门数据权限就只看研发切到长沙分公司同一套角色定义会作用在长沙的部门树上。不要指望切组织却仍按用户主档过滤——那是没 overlayLoginUser。新开单据的公司、部门默认值也取当前会话而不是档案。客户验收时最直观的用例切到长沙 → 发起用车 → 单据抬头是长沙。十一、关闭多组织之后关闭不是删数据。任职表、任职角色、任职岗位都还在。运行时isEnabled()为 false顶栏切换器消失用户列表「关联组织」隐藏菜单和数据范围回到用户档案的部门、角色、岗位Redis 里的会话 deptId 不再被读取开关关了 overlay 直接 return预览框里已经写了副作用兼职上单独配的角色失效档案和默认任职不一致时菜单会变其它任职部门下的单据按「本部门」可能看不见。实施清单建议关之前导出任职确认默认行角色与档案一致再通知相关账号重新登录。重新打开时旧任职立刻可用不用重录——这就是「保留记录」的价值。登录后的权限包会带上multiOrgEnabled、orgList、defaultDeptId和当前deptId。当前部门优先取会话里 overlay 过的值没有则回用户档案privateLongresolveCurrentDeptId(AdminUserDOuser){LongcurrentDeptIduser.getDeptId();LongloginDeptIdSecurityFrameworkUtils.getLoginUserDeptId();if(multiOrgSupport.isEnabled()loginDeptId!null){returnloginDeptId;}returncurrentDeptId;}前端顶栏不自己调my-list拼菜单而是吃权限接口里的orgList。这样和按钮显隐用的是同一份数据避免「列表有任职、顶栏没有」。切换失败时不要只弹「系统错误」产品码已经写清错误码语义什么时候出现用户怎么处理当前租户未启用用户多组织部署或租户开关没开仍调 switch先对两道开关用户未关联该组织无法切换改了 deptId 或任职已删回到关联组织检查必须且只能指定一个默认组织保存时 0 条或多条默认弹窗里只留一个「默认」同一部门不能重复任职两条卡片选了同一个部门删掉重复行无法识别当前登录会话Access Token 对不上 refresh重新登录再切默认组织必须至少配置一个角色默认行角色空先给 PRIMARY 配角色十二、设计对照传统方案 vs 本方案决策点传统 / 错法本方案理由一人两公司两个账号一条用户、多条任职待办、消息、人事主数据不分裂当前公司存在哪只放 VuexRedis 跟 refresh token Token userInfo刷新 Access Token 不丢兼职角色共用全局角色每条任职独立roleIds长沙不能看见深圳合同菜单关开关清空任职保留运行时回主档可逆实施敢关无角色任职允许保存默认行禁止空角色切换时警告防止把自己切进空白工作台定时任务读会话部门无 tenantId 视为未开启Job 口径稳定十三、技术亮点设计要点实现方式价值双开关部署multi-org.enable× 租户multiOrgEnabled不是所有客户都要一人多公司默认唯一前后端都校验恰好一条 PRIMARY关开关有兜底身份整表保存PUT /save覆盖 级联删授权不会留下孤儿角色行会话 TTLmax(Access 剩余, 30 天)组织偏好活过短 Token切完硬刷新关页签 location.href动态菜单不会半新半旧请求头兜底current-dept-id拦截器网关缓存窗口内仍正确公司冗余部门向上解析写入companyId顶栏、统计不用每次爬树Job 隔离无线程租户上下文则关闭避免兼职污染批处理十四、FAQQ1能不能只开租户开关、部署保持 false不能当生产用法。表单能勾、运行时isEnabled()仍是 false客户会以为功能坏了。演示或灰度必须两道门一起开。Q2切换组织后已经打开的用车详情还算哪家公司页签会被关掉。请重新从列表进。不要在切换后指望旧页签里的表单公司字段自动变——那是另一份已加载的数据。Q3兼职没有角色为什么还允许保存默认行不允许空角色。兼职行允许先建部门、后补角色但顶栏切换会二次确认「菜单会空」。给实施一个「先挂上部门」的窗口同时不让用户无提示地切进去。Q4这和 SaaS 多租户套餐有什么关系套餐决定租户能用哪些菜单多组织决定同一租户里一个人能以几个部门身份工作。可以只开套餐不开多组织反之则两道开关都要开。Q5移动端怎么切同一套my-listswitch接口。切完同样要清掉本地路由缓存并重拉权限不要只改展示用的部门名称。Q6薪酬和考勤会不会跟着兼职部门走不会。弹窗说明写了薪酬、考勤、员工档案认默认组织。兼职只影响「当前能点哪些菜单、新单算哪家公司」。要把工资发到长沙应走人事调动或改默认而不是顶栏切一下。Q7网关缓存 1 分钟切组织后接口还用旧部门怎么办请求头带current-dept-id后端拦截器校验任职后覆盖本次LoginUser。这是填缓存窗口不是第二套会话存储。十五、快速体验在线演示https://ruoyioffice.com/web/账号 admin / admin123建议路径系统管理 → 租户列表 → 编辑当前租户确认「用户多组织」为开启且部署已打开multi-org.enable系统管理 → 用户管理找一个非超管账号点「关联组织」加一条其它公司的部门给独立角色用该账号登录看顶栏是否出现「公司/部门」切到兼职组织确认菜单变化、新开单据的抬头公司变了再切回默认页签应全部关掉并回到首页可选关掉租户开关确认顶栏消失、任职弹窗不再展示重新打开后旧任职还在实施上线前再对一次清单避免「功能开了但菜单空」检查项通过标准部署multi-org.enable与要启用的环境一致默认 false 不要误以为「配了租户就够」租户multiOrgEnabled目标租户为开启其它租户保持关闭也可以每个用户恰好一条默认关联组织弹窗里默认单选唯一默认行至少一角色关开关后仍能进系统兼职角色按需空角色会在切换时警告不是保存时报错顶栏能搜到兼职公司权限接口orgList非空切完新单抬头用车/报销所属单位变成当前公司薪酬考勤仍认默认不要用顶栏切换当人事调动源码仓库GitHubhttps://github.com/yuqing2026/ruoyi-office GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office Giteehttps://gitee.com/yqzy1688/ruoyi-office结语一人多公司的正确抽象是任职不是第二个账号也不是换租户。默认行保证关开关后还能进系统兼职行保证菜单和数据范围按当前公司收紧会话跟 refresh token 走顶栏切完硬刷新客户才不会遇到「界面上换了公司、单子还开在原来的部门」。集团型 OA、连锁门店、项目型组织人挂多个项目部都可以复用这套「任职表 会话 deptId」。先把两道开关和默认角色配明白再谈移动端和数据权限口径。想要体验 RuoYi Office 的强大功能在线演示https://ruoyioffice.com/web/账号 admin / admin123源码仓库GitHubhttps://github.com/yuqing2026/ruoyi-office GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office Giteehttps://gitee.com/yqzy1688/ruoyi-office技术咨询添加微信17156169080备注「RuoYi Office」⭐如果觉得不错请给个 Star 支持一下
返回列表