Compare commits
No commits in common. "52a35e5b208d173438f945df1c85f97e766f5c86" and "b5b01d4f7976bea686e435bf6d9b5eea80d55f38" have entirely different histories.
52a35e5b20
...
b5b01d4f79
|
|
@ -4,21 +4,7 @@
|
|||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="4c558d98-824e-4a48-ba48-bd2e6172f9f4" name="更改" comment="修改定位信息 0323">
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/mapper/LlmMcpMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/mapper/LlmMcpMapper.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/security/McpDataPermissionInterceptor.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/security/McpDataPermissionInterceptor.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmCheckinSearchToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmCheckinSearchToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmDetailSearchToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmDetailSearchToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmExpansionSearchToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmExpansionSearchToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmFollowupSearchToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmFollowupSearchToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmReportCatalogToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmReportCatalogToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmReportQueryToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmReportQueryToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmTodoSearchToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/CrmTodoSearchToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/OpportunitySearchToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/OpportunitySearchToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/WorkReportSearchToolProvider.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/java/com/unis/crm/llm/tools/WorkReportSearchToolProvider.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/main/resources/mapper/llm/LlmMcpMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/main/resources/mapper/llm/LlmMcpMapper.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/backend/src/test/java/com/unis/crm/llm/security/McpDataPermissionInterceptorTest.java" beforeDir="false" afterPath="$PROJECT_DIR$/backend/src/test/java/com/unis/crm/llm/security/McpDataPermissionInterceptorTest.java" afterDir="false" />
|
||||
</list>
|
||||
<list default="true" id="4c558d98-824e-4a48-ba48-bd2e6172f9f4" name="更改" comment="修改定位信息 0323" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
|
|
@ -115,8 +101,6 @@
|
|||
<workItem from="1787721570035" duration="18307000" />
|
||||
<workItem from="1788916111243" duration="625000" />
|
||||
<workItem from="1789033867432" duration="17000" />
|
||||
<workItem from="1789440226191" duration="937000" />
|
||||
<workItem from="1789459774783" duration="30000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="修改定位信息 0323">
|
||||
<option name="closed" value="true" />
|
||||
|
|
|
|||
|
|
@ -25,14 +25,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countWorkReports(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("targetUserId") Long targetUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") String status);
|
||||
|
||||
List<Map<String, Object>> searchOpportunities(
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
|
|
@ -42,13 +34,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countOpportunities(
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("stage") String stage,
|
||||
@Param("includeArchived") boolean includeArchived);
|
||||
|
||||
List<Map<String, Object>> universalSearchCustomers(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
|
|
@ -224,30 +209,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
List<Map<String, Object>> channelTierDistribution(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> channelIntentDistribution(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> channelCoverageSummary(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> channelRevenueTier(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
Map<String, Object> selectWorkTodayStatus(
|
||||
@Param("targetUserId") Long targetUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
|
|
@ -265,16 +226,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countTodos(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") String status,
|
||||
@Param("priority") String priority,
|
||||
@Param("bizType") String bizType,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> searchOrgUsers(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") Integer status,
|
||||
|
|
@ -306,16 +257,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countCustomers(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") String status,
|
||||
@Param("source") String source,
|
||||
@Param("industry") String industry,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> searchCheckins(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") String status,
|
||||
|
|
@ -327,15 +268,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countCheckins(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") String status,
|
||||
@Param("bizType") String bizType,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> searchSalesExpansions(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("stage") String stage,
|
||||
|
|
@ -347,15 +279,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countSalesExpansions(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("stage") String stage,
|
||||
@Param("intentLevel") String intentLevel,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> searchChannelExpansions(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("stage") String stage,
|
||||
|
|
@ -367,15 +290,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countChannelExpansions(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("stage") String stage,
|
||||
@Param("intentLevel") String intentLevel,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
List<Map<String, Object>> searchFollowups(
|
||||
@Param("bizType") String bizType,
|
||||
@Param("keyword") String keyword,
|
||||
|
|
@ -386,14 +300,6 @@ public interface LlmMcpMapper {
|
|||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
long countFollowups(
|
||||
@Param("bizType") String bizType,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
Map<String, Object> selectCustomerDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
Map<String, Object> selectOpportunityDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class McpDataPermissionInterceptor implements Interceptor {
|
|||
validateParameters(methodName, boundSql.getParameterObject(), loginUser);
|
||||
|
||||
CrmDataVisibilityService visibilityService = visibilityServiceProvider.getIfAvailable();
|
||||
if ("searchFollowups".equals(methodName) || "countFollowups".equals(methodName)) {
|
||||
if ("searchFollowups".equals(methodName)) {
|
||||
if (visibilityService == null) {
|
||||
throw new BusinessException("MCP 数据权限服务不可用");
|
||||
}
|
||||
|
|
@ -293,7 +293,6 @@ public class McpDataPermissionInterceptor implements Interceptor {
|
|||
static Set<String> policyMethodNames() {
|
||||
java.util.LinkedHashSet<String> methodNames = new java.util.LinkedHashSet<>(POLICIES.keySet());
|
||||
methodNames.add("searchFollowups");
|
||||
methodNames.add("countFollowups");
|
||||
methodNames.add("salesPerformance");
|
||||
return Set.copyOf(methodNames);
|
||||
}
|
||||
|
|
@ -402,23 +401,22 @@ public class McpDataPermissionInterceptor implements Interceptor {
|
|||
private static Map<String, QueryPolicy> buildPolicies() {
|
||||
Map<String, QueryPolicy> policies = new LinkedHashMap<>();
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_DAILY_REPORT, "r.user_id", null, false,
|
||||
"searchWorkReports", "countWorkReports", "universalSearchWorkReports", "dashboardDailyReportMetric",
|
||||
"searchWorkReports", "universalSearchWorkReports", "dashboardDailyReportMetric",
|
||||
"selectWorkReportDetail", "selectWorkReportComments");
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_CHECKIN, "c.user_id", null, false,
|
||||
"universalSearchCheckins", "countCheckins", "dashboardCheckinMetric", "checkinSummary",
|
||||
"universalSearchCheckins", "dashboardCheckinMetric", "checkinSummary",
|
||||
"searchCheckins", "selectCheckinDetail");
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_CUSTOMER, "c.owner_user_id", null, false,
|
||||
"universalSearchCustomers", "countCustomers", "dashboardCustomerMetric", "customerSummary",
|
||||
"universalSearchCustomers", "dashboardCustomerMetric", "customerSummary",
|
||||
"searchCustomers", "selectCustomerDetail");
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_EXPANSION, "s.owner_user_id", null, false,
|
||||
"universalSearchSalesExpansions", "countSalesExpansions", "salesExpansionSummary", "searchSalesExpansions",
|
||||
"universalSearchSalesExpansions", "salesExpansionSummary", "searchSalesExpansions",
|
||||
"selectSalesExpansionDetail", "selectSalesExpansionFollowups");
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_EXPANSION, "c.owner_user_id", null, false,
|
||||
"universalSearchChannelExpansions", "countChannelExpansions", "channelExpansionSummary", "searchChannelExpansions",
|
||||
"selectChannelExpansionDetail", "selectChannelExpansionContacts", "selectChannelExpansionFollowups",
|
||||
"channelTierDistribution", "channelIntentDistribution", "channelCoverageSummary", "channelRevenueTier");
|
||||
"universalSearchChannelExpansions", "channelExpansionSummary", "searchChannelExpansions",
|
||||
"selectChannelExpansionDetail", "selectChannelExpansionContacts", "selectChannelExpansionFollowups");
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_WORK, "t.user_id", null, false,
|
||||
"universalSearchTodos", "countTodos", "searchTodos", "todoSummary", "selectTodoDetail");
|
||||
"universalSearchTodos", "searchTodos", "todoSummary", "selectTodoDetail");
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_WORK, "l.operator_user_id", null, false,
|
||||
"universalSearchActivities");
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_OPPORTUNITY, "o.owner_user_id",
|
||||
|
|
@ -430,7 +428,7 @@ public class McpDataPermissionInterceptor implements Interceptor {
|
|||
|
||||
add(policies, CrmDataVisibilityService.RESOURCE_OPPORTUNITY, "o.owner_user_id",
|
||||
"o.project_ownership_location", true,
|
||||
"searchOpportunities", "countOpportunities", "universalSearchOpportunities", "dashboardNewOpportunityMetric",
|
||||
"searchOpportunities", "universalSearchOpportunities", "dashboardNewOpportunityMetric",
|
||||
"dashboardWonOpportunityMetric", "opportunityFunnel", "opportunityTrend",
|
||||
"selectOpportunityDetail", "selectCustomerOpportunities", "selectOpportunityFollowups",
|
||||
"selectSalesExpansionOpportunities", "selectChannelExpansionOpportunities");
|
||||
|
|
|
|||
|
|
@ -49,17 +49,7 @@ public class CrmCheckinSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
QueryContext context = buildContext(arguments);
|
||||
long total = llmMcpMapper.countCheckins(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
getString(arguments, "bizType"),
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId());
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("total", total);
|
||||
result.put("hasMore", (long) context.offset() + context.pageSize() < total);
|
||||
result.put("rows", sanitizeRows(llmMcpMapper.searchCheckins(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
|
|
|
|||
|
|
@ -50,18 +50,7 @@ public class CrmDetailSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
QueryContext context = buildContext(arguments);
|
||||
long total = llmMcpMapper.countCustomers(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
getString(arguments, "source"),
|
||||
getString(arguments, "industry"),
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId());
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("total", total);
|
||||
result.put("hasMore", (long) context.offset() + context.pageSize() < total);
|
||||
result.put("rows", sanitizeRows(llmMcpMapper.searchCustomers(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
|
|
|
|||
|
|
@ -61,40 +61,6 @@ public class CrmExpansionSearchToolProvider extends PermissionedMcpToolProvider
|
|||
if (!StringUtils.hasText(expansionType)) {
|
||||
expansionType = "all";
|
||||
}
|
||||
long total = switch (expansionType) {
|
||||
case "sales" -> llmMcpMapper.countSalesExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId());
|
||||
case "channel" -> llmMcpMapper.countChannelExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId());
|
||||
default -> llmMcpMapper.countSalesExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId())
|
||||
+ llmMcpMapper.countChannelExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId());
|
||||
};
|
||||
List<Map<String, Object>> rows = switch (expansionType) {
|
||||
case "sales" -> normalizeRows(llmMcpMapper.searchSalesExpansions(
|
||||
keyword,
|
||||
|
|
@ -119,8 +85,6 @@ public class CrmExpansionSearchToolProvider extends PermissionedMcpToolProvider
|
|||
default -> mergedRows(keyword, stage, intentLevel, context);
|
||||
};
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("total", total);
|
||||
result.put("hasMore", (long) context.offset() + context.pageSize() < total);
|
||||
result.put("rows", rows);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,16 +53,7 @@ public class CrmFollowupSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
if (!StringUtils.hasText(bizType)) {
|
||||
bizType = "all";
|
||||
}
|
||||
long total = llmMcpMapper.countFollowups(
|
||||
bizType,
|
||||
getString(arguments, "keyword"),
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId());
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("total", total);
|
||||
result.put("hasMore", (long) context.offset() + context.pageSize() < total);
|
||||
result.put("rows", sanitizeRows(llmMcpMapper.searchFollowups(
|
||||
bizType,
|
||||
getString(arguments, "keyword"),
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ public class CrmReportCatalogToolProvider extends PermissionedMcpToolProvider {
|
|||
Map.of("reportType", "daily_report_completion", "description", "日报应交、已交、未交、完成率"),
|
||||
Map.of("reportType", "checkin_summary", "description", "外勤打卡统计"),
|
||||
Map.of("reportType", "expansion_summary", "description", "销售拓客与渠道拓客统计"),
|
||||
Map.of("reportType", "channel_analytics", "description", "渠道统计分析:认证级别分布、合作意向、覆盖省市、年度营收分档(SQL 全量聚合)"),
|
||||
Map.of("reportType", "customer_summary", "description", "客户状态、来源、行业统计"),
|
||||
Map.of("reportType", "todo_summary", "description", "待办状态与优先级统计")));
|
||||
result.put("security", "查询结果遵循系统 @DataScope 数据权限。");
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ public class CrmReportQueryToolProvider extends PermissionedMcpToolProvider {
|
|||
"checkin_summary",
|
||||
"expansion_summary",
|
||||
"customer_summary",
|
||||
"todo_summary",
|
||||
"channel_analytics")));
|
||||
"todo_summary")));
|
||||
queryProperties.put("startDate", stringProperty("开始日期 YYYY-MM-DD;不传默认本月。"));
|
||||
queryProperties.put("endDate", stringProperty("结束日期 YYYY-MM-DD;不传默认今天,最大跨度 366 天。"));
|
||||
queryProperties.put("ownerUserId", integerProperty("目标用户 ID,实际可见范围仍按系统数据权限裁剪。"));
|
||||
|
|
@ -93,7 +92,6 @@ public class CrmReportQueryToolProvider extends PermissionedMcpToolProvider {
|
|||
case "expansion_summary" -> expansionSummary(context);
|
||||
case "customer_summary" -> customerSummary(context);
|
||||
case "todo_summary" -> todoSummary(context);
|
||||
case "channel_analytics" -> channelAnalytics(context);
|
||||
default -> throw new BusinessException("不支持的 reportType:" + reportType);
|
||||
};
|
||||
|
||||
|
|
@ -210,22 +208,6 @@ public class CrmReportQueryToolProvider extends PermissionedMcpToolProvider {
|
|||
context.offset()));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> channelAnalytics(QueryContext context) {
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("dimension", "tier");
|
||||
row.put("tierDistribution", llmMcpMapper.channelTierDistribution(
|
||||
context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
row.put("intentDistribution", llmMcpMapper.channelIntentDistribution(
|
||||
context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
row.put("coverages", llmMcpMapper.channelCoverageSummary(
|
||||
context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
row.put("revenueTier", llmMcpMapper.channelRevenueTier(
|
||||
context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
rows.add(row);
|
||||
return sanitizeRows(rows);
|
||||
}
|
||||
|
||||
private QueryContext buildContext(Map<String, Object> arguments) {
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
|
|
|
|||
|
|
@ -70,16 +70,6 @@ public class CrmTodoSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
int page = getPage(arguments);
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
Long ownerUserId = getLong(arguments, "ownerUserId");
|
||||
int offset = getOffset(page, pageSize);
|
||||
long total = llmMcpMapper.countTodos(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
getString(arguments, "priority"),
|
||||
getString(arguments, "bizType"),
|
||||
startDate,
|
||||
endDate,
|
||||
ownerUserId,
|
||||
tenantId);
|
||||
List<Map<String, Object>> rows = llmMcpMapper.searchTodos(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
|
|
@ -90,7 +80,7 @@ public class CrmTodoSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
ownerUserId,
|
||||
tenantId,
|
||||
pageSize,
|
||||
offset);
|
||||
getOffset(page, pageSize));
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("ownerUserId", ownerUserId);
|
||||
|
|
@ -99,8 +89,6 @@ public class CrmTodoSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
result.put("endDate", endDate == null ? null : endDate.toString());
|
||||
result.put("page", page);
|
||||
result.put("pageSize", pageSize);
|
||||
result.put("total", total);
|
||||
result.put("hasMore", (long) offset + pageSize < total);
|
||||
result.put("rows", sanitizeRows(rows));
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,13 +63,6 @@ public class OpportunitySearchToolProvider extends PermissionedMcpToolProvider {
|
|||
boolean includeArchived = getBoolean(arguments, "includeArchived", false);
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
|
||||
int offset = getOffset(page, pageSize);
|
||||
long total = llmMcpMapper.countOpportunities(
|
||||
ownerUserId,
|
||||
tenantId,
|
||||
keyword,
|
||||
stage,
|
||||
includeArchived);
|
||||
List<Map<String, Object>> rows = sanitizeRows(llmMcpMapper.searchOpportunities(
|
||||
ownerUserId,
|
||||
tenantId,
|
||||
|
|
@ -77,14 +70,12 @@ public class OpportunitySearchToolProvider extends PermissionedMcpToolProvider {
|
|||
stage,
|
||||
includeArchived,
|
||||
pageSize,
|
||||
offset));
|
||||
getOffset(page, pageSize)));
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("ownerUserId", ownerUserId);
|
||||
result.put("tenantId", tenantId);
|
||||
result.put("page", page);
|
||||
result.put("pageSize", pageSize);
|
||||
result.put("total", total);
|
||||
result.put("hasMore", (long) offset + pageSize < total);
|
||||
result.put("rows", rows);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,15 +67,7 @@ public class WorkReportSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
String keyword = getString(arguments, "keyword");
|
||||
String status = getString(arguments, "status");
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
int offset = getOffset(page, pageSize);
|
||||
|
||||
long total = llmMcpMapper.countWorkReports(
|
||||
dateRange.startDate(),
|
||||
dateRange.endDate(),
|
||||
targetUserId,
|
||||
tenantId,
|
||||
keyword,
|
||||
status);
|
||||
List<Map<String, Object>> rows = sanitizeRows(llmMcpMapper.searchWorkReports(
|
||||
dateRange.startDate(),
|
||||
dateRange.endDate(),
|
||||
|
|
@ -84,15 +76,13 @@ public class WorkReportSearchToolProvider extends PermissionedMcpToolProvider {
|
|||
keyword,
|
||||
status,
|
||||
pageSize,
|
||||
offset));
|
||||
getOffset(page, pageSize)));
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("startDate", dateRange.startDate().toString());
|
||||
result.put("endDate", dateRange.endDate().toString());
|
||||
result.put("userId", targetUserId);
|
||||
result.put("page", page);
|
||||
result.put("pageSize", pageSize);
|
||||
result.put("total", total);
|
||||
result.put("hasMore", (long) offset + pageSize < total);
|
||||
result.put("rows", rows);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,36 +99,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countWorkReports" resultType="long">
|
||||
select count(*)
|
||||
from work_daily_report r
|
||||
left join sys_user u on u.user_id = r.user_id and coalesce(u.is_deleted, 0) = 0
|
||||
where r.report_date between #{startDate} and #{endDate}
|
||||
<if test="targetUserId != null">
|
||||
and r.user_id = #{targetUserId}
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
and exists (
|
||||
select 1
|
||||
from sys_tenant_user tu
|
||||
where tu.user_id = r.user_id
|
||||
and tu.tenant_id = #{tenantId}
|
||||
and coalesce(tu.is_deleted, 0) = 0
|
||||
)
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(u.display_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(u.username, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(r.work_content, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(r.tomorrow_plan, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
and coalesce(r.status, 'submitted') = #{status}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="searchOpportunities" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
o.id as "id",
|
||||
|
|
@ -191,46 +161,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countOpportunities" resultType="long">
|
||||
select count(*)
|
||||
from crm_opportunity o
|
||||
left join crm_customer c on c.id = o.customer_id
|
||||
left join sys_user u on u.user_id = o.owner_user_id and coalesce(u.is_deleted, 0) = 0
|
||||
left join cnarea project_ownership_area
|
||||
on project_ownership_area.level = 1
|
||||
and project_ownership_area.area_code = o.project_ownership_location
|
||||
where 1 = 1
|
||||
<if test="ownerUserId != null">
|
||||
and o.owner_user_id = #{ownerUserId}
|
||||
</if>
|
||||
<if test="!includeArchived">
|
||||
and coalesce(o.archived, false) = false
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
and exists (
|
||||
select 1
|
||||
from sys_tenant_user tu
|
||||
where tu.user_id = o.owner_user_id
|
||||
and tu.tenant_id = #{tenantId}
|
||||
and coalesce(tu.is_deleted, 0) = 0
|
||||
)
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(o.opportunity_code, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(o.opportunity_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.customer_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(o.project_location, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(o.project_ownership_location, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(project_ownership_area.short_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(o.product_type, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="stage != null and stage != ''">
|
||||
and o.stage = #{stage}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="universalSearch" resultType="java.util.LinkedHashMap">
|
||||
with search_rows as (
|
||||
select
|
||||
|
|
@ -1229,28 +1159,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countTodos" resultType="long">
|
||||
select count(*)
|
||||
from work_todo t
|
||||
left join sys_user u on u.user_id = t.user_id and coalesce(u.is_deleted, 0) = 0
|
||||
where 1 = 1
|
||||
<if test="ownerUserId != null">and t.user_id = #{ownerUserId}</if>
|
||||
<if test="status != null and status != ''">and t.status = #{status}</if>
|
||||
<if test="priority != null and priority != ''">and t.priority = #{priority}</if>
|
||||
<if test="bizType != null and bizType != ''">and t.biz_type = #{bizType}</if>
|
||||
<if test="startDate != null">and t.due_date::date >= #{startDate}</if>
|
||||
<if test="endDate != null">and t.due_date::date <= #{endDate}</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = t.user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(t.title, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(t.biz_type, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(u.display_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(u.username, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="searchOrgUsers" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
u.user_id as "userId",
|
||||
|
|
@ -1403,29 +1311,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countCustomers" resultType="long">
|
||||
select count(*)
|
||||
from crm_customer c
|
||||
where 1 = 1
|
||||
<if test="ownerUserId != null">and c.owner_user_id = #{ownerUserId}</if>
|
||||
<if test="startDate != null">and c.created_at::date >= #{startDate}</if>
|
||||
<if test="endDate != null">and c.created_at::date <= #{endDate}</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = c.owner_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(c.customer_code, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.customer_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.industry, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.province, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.city, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.remark, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="status != null and status != ''">and c.status = #{status}</if>
|
||||
<if test="source != null and source != ''">and c.source = #{source}</if>
|
||||
<if test="industry != null and industry != ''">and c.industry = #{industry}</if>
|
||||
</select>
|
||||
|
||||
<select id="searchCheckins" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
c.id as "id",
|
||||
|
|
@ -1463,27 +1348,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countCheckins" resultType="long">
|
||||
select count(*)
|
||||
from work_checkin c
|
||||
where 1 = 1
|
||||
<if test="ownerUserId != null">and c.user_id = #{ownerUserId}</if>
|
||||
<if test="startDate != null">and c.checkin_date >= #{startDate}</if>
|
||||
<if test="endDate != null">and c.checkin_date <= #{endDate}</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = c.user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(c.user_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.dept_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.biz_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.location_text, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.remark, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="status != null and status != ''">and c.status = #{status}</if>
|
||||
<if test="bizType != null and bizType != ''">and c.biz_type = #{bizType}</if>
|
||||
</select>
|
||||
|
||||
<select id="searchExpansions" resultType="java.util.LinkedHashMap">
|
||||
with expansion_rows as (
|
||||
select
|
||||
|
|
@ -1602,28 +1466,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countSalesExpansions" resultType="long">
|
||||
select count(*)
|
||||
from crm_sales_expansion s
|
||||
where 1 = 1
|
||||
<if test="ownerUserId != null">and s.owner_user_id = #{ownerUserId}</if>
|
||||
<if test="stage != null and stage != ''">and s.stage = #{stage}</if>
|
||||
<if test="intentLevel != null and intentLevel != ''">and s.intent_level = #{intentLevel}</if>
|
||||
<if test="startDate != null">and s.created_at::date >= #{startDate}</if>
|
||||
<if test="endDate != null">and s.created_at::date <= #{endDate}</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(s.employee_no, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(s.candidate_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(s.mobile, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(s.target_dept, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(s.industry, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(s.remark, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = s.owner_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
</select>
|
||||
|
||||
<select id="searchChannelExpansions" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
'channel' as "expansionType",
|
||||
|
|
@ -1662,30 +1504,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countChannelExpansions" resultType="long">
|
||||
select count(*)
|
||||
from crm_channel_expansion c
|
||||
where 1 = 1
|
||||
<if test="ownerUserId != null">and c.owner_user_id = #{ownerUserId}</if>
|
||||
<if test="stage != null and stage != ''">and c.stage = #{stage}</if>
|
||||
<if test="intentLevel != null and intentLevel != ''">and c.intent_level = #{intentLevel}</if>
|
||||
<if test="startDate != null">and c.created_at::date >= #{startDate}</if>
|
||||
<if test="endDate != null">and c.created_at::date <= #{endDate}</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(c.channel_code, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.channel_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.province, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.city, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.channel_industry, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.contact_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.contact_mobile, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.remark, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = c.owner_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
</select>
|
||||
|
||||
<select id="searchFollowups" resultType="java.util.LinkedHashMap">
|
||||
with followup_rows as (
|
||||
select
|
||||
|
|
@ -1774,76 +1592,6 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<select id="countFollowups" resultType="long">
|
||||
with followup_rows as (
|
||||
select
|
||||
'opportunity' as "bizType",
|
||||
f.id as "id",
|
||||
f.opportunity_id as "bizId",
|
||||
coalesce(o.opportunity_name, '') as "bizName",
|
||||
f.followup_type as "followupType",
|
||||
left(coalesce(f.content, ''), 800) as "content",
|
||||
left(coalesce(f.next_action, ''), 300) as "nextAction",
|
||||
f.followup_user_id as "userId",
|
||||
f.followup_time as "followupTime",
|
||||
'OPPORTUNITY' as "scopeResourceType",
|
||||
o.owner_user_id as "scopeOwnerUserId",
|
||||
o.project_ownership_location as "scopeAreaCode",
|
||||
o.pre_sales_id as "scopePreSalesId",
|
||||
o.pre_sales_name as "scopePreSalesName"
|
||||
from crm_opportunity_followup f
|
||||
join crm_opportunity o on o.id = f.opportunity_id
|
||||
where #{bizType} in ('all', 'opportunity')
|
||||
<if test="ownerUserId != null">and f.followup_user_id = #{ownerUserId}</if>
|
||||
<if test="startDate != null">and f.followup_time::date >= #{startDate}</if>
|
||||
<if test="endDate != null">and f.followup_time::date <= #{endDate}</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(o.opportunity_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(f.content, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(f.next_action, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = f.followup_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
|
||||
union all
|
||||
|
||||
select
|
||||
f.biz_type as "bizType",
|
||||
f.id as "id",
|
||||
f.biz_id as "bizId",
|
||||
case when f.biz_type = 'sales' then coalesce(s.candidate_name, '') else coalesce(c.channel_name, '') end as "bizName",
|
||||
f.followup_type as "followupType",
|
||||
left(coalesce(f.content, ''), 800) as "content",
|
||||
left(coalesce(f.next_action, f.next_plan, ''), 300) as "nextAction",
|
||||
f.followup_user_id as "userId",
|
||||
f.followup_time as "followupTime",
|
||||
'EXPANSION' as "scopeResourceType",
|
||||
case when f.biz_type = 'sales' then s.owner_user_id else c.owner_user_id end as "scopeOwnerUserId",
|
||||
null::varchar as "scopeAreaCode",
|
||||
null::bigint as "scopePreSalesId",
|
||||
null::varchar as "scopePreSalesName"
|
||||
from crm_expansion_followup f
|
||||
left join crm_sales_expansion s on s.id = f.biz_id and f.biz_type = 'sales'
|
||||
left join crm_channel_expansion c on c.id = f.biz_id and f.biz_type = 'channel'
|
||||
where #{bizType} in ('all', 'sales', 'channel')
|
||||
<if test="ownerUserId != null">and f.followup_user_id = #{ownerUserId}</if>
|
||||
<if test="startDate != null">and f.followup_time::date >= #{startDate}</if>
|
||||
<if test="endDate != null">and f.followup_time::date <= #{endDate}</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
and (
|
||||
coalesce(s.candidate_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(c.channel_name, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(f.content, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(f.next_action, '') ilike concat('%', #{keyword}, '%')
|
||||
or coalesce(f.next_plan, '') ilike concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = f.followup_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
)
|
||||
select count(*) from followup_rows
|
||||
</select>
|
||||
|
||||
<select id="selectCustomerDetail" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
c.*,
|
||||
|
|
@ -2137,86 +1885,4 @@
|
|||
offset #{offset}
|
||||
</select>
|
||||
|
||||
<!-- 渠道统计分析:按认证级别(tz_rzjb字典)分组,全量无分页 -->
|
||||
<select id="channelTierDistribution" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
coalesce(nullif(btrim(ti.item_label), ''), '未分级') as "tierName",
|
||||
coalesce(ti.item_value, '') as "tierCode",
|
||||
count(c.id)::bigint as "channelCount",
|
||||
count(case when c.intent_level = 'high' then 1 end)::bigint as "highIntentCount",
|
||||
count(case when c.stage = 'won' then 1 end)::bigint as "wonCount",
|
||||
count(case when coalesce(c.has_desktop_exp, false) = true then 1 end)::bigint as "desktopExpCount"
|
||||
from crm_channel_expansion c
|
||||
left join sys_dict_item ti
|
||||
on ti.type_code = 'tz_rzjb'
|
||||
and ti.item_value = c.certification_level
|
||||
and coalesce(ti.is_deleted, 0) = 0
|
||||
and coalesce(ti.status, 1) = 1
|
||||
where c.created_at::date between #{startDate} and #{endDate}
|
||||
<if test="ownerUserId != null">and c.owner_user_id = #{ownerUserId}</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = c.owner_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
group by coalesce(nullif(btrim(ti.item_label), ''), '未分级'), coalesce(ti.item_value, '')
|
||||
order by "channelCount" desc, "tierName" asc
|
||||
</select>
|
||||
|
||||
<!-- 渠道统计分析:按合作意向(intent_level)分组,全量无分页 -->
|
||||
<select id="channelIntentDistribution" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
coalesce(c.intent_level, 'unknown') as "intentKey",
|
||||
case lower(coalesce(c.intent_level, 'unknown'))
|
||||
when 'high' then '高意向'
|
||||
when 'medium' then '中意向'
|
||||
when 'low' then '低意向'
|
||||
else '未标注'
|
||||
end as "intentName",
|
||||
count(1)::bigint as "channelCount"
|
||||
from crm_channel_expansion c
|
||||
where c.created_at::date between #{startDate} and #{endDate}
|
||||
<if test="ownerUserId != null">and c.owner_user_id = #{ownerUserId}</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = c.owner_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
group by c.intent_level
|
||||
order by "channelCount" desc, "intentName" asc
|
||||
</select>
|
||||
|
||||
<!-- 渠道统计分析:覆盖省份/城市,基于覆盖率从表 crm_channel_expansion_coverage,全量无分页 -->
|
||||
<select id="channelCoverageSummary" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
coalesce(cov.province, '未知') as "province",
|
||||
count(distinct c.id)::bigint as "channelCount",
|
||||
count(distinct nullif(btrim(cov.city), ''))::bigint as "cityCount"
|
||||
from crm_channel_expansion_coverage cov
|
||||
join crm_channel_expansion c on c.id = cov.channel_id
|
||||
where c.created_at::date between #{startDate} and #{endDate}
|
||||
<if test="ownerUserId != null">and c.owner_user_id = #{ownerUserId}</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = c.owner_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
group by cov.province
|
||||
order by "channelCount" desc, "province" asc
|
||||
</select>
|
||||
|
||||
<!-- 渠道统计分析:按年度营业额分档,全量无分页 -->
|
||||
<select id="channelRevenueTier" resultType="java.util.LinkedHashMap">
|
||||
select
|
||||
case
|
||||
when annual_revenue is null then '未填写'
|
||||
when annual_revenue < 100 then '<100万'
|
||||
when annual_revenue < 500 then '100-500万'
|
||||
when annual_revenue < 1000 then '500-1000万'
|
||||
else '1000万+'
|
||||
end as "revenueTier",
|
||||
count(1)::bigint as "channelCount",
|
||||
coalesce(round(avg(annual_revenue)), 0)::bigint as "avgRevenue"
|
||||
from crm_channel_expansion c
|
||||
where c.created_at::date between #{startDate} and #{endDate}
|
||||
<if test="ownerUserId != null">and c.owner_user_id = #{ownerUserId}</if>
|
||||
<if test="tenantId != null">and exists (select 1 from sys_tenant_user tu where tu.user_id = c.owner_user_id and tu.tenant_id = #{tenantId} and coalesce(tu.is_deleted, 0) = 0)</if>
|
||||
group by case
|
||||
when annual_revenue is null then '未填写'
|
||||
when annual_revenue < 100 then '<100万'
|
||||
when annual_revenue < 500 then '100-500万'
|
||||
when annual_revenue < 1000 then '500-1000万'
|
||||
else '1000万+'
|
||||
end
|
||||
order by "channelCount" desc
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ class McpDataPermissionInterceptorTest {
|
|||
CrmDataVisibilityService.RESOURCE_DAILY_REPORT, visibility,
|
||||
CrmDataVisibilityService.RESOURCE_CHECKIN, visibility));
|
||||
} else {
|
||||
String condition = ("searchFollowups".equals(methodName) || "countFollowups".equals(methodName))
|
||||
String condition = "searchFollowups".equals(methodName)
|
||||
? interceptor.buildFollowupVisibilityCondition(visibility, visibility, loginUser)
|
||||
: interceptor.buildVisibilityCondition(methodName, visibility, loginUser);
|
||||
rewritten = interceptor.rewriteSql(boundSql.getSql(), condition);
|
||||
|
|
@ -168,35 +168,6 @@ class McpDataPermissionInterceptorTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyCountStatementShouldNotPaginate() throws Exception {
|
||||
String[] countMethods = {
|
||||
"countWorkReports",
|
||||
"countOpportunities",
|
||||
"countTodos",
|
||||
"countCustomers",
|
||||
"countCheckins",
|
||||
"countSalesExpansions",
|
||||
"countChannelExpansions",
|
||||
"countFollowups"};
|
||||
Configuration configuration = new Configuration();
|
||||
Path mapperPath = Path.of("src/main/resources/mapper/llm/LlmMcpMapper.xml");
|
||||
try (InputStream inputStream = Files.newInputStream(mapperPath)) {
|
||||
new XMLMapperBuilder(inputStream, configuration, mapperPath.toString(), configuration.getSqlFragments()).parse();
|
||||
}
|
||||
for (String methodName : countMethods) {
|
||||
BoundSql boundSql = configuration.getMappedStatement(
|
||||
"com.unis.crm.llm.mapper.LlmMcpMapper." + methodName)
|
||||
.getBoundSql(allParameters());
|
||||
String sql = boundSql.getSql().toLowerCase();
|
||||
assertTrue(sql.startsWith("select count(*)") || sql.contains("select count(*)"),
|
||||
methodName + " should count all rows");
|
||||
assertTrue(!sql.contains(" limit ") && !sql.contains(" offset "),
|
||||
methodName + " must not apply pagination to count");
|
||||
assertTrue(!sql.contains("order by"), methodName + " must not order the count query");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyMcpMapperMethodShouldBeProtectedOrExplicitlyTenantSafe() {
|
||||
Set<String> tenantSafeMethods = Set.of(
|
||||
|
|
|
|||
Loading…
Reference in New Issue