feat(openapi): 扩展数据分析多维度查询接口
This commit is contained in:
parent
88803d6211
commit
4697dec964
@ -21,6 +21,9 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -212,6 +215,644 @@ public class OpenAnalyticsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询需求统计", description = "供外部机器人获取需求统计维度数据")
|
||||||
|
@PostMapping("/demand-statistics")
|
||||||
|
public Result<Map<String, Object>> getDemandStatistics(
|
||||||
|
@RequestBody OpenDemandStatisticsRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放需求统计接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放需求统计接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String dateRange = normalizeDateRange(requestBody.getDateRange());
|
||||||
|
String month = sanitizeNullableText(requestBody.getMonth());
|
||||||
|
String deptId = requestBody.getDeptId() == null ? "" : String.valueOf(requestBody.getDeptId());
|
||||||
|
String userId = requestBody.getUserId() == null ? "" : String.valueOf(requestBody.getUserId());
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_demand_statistics",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
dateRange,
|
||||||
|
month,
|
||||||
|
deptId,
|
||||||
|
userId,
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放需求统计接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of(
|
||||||
|
"dateRange", dateRange,
|
||||||
|
"month", month,
|
||||||
|
"deptId", deptId,
|
||||||
|
"userId", userId
|
||||||
|
));
|
||||||
|
result.put("data", analyticsService.getDemandStatistics(
|
||||||
|
null, dateRange, requestBody.getDeptId(), requestBody.getUserId(), month));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放需求统计接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放需求统计接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询设计师绩效", description = "供外部机器人获取设计师绩效维度数据")
|
||||||
|
@PostMapping("/designer-performance")
|
||||||
|
public Result<Map<String, Object>> getDesignerPerformance(
|
||||||
|
@RequestBody OpenDesignerPerformanceRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放设计师绩效接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放设计师绩效接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String month = sanitizeNullableText(requestBody.getMonth());
|
||||||
|
String deptId = requestBody.getDeptId() == null ? "" : String.valueOf(requestBody.getDeptId());
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_designer_performance",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
month,
|
||||||
|
deptId,
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放设计师绩效接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of(
|
||||||
|
"month", month,
|
||||||
|
"deptId", deptId
|
||||||
|
));
|
||||||
|
result.put("data", analyticsService.getDesignerPerformance(null, month, requestBody.getDeptId()));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放设计师绩效接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放设计师绩效接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询流程效率", description = "供外部机器人获取流程效率维度数据")
|
||||||
|
@PostMapping("/process-efficiency")
|
||||||
|
public Result<Map<String, Object>> getProcessEfficiency(
|
||||||
|
@RequestBody OpenProcessEfficiencyRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放流程效率接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放流程效率接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String month = sanitizeNullableText(requestBody.getMonth());
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_process_efficiency",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
month,
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放流程效率接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of("month", month));
|
||||||
|
result.put("data", analyticsService.getProcessEfficiency(null, month));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放流程效率接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放流程效率接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询预警任务", description = "供外部机器人获取预警任务列表")
|
||||||
|
@PostMapping("/warnings")
|
||||||
|
public Result<Map<String, Object>> getWarningTasks(
|
||||||
|
@RequestBody OpenWarningsRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放预警任务接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放预警任务接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_warnings",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放预警任务接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("data", analyticsService.getWarningTasks(null));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放预警任务接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放预警任务接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询多维度分析", description = "供外部机器人获取类目/品牌/部门/平台等维度统计")
|
||||||
|
@PostMapping("/dimension-analysis")
|
||||||
|
public Result<Map<String, Object>> getDimensionAnalysis(
|
||||||
|
@RequestBody OpenDimensionAnalysisRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放多维度分析接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放多维度分析接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String dateRange = normalizeDateRange(requestBody.getDateRange());
|
||||||
|
String month = sanitizeNullableText(requestBody.getMonth());
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_dimension_analysis",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
dateRange,
|
||||||
|
month,
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放多维度分析接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of("dateRange", dateRange, "month", month));
|
||||||
|
result.put("data", analyticsService.getDimensionAnalysis(dateRange, month));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放多维度分析接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放多维度分析接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询设计师扣分明细", description = "供外部机器人查询设计师扣分明细")
|
||||||
|
@PostMapping("/designer-deduction-details")
|
||||||
|
public Result<Map<String, Object>> getDesignerDeductionDetails(
|
||||||
|
@RequestBody OpenDesignerDeductionRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| requestBody.getUserId() == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放设计师扣分接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放设计师扣分接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String month = sanitizeNullableText(requestBody.getMonth());
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_designer_deduction_details",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
String.valueOf(requestBody.getUserId()),
|
||||||
|
month,
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放设计师扣分接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of("userId", requestBody.getUserId(), "month", month));
|
||||||
|
result.put("data", analyticsService.getDesignerDeductionDetails(requestBody.getUserId(), month));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放设计师扣分接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放设计师扣分接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询设计师积分明细", description = "供外部机器人查询设计师积分明细")
|
||||||
|
@PostMapping("/designer-score-details")
|
||||||
|
public Result<Map<String, Object>> getDesignerScoreDetails(
|
||||||
|
@RequestBody OpenDesignerScoreRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| requestBody.getUserId() == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放设计师积分接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放设计师积分接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String month = sanitizeNullableText(requestBody.getMonth());
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_designer_score_details",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
String.valueOf(requestBody.getUserId()),
|
||||||
|
month,
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放设计师积分接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of("userId", requestBody.getUserId(), "month", month));
|
||||||
|
result.put("data", analyticsService.getDesignerScoreDetails(requestBody.getUserId(), month));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放设计师积分接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放设计师积分接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询一级需求部门统计", description = "供外部机器人查询一级需求部门统计")
|
||||||
|
@PostMapping("/primary-dept/statistics")
|
||||||
|
public Result<Map<String, Object>> getPrimaryDeptStatistics(
|
||||||
|
@RequestBody OpenPrimaryDeptStatisticsRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getStartTime())
|
||||||
|
|| !StringUtils.hasText(requestBody.getEndTime())
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放一级需求统计接口appKey不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放一级需求统计接口IP不在白名单: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String deptId = requestBody.getDeptId() == null ? "" : String.valueOf(requestBody.getDeptId());
|
||||||
|
String payload = String.join("|",
|
||||||
|
"analytics_primary_dept_statistics",
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
requestBody.getStartTime().trim(),
|
||||||
|
requestBody.getEndTime().trim(),
|
||||||
|
deptId,
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放一级需求统计接口签名不匹配: traceId={}, ip={}", traceId, clientIp);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDateTime startTime = parseDateTime(requestBody.getStartTime());
|
||||||
|
LocalDateTime endTime = parseDateTime(requestBody.getEndTime());
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of(
|
||||||
|
"startTime", requestBody.getStartTime().trim(),
|
||||||
|
"endTime", requestBody.getEndTime().trim(),
|
||||||
|
"deptId", deptId
|
||||||
|
));
|
||||||
|
result.put("data", analyticsService.getPrimaryDeptStatistics(
|
||||||
|
requestBody.getDeptId(), startTime, endTime));
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放一级需求统计接口失败: traceId={}, ip={}, error={}", traceId, clientIp, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放一级需求统计接口系统异常: traceId={}, ip={}", traceId, clientIp, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询一级需求部门排名", description = "供外部机器人查询一级需求部门排名")
|
||||||
|
@PostMapping("/primary-dept/ranking")
|
||||||
|
public Result<Map<String, Object>> getPrimaryDeptRanking(
|
||||||
|
@RequestBody OpenPrimaryRankingRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
return handlePrimaryRangeQuery(requestBody, request, "analytics_primary_dept_ranking",
|
||||||
|
(startTime, endTime) -> analyticsService.getPrimaryDeptRanking(startTime, endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询一级需求流转效率", description = "供外部机器人查询一级需求流转效率")
|
||||||
|
@PostMapping("/primary-task/flow-efficiency")
|
||||||
|
public Result<Map<String, Object>> getPrimaryTaskFlowEfficiency(
|
||||||
|
@RequestBody OpenPrimaryRankingRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
return handlePrimaryRangeQuery(requestBody, request, "analytics_primary_task_flow_efficiency",
|
||||||
|
(startTime, endTime) -> analyticsService.getPrimaryTaskFlowEfficiency(startTime, endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询一级需求品牌分布", description = "供外部机器人查询一级需求品牌分布")
|
||||||
|
@PostMapping("/primary-task/brand-distribution")
|
||||||
|
public Result<Map<String, Object>> getPrimaryTaskBrandDistribution(
|
||||||
|
@RequestBody OpenPrimaryRankingRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
return handlePrimaryRangeQuery(requestBody, request, "analytics_primary_task_brand_distribution",
|
||||||
|
(startTime, endTime) -> analyticsService.getPrimaryTaskBrandDistribution(startTime, endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "开放接口查询一级需求用户排名", description = "供外部机器人查询一级需求用户排名")
|
||||||
|
@PostMapping("/primary-dept/user-ranking")
|
||||||
|
public Result<Map<String, Object>> getPrimaryDeptUserRanking(
|
||||||
|
@RequestBody OpenPrimaryRankingRequest requestBody,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
return handlePrimaryRangeQuery(requestBody, request, "analytics_primary_dept_user_ranking",
|
||||||
|
(startTime, endTime) -> analyticsService.getPrimaryDeptUserRanking(startTime, endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<Map<String, Object>> handlePrimaryRangeQuery(
|
||||||
|
OpenPrimaryRankingRequest requestBody,
|
||||||
|
HttpServletRequest request,
|
||||||
|
String action,
|
||||||
|
PrimaryRangeQueryExecutor queryExecutor
|
||||||
|
) {
|
||||||
|
String traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||||
|
String clientIp = resolveClientIp(request);
|
||||||
|
try {
|
||||||
|
if (!systemConfigService.getBooleanValue(KEY_ENABLED, false)) {
|
||||||
|
return Result.error("开放数据分析查询接口未启用");
|
||||||
|
}
|
||||||
|
if (requestBody == null
|
||||||
|
|| !StringUtils.hasText(requestBody.getStartTime())
|
||||||
|
|| !StringUtils.hasText(requestBody.getEndTime())
|
||||||
|
|| !StringUtils.hasText(requestBody.getAppKey())
|
||||||
|
|| !StringUtils.hasText(requestBody.getTimestamp())
|
||||||
|
|| !StringUtils.hasText(requestBody.getSign())) {
|
||||||
|
return Result.error("参数不完整");
|
||||||
|
}
|
||||||
|
OpenApiCredential credential = getCredential();
|
||||||
|
if (!StringUtils.hasText(credential.getAppKey()) || !StringUtils.hasText(credential.getAppSecret())) {
|
||||||
|
return Result.error("开放接口密钥未配置");
|
||||||
|
}
|
||||||
|
if (!credential.getAppKey().equals(requestBody.getAppKey().trim())) {
|
||||||
|
log.warn("开放一级需求范围接口appKey不匹配: traceId={}, ip={}, action={}", traceId, clientIp, action);
|
||||||
|
return Result.error("鉴权失败");
|
||||||
|
}
|
||||||
|
if (!isIpAllowed(clientIp, credential.getIpWhitelist())) {
|
||||||
|
log.warn("开放一级需求范围接口IP不在白名单: traceId={}, ip={}, action={}", traceId, clientIp, action);
|
||||||
|
return Result.error("IP未授权");
|
||||||
|
}
|
||||||
|
verifyTimestamp(requestBody.getTimestamp(), credential.getSignExpireSeconds());
|
||||||
|
|
||||||
|
String payload = String.join("|",
|
||||||
|
action,
|
||||||
|
requestBody.getAppKey().trim(),
|
||||||
|
requestBody.getStartTime().trim(),
|
||||||
|
requestBody.getEndTime().trim(),
|
||||||
|
requestBody.getTimestamp().trim());
|
||||||
|
String expectedSign = hmacSha256Hex(credential.getAppSecret(), payload);
|
||||||
|
if (!secureEquals(expectedSign, requestBody.getSign().trim().toLowerCase(Locale.ROOT))) {
|
||||||
|
log.warn("开放一级需求范围接口签名不匹配: traceId={}, ip={}, action={}", traceId, clientIp, action);
|
||||||
|
return Result.error("签名校验失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDateTime startTime = parseDateTime(requestBody.getStartTime());
|
||||||
|
LocalDateTime endTime = parseDateTime(requestBody.getEndTime());
|
||||||
|
Object data = queryExecutor.execute(startTime, endTime);
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("traceId", traceId);
|
||||||
|
result.put("time", Instant.now().toString());
|
||||||
|
result.put("query", Map.of(
|
||||||
|
"startTime", requestBody.getStartTime().trim(),
|
||||||
|
"endTime", requestBody.getEndTime().trim()
|
||||||
|
));
|
||||||
|
result.put("data", data);
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("开放一级需求范围接口失败: traceId={}, ip={}, action={}, error={}",
|
||||||
|
traceId, clientIp, action, e.getMessage(), e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("开放一级需求范围接口系统异常: traceId={}, ip={}, action={}", traceId, clientIp, action, e);
|
||||||
|
return Result.error("系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private OpenApiCredential getCredential() {
|
private OpenApiCredential getCredential() {
|
||||||
String appKey = systemConfigService.getConfigValue(KEY_APP_KEY, "");
|
String appKey = systemConfigService.getConfigValue(KEY_APP_KEY, "");
|
||||||
if (!StringUtils.hasText(appKey)) {
|
if (!StringUtils.hasText(appKey)) {
|
||||||
@ -326,6 +967,22 @@ public class OpenAnalyticsController {
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LocalDateTime parseDateTime(String value) {
|
||||||
|
if (!StringUtils.hasText(value)) {
|
||||||
|
throw new IllegalArgumentException("时间参数不能为空");
|
||||||
|
}
|
||||||
|
String text = value.trim();
|
||||||
|
try {
|
||||||
|
return LocalDateTime.parse(text);
|
||||||
|
} catch (DateTimeParseException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OffsetDateTime.parse(text).toLocalDateTime();
|
||||||
|
} catch (DateTimeParseException ignored) {
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("时间格式错误,需使用ISO时间格式");
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class OpenAnalyticsOverviewRequest {
|
public static class OpenAnalyticsOverviewRequest {
|
||||||
private String appKey;
|
private String appKey;
|
||||||
@ -348,6 +1005,87 @@ public class OpenAnalyticsController {
|
|||||||
private Long deptId;
|
private Long deptId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenDemandStatisticsRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private String dateRange;
|
||||||
|
private String month;
|
||||||
|
private Long deptId;
|
||||||
|
private Long userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenDesignerPerformanceRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private String month;
|
||||||
|
private Long deptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenProcessEfficiencyRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private String month;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenWarningsRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenDimensionAnalysisRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private String dateRange;
|
||||||
|
private String month;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenDesignerDeductionRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private Long userId;
|
||||||
|
private String month;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenDesignerScoreRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private Long userId;
|
||||||
|
private String month;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenPrimaryDeptStatisticsRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private Long deptId;
|
||||||
|
private String startTime;
|
||||||
|
private String endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class OpenPrimaryRankingRequest {
|
||||||
|
private String appKey;
|
||||||
|
private String timestamp;
|
||||||
|
private String sign;
|
||||||
|
private String startTime;
|
||||||
|
private String endTime;
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
private static class OpenApiCredential {
|
private static class OpenApiCredential {
|
||||||
private String appKey;
|
private String appKey;
|
||||||
@ -355,4 +1093,9 @@ public class OpenAnalyticsController {
|
|||||||
private Integer signExpireSeconds;
|
private Integer signExpireSeconds;
|
||||||
private String ipWhitelist;
|
private String ipWhitelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface PrimaryRangeQueryExecutor {
|
||||||
|
Object execute(LocalDateTime startTime, LocalDateTime endTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user