数据权限(@DataPermission)
迁移中 工作量最大的一处。
Java 侧做法
Java 用 @DataPermission 注解 + SpEL + MyBatis 拦截器动态拼 SQL,做部门 / 角色数据隔离。注解写在 service / mapper 方法上,运行时由拦截器解析 SpEL、按当前登录用户的角色数据范围(data_scope)拼出 WHERE 子句。
Go 侧做法
Go 无 SpEL 注解 + AOP 拦截器的等价机制。本项目在 repository 层用 GORM Scopes / Callback 手写数据权限过滤。
- 数据权限是按角色配置的 SQL 片段(如「本部门」「本部门及以下」「全部」「自定义部门集」),存
sys_role.data_scope。 - 登录时把角色的数据范围解析成
LoginUser.DataScopeRoleMap,存进 sa-token session。 - repository 层查询时取当前用户的
DataScopeRoleMap,按角色拼出dept_id IN (...)或直接放行(超管 /data_scope=1全部)。
关键点
- 超管短路:
LoginUser是超管时直接放行,不拼任何条件(等价 Javadata_scope=1全部数据)。 - 条件在 repository 拼:service 不感知数据权限,只把 ctx 传下去;repository 从
AuditUserFrom(ctx)取登录用户,按其DataScopeRoleMap拼 GORM Scope。 - 登录态经 ctx 接力:GORM 回调只拿得到
*gorm.DB,取不到*gin.Context,故登录态由loginhelper.AuditContext中间件写进request.Context(WithAuditUser),repository 用AuditUserFrom(ctx)取。详见 一次请求的流转。
写新查询时的注意
- 带
@DataPermission的列表查询(如SelectAllocatedList、SelectUnallocatedList)在 repository 层手写 Scope,别只写裸WHERE。 dest常是 VO,不能拿 dest 反推表名(会打到不存在的表并丢逻辑删除条件),必须显式Model(&entity)。Count/SelectList/SelectPageList都要带Model(),del_flag过滤由LogicDelete挂在字段类型上,须先解析出实体 schema 才生效。
详见 CRUD 指南 / Repository 与各模块 repository 源码(如 internal/system/repository/user_repository.go 的 SelectAllocatedList / SelectPageList)。