This commit is contained in:
2026-01-14 15:42:26 +08:00
parent 0bf7361672
commit 87f2772964
14 changed files with 5864 additions and 0 deletions

View File

@@ -0,0 +1,391 @@
# 权限注解转换指南
## 概述
本指南详细说明了如何将 urbanLifelineServ 的权限注解迁移到 pigx 平台的权限体系。
## 核心变更
### 1. 权限注解格式
| 特性 | urbanLifelineServ | pigx |
|------|------------------|------|
| 注解类 | @PreAuthorize | @PreAuthorize |
| 权限判断方法 | hasAuthority() | @pms.hasPermission() |
| 权限标识格式 | module:resource:action | module_resource_action |
| 分隔符 | 冒号 `:` | 下划线 `_` |
### 2. 用户信息获取
| 功能 | urbanLifelineServ | pigx |
|------|------------------|------|
| 获取用户ID | JwtUtils.getUserId() | SecurityUtils.getUser().getId() |
| 获取用户名 | JwtUtils.getUsername() | SecurityUtils.getUser().getUsername() |
| 获取租户ID | 不支持 | SecurityUtils.getUser().getTenantId() |
| 获取部门ID | 不支持 | SecurityUtils.getUser().getDeptId() |
### 3. 响应格式
| 特性 | urbanLifelineServ | pigx |
|------|------------------|------|
| 响应类 | ResultDomain<T> | R<T> |
| 成功响应 | ResultDomain.success(data) | R.ok(data) |
| 失败响应 | ResultDomain.fail(msg) | R.failed(msg) |
| 列表字段 | dataList | data |
## 转换步骤
### 步骤1权限注解转换
#### 1.1 基本转换
```java
// 转换前
@PreAuthorize("hasAuthority('workcase:ticket:create')")
public ResultDomain<TbWorkcaseDTO> createWorkcase(@RequestBody TbWorkcaseDTO workcase) {
return workcaseService.createWorkcase(workcase);
}
// 转换后
@PreAuthorize("@pms.hasPermission('workcase_ticket_add')")
public R<TbWorkcaseDTO> createWorkcase(@RequestBody TbWorkcaseDTO workcase) {
return R.ok(workcaseService.createWorkcase(workcase));
}
```
#### 1.2 多权限判断
```java
// 转换前
@PreAuthorize("hasAuthority('workcase:ticket:view') or hasAuthority('workcase:ticket:admin')")
public ResultDomain<List<TbWorkcaseDTO>> listWorkcase() {
return ResultDomain.success(workcaseService.list());
}
// 转换后
@PreAuthorize("@pms.hasPermission('workcase_ticket_view') or @pms.hasPermission('workcase_ticket_admin')")
public R<List<TbWorkcaseDTO>> listWorkcase() {
return R.ok(workcaseService.list());
}
```
### 步骤2用户信息获取转换
#### 2.1 Service层用户信息获取
```java
// 转换前
@Service
public class WorkcaseServiceImpl implements WorkcaseService {
public TbWorkcaseDTO createWorkcase(TbWorkcaseDTO workcase) {
Long userId = JwtUtils.getUserId();
String username = JwtUtils.getUsername();
workcase.setCreateBy(userId);
workcase.setCreateByName(username);
workcase.setCreateTime(new Date());
return workcaseMapper.insert(workcase);
}
}
// 转换后
@Service
public class WorkcaseServiceImpl implements WorkcaseService {
public TbWorkcaseDTO createWorkcase(TbWorkcaseDTO workcase) {
PigxUser user = SecurityUtils.getUser();
workcase.setCreateBy(user.getId());
workcase.setCreateByName(user.getUsername());
workcase.setTenantId(user.getTenantId()); // 新增:租户隔离
workcase.setDeptId(user.getDeptId()); // 新增:部门信息
workcase.setCreateTime(LocalDateTime.now());
return workcaseMapper.insert(workcase);
}
}
```
#### 2.2 远程用户服务调用
```java
// 转换前
@Service
public class WorkcaseServiceImpl {
@Autowired
private UserService userService;
public void assignWorkcase(String workcaseId, Long assigneeId) {
User assignee = userService.getById(assigneeId);
if (assignee == null) {
throw new BusinessException("用户不存在");
}
// 处理逻辑...
}
}
// 转换后
@Service
public class WorkcaseServiceImpl {
@Autowired
private RemoteUserService remoteUserService;
public void assignWorkcase(String workcaseId, Long assigneeId) {
R<SysUser> result = remoteUserService.selectById(assigneeId);
if (!result.isSuccess() || result.getData() == null) {
throw new BusinessException("用户不存在");
}
SysUser assignee = result.getData();
// 处理逻辑...
}
}
```
### 步骤3响应格式转换
#### 3.1 Controller响应转换
```java
// 转换前
@RestController
@RequestMapping("/api/workcase")
public class WorkcaseController {
// 单个对象返回
@GetMapping("/{id}")
public ResultDomain<TbWorkcaseDTO> getById(@PathVariable String id) {
TbWorkcaseDTO workcase = workcaseService.getById(id);
if (workcase == null) {
return ResultDomain.fail("工单不存在");
}
return ResultDomain.success(workcase);
}
// 列表返回
@GetMapping("/list")
public ResultDomain<List<TbWorkcaseDTO>> list() {
List<TbWorkcaseDTO> list = workcaseService.list();
ResultDomain<List<TbWorkcaseDTO>> result = ResultDomain.success();
result.setDataList(list); // 注意使用dataList字段
return result;
}
}
// 转换后
@RestController
@RequestMapping("/workcase")
public class WorkcaseController {
// 单个对象返回
@GetMapping("/{id}")
public R<TbWorkcaseDTO> getById(@PathVariable String id) {
TbWorkcaseDTO workcase = workcaseService.getById(id);
if (workcase == null) {
return R.failed("工单不存在");
}
return R.ok(workcase);
}
// 列表返回
@GetMapping("/list")
public R<List<TbWorkcaseDTO>> list() {
List<TbWorkcaseDTO> list = workcaseService.list();
return R.ok(list); // 直接返回列表不使用dataList
}
// 分页返回
@GetMapping("/page")
public R<IPage<TbWorkcaseDTO>> page(Page page) {
return R.ok(workcaseService.page(page));
}
}
```
### 步骤4数据权限适配
#### 4.1 添加租户隔离
```java
// 转换前 - 无租户隔离
@Service
public class WorkcaseServiceImpl {
public List<TbWorkcaseDTO> listMyWorkcase() {
Long userId = JwtUtils.getUserId();
return workcaseMapper.selectByUserId(userId);
}
}
// 转换后 - 支持租户隔离
@Service
public class WorkcaseServiceImpl {
public List<TbWorkcaseDTO> listMyWorkcase() {
PigxUser user = SecurityUtils.getUser();
QueryWrapper<TbWorkcaseDTO> wrapper = new QueryWrapper<>();
wrapper.eq("tenant_id", user.getTenantId()) // 租户隔离
.eq("create_by", user.getId());
return workcaseMapper.selectList(wrapper);
}
}
```
## 批量转换工具
### 使用IDE批量替换
#### IntelliJ IDEA 正则替换
1. **查找模式** (启用正则表达式):
```regex
@PreAuthorize\("hasAuthority\('([^:]+):([^:]+):([^']+)'\)"\)
```
2. **替换为**:
```regex
@PreAuthorize("@pms.hasPermission('$1_$2_$3')")
```
3. **动作映射** (执行第二次替换):
- 查找: `_create'` 替换为: `_add'`
- 查找: `_update'` 替换为: `_edit'`
- 查找: `_delete'` 替换为: `_del'`
### 命令行批量转换脚本
```bash
#!/bin/bash
# convert-permissions.sh
# 查找所有Java文件并转换权限注解
find . -name "*.java" -type f -exec sed -i.bak \
-e "s/@PreAuthorize(\"hasAuthority('\([^:]*\):\([^:]*\):\([^']*\)')\")/@PreAuthorize(\"@pms.hasPermission('\1_\2_\3')\")/g" \
-e "s/_create')/_add')/g" \
-e "s/_update')/_edit')/g" \
-e "s/_delete')/_del')/g" {} \;
# 转换JwtUtils为SecurityUtils
find . -name "*.java" -type f -exec sed -i.bak \
-e "s/JwtUtils.getUserId()/SecurityUtils.getUser().getId()/g" \
-e "s/JwtUtils.getUsername()/SecurityUtils.getUser().getUsername()/g" {} \;
# 转换ResultDomain为R
find . -name "*.java" -type f -exec sed -i.bak \
-e "s/ResultDomain.success(/R.ok(/g" \
-e "s/ResultDomain.fail(/R.failed(/g" \
-e "s/ResultDomain</R</g" {} \;
```
## 需要手工处理的情况
### 1. 复杂的权限逻辑
```java
// 需要手工审查的复杂权限
@PreAuthorize("hasAuthority('workcase:ticket:view') and #workcase.createBy == authentication.principal.userId")
public ResultDomain<TbWorkcaseDTO> getMyWorkcase(@PathVariable String id, TbWorkcaseDTO workcase) {
// 这种情况需要根据pigx的数据权限机制重新设计
}
```
### 2. 自定义权限判断
```java
// 转换前
if (SecurityContextHolder.getContext().getAuthentication().getAuthorities()
.contains(new SimpleGrantedAuthority("workcase:ticket:admin"))) {
// 管理员逻辑
}
// 转换后
if (SecurityUtils.getUser().getAuthorities()
.contains("workcase_ticket_admin")) {
// 管理员逻辑
}
```
### 3. 异步任务中的用户信息
```java
// 转换前
@Async
public void processAsync() {
Long userId = JwtUtils.getUserId(); // 异步线程中可能获取不到
}
// 转换后
@Async
public void processAsync() {
// 需要在调用异步方法前获取用户信息并传递
PigxUser user = SecurityUtils.getUser();
processAsyncWithUser(user);
}
```
## 测试验证清单
### 权限测试
- [ ] 所有 @PreAuthorize 注解已转换为 @pms.hasPermission 格式
- [ ] 权限标识符已从冒号改为下划线
- [ ] 动作已正确映射 (create→add, update→edit, delete→del)
- [ ] 多权限判断逻辑正确
### 用户信息测试
- [ ] SecurityUtils.getUser() 能正确获取用户信息
- [ ] 租户ID正确设置到业务数据
- [ ] RemoteUserService 调用正常
### 响应格式测试
- [ ] 所有接口返回 R<T> 格式
- [ ] 前端能正确解析新的响应格式
- [ ] 错误信息正确传递
### 数据权限测试
- [ ] 租户数据隔离正常
- [ ] 部门数据权限正常
- [ ] 个人数据权限正常
## 常见问题
### Q1: @pms.hasPermission 中的 @pms 是什么?
A: `@pms` 是 pigx 权限管理系统的 SpEL 表达式前缀,用于调用权限判断方法。这是 pigx 框架的固定写法,必须保留。
### Q2: 为什么要将 create 改为 add
A: 这是 pigx 平台的命名规范,保持统一的动作命名有助于权限管理的标准化。常见映射:
- create → add (新增)
- update → edit (编辑)
- delete → del (删除)
- view → view (查看)
### Q3: 如何处理没有对应 pigx 用户的情况?
A: 所有业务用户必须在 pigx 的 sys_user 表中存在。如果是数据迁移,需要先创建对应的 pigx 用户,或建立用户映射关系。
### Q4: 租户ID是必须的吗
A: 是的。pigx 是多租户系统,所有业务表都需要 tenant_id 字段。即使是单租户使用也需要设置默认租户ID通常为1
### Q5: 如何调试权限问题?
A: 可以通过以下方式调试:
1. 查看 pigx 日志中的权限判断记录
2. 使用 SecurityUtils.getUser().getAuthorities() 查看当前用户权限
3. 检查 sys_menu 表中的权限配置
4. 验证 sys_role_menu 表中的角色权限关联
## 相关文档
- [权限标识映射表](./permission-mapping.md)
- [数据库迁移指南](./database-migration.md)
- [前端适配指南](./frontend-migration.md)