luoweijian
54aa290c89
commit
4 weeks ago
luoweijian
4a4b31d8e1
commit
4 weeks ago
luoweijian
78aa9748e4
feat: 数据权限按业务模块可配(ADR-0018)
将数据权限从「单一全局档位」改造为「按业务模块分别配置档位」:
- 新增 sys_data_scope_module 注册表 + sys_role_data_scope 角色×模块档位表
- @DataScope 注解新增必填 module 属性,启动时校验 code 在注册表中存在
- DataVisibility 持有 Map<String, DataScopeLevel> 替代单个 level
- DataVisibilityContext.currentScope(moduleCode) 加参数取该模块的 scope
- DataScopeInterceptor 顺序调换:先找表→查注解→取该模块 scope→注入 WHERE
- PermissionResolverImpl 查 sys_role_data_scope 按 module_code 分组取最宽
- 删除 SysRole.dataScope 列 + 删除 DataScopeEnum.CUSTOM(5)
- RoleController saveOrUpdate 用 moduleScopes JSON 替代 dataScope
- 新增 GET /api/data-scope/modules 端点
- saveRoleWithScopes 原子事务(角色保存+范围全量替换同提交同回滚)
- DataScopeModuleController 返回 DTO 不泄露 BaseEntity 审计字段
- 一次性迁移 SQL 脚本(单事务,CROSS JOIN 旧值,DROP 旧列)
- DataInitializer 初始化 4 个内置模块 + ADMIN 每模块 ALL 档位
- 全量测试通过:crm-base 80 tests + crm-auth 153 tests
4 weeks ago
luoweijian
96230913ae
refactor: SystemController IO to DTOs (ADR-0017 ticket 07)
- new DeptDTO (extends BaseDTO, children tree, fromEntity/toEntity)
- new AssignDeptDTO (plain POJO, no key semantics)
- rename UserStatsVO -> UserStatsDTO
- menuTree returns List<ResourceNodeDTO>; PermissionResolver.visibleMenuTree maps entity->DTO before tree build
- deptTree returns List<DeptDTO>; saveDept takes DeptDTO; assignUserDepts takes AssignDeptDTO
- userPage drops @RequestBody -> form binding (aligns with documented form-urlencoded convention)
- sync role-management doc RoleDetailVO -> RoleDTO
- DataScopeIntegrationTest updated for ResourceNodeDTO
4 weeks ago
luoweijian
ef7ad8e368
refactor: RoleController IO to RoleDTO/RoleParam, sink paging into service (ADR-0017 ticket 06)
- rename RoleDetailVO -> RoleDTO extending BaseDTO, add toEntity()
- new RoleParam extends BaseParam; page() now uses PageConverter (size clamp + orderBy anti-injection), fixing pagination bypass
- saveOrUpdate 6 @RequestParam -> RoleDTO; detail returns RoleDTO
- add PageConverterTest covering size clamp + orderBy injection guard
4 weeks ago
luoweijian
f989b566cc
refactor: rename ResourceNode to ResourceNodeDTO extending BaseDTO (ADR-0017 ticket 05)
4 weeks ago
luoweijian
e888aeeb42
chore: mark tickets 02/03/04 as resolved (InitBinder + Dict IO)
4 weeks ago
luoweijian
0b4c4d2bbb
refactor: align dict tests with DTO return types (ADR-0017 tickets 03/04)
4 weeks ago
luoweijian
1a9cf36785
chore: stop tracking build artifacts (target/, .idea/, test-output.txt)
4 weeks ago
luoweijian
a542e48f86
commit
4 weeks ago
luoweijian
b50002f47f
commit
4 weeks ago
luoweijian
80bc06304c
chore: mark ticket 01 as resolved (BaseDTO refactor complete)
4 weeks ago
luoweijian
ae103a1f1c
refactor: BaseDTO 重构为 abstract 基类 + IBaseService 清理死代码 (ADR-0017 ticket 01)
- BaseDTO 改为 abstract,仅保留 id/createTime/updateTime(均加 @JsonInclude NON_NULL)
- 移除 creatorId/updaterId/deleted 字段及默认值——防止出参侧泄露
- 移除泛型 toEntity(Supplier) 方法——转换方法下沉到各 DTO 类
- IBaseService 删除 saveDTO/updateDTO 死代码(零调用方)
- BeanCopyUtils javadoc 同步更新(移除 BaseDTO.toEntity 引用)
- README §3/§4/§10 同步更新 BaseDTO 和 IBaseService 说明
- 修复 ResourceController/RoleController 预存 UTF-8 BOM 问题
- 新增 ADR-0017、spec.md、7 个 implementation tickets
Refs: ADR-0017, .scratch/controller-io-conventions/
4 weeks ago
luoweijian
ffbbb73143
commit
4 weeks ago
luoweijian
0dc2da99d5
commit
4 weeks ago
luoweijian
51b7e2a749
feat(auth,dict,base): extract PermissionSeeder seam to replace shadow entities (ADR-0016)
- Add PermissionSeeder interface + PermissionModuleDescriptor/ButtonSeed records in crm-base
- Implement PermissionSeederImpl in crm-auth with find-or-create idempotent seeding
- Refactor DataInitializer to use seam (3 seedModule calls) + @Order(1)
- Refactor DictPermissionInitializer to use seam (1 seedModule call) + @Order(10)
- Delete 6 shadow entity/mapper files from crm-dict (SysMenuSeed, SysRoleSeed, SysRoleMenuSeed + mappers)
- Rewrite DictPermissionInitializerTest to mock PermissionSeeder (no more H2 auth tables)
- Add PermissionSeederImplTest (H2 integration test covering idempotency, catalog sharing, role-not-found)
- Add ADR-0016 documenting the decision, rationale, alternatives, and consequences
- Create crm-base/CONTEXT.md with PermissionSeeder domain glossary
- Update CONTEXT-MAP.md to link crm-base/CONTEXT.md
Eliminates schema leakage: crm-dict no longer knows sys_menu's physical schema.
Runtime dependency accepted: crm-dict needs crm-auth's PermissionSeederImpl at runtime (via crm-app classpath aggregation).
4 weeks ago
luoweijian
ceaff54863
commit
1 month ago
luoweijian
ab9f64483c
refactor(crm-dict): judgement call #6 #7 — pom version extraction + VO independent fields
#6 mybatis-spring 版本提取到父 pom:
- 父 pom properties 新增 mybatis-spring.version=3.0.3
- 父 pom dependencyManagement 新增 mybatis-spring 依赖声明
- crm-dict/pom.xml 去掉 version 和 scope=compile(由父 pom 统一管理)
#7 VO extends 实体重构为独立字段:
- DictGroupVO:不 extends DictGroup,独立定义 9 个字段(排除审计字段和软删内部字段)
- DictItemVO:不 extends DictItem,独立定义 14 个字段
- DictGroupServiceImpl:DictGroup::getId -> DictGroupVO::getId(fillItemCount)
- DictItemServiceImpl:DictItem::getGroupId -> DictItemVO::getGroupId(enrich)
- DictGroupServiceTest:pageGroups 断言 DictGroup::getCode -> DictGroupVO::getCode
全量 48/48 测试通过。
1 month ago
luoweijian
f3fba4a229
fix(dict): clear client-supplied audit fields in saveGroup/saveItem — prevent strictFill bypass forgery
saveGroup/saveItem accepted entity params whose createTime/creatorId/updaterId
were not sanitized; MetaObjectFillHandler uses strictInsertFill/strictUpdateFill
which only fills null fields, so client-supplied audit values were retained.
Now: null out all audit fields on entry (framework refills), restore
createTime/creatorId from exist on edit.
1 month ago
luoweijian
3d55eeb196
feat(dict): data dictionary module — two-level flat model, CRUD/default/cache/permission/seed (ADR-0015)
- 4 tables (dict_group/dict_item/dict_group_default/dict_ref_count), delete_key soft-delete reuse
- DictGroup/DictItem full CRUD + force-delete + status toggle + enabled-list
- DictReferenceService: increment/decrement/batchApply/isReferenced, same-tx ref_count, no cache
- DictQueryService: Caffeine 10s cache + 2s negative cache for empty results, group.status join
- Default item: INSERT ON DUPLICATE KEY UPDATE, hibernate on group disable, auto-restore
- Permission seeds: 10 perm codes → sys_menu tree, bound to ROLE_ADMIN, no crm-auth import
- Built-in dict initializer: idempotent upsert, builtin=false never touched (US-41), value not overwritten
- 48 tests pass; full project compiles
1 month ago
luoweijian
50f79a884d
commit
1 month ago
luoweijian
3499353b28
feat(file): thumbnail preview - async generation + sync fallback, PDFBox/LibreOffice renderers, retry (ADR-0013)
1 month ago
luoweijian
a263f1736c
feat(auth): role management — ancestor completion, CRUD, permission assignment, cascade delete (ADR-0012)
Implements ADR-0005 (backend ancestor completion) and ADR-0012 (role management
architecture) across 5 tracer-bullet tickets:
- ISysMenuService.getAncestorIds: batch query full table, in-memory parentId
traversal, returns complete ancestor set (ADR-0005)
- SysRole.builtin field + DataInitializer idempotent refactor + 5 button
permission seeds (crm:role:list/detail/save/delete/assign) bound to ADMIN
- RoleController (/api/roles/*): page, saveOrUpdate, detail, assign-resources,
delete — pure hasAuthority, POST + action suffix, form params
- SysRoleServiceImpl.saveRole: roleCode uniqueness, builtin protection,
dataScope 1-4 range, create can't set builtin=true
- assignResources: calls getAncestorIds for ancestor completion, then
full replacement (delete + insert) of sys_role_menu
- getRoleDetail: returns RoleDetailVO with basic info + resourceIds
- deleteRoleCascade: cleans sys_role_menu + sys_user_role, builtin rejection,
three-table transaction
- SystemController: all role endpoints removed, unused deps cleaned
- Dead code removed: assignMenus, getMenuIdsByRoleId (superseded by
assignResources, getRoleDetail)
- H2 test schemas updated with builtin column
- 150 tests pass (16 new in SysRoleServiceImplTest, 7 in SysMenuServiceImplTest)
1 month ago
luoweijian
f0f70c6137
refactor(auth): deduplicate user location logic in login flow
Extract locateUser() as the single source of truth for 'identity binding ->
mobile match' resolution. Both preCheckOrgMembership (read-only probe) and
resolveUser (login path) now share the same implementation, eliminating the
risk of rule drift between the two call sites.
Identity query is passed through to avoid redundant DB lookups.
1 month ago
luoweijian
4759518aa0
refactor(auth): remove dead MenuTypeEnum, unify node type vocabulary to MenuType (ADR-0011)
MenuTypeEnum (DIR/MENU/BUTTON) duplicated MenuType (CATALOG/MENU/BUTTON) with
no callers. Removing it eliminates the DIR-vs-CATALOG domain vocabulary split
and leaves a single source of truth for node type rules (isLegalChild etc.).
1 month ago
luoweijian
b38ab9ec94
feat(auth): 动态权限拦截 + 封遗留写路径
基于 apiUrl 的动态权限拦截机制(fail-open,Ant 风格匹配):
- ApiPermissionCache:Caffeine 缓存 button 节点 apiUrl→perms 规则
- ApiPermissionInterceptor:HandlerInterceptor 匹配 URL 校验 authority
- ApiPermissionRule:pattern→perms 映射 record
- WebMvcConfig:注册拦截器 /api/**
- ResourceServiceImpl:save/delete 后触发缓存失效
封遗留写路径 + 收拢 Mapper 到 Service 层:
- 删除 /menus/save 和 /menus/delete(ResourceController 已覆盖)
- ISysRoleService.assignMenus() + deleteRoleCascade() 事务保护
- IAuthUserService.assignRoles() 事务保护
- SystemController 不再持有 SysRoleMenuMapper/SysUserRoleMapper
测试:ApiPermissionInterceptorTest 7 个 + ResourceServiceImplTest 缓存失效验证
1 month ago
luoweijian
c877f0d6c4
refactor(auth): 权限解析收成深模块 PermissionResolver
将 PermissionServiceImpl(双职责、无接口、吞异常、混合返回类型)重构为深模块:
- PermissionResolver 接口:resolve(userId) + visibleMenuTree(userId)
- PermissionGrant 不可变值对象:结构化分离 permCodes / roleCodes,asAuthorities() 幂等归一 ROLE_ 前缀
- resolve 变纯函数:不装载 DataVisibilityContext,由过滤器负责
- fail-closed:删除 try/catch 吞异常,解析失败即请求失败(ADR-0006)
- 查询链 locality:resolve 与 visibleMenuTree 共用 userRolesOf + authorizedMenus
- JwtAuthenticationFilter / SystemController 切换到新接口
- DataScopeIntegrationTest 迁移到 resolve(),新增 6 个权限码并集场景
1 month ago
luoweijian
ae038a1398
feat: /api/resources/list 后端组树返回 — TreeUtils.buildTree + sort
- ResourceNode 新增 children 字段
- listAll() 改用 TreeUtils.buildTree 组树,按 sort 排序
- 前端无需自行组树,直接渲染 data
- 测试适配树结构,前端文档同步更新
1 month ago
luoweijian
170c16b226
refactor: ResourceController 对齐全局接口契约 — POST + 动作后缀 + 表单参数
- GET /api/resources → GET /api/resources/list
- POST /api/resources + @RequestBody → POST /api/resources/saveOrUpdate + 表单字段
- DELETE /api/resources/{id} → POST /api/resources/delete + @RequestParam
- 前端对接文档同步更新
1 month ago
luoweijian
4868051399
fix: validateHierarchy 增加 parentType null 守卫,防止 switch NPE
Spec review 发现:MenuType.fromCode() 对未知 DB 值返回 null 时,switch 表达式会抛 NPE 500,
改为提前抛 BusinessErrorException 并附带 menuType 值,便于排查脏数据。
1 month ago
luoweijian
a70ce78ddd
fix: 代码审查修复 — 安全问题 + 规范 + 空路由
- Spec#1 CRITICAL: 移除 /api/resources/** 白名单,改为 @PreAuthorize(hasRole('ADMIN'))
- Spec#2 WARNING: applyToEntity 空串路由不覆盖 DB path (null→isNotBlank)
- Standards#1: validateHierarchy 复用 MenuType.isLegalChild,消除重复代码
- Standards#2: 消除魔法数字 3→MenuType.BUTTON.getCode(),消除重复 switch
1 month ago
luoweijian
53d82c960b
feat(crm-auth): Ticket 05 — 权限码并集注入(按钮级鉴权)
- initDataScopeContext 在数据范围计算后追加 perms 并集
- 查 sys_user_role → sys_role_menu → sys_menu 收集 enabled button 的 perms
- 停用(status=disabled)的权限点不进并集,重新启用自动恢复
- perms 与 roleCode 同为 Spring Security authority,hasAuthority/hasRole 各自匹配
- try-catch 保护:sys_menu/sys_role_menu 表未创建时静默跳过
- 114 tests 全绿,零回归
1 month ago
luoweijian
8c4ebb4827
feat(crm-auth): Ticket 04 — 图标上传
- POST /api/resources/icon/upload(multipart/file)
- 格式校验:只允许 png/jpg/jpeg/gif/svg/webp
- 大小校验:≤500KB
- 调 crm-file FileApi 上传到 MinIO(bizDomain=resources/icon)
- 返回 FileInfoDTO(含 fileId)
- crm-auth pom.xml 加 crm-file 依赖
- TDD: 36 个单测,114 tests 全绿
1 month ago
luoweijian
02cc9356c3
feat(crm-auth): Ticket 03 — 删除节点(含子节点检查 + 级联清理)
- DELETE /api/resources/{id} 删除资源节点
- 有子节点时拒绝并返回子节点数
- 无子节点时删除 + 级联清理 sys_role_menu 授权引用
- TDD: 31 个单测,109 tests 全绿
1 month ago
luoweijian
b54ff47350
feat(crm-auth): Ticket 02 — 全量资源树查询 + 新增/编辑节点(含层级约束)
- GET /api/resources 全量扁平数组(含 catalog/menu/button)
- POST /api/resources JSON body 新增/编辑 + 6 种层级约束校验
- 字段必填:menu 缺 route 拒绝、button 缺 perms/denyBehavior/apiUrl/status 拒绝
- 编辑锁 type(不可变)+ 编辑时 route 未传保留原值
- SecurityConfig DEFAULT_IGNORE_URLS 放行 /api/resources/**
- TDD: 27 个单测覆盖正反例,105 tests 全绿
1 month ago
luoweijian
aacc3d4c04
feat(crm-auth): Ticket 01 — 数据模型扩展(ADR-0011 统一权限资源树)
- 新增 MenuType 枚举(CATALOG/MENU/BUTTON,含 fromCode 和 isLegalChild)
- SysMenu 实体加 5 字段:description/perms/denyBehavior/status/apiUrl
- 新增 ResourceNode DTO(含 fromEntity/toEntity 互转,映射 menuName/name、path/route)
- 95 tests 全绿,零回归
1 month ago
luoweijian
cceb8e5481
commit
1 month ago
luoweijian
13e15c142a
fix: 组织准入校验移出事务 + 细分 getbyunionid 错误码
- 组织校验 HTTP 调用从 DB 事务内移到 login() 事务外预校验,
避免首登洪峰长时间占用事务连接(code review M1)
- parseOrgMember 只将 60121(未找到对应员工)判为非成员,
其他 errcode 视为调用失败抛异常,不误判拒绝(code review m1)
- 企业级 token 失效(40014/42001)时清缓存重取重试一次
1 month ago
luoweijian
73304b7734
commit
1 month ago
luoweijian
6329a6ba74
first-commit
1 month ago