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