优化
This commit is contained in:
@@ -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=? " +
|
||||
|
||||
+3
-2
@@ -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) {
|
||||
|
||||
+15
-2
@@ -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;
|
||||
|
||||
+28
@@ -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
|
||||
);
|
||||
Reference in New Issue
Block a user