This commit is contained in:
haomingming
2026-06-04 10:42:23 +08:00
parent db10401b13
commit 52fd2e7560
34 changed files with 845 additions and 162 deletions
@@ -524,7 +524,7 @@ public class AuditService {
if (task.getNode() == AuditNode.INIT_REVIEW) {
// 初审拒绝:整体审核结束,会议审核状态置为已拒绝
meetingService.updateAuditStatus(meetingId, MeetingAuditStatus.REJECTED);
meetingService.updateAuditStatus(meetingId, MeetingAuditStatus.REJECTED, request.getOpinion());
triggerAuditNotification(task, request.getOpinion(), "AUDIT_REJECTED");
} else {
// 复审 / 终审拒绝:整体回到初审阶段,重新创建初审任务
@@ -539,7 +539,7 @@ public class AuditService {
assigneeUserId = auditFlowConfigService.resolveAssigneeUserId(tenantId, firstNode);
}
meetingService.updateAuditStatus(meetingId, MeetingAuditStatus.IN_REVIEW);
meetingService.updateAuditStatus(meetingId, MeetingAuditStatus.IN_REVIEW, request.getOpinion());
meetingService.updateCurrentAuditNode(meetingId, firstNode.name(), assigneeUserId);
AuditTask resetTask = new AuditTask(
null,
@@ -566,7 +566,7 @@ public class AuditService {
task.setStatus(AuditTaskStatus.REJECTED);
task.setOpinion("退回修改:" + request.getOpinion());
auditTaskRepository.save(task);
meetingService.updateAuditStatus(task.getMeetingId(), MeetingAuditStatus.PENDING);
meetingService.updateAuditStatus(task.getMeetingId(), MeetingAuditStatus.PENDING, "退回修改:" + request.getOpinion());
logAuditTaskAction(task, "AUDIT_TASK_RETURNED", request.getOpinion());
triggerAuditNotification(task, request.getOpinion(), "AUDIT_RETURNED");
Map<String, Object> result = new LinkedHashMap<>();
@@ -120,7 +120,7 @@ public class AuthController {
.body(ApiErrorResponse.of(11005, "账号已被锁定,请" + (remaining / 60 + 1) + "分钟后重试", errors));
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
"SELECT u.id, u.tenant_id, u.user_name, u.phone, u.status, u.password_hash, u.valid_from, u.valid_to, " +
"SELECT u.id, u.tenant_id, u.user_name, u.phone, u.email, u.status, u.password_hash, u.valid_from, u.valid_to, " +
"t.tenant_code, t.tenant_name, t.status AS tenant_status " +
"FROM sys_user u JOIN tenant t ON u.tenant_id=t.id " +
"WHERE u.phone=? AND t.tenant_code=? AND u.is_deleted=0 AND t.is_deleted=0 LIMIT 1",
@@ -169,7 +169,7 @@ public class AuthController {
String refreshToken = String.valueOf(issueResult.get("refreshToken"));
String token = jwtTokenService.createTenantToken(userId, tenantId, request.getPhone(), sessionId);
setRefreshCookie(httpResponse, refreshToken, false);
Map<String, Object> data = buildTenantAuthData(userId, tenantId, String.valueOf(row.get("user_name")), request.getPhone(), token);
Map<String, Object> data = buildTenantAuthData(userId, tenantId, String.valueOf(row.get("user_name")), request.getPhone(), String.valueOf(row.get("email")), token);
return ResponseEntity.ok(ApiResponse.success(data));
}
@@ -194,7 +194,7 @@ public class AuthController {
.body(ApiErrorResponse.of(11005, "账号已被锁定,请" + (remaining / 60 + 1) + "分钟后重试", errors));
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
"SELECT id, user_name, phone, status, password_hash, valid_from, valid_to " +
"SELECT id, user_name, phone, email, status, password_hash, valid_from, valid_to " +
"FROM platform_user WHERE phone=? AND is_deleted=0 LIMIT 1",
request.getPhone()
);
@@ -235,7 +235,7 @@ public class AuthController {
String refreshToken = String.valueOf(issueResult.get("refreshToken"));
String token = jwtTokenService.createPlatformToken(userId, request.getPhone(), sessionId);
setRefreshCookie(httpResponse, refreshToken, false);
Map<String, Object> data = buildPlatformAuthData(userId, String.valueOf(row.get("user_name")), request.getPhone(), token);
Map<String, Object> data = buildPlatformAuthData(userId, String.valueOf(row.get("user_name")), request.getPhone(), String.valueOf(row.get("email")), token);
return ResponseEntity.ok(ApiResponse.success(data));
}
@@ -292,36 +292,29 @@ public class AuthController {
Map<String, Object> data;
if (scope == AuthScope.TENANT) {
validateTenantSession(userId, tenantId);
String phone = jdbcTemplate.queryForObject(
"SELECT phone FROM sys_user WHERE id=? AND tenant_id=? AND is_deleted=0 LIMIT 1",
String.class,
userId,
tenantId
);
String userName = jdbcTemplate.queryForObject(
"SELECT user_name FROM sys_user WHERE id=? AND tenant_id=? AND is_deleted=0 LIMIT 1",
String.class,
Map<String, Object> uRow = jdbcTemplate.queryForMap(
"SELECT phone, user_name, email FROM sys_user WHERE id=? AND tenant_id=? AND is_deleted=0 LIMIT 1",
userId,
tenantId
);
String phone = String.valueOf(uRow.get("phone"));
String userName = String.valueOf(uRow.get("user_name"));
String email = String.valueOf(uRow.get("email"));
Long sessionId = rotateResult.get("sessionId") == null ? null : ((Number) rotateResult.get("sessionId")).longValue();
String token = jwtTokenService.createTenantToken(userId, tenantId, phone, sessionId);
data = buildTenantAuthData(userId, tenantId, userName, phone, token);
data = buildTenantAuthData(userId, tenantId, userName, phone, email, token);
} else {
validatePlatformSession(userId);
String phone = jdbcTemplate.queryForObject(
"SELECT phone FROM platform_user WHERE id=? AND is_deleted=0 LIMIT 1",
String.class,
userId
);
String userName = jdbcTemplate.queryForObject(
"SELECT user_name FROM platform_user WHERE id=? AND is_deleted=0 LIMIT 1",
String.class,
Map<String, Object> puRow = jdbcTemplate.queryForMap(
"SELECT phone, user_name, email FROM platform_user WHERE id=? AND is_deleted=0 LIMIT 1",
userId
);
String phone = String.valueOf(puRow.get("phone"));
String userName = String.valueOf(puRow.get("user_name"));
String email = String.valueOf(puRow.get("email"));
Long sessionId = rotateResult.get("sessionId") == null ? null : ((Number) rotateResult.get("sessionId")).longValue();
String token = jwtTokenService.createPlatformToken(userId, phone, sessionId);
data = buildPlatformAuthData(userId, userName, phone, token);
data = buildPlatformAuthData(userId, userName, phone, email, token);
}
setRefreshCookie(response, nextRefreshToken, false);
return ApiResponse.success(data);
@@ -377,7 +370,7 @@ public class AuthController {
String phone = String.valueOf(targetIdentity.get("phone"));
String token = jwtTokenService.createTenantToken(targetUserId, targetTenantId, phone, sessionId);
setRefreshCookie(httpResponse, refreshToken, false);
return ApiResponse.success(buildTenantAuthData(targetUserId, targetTenantId, String.valueOf(targetIdentity.get("user_name")), phone, token));
return ApiResponse.success(buildTenantAuthData(targetUserId, targetTenantId, String.valueOf(targetIdentity.get("user_name")), phone, String.valueOf(targetIdentity.get("email")), token));
}
@PostMapping("/logout")
@@ -462,7 +455,7 @@ public class AuthController {
return dt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
private Map<String, Object> buildTenantAuthData(Long userId, Long tenantId, String userName, String phone, String token) {
private Map<String, Object> buildTenantAuthData(Long userId, Long tenantId, String userName, String phone, String email, String token) {
Map<String, Object> tenant = loadTenantInfo(tenantId);
Map<String, Object> data = new LinkedHashMap<String, Object>();
data.put("token", token);
@@ -476,11 +469,12 @@ public class AuthController {
data.put("roles", systemUserService.getUserRoles(userId, tenantId));
data.put("permissions", permissionService.getPermissions(userId, tenantId));
data.put("phone", phone);
data.put("email", email);
data.put("appearance", loadTenantPreferences(userId, tenantId));
return data;
}
private Map<String, Object> buildPlatformAuthData(Long userId, String userName, String phone, String token) {
private Map<String, Object> buildPlatformAuthData(Long userId, String userName, String phone, String email, String token) {
Map<String, Object> data = new LinkedHashMap<String, Object>();
data.put("token", token);
data.put("scope", AuthScope.PLATFORM.name());
@@ -490,6 +484,7 @@ public class AuthController {
data.put("roles", permissionService.getPlatformRoles(userId));
data.put("permissions", permissionService.getPlatformPermissions(userId));
data.put("phone", phone);
data.put("email", email);
data.put("appearance", loadPlatformPreferences(userId));
return data;
}
@@ -678,7 +673,7 @@ public class AuthController {
String normalizedSwitchAccountKey = normalizeTenantSwitchAccountKey(switchAccountKey);
if (!normalizedSwitchAccountKey.isEmpty()) {
List<Map<String, Object>> accountKeyRows = jdbcTemplate.queryForList(
"SELECT u.id AS user_id, u.tenant_id, u.user_name, u.phone, u.password_hash, u.tenant_switch_account_key " +
"SELECT u.id AS user_id, u.tenant_id, u.user_name, u.phone, u.email, u.password_hash, u.tenant_switch_account_key " +
"FROM sys_user u " +
"JOIN tenant t ON u.tenant_id=t.id " +
"WHERE u.tenant_id=? AND u.tenant_switch_account_key=? " +
@@ -692,7 +687,7 @@ public class AuthController {
}
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
"SELECT u.id AS user_id, u.tenant_id, u.user_name, u.phone, u.password_hash, u.tenant_switch_account_key " +
"SELECT u.id AS user_id, u.tenant_id, u.user_name, u.phone, u.email, u.password_hash, u.tenant_switch_account_key " +
"FROM sys_user u " +
"JOIN tenant t ON u.tenant_id=t.id " +
"WHERE u.tenant_id=? AND u.phone=? AND u.password_hash=? " +
@@ -23,8 +23,9 @@ public class ExportTaskController {
@GetMapping
@RequirePermission(value = "export.task.read", dataScope = DataScopeType.TENANT, auditAction = "EXPORT_TASK_LIST")
public ApiResponse<PageResult<ExportTaskInfo>> list() {
return ApiResponse.success(exportTaskService.list());
public ApiResponse<PageResult<ExportTaskInfo>> list(@RequestParam(value = "pageNo", defaultValue = "1") int pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {
return ApiResponse.success(exportTaskService.list(pageNo, pageSize));
}
@PostMapping
@@ -64,16 +64,32 @@ public class ExportTaskService {
this.meetingSummaryExportService = meetingSummaryExportService;
}
public PageResult<ExportTaskInfo> list() {
List<ExportTaskInfo> list = jdbcTemplate.query(
"SELECT id, task_code, biz_type, biz_id, file_name, file_oss_key, status, retry_count, IFNULL(download_count,0) AS download_count, " +
"DATE_FORMAT(download_token_expire_at, '%Y-%m-%d %H:%i:%s') AS token_expire_at, error_message, " +
"DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') AS created_at, DATE_FORMAT(finished_at, '%Y-%m-%d %H:%i:%s') AS finished_at " +
"FROM export_task WHERE tenant_id=? AND is_deleted=0 ORDER BY id DESC LIMIT 300",
ROW_MAPPER,
public PageResult<ExportTaskInfo> list(int pageNo, int pageSize) {
int page = Math.max(1, pageNo);
int size = Math.max(1, Math.min(1000, pageSize));
int offset = (page - 1) * size;
Integer totalObj = jdbcTemplate.queryForObject(
"SELECT COUNT(1) FROM export_task WHERE tenant_id=? AND is_deleted=0",
Integer.class,
tenantId()
);
return new PageResult<ExportTaskInfo>(list, list.size(), 1, 300);
int totalElements = totalObj == null ? 0 : totalObj;
List<ExportTaskInfo> list = new ArrayList<>();
if (totalElements > 0) {
list = jdbcTemplate.query(
"SELECT id, task_code, biz_type, biz_id, file_name, file_oss_key, status, retry_count, IFNULL(download_count,0) AS download_count, " +
"DATE_FORMAT(download_token_expire_at, '%Y-%m-%d %H:%i:%s') AS token_expire_at, error_message, " +
"DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') AS created_at, DATE_FORMAT(finished_at, '%Y-%m-%d %H:%i:%s') AS finished_at " +
"FROM export_task WHERE tenant_id=? AND is_deleted=0 ORDER BY id DESC LIMIT ? OFFSET ?",
ROW_MAPPER,
tenantId(),
size,
offset
);
}
return new PageResult<ExportTaskInfo>(list, totalElements, page, size);
}
@Transactional(rollbackFor = Exception.class)
@@ -77,6 +77,15 @@ public class MeetingController {
return ApiResponse.success(meetingService.list(query));
}
@GetMapping("/default-budget")
@RequirePermission(value = "meeting.create", dataScope = DataScopeType.TENANT, auditAction = "MEETING_DEFAULT_BUDGET")
public ApiResponse<Map<String, Long>> getDefaultBudget(@RequestParam("projectId") Long projectId) {
long defaultBudgetCent = meetingService.getDefaultMeetingBudgetCent(projectId);
java.util.Map<String, Long> result = new java.util.LinkedHashMap<>();
result.put("defaultMeetingBudgetCent", defaultBudgetCent);
return ApiResponse.success(result);
}
@GetMapping("/tenant-experts")
@RequirePermission(value = "meeting.material.read", dataScope = DataScopeType.TENANT, auditAction = "MEETING_TENANT_EXPERT_LIST")
public ApiResponse<PageResult<ExpertInfo>> tenantExperts(@RequestParam(value = "keyword", required = false) String keyword) {
@@ -915,7 +915,7 @@ public class MeetingMaterialService {
return true;
}
String status = stringValue(issue.get("status")).toUpperCase(Locale.ROOT);
return "CHANGED".equals(status) || "RESOLVED".equals(status);
return "CHANGED".equals(status) || "RESOLVED".equals(status) || "PENDING_CONFIRM".equals(status);
}
private String normalizeSupportedModuleCode(String moduleCode) {
@@ -2106,7 +2106,7 @@ public class MeetingMaterialService {
Map<String, Object> profileFile = asObjectMapOrEmpty(root.get("profileFile"));
String ossKey = stringValue(firstNonNull(profileFile.get("ossKey"), root.get("ossKey")));
if (ossKey != null && !ossKey.isEmpty()) {
addReviewItem(items, "expert_profile_file", "涓撳绠€浠?涓插満鏂囦欢");
addReviewItem(items, "expert_profile_file", "专家简介/串场文件");
}
} else if ("EXPERT_LIST".equals(moduleCode)) {
Map<String, Object> onsiteRoot = asObjectMapOrEmpty(root.get("onsitePhoto"));
@@ -2380,6 +2380,19 @@ public class MeetingMaterialService {
}
addAttachmentNodeFromSingleFile(index, "signInSheet", "签到表", root.get("signInSheet"));
addAttachmentNodeFromSingleFile(index, "themePhoto", "主题照片", root.get("themePhoto"));
Object videoObj = root.get("meetingVideo");
if (videoObj instanceof Map) {
Map<String, Object> videoMap = asObjectMapOrEmpty(videoObj);
String type = stringValue(videoMap.get("type"));
if ("link".equals(type)) {
String link = stringValue(videoMap.get("link"));
if (!link.isEmpty()) {
addFieldNode(index, "meetingVideo", "会议视频", "网盘链接:" + link);
}
} else {
addAttachmentNodeFromSingleFile(index, "meetingVideo", "会议视频", videoObj);
}
}
Object invitationObj = root.get("invitation");
if (invitationObj instanceof Collection) {
int idx = 1;
@@ -315,7 +315,15 @@ public class MeetingService {
}
}
private long calculateDefaultMeetingBudgetCent(Project project) {
public long getDefaultMeetingBudgetCent(Long projectId) {
Project project = projectService.getById(projectId);
if (project == null) {
throw new BusinessException(ErrorCodes.RESOURCE_NOT_FOUND, "项目不存在");
}
return calculateDefaultMeetingBudgetCent(project);
}
public long calculateDefaultMeetingBudgetCent(Project project) {
long projectBudgetCent = Math.max(0L, project.getBudgetCent());
ProjectFeeSummary feeSummary = parseProjectFeeSummary(project.getProjectFeeJson());
long distributableBudgetCent = projectBudgetCent - feeSummary.managementFeeCent - feeSummary.taxFeeCent - feeSummary.customFeeTotalCent;
@@ -737,8 +745,18 @@ public class MeetingService {
}
public void updateAuditStatus(Long meetingId, MeetingAuditStatus status) {
updateAuditStatus(meetingId, status, null);
}
public void updateAuditStatus(Long meetingId, MeetingAuditStatus status, String rejectReason) {
Meeting meeting = getById(meetingId);
meeting.setAuditStatus(status);
if (rejectReason != null && !rejectReason.isEmpty()) {
meeting.setLastRejectReason(rejectReason);
meeting.setRejectCount(meeting.getRejectCount() + 1);
} else if (status == MeetingAuditStatus.APPROVED) {
meeting.setLastRejectReason("");
}
meetingRepository.save(meeting);
}
@@ -30,8 +30,11 @@ public class ProjectController {
@GetMapping
public ApiResponse<PageResult<Project>> list(@RequestParam(value = "parentOnly", required = false) Boolean parentOnly,
@RequestParam(value = "includeDeleted", required = false) Boolean includeDeleted) {
return ApiResponse.success(projectService.list(parentOnly, includeDeleted));
@RequestParam(value = "includeDeleted", required = false) Boolean includeDeleted,
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "pageNo", required = false) Integer pageNo,
@RequestParam(value = "pageSize", required = false) Integer pageSize) {
return ApiResponse.success(projectService.list(parentOnly, includeDeleted, keyword, pageNo, pageSize));
}
@GetMapping("/{id}/children")
@@ -264,6 +264,10 @@ public class Project {
return budgetCent;
}
public void setBudgetCent(long budgetCent) {
this.budgetCent = budgetCent;
}
public int getMeetingTotal() {
return meetingTotal;
}
@@ -64,13 +64,18 @@ public class ProjectService {
this(projectRepository, null, null, null, null, null);
}
public PageResult<Project> list(Boolean parentOnly, Boolean includeDeleted) {
public PageResult<Project> list(Boolean parentOnly, Boolean includeDeleted, String keyword, Integer pageNo, Integer pageSize) {
List<Project> list = projectRepository.findAll(Boolean.TRUE.equals(includeDeleted));
if (Boolean.TRUE.equals(parentOnly)) {
list = list.stream()
.filter(project -> project.getParentProjectId() == null)
.collect(Collectors.toList());
}
if (keyword != null && !keyword.trim().isEmpty()) {
list = list.stream()
.filter(project -> project.getName() != null && project.getName().contains(keyword.trim()))
.collect(Collectors.toList());
}
if (dataPermissionService != null) {
DataPermissionService.DataScope scope = dataPermissionService.resolveCurrentUserScope();
final Map<Long, Long> creatorMap = scope.isProjectOwnerOnly()
@@ -80,7 +85,14 @@ public class ProjectService {
.filter(project -> dataPermissionService.canAccessProject(project.getId(), creatorMap.get(project.getId()), scope))
.collect(Collectors.toList());
}
return new PageResult<>(list, list.size(), 1, 20);
int page = pageNo == null || pageNo < 1 ? 1 : pageNo;
int size = pageSize == null || pageSize < 1 ? 20 : pageSize;
int total = list.size();
int fromIndex = (page - 1) * size;
int toIndex = Math.min(fromIndex + size, total);
List<Project> pagedList = fromIndex < total ? list.subList(fromIndex, toIndex) : new ArrayList<>();
redactSensitiveData(pagedList);
return new PageResult<>(pagedList, total, page, size);
}
public List<Project> listChildren(Long parentProjectId) {
@@ -95,6 +107,7 @@ public class ProjectService {
.filter(project -> dataPermissionService.canAccessProject(project.getId(), creatorMap.get(project.getId()), scope))
.collect(Collectors.toList());
}
redactSensitiveData(children);
return children;
}
@@ -110,6 +123,7 @@ public class ProjectService {
.filter(project -> dataPermissionService.canAccessProject(project.getId(), creatorMap.get(project.getId()), scope))
.collect(Collectors.toList());
}
redactSensitiveData(children);
return children;
}
@@ -134,7 +148,7 @@ public class ProjectService {
double budgetExecutionRatio = calculateBudgetExecutionRatio(request.getBudgetCent(), projectFee.totalCent);
assertProjectBudgetConstraint(
request.getAllowProjectOverBudget() != null && request.getAllowProjectOverBudget(),
request.getOverBudgetThresholdRatio() == null ? 0.1d : request.getOverBudgetThresholdRatio(),
request.getOverBudgetThresholdRatio() == null ? 0.0d : request.getOverBudgetThresholdRatio(),
budgetExecutionRatio,
request.getBudgetCent(),
projectFee.totalCent
@@ -157,7 +171,7 @@ public class ProjectService {
request.getMeetingTotal(),
0,
request.getAllowMeetingOverBudget() != null && request.getAllowMeetingOverBudget(),
request.getOverBudgetThresholdRatio() == null ? 0.1d : request.getOverBudgetThresholdRatio(),
request.getOverBudgetThresholdRatio() == null ? 0.0d : request.getOverBudgetThresholdRatio(),
request.getOverBudgetApprovalChainJson(),
budgetExecutionRatio,
null,
@@ -215,7 +229,7 @@ public class ProjectService {
assertCurrentBudgetCanCoverChildren(projectId, request.getBudgetCent());
assertProjectBudgetConstraint(
request.getAllowProjectOverBudget() != null && request.getAllowProjectOverBudget(),
request.getOverBudgetThresholdRatio() == null ? 0.1d : request.getOverBudgetThresholdRatio(),
request.getOverBudgetThresholdRatio() == null ? 0.0d : request.getOverBudgetThresholdRatio(),
budgetExecutionRatio,
request.getBudgetCent(),
projectFee.totalCent
@@ -238,7 +252,7 @@ public class ProjectService {
request.getMeetingTotal(),
existing.getMeetingCompletedCount(),
request.getAllowMeetingOverBudget() != null && request.getAllowMeetingOverBudget(),
request.getOverBudgetThresholdRatio() == null ? 0.1d : request.getOverBudgetThresholdRatio(),
request.getOverBudgetThresholdRatio() == null ? 0.0d : request.getOverBudgetThresholdRatio(),
request.getOverBudgetApprovalChainJson(),
budgetExecutionRatio,
existing.getRiskFlagsJson(),
@@ -607,7 +621,7 @@ public class ProjectService {
try {
return new ProjectFeeSummary(
objectMapper.writeValueAsString(normalizedRoot),
managementFeeCent + taxFeeCent + paidAmountCent + customTotalCent
managementFeeCent + taxFeeCent + customTotalCent
);
} catch (Exception e) {
throw new BusinessException(10001, "项目费用配置序列化失败");
@@ -704,10 +718,10 @@ public class ProjectService {
double budgetExecutionRatio,
Long budgetCent,
long feeTotalCent) {
if (allowProjectOverBudget) {
return;
}
double allowedRatio = 1d + Math.max(0d, thresholdRatio);
// 如果关闭了“允许超支”开关,强制阈值为 0
double actualThreshold = allowProjectOverBudget ? Math.max(0d, thresholdRatio) : 0d;
double allowedRatio = 1d + actualThreshold;
if (budgetExecutionRatio > allowedRatio) {
long budget = budgetCent == null ? 0L : budgetCent;
long allowedTotalCent = Math.round(budget * allowedRatio);
@@ -893,6 +907,19 @@ public class ProjectService {
}
return "项目变更";
}
private void redactSensitiveData(List<Project> list) {
if (list == null || list.isEmpty()) {
return;
}
Long userId = AuthContext.userId();
boolean canReadSensitive = permissionService != null && userId != null && permissionService.hasPermission(userId, "project.sensitive_data.read");
if (!canReadSensitive) {
for (Project project : list) {
project.setBudgetCent(-1L);
project.setProjectFeeJson("{}");
}
}
}
private static class ProjectFeeSummary {
private final String normalizedJson;
@@ -0,0 +1,28 @@
SET @next_permission_id := (SELECT IFNULL(MAX(id), 0) + 1 FROM permission);
INSERT INTO permission (id, permission_code, permission_name, module)
SELECT @next_permission_id, 'project.sensitive_data.read', '查看项目敏感数据', 'project'
FROM dual
WHERE NOT EXISTS (
SELECT 1
FROM permission
WHERE permission_code = 'project.sensitive_data.read'
);
SET @next_role_permission_id := (SELECT IFNULL(MAX(id), 0) FROM role_permission);
INSERT INTO role_permission (id, tenant_id, role_id, permission_id)
SELECT
(@next_role_permission_id := @next_role_permission_id + 1) AS id,
r.tenant_id,
r.id AS role_id,
p.id AS permission_id
FROM role r
JOIN permission p ON p.permission_code = 'project.sensitive_data.read'
WHERE r.role_code IN ('TENANT_ADMIN', 'PROJECT_OWNER', 'FINANCE')
AND r.is_deleted = 0
AND NOT EXISTS (
SELECT 1
FROM role_permission rp
WHERE rp.tenant_id = r.tenant_id
AND rp.role_id = r.id
AND rp.permission_id = p.id
);